text: /* $Id: VBoxCPP.cpp 73097 2018-07-12 21:06:33Z vboxsync $ */
  /** @file
   * VBox Build Tool - A mini C Preprocessor.
   *
   * Purposes to which this preprocessor will be put:
   *      - Preprocessig vm.h into dtrace/lib/vm.d so we can access the VM
   *        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-2017 Oracle Corporation
   *
   * This file is part of VirtualBox Open Source Edition (OSE), as
   * available from http://www.virtualbox.org. This file is free software;
   * you can redistribute it and/or modify it under the terms of the GNU
   * 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 <VBox/VBoxTpG.h>
  
  #include <iprt/alloca.h>
  #include <iprt/assert.h>
  #include <iprt/asm.h>
  #include <iprt/ctype.h>
  #include <iprt/err.h>
  #include <iprt/file.h>
  #include <iprt/getopt.h>
  #include <iprt/initterm.h>
  #include <iprt/list.h>
  #include <iprt/mem.h>
  #include <iprt/message.h>
  #include <iprt/path.h>
  #include <iprt/stream.h>
  #include <iprt/string.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. */
  #define VBCPP_BITMAP_SIZE                   (128 / 64)
  /** Checks if a bit is set. */
  #define VBCPP_BITMAP_IS_SET(a_bm, a_ch)     ASMBitTest(a_bm, (a_ch) & 0x7f)
  /** Sets a bit. */
  #define VBCPP_BITMAP_SET(a_bm, a_ch)        ASMBitSet(a_bm, (a_ch) & 0x7f)
  /** Empties the bitmap. */
  #define VBCPP_BITMAP_EMPTY(a_bm)            do { (a_bm)[0] = 0; (a_bm)[1] = 0; } while (0)
  /** 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                                                                                                      *
  *********************************************************************************************************************************/
  /** Pointer to the C preprocessor instance data. */
  typedef struct VBCPP *PVBCPP;
  
  
  /**
   * Variable string buffer (very simple version of SCMSTREAM).
   */
  typedef struct VBCPPSTRBUF
  {
      /** The preprocessor instance (for error reporting). */
      struct VBCPP   *pThis;
      /** The length of the string in the buffer. */
      size_t          cchBuf;
      /** The string storage. */
      char           *pszBuf;
      /** Allocated buffer space. */
      size_t          cbBufAllocated;
  } VBCPPSTRBUF;
  /** Pointer to a variable string buffer. */
  typedef VBCPPSTRBUF *PVBCPPSTRBUF;
  
  
  /**
   * The preprocessor mode.
   */
  typedef enum VBCPPMODE
  {
      kVBCppMode_Invalid = 0,
      kVBCppMode_Standard,
      kVBCppMode_Selective,
      kVBCppMode_SelectiveD,
      kVBCppMode_End
  } VBCPPMODE;
  
  
  /**
   * A macro (aka define).
   */
  typedef struct VBCPPMACRO
  {
      /** The string space core. */
      RTSTRSPACECORE      Core;
  #if 0
      /** For linking macros that have the fExpanding flag set. */
      struct VBCPPMACRO  *pUpExpanding;
  #endif
      /** 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. */
      uint32_t            cArgs;
      /** Pointer to a list of argument names. */
      const char        **papszArgs;
      /** Lead character bitmap for the argument names. */
      VBCPP_BITMAP_TYPE   bmArgs[VBCPP_BITMAP_SIZE];
      /** The value length. */
      size_t              cchValue;
      /** The define value.  (This is followed by the name and arguments.) */
      char                szValue[1];
  } VBCPPMACRO;
  /** Pointer to a macro. */
  typedef VBCPPMACRO *PVBCPPMACRO;
  
  
  /**
   * Macro expansion data.
   */
  typedef struct VBCPPMACROEXP
  {
      /** The expansion buffer. */
      VBCPPSTRBUF     StrBuf;
  #if 0
      /** List of expanding macros (Stack). */
      PVBCPPMACRO     pMacroStack;
  #endif
      /** The input stream (in case we want to look for parameter lists). */
      PSCMSTREAM      pStrmInput;
      /** Array of argument values.  Used when expanding function style macros.  */
      char          **papszArgs;
      /** The number of argument values current in papszArgs. */
      uint32_t        cArgs;
      /** The number of argument values papszArgs can currently hold  */
      uint32_t        cArgsAlloced;
  } VBCPPMACROEXP;
  /** Pointer to macro expansion data. */
  typedef VBCPPMACROEXP *PVBCPPMACROEXP;
  
  
  /**
   * The vbcppMacroExpandReScan mode of operation.
   */
  typedef enum VBCPPMACRORESCANMODE
  {
      /** Invalid mode.  */
      kMacroReScanMode_Invalid = 0,
      /** Normal expansion mode. */
      kMacroReScanMode_Normal,
      /** Replaces known macros and heeds the 'defined' operator. */
      kMacroReScanMode_Expression,
      /** End of valid modes. */
      kMacroReScanMode_End
  } VBCPPMACRORESCANMODE;
  
  
  /**
   * Expression node type.
   */
  typedef enum VBCPPEXPRKIND
  {
      kVBCppExprKind_Invalid = 0,
      kVBCppExprKind_Unary,
      kVBCppExprKind_Binary,
      kVBCppExprKind_Ternary,
      kVBCppExprKind_SignedValue,
      kVBCppExprKind_UnsignedValue,
      kVBCppExprKind_End
  } VBCPPEXPRKIND;
  
  
  /** Macro used for the precedence field. */
  #define VBCPPOP_PRECEDENCE(a_iPrecedence)   ((a_iPrecedence) << 8)
  /** Mask for getting the precedence field value. */
  #define VBCPPOP_PRECEDENCE_MASK             0xff00
  /** Operator associativity - Left to right. */
  #define VBCPPOP_L2R                         (1 << 16)
  /** Operator associativity - Right to left. */
  #define VBCPPOP_R2L                         (2 << 16)
  
  /**
   * Unary operators.
   */
  typedef enum VBCPPUNARYOP
  {
      kVBCppUnaryOp_Invalid = 0,
      kVBCppUnaryOp_Pluss             = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) |  5,
      kVBCppUnaryOp_Minus             = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) |  6,
      kVBCppUnaryOp_LogicalNot        = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) |  7,
      kVBCppUnaryOp_BitwiseNot        = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) |  8,
      kVBCppUnaryOp_Parenthesis       = VBCPPOP_R2L | VBCPPOP_PRECEDENCE(15) |  9,
      kVBCppUnaryOp_End
  } VBCPPUNARYOP;
  
  /**
   * Binary operators.
   */
  typedef enum VBCPPBINARYOP
  {
      kVBCppBinary_Invalid = 0,
      kVBCppBinary_Multiplication     = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 5) |  2,
      kVBCppBinary_Division           = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 5) |  4,
      kVBCppBinary_Modulo             = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 5) |  5,
      kVBCppBinary_Addition           = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 6) |  6,
      kVBCppBinary_Subtraction        = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 6) |  7,
      kVBCppBinary_LeftShift          = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 7) |  8,
      kVBCppBinary_RightShift         = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 7) |  9,
      kVBCppBinary_LessThan           = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 10,
      kVBCppBinary_LessThanOrEqual    = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 11,
      kVBCppBinary_GreaterThan        = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 12,
      kVBCppBinary_GreaterThanOrEqual = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 13,
      kVBCppBinary_EqualTo            = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 9) | 14,
      kVBCppBinary_NotEqualTo         = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 9) | 15,
      kVBCppBinary_BitwiseAnd         = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(10) | 16,
      kVBCppBinary_BitwiseXor         = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(11) | 17,
      kVBCppBinary_BitwiseOr          = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(12) | 18,
      kVBCppBinary_LogicalAnd         = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(13) | 19,
      kVBCppBinary_LogicalOr          = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(14) | 20,
      kVBCppBinary_End
  } VBCPPBINARYOP;
  
  /** The precedence of the ternary operator (expr ? true : false). */
  #define VBCPPTERNAROP_PRECEDENCE   VBCPPOP_PRECEDENCE(16)
  
  
  /** Pointer to an expression parsing node. */
  typedef struct VBCPPEXPR *PVBCPPEXPR;
  /**
   * Expression parsing node.
   */
  typedef struct VBCPPEXPR
  {
      /** Parent expression. */
      PVBCPPEXPR          pParent;
      /** Whether the expression is complete or not. */
      bool                fComplete;
      /** The kind of expression. */
      VBCPPEXPRKIND       enmKind;
      /** Kind specific content. */
      union
      {
          /** kVBCppExprKind_Unary */
          struct
          {
              VBCPPUNARYOP    enmOperator;
              PVBCPPEXPR      pArg;
          } Unary;
  
          /** kVBCppExprKind_Binary */
          struct
          {
              VBCPPBINARYOP   enmOperator;
              PVBCPPEXPR      pLeft;
              PVBCPPEXPR      pRight;
          } Binary;
  
          /** kVBCppExprKind_Ternary */
          struct
          {
              PVBCPPEXPR      pExpr;
              PVBCPPEXPR      pTrue;
              PVBCPPEXPR      pFalse;
          } Ternary;
  
          /** kVBCppExprKind_SignedValue */
          struct
          {
              int64_t         s64;
          } SignedValue;
  
          /** kVBCppExprKind_UnsignedValue */
          struct
          {
              uint64_t        u64;
          } UnsignedValue;
      } u;
  } VBCPPEXPR;
  
  
  /**
   * Operator return statuses.
   */
  typedef enum VBCPPEXPRRET
  {
      kExprRet_Error = -1,
      kExprRet_Ok = 0,
      kExprRet_UnaryOperator,
      kExprRet_Value,
      kExprRet_EndOfExpr,
      kExprRet_End
  } VBCPPEXPRRET;
  
  /**
   * Expression parser context.
   */
  typedef struct VBCPPEXPRPARSER
  {
      /** The current expression posistion. */
      const char         *pszCur;
      /** The root node. */
      PVBCPPEXPR          pRoot;
      /** The current expression node. */
      PVBCPPEXPR          pCur;
      /** Where to insert the next expression. */
      PVBCPPEXPR         *ppCur;
      /** The expression. */
      const char         *pszExpr;
      /** The number of undefined macros we've encountered while parsing. */
      size_t              cUndefined;
      /** Pointer to the C preprocessor instance. */
      PVBCPP              pThis;
  } VBCPPEXPRPARSER;
  /** Pointer to an expression parser context. */
  typedef VBCPPEXPRPARSER *PVBCPPEXPRPARSER;
  
  
  /**
   * Evaluation result.
   */
  typedef enum VBCPPEVAL
  {
      kVBCppEval_Invalid = 0,
      kVBCppEval_True,
      kVBCppEval_False,
      kVBCppEval_Undecided,
      kVBCppEval_End
  } VBCPPEVAL;
  
  
  /**
   * The condition kind.
   */
  typedef enum VBCPPCONDKIND
  {
      kVBCppCondKind_Invalid = 0,
      /** \#if expr  */
      kVBCppCondKind_If,
      /** \#ifdef define  */
      kVBCppCondKind_IfDef,
      /** \#ifndef define  */
      kVBCppCondKind_IfNDef,
      /** \#elif expr */
      kVBCppCondKind_ElIf,
      /** The end of valid values. */
      kVBCppCondKind_End
  } VBCPPCONDKIND;
  
  
  /**
   * Conditional stack entry.
   */
  typedef struct VBCPPCOND
  {
      /** The next conditional on the stack. */
      struct VBCPPCOND   *pUp;
      /** The kind of conditional. This changes on encountering \#elif. */
      VBCPPCONDKIND       enmKind;
      /** Evaluation result. */
      VBCPPEVAL           enmResult;
      /** The evaluation result of the whole stack. */
      VBCPPEVAL           enmStackResult;
  
      /** Whether we've seen the last else. */
      bool                fSeenElse;
      /** Set if we have an else if which has already been decided. */
      bool                fElIfDecided;
      /** The nesting level of this condition. */
      uint16_t            iLevel;
      /** The nesting level of this condition wrt the ones we keep. */
      uint16_t            iKeepLevel;
  
      /** The condition string. (Points within the stream buffer.) */
      const char         *pchCond;
      /** The condition length. */
      size_t              cchCond;
  } 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. */
      SCMSTREAM           StrmInput;
      /** 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
  {
      kVBCppIncludeAction_Invalid = 0,
      kVBCppIncludeAction_Include,
      kVBCppIncludeAction_PassThru,
      kVBCppIncludeAction_Drop,
      kVBCppIncludeAction_End
  } VBCPPINCLUDEACTION;
  
  
  /**
   * C Preprocessor instance data.
   */
  typedef struct VBCPP
  {
      /** @name Options
       * @{ */
      /** The preprocessing mode. */
      VBCPPMODE           enmMode;
      /** 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. */
      bool                fAllowRedefiningCmdLineDefines;
      /** 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. */
      VBCPPINCLUDEACTION  enmIncludeAction;
  
      /** The number of include directories. */
      uint32_t            cIncludes;
      /** 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. */
      RTSTRSPACE          StrSpace;
      /** The string space holding explicitly undefined macros for selective
       * preprocessing runs. */
      RTSTRSPACE          UndefStrSpace;
      /** 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. */
      VBCPP_BITMAP_TYPE   bmDefined[VBCPP_BITMAP_SIZE];
  
      /** The current depth of the conditional stack. */
      uint32_t            cCondStackDepth;
      /** Conditional stack. */
      PVBCPPCOND          pCondStack;
      /** 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 */
      uint32_t            cInputStackDepth;
      /** The input buffer stack. */
      PVBCPPINPUT         pInputStack;
  
      /** The output stream. */
      SCMSTREAM           StrmOutput;
  
      /** The status of the whole job, as far as we know. */
      RTEXITCODE          rcExit;
      /** Whether StrmOutput is valid (for vbcppTerm). */
      bool                fStrmOutputValid;
  } VBCPP;
  
  
  /*********************************************************************************************************************************
  *   Internal Functions                                                                                                           *
  *********************************************************************************************************************************/
  static PVBCPPMACRO  vbcppMacroLookup(PVBCPP pThis, const char *pszDefine, size_t cchDefine);
  static RTEXITCODE   vbcppMacroExpandIt(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offMacro, PVBCPPMACRO pMacro, size_t offParameters);
  static RTEXITCODE   vbcppMacroExpandReScan(PVBCPP pThis, PVBCPPMACROEXP pExp, VBCPPMACRORESCANMODE enmMode, size_t *pcReplacements);
  static void         vbcppMacroExpandCleanup(PVBCPPMACROEXP pExp);
  
  
  
  /*
   *
   *
   * 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   va                  Message arguments.
   */
  static RTEXITCODE vbcppErrorV(PVBCPP pThis, const char *pszMsg, va_list va)
  {
      NOREF(pThis);
      if (pThis->pInputStack)
      {
          PSCMSTREAM pStrm = &pThis->pInputStack->StrmInput;
  
          size_t const off     = ScmStreamTell(pStrm);
          size_t const iLine   = ScmStreamTellLine(pStrm);
          ScmStreamSeekByLine(pStrm, iLine);
          size_t const offLine = ScmStreamTell(pStrm);
  
          RTPrintf("%s:%d:%zd: error: %N.\n", pThis->pInputStack->szName, iLine + 1, off - offLine + 1, pszMsg, va);
  
          size_t cchLine;
          SCMEOL enmEof;
          const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
          if (pszLine)
              RTPrintf("  %.*s\n"
                       "  %*s^\n",
                       cchLine, pszLine, off - offLine, "");
  
          ScmStreamSeekAbsolute(pStrm, off);
      }
      else
          RTMsgErrorV(pszMsg, va);
      return pThis->rcExit = RTEXITCODE_FAILURE;
  }
  
  
  /**
   * Displays an error message.
   *
   * @returns RTEXITCODE_FAILURE
   * @param   pThis               The C preprocessor instance.
   * @param   pszMsg              The message.
   * @param   ...                 Message arguments.
   */
  static RTEXITCODE vbcppError(PVBCPP pThis, const char *pszMsg, ...)
  {
      va_list va;
      va_start(va, pszMsg);
      RTEXITCODE rcExit = vbcppErrorV(pThis, pszMsg, va);
      va_end(va);
      return rcExit;
  }
  
  
  /**
   * 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.
   */
  static RTEXITCODE vbcppErrorPos(PVBCPP pThis, const char *pszPos, const char *pszMsg, ...)
  {
      NOREF(pszPos); NOREF(pThis);
      va_list va;
      va_start(va, pszMsg);
      RTMsgErrorV(pszMsg, va);
      va_end(va);
      return pThis->rcExit = RTEXITCODE_FAILURE;
  }
  
  
  
  
  
  
  
  /*
   *
   *
   * Variable String Buffers.
   * Variable String Buffers.
   * Variable String Buffers.
   * Variable String Buffers.
   * Variable String Buffers.
   *
   *
   */
  
  
  /**
   * Initializes a string buffer.
   *
   * @param   pStrBuf             The buffer structure to initialize.
   * @param   pThis               The C preprocessor instance.
   */
  static void vbcppStrBufInit(PVBCPPSTRBUF pStrBuf, PVBCPP pThis)
  {
      pStrBuf->pThis              = pThis;
      pStrBuf->cchBuf             = 0;
      pStrBuf->cbBufAllocated     = 0;
      pStrBuf->pszBuf             = NULL;
  }
  
  
  /**
   * Deletes a string buffer.
   *
   * @param   pStrBuf             Pointer to the string buffer.
   */
  static void vbcppStrBufDelete(PVBCPPSTRBUF pStrBuf)
  {
      RTMemFree(pStrBuf->pszBuf);
      pStrBuf->pszBuf = NULL;
  }
  
  
  /**
   * Ensures that sufficient bufferspace is available, growing the buffer if
   * necessary.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
   * @param   pStrBuf             Pointer to the string buffer.
   * @param   cbMin               The minimum buffer size.
   */
  static RTEXITCODE vbcppStrBufGrow(PVBCPPSTRBUF pStrBuf, size_t cbMin)
  {
      if (pStrBuf->cbBufAllocated >= cbMin)
          return RTEXITCODE_SUCCESS;
  
      size_t cbNew = pStrBuf->cbBufAllocated * 2;
      if (cbNew < cbMin)
          cbNew = RT_ALIGN_Z(cbMin, _1K);
      void *pv = RTMemRealloc(pStrBuf->pszBuf, cbNew);
      if (!pv)
          return vbcppError(pStrBuf->pThis, "out of memory (%zu bytes)", cbNew);
  
      pStrBuf->pszBuf         = (char *)pv;
      pStrBuf->cbBufAllocated = cbNew;
      return RTEXITCODE_SUCCESS;
  }
  
  
  /**
   * Appends a substring.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
   * @param   pStrBuf             Pointer to the string buffer.
   * @param   pchSrc              Pointer to the first character in the substring.
   * @param   cchSrc              The length of the substring.
   */
  static RTEXITCODE vbcppStrBufAppendN(PVBCPPSTRBUF pStrBuf, const char *pchSrc, size_t cchSrc)
  {
      size_t cchBuf = pStrBuf->cchBuf;
      if (cchBuf + cchSrc + 1 > pStrBuf->cbBufAllocated)
      {
          RTEXITCODE rcExit = vbcppStrBufGrow(pStrBuf, cchBuf + cchSrc + 1);
          if (rcExit != RTEXITCODE_SUCCESS)
              return rcExit;
      }
  
      memcpy(&pStrBuf->pszBuf[cchBuf], pchSrc, cchSrc);
      cchBuf += cchSrc;
      pStrBuf->pszBuf[cchBuf] = '\0';
      pStrBuf->cchBuf = cchBuf;
  
      return RTEXITCODE_SUCCESS;
  }
  
  
  /**
   * Appends a character.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
   * @param   pStrBuf             Pointer to the string buffer.
   * @param   ch                  The charater to append.
   */
  static RTEXITCODE vbcppStrBufAppendCh(PVBCPPSTRBUF pStrBuf, char ch)
  {
      size_t cchBuf = pStrBuf->cchBuf;
      if (cchBuf + 2 > pStrBuf->cbBufAllocated)
      {
          RTEXITCODE rcExit = vbcppStrBufGrow(pStrBuf, cchBuf + 2);
          if (rcExit != RTEXITCODE_SUCCESS)
              return rcExit;
      }
  
      pStrBuf->pszBuf[cchBuf++] = ch;
      pStrBuf->pszBuf[cchBuf] = '\0';
      pStrBuf->cchBuf = cchBuf;
  
      return RTEXITCODE_SUCCESS;
  }
  
  
  /**
   * Appends a string to the buffer.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
   * @param   pStrBuf             Pointer to the string buffer.
   * @param   psz                 The string to append.
   */
  static RTEXITCODE vbcppStrBufAppend(PVBCPPSTRBUF pStrBuf, const char *psz)
  {
      return vbcppStrBufAppendN(pStrBuf, psz, strlen(psz));
  }
  
  
  /**
   * Gets the last char in the buffer.
   *
   * @returns Last character, 0 if empty.
   * @param   pStrBuf             Pointer to the string buffer.
   */
  static char vbcppStrBufLastCh(PVBCPPSTRBUF pStrBuf)
  {
      if (!pStrBuf->cchBuf)
          return '\0';
      return pStrBuf->pszBuf[pStrBuf->cchBuf - 1];
  }
  
  
  
  
  
  
  
  /*
   *
   *
   * 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.
   */
  DECLINLINE(bool) vbcppIsCIdentifierLeadChar(char ch)
  {
      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.
   */
  DECLINLINE(bool) vbcppIsCIdentifierChar(char ch)
  {
      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.
   */
  static bool vbcppValidateCIdentifier(PVBCPP pThis, const char *pchIdentifier, size_t cchIdentifier)
  {
      if (cchIdentifier == RTSTR_MAX)
          cchIdentifier = strlen(pchIdentifier);
  
      if (cchIdentifier == 0)
      {
          vbcppErrorPos(pThis, pchIdentifier, "Zero length identifier");
          return false;
      }
  
      if (!vbcppIsCIdentifierLeadChar(*pchIdentifier))
      {
          vbcppErrorPos(pThis, pchIdentifier, "Bad lead chararacter in identifier: '%.*s'", cchIdentifier, pchIdentifier);
          return false;
      }
  
      for (size_t off = 1; off < cchIdentifier; off++)
      {
          if (!vbcppIsCIdentifierChar(pchIdentifier[off]))
          {
              vbcppErrorPos(pThis, pchIdentifier + off, "Illegal chararacter in identifier: '%.*s' (#%zu)", cchIdentifier, pchIdentifier, off + 1);
              return false;
          }
      }
  
      return true;
  }
  
  #if 0
  
  /**
   * Checks if the given character is valid C punctuation.
   *
   * @returns true / false.
   * @param   ch                  The character to inspect.
   */
  DECLINLINE(bool) vbcppIsCPunctuationLeadChar(char ch)
  {
      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.
   */
  static size_t vbcppIsCPunctuationLeadChar(const char *psz, size_t cchMax)
  {
      if (!cchMax)
          return 0;
  
      switch (psz[0])
      {
          case '!':
          case '*':
          case '/':
          case '=':
          case '^':
              if (cchMax >= 2 && psz[1] == '=')
                  return 2;
              return 1;
  
          case '#':
              if (cchMax >= 2 && psz[1] == '#')
                  return 2;
              return 1;
  
          case '%':
              if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '>'))
                  return 2;
              if (cchMax >= 2 && psz[1] == ':')
              {
                  if (cchMax >= 4 && psz[2] == '%' && psz[3] == ':')
                      return 4;
                  return 2;
              }
              return 1;
  
          case '&':
              if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '&'))
                  return 2;
              return 1;
  
          case '(':
          case ')':
          case ',':
          case '?':
          case '[':
          case ']':
          case '{':
          case '}':
              return 1;
  
          case '+':
              if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '+'))
                  return 2;
              return 1;
  
          case '-':
              if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '-' || psz[1] == '>'))
                  return 2;
              return 1;
  
          case ':':
              if (cchMax >= 2 && psz[1] == '>')
                  return 2;
              return 1;
  
          case ';':
              return 1;
  
          case '<':
              if (cchMax >= 2 && psz[1] == '<')
              {
                  if (cchMax >= 3 && psz[2] == '=')
                      return 3;
                  return 2;
              }
              if (cchMax >= 2 && (psz[1] == '=' || psz[1] == ':' || psz[1] == '%'))
                  return 2;
              return 1;
  
          case '.':
              if (cchMax >= 3 && psz[1] == '.' && psz[2] == '.')
                  return 3;
              return 1;
  
          case '>':
              if (cchMax >= 2 && psz[1] == '>')
              {
                  if (cchMax >= 3 && psz[2] == '=')
                      return 3;
                  return 2;
              }
              if (cchMax >= 2 && psz[1] == '=')
                  return 2;
              return 1;
  
          case '|':
              if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '|'))
                  return 2;
              return 1;
  
          case '~':
              return 1;
  
          default:
              return 0;
      }
  }
  
  #endif
  
  
  
  
  
  /*
   *
   *
   * 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.
   */
  static RTEXITCODE vbcppOutputCh(PVBCPP pThis, char ch)
  {
      int rc = ScmStreamPutCh(&pThis->StrmOutput, ch);
      if (RT_SUCCESS(rc))
          return RTEXITCODE_SUCCESS;
      return vbcppError(pThis, "Output error: %Rrc", rc);
  }
  
  
  /**
   * 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.
   */
  static RTEXITCODE vbcppOutputWrite(PVBCPP pThis, const char *pch, size_t cch)
  {
      int rc = ScmStreamWrite(&pThis->StrmOutput, pch, cch);
      if (RT_SUCCESS(rc))
          return RTEXITCODE_SUCCESS;
      return vbcppError(pThis, "Output error: %Rrc", rc);
  }
  
  
  static RTEXITCODE vbcppOutputComment(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart, size_t cchOutputted,
                                       size_t cchMinIndent)
  {
      RT_NOREF_PV(cchMinIndent); /** @todo  cchMinIndent */
  
      size_t offCur = ScmStreamTell(pStrmInput);
      if (offStart < offCur)
      {
          int rc = ScmStreamSeekAbsolute(pStrmInput, offStart);
          AssertRCReturn(rc, vbcppError(pThis, "Input seek error: %Rrc", rc));
  
          /*
           * Use the same indent, if possible.
           */
          size_t cchIndent = offStart - ScmStreamTellOffsetOfLine(pStrmInput, ScmStreamTellLine(pStrmInput));
          if (cchOutputted < cchIndent)
              rc = ScmStreamPrintf(&pThis->StrmOutput, "%*s", cchIndent - cchOutputted, "");
          else
              rc = ScmStreamPutCh(&pThis->StrmOutput, ' ');
          if (RT_FAILURE(rc))
              return vbcppError(pThis, "Output error: %Rrc", rc);
  
          /*
           * Copy the bytes.
           */
          while (ScmStreamTell(pStrmInput) < offCur)
          {
              unsigned ch = ScmStreamGetCh(pStrmInput);
              if (ch == ~(unsigned)0)
                  return vbcppError(pThis, "Input error: %Rrc", rc);
              rc = ScmStreamPutCh(&pThis->StrmOutput, ch);
              if (RT_FAILURE(rc))
                  return vbcppError(pThis, "Output error: %Rrc", rc);
          }
      }
  
      return RTEXITCODE_SUCCESS;
  }
  
  
  
  
  
  /*
   *
   *
   * Input
   * Input
   * Input
   * Input
   * Input
   *
   *
   */
  
  
  #if 0 /* unused */
  /**
   * Skips white spaces, including escaped new-lines.
   *
   * @param   pStrmInput          The input stream.
   */
  static void vbcppProcessSkipWhiteAndEscapedEol(PSCMSTREAM pStrmInput)
  {
      unsigned chPrev = ~(unsigned)0;
      unsigned ch;
      while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
      {
          if (ch == '\r' || ch == '\n')
          {
              if (chPrev != '\\')
                  break;
              chPrev = ch;
              ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
          }
          else if (RT_C_IS_SPACE(ch))
          {
              chPrev = ch;
              ch = ScmStreamGetCh(pStrmInput);
              Assert(ch == chPrev);
          }
          else
              break;
      }
  }
  #endif
  
  
  /**
   * Skips white spaces, escaped new-lines and multi line comments.
   *
   * @param   pThis               The C preprocessor instance.
   * @param   pStrmInput          The input stream.
   */
  static RTEXITCODE vbcppProcessSkipWhiteEscapedEolAndComments(PVBCPP pThis, PSCMSTREAM pStrmInput)
  {
      unsigned chPrev = ~(unsigned)0;
      unsigned ch;
      while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
      {
          if (!RT_C_IS_SPACE(ch))
          {
              /* Multi-line Comment? */
              if (ch != '/')
                  break;                  /* most definitely, not. */
  
              size_t offSaved = ScmStreamTell(pStrmInput);
              ScmStreamGetCh(pStrmInput);
              if (ScmStreamPeekCh(pStrmInput) != '*')
              {
                  ScmStreamSeekAbsolute(pStrmInput, offSaved);
                  break;              /* no */
              }
  
              /* Skip to the end of the comment. */
              while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
              {
                  if (ch == '*')
                  {
                      ch = ScmStreamGetCh(pStrmInput);
                      if (ch == '/')
                          break;
                      if (ch == ~(unsigned)0)
                          break;
                  }
              }
              if (ch == ~(unsigned)0)
                  return vbcppError(pThis, "unterminated multi-line comment");
              chPrev = '/';
          }
          /* New line (also matched by RT_C_IS_SPACE). */
          else if (ch == '\r' || ch == '\n')
          {
              /* Stop if not escaped. */
              if (chPrev != '\\')
                  break;
              chPrev = ch;
              ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
          }
          /* Real space char. */
          else
          {
              chPrev = ch;
              ch = ScmStreamGetCh(pStrmInput);
              Assert(ch == chPrev);
          }
      }
      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)
  {
      RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          unsigned ch = ScmStreamPeekCh(pStrmInput);
          if (   ch != ~(unsigned)0
              && ch != '\r'
              && ch != '\n')
              rcExit = vbcppError(pThis, "Did not expected anything more on this line");
      }
      return rcExit;
  }
  
  
  /**
   * Skips white spaces.
   *
   * @returns The current location upon return.
   * @param   pStrmInput          The input stream.
   */
  static size_t vbcppProcessSkipWhite(PSCMSTREAM pStrmInput)
  {
      unsigned ch;
      while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
      {
          if (!RT_C_IS_SPACE(ch) || ch == '\r' || ch == '\n')
              break;
          unsigned chCheck = ScmStreamGetCh(pStrmInput);
          AssertBreak(chCheck == ch);
      }
      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.
   */
  static bool vbcppInputLookForLeftParenthesis(PVBCPP pThis, PSCMSTREAM pStrmInput)
  {
      size_t offSaved = ScmStreamTell(pStrmInput);
      /*RTEXITCODE rcExit =*/ vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
      unsigned ch = ScmStreamPeekCh(pStrmInput);
      if (ch == '(')
          return true;
  
      int rc = ScmStreamSeekAbsolute(pStrmInput, offSaved);
      AssertFatalRC(rc);
      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;
  
      RTEXITCODE  rcExit      = RTEXITCODE_SUCCESS;
      bool        fInComment  = false;
      unsigned    chPrev      = 0;
      unsigned    ch;
      while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
      {
          if (ch == '\r' || ch == '\n')
          {
              if (chPrev == '\\')
              {
                  ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
                  continue;
              }
              if (!fInComment)
                  break;
              /* The expression continues after multi-line comments. Cool. :-) */
          }
          else if (!fInComment)
          {
              if (chPrev == '/' && ch == '*' )
              {
                  fInComment = true;
                  if (poffComment)
                      *poffComment = ScmStreamTell(pStrmInput) - 1;
              }
              else if (chPrev == '/' && ch == '/')
              {
                  if (poffComment)
                      *poffComment = ScmStreamTell(pStrmInput) - 1;
                  rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
                  break;                  /* done */
              }
          }
          else if (ch == '/' && chPrev == '*')
              fInComment = false;
  
          /* advance */
          chPrev = ch;
          ch = ScmStreamGetCh(pStrmInput); Assert(ch == chPrev);
      }
      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.
   */
  static RTEXITCODE vbcppProcessMultiLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
  {
      /* The open comment sequence. */
      ScmStreamGetCh(pStrmInput);         /* '*' */
      RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
      if (   pThis->fKeepComments
          && !pThis->fIf0Mode)
          rcExit = vbcppOutputWrite(pThis, "/*", 2);
  
      /* The comment.*/
      unsigned ch;
      while (   rcExit == RTEXITCODE_SUCCESS
             && (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0 )
      {
          if (ch == '*')
          {
              /* Closing sequence? */
              unsigned ch2 = ScmStreamPeekCh(pStrmInput);
              if (ch2 == '/')
              {
                  ScmStreamGetCh(pStrmInput);
                  if (   pThis->fKeepComments
                      && !pThis->fIf0Mode)
                      rcExit = vbcppOutputWrite(pThis, "*/", 2);
                  break;
              }
          }
  
          if (ch == '\r' || ch == '\n')
          {
              if (   (   pThis->fKeepComments
                      && !pThis->fIf0Mode)
                  || !pThis->fRemoveDroppedLines
                  || !ScmStreamIsAtStartOfLine(&pThis->StrmOutput))
                  rcExit = vbcppOutputCh(pThis, ch);
              pThis->fJustDroppedLine       = false;
              pThis->fMaybePreprocessorLine = true;
          }
          else if (   pThis->fKeepComments
                   && !pThis->fIf0Mode)
              rcExit = vbcppOutputCh(pThis, ch);
  
          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.
   */
  static RTEXITCODE vbcppProcessOneLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
  {
      RTEXITCODE  rcExit = RTEXITCODE_SUCCESS;
      SCMEOL      enmEol;
      size_t      cchLine;
      const char *pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol); Assert(pszLine);
      pszLine--; cchLine++;               /* unfetching the first slash. */
      for (;;)
      {
          if (   pThis->fKeepComments
              && !pThis->fIf0Mode)
              rcExit = vbcppOutputWrite(pThis, pszLine, cchLine + enmEol);
          else if (   !pThis->fIf0Mode
                   || !pThis->fRemoveDroppedLines
                   || !ScmStreamIsAtStartOfLine(&pThis->StrmOutput) )
              rcExit = vbcppOutputWrite(pThis, pszLine + cchLine, enmEol);
          if (rcExit != RTEXITCODE_SUCCESS)
              break;
          if (   cchLine == 0
              || pszLine[cchLine - 1] != '\\')
              break;
  
          pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol);
          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.
   */
  static RTEXITCODE vbcppProcessStringLitteral(PVBCPP pThis, PSCMSTREAM pStrmInput)
  {
      RTEXITCODE rcExit = vbcppOutputCh(pThis, '"');
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          bool fEscaped = false;
          for (;;)
          {
              unsigned ch = ScmStreamGetCh(pStrmInput);
              if (ch == ~(unsigned)0)
              {
                  rcExit = vbcppError(pThis, "Unterminated double quoted string");
                  break;
              }
  
              rcExit = vbcppOutputCh(pThis, ch);
              if (rcExit != RTEXITCODE_SUCCESS)
                  break;
  
              if (ch == '"' && !fEscaped)
                  break;
              fEscaped = !fEscaped && ch == '\\';
          }
      }
      return rcExit;
  }
  
  
  /**
   * Processes a single quoted constant.
   *
   * Must not replace any C-words in character constants.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
   * @param   pThis               The C preprocessor instance.
   * @param   pStrmInput          The input stream.
   */
  static RTEXITCODE vbcppProcessCharacterConstant(PVBCPP pThis, PSCMSTREAM pStrmInput)
  {
      RTEXITCODE rcExit = vbcppOutputCh(pThis, '\'');
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          bool fEscaped = false;
          for (;;)
          {
              unsigned ch = ScmStreamGetCh(pStrmInput);
              if (ch == ~(unsigned)0)
              {
                  rcExit = vbcppError(pThis, "Unterminated singled quoted string");
                  break;
              }
  
              rcExit = vbcppOutputCh(pThis, ch);
              if (rcExit != RTEXITCODE_SUCCESS)
                  break;
  
              if (ch == '\'' && !fEscaped)
                  break;
              fEscaped = !fEscaped && ch == '\\';
          }
      }
      return rcExit;
  }
  
  
  /**
   * Processes a integer or floating point number constant.
   *
   * Must not replace the type suffix.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
   * @param   pThis               The C preprocessor instance.
   * @param   pStrmInput          The input stream.
   * @param   chFirst             The first character.
   */
  static RTEXITCODE vbcppProcessNumber(PVBCPP pThis, PSCMSTREAM pStrmInput, char chFirst)
  {
      RTEXITCODE rcExit = vbcppOutputCh(pThis, chFirst);
  
      unsigned ch;
      while (   rcExit == RTEXITCODE_SUCCESS
             && (ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
      {
          if (   !vbcppIsCIdentifierChar(ch)
              && ch != '.')
              break;
  
          unsigned ch2 = ScmStreamGetCh(pStrmInput);
          AssertBreakStmt(ch2 == ch, rcExit = vbcppError(pThis, "internal error"));
          rcExit = vbcppOutputCh(pThis, ch);
      }
  
      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.
   */
  static RTEXITCODE vbcppProcessIdentifier(PVBCPP pThis, PSCMSTREAM pStrmInput)
  {
      RTEXITCODE  rcExit;
      size_t      cchDefine;
      const char *pchDefine = ScmStreamCGetWordM1(pStrmInput, &cchDefine);
      AssertReturn(pchDefine, vbcppError(pThis, "Internal error in ScmStreamCGetWordM1"));
  
      /*
       * Does this look like a define we know?
       */
      PVBCPPMACRO pMacro = vbcppMacroLookup(pThis, pchDefine, cchDefine);
      if (   pMacro
          && (   !pMacro->fFunction
              || vbcppInputLookForLeftParenthesis(pThis, pStrmInput)) )
      {
          /*
           * Expand it.
           */
          VBCPPMACROEXP ExpCtx;
  #if 0
          ExpCtx.pMacroStack    = NULL;
  #endif
          ExpCtx.pStrmInput     = pStrmInput;
          ExpCtx.papszArgs      = NULL;
          ExpCtx.cArgs          = 0;
          ExpCtx.cArgsAlloced   = 0;
          vbcppStrBufInit(&ExpCtx.StrBuf, pThis);
          rcExit = vbcppStrBufAppendN(&ExpCtx.StrBuf, pchDefine, cchDefine);
          if (rcExit == RTEXITCODE_SUCCESS)
              rcExit = vbcppMacroExpandIt(pThis, &ExpCtx, 0 /* offset */, pMacro, cchDefine);
          if (rcExit == RTEXITCODE_SUCCESS)
              rcExit = vbcppMacroExpandReScan(pThis, &ExpCtx, kMacroReScanMode_Normal, NULL);
          if (rcExit == RTEXITCODE_SUCCESS)
          {
              /*
               * Insert it into the output stream.  Make sure there is a
               * whitespace following it.
               */
              int rc = ScmStreamWrite(&pThis->StrmOutput, ExpCtx.StrBuf.pszBuf, ExpCtx.StrBuf.cchBuf);
              if (RT_SUCCESS(rc))
              {
                  unsigned chAfter = ScmStreamPeekCh(pStrmInput);
                  if (chAfter != ~(unsigned)0 && !RT_C_IS_SPACE(chAfter))
                      rcExit = vbcppOutputCh(pThis, ' ');
              }
              else
                  rcExit = vbcppError(pThis, "Output error: %Rrc", rc);
          }
          vbcppMacroExpandCleanup(&ExpCtx);
      }
      else
      {
          /*
           * Not a macro or a function-macro name match but no invocation, just
           * output the text unchanged.
           */
          int rc = ScmStreamWrite(&pThis->StrmOutput, pchDefine, cchDefine);
          if (RT_SUCCESS(rc))
              rcExit = RTEXITCODE_SUCCESS;
          else
              rcExit = vbcppError(pThis, "Output error: %Rrc", rc);
      }
      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.
   */
  static bool vbcppMacroExists(PVBCPP pThis, const char *pszDefine, size_t cchDefine)
  {
      return cchDefine > 0
          && VBCPP_BITMAP_IS_SET(pThis->bmDefined, *pszDefine)
          && RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine) != NULL;
  }
  
  
  /**
   * 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.
   */
  static PVBCPPMACRO vbcppMacroLookup(PVBCPP pThis, const char *pszDefine, size_t cchDefine)
  {
      if (!cchDefine)
          return NULL;
      if (!VBCPP_BITMAP_IS_SET(pThis->bmDefined, *pszDefine))
          return NULL;
      return (PVBCPPMACRO)RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine);
  }
  
  
  static uint32_t vbcppMacroLookupArg(PVBCPPMACRO pMacro, const char *pchName, size_t cchName)
  {
      Assert(cchName > 0);
  
      char const ch = *pchName;
      for (uint32_t i = 0; i < pMacro->cArgs; i++)
          if (   pMacro->papszArgs[i][0] == ch
              && !strncmp(pMacro->papszArgs[i], pchName, cchName)
              && pMacro->papszArgs[i][cchName] == '\0')
              return i;
  
      if (   pMacro->fVarArg
          && cchName == sizeof("__VA_ARGS__") - 1
          && !strncmp(pchName, "__VA_ARGS__", sizeof("__VA_ARGS__") - 1) )
          return pMacro->cArgs;
  
      return UINT32_MAX;
  }
  
  
  static RTEXITCODE vbcppMacroExpandReplace(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t off, size_t cchToReplace,
                                            const char *pchReplacement, size_t cchReplacement)
  {
      RT_NOREF_PV(pThis);
  
      /*
       * Figure how much space we actually need.
       * (Hope this whitespace stuff is correct...)
       */
      bool const fLeadingSpace            = off > 0
                                         && !RT_C_IS_SPACE(pExp->StrBuf.pszBuf[off - 1]);
      bool const fTrailingSpace           = off + cchToReplace < pExp->StrBuf.cchBuf
                                         && !RT_C_IS_SPACE(pExp->StrBuf.pszBuf[off + cchToReplace]);
      size_t const cchActualReplacement   = fLeadingSpace + cchReplacement + fTrailingSpace;
  
      /*
       * Adjust the buffer size and contents.
       */
      if (cchActualReplacement > cchToReplace)
      {
          size_t const offMore = cchActualReplacement - cchToReplace;
  
          /* Ensure enough buffer space. */
          size_t cbMinBuf = offMore + pExp->StrBuf.cchBuf + 1;
          RTEXITCODE rcExit = vbcppStrBufGrow(&pExp->StrBuf, cbMinBuf);
          if (rcExit != RTEXITCODE_SUCCESS)
              return rcExit;
  
          /* Push the chars following the replacement area down to make room. */
          memmove(&pExp->StrBuf.pszBuf[off + cchToReplace + offMore],
                  &pExp->StrBuf.pszBuf[off + cchToReplace],
                  pExp->StrBuf.cchBuf - off - cchToReplace + 1);
          pExp->StrBuf.cchBuf += offMore;
  
      }
      else if (cchActualReplacement < cchToReplace)
      {
          size_t const offLess = cchToReplace - cchActualReplacement;
  
          /* Pull the chars following the replacement area up. */
          memmove(&pExp->StrBuf.pszBuf[off + cchToReplace - offLess],
                  &pExp->StrBuf.pszBuf[off + cchToReplace],
                  pExp->StrBuf.cchBuf - off - cchToReplace + 1);
          pExp->StrBuf.cchBuf -= offLess;
      }
  
      /*
       * Insert the replacement string.
       */
      char *pszCur = &pExp->StrBuf.pszBuf[off];
      if (fLeadingSpace)
          *pszCur++ = ' ';
      memcpy(pszCur, pchReplacement, cchReplacement);
      if (fTrailingSpace)
          *pszCur++ = ' ';
  
      Assert(strlen(pExp->StrBuf.pszBuf) == pExp->StrBuf.cchBuf);
  
      return RTEXITCODE_SUCCESS;
  }
  
  
  static unsigned vbcppMacroExpandPeekCh(PVBCPPMACROEXP pExp, size_t *poff)
  {
      size_t off = *poff;
      if (off >= pExp->StrBuf.cchBuf)
          return pExp->pStrmInput ? ScmStreamPeekCh(pExp->pStrmInput) : ~(unsigned)0;
      return pExp->StrBuf.pszBuf[off];
  }
  
  
  static unsigned vbcppMacroExpandGetCh(PVBCPPMACROEXP pExp, size_t *poff)
  {
      size_t off = *poff;
      if (off >= pExp->StrBuf.cchBuf)
          return pExp->pStrmInput ? ScmStreamGetCh(pExp->pStrmInput) : ~(unsigned)0;
      *poff = off + 1;
      return pExp->StrBuf.pszBuf[off];
  }
  
  
  static RTEXITCODE vbcppMacroExpandSkipEolEx(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff, unsigned chFirst)
  {
      if (chFirst == '\r')
      {
          unsigned ch2 = vbcppMacroExpandPeekCh(pExp, poff);
          if (ch2 == '\n')
          {
              ch2 = ScmStreamGetCh(pExp->pStrmInput);
              AssertReturn(ch2 == '\n', vbcppError(pThis, "internal error"));
          }
      }
      return RTEXITCODE_SUCCESS;
  }
  
  
  static RTEXITCODE vbcppMacroExpandSkipEol(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
  {
      unsigned ch = vbcppMacroExpandGetCh(pExp, poff);
      AssertReturn(ch == '\r' || ch == '\n', vbcppError(pThis, "internal error"));
      return vbcppMacroExpandSkipEolEx(pThis, pExp, poff, ch);
  }
  
  
  static RTEXITCODE vbcppMacroExpandSkipCommentLine(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
  {
      unsigned ch = vbcppMacroExpandGetCh(pExp, poff);
      AssertReturn(ch == '/', vbcppError(pThis, "Internal error - expected '/' got '%c'", ch));
  
      unsigned chPrev = 0;
      while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
      {
          if (ch == '\r' || ch == '\n')
          {
              RTEXITCODE rcExit = vbcppMacroExpandSkipEolEx(pThis, pExp, poff, ch);
              if (rcExit != RTEXITCODE_SUCCESS)
                  return rcExit;
              if (chPrev != '\\')
                  break;
          }
  
          chPrev = ch;
      }
      return RTEXITCODE_SUCCESS;
  }
  
  
  static RTEXITCODE vbcppMacroExpandSkipComment(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
  {
      unsigned ch = vbcppMacroExpandGetCh(pExp, poff);
      AssertReturn(ch == '*', vbcppError(pThis, "Internal error - expected '*' got '%c'", ch));
  
      unsigned chPrev2 = 0;
      unsigned chPrev  = 0;
      while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
      {
          if (ch == '/' && chPrev == '*')
              break;
  
          if (ch == '\r' || ch == '\n')
          {
              RTEXITCODE rcExit = vbcppMacroExpandSkipEolEx(pThis, pExp, poff, ch);
              if (rcExit != RTEXITCODE_SUCCESS)
                  return rcExit;
              if (chPrev == '\\')
              {
                  chPrev = chPrev2;       /* for line splicing */
                  continue;
              }
          }
  
          chPrev2 = chPrev;
          chPrev  = ch;
      }
      return RTEXITCODE_SUCCESS;
  }
  
  
  static RTEXITCODE vbcppMacroExpandGrowArgArray(PVBCPP pThis, PVBCPPMACROEXP pExp, uint32_t cMinArgs)
  {
      if (cMinArgs > pExp->cArgsAlloced)
      {
          void *pv = RTMemRealloc(pExp->papszArgs, cMinArgs * sizeof(char *));
          if (!pv)
              return vbcppError(pThis, "out of memory");
          pExp->papszArgs = (char **)pv;
          pExp->cArgsAlloced = cMinArgs;
      }
      return RTEXITCODE_SUCCESS;
  }
  
  
  static RTEXITCODE vbcppMacroExpandAddEmptyParameter(PVBCPP pThis, PVBCPPMACROEXP pExp)
  {
      RTEXITCODE rcExit = vbcppMacroExpandGrowArgArray(pThis, pExp, pExp->cArgs + 1);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          char *pszArg = (char *)RTMemAllocZ(1);
          if (pszArg)
              pExp->papszArgs[pExp->cArgs++] = pszArg;
          else
              rcExit = vbcppError(pThis, "out of memory");
      }
      return rcExit;
  }
  
  
  static RTEXITCODE vbcppMacroExpandGatherParameters(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff, uint32_t cArgsHint)
  {
      RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
  
      /*
       * Free previous argument values.
       */
      while (pExp->cArgs > 0)
      {
          RTMemFree(pExp->papszArgs[--pExp->cArgs]);
          pExp->papszArgs[pExp->cArgs] = NULL;
      }
  
      /*
       * The current character should be an opening parenthsis.
       */
      unsigned    ch = vbcppMacroExpandGetCh(pExp, poff);
      if (ch != '(')
          return vbcppError(pThis, "Internal error - expected '(', found '%c' (#x)", ch, ch);
  
      /*
       * Parse the argument list.
       */
      char        chQuote      = 0;
      size_t      cbArgAlloc   = 0;
      size_t      cchArg       = 0;
      char       *pszArg       = NULL;
      size_t      cParentheses = 1;
      unsigned    chPrev       = 0;
      while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
      {
  /** @todo check for '#directives'! */
          if (ch == ')' && !chQuote)
          {
              Assert(cParentheses >= 1);
              cParentheses--;
  
              /* The end? */
              if (!cParentheses)
              {
                  if (cchArg)
                      while (cchArg > 0 && RT_C_IS_SPACE(pszArg[cchArg - 1]))
                          pszArg[--cchArg] = '\0';
                  else if (pExp->cArgs || cArgsHint > 0)
                      rcExit = vbcppMacroExpandAddEmptyParameter(pThis, pExp);
                  break;
              }
          }
          else if (ch == '('  && !chQuote)
              cParentheses++;
          else if (ch == ',' && cParentheses == 1 && !chQuote)
          {
              /* End of one argument, start of the next. */
              if (cchArg)
                  while (cchArg > 0 && RT_C_IS_SPACE(pszArg[cchArg - 1]))
                      pszArg[--cchArg] = '\0';
              else
              {
                  rcExit = vbcppMacroExpandAddEmptyParameter(pThis, pExp);
                  if (rcExit != RTEXITCODE_SUCCESS)
                      break;
              }
  
              cbArgAlloc = 0;
              cchArg     = 0;
              pszArg     = NULL;
              continue;
          }
          else if (ch == '/' && !chQuote)
          {
              /* Comment? */
              unsigned ch2 = vbcppMacroExpandPeekCh(pExp, poff);
              /** @todo This ain't right wrt line splicing. */
              if (ch2 == '/' || ch == '*')
              {
                  if (ch2 == '/')
                      rcExit = vbcppMacroExpandSkipCommentLine(pThis, pExp, poff);
                  else
                      rcExit = vbcppMacroExpandSkipComment(pThis, pExp, poff);
                  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? */
              unsigned ch2 = vbcppMacroExpandPeekCh(pExp, poff);
              if (ch2 == '\r' || ch2 == '\n')
              {
                  rcExit = vbcppMacroExpandSkipEol(pThis, pExp, poff);
                  if (rcExit != RTEXITCODE_SUCCESS)
                      break;
                  continue;
              }
          }
          else if (cchArg == 0 && RT_C_IS_SPACE(ch))
              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. */
          if (cchArg + 1 >= cbArgAlloc)
          {
              /* Add argument to the vector. */
              if (!cchArg)
              {
                  rcExit = vbcppMacroExpandGrowArgArray(pThis, pExp, RT_MAX(pExp->cArgs + 1, cArgsHint));
                  if (rcExit != RTEXITCODE_SUCCESS)
                      break;
                  pExp->papszArgs[pExp->cArgs++] = pszArg;
              }
  
              /* Resize the argument value buffer. */
              cbArgAlloc = cbArgAlloc ? cbArgAlloc * 2 : 16;
              pszArg = (char *)RTMemRealloc(pszArg, cbArgAlloc);
              if (!pszArg)
              {
                  rcExit = vbcppError(pThis, "out of memory");
                  break;
              }
              pExp->papszArgs[pExp->cArgs - 1] = pszArg;
          }
  
          pszArg[cchArg++] = ch;
          pszArg[cchArg]   = '\0';
      }
  
      /*
       * Check that we're leaving on good terms.
       */
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          if (cParentheses)
              rcExit = vbcppError(pThis, "Missing ')'");
      }
  
      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, PVBCPPMACRO pMacro,
                                                       PVBCPPSTRBUF pStrBuf)
  {
      Assert(pMacro->fFunction);
  
      /*
       * Empty?
       */
      if (   !pMacro->cchValue
          || (pMacro->cchValue == 1 && pMacro->szValue[0] == '#'))
          return RTEXITCODE_SUCCESS;
  
      /*
       * Parse the value.
       */
      RTEXITCODE  rcExit    = RTEXITCODE_SUCCESS;
      const char *pszSrc    = pMacro->szValue;
      const char *pszSrcSeq;
      char        ch;
      while ((ch = *pszSrc++) != '\0')
      {
          Assert(ch != '\r'); Assert(ch != '\n'); /* probably not true atm. */
          if (ch == '#')
          {
              if (*pszSrc == '#')
              {
                  /* Concatenate operator. */
                  rcExit = vbcppError(pThis, "The '##' operatore is not yet implemented");
              }
              else
              {
                  /* Stringify macro argument. */
                  rcExit = vbcppError(pThis, "The '#' operatore is not yet implemented");
              }
              return rcExit;
          }
          else if (ch == '"')
          {
              /* String litteral. */
              pszSrcSeq = pszSrc - 1;
              while ((ch = *pszSrc++) != '"')
              {
                  if (ch == '\\')
                      ch = *pszSrc++;
                  if (ch == '\0')
                  {
                      rcExit = vbcppError(pThis, "String litteral is missing closing quote (\").");
                      break;
                  }
              }
              rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
          }
          else if (ch == '\'')
          {
              /* Character constant. */
              pszSrcSeq = pszSrc - 1;
              while ((ch = *pszSrc++) != '\'')
              {
                  if (ch == '\\')
                      ch = *pszSrc++;
                  if (ch == '\0')
                  {
                      rcExit = vbcppError(pThis, "Character constant is missing closing quote (').");
                      break;
                  }
              }
              rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
          }
          else if (RT_C_IS_DIGIT(ch))
          {
              /* Process numerical constants correctly (i.e. don't mess with the suffix). */
              pszSrcSeq = pszSrc - 1;
              while (   (ch = *pszSrc) != '\0'
                     && (   vbcppIsCIdentifierChar(ch)
                         || ch == '.') )
                  pszSrc++;
              rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
          }
          else if (RT_C_IS_SPACE(ch))
          {
              /* join spaces */
              if (RT_C_IS_SPACE(vbcppStrBufLastCh(pStrBuf)))
                  continue;
              rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
          }
          else if (vbcppIsCIdentifierLeadChar(ch))
          {
              /* Something we should replace? */
              pszSrcSeq = pszSrc - 1;
              while (   (ch = *pszSrc) != '\0'
                     && vbcppIsCIdentifierChar(ch))
                  pszSrc++;
              size_t      cchDefine = pszSrc - pszSrcSeq;
              uint32_t    iArg;
              if (   VBCPP_BITMAP_IS_SET(pMacro->bmArgs, *pszSrcSeq)
                  && (iArg = vbcppMacroLookupArg(pMacro, pszSrcSeq, cchDefine)) != UINT32_MAX)
              {
                  /** @todo check out spaces here! */
                  if (iArg < pMacro->cArgs)
                  {
                      Assert(iArg < pExp->cArgs);
                      rcExit = vbcppStrBufAppend(pStrBuf, pExp->papszArgs[iArg]);
                      if (*pExp->papszArgs[iArg] != '\0' && rcExit == RTEXITCODE_SUCCESS)
                          rcExit = vbcppStrBufAppendCh(pStrBuf, ' ');
                  }
                  else
                  {
                      /* __VA_ARGS__ */
                      if (iArg < pExp->cArgs)
                      {
                          for (;;)
                          {
                              rcExit = vbcppStrBufAppend(pStrBuf, pExp->papszArgs[iArg]);
                              if (rcExit != RTEXITCODE_SUCCESS)
                                  break;
                              iArg++;
                              if (iArg >= pExp->cArgs)
                                  break;
                              rcExit = vbcppStrBufAppendCh(pStrBuf, ',');
                              if (rcExit != RTEXITCODE_SUCCESS)
                                  break;
                          }
                      }
                      if (rcExit == RTEXITCODE_SUCCESS)
                          rcExit = vbcppStrBufAppendCh(pStrBuf, ' ');
                  }
              }
              /* Not an argument needing replacing. */
              else
                  rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, cchDefine);
          }
          else
          {
              rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
          }
      }
  
      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.
   *                              Ignored if not function macro.  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, PVBCPPMACRO pMacro,
                                       size_t offParameters)
  {
      RTEXITCODE rcExit;
      Assert(offMacro + pMacro->Core.cchString <= pExp->StrBuf.cchBuf);
      Assert(!pMacro->fExpanding);
  
      /*
       * Function macros are kind of difficult...
       */
      if (pMacro->fFunction)
      {
          rcExit = vbcppMacroExpandGatherParameters(pThis, pExp, &offParameters, pMacro->cArgs + pMacro->fVarArg);
          if (rcExit == RTEXITCODE_SUCCESS)
          {
              if (pExp->cArgs > pMacro->cArgs && !pMacro->fVarArg)
                  rcExit = vbcppError(pThis, "Too many arguments to macro '%s' - found %u, expected %u",
                                      pMacro->Core.pszString, pExp->cArgs, pMacro->cArgs);
              else if (pExp->cArgs < pMacro->cArgs)
                  rcExit = vbcppError(pThis, "Too few arguments to macro '%s' - found %u, expected %u",
                                      pMacro->Core.pszString, pExp->cArgs, pMacro->cArgs);
          }
          if (rcExit == RTEXITCODE_SUCCESS)
          {
              VBCPPSTRBUF ValueBuf;
              vbcppStrBufInit(&ValueBuf, pThis);
              rcExit = vbcppMacroExpandValueWithArguments(pThis, pExp, pMacro, &ValueBuf);
              if (rcExit == RTEXITCODE_SUCCESS)
                  rcExit = vbcppMacroExpandReplace(pThis, pExp, offMacro, offParameters - offMacro,
                                                   ValueBuf.pszBuf, ValueBuf.cchBuf);
              vbcppStrBufDelete(&ValueBuf);
          }
      }
      /*
       * Object-like macros are easy. :-)
       */
      else
          rcExit = vbcppMacroExpandReplace(pThis, pExp, offMacro, pMacro->Core.cchString, pMacro->szValue, pMacro->cchValue);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
  #if 0 /* wrong */
          /*
           * Push the macro onto the stack.
           */
          pMacro->fExpanding   = true;
          pMacro->pUpExpanding = pExp->pMacroStack;
          pExp->pMacroStack    = pMacro;
  #endif
      }
  
      return rcExit;
  }
  
  
  /**
   * Looks for a left parenthesis in the macro expansion buffer and the input
   * stream.
   *
   * @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   pExp                The expansion context.
   * @param   poff                The current offset in the expansion context.
   *                              Will be updated on success.
   *
   * @sa vbcppInputLookForLeftParenthesis
   */
  static bool vbcppMacroExpandLookForLeftParenthesis(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
  {
      /*
       * Search the buffer first. (No comments there.)
       */
      size_t off = *poff;
      while (off < pExp->StrBuf.cchBuf)
      {
          char ch = pExp->StrBuf.pszBuf[off];
          if (!RT_C_IS_SPACE(ch))
          {
              if (ch == '(')
              {
                  *poff = off;
                  return true;
              }
              return false;
          }
          off++;
      }
  
      /*
       * Reached the end of the buffer, continue searching in the stream.
       */
      PSCMSTREAM pStrmInput = pExp->pStrmInput;
      size_t     offSaved   = ScmStreamTell(pStrmInput);
      /*RTEXITCODE rcExit = */ vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
      unsigned ch = ScmStreamPeekCh(pStrmInput);
      if (ch == '(')
      {
          *poff = pExp->StrBuf.cchBuf;
          return true;
      }
  
      int rc = ScmStreamSeekAbsolute(pStrmInput, offSaved);
      AssertFatalRC(rc);
      return false;
  }
  
  
  /**
   * Implements the 'defined' unary operator for \#if and \#elif expressions.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
   * @param   pThis               The C preprocessor instance.
   * @param   pExp                The expansion context.
   * @param   offStart            The expansion buffer offset where the 'defined'
   *                              occurs.
   * @param   poff                Where to store the offset at which the re-scan
   *                              shall resume upon return.
   */
  static RTEXITCODE vbcppMacroExpandDefinedOperator(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offStart, size_t *poff)
  {
      Assert(!pExp->pStrmInput); /* offset usage below. */
  
      /*
       * Skip white space.
       */
      unsigned ch;
      while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
          if (!RT_C_IS_SPACE(ch))
              break;
      bool const fWithParenthesis = ch == '(';
      if (fWithParenthesis)
          while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
              if (!RT_C_IS_SPACE(ch))
                  break;
  
      /*
       * Macro identifier.
       */
      if (!vbcppIsCIdentifierLeadChar(ch))
          return vbcppError(pThis, "Expected macro name after 'defined' operator");
  
      size_t const offDefine = *poff - 1;
      while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
          if (!vbcppIsCIdentifierChar(ch))
              break;
      size_t const cchDefine = *poff - offDefine - 1;
  
      /*
       * Check for closing parenthesis.
       */
      if (fWithParenthesis)
      {
          while (RT_C_IS_SPACE(ch))
              ch = vbcppMacroExpandGetCh(pExp, poff);
          if (ch != ')')
              return vbcppError(pThis, "Expected closing parenthesis after macro name");
      }
  
      /*
       * Do the job.
       */
      const char *pszResult = vbcppMacroExists(pThis, &pExp->StrBuf.pszBuf[offDefine], cchDefine)
                            ? "1" : "0";
      RTEXITCODE  rcExit    = vbcppMacroExpandReplace(pThis, pExp, offStart, *poff - offStart, pszResult, 1);
      *poff = offStart + 1;
      return rcExit;
  }
  
  
  /**
   * Re-scan the expanded macro.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
   * @param   pThis               The C preprocessor instance.
   * @param   pExp                The expansion context.
   * @param   enmMode             The re-scan mode.
   * @param   pcReplacements      Where to return the number of replacements
   *                              performed.  Optional.
   */
  static RTEXITCODE vbcppMacroExpandReScan(PVBCPP pThis, PVBCPPMACROEXP pExp, VBCPPMACRORESCANMODE enmMode, size_t *pcReplacements)
  {
      RTEXITCODE  rcExit        = RTEXITCODE_SUCCESS;
      size_t      cReplacements = 0;
      size_t      off           = 0;
      unsigned    ch;
      while (   off < pExp->StrBuf.cchBuf
             && (ch = vbcppMacroExpandGetCh(pExp, &off)) != ~(unsigned)0)
      {
          /*
           * String litteral or character constant.
           */
          if (ch == '\'' || ch == '"')
          {
              unsigned const chEndQuote = ch;
              while (   off < pExp->StrBuf.cchBuf
                     && (ch = vbcppMacroExpandGetCh(pExp, &off)) != ~(unsigned)0)
              {
                  if (ch == '\\')
                  {
                      ch = vbcppMacroExpandGetCh(pExp, &off);
                      if (ch == ~(unsigned)0)
                          break;
                  }
                  else if (ch == chEndQuote)
                      break;
              }
              if (ch == ~(unsigned)0)
                  return vbcppError(pThis, "Missing end quote (%c)", chEndQuote);
          }
          /*
           * Number constant.
           */
          else if (   RT_C_IS_DIGIT(ch)
                   || (   ch == '.'
                       && off + 1 < pExp->StrBuf.cchBuf
                       && RT_C_IS_DIGIT(vbcppMacroExpandPeekCh(pExp, &off))
                      )
                  )
          {
              while (   off < pExp->StrBuf.cchBuf
                     && (ch = vbcppMacroExpandPeekCh(pExp, &off)) != ~(unsigned)0
                     && vbcppIsCIdentifierChar(ch) )
                  vbcppMacroExpandGetCh(pExp, &off);
          }
          /*
           * Something that can be replaced?
           */
          else if (vbcppIsCIdentifierLeadChar(ch))
          {
              size_t offDefine = off - 1;
              while (   off < pExp->StrBuf.cchBuf
                     && (ch = vbcppMacroExpandPeekCh(pExp, &off)) != ~(unsigned)0
                     && vbcppIsCIdentifierChar(ch) )
                  vbcppMacroExpandGetCh(pExp, &off);
              size_t cchDefine = off - offDefine;
  
              PVBCPPMACRO pMacro = vbcppMacroLookup(pThis, &pExp->StrBuf.pszBuf[offDefine], cchDefine);
              if (   pMacro
                  && (   !pMacro->fFunction
                      || vbcppMacroExpandLookForLeftParenthesis(pThis, pExp, &off)) )
              {
                  cReplacements++;
                  rcExit = vbcppMacroExpandIt(pThis, pExp, offDefine, pMacro, off);
                  off = offDefine;
              }
              else
              {
                  if (   !pMacro
                           && enmMode == kMacroReScanMode_Expression
                           && cchDefine == sizeof("defined") - 1
                           && !strncmp(&pExp->StrBuf.pszBuf[offDefine], "defined", cchDefine))
                  {
                      cReplacements++;
                      rcExit = vbcppMacroExpandDefinedOperator(pThis, pExp, offDefine, &off);
                  }
                  else
                      off = offDefine + cchDefine;
              }
          }
          else
          {
              Assert(RT_C_IS_SPACE(ch) || RT_C_IS_PUNCT(ch));
              Assert(ch != '\r' && ch != '\n');
          }
      }
  
      if (pcReplacements)
          *pcReplacements = cReplacements;
      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.
   */
  static void vbcppMacroExpandCleanup(PVBCPPMACROEXP pExp)
  {
  #if 0
      while (pExp->pMacroStack)
      {
          PVBCPPMACRO pMacro = pExp->pMacroStack;
          pExp->pMacroStack = pMacro->pUpExpanding;
  
          pMacro->fExpanding   = false;
          pMacro->pUpExpanding = NULL;
      }
  #endif
  
      while (pExp->cArgs > 0)
      {
          RTMemFree(pExp->papszArgs[--pExp->cArgs]);
          pExp->papszArgs[pExp->cArgs] = NULL;
      }
  
      RTMemFree(pExp->papszArgs);
      pExp->papszArgs = NULL;
  
      vbcppStrBufDelete(&pExp->StrBuf);
  }
  
  
  
  /**
   * Frees a define.
   *
   * @returns VINF_SUCCESS (used when called by RTStrSpaceDestroy)
   * @param   pStr                Pointer to the VBCPPMACRO::Core member.
   * @param   pvUser              Unused.
   */
  static DECLCALLBACK(int) vbcppMacroFree(PRTSTRSPACECORE pStr, void *pvUser)
  {
      RTMemFree(pStr);
      NOREF(pvUser);
      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)
  {
      PRTSTRSPACECORE pHit = RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine);
      if (pHit)
      {
          RTStrSpaceRemove(&pThis->StrSpace, pHit->pszString);
          vbcppMacroFree(pHit, NULL);
      }
  
      if (fExplicitUndef)
      {
          if (cchDefine == RTSTR_MAX)
              cchDefine = strlen(pszDefine);
  
          PRTSTRSPACECORE pStr = (PRTSTRSPACECORE)RTMemAlloc(sizeof(*pStr) + cchDefine + 1);
          if (!pStr)
              return vbcppError(pThis, "out of memory");
          char *pszDst = (char *)(pStr + 1);
          pStr->pszString = pszDst;
          memcpy(pszDst, pszDefine, cchDefine);
          pszDst[cchDefine] = '\0';
          if (!RTStrSpaceInsert(&pThis->UndefStrSpace, pStr))
              RTMemFree(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.
   */
  static RTEXITCODE vbcppMacroInsert(PVBCPP pThis, PVBCPPMACRO pMacro)
  {
      /*
       * Reject illegal macro names.
       */
      if (!strcmp(pMacro->Core.pszString, "defined"))
      {
          RTEXITCODE rcExit = vbcppError(pThis, "Cannot use '%s' as a macro name", pMacro->Core.pszString);
          vbcppMacroFree(&pMacro->Core, NULL);
          return rcExit;
      }
  
      /*
       * Ignore in source-file defines when doing selective preprocessing.
       */
      if (   !pThis->fRespectSourceDefines
          && !pMacro->fCmdLine)
      {
          /* Ignore*/
          vbcppMacroFree(&pMacro->Core, NULL);
          return RTEXITCODE_SUCCESS;
      }
  
      /*
       * Insert it and update the lead character hint bitmap.
       */
      if (RTStrSpaceInsert(&pThis->StrSpace, &pMacro->Core))
          VBCPP_BITMAP_SET(pThis->bmDefined, *pMacro->Core.pszString);
      else
      {
          /*
           * Duplicate. When doing selective D preprocessing, let the command
           * line take precendece.
           */
          PVBCPPMACRO pOld = (PVBCPPMACRO)RTStrSpaceGet(&pThis->StrSpace, pMacro->Core.pszString); Assert(pOld);
          if (   pThis->fAllowRedefiningCmdLineDefines
              || pMacro->fCmdLine == pOld->fCmdLine)
          {
              if (pMacro->fCmdLine)
                  RTMsgWarning("Redefining '%s'", pMacro->Core.pszString);
  
              RTStrSpaceRemove(&pThis->StrSpace, pOld->Core.pszString);
              vbcppMacroFree(&pOld->Core, NULL);
  
              bool fRc = RTStrSpaceInsert(&pThis->StrSpace, &pMacro->Core);
              Assert(fRc); NOREF(fRc);
          }
          else
          {
              RTMsgWarning("Ignoring redefinition of '%s'", pMacro->Core.pszString);
              vbcppMacroFree(&pMacro->Core, NULL);
          }
      }
  
      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.
   */
  static RTEXITCODE vbcppMacroAddFn(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
                                    const char *pszParams, size_t cchParams,
                                    const char *pszValue, size_t cchValue,
                                    bool fCmdLine)
  
  {
      Assert(RTStrNLen(pszDefine, cchDefine) == cchDefine);
      Assert(RTStrNLen(pszParams, cchParams) == cchParams);
      Assert(RTStrNLen(pszValue,  cchValue)  == cchValue);
  
      /*
       * Determin the number of arguments and how much space their names
       * requires.  Performing syntax validation while parsing.
       */
      uint32_t cchArgNames = 0;
      uint32_t cArgs       = 0;
      for (size_t off = 0; off < cchParams; off++)
      {
          /* Skip blanks and maybe one comma. */
          bool fIgnoreComma = cArgs != 0;
          while (off < cchParams)
          {
              if (!RT_C_IS_SPACE(pszParams[off]))
              {
                  if (pszParams[off] != ',' || !fIgnoreComma)
                  {
                      if (vbcppIsCIdentifierLeadChar(pszParams[off]))
                          break;
                      /** @todo variadic macros. */
                      return vbcppErrorPos(pThis, &pszParams[off], "Unexpected character");
                  }
                  fIgnoreComma = false;
              }
              off++;
          }
          if (off >= cchParams)
              break;
  
          /* Found and argument. First character is already validated. */
          cArgs++;
          cchArgNames += 2;
          off++;
          while (   off < cchParams
                 && vbcppIsCIdentifierChar(pszParams[off]))
              off++, cchArgNames++;
      }
  
      /*
       * Allocate a structure.
       */
      size_t    cbDef = RT_UOFFSETOF_DYN(VBCPPMACRO, szValue[cchValue + 1 + cchDefine + 1 + cchArgNames])
                      + sizeof(const char *) * cArgs;
      cbDef = RT_ALIGN_Z(cbDef, sizeof(const char *));
      PVBCPPMACRO pMacro  = (PVBCPPMACRO)RTMemAlloc(cbDef);
      if (!pMacro)
          return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
  
      char *pszDst = &pMacro->szValue[cchValue + 1];
      pMacro->Core.pszString = pszDst;
      memcpy(pszDst, pszDefine, cchDefine);
      pszDst += cchDefine;
      *pszDst++ = '\0';
      pMacro->fFunction   = true;
      pMacro->fVarArg     = false;
      pMacro->fCmdLine    = fCmdLine;
      pMacro->fExpanding  = false;
      pMacro->cArgs       = cArgs;
      pMacro->papszArgs   = (const char **)((uintptr_t)pMacro + cbDef - sizeof(const char *) * cArgs);
      VBCPP_BITMAP_EMPTY(pMacro->bmArgs);
      pMacro->cchValue    = cchValue;
      memcpy(pMacro->szValue, pszValue, cchValue);
      pMacro->szValue[cchValue] = '\0';
  
      /*
       * Set up the arguments.
       */
      uint32_t iArg = 0;
      for (size_t off = 0; off < cchParams; off++)
      {
          /* Skip blanks and maybe one comma. */
          bool fIgnoreComma = cArgs != 0;
          while (off < cchParams)
          {
              if (!RT_C_IS_SPACE(pszParams[off]))
              {
                  if (pszParams[off] != ',' || !fIgnoreComma)
                      break;
                  fIgnoreComma = false;
              }
              off++;
          }
          if (off >= cchParams)
              break;
  
          /* Found and argument. First character is already validated. */
          VBCPP_BITMAP_SET(pMacro->bmArgs, pszParams[off]);
          pMacro->papszArgs[iArg] = pszDst;
          do
          {
              *pszDst++ = pszParams[off++];
          } while (   off < cchParams
                   && vbcppIsCIdentifierChar(pszParams[off]));
          *pszDst++ = '\0';
          iArg++;
      }
      Assert((uintptr_t)pszDst <= (uintptr_t)pMacro->papszArgs);
  
      return vbcppMacroInsert(pThis, pMacro);
  }
  
  
  /**
   * 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.
   */
  static RTEXITCODE vbcppMacroAdd(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
                                  const char *pszValue, size_t cchValue, bool fCmdLine)
  {
      /*
       * We need the lengths. Trim the input.
       */
      if (cchDefine == RTSTR_MAX)
          cchDefine = strlen(pszDefine);
      while (cchDefine > 0 && RT_C_IS_SPACE(*pszDefine))
          pszDefine++, cchDefine--;
      while (cchDefine > 0 && RT_C_IS_SPACE(pszDefine[cchDefine - 1]))
          cchDefine--;
      if (!cchDefine)
          return vbcppErrorPos(pThis, pszDefine, "The define has no name");
  
      if (cchValue == RTSTR_MAX)
          cchValue = strlen(pszValue);
      while (cchValue > 0 && RT_C_IS_SPACE(*pszValue))
          pszValue++, cchValue--;
      while (cchValue > 0 && RT_C_IS_SPACE(pszValue[cchValue - 1]))
          cchValue--;
  
      /*
       * Arguments make the job a bit more annoying.  Handle that elsewhere
       */
      const char *pszParams = (const char *)memchr(pszDefine, '(', cchDefine);
      if (pszParams)
      {
          size_t cchParams = pszDefine + cchDefine - pszParams;
          cchDefine -= cchParams;
          if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
              return RTEXITCODE_FAILURE;
          if (pszParams[cchParams - 1] != ')')
              return vbcppErrorPos(pThis, pszParams + cchParams - 1, "Missing closing parenthesis");
          pszParams++;
          cchParams -= 2;
          return vbcppMacroAddFn(pThis, pszDefine, cchDefine, pszParams, cchParams, pszValue, cchValue, fCmdLine);
      }
  
      /*
       * Simple define, no arguments.
       */
      if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
          return RTEXITCODE_FAILURE;
  
      PVBCPPMACRO pMacro = (PVBCPPMACRO)RTMemAlloc(RT_UOFFSETOF_DYN(VBCPPMACRO, szValue[cchValue + 1 + cchDefine + 1]));
      if (!pMacro)
          return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
  
      pMacro->Core.pszString = &pMacro->szValue[cchValue + 1];
      memcpy((char *)pMacro->Core.pszString, pszDefine, cchDefine);
      ((char *)pMacro->Core.pszString)[cchDefine] = '\0';
      pMacro->fFunction   = false;
      pMacro->fVarArg     = false;
      pMacro->fCmdLine    = fCmdLine;
      pMacro->fExpanding  = false;
      pMacro->cArgs       = 0;
      pMacro->papszArgs   = NULL;
      VBCPP_BITMAP_EMPTY(pMacro->bmArgs);
      pMacro->cchValue    = cchValue;
      memcpy(pMacro->szValue, pszValue, cchValue);
      pMacro->szValue[cchValue] = '\0';
  
      return vbcppMacroInsert(pThis, 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.
   */
  static RTEXITCODE vbcppMacroTryConvertToInlineD(PVBCPP pThis, PVBCPPMACRO pMacro)
  {
      AssertReturn(pMacro, vbcppError(pThis, "Internal error"));
      if (pMacro->fFunction)
          return RTEXITCODE_SUCCESS;
  
      /*
       * Do some simple macro resolving. (Mostly to make x86.h work.)
       */
      const char *pszDefine = pMacro->Core.pszString;
      const char *pszValue  = pMacro->szValue;
      size_t      cchValue  = pMacro->cchValue;
  
      unsigned    i = 0;
      PVBCPPMACRO pMacro2;
      while (   i < 10
             && cchValue > 0
             && vbcppIsCIdentifierLeadChar(*pszValue)
             && (pMacro2 = vbcppMacroLookup(pThis, pszValue, cchValue)) != NULL
             && !pMacro2->fFunction )
      {
          pszValue = pMacro2->szValue;
          cchValue = pMacro2->cchValue;
          i++;
      }
  
      if (!pMacro->cchValue)
          return RTEXITCODE_SUCCESS;
  
  
      /*
       * A lone value?
       */
      ssize_t  cch = 0;
      uint64_t u64;
      char    *pszNext;
      int rc = RTStrToUInt64Ex(pszValue, &pszNext, 0, &u64);
      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)
          {
              if (!strcmp(pszNext, "u") || !strcmp(pszNext, "U"))
                  pszType = "uint32_t";
              else if (!strcmp(pszNext, "ul") || !strcmp(pszNext, "UL"))
                  pszType = "uintptr_t";
              else if (!strcmp(pszNext, "ull") || !strcmp(pszNext, "ULL"))
                  pszType = "uint64_t";
              else
                  pszType = NULL;
          }
          else if (u64 <= UINT8_MAX)
              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;
          cch = ScmStreamPrintf(&pThis->StrmOutput, "inline %s %s = %.*s;\n",
                                pszType, pszDefine, pszNext - pszValue, pszValue);
      }
      /*
       * A value wrapped in a constant macro?
       */
      else if (   (pszNext = (char *)strchr(pszValue, '(')) != NULL
               && pszValue[cchValue - 1] == ')' )
      {
          size_t      cchPrefix = pszNext - pszValue;
          size_t      cchInnerValue  = cchValue - cchPrefix - 2;
          const char *pchInnerValue  = &pszValue[cchPrefix + 1];
          while (cchInnerValue > 0 && RT_C_IS_SPACE(*pchInnerValue))
              cchInnerValue--, pchInnerValue++;
          while (cchInnerValue > 0 && RT_C_IS_SPACE(pchInnerValue[cchInnerValue - 1]))
              cchInnerValue--;
          if (!cchInnerValue || !RT_C_IS_XDIGIT(*pchInnerValue))
              return RTEXITCODE_SUCCESS;
  
          rc = RTStrToUInt64Ex(pchInnerValue, &pszNext, 0, &u64);
          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
              pszType = NULL;
          if (pszType)
              cch = ScmStreamPrintf(&pThis->StrmOutput, "inline %s %s = %.*s;\n",
                                    pszType, pszDefine, cchInnerValue, pchInnerValue);
          else if (MY_MATCH_STR("RT_BIT") || MY_MATCH_STR("RT_BIT_32"))
              cch = ScmStreamPrintf(&pThis->StrmOutput, "inline uint32_t %s = 1U << %llu;\n",
                                    pszDefine, u64);
          else if (MY_MATCH_STR("RT_BIT_64"))
              cch = ScmStreamPrintf(&pThis->StrmOutput, "inline uint64_t %s = 1ULL << %llu;\n",
                                    pszDefine, u64);
          else
              return RTEXITCODE_SUCCESS;
  #undef MY_MATCH_STR
      }
      /* Dunno what this is... */
      else
          return RTEXITCODE_SUCCESS;
  
      /*
       * Check for output error and clear the output suppression indicator.
       */
      if (cch < 0)
          return vbcppError(pThis, "Output error");
  
      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).
   */
  static RTEXITCODE vbcppDirectiveDefine(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
  {
      RT_NOREF_PV(offStart);
  
      /*
       * Parse it.
       */
      RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          size_t      cchDefine;
          const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
          if (pchDefine)
          {
              /* If it's a function style define, parse out the parameter list. */
              size_t      cchParams = 0;
              const char *pchParams = NULL;
              unsigned    ch = ScmStreamPeekCh(pStrmInput);
              if (ch == '(')
              {
                  ScmStreamGetCh(pStrmInput);
                  pchParams = ScmStreamGetCur(pStrmInput);
  
                  unsigned chPrev = ch;
                  while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
                  {
                      if (ch == '\r' || ch == '\n')
                      {
                          if (chPrev != '\\')
                          {
                              rcExit = vbcppError(pThis, "Missing ')'");
                              break;
                          }
                          ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
                      }
                      if (ch == ')')
                      {
                          cchParams = ScmStreamGetCur(pStrmInput) - pchParams;
                          ScmStreamGetCh(pStrmInput);
                          break;
                      }
                      ScmStreamGetCh(pStrmInput);
                  }
              }
              /* The simple kind. */
              else if (!RT_C_IS_SPACE(ch) && ch != ~(unsigned)0)
                  rcExit = vbcppError(pThis, "Expected whitespace after macro name");
  
              /* Parse out the value. */
              if (rcExit == RTEXITCODE_SUCCESS)
                  rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
              if (rcExit == RTEXITCODE_SUCCESS)
              {
                  size_t      offValue = ScmStreamTell(pStrmInput);
                  const char *pchValue = ScmStreamGetCur(pStrmInput);
                  unsigned    chPrev = ch;
                  while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
                  {
                      if (ch == '\r' || ch == '\n')
                      {
                          if (chPrev != '\\')
                              break;
                          ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
                      }
                      chPrev = ScmStreamGetCh(pStrmInput);
                  }
                  size_t cchValue = ScmStreamGetCur(pStrmInput) - pchValue;
  
                  /*
                   * Execute.
                   */
                  if (pchParams)
                      rcExit = vbcppMacroAddFn(pThis, pchDefine, cchDefine, pchParams, cchParams, pchValue, cchValue, false);
                  else
                      rcExit = vbcppMacroAdd(pThis, pchDefine, cchDefine, pchValue, cchValue, false);
  
                  /*
                   * Pass thru?
                   */
                  if (   rcExit == RTEXITCODE_SUCCESS
                      && pThis->fPassThruDefines)
                  {
                      unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
                      ssize_t  cch;
                      if (pchParams)
                          cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s(%.*s)",
                                                cchIndent, "", cchDefine, pchDefine, cchParams, pchParams);
                      else
                          cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s",
                                                cchIndent, "", cchDefine, pchDefine);
                      if (cch > 0)
                          vbcppOutputComment(pThis, pStrmInput, offValue, cch, 1);
                      else
                          rcExit = vbcppError(pThis, "output error");
                  }
                  else if (   rcExit == RTEXITCODE_SUCCESS
                           && pThis->enmMode == kVBCppMode_SelectiveD)
                      rcExit = vbcppMacroTryConvertToInlineD(pThis, vbcppMacroLookup(pThis, pchDefine, cchDefine));
                  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).
   */
  static RTEXITCODE vbcppDirectiveUndef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
  {
      RT_NOREF_PV(offStart);
  
      /*
       * Parse it.
       */
      RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          size_t      cchDefine;
          const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
          if (pchDefine)
          {
              size_t offMaybeComment = vbcppProcessSkipWhite(pStrmInput);
              rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
              if (rcExit == RTEXITCODE_SUCCESS)
              {
                  /*
                   * Take action.
                   */
                  PVBCPPMACRO pMacro = vbcppMacroLookup(pThis, pchDefine, cchDefine);
                  if (    pMacro
                      &&  pThis->fRespectSourceDefines
                      &&  (   !pMacro->fCmdLine
                           || pThis->fAllowRedefiningCmdLineDefines ) )
                  {
                      RTStrSpaceRemove(&pThis->StrSpace, pMacro->Core.pszString);
                      vbcppMacroFree(&pMacro->Core, NULL);
                  }
  
                  /*
                   * Pass thru.
                   */
                  if (   rcExit == RTEXITCODE_SUCCESS
                      && pThis->fPassThruDefines)
                  {
                      unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
                      ssize_t  cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sundef %.*s",
                                                     cchIndent, "", cchDefine, pchDefine);
                      if (cch > 0)
                          vbcppOutputComment(pThis, pStrmInput, offMaybeComment, cch, 1);
                      else
                          rcExit = vbcppError(pThis, "output error");
                  }
  
              }
          }
          else
              rcExit = vbcppError(pThis, "Malformed #ifndef");
      }
      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.
   */
  static VBCPPEVAL vbcppCondCombine(VBCPPEVAL enmEvalPush, VBCPPEVAL enmEvalStack)
  {
      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.
   */
  static RTEXITCODE vbcppCondPush(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart,
                                  VBCPPCONDKIND enmKind, VBCPPEVAL enmResult,
                                  const char *pchCondition, size_t cchCondition)
  {
      RT_NOREF_PV(offStart); RT_NOREF_PV(pStrmInput);
  
  
      if (pThis->cCondStackDepth >= _64K)
          return vbcppError(pThis, "Too many nested #if/#ifdef/#ifndef statements");
  
      /*
       * Allocate a new entry and push it.
       */
      PVBCPPCOND pCond = (PVBCPPCOND)RTMemAlloc(sizeof(*pCond));
      if (!pCond)
          return vbcppError(pThis, "out of memory");
  
      PVBCPPCOND pUp = pThis->pCondStack;
      pCond->enmKind          = enmKind;
      pCond->enmResult        = enmResult;
      pCond->enmStackResult   = pUp ? vbcppCondCombine(enmResult, pUp->enmStackResult) : enmResult;
      pCond->fSeenElse        = false;
      pCond->fElIfDecided     = enmResult == kVBCppEval_True;
      pCond->iLevel           = pThis->cCondStackDepth;
      pCond->iKeepLevel       = (pUp ? pUp->iKeepLevel : 0) + enmResult == kVBCppEval_Undecided;
      pCond->pchCond          = pchCondition;
      pCond->cchCond          = cchCondition;
  
      pCond->pUp              = pThis->pCondStack;
      pThis->pCondStack       = pCond;
      pThis->fIf0Mode         = pCond->enmStackResult == kVBCppEval_False;
  
      /*
       * Do pass thru.
       */
      if (   !pThis->fIf0Mode
          && enmResult == kVBCppEval_Undecided)
      {
          /** @todo this is stripping comments of \#ifdef and \#ifndef atm. */
          const char *pszDirective;
          switch (enmKind)
          {
              case kVBCppCondKind_If:     pszDirective = "if"; break;
              case kVBCppCondKind_IfDef:  pszDirective = "ifdef"; break;
              case kVBCppCondKind_IfNDef: pszDirective = "ifndef"; break;
              case kVBCppCondKind_ElIf:   pszDirective = "elif"; break;
              default: AssertFailedReturn(RTEXITCODE_FAILURE);
          }
          ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*s%s %.*s",
                                        pCond->iKeepLevel - 1, "", pszDirective, cchCondition, pchCondition);
          if (cch < 0)
              return vbcppError(pThis, "Output error %Rrc", (int)cch);
      }
      else
          pThis->fJustDroppedLine = true;
  
      return RTEXITCODE_SUCCESS;
  }
  
  
  /**
   * Recursively destroys the expression tree.
   *
   * @param   pExpr               The root of the expression tree to destroy.
   */
  static void vbcppExprDestoryTree(PVBCPPEXPR pExpr)
  {
      if (!pExpr)
          return;
  
      switch (pExpr->enmKind)
      {
          case kVBCppExprKind_Unary:
              vbcppExprDestoryTree(pExpr->u.Unary.pArg);
              break;
          case kVBCppExprKind_Binary:
              vbcppExprDestoryTree(pExpr->u.Binary.pLeft);
              vbcppExprDestoryTree(pExpr->u.Binary.pRight);
              break;
          case kVBCppExprKind_Ternary:
              vbcppExprDestoryTree(pExpr->u.Ternary.pExpr);
              vbcppExprDestoryTree(pExpr->u.Ternary.pExpr);
              vbcppExprDestoryTree(pExpr->u.Ternary.pFalse);
              break;
          case kVBCppExprKind_SignedValue:
          case kVBCppExprKind_UnsignedValue:
              break;
          default:
              AssertFailed();
              return;
      }
      RTMemFree(pExpr);
  }
  
  
  /**
   * Report error during expression parsing.
   *
   * @returns kExprRet_Error
   * @param   pParser             The parser instance.
   * @param   pszMsg              The error message.
   * @param   ...                 Format arguments.
   */
  static VBCPPEXPRRET vbcppExprParseError(PVBCPPEXPRPARSER pParser, const char *pszMsg, ...)
  {
      va_list va;
      va_start(va, pszMsg);
      vbcppErrorV(pParser->pThis, pszMsg, va);
      va_end(va);
      return kExprRet_Error;
  }
  
  
  /**
   * Skip white space.
   *
   * @param   pParser             The parser instance.
   */
  static void vbcppExprParseSkipWhiteSpace(PVBCPPEXPRPARSER pParser)
  {
      while (RT_C_IS_SPACE(*pParser->pszCur))
          pParser->pszCur++;
  }
  
  
  /**
   * Allocate a new
   *
   * @returns Pointer to the node. NULL+msg on failure.
   * @param   pParser             The parser instance.
   */
  static PVBCPPEXPR vbcppExprParseAllocNode(PVBCPPEXPRPARSER pParser)
  {
      PVBCPPEXPR pExpr = (PVBCPPEXPR)RTMemAllocZ(sizeof(*pExpr));
      if (!pExpr)
          vbcppExprParseError(pParser, "out of memory (expression node)");
      return pExpr;
  }
  
  
  /**
   * Looks for right parentheses and/or end of expression.
   *
   * @returns Expression status.
   * @retval  kExprRet_Ok
   * @retval  kExprRet_Error with msg.
   * @retval  kExprRet_EndOfExpr
   * @param   pParser             The parser instance.
   */
  static VBCPPEXPRRET vbcppExprParseMaybeRParenOrEoe(PVBCPPEXPRPARSER pParser)
  {
      Assert(!pParser->ppCur);
      for (;;)
      {
          vbcppExprParseSkipWhiteSpace(pParser);
          char ch = *pParser->pszCur;
          if (ch == '\0')
              return kExprRet_EndOfExpr;
          if (ch != ')')
              break;
          pParser->pszCur++;
  
          PVBCPPEXPR pCur = pParser->pCur;
          while (   pCur
                 && (   pCur->enmKind != kVBCppExprKind_Unary
                     || pCur->u.Unary.enmOperator != kVBCppUnaryOp_Parenthesis))
          {
              switch (pCur->enmKind)
              {
                  case kVBCppExprKind_SignedValue:
                  case kVBCppExprKind_UnsignedValue:
                      Assert(pCur->fComplete);
                      break;
                  case kVBCppExprKind_Unary:
                      AssertReturn(pCur->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
                      pCur->fComplete = true;
                      break;
                  case kVBCppExprKind_Binary:
                      AssertReturn(pCur->u.Binary.pLeft, vbcppExprParseError(pParser, "internal error"));
                      AssertReturn(pCur->u.Binary.pRight, vbcppExprParseError(pParser, "internal error"));
                      pCur->fComplete = true;
                      break;
                  case kVBCppExprKind_Ternary:
  #if 1 /** @todo Check out the ternary operator implementation. */
                      return vbcppExprParseError(pParser, "The ternary operator is not implemented");
  #else
                      Assert(pCur->u.Ternary.pExpr);
                      if (!pCur->u.Ternary.pTrue)
                          return vbcppExprParseError(pParser, "?!?!?");
                      if (!pCur->u.Ternary.pFalse)
                          return vbcppExprParseError(pParser, "?!?!?!?");
                      pCur->fComplete = true;
  #endif
                      break;
                  default:
                      return vbcppExprParseError(pParser, "Internal error (enmKind=%d)", pCur->enmKind);
              }
              pCur = pCur->pParent;
          }
          if (!pCur)
              return vbcppExprParseError(pParser, "Right parenthesis without a left one");
          pCur->fComplete = true;
  
          while (   pCur->enmKind == kVBCppExprKind_Unary
                 && pCur->u.Unary.enmOperator != kVBCppUnaryOp_Parenthesis
                 && pCur->pParent)
          {
              AssertReturn(pCur->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
              pCur->fComplete = true;
              pCur = pCur->pParent;
          }
      }
  
      return kExprRet_Ok;
  }
  
  
  /**
   * Parses an binary operator.
   *
   * @returns Expression status.
   * @retval  kExprRet_Ok
   * @retval  kExprRet_Error with msg.
   * @param   pParser             The parser instance.
   */
  static VBCPPEXPRRET vbcppExprParseBinaryOperator(PVBCPPEXPRPARSER pParser)
  {
      /*
       * Binary or ternary operator should follow now.
       */
      VBCPPBINARYOP enmOp;
      char ch = *pParser->pszCur;
      switch (ch)
      {
          case '*':
              if (pParser->pszCur[1] == '=')
                  return vbcppExprParseError(pParser, "The assignment by product operator is not valid in a preprocessor expression");
              enmOp = kVBCppBinary_Multiplication;
              break;
          case '/':
              if (pParser->pszCur[1] == '=')
                  return vbcppExprParseError(pParser, "The assignment by quotient operator is not valid in a preprocessor expression");
              enmOp = kVBCppBinary_Division;
              break;
          case '%':
              if (pParser->pszCur[1] == '=')
                  return vbcppExprParseError(pParser, "The assignment by remainder operator is not valid in a preprocessor expression");
              enmOp = kVBCppBinary_Modulo;
              break;
          case '+':
              if (pParser->pszCur[1] == '=')
                  return vbcppExprParseError(pParser, "The assignment by sum operator is not valid in a preprocessor expression");
              enmOp = kVBCppBinary_Addition;
              break;
          case '-':
              if (pParser->pszCur[1] == '=')
                  return vbcppExprParseError(pParser, "The assignment by difference operator is not valid in a preprocessor expression");
              enmOp = kVBCppBinary_Subtraction;
              break;
          case '<':
              enmOp = kVBCppBinary_LessThan;
              if (pParser->pszCur[1] == '=')
              {
                  pParser->pszCur++;
                  enmOp = kVBCppBinary_LessThanOrEqual;
              }
              else if (pParser->pszCur[1] == '<')
              {
                  pParser->pszCur++;
                  if (pParser->pszCur[1] == '=')
                      return vbcppExprParseError(pParser, "The assignment by bitwise left shift operator is not valid in a preprocessor expression");
                  enmOp = kVBCppBinary_LeftShift;
              }
              break;
          case '>':
              enmOp = kVBCppBinary_GreaterThan;
              if (pParser->pszCur[1] == '=')
              {
                  pParser->pszCur++;
                  enmOp = kVBCppBinary_GreaterThanOrEqual;
              }
              else if (pParser->pszCur[1] == '<')
              {
                  pParser->pszCur++;
                  if (pParser->pszCur[1] == '=')
                      return vbcppExprParseError(pParser, "The assignment by bitwise right shift operator is not valid in a preprocessor expression");
                  enmOp = kVBCppBinary_LeftShift;
              }
              break;
          case '=':
              if (pParser->pszCur[1] != '=')
                  return vbcppExprParseError(pParser, "The assignment operator is not valid in a preprocessor expression");
              pParser->pszCur++;
              enmOp = kVBCppBinary_EqualTo;
              break;
  
          case '!':
              if (pParser->pszCur[1] != '=')
                  return vbcppExprParseError(pParser, "Expected binary operator, found the unary operator logical NOT");
              pParser->pszCur++;
              enmOp = kVBCppBinary_NotEqualTo;
              break;
  
          case '&':
              if (pParser->pszCur[1] == '=')
                  return vbcppExprParseError(pParser, "The assignment by bitwise AND operator is not valid in a preprocessor expression");
              if (pParser->pszCur[1] == '&')
              {
                  pParser->pszCur++;
                  enmOp = kVBCppBinary_LogicalAnd;
              }
              else
                  enmOp = kVBCppBinary_BitwiseAnd;
              break;
          case '^':
              if (pParser->pszCur[1] == '=')
                  return vbcppExprParseError(pParser, "The assignment by bitwise XOR operator is not valid in a preprocessor expression");
              enmOp = kVBCppBinary_BitwiseXor;
              break;
          case '|':
              if (pParser->pszCur[1] == '=')
                  return vbcppExprParseError(pParser, "The assignment by bitwise AND operator is not valid in a preprocessor expression");
              if (pParser->pszCur[1] == '|')
              {
                  pParser->pszCur++;
                  enmOp = kVBCppBinary_LogicalOr;
              }
              else
                  enmOp = kVBCppBinary_BitwiseOr;
              break;
          case '~':
              return vbcppExprParseError(pParser, "Expected binary operator, found the unary operator bitwise NOT");
  
          case ':':
          case '?':
              return vbcppExprParseError(pParser, "The ternary operator is not yet implemented");
  
          default:
              return vbcppExprParseError(pParser, "Expected binary operator, found '%.20s'", pParser->pszCur);
      }
      pParser->pszCur++;
  
      /*
       * Create a binary operator node.
       */
      PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
      if (!pExpr)
          return kExprRet_Error;
      pExpr->fComplete            = true;
      pExpr->enmKind              = kVBCppExprKind_Binary;
      pExpr->u.Binary.enmOperator = enmOp;
      pExpr->u.Binary.pLeft       = NULL;
      pExpr->u.Binary.pRight      = NULL;
  
      /*
       * Back up the tree until we find our spot.
       */
      PVBCPPEXPR *ppPlace = NULL;
      PVBCPPEXPR  pChild  = NULL;
      PVBCPPEXPR  pParent = pParser->pCur;
      while (pParent)
      {
          if (pParent->enmKind == kVBCppExprKind_Unary)
          {
              if (pParent->u.Unary.enmOperator == kVBCppUnaryOp_Parenthesis)
              {
                  ppPlace = &pParent->u.Unary.pArg;
                  break;
              }
              AssertReturn(pParent->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
              pParent->fComplete = true;
          }
          else if (pParent->enmKind == kVBCppExprKind_Binary)
          {
              AssertReturn(pParent->u.Binary.pLeft, vbcppExprParseError(pParser, "internal error"));
              AssertReturn(pParent->u.Binary.pRight, vbcppExprParseError(pParser, "internal error"));
              if ((pParent->u.Binary.enmOperator & VBCPPOP_PRECEDENCE_MASK) >= (enmOp & VBCPPOP_PRECEDENCE_MASK))
              {
                  AssertReturn(pChild, vbcppExprParseError(pParser, "internal error"));
  
                  if (pParent->u.Binary.pRight == pChild)
                      ppPlace = &pParent->u.Binary.pRight;
                  else
                      ppPlace = &pParent->u.Binary.pLeft;
                  AssertReturn(*ppPlace == pChild, vbcppExprParseError(pParser, "internal error"));
                  break;
              }
              pParent->fComplete = true;
          }
          else if (pParent->enmKind == kVBCppExprKind_Ternary)
          {
              return vbcppExprParseError(pParser, "The ternary operator is not implemented");
          }
          else
              AssertReturn(   pParent->enmKind == kVBCppExprKind_SignedValue
                           || pParent->enmKind == kVBCppExprKind_UnsignedValue,
                           vbcppExprParseError(pParser, "internal error"));
  
          /* Up on level */
          pChild  = pParent;
          pParent = pParent->pParent;
      }
  
      /*
       * Do the rotation.
       */
      Assert(pChild);
      Assert(pChild->pParent == pParent);
      pChild->pParent       = pExpr;
  
      pExpr->u.Binary.pLeft = pChild;
      pExpr->pParent        = pParent;
  
      if (!pParent)
          pParser->pRoot    = pExpr;
      else
          *ppPlace          = pExpr;
  
      pParser->ppCur = &pExpr->u.Binary.pRight;
      pParser->pCur  = pExpr;
  
      return kExprRet_Ok;
  }
  
  
  /**
   * Deals with right paretheses or/and end of expression, looks for binary
   * operators.
   *
   * @returns Expression status.
   * @retval  kExprRet_Ok if binary operator was found processed.
   * @retval  kExprRet_Error with msg.
   * @retval  kExprRet_EndOfExpr
   * @param   pParser             The parser instance.
   */
  static VBCPPEXPRRET vbcppExprParseBinaryOrEoeOrRparen(PVBCPPEXPRPARSER pParser)
  {
      VBCPPEXPRRET enmRet = vbcppExprParseMaybeRParenOrEoe(pParser);
      if (enmRet != kExprRet_Ok)
          return enmRet;
      return vbcppExprParseBinaryOperator(pParser);
  }
  
  
  /**
   * Parses an identifier in the expression, replacing it by 0.
   *
   * All known identifiers has already been replaced by their macro values, so
   * what's left are unknown macros.  These are replaced by 0.
   *
   * @returns Expression status.
   * @retval  kExprRet_Value
   * @retval  kExprRet_Error with msg.
   * @param   pParser             The parser instance.
   */
  static VBCPPEXPRRET vbcppExprParseIdentifier(PVBCPPEXPRPARSER pParser)
  {
  /** @todo don't increment if it's an actively undefined macro. Need to revise
   *        the expression related code wrt selective preprocessing. */
      pParser->cUndefined++;
  
      /* Find the end. */
      const char *pszMacro = pParser->pszCur;
      const char *pszNext  = pszMacro + 1;
      while (vbcppIsCIdentifierChar(*pszNext))
          pszNext++;
      size_t cchMacro = pszNext - pszMacro;
  
      /* Create a signed value node. */
      PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
      if (!pExpr)
          return kExprRet_Error;
      pExpr->fComplete            = true;
      pExpr->enmKind              = kVBCppExprKind_UnsignedValue;
      pExpr->u.UnsignedValue.u64  = 0;
  
      /* Link it. */
      pExpr->pParent              = pParser->pCur;
      pParser->pCur               = pExpr;
      *pParser->ppCur             = pExpr;
      pParser->ppCur              = NULL;
  
      /* Skip spaces and check for parenthesis. */
      pParser->pszCur = pszNext;
      vbcppExprParseSkipWhiteSpace(pParser);
      if (*pParser->pszCur == '(')
          return vbcppExprParseError(pParser, "Unknown unary operator '%.*s'", cchMacro, pszMacro);
  
      return kExprRet_Value;
  }
  
  
  /**
   * Parses an numeric constant in the expression.
   *
   * @returns Expression status.
   * @retval  kExprRet_Value
   * @retval  kExprRet_Error with msg.
   * @param   pParser             The parser instance.
   */
  static VBCPPEXPRRET vbcppExprParseNumber(PVBCPPEXPRPARSER pParser)
  {
      bool        fSigned;
      char       *pszNext;
      uint64_t    u64;
      char        ch    = *pParser->pszCur++;
      char        ch2   = *pParser->pszCur;
      if (   ch == '0'
          && (ch == 'x' || ch == 'X'))
      {
          ch2 = *++pParser->pszCur;
          if (!RT_C_IS_XDIGIT(ch2))
              return vbcppExprParseError(pParser, "Expected hex digit following '0x'");
          int rc = RTStrToUInt64Ex(pParser->pszCur, &pszNext, 16, &u64);
          if (   RT_FAILURE(rc)
              || rc == VWRN_NUMBER_TOO_BIG)
              return vbcppExprParseError(pParser, "Invalid hex value '%.20s...' (%Rrc)", pParser->pszCur, rc);
          fSigned = false;
      }
      else if (ch == '0')
      {
          int rc = RTStrToUInt64Ex(pParser->pszCur - 1, &pszNext, 8, &u64);
          if (   RT_FAILURE(rc)
              || rc == VWRN_NUMBER_TOO_BIG)
              return vbcppExprParseError(pParser, "Invalid octal value '%.20s...' (%Rrc)", pParser->pszCur, rc);
          fSigned = u64 > (uint64_t)INT64_MAX ? false : true;
      }
      else
      {
          int rc = RTStrToUInt64Ex(pParser->pszCur - 1, &pszNext, 10, &u64);
          if (   RT_FAILURE(rc)
              || rc == VWRN_NUMBER_TOO_BIG)
              return vbcppExprParseError(pParser, "Invalid decimal value '%.20s...' (%Rrc)", pParser->pszCur, rc);
          fSigned = u64 > (uint64_t)INT64_MAX ? false : true;
      }
  
      /* suffix. */
      if (vbcppIsCIdentifierLeadChar(*pszNext))
      {
          size_t cchSuffix = 1;
          while (vbcppIsCIdentifierLeadChar(pszNext[cchSuffix]))
              cchSuffix++;
  
          if (cchSuffix == '1' && (*pszNext == 'u' || *pszNext == 'U'))
              fSigned = false;
          else if (   cchSuffix == '1'
                   && (*pszNext == 'l' || *pszNext == 'L'))
              fSigned = true;
          else if (   cchSuffix == '2'
                   && (!strncmp(pszNext, "ul", 2) || !strncmp(pszNext, "UL", 2)))
              fSigned = false;
          else if (   cchSuffix == '2'
                   && (!strncmp(pszNext, "ll", 2) || !strncmp(pszNext, "LL", 2)))
              fSigned = true;
          else if (   cchSuffix == '3'
                   && (!strncmp(pszNext, "ull", 3) || !strncmp(pszNext, "ULL", 3)))
              fSigned = false;
          else
              return vbcppExprParseError(pParser, "Invalid number suffix '%.*s'", cchSuffix, pszNext);
  
          pszNext += cchSuffix;
      }
      pParser->pszCur = pszNext;
  
      /* Create a signed value node. */
      PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
      if (!pExpr)
          return kExprRet_Error;
      pExpr->fComplete            = true;
      if (fSigned)
      {
          pExpr->enmKind              = kVBCppExprKind_SignedValue;
          pExpr->u.SignedValue.s64    = (int64_t)u64;
      }
      else
      {
          pExpr->enmKind              = kVBCppExprKind_UnsignedValue;
          pExpr->u.UnsignedValue.u64  = u64;
      }
  
      /* Link it. */
      pExpr->pParent              = pParser->pCur;
      pParser->pCur               = pExpr;
      *pParser->ppCur             = pExpr;
      pParser->ppCur              = NULL;
  
      return kExprRet_Value;
  }
  
  
  /**
   * Parses an character constant in the expression.
   *
   * @returns Expression status.
   * @retval  kExprRet_Value
   * @retval  kExprRet_Error with msg.
   * @param   pParser             The parser instance.
   */
  static VBCPPEXPRRET vbcppExprParseCharacterConstant(PVBCPPEXPRPARSER pParser)
  {
      Assert(*pParser->pszCur == '\'');
      pParser->pszCur++;
      char ch2 = *pParser->pszCur++;
      if (ch2 == '\'')
          return vbcppExprParseError(pParser, "Empty character constant");
      int64_t s64;
      if (ch2 == '\\')
      {
          ch2 = *pParser->pszCur++;
          switch (ch2)
          {
              case '0': s64 = 0x00; break;
              case 'n': s64 = 0x0d; break;
              case 'r': s64 = 0x0a; break;
              case 't': s64 = 0x09; break;
              default:
                  return vbcppExprParseError(pParser, "Escape character '%c' is not implemented", ch2);
          }
      }
      else
          s64 = ch2;
      if (*pParser->pszCur != '\'')
          return vbcppExprParseError(pParser, "Character constant contains more than one character");
  
      /* Create a signed value node. */
      PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
      if (!pExpr)
          return kExprRet_Error;
      pExpr->fComplete            = true;
      pExpr->enmKind              = kVBCppExprKind_SignedValue;
      pExpr->u.SignedValue.s64    = s64;
  
      /* Link it. */
      pExpr->pParent              = pParser->pCur;
      pParser->pCur               = pExpr;
      *pParser->ppCur             = pExpr;
      pParser->ppCur              = NULL;
  
      return kExprRet_Value;
  }
  
  
  /**
   * Parses a unary operator or a value.
   *
   * @returns Expression status.
   * @retval  kExprRet_Value if value was found and processed.
   * @retval  kExprRet_UnaryOperator if an unary operator was found and processed.
   * @retval  kExprRet_Error with msg.
   * @param   pParser             The parser instance.
   */
  static VBCPPEXPRRET vbcppExprParseUnaryOrValue(PVBCPPEXPRPARSER pParser)
  {
      vbcppExprParseSkipWhiteSpace(pParser);
      char ch = *pParser->pszCur;
      if (ch == '\0')
          return vbcppExprParseError(pParser, "Premature end of expression");
  
      /*
       * Value?
       */
      if (ch == '\'')
          return vbcppExprParseCharacterConstant(pParser);
      if (RT_C_IS_DIGIT(ch))
          return vbcppExprParseNumber(pParser);
      if (ch == '"')
          return vbcppExprParseError(pParser, "String litteral");
      if (vbcppIsCIdentifierLeadChar(ch))
          return vbcppExprParseIdentifier(pParser);
  
      /*
       * Operator?
       */
      VBCPPUNARYOP enmOperator;
      if (ch == '+')
      {
          enmOperator = kVBCppUnaryOp_Pluss;
          if (pParser->pszCur[1] == '+')
              return vbcppExprParseError(pParser, "The prefix increment operator is not valid in a preprocessor expression");
      }
      else if (ch == '-')
      {
          enmOperator = kVBCppUnaryOp_Minus;
          if (pParser->pszCur[1] == '-')
              return vbcppExprParseError(pParser, "The prefix decrement operator is not valid in a preprocessor expression");
      }
      else if (ch == '!')
          enmOperator = kVBCppUnaryOp_LogicalNot;
      else if (ch == '~')
          enmOperator = kVBCppUnaryOp_BitwiseNot;
      else if (ch == '(')
          enmOperator = kVBCppUnaryOp_Parenthesis;
      else
          return vbcppExprParseError(pParser, "Unknown token '%.*s'", 32, pParser->pszCur - 1);
      pParser->pszCur++;
  
      /* Create an operator node. */
      PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
      if (!pExpr)
          return kExprRet_Error;
      pExpr->fComplete            = false;
      pExpr->enmKind              = kVBCppExprKind_Unary;
      pExpr->u.Unary.enmOperator  = enmOperator;
      pExpr->u.Unary.pArg         = NULL;
  
      /* Link it into the tree. */
      pExpr->pParent              = pParser->pCur;
      pParser->pCur               = pExpr;
      *pParser->ppCur             = pExpr;
      pParser->ppCur              = &pExpr->u.Unary.pArg;
  
      return kExprRet_UnaryOperator;
  }
  
  
  /**
   * Parses an expanded preprocessor expression.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
   * @param   pThis               The C preprocessor instance.
   * @param   pszExpr             The expression to parse.
   * @param   cchExpr             The length of the expression in case we need it.
   * @param   ppExprTree          Where to return the parse tree.
   * @param   pcUndefined         Where to return the number of unknown undefined
   *                              macros.  Optional.
   */
  static RTEXITCODE vbcppExprParse(PVBCPP pThis, char *pszExpr, size_t cchExpr, PVBCPPEXPR *ppExprTree, size_t *pcUndefined)
  {
      RTEXITCODE rcExit = RTEXITCODE_FAILURE;
      NOREF(cchExpr);
  
      /*
       * Initialize the parser context structure.
       */
      VBCPPEXPRPARSER Parser;
      Parser.pszCur       = pszExpr;
      Parser.pRoot        = NULL;
      Parser.pCur         = NULL;
      Parser.ppCur        = &Parser.pRoot;
      Parser.pszExpr      = pszExpr;
      Parser.cUndefined   = 0;
      Parser.pThis        = pThis;
  
      /*
       * Do the parsing.
       */
      VBCPPEXPRRET enmRet;
      for (;;)
      {
          /*
           * Eat unary operators until we hit a value.
           */
          do
              enmRet = vbcppExprParseUnaryOrValue(&Parser);
          while (enmRet == kExprRet_UnaryOperator);
          if (enmRet == kExprRet_Error)
              break;
          AssertBreakStmt(enmRet == kExprRet_Value, enmRet = vbcppExprParseError(&Parser, "Expected value (enmRet=%d)", enmRet));
  
          /*
           * Non-unary operator, right parenthesis or end of expression is up next.
           */
          enmRet = vbcppExprParseBinaryOrEoeOrRparen(&Parser);
          if (enmRet == kExprRet_Error)
              break;
          if (enmRet == kExprRet_EndOfExpr)
          {
              /** @todo check if there are any open parentheses. */
              rcExit = RTEXITCODE_SUCCESS;
              break;
          }
          AssertBreakStmt(enmRet == kExprRet_Ok, enmRet = vbcppExprParseError(&Parser, "Expected value (enmRet=%d)", enmRet));
      }
  
      if (rcExit != RTEXITCODE_SUCCESS)
      {
          vbcppExprDestoryTree(Parser.pRoot);
          return rcExit;
      }
  
      if (pcUndefined)
          *pcUndefined = Parser.cUndefined;
      *ppExprTree = Parser.pRoot;
      return rcExit;
  }
  
  
  /**
   * Checks if an expression value value is evaluates to @c true or @c false.
   *
   * @returns @c true or @c false.
   * @param   pExpr               The value expression.
   */
  static bool vbcppExprIsExprTrue(PVBCPPEXPR pExpr)
  {
      Assert(pExpr->enmKind == kVBCppExprKind_SignedValue || pExpr->enmKind == kVBCppExprKind_UnsignedValue);
  
      return pExpr->enmKind == kVBCppExprKind_SignedValue
           ? pExpr->u.SignedValue.s64   != 0
           : pExpr->u.UnsignedValue.u64 != 0;
  }
  
  
  /**
   * Evalutes a parse (sub-)tree.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
   * @param   pThis               The C preprocessor instance.
   * @param   pRoot               The root of the parse (sub-)tree.
   * @param   pResult             Where to store the result value.
   */
  static RTEXITCODE vbcppExprEvaluteTree(PVBCPP pThis, PVBCPPEXPR pRoot, PVBCPPEXPR pResult)
  {
      RTEXITCODE rcExit;
      switch (pRoot->enmKind)
      {
          case kVBCppExprKind_SignedValue:
              pResult->enmKind                = kVBCppExprKind_SignedValue;
              pResult->u.SignedValue.s64      = pRoot->u.SignedValue.s64;
              return RTEXITCODE_SUCCESS;
  
          case kVBCppExprKind_UnsignedValue:
              pResult->enmKind                = kVBCppExprKind_UnsignedValue;
              pResult->u.UnsignedValue.u64    = pRoot->u.UnsignedValue.u64;
              return RTEXITCODE_SUCCESS;
  
          case kVBCppExprKind_Unary:
              rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Unary.pArg, pResult);
              if (rcExit != RTEXITCODE_SUCCESS)
                  return rcExit;
  
              /* Apply the unary operator to the value */
              switch (pRoot->u.Unary.enmOperator)
              {
                  case kVBCppUnaryOp_Minus:
                      if (pResult->enmKind == kVBCppExprKind_SignedValue)
                          pResult->u.SignedValue.s64   = -pResult->u.SignedValue.s64;
                      else
                          pResult->u.UnsignedValue.u64 = (uint64_t)-(int64_t)pResult->u.UnsignedValue.u64;
                      break;
  
                  case kVBCppUnaryOp_LogicalNot:
                      if (pResult->enmKind == kVBCppExprKind_SignedValue)
                          pResult->u.SignedValue.s64   = !pResult->u.SignedValue.s64;
                      else
                          pResult->u.UnsignedValue.u64 = !pResult->u.UnsignedValue.u64;
                      break;
  
                  case kVBCppUnaryOp_BitwiseNot:
                      if (pResult->enmKind == kVBCppExprKind_SignedValue)
                          pResult->u.SignedValue.s64   = ~pResult->u.SignedValue.s64;
                      else
                          pResult->u.UnsignedValue.u64 = ~pResult->u.UnsignedValue.u64;
                      break;
  
                  case kVBCppUnaryOp_Pluss:
                  case kVBCppUnaryOp_Parenthesis:
                      /* do nothing. */
                      break;
  
                  default:
                      return vbcppError(pThis, "Internal error: u.Unary.enmOperator=%d", pRoot->u.Unary.enmOperator);
              }
              return RTEXITCODE_SUCCESS;
  
          case kVBCppExprKind_Binary:
          {
              /* Always evalute the left side. */
              rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Binary.pLeft, pResult);
              if (rcExit != RTEXITCODE_SUCCESS)
                  return rcExit;
  
              /* If logical AND or OR we can sometimes skip evaluting the right side. */
              if (   pRoot->u.Binary.enmOperator == kVBCppBinary_LogicalAnd
                  && !vbcppExprIsExprTrue(pResult))
                  return RTEXITCODE_SUCCESS;
  
              if (   pRoot->u.Binary.enmOperator == kVBCppBinary_LogicalOr
                  && vbcppExprIsExprTrue(pResult))
                  return RTEXITCODE_SUCCESS;
  
              /* Evalute the right side. */
              VBCPPEXPR Result2;
              rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Binary.pRight, &Result2);
              if (rcExit != RTEXITCODE_SUCCESS)
                  return rcExit;
  
              /* If one of them is unsigned, promote the other to unsigned as well. */
              if (   pResult->enmKind == kVBCppExprKind_UnsignedValue
                  && Result2.enmKind  == kVBCppExprKind_SignedValue)
              {
                  Result2.enmKind              = kVBCppExprKind_UnsignedValue;
                  Result2.u.UnsignedValue.u64  = Result2.u.SignedValue.s64;
              }
              else if (   pResult->enmKind == kVBCppExprKind_SignedValue
                       && Result2.enmKind  == kVBCppExprKind_UnsignedValue)
              {
                  pResult->enmKind             = kVBCppExprKind_UnsignedValue;
                  pResult->u.UnsignedValue.u64 = pResult->u.SignedValue.s64;
              }
  
              /* Perform the operation. */
              if (pResult->enmKind == kVBCppExprKind_UnsignedValue)
              {
                  switch (pRoot->u.Binary.enmOperator)
                  {
                      case kVBCppBinary_Multiplication:
                          pResult->u.UnsignedValue.u64 *= Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_Division:
                          if (!Result2.u.UnsignedValue.u64)
                              return vbcppError(pThis, "Divide by zero");
                          pResult->u.UnsignedValue.u64 /= Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_Modulo:
                          if (!Result2.u.UnsignedValue.u64)
                              return vbcppError(pThis, "Divide by zero");
                          pResult->u.UnsignedValue.u64 %= Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_Addition:
                          pResult->u.UnsignedValue.u64 += Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_Subtraction:
                          pResult->u.UnsignedValue.u64 -= Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_LeftShift:
                          pResult->u.UnsignedValue.u64 <<= Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_RightShift:
                          pResult->u.UnsignedValue.u64 >>= Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_LessThan:
                          pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 < Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_LessThanOrEqual:
                          pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 <= Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_GreaterThan:
                          pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 > Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_GreaterThanOrEqual:
                          pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 >= Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_EqualTo:
                          pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 == Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_NotEqualTo:
                          pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 != Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_BitwiseAnd:
                          pResult->u.UnsignedValue.u64 &= Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_BitwiseXor:
                          pResult->u.UnsignedValue.u64 ^= Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_BitwiseOr:
                          pResult->u.UnsignedValue.u64 |= Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_LogicalAnd:
                          pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 && Result2.u.UnsignedValue.u64;
                          break;
                      case kVBCppBinary_LogicalOr:
                          pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 || Result2.u.UnsignedValue.u64;
                          break;
                      default:
                          return vbcppError(pThis, "Internal error: u.Binary.enmOperator=%d", pRoot->u.Binary.enmOperator);
                  }
              }
              else
              {
                  switch (pRoot->u.Binary.enmOperator)
                  {
                      case kVBCppBinary_Multiplication:
                          pResult->u.SignedValue.s64 *= Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_Division:
                          if (!Result2.u.SignedValue.s64)
                              return vbcppError(pThis, "Divide by zero");
                          pResult->u.SignedValue.s64 /= Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_Modulo:
                          if (!Result2.u.SignedValue.s64)
                              return vbcppError(pThis, "Divide by zero");
                          pResult->u.SignedValue.s64 %= Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_Addition:
                          pResult->u.SignedValue.s64 += Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_Subtraction:
                          pResult->u.SignedValue.s64 -= Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_LeftShift:
                          pResult->u.SignedValue.s64 <<= Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_RightShift:
                          pResult->u.SignedValue.s64 >>= Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_LessThan:
                          pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 < Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_LessThanOrEqual:
                          pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 <= Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_GreaterThan:
                          pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 > Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_GreaterThanOrEqual:
                          pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 >= Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_EqualTo:
                          pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 == Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_NotEqualTo:
                          pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 != Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_BitwiseAnd:
                          pResult->u.SignedValue.s64 &= Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_BitwiseXor:
                          pResult->u.SignedValue.s64 ^= Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_BitwiseOr:
                          pResult->u.SignedValue.s64 |= Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_LogicalAnd:
                          pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 && Result2.u.SignedValue.s64;
                          break;
                      case kVBCppBinary_LogicalOr:
                          pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 || Result2.u.SignedValue.s64;
                          break;
                      default:
                          return vbcppError(pThis, "Internal error: u.Binary.enmOperator=%d", pRoot->u.Binary.enmOperator);
                  }
              }
              return rcExit;
          }
  
          case kVBCppExprKind_Ternary:
              rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pExpr, pResult);
              if (rcExit != RTEXITCODE_SUCCESS)
                  return rcExit;
              if (vbcppExprIsExprTrue(pResult))
                  return vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pTrue, pResult);
              return vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pFalse, pResult);
  
          default:
              return vbcppError(pThis, "Internal error: enmKind=%d", pRoot->enmKind);
      }
  }
  
  
  /**
   * 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.
   */
  static RTEXITCODE vbcppExprEval(PVBCPP pThis, char *pszExpr, size_t cchExpr, size_t cReplacements, VBCPPEVAL *penmResult)
  {
      Assert(strlen(pszExpr) == cchExpr);
      RT_NOREF_PV(cReplacements);
  
      size_t      cUndefined;
      PVBCPPEXPR  pExprTree;
      RTEXITCODE  rcExit = vbcppExprParse(pThis, pszExpr, cchExpr, &pExprTree, &cUndefined);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          if (   !cUndefined
              || pThis->enmMode == kVBCppMode_SelectiveD
              || pThis->enmMode == kVBCppMode_Standard)
          {
              VBCPPEXPR Result;
              rcExit = vbcppExprEvaluteTree(pThis, pExprTree, &Result);
              if (rcExit == RTEXITCODE_SUCCESS)
              {
                  if (vbcppExprIsExprTrue(&Result))
                      *penmResult = kVBCppEval_True;
                  else
                      *penmResult = kVBCppEval_False;
              }
          }
          else
              *penmResult = kVBCppEval_Undecided;
      }
      return rcExit;
  }
  
  
  static RTEXITCODE vbcppExtractSkipCommentLine(PVBCPP pThis, PSCMSTREAM pStrmInput)
  {
      RT_NOREF_PV(pThis);
  
      unsigned chPrev = ScmStreamGetCh(pStrmInput); Assert(chPrev == '/');
      unsigned ch;
      while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
      {
          if (ch == '\r' || ch == '\n')
          {
              if (chPrev != '\\')
                  break;
              ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
              chPrev = ch;
          }
          else
          {
              chPrev = ScmStreamGetCh(pStrmInput);
              Assert(chPrev == ch);
          }
      }
      return RTEXITCODE_SUCCESS;
  }
  
  
  static RTEXITCODE vbcppExtractSkipComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
  {
      unsigned ch = ScmStreamGetCh(pStrmInput); Assert(ch == '*');
      while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
      {
          if (ch == '*')
          {
              ch = ScmStreamGetCh(pStrmInput);
              if (ch == '/')
                  return RTEXITCODE_SUCCESS;
          }
      }
      return vbcppError(pThis, "Expected '*/'");
  }
  
  
  static RTEXITCODE vbcppExtractQuotedString(PVBCPP pThis, PSCMSTREAM pStrmInput, PVBCPPSTRBUF pStrBuf,
                                             char chOpen, char chClose)
  {
      unsigned ch = ScmStreamGetCh(pStrmInput);
      Assert(ch == (unsigned)chOpen);
  
      RTEXITCODE rcExit = vbcppStrBufAppendCh(pStrBuf, chOpen);
      if (rcExit != RTEXITCODE_SUCCESS)
          return rcExit;
  
      for (;;)
      {
          ch = ScmStreamGetCh(pStrmInput);
          if (ch == '\\')
          {
              ch = ScmStreamGetCh(pStrmInput);
              if (ch == ~(unsigned)0)
                  break;
              rcExit = vbcppStrBufAppendCh(pStrBuf, '\\');
              if (rcExit == RTEXITCODE_SUCCESS)
                  rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
              if (rcExit != RTEXITCODE_SUCCESS)
                  return rcExit;
          }
          else if (ch != ~(unsigned)0)
          {
              rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
              if (rcExit != RTEXITCODE_SUCCESS)
                  return rcExit;
              if (ch == (unsigned)chClose)
                  return RTEXITCODE_SUCCESS;
          }
          else
              break;
      }
  
      return vbcppError(pThis, "File ended with an open character constant");
  }
  
  
  /**
   * Extracts a line from the stream, stripping it for comments and maybe
   * optimzing some of the whitespace.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
   * @param   pThis               The C preprocessor instance.
   * @param   pStrmInput          The input stream.
   * @param   pStrBuf             Where to store the extracted line. Caller must
   *                              initialize this prior to the call an delete it
   *                              after use (even on failure).
   * @param   poffComment         Where to note down the position of the final
   *                              comment. Optional.
   */
  static RTEXITCODE vbcppExtractDirectiveLine(PVBCPP pThis, PSCMSTREAM pStrmInput, PVBCPPSTRBUF pStrBuf, size_t *poffComment)
  {
      size_t      offComment  = ~(size_t)0;
      unsigned    ch;
      while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
      {
          RTEXITCODE rcExit;
          if (ch == '/')
          {
              /* Comment? */
              unsigned ch2 = ScmStreamGetCh(pStrmInput); Assert(ch == ch2); NOREF(ch2);
              ch = ScmStreamPeekCh(pStrmInput);
              if (ch == '*')
              {
                  offComment = ScmStreamTell(pStrmInput) - 1;
                  rcExit = vbcppExtractSkipComment(pThis, pStrmInput);
              }
              else if (ch == '/')
              {
                  offComment = ScmStreamTell(pStrmInput) - 1;
                  rcExit = vbcppExtractSkipCommentLine(pThis, pStrmInput);
              }
              else
                  rcExit = vbcppStrBufAppendCh(pStrBuf, '/');
          }
          else if (ch == '\'')
          {
              offComment = ~(size_t)0;
              rcExit = vbcppExtractQuotedString(pThis, pStrmInput, pStrBuf, '\'', '\'');
          }
          else if (ch == '"')
          {
              offComment = ~(size_t)0;
              rcExit = vbcppExtractQuotedString(pThis, pStrmInput, pStrBuf, '"', '"');
          }
          else if (ch == '\r' || ch == '\n')
              break; /* done */
          else if (   RT_C_IS_SPACE(ch)
                   && (   RT_C_IS_SPACE(vbcppStrBufLastCh(pStrBuf))
                       || vbcppStrBufLastCh(pStrBuf) == '\0') )
          {
              unsigned ch2 = ScmStreamGetCh(pStrmInput);
              Assert(ch == ch2); NOREF(ch2);
              rcExit = RTEXITCODE_SUCCESS;
          }
          else
          {
              unsigned ch2 = ScmStreamGetCh(pStrmInput); Assert(ch == ch2);
  
              /* Escaped newline? */
              if (   ch == '\\'
                  && (   (ch2 = ScmStreamPeekCh(pStrmInput)) == '\r'
                      || ch2 == '\n'))
              {
                  ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
                  rcExit = RTEXITCODE_SUCCESS;
              }
              else
              {
                  offComment = ~(size_t)0;
                  rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
              }
          }
          if (rcExit != RTEXITCODE_SUCCESS)
              return rcExit;
      }
  
      if (poffComment)
          *poffComment = offComment;
      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).
   * @param   enmKind             The kind of directive we're processing.
   */
  static RTEXITCODE vbcppDirectiveIfOrElif(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart,
                                           VBCPPCONDKIND enmKind)
  {
      /*
       * Check for missing #if if #elif.
       */
      if (   enmKind == kVBCppCondKind_ElIf
          && !pThis->pCondStack )
          return vbcppError(pThis, "#elif without #if");
  
      /*
       * Extract the expression string.
       */
      const char         *pchCondition = ScmStreamGetCur(pStrmInput);
      size_t              offComment;
      VBCPPMACROEXP       ExpCtx;
  #if 0
      ExpCtx.pMacroStack    = NULL;
  #endif
      ExpCtx.pStrmInput     = NULL;
      ExpCtx.papszArgs      = NULL;
      ExpCtx.cArgs          = 0;
      ExpCtx.cArgsAlloced   = 0;
      vbcppStrBufInit(&ExpCtx.StrBuf, pThis);
      RTEXITCODE  rcExit = vbcppExtractDirectiveLine(pThis, pStrmInput, &ExpCtx.StrBuf, &offComment);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          size_t const    cchCondition = ScmStreamGetCur(pStrmInput) - pchCondition;
  
          /*
           * Expand known macros in it.
           */
          size_t          cReplacements;
          rcExit = vbcppMacroExpandReScan(pThis, &ExpCtx, kMacroReScanMode_Expression, &cReplacements);
          if (rcExit == RTEXITCODE_SUCCESS)
          {
              /*
               * Strip it and check that it's not empty.
               */
              char   *pszExpr = ExpCtx.StrBuf.pszBuf;
              size_t  cchExpr = ExpCtx.StrBuf.cchBuf;
              while (cchExpr > 0 && RT_C_IS_SPACE(*pszExpr))
                  pszExpr++, cchExpr--;
  
              while (cchExpr > 0 && RT_C_IS_SPACE(pszExpr[cchExpr - 1]))
              {
                  pszExpr[--cchExpr] = '\0';
                  ExpCtx.StrBuf.cchBuf--;
              }
              if (cchExpr)
              {
                  /*
                   * Now, evalute the expression.
                   */
                  VBCPPEVAL enmResult;
                  rcExit = vbcppExprEval(pThis, pszExpr, cchExpr, cReplacements, &enmResult);
                  if (rcExit == RTEXITCODE_SUCCESS)
                  {
                      /*
                       * Take action.
                       */
                      if (enmKind != kVBCppCondKind_ElIf)
                          rcExit = vbcppCondPush(pThis, pStrmInput, offComment, enmKind, enmResult,
                                                 pchCondition, cchCondition);
                      else
                      {
                          PVBCPPCOND pCond = pThis->pCondStack;
                          if (   pCond->enmResult != kVBCppEval_Undecided
                              && (   !pCond->pUp
                                  || pCond->pUp->enmStackResult == kVBCppEval_True))
                          {
                              Assert(enmResult == kVBCppEval_True || enmResult == kVBCppEval_False);
                              if (   pCond->enmResult == kVBCppEval_False
                                  && enmResult        == kVBCppEval_True
                                  && !pCond->fElIfDecided)
                              {
                                  pCond->enmStackResult = kVBCppEval_True;
                                  pCond->fElIfDecided   = true;
                              }
                              else
                                  pCond->enmStackResult = kVBCppEval_False;
                              pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
                          }
                          pCond->enmKind   = kVBCppCondKind_ElIf;
                          pCond->enmResult = enmResult;
                          pCond->pchCond   = pchCondition;
                          pCond->cchCond   = cchCondition;
  
                          /*
                           * Do #elif pass thru.
                           */
                          if (   !pThis->fIf0Mode
                              && pCond->enmResult == kVBCppEval_Undecided)
                          {
                              ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*selif", pCond->iKeepLevel - 1, "");
                              if (cch > 0)
                                  rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 2);
                              else
                                  rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
                          }
                          else
                              pThis->fJustDroppedLine = true;
                      }
                  }
              }
              else
                  rcExit = vbcppError(pThis, "Empty #if expression");
          }
      }
      vbcppMacroExpandCleanup(&ExpCtx);
      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).
   */
  static RTEXITCODE vbcppDirectiveIfDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
  {
      /*
       * Parse it.
       */
      RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          size_t      cchDefine;
          const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
          if (pchDefine)
          {
              rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
              if (rcExit == RTEXITCODE_SUCCESS)
              {
                  /*
                   * Evaluate it.
                   */
                  VBCPPEVAL enmEval;
                  if (vbcppMacroExists(pThis, pchDefine, cchDefine))
                      enmEval = kVBCppEval_True;
                  else if (   !pThis->fUndecidedConditionals
                           || RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
                      enmEval = kVBCppEval_False;
                  else
                      enmEval = kVBCppEval_Undecided;
                  rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfDef, enmEval,
                                         pchDefine, cchDefine);
              }
          }
          else
              rcExit = vbcppError(pThis, "Malformed #ifdef");
      }
      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).
   */
  static RTEXITCODE vbcppDirectiveIfNDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
  {
      /*
       * Parse it.
       */
      RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          size_t      cchDefine;
          const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
          if (pchDefine)
          {
              rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
              if (rcExit == RTEXITCODE_SUCCESS)
              {
                  /*
                   * Evaluate it.
                   */
                  VBCPPEVAL enmEval;
                  if (vbcppMacroExists(pThis, pchDefine, cchDefine))
                      enmEval = kVBCppEval_False;
                  else if (   !pThis->fUndecidedConditionals
                           || RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
                      enmEval = kVBCppEval_True;
                  else
                      enmEval = kVBCppEval_Undecided;
                  rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfNDef, enmEval,
                                         pchDefine, cchDefine);
              }
          }
          else
              rcExit = vbcppError(pThis, "Malformed #ifndef");
      }
      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).
   */
  static RTEXITCODE vbcppDirectiveElse(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
  {
      /*
       * Nothing to parse, just comment positions to find and note down.
       */
      offStart = vbcppProcessSkipWhite(pStrmInput);
      RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          /*
           * Execute.
           */
          PVBCPPCOND pCond = pThis->pCondStack;
          if (pCond)
          {
              if (!pCond->fSeenElse)
              {
                  pCond->fSeenElse = true;
                  if (   pCond->enmResult != kVBCppEval_Undecided
                      && (   !pCond->pUp
                          || pCond->pUp->enmStackResult == kVBCppEval_True))
                  {
                      if (   pCond->enmResult == kVBCppEval_True
                          || pCond->fElIfDecided)
  
                          pCond->enmStackResult = kVBCppEval_False;
                      else
                          pCond->enmStackResult = kVBCppEval_True;
                      pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
                  }
  
                  /*
                   * Do pass thru.
                   */
                  if (   !pThis->fIf0Mode
                      && pCond->enmResult == kVBCppEval_Undecided)
                  {
                      ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*selse", pCond->iKeepLevel - 1, "");
                      if (cch > 0)
                          rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 2);
                      else
                          rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
                  }
                  else
                      pThis->fJustDroppedLine = true;
              }
              else
                  rcExit = vbcppError(pThis, "Double #else or/and missing #endif");
          }
          else
              rcExit = vbcppError(pThis, "#else without #if");
      }
      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).
   */
  static RTEXITCODE vbcppDirectiveEndif(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
  {
      /*
       * Nothing to parse, just comment positions to find and note down.
       */
      offStart = vbcppProcessSkipWhite(pStrmInput);
      RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          /*
           * Execute.
           */
          PVBCPPCOND pCond = pThis->pCondStack;
          if (pCond)
          {
              pThis->pCondStack = pCond->pUp;
              pThis->fIf0Mode   = pCond->pUp && pCond->pUp->enmStackResult == kVBCppEval_False;
  
              /*
               * Do pass thru.
               */
              if (   !pThis->fIf0Mode
                  && pCond->enmResult == kVBCppEval_Undecided)
              {
                  ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sendif", pCond->iKeepLevel - 1, "");
                  if (cch > 0)
                      rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 1);
                  else
                      rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
              }
              else
                  pThis->fJustDroppedLine = true;
          }
          else
              rcExit = vbcppError(pThis, "#endif without #if");
      }
      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.
   */
  static RTEXITCODE vbcppAddInclude(PVBCPP pThis, const char *pszDir)
  {
      uint32_t cIncludes = pThis->cIncludes;
      if (cIncludes >= _64K)
          return vbcppError(pThis, "Too many include directories");
  
      void *pv = RTMemRealloc(pThis->papszIncludes, (cIncludes + 1) * sizeof(char **));
      if (!pv)
          return vbcppError(pThis, "No memory for include directories");
      pThis->papszIncludes = (char **)pv;
  
      int rc = RTStrDupEx(&pThis->papszIncludes[cIncludes], pszDir);
      if (RT_FAILURE(rc))
          return vbcppError(pThis, "No string memory for include directories");
  
      pThis->cIncludes = cIncludes + 1;
      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).
   */
  static RTEXITCODE vbcppDirectiveInclude(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
  {
      RT_NOREF_PV(offStart);
  
      /*
       * Parse it.
       */
      RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          size_t      cchFileSpec = 0;
          const char *pchFileSpec = NULL;
          size_t      cchFilename = 0;
          const char *pchFilename = NULL;
  
          unsigned ch = ScmStreamPeekCh(pStrmInput);
          unsigned chType = ch;
          if (ch == '"' || ch == '<')
          {
              ScmStreamGetCh(pStrmInput);
              pchFileSpec = pchFilename = ScmStreamGetCur(pStrmInput);
              unsigned chEnd  = chType == '<' ? '>' : '"';
              while (   (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0
                     &&  ch != chEnd)
              {
                  if (ch == '\r' || ch == '\n')
                  {
                      rcExit = vbcppError(pThis, "Multi-line include file specfications are not supported");
                      break;
                  }
              }
  
              if (rcExit == RTEXITCODE_SUCCESS)
              {
                  if (ch != ~(unsigned)0)
                      cchFileSpec = cchFilename = ScmStreamGetCur(pStrmInput) - pchFilename - 1;
                  else
                      rcExit = vbcppError(pThis, "Expected '%c'", chType);
              }
          }
          else if (vbcppIsCIdentifierLeadChar(ch))
          {
              //pchFileSpec = ScmStreamCGetWord(pStrmInput, &cchFileSpec);
              rcExit = vbcppError(pThis, "Including via a define is not implemented yet");
          }
          else
              rcExit = vbcppError(pThis, "Malformed include directive");
  
          /*
           * 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.
           */
          size_t const offIncEnd = vbcppProcessSkipWhite(pStrmInput);
          if (rcExit == RTEXITCODE_SUCCESS)
              rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
  
          if (rcExit == RTEXITCODE_SUCCESS)
          {
              /*
               * Execute it.
               */
              if (pThis->enmIncludeAction == kVBCppIncludeAction_Include)
              {
                  /** @todo Search for the include file and push it onto the input stack.
                   *  Not difficult, just unnecessary rigth now. */
                  rcExit = vbcppError(pThis, "Includes are fully implemented");
              }
              else if (pThis->enmIncludeAction == kVBCppIncludeAction_PassThru)
              {
                  /* Pretty print the passthru. */
                  unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
                  ssize_t  cch;
                  if (chType == '<')
                      cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude <%.*s>",
                                            cchIndent, "", cchFileSpec, pchFileSpec);
                  else if (chType == '"')
                      cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude \"%.*s\"",
                                            cchIndent, "", cchFileSpec, pchFileSpec);
                  else
                      cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude %.*s",
                                            cchIndent, "", cchFileSpec, pchFileSpec);
                  if (cch > 0)
                      rcExit = vbcppOutputComment(pThis, pStrmInput, offIncEnd, cch, 1);
                  else
                      rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
  
              }
              else
              {
                  Assert(pThis->enmIncludeAction == kVBCppIncludeAction_Drop);
                  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).
   */
  static RTEXITCODE vbcppDirectivePragma(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
  {
      RT_NOREF_PV(offStart);
  
      /*
       * Parse out the first word.
       */
      RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
      if (rcExit == RTEXITCODE_SUCCESS)
      {
          size_t      cchPragma;
          const char *pchPragma = ScmStreamCGetWord(pStrmInput, &cchPragma);
          if (pchPragma)
          {
              size_t const off2nd = vbcppProcessSkipWhite(pStrmInput);
              size_t       offComment;
              rcExit = vbcppInputSkipToEndOfDirectiveLine(pThis, pStrmInput, &offComment);
              if (rcExit == RTEXITCODE_SUCCESS)
              {
                  /*
                   * What to do about this
                   */
                  bool fPassThru = false;
                  if (   cchPragma  == 1
                      && *pchPragma == 'D')
                      fPassThru = pThis->fPassThruPragmaD;
                  else if (    cchPragma == 3
                           &&  !strncmp(pchPragma, "STD", 3))
                      fPassThru = pThis->fPassThruPragmaSTD;
                  else
                      fPassThru = pThis->fPassThruPragmaOther;
                  if (fPassThru)
                  {
                      unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
                      ssize_t  cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*spragma %.*s",
                                                     cchIndent, "", cchPragma, pchPragma);
                      if (cch > 0)
                          rcExit = vbcppOutputComment(pThis, pStrmInput, off2nd, cch, 1);
                      else
                          rcExit = vbcppError(pThis, "output error");
                  }
                  else
                      pThis->fJustDroppedLine = true;
              }
          }
          else
              rcExit = vbcppError(pThis, "Malformed #pragma");
      }
  
      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).
   */
  static RTEXITCODE vbcppDirectiveError(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
  {
      RT_NOREF_PV(offStart);
      RT_NOREF_PV(pStrmInput);
      return vbcppError(pThis, "Hit an #error");
  }
  
  
  /**
   * 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).
   */
  static RTEXITCODE vbcppDirectiveLineNo(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
  {
      RT_NOREF_PV(offStart);
      RT_NOREF_PV(pStrmInput);
      return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
  }
  
  
  /**
   * Processes a abbreviated line number directive.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
   * @param   pThis               The C preprocessor instance.
   * @param   pStrmInput          The input stream.
   */
  static RTEXITCODE vbcppDirectiveLineNoShort(PVBCPP pThis, PSCMSTREAM pStrmInput)
  {
      RT_NOREF_PV(pStrmInput);
      return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
  }
  
  
  /**
   * Handles a preprocessor directive.
   *
   * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
   * @param   pThis               The C preprocessor instance.
   * @param   pStrmInput          The input stream.
   */
  static RTEXITCODE vbcppProcessDirective(PVBCPP pThis, PSCMSTREAM pStrmInput)
  {
      /*
       * Get the directive and do a string switch on it.
       */
      RTEXITCODE  rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
      if (rcExit != RTEXITCODE_SUCCESS)
          return rcExit;
      size_t      cchDirective;
      const char *pchDirective = ScmStreamCGetWord(pStrmInput, &cchDirective);
      if (pchDirective)
      {
          size_t const offStart = ScmStreamTell(pStrmInput);
  #define IS_DIRECTIVE(a_sz) ( sizeof(a_sz) - 1 == cchDirective && strncmp(pchDirective, a_sz, sizeof(a_sz) - 1) == 0)
          if (IS_DIRECTIVE("if"))
              rcExit = vbcppDirectiveIfOrElif(pThis, pStrmInput, offStart, kVBCppCondKind_If);
          else if (IS_DIRECTIVE("elif"))
              rcExit = vbcppDirectiveIfOrElif(pThis, pStrmInput, offStart, kVBCppCondKind_ElIf);
          else if (IS_DIRECTIVE("ifdef"))
              rcExit = vbcppDirectiveIfDef(pThis, pStrmInput, offStart);
          else if (IS_DIRECTIVE("ifndef"))
              rcExit = vbcppDirectiveIfNDef(pThis, pStrmInput, offStart);
          else if (IS_DIRECTIVE("else"))
              rcExit = vbcppDirectiveElse(pThis, pStrmInput, offStart);
          else if (IS_DIRECTIVE("endif"))
              rcExit = vbcppDirectiveEndif(pThis, pStrmInput, offStart);
          else if (!pThis->fIf0Mode)
          {
              if (IS_DIRECTIVE("include"))
                  rcExit = vbcppDirectiveInclude(pThis, pStrmInput, offStart);
              else if (IS_DIRECTIVE("define"))
                  rcExit = vbcppDirectiveDefine(pThis, pStrmInput, offStart);
              else if (IS_DIRECTIVE("undef"))
                  rcExit = vbcppDirectiveUndef(pThis, pStrmInput, offStart);
              else if (IS_DIRECTIVE("pragma"))
                  rcExit = vbcppDirectivePragma(pThis, pStrmInput, offStart);
              else if (IS_DIRECTIVE("error"))
                  rcExit = vbcppDirectiveError(pThis, pStrmInput, offStart);
              else if (IS_DIRECTIVE("line"))
                  rcExit = vbcppDirectiveLineNo(pThis, pStrmInput, offStart);
              else
                  rcExit = vbcppError(pThis, "Unknown preprocessor directive '#%.*s'", cchDirective, pchDirective);
          }
  #undef IS_DIRECTIVE
      }
      else if (!pThis->fIf0Mode)
      {
          /* Could it be a # <num> "file" directive? */
          unsigned ch = ScmStreamPeekCh(pStrmInput);
          if (RT_C_IS_DIGIT(ch))
              rcExit = vbcppDirectiveLineNoShort(pThis, pStrmInput);
          else
              rcExit = vbcppError(pThis, "Malformed preprocessor directive");
      }
      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.
   */
  static RTEXITCODE vbcppPreprocess(PVBCPP pThis)
  {
      RTEXITCODE  rcExit = RTEXITCODE_SUCCESS;
  
      /*
       * Parse.
       */
      while (pThis->pInputStack)
      {
          pThis->fMaybePreprocessorLine = true;
  
          PSCMSTREAM  pStrmInput = &pThis->pInputStack->StrmInput;
          unsigned    ch;
          while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
          {
              if (ch == '/')
              {
                  ch = ScmStreamPeekCh(pStrmInput);
                  if (ch == '*')
                      rcExit = vbcppProcessMultiLineComment(pThis, pStrmInput);
                  else if (ch == '/')
                      rcExit = vbcppProcessOneLineComment(pThis, pStrmInput);
                  else
                  {
                      pThis->fMaybePreprocessorLine = false;
                      if (!pThis->fIf0Mode)
                          rcExit = vbcppOutputCh(pThis, '/');
                  }
              }
              else if (ch == '#' && pThis->fMaybePreprocessorLine)
              {
                  rcExit = vbcppProcessDirective(pThis, pStrmInput);
                  pStrmInput = &pThis->pInputStack->StrmInput;
              }
              else if (ch == '\r' || ch == '\n')
              {
                  if (   (   !pThis->fIf0Mode
                          && !pThis->fJustDroppedLine)
                      || !pThis->fRemoveDroppedLines
                      || !ScmStreamIsAtStartOfLine(&pThis->StrmOutput))
                      rcExit = vbcppOutputCh(pThis, ch);
                  pThis->fJustDroppedLine       = false;
                  pThis->fMaybePreprocessorLine = true;
              }
              else if (RT_C_IS_SPACE(ch))
              {
                  if (!pThis->fIf0Mode)
                      rcExit = vbcppOutputCh(pThis, ch);
              }
              else
              {
                  pThis->fMaybePreprocessorLine = false;
                  if (!pThis->fIf0Mode)
                  {
                      if (ch == '"')
                          rcExit = vbcppProcessStringLitteral(pThis, pStrmInput);
                      else if (ch == '\'')
                          rcExit = vbcppProcessCharacterConstant(pThis, pStrmInput);
                      else if (vbcppIsCIdentifierLeadChar(ch))
                          rcExit = vbcppProcessIdentifier(pThis, pStrmInput);
                      else if (RT_C_IS_DIGIT(ch))
                          rcExit = vbcppProcessNumber(pThis, pStrmInput, ch);
                      else
                          rcExit = vbcppOutputCh(pThis, ch);
                  }
              }
              if (rcExit != RTEXITCODE_SUCCESS)
                  break;
          }
  
          /*
           * Check for errors.
           */
          if (rcExit != RTEXITCODE_SUCCESS)
              break;
  
          /*
           * Pop the input stack.
           */
          PVBCPPINPUT pPopped = pThis->pInputStack;
          pThis->pInputStack = pPopped->pUp;
          RTMemFree(pPopped);
      }
  
      return rcExit;
  }
  
  
  /**
   * Opens the input and output streams.
   *
   * @returns Exit code.
   * @param   pThis               The C preprocessor instance.
   */
  static RTEXITCODE vbcppOpenStreams(PVBCPP pThis)
  {
      if (!pThis->pszInput)
          return vbcppError(pThis, "Preprocessing the standard input stream is currently not supported");
  
      size_t      cchName = strlen(pThis->pszInput);
      PVBCPPINPUT pInput = (PVBCPPINPUT)RTMemAlloc(RT_UOFFSETOF_DYN(VBCPPINPUT, szName[cchName + 1]));
      if (!pInput)
          return vbcppError(pThis, "out of memory");
      pInput->pUp          = pThis->pInputStack;
      pInput->pszSpecified = pInput->szName;
      memcpy(pInput->szName, pThis->pszInput, cchName + 1);
      pThis->pInputStack   = pInput;
      int rc = ScmStreamInitForReading(&pInput->StrmInput, pThis->pszInput);
      if (RT_FAILURE(rc))
          return vbcppError(pThis, "ScmStreamInitForReading returned %Rrc when opening input file (%s)",
                            rc, pThis->pszInput);
  
      rc = ScmStreamInitForWriting(&pThis->StrmOutput, &pInput->StrmInput);
      if (RT_FAILURE(rc))
          return vbcppError(pThis, "ScmStreamInitForWriting returned %Rrc", rc);
  
      pThis->fStrmOutputValid = true;
      return RTEXITCODE_SUCCESS;
  }
  
  
  /**
   * Changes the preprocessing mode.
   *
   * @param   pThis               The C preprocessor instance.
   * @param   enmMode             The new mode.
   */
  static void vbcppSetMode(PVBCPP pThis, VBCPPMODE enmMode)
  {
      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;
              pThis->enmIncludeAction                 = kVBCppIncludeAction_Include;
              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;
              pThis->enmIncludeAction                 = kVBCppIncludeAction_PassThru;
              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;
              pThis->enmIncludeAction                 = kVBCppIncludeAction_Drop;
              break;
  
          default:
              AssertFailedReturnVoid();
      }
      pThis->enmMode = enmMode;
  }
  
  
  /**
   * 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.
   */
  static RTEXITCODE vbcppParseOptions(PVBCPP pThis, int argc, char **argv, bool *pfExit)
  {
      RTEXITCODE rcExit;
  
      *pfExit = false;
  
      /*
       * Option config.
       */
      static RTGETOPTDEF const s_aOpts[] =
      {
          { "--define",                   'D',                    RTGETOPT_REQ_STRING },
          { "--include-dir",              'I',                    RTGETOPT_REQ_STRING },
          { "--undefine",                 'U',                    RTGETOPT_REQ_STRING },
          { "--keep-comments",            'C',                    RTGETOPT_REQ_NOTHING },
          { "--strip-comments",           'c',                    RTGETOPT_REQ_NOTHING },
          { "--D-strip",                  'd',                    RTGETOPT_REQ_NOTHING },
      };
  
      RTGETOPTUNION   ValueUnion;
      RTGETOPTSTATE   GetOptState;
      int rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
      AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
  
      /*
       * Process the options.
       */
      while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
      {
          switch (rc)
          {
              case 'c':
                  pThis->fKeepComments = false;
                  break;
  
              case 'C':
                  pThis->fKeepComments = false;
                  break;
  
              case 'd':
                  vbcppSetMode(pThis, kVBCppMode_SelectiveD);
                  break;
  
              case 'D':
              {
                  const char *pszEqual = strchr(ValueUnion.psz, '=');
                  if (pszEqual)
                      rcExit = vbcppMacroAdd(pThis, ValueUnion.psz, pszEqual - ValueUnion.psz, pszEqual + 1, RTSTR_MAX, true);
                  else
                      rcExit = vbcppMacroAdd(pThis, ValueUnion.psz, RTSTR_MAX, "1", 1, true);
                  if (rcExit != RTEXITCODE_SUCCESS)
                      return rcExit;
                  break;
              }
  
              case 'I':
                  rcExit = vbcppAddInclude(pThis, ValueUnion.psz);
                  if (rcExit != RTEXITCODE_SUCCESS)
                      return rcExit;
                  break;
  
              case 'U':
                  rcExit = vbcppMacroUndef(pThis, ValueUnion.psz, RTSTR_MAX, true);
                  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: 73097 $";
                  const char *psz = RTStrStripL(strchr(s_szRev, ' '));
                  RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
                  *pfExit = true;
                  return RTEXITCODE_SUCCESS;
              }
  
              case VINF_GETOPT_NOT_OPTION:
                  if (!pThis->pszInput)
                      pThis->pszInput = ValueUnion.psz;
                  else if (!pThis->pszOutput)
                      pThis->pszOutput = ValueUnion.psz;
                  else
                      return RTMsgErrorExit(RTEXITCODE_SYNTAX, "too many file arguments");
                  break;
  
  
              /*
               * Errors and bugs.
               */
              default:
                  return RTGetOptPrintError(rc, &ValueUnion);
          }
      }
  
      return RTEXITCODE_SUCCESS;
  }
  
  
  /**
   * Terminates the preprocessor.
   *
   * This may return failure if an error was delayed.
   *
   * @returns Exit code.
   * @param   pThis               The C preprocessor instance.
   */
  static RTEXITCODE vbcppTerm(PVBCPP pThis)
  {
      /*
       * Flush the output first.
       */
      if (pThis->fStrmOutputValid)
      {
          if (pThis->pszOutput)
          {
              int rc = ScmStreamWriteToFile(&pThis->StrmOutput, "%s", pThis->pszOutput);
              if (RT_FAILURE(rc))
                  vbcppError(pThis, "ScmStreamWriteToFile failed with %Rrc when writing '%s'", rc, pThis->pszOutput);
          }
          else
          {
              int rc = ScmStreamWriteToStdOut(&pThis->StrmOutput);
              if (RT_FAILURE(rc))
                  vbcppError(pThis, "ScmStreamWriteToStdOut failed with %Rrc", rc);
          }
      }
  
      /*
       * Cleanup.
       */
      while (pThis->pInputStack)
      {
          ScmStreamDelete(&pThis->pInputStack->StrmInput);
          void *pvFree = pThis->pInputStack;
          pThis->pInputStack = pThis->pInputStack->pUp;
          RTMemFree(pvFree);
      }
  
      ScmStreamDelete(&pThis->StrmOutput);
  
      RTStrSpaceDestroy(&pThis->StrSpace, vbcppMacroFree, NULL);
      pThis->StrSpace = NULL;
  
      uint32_t i = pThis->cIncludes;
      while (i-- > 0)
          RTStrFree(pThis->papszIncludes[i]);
      RTMemFree(pThis->papszIncludes);
      pThis->papszIncludes = NULL;
  
      return pThis->rcExit;
  }
  
  
  /**
   * Initializes the C preprocessor instance data.
   *
   * @param   pThis               The C preprocessor instance data.
   */
  static void vbcppInit(PVBCPP pThis)
  {
      vbcppSetMode(pThis, kVBCppMode_Selective);
      pThis->cIncludes        = 0;
      pThis->papszIncludes    = NULL;
      pThis->pszInput         = NULL;
      pThis->pszOutput        = NULL;
      pThis->StrSpace         = NULL;
      pThis->UndefStrSpace    = NULL;
      pThis->cCondStackDepth  = 0;
      pThis->pCondStack       = NULL;
      pThis->fIf0Mode         = false;
      pThis->fJustDroppedLine = false;
      pThis->fMaybePreprocessorLine = true;
      VBCPP_BITMAP_EMPTY(pThis->bmDefined);
      pThis->cCondStackDepth  = 0;
      pThis->pInputStack      = NULL;
      RT_ZERO(pThis->StrmOutput);
      pThis->rcExit           = RTEXITCODE_SUCCESS;
      pThis->fStrmOutputValid = false;
  }
  
  
  
  int main(int argc, char **argv)
  {
      int rc = RTR3InitExe(argc, &argv, 0);
      if (RT_FAILURE(rc))
          return RTMsgInitFailure(rc);
  
      /*
       * Do the job.  The code says it all.
       */
      VBCPP This;
      vbcppInit(&This);
      bool fExit;
      RTEXITCODE rcExit = vbcppParseOptions(&This, argc, argv, &fExit);
      if (!fExit && rcExit == RTEXITCODE_SUCCESS)
      {
          rcExit = vbcppOpenStreams(&This);
          if (rcExit == RTEXITCODE_SUCCESS)
              rcExit = vbcppPreprocess(&This);
      }
  
      if (rcExit == RTEXITCODE_SUCCESS)
          rcExit = vbcppTerm(&This);
      else
          vbcppTerm(&This);
      return rcExit;
  }
  
  