1/*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de
3 * Copyright 2002-2008, Axel D��rfler, axeld@pinc-software.de
4 * Distributed under the terms of the MIT License.
5 *
6 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
7 * Distributed under the terms of the NewOS License.
8 */
9
10#include "debug_builtin_commands.h"
11
12#include <ctype.h>
13#include <string.h>
14#include <strings.h>
15
16#include <debug.h>
17#include <kernel.h>
18
19#include "debug_commands.h"
20#include "gdb.h"
21
22
23static int
24cmd_reboot(int argc, char **argv)
25{
26	arch_cpu_shutdown(true);
27	return 0;
28		// I'll be really suprised if this line ever runs! ;-)
29}
30
31
32static int
33cmd_shutdown(int argc, char **argv)
34{
35	arch_cpu_shutdown(false);
36	return 0;
37}
38
39
40static int
41cmd_help(int argc, char **argv)
42{
43	debugger_command *command, *specified = NULL;
44	const char *start = NULL;
45	int32 startLength = 0;
46	bool ambiguous;
47
48	if (argc > 1) {
49		specified = find_debugger_command(argv[1], false, ambiguous);
50		if (specified == NULL) {
51			start = argv[1];
52			startLength = strlen(start);
53		}
54	}
55
56	if (specified != NULL) {
57		// only print out the help of the specified command (and all of its aliases)
58		kprintf("debugger command for \"%s\" and aliases:\n", specified->name);
59	} else if (start != NULL)
60		kprintf("debugger commands starting with \"%s\":\n", start);
61	else
62		kprintf("debugger commands:\n");
63
64	for (command = get_debugger_commands(); command != NULL;
65			command = command->next) {
66		if (specified && command->func != specified->func)
67			continue;
68		if (start != NULL && strncmp(start, command->name, startLength))
69			continue;
70
71		kprintf(" %-20s\t\t%s\n", command->name, command->description ? command->description : "-");
72	}
73
74	return 0;
75}
76
77
78static int
79cmd_continue(int argc, char **argv)
80{
81	return B_KDEBUG_QUIT;
82}
83
84
85static int
86cmd_expr(int argc, char **argv)
87{
88	if (argc != 2) {
89		print_debugger_command_usage(argv[0]);
90		return 0;
91	}
92
93	uint64 result;
94	if (evaluate_debug_expression(argv[1], &result, false)) {
95		kprintf("%" B_PRIu64 " (0x%" B_PRIx64 ")\n", result, result);
96		set_debug_variable("_", result);
97	}
98
99	return 0;
100}
101
102
103static int
104cmd_error(int argc, char **argv)
105{
106	if (argc != 2) {
107		print_debugger_command_usage(argv[0]);
108		return 0;
109	}
110
111	int32 error = parse_expression(argv[1]);
112	kprintf("error 0x%" B_PRIx32 ": %s\n", error, strerror(error));
113
114	return 0;
115}
116
117
118static int
119cmd_head(int argc, char** argv)
120{
121	debugger_command_pipe_segment* segment
122		= get_current_debugger_command_pipe_segment();
123	if (segment == NULL) {
124		kprintf_unfiltered("%s can only be run as part of a pipe!\n", argv[0]);
125		return B_KDEBUG_ERROR;
126	}
127
128	struct user_data {
129		uint64	max_lines;
130		uint64	lines;
131	};
132	user_data* userData = (user_data*)segment->user_data;
133
134	if (segment->invocations == 0) {
135		if (argc != 3) {
136			print_debugger_command_usage(argv[0]);
137			return B_KDEBUG_ERROR;
138		}
139
140		if (!evaluate_debug_expression(argv[1], &userData->max_lines, false))
141			return B_KDEBUG_ERROR;
142		userData->lines = 0;
143	}
144
145	if (++userData->lines <= userData->max_lines) {
146		kputs(argv[2]);
147		kputs("\n");
148	}
149
150	return 0;
151}
152
153
154static int
155cmd_tail(int argc, char** argv)
156{
157	debugger_command_pipe_segment* segment
158		= get_current_debugger_command_pipe_segment();
159	if (segment == NULL) {
160		kprintf_unfiltered("%s can only be run as part of a pipe!\n", argv[0]);
161		return B_KDEBUG_ERROR;
162	}
163
164	struct user_data {
165		uint64	max_lines;
166		int64	line_count;
167		bool	restarted;
168	};
169	user_data* userData = (user_data*)segment->user_data;
170
171	if (segment->invocations == 0) {
172		if (argc > 3) {
173			print_debugger_command_usage(argv[0]);
174			return B_KDEBUG_ERROR;
175		}
176
177		userData->max_lines = 10;
178		if (argc > 2 && !evaluate_debug_expression(argv[1],
179				&userData->max_lines, false)) {
180			return B_KDEBUG_ERROR;
181		}
182
183		userData->line_count = 1;
184		userData->restarted = false;
185	} else if (!userData->restarted) {
186		if (argv[argc - 1] == NULL) {
187			userData->restarted = true;
188			userData->line_count -= userData->max_lines;
189			return B_KDEBUG_RESTART_PIPE;
190		}
191
192		++userData->line_count;
193	} else {
194		if (argv[argc - 1] == NULL)
195			return 0;
196
197		if (--userData->line_count < 0) {
198			kputs(argv[argc - 1]);
199			kputs("\n");
200		}
201	}
202
203	return 0;
204}
205
206
207static int
208cmd_grep(int argc, char** argv)
209{
210	bool caseSensitive = true;
211	bool inverseMatch = false;
212
213	int argi = 1;
214	for (; argi < argc; argi++) {
215		const char* arg = argv[argi];
216		if (arg[0] != '-')
217			break;
218
219		for (int32 i = 1; arg[i] != '\0'; i++) {
220			if (arg[i] == 'i') {
221				caseSensitive = false;
222			} else if (arg[i] == 'v') {
223				inverseMatch = true;
224			} else {
225				print_debugger_command_usage(argv[0]);
226				return B_KDEBUG_ERROR;
227			}
228		}
229	}
230
231	if (argc - argi != 2) {
232		print_debugger_command_usage(argv[0]);
233		return B_KDEBUG_ERROR;
234	}
235
236	const char* pattern = argv[argi++];
237	const char* line = argv[argi++];
238
239	bool match;
240	if (caseSensitive) {
241		match = strstr(line, pattern) != NULL;
242	} else {
243		match = false;
244		int32 lineLen = strlen(line);
245		int32 patternLen = strlen(pattern);
246		for (int32 i = 0; i <= lineLen - patternLen; i++) {
247			// This is rather slow, but should be OK for our purposes.
248			if (strncasecmp(line + i, pattern, patternLen) == 0) {
249				match = true;
250				break;
251			}
252		}
253	}
254
255	if (match != inverseMatch) {
256		kputs(line);
257		kputs("\n");
258	}
259
260	return 0;
261}
262
263
264static int
265cmd_wc(int argc, char** argv)
266{
267	debugger_command_pipe_segment* segment
268		= get_current_debugger_command_pipe_segment();
269	if (segment == NULL) {
270		kprintf_unfiltered("%s can only be run as part of a pipe!\n", argv[0]);
271		return B_KDEBUG_ERROR;
272	}
273
274	struct user_data {
275		uint64	lines;
276		uint64	words;
277		uint64	chars;
278	};
279	user_data* userData = (user_data*)segment->user_data;
280
281	if (segment->invocations == 0) {
282		if (argc != 2) {
283			print_debugger_command_usage(argv[0]);
284			return B_KDEBUG_ERROR;
285		}
286
287		userData->lines = 0;
288		userData->words = 0;
289		userData->chars = 0;
290	}
291
292	const char* line = argv[1];
293	if (line == NULL) {
294		// last run -- print results
295		kprintf("%10" B_PRIu64 " %10" B_PRIu64 " %10" B_PRIu64 "\n",
296			userData->lines, userData->words, userData->chars);
297		return 0;
298	}
299
300	userData->lines++;
301	userData->chars++;
302		// newline
303
304	// count words and chars in this line
305	bool inWord = false;
306	for (; *line != '\0'; line++) {
307		userData->chars++;
308		if ((isspace(*line) != 0) == inWord) {
309			inWord = !inWord;
310			if (inWord)
311				userData->words++;
312		}
313	}
314
315	return 0;
316}
317
318
319static int
320cmd_faults(int argc, char** argv)
321{
322	if (argc > 2) {
323		print_debugger_command_usage(argv[0]);
324		return B_KDEBUG_ERROR;
325	}
326
327	if (argc == 2)
328		gInvokeCommandDirectly = parse_expression(argv[1]) == 0;
329
330	kprintf("Fault handling is %s%s.\n", argc == 2 ? "now " : "",
331		gInvokeCommandDirectly ? "off" : "on");
332	return 0;
333}
334
335
336// #pragma mark -
337
338
339void
340debug_builtin_commands_init()
341{
342	add_debugger_command_etc("help", &cmd_help, "List all debugger commands",
343		"[name]\n"
344		"Lists all debugger commands or those starting with \"name\".\n", 0);
345	add_debugger_command_etc("reboot", &cmd_reboot, "Reboot the system",
346		"\n"
347		"Reboots the system.\n", 0);
348	add_debugger_command_etc("shutdown", &cmd_shutdown, "Shut down the system",
349		"\n"
350		"Shuts down the system.\n", 0);
351	add_debugger_command_etc("gdb", &cmd_gdb, "Connect to remote gdb",
352		"\n"
353		"Connects to a remote gdb connected to the serial port.\n", 0);
354	add_debugger_command_etc("continue", &cmd_continue, "Leave kernel debugger",
355		"\n"
356		"Leaves kernel debugger.\n", 0);
357	add_debugger_command_alias("exit", "continue", "Same as \"continue\"");
358	add_debugger_command_alias("es", "continue", "Same as \"continue\"");
359	add_debugger_command_etc("expr", &cmd_expr,
360		"Evaluates the given expression and prints the result",
361		"<expression>\n"
362		"Evaluates the given expression and prints the result.\n",
363		B_KDEBUG_DONT_PARSE_ARGUMENTS);
364	add_debugger_command_etc("error", &cmd_error,
365		"Prints a human-readable description for an error code",
366		"<error>\n"
367		"Prints a human-readable description for the given numeric error\n"
368		"code.\n"
369		"  <error>  - The numeric error code.\n", 0);
370	add_debugger_command_etc("faults", &cmd_faults, "Toggles fault handling "
371		"for debugger commands",
372		"[0|1]\n"
373		"Toggles fault handling on (1) or off (0).\n", 0);
374	add_debugger_command_etc("head", &cmd_head,
375		"Prints only the first lines of output from another command",
376		"<maxLines>\n"
377		"Should be used in a command pipe. It prints only the first\n"
378		"<maxLines> lines of output from the previous command in the pipe and\n"
379		"silently discards the rest of the output.\n", 0);
380	add_debugger_command_etc("tail", &cmd_tail,
381		"Prints only the last lines of output from another command",
382		"[ <maxLines> ]\n"
383		"Should be used in a command pipe. It prints only the last\n"
384		"<maxLines> (default 10) lines of output from the previous command in\n"
385		"the pipe and silently discards the rest of the output.\n",
386		B_KDEBUG_PIPE_FINAL_RERUN);
387	add_debugger_command_etc("grep", &cmd_grep,
388		"Filters output from another command",
389		"[ -i ] [ -v ] <pattern>\n"
390		"Should be used in a command pipe. It filters all output from the\n"
391		"previous command in the pipe according to the given pattern.\n"
392		"When \"-v\" is specified, only those lines are printed that don't\n"
393		"match the given pattern, otherwise only those that do match. When\n"
394		"\"-i\" is specified, the pattern is matched case insensitive,\n"
395		"otherwise case sensitive.\n", 0);
396	add_debugger_command_etc("wc", &cmd_wc,
397		"Counts the lines, words, and characters of another command's output",
398		"<maxLines>\n"
399		"Should be used in a command pipe. It prints how many lines, words,\n"
400		"and characters the output of the previous command consists of.\n",
401		B_KDEBUG_PIPE_FINAL_RERUN);
402}
403