VirtualBox

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

Last change on this file since 45907 was 44399, checked in by vboxsync, 12 years ago

DBGF,DBGC,++: PVM -> PUVM. Some refactoring and cleanup as well.

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