1*dba7b0efSSimon J. Gerraty /* $NetBSD: for.c,v 1.141 2021/02/04 21:33:13 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 32*dba7b0efSSimon J. Gerraty /* 33956e45f6SSimon J. Gerraty * Handling of .for/.endfor loops in a makefile. 343955d011SMarcel Moolenaar * 3506b9b3e0SSimon J. Gerraty * For loops have the form: 363955d011SMarcel Moolenaar * 37956e45f6SSimon J. Gerraty * .for <varname...> in <value...> 3806b9b3e0SSimon J. Gerraty * # the body 393955d011SMarcel Moolenaar * .endfor 403955d011SMarcel Moolenaar * 4106b9b3e0SSimon J. Gerraty * When a .for line is parsed, the following lines are copied to the body of 4206b9b3e0SSimon J. Gerraty * the .for loop, until the corresponding .endfor line is reached. In this 4306b9b3e0SSimon J. Gerraty * phase, the body is not yet evaluated. This also applies to any nested 4406b9b3e0SSimon J. Gerraty * .for loops. 453955d011SMarcel Moolenaar * 4606b9b3e0SSimon J. Gerraty * After reaching the .endfor, the values from the .for line are grouped 4706b9b3e0SSimon J. Gerraty * according to the number of variables. For each such group, the unexpanded 4806b9b3e0SSimon J. Gerraty * body is scanned for variable expressions, and those that match the variable 4906b9b3e0SSimon J. Gerraty * names are replaced with expressions of the form ${:U...} or $(:U...). 5006b9b3e0SSimon 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*dba7b0efSSimon J. Gerraty MAKE_RCSID("$NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $"); 62956e45f6SSimon J. Gerraty 633955d011SMarcel Moolenaar 64956e45f6SSimon J. Gerraty /* One of the variables to the left of the "in" in a .for loop. */ 65956e45f6SSimon J. Gerraty typedef struct ForVar { 66956e45f6SSimon J. Gerraty char *name; 6706b9b3e0SSimon J. Gerraty size_t nameLen; 68956e45f6SSimon J. Gerraty } ForVar; 69956e45f6SSimon J. Gerraty 70*dba7b0efSSimon J. Gerraty typedef struct ForLoop { 71956e45f6SSimon J. Gerraty Buffer body; /* Unexpanded body of the loop */ 72956e45f6SSimon J. Gerraty Vector /* of ForVar */ vars; /* Iteration variables */ 73956e45f6SSimon J. Gerraty Words items; /* Substitution items */ 74956e45f6SSimon J. Gerraty Buffer curBody; /* Expanded body of the current iteration */ 75956e45f6SSimon J. Gerraty /* Is any of the names 1 character long? If so, when the variable values 76956e45f6SSimon J. Gerraty * are substituted, the parser must handle $V expressions as well, not 77956e45f6SSimon J. Gerraty * only ${V} and $(V). */ 78956e45f6SSimon J. Gerraty Boolean short_var; 79956e45f6SSimon J. Gerraty unsigned int sub_next; /* Where to continue iterating */ 80*dba7b0efSSimon J. Gerraty } ForLoop; 813955d011SMarcel Moolenaar 823955d011SMarcel Moolenaar 83*dba7b0efSSimon J. Gerraty static ForLoop *accumFor; /* Loop being accumulated */ 84*dba7b0efSSimon J. Gerraty static int forLevel = 0; /* Nesting level */ 85*dba7b0efSSimon J. Gerraty 86*dba7b0efSSimon J. Gerraty 87*dba7b0efSSimon J. Gerraty static ForLoop * 88*dba7b0efSSimon J. Gerraty ForLoop_New(void) 89956e45f6SSimon J. Gerraty { 90*dba7b0efSSimon J. Gerraty ForLoop *f = bmake_malloc(sizeof *f); 91*dba7b0efSSimon J. Gerraty 92*dba7b0efSSimon J. Gerraty Buf_Init(&f->body); 93*dba7b0efSSimon J. Gerraty Vector_Init(&f->vars, sizeof(ForVar)); 94*dba7b0efSSimon J. Gerraty f->items.words = NULL; 95*dba7b0efSSimon J. Gerraty f->items.freeIt = NULL; 96*dba7b0efSSimon J. Gerraty Buf_Init(&f->curBody); 97*dba7b0efSSimon J. Gerraty f->short_var = FALSE; 98*dba7b0efSSimon J. Gerraty f->sub_next = 0; 99*dba7b0efSSimon J. Gerraty 100*dba7b0efSSimon J. Gerraty return f; 101956e45f6SSimon J. Gerraty } 1023955d011SMarcel Moolenaar 1033955d011SMarcel Moolenaar static void 104*dba7b0efSSimon J. Gerraty ForLoop_Free(ForLoop *f) 1053955d011SMarcel Moolenaar { 106*dba7b0efSSimon J. Gerraty Buf_Done(&f->body); 1073955d011SMarcel Moolenaar 108956e45f6SSimon J. Gerraty while (f->vars.len > 0) { 109956e45f6SSimon J. Gerraty ForVar *var = Vector_Pop(&f->vars); 110956e45f6SSimon J. Gerraty free(var->name); 111956e45f6SSimon J. Gerraty } 112956e45f6SSimon J. Gerraty Vector_Done(&f->vars); 113956e45f6SSimon J. Gerraty 114956e45f6SSimon J. Gerraty Words_Free(f->items); 115*dba7b0efSSimon J. Gerraty Buf_Done(&f->curBody); 116956e45f6SSimon J. Gerraty 117956e45f6SSimon J. Gerraty free(f); 1183955d011SMarcel Moolenaar } 1193955d011SMarcel Moolenaar 120*dba7b0efSSimon J. Gerraty static void 121*dba7b0efSSimon J. Gerraty ForLoop_AddVar(ForLoop *f, const char *name, size_t len) 122*dba7b0efSSimon J. Gerraty { 123*dba7b0efSSimon J. Gerraty ForVar *var = Vector_Push(&f->vars); 124*dba7b0efSSimon J. Gerraty var->name = bmake_strldup(name, len); 125*dba7b0efSSimon J. Gerraty var->nameLen = len; 126*dba7b0efSSimon J. Gerraty } 127*dba7b0efSSimon J. Gerraty 128*dba7b0efSSimon J. Gerraty static Boolean 129*dba7b0efSSimon J. Gerraty ForLoop_ParseVarnames(ForLoop *f, const char **pp) 130*dba7b0efSSimon J. Gerraty { 131*dba7b0efSSimon J. Gerraty const char *p = *pp; 132*dba7b0efSSimon J. Gerraty 133*dba7b0efSSimon J. Gerraty for (;;) { 134*dba7b0efSSimon J. Gerraty size_t len; 135*dba7b0efSSimon J. Gerraty 136*dba7b0efSSimon J. Gerraty cpp_skip_whitespace(&p); 137*dba7b0efSSimon J. Gerraty if (*p == '\0') { 138*dba7b0efSSimon J. Gerraty Parse_Error(PARSE_FATAL, "missing `in' in for"); 139*dba7b0efSSimon J. Gerraty return FALSE; 140*dba7b0efSSimon J. Gerraty } 141*dba7b0efSSimon J. Gerraty 142*dba7b0efSSimon J. Gerraty /* 143*dba7b0efSSimon J. Gerraty * XXX: This allows arbitrary variable names; 144*dba7b0efSSimon J. Gerraty * see directive-for.mk. 145*dba7b0efSSimon J. Gerraty */ 146*dba7b0efSSimon J. Gerraty for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++) 147*dba7b0efSSimon J. Gerraty continue; 148*dba7b0efSSimon J. Gerraty 149*dba7b0efSSimon J. Gerraty if (len == 2 && p[0] == 'i' && p[1] == 'n') { 150*dba7b0efSSimon J. Gerraty p += 2; 151*dba7b0efSSimon J. Gerraty break; 152*dba7b0efSSimon J. Gerraty } 153*dba7b0efSSimon J. Gerraty if (len == 1) 154*dba7b0efSSimon J. Gerraty f->short_var = TRUE; 155*dba7b0efSSimon J. Gerraty 156*dba7b0efSSimon J. Gerraty ForLoop_AddVar(f, p, len); 157*dba7b0efSSimon J. Gerraty p += len; 158*dba7b0efSSimon J. Gerraty } 159*dba7b0efSSimon J. Gerraty 160*dba7b0efSSimon J. Gerraty if (f->vars.len == 0) { 161*dba7b0efSSimon J. Gerraty Parse_Error(PARSE_FATAL, "no iteration variables in for"); 162*dba7b0efSSimon J. Gerraty return FALSE; 163*dba7b0efSSimon J. Gerraty } 164*dba7b0efSSimon J. Gerraty 165*dba7b0efSSimon J. Gerraty *pp = p; 166*dba7b0efSSimon J. Gerraty return TRUE; 167*dba7b0efSSimon J. Gerraty } 168*dba7b0efSSimon J. Gerraty 169*dba7b0efSSimon J. Gerraty static Boolean 170*dba7b0efSSimon J. Gerraty ForLoop_ParseItems(ForLoop *f, const char *p) 171*dba7b0efSSimon J. Gerraty { 172*dba7b0efSSimon J. Gerraty char *items; 173*dba7b0efSSimon J. Gerraty 174*dba7b0efSSimon J. Gerraty cpp_skip_whitespace(&p); 175*dba7b0efSSimon J. Gerraty 176*dba7b0efSSimon J. Gerraty if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) { 177*dba7b0efSSimon J. Gerraty Parse_Error(PARSE_FATAL, "Error in .for loop items"); 178*dba7b0efSSimon J. Gerraty return FALSE; 179*dba7b0efSSimon J. Gerraty } 180*dba7b0efSSimon J. Gerraty 181*dba7b0efSSimon J. Gerraty f->items = Str_Words(items, FALSE); 182*dba7b0efSSimon J. Gerraty free(items); 183*dba7b0efSSimon J. Gerraty 184*dba7b0efSSimon J. Gerraty if (f->items.len == 1 && f->items.words[0][0] == '\0') 185*dba7b0efSSimon J. Gerraty f->items.len = 0; /* .for var in ${:U} */ 186*dba7b0efSSimon J. Gerraty 187*dba7b0efSSimon J. Gerraty if (f->items.len != 0 && f->items.len % f->vars.len != 0) { 188*dba7b0efSSimon J. Gerraty Parse_Error(PARSE_FATAL, 189*dba7b0efSSimon J. Gerraty "Wrong number of words (%u) in .for " 190*dba7b0efSSimon J. Gerraty "substitution list with %u variables", 191*dba7b0efSSimon J. Gerraty (unsigned)f->items.len, (unsigned)f->vars.len); 192*dba7b0efSSimon J. Gerraty return FALSE; 193*dba7b0efSSimon J. Gerraty } 194*dba7b0efSSimon J. Gerraty 195*dba7b0efSSimon J. Gerraty return TRUE; 196*dba7b0efSSimon J. Gerraty } 197*dba7b0efSSimon J. Gerraty 198956e45f6SSimon J. Gerraty static Boolean 199956e45f6SSimon J. Gerraty IsFor(const char *p) 200956e45f6SSimon J. Gerraty { 201956e45f6SSimon J. Gerraty return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]); 202956e45f6SSimon J. Gerraty } 2033955d011SMarcel Moolenaar 204956e45f6SSimon J. Gerraty static Boolean 205956e45f6SSimon J. Gerraty IsEndfor(const char *p) 206956e45f6SSimon J. Gerraty { 207956e45f6SSimon J. Gerraty return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 && 208956e45f6SSimon J. Gerraty (p[6] == '\0' || ch_isspace(p[6])); 209956e45f6SSimon J. Gerraty } 210956e45f6SSimon J. Gerraty 21106b9b3e0SSimon J. Gerraty /* 21206b9b3e0SSimon J. Gerraty * Evaluate the for loop in the passed line. The line looks like this: 213956e45f6SSimon J. Gerraty * .for <varname...> in <value...> 214956e45f6SSimon J. Gerraty * 215956e45f6SSimon J. Gerraty * Input: 216956e45f6SSimon J. Gerraty * line Line to parse 217956e45f6SSimon J. Gerraty * 218956e45f6SSimon J. Gerraty * Results: 219956e45f6SSimon J. Gerraty * 0: Not a .for statement, parse the line 220956e45f6SSimon J. Gerraty * 1: We found a for loop 221956e45f6SSimon J. Gerraty * -1: A .for statement with a bad syntax error, discard. 222956e45f6SSimon J. Gerraty */ 223956e45f6SSimon J. Gerraty int 224956e45f6SSimon J. Gerraty For_Eval(const char *line) 225956e45f6SSimon J. Gerraty { 226*dba7b0efSSimon J. Gerraty ForLoop *f; 227956e45f6SSimon J. Gerraty const char *p; 228956e45f6SSimon J. Gerraty 229956e45f6SSimon J. Gerraty p = line + 1; /* skip the '.' */ 230956e45f6SSimon J. Gerraty cpp_skip_whitespace(&p); 231956e45f6SSimon J. Gerraty 232956e45f6SSimon J. Gerraty if (!IsFor(p)) { 233956e45f6SSimon J. Gerraty if (IsEndfor(p)) { 234956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "for-less endfor"); 235956e45f6SSimon J. Gerraty return -1; 236956e45f6SSimon J. Gerraty } 237956e45f6SSimon J. Gerraty return 0; 238956e45f6SSimon J. Gerraty } 239956e45f6SSimon J. Gerraty p += 3; 240956e45f6SSimon J. Gerraty 241*dba7b0efSSimon J. Gerraty f = ForLoop_New(); 242956e45f6SSimon J. Gerraty 243*dba7b0efSSimon J. Gerraty if (!ForLoop_ParseVarnames(f, &p)) { 244*dba7b0efSSimon J. Gerraty ForLoop_Free(f); 245956e45f6SSimon J. Gerraty return -1; 246956e45f6SSimon J. Gerraty } 247956e45f6SSimon J. Gerraty 248*dba7b0efSSimon J. Gerraty if (!ForLoop_ParseItems(f, p)) { 249*dba7b0efSSimon J. Gerraty /* Continue parsing the .for loop, but don't iterate. */ 250956e45f6SSimon J. Gerraty f->items.len = 0; 2513955d011SMarcel Moolenaar } 2523955d011SMarcel Moolenaar 253956e45f6SSimon J. Gerraty accumFor = f; 2543955d011SMarcel Moolenaar forLevel = 1; 2553955d011SMarcel Moolenaar return 1; 2563955d011SMarcel Moolenaar } 2573955d011SMarcel Moolenaar 2583955d011SMarcel Moolenaar /* 259*dba7b0efSSimon J. Gerraty * Add another line to the .for loop that is being built up. 260956e45f6SSimon J. Gerraty * Returns FALSE when the matching .endfor is reached. 2613955d011SMarcel Moolenaar */ 262956e45f6SSimon J. Gerraty Boolean 263956e45f6SSimon J. Gerraty For_Accum(const char *line) 2643955d011SMarcel Moolenaar { 265*dba7b0efSSimon J. Gerraty const char *p = line; 2663955d011SMarcel Moolenaar 267*dba7b0efSSimon J. Gerraty if (*p == '.') { 268*dba7b0efSSimon J. Gerraty p++; 269*dba7b0efSSimon J. Gerraty cpp_skip_whitespace(&p); 2703955d011SMarcel Moolenaar 271*dba7b0efSSimon J. Gerraty if (IsEndfor(p)) { 272956e45f6SSimon J. Gerraty DEBUG1(FOR, "For: end for %d\n", forLevel); 2733955d011SMarcel Moolenaar if (--forLevel <= 0) 274956e45f6SSimon J. Gerraty return FALSE; 275*dba7b0efSSimon J. Gerraty } else if (IsFor(p)) { 2763955d011SMarcel Moolenaar forLevel++; 277956e45f6SSimon J. Gerraty DEBUG1(FOR, "For: new loop %d\n", forLevel); 2783955d011SMarcel Moolenaar } 2793955d011SMarcel Moolenaar } 2803955d011SMarcel Moolenaar 281956e45f6SSimon J. Gerraty Buf_AddStr(&accumFor->body, line); 282956e45f6SSimon J. Gerraty Buf_AddByte(&accumFor->body, '\n'); 283956e45f6SSimon J. Gerraty return TRUE; 2843955d011SMarcel Moolenaar } 2853955d011SMarcel Moolenaar 2863955d011SMarcel Moolenaar 2872c3632d1SSimon J. Gerraty static size_t 2883955d011SMarcel Moolenaar for_var_len(const char *var) 2893955d011SMarcel Moolenaar { 2903955d011SMarcel Moolenaar char ch, var_start, var_end; 2913955d011SMarcel Moolenaar int depth; 2922c3632d1SSimon J. Gerraty size_t len; 2933955d011SMarcel Moolenaar 2943955d011SMarcel Moolenaar var_start = *var; 295e2eeea75SSimon J. Gerraty if (var_start == '\0') 2963955d011SMarcel Moolenaar /* just escape the $ */ 2973955d011SMarcel Moolenaar return 0; 2983955d011SMarcel Moolenaar 2993955d011SMarcel Moolenaar if (var_start == '(') 3003955d011SMarcel Moolenaar var_end = ')'; 3013955d011SMarcel Moolenaar else if (var_start == '{') 3023955d011SMarcel Moolenaar var_end = '}'; 3033955d011SMarcel Moolenaar else 30406b9b3e0SSimon J. Gerraty return 1; /* Single char variable */ 3053955d011SMarcel Moolenaar 3063955d011SMarcel Moolenaar depth = 1; 307e2eeea75SSimon J. Gerraty for (len = 1; (ch = var[len++]) != '\0';) { 3083955d011SMarcel Moolenaar if (ch == var_start) 3093955d011SMarcel Moolenaar depth++; 3103955d011SMarcel Moolenaar else if (ch == var_end && --depth == 0) 3113955d011SMarcel Moolenaar return len; 3123955d011SMarcel Moolenaar } 3133955d011SMarcel Moolenaar 3143955d011SMarcel Moolenaar /* Variable end not found, escape the $ */ 3153955d011SMarcel Moolenaar return 0; 3163955d011SMarcel Moolenaar } 3173955d011SMarcel Moolenaar 31806b9b3e0SSimon J. Gerraty /* 31906b9b3e0SSimon J. Gerraty * The .for loop substitutes the items as ${:U<value>...}, which means 32006b9b3e0SSimon J. Gerraty * that characters that break this syntax must be backslash-escaped. 32106b9b3e0SSimon J. Gerraty */ 322e2eeea75SSimon J. Gerraty static Boolean 323*dba7b0efSSimon J. Gerraty NeedsEscapes(const char *value, char endc) 324e2eeea75SSimon J. Gerraty { 325e2eeea75SSimon J. Gerraty const char *p; 326e2eeea75SSimon J. Gerraty 327*dba7b0efSSimon J. Gerraty for (p = value; *p != '\0'; p++) { 328e2eeea75SSimon J. Gerraty if (*p == ':' || *p == '$' || *p == '\\' || *p == endc) 329e2eeea75SSimon J. Gerraty return TRUE; 330e2eeea75SSimon J. Gerraty } 331e2eeea75SSimon J. Gerraty return FALSE; 332e2eeea75SSimon J. Gerraty } 333e2eeea75SSimon J. Gerraty 33406b9b3e0SSimon J. Gerraty /* 33506b9b3e0SSimon J. Gerraty * While expanding the body of a .for loop, write the item in the ${:U...} 336e2eeea75SSimon J. Gerraty * expression, escaping characters as needed. 337e2eeea75SSimon J. Gerraty * 33806b9b3e0SSimon J. Gerraty * The result is later unescaped by ApplyModifier_Defined. 33906b9b3e0SSimon J. Gerraty */ 3403955d011SMarcel Moolenaar static void 34106b9b3e0SSimon J. Gerraty Buf_AddEscaped(Buffer *cmds, const char *item, char endc) 3423955d011SMarcel Moolenaar { 3433955d011SMarcel Moolenaar char ch; 3443955d011SMarcel Moolenaar 34506b9b3e0SSimon J. Gerraty if (!NeedsEscapes(item, endc)) { 3462c3632d1SSimon J. Gerraty Buf_AddStr(cmds, item); 3473955d011SMarcel Moolenaar return; 3483955d011SMarcel Moolenaar } 3493955d011SMarcel Moolenaar 35006b9b3e0SSimon J. Gerraty /* Escape ':', '$', '\\' and 'endc' - these will be removed later by 351956e45f6SSimon J. Gerraty * :U processing, see ApplyModifier_Defined. */ 352956e45f6SSimon J. Gerraty while ((ch = *item++) != '\0') { 3533955d011SMarcel Moolenaar if (ch == '$') { 3542c3632d1SSimon J. Gerraty size_t len = for_var_len(item); 3553955d011SMarcel Moolenaar if (len != 0) { 3562c3632d1SSimon J. Gerraty Buf_AddBytes(cmds, item - 1, len + 1); 3573955d011SMarcel Moolenaar item += len; 3583955d011SMarcel Moolenaar continue; 3593955d011SMarcel Moolenaar } 3603955d011SMarcel Moolenaar Buf_AddByte(cmds, '\\'); 36106b9b3e0SSimon J. Gerraty } else if (ch == ':' || ch == '\\' || ch == endc) 3623955d011SMarcel Moolenaar Buf_AddByte(cmds, '\\'); 3633955d011SMarcel Moolenaar Buf_AddByte(cmds, ch); 3643955d011SMarcel Moolenaar } 3653955d011SMarcel Moolenaar } 3663955d011SMarcel Moolenaar 36706b9b3e0SSimon J. Gerraty /* 36806b9b3e0SSimon J. Gerraty * While expanding the body of a .for loop, replace the variable name of an 36906b9b3e0SSimon J. Gerraty * expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue". 37006b9b3e0SSimon J. Gerraty */ 371956e45f6SSimon J. Gerraty static void 372*dba7b0efSSimon J. Gerraty ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd, 373*dba7b0efSSimon J. Gerraty char endc, const char **inout_mark) 3743955d011SMarcel Moolenaar { 375956e45f6SSimon J. Gerraty size_t i; 376956e45f6SSimon J. Gerraty const char *p = *pp; 3773955d011SMarcel Moolenaar 378956e45f6SSimon J. Gerraty for (i = 0; i < f->vars.len; i++) { 379956e45f6SSimon J. Gerraty ForVar *forVar = Vector_Get(&f->vars, i); 38006b9b3e0SSimon J. Gerraty char *varname = forVar->name; 38106b9b3e0SSimon J. Gerraty size_t varnameLen = forVar->nameLen; 382956e45f6SSimon J. Gerraty 38306b9b3e0SSimon J. Gerraty if (varnameLen >= (size_t)(bodyEnd - p)) 38406b9b3e0SSimon J. Gerraty continue; 38506b9b3e0SSimon J. Gerraty if (memcmp(p, varname, varnameLen) != 0) 386956e45f6SSimon J. Gerraty continue; 387956e45f6SSimon J. Gerraty /* XXX: why test for backslash here? */ 38806b9b3e0SSimon J. Gerraty if (p[varnameLen] != ':' && p[varnameLen] != endc && 38906b9b3e0SSimon J. Gerraty p[varnameLen] != '\\') 390956e45f6SSimon J. Gerraty continue; 391956e45f6SSimon J. Gerraty 39206b9b3e0SSimon J. Gerraty /* 39306b9b3e0SSimon J. Gerraty * Found a variable match. Skip over the variable name and 39406b9b3e0SSimon J. Gerraty * instead add ':U<value>' to the current body. 39506b9b3e0SSimon J. Gerraty */ 396956e45f6SSimon J. Gerraty Buf_AddBytesBetween(&f->curBody, *inout_mark, p); 397956e45f6SSimon J. Gerraty Buf_AddStr(&f->curBody, ":U"); 39806b9b3e0SSimon J. Gerraty Buf_AddEscaped(&f->curBody, 39906b9b3e0SSimon J. Gerraty f->items.words[f->sub_next + i], endc); 400956e45f6SSimon J. Gerraty 40106b9b3e0SSimon J. Gerraty p += varnameLen; 402956e45f6SSimon J. Gerraty *inout_mark = p; 403956e45f6SSimon J. Gerraty *pp = p; 404956e45f6SSimon J. Gerraty return; 405956e45f6SSimon J. Gerraty } 406956e45f6SSimon J. Gerraty } 4073955d011SMarcel Moolenaar 4083955d011SMarcel Moolenaar /* 40906b9b3e0SSimon J. Gerraty * While expanding the body of a .for loop, replace single-character 41006b9b3e0SSimon J. Gerraty * variable expressions like $i with their ${:U...} expansion. 41106b9b3e0SSimon J. Gerraty */ 41206b9b3e0SSimon J. Gerraty static void 413*dba7b0efSSimon J. Gerraty ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark) 41406b9b3e0SSimon J. Gerraty { 41506b9b3e0SSimon J. Gerraty const char ch = *p; 41606b9b3e0SSimon J. Gerraty ForVar *vars; 41706b9b3e0SSimon J. Gerraty size_t i; 41806b9b3e0SSimon J. Gerraty 41906b9b3e0SSimon J. Gerraty /* Skip $$ and stupid ones. */ 42006b9b3e0SSimon J. Gerraty if (!f->short_var || strchr("}):$", ch) != NULL) 42106b9b3e0SSimon J. Gerraty return; 42206b9b3e0SSimon J. Gerraty 42306b9b3e0SSimon J. Gerraty vars = Vector_Get(&f->vars, 0); 42406b9b3e0SSimon J. Gerraty for (i = 0; i < f->vars.len; i++) { 42506b9b3e0SSimon J. Gerraty const char *varname = vars[i].name; 42606b9b3e0SSimon J. Gerraty if (varname[0] == ch && varname[1] == '\0') 42706b9b3e0SSimon J. Gerraty goto found; 42806b9b3e0SSimon J. Gerraty } 42906b9b3e0SSimon J. Gerraty return; 43006b9b3e0SSimon J. Gerraty 43106b9b3e0SSimon J. Gerraty found: 43206b9b3e0SSimon J. Gerraty /* Replace $<ch> with ${:U<value>} */ 43306b9b3e0SSimon J. Gerraty Buf_AddBytesBetween(&f->curBody, *inout_mark, p), *inout_mark = p + 1; 43406b9b3e0SSimon J. Gerraty Buf_AddStr(&f->curBody, "{:U"); 43506b9b3e0SSimon J. Gerraty Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], '}'); 43606b9b3e0SSimon J. Gerraty Buf_AddByte(&f->curBody, '}'); 43706b9b3e0SSimon J. Gerraty } 43806b9b3e0SSimon J. Gerraty 43906b9b3e0SSimon J. Gerraty /* 44006b9b3e0SSimon J. Gerraty * Compute the body for the current iteration by copying the unexpanded body, 44106b9b3e0SSimon J. Gerraty * replacing the expressions for the iteration variables on the way. 442956e45f6SSimon J. Gerraty * 44306b9b3e0SSimon J. Gerraty * Using variable expressions ensures that the .for loop can't generate 4443955d011SMarcel Moolenaar * syntax, and that the later parsing will still see a variable. 44506b9b3e0SSimon J. Gerraty * This code assumes that the variable with the empty name will never be 44606b9b3e0SSimon J. Gerraty * defined, see unit-tests/varname-empty.mk for more details. 4473955d011SMarcel Moolenaar * 448*dba7b0efSSimon J. Gerraty * The detection of substitutions of the loop control variables is naive. 449*dba7b0efSSimon J. Gerraty * Many of the modifiers use '\' to escape '$' (not '$'), so it is possible 4503955d011SMarcel Moolenaar * to contrive a makefile where an unwanted substitution happens. 4513955d011SMarcel Moolenaar */ 45206b9b3e0SSimon J. Gerraty static void 453*dba7b0efSSimon J. Gerraty ForLoop_SubstBody(ForLoop *f) 45406b9b3e0SSimon J. Gerraty { 45506b9b3e0SSimon J. Gerraty const char *p, *bodyEnd; 45606b9b3e0SSimon J. Gerraty const char *mark; /* where the last replacement left off */ 45706b9b3e0SSimon J. Gerraty 45806b9b3e0SSimon J. Gerraty Buf_Empty(&f->curBody); 45906b9b3e0SSimon J. Gerraty 46006b9b3e0SSimon J. Gerraty mark = f->body.data; 46106b9b3e0SSimon J. Gerraty bodyEnd = f->body.data + f->body.len; 46206b9b3e0SSimon J. Gerraty for (p = mark; (p = strchr(p, '$')) != NULL;) { 46306b9b3e0SSimon J. Gerraty if (p[1] == '{' || p[1] == '(') { 46406b9b3e0SSimon J. Gerraty p += 2; 465*dba7b0efSSimon J. Gerraty ForLoop_SubstVarLong(f, &p, bodyEnd, 466*dba7b0efSSimon J. Gerraty p[-1] == '{' ? '}' : ')', &mark); 46706b9b3e0SSimon J. Gerraty } else if (p[1] != '\0') { 468*dba7b0efSSimon J. Gerraty ForLoop_SubstVarShort(f, p + 1, &mark); 46906b9b3e0SSimon J. Gerraty p += 2; 47006b9b3e0SSimon J. Gerraty } else 47106b9b3e0SSimon J. Gerraty break; 47206b9b3e0SSimon J. Gerraty } 47306b9b3e0SSimon J. Gerraty 47406b9b3e0SSimon J. Gerraty Buf_AddBytesBetween(&f->curBody, mark, bodyEnd); 47506b9b3e0SSimon J. Gerraty } 47606b9b3e0SSimon J. Gerraty 47706b9b3e0SSimon J. Gerraty /* 47806b9b3e0SSimon J. Gerraty * Compute the body for the current iteration by copying the unexpanded body, 47906b9b3e0SSimon J. Gerraty * replacing the expressions for the iteration variables on the way. 48006b9b3e0SSimon J. Gerraty */ 481956e45f6SSimon J. Gerraty static char * 48206b9b3e0SSimon J. Gerraty ForReadMore(void *v_arg, size_t *out_len) 483956e45f6SSimon J. Gerraty { 484*dba7b0efSSimon J. Gerraty ForLoop *f = v_arg; 4853955d011SMarcel Moolenaar 48606b9b3e0SSimon J. Gerraty if (f->sub_next == f->items.len) { 487956e45f6SSimon J. Gerraty /* No more iterations */ 488*dba7b0efSSimon J. Gerraty ForLoop_Free(f); 489956e45f6SSimon J. Gerraty return NULL; 490956e45f6SSimon J. Gerraty } 491956e45f6SSimon J. Gerraty 492*dba7b0efSSimon J. Gerraty ForLoop_SubstBody(f); 49306b9b3e0SSimon J. Gerraty DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data); 49406b9b3e0SSimon J. Gerraty f->sub_next += (unsigned int)f->vars.len; 495956e45f6SSimon J. Gerraty 49606b9b3e0SSimon J. Gerraty *out_len = f->curBody.len; 49706b9b3e0SSimon J. Gerraty return f->curBody.data; 4983955d011SMarcel Moolenaar } 4993955d011SMarcel Moolenaar 50006b9b3e0SSimon J. Gerraty /* Run the .for loop, imitating the actions of an include file. */ 5013955d011SMarcel Moolenaar void 5023955d011SMarcel Moolenaar For_Run(int lineno) 5033955d011SMarcel Moolenaar { 504*dba7b0efSSimon J. Gerraty ForLoop *f = accumFor; 5053955d011SMarcel Moolenaar accumFor = NULL; 5063955d011SMarcel Moolenaar 507956e45f6SSimon J. Gerraty if (f->items.len == 0) { 50806b9b3e0SSimon J. Gerraty /* 50906b9b3e0SSimon J. Gerraty * Nothing to expand - possibly due to an earlier syntax 51006b9b3e0SSimon J. Gerraty * error. 51106b9b3e0SSimon J. Gerraty */ 512*dba7b0efSSimon J. Gerraty ForLoop_Free(f); 5133955d011SMarcel Moolenaar return; 5143955d011SMarcel Moolenaar } 5153955d011SMarcel Moolenaar 51606b9b3e0SSimon J. Gerraty Parse_SetInput(NULL, lineno, -1, ForReadMore, f); 5173955d011SMarcel Moolenaar } 518