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