| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: for.c,v 1.41 2008/12/29 10:12:30 dsl Exp $ */ | | 1 | /* $NetBSD: for.c,v 1.42 2009/01/10 16:59:02 dsl 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. |
| @@ -20,57 +20,61 @@ | | | @@ -20,57 +20,61 @@ |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | * SUCH DAMAGE. | | 29 | * SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | #ifndef MAKE_NATIVE | | 32 | #ifndef MAKE_NATIVE |
33 | static char rcsid[] = "$NetBSD: for.c,v 1.41 2008/12/29 10:12:30 dsl Exp $"; | | 33 | static char rcsid[] = "$NetBSD: for.c,v 1.42 2009/01/10 16:59:02 dsl Exp $"; |
34 | #else | | 34 | #else |
35 | #include <sys/cdefs.h> | | 35 | #include <sys/cdefs.h> |
36 | #ifndef lint | | 36 | #ifndef lint |
37 | #if 0 | | 37 | #if 0 |
38 | static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93"; | | 38 | static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93"; |
39 | #else | | 39 | #else |
40 | __RCSID("$NetBSD: for.c,v 1.41 2008/12/29 10:12:30 dsl Exp $"); | | 40 | __RCSID("$NetBSD: for.c,v 1.42 2009/01/10 16:59:02 dsl Exp $"); |
41 | #endif | | 41 | #endif |
42 | #endif /* not lint */ | | 42 | #endif /* not lint */ |
43 | #endif | | 43 | #endif |
44 | | | 44 | |
45 | /*- | | 45 | /*- |
46 | * for.c -- | | 46 | * for.c -- |
47 | * Functions to handle loops in a makefile. | | 47 | * Functions to handle loops in a makefile. |
48 | * | | 48 | * |
49 | * Interface: | | 49 | * Interface: |
50 | * For_Eval Evaluate the loop in the passed line. | | 50 | * For_Eval Evaluate the loop in the passed line. |
51 | * For_Run Run accumulated loop | | 51 | * For_Run Run accumulated loop |
52 | * | | 52 | * |
53 | */ | | 53 | */ |
54 | | | 54 | |
55 | #include <assert.h> | | 55 | #include <assert.h> |
56 | #include <ctype.h> | | 56 | #include <ctype.h> |
57 | | | 57 | |
58 | #include "make.h" | | 58 | #include "make.h" |
59 | #include "hash.h" | | 59 | #include "hash.h" |
60 | #include "dir.h" | | 60 | #include "dir.h" |
61 | #include "buf.h" | | 61 | #include "buf.h" |
62 | #include "strlist.h" | | 62 | #include "strlist.h" |
63 | | | 63 | |
| | | 64 | #define FOR_SUB_ESCAPE_COLON 1 |
| | | 65 | #define FOR_SUB_ESCAPE_BRACE 2 |
| | | 66 | #define FOR_SUB_ESCAPE_PAREN 4 |
| | | 67 | |
64 | /* | | 68 | /* |
65 | * For statements are of the form: | | 69 | * For statements are of the form: |
66 | * | | 70 | * |
67 | * .for <variable> in <varlist> | | 71 | * .for <variable> in <varlist> |
68 | * ... | | 72 | * ... |
69 | * .endfor | | 73 | * .endfor |
70 | * | | 74 | * |
71 | * The trick is to look for the matching end inside for for loop | | 75 | * The trick is to look for the matching end inside for for loop |
72 | * To do that, we count the current nesting level of the for loops. | | 76 | * To do that, we count the current nesting level of the for loops. |
73 | * and the .endfor statements, accumulating all the statements between | | 77 | * and the .endfor statements, accumulating all the statements between |
74 | * the initial .for loop and the matching .endfor; | | 78 | * the initial .for loop and the matching .endfor; |
75 | * then we evaluate the for loop for each variable in the varlist. | | 79 | * then we evaluate the for loop for each variable in the varlist. |
76 | * | | 80 | * |
| @@ -120,30 +124,28 @@ make_str(const char *ptr, int len) | | | @@ -120,30 +124,28 @@ make_str(const char *ptr, int len) |
120 | * 1: We found a for loop | | 124 | * 1: We found a for loop |
121 | * -1: A .for statement with a bad syntax error, discard. | | 125 | * -1: A .for statement with a bad syntax error, discard. |
122 | * | | 126 | * |
123 | * Side Effects: | | 127 | * Side Effects: |
124 | * None. | | 128 | * None. |
125 | * | | 129 | * |
126 | *----------------------------------------------------------------------- | | 130 | *----------------------------------------------------------------------- |
127 | */ | | 131 | */ |
128 | int | | 132 | int |
129 | For_Eval(char *line) | | 133 | For_Eval(char *line) |
130 | { | | 134 | { |
131 | char *ptr = line, *sub; | | 135 | char *ptr = line, *sub; |
132 | int len; | | 136 | int len; |
133 | int i; | | | |
134 | int escapes; | | 137 | int escapes; |
135 | int depth; | | 138 | unsigned char ch; |
136 | char ch; | | | |
137 | | | 139 | |
138 | /* Forget anything we previously knew about - it cannot be useful */ | | 140 | /* Forget anything we previously knew about - it cannot be useful */ |
139 | memset(&accumFor, 0, sizeof accumFor); | | 141 | memset(&accumFor, 0, sizeof accumFor); |
140 | | | 142 | |
141 | forLevel = 0; | | 143 | forLevel = 0; |
142 | for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) | | 144 | for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) |
143 | continue; | | 145 | continue; |
144 | /* | | 146 | /* |
145 | * If we are not in a for loop quickly determine if the statement is | | 147 | * If we are not in a for loop quickly determine if the statement is |
146 | * a for. | | 148 | * a for. |
147 | */ | | 149 | */ |
148 | if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || | | 150 | if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || |
149 | !isspace((unsigned char) ptr[3])) { | | 151 | !isspace((unsigned char) ptr[3])) { |
| @@ -163,71 +165,59 @@ For_Eval(char *line) | | | @@ -163,71 +165,59 @@ For_Eval(char *line) |
163 | for (;; ptr += len) { | | 165 | for (;; ptr += len) { |
164 | while (*ptr && isspace((unsigned char) *ptr)) | | 166 | while (*ptr && isspace((unsigned char) *ptr)) |
165 | ptr++; | | 167 | ptr++; |
166 | if (*ptr == '\0') { | | 168 | if (*ptr == '\0') { |
167 | Parse_Error(PARSE_FATAL, "missing `in' in for"); | | 169 | Parse_Error(PARSE_FATAL, "missing `in' in for"); |
168 | return -1; | | 170 | return -1; |
169 | } | | 171 | } |
170 | for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++) | | 172 | for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++) |
171 | continue; | | 173 | continue; |
172 | if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') { | | 174 | if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') { |
173 | ptr += 2; | | 175 | ptr += 2; |
174 | break; | | 176 | break; |
175 | } | | 177 | } |
176 | strlist_add_str(&accumFor.vars, make_str(ptr, len)); | | 178 | strlist_add_str(&accumFor.vars, make_str(ptr, len), len); |
177 | } | | 179 | } |
178 | | | 180 | |
179 | if (strlist_num(&accumFor.vars) == 0) { | | 181 | if (strlist_num(&accumFor.vars) == 0) { |
180 | Parse_Error(PARSE_FATAL, "no iteration variables in for"); | | 182 | Parse_Error(PARSE_FATAL, "no iteration variables in for"); |
181 | return -1; | | 183 | return -1; |
182 | } | | 184 | } |
183 | | | 185 | |
184 | while (*ptr && isspace((unsigned char) *ptr)) | | 186 | while (*ptr && isspace((unsigned char) *ptr)) |
185 | ptr++; | | 187 | ptr++; |
186 | | | 188 | |
187 | /* | | 189 | /* |
188 | * Make a list with the remaining words | | 190 | * Make a list with the remaining words |
189 | * The values are substituted as ${:U<value>... so we must \ escape | | 191 | * The values are substituted as ${:U<value>...} so we must \ escape |
190 | * characters that break that syntax - particularly ':', maybe $ and \. | | 192 | * characters that break that syntax - particularly ':', maybe $ and \. |
191 | */ | | 193 | */ |
192 | sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE); | | 194 | sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE); |
193 | | | 195 | |
194 | for (ptr = sub;; ptr += len) { | | 196 | for (ptr = sub;; ptr += len) { |
195 | while (*ptr && isspace((unsigned char)*ptr)) | | 197 | while (*ptr && isspace((unsigned char)*ptr)) |
196 | ptr++; | | 198 | ptr++; |
197 | if (*ptr == 0) | | 199 | if (*ptr == 0) |
198 | break; | | 200 | break; |
199 | escapes = 0; | | 201 | escapes = 0; |
200 | for (len = 0; ptr[len] && !isspace((unsigned char)ptr[len]); len++) | | 202 | for (len = 0; (ch = ptr[len]) != 0 && !isspace(ch); len++) { |
201 | if (ptr[len] == ':') | | 203 | if (ch == ':') |
202 | escapes++; | | 204 | escapes |= FOR_SUB_ESCAPE_COLON; |
203 | if (escapes == 0) | | 205 | else if (ch == ')') |
204 | strlist_add_str(&accumFor.items, make_str(ptr, len)); | | 206 | escapes |= FOR_SUB_ESCAPE_PAREN; |
205 | else { | | 207 | else if (ch == /*{*/ '}') |
206 | char *item = bmake_malloc(len + escapes + 1); | | 208 | escapes |= FOR_SUB_ESCAPE_BRACE; |
207 | strlist_add_str(&accumFor.items, item); | | | |
208 | for (depth= 0, i = 0; i < len; i++) { | | | |
209 | ch = ptr[i]; | | | |
210 | /* Loose determination of nested variable definitions. */ | | | |
211 | if (ch == '(' || ch == '{') | | | |
212 | depth++; | | | |
213 | else if (ch == ')' || ch == '}') | | | |
214 | depth--; | | | |
215 | else if (ch == ':' && depth == 0) | | | |
216 | *item++ = '\\'; | | | |
217 | *item++ = ch; | | | |
218 | } | | | |
219 | *item = 0; | | | |
220 | } | | 209 | } |
| | | 210 | strlist_add_str(&accumFor.items, make_str(ptr, len), escapes); |
221 | } | | 211 | } |
222 | | | 212 | |
223 | free(sub); | | 213 | free(sub); |
224 | | | 214 | |
225 | if (strlist_num(&accumFor.items) % strlist_num(&accumFor.vars)) { | | 215 | if (strlist_num(&accumFor.items) % strlist_num(&accumFor.vars)) { |
226 | Parse_Error(PARSE_FATAL, | | 216 | Parse_Error(PARSE_FATAL, |
227 | "Wrong number of words in .for substitution list %d %d", | | 217 | "Wrong number of words in .for substitution list %d %d", |
228 | strlist_num(&accumFor.items), strlist_num(&accumFor.vars)); | | 218 | strlist_num(&accumFor.items), strlist_num(&accumFor.vars)); |
229 | /* | | 219 | /* |
230 | * Return 'success' so that the body of the .for loop is accumulated. | | 220 | * Return 'success' so that the body of the .for loop is accumulated. |
231 | * The loop will have zero iterations expanded due a later test. | | 221 | * The loop will have zero iterations expanded due a later test. |
232 | */ | | 222 | */ |
233 | } | | 223 | } |
| @@ -275,34 +265,83 @@ For_Accum(char *line) | | | @@ -275,34 +265,83 @@ For_Accum(char *line) |
275 | /*- | | 265 | /*- |
276 | *----------------------------------------------------------------------- | | 266 | *----------------------------------------------------------------------- |
277 | * For_Run -- | | 267 | * For_Run -- |
278 | * Run the for loop, imitating the actions of an include file | | 268 | * Run the for loop, imitating the actions of an include file |
279 | * | | 269 | * |
280 | * Results: | | 270 | * Results: |
281 | * None. | | 271 | * None. |
282 | * | | 272 | * |
283 | * Side Effects: | | 273 | * Side Effects: |
284 | * None. | | 274 | * None. |
285 | * | | 275 | * |
286 | *----------------------------------------------------------------------- | | 276 | *----------------------------------------------------------------------- |
287 | */ | | 277 | */ |
| | | 278 | |
| | | 279 | static void |
| | | 280 | for_substitute(Buffer cmds, strlist_t *items, unsigned int item_no, char ech) |
| | | 281 | { |
| | | 282 | int depth, var_depth; |
| | | 283 | int escape; |
| | | 284 | const char *item = strlist_str(items, item_no); |
| | | 285 | int i; |
| | | 286 | char ch; |
| | | 287 | #define MAX_DEPTH 0x7fffffff |
| | | 288 | |
| | | 289 | /* If there were no escapes, or the only escape is the other variable |
| | | 290 | * terminator, then just substitute the full string */ |
| | | 291 | if (!(strlist_info(items, item_no) & |
| | | 292 | (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) { |
| | | 293 | Buf_AddBytes(cmds, strlen(item), item); |
| | | 294 | return; |
| | | 295 | } |
| | | 296 | |
| | | 297 | /* Escape ':' and 'ech' provided they aren't inside variable expansions */ |
| | | 298 | depth = 0; |
| | | 299 | var_depth = MAX_DEPTH; |
| | | 300 | escape = -1; |
| | | 301 | for (i = 0; (ch = item[i]) != 0; i++) { |
| | | 302 | /* Loose determination of nested variable definitions. */ |
| | | 303 | if (ch == '(' || ch == '{') { |
| | | 304 | depth++; |
| | | 305 | if (var_depth == MAX_DEPTH && i != 0 && item[i-1] == '$') |
| | | 306 | var_depth = depth; |
| | | 307 | } else if (ch == ')' || ch == '}') { |
| | | 308 | if (ch == ech && depth < var_depth) |
| | | 309 | escape = i; |
| | | 310 | if (depth == var_depth) |
| | | 311 | var_depth = MAX_DEPTH; |
| | | 312 | depth--; |
| | | 313 | } else if (ch == ':' && depth < var_depth) |
| | | 314 | escape = i; |
| | | 315 | if (escape == i) |
| | | 316 | Buf_AddByte(cmds, '\\'); |
| | | 317 | Buf_AddByte(cmds, ch); |
| | | 318 | } |
| | | 319 | |
| | | 320 | if (escape == -1) { |
| | | 321 | /* We didn't actually need to escape anything, remember for next time */ |
| | | 322 | strlist_set_info(items, item_no, strlist_info(items, item_no) & |
| | | 323 | (ech == ')' ? ~FOR_SUB_ESCAPE_PAREN : ~FOR_SUB_ESCAPE_BRACE)); |
| | | 324 | } |
| | | 325 | } |
| | | 326 | |
288 | void | | 327 | void |
289 | For_Run(int lineno) | | 328 | For_Run(int lineno) |
290 | { | | 329 | { |
291 | For arg; | | 330 | For arg; |
292 | int i, len; | | 331 | int i, len; |
293 | unsigned int num_items; | | 332 | unsigned int num_items; |
294 | char *for_body; | | 333 | char *for_body; |
295 | char *var, *item; | | 334 | char *var; |
296 | char *cp; | | 335 | char *cp; |
297 | char *cmd_cp; | | 336 | char *cmd_cp; |
298 | char *body_end; | | 337 | char *body_end; |
299 | char ch; | | 338 | char ch; |
300 | Buffer cmds; | | 339 | Buffer cmds; |
301 | int short_var; | | 340 | int short_var; |
302 | | | 341 | |
303 | arg = accumFor; | | 342 | arg = accumFor; |
304 | memset(&accumFor, 0, sizeof accumFor); | | 343 | memset(&accumFor, 0, sizeof accumFor); |
305 | | | 344 | |
306 | num_items = strlist_num(&arg.items); | | 345 | num_items = strlist_num(&arg.items); |
307 | if (num_items % strlist_num(&arg.vars)) | | 346 | if (num_items % strlist_num(&arg.vars)) |
308 | /* Error message already printed */ | | 347 | /* Error message already printed */ |
| @@ -327,64 +366,62 @@ For_Run(int lineno) | | | @@ -327,64 +366,62 @@ For_Run(int lineno) |
327 | * Many of the modifiers use \ to escape $ (not $) so it is possible | | 366 | * Many of the modifiers use \ to escape $ (not $) so it is possible |
328 | * to contrive a makefile where an unwanted substitution happens. | | 367 | * to contrive a makefile where an unwanted substitution happens. |
329 | * | | 368 | * |
330 | * Each loop expansion is fed back into the parser as if it were an | | 369 | * Each loop expansion is fed back into the parser as if it were an |
331 | * include file. This means we have to generate the last iteration first. | | 370 | * include file. This means we have to generate the last iteration first. |
332 | */ | | 371 | */ |
333 | while (num_items != 0) { | | 372 | while (num_items != 0) { |
334 | num_items -= strlist_num(&arg.vars); | | 373 | num_items -= strlist_num(&arg.vars); |
335 | for_body = (char *)Buf_GetAll(arg.buf, &len); | | 374 | for_body = (char *)Buf_GetAll(arg.buf, &len); |
336 | body_end = for_body + len; | | 375 | body_end = for_body + len; |
337 | cmds = Buf_Init(len + 256); | | 376 | cmds = Buf_Init(len + 256); |
338 | cmd_cp = for_body; | | 377 | cmd_cp = for_body; |
339 | for (cp = for_body; (cp = strchr(cp, '$')) != NULL;) { | | 378 | for (cp = for_body; (cp = strchr(cp, '$')) != NULL;) { |
| | | 379 | char ech; |
340 | ch = *++cp; | | 380 | ch = *++cp; |
341 | if (ch == '(' || ch == '{') { | | 381 | if ((ch == '(' && (ech = ')')) || (ch == '{' && (ech = '}'))) { |
342 | char ech = ch == '(' ? ')' : '}'; | | | |
343 | cp++; | | 382 | cp++; |
344 | /* Check variable name against the .for loop variables */ | | 383 | /* Check variable name against the .for loop variables */ |
345 | STRLIST_FOREACH(var, &arg.vars, i) { | | 384 | STRLIST_FOREACH(var, &arg.vars, i) { |
346 | len = strlen(var); | | 385 | len = strlist_info(&arg.vars, i); |
347 | if (memcmp(cp, var, len) != 0) | | 386 | if (memcmp(cp, var, len) != 0) |
348 | continue; | | 387 | continue; |
349 | if (cp[len] != ':' && cp[len] != ech && cp[len] != '\\') | | 388 | if (cp[len] != ':' && cp[len] != ech && cp[len] != '\\') |
350 | continue; | | 389 | continue; |
351 | /* Found a variable match. Replace with ${:U<value> */ | | 390 | /* Found a variable match. Replace with :U<value> */ |
352 | Buf_AddBytes(cmds, cp - cmd_cp, cmd_cp); | | 391 | Buf_AddBytes(cmds, cp - cmd_cp, cmd_cp); |
353 | Buf_AddBytes(cmds, 2, ":U"); | | 392 | Buf_AddBytes(cmds, 2, ":U"); |
354 | cp += len; | | 393 | cp += len; |
355 | cmd_cp = cp; | | 394 | cmd_cp = cp; |
356 | item = strlist_str(&arg.items, num_items + i); | | 395 | for_substitute(cmds, &arg.items, num_items + i, ech); |
357 | Buf_AddBytes(cmds, strlen(item), item); | | | |
358 | break; | | 396 | break; |
359 | } | | 397 | } |
360 | continue; | | 398 | continue; |
361 | } | | 399 | } |
362 | if (ch == 0) | | 400 | if (ch == 0) |
363 | break; | | 401 | break; |
364 | /* Probably a single character name, ignore $$ and stupid ones. */ | | 402 | /* Probably a single character name, ignore $$ and stupid ones. {*/ |
365 | if (!short_var || strchr("}):$", ch) != NULL) { | | 403 | if (!short_var || strchr("}):$", ch) != NULL) { |
366 | cp++; | | 404 | cp++; |
367 | continue; | | 405 | continue; |
368 | } | | 406 | } |
369 | STRLIST_FOREACH(var, &arg.vars, i) { | | 407 | STRLIST_FOREACH(var, &arg.vars, i) { |
370 | if (var[0] != ch || var[1] != 0) | | 408 | if (var[0] != ch || var[1] != 0) |
371 | continue; | | 409 | continue; |
372 | /* Found a variable match. Replace with ${:U<value>} */ | | 410 | /* Found a variable match. Replace with ${:U<value>} */ |
373 | Buf_AddBytes(cmds, cp - cmd_cp, cmd_cp); | | 411 | Buf_AddBytes(cmds, cp - cmd_cp, cmd_cp); |
374 | Buf_AddBytes(cmds, 3, "{:U"); | | 412 | Buf_AddBytes(cmds, 3, "{:U"); |
375 | cmd_cp = ++cp; | | 413 | cmd_cp = ++cp; |
376 | item = strlist_str(&arg.items, num_items + i); | | 414 | for_substitute(cmds, &arg.items, num_items + i, /*{*/ '}'); |
377 | Buf_AddBytes(cmds, strlen(item), item); | | | |
378 | Buf_AddBytes(cmds, 1, "}"); | | 415 | Buf_AddBytes(cmds, 1, "}"); |
379 | break; | | 416 | break; |
380 | } | | 417 | } |
381 | } | | 418 | } |
382 | Buf_AddBytes(cmds, body_end - cmd_cp, cmd_cp); | | 419 | Buf_AddBytes(cmds, body_end - cmd_cp, cmd_cp); |
383 | | | 420 | |
384 | cp = Buf_GetAll(cmds, NULL); | | 421 | cp = Buf_GetAll(cmds, NULL); |
385 | if (DEBUG(FOR)) | | 422 | if (DEBUG(FOR)) |
386 | (void)fprintf(debug_file, "For: loop body:\n%s", cp); | | 423 | (void)fprintf(debug_file, "For: loop body:\n%s", cp); |
387 | Parse_SetInput(NULL, lineno, -1, cp); | | 424 | Parse_SetInput(NULL, lineno, -1, cp); |
388 | Buf_Destroy(cmds, FALSE); | | 425 | Buf_Destroy(cmds, FALSE); |
389 | } | | 426 | } |
390 | | | 427 | |