1 /* $NetBSD: for.c,v 1.12 2002/03/12 20:15:15 christos 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.12 2002/03/12 20:15:15 christos 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.12 2002/03/12 20:15:15 christos 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 static const char instr[] = "in"; 162 163 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) 164 continue; 165 /* 166 * If we are not in a for loop quickly determine if the statement is 167 * a for. 168 */ 169 if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || 170 !isspace((unsigned char) ptr[3])) 171 return FALSE; 172 ptr += 3; 173 174 /* 175 * we found a for loop, and now we are going to parse it. 176 */ 177 while (*ptr && isspace((unsigned char) *ptr)) 178 ptr++; 179 180 /* 181 * Find the "in". 182 */ 183 for (in = ptr; *in; in++) { 184 if (isspace((unsigned char) in[0]) && in[1]== 'i' && 185 in[2] == 'n' && 186 (in[3] == '\0' || isspace((unsigned char) in[3]))) 187 break; 188 } 189 if (*in == '\0') { 190 Parse_Error(level, "missing `in' in for"); 191 return 0; 192 } 193 194 /* 195 * Grab the variables. 196 */ 197 accumFor.vars = NULL; 198 199 while (ptr < in) { 200 wrd = ptr; 201 while (*ptr && !isspace((unsigned char) *ptr)) 202 ptr++; 203 ForAddVar(wrd, ptr - wrd); 204 while (*ptr && isspace((unsigned char) *ptr)) 205 ptr++; 206 } 207 208 if (accumFor.nvars == 0) { 209 Parse_Error(level, "no iteration variables in for"); 210 return 0; 211 } 212 213 /* At this point we should be pointing right at the "in" */ 214 /* 215 * compensate for hp/ux's brain damaged assert macro that 216 * does not handle double quotes nicely. 217 */ 218 assert(!memcmp(ptr, instr, 2)); 219 ptr += 2; 220 221 while (*ptr && isspace((unsigned char) *ptr)) 222 ptr++; 223 224 /* 225 * Make a list with the remaining words 226 */ 227 accumFor.lst = Lst_Init(FALSE); 228 buf = Buf_Init(0); 229 sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE); 230 231 #define ADDWORD() \ 232 Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \ 233 Buf_AddByte(buf, (Byte) '\0'), \ 234 Lst_AtFront(accumFor.lst, (ClientData) Buf_GetAll(buf, &varlen)), \ 235 Buf_Destroy(buf, FALSE) 236 237 for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++) 238 continue; 239 240 for (wrd = ptr; *ptr; ptr++) 241 if (isspace((unsigned char) *ptr)) { 242 ADDWORD(); 243 buf = Buf_Init(0); 244 while (*ptr && isspace((unsigned char) *ptr)) 245 ptr++; 246 wrd = ptr--; 247 } 248 if (DEBUG(FOR)) { 249 int i; 250 for (i = 0; i < accumFor.nvars; i++) { 251 (void) fprintf(stderr, "For: variable %s\n", accumFor.vars[i]); 252 } 253 (void) fprintf(stderr, "For: list %s\n", sub); 254 } 255 if (ptr - wrd > 0) 256 ADDWORD(); 257 else 258 Buf_Destroy(buf, TRUE); 259 free((Address) sub); 260 261 accumFor.buf = Buf_Init(0); 262 forLevel++; 263 return 1; 264 } 265 else if (*ptr == '.') { 266 267 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) 268 continue; 269 270 if (strncmp(ptr, "endfor", 6) == 0 && 271 (isspace((unsigned char) ptr[6]) || !ptr[6])) { 272 if (DEBUG(FOR)) 273 (void) fprintf(stderr, "For: end for %d\n", forLevel); 274 if (--forLevel < 0) { 275 Parse_Error (level, "for-less endfor"); 276 return 0; 277 } 278 } 279 else if (strncmp(ptr, "for", 3) == 0 && 280 isspace((unsigned char) ptr[3])) { 281 forLevel++; 282 if (DEBUG(FOR)) 283 (void) fprintf(stderr, "For: new loop %d\n", forLevel); 284 } 285 } 286 287 if (forLevel != 0) { 288 Buf_AddBytes(accumFor.buf, strlen(line), (Byte *) line); 289 Buf_AddByte(accumFor.buf, (Byte) '\n'); 290 return 1; 291 } 292 else { 293 return 0; 294 } 295 } 296 297 298 /*- 299 *----------------------------------------------------------------------- 300 * For_Run -- 301 * Run the for loop, imitating the actions of an include file 302 * 303 * Results: 304 * None. 305 * 306 * Side Effects: 307 * None. 308 * 309 *----------------------------------------------------------------------- 310 */ 311 void 312 For_Run() 313 { 314 For arg; 315 LstNode ln; 316 char **values; 317 int i, done = 0, len; 318 char *guy, *orig_guy, *old_guy; 319 320 if (accumFor.buf == NULL || accumFor.vars == NULL || accumFor.lst == NULL) 321 return; 322 arg = accumFor; 323 accumFor.buf = NULL; 324 accumFor.vars = NULL; 325 accumFor.nvars = 0; 326 accumFor.lst = NULL; 327 328 if (Lst_Open(arg.lst) != SUCCESS) 329 return; 330 331 values = emalloc(arg.nvars * sizeof(char *)); 332 333 while (!done) { 334 /* 335 * due to the dumb way this is set up, this loop must run 336 * backwards. 337 */ 338 for (i = arg.nvars - 1; i >= 0; i--) { 339 ln = Lst_Next(arg.lst); 340 if (ln == NILLNODE) { 341 if (i != arg.nvars-1) { 342 Parse_Error(PARSE_FATAL, 343 "Not enough words in for substitution list"); 344 } 345 done = 1; 346 break; 347 } else { 348 values[i] = (char *) Lst_Datum(ln); 349 } 350 } 351 if (done) 352 break; 353 354 for (i = 0; i < arg.nvars; i++) { 355 Var_Set(arg.vars[i], values[i], VAR_GLOBAL, 0); 356 if (DEBUG(FOR)) 357 (void) fprintf(stderr, "--- %s = %s\n", arg.vars[i], 358 values[i]); 359 } 360 361 /* 362 * Hack, hack, kludge. 363 * This is really ugly, but to do it any better way would require 364 * making major changes to var.c, which I don't want to get into 365 * yet. There is no mechanism for expanding some variables, only 366 * for expanding a single variable. That should be corrected, but 367 * not right away. (XXX) 368 */ 369 370 guy = (char *) Buf_GetAll(arg.buf, &len); 371 orig_guy = guy; 372 for (i = 0; i < arg.nvars; i++) { 373 old_guy = guy; 374 guy = Var_Subst(arg.vars[i], guy, VAR_GLOBAL, FALSE); 375 if (old_guy != orig_guy) 376 free(old_guy); 377 } 378 Parse_FromString(guy); 379 380 for (i = 0; i < arg.nvars; i++) 381 Var_Delete(arg.vars[i], VAR_GLOBAL); 382 } 383 384 free(values); 385 386 Lst_Close(arg.lst); 387 388 for (i=0; i<arg.nvars; i++) { 389 free(arg.vars[i]); 390 } 391 free(arg.vars); 392 393 Lst_Destroy(arg.lst, (void (*) __P((ClientData))) free); 394 Buf_Destroy(arg.buf, TRUE); 395 } 396