xref: /trueos/usr.bin/make/for.c (revision bcd0e15cf642d6e5bf78ee585ad282b0e3061864)
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. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)for.c	8.1 (Berkeley) 6/6/93
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 /*-
39  * for.c --
40  *	Functions to handle loops in a makefile.
41  *
42  * Interface:
43  *	For_Eval 	Evaluate the loop in the passed line.
44  *	For_Run		Run accumulated loop
45  *
46  */
47 
48 #include <ctype.h>
49 #include <stdlib.h>
50 #include <string.h>
51 
52 #include "buf.h"
53 #include "for.h"
54 #include "globals.h"
55 #include "lst.h"
56 #include "parse.h"
57 #include "str.h"
58 #include "util.h"
59 #include "var.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  * For_For
82  *	Evaluate the for loop in the passed line. The line
83  *	looks like this:
84  *	    .for <variable> in <varlist>
85  *	The line pointer points just behind the for.
86  *
87  * Results:
88  *	TRUE: Syntax ok.
89  *	FALSE: Syntax error.
90  */
91 Boolean
For_For(char * line)92 For_For(char *line)
93 {
94 	char	*ptr;
95 	char	*wrd;
96 	char	*sub;
97 	Buffer	*buf;
98 	size_t	varlen;
99 	int	i;
100 	ArgArray words;
101 
102 	ptr = line;
103 
104 	/*
105 	 * Skip space between for and the variable.
106 	 */
107 	for (ptr++; *ptr && isspace((u_char)*ptr); ptr++)
108 		;
109 
110 	/*
111 	 * Grab the variable
112 	 */
113 	for (wrd = ptr; *ptr && !isspace((u_char)*ptr); ptr++)
114 		;
115 
116 	buf = Buf_Init(0);
117 	Buf_AppendRange(buf, wrd, ptr);
118 	forVar = Buf_GetAll(buf, &varlen);
119 
120 	if (varlen == 0) {
121 		Buf_Destroy(buf, TRUE);
122 		Parse_Error(PARSE_FATAL, "missing variable in for");
123 		return (FALSE);
124 	}
125 	Buf_Destroy(buf, FALSE);
126 
127 	/*
128 	 * Skip to 'in'.
129 	 */
130 	while (*ptr && isspace((u_char)*ptr))
131 		ptr++;
132 
133 	/*
134 	 * Grab the `in'
135 	 */
136 	if (ptr[0] != 'i' || ptr[1] != 'n' || !isspace((u_char)ptr[2])) {
137 		free(forVar);
138 		Parse_Error(PARSE_FATAL, "missing `in' in for");
139 		fprintf(stderr, "%s\n", ptr);
140 		return (FALSE);
141 	}
142 	ptr += 3;
143 
144 	/*
145 	 * Skip to values
146 	 */
147 	while (*ptr && isspace((u_char)*ptr))
148 		ptr++;
149 
150 	/*
151 	 * Make a list with the remaining words
152 	 */
153 	sub = Buf_Peel(Var_Subst(ptr, VAR_CMD, FALSE));
154 	brk_string(&words, sub, FALSE);
155 	Lst_Init(&forLst);
156 	for (i = 1; i < words.argc; i++) {
157 		if (words.argv[i][0] != '\0')
158 			Lst_AtFront(&forLst, estrdup(words.argv[i]));
159 	}
160 	ArgArray_Done(&words);
161 	DEBUGF(FOR, ("For: Iterator %s List %s\n", forVar, sub));
162 	free(sub);
163 
164 	forBuf = Buf_Init(0);
165 	forLevel++;
166 	return (TRUE);
167 }
168 
169 /**
170  * For_Eval
171  *	Eat a line of the .for body looking for embedded .for loops
172  *	and the .endfor
173  */
174 Boolean
For_Eval(char * line)175 For_Eval(char *line)
176 {
177 	char *ptr;
178 
179 	ptr = line;
180 
181 	if (*ptr == '.') {
182 		/*
183 		 * Need to check for 'endfor' and 'for' to find the end
184 		 * of our loop or to find embedded for loops.
185 		 */
186 		for (ptr++; *ptr != '\0' && isspace((u_char)*ptr); ptr++)
187 			;
188 
189 		/* XXX the isspace is wrong */
190 		if (strncmp(ptr, "endfor", 6) == 0 &&
191 		    (isspace((u_char)ptr[6]) || ptr[6] == '\0')) {
192 			DEBUGF(FOR, ("For: end for %d\n", forLevel));
193 			if (forLevel == 0) {
194 				/* should not be here */
195 				abort();
196 			}
197 			forLevel--;
198 
199 		} else if (strncmp(ptr, "for", 3) == 0 &&
200 		    isspace((u_char)ptr[3])) {
201 			forLevel++;
202 			DEBUGF(FOR, ("For: new loop %d\n", forLevel));
203 		}
204 	}
205 
206 	if (forLevel != 0) {
207 		/*
208 		 * Still in loop - append the line
209 		 */
210 		Buf_Append(forBuf, line);
211 		Buf_AddByte(forBuf, (Byte)'\n');
212 		return (TRUE);
213 	}
214 
215 	return (FALSE);
216 }
217 
218 /*-
219  *-----------------------------------------------------------------------
220  * For_Run --
221  *	Run the for loop, imitating the actions of an include file
222  *
223  * Results:
224  *	None.
225  *
226  * Side Effects:
227  *	The values of the variables forLst, forVar and forBuf are freed.
228  *
229  *-----------------------------------------------------------------------
230  */
231 void
For_Run(int lineno)232 For_Run(int lineno)
233 {
234 	Lst		values;	/* list of values for the variable */
235 	char		*var;	/* the variable's name */
236 	Buffer		*buf;	/* the contents of the for loop */
237 	const char	*val;	/* current value of loop variable */
238 	LstNode		*ln;
239 	char		*str;
240 
241 	if (forVar == NULL || forBuf == NULL)
242 		return;
243 
244 	/* copy the global variables to have them free for embedded fors */
245 	var = forVar;
246 	buf = forBuf;
247 	Lst_Init(&values);
248 	Lst_Concat(&values, &forLst, LST_CONCLINK);
249 
250 	forVar = NULL;
251 	forBuf = NULL;
252 
253 	LST_FOREACH(ln, &values) {
254 		val = Lst_Datum(ln);
255 		Var_SetGlobal(var, val);
256 
257 		DEBUGF(FOR, ("--- %s = %s\n", var, val));
258 		str = Buf_Peel(Var_SubstOnly(var, Buf_Data(buf), FALSE));
259 
260 		Parse_FromString(str, lineno);
261 		Var_Delete(var, VAR_GLOBAL);
262 	}
263 
264 	free(var);
265 	Lst_Destroy(&values, free);
266 	Buf_Destroy(buf, TRUE);
267 }
268