Deleted Added
full compact
for.c (146048) for.c (177545)
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>
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: head/usr.bin/make/for.c 146048 2005-05-10 12:02:15Z harti $");
36__FBSDID("$FreeBSD: head/usr.bin/make/for.c 177545 2008-03-24 12:33:28Z ru $");
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 "dir.h"
54#include "for.h"
55#include "globals.h"
56#include "lst.h"
57#include "make.h"
58#include "parse.h"
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 "dir.h"
54#include "for.h"
55#include "globals.h"
56#include "lst.h"
57#include "make.h"
58#include "parse.h"
59#include "str.h"
59#include "util.h"
60#include "var.h"
61
62/*
63 * For statements are of the form:
64 *
65 * .for <variable> in <varlist>
66 * ...
67 * .endfor
68 *
69 * The trick is to look for the matching end inside for for loop
70 * To do that, we count the current nesting level of the for loops.
71 * and the .endfor statements, accumulating all the statements between
72 * the initial .for loop and the matching .endfor;
73 * then we evaluate the for loop for each variable in the varlist.
74 */
75
76static int forLevel = 0; /* Nesting level */
77static char *forVar; /* Iteration variable */
78static Buffer *forBuf; /* Commands in loop */
79static Lst forLst; /* List of items */
80
81/**
82 * For_For
83 * Evaluate the for loop in the passed line. The line
84 * looks like this:
85 * .for <variable> in <varlist>
86 * The line pointer points just behind the for.
87 *
88 * Results:
89 * TRUE: Syntax ok.
90 * FALSE: Syntax error.
91 */
92Boolean
93For_For(char *line)
94{
95 char *ptr;
96 char *wrd;
97 char *sub;
98 Buffer *buf;
99 size_t varlen;
60#include "util.h"
61#include "var.h"
62
63/*
64 * For statements are of the form:
65 *
66 * .for <variable> in <varlist>
67 * ...
68 * .endfor
69 *
70 * The trick is to look for the matching end inside for for loop
71 * To do that, we count the current nesting level of the for loops.
72 * and the .endfor statements, accumulating all the statements between
73 * the initial .for loop and the matching .endfor;
74 * then we evaluate the for loop for each variable in the varlist.
75 */
76
77static int forLevel = 0; /* Nesting level */
78static char *forVar; /* Iteration variable */
79static Buffer *forBuf; /* Commands in loop */
80static Lst forLst; /* List of items */
81
82/**
83 * For_For
84 * Evaluate the for loop in the passed line. The line
85 * looks like this:
86 * .for <variable> in <varlist>
87 * The line pointer points just behind the for.
88 *
89 * Results:
90 * TRUE: Syntax ok.
91 * FALSE: Syntax error.
92 */
93Boolean
94For_For(char *line)
95{
96 char *ptr;
97 char *wrd;
98 char *sub;
99 Buffer *buf;
100 size_t varlen;
101 int i;
102 ArgArray words;
100
101 ptr = line;
102
103 /*
104 * Skip space between for and the variable.
105 */
106 for (ptr++; *ptr && isspace((u_char)*ptr); ptr++)
107 ;
108
109 /*
110 * Grab the variable
111 */
112 for (wrd = ptr; *ptr && !isspace((u_char)*ptr); ptr++)
113 ;
114
115 buf = Buf_Init(0);
116 Buf_AppendRange(buf, wrd, ptr);
117 forVar = Buf_GetAll(buf, &varlen);
118
119 if (varlen == 0) {
120 Buf_Destroy(buf, TRUE);
121 Parse_Error(PARSE_FATAL, "missing variable in for");
122 return (FALSE);
123 }
124 Buf_Destroy(buf, FALSE);
125
126 /*
127 * Skip to 'in'.
128 */
129 while (*ptr && isspace((u_char)*ptr))
130 ptr++;
131
132 /*
133 * Grab the `in'
134 */
135 if (ptr[0] != 'i' || ptr[1] != 'n' || !isspace((u_char)ptr[2])) {
136 free(forVar);
137 Parse_Error(PARSE_FATAL, "missing `in' in for");
138 fprintf(stderr, "%s\n", ptr);
139 return (FALSE);
140 }
141 ptr += 3;
142
143 /*
144 * Skip to values
145 */
146 while (*ptr && isspace((u_char)*ptr))
147 ptr++;
148
149 /*
150 * Make a list with the remaining words
103
104 ptr = line;
105
106 /*
107 * Skip space between for and the variable.
108 */
109 for (ptr++; *ptr && isspace((u_char)*ptr); ptr++)
110 ;
111
112 /*
113 * Grab the variable
114 */
115 for (wrd = ptr; *ptr && !isspace((u_char)*ptr); ptr++)
116 ;
117
118 buf = Buf_Init(0);
119 Buf_AppendRange(buf, wrd, ptr);
120 forVar = Buf_GetAll(buf, &varlen);
121
122 if (varlen == 0) {
123 Buf_Destroy(buf, TRUE);
124 Parse_Error(PARSE_FATAL, "missing variable in for");
125 return (FALSE);
126 }
127 Buf_Destroy(buf, FALSE);
128
129 /*
130 * Skip to 'in'.
131 */
132 while (*ptr && isspace((u_char)*ptr))
133 ptr++;
134
135 /*
136 * Grab the `in'
137 */
138 if (ptr[0] != 'i' || ptr[1] != 'n' || !isspace((u_char)ptr[2])) {
139 free(forVar);
140 Parse_Error(PARSE_FATAL, "missing `in' in for");
141 fprintf(stderr, "%s\n", ptr);
142 return (FALSE);
143 }
144 ptr += 3;
145
146 /*
147 * Skip to values
148 */
149 while (*ptr && isspace((u_char)*ptr))
150 ptr++;
151
152 /*
153 * Make a list with the remaining words
151 * XXX should use brk_string here.
152 */
153 sub = Buf_Peel(Var_Subst(ptr, VAR_CMD, FALSE));
154 */
155 sub = Buf_Peel(Var_Subst(ptr, VAR_CMD, FALSE));
154 for (ptr = sub; *ptr != '\0' && isspace((u_char)*ptr); ptr++)
155 ;
156
156 brk_string(&words, sub, FALSE);
157 Lst_Init(&forLst);
157 Lst_Init(&forLst);
158 buf = Buf_Init(0);
159 for (wrd = ptr; *ptr != '\0'; ptr++) {
160 if (isspace((u_char)*ptr)) {
161 Buf_AppendRange(buf, wrd, ptr);
162 Lst_AtFront(&forLst, Buf_Peel(buf));
163
164 buf = Buf_Init(0);
165 while (*ptr != '\0' && isspace((u_char)*ptr))
166 ptr++;
167 wrd = ptr--;
168 }
158 for (i = 1; i < words.argc; i++) {
159 if (words.argv[i][0] != '\0')
160 Lst_AtFront(&forLst, estrdup(words.argv[i]));
169 }
161 }
162 ArgArray_Done(&words);
170 DEBUGF(FOR, ("For: Iterator %s List %s\n", forVar, sub));
163 DEBUGF(FOR, ("For: Iterator %s List %s\n", forVar, sub));
171
172 if (ptr - wrd > 0) {
173 Buf_AppendRange(buf, wrd, ptr);
174 Lst_AtFront(&forLst, Buf_Peel(buf));
175 } else {
176 Buf_Destroy(buf, TRUE);
177 }
178 free(sub);
179
180 forBuf = Buf_Init(0);
181 forLevel++;
182 return (TRUE);
183}
184
185/**
186 * For_Eval
187 * Eat a line of the .for body looking for embedded .for loops
188 * and the .endfor
189 */
190Boolean
191For_Eval(char *line)
192{
193 char *ptr;
194
195 ptr = line;
196
197 if (*ptr == '.') {
198 /*
199 * Need to check for 'endfor' and 'for' to find the end
200 * of our loop or to find embedded for loops.
201 */
202 for (ptr++; *ptr != '\0' && isspace((u_char)*ptr); ptr++)
203 ;
204
205 /* XXX the isspace is wrong */
206 if (strncmp(ptr, "endfor", 6) == 0 &&
207 (isspace((u_char)ptr[6]) || ptr[6] == '\0')) {
208 DEBUGF(FOR, ("For: end for %d\n", forLevel));
209 if (forLevel == 0) {
210 /* should not be here */
211 abort();
212 }
213 forLevel--;
214
215 } else if (strncmp(ptr, "for", 3) == 0 &&
216 isspace((u_char)ptr[3])) {
217 forLevel++;
218 DEBUGF(FOR, ("For: new loop %d\n", forLevel));
219 }
220 }
221
222 if (forLevel != 0) {
223 /*
224 * Still in loop - append the line
225 */
226 Buf_Append(forBuf, line);
227 Buf_AddByte(forBuf, (Byte)'\n');
228 return (TRUE);
229 }
230
231 return (FALSE);
232}
233
234/*-
235 *-----------------------------------------------------------------------
236 * For_Run --
237 * Run the for loop, immitating the actions of an include file
238 *
239 * Results:
240 * None.
241 *
242 * Side Effects:
243 * The values of the variables forLst, forVar and forBuf are freed.
244 *
245 *-----------------------------------------------------------------------
246 */
247void
248For_Run(int lineno)
249{
250 Lst values; /* list of values for the variable */
251 char *var; /* the variable's name */
252 Buffer *buf; /* the contents of the for loop */
253 const char *val; /* current value of loop variable */
254 LstNode *ln;
255 char *str;
256
257 if (forVar == NULL || forBuf == NULL)
258 return;
259
260 /* copy the global variables to have them free for embedded fors */
261 var = forVar;
262 buf = forBuf;
263 Lst_Init(&values);
264 Lst_Concat(&values, &forLst, LST_CONCLINK);
265
266 forVar = NULL;
267 forBuf = NULL;
268
269 LST_FOREACH(ln, &values) {
270 val = Lst_Datum(ln);
271 Var_Set(var, val, VAR_GLOBAL);
272
273 DEBUGF(FOR, ("--- %s = %s\n", var, val));
274 str = Buf_Peel(Var_SubstOnly(var, Buf_Data(buf), FALSE));
275
276 Parse_FromString(str, lineno);
277 Var_Delete(var, VAR_GLOBAL);
278 }
279
280 free(var);
281 Lst_Destroy(&values, free);
282 Buf_Destroy(buf, TRUE);
283}
164 free(sub);
165
166 forBuf = Buf_Init(0);
167 forLevel++;
168 return (TRUE);
169}
170
171/**
172 * For_Eval
173 * Eat a line of the .for body looking for embedded .for loops
174 * and the .endfor
175 */
176Boolean
177For_Eval(char *line)
178{
179 char *ptr;
180
181 ptr = line;
182
183 if (*ptr == '.') {
184 /*
185 * Need to check for 'endfor' and 'for' to find the end
186 * of our loop or to find embedded for loops.
187 */
188 for (ptr++; *ptr != '\0' && isspace((u_char)*ptr); ptr++)
189 ;
190
191 /* XXX the isspace is wrong */
192 if (strncmp(ptr, "endfor", 6) == 0 &&
193 (isspace((u_char)ptr[6]) || ptr[6] == '\0')) {
194 DEBUGF(FOR, ("For: end for %d\n", forLevel));
195 if (forLevel == 0) {
196 /* should not be here */
197 abort();
198 }
199 forLevel--;
200
201 } else if (strncmp(ptr, "for", 3) == 0 &&
202 isspace((u_char)ptr[3])) {
203 forLevel++;
204 DEBUGF(FOR, ("For: new loop %d\n", forLevel));
205 }
206 }
207
208 if (forLevel != 0) {
209 /*
210 * Still in loop - append the line
211 */
212 Buf_Append(forBuf, line);
213 Buf_AddByte(forBuf, (Byte)'\n');
214 return (TRUE);
215 }
216
217 return (FALSE);
218}
219
220/*-
221 *-----------------------------------------------------------------------
222 * For_Run --
223 * Run the for loop, immitating the actions of an include file
224 *
225 * Results:
226 * None.
227 *
228 * Side Effects:
229 * The values of the variables forLst, forVar and forBuf are freed.
230 *
231 *-----------------------------------------------------------------------
232 */
233void
234For_Run(int lineno)
235{
236 Lst values; /* list of values for the variable */
237 char *var; /* the variable's name */
238 Buffer *buf; /* the contents of the for loop */
239 const char *val; /* current value of loop variable */
240 LstNode *ln;
241 char *str;
242
243 if (forVar == NULL || forBuf == NULL)
244 return;
245
246 /* copy the global variables to have them free for embedded fors */
247 var = forVar;
248 buf = forBuf;
249 Lst_Init(&values);
250 Lst_Concat(&values, &forLst, LST_CONCLINK);
251
252 forVar = NULL;
253 forBuf = NULL;
254
255 LST_FOREACH(ln, &values) {
256 val = Lst_Datum(ln);
257 Var_Set(var, val, VAR_GLOBAL);
258
259 DEBUGF(FOR, ("--- %s = %s\n", var, val));
260 str = Buf_Peel(Var_SubstOnly(var, Buf_Data(buf), FALSE));
261
262 Parse_FromString(str, lineno);
263 Var_Delete(var, VAR_GLOBAL);
264 }
265
266 free(var);
267 Lst_Destroy(&values, free);
268 Buf_Destroy(buf, TRUE);
269}