VirtualBox

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

Last change on this file since 98123 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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