for.c revision 1.35 1 /* $NetBSD: for.c,v 1.35 2008/12/01 21:05:21 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.35 2008/12/01 21:05:21 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.35 2008/12/01 21:05:21 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 int nitem; /* # of substitution items */
91 Lst lst; /* List of items */
92 } For;
93
94 static For accumFor; /* Loop being accumulated */
95
96 static void ForAddVar(const char *, size_t);
97
98
99
100
102 static char *
103 make_str(const char *ptr, int len)
104 {
105 char *new_ptr;
106
107 new_ptr = bmake_malloc(len + 1);
108 memcpy(new_ptr, ptr, len);
109 new_ptr[len] = 0;
110 return new_ptr;
111 }
112
113 /*-
114 *-----------------------------------------------------------------------
115 * ForAddVar --
116 * Add an iteration variable to the currently accumulating for.
117 *
118 * Results: none
119 * Side effects: no additional side effects.
120 *-----------------------------------------------------------------------
121 */
122 static void
123 ForAddVar(const char *data, size_t len)
124 {
125 int nvars;
126
127 nvars = accumFor.nvars;
128 accumFor.nvars = nvars + 1;
129 accumFor.vars = bmake_realloc(accumFor.vars,
130 accumFor.nvars * sizeof(*accumFor.vars));
131 accumFor.vars[nvars] = make_str(data, len);
132 }
133
134 /*-
135 *-----------------------------------------------------------------------
136 * For_Eval --
137 * Evaluate the for loop in the passed line. The line
138 * looks like this:
139 * .for <variable> in <varlist>
140 *
141 * Input:
142 * line Line to parse
143 *
144 * Results:
145 * 0: Not a .for statement, parse the line
146 * 1: We found a for loop
147 * -1: A .for statement with a bad syntax error, discard.
148 *
149 * Side Effects:
150 * None.
151 *
152 *-----------------------------------------------------------------------
153 */
154 int
155 For_Eval(char *line)
156 {
157 char *ptr = line, *sub;
158 int len;
159
160 /* Forget anything we previously knew about - it cannot be useful */
161 memset(&accumFor, 0, sizeof accumFor);
162
163 forLevel = 0;
164 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
165 continue;
166 /*
167 * If we are not in a for loop quickly determine if the statement is
168 * a for.
169 */
170 if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
171 !isspace((unsigned char) ptr[3])) {
172 if (ptr[0] == 'e' && strncmp(ptr+1, "ndfor", 5) == 0) {
173 Parse_Error(PARSE_FATAL, "for-less endfor");
174 return -1;
175 }
176 return 0;
177 }
178 ptr += 3;
179
180 /*
181 * we found a for loop, and now we are going to parse it.
182 */
183
184 /* Grab the variables. Terminate on "in". */
185 for (;; ptr += len) {
186 while (*ptr && isspace((unsigned char) *ptr))
187 ptr++;
188 if (*ptr == '\0') {
189 Parse_Error(PARSE_FATAL, "missing `in' in for");
190 return -1;
191 }
192 for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
193 continue;
194 if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') {
195 ptr += 2;
196 break;
197 }
198 ForAddVar(ptr, len);
199 }
200
201 if (accumFor.nvars == 0) {
202 Parse_Error(PARSE_FATAL, "no iteration variables in for");
203 return -1;
204 }
205
206 while (*ptr && isspace((unsigned char) *ptr))
207 ptr++;
208
209 /*
210 * Make a list with the remaining words
211 */
212 accumFor.lst = Lst_Init(FALSE);
213 sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
214
215 for (ptr = sub;; ptr += len, accumFor.nitem++) {
216 while (*ptr && isspace((unsigned char)*ptr))
217 ptr++;
218 if (*ptr == 0)
219 break;
220 for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
221 continue;
222 Lst_AtFront(accumFor.lst, make_str(ptr, len));
223 }
224
225 free(sub);
226
227 if (accumFor.nitem % accumFor.nvars) {
228 Parse_Error(PARSE_FATAL,
229 "Wrong number of words in .for substitution list %d %d",
230 accumFor.nitem, accumFor.nvars);
231 /*
232 * Return 'success' so that the body of the .for loop is accumulated.
233 * The loop will have zero iterations expanded due a later test.
234 */
235 }
236
237 accumFor.buf = Buf_Init(0);
238 forLevel = 1;
239 return 1;
240 }
241
242 /*
243 * Add another line to a .for loop.
244 * Returns 0 when the matching .enfor is reached.
245 */
246
247 int
248 For_Accum(char *line)
249 {
250 char *ptr = line;
251
252 if (*ptr == '.') {
253
254 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
255 continue;
256
257 if (strncmp(ptr, "endfor", 6) == 0 &&
258 (isspace((unsigned char) ptr[6]) || !ptr[6])) {
259 if (DEBUG(FOR))
260 (void)fprintf(debug_file, "For: end for %d\n", forLevel);
261 if (--forLevel <= 0)
262 return 0;
263 } else if (strncmp(ptr, "for", 3) == 0 &&
264 isspace((unsigned char) ptr[3])) {
265 forLevel++;
266 if (DEBUG(FOR))
267 (void)fprintf(debug_file, "For: new loop %d\n", forLevel);
268 }
269 }
270
271 Buf_AddBytes(accumFor.buf, strlen(line), (Byte *)line);
272 Buf_AddByte(accumFor.buf, (Byte)'\n');
273 return 1;
274 }
275
276
277 /*-
279 *-----------------------------------------------------------------------
280 * For_Run --
281 * Run the for loop, imitating the actions of an include file
282 *
283 * Results:
284 * None.
285 *
286 * Side Effects:
287 * None.
288 *
289 *-----------------------------------------------------------------------
290 */
291 void
292 For_Run(int lineno)
293 {
294 For arg;
295 LstNode ln;
296 char **values;
297 int i, done = 0, len;
298 char *guy, *orig_guy, *old_guy;
299
300 arg = accumFor;
301 accumFor.buf = NULL;
302 accumFor.vars = NULL;
303 accumFor.nvars = 0;
304 accumFor.lst = NULL;
305
306 if (arg.nitem % arg.nvars)
307 /* Error message already printed */
308 return;
309
310 if (Lst_Open(arg.lst) != SUCCESS)
311 return;
312
313 values = bmake_malloc(arg.nvars * sizeof(char *));
314
315 while (!done) {
316 /*
317 * due to the dumb way this is set up, this loop must run
318 * backwards.
319 */
320 for (i = arg.nvars - 1; i >= 0; i--) {
321 ln = Lst_Next(arg.lst);
322 if (ln == NILLNODE) {
323 done = 1;
324 break;
325 } else {
326 values[i] = (char *)Lst_Datum(ln);
327 }
328 }
329 if (done)
330 break;
331
332 for (i = 0; i < arg.nvars; i++) {
333 Var_Set(arg.vars[i], values[i], VAR_GLOBAL, 0);
334 if (DEBUG(FOR))
335 (void)fprintf(debug_file, "--- %s = %s\n", arg.vars[i],
336 values[i]);
337 }
338
339 /*
340 * Hack, hack, kludge.
341 * This is really ugly, but to do it any better way would require
342 * making major changes to var.c, which I don't want to get into
343 * yet. There is no mechanism for expanding some variables, only
344 * for expanding a single variable. That should be corrected, but
345 * not right away. (XXX)
346 */
347
348 guy = (char *)Buf_GetAll(arg.buf, &len);
349 orig_guy = guy;
350 for (i = 0; i < arg.nvars; i++) {
351 old_guy = guy;
352 guy = Var_Subst(arg.vars[i], guy, VAR_GLOBAL, FALSE);
353 if (old_guy != orig_guy)
354 free(old_guy);
355 }
356 Parse_SetInput(NULL, lineno, -1, guy);
357
358 for (i = 0; i < arg.nvars; i++)
359 Var_Delete(arg.vars[i], VAR_GLOBAL);
360 }
361
362 free(values);
363
364 Lst_Close(arg.lst);
365
366 for (i=0; i<arg.nvars; i++) {
367 free(arg.vars[i]);
368 }
369 free(arg.vars);
370
371 Lst_Destroy(arg.lst, (FreeProc *)free);
372 Buf_Destroy(arg.buf, TRUE);
373 }
374