1 /* $NetBSD: for.c,v 1.42 2009/01/10 16:59:02 dsl 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.42 2009/01/10 16:59:02 dsl 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.42 2009/01/10 16:59:02 dsl 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 #include "strlist.h" 63 64 #define FOR_SUB_ESCAPE_COLON 1 65 #define FOR_SUB_ESCAPE_BRACE 2 66 #define FOR_SUB_ESCAPE_PAREN 4 67 68 /* 69 * For statements are of the form: 70 * 71 * .for <variable> in <varlist> 72 * ... 73 * .endfor 74 * 75 * The trick is to look for the matching end inside for for loop 76 * To do that, we count the current nesting level of the for loops. 77 * and the .endfor statements, accumulating all the statements between 78 * the initial .for loop and the matching .endfor; 79 * then we evaluate the for loop for each variable in the varlist. 80 * 81 * Note that any nested fors are just passed through; they get handled 82 * recursively in For_Eval when we're expanding the enclosing for in 83 * For_Run. 84 */ 85 86 static int forLevel = 0; /* Nesting level */ 87 88 /* 89 * State of a for loop. 90 */ 91 typedef struct _For { 92 Buffer buf; /* Body of loop */ 93 strlist_t vars; /* Iteration variables */ 94 strlist_t items; /* Substitution items */ 95 } For; 96 97 static For accumFor; /* Loop being accumulated */ 98 99 100 101 static char * 102 make_str(const char *ptr, int len) 103 { 104 char *new_ptr; 105 106 new_ptr = bmake_malloc(len + 1); 107 memcpy(new_ptr, ptr, len); 108 new_ptr[len] = 0; 109 return new_ptr; 110 } 111 112 /*- 113 *----------------------------------------------------------------------- 114 * For_Eval -- 115 * Evaluate the for loop in the passed line. The line 116 * looks like this: 117 * .for <variable> in <varlist> 118 * 119 * Input: 120 * line Line to parse 121 * 122 * Results: 123 * 0: Not a .for statement, parse the line 124 * 1: We found a for loop 125 * -1: A .for statement with a bad syntax error, discard. 126 * 127 * Side Effects: 128 * None. 129 * 130 *----------------------------------------------------------------------- 131 */ 132 int 133 For_Eval(char *line) 134 { 135 char *ptr = line, *sub; 136 int len; 137 int escapes; 138 unsigned char ch; 139 140 /* Forget anything we previously knew about - it cannot be useful */ 141 memset(&accumFor, 0, sizeof accumFor); 142 143 forLevel = 0; 144 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) 145 continue; 146 /* 147 * If we are not in a for loop quickly determine if the statement is 148 * a for. 149 */ 150 if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || 151 !isspace((unsigned char) ptr[3])) { 152 if (ptr[0] == 'e' && strncmp(ptr+1, "ndfor", 5) == 0) { 153 Parse_Error(PARSE_FATAL, "for-less endfor"); 154 return -1; 155 } 156 return 0; 157 } 158 ptr += 3; 159 160 /* 161 * we found a for loop, and now we are going to parse it. 162 */ 163 164 /* Grab the variables. Terminate on "in". */ 165 for (;; ptr += len) { 166 while (*ptr && isspace((unsigned char) *ptr)) 167 ptr++; 168 if (*ptr == '\0') { 169 Parse_Error(PARSE_FATAL, "missing `in' in for"); 170 return -1; 171 } 172 for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++) 173 continue; 174 if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') { 175 ptr += 2; 176 break; 177 } 178 strlist_add_str(&accumFor.vars, make_str(ptr, len), len); 179 } 180 181 if (strlist_num(&accumFor.vars) == 0) { 182 Parse_Error(PARSE_FATAL, "no iteration variables in for"); 183 return -1; 184 } 185 186 while (*ptr && isspace((unsigned char) *ptr)) 187 ptr++; 188 189 /* 190 * Make a list with the remaining words 191 * The values are substituted as ${:U<value>...} so we must \ escape 192 * characters that break that syntax - particularly ':', maybe $ and \. 193 */ 194 sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE); 195 196 for (ptr = sub;; ptr += len) { 197 while (*ptr && isspace((unsigned char)*ptr)) 198 ptr++; 199 if (*ptr == 0) 200 break; 201 escapes = 0; 202 for (len = 0; (ch = ptr[len]) != 0 && !isspace(ch); len++) { 203 if (ch == ':') 204 escapes |= FOR_SUB_ESCAPE_COLON; 205 else if (ch == ')') 206 escapes |= FOR_SUB_ESCAPE_PAREN; 207 else if (ch == /*{*/ '}') 208 escapes |= FOR_SUB_ESCAPE_BRACE; 209 } 210 strlist_add_str(&accumFor.items, make_str(ptr, len), escapes); 211 } 212 213 free(sub); 214 215 if (strlist_num(&accumFor.items) % strlist_num(&accumFor.vars)) { 216 Parse_Error(PARSE_FATAL, 217 "Wrong number of words in .for substitution list %d %d", 218 strlist_num(&accumFor.items), strlist_num(&accumFor.vars)); 219 /* 220 * Return 'success' so that the body of the .for loop is accumulated. 221 * The loop will have zero iterations expanded due a later test. 222 */ 223 } 224 225 accumFor.buf = Buf_Init(0); 226 forLevel = 1; 227 return 1; 228 } 229 230 /* 231 * Add another line to a .for loop. 232 * Returns 0 when the matching .enfor is reached. 233 */ 234 235 int 236 For_Accum(char *line) 237 { 238 char *ptr = line; 239 240 if (*ptr == '.') { 241 242 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) 243 continue; 244 245 if (strncmp(ptr, "endfor", 6) == 0 && 246 (isspace((unsigned char) ptr[6]) || !ptr[6])) { 247 if (DEBUG(FOR)) 248 (void)fprintf(debug_file, "For: end for %d\n", forLevel); 249 if (--forLevel <= 0) 250 return 0; 251 } else if (strncmp(ptr, "for", 3) == 0 && 252 isspace((unsigned char) ptr[3])) { 253 forLevel++; 254 if (DEBUG(FOR)) 255 (void)fprintf(debug_file, "For: new loop %d\n", forLevel); 256 } 257 } 258 259 Buf_AddBytes(accumFor.buf, strlen(line), (Byte *)line); 260 Buf_AddByte(accumFor.buf, (Byte)'\n'); 261 return 1; 262 } 263 264 265 /*- 266 *----------------------------------------------------------------------- 267 * For_Run -- 268 * Run the for loop, imitating the actions of an include file 269 * 270 * Results: 271 * None. 272 * 273 * Side Effects: 274 * None. 275 * 276 *----------------------------------------------------------------------- 277 */ 278 279 static void 280 for_substitute(Buffer cmds, strlist_t *items, unsigned int item_no, char ech) 281 { 282 int depth, var_depth; 283 int escape; 284 const char *item = strlist_str(items, item_no); 285 int i; 286 char ch; 287 #define MAX_DEPTH 0x7fffffff 288 289 /* If there were no escapes, or the only escape is the other variable 290 * terminator, then just substitute the full string */ 291 if (!(strlist_info(items, item_no) & 292 (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) { 293 Buf_AddBytes(cmds, strlen(item), item); 294 return; 295 } 296 297 /* Escape ':' and 'ech' provided they aren't inside variable expansions */ 298 depth = 0; 299 var_depth = MAX_DEPTH; 300 escape = -1; 301 for (i = 0; (ch = item[i]) != 0; i++) { 302 /* Loose determination of nested variable definitions. */ 303 if (ch == '(' || ch == '{') { 304 depth++; 305 if (var_depth == MAX_DEPTH && i != 0 && item[i-1] == '$') 306 var_depth = depth; 307 } else if (ch == ')' || ch == '}') { 308 if (ch == ech && depth < var_depth) 309 escape = i; 310 if (depth == var_depth) 311 var_depth = MAX_DEPTH; 312 depth--; 313 } else if (ch == ':' && depth < var_depth) 314 escape = i; 315 if (escape == i) 316 Buf_AddByte(cmds, '\\'); 317 Buf_AddByte(cmds, ch); 318 } 319 320 if (escape == -1) { 321 /* We didn't actually need to escape anything, remember for next time */ 322 strlist_set_info(items, item_no, strlist_info(items, item_no) & 323 (ech == ')' ? ~FOR_SUB_ESCAPE_PAREN : ~FOR_SUB_ESCAPE_BRACE)); 324 } 325 } 326 327 void 328 For_Run(int lineno) 329 { 330 For arg; 331 int i, len; 332 unsigned int num_items; 333 char *for_body; 334 char *var; 335 char *cp; 336 char *cmd_cp; 337 char *body_end; 338 char ch; 339 Buffer cmds; 340 int short_var; 341 342 arg = accumFor; 343 memset(&accumFor, 0, sizeof accumFor); 344 345 num_items = strlist_num(&arg.items); 346 if (num_items % strlist_num(&arg.vars)) 347 /* Error message already printed */ 348 goto out; 349 350 short_var = 0; 351 STRLIST_FOREACH(var, &arg.vars, i) { 352 if (var[1] == 0) { 353 short_var = 1; 354 break; 355 } 356 } 357 358 /* 359 * Scan the for loop body and replace references to the loop variables 360 * with variable references that expand to the required text. 361 * Using variable expansions ensures that the .for loop can't generate 362 * syntax, and that the later parsing will still see a variable. 363 * We assume that the null variable will never be defined. 364 * 365 * The detection of substitions of the loop control variable is naive. 366 * Many of the modifiers use \ to escape $ (not $) so it is possible 367 * to contrive a makefile where an unwanted substitution happens. 368 * 369 * Each loop expansion is fed back into the parser as if it were an 370 * include file. This means we have to generate the last iteration first. 371 */ 372 while (num_items != 0) { 373 num_items -= strlist_num(&arg.vars); 374 for_body = (char *)Buf_GetAll(arg.buf, &len); 375 body_end = for_body + len; 376 cmds = Buf_Init(len + 256); 377 cmd_cp = for_body; 378 for (cp = for_body; (cp = strchr(cp, '$')) != NULL;) { 379 char ech; 380 ch = *++cp; 381 if ((ch == '(' && (ech = ')')) || (ch == '{' && (ech = '}'))) { 382 cp++; 383 /* Check variable name against the .for loop variables */ 384 STRLIST_FOREACH(var, &arg.vars, i) { 385 len = strlist_info(&arg.vars, i); 386 if (memcmp(cp, var, len) != 0) 387 continue; 388 if (cp[len] != ':' && cp[len] != ech && cp[len] != '\\') 389 continue; 390 /* Found a variable match. Replace with :U<value> */ 391 Buf_AddBytes(cmds, cp - cmd_cp, cmd_cp); 392 Buf_AddBytes(cmds, 2, ":U"); 393 cp += len; 394 cmd_cp = cp; 395 for_substitute(cmds, &arg.items, num_items + i, ech); 396 break; 397 } 398 continue; 399 } 400 if (ch == 0) 401 break; 402 /* Probably a single character name, ignore $$ and stupid ones. {*/ 403 if (!short_var || strchr("}):$", ch) != NULL) { 404 cp++; 405 continue; 406 } 407 STRLIST_FOREACH(var, &arg.vars, i) { 408 if (var[0] != ch || var[1] != 0) 409 continue; 410 /* Found a variable match. Replace with ${:U<value>} */ 411 Buf_AddBytes(cmds, cp - cmd_cp, cmd_cp); 412 Buf_AddBytes(cmds, 3, "{:U"); 413 cmd_cp = ++cp; 414 for_substitute(cmds, &arg.items, num_items + i, /*{*/ '}'); 415 Buf_AddBytes(cmds, 1, "}"); 416 break; 417 } 418 } 419 Buf_AddBytes(cmds, body_end - cmd_cp, cmd_cp); 420 421 cp = Buf_GetAll(cmds, NULL); 422 if (DEBUG(FOR)) 423 (void)fprintf(debug_file, "For: loop body:\n%s", cp); 424 Parse_SetInput(NULL, lineno, -1, cp); 425 Buf_Destroy(cmds, FALSE); 426 } 427 428 out: 429 strlist_clean(&arg.vars); 430 strlist_clean(&arg.items); 431 432 Buf_Destroy(arg.buf, TRUE); 433 } 434