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