Annotation of src/usr.bin/make/for.c, Revision 1.39
1.39 ! dsl 1: /* $NetBSD: for.c,v 1.38 2008/12/20 22:41:53 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.39 ! dsl 33: static char rcsid[] = "$NetBSD: for.c,v 1.38 2008/12/20 22:41:53 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.39 ! dsl 40: __RCSID("$NetBSD: for.c,v 1.38 2008/12/20 22:41:53 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:
64: /*
65: * For statements are of the form:
66: *
67: * .for <variable> in <varlist>
68: * ...
69: * .endfor
70: *
71: * 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.
73: * and the .endfor statements, accumulating all the statements between
1.4 christos 74: * the initial .for loop and the matching .endfor;
1.1 cgd 75: * then we evaluate the for loop for each variable in the varlist.
1.7 christos 76: *
77: * Note that any nested fors are just passed through; they get handled
78: * recursively in For_Eval when we're expanding the enclosing for in
79: * For_Run.
1.1 cgd 80: */
81:
82: static int forLevel = 0; /* Nesting level */
83:
84: /*
85: * State of a for loop.
86: */
1.2 jtc 87: typedef struct _For {
1.7 christos 88: Buffer buf; /* Body of loop */
1.38 dsl 89: strlist_t vars; /* Iteration variables */
90: strlist_t items; /* Substitution items */
1.2 jtc 91: } For;
1.1 cgd 92:
1.7 christos 93: static For accumFor; /* Loop being accumulated */
1.1 cgd 94:
95:
1.7 christos 96:
1.33 dsl 97: static char *
98: make_str(const char *ptr, int len)
99: {
100: char *new_ptr;
101:
102: new_ptr = bmake_malloc(len + 1);
103: memcpy(new_ptr, ptr, len);
104: new_ptr[len] = 0;
105: return new_ptr;
106: }
107:
1.7 christos 108: /*-
109: *-----------------------------------------------------------------------
1.1 cgd 110: * For_Eval --
111: * Evaluate the for loop in the passed line. The line
112: * looks like this:
113: * .for <variable> in <varlist>
114: *
1.13 wiz 115: * Input:
116: * line Line to parse
117: *
1.1 cgd 118: * Results:
1.35 dsl 119: * 0: Not a .for statement, parse the line
120: * 1: We found a for loop
121: * -1: A .for statement with a bad syntax error, discard.
1.1 cgd 122: *
123: * Side Effects:
124: * None.
125: *
126: *-----------------------------------------------------------------------
127: */
128: int
1.13 wiz 129: For_Eval(char *line)
1.1 cgd 130: {
1.33 dsl 131: char *ptr = line, *sub;
132: int len;
1.39 ! dsl 133: int i;
! 134: int escapes;
1.33 dsl 135:
136: /* Forget anything we previously knew about - it cannot be useful */
137: memset(&accumFor, 0, sizeof accumFor);
1.32 dsl 138:
139: forLevel = 0;
140: for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
141: continue;
142: /*
143: * If we are not in a for loop quickly determine if the statement is
144: * a for.
145: */
146: if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
147: !isspace((unsigned char) ptr[3])) {
148: if (ptr[0] == 'e' && strncmp(ptr+1, "ndfor", 5) == 0) {
149: Parse_Error(PARSE_FATAL, "for-less endfor");
150: return -1;
151: }
152: return 0;
153: }
154: ptr += 3;
1.1 cgd 155:
1.32 dsl 156: /*
157: * we found a for loop, and now we are going to parse it.
158: */
1.1 cgd 159:
1.33 dsl 160: /* Grab the variables. Terminate on "in". */
161: for (;; ptr += len) {
1.32 dsl 162: while (*ptr && isspace((unsigned char) *ptr))
163: ptr++;
1.33 dsl 164: if (*ptr == '\0') {
165: Parse_Error(PARSE_FATAL, "missing `in' in for");
166: return -1;
167: }
168: for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
169: continue;
170: if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') {
171: ptr += 2;
172: break;
173: }
1.38 dsl 174: strlist_add_str(&accumFor.vars, make_str(ptr, len));
1.32 dsl 175: }
1.1 cgd 176:
1.38 dsl 177: if (strlist_num(&accumFor.vars) == 0) {
1.32 dsl 178: Parse_Error(PARSE_FATAL, "no iteration variables in for");
179: return -1;
180: }
1.4 christos 181:
1.32 dsl 182: while (*ptr && isspace((unsigned char) *ptr))
183: ptr++;
184:
185: /*
186: * Make a list with the remaining words
1.39 ! dsl 187: * The values are substituted as ${:U<value>... so we must \ escape
! 188: * characters that break that syntax - particularly ':', maybe $ and \.
1.32 dsl 189: */
190: sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
1.4 christos 191:
1.38 dsl 192: for (ptr = sub;; ptr += len) {
1.33 dsl 193: while (*ptr && isspace((unsigned char)*ptr))
194: ptr++;
195: if (*ptr == 0)
196: break;
1.39 ! dsl 197: escapes = 0;
! 198: for (len = 0; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
! 199: if (ptr[len] == ':')
! 200: escapes++;
! 201: if (escapes == 0)
! 202: strlist_add_str(&accumFor.items, make_str(ptr, len));
! 203: else {
! 204: char *item = bmake_malloc(len + escapes + 1);
! 205: strlist_add_str(&accumFor.items, item);
! 206: for (i = 0; i < len; i++) {
! 207: if (ptr[i] == ':')
! 208: *item++ = '\\';
! 209: *item++ = ptr[i];
! 210: }
! 211: *item = 0;
! 212: }
1.33 dsl 213: }
1.1 cgd 214:
1.33 dsl 215: free(sub);
1.7 christos 216:
1.38 dsl 217: if (strlist_num(&accumFor.items) % strlist_num(&accumFor.vars)) {
1.33 dsl 218: Parse_Error(PARSE_FATAL,
219: "Wrong number of words in .for substitution list %d %d",
1.38 dsl 220: strlist_num(&accumFor.items), strlist_num(&accumFor.vars));
1.33 dsl 221: /*
222: * Return 'success' so that the body of the .for loop is accumulated.
223: * The loop will have zero iterations expanded due a later test.
224: */
1.32 dsl 225: }
226:
227: accumFor.buf = Buf_Init(0);
228: forLevel = 1;
229: return 1;
230: }
1.7 christos 231:
1.35 dsl 232: /*
233: * Add another line to a .for loop.
234: * Returns 0 when the matching .enfor is reached.
235: */
236:
1.32 dsl 237: int
238: For_Accum(char *line)
239: {
240: char *ptr = line;
1.26 dsl 241:
242: if (*ptr == '.') {
1.1 cgd 243:
1.2 jtc 244: for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
1.1 cgd 245: continue;
246:
1.2 jtc 247: if (strncmp(ptr, "endfor", 6) == 0 &&
1.26 dsl 248: (isspace((unsigned char) ptr[6]) || !ptr[6])) {
1.1 cgd 249: if (DEBUG(FOR))
1.23 dsl 250: (void)fprintf(debug_file, "For: end for %d\n", forLevel);
1.32 dsl 251: if (--forLevel <= 0)
1.1 cgd 252: return 0;
1.26 dsl 253: } else if (strncmp(ptr, "for", 3) == 0 &&
1.2 jtc 254: isspace((unsigned char) ptr[3])) {
1.1 cgd 255: forLevel++;
256: if (DEBUG(FOR))
1.23 dsl 257: (void)fprintf(debug_file, "For: new loop %d\n", forLevel);
1.1 cgd 258: }
259: }
260:
1.32 dsl 261: Buf_AddBytes(accumFor.buf, strlen(line), (Byte *)line);
262: Buf_AddByte(accumFor.buf, (Byte)'\n');
263: return 1;
1.1 cgd 264: }
265:
266:
267: /*-
268: *-----------------------------------------------------------------------
269: * For_Run --
1.7 christos 270: * Run the for loop, imitating the actions of an include file
1.1 cgd 271: *
272: * Results:
273: * None.
274: *
275: * Side Effects:
276: * None.
277: *
278: *-----------------------------------------------------------------------
279: */
280: void
1.16 enami 281: For_Run(int lineno)
1.1 cgd 282: {
1.2 jtc 283: For arg;
1.38 dsl 284: int i, len;
1.39 ! dsl 285: unsigned int num_items;
! 286: char *for_body;
1.38 dsl 287: char *var, *item;
1.39 ! dsl 288: char *cp;
! 289: char *cmd_cp;
! 290: char *body_end;
! 291: char ch;
! 292: Buffer cmds;
! 293: int short_var;
1.7 christos 294:
295: arg = accumFor;
1.38 dsl 296: memset(&accumFor, 0, sizeof accumFor);
1.1 cgd 297:
1.39 ! dsl 298: num_items = strlist_num(&arg.items);
! 299: if (num_items % strlist_num(&arg.vars))
1.33 dsl 300: /* Error message already printed */
1.38 dsl 301: goto out;
1.7 christos 302:
1.39 ! dsl 303: short_var = 0;
! 304: STRLIST_FOREACH(var, &arg.vars, i) {
! 305: if (var[1] == 0) {
! 306: short_var = 1;
! 307: break;
1.10 mycroft 308: }
1.7 christos 309: }
310:
1.39 ! dsl 311: /*
! 312: * Scan the for loop body and replace references to the loop variables
! 313: * with variable references that expand to the required text.
! 314: * Using variable expansions ensures that the .for loop can't generate
! 315: * syntax, and that the later parsing will still see a variable.
! 316: * We assume that the null variable will never be defined.
! 317: *
! 318: * The detection of substitions of the loop control variable is naive.
! 319: * Many of the modifiers use \ to escape $ (not $) so it is possible
! 320: * to contrive a makefile where an unwanted substitution happens.
! 321: *
! 322: * Each loop expansion is fed back into the parser as if it were an
! 323: * include file. This means we have to generate the last iteration first.
! 324: */
! 325: while (num_items != 0) {
! 326: num_items -= strlist_num(&arg.vars);
! 327: for_body = (char *)Buf_GetAll(arg.buf, &len);
! 328: body_end = for_body + len;
! 329: cmds = Buf_Init(len + 256);
! 330: cmd_cp = for_body;
! 331: for (cp = for_body; (cp = strchr(cp, '$')) != NULL;) {
! 332: ch = *++cp;
! 333: if (ch == '(' || ch == '{') {
! 334: char ech = ch == '(' ? ')' : '}';
! 335: cp++;
! 336: /* Check variable name against the .for loop varoables */
! 337: STRLIST_FOREACH(var, &arg.vars, i) {
! 338: len = strlen(var);
! 339: if (memcmp(cp, var, len) != 0)
! 340: continue;
! 341: if (cp[len] != ':' && cp[len] != ech)
! 342: continue;
! 343: /* Found a variable match. Replace with ${:U<value> */
! 344: Buf_AddBytes(cmds, cp - cmd_cp, cmd_cp);
! 345: Buf_AddBytes(cmds, 2, ":U");
! 346: cp += len;
! 347: cmd_cp = cp;
! 348: item = strlist_str(&arg.items, num_items + i);
! 349: Buf_AddBytes(cmds, strlen(item), item);
! 350: break;
! 351: }
! 352: continue;
! 353: }
! 354: if (ch == 0)
! 355: break;
! 356: /* Probably a single character name, ignore $$ and stupid ones. */
! 357: if (!short_var || strchr("}):$", ch) != NULL) {
! 358: cp++;
! 359: continue;
! 360: }
! 361: STRLIST_FOREACH(var, &arg.vars, i) {
! 362: if (var[0] != ch || var[1] != 0)
! 363: continue;
! 364: /* Found a variable match. Replace with ${:U<value>} */
! 365: Buf_AddBytes(cmds, cp - cmd_cp, cmd_cp);
! 366: Buf_AddBytes(cmds, 3, "{:U");
! 367: cmd_cp = ++cp;
! 368: item = strlist_str(&arg.items, num_items + i);
! 369: Buf_AddBytes(cmds, strlen(item), item);
! 370: Buf_AddBytes(cmds, 1, "}");
! 371: break;
! 372: }
! 373: }
! 374: Buf_AddBytes(cmds, body_end - cmd_cp, cmd_cp);
! 375:
! 376: cp = Buf_GetAll(cmds, NULL);
! 377: if (DEBUG(FOR))
! 378: (void)fprintf(debug_file, "For: loop body:\n%s", cp);
! 379: Parse_SetInput(NULL, lineno, -1, cp);
! 380: Buf_Destroy(cmds, FALSE);
! 381: }
1.7 christos 382:
1.38 dsl 383: out:
384: strlist_clean(&arg.vars);
385: strlist_clean(&arg.items);
1.1 cgd 386:
387: Buf_Destroy(arg.buf, TRUE);
388: }
CVSweb <webmaster@jp.NetBSD.org>