VirtualBox

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

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

VBoxCPP: Empty macro detection bug spotted by MSC & Vitali.

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