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