1 /* $NetBSD: for.c,v 1.11 2001/06/12 23:36:17 sjg Exp $ */ 2 3 /* 4 * Copyright (c) 1992, The Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifdef MAKE_BOOTSTRAP 37 static char rcsid[] = "$NetBSD: for.c,v 1.11 2001/06/12 23:36:17 sjg Exp $"; 38 #else 39 #include <sys/cdefs.h> 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93"; 43 #else 44 __RCSID("$NetBSD: for.c,v 1.11 2001/06/12 23:36:17 sjg Exp $"); 45 #endif 46 #endif /* not lint */ 47 #endif 48 49 /*- 50 * for.c -- 51 * Functions to handle loops in a makefile. 52 * 53 * Interface: 54 * For_Eval Evaluate the loop in the passed line. 55 * For_Run Run accumulated loop 56 * 57 */ 58 59 #include <ctype.h> 60 #include <assert.h> 61 #include "make.h" 62 #include "hash.h" 63 #include "dir.h" 64 #include "buf.h" 65 66 /* 67 * For statements are of the form: 68 * 69 * .for <variable> in <varlist> 70 * ... 71 * .endfor 72 * 73 * The trick is to look for the matching end inside for for loop 74 * To do that, we count the current nesting level of the for loops. 75 * and the .endfor statements, accumulating all the statements between 76 * the initial .for loop and the matching .endfor; 77 * then we evaluate the for loop for each variable in the varlist. 78 * 79 * Note that any nested fors are just passed through; they get handled 80 * recursively in For_Eval when we're expanding the enclosing for in 81 * For_Run. 82 */ 83 84 static int forLevel = 0; /* Nesting level */ 85 86 /* 87 * State of a for loop. 88 */ 89 typedef struct _For { 90 Buffer buf; /* Body of loop */ 91 char **vars; /* Iteration variables */ 92 int nvars; /* # of iteration vars */ 93 Lst lst; /* List of items */ 94 } For; 95 96 static For accumFor; /* Loop being accumulated */ 97 98 static void ForAddVar __P((const char *, size_t)); 99 100 101 102 103 /*- 104 *----------------------------------------------------------------------- 105 * ForAddVar -- 106 * Add an iteration variable to the currently accumulating for. 107 * 108 * Results: none 109 * Side effects: no additional side effects. 110 *----------------------------------------------------------------------- 111 */ 112 static void 113 ForAddVar(data, len) 114 const char *data; 115 size_t len; 116 { 117 Buffer buf; 118 int varlen; 119 120 buf = Buf_Init(0); 121 Buf_AddBytes(buf, len, (Byte *) data); 122 123 accumFor.nvars++; 124 accumFor.vars = erealloc(accumFor.vars, accumFor.nvars*sizeof(char *)); 125 126 accumFor.vars[accumFor.nvars-1] = (char *) Buf_GetAll(buf, &varlen); 127 128 Buf_Destroy(buf, FALSE); 129 } 130 131 /*- 132 *----------------------------------------------------------------------- 133 * For_Eval -- 134 * Evaluate the for loop in the passed line. The line 135 * looks like this: 136 * .for <variable> in <varlist> 137 * 138 * Results: 139 * TRUE: We found a for loop, or we are inside a for loop 140 * FALSE: We did not find a for loop, or we found the end of the for 141 * for loop. 142 * 143 * Side Effects: 144 * None. 145 * 146 *----------------------------------------------------------------------- 147 */ 148 int 149 For_Eval (line) 150 char *line; /* Line to parse */ 151 { 152 char *ptr = line, *sub, *in, *wrd; 153 int level; /* Level at which to report errors. */ 154 155 level = PARSE_FATAL; 156 157 158 if (forLevel == 0) { 159 Buffer buf; 160 int varlen; 161 162 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) 163 continue; 164 /* 165 * If we are not in a for loop quickly determine if the statement is 166 * a for. 167 */ 168 if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || 169 !isspace((unsigned char) ptr[3])) 170 return FALSE; 171 ptr += 3; 172 173 /* 174 * we found a for loop, and now we are going to parse it. 175 */ 176 while (*ptr && isspace((unsigned char) *ptr)) 177 ptr++; 178 179 /* 180 * Find the "in". 181 */ 182 for (in = ptr; *in; in++) { 183 if (isspace((unsigned char) in[0]) && in[1]== 'i' && 184 in[2] == 'n' && 185 (in[3] == '\0' || isspace((unsigned char) in[3]))) 186 break; 187 } 188 if (*in == '\0') { 189 Parse_Error(level, "missing `in' in for"); 190 return 0; 191 } 192 193 /* 194 * Grab the variables. 195 */ 196 accumFor.vars = NULL; 197 198 while (ptr < in) { 199 wrd = ptr; 200 while (*ptr && !isspace((unsigned char) *ptr)) 201 ptr++; 202 ForAddVar(wrd, ptr - wrd); 203 while (*ptr && isspace((unsigned char) *ptr)) 204 ptr++; 205 } 206 207 if (accumFor.nvars == 0) { 208 Parse_Error(level, "no iteration variables in for"); 209 return 0; 210 } 211 212 /* At this point we should be pointing right at the "in" */ 213 assert(!memcmp(ptr, "in", 2)); 214 ptr += 2; 215 216 while (*ptr && isspace((unsigned char) *ptr)) 217 ptr++; 218 219 /* 220 * Make a list with the remaining words 221 */ 222 accumFor.lst = Lst_Init(FALSE); 223 buf = Buf_Init(0); 224 sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE); 225 226 #define ADDWORD() \ 227 Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \ 228 Buf_AddByte(buf, (Byte) '\0'), \ 229 Lst_AtFront(accumFor.lst, (ClientData) Buf_GetAll(buf, &varlen)), \ 230 Buf_Destroy(buf, FALSE) 231 232 for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++) 233 continue; 234 235 for (wrd = ptr; *ptr; ptr++) 236 if (isspace((unsigned char) *ptr)) { 237 ADDWORD(); 238 buf = Buf_Init(0); 239 while (*ptr && isspace((unsigned char) *ptr)) 240 ptr++; 241 wrd = ptr--; 242 } 243 if (DEBUG(FOR)) { 244 int i; 245 for (i = 0; i < accumFor.nvars; i++) { 246 (void) fprintf(stderr, "For: variable %s\n", accumFor.vars[i]); 247 } 248 (void) fprintf(stderr, "For: list %s\n", sub); 249 } 250 if (ptr - wrd > 0) 251 ADDWORD(); 252 else 253 Buf_Destroy(buf, TRUE); 254 free((Address) sub); 255 256 accumFor.buf = Buf_Init(0); 257 forLevel++; 258 return 1; 259 } 260 else if (*ptr == '.') { 261 262 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) 263 continue; 264 265 if (strncmp(ptr, "endfor", 6) == 0 && 266 (isspace((unsigned char) ptr[6]) || !ptr[6])) { 267 if (DEBUG(FOR)) 268 (void) fprintf(stderr, "For: end for %d\n", forLevel); 269 if (--forLevel < 0) { 270 Parse_Error (level, "for-less endfor"); 271 return 0; 272 } 273 } 274 else if (strncmp(ptr, "for", 3) == 0 && 275 isspace((unsigned char) ptr[3])) { 276 forLevel++; 277 if (DEBUG(FOR)) 278 (void) fprintf(stderr, "For: new loop %d\n", forLevel); 279 } 280 } 281 282 if (forLevel != 0) { 283 Buf_AddBytes(accumFor.buf, strlen(line), (Byte *) line); 284 Buf_AddByte(accumFor.buf, (Byte) '\n'); 285 return 1; 286 } 287 else { 288 return 0; 289 } 290 } 291 292 293 /*- 294 *----------------------------------------------------------------------- 295 * For_Run -- 296 * Run the for loop, imitating the actions of an include file 297 * 298 * Results: 299 * None. 300 * 301 * Side Effects: 302 * None. 303 * 304 *----------------------------------------------------------------------- 305 */ 306 void 307 For_Run() 308 { 309 For arg; 310 LstNode ln; 311 char **values; 312 int i, done = 0, len; 313 char *guy, *orig_guy, *old_guy; 314 315 if (accumFor.buf == NULL || accumFor.vars == NULL || accumFor.lst == NULL) 316 return; 317 arg = accumFor; 318 accumFor.buf = NULL; 319 accumFor.vars = NULL; 320 accumFor.nvars = 0; 321 accumFor.lst = NULL; 322 323 if (Lst_Open(arg.lst) != SUCCESS) 324 return; 325 326 values = emalloc(arg.nvars * sizeof(char *)); 327 328 while (!done) { 329 /* 330 * due to the dumb way this is set up, this loop must run 331 * backwards. 332 */ 333 for (i = arg.nvars - 1; i >= 0; i--) { 334 ln = Lst_Next(arg.lst); 335 if (ln == NILLNODE) { 336 if (i != arg.nvars-1) { 337 Parse_Error(PARSE_FATAL, 338 "Not enough words in for substitution list"); 339 } 340 done = 1; 341 break; 342 } else { 343 values[i] = (char *) Lst_Datum(ln); 344 } 345 } 346 if (done) 347 break; 348 349 for (i = 0; i < arg.nvars; i++) { 350 Var_Set(arg.vars[i], values[i], VAR_GLOBAL, 0); 351 if (DEBUG(FOR)) 352 (void) fprintf(stderr, "--- %s = %s\n", arg.vars[i], 353 values[i]); 354 } 355 356 /* 357 * Hack, hack, kludge. 358 * This is really ugly, but to do it any better way would require 359 * making major changes to var.c, which I don't want to get into 360 * yet. There is no mechanism for expanding some variables, only 361 * for expanding a single variable. That should be corrected, but 362 * not right away. (XXX) 363 */ 364 365 guy = (char *) Buf_GetAll(arg.buf, &len); 366 orig_guy = guy; 367 for (i = 0; i < arg.nvars; i++) { 368 old_guy = guy; 369 guy = Var_Subst(arg.vars[i], guy, VAR_GLOBAL, FALSE); 370 if (old_guy != orig_guy) 371 free(old_guy); 372 } 373 Parse_FromString(guy); 374 375 for (i = 0; i < arg.nvars; i++) 376 Var_Delete(arg.vars[i], VAR_GLOBAL); 377 } 378 379 free(values); 380 381 Lst_Close(arg.lst); 382 383 for (i=0; i<arg.nvars; i++) { 384 free(arg.vars[i]); 385 } 386 free(arg.vars); 387 388 Lst_Destroy(arg.lst, (void (*) __P((ClientData))) free); 389 Buf_Destroy(arg.buf, TRUE); 390 } 391