Annotation of src/usr.bin/make/for.c, Revision 1.25
1.25 ! dsl 1: /* $NetBSD: for.c,v 1.24 2006/10/27 21:00:19 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.25 ! dsl 33: static char rcsid[] = "$NetBSD: for.c,v 1.24 2006/10/27 21:00:19 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.25 ! dsl 40: __RCSID("$NetBSD: for.c,v 1.24 2006/10/27 21:00:19 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"
62:
63: /*
64: * For statements are of the form:
65: *
66: * .for <variable> in <varlist>
67: * ...
68: * .endfor
69: *
70: * The trick is to look for the matching end inside for for loop
71: * To do that, we count the current nesting level of the for loops.
72: * and the .endfor statements, accumulating all the statements between
1.4 christos 73: * the initial .for loop and the matching .endfor;
1.1 cgd 74: * then we evaluate the for loop for each variable in the varlist.
1.7 christos 75: *
76: * Note that any nested fors are just passed through; they get handled
77: * recursively in For_Eval when we're expanding the enclosing for in
78: * For_Run.
1.1 cgd 79: */
80:
81: static int forLevel = 0; /* Nesting level */
82:
83: /*
84: * State of a for loop.
85: */
1.2 jtc 86: typedef struct _For {
1.7 christos 87: Buffer buf; /* Body of loop */
88: char **vars; /* Iteration variables */
89: int nvars; /* # of iteration vars */
90: Lst lst; /* List of items */
1.2 jtc 91: } For;
1.1 cgd 92:
1.7 christos 93: static For accumFor; /* Loop being accumulated */
1.1 cgd 94:
1.13 wiz 95: static void ForAddVar(const char *, size_t);
1.1 cgd 96:
97:
98:
1.7 christos 99:
100: /*-
101: *-----------------------------------------------------------------------
102: * ForAddVar --
103: * Add an iteration variable to the currently accumulating for.
104: *
105: * Results: none
106: * Side effects: no additional side effects.
107: *-----------------------------------------------------------------------
108: */
109: static void
1.13 wiz 110: ForAddVar(const char *data, size_t len)
1.7 christos 111: {
112: Buffer buf;
1.8 simonb 113: int varlen;
1.7 christos 114:
115: buf = Buf_Init(0);
1.14 christos 116: Buf_AddBytes(buf, len, (Byte *)UNCONST(data));
1.7 christos 117:
118: accumFor.nvars++;
119: accumFor.vars = erealloc(accumFor.vars, accumFor.nvars*sizeof(char *));
120:
1.19 christos 121: accumFor.vars[accumFor.nvars-1] = (char *)Buf_GetAll(buf, &varlen);
1.7 christos 122:
123: Buf_Destroy(buf, FALSE);
124: }
125:
1.1 cgd 126: /*-
127: *-----------------------------------------------------------------------
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:
137: * TRUE: We found a for loop, or we are inside a for loop
138: * FALSE: We did not find a for loop, or we found the end of the for
139: * for loop.
140: *
141: * Side Effects:
142: * None.
143: *
144: *-----------------------------------------------------------------------
145: */
146: int
1.13 wiz 147: For_Eval(char *line)
1.1 cgd 148: {
1.7 christos 149: char *ptr = line, *sub, *in, *wrd;
1.1 cgd 150: int level; /* Level at which to report errors. */
151:
152: level = PARSE_FATAL;
153:
154:
155: if (forLevel == 0) {
156: Buffer buf;
157: int varlen;
1.12 christos 158: static const char instr[] = "in";
1.1 cgd 159:
1.2 jtc 160: for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
1.1 cgd 161: continue;
162: /*
163: * If we are not in a for loop quickly determine if the statement is
164: * a for.
165: */
1.2 jtc 166: if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
167: !isspace((unsigned char) ptr[3]))
1.1 cgd 168: return FALSE;
169: ptr += 3;
1.4 christos 170:
1.1 cgd 171: /*
172: * we found a for loop, and now we are going to parse it.
173: */
1.2 jtc 174: while (*ptr && isspace((unsigned char) *ptr))
1.1 cgd 175: ptr++;
1.4 christos 176:
1.1 cgd 177: /*
1.7 christos 178: * Find the "in".
1.1 cgd 179: */
1.7 christos 180: for (in = ptr; *in; in++) {
181: if (isspace((unsigned char) in[0]) && in[1]== 'i' &&
182: in[2] == 'n' &&
183: (in[3] == '\0' || isspace((unsigned char) in[3])))
184: break;
185: }
186: if (*in == '\0') {
187: Parse_Error(level, "missing `in' in for");
1.1 cgd 188: return 0;
189: }
190:
191: /*
1.7 christos 192: * Grab the variables.
1.1 cgd 193: */
1.7 christos 194: accumFor.vars = NULL;
195:
196: while (ptr < in) {
197: wrd = ptr;
198: while (*ptr && !isspace((unsigned char) *ptr))
199: ptr++;
200: ForAddVar(wrd, ptr - wrd);
201: while (*ptr && isspace((unsigned char) *ptr))
202: ptr++;
203: }
204:
205: if (accumFor.nvars == 0) {
206: Parse_Error(level, "no iteration variables in for");
1.1 cgd 207: return 0;
208: }
1.7 christos 209:
210: /* At this point we should be pointing right at the "in" */
1.12 christos 211: /*
212: * compensate for hp/ux's brain damaged assert macro that
213: * does not handle double quotes nicely.
214: */
215: assert(!memcmp(ptr, instr, 2));
1.7 christos 216: ptr += 2;
1.1 cgd 217:
1.2 jtc 218: while (*ptr && isspace((unsigned char) *ptr))
1.1 cgd 219: ptr++;
220:
221: /*
222: * Make a list with the remaining words
223: */
1.7 christos 224: accumFor.lst = Lst_Init(FALSE);
1.1 cgd 225: buf = Buf_Init(0);
1.4 christos 226: sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
1.1 cgd 227:
228: #define ADDWORD() \
1.21 christos 229: Buf_AddBytes(buf, ptr - wrd, (Byte *)wrd), \
230: Buf_AddByte(buf, (Byte)'\0'), \
1.24 dsl 231: Lst_AtFront(accumFor.lst, Buf_GetAll(buf, &varlen)), \
1.1 cgd 232: Buf_Destroy(buf, FALSE)
233:
1.2 jtc 234: for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++)
1.1 cgd 235: continue;
236:
237: for (wrd = ptr; *ptr; ptr++)
1.2 jtc 238: if (isspace((unsigned char) *ptr)) {
1.1 cgd 239: ADDWORD();
240: buf = Buf_Init(0);
1.2 jtc 241: while (*ptr && isspace((unsigned char) *ptr))
1.1 cgd 242: ptr++;
243: wrd = ptr--;
244: }
1.7 christos 245: if (DEBUG(FOR)) {
246: int i;
247: for (i = 0; i < accumFor.nvars; i++) {
1.23 dsl 248: (void)fprintf(debug_file, "For: variable %s\n", accumFor.vars[i]);
1.7 christos 249: }
1.23 dsl 250: (void)fprintf(debug_file, "For: list %s\n", sub);
1.7 christos 251: }
1.4 christos 252: if (ptr - wrd > 0)
1.1 cgd 253: ADDWORD();
254: else
255: Buf_Destroy(buf, TRUE);
1.20 christos 256: free(sub);
1.4 christos 257:
1.7 christos 258: accumFor.buf = Buf_Init(0);
1.1 cgd 259: forLevel++;
260: return 1;
261: }
262: else if (*ptr == '.') {
263:
1.2 jtc 264: for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
1.1 cgd 265: continue;
266:
1.2 jtc 267: if (strncmp(ptr, "endfor", 6) == 0 &&
268: (isspace((unsigned char) ptr[6]) || !ptr[6])) {
1.1 cgd 269: if (DEBUG(FOR))
1.23 dsl 270: (void)fprintf(debug_file, "For: end for %d\n", forLevel);
1.1 cgd 271: if (--forLevel < 0) {
1.18 christos 272: Parse_Error(level, "for-less endfor");
1.1 cgd 273: return 0;
274: }
275: }
1.2 jtc 276: else if (strncmp(ptr, "for", 3) == 0 &&
277: isspace((unsigned char) ptr[3])) {
1.1 cgd 278: forLevel++;
279: if (DEBUG(FOR))
1.23 dsl 280: (void)fprintf(debug_file, "For: new loop %d\n", forLevel);
1.1 cgd 281: }
282: }
283:
284: if (forLevel != 0) {
1.21 christos 285: Buf_AddBytes(accumFor.buf, strlen(line), (Byte *)line);
286: Buf_AddByte(accumFor.buf, (Byte)'\n');
1.1 cgd 287: return 1;
288: }
289: else {
290: return 0;
291: }
292: }
293:
294:
295: /*-
296: *-----------------------------------------------------------------------
297: * For_Run --
1.7 christos 298: * Run the for loop, imitating the actions of an include file
1.1 cgd 299: *
300: * Results:
301: * None.
302: *
303: * Side Effects:
304: * None.
305: *
306: *-----------------------------------------------------------------------
307: */
308: void
1.16 enami 309: For_Run(int lineno)
1.1 cgd 310: {
1.2 jtc 311: For arg;
1.7 christos 312: LstNode ln;
313: char **values;
314: int i, done = 0, len;
1.10 mycroft 315: char *guy, *orig_guy, *old_guy;
1.7 christos 316:
317: if (accumFor.buf == NULL || accumFor.vars == NULL || accumFor.lst == NULL)
318: return;
319: arg = accumFor;
320: accumFor.buf = NULL;
321: accumFor.vars = NULL;
322: accumFor.nvars = 0;
323: accumFor.lst = NULL;
1.1 cgd 324:
1.7 christos 325: if (Lst_Open(arg.lst) != SUCCESS)
1.1 cgd 326: return;
327:
1.7 christos 328: values = emalloc(arg.nvars * sizeof(char *));
329:
330: while (!done) {
331: /*
332: * due to the dumb way this is set up, this loop must run
333: * backwards.
334: */
335: for (i = arg.nvars - 1; i >= 0; i--) {
336: ln = Lst_Next(arg.lst);
337: if (ln == NILLNODE) {
338: if (i != arg.nvars-1) {
339: Parse_Error(PARSE_FATAL,
340: "Not enough words in for substitution list");
341: }
342: done = 1;
343: break;
344: } else {
1.19 christos 345: values[i] = (char *)Lst_Datum(ln);
1.7 christos 346: }
347: }
348: if (done)
349: break;
350:
351: for (i = 0; i < arg.nvars; i++) {
1.11 sjg 352: Var_Set(arg.vars[i], values[i], VAR_GLOBAL, 0);
1.7 christos 353: if (DEBUG(FOR))
1.23 dsl 354: (void)fprintf(debug_file, "--- %s = %s\n", arg.vars[i],
1.7 christos 355: values[i]);
356: }
357:
358: /*
359: * Hack, hack, kludge.
360: * This is really ugly, but to do it any better way would require
361: * making major changes to var.c, which I don't want to get into
362: * yet. There is no mechanism for expanding some variables, only
363: * for expanding a single variable. That should be corrected, but
364: * not right away. (XXX)
365: */
366:
1.19 christos 367: guy = (char *)Buf_GetAll(arg.buf, &len);
1.10 mycroft 368: orig_guy = guy;
369: for (i = 0; i < arg.nvars; i++) {
370: old_guy = guy;
371: guy = Var_Subst(arg.vars[i], guy, VAR_GLOBAL, FALSE);
372: if (old_guy != orig_guy)
373: free(old_guy);
374: }
1.25 ! dsl 375: Parse_SetInput(NULL, lineno, -1, guy);
1.7 christos 376:
377: for (i = 0; i < arg.nvars; i++)
1.10 mycroft 378: Var_Delete(arg.vars[i], VAR_GLOBAL);
1.7 christos 379: }
380:
381: free(values);
382:
383: Lst_Close(arg.lst);
384:
385: for (i=0; i<arg.nvars; i++) {
386: free(arg.vars[i]);
387: }
388: free(arg.vars);
1.1 cgd 389:
1.22 christos 390: Lst_Destroy(arg.lst, (FreeProc *)free);
1.1 cgd 391: Buf_Destroy(arg.buf, TRUE);
392: }
CVSweb <webmaster@jp.NetBSD.org>