VBoxCPP.cpp revision 11e7ac503f6eb554fded39b064fa7cdab338adae
/* $Id$ */
/** @file
* VBox Build Tool - A mini C Preprocessor.
*
* Purposes to which this preprocessor will be put:
* structure (as well as substructures) from DTrace without having
* to handcraft it all.
* - Removing \#ifdefs relating to a new feature that has become
* stable and no longer needs \#ifdef'ing.
* - Pretty printing preprocessor directives. This will be used by
* SCM.
*/
/*
* Copyright (C) 2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/initterm.h>
#include "scmstream.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The bitmap type. */
#define VBCPP_BITMAP_TYPE uint64_t
/** The bitmap size as a multiple of VBCPP_BITMAP_TYPE. */
/** Checks if a bit is set. */
/** Sets a bit. */
/** Empties the bitmap. */
/** Joins to bitmaps by OR'ing their values.. */
#define VBCPP_BITMAP_OR(a_bm1, a_bm2) do { (a_bm1)[0] |= (a_bm2)[0]; (a_bm1)[1] |= (a_bm2)[1]; } while (0)
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Variable string buffer (very simple version of SCMSTREAM).
*/
typedef struct VBCPPSTRBUF
{
/** The preprocessor instance (for error reporting). */
/** The length of the string in the buffer. */
/** The string storage. */
char *pszBuf;
/** Allocated buffer space. */
} VBCPPSTRBUF;
/** Pointer to a variable string buffer. */
typedef VBCPPSTRBUF *PVBCPPSTRBUF;
/**
* The preprocessor mode.
*/
typedef enum VBCPPMODE
{
kVBCppMode_Invalid = 0,
} VBCPPMODE;
/**
* A define.
*/
typedef struct VBCPPDEF
{
/** The string space core. */
/** For linking macros that have the fExpanding flag set. */
struct VBCPPDEF *pUpExpanding;
/** Whether it's a function. */
bool fFunction;
/** Variable argument count. */
bool fVarArg;
/** Set if originating on the command line. */
bool fCmdLine;
/** Set if this macro is currently being expanded and should not be
* recursively applied. */
bool fExpanding;
/** The number of known arguments. */
/** Pointer to a list of argument names. */
const char **papszArgs;
/** Lead character bitmap for the argument names. */
/** The value length. */
/** The define value. (This is followed by the name and arguments.) */
char szValue[1];
} VBCPPDEF;
/** Pointer to a define. */
/**
* Macro expansion data.
*/
typedef struct VBCPPMACROEXP
{
/** The expansion buffer. */
char *pszBuf;
/** The length of the string in the expansion buffer. */
/** The current allocated buffer size. */
/** List of expanding macros (Stack). */
/** The input stream (in case we want to look for parameter lists). */
/** Array of argument values. Used when expanding function style macros. */
char **papszArgs;
/** The number of argument values current in papszArgs. */
/** The number of argument values papszArgs can currently hold */
/** Pointer to macro expansion data. */
typedef VBCPPMACROEXP *PVBCPPMACROEXP;
/**
* Evaluation result.
*/
typedef enum VBCPPEVAL
{
kVBCppEval_Invalid = 0,
} VBCPPEVAL;
/**
* The condition kind.
*/
typedef enum VBCPPCONDKIND
{
/** \#if expr */
/** \#ifdef define */
/** \#ifndef define */
/** \#elif expr */
/** The end of valid values. */
/**
* Conditional stack entry.
*/
typedef struct VBCPPCOND
{
/** The next conditional on the stack. */
/** The kind of conditional. This changes on encountering \#elif. */
/** Evaluation result. */
/** The evaluation result of the whole stack. */
/** Whether we've seen the last else. */
bool fSeenElse;
/** The nesting level of this condition. */
/** The nesting level of this condition wrt the ones we keep. */
/** The condition string. (Points within the stream buffer.) */
const char *pchCond;
/** The condition length. */
} VBCPPCOND;
/** Pointer to a conditional stack entry. */
typedef VBCPPCOND *PVBCPPCOND;
/**
* Input buffer stack entry.
*/
typedef struct VBCPPINPUT
{
/** Pointer to the next input on the stack. */
struct VBCPPINPUT *pUp;
/** The input stream. */
/** Pointer into szName to the part which was specified. */
const char *pszSpecified;
/** The input file name with include path. */
char szName[1];
} VBCPPINPUT;
/** Pointer to a input buffer stack entry */
typedef VBCPPINPUT *PVBCPPINPUT;
/**
* The action to take with \#include.
*/
typedef enum VBCPPINCLUDEACTION
{
/**
* C Preprocessor instance data.
*/
typedef struct VBCPP
{
/** @name Options
* @{ */
/** The preprocessing mode. */
/** Whether to keep comments. */
bool fKeepComments;
/** Whether to respect source defines. */
bool fRespectSourceDefines;
/** Whether to let source defines overrides the ones on the command
* line. */
/** Whether to pass thru defines. */
bool fPassThruDefines;
/** Whether to allow undecided conditionals. */
bool fUndecidedConditionals;
/** Whether to pass thru D pragmas. */
bool fPassThruPragmaD;
/** Whether to pass thru STD pragmas. */
bool fPassThruPragmaSTD;
/** Whether to pass thru other pragmas. */
bool fPassThruPragmaOther;
/** Whether to remove dropped lines from the output. */
bool fRemoveDroppedLines;
/** Whether to preforme line splicing.
* @todo implement line splicing */
bool fLineSplicing;
/** What to do about include files. */
/** The number of include directories. */
/** Array of directories to search for include files. */
char **papszIncludes;
/** The name of the input file. */
const char *pszInput;
/** The name of the output file. NULL if stdout. */
const char *pszOutput;
/** @} */
/** The define string space. */
/** The string space holding explicitly undefined macros for selective
* preprocessing runs. */
/** Indicates whether a C-word might need expansion.
* The bitmap is indexed by C-word lead character. Bits that are set
* indicates that the lead character is used in a \#define that we know and
* should expand. */
/** The current depth of the conditional stack. */
/** Conditional stack. */
/** The current condition evaluates to kVBCppEval_False, don't output. */
bool fIf0Mode;
/** Just dropped a line and should maybe drop the current line. */
bool fJustDroppedLine;
/** Whether the current line could be a preprocessor line.
* This is set when EOL is encountered and cleared again when a
* non-comment-or-space character is encountered. See vbcppPreprocess. */
bool fMaybePreprocessorLine;
/** The input stack depth */
/** The input buffer stack. */
/** The output stream. */
/** The status of the whole job, as far as we know. */
/** Whether StrmOutput is valid (for vbcppTerm). */
bool fStrmOutputValid;
} VBCPP;
/** Pointer to the C preprocessor instance data. */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static RTEXITCODE vbcppMacroExpandIt(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offMacro, PVBCPPDEF pMacro, size_t offParameters);
/*
*
*
* Message Handling.
* Message Handling.
* Message Handling.
* Message Handling.
* Message Handling.
*
*
*/
/**
* Displays an error message.
*
* @returns RTEXITCODE_FAILURE
* @param pThis The C preprocessor instance.
* @param pszMsg The message.
* @param ... Message arguments.
*/
{
if (pThis->pInputStack)
{
RTPrintf("%s:%d:%zd: error: %N.\n", pThis->pInputStack->szName, iLine + 1, off - offLine + 1, pszMsg, va);
if (pszLine)
RTPrintf(" %.*s\n"
" %*s^\n",
}
else
{
}
}
/**
* Displays an error message.
*
* @returns RTEXITCODE_FAILURE
* @param pThis The C preprocessor instance.
* @param pszPos Pointer to the offending character.
* @param pszMsg The message.
* @param ... Message arguments.
*/
{
}
/*
*
*
* Variable String Buffers.
* Variable String Buffers.
* Variable String Buffers.
* Variable String Buffers.
* Variable String Buffers.
*
*
*/
{
pStrBuf->cbBufAllocated = 0;
}
{
}
{
return RTEXITCODE_SUCCESS;
if (!pv)
return RTEXITCODE_SUCCESS;
}
{
{
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
}
return RTEXITCODE_SUCCESS;
}
{
{
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
}
return RTEXITCODE_SUCCESS;
}
{
}
{
return '\0';
}
/*
*
*
* C Identifier/Word Parsing.
* C Identifier/Word Parsing.
* C Identifier/Word Parsing.
* C Identifier/Word Parsing.
* C Identifier/Word Parsing.
*
*
*/
/**
* Checks if the given character is a valid C identifier lead character.
*
* @returns true / false.
* @param ch The character to inspect.
*/
{
return RT_C_IS_ALPHA(ch)
|| ch == '_';
}
/**
* Checks if the given character is a valid C identifier character.
*
* @returns true / false.
* @param ch The character to inspect.
*/
{
return RT_C_IS_ALNUM(ch)
|| ch == '_';
}
/**
*
* @returns @c true if valid, @c false if not. Error message already displayed
* on failure.
* @param pThis The C preprocessor instance.
* @param pchIdentifier The start of the identifier to validate.
* @param cchIdentifier The length of the identifier. RTSTR_MAX if not
* known.
*/
{
if (cchIdentifier == RTSTR_MAX)
if (cchIdentifier == 0)
{
return false;
}
{
vbcppErrorPos(pThis, pchIdentifier, "Bad lead chararacter in identifier: '%.*s'", cchIdentifier, pchIdentifier);
return false;
}
{
{
vbcppErrorPos(pThis, pchIdentifier + off, "Illegal chararacter in identifier: '%.*s' (#%zu)", cchIdentifier, pchIdentifier, off + 1);
return false;
}
}
return true;
}
/**
* Checks if the given character is valid C punctuation.
*
* @returns true / false.
* @param ch The character to inspect.
*/
{
switch (ch)
{
case '!':
case '#':
case '%':
case '&':
case '(':
case ')':
case '*':
case '+':
case ',':
case '-':
case '.':
case '/':
case ':':
case ';':
case '<':
case '=':
case '>':
case '?':
case '[':
case ']':
case '^':
case '{':
case '|':
case '}':
case '~':
return true;
default:
return false;
}
}
/**
* Checks if the given string start with valid C punctuation.
*
* @returns 0 if not, otherwise the length of the punctuation.
* @param pch The which start we should evaluate.
* @param cchMax The maximum string length.
*/
{
if (!cchMax)
return 0;
switch (psz[0])
{
case '!':
case '*':
case '/':
case '=':
case '^':
return 2;
return 1;
case '#':
return 2;
return 1;
case '%':
return 2;
{
return 4;
return 2;
}
return 1;
case '&':
return 2;
return 1;
case '(':
case ')':
case ',':
case '?':
case '[':
case ']':
case '{':
case '}':
return 1;
case '+':
return 2;
return 1;
case '-':
return 2;
return 1;
case ':':
return 2;
return 1;
case ';':
return 1;
case '<':
{
return 3;
return 2;
}
return 2;
return 1;
case '.':
return 3;
return 1;
case '>':
{
return 3;
return 2;
}
return 2;
return 1;
case '|':
return 2;
return 1;
case '~':
return 1;
default:
return 0;
}
}
/*
*
*
* Output
* Output
* Output
* Output
* Output
*
*
*/
/**
* Outputs a character.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param ch The character to output.
*/
{
if (RT_SUCCESS(rc))
return RTEXITCODE_SUCCESS;
}
/**
* Outputs a string.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pch The string.
* @param cch The number of characters to write.
*/
{
if (RT_SUCCESS(rc))
return RTEXITCODE_SUCCESS;
}
static RTEXITCODE vbcppOutputComment(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart, size_t cchOutputted,
{
{
/*
* Use the same indent, if possible.
*/
if (cchOutputted < cchIndent)
else
if (RT_FAILURE(rc))
/*
* Copy the bytes.
*/
{
if (ch == ~(unsigned)0)
if (RT_FAILURE(rc))
}
}
return RTEXITCODE_SUCCESS;
}
/*
*
*
* Input
* Input
* Input
* Input
* Input
*
*
*/
/**
* Skips white spaces, including escaped new-lines.
*
* @param pStrmInput The input stream.
*/
{
unsigned chPrev = ~(unsigned)0;
unsigned ch;
{
{
if (chPrev != '\\')
break;
}
else if (RT_C_IS_SPACE(ch))
{
}
else
break;
}
}
/**
* Skips white spaces, escaped new-lines and multi line comments.
*
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
{
unsigned chPrev = ~(unsigned)0;
unsigned ch;
{
if (!RT_C_IS_SPACE(ch))
{
/* Multi-line Comment? */
if (ch != '/')
break; /* most definitely, not. */
{
break; /* no */
}
/* Skip to the end of the comment. */
{
if (ch == '*')
{
if (ch == '/')
break;
if (ch == ~(unsigned)0)
break;
}
}
if (ch == ~(unsigned)0)
chPrev = '/';
}
/* New line (also matched by RT_C_IS_SPACE). */
{
/* Stop if not escaped. */
if (chPrev != '\\')
break;
}
/* Real space char. */
else
{
}
}
return RTEXITCODE_SUCCESS;
}
/**
* Skips white spaces, escaped new-lines, and multi line comments, then checking
* that we're at the end of a line.
*
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
static RTEXITCODE vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
if (rcExit == RTEXITCODE_SUCCESS)
{
if ( ch != ~(unsigned)0
&& ch != '\r'
&& ch != '\n')
}
return rcExit;
}
/**
* Skips white spaces.
*
* @returns The current location upon return.
* @param pStrmInput The input stream.
*/
{
unsigned ch;
{
break;
}
return ScmStreamTell(pStrmInput);
}
/**
* Looks for a left parenthesis in the input stream.
*
* Used during macro expansion. Will ignore comments, newlines and other
* whitespace.
*
* @retval true if found. The stream position at opening parenthesis.
* @retval false if not found. The stream position is unchanged.
*
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
{
if (ch == '(')
return true;
return false;
}
/**
* Skips input until the real end of the current directive line has been
* reached.
*
* This includes multiline comments starting on the same line
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param poffComment Where to note down the position of the final
* comment. Optional.
*/
static RTEXITCODE vbcppInputSkipToEndOfDirectiveLine(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t *poffComment)
{
if (poffComment)
*poffComment = ~(size_t)0;
bool fInComment = false;
unsigned chPrev = 0;
unsigned ch;
{
{
if (chPrev == '\\')
{
continue;
}
if (!fInComment)
break;
/* The expression continues after multi-line comments. Cool. :-) */
}
else if (!fInComment)
{
{
fInComment = true;
if (poffComment)
}
{
if (poffComment)
break; /* done */
}
}
fInComment = false;
/* advance */
}
return rcExit;
}
/**
* Processes a multi-line comment.
*
* Must either string the comment or keep it. If the latter, we must refrain
* from replacing C-words in it.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
{
/* The open comment sequence. */
if ( pThis->fKeepComments
/* The comment.*/
unsigned ch;
while ( rcExit == RTEXITCODE_SUCCESS
{
if (ch == '*')
{
/* Closing sequence? */
if (ch2 == '/')
{
if ( pThis->fKeepComments
break;
}
}
{
if ( ( pThis->fKeepComments
|| !pThis->fRemoveDroppedLines
pThis->fJustDroppedLine = false;
pThis->fMaybePreprocessorLine = true;
}
else if ( pThis->fKeepComments
if (rcExit != RTEXITCODE_SUCCESS)
break;
}
return rcExit;
}
/**
* Processes a single line comment.
*
* Must either string the comment or keep it. If the latter, we must refrain
* from replacing C-words in it.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
{
for (;;)
{
if ( pThis->fKeepComments
|| !pThis->fRemoveDroppedLines
if (rcExit != RTEXITCODE_SUCCESS)
break;
if ( cchLine == 0
break;
if (!pszLine)
break;
}
pThis->fJustDroppedLine = false;
pThis->fMaybePreprocessorLine = true;
return rcExit;
}
/**
* Processes a double quoted string.
*
* Must not replace any C-words in strings.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
{
if (rcExit == RTEXITCODE_SUCCESS)
{
bool fEscaped = false;
for (;;)
{
if (ch == ~(unsigned)0)
{
break;
}
if (rcExit != RTEXITCODE_SUCCESS)
break;
break;
}
}
return rcExit;
}
/**
* Processes a single quoted constant.
*
* Must not replace any C-words in strings.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
{
if (rcExit == RTEXITCODE_SUCCESS)
{
bool fEscaped = false;
for (;;)
{
if (ch == ~(unsigned)0)
{
break;
}
if (rcExit != RTEXITCODE_SUCCESS)
break;
break;
}
}
return rcExit;
}
/**
* Processes a integer or floating point number constant.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param chFirst The first character.
*/
{
unsigned ch;
while ( rcExit == RTEXITCODE_SUCCESS
{
if ( !vbcppIsCIdentifierChar(ch)
&& ch != '.')
break;
}
return rcExit;
}
/**
* Processes a identifier, possibly replacing it with a definition.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param ch The first character.
*/
{
/*
* Does this look like a define we know?
*/
if ( pMacro
{
/*
* Expand it.
*/
ExpCtx.cArgsAlloced = 0;
{
if (rcExit == RTEXITCODE_SUCCESS)
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Insert it into the output stream. Make sure there is a
* whitespace following it.
*/
if (RT_SUCCESS(rc))
{
}
else
}
}
else
}
else
{
/*
* Not a macro or a function-macro name match but no invocation, just
* output the text unchanged.
*/
if (RT_SUCCESS(rc))
else
}
return rcExit;
}
/*
*
*
* D E F I N E S / M A C R O S
* D E F I N E S / M A C R O S
* D E F I N E S / M A C R O S
* D E F I N E S / M A C R O S
* D E F I N E S / M A C R O S
*
*
*/
/**
* Checks if a define exists.
*
* @returns true or false.
* @param pThis The C preprocessor instance.
* @param pszDefine The define name and optionally the argument
* list.
* @param cchDefine The length of the name. RTSTR_MAX is ok.
*/
{
return cchDefine > 0
}
/**
* Looks up a define.
*
* @returns Pointer to the define if found, NULL if not.
* @param pThis The C preprocessor instance.
* @param pszDefine The define name and optionally the argument
* list.
* @param cchDefine The length of the name. RTSTR_MAX is ok.
*/
{
if (!cchDefine)
return NULL;
return NULL;
}
{
return i;
return UINT32_MAX;
}
static RTEXITCODE vbcppMacroExpandReplace(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t off, size_t cchToReplace,
{
/*
* Figure how much space we actually need.
* (Hope this whitespace stuff is correct...)
*/
bool const fLeadingSpace = off > 0
/*
* Adjust the buffer size and contents.
*/
if (cchActualReplacement > cchToReplace)
{
/* Ensure enough buffer space. */
{
cbNew *= 2;
if (!pvNew)
}
/* Push the chars following the replacement area down to make room. */
}
else if (cchActualReplacement < cchToReplace)
{
/* Pull the chars following the replacement area up. */
}
/*
* Insert the replacement string.
*/
if (fLeadingSpace)
*pszCur++ = ' ';
if (fTrailingSpace)
*pszCur++ = ' ';
return RTEXITCODE_SUCCESS;
}
{
}
{
}
static RTEXITCODE vbcppMacroExpandSkipEolEx(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff, unsigned chFirst)
{
if (chFirst == '\r')
{
if (ch2 == '\n')
{
}
}
return RTEXITCODE_SUCCESS;
}
{
}
{
unsigned chPrev = 0;
{
{
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
if (chPrev != '\\')
break;
}
}
return RTEXITCODE_SUCCESS;
}
{
unsigned chPrev2 = 0;
unsigned chPrev = 0;
{
break;
{
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
if (chPrev == '\\')
{
continue;
}
}
}
return RTEXITCODE_SUCCESS;
}
static RTEXITCODE vbcppMacroExpandGrowArgArray(PVBCPP pThis, PVBCPPMACROEXP pExp, uint32_t cMinArgs)
{
{
if (!pv)
}
return RTEXITCODE_SUCCESS;
}
{
if (rcExit == RTEXITCODE_SUCCESS)
{
if (pszArg)
else
}
return rcExit;
}
static RTEXITCODE vbcppMacroExpandGatherParameters(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff, uint32_t cArgsHint)
{
/*
* Free previous argument values.
*/
{
}
/*
* The current character should be an opening parenthsis.
*/
if (ch != '(')
/*
* Parse the argument list.
*/
char chQuote = 0;
size_t cbArgAlloc = 0;
unsigned chPrev = 0;
{
{
cParentheses--;
/* The end? */
if (!cParentheses)
{
if (cchArg)
break;
}
}
cParentheses++;
{
/* End of one argument, start of the next. */
if (cchArg)
else
{
if (rcExit != RTEXITCODE_SUCCESS)
break;
}
cbArgAlloc = 0;
cchArg = 0;
continue;
}
{
/* Comment? */
/** @todo This ain't right wrt line splicing. */
{
if (ch2 == '/')
else
if (rcExit != RTEXITCODE_SUCCESS)
break;
continue;
}
}
else if (ch == '"')
{
if (!chQuote)
chQuote = '"';
else if (chPrev != '\\')
chQuote = 0;
}
else if (ch == '\'')
{
if (!chQuote)
chQuote = '\'';
else if (chPrev != '\\')
chQuote = 0;
}
else if (ch == '\\')
{
/* Splice lines? */
{
if (rcExit != RTEXITCODE_SUCCESS)
break;
continue;
}
}
continue; /* ignore spaces leading up to an argument value */
/* Append the character to the argument value, adding the argument
to the output array if it's first character in it. */
{
/* Add argument to the vector. */
if (!cchArg)
{
if (rcExit != RTEXITCODE_SUCCESS)
break;
}
/* Resize the argument value buffer. */
if (!pszArg)
{
break;
}
}
}
/*
* Check that we're leaving on good terms.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
if (cParentheses)
}
return rcExit;
}
/**
* Expands the arguments referenced in the macro value.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pExp The expansion context.
* @param pMacro The macro. Must be a function macro.
* @param pStrBuf String buffer containing the result. The caller
* should initialize and destroy this!
*/
static RTEXITCODE vbcppMacroExpandValueWithArguments(PVBCPP pThis, PVBCPPMACROEXP pExp, PVBCPPDEF pMacro,
{
/*
* Empty?
*/
return RTEXITCODE_SUCCESS;
/*
* Parse the value.
*/
const char *pszSrcSeq;
char ch;
{
if (ch == '#')
{
if (*pszSrc == '#')
{
/* Concatenate operator. */
}
else
{
/* Stringify macro argument. */
}
return rcExit;
}
else if (ch == '"')
{
/* String litteral. */
{
if (ch == '\\')
if (ch == '\0')
{
break;
}
}
}
else if (ch == '\'')
{
/* Character constant. */
{
if (ch == '\\')
if (ch == '\0')
{
break;
}
}
}
else if (RT_C_IS_DIGIT(ch))
{
/* Process numerical constants correctly (i.e. don't mess with the suffix). */
&& ( vbcppIsCIdentifierChar(ch)
|| ch == '.') )
pszSrc++;
}
else if (RT_C_IS_SPACE(ch))
{
/* join spaces */
continue;
}
else if (vbcppIsCIdentifierLeadChar(ch))
{
/* Something we should replace? */
&& vbcppIsCIdentifierChar(ch))
pszSrc++;
{
/** @todo check out spaces here! */
{
}
else
{
/* __VA_ARGS__ */
{
for (;;)
{
if (rcExit != RTEXITCODE_SUCCESS)
break;
iArg++;
break;
if (rcExit != RTEXITCODE_SUCCESS)
break;
}
}
if (rcExit == RTEXITCODE_SUCCESS)
}
}
/* Not an argument needing replacing. */
else
}
else
{
}
}
return rcExit;
}
/**
* Expands the given macro.
*
* Caller already checked if a function macro should be expanded, i.e. whether
* there is a parameter list.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pExp The expansion context.
* @param offMacro Offset into the expansion buffer of the macro
* invocation.
* @param pMacro The macro.
* @param offParameters The start of the parameter list if applicable.
* If the parameter list starts at the current
* stream position shall be at the end of the
* expansion buffer.
*/
static RTEXITCODE vbcppMacroExpandIt(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offMacro, PVBCPPDEF pMacro,
{
/*
* Function macros are kind of difficult...
*/
{
if (rcExit == RTEXITCODE_SUCCESS)
{
}
if (rcExit == RTEXITCODE_SUCCESS)
{
if (rcExit == RTEXITCODE_SUCCESS)
rcExit = vbcppMacroExpandReplace(pThis, pExp, offMacro, offEnd - offMacro, ValueBuf.pszBuf, ValueBuf.cchBuf);
}
}
/*
* Object-like macros are easy. :-)
*/
else
rcExit = vbcppMacroExpandReplace(pThis, pExp, offMacro, pMacro->Core.cchString, pMacro->szValue, pMacro->cchValue);
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Push the macro onto the stack.
*/
pMacro->fExpanding = true;
}
return rcExit;
}
/**
* Re-scan the expanded macro.
*
* @returns
* @param pThis .
* @param pExp .
*/
{
return rcExit;
}
/**
* Cleans up the expansion context.
*
* This involves clearing VBCPPMACRO::fExpanding and VBCPPMACRO::pUpExpanding,
* and freeing the memory resources associated with the expansion context.
*
* @param pExp The expansion context.
*/
{
while (pExp->pMacroStack)
{
pMacro->fExpanding = false;
}
{
}
}
/**
* Frees a define.
*
* @returns VINF_SUCCESS (used when called by RTStrSpaceDestroy)
* @param pStr Pointer to the VBCPPDEF::Core member.
* @param pvUser Unused.
*/
{
return VINF_SUCCESS;
}
/**
* Removes a define.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pszDefine The define name, no argument list or anything.
* @param cchDefine The length of the name. RTSTR_MAX is ok.
* @param fExplicitUndef Explicit undefinition, that is, in a selective
* preprocessing run it will evaluate to undefined.
*/
static RTEXITCODE vbcppMacroUndef(PVBCPP pThis, const char *pszDefine, size_t cchDefine, bool fExplicitUndef)
{
if (pHit)
{
}
if (fExplicitUndef)
{
if (!pStr)
}
return RTEXITCODE_SUCCESS;
}
/**
* Inserts a define (rejecting and freeing it in some case).
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pMacro The define to insert.
*/
{
/*
* Reject illegal macro names.
*/
{
return rcExit;
}
/*
* Ignore in source-file defines when doing selective preprocessing.
*/
if ( !pThis->fRespectSourceDefines
{
/* Ignore*/
return RTEXITCODE_SUCCESS;
}
/*
* Insert it and update the lead character hint bitmap.
*/
else
{
/*
* Duplicate. When doing selective D preprocessing, let the command
* line take precendece.
*/
{
}
else
{
}
}
return RTEXITCODE_SUCCESS;
}
/**
* Adds a define.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pszDefine The define name, no parameter list.
* @param cchDefine The length of the name.
* @param pszParams The parameter list.
* @param cchParams The length of the parameter list.
* @param pszValue The value.
* @param cchDefine The length of the value.
* @param fCmdLine Set if originating on the command line.
*/
bool fCmdLine)
{
/*
* Determin the number of arguments and how much space their names
* requires. Performing syntax validation while parsing.
*/
uint32_t cchArgNames = 0;
{
/* Skip blanks and maybe one comma. */
bool fIgnoreComma = cArgs != 0;
{
{
{
break;
/** @todo variadic macros. */
}
fIgnoreComma = false;
}
off++;
}
break;
/* Found and argument. First character is already validated. */
cArgs++;
cchArgNames += 2;
off++;
off++, cchArgNames++;
}
/*
* Allocate a structure.
*/
+ sizeof(const char *) * cArgs;
if (!pMacro)
*pszDst++ = '\0';
/*
* Set up the arguments.
*/
{
/* Skip blanks and maybe one comma. */
bool fIgnoreComma = cArgs != 0;
{
{
break;
fIgnoreComma = false;
}
off++;
}
break;
/* Found and argument. First character is already validated. */
do
{
*pszDst++ = '\0';
iArg++;
}
}
/**
* Adds a define.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pszDefine The define name and optionally the argument
* list.
* @param cchDefine The length of the name. RTSTR_MAX is ok.
* @param pszValue The value.
* @param cchDefine The length of the value. RTSTR_MAX is ok.
* @param fCmdLine Set if originating on the command line.
*/
{
/*
* We need the lengths. Trim the input.
*/
cchDefine--;
if (!cchDefine)
cchValue--;
/*
* Arguments make the job a bit more annoying. Handle that elsewhere
*/
if (pszParams)
{
return RTEXITCODE_FAILURE;
pszParams++;
cchParams -= 2;
return vbcppMacroAddFn(pThis, pszDefine, cchDefine, pszParams, cchParams, pszValue, cchValue, fCmdLine);
}
/*
* Simple define, no arguments.
*/
return RTEXITCODE_FAILURE;
PVBCPPDEF pMacro = (PVBCPPDEF)RTMemAlloc(RT_OFFSETOF(VBCPPDEF, szValue[cchValue + 1 + cchDefine + 1]));
if (!pMacro)
}
/**
* Tries to convert a define into an inline D constant.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pMacro The macro.
*/
{
return RTEXITCODE_SUCCESS;
/*
* Do some simple macro resolving. (Mostly to make x86.h work.)
*/
unsigned i = 0;
while ( i < 10
&& cchValue > 0
{
i++;
}
return RTEXITCODE_SUCCESS;
/*
* A lone value?
*/
char *pszNext;
if (RT_SUCCESS(rc))
{
if ( rc == VWRN_TRAILING_SPACES
|| rc == VWRN_NEGATIVE_UNSIGNED
|| rc == VWRN_NUMBER_TOO_BIG)
return RTEXITCODE_SUCCESS;
const char *pszType;
if (rc == VWRN_TRAILING_CHARS)
{
pszType = "uint32_t";
pszType = "uintptr_t";
pszType = "uint64_t";
else
}
pszType = "uint8_t";
else if (u64 <= UINT16_MAX)
pszType = "uint16_t";
else if (u64 <= UINT32_MAX)
pszType = "uint32_t";
else
pszType = "uint64_t";
if (!pszType)
return RTEXITCODE_SUCCESS;
}
/*
* A value wrapped in a constant macro?
*/
{
cchInnerValue--, pchInnerValue++;
return RTEXITCODE_SUCCESS;
if ( RT_FAILURE(rc)
|| rc == VWRN_TRAILING_SPACES
|| rc == VWRN_NEGATIVE_UNSIGNED
|| rc == VWRN_NUMBER_TOO_BIG)
return RTEXITCODE_SUCCESS;
const char *pszType;
#define MY_MATCH_STR(a_sz) (sizeof(a_sz) - 1 == cchPrefix && !strncmp(pszValue, a_sz, sizeof(a_sz) - 1))
if (MY_MATCH_STR("UINT8_C"))
pszType = "uint8_t";
else if (MY_MATCH_STR("UINT16_C"))
pszType = "uint16_t";
else if (MY_MATCH_STR("UINT32_C"))
pszType = "uint32_t";
else if (MY_MATCH_STR("UINT64_C"))
pszType = "uint64_t";
else
if (pszType)
else if (MY_MATCH_STR("RT_BIT_64"))
else
return RTEXITCODE_SUCCESS;
}
/* Dunno what this is... */
else
return RTEXITCODE_SUCCESS;
/*
* Check for output error and clear the output suppression indicator.
*/
if (cch < 0)
pThis->fJustDroppedLine = false;
return RTEXITCODE_SUCCESS;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
{
/*
* Parse it.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
if (pchDefine)
{
/* If it's a function style define, parse out the parameter list. */
if (ch == '(')
{
{
{
if (chPrev != '\\')
{
break;
}
}
if (ch == ')')
{
break;
}
}
}
/* The simple kind. */
/* Parse out the value. */
if (rcExit == RTEXITCODE_SUCCESS)
if (rcExit == RTEXITCODE_SUCCESS)
{
{
{
if (chPrev != '\\')
break;
}
}
/*
* Execute.
*/
if (pchParams)
rcExit = vbcppMacroAddFn(pThis, pchDefine, cchDefine, pchParams, cchParams, pchValue, cchValue, false);
else
/*
* Pass thru?
*/
if ( rcExit == RTEXITCODE_SUCCESS
&& pThis->fPassThruDefines)
{
if (pchParams)
else
if (cch > 0)
else
}
else if ( rcExit == RTEXITCODE_SUCCESS
else
pThis->fJustDroppedLine = true;
}
}
}
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
{
/*
* Parse it.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
if (pchDefine)
{
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Take action.
*/
if ( pMacro
|| pThis->fAllowRedefiningCmdLineDefines ) )
{
}
/*
* Pass thru.
*/
if ( rcExit == RTEXITCODE_SUCCESS
&& pThis->fPassThruDefines)
{
if (cch > 0)
else
}
}
}
else
}
return rcExit;
}
/*
*
*
* C O N D I T I O N A L S
* C O N D I T I O N A L S
* C O N D I T I O N A L S
* C O N D I T I O N A L S
* C O N D I T I O N A L S
*
*
*/
/**
* Combines current stack result with the one being pushed.
*
* @returns Combined result.
* @param enmEvalPush The result of the condition being pushed.
* @param enmEvalStack The current stack result.
*/
{
if (enmEvalStack == kVBCppEval_False)
return kVBCppEval_False;
return enmEvalPush;
}
/**
* Pushes an conditional onto the stack.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The current input stream.
* @param offStart Not currently used, using @a pchCondition and
* @a cchCondition instead.
* @param enmKind The kind of conditional.
* @param enmResult The result of the evaluation.
* @param pchCondition The raw condition.
* @param cchCondition The length of @a pchCondition.
*/
{
/*
* Allocate a new entry and push it.
*/
if (!pCond)
/*
* Do pass thru.
*/
&& enmResult == kVBCppEval_Undecided)
{
/** @todo this is stripping comments of \#ifdef and \#ifndef atm. */
const char *pszDirective;
switch (enmKind)
{
default: AssertFailedReturn(RTEXITCODE_FAILURE);
}
if (cch < 0)
}
else
pThis->fJustDroppedLine = true;
return RTEXITCODE_SUCCESS;
}
#if 0
typedef enum VBCPPEXPRKIND
{
typedef struct VBCPPEXPR *PVBCPPEXPR;
/**
* Expression parsing structure.
*/
typedef struct VBCPPEXPR
{
/** Pointer to the first byte of the expression. */
const char *pchExpr;
/** The length of the expression. */
/** The kind of expression. */
/** */
union
{
struct
{
} Unary;
struct
{
} Binary;
struct
{
unsigned cBits;
} SignedValue;
struct
{
unsigned cBits;
struct
{
const char *pch;
} Define;
} u;
/** Parent expression. */
} VBCPPEXPR;
typedef struct VBCPPEXPRPARSER
{
/**
* Operator return statuses.
*/
typedef enum
{
kExprRet_Error = -1,
kExprRet_Ok = 0,
} VBCPPEXPRRET;
{
}
#endif
/**
* Evalutes the expression.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pszExpr The expression.
* @param cchExpr The length of the expression.
* @param penmResult Where to store the result.
*/
{
#if 0
/** @todo */
#else /* Greatly simplified for getting DTrace working. */
)
)
else if (pThis->fUndecidedConditionals)
)
{
if (pMacro)
{
else
}
else
}
{
else
}
)
{
else
}
else if ( !strcmp(pszExpr, "defined(VBOX_WITH_HYBRID_32BIT_KERNEL) && ( 32 != 32 || R0_ARCH_BITS != 32)")
|| !strcmp(pszExpr, "defined(VBOX_WITH_HYBRID_32BIT_KERNEL) && ( 64 != 32 || R0_ARCH_BITS != 32)") )
{
if ( pMacro1
else
}
else
#endif
return rcExit;
}
/**
* Expands known macros in the expression.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pcReplacements Where to store the number of replacements made.
* Optional.
*/
static RTEXITCODE vbcppExprExpand(PVBCPP pThis, char **ppszExpr, size_t *pcchExpr, size_t *pcReplacements)
{
char ch;
{
if (!vbcppIsCIdentifierLeadChar(ch))
off++;
else
{
/* Extract the identifier. */
off++;
/* Does it exist? Will save a whole lot of trouble if it doesn't. */
if (pMacro)
{
/* Skip white space and check for parenthesis. */
off++;
{
/* Try expand function define. */
break;
}
else
{
/* Expand simple define if found. */
{
}
{
{
do
{
cbExprAlloc *= 2;
if (!pv)
{
break;
}
}
}
/* Insert with spaces around it. Not entirely sure how
standard compliant this is... */
/* Restart parsing at the inserted macro. */
}
}
}
}
return rcExit;
}
/**
* Extracts the expression.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param ppszExpr Where to return the expression string..
* @param pcchExpr Where to return the expression length.
* Optional.
* @param poffComment Where to note down the position of the final
* comment. Optional.
*/
{
size_t cbExprAlloc = 0;
bool fInComment = false;
unsigned chPrev = 0;
unsigned ch;
{
{
if (chPrev == '\\')
{
continue;
}
if (!fInComment)
break;
/* The expression continues after multi-line comments. Cool. :-) */
}
else if (!fInComment)
{
{
fInComment = true;
}
{
break; /* done */
}
/* Append the char to the expression. */
else
{
{
if (!pv)
{
break;
}
}
}
}
fInComment = false;
/* advance */
}
if (rcExit == RTEXITCODE_SUCCESS)
{
if (pcchExpr)
if (poffComment)
}
else
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
* @param enmKind The kind of directive we're processing.
*/
{
/*
* Check for missing #if if #elif.
*/
if ( enmKind == kVBCppCondKind_ElIf
&& !pThis->pCondStack )
/*
* Extract and expand the expression string.
*/
char *pszExpr;
if (rcExit == RTEXITCODE_SUCCESS)
{
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Strip it and check that it's not empty.
*/
if (cchExpr)
{
/*
* Now, evalute the expression.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Take action.
*/
if (enmKind != kVBCppCondKind_ElIf)
else
{
{
if (enmResult == kVBCppEval_True)
else
}
/*
* Do #elif pass thru.
*/
{
if (cch > 0)
else
}
else
pThis->fJustDroppedLine = true;
}
}
}
else
}
}
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
{
/*
* Parse it.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
if (pchDefine)
{
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Evaluate it.
*/
else if ( !pThis->fUndecidedConditionals
else
}
}
else
}
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
{
/*
* Parse it.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
if (pchDefine)
{
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Evaluate it.
*/
else if ( !pThis->fUndecidedConditionals
else
}
}
else
}
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
{
/*
* Nothing to parse, just comment positions to find and note down.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Execute.
*/
if (pCond)
{
{
{
else
}
/*
* Do pass thru.
*/
{
if (cch > 0)
else
}
else
pThis->fJustDroppedLine = true;
}
else
}
else
}
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
{
/*
* Nothing to parse, just comment positions to find and note down.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Execute.
*/
if (pCond)
{
/*
* Do pass thru.
*/
{
if (cch > 0)
else
}
else
pThis->fJustDroppedLine = true;
}
else
}
return rcExit;
}
/*
*
*
* Misc Directives
* Misc Directives
* Misc Directives
* Misc Directives
*
*
*/
/**
* Adds an include directory.
*
* @returns Program exit code, with error message on failure.
* @param pThis The C preprocessor instance.
* @param pszDir The directory to add.
*/
{
if (!pv)
if (RT_FAILURE(rc))
return RTEXITCODE_SUCCESS;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
{
/*
* Parse it.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
size_t cchFileSpec = 0;
const char *pchFileSpec = NULL;
size_t cchFilename = 0;
const char *pchFilename = NULL;
{
{
{
break;
}
}
if (rcExit == RTEXITCODE_SUCCESS)
{
if (ch != ~(unsigned)0)
else
}
}
else if (vbcppIsCIdentifierLeadChar(ch))
{
//pchFileSpec = ScmStreamCGetWord(pStrmInput, &cchFileSpec);
}
else
/*
* Take down the location of the next non-white space, in case we need
* to pass thru the directive further down. Then skip to the end of the
* line.
*/
if (rcExit == RTEXITCODE_SUCCESS)
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Execute it.
*/
{
/** @todo Search for the include file and push it onto the input stack.
* Not difficult, just unnecessary rigth now. */
}
{
/* Pretty print the passthru. */
if (chType == '<')
else if (chType == '"')
else
if (cch > 0)
else
}
else
{
pThis->fJustDroppedLine = true;
}
}
}
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
{
/*
* Parse out the first word.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
if (pchPragma)
{
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* What to do about this
*/
bool fPassThru = false;
if ( cchPragma == 1
&& *pchPragma == 'D')
else if ( cchPragma == 3
else
if (fPassThru)
{
if (cch > 0)
else
}
else
pThis->fJustDroppedLine = true;
}
}
else
}
return rcExit;
}
/**
* Processes an error directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
{
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
{
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
{
}
/**
* Handles a preprocessor directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
{
/*
* Get the directive and do a string switch on it.
*/
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
if (pchDirective)
{
#define IS_DIRECTIVE(a_sz) ( sizeof(a_sz) - 1 == cchDirective && strncmp(pchDirective, a_sz, sizeof(a_sz) - 1) == 0)
if (IS_DIRECTIVE("if"))
else if (IS_DIRECTIVE("elif"))
else if (IS_DIRECTIVE("ifdef"))
else if (IS_DIRECTIVE("ifndef"))
else if (IS_DIRECTIVE("else"))
else if (IS_DIRECTIVE("endif"))
{
if (IS_DIRECTIVE("include"))
else if (IS_DIRECTIVE("define"))
else if (IS_DIRECTIVE("undef"))
else if (IS_DIRECTIVE("pragma"))
else if (IS_DIRECTIVE("error"))
else if (IS_DIRECTIVE("line"))
else
}
}
{
/* Could it be a # <num> "file" directive? */
if (RT_C_IS_DIGIT(ch))
else
}
return rcExit;
}
/*
*
*
* M a i n b o d y.
* M a i n b o d y.
* M a i n b o d y.
* M a i n b o d y.
* M a i n b o d y.
*
*
*/
/**
* Does the actually preprocessing of the input file.
*
* @returns Exit code.
* @param pThis The C preprocessor instance.
*/
{
/*
* Parse.
*/
while (pThis->pInputStack)
{
pThis->fMaybePreprocessorLine = true;
unsigned ch;
{
if (ch == '/')
{
if (ch == '*')
else if (ch == '/')
else
{
pThis->fMaybePreprocessorLine = false;
}
}
{
}
{
&& !pThis->fJustDroppedLine)
|| !pThis->fRemoveDroppedLines
pThis->fJustDroppedLine = false;
pThis->fMaybePreprocessorLine = true;
}
else if (RT_C_IS_SPACE(ch))
{
}
else
{
pThis->fMaybePreprocessorLine = false;
{
if (ch == '"')
else if (ch == '\'')
else if (vbcppIsCIdentifierLeadChar(ch))
else if (RT_C_IS_DIGIT(ch))
else
}
}
if (rcExit != RTEXITCODE_SUCCESS)
break;
}
/*
* Check for errors.
*/
if (rcExit != RTEXITCODE_SUCCESS)
break;
/*
* Pop the input stack.
*/
}
return rcExit;
}
/**
* Opens the input and output streams.
*
* @returns Exit code.
* @param pThis The C preprocessor instance.
*/
{
if (!pInput)
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
pThis->fStrmOutputValid = true;
return RTEXITCODE_SUCCESS;
}
/**
* Changes the preprocessing mode.
*
* @param pThis The C preprocessor instance.
* @param enmMode The new mode.
*/
{
switch (enmMode)
{
case kVBCppMode_Standard:
pThis->fKeepComments = false;
pThis->fRespectSourceDefines = true;
pThis->fAllowRedefiningCmdLineDefines = true;
pThis->fPassThruDefines = false;
pThis->fUndecidedConditionals = false;
pThis->fPassThruPragmaD = false;
pThis->fPassThruPragmaSTD = true;
pThis->fPassThruPragmaOther = true;
pThis->fRemoveDroppedLines = false;
pThis->fLineSplicing = true;
break;
case kVBCppMode_Selective:
pThis->fKeepComments = true;
pThis->fRespectSourceDefines = false;
pThis->fAllowRedefiningCmdLineDefines = false;
pThis->fPassThruDefines = true;
pThis->fUndecidedConditionals = true;
pThis->fPassThruPragmaD = true;
pThis->fPassThruPragmaSTD = true;
pThis->fPassThruPragmaOther = true;
pThis->fRemoveDroppedLines = true;
pThis->fLineSplicing = false;
break;
case kVBCppMode_SelectiveD:
pThis->fKeepComments = true;
pThis->fRespectSourceDefines = true;
pThis->fAllowRedefiningCmdLineDefines = false;
pThis->fPassThruDefines = false;
pThis->fUndecidedConditionals = false;
pThis->fPassThruPragmaD = true;
pThis->fPassThruPragmaSTD = false;
pThis->fPassThruPragmaOther = false;
pThis->fRemoveDroppedLines = true;
pThis->fLineSplicing = false;
break;
default:
}
}
/**
* Parses the command line options.
*
* @returns Program exit code. Exit on non-success or if *pfExit is set.
* @param pThis The C preprocessor instance.
* @param argc The argument count.
* @param argv The argument vector.
* @param pfExit Pointer to the exit indicator.
*/
{
*pfExit = false;
/*
* Option config.
*/
static RTGETOPTDEF const s_aOpts[] =
{
};
int rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
/*
* Process the options.
*/
{
switch (rc)
{
case 'c':
pThis->fKeepComments = false;
break;
case 'C':
pThis->fKeepComments = false;
break;
case 'd':
break;
case 'D':
{
if (pszEqual)
rcExit = vbcppMacroAdd(pThis, ValueUnion.psz, pszEqual - ValueUnion.psz, pszEqual + 1, RTSTR_MAX, true);
else
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
break;
}
case 'I':
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
break;
case 'U':
break;
case 'h':
RTPrintf("No help yet, sorry\n");
*pfExit = true;
return RTEXITCODE_SUCCESS;
case 'V':
{
/* The following is assuming that svn does it's job here. */
static const char s_szRev[] = "$Revision$";
*pfExit = true;
return RTEXITCODE_SUCCESS;
}
case VINF_GETOPT_NOT_OPTION:
else
break;
/*
* Errors and bugs.
*/
default:
}
}
return RTEXITCODE_SUCCESS;
}
/**
* Terminates the preprocessor.
*
* This may return failure if an error was delayed.
*
* @returns Exit code.
* @param pThis The C preprocessor instance.
*/
{
/*
* Flush the output first.
*/
if (pThis->fStrmOutputValid)
{
{
if (RT_FAILURE(rc))
}
else
{
if (RT_FAILURE(rc))
}
}
/*
* Cleanup.
*/
while (pThis->pInputStack)
{
}
while (i-- > 0)
}
/**
* Initializes the C preprocessor instance data.
*
* @param pThis The C preprocessor instance data.
*/
{
pThis->cCondStackDepth = 0;
pThis->fJustDroppedLine = false;
pThis->fMaybePreprocessorLine = true;
pThis->cCondStackDepth = 0;
pThis->fStrmOutputValid = false;
}
{
if (RT_FAILURE(rc))
return RTMsgInitFailure(rc);
/*
* Do the job. The code says it all.
*/
bool fExit;
{
if (rcExit == RTEXITCODE_SUCCESS)
}
if (rcExit == RTEXITCODE_SUCCESS)
else
return rcExit;
}