VirtualBox

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

Last change on this file was 103384, checked in by vboxsync, 3 months ago

VBoxCPP: Fixes two bugs found by parfait. bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 194.5 KB
Line 
1/* $Id: VBoxCPP.cpp 103384 2024-02-15 12:10:06Z 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-2023 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 rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
2120 }
2121 else if (ch == '\'')
2122 {
2123 /* Character constant. */
2124 pszSrcSeq = pszSrc - 1;
2125 while ((ch = *pszSrc++) != '\'')
2126 {
2127 if (ch == '\\')
2128 ch = *pszSrc++;
2129 if (ch == '\0')
2130 {
2131 rcExit = vbcppError(pThis, "Character constant is missing closing quote (').");
2132 break;
2133 }
2134 }
2135 rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
2136 }
2137 else if (RT_C_IS_DIGIT(ch))
2138 {
2139 /* Process numerical constants correctly (i.e. don't mess with the suffix). */
2140 pszSrcSeq = pszSrc - 1;
2141 while ( (ch = *pszSrc) != '\0'
2142 && ( vbcppIsCIdentifierChar(ch)
2143 || ch == '.') )
2144 pszSrc++;
2145 rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
2146 }
2147 else if (RT_C_IS_SPACE(ch))
2148 {
2149 /* join spaces */
2150 if (RT_C_IS_SPACE(vbcppStrBufLastCh(pStrBuf)))
2151 continue;
2152 rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
2153 }
2154 else if (vbcppIsCIdentifierLeadChar(ch))
2155 {
2156 /* Something we should replace? */
2157 pszSrcSeq = pszSrc - 1;
2158 while ( (ch = *pszSrc) != '\0'
2159 && vbcppIsCIdentifierChar(ch))
2160 pszSrc++;
2161 size_t cchDefine = pszSrc - pszSrcSeq;
2162 uint32_t iArg;
2163 if ( VBCPP_BITMAP_IS_SET(pMacro->bmArgs, *pszSrcSeq)
2164 && (iArg = vbcppMacroLookupArg(pMacro, pszSrcSeq, cchDefine)) != UINT32_MAX)
2165 {
2166 /** @todo check out spaces here! */
2167 if (iArg < pMacro->cArgs)
2168 {
2169 Assert(iArg < pExp->cArgs);
2170 rcExit = vbcppStrBufAppend(pStrBuf, pExp->papszArgs[iArg]);
2171 if (*pExp->papszArgs[iArg] != '\0' && rcExit == RTEXITCODE_SUCCESS)
2172 rcExit = vbcppStrBufAppendCh(pStrBuf, ' ');
2173 }
2174 else
2175 {
2176 /* __VA_ARGS__ */
2177 if (iArg < pExp->cArgs)
2178 {
2179 for (;;)
2180 {
2181 rcExit = vbcppStrBufAppend(pStrBuf, pExp->papszArgs[iArg]);
2182 if (rcExit != RTEXITCODE_SUCCESS)
2183 break;
2184 iArg++;
2185 if (iArg >= pExp->cArgs)
2186 break;
2187 rcExit = vbcppStrBufAppendCh(pStrBuf, ',');
2188 if (rcExit != RTEXITCODE_SUCCESS)
2189 break;
2190 }
2191 }
2192 if (rcExit == RTEXITCODE_SUCCESS)
2193 rcExit = vbcppStrBufAppendCh(pStrBuf, ' ');
2194 }
2195 }
2196 /* Not an argument needing replacing. */
2197 else
2198 rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, cchDefine);
2199 }
2200 else
2201 {
2202 rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
2203 }
2204 }
2205
2206 return rcExit;
2207}
2208
2209
2210
2211/**
2212 * Expands the given macro.
2213 *
2214 * Caller already checked if a function macro should be expanded, i.e. whether
2215 * there is a parameter list.
2216 *
2217 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2218 * @param pThis The C preprocessor instance.
2219 * @param pExp The expansion context.
2220 * @param offMacro Offset into the expansion buffer of the macro
2221 * invocation.
2222 * @param pMacro The macro.
2223 * @param offParameters The start of the parameter list if applicable.
2224 * Ignored if not function macro. If the
2225 * parameter list starts at the current stream
2226 * position shall be at the end of the expansion
2227 * buffer.
2228 */
2229static RTEXITCODE vbcppMacroExpandIt(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offMacro, PVBCPPMACRO pMacro,
2230 size_t offParameters)
2231{
2232 RTEXITCODE rcExit;
2233 Assert(offMacro + pMacro->Core.cchString <= pExp->StrBuf.cchBuf);
2234 Assert(!pMacro->fExpanding);
2235
2236 /*
2237 * Function macros are kind of difficult...
2238 */
2239 if (pMacro->fFunction)
2240 {
2241 rcExit = vbcppMacroExpandGatherParameters(pThis, pExp, &offParameters, pMacro->cArgs + pMacro->fVarArg);
2242 if (rcExit == RTEXITCODE_SUCCESS)
2243 {
2244 if (pExp->cArgs > pMacro->cArgs && !pMacro->fVarArg)
2245 rcExit = vbcppError(pThis, "Too many arguments to macro '%s' - found %u, expected %u",
2246 pMacro->Core.pszString, pExp->cArgs, pMacro->cArgs);
2247 else if (pExp->cArgs < pMacro->cArgs)
2248 rcExit = vbcppError(pThis, "Too few arguments to macro '%s' - found %u, expected %u",
2249 pMacro->Core.pszString, pExp->cArgs, pMacro->cArgs);
2250 }
2251 if (rcExit == RTEXITCODE_SUCCESS)
2252 {
2253 VBCPPSTRBUF ValueBuf;
2254 vbcppStrBufInit(&ValueBuf, pThis);
2255 rcExit = vbcppMacroExpandValueWithArguments(pThis, pExp, pMacro, &ValueBuf);
2256 if (rcExit == RTEXITCODE_SUCCESS)
2257 rcExit = vbcppMacroExpandReplace(pThis, pExp, offMacro, offParameters - offMacro,
2258 ValueBuf.pszBuf, ValueBuf.cchBuf);
2259 vbcppStrBufDelete(&ValueBuf);
2260 }
2261 }
2262 /*
2263 * Object-like macros are easy. :-)
2264 */
2265 else
2266 rcExit = vbcppMacroExpandReplace(pThis, pExp, offMacro, pMacro->Core.cchString, pMacro->szValue, pMacro->cchValue);
2267 if (rcExit == RTEXITCODE_SUCCESS)
2268 {
2269#if 0 /* wrong */
2270 /*
2271 * Push the macro onto the stack.
2272 */
2273 pMacro->fExpanding = true;
2274 pMacro->pUpExpanding = pExp->pMacroStack;
2275 pExp->pMacroStack = pMacro;
2276#endif
2277 }
2278
2279 return rcExit;
2280}
2281
2282
2283/**
2284 * Looks for a left parenthesis in the macro expansion buffer and the input
2285 * stream.
2286 *
2287 * @retval true if found. The stream position at opening parenthesis.
2288 * @retval false if not found. The stream position is unchanged.
2289 *
2290 * @param pThis The C preprocessor instance.
2291 * @param pExp The expansion context.
2292 * @param poff The current offset in the expansion context.
2293 * Will be updated on success.
2294 *
2295 * @sa vbcppInputLookForLeftParenthesis
2296 */
2297static bool vbcppMacroExpandLookForLeftParenthesis(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
2298{
2299 /*
2300 * Search the buffer first. (No comments there.)
2301 */
2302 size_t off = *poff;
2303 while (off < pExp->StrBuf.cchBuf)
2304 {
2305 char ch = pExp->StrBuf.pszBuf[off];
2306 if (!RT_C_IS_SPACE(ch))
2307 {
2308 if (ch == '(')
2309 {
2310 *poff = off;
2311 return true;
2312 }
2313 return false;
2314 }
2315 off++;
2316 }
2317
2318 /*
2319 * Reached the end of the buffer, continue searching in the stream.
2320 */
2321 PSCMSTREAM pStrmInput = pExp->pStrmInput;
2322 size_t offSaved = ScmStreamTell(pStrmInput);
2323 /*RTEXITCODE rcExit = */ vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
2324 unsigned ch = ScmStreamPeekCh(pStrmInput);
2325 if (ch == '(')
2326 {
2327 *poff = pExp->StrBuf.cchBuf;
2328 return true;
2329 }
2330
2331 int rc = ScmStreamSeekAbsolute(pStrmInput, offSaved);
2332 AssertFatalRC(rc);
2333 return false;
2334}
2335
2336
2337/**
2338 * Implements the 'defined' unary operator for \#if and \#elif expressions.
2339 *
2340 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2341 * @param pThis The C preprocessor instance.
2342 * @param pExp The expansion context.
2343 * @param offStart The expansion buffer offset where the 'defined'
2344 * occurs.
2345 * @param poff Where to store the offset at which the re-scan
2346 * shall resume upon return.
2347 */
2348static RTEXITCODE vbcppMacroExpandDefinedOperator(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offStart, size_t *poff)
2349{
2350 Assert(!pExp->pStrmInput); /* offset usage below. */
2351
2352 /*
2353 * Skip white space.
2354 */
2355 unsigned ch;
2356 while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
2357 if (!RT_C_IS_SPACE(ch))
2358 break;
2359 bool const fWithParenthesis = ch == '(';
2360 if (fWithParenthesis)
2361 while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
2362 if (!RT_C_IS_SPACE(ch))
2363 break;
2364
2365 /*
2366 * Macro identifier.
2367 */
2368 if (!vbcppIsCIdentifierLeadChar(ch))
2369 return vbcppError(pThis, "Expected macro name after 'defined' operator");
2370
2371 size_t const offDefine = *poff - 1;
2372 while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
2373 if (!vbcppIsCIdentifierChar(ch))
2374 break;
2375 size_t const cchDefine = *poff - offDefine - 1;
2376
2377 /*
2378 * Check for closing parenthesis.
2379 */
2380 if (fWithParenthesis)
2381 {
2382 while (RT_C_IS_SPACE(ch))
2383 ch = vbcppMacroExpandGetCh(pExp, poff);
2384 if (ch != ')')
2385 return vbcppError(pThis, "Expected closing parenthesis after macro name");
2386 }
2387
2388 /*
2389 * Do the job.
2390 */
2391 const char *pszResult = vbcppMacroExists(pThis, &pExp->StrBuf.pszBuf[offDefine], cchDefine)
2392 ? "1" : "0";
2393 RTEXITCODE rcExit = vbcppMacroExpandReplace(pThis, pExp, offStart, *poff - offStart, pszResult, 1);
2394 *poff = offStart + 1;
2395 return rcExit;
2396}
2397
2398
2399/**
2400 * Re-scan the expanded macro.
2401 *
2402 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2403 * @param pThis The C preprocessor instance.
2404 * @param pExp The expansion context.
2405 * @param enmMode The re-scan mode.
2406 * @param pcReplacements Where to return the number of replacements
2407 * performed. Optional.
2408 * @param pcDefinedUnknown Where to return the number of defined() checks
2409 * on unknown macros. Optional.
2410 */
2411static RTEXITCODE vbcppMacroExpandReScan(PVBCPP pThis, PVBCPPMACROEXP pExp, VBCPPMACRORESCANMODE enmMode,
2412 size_t *pcReplacements, size_t *pcDefinedUnknown)
2413{
2414 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2415 size_t cReplacements = 0;
2416 size_t cDefinedUnknown = 0;
2417 size_t off = 0;
2418 unsigned ch;
2419 while ( off < pExp->StrBuf.cchBuf
2420 && (ch = vbcppMacroExpandGetCh(pExp, &off)) != ~(unsigned)0)
2421 {
2422 /*
2423 * String litteral or character constant.
2424 */
2425 if (ch == '\'' || ch == '"')
2426 {
2427 unsigned const chEndQuote = ch;
2428 while ( off < pExp->StrBuf.cchBuf
2429 && (ch = vbcppMacroExpandGetCh(pExp, &off)) != ~(unsigned)0)
2430 {
2431 if (ch == '\\')
2432 {
2433 ch = vbcppMacroExpandGetCh(pExp, &off);
2434 if (ch == ~(unsigned)0)
2435 break;
2436 }
2437 else if (ch == chEndQuote)
2438 break;
2439 }
2440 if (ch == ~(unsigned)0)
2441 return vbcppError(pThis, "Missing end quote (%c)", chEndQuote);
2442 }
2443 /*
2444 * Number constant.
2445 */
2446 else if ( RT_C_IS_DIGIT(ch)
2447 || ( ch == '.'
2448 && off + 1 < pExp->StrBuf.cchBuf
2449 && RT_C_IS_DIGIT(vbcppMacroExpandPeekCh(pExp, &off))
2450 )
2451 )
2452 {
2453 while ( off < pExp->StrBuf.cchBuf
2454 && (ch = vbcppMacroExpandPeekCh(pExp, &off)) != ~(unsigned)0
2455 && vbcppIsCIdentifierChar(ch) )
2456 vbcppMacroExpandGetCh(pExp, &off);
2457 }
2458 /*
2459 * Something that can be replaced?
2460 */
2461 else if (vbcppIsCIdentifierLeadChar(ch))
2462 {
2463 size_t offDefine = off - 1;
2464 while ( off < pExp->StrBuf.cchBuf
2465 && (ch = vbcppMacroExpandPeekCh(pExp, &off)) != ~(unsigned)0
2466 && vbcppIsCIdentifierChar(ch) )
2467 vbcppMacroExpandGetCh(pExp, &off);
2468 size_t cchDefine = off - offDefine;
2469
2470 PVBCPPMACRO pMacro = vbcppMacroLookup(pThis, &pExp->StrBuf.pszBuf[offDefine], cchDefine);
2471 if ( pMacro
2472 && ( !pMacro->fFunction
2473 || vbcppMacroExpandLookForLeftParenthesis(pThis, pExp, &off)) )
2474 {
2475 cReplacements++;
2476 rcExit = vbcppMacroExpandIt(pThis, pExp, offDefine, pMacro, off);
2477 off = offDefine;
2478 }
2479 else
2480 {
2481 if ( !pMacro
2482 && enmMode == kMacroReScanMode_Expression
2483 && cchDefine == sizeof("defined") - 1
2484 && !strncmp(&pExp->StrBuf.pszBuf[offDefine], "defined", cchDefine))
2485 {
2486 cReplacements++;
2487 cDefinedUnknown++;
2488 rcExit = vbcppMacroExpandDefinedOperator(pThis, pExp, offDefine, &off);
2489 }
2490 else
2491 off = offDefine + cchDefine;
2492 }
2493 }
2494 else
2495 {
2496 Assert(RT_C_IS_SPACE(ch) || RT_C_IS_PUNCT(ch));
2497 Assert(ch != '\r' && ch != '\n');
2498 }
2499 }
2500
2501 if (pcReplacements)
2502 *pcReplacements = cReplacements;
2503 if (pcDefinedUnknown)
2504 *pcDefinedUnknown = cDefinedUnknown;
2505 return rcExit;
2506}
2507
2508
2509/**
2510 * Cleans up the expansion context.
2511 *
2512 * This involves clearing VBCPPMACRO::fExpanding and VBCPPMACRO::pUpExpanding,
2513 * and freeing the memory resources associated with the expansion context.
2514 *
2515 * @param pExp The expansion context.
2516 */
2517static void vbcppMacroExpandCleanup(PVBCPPMACROEXP pExp)
2518{
2519#if 0
2520 while (pExp->pMacroStack)
2521 {
2522 PVBCPPMACRO pMacro = pExp->pMacroStack;
2523 pExp->pMacroStack = pMacro->pUpExpanding;
2524
2525 pMacro->fExpanding = false;
2526 pMacro->pUpExpanding = NULL;
2527 }
2528#endif
2529
2530 while (pExp->cArgs > 0)
2531 {
2532 RTMemFree(pExp->papszArgs[--pExp->cArgs]);
2533 pExp->papszArgs[pExp->cArgs] = NULL;
2534 }
2535
2536 RTMemFree(pExp->papszArgs);
2537 pExp->papszArgs = NULL;
2538
2539 vbcppStrBufDelete(&pExp->StrBuf);
2540}
2541
2542
2543
2544/**
2545 * Frees a define.
2546 *
2547 * @returns VINF_SUCCESS (used when called by RTStrSpaceDestroy)
2548 * @param pStr Pointer to the VBCPPMACRO::Core member.
2549 * @param pvUser Unused.
2550 */
2551static DECLCALLBACK(int) vbcppMacroFree(PRTSTRSPACECORE pStr, void *pvUser)
2552{
2553 RTMemFree(pStr);
2554 NOREF(pvUser);
2555 return VINF_SUCCESS;
2556}
2557
2558
2559/**
2560 * Removes a define.
2561 *
2562 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2563 * @param pThis The C preprocessor instance.
2564 * @param pszDefine The define name, no argument list or anything.
2565 * @param cchDefine The length of the name. RTSTR_MAX is ok.
2566 * @param fExplicitUndef Explicit undefinition, that is, in a selective
2567 * preprocessing run it will evaluate to undefined.
2568 */
2569static RTEXITCODE vbcppMacroUndef(PVBCPP pThis, const char *pszDefine, size_t cchDefine, bool fExplicitUndef)
2570{
2571 PRTSTRSPACECORE pHit = RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine);
2572 if (pHit)
2573 {
2574 RTStrSpaceRemove(&pThis->StrSpace, pHit->pszString);
2575 vbcppMacroFree(pHit, NULL);
2576 }
2577
2578 if (fExplicitUndef)
2579 {
2580 if (cchDefine == RTSTR_MAX)
2581 cchDefine = strlen(pszDefine);
2582
2583 PRTSTRSPACECORE pStr = (PRTSTRSPACECORE)RTMemAlloc(sizeof(*pStr) + cchDefine + 1);
2584 if (!pStr)
2585 return vbcppError(pThis, "out of memory");
2586 char *pszDst = (char *)(pStr + 1);
2587 pStr->pszString = pszDst;
2588 memcpy(pszDst, pszDefine, cchDefine);
2589 pszDst[cchDefine] = '\0';
2590 if (!RTStrSpaceInsert(&pThis->UndefStrSpace, pStr))
2591 RTMemFree(pStr);
2592 }
2593
2594 return RTEXITCODE_SUCCESS;
2595}
2596
2597
2598/**
2599 * Inserts a define (rejecting and freeing it in some case).
2600 *
2601 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2602 * @param pThis The C preprocessor instance.
2603 * @param pMacro The define to insert.
2604 */
2605static RTEXITCODE vbcppMacroInsert(PVBCPP pThis, PVBCPPMACRO pMacro)
2606{
2607 /*
2608 * Reject illegal macro names.
2609 */
2610 if (!strcmp(pMacro->Core.pszString, "defined"))
2611 {
2612 RTEXITCODE rcExit = vbcppError(pThis, "Cannot use '%s' as a macro name", pMacro->Core.pszString);
2613 vbcppMacroFree(&pMacro->Core, NULL);
2614 return rcExit;
2615 }
2616
2617 /*
2618 * Ignore in source-file defines when doing selective preprocessing.
2619 */
2620 if ( !pThis->fRespectSourceDefines
2621 && !pMacro->fCmdLine)
2622 {
2623 /* Ignore*/
2624 vbcppMacroFree(&pMacro->Core, NULL);
2625 return RTEXITCODE_SUCCESS;
2626 }
2627
2628 /*
2629 * Insert it and update the lead character hint bitmap.
2630 */
2631 if (RTStrSpaceInsert(&pThis->StrSpace, &pMacro->Core))
2632 VBCPP_BITMAP_SET(pThis->bmDefined, *pMacro->Core.pszString);
2633 else
2634 {
2635 /*
2636 * Duplicate. When doing selective D preprocessing, let the command
2637 * line take precendece.
2638 */
2639 PVBCPPMACRO pOld = (PVBCPPMACRO)RTStrSpaceGet(&pThis->StrSpace, pMacro->Core.pszString); Assert(pOld);
2640 if ( pThis->fAllowRedefiningCmdLineDefines
2641 || pMacro->fCmdLine == pOld->fCmdLine)
2642 {
2643 if (pMacro->fCmdLine)
2644 RTMsgWarning("Redefining '%s'", pMacro->Core.pszString);
2645
2646 RTStrSpaceRemove(&pThis->StrSpace, pOld->Core.pszString);
2647 vbcppMacroFree(&pOld->Core, NULL);
2648
2649 bool fRc = RTStrSpaceInsert(&pThis->StrSpace, &pMacro->Core);
2650 Assert(fRc); NOREF(fRc);
2651 }
2652 else
2653 {
2654 RTMsgWarning("Ignoring redefinition of '%s'", pMacro->Core.pszString);
2655 vbcppMacroFree(&pMacro->Core, NULL);
2656 }
2657 }
2658
2659 return RTEXITCODE_SUCCESS;
2660}
2661
2662
2663/**
2664 * Adds a define.
2665 *
2666 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2667 * @param pThis The C preprocessor instance.
2668 * @param pszDefine The define name, no parameter list.
2669 * @param cchDefine The length of the name.
2670 * @param pszParams The parameter list.
2671 * @param cchParams The length of the parameter list.
2672 * @param pszValue The value.
2673 * @param cchDefine The length of the value.
2674 * @param fCmdLine Set if originating on the command line.
2675 */
2676static RTEXITCODE vbcppMacroAddFn(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
2677 const char *pszParams, size_t cchParams,
2678 const char *pszValue, size_t cchValue,
2679 bool fCmdLine)
2680
2681{
2682 Assert(RTStrNLen(pszDefine, cchDefine) == cchDefine);
2683 Assert(RTStrNLen(pszParams, cchParams) == cchParams);
2684 Assert(RTStrNLen(pszValue, cchValue) == cchValue);
2685
2686 /*
2687 * Determin the number of arguments and how much space their names
2688 * requires. Performing syntax validation while parsing.
2689 */
2690 bool fVariadic = false;
2691 size_t cchArgNames = 0;
2692 uint32_t cArgs = 0;
2693 for (size_t off = 0; off < cchParams; off++)
2694 {
2695 /* Skip blanks and maybe one comma. */
2696 bool fIgnoreComma = cArgs != 0;
2697 while (off < cchParams)
2698 {
2699 if ( !RT_C_IS_SPACE(pszParams[off])
2700 && ( pszParams[off] != '\\'
2701 || (pszParams[off + 1] != '\n' && pszParams[off + 1] != '\r')))
2702 {
2703 if (pszParams[off] != ',' || !fIgnoreComma)
2704 {
2705 if (vbcppIsCIdentifierLeadChar(pszParams[off]))
2706 break;
2707
2708 /* Check for variadic macro argument, just parsing it for now. */
2709 if ( pszParams[off] == '.'
2710 && off + 3 <= cchParams
2711 && pszParams[off + 1] == '.'
2712 && pszParams[off + 2] == '.')
2713 {
2714 size_t const offSaved = off;
2715 off += 3;
2716 while (off < cchParams && RT_C_IS_SPACE(pszParams[off]))
2717 off++;
2718 if (off == cchParams)
2719 {
2720 cArgs++;
2721 cchArgNames += sizeof("...");
2722 fVariadic = true;
2723 break;
2724 }
2725 off = offSaved;
2726 }
2727 return vbcppErrorPos(pThis, &pszParams[off], "Unexpected character (%#x, off=%zu)", pszParams[off], off);
2728 }
2729 fIgnoreComma = false;
2730 }
2731 off++;
2732 }
2733 if (off >= cchParams)
2734 break;
2735
2736 /* Found and argument. First character is already validated. */
2737 cArgs++;
2738 cchArgNames += 2;
2739 off++;
2740 while ( off < cchParams
2741 && vbcppIsCIdentifierChar(pszParams[off]))
2742 off++, cchArgNames++;
2743 }
2744
2745 /*
2746 * Allocate a structure.
2747 */
2748 size_t cbDef = RT_UOFFSETOF_DYN(VBCPPMACRO, szValue[cchValue + 1 + cchDefine + 1 + cchArgNames])
2749 + sizeof(const char *) * cArgs;
2750 cbDef = RT_ALIGN_Z(cbDef, sizeof(const char *));
2751 PVBCPPMACRO pMacro = (PVBCPPMACRO)RTMemAlloc(cbDef);
2752 if (!pMacro)
2753 return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
2754
2755 char *pszDst = &pMacro->szValue[cchValue + 1];
2756 pMacro->Core.pszString = pszDst;
2757 memcpy(pszDst, pszDefine, cchDefine);
2758 pszDst += cchDefine;
2759 *pszDst++ = '\0';
2760 pMacro->fFunction = true;
2761 pMacro->fVarArg = fVariadic;
2762 pMacro->fCmdLine = fCmdLine;
2763 pMacro->fExpanding = false;
2764 pMacro->cArgs = cArgs;
2765 pMacro->papszArgs = (const char **)((uintptr_t)pMacro + cbDef - sizeof(const char *) * cArgs);
2766 VBCPP_BITMAP_EMPTY(pMacro->bmArgs);
2767 pMacro->cchValue = cchValue;
2768 memcpy(pMacro->szValue, pszValue, cchValue);
2769 pMacro->szValue[cchValue] = '\0';
2770
2771 /*
2772 * Set up the arguments.
2773 */
2774 uint32_t iArg = 0;
2775 for (size_t off = 0; off < cchParams; off++)
2776 {
2777 /* Skip blanks and maybe one comma. */
2778 bool fIgnoreComma = cArgs != 0;
2779 while (off < cchParams)
2780 {
2781 if ( !RT_C_IS_SPACE(pszParams[off])
2782 && ( pszParams[off] != '\\'
2783 || (pszParams[off + 1] != '\n' && pszParams[off + 1] != '\r')))
2784 {
2785 if (pszParams[off] != ',' || !fIgnoreComma)
2786 break;
2787 fIgnoreComma = false;
2788 }
2789 off++;
2790 }
2791 if (off >= cchParams)
2792 break;
2793
2794 /* Found and argument. First character is already validated. */
2795 pMacro->papszArgs[iArg] = pszDst;
2796 if (!fVariadic || iArg + 1 < cArgs)
2797 {
2798 VBCPP_BITMAP_SET(pMacro->bmArgs, pszParams[off]);
2799 do
2800 {
2801 *pszDst++ = pszParams[off++];
2802 } while ( off < cchParams
2803 && vbcppIsCIdentifierChar(pszParams[off]));
2804 }
2805 else
2806 {
2807 Assert(strncmp(&pszParams[off], "...", 3) == 0);
2808 VBCPP_BITMAP_SET(pMacro->bmArgs, '_'); /* __VA_ARGS__ */
2809 off += 3;
2810 *pszDst++ = '.';
2811 *pszDst++ = '.';
2812 *pszDst++ = '.';
2813 }
2814 *pszDst++ = '\0';
2815 iArg++;
2816 }
2817 Assert((uintptr_t)pszDst <= (uintptr_t)pMacro->papszArgs);
2818
2819 return vbcppMacroInsert(pThis, pMacro);
2820}
2821
2822
2823/**
2824 * Adds a define.
2825 *
2826 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
2827 * @param pThis The C preprocessor instance.
2828 * @param pszDefine The define name and optionally the argument
2829 * list.
2830 * @param cchDefine The length of the name. RTSTR_MAX is ok.
2831 * @param pszValue The value.
2832 * @param cchDefine The length of the value. RTSTR_MAX is ok.
2833 * @param fCmdLine Set if originating on the command line.
2834 */
2835static RTEXITCODE vbcppMacroAdd(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
2836 const char *pszValue, size_t cchValue, bool fCmdLine)
2837{
2838 /*
2839 * We need the lengths. Trim the input.
2840 */
2841 if (cchDefine == RTSTR_MAX)
2842 cchDefine = strlen(pszDefine);
2843 while (cchDefine > 0 && RT_C_IS_SPACE(*pszDefine))
2844 pszDefine++, cchDefine--;
2845 while (cchDefine > 0 && RT_C_IS_SPACE(pszDefine[cchDefine - 1]))
2846 cchDefine--;
2847 if (!cchDefine)
2848 return vbcppErrorPos(pThis, pszDefine, "The define has no name");
2849
2850 if (cchValue == RTSTR_MAX)
2851 cchValue = strlen(pszValue);
2852 while (cchValue > 0 && RT_C_IS_SPACE(*pszValue))
2853 pszValue++, cchValue--;
2854 while (cchValue > 0 && RT_C_IS_SPACE(pszValue[cchValue - 1]))
2855 cchValue--;
2856
2857 /*
2858 * Arguments make the job a bit more annoying. Handle that elsewhere
2859 */
2860 const char *pszParams = (const char *)memchr(pszDefine, '(', cchDefine);
2861 if (pszParams)
2862 {
2863 size_t cchParams = pszDefine + cchDefine - pszParams;
2864 cchDefine -= cchParams;
2865 if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
2866 return RTEXITCODE_FAILURE;
2867 if (pszParams[cchParams - 1] != ')')
2868 return vbcppErrorPos(pThis, pszParams + cchParams - 1, "Missing closing parenthesis");
2869 pszParams++;
2870 cchParams -= 2;
2871 return vbcppMacroAddFn(pThis, pszDefine, cchDefine, pszParams, cchParams, pszValue, cchValue, fCmdLine);
2872 }
2873
2874 /*
2875 * Simple define, no arguments.
2876 */
2877 if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
2878 return RTEXITCODE_FAILURE;
2879
2880 PVBCPPMACRO pMacro = (PVBCPPMACRO)RTMemAlloc(RT_UOFFSETOF_DYN(VBCPPMACRO, szValue[cchValue + 1 + cchDefine + 1]));
2881 if (!pMacro)
2882 return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
2883
2884 pMacro->Core.pszString = &pMacro->szValue[cchValue + 1];
2885 memcpy((char *)pMacro->Core.pszString, pszDefine, cchDefine);
2886 ((char *)pMacro->Core.pszString)[cchDefine] = '\0';
2887 pMacro->fFunction = false;
2888 pMacro->fVarArg = false;
2889 pMacro->fCmdLine = fCmdLine;
2890 pMacro->fExpanding = false;
2891 pMacro->cArgs = 0;
2892 pMacro->papszArgs = NULL;
2893 VBCPP_BITMAP_EMPTY(pMacro->bmArgs);
2894 pMacro->cchValue = cchValue;
2895 memcpy(pMacro->szValue, pszValue, cchValue);
2896 pMacro->szValue[cchValue] = '\0';
2897
2898 return vbcppMacroInsert(pThis, pMacro);
2899}
2900
2901
2902/**
2903 * Tries to convert a define into an inline D constant.
2904 *
2905 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2906 * @param pThis The C preprocessor instance.
2907 * @param pMacro The macro.
2908 */
2909static RTEXITCODE vbcppMacroTryConvertToInlineD(PVBCPP pThis, PVBCPPMACRO pMacro)
2910{
2911 AssertReturn(pMacro, vbcppError(pThis, "Internal error"));
2912 if (pMacro->fFunction)
2913 return RTEXITCODE_SUCCESS;
2914
2915 /*
2916 * Do some simple macro resolving. (Mostly to make x86.h work.)
2917 */
2918 const char *pszDefine = pMacro->Core.pszString;
2919 const char *pszValue = pMacro->szValue;
2920 size_t cchValue = pMacro->cchValue;
2921
2922 unsigned i = 0;
2923 PVBCPPMACRO pMacro2;
2924 while ( i < 10
2925 && cchValue > 0
2926 && vbcppIsCIdentifierLeadChar(*pszValue)
2927 && (pMacro2 = vbcppMacroLookup(pThis, pszValue, cchValue)) != NULL
2928 && !pMacro2->fFunction )
2929 {
2930 pszValue = pMacro2->szValue;
2931 cchValue = pMacro2->cchValue;
2932 i++;
2933 }
2934
2935 if (!pMacro->cchValue)
2936 return RTEXITCODE_SUCCESS;
2937
2938
2939 /*
2940 * A lone value?
2941 */
2942 ssize_t cch = 0;
2943 uint64_t u64;
2944 char *pszNext;
2945 int rc = RTStrToUInt64Ex(pszValue, &pszNext, 0, &u64);
2946 if (RT_SUCCESS(rc))
2947 {
2948 if ( rc == VWRN_TRAILING_SPACES
2949 || rc == VWRN_NEGATIVE_UNSIGNED
2950 || rc == VWRN_NUMBER_TOO_BIG)
2951 return RTEXITCODE_SUCCESS;
2952 const char *pszType;
2953 if (rc == VWRN_TRAILING_CHARS)
2954 {
2955 if (!strcmp(pszNext, "u") || !strcmp(pszNext, "U"))
2956 pszType = "uint32_t";
2957 else if (!strcmp(pszNext, "ul") || !strcmp(pszNext, "UL"))
2958 pszType = "uintptr_t";
2959 else if (!strcmp(pszNext, "ull") || !strcmp(pszNext, "ULL"))
2960 pszType = "uint64_t";
2961 else
2962 pszType = NULL;
2963 }
2964 else if (u64 <= UINT8_MAX)
2965 pszType = "uint8_t";
2966 else if (u64 <= UINT16_MAX)
2967 pszType = "uint16_t";
2968 else if (u64 <= UINT32_MAX)
2969 pszType = "uint32_t";
2970 else
2971 pszType = "uint64_t";
2972 if (!pszType)
2973 return RTEXITCODE_SUCCESS;
2974 cch = ScmStreamPrintf(&pThis->StrmOutput, "inline %s %s = %.*s;\n",
2975 pszType, pszDefine, pszNext - pszValue, pszValue);
2976 }
2977 /*
2978 * A value wrapped in a constant macro?
2979 */
2980 else if ( (pszNext = (char *)strchr(pszValue, '(')) != NULL
2981 && pszValue[cchValue - 1] == ')' )
2982 {
2983 size_t cchPrefix = pszNext - pszValue;
2984 size_t cchInnerValue = cchValue - cchPrefix - 2;
2985 const char *pchInnerValue = &pszValue[cchPrefix + 1];
2986 while (cchInnerValue > 0 && RT_C_IS_SPACE(*pchInnerValue))
2987 cchInnerValue--, pchInnerValue++;
2988 while (cchInnerValue > 0 && RT_C_IS_SPACE(pchInnerValue[cchInnerValue - 1]))
2989 cchInnerValue--;
2990 if (!cchInnerValue || !RT_C_IS_XDIGIT(*pchInnerValue))
2991 return RTEXITCODE_SUCCESS;
2992
2993 rc = RTStrToUInt64Ex(pchInnerValue, &pszNext, 0, &u64);
2994 if ( RT_FAILURE(rc)
2995 || rc == VWRN_TRAILING_SPACES
2996 || rc == VWRN_NEGATIVE_UNSIGNED
2997 || rc == VWRN_NUMBER_TOO_BIG)
2998 return RTEXITCODE_SUCCESS;
2999
3000 const char *pszType;
3001#define MY_MATCH_STR(a_sz) (sizeof(a_sz) - 1 == cchPrefix && !strncmp(pszValue, a_sz, sizeof(a_sz) - 1))
3002 if (MY_MATCH_STR("UINT8_C"))
3003 pszType = "uint8_t";
3004 else if (MY_MATCH_STR("UINT16_C"))
3005 pszType = "uint16_t";
3006 else if (MY_MATCH_STR("UINT32_C"))
3007 pszType = "uint32_t";
3008 else if (MY_MATCH_STR("UINT64_C"))
3009 pszType = "uint64_t";
3010 else
3011 pszType = NULL;
3012 if (pszType)
3013 cch = ScmStreamPrintf(&pThis->StrmOutput, "inline %s %s = %.*s;\n",
3014 pszType, pszDefine, cchInnerValue, pchInnerValue);
3015 else if (MY_MATCH_STR("RT_BIT") || MY_MATCH_STR("RT_BIT_32"))
3016 cch = ScmStreamPrintf(&pThis->StrmOutput, "inline uint32_t %s = 1U << %llu;\n",
3017 pszDefine, u64);
3018 else if (MY_MATCH_STR("RT_BIT_64"))
3019 cch = ScmStreamPrintf(&pThis->StrmOutput, "inline uint64_t %s = 1ULL << %llu;\n",
3020 pszDefine, u64);
3021 else
3022 return RTEXITCODE_SUCCESS;
3023#undef MY_MATCH_STR
3024 }
3025 /* Dunno what this is... */
3026 else
3027 return RTEXITCODE_SUCCESS;
3028
3029 /*
3030 * Check for output error and clear the output suppression indicator.
3031 */
3032 if (cch < 0)
3033 return vbcppError(pThis, "Output error");
3034
3035 pThis->fJustDroppedLine = false;
3036 return RTEXITCODE_SUCCESS;
3037}
3038
3039
3040
3041/**
3042 * Processes a abbreviated line number directive.
3043 *
3044 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
3045 * @param pThis The C preprocessor instance.
3046 * @param pStrmInput The input stream.
3047 * @param offStart The stream position where the directive
3048 * started (for pass thru).
3049 */
3050static RTEXITCODE vbcppDirectiveDefine(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
3051{
3052 RT_NOREF_PV(offStart);
3053
3054 /*
3055 * Parse it.
3056 */
3057 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
3058 if (rcExit == RTEXITCODE_SUCCESS)
3059 {
3060 size_t cchDefine;
3061 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
3062 if (pchDefine)
3063 {
3064 /* If it's a function style define, parse out the parameter list. */
3065 size_t cchParams = 0;
3066 const char *pchParams = NULL;
3067 unsigned ch = ScmStreamPeekCh(pStrmInput);
3068 if (ch == '(')
3069 {
3070 ScmStreamGetCh(pStrmInput);
3071 pchParams = ScmStreamGetCur(pStrmInput);
3072
3073 unsigned chPrev = ch;
3074 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
3075 {
3076 if (ch == '\r' || ch == '\n')
3077 {
3078 if (chPrev != '\\')
3079 {
3080 rcExit = vbcppError(pThis, "Missing ')'");
3081 break;
3082 }
3083 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
3084 }
3085 if (ch == ')')
3086 {
3087 cchParams = ScmStreamGetCur(pStrmInput) - pchParams;
3088 ScmStreamGetCh(pStrmInput);
3089 break;
3090 }
3091 chPrev = ch;
3092 ScmStreamGetCh(pStrmInput);
3093 }
3094 }
3095 /* The simple kind. */
3096 else if (!RT_C_IS_SPACE(ch) && ch != ~(unsigned)0)
3097 rcExit = vbcppError(pThis, "Expected whitespace after macro name");
3098
3099 /* Parse out the value. */
3100 if (rcExit == RTEXITCODE_SUCCESS)
3101 rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
3102 if (rcExit == RTEXITCODE_SUCCESS)
3103 {
3104 size_t offValue = ScmStreamTell(pStrmInput);
3105 const char *pchValue = ScmStreamGetCur(pStrmInput);
3106 unsigned chPrev = ch;
3107 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
3108 {
3109 if (ch == '\r' || ch == '\n')
3110 {
3111 if (chPrev != '\\')
3112 break;
3113 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
3114 }
3115 chPrev = ScmStreamGetCh(pStrmInput);
3116 }
3117 size_t cchValue = ScmStreamGetCur(pStrmInput) - pchValue;
3118
3119 /*
3120 * Execute.
3121 */
3122 if (pchParams)
3123 rcExit = vbcppMacroAddFn(pThis, pchDefine, cchDefine, pchParams, cchParams, pchValue, cchValue, false);
3124 else
3125 rcExit = vbcppMacroAdd(pThis, pchDefine, cchDefine, pchValue, cchValue, false);
3126
3127 /*
3128 * Pass thru?
3129 */
3130 if ( rcExit == RTEXITCODE_SUCCESS
3131 && pThis->fPassThruDefines)
3132 {
3133 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
3134 ssize_t cch;
3135 if (pchParams)
3136 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s(%.*s)",
3137 cchIndent, "", cchDefine, pchDefine, cchParams, pchParams);
3138 else
3139 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s",
3140 cchIndent, "", cchDefine, pchDefine);
3141 if (cch > 0)
3142 vbcppOutputComment(pThis, pStrmInput, offValue, cch, 1);
3143 else
3144 rcExit = vbcppError(pThis, "output error");
3145 }
3146 else if ( rcExit == RTEXITCODE_SUCCESS
3147 && pThis->enmMode == kVBCppMode_SelectiveD)
3148 rcExit = vbcppMacroTryConvertToInlineD(pThis, vbcppMacroLookup(pThis, pchDefine, cchDefine));
3149 else
3150 pThis->fJustDroppedLine = true;
3151 }
3152 }
3153 }
3154 return rcExit;
3155}
3156
3157
3158/**
3159 * Processes a abbreviated line number directive.
3160 *
3161 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
3162 * @param pThis The C preprocessor instance.
3163 * @param pStrmInput The input stream.
3164 * @param offStart The stream position where the directive
3165 * started (for pass thru).
3166 */
3167static RTEXITCODE vbcppDirectiveUndef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
3168{
3169 RT_NOREF_PV(offStart);
3170
3171 /*
3172 * Parse it.
3173 */
3174 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
3175 if (rcExit == RTEXITCODE_SUCCESS)
3176 {
3177 size_t cchDefine;
3178 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
3179 if (pchDefine)
3180 {
3181 size_t offMaybeComment = vbcppProcessSkipWhite(pStrmInput);
3182 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
3183 if (rcExit == RTEXITCODE_SUCCESS)
3184 {
3185 /*
3186 * Take action.
3187 */
3188 PVBCPPMACRO pMacro = vbcppMacroLookup(pThis, pchDefine, cchDefine);
3189 if ( pMacro
3190 && pThis->fRespectSourceDefines
3191 && ( !pMacro->fCmdLine
3192 || pThis->fAllowRedefiningCmdLineDefines ) )
3193 {
3194 RTStrSpaceRemove(&pThis->StrSpace, pMacro->Core.pszString);
3195 vbcppMacroFree(&pMacro->Core, NULL);
3196 }
3197
3198 /*
3199 * Pass thru.
3200 */
3201 if ( rcExit == RTEXITCODE_SUCCESS
3202 && pThis->fPassThruDefines)
3203 {
3204 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
3205 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sundef %.*s",
3206 cchIndent, "", cchDefine, pchDefine);
3207 if (cch > 0)
3208 vbcppOutputComment(pThis, pStrmInput, offMaybeComment, cch, 1);
3209 else
3210 rcExit = vbcppError(pThis, "output error");
3211 }
3212
3213 }
3214 }
3215 else
3216 rcExit = vbcppError(pThis, "Malformed #ifndef");
3217 }
3218 return rcExit;
3219
3220}
3221
3222
3223
3224
3225
3226/*
3227 *
3228 *
3229 * C O N D I T I O N A L S
3230 * C O N D I T I O N A L S
3231 * C O N D I T I O N A L S
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 *
3235 *
3236 */
3237
3238
3239/**
3240 * Combines current stack result with the one being pushed.
3241 *
3242 * @returns Combined result.
3243 * @param enmEvalPush The result of the condition being pushed.
3244 * @param enmEvalStack The current stack result.
3245 */
3246static VBCPPEVAL vbcppCondCombine(VBCPPEVAL enmEvalPush, VBCPPEVAL enmEvalStack)
3247{
3248 if (enmEvalStack == kVBCppEval_False)
3249 return kVBCppEval_False;
3250 return enmEvalPush;
3251}
3252
3253
3254/**
3255 * Pushes an conditional onto the stack.
3256 *
3257 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
3258 * @param pThis The C preprocessor instance.
3259 * @param pStrmInput The current input stream.
3260 * @param offStart Not currently used, using @a pchCondition and
3261 * @a cchCondition instead.
3262 * @param enmKind The kind of conditional.
3263 * @param enmResult The result of the evaluation.
3264 * @param pchCondition The raw condition.
3265 * @param cchCondition The length of @a pchCondition.
3266 */
3267static RTEXITCODE vbcppCondPush(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart,
3268 VBCPPCONDKIND enmKind, VBCPPEVAL enmResult,
3269 const char *pchCondition, size_t cchCondition)
3270{
3271 RT_NOREF_PV(offStart); RT_NOREF_PV(pStrmInput);
3272
3273
3274 if (pThis->cCondStackDepth >= _64K)
3275 return vbcppError(pThis, "Too many nested #if/#ifdef/#ifndef statements");
3276
3277 /*
3278 * Allocate a new entry and push it.
3279 */
3280 PVBCPPCOND pCond = (PVBCPPCOND)RTMemAlloc(sizeof(*pCond));
3281 if (!pCond)
3282 return vbcppError(pThis, "out of memory");
3283
3284 PVBCPPCOND pUp = pThis->pCondStack;
3285 pCond->enmKind = enmKind;
3286 pCond->enmResult = enmResult;
3287 pCond->enmStackResult = pUp ? vbcppCondCombine(enmResult, pUp->enmStackResult) : enmResult;
3288 pCond->fSeenElse = false;
3289 pCond->fElIfDecided = enmResult == kVBCppEval_True;
3290 pCond->iLevel = pThis->cCondStackDepth;
3291 pCond->iKeepLevel = (pUp ? pUp->iKeepLevel : 0) + (enmResult == kVBCppEval_Undecided);
3292 pCond->pchCond = pchCondition;
3293 pCond->cchCond = cchCondition;
3294
3295 pCond->pUp = pThis->pCondStack;
3296 pThis->pCondStack = pCond;
3297 pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
3298
3299 /*
3300 * Do pass thru.
3301 */
3302 if ( !pThis->fIf0Mode
3303 && enmResult == kVBCppEval_Undecided)
3304 {
3305 /** @todo this is stripping comments of \#ifdef and \#ifndef atm. */
3306 const char *pszDirective;
3307 switch (enmKind)
3308 {
3309 case kVBCppCondKind_If: pszDirective = "if"; break;
3310 case kVBCppCondKind_IfDef: pszDirective = "ifdef"; break;
3311 case kVBCppCondKind_IfNDef: pszDirective = "ifndef"; break;
3312 case kVBCppCondKind_ElIf: pszDirective = "elif"; break;
3313 default: AssertFailedReturn(RTEXITCODE_FAILURE);
3314 }
3315 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*s%s %.*s",
3316 pCond->iKeepLevel - 1, "", pszDirective, cchCondition, pchCondition);
3317 if (cch < 0)
3318 return vbcppError(pThis, "Output error %Rrc", (int)cch);
3319 }
3320 else
3321 pThis->fJustDroppedLine = true;
3322
3323 return RTEXITCODE_SUCCESS;
3324}
3325
3326
3327/**
3328 * Recursively destroys the expression tree.
3329 *
3330 * @param pExpr The root of the expression tree to destroy.
3331 */
3332static void vbcppExprDestoryTree(PVBCPPEXPR pExpr)
3333{
3334 if (!pExpr)
3335 return;
3336
3337 switch (pExpr->enmKind)
3338 {
3339 case kVBCppExprKind_Unary:
3340 vbcppExprDestoryTree(pExpr->u.Unary.pArg);
3341 break;
3342 case kVBCppExprKind_Binary:
3343 vbcppExprDestoryTree(pExpr->u.Binary.pLeft);
3344 vbcppExprDestoryTree(pExpr->u.Binary.pRight);
3345 break;
3346 case kVBCppExprKind_Ternary:
3347 vbcppExprDestoryTree(pExpr->u.Ternary.pExpr);
3348 vbcppExprDestoryTree(pExpr->u.Ternary.pTrue);
3349 vbcppExprDestoryTree(pExpr->u.Ternary.pFalse);
3350 break;
3351 case kVBCppExprKind_SignedValue:
3352 case kVBCppExprKind_UnsignedValue:
3353 break;
3354 case kVBCppExprKind_UndefMacroCall:
3355 pExpr->u.UndefMacroCall.pszName = NULL;
3356 for (size_t i = 0; i < pExpr->u.UndefMacroCall.cArgs; i++)
3357 vbcppExprDestoryTree(pExpr->u.UndefMacroCall.papArgs[i]);
3358 RTMemFree(pExpr->u.UndefMacroCall.papArgs);
3359 RTMemFree(pExpr->u.UndefMacroCall.pszName);
3360 break;
3361 default:
3362 AssertFailed();
3363 return;
3364 }
3365 RTMemFree(pExpr);
3366}
3367
3368
3369/**
3370 * Report error during expression parsing.
3371 *
3372 * @returns kExprRet_Error
3373 * @param pParser The parser instance.
3374 * @param pszMsg The error message.
3375 * @param ... Format arguments.
3376 */
3377static VBCPPEXPRRET vbcppExprParseError(PVBCPPEXPRPARSER pParser, const char *pszMsg, ...)
3378{
3379 va_list va;
3380 va_start(va, pszMsg);
3381 vbcppErrorV(pParser->pThis, pszMsg, va);
3382 va_end(va);
3383 return kExprRet_Error;
3384}
3385
3386
3387/**
3388 * Skip white space.
3389 *
3390 * @param pParser The parser instance.
3391 */
3392static void vbcppExprParseSkipWhiteSpace(PVBCPPEXPRPARSER pParser)
3393{
3394 while (RT_C_IS_SPACE(*pParser->pszCur))
3395 pParser->pszCur++;
3396}
3397
3398
3399/**
3400 * Allocate a new
3401 *
3402 * @returns Pointer to the node. NULL+msg on failure.
3403 * @param pParser The parser instance.
3404 */
3405static PVBCPPEXPR vbcppExprParseAllocNode(PVBCPPEXPRPARSER pParser)
3406{
3407 PVBCPPEXPR pExpr = (PVBCPPEXPR)RTMemAllocZ(sizeof(*pExpr));
3408 if (!pExpr)
3409 vbcppExprParseError(pParser, "out of memory (expression node)");
3410 return pExpr;
3411}
3412
3413
3414/**
3415 * Checks if we're currently in an call to an undefined macro.
3416 *
3417 * This will walk up the expression chain and check for a call node while also
3418 * taking parenthesis nodes into account.
3419 *
3420 * @returns true if we are, false if we aren't.
3421 * @param pParser The parser instance.
3422 */
3423static bool vbcppExprParseIsInUndefCall(PVBCPPEXPRPARSER pParser)
3424{
3425 PVBCPPEXPR pExpr = pParser->pCur;
3426 while (pExpr)
3427 {
3428 if (pExpr->enmKind == kVBCppExprKind_UndefMacroCall)
3429 return true;
3430 if ( pExpr->enmKind == kVBCppExprKind_Unary
3431 && pExpr->u.Unary.enmOperator == kVBCppUnaryOp_Parenthesis)
3432 break;
3433 pExpr = pExpr->pParent;
3434 }
3435 return false;
3436}
3437
3438
3439/**
3440 * Looks for right parentheses, comma and/or end of expression.
3441 *
3442 * (The comma is only if we're in the context of a undefined macro call.)
3443 *
3444 * @returns Expression status.
3445 * @retval kExprRet_Ok
3446 * @retval kExprRet_Error with msg.
3447 * @retval kExprRet_EndOfExpr
3448 * @param pParser The parser instance.
3449 */
3450static VBCPPEXPRRET vbcppExprParseMaybeRParenOrEoe(PVBCPPEXPRPARSER pParser)
3451{
3452 Assert(!pParser->ppCur);
3453 for (;;)
3454 {
3455 vbcppExprParseSkipWhiteSpace(pParser);
3456 char const ch = *pParser->pszCur;
3457 if (ch == '\0')
3458 {
3459 /* Unwind making sure we don't have any incomplete parentheses or
3460 incomplete call expressions on the stack. */
3461 PVBCPPEXPR pCur = pParser->pCur;
3462 Assert(pCur);
3463 for (;;)
3464 {
3465 if (!pCur->fComplete)
3466 {
3467 if ( pCur->enmKind == kVBCppExprKind_Unary
3468 && pCur->u.Unary.enmOperator == kVBCppUnaryOp_Parenthesis)
3469 return vbcppExprParseError(pParser, "Missing right parenthesis");
3470 if (pCur->enmKind == kVBCppExprKind_UndefMacroCall)
3471 return vbcppExprParseError(pParser, "Missing right parenthesis for undefined macro call");
3472 }
3473 if (pCur->pParent)
3474 pCur = pCur->pParent;
3475 else
3476 {
3477 pParser->pCur = pCur;
3478 pParser->ppCur = NULL;
3479 return kExprRet_EndOfExpr;
3480 }
3481 }
3482 }
3483
3484 if (ch != ')' && (ch != ',' || !vbcppExprParseIsInUndefCall(pParser))) /** @todo just immediate? */
3485 break;
3486 pParser->pszCur++;
3487
3488 PVBCPPEXPR pCur = pParser->pCur;
3489 while ( pCur
3490 && ( pCur->enmKind != kVBCppExprKind_UndefMacroCall
3491 || pCur->fComplete /*?*/)
3492 && ( pCur->enmKind != kVBCppExprKind_Unary
3493 || pCur->u.Unary.enmOperator != kVBCppUnaryOp_Parenthesis
3494 || pCur->fComplete))
3495 {
3496 switch (pCur->enmKind)
3497 {
3498 case kVBCppExprKind_SignedValue:
3499 case kVBCppExprKind_UnsignedValue:
3500 Assert(pCur->fComplete);
3501 break;
3502 case kVBCppExprKind_Unary:
3503 AssertReturn(pCur->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
3504 pCur->fComplete = true;
3505 break;
3506 case kVBCppExprKind_Binary:
3507 AssertReturn(pCur->u.Binary.pLeft, vbcppExprParseError(pParser, "internal error"));
3508 AssertReturn(pCur->u.Binary.pRight, vbcppExprParseError(pParser, "internal error"));
3509 pCur->fComplete = true;
3510 break;
3511 case kVBCppExprKind_Ternary:
3512#if 1 /** @todo Check out the ternary operator implementation. */
3513 return vbcppExprParseError(pParser, "The ternary operator is not implemented");
3514#else
3515 Assert(pCur->u.Ternary.pExpr);
3516 if (!pCur->u.Ternary.pTrue)
3517 return vbcppExprParseError(pParser, "?!?!?");
3518 if (!pCur->u.Ternary.pFalse)
3519 return vbcppExprParseError(pParser, "?!?!?!?");
3520 pCur->fComplete = true;
3521#endif
3522 break;
3523 case kVBCppExprKind_UndefMacroCall:
3524 break;
3525 default:
3526 return vbcppExprParseError(pParser, "Internal error (enmKind=%d)", pCur->enmKind);
3527 }
3528 pCur = pCur->pParent;
3529 }
3530 if (!pCur)
3531 return vbcppExprParseError(pParser, "Right parenthesis without a left one");
3532
3533 /*
3534 * If we got down to an undefined macro call, we make it the current
3535 * parser expression and return EndOfExpr. The call expression is
3536 * completed if we hit a ')'.
3537 */
3538 if (pCur->enmKind == kVBCppExprKind_UndefMacroCall)
3539 {
3540 pCur->fComplete = ch == ')';
3541 pParser->pCur = pCur;
3542 pParser->ppCur = NULL;
3543 return kExprRet_EndOfExpr;
3544 }
3545
3546 /*
3547 * Complete the parenthesis expression and make it current.
3548 */
3549 Assert( pCur->enmKind == kVBCppExprKind_Unary
3550 && pCur->u.Unary.enmOperator == kVBCppUnaryOp_Parenthesis);
3551 AssertReturn(pCur->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
3552 pCur->fComplete = true;
3553 pParser->pCur = pCur;
3554 pParser->ppCur = NULL;
3555 }
3556
3557 return kExprRet_Ok;
3558}
3559
3560
3561/**
3562 * Parses an binary operator.
3563 *
3564 * @returns Expression status.
3565 * @retval kExprRet_Ok
3566 * @retval kExprRet_Error with msg.
3567 * @param pParser The parser instance.
3568 */
3569static VBCPPEXPRRET vbcppExprParseBinaryOperator(PVBCPPEXPRPARSER pParser)
3570{
3571 /*
3572 * Binary or ternary operator should follow now.
3573 */
3574 VBCPPBINARYOP enmOp;
3575 char ch = *pParser->pszCur;
3576 switch (ch)
3577 {
3578 case '*':
3579 if (pParser->pszCur[1] == '=')
3580 return vbcppExprParseError(pParser, "The assignment by product operator is not valid in a preprocessor expression");
3581 enmOp = kVBCppBinary_Multiplication;
3582 break;
3583 case '/':
3584 if (pParser->pszCur[1] == '=')
3585 return vbcppExprParseError(pParser, "The assignment by quotient operator is not valid in a preprocessor expression");
3586 enmOp = kVBCppBinary_Division;
3587 break;
3588 case '%':
3589 if (pParser->pszCur[1] == '=')
3590 return vbcppExprParseError(pParser, "The assignment by remainder operator is not valid in a preprocessor expression");
3591 enmOp = kVBCppBinary_Modulo;
3592 break;
3593 case '+':
3594 if (pParser->pszCur[1] == '=')
3595 return vbcppExprParseError(pParser, "The assignment by sum operator is not valid in a preprocessor expression");
3596 enmOp = kVBCppBinary_Addition;
3597 break;
3598 case '-':
3599 if (pParser->pszCur[1] == '=')
3600 return vbcppExprParseError(pParser, "The assignment by difference operator is not valid in a preprocessor expression");
3601 enmOp = kVBCppBinary_Subtraction;
3602 break;
3603 case '<':
3604 enmOp = kVBCppBinary_LessThan;
3605 if (pParser->pszCur[1] == '=')
3606 {
3607 pParser->pszCur++;
3608 enmOp = kVBCppBinary_LessThanOrEqual;
3609 }
3610 else if (pParser->pszCur[1] == '<')
3611 {
3612 pParser->pszCur++;
3613 if (pParser->pszCur[1] == '=')
3614 return vbcppExprParseError(pParser, "The assignment by bitwise left shift operator is not valid in a preprocessor expression");
3615 enmOp = kVBCppBinary_LeftShift;
3616 }
3617 break;
3618 case '>':
3619 enmOp = kVBCppBinary_GreaterThan;
3620 if (pParser->pszCur[1] == '=')
3621 {
3622 pParser->pszCur++;
3623 enmOp = kVBCppBinary_GreaterThanOrEqual;
3624 }
3625 else if (pParser->pszCur[1] == '<')
3626 {
3627 pParser->pszCur++;
3628 if (pParser->pszCur[1] == '=')
3629 return vbcppExprParseError(pParser, "The assignment by bitwise right shift operator is not valid in a preprocessor expression");
3630 enmOp = kVBCppBinary_LeftShift;
3631 }
3632 break;
3633 case '=':
3634 if (pParser->pszCur[1] != '=')
3635 return vbcppExprParseError(pParser, "The assignment operator is not valid in a preprocessor expression");
3636 pParser->pszCur++;
3637 enmOp = kVBCppBinary_EqualTo;
3638 break;
3639
3640 case '!':
3641 if (pParser->pszCur[1] != '=')
3642 return vbcppExprParseError(pParser, "Expected binary operator, found the unary operator logical NOT");
3643 pParser->pszCur++;
3644 enmOp = kVBCppBinary_NotEqualTo;
3645 break;
3646
3647 case '&':
3648 if (pParser->pszCur[1] == '=')
3649 return vbcppExprParseError(pParser, "The assignment by bitwise AND operator is not valid in a preprocessor expression");
3650 if (pParser->pszCur[1] == '&')
3651 {
3652 pParser->pszCur++;
3653 enmOp = kVBCppBinary_LogicalAnd;
3654 }
3655 else
3656 enmOp = kVBCppBinary_BitwiseAnd;
3657 break;
3658 case '^':
3659 if (pParser->pszCur[1] == '=')
3660 return vbcppExprParseError(pParser, "The assignment by bitwise XOR operator is not valid in a preprocessor expression");
3661 enmOp = kVBCppBinary_BitwiseXor;
3662 break;
3663 case '|':
3664 if (pParser->pszCur[1] == '=')
3665 return vbcppExprParseError(pParser, "The assignment by bitwise AND operator is not valid in a preprocessor expression");
3666 if (pParser->pszCur[1] == '|')
3667 {
3668 pParser->pszCur++;
3669 enmOp = kVBCppBinary_LogicalOr;
3670 }
3671 else
3672 enmOp = kVBCppBinary_BitwiseOr;
3673 break;
3674 case '~':
3675 return vbcppExprParseError(pParser, "Expected binary operator, found the unary operator bitwise NOT");
3676
3677 case ':':
3678 case '?':
3679 return vbcppExprParseError(pParser, "The ternary operator is not yet implemented");
3680
3681 default:
3682 return vbcppExprParseError(pParser, "Expected binary operator, found '%.20s'", pParser->pszCur);
3683 }
3684 pParser->pszCur++;
3685
3686 /*
3687 * Create a binary operator node.
3688 */
3689 PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
3690 if (!pExpr)
3691 return kExprRet_Error;
3692 pExpr->fComplete = true;
3693 pExpr->enmKind = kVBCppExprKind_Binary;
3694 pExpr->u.Binary.enmOperator = enmOp;
3695 pExpr->u.Binary.pLeft = NULL;
3696 pExpr->u.Binary.pRight = NULL;
3697
3698 /*
3699 * Back up the tree until we find our spot.
3700 */
3701 PVBCPPEXPR *ppPlace = NULL;
3702 PVBCPPEXPR pChild = pParser->pCur;
3703 PVBCPPEXPR pParent = pChild->pParent;
3704 while (pParent)
3705 {
3706 if (pParent->enmKind == kVBCppExprKind_Unary)
3707 {
3708 if (pParent->u.Unary.enmOperator == kVBCppUnaryOp_Parenthesis)
3709 {
3710 ppPlace = &pParent->u.Unary.pArg;
3711 break;
3712 }
3713 AssertReturn(pParent->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
3714 pParent->fComplete = true;
3715 }
3716 else if (pParent->enmKind == kVBCppExprKind_Binary)
3717 {
3718 AssertReturn(pParent->u.Binary.pLeft, vbcppExprParseError(pParser, "internal error"));
3719 AssertReturn(pParent->u.Binary.pRight, vbcppExprParseError(pParser, "internal error"));
3720 if ((pParent->u.Binary.enmOperator & VBCPPOP_PRECEDENCE_MASK) >= (enmOp & VBCPPOP_PRECEDENCE_MASK))
3721 {
3722 AssertReturn(pChild, vbcppExprParseError(pParser, "internal error"));
3723
3724 if (pParent->u.Binary.pRight == pChild)
3725 ppPlace = &pParent->u.Binary.pRight;
3726 else
3727 ppPlace = &pParent->u.Binary.pLeft;
3728 AssertReturn(*ppPlace == pChild, vbcppExprParseError(pParser, "internal error"));
3729 break;
3730 }
3731 pParent->fComplete = true;
3732 }
3733 else if (pParent->enmKind == kVBCppExprKind_Ternary)
3734 {
3735 return vbcppExprParseError(pParser, "The ternary operator is not implemented");
3736 }
3737 else if (pParent->enmKind == kVBCppExprKind_UndefMacroCall)
3738 {
3739 ppPlace = &pParent->u.UndefMacroCall.papArgs[pParent->u.UndefMacroCall.cArgs];
3740 break;
3741 }
3742 else
3743 AssertReturn( pParent->enmKind == kVBCppExprKind_SignedValue
3744 || pParent->enmKind == kVBCppExprKind_UnsignedValue,
3745 vbcppExprParseError(pParser, "internal error"));
3746
3747 /* Up on level */
3748 pChild = pParent;
3749 pParent = pParent->pParent;
3750 }
3751
3752 /*
3753 * Do the rotation.
3754 */
3755 Assert(pChild);
3756 Assert(pChild->pParent == pParent);
3757 pChild->pParent = pExpr;
3758
3759 pExpr->u.Binary.pLeft = pChild;
3760 pExpr->pParent = pParent;
3761
3762 if (!pParent)
3763 pParser->pRoot = pExpr;
3764 else
3765 *ppPlace = pExpr;
3766
3767 pParser->ppCur = &pExpr->u.Binary.pRight;
3768 pParser->pCur = pExpr;
3769
3770 return kExprRet_Ok;
3771}
3772
3773
3774/**
3775 * Deals with right paretheses or/and end of expression, looks for binary
3776 * operators.
3777 *
3778 * @returns Expression status.
3779 * @retval kExprRet_Ok if binary operator was found processed.
3780 * @retval kExprRet_Error with msg.
3781 * @retval kExprRet_EndOfExpr
3782 * @param pParser The parser instance.
3783 */
3784static VBCPPEXPRRET vbcppExprParseBinaryOrEoeOrRparen(PVBCPPEXPRPARSER pParser)
3785{
3786 VBCPPEXPRRET enmRet = vbcppExprParseMaybeRParenOrEoe(pParser);
3787 if (enmRet != kExprRet_Ok)
3788 return enmRet;
3789 return vbcppExprParseBinaryOperator(pParser);
3790}
3791
3792
3793/**
3794 * Worker for vbcppExprParseIdentifier that parses a call to an undefined macro
3795 * in selective mode.
3796 *
3797 * @returns Expression status.
3798 * @retval kExprRet_Ok if binary operator was found processed.
3799 * @retval kExprRet_Error with msg.
3800 * @retval kExprRet_EndOfExpr
3801 * @param pParser The parser instance.
3802 * @param pszMacro The start of the macro name.
3803 * @param cchMacro The length of the macro name.
3804 */
3805static VBCPPEXPRRET vbcppExprParseUndefMacroCall(PVBCPPEXPRPARSER pParser, const char *pszMacro, size_t cchMacro)
3806{
3807 /*
3808 * Treat it as a call to an undefined macro function.
3809 */
3810 /* Create a node. */
3811 PVBCPPEXPR const pExpr = vbcppExprParseAllocNode(pParser);
3812 if (!pExpr)
3813 return kExprRet_Error;
3814 pExpr->enmKind = kVBCppExprKind_UndefMacroCall;
3815 pExpr->fComplete = false;
3816 pExpr->u.UndefMacroCall.cArgs = 0;
3817 pExpr->u.UndefMacroCall.papArgs = NULL;
3818 pExpr->u.UndefMacroCall.pszName = RTStrDupN(pszMacro, cchMacro);
3819 AssertReturn(pExpr->u.UndefMacroCall.pszName, vbcppExprParseError(pParser, "out of memory"));
3820
3821 /* Link it. */
3822 pExpr->pParent = pParser->pCur;
3823 pParser->pCur = pExpr;
3824 *pParser->ppCur = pExpr;
3825 pParser->ppCur = NULL;
3826
3827 /*
3828 * Parse the argument list.
3829 */
3830 pParser->pszCur++;
3831 for (size_t iArg = 0 ; ; iArg++)
3832 {
3833 /*
3834 * Prepare the next argument expression pointer.
3835 */
3836 if (!(iArg % 16))
3837 {
3838 void *pvNew = RTMemRealloc(pExpr->u.UndefMacroCall.papArgs,
3839 sizeof(pExpr->u.UndefMacroCall.papArgs[0]) * (iArg + 16));
3840 AssertPtrReturn(pvNew, vbcppExprParseError(pParser, "out of memory"));
3841 pExpr->u.UndefMacroCall.papArgs = (PVBCPPEXPR *)pvNew;
3842 }
3843 pExpr->u.UndefMacroCall.papArgs[iArg] = NULL;
3844 pParser->ppCur = &pExpr->u.UndefMacroCall.papArgs[iArg];
3845
3846 /*
3847 * Do the parsing.
3848 */
3849 for (;;)
3850 {
3851 /*
3852 * Eat unary operators until we hit a value or end of argument/call.
3853 */
3854 VBCPPEXPRRET enmRet;
3855 do
3856 enmRet = vbcppExprParseUnaryOrValue(pParser);
3857 while (enmRet == kExprRet_UnaryOperator);
3858 if (enmRet == kExprRet_EndOfExpr)
3859 break;
3860 if (enmRet == kExprRet_Error)
3861 return enmRet;
3862 AssertReturn(enmRet == kExprRet_Value, vbcppExprParseError(pParser, "Expected value (enmRet=%d)", enmRet));
3863
3864 /*
3865 * Non-unary operator, right parenthesis or end of argument/call is up next.
3866 */
3867 enmRet = vbcppExprParseBinaryOrEoeOrRparen(pParser);
3868 if (enmRet == kExprRet_EndOfExpr)
3869 break;
3870 if (enmRet == kExprRet_Error)
3871 return enmRet;
3872 AssertReturn(enmRet == kExprRet_Ok, vbcppExprParseError(pParser, "Expected value (enmRet=%d)", enmRet));
3873 }
3874
3875 /*
3876 * Append the argument and skip past the comma or right parenthesis.
3877 */
3878 if (pExpr->u.UndefMacroCall.papArgs[iArg] != NULL || !pExpr->fComplete)
3879 pExpr->u.UndefMacroCall.cArgs = iArg + 1;
3880
3881 Assert(pParser->pCur == pExpr);
3882 if (pExpr->fComplete)
3883 break;
3884 }
3885
3886 pParser->ppCur = NULL;
3887 return kExprRet_Value;
3888}
3889
3890
3891/**
3892 * Parses an identifier in the expression, replacing it by 0.
3893 *
3894 * All known identifiers has already been replaced by their macro values, so
3895 * what's left are unknown macros. These are replaced by 0.
3896 *
3897 * @returns Expression status.
3898 * @retval kExprRet_Value
3899 * @retval kExprRet_Error with msg.
3900 * @param pParser The parser instance.
3901 */
3902static VBCPPEXPRRET vbcppExprParseIdentifier(PVBCPPEXPRPARSER pParser)
3903{
3904/** @todo don't increment if it's an actively undefined macro. Need to revise
3905 * the expression related code wrt selective preprocessing. */
3906 pParser->cUndefined++;
3907
3908 /* Find the end. */
3909 const char *pszMacro = pParser->pszCur;
3910 const char *pszNext = pszMacro + 1;
3911 while (vbcppIsCIdentifierChar(*pszNext))
3912 pszNext++;
3913 size_t const cchMacro = pszNext - pszMacro;
3914
3915 /* Skip spaces and check for parenthesis. */
3916 pParser->pszCur = pszNext;
3917 vbcppExprParseSkipWhiteSpace(pParser);
3918 if (*pParser->pszCur == '(')
3919 {
3920 if (pParser->pThis->enmMode == kVBCppMode_Selective)
3921 return vbcppExprParseUndefMacroCall(pParser, pszMacro, cchMacro);
3922 return vbcppExprParseError(pParser, "%sUnknown unary operator '%.*s'",
3923 pParser->pThis->enmMode != kVBCppMode_Standard ? "TODO selective: " : "",
3924 cchMacro, pszMacro);
3925 }
3926
3927 /* Create a signed value node. */
3928 PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
3929 if (!pExpr)
3930 return kExprRet_Error;
3931 pExpr->fComplete = true;
3932 pExpr->enmKind = kVBCppExprKind_UnsignedValue;
3933 pExpr->u.UnsignedValue.u64 = 0;
3934
3935 /* Link it. */
3936 pExpr->pParent = pParser->pCur;
3937 pParser->pCur = pExpr;
3938 *pParser->ppCur = pExpr;
3939 pParser->ppCur = NULL;
3940
3941 return kExprRet_Value;
3942}
3943
3944
3945/**
3946 * Parses an numeric constant in the expression.
3947 *
3948 * @returns Expression status.
3949 * @retval kExprRet_Value
3950 * @retval kExprRet_Error with msg.
3951 * @param pParser The parser instance.
3952 */
3953static VBCPPEXPRRET vbcppExprParseNumber(PVBCPPEXPRPARSER pParser)
3954{
3955 bool fSigned;
3956 char *pszNext;
3957 uint64_t u64;
3958 char ch = *pParser->pszCur++;
3959 char ch2 = *pParser->pszCur;
3960 if ( ch == '0'
3961 && (ch2 == 'x' || ch2 == 'X'))
3962 {
3963 ch2 = *++pParser->pszCur;
3964 if (!RT_C_IS_XDIGIT(ch2))
3965 return vbcppExprParseError(pParser, "Expected hex digit following '0x'");
3966 int rc = RTStrToUInt64Ex(pParser->pszCur, &pszNext, 16, &u64);
3967 if ( RT_FAILURE(rc)
3968 || rc == VWRN_NUMBER_TOO_BIG)
3969 return vbcppExprParseError(pParser, "Invalid hex value '%.20s...' (%Rrc)", pParser->pszCur, rc);
3970 fSigned = false;
3971 }
3972 else if (ch == '0')
3973 {
3974 int rc = RTStrToUInt64Ex(pParser->pszCur - 1, &pszNext, 8, &u64);
3975 if ( RT_FAILURE(rc)
3976 || rc == VWRN_NUMBER_TOO_BIG)
3977 return vbcppExprParseError(pParser, "Invalid octal value '%.20s...' (%Rrc)", pParser->pszCur, rc);
3978 fSigned = u64 > (uint64_t)INT64_MAX ? false : true;
3979 }
3980 else
3981 {
3982 int rc = RTStrToUInt64Ex(pParser->pszCur - 1, &pszNext, 10, &u64);
3983 if ( RT_FAILURE(rc)
3984 || rc == VWRN_NUMBER_TOO_BIG)
3985 return vbcppExprParseError(pParser, "Invalid decimal value '%.20s...' (%Rrc)", pParser->pszCur, rc);
3986 fSigned = u64 > (uint64_t)INT64_MAX ? false : true;
3987 }
3988
3989 /* suffix. */
3990 if (vbcppIsCIdentifierLeadChar(*pszNext))
3991 {
3992 size_t cchSuffix = 1;
3993 while (vbcppIsCIdentifierLeadChar(pszNext[cchSuffix]))
3994 cchSuffix++;
3995
3996 if (cchSuffix == 1 && (*pszNext == 'u' || *pszNext == 'U'))
3997 fSigned = false;
3998 else if ( cchSuffix == 1
3999 && (*pszNext == 'l' || *pszNext == 'L'))
4000 fSigned = true;
4001 else if ( cchSuffix == 2
4002 && (!strncmp(pszNext, "ul", 2) || !strncmp(pszNext, "UL", 2)))
4003 fSigned = false;
4004 else if ( cchSuffix == 2
4005 && (!strncmp(pszNext, "ll", 2) || !strncmp(pszNext, "LL", 2)))
4006 fSigned = true;
4007 else if ( cchSuffix == 3
4008 && (!strncmp(pszNext, "ull", 3) || !strncmp(pszNext, "ULL", 3)))
4009 fSigned = false;
4010 else
4011 return vbcppExprParseError(pParser, "Invalid number suffix '%.*s'", cchSuffix, pszNext);
4012
4013 pszNext += cchSuffix;
4014 }
4015 pParser->pszCur = pszNext;
4016
4017 /* Create a signed value node. */
4018 PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
4019 if (!pExpr)
4020 return kExprRet_Error;
4021 pExpr->fComplete = true;
4022 if (fSigned)
4023 {
4024 pExpr->enmKind = kVBCppExprKind_SignedValue;
4025 pExpr->u.SignedValue.s64 = (int64_t)u64;
4026 }
4027 else
4028 {
4029 pExpr->enmKind = kVBCppExprKind_UnsignedValue;
4030 pExpr->u.UnsignedValue.u64 = u64;
4031 }
4032
4033 /* Link it. */
4034 pExpr->pParent = pParser->pCur;
4035 pParser->pCur = pExpr;
4036 *pParser->ppCur = pExpr;
4037 pParser->ppCur = NULL;
4038
4039 return kExprRet_Value;
4040}
4041
4042
4043/**
4044 * Parses an character constant in the expression.
4045 *
4046 * @returns Expression status.
4047 * @retval kExprRet_Value
4048 * @retval kExprRet_Error with msg.
4049 * @param pParser The parser instance.
4050 */
4051static VBCPPEXPRRET vbcppExprParseCharacterConstant(PVBCPPEXPRPARSER pParser)
4052{
4053 Assert(*pParser->pszCur == '\'');
4054 pParser->pszCur++;
4055 char ch2 = *pParser->pszCur++;
4056 if (ch2 == '\'')
4057 return vbcppExprParseError(pParser, "Empty character constant");
4058 int64_t s64;
4059 if (ch2 == '\\')
4060 {
4061 ch2 = *pParser->pszCur++;
4062 switch (ch2)
4063 {
4064 case '0': s64 = 0x00; break;
4065 case 'n': s64 = 0x0d; break;
4066 case 'r': s64 = 0x0a; break;
4067 case 't': s64 = 0x09; break;
4068 default:
4069 return vbcppExprParseError(pParser, "Escape character '%c' is not implemented", ch2);
4070 }
4071 }
4072 else
4073 s64 = ch2;
4074 if (*pParser->pszCur != '\'')
4075 return vbcppExprParseError(pParser, "Character constant contains more than one character");
4076
4077 /* Create a signed value node. */
4078 PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
4079 if (!pExpr)
4080 return kExprRet_Error;
4081 pExpr->fComplete = true;
4082 pExpr->enmKind = kVBCppExprKind_SignedValue;
4083 pExpr->u.SignedValue.s64 = s64;
4084
4085 /* Link it. */
4086 pExpr->pParent = pParser->pCur;
4087 pParser->pCur = pExpr;
4088 *pParser->ppCur = pExpr;
4089 pParser->ppCur = NULL;
4090
4091 return kExprRet_Value;
4092}
4093
4094
4095/**
4096 * Parses a unary operator or a value.
4097 *
4098 * @returns Expression status.
4099 * @retval kExprRet_Value if value was found and processed.
4100 * @retval kExprRet_UnaryOperator if an unary operator was found and processed.
4101 * @retval kExprRet_Error with msg.
4102 * @retval kExprRet_EndOfExpr if reached ',' or ')' if in an undefined call.
4103 * @param pParser The parser instance.
4104 */
4105static VBCPPEXPRRET vbcppExprParseUnaryOrValue(PVBCPPEXPRPARSER pParser)
4106{
4107 vbcppExprParseSkipWhiteSpace(pParser);
4108 char ch = *pParser->pszCur;
4109 if (ch == '\0')
4110 return vbcppExprParseError(pParser, "Premature end of expression");
4111
4112 /*
4113 * Value?
4114 */
4115 if (ch == '\'')
4116 return vbcppExprParseCharacterConstant(pParser);
4117 if (RT_C_IS_DIGIT(ch))
4118 return vbcppExprParseNumber(pParser);
4119 if (ch == '"')
4120 return vbcppExprParseError(pParser, "String litteral");
4121 if (vbcppIsCIdentifierLeadChar(ch))
4122 return vbcppExprParseIdentifier(pParser);
4123
4124 /*
4125 * Operator?
4126 */
4127 VBCPPUNARYOP enmOperator;
4128 if (ch == '+')
4129 {
4130 enmOperator = kVBCppUnaryOp_Pluss;
4131 if (pParser->pszCur[1] == '+')
4132 return vbcppExprParseError(pParser, "The prefix increment operator is not valid in a preprocessor expression");
4133 }
4134 else if (ch == '-')
4135 {
4136 enmOperator = kVBCppUnaryOp_Minus;
4137 if (pParser->pszCur[1] == '-')
4138 return vbcppExprParseError(pParser, "The prefix decrement operator is not valid in a preprocessor expression");
4139 }
4140 else if (ch == '!')
4141 enmOperator = kVBCppUnaryOp_LogicalNot;
4142 else if (ch == '~')
4143 enmOperator = kVBCppUnaryOp_BitwiseNot;
4144 else if (ch == '(')
4145 enmOperator = kVBCppUnaryOp_Parenthesis;
4146 else if ((ch == ',' || ch == ')') && pParser->pCur->enmKind == kVBCppExprKind_UndefMacroCall)
4147 {
4148 pParser->pszCur++;
4149 pParser->pCur->fComplete = ch == ')';
4150 return kExprRet_EndOfExpr;
4151 }
4152 else
4153 return vbcppExprParseError(pParser, "Unexpected token '%.*s'", 32, pParser->pszCur - 1);
4154 pParser->pszCur++;
4155
4156 /* Create an operator node. */
4157 PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
4158 if (!pExpr)
4159 return kExprRet_Error;
4160 pExpr->fComplete = false;
4161 pExpr->enmKind = kVBCppExprKind_Unary;
4162 pExpr->u.Unary.enmOperator = enmOperator;
4163 pExpr->u.Unary.pArg = NULL;
4164
4165 /* Link it into the tree. */
4166 pExpr->pParent = pParser->pCur;
4167 pParser->pCur = pExpr;
4168 *pParser->ppCur = pExpr;
4169 pParser->ppCur = &pExpr->u.Unary.pArg;
4170
4171 return kExprRet_UnaryOperator;
4172}
4173
4174
4175/**
4176 * Parses an expanded preprocessor expression.
4177 *
4178 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4179 * @param pThis The C preprocessor instance.
4180 * @param pszExpr The expression to parse.
4181 * @param cchExpr The length of the expression in case we need it.
4182 * @param ppExprTree Where to return the parse tree.
4183 * @param pcUndefined Where to return the number of unknown undefined
4184 * macros. Optional.
4185 */
4186static RTEXITCODE vbcppExprParse(PVBCPP pThis, char *pszExpr, size_t cchExpr, PVBCPPEXPR *ppExprTree, size_t *pcUndefined)
4187{
4188 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
4189 NOREF(cchExpr);
4190
4191 /*
4192 * Initialize the parser context structure.
4193 */
4194 VBCPPEXPRPARSER Parser;
4195 Parser.pszCur = pszExpr;
4196 Parser.pRoot = NULL;
4197 Parser.pCur = NULL;
4198 Parser.ppCur = &Parser.pRoot;
4199 Parser.pszExpr = pszExpr;
4200 Parser.cUndefined = 0;
4201 Parser.pThis = pThis;
4202
4203 /*
4204 * Do the parsing.
4205 */
4206 VBCPPEXPRRET enmRet;
4207 for (;;)
4208 {
4209 /*
4210 * Eat unary operators until we hit a value.
4211 */
4212 do
4213 enmRet = vbcppExprParseUnaryOrValue(&Parser);
4214 while (enmRet == kExprRet_UnaryOperator);
4215 if (enmRet == kExprRet_Error)
4216 break;
4217 AssertBreakStmt(enmRet == kExprRet_Value, enmRet = vbcppExprParseError(&Parser, "Expected value (enmRet=%d)", enmRet));
4218
4219 /*
4220 * Non-unary operator, right parenthesis or end of expression is up next.
4221 */
4222 enmRet = vbcppExprParseBinaryOrEoeOrRparen(&Parser);
4223 if (enmRet == kExprRet_Error)
4224 break;
4225 if (enmRet == kExprRet_EndOfExpr)
4226 {
4227 /** @todo check if there are any open parentheses. */
4228 rcExit = RTEXITCODE_SUCCESS;
4229 break;
4230 }
4231 AssertBreakStmt(enmRet == kExprRet_Ok, enmRet = vbcppExprParseError(&Parser, "Expected value (enmRet=%d)", enmRet));
4232 }
4233
4234 if (rcExit != RTEXITCODE_SUCCESS)
4235 {
4236 vbcppExprDestoryTree(Parser.pRoot);
4237 return rcExit;
4238 }
4239
4240 if (pcUndefined)
4241 *pcUndefined = Parser.cUndefined;
4242 *ppExprTree = Parser.pRoot;
4243 return rcExit;
4244}
4245
4246
4247/**
4248 * Checks if an expression value value is evaluates to @c true or @c false.
4249 *
4250 * @returns @c true or @c false.
4251 * @param pExpr The value expression.
4252 */
4253static bool vbcppExprIsExprTrue(PVBCPPEXPR pExpr)
4254{
4255 Assert(pExpr->enmKind == kVBCppExprKind_SignedValue || pExpr->enmKind == kVBCppExprKind_UnsignedValue);
4256
4257 return pExpr->enmKind == kVBCppExprKind_SignedValue
4258 ? pExpr->u.SignedValue.s64 != 0
4259 : pExpr->u.UnsignedValue.u64 != 0;
4260}
4261
4262
4263/**
4264 * Evalutes a parse (sub-)tree.
4265 *
4266 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4267 * @param pThis The C preprocessor instance.
4268 * @param pRoot The root of the parse (sub-)tree.
4269 * @param pResult Where to store the result value.
4270 */
4271static RTEXITCODE vbcppExprEvaluteTree(PVBCPP pThis, PVBCPPEXPR pRoot, PVBCPPEXPR pResult)
4272{
4273 RTEXITCODE rcExit;
4274 switch (pRoot->enmKind)
4275 {
4276 case kVBCppExprKind_SignedValue:
4277 pResult->enmKind = kVBCppExprKind_SignedValue;
4278 pResult->u.SignedValue.s64 = pRoot->u.SignedValue.s64;
4279 return RTEXITCODE_SUCCESS;
4280
4281 case kVBCppExprKind_UnsignedValue:
4282 pResult->enmKind = kVBCppExprKind_UnsignedValue;
4283 pResult->u.UnsignedValue.u64 = pRoot->u.UnsignedValue.u64;
4284 return RTEXITCODE_SUCCESS;
4285
4286 case kVBCppExprKind_Unary:
4287 rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Unary.pArg, pResult);
4288 if (rcExit != RTEXITCODE_SUCCESS)
4289 return rcExit;
4290
4291 /* Apply the unary operator to the value */
4292 switch (pRoot->u.Unary.enmOperator)
4293 {
4294 case kVBCppUnaryOp_Minus:
4295 if (pResult->enmKind == kVBCppExprKind_SignedValue)
4296 pResult->u.SignedValue.s64 = -pResult->u.SignedValue.s64;
4297 else
4298 pResult->u.UnsignedValue.u64 = (uint64_t)-(int64_t)pResult->u.UnsignedValue.u64;
4299 break;
4300
4301 case kVBCppUnaryOp_LogicalNot:
4302 if (pResult->enmKind == kVBCppExprKind_SignedValue)
4303 pResult->u.SignedValue.s64 = !pResult->u.SignedValue.s64;
4304 else
4305 pResult->u.UnsignedValue.u64 = !pResult->u.UnsignedValue.u64;
4306 break;
4307
4308 case kVBCppUnaryOp_BitwiseNot:
4309 if (pResult->enmKind == kVBCppExprKind_SignedValue)
4310 pResult->u.SignedValue.s64 = ~pResult->u.SignedValue.s64;
4311 else
4312 pResult->u.UnsignedValue.u64 = ~pResult->u.UnsignedValue.u64;
4313 break;
4314
4315 case kVBCppUnaryOp_Pluss:
4316 case kVBCppUnaryOp_Parenthesis:
4317 /* do nothing. */
4318 break;
4319
4320 default:
4321 return vbcppError(pThis, "Internal error: u.Unary.enmOperator=%d", pRoot->u.Unary.enmOperator);
4322 }
4323 return RTEXITCODE_SUCCESS;
4324
4325 case kVBCppExprKind_Binary:
4326 {
4327 /* Always evalute the left side. */
4328 rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Binary.pLeft, pResult);
4329 if (rcExit != RTEXITCODE_SUCCESS)
4330 return rcExit;
4331
4332 /* If logical AND or OR we can sometimes skip evaluting the right side. */
4333 if ( pRoot->u.Binary.enmOperator == kVBCppBinary_LogicalAnd
4334 && !vbcppExprIsExprTrue(pResult))
4335 return RTEXITCODE_SUCCESS;
4336
4337 if ( pRoot->u.Binary.enmOperator == kVBCppBinary_LogicalOr
4338 && vbcppExprIsExprTrue(pResult))
4339 return RTEXITCODE_SUCCESS;
4340
4341 /* Evalute the right side. */
4342 VBCPPEXPR Result2;
4343 rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Binary.pRight, &Result2);
4344 if (rcExit != RTEXITCODE_SUCCESS)
4345 return rcExit;
4346
4347 /* If one of them is unsigned, promote the other to unsigned as well. */
4348 if ( pResult->enmKind == kVBCppExprKind_UnsignedValue
4349 && Result2.enmKind == kVBCppExprKind_SignedValue)
4350 {
4351 Result2.enmKind = kVBCppExprKind_UnsignedValue;
4352 Result2.u.UnsignedValue.u64 = Result2.u.SignedValue.s64;
4353 }
4354 else if ( pResult->enmKind == kVBCppExprKind_SignedValue
4355 && Result2.enmKind == kVBCppExprKind_UnsignedValue)
4356 {
4357 pResult->enmKind = kVBCppExprKind_UnsignedValue;
4358 pResult->u.UnsignedValue.u64 = pResult->u.SignedValue.s64;
4359 }
4360
4361 /* Perform the operation. */
4362 if (pResult->enmKind == kVBCppExprKind_UnsignedValue)
4363 {
4364 switch (pRoot->u.Binary.enmOperator)
4365 {
4366 case kVBCppBinary_Multiplication:
4367 pResult->u.UnsignedValue.u64 *= Result2.u.UnsignedValue.u64;
4368 break;
4369 case kVBCppBinary_Division:
4370 if (!Result2.u.UnsignedValue.u64)
4371 return vbcppError(pThis, "Divide by zero");
4372 pResult->u.UnsignedValue.u64 /= Result2.u.UnsignedValue.u64;
4373 break;
4374 case kVBCppBinary_Modulo:
4375 if (!Result2.u.UnsignedValue.u64)
4376 return vbcppError(pThis, "Divide by zero");
4377 pResult->u.UnsignedValue.u64 %= Result2.u.UnsignedValue.u64;
4378 break;
4379 case kVBCppBinary_Addition:
4380 pResult->u.UnsignedValue.u64 += Result2.u.UnsignedValue.u64;
4381 break;
4382 case kVBCppBinary_Subtraction:
4383 pResult->u.UnsignedValue.u64 -= Result2.u.UnsignedValue.u64;
4384 break;
4385 case kVBCppBinary_LeftShift:
4386 pResult->u.UnsignedValue.u64 <<= Result2.u.UnsignedValue.u64;
4387 break;
4388 case kVBCppBinary_RightShift:
4389 pResult->u.UnsignedValue.u64 >>= Result2.u.UnsignedValue.u64;
4390 break;
4391 case kVBCppBinary_LessThan:
4392 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 < Result2.u.UnsignedValue.u64;
4393 break;
4394 case kVBCppBinary_LessThanOrEqual:
4395 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 <= Result2.u.UnsignedValue.u64;
4396 break;
4397 case kVBCppBinary_GreaterThan:
4398 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 > Result2.u.UnsignedValue.u64;
4399 break;
4400 case kVBCppBinary_GreaterThanOrEqual:
4401 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 >= Result2.u.UnsignedValue.u64;
4402 break;
4403 case kVBCppBinary_EqualTo:
4404 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 == Result2.u.UnsignedValue.u64;
4405 break;
4406 case kVBCppBinary_NotEqualTo:
4407 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 != Result2.u.UnsignedValue.u64;
4408 break;
4409 case kVBCppBinary_BitwiseAnd:
4410 pResult->u.UnsignedValue.u64 &= Result2.u.UnsignedValue.u64;
4411 break;
4412 case kVBCppBinary_BitwiseXor:
4413 pResult->u.UnsignedValue.u64 ^= Result2.u.UnsignedValue.u64;
4414 break;
4415 case kVBCppBinary_BitwiseOr:
4416 pResult->u.UnsignedValue.u64 |= Result2.u.UnsignedValue.u64;
4417 break;
4418 case kVBCppBinary_LogicalAnd:
4419 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 && Result2.u.UnsignedValue.u64;
4420 break;
4421 case kVBCppBinary_LogicalOr:
4422 pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 || Result2.u.UnsignedValue.u64;
4423 break;
4424 default:
4425 return vbcppError(pThis, "Internal error: u.Binary.enmOperator=%d", pRoot->u.Binary.enmOperator);
4426 }
4427 }
4428 else
4429 {
4430 switch (pRoot->u.Binary.enmOperator)
4431 {
4432 case kVBCppBinary_Multiplication:
4433 pResult->u.SignedValue.s64 *= Result2.u.SignedValue.s64;
4434 break;
4435 case kVBCppBinary_Division:
4436 if (!Result2.u.SignedValue.s64)
4437 return vbcppError(pThis, "Divide by zero");
4438 pResult->u.SignedValue.s64 /= Result2.u.SignedValue.s64;
4439 break;
4440 case kVBCppBinary_Modulo:
4441 if (!Result2.u.SignedValue.s64)
4442 return vbcppError(pThis, "Divide by zero");
4443 pResult->u.SignedValue.s64 %= Result2.u.SignedValue.s64;
4444 break;
4445 case kVBCppBinary_Addition:
4446 pResult->u.SignedValue.s64 += Result2.u.SignedValue.s64;
4447 break;
4448 case kVBCppBinary_Subtraction:
4449 pResult->u.SignedValue.s64 -= Result2.u.SignedValue.s64;
4450 break;
4451 case kVBCppBinary_LeftShift:
4452 pResult->u.SignedValue.s64 <<= Result2.u.SignedValue.s64;
4453 break;
4454 case kVBCppBinary_RightShift:
4455 pResult->u.SignedValue.s64 >>= Result2.u.SignedValue.s64;
4456 break;
4457 case kVBCppBinary_LessThan:
4458 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 < Result2.u.SignedValue.s64;
4459 break;
4460 case kVBCppBinary_LessThanOrEqual:
4461 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 <= Result2.u.SignedValue.s64;
4462 break;
4463 case kVBCppBinary_GreaterThan:
4464 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 > Result2.u.SignedValue.s64;
4465 break;
4466 case kVBCppBinary_GreaterThanOrEqual:
4467 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 >= Result2.u.SignedValue.s64;
4468 break;
4469 case kVBCppBinary_EqualTo:
4470 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 == Result2.u.SignedValue.s64;
4471 break;
4472 case kVBCppBinary_NotEqualTo:
4473 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 != Result2.u.SignedValue.s64;
4474 break;
4475 case kVBCppBinary_BitwiseAnd:
4476 pResult->u.SignedValue.s64 &= Result2.u.SignedValue.s64;
4477 break;
4478 case kVBCppBinary_BitwiseXor:
4479 pResult->u.SignedValue.s64 ^= Result2.u.SignedValue.s64;
4480 break;
4481 case kVBCppBinary_BitwiseOr:
4482 pResult->u.SignedValue.s64 |= Result2.u.SignedValue.s64;
4483 break;
4484 case kVBCppBinary_LogicalAnd:
4485 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 && Result2.u.SignedValue.s64;
4486 break;
4487 case kVBCppBinary_LogicalOr:
4488 pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 || Result2.u.SignedValue.s64;
4489 break;
4490 default:
4491 return vbcppError(pThis, "Internal error: u.Binary.enmOperator=%d", pRoot->u.Binary.enmOperator);
4492 }
4493 }
4494 return rcExit;
4495 }
4496
4497 case kVBCppExprKind_Ternary:
4498 rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pExpr, pResult);
4499 if (rcExit != RTEXITCODE_SUCCESS)
4500 return rcExit;
4501 if (vbcppExprIsExprTrue(pResult))
4502 return vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pTrue, pResult);
4503 return vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pFalse, pResult);
4504
4505 default:
4506 return vbcppError(pThis, "Internal error: enmKind=%d", pRoot->enmKind);
4507 }
4508}
4509
4510
4511/**
4512 * Evalutes the expression.
4513 *
4514 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4515 * @param pThis The C preprocessor instance.
4516 * @param pszExpr The expression.
4517 * @param cchExpr The length of the expression.
4518 * @param cReplacements The number of replacements.
4519 * @param cDefinedUnknown The number of defined(UNKNOWN) cases. (Relevant
4520 * for selective modes.)
4521 * @param penmResult Where to store the result.
4522 */
4523static RTEXITCODE vbcppExprEval(PVBCPP pThis, char *pszExpr, size_t cchExpr, size_t cReplacements, size_t cDefinedUnknown,
4524 VBCPPEVAL *penmResult)
4525{
4526 Assert(strlen(pszExpr) == cchExpr);
4527 RT_NOREF_PV(cReplacements);
4528
4529 size_t cUndefined;
4530 PVBCPPEXPR pExprTree;
4531 RTEXITCODE rcExit = vbcppExprParse(pThis, pszExpr, cchExpr, &pExprTree, &cUndefined);
4532 if (rcExit == RTEXITCODE_SUCCESS)
4533 {
4534 if ( (!cUndefined && !cDefinedUnknown)
4535 || pThis->enmMode == kVBCppMode_SelectiveD
4536 || pThis->enmMode == kVBCppMode_Standard)
4537 {
4538 VBCPPEXPR Result;
4539 rcExit = vbcppExprEvaluteTree(pThis, pExprTree, &Result);
4540 if (rcExit == RTEXITCODE_SUCCESS)
4541 {
4542 if (vbcppExprIsExprTrue(&Result))
4543 *penmResult = kVBCppEval_True;
4544 else
4545 *penmResult = kVBCppEval_False;
4546 }
4547 }
4548 else
4549 *penmResult = kVBCppEval_Undecided;
4550 }
4551 return rcExit;
4552}
4553
4554
4555static RTEXITCODE vbcppExtractSkipCommentLine(PVBCPP pThis, PSCMSTREAM pStrmInput)
4556{
4557 RT_NOREF_PV(pThis);
4558
4559 unsigned chPrev = ScmStreamGetCh(pStrmInput); Assert(chPrev == '/');
4560 unsigned ch;
4561 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
4562 {
4563 if (ch == '\r' || ch == '\n')
4564 {
4565 if (chPrev != '\\')
4566 break;
4567 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
4568 chPrev = ch;
4569 }
4570 else
4571 {
4572 chPrev = ScmStreamGetCh(pStrmInput);
4573 Assert(chPrev == ch);
4574 }
4575 }
4576 return RTEXITCODE_SUCCESS;
4577}
4578
4579
4580static RTEXITCODE vbcppExtractSkipComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
4581{
4582 unsigned ch = ScmStreamGetCh(pStrmInput); Assert(ch == '*');
4583 while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
4584 {
4585 if (ch == '*')
4586 {
4587 ch = ScmStreamGetCh(pStrmInput);
4588 if (ch == '/')
4589 return RTEXITCODE_SUCCESS;
4590 }
4591 }
4592 return vbcppError(pThis, "Expected '*/'");
4593}
4594
4595
4596static RTEXITCODE vbcppExtractQuotedString(PVBCPP pThis, PSCMSTREAM pStrmInput, PVBCPPSTRBUF pStrBuf,
4597 char chOpen, char chClose)
4598{
4599 unsigned ch = ScmStreamGetCh(pStrmInput);
4600 Assert(ch == (unsigned)chOpen);
4601
4602 RTEXITCODE rcExit = vbcppStrBufAppendCh(pStrBuf, chOpen);
4603 if (rcExit != RTEXITCODE_SUCCESS)
4604 return rcExit;
4605
4606 for (;;)
4607 {
4608 ch = ScmStreamGetCh(pStrmInput);
4609 if (ch == '\\')
4610 {
4611 ch = ScmStreamGetCh(pStrmInput);
4612 if (ch == ~(unsigned)0)
4613 break;
4614 rcExit = vbcppStrBufAppendCh(pStrBuf, '\\');
4615 if (rcExit == RTEXITCODE_SUCCESS)
4616 rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
4617 if (rcExit != RTEXITCODE_SUCCESS)
4618 return rcExit;
4619 }
4620 else if (ch != ~(unsigned)0)
4621 {
4622 rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
4623 if (rcExit != RTEXITCODE_SUCCESS)
4624 return rcExit;
4625 if (ch == (unsigned)chClose)
4626 return RTEXITCODE_SUCCESS;
4627 }
4628 else
4629 break;
4630 }
4631
4632 return vbcppError(pThis, "File ended with an open character constant");
4633}
4634
4635
4636/**
4637 * Extracts a line from the stream, stripping it for comments and maybe
4638 * optimzing some of the whitespace.
4639 *
4640 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4641 * @param pThis The C preprocessor instance.
4642 * @param pStrmInput The input stream.
4643 * @param pStrBuf Where to store the extracted line. Caller must
4644 * initialize this prior to the call an delete it
4645 * after use (even on failure).
4646 * @param poffComment Where to note down the position of the final
4647 * comment. Optional.
4648 */
4649static RTEXITCODE vbcppExtractDirectiveLine(PVBCPP pThis, PSCMSTREAM pStrmInput, PVBCPPSTRBUF pStrBuf, size_t *poffComment)
4650{
4651 size_t offComment = ~(size_t)0;
4652 unsigned ch;
4653 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
4654 {
4655 RTEXITCODE rcExit;
4656 if (ch == '/')
4657 {
4658 /* Comment? */
4659 unsigned ch2 = ScmStreamGetCh(pStrmInput); Assert(ch == ch2); NOREF(ch2);
4660 ch = ScmStreamPeekCh(pStrmInput);
4661 if (ch == '*')
4662 {
4663 offComment = ScmStreamTell(pStrmInput) - 1;
4664 rcExit = vbcppExtractSkipComment(pThis, pStrmInput);
4665 }
4666 else if (ch == '/')
4667 {
4668 offComment = ScmStreamTell(pStrmInput) - 1;
4669 rcExit = vbcppExtractSkipCommentLine(pThis, pStrmInput);
4670 }
4671 else
4672 rcExit = vbcppStrBufAppendCh(pStrBuf, '/');
4673 }
4674 else if (ch == '\'')
4675 {
4676 offComment = ~(size_t)0;
4677 rcExit = vbcppExtractQuotedString(pThis, pStrmInput, pStrBuf, '\'', '\'');
4678 }
4679 else if (ch == '"')
4680 {
4681 offComment = ~(size_t)0;
4682 rcExit = vbcppExtractQuotedString(pThis, pStrmInput, pStrBuf, '"', '"');
4683 }
4684 else if (ch == '\r' || ch == '\n')
4685 break; /* done */
4686 else if ( RT_C_IS_SPACE(ch)
4687 && ( RT_C_IS_SPACE(vbcppStrBufLastCh(pStrBuf))
4688 || vbcppStrBufLastCh(pStrBuf) == '\0') )
4689 {
4690 unsigned ch2 = ScmStreamGetCh(pStrmInput);
4691 Assert(ch == ch2); NOREF(ch2);
4692 rcExit = RTEXITCODE_SUCCESS;
4693 }
4694 else
4695 {
4696 unsigned ch2 = ScmStreamGetCh(pStrmInput); Assert(ch == ch2);
4697
4698 /* Escaped newline? */
4699 if ( ch == '\\'
4700 && ( (ch2 = ScmStreamPeekCh(pStrmInput)) == '\r'
4701 || ch2 == '\n'))
4702 {
4703 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
4704 rcExit = RTEXITCODE_SUCCESS;
4705 }
4706 else
4707 {
4708 offComment = ~(size_t)0;
4709 rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
4710 }
4711 }
4712 if (rcExit != RTEXITCODE_SUCCESS)
4713 return rcExit;
4714 }
4715
4716 if (poffComment)
4717 *poffComment = offComment;
4718 return RTEXITCODE_SUCCESS;
4719}
4720
4721
4722/**
4723 * Processes a abbreviated line number directive.
4724 *
4725 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4726 * @param pThis The C preprocessor instance.
4727 * @param pStrmInput The input stream.
4728 * @param offStart The stream position where the directive
4729 * started (for pass thru).
4730 * @param enmKind The kind of directive we're processing.
4731 */
4732static RTEXITCODE vbcppDirectiveIfOrElif(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart,
4733 VBCPPCONDKIND enmKind)
4734{
4735 /*
4736 * Check for missing #if if #elif.
4737 */
4738 if ( enmKind == kVBCppCondKind_ElIf
4739 && !pThis->pCondStack )
4740 return vbcppError(pThis, "#elif without #if");
4741
4742 /*
4743 * Extract the expression string.
4744 */
4745 const char *pchCondition = ScmStreamGetCur(pStrmInput);
4746 size_t offComment;
4747 VBCPPMACROEXP ExpCtx;
4748#if 0
4749 ExpCtx.pMacroStack = NULL;
4750#endif
4751 ExpCtx.pStrmInput = NULL;
4752 ExpCtx.papszArgs = NULL;
4753 ExpCtx.cArgs = 0;
4754 ExpCtx.cArgsAlloced = 0;
4755 vbcppStrBufInit(&ExpCtx.StrBuf, pThis);
4756 RTEXITCODE rcExit = vbcppExtractDirectiveLine(pThis, pStrmInput, &ExpCtx.StrBuf, &offComment);
4757 if (rcExit == RTEXITCODE_SUCCESS)
4758 {
4759 if (RT_C_IS_SPACE(*pchCondition))
4760 pchCondition++;
4761 size_t const cchCondition = ScmStreamGetCur(pStrmInput) - pchCondition;
4762
4763 /*
4764 * Expand known macros in it.
4765 */
4766 size_t cReplacements;
4767 size_t cDefinedUnknown;
4768 rcExit = vbcppMacroExpandReScan(pThis, &ExpCtx, kMacroReScanMode_Expression, &cReplacements, &cDefinedUnknown);
4769 if (rcExit == RTEXITCODE_SUCCESS)
4770 {
4771 /*
4772 * Strip it and check that it's not empty.
4773 */
4774 char *pszExpr = ExpCtx.StrBuf.pszBuf;
4775 size_t cchExpr = ExpCtx.StrBuf.cchBuf;
4776 while (cchExpr > 0 && RT_C_IS_SPACE(*pszExpr))
4777 pszExpr++, cchExpr--;
4778
4779 while (cchExpr > 0 && RT_C_IS_SPACE(pszExpr[cchExpr - 1]))
4780 {
4781 pszExpr[--cchExpr] = '\0';
4782 ExpCtx.StrBuf.cchBuf--;
4783 }
4784 if (cchExpr)
4785 {
4786 /*
4787 * Now, evalute the expression.
4788 */
4789 VBCPPEVAL enmResult;
4790 rcExit = vbcppExprEval(pThis, pszExpr, cchExpr, cReplacements, cDefinedUnknown, &enmResult);
4791 if (rcExit == RTEXITCODE_SUCCESS)
4792 {
4793 /*
4794 * Take action.
4795 */
4796 if (enmKind != kVBCppCondKind_ElIf)
4797 rcExit = vbcppCondPush(pThis, pStrmInput, offComment, enmKind, enmResult,
4798 pchCondition, cchCondition);
4799 else
4800 {
4801 PVBCPPCOND pCond = pThis->pCondStack;
4802 if ( pCond->enmResult != kVBCppEval_Undecided
4803 && ( !pCond->pUp
4804 || pCond->pUp->enmStackResult == kVBCppEval_True))
4805 {
4806 Assert(enmResult == kVBCppEval_True || enmResult == kVBCppEval_False);
4807 if ( pCond->enmResult == kVBCppEval_False
4808 && enmResult == kVBCppEval_True
4809 && !pCond->fElIfDecided)
4810 {
4811 pCond->enmStackResult = kVBCppEval_True;
4812 pCond->fElIfDecided = true;
4813 }
4814 else
4815 pCond->enmStackResult = kVBCppEval_False;
4816 pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
4817 }
4818 pCond->enmKind = kVBCppCondKind_ElIf;
4819 pCond->enmResult = enmResult;
4820 pCond->pchCond = pchCondition;
4821 pCond->cchCond = cchCondition;
4822
4823 /*
4824 * Do #elif pass thru.
4825 */
4826 if ( !pThis->fIf0Mode
4827 && pCond->enmResult == kVBCppEval_Undecided)
4828 {
4829 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*selif", pCond->iKeepLevel - 1, "");
4830 if (cch > 0)
4831 rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 1);
4832 else
4833 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
4834 }
4835 else
4836 pThis->fJustDroppedLine = true;
4837 }
4838 }
4839 }
4840 else
4841 rcExit = vbcppError(pThis, "Empty #if expression");
4842 }
4843 }
4844 vbcppMacroExpandCleanup(&ExpCtx);
4845 return rcExit;
4846}
4847
4848
4849/**
4850 * Processes a abbreviated line number directive.
4851 *
4852 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4853 * @param pThis The C preprocessor instance.
4854 * @param pStrmInput The input stream.
4855 * @param offStart The stream position where the directive
4856 * started (for pass thru).
4857 */
4858static RTEXITCODE vbcppDirectiveIfDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
4859{
4860 /*
4861 * Parse it.
4862 */
4863 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
4864 if (rcExit == RTEXITCODE_SUCCESS)
4865 {
4866 size_t cchDefine;
4867 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
4868 if (pchDefine)
4869 {
4870 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
4871 if (rcExit == RTEXITCODE_SUCCESS)
4872 {
4873 /*
4874 * Evaluate it.
4875 */
4876 VBCPPEVAL enmEval;
4877 if (vbcppMacroExists(pThis, pchDefine, cchDefine))
4878 enmEval = kVBCppEval_True;
4879 else if ( !pThis->fUndecidedConditionals
4880 || RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
4881 enmEval = kVBCppEval_False;
4882 else
4883 enmEval = kVBCppEval_Undecided;
4884 rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfDef, enmEval,
4885 pchDefine, cchDefine);
4886 }
4887 }
4888 else
4889 rcExit = vbcppError(pThis, "Malformed #ifdef");
4890 }
4891 return rcExit;
4892}
4893
4894
4895/**
4896 * Processes a abbreviated line number directive.
4897 *
4898 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4899 * @param pThis The C preprocessor instance.
4900 * @param pStrmInput The input stream.
4901 * @param offStart The stream position where the directive
4902 * started (for pass thru).
4903 */
4904static RTEXITCODE vbcppDirectiveIfNDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
4905{
4906 /*
4907 * Parse it.
4908 */
4909 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
4910 if (rcExit == RTEXITCODE_SUCCESS)
4911 {
4912 size_t cchDefine;
4913 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
4914 if (pchDefine)
4915 {
4916 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
4917 if (rcExit == RTEXITCODE_SUCCESS)
4918 {
4919 /*
4920 * Evaluate it.
4921 */
4922 VBCPPEVAL enmEval;
4923 if (vbcppMacroExists(pThis, pchDefine, cchDefine))
4924 enmEval = kVBCppEval_False;
4925 else if ( !pThis->fUndecidedConditionals
4926 || RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
4927 enmEval = kVBCppEval_True;
4928 else
4929 enmEval = kVBCppEval_Undecided;
4930 rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfNDef, enmEval,
4931 pchDefine, cchDefine);
4932 }
4933 }
4934 else
4935 rcExit = vbcppError(pThis, "Malformed #ifndef");
4936 }
4937 return rcExit;
4938}
4939
4940
4941/**
4942 * Processes a abbreviated line number directive.
4943 *
4944 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
4945 * @param pThis The C preprocessor instance.
4946 * @param pStrmInput The input stream.
4947 * @param offStart The stream position where the directive
4948 * started (for pass thru).
4949 */
4950static RTEXITCODE vbcppDirectiveElse(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
4951{
4952 /*
4953 * Nothing to parse, just comment positions to find and note down.
4954 */
4955 offStart = vbcppProcessSkipWhite(pStrmInput);
4956 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
4957 if (rcExit == RTEXITCODE_SUCCESS)
4958 {
4959 /*
4960 * Execute.
4961 */
4962 PVBCPPCOND pCond = pThis->pCondStack;
4963 if (pCond)
4964 {
4965 if (!pCond->fSeenElse)
4966 {
4967 pCond->fSeenElse = true;
4968 if ( pCond->enmResult != kVBCppEval_Undecided
4969 && ( !pCond->pUp
4970 || pCond->pUp->enmStackResult == kVBCppEval_True))
4971 {
4972 if ( pCond->enmResult == kVBCppEval_True
4973 || pCond->fElIfDecided)
4974
4975 pCond->enmStackResult = kVBCppEval_False;
4976 else
4977 pCond->enmStackResult = kVBCppEval_True;
4978 pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
4979 }
4980
4981 /*
4982 * Do pass thru.
4983 */
4984 if ( !pThis->fIf0Mode
4985 && pCond->enmResult == kVBCppEval_Undecided)
4986 {
4987 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*selse", pCond->iKeepLevel - 1, "");
4988 if (cch > 0)
4989 rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 2);
4990 else
4991 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
4992 }
4993 else
4994 pThis->fJustDroppedLine = true;
4995 }
4996 else
4997 rcExit = vbcppError(pThis, "Double #else or/and missing #endif");
4998 }
4999 else
5000 rcExit = vbcppError(pThis, "#else without #if");
5001 }
5002 return rcExit;
5003}
5004
5005
5006/**
5007 * Processes a abbreviated line number directive.
5008 *
5009 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5010 * @param pThis The C preprocessor instance.
5011 * @param pStrmInput The input stream.
5012 * @param offStart The stream position where the directive
5013 * started (for pass thru).
5014 */
5015static RTEXITCODE vbcppDirectiveEndif(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
5016{
5017 /*
5018 * Nothing to parse, just comment positions to find and note down.
5019 */
5020 offStart = vbcppProcessSkipWhite(pStrmInput);
5021 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
5022 if (rcExit == RTEXITCODE_SUCCESS)
5023 {
5024 /*
5025 * Execute.
5026 */
5027 PVBCPPCOND pCond = pThis->pCondStack;
5028 if (pCond)
5029 {
5030 pThis->pCondStack = pCond->pUp;
5031 pThis->fIf0Mode = pCond->pUp && pCond->pUp->enmStackResult == kVBCppEval_False;
5032
5033 /*
5034 * Do pass thru.
5035 */
5036 if ( !pThis->fIf0Mode
5037 && pCond->enmResult == kVBCppEval_Undecided)
5038 {
5039 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sendif", pCond->iKeepLevel - 1, "");
5040 if (cch > 0)
5041 rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 1);
5042 else
5043 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
5044 }
5045 else
5046 pThis->fJustDroppedLine = true;
5047 }
5048 else
5049 rcExit = vbcppError(pThis, "#endif without #if");
5050 }
5051 return rcExit;
5052}
5053
5054
5055
5056
5057
5058/*
5059 *
5060 *
5061 * Misc Directives
5062 * Misc Directives
5063 * Misc Directives
5064 * Misc Directives
5065 *
5066 *
5067 */
5068
5069
5070/**
5071 * Adds an include directory.
5072 *
5073 * @returns Program exit code, with error message on failure.
5074 * @param pThis The C preprocessor instance.
5075 * @param pszDir The directory to add.
5076 */
5077static RTEXITCODE vbcppAddInclude(PVBCPP pThis, const char *pszDir)
5078{
5079 uint32_t cIncludes = pThis->cIncludes;
5080 if (cIncludes >= _64K)
5081 return vbcppError(pThis, "Too many include directories");
5082
5083 void *pv = RTMemRealloc(pThis->papszIncludes, (cIncludes + 1) * sizeof(char **));
5084 if (!pv)
5085 return vbcppError(pThis, "No memory for include directories");
5086 pThis->papszIncludes = (char **)pv;
5087
5088 int rc = RTStrDupEx(&pThis->papszIncludes[cIncludes], pszDir);
5089 if (RT_FAILURE(rc))
5090 return vbcppError(pThis, "No string memory for include directories");
5091
5092 pThis->cIncludes = cIncludes + 1;
5093 return RTEXITCODE_SUCCESS;
5094}
5095
5096
5097/**
5098 * Processes a abbreviated line number directive.
5099 *
5100 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5101 * @param pThis The C preprocessor instance.
5102 * @param pStrmInput The input stream.
5103 * @param offStart The stream position where the directive
5104 * started (for pass thru).
5105 */
5106static RTEXITCODE vbcppDirectiveInclude(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
5107{
5108 RT_NOREF_PV(offStart);
5109
5110 /*
5111 * Parse it.
5112 */
5113 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
5114 if (rcExit == RTEXITCODE_SUCCESS)
5115 {
5116 size_t cchFileSpec = 0;
5117 const char *pchFileSpec = NULL;
5118 size_t cchFilename = 0;
5119 const char *pchFilename = NULL;
5120
5121 unsigned ch = ScmStreamPeekCh(pStrmInput);
5122 unsigned chType = ch;
5123 if (ch == '"' || ch == '<')
5124 {
5125 ScmStreamGetCh(pStrmInput);
5126 pchFileSpec = pchFilename = ScmStreamGetCur(pStrmInput);
5127 unsigned chEnd = chType == '<' ? '>' : '"';
5128 while ( (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0
5129 && ch != chEnd)
5130 {
5131 if (ch == '\r' || ch == '\n')
5132 {
5133 rcExit = vbcppError(pThis, "Multi-line include file specfications are not supported");
5134 break;
5135 }
5136 }
5137
5138 if (rcExit == RTEXITCODE_SUCCESS)
5139 {
5140 if (ch != ~(unsigned)0)
5141 cchFileSpec = cchFilename = ScmStreamGetCur(pStrmInput) - pchFilename - 1;
5142 else
5143 rcExit = vbcppError(pThis, "Expected '%c'", chType);
5144 }
5145 }
5146 else if (vbcppIsCIdentifierLeadChar(ch))
5147 {
5148 //pchFileSpec = ScmStreamCGetWord(pStrmInput, &cchFileSpec);
5149 rcExit = vbcppError(pThis, "Including via a define is not implemented yet");
5150 }
5151 else
5152 rcExit = vbcppError(pThis, "Malformed include directive");
5153
5154 /*
5155 * Take down the location of the next non-white space, in case we need
5156 * to pass thru the directive further down. Then skip to the end of the
5157 * line.
5158 */
5159 size_t const offIncEnd = vbcppProcessSkipWhite(pStrmInput);
5160 if (rcExit == RTEXITCODE_SUCCESS)
5161 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
5162
5163 if (rcExit == RTEXITCODE_SUCCESS)
5164 {
5165 /*
5166 * Execute it.
5167 */
5168 if (pThis->enmIncludeAction == kVBCppIncludeAction_Include)
5169 {
5170 /** @todo Search for the include file and push it onto the input stack.
5171 * Not difficult, just unnecessary rigth now. */
5172 rcExit = vbcppError(pThis, "Sorry, includes are not yet implemented");
5173 }
5174 else if (pThis->enmIncludeAction == kVBCppIncludeAction_PassThru)
5175 {
5176 /* Pretty print the passthru. */
5177 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
5178 ssize_t cch;
5179 if (chType == '<')
5180 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude <%.*s>",
5181 cchIndent, "", cchFileSpec, pchFileSpec);
5182 else if (chType == '"')
5183 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude \"%.*s\"",
5184 cchIndent, "", cchFileSpec, pchFileSpec);
5185 else
5186 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude %.*s",
5187 cchIndent, "", cchFileSpec, pchFileSpec);
5188 if (cch > 0)
5189 rcExit = vbcppOutputComment(pThis, pStrmInput, offIncEnd, cch, 1);
5190 else
5191 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
5192
5193 }
5194 else
5195 {
5196 Assert(pThis->enmIncludeAction == kVBCppIncludeAction_Drop);
5197 pThis->fJustDroppedLine = true;
5198 }
5199 }
5200 }
5201 return rcExit;
5202}
5203
5204
5205/**
5206 * Processes a abbreviated line number directive.
5207 *
5208 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5209 * @param pThis The C preprocessor instance.
5210 * @param pStrmInput The input stream.
5211 * @param offStart The stream position where the directive
5212 * started (for pass thru).
5213 */
5214static RTEXITCODE vbcppDirectivePragma(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
5215{
5216 RT_NOREF_PV(offStart);
5217
5218 /*
5219 * Parse out the first word.
5220 */
5221 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
5222 if (rcExit == RTEXITCODE_SUCCESS)
5223 {
5224 size_t cchPragma;
5225 const char *pchPragma = ScmStreamCGetWord(pStrmInput, &cchPragma);
5226 if (pchPragma)
5227 {
5228 size_t const off2nd = vbcppProcessSkipWhite(pStrmInput);
5229 size_t offComment;
5230 rcExit = vbcppInputSkipToEndOfDirectiveLine(pThis, pStrmInput, &offComment);
5231 if (rcExit == RTEXITCODE_SUCCESS)
5232 {
5233 /*
5234 * What to do about this
5235 */
5236 bool fPassThru = false;
5237 if ( cchPragma == 1
5238 && *pchPragma == 'D')
5239 fPassThru = pThis->fPassThruPragmaD;
5240 else if ( cchPragma == 3
5241 && !strncmp(pchPragma, "STD", 3))
5242 fPassThru = pThis->fPassThruPragmaSTD;
5243 else
5244 fPassThru = pThis->fPassThruPragmaOther;
5245 if (fPassThru)
5246 {
5247 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
5248 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*spragma %.*s",
5249 cchIndent, "", cchPragma, pchPragma);
5250 if (cch > 0)
5251 rcExit = vbcppOutputComment(pThis, pStrmInput, off2nd, cch, 1);
5252 else
5253 rcExit = vbcppError(pThis, "output error");
5254 }
5255 else
5256 pThis->fJustDroppedLine = true;
5257 }
5258 }
5259 else
5260 rcExit = vbcppError(pThis, "Malformed #pragma");
5261 }
5262
5263 return rcExit;
5264}
5265
5266
5267/**
5268 * Processes an error directive.
5269 *
5270 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5271 * @param pThis The C preprocessor instance.
5272 * @param pStrmInput The input stream.
5273 * @param offStart The stream position where the directive
5274 * started (for pass thru).
5275 */
5276static RTEXITCODE vbcppDirectiveError(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
5277{
5278 RT_NOREF(offStart);
5279
5280 /*
5281 * Parse out the message.
5282 */
5283 size_t const off1st = vbcppProcessSkipWhite(pStrmInput);
5284 size_t offComment;
5285 RTEXITCODE rcExit = vbcppInputSkipToEndOfDirectiveLine(pThis, pStrmInput, &offComment);
5286 if (rcExit == RTEXITCODE_SUCCESS)
5287 {
5288 /*
5289 * What to do about this
5290 */
5291 if (pThis->fPassThruError)
5292 {
5293 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
5294 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*serror", cchIndent, "");
5295 if (cch > 0)
5296 rcExit = vbcppOutputComment(pThis, pStrmInput, off1st, cch, 1);
5297 else
5298 rcExit = vbcppError(pThis, "output error");
5299 return RTEXITCODE_SUCCESS;
5300 }
5301 return vbcppError(pThis, "Hit an #error");
5302 }
5303 RT_NOREF_PV(offStart);
5304 vbcppError(pThis, "Malformed #error");
5305 return vbcppError(pThis, "Hit an #error");
5306}
5307
5308
5309/**
5310 * Processes a abbreviated line number directive.
5311 *
5312 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5313 * @param pThis The C preprocessor instance.
5314 * @param pStrmInput The input stream.
5315 * @param offStart The stream position where the directive
5316 * started (for pass thru).
5317 */
5318static RTEXITCODE vbcppDirectiveLineNo(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
5319{
5320 RT_NOREF_PV(offStart);
5321 RT_NOREF_PV(pStrmInput);
5322 return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
5323}
5324
5325
5326/**
5327 * Processes a abbreviated line number directive.
5328 *
5329 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5330 * @param pThis The C preprocessor instance.
5331 * @param pStrmInput The input stream.
5332 */
5333static RTEXITCODE vbcppDirectiveLineNoShort(PVBCPP pThis, PSCMSTREAM pStrmInput)
5334{
5335 RT_NOREF_PV(pStrmInput);
5336 return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
5337}
5338
5339
5340/**
5341 * Handles a preprocessor directive.
5342 *
5343 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
5344 * @param pThis The C preprocessor instance.
5345 * @param pStrmInput The input stream.
5346 */
5347static RTEXITCODE vbcppProcessDirective(PVBCPP pThis, PSCMSTREAM pStrmInput)
5348{
5349 /*
5350 * Get the directive and do a string switch on it.
5351 */
5352 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
5353 if (rcExit != RTEXITCODE_SUCCESS)
5354 return rcExit;
5355 size_t cchDirective;
5356 const char *pchDirective = ScmStreamCGetWord(pStrmInput, &cchDirective);
5357 if (pchDirective)
5358 {
5359 size_t const offStart = ScmStreamTell(pStrmInput);
5360#define IS_DIRECTIVE(a_sz) ( sizeof(a_sz) - 1 == cchDirective && memcmp(pchDirective, a_sz, sizeof(a_sz) - 1) == 0)
5361 if (IS_DIRECTIVE("if"))
5362 rcExit = vbcppDirectiveIfOrElif(pThis, pStrmInput, offStart, kVBCppCondKind_If);
5363 else if (IS_DIRECTIVE("elif"))
5364 rcExit = vbcppDirectiveIfOrElif(pThis, pStrmInput, offStart, kVBCppCondKind_ElIf);
5365 else if (IS_DIRECTIVE("ifdef"))
5366 rcExit = vbcppDirectiveIfDef(pThis, pStrmInput, offStart);
5367 else if (IS_DIRECTIVE("ifndef"))
5368 rcExit = vbcppDirectiveIfNDef(pThis, pStrmInput, offStart);
5369 else if (IS_DIRECTIVE("else"))
5370 rcExit = vbcppDirectiveElse(pThis, pStrmInput, offStart);
5371 else if (IS_DIRECTIVE("endif"))
5372 rcExit = vbcppDirectiveEndif(pThis, pStrmInput, offStart);
5373 else if (!pThis->fIf0Mode)
5374 {
5375 if (IS_DIRECTIVE("include"))
5376 rcExit = vbcppDirectiveInclude(pThis, pStrmInput, offStart);
5377 else if (IS_DIRECTIVE("define"))
5378 rcExit = vbcppDirectiveDefine(pThis, pStrmInput, offStart);
5379 else if (IS_DIRECTIVE("undef"))
5380 rcExit = vbcppDirectiveUndef(pThis, pStrmInput, offStart);
5381 else if (IS_DIRECTIVE("pragma"))
5382 rcExit = vbcppDirectivePragma(pThis, pStrmInput, offStart);
5383 else if (IS_DIRECTIVE("error"))
5384 rcExit = vbcppDirectiveError(pThis, pStrmInput, offStart);
5385 else if (IS_DIRECTIVE("line"))
5386 rcExit = vbcppDirectiveLineNo(pThis, pStrmInput, offStart);
5387 else
5388 rcExit = vbcppError(pThis, "Unknown preprocessor directive '#%.*s'", cchDirective, pchDirective);
5389 }
5390#undef IS_DIRECTIVE
5391 }
5392 else if (!pThis->fIf0Mode)
5393 {
5394 /* Could it be a # <num> "file" directive? */
5395 unsigned ch = ScmStreamPeekCh(pStrmInput);
5396 if (RT_C_IS_DIGIT(ch))
5397 rcExit = vbcppDirectiveLineNoShort(pThis, pStrmInput);
5398 else
5399 rcExit = vbcppError(pThis, "Malformed preprocessor directive");
5400 }
5401 return rcExit;
5402}
5403
5404
5405/*
5406 *
5407 *
5408 * M a i n b o d y.
5409 * M a i n b o d y.
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 *
5414 *
5415 */
5416
5417
5418/**
5419 * Does the actually preprocessing of the input file.
5420 *
5421 * @returns Exit code.
5422 * @param pThis The C preprocessor instance.
5423 */
5424static RTEXITCODE vbcppPreprocess(PVBCPP pThis)
5425{
5426 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
5427
5428 /*
5429 * Parse.
5430 */
5431 while (pThis->pInputStack)
5432 {
5433 pThis->fMaybePreprocessorLine = true;
5434
5435 PSCMSTREAM pStrmInput = &pThis->pInputStack->StrmInput;
5436 unsigned ch;
5437 while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
5438 {
5439 if (ch == '/')
5440 {
5441 ch = ScmStreamPeekCh(pStrmInput);
5442 if (ch == '*')
5443 rcExit = vbcppProcessMultiLineComment(pThis, pStrmInput);
5444 else if (ch == '/')
5445 rcExit = vbcppProcessOneLineComment(pThis, pStrmInput);
5446 else
5447 {
5448 pThis->fMaybePreprocessorLine = false;
5449 if (!pThis->fIf0Mode)
5450 rcExit = vbcppOutputCh(pThis, '/');
5451 }
5452 }
5453 else if (ch == '#' && pThis->fMaybePreprocessorLine)
5454 {
5455 rcExit = vbcppProcessDirective(pThis, pStrmInput);
5456 pStrmInput = &pThis->pInputStack->StrmInput;
5457 }
5458 else if (ch == '\r' || ch == '\n')
5459 {
5460 if ( ( !pThis->fIf0Mode
5461 && !pThis->fJustDroppedLine)
5462 || !pThis->fRemoveDroppedLines
5463 || !ScmStreamIsAtStartOfLine(&pThis->StrmOutput))
5464 rcExit = vbcppOutputCh(pThis, ch);
5465 pThis->fJustDroppedLine = false;
5466 pThis->fMaybePreprocessorLine = true;
5467 }
5468 else if (RT_C_IS_SPACE(ch))
5469 {
5470 if (!pThis->fIf0Mode)
5471 rcExit = vbcppOutputCh(pThis, ch);
5472 }
5473 else
5474 {
5475 pThis->fMaybePreprocessorLine = false;
5476 if (!pThis->fIf0Mode)
5477 {
5478 if (ch == '"')
5479 rcExit = vbcppProcessStringLitteral(pThis, pStrmInput);
5480 else if (ch == '\'')
5481 rcExit = vbcppProcessCharacterConstant(pThis, pStrmInput);
5482 else if (vbcppIsCIdentifierLeadChar(ch))
5483 rcExit = vbcppProcessIdentifier(pThis, pStrmInput);
5484 else if (RT_C_IS_DIGIT(ch))
5485 rcExit = vbcppProcessNumber(pThis, pStrmInput, ch);
5486 else
5487 rcExit = vbcppOutputCh(pThis, ch);
5488 }
5489 }
5490 if (rcExit != RTEXITCODE_SUCCESS)
5491 break;
5492 }
5493
5494 /*
5495 * Check for errors.
5496 */
5497 if (rcExit != RTEXITCODE_SUCCESS)
5498 break;
5499
5500 /*
5501 * Pop the input stack.
5502 */
5503 PVBCPPINPUT pPopped = pThis->pInputStack;
5504 pThis->pInputStack = pPopped->pUp;
5505 RTMemFree(pPopped);
5506 }
5507
5508 return rcExit;
5509}
5510
5511
5512/**
5513 * Opens the input and output streams.
5514 *
5515 * @returns Exit code.
5516 * @param pThis The C preprocessor instance.
5517 */
5518static RTEXITCODE vbcppOpenStreams(PVBCPP pThis)
5519{
5520 const char * const pszInput = pThis->pszInput ? pThis->pszInput : "stdin";
5521 size_t const cchName = strlen(pszInput);
5522 PVBCPPINPUT const pInput = (PVBCPPINPUT)RTMemAlloc(RT_UOFFSETOF_DYN(VBCPPINPUT, szName[cchName + 1]));
5523 if (!pInput)
5524 return vbcppError(pThis, "out of memory");
5525 pInput->pUp = pThis->pInputStack;
5526 pInput->pszSpecified = pInput->szName;
5527 memcpy(pInput->szName, pszInput, cchName + 1);
5528 pThis->pInputStack = pInput;
5529 int rc;
5530 if (pThis->pszInput)
5531 {
5532 rc = ScmStreamInitForReading(&pInput->StrmInput, pThis->pszInput);
5533 if (RT_FAILURE(rc))
5534 return vbcppError(pThis, "ScmStreamInitForReading returned %Rrc when opening and reading in input file (%s)",
5535 rc, pThis->pszInput);
5536 }
5537 else
5538 {
5539 rc = ScmStreamInitForReadingFromStdInput(&pInput->StrmInput);
5540 if (RT_FAILURE(rc))
5541 return vbcppError(pThis, "ScmStreamInitForReadingFromStdInput returned %Rrc", rc);
5542
5543 }
5544
5545 rc = ScmStreamInitForWriting(&pThis->StrmOutput, &pInput->StrmInput);
5546 if (RT_FAILURE(rc))
5547 return vbcppError(pThis, "ScmStreamInitForWriting returned %Rrc", rc);
5548
5549 pThis->fStrmOutputValid = true;
5550 return RTEXITCODE_SUCCESS;
5551}
5552
5553
5554/**
5555 * Changes the preprocessing mode.
5556 *
5557 * @param pThis The C preprocessor instance.
5558 * @param enmMode The new mode.
5559 */
5560static void vbcppSetMode(PVBCPP pThis, VBCPPMODE enmMode)
5561{
5562 switch (enmMode)
5563 {
5564 case kVBCppMode_Standard:
5565 pThis->fKeepComments = false;
5566 pThis->fRespectSourceDefines = true;
5567 pThis->fAllowRedefiningCmdLineDefines = true;
5568 pThis->fPassThruDefines = false;
5569 pThis->fUndecidedConditionals = false;
5570 pThis->fPassThruPragmaD = false;
5571 pThis->fPassThruPragmaSTD = true;
5572 pThis->fPassThruPragmaOther = true;
5573 pThis->fPassThruError = false;
5574 pThis->fRemoveDroppedLines = false;
5575 pThis->fLineSplicing = true;
5576 pThis->enmIncludeAction = kVBCppIncludeAction_Include;
5577 break;
5578
5579 case kVBCppMode_Selective:
5580 pThis->fKeepComments = true;
5581 pThis->fRespectSourceDefines = false;
5582 pThis->fAllowRedefiningCmdLineDefines = false;
5583 pThis->fPassThruDefines = true;
5584 pThis->fUndecidedConditionals = true;
5585 pThis->fPassThruPragmaD = true;
5586 pThis->fPassThruPragmaSTD = true;
5587 pThis->fPassThruPragmaOther = true;
5588 pThis->fPassThruError = true;
5589 pThis->fRemoveDroppedLines = true;
5590 pThis->fLineSplicing = false;
5591 pThis->enmIncludeAction = kVBCppIncludeAction_PassThru;
5592 break;
5593
5594 case kVBCppMode_SelectiveD:
5595 pThis->fKeepComments = true;
5596 pThis->fRespectSourceDefines = true;
5597 pThis->fAllowRedefiningCmdLineDefines = false;
5598 pThis->fPassThruDefines = false;
5599 pThis->fUndecidedConditionals = false;
5600 pThis->fPassThruPragmaD = true;
5601 pThis->fPassThruPragmaSTD = false;
5602 pThis->fPassThruPragmaOther = false;
5603 pThis->fPassThruError = false;
5604 pThis->fRemoveDroppedLines = true;
5605 pThis->fLineSplicing = false;
5606 pThis->enmIncludeAction = kVBCppIncludeAction_Drop;
5607 break;
5608
5609 default:
5610 AssertFailedReturnVoid();
5611 }
5612 pThis->enmMode = enmMode;
5613}
5614
5615
5616/**
5617 * Parses the command line options.
5618 *
5619 * @returns Program exit code. Exit on non-success or if *pfExit is set.
5620 * @param pThis The C preprocessor instance.
5621 * @param argc The argument count.
5622 * @param argv The argument vector.
5623 * @param pfExit Pointer to the exit indicator.
5624 */
5625static RTEXITCODE vbcppParseOptions(PVBCPP pThis, int argc, char **argv, bool *pfExit)
5626{
5627 RTEXITCODE rcExit;
5628
5629 *pfExit = false;
5630
5631 /*
5632 * Option config.
5633 */
5634 static RTGETOPTDEF const s_aOpts[] =
5635 {
5636 { "--define", 'D', RTGETOPT_REQ_STRING },
5637 { "--include-dir", 'I', RTGETOPT_REQ_STRING },
5638 { "--undefine", 'U', RTGETOPT_REQ_STRING },
5639 { "--keep-comments", 'C', RTGETOPT_REQ_NOTHING },
5640 { "--strip-comments", 'c', RTGETOPT_REQ_NOTHING },
5641 { "--D-strip", 'd', RTGETOPT_REQ_NOTHING },
5642#define OPT_MODE 1000
5643 { "--mode", OPT_MODE, RTGETOPT_REQ_STRING },
5644 };
5645
5646 RTGETOPTUNION ValueUnion;
5647 RTGETOPTSTATE GetOptState;
5648 int rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
5649 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
5650
5651 /*
5652 * Process the options.
5653 */
5654 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
5655 {
5656 switch (rc)
5657 {
5658 case 'c':
5659 pThis->fKeepComments = false;
5660 break;
5661
5662 case 'C':
5663 pThis->fKeepComments = false;
5664 break;
5665
5666 case 'd':
5667 vbcppSetMode(pThis, kVBCppMode_SelectiveD);
5668 break;
5669
5670 case OPT_MODE:
5671 if ( strcmp(ValueUnion.psz, "standard") == 0
5672 || strcmp(ValueUnion.psz, "std") == 0)
5673 vbcppSetMode(pThis, kVBCppMode_Standard);
5674 else if ( strcmp(ValueUnion.psz, "selective") == 0
5675 || strcmp(ValueUnion.psz, "sel") == 0)
5676 vbcppSetMode(pThis, kVBCppMode_Selective);
5677 else if ( strcmp(ValueUnion.psz, "selective-D") == 0
5678 || strcmp(ValueUnion.psz, "D-strip") == 0)
5679 vbcppSetMode(pThis, kVBCppMode_SelectiveD);
5680 else
5681 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown mode '%s'!", ValueUnion.psz);
5682 break;
5683
5684 case 'D':
5685 {
5686 const char *pszEqual = strchr(ValueUnion.psz, '=');
5687 if (pszEqual)
5688 rcExit = vbcppMacroAdd(pThis, ValueUnion.psz, pszEqual - ValueUnion.psz, pszEqual + 1, RTSTR_MAX, true);
5689 else
5690 rcExit = vbcppMacroAdd(pThis, ValueUnion.psz, RTSTR_MAX, "1", 1, true);
5691 if (rcExit != RTEXITCODE_SUCCESS)
5692 return rcExit;
5693 break;
5694 }
5695
5696 case 'I':
5697 rcExit = vbcppAddInclude(pThis, ValueUnion.psz);
5698 if (rcExit != RTEXITCODE_SUCCESS)
5699 return rcExit;
5700 break;
5701
5702 case 'U':
5703 rcExit = vbcppMacroUndef(pThis, ValueUnion.psz, RTSTR_MAX, true);
5704 break;
5705
5706 case 'h':
5707 RTPrintf("Usage: VBoxCPP [options] <input.c> [output.ii]\n"
5708 "\n"
5709 "Options:\n"
5710 " --mode={mode}\n"
5711 " Sets the processing mode and asscoiated defaults:\n"
5712 " - selective / sel: (default)\n"
5713 " Selectively applying the defines from the command line\n"
5714 " and preserving the rest.\n"
5715 " - selective-D / D-strip:\n"
5716 " Selectively applying the defines from the command line\n"
5717 " and removing things DTrace doesn't like.\n"
5718 " - standard / std:\n"
5719 " Standard C preprocesing. This is incomplete as, among other\n"
5720 " things, include processing has not yet been implemented.\n"
5721 " --D-strip\n"
5722 " Alias for --mode=selective-D\n"
5723 " --define={macro}[=val], -D{macro}[=val]\n"
5724 " Defines a macro.\n"
5725 " --undefine={macro}, -U{macro}\n"
5726 " Undefines a macro.\n"
5727 " --include-dir={dir}, -I{dir}\n"
5728 " Adds {dir} to the list of include directories that should\n"
5729 " be searched for header files."
5730 " --keep-comments, -C\n"
5731 " Keep comments unchanged in the output.\n"
5732 " --strip-comments, -c\n"
5733 " Strip comments in the output. (default)\n"
5734 "\n"
5735 "This is not a complete C preprocessor implementation (yet), as its use is\n"
5736 "mainly to selectively apply and eliminate defines and/or things are DTrace\n"
5737 "finds problematic. Among the missing features are:\n"
5738 " - Support for ternary expressions.\n"
5739 " - Including header files (in standard mode).\n"
5740 " - +++\n"
5741 );
5742 *pfExit = true;
5743 return RTEXITCODE_SUCCESS;
5744
5745 case 'V':
5746 {
5747 /* The following is assuming that svn does it's job here. */
5748 static const char s_szRev[] = "$Revision: 103384 $";
5749 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
5750 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
5751 *pfExit = true;
5752 return RTEXITCODE_SUCCESS;
5753 }
5754
5755 case VINF_GETOPT_NOT_OPTION:
5756 if (!pThis->pszInput)
5757 pThis->pszInput = ValueUnion.psz;
5758 else if (!pThis->pszOutput)
5759 pThis->pszOutput = ValueUnion.psz;
5760 else
5761 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "too many file arguments");
5762 break;
5763
5764
5765 /*
5766 * Errors and bugs.
5767 */
5768 default:
5769 return RTGetOptPrintError(rc, &ValueUnion);
5770 }
5771 }
5772
5773 return RTEXITCODE_SUCCESS;
5774}
5775
5776
5777/**
5778 * Terminates the preprocessor.
5779 *
5780 * This may return failure if an error was delayed.
5781 *
5782 * @returns Exit code.
5783 * @param pThis The C preprocessor instance.
5784 */
5785static RTEXITCODE vbcppTerm(PVBCPP pThis)
5786{
5787 /*
5788 * Flush the output first.
5789 */
5790 if (pThis->fStrmOutputValid)
5791 {
5792 if (pThis->pszOutput)
5793 {
5794 int rc = ScmStreamWriteToFile(&pThis->StrmOutput, "%s", pThis->pszOutput);
5795 if (RT_FAILURE(rc))
5796 vbcppError(pThis, "ScmStreamWriteToFile failed with %Rrc when writing '%s'", rc, pThis->pszOutput);
5797 }
5798 else
5799 {
5800 int rc = ScmStreamWriteToStdOut(&pThis->StrmOutput);
5801 if (RT_FAILURE(rc))
5802 vbcppError(pThis, "ScmStreamWriteToStdOut failed with %Rrc", rc);
5803 }
5804 }
5805
5806 /*
5807 * Cleanup.
5808 */
5809 while (pThis->pInputStack)
5810 {
5811 ScmStreamDelete(&pThis->pInputStack->StrmInput);
5812 void *pvFree = pThis->pInputStack;
5813 pThis->pInputStack = pThis->pInputStack->pUp;
5814 RTMemFree(pvFree);
5815 }
5816
5817 ScmStreamDelete(&pThis->StrmOutput);
5818
5819 RTStrSpaceDestroy(&pThis->StrSpace, vbcppMacroFree, NULL);
5820 pThis->StrSpace = NULL;
5821
5822 uint32_t i = pThis->cIncludes;
5823 while (i-- > 0)
5824 RTStrFree(pThis->papszIncludes[i]);
5825 RTMemFree(pThis->papszIncludes);
5826 pThis->papszIncludes = NULL;
5827
5828 return pThis->rcExit;
5829}
5830
5831
5832/**
5833 * Initializes the C preprocessor instance data.
5834 *
5835 * @param pThis The C preprocessor instance data.
5836 */
5837static void vbcppInit(PVBCPP pThis)
5838{
5839 vbcppSetMode(pThis, kVBCppMode_Selective);
5840 pThis->cIncludes = 0;
5841 pThis->papszIncludes = NULL;
5842 pThis->pszInput = NULL;
5843 pThis->pszOutput = NULL;
5844 pThis->StrSpace = NULL;
5845 pThis->UndefStrSpace = NULL;
5846 pThis->cCondStackDepth = 0;
5847 pThis->pCondStack = NULL;
5848 pThis->fIf0Mode = false;
5849 pThis->fJustDroppedLine = false;
5850 pThis->fMaybePreprocessorLine = true;
5851 VBCPP_BITMAP_EMPTY(pThis->bmDefined);
5852 pThis->cCondStackDepth = 0;
5853 pThis->pInputStack = NULL;
5854 RT_ZERO(pThis->StrmOutput);
5855 pThis->rcExit = RTEXITCODE_SUCCESS;
5856 pThis->fStrmOutputValid = false;
5857}
5858
5859
5860
5861int main(int argc, char **argv)
5862{
5863 int rc = RTR3InitExe(argc, &argv, 0);
5864 if (RT_FAILURE(rc))
5865 return RTMsgInitFailure(rc);
5866
5867 /*
5868 * Do the job. The code says it all.
5869 */
5870 VBCPP This;
5871 vbcppInit(&This);
5872 bool fExit;
5873 RTEXITCODE rcExit = vbcppParseOptions(&This, argc, argv, &fExit);
5874 if (!fExit && rcExit == RTEXITCODE_SUCCESS)
5875 {
5876 rcExit = vbcppOpenStreams(&This);
5877 if (rcExit == RTEXITCODE_SUCCESS)
5878 rcExit = vbcppPreprocess(&This);
5879 }
5880
5881 if (rcExit == RTEXITCODE_SUCCESS)
5882 rcExit = vbcppTerm(&This);
5883 else
5884 vbcppTerm(&This);
5885 return rcExit;
5886}
5887
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use