VirtualBox

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

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

PCDBGCCMD & PFNDBGCCMD: Drop the return type & variable. Functions will be added separately from commands (superset of DBGCCMD).

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

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