diff options
Diffstat (limited to 'for.c')
-rw-r--r-- | for.c | 253 |
1 files changed, 133 insertions, 120 deletions
@@ -1,4 +1,4 @@ -/* $NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $ */ +/* $NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $ */ /* * Copyright (c) 1992, The Regents of the University of California. @@ -29,7 +29,7 @@ * SUCH DAMAGE. */ -/*- +/* * Handling of .for/.endfor loops in a makefile. * * For loops have the form: @@ -58,9 +58,8 @@ #include "make.h" /* "@(#)for.c 8.1 (Berkeley) 6/6/93" */ -MAKE_RCSID("$NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $"); +MAKE_RCSID("$NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $"); -static int forLevel = 0; /* Nesting level */ /* One of the variables to the left of the "in" in a .for loop. */ typedef struct ForVar { @@ -68,10 +67,7 @@ typedef struct ForVar { size_t nameLen; } ForVar; -/* - * State of a for loop. - */ -typedef struct For { +typedef struct ForLoop { Buffer body; /* Unexpanded body of the loop */ Vector /* of ForVar */ vars; /* Iteration variables */ Words items; /* Substitution items */ @@ -81,22 +77,33 @@ typedef struct For { * only ${V} and $(V). */ Boolean short_var; unsigned int sub_next; /* Where to continue iterating */ -} For; +} ForLoop; -static For *accumFor; /* Loop being accumulated */ -static void -ForAddVar(For *f, const char *name, size_t len) +static ForLoop *accumFor; /* Loop being accumulated */ +static int forLevel = 0; /* Nesting level */ + + +static ForLoop * +ForLoop_New(void) { - ForVar *var = Vector_Push(&f->vars); - var->name = bmake_strldup(name, len); - var->nameLen = len; + ForLoop *f = bmake_malloc(sizeof *f); + + Buf_Init(&f->body); + Vector_Init(&f->vars, sizeof(ForVar)); + f->items.words = NULL; + f->items.freeIt = NULL; + Buf_Init(&f->curBody); + f->short_var = FALSE; + f->sub_next = 0; + + return f; } static void -For_Free(For *f) +ForLoop_Free(ForLoop *f) { - Buf_Destroy(&f->body, TRUE); + Buf_Done(&f->body); while (f->vars.len > 0) { ForVar *var = Vector_Pop(&f->vars); @@ -105,11 +112,89 @@ For_Free(For *f) Vector_Done(&f->vars); Words_Free(f->items); - Buf_Destroy(&f->curBody, TRUE); + Buf_Done(&f->curBody); free(f); } +static void +ForLoop_AddVar(ForLoop *f, const char *name, size_t len) +{ + ForVar *var = Vector_Push(&f->vars); + var->name = bmake_strldup(name, len); + var->nameLen = len; +} + +static Boolean +ForLoop_ParseVarnames(ForLoop *f, const char **pp) +{ + const char *p = *pp; + + for (;;) { + size_t len; + + cpp_skip_whitespace(&p); + if (*p == '\0') { + Parse_Error(PARSE_FATAL, "missing `in' in for"); + return FALSE; + } + + /* + * XXX: This allows arbitrary variable names; + * see directive-for.mk. + */ + for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++) + continue; + + if (len == 2 && p[0] == 'i' && p[1] == 'n') { + p += 2; + break; + } + if (len == 1) + f->short_var = TRUE; + + ForLoop_AddVar(f, p, len); + p += len; + } + + if (f->vars.len == 0) { + Parse_Error(PARSE_FATAL, "no iteration variables in for"); + return FALSE; + } + + *pp = p; + return TRUE; +} + +static Boolean +ForLoop_ParseItems(ForLoop *f, const char *p) +{ + char *items; + + cpp_skip_whitespace(&p); + + if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) { + Parse_Error(PARSE_FATAL, "Error in .for loop items"); + return FALSE; + } + + f->items = Str_Words(items, FALSE); + free(items); + + if (f->items.len == 1 && f->items.words[0][0] == '\0') + f->items.len = 0; /* .for var in ${:U} */ + + if (f->items.len != 0 && f->items.len % f->vars.len != 0) { + Parse_Error(PARSE_FATAL, + "Wrong number of words (%u) in .for " + "substitution list with %u variables", + (unsigned)f->items.len, (unsigned)f->vars.len); + return FALSE; + } + + return TRUE; +} + static Boolean IsFor(const char *p) { @@ -138,7 +223,7 @@ IsEndfor(const char *p) int For_Eval(const char *line) { - For *f; + ForLoop *f; const char *p; p = line + 1; /* skip the '.' */ @@ -153,113 +238,41 @@ For_Eval(const char *line) } p += 3; - /* - * we found a for loop, and now we are going to parse it. - */ + f = ForLoop_New(); - f = bmake_malloc(sizeof *f); - Buf_Init(&f->body); - Vector_Init(&f->vars, sizeof(ForVar)); - f->items.words = NULL; - f->items.freeIt = NULL; - Buf_Init(&f->curBody); - f->short_var = FALSE; - f->sub_next = 0; - - /* Grab the variables. Terminate on "in". */ - for (;;) { - size_t len; - - cpp_skip_whitespace(&p); - if (*p == '\0') { - Parse_Error(PARSE_FATAL, "missing `in' in for"); - For_Free(f); - return -1; - } - - /* - * XXX: This allows arbitrary variable names; - * see directive-for.mk. - */ - for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++) - continue; - - if (len == 2 && p[0] == 'i' && p[1] == 'n') { - p += 2; - break; - } - if (len == 1) - f->short_var = TRUE; - - ForAddVar(f, p, len); - p += len; - } - - if (f->vars.len == 0) { - Parse_Error(PARSE_FATAL, "no iteration variables in for"); - For_Free(f); + if (!ForLoop_ParseVarnames(f, &p)) { + ForLoop_Free(f); return -1; } - cpp_skip_whitespace(&p); - - { - char *items; - if (Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items) != VPR_OK) { - Parse_Error(PARSE_FATAL, "Error in .for loop items"); - f->items.len = 0; - goto done; - } - - f->items = Str_Words(items, FALSE); - free(items); - - if (f->items.len == 1 && f->items.words[0][0] == '\0') - f->items.len = 0; /* .for var in ${:U} */ + if (!ForLoop_ParseItems(f, p)) { + /* Continue parsing the .for loop, but don't iterate. */ + f->items.len = 0; } - { - size_t nitems, nvars; - - if ((nitems = f->items.len) > 0 && - nitems % (nvars = f->vars.len) != 0) { - Parse_Error(PARSE_FATAL, - "Wrong number of words (%u) in .for " - "substitution list with %u variables", - (unsigned)nitems, (unsigned)nvars); - /* - * Return 'success' so that the body of the .for loop - * is accumulated. - * Remove all items so that the loop doesn't iterate. - */ - f->items.len = 0; - } - } - -done: accumFor = f; forLevel = 1; return 1; } /* - * Add another line to a .for loop. + * Add another line to the .for loop that is being built up. * Returns FALSE when the matching .endfor is reached. */ Boolean For_Accum(const char *line) { - const char *ptr = line; + const char *p = line; - if (*ptr == '.') { - ptr++; - cpp_skip_whitespace(&ptr); + if (*p == '.') { + p++; + cpp_skip_whitespace(&p); - if (IsEndfor(ptr)) { + if (IsEndfor(p)) { DEBUG1(FOR, "For: end for %d\n", forLevel); if (--forLevel <= 0) return FALSE; - } else if (IsFor(ptr)) { + } else if (IsFor(p)) { forLevel++; DEBUG1(FOR, "For: new loop %d\n", forLevel); } @@ -307,11 +320,11 @@ for_var_len(const char *var) * that characters that break this syntax must be backslash-escaped. */ static Boolean -NeedsEscapes(const char *word, char endc) +NeedsEscapes(const char *value, char endc) { const char *p; - for (p = word; *p != '\0'; p++) { + for (p = value; *p != '\0'; p++) { if (*p == ':' || *p == '$' || *p == '\\' || *p == endc) return TRUE; } @@ -356,8 +369,8 @@ Buf_AddEscaped(Buffer *cmds, const char *item, char endc) * expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue". */ static void -SubstVarLong(For *f, const char **pp, const char *bodyEnd, char endc, - const char **inout_mark) +ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd, + char endc, const char **inout_mark) { size_t i; const char *p = *pp; @@ -397,7 +410,7 @@ SubstVarLong(For *f, const char **pp, const char *bodyEnd, char endc, * variable expressions like $i with their ${:U...} expansion. */ static void -SubstVarShort(For *f, const char *p, const char **inout_mark) +ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark) { const char ch = *p; ForVar *vars; @@ -432,12 +445,12 @@ found: * This code assumes that the variable with the empty name will never be * defined, see unit-tests/varname-empty.mk for more details. * - * The detection of substitutions of the loop control variable is naive. - * Many of the modifiers use \ to escape $ (not $) so it is possible + * The detection of substitutions of the loop control variables is naive. + * Many of the modifiers use '\' to escape '$' (not '$'), so it is possible * to contrive a makefile where an unwanted substitution happens. */ static void -ForSubstBody(For *f) +ForLoop_SubstBody(ForLoop *f) { const char *p, *bodyEnd; const char *mark; /* where the last replacement left off */ @@ -449,10 +462,10 @@ ForSubstBody(For *f) for (p = mark; (p = strchr(p, '$')) != NULL;) { if (p[1] == '{' || p[1] == '(') { p += 2; - SubstVarLong(f, &p, bodyEnd, p[-1] == '{' ? '}' : ')', - &mark); + ForLoop_SubstVarLong(f, &p, bodyEnd, + p[-1] == '{' ? '}' : ')', &mark); } else if (p[1] != '\0') { - SubstVarShort(f, p + 1, &mark); + ForLoop_SubstVarShort(f, p + 1, &mark); p += 2; } else break; @@ -468,15 +481,15 @@ ForSubstBody(For *f) static char * ForReadMore(void *v_arg, size_t *out_len) { - For *f = v_arg; + ForLoop *f = v_arg; if (f->sub_next == f->items.len) { /* No more iterations */ - For_Free(f); + ForLoop_Free(f); return NULL; } - ForSubstBody(f); + ForLoop_SubstBody(f); DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data); f->sub_next += (unsigned int)f->vars.len; @@ -488,7 +501,7 @@ ForReadMore(void *v_arg, size_t *out_len) void For_Run(int lineno) { - For *f = accumFor; + ForLoop *f = accumFor; accumFor = NULL; if (f->items.len == 0) { @@ -496,7 +509,7 @@ For_Run(int lineno) * Nothing to expand - possibly due to an earlier syntax * error. */ - For_Free(f); + ForLoop_Free(f); return; } |