for.c revision 1.38 1 1.38 dsl /* $NetBSD: for.c,v 1.38 2008/12/20 22:41:53 dsl Exp $ */
2 1.3 christos
3 1.1 cgd /*
4 1.1 cgd * Copyright (c) 1992, The Regents of the University of California.
5 1.1 cgd * All rights reserved.
6 1.1 cgd *
7 1.1 cgd * Redistribution and use in source and binary forms, with or without
8 1.1 cgd * modification, are permitted provided that the following conditions
9 1.1 cgd * are met:
10 1.1 cgd * 1. Redistributions of source code must retain the above copyright
11 1.1 cgd * notice, this list of conditions and the following disclaimer.
12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 cgd * notice, this list of conditions and the following disclaimer in the
14 1.1 cgd * documentation and/or other materials provided with the distribution.
15 1.15 agc * 3. Neither the name of the University nor the names of its contributors
16 1.1 cgd * may be used to endorse or promote products derived from this software
17 1.1 cgd * without specific prior written permission.
18 1.1 cgd *
19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 1.1 cgd * SUCH DAMAGE.
30 1.1 cgd */
31 1.1 cgd
32 1.17 ross #ifndef MAKE_NATIVE
33 1.38 dsl static char rcsid[] = "$NetBSD: for.c,v 1.38 2008/12/20 22:41:53 dsl Exp $";
34 1.6 lukem #else
35 1.5 christos #include <sys/cdefs.h>
36 1.1 cgd #ifndef lint
37 1.3 christos #if 0
38 1.4 christos static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93";
39 1.3 christos #else
40 1.38 dsl __RCSID("$NetBSD: for.c,v 1.38 2008/12/20 22:41:53 dsl Exp $");
41 1.3 christos #endif
42 1.1 cgd #endif /* not lint */
43 1.6 lukem #endif
44 1.1 cgd
45 1.1 cgd /*-
46 1.1 cgd * for.c --
47 1.1 cgd * Functions to handle loops in a makefile.
48 1.1 cgd *
49 1.1 cgd * Interface:
50 1.1 cgd * For_Eval Evaluate the loop in the passed line.
51 1.1 cgd * For_Run Run accumulated loop
52 1.1 cgd *
53 1.1 cgd */
54 1.1 cgd
55 1.13 wiz #include <assert.h>
56 1.1 cgd #include <ctype.h>
57 1.13 wiz
58 1.1 cgd #include "make.h"
59 1.1 cgd #include "hash.h"
60 1.1 cgd #include "dir.h"
61 1.1 cgd #include "buf.h"
62 1.38 dsl #include "strlist.h"
63 1.1 cgd
64 1.1 cgd /*
65 1.1 cgd * For statements are of the form:
66 1.1 cgd *
67 1.1 cgd * .for <variable> in <varlist>
68 1.1 cgd * ...
69 1.1 cgd * .endfor
70 1.1 cgd *
71 1.1 cgd * The trick is to look for the matching end inside for for loop
72 1.1 cgd * To do that, we count the current nesting level of the for loops.
73 1.1 cgd * and the .endfor statements, accumulating all the statements between
74 1.4 christos * the initial .for loop and the matching .endfor;
75 1.1 cgd * then we evaluate the for loop for each variable in the varlist.
76 1.7 christos *
77 1.7 christos * Note that any nested fors are just passed through; they get handled
78 1.7 christos * recursively in For_Eval when we're expanding the enclosing for in
79 1.7 christos * For_Run.
80 1.1 cgd */
81 1.1 cgd
82 1.1 cgd static int forLevel = 0; /* Nesting level */
83 1.1 cgd
84 1.1 cgd /*
85 1.1 cgd * State of a for loop.
86 1.1 cgd */
87 1.2 jtc typedef struct _For {
88 1.7 christos Buffer buf; /* Body of loop */
89 1.38 dsl strlist_t vars; /* Iteration variables */
90 1.38 dsl strlist_t items; /* Substitution items */
91 1.2 jtc } For;
92 1.1 cgd
93 1.7 christos static For accumFor; /* Loop being accumulated */
94 1.1 cgd
95 1.1 cgd
96 1.7 christos
98 1.33 dsl static char *
99 1.33 dsl make_str(const char *ptr, int len)
100 1.33 dsl {
101 1.33 dsl char *new_ptr;
102 1.33 dsl
103 1.33 dsl new_ptr = bmake_malloc(len + 1);
104 1.33 dsl memcpy(new_ptr, ptr, len);
105 1.33 dsl new_ptr[len] = 0;
106 1.33 dsl return new_ptr;
107 1.33 dsl }
108 1.7 christos
109 1.7 christos /*-
110 1.1 cgd *-----------------------------------------------------------------------
111 1.1 cgd * For_Eval --
112 1.1 cgd * Evaluate the for loop in the passed line. The line
113 1.1 cgd * looks like this:
114 1.1 cgd * .for <variable> in <varlist>
115 1.13 wiz *
116 1.13 wiz * Input:
117 1.13 wiz * line Line to parse
118 1.1 cgd *
119 1.35 dsl * Results:
120 1.35 dsl * 0: Not a .for statement, parse the line
121 1.35 dsl * 1: We found a for loop
122 1.1 cgd * -1: A .for statement with a bad syntax error, discard.
123 1.1 cgd *
124 1.1 cgd * Side Effects:
125 1.1 cgd * None.
126 1.1 cgd *
127 1.1 cgd *-----------------------------------------------------------------------
128 1.1 cgd */
129 1.13 wiz int
130 1.1 cgd For_Eval(char *line)
131 1.33 dsl {
132 1.33 dsl char *ptr = line, *sub;
133 1.33 dsl int len;
134 1.33 dsl
135 1.33 dsl /* Forget anything we previously knew about - it cannot be useful */
136 1.32 dsl memset(&accumFor, 0, sizeof accumFor);
137 1.32 dsl
138 1.32 dsl forLevel = 0;
139 1.32 dsl for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
140 1.32 dsl continue;
141 1.32 dsl /*
142 1.32 dsl * If we are not in a for loop quickly determine if the statement is
143 1.32 dsl * a for.
144 1.32 dsl */
145 1.32 dsl if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
146 1.32 dsl !isspace((unsigned char) ptr[3])) {
147 1.32 dsl if (ptr[0] == 'e' && strncmp(ptr+1, "ndfor", 5) == 0) {
148 1.32 dsl Parse_Error(PARSE_FATAL, "for-less endfor");
149 1.32 dsl return -1;
150 1.32 dsl }
151 1.32 dsl return 0;
152 1.32 dsl }
153 1.1 cgd ptr += 3;
154 1.32 dsl
155 1.32 dsl /*
156 1.32 dsl * we found a for loop, and now we are going to parse it.
157 1.1 cgd */
158 1.33 dsl
159 1.33 dsl /* Grab the variables. Terminate on "in". */
160 1.32 dsl for (;; ptr += len) {
161 1.32 dsl while (*ptr && isspace((unsigned char) *ptr))
162 1.33 dsl ptr++;
163 1.33 dsl if (*ptr == '\0') {
164 1.33 dsl Parse_Error(PARSE_FATAL, "missing `in' in for");
165 1.33 dsl return -1;
166 1.33 dsl }
167 1.33 dsl for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
168 1.33 dsl continue;
169 1.33 dsl if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') {
170 1.33 dsl ptr += 2;
171 1.33 dsl break;
172 1.38 dsl }
173 1.32 dsl strlist_add_str(&accumFor.vars, make_str(ptr, len));
174 1.1 cgd }
175 1.38 dsl
176 1.32 dsl if (strlist_num(&accumFor.vars) == 0) {
177 1.32 dsl Parse_Error(PARSE_FATAL, "no iteration variables in for");
178 1.32 dsl return -1;
179 1.4 christos }
180 1.32 dsl
181 1.32 dsl while (*ptr && isspace((unsigned char) *ptr))
182 1.32 dsl ptr++;
183 1.32 dsl
184 1.32 dsl /*
185 1.32 dsl * Make a list with the remaining words
186 1.32 dsl */
187 1.4 christos sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
188 1.38 dsl
189 1.33 dsl for (ptr = sub;; ptr += len) {
190 1.33 dsl while (*ptr && isspace((unsigned char)*ptr))
191 1.33 dsl ptr++;
192 1.33 dsl if (*ptr == 0)
193 1.33 dsl break;
194 1.33 dsl for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
195 1.38 dsl continue;
196 1.33 dsl strlist_add_str(&accumFor.items, make_str(ptr, len));
197 1.1 cgd }
198 1.33 dsl
199 1.7 christos free(sub);
200 1.38 dsl
201 1.33 dsl if (strlist_num(&accumFor.items) % strlist_num(&accumFor.vars)) {
202 1.33 dsl Parse_Error(PARSE_FATAL,
203 1.38 dsl "Wrong number of words in .for substitution list %d %d",
204 1.33 dsl strlist_num(&accumFor.items), strlist_num(&accumFor.vars));
205 1.33 dsl /*
206 1.33 dsl * Return 'success' so that the body of the .for loop is accumulated.
207 1.33 dsl * The loop will have zero iterations expanded due a later test.
208 1.32 dsl */
209 1.32 dsl }
210 1.32 dsl
211 1.32 dsl accumFor.buf = Buf_Init(0);
212 1.32 dsl forLevel = 1;
213 1.32 dsl return 1;
214 1.7 christos }
215 1.35 dsl
216 1.35 dsl /*
217 1.35 dsl * Add another line to a .for loop.
218 1.35 dsl * Returns 0 when the matching .enfor is reached.
219 1.35 dsl */
220 1.32 dsl
221 1.32 dsl int
222 1.32 dsl For_Accum(char *line)
223 1.32 dsl {
224 1.26 dsl char *ptr = line;
225 1.26 dsl
226 1.1 cgd if (*ptr == '.') {
227 1.2 jtc
228 1.1 cgd for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
229 1.1 cgd continue;
230 1.2 jtc
231 1.26 dsl if (strncmp(ptr, "endfor", 6) == 0 &&
232 1.1 cgd (isspace((unsigned char) ptr[6]) || !ptr[6])) {
233 1.23 dsl if (DEBUG(FOR))
234 1.32 dsl (void)fprintf(debug_file, "For: end for %d\n", forLevel);
235 1.1 cgd if (--forLevel <= 0)
236 1.26 dsl return 0;
237 1.2 jtc } else if (strncmp(ptr, "for", 3) == 0 &&
238 1.1 cgd isspace((unsigned char) ptr[3])) {
239 1.1 cgd forLevel++;
240 1.23 dsl if (DEBUG(FOR))
241 1.1 cgd (void)fprintf(debug_file, "For: new loop %d\n", forLevel);
242 1.1 cgd }
243 1.1 cgd }
244 1.32 dsl
245 1.32 dsl Buf_AddBytes(accumFor.buf, strlen(line), (Byte *)line);
246 1.32 dsl Buf_AddByte(accumFor.buf, (Byte)'\n');
247 1.1 cgd return 1;
248 1.1 cgd }
249 1.1 cgd
250 1.1 cgd
251 1.1 cgd /*-
253 1.7 christos *-----------------------------------------------------------------------
254 1.1 cgd * For_Run --
255 1.1 cgd * Run the for loop, imitating the actions of an include file
256 1.1 cgd *
257 1.1 cgd * Results:
258 1.1 cgd * None.
259 1.1 cgd *
260 1.1 cgd * Side Effects:
261 1.1 cgd * None.
262 1.1 cgd *
263 1.1 cgd *-----------------------------------------------------------------------
264 1.16 enami */
265 1.1 cgd void
266 1.2 jtc For_Run(int lineno)
267 1.38 dsl {
268 1.38 dsl For arg;
269 1.10 mycroft int i, len;
270 1.38 dsl unsigned int item_no;
271 1.7 christos char *guy, *orig_guy, *old_guy;
272 1.7 christos char *var, *item;
273 1.38 dsl
274 1.1 cgd arg = accumFor;
275 1.38 dsl memset(&accumFor, 0, sizeof accumFor);
276 1.38 dsl
277 1.33 dsl item_no = strlist_num(&arg.items);
278 1.38 dsl if (item_no % strlist_num(&arg.vars))
279 1.7 christos /* Error message already printed */
280 1.38 dsl goto out;
281 1.7 christos
282 1.7 christos while (item_no != 0) {
283 1.7 christos /*
284 1.7 christos * Hack, hack, kludge.
285 1.7 christos * This is really ugly, but to do it any better way would require
286 1.7 christos * making major changes to var.c, which I don't want to get into
287 1.7 christos * yet. There is no mechanism for expanding some variables, only
288 1.7 christos * for expanding a single variable. That should be corrected, but
289 1.19 christos * not right away. (XXX)
290 1.10 mycroft */
291 1.38 dsl guy = (char *)Buf_GetAll(arg.buf, &len);
292 1.38 dsl orig_guy = guy;
293 1.38 dsl item_no -= strlist_num(&arg.vars);
294 1.38 dsl STRLIST_FOREACH(var, &arg.vars, i) {
295 1.38 dsl item = strlist_str(&arg.items, item_no + i);
296 1.38 dsl if (DEBUG(FOR))
297 1.10 mycroft (void)fprintf(debug_file, "--- %s = %s\n", var, item);
298 1.38 dsl Var_Set(var, item, VAR_GLOBAL, 0);
299 1.10 mycroft old_guy = guy;
300 1.10 mycroft guy = Var_Subst(var, guy, VAR_GLOBAL, FALSE);
301 1.10 mycroft if (old_guy != orig_guy)
302 1.25 dsl free(old_guy);
303 1.7 christos }
304 1.7 christos Parse_SetInput(NULL, lineno, -1, guy);
305 1.38 dsl }
306 1.38 dsl
307 1.7 christos STRLIST_FOREACH(var, &arg.vars, i)
308 1.38 dsl Var_Delete(var, VAR_GLOBAL);
309 1.38 dsl
310 1.38 dsl out:
311 1.1 cgd strlist_clean(&arg.vars);
312 1.1 cgd strlist_clean(&arg.items);
313 1.1 cgd
314 Buf_Destroy(arg.buf, TRUE);
315 }
316