1*2c3632d1SSimon J. Gerraty /* $NetBSD: for.c,v 1.67 2020/08/30 19:56:02 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 #ifndef MAKE_NATIVE 33*2c3632d1SSimon J. Gerraty static char rcsid[] = "$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $"; 343955d011SMarcel Moolenaar #else 353955d011SMarcel Moolenaar #include <sys/cdefs.h> 363955d011SMarcel Moolenaar #ifndef lint 373955d011SMarcel Moolenaar #if 0 383955d011SMarcel Moolenaar static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93"; 393955d011SMarcel Moolenaar #else 40*2c3632d1SSimon J. Gerraty __RCSID("$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $"); 413955d011SMarcel Moolenaar #endif 423955d011SMarcel Moolenaar #endif /* not lint */ 433955d011SMarcel Moolenaar #endif 443955d011SMarcel Moolenaar 453955d011SMarcel Moolenaar /*- 463955d011SMarcel Moolenaar * for.c -- 473955d011SMarcel Moolenaar * Functions to handle loops in a makefile. 483955d011SMarcel Moolenaar * 493955d011SMarcel Moolenaar * Interface: 503955d011SMarcel Moolenaar * For_Eval Evaluate the loop in the passed line. 513955d011SMarcel Moolenaar * For_Run Run accumulated loop 523955d011SMarcel Moolenaar * 533955d011SMarcel Moolenaar */ 543955d011SMarcel Moolenaar 553955d011SMarcel Moolenaar #include "make.h" 563955d011SMarcel Moolenaar #include "strlist.h" 573955d011SMarcel Moolenaar 583955d011SMarcel Moolenaar #define FOR_SUB_ESCAPE_CHAR 1 593955d011SMarcel Moolenaar #define FOR_SUB_ESCAPE_BRACE 2 603955d011SMarcel Moolenaar #define FOR_SUB_ESCAPE_PAREN 4 613955d011SMarcel Moolenaar 623955d011SMarcel Moolenaar /* 633955d011SMarcel Moolenaar * For statements are of the form: 643955d011SMarcel Moolenaar * 653955d011SMarcel Moolenaar * .for <variable> in <varlist> 663955d011SMarcel Moolenaar * ... 673955d011SMarcel Moolenaar * .endfor 683955d011SMarcel Moolenaar * 693955d011SMarcel Moolenaar * The trick is to look for the matching end inside for for loop 703955d011SMarcel Moolenaar * To do that, we count the current nesting level of the for loops. 713955d011SMarcel Moolenaar * and the .endfor statements, accumulating all the statements between 723955d011SMarcel Moolenaar * the initial .for loop and the matching .endfor; 733955d011SMarcel Moolenaar * then we evaluate the for loop for each variable in the varlist. 743955d011SMarcel Moolenaar * 753955d011SMarcel Moolenaar * Note that any nested fors are just passed through; they get handled 763955d011SMarcel Moolenaar * recursively in For_Eval when we're expanding the enclosing for in 773955d011SMarcel Moolenaar * For_Run. 783955d011SMarcel Moolenaar */ 793955d011SMarcel Moolenaar 803955d011SMarcel Moolenaar static int forLevel = 0; /* Nesting level */ 813955d011SMarcel Moolenaar 823955d011SMarcel Moolenaar /* 833955d011SMarcel Moolenaar * State of a for loop. 843955d011SMarcel Moolenaar */ 85*2c3632d1SSimon J. Gerraty typedef struct { 863955d011SMarcel Moolenaar Buffer buf; /* Body of loop */ 873955d011SMarcel Moolenaar strlist_t vars; /* Iteration variables */ 883955d011SMarcel Moolenaar strlist_t items; /* Substitution items */ 893955d011SMarcel Moolenaar char *parse_buf; 903955d011SMarcel Moolenaar int short_var; 913955d011SMarcel Moolenaar int sub_next; 923955d011SMarcel Moolenaar } For; 933955d011SMarcel Moolenaar 943955d011SMarcel Moolenaar static For *accumFor; /* Loop being accumulated */ 953955d011SMarcel Moolenaar 963955d011SMarcel Moolenaar 973955d011SMarcel Moolenaar static void 983955d011SMarcel Moolenaar For_Free(For *arg) 993955d011SMarcel Moolenaar { 1003955d011SMarcel Moolenaar Buf_Destroy(&arg->buf, TRUE); 1013955d011SMarcel Moolenaar strlist_clean(&arg->vars); 1023955d011SMarcel Moolenaar strlist_clean(&arg->items); 1033955d011SMarcel Moolenaar free(arg->parse_buf); 1043955d011SMarcel Moolenaar 1053955d011SMarcel Moolenaar free(arg); 1063955d011SMarcel Moolenaar } 1073955d011SMarcel Moolenaar 1083955d011SMarcel Moolenaar /*- 1093955d011SMarcel Moolenaar *----------------------------------------------------------------------- 1103955d011SMarcel Moolenaar * For_Eval -- 1113955d011SMarcel Moolenaar * Evaluate the for loop in the passed line. The line 1123955d011SMarcel Moolenaar * looks like this: 1133955d011SMarcel Moolenaar * .for <variable> in <varlist> 1143955d011SMarcel Moolenaar * 1153955d011SMarcel Moolenaar * Input: 1163955d011SMarcel Moolenaar * line Line to parse 1173955d011SMarcel Moolenaar * 1183955d011SMarcel Moolenaar * Results: 1193955d011SMarcel Moolenaar * 0: Not a .for statement, parse the line 1203955d011SMarcel Moolenaar * 1: We found a for loop 1213955d011SMarcel Moolenaar * -1: A .for statement with a bad syntax error, discard. 1223955d011SMarcel Moolenaar * 1233955d011SMarcel Moolenaar * Side Effects: 1243955d011SMarcel Moolenaar * None. 1253955d011SMarcel Moolenaar * 1263955d011SMarcel Moolenaar *----------------------------------------------------------------------- 1273955d011SMarcel Moolenaar */ 1283955d011SMarcel Moolenaar int 1293955d011SMarcel Moolenaar For_Eval(char *line) 1303955d011SMarcel Moolenaar { 1313955d011SMarcel Moolenaar For *new_for; 1323955d011SMarcel Moolenaar char *ptr = line, *sub; 133*2c3632d1SSimon J. Gerraty size_t len; 1343955d011SMarcel Moolenaar int escapes; 1353955d011SMarcel Moolenaar unsigned char ch; 136*2c3632d1SSimon J. Gerraty Words words; 1373955d011SMarcel Moolenaar 1383955d011SMarcel Moolenaar /* Skip the '.' and any following whitespace */ 1393955d011SMarcel Moolenaar for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++) 1403955d011SMarcel Moolenaar continue; 1413955d011SMarcel Moolenaar 1423955d011SMarcel Moolenaar /* 1433955d011SMarcel Moolenaar * If we are not in a for loop quickly determine if the statement is 1443955d011SMarcel Moolenaar * a for. 1453955d011SMarcel Moolenaar */ 1463955d011SMarcel Moolenaar if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || 1473955d011SMarcel Moolenaar !isspace((unsigned char)ptr[3])) { 1483955d011SMarcel Moolenaar if (ptr[0] == 'e' && strncmp(ptr + 1, "ndfor", 5) == 0) { 1493955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, "for-less endfor"); 1503955d011SMarcel Moolenaar return -1; 1513955d011SMarcel Moolenaar } 1523955d011SMarcel Moolenaar return 0; 1533955d011SMarcel Moolenaar } 1543955d011SMarcel Moolenaar ptr += 3; 1553955d011SMarcel Moolenaar 1563955d011SMarcel Moolenaar /* 1573955d011SMarcel Moolenaar * we found a for loop, and now we are going to parse it. 1583955d011SMarcel Moolenaar */ 1593955d011SMarcel Moolenaar 1603955d011SMarcel Moolenaar new_for = bmake_malloc(sizeof *new_for); 1613955d011SMarcel Moolenaar memset(new_for, 0, sizeof *new_for); 1623955d011SMarcel Moolenaar 1633955d011SMarcel Moolenaar /* Grab the variables. Terminate on "in". */ 1643955d011SMarcel Moolenaar for (;; ptr += len) { 1653955d011SMarcel Moolenaar while (*ptr && isspace((unsigned char)*ptr)) 1663955d011SMarcel Moolenaar ptr++; 1673955d011SMarcel Moolenaar if (*ptr == '\0') { 1683955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, "missing `in' in for"); 1693955d011SMarcel Moolenaar For_Free(new_for); 1703955d011SMarcel Moolenaar return -1; 1713955d011SMarcel Moolenaar } 1723955d011SMarcel Moolenaar for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++) 1733955d011SMarcel Moolenaar continue; 1743955d011SMarcel Moolenaar if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') { 1753955d011SMarcel Moolenaar ptr += 2; 1763955d011SMarcel Moolenaar break; 1773955d011SMarcel Moolenaar } 1783955d011SMarcel Moolenaar if (len == 1) 1793955d011SMarcel Moolenaar new_for->short_var = 1; 180*2c3632d1SSimon J. Gerraty strlist_add_str(&new_for->vars, bmake_strldup(ptr, len), len); 1813955d011SMarcel Moolenaar } 1823955d011SMarcel Moolenaar 1833955d011SMarcel Moolenaar if (strlist_num(&new_for->vars) == 0) { 1843955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, "no iteration variables in for"); 1853955d011SMarcel Moolenaar For_Free(new_for); 1863955d011SMarcel Moolenaar return -1; 1873955d011SMarcel Moolenaar } 1883955d011SMarcel Moolenaar 1893955d011SMarcel Moolenaar while (*ptr && isspace((unsigned char)*ptr)) 1903955d011SMarcel Moolenaar ptr++; 1913955d011SMarcel Moolenaar 1923955d011SMarcel Moolenaar /* 1933955d011SMarcel Moolenaar * Make a list with the remaining words 1943955d011SMarcel Moolenaar * The values are substituted as ${:U<value>...} so we must \ escape 1953955d011SMarcel Moolenaar * characters that break that syntax. 1963955d011SMarcel Moolenaar * Variables are fully expanded - so it is safe for escape $. 1973955d011SMarcel Moolenaar * We can't do the escapes here - because we don't know whether 1983955d011SMarcel Moolenaar * we are substuting into ${...} or $(...). 1993955d011SMarcel Moolenaar */ 200*2c3632d1SSimon J. Gerraty sub = Var_Subst(ptr, VAR_GLOBAL, VARE_WANTRES); 2013955d011SMarcel Moolenaar 2023955d011SMarcel Moolenaar /* 2033955d011SMarcel Moolenaar * Split into words allowing for quoted strings. 2043955d011SMarcel Moolenaar */ 205*2c3632d1SSimon J. Gerraty words = Str_Words(sub, FALSE); 2063955d011SMarcel Moolenaar 2073955d011SMarcel Moolenaar free(sub); 2083955d011SMarcel Moolenaar 209*2c3632d1SSimon J. Gerraty { 210*2c3632d1SSimon J. Gerraty size_t n; 211*2c3632d1SSimon J. Gerraty 212*2c3632d1SSimon J. Gerraty for (n = 0; n < words.len; n++) { 213*2c3632d1SSimon J. Gerraty ptr = words.words[n]; 2143955d011SMarcel Moolenaar if (!*ptr) 2153955d011SMarcel Moolenaar continue; 2163955d011SMarcel Moolenaar escapes = 0; 2173955d011SMarcel Moolenaar while ((ch = *ptr++)) { 2183955d011SMarcel Moolenaar switch (ch) { 2193955d011SMarcel Moolenaar case ':': 2203955d011SMarcel Moolenaar case '$': 2213955d011SMarcel Moolenaar case '\\': 2223955d011SMarcel Moolenaar escapes |= FOR_SUB_ESCAPE_CHAR; 2233955d011SMarcel Moolenaar break; 2243955d011SMarcel Moolenaar case ')': 2253955d011SMarcel Moolenaar escapes |= FOR_SUB_ESCAPE_PAREN; 2263955d011SMarcel Moolenaar break; 2273955d011SMarcel Moolenaar case /*{*/ '}': 2283955d011SMarcel Moolenaar escapes |= FOR_SUB_ESCAPE_BRACE; 2293955d011SMarcel Moolenaar break; 2303955d011SMarcel Moolenaar } 2313955d011SMarcel Moolenaar } 2323955d011SMarcel Moolenaar /* 2333955d011SMarcel Moolenaar * We have to dup words[n] to maintain the semantics of 2343955d011SMarcel Moolenaar * strlist. 2353955d011SMarcel Moolenaar */ 236*2c3632d1SSimon J. Gerraty strlist_add_str(&new_for->items, bmake_strdup(words.words[n]), 237*2c3632d1SSimon J. Gerraty escapes); 2383955d011SMarcel Moolenaar } 2393955d011SMarcel Moolenaar 240*2c3632d1SSimon J. Gerraty Words_Free(words); 2413955d011SMarcel Moolenaar 2423955d011SMarcel Moolenaar if ((len = strlist_num(&new_for->items)) > 0 && 2433955d011SMarcel Moolenaar len % (n = strlist_num(&new_for->vars))) { 2443955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, 245*2c3632d1SSimon J. Gerraty "Wrong number of words (%zu) in .for substitution list" 246*2c3632d1SSimon J. Gerraty " with %zu vars", len, n); 2473955d011SMarcel Moolenaar /* 2483955d011SMarcel Moolenaar * Return 'success' so that the body of the .for loop is 2493955d011SMarcel Moolenaar * accumulated. 2503955d011SMarcel Moolenaar * Remove all items so that the loop doesn't iterate. 2513955d011SMarcel Moolenaar */ 2523955d011SMarcel Moolenaar strlist_clean(&new_for->items); 2533955d011SMarcel Moolenaar } 2543955d011SMarcel Moolenaar } 2553955d011SMarcel Moolenaar 2563955d011SMarcel Moolenaar Buf_Init(&new_for->buf, 0); 2573955d011SMarcel Moolenaar accumFor = new_for; 2583955d011SMarcel Moolenaar forLevel = 1; 2593955d011SMarcel Moolenaar return 1; 2603955d011SMarcel Moolenaar } 2613955d011SMarcel Moolenaar 2623955d011SMarcel Moolenaar /* 2633955d011SMarcel Moolenaar * Add another line to a .for loop. 2643955d011SMarcel Moolenaar * Returns 0 when the matching .endfor is reached. 2653955d011SMarcel Moolenaar */ 2663955d011SMarcel Moolenaar 2673955d011SMarcel Moolenaar int 2683955d011SMarcel Moolenaar For_Accum(char *line) 2693955d011SMarcel Moolenaar { 2703955d011SMarcel Moolenaar char *ptr = line; 2713955d011SMarcel Moolenaar 2723955d011SMarcel Moolenaar if (*ptr == '.') { 2733955d011SMarcel Moolenaar 2743955d011SMarcel Moolenaar for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++) 2753955d011SMarcel Moolenaar continue; 2763955d011SMarcel Moolenaar 2773955d011SMarcel Moolenaar if (strncmp(ptr, "endfor", 6) == 0 && 2783955d011SMarcel Moolenaar (isspace((unsigned char)ptr[6]) || !ptr[6])) { 2793955d011SMarcel Moolenaar if (DEBUG(FOR)) 2803955d011SMarcel Moolenaar (void)fprintf(debug_file, "For: end for %d\n", forLevel); 2813955d011SMarcel Moolenaar if (--forLevel <= 0) 2823955d011SMarcel Moolenaar return 0; 2833955d011SMarcel Moolenaar } else if (strncmp(ptr, "for", 3) == 0 && 2843955d011SMarcel Moolenaar isspace((unsigned char)ptr[3])) { 2853955d011SMarcel Moolenaar forLevel++; 2863955d011SMarcel Moolenaar if (DEBUG(FOR)) 2873955d011SMarcel Moolenaar (void)fprintf(debug_file, "For: new loop %d\n", forLevel); 2883955d011SMarcel Moolenaar } 2893955d011SMarcel Moolenaar } 2903955d011SMarcel Moolenaar 291*2c3632d1SSimon J. Gerraty Buf_AddStr(&accumFor->buf, line); 2923955d011SMarcel Moolenaar Buf_AddByte(&accumFor->buf, '\n'); 2933955d011SMarcel Moolenaar return 1; 2943955d011SMarcel Moolenaar } 2953955d011SMarcel Moolenaar 2963955d011SMarcel Moolenaar 297*2c3632d1SSimon J. Gerraty static size_t 2983955d011SMarcel Moolenaar for_var_len(const char *var) 2993955d011SMarcel Moolenaar { 3003955d011SMarcel Moolenaar char ch, var_start, var_end; 3013955d011SMarcel Moolenaar int depth; 302*2c3632d1SSimon J. Gerraty size_t len; 3033955d011SMarcel Moolenaar 3043955d011SMarcel Moolenaar var_start = *var; 3053955d011SMarcel Moolenaar if (var_start == 0) 3063955d011SMarcel Moolenaar /* just escape the $ */ 3073955d011SMarcel Moolenaar return 0; 3083955d011SMarcel Moolenaar 3093955d011SMarcel Moolenaar if (var_start == '(') 3103955d011SMarcel Moolenaar var_end = ')'; 3113955d011SMarcel Moolenaar else if (var_start == '{') 3123955d011SMarcel Moolenaar var_end = '}'; 3133955d011SMarcel Moolenaar else 3143955d011SMarcel Moolenaar /* Single char variable */ 3153955d011SMarcel Moolenaar return 1; 3163955d011SMarcel Moolenaar 3173955d011SMarcel Moolenaar depth = 1; 3183955d011SMarcel Moolenaar for (len = 1; (ch = var[len++]) != 0;) { 3193955d011SMarcel Moolenaar if (ch == var_start) 3203955d011SMarcel Moolenaar depth++; 3213955d011SMarcel Moolenaar else if (ch == var_end && --depth == 0) 3223955d011SMarcel Moolenaar return len; 3233955d011SMarcel Moolenaar } 3243955d011SMarcel Moolenaar 3253955d011SMarcel Moolenaar /* Variable end not found, escape the $ */ 3263955d011SMarcel Moolenaar return 0; 3273955d011SMarcel Moolenaar } 3283955d011SMarcel Moolenaar 3293955d011SMarcel Moolenaar static void 3303955d011SMarcel Moolenaar for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech) 3313955d011SMarcel Moolenaar { 3323955d011SMarcel Moolenaar char ch; 3333955d011SMarcel Moolenaar 334*2c3632d1SSimon J. Gerraty const char *item = strlist_str(items, item_no); 335*2c3632d1SSimon J. Gerraty 3363955d011SMarcel Moolenaar /* If there were no escapes, or the only escape is the other variable 3373955d011SMarcel Moolenaar * terminator, then just substitute the full string */ 3383955d011SMarcel Moolenaar if (!(strlist_info(items, item_no) & 3393955d011SMarcel Moolenaar (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) { 340*2c3632d1SSimon J. Gerraty Buf_AddStr(cmds, item); 3413955d011SMarcel Moolenaar return; 3423955d011SMarcel Moolenaar } 3433955d011SMarcel Moolenaar 3443955d011SMarcel Moolenaar /* Escape ':', '$', '\\' and 'ech' - removed by :U processing */ 3453955d011SMarcel Moolenaar while ((ch = *item++) != 0) { 3463955d011SMarcel Moolenaar if (ch == '$') { 347*2c3632d1SSimon J. Gerraty size_t len = for_var_len(item); 3483955d011SMarcel Moolenaar if (len != 0) { 349*2c3632d1SSimon J. Gerraty Buf_AddBytes(cmds, item - 1, len + 1); 3503955d011SMarcel Moolenaar item += len; 3513955d011SMarcel Moolenaar continue; 3523955d011SMarcel Moolenaar } 3533955d011SMarcel Moolenaar Buf_AddByte(cmds, '\\'); 3543955d011SMarcel Moolenaar } else if (ch == ':' || ch == '\\' || ch == ech) 3553955d011SMarcel Moolenaar Buf_AddByte(cmds, '\\'); 3563955d011SMarcel Moolenaar Buf_AddByte(cmds, ch); 3573955d011SMarcel Moolenaar } 3583955d011SMarcel Moolenaar } 3593955d011SMarcel Moolenaar 3603955d011SMarcel Moolenaar static char * 3613955d011SMarcel Moolenaar For_Iterate(void *v_arg, size_t *ret_len) 3623955d011SMarcel Moolenaar { 3633955d011SMarcel Moolenaar For *arg = v_arg; 364*2c3632d1SSimon J. Gerraty int i; 3653955d011SMarcel Moolenaar char *var; 3663955d011SMarcel Moolenaar char *cp; 3673955d011SMarcel Moolenaar char *cmd_cp; 3683955d011SMarcel Moolenaar char *body_end; 3693955d011SMarcel Moolenaar char ch; 3703955d011SMarcel Moolenaar Buffer cmds; 371*2c3632d1SSimon J. Gerraty size_t cmd_len; 3723955d011SMarcel Moolenaar 3733955d011SMarcel Moolenaar if (arg->sub_next + strlist_num(&arg->vars) > strlist_num(&arg->items)) { 3743955d011SMarcel Moolenaar /* No more iterations */ 3753955d011SMarcel Moolenaar For_Free(arg); 3763955d011SMarcel Moolenaar return NULL; 3773955d011SMarcel Moolenaar } 3783955d011SMarcel Moolenaar 3793955d011SMarcel Moolenaar free(arg->parse_buf); 3803955d011SMarcel Moolenaar arg->parse_buf = NULL; 3813955d011SMarcel Moolenaar 3823955d011SMarcel Moolenaar /* 3833955d011SMarcel Moolenaar * Scan the for loop body and replace references to the loop variables 3843955d011SMarcel Moolenaar * with variable references that expand to the required text. 3853955d011SMarcel Moolenaar * Using variable expansions ensures that the .for loop can't generate 3863955d011SMarcel Moolenaar * syntax, and that the later parsing will still see a variable. 3873955d011SMarcel Moolenaar * We assume that the null variable will never be defined. 3883955d011SMarcel Moolenaar * 3893955d011SMarcel Moolenaar * The detection of substitions of the loop control variable is naive. 3903955d011SMarcel Moolenaar * Many of the modifiers use \ to escape $ (not $) so it is possible 3913955d011SMarcel Moolenaar * to contrive a makefile where an unwanted substitution happens. 3923955d011SMarcel Moolenaar */ 3933955d011SMarcel Moolenaar 394*2c3632d1SSimon J. Gerraty cmd_cp = Buf_GetAll(&arg->buf, &cmd_len); 395*2c3632d1SSimon J. Gerraty body_end = cmd_cp + cmd_len; 396*2c3632d1SSimon J. Gerraty Buf_Init(&cmds, cmd_len + 256); 3973955d011SMarcel Moolenaar for (cp = cmd_cp; (cp = strchr(cp, '$')) != NULL;) { 3983955d011SMarcel Moolenaar char ech; 3993955d011SMarcel Moolenaar ch = *++cp; 400e1cee40dSSimon J. Gerraty if ((ch == '(' && (ech = ')', 1)) || (ch == '{' && (ech = '}', 1))) { 4013955d011SMarcel Moolenaar cp++; 4023955d011SMarcel Moolenaar /* Check variable name against the .for loop variables */ 4033955d011SMarcel Moolenaar STRLIST_FOREACH(var, &arg->vars, i) { 404*2c3632d1SSimon J. Gerraty size_t vlen = strlist_info(&arg->vars, i); 405*2c3632d1SSimon J. Gerraty if (memcmp(cp, var, vlen) != 0) 4063955d011SMarcel Moolenaar continue; 407*2c3632d1SSimon J. Gerraty if (cp[vlen] != ':' && cp[vlen] != ech && cp[vlen] != '\\') 4083955d011SMarcel Moolenaar continue; 4093955d011SMarcel Moolenaar /* Found a variable match. Replace with :U<value> */ 410*2c3632d1SSimon J. Gerraty Buf_AddBytesBetween(&cmds, cmd_cp, cp); 411*2c3632d1SSimon J. Gerraty Buf_AddStr(&cmds, ":U"); 412*2c3632d1SSimon J. Gerraty cp += vlen; 4133955d011SMarcel Moolenaar cmd_cp = cp; 4143955d011SMarcel Moolenaar for_substitute(&cmds, &arg->items, arg->sub_next + i, ech); 4153955d011SMarcel Moolenaar break; 4163955d011SMarcel Moolenaar } 4173955d011SMarcel Moolenaar continue; 4183955d011SMarcel Moolenaar } 4193955d011SMarcel Moolenaar if (ch == 0) 4203955d011SMarcel Moolenaar break; 4213955d011SMarcel Moolenaar /* Probably a single character name, ignore $$ and stupid ones. {*/ 4223955d011SMarcel Moolenaar if (!arg->short_var || strchr("}):$", ch) != NULL) { 4233955d011SMarcel Moolenaar cp++; 4243955d011SMarcel Moolenaar continue; 4253955d011SMarcel Moolenaar } 4263955d011SMarcel Moolenaar STRLIST_FOREACH(var, &arg->vars, i) { 4273955d011SMarcel Moolenaar if (var[0] != ch || var[1] != 0) 4283955d011SMarcel Moolenaar continue; 4293955d011SMarcel Moolenaar /* Found a variable match. Replace with ${:U<value>} */ 430*2c3632d1SSimon J. Gerraty Buf_AddBytesBetween(&cmds, cmd_cp, cp); 431*2c3632d1SSimon J. Gerraty Buf_AddStr(&cmds, "{:U"); 4323955d011SMarcel Moolenaar cmd_cp = ++cp; 4333955d011SMarcel Moolenaar for_substitute(&cmds, &arg->items, arg->sub_next + i, /*{*/ '}'); 434*2c3632d1SSimon J. Gerraty Buf_AddByte(&cmds, '}'); 4353955d011SMarcel Moolenaar break; 4363955d011SMarcel Moolenaar } 4373955d011SMarcel Moolenaar } 438*2c3632d1SSimon J. Gerraty Buf_AddBytesBetween(&cmds, cmd_cp, body_end); 4393955d011SMarcel Moolenaar 4403955d011SMarcel Moolenaar cp = Buf_Destroy(&cmds, FALSE); 4413955d011SMarcel Moolenaar if (DEBUG(FOR)) 4423955d011SMarcel Moolenaar (void)fprintf(debug_file, "For: loop body:\n%s", cp); 4433955d011SMarcel Moolenaar 4443955d011SMarcel Moolenaar arg->sub_next += strlist_num(&arg->vars); 4453955d011SMarcel Moolenaar 4463955d011SMarcel Moolenaar arg->parse_buf = cp; 4473955d011SMarcel Moolenaar *ret_len = strlen(cp); 4483955d011SMarcel Moolenaar return cp; 4493955d011SMarcel Moolenaar } 4503955d011SMarcel Moolenaar 451*2c3632d1SSimon J. Gerraty /* Run the for loop, imitating the actions of an include file. */ 4523955d011SMarcel Moolenaar void 4533955d011SMarcel Moolenaar For_Run(int lineno) 4543955d011SMarcel Moolenaar { 4553955d011SMarcel Moolenaar For *arg; 4563955d011SMarcel Moolenaar 4573955d011SMarcel Moolenaar arg = accumFor; 4583955d011SMarcel Moolenaar accumFor = NULL; 4593955d011SMarcel Moolenaar 4603955d011SMarcel Moolenaar if (strlist_num(&arg->items) == 0) { 4613955d011SMarcel Moolenaar /* Nothing to expand - possibly due to an earlier syntax error. */ 4623955d011SMarcel Moolenaar For_Free(arg); 4633955d011SMarcel Moolenaar return; 4643955d011SMarcel Moolenaar } 4653955d011SMarcel Moolenaar 4663955d011SMarcel Moolenaar Parse_SetInput(NULL, lineno, -1, For_Iterate, arg); 4673955d011SMarcel Moolenaar } 468