xref: /original-bsd/usr.bin/make/for.c (revision 01e8f48f)
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.2 (Berkeley) 04/28/95";
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 typedef struct _For {
54     Buffer	  buf;			/* Unexpanded buffer	*/
55     char*	  var;			/* Index name		*/
56     Lst  	  lst;			/* List of variables	*/
57 } For;
58 
59 static int ForExec	__P((ClientData, ClientData));
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((unsigned char) *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' ||
102 	    !isspace((unsigned char) ptr[3]))
103 	    return FALSE;
104 	ptr += 3;
105 
106 	/*
107 	 * we found a for loop, and now we are going to parse it.
108 	 */
109 	while (*ptr && isspace((unsigned char) *ptr))
110 	    ptr++;
111 
112 	/*
113 	 * Grab the variable
114 	 */
115 	buf = Buf_Init(0);
116 	for (wrd = ptr; *ptr && !isspace((unsigned char) *ptr); ptr++)
117 	    continue;
118 	Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd);
119 
120 	forVar = (char *) Buf_GetAll(buf, &varlen);
121 	if (varlen == 0) {
122 	    Parse_Error (level, "missing variable in for");
123 	    return 0;
124 	}
125 	Buf_Destroy(buf, FALSE);
126 
127 	while (*ptr && isspace((unsigned char) *ptr))
128 	    ptr++;
129 
130 	/*
131 	 * Grab the `in'
132 	 */
133 	if (ptr[0] != 'i' || ptr[1] != 'n' ||
134 	    !isspace((unsigned char) ptr[2])) {
135 	    Parse_Error (level, "missing `in' in for");
136 	    printf("%s\n", ptr);
137 	    return 0;
138 	}
139 	ptr += 3;
140 
141 	while (*ptr && isspace((unsigned char) *ptr))
142 	    ptr++;
143 
144 	/*
145 	 * Make a list with the remaining words
146 	 */
147 	forLst = Lst_Init(FALSE);
148 	buf = Buf_Init(0);
149 	sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
150 
151 #define ADDWORD() \
152 	Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \
153 	Buf_AddByte(buf, (Byte) '\0'), \
154 	Lst_AtEnd(forLst, (ClientData) Buf_GetAll(buf, &varlen)), \
155 	Buf_Destroy(buf, FALSE)
156 
157 	for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++)
158 	    continue;
159 
160 	for (wrd = ptr; *ptr; ptr++)
161 	    if (isspace((unsigned char) *ptr)) {
162 		ADDWORD();
163 		buf = Buf_Init(0);
164 		while (*ptr && isspace((unsigned char) *ptr))
165 		    ptr++;
166 		wrd = ptr--;
167 	    }
168 	if (DEBUG(FOR))
169 	    (void) fprintf(stderr, "For: Iterator %s List %s\n", forVar, sub);
170 	if (ptr - wrd > 0)
171 	    ADDWORD();
172 	else
173 	    Buf_Destroy(buf, TRUE);
174 	free((Address) sub);
175 
176 	forBuf = Buf_Init(0);
177 	forLevel++;
178 	return 1;
179     }
180     else if (*ptr == '.') {
181 
182 	for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
183 	    continue;
184 
185 	if (strncmp(ptr, "endfor", 6) == 0 &&
186 	    (isspace((unsigned char) ptr[6]) || !ptr[6])) {
187 	    if (DEBUG(FOR))
188 		(void) fprintf(stderr, "For: end for %d\n", forLevel);
189 	    if (--forLevel < 0) {
190 		Parse_Error (level, "for-less endfor");
191 		return 0;
192 	    }
193 	}
194 	else if (strncmp(ptr, "for", 3) == 0 &&
195 		 isspace((unsigned char) ptr[3])) {
196 	    forLevel++;
197 	    if (DEBUG(FOR))
198 		(void) fprintf(stderr, "For: new loop %d\n", forLevel);
199 	}
200     }
201 
202     if (forLevel != 0) {
203 	Buf_AddBytes(forBuf, strlen(line), (Byte *) line);
204 	Buf_AddByte(forBuf, (Byte) '\n');
205 	return 1;
206     }
207     else {
208 	return 0;
209     }
210 }
211 
212 /*-
213  *-----------------------------------------------------------------------
214  * ForExec --
215  *	Expand the for loop for this index and push it in the Makefile
216  *
217  * Results:
218  *	None.
219  *
220  * Side Effects:
221  *	None.
222  *
223  *-----------------------------------------------------------------------
224  */
225 static int
226 ForExec(namep, argp)
227     ClientData namep;
228     ClientData argp;
229 {
230     char *name = (char *) namep;
231     For *arg = (For *) argp;
232     int len;
233     Var_Set(arg->var, name, VAR_GLOBAL);
234     if (DEBUG(FOR))
235 	(void) fprintf(stderr, "--- %s = %s\n", arg->var, name);
236     Parse_FromString(Var_Subst(arg->var, (char *) Buf_GetAll(arg->buf, &len),
237 			       VAR_GLOBAL, FALSE));
238     Var_Delete(arg->var, VAR_GLOBAL);
239 
240     return 0;
241 }
242 
243 
244 /*-
245  *-----------------------------------------------------------------------
246  * For_Run --
247  *	Run the for loop, immitating the actions of an include file
248  *
249  * Results:
250  *	None.
251  *
252  * Side Effects:
253  *	None.
254  *
255  *-----------------------------------------------------------------------
256  */
257 void
258 For_Run()
259 {
260     For arg;
261 
262     if (forVar == NULL || forBuf == NULL || forLst == NULL)
263 	return;
264     arg.var = forVar;
265     arg.buf = forBuf;
266     arg.lst = forLst;
267     forVar = NULL;
268     forBuf = NULL;
269     forLst = NULL;
270 
271     Lst_ForEach(arg.lst, ForExec, (ClientData) &arg);
272 
273     free((Address)arg.var);
274     Lst_Destroy(arg.lst, (void (*) __P((ClientData))) free);
275     Buf_Destroy(arg.buf, TRUE);
276 }
277