for.c revision 1590
1/*
2 * Copyright (c) 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Christos Zoulas.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38static char sccsid[] = "@(#)for.c	8.1 (Berkeley) 6/6/93";
39#endif /* not lint */
40
41/*-
42 * for.c --
43 *	Functions to handle loops in a makefile.
44 *
45 * Interface:
46 *	For_Eval 	Evaluate the loop in the passed line.
47 *	For_Run		Run accumulated loop
48 *
49 */
50
51#include    <ctype.h>
52#include    "make.h"
53#include    "hash.h"
54#include    "dir.h"
55#include    "buf.h"
56
57/*
58 * For statements are of the form:
59 *
60 * .for <variable> in <varlist>
61 * ...
62 * .endfor
63 *
64 * The trick is to look for the matching end inside for for loop
65 * To do that, we count the current nesting level of the for loops.
66 * and the .endfor statements, accumulating all the statements between
67 * the initial .for loop and the matching .endfor;
68 * then we evaluate the for loop for each variable in the varlist.
69 */
70
71static int  	  forLevel = 0;  	/* Nesting level	*/
72static char	 *forVar;		/* Iteration variable	*/
73static Buffer	  forBuf;		/* Commands in loop	*/
74static Lst	  forLst;		/* List of items	*/
75
76/*
77 * State of a for loop.
78 */
79struct For {
80    Buffer	  buf;			/* Unexpanded buffer	*/
81    char*	  var;			/* Index name		*/
82    Lst  	  lst;			/* List of variables	*/
83};
84
85static int ForExec	__P((char *, struct For *));
86
87
88
89
90/*-
91 *-----------------------------------------------------------------------
92 * For_Eval --
93 *	Evaluate the for loop in the passed line. The line
94 *	looks like this:
95 *	    .for <variable> in <varlist>
96 *
97 * Results:
98 *	TRUE: We found a for loop, or we are inside a for loop
99 *	FALSE: We did not find a for loop, or we found the end of the for
100 *	       for loop.
101 *
102 * Side Effects:
103 *	None.
104 *
105 *-----------------------------------------------------------------------
106 */
107int
108For_Eval (line)
109    char    	    *line;    /* Line to parse */
110{
111    char	    *ptr = line, *sub, *wrd;
112    int	    	    level;  	/* Level at which to report errors. */
113
114    level = PARSE_FATAL;
115
116
117    if (forLevel == 0) {
118	Buffer	    buf;
119	int	    varlen;
120
121	for (ptr++; *ptr && isspace(*ptr); ptr++)
122	    continue;
123	/*
124	 * If we are not in a for loop quickly determine if the statement is
125	 * a for.
126	 */
127	if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || !isspace(ptr[3]))
128	    return FALSE;
129	ptr += 3;
130
131	/*
132	 * we found a for loop, and now we are going to parse it.
133	 */
134	while (*ptr && isspace(*ptr))
135	    ptr++;
136
137	/*
138	 * Grab the variable
139	 */
140	buf = Buf_Init(0);
141	for (wrd = ptr; *ptr && !isspace(*ptr); ptr++)
142	    continue;
143	Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd);
144
145	forVar = (char *) Buf_GetAll(buf, &varlen);
146	if (varlen == 0) {
147	    Parse_Error (level, "missing variable in for");
148	    return 0;
149	}
150	Buf_Destroy(buf, FALSE);
151
152	while (*ptr && isspace(*ptr))
153	    ptr++;
154
155	/*
156	 * Grab the `in'
157	 */
158	if (ptr[0] != 'i' || ptr[1] != 'n' || !isspace(ptr[2])) {
159	    Parse_Error (level, "missing `in' in for");
160	    printf("%s\n", ptr);
161	    return 0;
162	}
163	ptr += 3;
164
165	while (*ptr && isspace(*ptr))
166	    ptr++;
167
168	/*
169	 * Make a list with the remaining words
170	 */
171	forLst = Lst_Init(FALSE);
172	buf = Buf_Init(0);
173	sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
174
175#define ADDWORD() \
176	Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \
177	Buf_AddByte(buf, (Byte) '\0'), \
178	Lst_AtEnd(forLst, (ClientData) Buf_GetAll(buf, &varlen)), \
179	Buf_Destroy(buf, FALSE)
180
181	for (ptr = sub; *ptr && isspace(*ptr); ptr++)
182	    continue;
183
184	for (wrd = ptr; *ptr; ptr++)
185	    if (isspace(*ptr)) {
186		ADDWORD();
187		buf = Buf_Init(0);
188		while (*ptr && isspace(*ptr))
189		    ptr++;
190		wrd = ptr--;
191	    }
192	if (DEBUG(FOR))
193	    (void) fprintf(stderr, "For: Iterator %s List %s\n", forVar, sub);
194	if (ptr - wrd > 0)
195	    ADDWORD();
196	else
197	    Buf_Destroy(buf, TRUE);
198	free((Address) sub);
199
200	forBuf = Buf_Init(0);
201	forLevel++;
202	return 1;
203    }
204    else if (*ptr == '.') {
205
206	for (ptr++; *ptr && isspace(*ptr); ptr++)
207	    continue;
208
209	if (strncmp(ptr, "endfor", 6) == 0 && (isspace(ptr[6]) || !ptr[6])) {
210	    if (DEBUG(FOR))
211		(void) fprintf(stderr, "For: end for %d\n", forLevel);
212	    if (--forLevel < 0) {
213		Parse_Error (level, "for-less endfor");
214		return 0;
215	    }
216	}
217	else if (strncmp(ptr, "for", 3) == 0 && isspace(ptr[3])) {
218	    forLevel++;
219	    if (DEBUG(FOR))
220		(void) fprintf(stderr, "For: new loop %d\n", forLevel);
221	}
222    }
223
224    if (forLevel != 0) {
225	Buf_AddBytes(forBuf, strlen(line), (Byte *) line);
226	Buf_AddByte(forBuf, (Byte) '\n');
227	return 1;
228    }
229    else {
230	return 0;
231    }
232}
233
234/*-
235 *-----------------------------------------------------------------------
236 * ForExec --
237 *	Expand the for loop for this index and push it in the Makefile
238 *
239 * Results:
240 *	None.
241 *
242 * Side Effects:
243 *	None.
244 *
245 *-----------------------------------------------------------------------
246 */
247static int
248ForExec(name, arg)
249    char *name;
250    struct For *arg;
251{
252    int len;
253    Var_Set(arg->var, name, VAR_GLOBAL);
254    if (DEBUG(FOR))
255	(void) fprintf(stderr, "--- %s = %s\n", arg->var, name);
256    Parse_FromString(Var_Subst(arg->var, (char *) Buf_GetAll(arg->buf, &len),
257			       VAR_GLOBAL, FALSE));
258    Var_Delete(arg->var, VAR_GLOBAL);
259
260    return 0;
261}
262
263
264/*-
265 *-----------------------------------------------------------------------
266 * For_Run --
267 *	Run the for loop, immitating the actions of an include file
268 *
269 * Results:
270 *	None.
271 *
272 * Side Effects:
273 *	None.
274 *
275 *-----------------------------------------------------------------------
276 */
277void
278For_Run()
279{
280    struct For arg;
281
282    if (forVar == NULL || forBuf == NULL || forLst == NULL)
283	return;
284    arg.var = forVar;
285    arg.buf = forBuf;
286    arg.lst = forLst;
287    forVar = NULL;
288    forBuf = NULL;
289    forLst = NULL;
290
291    Lst_ForEach(arg.lst, ForExec, (ClientData) &arg);
292
293    free((Address)arg.var);
294    Lst_Destroy(arg.lst, free);
295    Buf_Destroy(arg.buf, TRUE);
296}
297