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