VirtualBox

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

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

A fix and docs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.2 KB
Line 
1/* $Id: DBGCEval.cpp 41572 2012-06-04 20:08:54Z 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
401/**
402 * Handles a call.
403 *
404 * @returns VBox status code. pResult contains the result on success.
405 * @param pDbgc The DBGC instance.
406 * @param pszFuncNm The function name.
407 * @param cchFuncNm The length of the function name.
408 * @param fExternal Whether it's an external name.
409 * @param pszArgs The start of the arguments (after parenthesis).
410 * @param cchArgs The length for the argument (exclusing
411 * parentesis).
412 * @param enmCategory The desired category of the result (ignored).
413 * @param pResult The result.
414 */
415static int dbgcEvalSubCall(PDBGC pDbgc, char *pszFuncNm, size_t cchFuncNm, bool fExternal, char *pszArgs, size_t cchArgs,
416 DBGCVARCAT enmCategory, PDBGCVAR pResult)
417{
418 /*
419 * Lookup the function.
420 */
421 PCDBGCFUNC pFunc = dbgcFunctionLookup(pDbgc, pszFuncNm, cchFuncNm, fExternal);
422 if (!pFunc)
423 return VERR_DBGC_PARSE_FUNCTION_NOT_FOUND;
424
425 /*
426 * Parse the arguments.
427 */
428 unsigned cArgs;
429 unsigned iArg;
430 pszArgs[cchArgs] = '\0';
431 int rc = dbgcProcessArguments(pDbgc, pFunc->pszFuncNm,
432 pFunc->cArgsMin, pFunc->cArgsMax, pFunc->paArgDescs, pFunc->cArgDescs,
433 pszArgs, &iArg, &cArgs);
434 if (RT_SUCCESS(rc))
435 rc = pFunc->pfnHandler(pFunc, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[iArg], cArgs, pResult);
436 pDbgc->iArg = iArg;
437 return rc;
438}
439
440
441/**
442 * Evaluates one argument with respect to unary operators.
443 *
444 * @returns VBox status code. pResult contains the result on success.
445 *
446 * @param pDbgc Debugger console instance data.
447 * @param pszExpr The expression string.
448 * @param cchExpr The length of the expression.
449 * @param enmCategory The target category for the result.
450 * @param pResult Where to store the result of the expression evaluation.
451 */
452static int dbgcEvalSubUnary(PDBGC pDbgc, char *pszExpr, size_t cchExpr, DBGCVARCAT enmCategory, PDBGCVAR pResult)
453{
454 Log2(("dbgcEvalSubUnary: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
455
456 /*
457 * The state of the expression is now such that it will start by zero or more
458 * unary operators and being followed by an expression of some kind.
459 * The expression is either plain or in parenthesis.
460 *
461 * Being in a lazy, recursive mode today, the parsing is done as simple as possible. :-)
462 * ASSUME: unary operators are all of equal precedence.
463 */
464 int rc = VINF_SUCCESS;
465 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, pszExpr, false, ' ');
466 if (pOp)
467 {
468 /* binary operators means syntax error. */
469 if (pOp->fBinary)
470 return VERR_DBGC_PARSE_UNEXPECTED_OPERATOR;
471
472 /*
473 * If the next expression (the one following the unary operator) is in a
474 * parenthesis a full eval is needed. If not the unary eval will suffice.
475 */
476 /* calc and strip next expr. */
477 char *pszExpr2 = pszExpr + pOp->cchName;
478 while (RT_C_IS_BLANK(*pszExpr2))
479 pszExpr2++;
480
481 if (*pszExpr2)
482 {
483 DBGCVAR Arg;
484 if (*pszExpr2 == '(')
485 rc = dbgcEvalSub(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), pOp->enmCatArg1, &Arg);
486 else
487 rc = dbgcEvalSubUnary(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), pOp->enmCatArg1, &Arg);
488 if (RT_SUCCESS(rc))
489 rc = pOp->pfnHandlerUnary(pDbgc, &Arg, enmCategory, pResult);
490 }
491 else
492 rc = VERR_DBGC_PARSE_EMPTY_ARGUMENT;
493 return rc;
494 }
495
496 /*
497 * Could this be a function call?
498 *
499 * ASSUMPTIONS:
500 * - A function name only contains alphanumerical chars and it can not
501 * start with a numerical character.
502 * - Immediately following the name is a parenthesis which must cover
503 * the remaining part of the expression.
504 */
505 bool fExternal = *pszExpr == '.';
506 char *pszFun = fExternal ? pszExpr + 1 : pszExpr;
507 char *pszFunEnd = NULL;
508 if (pszExpr[cchExpr - 1] == ')' && RT_C_IS_ALPHA(*pszFun))
509 {
510 pszFunEnd = pszExpr + 1;
511 while (*pszFunEnd != '(' && RT_C_IS_ALNUM(*pszFunEnd))
512 pszFunEnd++;
513 if (*pszFunEnd != '(')
514 pszFunEnd = NULL;
515 }
516 if (pszFunEnd)
517 {
518 size_t cchFunNm = pszFunEnd - pszFun;
519 return dbgcEvalSubCall(pDbgc, pszFun, cchFunNm, fExternal, pszFunEnd + 1, cchExpr - cchFunNm - fExternal - 2,
520 enmCategory, pResult);
521 }
522
523 /*
524 * Assuming plain expression.
525 * Didn't find any operators, so it must be a plain expression.
526 * Go by desired category first, then if anythings go, try guess.
527 */
528 switch (enmCategory)
529 {
530 case DBGCVAR_CAT_ANY:
531 return dbgcEvalSubUnaryAny(pDbgc, pszExpr, cchExpr, pResult);
532
533 case DBGCVAR_CAT_POINTER_NUMBER:
534 case DBGCVAR_CAT_POINTER_NUMBER_NO_RANGE:
535 case DBGCVAR_CAT_POINTER:
536 case DBGCVAR_CAT_POINTER_NO_RANGE:
537 case DBGCVAR_CAT_GC_POINTER:
538 case DBGCVAR_CAT_GC_POINTER_NO_RANGE:
539 case DBGCVAR_CAT_NUMBER:
540 case DBGCVAR_CAT_NUMBER_NO_RANGE:
541 /* Pointers will be promoted later. */
542 return dbgcEvalSubNumericOrPointer(pDbgc, pszExpr, cchExpr, enmCategory, pResult);
543
544 case DBGCVAR_CAT_STRING:
545 case DBGCVAR_CAT_SYMBOL:
546 /* Symbols will be promoted later. */
547 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
548
549 case DBGCVAR_CAT_OPTION:
550 case DBGCVAR_CAT_OPTION_STRING:
551 case DBGCVAR_CAT_OPTION_NUMBER:
552 return VERR_DBGC_PARSE_NOT_IMPLEMENTED;
553 }
554
555 AssertMsgFailed(("enmCategory=%d\n", enmCategory));
556 return VERR_NOT_IMPLEMENTED;
557}
558
559
560/**
561 * Evaluates one argument.
562 *
563 * @returns VBox status code.
564 *
565 * @param pDbgc Debugger console instance data.
566 * @param pszExpr The expression string.
567 * @param enmCategory The target category for the result.
568 * @param pResult Where to store the result of the expression evaluation.
569 */
570int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, DBGCVARCAT enmCategory, PDBGCVAR pResult)
571{
572 Log2(("dbgcEvalSub: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
573
574 /*
575 * First we need to remove blanks in both ends.
576 * ASSUMES: There is no quoting unless the entire expression is a string.
577 */
578
579 /* stripping. */
580 while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1]))
581 pszExpr[--cchExpr] = '\0';
582 while (RT_C_IS_BLANK(*pszExpr))
583 pszExpr++, cchExpr--;
584 if (!*pszExpr)
585 return VERR_DBGC_PARSE_EMPTY_ARGUMENT;
586
587 /*
588 * Check if there are any parenthesis which needs removing.
589 */
590 if (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')')
591 {
592 do
593 {
594 unsigned cPar = 1;
595 char *psz = pszExpr + 1;
596 char ch;
597 while ((ch = *psz) != '\0')
598 {
599 if (ch == '(')
600 cPar++;
601 else if (ch == ')')
602 {
603 if (cPar <= 0)
604 return VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS;
605 cPar--;
606 if (cPar == 0 && psz[1]) /* If not at end, there's nothing to do. */
607 break;
608 }
609 /* next */
610 psz++;
611 }
612 if (ch)
613 break;
614
615 /* remove the parenthesis. */
616 pszExpr++;
617 cchExpr -= 2;
618 pszExpr[cchExpr] = '\0';
619
620 /* strip blanks. */
621 while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1]))
622 pszExpr[--cchExpr] = '\0';
623 while (RT_C_IS_BLANK(*pszExpr))
624 pszExpr++, cchExpr--;
625 if (!*pszExpr)
626 return VERR_DBGC_PARSE_EMPTY_ARGUMENT;
627 } while (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')');
628 }
629
630 /*
631 * Now, we need to look for the binary operator with the lowest precedence.
632 *
633 * If there are no operators we're left with a simple expression which we
634 * evaluate with respect to unary operators
635 */
636 char *pszOpSplit = NULL;
637 PCDBGCOP pOpSplit = NULL;
638 unsigned cBinaryOps = 0;
639 unsigned cPar = 0;
640 unsigned cchWord = 0;
641 char chQuote = '\0';
642 char chPrev = ' ';
643 bool fBinary = false;
644 char *psz = pszExpr;
645 char ch;
646
647 while ((ch = *psz) != '\0')
648 {
649 /*
650 * String quoting.
651 */
652 if (chQuote)
653 {
654 if (ch == chQuote)
655 {
656 if (psz[1] == chQuote)
657 {
658 psz++; /* escaped quote */
659 cchWord++;
660 }
661 else
662 {
663 chQuote = '\0';
664 fBinary = true;
665 cchWord = 0;
666 }
667 }
668 else
669 cchWord++;
670 }
671 else if (ch == '"' || ch == '\'')
672 {
673 if (fBinary || cchWord)
674 return VERR_DBGC_PARSE_EXPECTED_BINARY_OP;
675 chQuote = ch;
676 }
677 /*
678 * Parentheses.
679 */
680 else if (ch == '(')
681 {
682 if (!cPar && fBinary && !cchWord)
683 return VERR_DBGC_PARSE_EXPECTED_BINARY_OP;
684 cPar++;
685 fBinary = false;
686 cchWord = 0;
687 }
688 else if (ch == ')')
689 {
690 if (cPar <= 0)
691 return VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS;
692 cPar--;
693 fBinary = true;
694 cchWord = 0;
695 }
696 /*
697 * Potential operator.
698 */
699 else if (cPar == 0 && !RT_C_IS_BLANK(ch))
700 {
701 PCDBGCOP pOp = dbgcIsOpChar(ch)
702 ? dbgcOperatorLookup(pDbgc, psz, fBinary, chPrev)
703 : NULL;
704 if (pOp)
705 {
706 /* If not the right kind of operator we've got a syntax error. */
707 if (pOp->fBinary != fBinary)
708 return VERR_DBGC_PARSE_UNEXPECTED_OPERATOR;
709
710 /*
711 * Update the parse state and skip the operator.
712 */
713 if (!pOpSplit)
714 {
715 pOpSplit = pOp;
716 pszOpSplit = psz;
717 cBinaryOps = fBinary;
718 }
719 else if (fBinary)
720 {
721 cBinaryOps++;
722 if (pOp->iPrecedence >= pOpSplit->iPrecedence)
723 {
724 pOpSplit = pOp;
725 pszOpSplit = psz;
726 }
727 }
728
729 psz += pOp->cchName - 1;
730 fBinary = false;
731 cchWord = 0;
732 }
733 else if (fBinary && !cchWord)
734 return VERR_DBGC_PARSE_EXPECTED_BINARY_OP;
735 else
736 {
737 fBinary = true;
738 cchWord++;
739 }
740 }
741 else if (cPar == 0 && RT_C_IS_BLANK(ch))
742 cchWord++;
743
744 /* next */
745 psz++;
746 chPrev = ch;
747 } /* parse loop. */
748
749 if (chQuote)
750 return VERR_DBGC_PARSE_UNBALANCED_QUOTE;
751
752 /*
753 * Either we found an operator to divide the expression by
754 * or we didn't find any. In the first case it's divide and
755 * conquer. In the latter it's a single expression which
756 * needs dealing with its unary operators if any.
757 */
758 int rc;
759 if ( cBinaryOps
760 && pOpSplit->fBinary)
761 {
762 /* process 1st sub expression. */
763 *pszOpSplit = '\0';
764 DBGCVAR Arg1;
765 rc = dbgcEvalSub(pDbgc, pszExpr, pszOpSplit - pszExpr, pOpSplit->enmCatArg1, &Arg1);
766 if (RT_SUCCESS(rc))
767 {
768 /* process 2nd sub expression. */
769 char *psz2 = pszOpSplit + pOpSplit->cchName;
770 DBGCVAR Arg2;
771 rc = dbgcEvalSub(pDbgc, psz2, cchExpr - (psz2 - pszExpr), pOpSplit->enmCatArg2, &Arg2);
772 if (RT_SUCCESS(rc))
773 /* apply the operator. */
774 rc = pOpSplit->pfnHandlerBinary(pDbgc, &Arg1, &Arg2, pResult);
775 }
776 }
777 else if (cBinaryOps)
778 {
779 /* process sub expression. */
780 pszOpSplit += pOpSplit->cchName;
781 DBGCVAR Arg;
782 rc = dbgcEvalSub(pDbgc, pszOpSplit, cchExpr - (pszOpSplit - pszExpr), pOpSplit->enmCatArg1, &Arg);
783 if (RT_SUCCESS(rc))
784 /* apply the operator. */
785 rc = pOpSplit->pfnHandlerUnary(pDbgc, &Arg, enmCategory, pResult);
786 }
787 else
788 /* plain expression, qutoed string, or using unary operators perhaps with parentheses. */
789 rc = dbgcEvalSubUnary(pDbgc, pszExpr, cchExpr, enmCategory, pResult);
790
791 return rc;
792}
793
794
795/**
796 * Worker for dbgcProcessArguments that performs type checking and promoptions.
797 *
798 * @returns VBox status code.
799 *
800 * @param pDbgc Debugger console instance data.
801 * @param enmCategory The target category for the result.
802 * @param pArg The argument to check and promote.
803 */
804static int dbgcCheckAndTypePromoteArgument(PDBGC pDbgc, DBGCVARCAT enmCategory, PDBGCVAR pArg)
805{
806 switch (enmCategory)
807 {
808 /*
809 * Anything goes
810 */
811 case DBGCVAR_CAT_ANY:
812 return VINF_SUCCESS;
813
814 /*
815 * Pointer with and without range.
816 * We can try resolve strings and symbols as symbols and promote
817 * numbers to flat GC pointers.
818 */
819 case DBGCVAR_CAT_POINTER_NO_RANGE:
820 case DBGCVAR_CAT_POINTER_NUMBER_NO_RANGE:
821 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
822 return VERR_DBGC_PARSE_NO_RANGE_ALLOWED;
823 /* fallthru */
824 case DBGCVAR_CAT_POINTER:
825 case DBGCVAR_CAT_POINTER_NUMBER:
826 switch (pArg->enmType)
827 {
828 case DBGCVAR_TYPE_GC_FLAT:
829 case DBGCVAR_TYPE_GC_FAR:
830 case DBGCVAR_TYPE_GC_PHYS:
831 case DBGCVAR_TYPE_HC_FLAT:
832 case DBGCVAR_TYPE_HC_PHYS:
833 return VINF_SUCCESS;
834
835 case DBGCVAR_TYPE_SYMBOL:
836 case DBGCVAR_TYPE_STRING:
837 {
838 DBGCVAR Var;
839 int rc = dbgcSymbolGet(pDbgc, pArg->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
840 if (RT_SUCCESS(rc))
841 {
842 /* deal with range */
843 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
844 {
845 Var.enmRangeType = pArg->enmRangeType;
846 Var.u64Range = pArg->u64Range;
847 }
848 else if (enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
849 Var.enmRangeType = DBGCVAR_RANGE_NONE;
850 *pArg = Var;
851 }
852 return rc;
853 }
854
855 case DBGCVAR_TYPE_NUMBER:
856 if ( enmCategory != DBGCVAR_CAT_POINTER_NUMBER
857 && enmCategory != DBGCVAR_CAT_POINTER_NUMBER_NO_RANGE)
858 {
859 RTGCPTR GCPtr = (RTGCPTR)pArg->u.u64Number;
860 pArg->enmType = DBGCVAR_TYPE_GC_FLAT;
861 pArg->u.GCFlat = GCPtr;
862 }
863 return VINF_SUCCESS;
864
865 default:
866 AssertMsgFailedReturn(("Invalid type %d\n"), VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
867 }
868 break; /* (not reached) */
869
870 /*
871 * GC pointer with and without range.
872 * We can try resolve strings and symbols as symbols and
873 * promote numbers to flat GC pointers.
874 */
875 case DBGCVAR_CAT_GC_POINTER_NO_RANGE:
876 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
877 return VERR_DBGC_PARSE_NO_RANGE_ALLOWED;
878 /* fallthru */
879 case DBGCVAR_CAT_GC_POINTER:
880 switch (pArg->enmType)
881 {
882 case DBGCVAR_TYPE_GC_FLAT:
883 case DBGCVAR_TYPE_GC_FAR:
884 case DBGCVAR_TYPE_GC_PHYS:
885 return VINF_SUCCESS;
886
887 case DBGCVAR_TYPE_HC_FLAT:
888 case DBGCVAR_TYPE_HC_PHYS:
889 return VERR_DBGC_PARSE_CONVERSION_FAILED;
890
891 case DBGCVAR_TYPE_SYMBOL:
892 case DBGCVAR_TYPE_STRING:
893 {
894 DBGCVAR Var;
895 int rc = dbgcSymbolGet(pDbgc, pArg->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
896 if (RT_SUCCESS(rc))
897 {
898 /* deal with range */
899 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
900 {
901 Var.enmRangeType = pArg->enmRangeType;
902 Var.u64Range = pArg->u64Range;
903 }
904 else if (enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
905 Var.enmRangeType = DBGCVAR_RANGE_NONE;
906 *pArg = Var;
907 }
908 return rc;
909 }
910
911 case DBGCVAR_TYPE_NUMBER:
912 {
913 RTGCPTR GCPtr = (RTGCPTR)pArg->u.u64Number;
914 pArg->enmType = DBGCVAR_TYPE_GC_FLAT;
915 pArg->u.GCFlat = GCPtr;
916 return VINF_SUCCESS;
917 }
918
919 default:
920 AssertMsgFailedReturn(("Invalid type %d\n"), VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
921 }
922 break; /* (not reached) */
923
924 /*
925 * Number with or without a range.
926 * Numbers can be resolved from symbols, but we cannot demote a pointer
927 * to a number.
928 */
929 case DBGCVAR_CAT_NUMBER_NO_RANGE:
930 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
931 return VERR_DBGC_PARSE_NO_RANGE_ALLOWED;
932 /* fallthru */
933 case DBGCVAR_CAT_NUMBER:
934 switch (pArg->enmType)
935 {
936 case DBGCVAR_TYPE_GC_FLAT:
937 case DBGCVAR_TYPE_GC_FAR:
938 case DBGCVAR_TYPE_GC_PHYS:
939 case DBGCVAR_TYPE_HC_FLAT:
940 case DBGCVAR_TYPE_HC_PHYS:
941 return VERR_DBGC_PARSE_INCORRECT_ARG_TYPE;
942
943 case DBGCVAR_TYPE_NUMBER:
944 return VINF_SUCCESS;
945
946 case DBGCVAR_TYPE_SYMBOL:
947 case DBGCVAR_TYPE_STRING:
948 {
949 DBGCVAR Var;
950 int rc = dbgcSymbolGet(pDbgc, pArg->u.pszString, DBGCVAR_TYPE_NUMBER, &Var);
951 if (RT_SUCCESS(rc))
952 {
953 /* deal with range */
954 if (pArg->enmRangeType != DBGCVAR_RANGE_NONE)
955 {
956 Var.enmRangeType = pArg->enmRangeType;
957 Var.u64Range = pArg->u64Range;
958 }
959 else if (enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
960 Var.enmRangeType = DBGCVAR_RANGE_NONE;
961 *pArg = Var;
962 }
963 return rc;
964 }
965
966 default:
967 AssertMsgFailedReturn(("Invalid type %d\n"), VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
968 }
969 break; /* (not reached) */
970
971 /*
972 * Symbols and strings are basically the same thing for the time being.
973 */
974 case DBGCVAR_CAT_STRING:
975 case DBGCVAR_CAT_SYMBOL:
976 {
977 switch (pArg->enmType)
978 {
979 case DBGCVAR_TYPE_STRING:
980 if (enmCategory == DBGCVAR_CAT_SYMBOL)
981 pArg->enmType = DBGCVAR_TYPE_SYMBOL;
982 return VINF_SUCCESS;
983
984 case DBGCVAR_TYPE_SYMBOL:
985 if (enmCategory == DBGCVAR_CAT_STRING)
986 pArg->enmType = DBGCVAR_TYPE_STRING;
987 return VINF_SUCCESS;
988 default:
989 break;
990 }
991
992 /* Stringify numeric and poitner values. */
993 size_t cbScratch = sizeof(pDbgc->achScratch) - (pDbgc->pszScratch - &pDbgc->achScratch[0]);
994 size_t cch = pDbgc->CmdHlp.pfnStrPrintf(&pDbgc->CmdHlp, pDbgc->pszScratch, cbScratch, "%Dv", pArg);
995 if (cch + 1 >= cbScratch)
996 return VERR_DBGC_PARSE_NO_SCRATCH;
997
998 pArg->enmType = enmCategory == DBGCVAR_CAT_STRING ? DBGCVAR_TYPE_STRING : DBGCVAR_TYPE_SYMBOL;
999 pArg->u.pszString = pDbgc->pszScratch;
1000 pArg->enmRangeType = DBGCVAR_RANGE_BYTES;
1001 pArg->u64Range = cch;
1002
1003 pDbgc->pszScratch += cch + 1;
1004 return VINF_SUCCESS;
1005 }
1006
1007 /*
1008 * These are not yet implemented.
1009 */
1010 case DBGCVAR_CAT_OPTION:
1011 case DBGCVAR_CAT_OPTION_STRING:
1012 case DBGCVAR_CAT_OPTION_NUMBER:
1013 AssertMsgFailedReturn(("Not implemented enmCategory=%d\n", enmCategory), VERR_DBGC_PARSE_NOT_IMPLEMENTED);
1014
1015 default:
1016 AssertMsgFailedReturn(("Bad enmCategory=%d\n", enmCategory), VERR_DBGC_PARSE_NOT_IMPLEMENTED);
1017 }
1018}
1019
1020
1021/**
1022 * Parses the arguments of one command.
1023 *
1024 * @returns VBox statuc code. On parser errors the index of the troublesome
1025 * argument is indicated by *pcArg.
1026 *
1027 * @param pDbgc Debugger console instance data.
1028 * @param pszCmdOrFunc The name of the function or command. (For logging.)
1029 * @param cArgsMin See DBGCCMD::cArgsMin and DBGCFUNC::cArgsMin.
1030 * @param cArgsMax See DBGCCMD::cArgsMax and DBGCFUNC::cArgsMax.
1031 * @param paVarDescs See DBGCCMD::paVarDescs and DBGCFUNC::paVarDescs.
1032 * @param cVarDescs See DBGCCMD::cVarDescs and DBGCFUNC::cVarDescs.
1033 * @param pszArg Pointer to the arguments to parse.
1034 * @param piArg Where to return the index of the first argument in
1035 * DBGC::aArgs. Always set. Caller must restore DBGC::iArg
1036 * to this value when done, even on failure.
1037 * @param pcArgs Where to store the number of arguments. In the event
1038 * of an error this is (ab)used to store the index of the
1039 * offending argument.
1040 */
1041static int dbgcProcessArguments(PDBGC pDbgc, const char *pszCmdOrFunc,
1042 uint32_t const cArgsMin, uint32_t const cArgsMax,
1043 PCDBGCVARDESC const paVarDescs, uint32_t const cVarDescs,
1044 char *pszArgs, unsigned *piArg, unsigned *pcArgs)
1045{
1046 Log2(("dbgcProcessArguments: pszCmdOrFunc=%s pszArgs='%s'\n", pszCmdOrFunc, pszArgs));
1047
1048 /*
1049 * Check if we have any argument and if the command takes any.
1050 */
1051 *piArg = pDbgc->iArg;
1052 *pcArgs = 0;
1053 /* strip leading blanks. */
1054 while (*pszArgs && RT_C_IS_BLANK(*pszArgs))
1055 pszArgs++;
1056 if (!*pszArgs)
1057 {
1058 if (!cArgsMin)
1059 return VINF_SUCCESS;
1060 return VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS;
1061 }
1062 if (!cArgsMax)
1063 return VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS;
1064
1065 /*
1066 * The parse loop.
1067 */
1068 PDBGCVAR pArg = &pDbgc->aArgs[pDbgc->iArg];
1069 PCDBGCVARDESC pPrevDesc = NULL;
1070 unsigned cCurDesc = 0;
1071 unsigned iVar = 0;
1072 unsigned iVarDesc = 0;
1073 *pcArgs = 0;
1074 do
1075 {
1076 /*
1077 * Can we have another argument?
1078 */
1079 if (*pcArgs >= cArgsMax)
1080 return VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS;
1081 if (pDbgc->iArg >= RT_ELEMENTS(pDbgc->aArgs))
1082 return VERR_DBGC_PARSE_ARGUMENT_OVERFLOW;
1083 if (iVarDesc >= cVarDescs)
1084 return VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS;
1085
1086 /* Walk argument descriptors. */
1087 if (cCurDesc >= paVarDescs[iVarDesc].cTimesMax)
1088 {
1089 iVarDesc++;
1090 if (iVarDesc >= cVarDescs)
1091 return VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS;
1092 cCurDesc = 0;
1093 }
1094
1095 /*
1096 * Find the end of the argument. This is just rough splitting,
1097 * dbgcEvalSub will do stricter syntax checking later on.
1098 */
1099 int cPar = 0;
1100 char chQuote = '\0';
1101 char *pszEnd = NULL;
1102 char *psz = pszArgs;
1103 char ch;
1104 bool fBinary = false;
1105 for (;;)
1106 {
1107 /*
1108 * Check for the end.
1109 */
1110 if ((ch = *psz) == '\0')
1111 {
1112 if (chQuote)
1113 return VERR_DBGC_PARSE_UNBALANCED_QUOTE;
1114 if (cPar)
1115 return VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS;
1116 pszEnd = psz;
1117 break;
1118 }
1119 /*
1120 * When quoted we ignore everything but the quotation char.
1121 * We use the REXX way of escaping the quotation char, i.e. double occurrence.
1122 */
1123 else if (chQuote)
1124 {
1125 if (ch == chQuote)
1126 {
1127 if (psz[1] == chQuote)
1128 psz++; /* skip the escaped quote char */
1129 else
1130 {
1131 chQuote = '\0'; /* end of quoted string. */
1132 fBinary = true;
1133 }
1134 }
1135 }
1136 else if (ch == '\'' || ch == '"')
1137 {
1138 if (fBinary)
1139 return VERR_DBGC_PARSE_EXPECTED_BINARY_OP;
1140 chQuote = ch;
1141 }
1142 /*
1143 * Parenthesis can of course be nested.
1144 */
1145 else if (ch == '(')
1146 {
1147 cPar++;
1148 fBinary = false;
1149 }
1150 else if (ch == ')')
1151 {
1152 if (!cPar)
1153 return VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS;
1154 cPar--;
1155 fBinary = true;
1156 }
1157 else if (!cPar)
1158 {
1159 /*
1160 * Encountering a comma is a definite end of parameter.
1161 */
1162 if (ch == ',')
1163 {
1164 pszEnd = psz++;
1165 break;
1166 }
1167
1168 /*
1169 * Encountering blanks may mean the end of it all. A binary
1170 * operator will force continued parsing.
1171 */
1172 if (RT_C_IS_BLANK(ch))
1173 {
1174 pszEnd = psz++; /* in case it's the end. */
1175 while (RT_C_IS_BLANK(*psz))
1176 psz++;
1177
1178 if (*psz == ',')
1179 {
1180 psz++;
1181 break;
1182 }
1183
1184 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
1185 if (!pOp || pOp->fBinary != fBinary)
1186 break; /* the end. */
1187
1188 psz += pOp->cchName;
1189 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
1190 psz++;
1191 fBinary = false;
1192 continue;
1193 }
1194
1195 /*
1196 * Look for operators without a space up front.
1197 */
1198 if (dbgcIsOpChar(ch))
1199 {
1200 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
1201 if (pOp)
1202 {
1203 if (pOp->fBinary != fBinary)
1204 {
1205 pszEnd = psz;
1206 /** @todo this is a parsing error really. */
1207 break; /* the end. */
1208 }
1209 psz += pOp->cchName;
1210 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
1211 psz++;
1212 fBinary = false;
1213 continue;
1214 }
1215 }
1216 fBinary = true;
1217 }
1218
1219 /* next char */
1220 psz++;
1221 }
1222 *pszEnd = '\0';
1223 /* (psz = next char to process) */
1224 size_t cchArgs = strlen(pszArgs);
1225
1226 /*
1227 * Try optional arguments until we find something which matches
1228 * or can easily be promoted to what the descriptor want.
1229 */
1230 for (;;)
1231 {
1232 char *pszArgsCopy = (char *)RTMemDup(pszArgs, cchArgs + 1);
1233 if (!pszArgsCopy)
1234 return VERR_DBGC_PARSE_NO_MEMORY;
1235
1236 int rc = dbgcEvalSub(pDbgc, pszArgs, cchArgs, paVarDescs[iVarDesc].enmCategory, pArg);
1237 if (RT_SUCCESS(rc))
1238 rc = dbgcCheckAndTypePromoteArgument(pDbgc, paVarDescs[iVarDesc].enmCategory, pArg);
1239 if (RT_SUCCESS(rc))
1240 {
1241 pArg->pDesc = pPrevDesc = &paVarDescs[iVarDesc];
1242 cCurDesc++;
1243 RTMemFree(pszArgsCopy);
1244 break;
1245 }
1246
1247 memcpy(pszArgs, pszArgsCopy, cchArgs + 1);
1248 RTMemFree(pszArgsCopy);
1249
1250 /* Continue searching optional descriptors? */
1251 if ( rc != VERR_DBGC_PARSE_INCORRECT_ARG_TYPE
1252 && rc != VERR_DBGC_PARSE_INVALID_NUMBER
1253 && rc != VERR_DBGC_PARSE_NO_RANGE_ALLOWED
1254 )
1255 return rc;
1256
1257 /* Try advance to the next descriptor. */
1258 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
1259 return rc;
1260 iVarDesc++;
1261 if (!cCurDesc)
1262 while ( iVarDesc < cVarDescs
1263 && (paVarDescs[iVarDesc].fFlags & DBGCVD_FLAGS_DEP_PREV))
1264 iVarDesc++;
1265 if (iVarDesc >= cVarDescs)
1266 return rc;
1267 cCurDesc = 0;
1268 }
1269
1270 /*
1271 * Next argument.
1272 */
1273 iVar++;
1274 pArg++;
1275 pDbgc->iArg++;
1276 *pcArgs += 1;
1277 pszArgs = psz;
1278 while (*pszArgs && RT_C_IS_BLANK(*pszArgs))
1279 pszArgs++;
1280 } while (*pszArgs);
1281
1282 /*
1283 * Check that the rest of the argument descriptors indicate optional args.
1284 */
1285 if (iVarDesc < cVarDescs)
1286 {
1287 if (cCurDesc < paVarDescs[iVarDesc].cTimesMin)
1288 return VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS;
1289 iVarDesc++;
1290 while (iVarDesc < cVarDescs)
1291 {
1292 if (paVarDescs[iVarDesc].cTimesMin)
1293 return VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS;
1294 iVarDesc++;
1295 }
1296 }
1297
1298 return VINF_SUCCESS;
1299}
1300
1301
1302/**
1303 * Evaluate one command.
1304 *
1305 * @returns VBox status code. This is also stored in DBGC::rcCmd.
1306 *
1307 * @param pDbgc Debugger console instance data.
1308 * @param pszCmd Pointer to the command.
1309 * @param cchCmd Length of the command.
1310 * @param fNoExecute Indicates that no commands should actually be executed.
1311 *
1312 * @todo Change pszCmd into argc/argv?
1313 */
1314int dbgcEvalCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd, bool fNoExecute)
1315{
1316 char *pszCmdInput = pszCmd;
1317
1318 /*
1319 * Skip blanks.
1320 */
1321 while (RT_C_IS_BLANK(*pszCmd))
1322 pszCmd++, cchCmd--;
1323
1324 /* external command? */
1325 bool const fExternal = *pszCmd == '.';
1326 if (fExternal)
1327 pszCmd++, cchCmd--;
1328
1329 /*
1330 * Find arguments.
1331 */
1332 char *pszArgs = pszCmd;
1333 while (RT_C_IS_ALNUM(*pszArgs))
1334 pszArgs++;
1335 if (*pszArgs && (!RT_C_IS_BLANK(*pszArgs) || pszArgs == pszCmd))
1336 {
1337 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Syntax error: Invalid command '%s'!\n", pszCmdInput);
1338 return pDbgc->rcCmd = VERR_DBGC_PARSE_INVALD_COMMAND_NAME;
1339 }
1340
1341 /*
1342 * Find the command.
1343 */
1344 PCDBGCCMD pCmd = dbgcCommandLookup(pDbgc, pszCmd, pszArgs - pszCmd, fExternal);
1345 if (!pCmd)
1346 {
1347 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Syntax error: Unknown command '%s'!\n", pszCmdInput);
1348 return pDbgc->rcCmd = VERR_DBGC_PARSE_COMMAND_NOT_FOUND;
1349 }
1350
1351 /*
1352 * Parse arguments (if any).
1353 */
1354 unsigned iArg;
1355 unsigned cArgs;
1356 int rc = dbgcProcessArguments(pDbgc, pCmd->pszCmd,
1357 pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs,
1358 pszArgs, &iArg, &cArgs);
1359 if (RT_SUCCESS(rc))
1360 {
1361 AssertMsg(rc == VINF_SUCCESS, ("%Rrc\n", rc));
1362
1363 /*
1364 * Execute the command.
1365 */
1366 if (!fNoExecute)
1367 rc = pCmd->pfnHandler(pCmd, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[iArg], cArgs);
1368 pDbgc->rcCmd = rc;
1369 pDbgc->iArg = iArg;
1370 if (rc == VERR_DBGC_COMMAND_FAILED)
1371 rc = VINF_SUCCESS;
1372 }
1373 else
1374 {
1375 pDbgc->rcCmd = rc;
1376 pDbgc->iArg = iArg;
1377
1378 /* report parse / eval error. */
1379 switch (rc)
1380 {
1381 case VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS:
1382 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1383 "Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd);
1384 break;
1385 case VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS:
1386 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1387 "Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd);
1388 break;
1389 case VERR_DBGC_PARSE_ARGUMENT_OVERFLOW:
1390 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1391 "Syntax error: Too many arguments.\n");
1392 break;
1393 case VERR_DBGC_PARSE_UNBALANCED_QUOTE:
1394 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1395 "Syntax error: Unbalanced quote (argument %d).\n", cArgs);
1396 break;
1397 case VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS:
1398 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1399 "Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs);
1400 break;
1401 case VERR_DBGC_PARSE_EMPTY_ARGUMENT:
1402 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1403 "Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs);
1404 break;
1405 case VERR_DBGC_PARSE_UNEXPECTED_OPERATOR:
1406 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1407 "Syntax error: Invalid operator usage (argument %d).\n", cArgs);
1408 break;
1409 case VERR_DBGC_PARSE_INVALID_NUMBER:
1410 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1411 "Syntax error: Invalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs);
1412 break;
1413 case VERR_DBGC_PARSE_NUMBER_TOO_BIG:
1414 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1415 "Error: Numeric overflow (argument %d).\n", cArgs);
1416 break;
1417 case VERR_DBGC_PARSE_INVALID_OPERATION:
1418 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1419 "Error: Invalid operation attempted (argument %d).\n", cArgs);
1420 break;
1421 case VERR_DBGC_PARSE_FUNCTION_NOT_FOUND:
1422 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1423 "Error: Function not found (argument %d).\n", cArgs);
1424 break;
1425 case VERR_DBGC_PARSE_NOT_A_FUNCTION:
1426 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1427 "Error: The function specified is not a function (argument %d).\n", cArgs);
1428 break;
1429 case VERR_DBGC_PARSE_NO_MEMORY:
1430 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1431 "Error: Out memory in the regular heap! Expect odd stuff to happen...\n", cArgs);
1432 break;
1433 case VERR_DBGC_PARSE_INCORRECT_ARG_TYPE:
1434 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1435 "Error: Incorrect argument type (argument %d?).\n", cArgs);
1436 break;
1437 case VERR_DBGC_PARSE_VARIABLE_NOT_FOUND:
1438 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1439 "Error: An undefined variable was referenced (argument %d).\n", cArgs);
1440 break;
1441 case VERR_DBGC_PARSE_CONVERSION_FAILED:
1442 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1443 "Error: A conversion between two types failed (argument %d).\n", cArgs);
1444 break;
1445 case VERR_DBGC_PARSE_NOT_IMPLEMENTED:
1446 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1447 "Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs);
1448 break;
1449 case VERR_DBGC_PARSE_BAD_RESULT_TYPE:
1450 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1451 "Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs);
1452 break;
1453 case VERR_DBGC_PARSE_WRITEONLY_SYMBOL:
1454 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1455 "Error: Cannot get symbol, it's set only (argument %d).\n", cArgs);
1456 break;
1457
1458 case VERR_DBGC_COMMAND_FAILED:
1459 break;
1460
1461 default:
1462 {
1463 PCRTSTATUSMSG pErr = RTErrGet(rc);
1464 if (strncmp(pErr->pszDefine, RT_STR_TUPLE("Unknown Status")))
1465 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Error: %s (%d) - %s\n", pErr->pszDefine, rc, pErr->pszMsgFull);
1466 else
1467 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Error: Unknown error %d (%#x)!\n", rc, rc);
1468 break;
1469 }
1470 }
1471 }
1472
1473 return rc;
1474}
1475
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