1*06b9b3e0SSimon J. Gerraty /* $NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $ */ 23955d011SMarcel Moolenaar 33955d011SMarcel Moolenaar /* 43955d011SMarcel Moolenaar * Copyright (c) 1992, The Regents of the University of California. 53955d011SMarcel Moolenaar * All rights reserved. 63955d011SMarcel Moolenaar * 73955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 83955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions 93955d011SMarcel Moolenaar * are met: 103955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 113955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 123955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 133955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 143955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 153955d011SMarcel Moolenaar * 3. Neither the name of the University nor the names of its contributors 163955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software 173955d011SMarcel Moolenaar * without specific prior written permission. 183955d011SMarcel Moolenaar * 193955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 203955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 213955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 223955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 233955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 243955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 253955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 263955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 273955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 283955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 293955d011SMarcel Moolenaar * SUCH DAMAGE. 303955d011SMarcel Moolenaar */ 313955d011SMarcel Moolenaar 323955d011SMarcel Moolenaar /*- 33956e45f6SSimon J. Gerraty * Handling of .for/.endfor loops in a makefile. 343955d011SMarcel Moolenaar * 35*06b9b3e0SSimon J. Gerraty * For loops have the form: 363955d011SMarcel Moolenaar * 37956e45f6SSimon J. Gerraty * .for <varname...> in <value...> 38*06b9b3e0SSimon J. Gerraty * # the body 393955d011SMarcel Moolenaar * .endfor 403955d011SMarcel Moolenaar * 41*06b9b3e0SSimon J. Gerraty * When a .for line is parsed, the following lines are copied to the body of 42*06b9b3e0SSimon J. Gerraty * the .for loop, until the corresponding .endfor line is reached. In this 43*06b9b3e0SSimon J. Gerraty * phase, the body is not yet evaluated. This also applies to any nested 44*06b9b3e0SSimon J. Gerraty * .for loops. 453955d011SMarcel Moolenaar * 46*06b9b3e0SSimon J. Gerraty * After reaching the .endfor, the values from the .for line are grouped 47*06b9b3e0SSimon J. Gerraty * according to the number of variables. For each such group, the unexpanded 48*06b9b3e0SSimon J. Gerraty * body is scanned for variable expressions, and those that match the variable 49*06b9b3e0SSimon J. Gerraty * names are replaced with expressions of the form ${:U...} or $(:U...). 50*06b9b3e0SSimon J. Gerraty * After that, the body is treated like a file from an .include directive. 51956e45f6SSimon J. Gerraty * 52956e45f6SSimon J. Gerraty * Interface: 53956e45f6SSimon J. Gerraty * For_Eval Evaluate the loop in the passed line. 54956e45f6SSimon J. Gerraty * 55956e45f6SSimon J. Gerraty * For_Run Run accumulated loop 563955d011SMarcel Moolenaar */ 573955d011SMarcel Moolenaar 58956e45f6SSimon J. Gerraty #include "make.h" 59956e45f6SSimon J. Gerraty 60956e45f6SSimon J. Gerraty /* "@(#)for.c 8.1 (Berkeley) 6/6/93" */ 61*06b9b3e0SSimon J. Gerraty MAKE_RCSID("$NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $"); 62956e45f6SSimon J. Gerraty 633955d011SMarcel Moolenaar static int forLevel = 0; /* Nesting level */ 643955d011SMarcel Moolenaar 65956e45f6SSimon J. Gerraty /* One of the variables to the left of the "in" in a .for loop. */ 66956e45f6SSimon J. Gerraty typedef struct ForVar { 67956e45f6SSimon J. Gerraty char *name; 68*06b9b3e0SSimon J. Gerraty size_t nameLen; 69956e45f6SSimon J. Gerraty } ForVar; 70956e45f6SSimon J. Gerraty 713955d011SMarcel Moolenaar /* 723955d011SMarcel Moolenaar * State of a for loop. 733955d011SMarcel Moolenaar */ 74956e45f6SSimon J. Gerraty typedef struct For { 75956e45f6SSimon J. Gerraty Buffer body; /* Unexpanded body of the loop */ 76956e45f6SSimon J. Gerraty Vector /* of ForVar */ vars; /* Iteration variables */ 77956e45f6SSimon J. Gerraty Words items; /* Substitution items */ 78956e45f6SSimon J. Gerraty Buffer curBody; /* Expanded body of the current iteration */ 79956e45f6SSimon J. Gerraty /* Is any of the names 1 character long? If so, when the variable values 80956e45f6SSimon J. Gerraty * are substituted, the parser must handle $V expressions as well, not 81956e45f6SSimon J. Gerraty * only ${V} and $(V). */ 82956e45f6SSimon J. Gerraty Boolean short_var; 83956e45f6SSimon J. Gerraty unsigned int sub_next; /* Where to continue iterating */ 843955d011SMarcel Moolenaar } For; 853955d011SMarcel Moolenaar 863955d011SMarcel Moolenaar static For *accumFor; /* Loop being accumulated */ 873955d011SMarcel Moolenaar 88956e45f6SSimon J. Gerraty static void 89956e45f6SSimon J. Gerraty ForAddVar(For *f, const char *name, size_t len) 90956e45f6SSimon J. Gerraty { 91956e45f6SSimon J. Gerraty ForVar *var = Vector_Push(&f->vars); 92956e45f6SSimon J. Gerraty var->name = bmake_strldup(name, len); 93*06b9b3e0SSimon J. Gerraty var->nameLen = len; 94956e45f6SSimon J. Gerraty } 953955d011SMarcel Moolenaar 963955d011SMarcel Moolenaar static void 97956e45f6SSimon J. Gerraty For_Free(For *f) 983955d011SMarcel Moolenaar { 99956e45f6SSimon J. Gerraty Buf_Destroy(&f->body, TRUE); 1003955d011SMarcel Moolenaar 101956e45f6SSimon J. Gerraty while (f->vars.len > 0) { 102956e45f6SSimon J. Gerraty ForVar *var = Vector_Pop(&f->vars); 103956e45f6SSimon J. Gerraty free(var->name); 104956e45f6SSimon J. Gerraty } 105956e45f6SSimon J. Gerraty Vector_Done(&f->vars); 106956e45f6SSimon J. Gerraty 107956e45f6SSimon J. Gerraty Words_Free(f->items); 108956e45f6SSimon J. Gerraty Buf_Destroy(&f->curBody, TRUE); 109956e45f6SSimon J. Gerraty 110956e45f6SSimon J. Gerraty free(f); 1113955d011SMarcel Moolenaar } 1123955d011SMarcel Moolenaar 113956e45f6SSimon J. Gerraty static Boolean 114956e45f6SSimon J. Gerraty IsFor(const char *p) 115956e45f6SSimon J. Gerraty { 116956e45f6SSimon J. Gerraty return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]); 117956e45f6SSimon J. Gerraty } 1183955d011SMarcel Moolenaar 119956e45f6SSimon J. Gerraty static Boolean 120956e45f6SSimon J. Gerraty IsEndfor(const char *p) 121956e45f6SSimon J. Gerraty { 122956e45f6SSimon J. Gerraty return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 && 123956e45f6SSimon J. Gerraty (p[6] == '\0' || ch_isspace(p[6])); 124956e45f6SSimon J. Gerraty } 125956e45f6SSimon J. Gerraty 126*06b9b3e0SSimon J. Gerraty /* 127*06b9b3e0SSimon J. Gerraty * Evaluate the for loop in the passed line. The line looks like this: 128956e45f6SSimon J. Gerraty * .for <varname...> in <value...> 129956e45f6SSimon J. Gerraty * 130956e45f6SSimon J. Gerraty * Input: 131956e45f6SSimon J. Gerraty * line Line to parse 132956e45f6SSimon J. Gerraty * 133956e45f6SSimon J. Gerraty * Results: 134956e45f6SSimon J. Gerraty * 0: Not a .for statement, parse the line 135956e45f6SSimon J. Gerraty * 1: We found a for loop 136956e45f6SSimon J. Gerraty * -1: A .for statement with a bad syntax error, discard. 137956e45f6SSimon J. Gerraty */ 138956e45f6SSimon J. Gerraty int 139956e45f6SSimon J. Gerraty For_Eval(const char *line) 140956e45f6SSimon J. Gerraty { 141956e45f6SSimon J. Gerraty For *f; 142956e45f6SSimon J. Gerraty const char *p; 143956e45f6SSimon J. Gerraty 144956e45f6SSimon J. Gerraty p = line + 1; /* skip the '.' */ 145956e45f6SSimon J. Gerraty cpp_skip_whitespace(&p); 146956e45f6SSimon J. Gerraty 147956e45f6SSimon J. Gerraty if (!IsFor(p)) { 148956e45f6SSimon J. Gerraty if (IsEndfor(p)) { 149956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "for-less endfor"); 150956e45f6SSimon J. Gerraty return -1; 151956e45f6SSimon J. Gerraty } 152956e45f6SSimon J. Gerraty return 0; 153956e45f6SSimon J. Gerraty } 154956e45f6SSimon J. Gerraty p += 3; 155956e45f6SSimon J. Gerraty 156956e45f6SSimon J. Gerraty /* 157956e45f6SSimon J. Gerraty * we found a for loop, and now we are going to parse it. 158956e45f6SSimon J. Gerraty */ 159956e45f6SSimon J. Gerraty 160956e45f6SSimon J. Gerraty f = bmake_malloc(sizeof *f); 161e2eeea75SSimon J. Gerraty Buf_Init(&f->body); 162956e45f6SSimon J. Gerraty Vector_Init(&f->vars, sizeof(ForVar)); 163956e45f6SSimon J. Gerraty f->items.words = NULL; 164956e45f6SSimon J. Gerraty f->items.freeIt = NULL; 165e2eeea75SSimon J. Gerraty Buf_Init(&f->curBody); 166956e45f6SSimon J. Gerraty f->short_var = FALSE; 167956e45f6SSimon J. Gerraty f->sub_next = 0; 168956e45f6SSimon J. Gerraty 169956e45f6SSimon J. Gerraty /* Grab the variables. Terminate on "in". */ 170956e45f6SSimon J. Gerraty for (;;) { 171956e45f6SSimon J. Gerraty size_t len; 172956e45f6SSimon J. Gerraty 173956e45f6SSimon J. Gerraty cpp_skip_whitespace(&p); 174956e45f6SSimon J. Gerraty if (*p == '\0') { 175956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "missing `in' in for"); 176956e45f6SSimon J. Gerraty For_Free(f); 177956e45f6SSimon J. Gerraty return -1; 178956e45f6SSimon J. Gerraty } 179956e45f6SSimon J. Gerraty 180*06b9b3e0SSimon J. Gerraty /* 181*06b9b3e0SSimon J. Gerraty * XXX: This allows arbitrary variable names; 182*06b9b3e0SSimon J. Gerraty * see directive-for.mk. 183*06b9b3e0SSimon J. Gerraty */ 184956e45f6SSimon J. Gerraty for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++) 185956e45f6SSimon J. Gerraty continue; 186956e45f6SSimon J. Gerraty 187956e45f6SSimon J. Gerraty if (len == 2 && p[0] == 'i' && p[1] == 'n') { 188956e45f6SSimon J. Gerraty p += 2; 189956e45f6SSimon J. Gerraty break; 190956e45f6SSimon J. Gerraty } 191956e45f6SSimon J. Gerraty if (len == 1) 192956e45f6SSimon J. Gerraty f->short_var = TRUE; 193956e45f6SSimon J. Gerraty 194956e45f6SSimon J. Gerraty ForAddVar(f, p, len); 195956e45f6SSimon J. Gerraty p += len; 196956e45f6SSimon J. Gerraty } 197956e45f6SSimon J. Gerraty 198956e45f6SSimon J. Gerraty if (f->vars.len == 0) { 199956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "no iteration variables in for"); 200956e45f6SSimon J. Gerraty For_Free(f); 201956e45f6SSimon J. Gerraty return -1; 202956e45f6SSimon J. Gerraty } 203956e45f6SSimon J. Gerraty 204956e45f6SSimon J. Gerraty cpp_skip_whitespace(&p); 205956e45f6SSimon J. Gerraty 206956e45f6SSimon J. Gerraty { 207956e45f6SSimon J. Gerraty char *items; 208*06b9b3e0SSimon J. Gerraty if (Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items) != VPR_OK) { 209*06b9b3e0SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Error in .for loop items"); 210*06b9b3e0SSimon J. Gerraty f->items.len = 0; 211*06b9b3e0SSimon J. Gerraty goto done; 212*06b9b3e0SSimon J. Gerraty } 213*06b9b3e0SSimon J. Gerraty 214956e45f6SSimon J. Gerraty f->items = Str_Words(items, FALSE); 215956e45f6SSimon J. Gerraty free(items); 216956e45f6SSimon J. Gerraty 217956e45f6SSimon J. Gerraty if (f->items.len == 1 && f->items.words[0][0] == '\0') 218956e45f6SSimon J. Gerraty f->items.len = 0; /* .for var in ${:U} */ 219956e45f6SSimon J. Gerraty } 220956e45f6SSimon J. Gerraty 221956e45f6SSimon J. Gerraty { 222956e45f6SSimon J. Gerraty size_t nitems, nvars; 223956e45f6SSimon J. Gerraty 224*06b9b3e0SSimon J. Gerraty if ((nitems = f->items.len) > 0 && 225*06b9b3e0SSimon J. Gerraty nitems % (nvars = f->vars.len) != 0) { 2263955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, 227*06b9b3e0SSimon J. Gerraty "Wrong number of words (%u) in .for " 228*06b9b3e0SSimon J. Gerraty "substitution list with %u variables", 229*06b9b3e0SSimon J. Gerraty (unsigned)nitems, (unsigned)nvars); 2303955d011SMarcel Moolenaar /* 231*06b9b3e0SSimon J. Gerraty * Return 'success' so that the body of the .for loop 232*06b9b3e0SSimon J. Gerraty * is accumulated. 2333955d011SMarcel Moolenaar * Remove all items so that the loop doesn't iterate. 2343955d011SMarcel Moolenaar */ 235956e45f6SSimon J. Gerraty f->items.len = 0; 2363955d011SMarcel Moolenaar } 2373955d011SMarcel Moolenaar } 2383955d011SMarcel Moolenaar 239*06b9b3e0SSimon J. Gerraty done: 240956e45f6SSimon J. Gerraty accumFor = f; 2413955d011SMarcel Moolenaar forLevel = 1; 2423955d011SMarcel Moolenaar return 1; 2433955d011SMarcel Moolenaar } 2443955d011SMarcel Moolenaar 2453955d011SMarcel Moolenaar /* 2463955d011SMarcel Moolenaar * Add another line to a .for loop. 247956e45f6SSimon J. Gerraty * Returns FALSE when the matching .endfor is reached. 2483955d011SMarcel Moolenaar */ 249956e45f6SSimon J. Gerraty Boolean 250956e45f6SSimon J. Gerraty For_Accum(const char *line) 2513955d011SMarcel Moolenaar { 252956e45f6SSimon J. Gerraty const char *ptr = line; 2533955d011SMarcel Moolenaar 2543955d011SMarcel Moolenaar if (*ptr == '.') { 255956e45f6SSimon J. Gerraty ptr++; 256956e45f6SSimon J. Gerraty cpp_skip_whitespace(&ptr); 2573955d011SMarcel Moolenaar 258956e45f6SSimon J. Gerraty if (IsEndfor(ptr)) { 259956e45f6SSimon J. Gerraty DEBUG1(FOR, "For: end for %d\n", forLevel); 2603955d011SMarcel Moolenaar if (--forLevel <= 0) 261956e45f6SSimon J. Gerraty return FALSE; 262956e45f6SSimon J. Gerraty } else if (IsFor(ptr)) { 2633955d011SMarcel Moolenaar forLevel++; 264956e45f6SSimon J. Gerraty DEBUG1(FOR, "For: new loop %d\n", forLevel); 2653955d011SMarcel Moolenaar } 2663955d011SMarcel Moolenaar } 2673955d011SMarcel Moolenaar 268956e45f6SSimon J. Gerraty Buf_AddStr(&accumFor->body, line); 269956e45f6SSimon J. Gerraty Buf_AddByte(&accumFor->body, '\n'); 270956e45f6SSimon J. Gerraty return TRUE; 2713955d011SMarcel Moolenaar } 2723955d011SMarcel Moolenaar 2733955d011SMarcel Moolenaar 2742c3632d1SSimon J. Gerraty static size_t 2753955d011SMarcel Moolenaar for_var_len(const char *var) 2763955d011SMarcel Moolenaar { 2773955d011SMarcel Moolenaar char ch, var_start, var_end; 2783955d011SMarcel Moolenaar int depth; 2792c3632d1SSimon J. Gerraty size_t len; 2803955d011SMarcel Moolenaar 2813955d011SMarcel Moolenaar var_start = *var; 282e2eeea75SSimon J. Gerraty if (var_start == '\0') 2833955d011SMarcel Moolenaar /* just escape the $ */ 2843955d011SMarcel Moolenaar return 0; 2853955d011SMarcel Moolenaar 2863955d011SMarcel Moolenaar if (var_start == '(') 2873955d011SMarcel Moolenaar var_end = ')'; 2883955d011SMarcel Moolenaar else if (var_start == '{') 2893955d011SMarcel Moolenaar var_end = '}'; 2903955d011SMarcel Moolenaar else 291*06b9b3e0SSimon J. Gerraty return 1; /* Single char variable */ 2923955d011SMarcel Moolenaar 2933955d011SMarcel Moolenaar depth = 1; 294e2eeea75SSimon J. Gerraty for (len = 1; (ch = var[len++]) != '\0';) { 2953955d011SMarcel Moolenaar if (ch == var_start) 2963955d011SMarcel Moolenaar depth++; 2973955d011SMarcel Moolenaar else if (ch == var_end && --depth == 0) 2983955d011SMarcel Moolenaar return len; 2993955d011SMarcel Moolenaar } 3003955d011SMarcel Moolenaar 3013955d011SMarcel Moolenaar /* Variable end not found, escape the $ */ 3023955d011SMarcel Moolenaar return 0; 3033955d011SMarcel Moolenaar } 3043955d011SMarcel Moolenaar 305*06b9b3e0SSimon J. Gerraty /* 306*06b9b3e0SSimon J. Gerraty * The .for loop substitutes the items as ${:U<value>...}, which means 307*06b9b3e0SSimon J. Gerraty * that characters that break this syntax must be backslash-escaped. 308*06b9b3e0SSimon J. Gerraty */ 309e2eeea75SSimon J. Gerraty static Boolean 310e2eeea75SSimon J. Gerraty NeedsEscapes(const char *word, char endc) 311e2eeea75SSimon J. Gerraty { 312e2eeea75SSimon J. Gerraty const char *p; 313e2eeea75SSimon J. Gerraty 314e2eeea75SSimon J. Gerraty for (p = word; *p != '\0'; p++) { 315e2eeea75SSimon J. Gerraty if (*p == ':' || *p == '$' || *p == '\\' || *p == endc) 316e2eeea75SSimon J. Gerraty return TRUE; 317e2eeea75SSimon J. Gerraty } 318e2eeea75SSimon J. Gerraty return FALSE; 319e2eeea75SSimon J. Gerraty } 320e2eeea75SSimon J. Gerraty 321*06b9b3e0SSimon J. Gerraty /* 322*06b9b3e0SSimon J. Gerraty * While expanding the body of a .for loop, write the item in the ${:U...} 323e2eeea75SSimon J. Gerraty * expression, escaping characters as needed. 324e2eeea75SSimon J. Gerraty * 325*06b9b3e0SSimon J. Gerraty * The result is later unescaped by ApplyModifier_Defined. 326*06b9b3e0SSimon J. Gerraty */ 3273955d011SMarcel Moolenaar static void 328*06b9b3e0SSimon J. Gerraty Buf_AddEscaped(Buffer *cmds, const char *item, char endc) 3293955d011SMarcel Moolenaar { 3303955d011SMarcel Moolenaar char ch; 3313955d011SMarcel Moolenaar 332*06b9b3e0SSimon J. Gerraty if (!NeedsEscapes(item, endc)) { 3332c3632d1SSimon J. Gerraty Buf_AddStr(cmds, item); 3343955d011SMarcel Moolenaar return; 3353955d011SMarcel Moolenaar } 3363955d011SMarcel Moolenaar 337*06b9b3e0SSimon J. Gerraty /* Escape ':', '$', '\\' and 'endc' - these will be removed later by 338956e45f6SSimon J. Gerraty * :U processing, see ApplyModifier_Defined. */ 339956e45f6SSimon J. Gerraty while ((ch = *item++) != '\0') { 3403955d011SMarcel Moolenaar if (ch == '$') { 3412c3632d1SSimon J. Gerraty size_t len = for_var_len(item); 3423955d011SMarcel Moolenaar if (len != 0) { 3432c3632d1SSimon J. Gerraty Buf_AddBytes(cmds, item - 1, len + 1); 3443955d011SMarcel Moolenaar item += len; 3453955d011SMarcel Moolenaar continue; 3463955d011SMarcel Moolenaar } 3473955d011SMarcel Moolenaar Buf_AddByte(cmds, '\\'); 348*06b9b3e0SSimon J. Gerraty } else if (ch == ':' || ch == '\\' || ch == endc) 3493955d011SMarcel Moolenaar Buf_AddByte(cmds, '\\'); 3503955d011SMarcel Moolenaar Buf_AddByte(cmds, ch); 3513955d011SMarcel Moolenaar } 3523955d011SMarcel Moolenaar } 3533955d011SMarcel Moolenaar 354*06b9b3e0SSimon J. Gerraty /* 355*06b9b3e0SSimon J. Gerraty * While expanding the body of a .for loop, replace the variable name of an 356*06b9b3e0SSimon J. Gerraty * expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue". 357*06b9b3e0SSimon J. Gerraty */ 358956e45f6SSimon J. Gerraty static void 359*06b9b3e0SSimon J. Gerraty SubstVarLong(For *f, const char **pp, const char *bodyEnd, char endc, 360*06b9b3e0SSimon J. Gerraty const char **inout_mark) 3613955d011SMarcel Moolenaar { 362956e45f6SSimon J. Gerraty size_t i; 363956e45f6SSimon J. Gerraty const char *p = *pp; 3643955d011SMarcel Moolenaar 365956e45f6SSimon J. Gerraty for (i = 0; i < f->vars.len; i++) { 366956e45f6SSimon J. Gerraty ForVar *forVar = Vector_Get(&f->vars, i); 367*06b9b3e0SSimon J. Gerraty char *varname = forVar->name; 368*06b9b3e0SSimon J. Gerraty size_t varnameLen = forVar->nameLen; 369956e45f6SSimon J. Gerraty 370*06b9b3e0SSimon J. Gerraty if (varnameLen >= (size_t)(bodyEnd - p)) 371*06b9b3e0SSimon J. Gerraty continue; 372*06b9b3e0SSimon J. Gerraty if (memcmp(p, varname, varnameLen) != 0) 373956e45f6SSimon J. Gerraty continue; 374956e45f6SSimon J. Gerraty /* XXX: why test for backslash here? */ 375*06b9b3e0SSimon J. Gerraty if (p[varnameLen] != ':' && p[varnameLen] != endc && 376*06b9b3e0SSimon J. Gerraty p[varnameLen] != '\\') 377956e45f6SSimon J. Gerraty continue; 378956e45f6SSimon J. Gerraty 379*06b9b3e0SSimon J. Gerraty /* 380*06b9b3e0SSimon J. Gerraty * Found a variable match. Skip over the variable name and 381*06b9b3e0SSimon J. Gerraty * instead add ':U<value>' to the current body. 382*06b9b3e0SSimon J. Gerraty */ 383956e45f6SSimon J. Gerraty Buf_AddBytesBetween(&f->curBody, *inout_mark, p); 384956e45f6SSimon J. Gerraty Buf_AddStr(&f->curBody, ":U"); 385*06b9b3e0SSimon J. Gerraty Buf_AddEscaped(&f->curBody, 386*06b9b3e0SSimon J. Gerraty f->items.words[f->sub_next + i], endc); 387956e45f6SSimon J. Gerraty 388*06b9b3e0SSimon J. Gerraty p += varnameLen; 389956e45f6SSimon J. Gerraty *inout_mark = p; 390956e45f6SSimon J. Gerraty *pp = p; 391956e45f6SSimon J. Gerraty return; 392956e45f6SSimon J. Gerraty } 393956e45f6SSimon J. Gerraty } 3943955d011SMarcel Moolenaar 3953955d011SMarcel Moolenaar /* 396*06b9b3e0SSimon J. Gerraty * While expanding the body of a .for loop, replace single-character 397*06b9b3e0SSimon J. Gerraty * variable expressions like $i with their ${:U...} expansion. 398*06b9b3e0SSimon J. Gerraty */ 399*06b9b3e0SSimon J. Gerraty static void 400*06b9b3e0SSimon J. Gerraty SubstVarShort(For *f, const char *p, const char **inout_mark) 401*06b9b3e0SSimon J. Gerraty { 402*06b9b3e0SSimon J. Gerraty const char ch = *p; 403*06b9b3e0SSimon J. Gerraty ForVar *vars; 404*06b9b3e0SSimon J. Gerraty size_t i; 405*06b9b3e0SSimon J. Gerraty 406*06b9b3e0SSimon J. Gerraty /* Skip $$ and stupid ones. */ 407*06b9b3e0SSimon J. Gerraty if (!f->short_var || strchr("}):$", ch) != NULL) 408*06b9b3e0SSimon J. Gerraty return; 409*06b9b3e0SSimon J. Gerraty 410*06b9b3e0SSimon J. Gerraty vars = Vector_Get(&f->vars, 0); 411*06b9b3e0SSimon J. Gerraty for (i = 0; i < f->vars.len; i++) { 412*06b9b3e0SSimon J. Gerraty const char *varname = vars[i].name; 413*06b9b3e0SSimon J. Gerraty if (varname[0] == ch && varname[1] == '\0') 414*06b9b3e0SSimon J. Gerraty goto found; 415*06b9b3e0SSimon J. Gerraty } 416*06b9b3e0SSimon J. Gerraty return; 417*06b9b3e0SSimon J. Gerraty 418*06b9b3e0SSimon J. Gerraty found: 419*06b9b3e0SSimon J. Gerraty /* Replace $<ch> with ${:U<value>} */ 420*06b9b3e0SSimon J. Gerraty Buf_AddBytesBetween(&f->curBody, *inout_mark, p), *inout_mark = p + 1; 421*06b9b3e0SSimon J. Gerraty Buf_AddStr(&f->curBody, "{:U"); 422*06b9b3e0SSimon J. Gerraty Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], '}'); 423*06b9b3e0SSimon J. Gerraty Buf_AddByte(&f->curBody, '}'); 424*06b9b3e0SSimon J. Gerraty } 425*06b9b3e0SSimon J. Gerraty 426*06b9b3e0SSimon J. Gerraty /* 427*06b9b3e0SSimon J. Gerraty * Compute the body for the current iteration by copying the unexpanded body, 428*06b9b3e0SSimon J. Gerraty * replacing the expressions for the iteration variables on the way. 429956e45f6SSimon J. Gerraty * 430*06b9b3e0SSimon J. Gerraty * Using variable expressions ensures that the .for loop can't generate 4313955d011SMarcel Moolenaar * syntax, and that the later parsing will still see a variable. 432*06b9b3e0SSimon J. Gerraty * This code assumes that the variable with the empty name will never be 433*06b9b3e0SSimon J. Gerraty * defined, see unit-tests/varname-empty.mk for more details. 4343955d011SMarcel Moolenaar * 435956e45f6SSimon J. Gerraty * The detection of substitutions of the loop control variable is naive. 4363955d011SMarcel Moolenaar * Many of the modifiers use \ to escape $ (not $) so it is possible 4373955d011SMarcel Moolenaar * to contrive a makefile where an unwanted substitution happens. 4383955d011SMarcel Moolenaar */ 439*06b9b3e0SSimon J. Gerraty static void 440*06b9b3e0SSimon J. Gerraty ForSubstBody(For *f) 441*06b9b3e0SSimon J. Gerraty { 442*06b9b3e0SSimon J. Gerraty const char *p, *bodyEnd; 443*06b9b3e0SSimon J. Gerraty const char *mark; /* where the last replacement left off */ 444*06b9b3e0SSimon J. Gerraty 445*06b9b3e0SSimon J. Gerraty Buf_Empty(&f->curBody); 446*06b9b3e0SSimon J. Gerraty 447*06b9b3e0SSimon J. Gerraty mark = f->body.data; 448*06b9b3e0SSimon J. Gerraty bodyEnd = f->body.data + f->body.len; 449*06b9b3e0SSimon J. Gerraty for (p = mark; (p = strchr(p, '$')) != NULL;) { 450*06b9b3e0SSimon J. Gerraty if (p[1] == '{' || p[1] == '(') { 451*06b9b3e0SSimon J. Gerraty p += 2; 452*06b9b3e0SSimon J. Gerraty SubstVarLong(f, &p, bodyEnd, p[-1] == '{' ? '}' : ')', 453*06b9b3e0SSimon J. Gerraty &mark); 454*06b9b3e0SSimon J. Gerraty } else if (p[1] != '\0') { 455*06b9b3e0SSimon J. Gerraty SubstVarShort(f, p + 1, &mark); 456*06b9b3e0SSimon J. Gerraty p += 2; 457*06b9b3e0SSimon J. Gerraty } else 458*06b9b3e0SSimon J. Gerraty break; 459*06b9b3e0SSimon J. Gerraty } 460*06b9b3e0SSimon J. Gerraty 461*06b9b3e0SSimon J. Gerraty Buf_AddBytesBetween(&f->curBody, mark, bodyEnd); 462*06b9b3e0SSimon J. Gerraty } 463*06b9b3e0SSimon J. Gerraty 464*06b9b3e0SSimon J. Gerraty /* 465*06b9b3e0SSimon J. Gerraty * Compute the body for the current iteration by copying the unexpanded body, 466*06b9b3e0SSimon J. Gerraty * replacing the expressions for the iteration variables on the way. 467*06b9b3e0SSimon J. Gerraty */ 468956e45f6SSimon J. Gerraty static char * 469*06b9b3e0SSimon J. Gerraty ForReadMore(void *v_arg, size_t *out_len) 470956e45f6SSimon J. Gerraty { 471956e45f6SSimon J. Gerraty For *f = v_arg; 4723955d011SMarcel Moolenaar 473*06b9b3e0SSimon J. Gerraty if (f->sub_next == f->items.len) { 474956e45f6SSimon J. Gerraty /* No more iterations */ 475956e45f6SSimon J. Gerraty For_Free(f); 476956e45f6SSimon J. Gerraty return NULL; 477956e45f6SSimon J. Gerraty } 478956e45f6SSimon J. Gerraty 479*06b9b3e0SSimon J. Gerraty ForSubstBody(f); 480*06b9b3e0SSimon J. Gerraty DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data); 481*06b9b3e0SSimon J. Gerraty f->sub_next += (unsigned int)f->vars.len; 482956e45f6SSimon J. Gerraty 483*06b9b3e0SSimon J. Gerraty *out_len = f->curBody.len; 484*06b9b3e0SSimon J. Gerraty return f->curBody.data; 4853955d011SMarcel Moolenaar } 4863955d011SMarcel Moolenaar 487*06b9b3e0SSimon J. Gerraty /* Run the .for loop, imitating the actions of an include file. */ 4883955d011SMarcel Moolenaar void 4893955d011SMarcel Moolenaar For_Run(int lineno) 4903955d011SMarcel Moolenaar { 491956e45f6SSimon J. Gerraty For *f = accumFor; 4923955d011SMarcel Moolenaar accumFor = NULL; 4933955d011SMarcel Moolenaar 494956e45f6SSimon J. Gerraty if (f->items.len == 0) { 495*06b9b3e0SSimon J. Gerraty /* 496*06b9b3e0SSimon J. Gerraty * Nothing to expand - possibly due to an earlier syntax 497*06b9b3e0SSimon J. Gerraty * error. 498*06b9b3e0SSimon J. Gerraty */ 499956e45f6SSimon J. Gerraty For_Free(f); 5003955d011SMarcel Moolenaar return; 5013955d011SMarcel Moolenaar } 5023955d011SMarcel Moolenaar 503*06b9b3e0SSimon J. Gerraty Parse_SetInput(NULL, lineno, -1, ForReadMore, f); 5043955d011SMarcel Moolenaar } 505