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