VirtualBox

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

Last change on this file since 41301 was 41301, checked in by vboxsync, 13 years ago

recursive expansion needs to be prevent in a different way.

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