1/*
2 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de
3 * Copyright 2006, Stephan A��mus, superstippi@gmx.de
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include <debug.h>
9
10#include <ctype.h>
11#include <setjmp.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include <KernelExport.h>
17
18#include <debug_heap.h>
19
20#include "debug_commands.h"
21#include "debug_variables.h"
22
23
24/*
25	Grammar:
26
27	commandLine	:= ( commandPipe [ ";" commandLine  ] ) | assignment
28	expression	:= term | assignment
29	assignment	:= lhs ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" )
30				   expression
31	lhs			:= variable | dereference
32	term		:= sum
33	sum			:= product ( ( "+" | "-" ) product )*
34	product		:= unary ( ( "*" | "/" | "%" ) unary )*
35	unary		:= atom | ( "-"  unary ) | dereference
36	dereference	:= "*" [ "{" expression "}" ] unary
37	atom		:= variable | ( "(" expression ")" ) | ( "[" command "]" )
38	variable	:= identifier
39	identifier	:= ( "$" | "@" | "_" | "a" - "z" | "A" - "Z" )
40				   ( "_" | "a" - "z" | "A" - "Z" | "0" - "9" )*
41	commandPipe	:= command ( "|" command )*
42	command		:= identifier argument*
43	argument	:= ( "(" expression ")" ) | ( "[" commandLine "]" )
44				   | unquotedString | quotedString
45*/
46
47
48static const int kMaxTokenLength = 128;
49static const int kJumpBufferCount = 10;
50
51static const int kMaxArgumentCount = 64;
52
53static jmp_buf sJumpBuffers[kJumpBufferCount];
54static int sNextJumpBufferIndex = 0;
55
56static char sExceptionMessage[128];
57static int	sExceptionPosition;
58
59static char sTempBuffer[128];
60	// for composing debug output etc.
61
62enum {
63	TOKEN_ASSIGN_FLAG			= 0x100,
64	TOKEN_FLAGS					= TOKEN_ASSIGN_FLAG,
65
66	TOKEN_IDENTIFIER			= 'a',
67
68	TOKEN_CONSTANT				= '0',
69
70	TOKEN_PLUS					= '+',
71	TOKEN_MINUS					= '-',
72
73	TOKEN_STAR					= '*',
74	TOKEN_SLASH					= '/',
75	TOKEN_MODULO				= '%',
76
77	TOKEN_ASSIGN				= '='			| TOKEN_ASSIGN_FLAG,
78	TOKEN_PLUS_ASSIGN			= TOKEN_PLUS	| TOKEN_ASSIGN_FLAG,
79	TOKEN_MINUS_ASSIGN			= TOKEN_MINUS	| TOKEN_ASSIGN_FLAG,
80	TOKEN_STAR_ASSIGN			= TOKEN_STAR	| TOKEN_ASSIGN_FLAG,
81	TOKEN_SLASH_ASSIGN			= TOKEN_SLASH	| TOKEN_ASSIGN_FLAG,
82	TOKEN_MODULO_ASSIGN			= TOKEN_MODULO	| TOKEN_ASSIGN_FLAG,
83
84	TOKEN_OPENING_PARENTHESIS	= '(',
85	TOKEN_CLOSING_PARENTHESIS	= ')',
86	TOKEN_OPENING_BRACKET		= '[',
87	TOKEN_CLOSING_BRACKET		= ']',
88	TOKEN_OPENING_BRACE			= '{',
89	TOKEN_CLOSING_BRACE			= '}',
90
91	TOKEN_PIPE					= '|',
92	TOKEN_SEMICOLON				= ';',
93
94	TOKEN_STRING				= '"',
95	TOKEN_UNKNOWN				= '?',
96	TOKEN_NONE					= ' ',
97	TOKEN_END_OF_LINE			= '\n',
98};
99
100struct Token {
101	char	string[kMaxTokenLength];
102	uint64	value;
103	int32	type;
104	int32	position;
105
106	void SetTo(const char* string, int32 length, int32 position, int32 type)
107	{
108		length = min_c((size_t)length, (sizeof(this->string) - 1));
109		strlcpy(this->string, string, length + 1);
110		this->type = type;
111		this->value = 0;
112		this->position = position;
113	}
114
115	void Unset()
116	{
117		string[0] = '\0';
118		value = 0;
119		type = TOKEN_NONE;
120		position = 0;
121	}
122};
123
124
125// #pragma mark - exceptions
126
127
128static void
129parse_exception(const char* message, int32 position)
130{
131	if (sNextJumpBufferIndex == 0) {
132		kprintf_unfiltered("parse_exception(): No jump buffer!\n");
133		kprintf_unfiltered("exception: \"%s\", position: %" B_PRId32 "\n",
134			message, position);
135		return;
136	}
137
138	strlcpy(sExceptionMessage, message, sizeof(sExceptionMessage));
139	sExceptionPosition = position;
140
141	longjmp(sJumpBuffers[sNextJumpBufferIndex - 1], 1);
142}
143
144
145static void*
146checked_malloc(size_t size)
147{
148	void* address = debug_malloc(size);
149	if (address == NULL) {
150		parse_exception("out of memory for command execution", -1);
151		return NULL;
152	}
153
154	return address;
155}
156
157
158// #pragma mark - Tokenizer
159
160
161class Tokenizer {
162public:
163	Tokenizer(const char* string)
164		: fCommandMode(false)
165	{
166		SetTo(string);
167	}
168
169	void SetTo(const char* string)
170	{
171		fString = fCurrentChar = string;
172		fCurrentToken.Unset();
173		fReuseToken = false;
174	}
175
176	void SetPosition(int32 position)
177	{
178		fCurrentChar = fString + position;
179		fCurrentToken.Unset();
180		fReuseToken = false;
181	}
182
183	void SetCommandMode(bool commandMode)
184	{
185		if (fCommandMode == commandMode)
186			return;
187
188		fCommandMode = commandMode;
189
190		if (fReuseToken) {
191			// We can't reuse the token, since the parsing mode changed.
192			SetPosition(fCurrentToken.position);
193		}
194	}
195
196	const char* String() const
197	{
198		return fString;
199	}
200
201	const Token& NextToken()
202	{
203		if (fCurrentToken.type == TOKEN_END_OF_LINE)
204			return fCurrentToken;
205
206		if (fReuseToken) {
207			fReuseToken = false;
208			return fCurrentToken;
209		}
210
211		while (*fCurrentChar != 0 && isspace(*fCurrentChar))
212			fCurrentChar++;
213
214		if (*fCurrentChar == 0) {
215			fCurrentToken.SetTo("", 0, _CurrentPos(), TOKEN_END_OF_LINE);
216			return fCurrentToken;
217		}
218
219		return (fCommandMode ? _NextTokenCommand() : _NextTokenExpression());
220	}
221
222	const Token& CurrentToken() const
223	{
224		return fCurrentToken;
225	}
226
227	void RewindToken()
228	{
229		fReuseToken = true;
230	}
231
232 private:
233	const Token& _NextTokenExpression()
234	{
235		if (isdigit(*fCurrentChar)) {
236			// number
237			const char* begin = fCurrentChar++;
238
239			if (*fCurrentChar == 'x') {
240				// hex number
241				fCurrentChar++;
242				while (*fCurrentChar != 0
243					&& (isdigit(*fCurrentChar)
244						|| strchr("abcdefABCDEF", *fCurrentChar))) {
245					fCurrentChar++;
246				}
247
248				if (fCurrentChar - begin == 2)
249					parse_exception("invalid hex number", begin - fString);
250
251			} else {
252				// decimal number
253				while (*fCurrentChar != 0 && isdigit(*fCurrentChar))
254					fCurrentChar++;
255			}
256
257			int32 length = fCurrentChar - begin;
258			fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
259				TOKEN_CONSTANT);
260			fCurrentToken.value = strtoull(fCurrentToken.string, NULL, 0);
261
262		} else if (isalpha(*fCurrentChar) || *fCurrentChar == '_'
263				|| *fCurrentChar == '$' || *fCurrentChar == '@') {
264			// identifier
265			const char* begin = fCurrentChar;
266			fCurrentChar++;
267			while (*fCurrentChar != 0
268				&& (isalpha(*fCurrentChar) || *fCurrentChar == '_'
269					|| isdigit(*fCurrentChar))) {
270				fCurrentChar++;
271			}
272
273			int32 length = fCurrentChar - begin;
274			fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
275				TOKEN_IDENTIFIER);
276
277		} else {
278			const char* begin = fCurrentChar;
279			char c = *fCurrentChar;
280			fCurrentChar++;
281			int32 flags = 0;
282
283			switch (c) {
284				case '=':
285					fCurrentChar--;
286				case '+':
287				case '-':
288				case '*':
289				case '/':
290				case '%':
291					if (*fCurrentChar == '=') {
292						fCurrentChar++;
293						flags = TOKEN_ASSIGN_FLAG;
294					}
295
296				case '(':
297				case ')':
298				case '[':
299				case ']':
300				case '{':
301				case '}':
302				case ';':
303				{
304					int32 length = fCurrentChar - begin;
305					fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
306						c | flags);
307					break;
308				}
309
310				case '"':
311				{
312					fCurrentChar--;
313					_QuotedString();
314					break;
315				}
316
317				default:
318				{
319					fCurrentChar--;
320					_UnquotedString();
321					break;
322				}
323			}
324		}
325
326		return fCurrentToken;
327	}
328
329	const Token& _NextTokenCommand()
330	{
331		switch (*fCurrentChar) {
332			case '(':
333			case ')':
334			case '[':
335			case ']':
336			case '|':
337			case ';':
338				fCurrentToken.SetTo(fCurrentChar, 1, _CurrentPos(),
339					*fCurrentChar);
340				fCurrentChar++;
341				return fCurrentToken;
342			case '"':
343				return _QuotedString();
344
345			default:
346				return _UnquotedString();
347		}
348	}
349
350	const Token& _QuotedString()
351	{
352		const char* begin = fCurrentChar++;
353		int32 length = 0;
354
355		while (*fCurrentChar != '\0' && *fCurrentChar != '"') {
356			char c = *fCurrentChar;
357			fCurrentChar++;
358
359			if (c == '\\') {
360				// an escaped char
361				c = *fCurrentChar;
362				fCurrentChar++;
363
364				if (c == '\0')
365					break;
366			}
367
368			if ((size_t)length
369					>= sizeof(fCurrentToken.string) - 1) {
370				parse_exception("quoted string too long", begin - fString);
371			}
372
373			fCurrentToken.string[length++] = c;
374		}
375
376		if (*fCurrentChar == '\0') {
377			parse_exception("unexpected end of line while "
378				"parsing quoted string", begin - fString);
379		}
380
381		fCurrentChar++;
382
383		fCurrentToken.string[length] = '\0';
384		fCurrentToken.value = 0;
385		fCurrentToken.type = TOKEN_STRING;
386		fCurrentToken.position = begin - fString;
387
388		return fCurrentToken;
389	}
390
391	const Token& _UnquotedString()
392	{
393		const char* begin = fCurrentChar;
394
395		while (*fCurrentChar != 0 && !_IsUnquotedDelimitingChar(*fCurrentChar))
396			fCurrentChar++;
397
398		int32 length = fCurrentChar - begin;
399		fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
400			TOKEN_UNKNOWN);
401
402		return fCurrentToken;
403	}
404
405	bool _IsUnquotedDelimitingChar(char c)
406	{
407		if (isspace(c))
408			return true;
409
410		switch (c) {
411			case '(':
412			case ')':
413			case '[':
414			case ']':
415			case '"':
416				return true;
417
418			case '|':	// TODO: Move when we support & and | in expressions.
419			case ';':
420				return fCommandMode;
421
422			case '{':
423			case '}':
424			case '=':
425			case '+':
426			case '-':
427			case '*':
428			case '/':
429			case '%':
430				return !fCommandMode;
431
432			default:
433				return false;
434		}
435	}
436
437	int32 _CurrentPos() const
438	{
439		return fCurrentChar - fString;
440	}
441
442private:
443	const char*	fString;
444	const char*	fCurrentChar;
445	Token		fCurrentToken;
446	bool		fReuseToken;
447	bool		fCommandMode;
448};
449
450
451// #pragma mark - ExpressionParser
452
453
454class ExpressionParser {
455 public:
456								ExpressionParser();
457								~ExpressionParser();
458
459			uint64				EvaluateExpression(
460									const char* expressionString);
461			uint64				EvaluateCommand(
462									const char* expressionString,
463									int& returnCode);
464			status_t			ParseNextCommandArgument(
465									const char** expressionString, char* buffer,
466									size_t bufferSize);
467
468 private:
469			uint64				_ParseExpression(bool expectAssignment = false);
470			uint64				_ParseCommandPipe(int& returnCode);
471			void				_ParseCommand(
472									debugger_command_pipe_segment& segment);
473			bool				_ParseArgument(int& argc, char** argv);
474			void				_GetUnparsedArgument(int& argc, char** argv);
475			void				_AddArgument(int& argc, char** argv,
476									const char* argument, int32 length = -1);
477			uint64				_ParseSum(bool useValue, uint64 value);
478			uint64				_ParseProduct();
479			uint64				_ParsePower();
480			uint64				_ParseUnary();
481			uint64				_ParseDereference(void** _address,
482									uint32* _size);
483			uint64				_ParseAtom();
484
485			const Token&		_EatToken(int32 type);
486
487			Tokenizer			fTokenizer;
488};
489
490
491ExpressionParser::ExpressionParser()
492	: fTokenizer("")
493{
494}
495
496
497ExpressionParser::~ExpressionParser()
498{
499}
500
501
502uint64
503ExpressionParser::EvaluateExpression(const char* expressionString)
504{
505	fTokenizer.SetTo(expressionString);
506
507	uint64 value = _ParseExpression();
508	const Token& token = fTokenizer.NextToken();
509	if (token.type != TOKEN_END_OF_LINE)
510		parse_exception("parse error", token.position);
511
512	return value;
513}
514
515
516uint64
517ExpressionParser::EvaluateCommand(const char* expressionString, int& returnCode)
518{
519	fTokenizer.SetTo(expressionString);
520
521	// Allowed are command or assignment. A command always starts with an
522	// identifier, an assignment either with an identifier (variable name) or
523	// a dereferenced address.
524	const Token& token = fTokenizer.NextToken();
525	uint64 value = 0;
526
527	while (true) {
528		int32 startPosition = token.position;
529
530		if (token.type == TOKEN_IDENTIFIER) {
531			fTokenizer.NextToken();
532
533			if (token.type & TOKEN_ASSIGN_FLAG) {
534				// an assignment
535				fTokenizer.SetPosition(startPosition);
536				value =  _ParseExpression(true);
537				returnCode = 0;
538			} else {
539				// no assignment, so let's assume it's a command
540				fTokenizer.SetPosition(startPosition);
541				fTokenizer.SetCommandMode(true);
542				value = _ParseCommandPipe(returnCode);
543			}
544		} else if (token.type == TOKEN_STAR) {
545			// dereferenced address -- assignment
546			fTokenizer.SetPosition(startPosition);
547			value =  _ParseExpression(true);
548			returnCode = 0;
549		} else
550			parse_exception("expected command or assignment", token.position);
551
552		// might be chained with ";"
553		if (fTokenizer.NextToken().type != TOKEN_SEMICOLON)
554			break;
555
556		fTokenizer.SetCommandMode(false);
557		fTokenizer.NextToken();
558	}
559
560	if (token.type != TOKEN_END_OF_LINE)
561		parse_exception("parse error", token.position);
562
563	return value;
564}
565
566
567status_t
568ExpressionParser::ParseNextCommandArgument(const char** expressionString,
569	char* buffer, size_t bufferSize)
570{
571	fTokenizer.SetTo(*expressionString);
572	fTokenizer.SetCommandMode(true);
573
574	if (fTokenizer.NextToken().type == TOKEN_END_OF_LINE)
575		return B_ENTRY_NOT_FOUND;
576
577	fTokenizer.RewindToken();
578
579	char* argv[2];
580	int argc = 0;
581	if (!_ParseArgument(argc, argv))
582		return B_BAD_VALUE;
583
584	strlcpy(buffer, argv[0], bufferSize);
585
586	const Token& token = fTokenizer.NextToken();
587	if (token.type == TOKEN_END_OF_LINE)
588		*expressionString = NULL;
589	else
590		*expressionString += token.position;
591
592	return B_OK;
593}
594
595
596uint64
597ExpressionParser::_ParseExpression(bool expectAssignment)
598{
599	const Token& token = fTokenizer.NextToken();
600	int32 position = token.position;
601	if (token.type == TOKEN_IDENTIFIER) {
602		char variable[MAX_DEBUG_VARIABLE_NAME_LEN];
603		strlcpy(variable, token.string, sizeof(variable));
604
605		int32 assignmentType = fTokenizer.NextToken().type;
606		if (assignmentType & TOKEN_ASSIGN_FLAG) {
607			// an assignment
608			uint64 rhs = _ParseExpression();
609
610			// handle the standard assignment separately -- the other kinds
611			// need the variable to be defined
612			if (assignmentType == TOKEN_ASSIGN) {
613				if (!set_debug_variable(variable, rhs)) {
614					snprintf(sTempBuffer, sizeof(sTempBuffer),
615						"failed to set value for variable \"%s\"",
616						variable);
617					parse_exception(sTempBuffer, position);
618				}
619
620				return rhs;
621			}
622
623			// variable must be defined
624			if (!is_debug_variable_defined(variable)) {
625				snprintf(sTempBuffer, sizeof(sTempBuffer),
626					"variable \"%s\" not defined in modifying assignment",
627					variable);
628				parse_exception(sTempBuffer, position);
629			}
630
631			uint64 variableValue = get_debug_variable(variable, 0);
632
633			// check for division by zero for the respective assignment types
634			if ((assignmentType == TOKEN_SLASH_ASSIGN
635					|| assignmentType == TOKEN_MODULO_ASSIGN)
636				&& rhs == 0) {
637				parse_exception("division by zero", position);
638			}
639
640			// compute the new variable value
641			switch (assignmentType) {
642				case TOKEN_PLUS_ASSIGN:
643					variableValue += rhs;
644					break;
645				case TOKEN_MINUS_ASSIGN:
646					variableValue -= rhs;
647					break;
648				case TOKEN_STAR_ASSIGN:
649					variableValue *= rhs;
650					break;
651				case TOKEN_SLASH_ASSIGN:
652					variableValue /= rhs;
653					break;
654				case TOKEN_MODULO_ASSIGN:
655					variableValue %= rhs;
656					break;
657				default:
658					parse_exception("internal error: unknown assignment token",
659						position);
660					break;
661			}
662
663			set_debug_variable(variable, variableValue);
664			return variableValue;
665		}
666	} else if (token.type == TOKEN_STAR) {
667		void* address;
668		uint32 size;
669		uint64 value = _ParseDereference(&address, &size);
670
671		int32 assignmentType = fTokenizer.NextToken().type;
672		if (assignmentType & TOKEN_ASSIGN_FLAG) {
673			// an assignment
674			uint64 rhs = _ParseExpression();
675
676			// check for division by zero for the respective assignment types
677			if ((assignmentType == TOKEN_SLASH_ASSIGN
678					|| assignmentType == TOKEN_MODULO_ASSIGN)
679				&& rhs == 0) {
680				parse_exception("division by zero", position);
681			}
682
683			// compute the new value
684			switch (assignmentType) {
685				case TOKEN_ASSIGN:
686					value = rhs;
687					break;
688				case TOKEN_PLUS_ASSIGN:
689					value += rhs;
690					break;
691				case TOKEN_MINUS_ASSIGN:
692					value -= rhs;
693					break;
694				case TOKEN_STAR_ASSIGN:
695					value *= rhs;
696					break;
697				case TOKEN_SLASH_ASSIGN:
698					value /= rhs;
699					break;
700				case TOKEN_MODULO_ASSIGN:
701					value %= rhs;
702					break;
703				default:
704					parse_exception("internal error: unknown assignment token",
705						position);
706					break;
707			}
708
709			// convert the value for writing to the address
710			uint64 buffer = 0;
711			switch (size) {
712				case 1:
713					*(uint8*)&buffer = value;
714					break;
715				case 2:
716					*(uint16*)&buffer = value;
717					break;
718				case 4:
719					*(uint32*)&buffer = value;
720					break;
721				case 8:
722					value = buffer;
723					break;
724			}
725
726			if (debug_memcpy(B_CURRENT_TEAM, address, &buffer, size) != B_OK) {
727				snprintf(sTempBuffer, sizeof(sTempBuffer),
728					"failed to write to address %p", address);
729				parse_exception(sTempBuffer, position);
730			}
731
732			return value;
733		}
734	}
735
736	if (expectAssignment) {
737		parse_exception("expected assignment",
738			fTokenizer.CurrentToken().position);
739	}
740
741	// no assignment -- reset to the identifier position and parse a sum
742	fTokenizer.SetPosition(position);
743	return _ParseSum(false, 0);
744}
745
746
747uint64
748ExpressionParser::_ParseCommandPipe(int& returnCode)
749{
750	debugger_command_pipe* pipe = (debugger_command_pipe*)checked_malloc(
751		sizeof(debugger_command_pipe));
752
753	pipe->segment_count = 0;
754	pipe->broken = false;
755
756	do {
757		if (pipe->segment_count >= MAX_DEBUGGER_COMMAND_PIPE_LENGTH)
758			parse_exception("Pipe too long", fTokenizer.NextToken().position);
759
760		debugger_command_pipe_segment& segment
761			= pipe->segments[pipe->segment_count];
762		segment.index = pipe->segment_count++;
763
764		_ParseCommand(segment);
765
766	} while (fTokenizer.NextToken().type == TOKEN_PIPE);
767
768	fTokenizer.RewindToken();
769
770	// invoke the pipe
771	returnCode = invoke_debugger_command_pipe(pipe);
772
773	debug_free(pipe);
774
775	return get_debug_variable("_", 0);
776}
777
778
779void
780ExpressionParser::_ParseCommand(debugger_command_pipe_segment& segment)
781{
782	fTokenizer.SetCommandMode(false);
783	const Token& token = _EatToken(TOKEN_IDENTIFIER);
784	fTokenizer.SetCommandMode(true);
785
786	bool ambiguous;
787	debugger_command* command = find_debugger_command(token.string, true,
788		ambiguous);
789
790	if (command == NULL) {
791		if (ambiguous) {
792			snprintf(sTempBuffer, sizeof(sTempBuffer),
793				"Ambiguous command \"%s\". Use tab completion or enter "
794				"\"help %s\" get a list of matching commands.\n", token.string,
795				token.string);
796		} else {
797			snprintf(sTempBuffer, sizeof(sTempBuffer),
798				"Unknown command \"%s\". Enter \"help\" to get a list of "
799				"all supported commands.\n", token.string);
800		}
801
802		parse_exception(sTempBuffer, -1);
803	}
804
805	// allocate temporary buffer for the argument vector
806	char** argv = (char**)checked_malloc(kMaxArgumentCount * sizeof(char*));
807	int argc = 0;
808	argv[argc++] = (char*)command->name;
809
810	// get the arguments
811	if ((command->flags & B_KDEBUG_DONT_PARSE_ARGUMENTS) != 0) {
812		_GetUnparsedArgument(argc, argv);
813	} else {
814		while (fTokenizer.NextToken().type != TOKEN_END_OF_LINE) {
815			fTokenizer.RewindToken();
816			if (!_ParseArgument(argc, argv))
817				break;
818		}
819	}
820
821	if (segment.index > 0) {
822		if (argc >= kMaxArgumentCount)
823			parse_exception("too many arguments for command", 0);
824		else
825			argc++;
826	}
827
828	segment.command = command;
829	segment.argc = argc;
830	segment.argv = argv;
831	segment.invocations = 0;
832}
833
834
835bool
836ExpressionParser::_ParseArgument(int& argc, char** argv)
837{
838	const Token& token = fTokenizer.NextToken();
839	switch (token.type) {
840		case TOKEN_OPENING_PARENTHESIS:
841		{
842			// this starts an expression
843			fTokenizer.SetCommandMode(false);
844			uint64 value = _ParseExpression();
845			fTokenizer.SetCommandMode(true);
846			_EatToken(TOKEN_CLOSING_PARENTHESIS);
847
848			snprintf(sTempBuffer, sizeof(sTempBuffer), "%" B_PRIu64, value);
849			_AddArgument(argc, argv, sTempBuffer);
850			return true;
851		}
852
853		case TOKEN_OPENING_BRACKET:
854		{
855			// this starts a sub command
856			int returnValue;
857			uint64 value = _ParseCommandPipe(returnValue);
858			_EatToken(TOKEN_CLOSING_BRACKET);
859
860			snprintf(sTempBuffer, sizeof(sTempBuffer), "%" B_PRIu64, value);
861			_AddArgument(argc, argv, sTempBuffer);
862			return true;
863		}
864
865		case TOKEN_STRING:
866		case TOKEN_UNKNOWN:
867			_AddArgument(argc, argv, token.string);
868			return true;
869
870		case TOKEN_CLOSING_PARENTHESIS:
871		case TOKEN_CLOSING_BRACKET:
872		case TOKEN_PIPE:
873		case TOKEN_SEMICOLON:
874			// those don't belong to us
875			fTokenizer.RewindToken();
876			return false;
877
878		default:
879		{
880			snprintf(sTempBuffer, sizeof(sTempBuffer), "unexpected token "
881				"\"%s\"", token.string);
882			parse_exception(sTempBuffer, token.position);
883			return false;
884		}
885	}
886}
887
888
889void
890ExpressionParser::_GetUnparsedArgument(int& argc, char** argv)
891{
892	int32 startPosition = fTokenizer.NextToken().position;
893	fTokenizer.RewindToken();
894
895	// match parentheses and brackets, but otherwise skip all tokens
896	int32 parentheses = 0;
897	int32 brackets = 0;
898	bool done = false;
899	while (!done) {
900		const Token& token = fTokenizer.NextToken();
901		switch (token.type) {
902			case TOKEN_OPENING_PARENTHESIS:
903				parentheses++;
904				break;
905			case TOKEN_OPENING_BRACKET:
906				brackets++;
907				break;
908			case TOKEN_CLOSING_PARENTHESIS:
909				if (parentheses > 0)
910					parentheses--;
911				else
912					done = true;
913				break;
914			case TOKEN_CLOSING_BRACKET:
915				if (brackets > 0)
916					brackets--;
917				else
918					done = true;
919				break;
920			case TOKEN_PIPE:
921			case TOKEN_SEMICOLON:
922				if (parentheses == 0 && brackets == 0)
923					done = true;
924				break;
925			case TOKEN_END_OF_LINE:
926				done = true;
927				break;
928		}
929	}
930
931	int32 endPosition = fTokenizer.CurrentToken().position;
932	fTokenizer.RewindToken();
933
934	// add the argument only, if it's not just all spaces
935	const char* arg = fTokenizer.String() + startPosition;
936	int32 argLen = endPosition - startPosition;
937	bool allSpaces = true;
938	for (int32 i = 0; allSpaces && i < argLen; i++)
939		allSpaces = isspace(arg[i]);
940
941	if (!allSpaces)
942		_AddArgument(argc, argv, arg, argLen);
943}
944
945
946void
947ExpressionParser::_AddArgument(int& argc, char** argv, const char* argument,
948	int32 length)
949{
950	if (argc == kMaxArgumentCount)
951		parse_exception("too many arguments for command", 0);
952
953	if (length < 0)
954		length = strlen(argument);
955	length++;
956	char* buffer = (char*)checked_malloc(length);
957	strlcpy(buffer, argument, length);
958
959	argv[argc++] = buffer;
960}
961
962
963uint64
964ExpressionParser::_ParseSum(bool useValue, uint64 value)
965{
966	if (!useValue)
967		value = _ParseProduct();
968
969	while (true) {
970		const Token& token = fTokenizer.NextToken();
971		switch (token.type) {
972			case TOKEN_PLUS:
973				value = value + _ParseProduct();
974				break;
975			case TOKEN_MINUS:
976				value = value - _ParseProduct();
977				break;
978
979			default:
980				fTokenizer.RewindToken();
981				return value;
982		}
983	}
984}
985
986
987uint64
988ExpressionParser::_ParseProduct()
989{
990	uint64 value = _ParseUnary();
991
992	while (true) {
993		Token token = fTokenizer.NextToken();
994		switch (token.type) {
995			case TOKEN_STAR:
996				value = value * _ParseUnary();
997				break;
998			case TOKEN_SLASH: {
999				uint64 rhs = _ParseUnary();
1000				if (rhs == 0)
1001					parse_exception("division by zero", token.position);
1002				value = value / rhs;
1003				break;
1004			}
1005			case TOKEN_MODULO: {
1006				uint64 rhs = _ParseUnary();
1007				if (rhs == 0)
1008					parse_exception("modulo by zero", token.position);
1009				value = value % rhs;
1010				break;
1011			}
1012
1013			default:
1014				fTokenizer.RewindToken();
1015				return value;
1016		}
1017	}
1018}
1019
1020
1021uint64
1022ExpressionParser::_ParseUnary()
1023{
1024	switch (fTokenizer.NextToken().type) {
1025		case TOKEN_MINUS:
1026			return -_ParseUnary();
1027
1028		case TOKEN_STAR:
1029			return _ParseDereference(NULL, NULL);
1030
1031		default:
1032			fTokenizer.RewindToken();
1033			return _ParseAtom();
1034	}
1035
1036	return 0;
1037}
1038
1039
1040uint64
1041ExpressionParser::_ParseDereference(void** _address, uint32* _size)
1042{
1043	int32 starPosition = fTokenizer.CurrentToken().position;
1044
1045	// optional "{ ... }" specifying the size to read
1046	uint64 size = 4;
1047	if (fTokenizer.NextToken().type == TOKEN_OPENING_BRACE) {
1048		int32 position = fTokenizer.CurrentToken().position;
1049		size = _ParseExpression();
1050
1051		if (size != 1 && size != 2 && size != 4 && size != 8) {
1052			snprintf(sTempBuffer, sizeof(sTempBuffer),
1053				"invalid size (%" B_PRIu64 ") for unary * operator", size);
1054			parse_exception(sTempBuffer, position);
1055		}
1056
1057		_EatToken(TOKEN_CLOSING_BRACE);
1058	} else
1059		fTokenizer.RewindToken();
1060
1061	const void* address = (const void*)(addr_t)_ParseUnary();
1062
1063	// read bytes from address into a tempory buffer
1064	uint64 buffer;
1065	if (debug_memcpy(B_CURRENT_TEAM, &buffer, address, size) != B_OK) {
1066		snprintf(sTempBuffer, sizeof(sTempBuffer),
1067			"failed to dereference address %p", address);
1068		parse_exception(sTempBuffer, starPosition);
1069	}
1070
1071	// convert the value to uint64
1072	uint64 value = 0;
1073	switch (size) {
1074		case 1:
1075			value = *(uint8*)&buffer;
1076			break;
1077		case 2:
1078			value = *(uint16*)&buffer;
1079			break;
1080		case 4:
1081			value = *(uint32*)&buffer;
1082			break;
1083		case 8:
1084			value = buffer;
1085			break;
1086	}
1087
1088	if (_address != NULL)
1089		*_address = (void*)address;
1090	if (_size != NULL)
1091		*_size = size;
1092
1093	return value;
1094}
1095
1096
1097uint64
1098ExpressionParser::_ParseAtom()
1099{
1100	const Token& token = fTokenizer.NextToken();
1101	if (token.type == TOKEN_END_OF_LINE)
1102		parse_exception("unexpected end of expression", token.position);
1103
1104	if (token.type == TOKEN_CONSTANT)
1105		return token.value;
1106
1107	if (token.type == TOKEN_IDENTIFIER) {
1108		if (!is_debug_variable_defined(token.string)) {
1109			snprintf(sTempBuffer, sizeof(sTempBuffer),
1110				"variable '%s' undefined", token.string);
1111			parse_exception(sTempBuffer, token.position);
1112		}
1113
1114		return get_debug_variable(token.string, 0);
1115	}
1116
1117	if (token.type == TOKEN_OPENING_PARENTHESIS) {
1118		uint64 value = _ParseExpression();
1119
1120		_EatToken(TOKEN_CLOSING_PARENTHESIS);
1121
1122		return value;
1123	}
1124
1125	// it can only be a "[ command ]" expression now
1126	fTokenizer.RewindToken();
1127
1128	_EatToken(TOKEN_OPENING_BRACKET);
1129
1130	fTokenizer.SetCommandMode(true);
1131	int returnValue;
1132	uint64 value = _ParseCommandPipe(returnValue);
1133	fTokenizer.SetCommandMode(false);
1134
1135	_EatToken(TOKEN_CLOSING_BRACKET);
1136
1137	return value;
1138}
1139
1140
1141const Token&
1142ExpressionParser::_EatToken(int32 type)
1143{
1144	const Token& token = fTokenizer.NextToken();
1145	if (token.type != type) {
1146		snprintf(sTempBuffer, sizeof(sTempBuffer), "expected token type '%c', "
1147			"got token '%s'", char(type & ~TOKEN_FLAGS), token.string);
1148		parse_exception(sTempBuffer, token.position);
1149	}
1150
1151	return token;
1152}
1153
1154
1155
1156// #pragma mark -
1157
1158
1159bool
1160evaluate_debug_expression(const char* expression, uint64* _result, bool silent)
1161{
1162	if (sNextJumpBufferIndex >= kJumpBufferCount) {
1163		kprintf_unfiltered("evaluate_debug_expression(): Out of jump buffers "
1164			"for exception handling\n");
1165		return 0;
1166	}
1167
1168	bool success;
1169	uint64 result;
1170	DebugAllocPoolScope allocPoolScope;
1171		// Will clean up all allocations when we return.
1172
1173	if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
1174		result = ExpressionParser().EvaluateExpression(expression);
1175		success = true;
1176	} else {
1177		result = 0;
1178		success = false;
1179		if (!silent) {
1180			if (sExceptionPosition >= 0) {
1181				kprintf_unfiltered("%s, at position: %d, in expression: %s\n",
1182					sExceptionMessage, sExceptionPosition, expression);
1183			} else
1184				kprintf_unfiltered("%s\n", sExceptionMessage);
1185		}
1186	}
1187
1188	sNextJumpBufferIndex--;
1189
1190	if (success && _result != NULL)
1191		*_result = result;
1192
1193	return success;
1194}
1195
1196
1197int
1198evaluate_debug_command(const char* commandLine)
1199{
1200	if (sNextJumpBufferIndex >= kJumpBufferCount) {
1201		kprintf_unfiltered("evaluate_debug_command(): Out of jump buffers for "
1202			"exception handling\n");
1203		return 0;
1204	}
1205
1206	int returnCode = 0;
1207	DebugAllocPoolScope allocPoolScope;
1208		// Will clean up all allocations when we return.
1209
1210	if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
1211		ExpressionParser().EvaluateCommand(commandLine, returnCode);
1212	} else {
1213		if (sExceptionPosition >= 0) {
1214			kprintf_unfiltered("%s, at position: %d, in command line: %s\n",
1215				sExceptionMessage, sExceptionPosition, commandLine);
1216		} else
1217			kprintf_unfiltered("%s\n", sExceptionMessage);
1218	}
1219
1220	sNextJumpBufferIndex--;
1221
1222	return returnCode;
1223}
1224
1225
1226status_t
1227parse_next_debug_command_argument(const char** expressionString, char* buffer,
1228	size_t bufferSize)
1229{
1230	if (sNextJumpBufferIndex >= kJumpBufferCount) {
1231		kprintf_unfiltered("parse_next_debug_command_argument(): Out of jump "
1232			"buffers for exception handling\n");
1233		return B_ERROR;
1234	}
1235
1236	status_t error;
1237	DebugAllocPoolScope allocPoolScope;
1238		// Will clean up all allocations when we return.
1239
1240	if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
1241		error = ExpressionParser().ParseNextCommandArgument(expressionString,
1242			buffer, bufferSize);
1243	} else {
1244		if (sExceptionPosition >= 0) {
1245			kprintf_unfiltered("%s, at position: %d, in command line: %s\n",
1246				sExceptionMessage, sExceptionPosition, *expressionString);
1247		} else
1248			kprintf_unfiltered("%s\n", sExceptionMessage);
1249		error = B_BAD_VALUE;
1250	}
1251
1252	sNextJumpBufferIndex--;
1253
1254	return error;
1255}
1256