1 /* $NetBSD: for.c,v 1.4 1996/11/06 17:59:05 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 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93"; 39 #else 40 static char rcsid[] = "$NetBSD: for.c,v 1.4 1996/11/06 17:59:05 christos Exp $"; 41 #endif 42 #endif /* not lint */ 43 44 /*- 45 * for.c -- 46 * Functions to handle loops in a makefile. 47 * 48 * Interface: 49 * For_Eval Evaluate the loop in the passed line. 50 * For_Run Run accumulated loop 51 * 52 */ 53 54 #include <ctype.h> 55 #include "make.h" 56 #include "hash.h" 57 #include "dir.h" 58 #include "buf.h" 59 60 /* 61 * For statements are of the form: 62 * 63 * .for <variable> in <varlist> 64 * ... 65 * .endfor 66 * 67 * The trick is to look for the matching end inside for for loop 68 * To do that, we count the current nesting level of the for loops. 69 * and the .endfor statements, accumulating all the statements between 70 * the initial .for loop and the matching .endfor; 71 * then we evaluate the for loop for each variable in the varlist. 72 */ 73 74 static int forLevel = 0; /* Nesting level */ 75 static char *forVar; /* Iteration variable */ 76 static Buffer forBuf; /* Commands in loop */ 77 static Lst forLst; /* List of items */ 78 79 /* 80 * State of a for loop. 81 */ 82 typedef struct _For { 83 Buffer buf; /* Unexpanded buffer */ 84 char* var; /* Index name */ 85 Lst lst; /* List of variables */ 86 } For; 87 88 static int ForExec __P((ClientData, ClientData)); 89 90 91 92 93 /*- 94 *----------------------------------------------------------------------- 95 * For_Eval -- 96 * Evaluate the for loop in the passed line. The line 97 * looks like this: 98 * .for <variable> in <varlist> 99 * 100 * Results: 101 * TRUE: We found a for loop, or we are inside a for loop 102 * FALSE: We did not find a for loop, or we found the end of the for 103 * for loop. 104 * 105 * Side Effects: 106 * None. 107 * 108 *----------------------------------------------------------------------- 109 */ 110 int 111 For_Eval (line) 112 char *line; /* Line to parse */ 113 { 114 char *ptr = line, *sub, *wrd; 115 int level; /* Level at which to report errors. */ 116 117 level = PARSE_FATAL; 118 119 120 if (forLevel == 0) { 121 Buffer buf; 122 int varlen; 123 124 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) 125 continue; 126 /* 127 * If we are not in a for loop quickly determine if the statement is 128 * a for. 129 */ 130 if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || 131 !isspace((unsigned char) ptr[3])) 132 return FALSE; 133 ptr += 3; 134 135 /* 136 * we found a for loop, and now we are going to parse it. 137 */ 138 while (*ptr && isspace((unsigned char) *ptr)) 139 ptr++; 140 141 /* 142 * Grab the variable 143 */ 144 buf = Buf_Init(0); 145 for (wrd = ptr; *ptr && !isspace((unsigned char) *ptr); ptr++) 146 continue; 147 Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd); 148 149 forVar = (char *) Buf_GetAll(buf, &varlen); 150 if (varlen == 0) { 151 Parse_Error (level, "missing variable in for"); 152 return 0; 153 } 154 Buf_Destroy(buf, FALSE); 155 156 while (*ptr && isspace((unsigned char) *ptr)) 157 ptr++; 158 159 /* 160 * Grab the `in' 161 */ 162 if (ptr[0] != 'i' || ptr[1] != 'n' || 163 !isspace((unsigned char) ptr[2])) { 164 Parse_Error (level, "missing `in' in for"); 165 printf("%s\n", ptr); 166 return 0; 167 } 168 ptr += 3; 169 170 while (*ptr && isspace((unsigned char) *ptr)) 171 ptr++; 172 173 /* 174 * Make a list with the remaining words 175 */ 176 forLst = Lst_Init(FALSE); 177 buf = Buf_Init(0); 178 sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE); 179 180 #define ADDWORD() \ 181 Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \ 182 Buf_AddByte(buf, (Byte) '\0'), \ 183 Lst_AtFront(forLst, (ClientData) Buf_GetAll(buf, &varlen)), \ 184 Buf_Destroy(buf, FALSE) 185 186 for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++) 187 continue; 188 189 for (wrd = ptr; *ptr; ptr++) 190 if (isspace((unsigned char) *ptr)) { 191 ADDWORD(); 192 buf = Buf_Init(0); 193 while (*ptr && isspace((unsigned char) *ptr)) 194 ptr++; 195 wrd = ptr--; 196 } 197 if (DEBUG(FOR)) 198 (void) fprintf(stderr, "For: Iterator %s List %s\n", forVar, sub); 199 if (ptr - wrd > 0) 200 ADDWORD(); 201 else 202 Buf_Destroy(buf, TRUE); 203 free((Address) sub); 204 205 forBuf = Buf_Init(0); 206 forLevel++; 207 return 1; 208 } 209 else if (*ptr == '.') { 210 211 for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) 212 continue; 213 214 if (strncmp(ptr, "endfor", 6) == 0 && 215 (isspace((unsigned char) ptr[6]) || !ptr[6])) { 216 if (DEBUG(FOR)) 217 (void) fprintf(stderr, "For: end for %d\n", forLevel); 218 if (--forLevel < 0) { 219 Parse_Error (level, "for-less endfor"); 220 return 0; 221 } 222 } 223 else if (strncmp(ptr, "for", 3) == 0 && 224 isspace((unsigned char) ptr[3])) { 225 forLevel++; 226 if (DEBUG(FOR)) 227 (void) fprintf(stderr, "For: new loop %d\n", forLevel); 228 } 229 } 230 231 if (forLevel != 0) { 232 Buf_AddBytes(forBuf, strlen(line), (Byte *) line); 233 Buf_AddByte(forBuf, (Byte) '\n'); 234 return 1; 235 } 236 else { 237 return 0; 238 } 239 } 240 241 /*- 242 *----------------------------------------------------------------------- 243 * ForExec -- 244 * Expand the for loop for this index and push it in the Makefile 245 * 246 * Results: 247 * None. 248 * 249 * Side Effects: 250 * None. 251 * 252 *----------------------------------------------------------------------- 253 */ 254 static int 255 ForExec(namep, argp) 256 ClientData namep; 257 ClientData argp; 258 { 259 char *name = (char *) namep; 260 For *arg = (For *) argp; 261 int len; 262 Var_Set(arg->var, name, VAR_GLOBAL); 263 if (DEBUG(FOR)) 264 (void) fprintf(stderr, "--- %s = %s\n", arg->var, name); 265 Parse_FromString(Var_Subst(arg->var, (char *) Buf_GetAll(arg->buf, &len), 266 VAR_GLOBAL, FALSE)); 267 Var_Delete(arg->var, VAR_GLOBAL); 268 269 return 0; 270 } 271 272 273 /*- 274 *----------------------------------------------------------------------- 275 * For_Run -- 276 * Run the for loop, immitating the actions of an include file 277 * 278 * Results: 279 * None. 280 * 281 * Side Effects: 282 * None. 283 * 284 *----------------------------------------------------------------------- 285 */ 286 void 287 For_Run() 288 { 289 For arg; 290 291 if (forVar == NULL || forBuf == NULL || forLst == NULL) 292 return; 293 arg.var = forVar; 294 arg.buf = forBuf; 295 arg.lst = forLst; 296 forVar = NULL; 297 forBuf = NULL; 298 forLst = NULL; 299 300 Lst_ForEach(arg.lst, ForExec, (ClientData) &arg); 301 302 free((Address)arg.var); 303 Lst_Destroy(arg.lst, (void (*) __P((ClientData))) free); 304 Buf_Destroy(arg.buf, TRUE); 305 } 306