kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

input.c (30410B)


      1 /*
      2  * Parse a makefile and build the target/rule/macro graph. Ported from pdpmake
      3  * input.c. The read path no longer uses FILE*: a MakeSource iterates either an
      4  * in-memory makefile buffer or the built-in rules generator; included makefiles
      5  * are read through KitContext.file_io; `!=` / $(shell) run through host->capture.
      6  *
      7  * v1 limitation: shell-glob wildcards in prerequisites/targets are not expanded
      8  * (pdpmake used <glob.h>). Names are taken literally, with backslash escapes
      9  * removed, which matches the non-wildcard path exactly. Part of the
     10  * src/api/make.c amalgamation.
     11  */
     12 #include "make.h"
     13 
     14 #define mk_find_colon(p) strchr((p), ':')
     15 
     16 /* Defined below; used by mk_expand_macros for the $(shell ...) function. */
     17 static char* mk_run_command(MakeCtx* mc, const char* cmd);
     18 
     19 /*
     20  * Return a pointer to the next blank-delimited word or NULL if none are left.
     21  */
     22 static char* mk_gettok(char** ptr) {
     23   char* p;
     24 
     25   while (isblank((unsigned char)**ptr)) /* Skip blanks. */
     26     (*ptr)++;
     27 
     28   if (**ptr == '\0') /* Nothing after blanks. */
     29     return NULL;
     30 
     31   p = *ptr; /* Word starts here. */
     32 
     33   while (**ptr != '\0' && !isblank((unsigned char)**ptr))
     34     (*ptr)++; /* Find end of word. */
     35 
     36   if (**ptr != '\0') *(*ptr)++ = '\0';
     37 
     38   return p;
     39 }
     40 
     41 /*
     42  * Skip over (possibly adjacent or nested) macro expansions.
     43  */
     44 static char* mk_skip_macro(const char* s) {
     45   while (*s && s[0] == '$') {
     46     if (s[1] == '(' || s[1] == '{') {
     47       char end = *++s == '(' ? ')' : '}';
     48       while (*s && *s != end) s = mk_skip_macro(s + 1);
     49       if (*s == end) ++s;
     50     } else if (s[1] != '\0') {
     51       s += 2;
     52     } else {
     53       break;
     54     }
     55   }
     56   return (char*)s;
     57 }
     58 
     59 /*
     60  * Process each whitespace-separated word: replace paths with their directory or
     61  * filename part, and replace prefixes/suffixes. Returns an arena string, or
     62  * NULL if the input is unmodified.
     63  */
     64 static char* mk_modify_words(MakeCtx* mc, const char* val, int modifier,
     65                              size_t lenf, size_t lenr, const char* find_pref,
     66                              const char* repl_pref, const char* find_suff,
     67                              const char* repl_suff) {
     68   char *s, *copy, *word, *sep, *newword, *buf = NULL;
     69   size_t find_pref_len = 0, find_suff_len = 0;
     70 
     71   if (!modifier && lenf == 0 && lenr == 0) return buf;
     72 
     73   if (find_pref) {
     74     find_pref_len = strlen(find_pref);
     75     find_suff_len = lenf - find_pref_len - 1;
     76   }
     77 
     78   s = copy = mk_strdup(mc, val);
     79   while ((word = mk_gettok(&s)) != NULL) {
     80     newword = NULL;
     81     if (modifier) {
     82       sep = strrchr(word, '/');
     83       if (modifier == 'D') {
     84         if (!sep) {
     85           word[0] = '.';
     86           sep = word + 1;
     87         } else if (sep == word) {
     88           sep = word + 1;
     89         }
     90         *sep = '\0';
     91       } else if (/* modifier == 'F' && */ sep) {
     92         word = sep + 1;
     93       }
     94     }
     95     if (find_pref != NULL || lenf != 0 || lenr != 0) {
     96       size_t lenw = strlen(word);
     97       /* Pattern macro expansions: <prefix>%<suffix>, e.g. src/%.c. */
     98       if (find_pref != NULL && lenw + 1 >= lenf) {
     99         if (strncmp(word, find_pref, find_pref_len) == 0 &&
    100             strcmp(word + lenw - find_suff_len, find_suff) == 0) {
    101           if (!repl_suff) {
    102             word = newword = mk_strdup(mc, repl_pref);
    103           } else {
    104             word[lenw - find_suff_len] = '\0';
    105             word = newword =
    106                 mk_concat3(mc, repl_pref, word + find_pref_len, repl_suff);
    107           }
    108         }
    109       } else if (lenw >= lenf && strcmp(word + lenw - lenf, find_suff) == 0) {
    110         word[lenw - lenf] = '\0';
    111         word = newword = mk_concat3(mc, word, repl_suff, "");
    112       }
    113     }
    114     buf = mk_appendword(mc, buf, word);
    115   }
    116   return buf;
    117 }
    118 
    119 /*
    120  * Return a pointer to the next instance of a character, skipping over macro
    121  * expansions so ':' and '=' inside $(VAR:.s1=.s2) aren't seen as separators.
    122  */
    123 static char* mk_find_char(const char* str, int c) {
    124   const char* s;
    125 
    126   for (s = mk_skip_macro(str); *s; s = mk_skip_macro(s + 1)) {
    127     if (*s == c) return (char*)s;
    128   }
    129   return NULL;
    130 }
    131 
    132 /*
    133  * Recursively expand any macros in str to an arena string.
    134  */
    135 static char* mk_expand_macros(MakeCtx* mc, const char* str, int except_dollar) {
    136   char *exp, *newexp, *s, *t, *p, *q, *name;
    137   char *find, *replace, *modified;
    138   char *expval, *expfind, *find_suff, *repl_suff;
    139   char *find_pref = NULL, *repl_pref = NULL;
    140   size_t lenf, lenr;
    141   char modifier;
    142   struct macro* mp;
    143 
    144   exp = mk_strdup(mc, str);
    145   for (t = exp; *t; t++) {
    146     if (*t == '$') {
    147       if (t[1] == '\0') {
    148         break;
    149       }
    150       if (t[1] == '$' && except_dollar) {
    151         t++;
    152         continue;
    153       }
    154       /* Need to expand a macro. Find its extent (s to t inclusive) and copy. */
    155       s = t;
    156       t++;
    157       if (*t == '{' || *t == '(') {
    158         t = mk_find_char(t, *t == '{' ? '}' : ')');
    159         if (t == NULL) mk_error(mc, "unterminated variable '%s'", s);
    160         name = mk_strndup(mc, s + 2, t - s - 2);
    161       } else {
    162         name = mk_alloc(mc, 2);
    163         name[0] = *t;
    164         name[1] = '\0';
    165       }
    166 
    167       modified = NULL;
    168       /* $(shell command): a non-POSIX function; run it and substitute its
    169        * stdout. Detected before the ':'/'=' parsing below, which would
    170        * otherwise mangle a command containing those characters. */
    171       if (!mc->posix && strncmp(name, "shell", 5) == 0 &&
    172           isblank((unsigned char)name[5])) {
    173         char* scmd = mk_expand_macros(mc, name + 6, FALSE);
    174         modified = mk_run_command(mc, scmd);
    175         goto mk_expanded;
    176       }
    177 
    178       /* Only do suffix replacement or pattern macro expansion if both ':' and
    179        * '=' are found, plus a '%' for the latter. */
    180       expfind = NULL;
    181       find_suff = repl_suff = NULL;
    182       lenf = lenr = 0;
    183       if ((find = mk_find_char(name, ':'))) {
    184         *find++ = '\0';
    185         expfind = mk_expand_macros(mc, find, FALSE);
    186         if ((replace = mk_find_char(expfind, '='))) {
    187           *replace++ = '\0';
    188           lenf = strlen(expfind);
    189           if (!POSIX_2017 && (find_suff = strchr(expfind, '%'))) {
    190             find_pref = expfind;
    191             repl_pref = replace;
    192             *find_suff++ = '\0';
    193             if ((repl_suff = strchr(replace, '%'))) *repl_suff++ = '\0';
    194           } else {
    195             if (mc->posix && !(mc->pragma & P_EMPTY_SUFFIX) && lenf == 0)
    196               mk_error(mc, "empty suffix%s",
    197                        ": allow with pragma empty_suffix");
    198             find_suff = expfind;
    199             repl_suff = replace;
    200             lenr = strlen(repl_suff);
    201           }
    202         }
    203       }
    204 
    205       p = q = name;
    206       /* If not in POSIX mode expand macros in the name. */
    207       if (!POSIX_2017) {
    208         char* expname = mk_expand_macros(mc, name, FALSE);
    209         name = expname;
    210       } else
    211         /* Skip over nested expansions in name. */
    212         do {
    213           *q++ = *p;
    214         } while ((p = mk_skip_macro(p + 1)) && *p);
    215 
    216       /* The internal macros support 'D' and 'F' modifiers. */
    217       modifier = '\0';
    218       switch (name[0]) {
    219         case '^':
    220         case '+':
    221           if (POSIX_2017) break;
    222           /* fall through */
    223         case '@':
    224         case '%':
    225         case '?':
    226         case '<':
    227         case '*':
    228           if ((name[1] == 'D' || name[1] == 'F') && name[2] == '\0') {
    229             modifier = name[1];
    230             name[1] = '\0';
    231           }
    232           break;
    233       }
    234 
    235       modified = NULL;
    236       if ((mp = mk_getmp(mc, name))) {
    237         /* Recursive expansion. */
    238         if (mp->m_flag) mk_error(mc, "recursive macro %s", name);
    239         /* Note if we've expanded $(MAKE). */
    240         if (strcmp(name, "MAKE") == 0) mc->opts |= OPT_make;
    241         mp->m_flag = TRUE;
    242         /* Immediate-expansion macros aren't recursively expanded. */
    243         if (mp->m_immediate)
    244           expval = mk_strdup(mc, mp->m_val);
    245         else
    246           expval = mk_expand_macros(mc, mp->m_val, FALSE);
    247         mp->m_flag = FALSE;
    248         modified = mk_modify_words(mc, expval, modifier, lenf, lenr, find_pref,
    249                                    repl_pref, find_suff, repl_suff);
    250         if (!modified) modified = expval;
    251       }
    252 
    253     mk_expanded:
    254       if (modified && *modified) {
    255         /* Text replaced by the expansion is s to t inclusive. */
    256         *s = '\0';
    257         newexp = mk_concat3(mc, exp, modified, t + 1);
    258         t = newexp + (s - exp) + strlen(modified) - 1;
    259         exp = newexp;
    260       } else {
    261         /* Macro wasn't expanded or expanded to nothing. Close the gap. */
    262         q = t + 1;
    263         t = s - 1;
    264         while ((*s++ = *q++)) continue;
    265       }
    266     }
    267   }
    268   return exp;
    269 }
    270 
    271 /*
    272  * Process a non-command line: strip comment, join escaped newlines.
    273  */
    274 static void mk_process_line(MakeCtx* mc, char* s) {
    275   char* t;
    276 
    277   /* Strip comment. In non-POSIX mode don't treat '#' inside a macro expansion
    278    * as a comment, nor a backslash-escaped '#'. */
    279   if (!mc->posix) {
    280     char* u = s;
    281     while ((t = mk_find_char(u, '#')) && t > u && t[-1] == '\\') {
    282       for (u = t; *u; ++u) u[-1] = u[0];
    283       *u = '\0';
    284       u = t;
    285     }
    286   } else
    287     t = strchr(s, '#');
    288   if (t) *t = '\0';
    289 
    290   /* Replace escaped newline + leading whitespace on the next line with a single
    291    * space. Stop at a non-escaped newline. */
    292   for (t = s; *s && *s != '\n';) {
    293     if (s[0] == '\\' && s[1] == '\n') {
    294       s += 2;
    295       while (isspace((unsigned char)*s)) ++s;
    296       *t++ = ' ';
    297     } else {
    298       *t++ = *s++;
    299     }
    300   }
    301   *t = '\0';
    302 }
    303 
    304 enum { MK_INITIAL = 0, MK_SKIP_LINE = 1 << 0, MK_EXPECT_ELSE = 1 << 1,
    305        MK_GOT_MATCH = 1 << 2 };
    306 
    307 /*
    308  * Extract strings following ifeq/ifneq and compare them. Return -1 on error.
    309  */
    310 static int mk_compare_strings(MakeCtx* mc, char* arg1) {
    311   char *arg2, *end, term, *t1, *t2;
    312   int ret;
    313 
    314   if (arg1[0] == '(')
    315     term = ',';
    316   else if (arg1[0] == '"' || arg1[0] == '\'')
    317     term = arg1[0];
    318   else
    319     return -1;
    320 
    321   arg2 = mk_find_char(++arg1, term);
    322   if (arg2 == NULL) return -1;
    323   *arg2++ = '\0';
    324 
    325   if (term == ',') {
    326     term = ')';
    327   } else {
    328     while (isspace((unsigned char)arg2[0])) arg2++;
    329     if (arg2[0] == '"' || arg2[0] == '\'')
    330       term = arg2[0];
    331     else
    332       return -1;
    333     ++arg2;
    334   }
    335 
    336   end = mk_find_char(arg2, term);
    337   if (end == NULL) return -1;
    338   *end++ = '\0';
    339 
    340   if (mk_gettok(&end) != NULL) mk_warning(mc, "unexpected text");
    341 
    342   t1 = mk_expand_macros(mc, arg1, FALSE);
    343   t2 = mk_expand_macros(mc, arg2, FALSE);
    344   ret = strcmp(t1, t2) == 0;
    345   return ret;
    346 }
    347 
    348 /*
    349  * Process conditional directives; return TRUE if the current line is skipped.
    350  */
    351 static int mk_skip_line(MakeCtx* mc, const char* str1) {
    352   char *copy, *q, *token;
    353   bool new_level = TRUE;
    354   int ret = mc->cstate[mc->clevel] & MK_SKIP_LINE;
    355 
    356   q = copy = mk_strdup(mc, str1);
    357   mk_process_line(mc, copy);
    358   if ((token = mk_gettok(&q)) != NULL) {
    359     if (strcmp(token, "endif") == 0) {
    360       if (mk_gettok(&q) != NULL) mk_error_unexpected(mc, "text");
    361       if (mc->clevel == 0) mk_error_unexpected(mc, token);
    362       --mc->clevel;
    363       ret = TRUE;
    364       goto end;
    365     } else if (strcmp(token, "else") == 0) {
    366       if (!(mc->cstate[mc->clevel] & MK_EXPECT_ELSE))
    367         mk_error_unexpected(mc, token);
    368 
    369       if ((mc->cstate[mc->clevel] & MK_GOT_MATCH))
    370         mc->cstate[mc->clevel] |= MK_SKIP_LINE;
    371       else
    372         mc->cstate[mc->clevel] &= ~MK_SKIP_LINE;
    373 
    374       token = mk_gettok(&q);
    375       if (token == NULL) {
    376         mc->cstate[mc->clevel] &= ~MK_EXPECT_ELSE;
    377         ret = TRUE;
    378         goto end;
    379       } else {
    380         new_level = FALSE;
    381       }
    382     }
    383 
    384     if (strcmp(token, "ifdef") == 0 || strcmp(token, "ifndef") == 0 ||
    385         strcmp(token, "ifeq") == 0 || strcmp(token, "ifneq") == 0) {
    386       int match;
    387 
    388       if (token[2] == 'd' || token[3] == 'd') {
    389         char* name = mk_gettok(&q);
    390         if (name != NULL && mk_gettok(&q) == NULL) {
    391           char* t = mk_expand_macros(mc, name, FALSE);
    392           struct macro* mp = mk_getmp(mc, t);
    393           match = mp != NULL && mp->m_val[0] != '\0';
    394         } else {
    395           match = -1;
    396         }
    397       } else {
    398         match = mk_compare_strings(mc, q);
    399       }
    400 
    401       if (match >= 0) {
    402         if (new_level) {
    403           if (mc->clevel == MK_IF_MAX) mk_error(mc, "nesting too deep");
    404           ++mc->clevel;
    405           mc->cstate[mc->clevel] = MK_EXPECT_ELSE | MK_SKIP_LINE;
    406           if ((mc->cstate[mc->clevel - 1] & MK_SKIP_LINE))
    407             mc->cstate[mc->clevel] |= MK_GOT_MATCH;
    408         }
    409 
    410         if (!(mc->cstate[mc->clevel] & MK_GOT_MATCH)) {
    411           if (token[2] == 'n') match = !match;
    412           if (match) {
    413             mc->cstate[mc->clevel] &= ~MK_SKIP_LINE;
    414             mc->cstate[mc->clevel] |= MK_GOT_MATCH;
    415           }
    416         }
    417       } else {
    418         mk_error(mc, "invalid condition");
    419       }
    420       ret = TRUE;
    421     } else if (!new_level) {
    422       mk_error(mc, "missing conditional");
    423     }
    424   }
    425 end:
    426   return ret;
    427 }
    428 
    429 /*
    430  * fgets-style reader over a MakeSource: the built-in rules (data == NULL) or an
    431  * in-memory makefile buffer.
    432  */
    433 static char* mk_src_fgets(MakeCtx* mc, char* s, int size, MakeSource* src) {
    434   int i = 0;
    435 
    436   if (src->data == NULL) return mk_getrules(mc, s, size);
    437   if (src->pos >= src->len) return NULL;
    438   while (i < size - 1 && src->pos < src->len) {
    439     char c = (char)src->data[src->pos++];
    440     s[i++] = c;
    441     if (c == '\n') break;
    442   }
    443   s[i] = '\0';
    444   return i ? s : NULL;
    445 }
    446 
    447 /*
    448  * Read a newline-terminated logical line into an arena string. Backslash-escaped
    449  * newlines don't terminate it; comment lines are skipped. Return NULL on EOF.
    450  */
    451 static char* mk_readline(MakeCtx* mc, MakeSource* src, int want_command) {
    452   char *p, *str = NULL;
    453   int pos = 0;
    454   int len = 0;
    455 
    456   for (;;) {
    457     if (len - pos > 1 && mk_src_fgets(mc, str + pos, len - pos, src) == NULL) {
    458       if (pos) return str;
    459       return NULL; /* EOF */
    460     }
    461 
    462     if (len - pos < 2 || (p = strchr(str + pos, '\n')) == NULL) {
    463       int oldlen = len;
    464       if (len) pos = len - 1;
    465       len += 256;
    466       str = mk_realloc(mc, str, (size_t)oldlen, (size_t)len);
    467       continue;
    468     }
    469     mc->lineno++;
    470 
    471     if (p != str && p[-1] == '\r') {
    472       p[-1] = '\n';
    473       *p-- = '\0';
    474     }
    475 
    476     if (p != str && p[-1] == '\\') {
    477       pos = p - str + 1;
    478       continue;
    479     }
    480     mc->dispno = mc->lineno;
    481 
    482     if (mc->posix || !mk_skip_line(mc, str)) {
    483       if (want_command && *str == '\t') return str;
    484 
    485       p = str;
    486       while (isblank((unsigned char)*p)) p++;
    487 
    488       if (*p != '\n' && (mc->posix ? *str != '#' : *p != '#')) return str;
    489     }
    490 
    491     pos = 0;
    492   }
    493 }
    494 
    495 /*
    496  * Return the suffix name if the argument is a known suffix, else NULL.
    497  */
    498 static const char* mk_is_suffix(MakeCtx* mc, const char* s) {
    499   struct name* np;
    500   struct rule* rp;
    501   struct depend* dp;
    502 
    503   np = mk_newname(mc, ".SUFFIXES");
    504   for (rp = np->n_rule; rp; rp = rp->r_next) {
    505     for (dp = rp->r_dep; dp; dp = dp->d_next) {
    506       if (strcmp(s, dp->d_name->n_name) == 0) return dp->d_name->n_name;
    507     }
    508   }
    509   return NULL;
    510 }
    511 
    512 /*
    513  * Return TRUE if s is formed by concatenating two known suffixes.
    514  */
    515 static int mk_is_inference_target(MakeCtx* mc, const char* s) {
    516   struct name* np;
    517   struct rule *rp1, *rp2;
    518   struct depend *dp1, *dp2;
    519 
    520   np = mk_newname(mc, ".SUFFIXES");
    521   for (rp1 = np->n_rule; rp1; rp1 = rp1->r_next) {
    522     for (dp1 = rp1->r_dep; dp1; dp1 = dp1->d_next) {
    523       const char* suff1 = dp1->d_name->n_name;
    524       size_t len = strlen(suff1);
    525 
    526       if (strncmp(s, suff1, len) == 0) {
    527         for (rp2 = np->n_rule; rp2; rp2 = rp2->r_next) {
    528           for (dp2 = rp2->r_dep; dp2; dp2 = dp2->d_next) {
    529             const char* suff2 = dp2->d_name->n_name;
    530             if (strcmp(s + len, suff2) == 0) return TRUE;
    531           }
    532         }
    533       }
    534     }
    535   }
    536   return FALSE;
    537 }
    538 
    539 enum {
    540   T_NORMAL = 0,
    541   T_SPECIAL = (1 << 0),
    542   T_INFERENCE = (1 << 1),
    543   T_NOPREREQ = (1 << 2),
    544   T_COMMAND = (1 << 3),
    545 };
    546 
    547 /*
    548  * Determine if s is a special target and return flags describing it.
    549  */
    550 static int mk_target_type(MakeCtx* mc, char* s) {
    551   int ret;
    552   static const char* s_name[] = {
    553       ".DEFAULT", ".POSIX",   ".IGNORE",      ".PRECIOUS",
    554       ".SILENT",  ".SUFFIXES", ".PHONY",       ".NOTPARALLEL",
    555       ".WAIT",    ".PRAGMA",
    556   };
    557   static const uint8_t s_type[] = {
    558       T_SPECIAL | T_NOPREREQ | T_COMMAND,
    559       T_SPECIAL | T_NOPREREQ,
    560       T_SPECIAL,
    561       T_SPECIAL,
    562       T_SPECIAL,
    563       T_SPECIAL,
    564       T_SPECIAL,
    565       T_SPECIAL | T_NOPREREQ,
    566       T_SPECIAL | T_NOPREREQ,
    567       T_SPECIAL,
    568   };
    569 
    570   for (ret = 0; (size_t)ret < sizeof(s_name) / sizeof(s_name[0]); ret++)
    571     if (strcmp(s_name[ret], s) == 0) return s_type[ret];
    572 
    573   ret = T_NORMAL;
    574   if (!mc->posix) {
    575     if (mk_is_suffix(mc, s) || mk_is_inference_target(mc, s))
    576       ret = T_INFERENCE | T_NOPREREQ | T_COMMAND;
    577   } else {
    578     /* In POSIX inference rule targets must contain one or two dots. */
    579     char* sfx = mk_suffix(s);
    580     if (*s == '.' && mk_is_suffix(mc, sfx)) {
    581       if (s == sfx) {
    582         ret = T_INFERENCE | T_NOPREREQ | T_COMMAND;
    583       } else {
    584         *sfx = '\0';
    585         if (mk_is_suffix(mc, s)) ret = T_INFERENCE | T_NOPREREQ | T_COMMAND;
    586         *sfx = '.';
    587       }
    588     }
    589   }
    590   return ret;
    591 }
    592 
    593 static int mk_ends_with_bracket(const char* s) {
    594   const char* t = strrchr(s, ')');
    595   return t && t[1] == '\0';
    596 }
    597 
    598 /*
    599  * Process a command line: strip POSIX comments, collapse escaped newlines.
    600  */
    601 static char* mk_process_command(MakeCtx* mc, char* s) {
    602   char *t, *u;
    603   int len;
    604   char* outside;
    605 
    606   if (!(mc->pragma & P_COMMAND_COMMENT) && mc->posix) {
    607     /* POSIX strips comments from command lines. */
    608     t = strchr(s, '#');
    609     if (t) {
    610       *t = '\0';
    611       mk_warning(mc,
    612                  "comment in command removed: keep with pragma command_comment");
    613     }
    614   }
    615 
    616   len = (int)strlen(s) + 1;
    617   outside = mk_alloc(mc, (size_t)len);
    618   memset(outside, 0, (size_t)len);
    619   for (t = mk_skip_macro(s); *t; t = mk_skip_macro(t + 1)) outside[t - s] = 1;
    620 
    621   /* Process escaped newlines. Stop at first non-escaped newline. */
    622   for (t = u = s; *u && *u != '\n';) {
    623     if (u[0] == '\\' && u[1] == '\n') {
    624       if (POSIX_2017 || outside[u - s]) {
    625         /* Outside macro: remove tab following escaped newline. */
    626         *t++ = *u++;
    627         *t++ = *u++;
    628         u += (*u == '\t');
    629       } else {
    630         /* Inside macro: replace escaped newline + leading whitespace with a
    631          * single space. */
    632         u += 2;
    633         while (isspace((unsigned char)*u)) ++u;
    634         *t++ = ' ';
    635       }
    636     } else {
    637       *t++ = *u++;
    638     }
    639   }
    640   *t = '\0';
    641   return s;
    642 }
    643 
    644 /*
    645  * Run a command and capture its stdout for `!=` / $(shell). Returns an arena
    646  * string or NULL.
    647  */
    648 static char* mk_run_command(MakeCtx* mc, const char* cmd) {
    649   uint8_t* out = NULL;
    650   size_t out_len = 0;
    651   int status = 0;
    652   char* val;
    653   char* s;
    654   size_t len;
    655   const KitExec* ex = mc->host->exec;
    656   KitExecProc* proc = NULL;
    657   KitSlice av[3];
    658   KitExecOpts eo;
    659 
    660   av[0] = kit_slice_cstr(mc->shell);
    661   av[1] = KIT_SLICE_LIT("-c");
    662   av[2] = kit_slice_cstr(cmd);
    663   memset(&eo, 0, sizeof eo);
    664   eo.argv = av;
    665   eo.argc = 3;
    666   eo.env = mc->env;
    667   eo.nenv = mc->nenv;
    668   eo.cwd = mc->root ? kit_slice_cstr(mc->root) : KIT_SLICE_NULL;
    669   eo.search_path = 1;
    670   eo.capture_stdout = 1;
    671   if (ex->spawn(ex->user, &eo, &proc) != 0) return NULL;
    672   /* Like pdpmake's run_command, use the captured output regardless of status. */
    673   (void)ex->wait(ex->user, proc, &status, &out, &out_len);
    674   if (out == NULL || out_len == 0) {
    675     if (out) mc->ctx->heap->free(mc->ctx->heap, out, out_len);
    676     return NULL;
    677   }
    678 
    679   val = mk_alloc(mc, out_len + 1);
    680   memcpy(val, out, out_len);
    681   val[out_len] = '\0';
    682   len = out_len;
    683   mc->ctx->heap->free(mc->ctx->heap, out, out_len);
    684 
    685   /* Strip leading whitespace in POSIX mode. */
    686   if (mc->posix) {
    687     s = val;
    688     while (isspace((unsigned char)*s)) {
    689       ++s;
    690       --len;
    691     }
    692     if (len == 0) return NULL;
    693     memmove(val, s, len + 1);
    694   }
    695 
    696   /* Remove one trailing newline (BSD compatibility); others become spaces. */
    697   if (val[len - 1] == '\n') val[len - 1] = '\0';
    698   for (s = val; *s; ++s) {
    699     if (*s == '\n') *s = ' ';
    700   }
    701   return val;
    702 }
    703 
    704 /*
    705  * Remove backslash escapes from a name (the non-wildcard path of pdpmake's
    706  * wildcard()); v1 does not expand shell globs.
    707  */
    708 static void mk_deglob(char* p) {
    709   char* s;
    710   for (s = p; *p; ++p) {
    711     if (*p == '\\' && p[1] != '\0') continue;
    712     *s++ = *p;
    713   }
    714   *s = '\0';
    715 }
    716 
    717 /*
    718  * Determine if a line is a target rule with an inline command; return a pointer
    719  * to the semicolon separator if so, else NULL.
    720  */
    721 static char* mk_inline_command(char* line) {
    722   char* p = mk_find_char(line, ':');
    723   if (p) p = strchr(p, ';');
    724   return p;
    725 }
    726 
    727 /*
    728  * Parse input from a makefile source and construct the tree structure.
    729  */
    730 static void mk_input(MakeCtx* mc, MakeSource* src, int ilevel) {
    731   char *p, *q, *s, *a, *str, *expanded, *copy;
    732   char *str1, *str2;
    733   struct name* np;
    734   struct depend* dp;
    735   struct cmd* cp;
    736   int startno, count;
    737   bool semicolon_cmd, seen_inference;
    738   uint8_t old_clevel = mc->clevel;
    739   bool dbl;
    740   char* lib = NULL;
    741   int nfile, i;
    742   char** files;
    743   bool minus;
    744 
    745   mc->lineno = 0;
    746   str1 = mk_readline(mc, src, FALSE);
    747   while (str1) {
    748     str2 = NULL;
    749 
    750     /* Take a copy before non-command processing in case this is a rule with an
    751      * inline command (target: prereq; command). */
    752     copy = mk_strdup(mc, str1);
    753     mk_process_line(mc, str1);
    754     str = str1;
    755 
    756     /* Check for an include line. */
    757     if (!mc->posix)
    758       while (isblank((unsigned char)*str)) ++str;
    759     minus = !POSIX_2017 && *str == '-';
    760     p = str + minus;
    761     if (strncmp(p, "include", 7) == 0 && isblank((unsigned char)p[7])) {
    762       const char* old_makefile = mc->makefile;
    763       int old_lineno = mc->lineno;
    764 
    765       if (ilevel > 16) mk_error(mc, "too many includes");
    766 
    767       count = 0;
    768       q = expanded = mk_expand_macros(mc, p + 7, FALSE);
    769       while ((p = mk_gettok(&q)) != NULL) {
    770         KitFileData ifd;
    771 
    772         ++count;
    773         if (!POSIX_2017) {
    774           /* Try to create the include file or bring it up-to-date. */
    775           mc->opts |= OPT_include;
    776           mk_make(mc, mk_newname(mc, p), 1);
    777           mc->opts &= ~OPT_include;
    778         }
    779         ifd.data = NULL;
    780         ifd.size = 0;
    781         ifd.token = NULL;
    782         if (!mc->ctx->file_io ||
    783             mc->ctx->file_io->read_all(mc->ctx->file_io->user, mk_path(mc, p),
    784                                        &ifd) != KIT_OK) {
    785           if (!minus) mk_error(mc, "can't open include file '%s'", p);
    786         } else {
    787           MakeSource subsrc = {(const char*)ifd.data, ifd.size, 0};
    788           mc->makefile = p;
    789           mk_input(mc, &subsrc, ilevel + 1);
    790           if (mc->ctx->file_io->release)
    791             mc->ctx->file_io->release(mc->ctx->file_io->user, &ifd);
    792           mc->makefile = old_makefile;
    793           mc->lineno = old_lineno;
    794         }
    795         if (POSIX_2017) break;
    796       }
    797       if (POSIX_2017) {
    798         if (p == NULL || mk_gettok(&q)) mk_error(mc, "one include file per line");
    799       } else if (count == 0) {
    800         if (mc->posix) mk_error(mc, "no include file");
    801       }
    802       goto end_loop;
    803     }
    804 
    805     /* Check for a macro definition. */
    806     str = str1;
    807     if (POSIX_2017 && *str == '\t') mk_error(mc, "command not allowed here");
    808     if (mk_find_char(str, '=') != NULL) {
    809       int level = (useenv || src->data == NULL) ? 4 : 3;
    810       char* copy2 = mk_strdup(mc, str);
    811       char* newq = NULL;
    812       char eq = '\0';
    813       q = mk_find_char(copy2, '='); /* q can't be NULL */
    814 
    815       if (q - 1 > copy2) {
    816         switch (q[-1]) {
    817           case ':':
    818             /* '::=' and ':::=' are from POSIX 2024. */
    819             if (!POSIX_2017 && q - 2 > copy2 && q[-2] == ':') {
    820               if (q - 3 > copy2 && q[-3] == ':') {
    821                 eq = 'B'; /* BSD-style ':=' */
    822                 q[-3] = '\0';
    823               } else {
    824                 eq = ':'; /* GNU-style ':=' */
    825                 q[-2] = '\0';
    826               }
    827               break;
    828             }
    829             /* ':=' is a non-POSIX extension. */
    830             if (mc->posix) break;
    831             goto set_eq;
    832           case '+':
    833           case '?':
    834           case '!':
    835             /* '+=', '?=' and '!=' are from POSIX 2024. */
    836             if (POSIX_2017) break;
    837           set_eq:
    838             eq = q[-1];
    839             q[-1] = '\0';
    840             break;
    841         }
    842       }
    843       *q++ = '\0'; /* Separate name and value. */
    844       while (isblank((unsigned char)*q)) q++;
    845       if ((p = strrchr(q, '\n')) != NULL) *p = '\0';
    846 
    847       /* Expand LHS of the assignment. */
    848       p = expanded = mk_expand_macros(mc, copy2, FALSE);
    849       if ((a = mk_gettok(&p)) == NULL) mk_error(mc, "invalid macro assignment");
    850 
    851       /* If the expanded LHS contains ':' and ';' it might be a target rule. */
    852       if ((s = strchr(a, ':')) != NULL && strchr(s, ';') != NULL) {
    853         goto try_target;
    854       }
    855 
    856       if (mk_gettok(&p)) mk_error(mc, "invalid macro assignment");
    857 
    858       if (eq == ':') {
    859         /* GNU-style ':='. Expand RHS; immediate-expansion macro. */
    860         q = newq = mk_expand_macros(mc, q, FALSE);
    861         level |= M_IMMEDIATE;
    862       } else if (eq == 'B') {
    863         /* BSD-style ':='. Expand RHS but not '$$'; delayed-expansion. */
    864         q = newq = mk_expand_macros(mc, q, TRUE);
    865       } else if (eq == '?' && mk_getmp(mc, a) != NULL) {
    866         goto end_loop; /* Skip; macro already set. */
    867       } else if (eq == '+') {
    868         /* Append to current value. */
    869         struct macro* mp = mk_getmp(mc, a);
    870         char* rhs;
    871         newq = mp && mp->m_val[0] ? mk_strdup(mc, mp->m_val) : NULL;
    872         if (mp && mp->m_immediate) {
    873           rhs = mk_expand_macros(mc, q, FALSE);
    874           level |= M_IMMEDIATE;
    875         } else {
    876           rhs = q;
    877         }
    878         newq = mk_appendword(mc, newq, rhs);
    879         q = newq;
    880       } else if (eq == '!') {
    881         char* cmd = mk_expand_macros(mc, q, FALSE);
    882         q = newq = mk_run_command(mc, cmd);
    883       }
    884       mk_setmacro(mc, a, q, level);
    885       (void)newq;
    886       goto end_loop;
    887     }
    888 
    889     /* If we get here it must be a target rule. */
    890   try_target:
    891     if (*str == '\t') /* Command without target. */
    892       mk_error(mc, "command not allowed here");
    893     p = expanded = mk_expand_macros(mc, str, FALSE);
    894 
    895     /* Look for colon separator. */
    896     q = mk_find_colon(p);
    897     if (q == NULL) mk_error(mc, "expected separator");
    898 
    899     *q++ = '\0'; /* Separate targets and prerequisites. */
    900 
    901     /* Double colon. */
    902     dbl = !mc->posix && *q == ':';
    903     if (dbl) q++;
    904 
    905     /* Look for semicolon separator. */
    906     cp = NULL;
    907     s = strchr(q, ';');
    908     if (s) {
    909       /* Retrieve command from the original or expanded copy of the line. */
    910       char* copy3 = mk_expand_macros(mc, copy, FALSE);
    911       if ((p = mk_inline_command(copy)) || (p = mk_inline_command(copy3)))
    912         cp = mk_newcmd(mc, mk_process_command(mc, p + 1), cp);
    913       *s = '\0';
    914     }
    915     semicolon_cmd = cp != NULL && cp->c_cmd[0] != '\0';
    916 
    917     /* Create list of prerequisites. */
    918     dp = NULL;
    919     while (((p = mk_gettok(&q)) != NULL)) {
    920       char* newp = NULL;
    921 
    922       if (!mc->posix) {
    923         /* Allow prerequisites of form library(member1 member2). */
    924         if (!lib) {
    925           s = strchr(p, '(');
    926           if (s && !mk_ends_with_bracket(s) && strchr(q, ')')) {
    927             lib = p;
    928             if (s[1] != '\0') {
    929               p = newp = mk_concat3(mc, lib, ")", "");
    930               s[1] = '\0';
    931             } else {
    932               continue;
    933             }
    934           }
    935         } else if (mk_ends_with_bracket(p)) {
    936           if (*p != ')') p = newp = mk_concat3(mc, lib, p, "");
    937           lib = NULL;
    938           if (newp == NULL) continue;
    939         } else {
    940           p = newp = mk_concat3(mc, lib, p, ")");
    941         }
    942       }
    943 
    944       /* v1: names are literal (no glob); strip backslash escapes. */
    945       nfile = 1;
    946       files = &p;
    947       if (!mc->posix) mk_deglob(p);
    948       for (i = 0; i < nfile; ++i) {
    949         if (!POSIX_2017 && strcmp(files[i], ".WAIT") == 0) continue;
    950         np = mk_newname(mc, files[i]);
    951         dp = mk_newdep(mc, np, dp);
    952       }
    953     }
    954     lib = NULL;
    955 
    956     /* Create list of commands. */
    957     startno = mc->dispno;
    958     while ((str2 = mk_readline(mc, src, TRUE)) && *str2 == '\t') {
    959       cp = mk_newcmd(mc, mk_process_command(mc, str2), cp);
    960     }
    961     mc->dispno = startno;
    962 
    963     /* Create target names and attach the rule to them. */
    964     q = expanded;
    965     count = 0;
    966     seen_inference = FALSE;
    967     while ((p = mk_gettok(&q)) != NULL) {
    968       nfile = 1;
    969       files = &p;
    970       if (!mc->posix) mk_deglob(p);
    971       for (i = 0; i < nfile; ++i)
    972 #define p files[i]
    973       {
    974         int ttype = mk_target_type(mc, p);
    975 
    976         np = mk_newname(mc, p);
    977         if (ttype != T_NORMAL) {
    978           /* Enforce prerequisites/commands. */
    979           if ((ttype & T_NOPREREQ) && dp) mk_error_not_allowed(mc,
    980                                                                "prerequisites",
    981                                                                p);
    982           if ((ttype & T_INFERENCE)) {
    983             if (semicolon_cmd) mk_error_in_inference_rule(mc, "'; command'");
    984             seen_inference = TRUE;
    985           }
    986           if ((ttype & T_COMMAND) && !cp &&
    987               !((ttype & T_INFERENCE) && !semicolon_cmd))
    988             mk_error(mc, "commands required for %s", p);
    989           if (!(ttype & T_COMMAND) && cp) mk_error_not_allowed(mc, "commands",
    990                                                                p);
    991 
    992           if ((ttype & T_INFERENCE)) {
    993             np->n_flag |= N_INFERENCE;
    994           } else if (strcmp(p, ".DEFAULT") == 0) {
    995             np->n_flag |= N_SPECIAL | N_INFERENCE;
    996           } else {
    997             np->n_flag |= N_SPECIAL;
    998           }
    999         } else if (!mc->firstname) {
   1000           mc->firstname = np;
   1001         }
   1002         mk_addrule(mc, np, dp, cp, dbl);
   1003         count++;
   1004       }
   1005 #undef p
   1006     }
   1007     if (seen_inference && count != 1)
   1008       mk_error_in_inference_rule(mc, "multiple targets");
   1009 
   1010     /* Prerequisites/commands are unused if there were no targets. */
   1011     if (count == 0) {
   1012       (void)dp;
   1013       (void)cp;
   1014     }
   1015 
   1016   end_loop:
   1017     mc->dispno = mc->lineno;
   1018     str1 = str2 ? str2 : mk_readline(mc, src, FALSE);
   1019     (void)copy;
   1020     (void)expanded;
   1021     if (!mc->seen_first && src->data) {
   1022       if (mk_findname(mc, ".POSIX")) {
   1023         /* The first non-comment line defined .POSIX. */
   1024         mc->posix = TRUE;
   1025       }
   1026       mc->seen_first = TRUE;
   1027     }
   1028   }
   1029   /* Conditionals aren't allowed to span files. */
   1030   if (mc->clevel != old_clevel) mk_error(mc, "invalid conditional");
   1031 }