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