VirtualBox

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

Last change on this file since 106199 was 106061, checked in by vboxsync, 2 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.7 KB
Line 
1/* $Id: DBGCEval.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, command evaluator.
4 */
5
6/*
7 * Copyright (C) 2006-2024 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 */
98static size_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 */
111static char *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 unsigned cCurDesc = 0;
1094 unsigned iVarDesc = 0;
1095 *pcArgs = 0;
1096 do
1097 {
1098 /*
1099 * Can we have another argument?
1100 */
1101 if (*pcArgs >= cArgsMax)
1102 return VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS;
1103 if (pDbgc->iArg >= RT_ELEMENTS(pDbgc->aArgs))
1104 return VERR_DBGC_PARSE_ARGUMENT_OVERFLOW;
1105 if (iVarDesc >= cVarDescs)
1106 return VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS;
1107
1108 /* Walk argument descriptors. */
1109 if (cCurDesc >= paVarDescs[iVarDesc].cTimesMax)
1110 {
1111 iVarDesc++;
1112 if (iVarDesc >= cVarDescs)
1113 return VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS;
1114 cCurDesc = 0;
1115 }
1116
1117 /*
1118 * Find the end of the argument. This is just rough splitting,
1119 * dbgcEvalSub will do stricter syntax checking later on.
1120 */
1121 int cPar = 0;
1122 char chQuote = '\0';
1123 char *pszEnd = NULL;
1124 char *psz = pszArgs;
1125 char ch;
1126 bool fBinary = false;
1127 for (;;)
1128 {
1129 /*
1130 * Check for the end.
1131 */
1132 if ((ch = *psz) == '\0')
1133 {
1134 if (chQuote)
1135 return VERR_DBGC_PARSE_UNBALANCED_QUOTE;
1136 if (cPar)
1137 return VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS;
1138 pszEnd = psz;
1139 break;
1140 }
1141 /*
1142 * When quoted we ignore everything but the quotation char.
1143 * We use the REXX way of escaping the quotation char, i.e. double occurrence.
1144 */
1145 else if (chQuote)
1146 {
1147 if (ch == chQuote)
1148 {
1149 if (psz[1] == chQuote)
1150 psz++; /* skip the escaped quote char */
1151 else
1152 {
1153 chQuote = '\0'; /* end of quoted string. */
1154 fBinary = true;
1155 }
1156 }
1157 }
1158 else if (ch == '\'' || ch == '"')
1159 {
1160 if (fBinary)
1161 return VERR_DBGC_PARSE_EXPECTED_BINARY_OP;
1162 chQuote = ch;
1163 }
1164 /*
1165 * Parenthesis can of course be nested.
1166 */
1167 else if (ch == '(')
1168 {
1169 cPar++;
1170 fBinary = false;
1171 }
1172 else if (ch == ')')
1173 {
1174 if (!cPar)
1175 return VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS;
1176 cPar--;
1177 fBinary = true;
1178 }
1179 else if (!cPar)
1180 {
1181 /*
1182 * Encountering a comma is a definite end of parameter.
1183 */
1184 if (ch == ',')
1185 {
1186 pszEnd = psz++;
1187 break;
1188 }
1189
1190 /*
1191 * Encountering blanks may mean the end of it all. A binary
1192 * operator will force continued parsing.
1193 */
1194 if (RT_C_IS_BLANK(ch))
1195 {
1196 pszEnd = psz++; /* in case it's the end. */
1197 while (RT_C_IS_BLANK(*psz))
1198 psz++;
1199
1200 if (*psz == ',')
1201 {
1202 psz++;
1203 break;
1204 }
1205
1206 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
1207 if (!pOp || pOp->fBinary != fBinary)
1208 break; /* the end. */
1209
1210 psz += pOp->cchName;
1211 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
1212 psz++;
1213 fBinary = false;
1214 continue;
1215 }
1216
1217 /*
1218 * Look for operators without a space up front.
1219 */
1220 if (dbgcIsOpChar(ch))
1221 {
1222 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
1223 if (pOp)
1224 {
1225 if (pOp->fBinary != fBinary)
1226 {
1227 pszEnd = psz;
1228 /** @todo this is a parsing error really. */
1229 break; /* the end. */
1230 }
1231 psz += pOp->cchName;
1232 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
1233 psz++;
1234 fBinary = false;
1235 continue;
1236 }
1237 }
1238 fBinary = true;
1239 }
1240
1241 /* next char */
1242 psz++;
1243 }
1244 *pszEnd = '\0';
1245 /* (psz = next char to process) */
1246 size_t cchArgs = strlen(pszArgs);
1247
1248 /*
1249 * Try optional arguments until we find something which matches
1250 * or can easily be promoted to what the descriptor want.
1251 */
1252 for (;;)
1253 {
1254 char *pszArgsCopy = (char *)RTMemDup(pszArgs, cchArgs + 1);
1255 if (!pszArgsCopy)
1256 return VERR_DBGC_PARSE_NO_MEMORY;
1257
1258 int rc = dbgcEvalSub(pDbgc, pszArgs, cchArgs, paVarDescs[iVarDesc].enmCategory, pArg);
1259 if (RT_SUCCESS(rc))
1260 rc = dbgcCheckAndTypePromoteArgument(pDbgc, paVarDescs[iVarDesc].enmCategory, pArg);
1261 if (RT_SUCCESS(rc))
1262 {
1263 pArg->pDesc = &paVarDescs[iVarDesc];
1264 cCurDesc++;
1265 RTMemFree(pszArgsCopy);
1266 break;
1267 }
1268
1269 memcpy(pszArgs, pszArgsCopy, cchArgs + 1);
1270 RTMemFree(pszArgsCopy);
1271
1272 /* Continue searching optional descriptors? */
1273 if ( rc != VERR_DBGC_PARSE_INCORRECT_ARG_TYPE
1274 && rc != VERR_DBGC_PARSE_INVALID_NUMBER
1275 && rc != VERR_DBGC_PARSE_NO_RANGE_ALLOWED
1276 )
1277 return rc;
1278
1279 /* Try advance to the next descriptor. */
1280 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
1281 return rc;
1282 iVarDesc++;
1283 if (!cCurDesc)
1284 while ( iVarDesc < cVarDescs
1285 && (paVarDescs[iVarDesc].fFlags & DBGCVD_FLAGS_DEP_PREV))
1286 iVarDesc++;
1287 if (iVarDesc >= cVarDescs)
1288 return rc;
1289 cCurDesc = 0;
1290 }
1291
1292 /*
1293 * Next argument.
1294 */
1295 pArg++;
1296 pDbgc->iArg++;
1297 *pcArgs += 1;
1298 pszArgs = psz;
1299 while (*pszArgs && RT_C_IS_BLANK(*pszArgs))
1300 pszArgs++;
1301 } while (*pszArgs);
1302
1303 /*
1304 * Check that the rest of the argument descriptors indicate optional args.
1305 */
1306 if (iVarDesc < cVarDescs)
1307 {
1308 if (cCurDesc < paVarDescs[iVarDesc].cTimesMin)
1309 return VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS;
1310 iVarDesc++;
1311 while (iVarDesc < cVarDescs)
1312 {
1313 if (paVarDescs[iVarDesc].cTimesMin)
1314 return VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS;
1315 iVarDesc++;
1316 }
1317 }
1318
1319 return VINF_SUCCESS;
1320}
1321
1322
1323/**
1324 * Evaluate one command.
1325 *
1326 * @returns VBox status code. This is also stored in DBGC::rcCmd.
1327 *
1328 * @param pDbgc Debugger console instance data.
1329 * @param pszCmd Pointer to the command.
1330 * @param cchCmd Length of the command.
1331 * @param fNoExecute Indicates that no commands should actually be executed.
1332 */
1333int dbgcEvalCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd, bool fNoExecute)
1334{
1335 Assert(RTStrNLen(pszCmd, cchCmd) == cchCmd);
1336 char *pszCmdInput = pszCmd;
1337
1338 /*
1339 * Skip blanks.
1340 */
1341 while (RT_C_IS_BLANK(*pszCmd))
1342 pszCmd++, cchCmd--;
1343
1344 /* external command? */
1345 bool const fExternal = *pszCmd == '.';
1346 if (fExternal)
1347 pszCmd++, cchCmd--;
1348
1349 /*
1350 * Find the end of the command name.
1351 */
1352 size_t cchName = 0;
1353 while (cchName < cchCmd)
1354 {
1355 char const ch = pszCmd[cchName];
1356 if (RT_C_IS_ALNUM(ch) || ch == '_')
1357 cchName++;
1358 else if (RT_C_IS_SPACE(ch))
1359 break;
1360 else
1361 {
1362 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Syntax error: Invalid command '%s'!\n", pszCmdInput);
1363 return pDbgc->rcCmd = VERR_DBGC_PARSE_INVALD_COMMAND_NAME;
1364 }
1365 }
1366
1367 /*
1368 * Find the command.
1369 */
1370 PCDBGCCMD pCmd = dbgcCommandLookup(pDbgc, pszCmd, cchName, fExternal);
1371 if (!pCmd)
1372 {
1373 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Syntax error: Unknown command '%s'!\n", pszCmdInput);
1374 return pDbgc->rcCmd = VERR_DBGC_PARSE_COMMAND_NOT_FOUND;
1375 }
1376
1377 /*
1378 * Parse arguments (if any).
1379 *
1380 * If the input isn't zero terminated, we have to make a copy because the
1381 * argument parser code is to crappy to deal with sub-strings at present.
1382 */
1383 size_t offArgs = cchName;
1384 while (offArgs < cchCmd && RT_C_IS_SPACE(pszCmd[offArgs]))
1385 offArgs++;
1386
1387 char szEmpty[] = "";
1388 char *pszArgsFree = NULL;
1389 char *pszArgs = offArgs < cchCmd ? &pszCmd[offArgs] : szEmpty;
1390 if (pszArgs[cchCmd - offArgs] != '\0')
1391 {
1392 /** @todo rewrite the code so it doesn't require modifiable input! */
1393 pszArgsFree = pszArgs = (char *)RTMemDupEx(pszArgs, cchCmd - offArgs, 1);
1394 AssertReturn(pszArgs, VERR_NO_MEMORY);
1395 }
1396
1397 unsigned iArg;
1398 unsigned cArgs;
1399 int rc = dbgcProcessArguments(pDbgc, pCmd->pszCmd,
1400 pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs,
1401 pszArgs, &iArg, &cArgs);
1402 if (RT_SUCCESS(rc))
1403 {
1404 AssertMsg(rc == VINF_SUCCESS, ("%Rrc\n", rc));
1405
1406 /*
1407 * Execute the command.
1408 */
1409 if (!fNoExecute)
1410 rc = pCmd->pfnHandler(pCmd, &pDbgc->CmdHlp, pDbgc->pUVM, &pDbgc->aArgs[iArg], cArgs);
1411 pDbgc->rcCmd = rc;
1412 pDbgc->iArg = iArg;
1413 if (rc == VERR_DBGC_COMMAND_FAILED)
1414 rc = VINF_SUCCESS;
1415 }
1416 else
1417 {
1418 pDbgc->rcCmd = rc;
1419 pDbgc->iArg = iArg;
1420
1421 /* report parse / eval error. */
1422 switch (rc)
1423 {
1424 case VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS:
1425 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1426 "Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd);
1427 break;
1428 case VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS:
1429 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1430 "Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd);
1431 break;
1432 case VERR_DBGC_PARSE_ARGUMENT_OVERFLOW:
1433 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1434 "Syntax error: Too many arguments.\n");
1435 break;
1436 case VERR_DBGC_PARSE_UNBALANCED_QUOTE:
1437 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1438 "Syntax error: Unbalanced quote (argument %d).\n", cArgs);
1439 break;
1440 case VERR_DBGC_PARSE_UNBALANCED_PARENTHESIS:
1441 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1442 "Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs);
1443 break;
1444 case VERR_DBGC_PARSE_EMPTY_ARGUMENT:
1445 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1446 "Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs);
1447 break;
1448 case VERR_DBGC_PARSE_UNEXPECTED_OPERATOR:
1449 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1450 "Syntax error: Invalid operator usage (argument %d).\n", cArgs);
1451 break;
1452 case VERR_DBGC_PARSE_INVALID_NUMBER:
1453 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1454 "Syntax error: Invalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs);
1455 break;
1456 case VERR_DBGC_PARSE_NUMBER_TOO_BIG:
1457 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1458 "Error: Numeric overflow (argument %d).\n", cArgs);
1459 break;
1460 case VERR_DBGC_PARSE_INVALID_OPERATION:
1461 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1462 "Error: Invalid operation attempted (argument %d).\n", cArgs);
1463 break;
1464 case VERR_DBGC_PARSE_FUNCTION_NOT_FOUND:
1465 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1466 "Error: Function not found (argument %d).\n", cArgs);
1467 break;
1468 case VERR_DBGC_PARSE_NOT_A_FUNCTION:
1469 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1470 "Error: The function specified is not a function (argument %d).\n", cArgs);
1471 break;
1472 case VERR_DBGC_PARSE_NO_MEMORY:
1473 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1474 "Error: Out memory in the regular heap! Expect odd stuff to happen...\n");
1475 break;
1476 case VERR_DBGC_PARSE_INCORRECT_ARG_TYPE:
1477 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1478 "Error: Incorrect argument type (argument %d?).\n", cArgs);
1479 break;
1480 case VERR_DBGC_PARSE_VARIABLE_NOT_FOUND:
1481 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1482 "Error: An undefined variable was referenced (argument %d).\n", cArgs);
1483 break;
1484 case VERR_DBGC_PARSE_CONVERSION_FAILED:
1485 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1486 "Error: A conversion between two types failed (argument %d).\n", cArgs);
1487 break;
1488 case VERR_DBGC_PARSE_NOT_IMPLEMENTED:
1489 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1490 "Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs);
1491 break;
1492 case VERR_DBGC_PARSE_BAD_RESULT_TYPE:
1493 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1494 "Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs);
1495 break;
1496 case VERR_DBGC_PARSE_WRITEONLY_SYMBOL:
1497 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1498 "Error: Cannot get symbol, it's set only (argument %d).\n", cArgs);
1499 break;
1500
1501 case VERR_DBGC_COMMAND_FAILED:
1502 break;
1503
1504 default:
1505 if (RTErrIsKnown(rc))
1506 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Error: %Rra\n", rc);
1507 else
1508 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Error: Unknown error %d (%#x)!\n", rc, rc);
1509 break;
1510 }
1511 }
1512
1513 RTMemFree(pszArgsFree);
1514 return rc;
1515}
1516
1517
1518/**
1519 * Evaluate one or commands separated by ';' or '\n'.
1520 *
1521 * @returns VBox status code. This is also stored in DBGC::rcCmd.
1522 *
1523 * @param pDbgc Debugger console instance data.
1524 * @param pszCmds Pointer to the command.
1525 * @param cchCmds Length of the command.
1526 * @param fNoExecute Indicates that no commands should actually be executed.
1527 */
1528int dbgcEvalCommands(PDBGC pDbgc, char *pszCmds, size_t cchCmds, bool fNoExecute)
1529{
1530 /*
1531 * Trim the input.
1532 */
1533 while (cchCmds > 0 && RT_C_IS_SPACE(pszCmds[cchCmds]))
1534 cchCmds--;
1535 while (cchCmds > 0 && RT_C_IS_SPACE(*pszCmds))
1536 cchCmds--, pszCmds++;
1537
1538 /*
1539 * Split up the commands and pass them to dbgcEvalCommand.
1540 */
1541 int rcRet = VINF_SUCCESS;
1542 char chQuote = 0;
1543 size_t offStart = 0;
1544 size_t off = 0;
1545 while (off < cchCmds)
1546 {
1547 char const ch = pszCmds[off];
1548 if (ch == '"' || ch == '\'')
1549 chQuote = ch == chQuote ? 0 : chQuote == 0 ? ch : chQuote;
1550 else if (ch == ';' || ch == '\n')
1551 {
1552 /* Skip leading blanks and ignore empty commands. */
1553 while (offStart < off && RT_C_IS_SPACE(pszCmds[offStart]))
1554 offStart++;
1555 if (off > offStart)
1556 {
1557 int rc = dbgcEvalCommand(pDbgc, &pszCmds[offStart], off - offStart, fNoExecute);
1558 if (rcRet == VINF_SUCCESS || (RT_SUCCESS(rcRet) && RT_FAILURE(rc)))
1559 rcRet = rc;
1560 if ( rc == VERR_DBGC_QUIT
1561 || rc == VWRN_DBGC_CMD_PENDING)
1562 break;
1563 }
1564 offStart = ++off;
1565 continue;
1566 }
1567 off++;
1568 }
1569
1570 /*
1571 * Pending command?
1572 *
1573 * No need to skip leading blanks here in order to check for empty
1574 * commands, since we've already trimmed off tailing blanks.)
1575 */
1576 if (off > offStart)
1577 {
1578 int rc = dbgcEvalCommand(pDbgc, &pszCmds[offStart], off - offStart, fNoExecute);
1579 if (rcRet == VINF_SUCCESS || (RT_SUCCESS(rcRet) && RT_FAILURE(rc)))
1580 rcRet = rc;
1581 }
1582
1583 return rcRet;
1584}
1585
1586
1587/**
1588 * Loads the script in @a pszFilename and executes the commands within.
1589 *
1590 * @returns VBox status code. Will complain about error to console.
1591 * @param pDbgc Debugger console instance data.
1592 * @param pszFilename The path to the script file.
1593 * @param fAnnounce Whether to announce the script.
1594 */
1595int dbgcEvalScript(PDBGC pDbgc, const char *pszFilename, bool fAnnounce)
1596{
1597 FILE *pFile = fopen(pszFilename, "r");
1598 if (!pFile)
1599 return DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Failed to open '%s'.\n", pszFilename);
1600 if (fAnnounce)
1601 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Running script '%s'...\n", pszFilename);
1602
1603 /*
1604 * Execute it line by line.
1605 */
1606 int rc = VINF_SUCCESS;
1607 unsigned iLine = 0;
1608 char szLine[8192];
1609 while (fgets(szLine, sizeof(szLine), pFile))
1610 {
1611 /* check that the line isn't too long. */
1612 char *pszEnd = strchr(szLine, '\0');
1613 if (pszEnd == &szLine[sizeof(szLine) - 1])
1614 {
1615 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "runscript error: Line #%u is too long\n", iLine);
1616 break;
1617 }
1618 iLine++;
1619
1620 /* strip leading blanks and check for comment / blank line. */
1621 char *psz = RTStrStripL(szLine);
1622 if ( *psz == '\0'
1623 || *psz == '\n'
1624 || *psz == '#')
1625 continue;
1626
1627 /* strip trailing blanks and check for empty line (\r case). */
1628 while ( pszEnd > psz
1629 && RT_C_IS_SPACE(pszEnd[-1])) /* RT_C_IS_SPACE includes \n and \r normally. */
1630 *--pszEnd = '\0';
1631
1632 /** @todo check for Control-C / Cancel at this point... */
1633
1634 /*
1635 * Execute the command.
1636 *
1637 * This is a bit wasteful with scratch space btw., can fix it later.
1638 * The whole return code crap should be fixed too, so that it's possible
1639 * to know whether a command succeeded (RT_SUCCESS()) or failed, and
1640 * more importantly why it failed.
1641 */
1642 /** @todo optimize this. */
1643 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "%s", psz);
1644 if (RT_FAILURE(rc))
1645 {
1646 if (rc == VERR_BUFFER_OVERFLOW)
1647 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "runscript error: Line #%u is too long (exec overflowed)\n", iLine);
1648 break;
1649 }
1650 if (rc == VWRN_DBGC_CMD_PENDING)
1651 {
1652 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "runscript error: VWRN_DBGC_CMD_PENDING on line #%u, script terminated\n", iLine);
1653 break;
1654 }
1655 }
1656
1657 fclose(pFile);
1658 return rc;
1659}
1660
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