VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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