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