Annotation of src/usr.bin/make/for.c, Revision 1.10
1.9 mycroft 1: /* $NetBSD: for.c,v 1.8 2000/04/18 03:46:41 simonb 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.
15: * 3. All advertising materials mentioning features or use of this software
16: * must display the following acknowledgement:
17: * This product includes software developed by the University of
18: * California, Berkeley and its contributors.
19: * 4. Neither the name of the University nor the names of its contributors
20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
1.6 lukem 36: #ifdef MAKE_BOOTSTRAP
1.9 mycroft 37: static char rcsid[] = "$NetBSD: for.c,v 1.8 2000/04/18 03:46:41 simonb Exp $";
1.6 lukem 38: #else
1.5 christos 39: #include <sys/cdefs.h>
1.1 cgd 40: #ifndef lint
1.3 christos 41: #if 0
1.4 christos 42: static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93";
1.3 christos 43: #else
1.9 mycroft 44: __RCSID("$NetBSD: for.c,v 1.8 2000/04/18 03:46:41 simonb Exp $");
1.3 christos 45: #endif
1.1 cgd 46: #endif /* not lint */
1.6 lukem 47: #endif
1.1 cgd 48:
49: /*-
50: * for.c --
51: * Functions to handle loops in a makefile.
52: *
53: * Interface:
54: * For_Eval Evaluate the loop in the passed line.
55: * For_Run Run accumulated loop
56: *
57: */
58:
59: #include <ctype.h>
1.7 christos 60: #include <assert.h>
1.1 cgd 61: #include "make.h"
62: #include "hash.h"
63: #include "dir.h"
64: #include "buf.h"
65:
66: /*
67: * For statements are of the form:
68: *
69: * .for <variable> in <varlist>
70: * ...
71: * .endfor
72: *
73: * The trick is to look for the matching end inside for for loop
74: * To do that, we count the current nesting level of the for loops.
75: * and the .endfor statements, accumulating all the statements between
1.4 christos 76: * the initial .for loop and the matching .endfor;
1.1 cgd 77: * then we evaluate the for loop for each variable in the varlist.
1.7 christos 78: *
79: * Note that any nested fors are just passed through; they get handled
80: * recursively in For_Eval when we're expanding the enclosing for in
81: * For_Run.
1.1 cgd 82: */
83:
84: static int forLevel = 0; /* Nesting level */
85:
86: /*
87: * State of a for loop.
88: */
1.2 jtc 89: typedef struct _For {
1.7 christos 90: Buffer buf; /* Body of loop */
91: char **vars; /* Iteration variables */
92: int nvars; /* # of iteration vars */
93: Lst lst; /* List of items */
1.2 jtc 94: } For;
1.1 cgd 95:
1.7 christos 96: static For accumFor; /* Loop being accumulated */
1.1 cgd 97:
1.7 christos 98: static void ForAddVar __P((const char *, size_t));
1.1 cgd 99:
100:
101:
1.7 christos 102:
103: /*-
104: *-----------------------------------------------------------------------
105: * ForAddVar --
106: * Add an iteration variable to the currently accumulating for.
107: *
108: * Results: none
109: * Side effects: no additional side effects.
110: *-----------------------------------------------------------------------
111: */
112: static void
113: ForAddVar(data, len)
114: const char *data;
115: size_t len;
116: {
117: Buffer buf;
1.8 simonb 118: int varlen;
1.7 christos 119:
120: buf = Buf_Init(0);
121: Buf_AddBytes(buf, len, (Byte *) data);
122:
123: accumFor.nvars++;
124: accumFor.vars = erealloc(accumFor.vars, accumFor.nvars*sizeof(char *));
125:
126: accumFor.vars[accumFor.nvars-1] = (char *) Buf_GetAll(buf, &varlen);
127:
128: Buf_Destroy(buf, FALSE);
129: }
130:
1.1 cgd 131: /*-
132: *-----------------------------------------------------------------------
133: * For_Eval --
134: * Evaluate the for loop in the passed line. The line
135: * looks like this:
136: * .for <variable> in <varlist>
137: *
138: * Results:
139: * TRUE: We found a for loop, or we are inside a for loop
140: * FALSE: We did not find a for loop, or we found the end of the for
141: * for loop.
142: *
143: * Side Effects:
144: * None.
145: *
146: *-----------------------------------------------------------------------
147: */
148: int
149: For_Eval (line)
150: char *line; /* Line to parse */
151: {
1.7 christos 152: char *ptr = line, *sub, *in, *wrd;
1.1 cgd 153: int level; /* Level at which to report errors. */
154:
155: level = PARSE_FATAL;
156:
157:
158: if (forLevel == 0) {
159: Buffer buf;
160: int varlen;
161:
1.2 jtc 162: for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
1.1 cgd 163: continue;
164: /*
165: * If we are not in a for loop quickly determine if the statement is
166: * a for.
167: */
1.2 jtc 168: if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
169: !isspace((unsigned char) ptr[3]))
1.1 cgd 170: return FALSE;
171: ptr += 3;
1.4 christos 172:
1.1 cgd 173: /*
174: * we found a for loop, and now we are going to parse it.
175: */
1.2 jtc 176: while (*ptr && isspace((unsigned char) *ptr))
1.1 cgd 177: ptr++;
1.4 christos 178:
1.1 cgd 179: /*
1.7 christos 180: * Find the "in".
1.1 cgd 181: */
1.7 christos 182: for (in = ptr; *in; in++) {
183: if (isspace((unsigned char) in[0]) && in[1]== 'i' &&
184: in[2] == 'n' &&
185: (in[3] == '\0' || isspace((unsigned char) in[3])))
186: break;
187: }
188: if (*in == '\0') {
189: Parse_Error(level, "missing `in' in for");
1.1 cgd 190: return 0;
191: }
192:
193: /*
1.7 christos 194: * Grab the variables.
1.1 cgd 195: */
1.7 christos 196: accumFor.vars = NULL;
197:
198: while (ptr < in) {
199: wrd = ptr;
200: while (*ptr && !isspace((unsigned char) *ptr))
201: ptr++;
202: ForAddVar(wrd, ptr - wrd);
203: while (*ptr && isspace((unsigned char) *ptr))
204: ptr++;
205: }
206:
207: if (accumFor.nvars == 0) {
208: Parse_Error(level, "no iteration variables in for");
1.1 cgd 209: return 0;
210: }
1.7 christos 211:
212: /* At this point we should be pointing right at the "in" */
213: assert(!memcmp(ptr, "in", 2));
214: ptr += 2;
1.1 cgd 215:
1.2 jtc 216: while (*ptr && isspace((unsigned char) *ptr))
1.1 cgd 217: ptr++;
218:
219: /*
220: * Make a list with the remaining words
221: */
1.7 christos 222: accumFor.lst = Lst_Init(FALSE);
1.1 cgd 223: buf = Buf_Init(0);
1.4 christos 224: sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
1.1 cgd 225:
226: #define ADDWORD() \
227: Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \
228: Buf_AddByte(buf, (Byte) '\0'), \
1.7 christos 229: Lst_AtFront(accumFor.lst, (ClientData) Buf_GetAll(buf, &varlen)), \
1.1 cgd 230: Buf_Destroy(buf, FALSE)
231:
1.2 jtc 232: for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++)
1.1 cgd 233: continue;
234:
235: for (wrd = ptr; *ptr; ptr++)
1.2 jtc 236: if (isspace((unsigned char) *ptr)) {
1.1 cgd 237: ADDWORD();
238: buf = Buf_Init(0);
1.2 jtc 239: while (*ptr && isspace((unsigned char) *ptr))
1.1 cgd 240: ptr++;
241: wrd = ptr--;
242: }
1.7 christos 243: if (DEBUG(FOR)) {
244: int i;
245: for (i = 0; i < accumFor.nvars; i++) {
246: (void) fprintf(stderr, "For: variable %s\n", accumFor.vars[i]);
247: }
248: (void) fprintf(stderr, "For: list %s\n", sub);
249: }
1.4 christos 250: if (ptr - wrd > 0)
1.1 cgd 251: ADDWORD();
252: else
253: Buf_Destroy(buf, TRUE);
254: free((Address) sub);
1.4 christos 255:
1.7 christos 256: accumFor.buf = Buf_Init(0);
1.1 cgd 257: forLevel++;
258: return 1;
259: }
260: else if (*ptr == '.') {
261:
1.2 jtc 262: for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
1.1 cgd 263: continue;
264:
1.2 jtc 265: if (strncmp(ptr, "endfor", 6) == 0 &&
266: (isspace((unsigned char) ptr[6]) || !ptr[6])) {
1.1 cgd 267: if (DEBUG(FOR))
268: (void) fprintf(stderr, "For: end for %d\n", forLevel);
269: if (--forLevel < 0) {
270: Parse_Error (level, "for-less endfor");
271: return 0;
272: }
273: }
1.2 jtc 274: else if (strncmp(ptr, "for", 3) == 0 &&
275: isspace((unsigned char) ptr[3])) {
1.1 cgd 276: forLevel++;
277: if (DEBUG(FOR))
278: (void) fprintf(stderr, "For: new loop %d\n", forLevel);
279: }
280: }
281:
282: if (forLevel != 0) {
1.7 christos 283: Buf_AddBytes(accumFor.buf, strlen(line), (Byte *) line);
284: Buf_AddByte(accumFor.buf, (Byte) '\n');
1.1 cgd 285: return 1;
286: }
287: else {
288: return 0;
289: }
290: }
291:
292:
293: /*-
294: *-----------------------------------------------------------------------
295: * For_Run --
1.7 christos 296: * Run the for loop, imitating the actions of an include file
1.1 cgd 297: *
298: * Results:
299: * None.
300: *
301: * Side Effects:
302: * None.
303: *
304: *-----------------------------------------------------------------------
305: */
306: void
307: For_Run()
308: {
1.2 jtc 309: For arg;
1.7 christos 310: LstNode ln;
311: char **values;
312: int i, done = 0, len;
1.10 ! mycroft 313: char *guy, *orig_guy, *old_guy;
1.7 christos 314:
315: if (accumFor.buf == NULL || accumFor.vars == NULL || accumFor.lst == NULL)
316: return;
317: arg = accumFor;
318: accumFor.buf = NULL;
319: accumFor.vars = NULL;
320: accumFor.nvars = 0;
321: accumFor.lst = NULL;
1.1 cgd 322:
1.7 christos 323: if (Lst_Open(arg.lst) != SUCCESS)
1.1 cgd 324: return;
325:
1.7 christos 326: values = emalloc(arg.nvars * sizeof(char *));
327:
328: while (!done) {
329: /*
330: * due to the dumb way this is set up, this loop must run
331: * backwards.
332: */
333: for (i = arg.nvars - 1; i >= 0; i--) {
334: ln = Lst_Next(arg.lst);
335: if (ln == NILLNODE) {
336: if (i != arg.nvars-1) {
337: Parse_Error(PARSE_FATAL,
338: "Not enough words in for substitution list");
339: }
340: done = 1;
341: break;
342: } else {
343: values[i] = (char *) Lst_Datum(ln);
344: }
345: }
346: if (done)
347: break;
348:
349: for (i = 0; i < arg.nvars; i++) {
1.10 ! mycroft 350: Var_Set(arg.vars[i], values[i], VAR_GLOBAL);
1.7 christos 351: if (DEBUG(FOR))
352: (void) fprintf(stderr, "--- %s = %s\n", arg.vars[i],
353: values[i]);
354: }
355:
356: /*
357: * Hack, hack, kludge.
358: * This is really ugly, but to do it any better way would require
359: * making major changes to var.c, which I don't want to get into
360: * yet. There is no mechanism for expanding some variables, only
361: * for expanding a single variable. That should be corrected, but
362: * not right away. (XXX)
363: */
364:
365: guy = (char *) Buf_GetAll(arg.buf, &len);
1.10 ! mycroft 366: orig_guy = guy;
! 367: for (i = 0; i < arg.nvars; i++) {
! 368: old_guy = guy;
! 369: guy = Var_Subst(arg.vars[i], guy, VAR_GLOBAL, FALSE);
! 370: if (old_guy != orig_guy)
! 371: free(old_guy);
! 372: }
1.7 christos 373: Parse_FromString(guy);
374:
375: for (i = 0; i < arg.nvars; i++)
1.10 ! mycroft 376: Var_Delete(arg.vars[i], VAR_GLOBAL);
1.7 christos 377: }
378:
379: free(values);
380:
381: Lst_Close(arg.lst);
382:
383: for (i=0; i<arg.nvars; i++) {
384: free(arg.vars[i]);
385: }
386: free(arg.vars);
1.1 cgd 387:
1.2 jtc 388: Lst_Destroy(arg.lst, (void (*) __P((ClientData))) free);
1.1 cgd 389: Buf_Destroy(arg.buf, TRUE);
390: }
CVSweb <webmaster@jp.NetBSD.org>