Sat Jan 10 16:59:02 2009 UTC ()
When substituting .for control variables any } or ) that matches the
${ or $( must be \ escaped.
Should fix some pkgsrc issues - eg 'clean' in print/gv.


(dsl)
diff -r1.41 -r1.42 src/usr.bin/make/for.c

cvs diff -r1.41 -r1.42 src/usr.bin/make/for.c (expand / switch to unified diff)

--- src/usr.bin/make/for.c 2008/12/29 10:12:30 1.41
+++ src/usr.bin/make/for.c 2009/01/10 16:59:02 1.42
@@ -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
33static char rcsid[] = "$NetBSD: for.c,v 1.41 2008/12/29 10:12:30 dsl Exp $"; 33static 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
38static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93"; 38static 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 */
128int 132int
129For_Eval(char *line) 133For_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
 279static void
 280for_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
288void 327void
289For_Run(int lineno) 328For_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