f5761f4f65d11e6338a18dc8385baaf83aa3ba27
[dragonfly.git] / usr.bin / make / for.c
1 /*
2  * Copyright (c) 1992, The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)for.c    8.1 (Berkeley) 6/6/93
34  * $FreeBSD: src/usr.bin/make/for.c,v 1.10 1999/09/11 13:08:01 hoek Exp $
35  * $DragonFly: src/usr.bin/make/for.c,v 1.2 2003/06/17 04:29:28 dillon Exp $
36  */
37
38 /*-
39  * for.c --
40  *      Functions to handle loops in a makefile.
41  *
42  * Interface:
43  *      For_Eval        Evaluate the loop in the passed line.
44  *      For_Run         Run accumulated loop
45  *
46  */
47
48 #include    <ctype.h>
49 #include    "make.h"
50 #include    "hash.h"
51 #include    "dir.h"
52 #include    "buf.h"
53
54 /*
55  * For statements are of the form:
56  *
57  * .for <variable> in <varlist>
58  * ...
59  * .endfor
60  *
61  * The trick is to look for the matching end inside for for loop
62  * To do that, we count the current nesting level of the for loops.
63  * and the .endfor statements, accumulating all the statements between
64  * the initial .for loop and the matching .endfor;
65  * then we evaluate the for loop for each variable in the varlist.
66  */
67
68 static int        forLevel = 0;         /* Nesting level        */
69 static char      *forVar;               /* Iteration variable   */
70 static Buffer     forBuf;               /* Commands in loop     */
71 static Lst        forLst;               /* List of items        */
72
73 /*
74  * State of a for loop.
75  */
76 typedef struct _For {
77     Buffer        buf;                  /* Unexpanded buffer    */
78     char*         var;                  /* Index name           */
79     Lst           lst;                  /* List of variables    */
80 } For;
81
82 static int ForExec      __P((ClientData, ClientData));
83
84
85
86 \f
87 /*-
88  *-----------------------------------------------------------------------
89  * For_Eval --
90  *      Evaluate the for loop in the passed line. The line
91  *      looks like this:
92  *          .for <variable> in <varlist>
93  *
94  * Results:
95  *      TRUE: We found a for loop, or we are inside a for loop
96  *      FALSE: We did not find a for loop, or we found the end of the for
97  *             for loop.
98  *
99  * Side Effects:
100  *      None.
101  *
102  *-----------------------------------------------------------------------
103  */
104 int
105 For_Eval (line)
106     char            *line;    /* Line to parse */
107 {
108     char            *ptr = line, *sub, *wrd;
109     int             level;      /* Level at which to report errors. */
110
111     level = PARSE_FATAL;
112
113
114     if (forLevel == 0) {
115         Buffer      buf;
116         int         varlen;
117
118         for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
119             continue;
120         /*
121          * If we are not in a for loop quickly determine if the statement is
122          * a for.
123          */
124         if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
125             !isspace((unsigned char) ptr[3]))
126             return FALSE;
127         ptr += 3;
128
129         /*
130          * we found a for loop, and now we are going to parse it.
131          */
132         while (*ptr && isspace((unsigned char) *ptr))
133             ptr++;
134
135         /*
136          * Grab the variable
137          */
138         buf = Buf_Init(0);
139         for (wrd = ptr; *ptr && !isspace((unsigned char) *ptr); ptr++)
140             continue;
141         Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd);
142
143         forVar = (char *) Buf_GetAll(buf, &varlen);
144         if (varlen == 0) {
145             Parse_Error (level, "missing variable in for");
146             return 0;
147         }
148         Buf_Destroy(buf, FALSE);
149
150         while (*ptr && isspace((unsigned char) *ptr))
151             ptr++;
152
153         /*
154          * Grab the `in'
155          */
156         if (ptr[0] != 'i' || ptr[1] != 'n' ||
157             !isspace((unsigned char) ptr[2])) {
158             Parse_Error (level, "missing `in' in for");
159             printf("%s\n", ptr);
160             return 0;
161         }
162         ptr += 3;
163
164         while (*ptr && isspace((unsigned char) *ptr))
165             ptr++;
166
167         /*
168          * Make a list with the remaining words
169          */
170         forLst = Lst_Init(FALSE);
171         buf = Buf_Init(0);
172         sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
173
174 #define ADDWORD() \
175         Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \
176         Buf_AddByte(buf, (Byte) '\0'), \
177         Lst_AtFront(forLst, (ClientData) Buf_GetAll(buf, &varlen)), \
178         Buf_Destroy(buf, FALSE)
179
180         for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++)
181             continue;
182
183         for (wrd = ptr; *ptr; ptr++)
184             if (isspace((unsigned char) *ptr)) {
185                 ADDWORD();
186                 buf = Buf_Init(0);
187                 while (*ptr && isspace((unsigned char) *ptr))
188                     ptr++;
189                 wrd = ptr--;
190             }
191         if (DEBUG(FOR))
192             (void) fprintf(stderr, "For: Iterator %s List %s\n", forVar, sub);
193         if (ptr - wrd > 0)
194             ADDWORD();
195         else
196             Buf_Destroy(buf, TRUE);
197         free((Address) sub);
198
199         forBuf = Buf_Init(0);
200         forLevel++;
201         return 1;
202     }
203     else if (*ptr == '.') {
204
205         for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
206             continue;
207
208         if (strncmp(ptr, "endfor", 6) == 0 &&
209             (isspace((unsigned char) ptr[6]) || !ptr[6])) {
210             if (DEBUG(FOR))
211                 (void) fprintf(stderr, "For: end for %d\n", forLevel);
212             if (--forLevel < 0) {
213                 Parse_Error (level, "for-less endfor");
214                 return 0;
215             }
216         }
217         else if (strncmp(ptr, "for", 3) == 0 &&
218                  isspace((unsigned char) ptr[3])) {
219             forLevel++;
220             if (DEBUG(FOR))
221                 (void) fprintf(stderr, "For: new loop %d\n", forLevel);
222         }
223     }
224
225     if (forLevel != 0) {
226         Buf_AddBytes(forBuf, strlen(line), (Byte *) line);
227         Buf_AddByte(forBuf, (Byte) '\n');
228         return 1;
229     }
230     else {
231         return 0;
232     }
233 }
234
235 /*-
236  *-----------------------------------------------------------------------
237  * ForExec --
238  *      Expand the for loop for this index and push it in the Makefile
239  *
240  * Results:
241  *      None.
242  *
243  * Side Effects:
244  *      None.
245  *
246  *-----------------------------------------------------------------------
247  */
248 static int
249 ForExec(namep, argp)
250     ClientData namep;
251     ClientData argp;
252 {
253     char *name = (char *) namep;
254     For *arg = (For *) argp;
255     int len;
256     Var_Set(arg->var, name, VAR_GLOBAL);
257     if (DEBUG(FOR))
258         (void) fprintf(stderr, "--- %s = %s\n", arg->var, name);
259     Parse_FromString(Var_Subst(arg->var, (char *) Buf_GetAll(arg->buf, &len),
260                                VAR_GLOBAL, FALSE));
261     Var_Delete(arg->var, VAR_GLOBAL);
262
263     return 0;
264 }
265
266 \f
267 /*-
268  *-----------------------------------------------------------------------
269  * For_Run --
270  *      Run the for loop, immitating the actions of an include file
271  *
272  * Results:
273  *      None.
274  *
275  * Side Effects:
276  *      None.
277  *
278  *-----------------------------------------------------------------------
279  */
280 void
281 For_Run()
282 {
283     For arg;
284
285     if (forVar == NULL || forBuf == NULL || forLst == NULL)
286         return;
287     arg.var = forVar;
288     arg.buf = forBuf;
289     arg.lst = forLst;
290     forVar = NULL;
291     forBuf = NULL;
292     forLst = NULL;
293
294     Lst_ForEach(arg.lst, ForExec, (ClientData) &arg);
295
296     free((Address)arg.var);
297     Lst_Destroy(arg.lst, (void (*) __P((ClientData))) free);
298     Buf_Destroy(arg.buf, TRUE);
299 }