xref: /netbsd-src/usr.bin/make/for.c (revision fdecd6a253f999ae92b139670d9e15cc9df4497c)
1 /*	$NetBSD: for.c,v 1.5 1997/07/01 21:17:19 christos 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 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)for.c	8.1 (Berkeley) 6/6/93";
40 #else
41 __RCSID("$NetBSD: for.c,v 1.5 1997/07/01 21:17:19 christos Exp $");
42 #endif
43 #endif /* not lint */
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    <ctype.h>
56 #include    "make.h"
57 #include    "hash.h"
58 #include    "dir.h"
59 #include    "buf.h"
60 
61 /*
62  * For statements are of the form:
63  *
64  * .for <variable> in <varlist>
65  * ...
66  * .endfor
67  *
68  * The trick is to look for the matching end inside for for loop
69  * To do that, we count the current nesting level of the for loops.
70  * and the .endfor statements, accumulating all the statements between
71  * the initial .for loop and the matching .endfor;
72  * then we evaluate the for loop for each variable in the varlist.
73  */
74 
75 static int  	  forLevel = 0;  	/* Nesting level	*/
76 static char	 *forVar;		/* Iteration variable	*/
77 static Buffer	  forBuf;		/* Commands in loop	*/
78 static Lst	  forLst;		/* List of items	*/
79 
80 /*
81  * State of a for loop.
82  */
83 typedef struct _For {
84     Buffer	  buf;			/* Unexpanded buffer	*/
85     char*	  var;			/* Index name		*/
86     Lst  	  lst;			/* List of variables	*/
87 } For;
88 
89 static int ForExec	__P((ClientData, ClientData));
90 
91 
92 
93 
94 /*-
95  *-----------------------------------------------------------------------
96  * For_Eval --
97  *	Evaluate the for loop in the passed line. The line
98  *	looks like this:
99  *	    .for <variable> in <varlist>
100  *
101  * Results:
102  *	TRUE: We found a for loop, or we are inside a for loop
103  *	FALSE: We did not find a for loop, or we found the end of the for
104  *	       for loop.
105  *
106  * Side Effects:
107  *	None.
108  *
109  *-----------------------------------------------------------------------
110  */
111 int
112 For_Eval (line)
113     char    	    *line;    /* Line to parse */
114 {
115     char	    *ptr = line, *sub, *wrd;
116     int	    	    level;  	/* Level at which to report errors. */
117 
118     level = PARSE_FATAL;
119 
120 
121     if (forLevel == 0) {
122 	Buffer	    buf;
123 	int	    varlen;
124 
125 	for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
126 	    continue;
127 	/*
128 	 * If we are not in a for loop quickly determine if the statement is
129 	 * a for.
130 	 */
131 	if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
132 	    !isspace((unsigned char) ptr[3]))
133 	    return FALSE;
134 	ptr += 3;
135 
136 	/*
137 	 * we found a for loop, and now we are going to parse it.
138 	 */
139 	while (*ptr && isspace((unsigned char) *ptr))
140 	    ptr++;
141 
142 	/*
143 	 * Grab the variable
144 	 */
145 	buf = Buf_Init(0);
146 	for (wrd = ptr; *ptr && !isspace((unsigned char) *ptr); ptr++)
147 	    continue;
148 	Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd);
149 
150 	forVar = (char *) Buf_GetAll(buf, &varlen);
151 	if (varlen == 0) {
152 	    Parse_Error (level, "missing variable in for");
153 	    return 0;
154 	}
155 	Buf_Destroy(buf, FALSE);
156 
157 	while (*ptr && isspace((unsigned char) *ptr))
158 	    ptr++;
159 
160 	/*
161 	 * Grab the `in'
162 	 */
163 	if (ptr[0] != 'i' || ptr[1] != 'n' ||
164 	    !isspace((unsigned char) ptr[2])) {
165 	    Parse_Error (level, "missing `in' in for");
166 	    printf("%s\n", ptr);
167 	    return 0;
168 	}
169 	ptr += 3;
170 
171 	while (*ptr && isspace((unsigned char) *ptr))
172 	    ptr++;
173 
174 	/*
175 	 * Make a list with the remaining words
176 	 */
177 	forLst = Lst_Init(FALSE);
178 	buf = Buf_Init(0);
179 	sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
180 
181 #define ADDWORD() \
182 	Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \
183 	Buf_AddByte(buf, (Byte) '\0'), \
184 	Lst_AtFront(forLst, (ClientData) Buf_GetAll(buf, &varlen)), \
185 	Buf_Destroy(buf, FALSE)
186 
187 	for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++)
188 	    continue;
189 
190 	for (wrd = ptr; *ptr; ptr++)
191 	    if (isspace((unsigned char) *ptr)) {
192 		ADDWORD();
193 		buf = Buf_Init(0);
194 		while (*ptr && isspace((unsigned char) *ptr))
195 		    ptr++;
196 		wrd = ptr--;
197 	    }
198 	if (DEBUG(FOR))
199 	    (void) fprintf(stderr, "For: Iterator %s List %s\n", forVar, sub);
200 	if (ptr - wrd > 0)
201 	    ADDWORD();
202 	else
203 	    Buf_Destroy(buf, TRUE);
204 	free((Address) sub);
205 
206 	forBuf = Buf_Init(0);
207 	forLevel++;
208 	return 1;
209     }
210     else if (*ptr == '.') {
211 
212 	for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
213 	    continue;
214 
215 	if (strncmp(ptr, "endfor", 6) == 0 &&
216 	    (isspace((unsigned char) ptr[6]) || !ptr[6])) {
217 	    if (DEBUG(FOR))
218 		(void) fprintf(stderr, "For: end for %d\n", forLevel);
219 	    if (--forLevel < 0) {
220 		Parse_Error (level, "for-less endfor");
221 		return 0;
222 	    }
223 	}
224 	else if (strncmp(ptr, "for", 3) == 0 &&
225 		 isspace((unsigned char) ptr[3])) {
226 	    forLevel++;
227 	    if (DEBUG(FOR))
228 		(void) fprintf(stderr, "For: new loop %d\n", forLevel);
229 	}
230     }
231 
232     if (forLevel != 0) {
233 	Buf_AddBytes(forBuf, strlen(line), (Byte *) line);
234 	Buf_AddByte(forBuf, (Byte) '\n');
235 	return 1;
236     }
237     else {
238 	return 0;
239     }
240 }
241 
242 /*-
243  *-----------------------------------------------------------------------
244  * ForExec --
245  *	Expand the for loop for this index and push it in the Makefile
246  *
247  * Results:
248  *	None.
249  *
250  * Side Effects:
251  *	None.
252  *
253  *-----------------------------------------------------------------------
254  */
255 static int
256 ForExec(namep, argp)
257     ClientData namep;
258     ClientData argp;
259 {
260     char *name = (char *) namep;
261     For *arg = (For *) argp;
262     int len;
263     Var_Set(arg->var, name, VAR_GLOBAL);
264     if (DEBUG(FOR))
265 	(void) fprintf(stderr, "--- %s = %s\n", arg->var, name);
266     Parse_FromString(Var_Subst(arg->var, (char *) Buf_GetAll(arg->buf, &len),
267 			       VAR_GLOBAL, FALSE));
268     Var_Delete(arg->var, VAR_GLOBAL);
269 
270     return 0;
271 }
272 
273 
274 /*-
275  *-----------------------------------------------------------------------
276  * For_Run --
277  *	Run the for loop, immitating the actions of an include file
278  *
279  * Results:
280  *	None.
281  *
282  * Side Effects:
283  *	None.
284  *
285  *-----------------------------------------------------------------------
286  */
287 void
288 For_Run()
289 {
290     For arg;
291 
292     if (forVar == NULL || forBuf == NULL || forLst == NULL)
293 	return;
294     arg.var = forVar;
295     arg.buf = forBuf;
296     arg.lst = forLst;
297     forVar = NULL;
298     forBuf = NULL;
299     forLst = NULL;
300 
301     Lst_ForEach(arg.lst, ForExec, (ClientData) &arg);
302 
303     free((Address)arg.var);
304     Lst_Destroy(arg.lst, (void (*) __P((ClientData))) free);
305     Buf_Destroy(arg.buf, TRUE);
306 }
307