VirtualBox

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

Last change on this file since 77235 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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