Annotation of src/usr.bin/make/for.c, Revision 1.47
1.47 ! dholland 1: /* $NetBSD: for.c,v 1.46 2009/01/17 13:29:37 dsl Exp $ */
1.3 christos 2:
1.1 cgd 3: /*
4: * Copyright (c) 1992, The Regents of the University of California.
5: * All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
1.15 agc 15: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 16: * may be used to endorse or promote products derived from this software
17: * without specific prior written permission.
18: *
19: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
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
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
29: * SUCH DAMAGE.
30: */
31:
1.17 ross 32: #ifndef MAKE_NATIVE
1.47 ! dholland 33: static char rcsid[] = "$NetBSD: for.c,v 1.46 2009/01/17 13:29:37 dsl Exp $";
1.6 lukem 34: #else
1.5 christos 35: #include <sys/cdefs.h>
1.1 cgd 36: #ifndef lint
1.3 christos 37: #if 0
1.4 christos 38: static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93";
1.3 christos 39: #else
1.47 ! dholland 40: __RCSID("$NetBSD: for.c,v 1.46 2009/01/17 13:29:37 dsl Exp $");
1.3 christos 41: #endif
1.1 cgd 42: #endif /* not lint */
1.6 lukem 43: #endif
1.1 cgd 44:
45: /*-
46: * for.c --
47: * Functions to handle loops in a makefile.
48: *
49: * Interface:
50: * For_Eval Evaluate the loop in the passed line.
51: * For_Run Run accumulated loop
52: *
53: */
54:
1.13 wiz 55: #include <assert.h>
1.1 cgd 56: #include <ctype.h>
1.13 wiz 57:
1.1 cgd 58: #include "make.h"
59: #include "hash.h"
60: #include "dir.h"
61: #include "buf.h"
1.38 dsl 62: #include "strlist.h"
1.1 cgd 63:
1.44 dsl 64: #define FOR_SUB_ESCAPE_CHAR 1
1.42 dsl 65: #define FOR_SUB_ESCAPE_BRACE 2
66: #define FOR_SUB_ESCAPE_PAREN 4
67:
1.1 cgd 68: /*
69: * For statements are of the form:
70: *
71: * .for <variable> in <varlist>
72: * ...
73: * .endfor
74: *
75: * The trick is to look for the matching end inside for for loop
76: * To do that, we count the current nesting level of the for loops.
77: * and the .endfor statements, accumulating all the statements between
1.4 christos 78: * the initial .for loop and the matching .endfor;
1.1 cgd 79: * then we evaluate the for loop for each variable in the varlist.
1.7 christos 80: *
81: * Note that any nested fors are just passed through; they get handled
82: * recursively in For_Eval when we're expanding the enclosing for in
83: * For_Run.
1.1 cgd 84: */
85:
86: static int forLevel = 0; /* Nesting level */
87:
88: /*
89: * State of a for loop.
90: */
1.2 jtc 91: typedef struct _For {
1.7 christos 92: Buffer buf; /* Body of loop */
1.38 dsl 93: strlist_t vars; /* Iteration variables */
94: strlist_t items; /* Substitution items */
1.43 dsl 95: char *parse_buf;
96: int short_var;
97: int sub_next;
1.2 jtc 98: } For;
1.1 cgd 99:
1.43 dsl 100: static For *accumFor; /* Loop being accumulated */
1.1 cgd 101:
102:
1.7 christos 103:
1.33 dsl 104: static char *
105: make_str(const char *ptr, int len)
106: {
107: char *new_ptr;
108:
109: new_ptr = bmake_malloc(len + 1);
110: memcpy(new_ptr, ptr, len);
111: new_ptr[len] = 0;
112: return new_ptr;
113: }
114:
1.43 dsl 115: static void
116: For_Free(For *arg)
117: {
1.46 dsl 118: Buf_Destroy(&arg->buf, TRUE);
1.43 dsl 119: strlist_clean(&arg->vars);
120: strlist_clean(&arg->items);
121: free(arg->parse_buf);
122:
123: free(arg);
124: }
125:
1.7 christos 126: /*-
127: *-----------------------------------------------------------------------
1.1 cgd 128: * For_Eval --
129: * Evaluate the for loop in the passed line. The line
130: * looks like this:
131: * .for <variable> in <varlist>
132: *
1.13 wiz 133: * Input:
134: * line Line to parse
135: *
1.1 cgd 136: * Results:
1.35 dsl 137: * 0: Not a .for statement, parse the line
138: * 1: We found a for loop
139: * -1: A .for statement with a bad syntax error, discard.
1.1 cgd 140: *
141: * Side Effects:
142: * None.
143: *
144: *-----------------------------------------------------------------------
145: */
146: int
1.13 wiz 147: For_Eval(char *line)
1.1 cgd 148: {
1.43 dsl 149: For *new_for;
1.33 dsl 150: char *ptr = line, *sub;
151: int len;
1.39 dsl 152: int escapes;
1.42 dsl 153: unsigned char ch;
1.33 dsl 154:
1.43 dsl 155: /* Skip the '.' and any following whitespace */
1.32 dsl 156: for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
157: continue;
1.43 dsl 158:
1.32 dsl 159: /*
160: * If we are not in a for loop quickly determine if the statement is
161: * a for.
162: */
163: if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
164: !isspace((unsigned char) ptr[3])) {
165: if (ptr[0] == 'e' && strncmp(ptr+1, "ndfor", 5) == 0) {
166: Parse_Error(PARSE_FATAL, "for-less endfor");
167: return -1;
168: }
169: return 0;
170: }
171: ptr += 3;
1.1 cgd 172:
1.32 dsl 173: /*
174: * we found a for loop, and now we are going to parse it.
175: */
1.1 cgd 176:
1.43 dsl 177: new_for = bmake_malloc(sizeof *new_for);
178: memset(new_for, 0, sizeof *new_for);
179:
1.33 dsl 180: /* Grab the variables. Terminate on "in". */
181: for (;; ptr += len) {
1.32 dsl 182: while (*ptr && isspace((unsigned char) *ptr))
183: ptr++;
1.33 dsl 184: if (*ptr == '\0') {
185: Parse_Error(PARSE_FATAL, "missing `in' in for");
1.43 dsl 186: For_Free(new_for);
1.33 dsl 187: return -1;
188: }
189: for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
190: continue;
191: if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') {
192: ptr += 2;
193: break;
194: }
1.43 dsl 195: if (len == 1)
196: new_for->short_var = 1;
197: strlist_add_str(&new_for->vars, make_str(ptr, len), len);
1.32 dsl 198: }
1.1 cgd 199:
1.43 dsl 200: if (strlist_num(&new_for->vars) == 0) {
1.32 dsl 201: Parse_Error(PARSE_FATAL, "no iteration variables in for");
1.43 dsl 202: For_Free(new_for);
1.32 dsl 203: return -1;
204: }
1.4 christos 205:
1.32 dsl 206: while (*ptr && isspace((unsigned char) *ptr))
207: ptr++;
208:
209: /*
210: * Make a list with the remaining words
1.42 dsl 211: * The values are substituted as ${:U<value>...} so we must \ escape
1.44 dsl 212: * characters that break that syntax.
213: * Variables are fully expanded - so it is safe for escape $.
214: * We can't do the escapes here - because we don't know whether
215: * we are substuting into ${...} or $(...).
1.32 dsl 216: */
217: sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
1.4 christos 218:
1.38 dsl 219: for (ptr = sub;; ptr += len) {
1.33 dsl 220: while (*ptr && isspace((unsigned char)*ptr))
221: ptr++;
222: if (*ptr == 0)
223: break;
1.39 dsl 224: escapes = 0;
1.42 dsl 225: for (len = 0; (ch = ptr[len]) != 0 && !isspace(ch); len++) {
1.44 dsl 226: if (ch == ':' || ch == '$' || ch == '\\')
227: escapes |= FOR_SUB_ESCAPE_CHAR;
1.42 dsl 228: else if (ch == ')')
229: escapes |= FOR_SUB_ESCAPE_PAREN;
230: else if (ch == /*{*/ '}')
231: escapes |= FOR_SUB_ESCAPE_BRACE;
1.39 dsl 232: }
1.43 dsl 233: strlist_add_str(&new_for->items, make_str(ptr, len), escapes);
1.33 dsl 234: }
1.1 cgd 235:
1.33 dsl 236: free(sub);
1.7 christos 237:
1.43 dsl 238: if (strlist_num(&new_for->items) % strlist_num(&new_for->vars)) {
1.33 dsl 239: Parse_Error(PARSE_FATAL,
1.47 ! dholland 240: "Wrong number of words (%d) in .for substitution list"
! 241: " with %d vars",
1.43 dsl 242: strlist_num(&new_for->items), strlist_num(&new_for->vars));
1.33 dsl 243: /*
244: * Return 'success' so that the body of the .for loop is accumulated.
1.43 dsl 245: * Remove all items so that the loop doesn't iterate.
1.33 dsl 246: */
1.43 dsl 247: strlist_clean(&new_for->items);
1.32 dsl 248: }
249:
1.46 dsl 250: Buf_Init(&new_for->buf, 0);
1.43 dsl 251: accumFor = new_for;
1.32 dsl 252: forLevel = 1;
253: return 1;
254: }
1.7 christos 255:
1.35 dsl 256: /*
257: * Add another line to a .for loop.
1.43 dsl 258: * Returns 0 when the matching .endfor is reached.
1.35 dsl 259: */
260:
1.32 dsl 261: int
262: For_Accum(char *line)
263: {
264: char *ptr = line;
1.26 dsl 265:
266: if (*ptr == '.') {
1.1 cgd 267:
1.2 jtc 268: for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
1.1 cgd 269: continue;
270:
1.2 jtc 271: if (strncmp(ptr, "endfor", 6) == 0 &&
1.26 dsl 272: (isspace((unsigned char) ptr[6]) || !ptr[6])) {
1.1 cgd 273: if (DEBUG(FOR))
1.23 dsl 274: (void)fprintf(debug_file, "For: end for %d\n", forLevel);
1.32 dsl 275: if (--forLevel <= 0)
1.1 cgd 276: return 0;
1.26 dsl 277: } else if (strncmp(ptr, "for", 3) == 0 &&
1.2 jtc 278: isspace((unsigned char) ptr[3])) {
1.1 cgd 279: forLevel++;
280: if (DEBUG(FOR))
1.23 dsl 281: (void)fprintf(debug_file, "For: new loop %d\n", forLevel);
1.1 cgd 282: }
283: }
284:
1.46 dsl 285: Buf_AddBytes(&accumFor->buf, strlen(line), line);
286: Buf_AddByte(&accumFor->buf, '\n');
1.32 dsl 287: return 1;
1.1 cgd 288: }
289:
290:
291: /*-
292: *-----------------------------------------------------------------------
293: * For_Run --
1.7 christos 294: * Run the for loop, imitating the actions of an include file
1.1 cgd 295: *
296: * Results:
297: * None.
298: *
299: * Side Effects:
300: * None.
301: *
302: *-----------------------------------------------------------------------
303: */
1.42 dsl 304:
1.45 dsl 305: static int
306: for_var_len(const char *var)
307: {
308: char ch, var_start, var_end;
309: int depth;
310: int len;
311:
312: var_start = *var;
313: if (var_start == 0)
314: /* just escape the $ */
315: return 0;
316:
317: if (var_start == '(')
318: var_end = ')';
319: else if (var_start == '{')
320: var_end = '}';
321: else
322: /* Single char variable */
323: return 1;
324:
325: depth = 1;
326: for (len = 1; (ch = var[len++]) != 0;) {
327: if (ch == var_start)
328: depth++;
329: else if (ch == var_end && --depth == 0)
330: return len;
331: }
332:
333: /* Variable end not found, escape the $ */
334: return 0;
335: }
336:
1.42 dsl 337: static void
1.46 dsl 338: for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech)
1.42 dsl 339: {
340: const char *item = strlist_str(items, item_no);
1.45 dsl 341: int len;
1.42 dsl 342: char ch;
343:
344: /* If there were no escapes, or the only escape is the other variable
345: * terminator, then just substitute the full string */
346: if (!(strlist_info(items, item_no) &
347: (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) {
348: Buf_AddBytes(cmds, strlen(item), item);
349: return;
350: }
351:
1.44 dsl 352: /* Escape ':', '$', '\\' and 'ech' - removed by :U processing */
1.45 dsl 353: while ((ch = *item++) != 0) {
354: if (ch == '$') {
355: len = for_var_len(item);
356: if (len != 0) {
357: Buf_AddBytes(cmds, len + 1, item - 1);
358: item += len;
359: continue;
360: }
361: Buf_AddByte(cmds, '\\');
362: } else if (ch == ':' || ch == '\\' || ch == ech)
1.42 dsl 363: Buf_AddByte(cmds, '\\');
364: Buf_AddByte(cmds, ch);
365: }
366: }
367:
1.43 dsl 368: static char *
369: For_Iterate(void *v_arg)
1.1 cgd 370: {
1.43 dsl 371: For *arg = v_arg;
1.38 dsl 372: int i, len;
1.42 dsl 373: char *var;
1.39 dsl 374: char *cp;
375: char *cmd_cp;
376: char *body_end;
377: char ch;
378: Buffer cmds;
1.7 christos 379:
1.43 dsl 380: if (arg->sub_next + strlist_num(&arg->vars) > strlist_num(&arg->items)) {
381: /* No more iterations */
382: For_Free(arg);
383: return NULL;
384: }
1.1 cgd 385:
1.43 dsl 386: free(arg->parse_buf);
387: arg->parse_buf = NULL;
1.7 christos 388:
1.39 dsl 389: /*
390: * Scan the for loop body and replace references to the loop variables
391: * with variable references that expand to the required text.
392: * Using variable expansions ensures that the .for loop can't generate
393: * syntax, and that the later parsing will still see a variable.
394: * We assume that the null variable will never be defined.
395: *
396: * The detection of substitions of the loop control variable is naive.
397: * Many of the modifiers use \ to escape $ (not $) so it is possible
398: * to contrive a makefile where an unwanted substitution happens.
399: */
1.43 dsl 400:
1.46 dsl 401: cmd_cp = Buf_GetAll(&arg->buf, &len);
1.43 dsl 402: body_end = cmd_cp + len;
1.46 dsl 403: Buf_Init(&cmds, len + 256);
1.43 dsl 404: for (cp = cmd_cp; (cp = strchr(cp, '$')) != NULL;) {
405: char ech;
406: ch = *++cp;
407: if ((ch == '(' && (ech = ')')) || (ch == '{' && (ech = '}'))) {
408: cp++;
409: /* Check variable name against the .for loop variables */
410: STRLIST_FOREACH(var, &arg->vars, i) {
411: len = strlist_info(&arg->vars, i);
412: if (memcmp(cp, var, len) != 0)
413: continue;
414: if (cp[len] != ':' && cp[len] != ech && cp[len] != '\\')
1.39 dsl 415: continue;
1.43 dsl 416: /* Found a variable match. Replace with :U<value> */
1.46 dsl 417: Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp);
418: Buf_AddBytes(&cmds, 2, ":U");
1.43 dsl 419: cp += len;
420: cmd_cp = cp;
1.46 dsl 421: for_substitute(&cmds, &arg->items, arg->sub_next + i, ech);
1.39 dsl 422: break;
423: }
1.43 dsl 424: continue;
425: }
426: if (ch == 0)
427: break;
428: /* Probably a single character name, ignore $$ and stupid ones. {*/
429: if (!arg->short_var || strchr("}):$", ch) != NULL) {
430: cp++;
431: continue;
432: }
433: STRLIST_FOREACH(var, &arg->vars, i) {
434: if (var[0] != ch || var[1] != 0)
435: continue;
436: /* Found a variable match. Replace with ${:U<value>} */
1.46 dsl 437: Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp);
438: Buf_AddBytes(&cmds, 3, "{:U");
1.43 dsl 439: cmd_cp = ++cp;
1.46 dsl 440: for_substitute(&cmds, &arg->items, arg->sub_next + i, /*{*/ '}');
441: Buf_AddBytes(&cmds, 1, "}");
1.43 dsl 442: break;
1.39 dsl 443: }
1.43 dsl 444: }
1.46 dsl 445: Buf_AddBytes(&cmds, body_end - cmd_cp, cmd_cp);
1.43 dsl 446:
1.46 dsl 447: cp = Buf_Destroy(&cmds, FALSE);
1.43 dsl 448: if (DEBUG(FOR))
449: (void)fprintf(debug_file, "For: loop body:\n%s", cp);
1.39 dsl 450:
1.43 dsl 451: arg->sub_next += strlist_num(&arg->vars);
452:
453: arg->parse_buf = cp;
454: return cp;
455: }
1.7 christos 456:
1.43 dsl 457: void
458: For_Run(int lineno)
459: {
460: For *arg;
461:
462: arg = accumFor;
463: accumFor = NULL;
1.1 cgd 464:
1.43 dsl 465: if (strlist_num(&arg->items) == 0) {
466: /* Nothing to expand - possibly due to an earlier syntax error. */
467: For_Free(arg);
468: return;
469: }
470:
471: Parse_SetInput(NULL, lineno, -1, For_Iterate, arg);
1.1 cgd 472: }
CVSweb <webmaster@jp.NetBSD.org>