VirtualBox

Changeset 2789 in kBuild for trunk/src/kmk/kmk_cc_exec.c


Ignore:
Timestamp:
Sep 12, 2015 11:44:09 PM (10 years ago)
Author:
bird
Message:

kmk_cc_exec.c: More code for the makefile 'compiler'. (Hacking in progress.)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kmk/kmk_cc_exec.c

    r2788 r2789  
    4242#include <stdarg.h>
    4343#include <assert.h>
     44#include "k/kDefs.h"
     45#include "k/kTypes.h"
    4446
    4547
     
    117119
    118120
     121/** @def KMK_CC_IS_SPACE_CH
     122 * Checks if it's a space char. */
     123#define KMK_CC_IS_SPACE_CH(a_ch)                    isspace((unsigned char)(a_ch))
     124
     125
     126/** @defgroup grp_kmk_cc_evalprog Makefile Evaluation
     127 * @{
     128 */
     129/** @name KMK_CC_EVAL_QUALIFIER_XXX - Variable qualifiers.
     130 * @{ */
     131#define KMK_CC_EVAL_QUALIFIER_LOCAL         1
     132#define KMK_CC_EVAL_QUALIFIER_EXPORT        2
     133#define KMK_CC_EVAL_QUALIFIER_OVERRIDE      4
     134#define KMK_CC_EVAL_QUALIFIER_PRIVATE       8
     135/** @} */
     136
     137/** Eval: Max nesting depth of makefile conditionals.
     138 * Affects stack usage in kmk_cc_eval_compile_worker.  */
     139#define KMK_CC_EVAL_MAX_IF_DEPTH            32
     140/** Eval: Maximum number of escaped end of line sequences to track.
     141 * Affects stack usage in kmk_cc_eval_compile_worker, but not the actual
     142 * number of consequtive escaped newlines in the input file/variable. */
     143#define KMK_CC_EVAL_MAX_ESC_EOLS            2
     144
     145/** Minimum keyword length. */
     146#define KMK_CC_EVAL_KEYWORD_MIN             2
     147/** Maximum keyword length. */
     148#define KMK_CC_EVAL_KEYWORD_MAX             16
     149
     150/** @name KMK_CC_EVAL_CH_XXX - flags found in g_abEvalCcChars.
     151 * @{ */
     152/** Normal character, nothing special. */
     153#define KMK_CC_EVAL_CH_NORMAL                       UINT8_C(0)
     154/** Blank character. */
     155#define KMK_CC_EVAL_CH_BLANK                        UINT8_C(1)
     156#define KMK_CC_EVAL_IS_BLANK(a_ch)                  (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_BLANK)
     157/** Space character. */
     158#define KMK_CC_EVAL_CH_SPACE                        UINT8_C(2)
     159#define KMK_CC_EVAL_IS_SPACE(a_ch)                  (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_SPACE)
     160/** Space character or potential EOL escape backslash. */
     161#define KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH           UINT8_C(4)
     162#define KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(a_ch)     (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH)
     163/** Possible EOL character. */
     164#define KMK_CC_EVAL_CH_EOL_CANDIDATE                UINT8_C(8)
     165#define KMK_CC_EVAL_IS_EOL_CANDIDATE(a_ch)          (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_EOL_CANDIDATE)
     166/** First character in a keyword. */
     167#define KMK_CC_EVAL_CH_1ST_IN_KEYWORD               UINT8_C(16)
     168#define KMK_CC_EVAL_IS_1ST_IN_KEYWORD(a_ch)         (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_1ST_IN_KEYWORD)
     169/** Second character in a keyword. */
     170#define KMK_CC_EVAL_CH_2ND_IN_KEYWORD               UINT8_C(32)
     171#define KMK_CC_EVAL_IS_2ND_IN_KEYWORD(a_ch)         (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_2ND_IN_KEYWORD)
     172/** First character in a variable qualifier keyword or 'define'. */
     173#define KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD      UINT8_C(64)
     174#define KMK_CC_EVAL_IS_1ST_IN_VARIABLE_KEYWORD(a_ch) (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD)
     175/** Assignment punctuation character. */
     176#define KMK_CC_EVAL_CH_ASSIGNMENT                   UINT8_C(128)
     177#define KMK_CC_EVAL_IS_ASSIGNMENT(a_ch)             (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_ASSIGNMENT)
     178/** @} */
     179
     180/** Sets a bitmap entry.
     181 * @param   a_abBitmap      Typically g_abEvalCcChars.
     182 * @param   a_ch            The character to set.
     183 * @param   a_uVal          The value to OR in.  */
     184#define KMK_CC_EVAL_BM_OR(g_abBitmap, a_ch, a_uVal) do { (g_abBitmap)[(unsigned char)(a_ch)] |= (a_uVal); } while (0)
     185
     186/** Gets a bitmap entry.
     187 * @returns The value corresponding to @a a_ch.
     188 * @param   a_abBitmap      Typically g_abEvalCcChars.
     189 * @param   a_ch            The character to set. */
     190#define KMK_CC_EVAL_BM_GET(g_abBitmap, a_ch)        ( (g_abBitmap)[(unsigned char)(a_ch)] )
     191
     192/** @} */
     193
     194
    119195/*********************************************************************************************************************************
    120196*   Structures and Typedefs                                                                                                      *
     
    139215
    140216
    141 /** @name String Expansion
     217/** @defgroup grp_kmk_cc_exp  String Expansion
    142218 * @{*/
    143219
     
    406482
    407483
    408 /** @name Makefile Evaluation
     484/** @addtogroup grp_kmk_cc_evalprog
    409485 * @{  */
    410486
     
    487563    /** Adds more commands to the current recipe - KMKCCEVALRECIPECOMMANDS. */
    488564    kKmkCcEvalInstr_recipe_commands,
    489     /** Adds more commands to the current recipe - KMKCCEVALRECIPECOMMANDS. */
    490     kKmkCcEvalInstr_recipe_vari,
    491565    /** Special instruction for indicating the end of the recipe commands - KMKCCEVALCORE. */
    492566    kKmkCcEvalInstr_recipe_end,
     
    602676    PKMKCCEVALCORE          pNextFalse;
    603677    /** Pointer to the previous conditional for 'else if*' directives.
    604      * This is to assist the compilation process. */
    605     PKMKCCEVALCORE          pPrevCond;
     678     * This is only to assist the compilation process. */
     679    struct kmk_cc_eval_if_core *pPrevCond;
     680    /** Pointer to the jump out of the true block, if followed by 'else'.
     681     * This is only to assist the compilation process. */
     682    PKMKCCEVALJUMP          pTrueEndJump;
    606683} KMKCCEVALIFCORE;
    607684typedef KMKCCEVALIFCORE *PKMKCCEVALIFCORE;
     
    633710    KMKCCEXPSUBPROG         NameSubProg;
    634711} KMKCCEVALIFDEFDYNAMIC;
    635 KMK_CC_STATIC_ASSERT(sizeof(KMKCCEVALIFDEFDYNAMIC) == 48 || sizeof(void *) != 8);
    636712typedef KMKCCEVALIFDEFDYNAMIC *PKMKCCEVALIFDEFDYNAMIC;
    637713
     
    752828     * counts.  Expressions will be expanded and split on space.
    753829     *
    754      * The KMKCCEXPSUBPROGORPLAIN::bUser member is used to indicate secondary
    755      * expansion for a plain text entry.
     830     * The KMKCCEXPSUBPROGORPLAIN::bUser member one of KMKCCEVALRECIPE_FD_XXX.
    756831     *
    757832     * @todo Plain text file name could be replaced by file string cache entries. */
     
    762837 *  files. */
    763838#define KMKCCEVALRECIPE_SIZE(a_cFilesAndDeps) KMK_CC_SIZEOF_VAR_STRUCT(KMKCCEVALRECIPE, aFilesAndDeps, a_cFilesAndDeps)
     839/** @name KMKCCEVALRECIPE_FD_XXX - Values for KMKCCEVALRECIPE::aFilesAndDeps[x].bUser
     840 * @{  */
     841#define KMKCCEVALRECIPE_FD_NORMAL                   0
     842#define KMKCCEVALRECIPE_FD_SEC_EXP                  1
     843#define KMKCCEVALRECIPE_FD_SPECIAL_POSIX            2
     844#define KMKCCEVALRECIPE_FD_SPECIAL_SECONDEXPANSION  3
     845#define KMKCCEVALRECIPE_FD_SPECIAL_ONESHELL         4
     846/** @} */
     847
    764848
    765849/**
     
    815899    /** List of blocks for this program (LIFO). */
    816900    PKMKCCBLOCK             pBlockTail;
    817 
     901    /** The name of the file containing this program. */
     902    const char             *pszFilename;
     903    /** The name of the variable containing this program, if applicable.  */
     904    const char             *pszVarName;
     905#ifdef KMK_CC_STRICT
     906    /** The hash of the input string.  Used to check that we get all the change
     907     * notifications we require. */
     908    uint32_t                uInputHash;
     909#endif
     910    /** Reference count. */
     911    uint32_t volatile       cRefs;
    818912} KMKCCEVALPROG;
     913typedef KMKCCEVALPROG *PKMKCCEVALPROG;
    819914
    820915/** @} */
     
    826921static uint32_t g_cVarForExpandCompilations = 0;
    827922static uint32_t g_cVarForExpandExecs = 0;
     923static uint32_t g_cVarForEvalCompilations = 0;
     924static uint32_t g_cVarForEvalExecs = 0;
     925static uint32_t g_cFileForEvalCompilations = 0;
     926static uint32_t g_cFileForEvalExecs = 0;
    828927#ifdef KMK_CC_WITH_STATS
    829928static uint32_t g_cBlockAllocated = 0;
    830929static uint32_t g_cbAllocated = 0;
     930
    831931static uint32_t g_cBlocksAllocatedExpProgs = 0;
    832932static uint32_t g_cbAllocatedExpProgs = 0;
     
    835935static uint32_t g_cMultiBlockExpProgs = 0;
    836936static uint32_t g_cbUnusedMemExpProgs = 0;
     937
     938static uint32_t g_cBlocksAllocatedEvalProgs = 0;
     939static uint32_t g_cbAllocatedEvalProgs = 0;
     940static uint32_t g_cSingleBlockEvalProgs = 0;
     941static uint32_t g_cTwoBlockEvalProgs = 0;
     942static uint32_t g_cMultiBlockEvalProgs = 0;
     943static uint32_t g_cbUnusedMemEvalProgs = 0;
     944
    837945#endif
     946
     947/** Bitmap taking 'unsigned char' index.
     948 * ASSUMES unsigned char is 8-bits. */
     949static uint8_t  g_abEvalCcChars[256];
     950
     951/**
     952 * Makefile evaluation keywords.
     953 */
     954static const char * const g_apszEvalKeywords[] =
     955{
     956    "define",
     957    "export",
     958    "else",
     959    "endef",
     960    "endif",
     961    "ifdef",
     962    "ifndef",
     963    "ifeq",
     964    "ifneq",
     965    "if1of",
     966    "ifn1of",
     967    "if",
     968    "include",
     969    "includedep",
     970    "includedep-queue",
     971    "includedep-flush",
     972    "local",
     973    "override",
     974    "private",
     975    "sinclude",
     976    "unexport",
     977    "undefine",
     978    "vpath",
     979    "-include",
     980};
    838981
    839982
     
    850993void kmk_cc_init(void)
    851994{
     995    unsigned i;
     996
     997    /*
     998     * Initialize the bitmap.
     999     */
     1000    memset(g_abEvalCcChars, 0, sizeof(g_abEvalCcChars));
     1001
     1002    /* blank chars */
     1003    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ' ',  KMK_CC_EVAL_CH_BLANK);
     1004    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\t', KMK_CC_EVAL_CH_BLANK);
     1005
     1006    /* space chars and zero terminator. */
     1007    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ' ',  KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH);
     1008    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\t', KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH);
     1009    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\n', KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_EOL_CANDIDATE);
     1010    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\v', KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH);
     1011    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\f', KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH);
     1012    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\r', KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_EOL_CANDIDATE);
     1013    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\\', KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH);
     1014
     1015    /* keywords  */
     1016    for (i = 0; i < K_ELEMENTS(g_apszEvalKeywords); i++)
     1017    {
     1018        size_t cch = strlen(g_apszEvalKeywords[i]);
     1019        KMK_CC_ASSERT(cch >= KMK_CC_EVAL_KEYWORD_MIN);
     1020        KMK_CC_ASSERT(cch <= KMK_CC_EVAL_KEYWORD_MAX);
     1021
     1022        KMK_CC_EVAL_BM_OR(g_abEvalCcChars, g_apszEvalKeywords[i][0], KMK_CC_EVAL_CH_1ST_IN_KEYWORD);
     1023        KMK_CC_EVAL_BM_OR(g_abEvalCcChars, g_apszEvalKeywords[i][1], KMK_CC_EVAL_CH_2ND_IN_KEYWORD);
     1024    }
     1025    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'd', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* define */
     1026    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'e', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* export (, endef) */
     1027    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'l', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* local */
     1028    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'o', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* override */
     1029    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'p', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* private */
     1030    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'u', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* undefine */
     1031
     1032    /* Assignment punctuation. */
     1033    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '=', KMK_CC_EVAL_CH_ASSIGNMENT);
     1034    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ':', KMK_CC_EVAL_CH_ASSIGNMENT);
     1035    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '<', KMK_CC_EVAL_CH_ASSIGNMENT);
     1036    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '?', KMK_CC_EVAL_CH_ASSIGNMENT);
     1037    KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '+', KMK_CC_EVAL_CH_ASSIGNMENT);
    8521038}
    8531039
     
    8581044void kmk_cc_print_stats(void)
    8591045{
     1046#ifdef KMK_CC_WITH_STATS
     1047    uint32_t const cEvalCompilations = g_cFileForEvalCompilations + g_cVarForEvalCompilations;
     1048#endif
     1049
    8601050    puts(_("\n# The kmk 'compiler' and kmk 'program executor':\n"));
    8611051
     
    8801070           g_cbUnusedMemExpProgs, (uint32_t)((uint64_t)g_cbUnusedMemExpProgs * 100 / g_cbAllocatedExpProgs),
    8811071           g_cbUnusedMemExpProgs / g_cBlocksAllocatedExpProgs);
    882 
     1072    puts("");
     1073#endif
     1074    printf(_("# Variables compiled for string eval:      %6u\n"), g_cVarForEvalCompilations);
     1075    printf(_("# Variables string eval runs:              %6u\n"), g_cVarForEvalExecs);
     1076    printf(_("# String evals runs per compile:           %6u\n"), g_cVarForEvalExecs / g_cVarForEvalCompilations);
     1077    printf(_("# Files compiled:                          %6u\n"), g_cFileForEvalCompilations);
     1078    printf(_("# Files runs:                              %6u\n"), g_cFileForEvalExecs);
     1079    printf(_("# Files eval runs per compile:             %6u\n"), g_cFileForEvalExecs / g_cFileForEvalCompilations);
     1080#ifdef KMK_CC_WITH_STATS
     1081    printf(_("#         Single alloc block eval progs:   %6u (%u%%)\n"
     1082             "#            Two alloc block eval progs:   %6u (%u%%)\n"
     1083             "#  Three or more alloc block eval progs:   %6u (%u%%)\n"
     1084             ),
     1085           g_cSingleBlockEvalProgs, (uint32_t)((uint64_t)g_cSingleBlockEvalProgs * 100 / cEvalCompilations),
     1086           g_cTwoBlockEvalProgs,    (uint32_t)((uint64_t)g_cTwoBlockEvalProgs    * 100 / cEvalCompilations),
     1087           g_cMultiBlockEvalProgs,  (uint32_t)((uint64_t)g_cMultiBlockEvalProgs  * 100 / cEvalCompilations));
     1088    printf(_("# Total amount of memory for eval progs: %8u bytes\n"
     1089             "#                                    in:   %6u blocks\n"
     1090             "#                        avg block size:   %6u bytes\n"
     1091             "#                         unused memory: %8u bytes (%u%%)\n"
     1092             "#           avg unused memory per block:   %6u bytes\n"
     1093             "\n"),
     1094           g_cbAllocatedEvalProgs, g_cBlocksAllocatedEvalProgs, g_cbAllocatedEvalProgs / g_cBlocksAllocatedEvalProgs,
     1095           g_cbUnusedMemEvalProgs, (uint32_t)((uint64_t)g_cbUnusedMemEvalProgs * 100 / g_cbAllocatedEvalProgs),
     1096           g_cbUnusedMemEvalProgs / g_cBlocksAllocatedEvalProgs);
     1097    puts("");
    8831098    printf(_("#   Total amount of block mem allocated: %8u bytes\n"), g_cbAllocated);
    8841099    printf(_("#       Total number of block allocated: %8u\n"), g_cBlockAllocated);
     
    9781193
    9791194    KMK_CC_ASSERT_ALIGNED(cbFirst, sizeof(void *));
     1195    KMK_CC_ASSERT(cbFirst <= 128);
    9801196
    9811197    /*
    9821198     * Turn the hint into a block size.
    9831199     */
     1200    cbHint += cbFirst;
    9841201    if (cbHint <= 512)
    9851202    {
    9861203        if (cbHint <= 256)
    987             cbBlock = 128;
     1204        {
     1205            if (cbFirst <= 64)
     1206                cbBlock = 128;
     1207            else
     1208                cbBlock = 256;
     1209        }
    9881210        else
    9891211            cbBlock = 256;
     
    12101432
    12111433/**
     1434 * Grows the allocation with another block, makefile evaluation program case.
     1435 *
     1436 * @returns Pointer to a makefile evaluation instruction core.
     1437 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     1438 * @param   cb                  The number of bytes to allocate.
     1439 */
     1440static PKMKCCEVALCORE kmk_cc_block_alloc_eval_grow(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
     1441{
     1442    PKMKCCBLOCK     pOldBlock = *ppBlockTail;
     1443    PKMKCCBLOCK     pNewBlock;
     1444    PKMKCCEVALCORE  pRet;
     1445    PKMKCCEVALJUMP  pJump;
     1446
     1447    /* Figure the block size. */
     1448    uint32_t cbBlock = !pOldBlock->pNext ? 128 : pOldBlock->cbBlock;
     1449    while (cbBlock - sizeof(KMKCCEVALJUMP) - sizeof(*pNewBlock) < cb)
     1450        cbBlock *= 2;
     1451
     1452    /* Allocate and initialize the block it with the new instruction already accounted for. */
     1453    pNewBlock = (PKMKCCBLOCK)xmalloc(cbBlock);
     1454    pNewBlock->cbBlock = cbBlock;
     1455    pNewBlock->offNext = sizeof(*pNewBlock) + cb;
     1456    pNewBlock->pNext   = pOldBlock;
     1457    *ppBlockTail = pNewBlock;
     1458
     1459#ifdef KMK_CC_WITH_STATS
     1460    g_cBlockAllocated++;
     1461    g_cbAllocated += cbBlock;
     1462#endif
     1463
     1464    pRet = (PKMKCCEVALCORE)(pNewBlock + 1);
     1465
     1466    /* Emit jump. */
     1467    pJump = (PKMKCCEVALJUMP)((char *)pOldBlock + pOldBlock->offNext);
     1468    pJump->Core.enmOpCode = kKmkCcEvalInstr_jump;
     1469    pJump->pNext = pRet;
     1470    pOldBlock->offNext += sizeof(*pJump);
     1471    KMK_CC_ASSERT(pOldBlock->offNext <= pOldBlock->cbBlock);
     1472
     1473    return pRet;
     1474}
     1475
     1476
     1477/**
     1478 * Allocates a makefile evaluation instruction of size @a cb.
     1479 *
     1480 * @returns Pointer to a makefile evaluation instruction core.
     1481 * @param   ppBlockTail         Pointer to the allocator tail pointer.
     1482 * @param   cb                  The number of bytes to allocate.
     1483 */
     1484static PKMKCCEVALCORE kmk_cc_block_alloc_eval(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
     1485{
     1486    PKMKCCBLOCK pBlockTail = *ppBlockTail;
     1487    uint32_t    cbLeft = pBlockTail->cbBlock - pBlockTail->offNext;
     1488
     1489    KMK_CC_ASSERT(cbLeft >= sizeof(KMKCCEVALJUMP));
     1490    KMK_CC_ASSERT( (cb & (sizeof(void *) - 1)) == 0 );
     1491
     1492    if (cbLeft >= cb + sizeof(KMKCCEVALJUMP))
     1493    {
     1494        PKMKCCEVALCORE pRet = (PKMKCCEVALCORE)((char *)pBlockTail + pBlockTail->offNext);
     1495        pBlockTail->offNext += cb;
     1496        return pRet;
     1497    }
     1498    return kmk_cc_block_alloc_eval_grow(ppBlockTail, cb);
     1499}
     1500
     1501
     1502/**
    12121503 * Frees all memory used by an allocator.
    12131504 *
     
    12241515}
    12251516
    1226    /*
     1517
     1518/*
    12271519 *
    12281520 * The string expansion compiler.
     
    24392731 *
    24402732 */
     2733
     2734static size_t kmk_cc_eval_detect_eol_style(char *pchFirst, char *pchSecond, const char *pszContent, size_t cchContent)
     2735{
     2736    /* Look for LF first. */
     2737    const char *pszTmp = (const char *)memchr(pszContent, '\n', cchContent);
     2738    if (pszTmp)
     2739    {
     2740        /* CRLF? */
     2741        if (pszTmp != pszContent && pszTmp[-1] == '\r')
     2742        {
     2743            *pchFirst = '\r';
     2744            *pchSecond = '\n';
     2745            return 2;
     2746        }
     2747
     2748        /* No, LF or LFCR. (pszContent is zero terminated, so no bounds checking necessary.) */
     2749        *pchFirst = '\n';
     2750        if (pszTmp[1] != '\r')
     2751        {
     2752            *pchSecond = 0;
     2753            return 1;
     2754        }
     2755        *pchSecond = '\r';
     2756        return 2;
     2757    }
     2758
     2759    /* Probably no EOLs here. */
     2760    if (memchr(pszContent, '\r', cchContent) == NULL)
     2761    {
     2762        *pchSecond = *pchFirst = 0;
     2763        return 0;
     2764    }
     2765
     2766    /* kind of unlikely */
     2767    *pchFirst  = '\r';
     2768    *pchSecond = 0;
     2769    return 1;
     2770}
     2771
     2772
     2773/**
     2774 * Checks whether we've got an EOL escape sequence or not.
     2775 *
     2776 * @returns non-zero if escaped EOL, 0 if not (i.e. actual EOL).
     2777 * @param   pszContent          The string pointer @a offEol is relative to.
     2778 * @param   offEol              The offset of the first EOL char.
     2779 */
     2780static unsigned kmk_cc_eval_is_eol_escape_seq(const char *pszContent, size_t offEol)
     2781{
     2782    /* The caller has already checked out two backslashes. */
     2783    size_t offFirstBackslash = offEol;
     2784    KMK_CC_ASSERT(offFirstBackslash >= 2);
     2785    offFirstBackslash -= 2;
     2786
     2787    /* Find the first backslash. */
     2788    while (offFirstBackslash > 0 && pszContent[offFirstBackslash - 1] == '\\')
     2789        offFirstBackslash--;
     2790
     2791    /* Odd number -> escaped EOL; Even number -> real EOL; */
     2792    return (offEol - offFirstBackslash) & 1;
     2793}
     2794
     2795
     2796
     2797typedef enum kmk_cc_eval_token
     2798{
     2799    /** Invalid token . */
     2800    kKmkCcEvalToken_Invalid = 0,
     2801
     2802    /** Assignment: '=' */
     2803    kKmkCcEvalToken_AssignRecursive,
     2804    /** Assignment: ':=' */
     2805    kKmkCcEvalToken_AssignSimple,
     2806    /** Assignment: '+=' */
     2807    kKmkCcEvalToken_AssignAppend,
     2808    /** Assignment: '<=' */
     2809    kKmkCcEvalToken_AssignPrepend,
     2810    /** Assignment: '?=' */
     2811    kKmkCcEvalToken_AssignIfNew,
     2812    /** Assignment: 'define' */
     2813    kKmkCcEvalToken_define,
     2814    /** Unassignment: 'undefine' */
     2815    kKmkCcEvalToken_undefine,
     2816
     2817    /* Assignment modifier: 'local'  */
     2818    kKmkCcEvalToken_local,
     2819    /* Assignment modifier: 'override' */
     2820    kKmkCcEvalToken_override,
     2821    /* Assignment modifier: 'private' (target variable not inh by deps) */
     2822    kKmkCcEvalToken_private,
     2823    /* Assignment modifier / other variable thing: 'export' */
     2824    kKmkCcEvalToken_export,
     2825    /* Other variable thing: 'unexport' */
     2826    kKmkCcEvalToken_unexport,
     2827
     2828    kKmkCcEvalToken_ifdef,
     2829    kKmkCcEvalToken_ifndef,
     2830    kKmkCcEvalToken_ifeq,
     2831    kKmkCcEvalToken_ifneq,
     2832    kKmkCcEvalToken_if1of,
     2833    kKmkCcEvalToken_ifn1of,
     2834    kKmkCcEvalToken_if,
     2835    kKmkCcEvalToken_else,
     2836    kKmkCcEvalToken_endif,
     2837
     2838    kKmkCcEvalToken_include,
     2839    kKmkCcEvalToken_include_silent,
     2840    kKmkCcEvalToken_includedep,
     2841    kKmkCcEvalToken_includedep_queue,
     2842    kKmkCcEvalToken_includedep_flush,
     2843
     2844    kKmkCcEvalToken_colon,
     2845    kKmkCcEvalToken_double_colon,
     2846    kKmkCcEvalToken_plus,
     2847    kKmkCcEvalToken_plus_maybe,
     2848
     2849    kKmkCcEvalToken_vpath,
     2850
     2851    /** Plain word. */
     2852    kKmkCcEvalToken_WordPlain,
     2853    /** Word that maybe in need of expanding. */
     2854    kKmkCcEvalToken_WordWithDollar,
     2855
     2856    kKmkCcEvalToken_End
     2857} KMKCCEVALTOKEN;
     2858
     2859/**
     2860 * A tokenized word.
     2861 */
     2862typedef struct kmk_cc_eval_word
     2863{
     2864    /** The token word (lexeme).   */
     2865    const char         *pszWord;
     2866    /** The length of the word (lexeme). */
     2867    uint32_t            cchWord;
     2868    /** The token classification. */
     2869    KMKCCEVALTOKEN      enmToken;
     2870} KMKCCEVALWORD;
     2871typedef KMKCCEVALWORD *PKMKCCEVALWORD;
     2872
     2873
     2874/**
     2875 * Escaped end-of-line sequence in the current line.
     2876 *
     2877 * This is initially noting down the start of the escape sequence and the EOL
     2878 * marker.  But after kmk_cc_eval_prep_command_line or
     2879 * kmk_cc_eval_prep_normal_line it will change into copy instructions.  The
     2880 * offEsc is the end position of the current line, while offEol is the start of
     2881 * the next line.  If the next line doesn't contain anything to copy, offEsc
     2882 * will contain the same value as offEol on the previous.
     2883 */
     2884typedef struct KMKCCEVALESCEOL
     2885{
     2886    /** Offset at which the EOL escape sequence starts for a non-command line.
     2887     * After prepping the line, this will be moved back to indicate the end of
     2888     * the copy sequence for that line. */
     2889    size_t              offEsc;
     2890    /** Offset of the newline sequence.
     2891     * After prepping the line, this will indicate where to start copying on the
     2892     * following line.  This may be the same as the offEsc in the next entry. */
     2893    size_t              offEol;
     2894} KMKCCEVALESCEOL;
     2895typedef KMKCCEVALESCEOL *PKMKCCEVALESCEOL;
     2896
     2897
     2898typedef struct KMKCCEVALCOMPILER
     2899{
     2900    /** Pointer to the KMKCCEVALPROG::pBlockTail member.  */
     2901    PKMKCCBLOCK        *ppBlockTail;
     2902
     2903    /** @name Line parsing state.
     2904     * @{ */
     2905    /** Offset of newline escape sequences in the current line.
     2906     * This is only applicable if cEscEols is not zero.  */
     2907    PKMKCCEVALESCEOL    paEscEols;
     2908    /** The number of number of paEscEols entries we've allocated. */
     2909    unsigned            cEscEolsAllocated;
     2910    /** Number of escaped EOLs (line count - 1). */
     2911    unsigned            cEscEols;
     2912    /** The paEscEols entry corresponding to the current parsing location.
     2913     * Still to be seen how accurate this can be made to be. */
     2914    unsigned            iEscEol;
     2915
     2916    /** The current line number (for error handling / debugging). */
     2917    unsigned            iLine;
     2918    /** The start offset of the current line. */
     2919    size_t              offLine;
     2920    /** Length of the current line, sans the final EOL and comments. */
     2921    size_t              cchLine;
     2922
     2923    /** The first char in an EOL sequence.
     2924     * We ASSUMES that this char won't appear in any other sequence in the file,
     2925     * thus skipping matching any subsequent chars. */
     2926    char                chFirstEol;
     2927    /** The second char in an EOL sequence, if applicable. */
     2928    char                chSecondEol;
     2929
     2930    /** The length of the EOL sequence. */
     2931    size_t              cchEolSeq;
     2932    /** The minimum length of an esacped EOL sequence (cchEolSeq + 1). */
     2933    size_t              cchEscEolSeq;
     2934    /** @} */
     2935
     2936
     2937    /** @name Recipe state.
     2938     * @{ */
     2939    /** Set if we're working on a recipe. */
     2940    PKMKCCEVALRECIPE    pRecipe;
     2941    /** Set for ignoring recipes without targets (Sun OS 4 Make). */
     2942    uint8_t             fNoTargetRecipe;
     2943    /** The command prefix character. */
     2944    char                chCmdPrefix;
     2945    /** @} */
     2946
     2947    /** @name Tokenzied words.
     2948     * @{ */
     2949    uint32_t            cWords;
     2950    uint32_t            cWordsAllocated;
     2951    PKMKCCEVALWORD      paWords;
     2952    /** @} */
     2953
     2954    /** @name Conditionals.
     2955     * @{ */
     2956    /** Current conditional stack depth. */
     2957    unsigned            cIfs;
     2958    /** The conditional directive stack. */
     2959    PKMKCCEVALIFCORE    apIfs[KMK_CC_EVAL_MAX_IF_DEPTH];
     2960    /** @} */
     2961
     2962    /** The program being compiled. */
     2963    PKMKCCEVALPROG      pEvalProg;
     2964    /** Pointer to the content. */
     2965    const char         *pszContent;
     2966    /** The amount of input to parse. */
     2967    size_t              cchContent;
     2968} KMKCCEVALCOMPILER;
     2969typedef KMKCCEVALCOMPILER *PKMKCCEVALCOMPILER;
     2970
     2971
     2972static void kmk_cc_eval_init_compiler(PKMKCCEVALCOMPILER pCompiler, PKMKCCEVALPROG pEvalProg, unsigned iLine,
     2973                                      const char *pszContent, size_t cchContent)
     2974{
     2975    pCompiler->ppBlockTail      = &pEvalProg->pBlockTail;
     2976
     2977    pCompiler->pRecipe          = NULL;
     2978    pCompiler->fNoTargetRecipe  = 0;
     2979    pCompiler->chCmdPrefix      = cmd_prefix;
     2980
     2981    pCompiler->cWordsAllocated  = 0;
     2982    pCompiler->paWords          = NULL;
     2983
     2984    pCompiler->cEscEolsAllocated = 0;
     2985    pCompiler->paEscEols        = NULL;
     2986    pCompiler->iLine            = iLine;
     2987
     2988    pCompiler->cIfs             = 0;
     2989
     2990    pCompiler->pEvalProg        = pEvalProg;
     2991    pCompiler->pszContent       = pszContent;
     2992    pCompiler->cchContent       = cchContent;
     2993
     2994    /* Detect EOL style. */
     2995    pCompiler->cchEolSeq        = kmk_cc_eval_detect_eol_style(&pCompiler->chFirstEol, &pCompiler->chSecondEol,
     2996                                                               pszContent, cchContent);
     2997    pCompiler->cchEscEolSeq     = 1 + pCompiler->cchEolSeq;
     2998}
     2999
     3000
     3001static void kmk_cc_eval_delete_compiler(PKMKCCEVALCOMPILER pCompiler)
     3002{
     3003    if (pCompiler->paWords)
     3004        free(pCompiler->paWords);
     3005    if (pCompiler->paEscEols)
     3006        free(pCompiler->paEscEols);
     3007}
     3008
     3009static void kmk_cc_eval_fatal(PKMKCCEVALCOMPILER pCompiler, const char *pchWhere, const char *pszMsg, ...)
     3010{
     3011    va_list  va;
     3012    unsigned iLine = pCompiler->iLine;
     3013
     3014    log_working_directory(1);
     3015
     3016    /*
     3017     * If we have a pointer location, use it to figure out the exact line and column.
     3018     */
     3019    if (pchWhere)
     3020    {
     3021        size_t   offLine = pCompiler->offLine;
     3022        size_t   off     = pchWhere - pCompiler->pszContent;
     3023        unsigned i       = 0;
     3024        while (   i   < pCompiler->cEscEols
     3025               && off > pCompiler->paEscEols[i].offEol)
     3026        {
     3027            offLine = pCompiler->paEscEols[i].offEol + 1 + pCompiler->cchEolSeq;
     3028            iLine++;
     3029            i++;
     3030        }
     3031        KMK_CC_ASSERT(off <= pCompiler->cchContent);
     3032
     3033        if (pCompiler->pEvalProg->pszVarName)
     3034            fprintf(stderr, "%s:%u:%u: *** fatal parsing error in %s: ",
     3035                    pCompiler->pEvalProg->pszFilename, iLine, (unsigned)(off - offLine), pCompiler->pEvalProg->pszVarName);
     3036        else
     3037            fprintf(stderr, "%s:%u:%u: *** fatal parsing error: ",
     3038                    pCompiler->pEvalProg->pszFilename, iLine, (unsigned)(off - offLine));
     3039    }
     3040    else if (pCompiler->pEvalProg->pszVarName)
     3041        fprintf(stderr, "%s:%u: *** fatal parsing error in %s: ",
     3042                pCompiler->pEvalProg->pszFilename, iLine, pCompiler->pEvalProg->pszVarName);
     3043    else
     3044        fprintf(stderr, "%s:%u: *** fatal parsing error: ",
     3045                pCompiler->pEvalProg->pszFilename, iLine);
     3046
     3047    /*
     3048     * Print the message and die.
     3049     */
     3050    va_start(va, pszMsg);
     3051    vfprintf(stderr, pszMsg, va);
     3052    va_end(va);
     3053    fputs(".  Stop.\n", stderr);
     3054
     3055    for (;;)
     3056        die(2);
     3057}
     3058
     3059
     3060static void kmk_cc_eval_fatal_eol(PKMKCCEVALCOMPILER pCompiler, const char *pchEol, unsigned iLine, size_t offLine)
     3061{
     3062    pCompiler->iLine   = iLine;
     3063    pCompiler->offLine = offLine;
     3064
     3065    for (;;)
     3066        kmk_cc_eval_fatal(pCompiler, pchEol, "Missing 2nd EOL character: found %#x instead of %#x\n",
     3067                                   pchEol, pCompiler->chSecondEol);
     3068}
     3069
     3070
     3071
     3072/** @name KMK_CC_WORD_COMP_CONST_XXX - Optimal(/insane) constant work matching.
     3073 * @{
     3074 */
     3075#if defined(KBUILD_ARCH_X86) || defined(KBUILD_ARCH_AMD64) /* Unaligned access is reasonably cheap. */
     3076# define KMK_CC_WORD_COMP_CONST_0(a_pchLine, a_pszWord) (1)
     3077# define KMK_CC_WORD_COMP_CONST_2(a_pchLine, a_pszWord) \
     3078        (   *(uint16_t const *)(a_pchLine)     == *(uint16_t const *)(a_pszWord) )
     3079# define KMK_CC_WORD_COMP_CONST_3(a_pchLine, a_pszWord) \
     3080        (   *(uint16_t const *)(a_pchLine)     == *(uint16_t const *)(a_pszWord) \
     3081         && (a_pchLine)[2]                     == (a_pszWord)[2] )
     3082# define KMK_CC_WORD_COMP_CONST_4(a_pchLine, a_pszWord) \
     3083        (   *(uint32_t const *)(a_pchLine)     == *(uint32_t const *)(a_pszWord) )
     3084# define KMK_CC_WORD_COMP_CONST_5(a_pchLine, a_pszWord) \
     3085        (   *(uint32_t const *)(a_pchLine)     == *(uint32_t const *)(a_pszWord) \
     3086         && (a_pchLine)[4]                     == (a_pszWord)[4] )
     3087# define KMK_CC_WORD_COMP_CONST_6(a_pchLine, a_pszWord) \
     3088        (   *(uint32_t const *)(a_pchLine)     == *(uint32_t const *)(a_pszWord) \
     3089         && ((uint16_t const *)(a_pchLine))[2] == ((uint32_t const *)(a_pszWord))[2] )
     3090# define KMK_CC_WORD_COMP_CONST_7(a_pchLine, a_pszWord) \
     3091        (   *(uint32_t const *)(a_pchLine)     == *(uint32_t const *)(a_pszWord) \
     3092         && ((uint16_t const *)(a_pchLine))[2] == ((uint32_t const *)(a_pszWord))[2] \
     3093         && (a_pchLine)[6]                     == (a_pszWord)[6] )
     3094# define KMK_CC_WORD_COMP_CONST_8(a_pchLine, a_pszWord) \
     3095        (   *(uint64_t const *)(a_pchLine)     == *(uint64_t const *)(a_pszWord) )
     3096# define KMK_CC_WORD_COMP_CONST_10(a_pchLine, a_pszWord) \
     3097        (   *(uint64_t const *)(a_pchLine)     == *(uint64_t const *)(a_pszWord) \
     3098         && ((uint16_t const *)(a_pchLine))[4] == ((uint16_t const *)(a_pszWord))[4] )
     3099# define KMK_CC_WORD_COMP_CONST_16(a_pchLine, a_pszWord) \
     3100        (   *(uint64_t const *)(a_pchLine)     == *(uint64_t const *)(a_pszWord) \
     3101         && ((uint64_t const *)(a_pchLine))[1] == ((uint64_t const *)(a_pszWord))[1] )
     3102#else
     3103# define KMK_CC_WORD_COMP_CONST_0(a_pchLine, a_pszWord) (1)
     3104# define KMK_CC_WORD_COMP_CONST_2(a_pchLine, a_pszWord) \
     3105        (   (a_pchLine)[0] == (a_pszWord)[0] \
     3106         && (a_pchLine)[1] == (a_pszWord)[1] )
     3107# define KMK_CC_WORD_COMP_CONST_3(a_pchLine, a_pszWord) \
     3108        (   (a_pchLine)[0] == (a_pszWord)[0] \
     3109         && (a_pchLine)[1] == (a_pszWord)[1] \
     3110         && (a_pchLine)[2] == (a_pszWord)[2] )
     3111# define KMK_CC_WORD_COMP_CONST_4(a_pchLine, a_pszWord) \
     3112        (   (a_pchLine)[0] == (a_pszWord)[0] \
     3113         && (a_pchLine)[1] == (a_pszWord)[1] \
     3114         && (a_pchLine)[2] == (a_pszWord)[2] \
     3115         && (a_pchLine)[3] == (a_pszWord)[3] )
     3116# define KMK_CC_WORD_COMP_CONST_5(a_pchLine, a_pszWord) \
     3117        (   (a_pchLine)[0] == (a_pszWord)[0] \
     3118         && (a_pchLine)[1] == (a_pszWord)[1] \
     3119         && (a_pchLine)[2] == (a_pszWord)[2] \
     3120         && (a_pchLine)[3] == (a_pszWord)[3] \
     3121         && (a_pchLine)[4] == (a_pszWord)[4] )
     3122# define KMK_CC_WORD_COMP_CONST_6(a_pchLine, a_pszWord) \
     3123        (   (a_pchLine)[0] == (a_pszWord)[0] \
     3124         && (a_pchLine)[1] == (a_pszWord)[1] \
     3125         && (a_pchLine)[2] == (a_pszWord)[2] \
     3126         && (a_pchLine)[3] == (a_pszWord)[3] \
     3127         && (a_pchLine)[4] == (a_pszWord)[4] \
     3128         && (a_pchLine)[5] == (a_pszWord)[5] )
     3129# define KMK_CC_WORD_COMP_CONST_7(a_pchLine, a_pszWord) \
     3130        (   (a_pchLine)[0] == (a_pszWord)[0] \
     3131         && (a_pchLine)[1] == (a_pszWord)[1] \
     3132         && (a_pchLine)[2] == (a_pszWord)[2] \
     3133         && (a_pchLine)[3] == (a_pszWord)[3] \
     3134         && (a_pchLine)[4] == (a_pszWord)[4] \
     3135         && (a_pchLine)[5] == (a_pszWord)[5] \
     3136         && (a_pchLine)[6] == (a_pszWord)[6] )
     3137# define KMK_CC_WORD_COMP_CONST_8(a_pchLine, a_pszWord) \
     3138        (   (a_pchLine)[0] == (a_pszWord)[0] \
     3139         && (a_pchLine)[1] == (a_pszWord)[1] \
     3140         && (a_pchLine)[2] == (a_pszWord)[2] \
     3141         && (a_pchLine)[3] == (a_pszWord)[3] \
     3142         && (a_pchLine)[4] == (a_pszWord)[4] \
     3143         && (a_pchLine)[5] == (a_pszWord)[5] \
     3144         && (a_pchLine)[6] == (a_pszWord)[6] \
     3145         && (a_pchLine)[7] == (a_pszWord)[7] )
     3146# define KMK_CC_WORD_COMP_CONST_10(a_pchLine, a_pszWord) \
     3147        (   (a_pchLine)[0] == (a_pszWord)[0] \
     3148         && (a_pchLine)[1] == (a_pszWord)[1] \
     3149         && (a_pchLine)[2] == (a_pszWord)[2] \
     3150         && (a_pchLine)[3] == (a_pszWord)[3] \
     3151         && (a_pchLine)[4] == (a_pszWord)[4] \
     3152         && (a_pchLine)[5] == (a_pszWord)[5] \
     3153         && (a_pchLine)[6] == (a_pszWord)[6] \
     3154         && (a_pchLine)[7] == (a_pszWord)[7] \
     3155         && (a_pchLine)[8] == (a_pszWord)[8] \
     3156         && (a_pchLine)[9] == (a_pszWord)[9] )
     3157# define KMK_CC_WORD_COMP_CONST_16(a_pchLine, a_pszWord) \
     3158        (   (a_pchLine)[0] == (a_pszWord)[0] \
     3159         && (a_pchLine)[1] == (a_pszWord)[1] \
     3160         && (a_pchLine)[2] == (a_pszWord)[2] \
     3161         && (a_pchLine)[3] == (a_pszWord)[3] \
     3162         && (a_pchLine)[4] == (a_pszWord)[4] \
     3163         && (a_pchLine)[5] == (a_pszWord)[5] \
     3164         && (a_pchLine)[6] == (a_pszWord)[6] \
     3165         && (a_pchLine)[7] == (a_pszWord)[7] \
     3166         && (a_pchLine)[8] == (a_pszWord)[8] \
     3167         && (a_pchLine)[9] == (a_pszWord)[9] \
     3168         && (a_pchLine)[10] == (a_pszWord)[10] \
     3169         && (a_pchLine)[11] == (a_pszWord)[11] \
     3170         && (a_pchLine)[12] == (a_pszWord)[12] \
     3171         && (a_pchLine)[13] == (a_pszWord)[13] \
     3172         && (a_pchLine)[14] == (a_pszWord)[14] \
     3173         && (a_pchLine)[15] == (a_pszWord)[15])
     3174#endif
     3175/** See if a starting of a given length starts with a constant word. */
     3176#define KMK_CC_WORD_COMP_CONST(a_pCompiler, a_pchLine, a_cchLine, a_pszWord, a_cchWord) \
     3177    (    (a_cchLine) >= (a_cchWord) \
     3178      && (   (a_cchLine) == (a_cchWord) \
     3179          || KMK_CC_EVAL_IS_SPACE((a_pchLine)[a_cchWord]) \
     3180          || ((a_pchLine)[a_cchWord] == '\\' && (a_pchLine)[(a_cchWord) + 1] == (a_pCompiler)->chFirstEol) ) \
     3181      && KMK_CC_WORD_COMP_CONST_##a_cchWord(a_pchLine, a_pszWord) )
     3182/** @} */
     3183
     3184
     3185/**
     3186 * Checks if a_ch is a space after a word.
     3187 *
     3188 * Since there is always a terminating zero, the user can safely access a char
     3189 * beyond @a a_cchLeft.  However, that byte isn't necessarily a zero terminator
     3190 * character, so we have to check @a a_cchLeft whether we're at the end of the
     3191 * parsing input string.
     3192 *
     3193 * @returns true / false.
     3194 * @param   a_pCompiler     The compiler instance data.
     3195 * @param   a_ch            The character to inspect.
     3196 * @param   a_ch2           The character following it, in case of escaped EOL.
     3197 * @param   a_cchLeft       The number of chars left to parse (from @a a_ch).
     3198 */
     3199#define KMK_CC_EVAL_IS_SPACE_AFTER_WORD(a_pCompiler, a_ch, a_ch2, a_cchLeft) \
     3200    (   a_cchLeft == 0 \
     3201     || KMK_CC_EVAL_IS_SPACE(a_ch) \
     3202     || ((a_ch) == '\\' && (a_ch2) == (a_pCompiler)->chFirstEol) )
     3203
     3204/**
     3205 * Used to skip spaces after a word.
     3206 *
     3207 * We ASSUME that the first char is a space or that we've reached the end of the
     3208 * string (a_cchLeft == 0).
     3209 *
     3210 * @param   a_pCompiler     The compiler instance data.
     3211 * @param   a_pchWord       The current input position, this will be moved to
     3212 *                          the start of the next word or end of the input.
     3213 * @param   a_cchLeft       The number of chars left to parse.  This will be
     3214 *                          updated.
     3215 */
     3216#define KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(a_pCompiler, a_pchWord, a_cchLeft) \
     3217    do { \
     3218        /* Skip the first char which is known to be a space, end of line or end of input. */ \
     3219        if ((a_cchLeft) > 0) \
     3220        { \
     3221            char const chSkipBlanksFirst = *(a_pchWord); \
     3222            KMK_CC_ASSERT(KMK_CC_EVAL_IS_SPACE_AFTER_WORD(a_pCompiler, chSkipBlanksFirst, (a_pchWord)[1], a_cchLeft)); \
     3223            if (chSkipBlanksFirst != '\\') \
     3224            { \
     3225                (a_pchWord) += 1; \
     3226                (a_cchLeft) -= 1; \
     3227                \
     3228                /* Another space or escaped EOL? Then there are probably more then, so call worker function. */ \
     3229                if ((a_cchLeft) > 0) \
     3230                { \
     3231                    char const chSkipBlanksSecond = *(a_pchWord); \
     3232                    if (KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(chSkipBlanksSecond)) \
     3233                        (a_pchWord) = kmk_cc_eval_skip_spaces_after_word_slow(a_pchWord, &(a_cchLeft), \
     3234                                                                              chSkipBlanksSecond, a_pCompiler); \
     3235                } \
     3236            } \
     3237            else /* escape sequences can be complicated. */ \
     3238                (a_pchWord) = kmk_cc_eval_skip_spaces_after_word_slow(a_pchWord, &(a_cchLeft), \
     3239                                                                      chSkipBlanksFirst, a_pCompiler); \
     3240        } \
     3241    } while (0)
     3242
     3243
     3244//static int kmk_cc_eval_skip_esc_eol_slow(const char *pchWord, size_t cchLeft, PKMKCCEVALCOMPILER pCompiler,
     3245//                                                  const char **ppchWord, size_t *pcchLeft)
     3246//{
     3247//    KMK_CC_ASSERT(pchWord[0] == '\\');
     3248//    if (pCompiler->cEscEols)
     3249//    {
     3250//        size_t cchMin = 1 + pCompiler->chFirstEol;
     3251//        if (cchLeft >= cchMin)
     3252//        {
     3253//            /* The simple case, no extra backslashes. */
     3254//            if (pchWord[1] == pCompiler->chFirstEol)
     3255//            {
     3256//                *pcchLeft  = cchLeft - cchMin;
     3257//                *pchWord   = pchWord + cchMin;
     3258//                return 1;
     3259//            }
     3260//
     3261//            /* The unlikly case,  */
     3262//
     3263//        }
     3264//    }
     3265//    return 0;
     3266//}
     3267
     3268
     3269/**
     3270 * The slow path of KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD.
     3271 *
     3272 * This is called to handles escaped EOL sequences, as these can involve
     3273 * multiple backslashes and therefore doesn't led themselves well to inlined
     3274 * code.
     3275 *
     3276 * The other case this is used for is to handle more than once space, since it's
     3277 * likely that when there are two there might be more.  No point in inlining
     3278 * that, better do some loop unrolling instead.
     3279 *
     3280 * @returns Points to the first non-space character or end of input.
     3281 * @param   pchWord             The current position. There is some kind of char
     3282 * @param   pcchLeft            Pointer to the cchLeft variable, this is both
     3283 *                              input and output.
     3284 * @param   ch                  The current character.
     3285 * @param   pCompiler           The compiler instance data.
     3286 */
     3287static const char *kmk_cc_eval_skip_spaces_after_word_slow(const char *pchWord, size_t *pcchLeft, char ch,
     3288                                                                    PKMKCCEVALCOMPILER pCompiler)
     3289{
     3290    size_t cchLeft = *pcchLeft;
     3291
     3292    /*
     3293     * Skip the pending space or EOL found by the caller.  We need to
     3294     * confirm the EOL.
     3295     *
     3296     * Note! We only need to care about simple backslash+EOL sequences here
     3297     *       since we're either at the end of a validated word, or we've already
     3298     *       skipped one space.  In the former case, someone else has already
     3299     *       validated the escape esequence, in the latter case multiple
     3300     *       backslashes would indicate a new word that that we should return.
     3301     */
     3302    if (ch != '\\')
     3303    {
     3304        pchWord += 1;
     3305        cchLeft -= 1;
     3306    }
     3307    else if (   cchLeft >= pCompiler->cchEscEolSeq
     3308             && pchWord[1] == pCompiler->chFirstEol)
     3309    {
     3310        KMK_CC_ASSERT(pCompiler->cchEolSeq == 1 || pchWord[2] == pCompiler->chSecondEol);
     3311        pchWord += pCompiler->cchEscEolSeq;
     3312        cchLeft -= pCompiler->cchEscEolSeq;
     3313        pCompiler->iEscEol++;
     3314    }
     3315    else
     3316        return pchWord;
     3317
     3318    /*
     3319     * Skip further spaces. We unrolls 4 loops here.
     3320     * ASSUMES cchEscEolSeq is either 2 or 3!
     3321     */
     3322    KMK_CC_ASSERT(pCompiler->cchEscEolSeq == 2 || pCompiler->cchEscEolSeq == 3);
     3323    while (cchLeft >= 4)
     3324    {
     3325        /* First char. */
     3326        ch = pchWord[0];
     3327        if (KMK_CC_EVAL_IS_SPACE(ch))
     3328        { /* maybe likely */ }
     3329        else if (   ch == '\\'
     3330                 && pchWord[1] == pCompiler->chFirstEol)
     3331        {
     3332            pchWord += pCompiler->cchEscEolSeq;
     3333            cchLeft -= pCompiler->cchEscEolSeq;
     3334            pCompiler->iEscEol++;
     3335            continue;
     3336        }
     3337        else
     3338        {
     3339            *pcchLeft = cchLeft;
     3340            return pchWord;
     3341        }
     3342
     3343        /* Second char. */
     3344        ch = pchWord[1];
     3345        if (KMK_CC_EVAL_IS_SPACE(ch))
     3346        { /* maybe likely */ }
     3347        else if (   ch == '\\'
     3348                 && pchWord[2] == pCompiler->chFirstEol)
     3349        {
     3350            pchWord += 1 + pCompiler->cchEscEolSeq;
     3351            cchLeft -= 1 + pCompiler->cchEscEolSeq;
     3352            pCompiler->iEscEol++;
     3353            continue;
     3354        }
     3355        else
     3356        {
     3357            *pcchLeft = cchLeft - 1;
     3358            return pchWord + 1;
     3359        }
     3360
     3361        /* Third char. */
     3362        ch = pchWord[2];
     3363        if (KMK_CC_EVAL_IS_SPACE(ch))
     3364        { /* maybe likely */ }
     3365        else if (   ch == '\\'
     3366                 && pchWord[3] == pCompiler->chFirstEol
     3367                 && cchLeft >= 2 + pCompiler->cchEscEolSeq)
     3368        {
     3369            pchWord += 2 + pCompiler->cchEscEolSeq;
     3370            cchLeft -= 2 + pCompiler->cchEscEolSeq;
     3371            pCompiler->iEscEol++;
     3372            continue;
     3373        }
     3374        else
     3375        {
     3376            *pcchLeft = cchLeft - 2;
     3377            return pchWord + 2;
     3378        }
     3379
     3380        /* Third char. */
     3381        ch = pchWord[3];
     3382        if (KMK_CC_EVAL_IS_SPACE(ch))
     3383        {
     3384            pchWord += 4;
     3385            cchLeft -= 4;
     3386        }
     3387        else if (   ch == '\\'
     3388                 && cchLeft >= 3 + pCompiler->cchEscEolSeq
     3389                 && pchWord[4] == pCompiler->chFirstEol)
     3390        {
     3391            pchWord += 3 + pCompiler->cchEscEolSeq;
     3392            cchLeft -= 3 + pCompiler->cchEscEolSeq;
     3393            pCompiler->iEscEol++;
     3394        }
     3395        else
     3396        {
     3397            *pcchLeft = cchLeft - 3;
     3398            return pchWord + 3;
     3399        }
     3400    }
     3401
     3402    /*
     3403     * Simple loop for the final three chars.
     3404     */
     3405    while (cchLeft > 0)
     3406    {
     3407        /* First char. */
     3408        ch = *pchWord;
     3409        if (KMK_CC_EVAL_IS_SPACE(ch))
     3410        {
     3411            pchWord += 1;
     3412            cchLeft -= 1;
     3413        }
     3414        else if (   ch == '\\'
     3415                 && cchLeft > pCompiler->cchEolSeq
     3416                 && pchWord[1] == pCompiler->chFirstEol)
     3417        {
     3418            pchWord += pCompiler->cchEscEolSeq;
     3419            cchLeft -= pCompiler->cchEscEolSeq;
     3420            pCompiler->iEscEol++;
     3421        }
     3422        else
     3423            break;
     3424    }
     3425
     3426    *pcchLeft = cchLeft;
     3427    return pchWord;
     3428}
     3429
     3430
     3431#if 0
     3432/**
     3433 * Used to shed trailing spaces in a string by decrementing @ a_cchLeft.
     3434 *
     3435 * @param   a_pCompiler     The compiler instance data.
     3436 * @param   a_pchWord       The current input position, this will not be
     3437 *                          changed.
     3438 * @param   a_cchLeft       The number of chars left to parse.  This will be
     3439 *                          updated if there are trailing spaces.
     3440 */
     3441#define KMK_CC_EVAL_SHED_TRAILING_SPACES(a_pCompiler, a_pchWord, a_cchLeft) \
     3442    do { \
     3443        if ((a_cchLeft) > 0) \
     3444        { \
     3445            char const chShedTrailing1 = (a_pchWord)[(a_cchLeft) - 1]; \
     3446            if (KMK_CC_EVAL_IS_SPACE(chShedTrailing1)) \
     3447            { \
     3448                /* If there are two or more, or any of these are potential EOL chars, call worker. */ \
     3449                if (!KMK_CC_EVAL_IS_EOL_CANDIDATE(chShedTrailing1)) \
     3450                { \
     3451                    (a_cchLeft) -= 1; \
     3452                    if ((a_cchLeft) > 0) \
     3453                    { \
     3454                        char const chShedTrailing2 = (a_pchWord)[(a_cchLeft) - 1]; \
     3455                        if (KMK_CC_EVAL_IS_SPACE(chShedTrailing)) \
     3456                            (a_cchLeft) = kmk_cc_eval_shed_trailing_spaces_slow(a_pchWord, (a_cchLeft), \
     3457                                                                                         chShedTrailing2, a_pCompiler); \
     3458                    } \
     3459                } \
     3460                else \
     3461                    (a_cchWord) = kmk_cc_eval_shed_trailing_spaces_slow(a_pchWord, (a_cchLeft), \
     3462                                                                                 chShedTrailing1, a_pCompiler); \
     3463            } \
     3464        } \
     3465    } while (0)
     3466
     3467
     3468
     3469/**
     3470 * The slow path of KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD.
     3471 *
     3472 * It's called when a space or backslash is encountered as the 2nd character.
     3473 * A second space is likely to be followed by more spaces, while an escaped
     3474 * newline (if it checks out) is very likely to be followed by more spaces or
     3475 * tabs before we get to anything worthwhile.  Also, checking for an esacped
     3476 * end-of-line character is a bit tedious. (It could be worse, though, as we
     3477 * know that any EOL sequence inside the text area we're currently working on
     3478 * must be escaped.)
     3479 *
     3480 * @returns Points to the first non-space character or end of input.
     3481 * @param   pchWord             The current position. There is some kind of char
     3482 * @param   cchLeft             The number of chars left to parse at @a pchWord.
     3483 * @param   ch                  The current tail character.
     3484 * @param   pCompiler           The compiler instance data.
     3485 */
     3486static size_t kmk_cc_eval_shed_trailing_spaces_slow(const char *pchWord, size_t cchLeft, char ch,
     3487                                                    PKMKCCEVALCOMPILER pCompiler)
     3488{
     3489    for (;;)
     3490    {
     3491        if (KMK_CC_EVAL_IS_EOL_CANDIDATE(chShedTrailing1))
     3492        {
     3493
     3494        }
     3495
     3496    }
     3497}
     3498#endif
     3499
     3500
     3501/**
     3502 * Prepares for copying a command line.
     3503 *
     3504 * The current version of this code will not modify any of the paEscEols
     3505 * entries, unlike our kmk_cc_eval_prep_normal_line sibling function.
     3506 *
     3507 * @returns The number of chars that will be copied by
     3508 *          kmk_cc_eval_copy_prepped_command_line().
     3509 * @param   pCompiler   The compiler instance data.
     3510 * @param   pchLeft     Pointer to the first char to copy from the current line.
     3511 *                      This does not have to the start of a word.
     3512 * @param   cchLeft     The number of chars left on the current line starting at
     3513 *                      @a pchLeft.
     3514 */
     3515static size_t kmk_cc_eval_prep_command_line(PKMKCCEVALCOMPILER pCompiler, const char * const pchLeft, size_t cchLeft)
     3516{
     3517    size_t          cchRet;
     3518    unsigned        iEscEol  = pCompiler->iEscEol;
     3519    unsigned const  cEscEols = pCompiler->cEscEols;
     3520
     3521    KMK_CC_ASSERT(cchLeft > 0);
     3522    KMK_CC_ASSERT(iEscEol <= cEscEols);
     3523
     3524    if (iEscEol >= cEscEols)
     3525    {
     3526        /*
     3527         * No escaped EOLs left, dead simple.
     3528         */
     3529        cchRet = cchLeft;
     3530    }
     3531    else
     3532    {
     3533        /*
     3534         * Compared to the normal prepping of a line, this is actually
     3535         * really simple.  We need to account for two kind of conversions:
     3536         *      - One leading tab is skipped after escaped EOL.
     3537         *      - Convert EOL to LF.
     3538         */
     3539        const char * const  pszContent = pCompiler->pszContent;
     3540        size_t       const  cchEolSeq  = pCompiler->cchEolSeq;
     3541
     3542#ifdef KMK_CC_STRICT
     3543        size_t const offLeft   = pchLeft - pszContent;
     3544        KMK_CC_ASSERT(offLeft + cchLeft <= pCompiler->offLine + pCompiler->cchLine);
     3545        KMK_CC_ASSERT(offLeft + cchLeft <= pCompiler->cchContent);
     3546        KMK_CC_ASSERT(offLeft <  pCompiler->paEscEols[iEscEol].offEsc);
     3547        KMK_CC_ASSERT(offLeft >= (iEscEol ? pCompiler->paEscEols[cEscEols - 1].offEol + pCompiler->cchEolSeq : pCompiler->offLine));
     3548#endif
     3549
     3550        cchRet = cchLeft;
     3551        if (cchEolSeq > 1)
     3552            cchRet -= (cchEolSeq - 1) * cEscEols;
     3553        do
     3554        {
     3555            if (pszContent[pCompiler->paEscEols[cchEolSeq].offEol])
     3556                cchRet--;
     3557            iEscEol++;
     3558        } while (iEscEol < cEscEols);
     3559    }
     3560    return cchRet;
     3561}
     3562
     3563
     3564/**
     3565 * Copies a command line to the buffer @a pszDst points to.
     3566 *
     3567 * Must only be used immediately after kmk_cc_eval_prep_command_line().
     3568 *
     3569 * @returns
     3570 * @param   pCompiler   The compiler instance data.
     3571 * @param   pchLeft     Pointer to the first char to copy from the current line.
     3572 *                      This does not have to the start of a word.
     3573 * @param   cchPrepped  The return value of kmk_cc_eval_prep_command_line().
     3574 * @param   pszDst      The destination buffer, must be at least @a cchPrepped
     3575 *                      plus one (terminator) char big.
     3576 */
     3577static void kmk_cc_eval_copy_prepped_command_line(PKMKCCEVALCOMPILER pCompiler, const char *pchLeft,
     3578                                                  size_t cchPrepped, char *pszDst)
     3579{
     3580    unsigned        iEscEol  = pCompiler->iEscEol;
     3581    unsigned const  cEscEols = pCompiler->cEscEols;
     3582    if (iEscEol >= cEscEols)
     3583    {
     3584        /* Single line. */
     3585        memcpy(pszDst, pchLeft, cchPrepped);
     3586        pszDst[cchPrepped] = '\0';
     3587    }
     3588    else
     3589    {
     3590        /* Multiple lines with normalized EOL and maybe one stripped leading TAB. */
     3591        char * const        pszDstStart = pszDst;
     3592        const char * const  pszContent  = pCompiler->pszContent;
     3593        size_t const        cchEolSeq   = pCompiler->cchEolSeq;
     3594        size_t              offLeft     = pchLeft - pCompiler->pszContent;
     3595        size_t              cchCopy;
     3596
     3597        do
     3598        {
     3599            size_t offEol = pCompiler->paEscEols[iEscEol].offEsc;
     3600            cchCopy = offEol - offLeft;
     3601            KMK_CC_ASSERT(offEol >= offLeft);
     3602
     3603            memcpy(pszDst, &pszContent[offLeft], cchCopy);
     3604            pszDst += cchCopy;
     3605            *pszDst += '\n';
     3606
     3607            offLeft = offEol + cchEolSeq;
     3608            if (pszContent[offLeft] == '\t')
     3609                offLeft++;
     3610        } while (iEscEol < cEscEols);
     3611
     3612        cchCopy = cchPrepped - (pszDst - pszDstStart);
     3613        KMK_CC_ASSERT(cchCopy <= cchPrepped);
     3614        memcpy(pszDst, &pszContent[offLeft], cchCopy);
     3615        pszDst += cchCopy;
     3616
     3617        *pszDst = '\0';
     3618        KMK_CC_ASSERT(pszDst == &pszDstStart[cchPrepped]);
     3619    }
     3620}
     3621
     3622
     3623/**
     3624 * Prepares for copying a normal line.
     3625 *
     3626 * This will modify the members of paEscEols to store copy instructions for the
     3627 * following kmk_cc_eval_copy_prepped_normal_line() call.
     3628 *
     3629 * @returns The number of chars that will be copied by
     3630 *          kmk_cc_eval_copy_prepped_normal_line().
     3631 * @param   pCompiler               The compiler instance data.
     3632 * @param   pchWord                 Pointer to the first char to copy from the
     3633 *                                  current line. This must be the start of a
     3634 *                                  word.
     3635 * @param   cchLeft                 The number of chars left on the current line
     3636 *                                  starting at @a pchWord.
     3637 * @param   fStripTrailingSpaces    Whether to strip trailing spaces.
     3638 */
     3639static size_t kmk_cc_eval_prep_normal_line(PKMKCCEVALCOMPILER pCompiler, const char * const pchWord, size_t cchLeft,
     3640                                           int fStripTrailingSpaces)
     3641{
     3642    size_t          cchRet;
     3643    unsigned        iEscEol  = pCompiler->iEscEol;
     3644    unsigned const  cEscEols = pCompiler->cEscEols;
     3645
     3646    KMK_CC_ASSERT(cchLeft > 0);
     3647    KMK_CC_ASSERT(!KMK_CC_EVAL_IS_SPACE(*pchWord)); /* The fact that we're standing at a word, is exploited below. */
     3648    KMK_CC_ASSERT(iEscEol <= cEscEols);
     3649
     3650    if (iEscEol >= cEscEols)
     3651    {
     3652        /*
     3653         * No escaped EOLs left, simple.  Just need to strip trailing spaces
     3654         * when desired by the caller.
     3655         */
     3656        cchRet = cchLeft;
     3657        if (fStripTrailingSpaces)
     3658            while (cchRet > 0 && KMK_CC_IS_SPACE_CH(pchWord[cchRet - 1]))
     3659                cchRet--;
     3660    }
     3661    else
     3662    {
     3663        /*
     3664         * This is a tad complicated.  Working with offsets here is simpler.
     3665         */
     3666        const char *pszContent = pCompiler->pszContent;
     3667        size_t      offWord    = pchWord - pCompiler->pszContent;
     3668        size_t      offEsc;
     3669        size_t      fPendingSpace;
     3670
     3671        /* Go nuts checking our preconditions here. */
     3672        KMK_CC_ASSERT(offWord >= pCompiler->offLine);
     3673        KMK_CC_ASSERT(offWord + cchLeft <= pCompiler->offLine + pCompiler->cchLine);
     3674        KMK_CC_ASSERT(offWord + cchLeft <= pCompiler->cchContent);
     3675        KMK_CC_ASSERT(offWord <  pCompiler->paEscEols[iEscEol].offEsc);
     3676        KMK_CC_ASSERT(offWord >= (iEscEol ? pCompiler->paEscEols[cEscEols - 1].offEol + pCompiler->cchEolSeq : pCompiler->offLine));
     3677
     3678        /* First line - We're at the start of a word, so no left stripping needed.  */
     3679        offEsc = pCompiler->paEscEols[iEscEol].offEsc;
     3680        KMK_CC_ASSERT(offEsc > offWord);
     3681        while (KMK_CC_EVAL_IS_SPACE(pszContent[offEsc - 1]))
     3682            offEsc--;
     3683        KMK_CC_ASSERT(offEsc > offWord);
     3684
     3685        fPendingSpace = 1;
     3686        cchRet        = offEsc - offWord;
     3687        pCompiler->paEscEols[iEscEol].offEsc = offEsc;
     3688
     3689        offWord = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq;
     3690        iEscEol++;
     3691
     3692        /* All but the last line. */
     3693        while (iEscEol < cEscEols)
     3694        {
     3695            offEsc = pCompiler->paEscEols[iEscEol].offEsc;
     3696            while (offWord < offEsc && KMK_CC_EVAL_IS_SPACE(pszContent[offWord]))
     3697                offWord++;
     3698
     3699            if (offWord < offEsc)
     3700            {
     3701                while (KMK_CC_EVAL_IS_SPACE(pszContent[offEsc - 1]))
     3702                    offEsc--;
     3703                cchRet += offEsc - offWord + fPendingSpace;
     3704                fPendingSpace = 1;
     3705            }
     3706
     3707            pCompiler->paEscEols[iEscEol - 1].offEol = offWord;
     3708            pCompiler->paEscEols[iEscEol].offEsc     = offEsc;
     3709
     3710            offWord = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq;
     3711            iEscEol++;
     3712        }
     3713
     3714        /* Final line. We must calculate the end of line offset our selves here. */
     3715        offEsc = &pchWord[cchLeft] - pszContent;
     3716        while (offWord < offEsc && KMK_CC_EVAL_IS_SPACE(pszContent[offWord]))
     3717            offWord++;
     3718
     3719        if (offWord < offEsc)
     3720        {
     3721            if (fStripTrailingSpaces)
     3722                while (KMK_CC_EVAL_IS_SPACE(pszContent[offEsc - 1]))
     3723                    offEsc--;
     3724            cchRet += offEsc - offWord + fPendingSpace;
     3725        }
     3726        pCompiler->paEscEols[iEscEol - 1].offEol = offWord;
     3727    }
     3728    return cchRet;
     3729}
     3730
     3731
     3732/**
     3733 * Copies a normal line to the buffer @a pszDst points to.
     3734 *
     3735 * Must only be used immediately after kmk_cc_eval_prep_normal_line().
     3736 *
     3737 * @param   pCompiler   The compiler instance data.
     3738 * @param   pchLeft     Pointer to the first char to copy from the current line.
     3739 *                      This does not have to the start of a word.
     3740 * @param   cchPrepped  The return value of kmk_cc_eval_prep_normal_line().
     3741 * @param   pszDst      The destination buffer, must be at least @a cchPrepped
     3742 *                      plus one (terminator) char big.
     3743 */
     3744static void kmk_cc_eval_copy_prepped_normal_line(PKMKCCEVALCOMPILER pCompiler, const char *pchWord,
     3745                                                 size_t cchPrepped, char *pszDst)
     3746{
     3747    unsigned        iEscEol  = pCompiler->iEscEol;
     3748    unsigned const  cEscEols = pCompiler->cEscEols;
     3749    if (iEscEol >= cEscEols)
     3750    {
     3751        /* Single line. */
     3752        memcpy(pszDst, pchWord, cchPrepped);
     3753        pszDst[cchPrepped] = '\0';
     3754    }
     3755    else
     3756    {
     3757        /* offEsc = end of copy on a line, offWord/offEol is where to start on the next. */
     3758        size_t          offWord       = pchWord - pCompiler->pszContent;
     3759        int             fPendingSpace = 0;
     3760        char * const    pszDstStart   = pszDst;
     3761        size_t          cchCopy;
     3762        do
     3763        {
     3764            cchCopy = pCompiler->paEscEols[iEscEol].offEsc - offWord;
     3765            KMK_CC_ASSERT(pCompiler->paEscEols[iEscEol].offEsc >= offWord);
     3766            if (cchCopy)
     3767            {
     3768                if (fPendingSpace)
     3769                    *pszDst++ = ' ';
     3770                memcpy(pszDst, &pCompiler->pszContent[offWord], cchCopy);
     3771                pszDst += cchCopy;
     3772                fPendingSpace = 1;
     3773            }
     3774            offWord = pCompiler->paEscEols[iEscEol].offEol;
     3775            iEscEol++;
     3776        } while (iEscEol < cEscEols);
     3777
     3778        /* The final line is delimited by cchPrepped. */
     3779        cchCopy = cchPrepped - (pszDst - pszDstStart);
     3780        KMK_CC_ASSERT(cchCopy <= cchPrepped);
     3781        if (cchCopy > 0)
     3782        {
     3783            if (fPendingSpace)
     3784                *pszDst++ = ' ';
     3785            memcpy(pszDst, &pCompiler->pszContent[offWord], cchCopy);
     3786            pszDst += cchCopy;
     3787        }
     3788        *pszDst = '\0';
     3789        KMK_CC_ASSERT(pszDst == &pszDstStart[cchPrepped]);
     3790    }
     3791}
     3792
     3793
     3794/**
     3795 * Common worker for all kmk_cc_eval_do_if*() functions.
     3796 *
     3797 * @param   pCompiler   The compiler state.
     3798 * @param   pIfCore     The new IF statement.
     3799 * @param   fInElse     Set if this is an 'else if' (rather than just 'if').
     3800 */
     3801static void kmk_cc_eval_do_if_core(PKMKCCEVALCOMPILER pCompiler, PKMKCCEVALIFCORE pIfCore, int fInElse)
     3802{
     3803    unsigned iIf = pCompiler->cIfs;
     3804    if (!fInElse)
     3805    {
     3806        /* Push an IF statement. */
     3807        if (iIf < KMK_CC_EVAL_MAX_IF_DEPTH)
     3808        {
     3809            pCompiler->cIfs = iIf + 1;
     3810            pCompiler->apIfs[iIf] = pIfCore;
     3811            pIfCore->pPrevCond = NULL;
     3812        }
     3813        else
     3814            kmk_cc_eval_fatal(pCompiler, NULL, "Too deep IF nesting");
     3815    }
     3816    else if (iIf > 0)
     3817    {
     3818        /* Link an IF statement. */
     3819        iIf--;
     3820        pIfCore->pPrevCond    = pCompiler->apIfs[iIf];
     3821        pCompiler->apIfs[iIf] = pIfCore;
     3822    }
     3823    else
     3824        kmk_cc_eval_fatal(pCompiler, NULL, "'else if' without 'if'");
     3825    pIfCore->pNextTrue    = (PKMKCCEVALCORE)kmk_cc_block_get_next_ptr(*pCompiler->ppBlockTail);
     3826    pIfCore->pNextFalse   = NULL; /* This is set by else or endif. */
     3827    pIfCore->pTrueEndJump = NULL; /* This is set by else or endif. */
     3828}
     3829
     3830
     3831/**
     3832 * Deals with 'if expr' and 'else if expr' statements.
     3833 *
     3834 * @returns 1 to indicate we've handled a keyword (see
     3835 *          kmk_cc_eval_try_handle_keyword).
     3836 * @param   pCompiler   The compiler state.
     3837 * @param   pchWord     First char after 'define'.
     3838 * @param   cchLeft     The number of chars left to parse on this line.
     3839 * @param   fInElse     Set if this is an 'else if' (rather than just 'if').
     3840 */
     3841static int kmk_cc_eval_do_if(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, int fInElse)
     3842{
     3843    KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
     3844    if (cchLeft)
     3845    {
     3846        size_t           cchExpr = kmk_cc_eval_prep_normal_line(pCompiler, pchWord, cchLeft, 1 /*fStripTrailingSpaces*/);
     3847        PKMKCCEVALIFEXPR pInstr  = (PKMKCCEVALIFEXPR)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail,
     3848                                                                             KMKCCEVALIFEXPR_SIZE(cchExpr));
     3849        kmk_cc_eval_copy_prepped_normal_line(pCompiler, pchWord, cchExpr, pInstr->szExpr);
     3850        pInstr->cchExpr = cchExpr;
     3851        pInstr->IfCore.Core.enmOpCode = kKmkCcEvalInstr_if;
     3852        pInstr->IfCore.Core.iLine     = pCompiler->iLine;
     3853        kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse);
     3854    }
     3855    else
     3856        kmk_cc_eval_fatal(pCompiler, pchWord, "Expected expression after 'if' directive");
     3857    return 1;
     3858}
     3859
     3860
     3861static int kmk_cc_eval_do_ifdef(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, int fInElse, int fPositiveStmt)
     3862{
     3863    return 1;
     3864}
     3865
     3866
     3867static int kmk_cc_eval_do_ifeq(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, int fInElse, int fPositiveStmt)
     3868{
     3869    return 1;
     3870}
     3871
     3872
     3873static int kmk_cc_eval_do_if1of(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, int fInElse, int fPositiveStmt)
     3874{
     3875    return 1;
     3876}
     3877
     3878
     3879/**
     3880 * Deals with 'else' and 'else ifxxx' statements.
     3881 *
     3882 * @returns 1 to indicate we've handled a keyword (see
     3883 *          kmk_cc_eval_try_handle_keyword).
     3884 * @param   pCompiler   The compiler state.
     3885 * @param   pchWord     First char after 'define'.
     3886 * @param   cchLeft     The number of chars left to parse on this line.
     3887 */
     3888static int kmk_cc_eval_do_else(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
     3889{
     3890    /*
     3891     * There must be an 'if' on the stack.
     3892     */
     3893    unsigned iIf = pCompiler->cIfs;
     3894    if (iIf > 0)
     3895    {
     3896        PKMKCCEVALIFCORE pIfCore = pCompiler->apIfs[--iIf];
     3897        if (!pIfCore->pTrueEndJump)
     3898        {
     3899            /* Emit a jump instruction that will take us from the 'True' block to the 'endif'. */
     3900            PKMKCCEVALJUMP  pInstr = (PKMKCCEVALJUMP)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
     3901            pInstr->Core.enmOpCode = kKmkCcEvalInstr_jump;
     3902            pInstr->Core.iLine     = pCompiler->iLine;
     3903            pInstr->pNext          = NULL;
     3904            pIfCore->pTrueEndJump  = pInstr;
     3905
     3906            /* The next instruction is the first in the 'False' block of the current 'if'.
     3907               Should this be an 'else if', this will be the 'if' instruction emitted below. */
     3908            pIfCore->pNextFalse    = (PKMKCCEVALCORE)kmk_cc_block_get_next_ptr(*pCompiler->ppBlockTail);
     3909        }
     3910        else if (iIf == 0)
     3911            kmk_cc_eval_fatal(pCompiler, pchWord, "2nd 'else' for 'if' at line %u", pIfCore->Core.iLine);
     3912        else
     3913            kmk_cc_eval_fatal(pCompiler, pchWord, "2nd 'else' in a row - missing 'endif' for 'if' at line %u?",
     3914                              pIfCore->Core.iLine);
     3915    }
     3916    else
     3917        kmk_cc_eval_fatal(pCompiler, pchWord, "'else' without 'if'");
     3918
     3919    /*
     3920     * Check for 'else ifxxx'. There can be nothing else following an else.
     3921     */
     3922    KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
     3923    if (cchLeft)
     3924    {
     3925        if (   cchLeft > 2
     3926            && KMK_CC_WORD_COMP_CONST_2(pchWord, "if"))
     3927        {
     3928            pchWord += 2;
     3929            cchLeft -= 2;
     3930
     3931            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "", 0))
     3932                return kmk_cc_eval_do_if(pCompiler, pchWord, cchLeft, 1 /* in else */);
     3933
     3934            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "eq", 2))
     3935                return kmk_cc_eval_do_ifeq( pCompiler, pchWord + 2, cchLeft + 2, 1 /* in else */, 1 /* positive */);
     3936
     3937            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "def", 3))
     3938                return kmk_cc_eval_do_ifdef(pCompiler, pchWord + 3, cchLeft + 3, 1 /* in else */, 1 /* positive */);
     3939
     3940            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "neq", 3))
     3941                return kmk_cc_eval_do_ifeq( pCompiler, pchWord + 3, cchLeft + 3, 1 /* in else */, 0 /* positive */);
     3942
     3943            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "1of", 3))
     3944                return kmk_cc_eval_do_if1of(pCompiler, pchWord + 3, cchLeft + 3, 1 /* in else */, 1 /* positive */);
     3945
     3946            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "ndef", 4))
     3947                return kmk_cc_eval_do_ifdef(pCompiler, pchWord + 4, cchLeft + 4, 1 /* in else */, 0 /* positive */);
     3948
     3949            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "n1of", 4))
     3950                return kmk_cc_eval_do_if1of(pCompiler, pchWord + 4, cchLeft + 4, 1 /* in else */, 0 /* positive */);
     3951
     3952            pchWord -= 2;
     3953            cchLeft += 2;
     3954        }
     3955        kmk_cc_eval_fatal(pCompiler, pchWord, "Bogus stuff after 'else'");
     3956    }
     3957
     3958    return 1;
     3959}
     3960
     3961
     3962/**
     3963 * Deals with the 'endif' statement.
     3964 *
     3965 * @returns 1 to indicate we've handled a keyword (see
     3966 *          kmk_cc_eval_try_handle_keyword).
     3967 * @param   pCompiler   The compiler state.
     3968 * @param   pchWord     First char after 'define'.
     3969 * @param   cchLeft     The number of chars left to parse on this line.
     3970 */
     3971static int kmk_cc_eval_do_endif(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
     3972{
     3973    /*
     3974     * There must be an 'if' on the stack.  We'll POP it.
     3975     */
     3976    unsigned iIf = pCompiler->cIfs;
     3977    if (iIf > 0)
     3978    {
     3979        PKMKCCEVALCORE   pNextInstr;
     3980        PKMKCCEVALIFCORE pIfCore = pCompiler->apIfs[--iIf];
     3981        pCompiler->cIfs = iIf; /* POP! */
     3982
     3983        /* Update the jump targets for all IFs at this level. */
     3984        pNextInstr = (PKMKCCEVALCORE)kmk_cc_block_get_next_ptr(*pCompiler->ppBlockTail);
     3985        do
     3986        {
     3987            if (pIfCore->pTrueEndJump)
     3988            {
     3989                /* Make the true block jump here, to the 'endif'. The false block is already here. */
     3990                pIfCore->pTrueEndJump->pNext = pNextInstr;
     3991                KMK_CC_ASSERT(pIfCore->pNextFalse);
     3992            }
     3993            else
     3994            {
     3995                /* No 'else'. The false-case jump here, to the 'endif'. */
     3996                KMK_CC_ASSERT(!pIfCore->pNextFalse);
     3997                pIfCore->pNextFalse = pNextInstr;
     3998            }
     3999
     4000            pIfCore = pIfCore->pPrevCond;
     4001        } while (pIfCore);
     4002    }
     4003    else
     4004        kmk_cc_eval_fatal(pCompiler, pchWord, "'endif' without 'if'");
     4005
     4006    /*
     4007     * There shouldn't be anything trailing an 'endif'.
     4008     */
     4009    KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
     4010    if (!cchLeft) { /* likely */ }
     4011    else kmk_cc_eval_fatal(pCompiler, pchWord, "Bogus stuff after 'else'");
     4012
     4013    return 1;
     4014}
     4015
     4016
     4017static int kmk_cc_eval_do_include(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, KMKCCEVALINSTR enmInst)
     4018{
     4019    return 1;
     4020}
     4021
     4022
     4023static int kmk_cc_eval_do_vpath(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
     4024{
     4025    return 1;
     4026}
     4027
     4028
     4029static void kmk_cc_eval_handle_command(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
     4030{
     4031
     4032}
     4033
     4034
     4035static void kmk_cc_eval_handle_recipe(PKMKCCEVALCOMPILER pCompiler, const char *pszEqual, const char *pchWord, size_t cchLeft)
     4036{
     4037
     4038}
     4039
     4040
     4041/**
     4042 * Parses a 'undefine variable [..]' expression.
     4043 *
     4044 * A 'undefine' directive is final, any qualifiers must preceed it.  So, we just
     4045 * have to extract the variable names now.
     4046 *
     4047 * @returns 1 to indicate we've handled a keyword (see
     4048 *          kmk_cc_eval_try_handle_keyword).
     4049 * @param   pCompiler   The compiler state.
     4050 * @param   pchWord     First char after 'define'.
     4051 * @param   cchLeft     The number of chars left to parse on this line.
     4052 * @param   fQualifiers The qualifiers.
     4053 */
     4054static int kmk_cc_eval_do_var_undefine(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft,
     4055                                                unsigned fQualifiers)
     4056{
     4057
     4058    return 1;
     4059}
     4060
     4061
     4062/**
     4063 * Parses a 'define variable' expression.
     4064 *
     4065 * A 'define' directive is final, any qualifiers must preceed it.  So, we just
     4066 * have to extract the variable name now, well and find the corresponding
     4067 * 'endef'.
     4068 *
     4069 * @returns 1 to indicate we've handled a keyword (see
     4070 *          kmk_cc_eval_try_handle_keyword).
     4071 * @param   pCompiler   The compiler state.
     4072 * @param   pchWord     First char after 'define'.
     4073 * @param   cchLeft     The number of chars left to parse on this line.
     4074 * @param   fQualifiers The qualifiers.
     4075 */
     4076static int kmk_cc_eval_do_var_define(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft,
     4077                                              unsigned fQualifiers)
     4078{
     4079
     4080    return 1;
     4081}
     4082
     4083
     4084static int kmk_cc_eval_try_handle_assignment(PKMKCCEVALCOMPILER pCompiler, const char *pchTmp,
     4085                                                      const char *pchWord, size_t cchLeft, unsigned fQualifiers)
     4086{
     4087    return 0;
     4088}
     4089
     4090
     4091/**
     4092 * Parses a 'local [override] variable = value' expression.
     4093 *
     4094 * The 'local' directive must be first and it does not permit any qualifiers at
     4095 * the moment.  Should any be added later, they will have to come after 'local'.
     4096 *
     4097 * @returns 1 to indicate we've handled a keyword (see
     4098 *          kmk_cc_eval_try_handle_keyword).
     4099 * @param   pCompiler   The compiler state.
     4100 * @param   pchWord     First char after 'define'.
     4101 * @param   cchLeft     The number of chars left to parse on this line.
     4102 * @param   fQualifiers The qualifiers.
     4103 */
     4104static int kmk_cc_eval_do_var_local(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
     4105{
     4106
     4107    return 1;
     4108}
     4109
     4110
     4111/**
     4112 * Parses 'export [variable]' and 'export [qualifiers] variable = value'
     4113 * expressions.
     4114 *
     4115 * When we find the 'export' directive at the start of a line, we need to
     4116 * continue parsing with till we can tell the difference between the two forms.
     4117 *
     4118 * @returns 1 to indicate we've handled a keyword (see
     4119 *          kmk_cc_eval_try_handle_keyword).
     4120 * @param   pCompiler   The compiler state.
     4121 * @param   pchWord     First char after 'define'.
     4122 * @param   cchLeft     The number of chars left to parse on this line.
     4123 * @param   fQualifiers The qualifiers.
     4124 */
     4125static int kmk_cc_eval_handle_var_export(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft,
     4126                                                  unsigned fQualifiers)
     4127{
     4128
     4129    return 1;
     4130}
     4131
     4132
     4133/**
     4134 * We've found one variable qualification keyword, now continue parsing and see
     4135 * if this is some kind of variable assignment expression or not.
     4136 *
     4137 * @returns 1 if variable assignment, 0 if not.
     4138 * @param   pCompiler   The compiler state.
     4139 * @param   pchWord     First char after the first qualifier.
     4140 * @param   cchLeft     The number of chars left to parse on this line.
     4141 * @param   fQualifiers The qualifier.
     4142 */
     4143static int kmk_cc_eval_try_handle_var_with_keywords(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft,
     4144                                                             unsigned fQualifiers)
     4145{
     4146    for (;;)
     4147    {
     4148        KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
     4149        if (cchLeft)
     4150        {
     4151            char ch = *pchWord;
     4152            if (KMK_CC_EVAL_IS_1ST_IN_VARIABLE_KEYWORD(ch))
     4153            {
     4154                if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "define", 6))   /* final */
     4155                    return kmk_cc_eval_do_var_define(pCompiler, pchWord + 6, cchLeft - 6, fQualifiers);
     4156
     4157                if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "export", 6))   /* special */
     4158                    return kmk_cc_eval_handle_var_export(pCompiler, pchWord + 6, cchLeft - 6, fQualifiers);
     4159
     4160                if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "undefine", 8)) /* final */
     4161                    return kmk_cc_eval_do_var_undefine(pCompiler, pchWord + 6, cchLeft - 6, fQualifiers);
     4162
     4163                if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "override", 8))
     4164                {
     4165                    /** @todo warn if repeated. */
     4166                    fQualifiers |= KMK_CC_EVAL_QUALIFIER_OVERRIDE;
     4167                    pchWord += 8;
     4168                    cchLeft -= 8;
     4169                    continue;
     4170                }
     4171
     4172                if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "private", 7))
     4173                {
     4174                    /** @todo warn if repeated. */
     4175                    fQualifiers |= KMK_CC_EVAL_QUALIFIER_PRIVATE;
     4176                    pchWord += 7;
     4177                    cchLeft -= 7;
     4178                    continue;
     4179                }
     4180            }
     4181
     4182            /*
     4183             * Not a keyword, likely variable name followed by an assignment
     4184             * operator and a value.  Do a rough check for the assignment operator
     4185             * and join paths with the unqualified assignment handling code.
     4186             */
     4187            {
     4188                const char *pchEqual = (const char *)memchr(pchWord, '=', cchLeft);
     4189                if (pchEqual)
     4190                    return kmk_cc_eval_try_handle_assignment(pCompiler, pchEqual, pchWord, cchLeft, fQualifiers);
     4191            }
     4192            return 0;
     4193        }
     4194        else
     4195            kmk_cc_eval_fatal(pCompiler, NULL,
     4196                                       "Expected assignment operator or variable directive after variable qualifier(s)\n");
     4197    }
     4198}
     4199
     4200
     4201/**
     4202 * When entering this function we know that the first two character in the first
     4203 * word both independently occurs in keywords.
     4204 *
     4205 * @returns 1 if make directive or qualified variable assignment, 0 if neither.
     4206 * @param   pCompiler   The compiler state.
     4207 * @param   ch          The first char.
     4208 * @param   pchWord     Pointer to the first word.
     4209 * @param   cchLeft     Number of characters left to parse starting at
     4210 *                      @a cchLeft.
     4211 */
     4212int kmk_cc_eval_try_handle_keyword(PKMKCCEVALCOMPILER pCompiler, char ch, const char *pchWord, size_t cchLeft)
     4213{
     4214    unsigned iSavedEscEol = pCompiler->iEscEol;
     4215
     4216    KMK_CC_ASSERT(cchLeft >= 2);
     4217    KMK_CC_ASSERT(ch == pchWord[0]);
     4218    KMK_CC_ASSERT(KMK_CC_EVAL_IS_1ST_IN_KEYWORD(pchWord[0]));
     4219    KMK_CC_ASSERT(KMK_CC_EVAL_IS_2ND_IN_KEYWORD(pchWord[1]));
     4220
     4221    /*
     4222     * If it's potentially a variable related keyword, check that out first.
     4223     */
     4224    if (KMK_CC_EVAL_IS_1ST_IN_VARIABLE_KEYWORD(ch))
     4225    {
     4226        if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "local", 5))
     4227            return kmk_cc_eval_do_var_local(pCompiler, pchWord + 5, cchLeft - 5);
     4228        if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "define", 6))
     4229            return kmk_cc_eval_do_var_define(pCompiler, pchWord + 6, cchLeft - 6, 0);
     4230        if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "export", 6))
     4231            return kmk_cc_eval_handle_var_export(pCompiler, pchWord + 6, cchLeft - 6, 0);
     4232        if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "undefine", 8))
     4233            return kmk_cc_eval_do_var_undefine(pCompiler, pchWord + 8, cchLeft - 8, 0);
     4234        if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "override", 8))
     4235        {
     4236            if (kmk_cc_eval_try_handle_var_with_keywords(pCompiler, pchWord + 8, cchLeft - 8, KMK_CC_EVAL_QUALIFIER_OVERRIDE))
     4237                return 1;
     4238            pCompiler->iEscEol = iSavedEscEol;
     4239        }
     4240        else if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "private", 7))
     4241        {
     4242            if (kmk_cc_eval_try_handle_var_with_keywords(pCompiler, pchWord + 7, cchLeft - 7, KMK_CC_EVAL_QUALIFIER_PRIVATE))
     4243                return 1;
     4244            pCompiler->iEscEol = iSavedEscEol;
     4245        }
     4246    }
     4247
     4248    /*
     4249     * Check out the other keywords.
     4250     */
     4251    if (ch == 'i') /* Lots of directives starting with 'i'. */
     4252    {
     4253        char ch2 = pchWord[1];
     4254        pchWord += 2;
     4255        cchLeft -= 2;
     4256
     4257        /* 'if...' */
     4258        if (ch2 == 'f')
     4259        {
     4260            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "", 0))
     4261                return kmk_cc_eval_do_if(pCompiler, pchWord, cchLeft, 0 /* in else */);
     4262
     4263            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "eq", 2))
     4264                return kmk_cc_eval_do_ifeq( pCompiler, pchWord + 2, cchLeft + 2, 0 /* in else */, 1 /* positive */);
     4265
     4266            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "def", 3))
     4267                return kmk_cc_eval_do_ifdef(pCompiler, pchWord + 3, cchLeft + 3, 0 /* in else */, 1 /* positive */);
     4268
     4269            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "neq", 3))
     4270                return kmk_cc_eval_do_ifeq( pCompiler, pchWord + 3, cchLeft + 3, 0 /* in else */, 0 /* positive */);
     4271
     4272            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "1of", 3))
     4273                return kmk_cc_eval_do_if1of(pCompiler, pchWord + 3, cchLeft + 3, 0 /* in else */, 1 /* positive */);
     4274
     4275            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "ndef", 4))
     4276                return kmk_cc_eval_do_ifdef(pCompiler, pchWord + 4, cchLeft + 4, 0 /* in else */, 0 /* positive */);
     4277
     4278            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "n1of", 4))
     4279                return kmk_cc_eval_do_if1of(pCompiler, pchWord + 4, cchLeft + 4, 0 /* in else */, 0 /* positive */);
     4280        }
     4281        /* include... */
     4282        else if (ch2 == 'n' && cchLeft >= 5 && KMK_CC_WORD_COMP_CONST_5(pchWord, "clude") ) /* 'in...' */
     4283        {
     4284            pchWord += 5;
     4285            cchLeft -= 5;
     4286            if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "", 0))
     4287                return kmk_cc_eval_do_include(pCompiler, pchWord, cchLeft, kKmkCcEvalInstr_include);
     4288            if (cchLeft >= 3 && KMK_CC_WORD_COMP_CONST_3(pchWord, "dep"))
     4289            {
     4290                pchWord += 3;
     4291                cchLeft -= 3;
     4292                if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "", 0))
     4293                    return kmk_cc_eval_do_include(pCompiler, pchWord, cchLeft, kKmkCcEvalInstr_includedep);
     4294                if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "-queue", 6))
     4295                    return kmk_cc_eval_do_include(pCompiler, pchWord + 6, cchLeft + 6, kKmkCcEvalInstr_includedep_queue);
     4296                if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "-flush", 6))
     4297                    return kmk_cc_eval_do_include(pCompiler, pchWord + 6, cchLeft + 6, kKmkCcEvalInstr_includedep_flush);
     4298            }
     4299        }
     4300    }
     4301    else if (ch == 'e') /* A few directives starts with 'e'. */
     4302    {
     4303        if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "else", 4))
     4304            return kmk_cc_eval_do_else(pCompiler, pchWord + 4, cchLeft + 4);
     4305        if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "endif", 5))
     4306            return kmk_cc_eval_do_endif(pCompiler, pchWord + 5, cchLeft + 5);
     4307        /* export and endef are handled elsewhere, though stray endef's may end up here... */
     4308        KMK_CC_ASSERT(!KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "export", 6));
     4309
     4310    }
     4311    else /* the rest. */
     4312    {
     4313        if (   KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "sinclude", 8)
     4314            || KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "-include", 8))
     4315            return kmk_cc_eval_do_include(pCompiler, pchWord + 8, cchLeft + 8, kKmkCcEvalInstr_include_silent);
     4316        if (KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "vpath", 5))
     4317            return kmk_cc_eval_do_vpath(pCompiler, pchWord + 5, cchLeft + 5);
     4318
     4319        KMK_CC_ASSERT(!KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "local", 5));
     4320        KMK_CC_ASSERT(!KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "define", 6));
     4321        KMK_CC_ASSERT(!KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "private", 7));
     4322        KMK_CC_ASSERT(!KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "override", 8));
     4323        KMK_CC_ASSERT(!KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "unexport", 8));
     4324        KMK_CC_ASSERT(!KMK_CC_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "undefine", 8));
     4325    }
     4326
     4327    pCompiler->iEscEol = iSavedEscEol;
     4328    return 0;
     4329}
     4330
     4331
     4332
     4333
     4334static int kmk_cc_eval_compile_worker(PKMKCCEVALPROG pEvalProg, const char *pszContent, size_t cchContent, unsigned iLine)
     4335{
     4336    const char *pchTmp;
     4337
     4338    /*
     4339     * Compiler state.
     4340     */
     4341    KMKCCEVALCOMPILER   Compiler;
     4342    kmk_cc_eval_init_compiler(&Compiler, pEvalProg, iLine, pszContent, cchContent);
     4343
     4344    {
     4345        /*
     4346         * Line state.
     4347         */
     4348        size_t              cchLine;                                /* The length of the current line (w/o comments). */
     4349        size_t              offNext = 0;                            /* The offset of the next line. */
     4350        size_t              off     = 0;                            /* The offset into pszContent of the current line. */
     4351
     4352        /* Try for some register/whatever optimzations. */
     4353        int const           chFirstEol = Compiler.chFirstEol;
     4354        size_t const        cchEolSeq  = Compiler.cchEolSeq;
     4355
     4356        /*
     4357         * Process input lines.
     4358         *
     4359         * The code here concerns itself with getting the next line in an efficient
     4360         * manner, very basic classification and trying out corresponding handlers.
     4361         * The real work is done in the handlers.
     4362         */
     4363        while (offNext < cchContent)
     4364        {
     4365            size_t offFirstWord;
     4366
     4367            /*
     4368             * Find the end of the next line.
     4369             */
     4370            KMK_CC_ASSERT(off == offNext);
     4371
     4372            /* Simple case: No escaped EOL, nor the end of the input. */
     4373            pchTmp = (const char *)memchr(&pszContent[offNext], chFirstEol, cchContent - offNext);
     4374            if (pchTmp && (&pszContent[offNext] == pchTmp || pchTmp[-1] != '\\') )
     4375            {
     4376                if (   cchEolSeq == 1
     4377                    || pchTmp[1] == Compiler.chSecondEol)
     4378                {
     4379                    /* Frequent: Blank line or comment line. Skip. */
     4380                    if (   &pszContent[offNext] == pchTmp
     4381                        || pszContent[offNext] == '#')
     4382                    {
     4383                        iLine++;
     4384                        off = offNext += cchEolSeq;
     4385                        continue;
     4386                    }
     4387
     4388                    cchLine = pchTmp - pszContent;
     4389                    offNext = cchLine + cchEolSeq;
     4390                    Compiler.cEscEols = 0;
     4391                    Compiler.iEscEol  = 0;
     4392
     4393                    offFirstWord = off;
     4394                    while (offFirstWord < cchLine && KMK_CC_EVAL_IS_SPACE(pszContent[offFirstWord]))
     4395                        offFirstWord++;
     4396                }
     4397                else
     4398                    kmk_cc_eval_fatal_eol(&Compiler, pchTmp, iLine, off);
     4399            }
     4400            /* The complicated, less common cases. */
     4401            else
     4402            {
     4403                Compiler.cEscEols = 0;
     4404                Compiler.iEscEol  = 0;
     4405                offFirstWord = offNext;
     4406                for (;;)
     4407                {
     4408                    if (offFirstWord == offNext)
     4409                        while (offFirstWord < cchLine && KMK_CC_EVAL_IS_SPACE(pszContent[offFirstWord]))
     4410                            offFirstWord++;
     4411
     4412                    if (pchTmp)
     4413                    {
     4414                        if (   cchEolSeq == 1
     4415                            || pchTmp[1] == Compiler.chSecondEol)
     4416                        {
     4417                            size_t offEsc;
     4418                            offNext = pchTmp - pszContent;
     4419
     4420                            /* Is it an escape sequence? */
     4421                            if (   !offNext
     4422                                || pchTmp[-1] != '\\')
     4423                            {
     4424                                cchLine  = offNext - off;
     4425                                offNext += cchEolSeq;
     4426                                break;
     4427                            }
     4428                            if (offNext < 2 || pchTmp[-2] != '\\')
     4429                                offEsc = offNext - 1;
     4430                            else
     4431                            {
     4432                                /* Count how many backslashes there are. Must be odd number to be an escape
     4433                                   sequence.  Normally we keep half of them, except for command lines.  */
     4434                                size_t cSlashes = 2;
     4435                                while (offNext >= cSlashes && pchTmp[0 - cSlashes] == '\\')
     4436                                    cSlashes--;
     4437                                if (!(cSlashes & 1))
     4438                                {
     4439                                    cchLine  = offNext - off;
     4440                                    offNext += cchEolSeq;
     4441                                    break;
     4442                                }
     4443                                offEsc = offNext - (cSlashes >> 1);
     4444                            }
     4445
     4446                            /* Record it. */
     4447                            if (Compiler.cEscEols < Compiler.cEscEolsAllocated) { /* likely */ }
     4448                            else
     4449                            {
     4450                                KMK_CC_ASSERT(Compiler.cEscEols == Compiler.cEscEolsAllocated);
     4451                                Compiler.cEscEolsAllocated = Compiler.cEscEolsAllocated
     4452                                                           ? Compiler.cEscEolsAllocated * 2 : 2;
     4453                                Compiler.paEscEols = (PKMKCCEVALESCEOL)xrealloc(Compiler.paEscEols,
     4454                                                                                  Compiler.cEscEolsAllocated
     4455                                                                                * sizeof(Compiler.paEscEols[0]));
     4456                            }
     4457                            Compiler.paEscEols[Compiler.cEscEols].offEsc = offEsc;
     4458                            Compiler.paEscEols[Compiler.cEscEols].offEol = offNext;
     4459                            Compiler.cEscEols++;
     4460
     4461                            /* Advance. */
     4462                            offNext += cchEolSeq;
     4463                            if (offFirstWord == offEsc)
     4464                            {
     4465                                offFirstWord = offNext;
     4466                                Compiler.iEscEol++;
     4467                            }
     4468                        }
     4469                        else
     4470                            kmk_cc_eval_fatal_eol(&Compiler, pchTmp, iLine, off);
     4471                    }
     4472                    else
     4473                    {
     4474                        /* End of input. Happens only once per compilation, nothing to optimize for. */
     4475                        offNext = cchContent;
     4476                        cchLine = cchContent - off;
     4477                        break;
     4478                    }
     4479                    pchTmp = (const char *)memchr(&pszContent[offNext], chFirstEol, cchContent - offNext);
     4480                }
     4481            }
     4482            KMK_CC_ASSERT(offNext       <= cchContent);
     4483            KMK_CC_ASSERT(off + cchLine <= cchContent && cchLine <= cchContent);
     4484            KMK_CC_ASSERT(offFirstWord  <= cchLine);
     4485            KMK_CC_ASSERT(offFirstWord  >= off);
     4486            KMK_CC_ASSERT(pszContent[offFirstWord] != ' ' && pszContent[offFirstWord] != '\t');
     4487
     4488            /*
     4489             * Check for comments and skip blank lines.
     4490             */
     4491            if (   offFirstWord < cchLine
     4492                && pszContent[offFirstWord] != '#')
     4493            {
     4494                const char *pchWord = &pszContent[offFirstWord];
     4495                size_t      cchLeft = cchLine - offFirstWord;
     4496                pchTmp = (const char *)memchr(pchWord, '#', cchLeft); /** @todo not correct for command lines! */
     4497                if (pchTmp)
     4498                {
     4499                    cchLeft = pchTmp - pchWord;
     4500                    cchLine = pchTmp - &pszContent[off];
     4501                }
     4502
     4503                /*
     4504                 * Update the compiler state that we pass around.
     4505                 */
     4506                Compiler.iLine   = iLine;
     4507                Compiler.offLine = off;
     4508                Compiler.cchLine = cchLine;
     4509
     4510                /*
     4511                 * Command? Ignore command prefix if no open recipe (SunOS 4 behavior).
     4512                 */
     4513                if (   pszContent[off] == Compiler.chCmdPrefix
     4514                    && (Compiler.pRecipe || Compiler.fNoTargetRecipe))
     4515                {
     4516                    if (!Compiler.fNoTargetRecipe)
     4517                        kmk_cc_eval_handle_command(&Compiler, &pszContent[off], cchLine);
     4518                }
     4519                else
     4520                {
     4521                    /*
     4522                     * If not a directive or variable qualifier, it's either a variable
     4523                     * assignment or a recipe.
     4524                     */
     4525                    char ch = *pchWord;
     4526                    if (   !KMK_CC_EVAL_IS_1ST_IN_KEYWORD(ch)
     4527                        || !KMK_CC_EVAL_IS_2ND_IN_KEYWORD(pchWord[1])
     4528                        || !kmk_cc_eval_try_handle_keyword(&Compiler, ch, pchWord, cchLeft) )
     4529                    {
     4530                        pchTmp = (const char *)memchr(pchWord, '=', cchLeft);
     4531                        if (   !pchTmp
     4532                            || !kmk_cc_eval_try_handle_assignment(&Compiler, pchTmp, pchWord, cchLeft, 0) )
     4533                            kmk_cc_eval_handle_recipe(&Compiler, pchTmp, pchWord, cchLeft);
     4534                    }
     4535                    /* else: handled a keyword expression */
     4536                }
     4537            }
     4538
     4539            /*
     4540             * Advance to the next line.
     4541             */
     4542            off    = offNext;
     4543            iLine += Compiler.cEscEols + 1;
     4544        }
     4545    }
     4546
     4547    /*
     4548     * Check whether
     4549     */
     4550
     4551    kmk_cc_eval_delete_compiler(&Compiler);
     4552    return 0;
     4553}
     4554
     4555
    24414556/*#define KMK_CC_EVAL_ENABLE*/
     4557
     4558static PKMKCCEVALPROG kmk_cc_eval_compile(const char *pszContent, size_t cchContent,
     4559                                          const char *pszFilename, unsigned iLine, const char *pszVarName)
     4560{
     4561    /*
     4562     * Estimate block size, allocate one and initialize it.
     4563     */
     4564    PKMKCCEVALPROG  pEvalProg;
     4565    PKMKCCBLOCK     pBlock;
     4566    pEvalProg = kmk_cc_block_alloc_first(&pBlock, sizeof(*pEvalProg), cchContent / 32); /** @todo adjust */
     4567    if (pEvalProg)
     4568    {
     4569        int rc = 0;
     4570
     4571        pEvalProg->pBlockTail   = pBlock;
     4572        pEvalProg->pFirstInstr  = (PKMKCCEVALCORE)kmk_cc_block_get_next_ptr(pBlock);
     4573        pEvalProg->pszFilename  = pszFilename ? pszFilename : "<unknown>";
     4574        pEvalProg->pszVarName   = pszVarName;
     4575        pEvalProg->cRefs        = 1;
     4576#ifdef KMK_CC_STRICT
     4577        pEvalProg->uInputHash   = kmk_cc_debug_string_hash_n(0, pszContent, cchContent);
     4578#endif
     4579
     4580        /*
     4581         * Do the actual compiling.
     4582         */
     4583#ifdef KMK_CC_EVAL_ENABLE
     4584        if (kmk_cc_eval_compile_worker(pEvalProg, pszContent, cchContent, iLine) == 0)
     4585#else
     4586        if (0)
     4587#endif
     4588        {
     4589#ifdef KMK_CC_WITH_STATS
     4590            pBlock = pEvalProg->pBlockTail;
     4591            if (!pBlock->pNext)
     4592                g_cSingleBlockEvalProgs++;
     4593            else if (!pBlock->pNext->pNext)
     4594                g_cTwoBlockEvalProgs++;
     4595            else
     4596                g_cMultiBlockEvalProgs++;
     4597            for (; pBlock; pBlock = pBlock->pNext)
     4598            {
     4599                g_cBlocksAllocatedEvalProgs++;
     4600                g_cbAllocatedEvalProgs += pBlock->cbBlock;
     4601                g_cbUnusedMemEvalProgs += pBlock->cbBlock - pBlock->offNext;
     4602            }
     4603#endif
     4604            return pEvalProg;
     4605        }
     4606        kmk_cc_block_free_list(pEvalProg->pBlockTail);
     4607    }
     4608    return NULL;
     4609}
    24424610
    24434611
     
    24504618struct kmk_cc_evalprog   *kmk_cc_compile_variable_for_eval(struct variable *pVar)
    24514619{
    2452     return NULL;
     4620    PKMKCCEVALPROG pEvalProg = pVar->evalprog;
     4621    if (!pEvalProg)
     4622    {
     4623#ifdef KMK_CC_EVAL_ENABLE
     4624        pEvalProg = kmk_cc_eval_compile(pVar->value, pVar->value_length,
     4625                                        pVar->fileinfo.filenm, pVar->fileinfo.lineno, pVar->name);
     4626        pVar->evalprog = pEvalProg;
     4627#endif
     4628        g_cVarForEvalCompilations++;
     4629    }
     4630    return pEvalProg;
    24534631}
    24544632
     
    24624640struct kmk_cc_evalprog   *kmk_cc_compile_file_for_eval(FILE *pFile, const char *pszFilename)
    24634641{
    2464 #ifdef KMK_CC_EVAL_ENABLE
     4642    PKMKCCEVALPROG  pEvalProg;
     4643
    24654644    /*
    24664645     * Read the entire file into a zero terminate memory buffer.
    24674646     */
    2468     size_t      cchContent = 0;
    2469     char       *pszContent = NULL;
     4647    size_t          cchContent = 0;
     4648    char           *pszContent = NULL;
    24704649    struct stat st;
    24714650    if (!fstat(fileno(pFile), &st))
     
    25004679     * Call common function to do the compilation.
    25014680     */
    2502     //kmk_cc_eval_compile_common()
     4681    pEvalProg = kmk_cc_eval_compile(pszContent, cchContent, pszFilename, 1, NULL /*pszVarName*/);
     4682    g_cFileForEvalCompilations++;
    25034683
    25044684    free(pszContent);
    2505     return NULL;
    2506 #else
    2507     return NULL;
    2508 #endif
     4685    if (!pEvalProg)
     4686        fseek(pFile, 0, SEEK_SET);
     4687    return pEvalProg;
    25094688}
    25104689
Note: See TracChangeset for help on using the changeset viewer.

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