VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGCEval.cpp@ 35632

Last change on this file since 35632 was 35632, checked in by vboxsync, 14 years ago

DBGC: Simplified the status code handling around dbgcEvalCommand.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.3 KB
Line 
1/* $Id: DBGCEval.cpp 35632 2011-01-19 15:55:05Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, command evaluator.
4 */
5
6/*
7 * Copyright (C) 2006-2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_DBGC
22#include <VBox/dbg.h>
23#include <VBox/err.h>
24#include <VBox/log.h>
25
26#include <iprt/asm.h>
27#include <iprt/assert.h>
28#include <iprt/string.h>
29#include <iprt/ctype.h>
30
31#include "DBGCInternal.h"
32
33
34/*******************************************************************************
35* Global Variables *
36*******************************************************************************/
37/** Bitmap where set bits indicates the characters the may start an operator name. */
38static uint32_t g_bmOperatorChars[256 / (4*8)];
39
40
41
42/**
43 * Initializes g_bmOperatorChars.
44 */
45void dbgcEvalInit(void)
46{
47 memset(g_bmOperatorChars, 0, sizeof(g_bmOperatorChars));
48 for (unsigned iOp = 0; iOp < g_cOps; iOp++)
49 ASMBitSet(&g_bmOperatorChars[0], (uint8_t)g_aOps[iOp].szName[0]);
50}
51
52
53/**
54 * Checks whether the character may be the start of an operator.
55 *
56 * @returns true/false.
57 * @param ch The character.
58 */
59DECLINLINE(bool) dbgcIsOpChar(char ch)
60{
61 return ASMBitTest(&g_bmOperatorChars[0], (uint8_t)ch);
62}
63
64
65static int dbgcEvalSubString(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pArg)
66{
67 Log2(("dbgcEvalSubString: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
68
69 /*
70 * Removing any quoting and escapings.
71 */
72 char ch = *pszExpr;
73 if (ch == '"' || ch == '\'' || ch == '`')
74 {
75 if (pszExpr[--cchExpr] != ch)
76 return VERR_PARSE_UNBALANCED_QUOTE;
77 cchExpr--;
78 pszExpr++;
79
80 /** @todo string unescaping. */
81 }
82 pszExpr[cchExpr] = '\0';
83
84 /*
85 * Make the argument.
86 */
87 pArg->pDesc = NULL;
88 pArg->pNext = NULL;
89 pArg->enmType = DBGCVAR_TYPE_STRING;
90 pArg->u.pszString = pszExpr;
91 pArg->enmRangeType = DBGCVAR_RANGE_BYTES;
92 pArg->u64Range = cchExpr;
93
94 NOREF(pDbgc);
95 return VINF_SUCCESS;
96}
97
98
99static int dbgcEvalSubNum(char *pszExpr, unsigned uBase, PDBGCVAR pArg)
100{
101 Log2(("dbgcEvalSubNum: uBase=%d pszExpr=%s\n", uBase, pszExpr));
102 /*
103 * Convert to number.
104 */
105 uint64_t u64 = 0;
106 char ch;
107 while ((ch = *pszExpr) != '\0')
108 {
109 uint64_t u64Prev = u64;
110 unsigned u = ch - '0';
111 if (u < 10 && u < uBase)
112 u64 = u64 * uBase + u;
113 else if (ch >= 'a' && (u = ch - ('a' - 10)) < uBase)
114 u64 = u64 * uBase + u;
115 else if (ch >= 'A' && (u = ch - ('A' - 10)) < uBase)
116 u64 = u64 * uBase + u;
117 else
118 return VERR_PARSE_INVALID_NUMBER;
119
120 /* check for overflow - ARG!!! How to detect overflow correctly!?!?!? */
121 if (u64Prev != u64 / uBase)
122 return VERR_PARSE_NUMBER_TOO_BIG;
123
124 /* next */
125 pszExpr++;
126 }
127
128 /*
129 * Initialize the argument.
130 */
131 pArg->pDesc = NULL;
132 pArg->pNext = NULL;
133 pArg->enmType = DBGCVAR_TYPE_NUMBER;
134 pArg->u.u64Number = u64;
135 pArg->enmRangeType = DBGCVAR_RANGE_NONE;
136 pArg->u64Range = 0;
137
138 return VINF_SUCCESS;
139}
140
141
142/**
143 * Match variable and variable descriptor, promoting the variable if necessary.
144 *
145 * @returns VBox status code.
146 * @param pDbgc Debug console instanace.
147 * @param pVar Variable.
148 * @param pVarDesc Variable descriptor.
149 */
150static int dbgcEvalSubMatchVar(PDBGC pDbgc, PDBGCVAR pVar, PCDBGCVARDESC pVarDesc)
151{
152 /*
153 * (If match or promoted to match, return, else break.)
154 */
155 switch (pVarDesc->enmCategory)
156 {
157 /*
158 * Anything goes
159 */
160 case DBGCVAR_CAT_ANY:
161 return VINF_SUCCESS;
162
163 /*
164 * Pointer with and without range.
165 * We can try resolve strings and symbols as symbols and
166 * promote numbers to flat GC pointers.
167 */
168 case DBGCVAR_CAT_POINTER_NO_RANGE:
169 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
170 return VERR_PARSE_NO_RANGE_ALLOWED;
171 /* fallthru */
172 case DBGCVAR_CAT_POINTER:
173 switch (pVar->enmType)
174 {
175 case DBGCVAR_TYPE_GC_FLAT:
176 case DBGCVAR_TYPE_GC_FAR:
177 case DBGCVAR_TYPE_GC_PHYS:
178 case DBGCVAR_TYPE_HC_FLAT:
179 case DBGCVAR_TYPE_HC_PHYS:
180 return VINF_SUCCESS;
181
182 case DBGCVAR_TYPE_SYMBOL:
183 case DBGCVAR_TYPE_STRING:
184 {
185 DBGCVAR Var;
186 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
187 if (RT_SUCCESS(rc))
188 {
189 /* deal with range */
190 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
191 {
192 Var.enmRangeType = pVar->enmRangeType;
193 Var.u64Range = pVar->u64Range;
194 }
195 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
196 Var.enmRangeType = DBGCVAR_RANGE_NONE;
197 *pVar = Var;
198 return rc;
199 }
200 break;
201 }
202
203 case DBGCVAR_TYPE_NUMBER:
204 {
205 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
206 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
207 pVar->u.GCFlat = GCPtr;
208 return VINF_SUCCESS;
209 }
210
211 default:
212 break;
213 }
214 break;
215
216 /*
217 * GC pointer with and without range.
218 * We can try resolve strings and symbols as symbols and
219 * promote numbers to flat GC pointers.
220 */
221 case DBGCVAR_CAT_GC_POINTER_NO_RANGE:
222 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
223 return VERR_PARSE_NO_RANGE_ALLOWED;
224 /* fallthru */
225 case DBGCVAR_CAT_GC_POINTER:
226 switch (pVar->enmType)
227 {
228 case DBGCVAR_TYPE_GC_FLAT:
229 case DBGCVAR_TYPE_GC_FAR:
230 case DBGCVAR_TYPE_GC_PHYS:
231 return VINF_SUCCESS;
232
233 case DBGCVAR_TYPE_HC_FLAT:
234 case DBGCVAR_TYPE_HC_PHYS:
235 return VERR_PARSE_CONVERSION_FAILED;
236
237 case DBGCVAR_TYPE_SYMBOL:
238 case DBGCVAR_TYPE_STRING:
239 {
240 DBGCVAR Var;
241 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
242 if (RT_SUCCESS(rc))
243 {
244 /* deal with range */
245 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
246 {
247 Var.enmRangeType = pVar->enmRangeType;
248 Var.u64Range = pVar->u64Range;
249 }
250 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
251 Var.enmRangeType = DBGCVAR_RANGE_NONE;
252 *pVar = Var;
253 return rc;
254 }
255 break;
256 }
257
258 case DBGCVAR_TYPE_NUMBER:
259 {
260 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
261 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
262 pVar->u.GCFlat = GCPtr;
263 return VINF_SUCCESS;
264 }
265
266 default:
267 break;
268 }
269 break;
270
271 /*
272 * Number with or without a range.
273 * Numbers can be resolved from symbols, but we cannot demote a pointer
274 * to a number.
275 */
276 case DBGCVAR_CAT_NUMBER_NO_RANGE:
277 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
278 return VERR_PARSE_NO_RANGE_ALLOWED;
279 /* fallthru */
280 case DBGCVAR_CAT_NUMBER:
281 switch (pVar->enmType)
282 {
283 case DBGCVAR_TYPE_NUMBER:
284 return VINF_SUCCESS;
285
286 case DBGCVAR_TYPE_SYMBOL:
287 case DBGCVAR_TYPE_STRING:
288 {
289 DBGCVAR Var;
290 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_NUMBER, &Var);
291 if (RT_SUCCESS(rc))
292 {
293 *pVar = Var;
294 return rc;
295 }
296 break;
297 }
298 default:
299 break;
300 }
301 break;
302
303 /*
304 * Strings can easily be made from symbols (and of course strings).
305 * We could consider reformatting the addresses and numbers into strings later...
306 */
307 case DBGCVAR_CAT_STRING:
308 switch (pVar->enmType)
309 {
310 case DBGCVAR_TYPE_SYMBOL:
311 pVar->enmType = DBGCVAR_TYPE_STRING;
312 /* fallthru */
313 case DBGCVAR_TYPE_STRING:
314 return VINF_SUCCESS;
315 default:
316 break;
317 }
318 break;
319
320 /*
321 * Symol is pretty much the same thing as a string (at least until we actually implement it).
322 */
323 case DBGCVAR_CAT_SYMBOL:
324 switch (pVar->enmType)
325 {
326 case DBGCVAR_TYPE_STRING:
327 pVar->enmType = DBGCVAR_TYPE_SYMBOL;
328 /* fallthru */
329 case DBGCVAR_TYPE_SYMBOL:
330 return VINF_SUCCESS;
331 default:
332 break;
333 }
334 break;
335
336 /*
337 * Anything else is illegal.
338 */
339 default:
340 AssertMsgFailed(("enmCategory=%d\n", pVar->enmType));
341 break;
342 }
343
344 return VERR_PARSE_NO_ARGUMENT_MATCH;
345}
346
347
348/**
349 * Matches a set of variables with a description set.
350 *
351 * This is typically used for routine arguments before a call. The effects in
352 * addition to the validation, is that some variables might be propagated to
353 * other types in order to match the description. The following transformations
354 * are supported:
355 * - String reinterpreted as a symbol and resolved to a number or pointer.
356 * - Number to a pointer.
357 * - Pointer to a number.
358 *
359 * @returns VBox status code. Modified @a paVars on success.
360 */
361static int dbgcEvalSubMatchVars(PDBGC pDbgc, unsigned cVarsMin, unsigned cVarsMax,
362 PCDBGCVARDESC paVarDescs, unsigned cVarDescs,
363 PDBGCVAR paVars, unsigned cVars)
364{
365 /*
366 * Just do basic min / max checks first.
367 */
368 if (cVars < cVarsMin)
369 return VERR_PARSE_TOO_FEW_ARGUMENTS;
370 if (cVars > cVarsMax)
371 return VERR_PARSE_TOO_MANY_ARGUMENTS;
372
373 /*
374 * Match the descriptors and actual variables.
375 */
376 PCDBGCVARDESC pPrevDesc = NULL;
377 unsigned cCurDesc = 0;
378 unsigned iVar = 0;
379 unsigned iVarDesc = 0;
380 while (iVar < cVars)
381 {
382 /* walk the descriptors */
383 if (iVarDesc >= cVarDescs)
384 return VERR_PARSE_TOO_MANY_ARGUMENTS;
385 if ( ( paVarDescs[iVarDesc].fFlags & DBGCVD_FLAGS_DEP_PREV
386 && &paVarDescs[iVarDesc - 1] != pPrevDesc)
387 || cCurDesc >= paVarDescs[iVarDesc].cTimesMax)
388 {
389 iVarDesc++;
390 if (iVarDesc >= cVarDescs)
391 return VERR_PARSE_TOO_MANY_ARGUMENTS;
392 cCurDesc = 0;
393 }
394
395 /*
396 * Skip thru optional arguments until we find something which matches
397 * or can easily be promoted to what the descriptor want.
398 */
399 for (;;)
400 {
401 int rc = dbgcEvalSubMatchVar(pDbgc, &paVars[iVar], &paVarDescs[iVarDesc]);
402 if (RT_SUCCESS(rc))
403 {
404 paVars[iVar].pDesc = &paVarDescs[iVarDesc];
405 cCurDesc++;
406 break;
407 }
408
409 /* can we advance? */
410 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
411 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
412 if (++iVarDesc >= cVarDescs)
413 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
414 cCurDesc = 0;
415 }
416
417 /* next var */
418 iVar++;
419 }
420
421 /*
422 * Check that the rest of the descriptors are optional.
423 */
424 while (iVarDesc < cVarDescs)
425 {
426 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
427 return VERR_PARSE_TOO_FEW_ARGUMENTS;
428 cCurDesc = 0;
429
430 /* next */
431 iVarDesc++;
432 }
433
434 return VINF_SUCCESS;
435}
436
437
438/**
439 * Evaluates one argument with respect to unary operators.
440 *
441 * @returns VBox status code. pResult contains the result on success.
442 *
443 * @param pDbgc Debugger console instance data.
444 * @param pszExpr The expression string.
445 * @param pResult Where to store the result of the expression evaluation.
446 */
447static int dbgcEvalSubUnary(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)
448{
449 Log2(("dbgcEvalSubUnary: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
450
451 /*
452 * The state of the expression is now such that it will start by zero or more
453 * unary operators and being followed by an expression of some kind.
454 * The expression is either plain or in parenthesis.
455 *
456 * Being in a lazy, recursive mode today, the parsing is done as simple as possible. :-)
457 * ASSUME: unary operators are all of equal precedence.
458 */
459 int rc = VINF_SUCCESS;
460 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, pszExpr, false, ' ');
461 if (pOp)
462 {
463 /* binary operators means syntax error. */
464 if (pOp->fBinary)
465 return VERR_PARSE_UNEXPECTED_OPERATOR;
466
467 /*
468 * If the next expression (the one following the unary operator) is in a
469 * parenthesis a full eval is needed. If not the unary eval will suffice.
470 */
471 /* calc and strip next expr. */
472 char *pszExpr2 = pszExpr + pOp->cchName;
473 while (RT_C_IS_BLANK(*pszExpr2))
474 pszExpr2++;
475
476 if (!*pszExpr2)
477 rc = VERR_PARSE_EMPTY_ARGUMENT;
478 else
479 {
480 DBGCVAR Arg;
481 if (*pszExpr2 == '(')
482 rc = dbgcEvalSub(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);
483 else
484 rc = dbgcEvalSubUnary(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);
485 if (RT_SUCCESS(rc))
486 rc = pOp->pfnHandlerUnary(pDbgc, &Arg, pResult);
487 }
488 }
489 else
490 {
491 /*
492 * Didn't find any operators, so it we have to check if this can be an
493 * function call before assuming numeric or string expression.
494 *
495 * (ASSUMPTIONS:)
496 * A function name only contains alphanumerical chars and it can not start
497 * with a numerical character.
498 * Immediately following the name is a parenthesis which must over
499 * the remaining part of the expression.
500 */
501 bool fExternal = *pszExpr == '.';
502 char *pszFun = fExternal ? pszExpr + 1 : pszExpr;
503 char *pszFunEnd = NULL;
504 if (pszExpr[cchExpr - 1] == ')' && RT_C_IS_ALPHA(*pszFun))
505 {
506 pszFunEnd = pszExpr + 1;
507 while (*pszFunEnd != '(' && RT_C_IS_ALNUM(*pszFunEnd))
508 pszFunEnd++;
509 if (*pszFunEnd != '(')
510 pszFunEnd = NULL;
511 }
512
513 if (pszFunEnd)
514 {
515 /*
516 * Ok, it's a function call.
517 */
518 if (fExternal)
519 pszExpr++, cchExpr--;
520 PCDBGCCMD pFun = dbgcRoutineLookup(pDbgc, pszExpr, pszFunEnd - pszExpr, fExternal);
521 if (!pFun)
522 return VERR_PARSE_FUNCTION_NOT_FOUND;
523 if (!pFun->pResultDesc)
524 return VERR_PARSE_NOT_A_FUNCTION;
525
526 /*
527 * Parse the expression in parenthesis.
528 */
529 cchExpr -= pszFunEnd - pszExpr;
530 pszExpr = pszFunEnd;
531 /** @todo implement multiple arguments. */
532 DBGCVAR Arg;
533 rc = dbgcEvalSub(pDbgc, pszExpr, cchExpr, &Arg);
534 if (!rc)
535 {
536 rc = dbgcEvalSubMatchVars(pDbgc, pFun->cArgsMin, pFun->cArgsMax, pFun->paArgDescs, pFun->cArgDescs, &Arg, 1);
537 if (!rc)
538 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, &Arg, 1, pResult);
539 }
540 else if (rc == VERR_PARSE_EMPTY_ARGUMENT && pFun->cArgsMin == 0)
541 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, NULL, 0, pResult);
542 }
543 else
544 {
545 /*
546 * Didn't find any operators, so it must be a plain expression.
547 * This might be numeric or a string expression.
548 */
549 char ch = pszExpr[0];
550 char ch2 = pszExpr[1];
551 if (ch == '0' && (ch2 == 'x' || ch2 == 'X'))
552 rc = dbgcEvalSubNum(pszExpr + 2, 16, pResult);
553 else if (ch == '0' && (ch2 == 'i' || ch2 == 'i'))
554 rc = dbgcEvalSubNum(pszExpr + 2, 10, pResult);
555 else if (ch == '0' && (ch2 == 't' || ch2 == 'T'))
556 rc = dbgcEvalSubNum(pszExpr + 2, 8, pResult);
557 /// @todo 0b doesn't work as a binary prefix, we confuse it with 0bf8:0123 and stuff.
558 //else if (ch == '0' && (ch2 == 'b' || ch2 == 'b'))
559 // rc = dbgcEvalSubNum(pszExpr + 2, 2, pResult);
560 else
561 {
562 /*
563 * Hexadecimal number or a string?
564 */
565 char *psz = pszExpr;
566 while (RT_C_IS_XDIGIT(*psz))
567 psz++;
568 if (!*psz)
569 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
570 else if ((*psz == 'h' || *psz == 'H') && !psz[1])
571 {
572 *psz = '\0';
573 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
574 }
575 else
576 rc = dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
577 }
578 }
579 }
580
581 return rc;
582}
583
584
585/**
586 * Evaluates one argument.
587 *
588 * @returns VBox status code.
589 *
590 * @param pDbgc Debugger console instance data.
591 * @param pszExpr The expression string.
592 * @param pResult Where to store the result of the expression evaluation.
593 */
594int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)
595{
596 Log2(("dbgcEvalSub: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
597 /*
598 * First we need to remove blanks in both ends.
599 * ASSUMES: There is no quoting unless the entire expression is a string.
600 */
601
602 /* stripping. */
603 while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1]))
604 pszExpr[--cchExpr] = '\0';
605 while (RT_C_IS_BLANK(*pszExpr))
606 pszExpr++, cchExpr--;
607 if (!*pszExpr)
608 return VERR_PARSE_EMPTY_ARGUMENT;
609
610 /* it there is any kind of quoting in the expression, it's string meat. */
611 if (strpbrk(pszExpr, "\"'`"))
612 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
613
614 /*
615 * Check if there are any parenthesis which needs removing.
616 */
617 if (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')')
618 {
619 do
620 {
621 unsigned cPar = 1;
622 char *psz = pszExpr + 1;
623 char ch;
624 while ((ch = *psz) != '\0')
625 {
626 if (ch == '(')
627 cPar++;
628 else if (ch == ')')
629 {
630 if (cPar <= 0)
631 return VERR_PARSE_UNBALANCED_PARENTHESIS;
632 cPar--;
633 if (cPar == 0 && psz[1]) /* If not at end, there's nothing to do. */
634 break;
635 }
636 /* next */
637 psz++;
638 }
639 if (ch)
640 break;
641
642 /* remove the parenthesis. */
643 pszExpr++;
644 cchExpr -= 2;
645 pszExpr[cchExpr] = '\0';
646
647 /* strip blanks. */
648 while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1]))
649 pszExpr[--cchExpr] = '\0';
650 while (RT_C_IS_BLANK(*pszExpr))
651 pszExpr++, cchExpr--;
652 if (!*pszExpr)
653 return VERR_PARSE_EMPTY_ARGUMENT;
654 } while (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')');
655 }
656
657 /* tabs to spaces. */
658 char *psz = pszExpr;
659 while ((psz = strchr(psz, '\t')) != NULL)
660 *psz = ' ';
661
662 /*
663 * Now, we need to look for the binary operator with the lowest precedence.
664 *
665 * If there are no operators we're left with a simple expression which we
666 * evaluate with respect to unary operators
667 */
668 char *pszOpSplit = NULL;
669 PCDBGCOP pOpSplit = NULL;
670 unsigned cBinaryOps = 0;
671 unsigned cPar = 0;
672 char ch;
673 char chPrev = ' ';
674 bool fBinary = false;
675 psz = pszExpr;
676
677 while ((ch = *psz) != '\0')
678 {
679 //Log2(("ch=%c cPar=%d fBinary=%d\n", ch, cPar, fBinary));
680 /*
681 * Parenthesis.
682 */
683 if (ch == '(')
684 {
685 cPar++;
686 fBinary = false;
687 }
688 else if (ch == ')')
689 {
690 if (cPar <= 0)
691 return VERR_PARSE_UNBALANCED_PARENTHESIS;
692 cPar--;
693 fBinary = true;
694 }
695 /*
696 * Potential operator.
697 */
698 else if (cPar == 0 && !RT_C_IS_BLANK(ch))
699 {
700 PCDBGCOP pOp = dbgcIsOpChar(ch)
701 ? dbgcOperatorLookup(pDbgc, psz, fBinary, chPrev)
702 : NULL;
703 if (pOp)
704 {
705 /* If not the right kind of operator we've got a syntax error. */
706 if (pOp->fBinary != fBinary)
707 return VERR_PARSE_UNEXPECTED_OPERATOR;
708
709 /*
710 * Update the parse state and skip the operator.
711 */
712 if (!pOpSplit)
713 {
714 pOpSplit = pOp;
715 pszOpSplit = psz;
716 cBinaryOps = fBinary;
717 }
718 else if (fBinary)
719 {
720 cBinaryOps++;
721 if (pOp->iPrecedence >= pOpSplit->iPrecedence)
722 {
723 pOpSplit = pOp;
724 pszOpSplit = psz;
725 }
726 }
727
728 psz += pOp->cchName - 1;
729 fBinary = false;
730 }
731 else
732 fBinary = true;
733 }
734
735 /* next */
736 psz++;
737 chPrev = ch;
738 } /* parse loop. */
739
740
741 /*
742 * Either we found an operator to divide the expression by
743 * or we didn't find any. In the first case it's divide and
744 * conquer. In the latter it's a single expression which
745 * needs dealing with its unary operators if any.
746 */
747 int rc;
748 if ( cBinaryOps
749 && pOpSplit->fBinary)
750 {
751 /* process 1st sub expression. */
752 *pszOpSplit = '\0';
753 DBGCVAR Arg1;
754 rc = dbgcEvalSub(pDbgc, pszExpr, pszOpSplit - pszExpr, &Arg1);
755 if (RT_SUCCESS(rc))
756 {
757 /* process 2nd sub expression. */
758 char *psz2 = pszOpSplit + pOpSplit->cchName;
759 DBGCVAR Arg2;
760 rc = dbgcEvalSub(pDbgc, psz2, cchExpr - (psz2 - pszExpr), &Arg2);
761 if (RT_SUCCESS(rc))
762 /* apply the operator. */
763 rc = pOpSplit->pfnHandlerBinary(pDbgc, &Arg1, &Arg2, pResult);
764 }
765 }
766 else if (cBinaryOps)
767 {
768 /* process sub expression. */
769 pszOpSplit += pOpSplit->cchName;
770 DBGCVAR Arg;
771 rc = dbgcEvalSub(pDbgc, pszOpSplit, cchExpr - (pszOpSplit - pszExpr), &Arg);
772 if (RT_SUCCESS(rc))
773 /* apply the operator. */
774 rc = pOpSplit->pfnHandlerUnary(pDbgc, &Arg, pResult);
775 }
776 else
777 /* plain expression or using unary operators perhaps with parentheses. */
778 rc = dbgcEvalSubUnary(pDbgc, pszExpr, cchExpr, pResult);
779
780 return rc;
781}
782
783
784/**
785 * Parses the arguments of one command.
786 *
787 * @returns VBox statuc code. On parser errors the index of the troublesome
788 * argument is indicated by *pcArg.
789 *
790 * @param pDbgc Debugger console instance data.
791 * @param pCmd Pointer to the command descriptor.
792 * @param pszArg Pointer to the arguments to parse.
793 * @param paArgs Where to store the parsed arguments.
794 * @param cArgs Size of the paArgs array.
795 * @param pcArgs Where to store the number of arguments. In the event
796 * of an error this is (ab)used to store the index of the
797 * offending argument.
798 */
799static int dbgcProcessArguments(PDBGC pDbgc, PCDBGCCMD pCmd, char *pszArgs, PDBGCVAR paArgs, unsigned cArgs, unsigned *pcArgs)
800{
801 Log2(("dbgcProcessArguments: pCmd=%s pszArgs='%s'\n", pCmd->pszCmd, pszArgs));
802
803 /*
804 * Check if we have any argument and if the command takes any.
805 */
806 *pcArgs = 0;
807 /* strip leading blanks. */
808 while (*pszArgs && RT_C_IS_BLANK(*pszArgs))
809 pszArgs++;
810 if (!*pszArgs)
811 {
812 if (!pCmd->cArgsMin)
813 return VINF_SUCCESS;
814 return VERR_PARSE_TOO_FEW_ARGUMENTS;
815 }
816 /** @todo fixme - foo() doesn't work. */
817 if (!pCmd->cArgsMax)
818 return VERR_PARSE_TOO_MANY_ARGUMENTS;
819
820 /*
821 * This is a hack, it's "temporary" and should go away "when" the parser is
822 * modified to match arguments while parsing.
823 */
824 if ( pCmd->cArgsMax == 1
825 && pCmd->cArgsMin == 1
826 && pCmd->cArgDescs == 1
827 && pCmd->paArgDescs[0].enmCategory == DBGCVAR_CAT_STRING
828 && cArgs >= 1)
829 {
830 *pcArgs = 1;
831 RTStrStripR(pszArgs);
832 return dbgcEvalSubString(pDbgc, pszArgs, strlen(pszArgs), &paArgs[0]);
833 }
834
835
836 /*
837 * The parse loop.
838 */
839 PDBGCVAR pArg0 = &paArgs[0];
840 PDBGCVAR pArg = pArg0;
841 *pcArgs = 0;
842 do
843 {
844 /*
845 * Can we have another argument?
846 */
847 if (*pcArgs >= pCmd->cArgsMax)
848 return VERR_PARSE_TOO_MANY_ARGUMENTS;
849 if (pArg >= &paArgs[cArgs])
850 return VERR_PARSE_ARGUMENT_OVERFLOW;
851
852 /*
853 * Find the end of the argument.
854 */
855 int cPar = 0;
856 char chQuote = '\0';
857 char *pszEnd = NULL;
858 char *psz = pszArgs;
859 char ch;
860 bool fBinary = false;
861 for (;;)
862 {
863 /*
864 * Check for the end.
865 */
866 if ((ch = *psz) == '\0')
867 {
868 if (chQuote)
869 return VERR_PARSE_UNBALANCED_QUOTE;
870 if (cPar)
871 return VERR_PARSE_UNBALANCED_PARENTHESIS;
872 pszEnd = psz;
873 break;
874 }
875 /*
876 * When quoted we ignore everything but the quotation char.
877 * We use the REXX way of escaping the quotation char, i.e. double occurrence.
878 */
879 else if (ch == '\'' || ch == '"' || ch == '`')
880 {
881 if (chQuote)
882 {
883 /* end quote? */
884 if (ch == chQuote)
885 {
886 if (psz[1] == ch)
887 psz++; /* skip the escaped quote char */
888 else
889 chQuote = '\0'; /* end of quoted string. */
890 }
891 }
892 else
893 chQuote = ch; /* open new quote */
894 }
895 /*
896 * Parenthesis can of course be nested.
897 */
898 else if (ch == '(')
899 {
900 cPar++;
901 fBinary = false;
902 }
903 else if (ch == ')')
904 {
905 if (!cPar)
906 return VERR_PARSE_UNBALANCED_PARENTHESIS;
907 cPar--;
908 fBinary = true;
909 }
910 else if (!chQuote && !cPar)
911 {
912 /*
913 * Encountering blanks may mean the end of it all. A binary operator
914 * will force continued parsing.
915 */
916 if (RT_C_IS_BLANK(*psz))
917 {
918 pszEnd = psz++; /* just in case. */
919 while (RT_C_IS_BLANK(*psz))
920 psz++;
921 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
922 if (!pOp || pOp->fBinary != fBinary)
923 break; /* the end. */
924 psz += pOp->cchName;
925 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
926 psz++;
927 fBinary = false;
928 continue;
929 }
930
931 /*
932 * Look for operators without a space up front.
933 */
934 if (dbgcIsOpChar(*psz))
935 {
936 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
937 if (pOp)
938 {
939 if (pOp->fBinary != fBinary)
940 {
941 pszEnd = psz;
942 /** @todo this is a parsing error really. */
943 break; /* the end. */
944 }
945 psz += pOp->cchName;
946 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
947 psz++;
948 fBinary = false;
949 continue;
950 }
951 }
952 fBinary = true;
953 }
954
955 /* next char */
956 psz++;
957 }
958 *pszEnd = '\0';
959 /* (psz = next char to process) */
960
961 /*
962 * Parse and evaluate the argument.
963 */
964 int rc = dbgcEvalSub(pDbgc, pszArgs, strlen(pszArgs), pArg);
965 if (RT_FAILURE(rc))
966 return rc;
967
968 /*
969 * Next.
970 */
971 pArg++;
972 (*pcArgs)++;
973 pszArgs = psz;
974 while (*pszArgs && RT_C_IS_BLANK(*pszArgs))
975 pszArgs++;
976 } while (*pszArgs);
977
978 /*
979 * Match the arguments.
980 */
981 return dbgcEvalSubMatchVars(pDbgc, pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs, pArg0, pArg - pArg0);
982}
983
984
985/**
986 * Evaluate one command.
987 *
988 * @returns VBox status code. This is also stored in DBGC::rcCmd.
989 *
990 * @param pDbgc Debugger console instance data.
991 * @param pszCmd Pointer to the command.
992 * @param cchCmd Length of the command.
993 * @param fNoExecute Indicates that no commands should actually be executed.
994 *
995 * @todo Change pszCmd into argc/argv?
996 */
997int dbgcEvalCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd, bool fNoExecute)
998{
999 char *pszCmdInput = pszCmd;
1000
1001 /*
1002 * Skip blanks.
1003 */
1004 while (RT_C_IS_BLANK(*pszCmd))
1005 pszCmd++, cchCmd--;
1006
1007 /* external command? */
1008 bool const fExternal = *pszCmd == '.';
1009 if (fExternal)
1010 pszCmd++, cchCmd--;
1011
1012 /*
1013 * Find arguments.
1014 */
1015 char *pszArgs = pszCmd;
1016 while (RT_C_IS_ALNUM(*pszArgs))
1017 pszArgs++;
1018 if (*pszArgs && (!RT_C_IS_BLANK(*pszArgs) || pszArgs == pszCmd))
1019 {
1020 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Syntax error: Invalid command '%s'!\n", pszCmdInput);
1021 return pDbgc->rcCmd = VINF_PARSE_INVALD_COMMAND_NAME;
1022 }
1023
1024 /*
1025 * Find the command.
1026 */
1027 PCDBGCCMD pCmd = dbgcRoutineLookup(pDbgc, pszCmd, pszArgs - pszCmd, fExternal);
1028 if (!pCmd || (pCmd->fFlags & DBGCCMD_FLAGS_FUNCTION))
1029 {
1030 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Syntax error: Unknown command '%s'!\n", pszCmdInput);
1031 return pDbgc->rcCmd = VINF_PARSE_COMMAND_NOT_FOUND;
1032 }
1033
1034 /*
1035 * Parse arguments (if any).
1036 */
1037 unsigned cArgs;
1038 int rc = dbgcProcessArguments(pDbgc, pCmd, pszArgs, &pDbgc->aArgs[pDbgc->iArg], RT_ELEMENTS(pDbgc->aArgs) - pDbgc->iArg, &cArgs);
1039 if (RT_SUCCESS(rc))
1040 {
1041 AssertMsg(rc == VINF_SUCCESS, ("%Rrc\n", rc));
1042
1043 /*
1044 * Execute the command.
1045 */
1046 if (!fNoExecute)
1047 rc = pCmd->pfnHandler(pCmd, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[0], cArgs, NULL);
1048 pDbgc->rcCmd = rc;
1049 if (rc == VERR_DBGC_COMMAND_FAILED)
1050 rc = VINF_SUCCESS;
1051 }
1052 else
1053 {
1054 pDbgc->rcCmd = rc;
1055
1056 /* report parse / eval error. */
1057 switch (rc)
1058 {
1059 case VERR_PARSE_TOO_FEW_ARGUMENTS:
1060 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1061 "Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd);
1062 break;
1063 case VERR_PARSE_TOO_MANY_ARGUMENTS:
1064 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1065 "Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd);
1066 break;
1067 case VERR_PARSE_ARGUMENT_OVERFLOW:
1068 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1069 "Syntax error: Too many arguments.\n");
1070 break;
1071 case VERR_PARSE_UNBALANCED_QUOTE:
1072 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1073 "Syntax error: Unbalanced quote (argument %d).\n", cArgs);
1074 break;
1075 case VERR_PARSE_UNBALANCED_PARENTHESIS:
1076 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1077 "Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs);
1078 break;
1079 case VERR_PARSE_EMPTY_ARGUMENT:
1080 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1081 "Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs);
1082 break;
1083 case VERR_PARSE_UNEXPECTED_OPERATOR:
1084 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1085 "Syntax error: Invalid operator usage (argument %d).\n", cArgs);
1086 break;
1087 case VERR_PARSE_INVALID_NUMBER:
1088 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1089 "Syntax error: Ivalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs);
1090 break;
1091 case VERR_PARSE_NUMBER_TOO_BIG:
1092 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1093 "Error: Numeric overflow (argument %d).\n", cArgs);
1094 break;
1095 case VERR_PARSE_INVALID_OPERATION:
1096 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1097 "Error: Invalid operation attempted (argument %d).\n", cArgs);
1098 break;
1099 case VERR_PARSE_FUNCTION_NOT_FOUND:
1100 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1101 "Error: Function not found (argument %d).\n", cArgs);
1102 break;
1103 case VERR_PARSE_NOT_A_FUNCTION:
1104 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1105 "Error: The function specified is not a function (argument %d).\n", cArgs);
1106 break;
1107 case VERR_PARSE_NO_MEMORY:
1108 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1109 "Error: Out memory in the regular heap! Expect odd stuff to happen...\n", cArgs);
1110 break;
1111 case VERR_PARSE_INCORRECT_ARG_TYPE:
1112 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1113 "Error: Incorrect argument type (argument %d?).\n", cArgs);
1114 break;
1115 case VERR_PARSE_VARIABLE_NOT_FOUND:
1116 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1117 "Error: An undefined variable was referenced (argument %d).\n", cArgs);
1118 break;
1119 case VERR_PARSE_CONVERSION_FAILED:
1120 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1121 "Error: A conversion between two types failed (argument %d).\n", cArgs);
1122 break;
1123 case VERR_PARSE_NOT_IMPLEMENTED:
1124 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1125 "Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs);
1126 break;
1127 case VERR_PARSE_BAD_RESULT_TYPE:
1128 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1129 "Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs);
1130 break;
1131 case VERR_PARSE_WRITEONLY_SYMBOL:
1132 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1133 "Error: Cannot get symbol, it's set only (argument %d).\n", cArgs);
1134 break;
1135
1136 case VERR_DBGC_COMMAND_FAILED:
1137 break;
1138
1139 default:
1140 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Error: Unknown error %d!\n", rc);
1141 break;
1142 }
1143 }
1144 return rc;
1145}
1146
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette