1 /* $OpenPackages$ */
2 /* $OpenBSD: for.c,v 1.31 2007/03/20 03:50:39 tedu Exp $ */
3 /* $NetBSD: for.c,v 1.4 1996/11/06 17:59:05 christos Exp $ */
4
5 /*
6 * Copyright (c) 1999 Marc Espie.
7 *
8 * Extensive code modifications for the OpenBSD project.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
23 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Copyright (c) 1992, The Regents of the University of California.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61 #include <assert.h>
62 #include <ctype.h>
63 #include <stddef.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include "config.h"
68 #include "defines.h"
69 #include "buf.h"
70 #include "for.h"
71 #include "lst.h"
72 #include "error.h"
73 #include "var.h"
74 #include "lowparse.h"
75 #include "str.h"
76 #include "memory.h"
77
78 /*
79 * For statements are of the form:
80 *
81 * .for <variable> [variable...] in <varlist>
82 * ...
83 * .endfor
84 *
85 * The trick is to look for the matching .end inside .for loops.
86 * To do that, we keep track of the nesting level of .for loops
87 * and matching .endfor statements, accumulating all statements between
88 * the initial .for loop and the matching .endfor,
89 * then we evaluate the .for loop for each variable in the varlist.
90 */
91
92 /* State of a for loop. */
93 struct For_ {
94 char *text; /* Unexpanded text */
95 LIST vars; /* List of variables */
96 LstNode var; /* Current var */
97 int nvars; /* Total number of vars */
98 LIST lst; /* List of items */
99 size_t guess; /* Estimated expansion size */
100 BUFFER buf; /* Accumulating text */
101 unsigned long lineno; /* Line number at start of loop */
102 unsigned long level; /* Nesting level */
103 bool freeold;
104 };
105
106 /* ForExec(value, handle);
107 * Expands next variable in loop sequence described by handle to value. */
108 static void ForExec(void *, void *);
109
110 /* n = build_words_list(lst, s);
111 * Cuts string into words, pushes words into list, in reverse order,
112 * because Parse_FromString works as a stack.
113 * Returns the number of words. */
114 static unsigned long build_words_list(Lst, const char *);
115
116 static unsigned long
build_words_list(Lst lst,const char * s)117 build_words_list(Lst lst, const char *s)
118 {
119 const char *end, *wrd;
120 unsigned long n;
121
122 n = 0;
123 end = s;
124
125 while ((wrd = iterate_words(&end)) != NULL) {
126 Lst_AtFront(lst, escape_dupi(wrd, end, "\"'"));
127 n++;
128 }
129 return n;
130 }
131
132 For *
For_Eval(const char * line)133 For_Eval(const char *line)
134 {
135 const char *ptr = line;
136 const char *wrd;
137 char *sub;
138 const char *endVar;
139 For *arg;
140 unsigned long n;
141
142 while (isspace(*ptr))
143 ptr++;
144
145 /* Parse loop. */
146
147 arg = emalloc(sizeof(*arg));
148 arg->nvars = 0;
149 Lst_Init(&arg->vars);
150
151 for (;;) {
152 /* Grab the variables. */
153 for (wrd = ptr; *ptr && !isspace(*ptr); ptr++)
154 continue;
155 if (ptr - wrd == 0) {
156 Parse_Error(PARSE_FATAL, "Syntax error in for");
157 return 0;
158 }
159 endVar = ptr++;
160 while (isspace(*ptr))
161 ptr++;
162 /* End of variable list ? */
163 if (endVar - wrd == 2 && wrd[0] == 'i' && wrd[1] == 'n')
164 break;
165 Lst_AtEnd(&arg->vars, Str_dupi(wrd, endVar));
166 arg->nvars++;
167 }
168 if (arg->nvars == 0) {
169 Parse_Error(PARSE_FATAL, "Missing variable in for");
170 return 0;
171 }
172
173 /* Make a list with the remaining words. */
174 sub = Var_Subst(ptr, NULL, false);
175 if (DEBUG(FOR)) {
176 LstNode ln;
177 (void)fprintf(stderr, "For: Iterator ");
178 for (ln = Lst_First(&arg->vars); ln != NULL; ln = Lst_Adv(ln))
179 (void)fprintf(stderr, "%s ", (char *)Lst_Datum(ln));
180 (void)fprintf(stderr, "List %s\n", sub);
181 }
182
183 Lst_Init(&arg->lst);
184 n = build_words_list(&arg->lst, sub);
185 free(sub);
186 if (arg->nvars != 1 && n % arg->nvars != 0) {
187 Parse_Error(PARSE_FATAL, "Wrong number of items in for loop");
188 return 0;
189 }
190 arg->lineno = Parse_Getlineno();
191 arg->level = 1;
192 Buf_Init(&arg->buf, 0);
193
194 return arg;
195 }
196
197
198 bool
For_Accumulate(For * arg,const char * line)199 For_Accumulate(For *arg, const char *line)
200 {
201 const char *ptr = line;
202
203 assert(arg->level > 0);
204
205 if (*ptr == '.') {
206
207 for (ptr++; isspace(*ptr); ptr++)
208 continue;
209
210 if (strncmp(ptr, "endfor", 6) == 0 &&
211 (isspace(ptr[6]) || !ptr[6])) {
212 if (DEBUG(FOR))
213 (void)fprintf(stderr, "For: end for %lu\n", arg->level);
214 /* If matching endfor, don't add line to buffer. */
215 if (--arg->level == 0)
216 return false;
217 }
218 else if (strncmp(ptr, "for", 3) == 0 &&
219 isspace(ptr[3])) {
220 arg->level++;
221 if (DEBUG(FOR))
222 (void)fprintf(stderr, "For: new loop %lu\n", arg->level);
223 }
224 }
225 Buf_AddString(&arg->buf, line);
226 Buf_AddChar(&arg->buf, '\n');
227 return true;
228 }
229
230
231 #define GUESS_EXPANSION 32
232 static void
ForExec(void * valuep,void * argp)233 ForExec(void *valuep, void *argp)
234 {
235 char *value = (char *)valuep;
236 For *arg = (For *)argp;
237 BUFFER buf;
238
239 /* Parse_FromString pushes stuff back, so we need to go over vars in
240 reverse. */
241 if (arg->var == NULL) {
242 arg->var = Lst_Last(&arg->vars);
243 arg->text = Buf_Retrieve(&arg->buf);
244 arg->freeold = false;
245 }
246
247 if (DEBUG(FOR))
248 (void)fprintf(stderr, "--- %s = %s\n", (char *)Lst_Datum(arg->var),
249 value);
250 Buf_Init(&buf, arg->guess);
251 Var_SubstVar(&buf, arg->text, Lst_Datum(arg->var), value);
252 if (arg->freeold)
253 free(arg->text);
254 arg->text = Buf_Retrieve(&buf);
255 arg->freeold = true;
256 arg->var = Lst_Rev(arg->var);
257 if (arg->var == NULL)
258 Parse_FromString(arg->text, arg->lineno);
259 }
260
261
262 void
For_Run(For * arg)263 For_Run(For *arg)
264 {
265 arg->text = Buf_Retrieve(&arg->buf);
266 arg->guess = Buf_Size(&arg->buf) + GUESS_EXPANSION;
267
268 arg->var = NULL;
269 Lst_ForEach(&arg->lst, ForExec, arg);
270 Buf_Destroy(&arg->buf);
271 Lst_Destroy(&arg->vars, (SimpleProc)free);
272 Lst_Destroy(&arg->lst, (SimpleProc)free);
273 free(arg);
274 }
275