VirtualBox

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

Last change on this file since 62527 was 62452, checked in by vboxsync, 8 years ago

*: MSC level 4 warnings in release bldprogs.

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