VirtualBox

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

Last change on this file since 106724 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette