VirtualBox

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

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

Working on simple function expansion for R3PTRYPE and friends.

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