VirtualBox

Changeset 41179 in vbox for trunk


Ignore:
Timestamp:
May 6, 2012 8:31:02 PM (13 years ago)
Author:
vboxsync
Message:

More CPP core.

Location:
trunk/src/bldprogs
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bldprogs/Makefile.kmk

    r41177 r41179  
    5050        scmstream.cpp
    5151
    52  #BLDPROGS += VBoxCPP
     52 BLDPROGS += VBoxCPP
    5353 VBoxCPP_TEMPLATE = VBoxAdvBldProg
    5454 VBoxCPP_SOURCES = \
  • trunk/src/bldprogs/VBoxCPP.cpp

    r41177 r41179  
    11/* $Id$ */
    22/** @file
    3  * VBox Build Tool - A mini C Preprocessor.
     3 * VBox Build Tool - A mini C Preprocessor.
     4 * 
     5 * This is not attempting to be standard compliant, just get the job done!
    46 */
    57
     
    2426#include <iprt/alloca.h>
    2527#include <iprt/assert.h>
     28#include <iprt/asm.h>
    2629#include <iprt/ctype.h>
    27 #include <iprt/env.h>
    2830#include <iprt/err.h>
    2931#include <iprt/file.h>
     
    3436#include <iprt/message.h>
    3537#include <iprt/path.h>
    36 #include <iprt/process.h>
    3738#include <iprt/stream.h>
    3839#include <iprt/string.h>
    39 #include <iprt/uuid.h>
    4040
    4141#include "scmstream.h"
    4242
     43
     44/*******************************************************************************
     45*   Defined Constants And Macros                                               *
     46*******************************************************************************/
     47/** The bitmap type. */
     48#define VBCPP_BITMAP_TYPE                   uint64_t
     49/** The bitmap size as a multiple of VBCPP_BITMAP_TYPE. */
     50#define VBCPP_BITMAP_SIZE                   (128 / 64)
     51/** Checks if a bit is set. */
     52#define VBCPP_BITMAP_IS_SET(a_bm, a_ch)     ASMBitTest(a_bm, (a_ch) & 0x7f)
     53/** Sets a bit. */
     54#define VBCPP_BITMAP_SET(a_bm, a_ch)        ASMBitSet(a_bm, (a_ch) & 0x7f)
     55/** Empties the bitmap. */
     56#define VBCPP_BITMAP_EMPTY(a_bm)            do { (a_bm)[0] = 0; (a_bm)[1] = 0; } while (0)
     57/** Joins to bitmaps by OR'ing their values.. */
     58#define VBCPP_BITMAP_OR(a_bm1, a_bm2)       do { (a_bm1)[0] |= (a_bm2)[0]; (a_bm1)[1] |= (a_bm2)[1]; } while (0)
    4359
    4460
     
    7288    /** The number of known arguments.*/
    7389    uint32_t            cArgs;
    74 
    75     /** @todo More to come later some day... Currently, only care about selective
    76      *        preprocessing. */
    77 
    78     /** The define value.  (This is followed by the name). */
     90    /** Pointer to a list of argument names. */
     91    const char        **papszArgs;
     92    /** Lead character bitmap for the argument names. */
     93    VBCPP_BITMAP_TYPE   bmArgs[VBCPP_BITMAP_SIZE];
     94    /** The define value.  (This is followed by the name and arguments.) */
    7995    char                szValue[1];
    8096} VBCPPDEF;
     
    84100
    85101/**
     102 * Expansion context.
     103 */
     104typedef struct VBCPPCTX
     105{
     106    /** The next context on the stack. */
     107    struct VBCPPCTX    *pUp;
     108    /** The define being expanded. */
     109    PVBCPPDEF           pDef;
     110    /** Arguments. */
     111    struct VBCPPCTXARG
     112    {
     113        /** The value. */
     114        const char *pchValue;
     115        /** The value length. */
     116        const char *cchValue;
     117    }                   aArgs[1];
     118} VBCPPCTX;
     119/** Pointer to an define expansion context. */
     120typedef VBCPPCTX *PVBCPPCTX;
     121
     122
     123/**
    86124 * C Preprocessor instance data.
    87125 */
     
    91129     * @{ */
    92130    /** The preprocessing mode. */
    93     VBCPPMODE       enmMode;
     131    VBCPPMODE           enmMode;
    94132    /** Whether to keep comments. */
    95     bool            fKeepComments;
     133    bool                fKeepComments;
    96134
    97135    /** The number of include directories. */
    98     uint32_t        cIncludes;
     136    uint32_t            cIncludes;
    99137    /** Array of directories to search for include files. */
    100     const char    **papszIncludes;
     138    char              **papszIncludes;
    101139
    102140    /** The name of the input file. */
    103     const char     *pszInput;
     141    const char         *pszInput;
    104142    /** The name of the output file. NULL if stdout. */
    105     const char     *pszOutput;
     143    const char         *pszOutput;
    106144    /** @} */
    107145   
    108146    /** The define string space. */
    109     RTSTRSPACE      StrSpace;
     147    RTSTRSPACE          StrSpace;
    110148    /** Indicates whether a C-word might need expansion.
    111149     * The bitmap is indexed by C-word lead character.  Bits that are set
    112150     * indicates that the lead character is used in a \#define that we know and
    113151     * should expand. */
    114     uint32_t        bmDefined[256/32];
     152    VBCPP_BITMAP_TYPE   bmDefined[VBCPP_BITMAP_SIZE];
     153    /** Indicates whether a C-word might need argument expansion.
     154     * The bitmap is indexed by C-word lead character.  Bits that are set
     155     * indicates that the lead character is used in an argument of an currently
     156     * expanding  \#define. */
     157    VBCPP_BITMAP_TYPE   bmArgs[VBCPP_BITMAP_SIZE];
     158
     159    /** Expansion context stack. */
     160    PVBCPPCTX           pStack;
     161    /** The current stack depth. */
     162    uint32_t            cStackDepth;
     163    /** Whether the current line could be a preprocessor line.
     164     * This is set when EOL is encountered and cleared again when a
     165     * non-comment-or-space character is encountered.  See vbcppPreprocess. */
     166    bool                fMaybePreprocessorLine;
     167
     168    /** The current input stream. */
     169    PSCMSTREAM          pCurStrmInput;
     170    /** The input stream. */
     171    SCMSTREAM           StrmInput;
     172    /** The output stream. */
     173    SCMSTREAM           StrmOutput;
     174
     175    /** The status of the whole job, as far as we know. */
     176    RTEXITCODE          rcExit;
     177    /** Whether StrmOutput is valid (for vbcppTerm). */
     178    bool                fStrmOutputValid;
    115179} VBCPP;
    116180/** Pointer to the C preprocessor instance data. */
     
    134198    pThis->pszOutput        = NULL;
    135199    pThis->StrSpace         = NULL;
    136     RT_ZERO(pThis->bmDefined);
    137 
     200    pThis->pStack           = NULL;
     201    pThis->cStackDepth      = 0;
     202    VBCPP_BITMAP_EMPTY(pThis->bmDefined);
     203    VBCPP_BITMAP_EMPTY(pThis->bmArgs);
     204    RT_ZERO(pThis->StrmInput);
     205    RT_ZERO(pThis->StrmOutput);
     206    pThis->rcExit           = RTEXITCODE_SUCCESS;
     207    pThis->fStrmOutputValid = false;
     208}
     209
     210
     211/**
     212 * Displays an error message.
     213 * 
     214 * @returns RTEXITCODE_FAILURE
     215 * @param   pThis               The C preprocessor instance.
     216 * @param   pszMsg              The message.
     217 * @param   ...                 Message arguments.
     218 */
     219static RTEXITCODE vbcppError(PVBCPP pThis, const char *pszMsg, ...)
     220{
     221    NOREF(pThis);
     222    va_list va;
     223    va_start(va, pszMsg);
     224    RTMsgErrorV(pszMsg, va);
     225    va_end(va);
     226    return pThis->rcExit = RTEXITCODE_FAILURE;
     227}
     228
     229
     230/**
     231 * Displays an error message.
     232 * 
     233 * @returns RTEXITCODE_FAILURE
     234 * @param   pThis               The C preprocessor instance.
     235 * @param   pszPos              Pointer to the offending character.
     236 * @param   pszMsg              The message.
     237 * @param   ...                 Message arguments.
     238 */
     239static RTEXITCODE vbcppErrorPos(PVBCPP pThis, const char *pszPos, const char *pszMsg, ...)
     240{
     241    NOREF(pszPos); NOREF(pThis);
     242    va_list va;
     243    va_start(va, pszMsg);
     244    RTMsgErrorV(pszMsg, va);
     245    va_end(va);
     246    return pThis->rcExit = RTEXITCODE_FAILURE;
     247}
     248
     249
     250/**
     251 * Checks if the given character is a valid C identifier lead character.
     252 * 
     253 * @returns true / false.
     254 * @param   ch                  The character to inspect.
     255 */
     256DECLINLINE(bool) vbcppIsCIdentifierLeadChar(char ch)
     257{
     258    return RT_C_IS_ALPHA(ch)
     259        || ch == '_';
     260}
     261
     262
     263/**
     264 * Checks if the given character is a valid C identifier character.
     265 * 
     266 * @returns true / false.
     267 * @param   ch                  The character to inspect.
     268 */
     269DECLINLINE(bool) vbcppIsCIdentifierChar(char ch)
     270{
     271    return RT_C_IS_ALNUM(ch)
     272        || ch == '_';
     273}
     274
     275
     276
     277/**
     278 *
     279 * @returns @c true if valid, @c false if not. Error message already displayed
     280 *          on failure.
     281 * @param   pThis           The C preprocessor instance.
     282 * @param   pchIdentifier   The start of the identifier to validate.
     283 * @param   cchIdentifier   The length of the identifier. RTSTR_MAX if not
     284 *                          known.
     285 */
     286static bool vbcppValidateCIdentifier(PVBCPP pThis, const char *pchIdentifier, size_t cchIdentifier)
     287{
     288    if (cchIdentifier == RTSTR_MAX)
     289        cchIdentifier = strlen(pchIdentifier);
     290
     291    if (cchIdentifier == 0)
     292    {
     293        vbcppErrorPos(pThis, pchIdentifier, "Zero length identifier");
     294        return false;
     295    }
     296
     297    if (!vbcppIsCIdentifierLeadChar(*pchIdentifier))
     298    {
     299        vbcppErrorPos(pThis, pchIdentifier, "Bad lead chararacter in identifier: '%.*s'", cchIdentifier, pchIdentifier);
     300        return false;
     301    }
     302
     303    for (size_t off = 1; off < cchIdentifier; off++)
     304    {
     305        if (!vbcppIsCIdentifierChar(pchIdentifier[off]))
     306        {
     307            vbcppErrorPos(pThis, pchIdentifier + off, "Illegal chararacter in identifier: '%.*s' (#%zu)", cchIdentifier, pchIdentifier, off + 1);
     308            return false;
     309        }
     310    }
     311
     312    return true;
     313}
     314
     315
     316/**
     317 * Frees a define.
     318 * 
     319 * @returns VINF_SUCCESS (used when called by RTStrSpaceDestroy)
     320 * @param   pStr                Pointer to the VBCPPDEF::Core member.
     321 * @param   pvUser              Unused.
     322 */
     323static DECLCALLBACK(int) vbcppFreeDefine(PRTSTRSPACECORE pStr, void *pvUser)
     324{
     325    RTMemFree(pStr);
     326    NOREF(pvUser);
     327    return VINF_SUCCESS;
    138328}
    139329
     
    143333 * 
    144334 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
    145  * @param   pThis               The C processor instance.
     335 * @param   pThis               The C preprocessor instance.
    146336 * @param   pszDefine           The define name, no argument list or anything.
    147337 * @param   cchDefine           The length of the name. RTSTR_MAX is ok.
     
    153343    {
    154344        RTStrSpaceRemove(&pThis->StrSpace, pHit->pszString);
    155         RTMemFree(pHit);
     345        vbcppFreeDefine(pHit, NULL);
    156346    }
    157347    return RTEXITCODE_SUCCESS;
    158348}
    159349
     350/**
     351 * Inserts a define.
     352 * 
     353 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
     354 * @param   pThis               The C preprocessor instance.
     355 * @param   pDef                The define to insert.
     356 */
     357static RTEXITCODE vbcppInsertDefine(PVBCPP pThis, PVBCPPDEF pDef)
     358{
     359    if (RTStrSpaceInsert(&pThis->StrSpace, &pDef->Core))
     360        VBCPP_BITMAP_SET(pThis->bmDefined, *pDef->Core.pszString);
     361    else
     362    {
     363        RTMsgWarning("Redefining '%s'\n", pDef->Core.pszString);
     364        PVBCPPDEF pOld = (PVBCPPDEF)vbcppRemoveDefine(pThis, pDef->Core.pszString, pDef->Core.cchString);
     365        bool fRc = RTStrSpaceInsert(&pThis->StrSpace, &pDef->Core);
     366        Assert(fRc); Assert(pOld);
     367        vbcppFreeDefine(&pOld->Core, NULL);
     368    }
     369
     370    return RTEXITCODE_SUCCESS;
     371}
     372
    160373
    161374/**
     
    163376 * 
    164377 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
    165  * @param   pThis               The C processor instance.
     378 * @param   pThis               The C preprocessor instance.
     379 * @param   pszDefine           The define name, no parameter list.
     380 * @param   cchDefine           The length of the name.
     381 * @param   pszParams           The parameter list.
     382 * @param   cchParams           The length of the parameter list.
     383 * @param   pszValue            The value.
     384 * @param   cchDefine           The length of the value.
     385 */
     386static RTEXITCODE vbcppAddDefineFn(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
     387                                   const char *pszParams, size_t cchParams,
     388                                   const char *pszValue, size_t cchValue)
     389
     390{
     391    Assert(RTStrNLen(pszDefine, cchDefine) == cchDefine);
     392    Assert(RTStrNLen(pszParams, cchParams) == cchParams);
     393    Assert(RTStrNLen(pszValue,  cchValue)  == cchValue);
     394
     395    /*
     396     * Determin the number of arguments and how much space their names
     397     * requires.  Performing syntax validation while parsing.
     398     */
     399    uint32_t cchArgNames = 0;
     400    uint32_t cArgs       = 0;
     401    for (size_t off = 0; off < cchParams; off++)
     402    {
     403        /* Skip blanks and maybe one comma. */
     404        bool fIgnoreComma = cArgs != 0;
     405        while (off < cchParams)
     406        {
     407            if (!RT_C_IS_SPACE(pszParams[off]))
     408            {
     409                if (pszParams[off] != ',' || !fIgnoreComma)
     410                {
     411                    if (vbcppIsCIdentifierLeadChar(pszParams[off]))
     412                        break;
     413                    /** @todo variadic macros. */
     414                    return vbcppErrorPos(pThis, &pszParams[off], "Unexpected character");
     415                }
     416                fIgnoreComma = false;
     417            }
     418            off++;
     419        }
     420        if (off >= cchParams)
     421            break;
     422
     423        /* Found and argument. First character is already validated. */
     424        cArgs++;
     425        cchArgNames += 2;
     426        off++;
     427        while (   off < cchParams
     428               && vbcppIsCIdentifierChar(pszParams[off]))
     429            off++, cchArgNames++;
     430    }
     431
     432    /*
     433     * Allocate a structure.
     434     */
     435    size_t    cbDef = RT_OFFSETOF(VBCPPDEF, szValue[cchValue + 1 + cchDefine + 1 + cchArgNames])
     436                    + sizeof(const char *) * cArgs;
     437    cbDef = RT_ALIGN_Z(cbDef, sizeof(const char *));
     438    PVBCPPDEF pDef  = (PVBCPPDEF)RTMemAlloc(cbDef);
     439    if (!pDef)
     440        return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
     441
     442    char *pszDst = &pDef->szValue[cchValue + 1];
     443    pDef->Core.pszString = pszDst;
     444    memcpy(pszDst, pszDefine, cchDefine);
     445    pszDst += cchDefine;
     446    *pszDst++ = '\0';
     447    pDef->fFunction = true;
     448    pDef->fVarArg   = false;
     449    pDef->cArgs     = cArgs;
     450    pDef->papszArgs = (const char **)((uintptr_t)pDef + cbDef - sizeof(const char *) * cArgs);
     451    VBCPP_BITMAP_EMPTY(pDef->bmArgs);
     452    memcpy(pDef->szValue, pszValue, cchValue);
     453    pDef->szValue[cchValue] = '\0';
     454
     455    /*
     456     * Set up the arguments.
     457     */
     458    uint32_t iArg = 0;
     459    for (size_t off = 0; off < cchParams; off++)
     460    {
     461        /* Skip blanks and maybe one comma. */
     462        bool fIgnoreComma = cArgs != 0;
     463        while (off < cchParams)
     464        {
     465            if (!RT_C_IS_SPACE(pszParams[off]))
     466            {
     467                if (pszParams[off] != ',' || !fIgnoreComma)
     468                    break;
     469                fIgnoreComma = false;
     470            }
     471            off++;
     472        }
     473        if (off >= cchParams)
     474            break;
     475
     476        /* Found and argument. First character is already validated. */
     477        pDef->papszArgs[iArg] = pszDst;
     478        do
     479        {
     480            *pszDst++ = pszParams[off++];
     481        } while (   off < cchParams
     482                 && vbcppIsCIdentifierChar(pszParams[off]));
     483        *pszDst++ = '\0';
     484        iArg++;
     485    }
     486    Assert((uintptr_t)pszDst <= (uintptr_t)pDef->papszArgs);
     487
     488    return vbcppInsertDefine(pThis, pDef);
     489}
     490
     491
     492/**
     493 * Adds a define.
     494 * 
     495 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
     496 * @param   pThis               The C preprocessor instance.
    166497 * @param   pszDefine           The define name and optionally the argument
    167498 *                              list.
     
    173504                                 const char *pszValue, size_t cchValue)
    174505{
     506    /*
     507     * We need the lengths. Trim the input.
     508     */
    175509    if (cchDefine == RTSTR_MAX)
    176510        cchDefine = strlen(pszDefine);
     511    while (cchDefine > 0 && RT_C_IS_SPACE(*pszDefine))
     512        pszDefine++, cchDefine--;
     513    while (cchDefine > 0 && RT_C_IS_SPACE(pszDefine[cchDefine - 1]))
     514        cchDefine--;
     515    if (!cchDefine)
     516        return vbcppErrorPos(pThis, pszDefine, "The define has no name");
     517
    177518    if (cchValue == RTSTR_MAX)
    178519        cchValue = strlen(pszValue);
    179 
    180     const char *pszParan = (const char *)memchr(pszDefine, '(', cchDefine);
    181     if (pszParan)
    182         return RTMsgErrorExit(RTEXITCODE_FAILURE, "function defines are not implemented. sorry");
    183 
    184     PVBCPPDEF pDef = (PVBCPPDEF)RTMemAlloc(sizeof(*pDef) + cchValue + cchDefine + 2);
     520    while (cchValue > 0 && RT_C_IS_SPACE(*pszValue))
     521        pszValue++, cchValue--;
     522    while (cchValue > 0 && RT_C_IS_SPACE(pszValue[cchValue - 1]))
     523        cchValue--;
     524
     525    /*
     526     * Arguments make the job a bit more annoying.  Handle that elsewhere
     527     */
     528    const char *pszParams = (const char *)memchr(pszDefine, '(', cchDefine);
     529    if (pszParams)
     530    {
     531        size_t cchParams = pszDefine + cchDefine - pszParams;
     532        cchDefine -= cchParams;
     533        if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
     534            return RTEXITCODE_FAILURE;
     535        if (pszParams[cchParams - 1] != ')')
     536            return vbcppErrorPos(pThis, pszParams + cchParams - 1, "Missing closing parenthesis");
     537        pszParams++;
     538        cchParams -= 2;
     539        return vbcppAddDefineFn(pThis, pszDefine, cchDefine, pszParams, cchParams, pszValue, cchValue);
     540    }
     541
     542    /*
     543     * Simple define, no arguments.
     544     */
     545    if (vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
     546        return RTEXITCODE_FAILURE;
     547
     548    PVBCPPDEF pDef = (PVBCPPDEF)RTMemAlloc(RT_OFFSETOF(VBCPPDEF, szValue[cchValue + 1 + cchDefine + 1]));
    185549    if (!pDef)
    186550        return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
    187551
     552    pDef->Core.pszString = &pDef->szValue[cchValue + 1];
     553    memcpy((char *)pDef->Core.pszString, pszDefine, cchDefine);
     554    ((char *)pDef->Core.pszString)[cchDefine] = '\0';
    188555    pDef->fFunction = false;
    189556    pDef->fVarArg   = false;
    190557    pDef->cArgs     = 0;
     558    pDef->papszArgs = NULL;
     559    VBCPP_BITMAP_EMPTY(pDef->bmArgs);
    191560    memcpy(pDef->szValue, pszValue, cchValue);
    192561    pDef->szValue[cchValue] = '\0';
    193     pDef->Core.pszString = &pDef->szValue[cchValue + 1];
    194     memcpy(&pDef->szValue[cchValue + 1], pszDefine, cchDefine);
    195     pDef->szValue[cchValue + 1 + cchDefine] = '\0';
    196 
    197     if (!RTStrSpaceInsert(&pThis->StrSpace, &pDef->Core))
    198     {
    199         RTMsgWarning("Redefining '%s'\n", pDef->Core.pszString);
    200         vbcppRemoveDefine(pThis, pDef->Core.pszString, cchDefine);
    201         RTStrSpaceInsert(&pThis->StrSpace, &pDef->Core);
    202     }
    203     return RTEXITCODE_SUCCESS;
    204 }
    205 
     562
     563    return vbcppInsertDefine(pThis, pDef);
     564}
     565
     566
     567/**
     568 * Adds an include directory.
     569 * 
     570 * @returns Program exit code, with error message on failure.
     571 * @param   pThis               The C preprocessor instance.
     572 * @param   pszDir              The directory to add.
     573 */
    206574static RTEXITCODE vbcppAddInclude(PVBCPP pThis, const char *pszDir)
    207575{
     576    uint32_t cIncludes = pThis->cIncludes;
     577    if (cIncludes >= _64K)
     578        return vbcppError(pThis, "Too many include directories");
     579
     580    void *pv = RTMemRealloc(pThis->papszIncludes, (cIncludes + 1) * sizeof(char **));
     581    if (!pv)
     582        return vbcppError(pThis, "No memory for include directories");
     583    pThis->papszIncludes = (char **)pv;
     584
     585    int rc = RTStrDupEx(&pThis->papszIncludes[cIncludes], pszDir);
     586    if (RT_FAILURE(rc))
     587        return vbcppError(pThis, "No string memory for include directories");
     588
     589    pThis->cIncludes = cIncludes + 1;
    208590    return RTEXITCODE_SUCCESS;
    209591}
     
    233615        { "--include-dir",              'I',                    RTGETOPT_REQ_STRING },
    234616        { "--undefine",                 'U',                    RTGETOPT_REQ_STRING },
     617        { "--keep-comments",            'C',                    RTGETOPT_REQ_NOTHING },
     618        { "--strip-comments",           'c',                    RTGETOPT_REQ_NOTHING },
    235619        { "--D-strip",                  'd',                    RTGETOPT_REQ_NOTHING },
    236620    };
     
    248632        switch (rc)
    249633        {
     634            case 'c':
     635                pThis->fKeepComments = false;
     636                break;
     637
     638            case 'C':
     639                pThis->fKeepComments = false;
     640                break;
     641
    250642            case 'd':
    251643                pThis->enmMode = kVBCppMode_SelectiveD;
     644                pThis->fKeepComments = true;
    252645                break;
    253646
     
    311704
    312705
     706/**
     707 * Opens the input and output streams.
     708 * 
     709 * @returns Exit code.
     710 * @param   pThis               The C preprocessor instance.
     711 */
     712static RTEXITCODE vbcppOpenStreams(PVBCPP pThis)
     713{
     714    if (!pThis->pszInput)
     715        return vbcppError(pThis, "Preprocessing the standard input stream is currently not supported");
     716
     717    int rc = ScmStreamInitForReading(&pThis->StrmInput, pThis->pszInput);
     718    if (RT_FAILURE(rc))
     719        return vbcppError(pThis, "ScmStreamInitForReading returned %Rrc when opening input file (%s)",
     720                          rc, pThis->pszInput);
     721
     722    rc = ScmStreamInitForWriting(&pThis->StrmOutput, &pThis->StrmInput);
     723    if (RT_FAILURE(rc))
     724        return vbcppError(pThis, "ScmStreamInitForWriting returned %Rrc", rc);
     725
     726    pThis->fStrmOutputValid = true;
     727    return RTEXITCODE_SUCCESS;
     728}
     729
     730
     731/**
     732 * Outputs a character.
     733 * 
     734 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
     735 * @param   pThis               The C preprocessor instance.
     736 * @param   ch                  The character to output.
     737 */
     738static RTEXITCODE vbcppOutputCh(PVBCPP pThis, char ch)
     739{
     740    int rc = ScmStreamPutCh(&pThis->StrmOutput, ch);
     741    if (RT_SUCCESS(rc))
     742        return RTEXITCODE_SUCCESS;
     743    return vbcppError(pThis, "Output error %Rrc");
     744}
     745
     746
     747/**
     748 * Outputs a string.
     749 * 
     750 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
     751 * @param   pThis               The C preprocessor instance.
     752 * @param   pch                 The string.
     753 * @param   cch                 The number of characters to write.
     754 */
     755static RTEXITCODE vbcppOutputWrite(PVBCPP pThis, const char *pch, size_t cch)
     756{
     757    int rc = ScmStreamWrite(&pThis->StrmOutput, pch, cch);
     758    if (RT_SUCCESS(rc))
     759        return RTEXITCODE_SUCCESS;
     760    return vbcppError(pThis, "Output error %Rrc");
     761}
     762
     763
     764/**
     765 * Processes a multi-line comment.
     766 * 
     767 * Must either string the comment or keep it. If the latter, we must refrain
     768 * from replacing C-words in it.
     769 * 
     770 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
     771 * @param   pThis               The C preprocessor instance.
     772 * @param   pStrmInput          The input stream.
     773 */
     774static RTEXITCODE vbcppProcessMultiLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
     775{
     776    /* The open comment sequence. */
     777    ScmStreamGetCh(pStrmInput);         /* '*' */
     778    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
     779    if (pThis->fKeepComments)
     780        rcExit = vbcppOutputWrite(pThis, "/*", 2);
     781
     782    /* The comment.*/
     783    unsigned ch;
     784    while (   rcExit == RTEXITCODE_SUCCESS
     785           && (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0 )
     786    {
     787        if (ch == '*')
     788        {
     789            /* Closing sequence? */
     790            unsigned ch2 = ScmStreamPeekCh(pStrmInput);
     791            if (ch2 == '/')
     792            {
     793                ScmStreamGetCh(pStrmInput);
     794                if (pThis->fKeepComments)
     795                    rcExit = vbcppOutputWrite(pThis, "*/", 2);
     796                break;
     797            }
     798        }
     799
     800        if (pThis->fKeepComments || ch == '\r' || ch == '\n')
     801        {
     802            rcExit = vbcppOutputCh(pThis, ch);
     803            if (rcExit != RTEXITCODE_SUCCESS)
     804                break;
     805
     806            /* Reset the maybe-preprocessor-line indicator when necessary. */
     807            if (ch == '\r' || ch == '\n')
     808                pThis->fMaybePreprocessorLine = true;
     809        }
     810    }
     811    return rcExit;
     812}
     813
     814
     815/**
     816 * Processes a single line comment.
     817 * 
     818 * Must either string the comment or keep it. If the latter, we must refrain
     819 * from replacing C-words in it.
     820 * 
     821 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
     822 * @param   pThis               The C preprocessor instance.
     823 * @param   pStrmInput          The input stream.
     824 */
     825static RTEXITCODE vbcppProcessOneLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
     826{
     827    RTEXITCODE  rcExit;
     828    SCMEOL      enmEol;
     829    size_t      cchLine;
     830    const char *pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol); Assert(pszLine);
     831    pszLine--; cchLine++;               /* unfetching the first slash. */
     832    for (;;)
     833    {
     834        if (pThis->fKeepComments)
     835            rcExit = vbcppOutputWrite(pThis, pszLine, cchLine + enmEol);
     836        else
     837            rcExit = vbcppOutputWrite(pThis, pszLine + cchLine, enmEol);
     838        if (rcExit != RTEXITCODE_SUCCESS)
     839            break;
     840        if (   cchLine == 0
     841            || pszLine[cchLine - 1] != '\\')
     842            break;
     843
     844        pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol);
     845        if (!pszLine)
     846            break;
     847    }
     848    pThis->fMaybePreprocessorLine = true;
     849    return rcExit;
     850}
     851
     852
     853/**
     854 * Processes a double quoted string.
     855 * 
     856 * Must not replace any C-words in strings.
     857 * 
     858 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
     859 * @param   pThis               The C preprocessor instance.
     860 * @param   pStrmInput          The input stream.
     861 */
     862static RTEXITCODE vbcppProcessDoubleQuotedString(PVBCPP pThis, PSCMSTREAM pStrmInput)
     863{
     864    RTEXITCODE rcExit = vbcppOutputCh(pThis, '"');
     865    if (rcExit == RTEXITCODE_SUCCESS)
     866    {
     867        bool fEscaped = false;
     868        for (;;)
     869        {
     870            unsigned ch = ScmStreamGetCh(pStrmInput);
     871            if (ch == ~(unsigned)0)
     872            {
     873                rcExit = vbcppError(pThis, "Unterminated double quoted string");
     874                break;
     875            }
     876
     877            rcExit = vbcppOutputCh(pThis, ch);
     878            if (rcExit != RTEXITCODE_SUCCESS)
     879                break;
     880
     881            if (ch == '"' && !fEscaped)
     882                break;
     883            fEscaped = !fEscaped && ch == '\\';
     884        }
     885    }
     886    return rcExit;
     887}
     888
     889
     890/**
     891 * Processes a single quoted litteral.
     892 * 
     893 * Must not replace any C-words in strings.
     894 * 
     895 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
     896 * @param   pThis               The C preprocessor instance.
     897 * @param   pStrmInput          The input stream.
     898 */
     899static RTEXITCODE vbcppProcessSingledQuotedString(PVBCPP pThis, PSCMSTREAM pStrmInput)
     900{
     901    RTEXITCODE rcExit = vbcppOutputCh(pThis, '\'');
     902    if (rcExit == RTEXITCODE_SUCCESS)
     903    {
     904        bool fEscaped = false;
     905        for (;;)
     906        {
     907            unsigned ch = ScmStreamGetCh(pStrmInput);
     908            if (ch == ~(unsigned)0)
     909            {
     910                rcExit = vbcppError(pThis, "Unterminated singled quoted string");
     911                break;
     912            }
     913
     914            rcExit = vbcppOutputCh(pThis, ch);
     915            if (rcExit != RTEXITCODE_SUCCESS)
     916                break;
     917
     918            if (ch == '\'' && !fEscaped)
     919                break;
     920            fEscaped = !fEscaped && ch == '\\';
     921        }
     922    }
     923    return rcExit;
     924}
     925
     926
     927/**
     928 * Processes a preprocessor directive.
     929 * 
     930 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
     931 * @param   pThis               The C preprocessor instance.
     932 * @param   pStrmInput          The input stream.
     933 */
     934static RTEXITCODE vbcppProcessDirective(PVBCPP pThis, PSCMSTREAM pStrmInput)
     935{
     936#if 0
     937    size_t const offStart = ScmStreamTell(pStrmInput);
     938
     939    /*
     940     * Skip spaces.
     941     */
     942    unsigned chPrev = ~(unsigned)0;
     943    unsigned ch;                 
     944    while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
     945    {
     946        if (!RT_C_IS_SPACE(ch))
     947        {
     948            if ()
     949            {
     950            }
     951        }
     952        ch = chPrev;
     953    }
     954#endif
     955    return vbcppError(pThis, "Not implemented");
     956}
     957
     958
     959/**
     960 * Processes a C word, possibly replacing it with a definition.
     961 * 
     962 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
     963 * @param   pThis               The C preprocessor instance.
     964 * @param   pStrmInput          The input stream.
     965 * @param   ch                  The first character.
     966 */
     967static RTEXITCODE vbcppProcessCWord(PVBCPP pThis, PSCMSTREAM pStrmInput, char ch)
     968{
     969    /** @todo Implement this... */
     970    return vbcppOutputCh(pThis, ch);
     971}
     972
     973
     974/**
     975 * Does the actually preprocessing of the input file.
     976 * 
     977 * @returns Exit code.
     978 * @param   pThis               The C preprocessor instance.
     979 * @param   pStrmInput          The input stream.
     980 */
     981static RTEXITCODE vbcppPreprocess(PVBCPP pThis, PSCMSTREAM pStrmInput)
     982{
     983    /*
     984     * Push.
     985     */
     986    PSCMSTREAM pStrmInputOld = pThis->pCurStrmInput;
     987    pThis->pCurStrmInput = pStrmInput;
     988    pThis->fMaybePreprocessorLine = true;
     989
     990    /*
     991     * Parse.
     992     */
     993    RTEXITCODE  rcExit = RTEXITCODE_SUCCESS;
     994    unsigned    ch;
     995    while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
     996    {
     997        if (ch == '/')
     998        {
     999            ch = ScmStreamPeekCh(pStrmInput);
     1000            if (ch == '*')
     1001                rcExit = vbcppProcessMultiLineComment(pThis, pStrmInput);
     1002            else if (ch == '/')
     1003                rcExit = vbcppProcessOneLineComment(pThis, pStrmInput);
     1004            else
     1005            {
     1006                pThis->fMaybePreprocessorLine = false;
     1007                rcExit = vbcppOutputCh(pThis, '/');
     1008            }
     1009        }
     1010        else if (ch == '#' && pThis->fMaybePreprocessorLine)
     1011            rcExit = vbcppProcessDirective(pThis, pStrmInput);
     1012        else if (ch == '\r' || ch == '\n')
     1013        {
     1014            pThis->fMaybePreprocessorLine = true;
     1015            rcExit = vbcppOutputCh(pThis, ch);
     1016        }
     1017        else if (RT_C_IS_SPACE(ch))
     1018            rcExit = vbcppOutputCh(pThis, ch);
     1019        else
     1020        {
     1021            pThis->fMaybePreprocessorLine = false;
     1022            if (ch == '"')
     1023                rcExit = vbcppProcessDoubleQuotedString(pThis, pStrmInput);
     1024            else if (ch == '\'')
     1025                rcExit = vbcppProcessSingledQuotedString(pThis, pStrmInput);
     1026            else if (vbcppIsCIdentifierLeadChar(ch))
     1027                rcExit = vbcppProcessCWord(pThis, pStrmInput, ch);
     1028            else
     1029                rcExit = vbcppOutputCh(pThis, ch);
     1030        }
     1031        if (rcExit != RTEXITCODE_SUCCESS)
     1032            break;
     1033    }
     1034   
     1035    /*
     1036     * Pop.
     1037     */
     1038    pThis->pCurStrmInput = pStrmInputOld;
     1039    pThis->fMaybePreprocessorLine = true;
     1040    return rcExit;
     1041}
     1042
     1043
     1044/**
     1045 * Terminates the preprocessor.
     1046 * 
     1047 * This may return failure if an error was delayed.
     1048 * 
     1049 * @returns Exit code.
     1050 * @param   pThis               The C preprocessor instance.
     1051 */
     1052static RTEXITCODE vbcppTerm(PVBCPP pThis)
     1053{
     1054    /*
     1055     * Flush the output first.
     1056     */
     1057    if (pThis->fStrmOutputValid)
     1058    {
     1059        if (pThis->pszOutput)
     1060        {
     1061            int rc = ScmStreamWriteToFile(&pThis->StrmOutput, "%s", pThis->pszOutput);
     1062            if (RT_FAILURE(rc))
     1063                vbcppError(pThis, "ScmStreamWriteToFile failed with %Rrc when writing '%s'", rc, pThis->pszOutput);
     1064        }
     1065        else
     1066        {
     1067            int rc = ScmStreamWriteToStdOut(&pThis->StrmOutput);
     1068            if (RT_FAILURE(rc))
     1069                vbcppError(pThis, "ScmStreamWriteToStdOut failed with %Rrc", rc);
     1070        }
     1071    }
     1072
     1073    /*
     1074     * Cleanup.
     1075     */
     1076    ScmStreamDelete(&pThis->StrmInput);
     1077    ScmStreamDelete(&pThis->StrmOutput);
     1078
     1079    RTStrSpaceDestroy(&pThis->StrSpace, vbcppFreeDefine, NULL);
     1080    pThis->StrSpace = NULL;
     1081
     1082    uint32_t i = pThis->cIncludes;
     1083    while (i-- > 0)
     1084        RTStrFree(pThis->papszIncludes[i]);
     1085    RTMemFree(pThis->papszIncludes);
     1086    pThis->papszIncludes = NULL;
     1087
     1088    return pThis->rcExit;
     1089}
     1090
    3131091
    3141092
     
    3191097        return RTMsgInitFailure(rc);
    3201098
    321     /* 
    322      * Parse options.
     1099    /*
     1100     * Do the job.  The code says it all.
    3231101     */
    3241102    VBCPP This;
     
    3281106    if (!fExit && rcExit == RTEXITCODE_SUCCESS)
    3291107    {
    330         /*
    331          * Process the input file.
    332          */
    333 
    334     }
    335 
     1108        rcExit = vbcppOpenStreams(&This);
     1109        if (rcExit == RTEXITCODE_SUCCESS)
     1110            rcExit = vbcppPreprocess(&This, &This.StrmInput);
     1111    }
     1112
     1113    if (rcExit == RTEXITCODE_SUCCESS)
     1114        rcExit = vbcppTerm(&This);
     1115    else
     1116        vbcppTerm(&This);
    3361117    return rcExit;
    3371118}
  • trunk/src/bldprogs/scmstream.cpp

    r40558 r41179  
    2121#include <iprt/assert.h>
    2222#include <iprt/ctype.h>
     23#include <iprt/err.h>
    2324#include <iprt/file.h>
    24 #include <iprt/err.h>
     25#include <iprt/handle.h>
    2526#include <iprt/mem.h>
     27#include <iprt/pipe.h>
    2628#include <iprt/string.h>
    2729
     
    328330
    329331/**
     332 * Writes the stream to standard output.
     333 *
     334 * @returns IPRT status code
     335 * @param   pStream             The stream.
     336 */
     337int ScmStreamWriteToStdOut(PSCMSTREAM pStream)
     338{
     339    int rc;
     340
     341#ifdef RT_STRICT
     342    /*
     343     * Check that what we're going to write makes sense first.
     344     */
     345    rc = ScmStreamCheckItegrity(pStream);
     346    if (RT_FAILURE(rc))
     347        return rc;
     348#endif
     349
     350    /*
     351     * Do the actual writing.
     352     */
     353    RTHANDLE h;
     354    rc = RTHandleGetStandard(RTHANDLESTD_OUTPUT, &h);
     355    if (RT_SUCCESS(rc))
     356    {
     357        switch (h.enmType)
     358        {
     359            case RTHANDLETYPE_FILE:
     360                rc = RTFileWrite(h.u.hFile, pStream->pch, pStream->cb, NULL);
     361                break;
     362            case RTHANDLETYPE_PIPE:
     363                rc = RTPipeWriteBlocking(h.u.hPipe, pStream->pch, pStream->cb, NULL);
     364                break;
     365            default:
     366                rc = VERR_INVALID_HANDLE;
     367                break;
     368        }
     369    }
     370    return rc;
     371}
     372
     373/**
    330374 * Worker for ScmStreamGetLine that builds the line number index while parsing
    331375 * the stream.
     
    642686const char *ScmStreamGetLine(PSCMSTREAM pStream, size_t *pcchLine, PSCMEOL penmEol)
    643687{
    644     /** @todo this doesn't work when pStream->off !=
    645      *        pStream->paLines[pStream->iLine-1].off. */
    646688    if (!pStream->fFullyLineated)
    647689        return scmStreamGetLineInternal(pStream, pcchLine, penmEol);
    648     return ScmStreamGetLineByNo(pStream, pStream->iLine, pcchLine, penmEol);
     690
     691    size_t      offCur   = pStream->off;
     692    size_t      iCurLine = pStream->iLine;
     693    const char *pszLine  = ScmStreamGetLineByNo(pStream, iCurLine, pcchLine, penmEol);
     694    if (   pszLine
     695        && pStream->paLines[iCurLine].off < offCur)
     696    {
     697        offCur -= pStream->paLines[iCurLine].off;
     698        Assert(offCur <= pStream->paLines[iCurLine].off);
     699        *pcchLine -= offCur;
     700        pszLine   += offCur;
     701    }
     702    return pszLine;
    649703}
    650704
  • trunk/src/bldprogs/scmstream.h

    r40554 r41179  
    9898int         ScmStreamCheckItegrity(PSCMSTREAM pStream);
    9999int         ScmStreamWriteToFile(PSCMSTREAM pStream, const char *pszFilenameFmt, ...);
     100int         ScmStreamWriteToStdOut(PSCMSTREAM pStream);
    100101size_t      ScmStreamTell(PSCMSTREAM pStream);
    101102size_t      ScmStreamTellLine(PSCMSTREAM pStream);
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette