VirtualBox

source: vbox/trunk/src/bldprogs/VBoxCPP.cpp@ 107596

Last change on this file since 107596 was 107587, checked in by vboxsync, 3 weeks ago

src/bldprogs/VBoxCPP.cpp: Fixed warnings found by Parfait (assignment unused). jiraref:VBP-1424

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 194.6 KB
Line 
1/* $Id: VBoxCPP.cpp 107587 2025-01-09 10:57:25Z vboxsync $ */
2/** @file
3 * VBox Build Tool - A mini C Preprocessor.
4 *
5 * Purposes to which this preprocessor will be put:
6 * - Preprocessig vm.h into dtrace/lib/vm.d so we can access the VM
7 * structure (as well as substructures) from DTrace without having
8 * to handcraft it all.
9 * - Removing \#ifdefs relating to a new feature that has become
10 * stable and no longer needs \#ifdef'ing.
11 * - Pretty printing preprocessor directives. This will be used by
12 * SCM.
13 */
14
15/*
16 * Copyright (C) 2012-2024 Oracle and/or its affiliates.
17 *
18 * This file is part of VirtualBox base platform packages, as
19 * available from https://www.virtualbox.org.
20 *
21 * This program is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU General Public License
23 * as published by the Free Software Foundation, in version 3 of the
24 * License.
25 *
26 * This program is distributed in the hope that it will be useful, but
27 * WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29 * General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, see <https://www.gnu.org/licenses>.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <VBox/VBoxTpG.h>
42
43#include <iprt/alloca.h>
44#include <iprt/assert.h>
45#include <iprt/asm.h>
46#include <iprt/ctype.h>
47#include <iprt/err.h>
48#include <iprt/file.h>
49#include <iprt/getopt.h>
50#include <iprt/initterm.h>
51#include <iprt/list.h>
52#include <iprt/mem.h>
53#include <iprt/message.h>
54#include <iprt/path.h>
55#include <iprt/stream.h>
56#include <iprt/string.h>
57
58#include "scmstream.h"
59
60
61/*********************************************************************************************************************************
62* Defined Constants And Macros *
63*********************************************************************************************************************************/
64/** The bitmap type. */
65#define VBCPP_BITMAP_TYPE uint64_t
66/** The bitmap size as a multiple of VBCPP_BITMAP_TYPE. */
67#define VBCPP_BITMAP_SIZE (128 / 64)
68/** Checks if a bit is set. */
69#define VBCPP_BITMAP_IS_SET(a_bm, a_ch) ASMBitTest(a_bm, (a_ch) & 0x7f)
70/** Sets a bit. */
71#define VBCPP_BITMAP_SET(a_bm, a_ch) ASMBitSet(a_bm, (a_ch) & 0x7f)
72/** Empties the bitmap. */
73#define VBCPP_BITMAP_EMPTY(a_bm) do { (a_bm)[0] = 0; (a_bm)[1] = 0; } while (0)
74/** Joins to bitmaps by OR'ing their values.. */
75#define VBCPP_BITMAP_OR(a_bm1, a_bm2) do { (a_bm1)[0] |= (a_bm2)[0]; (a_bm1)[1] |= (a_bm2)[1]; } while (0)
76
77
78/*********************************************************************************************************************************
79* Structures and Typedefs *
80*********************************************************************************************************************************/
81/** Pointer to the C preprocessor instance data. */
82typedef struct VBCPP *PVBCPP;
83
84
85/**
86 * Variable string buffer (very simple version of SCMSTREAM).
87 */
88typedef struct VBCPPSTRBUF
89{
90 /** The preprocessor instance (for error reporting). */
91 struct VBCPP *pThis;
92 /** The length of the string in the buffer. */
93 size_t cchBuf;
94 /** The string storage. */
95 char *pszBuf;
96 /** Allocated buffer space. */
97 size_t cbBufAllocated;
98} VBCPPSTRBUF;
99/** Pointer to a variable string buffer. */
100typedef VBCPPSTRBUF *PVBCPPSTRBUF;
101
102
103/**
104 * The preprocessor mode.
105 */
106typedef enum VBCPPMODE
107{
108 kVBCppMode_Invalid = 0,
109 kVBCppMode_Standard,
110 kVBCppMode_Selective,
111 kVBCppMode_SelectiveD,
112 kVBCppMode_End
113} VBCPPMODE;
114
115
116/**
117 * A macro (aka define).
118 */
119typedef struct VBCPPMACRO
120{
121 /** The string space core. */
122 RTSTRSPACECORE Core;
123#if 0
124 /** For linking macros that have the fExpanding flag set. */
125 struct VBCPPMACRO *pUpExpanding;
126#endif
127 /** Whether it's a function. */
128 bool fFunction;
129 /** Variable argument count. */
130 bool fVarArg;
131 /** Set if originating on the command line. */
132 bool fCmdLine;
133 /** Set if this macro is currently being expanded and should not be
134 * recursively applied. */
135 bool fExpanding;
136 /** The number of known arguments. */
137 uint32_t cArgs;
138 /** Pointer to a list of argument names. */
139 const char **papszArgs;
140 /** Lead character bitmap for the argument names. */
141 VBCPP_BITMAP_TYPE bmArgs[VBCPP_BITMAP_SIZE];
142 /** The value length. */
143 size_t cchValue;
144 /** The define value. (This is followed by the name and arguments.) */
145 char szValue[1];
146} VBCPPMACRO;
147/** Pointer to a macro. */
148typedef VBCPPMACRO *PVBCPPMACRO;
149
150
151/**
152 * Macro expansion data.
153 */
154typedef struct VBCPPMACROEXP
155{
156 /** The expansion buffer. */
157 VBCPPSTRBUF StrBuf;
158#if 0
159 /** List of expanding macros (Stack). */
160 PVBCPPMACRO pMacroStack;
161#endif
162 /** The input stream (in case we want to look for parameter lists). */
163 PSCMSTREAM pStrmInput;
164 /** Array of argument values. Used when expanding function style macros. */
165 char **papszArgs;
166 /** The number of argument values current in papszArgs. */
167 uint32_t cArgs;
168 /** The number of argument values papszArgs can currently hold */
169 uint32_t cArgsAlloced;
170} VBCPPMACROEXP;
171/** Pointer to macro expansion data. */
172typedef VBCPPMACROEXP *PVBCPPMACROEXP;
173
174
175/**
176 * The vbcppMacroExpandReScan mode of operation.
177 */
178typedef enum VBCPPMACRORESCANMODE
179{
180 /** Invalid mode. */
181 kMacroReScanMode_Invalid = 0,
182 /** Normal expansion mode. */
183 kMacroReScanMode_Normal,
184 /** Replaces known macros and heeds the 'defined' operator. */
185 kMacroReScanMode_Expression,
186 /** End of valid modes. */
187 kMacroReScanMode_End
188} VBCPPMACRORESCANMODE;
189
190
191/**
192 * Expression node type.
193 */
194typedef enum VBCPPEXPRKIND
195{
196 kVBCppExprKind_Invalid = 0,
197 kVBCppExprKind_Unary,
198 kVBCppExprKind_Binary,
199 kVBCppExprKind_Ternary,
200 kVBCppExprKind_SignedValue,
201 kVBCppExprKind_UnsignedValue,
202 kVBCppExprKind_UndefMacroCall,
203 kVBCppExprKind_End
204} VBCPPEXPRKIND;
205
206
207/** Macro used for the precedence field. */
208#define VBCPPOP_PRECEDENCE(a_iPrecedence) ((a_iPrecedence) << 8)
209/** Mask for getting the precedence field value. */
210#define VBCPPOP_PRECEDENCE_MASK 0xff00
211/** Operator associativity - Left to right. */
212#define VBCPPOP_L2R (1 << 16)
213/** Operator associativity - Right to left. */
214#define VBCPPOP_R2L (2 << 16)
215
216/**
217 * Unary operators.
218 */
219typedef enum VBCPPUNARYOP
220{
221 kVBCppUnaryOp_Invalid = 0,
222 kVBCppUnaryOp_Pluss = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) | 5,
223 kVBCppUnaryOp_Minus = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) | 6,
224 kVBCppUnaryOp_LogicalNot = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) | 7,
225 kVBCppUnaryOp_BitwiseNot = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) | 8,
226 kVBCppUnaryOp_Parenthesis = VBCPPOP_R2L | VBCPPOP_PRECEDENCE(15) | 9,
227 kVBCppUnaryOp_End
228} VBCPPUNARYOP;
229
230/**
231 * Binary operators.
232 */
233typedef enum VBCPPBINARYOP
234{
235 kVBCppBinary_Invalid = 0,
236 kVBCppBinary_Multiplication = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 5) | 2,
237 kVBCppBinary_Division = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 5) | 4,
238 kVBCppBinary_Modulo = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 5) | 5,
239 kVBCppBinary_Addition = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 6) | 6,
240 kVBCppBinary_Subtraction = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 6) | 7,
241 kVBCppBinary_LeftShift = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 7) | 8,
242 kVBCppBinary_RightShift = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 7) | 9,
243 kVBCppBinary_LessThan = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 10,
244 kVBCppBinary_LessThanOrEqual = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 11,
245 kVBCppBinary_GreaterThan = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 12,
246 kVBCppBinary_GreaterThanOrEqual = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 13,
247 kVBCppBinary_EqualTo = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 9) | 14,
248 kVBCppBinary_NotEqualTo = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 9) | 15,
249 kVBCppBinary_BitwiseAnd = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(10) | 16,
250 kVBCppBinary_BitwiseXor = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(11) | 17,
251 kVBCppBinary_BitwiseOr = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(12) | 18,
252 kVBCppBinary_LogicalAnd = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(13) | 19,
253 kVBCppBinary_LogicalOr = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(14) | 20,
254 kVBCppBinary_End
255} VBCPPBINARYOP;
256
257/** The precedence of the ternary operator (expr ? true : false). */
258#define VBCPPTERNAROP_PRECEDENCE VBCPPOP_PRECEDENCE(16)
259
260
261/** Pointer to an expression parsing node. */
262typedef struct VBCPPEXPR *PVBCPPEXPR;
263/**
264 * Expression parsing node.
265 */
266typedef struct VBCPPEXPR
267{
268 /** Parent expression. */
269 PVBCPPEXPR pParent;
270 /** Whether the expression is complete or not. */
271 bool fComplete;
272 /** The kind of expression. */
273 VBCPPEXPRKIND enmKind;
274 /** Kind specific content. */
275 union
276 {
277 /** kVBCppExprKind_Unary */
278 struct
279 {
280 VBCPPUNARYOP enmOperator;
281 PVBCPPEXPR pArg;
282 } Unary;
283
284 /** kVBCppExprKind_Binary */
285 struct
286 {
287 VBCPPBINARYOP enmOperator;
288 PVBCPPEXPR pLeft;
289 PVBCPPEXPR pRight;
290 } Binary;
291
292 /** kVBCppExprKind_Ternary */
293 struct
294 {
295 PVBCPPEXPR pExpr;
296 PVBCPPEXPR pTrue;
297 PVBCPPEXPR pFalse;
298 } Ternary;
299
300 /** kVBCppExprKind_SignedValue */
301 struct
302 {
303 int64_t s64;
304 } SignedValue;
305
306 /** kVBCppExprKind_UnsignedValue */
307 struct
308 {
309 uint64_t u64;
310 } UnsignedValue;
311
312 /** kVBCppExprKind_UndefMacroCall */
313 struct
314 {
315 char *pszName;
316 size_t cArgs;
317 PVBCPPEXPR *papArgs;
318 } UndefMacroCall;
319 } u;
320} VBCPPEXPR;
321
322
323/**
324 * Operator return statuses.
325 */
326typedef enum VBCPPEXPRRET
327{
328 kExprRet_Error = -1,
329 kExprRet_Ok = 0,
330 kExprRet_UnaryOperator,
331 kExprRet_Value,
332 kExprRet_EndOfExpr,
333 kExprRet_End
334} VBCPPEXPRRET;
335
336/**
337 * Expression parser context.
338 */
339typedef struct VBCPPEXPRPARSER
340{
341 /** The current expression posistion. */
342 const char *pszCur;
343 /** The root node. */
344 PVBCPPEXPR pRoot;
345 /** The current expression node. */
346 PVBCPPEXPR pCur;
347 /** Where to insert the next expression. */
348 PVBCPPEXPR *ppCur;
349 /** The expression. */
350 const char *pszExpr;
351 /** The number of undefined macros we've encountered while parsing. */
352 size_t cUndefined;
353 /** Pointer to the C preprocessor instance. */
354 PVBCPP pThis;
355} VBCPPEXPRPARSER;
356/** Pointer to an expression parser context. */
357typedef VBCPPEXPRPARSER *PVBCPPEXPRPARSER;
358
359
360/**
361 * Evaluation result.
362 */
363typedef enum VBCPPEVAL
364{
365 kVBCppEval_Invalid = 0,
366 kVBCppEval_True,
367 kVBCppEval_False,
368 kVBCppEval_Undecided,
369 kVBCppEval_End
370} VBCPPEVAL;
371
372
373/**
374 * The condition kind.
375 */
376typedef enum VBCPPCONDKIND
377{
378 kVBCppCondKind_Invalid = 0,
379 /** \#if expr */
380 kVBCppCondKind_If,
381 /** \#ifdef define */
382 kVBCppCondKind_IfDef,
383 /** \#ifndef define */
384 kVBCppCondKind_IfNDef,
385 /** \#elif expr */
386 kVBCppCondKind_ElIf,
387 /** The end of valid values. */
388 kVBCppCondKind_End
389} VBCPPCONDKIND;
390
391
392/**
393 * Conditional stack entry.
394 */
395typedef struct VBCPPCOND
396{
397 /** The next conditional on the stack. */
398 struct VBCPPCOND *pUp;
399 /** The kind of conditional. This changes on encountering \#elif. */
400 VBCPPCONDKIND enmKind;
401 /** Evaluation result. */
402 VBCPPEVAL enmResult;
403 /** The evaluation result of the whole stack. */
404 VBCPPEVAL enmStackResult;
405
406 /** Whether we've seen the last else. */
407 bool fSeenElse;
408 /** Set if we have an else if which has already been decided. */
409 bool fElIfDecided;
410 /** The nesting level of this condition. */
411 uint16_t iLevel;
412 /** The nesting level of this condition wrt the ones we keep. */
413 uint16_t iKeepLevel;
414
415 /** The condition string. (Points within the stream buffer.) */
416 const char *pchCond;
417 /** The condition length. */
418 size_t cchCond;
419} VBCPPCOND;
420/** Pointer to a conditional stack entry. */
421typedef VBCPPCOND *PVBCPPCOND;
422
423
424/**
425 * Input buffer stack entry.
426 */
427typedef struct VBCPPINPUT
428{
429 /** Pointer to the next input on the stack. */
430 struct VBCPPINPUT *pUp;
431 /** The input stream. */
432 SCMSTREAM StrmInput;
433 /** Pointer into szName to the part which was specified. */
434 const char *pszSpecified;
435 /** The input file name with include path. */
436 char szName[1];
437} VBCPPINPUT;
438/** Pointer to a input buffer stack entry */
439typedef VBCPPINPUT *PVBCPPINPUT;
440
441
442/**
443 * The action to take with \#include.
444 */
445typedef enum VBCPPINCLUDEACTION
446{
447 kVBCppIncludeAction_Invalid = 0,
448 kVBCppIncludeAction_Include,
449 kVBCppIncludeAction_PassThru,
450 kVBCppIncludeAction_Drop,
451 kVBCppIncludeAction_End
452} VBCPPINCLUDEACTION;
453
454
455/**
456 * C Preprocessor instance data.
457 */
458typedef struct VBCPP
459{
460 /** @name Options
461 * @{ */
462 /** The preprocessing mode. */
463 VBCPPMODE enmMode;
464 /** Whether to keep comments. */
465 bool fKeepComments;
466 /** Whether to respect source defines. */
467 bool fRespectSourceDefines;
468 /** Whether to let source defines overrides the ones on the command
469 * line. */
470 bool fAllowRedefiningCmdLineDefines;
471 /** Whether to pass thru defines. */
472 bool fPassThruDefines;
473 /** Whether to allow undecided conditionals. */
474 bool fUndecidedConditionals;
475 /** Whether to pass thru D pragmas. */
476 bool fPassThruPragmaD;
477 /** Whether to pass thru STD pragmas. */
478 bool fPassThruPragmaSTD;
479 /** Whether to pass thru other pragmas. */
480 bool fPassThruPragmaOther;
481 /** Whether to pass thru \#error directives or execute them. */
482 bool fPassThruError;
483 /** Whether to remove dropped lines from the output. */
484 bool fRemoveDroppedLines;
485 /** Whether to preforme line splicing.
486 * @todo implement line splicing */
487 bool fLineSplicing;
488 /** What to do about include files. */
489 VBCPPINCLUDEACTION enmIncludeAction;
490
491 /** The number of include directories. */
492 uint32_t cIncludes;
493 /** Array of directories to search for include files. */
494 char **papszIncludes;
495
496 /** The name of the input file. */
497 const char *pszInput;
498 /** The name of the output file. NULL if stdout. */
499 const char *pszOutput;
500 /** @} */
501
502 /** The define string space. */
503 RTSTRSPACE StrSpace;
504 /** The string space holding explicitly undefined macros for selective
505 * preprocessing runs. */
506 RTSTRSPACE UndefStrSpace;
507 /** Indicates whether a C-word might need expansion.
508 * The bitmap is indexed by C-word lead character. Bits that are set
509 * indicates that the lead character is used in a \#define that we know and
510 * should expand. */
511 VBCPP_BITMAP_TYPE bmDefined[VBCPP_BITMAP_SIZE];
512
513 /** The current depth of the conditional stack. */
514 uint32_t cCondStackDepth;
515 /** Conditional stack. */
516 PVBCPPCOND pCondStack;
517 /** The current condition evaluates to kVBCppEval_False, don't output. */
518 bool fIf0Mode;
519 /** Just dropped a line and should maybe drop the current line. */
520 bool fJustDroppedLine;
521
522 /** Whether the current line could be a preprocessor line.
523 * This is set when EOL is encountered and cleared again when a
524 * non-comment-or-space character is encountered. See vbcppPreprocess. */
525 bool fMaybePreprocessorLine;
526
527 /** The input stack depth */
528 uint32_t cInputStackDepth;
529 /** The input buffer stack. */
530 PVBCPPINPUT pInputStack;
531
532 /** The output stream. */
533 SCMSTREAM StrmOutput;
534
535 /** The status of the whole job, as far as we know. */
536 RTEXITCODE rcExit;
537 /** Whether StrmOutput is valid (for vbcppTerm). */
538 bool fStrmOutputValid;
539} VBCPP;
540
541
542/*********************************************************************************************************************************
543* Internal Functions *
544*********************************************************************************************************************************/
545static VBCPPEXPRRET vbcppExprParseUnaryOrValue(PVBCPPEXPRPARSER pParser);
546static PVBCPPMACRO vbcppMacroLookup(PVBCPP pThis, const char *pszDefine, size_t cchDefine);
547static RTEXITCODE vbcppMacroExpandIt(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offMacro, PVBCPPMACRO pMacro, size_t offParameters);
548static RTEXITCODE vbcppMacroExpandReScan(PVBCPP pThis, PVBCPPMACROEXP pExp, VBCPPMACRORESCANMODE enmMode,
549 size_t *pcReplacements, size_t *pcDefinedUnknown);
550static void vbcppMacroExpandCleanup(PVBCPPMACROEXP pExp);
551
552
553
554/*
555 *
556 *
557 * Message Handling.
558 * Message Handling.
559 * Message Handling.
560 * Message Handling.
561 * Message Handling.
562 *
563 *
564 */
565
566
567/**
568 * Displays an error message.
569 *
570 * @returns RTEXITCODE_FAILURE
571 * @param pThis The C preprocessor instance.
572 * @param pszMsg The message.
573 * @param va Message arguments.
574 */
575static RTEXITCODE vbcppErrorV(PVBCPP pThis, const char *pszMsg, va_list va)
576{
577 NOREF(pThis);
578 if (pThis->pInputStack)
579 {
580 PSCMSTREAM pStrm = &pThis->pInputStack->StrmInput;
581
582 size_t const off = ScmStreamTell(pStrm);
583 size_t const iLine = ScmStreamTellLine(pStrm);
584 ScmStreamSeekByLine(pStrm, iLine);
585 size_t const offLine = ScmStreamTell(pStrm);
586
587 RTPrintf("%s:%d:%zd: error: %N.\n", pThis->pInputStack->szName, iLine + 1, off - offLine + 1, pszMsg, va);
588
589 size_t cchLine;
590 SCMEOL enmEof;
591 const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
592 if (pszLine)
593 RTPrintf(" %.*s\n"
594 " %*s^\n",
595 cchLine, pszLine, off - offLine, "");
596
597 ScmStreamSeekAbsolute(pStrm, off);
598 }
599 else
600 RTMsgErrorV(pszMsg, va);
601 return pThis->rcExit = RTEXITCODE_FAILURE;
602}
603
604
605/**
606 * Displays an error message.
607 *
608 * @returns RTEXITCODE_FAILURE
609 * @param pThis The C preprocessor instance.
610 * @param pszMsg The message.
611 * @param ... Message arguments.
612 */
613static RTEXITCODE vbcppError(PVBCPP pThis, const char *pszMsg, ...)
614{
615 va_list va;
616 va_start(va, pszMsg);
617 RTEXITCODE rcExit = vbcppErrorV(pThis, pszMsg, va);
618 va_end(va);
619 return rcExit;
620}
621
622
623/**
624 * Displays an error message.
625 *
626 * @returns RTEXITCODE_FAILURE
627 * @param pThis The C preprocessor instance.
628 * @param pszPos Pointer to the offending character.
629 * @param pszMsg The message.
630 * @param ... Message arguments.
631 */
632static RTEXITCODE vbcppErrorPos(PVBCPP pThis, const char *pszPos, const char *pszMsg, ...)
633{
634 NOREF(pszPos); NOREF(pThis);
635 va_list va;
636 va_start(va, pszMsg);
637 //RTMsgErrorV(pszMsg, va);
638 RTEXITCODE rcExit = vbcppErrorV(pThis, pszMsg, va);
639 va_end(va);
640 return rcExit;
641}
642
643
644
645
646
647
648
649/*
650 *
651 *
652 * Variable String Buffers.
653 * Variable String Buffers.
654 * Variable String Buffers.
655 * Variable String Buffers.
656 * Variable String Buffers.
657 *
658 *
659 */
660
661
662/**
663 * Initializes a string buffer.
664 *
665 * @param pStrBuf The buffer structure to initialize.
666 * @param pThis The C preprocessor instance.
667 */
668static void vbcppStrBufInit(PVBCPPSTRBUF pStrBuf, PVBCPP pThis)
669{
670 pStrBuf->pThis = pThis;
671 pStrBuf->cchBuf = 0;
672 pStrBuf->cbBufAllocated = 0;
673 pStrBuf->pszBuf = NULL;
674}
675
676
677/**
678 * Deletes a string buffer.
679 *
680 * @param pStrBuf Pointer to the string buffer.
681 */
682static void vbcppStrBufDelete(PVBCPPSTRBUF pStrBuf)
683{
684 RTMemFree(pStrBuf->pszBuf);
685 pStrBuf->pszBuf = NULL;
686}
687
688
689/**
690 * Ensures that sufficient bufferspace is available, growing the buffer if
691 * necessary.
692 *
693 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
694 * @param pStrBuf Pointer to the string buffer.
695 * @param cbMin The minimum buffer size.
696 */
697static RTEXITCODE vbcppStrBufGrow(PVBCPPSTRBUF pStrBuf, size_t cbMin)
698{
699 if (pStrBuf->cbBufAllocated >= cbMin)
700 return RTEXITCODE_SUCCESS;
701
702 size_t cbNew = pStrBuf->cbBufAllocated * 2;
703 if (cbNew < cbMin)
704 cbNew = RT_ALIGN_Z(cbMin, _1K);
705 void *pv = RTMemRealloc(pStrBuf->pszBuf, cbNew);
706 if (!pv)
707 return vbcppError(pStrBuf->pThis, "out of memory (%zu bytes)", cbNew);
708
709 pStrBuf->pszBuf = (char *)pv;
710 pStrBuf->cbBufAllocated = cbNew;
711 return RTEXITCODE_SUCCESS;
712}
713
714
715/**
716 * Appends a substring.
717 *
718 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
719 * @param pStrBuf Pointer to the string buffer.
720 * @param pchSrc Pointer to the first character in the substring.
721 * @param cchSrc The length of the substring.
722 */
723static RTEXITCODE vbcppStrBufAppendN(PVBCPPSTRBUF pStrBuf, const char *pchSrc, size_t cchSrc)
724{
725 size_t cchBuf = pStrBuf->cchBuf;
726 if (cchBuf + cchSrc + 1 > pStrBuf->cbBufAllocated)
727 {
728 RTEXITCODE rcExit = vbcppStrBufGrow(pStrBuf, cchBuf + cchSrc + 1);
729 if (rcExit != RTEXITCODE_SUCCESS)
730 return rcExit;
731 }
732
733 memcpy(&pStrBuf->pszBuf[cchBuf], pchSrc, cchSrc);
734 cchBuf += cchSrc;
735 pStrBuf->pszBuf[cchBuf] = '\0';
736 pStrBuf->cchBuf = cchBuf;
737
738 return RTEXITCODE_SUCCESS;
739}
740
741
742/**
743 * Appends a character.
744 *
745 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
746 * @param pStrBuf Pointer to the string buffer.
747 * @param ch The charater to append.
748 */
749static RTEXITCODE vbcppStrBufAppendCh(PVBCPPSTRBUF pStrBuf, char ch)
750{
751 size_t cchBuf = pStrBuf->cchBuf;
752 if (cchBuf + 2 > pStrBuf->cbBufAllocated)
753 {
754 RTEXITCODE rcExit = vbcppStrBufGrow(pStrBuf, cchBuf + 2);
755 if (rcExit != RTEXITCODE_SUCCESS)
756 return rcExit;
757 }
758
759 pStrBuf->pszBuf[cchBuf++] = ch;
760 pStrBuf->pszBuf[cchBuf] = '\0';
761 pStrBuf->cchBuf = cchBuf;
762
763 return RTEXITCODE_SUCCESS;
764}
765
766
767/**
768 * Appends a string to the buffer.
769 *
770 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
771 * @param pStrBuf Pointer to the string buffer.
772 * @param psz The string to append.
773 */
774static RTEXITCODE vbcppStrBufAppend(PVBCPPSTRBUF pStrBuf, const char *psz)
775{
776 return vbcppStrBufAppendN(pStrBuf, psz, strlen(psz));
777}
778
779
780/**
781 * Gets the last char in the buffer.
782 *
783 * @returns Last character, 0 if empty.
784 * @param pStrBuf Pointer to the string buffer.
785 */
786static char vbcppStrBufLastCh(PVBCPPSTRBUF pStrBuf)
787{
788 if (!pStrBuf->cchBuf)
789 return '\0';
790 return pStrBuf->pszBuf[pStrBuf->cchBuf - 1];
791}
792
793
794
795
796
797
798
799/*
800 *
801 *
802 * C Identifier/Word Parsing.
803 * C Identifier/Word Parsing.
804 * C Identifier/Word Parsing.
805 * C Identifier/Word Parsing.
806 * C Identifier/Word Parsing.
807 *
808 *
809 */
810
811
812/**
813 * Checks if the given character is a valid C identifier lead character.
814 *
815 * @returns true / false.
816 * @param ch The character to inspect.
817 */
818DECLINLINE(bool) vbcppIsCIdentifierLeadChar(char ch)
819{
820 return RT_C_IS_ALPHA(ch)
821 || ch == '_';
822}
823
824
825/**
826 * Checks if the given character is a valid C identifier character.
827 *
828 * @returns true / false.
829 * @param ch The character to inspect.
830 */
831DECLINLINE(bool) vbcppIsCIdentifierChar(char ch)
832{
833 return RT_C_IS_ALNUM(ch)
834 || ch == '_';
835}
836
837
838
839/**
840 *
841 * @returns @c true if valid, @c false if not. Error message already displayed
842 * on failure.
843 * @param pThis The C preprocessor instance.
844 * @param pchIdentifier The start of the identifier to validate.
845 * @param cchIdentifier The length of the identifier. RTSTR_MAX if not
846 * known.
847 */
848static bool vbcppValidateCIdentifier(PVBCPP pThis, const char *pchIdentifier, size_t cchIdentifier)
849{
850 if (cchIdentifier == RTSTR_MAX)
851 cchIdentifier = strlen(pchIdentifier);
852
853 if (cchIdentifier == 0)
854 {
855 vbcppErrorPos(pThis, pchIdentifier, "Zero length identifier");
856 return false;
857 }
858
859 if (!vbcppIsCIdentifierLeadChar(*pchIdentifier))
860 {
861 vbcppErrorPos(pThis, pchIdentifier, "Bad lead chararacter in identifier: '%.*s'", cchIdentifier, pchIdentifier);
862 return false;
863 }
864
865 for (size_t off = 1; off < cchIdentifier; off++)
866 {
867 if (!vbcppIsCIdentifierChar(pchIdentifier[off]))
868 {
869 vbcppErrorPos(pThis, pchIdentifier + off, "Illegal chararacter in identifier: '%.*s' (#%zu)", cchIdentifier, pchIdentifier, off + 1);
870 return false;
871 }
872 }
873
874 return true;
875}
876
877#if 0
878
879/**
880 * Checks if the given character is valid C punctuation.
881 *
882 * @returns true / false.
883 * @param ch The character to inspect.
884 */
885DECLINLINE(bool) vbcppIsCPunctuationLeadChar(char ch)
886{
887 switch (ch)
888 {
889 case '!':
890 case '#':
891 case '%':
892 case '&':
893 case '(':
894 case ')':
895 case '*':
896 case '+':
897 case ',':
898 case '-':
899 case '.':
900 case '/':
901 case ':':
902 case ';':
903 case '<':
904 case '=':
905 case '>':
906 case '?':
907 case '[':
908 case ']':
909 case '^':
910 case '{':
911 case '|':
912 case '}':
913 case '~':
914 return true;
915 default:
916 return false;
917 }
918}
919
920
921/**
922 * Checks if the given string start with valid C punctuation.
923 *
924 * @returns 0 if not, otherwise the length of the punctuation.
925 * @param pch The which start we should evaluate.
926 * @param cchMax The maximum string length.
927 */
928static size_t vbcppIsCPunctuationLeadChar(const char *psz, size_t cchMax)
929{
930 if (!cchMax)
931 return 0;
932
933 switch (psz[0])
934 {
935 case '!':
936 case '*':
937 case '/':
938 case '=':
939 case '^':
940 if (cchMax >= 2 && psz[1] == '=')
941 return 2;
942 return 1;
943
944 case '#':
945 if (cchMax >= 2 && psz[1] == '#')
946 return 2;
947 return 1;
948
949 case '%':
950 if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '>'))
951 return 2;
952 if (cchMax >= 2 && psz[1] == ':')
953 {
954 if (cchMax >= 4 && psz[2] == '%' && psz[3] == ':')
955 return 4;
956 return 2;
957 }
958 return 1;
959
960 case '&':
961 if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '&'))
962 return 2;
963 return 1;
964
965 case '(':
966 case ')':
967 case ',':
968 case '?':
969 case '[':
970 case ']':
971 case '{':
972 case '}':
973 return 1;
974
975 case '+':
976 if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '+'))
977 return 2;
978 return 1;
979
980 case '-':
981 if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '-' || psz[1] == '>'))
982 return 2;
983 return 1;
984
985 case ':':
986 if (cchMax >= 2 && psz[1] == '>')
987 return 2;
988 return 1;
989
990 case ';':
991 return 1;
992
993 case '<':
994 if (cchMax >= 2 && psz[1] == '<')
995 {
996 if (cchMax >= 3 && psz[2] == '=')
997 return 3;
998 return 2;
999 }
1000 if (cchMax >= 2 && (psz[1] == '=' || psz[1] == ':' || psz[1] == '%'))
1001 return 2;
1002 return 1;
1003
1004 case '.':
1005 if (cchMax >= 3 && psz[1] == '.' && psz[2] == '.')
1006 return 3;
1007 return 1;
1008
1009 case '>':
1010 if (cchMax >= 2 && psz[1] == '>')
1011 {
1012 if (cchMax >= 3 && psz[2] == '=')
1013 return 3;
1014 return 2;
1015 }
1016 if (cchMax >= 2 && psz[1] == '=')
1017 return 2;
1018 return 1;
1019
1020 case '|':
1021 if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '|'))
1022 return 2;
1023 return 1;
1024
1025 case '~':
1026 return 1;
1027
1028 default:
1029 return 0;
1030 }
1031}
1032
1033#endif
1034
1035
1036
1037
1038
1039/*
1040 *
1041 *
1042 * Output
1043 * Output
1044 * Output
1045 * Output
1046 * Output
1047 *
1048 *
1049 */
1050
1051
1052/**
1053 * Outputs a character.
1054 *
1055 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1056 * @param pThis The C preprocessor instance.
1057 * @param ch The character to output.
1058 */
1059static RTEXITCODE vbcppOutputCh(PVBCPP pThis, char ch)
1060{
1061 int rc = ScmStreamPutCh(&pThis->StrmOutput, ch);
1062 if (RT_SUCCESS(rc))
1063 return RTEXITCODE_SUCCESS;
1064 return vbcppError(pThis, "Output error: %Rrc", rc);
1065}
1066
1067
1068/**
1069 * Outputs a string.
1070 *
1071 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1072 * @param pThis The C preprocessor instance.
1073 * @param pch The string.
1074 * @param cch The number of characters to write.
1075 */
1076static RTEXITCODE vbcppOutputWrite(PVBCPP pThis, const char *pch, size_t cch)
1077{
1078 int rc = ScmStreamWrite(&pThis->StrmOutput, pch, cch);
1079 if (RT_SUCCESS(rc))
1080 return RTEXITCODE_SUCCESS;
1081 return vbcppError(pThis, "Output error: %Rrc", rc);
1082}
1083
1084
1085static RTEXITCODE vbcppOutputComment(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart, size_t cchOutputted,
1086 size_t cchMinIndent)
1087{
1088 RT_NOREF_PV(cchMinIndent); /** @todo cchMinIndent */
1089
1090 size_t offCur = ScmStreamTell(pStrmInput);
1091 if (offStart < offCur)
1092 {
1093 int rc = ScmStreamSeekAbsolute(pStrmInput, offStart);
1094 AssertRCReturn(rc, vbcppError(pThis, "Input seek error: %Rrc", rc));
1095
1096 /*
1097 * Use the same indent, if possible.
1098 */
1099 size_t cchIndent = offStart - ScmStreamTellOffsetOfLine(pStrmInput, ScmStreamTellLine(pStrmInput));
1100 if (cchOutputted < cchIndent)
1101 rc = ScmStreamPrintf(&pThis->StrmOutput, "%*s", cchIndent - cchOutputted, "");
1102 else
1103 rc = ScmStreamPutCh(&pThis->StrmOutput, ' ');
1104 if (RT_FAILURE(rc))
1105 return vbcppError(pThis, "Output error: %Rrc", rc);
1106
1107 /*
1108 * Copy the bytes.
1109 */
1110 while (ScmStreamTell(pStrmInput) < offCur)
1111 {
1112 unsigned ch = ScmStreamGetCh(pStrmInput);
1113 if (ch == ~(unsigned)0)
1114 return vbcppError(pThis, "Input error: %Rrc", rc);
1115 rc = ScmStreamPutCh(&pThis->StrmOutput, ch);
1116 if (RT_FAILURE(rc))
1117 return vbcppError(pThis, "Output error: %Rrc", rc);
1118 }
1119 }
1120
1121 return RTEXITCODE_SUCCESS;
1122}
1123
1124
1125
1126
1127
1128/*
1129 *
1130 *
1131 * Input
1132 * Input
1133 * Input
1134 * Input
1135 * Input
1136 *
1137 *
1138 */
1139
1140
1141#if 0 /* unused */
1142/**
1143 * Skips white spaces, including escaped new-lines.
1144 *
1145 * @param pStrmInput The input stream.
1146 */
1147static void vbcppProcessSkipWhiteAndEscapedEol(PSCMSTREAM pStrmInput)
1148{
1149 unsigned chPrev = ~(unsigned)0;
1150 unsigned ch;
1151 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
1152 {
1153 if (ch == '\r' || ch == '\n')
1154 {
1155 if (chPrev != '\\')
1156 break;
1157 chPrev = ch;
1158 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
1159 }
1160 else if (RT_C_IS_SPACE(ch))
1161 {
1162 chPrev = ch;
1163 ch = ScmStreamGetCh(pStrmInput);
1164 Assert(ch == chPrev);
1165 }
1166 else
1167 break;
1168 }
1169}
1170#endif
1171
1172
1173/**
1174 * Skips white spaces, escaped new-lines and multi line comments.
1175 *
1176 * @param pThis The C preprocessor instance.
1177 * @param pStrmInput The input stream.
1178 */
1179static RTEXITCODE vbcppProcessSkipWhiteEscapedEolAndComments(PVBCPP pThis, PSCMSTREAM pStrmInput)
1180{
1181 unsigned chPrev = ~(unsigned)0;
1182 unsigned ch;
1183 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
1184 {
1185 if (!RT_C_IS_SPACE(ch))
1186 {
1187 /* Multi-line Comment? */
1188 if (ch != '/')
1189 break; /* most definitely, not. */
1190
1191 size_t offSaved = ScmStreamTell(pStrmInput);
1192 ScmStreamGetCh(pStrmInput);
1193 if (ScmStreamPeekCh(pStrmInput) != '*')
1194 {
1195 ScmStreamSeekAbsolute(pStrmInput, offSaved);
1196 break; /* no */
1197 }
1198
1199 /* Skip to the end of the comment. */
1200 while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
1201 {
1202 if (ch == '*')
1203 {
1204 ch = ScmStreamGetCh(pStrmInput);
1205 if (ch == '/')
1206 break;
1207 if (ch == ~(unsigned)0)
1208 break;
1209 }
1210 }
1211 if (ch == ~(unsigned)0)
1212 return vbcppError(pThis, "unterminated multi-line comment");
1213 chPrev = '/';
1214 }
1215 /* New line (also matched by RT_C_IS_SPACE). */
1216 else if (ch == '\r' || ch == '\n')
1217 {
1218 /* Stop if not escaped. */
1219 if (chPrev != '\\')
1220 break;
1221 chPrev = ch;
1222 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
1223 }
1224 /* Real space char. */
1225 else
1226 {
1227 chPrev = ch;
1228 ch = ScmStreamGetCh(pStrmInput);
1229 Assert(ch == chPrev);
1230 }
1231 }
1232 return RTEXITCODE_SUCCESS;
1233}
1234
1235
1236/**
1237 * Skips white spaces, escaped new-lines, and multi line comments, then checking
1238 * that we're at the end of a line.
1239 *
1240 * @param pThis The C preprocessor instance.
1241 * @param pStrmInput The input stream.
1242 */
1243static RTEXITCODE vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(PVBCPP pThis, PSCMSTREAM pStrmInput)
1244{
1245 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1246 if (rcExit == RTEXITCODE_SUCCESS)
1247 {
1248 unsigned ch = ScmStreamPeekCh(pStrmInput);
1249 if ( ch != ~(unsigned)0
1250 && ch != '\r'
1251 && ch != '\n')
1252 rcExit = vbcppError(pThis, "Did not expected anything more on this line");
1253 }
1254 return rcExit;
1255}
1256
1257
1258/**
1259 * Skips white spaces.
1260 *
1261 * @returns The current location upon return.
1262 * @param pStrmInput The input stream.
1263 */
1264static size_t vbcppProcessSkipWhite(PSCMSTREAM pStrmInput)
1265{
1266 unsigned ch;
1267 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
1268 {
1269 if (!RT_C_IS_SPACE(ch) || ch == '\r' || ch == '\n')
1270 break;
1271 unsigned chCheck = ScmStreamGetCh(pStrmInput);
1272 AssertBreak(chCheck == ch);
1273 }
1274 return ScmStreamTell(pStrmInput);
1275}
1276
1277
1278/**
1279 * Looks for a left parenthesis in the input stream.
1280 *
1281 * Used during macro expansion. Will ignore comments, newlines and other
1282 * whitespace.
1283 *
1284 * @retval true if found. The stream position at opening parenthesis.
1285 * @retval false if not found. The stream position is unchanged.
1286 *
1287 * @param pThis The C preprocessor instance.
1288 * @param pStrmInput The input stream.
1289 */
1290static bool vbcppInputLookForLeftParenthesis(PVBCPP pThis, PSCMSTREAM pStrmInput)
1291{
1292 size_t offSaved = ScmStreamTell(pStrmInput);
1293 /*RTEXITCODE rcExit =*/ vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1294 unsigned ch = ScmStreamPeekCh(pStrmInput);
1295 if (ch == '(')
1296 return true;
1297
1298 int rc = ScmStreamSeekAbsolute(pStrmInput, offSaved);
1299 AssertFatalRC(rc);
1300 return false;
1301}
1302
1303
1304/**
1305 * Skips input until the real end of the current directive line has been
1306 * reached.
1307 *
1308 * This includes multiline comments starting on the same line
1309 *
1310 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1311 * @param pThis The C preprocessor instance.
1312 * @param pStrmInput The input stream.
1313 * @param poffComment Where to note down the position of the final
1314 * comment. Optional.
1315 */
1316static RTEXITCODE vbcppInputSkipToEndOfDirectiveLine(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t *poffComment)
1317{
1318 if (poffComment)
1319 *poffComment = ~(size_t)0;
1320
1321 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1322 bool fInComment = false;
1323 unsigned chPrev = 0;
1324 unsigned ch;
1325 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
1326 {
1327 if (ch == '\r' || ch == '\n')
1328 {
1329 if (chPrev == '\\')
1330 {
1331 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
1332 continue;
1333 }
1334 if (!fInComment)
1335 break;
1336 /* The expression continues after multi-line comments. Cool. :-) */
1337 }
1338 else if (!fInComment)
1339 {
1340 if (chPrev == '/' && ch == '*' )
1341 {
1342 fInComment = true;
1343 if (poffComment)
1344 *poffComment = ScmStreamTell(pStrmInput) - 1;
1345 }
1346 else if (chPrev == '/' && ch == '/')
1347 {
1348 if (poffComment)
1349 *poffComment = ScmStreamTell(pStrmInput) - 1;
1350 rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1351 break; /* done */
1352 }
1353 }
1354 else if (ch == '/' && chPrev == '*')
1355 fInComment = false;
1356
1357 /* advance */
1358 chPrev = ch;
1359 ch = ScmStreamGetCh(pStrmInput); Assert(ch == chPrev);
1360 }
1361 return rcExit;
1362}
1363
1364
1365/**
1366 * Processes a multi-line comment.
1367 *
1368 * Must either string the comment or keep it. If the latter, we must refrain
1369 * from replacing C-words in it.
1370 *
1371 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1372 * @param pThis The C preprocessor instance.
1373 * @param pStrmInput The input stream.
1374 */
1375static RTEXITCODE vbcppProcessMultiLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
1376{
1377 /* The open comment sequence. */
1378 ScmStreamGetCh(pStrmInput); /* '*' */
1379 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1380 if ( pThis->fKeepComments
1381 && !pThis->fIf0Mode)
1382 rcExit = vbcppOutputWrite(pThis, "/*", 2);
1383
1384 /* The comment.*/
1385 unsigned ch;
1386 while ( rcExit == RTEXITCODE_SUCCESS
1387 && (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0 )
1388 {
1389 if (ch == '*')
1390 {
1391 /* Closing sequence? */
1392 unsigned ch2 = ScmStreamPeekCh(pStrmInput);
1393 if (ch2 == '/')
1394 {
1395 ScmStreamGetCh(pStrmInput);
1396 if ( pThis->fKeepComments
1397 && !pThis->fIf0Mode)
1398 rcExit = vbcppOutputWrite(pThis, "*/", 2);
1399 break;
1400 }
1401 }
1402
1403 if (ch == '\r' || ch == '\n')
1404 {
1405 if ( ( pThis->fKeepComments
1406 && !pThis->fIf0Mode)
1407 || !pThis->fRemoveDroppedLines
1408 || !ScmStreamIsAtStartOfLine(&pThis->StrmOutput))
1409 rcExit = vbcppOutputCh(pThis, ch);
1410 pThis->fJustDroppedLine = false;
1411 pThis->fMaybePreprocessorLine = true;
1412 }
1413 else if ( pThis->fKeepComments
1414 && !pThis->fIf0Mode)
1415 rcExit = vbcppOutputCh(pThis, ch);
1416
1417 if (rcExit != RTEXITCODE_SUCCESS)
1418 break;
1419 }
1420 return rcExit;
1421}
1422
1423
1424/**
1425 * Processes a single line comment.
1426 *
1427 * Must either string the comment or keep it. If the latter, we must refrain
1428 * from replacing C-words in it.
1429 *
1430 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1431 * @param pThis The C preprocessor instance.
1432 * @param pStrmInput The input stream.
1433 */
1434static RTEXITCODE vbcppProcessOneLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
1435{
1436 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1437 SCMEOL enmEol;
1438 size_t cchLine;
1439 const char *pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol); Assert(pszLine);
1440 pszLine--; cchLine++; /* unfetching the first slash. */
1441 for (;;)
1442 {
1443 if ( pThis->fKeepComments
1444 && !pThis->fIf0Mode)
1445 rcExit = vbcppOutputWrite(pThis, pszLine, cchLine + enmEol);
1446 else if ( !pThis->fIf0Mode
1447 || !pThis->fRemoveDroppedLines
1448 || !ScmStreamIsAtStartOfLine(&pThis->StrmOutput) )
1449 rcExit = vbcppOutputWrite(pThis, pszLine + cchLine, enmEol);
1450 if (rcExit != RTEXITCODE_SUCCESS)
1451 break;
1452 if ( cchLine == 0
1453 || pszLine[cchLine - 1] != '\\')
1454 break;
1455
1456 pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol);
1457 if (!pszLine)
1458 break;
1459 }
1460 pThis->fJustDroppedLine = false;
1461 pThis->fMaybePreprocessorLine = true;
1462 return rcExit;
1463}
1464
1465
1466/**
1467 * Processes a double quoted string.
1468 *
1469 * Must not replace any C-words in strings.
1470 *
1471 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1472 * @param pThis The C preprocessor instance.
1473 * @param pStrmInput The input stream.
1474 */
1475static RTEXITCODE vbcppProcessStringLitteral(PVBCPP pThis, PSCMSTREAM pStrmInput)
1476{
1477 RTEXITCODE rcExit = vbcppOutputCh(pThis, '"');
1478 if (rcExit == RTEXITCODE_SUCCESS)
1479 {
1480 bool fEscaped = false;
1481 for (;;)
1482 {
1483 unsigned ch = ScmStreamGetCh(pStrmInput);
1484 if (ch == ~(unsigned)0)
1485 {
1486 rcExit = vbcppError(pThis, "Unterminated double quoted string");
1487 break;
1488 }
1489
1490 rcExit = vbcppOutputCh(pThis, ch);
1491 if (rcExit != RTEXITCODE_SUCCESS)
1492 break;
1493
1494 if (ch == '"' && !fEscaped)
1495 break;
1496 fEscaped = !fEscaped && ch == '\\';
1497 }
1498 }
1499 return rcExit;
1500}
1501
1502
1503/**
1504 * Processes a single quoted constant.
1505 *
1506 * Must not replace any C-words in character constants.
1507 *
1508 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1509 * @param pThis The C preprocessor instance.
1510 * @param pStrmInput The input stream.
1511 */
1512static RTEXITCODE vbcppProcessCharacterConstant(PVBCPP pThis, PSCMSTREAM pStrmInput)
1513{
1514 RTEXITCODE rcExit = vbcppOutputCh(pThis, '\'');
1515 if (rcExit == RTEXITCODE_SUCCESS)
1516 {
1517 bool fEscaped = false;
1518 for (;;)
1519 {
1520 unsigned ch = ScmStreamGetCh(pStrmInput);
1521 if (ch == ~(unsigned)0)
1522 {
1523 rcExit = vbcppError(pThis, "Unterminated singled quoted string");
1524 break;
1525 }
1526
1527 rcExit = vbcppOutputCh(pThis, ch);
1528 if (rcExit != RTEXITCODE_SUCCESS)
1529 break;
1530
1531 if (ch == '\'' && !fEscaped)
1532 break;
1533 fEscaped = !fEscaped && ch == '\\';
1534 }
1535 }
1536 return rcExit;
1537}
1538
1539
1540/**
1541 * Processes a integer or floating point number constant.
1542 *
1543 * Must not replace the type suffix.
1544 *
1545 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1546 * @param pThis The C preprocessor instance.
1547 * @param pStrmInput The input stream.
1548 * @param chFirst The first character.
1549 */
1550static RTEXITCODE vbcppProcessNumber(PVBCPP pThis, PSCMSTREAM pStrmInput, char chFirst)
1551{
1552 RTEXITCODE rcExit = vbcppOutputCh(pThis, chFirst);
1553
1554 unsigned ch;
1555 while ( rcExit == RTEXITCODE_SUCCESS
1556 && (ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
1557 {
1558 if ( !vbcppIsCIdentifierChar(ch)
1559 && ch != '.')
1560 break;
1561
1562 unsigned ch2 = ScmStreamGetCh(pStrmInput);
1563 AssertBreakStmt(ch2 == ch, rcExit = vbcppError(pThis, "internal error"));
1564 rcExit = vbcppOutputCh(pThis, ch);
1565 }
1566
1567 return rcExit;
1568}
1569
1570
1571/**
1572 * Processes a identifier, possibly replacing it with a definition.
1573 *
1574 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1575 * @param pThis The C preprocessor instance.
1576 * @param pStrmInput The input stream.
1577 */
1578static RTEXITCODE vbcppProcessIdentifier(PVBCPP pThis, PSCMSTREAM pStrmInput)
1579{
1580 RTEXITCODE rcExit;
1581 size_t cchDefine;
1582 const char *pchDefine = ScmStreamCGetWordM1(pStrmInput, &cchDefine);
1583 AssertReturn(pchDefine, vbcppError(pThis, "Internal error in ScmStreamCGetWordM1"));
1584
1585 /*
1586 * Does this look like a define we know?
1587 */
1588 PVBCPPMACRO pMacro = vbcppMacroLookup(pThis, pchDefine, cchDefine);
1589 if ( pMacro
1590 && ( !pMacro->fFunction
1591 || vbcppInputLookForLeftParenthesis(pThis, pStrmInput)) )
1592 {
1593 /*
1594 * Expand it.
1595 */
1596 VBCPPMACROEXP ExpCtx;
1597#if 0
1598 ExpCtx.pMacroStack = NULL;
1599#endif
1600 ExpCtx.pStrmInput = pStrmInput;
1601 ExpCtx.papszArgs = NULL;
1602 ExpCtx.cArgs = 0;
1603 ExpCtx.cArgsAlloced = 0;
1604 vbcppStrBufInit(&ExpCtx.StrBuf, pThis);
1605 rcExit = vbcppStrBufAppendN(&ExpCtx.StrBuf, pchDefine, cchDefine);
1606 if (rcExit == RTEXITCODE_SUCCESS)
1607 rcExit = vbcppMacroExpandIt(pThis, &ExpCtx, 0 /* offset */, pMacro, cchDefine);
1608 if (rcExit == RTEXITCODE_SUCCESS)
1609 rcExit = vbcppMacroExpandReScan(pThis, &ExpCtx, kMacroReScanMode_Normal, NULL, NULL);
1610 if (rcExit == RTEXITCODE_SUCCESS)
1611 {
1612 /*
1613 * Insert it into the output stream. Make sure there is a
1614 * whitespace following it.
1615 */
1616 int rc = ScmStreamWrite(&pThis->StrmOutput, ExpCtx.StrBuf.pszBuf, ExpCtx.StrBuf.cchBuf);
1617 if (RT_SUCCESS(rc))
1618 {
1619 unsigned chAfter = ScmStreamPeekCh(pStrmInput);
1620 if (chAfter != ~(unsigned)0 && !RT_C_IS_SPACE(chAfter))
1621 rcExit = vbcppOutputCh(pThis, ' ');
1622 }
1623 else
1624 rcExit = vbcppError(pThis, "Output error: %Rrc", rc);
1625 }
1626 vbcppMacroExpandCleanup(&ExpCtx);
1627 }
1628 else
1629 {
1630 /*
1631 * Not a macro or a function-macro name match but no invocation, just
1632 * output the text unchanged.
1633 */
1634 int rc = ScmStreamWrite(&pThis->StrmOutput, pchDefine, cchDefine);
1635 if (RT_SUCCESS(rc))
1636 rcExit = RTEXITCODE_SUCCESS;
1637 else
1638 rcExit = vbcppError(pThis, "Output error: %Rrc", rc);
1639 }
1640 return rcExit;
1641}
1642
1643
1644
1645
1646
1647
1648
1649/*
1650 *
1651 *
1652 * D E F I N E S / M A C R O S
1653 * D E F I N E S / M A C R O S
1654 * D E F I N E S / M A C R O S
1655 * D E F I N E S / M A C R O S
1656 * D E F I N E S / M A C R O S
1657 *
1658 *
1659 */
1660
1661
1662/**
1663 * Checks if a define exists.
1664 *
1665 * @returns true or false.
1666 * @param pThis The C preprocessor instance.
1667 * @param pszDefine The define name and optionally the argument
1668 * list.
1669 * @param cchDefine The length of the name. RTSTR_MAX is ok.
1670 */
1671static bool vbcppMacroExists(PVBCPP pThis, const char *pszDefine, size_t cchDefine)
1672{
1673 return cchDefine > 0
1674 && VBCPP_BITMAP_IS_SET(pThis->bmDefined, *pszDefine)
1675 && RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine) != NULL;
1676}
1677
1678
1679/**
1680 * Looks up a define.
1681 *
1682 * @returns Pointer to the define if found, NULL if not.
1683 * @param pThis The C preprocessor instance.
1684 * @param pszDefine The define name and optionally the argument
1685 * list.
1686 * @param cchDefine The length of the name. RTSTR_MAX is ok.
1687 */
1688static PVBCPPMACRO vbcppMacroLookup(PVBCPP pThis, const char *pszDefine, size_t cchDefine)
1689{
1690 if (!cchDefine)
1691 return NULL;
1692 if (!VBCPP_BITMAP_IS_SET(pThis->bmDefined, *pszDefine))
1693 return NULL;
1694 return (PVBCPPMACRO)RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine);
1695}
1696
1697
1698static uint32_t vbcppMacroLookupArg(PVBCPPMACRO pMacro, const char *pchName, size_t cchName)
1699{
1700 Assert(cchName > 0);
1701
1702 char const ch = *pchName;
1703 for (uint32_t i = 0; i < pMacro->cArgs; i++)
1704 if ( pMacro->papszArgs[i][0] == ch
1705 && !strncmp(pMacro->papszArgs[i], pchName, cchName)
1706 && pMacro->papszArgs[i][cchName] == '\0')
1707 return i;
1708
1709 if ( pMacro->fVarArg
1710 && cchName == sizeof("__VA_ARGS__") - 1
1711 && !strncmp(pchName, "__VA_ARGS__", sizeof("__VA_ARGS__") - 1) )
1712 return pMacro->cArgs;
1713
1714 return UINT32_MAX;
1715}
1716
1717
1718static RTEXITCODE vbcppMacroExpandReplace(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t off, size_t cchToReplace,
1719 const char *pchReplacement, size_t cchReplacement)
1720{
1721 RT_NOREF_PV(pThis);
1722
1723 /*
1724 * Figure how much space we actually need.
1725 * (Hope this whitespace stuff is correct...)
1726 */
1727 bool const fLeadingSpace = off > 0
1728 && !RT_C_IS_SPACE(pExp->StrBuf.pszBuf[off - 1]);
1729 bool const fTrailingSpace = off + cchToReplace < pExp->StrBuf.cchBuf
1730 && !RT_C_IS_SPACE(pExp->StrBuf.pszBuf[off + cchToReplace]);
1731 size_t const cchActualReplacement = fLeadingSpace + cchReplacement + fTrailingSpace;
1732
1733 /*
1734 * Adjust the buffer size and contents.
1735 */
1736 if (cchActualReplacement > cchToReplace)
1737 {
1738 size_t const offMore = cchActualReplacement - cchToReplace;
1739
1740 /* Ensure enough buffer space. */
1741 size_t cbMinBuf = offMore + pExp->StrBuf.cchBuf + 1;
1742 RTEXITCODE rcExit = vbcppStrBufGrow(&pExp->StrBuf, cbMinBuf);
1743 if (rcExit != RTEXITCODE_SUCCESS)
1744 return rcExit;
1745
1746 /* Push the chars following the replacement area down to make room. */
1747 memmove(&pExp->StrBuf.pszBuf[off + cchToReplace + offMore],
1748 &pExp->StrBuf.pszBuf[off + cchToReplace],
1749 pExp->StrBuf.cchBuf - off - cchToReplace + 1);
1750 pExp->StrBuf.cchBuf += offMore;
1751
1752 }
1753 else if (cchActualReplacement < cchToReplace)
1754 {
1755 size_t const offLess = cchToReplace - cchActualReplacement;
1756
1757 /* Pull the chars following the replacement area up. */
1758 memmove(&pExp->StrBuf.pszBuf[off + cchToReplace - offLess],
1759 &pExp->StrBuf.pszBuf[off + cchToReplace],
1760 pExp->StrBuf.cchBuf - off - cchToReplace + 1);
1761 pExp->StrBuf.cchBuf -= offLess;
1762 }
1763
1764 /*
1765 * Insert the replacement string.
1766 */
1767 char *pszCur = &pExp->StrBuf.pszBuf[off];
1768 if (fLeadingSpace)
1769 *pszCur++ = ' ';
1770 memcpy(pszCur, pchReplacement, cchReplacement);
1771 if (fTrailingSpace)
1772 *pszCur++ = ' ';
1773
1774 Assert(strlen(pExp->StrBuf.pszBuf) == pExp->StrBuf.cchBuf);
1775
1776 return RTEXITCODE_SUCCESS;
1777}
1778
1779
1780static unsigned vbcppMacroExpandPeekCh(PVBCPPMACROEXP pExp, size_t *poff)
1781{
1782 size_t off = *poff;
1783 if (off >= pExp->StrBuf.cchBuf)
1784 return pExp->pStrmInput ? ScmStreamPeekCh(pExp->pStrmInput) : ~(unsigned)0;
1785 return pExp->StrBuf.pszBuf[off];
1786}
1787
1788
1789static unsigned vbcppMacroExpandGetCh(PVBCPPMACROEXP pExp, size_t *poff)
1790{
1791 size_t off = *poff;
1792 if (off >= pExp->StrBuf.cchBuf)
1793 return pExp->pStrmInput ? ScmStreamGetCh(pExp->pStrmInput) : ~(unsigned)0;
1794 *poff = off + 1;
1795 return pExp->StrBuf.pszBuf[off];
1796}
1797
1798
1799static RTEXITCODE vbcppMacroExpandSkipEolEx(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff, unsigned chFirst)
1800{
1801 if (chFirst == '\r')
1802 {
1803 unsigned ch2 = vbcppMacroExpandPeekCh(pExp, poff);
1804 if (ch2 == '\n')
1805 {
1806 ch2 = ScmStreamGetCh(pExp->pStrmInput);
1807 AssertReturn(ch2 == '\n', vbcppError(pThis, "internal error"));
1808 }
1809 }
1810 return RTEXITCODE_SUCCESS;
1811}
1812
1813
1814static RTEXITCODE vbcppMacroExpandSkipEol(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
1815{
1816 unsigned ch = vbcppMacroExpandGetCh(pExp, poff);
1817 AssertReturn(ch == '\r' || ch == '\n', vbcppError(pThis, "internal error"));
1818 return vbcppMacroExpandSkipEolEx(pThis, pExp, poff, ch);
1819}
1820
1821
1822static RTEXITCODE vbcppMacroExpandSkipCommentLine(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
1823{
1824 unsigned ch = vbcppMacroExpandGetCh(pExp, poff);
1825 AssertReturn(ch == '/', vbcppError(pThis, "Internal error - expected '/' got '%c'", ch));
1826
1827 unsigned chPrev = 0;
1828 while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
1829 {
1830 if (ch == '\r' || ch == '\n')
1831 {
1832 RTEXITCODE rcExit = vbcppMacroExpandSkipEolEx(pThis, pExp, poff, ch);
1833 if (rcExit != RTEXITCODE_SUCCESS)
1834 return rcExit;
1835 if (chPrev != '\\')
1836 break;
1837 }
1838
1839 chPrev = ch;
1840 }
1841 return RTEXITCODE_SUCCESS;
1842}
1843
1844
1845static RTEXITCODE vbcppMacroExpandSkipComment(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
1846{
1847 unsigned ch = vbcppMacroExpandGetCh(pExp, poff);
1848 AssertReturn(ch == '*', vbcppError(pThis, "Internal error - expected '*' got '%c'", ch));
1849
1850 unsigned chPrev2 = 0;
1851 unsigned chPrev = 0;
1852 while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
1853 {
1854 if (ch == '/' && chPrev == '*')
1855 break;
1856
1857 if (ch == '\r' || ch == '\n')
1858 {
1859 RTEXITCODE rcExit = vbcppMacroExpandSkipEolEx(pThis, pExp, poff, ch);
1860 if (rcExit != RTEXITCODE_SUCCESS)
1861 return rcExit;
1862 if (chPrev == '\\')
1863 {
1864 chPrev = chPrev2; /* for line splicing */
1865 continue;
1866 }
1867 }
1868
1869 chPrev2 = chPrev;
1870 chPrev = ch;
1871 }
1872 return RTEXITCODE_SUCCESS;
1873}
1874
1875
1876static RTEXITCODE vbcppMacroExpandGrowArgArray(PVBCPP pThis, PVBCPPMACROEXP pExp, uint32_t cMinArgs)
1877{
1878 if (cMinArgs > pExp->cArgsAlloced)
1879 {
1880 void *pv = RTMemRealloc(pExp->papszArgs, cMinArgs * sizeof(char *));
1881 if (!pv)
1882 return vbcppError(pThis, "out of memory");
1883 pExp->papszArgs = (char **)pv;
1884 pExp->cArgsAlloced = cMinArgs;
1885 }
1886 return RTEXITCODE_SUCCESS;
1887}
1888
1889
1890static RTEXITCODE vbcppMacroExpandAddEmptyParameter(PVBCPP pThis, PVBCPPMACROEXP pExp)
1891{
1892 RTEXITCODE rcExit = vbcppMacroExpandGrowArgArray(pThis, pExp, pExp->cArgs + 1);
1893 if (rcExit == RTEXITCODE_SUCCESS)
1894 {
1895 char *pszArg = (char *)RTMemAllocZ(1);
1896 if (pszArg)
1897 pExp->papszArgs[pExp->cArgs++] = pszArg;
1898 else
1899 rcExit = vbcppError(pThis, "out of memory");
1900 }
1901 return rcExit;
1902}
1903
1904
1905static RTEXITCODE vbcppMacroExpandGatherParameters(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff, uint32_t cArgsHint)
1906{
1907 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1908
1909 /*
1910 * Free previous argument values.
1911 */
1912 while (pExp->cArgs > 0)
1913 {
1914 RTMemFree(pExp->papszArgs[--pExp->cArgs]);
1915 pExp->papszArgs[pExp->cArgs] = NULL;
1916 }
1917
1918 /*
1919 * The current character should be an opening parenthsis.
1920 */
1921 unsigned ch = vbcppMacroExpandGetCh(pExp, poff);
1922 if (ch != '(')
1923 return vbcppError(pThis, "Internal error - expected '(', found '%c' (#x)", ch, ch);
1924
1925 /*
1926 * Parse the argument list.
1927 */
1928 char chQuote = 0;
1929 size_t cbArgAlloc = 0;
1930 size_t cchArg = 0;
1931 char *pszArg = NULL;
1932 size_t cParentheses = 1;
1933 unsigned chPrev = 0;
1934 while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
1935 {
1936/** @todo check for '#directives'! */
1937 if (ch == ')' && !chQuote)
1938 {
1939 Assert(cParentheses >= 1);
1940 cParentheses--;
1941
1942 /* The end? */
1943 if (!cParentheses)
1944 {
1945 if (cchArg)
1946 while (cchArg > 0 && RT_C_IS_SPACE(pszArg[cchArg - 1]))
1947 pszArg[--cchArg] = '\0';
1948 else if (pExp->cArgs || cArgsHint > 0)
1949 rcExit = vbcppMacroExpandAddEmptyParameter(pThis, pExp);
1950 break;
1951 }
1952 }
1953 else if (ch == '(' && !chQuote)
1954 cParentheses++;
1955 else if (ch == ',' && cParentheses == 1 && !chQuote)
1956 {
1957 /* End of one argument, start of the next. */
1958 if (cchArg)
1959 while (cchArg > 0 && RT_C_IS_SPACE(pszArg[cchArg - 1]))
1960 pszArg[--cchArg] = '\0';
1961 else
1962 {
1963 rcExit = vbcppMacroExpandAddEmptyParameter(pThis, pExp);
1964 if (rcExit != RTEXITCODE_SUCCESS)
1965 break;
1966 }
1967
1968 cbArgAlloc = 0;
1969 cchArg = 0;
1970 pszArg = NULL;
1971 continue;
1972 }
1973 else if (ch == '/' && !chQuote)
1974 {
1975 /* Comment? */
1976 unsigned ch2 = vbcppMacroExpandPeekCh(pExp, poff);
1977 /** @todo This ain't right wrt line splicing. */
1978 if (ch2 == '/' || ch2 == '*')
1979 {
1980 if (ch2 == '/')
1981 rcExit = vbcppMacroExpandSkipCommentLine(pThis, pExp, poff);
1982 else
1983 rcExit = vbcppMacroExpandSkipComment(pThis, pExp, poff);
1984 if (rcExit != RTEXITCODE_SUCCESS)
1985 break;
1986 continue;
1987 }
1988 }
1989 else if (ch == '"')
1990 {
1991 if (!chQuote)
1992 chQuote = '"';
1993 else if (chPrev != '\\')
1994 chQuote = 0;
1995 }
1996 else if (ch == '\'')
1997 {
1998 if (!chQuote)
1999 chQuote = '\'';
2000 else if (chPrev != '\\')
2001 chQuote = 0;
2002 }
2003 else if (ch == '\\')
2004 {
2005 /* Splice lines? */
2006 unsigned ch2 = vbcppMacroExpandPeekCh(pExp, poff);
2007 if (ch2 == '\r' || ch2 == '\n')
2008 {
2009 rcExit = vbcppMacroExpandSkipEol(pThis, pExp, poff);
2010 if (rcExit != RTEXITCODE_SUCCESS)
2011 break;
2012 continue;
2013 }
2014 }
2015 else if (cchArg == 0 && RT_C_IS_SPACE(ch))
2016 continue; /* ignore spaces leading up to an argument value */
2017
2018 /* Append the character to the argument value, adding the argument
2019 to the output array if it's first character in it. */
2020 if (cchArg + 1 >= cbArgAlloc)
2021 {
2022 /* Add argument to the vector. */
2023 if (!cchArg)
2024 {
2025 rcExit = vbcppMacroExpandGrowArgArray(pThis, pExp, RT_MAX(pExp->cArgs + 1, cArgsHint));
2026 if (rcExit != RTEXITCODE_SUCCESS)
2027 break;
2028 pExp->papszArgs[pExp->cArgs++] = pszArg;
2029 }
2030
2031 /* Resize the argument value buffer. */
2032 cbArgAlloc = cbArgAlloc ? cbArgAlloc * 2 : 16;
2033 pszArg = (char *)RTMemRealloc(pszArg, cbArgAlloc);
2034 if (!pszArg)
2035 {
2036 rcExit = vbcppError(pThis, "out of memory");
2037 break;
2038 }
2039 pExp->papszArgs[pExp->cArgs - 1] = pszArg;
2040 }
2041
2042 pszArg[cchArg++] = ch;
2043 pszArg[cchArg] = '\0';
2044 }
2045
2046 /*
2047 * Check that we're leaving on good terms.
2048 */
2049 if (rcExit == RTEXITCODE_SUCCESS)
2050 {
2051 if (cParentheses)
2052 rcExit = vbcppError(pThis, "Missing ')'");
2053 }
2054
2055 return rcExit;
2056}
2057
2058
2059/**
2060 * Expands the arguments referenced in the macro value.
2061 *
2062 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2063 * @param pThis The C preprocessor instance.
2064 * @param pExp The expansion context.
2065 * @param pMacro The macro. Must be a function macro.
2066 * @param pStrBuf String buffer containing the result. The caller
2067 * should initialize and destroy this!
2068 */
2069static RTEXITCODE vbcppMacroExpandValueWithArguments(PVBCPP pThis, PVBCPPMACROEXP pExp, PVBCPPMACRO pMacro,
2070 PVBCPPSTRBUF pStrBuf)
2071{
2072 Assert(pMacro->fFunction);
2073
2074 /*
2075 * Empty?
2076 */
2077 if ( !pMacro->cchValue
2078 || (pMacro->cchValue == 1 && pMacro->szValue[0] == '#'))
2079 return RTEXITCODE_SUCCESS;
2080
2081 /*
2082 * Parse the value.
2083 */
2084 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2085 const char *pszSrc = pMacro->szValue;
2086 const char *pszSrcSeq;
2087 char ch;
2088 while ((ch = *pszSrc++) != '\0')
2089 {
2090 Assert(ch != '\r'); Assert(ch != '\n'); /* probably not true atm. */
2091 if (ch == '#')
2092 {
2093 if (*pszSrc == '#')
2094 {
2095 /* Concatenate operator. */
2096 rcExit = vbcppError(pThis, "The '##' operatore is not yet implemented");
2097 }
2098 else
2099 {
2100 /* Stringify macro argument. */
2101 rcExit = vbcppError(pThis, "The '#' operatore is not yet implemented");
2102 }
2103 return rcExit;
2104 }
2105 else if (ch == '"')
2106 {
2107 /* String litteral. */
2108 pszSrcSeq = pszSrc - 1;
2109 while ((ch = *pszSrc++) != '"')
2110 {
2111 if (ch == '\\')
2112 ch = *pszSrc++;
2113 if (ch == '\0')
2114 {
2115 rcExit = vbcppError(pThis, "String litteral is missing closing quote (\").");
2116 break;
2117 }
2118 }
2119 if (rcExit != RTEXITCODE_SUCCESS)
2120 break;
2121 rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
2122 }
2123 else if (ch == '\'')
2124 {
2125 /* Character constant. */
2126 pszSrcSeq = pszSrc - 1;
2127 while ((ch = *pszSrc++) != '\'')
2128 {
2129 if (ch == '\\')
2130 ch = *pszSrc++;
2131 if (ch == '\0')
2132 {
2133 rcExit = vbcppError(pThis, "Character constant is missing closing quote (').");
2134 break;
2135 }
2136 }
2137 if (rcExit != RTEXITCODE_SUCCESS)
2138 break;
2139 rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
2140 }
2141 else if (RT_C_IS_DIGIT(ch))
2142 {
2143 /* Process numerical constants correctly (i.e. don't mess with the suffix). */
2144 pszSrcSeq = pszSrc - 1;
2145 while ( (ch = *pszSrc) != '\0'
2146 && ( vbcppIsCIdentifierChar(ch)
2147 || ch == '.') )
2148 pszSrc++;
2149 rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
2150 }
2151 else if (RT_C_IS_SPACE(ch))
2152 {
2153 /* join spaces */
2154 if (RT_C_IS_SPACE(vbcppStrBufLastCh(pStrBuf)))
2155 continue;
2156 rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
2157 }
2158 else if (vbcppIsCIdentifierLeadChar(ch))
2159 {
2160 /* Something we should replace? */
2161 pszSrcSeq = pszSrc - 1;
2162 while ( (ch = *pszSrc) != '\0'
2163 && vbcppIsCIdentifierChar(ch))
2164 pszSrc++;
2165 size_t cchDefine = pszSrc - pszSrcSeq;
2166 uint32_t iArg;
2167 if ( VBCPP_BITMAP_IS_SET(pMacro->bmArgs, *pszSrcSeq)
2168 && (iArg = vbcppMacroLookupArg(pMacro, pszSrcSeq, cchDefine)) != UINT32_MAX)
2169 {
2170 /** @todo check out spaces here! */
2171 if (iArg < pMacro->cArgs)
2172 {
2173 Assert(iArg < pExp->cArgs);
2174 rcExit = vbcppStrBufAppend(pStrBuf, pExp->papszArgs[iArg]);
2175 if (*pExp->papszArgs[iArg] != '\0' && rcExit == RTEXITCODE_SUCCESS)
2176 rcExit = vbcppStrBufAppendCh(pStrBuf, ' ');
2177 }
2178 else
2179 {
2180 /* __VA_ARGS__ */
2181 if (iArg < pExp->cArgs)
2182 {
2183 for (;;)
2184 {
2185 rcExit = vbcppStrBufAppend(pStrBuf, pExp->papszArgs[iArg]);
2186 if (rcExit != RTEXITCODE_SUCCESS)
2187 break;
2188 iArg++;
2189 if (iArg >= pExp->cArgs)
2190 break;
2191 rcExit = vbcppStrBufAppendCh(pStrBuf, ',');
2192 if (rcExit != RTEXITCODE_SUCCESS)
2193 break;
2194 }
2195 }
2196 if (rcExit == RTEXITCODE_SUCCESS)
2197 rcExit = vbcppStrBufAppendCh(pStrBuf, ' ');
2198 }
2199 }
2200 /* Not an argument needing replacing. */
2201 else
2202 rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, cchDefine);
2203 }
2204 else
2205 {
2206 rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
2207 }
2208 }
2209
2210 return rcExit;
2211}
2212
2213
2214
2215/**
2216 * Expands the given macro.
2217 *
2218 * Caller already checked if a function macro should be expanded, i.e. whether
2219 * there is a parameter list.
2220 *
2221 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2222 * @param pThis The C preprocessor instance.
2223 * @param pExp The expansion context.
2224 * @param offMacro Offset into the expansion buffer of the macro
2225 * invocation.
2226 * @param pMacro The macro.
2227 * @param offParameters The start of the parameter list if applicable.
2228 * Ignored if not function macro. If the
2229 * parameter list starts at the current stream
2230 * position shall be at the end of the expansion
2231 * buffer.
2232 */
2233static RTEXITCODE vbcppMacroExpandIt(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offMacro, PVBCPPMACRO pMacro,
2234 size_t offParameters)
2235{
2236 RTEXITCODE rcExit;
2237 Assert(offMacro + pMacro->Core.cchString <= pExp->StrBuf.cchBuf);
2238 Assert(!pMacro->fExpanding);
2239
2240 /*
2241 * Function macros are kind of difficult...
2242 */
2243 if (pMacro->fFunction)
2244 {
2245 rcExit = vbcppMacroExpandGatherParameters(pThis, pExp, &offParameters, pMacro->cArgs + pMacro->fVarArg);
2246 if (rcExit == RTEXITCODE_SUCCESS)
2247 {
2248 if (pExp->cArgs > pMacro->cArgs && !pMacro->fVarArg)
2249 rcExit = vbcppError(pThis, "Too many arguments to macro '%s' - found %u, expected %u",
2250 pMacro->Core.pszString, pExp->cArgs, pMacro->cArgs);
2251 else if (pExp->cArgs < pMacro->cArgs)
2252 rcExit = vbcppError(pThis, "Too few arguments to macro '%s' - found %u, expected %u",
2253 pMacro->Core.pszString, pExp->cArgs, pMacro->cArgs);
2254 }
2255 if (rcExit == RTEXITCODE_SUCCESS)
2256 {
2257 VBCPPSTRBUF ValueBuf;
2258 vbcppStrBufInit(&ValueBuf, pThis);
2259 rcExit = vbcppMacroExpandValueWithArguments(pThis, pExp, pMacro, &ValueBuf);
2260 if (rcExit == RTEXITCODE_SUCCESS)
2261 rcExit = vbcppMacroExpandReplace(pThis, pExp, offMacro, offParameters - offMacro,
2262 ValueBuf.pszBuf, ValueBuf.cchBuf);
2263 vbcppStrBufDelete(&ValueBuf);
2264 }
2265 }
2266 /*
2267 * Object-like macros are easy. :-)
2268 */
2269 else
2270 rcExit = vbcppMacroExpandReplace(pThis, pExp, offMacro, pMacro->Core.cchString, pMacro->szValue, pMacro->cchValue);
2271 if (rcExit == RTEXITCODE_SUCCESS)
2272 {
2273#if 0 /* wrong */
2274 /*
2275 * Push the macro onto the stack.
2276 */
2277 pMacro->fExpanding = true;
2278 pMacro->pUpExpanding = pExp->pMacroStack;
2279 pExp->pMacroStack = pMacro;
2280#endif
2281 }
2282
2283 return rcExit;
2284}
2285
2286
2287/**
2288 * Looks for a left parenthesis in the macro expansion buffer and the input
2289 * stream.
2290 *
2291 * @retval true if found. The stream position at opening parenthesis.
2292 * @retval false if not found. The stream position is unchanged.
2293 *
2294 * @param pThis The C preprocessor instance.
2295 * @param pExp The expansion context.
2296 * @param poff The current offset in the expansion context.
2297 * Will be updated on success.
2298 *
2299 * @sa vbcppInputLookForLeftParenthesis
2300 */
2301static bool vbcppMacroExpandLookForLeftParenthesis(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
2302{
2303 /*
2304 * Search the buffer first. (No comments there.)
2305 */
2306 size_t off = *poff;
2307 while (off < pExp->StrBuf.cchBuf)
2308 {
2309 char ch = pExp->StrBuf.pszBuf[off];
2310 if (!RT_C_IS_SPACE(ch))
2311 {
2312 if (ch == '(')
2313 {
2314 *poff = off;
2315 return true;
2316 }
2317 return false;
2318 }
2319 off++;
2320 }
2321
2322 /*
2323 * Reached the end of the buffer, continue searching in the stream.
2324 */
2325 PSCMSTREAM pStrmInput = pExp->pStrmInput;
2326 size_t offSaved = ScmStreamTell(pStrmInput);
2327 /*RTEXITCODE rcExit = */ vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
2328 unsigned ch = ScmStreamPeekCh(pStrmInput);
2329 if (ch == '(')
2330 {
2331 *poff = pExp->StrBuf.cchBuf;
2332 return true;
2333 }
2334
2335 int rc = ScmStreamSeekAbsolute(pStrmInput, offSaved);
2336 AssertFatalRC(rc);
2337 return false;
2338}
2339
2340
2341/**
2342 * Implements the 'defined' unary operator for \#if and \#elif expressions.
2343 *
2344 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2345 * @param pThis The C preprocessor instance.
2346 * @param pExp The expansion context.
2347 * @param offStart The expansion buffer offset where the 'defined'
2348 * occurs.
2349 * @param poff Where to store the offset at which the re-scan
2350 * shall resume upon return.
2351 */
2352static RTEXITCODE vbcppMacroExpandDefinedOperator(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offStart, size_t *poff)
2353{
2354 Assert(!pExp->pStrmInput); /* offset usage below. */
2355
2356 /*
2357 * Skip white space.
2358 */
2359 unsigned ch;
2360 while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
2361 if (!RT_C_IS_SPACE(ch))
2362 break;
2363 bool const fWithParenthesis = ch == '(';
2364 if (fWithParenthesis)
2365 while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
2366 if (!RT_C_IS_SPACE(ch))
2367 break;
2368
2369 /*
2370 * Macro identifier.
2371 */
2372 if (!vbcppIsCIdentifierLeadChar(ch))
2373 return vbcppError(pThis, "Expected macro name after 'defined' operator");
2374
2375 size_t const offDefine = *poff - 1;
2376 while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
2377 if (!vbcppIsCIdentifierChar(ch))
2378 break;
2379 size_t const cchDefine = *poff - offDefine - 1;
2380
2381 /*
2382 * Check for closing parenthesis.
2383 */
2384 if (fWithParenthesis)
2385 {
2386 while (RT_C_IS_SPACE(ch))
2387 ch = vbcppMacroExpandGetCh(pExp, poff);
2388 if (ch != ')')
2389 return vbcppError(pThis, "Expected closing parenthesis after macro name");
2390 }
2391
2392 /*
2393 * Do the job.
2394 */
2395 const char *pszResult = vbcppMacroExists(pThis, &pExp->StrBuf.pszBuf[offDefine], cchDefine)
2396 ? "1" : "0";
2397 RTEXITCODE rcExit = vbcppMacroExpandReplace(pThis, pExp, offStart, *poff - offStart, pszResult, 1);
2398 *poff = offStart + 1;
2399 return rcExit;
2400}
2401
2402
2403/**
2404 * Re-scan the expanded macro.
2405 *
2406 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2407 * @param pThis The C preprocessor instance.
2408 * @param pExp The expansion context.
2409 * @param enmMode The re-scan mode.
2410 * @param pcReplacements Where to return the number of replacements
2411 * performed. Optional.
2412 * @param pcDefinedUnknown Where to return the number of defined() checks
2413 * on unknown macros. Optional.
2414 */
2415static RTEXITCODE vbcppMacroExpandReScan(PVBCPP pThis, PVBCPPMACROEXP pExp, VBCPPMACRORESCANMODE enmMode,
2416 size_t *pcReplacements, size_t *pcDefinedUnknown)
2417{
2418 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2419 size_t cReplacements = 0;
2420 size_t cDefinedUnknown = 0;
2421 size_t off = 0;
2422 unsigned ch;
2423 while ( off < pExp->StrBuf.cchBuf
2424 && (ch = vbcppMacroExpandGetCh(pExp, &off)) != ~(unsigned)0)
2425 {
2426 /*
2427 * String litteral or character constant.
2428 */
2429 if (ch == '\'' || ch == '"')
2430 {
2431 unsigned const chEndQuote = ch;
2432 while ( off < pExp->StrBuf.cchBuf
2433 && (ch = vbcppMacroExpandGetCh(pExp, &off)) != ~(unsigned)0)
2434 {
2435 if (ch == '\\')
2436 {
2437 ch = vbcppMacroExpandGetCh(pExp, &off);
2438 if (ch == ~(unsigned)0)
2439 break;
2440 }
2441 else if (ch == chEndQuote)
2442 break;
2443 }
2444 if (ch == ~(unsigned)0)
2445 return vbcppError(pThis, "Missing end quote (%c)", chEndQuote);
2446 }
2447 /*
2448 * Number constant.
2449 */
2450 else if ( RT_C_IS_DIGIT(ch)
2451 || ( ch == '.'
2452 && off + 1 < pExp->StrBuf.cchBuf
2453 && RT_C_IS_DIGIT(vbcppMacroExpandPeekCh(pExp, &off))
2454 )
2455 )
2456 {
2457 while ( off < pExp->StrBuf.cchBuf
2458 && (ch = vbcppMacroExpandPeekCh(pExp, &off)) != ~(unsigned)0
2459 && vbcppIsCIdentifierChar(ch) )
2460 vbcppMacroExpandGetCh(pExp, &off);
2461 }
2462 /*
2463 * Something that can be replaced?
2464 */
2465 else if (vbcppIsCIdentifierLeadChar(ch))
2466 {
2467 size_t offDefine = off - 1;
2468 while ( off < pExp->StrBuf.cchBuf
2469 && (ch = vbcppMacroExpandPeekCh(pExp, &off)) != ~(unsigned)0
2470 && vbcppIsCIdentifierChar(ch) )
2471 vbcppMacroExpandGetCh(pExp, &off);
2472 size_t cchDefine = off - offDefine;
2473
2474 PVBCPPMACRO pMacro = vbcppMacroLookup(pThis, &pExp->StrBuf.pszBuf[offDefine], cchDefine);
2475 if ( pMacro
2476 && ( !pMacro->fFunction
2477 || vbcppMacroExpandLookForLeftParenthesis(pThis, pExp, &off)) )
2478 {
2479 cReplacements++;
2480 rcExit = vbcppMacroExpandIt(pThis, pExp, offDefine, pMacro, off);
2481 off = offDefine;
2482 }
2483 else
2484 {
2485 if ( !pMacro
2486 && enmMode == kMacroReScanMode_Expression
2487 && cchDefine == sizeof("defined") - 1
2488 && !strncmp(&pExp->StrBuf.pszBuf[offDefine], "defined", cchDefine))
2489 {
2490 cReplacements++;
2491 cDefinedUnknown++;
2492 rcExit = vbcppMacroExpandDefinedOperator(pThis, pExp, offDefine, &off);
2493 }
2494 else
2495 off = offDefine + cchDefine;
2496 }
2497 }
2498 else
2499 {
2500 Assert(RT_C_IS_SPACE(ch) || RT_C_IS_PUNCT(ch));
2501 Assert(ch != '\r' && ch != '\n');
2502 }
2503 }
2504
2505 if (pcReplacements)
2506 *pcReplacements = cReplacements;
2507 if (pcDefinedUnknown)
2508 *pcDefinedUnknown = cDefinedUnknown;
2509 return rcExit;
2510}
2511
2512
2513/**
2514 * Cleans up the expansion context.
2515 *
2516 * This involves clearing VBCPPMACRO::fExpanding and VBCPPMACRO::pUpExpanding,
2517 * and freeing the memory resources associated with the expansion context.
2518 *
2519 * @param pExp The expansion context.
2520 */
2521static void vbcppMacroExpandCleanup(PVBCPPMACROEXP pExp)
2522{
2523#if 0
2524 while (pExp->pMacroStack)
2525 {
2526 PVBCPPMACRO pMacro = pExp->pMacroStack;
2527 pExp->pMacroStack = pMacro->pUpExpanding;
2528
2529 pMacro->fExpanding = false;
2530 pMacro->pUpExpanding = NULL;
2531 }
2532#endif
2533
2534 while (pExp->cArgs > 0)
2535 {
2536 RTMemFree(pExp->papszArgs[--pExp->cArgs]);
2537 pExp->papszArgs[pExp->cArgs] = NULL;
2538 }
2539
2540 RTMemFree(pExp->papszArgs);
2541 pExp->papszArgs = NULL;
2542
2543 vbcppStrBufDelete(&pExp->StrBuf);
2544}
2545
2546
2547
2548/**
2549 * Frees a define.
2550 *
2551 * @returns VINF_SUCCESS (used when called by RTStrSpaceDestroy)
2552 * @param pStr Pointer to the VBCPPMACRO::Core member.
2553 * @param pvUser Unused.
2554 */
2555static DECLCALLBACK(int) vbcppMacroFree(PRTSTRSPACECORE pStr, void *pvUser)
2556{
2557 RTMemFree(pStr);
2558 NOREF(pvUser);
2559 return VINF_SUCCESS;
2560}
2561
2562
2563/**
2564 * Removes a define.
2565 *
2566 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2567 * @param pThis The C preprocessor instance.
2568 * @param pszDefine The define name, no argument list or anything.
2569 * @param cchDefine The length of the name. RTSTR_MAX is ok.
2570 * @param fExplicitUndef Explicit undefinition, that is, in a selective
2571 * preprocessing run it will evaluate to undefined.
2572 */
2573static RTEXITCODE vbcppMacroUndef(PVBCPP pThis, const char *pszDefine, size_t cchDefine, bool fExplicitUndef)
2574{
2575 PRTSTRSPACECORE pHit = RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine);
2576 if (pHit)
2577 {
2578 RTStrSpaceRemove(&pThis->StrSpace, pHit->pszString);
2579 vbcppMacroFree(pHit, NULL);
2580 }
2581
2582 if (fExplicitUndef)
2583 {
2584 if (cchDefine == RTSTR_MAX)
2585 cchDefine = strlen(pszDefine);
2586
2587 PRTSTRSPACECORE pStr = (PRTSTRSPACECORE)RTMemAlloc(sizeof(*pStr) + cchDefine + 1);
2588 if (!pStr)
2589 return vbcppError(pThis, "out of memory");
2590 char *pszDst = (char *)(pStr + 1);
2591 pStr->pszString = pszDst;
2592 memcpy(pszDst, pszDefine, cchDefine);
2593 pszDst[cchDefine] = '\0';
2594 if (!RTStrSpaceInsert(&pThis->UndefStrSpace, pStr))
2595 RTMemFree(pStr);
2596 }
2597
2598 return RTEXITCODE_SUCCESS;
2599}
2600
2601
2602/**
2603 * Inserts a define (rejecting and freeing it in some case).
2604 *
2605 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2606 * @param pThis The C preprocessor instance.
2607 * @param pMacro The define to insert.
2608 */
2609static RTEXITCODE vbcppMacroInsert(PVBCPP pThis, PVBCPPMACRO pMacro)
2610{
2611 /*
2612 * Reject illegal macro names.
2613 */
2614 if (!strcmp(pMacro->Core.pszString, "defined"))
2615 {
2616 RTEXITCODE rcExit = vbcppError(pThis, "Cannot use '%s' as a macro name", pMacro->Core.pszString);
2617 vbcppMacroFree(&pMacro->Core, NULL);
2618 return rcExit;
2619 }
2620
2621 /*
2622 * Ignore in source-file defines when doing selective preprocessing.
2623 */
2624 if ( !pThis->fRespectSourceDefines
2625 && !pMacro->fCmdLine)
2626 {
2627 /* Ignore*/
2628 vbcppMacroFree(&pMacro->Core, NULL);
2629 return RTEXITCODE_SUCCESS;
2630 }
2631
2632 /*
2633 * Insert it and update the lead character hint bitmap.
2634 */
2635 if (RTStrSpaceInsert(&pThis->StrSpace, &pMacro->Core))
2636 VBCPP_BITMAP_SET(pThis->bmDefined, *pMacro->Core.pszString);
2637 else
2638 {
2639 /*
2640 * Duplicate. When doing selective D preprocessing, let the command
2641 * line take precendece.
2642 */
2643 PVBCPPMACRO pOld = (PVBCPPMACRO)RTStrSpaceGet(&pThis->StrSpace, pMacro->Core.pszString); Assert(pOld);
2644 if ( pThis->fAllowRedefiningCmdLineDefines
2645 || pMacro->fCmdLine == pOld->fCmdLine)
2646 {
2647 if (pMacro->fCmdLine)
2648 RTMsgWarning("Redefining '%s'", pMacro->Core.pszString);
2649
2650 RTStrSpaceRemove(&pThis->StrSpace, pOld->Core.pszString);
2651 vbcppMacroFree(&pOld->Core, NULL);
2652
2653 bool fRc = RTStrSpaceInsert(&pThis->StrSpace, &pMacro->Core);
2654 Assert(fRc); NOREF(fRc);
2655 }
2656 else
2657 {
2658 RTMsgWarning("Ignoring redefinition of '%s'", pMacro->Core.pszString);
2659 vbcppMacroFree(&pMacro->Core, NULL);
2660 }
2661 }
2662
2663 return RTEXITCODE_SUCCESS;
2664}
2665
2666
2667/**
2668 * Adds a define.
2669 *
2670 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2671 * @param pThis The C preprocessor instance.
2672 * @param pszDefine The define name, no parameter list.
2673 * @param cchDefine The length of the name.
2674 * @param pszParams The parameter list.
2675 * @param cchParams The length of the parameter list.
2676 * @param pszValue The value.
2677 * @param cchDefine The length of the value.
2678 * @param fCmdLine Set if originating on the command line.
2679 */
2680static RTEXITCODE vbcppMacroAddFn(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
2681 const char *pszParams, size_t cchParams,
2682 const char *pszValue, size_t cchValue,
2683 bool fCmdLine)
2684
2685{
2686 Assert(RTStrNLen(pszDefine, cchDefine) == cchDefine);
2687 Assert(RTStrNLen(pszParams, cchParams) == cchParams);
2688 Assert(RTStrNLen(pszValue, cchValue) == cchValue);
2689
2690 /*
2691 * Determin the number of arguments and how much space their names
2692 * requires. Performing syntax validation while parsing.
2693 */
2694 bool fVariadic = false;
2695 size_t cchArgNames = 0;
2696 uint32_t cArgs = 0;
2697 for (size_t off = 0; off < cchParams; off++)
2698 {
2699 /* Skip blanks and maybe one comma. */
2700 bool fIgnoreComma = cArgs != 0;
2701 while (off < cchParams)
2702 {
2703 if ( !RT_C_IS_SPACE(pszParams[off])
2704 && ( pszParams[off] != '\\'
2705 || (pszParams[off + 1] != '\n' && pszParams[off + 1] != '\r')))
2706 {
2707 if (pszParams[off] != ',' || !fIgnoreComma)
2708 {
2709 if (vbcppIsCIdentifierLeadChar(pszParams[off]))
2710 break;
2711
2712 /* Check for variadic macro argument, just parsing it for now. */
2713 if ( pszParams[off] == '.'
2714 && off + 3 <= cchParams
2715 && pszParams[off + 1] == '.'
2716 && pszParams[off + 2] == '.')
2717 {
2718 size_t const offSaved = off;
2719 off += 3;
2720 while (off < cchParams && RT_C_IS_SPACE(pszParams[off]))
2721 off++;
2722 if (off == cchParams)
2723 {
2724 cArgs++;
2725 cchArgNames += sizeof("...");
2726 fVariadic = true;
2727 break;
2728 }
2729 off = offSaved;
2730 }
2731 return vbcppErrorPos(pThis, &pszParams[off], "Unexpected character (%#x, off=%zu)", pszParams[off], off);
2732 }
2733 fIgnoreComma = false;
2734 }
2735 off++;
2736 }
2737 if (off >= cchParams)
2738 break;
2739
2740 /* Found and argument. First character is already validated. */
2741 cArgs++;
2742 cchArgNames += 2;
2743 off++;
2744 while ( off < cchParams
2745 && vbcppIsCIdentifierChar(pszParams[off]))
2746 off++, cchArgNames++;
2747 }
2748
2749 /*
2750 * Allocate a structure.
2751 */
2752 size_t cbDef = RT_UOFFSETOF_DYN(VBCPPMACRO, szValue[cchValue + 1 + cchDefine + 1 + cchArgNames])
2753 + sizeof(const char *) * cArgs;
2754 cbDef = RT_ALIGN_Z(cbDef, sizeof(const char *));
2755 PVBCPPMACRO pMacro = (PVBCPPMACRO)RTMemAlloc(cbDef);
2756 if (!pMacro)
2757 return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
2758
2759 char *pszDst = &pMacro->szValue[cchValue + 1];
2760 pMacro->Core.pszString = pszDst;
2761 memcpy(pszDst, pszDefine, cchDefine);
2762 pszDst += cchDefine;
2763 *pszDst++ = '\0';
2764 pMacro->fFunction = true;
2765 pMacro->fVarArg = fVariadic;
2766 pMacro->fCmdLine = fCmdLine;
2767 pMacro->fExpanding = false;
2768 pMacro->cArgs = cArgs;
2769 pMacro->papszArgs = (const char **)((uintptr_t)pMacro + cbDef - sizeof(const char *) * cArgs);
2770 VBCPP_BITMAP_EMPTY(pMacro->bmArgs);
2771 pMacro->cchValue = cchValue;
2772 memcpy(pMacro->szValue, pszValue, cchValue);
2773 pMacro->szValue[cchValue] = '\0';
2774
2775 /*
2776 * Set up the arguments.
2777 */
2778 uint32_t iArg = 0;
2779 for (size_t off = 0; off < cchParams; off++)
2780 {
2781 /* Skip blanks and maybe one comma. */
2782 bool fIgnoreComma = cArgs != 0;
2783 while (off < cchParams)
2784 {
2785 if ( !RT_C_IS_SPACE(pszParams[off])
2786 && ( pszParams[off] != '\\'
2787 || (pszParams[off + 1] != '\n' && pszParams[off + 1] != '\r')))
2788 {
2789 if (pszParams[off] != ',' || !fIgnoreComma)
2790 break;
2791 fIgnoreComma = false;
2792 }
2793 off++;
2794 }
2795 if (off >= cchParams)
2796 break;
2797
2798 /* Found and argument. First character is already validated. */
2799 pMacro->papszArgs[iArg] = pszDst;
2800 if (!fVariadic || iArg + 1 < cArgs)
2801 {
2802 VBCPP_BITMAP_SET(pMacro->bmArgs, pszParams[off]);
2803 do
2804 {
2805 *pszDst++ = pszParams[off++];
2806 } while ( off < cchParams
2807 && vbcppIsCIdentifierChar(pszParams[off]));
2808 }
2809 else
2810 {
2811 Assert(strncmp(&pszParams[off], "...", 3) == 0);
2812 VBCPP_BITMAP_SET(pMacro->bmArgs, '_'); /* __VA_ARGS__ */
2813 off += 3;
2814 *pszDst++ = '.';
2815 *pszDst++ = '.';
2816 *pszDst++ = '.';
2817 }
2818 *pszDst++ = '\0';
2819 iArg++;
2820 }
2821 Assert((uintptr_t)pszDst <= (uintptr_t)pMacro->papszArgs);
2822
2823 return vbcppMacroInsert(pThis, pMacro);
2824}
2825
2826
2827/**
2828 * Adds a define.
2829 *
2830 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2831 * @param pThis The C preprocessor instance.
2832 * @param pszDefine The define name and optionally the argument
2833 * list.
2834 * @param cchDefine The length of the name. RTSTR_MAX is ok.
2835 * @param pszValue The value.
2836 * @param cchDefine The length of the value. RTSTR_MAX is ok.
2837 * @param fCmdLine Set if originating on the command line.
2838 */
2839static RTEXITCODE vbcppMacroAdd(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
2840 const char *pszValue, size_t cchValue, bool fCmdLine)
2841{
2842 /*
2843 * We need the lengths. Trim the input.
2844 */
2845 if (cchDefine == RTSTR_MAX)
2846 cchDefine = strlen(pszDefine);
2847 while (cchDefine > 0 && RT_C_IS_SPACE(*pszDefine))
2848 pszDefine++, cchDefine--;
2849 while (cchDefine > 0 && RT_C_IS_SPACE(pszDefine[cchDefine - 1]))
2850 cchDefine--;
2851 if (!cchDefine)
2852 return vbcppErrorPos(pThis, pszDefine, "The define has no name");
2853
2854 if (cchValue == RTSTR_MAX)
2855 cchValue = strlen(pszValue);
2856 while (cchValue > 0 && RT_C_IS_SPACE(*pszValue))
2857 pszValue++, cchValue--;
2858 while (cchValue > 0 && RT_C_IS_SPACE(pszValue[cchValue - 1]))
2859 cchValue--;
2860
2861 /*
2862 * Arguments make the job a bit more annoying. Handle that elsewhere
2863 */
2864 const char *pszParams = (const char *)memchr(pszDefine, '(', cchDefine);
2865 if (pszParams)
2866 {
2867 size_t cchParams = pszDefine + cchDefine - pszParams;
2868 cchDefine -= cchParams;
2869 if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
2870 return RTEXITCODE_FAILURE;
2871 if (pszParams[cchParams - 1] != ')')
2872 return vbcppErrorPos(pThis, pszParams + cchParams - 1, "Missing closing parenthesis");
2873 pszParams++;
2874 cchParams -= 2;
2875 return vbcppMacroAddFn(pThis, pszDefine, cchDefine, pszParams, cchParams, pszValue, cchValue, fCmdLine);
2876 }
2877
2878 /*
2879 * Simple define, no arguments.
2880 */
2881 if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
2882 return RTEXITCODE_FAILURE;
2883
2884 PVBCPPMACRO pMacro = (PVBCPPMACRO)RTMemAlloc(RT_UOFFSETOF_DYN(VBCPPMACRO, szValue[cchValue + 1 + cchDefine + 1]));
2885 if (!pMacro)
2886 return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
2887
2888 pMacro->Core.pszString = &pMacro->szValue[cchValue + 1];
2889 memcpy((char *)pMacro->Core.pszString, pszDefine, cchDefine);
2890 ((char *)pMacro->Core.pszString)[cchDefine] = '\0';
2891 pMacro->fFunction = false;
2892 pMacro->fVarArg = false;
2893 pMacro->fCmdLine = fCmdLine;
2894 pMacro->fExpanding = false;
2895 pMacro->cArgs = 0;
2896 pMacro->papszArgs = NULL;
2897 VBCPP_BITMAP_EMPTY(pMacro->bmArgs);
2898 pMacro->cchValue = cchValue;
2899 memcpy(pMacro->szValue, pszValue, cchValue);
2900 pMacro->szValue[cchValue] = '\0';
2901
2902 return vbcppMacroInsert(pThis, pMacro);
2903}
2904
2905
2906/**
2907 * Tries to convert a define into an inline D constant.
2908 *
2909 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2910 * @param pThis The C preprocessor instance.
2911 * @param pMacro The macro.
2912 */
2913static RTEXITCODE vbcppMacroTryConvertToInlineD(PVBCPP pThis, PVBCPPMACRO pMacro)
2914{
2915 AssertReturn(pMacro, vbcppError(pThis, "Internal error"));
2916 if (pMacro->fFunction)
2917 return RTEXITCODE_SUCCESS;
2918
2919 /*
2920 * Do some simple macro resolving. (Mostly to make x86.h work.)
2921 */
2922 const char *pszDefine = pMacro->Core.pszString;
2923 const char *pszValue = pMacro->szValue;
2924 size_t cchValue = pMacro->cchValue;
2925
2926 unsigned i = 0;
2927 PVBCPPMACRO pMacro2;
2928 while ( i < 10
2929 && cchValue > 0
2930 && vbcppIsCIdentifierLeadChar(*pszValue)
2931 && (pMacro2 = vbcppMacroLookup(pThis, pszValue, cchValue)) != NULL
2932 && !pMacro2->fFunction )
2933 {
2934 pszValue = pMacro2->szValue;
2935 cchValue = pMacro2->cchValue;
2936 i++;
2937 }
2938
2939 if (!pMacro->cchValue)
2940 return RTEXITCODE_SUCCESS;
2941
2942
2943 /*
2944 * A lone value?
2945 */
2946 ssize_t cch = 0;
2947 uint64_t u64;
2948 char *pszNext;
2949 int rc = RTStrToUInt64Ex(pszValue, &pszNext, 0, &u64);
2950 if (RT_SUCCESS(rc))
2951 {
2952 if ( rc == VWRN_TRAILING_SPACES
2953 || rc == VWRN_NEGATIVE_UNSIGNED
2954 || rc == VWRN_NUMBER_TOO_BIG)
2955 return RTEXITCODE_SUCCESS;
2956 const char *pszType;
2957 if (rc == VWRN_TRAILING_CHARS)
2958 {
2959 if (!strcmp(pszNext, "u") || !strcmp(pszNext, "U"))
2960 pszType = "uint32_t";
2961 else if (!strcmp(pszNext, "ul") || !strcmp(pszNext, "UL"))
2962 pszType = "uintptr_t";
2963 else if (!strcmp(pszNext, "ull") || !strcmp(pszNext, "ULL"))
2964 pszType = "uint64_t";
2965 else
2966 pszType = NULL;
2967 }
2968 else if (u64 <= UINT8_MAX)
2969 pszType = "uint8_t";
2970 else if (u64 <= UINT16_MAX)
2971 pszType = "uint16_t";
2972 else if (u64 <= UINT32_MAX)
2973 pszType = "uint32_t";
2974 else
2975 pszType = "uint64_t";
2976 if (!pszType)
2977 return RTEXITCODE_SUCCESS;
2978 cch = ScmStreamPrintf(&pThis->StrmOutput, "inline %s %s = %.*s;\n",
2979 pszType, pszDefine, pszNext - pszValue, pszValue);
2980 }
2981 /*
2982 * A value wrapped in a constant macro?
2983 */
2984 else if ( (pszNext = (char *)strchr(pszValue, '(')) != NULL
2985 && pszValue[cchValue - 1] == ')' )
2986 {
2987 size_t cchPrefix = pszNext - pszValue;
2988 size_t cchInnerValue = cchValue - cchPrefix - 2;
2989 const char *pchInnerValue = &pszValue[cchPrefix + 1];
2990 while (cchInnerValue > 0 && RT_C_IS_SPACE(*pchInnerValue))
2991 cchInnerValue--, pchInnerValue++;
2992 while (cchInnerValue > 0 && RT_C_IS_SPACE(pchInnerValue[cchInnerValue - 1]))
2993 cchInnerValue--;
2994 if (!cchInnerValue || !RT_C_IS_XDIGIT(*pchInnerValue))
2995 return RTEXITCODE_SUCCESS;
2996
2997 rc = RTStrToUInt64Ex(pchInnerValue, &pszNext, 0, &u64);
2998 if ( RT_FAILURE(rc)
2999 || rc == VWRN_TRAILING_SPACES
3000 || rc == VWRN_NEGATIVE_UNSIGNED
3001 || rc == VWRN_NUMBER_TOO_BIG)
3002 return RTEXITCODE_SUCCESS;
3003
3004 const char *pszType;
3005#define MY_MATCH_STR(a_sz) (sizeof(a_sz) - 1 == cchPrefix && !strncmp(pszValue, a_sz, sizeof(a_sz) - 1))
3006 if (MY_MATCH_STR("UINT8_C"))
3007 pszType = "uint8_t";
3008 else if (MY_MATCH_STR("UINT16_C"))
3009 pszType = "uint16_t";
3010 else if (MY_MATCH_STR("UINT32_C"))
3011 pszType = "uint32_t";
3012 else if (MY_MATCH_STR("UINT64_C"))
3013 pszType = "uint64_t";
3014 else
3015 pszType = NULL;
3016 if (pszType)
3017 cch = ScmStreamPrintf(&pThis->StrmOutput, "inline %s %s = %.*s;\n",
3018 pszType, pszDefine, cchInnerValue, pchInnerValue);
3019 else if (MY_MATCH_STR("RT_BIT") || MY_MATCH_STR("RT_BIT_32"))
3020 cch = ScmStreamPrintf(&pThis->StrmOutput, "inline uint32_t %s = 1U << %llu;\n",
3021 pszDefine, u64);
3022 else if (MY_MATCH_STR("RT_BIT_64"))
3023 cch = ScmStreamPrintf(&pThis->StrmOutput, "inline uint64_t %s = 1ULL << %llu;\n",
3024 pszDefine, u64);
3025 else
3026 return RTEXITCODE_SUCCESS;
3027#undef MY_MATCH_STR
3028 }
3029 /* Dunno what this is... */
3030 else
3031 return RTEXITCODE_SUCCESS;
3032
3033 /*
3034 * Check for output error and clear the output suppression indicator.
3035 */
3036 if (cch < 0)
3037 return vbcppError(pThis, "Output error");
3038
3039 pThis->fJustDroppedLine = false;
3040 return RTEXITCODE_SUCCESS;
3041}
3042
3043
3044
3045/**
3046 * Processes a abbreviated line number directive.
3047 *
3048 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
3049 * @param pThis The C preprocessor instance.
3050 * @param pStrmInput The input stream.
3051 * @param offStart The stream position where the directive
3052 * started (for pass thru).
3053 */
3054static RTEXITCODE vbcppDirectiveDefine(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
3055{
3056 RT_NOREF_PV(offStart);
3057
3058 /*
3059 * Parse it.
3060 */
3061 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
3062 if (rcExit == RTEXITCODE_SUCCESS)
3063 {
3064 size_t cchDefine;
3065 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
3066 if (pchDefine)
3067 {
3068 /* If it's a function style define, parse out the parameter list. */
3069 size_t cchParams = 0;
3070 const char *pchParams = NULL;
3071 unsigned ch = ScmStreamPeekCh(pStrmInput);
3072 if (ch == '(')
3073 {
3074 ScmStreamGetCh(pStrmInput);
3075 pchParams = ScmStreamGetCur(pStrmInput);
3076
3077 unsigned chPrev = ch;
3078 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
3079 {
3080 if (ch == '\r' || ch == '\n')
3081 {
3082 if (chPrev != '\\')
3083 {
3084 rcExit = vbcppError(pThis, "Missing ')'");
3085 break;
3086 }
3087 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
3088 }
3089 if (ch == ')')
3090 {
3091 cchParams = ScmStreamGetCur(pStrmInput) - pchParams;
3092 ScmStreamGetCh(pStrmInput);
3093 break;
3094 }
3095 chPrev = ch;
3096 ScmStreamGetCh(pStrmInput);
3097 }
3098 }
3099 /* The simple kind. */
3100 else if (!RT_C_IS_SPACE(ch) && ch != ~(unsigned)0)
3101 rcExit = vbcppError(pThis, "Expected whitespace after macro name");
3102
3103 /* Parse out the value. */
3104 if (rcExit == RTEXITCODE_SUCCESS)
3105 rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
3106 if (rcExit == RTEXITCODE_SUCCESS)
3107 {
3108 size_t offValue = ScmStreamTell(pStrmInput);
3109 const char *pchValue = ScmStreamGetCur(pStrmInput);
3110 unsigned chPrev = ch;
3111 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
3112 {
3113 if (ch == '\r' || ch == '\n')
3114 {
3115 if (chPrev != '\\')
3116 break;
3117 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
3118 }
3119 chPrev = ScmStreamGetCh(pStrmInput);
3120 }
3121 size_t cchValue = ScmStreamGetCur(pStrmInput) - pchValue;
3122
3123 /*
3124 * Execute.
3125 */
3126 if (pchParams)
3127 rcExit = vbcppMacroAddFn(pThis, pchDefine, cchDefine, pchParams, cchParams, pchValue, cchValue, false);
3128 else
3129 rcExit = vbcppMacroAdd(pThis, pchDefine, cchDefine, pchValue, cchValue, false);
3130
3131 /*
3132 * Pass thru?
3133 */
3134 if ( rcExit == RTEXITCODE_SUCCESS
3135 && pThis->fPassThruDefines)
3136 {
3137 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
3138 ssize_t cch;
3139 if (pchParams)
3140 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s(%.*s)",
3141 cchIndent, "", cchDefine, pchDefine, cchParams, pchParams);
3142 else
3143 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s",
3144 cchIndent, "", cchDefine, pchDefine);
3145 if (cch > 0)
3146 vbcppOutputComment(pThis, pStrmInput, offValue, cch, 1);
3147 else
3148 rcExit = vbcppError(pThis, "output error");
3149 }
3150 else if ( rcExit == RTEXITCODE_SUCCESS
3151 && pThis->enmMode == kVBCppMode_SelectiveD)
3152 rcExit = vbcppMacroTryConvertToInlineD(pThis, vbcppMacroLookup(pThis, pchDefine, cchDefine));
3153 else
3154 pThis->fJustDroppedLine = true;
3155 }
3156 }
3157 }
3158 return rcExit;
3159}
3160
3161
3162/**
3163 * Processes a abbreviated line number directive.
3164 *
3165 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
3166 * @param pThis The C preprocessor instance.
3167 * @param pStrmInput The input stream.
3168 * @param offStart The stream position where the directive
3169 * started (for pass thru).
3170 */
3171static RTEXITCODE vbcppDirectiveUndef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
3172{
3173 RT_NOREF_PV(offStart);
3174
3175 /*
3176 * Parse it.
3177 */
3178 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
3179 if (rcExit == RTEXITCODE_SUCCESS)
3180 {
3181 size_t cchDefine;
3182 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
3183 if (pchDefine)
3184 {
3185 size_t offMaybeComment = vbcppProcessSkipWhite(pStrmInput);
3186 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
3187 if (rcExit == RTEXITCODE_SUCCESS)
3188 {
3189 /*
3190 * Take action.
3191 */
3192 PVBCPPMACRO pMacro = vbcppMacroLookup(pThis, pchDefine, cchDefine);
3193 if ( pMacro
3194 && pThis->fRespectSourceDefines
3195 && ( !pMacro->fCmdLine
3196 || pThis->fAllowRedefiningCmdLineDefines ) )
3197 {
3198 RTStrSpaceRemove(&pThis->StrSpace, pMacro->Core.pszString);
3199 vbcppMacroFree(&pMacro->Core, NULL);
3200 }
3201
3202 /*
3203 * Pass thru.
3204 */
3205 if (pThis->fPassThruDefines)
3206 {
3207 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
3208 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sundef %.*s",
3209 cchIndent, "", cchDefine, pchDefine);
3210 if (cch > 0)
3211 vbcppOutputComment(pThis, pStrmInput, offMaybeComment, cch, 1);
3212 else
3213 rcExit = vbcppError(pThis, "output error");
3214 }
3215
3216 }
3217 }
3218 else
3219 rcExit = vbcppError(pThis, "Malformed #ifndef");
3220 }
3221 return rcExit;
3222
3223}
3224
3225
3226
3227
3228
3229/*
3230 *
3231 *
3232 * C O N D I T I O N A L S
3233 * C O N D I T I O N A L S
3234 * C O N D I T I O N A L S
3235 * C O N D I T I O N A L S
3236 * C O N D I T I O N A L S
3237 *
3238 *
3239 */
3240
3241
3242/**
3243 * Combines current stack result with the one being pushed.
3244 *
3245 * @returns Combined result.
3246 * @param enmEvalPush The result of the condition being pushed.
3247 * @param enmEvalStack The current stack result.
3248 */
3249static VBCPPEVAL vbcppCondCombine(VBCPPEVAL enmEvalPush, VBCPPEVAL enmEvalStack)
3250{
3251 if (enmEvalStack == kVBCppEval_False)
3252 return kVBCppEval_False;
3253 return enmEvalPush;
3254}
3255
3256
3257/**
3258 * Pushes an conditional onto the stack.
3259 *
3260 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
3261 * @param pThis The C preprocessor instance.
3262 * @param pStrmInput The current input stream.
3263 * @param offStart Not currently used, using @a pchCondition and
3264 * @a cchCondition instead.
3265 * @param enmKind The kind of conditional.
3266 * @param enmResult The result of the evaluation.
3267 * @param pchCondition The raw condition.
3268 * @param cchCondition The length of @a pchCondition.
3269 */
3270static RTEXITCODE vbcppCondPush(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart,
3271 VBCPPCONDKIND enmKind, VBCPPEVAL enmResult,
3272 const char *pchCondition, size_t cchCondition)
3273{
3274 RT_NOREF_PV(offStart); RT_NOREF_PV(pStrmInput);
3275
3276
3277 if (pThis->cCondStackDepth >= _64K)
3278 return vbcppError(pThis, "Too many nested #if/#ifdef/#ifndef statements");
3279
3280 /*
3281 * Allocate a new entry and push it.
3282 */
3283 PVBCPPCOND pCond = (PVBCPPCOND)RTMemAlloc(sizeof(*pCond));
3284 if (!pCond)
3285 return vbcppError(pThis, "out of memory");
3286
3287 PVBCPPCOND pUp = pThis->pCondStack;
3288 pCond->enmKind = enmKind;
3289 pCond->enmResult = enmResult;
3290 pCond->enmStackResult = pUp ? vbcppCondCombine(enmResult, pUp->enmStackResult) : enmResult;
3291 pCond->fSeenElse = false;
3292 pCond->fElIfDecided = enmResult == kVBCppEval_True;
3293 pCond->iLevel = pThis->cCondStackDepth;
3294 pCond->iKeepLevel = (pUp ? pUp->iKeepLevel : 0) + (enmResult == kVBCppEval_Undecided);
3295 pCond->pchCond = pchCondition;
3296 pCond->cchCond = cchCondition;
3297
3298 pCond->pUp = pThis->pCondStack;
3299 pThis->pCondStack = pCond;
3300 pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
3301
3302 /*
3303 * Do pass thru.
3304 */
3305 if ( !pThis->fIf0Mode
3306 && enmResult == kVBCppEval_Undecided)
3307 {
3308 /** @todo this is stripping comments of \#ifdef and \#ifndef atm. */
3309 const char *pszDirective;
3310 switch (enmKind)
3311 {
3312 case kVBCppCondKind_If: pszDirective = "if"; break;
3313 case kVBCppCondKind_IfDef: pszDirective = "ifdef"; break;
3314 case kVBCppCondKind_IfNDef: pszDirective = "ifndef"; break;
3315 case kVBCppCondKind_ElIf: pszDirective = "elif"; break;
3316 default: AssertFailedReturn(RTEXITCODE_FAILURE);
3317 }
3318 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*s%s %.*s",
3319 pCond->iKeepLevel - 1, "", pszDirective, cchCondition, pchCondition);
3320 if (cch < 0)
3321 return vbcppError(pThis, "Output error %Rrc", (int)cch);
3322 }
3323 else
3324 pThis->fJustDroppedLine = true;
3325
3326 return RTEXITCODE_SUCCESS;
3327}
3328
3329
3330/**
3331 * Recursively destroys the expression tree.
3332 *
3333 * @param pExpr The root of the expression tree to destroy.
3334 */
3335static void vbcppExprDestoryTree(PVBCPPEXPR pExpr)
3336{
3337 if (!pExpr)
3338 return;
3339
3340 switch (pExpr->enmKind)
3341 {
3342 case kVBCppExprKind_Unary:
3343 vbcppExprDestoryTree(pExpr->u.Unary.pArg);
3344 break;
3345 case kVBCppExprKind_Binary:
3346 vbcppExprDestoryTree(pExpr->u.Binary.pLeft);
3347 vbcppExprDestoryTree(pExpr->u.Binary.pRight);
3348 break;
3349 case kVBCppExprKind_Ternary:
3350 vbcppExprDestoryTree(pExpr->u.Ternary.pExpr);
3351 vbcppExprDestoryTree(pExpr->u.Ternary.pTrue);
3352 vbcppExprDestoryTree(pExpr->u.Ternary.pFalse);
3353 break;
3354 case kVBCppExprKind_SignedValue:
3355 case kVBCppExprKind_UnsignedValue:
3356 break;
3357 case kVBCppExprKind_UndefMacroCall:
3358 pExpr->u.UndefMacroCall.pszName = NULL;
3359 for (size_t i = 0; i < pExpr->u.UndefMacroCall.cArgs; i++)
3360 vbcppExprDestoryTree(pExpr->u.UndefMacroCall.papArgs[i]);
3361 RTMemFree(pExpr->u.UndefMacroCall.papArgs);
3362 RTMemFree(pExpr->u.UndefMacroCall.pszName);
3363 break;
3364 default:
3365 AssertFailed();
3366 return;
3367 }
3368 RTMemFree(pExpr);
3369}
3370
3371
3372/**
3373 * Report error during expression parsing.
3374 *
3375 * @returns kExprRet_Error
3376 * @param pParser The parser instance.
3377 * @param pszMsg The error message.
3378 * @param ... Format arguments.
3379 */
3380static VBCPPEXPRRET vbcppExprParseError(PVBCPPEXPRPARSER pParser, const char *pszMsg, ...)
3381{
3382 va_list va;
3383 va_start(va, pszMsg);
3384 vbcppErrorV(pParser->pThis, pszMsg, va);
3385 va_end(va);
3386 return kExprRet_Error;
3387}
3388
3389
3390/**
3391 * Skip white space.
3392 *
3393 * @param pParser The parser instance.
3394 */
3395static void vbcppExprParseSkipWhiteSpace(PVBCPPEXPRPARSER pParser)
3396{
3397 while (RT_C_IS_SPACE(*pParser->pszCur))
3398 pParser->pszCur++;
3399}
3400
3401
3402/**
3403 * Allocate a new
3404 *
3405 * @returns Pointer to the node. NULL+msg on failure.
3406 * @param pParser The parser instance.
3407 */
3408static PVBCPPEXPR vbcppExprParseAllocNode(PVBCPPEXPRPARSER pParser)
3409{
3410 PVBCPPEXPR pExpr = (PVBCPPEXPR)RTMemAllocZ(sizeof(*pExpr));
3411 if (!pExpr)
3412 vbcppExprParseError(pParser, "out of memory (expression node)");
3413 return pExpr;
3414}
3415
3416
3417/**
3418 * Checks if we're currently in an call to an undefined macro.
3419 *
3420 * This will walk up the expression chain and check for a call node while also
3421 * taking parenthesis nodes into account.
3422 *
3423 * @returns true if we are, false if we aren't.
3424 * @param pParser The parser instance.
3425 */
3426static bool vbcppExprParseIsInUndefCall(PVBCPPEXPRPARSER pParser)
3427{
3428 PVBCPPEXPR pExpr = pParser->pCur;
3429 while (pExpr)
3430 {
3431 if (pExpr->enmKind == kVBCppExprKind_UndefMacroCall)
3432 return true;
3433 if ( pExpr->enmKind == kVBCppExprKind_Unary
3434 && pExpr->u.Unary.enmOperator == kVBCppUnaryOp_Parenthesis)
3435 break;
3436 pExpr = pExpr->pParent;
3437 }
3438 return false;
3439}
3440
3441
3442/**
3443 * Looks for right parentheses, comma and/or end of expression.
3444 *
3445 * (The comma is only if we're in the context of a undefined macro call.)
3446 *
3447 * @returns Expression status.
3448 * @retval kExprRet_Ok
3449 * @retval kExprRet_Error with msg.
3450 * @retval kExprRet_EndOfExpr
3451 * @param pParser The parser instance.
3452 */
3453static VBCPPEXPRRET vbcppExprParseMaybeRParenOrEoe(PVBCPPEXPRPARSER pParser)
3454{
3455 Assert(!pParser->ppCur);
3456 for (;;)
3457 {
3458 vbcppExprParseSkipWhiteSpace(pParser);
3459 char const ch = *pParser->pszCur;
3460 if (ch == '\0')
3461 {
3462 /* Unwind making sure we don't have any incomplete parentheses or
3463 incomplete call expressions on the stack. */
3464 PVBCPPEXPR pCur = pParser->pCur;
3465 Assert(pCur);
3466 for (;;)
3467 {
3468 if (!pCur->fComplete)
3469 {
3470 if ( pCur->enmKind == kVBCppExprKind_Unary
3471 && pCur->u.Unary.enmOperator == kVBCppUnaryOp_Parenthesis)
3472 return vbcppExprParseError(pParser, "Missing right parenthesis");
3473 if (pCur->enmKind == kVBCppExprKind_UndefMacroCall)
3474 return vbcppExprParseError(pParser, "Missing right parenthesis for undefined macro call");
3475 }
3476 if (pCur->pParent)
3477 pCur = pCur->pParent;
3478 else
3479 {
3480 pParser->pCur = pCur;
3481 pParser->ppCur = NULL;
3482 return kExprRet_EndOfExpr;
3483 }
3484 }
3485 }
3486
3487 if (ch != ')' && (ch != ',' || !vbcppExprParseIsInUndefCall(pParser))) /** @todo just immediate? */
3488 break;
3489 pParser->pszCur++;
3490
3491 PVBCPPEXPR pCur = pParser->pCur;
3492 while ( pCur
3493 && ( pCur->enmKind != kVBCppExprKind_UndefMacroCall
3494 || pCur->fComplete /*?*/)
3495 && ( pCur->enmKind != kVBCppExprKind_Unary
3496 || pCur->u.Unary.enmOperator != kVBCppUnaryOp_Parenthesis
3497 || pCur->fComplete))
3498 {
3499 switch (pCur->enmKind)
3500 {
3501 case kVBCppExprKind_SignedValue:
3502 case kVBCppExprKind_UnsignedValue:
3503 Assert(pCur->fComplete);
3504 break;
3505 case kVBCppExprKind_Unary:
3506 AssertReturn(pCur->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
3507 pCur->fComplete = true;
3508 break;
3509 case kVBCppExprKind_Binary:
3510 AssertReturn(pCur->u.Binary.pLeft, vbcppExprParseError(pParser, "internal error"));
3511 AssertReturn(pCur->u.Binary.pRight, vbcppExprParseError(pParser, "internal error"));
3512 pCur->fComplete = true;
3513 break;
3514 case kVBCppExprKind_Ternary:
3515#if 1 /** @todo Check out the ternary operator implementation. */
3516 return vbcppExprParseError(pParser, "The ternary operator is not implemented");
3517#else
3518 Assert(pCur->u.Ternary.pExpr);
3519 if (!pCur->u.Ternary.pTrue)
3520 return vbcppExprParseError(pParser, "?!?!?");
3521 if (!pCur->u.Ternary.pFalse)
3522 return vbcppExprParseError(pParser, "?!?!?!?");
3523 pCur->fComplete = true;
3524#endif
3525 break;
3526 case kVBCppExprKind_UndefMacroCall:
3527 break;
3528 default:
3529 return vbcppExprParseError(pParser, "Internal error (enmKind=%d)", pCur->enmKind);
3530 }
3531 pCur = pCur->pParent;
3532 }
3533 if (!pCur)
3534 return vbcppExprParseError(pParser, "Right parenthesis without a left one");
3535
3536 /*
3537 * If we got down to an undefined macro call, we make it the current
3538 * parser expression and return EndOfExpr. The call expression is
3539 * completed if we hit a ')'.
3540 */
3541 if (pCur->enmKind == kVBCppExprKind_UndefMacroCall)
3542 {
3543 pCur->fComplete = ch == ')';
3544 pParser->pCur = pCur;
3545 pParser->ppCur = NULL;
3546 return kExprRet_EndOfExpr;
3547 }
3548
3549 /*
3550 * Complete the parenthesis expression and make it current.
3551 */
3552 Assert( pCur->enmKind == kVBCppExprKind_Unary
3553 && pCur->u.Unary.enmOperator == kVBCppUnaryOp_Parenthesis);
3554 AssertReturn(pCur->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
3555 pCur->fComplete = true;
3556 pParser->pCur = pCur;
3557 pParser->ppCur = NULL;
3558 }
3559
3560 return kExprRet_Ok;
3561}
3562
3563
3564/**
3565 * Parses an binary operator.
3566 *
3567 * @returns Expression status.
3568 * @retval kExprRet_Ok
3569 * @retval kExprRet_Error with msg.
3570 * @param pParser The parser instance.
3571 */
3572static VBCPPEXPRRET vbcppExprParseBinaryOperator(PVBCPPEXPRPARSER pParser)
3573{
3574 /*
3575 * Binary or ternary operator should follow now.
3576 */
3577 VBCPPBINARYOP enmOp;
3578 char ch = *pParser->pszCur;
3579 switch (ch)
3580 {
3581 case '*':
3582 if (pParser->pszCur[1] == '=')
3583 return vbcppExprParseError(pParser, "The assignment by product operator is not valid in a preprocessor expression");
3584 enmOp = kVBCppBinary_Multiplication;
3585 break;
3586 case '/':
3587 if (pParser->pszCur[1] == '=')
3588 return vbcppExprParseError(pParser, "The assignment by quotient operator is not valid in a preprocessor expression");
3589 enmOp = kVBCppBinary_Division;
3590 break;
3591 case '%':
3592 if (pParser->pszCur[1] == '=')
3593 return vbcppExprParseError(pParser, "The assignment by remainder operator is not valid in a preprocessor expression");
3594 enmOp = kVBCppBinary_Modulo;
3595 break;
3596 case '+':
3597 if (pParser->pszCur[1] == '=')
3598 return vbcppExprParseError(pParser, "The assignment by sum operator is not valid in a preprocessor expression");
3599 enmOp = kVBCppBinary_Addition;
3600 break;
3601 case '-':
3602 if (pParser->pszCur[1] == '=')
3603 return vbcppExprParseError(pParser, "The assignment by difference operator is not valid in a preprocessor expression");
3604 enmOp = kVBCppBinary_Subtraction;
3605 break;
3606 case '<':
3607 enmOp = kVBCppBinary_LessThan;
3608 if (pParser->pszCur[1] == '=')
3609 {
3610 pParser->pszCur++;
3611 enmOp = kVBCppBinary_LessThanOrEqual;
3612 }
3613 else if (pParser->pszCur[1] == '<')
3614 {
3615 pParser->pszCur++;
3616 if (pParser->pszCur[1] == '=')
3617 return vbcppExprParseError(pParser, "The assignment by bitwise left shift operator is not valid in a preprocessor expression");
3618 enmOp = kVBCppBinary_LeftShift;
3619 }
3620 break;
3621 case '>':
3622 enmOp = kVBCppBinary_GreaterThan;
3623 if (pParser->pszCur[1] == '=')
3624 {
3625 pParser->pszCur++;
3626 enmOp = kVBCppBinary_GreaterThanOrEqual;
3627 }
3628 else if (pParser->pszCur[1] == '<')
3629 {
3630 pParser->pszCur++;
3631 if (pParser->pszCur[1] == '=')
3632 return vbcppExprParseError(pParser, "The assignment by bitwise right shift operator is not valid in a preprocessor expression");
3633 enmOp = kVBCppBinary_LeftShift;
3634 }
3635 break;
3636 case '=':
3637 if (pParser->pszCur[1] != '=')
3638 return vbcppExprParseError(pParser, "The assignment operator is not valid in a preprocessor expression");
3639 pParser->pszCur++;
3640 enmOp = kVBCppBinary_EqualTo;
3641 break;
3642
3643 case '!':
3644 if (pParser->pszCur[1] != '=')
3645 return vbcppExprParseError(pParser, "Expected binary operator, found the unary operator logical NOT");
3646 pParser->pszCur++;
3647 enmOp = kVBCppBinary_NotEqualTo;
3648 break;
3649
3650 case '&':
3651 if (pParser->pszCur[1] == '=')
3652 return vbcppExprParseError(pParser, "The assignment by bitwise AND operator is not valid in a preprocessor expression");
3653 if (pParser->pszCur[1] == '&')
3654 {
3655 pParser->pszCur++;
3656 enmOp = kVBCppBinary_LogicalAnd;
3657 }
3658 else
3659 enmOp = kVBCppBinary_BitwiseAnd;
3660 break;
3661 case '^':
3662 if (pParser->pszCur[1] == '=')
3663 return vbcppExprParseError(pParser, "The assignment by bitwise XOR operator is not valid in a preprocessor expression");
3664 enmOp = kVBCppBinary_BitwiseXor;
3665 break;
3666 case '|':
3667 if (pParser->pszCur[1] == '=')
3668 return vbcppExprParseError(pParser, "The assignment by bitwise AND operator is not valid in a preprocessor expression");
3669 if (pParser->pszCur[1] == '|')
3670 {
3671 pParser->pszCur++;
3672 enmOp = kVBCppBinary_LogicalOr;
3673 }
3674 else
3675 enmOp = kVBCppBinary_BitwiseOr;
3676 break;
3677 case '~':
3678 return vbcppExprParseError(pParser, "Expected binary operator, found the unary operator bitwise NOT");
3679
3680 case ':':
3681 case '?':
3682 return vbcppExprParseError(pParser, "The ternary operator is not yet implemented");
3683
3684 default:
3685 return vbcppExprParseError(pParser, "Expected binary operator, found '%.20s'", pParser->pszCur);
3686 }
3687 pParser->pszCur++;
3688
3689 /*
3690 * Create a binary operator node.
3691 */
3692 PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
3693 if (!pExpr)
3694 return kExprRet_Error;
3695 pExpr->fComplete = true;
3696 pExpr->enmKind = kVBCppExprKind_Binary;
3697 pExpr->u.Binary.enmOperator = enmOp;
3698 pExpr->u.Binary.pLeft = NULL;
3699 pExpr->u.Binary.pRight = NULL;
3700
3701 /*
3702 * Back up the tree until we find our spot.
3703 */
3704 PVBCPPEXPR *ppPlace = NULL;
3705 PVBCPPEXPR pChild = pParser->pCur;
3706 PVBCPPEXPR pParent = pChild->pParent;
3707 while (pParent)
3708 {
3709 if (pParent->enmKind == kVBCppExprKind_Unary)
3710 {
3711 if (pParent->u.Unary.enmOperator == kVBCppUnaryOp_Parenthesis)
3712 {
3713 ppPlace = &pParent->u.Unary.pArg;
3714 break;
3715 }
3716 AssertReturn(pParent->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
3717 pParent->fComplete = true;
3718 }
3719 else if (pParent->enmKind == kVBCppExprKind_Binary)
3720 {
3721 AssertReturn(pParent->u.Binary.pLeft, vbcppExprParseError(pParser, "internal error"));
3722 AssertReturn(pParent->u.Binary.pRight, vbcppExprParseError(pParser, "internal error"));
3723 if ((pParent->u.Binary.enmOperator & VBCPPOP_PRECEDENCE_MASK) >= (enmOp & VBCPPOP_PRECEDENCE_MASK))
3724 {
3725 AssertReturn(pChild, vbcppExprParseError(pParser, "internal error"));
3726
3727 if (pParent->u.Binary.pRight == pChild)
3728 ppPlace = &pParent->u.Binary.pRight;
3729 else
3730 ppPlace = &pParent->u.Binary.pLeft;
3731 AssertReturn(*ppPlace == pChild, vbcppExprParseError(pParser, "internal error"));
3732 break;
3733 }
3734 pParent->fComplete = true;
3735 }
3736 else if (pParent->enmKind == kVBCppExprKind_Ternary)
3737 {
3738 return vbcppExprParseError(pParser, "The ternary operator is not implemented");
3739 }
3740 else if (pParent->enmKind == kVBCppExprKind_UndefMacroCall)
3741 {
3742 ppPlace = &pParent->u.UndefMacroCall.papArgs[pParent->u.UndefMacroCall.cArgs];
3743 break;
3744 }
3745 else
3746 AssertReturn( pParent->enmKind == kVBCppExprKind_SignedValue
3747 || pParent->enmKind == kVBCppExprKind_UnsignedValue,
3748 vbcppExprParseError(pParser, "internal error"));
3749
3750 /* Up on level */
3751 pChild = pParent;
3752 pParent = pParent->pParent;
3753 }
3754
3755 /*
3756 * Do the rotation.
3757 */
3758 Assert(pChild);
3759 Assert(pChild->pParent == pParent);
3760 pChild->pParent = pExpr;
3761
3762 pExpr->u.Binary.pLeft = pChild;
3763 pExpr->pParent = pParent;
3764
3765 if (!pParent)
3766 pParser->pRoot = pExpr;
3767 else
3768 *ppPlace = pExpr;
3769
3770 pParser->ppCur = &pExpr->u.Binary.pRight;
3771 pParser->pCur = pExpr;
3772
3773 return kExprRet_Ok;
3774}
3775
3776
3777/**
3778 * Deals with right paretheses or/and end of expression, looks for binary
3779 * operators.
3780 *
3781 * @returns Expression status.
3782 * @retval kExprRet_Ok if binary operator was found processed.
3783 * @retval kExprRet_Error with msg.
3784 * @retval kExprRet_EndOfExpr
3785 * @param pParser The parser instance.
3786 */
3787static VBCPPEXPRRET vbcppExprParseBinaryOrEoeOrRparen(PVBCPPEXPRPARSER pParser)
3788{
3789 VBCPPEXPRRET enmRet = vbcppExprParseMaybeRParenOrEoe(pParser);
3790 if (enmRet != kExprRet_Ok)
3791 return enmRet;
3792 return vbcppExprParseBinaryOperator(pParser);
3793}
3794
3795
3796/**
3797 * Worker for vbcppExprParseIdentifier that parses a call to an undefined macro
3798 * in selective mode.
3799 *
3800 * @returns Expression status.
3801 * @retval kExprRet_Ok if binary operator was found processed.
3802 * @retval kExprRet_Error with msg.
3803 * @retval kExprRet_EndOfExpr
3804 * @param pParser The parser instance.
3805 * @param pszMacro The start of the macro name.
3806 * @param cchMacro The length of the macro name.
3807 */
3808static VBCPPEXPRRET vbcppExprParseUndefMacroCall(PVBCPPEXPRPARSER pParser, const char *pszMacro, size_t cchMacro)
3809{
3810 /*
3811 * Treat it as a call to an undefined macro function.
3812 */
3813 /* Create a node. */
3814 PVBCPPEXPR const pExpr = vbcppExprParseAllocNode(pParser);
3815 if (!pExpr)
3816 return kExprRet_Error;
3817 pExpr->enmKind = kVBCppExprKind_UndefMacroCall;
3818 pExpr->fComplete = false;
3819 pExpr->u.UndefMacroCall.cArgs = 0;
3820 pExpr->u.UndefMacroCall.papArgs = NULL;
3821 pExpr->u.UndefMacroCall.pszName = RTStrDupN(pszMacro, cchMacro);
3822 AssertReturn(pExpr->u.UndefMacroCall.pszName, vbcppExprParseError(pParser, "out of memory"));
3823
3824 /* Link it. */
3825 pExpr->pParent = pParser->pCur;
3826 pParser->pCur = pExpr;
3827 *pParser->ppCur = pExpr;
3828 pParser->ppCur = NULL;
3829
3830 /*
3831 * Parse the argument list.
3832 */
3833 pParser->pszCur++;
3834 for (size_t iArg = 0 ; ; iArg++)
3835 {
3836 /*
3837 * Prepare the next argument expression pointer.
3838 */
3839 if (!(iArg % 16))
3840 {
3841 void *pvNew = RTMemRealloc(pExpr->u.UndefMacroCall.papArgs,
3842 sizeof(pExpr->u.UndefMacroCall.papArgs[0]) * (iArg + 16));
3843 AssertPtrReturn(pvNew, vbcppExprParseError(pParser, "out of memory"));
3844 pExpr->u.UndefMacroCall.papArgs = (PVBCPPEXPR *)pvNew;
3845 }
3846 pExpr->u.UndefMacroCall.papArgs[iArg] = NULL;
3847 pParser->ppCur = &pExpr->u.UndefMacroCall.papArgs[iArg];
3848
3849 /*
3850 * Do the parsing.
3851 */
3852 for (;;)
3853 {
3854 /*
3855 * Eat unary operators until we hit a value or end of argument/call.
3856 */
3857 VBCPPEXPRRET enmRet;
3858 do
3859 enmRet = vbcppExprParseUnaryOrValue(pParser);
3860 while (enmRet == kExprRet_UnaryOperator);
3861 if (enmRet == kExprRet_EndOfExpr)
3862 break;
3863 if (enmRet == kExprRet_Error)
3864 return enmRet;
3865 AssertReturn(enmRet == kExprRet_Value, vbcppExprParseError(pParser, "Expected value (enmRet=%d)", enmRet));
3866
3867 /*
3868 * Non-unary operator, right parenthesis or end of argument/call is up next.
3869 */
3870 enmRet = vbcppExprParseBinaryOrEoeOrRparen(pParser);
3871 if (enmRet == kExprRet_EndOfExpr)
3872 break;
3873 if (enmRet == kExprRet_Error)
3874 return enmRet;
3875 AssertReturn(enmRet == kExprRet_Ok, vbcppExprParseError(pParser, "Expected value (enmRet=%d)", enmRet));
3876 }
3877
3878 /*
3879 * Append the argument and skip past the comma or right parenthesis.
3880 */
3881 if (pExpr->u.UndefMacroCall.papArgs[iArg] != NULL || !pExpr->fComplete)
3882 pExpr->u.UndefMacroCall.cArgs = iArg + 1;
3883
3884 Assert(pParser->pCur == pExpr);
3885 if (pExpr->fComplete)
3886 break;
3887 }
3888
3889 pParser->ppCur = NULL;
3890 return kExprRet_Value;
3891}
3892
3893
3894/**
3895 * Parses an identifier in the expression, replacing it by 0.
3896 *
3897 * All known identifiers has already been replaced by their macro values, so
3898 * what's left are unknown macros. These are replaced by 0.
3899 *
3900 * @returns Expression status.
3901 * @retval kExprRet_Value
3902 * @retval kExprRet_Error with msg.
3903 * @param pParser The parser instance.
3904 */
3905static VBCPPEXPRRET vbcppExprParseIdentifier(PVBCPPEXPRPARSER pParser)
3906{
3907/** @todo don't increment if it's an actively undefined macro. Need to revise
3908 * the expression related code wrt selective preprocessing. */
3909 pParser->cUndefined++;
3910
3911 /* Find the end. */
3912 const char *pszMacro = pParser->pszCur;
3913 const char *pszNext = pszMacro + 1;
3914 while (vbcppIsCIdentifierChar(*pszNext))
3915 pszNext++;
3916 size_t const cchMacro = pszNext - pszMacro;
3917
3918 /* Skip spaces and check for parenthesis. */
3919 pParser->pszCur = pszNext;
3920 vbcppExprParseSkipWhiteSpace(pParser);
3921 if (*pParser->pszCur == '(')
3922 {
3923 if (pParser->pThis->enmMode == kVBCppMode_Selective)
3924 return vbcppExprParseUndefMacroCall(pParser, pszMacro, cchMacro);
3925 return vbcppExprParseError(pParser, "%sUnknown unary operator '%.*s'",
3926 pParser->pThis->enmMode != kVBCppMode_Standard ? "TODO selective: " : "",
3927 cchMacro, pszMacro);
3928 }
3929
3930 /* Create a signed value node. */
3931 PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
3932 if (!pExpr)
3933 return kExprRet_Error;
3934 pExpr->fComplete = true;
3935 pExpr->enmKind = kVBCppExprKind_UnsignedValue;
3936 pExpr->u.UnsignedValue.u64 = 0;
3937
3938 /* Link it. */
3939 pExpr->pParent = pParser->pCur;
3940 pParser->pCur = pExpr;
3941 *pParser->ppCur = pExpr;
3942 pParser->ppCur = NULL;
3943
3944 return kExprRet_Value;
3945}
3946
3947
3948/**
3949 * Parses an numeric constant in the expression.
3950 *
3951 * @returns Expression status.
3952 * @retval kExprRet_Value
3953 * @retval kExprRet_Error with msg.
3954 * @param pParser The parser instance.
3955 */
3956static VBCPPEXPRRET vbcppExprParseNumber(PVBCPPEXPRPARSER pParser)
3957{
3958 bool fSigned;
3959 char *pszNext;
3960 uint64_t u64;
3961 char ch = *pParser->pszCur++;
3962 char ch2 = *pParser->pszCur;
3963 if ( ch == '0'
3964 && (ch2 == 'x' || ch2 == 'X'))
3965 {
3966 ch2 = *++pParser->pszCur;
3967 if (!RT_C_IS_XDIGIT(ch2))
3968 return vbcppExprParseError(pParser, "Expected hex digit following '0x'");
3969 int rc = RTStrToUInt64Ex(pParser->pszCur, &pszNext, 16, &u64);
3970 if ( RT_FAILURE(rc)
3971 || rc == VWRN_NUMBER_TOO_BIG)
3972 return vbcppExprParseError(pParser, "Invalid hex value '%.20s...' (%Rrc)", pParser->pszCur, rc);
3973 fSigned = false;
3974 }
3975 else if (ch == '0')
3976 {
3977 int rc = RTStrToUInt64Ex(pParser->pszCur - 1, &pszNext, 8, &u64);
3978 if ( RT_FAILURE(rc)
3979 || rc == VWRN_NUMBER_TOO_BIG)
3980 return vbcppExprParseError(pParser, "Invalid octal value '%.20s...' (%Rrc)", pParser->pszCur, rc);
3981 fSigned = u64 > (uint64_t)INT64_MAX ? false : true;
3982 }
3983 else
3984 {
3985 int rc = RTStrToUInt64Ex(pParser->pszCur - 1, &pszNext, 10, &u64);
3986 if ( RT_FAILURE(rc)
3987 || rc == VWRN_NUMBER_TOO_BIG)
3988 return vbcppExprParseError(pParser, "Invalid decimal value '%.20s...' (%Rrc)", pParser->pszCur, rc);
3989 fSigned = u64 > (uint64_t)INT64_MAX ? false : true;
3990 }
3991
3992 /* suffix. */
3993 if (vbcppIsCIdentifierLeadChar(*pszNext))
3994 {
3995 size_t cchSuffix = 1;
3996 while (vbcppIsCIdentifierLeadChar(pszNext[cchSuffix]))
3997 cchSuffix++;
3998
3999 if (cchSuffix == 1 && (*pszNext == 'u' || *pszNext == 'U'))
4000 fSigned = false;
4001 else if ( cchSuffix == 1
4002 && (*pszNext == 'l' || *pszNext == 'L'))
4003 fSigned = true;
4004 else if ( cchSuffix == 2
4005 && (!strncmp(pszNext, "ul", 2) || !strncmp(pszNext, "UL", 2)))
4006 fSigned = false;
4007 else if ( cchSuffix == 2
4008 && (!strncmp(pszNext, "ll", 2) || !strncmp(pszNext, "LL", 2)))
4009 fSigned = true;
4010 else if ( cchSuffix == 3
4011 && (!strncmp(pszNext, "ull", 3) || !strncmp(pszNext, "ULL", 3)))
4012 fSigned = false;
4013 else
4014 return vbcppExprParseError(pParser, "Invalid number suffix '%.*s'", cchSuffix, pszNext);
4015
4016 pszNext += cchSuffix;
4017 }
4018 pParser->pszCur = pszNext;
4019
4020 /* Create a signed value node. */
4021 PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
4022 if (!pExpr)
4023 return kExprRet_Error;
4024 pExpr->fComplete = true;
4025 if (fSigned)
4026 {
4027 pExpr->enmKind = kVBCppExprKind_SignedValue;
4028 pExpr->u.SignedValue.s64 = (int64_t)u64;
4029 }
4030 else
4031 {
4032 pExpr->enmKind = kVBCppExprKind_UnsignedValue;
4033 pExpr->u.UnsignedValue.u64 = u64;
4034 }
4035
4036 /* Link it. */
4037 pExpr->pParent = pParser->pCur;
4038 pParser->pCur = pExpr;
4039 *pParser->ppCur = pExpr;
4040 pParser->ppCur = NULL;
4041
4042 return kExprRet_Value;
4043}
4044
4045
4046/**
4047 * Parses an character constant in the expression.
4048 *
4049 * @returns Expression status.
4050 * @retval kExprRet_Value
4051 * @retval kExprRet_Error with msg.
4052 * @param pParser The parser instance.
4053 */
4054static VBCPPEXPRRET vbcppExprParseCharacterConstant(PVBCPPEXPRPARSER pParser)
4055{
4056 Assert(*pParser->pszCur == '\'');
4057 pParser->pszCur++;
4058 char ch2 = *pParser->pszCur++;
4059 if (ch2 == '\'')
4060 return vbcppExprParseError(pParser, "Empty character constant");
4061 int64_t s64;
4062 if (ch2 == '\\')
4063 {
4064 ch2 = *pParser->pszCur++;
4065 switch (ch2)
4066 {
4067 case '0': s64 = 0x00; break;
4068 case 'n': s64 = 0x0d; break;
4069 case 'r': s64 = 0x0a; break;
4070 case 't': s64 = 0x09; break;
4071 default:
4072 return vbcppExprParseError(pParser, "Escape character '%c' is not implemented", ch2);
4073 }
4074 }
4075 else
4076 s64 = ch2;
4077 if (*pParser->pszCur != '\'')
4078 return vbcppExprParseError(pParser, "Character constant contains more than one character");
4079
4080 /* Create a signed value node. */
4081 PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
4082 if (!pExpr)
4083 return kExprRet_Error;
4084 pExpr->fComplete = true;
4085 pExpr->enmKind = kVBCppExprKind_SignedValue;
4086 pExpr->u.SignedValue.s64 = s64;
4087
4088 /* Link it. */
4089 pExpr->pParent = pParser->pCur;
4090 pParser->pCur = pExpr;
4091 *pParser->ppCur = pExpr;
4092 pParser->ppCur = NULL;
4093
4094 return kExprRet_Value;
4095}
4096
4097
4098/**
4099 * Parses a unary operator or a value.
4100 *
4101 * @returns Expression status.
4102 * @retval kExprRet_Value if value was found and processed.
4103 * @retval kExprRet_UnaryOperator if an unary operator was found and processed.
4104 * @retval kExprRet_Error with msg.
4105 * @retval kExprRet_EndOfExpr if reached ',' or ')' if in an undefined call.
4106 * @param pParser The parser instance.
4107 */
4108static VBCPPEXPRRET vbcppExprParseUnaryOrValue(PVBCPPEXPRPARSER pParser)
4109{
4110 vbcppExprParseSkipWhiteSpace(pParser);
4111 char ch = *pParser->pszCur;
4112 if (ch == '\0')
4113 return vbcppExprParseError(pParser, "Premature end of expression");
4114
4115 /*
4116 * Value?
4117 */
4118 if (ch == '\'')
4119 return vbcppExprParseCharacterConstant(pParser);
4120 if (RT_C_IS_DIGIT(ch))
4121 return vbcppExprParseNumber(pParser);
4122 if (ch == '"')
4123 return vbcppExprParseError(pParser, "String litteral");
4124 if (vbcppIsCIdentifierLeadChar(ch))
4125 return vbcppExprParseIdentifier(pParser);
4126
4127 /*
4128 * Operator?
4129 */
4130 VBCPPUNARYOP enmOperator;
4131 if (ch == '+')
4132 {
4133 enmOperator = kVBCppUnaryOp_Pluss;
4134 if (pParser->pszCur[1] == '+')
4135 return vbcppExprParseError(pParser, "The prefix increment operator is not valid in a preprocessor expression");
4136 }
4137 else if (ch == '-')
4138 {
4139 enmOperator = kVBCppUnaryOp_Minus;
4140 if (pParser->pszCur[1] == '-')
4141 return vbcppExprParseError(pParser, "The prefix decrement operator is not valid in a preprocessor expression");
4142 }
4143 else if (ch == '!')
4144 enmOperator = kVBCppUnaryOp_LogicalNot;
4145 else if (ch == '~')
4146 enmOperator = kVBCppUnaryOp_BitwiseNot;
4147 else if (ch == '(')
4148 enmOperator = kVBCppUnaryOp_Parenthesis;
4149 else if ((ch == ',' || ch == ')') && pParser->pCur->enmKind == kVBCppExprKind_UndefMacroCall)
4150 {
4151 pParser->pszCur++;
4152 pParser->pCur->fComplete = ch == ')';
4153 return kExprRet_EndOfExpr;
4154 }
4155 else
4156 return vbcppExprParseError(pParser, "Unexpected token '%.*s'", 32, pParser->pszCur - 1);
4157 pParser->pszCur++;
4158
4159 /* Create an operator node. */
4160 PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
4161 if (!pExpr)
4162 return kExprRet_Error;
4163 pExpr->fComplete = false;
4164 pExpr->enmKind = kVBCppExprKind_Unary;
4165 pExpr->u.Unary.enmOperator = enmOperator;
4166 pExpr->u.Unary.pArg = NULL;
4167
4168 /* Link it into the tree. */
4169 pExpr->pParent = pParser->pCur;
4170 pParser->pCur = pExpr;
4171 *pParser->ppCur = pExpr;
4172 pParser->ppCur = &pExpr->u.Unary.pArg;
4173
4174 return kExprRet_UnaryOperator;
4175}
4176
4177
4178/**
4179 * Parses an expanded preprocessor expression.
4180 *
4181 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4182 * @param pThis The C preprocessor instance.
4183 * @param pszExpr The expression to parse.
4184 * @param cchExpr The length of the expression in case we need it.
4185 * @param ppExprTree Where to return the parse tree.
4186 * @param pcUndefined Where to return the number of unknown undefined
4187 * macros. Optional.
4188 */
4189static RTEXITCODE vbcppExprParse(PVBCPP pThis, char *pszExpr, size_t cchExpr, PVBCPPEXPR *ppExprTree, size_t *pcUndefined)
4190{
4191 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
4192 NOREF(cchExpr);
4193
4194 /*
4195 * Initialize the parser context structure.
4196 */
4197 VBCPPEXPRPARSER Parser;
4198 Parser.pszCur = pszExpr;
4199 Parser.pRoot = NULL;
4200 Parser.pCur = NULL;
4201 Parser.ppCur = &Parser.pRoot;
4202 Parser.pszExpr = pszExpr;
4203 Parser.cUndefined = 0;
4204 Parser.pThis = pThis;
4205
4206 /*
4207 * Do the parsing.
4208 */
4209 VBCPPEXPRRET enmRet;
4210 for (;;)
4211 {
4212 /*
4213 * Eat unary operators until we hit a value.
4214 */
4215 do
4216 enmRet = vbcppExprParseUnaryOrValue(&Parser);
4217 while (enmRet == kExprRet_UnaryOperator);
4218 if (enmRet == kExprRet_Error)
4219 break;
4220 AssertBreakStmt(enmRet == kExprRet_Value, enmRet = vbcppExprParseError(&Parser, "Expected value (enmRet=%d)", enmRet));
4221
4222 /*
4223 * Non-unary operator, right parenthesis or end of expression is up next.
4224 */
4225 enmRet = vbcppExprParseBinaryOrEoeOrRparen(&Parser);
4226 if (enmRet == kExprRet_Error)
4227 break;
4228 if (enmRet == kExprRet_EndOfExpr)
4229 {
4230 /** @todo check if there are any open parentheses. */
4231 rcExit = RTEXITCODE_SUCCESS;
4232 break;
4233 }
4234 AssertBreakStmt(enmRet == kExprRet_Ok, enmRet = vbcppExprParseError(&Parser, "Expected value (enmRet=%d)", enmRet));
4235 }
4236
4237 if (rcExit != RTEXITCODE_SUCCESS)
4238 {
4239 vbcppExprDestoryTree(Parser.pRoot);
4240 return rcExit;
4241 }
4242
4243 if (pcUndefined)
4244 *pcUndefined = Parser.cUndefined;
4245 *ppExprTree = Parser.pRoot;
4246 return rcExit;
4247}
4248
4249
4250/**
4251 * Checks if an expression value value is evaluates to @c true or @c false.
4252 *
4253 * @returns @c true or @c false.
4254 * @param pExpr The value expression.
4255 */
4256static bool vbcppExprIsExprTrue(PVBCPPEXPR pExpr)
4257{
4258 Assert(pExpr->enmKind == kVBCppExprKind_SignedValue || pExpr->enmKind == kVBCppExprKind_UnsignedValue);
4259
4260 return pExpr->enmKind == kVBCppExprKind_SignedValue
4261 ? pExpr->u.SignedValue.s64 != 0
4262 : pExpr->u.UnsignedValue.u64 != 0;
4263}
4264
4265
4266/**
4267 * Evalutes a parse (sub-)tree.
4268 *
4269 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4270 * @param pThis The C preprocessor instance.
4271 * @param pRoot The root of the parse (sub-)tree.
4272 * @param pResult Where to store the result value.
4273 */
4274static RTEXITCODE vbcppExprEvaluteTree(PVBCPP pThis, PVBCPPEXPR pRoot, PVBCPPEXPR pResult)
4275{
4276 RTEXITCODE rcExit;
4277 switch (pRoot->enmKind)
4278 {
4279 case kVBCppExprKind_SignedValue:
4280 pResult->enmKind = kVBCppExprKind_SignedValue;
4281 pResult->u.SignedValue.s64 = pRoot->u.SignedValue.s64;
4282 return RTEXITCODE_SUCCESS;
4283
4284 case kVBCppExprKind_UnsignedValue:
4285 pResult->enmKind = kVBCppExprKind_UnsignedValue;
4286 pResult->u.UnsignedValue.u64 = pRoot->u.UnsignedValue.u64;
4287 return RTEXITCODE_SUCCESS;
4288
4289 case kVBCppExprKind_Unary:
4290 rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Unary.pArg, pResult);
4291 if (rcExit != RTEXITCODE_SUCCESS)
4292 return rcExit;
4293
4294 /* Apply the unary operator to the value */
4295 switch (pRoot->u.Unary.enmOperator)
4296 {
4297 case kVBCppUnaryOp_Minus:
4298 if (pResult->enmKind == kVBCppExprKind_SignedValue)
4299 pResult->u.SignedValue.s64 = -pResult->u.SignedValue.s64;
4300 else
4301 pResult->u.UnsignedValue.u64 = (uint64_t)-(int64_t)pResult->u.UnsignedValue.u64;
4302 break;
4303
4304 case kVBCppUnaryOp_LogicalNot:
4305 if (pResult->enmKind == kVBCppExprKind_SignedValue)
4306 pResult->u.SignedValue.s64 = !pResult->u.SignedValue.s64;
4307 else
4308 pResult->u.UnsignedValue.u64 = !pResult->u.UnsignedValue.u64;
4309 break;
4310
4311 case kVBCppUnaryOp_BitwiseNot:
4312 if (pResult->enmKind == kVBCppExprKind_SignedValue)
4313 pResult->u.SignedValue.s64 = ~pResult->u.SignedValue.s64;
4314 else
4315 pResult->u.UnsignedValue.u64 = ~pResult->u.UnsignedValue.u64;
4316 break;
4317
4318 case kVBCppUnaryOp_Pluss:
4319 case kVBCppUnaryOp_Parenthesis:
4320 /* do nothing. */
4321 break;
4322
4323 default:
4324 return vbcppError(pThis, "Internal error: u.Unary.enmOperator=%d", pRoot->u.Unary.enmOperator);
4325 }
4326 return RTEXITCODE_SUCCESS;
4327
4328 case kVBCppExprKind_Binary:
4329 {
4330 /* Always evalute the left side. */
4331 rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Binary.pLeft, pResult);
4332 if (rcExit != RTEXITCODE_SUCCESS)
4333 return rcExit;
4334
4335 /* If logical AND or OR we can sometimes skip evaluting the right side. */
4336 if ( pRoot->u.Binary.enmOperator == kVBCppBinary_LogicalAnd
4337 && !vbcppExprIsExprTrue(pResult))
4338 return RTEXITCODE_SUCCESS;
4339
4340 if ( pRoot->u.Binary.enmOperator == kVBCppBinary_LogicalOr
4341 && vbcppExprIsExprTrue(pResult))
4342 return RTEXITCODE_SUCCESS;
4343
4344 /* Evalute the right side. */
4345 VBCPPEXPR Result2;
4346 rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Binary.pRight, &Result2);
4347 if (rcExit != RTEXITCODE_SUCCESS)
4348 return rcExit;
4349
4350 /* If one of them is unsigned, promote the other to unsigned as well. */
4351 if ( pResult->enmKind == kVBCppExprKind_UnsignedValue
4352 && Result2.enmKind == kVBCppExprKind_SignedValue)
4353 {
4354 Result2.enmKind = kVBCppExprKind_UnsignedValue;
4355 Result2.u.UnsignedValue.u64 = Result2.u.SignedValue.s64;
4356 }
4357 else if ( pResult->enmKind == kVBCppExprKind_SignedValue
4358 && Result2.enmKind == kVBCppExprKind_UnsignedValue)
4359 {
4360 pResult->enmKind = kVBCppExprKind_UnsignedValue;
4361 pResult->u.UnsignedValue.u64 = pResult->u.SignedValue.s64;
4362 }
4363
4364 /* Perform the operation. */
4365 if (pResult->enmKind == kVBCppExprKind_UnsignedValue)
4366 {
4367 switch (pRoot->u.Binary.enmOperator)
4368 {
4369 case kVBCppBinary_Multiplication:
4370 pResult->u.UnsignedValue.u64 *= Result2.u.UnsignedValue.u64;
4371 break;
4372 case kVBCppBinary_Division:
4373 if (!Result2.u.UnsignedValue.u64)
4374 return vbcppError(pThis, "Divide by zero");
4375 pResult->u.UnsignedValue.u64 /= Result2.u.UnsignedValue.u64;
4376 break;
4377 case kVBCppBinary_Modulo:
4378 if (!Result2.u.UnsignedValue.u64)
4379 return vbcppError(pThis, "Divide by zero");
4380 pResult->u.UnsignedValue.u64 %= Result2.u.UnsignedValue.u64;
4381 break;
4382 case kVBCppBinary_Addition:
4383 pResult->u.UnsignedValue.u64 += Result2.u.UnsignedValue.u64;
4384 break;
4385 case kVBCppBinary_Subtraction:
4386 pResult->u.UnsignedValue.u64 -= Result2.u.UnsignedValue.u64;
4387 break;
4388 case kVBCppBinary_LeftShift:
4389 pResult->u.UnsignedValue.u64 <<= Result2.u.UnsignedValue.u64;
4390 break;
4391 case kVBCppBinary_RightShift:
4392 pResult->u.UnsignedValue.u64 >>= Result2.u.UnsignedValue.u64;
4393 break;
4394 case kVBCppBinary_LessThan:
4395 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 < Result2.u.UnsignedValue.u64;
4396 break;
4397 case kVBCppBinary_LessThanOrEqual:
4398 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 <= Result2.u.UnsignedValue.u64;
4399 break;
4400 case kVBCppBinary_GreaterThan:
4401 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 > Result2.u.UnsignedValue.u64;
4402 break;
4403 case kVBCppBinary_GreaterThanOrEqual:
4404 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 >= Result2.u.UnsignedValue.u64;
4405 break;
4406 case kVBCppBinary_EqualTo:
4407 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 == Result2.u.UnsignedValue.u64;
4408 break;
4409 case kVBCppBinary_NotEqualTo:
4410 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 != Result2.u.UnsignedValue.u64;
4411 break;
4412 case kVBCppBinary_BitwiseAnd:
4413 pResult->u.UnsignedValue.u64 &= Result2.u.UnsignedValue.u64;
4414 break;
4415 case kVBCppBinary_BitwiseXor:
4416 pResult->u.UnsignedValue.u64 ^= Result2.u.UnsignedValue.u64;
4417 break;
4418 case kVBCppBinary_BitwiseOr:
4419 pResult->u.UnsignedValue.u64 |= Result2.u.UnsignedValue.u64;
4420 break;
4421 case kVBCppBinary_LogicalAnd:
4422 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 && Result2.u.UnsignedValue.u64;
4423 break;
4424 case kVBCppBinary_LogicalOr:
4425 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 || Result2.u.UnsignedValue.u64;
4426 break;
4427 default:
4428 return vbcppError(pThis, "Internal error: u.Binary.enmOperator=%d", pRoot->u.Binary.enmOperator);
4429 }
4430 }
4431 else
4432 {
4433 switch (pRoot->u.Binary.enmOperator)
4434 {
4435 case kVBCppBinary_Multiplication:
4436 pResult->u.SignedValue.s64 *= Result2.u.SignedValue.s64;
4437 break;
4438 case kVBCppBinary_Division:
4439 if (!Result2.u.SignedValue.s64)
4440 return vbcppError(pThis, "Divide by zero");
4441 pResult->u.SignedValue.s64 /= Result2.u.SignedValue.s64;
4442 break;
4443 case kVBCppBinary_Modulo:
4444 if (!Result2.u.SignedValue.s64)
4445 return vbcppError(pThis, "Divide by zero");
4446 pResult->u.SignedValue.s64 %= Result2.u.SignedValue.s64;
4447 break;
4448 case kVBCppBinary_Addition:
4449 pResult->u.SignedValue.s64 += Result2.u.SignedValue.s64;
4450 break;
4451 case kVBCppBinary_Subtraction:
4452 pResult->u.SignedValue.s64 -= Result2.u.SignedValue.s64;
4453 break;
4454 case kVBCppBinary_LeftShift:
4455 pResult->u.SignedValue.s64 <<= Result2.u.SignedValue.s64;
4456 break;
4457 case kVBCppBinary_RightShift:
4458 pResult->u.SignedValue.s64 >>= Result2.u.SignedValue.s64;
4459 break;
4460 case kVBCppBinary_LessThan:
4461 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 < Result2.u.SignedValue.s64;
4462 break;
4463 case kVBCppBinary_LessThanOrEqual:
4464 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 <= Result2.u.SignedValue.s64;
4465 break;
4466 case kVBCppBinary_GreaterThan:
4467 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 > Result2.u.SignedValue.s64;
4468 break;
4469 case kVBCppBinary_GreaterThanOrEqual:
4470 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 >= Result2.u.SignedValue.s64;
4471 break;
4472 case kVBCppBinary_EqualTo:
4473 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 == Result2.u.SignedValue.s64;
4474 break;
4475 case kVBCppBinary_NotEqualTo:
4476 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 != Result2.u.SignedValue.s64;
4477 break;
4478 case kVBCppBinary_BitwiseAnd:
4479 pResult->u.SignedValue.s64 &= Result2.u.SignedValue.s64;
4480 break;
4481 case kVBCppBinary_BitwiseXor:
4482 pResult->u.SignedValue.s64 ^= Result2.u.SignedValue.s64;
4483 break;
4484 case kVBCppBinary_BitwiseOr:
4485 pResult->u.SignedValue.s64 |= Result2.u.SignedValue.s64;
4486 break;
4487 case kVBCppBinary_LogicalAnd:
4488 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 && Result2.u.SignedValue.s64;
4489 break;
4490 case kVBCppBinary_LogicalOr:
4491 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 || Result2.u.SignedValue.s64;
4492 break;
4493 default:
4494 return vbcppError(pThis, "Internal error: u.Binary.enmOperator=%d", pRoot->u.Binary.enmOperator);
4495 }
4496 }
4497 return rcExit;
4498 }
4499
4500 case kVBCppExprKind_Ternary:
4501 rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pExpr, pResult);
4502 if (rcExit != RTEXITCODE_SUCCESS)
4503 return rcExit;
4504 if (vbcppExprIsExprTrue(pResult))
4505 return vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pTrue, pResult);
4506 return vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pFalse, pResult);
4507
4508 default:
4509 return vbcppError(pThis, "Internal error: enmKind=%d", pRoot->enmKind);
4510 }
4511}
4512
4513
4514/**
4515 * Evalutes the expression.
4516 *
4517 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4518 * @param pThis The C preprocessor instance.
4519 * @param pszExpr The expression.
4520 * @param cchExpr The length of the expression.
4521 * @param cReplacements The number of replacements.
4522 * @param cDefinedUnknown The number of defined(UNKNOWN) cases. (Relevant
4523 * for selective modes.)
4524 * @param penmResult Where to store the result.
4525 */
4526static RTEXITCODE vbcppExprEval(PVBCPP pThis, char *pszExpr, size_t cchExpr, size_t cReplacements, size_t cDefinedUnknown,
4527 VBCPPEVAL *penmResult)
4528{
4529 Assert(strlen(pszExpr) == cchExpr);
4530 RT_NOREF_PV(cReplacements);
4531
4532 size_t cUndefined;
4533 PVBCPPEXPR pExprTree;
4534 RTEXITCODE rcExit = vbcppExprParse(pThis, pszExpr, cchExpr, &pExprTree, &cUndefined);
4535 if (rcExit == RTEXITCODE_SUCCESS)
4536 {
4537 if ( (!cUndefined && !cDefinedUnknown)
4538 || pThis->enmMode == kVBCppMode_SelectiveD
4539 || pThis->enmMode == kVBCppMode_Standard)
4540 {
4541 VBCPPEXPR Result;
4542 rcExit = vbcppExprEvaluteTree(pThis, pExprTree, &Result);
4543 if (rcExit == RTEXITCODE_SUCCESS)
4544 {
4545 if (vbcppExprIsExprTrue(&Result))
4546 *penmResult = kVBCppEval_True;
4547 else
4548 *penmResult = kVBCppEval_False;
4549 }
4550 }
4551 else
4552 *penmResult = kVBCppEval_Undecided;
4553 }
4554 return rcExit;
4555}
4556
4557
4558static RTEXITCODE vbcppExtractSkipCommentLine(PVBCPP pThis, PSCMSTREAM pStrmInput)
4559{
4560 RT_NOREF_PV(pThis);
4561
4562 unsigned chPrev = ScmStreamGetCh(pStrmInput); Assert(chPrev == '/');
4563 unsigned ch;
4564 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
4565 {
4566 if (ch == '\r' || ch == '\n')
4567 {
4568 if (chPrev != '\\')
4569 break;
4570 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
4571 chPrev = ch;
4572 }
4573 else
4574 {
4575 chPrev = ScmStreamGetCh(pStrmInput);
4576 Assert(chPrev == ch);
4577 }
4578 }
4579 return RTEXITCODE_SUCCESS;
4580}
4581
4582
4583static RTEXITCODE vbcppExtractSkipComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
4584{
4585 unsigned ch = ScmStreamGetCh(pStrmInput); Assert(ch == '*');
4586 while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
4587 {
4588 if (ch == '*')
4589 {
4590 ch = ScmStreamGetCh(pStrmInput);
4591 if (ch == '/')
4592 return RTEXITCODE_SUCCESS;
4593 }
4594 }
4595 return vbcppError(pThis, "Expected '*/'");
4596}
4597
4598
4599static RTEXITCODE vbcppExtractQuotedString(PVBCPP pThis, PSCMSTREAM pStrmInput, PVBCPPSTRBUF pStrBuf,
4600 char chOpen, char chClose)
4601{
4602 unsigned ch = ScmStreamGetCh(pStrmInput);
4603 Assert(ch == (unsigned)chOpen);
4604
4605 RTEXITCODE rcExit = vbcppStrBufAppendCh(pStrBuf, chOpen);
4606 if (rcExit != RTEXITCODE_SUCCESS)
4607 return rcExit;
4608
4609 for (;;)
4610 {
4611 ch = ScmStreamGetCh(pStrmInput);
4612 if (ch == '\\')
4613 {
4614 ch = ScmStreamGetCh(pStrmInput);
4615 if (ch == ~(unsigned)0)
4616 break;
4617 rcExit = vbcppStrBufAppendCh(pStrBuf, '\\');
4618 if (rcExit == RTEXITCODE_SUCCESS)
4619 rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
4620 if (rcExit != RTEXITCODE_SUCCESS)
4621 return rcExit;
4622 }
4623 else if (ch != ~(unsigned)0)
4624 {
4625 rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
4626 if (rcExit != RTEXITCODE_SUCCESS)
4627 return rcExit;
4628 if (ch == (unsigned)chClose)
4629 return RTEXITCODE_SUCCESS;
4630 }
4631 else
4632 break;
4633 }
4634
4635 return vbcppError(pThis, "File ended with an open character constant");
4636}
4637
4638
4639/**
4640 * Extracts a line from the stream, stripping it for comments and maybe
4641 * optimzing some of the whitespace.
4642 *
4643 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4644 * @param pThis The C preprocessor instance.
4645 * @param pStrmInput The input stream.
4646 * @param pStrBuf Where to store the extracted line. Caller must
4647 * initialize this prior to the call an delete it
4648 * after use (even on failure).
4649 * @param poffComment Where to note down the position of the final
4650 * comment. Optional.
4651 */
4652static RTEXITCODE vbcppExtractDirectiveLine(PVBCPP pThis, PSCMSTREAM pStrmInput, PVBCPPSTRBUF pStrBuf, size_t *poffComment)
4653{
4654 size_t offComment = ~(size_t)0;
4655 unsigned ch;
4656 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
4657 {
4658 RTEXITCODE rcExit;
4659 if (ch == '/')
4660 {
4661 /* Comment? */
4662 unsigned ch2 = ScmStreamGetCh(pStrmInput); Assert(ch == ch2); NOREF(ch2);
4663 ch = ScmStreamPeekCh(pStrmInput);
4664 if (ch == '*')
4665 {
4666 offComment = ScmStreamTell(pStrmInput) - 1;
4667 rcExit = vbcppExtractSkipComment(pThis, pStrmInput);
4668 }
4669 else if (ch == '/')
4670 {
4671 offComment = ScmStreamTell(pStrmInput) - 1;
4672 rcExit = vbcppExtractSkipCommentLine(pThis, pStrmInput);
4673 }
4674 else
4675 rcExit = vbcppStrBufAppendCh(pStrBuf, '/');
4676 }
4677 else if (ch == '\'')
4678 {
4679 offComment = ~(size_t)0;
4680 rcExit = vbcppExtractQuotedString(pThis, pStrmInput, pStrBuf, '\'', '\'');
4681 }
4682 else if (ch == '"')
4683 {
4684 offComment = ~(size_t)0;
4685 rcExit = vbcppExtractQuotedString(pThis, pStrmInput, pStrBuf, '"', '"');
4686 }
4687 else if (ch == '\r' || ch == '\n')
4688 break; /* done */
4689 else if ( RT_C_IS_SPACE(ch)
4690 && ( RT_C_IS_SPACE(vbcppStrBufLastCh(pStrBuf))
4691 || vbcppStrBufLastCh(pStrBuf) == '\0') )
4692 {
4693 unsigned ch2 = ScmStreamGetCh(pStrmInput);
4694 Assert(ch == ch2); NOREF(ch2);
4695 rcExit = RTEXITCODE_SUCCESS;
4696 }
4697 else
4698 {
4699 unsigned ch2 = ScmStreamGetCh(pStrmInput); Assert(ch == ch2);
4700
4701 /* Escaped newline? */
4702 if ( ch == '\\'
4703 && ( (ch2 = ScmStreamPeekCh(pStrmInput)) == '\r'
4704 || ch2 == '\n'))
4705 {
4706 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
4707 rcExit = RTEXITCODE_SUCCESS;
4708 }
4709 else
4710 {
4711 offComment = ~(size_t)0;
4712 rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
4713 }
4714 }
4715 if (rcExit != RTEXITCODE_SUCCESS)
4716 return rcExit;
4717 }
4718
4719 if (poffComment)
4720 *poffComment = offComment;
4721 return RTEXITCODE_SUCCESS;
4722}
4723
4724
4725/**
4726 * Processes a abbreviated line number directive.
4727 *
4728 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4729 * @param pThis The C preprocessor instance.
4730 * @param pStrmInput The input stream.
4731 * @param offStart The stream position where the directive
4732 * started (for pass thru).
4733 * @param enmKind The kind of directive we're processing.
4734 */
4735static RTEXITCODE vbcppDirectiveIfOrElif(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart,
4736 VBCPPCONDKIND enmKind)
4737{
4738 /*
4739 * Check for missing #if if #elif.
4740 */
4741 if ( enmKind == kVBCppCondKind_ElIf
4742 && !pThis->pCondStack )
4743 return vbcppError(pThis, "#elif without #if");
4744
4745 /*
4746 * Extract the expression string.
4747 */
4748 const char *pchCondition = ScmStreamGetCur(pStrmInput);
4749 size_t offComment;
4750 VBCPPMACROEXP ExpCtx;
4751#if 0
4752 ExpCtx.pMacroStack = NULL;
4753#endif
4754 ExpCtx.pStrmInput = NULL;
4755 ExpCtx.papszArgs = NULL;
4756 ExpCtx.cArgs = 0;
4757 ExpCtx.cArgsAlloced = 0;
4758 vbcppStrBufInit(&ExpCtx.StrBuf, pThis);
4759 RTEXITCODE rcExit = vbcppExtractDirectiveLine(pThis, pStrmInput, &ExpCtx.StrBuf, &offComment);
4760 if (rcExit == RTEXITCODE_SUCCESS)
4761 {
4762 if (RT_C_IS_SPACE(*pchCondition))
4763 pchCondition++;
4764 size_t const cchCondition = ScmStreamGetCur(pStrmInput) - pchCondition;
4765
4766 /*
4767 * Expand known macros in it.
4768 */
4769 size_t cReplacements;
4770 size_t cDefinedUnknown;
4771 rcExit = vbcppMacroExpandReScan(pThis, &ExpCtx, kMacroReScanMode_Expression, &cReplacements, &cDefinedUnknown);
4772 if (rcExit == RTEXITCODE_SUCCESS)
4773 {
4774 /*
4775 * Strip it and check that it's not empty.
4776 */
4777 char *pszExpr = ExpCtx.StrBuf.pszBuf;
4778 size_t cchExpr = ExpCtx.StrBuf.cchBuf;
4779 while (cchExpr > 0 && RT_C_IS_SPACE(*pszExpr))
4780 pszExpr++, cchExpr--;
4781
4782 while (cchExpr > 0 && RT_C_IS_SPACE(pszExpr[cchExpr - 1]))
4783 {
4784 pszExpr[--cchExpr] = '\0';
4785 ExpCtx.StrBuf.cchBuf--;
4786 }
4787 if (cchExpr)
4788 {
4789 /*
4790 * Now, evalute the expression.
4791 */
4792 VBCPPEVAL enmResult;
4793 rcExit = vbcppExprEval(pThis, pszExpr, cchExpr, cReplacements, cDefinedUnknown, &enmResult);
4794 if (rcExit == RTEXITCODE_SUCCESS)
4795 {
4796 /*
4797 * Take action.
4798 */
4799 if (enmKind != kVBCppCondKind_ElIf)
4800 rcExit = vbcppCondPush(pThis, pStrmInput, offComment, enmKind, enmResult,
4801 pchCondition, cchCondition);
4802 else
4803 {
4804 PVBCPPCOND pCond = pThis->pCondStack;
4805 if ( pCond->enmResult != kVBCppEval_Undecided
4806 && ( !pCond->pUp
4807 || pCond->pUp->enmStackResult == kVBCppEval_True))
4808 {
4809 Assert(enmResult == kVBCppEval_True || enmResult == kVBCppEval_False);
4810 if ( pCond->enmResult == kVBCppEval_False
4811 && enmResult == kVBCppEval_True
4812 && !pCond->fElIfDecided)
4813 {
4814 pCond->enmStackResult = kVBCppEval_True;
4815 pCond->fElIfDecided = true;
4816 }
4817 else
4818 pCond->enmStackResult = kVBCppEval_False;
4819 pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
4820 }
4821 pCond->enmKind = kVBCppCondKind_ElIf;
4822 pCond->enmResult = enmResult;
4823 pCond->pchCond = pchCondition;
4824 pCond->cchCond = cchCondition;
4825
4826 /*
4827 * Do #elif pass thru.
4828 */
4829 if ( !pThis->fIf0Mode
4830 && pCond->enmResult == kVBCppEval_Undecided)
4831 {
4832 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*selif", pCond->iKeepLevel - 1, "");
4833 if (cch > 0)
4834 rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 1);
4835 else
4836 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
4837 }
4838 else
4839 pThis->fJustDroppedLine = true;
4840 }
4841 }
4842 }
4843 else
4844 rcExit = vbcppError(pThis, "Empty #if expression");
4845 }
4846 }
4847 vbcppMacroExpandCleanup(&ExpCtx);
4848 return rcExit;
4849}
4850
4851
4852/**
4853 * Processes a abbreviated line number directive.
4854 *
4855 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4856 * @param pThis The C preprocessor instance.
4857 * @param pStrmInput The input stream.
4858 * @param offStart The stream position where the directive
4859 * started (for pass thru).
4860 */
4861static RTEXITCODE vbcppDirectiveIfDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
4862{
4863 /*
4864 * Parse it.
4865 */
4866 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
4867 if (rcExit == RTEXITCODE_SUCCESS)
4868 {
4869 size_t cchDefine;
4870 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
4871 if (pchDefine)
4872 {
4873 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
4874 if (rcExit == RTEXITCODE_SUCCESS)
4875 {
4876 /*
4877 * Evaluate it.
4878 */
4879 VBCPPEVAL enmEval;
4880 if (vbcppMacroExists(pThis, pchDefine, cchDefine))
4881 enmEval = kVBCppEval_True;
4882 else if ( !pThis->fUndecidedConditionals
4883 || RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
4884 enmEval = kVBCppEval_False;
4885 else
4886 enmEval = kVBCppEval_Undecided;
4887 rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfDef, enmEval,
4888 pchDefine, cchDefine);
4889 }
4890 }
4891 else
4892 rcExit = vbcppError(pThis, "Malformed #ifdef");
4893 }
4894 return rcExit;
4895}
4896
4897
4898/**
4899 * Processes a abbreviated line number directive.
4900 *
4901 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4902 * @param pThis The C preprocessor instance.
4903 * @param pStrmInput The input stream.
4904 * @param offStart The stream position where the directive
4905 * started (for pass thru).
4906 */
4907static RTEXITCODE vbcppDirectiveIfNDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
4908{
4909 /*
4910 * Parse it.
4911 */
4912 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
4913 if (rcExit == RTEXITCODE_SUCCESS)
4914 {
4915 size_t cchDefine;
4916 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
4917 if (pchDefine)
4918 {
4919 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
4920 if (rcExit == RTEXITCODE_SUCCESS)
4921 {
4922 /*
4923 * Evaluate it.
4924 */
4925 VBCPPEVAL enmEval;
4926 if (vbcppMacroExists(pThis, pchDefine, cchDefine))
4927 enmEval = kVBCppEval_False;
4928 else if ( !pThis->fUndecidedConditionals
4929 || RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
4930 enmEval = kVBCppEval_True;
4931 else
4932 enmEval = kVBCppEval_Undecided;
4933 rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfNDef, enmEval,
4934 pchDefine, cchDefine);
4935 }
4936 }
4937 else
4938 rcExit = vbcppError(pThis, "Malformed #ifndef");
4939 }
4940 return rcExit;
4941}
4942
4943
4944/**
4945 * Processes a abbreviated line number directive.
4946 *
4947 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4948 * @param pThis The C preprocessor instance.
4949 * @param pStrmInput The input stream.
4950 * @param offStart The stream position where the directive
4951 * started (for pass thru).
4952 */
4953static RTEXITCODE vbcppDirectiveElse(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
4954{
4955 /*
4956 * Nothing to parse, just comment positions to find and note down.
4957 */
4958 offStart = vbcppProcessSkipWhite(pStrmInput);
4959 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
4960 if (rcExit == RTEXITCODE_SUCCESS)
4961 {
4962 /*
4963 * Execute.
4964 */
4965 PVBCPPCOND pCond = pThis->pCondStack;
4966 if (pCond)
4967 {
4968 if (!pCond->fSeenElse)
4969 {
4970 pCond->fSeenElse = true;
4971 if ( pCond->enmResult != kVBCppEval_Undecided
4972 && ( !pCond->pUp
4973 || pCond->pUp->enmStackResult == kVBCppEval_True))
4974 {
4975 if ( pCond->enmResult == kVBCppEval_True
4976 || pCond->fElIfDecided)
4977
4978 pCond->enmStackResult = kVBCppEval_False;
4979 else
4980 pCond->enmStackResult = kVBCppEval_True;
4981 pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
4982 }
4983
4984 /*
4985 * Do pass thru.
4986 */
4987 if ( !pThis->fIf0Mode
4988 && pCond->enmResult == kVBCppEval_Undecided)
4989 {
4990 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*selse", pCond->iKeepLevel - 1, "");
4991 if (cch > 0)
4992 rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 2);
4993 else
4994 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
4995 }
4996 else
4997 pThis->fJustDroppedLine = true;
4998 }
4999 else
5000 rcExit = vbcppError(pThis, "Double #else or/and missing #endif");
5001 }
5002 else
5003 rcExit = vbcppError(pThis, "#else without #if");
5004 }
5005 return rcExit;
5006}
5007
5008
5009/**
5010 * Processes a abbreviated line number directive.
5011 *
5012 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5013 * @param pThis The C preprocessor instance.
5014 * @param pStrmInput The input stream.
5015 * @param offStart The stream position where the directive
5016 * started (for pass thru).
5017 */
5018static RTEXITCODE vbcppDirectiveEndif(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
5019{
5020 /*
5021 * Nothing to parse, just comment positions to find and note down.
5022 */
5023 offStart = vbcppProcessSkipWhite(pStrmInput);
5024 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
5025 if (rcExit == RTEXITCODE_SUCCESS)
5026 {
5027 /*
5028 * Execute.
5029 */
5030 PVBCPPCOND pCond = pThis->pCondStack;
5031 if (pCond)
5032 {
5033 pThis->pCondStack = pCond->pUp;
5034 pThis->fIf0Mode = pCond->pUp && pCond->pUp->enmStackResult == kVBCppEval_False;
5035
5036 /*
5037 * Do pass thru.
5038 */
5039 if ( !pThis->fIf0Mode
5040 && pCond->enmResult == kVBCppEval_Undecided)
5041 {
5042 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sendif", pCond->iKeepLevel - 1, "");
5043 if (cch > 0)
5044 rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 1);
5045 else
5046 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
5047 }
5048 else
5049 pThis->fJustDroppedLine = true;
5050 }
5051 else
5052 rcExit = vbcppError(pThis, "#endif without #if");
5053 }
5054 return rcExit;
5055}
5056
5057
5058
5059
5060
5061/*
5062 *
5063 *
5064 * Misc Directives
5065 * Misc Directives
5066 * Misc Directives
5067 * Misc Directives
5068 *
5069 *
5070 */
5071
5072
5073/**
5074 * Adds an include directory.
5075 *
5076 * @returns Program exit code, with error message on failure.
5077 * @param pThis The C preprocessor instance.
5078 * @param pszDir The directory to add.
5079 */
5080static RTEXITCODE vbcppAddInclude(PVBCPP pThis, const char *pszDir)
5081{
5082 uint32_t cIncludes = pThis->cIncludes;
5083 if (cIncludes >= _64K)
5084 return vbcppError(pThis, "Too many include directories");
5085
5086 void *pv = RTMemRealloc(pThis->papszIncludes, (cIncludes + 1) * sizeof(char **));
5087 if (!pv)
5088 return vbcppError(pThis, "No memory for include directories");
5089 pThis->papszIncludes = (char **)pv;
5090
5091 int rc = RTStrDupEx(&pThis->papszIncludes[cIncludes], pszDir);
5092 if (RT_FAILURE(rc))
5093 return vbcppError(pThis, "No string memory for include directories");
5094
5095 pThis->cIncludes = cIncludes + 1;
5096 return RTEXITCODE_SUCCESS;
5097}
5098
5099
5100/**
5101 * Processes a abbreviated line number directive.
5102 *
5103 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5104 * @param pThis The C preprocessor instance.
5105 * @param pStrmInput The input stream.
5106 * @param offStart The stream position where the directive
5107 * started (for pass thru).
5108 */
5109static RTEXITCODE vbcppDirectiveInclude(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
5110{
5111 RT_NOREF_PV(offStart);
5112
5113 /*
5114 * Parse it.
5115 */
5116 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
5117 if (rcExit == RTEXITCODE_SUCCESS)
5118 {
5119 size_t cchFileSpec = 0;
5120 const char *pchFileSpec = NULL;
5121 const char *pchFilename = NULL;
5122
5123 unsigned ch = ScmStreamPeekCh(pStrmInput);
5124 unsigned chType = ch;
5125 if (ch == '"' || ch == '<')
5126 {
5127 ScmStreamGetCh(pStrmInput);
5128 pchFileSpec = pchFilename = ScmStreamGetCur(pStrmInput);
5129 unsigned chEnd = chType == '<' ? '>' : '"';
5130 while ( (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0
5131 && ch != chEnd)
5132 {
5133 if (ch == '\r' || ch == '\n')
5134 {
5135 rcExit = vbcppError(pThis, "Multi-line include file specfications are not supported");
5136 break;
5137 }
5138 }
5139
5140 if (rcExit == RTEXITCODE_SUCCESS)
5141 {
5142 if (ch != ~(unsigned)0)
5143 cchFileSpec = ScmStreamGetCur(pStrmInput) - pchFilename - 1;
5144 else
5145 rcExit = vbcppError(pThis, "Expected '%c'", chType);
5146 }
5147 }
5148 else if (vbcppIsCIdentifierLeadChar(ch))
5149 {
5150 //pchFileSpec = ScmStreamCGetWord(pStrmInput, &cchFileSpec);
5151 rcExit = vbcppError(pThis, "Including via a define is not implemented yet");
5152 }
5153 else
5154 rcExit = vbcppError(pThis, "Malformed include directive");
5155
5156 /*
5157 * Take down the location of the next non-white space, in case we need
5158 * to pass thru the directive further down. Then skip to the end of the
5159 * line.
5160 */
5161 size_t const offIncEnd = vbcppProcessSkipWhite(pStrmInput);
5162 if (rcExit == RTEXITCODE_SUCCESS)
5163 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
5164
5165 if (rcExit == RTEXITCODE_SUCCESS)
5166 {
5167 /*
5168 * Execute it.
5169 */
5170 if (pThis->enmIncludeAction == kVBCppIncludeAction_Include)
5171 {
5172 /** @todo Search for the include file and push it onto the input stack.
5173 * Not difficult, just unnecessary rigth now. */
5174 rcExit = vbcppError(pThis, "Sorry, includes are not yet implemented");
5175 }
5176 else if (pThis->enmIncludeAction == kVBCppIncludeAction_PassThru)
5177 {
5178 /* Pretty print the passthru. */
5179 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
5180 ssize_t cch;
5181 if (chType == '<')
5182 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude <%.*s>",
5183 cchIndent, "", cchFileSpec, pchFileSpec);
5184 else if (chType == '"')
5185 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude \"%.*s\"",
5186 cchIndent, "", cchFileSpec, pchFileSpec);
5187 else
5188 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude %.*s",
5189 cchIndent, "", cchFileSpec, pchFileSpec);
5190 if (cch > 0)
5191 rcExit = vbcppOutputComment(pThis, pStrmInput, offIncEnd, cch, 1);
5192 else
5193 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
5194
5195 }
5196 else
5197 {
5198 Assert(pThis->enmIncludeAction == kVBCppIncludeAction_Drop);
5199 pThis->fJustDroppedLine = true;
5200 }
5201 }
5202 }
5203 return rcExit;
5204}
5205
5206
5207/**
5208 * Processes a abbreviated line number directive.
5209 *
5210 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5211 * @param pThis The C preprocessor instance.
5212 * @param pStrmInput The input stream.
5213 * @param offStart The stream position where the directive
5214 * started (for pass thru).
5215 */
5216static RTEXITCODE vbcppDirectivePragma(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
5217{
5218 RT_NOREF_PV(offStart);
5219
5220 /*
5221 * Parse out the first word.
5222 */
5223 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
5224 if (rcExit == RTEXITCODE_SUCCESS)
5225 {
5226 size_t cchPragma;
5227 const char *pchPragma = ScmStreamCGetWord(pStrmInput, &cchPragma);
5228 if (pchPragma)
5229 {
5230 size_t const off2nd = vbcppProcessSkipWhite(pStrmInput);
5231 size_t offComment;
5232 rcExit = vbcppInputSkipToEndOfDirectiveLine(pThis, pStrmInput, &offComment);
5233 if (rcExit == RTEXITCODE_SUCCESS)
5234 {
5235 /*
5236 * What to do about this
5237 */
5238 bool fPassThru = false;
5239 if ( cchPragma == 1
5240 && *pchPragma == 'D')
5241 fPassThru = pThis->fPassThruPragmaD;
5242 else if ( cchPragma == 3
5243 && !strncmp(pchPragma, "STD", 3))
5244 fPassThru = pThis->fPassThruPragmaSTD;
5245 else
5246 fPassThru = pThis->fPassThruPragmaOther;
5247 if (fPassThru)
5248 {
5249 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
5250 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*spragma %.*s",
5251 cchIndent, "", cchPragma, pchPragma);
5252 if (cch > 0)
5253 rcExit = vbcppOutputComment(pThis, pStrmInput, off2nd, cch, 1);
5254 else
5255 rcExit = vbcppError(pThis, "output error");
5256 }
5257 else
5258 pThis->fJustDroppedLine = true;
5259 }
5260 }
5261 else
5262 rcExit = vbcppError(pThis, "Malformed #pragma");
5263 }
5264
5265 return rcExit;
5266}
5267
5268
5269/**
5270 * Processes an error directive.
5271 *
5272 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5273 * @param pThis The C preprocessor instance.
5274 * @param pStrmInput The input stream.
5275 * @param offStart The stream position where the directive
5276 * started (for pass thru).
5277 */
5278static RTEXITCODE vbcppDirectiveError(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
5279{
5280 RT_NOREF(offStart);
5281
5282 /*
5283 * Parse out the message.
5284 */
5285 size_t const off1st = vbcppProcessSkipWhite(pStrmInput);
5286 size_t offComment;
5287 RTEXITCODE rcExit = vbcppInputSkipToEndOfDirectiveLine(pThis, pStrmInput, &offComment);
5288 if (rcExit == RTEXITCODE_SUCCESS)
5289 {
5290 /*
5291 * What to do about this
5292 */
5293 if (pThis->fPassThruError)
5294 {
5295 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
5296 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*serror", cchIndent, "");
5297 if (cch > 0)
5298 rcExit = vbcppOutputComment(pThis, pStrmInput, off1st, cch, 1);
5299 else
5300 rcExit = vbcppError(pThis, "output error");
5301 return rcExit;
5302 }
5303 return vbcppError(pThis, "Hit an #error");
5304 }
5305 RT_NOREF_PV(offStart);
5306 vbcppError(pThis, "Malformed #error");
5307 return vbcppError(pThis, "Hit an #error");
5308}
5309
5310
5311/**
5312 * Processes a abbreviated line number directive.
5313 *
5314 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5315 * @param pThis The C preprocessor instance.
5316 * @param pStrmInput The input stream.
5317 * @param offStart The stream position where the directive
5318 * started (for pass thru).
5319 */
5320static RTEXITCODE vbcppDirectiveLineNo(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
5321{
5322 RT_NOREF_PV(offStart);
5323 RT_NOREF_PV(pStrmInput);
5324 return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
5325}
5326
5327
5328/**
5329 * Processes a abbreviated line number directive.
5330 *
5331 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5332 * @param pThis The C preprocessor instance.
5333 * @param pStrmInput The input stream.
5334 */
5335static RTEXITCODE vbcppDirectiveLineNoShort(PVBCPP pThis, PSCMSTREAM pStrmInput)
5336{
5337 RT_NOREF_PV(pStrmInput);
5338 return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
5339}
5340
5341
5342/**
5343 * Handles a preprocessor directive.
5344 *
5345 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5346 * @param pThis The C preprocessor instance.
5347 * @param pStrmInput The input stream.
5348 */
5349static RTEXITCODE vbcppProcessDirective(PVBCPP pThis, PSCMSTREAM pStrmInput)
5350{
5351 /*
5352 * Get the directive and do a string switch on it.
5353 */
5354 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
5355 if (rcExit != RTEXITCODE_SUCCESS)
5356 return rcExit;
5357 size_t cchDirective;
5358 const char *pchDirective = ScmStreamCGetWord(pStrmInput, &cchDirective);
5359 if (pchDirective)
5360 {
5361 size_t const offStart = ScmStreamTell(pStrmInput);
5362#define IS_DIRECTIVE(a_sz) ( sizeof(a_sz) - 1 == cchDirective && memcmp(pchDirective, a_sz, sizeof(a_sz) - 1) == 0)
5363 if (IS_DIRECTIVE("if"))
5364 rcExit = vbcppDirectiveIfOrElif(pThis, pStrmInput, offStart, kVBCppCondKind_If);
5365 else if (IS_DIRECTIVE("elif"))
5366 rcExit = vbcppDirectiveIfOrElif(pThis, pStrmInput, offStart, kVBCppCondKind_ElIf);
5367 else if (IS_DIRECTIVE("ifdef"))
5368 rcExit = vbcppDirectiveIfDef(pThis, pStrmInput, offStart);
5369 else if (IS_DIRECTIVE("ifndef"))
5370 rcExit = vbcppDirectiveIfNDef(pThis, pStrmInput, offStart);
5371 else if (IS_DIRECTIVE("else"))
5372 rcExit = vbcppDirectiveElse(pThis, pStrmInput, offStart);
5373 else if (IS_DIRECTIVE("endif"))
5374 rcExit = vbcppDirectiveEndif(pThis, pStrmInput, offStart);
5375 else if (!pThis->fIf0Mode)
5376 {
5377 if (IS_DIRECTIVE("include"))
5378 rcExit = vbcppDirectiveInclude(pThis, pStrmInput, offStart);
5379 else if (IS_DIRECTIVE("define"))
5380 rcExit = vbcppDirectiveDefine(pThis, pStrmInput, offStart);
5381 else if (IS_DIRECTIVE("undef"))
5382 rcExit = vbcppDirectiveUndef(pThis, pStrmInput, offStart);
5383 else if (IS_DIRECTIVE("pragma"))
5384 rcExit = vbcppDirectivePragma(pThis, pStrmInput, offStart);
5385 else if (IS_DIRECTIVE("error"))
5386 rcExit = vbcppDirectiveError(pThis, pStrmInput, offStart);
5387 else if (IS_DIRECTIVE("line"))
5388 rcExit = vbcppDirectiveLineNo(pThis, pStrmInput, offStart);
5389 else
5390 rcExit = vbcppError(pThis, "Unknown preprocessor directive '#%.*s'", cchDirective, pchDirective);
5391 }
5392#undef IS_DIRECTIVE
5393 }
5394 else if (!pThis->fIf0Mode)
5395 {
5396 /* Could it be a # <num> "file" directive? */
5397 unsigned ch = ScmStreamPeekCh(pStrmInput);
5398 if (RT_C_IS_DIGIT(ch))
5399 rcExit = vbcppDirectiveLineNoShort(pThis, pStrmInput);
5400 else
5401 rcExit = vbcppError(pThis, "Malformed preprocessor directive");
5402 }
5403 return rcExit;
5404}
5405
5406
5407/*
5408 *
5409 *
5410 * M a i n b o d y.
5411 * M a i n b o d y.
5412 * M a i n b o d y.
5413 * M a i n b o d y.
5414 * M a i n b o d y.
5415 *
5416 *
5417 */
5418
5419
5420/**
5421 * Does the actually preprocessing of the input file.
5422 *
5423 * @returns Exit code.
5424 * @param pThis The C preprocessor instance.
5425 */
5426static RTEXITCODE vbcppPreprocess(PVBCPP pThis)
5427{
5428 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
5429
5430 /*
5431 * Parse.
5432 */
5433 while (pThis->pInputStack)
5434 {
5435 pThis->fMaybePreprocessorLine = true;
5436
5437 PSCMSTREAM pStrmInput = &pThis->pInputStack->StrmInput;
5438 unsigned ch;
5439 while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
5440 {
5441 if (ch == '/')
5442 {
5443 ch = ScmStreamPeekCh(pStrmInput);
5444 if (ch == '*')
5445 rcExit = vbcppProcessMultiLineComment(pThis, pStrmInput);
5446 else if (ch == '/')
5447 rcExit = vbcppProcessOneLineComment(pThis, pStrmInput);
5448 else
5449 {
5450 pThis->fMaybePreprocessorLine = false;
5451 if (!pThis->fIf0Mode)
5452 rcExit = vbcppOutputCh(pThis, '/');
5453 }
5454 }
5455 else if (ch == '#' && pThis->fMaybePreprocessorLine)
5456 {
5457 rcExit = vbcppProcessDirective(pThis, pStrmInput);
5458 pStrmInput = &pThis->pInputStack->StrmInput;
5459 }
5460 else if (ch == '\r' || ch == '\n')
5461 {
5462 if ( ( !pThis->fIf0Mode
5463 && !pThis->fJustDroppedLine)
5464 || !pThis->fRemoveDroppedLines
5465 || !ScmStreamIsAtStartOfLine(&pThis->StrmOutput))
5466 rcExit = vbcppOutputCh(pThis, ch);
5467 pThis->fJustDroppedLine = false;
5468 pThis->fMaybePreprocessorLine = true;
5469 }
5470 else if (RT_C_IS_SPACE(ch))
5471 {
5472 if (!pThis->fIf0Mode)
5473 rcExit = vbcppOutputCh(pThis, ch);
5474 }
5475 else
5476 {
5477 pThis->fMaybePreprocessorLine = false;
5478 if (!pThis->fIf0Mode)
5479 {
5480 if (ch == '"')
5481 rcExit = vbcppProcessStringLitteral(pThis, pStrmInput);
5482 else if (ch == '\'')
5483 rcExit = vbcppProcessCharacterConstant(pThis, pStrmInput);
5484 else if (vbcppIsCIdentifierLeadChar(ch))
5485 rcExit = vbcppProcessIdentifier(pThis, pStrmInput);
5486 else if (RT_C_IS_DIGIT(ch))
5487 rcExit = vbcppProcessNumber(pThis, pStrmInput, ch);
5488 else
5489 rcExit = vbcppOutputCh(pThis, ch);
5490 }
5491 }
5492 if (rcExit != RTEXITCODE_SUCCESS)
5493 break;
5494 }
5495
5496 /*
5497 * Check for errors.
5498 */
5499 if (rcExit != RTEXITCODE_SUCCESS)
5500 break;
5501
5502 /*
5503 * Pop the input stack.
5504 */
5505 PVBCPPINPUT pPopped = pThis->pInputStack;
5506 pThis->pInputStack = pPopped->pUp;
5507 RTMemFree(pPopped);
5508 }
5509
5510 return rcExit;
5511}
5512
5513
5514/**
5515 * Opens the input and output streams.
5516 *
5517 * @returns Exit code.
5518 * @param pThis The C preprocessor instance.
5519 */
5520static RTEXITCODE vbcppOpenStreams(PVBCPP pThis)
5521{
5522 const char * const pszInput = pThis->pszInput ? pThis->pszInput : "stdin";
5523 size_t const cchName = strlen(pszInput);
5524 PVBCPPINPUT const pInput = (PVBCPPINPUT)RTMemAlloc(RT_UOFFSETOF_DYN(VBCPPINPUT, szName[cchName + 1]));
5525 if (!pInput)
5526 return vbcppError(pThis, "out of memory");
5527 pInput->pUp = pThis->pInputStack;
5528 pInput->pszSpecified = pInput->szName;
5529 memcpy(pInput->szName, pszInput, cchName + 1);
5530 pThis->pInputStack = pInput;
5531 int rc;
5532 if (pThis->pszInput)
5533 {
5534 rc = ScmStreamInitForReading(&pInput->StrmInput, pThis->pszInput);
5535 if (RT_FAILURE(rc))
5536 return vbcppError(pThis, "ScmStreamInitForReading returned %Rrc when opening and reading in input file (%s)",
5537 rc, pThis->pszInput);
5538 }
5539 else
5540 {
5541 rc = ScmStreamInitForReadingFromStdInput(&pInput->StrmInput);
5542 if (RT_FAILURE(rc))
5543 return vbcppError(pThis, "ScmStreamInitForReadingFromStdInput returned %Rrc", rc);
5544
5545 }
5546
5547 rc = ScmStreamInitForWriting(&pThis->StrmOutput, &pInput->StrmInput);
5548 if (RT_FAILURE(rc))
5549 return vbcppError(pThis, "ScmStreamInitForWriting returned %Rrc", rc);
5550
5551 pThis->fStrmOutputValid = true;
5552 return RTEXITCODE_SUCCESS;
5553}
5554
5555
5556/**
5557 * Changes the preprocessing mode.
5558 *
5559 * @param pThis The C preprocessor instance.
5560 * @param enmMode The new mode.
5561 */
5562static void vbcppSetMode(PVBCPP pThis, VBCPPMODE enmMode)
5563{
5564 switch (enmMode)
5565 {
5566 case kVBCppMode_Standard:
5567 pThis->fKeepComments = false;
5568 pThis->fRespectSourceDefines = true;
5569 pThis->fAllowRedefiningCmdLineDefines = true;
5570 pThis->fPassThruDefines = false;
5571 pThis->fUndecidedConditionals = false;
5572 pThis->fPassThruPragmaD = false;
5573 pThis->fPassThruPragmaSTD = true;
5574 pThis->fPassThruPragmaOther = true;
5575 pThis->fPassThruError = false;
5576 pThis->fRemoveDroppedLines = false;
5577 pThis->fLineSplicing = true;
5578 pThis->enmIncludeAction = kVBCppIncludeAction_Include;
5579 break;
5580
5581 case kVBCppMode_Selective:
5582 pThis->fKeepComments = true;
5583 pThis->fRespectSourceDefines = false;
5584 pThis->fAllowRedefiningCmdLineDefines = false;
5585 pThis->fPassThruDefines = true;
5586 pThis->fUndecidedConditionals = true;
5587 pThis->fPassThruPragmaD = true;
5588 pThis->fPassThruPragmaSTD = true;
5589 pThis->fPassThruPragmaOther = true;
5590 pThis->fPassThruError = true;
5591 pThis->fRemoveDroppedLines = true;
5592 pThis->fLineSplicing = false;
5593 pThis->enmIncludeAction = kVBCppIncludeAction_PassThru;
5594 break;
5595
5596 case kVBCppMode_SelectiveD:
5597 pThis->fKeepComments = true;
5598 pThis->fRespectSourceDefines = true;
5599 pThis->fAllowRedefiningCmdLineDefines = false;
5600 pThis->fPassThruDefines = false;
5601 pThis->fUndecidedConditionals = false;
5602 pThis->fPassThruPragmaD = true;
5603 pThis->fPassThruPragmaSTD = false;
5604 pThis->fPassThruPragmaOther = false;
5605 pThis->fPassThruError = false;
5606 pThis->fRemoveDroppedLines = true;
5607 pThis->fLineSplicing = false;
5608 pThis->enmIncludeAction = kVBCppIncludeAction_Drop;
5609 break;
5610
5611 default:
5612 AssertFailedReturnVoid();
5613 }
5614 pThis->enmMode = enmMode;
5615}
5616
5617
5618/**
5619 * Parses the command line options.
5620 *
5621 * @returns Program exit code. Exit on non-success or if *pfExit is set.
5622 * @param pThis The C preprocessor instance.
5623 * @param argc The argument count.
5624 * @param argv The argument vector.
5625 * @param pfExit Pointer to the exit indicator.
5626 */
5627static RTEXITCODE vbcppParseOptions(PVBCPP pThis, int argc, char **argv, bool *pfExit)
5628{
5629 RTEXITCODE rcExit;
5630
5631 *pfExit = false;
5632
5633 /*
5634 * Option config.
5635 */
5636 static RTGETOPTDEF const s_aOpts[] =
5637 {
5638 { "--define", 'D', RTGETOPT_REQ_STRING },
5639 { "--include-dir", 'I', RTGETOPT_REQ_STRING },
5640 { "--undefine", 'U', RTGETOPT_REQ_STRING },
5641 { "--keep-comments", 'C', RTGETOPT_REQ_NOTHING },
5642 { "--strip-comments", 'c', RTGETOPT_REQ_NOTHING },
5643 { "--D-strip", 'd', RTGETOPT_REQ_NOTHING },
5644#define OPT_MODE 1000
5645 { "--mode", OPT_MODE, RTGETOPT_REQ_STRING },
5646 };
5647
5648 RTGETOPTUNION ValueUnion;
5649 RTGETOPTSTATE GetOptState;
5650 int rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
5651 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
5652
5653 /*
5654 * Process the options.
5655 */
5656 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
5657 {
5658 switch (rc)
5659 {
5660 case 'c':
5661 pThis->fKeepComments = false;
5662 break;
5663
5664 case 'C':
5665 pThis->fKeepComments = false;
5666 break;
5667
5668 case 'd':
5669 vbcppSetMode(pThis, kVBCppMode_SelectiveD);
5670 break;
5671
5672 case OPT_MODE:
5673 if ( strcmp(ValueUnion.psz, "standard") == 0
5674 || strcmp(ValueUnion.psz, "std") == 0)
5675 vbcppSetMode(pThis, kVBCppMode_Standard);
5676 else if ( strcmp(ValueUnion.psz, "selective") == 0
5677 || strcmp(ValueUnion.psz, "sel") == 0)
5678 vbcppSetMode(pThis, kVBCppMode_Selective);
5679 else if ( strcmp(ValueUnion.psz, "selective-D") == 0
5680 || strcmp(ValueUnion.psz, "D-strip") == 0)
5681 vbcppSetMode(pThis, kVBCppMode_SelectiveD);
5682 else
5683 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown mode '%s'!", ValueUnion.psz);
5684 break;
5685
5686 case 'D':
5687 {
5688 const char *pszEqual = strchr(ValueUnion.psz, '=');
5689 if (pszEqual)
5690 rcExit = vbcppMacroAdd(pThis, ValueUnion.psz, pszEqual - ValueUnion.psz, pszEqual + 1, RTSTR_MAX, true);
5691 else
5692 rcExit = vbcppMacroAdd(pThis, ValueUnion.psz, RTSTR_MAX, "1", 1, true);
5693 if (rcExit != RTEXITCODE_SUCCESS)
5694 return rcExit;
5695 break;
5696 }
5697
5698 case 'I':
5699 rcExit = vbcppAddInclude(pThis, ValueUnion.psz);
5700 if (rcExit != RTEXITCODE_SUCCESS)
5701 return rcExit;
5702 break;
5703
5704 case 'U':
5705 rcExit = vbcppMacroUndef(pThis, ValueUnion.psz, RTSTR_MAX, true);
5706 if (rcExit != RTEXITCODE_SUCCESS)
5707 return rcExit;
5708 break;
5709
5710 case 'h':
5711 RTPrintf("Usage: VBoxCPP [options] <input.c> [output.ii]\n"
5712 "\n"
5713 "Options:\n"
5714 " --mode={mode}\n"
5715 " Sets the processing mode and asscoiated defaults:\n"
5716 " - selective / sel: (default)\n"
5717 " Selectively applying the defines from the command line\n"
5718 " and preserving the rest.\n"
5719 " - selective-D / D-strip:\n"
5720 " Selectively applying the defines from the command line\n"
5721 " and removing things DTrace doesn't like.\n"
5722 " - standard / std:\n"
5723 " Standard C preprocesing. This is incomplete as, among other\n"
5724 " things, include processing has not yet been implemented.\n"
5725 " --D-strip\n"
5726 " Alias for --mode=selective-D\n"
5727 " --define={macro}[=val], -D{macro}[=val]\n"
5728 " Defines a macro.\n"
5729 " --undefine={macro}, -U{macro}\n"
5730 " Undefines a macro.\n"
5731 " --include-dir={dir}, -I{dir}\n"
5732 " Adds {dir} to the list of include directories that should\n"
5733 " be searched for header files."
5734 " --keep-comments, -C\n"
5735 " Keep comments unchanged in the output.\n"
5736 " --strip-comments, -c\n"
5737 " Strip comments in the output. (default)\n"
5738 "\n"
5739 "This is not a complete C preprocessor implementation (yet), as its use is\n"
5740 "mainly to selectively apply and eliminate defines and/or things are DTrace\n"
5741 "finds problematic. Among the missing features are:\n"
5742 " - Support for ternary expressions.\n"
5743 " - Including header files (in standard mode).\n"
5744 " - +++\n"
5745 );
5746 *pfExit = true;
5747 return RTEXITCODE_SUCCESS;
5748
5749 case 'V':
5750 {
5751 /* The following is assuming that svn does it's job here. */
5752 static const char s_szRev[] = "$Revision: 107587 $";
5753 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
5754 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
5755 *pfExit = true;
5756 return RTEXITCODE_SUCCESS;
5757 }
5758
5759 case VINF_GETOPT_NOT_OPTION:
5760 if (!pThis->pszInput)
5761 pThis->pszInput = ValueUnion.psz;
5762 else if (!pThis->pszOutput)
5763 pThis->pszOutput = ValueUnion.psz;
5764 else
5765 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "too many file arguments");
5766 break;
5767
5768
5769 /*
5770 * Errors and bugs.
5771 */
5772 default:
5773 return RTGetOptPrintError(rc, &ValueUnion);
5774 }
5775 }
5776
5777 return RTEXITCODE_SUCCESS;
5778}
5779
5780
5781/**
5782 * Terminates the preprocessor.
5783 *
5784 * This may return failure if an error was delayed.
5785 *
5786 * @returns Exit code.
5787 * @param pThis The C preprocessor instance.
5788 */
5789static RTEXITCODE vbcppTerm(PVBCPP pThis)
5790{
5791 /*
5792 * Flush the output first.
5793 */
5794 if (pThis->fStrmOutputValid)
5795 {
5796 if (pThis->pszOutput)
5797 {
5798 int rc = ScmStreamWriteToFile(&pThis->StrmOutput, "%s", pThis->pszOutput);
5799 if (RT_FAILURE(rc))
5800 vbcppError(pThis, "ScmStreamWriteToFile failed with %Rrc when writing '%s'", rc, pThis->pszOutput);
5801 }
5802 else
5803 {
5804 int rc = ScmStreamWriteToStdOut(&pThis->StrmOutput);
5805 if (RT_FAILURE(rc))
5806 vbcppError(pThis, "ScmStreamWriteToStdOut failed with %Rrc", rc);
5807 }
5808 }
5809
5810 /*
5811 * Cleanup.
5812 */
5813 while (pThis->pInputStack)
5814 {
5815 ScmStreamDelete(&pThis->pInputStack->StrmInput);
5816 void *pvFree = pThis->pInputStack;
5817 pThis->pInputStack = pThis->pInputStack->pUp;
5818 RTMemFree(pvFree);
5819 }
5820
5821 ScmStreamDelete(&pThis->StrmOutput);
5822
5823 RTStrSpaceDestroy(&pThis->StrSpace, vbcppMacroFree, NULL);
5824 pThis->StrSpace = NULL;
5825
5826 uint32_t i = pThis->cIncludes;
5827 while (i-- > 0)
5828 RTStrFree(pThis->papszIncludes[i]);
5829 RTMemFree(pThis->papszIncludes);
5830 pThis->papszIncludes = NULL;
5831
5832 return pThis->rcExit;
5833}
5834
5835
5836/**
5837 * Initializes the C preprocessor instance data.
5838 *
5839 * @param pThis The C preprocessor instance data.
5840 */
5841static void vbcppInit(PVBCPP pThis)
5842{
5843 vbcppSetMode(pThis, kVBCppMode_Selective);
5844 pThis->cIncludes = 0;
5845 pThis->papszIncludes = NULL;
5846 pThis->pszInput = NULL;
5847 pThis->pszOutput = NULL;
5848 pThis->StrSpace = NULL;
5849 pThis->UndefStrSpace = NULL;
5850 pThis->cCondStackDepth = 0;
5851 pThis->pCondStack = NULL;
5852 pThis->fIf0Mode = false;
5853 pThis->fJustDroppedLine = false;
5854 pThis->fMaybePreprocessorLine = true;
5855 VBCPP_BITMAP_EMPTY(pThis->bmDefined);
5856 pThis->cCondStackDepth = 0;
5857 pThis->pInputStack = NULL;
5858 RT_ZERO(pThis->StrmOutput);
5859 pThis->rcExit = RTEXITCODE_SUCCESS;
5860 pThis->fStrmOutputValid = false;
5861}
5862
5863
5864
5865int main(int argc, char **argv)
5866{
5867 int rc = RTR3InitExe(argc, &argv, 0);
5868 if (RT_FAILURE(rc))
5869 return RTMsgInitFailure(rc);
5870
5871 /*
5872 * Do the job. The code says it all.
5873 */
5874 VBCPP This;
5875 vbcppInit(&This);
5876 bool fExit;
5877 RTEXITCODE rcExit = vbcppParseOptions(&This, argc, argv, &fExit);
5878 if (!fExit && rcExit == RTEXITCODE_SUCCESS)
5879 {
5880 rcExit = vbcppOpenStreams(&This);
5881 if (rcExit == RTEXITCODE_SUCCESS)
5882 rcExit = vbcppPreprocess(&This);
5883 }
5884
5885 if (rcExit == RTEXITCODE_SUCCESS)
5886 rcExit = vbcppTerm(&This);
5887 else
5888 vbcppTerm(&This);
5889 return rcExit;
5890}
5891
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