VirtualBox

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

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

Moved the code around in a more logical order.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 72.3 KB
Line 
1/* $Id: VBoxCPP.cpp 41204 2012-05-08 12:14:06Z 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/**
72 * The preprocessor mode.
73 */
74typedef enum VBCPPMODE
75{
76 kVBCppMode_Invalid = 0,
77 kVBCppMode_Standard,
78 kVBCppMode_Selective,
79 kVBCppMode_SelectiveD,
80 kVBCppMode_End
81} VBCPPMODE;
82
83
84/**
85 * A define.
86 */
87typedef struct VBCPPDEF
88{
89 /** The string space core. */
90 RTSTRSPACECORE Core;
91 /** Whether it's a function. */
92 bool fFunction;
93 /** Variable argument count. */
94 bool fVarArg;
95 /** Set if originating on the command line. */
96 bool fCmdLine;
97 /** The number of known arguments.*/
98 uint32_t cArgs;
99 /** Pointer to a list of argument names. */
100 const char **papszArgs;
101 /** Lead character bitmap for the argument names. */
102 VBCPP_BITMAP_TYPE bmArgs[VBCPP_BITMAP_SIZE];
103 /** The define value. (This is followed by the name and arguments.) */
104 char szValue[1];
105} VBCPPDEF;
106/** Pointer to a define. */
107typedef VBCPPDEF *PVBCPPDEF;
108
109
110/**
111 * Expansion context.
112 */
113typedef struct VBCPPCTX
114{
115 /** The next context on the stack. */
116 struct VBCPPCTX *pUp;
117 /** The define being expanded. */
118 PVBCPPDEF pDef;
119 /** Arguments. */
120 struct VBCPPCTXARG
121 {
122 /** The value. */
123 const char *pchValue;
124 /** The value length. */
125 const char *cchValue;
126 } aArgs[1];
127} VBCPPCTX;
128/** Pointer to an define expansion context. */
129typedef VBCPPCTX *PVBCPPCTX;
130
131/**
132 * Evaluation result.
133 */
134typedef enum VBCPPEVAL
135{
136 kVBCppEval_Invalid = 0,
137 kVBCppEval_True,
138 kVBCppEval_False,
139 kVBCppEval_Undecided,
140 kVBCppEval_End
141} VBCPPEVAL;
142
143
144/**
145 * The condition kind.
146 */
147typedef enum VBCPPCONDKIND
148{
149 kVBCppCondKind_Invalid = 0,
150 /** \#if expr */
151 kVBCppCondKind_If,
152 /** \#ifdef define */
153 kVBCppCondKind_IfDef,
154 /** \#ifndef define */
155 kVBCppCondKind_IfNDef,
156 /** \#elif expr */
157 kVBCppCondKind_ElIf,
158 /** The end of valid values. */
159 kVBCppCondKind_End
160} VBCPPCONDKIND;
161
162
163/**
164 * Conditional stack entry.
165 */
166typedef struct VBCPPCOND
167{
168 /** The next conditional on the stack. */
169 struct VBCPPCOND *pUp;
170 /** The kind of conditional. This changes on encountering \#elif. */
171 VBCPPCONDKIND enmKind;
172 /** Evaluation result. */
173 VBCPPEVAL enmResult;
174 /** The evaluation result of the whole stack. */
175 VBCPPEVAL enmStackResult;
176
177 /** Whether we've seen the last else. */
178 bool fSeenElse;
179 /** The nesting level of this condition. */
180 uint16_t iLevel;
181 /** The nesting level of this condition wrt the ones we keep. */
182 uint16_t iKeepLevel;
183
184 /** The condition string. (Points within the stream buffer.) */
185 const char *pchCond;
186 /** The condition length. */
187 size_t cchCond;
188} VBCPPCOND;
189/** Pointer to a conditional stack entry. */
190typedef VBCPPCOND *PVBCPPCOND;
191
192
193/**
194 * Input buffer stack entry.
195 */
196typedef struct VBCPPINPUT
197{
198 /** Pointer to the next input on the stack. */
199 struct VBCPPINPUT *pUp;
200 /** The input stream. */
201 SCMSTREAM StrmInput;
202 /** Pointer into szName to the part which was specified. */
203 const char *pszSpecified;
204 /** The input file name with include path. */
205 char szName[1];
206} VBCPPINPUT;
207/** Pointer to a input buffer stack entry */
208typedef VBCPPINPUT *PVBCPPINPUT;
209
210
211/**
212 * The action to take with \#include.
213 */
214typedef enum VBCPPINCLUDEACTION
215{
216 kVBCppIncludeAction_Invalid = 0,
217 kVBCppIncludeAction_Include,
218 kVBCppIncludeAction_PassThru,
219 kVBCppIncludeAction_Drop,
220 kVBCppIncludeAction_End
221} VBCPPINCLUDEACTION;
222
223
224/**
225 * C Preprocessor instance data.
226 */
227typedef struct VBCPP
228{
229 /** @name Options
230 * @{ */
231 /** The preprocessing mode. */
232 VBCPPMODE enmMode;
233 /** Whether to keep comments. */
234 bool fKeepComments;
235 /** Whether to respect source defines. */
236 bool fRespectSourceDefines;
237 /** Whether to let source defines overrides the ones on the command
238 * line. */
239 bool fAllowRedefiningCmdLineDefines;
240 /** Whether to pass thru defines. */
241 bool fPassThruDefines;
242 /** Whether to allow undecided conditionals. */
243 bool fUndecidedConditionals;
244 /** Whether to preforme line splicing.
245 * @todo implement line splicing */
246 bool fLineSplicing;
247 /** What to do about include files. */
248 VBCPPINCLUDEACTION enmIncludeAction;
249
250 /** The number of include directories. */
251 uint32_t cIncludes;
252 /** Array of directories to search for include files. */
253 char **papszIncludes;
254
255 /** The name of the input file. */
256 const char *pszInput;
257 /** The name of the output file. NULL if stdout. */
258 const char *pszOutput;
259 /** @} */
260
261 /** The define string space. */
262 RTSTRSPACE StrSpace;
263 /** The string space holding explicitly undefined macros for selective
264 * preprocessing runs. */
265 RTSTRSPACE UndefStrSpace;
266 /** Indicates whether a C-word might need expansion.
267 * The bitmap is indexed by C-word lead character. Bits that are set
268 * indicates that the lead character is used in a \#define that we know and
269 * should expand. */
270 VBCPP_BITMAP_TYPE bmDefined[VBCPP_BITMAP_SIZE];
271 /** Indicates whether a C-word might need argument expansion.
272 * The bitmap is indexed by C-word lead character. Bits that are set
273 * indicates that the lead character is used in an argument of an currently
274 * expanding \#define. */
275 VBCPP_BITMAP_TYPE bmArgs[VBCPP_BITMAP_SIZE];
276
277 /** Expansion context stack. */
278 PVBCPPCTX pExpStack;
279 /** The current expansion stack depth. */
280 uint32_t cExpStackDepth;
281
282 /** The current depth of the conditional stack. */
283 uint32_t cCondStackDepth;
284 /** Conditional stack. */
285 PVBCPPCOND pCondStack;
286 /** The current condition evaluates to kVBCppEval_False, don't output. */
287 bool fIf0Mode;
288
289 /** Whether the current line could be a preprocessor line.
290 * This is set when EOL is encountered and cleared again when a
291 * non-comment-or-space character is encountered. See vbcppPreprocess. */
292 bool fMaybePreprocessorLine;
293
294 /** The input stack depth */
295 uint32_t cInputStackDepth;
296 /** The input buffer stack. */
297 PVBCPPINPUT pInputStack;
298
299 /** The output stream. */
300 SCMSTREAM StrmOutput;
301
302 /** The status of the whole job, as far as we know. */
303 RTEXITCODE rcExit;
304 /** Whether StrmOutput is valid (for vbcppTerm). */
305 bool fStrmOutputValid;
306} VBCPP;
307/** Pointer to the C preprocessor instance data. */
308typedef VBCPP *PVBCPP;
309
310
311
312
313
314/*
315 *
316 *
317 * Message Handling.
318 * Message Handling.
319 * Message Handling.
320 * Message Handling.
321 * Message Handling.
322 *
323 *
324 */
325
326
327/**
328 * Displays an error message.
329 *
330 * @returns RTEXITCODE_FAILURE
331 * @param pThis The C preprocessor instance.
332 * @param pszMsg The message.
333 * @param ... Message arguments.
334 */
335static RTEXITCODE vbcppError(PVBCPP pThis, const char *pszMsg, ...)
336{
337 NOREF(pThis);
338 if (pThis->pInputStack)
339 {
340 PSCMSTREAM pStrm = &pThis->pInputStack->StrmInput;
341
342 size_t const off = ScmStreamTell(pStrm);
343 size_t const iLine = ScmStreamTellLine(pStrm);
344 ScmStreamSeekByLine(pStrm, iLine);
345 size_t const offLine = ScmStreamTell(pStrm);
346
347 va_list va;
348 va_start(va, pszMsg);
349 RTPrintf("%s:%d:%zd: error: %N.\n", pThis->pInputStack->szName, iLine + 1, off - offLine + 1, pszMsg, va);
350 va_end(va);
351
352 size_t cchLine;
353 SCMEOL enmEof;
354 const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
355 if (pszLine)
356 RTPrintf(" %.*s\n"
357 " %*s^\n",
358 cchLine, pszLine, off - offLine, "");
359
360 ScmStreamSeekAbsolute(pStrm, off);
361 }
362 else
363 {
364 va_list va;
365 va_start(va, pszMsg);
366 RTMsgErrorV(pszMsg, va);
367 va_end(va);
368 }
369 return pThis->rcExit = RTEXITCODE_FAILURE;
370}
371
372
373/**
374 * Displays an error message.
375 *
376 * @returns RTEXITCODE_FAILURE
377 * @param pThis The C preprocessor instance.
378 * @param pszPos Pointer to the offending character.
379 * @param pszMsg The message.
380 * @param ... Message arguments.
381 */
382static RTEXITCODE vbcppErrorPos(PVBCPP pThis, const char *pszPos, const char *pszMsg, ...)
383{
384 NOREF(pszPos); NOREF(pThis);
385 va_list va;
386 va_start(va, pszMsg);
387 RTMsgErrorV(pszMsg, va);
388 va_end(va);
389 return pThis->rcExit = RTEXITCODE_FAILURE;
390}
391
392
393
394
395
396/*
397 *
398 *
399 * C Identifier/Word Parsing.
400 * C Identifier/Word Parsing.
401 * C Identifier/Word Parsing.
402 * C Identifier/Word Parsing.
403 * C Identifier/Word Parsing.
404 *
405 *
406 */
407
408
409/**
410 * Checks if the given character is a valid C identifier lead character.
411 *
412 * @returns true / false.
413 * @param ch The character to inspect.
414 */
415DECLINLINE(bool) vbcppIsCIdentifierLeadChar(char ch)
416{
417 return RT_C_IS_ALPHA(ch)
418 || ch == '_';
419}
420
421
422/**
423 * Checks if the given character is a valid C identifier character.
424 *
425 * @returns true / false.
426 * @param ch The character to inspect.
427 */
428DECLINLINE(bool) vbcppIsCIdentifierChar(char ch)
429{
430 return RT_C_IS_ALNUM(ch)
431 || ch == '_';
432}
433
434
435
436/**
437 *
438 * @returns @c true if valid, @c false if not. Error message already displayed
439 * on failure.
440 * @param pThis The C preprocessor instance.
441 * @param pchIdentifier The start of the identifier to validate.
442 * @param cchIdentifier The length of the identifier. RTSTR_MAX if not
443 * known.
444 */
445static bool vbcppValidateCIdentifier(PVBCPP pThis, const char *pchIdentifier, size_t cchIdentifier)
446{
447 if (cchIdentifier == RTSTR_MAX)
448 cchIdentifier = strlen(pchIdentifier);
449
450 if (cchIdentifier == 0)
451 {
452 vbcppErrorPos(pThis, pchIdentifier, "Zero length identifier");
453 return false;
454 }
455
456 if (!vbcppIsCIdentifierLeadChar(*pchIdentifier))
457 {
458 vbcppErrorPos(pThis, pchIdentifier, "Bad lead chararacter in identifier: '%.*s'", cchIdentifier, pchIdentifier);
459 return false;
460 }
461
462 for (size_t off = 1; off < cchIdentifier; off++)
463 {
464 if (!vbcppIsCIdentifierChar(pchIdentifier[off]))
465 {
466 vbcppErrorPos(pThis, pchIdentifier + off, "Illegal chararacter in identifier: '%.*s' (#%zu)", cchIdentifier, pchIdentifier, off + 1);
467 return false;
468 }
469 }
470
471 return true;
472}
473
474
475
476
477
478
479/*
480 *
481 *
482 * Output
483 * Output
484 * Output
485 * Output
486 * Output
487 *
488 *
489 */
490
491
492/**
493 * Outputs a character.
494 *
495 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
496 * @param pThis The C preprocessor instance.
497 * @param ch The character to output.
498 */
499static RTEXITCODE vbcppOutputCh(PVBCPP pThis, char ch)
500{
501 int rc = ScmStreamPutCh(&pThis->StrmOutput, ch);
502 if (RT_SUCCESS(rc))
503 return RTEXITCODE_SUCCESS;
504 return vbcppError(pThis, "Output error: %Rrc", rc);
505}
506
507
508/**
509 * Outputs a string.
510 *
511 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
512 * @param pThis The C preprocessor instance.
513 * @param pch The string.
514 * @param cch The number of characters to write.
515 */
516static RTEXITCODE vbcppOutputWrite(PVBCPP pThis, const char *pch, size_t cch)
517{
518 int rc = ScmStreamWrite(&pThis->StrmOutput, pch, cch);
519 if (RT_SUCCESS(rc))
520 return RTEXITCODE_SUCCESS;
521 return vbcppError(pThis, "Output error: %Rrc", rc);
522}
523
524
525static RTEXITCODE vbcppOutputComment(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart, size_t cchOutputted,
526 size_t cchMinIndent)
527{
528 size_t offCur = ScmStreamTell(pStrmInput);
529 if (offStart < offCur)
530 {
531 int rc = ScmStreamSeekAbsolute(pStrmInput, offStart);
532 AssertRCReturn(rc, vbcppError(pThis, "Input seek error: %Rrc", rc));
533
534 /*
535 * Use the same indent, if possible.
536 */
537 size_t cchIndent = offStart - ScmStreamTellOffsetOfLine(pStrmInput, ScmStreamTellLine(pStrmInput));
538 if (cchOutputted < cchIndent)
539 rc = ScmStreamPrintf(&pThis->StrmOutput, "%*s", cchIndent - cchOutputted, "");
540 else
541 rc = ScmStreamPutCh(&pThis->StrmOutput, ' ');
542 if (RT_FAILURE(rc))
543 return vbcppError(pThis, "Output error: %Rrc", rc);
544
545 /*
546 * Copy the bytes.
547 */
548 while (ScmStreamTell(pStrmInput) < offCur)
549 {
550 unsigned ch = ScmStreamGetCh(pStrmInput);
551 if (ch == ~(unsigned)0)
552 return vbcppError(pThis, "Input error: %Rrc", rc);
553 rc = ScmStreamPutCh(&pThis->StrmOutput, ch);
554 if (RT_FAILURE(rc))
555 return vbcppError(pThis, "Output error: %Rrc", rc);
556 }
557 }
558
559 return RTEXITCODE_SUCCESS;
560}
561
562
563
564
565
566/*
567 *
568 *
569 * Input
570 * Input
571 * Input
572 * Input
573 * Input
574 *
575 *
576 */
577
578
579/**
580 * Skips white spaces, including escaped new-lines.
581 *
582 * @param pStrmInput The input stream.
583 */
584static void vbcppProcessSkipWhiteAndEscapedEol(PSCMSTREAM pStrmInput)
585{
586 unsigned chPrev = ~(unsigned)0;
587 unsigned ch;
588 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
589 {
590 if (ch == '\r' || ch == '\n')
591 {
592 if (chPrev != '\\')
593 break;
594 chPrev = ch;
595 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
596 }
597 else if (RT_C_IS_SPACE(ch))
598 {
599 ch = chPrev;
600 ch = ScmStreamGetCh(pStrmInput);
601 Assert(ch == chPrev);
602 }
603 else
604 break;
605 }
606}
607
608
609/**
610 * Skips white spaces, escaped new-lines and multi line comments.
611 *
612 * @param pThis The C preprocessor instance.
613 * @param pStrmInput The input stream.
614 */
615static RTEXITCODE vbcppProcessSkipWhiteEscapedEolAndComments(PVBCPP pThis, PSCMSTREAM pStrmInput)
616{
617 unsigned chPrev = ~(unsigned)0;
618 unsigned ch;
619 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
620 {
621 if (!RT_C_IS_SPACE(ch))
622 {
623 /* Multi-line Comment? */
624 if (ch != '/')
625 break; /* most definitely, not. */
626
627 size_t offSaved = ScmStreamTell(pStrmInput);
628 ScmStreamGetCh(pStrmInput);
629 if (ScmStreamPeekCh(pStrmInput) != '*')
630 {
631 ScmStreamSeekAbsolute(pStrmInput, offSaved);
632 break; /* no */
633 }
634
635 /* Skip to the end of the comment. */
636 while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
637 {
638 if (ch == '*')
639 {
640 ch = ScmStreamGetCh(pStrmInput);
641 if (ch == '/')
642 break;
643 if (ch == ~(unsigned)0)
644 break;
645 }
646 }
647 if (ch == ~(unsigned)0)
648 return vbcppError(pThis, "unterminated multi-line comment");
649 chPrev = '/';
650 }
651 /* New line (also matched by RT_C_IS_SPACE). */
652 else if (ch == '\r' || ch == '\n')
653 {
654 /* Stop if not escaped. */
655 if (chPrev != '\\')
656 break;
657 chPrev = ch;
658 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
659 }
660 /* Real space char. */
661 else
662 {
663 chPrev = ch;
664 ch = ScmStreamGetCh(pStrmInput);
665 Assert(ch == chPrev);
666 }
667 }
668 return RTEXITCODE_SUCCESS;
669}
670
671
672/**
673 * Skips white spaces, escaped new-lines, and multi line comments, then checking
674 * that we're at the end of a line.
675 *
676 * @param pThis The C preprocessor instance.
677 * @param pStrmInput The input stream.
678 */
679static RTEXITCODE vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(PVBCPP pThis, PSCMSTREAM pStrmInput)
680{
681 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
682 if (rcExit == RTEXITCODE_SUCCESS)
683 {
684 unsigned ch = ScmStreamPeekCh(pStrmInput);
685 if ( ch != ~(unsigned)0
686 && ch != '\r'
687 && ch != '\n')
688 rcExit = vbcppError(pThis, "Did not expected anything more on this line");
689 }
690 return rcExit;
691}
692
693
694/**
695 * Skips white spaces.
696 *
697 * @returns The current location upon return..
698 * @param pStrmInput The input stream.
699 */
700static size_t vbcppProcessSkipWhite(PSCMSTREAM pStrmInput)
701{
702 unsigned ch;
703 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
704 {
705 if (!RT_C_IS_SPACE(ch) || ch == '\r' || ch == '\n')
706 break;
707 unsigned chCheck = ScmStreamGetCh(pStrmInput);
708 AssertBreak(chCheck == ch);
709 }
710 return ScmStreamTell(pStrmInput);
711}
712
713
714/**
715 * Processes a multi-line comment.
716 *
717 * Must either string the comment or keep it. If the latter, we must refrain
718 * from replacing C-words in it.
719 *
720 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
721 * @param pThis The C preprocessor instance.
722 * @param pStrmInput The input stream.
723 */
724static RTEXITCODE vbcppProcessMultiLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
725{
726 /* The open comment sequence. */
727 ScmStreamGetCh(pStrmInput); /* '*' */
728 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
729 if ( pThis->fKeepComments
730 && !pThis->fIf0Mode)
731 rcExit = vbcppOutputWrite(pThis, "/*", 2);
732
733 /* The comment.*/
734 unsigned ch;
735 while ( rcExit == RTEXITCODE_SUCCESS
736 && (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0 )
737 {
738 if (ch == '*')
739 {
740 /* Closing sequence? */
741 unsigned ch2 = ScmStreamPeekCh(pStrmInput);
742 if (ch2 == '/')
743 {
744 ScmStreamGetCh(pStrmInput);
745 if ( pThis->fKeepComments
746 && !pThis->fIf0Mode)
747 rcExit = vbcppOutputWrite(pThis, "*/", 2);
748 break;
749 }
750 }
751
752 if ( ( pThis->fKeepComments
753 && !pThis->fIf0Mode)
754 || ch == '\r'
755 || ch == '\n')
756 {
757 rcExit = vbcppOutputCh(pThis, ch);
758 if (rcExit != RTEXITCODE_SUCCESS)
759 break;
760
761 /* Reset the maybe-preprocessor-line indicator when necessary. */
762 if (ch == '\r' || ch == '\n')
763 pThis->fMaybePreprocessorLine = true;
764 }
765 }
766 return rcExit;
767}
768
769
770/**
771 * Processes a single line comment.
772 *
773 * Must either string the comment or keep it. If the latter, we must refrain
774 * from replacing C-words in it.
775 *
776 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
777 * @param pThis The C preprocessor instance.
778 * @param pStrmInput The input stream.
779 */
780static RTEXITCODE vbcppProcessOneLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
781{
782 RTEXITCODE rcExit;
783 SCMEOL enmEol;
784 size_t cchLine;
785 const char *pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol); Assert(pszLine);
786 pszLine--; cchLine++; /* unfetching the first slash. */
787 for (;;)
788 {
789 if ( pThis->fKeepComments
790 && !pThis->fIf0Mode)
791 rcExit = vbcppOutputWrite(pThis, pszLine, cchLine + enmEol);
792 else
793 rcExit = vbcppOutputWrite(pThis, pszLine + cchLine, enmEol);
794 if (rcExit != RTEXITCODE_SUCCESS)
795 break;
796 if ( cchLine == 0
797 || pszLine[cchLine - 1] != '\\')
798 break;
799
800 pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol);
801 if (!pszLine)
802 break;
803 }
804 pThis->fMaybePreprocessorLine = true;
805 return rcExit;
806}
807
808
809/**
810 * Processes a double quoted string.
811 *
812 * Must not replace any C-words in strings.
813 *
814 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
815 * @param pThis The C preprocessor instance.
816 * @param pStrmInput The input stream.
817 */
818static RTEXITCODE vbcppProcessDoubleQuotedString(PVBCPP pThis, PSCMSTREAM pStrmInput)
819{
820 RTEXITCODE rcExit = vbcppOutputCh(pThis, '"');
821 if (rcExit == RTEXITCODE_SUCCESS)
822 {
823 bool fEscaped = false;
824 for (;;)
825 {
826 unsigned ch = ScmStreamGetCh(pStrmInput);
827 if (ch == ~(unsigned)0)
828 {
829 rcExit = vbcppError(pThis, "Unterminated double quoted string");
830 break;
831 }
832
833 rcExit = vbcppOutputCh(pThis, ch);
834 if (rcExit != RTEXITCODE_SUCCESS)
835 break;
836
837 if (ch == '"' && !fEscaped)
838 break;
839 fEscaped = !fEscaped && ch == '\\';
840 }
841 }
842 return rcExit;
843}
844
845
846/**
847 * Processes a single quoted litteral.
848 *
849 * Must not replace any C-words in strings.
850 *
851 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
852 * @param pThis The C preprocessor instance.
853 * @param pStrmInput The input stream.
854 */
855static RTEXITCODE vbcppProcessSingledQuotedString(PVBCPP pThis, PSCMSTREAM pStrmInput)
856{
857 RTEXITCODE rcExit = vbcppOutputCh(pThis, '\'');
858 if (rcExit == RTEXITCODE_SUCCESS)
859 {
860 bool fEscaped = false;
861 for (;;)
862 {
863 unsigned ch = ScmStreamGetCh(pStrmInput);
864 if (ch == ~(unsigned)0)
865 {
866 rcExit = vbcppError(pThis, "Unterminated singled quoted string");
867 break;
868 }
869
870 rcExit = vbcppOutputCh(pThis, ch);
871 if (rcExit != RTEXITCODE_SUCCESS)
872 break;
873
874 if (ch == '\'' && !fEscaped)
875 break;
876 fEscaped = !fEscaped && ch == '\\';
877 }
878 }
879 return rcExit;
880}
881
882
883/**
884 * Processes a C word, possibly replacing it with a definition.
885 *
886 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
887 * @param pThis The C preprocessor instance.
888 * @param pStrmInput The input stream.
889 * @param ch The first character.
890 */
891static RTEXITCODE vbcppProcessCWord(PVBCPP pThis, PSCMSTREAM pStrmInput, char ch)
892{
893 /** @todo Implement this... */
894 return vbcppOutputCh(pThis, ch);
895}
896
897
898
899
900
901
902
903/*
904 *
905 *
906 * D E F I N E S
907 * D E F I N E S
908 * D E F I N E S
909 * D E F I N E S
910 * D E F I N E S
911 *
912 *
913 */
914
915
916/**
917 * Checks if a define exists.
918 *
919 * @returns true or false.
920 * @param pThis The C preprocessor instance.
921 * @param pszDefine The define name and optionally the argument
922 * list.
923 * @param cchDefine The length of the name. RTSTR_MAX is ok.
924 */
925static bool vbcppDefineExists(PVBCPP pThis, const char *pszDefine, size_t cchDefine)
926{
927 return cchDefine > 0
928 && VBCPP_BITMAP_IS_SET(pThis->bmDefined, *pszDefine)
929 && RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine) != NULL;
930}
931
932
933/**
934 * Frees a define.
935 *
936 * @returns VINF_SUCCESS (used when called by RTStrSpaceDestroy)
937 * @param pStr Pointer to the VBCPPDEF::Core member.
938 * @param pvUser Unused.
939 */
940static DECLCALLBACK(int) vbcppFreeDefine(PRTSTRSPACECORE pStr, void *pvUser)
941{
942 RTMemFree(pStr);
943 NOREF(pvUser);
944 return VINF_SUCCESS;
945}
946
947
948/**
949 * Removes a define.
950 *
951 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
952 * @param pThis The C preprocessor instance.
953 * @param pszDefine The define name, no argument list or anything.
954 * @param cchDefine The length of the name. RTSTR_MAX is ok.
955 * @param fExplicitUndef Explicit undefinition, that is, in a selective
956 * preprocessing run it will evaluate to undefined.
957 */
958static RTEXITCODE vbcppDefineUndef(PVBCPP pThis, const char *pszDefine, size_t cchDefine, bool fExplicitUndef)
959{
960 PRTSTRSPACECORE pHit = RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine);
961 if (pHit)
962 {
963 RTStrSpaceRemove(&pThis->StrSpace, pHit->pszString);
964 vbcppFreeDefine(pHit, NULL);
965 }
966
967 if (fExplicitUndef)
968 {
969 if (cchDefine == RTSTR_MAX)
970 cchDefine = strlen(pszDefine);
971
972 PRTSTRSPACECORE pStr = (PRTSTRSPACECORE)RTMemAlloc(sizeof(*pStr) + cchDefine + 1);
973 if (!pStr)
974 return vbcppError(pThis, "out of memory");
975 char *pszDst = (char *)(pStr + 1);
976 pStr->pszString = pszDst;
977 memcpy(pszDst, pszDefine, cchDefine);
978 pszDst[cchDefine] = '\0';
979 if (!RTStrSpaceInsert(&pThis->UndefStrSpace, pStr))
980 RTMemFree(pStr);
981 }
982
983 return RTEXITCODE_SUCCESS;
984}
985
986
987/**
988 * Inserts a define (rejecting and freeing it in some case).
989 *
990 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
991 * @param pThis The C preprocessor instance.
992 * @param pDef The define to insert.
993 */
994static RTEXITCODE vbcppDefineInsert(PVBCPP pThis, PVBCPPDEF pDef)
995{
996 /*
997 * Ignore in source-file defines when doing selective preprocessing.
998 */
999 if ( !pThis->fRespectSourceDefines
1000 && !pDef->fCmdLine)
1001 {
1002 /* Ignore*/
1003 vbcppFreeDefine(&pDef->Core, NULL);
1004 return RTEXITCODE_SUCCESS;
1005 }
1006
1007 /*
1008 * Insert it and update the lead character hint bitmap.
1009 */
1010 if (RTStrSpaceInsert(&pThis->StrSpace, &pDef->Core))
1011 VBCPP_BITMAP_SET(pThis->bmDefined, *pDef->Core.pszString);
1012 else
1013 {
1014 /*
1015 * Duplicate. When doing selective D preprocessing, let the command
1016 * line take precendece.
1017 */
1018 PVBCPPDEF pOld = (PVBCPPDEF)RTStrSpaceGet(&pThis->StrSpace, pDef->Core.pszString); Assert(pOld);
1019 if ( pThis->fAllowRedefiningCmdLineDefines
1020 || pDef->fCmdLine == pOld->fCmdLine)
1021 {
1022 if (pDef->fCmdLine)
1023 RTMsgWarning("Redefining '%s'\n", pDef->Core.pszString);
1024
1025 RTStrSpaceRemove(&pThis->StrSpace, pOld->Core.pszString);
1026 vbcppFreeDefine(&pOld->Core, NULL);
1027
1028 bool fRc = RTStrSpaceInsert(&pThis->StrSpace, &pDef->Core);
1029 Assert(fRc);
1030 }
1031 else
1032 {
1033 RTMsgWarning("Ignoring redefinition of '%s'\n", pDef->Core.pszString);
1034 vbcppFreeDefine(&pDef->Core, NULL);
1035 }
1036 }
1037
1038 return RTEXITCODE_SUCCESS;
1039}
1040
1041
1042/**
1043 * Adds a define.
1044 *
1045 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
1046 * @param pThis The C preprocessor instance.
1047 * @param pszDefine The define name, no parameter list.
1048 * @param cchDefine The length of the name.
1049 * @param pszParams The parameter list.
1050 * @param cchParams The length of the parameter list.
1051 * @param pszValue The value.
1052 * @param cchDefine The length of the value.
1053 * @param fCmdLine Set if originating on the command line.
1054 */
1055static RTEXITCODE vbcppDefineAddFn(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
1056 const char *pszParams, size_t cchParams,
1057 const char *pszValue, size_t cchValue,
1058 bool fCmdLine)
1059
1060{
1061 Assert(RTStrNLen(pszDefine, cchDefine) == cchDefine);
1062 Assert(RTStrNLen(pszParams, cchParams) == cchParams);
1063 Assert(RTStrNLen(pszValue, cchValue) == cchValue);
1064
1065 /*
1066 * Determin the number of arguments and how much space their names
1067 * requires. Performing syntax validation while parsing.
1068 */
1069 uint32_t cchArgNames = 0;
1070 uint32_t cArgs = 0;
1071 for (size_t off = 0; off < cchParams; off++)
1072 {
1073 /* Skip blanks and maybe one comma. */
1074 bool fIgnoreComma = cArgs != 0;
1075 while (off < cchParams)
1076 {
1077 if (!RT_C_IS_SPACE(pszParams[off]))
1078 {
1079 if (pszParams[off] != ',' || !fIgnoreComma)
1080 {
1081 if (vbcppIsCIdentifierLeadChar(pszParams[off]))
1082 break;
1083 /** @todo variadic macros. */
1084 return vbcppErrorPos(pThis, &pszParams[off], "Unexpected character");
1085 }
1086 fIgnoreComma = false;
1087 }
1088 off++;
1089 }
1090 if (off >= cchParams)
1091 break;
1092
1093 /* Found and argument. First character is already validated. */
1094 cArgs++;
1095 cchArgNames += 2;
1096 off++;
1097 while ( off < cchParams
1098 && vbcppIsCIdentifierChar(pszParams[off]))
1099 off++, cchArgNames++;
1100 }
1101
1102 /*
1103 * Allocate a structure.
1104 */
1105 size_t cbDef = RT_OFFSETOF(VBCPPDEF, szValue[cchValue + 1 + cchDefine + 1 + cchArgNames])
1106 + sizeof(const char *) * cArgs;
1107 cbDef = RT_ALIGN_Z(cbDef, sizeof(const char *));
1108 PVBCPPDEF pDef = (PVBCPPDEF)RTMemAlloc(cbDef);
1109 if (!pDef)
1110 return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
1111
1112 char *pszDst = &pDef->szValue[cchValue + 1];
1113 pDef->Core.pszString = pszDst;
1114 memcpy(pszDst, pszDefine, cchDefine);
1115 pszDst += cchDefine;
1116 *pszDst++ = '\0';
1117 pDef->fFunction = true;
1118 pDef->fVarArg = false;
1119 pDef->fCmdLine = fCmdLine;
1120 pDef->cArgs = cArgs;
1121 pDef->papszArgs = (const char **)((uintptr_t)pDef + cbDef - sizeof(const char *) * cArgs);
1122 VBCPP_BITMAP_EMPTY(pDef->bmArgs);
1123 memcpy(pDef->szValue, pszValue, cchValue);
1124 pDef->szValue[cchValue] = '\0';
1125
1126 /*
1127 * Set up the arguments.
1128 */
1129 uint32_t iArg = 0;
1130 for (size_t off = 0; off < cchParams; off++)
1131 {
1132 /* Skip blanks and maybe one comma. */
1133 bool fIgnoreComma = cArgs != 0;
1134 while (off < cchParams)
1135 {
1136 if (!RT_C_IS_SPACE(pszParams[off]))
1137 {
1138 if (pszParams[off] != ',' || !fIgnoreComma)
1139 break;
1140 fIgnoreComma = false;
1141 }
1142 off++;
1143 }
1144 if (off >= cchParams)
1145 break;
1146
1147 /* Found and argument. First character is already validated. */
1148 pDef->papszArgs[iArg] = pszDst;
1149 do
1150 {
1151 *pszDst++ = pszParams[off++];
1152 } while ( off < cchParams
1153 && vbcppIsCIdentifierChar(pszParams[off]));
1154 *pszDst++ = '\0';
1155 iArg++;
1156 }
1157 Assert((uintptr_t)pszDst <= (uintptr_t)pDef->papszArgs);
1158
1159 return vbcppDefineInsert(pThis, pDef);
1160}
1161
1162
1163/**
1164 * Adds a define.
1165 *
1166 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
1167 * @param pThis The C preprocessor instance.
1168 * @param pszDefine The define name and optionally the argument
1169 * list.
1170 * @param cchDefine The length of the name. RTSTR_MAX is ok.
1171 * @param pszValue The value.
1172 * @param cchDefine The length of the value. RTSTR_MAX is ok.
1173 * @param fCmdLine Set if originating on the command line.
1174 */
1175static RTEXITCODE vbcppDefineAdd(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
1176 const char *pszValue, size_t cchValue, bool fCmdLine)
1177{
1178 /*
1179 * We need the lengths. Trim the input.
1180 */
1181 if (cchDefine == RTSTR_MAX)
1182 cchDefine = strlen(pszDefine);
1183 while (cchDefine > 0 && RT_C_IS_SPACE(*pszDefine))
1184 pszDefine++, cchDefine--;
1185 while (cchDefine > 0 && RT_C_IS_SPACE(pszDefine[cchDefine - 1]))
1186 cchDefine--;
1187 if (!cchDefine)
1188 return vbcppErrorPos(pThis, pszDefine, "The define has no name");
1189
1190 if (cchValue == RTSTR_MAX)
1191 cchValue = strlen(pszValue);
1192 while (cchValue > 0 && RT_C_IS_SPACE(*pszValue))
1193 pszValue++, cchValue--;
1194 while (cchValue > 0 && RT_C_IS_SPACE(pszValue[cchValue - 1]))
1195 cchValue--;
1196
1197 /*
1198 * Arguments make the job a bit more annoying. Handle that elsewhere
1199 */
1200 const char *pszParams = (const char *)memchr(pszDefine, '(', cchDefine);
1201 if (pszParams)
1202 {
1203 size_t cchParams = pszDefine + cchDefine - pszParams;
1204 cchDefine -= cchParams;
1205 if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
1206 return RTEXITCODE_FAILURE;
1207 if (pszParams[cchParams - 1] != ')')
1208 return vbcppErrorPos(pThis, pszParams + cchParams - 1, "Missing closing parenthesis");
1209 pszParams++;
1210 cchParams -= 2;
1211 return vbcppDefineAddFn(pThis, pszDefine, cchDefine, pszParams, cchParams, pszValue, cchValue, fCmdLine);
1212 }
1213
1214 /*
1215 * Simple define, no arguments.
1216 */
1217 if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
1218 return RTEXITCODE_FAILURE;
1219
1220 PVBCPPDEF pDef = (PVBCPPDEF)RTMemAlloc(RT_OFFSETOF(VBCPPDEF, szValue[cchValue + 1 + cchDefine + 1]));
1221 if (!pDef)
1222 return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
1223
1224 pDef->Core.pszString = &pDef->szValue[cchValue + 1];
1225 memcpy((char *)pDef->Core.pszString, pszDefine, cchDefine);
1226 ((char *)pDef->Core.pszString)[cchDefine] = '\0';
1227 pDef->fFunction = false;
1228 pDef->fVarArg = false;
1229 pDef->fCmdLine = fCmdLine;
1230 pDef->cArgs = 0;
1231 pDef->papszArgs = NULL;
1232 VBCPP_BITMAP_EMPTY(pDef->bmArgs);
1233 memcpy(pDef->szValue, pszValue, cchValue);
1234 pDef->szValue[cchValue] = '\0';
1235
1236 return vbcppDefineInsert(pThis, pDef);
1237}
1238
1239
1240/**
1241 * Processes a abbreviated line number directive.
1242 *
1243 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1244 * @param pThis The C preprocessor instance.
1245 * @param pStrmInput The input stream.
1246 * @param offStart The stream position where the directive
1247 * started (for pass thru).
1248 */
1249static RTEXITCODE vbcppProcessDefine(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1250{
1251 /*
1252 * Parse it.
1253 */
1254 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1255 if (rcExit == RTEXITCODE_SUCCESS)
1256 {
1257 size_t cchDefine;
1258 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
1259 if (pchDefine)
1260 {
1261 /* If it's a function style define, parse out the parameter list. */
1262 size_t cchParams = 0;
1263 const char *pchParams = NULL;
1264 unsigned ch = ScmStreamPeekCh(pStrmInput);
1265 if (ch == '(')
1266 {
1267 ScmStreamGetCh(pStrmInput);
1268 pchParams = ScmStreamGetCur(pStrmInput);
1269
1270 unsigned chPrev = ch;
1271 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
1272 {
1273 if (ch == '\r' || ch == '\n')
1274 {
1275 if (chPrev != '\\')
1276 {
1277 rcExit = vbcppError(pThis, "Missing ')'");
1278 break;
1279 }
1280 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
1281 }
1282 if (ch == ')')
1283 {
1284 cchParams = ScmStreamGetCur(pStrmInput) - pchParams;
1285 ScmStreamGetCh(pStrmInput);
1286 break;
1287 }
1288 ScmStreamGetCh(pStrmInput);
1289 }
1290 }
1291 /* The simple kind. */
1292 else if (!RT_C_IS_SPACE(ch) && ch != ~(unsigned)0)
1293 rcExit = vbcppError(pThis, "Expected whitespace after macro name");
1294
1295 /* Parse out the value. */
1296 if (rcExit == RTEXITCODE_SUCCESS)
1297 rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1298 if (rcExit == RTEXITCODE_SUCCESS)
1299 {
1300 size_t offValue = ScmStreamTell(pStrmInput);
1301 const char *pchValue = ScmStreamGetCur(pStrmInput);
1302 unsigned chPrev = ch;
1303 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
1304 {
1305 if (ch == '\r' || ch == '\n')
1306 {
1307 if (chPrev != '\\')
1308 break;
1309 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
1310 }
1311 ScmStreamGetCh(pStrmInput);
1312 }
1313 size_t cchValue = ScmStreamGetCur(pStrmInput) - pchValue;
1314
1315 /*
1316 * Execute.
1317 */
1318 if (pchParams)
1319 rcExit = vbcppDefineAddFn(pThis, pchDefine, cchDefine, pchParams, cchParams, pchValue, cchValue, false);
1320 else
1321 rcExit = vbcppDefineAdd(pThis, pchDefine, cchDefine, pchValue, cchValue, false);
1322
1323 /*
1324 * Pass thru?
1325 */
1326 if ( rcExit == RTEXITCODE_SUCCESS
1327 && pThis->fPassThruDefines)
1328 {
1329 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
1330 size_t cch;
1331 if (pchParams)
1332 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s(%.*s)",
1333 cchIndent, "", cchDefine, pchDefine, cchParams, pchParams);
1334 else
1335 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s",
1336 cchIndent, "", cchDefine, pchDefine);
1337 if (cch > 0)
1338 vbcppOutputComment(pThis, pStrmInput, offValue, cch, 1);
1339 else
1340 rcExit = vbcppError(pThis, "output error");
1341 }
1342 }
1343
1344 }
1345 }
1346 return rcExit;
1347}
1348
1349
1350/**
1351 * Processes a abbreviated line number directive.
1352 *
1353 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1354 * @param pThis The C preprocessor instance.
1355 * @param pStrmInput The input stream.
1356 * @param offStart The stream position where the directive
1357 * started (for pass thru).
1358 */
1359static RTEXITCODE vbcppProcessUndef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1360{
1361 return vbcppError(pThis, "Not implemented %s", __FUNCTION__);
1362}
1363
1364
1365
1366
1367
1368/*
1369 *
1370 *
1371 * C O N D I T I O N A L S
1372 * C O N D I T I O N A L S
1373 * C O N D I T I O N A L S
1374 * C O N D I T I O N A L S
1375 * C O N D I T I O N A L S
1376 *
1377 *
1378 */
1379
1380
1381/**
1382 * Combines current stack result with the one being pushed.
1383 *
1384 * @returns Combined result.
1385 * @param enmEvalPush The result of the condition being pushed.
1386 * @param enmEvalStack The current stack result.
1387 */
1388static VBCPPEVAL vbcppCondCombine(VBCPPEVAL enmEvalPush, VBCPPEVAL enmEvalStack)
1389{
1390 if (enmEvalStack == kVBCppEval_False)
1391 return kVBCppEval_False;
1392 return enmEvalPush;
1393}
1394
1395
1396/**
1397 * Pushes an conditional onto the stack.
1398 *
1399 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1400 * @param pThis The C preprocessor instance.
1401 * @param pStrmInput The current input stream.
1402 * @param offStart Not currently used, using @a pchCondition and
1403 * @a cchCondition instead.
1404 * @param enmKind The kind of conditional.
1405 * @param enmResult The result of the evaluation.
1406 * @param pchCondition The raw condition.
1407 * @param cchCondition The length of @a pchCondition.
1408 */
1409static RTEXITCODE vbcppCondPush(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart,
1410 VBCPPCONDKIND enmKind, VBCPPEVAL enmResult,
1411 const char *pchCondition, size_t cchCondition)
1412{
1413 if (pThis->cCondStackDepth >= _64K)
1414 return vbcppError(pThis, "Too many nested #if/#ifdef/#ifndef statements");
1415
1416 /*
1417 * Allocate a new entry and push it.
1418 */
1419 PVBCPPCOND pCond = (PVBCPPCOND)RTMemAlloc(sizeof(*pCond));
1420 if (!pCond)
1421 return vbcppError(pThis, "out of memory");
1422
1423 PVBCPPCOND pUp = pThis->pCondStack;
1424 pCond->enmKind = enmKind;
1425 pCond->enmResult = enmResult;
1426 pCond->enmStackResult = pUp ? vbcppCondCombine(enmResult, pUp->enmStackResult) : enmResult;
1427 pCond->fSeenElse = false;
1428 pCond->iLevel = pThis->cCondStackDepth;
1429 pCond->iKeepLevel = (pUp ? pUp->iKeepLevel : 0) + enmResult == kVBCppEval_Undecided;
1430 pCond->pchCond = pchCondition;
1431 pCond->cchCond = cchCondition;
1432
1433 pCond->pUp = pThis->pCondStack;
1434 pThis->pCondStack = pCond;
1435 pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
1436
1437 /*
1438 * Do pass thru.
1439 */
1440 if ( !pThis->fIf0Mode
1441 && enmResult == kVBCppEval_Undecided)
1442 {
1443 /** @todo this is stripping comments of \#ifdef and \#ifndef atm. */
1444 const char *pszDirective;
1445 switch (enmKind)
1446 {
1447 case kVBCppCondKind_If: pszDirective = "if"; break;
1448 case kVBCppCondKind_IfDef: pszDirective = "ifdef"; break;
1449 case kVBCppCondKind_IfNDef: pszDirective = "ifndef"; break;
1450 case kVBCppCondKind_ElIf: pszDirective = "elif"; break;
1451 default: AssertFailedReturn(RTEXITCODE_FAILURE);
1452 }
1453 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*s%s %.*s",
1454 pCond->iKeepLevel - 1, "", pszDirective, cchCondition, pchCondition);
1455 if (cch < 0)
1456 return vbcppError(pThis, "Output error %Rrc", (int)cch);
1457 }
1458
1459 return RTEXITCODE_SUCCESS;
1460}
1461
1462
1463/**
1464 * Processes a abbreviated line number directive.
1465 *
1466 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1467 * @param pThis The C preprocessor instance.
1468 * @param pStrmInput The input stream.
1469 * @param offStart The stream position where the directive
1470 * started (for pass thru).
1471 */
1472static RTEXITCODE vbcppProcessIf(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1473{
1474 return vbcppError(pThis, "Not implemented %s", __FUNCTION__);
1475}
1476
1477
1478/**
1479 * Processes a abbreviated line number directive.
1480 *
1481 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1482 * @param pThis The C preprocessor instance.
1483 * @param pStrmInput The input stream.
1484 * @param offStart The stream position where the directive
1485 * started (for pass thru).
1486 */
1487static RTEXITCODE vbcppProcessIfDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1488{
1489 /*
1490 * Parse it.
1491 */
1492 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1493 if (rcExit == RTEXITCODE_SUCCESS)
1494 {
1495 size_t cchDefine;
1496 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
1497 if (pchDefine)
1498 {
1499 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
1500 if (rcExit == RTEXITCODE_SUCCESS)
1501 {
1502 /*
1503 * Evaluate it.
1504 */
1505 VBCPPEVAL enmEval;
1506 if (vbcppDefineExists(pThis, pchDefine, cchDefine))
1507 enmEval = kVBCppEval_True;
1508 else if ( pThis->fUndecidedConditionals
1509 || RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
1510 enmEval = kVBCppEval_False;
1511 else
1512 enmEval = kVBCppEval_Undecided;
1513 rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfDef, enmEval,
1514 pchDefine, cchDefine);
1515 }
1516 }
1517 else
1518 rcExit = vbcppError(pThis, "Malformed #ifdef");
1519 }
1520 return rcExit;
1521}
1522
1523
1524/**
1525 * Processes a abbreviated line number directive.
1526 *
1527 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1528 * @param pThis The C preprocessor instance.
1529 * @param pStrmInput The input stream.
1530 * @param offStart The stream position where the directive
1531 * started (for pass thru).
1532 */
1533static RTEXITCODE vbcppProcessIfNDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1534{
1535 /*
1536 * Parse it.
1537 */
1538 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1539 if (rcExit == RTEXITCODE_SUCCESS)
1540 {
1541 size_t cchDefine;
1542 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
1543 if (pchDefine)
1544 {
1545 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
1546 if (rcExit == RTEXITCODE_SUCCESS)
1547 {
1548 /*
1549 * Evaluate it.
1550 */
1551 VBCPPEVAL enmEval;
1552 if (vbcppDefineExists(pThis, pchDefine, cchDefine))
1553 enmEval = kVBCppEval_False;
1554 else if ( pThis->fUndecidedConditionals
1555 || RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
1556 enmEval = kVBCppEval_True;
1557 else
1558 enmEval = kVBCppEval_Undecided;
1559 rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfNDef, enmEval,
1560 pchDefine, cchDefine);
1561 }
1562 }
1563 else
1564 rcExit = vbcppError(pThis, "Malformed #ifndef");
1565 }
1566 return rcExit;
1567}
1568
1569
1570/**
1571 * Processes a abbreviated line number directive.
1572 *
1573 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1574 * @param pThis The C preprocessor instance.
1575 * @param pStrmInput The input stream.
1576 * @param offStart The stream position where the directive
1577 * started (for pass thru).
1578 */
1579static RTEXITCODE vbcppProcessElse(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1580{
1581 /*
1582 * Nothing to parse, just comment positions to find and note down.
1583 */
1584 offStart = vbcppProcessSkipWhite(pStrmInput);
1585 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
1586 if (rcExit == RTEXITCODE_SUCCESS)
1587 {
1588 /*
1589 * Execute.
1590 */
1591 PVBCPPCOND pCond = pThis->pCondStack;
1592 if (pCond)
1593 {
1594 if (!pCond->fSeenElse)
1595 {
1596 pCond->fSeenElse = true;
1597 if ( pCond->enmResult != kVBCppEval_Undecided
1598 && ( !pCond->pUp
1599 || pCond->pUp->enmStackResult == kVBCppEval_True))
1600 {
1601 if (pCond->enmResult == kVBCppEval_True)
1602 pCond->enmStackResult = kVBCppEval_False;
1603 else
1604 pCond->enmStackResult = kVBCppEval_True;
1605 pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
1606 }
1607
1608 /*
1609 * Do pass thru.
1610 */
1611 if ( !pThis->fIf0Mode
1612 && pCond->enmResult == kVBCppEval_Undecided)
1613 {
1614 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*selse", pCond->iKeepLevel - 1, "");
1615 if (cch > 0)
1616 rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 2);
1617 else
1618 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
1619 }
1620 }
1621 else
1622 rcExit = vbcppError(pThis, "Double #else or/and missing #endif");
1623 }
1624 else
1625 rcExit = vbcppError(pThis, "#else without #if");
1626 }
1627 return rcExit;
1628}
1629
1630
1631/**
1632 * Processes a abbreviated line number directive.
1633 *
1634 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1635 * @param pThis The C preprocessor instance.
1636 * @param pStrmInput The input stream.
1637 * @param offStart The stream position where the directive
1638 * started (for pass thru).
1639 */
1640static RTEXITCODE vbcppProcessEndif(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1641{
1642 /*
1643 * Nothing to parse, just comment positions to find and note down.
1644 */
1645 offStart = vbcppProcessSkipWhite(pStrmInput);
1646 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
1647 if (rcExit == RTEXITCODE_SUCCESS)
1648 {
1649 /*
1650 * Execute.
1651 */
1652 PVBCPPCOND pCond = pThis->pCondStack;
1653 if (pCond)
1654 {
1655 pThis->pCondStack = pCond->pUp;
1656 pThis->fIf0Mode = pCond->pUp && pCond->pUp->enmStackResult == kVBCppEval_False;
1657
1658 /*
1659 * Do pass thru.
1660 */
1661 if ( !pThis->fIf0Mode
1662 && pCond->enmResult == kVBCppEval_Undecided)
1663 {
1664 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sendif", pCond->iKeepLevel - 1, "");
1665 if (cch > 0)
1666 rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 1);
1667 else
1668 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
1669 }
1670 }
1671 else
1672 rcExit = vbcppError(pThis, "#endif without #if");
1673 }
1674 return rcExit;
1675}
1676
1677
1678
1679
1680
1681/*
1682 *
1683 *
1684 * Misc Directives
1685 * Misc Directives
1686 * Misc Directives
1687 * Misc Directives
1688 *
1689 *
1690 */
1691
1692
1693/**
1694 * Adds an include directory.
1695 *
1696 * @returns Program exit code, with error message on failure.
1697 * @param pThis The C preprocessor instance.
1698 * @param pszDir The directory to add.
1699 */
1700static RTEXITCODE vbcppAddInclude(PVBCPP pThis, const char *pszDir)
1701{
1702 uint32_t cIncludes = pThis->cIncludes;
1703 if (cIncludes >= _64K)
1704 return vbcppError(pThis, "Too many include directories");
1705
1706 void *pv = RTMemRealloc(pThis->papszIncludes, (cIncludes + 1) * sizeof(char **));
1707 if (!pv)
1708 return vbcppError(pThis, "No memory for include directories");
1709 pThis->papszIncludes = (char **)pv;
1710
1711 int rc = RTStrDupEx(&pThis->papszIncludes[cIncludes], pszDir);
1712 if (RT_FAILURE(rc))
1713 return vbcppError(pThis, "No string memory for include directories");
1714
1715 pThis->cIncludes = cIncludes + 1;
1716 return RTEXITCODE_SUCCESS;
1717}
1718
1719
1720/**
1721 * Processes a abbreviated line number directive.
1722 *
1723 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1724 * @param pThis The C preprocessor instance.
1725 * @param pStrmInput The input stream.
1726 * @param offStart The stream position where the directive
1727 * started (for pass thru).
1728 */
1729static RTEXITCODE vbcppProcessInclude(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1730{
1731 /*
1732 * Parse it.
1733 */
1734 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1735 if (rcExit == RTEXITCODE_SUCCESS)
1736 {
1737 size_t cchFileSpec = 0;
1738 const char *pchFileSpec = NULL;
1739 size_t cchFilename = 0;
1740 const char *pchFilename = NULL;
1741
1742 unsigned ch = ScmStreamPeekCh(pStrmInput);
1743 unsigned chType = ch;
1744 if (ch == '"' || ch == '<')
1745 {
1746 ScmStreamGetCh(pStrmInput);
1747 pchFileSpec = pchFilename = ScmStreamGetCur(pStrmInput);
1748 unsigned chEnd = chType == '<' ? '>' : '"';
1749 unsigned chPrev = ch;
1750 while ( (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0
1751 && ch != chEnd)
1752 {
1753 if (ch == '\r' || ch == '\n')
1754 {
1755 rcExit = vbcppError(pThis, "Multi-line include file specfications are not supported");
1756 break;
1757 }
1758 }
1759
1760 if (rcExit == RTEXITCODE_SUCCESS)
1761 {
1762 if (ch != ~(unsigned)0)
1763 cchFileSpec = cchFilename = ScmStreamGetCur(pStrmInput) - pchFilename - 1;
1764 else
1765 rcExit = vbcppError(pThis, "Expected '%c'", chType);
1766 }
1767 }
1768 else if (vbcppIsCIdentifierLeadChar(ch))
1769 {
1770 //pchFileSpec = ScmStreamCGetWord(pStrmInput, &cchFileSpec);
1771 rcExit = vbcppError(pThis, "Including via a define is not implemented yet");
1772 }
1773 else
1774 rcExit = vbcppError(pThis, "Malformed include directive");
1775
1776 /*
1777 * Take down the location of the next non-white space, in case we need
1778 * to pass thru the directive further down. Then skip to the end of the
1779 * line.
1780 */
1781 size_t const offIncEnd = vbcppProcessSkipWhite(pStrmInput);
1782 if (rcExit == RTEXITCODE_SUCCESS)
1783 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
1784
1785 if (rcExit == RTEXITCODE_SUCCESS)
1786 {
1787 /*
1788 * Execute it.
1789 */
1790 if (pThis->enmIncludeAction == kVBCppIncludeAction_Include)
1791 {
1792 /** @todo Search for the include file and push it onto the input stack.
1793 * Not difficult, just unnecessary rigth now. */
1794 rcExit = vbcppError(pThis, "Includes are fully implemented");
1795 }
1796 else if (pThis->enmIncludeAction == kVBCppIncludeAction_PassThru)
1797 {
1798 /* Pretty print the passthru. */
1799 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
1800 size_t cch;
1801 if (chType == '<')
1802 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude <%.*s>",
1803 cchIndent, "", cchFileSpec, pchFileSpec);
1804 else if (chType == '"')
1805 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude \"%.*s\"",
1806 cchIndent, "", cchFileSpec, pchFileSpec);
1807 else
1808 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude %.*s",
1809 cchIndent, "", cchFileSpec, pchFileSpec);
1810 if (cch > 0)
1811 rcExit = vbcppOutputComment(pThis, pStrmInput, offIncEnd, cch, 1);
1812 else
1813 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
1814
1815 }
1816 else
1817 Assert(pThis->enmIncludeAction == kVBCppIncludeAction_Drop);
1818 }
1819 }
1820 return rcExit;
1821}
1822
1823
1824/**
1825 * Processes a abbreviated line number directive.
1826 *
1827 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1828 * @param pThis The C preprocessor instance.
1829 * @param pStrmInput The input stream.
1830 * @param offStart The stream position where the directive
1831 * started (for pass thru).
1832 */
1833static RTEXITCODE vbcppProcessPragma(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1834{
1835 return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
1836}
1837
1838
1839/**
1840 * Processes a abbreviated line number directive.
1841 *
1842 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1843 * @param pThis The C preprocessor instance.
1844 * @param pStrmInput The input stream.
1845 * @param offStart The stream position where the directive
1846 * started (for pass thru).
1847 */
1848static RTEXITCODE vbcppProcessLineNo(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1849{
1850 return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
1851}
1852
1853
1854/**
1855 * Processes a abbreviated line number directive.
1856 *
1857 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1858 * @param pThis The C preprocessor instance.
1859 * @param pStrmInput The input stream.
1860 */
1861static RTEXITCODE vbcppProcessLineNoShort(PVBCPP pThis, PSCMSTREAM pStrmInput)
1862{
1863 return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
1864}
1865
1866
1867/**
1868 * Handles a preprocessor directive.
1869 *
1870 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1871 * @param pThis The C preprocessor instance.
1872 * @param pStrmInput The input stream.
1873 */
1874static RTEXITCODE vbcppProcessDirective(PVBCPP pThis, PSCMSTREAM pStrmInput)
1875{
1876 /*
1877 * Get the directive and do a string switch on it.
1878 */
1879 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1880 if (rcExit != RTEXITCODE_SUCCESS)
1881 return rcExit;
1882 size_t cchDirective;
1883 const char *pchDirective = ScmStreamCGetWord(pStrmInput, &cchDirective);
1884 if (pchDirective)
1885 {
1886 size_t const offStart = ScmStreamTell(pStrmInput);
1887#define IS_DIRECTIVE(a_sz) ( sizeof(a_sz) - 1 == cchDirective && strncmp(pchDirective, a_sz, sizeof(a_sz) - 1) == 0)
1888 if (IS_DIRECTIVE("if"))
1889 rcExit = vbcppProcessIf(pThis, pStrmInput, offStart);
1890 else if (IS_DIRECTIVE("ifdef"))
1891 rcExit = vbcppProcessIfDef(pThis, pStrmInput, offStart);
1892 else if (IS_DIRECTIVE("ifndef"))
1893 rcExit = vbcppProcessIfNDef(pThis, pStrmInput, offStart);
1894 else if (IS_DIRECTIVE("else"))
1895 rcExit = vbcppProcessElse(pThis, pStrmInput, offStart);
1896 else if (IS_DIRECTIVE("endif"))
1897 rcExit = vbcppProcessEndif(pThis, pStrmInput, offStart);
1898 else if (!pThis->fIf0Mode)
1899 {
1900 if (IS_DIRECTIVE("include"))
1901 rcExit = vbcppProcessInclude(pThis, pStrmInput, offStart);
1902 else if (IS_DIRECTIVE("define"))
1903 rcExit = vbcppProcessDefine(pThis, pStrmInput, offStart);
1904 else if (IS_DIRECTIVE("undef"))
1905 rcExit = vbcppProcessUndef(pThis, pStrmInput, offStart);
1906 else if (IS_DIRECTIVE("pragma"))
1907 rcExit = vbcppProcessPragma(pThis, pStrmInput, offStart);
1908 else if (IS_DIRECTIVE("line"))
1909 rcExit = vbcppProcessLineNo(pThis, pStrmInput, offStart);
1910 else
1911 rcExit = vbcppError(pThis, "Unknown preprocessor directive '#%.*s'", cchDirective, pchDirective);
1912 }
1913#undef IS_DIRECTIVE
1914 }
1915 else if (!pThis->fIf0Mode)
1916 {
1917 /* Could it be a # <num> "file" directive? */
1918 unsigned ch = ScmStreamPeekCh(pStrmInput);
1919 if (RT_C_IS_DIGIT(ch))
1920 rcExit = vbcppProcessLineNoShort(pThis, pStrmInput);
1921 else
1922 rcExit = vbcppError(pThis, "Malformed preprocessor directive");
1923 }
1924 return rcExit;
1925}
1926
1927
1928/*
1929 *
1930 *
1931 * M a i n b o d y.
1932 * M a i n b o d y.
1933 * M a i n b o d y.
1934 * M a i n b o d y.
1935 * M a i n b o d y.
1936 *
1937 *
1938 */
1939
1940
1941/**
1942 * Does the actually preprocessing of the input file.
1943 *
1944 * @returns Exit code.
1945 * @param pThis The C preprocessor instance.
1946 */
1947static RTEXITCODE vbcppPreprocess(PVBCPP pThis)
1948{
1949 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1950
1951 /*
1952 * Parse.
1953 */
1954 while (pThis->pInputStack)
1955 {
1956 pThis->fMaybePreprocessorLine = true;
1957
1958 PSCMSTREAM pStrmInput = &pThis->pInputStack->StrmInput;
1959 unsigned ch;
1960 while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
1961 {
1962 if (ch == '/')
1963 {
1964 ch = ScmStreamPeekCh(pStrmInput);
1965 if (ch == '*')
1966 rcExit = vbcppProcessMultiLineComment(pThis, pStrmInput);
1967 else if (ch == '/')
1968 rcExit = vbcppProcessOneLineComment(pThis, pStrmInput);
1969 else
1970 {
1971 pThis->fMaybePreprocessorLine = false;
1972 if (!pThis->fIf0Mode)
1973 rcExit = vbcppOutputCh(pThis, '/');
1974 }
1975 }
1976 else if (ch == '#' && pThis->fMaybePreprocessorLine)
1977 {
1978 rcExit = vbcppProcessDirective(pThis, pStrmInput);
1979 pStrmInput = &pThis->pInputStack->StrmInput;
1980 }
1981 else if (ch == '\r' || ch == '\n')
1982 {
1983 pThis->fMaybePreprocessorLine = true;
1984 rcExit = vbcppOutputCh(pThis, ch);
1985 }
1986 else if (RT_C_IS_SPACE(ch))
1987 {
1988 if (!pThis->fIf0Mode)
1989 rcExit = vbcppOutputCh(pThis, ch);
1990 }
1991 else
1992 {
1993 pThis->fMaybePreprocessorLine = false;
1994 if (!pThis->fIf0Mode)
1995 {
1996 if (ch == '"')
1997 rcExit = vbcppProcessDoubleQuotedString(pThis, pStrmInput);
1998 else if (ch == '\'')
1999 rcExit = vbcppProcessSingledQuotedString(pThis, pStrmInput);
2000 else if (vbcppIsCIdentifierLeadChar(ch))
2001 rcExit = vbcppProcessCWord(pThis, pStrmInput, ch);
2002 else
2003 rcExit = vbcppOutputCh(pThis, ch);
2004 }
2005 }
2006 if (rcExit != RTEXITCODE_SUCCESS)
2007 break;
2008 }
2009
2010 /*
2011 * Check for errors.
2012 */
2013 if (rcExit != RTEXITCODE_SUCCESS)
2014 break;
2015
2016 /*
2017 * Pop the input stack.
2018 */
2019 PVBCPPINPUT pPopped = pThis->pInputStack;
2020 pThis->pInputStack = pPopped->pUp;
2021 RTMemFree(pPopped);
2022 }
2023
2024 return rcExit;
2025}
2026
2027
2028/**
2029 * Opens the input and output streams.
2030 *
2031 * @returns Exit code.
2032 * @param pThis The C preprocessor instance.
2033 */
2034static RTEXITCODE vbcppOpenStreams(PVBCPP pThis)
2035{
2036 if (!pThis->pszInput)
2037 return vbcppError(pThis, "Preprocessing the standard input stream is currently not supported");
2038
2039 size_t cchName = strlen(pThis->pszInput);
2040 PVBCPPINPUT pInput = (PVBCPPINPUT)RTMemAlloc(RT_OFFSETOF(VBCPPINPUT, szName[cchName + 1]));
2041 if (!pInput)
2042 return vbcppError(pThis, "out of memory");
2043 pInput->pUp = pThis->pInputStack;
2044 pInput->pszSpecified = pInput->szName;
2045 memcpy(pInput->szName, pThis->pszInput, cchName + 1);
2046 pThis->pInputStack = pInput;
2047 int rc = ScmStreamInitForReading(&pInput->StrmInput, pThis->pszInput);
2048 if (RT_FAILURE(rc))
2049 return vbcppError(pThis, "ScmStreamInitForReading returned %Rrc when opening input file (%s)",
2050 rc, pThis->pszInput);
2051
2052 rc = ScmStreamInitForWriting(&pThis->StrmOutput, &pInput->StrmInput);
2053 if (RT_FAILURE(rc))
2054 return vbcppError(pThis, "ScmStreamInitForWriting returned %Rrc", rc);
2055
2056 pThis->fStrmOutputValid = true;
2057 return RTEXITCODE_SUCCESS;
2058}
2059
2060
2061/**
2062 * Changes the preprocessing mode.
2063 *
2064 * @param pThis The C preprocessor instance.
2065 * @param enmMode The new mode.
2066 */
2067static void vbcppSetMode(PVBCPP pThis, VBCPPMODE enmMode)
2068{
2069 switch (enmMode)
2070 {
2071 case kVBCppMode_Standard:
2072 pThis->fKeepComments = false;
2073 pThis->fRespectSourceDefines = true;
2074 pThis->fAllowRedefiningCmdLineDefines = true;
2075 pThis->fPassThruDefines = false;
2076 pThis->fUndecidedConditionals = false;
2077 pThis->fLineSplicing = true;
2078 pThis->enmIncludeAction = kVBCppIncludeAction_Include;
2079 break;
2080
2081 case kVBCppMode_Selective:
2082 pThis->fKeepComments = true;
2083 pThis->fRespectSourceDefines = false;
2084 pThis->fAllowRedefiningCmdLineDefines = false;
2085 pThis->fPassThruDefines = true;
2086 pThis->fUndecidedConditionals = true;
2087 pThis->fLineSplicing = false;
2088 pThis->enmIncludeAction = kVBCppIncludeAction_PassThru;
2089 break;
2090
2091 case kVBCppMode_SelectiveD:
2092 pThis->fKeepComments = true;
2093 pThis->fRespectSourceDefines = true;
2094 pThis->fAllowRedefiningCmdLineDefines = false;
2095 pThis->fPassThruDefines = false;
2096 pThis->fUndecidedConditionals = false;
2097 pThis->fLineSplicing = false;
2098 pThis->enmIncludeAction = kVBCppIncludeAction_Drop;
2099 break;
2100
2101 default:
2102 AssertFailedReturnVoid();
2103 }
2104 pThis->enmMode = enmMode;
2105}
2106
2107
2108/**
2109 * Parses the command line options.
2110 *
2111 * @returns Program exit code. Exit on non-success or if *pfExit is set.
2112 * @param pThis The C preprocessor instance.
2113 * @param argc The argument count.
2114 * @param argv The argument vector.
2115 * @param pfExit Pointer to the exit indicator.
2116 */
2117static RTEXITCODE vbcppParseOptions(PVBCPP pThis, int argc, char **argv, bool *pfExit)
2118{
2119 RTEXITCODE rcExit;
2120
2121 *pfExit = false;
2122
2123 /*
2124 * Option config.
2125 */
2126 static RTGETOPTDEF const s_aOpts[] =
2127 {
2128 { "--define", 'D', RTGETOPT_REQ_STRING },
2129 { "--include-dir", 'I', RTGETOPT_REQ_STRING },
2130 { "--undefine", 'U', RTGETOPT_REQ_STRING },
2131 { "--keep-comments", 'C', RTGETOPT_REQ_NOTHING },
2132 { "--strip-comments", 'c', RTGETOPT_REQ_NOTHING },
2133 { "--D-strip", 'd', RTGETOPT_REQ_NOTHING },
2134 };
2135
2136 RTGETOPTUNION ValueUnion;
2137 RTGETOPTSTATE GetOptState;
2138 int rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2139 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
2140
2141 /*
2142 * Process the options.
2143 */
2144 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
2145 {
2146 switch (rc)
2147 {
2148 case 'c':
2149 pThis->fKeepComments = false;
2150 break;
2151
2152 case 'C':
2153 pThis->fKeepComments = false;
2154 break;
2155
2156 case 'd':
2157 vbcppSetMode(pThis, kVBCppMode_SelectiveD);
2158 break;
2159
2160 case 'D':
2161 {
2162 const char *pszEqual = strchr(ValueUnion.psz, '=');
2163 if (pszEqual)
2164 rcExit = vbcppDefineAdd(pThis, ValueUnion.psz, pszEqual - ValueUnion.psz, pszEqual + 1, RTSTR_MAX, true);
2165 else
2166 rcExit = vbcppDefineAdd(pThis, ValueUnion.psz, RTSTR_MAX, "1", 1, true);
2167 if (rcExit != RTEXITCODE_SUCCESS)
2168 return rcExit;
2169 break;
2170 }
2171
2172 case 'I':
2173 rcExit = vbcppAddInclude(pThis, ValueUnion.psz);
2174 if (rcExit != RTEXITCODE_SUCCESS)
2175 return rcExit;
2176 break;
2177
2178 case 'U':
2179 rcExit = vbcppDefineUndef(pThis, ValueUnion.psz, RTSTR_MAX, true);
2180 break;
2181
2182 case 'h':
2183 RTPrintf("No help yet, sorry\n");
2184 *pfExit = true;
2185 return RTEXITCODE_SUCCESS;
2186
2187 case 'V':
2188 {
2189 /* The following is assuming that svn does it's job here. */
2190 static const char s_szRev[] = "$Revision: 41204 $";
2191 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
2192 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
2193 *pfExit = true;
2194 return RTEXITCODE_SUCCESS;
2195 }
2196
2197 case VINF_GETOPT_NOT_OPTION:
2198 if (!pThis->pszInput)
2199 pThis->pszInput = ValueUnion.psz;
2200 else if (!pThis->pszOutput)
2201 pThis->pszOutput = ValueUnion.psz;
2202 else
2203 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "too many file arguments");
2204 break;
2205
2206
2207 /*
2208 * Errors and bugs.
2209 */
2210 default:
2211 return RTGetOptPrintError(rc, &ValueUnion);
2212 }
2213 }
2214
2215 return RTEXITCODE_SUCCESS;
2216}
2217
2218
2219/**
2220 * Terminates the preprocessor.
2221 *
2222 * This may return failure if an error was delayed.
2223 *
2224 * @returns Exit code.
2225 * @param pThis The C preprocessor instance.
2226 */
2227static RTEXITCODE vbcppTerm(PVBCPP pThis)
2228{
2229 /*
2230 * Flush the output first.
2231 */
2232 if (pThis->fStrmOutputValid)
2233 {
2234 if (pThis->pszOutput)
2235 {
2236 int rc = ScmStreamWriteToFile(&pThis->StrmOutput, "%s", pThis->pszOutput);
2237 if (RT_FAILURE(rc))
2238 vbcppError(pThis, "ScmStreamWriteToFile failed with %Rrc when writing '%s'", rc, pThis->pszOutput);
2239 }
2240 else
2241 {
2242 int rc = ScmStreamWriteToStdOut(&pThis->StrmOutput);
2243 if (RT_FAILURE(rc))
2244 vbcppError(pThis, "ScmStreamWriteToStdOut failed with %Rrc", rc);
2245 }
2246 }
2247
2248 /*
2249 * Cleanup.
2250 */
2251 while (pThis->pInputStack)
2252 {
2253 ScmStreamDelete(&pThis->pInputStack->StrmInput);
2254 void *pvFree = pThis->pInputStack;
2255 pThis->pInputStack = pThis->pInputStack->pUp;
2256 RTMemFree(pvFree);
2257 }
2258
2259 ScmStreamDelete(&pThis->StrmOutput);
2260
2261 RTStrSpaceDestroy(&pThis->StrSpace, vbcppFreeDefine, NULL);
2262 pThis->StrSpace = NULL;
2263
2264 uint32_t i = pThis->cIncludes;
2265 while (i-- > 0)
2266 RTStrFree(pThis->papszIncludes[i]);
2267 RTMemFree(pThis->papszIncludes);
2268 pThis->papszIncludes = NULL;
2269
2270 return pThis->rcExit;
2271}
2272
2273
2274/**
2275 * Initializes the C preprocessor instance data.
2276 *
2277 * @param pThis The C preprocessor instance data.
2278 */
2279static void vbcppInit(PVBCPP pThis)
2280{
2281 vbcppSetMode(pThis, kVBCppMode_Selective);
2282 pThis->cIncludes = 0;
2283 pThis->papszIncludes = NULL;
2284 pThis->pszInput = NULL;
2285 pThis->pszOutput = NULL;
2286 pThis->StrSpace = NULL;
2287 pThis->UndefStrSpace = NULL;
2288 pThis->pExpStack = NULL;
2289 pThis->cExpStackDepth = 0;
2290 pThis->cCondStackDepth = 0;
2291 pThis->pCondStack = NULL;
2292 pThis->fIf0Mode = false;
2293 pThis->fMaybePreprocessorLine = true;
2294 VBCPP_BITMAP_EMPTY(pThis->bmDefined);
2295 VBCPP_BITMAP_EMPTY(pThis->bmArgs);
2296 pThis->cCondStackDepth = 0;
2297 pThis->pInputStack = NULL;
2298 RT_ZERO(pThis->StrmOutput);
2299 pThis->rcExit = RTEXITCODE_SUCCESS;
2300 pThis->fStrmOutputValid = false;
2301}
2302
2303
2304
2305int main(int argc, char **argv)
2306{
2307 int rc = RTR3InitExe(argc, &argv, 0);
2308 if (RT_FAILURE(rc))
2309 return RTMsgInitFailure(rc);
2310
2311 /*
2312 * Do the job. The code says it all.
2313 */
2314 VBCPP This;
2315 vbcppInit(&This);
2316 bool fExit;
2317 RTEXITCODE rcExit = vbcppParseOptions(&This, argc, argv, &fExit);
2318 if (!fExit && rcExit == RTEXITCODE_SUCCESS)
2319 {
2320 rcExit = vbcppOpenStreams(&This);
2321 if (rcExit == RTEXITCODE_SUCCESS)
2322 rcExit = vbcppPreprocess(&This);
2323 }
2324
2325 if (rcExit == RTEXITCODE_SUCCESS)
2326 rcExit = vbcppTerm(&This);
2327 else
2328 vbcppTerm(&This);
2329 return rcExit;
2330}
2331
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