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