| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: for.c,v 1.156 2022/01/07 20:09:58 rillig Exp $ */ | | 1 | /* $NetBSD: for.c,v 1.157 2022/01/07 20:15:10 rillig Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1992, The Regents of the University of California. | | 4 | * Copyright (c) 1992, The Regents of the University of California. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
| @@ -48,63 +48,62 @@ | | | @@ -48,63 +48,62 @@ |
48 | * body is scanned for variable expressions, and those that match the | | 48 | * body is scanned for variable expressions, and those that match the |
49 | * variable names are replaced with expressions of the form ${:U...}. After | | 49 | * variable names are replaced with expressions of the form ${:U...}. After |
50 | * that, the body is treated like a file from an .include directive. | | 50 | * that, the body is treated like a file from an .include directive. |
51 | * | | 51 | * |
52 | * Interface: | | 52 | * Interface: |
53 | * For_Eval Evaluate the loop in the passed line. | | 53 | * For_Eval Evaluate the loop in the passed line. |
54 | * | | 54 | * |
55 | * For_Run Run accumulated loop | | 55 | * For_Run Run accumulated loop |
56 | */ | | 56 | */ |
57 | | | 57 | |
58 | #include "make.h" | | 58 | #include "make.h" |
59 | | | 59 | |
60 | /* "@(#)for.c 8.1 (Berkeley) 6/6/93" */ | | 60 | /* "@(#)for.c 8.1 (Berkeley) 6/6/93" */ |
61 | MAKE_RCSID("$NetBSD: for.c,v 1.156 2022/01/07 20:09:58 rillig Exp $"); | | 61 | MAKE_RCSID("$NetBSD: for.c,v 1.157 2022/01/07 20:15:10 rillig Exp $"); |
62 | | | 62 | |
63 | | | 63 | |
64 | typedef struct ForLoop { | | 64 | typedef struct ForLoop { |
65 | Buffer body; /* Unexpanded body of the loop */ | | | |
66 | Vector /* of 'char *' */ vars; /* Iteration variables */ | | 65 | Vector /* of 'char *' */ vars; /* Iteration variables */ |
67 | SubstringWords items; /* Substitution items */ | | 66 | SubstringWords items; /* Substitution items */ |
| | | 67 | Buffer body; /* Unexpanded body of the loop */ |
68 | unsigned int nextItem; /* Where to continue iterating */ | | 68 | unsigned int nextItem; /* Where to continue iterating */ |
69 | } ForLoop; | | 69 | } ForLoop; |
70 | | | 70 | |
71 | | | 71 | |
72 | static ForLoop *accumFor; /* Loop being accumulated */ | | 72 | static ForLoop *accumFor; /* Loop being accumulated */ |
73 | | | 73 | |
74 | | | 74 | |
75 | static ForLoop * | | 75 | static ForLoop * |
76 | ForLoop_New(void) | | 76 | ForLoop_New(void) |
77 | { | | 77 | { |
78 | ForLoop *f = bmake_malloc(sizeof *f); | | 78 | ForLoop *f = bmake_malloc(sizeof *f); |
79 | | | 79 | |
80 | Buf_Init(&f->body); | | | |
81 | Vector_Init(&f->vars, sizeof(char *)); | | 80 | Vector_Init(&f->vars, sizeof(char *)); |
82 | SubstringWords_Init(&f->items); | | 81 | SubstringWords_Init(&f->items); |
| | | 82 | Buf_Init(&f->body); |
83 | f->nextItem = 0; | | 83 | f->nextItem = 0; |
84 | | | 84 | |
85 | return f; | | 85 | return f; |
86 | } | | 86 | } |
87 | | | 87 | |
88 | static void | | 88 | static void |
89 | ForLoop_Free(ForLoop *f) | | 89 | ForLoop_Free(ForLoop *f) |
90 | { | | 90 | { |
91 | Buf_Done(&f->body); | | | |
92 | | | | |
93 | while (f->vars.len > 0) | | 91 | while (f->vars.len > 0) |
94 | free(*(char **)Vector_Pop(&f->vars)); | | 92 | free(*(char **)Vector_Pop(&f->vars)); |
95 | Vector_Done(&f->vars); | | 93 | Vector_Done(&f->vars); |
96 | | | 94 | |
97 | SubstringWords_Free(f->items); | | 95 | SubstringWords_Free(f->items); |
| | | 96 | Buf_Done(&f->body); |
98 | | | 97 | |
99 | free(f); | | 98 | free(f); |
100 | } | | 99 | } |
101 | | | 100 | |
102 | static bool | | 101 | static bool |
103 | ForLoop_ParseVarnames(ForLoop *f, const char **pp) | | 102 | ForLoop_ParseVarnames(ForLoop *f, const char **pp) |
104 | { | | 103 | { |
105 | const char *p = *pp; | | 104 | const char *p = *pp; |
106 | | | 105 | |
107 | for (;;) { | | 106 | for (;;) { |
108 | size_t len; | | 107 | size_t len; |
109 | | | 108 | |
110 | cpp_skip_whitespace(&p); | | 109 | cpp_skip_whitespace(&p); |
| @@ -146,27 +145,27 @@ ForLoop_ParseItems(ForLoop *f, const cha | | | @@ -146,27 +145,27 @@ ForLoop_ParseItems(ForLoop *f, const cha |
146 | cpp_skip_whitespace(&p); | | 145 | cpp_skip_whitespace(&p); |
147 | | | 146 | |
148 | if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) { | | 147 | if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) { |
149 | Parse_Error(PARSE_FATAL, "Error in .for loop items"); | | 148 | Parse_Error(PARSE_FATAL, "Error in .for loop items"); |
150 | return false; | | 149 | return false; |
151 | } | | 150 | } |
152 | | | 151 | |
153 | f->items = Substring_Words(items, false); | | 152 | f->items = Substring_Words(items, false); |
154 | free(items); | | 153 | free(items); |
155 | | | 154 | |
156 | if (f->items.len == 1 && Substring_IsEmpty(f->items.words[0])) | | 155 | if (f->items.len == 1 && Substring_IsEmpty(f->items.words[0])) |
157 | f->items.len = 0; /* .for var in ${:U} */ | | 156 | f->items.len = 0; /* .for var in ${:U} */ |
158 | | | 157 | |
159 | if (f->items.len != 0 && f->items.len % f->vars.len != 0) { | | 158 | if (f->items.len % f->vars.len != 0) { |
160 | Parse_Error(PARSE_FATAL, | | 159 | Parse_Error(PARSE_FATAL, |
161 | "Wrong number of words (%u) in .for " | | 160 | "Wrong number of words (%u) in .for " |
162 | "substitution list with %u variables", | | 161 | "substitution list with %u variables", |
163 | (unsigned)f->items.len, (unsigned)f->vars.len); | | 162 | (unsigned)f->items.len, (unsigned)f->vars.len); |
164 | return false; | | 163 | return false; |
165 | } | | 164 | } |
166 | | | 165 | |
167 | return true; | | 166 | return true; |
168 | } | | 167 | } |
169 | | | 168 | |
170 | static bool | | 169 | static bool |
171 | IsFor(const char *p) | | 170 | IsFor(const char *p) |
172 | { | | 171 | { |
| @@ -174,33 +173,30 @@ IsFor(const char *p) | | | @@ -174,33 +173,30 @@ IsFor(const char *p) |
174 | } | | 173 | } |
175 | | | 174 | |
176 | static bool | | 175 | static bool |
177 | IsEndfor(const char *p) | | 176 | IsEndfor(const char *p) |
178 | { | | 177 | { |
179 | return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 && | | 178 | return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 && |
180 | (p[6] == '\0' || ch_isspace(p[6])); | | 179 | (p[6] == '\0' || ch_isspace(p[6])); |
181 | } | | 180 | } |
182 | | | 181 | |
183 | /* | | 182 | /* |
184 | * Evaluate the for loop in the passed line. The line looks like this: | | 183 | * Evaluate the for loop in the passed line. The line looks like this: |
185 | * .for <varname...> in <value...> | | 184 | * .for <varname...> in <value...> |
186 | * | | 185 | * |
187 | * Input: | | | |
188 | * line Line to parse | | | |
189 | * | | | |
190 | * Results: | | 186 | * Results: |
191 | * 0: Not a .for statement, parse the line | | 187 | * 0 not a .for directive |
192 | * 1: We found a for loop | | 188 | * 1 found a .for directive |
193 | * -1: A .for statement with a bad syntax error, discard. | | 189 | * -1 erroneous .for directive |
194 | */ | | 190 | */ |
195 | int | | 191 | int |
196 | For_Eval(const char *line) | | 192 | For_Eval(const char *line) |
197 | { | | 193 | { |
198 | ForLoop *f; | | 194 | ForLoop *f; |
199 | const char *p; | | 195 | const char *p; |
200 | | | 196 | |
201 | p = line + 1; /* skip the '.' */ | | 197 | p = line + 1; /* skip the '.' */ |
202 | cpp_skip_whitespace(&p); | | 198 | cpp_skip_whitespace(&p); |
203 | | | 199 | |
204 | if (!IsFor(p)) { | | 200 | if (!IsFor(p)) { |
205 | if (IsEndfor(p)) { | | 201 | if (IsEndfor(p)) { |
206 | Parse_Error(PARSE_FATAL, "for-less endfor"); | | 202 | Parse_Error(PARSE_FATAL, "for-less endfor"); |
| @@ -231,27 +227,27 @@ For_Eval(const char *line) | | | @@ -231,27 +227,27 @@ For_Eval(const char *line) |
231 | * Returns false when the matching .endfor is reached. | | 227 | * Returns false when the matching .endfor is reached. |
232 | */ | | 228 | */ |
233 | bool | | 229 | bool |
234 | For_Accum(const char *line, int *forLevel) | | 230 | For_Accum(const char *line, int *forLevel) |
235 | { | | 231 | { |
236 | const char *p = line; | | 232 | const char *p = line; |
237 | | | 233 | |
238 | if (*p == '.') { | | 234 | if (*p == '.') { |
239 | p++; | | 235 | p++; |
240 | cpp_skip_whitespace(&p); | | 236 | cpp_skip_whitespace(&p); |
241 | | | 237 | |
242 | if (IsEndfor(p)) { | | 238 | if (IsEndfor(p)) { |
243 | DEBUG1(FOR, "For: end for %d\n", *forLevel); | | 239 | DEBUG1(FOR, "For: end for %d\n", *forLevel); |
244 | if (--*forLevel <= 0) | | 240 | if (--*forLevel == 0) |
245 | return false; | | 241 | return false; |
246 | } else if (IsFor(p)) { | | 242 | } else if (IsFor(p)) { |
247 | (*forLevel)++; | | 243 | (*forLevel)++; |
248 | DEBUG1(FOR, "For: new loop %d\n", *forLevel); | | 244 | DEBUG1(FOR, "For: new loop %d\n", *forLevel); |
249 | } | | 245 | } |
250 | } | | 246 | } |
251 | | | 247 | |
252 | Buf_AddStr(&accumFor->body, line); | | 248 | Buf_AddStr(&accumFor->body, line); |
253 | Buf_AddByte(&accumFor->body, '\n'); | | 249 | Buf_AddByte(&accumFor->body, '\n'); |
254 | return true; | | 250 | return true; |
255 | } | | 251 | } |
256 | | | 252 | |
257 | | | 253 | |
| @@ -299,27 +295,27 @@ NeedsEscapes(Substring value, char endc) | | | @@ -299,27 +295,27 @@ NeedsEscapes(Substring value, char endc) |
299 | *p == '\n') | | 295 | *p == '\n') |
300 | return true; | | 296 | return true; |
301 | } | | 297 | } |
302 | return false; | | 298 | return false; |
303 | } | | 299 | } |
304 | | | 300 | |
305 | /* | | 301 | /* |
306 | * While expanding the body of a .for loop, write the item in the ${:U...} | | 302 | * While expanding the body of a .for loop, write the item in the ${:U...} |
307 | * expression, escaping characters as needed. | | 303 | * expression, escaping characters as needed. |
308 | * | | 304 | * |
309 | * The result is later unescaped by ApplyModifier_Defined. | | 305 | * The result is later unescaped by ApplyModifier_Defined. |
310 | */ | | 306 | */ |
311 | static void | | 307 | static void |
312 | Buf_AddEscaped(Buffer *cmds, Substring item, char endc) | | 308 | AddEscaped(Buffer *cmds, Substring item, char endc) |
313 | { | | 309 | { |
314 | const char *p; | | 310 | const char *p; |
315 | char ch; | | 311 | char ch; |
316 | | | 312 | |
317 | if (!NeedsEscapes(item, endc)) { | | 313 | if (!NeedsEscapes(item, endc)) { |
318 | Buf_AddBytesBetween(cmds, item.start, item.end); | | 314 | Buf_AddBytesBetween(cmds, item.start, item.end); |
319 | return; | | 315 | return; |
320 | } | | 316 | } |
321 | | | 317 | |
322 | /* | | 318 | /* |
323 | * Escape ':', '$', '\\' and 'endc' - these will be removed later by | | 319 | * Escape ':', '$', '\\' and 'endc' - these will be removed later by |
324 | * :U processing, see ApplyModifier_Defined. | | 320 | * :U processing, see ApplyModifier_Defined. |
325 | */ | | 321 | */ |
| @@ -367,27 +363,27 @@ ForLoop_SubstVarLong(ForLoop *f, Buffer | | | @@ -367,27 +363,27 @@ ForLoop_SubstVarLong(ForLoop *f, Buffer |
367 | p++, varname++; | | 363 | p++, varname++; |
368 | if (*varname != '\0') | | 364 | if (*varname != '\0') |
369 | continue; | | 365 | continue; |
370 | /* XXX: why test for backslash here? */ | | 366 | /* XXX: why test for backslash here? */ |
371 | if (*p != ':' && *p != endc && *p != '\\') | | 367 | if (*p != ':' && *p != endc && *p != '\\') |
372 | continue; | | 368 | continue; |
373 | | | 369 | |
374 | /* | | 370 | /* |
375 | * Found a variable match. Skip over the variable name and | | 371 | * Found a variable match. Skip over the variable name and |
376 | * instead add ':U<value>' to the current body. | | 372 | * instead add ':U<value>' to the current body. |
377 | */ | | 373 | */ |
378 | Buf_AddBytesBetween(body, *inout_mark, start); | | 374 | Buf_AddBytesBetween(body, *inout_mark, start); |
379 | Buf_AddStr(body, ":U"); | | 375 | Buf_AddStr(body, ":U"); |
380 | Buf_AddEscaped(body, f->items.words[f->nextItem + i], endc); | | 376 | AddEscaped(body, f->items.words[f->nextItem + i], endc); |
381 | | | 377 | |
382 | *inout_mark = p; | | 378 | *inout_mark = p; |
383 | *pp = p; | | 379 | *pp = p; |
384 | return; | | 380 | return; |
385 | } | | 381 | } |
386 | } | | 382 | } |
387 | | | 383 | |
388 | /* | | 384 | /* |
389 | * When expanding the body of a .for loop, replace single-character | | 385 | * When expanding the body of a .for loop, replace single-character |
390 | * variable expressions like $i with their ${:U...} expansion. | | 386 | * variable expressions like $i with their ${:U...} expansion. |
391 | */ | | 387 | */ |
392 | static void | | 388 | static void |
393 | ForLoop_SubstVarShort(ForLoop *f, Buffer *body, | | 389 | ForLoop_SubstVarShort(ForLoop *f, Buffer *body, |
| @@ -405,27 +401,27 @@ ForLoop_SubstVarShort(ForLoop *f, Buffer | | | @@ -405,27 +401,27 @@ ForLoop_SubstVarShort(ForLoop *f, Buffer |
405 | for (i = 0; i < f->vars.len; i++) { | | 401 | for (i = 0; i < f->vars.len; i++) { |
406 | const char *varname = vars[i]; | | 402 | const char *varname = vars[i]; |
407 | if (varname[0] == ch && varname[1] == '\0') | | 403 | if (varname[0] == ch && varname[1] == '\0') |
408 | goto found; | | 404 | goto found; |
409 | } | | 405 | } |
410 | return; | | 406 | return; |
411 | | | 407 | |
412 | found: | | 408 | found: |
413 | Buf_AddBytesBetween(body, *inout_mark, p); | | 409 | Buf_AddBytesBetween(body, *inout_mark, p); |
414 | *inout_mark = p + 1; | | 410 | *inout_mark = p + 1; |
415 | | | 411 | |
416 | /* Replace $<ch> with ${:U<value>} */ | | 412 | /* Replace $<ch> with ${:U<value>} */ |
417 | Buf_AddStr(body, "{:U"); | | 413 | Buf_AddStr(body, "{:U"); |
418 | Buf_AddEscaped(body, f->items.words[f->nextItem + i], '}'); | | 414 | AddEscaped(body, f->items.words[f->nextItem + i], '}'); |
419 | Buf_AddByte(body, '}'); | | 415 | Buf_AddByte(body, '}'); |
420 | } | | 416 | } |
421 | | | 417 | |
422 | /* | | 418 | /* |
423 | * Compute the body for the current iteration by copying the unexpanded body, | | 419 | * Compute the body for the current iteration by copying the unexpanded body, |
424 | * replacing the expressions for the iteration variables on the way. | | 420 | * replacing the expressions for the iteration variables on the way. |
425 | * | | 421 | * |
426 | * Using variable expressions ensures that the .for loop can't generate | | 422 | * Using variable expressions ensures that the .for loop can't generate |
427 | * syntax, and that the later parsing will still see a variable. | | 423 | * syntax, and that the later parsing will still see a variable. |
428 | * This code assumes that the variable with the empty name will never be | | 424 | * This code assumes that the variable with the empty name will never be |
429 | * defined, see unit-tests/varname-empty.mk for more details. | | 425 | * defined, see unit-tests/varname-empty.mk for more details. |
430 | * | | 426 | * |
431 | * The detection of substitutions of the loop control variables is naive. | | 427 | * The detection of substitutions of the loop control variables is naive. |