for.c revision 1.10 1 /* $NetBSD: for.c,v 1.10 2000/06/06 04:56:52 mycroft Exp $ */
2
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
36 #ifdef MAKE_BOOTSTRAP
37 static char rcsid[] = "$NetBSD: for.c,v 1.10 2000/06/06 04:56:52 mycroft Exp $";
38 #else
39 #include <sys/cdefs.h>
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93";
43 #else
44 __RCSID("$NetBSD: for.c,v 1.10 2000/06/06 04:56:52 mycroft Exp $");
45 #endif
46 #endif /* not lint */
47 #endif
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>
60 #include <assert.h>
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
76 * the initial .for loop and the matching .endfor;
77 * then we evaluate the for loop for each variable in the varlist.
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.
82 */
83
84 static int forLevel = 0; /* Nesting level */
85
86 /*
87 * State of a for loop.
88 */
89 typedef struct _For {
90 Buffer buf; /* Body of loop */
91 char **vars; /* Iteration variables */
92 int nvars; /* # of iteration vars */
93 Lst lst; /* List of items */
94 } For;
95
96 static For accumFor; /* Loop being accumulated */
97
98 static void ForAddVar __P((const char *, size_t));
99
100
101
102
104 /*-
105 *-----------------------------------------------------------------------
106 * ForAddVar --
107 * Add an iteration variable to the currently accumulating for.
108 *
109 * Results: none
110 * Side effects: no additional side effects.
111 *-----------------------------------------------------------------------
112 */
113 static void
114 ForAddVar(data, len)
115 const char *data;
116 size_t len;
117 {
118 Buffer buf;
119 int varlen;
120
121 buf = Buf_Init(0);
122 Buf_AddBytes(buf, len, (Byte *) data);
123
124 accumFor.nvars++;
125 accumFor.vars = erealloc(accumFor.vars, accumFor.nvars*sizeof(char *));
126
127 accumFor.vars[accumFor.nvars-1] = (char *) Buf_GetAll(buf, &varlen);
128
129 Buf_Destroy(buf, FALSE);
130 }
131
132 /*-
133 *-----------------------------------------------------------------------
134 * For_Eval --
135 * Evaluate the for loop in the passed line. The line
136 * looks like this:
137 * .for <variable> in <varlist>
138 *
139 * Results:
140 * TRUE: We found a for loop, or we are inside a for loop
141 * FALSE: We did not find a for loop, or we found the end of the for
142 * for loop.
143 *
144 * Side Effects:
145 * None.
146 *
147 *-----------------------------------------------------------------------
148 */
149 int
150 For_Eval (line)
151 char *line; /* Line to parse */
152 {
153 char *ptr = line, *sub, *in, *wrd;
154 int level; /* Level at which to report errors. */
155
156 level = PARSE_FATAL;
157
158
159 if (forLevel == 0) {
160 Buffer buf;
161 int varlen;
162
163 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
164 continue;
165 /*
166 * If we are not in a for loop quickly determine if the statement is
167 * a for.
168 */
169 if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
170 !isspace((unsigned char) ptr[3]))
171 return FALSE;
172 ptr += 3;
173
174 /*
175 * we found a for loop, and now we are going to parse it.
176 */
177 while (*ptr && isspace((unsigned char) *ptr))
178 ptr++;
179
180 /*
181 * Find the "in".
182 */
183 for (in = ptr; *in; in++) {
184 if (isspace((unsigned char) in[0]) && in[1]== 'i' &&
185 in[2] == 'n' &&
186 (in[3] == '\0' || isspace((unsigned char) in[3])))
187 break;
188 }
189 if (*in == '\0') {
190 Parse_Error(level, "missing `in' in for");
191 return 0;
192 }
193
194 /*
195 * Grab the variables.
196 */
197 accumFor.vars = NULL;
198
199 while (ptr < in) {
200 wrd = ptr;
201 while (*ptr && !isspace((unsigned char) *ptr))
202 ptr++;
203 ForAddVar(wrd, ptr - wrd);
204 while (*ptr && isspace((unsigned char) *ptr))
205 ptr++;
206 }
207
208 if (accumFor.nvars == 0) {
209 Parse_Error(level, "no iteration variables in for");
210 return 0;
211 }
212
213 /* At this point we should be pointing right at the "in" */
214 assert(!memcmp(ptr, "in", 2));
215 ptr += 2;
216
217 while (*ptr && isspace((unsigned char) *ptr))
218 ptr++;
219
220 /*
221 * Make a list with the remaining words
222 */
223 accumFor.lst = Lst_Init(FALSE);
224 buf = Buf_Init(0);
225 sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
226
227 #define ADDWORD() \
228 Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \
229 Buf_AddByte(buf, (Byte) '\0'), \
230 Lst_AtFront(accumFor.lst, (ClientData) Buf_GetAll(buf, &varlen)), \
231 Buf_Destroy(buf, FALSE)
232
233 for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++)
234 continue;
235
236 for (wrd = ptr; *ptr; ptr++)
237 if (isspace((unsigned char) *ptr)) {
238 ADDWORD();
239 buf = Buf_Init(0);
240 while (*ptr && isspace((unsigned char) *ptr))
241 ptr++;
242 wrd = ptr--;
243 }
244 if (DEBUG(FOR)) {
245 int i;
246 for (i = 0; i < accumFor.nvars; i++) {
247 (void) fprintf(stderr, "For: variable %s\n", accumFor.vars[i]);
248 }
249 (void) fprintf(stderr, "For: list %s\n", sub);
250 }
251 if (ptr - wrd > 0)
252 ADDWORD();
253 else
254 Buf_Destroy(buf, TRUE);
255 free((Address) sub);
256
257 accumFor.buf = Buf_Init(0);
258 forLevel++;
259 return 1;
260 }
261 else if (*ptr == '.') {
262
263 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
264 continue;
265
266 if (strncmp(ptr, "endfor", 6) == 0 &&
267 (isspace((unsigned char) ptr[6]) || !ptr[6])) {
268 if (DEBUG(FOR))
269 (void) fprintf(stderr, "For: end for %d\n", forLevel);
270 if (--forLevel < 0) {
271 Parse_Error (level, "for-less endfor");
272 return 0;
273 }
274 }
275 else if (strncmp(ptr, "for", 3) == 0 &&
276 isspace((unsigned char) ptr[3])) {
277 forLevel++;
278 if (DEBUG(FOR))
279 (void) fprintf(stderr, "For: new loop %d\n", forLevel);
280 }
281 }
282
283 if (forLevel != 0) {
284 Buf_AddBytes(accumFor.buf, strlen(line), (Byte *) line);
285 Buf_AddByte(accumFor.buf, (Byte) '\n');
286 return 1;
287 }
288 else {
289 return 0;
290 }
291 }
292
293
294 /*-
296 *-----------------------------------------------------------------------
297 * For_Run --
298 * Run the for loop, imitating the actions of an include file
299 *
300 * Results:
301 * None.
302 *
303 * Side Effects:
304 * None.
305 *
306 *-----------------------------------------------------------------------
307 */
308 void
309 For_Run()
310 {
311 For arg;
312 LstNode ln;
313 char **values;
314 int i, done = 0, len;
315 char *guy, *orig_guy, *old_guy;
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;
324
325 if (Lst_Open(arg.lst) != SUCCESS)
326 return;
327
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 {
345 values[i] = (char *) Lst_Datum(ln);
346 }
347 }
348 if (done)
349 break;
350
351 for (i = 0; i < arg.nvars; i++) {
352 Var_Set(arg.vars[i], values[i], VAR_GLOBAL);
353 if (DEBUG(FOR))
354 (void) fprintf(stderr, "--- %s = %s\n", arg.vars[i],
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
367 guy = (char *) Buf_GetAll(arg.buf, &len);
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 }
375 Parse_FromString(guy);
376
377 for (i = 0; i < arg.nvars; i++)
378 Var_Delete(arg.vars[i], VAR_GLOBAL);
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);
389
390 Lst_Destroy(arg.lst, (void (*) __P((ClientData))) free);
391 Buf_Destroy(arg.buf, TRUE);
392 }
393