1 #ifdef CONFIG_WITH_COMPILER
2 /* $Id: kmk_cc_exec.c 2817 2016-08-14 12:18:20Z bird $ */
3 /** @file
4  * kmk_cc - Make "Compiler".
5  */
6 
7 /*
8  * Copyright (c) 2015 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
9  *
10  * This file is part of kBuild.
11  *
12  * kBuild is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * kBuild is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with kBuild.  If not, see <http://www.gnu.org/licenses/>
24  *
25  */
26 
27 
28 /*********************************************************************************************************************************
29 *   Header Files                                                                                                                 *
30 *********************************************************************************************************************************/
31 #include "make.h"
32 
33 #include "dep.h"
34 #include "variable.h"
35 #include "rule.h"
36 #include "debug.h"
37 #include "hash.h"
38 #include <ctype.h>
39 #ifdef HAVE_STDINT_H
40 # include <stdint.h>
41 #endif
42 #include <stdarg.h>
43 #include <assert.h>
44 #include "k/kDefs.h"
45 #include "k/kTypes.h"
46 
47 
48 /*********************************************************************************************************************************
49 *   Defined Constants And Macros                                                                                                 *
50 *********************************************************************************************************************************/
51 /** @def KMK_CC_WITH_STATS
52  * Enables the collection of extra statistics. */
53 #ifndef KMK_CC_WITH_STATS
54 # ifdef CONFIG_WITH_MAKE_STATS
55 #  define KMK_CC_WITH_STATS
56 # endif
57 #endif
58 
59 /** @def KMK_CC_STRICT
60  * Indicates whether assertions and other checks are enabled. */
61 #ifndef KMK_CC_STRICT
62 # ifndef NDEBUG
63 #  define KMK_CC_STRICT
64 # endif
65 #endif
66 
67 #ifdef KMK_CC_STRICT
68 # ifdef _MSC_VER
69 #  define KMK_CC_ASSERT(a_TrueExpr)         do { if (!(a_TrueExpr)) __debugbreak(); } while (0)
70 # elif defined(__GNUC__) && (defined(KBUILD_ARCH_X86) || defined(KBUILD_ARCH_AMD64))
71 #  define KMK_CC_ASSERT(a_TrueExpr)         do { if (!(a_TrueExpr)) __asm__ __volatile__("int3;nop"); } while (0)
72 # else
73 #  define KMK_CC_ASSERT(a_TrueExpr)         assert(a_TrueExpr)
74 # endif
75 #else
76 # define KMK_CC_ASSERT(a_TrueExpr)          do {} while (0)
77 #endif
78 #define KMK_CC_ASSERT_ALIGNED(a_uValue, a_uAlignment) \
79     KMK_CC_ASSERT( ((a_uValue) & ((a_uAlignment) - 1)) == 0 )
80 
81 
82 /** @def KMK_CC_OFFSETOF
83  * Offsetof for simple stuff.  */
84 #if defined(__GNUC__)
85 # define KMK_CC_OFFSETOF(a_Struct, a_Member)        __builtin_offsetof(a_Struct, a_Member)
86 #else
87 # define KMK_CC_OFFSETOF(a_Struct, a_Member)        ( (uintptr_t)&( ((a_Struct *)(void *)0)->a_Member) )
88 #endif
89 
90 /** def KMK_CC_SIZEOF_MEMBER   */
91 #define KMK_CC_SIZEOF_MEMBER(a_Struct, a_Member)    ( sizeof( ((a_Struct *)(void *)0x1000)->a_Member) )
92 
93 /** @def KMK_CC_SIZEOF_VAR_STRUCT
94  * Size of a struct with a variable sized array as the final member. */
95 #define KMK_CC_SIZEOF_VAR_STRUCT(a_Struct, a_FinalArrayMember, a_cArray) \
96     ( KMK_CC_OFFSETOF(a_Struct, a_FinalArrayMember) + KMK_CC_SIZEOF_MEMBER(a_Struct, a_FinalArrayMember) * (a_cArray) )
97 
98 
99 
100 /** @def KMK_CC_STATIC_ASSERT_EX
101  * Compile time assertion with text.
102  */
103 #ifdef _MSC_VER_
104 # if _MSC_VER >= 1600
105 #  define KMK_CC_STATIC_ASSERT_EX(a_Expr, a_szExpl) static_assert(a_Expr, a_szExpl)
106 # else
107 #  define KMK_CC_STATIC_ASSERT_EX(a_Expr, a_szExpl) typedef int RTASSERTVAR[(a_Expr) ? 1 : 0]
108 # endif
109 #elif defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__)
110 # define KMK_CC_STATIC_ASSERT_EX(a_Expr, a_szExpl)     static_assert(a_Expr, a_szExpl)
111 #elif !defined(__GNUC__) && !defined(__IBMC__) && !defined(__IBMCPP__)
112 # define KMK_CC_STATIC_ASSERT_EX(a_Expr, a_szExpl)  typedef int KMK_CC_STATIC_ASSERT_EX_TYPE[(a_Expr) ? 1 : 0]
113 #else
114 # define KMK_CC_STATIC_ASSERT_EX(a_Expr, a_szExpl)  extern int KMK_CC_STATIC_ASSERT_EX_VAR[(a_Expr) ? 1 : 0]
115 extern int KMK_CC_STATIC_ASSERT_EX_VAR[1];
116 #endif
117 /** @def KMK_CC_STATIC_ASSERT
118  * Compile time assertion, simple variant.
119  */
120 #define KMK_CC_STATIC_ASSERT(a_Expr)                KMK_CC_STATIC_ASSERT_EX(a_Expr, #a_Expr)
121 
122 
123 /** Aligns a size for the block allocator. */
124 #define KMK_CC_BLOCK_ALIGN_SIZE(a_cb)               ( ((a_cb) + (sizeof(void *) - 1U)) & ~(uint32_t)(sizeof(void *) - 1U) )
125 
126 /** How to declare a no-return function.
127  * Place between scope (if any) and return type.  */
128 #ifdef _MSC_VER
129 # define KMK_CC_FN_NO_RETURN                        __declspec(noreturn)
130 #elif defined(__GNUC__)
131 # define KMK_CC_FN_NO_RETURN                        __attribute__((__noreturn__))
132 #endif
133 
134 
135 /** @defgroup grp_kmk_cc_evalprog Makefile Evaluation
136  * @{
137  */
138 #if 1
139 # define KMK_CC_EVAL_DPRINTF_UNPACK(...)            __VA_ARGS__
140 # define KMK_CC_EVAL_DPRINTF(a)                     fprintf(stderr, KMK_CC_EVAL_DPRINTF_UNPACK a)
141 #else
142 # define KMK_CC_EVAL_DPRINTF(a)                     do { } while (0)
143 #endif
144 
145 /** @name KMK_CC_EVAL_QUALIFIER_XXX - Variable qualifiers.
146  * @{ */
147 #define KMK_CC_EVAL_QUALIFIER_LOCAL         1
148 #define KMK_CC_EVAL_QUALIFIER_EXPORT        2
149 #define KMK_CC_EVAL_QUALIFIER_OVERRIDE      4
150 #define KMK_CC_EVAL_QUALIFIER_PRIVATE       8
151 /** @} */
152 
153 /** Eval: Max nesting depth of makefile conditionals.
154  * Affects stack usage in kmk_cc_eval_compile_worker.  */
155 #define KMK_CC_EVAL_MAX_IF_DEPTH            32
156 /** Eval: Maximum number of escaped end of line sequences to track.
157  * Affects stack usage in kmk_cc_eval_compile_worker, but not the actual
158  * number of consequtive escaped newlines in the input file/variable. */
159 #define KMK_CC_EVAL_MAX_ESC_EOLS            2
160 
161 /** Minimum keyword length. */
162 #define KMK_CC_EVAL_KEYWORD_MIN             2
163 /** Maximum keyword length. */
164 #define KMK_CC_EVAL_KEYWORD_MAX             16
165 
166 /** @name KMK_CC_EVAL_CH_XXX - flags found in g_abEvalCcChars.
167  * @{ */
168 /** Normal character, nothing special. */
169 #define KMK_CC_EVAL_CH_NORMAL                       UINT16_C(0)
170 /** Blank character. */
171 #define KMK_CC_EVAL_CH_BLANK                        UINT16_C(1)
172 #define KMK_CC_EVAL_IS_BLANK(a_ch)                  (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_BLANK)
173 /** Space character. */
174 #define KMK_CC_EVAL_CH_SPACE                        UINT16_C(2)
175 #define KMK_CC_EVAL_IS_SPACE(a_ch)                  (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_SPACE)
176 /** Space character or potential EOL escape backslash. */
177 #define KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH           UINT16_C(4)
178 #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)
179 /** Anything we need to take notice of when parsing something could be a
180  * variable name or a recipe.
181  * All space characters, backslash (EOL escape), variable expansion dollar,
182  * variable assignment operator chars, recipe colon and recipe percent. */
183 #define KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE          UINT16_C(8)
184 #define KMK_CC_EVAL_IS_SPACE_VAR_OR_RECIPE(a_ch)    (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE)
185 /** Dollar character (possible variable expansion). */
186 #define KMK_CC_EVAL_CH_DOLLAR                       UINT16_C(16)
187 #define KMK_CC_EVAL_IS_DOLLAR(a_ch)                 (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_DOLLAR)
188 /** Dollar character (possible variable expansion). */
189 #define KMK_CC_EVAL_CH_BACKSLASH                    UINT16_C(32)
190 #define KMK_CC_EVAL_IS_BACKSLASH(a_ch)              (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_BACKSLASH)
191 /** Possible EOL character. */
192 #define KMK_CC_EVAL_CH_EOL_CANDIDATE                UINT16_C(64)
193 #define KMK_CC_EVAL_IS_EOL_CANDIDATE(a_ch)          (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_EOL_CANDIDATE)
194 /** First character in a keyword. */
195 #define KMK_CC_EVAL_CH_1ST_IN_KEYWORD               UINT16_C(128)
196 #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)
197 /** Second character in a keyword. */
198 #define KMK_CC_EVAL_CH_2ND_IN_KEYWORD               UINT16_C(256)
199 #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)
200 /** First character in a variable qualifier keyword or 'define'. */
201 #define KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD      UINT16_C(512)
202 #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)
203 /** Used when parsing variable names, looking for the end of a nested
204  *  variable reference.  Matches parentheses and backslash (escaped eol). */
205 #define KMK_CC_EVAL_CH_PAREN_OR_SLASH               UINT16_C(1024)
206 #define KMK_CC_EVAL_IS_PAREN_OR_SLASH(a_ch)         (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_PAREN_OR_SLASH)
207 /** Used when parsing ifeq/ifneq (,) sequences.
208  * Matches parentheses, comma and dollar (for non-plain string detection). */
209 #define KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR        UINT16_C(2048)
210 #define KMK_CC_EVAL_IS_PAREN_COMMA_OR_DOLLAR(a_ch)  (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR)
211 
212 /** Test of space or dollar characters. */
213 #define KMK_CC_EVAL_IS_SPACE_OR_DOLLAR(a_ch)        (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & (KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_DOLLAR))
214 /** Test of space, dollar or backslash (possible EOL escape) characters. */
215 #define KMK_CC_EVAL_IS_SPACE_DOLLAR_OR_SLASH(a_ch)  (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & (KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_DOLLAR | KMK_CC_EVAL_CH_BACKSLASH))
216 /** Test of space, dollar, backslash (possible EOL escape) or variable
217  * assingment characters. */
218 #define KMK_CC_EVAL_IS_SPACE_DOLLAR_SLASH_OR_ASSIGN(a_ch)   \
219     (KMK_CC_EVAL_BM_GET(g_abEvalCcChars, a_ch) & (KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE | KMK_CC_EVAL_CH_DOLLAR))
220 /** @} */
221 
222 /** Sets a bitmap entry.
223  * @param   a_abBitmap      Typically g_abEvalCcChars.
224  * @param   a_ch            The character to set.
225  * @param   a_uVal          The value to OR in.  */
226 #define KMK_CC_EVAL_BM_OR(g_abBitmap, a_ch, a_uVal) do { (g_abBitmap)[(unsigned char)(a_ch)] |= (a_uVal); } while (0)
227 
228 /** Gets a bitmap entry.
229  * @returns The value corresponding to @a a_ch.
230  * @param   a_abBitmap      Typically g_abEvalCcChars.
231  * @param   a_ch            The character to set. */
232 #define KMK_CC_EVAL_BM_GET(g_abBitmap, a_ch)        ( (g_abBitmap)[(unsigned char)(a_ch)] )
233 
234 /** @} */
235 
236 
237 /*********************************************************************************************************************************
238 *   Structures and Typedefs                                                                                                      *
239 *********************************************************************************************************************************/
240 /**
241  * Block of expand instructions.
242  *
243  * To avoid wasting space on "next" pointers, as well as a lot of time walking
244  * these chains when destroying programs, we work with blocks of instructions.
245  */
246 typedef struct kmk_cc_block
247 {
248     /** The pointer to the next block (LIFO). */
249     struct kmk_cc_block        *pNext;
250     /** The size of this block. */
251     uint32_t                    cbBlock;
252     /** The offset of the next free byte in the block.  When set to cbBlock the
253      *  block is 100% full. */
254     uint32_t                    offNext;
255 } KMKCCBLOCK;
256 typedef KMKCCBLOCK *PKMKCCBLOCK;
257 
258 
259 /** @defgroup grp_kmk_cc_exp   String Expansion
260  * @{*/
261 
262 /**
263  * String expansion statistics.
264  */
265 typedef struct KMKCCEXPSTATS
266 {
267     /** Recent average size. */
268     uint32_t                    cchAvg;
269 } KMKCCEXPSTATS;
270 typedef KMKCCEXPSTATS *PKMKCCEXPSTATS;
271 
272 /**
273  * Expansion instructions.
274  */
275 typedef enum KMKCCEXPINSTR
276 {
277     /** Copy a plain string. */
278     kKmkCcExpInstr_CopyString = 0,
279     /** Insert an expanded variable value, which name we already know.  */
280     kKmkCcExpInstr_PlainVariable,
281     /** Insert an expanded variable value, the name is dynamic (sub prog). */
282     kKmkCcExpInstr_DynamicVariable,
283     /** Insert an expanded variable value, which name we already know, doing
284      * search an replace on a string. */
285     kKmkCcExpInstr_SearchAndReplacePlainVariable,
286     /** Insert the output of function that requires no argument expansion. */
287     kKmkCcExpInstr_PlainFunction,
288     /** Insert the output of function that requires dynamic expansion of one ore
289      * more arguments.  (Dynamic is perhaps not such a great name, but whatever.) */
290     kKmkCcExpInstr_DynamicFunction,
291     /** Jump to a new instruction block. */
292     kKmkCcExpInstr_Jump,
293     /** We're done, return.  Has no specific structure. */
294     kKmkCcExpInstr_Return,
295     /** The end of valid instructions (exclusive). */
296     kKmkCcExpInstr_End
297 } KMKCCEXPINSTR;
298 
299 /** Instruction core. */
300 typedef struct kmk_cc_exp_core
301 {
302     /** The instruction opcode number (KMKCCEXPINSTR). */
303     KMKCCEXPINSTR           enmOpcode;
304 } KMKCCEXPCORE;
305 typedef KMKCCEXPCORE *PKMKCCEXPCORE;
306 
307 /**
308  * String expansion subprogram.
309  */
310 #pragma pack(1) /* save some precious bytes */
311 typedef struct kmk_cc_exp_subprog
312 {
313     /** Pointer to the first instruction. */
314     PKMKCCEXPCORE           pFirstInstr;
315     /** Statistics. */
316     KMKCCEXPSTATS           Stats;
317 } KMKCCEXPSUBPROG;
318 #pragma pack()
319 typedef KMKCCEXPSUBPROG *PKMKCCEXPSUBPROG;
320 KMK_CC_STATIC_ASSERT(sizeof(KMKCCEXPSUBPROG) == 12 || sizeof(void *) != 8);
321 
322 
323 /**
324  * String expansion subprogram or plain string.
325  */
326 #pragma pack(1) /* save some precious bytes */
327 typedef struct kmk_cc_exp_subprog_or_string
328 {
329     /** Either a plain string pointer or a subprogram.   */
330     union
331     {
332         /** Subprogram for expanding this argument. */
333         KMKCCEXPSUBPROG     Subprog;
334         /** Pointer to the plain string. */
335         struct
336         {
337             /** Pointer to the string. */
338             const char     *psz;
339             /** String length. */
340             uint32_t        cch;
341         } Plain;
342     } u;
343     /** Set if subprogram (u.Subprog), clear if plain string (u.Plain). */
344     uint8_t                 fSubprog;
345     /** Set if the plain string is kept in the variable_strcache.
346      * @remarks Here rather than in u.Plain to make use of alignment padding. */
347     uint8_t                 fPlainIsInVarStrCache;
348     /** Context/user specific. */
349     uint8_t                 bUser;
350     /** Context/user specific #2. */
351     uint8_t                 bUser2;
352 } KMKCCEXPSUBPROGORPLAIN;
353 #pragma pack()
354 typedef KMKCCEXPSUBPROGORPLAIN *PKMKCCEXPSUBPROGORPLAIN;
355 KMK_CC_STATIC_ASSERT(  sizeof(void *) == 8
356                      ? sizeof(KMKCCEXPSUBPROGORPLAIN) == 16
357                      : sizeof(void *) == 4
358                      ? sizeof(KMKCCEXPSUBPROGORPLAIN) == 12
359                      : 1);
360 
361 /**
362  * kKmkCcExpInstr_CopyString instruction format.
363  */
364 typedef struct kmk_cc_exp_copy_string
365 {
366     /** The core instruction. */
367     KMKCCEXPCORE            Core;
368     /** The number of bytes to copy. */
369     uint32_t                cchCopy;
370     /** Pointer to the source string (not terminated at cchCopy). */
371     const char             *pachSrc;
372 } KMKCCEXPCOPYSTRING;
373 typedef KMKCCEXPCOPYSTRING *PKMKCCEXPCOPYSTRING;
374 
375 /**
376  * kKmkCcExpInstr_PlainVariable instruction format.
377  */
378 typedef struct kmk_cc_exp_plain_variable
379 {
380     /** The core instruction. */
381     KMKCCEXPCORE            Core;
382     /** The name of the variable (points into variable_strcache). */
383     const char             *pszName;
384 } KMKCCEXPPLAINVAR;
385 typedef KMKCCEXPPLAINVAR *PKMKCCEXPPLAINVAR;
386 
387 /**
388  * kKmkCcExpInstr_DynamicVariable instruction format.
389  */
390 typedef struct kmk_cc_exp_dynamic_variable
391 {
392     /** The core instruction. */
393     KMKCCEXPCORE            Core;
394     /** The subprogram that will give us the variable name. */
395     KMKCCEXPSUBPROG         Subprog;
396     /** Where to continue after this instruction.  (This is necessary since the
397      * instructions of the subprogram are emitted after this instruction.) */
398     PKMKCCEXPCORE           pNext;
399 } KMKCCEXPDYNVAR;
400 typedef KMKCCEXPDYNVAR *PKMKCCEXPDYNVAR;
401 
402 /**
403  * kKmkCcExpInstr_SearchAndReplacePlainVariable instruction format.
404  */
405 typedef struct kmk_cc_exp_sr_plain_variable
406 {
407     /** The core instruction. */
408     KMKCCEXPCORE            Core;
409     /** Where to continue after this instruction.  (This is necessary since the
410      * instruction contains string data of variable size.) */
411     PKMKCCEXPCORE           pNext;
412     /** The name of the variable (points into variable_strcache). */
413     const char             *pszName;
414     /** Search pattern.  */
415     const char             *pszSearchPattern;
416     /** Replacement pattern. */
417     const char             *pszReplacePattern;
418     /** Offset into pszSearchPattern of the significant '%' char. */
419     uint32_t                offPctSearchPattern;
420     /** Offset into pszReplacePattern of the significant '%' char. */
421     uint32_t                offPctReplacePattern;
422 } KMKCCEXPSRPLAINVAR;
423 typedef KMKCCEXPSRPLAINVAR *PKMKCCEXPSRPLAINVAR;
424 
425 /**
426  * Instruction format parts common to both kKmkCcExpInstr_PlainFunction and
427  * kKmkCcExpInstr_DynamicFunction.
428  */
429 typedef struct kmk_cc_exp_function_core
430 {
431     /** The core instruction. */
432     KMKCCEXPCORE            Core;
433     /** Number of arguments. */
434     uint32_t                cArgs; /**< @todo uint16_t to save 7 bytes of unecessary alignment padding on 64-bit systems, or merge fDirty into this member. */
435     /** Set if the function could be modifying the input arguments. */
436     uint8_t                 fDirty;
437     /** Where to continue after this instruction.  (This is necessary since the
438      * instructions are of variable size and may be followed by string data.) */
439     PKMKCCEXPCORE           pNext;
440     /**
441      * Pointer to the function table entry.
442      *
443      * @returns New variable buffer position.
444      * @param   pchDst      Current variable buffer position.
445      * @param   papszArgs   Pointer to a NULL terminated array of argument strings.
446      * @param   pszFuncName The name of the function being called.
447      */
448     char *                (*pfnFunction)(char *pchDst, char **papszArgs, const char *pszFuncName);
449     /** Pointer to the function name in the variable string cache. */
450     const char             *pszFuncName;
451 } KMKCCEXPFUNCCORE;
452 typedef KMKCCEXPFUNCCORE *PKMKCCEXPFUNCCORE;
453 
454 /**
455  * Instruction format for kKmkCcExpInstr_PlainFunction.
456  */
457 typedef struct kmk_cc_exp_plain_function
458 {
459     /** The bits comment to both plain and dynamic functions. */
460     KMKCCEXPFUNCCORE        FnCore;
461     /** Variable sized argument list (cArgs + 1 in length, last entry is NULL).
462      * The string pointers are to memory following this instruction, to memory in
463      * the next block or to memory in the variable / makefile we're working on
464      * (if zero terminated appropriately). */
465     const char             *apszArgs[1];
466 } KMKCCEXPPLAINFUNC;
467 typedef KMKCCEXPPLAINFUNC *PKMKCCEXPPLAINFUNC;
468 /** Calculates the size of an KMKCCEXPPLAINFUNC structure with the apszArgs
469  * member holding a_cArgs entries plus a NULL terminator. */
470 #define KMKCCEXPPLAINFUNC_SIZE(a_cArgs) KMK_CC_SIZEOF_VAR_STRUCT(KMKCCEXPDYNFUNC, aArgs, (a_cArgs) + 1)
471 
472 /**
473  * Instruction format for kKmkCcExpInstr_DynamicFunction.
474  */
475 typedef struct kmk_cc_exp_dyn_function
476 {
477     /** The bits comment to both plain and dynamic functions. */
478     KMKCCEXPFUNCCORE        FnCore;
479     /** Variable sized argument list (FnCore.cArgs in length).
480      * The subprograms / strings are allocated after this array (or in the next
481      * block). */
482     KMKCCEXPSUBPROGORPLAIN  aArgs[1];
483 } KMKCCEXPDYNFUNC;
484 typedef KMKCCEXPDYNFUNC *PKMKCCEXPDYNFUNC;
485 /** Calculates the size of an KMKCCEXPDYNFUNC structure with the apszArgs
486  * member holding a_cArgs entries (no zero terminator). */
487 #define KMKCCEXPDYNFUNC_SIZE(a_cArgs)  KMK_CC_SIZEOF_VAR_STRUCT(KMKCCEXPDYNFUNC, aArgs, a_cArgs)
488 
489 /**
490  * Instruction format for kKmkCcExpInstr_Jump.
491  */
492 typedef struct kmk_cc_exp_jump
493 {
494     /** The core instruction. */
495     KMKCCEXPCORE            Core;
496     /** Where to jump to (new instruction block, typically). */
497     PKMKCCEXPCORE           pNext;
498 } KMKCCEXPJUMP;
499 typedef KMKCCEXPJUMP *PKMKCCEXPJUMP;
500 
501 /**
502  * String expansion program.
503  */
504 typedef struct kmk_cc_expandprog
505 {
506     /** Pointer to the first instruction for this program. */
507     PKMKCCEXPCORE           pFirstInstr;
508     /** List of blocks for this program (LIFO). */
509     PKMKCCBLOCK             pBlockTail;
510     /** Statistics. */
511     KMKCCEXPSTATS           Stats;
512 #ifdef KMK_CC_STRICT
513     /** The hash of the input string.  Used to check that we get all the change
514      * notifications we require. */
515     uint32_t                uInputHash;
516 #endif
517     /** Reference count. */
518     uint32_t volatile       cRefs;
519 } KMKCCEXPPROG;
520 /** Pointer to a string expansion program. */
521 typedef KMKCCEXPPROG *PKMKCCEXPPROG;
522 
523 /** @} */
524 
525 
526 /** @addtogroup grp_kmk_cc_evalprog
527  * @{  */
528 
529 /** Pointer to a makefile evaluation program. */
530 typedef struct kmk_cc_evalprog *PKMKCCEVALPROG;
531 
532 /**
533  * Makefile evaluation instructions.
534  */
535 typedef enum KMKCCEVALINSTR
536 {
537     /** Jump instruction - KMKCCEVALJUMP. */
538     kKmkCcEvalInstr_jump = 0,
539 
540     /** [local|override|export] variable  = value - KMKCCEVALASSIGN.
541      * @note Can be used for target-specific variables. */
542     kKmkCcEvalInstr_assign_recursive,
543     /** [local|override|export] variable := value - KMKCCEVALASSIGN.
544      * @note Can be used for target-specific variables. */
545     kKmkCcEvalInstr_assign_simple,
546     /** [local|override|export] variable += value - KMKCCEVALASSIGN.
547      * @note Can be used for target-specific variables. */
548     kKmkCcEvalInstr_assign_append,
549     /** [local|override|export] variable -= value - KMKCCEVALASSIGN.
550      * @note Can be used for target-specific variables. */
551     kKmkCcEvalInstr_assign_prepend,
552     /** [local|override|export] variable ?= value - KMKCCEVALASSIGN.
553      * @note Can be used for target-specific variables. */
554     kKmkCcEvalInstr_assign_if_new,
555     /** [local|override|export] define variable ... endef - KMKCCEVALASSIGNDEF. */
556     kKmkCcEvalInstr_assign_define,
557 
558     /** export variable1 [variable2...] - KMKCCEVALVARIABLES. */
559     kKmkCcEvalInstr_export,
560     /** unexport variable1 [variable2...] - KMKCCEVALVARIABLES. */
561     kKmkCcEvalInstr_unexport,
562     /** export - KMKCCEVALCORE. */
563     kKmkCcEvalInstr_export_all,
564     /** unexport - KMKCCEVALCORE. */
565     kKmkCcEvalInstr_unexport_all,
566     /** [local|override] undefine - KMKCCEVALVARIABLES. */
567     kKmkCcEvalInstr_undefine,
568 
569 
570     /** [else] ifdef variable - KMKCCEVALIFDEFPLAIN. */
571     kKmkCcEvalInstr_ifdef_plain,
572     /** [else] ifndef variable - KMKCCEVALIFDEFPLAIN. */
573     kKmkCcEvalInstr_ifndef_plain,
574     /** [else] ifdef variable - KMKCCEVALIFDEFDYNAMIC. */
575     kKmkCcEvalInstr_ifdef_dynamic,
576     /** [else] ifndef variable - KMKCCEVALIFDEFDYNAMIC. */
577     kKmkCcEvalInstr_ifndef_dynamic,
578     /** [else] ifeq (a,b) - KMKCCEVALIFEQ. */
579     kKmkCcEvalInstr_ifeq,
580     /** [else] ifeq (a,b) - KMKCCEVALIFEQ. */
581     kKmkCcEvalInstr_ifneq,
582     /** [else] if1of (set-a,set-b) - KMKCCEVALIF1OF. */
583     kKmkCcEvalInstr_if1of,
584     /** [else] ifn1of (set-a,set-b) - KMKCCEVALIF1OF. */
585     kKmkCcEvalInstr_ifn1of,
586     /** [else] if expr - KMKCCEVALIFEXPR. */
587     kKmkCcEvalInstr_if,
588 
589     /** include file1 [file2...] - KMKCCEVALINCLUDE. */
590     kKmkCcEvalInstr_include,
591     /** [sinclude|-include] file1 [file2...]  - KMKCCEVALINCLUDE. */
592     kKmkCcEvalInstr_include_silent,
593     /** includedep file1 [file2...] - KMKCCEVALINCLUDE. */
594     kKmkCcEvalInstr_includedep,
595     /** includedep-queue file1 [file2...] - KMKCCEVALINCLUDE. */
596     kKmkCcEvalInstr_includedep_queue,
597     /** includedep-flush file1 [file2...] - KMKCCEVALINCLUDE. */
598     kKmkCcEvalInstr_includedep_flush,
599 
600     /** Recipe without commands (defines dependencies) - KMKCCEVALRECIPE. */
601     kKmkCcEvalInstr_recipe_no_commands,
602     /** Recipe with commands (defines dependencies) - KMKCCEVALRECIPE. */
603     kKmkCcEvalInstr_recipe_start_normal,
604     /** Recipe with commands (defines dependencies) - KMKCCEVALRECIPE. */
605     kKmkCcEvalInstr_recipe_start_double_colon,
606     /** Recipe with commands (defines dependencies) - KMKCCEVALRECIPE. */
607     kKmkCcEvalInstr_recipe_start_pattern,
608     /** Adds more commands to the current recipe - KMKCCEVALRECIPECOMMANDS. */
609     kKmkCcEvalInstr_recipe_commands,
610     /** Special instruction for indicating the end of the recipe commands - KMKCCEVALCORE. */
611     kKmkCcEvalInstr_recipe_end,
612     /** Cancel previously defined pattern rule - KMKCCEVALRECIPE.  */
613     kKmkCcEvalInstr_recipe_cancel_pattern,
614 
615     /** vpath pattern directories - KMKCCEVALVPATH. */
616     kKmkCcEvalInstr_vpath,
617     /** vpath pattern directories - KMKCCEVALVPATH. */
618     kKmkCcEvalInstr_vpath_clear_pattern,
619     /** vpath - KMKCCEVALCORE. */
620     kKmkCcEvalInstr_vpath_clear_all,
621 
622     /** The end of valid instructions (exclusive). */
623     kKmkCcEvalInstr_End
624 } KMKCCEVALINSTR;
625 
626 /**
627  * Instruction core common to all instructions.
628  */
629 typedef struct kmk_cc_eval_core
630 {
631     /** The instruction opcode number (KMKCCEVALINSTR). */
632     KMKCCEVALINSTR          enmOpcode;
633     /** The line number in the source this statement is associated with. */
634     unsigned                iLine;
635 } KMKCCEVALCORE;
636 /** Pointer to an instruction core structure. */
637 typedef KMKCCEVALCORE *PKMKCCEVALCORE;
638 
639 /**
640  * Instruction format for kKmkCcEvalInstr_jump.
641  */
642 typedef struct kmk_cc_eval_jump
643 {
644     /** The core instruction. */
645     KMKCCEVALCORE           Core;
646     /** Where to jump to (new instruction block or endif, typically). */
647     PKMKCCEVALCORE          pNext;
648 } KMKCCEVALJUMP;
649 typedef KMKCCEVALJUMP *PKMKCCEVALJUMP;
650 
651 /**
652  * Instruction format for kKmkCcEvalInstr_assign_recursive,
653  * kKmkCcEvalInstr_assign_simple, kKmkCcEvalInstr_assign_append,
654  * kKmkCcEvalInstr_assign_prepend and kKmkCcEvalInstr_assign_if_new.
655  */
656 typedef struct kmk_cc_eval_assign
657 {
658     /** The core instruction. */
659     KMKCCEVALCORE           Core;
660     /** Whether the 'export' qualifier was used. */
661     uint8_t                 fExport;
662     /** Whether the 'override' qualifier was used. */
663     uint8_t                 fOverride;
664     /** Whether the 'local' qualifier was used. */
665     uint8_t                 fLocal;
666     /** Whether the 'private' qualifier was used. */
667     uint8_t                 fPrivate;
668     /** The variable name.
669      * @remarks Plain text names are in variable_strcache. */
670     KMKCCEXPSUBPROGORPLAIN  Variable;
671     /** The value or value expression. */
672     KMKCCEXPSUBPROGORPLAIN  Value;
673     /** Pointer to the next instruction. */
674     PKMKCCEVALCORE          pNext;
675 } KMKCCEVALASSIGN;
676 typedef KMKCCEVALASSIGN *PKMKCCEVALASSIGN;
677 
678 /**
679  * Instruction format for kKmkCcEvalInstr_assign_define.
680  */
681 typedef struct kmk_cc_eval_assign_define
682 {
683     /** The assignment core structure. */
684     KMKCCEVALASSIGN         AssignCore;
685     /** Makefile evaluation program compiled from the define.
686      * NULL if it does not compile.
687      * @todo Let's see if this is actually doable... */
688     PKMKCCEVALPROG          pEvalProg;
689 } KMKCCEVALASSIGNDEF;
690 typedef KMKCCEVALASSIGNDEF *PKMKCCEVALASSIGNDEF;
691 
692 /**
693  * Instruction format for kKmkCcEvalInstr_export, kKmkCcEvalInstr_unexport and
694  * kKmkCcEvalInstr_undefine.
695  */
696 typedef struct kmk_cc_eval_variables
697 {
698     /** The core instruction. */
699     KMKCCEVALCORE           Core;
700     /** The number of variables named in aVars. */
701     uint32_t                cVars;
702     /** Whether the 'local' qualifier was used (undefine only). */
703     uint8_t                 fLocal;
704     /** Pointer to the next instruction. */
705     PKMKCCEVALCORE          pNext;
706     /** The variable names.
707      * Expressions will be expanded and split on space.
708      * @remarks Plain text names are in variable_strcache. */
709     KMKCCEXPSUBPROGORPLAIN  aVars[1];
710 } KMKCCEVALVARIABLES;
711 typedef KMKCCEVALVARIABLES *PKMKCCEVALVARIABLES;
712 /** Calculates the size of an KMKCCEVALVARIABLES structure for @a a_cVars. */
713 #define KMKCCEVALVARIABLES_SIZE(a_cVars) KMK_CC_SIZEOF_VAR_STRUCT(KMKCCEVALVARIABLES, aVars, a_cVars)
714 
715 /**
716  * Core structure for all conditionals (kKmkCcEvalInstr_if*).
717  */
718 typedef struct kmk_cc_eval_if_core
719 {
720     /** The core instruction. */
721     KMKCCEVALCORE           Core;
722     /** Condition true: Pointer to the next instruction. */
723     PKMKCCEVALCORE          pNextTrue;
724     /** Condition false: Pointer to the next instruction (i.e. 'else if*'
725      * or whatever follows 'else' / 'endif'. */
726     PKMKCCEVALCORE          pNextFalse;
727     /** Pointer to the previous conditional for 'else if*' directives.
728      * This is only to assist the compilation process. */
729     struct kmk_cc_eval_if_core *pPrevCond;
730     /** Pointer to the jump out of the true block, if followed by 'else'.
731      * This is only to assist the compilation process. */
732     PKMKCCEVALJUMP          pTrueEndJump;
733 } KMKCCEVALIFCORE;
734 typedef KMKCCEVALIFCORE *PKMKCCEVALIFCORE;
735 
736 /**
737  * Instruction format for kKmkCcEvalInstr_ifdef_plain and
738  * kKmkCcEvalInstr_ifndef_plain.
739  * The variable name is known at compilation time.
740  */
741 typedef struct kmk_cc_eval_ifdef_plain
742 {
743     /** The 'if' core structure. */
744     KMKCCEVALIFCORE         IfCore;
745     /** The name of the variable (points into variable_strcache). */
746     const char             *pszName;
747 } KMKCCEVALIFDEFPLAIN;
748 typedef KMKCCEVALIFDEFPLAIN *PKMKCCEVALIFDEFPLAIN;
749 
750 /**
751  * Instruction format for kKmkCcEvalInstr_ifdef_dynamic and
752  * kKmkCcEvalInstr_ifndef_dynamic.
753  * The variable name is dynamically expanded at run time.
754  */
755 typedef struct kmk_cc_eval_ifdef_dynamic
756 {
757     /** The 'if' core structure. */
758     KMKCCEVALIFCORE         IfCore;
759     /** The subprogram that will give us the variable name. */
760     KMKCCEXPSUBPROG         NameSubprog;
761 } KMKCCEVALIFDEFDYNAMIC;
762 typedef KMKCCEVALIFDEFDYNAMIC *PKMKCCEVALIFDEFDYNAMIC;
763 
764 /**
765  * Instruction format for kKmkCcEvalInstr_ifeq and kKmkCcEvalInstr_ifneq.
766  */
767 typedef struct kmk_cc_eval_ifeq
768 {
769     /** The 'if' core structure. */
770     KMKCCEVALIFCORE         IfCore;
771     /** The left hand side string expression (dynamic or plain). */
772     KMKCCEXPSUBPROGORPLAIN  Left;
773     /** The rigth hand side string expression (dynamic or plain). */
774     KMKCCEXPSUBPROGORPLAIN  Right;
775 } KMKCCEVALIFEQ;
776 typedef KMKCCEVALIFEQ *PKMKCCEVALIFEQ;
777 
778 /**
779  * Instruction format for kKmkCcEvalInstr_if1of and kKmkCcEvalInstr_ifn1of.
780  *
781  * @todo This can be optimized further by pre-hashing plain text items.  One of
782  *       the sides are usually plain text.
783  */
784 typedef struct kmk_cc_eval_if1of
785 {
786     /** The 'if' core structure. */
787     KMKCCEVALIFCORE         IfCore;
788     /** The left hand side string expression (dynamic or plain). */
789     KMKCCEXPSUBPROGORPLAIN  Left;
790     /** The rigth hand side string expression (dynamic or plain). */
791     KMKCCEXPSUBPROGORPLAIN  Right;
792 } KMKCCEVALIF1OF;
793 typedef KMKCCEVALIF1OF *PKMKCCEVALIF1OF;
794 
795 /**
796  * Instruction format for kKmkCcEvalInstr_if.
797  *
798  * @todo Parse and compile the expression.  At least strip whitespace in it.
799  */
800 typedef struct kmk_cc_eval_if_expr
801 {
802     /** The 'if' core structure. */
803     KMKCCEVALIFCORE         IfCore;
804     /** The expression string length. */
805     uint16_t                cchExpr;
806     /** The expression string. */
807     char                    szExpr[1];
808 } KMKCCEVALIFEXPR;
809 typedef KMKCCEVALIFEXPR *PKMKCCEVALIFEXPR;
810 /** Calculates the size of an KMKCCEVALIFEXPR structure for @a a_cchExpr long
811  * expression string (terminator is automatically added).  */
812 #define KMKCCEVALIFEXPR_SIZE(a_cchExpr) KMK_CC_BLOCK_ALIGN_SIZE(KMK_CC_SIZEOF_VAR_STRUCT(KMKCCEVALIFEXPR, szExpr, (a_cchExpr) + 1))
813 
814 /**
815  * Instruction format for kKmkCcEvalInstr_include,
816  * kKmkCcEvalInstr_include_silent, kKmkCcEvalInstr_includedep,
817  * kKmkCcEvalInstr_includedep_queue, kKmkCcEvalInstr_includedep_flush.
818  */
819 typedef struct kmk_cc_eval_include
820 {
821     /** The core instruction. */
822     KMKCCEVALCORE           Core;
823     /** The number of files. */
824     uint32_t                cFiles;
825     /** Pointer to the next instruction (subprogs and strings after this one). */
826     PKMKCCEVALCORE          pNext;
827     /** The files to be included.
828      * Expressions will be expanded and split on space.
829      * @todo Plain text file name could be replaced by file string cache entries. */
830     KMKCCEXPSUBPROGORPLAIN  aFiles[1];
831 } KMKCCEVALINCLUDE;
832 typedef KMKCCEVALINCLUDE *PKMKCCEVALINCLUDE;
833 /** Calculates the size of an KMKCCEVALINCLUDE structure for @a a_cFiles files. */
834 #define KMKCCEVALINCLUDE_SIZE(a_cFiles) KMK_CC_SIZEOF_VAR_STRUCT(KMKCCEVALINCLUDE, aFiles, a_cFiles)
835 
836 /**
837  * Instruction format for kKmkCcEvalInstr_recipe_no_commands,
838  * kKmkCcEvalInstr_recipe_start_normal,
839  * kKmkCcEvalInstr_recipe_start_double_colon, kKmkCcEvalInstr_includedep_queue,
840  * kKmkCcEvalInstr_recipe_start_pattern.
841  */
842 typedef struct kmk_cc_eval_recipe
843 {
844     /** The core instruction. */
845     KMKCCEVALCORE           Core;
846     /** The total number of files and dependencies in aFilesAndDeps. */
847     uint16_t                cFilesAndDeps;
848 
849     /** Number of targets (from index 0).
850      * This is always 1 if this is an explicit multitarget or pattern recipe,
851      * indicating the main target. */
852     uint16_t                cTargets;
853     /** Explicit multitarget & patterns: First always made target. */
854     uint16_t                iFirstAlwaysMadeTargets;
855     /** Explicit multitarget & patterns: Number of always targets. */
856     uint16_t                cAlwaysMadeTargets;
857     /** Explicit multitarget: First maybe made target. */
858     uint16_t                iFirstMaybeTarget;
859     /** Explicit multitarget: Number of maybe made targets. */
860     uint16_t                cMaybeTargets;
861 
862     /** First dependency. */
863     uint16_t                iFirstDep;
864     /** Number of ordinary dependnecies. */
865     uint16_t                cDeps;
866     /** First order only dependency. */
867     uint16_t                iFirstOrderOnlyDep;
868     /** Number of ordinary dependnecies. */
869     uint16_t                cOrderOnlyDeps;
870 
871     /** Pointer to the next instruction (subprogs and strings after this one). */
872     PKMKCCEVALCORE          pNext;
873     /** The .MUST_MAKE variable value, if present.
874      * If not present, this is a zero length plain string. */
875     KMKCCEXPSUBPROGORPLAIN  MustMake;
876     /** The target files and dependencies.
877      * This is sorted into several sections, as defined by the above indexes and
878      * counts.  Expressions will be expanded and split on space.
879      *
880      * The KMKCCEXPSUBPROGORPLAIN::bUser member one of KMKCCEVALRECIPE_FD_XXX.
881      *
882      * @todo Plain text file name could be replaced by file string cache entries. */
883     KMKCCEXPSUBPROGORPLAIN  aFilesAndDeps[1];
884 } KMKCCEVALRECIPE;
885 typedef KMKCCEVALRECIPE *PKMKCCEVALRECIPE;
886 /** Calculates the size of an KMKCCEVALRECIPE structure for @a a_cFiles
887  *  files. */
888 #define KMKCCEVALRECIPE_SIZE(a_cFilesAndDeps) KMK_CC_SIZEOF_VAR_STRUCT(KMKCCEVALRECIPE, aFilesAndDeps, a_cFilesAndDeps)
889 /** @name KMKCCEVALRECIPE_FD_XXX - Values for KMKCCEVALRECIPE::aFilesAndDeps[x].bUser
890  * @{  */
891 #define KMKCCEVALRECIPE_FD_NORMAL                   0
892 #define KMKCCEVALRECIPE_FD_SEC_EXP                  1
893 #define KMKCCEVALRECIPE_FD_SPECIAL_POSIX            2
894 #define KMKCCEVALRECIPE_FD_SPECIAL_SECONDEXPANSION  3
895 #define KMKCCEVALRECIPE_FD_SPECIAL_ONESHELL         4
896 /** @} */
897 
898 
899 /**
900  * Instruction format for kKmkCcEvalInstr_recipe_commands.
901  */
902 typedef struct kmk_cc_eval_recipe_commands
903 {
904     /** The core instruction. */
905     KMKCCEVALCORE           Core;
906     /** The number of search directories. */
907     uint32_t                cCommands;
908     /** Pointer to the next instruction (subprogs and strings after this one). */
909     PKMKCCEVALCORE          pNext;
910     /** Commands to add to the current recipe.
911      * Expressions will be expanded and split on space. */
912     KMKCCEXPSUBPROGORPLAIN  aCommands[1];
913 } KMKCCEVALRECIPECOMMANDS;
914 typedef KMKCCEVALRECIPECOMMANDS *PKMKCCEVALRECIPECOMMANDS;
915 /** Calculates the size of an KMKCCEVALRECIPECOMMANDS structure for
916  * @a a_cCommands commands. */
917 #define KMKCCEVALRECIPECOMMANDS_SIZE(a_cCommands) KMK_CC_SIZEOF_VAR_STRUCT(KMKCCEVALRECIPECOMMANDS, aCommands, a_cCommands)
918 
919 /**
920  * Instruction format for kKmkCcEvalInstr_vpath and
921  * kKmkCcEvalInstr_vpath_clear_pattern.
922  */
923 typedef struct kmk_cc_eval_vpath
924 {
925     /** The core instruction. */
926     KMKCCEVALCORE           Core;
927     /** The number of search directories.
928      * This will be zero for kKmkCcEvalInstr_vpath_clear_pattern. */
929     uint32_t                cDirs;
930     /** Pointer to the next instruction (subprogs and strings after this one). */
931     PKMKCCEVALCORE          pNext;
932     /** The pattern. */
933     KMKCCEXPSUBPROGORPLAIN  Pattern;
934     /** The directory. Expressions will be expanded and split on space. */
935     KMKCCEXPSUBPROGORPLAIN  aDirs[1];
936 } KMKCCEVALVPATH;
937 typedef KMKCCEVALVPATH *PKMKCCEVALVPATH;
938 /** Calculates the size of an KMKCCEVALVPATH structure for @a a_cFiles files. */
939 #define KMKCCEVALVPATH_SIZE(a_cFiles) KMK_CC_SIZEOF_VAR_STRUCT(KMKCCEVALVPATH, aDirs, a_cDirs)
940 
941 
942 /**
943  * Makefile evaluation program.
944  */
945 typedef struct kmk_cc_evalprog
946 {
947     /** Pointer to the first instruction for this program. */
948     PKMKCCEVALCORE          pFirstInstr;
949     /** List of blocks for this program (LIFO). */
950     PKMKCCBLOCK             pBlockTail;
951     /** The name of the file containing this program. */
952     const char             *pszFilename;
953     /** The name of the variable containing this program, if applicable.  */
954     const char             *pszVarName;
955 #ifdef KMK_CC_STRICT
956     /** The hash of the input string.  Used to check that we get all the change
957      * notifications we require. */
958     uint32_t                uInputHash;
959 #endif
960     /** Reference count. */
961     uint32_t volatile       cRefs;
962 } KMKCCEVALPROG;
963 typedef KMKCCEVALPROG *PKMKCCEVALPROG;
964 
965 /** @} */
966 
967 
968 /*********************************************************************************************************************************
969 *   Global Variables                                                                                                             *
970 *********************************************************************************************************************************/
971 static uint32_t g_cVarForExpandCompilations = 0;
972 static uint32_t g_cVarForExpandExecs = 0;
973 static uint32_t g_cVarForEvalCompilations = 0;
974 static uint32_t g_cVarForEvalExecs = 0;
975 static uint32_t g_cFileForEvalCompilations = 0;
976 static uint32_t g_cFileForEvalExecs = 0;
977 #ifdef KMK_CC_WITH_STATS
978 static uint32_t g_cBlockAllocated = 0;
979 static uint32_t g_cbAllocated = 0;
980 
981 static uint32_t g_cBlocksAllocatedExpProgs = 0;
982 static uint32_t g_cbAllocatedExpProgs = 0;
983 static uint32_t g_cSingleBlockExpProgs = 0;
984 static uint32_t g_cTwoBlockExpProgs = 0;
985 static uint32_t g_cMultiBlockExpProgs = 0;
986 static uint32_t g_cbUnusedMemExpProgs = 0;
987 
988 static uint32_t g_cBlocksAllocatedEvalProgs = 0;
989 static uint32_t g_cbAllocatedEvalProgs = 0;
990 static uint32_t g_cSingleBlockEvalProgs = 0;
991 static uint32_t g_cTwoBlockEvalProgs = 0;
992 static uint32_t g_cMultiBlockEvalProgs = 0;
993 static uint32_t g_cbUnusedMemEvalProgs = 0;
994 
995 #endif
996 
997 /** Generic character classification, taking an 'unsigned char' index.
998  * ASSUMES unsigned char is 8-bits. */
999 static uint16_t g_abEvalCcChars[256];
1000 
1001 
1002 /**
1003  * Makefile evaluation keywords.
1004  */
1005 static const char * const g_apszEvalKeywords[] =
1006 {
1007     "define",
1008     "export",
1009     "else",
1010     "endef",
1011     "endif",
1012     "ifdef",
1013     "ifndef",
1014     "ifeq",
1015     "ifneq",
1016     "if1of",
1017     "ifn1of",
1018     "if",
1019     "include",
1020     "includedep",
1021     "includedep-queue",
1022     "includedep-flush",
1023     "local",
1024     "override",
1025     "private",
1026     "sinclude",
1027     "unexport",
1028     "undefine",
1029     "vpath",
1030     "-include",
1031 };
1032 
1033 
1034 /** This is parallel to KMKCCEVALINSTR.   */
1035 static const char * const g_apszEvalInstrNms[] =
1036 {
1037     "jump",
1038     "assign_recursive",
1039     "assign_simple",
1040     "assign_append",
1041     "assign_prepend",
1042     "assign_if_new",
1043     "assign_define",
1044     "export",
1045     "unexport",
1046     "export_all",
1047     "unexport_all",
1048     "undefine",
1049     "ifdef_plain",
1050     "ifndef_plain",
1051     "ifdef_dynamic",
1052     "ifndef_dynamic",
1053     "ifeq",
1054     "ifneq",
1055     "if1of",
1056     "ifn1of",
1057     "if",
1058     "include",
1059     "include_silent",
1060     "includedep",
1061     "includedep_queue",
1062     "includedep_flush",
1063     "recipe_no_commands",
1064     "recipe_start_normal",
1065     "recipe_start_double_colon",
1066     "recipe_start_pattern",
1067     "recipe_commands",
1068     "recipe_end",
1069     "recipe_cancel_pattern",
1070     "vpath",
1071     "vpath_clear_pattern",
1072     "vpath_clear_all",
1073 };
1074 
1075 /*********************************************************************************************************************************
1076 *   Internal Functions                                                                                                           *
1077 *********************************************************************************************************************************/
1078 static int kmk_cc_exp_compile_subprog(PKMKCCBLOCK *ppBlockTail, const char *pchStr, uint32_t cchStr, PKMKCCEXPSUBPROG pSubprog);
1079 static char *kmk_exec_expand_subprog_to_tmp(PKMKCCEXPSUBPROG pSubprog, uint32_t *pcch);
1080 
1081 
1082 /**
1083  * Initializes global variables for the 'compiler'.
1084  */
kmk_cc_init(void)1085 void kmk_cc_init(void)
1086 {
1087     unsigned i;
1088 
1089     /*
1090      * Initialize the bitmap.
1091      */
1092     memset(g_abEvalCcChars, 0, sizeof(g_abEvalCcChars));
1093 
1094     /* blank chars */
1095     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ' ',  KMK_CC_EVAL_CH_BLANK);
1096     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\t', KMK_CC_EVAL_CH_BLANK);
1097 
1098     /* space chars and zero terminator. */
1099 #define MY_SPACE_BITS KMK_CC_EVAL_CH_SPACE | KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE
1100     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ' ',  MY_SPACE_BITS);
1101     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\t', MY_SPACE_BITS);
1102     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\n', MY_SPACE_BITS | KMK_CC_EVAL_CH_EOL_CANDIDATE);
1103     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\v', MY_SPACE_BITS);
1104     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\f', MY_SPACE_BITS);
1105     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\r', MY_SPACE_BITS | KMK_CC_EVAL_CH_EOL_CANDIDATE);
1106     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\\', KMK_CC_EVAL_CH_SPACE_OR_BACKSLASH | KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE);
1107 #undef MY_SPACE_BITS
1108 
1109     /* keywords  */
1110     for (i = 0; i < K_ELEMENTS(g_apszEvalKeywords); i++)
1111     {
1112         size_t cch = strlen(g_apszEvalKeywords[i]);
1113         KMK_CC_ASSERT(cch >= KMK_CC_EVAL_KEYWORD_MIN);
1114         KMK_CC_ASSERT(cch <= KMK_CC_EVAL_KEYWORD_MAX);
1115 
1116         KMK_CC_EVAL_BM_OR(g_abEvalCcChars, g_apszEvalKeywords[i][0], KMK_CC_EVAL_CH_1ST_IN_KEYWORD);
1117         KMK_CC_EVAL_BM_OR(g_abEvalCcChars, g_apszEvalKeywords[i][1], KMK_CC_EVAL_CH_2ND_IN_KEYWORD);
1118     }
1119     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'd', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* define */
1120     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'e', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* export (, endef) */
1121     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'l', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* local */
1122     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'o', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* override */
1123     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'p', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* private */
1124     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, 'u', KMK_CC_EVAL_CH_1ST_IN_VARIABLE_KEYWORD); /* undefine, unexport */
1125 
1126     /* Assignment punctuation and recipe stuff. */
1127     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '=', KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE);
1128     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ':', KMK_CC_EVAL_CH_SPACE_VAR_OR_RECIPE);
1129 
1130     /* For locating the end of variable expansion.  */
1131     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '(', KMK_CC_EVAL_CH_PAREN_OR_SLASH);
1132     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ')', KMK_CC_EVAL_CH_PAREN_OR_SLASH);
1133     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '{', KMK_CC_EVAL_CH_PAREN_OR_SLASH);
1134     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '}', KMK_CC_EVAL_CH_PAREN_OR_SLASH);
1135     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\\', KMK_CC_EVAL_CH_PAREN_OR_SLASH);
1136 
1137     /* For parsing ifeq and if1of expressions. (GNU weirdly does not respect {} style function references.)  */
1138     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '(', KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR);
1139     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ')', KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR);
1140     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, ',', KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR);
1141     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '$', KMK_CC_EVAL_CH_PAREN_COMMA_OR_DOLLAR);
1142 
1143     /* Misc. */
1144     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '$',  KMK_CC_EVAL_CH_DOLLAR);
1145     KMK_CC_EVAL_BM_OR(g_abEvalCcChars, '\\', KMK_CC_EVAL_CH_BACKSLASH);
1146 
1147     /*
1148      * Check that the eval instruction names match up.
1149      */
1150     KMK_CC_ASSERT(strcmp(g_apszEvalInstrNms[kKmkCcEvalInstr_ifneq], "ifneq") == 0);
1151     KMK_CC_ASSERT(strcmp(g_apszEvalInstrNms[kKmkCcEvalInstr_vpath_clear_all], "vpath_clear_all") == 0);
1152 }
1153 
1154 
1155 /**
1156  * Prints stats (for kmk -p).
1157  */
kmk_cc_print_stats(void)1158 void kmk_cc_print_stats(void)
1159 {
1160 #ifdef KMK_CC_WITH_STATS
1161     uint32_t const cEvalCompilations = g_cFileForEvalCompilations + g_cVarForEvalCompilations;
1162 #endif
1163 
1164     puts(_("\n# The kmk 'compiler' and kmk 'program executor':\n"));
1165 
1166     printf(_("# Variables compiled for string expansion: %6u\n"), g_cVarForExpandCompilations);
1167     printf(_("# Variables string expansion runs:         %6u\n"), g_cVarForExpandExecs);
1168     printf(_("# String expansion runs per compile:       %6u\n"), g_cVarForExpandExecs / g_cVarForExpandCompilations);
1169 #ifdef KMK_CC_WITH_STATS
1170     printf(_("#          Single alloc block exp progs:   %6u (%u%%)\n"
1171              "#             Two alloc block exp progs:   %6u (%u%%)\n"
1172              "#   Three or more alloc block exp progs:   %6u (%u%%)\n"
1173              ),
1174            g_cSingleBlockExpProgs, (uint32_t)((uint64_t)g_cSingleBlockExpProgs * 100 / g_cVarForExpandCompilations),
1175            g_cTwoBlockExpProgs,    (uint32_t)((uint64_t)g_cTwoBlockExpProgs    * 100 / g_cVarForExpandCompilations),
1176            g_cMultiBlockExpProgs,  (uint32_t)((uint64_t)g_cMultiBlockExpProgs  * 100 / g_cVarForExpandCompilations));
1177     printf(_("#  Total amount of memory for exp progs: %8u bytes\n"
1178              "#                                    in:   %6u blocks\n"
1179              "#                        avg block size:   %6u bytes\n"
1180              "#                         unused memory: %8u bytes (%u%%)\n"
1181              "#           avg unused memory per block:   %6u bytes\n"
1182              "\n"),
1183            g_cbAllocatedExpProgs, g_cBlocksAllocatedExpProgs, g_cbAllocatedExpProgs / g_cBlocksAllocatedExpProgs,
1184            g_cbUnusedMemExpProgs, (uint32_t)((uint64_t)g_cbUnusedMemExpProgs * 100 / g_cbAllocatedExpProgs),
1185            g_cbUnusedMemExpProgs / g_cBlocksAllocatedExpProgs);
1186     puts("");
1187 #endif
1188     printf(_("# Variables compiled for string eval:      %6u\n"), g_cVarForEvalCompilations);
1189     printf(_("# Variables string eval runs:              %6u\n"), g_cVarForEvalExecs);
1190     printf(_("# String evals runs per compile:           %6u\n"), g_cVarForEvalExecs / g_cVarForEvalCompilations);
1191     printf(_("# Files compiled:                          %6u\n"), g_cFileForEvalCompilations);
1192     printf(_("# Files runs:                              %6u\n"), g_cFileForEvalExecs);
1193     printf(_("# Files eval runs per compile:             %6u\n"), g_cFileForEvalExecs / g_cFileForEvalCompilations);
1194 #ifdef KMK_CC_WITH_STATS
1195     printf(_("#         Single alloc block eval progs:   %6u (%u%%)\n"
1196              "#            Two alloc block eval progs:   %6u (%u%%)\n"
1197              "#  Three or more alloc block eval progs:   %6u (%u%%)\n"
1198              ),
1199            g_cSingleBlockEvalProgs, (uint32_t)((uint64_t)g_cSingleBlockEvalProgs * 100 / cEvalCompilations),
1200            g_cTwoBlockEvalProgs,    (uint32_t)((uint64_t)g_cTwoBlockEvalProgs    * 100 / cEvalCompilations),
1201            g_cMultiBlockEvalProgs,  (uint32_t)((uint64_t)g_cMultiBlockEvalProgs  * 100 / cEvalCompilations));
1202     printf(_("# Total amount of memory for eval progs: %8u bytes\n"
1203              "#                                    in:   %6u blocks\n"
1204              "#                        avg block size:   %6u bytes\n"
1205              "#                         unused memory: %8u bytes (%u%%)\n"
1206              "#           avg unused memory per block:   %6u bytes\n"
1207              "\n"),
1208            g_cbAllocatedEvalProgs, g_cBlocksAllocatedEvalProgs, g_cbAllocatedEvalProgs / g_cBlocksAllocatedEvalProgs,
1209            g_cbUnusedMemEvalProgs, (uint32_t)((uint64_t)g_cbUnusedMemEvalProgs * 100 / g_cbAllocatedEvalProgs),
1210            g_cbUnusedMemEvalProgs / g_cBlocksAllocatedEvalProgs);
1211     puts("");
1212     printf(_("#   Total amount of block mem allocated: %8u bytes\n"), g_cbAllocated);
1213     printf(_("#       Total number of block allocated: %8u\n"), g_cBlockAllocated);
1214     printf(_("#                    Average block size: %8u byte\n"), g_cbAllocated / g_cBlockAllocated);
1215 #endif
1216 
1217     puts("");
1218 }
1219 
1220 
1221 /*
1222  *
1223  * Various utility functions.
1224  * Various utility functions.
1225  * Various utility functions.
1226  *
1227  */
1228 
1229 /**
1230  * Counts the number of dollar chars in the string.
1231  *
1232  * @returns Number of dollar chars.
1233  * @param   pchStr      The string to search (does not need to be zero
1234  *                      terminated).
1235  * @param   cchStr      The length of the string.
1236  */
kmk_cc_count_dollars(const char * pchStr,uint32_t cchStr)1237 static uint32_t kmk_cc_count_dollars(const char *pchStr, uint32_t cchStr)
1238 {
1239     uint32_t cDollars = 0;
1240     const char *pch;
1241     while ((pch = memchr(pchStr, '$', cchStr)) != NULL)
1242     {
1243         cDollars++;
1244         cchStr -= pch - pchStr + 1;
1245         pchStr  = pch + 1;
1246     }
1247     return cDollars;
1248 }
1249 
1250 #ifdef KMK_CC_STRICT
1251 /**
1252  * Used to check that function arguments are left alone.
1253  * @returns Updated hash.
1254  * @param   uHash       The current hash value.
1255  * @param   psz         The string to hash.
1256  */
kmk_cc_debug_string_hash(uint32_t uHash,const char * psz)1257 static uint32_t kmk_cc_debug_string_hash(uint32_t uHash, const char *psz)
1258 {
1259     unsigned char ch;
1260     while ((ch = *(unsigned char const *)psz++) != '\0')
1261         uHash = (uHash << 6) + (uHash << 16) - uHash + (unsigned char)ch;
1262     return uHash;
1263 }
1264 
1265 /**
1266  * Used to check that function arguments are left alone.
1267  * @returns Updated hash.
1268  * @param   uHash       The current hash value.
1269  * @param   pch         The string to hash, not terminated.
1270  * @param   cch         The number of chars to hash.
1271  */
kmk_cc_debug_string_hash_n(uint32_t uHash,const char * pch,uint32_t cch)1272 static uint32_t kmk_cc_debug_string_hash_n(uint32_t uHash, const char *pch, uint32_t cch)
1273 {
1274     while (cch-- > 0)
1275     {
1276         unsigned char ch = *(unsigned char const *)pch++;
1277         uHash = (uHash << 6) + (uHash << 16) - uHash + (unsigned char)ch;
1278     }
1279     return uHash;
1280 }
1281 
1282 #endif
1283 
1284 
1285 
1286 /*
1287  *
1288  * The allocator.
1289  * The allocator.
1290  * The allocator.
1291  *
1292  */
1293 
1294 
1295 /**
1296  * For the first allocation using the block allocator.
1297  *
1298  * @returns Pointer to the first allocation (@a cbFirst in size).
1299  * @param   ppBlockTail         Where to return the pointer to the first block.
1300  * @param   cbFirst             The size of the first allocation.
1301  * @param   cbHint              Hint about how much memory we might be needing.
1302  */
kmk_cc_block_alloc_first(PKMKCCBLOCK * ppBlockTail,size_t cbFirst,size_t cbHint)1303 static void *kmk_cc_block_alloc_first(PKMKCCBLOCK *ppBlockTail, size_t cbFirst, size_t cbHint)
1304 {
1305     uint32_t        cbBlock;
1306     PKMKCCBLOCK     pNewBlock;
1307 
1308     KMK_CC_ASSERT_ALIGNED(cbFirst, sizeof(void *));
1309     KMK_CC_ASSERT(cbFirst <= 128);
1310 
1311     /*
1312      * Turn the hint into a block size.
1313      */
1314     cbHint += cbFirst;
1315     if (cbHint <= 512)
1316     {
1317         if (cbHint <= 256)
1318         {
1319             if (cbFirst <= 64)
1320                 cbBlock = 128;
1321             else
1322                 cbBlock = 256;
1323         }
1324         else
1325             cbBlock = 256;
1326     }
1327     else if (cbHint < 2048)
1328         cbBlock = 1024;
1329     else if (cbHint < 3072)
1330         cbBlock = 2048;
1331     else
1332         cbBlock = 4096;
1333 
1334     /*
1335      * Allocate and initialize the first block.
1336      */
1337     pNewBlock = (PKMKCCBLOCK)xmalloc(cbBlock);
1338     pNewBlock->cbBlock = cbBlock;
1339     pNewBlock->offNext = sizeof(*pNewBlock) + cbFirst;
1340     pNewBlock->pNext   = NULL;
1341     *ppBlockTail = pNewBlock;
1342 
1343 #ifdef KMK_CC_WITH_STATS
1344     g_cBlockAllocated++;
1345     g_cbAllocated += cbBlock;
1346 #endif
1347 
1348     return pNewBlock + 1;
1349 }
1350 
1351 
1352 /**
1353  * Used for getting the address of the next instruction.
1354  *
1355  * @returns Pointer to the next allocation.
1356  * @param   pBlockTail          The allocator tail pointer.
1357  */
kmk_cc_block_get_next_ptr(PKMKCCBLOCK pBlockTail)1358 static void *kmk_cc_block_get_next_ptr(PKMKCCBLOCK pBlockTail)
1359 {
1360     return (char *)pBlockTail + pBlockTail->offNext;
1361 }
1362 
1363 
1364 /**
1365  * Realigns the allocator after doing byte or string allocations.
1366  *
1367  * @param   ppBlockTail         Pointer to the allocator tail pointer.
1368  */
kmk_cc_block_realign(PKMKCCBLOCK * ppBlockTail)1369 static void kmk_cc_block_realign(PKMKCCBLOCK *ppBlockTail)
1370 {
1371     PKMKCCBLOCK pBlockTail = *ppBlockTail;
1372     if (pBlockTail->offNext & (sizeof(void *) - 1U))
1373     {
1374         pBlockTail->offNext = KMK_CC_BLOCK_ALIGN_SIZE(pBlockTail->offNext);
1375         KMK_CC_ASSERT(pBlockTail->cbBlock - pBlockTail->offNext >= sizeof(KMKCCEXPJUMP));
1376     }
1377 }
1378 
1379 
1380 /**
1381  * Grows the allocation with another block, byte allocator case.
1382  *
1383  * @returns Pointer to the byte allocation.
1384  * @param   ppBlockTail         Pointer to the allocator tail pointer.
1385  * @param   cb                  The number of bytes to allocate.
1386  */
kmk_cc_block_byte_alloc_grow(PKMKCCBLOCK * ppBlockTail,uint32_t cb)1387 static void *kmk_cc_block_byte_alloc_grow(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
1388 {
1389     PKMKCCBLOCK     pOldBlock  = *ppBlockTail;
1390     PKMKCCBLOCK     pPrevBlock = pOldBlock->pNext;
1391     PKMKCCBLOCK     pNewBlock;
1392     uint32_t        cbBlock;
1393 
1394     /*
1395      * Check if there accidentally is some space left in the previous block first.
1396      */
1397     if (   pPrevBlock
1398         && pPrevBlock->cbBlock - pPrevBlock->offNext >= cb)
1399     {
1400         void *pvRet = (char *)pPrevBlock + pPrevBlock->offNext;
1401         pPrevBlock->offNext += cb;
1402         return pvRet;
1403     }
1404 
1405     /*
1406      * Allocate a new block.
1407      */
1408 
1409     /* Figure the block size. */
1410     cbBlock = pOldBlock->cbBlock;
1411     while (cbBlock - sizeof(KMKCCEXPJUMP) - sizeof(*pNewBlock) < cb)
1412         cbBlock *= 2;
1413 
1414     /* Allocate and initialize the block it with the new instruction already accounted for. */
1415     pNewBlock = (PKMKCCBLOCK)xmalloc(cbBlock);
1416     pNewBlock->cbBlock = cbBlock;
1417     pNewBlock->offNext = sizeof(*pNewBlock) + cb;
1418     pNewBlock->pNext   = pOldBlock;
1419     *ppBlockTail = pNewBlock;
1420 
1421 #ifdef KMK_CC_WITH_STATS
1422     g_cBlockAllocated++;
1423     g_cbAllocated += cbBlock;
1424 #endif
1425 
1426     return pNewBlock + 1;
1427 }
1428 
1429 
1430 /**
1431  * Make a byte allocation.
1432  *
1433  * Must call kmk_cc_block_realign() when done doing byte and string allocations.
1434  *
1435  * @returns Pointer to the byte allocation (byte aligned).
1436  * @param   ppBlockTail         Pointer to the allocator tail pointer.
1437  * @param   cb                  The number of bytes to allocate.
1438  */
kmk_cc_block_byte_alloc(PKMKCCBLOCK * ppBlockTail,uint32_t cb)1439 static void *kmk_cc_block_byte_alloc(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
1440 {
1441     PKMKCCBLOCK pBlockTail = *ppBlockTail;
1442     uint32_t    cbLeft     = pBlockTail->cbBlock - pBlockTail->offNext;
1443 
1444     KMK_CC_ASSERT(cbLeft >= sizeof(KMKCCEXPJUMP));
1445     if (cbLeft >= cb + sizeof(KMKCCEXPJUMP))
1446     {
1447         void *pvRet = (char *)pBlockTail + pBlockTail->offNext;
1448         pBlockTail->offNext += cb;
1449         return pvRet;
1450     }
1451     return kmk_cc_block_byte_alloc_grow(ppBlockTail, cb);
1452 }
1453 
1454 
1455 /**
1456  * Duplicates the given string in a byte allocation.
1457  *
1458  * Must call kmk_cc_block_realign() when done doing byte and string allocations.
1459  *
1460  * @returns Pointer to the byte allocation (byte aligned).
1461  * @param   ppBlockTail         Pointer to the allocator tail pointer.
1462  * @param   cb                  The number of bytes to allocate.
1463  */
kmk_cc_block_strdup(PKMKCCBLOCK * ppBlockTail,const char * pachStr,uint32_t cchStr)1464 static const char *kmk_cc_block_strdup(PKMKCCBLOCK *ppBlockTail, const char *pachStr, uint32_t cchStr)
1465 {
1466     char *pszCopy;
1467     if (cchStr)
1468     {
1469         pszCopy = kmk_cc_block_byte_alloc(ppBlockTail, cchStr + 1);
1470         memcpy(pszCopy, pachStr, cchStr);
1471         pszCopy[cchStr] = '\0';
1472         return pszCopy;
1473     }
1474     return "";
1475 }
1476 
1477 
1478 /**
1479  * Grows the allocation with another block, string expansion program case.
1480  *
1481  * @returns Pointer to a string expansion instruction core.
1482  * @param   ppBlockTail         Pointer to the allocator tail pointer.
1483  * @param   cb                  The number of bytes to allocate.
1484  */
kmk_cc_block_alloc_exp_grow(PKMKCCBLOCK * ppBlockTail,uint32_t cb)1485 static PKMKCCEXPCORE kmk_cc_block_alloc_exp_grow(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
1486 {
1487     PKMKCCBLOCK     pOldBlock = *ppBlockTail;
1488     PKMKCCBLOCK     pNewBlock;
1489     PKMKCCEXPCORE   pRet;
1490     PKMKCCEXPJUMP   pJump;
1491 
1492     /* Figure the block size. */
1493     uint32_t cbBlock = !pOldBlock->pNext ? 128 : pOldBlock->cbBlock;
1494     while (cbBlock - sizeof(KMKCCEXPJUMP) - sizeof(*pNewBlock) < cb)
1495         cbBlock *= 2;
1496 
1497     /* Allocate and initialize the block it with the new instruction already accounted for. */
1498     pNewBlock = (PKMKCCBLOCK)xmalloc(cbBlock);
1499     pNewBlock->cbBlock = cbBlock;
1500     pNewBlock->offNext = sizeof(*pNewBlock) + cb;
1501     pNewBlock->pNext   = pOldBlock;
1502     *ppBlockTail = pNewBlock;
1503 
1504 #ifdef KMK_CC_WITH_STATS
1505     g_cBlockAllocated++;
1506     g_cbAllocated += cbBlock;
1507 #endif
1508 
1509     pRet = (PKMKCCEXPCORE)(pNewBlock + 1);
1510     KMK_CC_ASSERT(((size_t)pRet & (sizeof(void *) - 1)) == 0);
1511 
1512     /* Emit jump. */
1513     pJump = (PKMKCCEXPJUMP)((char *)pOldBlock + pOldBlock->offNext);
1514     pJump->Core.enmOpcode = kKmkCcExpInstr_Jump;
1515     pJump->pNext = pRet;
1516     pOldBlock->offNext += sizeof(*pJump);
1517     KMK_CC_ASSERT(pOldBlock->offNext <= pOldBlock->cbBlock);
1518 
1519     return pRet;
1520 }
1521 
1522 
1523 /**
1524  * Allocates a string expansion instruction of size @a cb.
1525  *
1526  * @returns Pointer to a string expansion instruction core.
1527  * @param   ppBlockTail         Pointer to the allocator tail pointer.
1528  * @param   cb                  The number of bytes to allocate.
1529  */
kmk_cc_block_alloc_exp(PKMKCCBLOCK * ppBlockTail,uint32_t cb)1530 static PKMKCCEXPCORE kmk_cc_block_alloc_exp(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
1531 {
1532     PKMKCCBLOCK pBlockTail = *ppBlockTail;
1533     uint32_t    cbLeft = pBlockTail->cbBlock - pBlockTail->offNext;
1534 
1535     KMK_CC_ASSERT(cbLeft >= sizeof(KMKCCEXPJUMP));
1536     KMK_CC_ASSERT( (cb & (sizeof(void *) - 1)) == 0 || cb == sizeof(KMKCCEXPCORE) /* final */ );
1537 
1538     if (cbLeft >= cb + sizeof(KMKCCEXPJUMP))
1539     {
1540         PKMKCCEXPCORE pRet = (PKMKCCEXPCORE)((char *)pBlockTail + pBlockTail->offNext);
1541         pBlockTail->offNext += cb;
1542         KMK_CC_ASSERT(((size_t)pRet & (sizeof(void *) - 1)) == 0);
1543         return pRet;
1544     }
1545     return kmk_cc_block_alloc_exp_grow(ppBlockTail, cb);
1546 }
1547 
1548 
1549 /**
1550  * Grows the allocation with another block, makefile evaluation program case.
1551  *
1552  * @returns Pointer to a makefile evaluation instruction core.
1553  * @param   ppBlockTail         Pointer to the allocator tail pointer.
1554  * @param   cb                  The number of bytes to allocate.
1555  */
kmk_cc_block_alloc_eval_grow(PKMKCCBLOCK * ppBlockTail,uint32_t cb)1556 static PKMKCCEVALCORE kmk_cc_block_alloc_eval_grow(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
1557 {
1558     PKMKCCBLOCK     pOldBlock = *ppBlockTail;
1559     PKMKCCBLOCK     pNewBlock;
1560     PKMKCCEVALCORE  pRet;
1561     PKMKCCEVALJUMP  pJump;
1562 
1563     /* Figure the block size. */
1564     uint32_t cbBlock = !pOldBlock->pNext ? 128 : pOldBlock->cbBlock;
1565     while (cbBlock - sizeof(KMKCCEVALJUMP) - sizeof(*pNewBlock) < cb)
1566         cbBlock *= 2;
1567 
1568     /* Allocate and initialize the block it with the new instruction already accounted for. */
1569     pNewBlock = (PKMKCCBLOCK)xmalloc(cbBlock);
1570     pNewBlock->cbBlock = cbBlock;
1571     pNewBlock->offNext = sizeof(*pNewBlock) + cb;
1572     pNewBlock->pNext   = pOldBlock;
1573     *ppBlockTail = pNewBlock;
1574 
1575 #ifdef KMK_CC_WITH_STATS
1576     g_cBlockAllocated++;
1577     g_cbAllocated += cbBlock;
1578 #endif
1579 
1580     pRet = (PKMKCCEVALCORE)(pNewBlock + 1);
1581 
1582     /* Emit jump. */
1583     pJump = (PKMKCCEVALJUMP)((char *)pOldBlock + pOldBlock->offNext);
1584     pJump->Core.enmOpcode = kKmkCcEvalInstr_jump;
1585     pJump->pNext = pRet;
1586     pOldBlock->offNext += sizeof(*pJump);
1587     KMK_CC_ASSERT(pOldBlock->offNext <= pOldBlock->cbBlock);
1588 
1589     return pRet;
1590 }
1591 
1592 
1593 /**
1594  * Allocates a makefile evaluation instruction of size @a cb.
1595  *
1596  * @returns Pointer to a makefile evaluation instruction core.
1597  * @param   ppBlockTail         Pointer to the allocator tail pointer.
1598  * @param   cb                  The number of bytes to allocate.
1599  */
kmk_cc_block_alloc_eval(PKMKCCBLOCK * ppBlockTail,uint32_t cb)1600 static PKMKCCEVALCORE kmk_cc_block_alloc_eval(PKMKCCBLOCK *ppBlockTail, uint32_t cb)
1601 {
1602     PKMKCCBLOCK pBlockTail = *ppBlockTail;
1603     uint32_t    cbLeft = pBlockTail->cbBlock - pBlockTail->offNext;
1604 
1605     KMK_CC_ASSERT(cbLeft >= sizeof(KMKCCEVALJUMP));
1606     KMK_CC_ASSERT( (cb & (sizeof(void *) - 1)) == 0 );
1607 
1608     if (cbLeft >= cb + sizeof(KMKCCEVALJUMP))
1609     {
1610         PKMKCCEVALCORE pRet = (PKMKCCEVALCORE)((char *)pBlockTail + pBlockTail->offNext);
1611         pBlockTail->offNext += cb;
1612         return pRet;
1613     }
1614     return kmk_cc_block_alloc_eval_grow(ppBlockTail, cb);
1615 }
1616 
1617 
1618 /**
1619  * Frees all memory used by an allocator.
1620  *
1621  * @param   ppBlockTail         The allocator tail pointer.
1622  */
kmk_cc_block_free_list(PKMKCCBLOCK pBlockTail)1623 static void kmk_cc_block_free_list(PKMKCCBLOCK pBlockTail)
1624 {
1625     while (pBlockTail)
1626     {
1627         PKMKCCBLOCK pThis = pBlockTail;
1628         pBlockTail = pBlockTail->pNext;
1629         free(pThis);
1630     }
1631 }
1632 
1633 
1634 /*
1635  *
1636  * The string expansion compiler.
1637  * The string expansion compiler.
1638  * The string expansion compiler.
1639  *
1640  */
1641 
1642 
1643 /**
1644  * Emits a kKmkCcExpInstr_Return.
1645  *
1646  * @param   ppBlockTail         Pointer to the allocator tail pointer.
1647  */
kmk_cc_exp_emit_return(PKMKCCBLOCK * ppBlockTail)1648 static void kmk_cc_exp_emit_return(PKMKCCBLOCK *ppBlockTail)
1649 {
1650     PKMKCCEXPCORE pCore = kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pCore));
1651     pCore->enmOpcode = kKmkCcExpInstr_Return;
1652     kmk_cc_block_realign(ppBlockTail);
1653 }
1654 
1655 
1656 /**
1657  * Checks if a function is known to mess up the arguments its given.
1658  *
1659  * When executing calls to "dirty" functions, all arguments must be duplicated
1660  * on the heap.
1661  *
1662  * @returns 1 if dirty, 0 if clean.
1663  * @param   pszFunction         The function name.
1664  */
kmk_cc_is_dirty_function(const char * pszFunction)1665 static uint8_t kmk_cc_is_dirty_function(const char *pszFunction)
1666 {
1667     switch (pszFunction[0])
1668     {
1669         default:
1670             return 0;
1671 
1672         case 'e':
1673             if (!strcmp(pszFunction, "eval"))
1674                 return 1;
1675             if (!strcmp(pszFunction, "evalctx"))
1676                 return 1;
1677             return 0;
1678 
1679         case 'f':
1680             if (!strcmp(pszFunction, "filter"))
1681                 return 1;
1682             if (!strcmp(pszFunction, "filter-out"))
1683                 return 1;
1684             if (!strcmp(pszFunction, "for"))
1685                 return 1;
1686             return 0;
1687 
1688         case 's':
1689             if (!strcmp(pszFunction, "sort"))
1690                 return 1;
1691             return 0;
1692     }
1693 }
1694 
1695 
1696 /**
1697  * Emits a function call instruction taking arguments that needs expanding.
1698  *
1699  * @returns 0 on success, non-zero on failure.
1700  * @param   ppBlockTail     Pointer to the allocator tail pointer.
1701  * @param   pszFunction     The function name (const string from function.c).
1702  * @param   pchArgs         Pointer to the arguments expression string, leading
1703  *                          any blanks has been stripped.
1704  * @param   cchArgs         The length of the arguments expression string.
1705  * @param   cArgs           Number of arguments found.
1706  * @param   chOpen          The char used to open the function call.
1707  * @param   chClose         The char used to close the function call.
1708  * @param   pfnFunction     The function implementation.
1709  * @param   cMaxArgs        Maximum number of arguments the function takes.
1710  */
kmk_cc_exp_emit_dyn_function(PKMKCCBLOCK * ppBlockTail,const char * pszFunction,const char * pchArgs,uint32_t cchArgs,uint32_t cArgs,char chOpen,char chClose,make_function_ptr_t pfnFunction,unsigned char cMaxArgs)1711 static int kmk_cc_exp_emit_dyn_function(PKMKCCBLOCK *ppBlockTail, const char *pszFunction,
1712                                         const char *pchArgs, uint32_t cchArgs, uint32_t cArgs, char chOpen, char chClose,
1713                                         make_function_ptr_t pfnFunction, unsigned char cMaxArgs)
1714 {
1715     uint32_t iArg;
1716 
1717     /*
1718      * The function instruction has variable size.  The maximum argument count
1719      * isn't quite like the minium one.  Zero means no limit.  While a non-zero
1720      * value means that any commas beyond the max will be taken to be part of
1721      * the final argument.
1722      */
1723     uint32_t            cActualArgs = cArgs <= cMaxArgs || !cMaxArgs ? cArgs : cMaxArgs;
1724     PKMKCCEXPDYNFUNC    pInstr  = (PKMKCCEXPDYNFUNC)kmk_cc_block_alloc_exp(ppBlockTail, KMKCCEXPDYNFUNC_SIZE(cActualArgs));
1725     pInstr->FnCore.Core.enmOpcode = kKmkCcExpInstr_DynamicFunction;
1726     pInstr->FnCore.cArgs          = cActualArgs;
1727     pInstr->FnCore.pfnFunction    = pfnFunction;
1728     pInstr->FnCore.pszFuncName    = pszFunction;
1729     pInstr->FnCore.fDirty         = kmk_cc_is_dirty_function(pszFunction);
1730 
1731     /*
1732      * Parse the arguments.  Plain arguments gets duplicated in the program
1733      * memory so that they are terminated and no extra processing is necessary
1734      * later on.  ASSUMES that the function implementations do NOT change
1735      * argument memory.  Other arguments the compiled into their own expansion
1736      * sub programs.
1737      */
1738     iArg = 0;
1739     for (;;)
1740     {
1741         /* Find the end of the argument. Check for $. */
1742         char     ch         = '\0';
1743         uint8_t  fDollar    = 0;
1744         int32_t  cDepth     = 0;
1745         uint32_t cchThisArg = 0;
1746         while (cchThisArg < cchArgs)
1747         {
1748             ch = pchArgs[cchThisArg];
1749             if (ch == chClose)
1750             {
1751                 KMK_CC_ASSERT(cDepth > 0);
1752                 if (cDepth > 0)
1753                     cDepth--;
1754             }
1755             else if (ch == chOpen)
1756                 cDepth++;
1757             else if (ch == ',' && cDepth == 0 && iArg + 1 < cActualArgs)
1758                 break;
1759             else if (ch == '$')
1760                 fDollar = 1;
1761             cchThisArg++;
1762         }
1763 
1764         pInstr->aArgs[iArg].fSubprog = fDollar;
1765         if (fDollar)
1766         {
1767             /* Compile it. */
1768             int rc;
1769             kmk_cc_block_realign(ppBlockTail);
1770             rc = kmk_cc_exp_compile_subprog(ppBlockTail, pchArgs, cchThisArg, &pInstr->aArgs[iArg].u.Subprog);
1771             if (rc != 0)
1772                 return rc;
1773         }
1774         else
1775         {
1776             /* Duplicate it. */
1777             pInstr->aArgs[iArg].u.Plain.psz = kmk_cc_block_strdup(ppBlockTail, pchArgs, cchThisArg);
1778             pInstr->aArgs[iArg].u.Plain.cch = cchThisArg;
1779         }
1780         iArg++;
1781         if (ch != ',')
1782             break;
1783         pchArgs += cchThisArg + 1;
1784         cchArgs -= cchThisArg + 1;
1785     }
1786     KMK_CC_ASSERT(iArg == cActualArgs);
1787 
1788     /*
1789      * Realign the allocator and take down the address of the next instruction.
1790      */
1791     kmk_cc_block_realign(ppBlockTail);
1792     pInstr->FnCore.pNext = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(*ppBlockTail);
1793     return 0;
1794 }
1795 
1796 
1797 /**
1798  * Emits a function call instruction taking plain arguments.
1799  *
1800  * @returns 0 on success, non-zero on failure.
1801  * @param   ppBlockTail     Pointer to the allocator tail pointer.
1802  * @param   pszFunction     The function name (const string from function.c).
1803  * @param   pchArgs         Pointer to the arguments string, leading any blanks
1804  *                          has been stripped.
1805  * @param   cchArgs         The length of the arguments string.
1806  * @param   cArgs           Number of arguments found.
1807  * @param   chOpen          The char used to open the function call.
1808  * @param   chClose         The char used to close the function call.
1809  * @param   pfnFunction     The function implementation.
1810  * @param   cMaxArgs        Maximum number of arguments the function takes.
1811  */
kmk_cc_exp_emit_plain_function(PKMKCCBLOCK * ppBlockTail,const char * pszFunction,const char * pchArgs,uint32_t cchArgs,uint32_t cArgs,char chOpen,char chClose,make_function_ptr_t pfnFunction,unsigned char cMaxArgs)1812 static void kmk_cc_exp_emit_plain_function(PKMKCCBLOCK *ppBlockTail, const char *pszFunction,
1813                                            const char *pchArgs, uint32_t cchArgs, uint32_t cArgs, char chOpen, char chClose,
1814                                            make_function_ptr_t pfnFunction, unsigned char cMaxArgs)
1815 {
1816     uint32_t iArg;
1817 
1818     /*
1819      * The function instruction has variable size.  The maximum argument count
1820      * isn't quite like the minium one.  Zero means no limit.  While a non-zero
1821      * value means that any commas beyond the max will be taken to be part of
1822      * the final argument.
1823      */
1824     uint32_t            cActualArgs = cArgs <= cMaxArgs || !cMaxArgs ? cArgs : cMaxArgs;
1825     PKMKCCEXPPLAINFUNC  pInstr  = (PKMKCCEXPPLAINFUNC)kmk_cc_block_alloc_exp(ppBlockTail, KMKCCEXPPLAINFUNC_SIZE(cActualArgs));
1826     pInstr->FnCore.Core.enmOpcode = kKmkCcExpInstr_PlainFunction;
1827     pInstr->FnCore.cArgs          = cActualArgs;
1828     pInstr->FnCore.pfnFunction    = pfnFunction;
1829     pInstr->FnCore.pszFuncName    = pszFunction;
1830     pInstr->FnCore.fDirty         = kmk_cc_is_dirty_function(pszFunction);
1831 
1832     /*
1833      * Parse the arguments.  Plain arguments gets duplicated in the program
1834      * memory so that they are terminated and no extra processing is necessary
1835      * later on.  ASSUMES that the function implementations do NOT change
1836      * argument memory.
1837      */
1838     iArg = 0;
1839     for (;;)
1840     {
1841         /* Find the end of the argument. */
1842         char     ch         = '\0';
1843         int32_t  cDepth     = 0;
1844         uint32_t cchThisArg = 0;
1845         while (cchThisArg < cchArgs)
1846         {
1847             ch = pchArgs[cchThisArg];
1848             if (ch == chClose)
1849             {
1850                 KMK_CC_ASSERT(cDepth > 0);
1851                 if (cDepth > 0)
1852                     cDepth--;
1853             }
1854             else if (ch == chOpen)
1855                 cDepth++;
1856             else if (ch == ',' && cDepth == 0 && iArg + 1 < cActualArgs)
1857                 break;
1858             cchThisArg++;
1859         }
1860 
1861         /* Duplicate it. */
1862         pInstr->apszArgs[iArg++] = kmk_cc_block_strdup(ppBlockTail, pchArgs, cchThisArg);
1863         if (ch != ',')
1864             break;
1865         pchArgs += cchThisArg + 1;
1866         cchArgs -= cchThisArg + 1;
1867     }
1868 
1869     KMK_CC_ASSERT(iArg == cActualArgs);
1870     pInstr->apszArgs[iArg] = NULL;
1871 
1872     /*
1873      * Realign the allocator and take down the address of the next instruction.
1874      */
1875     kmk_cc_block_realign(ppBlockTail);
1876     pInstr->FnCore.pNext = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(*ppBlockTail);
1877 }
1878 
1879 
1880 /**
1881  * Emits a kKmkCcExpInstr_DynamicVariable.
1882  *
1883  * @returns 0 on success, non-zero on failure.
1884  * @param   ppBlockTail         Pointer to the allocator tail pointer.
1885  * @param   pchNameExpr         The name of the variable (ASSUMED presistent
1886  *                              thru-out the program life time).
1887  * @param   cchNameExpr         The length of the variable name. If zero,
1888  *                              nothing will be emitted.
1889  */
kmk_cc_exp_emit_dyn_variable(PKMKCCBLOCK * ppBlockTail,const char * pchNameExpr,uint32_t cchNameExpr)1890 static int kmk_cc_exp_emit_dyn_variable(PKMKCCBLOCK *ppBlockTail, const char *pchNameExpr, uint32_t cchNameExpr)
1891 {
1892     PKMKCCEXPDYNVAR pInstr;
1893     int rc;
1894     KMK_CC_ASSERT(cchNameExpr > 0);
1895 
1896     pInstr = (PKMKCCEXPDYNVAR)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr));
1897     pInstr->Core.enmOpcode = kKmkCcExpInstr_DynamicVariable;
1898 
1899     rc = kmk_cc_exp_compile_subprog(ppBlockTail, pchNameExpr, cchNameExpr, &pInstr->Subprog);
1900 
1901     pInstr->pNext = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(*ppBlockTail);
1902     return rc;
1903 }
1904 
1905 
1906 /**
1907  * Emits either a kKmkCcExpInstr_PlainVariable or
1908  * kKmkCcExpInstr_SearchAndReplacePlainVariable instruction.
1909  *
1910  * @param   ppBlockTail         Pointer to the allocator tail pointer.
1911  * @param   pchName             The name of the variable.  (Does not need to be
1912  *                              valid beyond the call.)
1913  * @param   cchName             The length of the variable name. If zero,
1914  *                              nothing will be emitted.
1915  */
kmk_cc_exp_emit_plain_variable_maybe_sr(PKMKCCBLOCK * ppBlockTail,const char * pchName,uint32_t cchName)1916 static void kmk_cc_exp_emit_plain_variable_maybe_sr(PKMKCCBLOCK *ppBlockTail, const char *pchName, uint32_t cchName)
1917 {
1918     if (cchName > 0)
1919     {
1920         /*
1921          * Hopefully, we're not expected to do any search and replace on the
1922          * expanded variable string later...  Requires both ':' and '='.
1923          */
1924         const char *pchEqual;
1925         const char *pchColon = (const char *)memchr(pchName, ':', cchName);
1926         if (   pchColon == NULL
1927             || (pchEqual = (const char *)memchr(pchColon + 1, ':', cchName - (pchColon - pchName - 1))) == NULL
1928             || pchEqual == pchEqual + 1)
1929         {
1930             PKMKCCEXPPLAINVAR pInstr = (PKMKCCEXPPLAINVAR)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr));
1931             pInstr->Core.enmOpcode = kKmkCcExpInstr_PlainVariable;
1932             pInstr->pszName = strcache2_add(&variable_strcache, pchName, cchName);
1933         }
1934         else if (pchColon != pchName)
1935         {
1936             /*
1937              * Okay, we need to do search and replace the variable value.
1938              * This is performed by patsubst_expand_pat using '%' patterns.
1939              */
1940             uint32_t            cchName2   = (uint32_t)(pchColon - pchName);
1941             uint32_t            cchSearch  = (uint32_t)(pchEqual - pchColon - 1);
1942             uint32_t            cchReplace = cchName - cchName2 - cchSearch - 2;
1943             const char         *pchPct;
1944             char               *psz;
1945             PKMKCCEXPSRPLAINVAR pInstr;
1946 
1947             pInstr = (PKMKCCEXPSRPLAINVAR)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr));
1948             pInstr->Core.enmOpcode = kKmkCcExpInstr_SearchAndReplacePlainVariable;
1949             pInstr->pszName = strcache2_add(&variable_strcache, pchName, cchName2);
1950 
1951             /* Figure out the search pattern, unquoting percent chars.. */
1952             psz = (char *)kmk_cc_block_byte_alloc(ppBlockTail, cchSearch + 2);
1953             psz[0] = '%';
1954             memcpy(psz + 1, pchColon + 1, cchSearch);
1955             psz[1 + cchSearch] = '\0';
1956             pchPct = find_percent(psz + 1); /* also performs unquoting */
1957             if (pchPct)
1958             {
1959                 pInstr->pszSearchPattern    = psz + 1;
1960                 pInstr->offPctSearchPattern = (uint32_t)(pchPct - psz - 1);
1961             }
1962             else
1963             {
1964                 pInstr->pszSearchPattern    = psz;
1965                 pInstr->offPctSearchPattern = 0;
1966             }
1967 
1968             /* Figure out the replacement pattern, unquoting percent chars.. */
1969             if (cchReplace == 0)
1970             {
1971                 pInstr->pszReplacePattern    = "%";
1972                 pInstr->offPctReplacePattern = 0;
1973             }
1974             else
1975             {
1976                 psz = (char *)kmk_cc_block_byte_alloc(ppBlockTail, cchReplace + 2);
1977                 psz[0] = '%';
1978                 memcpy(psz + 1, pchEqual + 1, cchReplace);
1979                 psz[1 + cchReplace] = '\0';
1980                 pchPct = find_percent(psz + 1); /* also performs unquoting */
1981                 if (pchPct)
1982                 {
1983                     pInstr->pszReplacePattern    = psz + 1;
1984                     pInstr->offPctReplacePattern = (uint32_t)(pchPct - psz - 1);
1985                 }
1986                 else
1987                 {
1988                     pInstr->pszReplacePattern    = psz;
1989                     pInstr->offPctReplacePattern = 0;
1990                 }
1991             }
1992 
1993             /* Note down where the next instruction is after realigning the allocator. */
1994             kmk_cc_block_realign(ppBlockTail);
1995             pInstr->pNext = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(*ppBlockTail);
1996         }
1997     }
1998 }
1999 
2000 
2001 /**
2002  * Emits a kKmkCcExpInstr_CopyString.
2003  *
2004  * @param   ppBlockTail         Pointer to the allocator tail pointer.
2005  * @param   pchStr              The string to emit (ASSUMED presistent thru-out
2006  *                              the program life time).
2007  * @param   cchStr              The number of chars to copy. If zero, nothing
2008  *                              will be emitted.
2009  */
kmk_cc_exp_emit_copy_string(PKMKCCBLOCK * ppBlockTail,const char * pchStr,uint32_t cchStr)2010 static void kmk_cc_exp_emit_copy_string(PKMKCCBLOCK *ppBlockTail, const char *pchStr, uint32_t cchStr)
2011 {
2012     if (cchStr > 0)
2013     {
2014         PKMKCCEXPCOPYSTRING pInstr = (PKMKCCEXPCOPYSTRING)kmk_cc_block_alloc_exp(ppBlockTail, sizeof(*pInstr));
2015         pInstr->Core.enmOpcode = kKmkCcExpInstr_CopyString;
2016         pInstr->cchCopy = cchStr;
2017         pInstr->pachSrc = pchStr;
2018     }
2019 }
2020 
2021 
2022 /**
2023  * String expansion compilation function common to both normal and sub programs.
2024  *
2025  * @returns 0 on success, non-zero on failure.
2026  * @param   ppBlockTail         Pointer to the allocator tail pointer.
2027  * @param   pchStr              The expression to compile.
2028  * @param   cchStr              The length of the expression to compile.
2029  */
kmk_cc_exp_compile_common(PKMKCCBLOCK * ppBlockTail,const char * pchStr,uint32_t cchStr)2030 static int kmk_cc_exp_compile_common(PKMKCCBLOCK *ppBlockTail, const char *pchStr, uint32_t cchStr)
2031 {
2032     /*
2033      * Process the string.
2034      */
2035     while (cchStr > 0)
2036     {
2037         /* Look for dollar sign, marks variable expansion or dollar-escape. */
2038         int         rc;
2039         const char *pchDollar = memchr(pchStr, '$', cchStr);
2040         if (pchDollar)
2041         {
2042             /*
2043              * Check for multiple dollar chars.
2044              */
2045             uint32_t offDollar = (uint32_t)(pchDollar - pchStr);
2046             uint32_t cDollars  = 1;
2047             while (   offDollar + cDollars < cchStr
2048                    && pchStr[offDollar + cDollars] == '$')
2049                 cDollars++;
2050 
2051             /*
2052              * Emit a string copy for any preceeding stuff, including half of
2053              * the dollars we found (dollar escape: $$ -> $).
2054              * (kmk_cc_exp_emit_copy_string ignore zero length strings).
2055              */
2056             kmk_cc_exp_emit_copy_string(ppBlockTail, pchStr, offDollar + cDollars / 2);
2057             pchStr += offDollar + cDollars;
2058             cchStr -= offDollar + cDollars;
2059 
2060             /*
2061              * Odd number of dollar chars means there is a variable to expand
2062              * or function to call.
2063              */
2064             if (cDollars & 1)
2065             {
2066                 if (cchStr > 0)
2067                 {
2068                     char const chOpen = *pchStr;
2069                     if (chOpen == '(' || chOpen == '{')
2070                     {
2071                         /* There are several alternative ways of finding the ending
2072                            parenthesis / braces.
2073 
2074                            GNU make does one thing for functions and variable containing
2075                            any '$' chars before the first closing char.  While for
2076                            variables where a closing char comes before any '$' char, a
2077                            simplified approach is taken.  This means that for example:
2078 
2079                                 Given VAR=var, the expressions "$(var())" and
2080                                 "$($(VAR)())" would be expanded differently.
2081                                 In the first case the variable "var(" would be
2082                                 used and in the second "var()".
2083 
2084                            This code will not duplicate this weird behavior, but work
2085                            the same regardless of whether there is a '$' char before
2086                            the first closing char. */
2087                         make_function_ptr_t pfnFunction;
2088                         const char         *pszFunction;
2089                         unsigned char       cMaxArgs;
2090                         unsigned char       cMinArgs;
2091                         char                fExpandArgs;
2092                         char const          chClose   = chOpen == '(' ? ')' : '}';
2093                         char                ch        = 0;
2094                         uint32_t            cchName   = 0;
2095                         uint32_t            cDepth    = 1;
2096                         uint32_t            cMaxDepth = 1;
2097                         cDollars = 0;
2098 
2099                         pchStr++;
2100                         cchStr--;
2101 
2102                         /* First loop: Identify potential function calls and dynamic expansion. */
2103                         KMK_CC_ASSERT(!func_char_map[(unsigned char)chOpen]);
2104                         KMK_CC_ASSERT(!func_char_map[(unsigned char)chClose]);
2105                         KMK_CC_ASSERT(!func_char_map[(unsigned char)'$']);
2106                         while (cchName < cchStr)
2107                         {
2108                             ch = pchStr[cchName];
2109                             if (!func_char_map[(unsigned char)ch])
2110                                 break;
2111                             cchName++;
2112                         }
2113 
2114                         if (   cchName >= MIN_FUNCTION_LENGTH
2115                             && cchName <= MAX_FUNCTION_LENGTH
2116                             && (isblank(ch) || ch == chClose || cchName == cchStr)
2117                             && (pfnFunction = lookup_function_for_compiler(pchStr, cchName, &cMinArgs, &cMaxArgs,
2118                                                                            &fExpandArgs, &pszFunction)) != NULL)
2119                         {
2120                             /*
2121                              * It's a function invocation, we should count parameters while
2122                              * looking for the end.
2123                              * Note! We use cchName for the length of the argument list.
2124                              */
2125                             uint32_t cArgs = 1;
2126                             if (ch != chClose)
2127                             {
2128                                 /* Skip leading spaces before the first arg. */
2129                                 cchName++;
2130                                 while (cchName < cchStr && isblank((unsigned char)pchStr[cchName]))
2131                                     cchName++;
2132 
2133                                 pchStr += cchName;
2134                                 cchStr -= cchName;
2135                                 cchName = 0;
2136 
2137                                 while (cchName < cchStr)
2138                                 {
2139                                     ch = pchStr[cchName];
2140                                     if (ch == ',')
2141                                     {
2142                                         if (cDepth == 1)
2143                                             cArgs++;
2144                                     }
2145                                     else if (ch == chClose)
2146                                     {
2147                                         if (!--cDepth)
2148                                             break;
2149                                     }
2150                                     else if (ch == chOpen)
2151                                     {
2152                                         if (++cDepth > cMaxDepth)
2153                                             cMaxDepth = cDepth;
2154                                     }
2155                                     else if (ch == '$')
2156                                         cDollars++;
2157                                     cchName++;
2158                                 }
2159                             }
2160                             else
2161                             {
2162                                 pchStr += cchName;
2163                                 cchStr -= cchName;
2164                                 cchName = 0;
2165                             }
2166                             if (cArgs < cMinArgs)
2167                             {
2168                                 fatal(NULL, _("Function '%s' takes a minimum of %d arguments: %d given"),
2169                                       pszFunction, (int)cMinArgs, (int)cArgs);
2170                                 return -1; /* not reached */
2171                             }
2172                             if (cDepth != 0)
2173                             {
2174                                 fatal(NULL, chOpen == '('
2175                                       ? _("Missing closing parenthesis calling '%s'") : _("Missing closing braces calling '%s'"),
2176                                       pszFunction);
2177                                 return -1; /* not reached */
2178                             }
2179                             if (cMaxDepth > 16 && fExpandArgs)
2180                             {
2181                                 fatal(NULL, _("Too many levels of nested function arguments expansions: %s"), pszFunction);
2182                                 return -1; /* not reached */
2183                             }
2184                             if (!fExpandArgs || cDollars == 0)
2185                                 kmk_cc_exp_emit_plain_function(ppBlockTail, pszFunction, pchStr, cchName,
2186                                                                cArgs, chOpen, chClose, pfnFunction, cMaxArgs);
2187                             else
2188                             {
2189                                 rc = kmk_cc_exp_emit_dyn_function(ppBlockTail, pszFunction, pchStr, cchName,
2190                                                                   cArgs, chOpen, chClose, pfnFunction, cMaxArgs);
2191                                 if (rc != 0)
2192                                     return rc;
2193                             }
2194                         }
2195                         else
2196                         {
2197                             /*
2198                              * Variable, find the end while checking whether anything needs expanding.
2199                              */
2200                             if (ch == chClose)
2201                                 cDepth = 0;
2202                             else if (cchName < cchStr)
2203                             {
2204                                 if (ch != '$')
2205                                 {
2206                                     /* Second loop: Look for things that needs expanding. */
2207                                     while (cchName < cchStr)
2208                                     {
2209                                         ch = pchStr[cchName];
2210                                         if (ch == chClose)
2211                                         {
2212                                             if (!--cDepth)
2213                                                 break;
2214                                         }
2215                                         else if (ch == chOpen)
2216                                         {
2217                                             if (++cDepth > cMaxDepth)
2218                                                 cMaxDepth = cDepth;
2219                                         }
2220                                         else if (ch == '$')
2221                                             break;
2222                                         cchName++;
2223                                     }
2224                                 }
2225                                 if (ch == '$')
2226                                 {
2227                                     /* Third loop: Something needs expanding, just find the end. */
2228                                     cDollars = 1;
2229                                     cchName++;
2230                                     while (cchName < cchStr)
2231                                     {
2232                                         ch = pchStr[cchName];
2233                                         if (ch == chClose)
2234                                         {
2235                                             if (!--cDepth)
2236                                                 break;
2237                                         }
2238                                         else if (ch == chOpen)
2239                                         {
2240                                             if (++cDepth > cMaxDepth)
2241                                                 cMaxDepth = cDepth;
2242                                         }
2243                                         cchName++;
2244                                     }
2245                                 }
2246                             }
2247                             if (cDepth > 0) /* After warning, we just assume they're all there. */
2248                                 error(NULL, chOpen == '(' ? _("Missing closing parenthesis ") : _("Missing closing braces"));
2249                             if (cMaxDepth >= 16)
2250                             {
2251                                 fatal(NULL, _("Too many levels of nested variable expansions: '%.*s'"), (int)cchName + 2, pchStr - 1);
2252                                 return -1; /* not reached */
2253                             }
2254                             if (cDollars == 0)
2255                                 kmk_cc_exp_emit_plain_variable_maybe_sr(ppBlockTail, pchStr, cchName);
2256                             else
2257                             {
2258                                 rc = kmk_cc_exp_emit_dyn_variable(ppBlockTail, pchStr, cchName);
2259                                 if (rc != 0)
2260                                     return rc;
2261                             }
2262                         }
2263                         pchStr += cchName + 1;
2264                         cchStr -= cchName + (cDepth == 0);
2265                     }
2266                     else
2267                     {
2268                         /* Single character variable name. */
2269                         kmk_cc_exp_emit_plain_variable_maybe_sr(ppBlockTail, pchStr, 1);
2270                         pchStr++;
2271                         cchStr--;
2272                     }
2273                 }
2274                 else
2275                 {
2276                     error(NULL, _("Unexpected end of string after $"));
2277                     break;
2278                 }
2279             }
2280         }
2281         else
2282         {
2283             /*
2284              * Nothing more to expand, the remainder is a simple string copy.
2285              */
2286             kmk_cc_exp_emit_copy_string(ppBlockTail, pchStr, cchStr);
2287             break;
2288         }
2289     }
2290 
2291     /*
2292      * Emit final instruction.
2293      */
2294     kmk_cc_exp_emit_return(ppBlockTail);
2295     return 0;
2296 }
2297 
2298 
2299 /**
2300  * Initializes string expansion program statistics.
2301  * @param   pStats              Pointer to the statistics structure to init.
2302  */
kmk_cc_exp_stats_init(PKMKCCEXPSTATS pStats)2303 static void kmk_cc_exp_stats_init(PKMKCCEXPSTATS pStats)
2304 {
2305     pStats->cchAvg = 0;
2306 }
2307 
2308 
2309 /**
2310  * Compiles a string expansion subprogram.
2311  *
2312  * The caller typically make a call to kmk_cc_block_get_next_ptr after this
2313  * function returns to figure out where to continue executing.
2314  *
2315  * @returns 0 on success, non-zero on failure.
2316  * @param   ppBlockTail         Pointer to the allocator tail pointer.
2317  * @param   pchStr              Pointer to the string to compile an expansion
2318  *                              program for (ASSUMED to be valid for the
2319  *                              lifetime of the program).
2320  * @param   cchStr              The length of the string to compile. Expected to
2321  *                              be at least on char long.
2322  * @param   pSubprog            The subprogram structure to initialize.
2323  */
kmk_cc_exp_compile_subprog(PKMKCCBLOCK * ppBlockTail,const char * pchStr,uint32_t cchStr,PKMKCCEXPSUBPROG pSubprog)2324 static int kmk_cc_exp_compile_subprog(PKMKCCBLOCK *ppBlockTail, const char *pchStr, uint32_t cchStr, PKMKCCEXPSUBPROG pSubprog)
2325 {
2326     KMK_CC_ASSERT(cchStr > 0);
2327     pSubprog->pFirstInstr = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(*ppBlockTail);
2328     kmk_cc_exp_stats_init(&pSubprog->Stats);
2329     return kmk_cc_exp_compile_common(ppBlockTail, pchStr, cchStr);
2330 }
2331 
2332 
2333 /**
2334  * Compiles a string expansion program.
2335  *
2336  * @returns Pointer to the program on success, NULL on failure.
2337  * @param   pchStr              Pointer to the string to compile an expansion
2338  *                              program for (ASSUMED to be valid for the
2339  *                              lifetime of the program).
2340  * @param   cchStr              The length of the string to compile. Expected to
2341  *                              be at least on char long.
2342  */
kmk_cc_exp_compile(const char * pchStr,uint32_t cchStr)2343 static PKMKCCEXPPROG kmk_cc_exp_compile(const char *pchStr, uint32_t cchStr)
2344 {
2345     /*
2346      * Estimate block size, allocate one and initialize it.
2347      */
2348     PKMKCCEXPPROG   pProg;
2349     PKMKCCBLOCK     pBlock;
2350     pProg = kmk_cc_block_alloc_first(&pBlock, sizeof(*pProg),
2351                                      (kmk_cc_count_dollars(pchStr, cchStr) + 4)  * 8);
2352     if (pProg)
2353     {
2354         pProg->pBlockTail   = pBlock;
2355         pProg->pFirstInstr  = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(pBlock);
2356         kmk_cc_exp_stats_init(&pProg->Stats);
2357         pProg->cRefs        = 1;
2358 #ifdef KMK_CC_STRICT
2359         pProg->uInputHash   = kmk_cc_debug_string_hash_n(0, pchStr, cchStr);
2360 #endif
2361 
2362         /*
2363          * Join forces with the subprogram compilation code.
2364          */
2365         if (kmk_cc_exp_compile_common(&pProg->pBlockTail, pchStr, cchStr) == 0)
2366         {
2367 #ifdef KMK_CC_WITH_STATS
2368             pBlock = pProg->pBlockTail;
2369             if (!pBlock->pNext)
2370                 g_cSingleBlockExpProgs++;
2371             else if (!pBlock->pNext->pNext)
2372                 g_cTwoBlockExpProgs++;
2373             else
2374                 g_cMultiBlockExpProgs++;
2375             for (; pBlock; pBlock = pBlock->pNext)
2376             {
2377                 g_cBlocksAllocatedExpProgs++;
2378                 g_cbAllocatedExpProgs += pBlock->cbBlock;
2379                 g_cbUnusedMemExpProgs += pBlock->cbBlock - pBlock->offNext;
2380             }
2381 #endif
2382             return pProg;
2383         }
2384         kmk_cc_block_free_list(pProg->pBlockTail);
2385     }
2386     return NULL;
2387 }
2388 
2389 
2390 /**
2391  * Updates the recursive_without_dollar member of a variable structure.
2392  *
2393  * This avoid compiling string expansion programs with only a CopyString
2394  * instruction.  By setting recursive_without_dollar to 1, code calling
2395  * kmk_cc_compile_variable_for_expand and kmk_exec_expand_to_var_buf will
2396  * instead treat start treating it as a simple variable, which is faster.
2397  *
2398  * @returns The updated recursive_without_dollar value.
2399  * @param   pVar        Pointer to the variable.
2400  */
kmk_cc_update_variable_recursive_without_dollar(struct variable * pVar)2401 static int kmk_cc_update_variable_recursive_without_dollar(struct variable *pVar)
2402 {
2403     int fValue;
2404     KMK_CC_ASSERT(pVar->recursive_without_dollar == 0);
2405 
2406     if (memchr(pVar->value, '$', pVar->value_length))
2407         fValue = -1;
2408     else
2409         fValue = 1;
2410     pVar->recursive_without_dollar = fValue;
2411 
2412     return fValue;
2413 }
2414 
2415 
2416 /**
2417  * Compiles a variable for string expansion.
2418  *
2419  * @returns Pointer to the string expansion program on success, NULL if no
2420  *          program was created.
2421  * @param   pVar        Pointer to the variable.
2422  */
kmk_cc_compile_variable_for_expand(struct variable * pVar)2423 struct kmk_cc_expandprog *kmk_cc_compile_variable_for_expand(struct variable *pVar)
2424 {
2425     KMK_CC_ASSERT(strlen(pVar->value) == pVar->value_length);
2426     KMK_CC_ASSERT(!pVar->expandprog);
2427     KMK_CC_ASSERT(pVar->recursive_without_dollar <= 0);
2428 
2429     if (   !pVar->expandprog
2430         && pVar->recursive)
2431     {
2432         if (   pVar->recursive_without_dollar < 0
2433             || (   pVar->recursive_without_dollar == 0
2434                 && kmk_cc_update_variable_recursive_without_dollar(pVar) < 0) )
2435         {
2436             pVar->expandprog = kmk_cc_exp_compile(pVar->value, pVar->value_length);
2437             g_cVarForExpandCompilations++;
2438         }
2439     }
2440     return pVar->expandprog;
2441 }
2442 
2443 
2444 /**
2445  * String expansion execution worker for outputting a variable.
2446  *
2447  * @returns The new variable buffer position.
2448  * @param   pVar        The variable to reference.
2449  * @param   pchDst      The current variable buffer position.
2450  */
kmk_exec_expand_worker_reference_variable(struct variable * pVar,char * pchDst)2451 static char *kmk_exec_expand_worker_reference_variable(struct variable *pVar, char *pchDst)
2452 {
2453     if (pVar->value_length > 0)
2454     {
2455         if (!pVar->recursive || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar))
2456             pchDst = variable_buffer_output(pchDst, pVar->value, pVar->value_length);
2457         else
2458             pchDst = reference_recursive_variable(pchDst, pVar);
2459     }
2460     else if (pVar->append)
2461         pchDst = reference_recursive_variable(pchDst, pVar);
2462     return pchDst;
2463 }
2464 
2465 
2466 /**
2467  * Executes a stream string expansion instructions, outputting to the current
2468  * varaible buffer.
2469  *
2470  * @returns The new variable buffer position.
2471  * @param   pInstrCore      The instruction to start executing at.
2472  * @param   pchDst          The current variable buffer position.
2473  */
kmk_exec_expand_instruction_stream_to_var_buf(PKMKCCEXPCORE pInstrCore,char * pchDst)2474 static char *kmk_exec_expand_instruction_stream_to_var_buf(PKMKCCEXPCORE pInstrCore, char *pchDst)
2475 {
2476     for (;;)
2477     {
2478         switch (pInstrCore->enmOpcode)
2479         {
2480             case kKmkCcExpInstr_CopyString:
2481             {
2482                 PKMKCCEXPCOPYSTRING pInstr = (PKMKCCEXPCOPYSTRING)pInstrCore;
2483                 pchDst = variable_buffer_output(pchDst, pInstr->pachSrc, pInstr->cchCopy);
2484 
2485                 pInstrCore = &(pInstr + 1)->Core;
2486                 break;
2487             }
2488 
2489             case kKmkCcExpInstr_PlainVariable:
2490             {
2491                 PKMKCCEXPPLAINVAR pInstr = (PKMKCCEXPPLAINVAR)pInstrCore;
2492                 struct variable  *pVar = lookup_variable_strcached(pInstr->pszName);
2493                 if (pVar)
2494                     pchDst = kmk_exec_expand_worker_reference_variable(pVar, pchDst);
2495                 else
2496                     warn_undefined(pInstr->pszName, strcache2_get_len(&variable_strcache, pInstr->pszName));
2497 
2498                 pInstrCore = &(pInstr + 1)->Core;
2499                 break;
2500             }
2501 
2502             case kKmkCcExpInstr_DynamicVariable:
2503             {
2504                 PKMKCCEXPDYNVAR  pInstr = (PKMKCCEXPDYNVAR)pInstrCore;
2505                 struct variable *pVar;
2506                 uint32_t         cchName;
2507                 char            *pszName = kmk_exec_expand_subprog_to_tmp(&pInstr->Subprog, &cchName);
2508                 char            *pszColon = (char *)memchr(pszName, ':', cchName);
2509                 char            *pszEqual;
2510                 if (   pszColon == NULL
2511                     || (pszEqual = (char *)memchr(pszColon + 1, '=', &pszName[cchName] - pszColon - 1)) == NULL
2512                     || pszEqual == pszColon + 1)
2513                 {
2514                     pVar = lookup_variable(pszName, cchName);
2515                     if (pVar)
2516                         pchDst = kmk_exec_expand_worker_reference_variable(pVar, pchDst);
2517                     else
2518                         warn_undefined(pszName, cchName);
2519                 }
2520                 else if (pszColon != pszName)
2521                 {
2522                     /*
2523                      * Oh, we have to do search and replace. How tedious.
2524                      * Since the variable name is a temporary buffer, we can transform
2525                      * the strings into proper search and replacement patterns directly.
2526                      */
2527                     pVar = lookup_variable(pszName, pszColon - pszName);
2528                     if (pVar)
2529                     {
2530                         char const *pszExpandedVarValue = pVar->recursive ? recursively_expand(pVar) : pVar->value;
2531                         char       *pszSearchPat  = pszColon + 1;
2532                         char       *pszReplacePat = pszEqual + 1;
2533                         const char *pchPctSearchPat;
2534                         const char *pchPctReplacePat;
2535 
2536                         *pszEqual = '\0';
2537                         pchPctSearchPat = find_percent(pszSearchPat);
2538                         pchPctReplacePat = find_percent(pszReplacePat);
2539 
2540                         if (!pchPctReplacePat)
2541                         {
2542                             if (pszReplacePat[-2] != '\0') /* On the offchance that a pct was unquoted by find_percent. */
2543                             {
2544                                 memmove(pszName + 1, pszSearchPat, pszReplacePat - pszSearchPat);
2545                                 if (pchPctSearchPat)
2546                                     pchPctSearchPat -= pszSearchPat - &pszName[1];
2547                                 pszSearchPat = &pszName[1];
2548                             }
2549                             pchPctReplacePat = --pszReplacePat;
2550                             *pszReplacePat = '%';
2551                         }
2552 
2553                         if (!pchPctSearchPat)
2554                         {
2555                             pchPctSearchPat = --pszSearchPat;
2556                             *pszSearchPat = '%';
2557                         }
2558 
2559                         pchDst = patsubst_expand_pat(pchDst, pszExpandedVarValue,
2560                                                      pszSearchPat, pszReplacePat,
2561                                                      pchPctSearchPat, pchPctReplacePat);
2562 
2563                         if (pVar->recursive)
2564                             free((void *)pszExpandedVarValue);
2565                     }
2566                     else
2567                         warn_undefined(pszName, pszColon - pszName);
2568                 }
2569                 free(pszName);
2570 
2571                 pInstrCore = pInstr->pNext;
2572                 break;
2573             }
2574 
2575 
2576             case kKmkCcExpInstr_SearchAndReplacePlainVariable:
2577             {
2578                 PKMKCCEXPSRPLAINVAR pInstr = (PKMKCCEXPSRPLAINVAR)pInstrCore;
2579                 struct variable    *pVar = lookup_variable_strcached(pInstr->pszName);
2580                 if (pVar)
2581                 {
2582                     char const *pszExpandedVarValue = pVar->recursive ? recursively_expand(pVar) : pVar->value;
2583                     pchDst = patsubst_expand_pat(pchDst,
2584                                                  pszExpandedVarValue,
2585                                                  pInstr->pszSearchPattern,
2586                                                  pInstr->pszReplacePattern,
2587                                                  &pInstr->pszSearchPattern[pInstr->offPctSearchPattern],
2588                                                  &pInstr->pszReplacePattern[pInstr->offPctReplacePattern]);
2589                     if (pVar->recursive)
2590                         free((void *)pszExpandedVarValue);
2591                 }
2592                 else
2593                     warn_undefined(pInstr->pszName, strcache2_get_len(&variable_strcache, pInstr->pszName));
2594 
2595                 pInstrCore = pInstr->pNext;
2596                 break;
2597             }
2598 
2599             case kKmkCcExpInstr_PlainFunction:
2600             {
2601                 PKMKCCEXPPLAINFUNC pInstr = (PKMKCCEXPPLAINFUNC)pInstrCore;
2602                 uint32_t iArg;
2603                 if (!pInstr->FnCore.fDirty)
2604                 {
2605 #ifdef KMK_CC_STRICT
2606                     uint32_t uCrcBefore = 0;
2607                     uint32_t uCrcAfter = 0;
2608                     iArg = pInstr->FnCore.cArgs;
2609                     while (iArg-- > 0)
2610                         uCrcBefore = kmk_cc_debug_string_hash(uCrcBefore, pInstr->apszArgs[iArg]);
2611 #endif
2612 
2613                     pchDst = pInstr->FnCore.pfnFunction(pchDst, (char **)&pInstr->apszArgs[0], pInstr->FnCore.pszFuncName);
2614 
2615 #ifdef KMK_CC_STRICT
2616                     iArg = pInstr->FnCore.cArgs;
2617                     while (iArg-- > 0)
2618                         uCrcAfter = kmk_cc_debug_string_hash(uCrcAfter, pInstr->apszArgs[iArg]);
2619                     KMK_CC_ASSERT(uCrcBefore == uCrcAfter);
2620 #endif
2621                 }
2622                 else
2623                 {
2624                     char **papszShadowArgs = xmalloc((pInstr->FnCore.cArgs * 2 + 1) * sizeof(papszShadowArgs[0]));
2625                     char **papszArgs = &papszShadowArgs[pInstr->FnCore.cArgs];
2626 
2627                     iArg = pInstr->FnCore.cArgs;
2628                     papszArgs[iArg] = NULL;
2629                     while (iArg-- > 0)
2630                         papszArgs[iArg] = papszShadowArgs[iArg] = xstrdup(pInstr->apszArgs[iArg]);
2631 
2632                     pchDst = pInstr->FnCore.pfnFunction(pchDst, (char **)&pInstr->apszArgs[0], pInstr->FnCore.pszFuncName);
2633 
2634                     iArg = pInstr->FnCore.cArgs;
2635                     while (iArg-- > 0)
2636                         free(papszShadowArgs[iArg]);
2637                     free(papszShadowArgs);
2638                 }
2639 
2640                 pInstrCore = pInstr->FnCore.pNext;
2641                 break;
2642             }
2643 
2644             case kKmkCcExpInstr_DynamicFunction:
2645             {
2646                 PKMKCCEXPDYNFUNC pInstr = (PKMKCCEXPDYNFUNC)pInstrCore;
2647                 char           **papszArgsShadow = xmalloc( (pInstr->FnCore.cArgs * 2 + 1) * sizeof(char *));
2648                 char           **papszArgs = &papszArgsShadow[pInstr->FnCore.cArgs];
2649                 uint32_t         iArg;
2650 
2651                 if (!pInstr->FnCore.fDirty)
2652                 {
2653 #ifdef KMK_CC_STRICT
2654                     uint32_t    uCrcBefore = 0;
2655                     uint32_t    uCrcAfter = 0;
2656 #endif
2657                     iArg = pInstr->FnCore.cArgs;
2658                     papszArgs[iArg] = NULL;
2659                     while (iArg-- > 0)
2660                     {
2661                         char *pszArg;
2662                         if (pInstr->aArgs[iArg].fSubprog)
2663                             pszArg = kmk_exec_expand_subprog_to_tmp(&pInstr->aArgs[iArg].u.Subprog, NULL);
2664                         else
2665                             pszArg = (char *)pInstr->aArgs[iArg].u.Plain.psz;
2666                         papszArgsShadow[iArg] = pszArg;
2667                         papszArgs[iArg]       = pszArg;
2668 #ifdef KMK_CC_STRICT
2669                         uCrcBefore = kmk_cc_debug_string_hash(uCrcBefore, pszArg);
2670 #endif
2671                     }
2672                     pchDst = pInstr->FnCore.pfnFunction(pchDst, papszArgs, pInstr->FnCore.pszFuncName);
2673 
2674                     iArg = pInstr->FnCore.cArgs;
2675                     while (iArg-- > 0)
2676                     {
2677 #ifdef KMK_CC_STRICT
2678                         KMK_CC_ASSERT(papszArgsShadow[iArg] == papszArgs[iArg]);
2679                         uCrcAfter = kmk_cc_debug_string_hash(uCrcAfter, papszArgsShadow[iArg]);
2680 #endif
2681                         if (pInstr->aArgs[iArg].fSubprog)
2682                             free(papszArgsShadow[iArg]);
2683                     }
2684                     KMK_CC_ASSERT(uCrcBefore == uCrcAfter);
2685                 }
2686                 else
2687                 {
2688                     iArg = pInstr->FnCore.cArgs;
2689                     papszArgs[iArg] = NULL;
2690                     while (iArg-- > 0)
2691                     {
2692                         char *pszArg;
2693                         if (pInstr->aArgs[iArg].fSubprog)
2694                             pszArg = kmk_exec_expand_subprog_to_tmp(&pInstr->aArgs[iArg].u.Subprog, NULL);
2695                         else
2696                             pszArg = xstrdup(pInstr->aArgs[iArg].u.Plain.psz);
2697                         papszArgsShadow[iArg] = pszArg;
2698                         papszArgs[iArg]       = pszArg;
2699                     }
2700 
2701                     pchDst = pInstr->FnCore.pfnFunction(pchDst, papszArgs, pInstr->FnCore.pszFuncName);
2702 
2703                     iArg = pInstr->FnCore.cArgs;
2704                     while (iArg-- > 0)
2705                         free(papszArgsShadow[iArg]);
2706                 }
2707                 free(papszArgsShadow);
2708 
2709                 pInstrCore = pInstr->FnCore.pNext;
2710                 break;
2711             }
2712 
2713             case kKmkCcExpInstr_Jump:
2714             {
2715                 PKMKCCEXPJUMP pInstr = (PKMKCCEXPJUMP)pInstrCore;
2716                 pInstrCore = pInstr->pNext;
2717                 break;
2718             }
2719 
2720             case kKmkCcExpInstr_Return:
2721                 return pchDst;
2722 
2723             default:
2724                 fatal(NULL, _("Unknown string expansion opcode: %d (%#x)"),
2725                       (int)pInstrCore->enmOpcode, (int)pInstrCore->enmOpcode);
2726                 return NULL;
2727         }
2728     }
2729 }
2730 
2731 
2732 /**
2733  * Updates the string expansion statistics.
2734  *
2735  * @param   pStats              The statistics structure to update.
2736  * @param   cchResult           The result lenght.
2737  */
kmk_cc_exp_stats_update(PKMKCCEXPSTATS pStats,uint32_t cchResult)2738 void kmk_cc_exp_stats_update(PKMKCCEXPSTATS pStats, uint32_t cchResult)
2739 {
2740     /*
2741      * The average is simplified and not an exact average for every
2742      * expansion that has taken place.
2743      */
2744     pStats->cchAvg = (pStats->cchAvg * 7 + cchResult) / 8;
2745 }
2746 
2747 
2748 /**
2749  * Execute a string expansion subprogram, outputting to a new heap buffer.
2750  *
2751  * @returns Pointer to the output buffer (hand to free when done).
2752  * @param   pSubprog          The subprogram to execute.
2753  * @param   pcchResult        Where to return the size of the result. Optional.
2754  */
kmk_exec_expand_subprog_to_tmp(PKMKCCEXPSUBPROG pSubprog,uint32_t * pcchResult)2755 static char *kmk_exec_expand_subprog_to_tmp(PKMKCCEXPSUBPROG pSubprog, uint32_t *pcchResult)
2756 {
2757     char           *pchOldVarBuf;
2758     unsigned int    cbOldVarBuf;
2759     char           *pchDst;
2760     char           *pszResult;
2761     uint32_t        cchResult;
2762 
2763     /*
2764      * Temporarily replace the variable buffer while executing the instruction
2765      * stream for this subprogram.
2766      */
2767     pchDst = install_variable_buffer_with_hint(&pchOldVarBuf, &cbOldVarBuf,
2768                                                pSubprog->Stats.cchAvg ? pSubprog->Stats.cchAvg + 32 : 256);
2769 
2770     pchDst = kmk_exec_expand_instruction_stream_to_var_buf(pSubprog->pFirstInstr, pchDst);
2771 
2772     /* Ensure that it's terminated. */
2773     pchDst = variable_buffer_output(pchDst, "\0", 1) - 1;
2774 
2775     /* Grab the result buffer before restoring the previous one. */
2776     pszResult = variable_buffer;
2777     cchResult = (uint32_t)(pchDst - pszResult);
2778     if (pcchResult)
2779         *pcchResult = cchResult;
2780     kmk_cc_exp_stats_update(&pSubprog->Stats, cchResult);
2781 
2782     variable_buffer = pchOldVarBuf;
2783     variable_buffer_length = cbOldVarBuf;
2784 
2785     return pszResult;
2786 }
2787 
2788 
2789 /**
2790  * Execute a string expansion program, outputting to the current variable
2791  * buffer.
2792  *
2793  * @returns New variable buffer position.
2794  * @param   pProg               The program to execute.
2795  * @param   pchDst              The current varaible buffer position.
2796  */
kmk_exec_expand_prog_to_var_buf(PKMKCCEXPPROG pProg,char * pchDst)2797 static char *kmk_exec_expand_prog_to_var_buf(PKMKCCEXPPROG pProg, char *pchDst)
2798 {
2799     uint32_t cchResult;
2800     uint32_t offStart = (uint32_t)(pchDst - variable_buffer);
2801 
2802     if (pProg->Stats.cchAvg >= variable_buffer_length - offStart)
2803         pchDst = ensure_variable_buffer_space(pchDst, offStart + pProg->Stats.cchAvg + 32);
2804 
2805     KMK_CC_ASSERT(pProg->cRefs > 0);
2806     pProg->cRefs++;
2807 
2808     pchDst = kmk_exec_expand_instruction_stream_to_var_buf(pProg->pFirstInstr, pchDst);
2809 
2810     pProg->cRefs--;
2811     KMK_CC_ASSERT(pProg->cRefs > 0);
2812 
2813     cchResult = (uint32_t)(pchDst - variable_buffer);
2814     KMK_CC_ASSERT(cchResult >= offStart);
2815     cchResult -= offStart;
2816     kmk_cc_exp_stats_update(&pProg->Stats, cchResult);
2817     g_cVarForExpandExecs++;
2818 
2819     return pchDst;
2820 }
2821 
2822 
2823 /**
2824  * Expands a variable into a variable buffer using its expandprog.
2825  *
2826  * @returns The new variable buffer position.
2827  * @param   pVar        Pointer to the variable.  Must have a program.
2828  * @param   pchDst      Pointer to the current variable buffer position.
2829  */
kmk_exec_expand_to_var_buf(struct variable * pVar,char * pchDst)2830 char *kmk_exec_expand_to_var_buf(struct variable *pVar, char *pchDst)
2831 {
2832     KMK_CC_ASSERT(pVar->expandprog);
2833     KMK_CC_ASSERT(pVar->expandprog->uInputHash == kmk_cc_debug_string_hash(0, pVar->value));
2834     return kmk_exec_expand_prog_to_var_buf(pVar->expandprog, pchDst);
2835 }
2836 
2837 
2838 
2839 
2840 
2841 /*
2842  *
2843  * Makefile evaluation programs.
2844  * Makefile evaluation programs.
2845  * Makefile evaluation programs.
2846  *
2847  */
2848 
kmk_cc_eval_detect_eol_style(char * pchFirst,char * pchSecond,const char * pszContent,size_t cchContent)2849 static size_t kmk_cc_eval_detect_eol_style(char *pchFirst, char *pchSecond, const char *pszContent, size_t cchContent)
2850 {
2851     /* Look for LF first. */
2852     const char *pszTmp = (const char *)memchr(pszContent, '\n', cchContent);
2853     if (pszTmp)
2854     {
2855         /* CRLF? */
2856         if (pszTmp != pszContent && pszTmp[-1] == '\r')
2857         {
2858             *pchFirst = '\r';
2859             *pchSecond = '\n';
2860             return 2;
2861         }
2862 
2863         /* No, LF or LFCR. (pszContent is zero terminated, so no bounds checking necessary.) */
2864         *pchFirst = '\n';
2865         if (pszTmp[1] != '\r')
2866         {
2867             *pchSecond = 0;
2868             return 1;
2869         }
2870         *pchSecond = '\r';
2871         return 2;
2872     }
2873 
2874     /* Probably no EOLs here. */
2875     if (memchr(pszContent, '\r', cchContent) == NULL)
2876     {
2877         *pchSecond = *pchFirst = 0;
2878         return 0;
2879     }
2880 
2881     /* kind of unlikely */
2882     *pchFirst  = '\r';
2883     *pchSecond = 0;
2884     return 1;
2885 }
2886 
2887 
2888 #if 0
2889 /**
2890  * Checks whether we've got an EOL escape sequence or not.
2891  *
2892  * @returns non-zero if escaped EOL, 0 if not (i.e. actual EOL).
2893  * @param   pszContent          The string pointer @a offEol is relative to.
2894  * @param   offEol              The offset of the first EOL char.
2895  */
2896 static unsigned kmk_cc_eval_is_eol_escape_seq(const char *pszContent, size_t offEol)
2897 {
2898     /* The caller has already checked out two backslashes. */
2899     size_t offFirstBackslash = offEol;
2900     KMK_CC_ASSERT(offFirstBackslash >= 2);
2901     offFirstBackslash -= 2;
2902 
2903     /* Find the first backslash. */
2904     while (offFirstBackslash > 0 && pszContent[offFirstBackslash - 1] == '\\')
2905         offFirstBackslash--;
2906 
2907     /* Odd number -> escaped EOL; Even number -> real EOL; */
2908     return (offEol - offFirstBackslash) & 1;
2909 }
2910 #endif
2911 
2912 
2913 
2914 typedef enum kmk_cc_eval_token
2915 {
2916     /** Invalid token . */
2917     kKmkCcEvalToken_Invalid = 0,
2918 
2919     /** Assignment: '=' */
2920     kKmkCcEvalToken_AssignRecursive,
2921     /** Assignment: ':=' */
2922     kKmkCcEvalToken_AssignSimple,
2923     /** Assignment: '+=' */
2924     kKmkCcEvalToken_AssignAppend,
2925     /** Assignment: '<=' */
2926     kKmkCcEvalToken_AssignPrepend,
2927     /** Assignment: '?=' */
2928     kKmkCcEvalToken_AssignIfNew,
2929     /** Assignment: 'define' */
2930     kKmkCcEvalToken_define,
2931     /** Unassignment: 'undefine' */
2932     kKmkCcEvalToken_undefine,
2933 
2934     /* Assignment modifier: 'local'  */
2935     kKmkCcEvalToken_local,
2936     /* Assignment modifier: 'override' */
2937     kKmkCcEvalToken_override,
2938     /* Assignment modifier: 'private' (target variable not inh by deps) */
2939     kKmkCcEvalToken_private,
2940     /* Assignment modifier / other variable thing: 'export' */
2941     kKmkCcEvalToken_export,
2942     /* Other variable thing: 'unexport' */
2943     kKmkCcEvalToken_unexport,
2944 
2945     kKmkCcEvalToken_ifdef,
2946     kKmkCcEvalToken_ifndef,
2947     kKmkCcEvalToken_ifeq,
2948     kKmkCcEvalToken_ifneq,
2949     kKmkCcEvalToken_if1of,
2950     kKmkCcEvalToken_ifn1of,
2951     kKmkCcEvalToken_if,
2952     kKmkCcEvalToken_else,
2953     kKmkCcEvalToken_endif,
2954 
2955     kKmkCcEvalToken_include,
2956     kKmkCcEvalToken_include_silent,
2957     kKmkCcEvalToken_includedep,
2958     kKmkCcEvalToken_includedep_queue,
2959     kKmkCcEvalToken_includedep_flush,
2960 
2961     kKmkCcEvalToken_colon,
2962     kKmkCcEvalToken_double_colon,
2963     kKmkCcEvalToken_plus,
2964     kKmkCcEvalToken_plus_maybe,
2965 
2966     kKmkCcEvalToken_vpath,
2967 
2968     /** Plain word. */
2969     kKmkCcEvalToken_WordPlain,
2970     /** Word that maybe in need of expanding. */
2971     kKmkCcEvalToken_WordWithDollar,
2972 
2973     kKmkCcEvalToken_End
2974 } KMKCCEVALTOKEN;
2975 
2976 /**
2977  * A tokenized word.
2978  */
2979 typedef struct kmk_cc_eval_word
2980 {
2981     /** The token word (lexeme).   */
2982     const char         *pchWord;
2983     /** The length of the word (lexeme). */
2984     uint32_t            cchWord;
2985     /** The token classification. */
2986     KMKCCEVALTOKEN      enmToken;
2987 } KMKCCEVALWORD;
2988 typedef KMKCCEVALWORD *PKMKCCEVALWORD;
2989 typedef KMKCCEVALWORD const *PCKMKCCEVALWORD;
2990 
2991 
2992 /**
2993  * Escaped end-of-line sequence in the current line.
2994  */
2995 typedef struct KMKCCEVALESCEOL
2996 {
2997     /** Offset at which the EOL escape sequence starts for a non-command line. */
2998     size_t              offEsc;
2999     /** Offset of the newline sequence. */
3000     size_t              offEol;
3001 } KMKCCEVALESCEOL;
3002 typedef KMKCCEVALESCEOL *PKMKCCEVALESCEOL;
3003 
3004 
3005 /**
3006  * String copy segment.
3007  */
3008 typedef struct KMKCCEVALSTRCPYSEG
3009 {
3010     /** The start. */
3011     const char         *pchSrc;
3012     /** The number of chars to copy and whether to prepend space.
3013      * Negative values indicates that we should prepend a space. */
3014     ssize_t             cchSrcAndPrependSpace;
3015 } KMKCCEVALSTRCPYSEG;
3016 typedef KMKCCEVALSTRCPYSEG *PKMKCCEVALSTRCPYSEG;
3017 typedef KMKCCEVALSTRCPYSEG const *PCKMKCCEVALSTRCPYSEG;
3018 
3019 
3020 typedef struct KMKCCEVALCOMPILER
3021 {
3022     /** Pointer to the KMKCCEVALPROG::pBlockTail member.  */
3023     PKMKCCBLOCK        *ppBlockTail;
3024 
3025     /** @name Line parsing state.
3026      * @{ */
3027     /** Offset of newline escape sequences in the current line.
3028      * This is only applicable if cEscEols is not zero.  */
3029     PKMKCCEVALESCEOL    paEscEols;
3030     /** The number of number of paEscEols entries we've allocated. */
3031     unsigned            cEscEolsAllocated;
3032     /** Number of escaped EOLs (line count - 1). */
3033     unsigned            cEscEols;
3034     /** The paEscEols entry corresponding to the current parsing location.
3035      * Still to be seen how accurate this can be made to be. */
3036     unsigned            iEscEol;
3037 
3038     /** The current line number (for error handling / debugging). */
3039     unsigned            iLine;
3040     /** The start offset of the current line. */
3041     size_t              offLine;
3042     /** Length of the current line, sans the final EOL and comments. */
3043     size_t              cchLine;
3044     /** Length of the current line, sans the final EOL but with comments. */
3045     size_t              cchLineWithComments;
3046 
3047     /** The first char in an EOL sequence.
3048      * We ASSUMES that this char won't appear in any other sequence in the file,
3049      * thus skipping matching any subsequent chars. */
3050     char                chFirstEol;
3051     /** The second char in an EOL sequence, if applicable. */
3052     char                chSecondEol;
3053 
3054     /** The length of the EOL sequence. */
3055     size_t              cchEolSeq;
3056     /** The minimum length of an esacped EOL sequence (cchEolSeq + 1). */
3057     size_t              cchEscEolSeq;
3058 
3059     /** String copy segments. */
3060     PKMKCCEVALSTRCPYSEG paStrCopySegs;
3061     /** The number of segments that has been prepared. */
3062     unsigned            cStrCopySegs;
3063     /** The number of segments we've allocated. */
3064     unsigned            cStrCopySegsAllocated;
3065     /** @} */
3066 
3067 
3068     /** @name Recipe state.
3069      * @{ */
3070     /** Set if we're working on a recipe. */
3071     PKMKCCEVALRECIPE    pRecipe;
3072     /** Set for ignoring recipes without targets (Sun OS 4 Make). */
3073     uint8_t             fNoTargetRecipe;
3074     /** The command prefix character. */
3075     char                chCmdPrefix;
3076     /** @} */
3077 
3078     /** @name Tokenzied words.
3079      * @{ */
3080     uint32_t            cWords;
3081     uint32_t            cWordsAllocated;
3082     PKMKCCEVALWORD      paWords;
3083     /** @} */
3084 
3085     /** @name Conditionals.
3086      * @{ */
3087     /** Current conditional stack depth. */
3088     unsigned            cIfs;
3089     /** The conditional directive stack. */
3090     PKMKCCEVALIFCORE    apIfs[KMK_CC_EVAL_MAX_IF_DEPTH];
3091     /** @} */
3092 
3093     /** The program being compiled. */
3094     PKMKCCEVALPROG      pEvalProg;
3095     /** Pointer to the content. */
3096     const char         *pszContent;
3097     /** The amount of input to parse. */
3098     size_t              cchContent;
3099 } KMKCCEVALCOMPILER;
3100 typedef KMKCCEVALCOMPILER *PKMKCCEVALCOMPILER;
3101 
3102 
kmk_cc_eval_init_compiler(PKMKCCEVALCOMPILER pCompiler,PKMKCCEVALPROG pEvalProg,unsigned iLine,const char * pszContent,size_t cchContent)3103 static void kmk_cc_eval_init_compiler(PKMKCCEVALCOMPILER pCompiler, PKMKCCEVALPROG pEvalProg, unsigned iLine,
3104                                       const char *pszContent, size_t cchContent)
3105 {
3106     pCompiler->ppBlockTail      = &pEvalProg->pBlockTail;
3107 
3108     pCompiler->pRecipe          = NULL;
3109     pCompiler->fNoTargetRecipe  = 0;
3110     pCompiler->chCmdPrefix      = cmd_prefix;
3111 
3112     pCompiler->cWordsAllocated  = 0;
3113     pCompiler->paWords          = NULL;
3114 
3115     pCompiler->cEscEolsAllocated = 0;
3116     pCompiler->paEscEols        = NULL;
3117     pCompiler->iLine            = iLine;
3118 
3119     pCompiler->cStrCopySegsAllocated = 0;
3120     pCompiler->paStrCopySegs    = NULL;
3121 
3122     pCompiler->cIfs             = 0;
3123 
3124     pCompiler->pEvalProg        = pEvalProg;
3125     pCompiler->pszContent       = pszContent;
3126     pCompiler->cchContent       = cchContent;
3127 
3128     /* Detect EOL style. */
3129     pCompiler->cchEolSeq        = kmk_cc_eval_detect_eol_style(&pCompiler->chFirstEol, &pCompiler->chSecondEol,
3130                                                                pszContent, cchContent);
3131     pCompiler->cchEscEolSeq     = 1 + pCompiler->cchEolSeq;
3132 }
3133 
3134 
kmk_cc_eval_delete_compiler(PKMKCCEVALCOMPILER pCompiler)3135 static void kmk_cc_eval_delete_compiler(PKMKCCEVALCOMPILER pCompiler)
3136 {
3137     if (pCompiler->paWords)
3138         free(pCompiler->paWords);
3139     if (pCompiler->paEscEols)
3140         free(pCompiler->paEscEols);
3141 }
3142 
kmk_cc_eval_fatal(PKMKCCEVALCOMPILER pCompiler,const char * pchWhere,const char * pszMsg,...)3143 static void KMK_CC_FN_NO_RETURN kmk_cc_eval_fatal(PKMKCCEVALCOMPILER pCompiler, const char *pchWhere, const char *pszMsg, ...)
3144 {
3145     va_list  va;
3146     unsigned iLine = pCompiler->iLine;
3147 
3148     log_working_directory(1);
3149 
3150     /*
3151      * If we have a pointer location, use it to figure out the exact line and column.
3152      */
3153     if (pchWhere)
3154     {
3155         size_t   offLine = pCompiler->offLine;
3156         size_t   off     = pchWhere - pCompiler->pszContent;
3157         unsigned i       = 0;
3158         while (   i   < pCompiler->cEscEols
3159                && off > pCompiler->paEscEols[i].offEol)
3160         {
3161             offLine = pCompiler->paEscEols[i].offEol + 1 + pCompiler->cchEolSeq;
3162             iLine++;
3163             i++;
3164         }
3165         KMK_CC_ASSERT(off <= pCompiler->cchContent);
3166 
3167         if (pCompiler->pEvalProg->pszVarName)
3168             fprintf(stderr, "%s:%u:%u: *** fatal parsing error in %s: ",
3169                     pCompiler->pEvalProg->pszFilename, iLine, (unsigned)(off - offLine), pCompiler->pEvalProg->pszVarName);
3170         else
3171             fprintf(stderr, "%s:%u:%u: *** fatal parsing error: ",
3172                     pCompiler->pEvalProg->pszFilename, iLine, (unsigned)(off - offLine));
3173     }
3174     else if (pCompiler->pEvalProg->pszVarName)
3175         fprintf(stderr, "%s:%u: *** fatal parsing error in %s: ",
3176                 pCompiler->pEvalProg->pszFilename, iLine, pCompiler->pEvalProg->pszVarName);
3177     else
3178         fprintf(stderr, "%s:%u: *** fatal parsing error: ",
3179                 pCompiler->pEvalProg->pszFilename, iLine);
3180 
3181     /*
3182      * Print the message and die.
3183      */
3184     va_start(va, pszMsg);
3185     vfprintf(stderr, pszMsg, va);
3186     va_end(va);
3187     fputs(".  Stop.\n", stderr);
3188 
3189     for (;;)
3190         die(2);
3191 }
3192 
3193 
3194 static KMK_CC_FN_NO_RETURN void
kmk_cc_eval_fatal_eol(PKMKCCEVALCOMPILER pCompiler,const char * pchEol,unsigned iLine,size_t offLine)3195 kmk_cc_eval_fatal_eol(PKMKCCEVALCOMPILER pCompiler, const char *pchEol, unsigned iLine, size_t offLine)
3196 {
3197     pCompiler->iLine   = iLine;
3198     pCompiler->offLine = offLine;
3199 
3200     for (;;)
3201         kmk_cc_eval_fatal(pCompiler, pchEol, "Missing 2nd EOL character: found %#x instead of %#x\n",
3202                                    pchEol, pCompiler->chSecondEol);
3203 }
3204 
3205 
kmk_cc_eval_warn(PKMKCCEVALCOMPILER pCompiler,const char * pchWhere,const char * pszMsg,...)3206 static void kmk_cc_eval_warn(PKMKCCEVALCOMPILER pCompiler, const char *pchWhere, const char *pszMsg, ...)
3207 {
3208     /** @todo warnings.   */
3209     (void)pchWhere;
3210     (void)pCompiler;
3211     (void)pszMsg;
3212 }
3213 
3214 
3215 /**
3216  * Compiles a string expansion subprogram.
3217  *
3218  * @param   pCompiler   The compiler state.
3219  * @param   pszExpr     The expression to compile.
3220  * @param   cchExpr     The length of the expression.
3221  * @param   pSubprog    The subprogram to compile.
3222  */
kmk_cc_eval_compile_string_exp_subprog(PKMKCCEVALCOMPILER pCompiler,const char * pszExpr,size_t cchExpr,PKMKCCEXPSUBPROG pSubprog)3223 static void kmk_cc_eval_compile_string_exp_subprog(PKMKCCEVALCOMPILER pCompiler, const char *pszExpr, size_t cchExpr,
3224                                                    PKMKCCEXPSUBPROG pSubprog)
3225 {
3226     int rc = kmk_cc_exp_compile_subprog(pCompiler->ppBlockTail, pszExpr, cchExpr, pSubprog);
3227     if (rc == 0)
3228         return;
3229     kmk_cc_eval_fatal(pCompiler, NULL, "String expansion compile error");
3230 }
3231 
3232 
3233 /**
3234  * Initializes a subprogam or plain operand structure.
3235  *
3236  * @param   pCompiler   The compiler state.
3237  * @param   pOperand    The subprogram or plain structure to init.
3238  * @param   pszString   The string.
3239  * @param   cchString   The length of the string.
3240  * @param   fPlain      Whether it's plain or not.  If not, we'll compile it.
3241  */
kmk_cc_eval_init_subprogram_or_plain(PKMKCCEVALCOMPILER pCompiler,PKMKCCEXPSUBPROGORPLAIN pOperand,const char * pszString,size_t cchString,int fPlain)3242 static void kmk_cc_eval_init_subprogram_or_plain(PKMKCCEVALCOMPILER pCompiler, PKMKCCEXPSUBPROGORPLAIN pOperand,
3243                                                  const char *pszString, size_t cchString, int fPlain)
3244 {
3245     pOperand->fPlainIsInVarStrCache  = 0;
3246     pOperand->bUser                  = 0;
3247     pOperand->bUser2                 = 0;
3248     pOperand->fSubprog               = fPlain;
3249     if (fPlain)
3250     {
3251         pOperand->u.Plain.cch = cchString;
3252         pOperand->u.Plain.psz = pszString;
3253     }
3254     else
3255         kmk_cc_eval_compile_string_exp_subprog(pCompiler, pszString, cchString, &pOperand->u.Subprog);
3256 }
3257 
3258 /**
3259  * Initializes an array of subprogram-or-plain (spp) operands from a word array.
3260  *
3261  * The words will be duplicated and the caller must therefore call
3262  * kmk_cc_block_realign() when done (it's not done here as the caller may
3263  * initialize several string operands and we don't want any unnecessary
3264  * fragmentation).
3265  *
3266  * @param   pCompiler   The compiler state.
3267  * @param   cWords      The number of words to copy.
3268  * @param   paSrc       The source words.
3269  * @param   paDst       The destination subprogram-or-plain array.
3270  */
kmk_cc_eval_init_spp_array_from_duplicated_words(PKMKCCEVALCOMPILER pCompiler,unsigned cWords,PKMKCCEVALWORD paSrc,PKMKCCEXPSUBPROGORPLAIN paDst)3271 static void kmk_cc_eval_init_spp_array_from_duplicated_words(PKMKCCEVALCOMPILER pCompiler, unsigned cWords,
3272                                                              PKMKCCEVALWORD paSrc, PKMKCCEXPSUBPROGORPLAIN paDst)
3273 {
3274     unsigned i;
3275     for (i = 0; i < cWords; i++)
3276     {
3277         const char *pszCopy = kmk_cc_block_strdup(pCompiler->ppBlockTail, paSrc[i].pchWord, paSrc[i].cchWord);
3278         paDst[i].fPlainIsInVarStrCache  = 0;
3279         paDst[i].bUser                  = 0;
3280         paDst[i].bUser2                 = 0;
3281         if (paSrc[i].enmToken == kKmkCcEvalToken_WordWithDollar)
3282         {
3283             paDst[i].fSubprog    = 1;
3284             kmk_cc_eval_compile_string_exp_subprog(pCompiler, pszCopy, paSrc[i].cchWord, &paDst[i].u.Subprog);
3285         }
3286         else
3287         {
3288             paDst[i].fSubprog    = 0;
3289             paDst[i].u.Plain.cch = paSrc[i].cchWord;
3290             paDst[i].u.Plain.psz = pszCopy;
3291         }
3292         KMK_CC_EVAL_DPRINTF(("  %s\n", pszCopy));
3293     }
3294 }
3295 
3296 
3297 
3298 /** @name KMK_CC_WORD_COMP_CONST_XXX - Optimal(/insane) constant work matching.
3299  * @{
3300  */
3301 #if (defined(KBUILD_ARCH_X86) || defined(KBUILD_ARCH_AMD64)) /* Unaligned access is reasonably cheap. */ \
3302  && !defined(GCC_ADDRESS_SANITIZER)
3303 # define KMK_CC_WORD_COMP_CONST_2(a_pchLine, a_pszWord) \
3304         (   *(uint16_t const *)(a_pchLine)     == *(uint16_t const *)(a_pszWord) )
3305 # define KMK_CC_WORD_COMP_CONST_3(a_pchLine, a_pszWord) \
3306         (   *(uint16_t const *)(a_pchLine)     == *(uint16_t const *)(a_pszWord) \
3307          && (a_pchLine)[2]                     == (a_pszWord)[2] )
3308 # define KMK_CC_WORD_COMP_CONST_4(a_pchLine, a_pszWord) \
3309         (   *(uint32_t const *)(a_pchLine)     == *(uint32_t const *)(a_pszWord) )
3310 # define KMK_CC_WORD_COMP_CONST_5(a_pchLine, a_pszWord) \
3311         (   *(uint32_t const *)(a_pchLine)     == *(uint32_t const *)(a_pszWord) \
3312          && (a_pchLine)[4]                     == (a_pszWord)[4] )
3313 # define KMK_CC_WORD_COMP_CONST_6(a_pchLine, a_pszWord) \
3314         (   *(uint32_t const *)(a_pchLine)     == *(uint32_t const *)(a_pszWord) \
3315          && ((uint16_t const *)(a_pchLine))[2] == ((uint32_t const *)(a_pszWord))[2] )
3316 # define KMK_CC_WORD_COMP_CONST_7(a_pchLine, a_pszWord) \
3317         (   *(uint32_t const *)(a_pchLine)     == *(uint32_t const *)(a_pszWord) \
3318          && ((uint16_t const *)(a_pchLine))[2] == ((uint32_t const *)(a_pszWord))[2] \
3319          && (a_pchLine)[6]                     == (a_pszWord)[6] )
3320 # define KMK_CC_WORD_COMP_CONST_8(a_pchLine, a_pszWord) \
3321         (   *(uint64_t const *)(a_pchLine)     == *(uint64_t const *)(a_pszWord) )
3322 # define KMK_CC_WORD_COMP_CONST_10(a_pchLine, a_pszWord) \
3323         (   *(uint64_t const *)(a_pchLine)     == *(uint64_t const *)(a_pszWord) \
3324          && ((uint16_t const *)(a_pchLine))[4] == ((uint16_t const *)(a_pszWord))[4] )
3325 # define KMK_CC_WORD_COMP_CONST_16(a_pchLine, a_pszWord) \
3326         (   *(uint64_t const *)(a_pchLine)     == *(uint64_t const *)(a_pszWord) \
3327          && ((uint64_t const *)(a_pchLine))[1] == ((uint64_t const *)(a_pszWord))[1] )
3328 #else
3329 # define KMK_CC_WORD_COMP_CONST_2(a_pchLine, a_pszWord) \
3330         (   (a_pchLine)[0] == (a_pszWord)[0] \
3331          && (a_pchLine)[1] == (a_pszWord)[1] )
3332 # define KMK_CC_WORD_COMP_CONST_3(a_pchLine, a_pszWord) \
3333         (   (a_pchLine)[0] == (a_pszWord)[0] \
3334          && (a_pchLine)[1] == (a_pszWord)[1] \
3335          && (a_pchLine)[2] == (a_pszWord)[2] )
3336 # define KMK_CC_WORD_COMP_CONST_4(a_pchLine, a_pszWord) \
3337         (   (a_pchLine)[0] == (a_pszWord)[0] \
3338          && (a_pchLine)[1] == (a_pszWord)[1] \
3339          && (a_pchLine)[2] == (a_pszWord)[2] \
3340          && (a_pchLine)[3] == (a_pszWord)[3] )
3341 # define KMK_CC_WORD_COMP_CONST_5(a_pchLine, a_pszWord) \
3342         (   (a_pchLine)[0] == (a_pszWord)[0] \
3343          && (a_pchLine)[1] == (a_pszWord)[1] \
3344          && (a_pchLine)[2] == (a_pszWord)[2] \
3345          && (a_pchLine)[3] == (a_pszWord)[3] \
3346          && (a_pchLine)[4] == (a_pszWord)[4] )
3347 # define KMK_CC_WORD_COMP_CONST_6(a_pchLine, a_pszWord) \
3348         (   (a_pchLine)[0] == (a_pszWord)[0] \
3349          && (a_pchLine)[1] == (a_pszWord)[1] \
3350          && (a_pchLine)[2] == (a_pszWord)[2] \
3351          && (a_pchLine)[3] == (a_pszWord)[3] \
3352          && (a_pchLine)[4] == (a_pszWord)[4] \
3353          && (a_pchLine)[5] == (a_pszWord)[5] )
3354 # define KMK_CC_WORD_COMP_CONST_7(a_pchLine, a_pszWord) \
3355         (   (a_pchLine)[0] == (a_pszWord)[0] \
3356          && (a_pchLine)[1] == (a_pszWord)[1] \
3357          && (a_pchLine)[2] == (a_pszWord)[2] \
3358          && (a_pchLine)[3] == (a_pszWord)[3] \
3359          && (a_pchLine)[4] == (a_pszWord)[4] \
3360          && (a_pchLine)[5] == (a_pszWord)[5] \
3361          && (a_pchLine)[6] == (a_pszWord)[6] )
3362 # define KMK_CC_WORD_COMP_CONST_8(a_pchLine, a_pszWord) \
3363         (   (a_pchLine)[0] == (a_pszWord)[0] \
3364          && (a_pchLine)[1] == (a_pszWord)[1] \
3365          && (a_pchLine)[2] == (a_pszWord)[2] \
3366          && (a_pchLine)[3] == (a_pszWord)[3] \
3367          && (a_pchLine)[4] == (a_pszWord)[4] \
3368          && (a_pchLine)[5] == (a_pszWord)[5] \
3369          && (a_pchLine)[6] == (a_pszWord)[6] \
3370          && (a_pchLine)[7] == (a_pszWord)[7] )
3371 # define KMK_CC_WORD_COMP_CONST_10(a_pchLine, a_pszWord) \
3372         (   (a_pchLine)[0] == (a_pszWord)[0] \
3373          && (a_pchLine)[1] == (a_pszWord)[1] \
3374          && (a_pchLine)[2] == (a_pszWord)[2] \
3375          && (a_pchLine)[3] == (a_pszWord)[3] \
3376          && (a_pchLine)[4] == (a_pszWord)[4] \
3377          && (a_pchLine)[5] == (a_pszWord)[5] \
3378          && (a_pchLine)[6] == (a_pszWord)[6] \
3379          && (a_pchLine)[7] == (a_pszWord)[7] \
3380          && (a_pchLine)[8] == (a_pszWord)[8] \
3381          && (a_pchLine)[9] == (a_pszWord)[9] )
3382 # define KMK_CC_WORD_COMP_CONST_16(a_pchLine, a_pszWord) \
3383         (   (a_pchLine)[0] == (a_pszWord)[0] \
3384          && (a_pchLine)[1] == (a_pszWord)[1] \
3385          && (a_pchLine)[2] == (a_pszWord)[2] \
3386          && (a_pchLine)[3] == (a_pszWord)[3] \
3387          && (a_pchLine)[4] == (a_pszWord)[4] \
3388          && (a_pchLine)[5] == (a_pszWord)[5] \
3389          && (a_pchLine)[6] == (a_pszWord)[6] \
3390          && (a_pchLine)[7] == (a_pszWord)[7] \
3391          && (a_pchLine)[8] == (a_pszWord)[8] \
3392          && (a_pchLine)[9] == (a_pszWord)[9] \
3393          && (a_pchLine)[10] == (a_pszWord)[10] \
3394          && (a_pchLine)[11] == (a_pszWord)[11] \
3395          && (a_pchLine)[12] == (a_pszWord)[12] \
3396          && (a_pchLine)[13] == (a_pszWord)[13] \
3397          && (a_pchLine)[14] == (a_pszWord)[14] \
3398          && (a_pchLine)[15] == (a_pszWord)[15])
3399 #endif
3400 
3401 /** See if the given string match a constant string. */
3402 #define KMK_CC_STRCMP_CONST(a_pchLeft, a_cchLeft, a_pszConst, a_cchConst) \
3403     (   (a_cchLeft) == (a_cchConst) \
3404      && KMK_CC_WORD_COMP_CONST_##a_cchConst(a_pchLeft, a_pszConst) )
3405 
3406 /** See if a starting of a given length starts with a constant word. */
3407 #define KMK_CC_EVAL_WORD_COMP_IS_EOL(a_pCompiler, a_pchLine, a_cchLine) \
3408     (   (a_cchLine) == 0 \
3409      || KMK_CC_EVAL_IS_SPACE((a_pchLine)[0]) \
3410      || ((a_pchLine)[0] == '\\' && (a_pchLine)[1] == (a_pCompiler)->chFirstEol) ) \
3411 
3412 /** See if a starting of a given length starts with a constant word. */
3413 #define KMK_CC_EVAL_WORD_COMP_CONST(a_pCompiler, a_pchLine, a_cchLine, a_pszWord, a_cchWord) \
3414     (    (a_cchLine) >= (a_cchWord) \
3415       && (   (a_cchLine) == (a_cchWord) \
3416           || KMK_CC_EVAL_IS_SPACE((a_pchLine)[a_cchWord]) \
3417           || ((a_pchLine)[a_cchWord] == '\\' && (a_pchLine)[(a_cchWord) + 1] == (a_pCompiler)->chFirstEol) ) \
3418       && KMK_CC_WORD_COMP_CONST_##a_cchWord(a_pchLine, a_pszWord) )
3419 /** @} */
3420 
3421 
3422 /**
3423  * Checks if a_ch is a space after a word.
3424  *
3425  * Since there is always a terminating zero, the user can safely access a char
3426  * beyond @a a_cchLeft.  However, that byte isn't necessarily a zero terminator
3427  * character, so we have to check @a a_cchLeft whether we're at the end of the
3428  * parsing input string.
3429  *
3430  * @returns true / false.
3431  * @param   a_pCompiler     The compiler instance data.
3432  * @param   a_ch            The character to inspect.
3433  * @param   a_ch2           The character following it, in case of escaped EOL.
3434  * @param   a_cchLeft       The number of chars left to parse (from @a a_ch).
3435  */
3436 #define KMK_CC_EVAL_IS_SPACE_AFTER_WORD(a_pCompiler, a_ch, a_ch2, a_cchLeft) \
3437     (   a_cchLeft == 0 \
3438      || KMK_CC_EVAL_IS_SPACE(a_ch) \
3439      || ((a_ch) == '\\' && (a_ch2) == (a_pCompiler)->chFirstEol) )
3440 
3441 
3442 /**
3443  * Common path for space skipping worker functions when escaped EOLs may be
3444  * involed.
3445  *
3446  * @returns Points to the first non-space character or end of input.
3447  * @param   pchWord         The current position. There is some kind of char
3448  * @param   cchLeft         The current number of chars left to parse in the
3449  *                          current line.
3450  * @param   pcchLeft        Where to store the updated @a cchLeft value.
3451  * @param   pCompiler       The compiler instance data.
3452  */
kmk_cc_eval_skip_spaces_with_esc_eol(const char * pchWord,size_t cchLeft,size_t * pcchLeft,PKMKCCEVALCOMPILER pCompiler)3453 static const char *kmk_cc_eval_skip_spaces_with_esc_eol(const char *pchWord, size_t cchLeft, size_t *pcchLeft,
3454                                                         PKMKCCEVALCOMPILER pCompiler)
3455 {
3456     /*
3457      * Skip further spaces. We unrolls 4 loops here.
3458      * ASSUMES cchEscEolSeq is either 2 or 3!
3459      */
3460     KMK_CC_ASSERT(pCompiler->cchEscEolSeq == 2 || pCompiler->cchEscEolSeq == 3);
3461     KMK_CC_ASSERT(pCompiler->iEscEol < pCompiler->cEscEols);
3462     while (cchLeft >= 4)
3463     {
3464         /* First char. */
3465         char ch = pchWord[0];
3466         if (KMK_CC_EVAL_IS_SPACE(ch))
3467         { /* maybe likely */ }
3468         else if (   ch == '\\'
3469                  && pchWord[1] == pCompiler->chFirstEol)
3470         {
3471             pchWord += pCompiler->cchEscEolSeq;
3472             cchLeft -= pCompiler->cchEscEolSeq;
3473             pCompiler->iEscEol++;
3474             continue;
3475         }
3476         else
3477         {
3478             *pcchLeft = cchLeft;
3479             return pchWord;
3480         }
3481 
3482         /* Second char. */
3483         ch = pchWord[1];
3484         if (KMK_CC_EVAL_IS_SPACE(ch))
3485         { /* maybe likely */ }
3486         else if (   ch == '\\'
3487                  && pchWord[2] == pCompiler->chFirstEol)
3488         {
3489             pchWord += 1 + pCompiler->cchEscEolSeq;
3490             cchLeft -= 1 + pCompiler->cchEscEolSeq;
3491             pCompiler->iEscEol++;
3492             continue;
3493         }
3494         else
3495         {
3496             *pcchLeft = cchLeft - 1;
3497             return pchWord + 1;
3498         }
3499 
3500         /* Third char. */
3501         ch = pchWord[2];
3502         if (KMK_CC_EVAL_IS_SPACE(ch))
3503         { /* maybe likely */ }
3504         else if (   ch == '\\'
3505                  && pchWord[3] == pCompiler->chFirstEol
3506                  && cchLeft >= 2 + pCompiler->cchEscEolSeq)
3507         {
3508             pchWord += 2 + pCompiler->cchEscEolSeq;
3509             cchLeft -= 2 + pCompiler->cchEscEolSeq;
3510             pCompiler->iEscEol++;
3511             continue;
3512         }
3513         else
3514         {
3515             *pcchLeft = cchLeft - 2;
3516             return pchWord + 2;
3517         }
3518 
3519         /* Third char. */
3520         ch = pchWord[3];
3521         if (KMK_CC_EVAL_IS_SPACE(ch))
3522         {
3523             pchWord += 4;
3524             cchLeft -= 4;
3525         }
3526         else if (   ch == '\\'
3527                  && cchLeft >= 3 + pCompiler->cchEscEolSeq
3528                  && pchWord[4] == pCompiler->chFirstEol)
3529         {
3530             pchWord += 3 + pCompiler->cchEscEolSeq;
3531             cchLeft -= 3 + pCompiler->cchEscEolSeq;
3532             pCompiler->iEscEol++;
3533         }
3534         else
3535         {
3536             *pcchLeft = cchLeft - 3;
3537             return pchWord + 3;
3538         }
3539     }
3540 
3541     /*
3542      * Simple loop for the final three chars.
3543      */
3544     while (cchLeft > 0)
3545     {
3546         /* First char. */
3547         char ch = *pchWord;
3548         if (KMK_CC_EVAL_IS_SPACE(ch))
3549         {
3550             pchWord += 1;
3551             cchLeft -= 1;
3552         }
3553         else if (   ch == '\\'
3554                  && cchLeft > pCompiler->cchEolSeq
3555                  && pchWord[1] == pCompiler->chFirstEol)
3556         {
3557             pchWord += pCompiler->cchEscEolSeq;
3558             cchLeft -= pCompiler->cchEscEolSeq;
3559             pCompiler->iEscEol++;
3560         }
3561         else
3562             break;
3563     }
3564 
3565     *pcchLeft = cchLeft;
3566     return pchWord;
3567 }
3568 
3569 
3570 /**
3571  * Common path for space skipping worker functions when no escaped EOLs need
3572  * considering.
3573  *
3574  * @returns Points to the first non-space character or end of input.
3575  * @param   pchWord         The current position. There is some kind of char
3576  * @param   cchLeft         The current number of chars left to parse in the
3577  *                          current line.
3578  * @param   pcchLeft        Where to store the updated @a cchLeft value.
3579  * @param   pCompiler       The compiler instance data.
3580  */
kmk_cc_eval_skip_spaces_without_esc_eol(const char * pchWord,size_t cchLeft,size_t * pcchLeft,PKMKCCEVALCOMPILER pCompiler)3581 static const char *kmk_cc_eval_skip_spaces_without_esc_eol(const char *pchWord, size_t cchLeft, size_t *pcchLeft,
3582                                                            PKMKCCEVALCOMPILER pCompiler)
3583 {
3584     /*
3585      * 4x loop unroll.
3586      */
3587     while (cchLeft >= 4)
3588     {
3589         if (KMK_CC_EVAL_IS_SPACE(pchWord[0]))
3590         {
3591             if (KMK_CC_EVAL_IS_SPACE(pchWord[1]))
3592             {
3593                 if (KMK_CC_EVAL_IS_SPACE(pchWord[2]))
3594                 {
3595                     if (KMK_CC_EVAL_IS_SPACE(pchWord[3]))
3596                     {
3597                         pchWord += 4;
3598                         cchLeft -= 4;
3599                     }
3600                     else
3601                     {
3602                         *pcchLeft = cchLeft - 3;
3603                         return pchWord + 3;
3604                     }
3605                 }
3606                 else
3607                 {
3608                     *pcchLeft = cchLeft - 2;
3609                     return pchWord + 2;
3610                 }
3611             }
3612             else
3613             {
3614                 *pcchLeft = cchLeft - 1;
3615                 return pchWord + 1;
3616             }
3617         }
3618         else
3619         {
3620             *pcchLeft = cchLeft;
3621             return pchWord;
3622         }
3623     }
3624 
3625     /*
3626      * The last 3. Not entirely sure if this yield good code.
3627      */
3628     switch (cchLeft & 3)
3629     {
3630         case 3:
3631             if (!KMK_CC_EVAL_IS_SPACE(*pchWord))
3632                 break;
3633             pchWord++;
3634             cchLeft--;
3635         case 2:
3636             if (!KMK_CC_EVAL_IS_SPACE(*pchWord))
3637                 break;
3638             pchWord++;
3639             cchLeft--;
3640         case 1:
3641             if (!KMK_CC_EVAL_IS_SPACE(*pchWord))
3642                 break;
3643             pchWord++;
3644             cchLeft--;
3645         case 0:
3646             break;
3647     }
3648 
3649     *pcchLeft = cchLeft;
3650     return pchWord;
3651 }
3652 
3653 
3654 /**
3655  * Used to skip spaces after a word.
3656  *
3657  * We ASSUME that the first char is a space or that we've reached the end of the
3658  * string (a_cchLeft == 0).
3659  *
3660  * @param   a_pCompiler     The compiler instance data.
3661  * @param   a_pchWord       The current input position, this will be moved to
3662  *                          the start of the next word or end of the input.
3663  * @param   a_cchLeft       The number of chars left to parse.  This will be
3664  *                          updated.
3665  */
3666 #define KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(a_pCompiler, a_pchWord, a_cchLeft) \
3667     do { \
3668         /* Skip the first char which is known to be a space, end of line or end of input. */ \
3669         if ((a_cchLeft) > 0) \
3670         { \
3671             char const chSkipBlanksFirst = *(a_pchWord); \
3672             KMK_CC_ASSERT(KMK_CC_EVAL_IS_SPACE_AFTER_WORD(a_pCompiler, chSkipBlanksFirst, (a_pchWord)[1], a_cchLeft)); \
3673             if (chSkipBlanksFirst != '\\') \
3674             { \
3675                 (a_pchWord) += 1; \
3676                 (a_cchLeft) -= 1; \
3677                 \
3678                 /* Another space or escaped EOL? Then there are probably more then, so call worker function. */ \
3679                 if ((a_cchLeft) > 0) \
3680                 { \
3681                     char const chSkipBlanksSecond = *(a_pchWord); \
3682                     if (KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(chSkipBlanksSecond)) \
3683                         (a_pchWord) = kmk_cc_eval_skip_spaces_after_word_slow(a_pchWord, &(a_cchLeft), \
3684                                                                               chSkipBlanksSecond, a_pCompiler); \
3685                 } \
3686             } \
3687             else /* escape sequences can be complicated. */ \
3688                 (a_pchWord) = kmk_cc_eval_skip_spaces_after_word_slow(a_pchWord, &(a_cchLeft), \
3689                                                                       chSkipBlanksFirst, a_pCompiler); \
3690         } \
3691     } while (0)
3692 
3693 /**
3694  * The slow path of KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD.
3695  *
3696  * This is called to handles escaped EOL sequences, as these can involve
3697  * multiple backslashes and therefore doesn't led themselves well to inlined
3698  * code.
3699  *
3700  * The other case this is used for is to handle more than once space, since it's
3701  * likely that when there are two there might be more.  No point in inlining
3702  * that, better do some loop unrolling instead.
3703  *
3704  * @returns Points to the first non-space character or end of input.
3705  * @param   pchWord             The current position. There is some kind of char
3706  * @param   pcchLeft            Pointer to the cchLeft variable, this is both
3707  *                              input and output.
3708  * @param   ch                  The current character.
3709  * @param   pCompiler           The compiler instance data.
3710  */
kmk_cc_eval_skip_spaces_after_word_slow(const char * pchWord,size_t * pcchLeft,char ch,PKMKCCEVALCOMPILER pCompiler)3711 static const char *kmk_cc_eval_skip_spaces_after_word_slow(const char *pchWord, size_t *pcchLeft, char ch,
3712                                                            PKMKCCEVALCOMPILER pCompiler)
3713 {
3714     size_t cchLeft = *pcchLeft;
3715 
3716     /*
3717      * It's all very simple when we don't have to consider escaped EOLs.
3718      */
3719     if (pCompiler->iEscEol >= pCompiler->cEscEols)
3720     {
3721         if (ch != '\\')
3722         {
3723             pchWord += 1;
3724             cchLeft -= 1;
3725         }
3726         else
3727             return pchWord;
3728         return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
3729     }
3730 
3731     /*
3732      * Skip the pending space or EOL found by the caller.  We need to
3733      * confirm the EOL.
3734      *
3735      * Note! We only need to care about simple backslash+EOL sequences here
3736      *       since we're either at the end of a validated word, or we've already
3737      *       skipped one space.  In the former case, someone else has already
3738      *       validated the escape esequence, in the latter case multiple
3739      *       backslashes would indicate a new word that that we should return.
3740      */
3741     if (ch != '\\')
3742     {
3743         pchWord += 1;
3744         cchLeft -= 1;
3745     }
3746     else if (   cchLeft >= pCompiler->cchEscEolSeq
3747              && pchWord[1] == pCompiler->chFirstEol)
3748     {
3749         KMK_CC_ASSERT(pCompiler->cchEolSeq == 1 || pchWord[2] == pCompiler->chSecondEol);
3750         pchWord += pCompiler->cchEscEolSeq;
3751         cchLeft -= pCompiler->cchEscEolSeq;
3752         pCompiler->iEscEol++;
3753 
3754         if (pCompiler->iEscEol < pCompiler->cEscEols)
3755         { /* likely */ }
3756         else return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
3757     }
3758     else
3759         return pchWord;
3760     return kmk_cc_eval_skip_spaces_with_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
3761 }
3762 
3763 
3764 /**
3765  * Skip zero or more spaces.
3766  *
3767  * This macro deals with a single space, if there are more or we're hittin some
3768  * possible escaped EOL sequence, work is deferred to a worker function.
3769  *
3770  * @param   a_pCompiler The compiler state.
3771  * @param   a_pchWord   The current input position. Advanced past spaces.
3772  * @param   a_cchLeft   The amount of input left to parse. Will be updated.
3773  */
3774 #define KMK_CC_EVAL_SKIP_SPACES(a_pCompiler, a_pchWord, a_cchLeft) \
3775     do { \
3776         if ((a_cchLeft) > 0) \
3777         { \
3778             char chSkipSpaces = *(a_pchWord); \
3779             if (KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(chSkipSpaces)) \
3780             { \
3781                 if (chSkipSpaces != '\\') \
3782                 { \
3783                     (a_pchWord) += 1; \
3784                     (a_cchLeft) -= 1; \
3785                     chSkipSpaces = *(a_pchWord); \
3786                     if (KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(chSkipSpaces)) \
3787                         (a_pchWord) = kmk_cc_eval_skip_spaces_slow(a_pchWord, &(a_cchLeft), chSkipSpaces, a_pCompiler); \
3788                 } \
3789                 else \
3790                     (a_pchWord) = kmk_cc_eval_skip_spaces_slow(a_pchWord, &(a_cchLeft), chSkipSpaces, a_pCompiler); \
3791             } \
3792         } \
3793     } while (0)
3794 
3795 
3796 /**
3797  * Worker for KMK_CC_EVAL_SKIP_SPACES.
3798  *
3799  * @returns Points to the first non-space character or end of input.
3800  * @param   pchWord             The current position. There is some kind of char
3801  * @param   pcchLeft            Pointer to the cchLeft variable, this is both
3802  *                              input and output.
3803  * @param   ch                  The current character.
3804  * @param   pCompiler           The compiler instance data.
3805  */
kmk_cc_eval_skip_spaces_slow(const char * pchWord,size_t * pcchLeft,char ch,PKMKCCEVALCOMPILER pCompiler)3806 static const char *kmk_cc_eval_skip_spaces_slow(const char *pchWord, size_t *pcchLeft, char ch, PKMKCCEVALCOMPILER pCompiler)
3807 {
3808     size_t cchLeft = *pcchLeft;
3809 #ifdef KMK_CC_STRICT
3810     size_t offWordCcStrict = pchWord - pCompiler->pszContent;
3811 #endif
3812     KMK_CC_ASSERT(cchLeft > 0);
3813     KMK_CC_ASSERT(cchLeft <= pCompiler->cchLine);
3814     KMK_CC_ASSERT(*pchWord == ch);
3815     KMK_CC_ASSERT(KMK_CC_EVAL_IS_SPACE_OR_BACKSLASH(ch));
3816     KMK_CC_ASSERT(offWordCcStrict >= pCompiler->offLine);
3817     KMK_CC_ASSERT(offWordCcStrict < pCompiler->offLine + pCompiler->cchLine);
3818     KMK_CC_ASSERT(   pCompiler->iEscEol >= pCompiler->cEscEols
3819                   || offWordCcStrict <= pCompiler->paEscEols[pCompiler->iEscEol].offEsc);
3820     KMK_CC_ASSERT(   pCompiler->iEscEol >= pCompiler->cEscEols
3821                   || pCompiler->iEscEol == 0
3822                   || offWordCcStrict >= pCompiler->paEscEols[pCompiler->iEscEol - 1].offEol + pCompiler->cchEolSeq);
3823 
3824     /*
3825      * If we don't need to consider escaped EOLs, things are much much simpler.
3826      */
3827     if (pCompiler->iEscEol >= pCompiler->cEscEols)
3828     {
3829         if (ch != '\\')
3830         {
3831             pchWord++;
3832             cchLeft--;
3833         }
3834         else
3835             return pchWord;
3836         return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
3837     }
3838 
3839     /*
3840      * Possible escaped EOL complications.
3841      */
3842     if (ch != '\\')
3843     {
3844         pchWord++;
3845         cchLeft--;
3846     }
3847     else
3848     {
3849         size_t          cchSkip;
3850         size_t          offWord;
3851         unsigned        iEscEol = pCompiler->iEscEol;
3852         if (iEscEol >= pCompiler->cEscEols)
3853             return pchWord;
3854 
3855         offWord = pchWord - pCompiler->pszContent;
3856         if (offWord < pCompiler->paEscEols[iEscEol].offEsc)
3857             return pchWord;
3858         KMK_CC_ASSERT(offWord == pCompiler->paEscEols[iEscEol].offEsc);
3859 
3860         cchSkip  = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq - offWord;
3861         pchWord += cchSkip;
3862         cchLeft -= cchSkip;
3863         pCompiler->iEscEol = ++iEscEol;
3864 
3865         if (iEscEol < pCompiler->cEscEols)
3866         { /* likely */ }
3867         else return kmk_cc_eval_skip_spaces_without_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
3868     }
3869     return kmk_cc_eval_skip_spaces_with_esc_eol(pchWord, cchLeft, pcchLeft, pCompiler);
3870 }
3871 
3872 
3873 /**
3874  * Skips to the end of a variable name.
3875  *
3876  * This may advance pCompiler->iEscEol.
3877  *
3878  * @returns Pointer to the first char after the variable name.
3879  * @param   pCompiler   The compiler state.
3880  * @param   pchWord     The current position. Must be at the start of the
3881  *                      variable name.
3882  * @param   cchLeft     The number of chars left to parse in the current line.
3883  * @param   pcchLeft    The to store the updated count of characters left to
3884  *                      parse.
3885  * @param   pfPlain     Where to store the plain variable name indicator.
3886  *                      Returns 0 if plain, and 1 if there are variable
3887  *                      references in it.
3888  */
kmk_cc_eval_skip_var_name(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,size_t * pcchLeft,int * pfPlain)3889 static const char *kmk_cc_eval_skip_var_name(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft,
3890                                              size_t *pcchLeft, int *pfPlain)
3891 {
3892     const char * const  pszContent = pCompiler->pszContent;
3893     size_t              off        = pchWord - pszContent;
3894     size_t const        offLineEnd = off + cchLeft;
3895     int                 fPlain     = 1;
3896     unsigned            iEscEol    = pCompiler->iEscEol;
3897 
3898     /* Check our expectations. */
3899     KMK_CC_ASSERT(cchLeft);
3900     KMK_CC_ASSERT(!KMK_CC_EVAL_IS_SPACE(*pchWord));
3901     KMK_CC_ASSERT(iEscEol <= pCompiler->cEscEols);
3902     KMK_CC_ASSERT(   iEscEol >= pCompiler->cEscEols
3903                   || off < pCompiler->paEscEols[iEscEol].offEol);
3904     KMK_CC_ASSERT(off >= (iEscEol == 0 ? pCompiler->offLine : pCompiler->paEscEols[iEscEol - 1].offEol + pCompiler->cchEolSeq));
3905 
3906     /*
3907      * The outer loop parses plain text.  Variable expansion ($) is handled
3908      * by an inner loop.
3909      */
3910     while (off < offLineEnd)
3911     {
3912         char ch = pszContent[off];
3913         if (!KMK_CC_EVAL_IS_SPACE_DOLLAR_OR_SLASH(ch))
3914             off++;
3915         else if (KMK_CC_EVAL_IS_SPACE(ch))
3916             break;
3917         else if (ch == '$')
3918         {
3919             off++;
3920             if (off < offLineEnd)
3921             {
3922                 char const chOpen = pszContent[off];
3923                 if (chOpen == '(' || chOpen == '{')
3924                 {
3925                     /*
3926                      * Got a $(VAR) or ${VAR} to deal with here.  This may
3927                      * include nested variable references and span multiple
3928                      * lines (at least for function calls).
3929                      *
3930                      * We scan forward till we've found the corresponding
3931                      * closing parenthesis, considering any open parentheses
3932                      * of the same kind as worth counting, even if there are
3933                      * no dollar preceeding them, just like GNU make does.
3934                      */
3935                     size_t const offStart = off - 1;
3936                     char const   chClose  = chOpen == '(' ? ')' : '}';
3937                     unsigned     cOpen    = 1;
3938                     off++;
3939                     for (;;)
3940                     {
3941                         if (off < offLineEnd)
3942                         {
3943                             ch = pszContent[off];
3944                             if (!(KMK_CC_EVAL_IS_PAREN_OR_SLASH(ch)))
3945                                 off++;
3946                             else
3947                             {
3948                                 off++;
3949                                 if (ch == chClose)
3950                                 {
3951                                     if (--cOpen == 0)
3952                                         break;
3953                                 }
3954                                 else if (ch == chOpen)
3955                                     cOpen++;
3956                                 else if (   ch == '\\'
3957                                          && iEscEol < pCompiler->cEscEols
3958                                          && off == pCompiler->paEscEols[iEscEol].offEsc)
3959                                 {
3960                                     off = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq;
3961                                     pCompiler->iEscEol = ++iEscEol;
3962                                 }
3963                             }
3964                         }
3965                         else if (cOpen == 1)
3966                             kmk_cc_eval_fatal(pCompiler, &pszContent[offStart],
3967                                               "Variable reference is missing '%c'", chClose);
3968                         else
3969                             kmk_cc_eval_fatal(pCompiler, &pszContent[offStart],
3970                                               "%u variable references are missing '%c'", cOpen, chClose);
3971                     }
3972                 }
3973                 /* Single char variable name. */
3974                 else if (!KMK_CC_EVAL_IS_SPACE(chOpen))
3975                 {  /* likely */ }
3976                 else
3977                     kmk_cc_eval_fatal(pCompiler, &pszContent[off], "Expected variable name after '$', not end of line");
3978             }
3979             else
3980                 kmk_cc_eval_fatal(pCompiler, &pszContent[off], "Expected variable name after '$', not end of line");
3981             fPlain = 0;
3982         }
3983         /* Deal with potential escaped EOL. */
3984         else if (   ch != '\\'
3985                  || iEscEol >= pCompiler->cEscEols
3986                  || off != pCompiler->paEscEols[iEscEol].offEsc )
3987             off++;
3988         else
3989             break;
3990     }
3991 
3992     *pcchLeft = offLineEnd - off;
3993     *pfPlain  = fPlain;
3994     return &pszContent[off];
3995 }
3996 
3997 
3998 #if 0  /* unused atm */
3999 /**
4000  * Prepares for copying a command line.
4001  *
4002  * The current version of this code will not modify any of the paEscEols
4003  * entries, unlike our kmk_cc_eval_prep_normal_line sibling function.
4004  *
4005  * @returns The number of chars that will be copied by
4006  *          kmk_cc_eval_copy_prepped_command_line().
4007  * @param   pCompiler   The compiler instance data.
4008  * @param   pchLeft     Pointer to the first char to copy from the current line.
4009  *                      This does not have to the start of a word.
4010  * @param   cchLeft     The number of chars left on the current line starting at
4011  *                      @a pchLeft.
4012  */
4013 static size_t kmk_cc_eval_prep_command_line(PKMKCCEVALCOMPILER pCompiler, const char * const pchLeft, size_t cchLeft)
4014 {
4015     size_t          cchRet;
4016     unsigned        iEscEol  = pCompiler->iEscEol;
4017     unsigned const  cEscEols = pCompiler->cEscEols;
4018 
4019     KMK_CC_ASSERT(cchLeft > 0);
4020     KMK_CC_ASSERT(iEscEol <= cEscEols);
4021 
4022     if (iEscEol >= cEscEols)
4023     {
4024         /*
4025          * No escaped EOLs left, dead simple.
4026          */
4027         cchRet = cchLeft;
4028     }
4029     else
4030     {
4031         /*
4032          * Compared to the normal prepping of a line, this is actually
4033          * really simple.  We need to account for two kind of conversions:
4034          *      - One leading tab is skipped after escaped EOL.
4035          *      - Convert EOL to LF.
4036          */
4037         const char * const  pszContent = pCompiler->pszContent;
4038         size_t       const  cchEolSeq  = pCompiler->cchEolSeq;
4039 
4040 #ifdef KMK_CC_STRICT
4041         size_t const offLeft   = pchLeft - pszContent;
4042         KMK_CC_ASSERT(offLeft + cchLeft <= pCompiler->offLine + pCompiler->cchLine);
4043         KMK_CC_ASSERT(offLeft + cchLeft <= pCompiler->cchContent);
4044         KMK_CC_ASSERT(offLeft <  pCompiler->paEscEols[iEscEol].offEsc);
4045         KMK_CC_ASSERT(offLeft >= (iEscEol ? pCompiler->paEscEols[cEscEols - 1].offEol + pCompiler->cchEolSeq : pCompiler->offLine));
4046 #endif
4047 
4048         cchRet = cchLeft;
4049         if (cchEolSeq > 1)
4050             cchRet -= (cchEolSeq - 1) * cEscEols;
4051         do
4052         {
4053             if (pszContent[pCompiler->paEscEols[cchEolSeq].offEol])
4054                 cchRet--;
4055             iEscEol++;
4056         } while (iEscEol < cEscEols);
4057     }
4058     return cchRet;
4059 }
4060 
4061 
4062 /**
4063  * Copies a command line to the buffer @a pszDst points to.
4064  *
4065  * Must only be used immediately after kmk_cc_eval_prep_command_line().
4066  *
4067  * @returns
4068  * @param   pCompiler   The compiler instance data.
4069  * @param   pchLeft     Pointer to the first char to copy from the current line.
4070  *                      This does not have to the start of a word.
4071  * @param   cchPrepped  The return value of kmk_cc_eval_prep_command_line().
4072  * @param   pszDst      The destination buffer, must be at least @a cchPrepped
4073  *                      plus one (terminator) char big.
4074  */
4075 static void kmk_cc_eval_copy_prepped_command_line(PKMKCCEVALCOMPILER pCompiler, const char *pchLeft,
4076                                                   size_t cchPrepped, char *pszDst)
4077 {
4078     unsigned        iEscEol  = pCompiler->iEscEol;
4079     unsigned const  cEscEols = pCompiler->cEscEols;
4080     if (iEscEol >= cEscEols)
4081     {
4082         /* Single line. */
4083         memcpy(pszDst, pchLeft, cchPrepped);
4084         pszDst[cchPrepped] = '\0';
4085     }
4086     else
4087     {
4088         /* Multiple lines with normalized EOL and maybe one stripped leading TAB. */
4089         char * const        pszDstStart = pszDst;
4090         const char * const  pszContent  = pCompiler->pszContent;
4091         size_t const        cchEolSeq   = pCompiler->cchEolSeq;
4092         size_t              offLeft     = pchLeft - pCompiler->pszContent;
4093         size_t              cchCopy;
4094 
4095         do
4096         {
4097             size_t offEol = pCompiler->paEscEols[iEscEol].offEsc;
4098             cchCopy = offEol - offLeft;
4099             KMK_CC_ASSERT(offEol >= offLeft);
4100 
4101             memcpy(pszDst, &pszContent[offLeft], cchCopy);
4102             pszDst += cchCopy;
4103             *pszDst += '\n';
4104 
4105             offLeft = offEol + cchEolSeq;
4106             if (pszContent[offLeft] == '\t')
4107                 offLeft++;
4108         } while (iEscEol < cEscEols);
4109 
4110         cchCopy = cchPrepped - (pszDst - pszDstStart);
4111         KMK_CC_ASSERT(cchCopy <= cchPrepped);
4112         memcpy(pszDst, &pszContent[offLeft], cchCopy);
4113         pszDst += cchCopy;
4114 
4115         *pszDst = '\0';
4116         KMK_CC_ASSERT(pszDst == &pszDstStart[cchPrepped]);
4117     }
4118 }
4119 #endif /* unused atm */
4120 
4121 
4122 /**
4123  * Helper for ensuring that we've got sufficient number of words allocated.
4124  */
4125 #define KMK_CC_EVAL_ENSURE_WORDS(a_pCompiler, a_cRequiredWords) \
4126     do { \
4127         if ((a_cRequiredWords) < (a_pCompiler)->cWordsAllocated) \
4128         { /* likely */ } \
4129         else \
4130         { \
4131             unsigned cEnsureWords = ((a_cRequiredWords) + 3 /*15*/) & ~(unsigned)3/*15*/; \
4132             KMK_CC_ASSERT((a_cRequiredWords) < 0x8000); \
4133             (a_pCompiler)->paWords = (PKMKCCEVALWORD)xmalloc(cEnsureWords * sizeof((a_pCompiler)->paWords)[0]); \
4134         } \
4135     } while (0)
4136 
4137 /**
4138  * Parses the remainder of the line into simple words.
4139  *
4140  * The resulting words are classified as either kKmkCcEvalToken_WordPlain or
4141  * kKmkCcEvalToken_WordWithDollar.
4142  *
4143  * @returns Number of words.
4144  * @param   pCompiler   The compiler state.
4145  * @param   pchWord     Where to start, we expect this to be at a word.
4146  * @param   cchLeft     The number of chars left to parse on this line.
4147  *                      This is expected to be non-zero.
4148  */
kmk_cc_eval_parse_words(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft)4149 static unsigned kmk_cc_eval_parse_words(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
4150 {
4151     unsigned iEscEol  = pCompiler->iEscEol;
4152     unsigned cEscEols = pCompiler->cEscEols;
4153     unsigned cWords   = 0;
4154 
4155     /* Precoditions. */
4156     KMK_CC_ASSERT(cchLeft > 0);
4157     KMK_CC_ASSERT(!KMK_CC_EVAL_IS_SPACE(*pchWord));
4158 
4159     /*
4160      * If we don't have to deal with escaped EOLs, the find-end-of word search
4161      * becomes a little bit simpler.  Since this function will be used a lot
4162      * for simple lines with single words, this could maybe save a nano second
4163      * or two.
4164      */
4165     if (iEscEol >= cEscEols)
4166     {
4167         do
4168         {
4169             size_t          cchSkipAfter = 0;
4170             size_t          cchWord      = 1;
4171             KMKCCEVALTOKEN  enmToken     = kKmkCcEvalToken_WordPlain;
4172 
4173             /* Find the end of the current word. */
4174             while (cchWord < cchLeft)
4175             {
4176                 char ch = pchWord[cchWord];
4177                 if (!KMK_CC_EVAL_IS_SPACE_OR_DOLLAR(ch))
4178                 { /* likely */ }
4179                 else if (ch == '$')
4180                     enmToken = kKmkCcEvalToken_WordWithDollar;
4181                 else
4182                     break;
4183                 cchWord++;
4184             }
4185 
4186             /* Add the word. */
4187             KMK_CC_EVAL_ENSURE_WORDS(pCompiler, cWords + 1);
4188             pCompiler->paWords[cWords].pchWord  = pchWord;
4189             pCompiler->paWords[cWords].cchWord  = cchWord;
4190             pCompiler->paWords[cWords].enmToken = enmToken;
4191             cWords++;
4192 
4193             /* Skip the work and any trailing blanks. */
4194             cchWord += cchSkipAfter;
4195             pchWord += cchWord;
4196             cchLeft -= cchWord;
4197             KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
4198         } while (cchLeft > 0);
4199     }
4200     /*
4201      * Have to deal with escaped EOLs.
4202      */
4203     else
4204     {
4205         const char *pszContent = pCompiler->pszContent;
4206         do
4207         {
4208             size_t          cchSkipAfter = 0;
4209             size_t          cchWord      = 1;
4210             KMKCCEVALTOKEN  enmToken     = kKmkCcEvalToken_WordPlain;
4211 
4212             /* Find the end of the current word. */
4213             while (cchWord < cchLeft)
4214             {
4215                 char ch = pchWord[cchWord];
4216                 if (!KMK_CC_EVAL_IS_SPACE_DOLLAR_OR_SLASH(ch))
4217                 { /* likely */ }
4218                 else if (ch == '$')
4219                     enmToken = kKmkCcEvalToken_WordWithDollar;
4220                 else if (ch != '\\')
4221                     break;
4222                 else if ((size_t)(&pchWord[cchWord] - pszContent) == pCompiler->paEscEols[iEscEol].offEsc)
4223                 {
4224                     cchSkipAfter = pCompiler->paEscEols[iEscEol].offEol - pCompiler->paEscEols[iEscEol].offEsc
4225                                  + pCompiler->cchEolSeq;
4226                     iEscEol++;
4227                     break;
4228                 }
4229                 cchWord++;
4230             }
4231 
4232             /* Add the word. */
4233             KMK_CC_EVAL_ENSURE_WORDS(pCompiler, cWords + 1);
4234             pCompiler->paWords[cWords].pchWord  = pchWord;
4235             pCompiler->paWords[cWords].cchWord  = cchWord;
4236             pCompiler->paWords[cWords].enmToken = enmToken;
4237             cWords++;
4238 
4239             /* Skip the work and any trailing blanks. */
4240             cchWord += cchSkipAfter;
4241             pchWord += cchWord;
4242             cchLeft -= cchWord;
4243             KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
4244         } while (cchLeft > 0);
4245     }
4246     pCompiler->cWords = cWords;
4247     return cWords;
4248 }
4249 
4250 
4251 
4252 
4253 /**
4254  * Gather string from segments and optional space insertion trick.
4255  *
4256  * @param   pszDst          The destination buffer.
4257  * @param   paSegs          The source segments.
4258  * @param   cSegs           The number of segments.
4259  * @param   cchDstPrepped   The size of pszDst, excluding the terminator.
4260  */
kmk_cc_eval_strcpyv(char * pszDst,PCKMKCCEVALSTRCPYSEG paSegs,unsigned cSegs,size_t cchDstPrepped)4261 static void kmk_cc_eval_strcpyv(char *pszDst, PCKMKCCEVALSTRCPYSEG paSegs, unsigned cSegs, size_t cchDstPrepped)
4262 {
4263     const char *pszDstStart = pszDst;
4264     unsigned    iSeg = 0;
4265     while (iSeg < cSegs)
4266     {
4267         size_t cchToCopy;
4268         if (paSegs[iSeg].cchSrcAndPrependSpace >= 0)
4269             cchToCopy = paSegs[iSeg].cchSrcAndPrependSpace;
4270         else
4271         {
4272             cchToCopy = -paSegs[iSeg].cchSrcAndPrependSpace;
4273             *pszDst++ = ' ';
4274         }
4275 
4276         memcpy(pszDst, paSegs[iSeg].pchSrc, cchToCopy);
4277         pszDst += cchToCopy;
4278 
4279         iSeg++;
4280     }
4281     *pszDst = '\0';
4282     KMK_CC_ASSERT(pszDst == &pszDstStart[cchDstPrepped]); K_NOREF(pszDstStart); K_NOREF(cchDstPrepped);
4283 }
4284 
4285 
4286 /**
4287  * Allocate a byte buffer and ocpy the prepared string segments into it.
4288  *
4289  * The caller must call kmk_cc_block_realign!
4290  *
4291  * @returns Pointer to the duplicated string.
4292  * @param   pCompiler       The compiler instance data.
4293  * @param   cchPrepped      The length of the prepped string segments.
4294  */
kmk_cc_eval_strdup_prepped(PKMKCCEVALCOMPILER pCompiler,size_t cchPrepped)4295 static char *kmk_cc_eval_strdup_prepped(PKMKCCEVALCOMPILER pCompiler, size_t cchPrepped)
4296 {
4297     char *pszCopy = kmk_cc_block_byte_alloc(pCompiler->ppBlockTail, cchPrepped + 1);
4298     kmk_cc_eval_strcpyv(pszCopy, pCompiler->paStrCopySegs, pCompiler->cStrCopySegs, cchPrepped);
4299     return pszCopy;
4300 }
4301 
4302 
4303 /**
4304  * Strip trailing spaces from prepped copy
4305  *
4306  * @param   paSegs          The segments to strip trailing chars from.
4307  * @param   pcSegs          The number of segments (in/out).
4308  * @param   pcchDstPrepped  The total number of chars prepped (in/out).
4309  */
kmk_cc_eval_strip_right_v(PKMKCCEVALSTRCPYSEG paSegs,unsigned * pcSegs,size_t * pcchDstPrepped)4310 static void kmk_cc_eval_strip_right_v(PKMKCCEVALSTRCPYSEG paSegs, unsigned *pcSegs, size_t *pcchDstPrepped)
4311 {
4312     /*
4313      * Work our way thru the segments, from the end obviously.
4314      */
4315     size_t   cchDstPrepped = *pcchDstPrepped;
4316     unsigned cSegs         = *pcSegs;
4317     while (cSegs > 0)
4318     {
4319         unsigned    iSeg   = cSegs - 1;
4320         const char *pszSrc = paSegs[iSeg].pchSrc;
4321         size_t      cchSrc = paSegs[iSeg].cchSrcAndPrependSpace >= 0
4322                            ? paSegs[iSeg].cchSrcAndPrependSpace : -paSegs[iSeg].cchSrcAndPrependSpace;
4323         if (cchSrc)
4324         {
4325             /*
4326              * Check for trailing spaces.
4327              */
4328             size_t cchSrcOrg;
4329             if (!KMK_CC_EVAL_IS_SPACE(pszSrc[cchSrc - 1]))
4330             {
4331                 /* Special case: No trailing spaces at all. No need to update
4332                                  input/output variables. */
4333                 if (cSegs == *pcSegs)
4334                     return;
4335                 break;
4336             }
4337 
4338             /* Skip the rest of the trailing spaces. */
4339             cchSrcOrg = cchSrc;
4340             do
4341                 cchSrc--;
4342             while (cchSrc > 0 && KMK_CC_EVAL_IS_SPACE(pszSrc[cchSrc - 1]));
4343 
4344             if (cchSrc > 0)
4345             {
4346                 /*
4347                  * There are non-space chars in this segment. So, update the
4348                  * segment and total char count and we're done.
4349                  */
4350                 cchDstPrepped -= cchSrcOrg - cchSrc;
4351                 if (paSegs[iSeg].cchSrcAndPrependSpace < 0)
4352                     paSegs[iSeg].cchSrcAndPrependSpace = -(ssize_t)cchSrc;
4353                 else
4354                     paSegs[iSeg].cchSrcAndPrependSpace = cchSrc;
4355                 break;
4356             }
4357 
4358             /*
4359              * Skip the whole segment.
4360              */
4361             cchDstPrepped -= cchSrcOrg + (paSegs[iSeg].cchSrcAndPrependSpace < 0);
4362         }
4363         cSegs--;
4364     }
4365     *pcchDstPrepped = cchDstPrepped;
4366     *pcSegs         = cSegs;
4367 }
4368 
4369 /**
4370  * Helper for ensuring that we've got sufficient number of string copy segments.
4371  */
4372 #define KMK_CC_EVAL_ENSURE_STRCOPY_SEGS(a_pCompiler, a_cRequiredSegs) \
4373     do { \
4374         if ((a_cRequiredSegs) < (a_pCompiler)->cStrCopySegsAllocated) \
4375         { /* likely */ } \
4376         else \
4377         { \
4378             unsigned cEnsureSegs = ((a_cRequiredSegs) + 3 /*15*/) & ~(unsigned)3/*15*/; \
4379             KMK_CC_ASSERT((a_cRequiredSegs) < 0x8000); \
4380             (a_pCompiler)->paStrCopySegs = (PKMKCCEVALSTRCPYSEG)xmalloc(cEnsureSegs * sizeof((a_pCompiler)->paStrCopySegs)[0]); \
4381         } \
4382     } while (0)
4383 
4384 
4385 /**
4386  * Prepares for copying a normal line, extended version.
4387  *
4388  * This does not assume that we start on a word, it can handle any starting
4389  * character.  It can also prepare partial copies.
4390  *
4391  * In addition to the returned information, this will store instruction in
4392  * paEscEols for the following kmk_cc_eval_strcpyv() call.
4393  *
4394  * This will advance pCompiler->iEscEol, so that it's possible to use the common
4395  * macros and helpers for parsing what comes afterwards.
4396  *
4397  * @returns The number of chars that will be copied by kmk_cc_eval_strcpyv().
4398  * @param   pCompiler               The compiler instance data.
4399  * @param   pchWord                 Pointer to the first char to copy from the
4400  *                                  current line. This must be the start of a
4401  *                                  word.
4402  * @param   cchLeft                 The number of chars left on the current line
4403  *                                  starting at @a pchWord.
4404  */
kmk_cc_eval_prep_normal_line_ex(PKMKCCEVALCOMPILER pCompiler,const char * const pchWord,size_t cchLeft)4405 static size_t kmk_cc_eval_prep_normal_line_ex(PKMKCCEVALCOMPILER pCompiler, const char * const pchWord, size_t cchLeft)
4406 {
4407     size_t          cchRet;
4408     unsigned        iEscEol  = pCompiler->iEscEol;
4409     unsigned const  cEscEols = pCompiler->cEscEols;
4410 
4411     KMK_CC_ASSERT(iEscEol <= cEscEols);
4412 
4413     if (cchLeft > 0)
4414     {
4415         /*
4416          * If there are no escaped EOLs left, just copy exactly
4417          * what was passed in.
4418          */
4419         if (iEscEol >= cEscEols)
4420         {
4421             KMK_CC_EVAL_ENSURE_STRCOPY_SEGS(pCompiler, 1);
4422             pCompiler->cStrCopySegs = 1;
4423             pCompiler->paStrCopySegs[0].pchSrc = pchWord;
4424             pCompiler->paStrCopySegs[0].cchSrcAndPrependSpace = cchRet = cchLeft;
4425         }
4426         /*
4427          * Ok, we have to deal with escaped EOLs and do the proper
4428          * replacement of escaped newlines with space.  The deal is that we
4429          * collaps all whitespace before and after one or more newlines into a
4430          * single space.  (FreeBSD make does this differently, by the by.)
4431          */
4432         else
4433         {
4434             const char * const  pszContent    = pCompiler->pszContent;
4435             size_t              offWord       = pchWord - pCompiler->pszContent;
4436             size_t const        offLineEnd    = offWord + cchLeft;             /* Note! Not necessarily end of line.*/
4437             size_t              offEsc;
4438             size_t              fPendingSpace = 0;
4439             unsigned            cSegs         = 0;
4440             size_t              cchSeg;
4441 
4442             /* Go nuts checking our preconditions here. */
4443             KMK_CC_ASSERT(offWord >= pCompiler->offLine);
4444             KMK_CC_ASSERT(offWord + cchLeft <= pCompiler->offLine + pCompiler->cchLine);
4445             KMK_CC_ASSERT(offWord + cchLeft <= pCompiler->cchContent);
4446             KMK_CC_ASSERT(offWord <= pCompiler->paEscEols[iEscEol].offEsc);
4447             KMK_CC_ASSERT(offWord >= (iEscEol ? pCompiler->paEscEols[cEscEols - 1].offEol + pCompiler->cchEolSeq
4448                                               : pCompiler->offLine));
4449             KMK_CC_ASSERT(offWord <  offLineEnd);
4450 
4451             /* Make sure we've got more than enough segments to fill in. */
4452             KMK_CC_EVAL_ENSURE_STRCOPY_SEGS(pCompiler, cEscEols - iEscEol + 2);
4453 
4454             /*
4455              * All but the last line.
4456              */
4457             cchRet = 0;
4458             do
4459             {
4460                 KMK_CC_ASSERT(offWord < offLineEnd);
4461                 offEsc = pCompiler->paEscEols[iEscEol].offEsc;
4462                 if (offWord < offEsc)
4463                 {
4464                     /* Strip trailing spaces. */
4465                     while (offEsc > offWord && KMK_CC_EVAL_IS_SPACE(pszContent[offEsc - 1]))
4466                         offEsc--;
4467                     cchSeg = offEsc - offWord;
4468                     if (cchSeg)
4469                     {
4470                         /* Add segment. */
4471                         pCompiler->paStrCopySegs[cSegs].pchSrc = &pszContent[offWord];
4472                         if (offEsc < offLineEnd)
4473                         {
4474                             pCompiler->paStrCopySegs[cSegs].cchSrcAndPrependSpace = fPendingSpace
4475                                                                                   ? -(ssize_t)cchSeg : (ssize_t)cchSeg;
4476                             cchRet       += cchSeg + fPendingSpace;
4477                             cSegs        += 1;
4478                             fPendingSpace = 1;
4479                         }
4480                         else
4481                         {
4482                             cchSeg = offLineEnd - offWord;
4483                             pCompiler->paStrCopySegs[cSegs].cchSrcAndPrependSpace = fPendingSpace
4484                                                                                   ? -(ssize_t)cchSeg : (ssize_t)cchSeg;
4485                             pCompiler->cStrCopySegs = cSegs + 1;
4486                             pCompiler->iEscEol      = iEscEol;
4487                             return cchRet + cchSeg + fPendingSpace;
4488                         }
4489                     }
4490                 }
4491                 else
4492                     KMK_CC_ASSERT(offWord == offEsc);
4493 
4494                 /* Next line. */
4495                 offWord = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq;
4496                 iEscEol++;
4497 
4498                 /* Strip leading spaces. */
4499                 while (offWord < offLineEnd && KMK_CC_EVAL_IS_SPACE(pszContent[offWord]))
4500                     offWord++;
4501                 if (offWord >= offLineEnd)
4502                 {
4503                     pCompiler->cStrCopySegs = cSegs;
4504                     pCompiler->iEscEol      = iEscEol;
4505                     return cchRet;
4506                 }
4507             } while (iEscEol < cEscEols);
4508 
4509             /*
4510              * The last line.
4511              */
4512             cchSeg      = offLineEnd - offWord;
4513             cchRet     += cchSeg;
4514             pCompiler->paStrCopySegs[cSegs].pchSrc                = &pszContent[offWord];
4515             pCompiler->paStrCopySegs[cSegs].cchSrcAndPrependSpace = fPendingSpace
4516                                                                   ? -(ssize_t)cchSeg : (ssize_t)cchSeg;
4517             pCompiler->cStrCopySegs = cSegs + 1;
4518             pCompiler->iEscEol      = iEscEol;
4519         }
4520     }
4521     /*
4522      * Odd case: Nothing to copy.
4523      */
4524     else
4525     {
4526         cchRet = 0;
4527         pCompiler->cStrCopySegs = 0;
4528     }
4529     return cchRet;
4530 }
4531 
4532 
4533 /**
4534  * Prepares for copying a normal line, from the given position all the way to
4535  * the end.
4536  *
4537  * In addition to the returned information, this will store instruction in
4538  * paStrCopySegs and cSTrCopySeg for the following kmk_cc_eval_strcpyv() call.
4539  *
4540  * @returns The number of chars that will be copied by kmk_cc_eval_strcpyv().
4541  * @param   pCompiler               The compiler instance data.
4542  * @param   pchWord                 Pointer to the first char to copy from the
4543  *                                  current line. This must be the start of a
4544  *                                  word.
4545  * @param   cchLeft                 The number of chars left on the current line
4546  *                                  starting at @a pchWord.
4547  */
kmk_cc_eval_prep_normal_line(PKMKCCEVALCOMPILER pCompiler,const char * const pchWord,size_t cchLeft)4548 static size_t kmk_cc_eval_prep_normal_line(PKMKCCEVALCOMPILER pCompiler, const char * const pchWord, size_t cchLeft)
4549 {
4550     size_t          cchRet;
4551     unsigned        iEscEol  = pCompiler->iEscEol;
4552     unsigned const  cEscEols = pCompiler->cEscEols;
4553 
4554     KMK_CC_ASSERT(cchLeft > 0);
4555     KMK_CC_ASSERT(!KMK_CC_EVAL_IS_SPACE(*pchWord)); /* The fact that we're standing at a word, is exploited below. */
4556     KMK_CC_ASSERT(iEscEol <= cEscEols);
4557 
4558     /*
4559      * If there are no escaped EOLs left, just copy what was specified,
4560      * optionally sans any trailing spaces.
4561      */
4562     if (iEscEol >= cEscEols)
4563     {
4564         cchRet = cchLeft;
4565 
4566         KMK_CC_EVAL_ENSURE_STRCOPY_SEGS(pCompiler, 1);
4567         pCompiler->cStrCopySegs = 1;
4568         pCompiler->paStrCopySegs[0].pchSrc = pchWord;
4569         pCompiler->paStrCopySegs[0].cchSrcAndPrependSpace = cchRet;
4570     }
4571     /*
4572      * Ok, we have to deal with escaped EOLs and do the proper
4573      * replacement of escaped newlines with space.  The deal is that we
4574      * collaps all whitespace before and after one or more newlines into a
4575      * single space.  (FreeBSD make does this differently, by the by.)
4576      */
4577     else
4578     {
4579         const char *pszContent = pCompiler->pszContent;
4580         size_t      offWord    = pchWord - pCompiler->pszContent;
4581         size_t      offEsc;
4582         size_t      fPendingSpace;
4583         size_t      cchSeg;
4584         unsigned    cSegs      = 0;
4585 
4586         /* Go nuts checking our preconditions here. */
4587         KMK_CC_ASSERT(offWord >= pCompiler->offLine);
4588         KMK_CC_ASSERT(offWord + cchLeft <= pCompiler->offLine + pCompiler->cchLine);
4589         KMK_CC_ASSERT(offWord + cchLeft <= pCompiler->cchContent);
4590         KMK_CC_ASSERT(offWord <  pCompiler->paEscEols[iEscEol].offEsc);
4591         KMK_CC_ASSERT(offWord >= (iEscEol ? pCompiler->paEscEols[iEscEol - 1].offEol + pCompiler->cchEolSeq : pCompiler->offLine));
4592 
4593         /* Make sure we've got more than enough segments to fill in. */
4594         KMK_CC_EVAL_ENSURE_STRCOPY_SEGS(pCompiler, cEscEols - iEscEol + 2);
4595 
4596         /*
4597          * First line - We're at the start of a word, so no left stripping needed.
4598          */
4599         offEsc = pCompiler->paEscEols[iEscEol].offEsc;
4600         KMK_CC_ASSERT(offEsc > offWord);
4601         while (KMK_CC_EVAL_IS_SPACE(pszContent[offEsc - 1]))
4602             offEsc--;
4603         KMK_CC_ASSERT(offEsc > offWord);
4604 
4605         fPendingSpace = 1;
4606         cchRet        = offEsc - offWord;
4607         pCompiler->paStrCopySegs[cSegs].cchSrcAndPrependSpace = cchRet;
4608         pCompiler->paStrCopySegs[cSegs].pchSrc                = pchWord;
4609         cSegs++;
4610 
4611         offWord = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq;
4612         iEscEol++;
4613 
4614         /*
4615          * All but the last line.
4616          */
4617         while (iEscEol < cEscEols)
4618         {
4619             offEsc = pCompiler->paEscEols[iEscEol].offEsc;
4620 
4621             /* Strip leading spaces. */
4622             while (offWord < offEsc && KMK_CC_EVAL_IS_SPACE(pszContent[offWord]))
4623                 offWord++;
4624 
4625             if (offWord < offEsc)
4626             {
4627                 /* Strip trailing spaces. */
4628                 while (KMK_CC_EVAL_IS_SPACE(pszContent[offEsc - 1]))
4629                     offEsc--;
4630                 cchSeg = offEsc - offWord;
4631                 pCompiler->paStrCopySegs[cSegs].cchSrcAndPrependSpace = fPendingSpace ? -(ssize_t)cchSeg : (ssize_t)cchSeg;
4632                 cchRet += cchSeg + fPendingSpace;
4633                 pCompiler->paStrCopySegs[cSegs].pchSrc                = &pszContent[offWord];
4634                 cSegs  += 1;
4635                 fPendingSpace = 1;
4636             }
4637 
4638             /* Next. */
4639             offWord = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq;
4640             iEscEol++;
4641         }
4642 
4643         /*
4644          * Final line. We must calculate the end of line offset our selves here.
4645          */
4646         offEsc = &pchWord[cchLeft] - pszContent;
4647         while (offWord < offEsc && KMK_CC_EVAL_IS_SPACE(pszContent[offWord]))
4648             offWord++;
4649 
4650         if (offWord < offEsc)
4651         {
4652             cchSeg = offEsc - offWord;
4653             pCompiler->paStrCopySegs[cSegs].cchSrcAndPrependSpace = fPendingSpace ? -(ssize_t)cchSeg : (ssize_t)cchSeg;
4654             cchRet += cchSeg + fPendingSpace;
4655             pCompiler->paStrCopySegs[cSegs].pchSrc                = &pszContent[offWord];
4656             cSegs  += 1;
4657         }
4658 
4659         pCompiler->cStrCopySegs = cSegs;
4660     }
4661     return cchRet;
4662 }
4663 
4664 
4665 /**
4666  * Common worker for all kmk_cc_eval_do_if*() functions.
4667  *
4668  * @param   pCompiler   The compiler state.
4669  * @param   pIfCore     The new IF statement.
4670  * @param   fInElse     Set if this is an 'else if' (rather than just 'if').
4671  */
kmk_cc_eval_do_if_core(PKMKCCEVALCOMPILER pCompiler,PKMKCCEVALIFCORE pIfCore,int fInElse)4672 static void kmk_cc_eval_do_if_core(PKMKCCEVALCOMPILER pCompiler, PKMKCCEVALIFCORE pIfCore, int fInElse)
4673 {
4674     unsigned iIf = pCompiler->cIfs;
4675     if (!fInElse)
4676     {
4677         /* Push an IF statement. */
4678         if (iIf < KMK_CC_EVAL_MAX_IF_DEPTH)
4679         {
4680             pCompiler->cIfs = iIf + 1;
4681             pCompiler->apIfs[iIf] = pIfCore;
4682             pIfCore->pPrevCond = NULL;
4683         }
4684         else
4685             kmk_cc_eval_fatal(pCompiler, NULL, "Too deep IF nesting");
4686     }
4687     else if (iIf > 0)
4688     {
4689         /* Link an IF statement. */
4690         iIf--;
4691         pIfCore->pPrevCond    = pCompiler->apIfs[iIf];
4692         pCompiler->apIfs[iIf] = pIfCore;
4693     }
4694     else
4695         kmk_cc_eval_fatal(pCompiler, NULL, "'else if' without 'if'");
4696     pIfCore->pNextTrue    = (PKMKCCEVALCORE)kmk_cc_block_get_next_ptr(*pCompiler->ppBlockTail);
4697     pIfCore->pNextFalse   = NULL; /* This is set by else or endif. */
4698     pIfCore->pTrueEndJump = NULL; /* This is set by else or endif. */
4699 }
4700 
4701 
4702 /**
4703  * Deals with 'if expr' and 'else if expr' statements.
4704  *
4705  * @returns 1 to indicate we've handled a keyword (see
4706  *          kmk_cc_eval_try_handle_keyword).
4707  * @param   pCompiler   The compiler state.
4708  * @param   pchWord     First char after 'if'.
4709  * @param   cchLeft     The number of chars left to parse on this line.
4710  * @param   fInElse     Set if this is an 'else if' (rather than just 'if').
4711  */
kmk_cc_eval_do_if(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,int fInElse)4712 static int kmk_cc_eval_do_if(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, int fInElse)
4713 {
4714     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
4715     if (cchLeft)
4716     {
4717         PKMKCCEVALIFEXPR pInstr;
4718         size_t           cchExpr = kmk_cc_eval_prep_normal_line(pCompiler, pchWord, cchLeft);
4719         kmk_cc_eval_strip_right_v(pCompiler->paStrCopySegs, &pCompiler->cStrCopySegs, &cchExpr);
4720 
4721         pInstr = (PKMKCCEVALIFEXPR)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, KMKCCEVALIFEXPR_SIZE(cchExpr));
4722         kmk_cc_eval_strcpyv(pInstr->szExpr, pCompiler->paStrCopySegs, pCompiler->cStrCopySegs, cchExpr);
4723         pInstr->cchExpr = cchExpr;
4724         pInstr->IfCore.Core.enmOpcode = kKmkCcEvalInstr_if;
4725         pInstr->IfCore.Core.iLine     = pCompiler->iLine;
4726         kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse);
4727     }
4728     else
4729         kmk_cc_eval_fatal(pCompiler, pchWord, "Expected expression after 'if' directive");
4730     return 1;
4731 }
4732 
4733 
4734 /**
4735  * Deals with 'ifdef var', 'ifndef var', 'else ifdef var' and 'else ifndef var'
4736  * statements.
4737  *
4738  * @returns 1 to indicate we've handled a keyword (see
4739  *          kmk_cc_eval_try_handle_keyword).
4740  * @param   pCompiler       The compiler state.
4741  * @param   pchWord         First char after 'if[n]def'.
4742  * @param   cchLeft         The number of chars left to parse on this line.
4743  * @param   fInElse         Set if this is an 'else if' (rather than just 'if').
4744  * @param   fPositiveStmt   Set if 'ifdef', clear if 'ifndef'.
4745  */
kmk_cc_eval_do_ifdef(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,int fInElse,int fPositiveStmt)4746 static int kmk_cc_eval_do_ifdef(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, int fInElse, int fPositiveStmt)
4747 {
4748     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
4749     if (cchLeft)
4750     {
4751         /*
4752          * Skip to the end of the variable name.
4753          */
4754         unsigned const      iSavedEscEol = pCompiler->iEscEol;
4755         const char * const  pchVarNm     = pchWord;
4756         int                 fPlain;
4757 /** @todo this isn't quite right. It is a variable name, correct. However, it
4758  *        doesn't need to subscribe entirely to the rules of a variable name.
4759  *        Just find the end of the word, taking variable refs into account,
4760  *        and consider it what we need. */
4761         pchWord = kmk_cc_eval_skip_var_name(pCompiler, pchWord, cchLeft, &cchLeft, &fPlain);
4762         KMK_CC_ASSERT(pCompiler->iEscEol == iSavedEscEol || !fPlain);
4763         if (fPlain)
4764         {
4765             size_t const            cchVarNm = pchWord - pchVarNm;
4766             PKMKCCEVALIFDEFPLAIN    pInstr;
4767             pInstr = (PKMKCCEVALIFDEFPLAIN)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
4768             pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_ifdef_plain : kKmkCcEvalInstr_ifndef_plain;
4769             pInstr->IfCore.Core.iLine     = pCompiler->iLine;
4770             pInstr->pszName = strcache2_add(&variable_strcache, pchVarNm, cchVarNm);
4771             kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse);
4772         }
4773         else
4774         {
4775             PKMKCCEVALIFDEFDYNAMIC  pInstr;
4776             size_t const            cchVarNm = pchWord - pchVarNm;
4777             size_t                  cchCopy;
4778             char                   *pszCopy;
4779             pCompiler->iEscEol = iSavedEscEol;
4780             cchCopy = kmk_cc_eval_prep_normal_line(pCompiler, pchVarNm, cchVarNm);
4781 
4782             pInstr = (PKMKCCEVALIFDEFDYNAMIC)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
4783 
4784             /** @todo Make the subprogram embed necessary strings. */
4785             pszCopy = kmk_cc_eval_strdup_prepped(pCompiler, cchCopy);
4786             kmk_cc_block_realign(pCompiler->ppBlockTail);
4787 
4788             pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_ifdef_dynamic : kKmkCcEvalInstr_ifndef_dynamic;
4789             pInstr->IfCore.Core.iLine     = pCompiler->iLine;
4790             kmk_cc_eval_compile_string_exp_subprog(pCompiler, pszCopy, cchCopy, &pInstr->NameSubprog);
4791 
4792             kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse);
4793         }
4794 
4795         /*
4796          * Make sure there is nothing following the variable name.
4797          */
4798         KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
4799         if (cchLeft)
4800             kmk_cc_eval_fatal(pCompiler, pchWord, "Bogus stuff after 'if%sdef' variable name", fPositiveStmt ? "" : "n");
4801     }
4802     else
4803         kmk_cc_eval_fatal(pCompiler, pchWord, "Expected expression after 'if' directive");
4804     return 1;
4805 }
4806 
4807 
4808 /**
4809  * Deals with 'ifeq (a,b)', 'ifeq "a" "b"', 'ifneq (a,b)', 'ifneq "a" "b"',
4810  * 'else ifeq (a,b)', 'else ifeq "a" "b"', 'else ifneq (a,b)' and
4811  * 'else ifneq "a" "b"' statements.
4812  *
4813  * @returns 1 to indicate we've handled a keyword (see
4814  *          kmk_cc_eval_try_handle_keyword).
4815  * @param   pCompiler       The compiler state.
4816  * @param   pchWord         First char after 'if[n]eq'.
4817  * @param   cchLeft         The number of chars left to parse on this line.
4818  * @param   fInElse         Set if this is an 'else if' (rather than just 'if').
4819  * @param   fPositiveStmt   Set if 'ifeq', clear if 'ifneq'.
4820  */
kmk_cc_eval_do_ifeq(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,int fInElse,int fPositiveStmt)4821 static int kmk_cc_eval_do_ifeq(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, int fInElse, int fPositiveStmt)
4822 {
4823     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
4824     if (cchLeft)
4825     {
4826         /*
4827          * There are two forms:
4828          *
4829          *   ifeq (string1, string2)
4830          *   ifeq "string1" 'string2'
4831          *
4832          */
4833         const char * const  pchEnd = &pchWord[cchLeft];
4834         PKMKCCEVALIFEQ      pInstr = (PKMKCCEVALIFEQ)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
4835 
4836         struct
4837         {
4838             char           *pszCopy;
4839             size_t          cchCopy;
4840             int             fPlain;
4841         } Left, Right;
4842 
4843         char ch = *pchWord;
4844         if (ch == '(')
4845         {
4846             int      cCounts;
4847             size_t   off;
4848 
4849             /*
4850              * The left side ends with a comma.  We respect parentheses, but
4851              * not curly brackets.
4852              */
4853 
4854             /* Skip the parenthesis. */
4855             pchWord++;
4856             cchLeft--;
4857 
4858             /* Find the comma, checking for non-plainness. */
4859             cCounts = 0;
4860             Left.fPlain = 1;
4861             for (off = 0; off < cchLeft; off++)
4862             {
4863                 ch = pchWord[off];
4864                 if (!KMK_CC_EVAL_IS_PAREN_COMMA_OR_DOLLAR(ch))
4865                 { /* likely */ }
4866                 else if (ch == '$')
4867                     Left.fPlain = 0;
4868                 else if (ch == '(')
4869                     cCounts++;
4870                 else if (ch == ')')
4871                     cCounts--; /** @todo warn if it goes negative. */
4872                 else if (ch == ',' && cCounts == 0)
4873                     break;
4874                 else
4875                     KMK_CC_ASSERT(cCounts > 0);
4876             }
4877             if (ch == ',' && cCounts == 0) { /* likely */ }
4878             else kmk_cc_eval_fatal(pCompiler, &pchWord[off], "Expected ',' before end of line");
4879 
4880             /* Copy out the string. */
4881             Left.cchCopy = kmk_cc_eval_prep_normal_line_ex(pCompiler, pchWord, off);
4882             kmk_cc_eval_strip_right_v(pCompiler->paStrCopySegs, &pCompiler->cStrCopySegs, &Left.cchCopy);
4883             Left.pszCopy = kmk_cc_eval_strdup_prepped(pCompiler, Left.cchCopy);
4884 
4885             /* Skip past the comma and any following spaces. */
4886             pchWord += off + 1;
4887             cchLeft -= off + 1;
4888             if (   cchLeft /** @todo replace with straight 'isspace' that takes escaped EOLs into account. */
4889                 && KMK_CC_EVAL_IS_SPACE_AFTER_WORD(pCompiler, pchWord[0], pchWord[1], cchLeft))
4890                 KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
4891 
4892             /*
4893              * Ditto for the right side, only it ends with a closing parenthesis.
4894              */
4895             cCounts = 1;
4896             Right.fPlain = 1;
4897             for (off = 0; off < cchLeft; off++)
4898             {
4899                 ch = pchWord[off];
4900                 if (!KMK_CC_EVAL_IS_PAREN_COMMA_OR_DOLLAR(ch))
4901                 { /* likely */ }
4902                 else if (ch == '$')
4903                     Right.fPlain = 0;
4904                 else if (ch == '(')
4905                     cCounts++;
4906                 else if (ch == ')')
4907                 {
4908                     if (--cCounts == 0)
4909                         break;
4910                 }
4911                 else
4912                     KMK_CC_ASSERT(cCounts > 0 || ch == ',');
4913             }
4914             if (ch == ')' && cCounts == 0) { /* likely */ }
4915             else kmk_cc_eval_fatal(pCompiler, &pchWord[off], "Expected ')' before end of line");
4916 
4917             /* Copy out the string. */
4918             Right.cchCopy = kmk_cc_eval_prep_normal_line_ex(pCompiler, pchWord, off);
4919             kmk_cc_eval_strip_right_v(pCompiler->paStrCopySegs, &pCompiler->cStrCopySegs, &Right.cchCopy);
4920             Right.pszCopy = kmk_cc_eval_strdup_prepped(pCompiler, Right.cchCopy);
4921 
4922             /* Skip past the parenthesis. */
4923             pchWord += off + 1;
4924             cchLeft -= off + 1;
4925         }
4926         else if (ch == '"' || ch == '\'')
4927         {
4928             const char *pchTmp;
4929 
4930             /*
4931              * Quoted left side.
4932              */
4933             /* Skip leading quote. */
4934             pchWord++;
4935             cchLeft--;
4936 
4937             /* Locate the end quote. */
4938             pchTmp = (const char *)memchr(pchWord, ch, cchLeft);
4939             if (pchTmp) { /* likely */ }
4940             else kmk_cc_eval_fatal(pCompiler, pchWord - 1, "Unbalanced quote in first if%seq string", fPositiveStmt ? "" : "n");
4941 
4942             Left.cchCopy = kmk_cc_eval_prep_normal_line_ex(pCompiler, pchWord, pchTmp - pchWord);
4943             Left.pszCopy = kmk_cc_eval_strdup_prepped(pCompiler, Left.cchCopy);
4944             Left.fPlain  = memchr(Left.pszCopy, '$', Left.cchCopy) == NULL;
4945 
4946             /* skip end quote */
4947             pchWord  = pchTmp + 1;
4948             cchLeft  = pchEnd - pchWord;
4949 
4950             /* Skip anything inbetween the left and right hand side (not mandatory). */
4951             if (   cchLeft /** @todo replace with straight 'isspace' that takes escaped EOLs into account. */
4952                 && KMK_CC_EVAL_IS_SPACE_AFTER_WORD(pCompiler, pchWord[0], pchWord[1], cchLeft))
4953                 KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
4954 
4955             /*
4956              * Quoted right side.
4957              */
4958             if (   cchLeft > 0
4959                 && ( (ch = *pchWord) != '"' || ch == '\'') )
4960             {
4961                 /* Skip leading quote. */
4962                 pchWord++;
4963                 cchLeft--;
4964 
4965                 /* Locate the end quote. */
4966                 pchTmp = (const char *)memchr(pchWord, ch, cchLeft);
4967                 if (pchTmp) { /* likely */ }
4968                 else kmk_cc_eval_fatal(pCompiler, pchWord - 1, "Unbalanced quote in second if%seq string", fPositiveStmt ? "" : "n");
4969 
4970                 Right.cchCopy = kmk_cc_eval_prep_normal_line_ex(pCompiler, pchWord, pchTmp - pchWord);
4971                 Right.pszCopy = kmk_cc_eval_strdup_prepped(pCompiler, Right.cchCopy);
4972                 Right.fPlain  = memchr(Right.pszCopy, '$', Right.cchCopy) == NULL;
4973 
4974                 /* skip end quote */
4975                 pchWord  = pchTmp + 1;
4976                 cchLeft  = pchEnd - pchWord;
4977             }
4978             else
4979                 kmk_cc_eval_fatal(pCompiler, pchWord, "Expected a second quoted string for 'if%seq'",
4980                                   fPositiveStmt ? "" : "n");
4981         }
4982         else
4983             kmk_cc_eval_fatal(pCompiler, pchWord, "Expected parentheses or quoted string after 'if%seq'",
4984                               fPositiveStmt ? "" : "n");
4985         kmk_cc_block_realign(pCompiler->ppBlockTail);
4986 
4987         /*
4988          * Initialize the instruction.
4989          */
4990         pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_ifeq : kKmkCcEvalInstr_ifneq;
4991         pInstr->IfCore.Core.iLine     = pCompiler->iLine;
4992         kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Left, Left.pszCopy, Left.cchCopy, Left.fPlain);
4993         kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Right, Right.pszCopy, Right.cchCopy, Right.fPlain);
4994         kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse);
4995 
4996         /*
4997          * Make sure there is nothing following the variable name.
4998          */
4999         KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5000         if (cchLeft)
5001             kmk_cc_eval_fatal(pCompiler, pchWord, "Bogus stuff after 'if%sdef' variable name", fPositiveStmt ? "" : "n");
5002     }
5003     else
5004         kmk_cc_eval_fatal(pCompiler, pchWord, "Expected expression after 'if' directive");
5005     return 1;
5006 }
5007 
5008 
5009 /**
5010  * Deals with 'if1of (set-a,set-b)', 'ifn1of (set-a,set-b)',
5011  * 'else if1of (set-a,set-b)' and 'else ifn1of (set-a,set-b)' statements.
5012  *
5013  * @returns 1 to indicate we've handled a keyword (see
5014  *          kmk_cc_eval_try_handle_keyword).
5015  * @param   pCompiler       The compiler state.
5016  * @param   pchWord         First char after 'if[n]1of'.
5017  * @param   cchLeft         The number of chars left to parse on this line.
5018  * @param   fInElse         Set if this is an 'else if' (rather than just 'if').
5019  * @param   fPositiveStmt   Set if 'if1of', clear if 'ifn1of'.
5020  */
kmk_cc_eval_do_if1of(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,int fInElse,int fPositiveStmt)5021 static int kmk_cc_eval_do_if1of(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, int fInElse, int fPositiveStmt)
5022 {
5023     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5024     if (cchLeft)
5025     {
5026         /*
5027          * This code is (currently) very similar to kmk_cc_eval_do_ifeq.
5028          * However, we may want to add hashing optimizations of plain text,
5029          * and we don't want to support the quoted form as it is not necessary
5030          * and may interfere with support for quoted words later on.
5031          */
5032         PKMKCCEVALIF1OF     pInstr = (PKMKCCEVALIF1OF)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
5033 
5034         struct
5035         {
5036             char           *pszCopy;
5037             size_t          cchCopy;
5038             int             fPlain;
5039         } Left, Right;
5040 
5041         char ch = *pchWord;
5042         if (ch == '(')
5043         {
5044             int      cCounts;
5045             size_t   off;
5046 
5047             /*
5048              * The left side ends with a comma.  We respect parentheses, but
5049              * not curly brackets.
5050              */
5051 
5052             /* Skip the parenthesis. */
5053             pchWord++;
5054             cchLeft--;
5055 
5056             /* Find the comma, checking for non-plainness. */
5057             cCounts = 0;
5058             Left.fPlain = 1;
5059             for (off = 0; off < cchLeft; off++)
5060             {
5061                 ch = pchWord[off];
5062                 if (!KMK_CC_EVAL_IS_PAREN_COMMA_OR_DOLLAR(ch))
5063                 { /* likely */ }
5064                 else if (ch == '$')
5065                     Left.fPlain = 0;
5066                 else if (ch == '(')
5067                     cCounts++;
5068                 else if (ch == ')')
5069                     cCounts--; /** @todo warn if it goes negative. */
5070                 else if (ch == ',' && cCounts == 0)
5071                     break;
5072                 else
5073                     KMK_CC_ASSERT(cCounts > 0);
5074             }
5075             if (ch == ',' && cCounts == 0) { /* likely */ }
5076             else kmk_cc_eval_fatal(pCompiler, &pchWord[off], "Expected ',' before end of line");
5077 
5078             /* Copy out the string. */
5079             Left.cchCopy = kmk_cc_eval_prep_normal_line_ex(pCompiler, pchWord, off);
5080             kmk_cc_eval_strip_right_v(pCompiler->paStrCopySegs, &pCompiler->cStrCopySegs, &Left.cchCopy);
5081             Left.pszCopy = kmk_cc_eval_strdup_prepped(pCompiler, Left.cchCopy);
5082 
5083             /* Skip past the comma and any following spaces. */
5084             pchWord += off + 1;
5085             cchLeft -= off + 1;
5086             if (   cchLeft /** @todo replace with straight 'isspace' that takes escaped EOLs into account. */
5087                 && KMK_CC_EVAL_IS_SPACE_AFTER_WORD(pCompiler, pchWord[0], pchWord[1], cchLeft))
5088                 KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5089 
5090             /*
5091              * Ditto for the right side, only it ends with a closing parenthesis.
5092              */
5093             cCounts = 1;
5094             Right.fPlain = 1;
5095             for (off = 0; off < cchLeft; off++)
5096             {
5097                 ch = pchWord[off];
5098                 if (!KMK_CC_EVAL_IS_PAREN_COMMA_OR_DOLLAR(ch))
5099                 { /* likely */ }
5100                 else if (ch == '$')
5101                     Right.fPlain = 0;
5102                 else if (ch == '(')
5103                     cCounts++;
5104                 else if (ch == ')')
5105                 {
5106                     if (--cCounts == 0)
5107                         break;
5108                 }
5109                 else
5110                     KMK_CC_ASSERT(cCounts > 0 || ch == ',');
5111             }
5112             if (ch == ')' && cCounts == 0) { /* likely */ }
5113             else kmk_cc_eval_fatal(pCompiler, &pchWord[off], "Expected ')' before end of line");
5114 
5115             /* Copy out the string. */
5116             Right.cchCopy = kmk_cc_eval_prep_normal_line_ex(pCompiler, pchWord, off);
5117             kmk_cc_eval_strip_right_v(pCompiler->paStrCopySegs, &pCompiler->cStrCopySegs, &Right.cchCopy);
5118             Right.pszCopy = kmk_cc_eval_strdup_prepped(pCompiler, Right.cchCopy);
5119 
5120             /* Skip past the parenthesis. */
5121             pchWord += off + 1;
5122             cchLeft -= off + 1;
5123         }
5124         else
5125             kmk_cc_eval_fatal(pCompiler, pchWord, "Expected parentheses after 'if%s1of'", fPositiveStmt ? "" : "n");
5126         kmk_cc_block_realign(pCompiler->ppBlockTail);
5127 
5128         /*
5129          * Initialize the instruction.
5130          */
5131         pInstr->IfCore.Core.enmOpcode = fPositiveStmt ? kKmkCcEvalInstr_if1of : kKmkCcEvalInstr_ifn1of;
5132         pInstr->IfCore.Core.iLine     = pCompiler->iLine;
5133         kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Left, Left.pszCopy, Left.cchCopy, Left.fPlain);
5134         kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Right, Right.pszCopy, Right.cchCopy, Right.fPlain);
5135         kmk_cc_eval_do_if_core(pCompiler, &pInstr->IfCore, fInElse);
5136 
5137         /*
5138          * Make sure there is nothing following the variable name.
5139          */
5140         KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5141         if (cchLeft)
5142             kmk_cc_eval_fatal(pCompiler, pchWord, "Bogus stuff after 'if%s1of' variable name", fPositiveStmt ? "" : "n");
5143     }
5144     else
5145         kmk_cc_eval_fatal(pCompiler, pchWord, "Expected expression after 'if' directive");
5146     return 1;
5147 }
5148 
5149 
5150 /**
5151  * Deals with 'else' and 'else ifxxx' statements.
5152  *
5153  * @returns 1 to indicate we've handled a keyword (see
5154  *          kmk_cc_eval_try_handle_keyword).
5155  * @param   pCompiler   The compiler state.
5156  * @param   pchWord     First char after 'define'.
5157  * @param   cchLeft     The number of chars left to parse on this line.
5158  */
kmk_cc_eval_do_else(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft)5159 static int kmk_cc_eval_do_else(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
5160 {
5161     /*
5162      * There must be an 'if' on the stack.
5163      */
5164     unsigned iIf = pCompiler->cIfs;
5165     if (iIf > 0)
5166     {
5167         PKMKCCEVALIFCORE pIfCore = pCompiler->apIfs[--iIf];
5168         if (!pIfCore->pTrueEndJump)
5169         {
5170             /* Emit a jump instruction that will take us from the 'True' block to the 'endif'. */
5171             PKMKCCEVALJUMP  pInstr = (PKMKCCEVALJUMP)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
5172             pInstr->Core.enmOpcode = kKmkCcEvalInstr_jump;
5173             pInstr->Core.iLine     = pCompiler->iLine;
5174             pInstr->pNext          = NULL;
5175             pIfCore->pTrueEndJump  = pInstr;
5176 
5177             /* The next instruction is the first in the 'False' block of the current 'if'.
5178                Should this be an 'else if', this will be the 'if' instruction emitted below. */
5179             pIfCore->pNextFalse    = (PKMKCCEVALCORE)kmk_cc_block_get_next_ptr(*pCompiler->ppBlockTail);
5180         }
5181         else if (iIf == 0)
5182             kmk_cc_eval_fatal(pCompiler, pchWord, "2nd 'else' for 'if' at line %u", pIfCore->Core.iLine);
5183         else
5184             kmk_cc_eval_fatal(pCompiler, pchWord, "2nd 'else' in a row - missing 'endif' for 'if' at line %u?",
5185                               pIfCore->Core.iLine);
5186     }
5187     else
5188         kmk_cc_eval_fatal(pCompiler, pchWord, "'else' without 'if'");
5189 
5190     /*
5191      * Check for 'else ifxxx'. There can be nothing else following an else.
5192      */
5193     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5194     if (cchLeft)
5195     {
5196         if (   cchLeft > 2
5197             && KMK_CC_WORD_COMP_CONST_2(pchWord, "if"))
5198         {
5199             pchWord += 2;
5200             cchLeft -= 2;
5201 
5202             if (KMK_CC_EVAL_WORD_COMP_IS_EOL(pCompiler, pchWord, cchLeft))
5203                 return kmk_cc_eval_do_if(pCompiler, pchWord, cchLeft, 1 /* in else */);
5204 
5205             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "eq", 2))
5206                 return kmk_cc_eval_do_ifeq( pCompiler, pchWord + 2, cchLeft - 2, 1 /* in else */, 1 /* positive */);
5207 
5208             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "def", 3))
5209                 return kmk_cc_eval_do_ifdef(pCompiler, pchWord + 3, cchLeft - 3, 1 /* in else */, 1 /* positive */);
5210 
5211             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "neq", 3))
5212                 return kmk_cc_eval_do_ifeq( pCompiler, pchWord + 3, cchLeft - 3, 1 /* in else */, 0 /* positive */);
5213 
5214             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "1of", 3))
5215                 return kmk_cc_eval_do_if1of(pCompiler, pchWord + 3, cchLeft - 3, 1 /* in else */, 1 /* positive */);
5216 
5217             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "ndef", 4))
5218                 return kmk_cc_eval_do_ifdef(pCompiler, pchWord + 4, cchLeft - 4, 1 /* in else */, 0 /* positive */);
5219 
5220             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "n1of", 4))
5221                 return kmk_cc_eval_do_if1of(pCompiler, pchWord + 4, cchLeft - 4, 1 /* in else */, 0 /* positive */);
5222 
5223             pchWord -= 2;
5224             cchLeft += 2;
5225         }
5226         kmk_cc_eval_fatal(pCompiler, pchWord, "Bogus stuff after 'else'");
5227     }
5228 
5229     return 1;
5230 }
5231 
5232 
5233 /**
5234  * Deals with the 'endif' statement.
5235  *
5236  * @returns 1 to indicate we've handled a keyword (see
5237  *          kmk_cc_eval_try_handle_keyword).
5238  * @param   pCompiler   The compiler state.
5239  * @param   pchWord     First char after 'define'.
5240  * @param   cchLeft     The number of chars left to parse on this line.
5241  */
kmk_cc_eval_do_endif(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft)5242 static int kmk_cc_eval_do_endif(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
5243 {
5244     /*
5245      * There must be an 'if' on the stack.  We'll POP it.
5246      */
5247     unsigned iIf = pCompiler->cIfs;
5248     if (iIf > 0)
5249     {
5250         PKMKCCEVALCORE   pNextInstr;
5251         PKMKCCEVALIFCORE pIfCore = pCompiler->apIfs[--iIf];
5252         pCompiler->cIfs = iIf; /* POP! */
5253 
5254         /* Update the jump targets for all IFs at this level. */
5255         pNextInstr = (PKMKCCEVALCORE)kmk_cc_block_get_next_ptr(*pCompiler->ppBlockTail);
5256         do
5257         {
5258             if (pIfCore->pTrueEndJump)
5259             {
5260                 /* Make the true block jump here, to the 'endif'. The false block is already here. */
5261                 pIfCore->pTrueEndJump->pNext = pNextInstr;
5262                 KMK_CC_ASSERT(pIfCore->pNextFalse);
5263             }
5264             else
5265             {
5266                 /* No 'else'. The false-case jump here, to the 'endif'. */
5267                 KMK_CC_ASSERT(!pIfCore->pNextFalse);
5268                 pIfCore->pNextFalse = pNextInstr;
5269             }
5270 
5271             pIfCore = pIfCore->pPrevCond;
5272         } while (pIfCore);
5273     }
5274     else
5275         kmk_cc_eval_fatal(pCompiler, pchWord, "'endif' without 'if'");
5276 
5277     /*
5278      * There shouldn't be anything trailing an 'endif'.
5279      */
5280     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5281     if (!cchLeft) { /* likely */ }
5282     else kmk_cc_eval_fatal(pCompiler, pchWord, "Bogus stuff after 'else'");
5283 
5284     return 1;
5285 }
5286 
5287 
5288 /**
5289  * Parses a 'include file...', 'sinclude file...', '-include file...',
5290  * 'includedep file...', 'includedep-queue file...' and
5291  * 'includedep-flush file...'
5292  *
5293  * @returns 1 to indicate we've handled a keyword (see
5294  *          kmk_cc_eval_try_handle_keyword).
5295  * @param   pCompiler   The compiler state.
5296  * @param   pchWord     First char after the include directive.
5297  * @param   cchLeft     The number of chars left to parse on this line.
5298  * @param   enmOpcode   The opcode for the include directive we're parsing.
5299  */
kmk_cc_eval_do_include(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,KMKCCEVALINSTR enmOpcode)5300 static int kmk_cc_eval_do_include(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, KMKCCEVALINSTR enmOpcode)
5301 {
5302     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5303     if (cchLeft)
5304     {
5305         /*
5306          * Split what's left up into words.
5307          */
5308         unsigned cWords = kmk_cc_eval_parse_words(pCompiler, pchWord, cchLeft);
5309         KMK_CC_EVAL_DPRINTF(("%s: cWords=%d\n", g_apszEvalInstrNms[enmOpcode], cWords));
5310         if (cWords)
5311         {
5312             PKMKCCEVALINCLUDE pInstr = (PKMKCCEVALINCLUDE)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail,
5313                                                                                   KMKCCEVALINCLUDE_SIZE(cWords));
5314             pInstr->Core.enmOpcode = enmOpcode;
5315             pInstr->Core.iLine     = pCompiler->iLine;
5316             pInstr->cFiles         = cWords;
5317             kmk_cc_eval_init_spp_array_from_duplicated_words(pCompiler, cWords, pCompiler->paWords, pInstr->aFiles);
5318             kmk_cc_block_realign(pCompiler->ppBlockTail);
5319         }
5320         else
5321             KMK_CC_ASSERT(0);
5322     }
5323     else
5324         KMK_CC_EVAL_DPRINTF(("%s: include without args\n", g_apszEvalInstrNms[enmOpcode]));
5325     return 1;
5326 }
5327 
5328 
kmk_cc_eval_do_vpath(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft)5329 static int kmk_cc_eval_do_vpath(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
5330 {
5331     kmk_cc_eval_fatal(pCompiler, NULL, "vpath directive is not implemented\n");
5332     return 1;
5333 }
5334 
5335 
kmk_cc_eval_handle_command(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft)5336 static void kmk_cc_eval_handle_command(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
5337 {
5338     kmk_cc_eval_fatal(pCompiler, pchWord, "command handling not implemented yet");
5339 }
5340 
5341 
kmk_cc_eval_handle_recipe_cont_colon(PKMKCCEVALCOMPILER pCompiler,const char * pchWord0,size_t cchWord0,const char * pchColon,size_t cchLeft,unsigned fQualifiers)5342 static int kmk_cc_eval_handle_recipe_cont_colon(PKMKCCEVALCOMPILER pCompiler, const char *pchWord0, size_t cchWord0,
5343                                                 const char *pchColon, size_t cchLeft, unsigned fQualifiers)
5344 {
5345     kmk_cc_eval_fatal(pCompiler, pchWord0, "recipe handling not implemented yet (#1)");
5346     return 1;
5347 }
5348 
5349 
kmk_cc_eval_handle_recipe_cont_2nd_word(PKMKCCEVALCOMPILER pCompiler,const char * pchWord0,size_t cchWord0,const char * pchWord,size_t cchLeft,unsigned fQualifiers)5350 static int kmk_cc_eval_handle_recipe_cont_2nd_word(PKMKCCEVALCOMPILER pCompiler, const char *pchWord0, size_t cchWord0,
5351                                                    const char *pchWord, size_t cchLeft, unsigned fQualifiers)
5352 {
5353     kmk_cc_eval_fatal(pCompiler, pchWord, "recipe handling not implemented yet (#2)");
5354     return 1;
5355 }
5356 
5357 
kmk_cc_eval_handle_recipe(PKMKCCEVALCOMPILER pCompiler,const char * pszEqual,const char * pchWord,size_t cchLeft)5358 static void kmk_cc_eval_handle_recipe(PKMKCCEVALCOMPILER pCompiler, const char *pszEqual, const char *pchWord, size_t cchLeft)
5359 {
5360     kmk_cc_eval_fatal(pCompiler, pchWord, "recipe handling not implemented yet (#3)");
5361 }
5362 
kmk_cc_eval_end_of_recipe(PKMKCCEVALCOMPILER pCompiler)5363 static void kmk_cc_eval_end_of_recipe(PKMKCCEVALCOMPILER pCompiler)
5364 {
5365     if (pCompiler->pRecipe)
5366     {
5367         /** @todo do stuff here. */
5368     }
5369 }
5370 
5371 
5372 /**
5373  * Common worker for handling export (non-assign), undefine and unexport.
5374  *
5375  * For instructions using the KMKCCEVALVARIABLES structure.
5376  *
5377  * @returns 1 to indicate we've handled a keyword (see
5378  *          kmk_cc_eval_try_handle_keyword).
5379  * @param   pCompiler   The compiler state.
5380  * @param   pchWord     First non-space chare after the keyword.
5381  * @param   cchLeft     The number of chars left to parse on this line.
5382  * @param   fQualifiers The qualifiers.
5383  */
kmk_cc_eval_do_with_variable_list(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,KMKCCEVALINSTR enmOpcode,unsigned fQualifiers)5384 static int kmk_cc_eval_do_with_variable_list(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft,
5385                                              KMKCCEVALINSTR enmOpcode, unsigned fQualifiers)
5386 {
5387     if (cchLeft)
5388     {
5389         /*
5390          * Parse the variable name list.  GNU make is using normal word
5391          * handling here, so we can share code with the include directives.
5392          */
5393         unsigned cWords = kmk_cc_eval_parse_words(pCompiler, pchWord, cchLeft);
5394         KMK_CC_EVAL_DPRINTF(("%s: cWords=%d\n", g_apszEvalInstrNms[enmOpcode], cWords));
5395         if (cWords)
5396         {
5397             PKMKCCEVALVARIABLES pInstr = (PKMKCCEVALVARIABLES)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail,
5398                                                                                       KMKCCEVALVARIABLES_SIZE(cWords));
5399             pInstr->Core.enmOpcode = enmOpcode;
5400             pInstr->Core.iLine     = pCompiler->iLine;
5401             pInstr->cVars         = cWords;
5402             kmk_cc_eval_init_spp_array_from_duplicated_words(pCompiler, cWords, pCompiler->paWords, pInstr->aVars);
5403             kmk_cc_block_realign(pCompiler->ppBlockTail);
5404         }
5405         else
5406             KMK_CC_ASSERT(0);
5407     }
5408     /* else: NOP */
5409     return 1;
5410 }
5411 
5412 
5413 /**
5414  * Parses a '[qualifiers] undefine variable [..]' expression.
5415  *
5416  * A 'undefine' directive is final, any qualifiers must preceed it.  So, we just
5417  * have to extract the variable names now.
5418  *
5419  * @returns 1 to indicate we've handled a keyword (see
5420  *          kmk_cc_eval_try_handle_keyword).
5421  * @param   pCompiler   The compiler state.
5422  * @param   pchWord     First char after 'define'.
5423  * @param   cchLeft     The number of chars left to parse on this line.
5424  * @param   fQualifiers The qualifiers.
5425  */
kmk_cc_eval_do_var_undefine(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,unsigned fQualifiers)5426 static int kmk_cc_eval_do_var_undefine(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, unsigned fQualifiers)
5427 {
5428     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5429     if (!cchLeft)
5430         kmk_cc_eval_fatal(pCompiler, pchWord, "undefine requires a variable name");
5431 
5432     /** @todo GNU make doesn't actually do the list thing for undefine, it seems
5433      *        to assume everything after it is a single variable...  Going with
5434      *        simple common code for now. */
5435     return kmk_cc_eval_do_with_variable_list(pCompiler, pchWord, cchLeft, kKmkCcEvalInstr_undefine, fQualifiers);
5436 }
5437 
5438 
5439 /**
5440  * Parses a '[qualifiers] unexport variable [..]' expression.
5441  *
5442  * A 'unexport' directive is final, any qualifiers must preceed it.  So, we just
5443  * have to extract the variable names now.
5444  *
5445  * @returns 1 to indicate we've handled a keyword (see
5446  *          kmk_cc_eval_try_handle_keyword).
5447  * @param   pCompiler   The compiler state.
5448  * @param   pchWord     First char after 'define'.
5449  * @param   cchLeft     The number of chars left to parse on this line.
5450  * @param   fQualifiers The qualifiers.
5451  */
kmk_cc_eval_do_var_unexport(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,unsigned fQualifiers)5452 static int kmk_cc_eval_do_var_unexport(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, unsigned fQualifiers)
5453 {
5454     PKMKCCEVALCORE pInstr;
5455 
5456     /*
5457      * Join paths with undefine and export, unless it's an unexport all directive.
5458      */
5459     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5460     if (cchLeft)
5461         return kmk_cc_eval_do_with_variable_list(pCompiler, pchWord, cchLeft, kKmkCcEvalInstr_unexport, fQualifiers);
5462 
5463     /*
5464      * We're unexporting all variables.
5465      */
5466     pInstr = kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
5467     pInstr->enmOpcode = kKmkCcEvalInstr_unexport_all;
5468     pInstr->iLine     = pCompiler->iLine;
5469     return 1;
5470 }
5471 
5472 
5473 /**
5474  * Parses a 'define variable' expression.
5475  *
5476  * A 'define' directive is final, any qualifiers must preceed it.  So, we just
5477  * have to extract the variable name now, well and find the corresponding
5478  * 'endef'.
5479  *
5480  * @returns 1 to indicate we've handled a keyword (see
5481  *          kmk_cc_eval_try_handle_keyword).
5482  * @param   pCompiler   The compiler state.
5483  * @param   pchWord     First char after 'define'.
5484  * @param   cchLeft     The number of chars left to parse on this line.
5485  * @param   fQualifiers The qualifiers.
5486  */
kmk_cc_eval_do_var_define(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,unsigned fQualifiers)5487 static int kmk_cc_eval_do_var_define(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft, unsigned fQualifiers)
5488 {
5489 
5490     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5491     kmk_cc_eval_fatal(pCompiler, pchWord, "define handling not implemented yet");
5492     return 1;
5493 }
5494 
5495 
kmk_cc_eval_handle_assignment_or_recipe(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,unsigned fQualifiers)5496 static int kmk_cc_eval_handle_assignment_or_recipe(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft,
5497                                                    unsigned fQualifiers)
5498 {
5499     /*
5500      * We're currently at a word which may or may not be a variable name
5501      * followed by an assignment operator, alternatively it must be a recipe.
5502      * We need to figure this out and deal with it in the most efficient
5503      * manner as this is a very common occurence.
5504      */
5505     unsigned const  iEscEolVarNm = pCompiler->iEscEol;
5506     int             fPlainVarNm  = 1;
5507     const char     *pchVarNm     = pchWord;
5508     size_t          cchVarNm;
5509     size_t          cch = 0;
5510     char            ch;
5511 
5512     /*
5513      * The variable name.  Complicate by there being no requirement of a space
5514      * preceeding the assignment operator, as well as that the variable name
5515      * may include variable references with spaces (function++) in them.
5516      */
5517     for (;;)
5518     {
5519         if (cch < cchLeft)
5520         { /*likely*/ }
5521         else
5522             kmk_cc_eval_fatal(pCompiler, &pchWord[cch], "Neither recipe nor variable assignment");
5523 
5524         ch = pchWord[cch];
5525         if (!KMK_CC_EVAL_IS_SPACE_DOLLAR_SLASH_OR_ASSIGN(ch))
5526             cch++;
5527         /* Space? */
5528         else if (KMK_CC_EVAL_IS_SPACE(ch))
5529         {
5530             cchVarNm = cch;
5531             pchWord += cch;
5532             cchLeft -= cch;
5533             KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5534             break;
5535         }
5536         /* Variable expansion may contain spaces, so handle specially. */
5537         else if (ch == '$')
5538         {
5539             cch++;
5540             if (cch < cchLeft)
5541             {
5542                 char const chOpen = pchWord[cch];
5543                 if (chOpen == '(' || chOpen == '{')
5544                 {
5545                     /*
5546                      * Got a $(VAR) or ${VAR} to deal with here.  This may
5547                      * include nested variable references and span multiple
5548                      * lines (at least for function calls).
5549                      *
5550                      * We scan forward till we've found the corresponding
5551                      * closing parenthesis, considering any open parentheses
5552                      * of the same kind as worth counting, even if there are
5553                      * no dollar preceeding them, just like GNU make does.
5554                      */
5555                     size_t const cchStart = cch - 1;
5556                     char const   chClose  = chOpen == '(' ? ')' : '}';
5557                     unsigned     cOpen    = 1;
5558                     cch++;
5559                     for (;;)
5560                     {
5561                         if (cch < cchLeft)
5562                         {
5563                             ch = pchWord[cch];
5564                             if (!(KMK_CC_EVAL_IS_PAREN_OR_SLASH(ch)))
5565                                 cch++;
5566                             else
5567                             {
5568                                 cch++;
5569                                 if (ch == chClose)
5570                                 {
5571                                     if (--cOpen == 0)
5572                                         break;
5573                                 }
5574                                 else if (ch == chOpen)
5575                                     cOpen++;
5576                                 else if (   ch == '\\'
5577                                          && pCompiler->iEscEol < pCompiler->cEscEols
5578                                          &&    (size_t)(&pchWord[cch] - pCompiler->pszContent)
5579                                             == pCompiler->paEscEols[pCompiler->iEscEol].offEsc)
5580                                 {
5581                                     cch += pCompiler->paEscEols[pCompiler->iEscEol].offEol
5582                                          - pCompiler->paEscEols[pCompiler->iEscEol].offEsc
5583                                          + pCompiler->cchEolSeq;
5584                                     pCompiler->iEscEol++;
5585                                 }
5586                             }
5587                         }
5588                         else if (cOpen == 1)
5589                             kmk_cc_eval_fatal(pCompiler, &pchWord[cchStart], "Variable reference is missing '%c'", chClose);
5590                         else
5591                             kmk_cc_eval_fatal(pCompiler, &pchWord[cchStart],
5592                                               "%u variable references are missing '%c'", cOpen, chClose);
5593                     }
5594                 }
5595                 /* Single char variable name. */
5596                 else if (!KMK_CC_EVAL_IS_SPACE(chOpen))
5597                 {  /* likely */ }
5598                 else
5599                     kmk_cc_eval_fatal(pCompiler, &pchWord[cch], "Expected variable name after '$', not end of line");
5600             }
5601             else
5602                 kmk_cc_eval_fatal(pCompiler, &pchWord[cch], "Neither recipe nor variable assignment");
5603             fPlainVarNm = 0;
5604         }
5605         /* Check out potential recipe. */
5606         else if (ch == ':')
5607         {
5608             if (   cch + 1 < cchLeft
5609                 && pchWord[cch + 1] != '=')
5610             {
5611                 cchVarNm = cch;
5612                 pchWord += cch;
5613                 cchLeft -= cch;
5614                 break;
5615             }
5616 #ifdef HAVE_DOS_PATHS
5617             /* Don't confuse the first colon in:
5618                     C:/Windows/System32/Kernel32.dll: C:/Windows/System32/NtDll.dll
5619                for a recipe, it is only the second one which counts. */
5620             else if (   cch == 1
5621                      && isalpha((unsigned char)pchWord[0]))
5622                 cch++;
5623 #endif
5624             else
5625                 return kmk_cc_eval_handle_recipe_cont_colon(pCompiler, pchWord, cch, pchWord + cch, cchLeft - cch, fQualifiers);
5626         }
5627         /* Check out assignment operator. */
5628         else if (ch == '=')
5629         {
5630             if (cch)
5631             {
5632                 char chPrev = pchWord[cch - 1];
5633                 if (chPrev == ':' || chPrev == '+' || chPrev == '?' || chPrev == '<')
5634                     cch--;
5635                 cchVarNm = cch;
5636                 pchWord += cch;
5637                 cchLeft -= cch;
5638                 break;
5639             }
5640             else
5641                 kmk_cc_eval_fatal(pCompiler, pchWord, "Empty variable name.");
5642         }
5643         /* Check out potential escaped EOL sequence. */
5644         else if (ch == '\\')
5645         {
5646             unsigned const iEscEol = pCompiler->iEscEol;
5647             if (iEscEol >= pCompiler->cEscEols)
5648                 cch++;
5649             else
5650             {
5651                 size_t offCur = &pchWord[cch] - pCompiler->pszContent;
5652                 if (offCur < pCompiler->paEscEols[iEscEol].offEol)
5653                     cch++;
5654                 else
5655                 {
5656                     cchVarNm = cch;
5657                     KMK_CC_ASSERT(offCur == pCompiler->paEscEols[iEscEol].offEol);
5658                     cch = pCompiler->paEscEols[iEscEol].offEol + pCompiler->cchEolSeq - offCur;
5659                     pCompiler->iEscEol = iEscEol + 1;
5660                     pchWord += cch;
5661                     cchLeft -= cch;
5662                     KMK_CC_EVAL_SKIP_SPACES(pCompiler, pchWord, cchLeft);
5663                     break;
5664                 }
5665             }
5666         }
5667         else
5668             KMK_CC_ASSERT(0);
5669     }
5670 
5671     /*
5672      * Check for assignment operator.
5673      */
5674     if (cchLeft)
5675     {
5676         size_t              cchValue;
5677         PKMKCCEVALASSIGN    pInstr;
5678         KMKCCEVALINSTR      enmOpCode;
5679         int                 fPlainValue;
5680         char               *pszValue;
5681 
5682         ch = *pchWord;
5683         if (ch == '=')
5684         {
5685             enmOpCode = kKmkCcEvalInstr_assign_recursive;
5686             pchWord++;
5687             cchLeft--;
5688         }
5689         else if (cchLeft >= 2 && pchWord[1] == '=')
5690         {
5691             if (ch == ':')
5692                 enmOpCode = kKmkCcEvalInstr_assign_simple;
5693             else if (ch == '+')
5694                 enmOpCode = kKmkCcEvalInstr_assign_append;
5695             else if (ch == '<')
5696                 enmOpCode = kKmkCcEvalInstr_assign_prepend;
5697             else if (ch == '?')
5698                 enmOpCode = kKmkCcEvalInstr_assign_if_new;
5699             else
5700                 return kmk_cc_eval_handle_recipe_cont_2nd_word(pCompiler, pchVarNm, cchVarNm, pchWord, cchLeft, fQualifiers);
5701             pchWord += 2;
5702             cchLeft -= 2;
5703         }
5704         else
5705             return kmk_cc_eval_handle_recipe_cont_2nd_word(pCompiler, pchVarNm, cchVarNm, pchWord, cchLeft, fQualifiers);
5706 
5707         /*
5708          * Skip leading spaces, if any and prep the value for copying.
5709          */
5710         KMK_CC_EVAL_SKIP_SPACES(pCompiler, pchWord, cchLeft);
5711         cchValue    = kmk_cc_eval_prep_normal_line(pCompiler, pchWord, cchLeft);
5712         fPlainValue = memchr(pchWord, '$', cchLeft) == NULL;
5713 
5714 
5715         /*
5716          * Emit the instruction.
5717          */
5718         kmk_cc_eval_end_of_recipe(pCompiler);
5719 
5720         pInstr = (PKMKCCEVALASSIGN)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
5721         pInstr->Core.enmOpcode = enmOpCode;
5722         pInstr->Core.iLine     = pCompiler->iLine;
5723         pInstr->fExport        = (fQualifiers & KMK_CC_EVAL_QUALIFIER_EXPORT)   != 0;
5724         pInstr->fOverride      = (fQualifiers & KMK_CC_EVAL_QUALIFIER_OVERRIDE) != 0;
5725         pInstr->fPrivate       = (fQualifiers & KMK_CC_EVAL_QUALIFIER_PRIVATE)  != 0;
5726         pInstr->fLocal         = (fQualifiers & KMK_CC_EVAL_QUALIFIER_LOCAL)    != 0;
5727 
5728         /* We copy the value before messing around with the variable name since
5729            we have to do more iEolEsc saves & restores the other way around. */
5730         pszValue = kmk_cc_eval_strdup_prepped(pCompiler, cchValue);
5731         if (fPlainVarNm)
5732             pchVarNm = strcache2_add(&variable_strcache, pchVarNm, cchVarNm);
5733         else
5734         {
5735             pCompiler->iEscEol = iEscEolVarNm;
5736             cchVarNm = kmk_cc_eval_prep_normal_line_ex(pCompiler, pchVarNm, cchVarNm);
5737             pchVarNm = kmk_cc_eval_strdup_prepped(pCompiler, cchVarNm);
5738         }
5739         kmk_cc_block_realign(pCompiler->ppBlockTail);
5740         KMK_CC_EVAL_DPRINTF(("%s: '%s' '%s'\n", g_apszEvalInstrNms[enmOpCode], pchVarNm, pszValue));
5741 
5742         kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Variable, pchVarNm, cchVarNm, fPlainVarNm);
5743         kmk_cc_eval_init_subprogram_or_plain(pCompiler, &pInstr->Value, pszValue, cchValue, fPlainValue);
5744 
5745         pInstr->pNext = (PKMKCCEVALCORE)kmk_cc_block_get_next_ptr(*pCompiler->ppBlockTail);
5746     }
5747     else
5748         kmk_cc_eval_fatal(pCompiler, pchWord, "Neither recipe nor variable assignment");
5749     return 1;
5750 }
5751 
5752 
5753 /**
5754  * Parses a 'local [override] variable = value', 'local define variable', and
5755  * 'local undefine variable [...]' expressions.
5756  *
5757  * The 'local' directive must be first and it does not permit any qualifiers at
5758  * the moment.  Should any be added later, they will have to come after 'local'.
5759  *
5760  * @returns 1 to indicate we've handled a keyword (see
5761  *          kmk_cc_eval_try_handle_keyword).
5762  * @param   pCompiler   The compiler state.
5763  * @param   pchWord     First char after 'local'.
5764  * @param   cchLeft     The number of chars left to parse on this line.
5765  * @param   fQualifiers The qualifiers.
5766  */
kmk_cc_eval_do_var_local(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft)5767 static int kmk_cc_eval_do_var_local(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
5768 {
5769     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5770     if (cchLeft)
5771     {
5772         /*
5773          * Check for 'local define' and 'local undefine'
5774          */
5775         if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "define", 6))   /* final */
5776             return kmk_cc_eval_do_var_define(pCompiler, pchWord + 6, cchLeft + 6, KMK_CC_EVAL_QUALIFIER_LOCAL);
5777         if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "undefine", 8))   /* final */
5778             return kmk_cc_eval_do_var_undefine(pCompiler, pchWord + 8, cchLeft + 8, KMK_CC_EVAL_QUALIFIER_LOCAL);
5779 
5780         /*
5781          * Simpler to just join paths with the rest here, even if we could
5782          * probably optimize the parsing a little if we liked.
5783          */
5784         return kmk_cc_eval_handle_assignment_or_recipe(pCompiler, pchWord, cchLeft, KMK_CC_EVAL_QUALIFIER_LOCAL);
5785     }
5786     kmk_cc_eval_fatal(pCompiler, pchWord, "Expected variable name, assignment operator and value after 'local'");
5787     return 1;
5788 }
5789 
5790 
5791 /**
5792  * We've found one variable qualification keyword, now continue parsing and see
5793  * if this is some kind of variable assignment expression or not.
5794  *
5795  * @returns 1 if variable assignment, 0 if not.
5796  * @param   pCompiler   The compiler state.
5797  * @param   pchWord     First char after the first qualifier.
5798  * @param   cchLeft     The number of chars left to parse on this line.
5799  * @param   fQualifiers The qualifier.
5800  */
kmk_cc_eval_try_handle_var_with_keywords(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft,unsigned fQualifiers)5801 static int kmk_cc_eval_try_handle_var_with_keywords(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft,
5802                                                     unsigned fQualifiers)
5803 {
5804     for (;;)
5805     {
5806         KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5807         if (cchLeft)
5808         {
5809             char ch = *pchWord;
5810             if (KMK_CC_EVAL_IS_1ST_IN_VARIABLE_KEYWORD(ch))
5811             {
5812                 if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "define", 6))   /* final */
5813                     return kmk_cc_eval_do_var_define(pCompiler, pchWord + 6, cchLeft - 6, fQualifiers);
5814 
5815                 if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "undefine", 8)) /* final */
5816                     return kmk_cc_eval_do_var_undefine(pCompiler, pchWord + 8, cchLeft -86, fQualifiers);
5817 
5818                 if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "unexport", 8)) /* final */
5819                     return kmk_cc_eval_do_var_unexport(pCompiler, pchWord + 8, cchLeft - 8, fQualifiers);
5820 
5821                 if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "export", 6))
5822                 {
5823                     if (!(fQualifiers & KMK_CC_EVAL_QUALIFIER_EXPORT))
5824                         fQualifiers |= KMK_CC_EVAL_QUALIFIER_EXPORT;
5825                     else
5826                         kmk_cc_eval_warn(pCompiler, pchWord, "'export' qualifier repeated");
5827                     pchWord += 6;
5828                     cchLeft -= 6;
5829                     continue;
5830                 }
5831 
5832                 if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "override", 8))
5833                 {
5834                     if (!(fQualifiers & KMK_CC_EVAL_QUALIFIER_OVERRIDE))
5835                         fQualifiers |= KMK_CC_EVAL_QUALIFIER_OVERRIDE;
5836                     else
5837                         kmk_cc_eval_warn(pCompiler, pchWord, "'override' qualifier repeated");
5838                     pchWord += 8;
5839                     cchLeft -= 8;
5840                     continue;
5841                 }
5842 
5843                 if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "private", 7))
5844                 {
5845                     if (!(fQualifiers & KMK_CC_EVAL_QUALIFIER_PRIVATE))
5846                         fQualifiers |= KMK_CC_EVAL_QUALIFIER_PRIVATE;
5847                     else
5848                         kmk_cc_eval_warn(pCompiler, pchWord, "'private' qualifier repeated");
5849                     pchWord += 7;
5850                     cchLeft -= 7;
5851                     continue;
5852                 }
5853             }
5854 
5855             /*
5856              * Not a keyword, likely variable name followed by an assignment
5857              * operator and a value.  Do a rough check for the assignment operator
5858              * and join paths with the unqualified assignment handling code.
5859              */
5860             {
5861                 const char *pchEqual = (const char *)memchr(pchWord, '=', cchLeft);
5862                 if (pchEqual)
5863                     return kmk_cc_eval_handle_assignment_or_recipe(pCompiler, pchWord, cchLeft, fQualifiers);
5864             }
5865             return 0;
5866         }
5867         else
5868             kmk_cc_eval_fatal(pCompiler, NULL,
5869                               "Expected assignment operator or variable directive after variable qualifier(s)\n");
5870     }
5871 }
5872 
5873 
5874 /**
5875  * Parses 'export [variable]' and 'export [qualifiers] variable = value'
5876  * expressions.
5877  *
5878  * When we find the 'export' directive at the start of a line, we need to
5879  * continue parsing with till we can tell the difference between the two forms.
5880  *
5881  * @returns 1 to indicate we've handled a keyword (see
5882  *          kmk_cc_eval_try_handle_keyword).
5883  * @param   pCompiler   The compiler state.
5884  * @param   pchWord     First char after 'define'.
5885  * @param   cchLeft     The number of chars left to parse on this line.
5886  */
kmk_cc_eval_handle_var_export(PKMKCCEVALCOMPILER pCompiler,const char * pchWord,size_t cchLeft)5887 static int kmk_cc_eval_handle_var_export(PKMKCCEVALCOMPILER pCompiler, const char *pchWord, size_t cchLeft)
5888 {
5889     KMK_CC_EVAL_SKIP_SPACES_AFTER_WORD(pCompiler, pchWord, cchLeft);
5890 
5891     if (cchLeft)
5892     {
5893         unsigned iSavedEscEol;
5894         unsigned cWords;
5895 
5896         /*
5897          * We need to figure out whether this is an assignment or a export statement,
5898          * in the latter case join paths with 'export' and 'undefine'.
5899          */
5900         const char *pchEqual = (const char *)memchr(pchWord, '=', cchLeft);
5901         if (!pchEqual)
5902             return kmk_cc_eval_do_with_variable_list(pCompiler, pchWord, cchLeft, kKmkCcEvalInstr_export, 0 /*fQualifiers*/);
5903 
5904         /*
5905          * Found an '=', could be an assignment.  Let's take the easy way out
5906          * and just parse the whole statement into words like we would do if
5907          * it wasn't an assignment, and then check the words out for
5908          * assignment keywords and operators.
5909          */
5910         iSavedEscEol = pCompiler->iEscEol;
5911         cWords       = kmk_cc_eval_parse_words(pCompiler, pchWord, cchLeft);
5912         if (cWords)
5913         {
5914             PKMKCCEVALVARIABLES pInstr;
5915             PKMKCCEVALWORD pWord = pCompiler->paWords;
5916             unsigned       iWord = 0;
5917             while (iWord < cWords)
5918             {
5919                 /* Trailing assignment operator or terminal assignment directive ('undefine'
5920                    and 'unexport' makes no sense here but GNU make ignores that). */
5921                 if (   (   pWord->cchWord > 1
5922                         && pWord->pchWord[pWord->cchWord - 1] == '=')
5923                     || KMK_CC_STRCMP_CONST(pWord->pchWord, pWord->cchWord, "define", 6)
5924                     || KMK_CC_STRCMP_CONST(pWord->pchWord, pWord->cchWord, "undefine", 8)
5925                     || KMK_CC_STRCMP_CONST(pWord->pchWord, pWord->cchWord, "unexport", 8) )
5926                 {
5927                     pCompiler->iEscEol = iSavedEscEol;
5928                     return kmk_cc_eval_try_handle_var_with_keywords(pCompiler, pchWord, cchLeft, KMK_CC_EVAL_QUALIFIER_EXPORT);
5929                 }
5930 
5931                 /* If not a variable assignment qualifier, it must be a variable name
5932                    followed by an assignment operator. */
5933                 if (iWord + 1 < cWords)
5934                 {
5935                     if (   !KMK_CC_STRCMP_CONST(pWord->pchWord, pWord->cchWord, "export", 6)
5936                         && !KMK_CC_STRCMP_CONST(pWord->pchWord, pWord->cchWord, "private", 7)
5937                         && !KMK_CC_STRCMP_CONST(pWord->pchWord, pWord->cchWord, "override", 8))
5938                     {
5939                         pWord++;
5940                         if (   pWord->cchWord > 0
5941                             && (   pWord->pchWord[0] == '='
5942                                 || (   pWord->cchWord > 1
5943                                     && pWord->pchWord[1] == '='
5944                                     && (   pWord->pchWord[0] == ':'
5945                                         || pWord->pchWord[0] == '+'
5946                                         || pWord->pchWord[0] == '?'
5947                                         || pWord->pchWord[0] == '<') ) ) )
5948                         {
5949                             pCompiler->iEscEol = iSavedEscEol;
5950                             return kmk_cc_eval_try_handle_var_with_keywords(pCompiler, pchWord, cchLeft,
5951                                                                             KMK_CC_EVAL_QUALIFIER_EXPORT);
5952                         }
5953                         break;
5954                     }
5955                 }
5956                 else
5957                     break;
5958                 /* next */
5959                 pWord++;
5960                 iWord++;
5961             }
5962 
5963             /*
5964              * It's not an assignment.
5965              * (This is the same as kmk_cc_eval_do_with_variable_list does.)
5966              */
5967             pInstr = (PKMKCCEVALVARIABLES)kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, KMKCCEVALVARIABLES_SIZE(cWords));
5968             pInstr->Core.enmOpcode = kKmkCcEvalInstr_export;
5969             pInstr->Core.iLine     = pCompiler->iLine;
5970             pInstr->cVars          = cWords;
5971             kmk_cc_eval_init_spp_array_from_duplicated_words(pCompiler, cWords, pCompiler->paWords, pInstr->aVars);
5972             kmk_cc_block_realign(pCompiler->ppBlockTail);
5973         }
5974         else
5975             KMK_CC_ASSERT(0);
5976     }
5977     else
5978     {
5979         /*
5980          * We're exporting all variables.
5981          */
5982         PKMKCCEVALCORE pInstr = kmk_cc_block_alloc_eval(pCompiler->ppBlockTail, sizeof(*pInstr));
5983         pInstr->enmOpcode = kKmkCcEvalInstr_export_all;
5984         pInstr->iLine     = pCompiler->iLine;
5985     }
5986     return 1;
5987 }
5988 
5989 
5990 /**
5991  * When entering this function we know that the first two character in the first
5992  * word both independently occurs in keywords.
5993  *
5994  * @returns 1 if make directive or qualified variable assignment, 0 if neither.
5995  * @param   pCompiler   The compiler state.
5996  * @param   ch          The first char.
5997  * @param   pchWord     Pointer to the first word.
5998  * @param   cchLeft     Number of characters left to parse starting at
5999  *                      @a cchLeft.
6000  */
kmk_cc_eval_try_handle_keyword(PKMKCCEVALCOMPILER pCompiler,char ch,const char * pchWord,size_t cchLeft)6001 int kmk_cc_eval_try_handle_keyword(PKMKCCEVALCOMPILER pCompiler, char ch, const char *pchWord, size_t cchLeft)
6002 {
6003     unsigned iSavedEscEol = pCompiler->iEscEol;
6004 
6005     KMK_CC_ASSERT(cchLeft >= 2);
6006     KMK_CC_ASSERT(ch == pchWord[0]);
6007     KMK_CC_ASSERT(KMK_CC_EVAL_IS_1ST_IN_KEYWORD(pchWord[0]));
6008     KMK_CC_ASSERT(KMK_CC_EVAL_IS_2ND_IN_KEYWORD(pchWord[1]));
6009 
6010     /*
6011      * If it's potentially a variable related keyword, check that out first.
6012      */
6013     if (KMK_CC_EVAL_IS_1ST_IN_VARIABLE_KEYWORD(ch))
6014     {
6015         if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "local", 5))
6016             return kmk_cc_eval_do_var_local(pCompiler, pchWord + 5, cchLeft - 5);
6017         if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "define", 6))
6018             return kmk_cc_eval_do_var_define(pCompiler, pchWord + 6, cchLeft - 6, 0);
6019         if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "export", 6))
6020             return kmk_cc_eval_handle_var_export(pCompiler, pchWord + 6, cchLeft - 6);
6021         if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "undefine", 8))
6022             return kmk_cc_eval_do_var_undefine(pCompiler, pchWord + 8, cchLeft - 8, 0);
6023         if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "unexport", 8))
6024             return kmk_cc_eval_do_var_unexport(pCompiler, pchWord + 8, cchLeft - 8, 0);
6025         if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "override", 8))
6026         {
6027             if (kmk_cc_eval_try_handle_var_with_keywords(pCompiler, pchWord + 8, cchLeft - 8, KMK_CC_EVAL_QUALIFIER_OVERRIDE))
6028                 return 1;
6029             pCompiler->iEscEol = iSavedEscEol;
6030         }
6031         else if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "private", 7))
6032         {
6033             if (kmk_cc_eval_try_handle_var_with_keywords(pCompiler, pchWord + 7, cchLeft - 7, KMK_CC_EVAL_QUALIFIER_PRIVATE))
6034                 return 1;
6035             pCompiler->iEscEol = iSavedEscEol;
6036         }
6037     }
6038 
6039     /*
6040      * Check out the other keywords.
6041      */
6042     if (ch == 'i') /* Lots of directives starting with 'i'. */
6043     {
6044         char ch2 = pchWord[1];
6045         pchWord += 2;
6046         cchLeft -= 2;
6047 
6048         /* 'if...' */
6049         if (ch2 == 'f')
6050         {
6051             if (KMK_CC_EVAL_WORD_COMP_IS_EOL(pCompiler, pchWord, cchLeft))
6052                 return kmk_cc_eval_do_if(pCompiler, pchWord, cchLeft, 0 /* in else */);
6053 
6054             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "eq", 2))
6055                 return kmk_cc_eval_do_ifeq( pCompiler, pchWord + 2, cchLeft - 2, 0 /* in else */, 1 /* positive */);
6056 
6057             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "def", 3))
6058                 return kmk_cc_eval_do_ifdef(pCompiler, pchWord + 3, cchLeft - 3, 0 /* in else */, 1 /* positive */);
6059 
6060             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "neq", 3))
6061                 return kmk_cc_eval_do_ifeq( pCompiler, pchWord + 3, cchLeft - 3, 0 /* in else */, 0 /* positive */);
6062 
6063             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "1of", 3))
6064                 return kmk_cc_eval_do_if1of(pCompiler, pchWord + 3, cchLeft - 3, 0 /* in else */, 1 /* positive */);
6065 
6066             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "ndef", 4))
6067                 return kmk_cc_eval_do_ifdef(pCompiler, pchWord + 4, cchLeft - 4, 0 /* in else */, 0 /* positive */);
6068 
6069             if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "n1of", 4))
6070                 return kmk_cc_eval_do_if1of(pCompiler, pchWord + 4, cchLeft - 4, 0 /* in else */, 0 /* positive */);
6071         }
6072         /* include... */
6073         else if (ch2 == 'n' && cchLeft >= 5 && KMK_CC_WORD_COMP_CONST_5(pchWord, "clude") ) /* 'in...' */
6074         {
6075             pchWord += 5;
6076             cchLeft -= 5;
6077             if (KMK_CC_EVAL_WORD_COMP_IS_EOL(pCompiler, pchWord, cchLeft))
6078                 return kmk_cc_eval_do_include(pCompiler, pchWord, cchLeft, kKmkCcEvalInstr_include);
6079             if (cchLeft >= 3 && KMK_CC_WORD_COMP_CONST_3(pchWord, "dep"))
6080             {
6081                 pchWord += 3;
6082                 cchLeft -= 3;
6083                 if (KMK_CC_EVAL_WORD_COMP_IS_EOL(pCompiler, pchWord, cchLeft))
6084                     return kmk_cc_eval_do_include(pCompiler, pchWord, cchLeft, kKmkCcEvalInstr_includedep);
6085                 if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "-queue", 6))
6086                     return kmk_cc_eval_do_include(pCompiler, pchWord + 6, cchLeft - 6, kKmkCcEvalInstr_includedep_queue);
6087                 if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "-flush", 6))
6088                     return kmk_cc_eval_do_include(pCompiler, pchWord + 6, cchLeft - 6, kKmkCcEvalInstr_includedep_flush);
6089             }
6090         }
6091     }
6092     else if (ch == 'e') /* A few directives starts with 'e'. */
6093     {
6094         if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "else", 4))
6095             return kmk_cc_eval_do_else(pCompiler, pchWord + 4, cchLeft - 4);
6096         if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "endif", 5))
6097             return kmk_cc_eval_do_endif(pCompiler, pchWord + 5, cchLeft - 5);
6098         /* export and endef are handled elsewhere, though stray endef's may end up here... */
6099         KMK_CC_ASSERT(!KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "export", 6));
6100 
6101     }
6102     else /* the rest. */
6103     {
6104         if (   KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "sinclude", 8)
6105             || KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "-include", 8))
6106             return kmk_cc_eval_do_include(pCompiler, pchWord + 8, cchLeft - 8, kKmkCcEvalInstr_include_silent);
6107         if (KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "vpath", 5))
6108             return kmk_cc_eval_do_vpath(pCompiler, pchWord + 5, cchLeft - 5);
6109 
6110         KMK_CC_ASSERT(!KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "local", 5));
6111         KMK_CC_ASSERT(!KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "define", 6));
6112         KMK_CC_ASSERT(!KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "private", 7));
6113         KMK_CC_ASSERT(!KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "override", 8));
6114         KMK_CC_ASSERT(!KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "unexport", 8));
6115         KMK_CC_ASSERT(!KMK_CC_EVAL_WORD_COMP_CONST(pCompiler, pchWord, cchLeft, "undefine", 8));
6116     }
6117 
6118     pCompiler->iEscEol = iSavedEscEol;
6119     return 0;
6120 }
6121 
6122 
6123 
6124 
kmk_cc_eval_compile_worker(PKMKCCEVALPROG pEvalProg,const char * pszContent,size_t cchContent,unsigned iLine)6125 static int kmk_cc_eval_compile_worker(PKMKCCEVALPROG pEvalProg, const char *pszContent, size_t cchContent, unsigned iLine)
6126 {
6127     const char *pchTmp;
6128 
6129     /*
6130      * Compiler state.
6131      */
6132     KMKCCEVALCOMPILER   Compiler;
6133     kmk_cc_eval_init_compiler(&Compiler, pEvalProg, iLine, pszContent, cchContent);
6134     KMK_CC_EVAL_DPRINTF(("\nkmk_cc_eval_compile_worker - begin (%s/%s/%d)\n", pEvalProg->pszFilename, pEvalProg->pszVarName, iLine));
6135 
6136     {
6137         /*
6138          * Line state.
6139          */
6140         size_t              cchLine;                                /* The length of the current line (w/o comments). */
6141         size_t              offNext = 0;                            /* The offset of the next line. */
6142         size_t              off     = 0;                            /* The offset into pszContent of the current line. */
6143 
6144         /* Try for some register/whatever optimzations. */
6145         int const           chFirstEol = Compiler.chFirstEol;
6146         size_t const        cchEolSeq  = Compiler.cchEolSeq;
6147 
6148         /*
6149          * Process input lines.
6150          *
6151          * The code here concerns itself with getting the next line in an efficient
6152          * manner, very basic classification and trying out corresponding handlers.
6153          * The real work is done in the handlers.
6154          */
6155         while (offNext < cchContent)
6156         {
6157             size_t offFirstWord;
6158 
6159             /*
6160              * Find the end of the next line.
6161              */
6162             KMK_CC_ASSERT(off == offNext);
6163 
6164             /* Simple case: No escaped EOL, nor the end of the input. */
6165             pchTmp = (const char *)memchr(&pszContent[offNext], chFirstEol, cchContent - offNext);
6166             if (   pchTmp
6167                 && (   &pszContent[offNext] == pchTmp
6168                     || pchTmp[-1] != '\\') )
6169             {
6170                 if (   cchEolSeq == 1
6171                     || pchTmp[1] == Compiler.chSecondEol)
6172                 {
6173                     /* Frequent: Blank line. */
6174                     if (&pszContent[offNext] == pchTmp)
6175                     {
6176                         KMK_CC_EVAL_DPRINTF(("#%03u: <empty>\n", Compiler.iLine));
6177                         Compiler.iLine++;
6178                         off = offNext += cchEolSeq;
6179                         continue;
6180                     }
6181                     if (pszContent[offNext] == '#')
6182                     {
6183                         KMK_CC_EVAL_DPRINTF(("#%03u: <comment>\n", Compiler.iLine));
6184                         Compiler.iLine++;
6185                         offNext = pchTmp - pszContent;
6186                         off = offNext += cchEolSeq;
6187                         continue;
6188                     }
6189 
6190                     offNext  = pchTmp - pszContent;
6191                     cchLine  = offNext - off;
6192 
6193                     offFirstWord = off;
6194                     while (offFirstWord < offNext && KMK_CC_EVAL_IS_SPACE(pszContent[offFirstWord]))
6195                         offFirstWord++;
6196 
6197                     offNext += cchEolSeq;
6198                     Compiler.cEscEols = 0;
6199                     Compiler.iEscEol  = 0;
6200                 }
6201                 else
6202                     kmk_cc_eval_fatal_eol(&Compiler, pchTmp, Compiler.iLine, off);
6203             }
6204             /* The complicated, less common cases. */
6205             else
6206             {
6207                 Compiler.cEscEols = 0;
6208                 Compiler.iEscEol  = 0;
6209                 offFirstWord = offNext;
6210                 for (;;)
6211                 {
6212                     if (offFirstWord == offNext)
6213                     {
6214                         size_t offEol = off + cchLine;
6215                         while (offFirstWord < offEol && KMK_CC_EVAL_IS_SPACE(pszContent[offFirstWord]))
6216                             offFirstWord++;
6217                     }
6218 
6219                     if (pchTmp)
6220                     {
6221                         if (   cchEolSeq == 1
6222                             || pchTmp[1] == Compiler.chSecondEol)
6223                         {
6224                             size_t offEsc;
6225                             if (offFirstWord != offNext)
6226                                 offNext = pchTmp - pszContent;
6227                             else
6228                             {
6229                                 offNext = pchTmp - pszContent;
6230                                 while (offFirstWord < offNext && KMK_CC_EVAL_IS_SPACE(pszContent[offFirstWord]))
6231                                     offFirstWord++;
6232                             }
6233 
6234 
6235                             /* Is it an escape sequence? */
6236                             if (   !offNext
6237                                 || pchTmp[-1] != '\\')
6238                             {
6239                                 cchLine  = offNext - off;
6240                                 offNext += cchEolSeq;
6241                                 break;
6242                             }
6243                             if (offNext < 2 || pchTmp[-2] != '\\')
6244                                 offEsc = offNext - 1;
6245                             else
6246                             {
6247                                 /* Count how many backslashes there are. Must be odd number to be an escape
6248                                    sequence.  Normally we keep half of them, except for command lines.  */
6249                                 size_t cSlashes = 2;
6250                                 while (offNext >= cSlashes && pchTmp[0 - cSlashes] == '\\')
6251                                     cSlashes--;
6252                                 if (!(cSlashes & 1))
6253                                 {
6254                                     cchLine  = offNext - off;
6255                                     offNext += cchEolSeq;
6256                                     break;
6257                                 }
6258                                 offEsc = offNext - (cSlashes >> 1);
6259                             }
6260 
6261                             /* Record it. */
6262                             if (Compiler.cEscEols < Compiler.cEscEolsAllocated) { /* likely */ }
6263                             else
6264                             {
6265                                 KMK_CC_ASSERT(Compiler.cEscEols == Compiler.cEscEolsAllocated);
6266                                 Compiler.cEscEolsAllocated = Compiler.cEscEolsAllocated
6267                                                            ? Compiler.cEscEolsAllocated * 2 : 2;
6268                                 Compiler.paEscEols = (PKMKCCEVALESCEOL)xrealloc(Compiler.paEscEols,
6269                                                                                   Compiler.cEscEolsAllocated
6270                                                                                 * sizeof(Compiler.paEscEols[0]));
6271                             }
6272                             Compiler.paEscEols[Compiler.cEscEols].offEsc = offEsc;
6273                             Compiler.paEscEols[Compiler.cEscEols].offEol = offNext;
6274                             Compiler.cEscEols++;
6275 
6276                             /* Advance. */
6277                             offNext += cchEolSeq;
6278                             if (offFirstWord == offEsc)
6279                             {
6280                                 offFirstWord = offNext;
6281                                 Compiler.iEscEol++;
6282                             }
6283                         }
6284                         else
6285                             kmk_cc_eval_fatal_eol(&Compiler, pchTmp, Compiler.iLine, off);
6286                     }
6287                     else
6288                     {
6289                         /* End of input. Happens only once per compilation, nothing to optimize for. */
6290                         if (offFirstWord == offNext)
6291                             while (offFirstWord < cchContent && KMK_CC_EVAL_IS_SPACE(pszContent[offFirstWord]))
6292                                 offFirstWord++;
6293                         offNext = cchContent;
6294                         cchLine = cchContent - off;
6295                         break;
6296                     }
6297                     pchTmp = (const char *)memchr(&pszContent[offNext], chFirstEol, cchContent - offNext);
6298                 }
6299             }
6300             KMK_CC_ASSERT(offNext       <= cchContent);
6301             KMK_CC_ASSERT(offNext       >= off + cchLine);
6302             KMK_CC_ASSERT(off + cchLine <= cchContent && cchLine <= cchContent);
6303             KMK_CC_ASSERT(offFirstWord  <= off + cchLine);
6304             KMK_CC_ASSERT(offFirstWord  >= off);
6305             KMK_CC_ASSERT(pszContent[offFirstWord] != ' ' && pszContent[offFirstWord] != '\t');
6306 
6307             KMK_CC_EVAL_DPRINTF(("#%03u: %*.*s\n", Compiler.iLine, (int)cchLine, (int)cchLine, &pszContent[off]));
6308 
6309             /*
6310              * Skip blank lines.
6311              */
6312             if (offFirstWord < off + cchLine)
6313             {
6314                 /*
6315                  * Command? Ignore command prefix if no open recipe (SunOS 4 behavior).
6316                  */
6317                 if (   pszContent[off] == Compiler.chCmdPrefix
6318                     && (Compiler.pRecipe || Compiler.fNoTargetRecipe))
6319                 {
6320                     if (!Compiler.fNoTargetRecipe)
6321                         kmk_cc_eval_handle_command(&Compiler, &pszContent[off], cchLine);
6322                 }
6323                 /*
6324                  * Since it's not a command line, we can now skip comment lines
6325                  * even with a tab indentation.  If it's not a comment line, we
6326                  * tentatively strip any trailing comment.
6327                  */
6328                 else if (pszContent[offFirstWord] != '#')
6329                 {
6330                     const char *pchWord = &pszContent[offFirstWord];
6331                     size_t      cchLeft = off + cchLine - offFirstWord;
6332                     char        ch;
6333 
6334                     Compiler.cchLineWithComments =  cchLine;
6335                     pchTmp = (const char *)memchr(pchWord, '#', cchLeft);
6336                     if (pchTmp)
6337                     {
6338                         cchLeft = pchTmp - pchWord;
6339                         cchLine = pchTmp - &pszContent[off];
6340                     }
6341                     Compiler.cchLine = cchLine;
6342                     Compiler.offLine = off;
6343 
6344                     /*
6345                      * If not a directive or variable qualifier, it's either a variable
6346                      * assignment or a recipe.
6347                      */
6348                     ch = *pchWord;
6349                     if (   !KMK_CC_EVAL_IS_1ST_IN_KEYWORD(ch)
6350                         || !KMK_CC_EVAL_IS_2ND_IN_KEYWORD(pchWord[1])
6351                         || !kmk_cc_eval_try_handle_keyword(&Compiler, ch, pchWord, cchLeft) )
6352                     {
6353                         pchTmp = (const char *)memchr(pchWord, '=', cchLeft);
6354                         if (pchTmp)
6355                             kmk_cc_eval_handle_assignment_or_recipe(&Compiler, pchWord, cchLeft, 0 /*fQualifiers*/);
6356                         else
6357                             kmk_cc_eval_handle_recipe(&Compiler, pchTmp, pchWord, cchLeft);
6358                     }
6359                     /* else: handled a keyword expression */
6360                 }
6361             }
6362 
6363             /*
6364              * Advance to the next line.
6365              */
6366             off             = offNext;
6367             Compiler.iLine += Compiler.cEscEols + 1;
6368         }
6369     }
6370 
6371     /*
6372      * Check whether
6373      */
6374 
6375     kmk_cc_eval_delete_compiler(&Compiler);
6376     KMK_CC_EVAL_DPRINTF(("kmk_cc_eval_compile_worker - done (%s/%s)\n\n", pEvalProg->pszFilename, pEvalProg->pszVarName));
6377     return 0;
6378 }
6379 
6380 
6381 
kmk_cc_eval_compile(const char * pszContent,size_t cchContent,const char * pszFilename,unsigned iLine,const char * pszVarName)6382 static PKMKCCEVALPROG kmk_cc_eval_compile(const char *pszContent, size_t cchContent,
6383                                           const char *pszFilename, unsigned iLine, const char *pszVarName)
6384 {
6385     /*
6386      * Estimate block size, allocate one and initialize it.
6387      */
6388     PKMKCCEVALPROG  pEvalProg;
6389     PKMKCCBLOCK     pBlock;
6390     pEvalProg = kmk_cc_block_alloc_first(&pBlock, sizeof(*pEvalProg), cchContent / 32); /** @todo adjust */
6391     if (pEvalProg)
6392     {
6393         pEvalProg->pBlockTail   = pBlock;
6394         pEvalProg->pFirstInstr  = (PKMKCCEVALCORE)kmk_cc_block_get_next_ptr(pBlock);
6395         pEvalProg->pszFilename  = pszFilename ? pszFilename : "<unknown>";
6396         pEvalProg->pszVarName   = pszVarName;
6397         pEvalProg->cRefs        = 1;
6398 #ifdef KMK_CC_STRICT
6399         pEvalProg->uInputHash   = kmk_cc_debug_string_hash_n(0, pszContent, cchContent);
6400 #endif
6401 
6402         /*
6403          * Do the actual compiling.
6404          */
6405 #ifdef CONFIG_WITH_EVAL_COMPILER
6406         if (kmk_cc_eval_compile_worker(pEvalProg, pszContent, cchContent, iLine) == 0)
6407 #else
6408         if (0)
6409 #endif
6410         {
6411 #ifdef KMK_CC_WITH_STATS
6412             pBlock = pEvalProg->pBlockTail;
6413             if (!pBlock->pNext)
6414                 g_cSingleBlockEvalProgs++;
6415             else if (!pBlock->pNext->pNext)
6416                 g_cTwoBlockEvalProgs++;
6417             else
6418                 g_cMultiBlockEvalProgs++;
6419             for (; pBlock; pBlock = pBlock->pNext)
6420             {
6421                 g_cBlocksAllocatedEvalProgs++;
6422                 g_cbAllocatedEvalProgs += pBlock->cbBlock;
6423                 g_cbUnusedMemEvalProgs += pBlock->cbBlock - pBlock->offNext;
6424             }
6425 #endif
6426             return pEvalProg;
6427         }
6428         kmk_cc_block_free_list(pEvalProg->pBlockTail);
6429     }
6430     return NULL;
6431 }
6432 
6433 
6434 /**
6435  * Compiles a variable direct evaluation as is, setting v->evalprog on success.
6436  *
6437  * @returns Pointer to the program on success, NULL if no program was created.
6438  * @param   pVar        Pointer to the variable.
6439  */
kmk_cc_compile_variable_for_eval(struct variable * pVar)6440 struct kmk_cc_evalprog   *kmk_cc_compile_variable_for_eval(struct variable *pVar)
6441 {
6442     PKMKCCEVALPROG pEvalProg = pVar->evalprog;
6443     if (!pEvalProg)
6444     {
6445 #ifdef CONFIG_WITH_EVAL_COMPILER
6446         pEvalProg = kmk_cc_eval_compile(pVar->value, pVar->value_length,
6447                                         pVar->fileinfo.filenm, pVar->fileinfo.lineno, pVar->name);
6448         pVar->evalprog = pEvalProg;
6449 #endif
6450         g_cVarForEvalCompilations++;
6451     }
6452     return pEvalProg;
6453 }
6454 
6455 
6456 /**
6457  * Compiles a makefile for
6458  *
6459  * @returns Pointer to the program on success, NULL if no program was created.
6460  * @param   pVar        Pointer to the variable.
6461  */
kmk_cc_compile_file_for_eval(FILE * pFile,const char * pszFilename)6462 struct kmk_cc_evalprog   *kmk_cc_compile_file_for_eval(FILE *pFile, const char *pszFilename)
6463 {
6464     PKMKCCEVALPROG  pEvalProg;
6465 
6466     /*
6467      * Read the entire file into a zero terminate memory buffer.
6468      */
6469     size_t          cchContent = 0;
6470     char           *pszContent = NULL;
6471     struct stat st;
6472     if (!fstat(fileno(pFile), &st))
6473     {
6474         if (   st.st_size > (off_t)16*1024*1024
6475             && st.st_size < 0)
6476             fatal(NULL, _("Makefile too large to compile: %ld bytes (%#lx) - max 16MB"), (long)st.st_size, (long)st.st_size);
6477         cchContent = (size_t)st.st_size;
6478         pszContent = (char *)xmalloc(cchContent + 1);
6479 
6480         cchContent = fread(pszContent, 1, cchContent, pFile);
6481         if (ferror(pFile))
6482             fatal(NULL, _("Read error: %s"), strerror(errno));
6483     }
6484     else
6485     {
6486         size_t cbAllocated = 2048;
6487         do
6488         {
6489             cbAllocated *= 2;
6490             if (cbAllocated > 16*1024*1024)
6491                 fatal(NULL, _("Makefile too large to compile: max 16MB"));
6492             pszContent = (char *)xrealloc(pszContent, cbAllocated);
6493             cchContent += fread(&pszContent[cchContent], 1, cbAllocated - 1 - cchContent, pFile);
6494             if (ferror(pFile))
6495                 fatal(NULL, _("Read error: %s"), strerror(errno));
6496         } while (!feof(pFile));
6497     }
6498     pszContent[cchContent] = '\0';
6499 
6500     /*
6501      * Call common function to do the compilation.
6502      */
6503     pEvalProg = kmk_cc_eval_compile(pszContent, cchContent, pszFilename, 1, NULL /*pszVarName*/);
6504     g_cFileForEvalCompilations++;
6505 
6506     free(pszContent);
6507     if (!pEvalProg)
6508         fseek(pFile, 0, SEEK_SET);
6509     return pEvalProg;
6510 }
6511 
6512 
6513 /**
6514  * Equivalent of eval_buffer, only it's using the evalprog of the variable.
6515  *
6516  * @param   pVar        Pointer to the variable. Must have a program.
6517  */
kmk_exec_eval_variable(struct variable * pVar)6518 void kmk_exec_eval_variable(struct variable *pVar)
6519 {
6520     KMK_CC_ASSERT(pVar->evalprog);
6521     assert(0);
6522 }
6523 
6524 
6525 /**
6526  * Worker for eval_makefile.
6527  *
6528  * @param   pEvalProg   The program pointer.
6529  */
kmk_exec_eval_file(struct kmk_cc_evalprog * pEvalProg)6530 void kmk_exec_eval_file(struct kmk_cc_evalprog *pEvalProg)
6531 {
6532     KMK_CC_ASSERT(pEvalProg);
6533     assert(0);
6534 }
6535 
6536 
6537 
6538 /*
6539  *
6540  * Program destruction hooks.
6541  * Program destruction hooks.
6542  * Program destruction hooks.
6543  *
6544  */
6545 
6546 
6547 /**
6548  * Called when a variable with expandprog or/and evalprog changes.
6549  *
6550  * @param   pVar        Pointer to the variable.
6551  */
kmk_cc_variable_changed(struct variable * pVar)6552 void  kmk_cc_variable_changed(struct variable *pVar)
6553 {
6554     PKMKCCEXPPROG pProg = pVar->expandprog;
6555 
6556     KMK_CC_ASSERT(pVar->evalprog || pProg);
6557 
6558     if (pVar->evalprog)
6559     {
6560         kmk_cc_block_free_list(pVar->evalprog->pBlockTail);
6561         pVar->evalprog = NULL;
6562     }
6563 
6564     if (pProg)
6565     {
6566         if (pProg->cRefs == 1)
6567             kmk_cc_block_free_list(pProg->pBlockTail);
6568         else
6569             fatal(NULL, _("Modifying a variable (%s) while its expansion program is running is not supported"), pVar->name);
6570         pVar->expandprog = NULL;
6571     }
6572 }
6573 
6574 
6575 /**
6576  * Called when a variable with expandprog or/and evalprog is deleted.
6577  *
6578  * @param   pVar        Pointer to the variable.
6579  */
kmk_cc_variable_deleted(struct variable * pVar)6580 void  kmk_cc_variable_deleted(struct variable *pVar)
6581 {
6582     PKMKCCEXPPROG pProg = pVar->expandprog;
6583 
6584     KMK_CC_ASSERT(pVar->evalprog || pProg);
6585 
6586     if (pVar->evalprog)
6587     {
6588         kmk_cc_block_free_list(pVar->evalprog->pBlockTail);
6589         pVar->evalprog = NULL;
6590     }
6591 
6592     if (pProg)
6593     {
6594         if (pProg->cRefs == 1)
6595             kmk_cc_block_free_list(pProg->pBlockTail);
6596         else
6597             fatal(NULL, _("Deleting a variable (%s) while its expansion program is running is not supported"), pVar->name);
6598         pVar->expandprog = NULL;
6599     }
6600 }
6601 
6602 
6603 
6604 
6605 
6606 
6607 
6608 #endif /* CONFIG_WITH_COMPILER */
6609 
6610