1 /* $NetBSD: for.c,v 1.13 2002/06/15 18:24:56 wiz 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.13 2002/06/15 18:24:56 wiz 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.13 2002/06/15 18:24:56 wiz 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 <assert.h> 60 #include <ctype.h> 61 62 #include "make.h" 63 #include "hash.h" 64 #include "dir.h" 65 #include "buf.h" 66 67 /* 68 * For statements are of the form: 69 * 70 * .for <variable> in <varlist> 71 * ... 72 * .endfor 73 * 74 * The trick is to look for the matching end inside for for loop 75 * To do that, we count the current nesting level of the for loops. 76 * and the .endfor statements, accumulating all the statements between 77 * the initial .for loop and the matching .endfor; 78 * then we evaluate the for loop for each variable in the varlist. 79 * 80 * Note that any nested fors are just passed through; they get handled 81 * recursively in For_Eval when we're expanding the enclosing for in 82 * For_Run. 83 */ 84 85 static int forLevel = 0; /* Nesting level */ 86 87 /* 88 * State of a for loop. 89 */ 90 typedef struct _For { 91 Buffer buf; /* Body of loop */ 92 char **vars; /* Iteration variables */ 93 int nvars; /* # of iteration vars */ 94 Lst lst; /* List of items */ 95 } For; 96 97 static For accumFor; /* Loop being accumulated */ 98 99 static void ForAddVar(const char *, size_t); 100 101 102 103 104 /*- 105 *----------------------------------------------------------------------- 106 * ForAddVar -- 107 * Add an iteration variable to the currently accumulating for. 108 * 109 * Results: none 110 * Side effects: no additional side effects. 111 *----------------------------------------------------------------------- 112 */ 113 static void 114 ForAddVar(const char *data, size_t len) 115 { 116 Buffer buf; 117 int varlen; 118 119 buf = Buf_Init(0); 120 Buf_AddBytes(buf, len, (Byte *) data); 121 122 accumFor.nvars++; 123 accumFor.vars = erealloc(accumFor.vars, accumFor.nvars*sizeof(char *)); 124 125 accumFor.vars[accumFor.nvars-1] = (char *) Buf_GetAll(buf, &varlen); 126 127 Buf_Destroy(buf, FALSE); 128 } 129 130 /*- 131 *----------------------------------------------------------------------- 132 * For_Eval -- 133 * Evaluate the for loop in the passed line. The line 134 * looks like this: 135 * .for <variable> in <varlist> 136 * 137 * Input: 138 * line Line to parse 139 * 140 * Results: 141 * TRUE: We found a for loop, or we are inside a for loop 142 * FALSE: We did not find a for loop, or we found the end of the for 143 * for loop. 144 * 145 * Side Effects: 146 * None. 147 * 148 *----------------------------------------------------------------------- 149 */ 150 int 151 For_Eval(char *line) 152 { 153 char *ptr = line, *sub, *in, *wrd; 154 int level; /* Level at which to report errors. */ 155 156 level = PARSE_FATAL; 157 158 159 if (forLevel == 0) { 160 Buffer buf; 161 int varlen; 162 static const char instr[] = "in"; 163 164 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) 165 continue; 166 /* 167 * If we are not in a for loop quickly determine if the statement is 168 * a for. 169 */ 170 if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || 171 !isspace((unsigned char) ptr[3])) 172 return FALSE; 173 ptr += 3; 174 175 /* 176 * we found a for loop, and now we are going to parse it. 177 */ 178 while (*ptr && isspace((unsigned char) *ptr)) 179 ptr++; 180 181 /* 182 * Find the "in". 183 */ 184 for (in = ptr; *in; in++) { 185 if (isspace((unsigned char) in[0]) && in[1]== 'i' && 186 in[2] == 'n' && 187 (in[3] == '\0' || isspace((unsigned char) in[3]))) 188 break; 189 } 190 if (*in == '\0') { 191 Parse_Error(level, "missing `in' in for"); 192 return 0; 193 } 194 195 /* 196 * Grab the variables. 197 */ 198 accumFor.vars = NULL; 199 200 while (ptr < in) { 201 wrd = ptr; 202 while (*ptr && !isspace((unsigned char) *ptr)) 203 ptr++; 204 ForAddVar(wrd, ptr - wrd); 205 while (*ptr && isspace((unsigned char) *ptr)) 206 ptr++; 207 } 208 209 if (accumFor.nvars == 0) { 210 Parse_Error(level, "no iteration variables in for"); 211 return 0; 212 } 213 214 /* At this point we should be pointing right at the "in" */ 215 /* 216 * compensate for hp/ux's brain damaged assert macro that 217 * does not handle double quotes nicely. 218 */ 219 assert(!memcmp(ptr, instr, 2)); 220 ptr += 2; 221 222 while (*ptr && isspace((unsigned char) *ptr)) 223 ptr++; 224 225 /* 226 * Make a list with the remaining words 227 */ 228 accumFor.lst = Lst_Init(FALSE); 229 buf = Buf_Init(0); 230 sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE); 231 232 #define ADDWORD() \ 233 Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \ 234 Buf_AddByte(buf, (Byte) '\0'), \ 235 Lst_AtFront(accumFor.lst, (ClientData) Buf_GetAll(buf, &varlen)), \ 236 Buf_Destroy(buf, FALSE) 237 238 for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++) 239 continue; 240 241 for (wrd = ptr; *ptr; ptr++) 242 if (isspace((unsigned char) *ptr)) { 243 ADDWORD(); 244 buf = Buf_Init(0); 245 while (*ptr && isspace((unsigned char) *ptr)) 246 ptr++; 247 wrd = ptr--; 248 } 249 if (DEBUG(FOR)) { 250 int i; 251 for (i = 0; i < accumFor.nvars; i++) { 252 (void) fprintf(stderr, "For: variable %s\n", accumFor.vars[i]); 253 } 254 (void) fprintf(stderr, "For: list %s\n", sub); 255 } 256 if (ptr - wrd > 0) 257 ADDWORD(); 258 else 259 Buf_Destroy(buf, TRUE); 260 free((Address) sub); 261 262 accumFor.buf = Buf_Init(0); 263 forLevel++; 264 return 1; 265 } 266 else if (*ptr == '.') { 267 268 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) 269 continue; 270 271 if (strncmp(ptr, "endfor", 6) == 0 && 272 (isspace((unsigned char) ptr[6]) || !ptr[6])) { 273 if (DEBUG(FOR)) 274 (void) fprintf(stderr, "For: end for %d\n", forLevel); 275 if (--forLevel < 0) { 276 Parse_Error (level, "for-less endfor"); 277 return 0; 278 } 279 } 280 else if (strncmp(ptr, "for", 3) == 0 && 281 isspace((unsigned char) ptr[3])) { 282 forLevel++; 283 if (DEBUG(FOR)) 284 (void) fprintf(stderr, "For: new loop %d\n", forLevel); 285 } 286 } 287 288 if (forLevel != 0) { 289 Buf_AddBytes(accumFor.buf, strlen(line), (Byte *) line); 290 Buf_AddByte(accumFor.buf, (Byte) '\n'); 291 return 1; 292 } 293 else { 294 return 0; 295 } 296 } 297 298 299 /*- 300 *----------------------------------------------------------------------- 301 * For_Run -- 302 * Run the for loop, imitating the actions of an include file 303 * 304 * Results: 305 * None. 306 * 307 * Side Effects: 308 * None. 309 * 310 *----------------------------------------------------------------------- 311 */ 312 void 313 For_Run(void) 314 { 315 For arg; 316 LstNode ln; 317 char **values; 318 int i, done = 0, len; 319 char *guy, *orig_guy, *old_guy; 320 321 if (accumFor.buf == NULL || accumFor.vars == NULL || accumFor.lst == NULL) 322 return; 323 arg = accumFor; 324 accumFor.buf = NULL; 325 accumFor.vars = NULL; 326 accumFor.nvars = 0; 327 accumFor.lst = NULL; 328 329 if (Lst_Open(arg.lst) != SUCCESS) 330 return; 331 332 values = emalloc(arg.nvars * sizeof(char *)); 333 334 while (!done) { 335 /* 336 * due to the dumb way this is set up, this loop must run 337 * backwards. 338 */ 339 for (i = arg.nvars - 1; i >= 0; i--) { 340 ln = Lst_Next(arg.lst); 341 if (ln == NILLNODE) { 342 if (i != arg.nvars-1) { 343 Parse_Error(PARSE_FATAL, 344 "Not enough words in for substitution list"); 345 } 346 done = 1; 347 break; 348 } else { 349 values[i] = (char *) Lst_Datum(ln); 350 } 351 } 352 if (done) 353 break; 354 355 for (i = 0; i < arg.nvars; i++) { 356 Var_Set(arg.vars[i], values[i], VAR_GLOBAL, 0); 357 if (DEBUG(FOR)) 358 (void) fprintf(stderr, "--- %s = %s\n", arg.vars[i], 359 values[i]); 360 } 361 362 /* 363 * Hack, hack, kludge. 364 * This is really ugly, but to do it any better way would require 365 * making major changes to var.c, which I don't want to get into 366 * yet. There is no mechanism for expanding some variables, only 367 * for expanding a single variable. That should be corrected, but 368 * not right away. (XXX) 369 */ 370 371 guy = (char *) Buf_GetAll(arg.buf, &len); 372 orig_guy = guy; 373 for (i = 0; i < arg.nvars; i++) { 374 old_guy = guy; 375 guy = Var_Subst(arg.vars[i], guy, VAR_GLOBAL, FALSE); 376 if (old_guy != orig_guy) 377 free(old_guy); 378 } 379 Parse_FromString(guy); 380 381 for (i = 0; i < arg.nvars; i++) 382 Var_Delete(arg.vars[i], VAR_GLOBAL); 383 } 384 385 free(values); 386 387 Lst_Close(arg.lst); 388 389 for (i=0; i<arg.nvars; i++) { 390 free(arg.vars[i]); 391 } 392 free(arg.vars); 393 394 Lst_Destroy(arg.lst, (void (*)(ClientData)) free); 395 Buf_Destroy(arg.buf, TRUE); 396 } 397