VirtualBox

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

Last change on this file since 41571 was 41571, checked in by vboxsync, 13 years ago

DBGC: Fixed cpu register references without @. Some symbol/string work. Updated the docs a little bit.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.6 KB
Line 
1/* $Id: DBGCEval.cpp 41571 2012-06-04 19:49:03Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, command evaluator.
4 */
5
6/*
7 * Copyright (C) 2006-2012 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/mem.h>
29#include <iprt/string.h>
30#include <iprt/ctype.h>
31
32#include "DBGCInternal.h"
33
34/** Rewrite in progress. */
35#define BETTER_ARGUMENT_MATCHING
36
37
38/*******************************************************************************
39* Global Variables *
40*******************************************************************************/
41/** Bitmap where set bits indicates the characters the may start an operator name. */
42static uint32_t g_bmOperatorChars[256 / (4*8)];
43
44
45/*******************************************************************************
46* Internal Functions *
47*******************************************************************************/
48static int dbgcProcessArguments(PDBGC pDbgc, const char *pszCmdOrFunc,
49 uint32_t const cArgsMin, uint32_t const cArgsMax,
50 PCDBGCVARDESC const paVarDescs, uint32_t const cVarDescs,
51 char *pszArgs, unsigned *piArg, unsigned *pcArgs);
52
53
54
55/**
56 * Initializes g_bmOperatorChars.
57 */
58void dbgcEvalInit(void)
59{
60 memset(g_bmOperatorChars, 0, sizeof(g_bmOperatorChars));
61 for (unsigned iOp = 0; iOp < g_cDbgcOps; iOp++)
62 ASMBitSet(&g_bmOperatorChars[0], (uint8_t)g_aDbgcOps[iOp].szName[0]);
63}
64
65
66/**
67 * Checks whether the character may be the start of an operator.
68 *
69 * @returns true/false.
70 * @param ch The character.
71 */
72DECLINLINE(bool) dbgcIsOpChar(char ch)
73{
74 return ASMBitTest(&g_bmOperatorChars[0], (uint8_t)ch);
75}
76
77
78/**
79 * Returns the amount of free scratch space.
80 *
81 * @returns Number of unallocated bytes.
82 * @param pDbgc The DBGC instance.
83 */
84size_t dbgcGetFreeScratchSpace(PDBGC pDbgc)
85{
86 return sizeof(pDbgc->achScratch) - (pDbgc->pszScratch - &pDbgc->achScratch[0]);
87}
88
89
90/**
91 * Allocates a string from the scratch space.
92 *
93 * @returns Pointer to the allocated string buffer, NULL if out of space.
94 * @param pDbgc The DBGC instance.
95 * @param cbRequested The number of bytes to allocate.
96 */
97char *dbgcAllocStringScatch(PDBGC pDbgc, size_t cbRequested)
98{
99 if (cbRequested > dbgcGetFreeScratchSpace(pDbgc))
100 return NULL;
101 char *psz = pDbgc->pszScratch;
102 pDbgc->pszScratch += cbRequested;
103 return psz;
104}
105
106
107/**
108 * Evals an expression into a string or symbol (single quotes).
109 *
110 * The string memory is allocated from the scratch buffer.
111 *
112 * @returns VBox status code.
113 * @param pDbgc The DBGC instance.
114 * @param pachExpr The string/symbol expression.
115 * @param cchExpr The length of the expression.
116 * @param pArg Where to return the string.
117 */
118static int dbgcEvalSubString(PDBGC pDbgc, const char *pachExpr, size_t cchExpr, PDBGCVAR pArg)
119{
120 Log2(("dbgcEvalSubString: cchExpr=%d pachExpr=%.*s\n", cchExpr, cchExpr, pachExpr));
121
122 /*
123 * Allocate scratch space for the string.
124 */
125 char *pszCopy = dbgcAllocStringScatch(pDbgc, cchExpr + 1);
126 if (!pszCopy)
127 return VERR_DBGC_PARSE_NO_SCRATCH;
128
129 /*
130 * Removing any quoting and escapings.
131 */
132 char const chQuote = *pachExpr;
133 if (chQuote == '"' || chQuote == '\'')
134 {
135 if (pachExpr[--cchExpr] != chQuote)
136 return VERR_DBGC_PARSE_UNBALANCED_QUOTE;
137
138 cchExpr--;
139 pachExpr++;
140 if (!memchr(pachExpr, chQuote, cchExpr))
141 memcpy(pszCopy, pachExpr, cchExpr);
142 else
143 {
144 size_t offSrc = 0;
145 size_t offDst = 0;
146 while (offSrc < cchExpr)
147 {
148 char const ch = pachExpr[offSrc++];
149 if (ch == chQuote)
150 {
151 if (pachExpr[offSrc] != ch)
152 return VERR_DBGC_PARSE_EXPECTED_BINARY_OP;
153 offSrc++;
154 }
155 pszCopy[offDst++] = ch;
156 }
157 }
158 }
159 else
160 memcpy(pszCopy, pachExpr, cchExpr);
161 pszCopy[cchExpr] = '\0';
162
163 /*
164 * Make the argument.
165 */
166 pArg->pDesc = NULL;
167 pArg->pNext = NULL;
168 pArg->enmType = chQuote == '\'' ? DBGCVAR_TYPE_SYMBOL : DBGCVAR_TYPE_STRING;
169 pArg->u.pszString = pszCopy;
170 pArg->enmRangeType = DBGCVAR_RANGE_BYTES;
171 pArg->u64Range = cchExpr;
172
173 NOREF(pDbgc);
174 return VINF_SUCCESS;
175}
176
177
178static int dbgcEvalSubNum(const char *pachExpr, size_t cchExpr, unsigned uBase, PDBGCVAR pArg)
179{
180 Log2(("dbgcEvalSubNum: uBase=%d pachExpr=%.*s\n", uBase, cchExpr, pachExpr));
181
182 /*
183 * Empty expressions cannot be valid numbers.
184 */
185 if (!cchExpr)
186 return VERR_DBGC_PARSE_INVALID_NUMBER;
187
188 /*
189 * Convert to number.
190 */
191 uint64_t u64 = 0;
192 while (cchExpr-- > 0)
193 {
194 char const ch = *pachExpr;
195 uint64_t u64Prev = u64;
196 unsigned u = ch - '0';
197 if (u < 10 && u < uBase)
198 u64 = u64 * uBase + u;
199 else if (ch >= 'a' && (u = ch - ('a' - 10)) < uBase)
200 u64 = u64 * uBase + u;
201 else if (ch >= 'A' && (u = ch - ('A' - 10)) < uBase)
202 u64 = u64 * uBase + u;
203 else
204 return VERR_DBGC_PARSE_INVALID_NUMBER;
205
206 /* check for overflow - ARG!!! How to detect overflow correctly!?!?!? */
207 if (u64Prev != u64 / uBase)
208 return VERR_DBGC_PARSE_NUMBER_TOO_BIG;
209
210 /* next */
211 pachExpr++;
212 }
213
214 /*
215 * Initialize the argument.
216 */
217 pArg->pDesc = NULL;
218 pArg->pNext = NULL;
219 pArg->enmType = DBGCVAR_TYPE_NUMBER;
220 pArg->u.u64Number = u64;
221 pArg->enmRangeType = DBGCVAR_RANGE_NONE;
222 pArg->u64Range = 0;
223
224 return VINF_SUCCESS;
225}
226
227
228/**
229 * dbgcEvalSubUnary worker that handles simple numeric or pointer expressions.
230 *
231 * @returns VBox status code. pResult contains the result on success.
232 * @param pDbgc Debugger console instance data.
233 * @param pszExpr The expression string.
234 * @param cchExpr The length of the expression.
235 * @param enmCategory The desired type category (for range / no range).
236 * @param pResult Where to store the result of the expression evaluation.
237 */
238static int dbgcEvalSubNumericOrPointer(PDBGC pDbgc, char *pszExpr, size_t cchExpr, DBGCVARCAT enmCategory,
239 PDBGCVAR pResult)
240{
241 char const ch = pszExpr[0];
242 char const ch2 = pszExpr[1];
243
244 /* 0x<hex digits> */
245 if (ch == '0' && (ch2 == 'x' || ch2 == 'X'))
246 return dbgcEvalSubNum(pszExpr + 2, cchExpr - 2, 16, pResult);
247
248 /* <hex digits>h */
249 if (RT_C_IS_XDIGIT(*pszExpr) && (pszExpr[cchExpr - 1] == 'h' || pszExpr[cchExpr - 1] == 'H'))
250 {
251 pszExpr[cchExpr] = '\0';
252 return dbgcEvalSubNum(pszExpr, cchExpr - 1, 16, pResult);
253 }
254
255 /* 0i<decimal digits> */
256 if (ch == '0' && ch2 == 'i')
257 return dbgcEvalSubNum(pszExpr + 2, cchExpr - 2, 10, pResult);
258
259 /* 0t<octal digits> */
260 if (ch == '0' && ch2 == 't')
261 return dbgcEvalSubNum(pszExpr + 2, cchExpr - 2, 8, pResult);
262
263 /* 0y<binary digits> */
264 if (ch == '0' && ch2 == 'y')
265 return dbgcEvalSubNum(pszExpr + 2, cchExpr - 2, 10, pResult);
266
267 /* Hex number? */
268 unsigned off = 0;
269 while (off < cchExpr && (RT_C_IS_XDIGIT(pszExpr[off]) || pszExpr[off] == '`'))
270 off++;
271 if (off == cchExpr)
272 return dbgcEvalSubNum(pszExpr, cchExpr, 16, pResult);
273
274 /*
275 * Some kind of symbol? Rejected double quoted strings, only unquoted
276 * and single quoted strings will be considered as symbols.
277 */
278 DBGCVARTYPE enmType;
279 bool fStripRange = false;
280 switch (enmCategory)
281 {
282 case DBGCVAR_CAT_POINTER_NUMBER: enmType = DBGCVAR_TYPE_NUMBER; break;
283 case DBGCVAR_CAT_POINTER_NUMBER_NO_RANGE: enmType = DBGCVAR_TYPE_NUMBER; fStripRange = true; break;
284 case DBGCVAR_CAT_POINTER: enmType = DBGCVAR_TYPE_NUMBER; break;
285 case DBGCVAR_CAT_POINTER_NO_RANGE: enmType = DBGCVAR_TYPE_NUMBER; fStripRange = true; break;
286 case DBGCVAR_CAT_GC_POINTER: enmType = DBGCVAR_TYPE_GC_FLAT; break;
287 case DBGCVAR_CAT_GC_POINTER_NO_RANGE: enmType = DBGCVAR_TYPE_GC_FLAT; fStripRange = true; break;
288 case DBGCVAR_CAT_NUMBER: enmType = DBGCVAR_TYPE_NUMBER; break;
289 case DBGCVAR_CAT_NUMBER_NO_RANGE: enmType = DBGCVAR_TYPE_NUMBER; fStripRange = true; break;
290 default:
291 AssertFailedReturn(VERR_DBGC_PARSE_NOT_IMPLEMENTED);
292 }
293
294 char const chQuote = *pszExpr;
295 if (chQuote == '"')
296 return VERR_DBGC_PARSE_INVALID_NUMBER;
297
298 if (chQuote == '\'')
299 {
300 if (pszExpr[cchExpr - 1] != chQuote)
301 return VERR_DBGC_PARSE_UNBALANCED_QUOTE;
302 pszExpr[cchExpr - 1] = '\0';
303 pszExpr++;
304 }
305
306 int rc = dbgcSymbolGet(pDbgc, pszExpr, enmType, pResult);
307 if (RT_SUCCESS(rc))
308 {
309 if (fStripRange)
310 {
311 pResult->enmRangeType = DBGCVAR_RANGE_NONE;
312 pResult->u64Range = 0;
313 }
314 }
315 else if (rc == VERR_DBGC_PARSE_NOT_IMPLEMENTED)
316 rc = VERR_DBGC_PARSE_INVALID_NUMBER;
317 return rc;
318}
319
320
321/**
322 * dbgcEvalSubUnary worker that handles simple DBGCVAR_CAT_ANY expressions.
323 *
324 * @returns VBox status code. pResult contains the result on success.
325 * @param pDbgc Debugger console instance data.
326 * @param pszExpr The expression string.
327 * @param cchExpr The length of the expression.
328 * @param pResult Where to store the result of the expression evaluation.
329 */
330static int dbgcEvalSubUnaryAny(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)
331{
332 char const ch = pszExpr[0];
333 char const ch2 = pszExpr[1];
334 unsigned off = 2;
335
336 /* 0x<hex digits> */
337 if (ch == '0' && (ch2 == 'x' || ch2 == 'X'))
338 {
339 while (RT_C_IS_XDIGIT(pszExpr[off]) || pszExpr[off] == '`')
340 off++;
341 if (off == cchExpr)
342 return dbgcEvalSubNum(pszExpr + 2, cchExpr - 2, 16, pResult);
343 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
344 }
345
346 /* <hex digits>h */
347 if (RT_C_IS_XDIGIT(*pszExpr) && (pszExpr[cchExpr - 1] == 'h' || pszExpr[cchExpr - 1] == 'H'))
348 {
349 cchExpr--;
350 while (off < cchExpr && (RT_C_IS_XDIGIT(pszExpr[off]) || pszExpr[off] == '`'))
351 off++;
352 if (off == cchExpr)
353 {
354 pszExpr[cchExpr] = '\0';
355 return dbgcEvalSubNum(pszExpr, cchExpr, 16, pResult);
356 }
357 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr + 1, pResult);
358 }
359
360 /* 0n<decimal digits> or 0i<decimal digits> */
361 if (ch == '0' && (ch2 == 'n' || ch2 == 'i'))
362 {
363 while (RT_C_IS_DIGIT(pszExpr[off]) || pszExpr[off] == '`')
364 off++;
365 if (off == cchExpr)
366 return dbgcEvalSubNum(pszExpr + 2, cchExpr - 2, 10, pResult);
367 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
368 }
369
370 /* 0t<octal digits> */
371 if (ch == '0' && ch2 == 't')
372 {
373 while (RT_C_IS_ODIGIT(pszExpr[off]) || pszExpr[off] == '`')
374 off++;
375 if (off == cchExpr)
376 return dbgcEvalSubNum(pszExpr + 2, cchExpr - 2, 8, pResult);
377 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
378 }
379
380 /* 0y<binary digits> */
381 if (ch == '0' && ch2 == 'y')
382 {
383 while (pszExpr[off] == '0' || pszExpr[off] == '1' || pszExpr[off] == '`')
384 off++;
385 if (off == cchExpr)
386 return dbgcEvalSubNum(pszExpr + 2, cchExpr - 2, 10, pResult);
387 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
388 }
389
390 /* Ok, no prefix of suffix. Is it a hex number after all? If not it must
391 be a string. */
392 off = 0;
393 while (RT_C_IS_XDIGIT(pszExpr[off]) || pszExpr[off] == '`')
394 off++;
395 if (off == cchExpr)
396 return dbgcEvalSubNum(pszExpr, cchExpr, 16, pResult);
397 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
398}
399
400
401static int dbgcEvalSubCall(PDBGC pDbgc, char *pszFuncNm, size_t cchFuncNm, bool fExternal, char *pszArgs, size_t cchArgs,
402 DBGCVARCAT enmCategory, PDBGCVAR pResult)
403{
404 /*
405 * Lookup the function.
406 */
407 PCDBGCFUNC pFunc = dbgcFunctionLookup(pDbgc, pszFuncNm, cchFuncNm, fExternal);
408 if (!pFunc)
409 return VERR_DBGC_PARSE_FUNCTION_NOT_FOUND;
410
411 /*
412 * Parse the arguments.
413 */
414 unsigned cArgs;
415 unsigned iArg;
416 pszArgs[cchArgs] = '\0';
417 int rc = dbgcProcessArguments(pDbgc, pFunc->pszFuncNm,
418 pFunc->cArgsMin, pFunc->cArgsMax, pFunc->paArgDescs, pFunc->cArgDescs,
419 pszArgs, &iArg, &cArgs);
420 if (RT_SUCCESS(rc))
421 rc = pFunc->pfnHandler(pFunc, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[iArg], cArgs, pResult);
422 pDbgc->iArg = iArg;
423 return rc;
424}
425
426
427/**
428 * Evaluates one argument with respect to unary operators.
429 *
430 * @returns VBox status code. pResult contains the result on success.
431 *
432 * @param pDbgc Debugger console instance data.
433 * @param pszExpr The expression string.
434 * @param cchExpr The length of the expression.
435 * @param enmCategory The target category for the result.
436 * @param pResult Where to store the result of the expression evaluation.
437 */
438static int dbgcEvalSubUnary(PDBGC pDbgc, char *pszExpr, size_t cchExpr, DBGCVARCAT enmCategory, PDBGCVAR pResult)
439{
440 Log2(("dbgcEvalSubUnary: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
441
442 /*
443 * The state of the expression is now such that it will start by zero or more
444 * unary operators and being followed by an expression of some kind.
445 * The expression is either plain or in parenthesis.
446 *
447 * Being in a lazy, recursive mode today, the parsing is done as simple as possible. :-)
448 * ASSUME: unary operators are all of equal precedence.
449 */
450 int rc = VINF_SUCCESS;
451 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, pszExpr, false, ' ');
452 if (pOp)
453 {
454 /* binary operators means syntax error. */
455 if (pOp->fBinary)
456 return VERR_DBGC_PARSE_UNEXPECTED_OPERATOR;
457
458 /*
459 * If the next expression (the one following the unary operator) is in a
460 * parenthesis a full eval is needed. If not the unary eval will suffice.
461 */
462 /* calc and strip next expr. */
463 char *pszExpr2 = pszExpr + pOp->cchName;
464 while (RT_C_IS_BLANK(*pszExpr2))
465 pszExpr2++;
466
467 if (*pszExpr2)
468 {
469 DBGCVAR Arg;
470 if (*pszExpr2 == '(')
471 rc = dbgcEvalSub(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), pOp->enmCatArg1, &Arg);
472 else
473 rc = dbgcEvalSubUnary(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), pOp->enmCatArg1, &Arg);
474 if (RT_SUCCESS(rc))
475 rc = pOp->pfnHandlerUnary(pDbgc, &Arg, enmCategory, pResult);
476 }
477 else
478 rc = VERR_DBGC_PARSE_EMPTY_ARGUMENT;
479 return rc;
480 }
481
482 /*
483 * Could this be a function call?
484 *
485 * ASSUMPTIONS:
486 * - A function name only contains alphanumerical chars and it can not
487 * start with a numerical character.
488 * - Immediately following the name is a parenthesis which must cover
489 * the remaining part of the expression.
490 */
491 bool fExternal = *pszExpr == '.';
492 char *pszFun = fExternal ? pszExpr + 1 : pszExpr;
493 char *pszFunEnd = NULL;
494 if (pszExpr[cchExpr - 1] == ')' && RT_C_IS_ALPHA(*pszFun))
495 {
496 pszFunEnd = pszExpr + 1;
497 while (*pszFunEnd != '(' && RT_C_IS_ALNUM(*pszFunEnd))
498 pszFunEnd++;
499 if (*pszFunEnd != '(')
500 pszFunEnd = NULL;
501 }
502 if (pszFunEnd)
503 {
504 size_t cchFunNm = pszFunEnd - pszFun;
505 return dbgcEvalSubCall(pDbgc, pszFun, cchFunNm, fExternal, pszFunEnd + 1, cchExpr - cchFunNm - fExternal - 2,
506 enmCategory, pResult);
507 }
508
509 /*
510 * Assuming plain expression.
511 * Didn't find any operators, so it must be a plain expression.
512 * Go by desired category first, then if anythings go, try guess.
513 */
514 switch (enmCategory)
515 {
516 case DBGCVAR_CAT_ANY:
517 return dbgcEvalSubUnaryAny(pDbgc, pszExpr, cchExpr, pResult);
518
519 case DBGCVAR_CAT_POINTER_NUMBER:
520 case DBGCVAR_CAT_POINTER_NUMBER_NO_RANGE:
521 case DBGCVAR_CAT_POINTER:
522 case DBGCVAR_CAT_POINTER_NO_RANGE:
523 case DBGCVAR_CAT_GC_POINTER:
524 case DBGCVAR_CAT_GC_POINTER_NO_RANGE:
525 case DBGCVAR_CAT_NUMBER:
526 case DBGCVAR_CAT_NUMBER_NO_RANGE:
527 /* Pointers will be promoted later. */
528 return dbgcEvalSubNumericOrPointer(pDbgc, pszExpr, cchExpr, enmCategory, pResult);
529
530 case DBGCVAR_CAT_STRING:
531 case DBGCVAR_CAT_SYMBOL:
532 /* Symbols will be promoted later. */
533 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
534
535 case DBGCVAR_CAT_OPTION:
536 case DBGCVAR_CAT_OPTION_STRING:
537 case DBGCVAR_CAT_OPTION_NUMBER:
538 return VERR_DBGC_PARSE_NOT_IMPLEMENTED;
539 }
540
541 AssertMsgFailed(("enmCategory=%d\n", enmCategory));
542 rc = VERR_NOT_IMPLEMENTED;
543
544 return rc;
545}
546
547
548/**
549 * Evaluates one argument.
550 *
551 * @returns VBox status code.
552 *
553 * @param pDbgc Debugger console instance data.
554 * @param pszExpr The expression string.
555 * @param enmCategory The target category for the result.
556 * @param pResult Where to store the result of the expression evaluation.
557 */
558int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, DBGCVARCAT enmCategory, PDBGCVAR pResult)
559{
560 Log2(("dbgcEvalSub: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
561
562 /*
563 * First we need to remove blanks in both ends.
564 * ASSUMES: There is no quoting unless the entire expression is a string.
565 */
566
567 /* stripping. */
568 while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1]))
569 pszExpr[--cchExpr] = '\0';
570 while (RT_C_IS_BLANK(*pszExpr))
571 pszExpr++, cchExpr--;
572 if (!*pszExpr)
573 return VERR_DBGC_PARSE_EMPTY_ARGUMENT;
574
575 /*
576 * Check if there are any parenthesis which needs removing.
577 */
578 if (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')')
579 {
580 do
581 {
582 unsigned cPar = 1;
583 char *psz = pszExpr + 1;
584 char ch;
585 while ((ch = *psz) != '\0')
586 {
587 if (ch == '(')
588 cPar++;
589 else if (ch == ')')
590 {
591 if (cPar <= 0)
592 return VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS;
593 cPar--;
594 if (cPar == 0 && psz[1]) /* If not at end, there's nothing to do. */
595 break;
596 }
597 /* next */
598 psz++;
599 }
600 if (ch)
601 break;
602
603 /* remove the parenthesis. */
604 pszExpr++;
605 cchExpr -= 2;
606 pszExpr[cchExpr] = '\0';
607
608 /* strip blanks. */
609 while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1]))
610 pszExpr[--cchExpr] = '\0';
611 while (RT_C_IS_BLANK(*pszExpr))
612 pszExpr++, cchExpr--;
613 if (!*pszExpr)
614 return VERR_DBGC_PARSE_EMPTY_ARGUMENT;
615 } while (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')');
616 }
617
618 /*
619 * Now, we need to look for the binary operator with the lowest precedence.
620 *
621 * If there are no operators we're left with a simple expression which we
622 * evaluate with respect to unary operators
623 */
624 char *pszOpSplit = NULL;
625 PCDBGCOP pOpSplit = NULL;
626 unsigned cBinaryOps = 0;
627 unsigned cPar = 0;
628 unsigned cchWord = 0;
629 char chQuote = '\0';
630 char chPrev = ' ';
631 bool fBinary = false;
632 char *psz = pszExpr;
633 char ch;
634
635 while ((ch = *psz) != '\0')
636 {
637 /*
638 * String quoting.
639 */
640 if (chQuote)
641 {
642 if (ch == chQuote)
643 {
644 if (psz[1] == chQuote)
645 {
646 psz++; /* escaped quote */
647 cchWord++;
648 }
649 else
650 {
651 chQuote = '\0';
652 fBinary = true;
653 cchWord = 0;
654 }
655 }
656 else
657 cchWord++;
658 }
659 else if (ch == '"' || ch == '\'')
660 {
661 if (fBinary || cchWord)
662 return VERR_DBGC_PARSE_EXPECTED_BINARY_OP;
663 chQuote = ch;
664 }
665 /*
666 * Parentheses.
667 */
668 else if (ch == '(')
669 {
670 if (!cPar && fBinary && !cchWord)
671 return VERR_DBGC_PARSE_EXPECTED_BINARY_OP;
672 cPar++;
673 fBinary = false;
674 cchWord = 0;
675 }
676 else if (ch == ')')
677 {
678 if (cPar <= 0)
679 return VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS;
680 cPar--;
681 fBinary = true;
682 cchWord = 0;
683 }
684 /*
685 * Potential operator.
686 */
687 else if (cPar == 0 && !RT_C_IS_BLANK(ch))
688 {
689 PCDBGCOP pOp = dbgcIsOpChar(ch)
690 ? dbgcOperatorLookup(pDbgc, psz, fBinary, chPrev)
691 : NULL;
692 if (pOp)
693 {
694 /* If not the right kind of operator we've got a syntax error. */
695 if (pOp->fBinary != fBinary)
696 return VERR_DBGC_PARSE_UNEXPECTED_OPERATOR;
697
698 /*
699 * Update the parse state and skip the operator.
700 */
701 if (!pOpSplit)
702 {
703 pOpSplit = pOp;
704 pszOpSplit = psz;
705 cBinaryOps = fBinary;
706 }
707 else if (fBinary)
708 {
709 cBinaryOps++;
710 if (pOp->iPrecedence >= pOpSplit->iPrecedence)
711 {
712 pOpSplit = pOp;
713 pszOpSplit = psz;
714 }
715 }
716
717 psz += pOp->cchName - 1;
718 fBinary = false;
719 cchWord = 0;
720 }
721 else if (fBinary && !cchWord)
722 return VERR_DBGC_PARSE_EXPECTED_BINARY_OP;
723 else
724 {
725 fBinary = true;
726 cchWord++;
727 }
728 }
729 else if (cPar == 0 && RT_C_IS_BLANK(ch))
730 cchWord++;
731
732 /* next */
733 psz++;
734 chPrev = ch;
735 } /* parse loop. */
736
737 if (chQuote)
738 return VERR_DBGC_PARSE_UNBALANCED_QUOTE;
739
740 /*
741 * Either we found an operator to divide the expression by
742 * or we didn't find any. In the first case it's divide and
743 * conquer. In the latter it's a single expression which
744 * needs dealing with its unary operators if any.
745 */
746 int rc;
747 if ( cBinaryOps
748 && pOpSplit->fBinary)
749 {
750 /* process 1st sub expression. */
751 *pszOpSplit = '\0';
752 DBGCVAR Arg1;
753 rc = dbgcEvalSub(pDbgc, pszExpr, pszOpSplit - pszExpr, pOpSplit->enmCatArg1, &Arg1);
754 if (RT_SUCCESS(rc))
755 {
756 /* process 2nd sub expression. */
757 char *psz2 = pszOpSplit + pOpSplit->cchName;
758 DBGCVAR Arg2;
759 rc = dbgcEvalSub(pDbgc, psz2, cchExpr - (psz2 - pszExpr), pOpSplit->enmCatArg2, &Arg2);
760 if (RT_SUCCESS(rc))
761 /* apply the operator. */
762 rc = pOpSplit->pfnHandlerBinary(pDbgc, &Arg1, &Arg2, pResult);
763 }
764 }
765 else if (cBinaryOps)
766 {
767 /* process sub expression. */
768 pszOpSplit += pOpSplit->cchName;
769 DBGCVAR Arg;
770 rc = dbgcEvalSub(pDbgc, pszOpSplit, cchExpr - (pszOpSplit - pszExpr), pOpSplit->enmCatArg1, &Arg);
771 if (RT_SUCCESS(rc))
772 /* apply the operator. */
773 rc = pOpSplit->pfnHandlerUnary(pDbgc, &Arg, enmCategory, pResult);
774 }
775 else
776 /* plain expression, qutoed string, or using unary operators perhaps with parentheses. */
777 rc = dbgcEvalSubUnary(pDbgc, pszExpr, cchExpr, enmCategory, pResult);
778
779 return rc;
780}
781
782
783/**
784 * Worker for dbgcProcessArguments that performs type checking and promoptions.
785 *
786 * @returns VBox status code.
787 *
788 * @param pDbgc Debugger console instance data.
789 * @param enmCategory The target category for the result.
790 * @param pArg The argument to check and promote.
791 */
792static int dbgcCheckAndTypePromoteArgument(PDBGC pDbgc, DBGCVARCAT enmCategory, PDBGCVAR pArg)
793{
794 switch (enmCategory)
795 {
796 /*
797 * Anything goes
798 */
799 case DBGCVAR_CAT_ANY:
800 return VINF_SUCCESS;
801
802 /*
803 * Pointer with and without range.
804 * We can try resolve strings and symbols as symbols and promote
805 * numbers to flat GC pointers.
806 */
807 case DBGCVAR_CAT_POINTER_NO_RANGE:
808 case DBGCVAR_CAT_POINTER_NUMBER_NO_RANGE:
809 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
810 return VERR_DBGC_PARSE_NO_RANGE_ALLOWED;
811 /* fallthru */
812 case DBGCVAR_CAT_POINTER:
813 case DBGCVAR_CAT_POINTER_NUMBER:
814 switch (pArg->enmType)
815 {
816 case DBGCVAR_TYPE_GC_FLAT:
817 case DBGCVAR_TYPE_GC_FAR:
818 case DBGCVAR_TYPE_GC_PHYS:
819 case DBGCVAR_TYPE_HC_FLAT:
820 case DBGCVAR_TYPE_HC_PHYS:
821 return VINF_SUCCESS;
822
823 case DBGCVAR_TYPE_SYMBOL:
824 case DBGCVAR_TYPE_STRING:
825 {
826 DBGCVAR Var;
827 int rc = dbgcSymbolGet(pDbgc, pArg->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
828 if (RT_SUCCESS(rc))
829 {
830 /* deal with range */
831 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
832 {
833 Var.enmRangeType = pArg->enmRangeType;
834 Var.u64Range = pArg->u64Range;
835 }
836 else if (enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
837 Var.enmRangeType = DBGCVAR_RANGE_NONE;
838 *pArg = Var;
839 }
840 return rc;
841 }
842
843 case DBGCVAR_TYPE_NUMBER:
844 if ( enmCategory != DBGCVAR_CAT_POINTER_NUMBER
845 && enmCategory != DBGCVAR_CAT_POINTER_NUMBER_NO_RANGE)
846 {
847 RTGCPTR GCPtr = (RTGCPTR)pArg->u.u64Number;
848 pArg->enmType = DBGCVAR_TYPE_GC_FLAT;
849 pArg->u.GCFlat = GCPtr;
850 }
851 return VINF_SUCCESS;
852
853 default:
854 AssertMsgFailedReturn(("Invalid type %d\n"), VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
855 }
856 break; /* (not reached) */
857
858 /*
859 * GC pointer with and without range.
860 * We can try resolve strings and symbols as symbols and
861 * promote numbers to flat GC pointers.
862 */
863 case DBGCVAR_CAT_GC_POINTER_NO_RANGE:
864 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
865 return VERR_DBGC_PARSE_NO_RANGE_ALLOWED;
866 /* fallthru */
867 case DBGCVAR_CAT_GC_POINTER:
868 switch (pArg->enmType)
869 {
870 case DBGCVAR_TYPE_GC_FLAT:
871 case DBGCVAR_TYPE_GC_FAR:
872 case DBGCVAR_TYPE_GC_PHYS:
873 return VINF_SUCCESS;
874
875 case DBGCVAR_TYPE_HC_FLAT:
876 case DBGCVAR_TYPE_HC_PHYS:
877 return VERR_DBGC_PARSE_CONVERSION_FAILED;
878
879 case DBGCVAR_TYPE_SYMBOL:
880 case DBGCVAR_TYPE_STRING:
881 {
882 DBGCVAR Var;
883 int rc = dbgcSymbolGet(pDbgc, pArg->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
884 if (RT_SUCCESS(rc))
885 {
886 /* deal with range */
887 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
888 {
889 Var.enmRangeType = pArg->enmRangeType;
890 Var.u64Range = pArg->u64Range;
891 }
892 else if (enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
893 Var.enmRangeType = DBGCVAR_RANGE_NONE;
894 *pArg = Var;
895 }
896 return rc;
897 }
898
899 case DBGCVAR_TYPE_NUMBER:
900 {
901 RTGCPTR GCPtr = (RTGCPTR)pArg->u.u64Number;
902 pArg->enmType = DBGCVAR_TYPE_GC_FLAT;
903 pArg->u.GCFlat = GCPtr;
904 return VINF_SUCCESS;
905 }
906
907 default:
908 AssertMsgFailedReturn(("Invalid type %d\n"), VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
909 }
910 break; /* (not reached) */
911
912 /*
913 * Number with or without a range.
914 * Numbers can be resolved from symbols, but we cannot demote a pointer
915 * to a number.
916 */
917 case DBGCVAR_CAT_NUMBER_NO_RANGE:
918 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
919 return VERR_DBGC_PARSE_NO_RANGE_ALLOWED;
920 /* fallthru */
921 case DBGCVAR_CAT_NUMBER:
922 switch (pArg->enmType)
923 {
924 case DBGCVAR_TYPE_GC_FLAT:
925 case DBGCVAR_TYPE_GC_FAR:
926 case DBGCVAR_TYPE_GC_PHYS:
927 case DBGCVAR_TYPE_HC_FLAT:
928 case DBGCVAR_TYPE_HC_PHYS:
929 return VERR_DBGC_PARSE_INCORRECT_ARG_TYPE;
930
931 case DBGCVAR_TYPE_NUMBER:
932 return VINF_SUCCESS;
933
934 case DBGCVAR_TYPE_SYMBOL:
935 case DBGCVAR_TYPE_STRING:
936 {
937 DBGCVAR Var;
938 int rc = dbgcSymbolGet(pDbgc, pArg->u.pszString, DBGCVAR_TYPE_NUMBER, &Var);
939 if (RT_SUCCESS(rc))
940 {
941 /* deal with range */
942 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
943 {
944 Var.enmRangeType = pArg->enmRangeType;
945 Var.u64Range = pArg->u64Range;
946 }
947 else if (enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
948 Var.enmRangeType = DBGCVAR_RANGE_NONE;
949 *pArg = Var;
950 }
951 return rc;
952 }
953
954 default:
955 AssertMsgFailedReturn(("Invalid type %d\n"), VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
956 }
957 break; /* (not reached) */
958
959 /*
960 * Symbols and strings are basically the same thing for the time being.
961 */
962 case DBGCVAR_CAT_STRING:
963 case DBGCVAR_CAT_SYMBOL:
964 {
965 switch (pArg->enmType)
966 {
967 case DBGCVAR_TYPE_STRING:
968 if (enmCategory == DBGCVAR_CAT_SYMBOL)
969 pArg->enmType = DBGCVAR_TYPE_SYMBOL;
970 return VINF_SUCCESS;
971
972 case DBGCVAR_TYPE_SYMBOL:
973 if (enmCategory == DBGCVAR_CAT_STRING)
974 pArg->enmType = DBGCVAR_TYPE_STRING;
975 return VINF_SUCCESS;
976 default:
977 break;
978 }
979
980 /* Stringify numeric and poitner values. */
981 size_t cbScratch = sizeof(pDbgc->achScratch) - (pDbgc->pszScratch - &pDbgc->achScratch[0]);
982 size_t cch = pDbgc->CmdHlp.pfnStrPrintf(&pDbgc->CmdHlp, pDbgc->pszScratch, cbScratch, "%Dv", pArg);
983 if (cch + 1 >= cbScratch)
984 return VERR_DBGC_PARSE_NO_SCRATCH;
985
986 pArg->enmType = enmCategory == DBGCVAR_CAT_STRING ? DBGCVAR_TYPE_STRING : DBGCVAR_TYPE_SYMBOL;
987 pArg->u.pszString = pDbgc->pszScratch;
988 pArg->enmRangeType = DBGCVAR_RANGE_BYTES;
989 pArg->u64Range = cch;
990
991 pDbgc->pszScratch += cch + 1;
992 return VINF_SUCCESS;
993 }
994
995 /*
996 * These are not yet implemented.
997 */
998 case DBGCVAR_CAT_OPTION:
999 case DBGCVAR_CAT_OPTION_STRING:
1000 case DBGCVAR_CAT_OPTION_NUMBER:
1001 AssertMsgFailedReturn(("Not implemented enmCategory=%d\n", enmCategory), VERR_DBGC_PARSE_NOT_IMPLEMENTED);
1002
1003 default:
1004 AssertMsgFailedReturn(("Bad enmCategory=%d\n", enmCategory), VERR_DBGC_PARSE_NOT_IMPLEMENTED);
1005 }
1006}
1007
1008
1009/**
1010 * Parses the arguments of one command.
1011 *
1012 * @returns VBox statuc code. On parser errors the index of the troublesome
1013 * argument is indicated by *pcArg.
1014 *
1015 * @param pDbgc Debugger console instance data.
1016 * @param pszCmdOrFunc The name of the function or command. (For logging.)
1017 * @param cArgsMin See DBGCCMD::cArgsMin and DBGCFUNC::cArgsMin.
1018 * @param cArgsMax See DBGCCMD::cArgsMax and DBGCFUNC::cArgsMax.
1019 * @param paVarDescs See DBGCCMD::paVarDescs and DBGCFUNC::paVarDescs.
1020 * @param cVarDescs See DBGCCMD::cVarDescs and DBGCFUNC::cVarDescs.
1021 * @param pszArg Pointer to the arguments to parse.
1022 * @param piArg Where to return the index of the first argument in
1023 * DBGC::aArgs. Always set. Caller must restore DBGC::iArg
1024 * to this value when done, even on failure.
1025 * @param pcArgs Where to store the number of arguments. In the event
1026 * of an error this is (ab)used to store the index of the
1027 * offending argument.
1028 */
1029static int dbgcProcessArguments(PDBGC pDbgc, const char *pszCmdOrFunc,
1030 uint32_t const cArgsMin, uint32_t const cArgsMax,
1031 PCDBGCVARDESC const paVarDescs, uint32_t const cVarDescs,
1032 char *pszArgs, unsigned *piArg, unsigned *pcArgs)
1033{
1034 Log2(("dbgcProcessArguments: pszCmdOrFunc=%s pszArgs='%s'\n", pszCmdOrFunc, pszArgs));
1035
1036 /*
1037 * Check if we have any argument and if the command takes any.
1038 */
1039 *piArg = pDbgc->iArg;
1040 *pcArgs = 0;
1041 /* strip leading blanks. */
1042 while (*pszArgs && RT_C_IS_BLANK(*pszArgs))
1043 pszArgs++;
1044 if (!*pszArgs)
1045 {
1046 if (!cArgsMin)
1047 return VINF_SUCCESS;
1048 return VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS;
1049 }
1050 if (!cArgsMax)
1051 return VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS;
1052
1053 /*
1054 * The parse loop.
1055 */
1056 PDBGCVAR pArg = &pDbgc->aArgs[pDbgc->iArg];
1057 PCDBGCVARDESC pPrevDesc = NULL;
1058 unsigned cCurDesc = 0;
1059 unsigned iVar = 0;
1060 unsigned iVarDesc = 0;
1061 *pcArgs = 0;
1062 do
1063 {
1064 /*
1065 * Can we have another argument?
1066 */
1067 if (*pcArgs >= cArgsMax)
1068 return VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS;
1069 if (pDbgc->iArg >= RT_ELEMENTS(pDbgc->aArgs))
1070 return VERR_DBGC_PARSE_ARGUMENT_OVERFLOW;
1071 if (iVarDesc >= cVarDescs)
1072 return VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS;
1073
1074 /* Walk argument descriptors. */
1075 if (cCurDesc >= paVarDescs[iVarDesc].cTimesMax)
1076 {
1077 iVarDesc++;
1078 if (iVarDesc >= cVarDescs)
1079 return VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS;
1080 cCurDesc = 0;
1081 }
1082
1083 /*
1084 * Find the end of the argument. This is just rough splitting,
1085 * dbgcEvalSub will do stricter syntax checking later on.
1086 */
1087 int cPar = 0;
1088 char chQuote = '\0';
1089 char *pszEnd = NULL;
1090 char *psz = pszArgs;
1091 char ch;
1092 bool fBinary = false;
1093 for (;;)
1094 {
1095 /*
1096 * Check for the end.
1097 */
1098 if ((ch = *psz) == '\0')
1099 {
1100 if (chQuote)
1101 return VERR_DBGC_PARSE_UNBALANCED_QUOTE;
1102 if (cPar)
1103 return VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS;
1104 pszEnd = psz;
1105 break;
1106 }
1107 /*
1108 * When quoted we ignore everything but the quotation char.
1109 * We use the REXX way of escaping the quotation char, i.e. double occurrence.
1110 */
1111 else if (chQuote)
1112 {
1113 if (ch == chQuote)
1114 {
1115 if (psz[1] == chQuote)
1116 psz++; /* skip the escaped quote char */
1117 else
1118 {
1119 chQuote = '\0'; /* end of quoted string. */
1120 fBinary = true;
1121 }
1122 }
1123 }
1124 else if (ch == '\'' || ch == '"')
1125 {
1126 if (fBinary)
1127 return VERR_DBGC_PARSE_EXPECTED_BINARY_OP;
1128 chQuote = ch;
1129 }
1130 /*
1131 * Parenthesis can of course be nested.
1132 */
1133 else if (ch == '(')
1134 {
1135 cPar++;
1136 fBinary = false;
1137 }
1138 else if (ch == ')')
1139 {
1140 if (!cPar)
1141 return VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS;
1142 cPar--;
1143 fBinary = true;
1144 }
1145 else if (!cPar)
1146 {
1147 /*
1148 * Encountering a comma is a definite end of parameter.
1149 */
1150 if (ch == ',')
1151 {
1152 pszEnd = psz++;
1153 break;
1154 }
1155
1156 /*
1157 * Encountering blanks may mean the end of it all. A binary
1158 * operator will force continued parsing.
1159 */
1160 if (RT_C_IS_BLANK(ch))
1161 {
1162 pszEnd = psz++; /* in case it's the end. */
1163 while (RT_C_IS_BLANK(*psz))
1164 psz++;
1165
1166 if (*psz == ',')
1167 {
1168 psz++;
1169 break;
1170 }
1171
1172 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
1173 if (!pOp || pOp->fBinary != fBinary)
1174 break; /* the end. */
1175
1176 psz += pOp->cchName;
1177 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
1178 psz++;
1179 fBinary = false;
1180 continue;
1181 }
1182
1183 /*
1184 * Look for operators without a space up front.
1185 */
1186 if (dbgcIsOpChar(ch))
1187 {
1188 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
1189 if (pOp)
1190 {
1191 if (pOp->fBinary != fBinary)
1192 {
1193 pszEnd = psz;
1194 /** @todo this is a parsing error really. */
1195 break; /* the end. */
1196 }
1197 psz += pOp->cchName;
1198 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
1199 psz++;
1200 fBinary = false;
1201 continue;
1202 }
1203 }
1204 fBinary = true;
1205 }
1206
1207 /* next char */
1208 psz++;
1209 }
1210 *pszEnd = '\0';
1211 /* (psz = next char to process) */
1212 size_t cchArgs = strlen(pszArgs);
1213
1214 /*
1215 * Try optional arguments until we find something which matches
1216 * or can easily be promoted to what the descriptor want.
1217 */
1218 for (;;)
1219 {
1220 char *pszArgsCopy = (char *)RTMemDup(pszArgs, cchArgs + 1);
1221 if (!pszArgsCopy)
1222 return VERR_DBGC_PARSE_NO_MEMORY;
1223
1224 int rc = dbgcEvalSub(pDbgc, pszArgs, cchArgs, paVarDescs[iVarDesc].enmCategory, pArg);
1225 if (RT_SUCCESS(rc))
1226 rc = dbgcCheckAndTypePromoteArgument(pDbgc, paVarDescs[iVarDesc].enmCategory, pArg);
1227 if (RT_SUCCESS(rc))
1228 {
1229 pArg->pDesc = pPrevDesc = &paVarDescs[iVarDesc];
1230 cCurDesc++;
1231 RTMemFree(pszArgsCopy);
1232 break;
1233 }
1234
1235 memcpy(pszArgs, pszArgsCopy, cchArgs + 1);
1236 RTMemFree(pszArgsCopy);
1237
1238 /* Continue searching optional descriptors? */
1239 if ( rc != VERR_DBGC_PARSE_INCORRECT_ARG_TYPE
1240 && rc != VERR_DBGC_PARSE_INVALID_NUMBER
1241 && rc != VERR_DBGC_PARSE_NO_RANGE_ALLOWED
1242 )
1243 return rc;
1244
1245 /* Try advance to the next descriptor. */
1246 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
1247 return rc;
1248 iVarDesc++;
1249 if (!cCurDesc)
1250 while ( iVarDesc < cVarDescs
1251 && (paVarDescs[iVarDesc].fFlags & DBGCVD_FLAGS_DEP_PREV))
1252 iVarDesc++;
1253 if (iVarDesc >= cVarDescs)
1254 return rc;
1255 cCurDesc = 0;
1256 }
1257
1258 /*
1259 * Next argument.
1260 */
1261 iVar++;
1262 pArg++;
1263 pDbgc->iArg++;
1264 *pcArgs += 1;
1265 pszArgs = psz;
1266 while (*pszArgs && RT_C_IS_BLANK(*pszArgs))
1267 pszArgs++;
1268 } while (*pszArgs);
1269
1270 /*
1271 * Check that the rest of the argument descriptors indicate optional args.
1272 */
1273 if (iVarDesc < cVarDescs)
1274 {
1275 if (cCurDesc < paVarDescs[iVarDesc].cTimesMin)
1276 return VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS;
1277 iVarDesc++;
1278 while (iVarDesc < cVarDescs)
1279 {
1280 if (paVarDescs[iVarDesc].cTimesMin)
1281 return VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS;
1282 iVarDesc++;
1283 }
1284 }
1285
1286 return VINF_SUCCESS;
1287}
1288
1289
1290/**
1291 * Evaluate one command.
1292 *
1293 * @returns VBox status code. This is also stored in DBGC::rcCmd.
1294 *
1295 * @param pDbgc Debugger console instance data.
1296 * @param pszCmd Pointer to the command.
1297 * @param cchCmd Length of the command.
1298 * @param fNoExecute Indicates that no commands should actually be executed.
1299 *
1300 * @todo Change pszCmd into argc/argv?
1301 */
1302int dbgcEvalCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd, bool fNoExecute)
1303{
1304 char *pszCmdInput = pszCmd;
1305
1306 /*
1307 * Skip blanks.
1308 */
1309 while (RT_C_IS_BLANK(*pszCmd))
1310 pszCmd++, cchCmd--;
1311
1312 /* external command? */
1313 bool const fExternal = *pszCmd == '.';
1314 if (fExternal)
1315 pszCmd++, cchCmd--;
1316
1317 /*
1318 * Find arguments.
1319 */
1320 char *pszArgs = pszCmd;
1321 while (RT_C_IS_ALNUM(*pszArgs))
1322 pszArgs++;
1323 if (*pszArgs && (!RT_C_IS_BLANK(*pszArgs) || pszArgs == pszCmd))
1324 {
1325 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Syntax error: Invalid command '%s'!\n", pszCmdInput);
1326 return pDbgc->rcCmd = VERR_DBGC_PARSE_INVALD_COMMAND_NAME;
1327 }
1328
1329 /*
1330 * Find the command.
1331 */
1332 PCDBGCCMD pCmd = dbgcCommandLookup(pDbgc, pszCmd, pszArgs - pszCmd, fExternal);
1333 if (!pCmd)
1334 {
1335 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Syntax error: Unknown command '%s'!\n", pszCmdInput);
1336 return pDbgc->rcCmd = VERR_DBGC_PARSE_COMMAND_NOT_FOUND;
1337 }
1338
1339 /*
1340 * Parse arguments (if any).
1341 */
1342 unsigned iArg;
1343 unsigned cArgs;
1344 int rc = dbgcProcessArguments(pDbgc, pCmd->pszCmd,
1345 pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs,
1346 pszArgs, &iArg, &cArgs);
1347 if (RT_SUCCESS(rc))
1348 {
1349 AssertMsg(rc == VINF_SUCCESS, ("%Rrc\n", rc));
1350
1351 /*
1352 * Execute the command.
1353 */
1354 if (!fNoExecute)
1355 rc = pCmd->pfnHandler(pCmd, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[iArg], cArgs);
1356 pDbgc->rcCmd = rc;
1357 pDbgc->iArg = iArg;
1358 if (rc == VERR_DBGC_COMMAND_FAILED)
1359 rc = VINF_SUCCESS;
1360 }
1361 else
1362 {
1363 pDbgc->rcCmd = rc;
1364 pDbgc->iArg = iArg;
1365
1366 /* report parse / eval error. */
1367 switch (rc)
1368 {
1369 case VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS:
1370 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1371 "Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd);
1372 break;
1373 case VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS:
1374 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1375 "Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd);
1376 break;
1377 case VERR_DBGC_PARSE_ARGUMENT_OVERFLOW:
1378 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1379 "Syntax error: Too many arguments.\n");
1380 break;
1381 case VERR_DBGC_PARSE_UNBALANCED_QUOTE:
1382 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1383 "Syntax error: Unbalanced quote (argument %d).\n", cArgs);
1384 break;
1385 case VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS:
1386 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1387 "Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs);
1388 break;
1389 case VERR_DBGC_PARSE_EMPTY_ARGUMENT:
1390 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1391 "Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs);
1392 break;
1393 case VERR_DBGC_PARSE_UNEXPECTED_OPERATOR:
1394 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1395 "Syntax error: Invalid operator usage (argument %d).\n", cArgs);
1396 break;
1397 case VERR_DBGC_PARSE_INVALID_NUMBER:
1398 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1399 "Syntax error: Invalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs);
1400 break;
1401 case VERR_DBGC_PARSE_NUMBER_TOO_BIG:
1402 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1403 "Error: Numeric overflow (argument %d).\n", cArgs);
1404 break;
1405 case VERR_DBGC_PARSE_INVALID_OPERATION:
1406 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1407 "Error: Invalid operation attempted (argument %d).\n", cArgs);
1408 break;
1409 case VERR_DBGC_PARSE_FUNCTION_NOT_FOUND:
1410 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1411 "Error: Function not found (argument %d).\n", cArgs);
1412 break;
1413 case VERR_DBGC_PARSE_NOT_A_FUNCTION:
1414 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1415 "Error: The function specified is not a function (argument %d).\n", cArgs);
1416 break;
1417 case VERR_DBGC_PARSE_NO_MEMORY:
1418 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1419 "Error: Out memory in the regular heap! Expect odd stuff to happen...\n", cArgs);
1420 break;
1421 case VERR_DBGC_PARSE_INCORRECT_ARG_TYPE:
1422 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1423 "Error: Incorrect argument type (argument %d?).\n", cArgs);
1424 break;
1425 case VERR_DBGC_PARSE_VARIABLE_NOT_FOUND:
1426 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1427 "Error: An undefined variable was referenced (argument %d).\n", cArgs);
1428 break;
1429 case VERR_DBGC_PARSE_CONVERSION_FAILED:
1430 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1431 "Error: A conversion between two types failed (argument %d).\n", cArgs);
1432 break;
1433 case VERR_DBGC_PARSE_NOT_IMPLEMENTED:
1434 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1435 "Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs);
1436 break;
1437 case VERR_DBGC_PARSE_BAD_RESULT_TYPE:
1438 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1439 "Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs);
1440 break;
1441 case VERR_DBGC_PARSE_WRITEONLY_SYMBOL:
1442 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1443 "Error: Cannot get symbol, it's set only (argument %d).\n", cArgs);
1444 break;
1445
1446 case VERR_DBGC_COMMAND_FAILED:
1447 break;
1448
1449 default:
1450 {
1451 PCRTSTATUSMSG pErr = RTErrGet(rc);
1452 if (strncmp(pErr->pszDefine, RT_STR_TUPLE("Unknown Status")))
1453 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Error: %s (%d) - %s\n", pErr->pszDefine, rc, pErr->pszMsgFull);
1454 else
1455 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Error: Unknown error %d (%#x)!\n", rc, rc);
1456 break;
1457 }
1458 }
1459 }
1460
1461 return rc;
1462}
1463
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