VirtualBox

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

Last change on this file since 41217 was 41217, checked in by vboxsync, 12 years ago

VBoxCPP: Groks cpumctx.h now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 93.7 KB
Line 
1/* $Id: VBoxCPP.cpp 41217 2012-05-08 20:18:36Z 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 value length. */
104 size_t cchValue;
105 /** The define value. (This is followed by the name and arguments.) */
106 char szValue[1];
107} VBCPPDEF;
108/** Pointer to a define. */
109typedef VBCPPDEF *PVBCPPDEF;
110
111
112/**
113 * Expansion context.
114 */
115typedef struct VBCPPCTX
116{
117 /** The next context on the stack. */
118 struct VBCPPCTX *pUp;
119 /** The define being expanded. */
120 PVBCPPDEF pDef;
121 /** Arguments. */
122 struct VBCPPCTXARG
123 {
124 /** The value. */
125 const char *pchValue;
126 /** The value length. */
127 const char *cchValue;
128 } aArgs[1];
129} VBCPPCTX;
130/** Pointer to an define expansion context. */
131typedef VBCPPCTX *PVBCPPCTX;
132
133/**
134 * Evaluation result.
135 */
136typedef enum VBCPPEVAL
137{
138 kVBCppEval_Invalid = 0,
139 kVBCppEval_True,
140 kVBCppEval_False,
141 kVBCppEval_Undecided,
142 kVBCppEval_End
143} VBCPPEVAL;
144
145
146/**
147 * The condition kind.
148 */
149typedef enum VBCPPCONDKIND
150{
151 kVBCppCondKind_Invalid = 0,
152 /** \#if expr */
153 kVBCppCondKind_If,
154 /** \#ifdef define */
155 kVBCppCondKind_IfDef,
156 /** \#ifndef define */
157 kVBCppCondKind_IfNDef,
158 /** \#elif expr */
159 kVBCppCondKind_ElIf,
160 /** The end of valid values. */
161 kVBCppCondKind_End
162} VBCPPCONDKIND;
163
164
165/**
166 * Conditional stack entry.
167 */
168typedef struct VBCPPCOND
169{
170 /** The next conditional on the stack. */
171 struct VBCPPCOND *pUp;
172 /** The kind of conditional. This changes on encountering \#elif. */
173 VBCPPCONDKIND enmKind;
174 /** Evaluation result. */
175 VBCPPEVAL enmResult;
176 /** The evaluation result of the whole stack. */
177 VBCPPEVAL enmStackResult;
178
179 /** Whether we've seen the last else. */
180 bool fSeenElse;
181 /** The nesting level of this condition. */
182 uint16_t iLevel;
183 /** The nesting level of this condition wrt the ones we keep. */
184 uint16_t iKeepLevel;
185
186 /** The condition string. (Points within the stream buffer.) */
187 const char *pchCond;
188 /** The condition length. */
189 size_t cchCond;
190} VBCPPCOND;
191/** Pointer to a conditional stack entry. */
192typedef VBCPPCOND *PVBCPPCOND;
193
194
195/**
196 * Input buffer stack entry.
197 */
198typedef struct VBCPPINPUT
199{
200 /** Pointer to the next input on the stack. */
201 struct VBCPPINPUT *pUp;
202 /** The input stream. */
203 SCMSTREAM StrmInput;
204 /** Pointer into szName to the part which was specified. */
205 const char *pszSpecified;
206 /** The input file name with include path. */
207 char szName[1];
208} VBCPPINPUT;
209/** Pointer to a input buffer stack entry */
210typedef VBCPPINPUT *PVBCPPINPUT;
211
212
213/**
214 * The action to take with \#include.
215 */
216typedef enum VBCPPINCLUDEACTION
217{
218 kVBCppIncludeAction_Invalid = 0,
219 kVBCppIncludeAction_Include,
220 kVBCppIncludeAction_PassThru,
221 kVBCppIncludeAction_Drop,
222 kVBCppIncludeAction_End
223} VBCPPINCLUDEACTION;
224
225
226/**
227 * C Preprocessor instance data.
228 */
229typedef struct VBCPP
230{
231 /** @name Options
232 * @{ */
233 /** The preprocessing mode. */
234 VBCPPMODE enmMode;
235 /** Whether to keep comments. */
236 bool fKeepComments;
237 /** Whether to respect source defines. */
238 bool fRespectSourceDefines;
239 /** Whether to let source defines overrides the ones on the command
240 * line. */
241 bool fAllowRedefiningCmdLineDefines;
242 /** Whether to pass thru defines. */
243 bool fPassThruDefines;
244 /** Whether to allow undecided conditionals. */
245 bool fUndecidedConditionals;
246 /** Whether to pass thru D pragmas. */
247 bool fPassThruPragmaD;
248 /** Whether to pass thru STD pragmas. */
249 bool fPassThruPragmaSTD;
250 /** Whether to pass thru other pragmas. */
251 bool fPassThruPragmaOther;
252 /** Whether to remove dropped lines from the output. */
253 bool fRemoveDroppedLines;
254 /** Whether to preforme line splicing.
255 * @todo implement line splicing */
256 bool fLineSplicing;
257 /** What to do about include files. */
258 VBCPPINCLUDEACTION enmIncludeAction;
259
260 /** The number of include directories. */
261 uint32_t cIncludes;
262 /** Array of directories to search for include files. */
263 char **papszIncludes;
264
265 /** The name of the input file. */
266 const char *pszInput;
267 /** The name of the output file. NULL if stdout. */
268 const char *pszOutput;
269 /** @} */
270
271 /** The define string space. */
272 RTSTRSPACE StrSpace;
273 /** The string space holding explicitly undefined macros for selective
274 * preprocessing runs. */
275 RTSTRSPACE UndefStrSpace;
276 /** Indicates whether a C-word might need expansion.
277 * The bitmap is indexed by C-word lead character. Bits that are set
278 * indicates that the lead character is used in a \#define that we know and
279 * should expand. */
280 VBCPP_BITMAP_TYPE bmDefined[VBCPP_BITMAP_SIZE];
281 /** Indicates whether a C-word might need argument expansion.
282 * The bitmap is indexed by C-word lead character. Bits that are set
283 * indicates that the lead character is used in an argument of an currently
284 * expanding \#define. */
285 VBCPP_BITMAP_TYPE bmArgs[VBCPP_BITMAP_SIZE];
286
287 /** Expansion context stack. */
288 PVBCPPCTX pExpStack;
289 /** The current expansion stack depth. */
290 uint32_t cExpStackDepth;
291
292 /** The current depth of the conditional stack. */
293 uint32_t cCondStackDepth;
294 /** Conditional stack. */
295 PVBCPPCOND pCondStack;
296 /** The current condition evaluates to kVBCppEval_False, don't output. */
297 bool fIf0Mode;
298 /** Just dropped a line and should maybe drop the current line. */
299 bool fJustDroppedLine;
300
301 /** Whether the current line could be a preprocessor line.
302 * This is set when EOL is encountered and cleared again when a
303 * non-comment-or-space character is encountered. See vbcppPreprocess. */
304 bool fMaybePreprocessorLine;
305
306 /** The input stack depth */
307 uint32_t cInputStackDepth;
308 /** The input buffer stack. */
309 PVBCPPINPUT pInputStack;
310
311 /** The output stream. */
312 SCMSTREAM StrmOutput;
313
314 /** The status of the whole job, as far as we know. */
315 RTEXITCODE rcExit;
316 /** Whether StrmOutput is valid (for vbcppTerm). */
317 bool fStrmOutputValid;
318} VBCPP;
319/** Pointer to the C preprocessor instance data. */
320typedef VBCPP *PVBCPP;
321
322
323/*******************************************************************************
324* Internal Functions *
325*******************************************************************************/
326static PVBCPPDEF vbcppDefineLookup(PVBCPP pThis, const char *pszDefine, size_t cchDefine);
327
328
329
330
331/*
332 *
333 *
334 * Message Handling.
335 * Message Handling.
336 * Message Handling.
337 * Message Handling.
338 * Message Handling.
339 *
340 *
341 */
342
343
344/**
345 * Displays an error message.
346 *
347 * @returns RTEXITCODE_FAILURE
348 * @param pThis The C preprocessor instance.
349 * @param pszMsg The message.
350 * @param ... Message arguments.
351 */
352static RTEXITCODE vbcppError(PVBCPP pThis, const char *pszMsg, ...)
353{
354 NOREF(pThis);
355 if (pThis->pInputStack)
356 {
357 PSCMSTREAM pStrm = &pThis->pInputStack->StrmInput;
358
359 size_t const off = ScmStreamTell(pStrm);
360 size_t const iLine = ScmStreamTellLine(pStrm);
361 ScmStreamSeekByLine(pStrm, iLine);
362 size_t const offLine = ScmStreamTell(pStrm);
363
364 va_list va;
365 va_start(va, pszMsg);
366 RTPrintf("%s:%d:%zd: error: %N.\n", pThis->pInputStack->szName, iLine + 1, off - offLine + 1, pszMsg, va);
367 va_end(va);
368
369 size_t cchLine;
370 SCMEOL enmEof;
371 const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
372 if (pszLine)
373 RTPrintf(" %.*s\n"
374 " %*s^\n",
375 cchLine, pszLine, off - offLine, "");
376
377 ScmStreamSeekAbsolute(pStrm, off);
378 }
379 else
380 {
381 va_list va;
382 va_start(va, pszMsg);
383 RTMsgErrorV(pszMsg, va);
384 va_end(va);
385 }
386 return pThis->rcExit = RTEXITCODE_FAILURE;
387}
388
389
390/**
391 * Displays an error message.
392 *
393 * @returns RTEXITCODE_FAILURE
394 * @param pThis The C preprocessor instance.
395 * @param pszPos Pointer to the offending character.
396 * @param pszMsg The message.
397 * @param ... Message arguments.
398 */
399static RTEXITCODE vbcppErrorPos(PVBCPP pThis, const char *pszPos, const char *pszMsg, ...)
400{
401 NOREF(pszPos); NOREF(pThis);
402 va_list va;
403 va_start(va, pszMsg);
404 RTMsgErrorV(pszMsg, va);
405 va_end(va);
406 return pThis->rcExit = RTEXITCODE_FAILURE;
407}
408
409
410
411
412
413/*
414 *
415 *
416 * C Identifier/Word Parsing.
417 * C Identifier/Word Parsing.
418 * C Identifier/Word Parsing.
419 * C Identifier/Word Parsing.
420 * C Identifier/Word Parsing.
421 *
422 *
423 */
424
425
426/**
427 * Checks if the given character is a valid C identifier lead character.
428 *
429 * @returns true / false.
430 * @param ch The character to inspect.
431 */
432DECLINLINE(bool) vbcppIsCIdentifierLeadChar(char ch)
433{
434 return RT_C_IS_ALPHA(ch)
435 || ch == '_';
436}
437
438
439/**
440 * Checks if the given character is a valid C identifier character.
441 *
442 * @returns true / false.
443 * @param ch The character to inspect.
444 */
445DECLINLINE(bool) vbcppIsCIdentifierChar(char ch)
446{
447 return RT_C_IS_ALNUM(ch)
448 || ch == '_';
449}
450
451
452
453/**
454 *
455 * @returns @c true if valid, @c false if not. Error message already displayed
456 * on failure.
457 * @param pThis The C preprocessor instance.
458 * @param pchIdentifier The start of the identifier to validate.
459 * @param cchIdentifier The length of the identifier. RTSTR_MAX if not
460 * known.
461 */
462static bool vbcppValidateCIdentifier(PVBCPP pThis, const char *pchIdentifier, size_t cchIdentifier)
463{
464 if (cchIdentifier == RTSTR_MAX)
465 cchIdentifier = strlen(pchIdentifier);
466
467 if (cchIdentifier == 0)
468 {
469 vbcppErrorPos(pThis, pchIdentifier, "Zero length identifier");
470 return false;
471 }
472
473 if (!vbcppIsCIdentifierLeadChar(*pchIdentifier))
474 {
475 vbcppErrorPos(pThis, pchIdentifier, "Bad lead chararacter in identifier: '%.*s'", cchIdentifier, pchIdentifier);
476 return false;
477 }
478
479 for (size_t off = 1; off < cchIdentifier; off++)
480 {
481 if (!vbcppIsCIdentifierChar(pchIdentifier[off]))
482 {
483 vbcppErrorPos(pThis, pchIdentifier + off, "Illegal chararacter in identifier: '%.*s' (#%zu)", cchIdentifier, pchIdentifier, off + 1);
484 return false;
485 }
486 }
487
488 return true;
489}
490
491
492
493
494
495
496/*
497 *
498 *
499 * Output
500 * Output
501 * Output
502 * Output
503 * Output
504 *
505 *
506 */
507
508
509/**
510 * Outputs a character.
511 *
512 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
513 * @param pThis The C preprocessor instance.
514 * @param ch The character to output.
515 */
516static RTEXITCODE vbcppOutputCh(PVBCPP pThis, char ch)
517{
518 int rc = ScmStreamPutCh(&pThis->StrmOutput, ch);
519 if (RT_SUCCESS(rc))
520 return RTEXITCODE_SUCCESS;
521 return vbcppError(pThis, "Output error: %Rrc", rc);
522}
523
524
525/**
526 * Outputs a string.
527 *
528 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
529 * @param pThis The C preprocessor instance.
530 * @param pch The string.
531 * @param cch The number of characters to write.
532 */
533static RTEXITCODE vbcppOutputWrite(PVBCPP pThis, const char *pch, size_t cch)
534{
535 int rc = ScmStreamWrite(&pThis->StrmOutput, pch, cch);
536 if (RT_SUCCESS(rc))
537 return RTEXITCODE_SUCCESS;
538 return vbcppError(pThis, "Output error: %Rrc", rc);
539}
540
541
542static RTEXITCODE vbcppOutputComment(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart, size_t cchOutputted,
543 size_t cchMinIndent)
544{
545 size_t offCur = ScmStreamTell(pStrmInput);
546 if (offStart < offCur)
547 {
548 int rc = ScmStreamSeekAbsolute(pStrmInput, offStart);
549 AssertRCReturn(rc, vbcppError(pThis, "Input seek error: %Rrc", rc));
550
551 /*
552 * Use the same indent, if possible.
553 */
554 size_t cchIndent = offStart - ScmStreamTellOffsetOfLine(pStrmInput, ScmStreamTellLine(pStrmInput));
555 if (cchOutputted < cchIndent)
556 rc = ScmStreamPrintf(&pThis->StrmOutput, "%*s", cchIndent - cchOutputted, "");
557 else
558 rc = ScmStreamPutCh(&pThis->StrmOutput, ' ');
559 if (RT_FAILURE(rc))
560 return vbcppError(pThis, "Output error: %Rrc", rc);
561
562 /*
563 * Copy the bytes.
564 */
565 while (ScmStreamTell(pStrmInput) < offCur)
566 {
567 unsigned ch = ScmStreamGetCh(pStrmInput);
568 if (ch == ~(unsigned)0)
569 return vbcppError(pThis, "Input error: %Rrc", rc);
570 rc = ScmStreamPutCh(&pThis->StrmOutput, ch);
571 if (RT_FAILURE(rc))
572 return vbcppError(pThis, "Output error: %Rrc", rc);
573 }
574 }
575
576 return RTEXITCODE_SUCCESS;
577}
578
579
580
581
582
583/*
584 *
585 *
586 * Input
587 * Input
588 * Input
589 * Input
590 * Input
591 *
592 *
593 */
594
595
596/**
597 * Skips white spaces, including escaped new-lines.
598 *
599 * @param pStrmInput The input stream.
600 */
601static void vbcppProcessSkipWhiteAndEscapedEol(PSCMSTREAM pStrmInput)
602{
603 unsigned chPrev = ~(unsigned)0;
604 unsigned ch;
605 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
606 {
607 if (ch == '\r' || ch == '\n')
608 {
609 if (chPrev != '\\')
610 break;
611 chPrev = ch;
612 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
613 }
614 else if (RT_C_IS_SPACE(ch))
615 {
616 ch = chPrev;
617 ch = ScmStreamGetCh(pStrmInput);
618 Assert(ch == chPrev);
619 }
620 else
621 break;
622 }
623}
624
625
626/**
627 * Skips white spaces, escaped new-lines and multi line comments.
628 *
629 * @param pThis The C preprocessor instance.
630 * @param pStrmInput The input stream.
631 */
632static RTEXITCODE vbcppProcessSkipWhiteEscapedEolAndComments(PVBCPP pThis, PSCMSTREAM pStrmInput)
633{
634 unsigned chPrev = ~(unsigned)0;
635 unsigned ch;
636 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
637 {
638 if (!RT_C_IS_SPACE(ch))
639 {
640 /* Multi-line Comment? */
641 if (ch != '/')
642 break; /* most definitely, not. */
643
644 size_t offSaved = ScmStreamTell(pStrmInput);
645 ScmStreamGetCh(pStrmInput);
646 if (ScmStreamPeekCh(pStrmInput) != '*')
647 {
648 ScmStreamSeekAbsolute(pStrmInput, offSaved);
649 break; /* no */
650 }
651
652 /* Skip to the end of the comment. */
653 while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
654 {
655 if (ch == '*')
656 {
657 ch = ScmStreamGetCh(pStrmInput);
658 if (ch == '/')
659 break;
660 if (ch == ~(unsigned)0)
661 break;
662 }
663 }
664 if (ch == ~(unsigned)0)
665 return vbcppError(pThis, "unterminated multi-line comment");
666 chPrev = '/';
667 }
668 /* New line (also matched by RT_C_IS_SPACE). */
669 else if (ch == '\r' || ch == '\n')
670 {
671 /* Stop if not escaped. */
672 if (chPrev != '\\')
673 break;
674 chPrev = ch;
675 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
676 }
677 /* Real space char. */
678 else
679 {
680 chPrev = ch;
681 ch = ScmStreamGetCh(pStrmInput);
682 Assert(ch == chPrev);
683 }
684 }
685 return RTEXITCODE_SUCCESS;
686}
687
688
689/**
690 * Skips white spaces, escaped new-lines, and multi line comments, then checking
691 * that we're at the end of a line.
692 *
693 * @param pThis The C preprocessor instance.
694 * @param pStrmInput The input stream.
695 */
696static RTEXITCODE vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(PVBCPP pThis, PSCMSTREAM pStrmInput)
697{
698 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
699 if (rcExit == RTEXITCODE_SUCCESS)
700 {
701 unsigned ch = ScmStreamPeekCh(pStrmInput);
702 if ( ch != ~(unsigned)0
703 && ch != '\r'
704 && ch != '\n')
705 rcExit = vbcppError(pThis, "Did not expected anything more on this line");
706 }
707 return rcExit;
708}
709
710
711/**
712 * Skips white spaces.
713 *
714 * @returns The current location upon return..
715 * @param pStrmInput The input stream.
716 */
717static size_t vbcppProcessSkipWhite(PSCMSTREAM pStrmInput)
718{
719 unsigned ch;
720 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
721 {
722 if (!RT_C_IS_SPACE(ch) || ch == '\r' || ch == '\n')
723 break;
724 unsigned chCheck = ScmStreamGetCh(pStrmInput);
725 AssertBreak(chCheck == ch);
726 }
727 return ScmStreamTell(pStrmInput);
728}
729
730
731/**
732 * Skips input until the real end of the current directive line has been
733 * reached.
734 *
735 * This includes multiline comments starting on the same line
736 *
737 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
738 * @param pThis The C preprocessor instance.
739 * @param pStrmInput The input stream.
740 * @param poffComment Where to note down the position of the final
741 * comment. Optional.
742 */
743static RTEXITCODE vbcppInputSkipToEndOfDirectiveLine(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t *poffComment)
744{
745 if (poffComment)
746 *poffComment = ~(size_t)0;
747
748 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
749 bool fInComment = false;
750 unsigned chPrev = 0;
751 unsigned ch;
752 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
753 {
754 if (ch == '\r' || ch == '\n')
755 {
756 if (chPrev == '\\')
757 {
758 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
759 continue;
760 }
761 if (!fInComment)
762 break;
763 /* The expression continues after multi-line comments. Cool. :-) */
764 }
765 else if (!fInComment)
766 {
767 if (chPrev == '/' && ch == '*' )
768 {
769 fInComment = true;
770 if (poffComment)
771 *poffComment = ScmStreamTell(pStrmInput) - 1;
772 }
773 else if (chPrev == '/' && ch == '/')
774 {
775 if (poffComment)
776 *poffComment = ScmStreamTell(pStrmInput) - 1;
777 rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
778 break; /* done */
779 }
780 }
781 else if (ch == '/' && chPrev == '*')
782 fInComment = false;
783
784 /* advance */
785 chPrev = ch;
786 ch = ScmStreamGetCh(pStrmInput); Assert(ch == chPrev);
787 }
788 return rcExit;
789}
790
791
792
793/**
794 * Processes a multi-line comment.
795 *
796 * Must either string the comment or keep it. If the latter, we must refrain
797 * from replacing C-words in it.
798 *
799 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
800 * @param pThis The C preprocessor instance.
801 * @param pStrmInput The input stream.
802 */
803static RTEXITCODE vbcppProcessMultiLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
804{
805 /* The open comment sequence. */
806 ScmStreamGetCh(pStrmInput); /* '*' */
807 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
808 if ( pThis->fKeepComments
809 && !pThis->fIf0Mode)
810 rcExit = vbcppOutputWrite(pThis, "/*", 2);
811
812 /* The comment.*/
813 unsigned ch;
814 while ( rcExit == RTEXITCODE_SUCCESS
815 && (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0 )
816 {
817 if (ch == '*')
818 {
819 /* Closing sequence? */
820 unsigned ch2 = ScmStreamPeekCh(pStrmInput);
821 if (ch2 == '/')
822 {
823 ScmStreamGetCh(pStrmInput);
824 if ( pThis->fKeepComments
825 && !pThis->fIf0Mode)
826 rcExit = vbcppOutputWrite(pThis, "*/", 2);
827 break;
828 }
829 }
830
831 if (ch == '\r' || ch == '\n')
832 {
833 if ( ( pThis->fKeepComments
834 && !pThis->fIf0Mode)
835 || !pThis->fRemoveDroppedLines
836 || !ScmStreamIsAtStartOfLine(&pThis->StrmOutput))
837 rcExit = vbcppOutputCh(pThis, ch);
838 pThis->fJustDroppedLine = false;
839 pThis->fMaybePreprocessorLine = true;
840 }
841 else if ( pThis->fKeepComments
842 && !pThis->fIf0Mode)
843 rcExit = vbcppOutputCh(pThis, ch);
844
845 if (rcExit != RTEXITCODE_SUCCESS)
846 break;
847 }
848 return rcExit;
849}
850
851
852/**
853 * Processes a single line comment.
854 *
855 * Must either string the comment or keep it. If the latter, we must refrain
856 * from replacing C-words in it.
857 *
858 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
859 * @param pThis The C preprocessor instance.
860 * @param pStrmInput The input stream.
861 */
862static RTEXITCODE vbcppProcessOneLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
863{
864 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
865 SCMEOL enmEol;
866 size_t cchLine;
867 const char *pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol); Assert(pszLine);
868 pszLine--; cchLine++; /* unfetching the first slash. */
869 for (;;)
870 {
871 if ( pThis->fKeepComments
872 && !pThis->fIf0Mode)
873 rcExit = vbcppOutputWrite(pThis, pszLine, cchLine + enmEol);
874 else if ( !pThis->fIf0Mode
875 || !pThis->fRemoveDroppedLines
876 || !ScmStreamIsAtStartOfLine(&pThis->StrmOutput) )
877 rcExit = vbcppOutputWrite(pThis, pszLine + cchLine, enmEol);
878 if (rcExit != RTEXITCODE_SUCCESS)
879 break;
880 if ( cchLine == 0
881 || pszLine[cchLine - 1] != '\\')
882 break;
883
884 pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol);
885 if (!pszLine)
886 break;
887 }
888 pThis->fJustDroppedLine = false;
889 pThis->fMaybePreprocessorLine = true;
890 return rcExit;
891}
892
893
894/**
895 * Processes a double quoted string.
896 *
897 * Must not replace any C-words in strings.
898 *
899 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
900 * @param pThis The C preprocessor instance.
901 * @param pStrmInput The input stream.
902 */
903static RTEXITCODE vbcppProcessDoubleQuotedString(PVBCPP pThis, PSCMSTREAM pStrmInput)
904{
905 RTEXITCODE rcExit = vbcppOutputCh(pThis, '"');
906 if (rcExit == RTEXITCODE_SUCCESS)
907 {
908 bool fEscaped = false;
909 for (;;)
910 {
911 unsigned ch = ScmStreamGetCh(pStrmInput);
912 if (ch == ~(unsigned)0)
913 {
914 rcExit = vbcppError(pThis, "Unterminated double quoted string");
915 break;
916 }
917
918 rcExit = vbcppOutputCh(pThis, ch);
919 if (rcExit != RTEXITCODE_SUCCESS)
920 break;
921
922 if (ch == '"' && !fEscaped)
923 break;
924 fEscaped = !fEscaped && ch == '\\';
925 }
926 }
927 return rcExit;
928}
929
930
931/**
932 * Processes a single quoted litteral.
933 *
934 * Must not replace any C-words in strings.
935 *
936 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
937 * @param pThis The C preprocessor instance.
938 * @param pStrmInput The input stream.
939 */
940static RTEXITCODE vbcppProcessSingledQuotedString(PVBCPP pThis, PSCMSTREAM pStrmInput)
941{
942 RTEXITCODE rcExit = vbcppOutputCh(pThis, '\'');
943 if (rcExit == RTEXITCODE_SUCCESS)
944 {
945 bool fEscaped = false;
946 for (;;)
947 {
948 unsigned ch = ScmStreamGetCh(pStrmInput);
949 if (ch == ~(unsigned)0)
950 {
951 rcExit = vbcppError(pThis, "Unterminated singled quoted string");
952 break;
953 }
954
955 rcExit = vbcppOutputCh(pThis, ch);
956 if (rcExit != RTEXITCODE_SUCCESS)
957 break;
958
959 if (ch == '\'' && !fEscaped)
960 break;
961 fEscaped = !fEscaped && ch == '\\';
962 }
963 }
964 return rcExit;
965}
966
967
968/**
969 * Processes a C word, possibly replacing it with a definition.
970 *
971 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
972 * @param pThis The C preprocessor instance.
973 * @param pStrmInput The input stream.
974 * @param ch The first character.
975 */
976static RTEXITCODE vbcppProcessCWord(PVBCPP pThis, PSCMSTREAM pStrmInput, char ch)
977{
978 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
979 size_t cchDefine;
980 const char *pchDefine = ScmStreamCGetWordM1(pStrmInput, &cchDefine);
981 AssertReturn(pchDefine, vbcppError(pThis, "Internal error in ScmStreamCGetWordM1"));
982
983
984 /*
985 * Does this look like a define we know?
986 */
987 PVBCPPDEF pDef = vbcppDefineLookup(pThis, pchDefine, cchDefine);
988 if (pDef)
989 {
990 if (!pDef->fFunction)
991 {
992#if 0 /** @todo proper expansion! */
993 vbcppDefineExpandSimple(pThis, pDef, )
994#else
995 int rc = ScmStreamWrite(&pThis->StrmOutput, pDef->szValue, pDef->cchValue);
996 if (RT_FAILURE(rc))
997 rcExit = vbcppError(pThis, "Output error: %Rrc", rc);
998#endif
999 }
1000 else
1001 rcExit = vbcppError(pThis, "Expanding function macros is not implemented\n");
1002 }
1003 else
1004 {
1005 /*
1006 * Not a define, just output the text unchanged.
1007 */
1008 int rc = ScmStreamWrite(&pThis->StrmOutput, pchDefine, cchDefine);
1009 if (RT_FAILURE(rc))
1010 rcExit = vbcppError(pThis, "Output error: %Rrc", rc);
1011 }
1012 return rcExit;
1013}
1014
1015
1016
1017
1018
1019
1020
1021/*
1022 *
1023 *
1024 * D E F I N E S
1025 * D E F I N E S
1026 * D E F I N E S
1027 * D E F I N E S
1028 * D E F I N E S
1029 *
1030 *
1031 */
1032
1033
1034/**
1035 * Checks if a define exists.
1036 *
1037 * @returns true or false.
1038 * @param pThis The C preprocessor instance.
1039 * @param pszDefine The define name and optionally the argument
1040 * list.
1041 * @param cchDefine The length of the name. RTSTR_MAX is ok.
1042 */
1043static bool vbcppDefineExists(PVBCPP pThis, const char *pszDefine, size_t cchDefine)
1044{
1045 return cchDefine > 0
1046 && VBCPP_BITMAP_IS_SET(pThis->bmDefined, *pszDefine)
1047 && RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine) != NULL;
1048}
1049
1050
1051/**
1052 * Looks up a define.
1053 *
1054 * @returns Pointer to the define if found, NULL if not.
1055 * @param pThis The C preprocessor instance.
1056 * @param pszDefine The define name and optionally the argument
1057 * list.
1058 * @param cchDefine The length of the name. RTSTR_MAX is ok.
1059 */
1060static PVBCPPDEF vbcppDefineLookup(PVBCPP pThis, const char *pszDefine, size_t cchDefine)
1061{
1062 if (!cchDefine)
1063 return NULL;
1064 if (!VBCPP_BITMAP_IS_SET(pThis->bmDefined, *pszDefine))
1065 return NULL;
1066 return (PVBCPPDEF)RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine);
1067}
1068
1069
1070/**
1071 * Frees a define.
1072 *
1073 * @returns VINF_SUCCESS (used when called by RTStrSpaceDestroy)
1074 * @param pStr Pointer to the VBCPPDEF::Core member.
1075 * @param pvUser Unused.
1076 */
1077static DECLCALLBACK(int) vbcppFreeDefine(PRTSTRSPACECORE pStr, void *pvUser)
1078{
1079 RTMemFree(pStr);
1080 NOREF(pvUser);
1081 return VINF_SUCCESS;
1082}
1083
1084
1085/**
1086 * Removes a define.
1087 *
1088 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
1089 * @param pThis The C preprocessor instance.
1090 * @param pszDefine The define name, no argument list or anything.
1091 * @param cchDefine The length of the name. RTSTR_MAX is ok.
1092 * @param fExplicitUndef Explicit undefinition, that is, in a selective
1093 * preprocessing run it will evaluate to undefined.
1094 */
1095static RTEXITCODE vbcppDefineUndef(PVBCPP pThis, const char *pszDefine, size_t cchDefine, bool fExplicitUndef)
1096{
1097 PRTSTRSPACECORE pHit = RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine);
1098 if (pHit)
1099 {
1100 RTStrSpaceRemove(&pThis->StrSpace, pHit->pszString);
1101 vbcppFreeDefine(pHit, NULL);
1102 }
1103
1104 if (fExplicitUndef)
1105 {
1106 if (cchDefine == RTSTR_MAX)
1107 cchDefine = strlen(pszDefine);
1108
1109 PRTSTRSPACECORE pStr = (PRTSTRSPACECORE)RTMemAlloc(sizeof(*pStr) + cchDefine + 1);
1110 if (!pStr)
1111 return vbcppError(pThis, "out of memory");
1112 char *pszDst = (char *)(pStr + 1);
1113 pStr->pszString = pszDst;
1114 memcpy(pszDst, pszDefine, cchDefine);
1115 pszDst[cchDefine] = '\0';
1116 if (!RTStrSpaceInsert(&pThis->UndefStrSpace, pStr))
1117 RTMemFree(pStr);
1118 }
1119
1120 return RTEXITCODE_SUCCESS;
1121}
1122
1123
1124/**
1125 * Inserts a define (rejecting and freeing it in some case).
1126 *
1127 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
1128 * @param pThis The C preprocessor instance.
1129 * @param pDef The define to insert.
1130 */
1131static RTEXITCODE vbcppDefineInsert(PVBCPP pThis, PVBCPPDEF pDef)
1132{
1133 /*
1134 * Reject illegal macro names.
1135 */
1136 if (!strcmp(pDef->Core.pszString, "defined"))
1137 {
1138 RTEXITCODE rcExit = vbcppError(pThis, "Cannot use '%s' as a macro name", pDef->Core.pszString);
1139 vbcppFreeDefine(&pDef->Core, NULL);
1140 return rcExit;
1141 }
1142
1143 /*
1144 * Ignore in source-file defines when doing selective preprocessing.
1145 */
1146 if ( !pThis->fRespectSourceDefines
1147 && !pDef->fCmdLine)
1148 {
1149 /* Ignore*/
1150 vbcppFreeDefine(&pDef->Core, NULL);
1151 return RTEXITCODE_SUCCESS;
1152 }
1153
1154 /*
1155 * Insert it and update the lead character hint bitmap.
1156 */
1157 if (RTStrSpaceInsert(&pThis->StrSpace, &pDef->Core))
1158 VBCPP_BITMAP_SET(pThis->bmDefined, *pDef->Core.pszString);
1159 else
1160 {
1161 /*
1162 * Duplicate. When doing selective D preprocessing, let the command
1163 * line take precendece.
1164 */
1165 PVBCPPDEF pOld = (PVBCPPDEF)RTStrSpaceGet(&pThis->StrSpace, pDef->Core.pszString); Assert(pOld);
1166 if ( pThis->fAllowRedefiningCmdLineDefines
1167 || pDef->fCmdLine == pOld->fCmdLine)
1168 {
1169 if (pDef->fCmdLine)
1170 RTMsgWarning("Redefining '%s'\n", pDef->Core.pszString);
1171
1172 RTStrSpaceRemove(&pThis->StrSpace, pOld->Core.pszString);
1173 vbcppFreeDefine(&pOld->Core, NULL);
1174
1175 bool fRc = RTStrSpaceInsert(&pThis->StrSpace, &pDef->Core);
1176 Assert(fRc);
1177 }
1178 else
1179 {
1180 RTMsgWarning("Ignoring redefinition of '%s'\n", pDef->Core.pszString);
1181 vbcppFreeDefine(&pDef->Core, NULL);
1182 }
1183 }
1184
1185 return RTEXITCODE_SUCCESS;
1186}
1187
1188
1189/**
1190 * Adds a define.
1191 *
1192 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
1193 * @param pThis The C preprocessor instance.
1194 * @param pszDefine The define name, no parameter list.
1195 * @param cchDefine The length of the name.
1196 * @param pszParams The parameter list.
1197 * @param cchParams The length of the parameter list.
1198 * @param pszValue The value.
1199 * @param cchDefine The length of the value.
1200 * @param fCmdLine Set if originating on the command line.
1201 */
1202static RTEXITCODE vbcppDefineAddFn(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
1203 const char *pszParams, size_t cchParams,
1204 const char *pszValue, size_t cchValue,
1205 bool fCmdLine)
1206
1207{
1208 Assert(RTStrNLen(pszDefine, cchDefine) == cchDefine);
1209 Assert(RTStrNLen(pszParams, cchParams) == cchParams);
1210 Assert(RTStrNLen(pszValue, cchValue) == cchValue);
1211
1212 /*
1213 * Determin the number of arguments and how much space their names
1214 * requires. Performing syntax validation while parsing.
1215 */
1216 uint32_t cchArgNames = 0;
1217 uint32_t cArgs = 0;
1218 for (size_t off = 0; off < cchParams; off++)
1219 {
1220 /* Skip blanks and maybe one comma. */
1221 bool fIgnoreComma = cArgs != 0;
1222 while (off < cchParams)
1223 {
1224 if (!RT_C_IS_SPACE(pszParams[off]))
1225 {
1226 if (pszParams[off] != ',' || !fIgnoreComma)
1227 {
1228 if (vbcppIsCIdentifierLeadChar(pszParams[off]))
1229 break;
1230 /** @todo variadic macros. */
1231 return vbcppErrorPos(pThis, &pszParams[off], "Unexpected character");
1232 }
1233 fIgnoreComma = false;
1234 }
1235 off++;
1236 }
1237 if (off >= cchParams)
1238 break;
1239
1240 /* Found and argument. First character is already validated. */
1241 cArgs++;
1242 cchArgNames += 2;
1243 off++;
1244 while ( off < cchParams
1245 && vbcppIsCIdentifierChar(pszParams[off]))
1246 off++, cchArgNames++;
1247 }
1248
1249 /*
1250 * Allocate a structure.
1251 */
1252 size_t cbDef = RT_OFFSETOF(VBCPPDEF, szValue[cchValue + 1 + cchDefine + 1 + cchArgNames])
1253 + sizeof(const char *) * cArgs;
1254 cbDef = RT_ALIGN_Z(cbDef, sizeof(const char *));
1255 PVBCPPDEF pDef = (PVBCPPDEF)RTMemAlloc(cbDef);
1256 if (!pDef)
1257 return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
1258
1259 char *pszDst = &pDef->szValue[cchValue + 1];
1260 pDef->Core.pszString = pszDst;
1261 memcpy(pszDst, pszDefine, cchDefine);
1262 pszDst += cchDefine;
1263 *pszDst++ = '\0';
1264 pDef->fFunction = true;
1265 pDef->fVarArg = false;
1266 pDef->fCmdLine = fCmdLine;
1267 pDef->cArgs = cArgs;
1268 pDef->papszArgs = (const char **)((uintptr_t)pDef + cbDef - sizeof(const char *) * cArgs);
1269 VBCPP_BITMAP_EMPTY(pDef->bmArgs);
1270 pDef->cchValue = cchValue;
1271 memcpy(pDef->szValue, pszValue, cchValue);
1272 pDef->szValue[cchValue] = '\0';
1273
1274 /*
1275 * Set up the arguments.
1276 */
1277 uint32_t iArg = 0;
1278 for (size_t off = 0; off < cchParams; off++)
1279 {
1280 /* Skip blanks and maybe one comma. */
1281 bool fIgnoreComma = cArgs != 0;
1282 while (off < cchParams)
1283 {
1284 if (!RT_C_IS_SPACE(pszParams[off]))
1285 {
1286 if (pszParams[off] != ',' || !fIgnoreComma)
1287 break;
1288 fIgnoreComma = false;
1289 }
1290 off++;
1291 }
1292 if (off >= cchParams)
1293 break;
1294
1295 /* Found and argument. First character is already validated. */
1296 pDef->papszArgs[iArg] = pszDst;
1297 do
1298 {
1299 *pszDst++ = pszParams[off++];
1300 } while ( off < cchParams
1301 && vbcppIsCIdentifierChar(pszParams[off]));
1302 *pszDst++ = '\0';
1303 iArg++;
1304 }
1305 Assert((uintptr_t)pszDst <= (uintptr_t)pDef->papszArgs);
1306
1307 return vbcppDefineInsert(pThis, pDef);
1308}
1309
1310
1311/**
1312 * Adds a define.
1313 *
1314 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
1315 * @param pThis The C preprocessor instance.
1316 * @param pszDefine The define name and optionally the argument
1317 * list.
1318 * @param cchDefine The length of the name. RTSTR_MAX is ok.
1319 * @param pszValue The value.
1320 * @param cchDefine The length of the value. RTSTR_MAX is ok.
1321 * @param fCmdLine Set if originating on the command line.
1322 */
1323static RTEXITCODE vbcppDefineAdd(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
1324 const char *pszValue, size_t cchValue, bool fCmdLine)
1325{
1326 /*
1327 * We need the lengths. Trim the input.
1328 */
1329 if (cchDefine == RTSTR_MAX)
1330 cchDefine = strlen(pszDefine);
1331 while (cchDefine > 0 && RT_C_IS_SPACE(*pszDefine))
1332 pszDefine++, cchDefine--;
1333 while (cchDefine > 0 && RT_C_IS_SPACE(pszDefine[cchDefine - 1]))
1334 cchDefine--;
1335 if (!cchDefine)
1336 return vbcppErrorPos(pThis, pszDefine, "The define has no name");
1337
1338 if (cchValue == RTSTR_MAX)
1339 cchValue = strlen(pszValue);
1340 while (cchValue > 0 && RT_C_IS_SPACE(*pszValue))
1341 pszValue++, cchValue--;
1342 while (cchValue > 0 && RT_C_IS_SPACE(pszValue[cchValue - 1]))
1343 cchValue--;
1344
1345 /*
1346 * Arguments make the job a bit more annoying. Handle that elsewhere
1347 */
1348 const char *pszParams = (const char *)memchr(pszDefine, '(', cchDefine);
1349 if (pszParams)
1350 {
1351 size_t cchParams = pszDefine + cchDefine - pszParams;
1352 cchDefine -= cchParams;
1353 if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
1354 return RTEXITCODE_FAILURE;
1355 if (pszParams[cchParams - 1] != ')')
1356 return vbcppErrorPos(pThis, pszParams + cchParams - 1, "Missing closing parenthesis");
1357 pszParams++;
1358 cchParams -= 2;
1359 return vbcppDefineAddFn(pThis, pszDefine, cchDefine, pszParams, cchParams, pszValue, cchValue, fCmdLine);
1360 }
1361
1362 /*
1363 * Simple define, no arguments.
1364 */
1365 if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
1366 return RTEXITCODE_FAILURE;
1367
1368 PVBCPPDEF pDef = (PVBCPPDEF)RTMemAlloc(RT_OFFSETOF(VBCPPDEF, szValue[cchValue + 1 + cchDefine + 1]));
1369 if (!pDef)
1370 return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
1371
1372 pDef->Core.pszString = &pDef->szValue[cchValue + 1];
1373 memcpy((char *)pDef->Core.pszString, pszDefine, cchDefine);
1374 ((char *)pDef->Core.pszString)[cchDefine] = '\0';
1375 pDef->fFunction = false;
1376 pDef->fVarArg = false;
1377 pDef->fCmdLine = fCmdLine;
1378 pDef->cArgs = 0;
1379 pDef->papszArgs = NULL;
1380 VBCPP_BITMAP_EMPTY(pDef->bmArgs);
1381 pDef->cchValue = cchValue;
1382 memcpy(pDef->szValue, pszValue, cchValue);
1383 pDef->szValue[cchValue] = '\0';
1384
1385 return vbcppDefineInsert(pThis, pDef);
1386}
1387
1388
1389/**
1390 * Processes a abbreviated line number directive.
1391 *
1392 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1393 * @param pThis The C preprocessor instance.
1394 * @param pStrmInput The input stream.
1395 * @param offStart The stream position where the directive
1396 * started (for pass thru).
1397 */
1398static RTEXITCODE vbcppProcessDefine(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1399{
1400 /*
1401 * Parse it.
1402 */
1403 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1404 if (rcExit == RTEXITCODE_SUCCESS)
1405 {
1406 size_t cchDefine;
1407 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
1408 if (pchDefine)
1409 {
1410 /* If it's a function style define, parse out the parameter list. */
1411 size_t cchParams = 0;
1412 const char *pchParams = NULL;
1413 unsigned ch = ScmStreamPeekCh(pStrmInput);
1414 if (ch == '(')
1415 {
1416 ScmStreamGetCh(pStrmInput);
1417 pchParams = ScmStreamGetCur(pStrmInput);
1418
1419 unsigned chPrev = ch;
1420 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
1421 {
1422 if (ch == '\r' || ch == '\n')
1423 {
1424 if (chPrev != '\\')
1425 {
1426 rcExit = vbcppError(pThis, "Missing ')'");
1427 break;
1428 }
1429 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
1430 }
1431 if (ch == ')')
1432 {
1433 cchParams = ScmStreamGetCur(pStrmInput) - pchParams;
1434 ScmStreamGetCh(pStrmInput);
1435 break;
1436 }
1437 ScmStreamGetCh(pStrmInput);
1438 }
1439 }
1440 /* The simple kind. */
1441 else if (!RT_C_IS_SPACE(ch) && ch != ~(unsigned)0)
1442 rcExit = vbcppError(pThis, "Expected whitespace after macro name");
1443
1444 /* Parse out the value. */
1445 if (rcExit == RTEXITCODE_SUCCESS)
1446 rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1447 if (rcExit == RTEXITCODE_SUCCESS)
1448 {
1449 size_t offValue = ScmStreamTell(pStrmInput);
1450 const char *pchValue = ScmStreamGetCur(pStrmInput);
1451 unsigned chPrev = ch;
1452 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
1453 {
1454 if (ch == '\r' || ch == '\n')
1455 {
1456 if (chPrev != '\\')
1457 break;
1458 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
1459 }
1460 chPrev = ScmStreamGetCh(pStrmInput);
1461 }
1462 size_t cchValue = ScmStreamGetCur(pStrmInput) - pchValue;
1463
1464 /*
1465 * Execute.
1466 */
1467 if (pchParams)
1468 rcExit = vbcppDefineAddFn(pThis, pchDefine, cchDefine, pchParams, cchParams, pchValue, cchValue, false);
1469 else
1470 rcExit = vbcppDefineAdd(pThis, pchDefine, cchDefine, pchValue, cchValue, false);
1471
1472 /*
1473 * Pass thru?
1474 */
1475 if ( rcExit == RTEXITCODE_SUCCESS
1476 && pThis->fPassThruDefines)
1477 {
1478 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
1479 size_t cch;
1480 if (pchParams)
1481 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s(%.*s)",
1482 cchIndent, "", cchDefine, pchDefine, cchParams, pchParams);
1483 else
1484 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s",
1485 cchIndent, "", cchDefine, pchDefine);
1486 if (cch > 0)
1487 vbcppOutputComment(pThis, pStrmInput, offValue, cch, 1);
1488 else
1489 rcExit = vbcppError(pThis, "output error");
1490 }
1491 }
1492
1493 }
1494 }
1495 return rcExit;
1496}
1497
1498
1499/**
1500 * Processes a abbreviated line number directive.
1501 *
1502 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1503 * @param pThis The C preprocessor instance.
1504 * @param pStrmInput The input stream.
1505 * @param offStart The stream position where the directive
1506 * started (for pass thru).
1507 */
1508static RTEXITCODE vbcppProcessUndef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
1509{
1510 return vbcppError(pThis, "Not implemented %s", __FUNCTION__);
1511}
1512
1513
1514
1515
1516
1517/*
1518 *
1519 *
1520 * C O N D I T I O N A L S
1521 * C O N D I T I O N A L S
1522 * C O N D I T I O N A L S
1523 * C O N D I T I O N A L S
1524 * C O N D I T I O N A L S
1525 *
1526 *
1527 */
1528
1529
1530/**
1531 * Combines current stack result with the one being pushed.
1532 *
1533 * @returns Combined result.
1534 * @param enmEvalPush The result of the condition being pushed.
1535 * @param enmEvalStack The current stack result.
1536 */
1537static VBCPPEVAL vbcppCondCombine(VBCPPEVAL enmEvalPush, VBCPPEVAL enmEvalStack)
1538{
1539 if (enmEvalStack == kVBCppEval_False)
1540 return kVBCppEval_False;
1541 return enmEvalPush;
1542}
1543
1544
1545/**
1546 * Pushes an conditional onto the stack.
1547 *
1548 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1549 * @param pThis The C preprocessor instance.
1550 * @param pStrmInput The current input stream.
1551 * @param offStart Not currently used, using @a pchCondition and
1552 * @a cchCondition instead.
1553 * @param enmKind The kind of conditional.
1554 * @param enmResult The result of the evaluation.
1555 * @param pchCondition The raw condition.
1556 * @param cchCondition The length of @a pchCondition.
1557 */
1558static RTEXITCODE vbcppCondPush(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart,
1559 VBCPPCONDKIND enmKind, VBCPPEVAL enmResult,
1560 const char *pchCondition, size_t cchCondition)
1561{
1562 if (pThis->cCondStackDepth >= _64K)
1563 return vbcppError(pThis, "Too many nested #if/#ifdef/#ifndef statements");
1564
1565 /*
1566 * Allocate a new entry and push it.
1567 */
1568 PVBCPPCOND pCond = (PVBCPPCOND)RTMemAlloc(sizeof(*pCond));
1569 if (!pCond)
1570 return vbcppError(pThis, "out of memory");
1571
1572 PVBCPPCOND pUp = pThis->pCondStack;
1573 pCond->enmKind = enmKind;
1574 pCond->enmResult = enmResult;
1575 pCond->enmStackResult = pUp ? vbcppCondCombine(enmResult, pUp->enmStackResult) : enmResult;
1576 pCond->fSeenElse = false;
1577 pCond->iLevel = pThis->cCondStackDepth;
1578 pCond->iKeepLevel = (pUp ? pUp->iKeepLevel : 0) + enmResult == kVBCppEval_Undecided;
1579 pCond->pchCond = pchCondition;
1580 pCond->cchCond = cchCondition;
1581
1582 pCond->pUp = pThis->pCondStack;
1583 pThis->pCondStack = pCond;
1584 pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
1585
1586 /*
1587 * Do pass thru.
1588 */
1589 if ( !pThis->fIf0Mode
1590 && enmResult == kVBCppEval_Undecided)
1591 {
1592 /** @todo this is stripping comments of \#ifdef and \#ifndef atm. */
1593 const char *pszDirective;
1594 switch (enmKind)
1595 {
1596 case kVBCppCondKind_If: pszDirective = "if"; break;
1597 case kVBCppCondKind_IfDef: pszDirective = "ifdef"; break;
1598 case kVBCppCondKind_IfNDef: pszDirective = "ifndef"; break;
1599 case kVBCppCondKind_ElIf: pszDirective = "elif"; break;
1600 default: AssertFailedReturn(RTEXITCODE_FAILURE);
1601 }
1602 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*s%s %.*s",
1603 pCond->iKeepLevel - 1, "", pszDirective, cchCondition, pchCondition);
1604 if (cch < 0)
1605 return vbcppError(pThis, "Output error %Rrc", (int)cch);
1606 }
1607 else
1608 pThis->fJustDroppedLine = true;
1609
1610 return RTEXITCODE_SUCCESS;
1611}
1612
1613
1614#if 0
1615typedef enum VBCPPEXPRKIND
1616{
1617 kVBCppExprKind_Invalid = 0,
1618 kVBCppExprKind_Binary,
1619 kVBCppExprKind_Unary,
1620 kVBCppExprKind_SignedValue,
1621 kVBCppExprKind_UnsignedValue,
1622 kVBCppExprKind_Define,
1623 kVBCppExprKind_End
1624} VBCPPEXPRKIND;
1625
1626typedef struct VBCPPEXPR *PVBCPPEXPR;
1627
1628/**
1629 * Expression parsing structure.
1630 */
1631typedef struct VBCPPEXPR
1632{
1633 /** Pointer to the first byte of the expression. */
1634 const char *pchExpr;
1635 /** The length of the expression. */
1636 size_t cchExpr;
1637
1638 uint32_t iDepth;
1639 /** The kind of expression. */
1640 VBCPPEXPRKIND enmKind;
1641 /** */
1642 union
1643 {
1644 struct
1645 {
1646 VBCPPUNARYOP enmOperator;
1647 PVBCPPEXPR pArg;
1648 } Unary;
1649
1650 struct
1651 {
1652 VBCPPBINARYOP enmOperator;
1653 PVBCPPEXPR pLeft;
1654 PVBCPPEXPR pRight;
1655 } Binary;
1656
1657 struct
1658 {
1659 int64_t s64;
1660 unsigned cBits;
1661 } SignedValue;
1662
1663 struct
1664 {
1665 uint64_t u64;
1666 unsigned cBits;
1667 } UnsignedValue;
1668
1669 struct
1670 {
1671 const char *pch;
1672 size_t cch;
1673 } Define;
1674
1675 } u;
1676 /** Parent expression. */
1677 PVBCPPEXPR pParent;
1678} VBCPPEXPR;
1679
1680
1681
1682typedef struct VBCPPEXPRPARSER
1683{
1684 PVBCPPEXPR pStack
1685} VBCPPEXPRPARSER;
1686
1687
1688/**
1689 * Operator return statuses.
1690 */
1691typedef enum
1692{
1693 kExprRet_Error = -1,
1694 kExprRet_Ok = 0,
1695 kExprRet_Operator,
1696 kExprRet_Operand,
1697 kExprRet_EndOfExpr,
1698 kExprRet_End
1699} VBCPPEXPRRET;
1700
1701
1702static VBCPPEXPRRET vbcppExprEatUnaryOrOperand(PVBCPPEXPRPARSER pThis, PSCMSTREAM pStrmInput)
1703{
1704
1705}
1706
1707#endif
1708
1709
1710/**
1711 * Evalutes the expression.
1712 *
1713 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1714 * @param pThis The C preprocessor instance.
1715 * @param pszExpr The expression.
1716 * @param cchExpr The length of the expression.
1717 * @param penmResult Where to store the result.
1718 */
1719static RTEXITCODE vbcppExprEval(PVBCPP pThis, char *pszExpr, size_t cchExpr, VBCPPEVAL *penmResult)
1720{
1721 Assert(strlen(pszExpr) == cchExpr);
1722#if 0
1723 /** @todo */
1724#else /* Greatly simplified for getting DTrace working. */
1725 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1726 if (strcmp(pszExpr, "1"))
1727 *penmResult = kVBCppEval_True;
1728 else if (strcmp(pszExpr, "0"))
1729 *penmResult = kVBCppEval_False;
1730 else if (pThis->fUndecidedConditionals)
1731 *penmResult = kVBCppEval_Undecided;
1732 else
1733 rcExit = vbcppError(pThis, "Too compliated expression '%s'", pszExpr);
1734#endif
1735 return rcExit;
1736}
1737
1738
1739/**
1740 * Expands known macros in the expression.
1741 *
1742 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1743 * @param pThis The C preprocessor instance.
1744 * @param ppszExpr The expression to expand. Input/Output.
1745 * @param pcchExpr The length of the expression. Input/Output.
1746 * @param pcReplacements Where to store the number of replacements made.
1747 * Optional.
1748 */
1749static RTEXITCODE vbcppExprExpand(PVBCPP pThis, char **ppszExpr, size_t *pcchExpr, size_t *pcReplacements)
1750{
1751 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1752 char *pszExpr = *ppszExpr;
1753 size_t cchExpr = pcchExpr ? *pcchExpr : strlen(pszExpr);
1754 size_t cbExprAlloc = cchExpr + 1;
1755 size_t cHits = 0;
1756 size_t off = 0;
1757 char ch;
1758 while ((ch = pszExpr[off]) != '\0')
1759 {
1760 if (!vbcppIsCIdentifierLeadChar(ch))
1761 off++;
1762 else
1763 {
1764 /* Extract the identifier. */
1765 size_t const offIdentifier = off++;
1766 while ( off < cchExpr
1767 && vbcppIsCIdentifierChar(pszExpr[off]))
1768 off++;
1769 size_t const cchIdentifier = off - offIdentifier;
1770
1771 /* Does it exist? Will save a whole lot of trouble if it doesn't. */
1772 PVBCPPDEF pDef = vbcppDefineLookup(pThis, &pszExpr[offIdentifier], cchIdentifier);
1773 if (pDef)
1774 {
1775 /* Skip white space and check for parenthesis. */
1776 while ( off < cchExpr
1777 && RT_C_IS_SPACE(pszExpr[off]))
1778 off++;
1779 if ( off < cchExpr
1780 && pszExpr[off] == '(')
1781 {
1782 /* Try expand function define. */
1783 rcExit = vbcppError(pThis, "Expanding function macros is not yet implemented");
1784 break;
1785 }
1786 else
1787 {
1788 /* Expand simple define if found. */
1789 if (pDef->cchValue + 2 < cchIdentifier)
1790 {
1791 size_t offDelta = cchIdentifier - pDef->cchValue - 2;
1792 memmove(&pszExpr[offIdentifier], &pszExpr[offIdentifier + offDelta],
1793 cchExpr - offIdentifier - offDelta + 1); /* Lazy bird is moving too much! */
1794 cchExpr -= offDelta;
1795 }
1796 else if (pDef->cchValue + 2 > cchIdentifier)
1797 {
1798 size_t offDelta = pDef->cchValue + 2 - cchIdentifier;
1799 if (cchExpr + offDelta + 1 > cbExprAlloc)
1800 {
1801 do
1802 {
1803 cbExprAlloc *= 2;
1804 } while (cchExpr + offDelta + 1 > cbExprAlloc);
1805 void *pv = RTMemRealloc(pszExpr, cbExprAlloc);
1806 if (!pv)
1807 {
1808 rcExit = vbcppError(pThis, "out of memory (%zu bytes)", cbExprAlloc);
1809 break;
1810 }
1811 pszExpr = (char *)pv;
1812 }
1813 memmove(&pszExpr[offIdentifier + offDelta], &pszExpr[offIdentifier],
1814 cchExpr - offIdentifier + 1); /* Lazy bird is moving too much! */
1815 cchExpr += offDelta;
1816 }
1817
1818 /* Insert with spaces around it. Not entirely sure how
1819 standard compliant this is... */
1820 pszExpr[offIdentifier] = ' ';
1821 memcpy(&pszExpr[offIdentifier + 1], pDef->szValue, pDef->cchValue);
1822 pszExpr[offIdentifier + 1 + pDef->cchValue] = ' ';
1823
1824 /* Restart parsing at the inserted macro. */
1825 off = offIdentifier + 1;
1826 }
1827 }
1828 }
1829 }
1830
1831 return rcExit;
1832}
1833
1834
1835
1836/**
1837 * Extracts the expression.
1838 *
1839 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1840 * @param pThis The C preprocessor instance.
1841 * @param pStrmInput The input stream.
1842 * @param ppszExpr Where to return the expression string..
1843 * @param pcchExpr Where to return the expression length.
1844 * Optional.
1845 * @param poffComment Where to note down the position of the final
1846 * comment. Optional.
1847 */
1848static RTEXITCODE vbcppExprExtract(PVBCPP pThis, PSCMSTREAM pStrmInput,
1849 char **ppszExpr, size_t *pcchExpr, size_t *poffComment)
1850{
1851 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1852 size_t cbExprAlloc = 0;
1853 size_t cchExpr = 0;
1854 char *pszExpr = NULL;
1855 bool fInComment = false;
1856 size_t offComment = ~(size_t)0;
1857 unsigned chPrev = 0;
1858 unsigned ch;
1859 while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
1860 {
1861 if (ch == '\r' || ch == '\n')
1862 {
1863 if (chPrev == '\\')
1864 {
1865 ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
1866 pszExpr[--cchExpr] = '\0';
1867 continue;
1868 }
1869 if (!fInComment)
1870 break;
1871 /* The expression continues after multi-line comments. Cool. :-) */
1872 }
1873 else if (!fInComment)
1874 {
1875 if (chPrev == '/' && ch == '*' )
1876 {
1877 pszExpr[--cchExpr] = '\0';
1878 fInComment = true;
1879 offComment = ScmStreamTell(pStrmInput) - 1;
1880 }
1881 else if (chPrev == '/' && ch == '/')
1882 {
1883 offComment = ScmStreamTell(pStrmInput) - 1;
1884 rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
1885 break; /* done */
1886 }
1887 /* Append the char to the expression. */
1888 else
1889 {
1890 if (cchExpr + 2 > cbExprAlloc)
1891 {
1892 cbExprAlloc = cbExprAlloc ? cbExprAlloc * 2 : 8;
1893 void *pv = RTMemRealloc(pszExpr, cbExprAlloc);
1894 if (!pv)
1895 {
1896 rcExit = vbcppError(pThis, "out of memory (%zu bytes)", cbExprAlloc);
1897 break;
1898 }
1899 pszExpr = (char *)pv;
1900 }
1901 pszExpr[cchExpr++] = ch;
1902 pszExpr[cchExpr] = '\0';
1903 }
1904 }
1905 else if (ch == '/' && chPrev == '*')
1906 fInComment = false;
1907
1908 /* advance */
1909 chPrev = ch;
1910 ch = ScmStreamGetCh(pStrmInput); Assert(ch == chPrev);
1911 }
1912
1913 if (rcExit == RTEXITCODE_SUCCESS)
1914 {
1915 *ppszExpr = pszExpr;
1916 if (pcchExpr)
1917 *pcchExpr = cchExpr;
1918 if (poffComment)
1919 *poffComment = offComment;
1920 }
1921 else
1922 RTMemFree(pszExpr);
1923 return rcExit;
1924}
1925
1926
1927/**
1928 * Processes a abbreviated line number directive.
1929 *
1930 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1931 * @param pThis The C preprocessor instance.
1932 * @param pStrmInput The input stream.
1933 * @param offStart The stream position where the directive
1934 * started (for pass thru).
1935 * @param enmKind The kind of directive we're processing.
1936 */
1937static RTEXITCODE vbcppProcessIfOrElif(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart,
1938 VBCPPCONDKIND enmKind)
1939{
1940 /*
1941 * Check for missing #if if #elif.
1942 */
1943 if ( enmKind == kVBCppCondKind_ElIf
1944 && !pThis->pCondStack )
1945 return vbcppError(pThis, "#elif without #if");
1946
1947 /*
1948 * Extract and expand the expression string.
1949 */
1950 const char *pchCondition = ScmStreamGetCur(pStrmInput);
1951 char *pszExpr;
1952 size_t cchExpr;
1953 size_t offComment;
1954 RTEXITCODE rcExit = vbcppExprExtract(pThis, pStrmInput, &pszExpr, &cchExpr, &offComment);
1955 if (rcExit == RTEXITCODE_SUCCESS)
1956 {
1957 size_t const cchCondition = ScmStreamGetCur(pStrmInput) - pchCondition;
1958 size_t cReplacements;
1959 rcExit = vbcppExprExpand(pThis, &pszExpr, &cchExpr, &cReplacements);
1960 if (rcExit == RTEXITCODE_SUCCESS)
1961 {
1962 /*
1963 * Strip it and check that it's not empty.
1964 */
1965 char *pszExpr2 = pszExpr;
1966 while (cchExpr > 0 && RT_C_IS_SPACE(*pszExpr2))
1967 pszExpr2++, cchExpr--;
1968
1969 while (cchExpr > 0 && RT_C_IS_SPACE(pszExpr2[cchExpr - 1]))
1970 pszExpr2[--cchExpr] = '\0';
1971 if (cchExpr)
1972 {
1973 /*
1974 * Now, evalute the expression.
1975 */
1976 VBCPPEVAL enmResult;
1977 rcExit = vbcppExprEval(pThis, pszExpr2, cchExpr, &enmResult);
1978 if (rcExit == RTEXITCODE_SUCCESS)
1979 {
1980 /*
1981 * Take action.
1982 */
1983 if (enmKind != kVBCppCondKind_ElIf)
1984 rcExit = vbcppCondPush(pThis, pStrmInput, offComment, enmKind, enmResult,
1985 pchCondition, cchCondition);
1986 else
1987 {
1988 PVBCPPCOND pCond = pThis->pCondStack;
1989 if ( pCond->enmResult != kVBCppEval_Undecided
1990 && ( !pCond->pUp
1991 || pCond->pUp->enmStackResult == kVBCppEval_True))
1992 {
1993 if (enmResult == kVBCppEval_True)
1994 pCond->enmStackResult = kVBCppEval_False;
1995 else
1996 pCond->enmStackResult = kVBCppEval_True;
1997 pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
1998 }
1999 pCond->enmResult = enmResult;
2000 pCond->pchCond = pchCondition;
2001 pCond->cchCond = cchCondition;
2002
2003 /*
2004 * Do #elif pass thru.
2005 */
2006 if ( !pThis->fIf0Mode
2007 && pCond->enmResult == kVBCppEval_Undecided)
2008 {
2009 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*selif", pCond->iKeepLevel - 1, "");
2010 if (cch > 0)
2011 rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 2);
2012 else
2013 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
2014 }
2015 else
2016 pThis->fJustDroppedLine = true;
2017 }
2018 }
2019 }
2020 else
2021 rcExit = vbcppError(pThis, "Empty #if expression");
2022 }
2023 RTMemFree(pszExpr);
2024 }
2025 return rcExit;
2026}
2027
2028
2029/**
2030 * Processes a abbreviated line number directive.
2031 *
2032 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2033 * @param pThis The C preprocessor instance.
2034 * @param pStrmInput The input stream.
2035 * @param offStart The stream position where the directive
2036 * started (for pass thru).
2037 */
2038static RTEXITCODE vbcppProcessIfDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
2039{
2040 /*
2041 * Parse it.
2042 */
2043 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
2044 if (rcExit == RTEXITCODE_SUCCESS)
2045 {
2046 size_t cchDefine;
2047 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
2048 if (pchDefine)
2049 {
2050 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
2051 if (rcExit == RTEXITCODE_SUCCESS)
2052 {
2053 /*
2054 * Evaluate it.
2055 */
2056 VBCPPEVAL enmEval;
2057 if (vbcppDefineExists(pThis, pchDefine, cchDefine))
2058 enmEval = kVBCppEval_True;
2059 else if ( !pThis->fUndecidedConditionals
2060 || RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
2061 enmEval = kVBCppEval_False;
2062 else
2063 enmEval = kVBCppEval_Undecided;
2064 rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfDef, enmEval,
2065 pchDefine, cchDefine);
2066 }
2067 }
2068 else
2069 rcExit = vbcppError(pThis, "Malformed #ifdef");
2070 }
2071 return rcExit;
2072}
2073
2074
2075/**
2076 * Processes a abbreviated line number directive.
2077 *
2078 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2079 * @param pThis The C preprocessor instance.
2080 * @param pStrmInput The input stream.
2081 * @param offStart The stream position where the directive
2082 * started (for pass thru).
2083 */
2084static RTEXITCODE vbcppProcessIfNDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
2085{
2086 /*
2087 * Parse it.
2088 */
2089 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
2090 if (rcExit == RTEXITCODE_SUCCESS)
2091 {
2092 size_t cchDefine;
2093 const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
2094 if (pchDefine)
2095 {
2096 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
2097 if (rcExit == RTEXITCODE_SUCCESS)
2098 {
2099 /*
2100 * Evaluate it.
2101 */
2102 VBCPPEVAL enmEval;
2103 if (vbcppDefineExists(pThis, pchDefine, cchDefine))
2104 enmEval = kVBCppEval_False;
2105 else if ( !pThis->fUndecidedConditionals
2106 || RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
2107 enmEval = kVBCppEval_True;
2108 else
2109 enmEval = kVBCppEval_Undecided;
2110 rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfNDef, enmEval,
2111 pchDefine, cchDefine);
2112 }
2113 }
2114 else
2115 rcExit = vbcppError(pThis, "Malformed #ifndef");
2116 }
2117 return rcExit;
2118}
2119
2120
2121/**
2122 * Processes a abbreviated line number directive.
2123 *
2124 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2125 * @param pThis The C preprocessor instance.
2126 * @param pStrmInput The input stream.
2127 * @param offStart The stream position where the directive
2128 * started (for pass thru).
2129 */
2130static RTEXITCODE vbcppProcessElse(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
2131{
2132 /*
2133 * Nothing to parse, just comment positions to find and note down.
2134 */
2135 offStart = vbcppProcessSkipWhite(pStrmInput);
2136 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
2137 if (rcExit == RTEXITCODE_SUCCESS)
2138 {
2139 /*
2140 * Execute.
2141 */
2142 PVBCPPCOND pCond = pThis->pCondStack;
2143 if (pCond)
2144 {
2145 if (!pCond->fSeenElse)
2146 {
2147 pCond->fSeenElse = true;
2148 if ( pCond->enmResult != kVBCppEval_Undecided
2149 && ( !pCond->pUp
2150 || pCond->pUp->enmStackResult == kVBCppEval_True))
2151 {
2152 if (pCond->enmResult == kVBCppEval_True)
2153 pCond->enmStackResult = kVBCppEval_False;
2154 else
2155 pCond->enmStackResult = kVBCppEval_True;
2156 pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
2157 }
2158
2159 /*
2160 * Do pass thru.
2161 */
2162 if ( !pThis->fIf0Mode
2163 && pCond->enmResult == kVBCppEval_Undecided)
2164 {
2165 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*selse", pCond->iKeepLevel - 1, "");
2166 if (cch > 0)
2167 rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 2);
2168 else
2169 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
2170 }
2171 else
2172 pThis->fJustDroppedLine = true;
2173 }
2174 else
2175 rcExit = vbcppError(pThis, "Double #else or/and missing #endif");
2176 }
2177 else
2178 rcExit = vbcppError(pThis, "#else without #if");
2179 }
2180 return rcExit;
2181}
2182
2183
2184/**
2185 * Processes a abbreviated line number directive.
2186 *
2187 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2188 * @param pThis The C preprocessor instance.
2189 * @param pStrmInput The input stream.
2190 * @param offStart The stream position where the directive
2191 * started (for pass thru).
2192 */
2193static RTEXITCODE vbcppProcessEndif(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
2194{
2195 /*
2196 * Nothing to parse, just comment positions to find and note down.
2197 */
2198 offStart = vbcppProcessSkipWhite(pStrmInput);
2199 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
2200 if (rcExit == RTEXITCODE_SUCCESS)
2201 {
2202 /*
2203 * Execute.
2204 */
2205 PVBCPPCOND pCond = pThis->pCondStack;
2206 if (pCond)
2207 {
2208 pThis->pCondStack = pCond->pUp;
2209 pThis->fIf0Mode = pCond->pUp && pCond->pUp->enmStackResult == kVBCppEval_False;
2210
2211 /*
2212 * Do pass thru.
2213 */
2214 if ( !pThis->fIf0Mode
2215 && pCond->enmResult == kVBCppEval_Undecided)
2216 {
2217 ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sendif", pCond->iKeepLevel - 1, "");
2218 if (cch > 0)
2219 rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 1);
2220 else
2221 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
2222 }
2223 else
2224 pThis->fJustDroppedLine = true;
2225 }
2226 else
2227 rcExit = vbcppError(pThis, "#endif without #if");
2228 }
2229 return rcExit;
2230}
2231
2232
2233
2234
2235
2236/*
2237 *
2238 *
2239 * Misc Directives
2240 * Misc Directives
2241 * Misc Directives
2242 * Misc Directives
2243 *
2244 *
2245 */
2246
2247
2248/**
2249 * Adds an include directory.
2250 *
2251 * @returns Program exit code, with error message on failure.
2252 * @param pThis The C preprocessor instance.
2253 * @param pszDir The directory to add.
2254 */
2255static RTEXITCODE vbcppAddInclude(PVBCPP pThis, const char *pszDir)
2256{
2257 uint32_t cIncludes = pThis->cIncludes;
2258 if (cIncludes >= _64K)
2259 return vbcppError(pThis, "Too many include directories");
2260
2261 void *pv = RTMemRealloc(pThis->papszIncludes, (cIncludes + 1) * sizeof(char **));
2262 if (!pv)
2263 return vbcppError(pThis, "No memory for include directories");
2264 pThis->papszIncludes = (char **)pv;
2265
2266 int rc = RTStrDupEx(&pThis->papszIncludes[cIncludes], pszDir);
2267 if (RT_FAILURE(rc))
2268 return vbcppError(pThis, "No string memory for include directories");
2269
2270 pThis->cIncludes = cIncludes + 1;
2271 return RTEXITCODE_SUCCESS;
2272}
2273
2274
2275/**
2276 * Processes a abbreviated line number directive.
2277 *
2278 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2279 * @param pThis The C preprocessor instance.
2280 * @param pStrmInput The input stream.
2281 * @param offStart The stream position where the directive
2282 * started (for pass thru).
2283 */
2284static RTEXITCODE vbcppProcessInclude(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
2285{
2286 /*
2287 * Parse it.
2288 */
2289 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
2290 if (rcExit == RTEXITCODE_SUCCESS)
2291 {
2292 size_t cchFileSpec = 0;
2293 const char *pchFileSpec = NULL;
2294 size_t cchFilename = 0;
2295 const char *pchFilename = NULL;
2296
2297 unsigned ch = ScmStreamPeekCh(pStrmInput);
2298 unsigned chType = ch;
2299 if (ch == '"' || ch == '<')
2300 {
2301 ScmStreamGetCh(pStrmInput);
2302 pchFileSpec = pchFilename = ScmStreamGetCur(pStrmInput);
2303 unsigned chEnd = chType == '<' ? '>' : '"';
2304 unsigned chPrev = ch;
2305 while ( (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0
2306 && ch != chEnd)
2307 {
2308 if (ch == '\r' || ch == '\n')
2309 {
2310 rcExit = vbcppError(pThis, "Multi-line include file specfications are not supported");
2311 break;
2312 }
2313 }
2314
2315 if (rcExit == RTEXITCODE_SUCCESS)
2316 {
2317 if (ch != ~(unsigned)0)
2318 cchFileSpec = cchFilename = ScmStreamGetCur(pStrmInput) - pchFilename - 1;
2319 else
2320 rcExit = vbcppError(pThis, "Expected '%c'", chType);
2321 }
2322 }
2323 else if (vbcppIsCIdentifierLeadChar(ch))
2324 {
2325 //pchFileSpec = ScmStreamCGetWord(pStrmInput, &cchFileSpec);
2326 rcExit = vbcppError(pThis, "Including via a define is not implemented yet");
2327 }
2328 else
2329 rcExit = vbcppError(pThis, "Malformed include directive");
2330
2331 /*
2332 * Take down the location of the next non-white space, in case we need
2333 * to pass thru the directive further down. Then skip to the end of the
2334 * line.
2335 */
2336 size_t const offIncEnd = vbcppProcessSkipWhite(pStrmInput);
2337 if (rcExit == RTEXITCODE_SUCCESS)
2338 rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
2339
2340 if (rcExit == RTEXITCODE_SUCCESS)
2341 {
2342 /*
2343 * Execute it.
2344 */
2345 if (pThis->enmIncludeAction == kVBCppIncludeAction_Include)
2346 {
2347 /** @todo Search for the include file and push it onto the input stack.
2348 * Not difficult, just unnecessary rigth now. */
2349 rcExit = vbcppError(pThis, "Includes are fully implemented");
2350 }
2351 else if (pThis->enmIncludeAction == kVBCppIncludeAction_PassThru)
2352 {
2353 /* Pretty print the passthru. */
2354 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
2355 size_t cch;
2356 if (chType == '<')
2357 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude <%.*s>",
2358 cchIndent, "", cchFileSpec, pchFileSpec);
2359 else if (chType == '"')
2360 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude \"%.*s\"",
2361 cchIndent, "", cchFileSpec, pchFileSpec);
2362 else
2363 cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude %.*s",
2364 cchIndent, "", cchFileSpec, pchFileSpec);
2365 if (cch > 0)
2366 rcExit = vbcppOutputComment(pThis, pStrmInput, offIncEnd, cch, 1);
2367 else
2368 rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
2369
2370 }
2371 else
2372 {
2373 Assert(pThis->enmIncludeAction == kVBCppIncludeAction_Drop);
2374 pThis->fJustDroppedLine = true;
2375 }
2376 }
2377 }
2378 return rcExit;
2379}
2380
2381
2382/**
2383 * Processes a abbreviated line number directive.
2384 *
2385 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2386 * @param pThis The C preprocessor instance.
2387 * @param pStrmInput The input stream.
2388 * @param offStart The stream position where the directive
2389 * started (for pass thru).
2390 */
2391static RTEXITCODE vbcppProcessPragma(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
2392{
2393 /*
2394 * Parse out the first word.
2395 */
2396 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
2397 if (rcExit == RTEXITCODE_SUCCESS)
2398 {
2399 size_t cchPragma;
2400 const char *pchPragma = ScmStreamCGetWord(pStrmInput, &cchPragma);
2401 if (pchPragma)
2402 {
2403 size_t const off2nd = vbcppProcessSkipWhite(pStrmInput);
2404 size_t offComment;
2405 rcExit = vbcppInputSkipToEndOfDirectiveLine(pThis, pStrmInput, &offComment);
2406 if (rcExit == RTEXITCODE_SUCCESS)
2407 {
2408 /*
2409 * What to do about this
2410 */
2411 bool fPassThru = false;
2412 if ( cchPragma == 1
2413 && *pchPragma == 'D')
2414 fPassThru = pThis->fPassThruPragmaD;
2415 else if ( cchPragma == 3
2416 && !strncmp(pchPragma, "STD", 3))
2417 fPassThru = pThis->fPassThruPragmaSTD;
2418 else
2419 fPassThru = pThis->fPassThruPragmaOther;
2420 if (fPassThru)
2421 {
2422 unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
2423 size_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*spragma %.*s",
2424 cchIndent, "", cchPragma, pchPragma);
2425 if (cch > 0)
2426 rcExit = vbcppOutputComment(pThis, pStrmInput, off2nd, cch, 1);
2427 else
2428 rcExit = vbcppError(pThis, "output error");
2429 }
2430 else
2431 pThis->fJustDroppedLine = true;
2432 }
2433 }
2434 else
2435 rcExit = vbcppError(pThis, "Malformed #pragma");
2436 }
2437
2438 return rcExit;
2439}
2440
2441
2442/**
2443 * Processes a abbreviated line number directive.
2444 *
2445 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2446 * @param pThis The C preprocessor instance.
2447 * @param pStrmInput The input stream.
2448 * @param offStart The stream position where the directive
2449 * started (for pass thru).
2450 */
2451static RTEXITCODE vbcppProcessLineNo(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
2452{
2453 return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
2454}
2455
2456
2457/**
2458 * Processes a abbreviated line number directive.
2459 *
2460 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2461 * @param pThis The C preprocessor instance.
2462 * @param pStrmInput The input stream.
2463 */
2464static RTEXITCODE vbcppProcessLineNoShort(PVBCPP pThis, PSCMSTREAM pStrmInput)
2465{
2466 return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
2467}
2468
2469
2470/**
2471 * Handles a preprocessor directive.
2472 *
2473 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2474 * @param pThis The C preprocessor instance.
2475 * @param pStrmInput The input stream.
2476 */
2477static RTEXITCODE vbcppProcessDirective(PVBCPP pThis, PSCMSTREAM pStrmInput)
2478{
2479 /*
2480 * Get the directive and do a string switch on it.
2481 */
2482 RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
2483 if (rcExit != RTEXITCODE_SUCCESS)
2484 return rcExit;
2485 size_t cchDirective;
2486 const char *pchDirective = ScmStreamCGetWord(pStrmInput, &cchDirective);
2487 if (pchDirective)
2488 {
2489 size_t const offStart = ScmStreamTell(pStrmInput);
2490#define IS_DIRECTIVE(a_sz) ( sizeof(a_sz) - 1 == cchDirective && strncmp(pchDirective, a_sz, sizeof(a_sz) - 1) == 0)
2491 if (IS_DIRECTIVE("if"))
2492 rcExit = vbcppProcessIfOrElif(pThis, pStrmInput, offStart, kVBCppCondKind_If);
2493 else if (IS_DIRECTIVE("elif"))
2494 rcExit = vbcppProcessIfOrElif(pThis, pStrmInput, offStart, kVBCppCondKind_ElIf);
2495 else if (IS_DIRECTIVE("ifdef"))
2496 rcExit = vbcppProcessIfDef(pThis, pStrmInput, offStart);
2497 else if (IS_DIRECTIVE("ifndef"))
2498 rcExit = vbcppProcessIfNDef(pThis, pStrmInput, offStart);
2499 else if (IS_DIRECTIVE("else"))
2500 rcExit = vbcppProcessElse(pThis, pStrmInput, offStart);
2501 else if (IS_DIRECTIVE("endif"))
2502 rcExit = vbcppProcessEndif(pThis, pStrmInput, offStart);
2503 else if (!pThis->fIf0Mode)
2504 {
2505 if (IS_DIRECTIVE("include"))
2506 rcExit = vbcppProcessInclude(pThis, pStrmInput, offStart);
2507 else if (IS_DIRECTIVE("define"))
2508 rcExit = vbcppProcessDefine(pThis, pStrmInput, offStart);
2509 else if (IS_DIRECTIVE("undef"))
2510 rcExit = vbcppProcessUndef(pThis, pStrmInput, offStart);
2511 else if (IS_DIRECTIVE("pragma"))
2512 rcExit = vbcppProcessPragma(pThis, pStrmInput, offStart);
2513 else if (IS_DIRECTIVE("line"))
2514 rcExit = vbcppProcessLineNo(pThis, pStrmInput, offStart);
2515 else
2516 rcExit = vbcppError(pThis, "Unknown preprocessor directive '#%.*s'", cchDirective, pchDirective);
2517 }
2518#undef IS_DIRECTIVE
2519 }
2520 else if (!pThis->fIf0Mode)
2521 {
2522 /* Could it be a # <num> "file" directive? */
2523 unsigned ch = ScmStreamPeekCh(pStrmInput);
2524 if (RT_C_IS_DIGIT(ch))
2525 rcExit = vbcppProcessLineNoShort(pThis, pStrmInput);
2526 else
2527 rcExit = vbcppError(pThis, "Malformed preprocessor directive");
2528 }
2529 return rcExit;
2530}
2531
2532
2533/*
2534 *
2535 *
2536 * M a i n b o d y.
2537 * M a i n b o d y.
2538 * M a i n b o d y.
2539 * M a i n b o d y.
2540 * M a i n b o d y.
2541 *
2542 *
2543 */
2544
2545
2546/**
2547 * Does the actually preprocessing of the input file.
2548 *
2549 * @returns Exit code.
2550 * @param pThis The C preprocessor instance.
2551 */
2552static RTEXITCODE vbcppPreprocess(PVBCPP pThis)
2553{
2554 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2555
2556 /*
2557 * Parse.
2558 */
2559 while (pThis->pInputStack)
2560 {
2561 pThis->fMaybePreprocessorLine = true;
2562
2563 PSCMSTREAM pStrmInput = &pThis->pInputStack->StrmInput;
2564 unsigned ch;
2565 while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
2566 {
2567 if (ch == '/')
2568 {
2569 ch = ScmStreamPeekCh(pStrmInput);
2570 if (ch == '*')
2571 rcExit = vbcppProcessMultiLineComment(pThis, pStrmInput);
2572 else if (ch == '/')
2573 rcExit = vbcppProcessOneLineComment(pThis, pStrmInput);
2574 else
2575 {
2576 pThis->fMaybePreprocessorLine = false;
2577 if (!pThis->fIf0Mode)
2578 rcExit = vbcppOutputCh(pThis, '/');
2579 }
2580 }
2581 else if (ch == '#' && pThis->fMaybePreprocessorLine)
2582 {
2583 rcExit = vbcppProcessDirective(pThis, pStrmInput);
2584 pStrmInput = &pThis->pInputStack->StrmInput;
2585 }
2586 else if (ch == '\r' || ch == '\n')
2587 {
2588 if ( ( !pThis->fIf0Mode
2589 && !pThis->fJustDroppedLine)
2590 || !pThis->fRemoveDroppedLines
2591 || !ScmStreamIsAtStartOfLine(&pThis->StrmOutput))
2592 rcExit = vbcppOutputCh(pThis, ch);
2593 pThis->fJustDroppedLine = false;
2594 pThis->fMaybePreprocessorLine = true;
2595 }
2596 else if (RT_C_IS_SPACE(ch))
2597 {
2598 if (!pThis->fIf0Mode)
2599 rcExit = vbcppOutputCh(pThis, ch);
2600 }
2601 else
2602 {
2603 pThis->fMaybePreprocessorLine = false;
2604 if (!pThis->fIf0Mode)
2605 {
2606 if (ch == '"')
2607 rcExit = vbcppProcessDoubleQuotedString(pThis, pStrmInput);
2608 else if (ch == '\'')
2609 rcExit = vbcppProcessSingledQuotedString(pThis, pStrmInput);
2610 else if (vbcppIsCIdentifierLeadChar(ch))
2611 rcExit = vbcppProcessCWord(pThis, pStrmInput, ch);
2612 else
2613 rcExit = vbcppOutputCh(pThis, ch);
2614 }
2615 }
2616 if (rcExit != RTEXITCODE_SUCCESS)
2617 break;
2618 }
2619
2620 /*
2621 * Check for errors.
2622 */
2623 if (rcExit != RTEXITCODE_SUCCESS)
2624 break;
2625
2626 /*
2627 * Pop the input stack.
2628 */
2629 PVBCPPINPUT pPopped = pThis->pInputStack;
2630 pThis->pInputStack = pPopped->pUp;
2631 RTMemFree(pPopped);
2632 }
2633
2634 return rcExit;
2635}
2636
2637
2638/**
2639 * Opens the input and output streams.
2640 *
2641 * @returns Exit code.
2642 * @param pThis The C preprocessor instance.
2643 */
2644static RTEXITCODE vbcppOpenStreams(PVBCPP pThis)
2645{
2646 if (!pThis->pszInput)
2647 return vbcppError(pThis, "Preprocessing the standard input stream is currently not supported");
2648
2649 size_t cchName = strlen(pThis->pszInput);
2650 PVBCPPINPUT pInput = (PVBCPPINPUT)RTMemAlloc(RT_OFFSETOF(VBCPPINPUT, szName[cchName + 1]));
2651 if (!pInput)
2652 return vbcppError(pThis, "out of memory");
2653 pInput->pUp = pThis->pInputStack;
2654 pInput->pszSpecified = pInput->szName;
2655 memcpy(pInput->szName, pThis->pszInput, cchName + 1);
2656 pThis->pInputStack = pInput;
2657 int rc = ScmStreamInitForReading(&pInput->StrmInput, pThis->pszInput);
2658 if (RT_FAILURE(rc))
2659 return vbcppError(pThis, "ScmStreamInitForReading returned %Rrc when opening input file (%s)",
2660 rc, pThis->pszInput);
2661
2662 rc = ScmStreamInitForWriting(&pThis->StrmOutput, &pInput->StrmInput);
2663 if (RT_FAILURE(rc))
2664 return vbcppError(pThis, "ScmStreamInitForWriting returned %Rrc", rc);
2665
2666 pThis->fStrmOutputValid = true;
2667 return RTEXITCODE_SUCCESS;
2668}
2669
2670
2671/**
2672 * Changes the preprocessing mode.
2673 *
2674 * @param pThis The C preprocessor instance.
2675 * @param enmMode The new mode.
2676 */
2677static void vbcppSetMode(PVBCPP pThis, VBCPPMODE enmMode)
2678{
2679 switch (enmMode)
2680 {
2681 case kVBCppMode_Standard:
2682 pThis->fKeepComments = false;
2683 pThis->fRespectSourceDefines = true;
2684 pThis->fAllowRedefiningCmdLineDefines = true;
2685 pThis->fPassThruDefines = false;
2686 pThis->fUndecidedConditionals = false;
2687 pThis->fPassThruPragmaD = false;
2688 pThis->fPassThruPragmaSTD = true;
2689 pThis->fPassThruPragmaOther = true;
2690 pThis->fRemoveDroppedLines = false;
2691 pThis->fLineSplicing = true;
2692 pThis->enmIncludeAction = kVBCppIncludeAction_Include;
2693 break;
2694
2695 case kVBCppMode_Selective:
2696 pThis->fKeepComments = true;
2697 pThis->fRespectSourceDefines = false;
2698 pThis->fAllowRedefiningCmdLineDefines = false;
2699 pThis->fPassThruDefines = true;
2700 pThis->fUndecidedConditionals = true;
2701 pThis->fPassThruPragmaD = true;
2702 pThis->fPassThruPragmaSTD = true;
2703 pThis->fPassThruPragmaOther = true;
2704 pThis->fRemoveDroppedLines = true;
2705 pThis->fLineSplicing = false;
2706 pThis->enmIncludeAction = kVBCppIncludeAction_PassThru;
2707 break;
2708
2709 case kVBCppMode_SelectiveD:
2710 pThis->fKeepComments = true;
2711 pThis->fRespectSourceDefines = true;
2712 pThis->fAllowRedefiningCmdLineDefines = false;
2713 pThis->fPassThruDefines = false;
2714 pThis->fUndecidedConditionals = false;
2715 pThis->fPassThruPragmaD = true;
2716 pThis->fPassThruPragmaSTD = false;
2717 pThis->fPassThruPragmaOther = false;
2718 pThis->fRemoveDroppedLines = true;
2719 pThis->fLineSplicing = false;
2720 pThis->enmIncludeAction = kVBCppIncludeAction_Drop;
2721 break;
2722
2723 default:
2724 AssertFailedReturnVoid();
2725 }
2726 pThis->enmMode = enmMode;
2727}
2728
2729
2730/**
2731 * Parses the command line options.
2732 *
2733 * @returns Program exit code. Exit on non-success or if *pfExit is set.
2734 * @param pThis The C preprocessor instance.
2735 * @param argc The argument count.
2736 * @param argv The argument vector.
2737 * @param pfExit Pointer to the exit indicator.
2738 */
2739static RTEXITCODE vbcppParseOptions(PVBCPP pThis, int argc, char **argv, bool *pfExit)
2740{
2741 RTEXITCODE rcExit;
2742
2743 *pfExit = false;
2744
2745 /*
2746 * Option config.
2747 */
2748 static RTGETOPTDEF const s_aOpts[] =
2749 {
2750 { "--define", 'D', RTGETOPT_REQ_STRING },
2751 { "--include-dir", 'I', RTGETOPT_REQ_STRING },
2752 { "--undefine", 'U', RTGETOPT_REQ_STRING },
2753 { "--keep-comments", 'C', RTGETOPT_REQ_NOTHING },
2754 { "--strip-comments", 'c', RTGETOPT_REQ_NOTHING },
2755 { "--D-strip", 'd', RTGETOPT_REQ_NOTHING },
2756 };
2757
2758 RTGETOPTUNION ValueUnion;
2759 RTGETOPTSTATE GetOptState;
2760 int rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2761 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
2762
2763 /*
2764 * Process the options.
2765 */
2766 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
2767 {
2768 switch (rc)
2769 {
2770 case 'c':
2771 pThis->fKeepComments = false;
2772 break;
2773
2774 case 'C':
2775 pThis->fKeepComments = false;
2776 break;
2777
2778 case 'd':
2779 vbcppSetMode(pThis, kVBCppMode_SelectiveD);
2780 break;
2781
2782 case 'D':
2783 {
2784 const char *pszEqual = strchr(ValueUnion.psz, '=');
2785 if (pszEqual)
2786 rcExit = vbcppDefineAdd(pThis, ValueUnion.psz, pszEqual - ValueUnion.psz, pszEqual + 1, RTSTR_MAX, true);
2787 else
2788 rcExit = vbcppDefineAdd(pThis, ValueUnion.psz, RTSTR_MAX, "1", 1, true);
2789 if (rcExit != RTEXITCODE_SUCCESS)
2790 return rcExit;
2791 break;
2792 }
2793
2794 case 'I':
2795 rcExit = vbcppAddInclude(pThis, ValueUnion.psz);
2796 if (rcExit != RTEXITCODE_SUCCESS)
2797 return rcExit;
2798 break;
2799
2800 case 'U':
2801 rcExit = vbcppDefineUndef(pThis, ValueUnion.psz, RTSTR_MAX, true);
2802 break;
2803
2804 case 'h':
2805 RTPrintf("No help yet, sorry\n");
2806 *pfExit = true;
2807 return RTEXITCODE_SUCCESS;
2808
2809 case 'V':
2810 {
2811 /* The following is assuming that svn does it's job here. */
2812 static const char s_szRev[] = "$Revision: 41217 $";
2813 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
2814 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
2815 *pfExit = true;
2816 return RTEXITCODE_SUCCESS;
2817 }
2818
2819 case VINF_GETOPT_NOT_OPTION:
2820 if (!pThis->pszInput)
2821 pThis->pszInput = ValueUnion.psz;
2822 else if (!pThis->pszOutput)
2823 pThis->pszOutput = ValueUnion.psz;
2824 else
2825 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "too many file arguments");
2826 break;
2827
2828
2829 /*
2830 * Errors and bugs.
2831 */
2832 default:
2833 return RTGetOptPrintError(rc, &ValueUnion);
2834 }
2835 }
2836
2837 return RTEXITCODE_SUCCESS;
2838}
2839
2840
2841/**
2842 * Terminates the preprocessor.
2843 *
2844 * This may return failure if an error was delayed.
2845 *
2846 * @returns Exit code.
2847 * @param pThis The C preprocessor instance.
2848 */
2849static RTEXITCODE vbcppTerm(PVBCPP pThis)
2850{
2851 /*
2852 * Flush the output first.
2853 */
2854 if (pThis->fStrmOutputValid)
2855 {
2856 if (pThis->pszOutput)
2857 {
2858 int rc = ScmStreamWriteToFile(&pThis->StrmOutput, "%s", pThis->pszOutput);
2859 if (RT_FAILURE(rc))
2860 vbcppError(pThis, "ScmStreamWriteToFile failed with %Rrc when writing '%s'", rc, pThis->pszOutput);
2861 }
2862 else
2863 {
2864 int rc = ScmStreamWriteToStdOut(&pThis->StrmOutput);
2865 if (RT_FAILURE(rc))
2866 vbcppError(pThis, "ScmStreamWriteToStdOut failed with %Rrc", rc);
2867 }
2868 }
2869
2870 /*
2871 * Cleanup.
2872 */
2873 while (pThis->pInputStack)
2874 {
2875 ScmStreamDelete(&pThis->pInputStack->StrmInput);
2876 void *pvFree = pThis->pInputStack;
2877 pThis->pInputStack = pThis->pInputStack->pUp;
2878 RTMemFree(pvFree);
2879 }
2880
2881 ScmStreamDelete(&pThis->StrmOutput);
2882
2883 RTStrSpaceDestroy(&pThis->StrSpace, vbcppFreeDefine, NULL);
2884 pThis->StrSpace = NULL;
2885
2886 uint32_t i = pThis->cIncludes;
2887 while (i-- > 0)
2888 RTStrFree(pThis->papszIncludes[i]);
2889 RTMemFree(pThis->papszIncludes);
2890 pThis->papszIncludes = NULL;
2891
2892 return pThis->rcExit;
2893}
2894
2895
2896/**
2897 * Initializes the C preprocessor instance data.
2898 *
2899 * @param pThis The C preprocessor instance data.
2900 */
2901static void vbcppInit(PVBCPP pThis)
2902{
2903 vbcppSetMode(pThis, kVBCppMode_Selective);
2904 pThis->cIncludes = 0;
2905 pThis->papszIncludes = NULL;
2906 pThis->pszInput = NULL;
2907 pThis->pszOutput = NULL;
2908 pThis->StrSpace = NULL;
2909 pThis->UndefStrSpace = NULL;
2910 pThis->pExpStack = NULL;
2911 pThis->cExpStackDepth = 0;
2912 pThis->cCondStackDepth = 0;
2913 pThis->pCondStack = NULL;
2914 pThis->fIf0Mode = false;
2915 pThis->fJustDroppedLine = false;
2916 pThis->fMaybePreprocessorLine = true;
2917 VBCPP_BITMAP_EMPTY(pThis->bmDefined);
2918 VBCPP_BITMAP_EMPTY(pThis->bmArgs);
2919 pThis->cCondStackDepth = 0;
2920 pThis->pInputStack = NULL;
2921 RT_ZERO(pThis->StrmOutput);
2922 pThis->rcExit = RTEXITCODE_SUCCESS;
2923 pThis->fStrmOutputValid = false;
2924}
2925
2926
2927
2928int main(int argc, char **argv)
2929{
2930 int rc = RTR3InitExe(argc, &argv, 0);
2931 if (RT_FAILURE(rc))
2932 return RTMsgInitFailure(rc);
2933
2934 /*
2935 * Do the job. The code says it all.
2936 */
2937 VBCPP This;
2938 vbcppInit(&This);
2939 bool fExit;
2940 RTEXITCODE rcExit = vbcppParseOptions(&This, argc, argv, &fExit);
2941 if (!fExit && rcExit == RTEXITCODE_SUCCESS)
2942 {
2943 rcExit = vbcppOpenStreams(&This);
2944 if (rcExit == RTEXITCODE_SUCCESS)
2945 rcExit = vbcppPreprocess(&This);
2946 }
2947
2948 if (rcExit == RTEXITCODE_SUCCESS)
2949 rcExit = vbcppTerm(&This);
2950 else
2951 vbcppTerm(&This);
2952 return rcExit;
2953}
2954
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