Do some deregisterization.
[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.5 2004/11/12 22:02:51 dillon 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 } For;
84
85 static int ForExec(void *, void *);
86
87
88
89 \f
90 /*-
91  *-----------------------------------------------------------------------
92  * For_Eval --
93  *      Evaluate the for loop in the passed line. The line
94  *      looks like this:
95  *          .for <variable> in <varlist>
96  *
97  * Results:
98  *      TRUE: We found a for loop, or we are inside a for loop
99  *      FALSE: We did not find a for loop, or we found the end of the for
100  *             for loop.
101  *
102  * Side Effects:
103  *      None.
104  *
105  *-----------------------------------------------------------------------
106  */
107 int
108 For_Eval (line)
109     char            *line;    /* Line to parse */
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_GLOBAL, 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         if (DEBUG(FOR))
195             (void) fprintf(stderr, "For: Iterator %s List %s\n", forVar, sub);
196         if (ptr - wrd > 0)
197             ADDWORD();
198         else
199             Buf_Destroy(buf, TRUE);
200         free(sub);
201
202         forBuf = Buf_Init(0);
203         forLevel++;
204         return 1;
205     }
206     else if (*ptr == '.') {
207
208         for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
209             continue;
210
211         if (strncmp(ptr, "endfor", 6) == 0 &&
212             (isspace((unsigned char) ptr[6]) || !ptr[6])) {
213             if (DEBUG(FOR))
214                 (void) fprintf(stderr, "For: end for %d\n", forLevel);
215             if (--forLevel < 0) {
216                 Parse_Error (level, "for-less endfor");
217                 return 0;
218             }
219         }
220         else if (strncmp(ptr, "for", 3) == 0 &&
221                  isspace((unsigned char) ptr[3])) {
222             forLevel++;
223             if (DEBUG(FOR))
224                 (void) fprintf(stderr, "For: new loop %d\n", forLevel);
225         }
226     }
227
228     if (forLevel != 0) {
229         Buf_AddBytes(forBuf, strlen(line), (Byte *) line);
230         Buf_AddByte(forBuf, (Byte) '\n');
231         return 1;
232     }
233     else {
234         return 0;
235     }
236 }
237
238 /*-
239  *-----------------------------------------------------------------------
240  * ForExec --
241  *      Expand the for loop for this index and push it in the Makefile
242  *
243  * Results:
244  *      None.
245  *
246  * Side Effects:
247  *      None.
248  *
249  *-----------------------------------------------------------------------
250  */
251 static int
252 ForExec(namep, argp)
253     void * namep;
254     void * argp;
255 {
256     char *name = (char *) namep;
257     For *arg = (For *) argp;
258     int len;
259     Var_Set(arg->var, name, VAR_GLOBAL);
260     if (DEBUG(FOR))
261         (void) fprintf(stderr, "--- %s = %s\n", arg->var, name);
262     Parse_FromString(Var_Subst(arg->var, (char *) Buf_GetAll(arg->buf, &len),
263                                VAR_GLOBAL, FALSE));
264     Var_Delete(arg->var, VAR_GLOBAL);
265
266     return 0;
267 }
268
269 \f
270 /*-
271  *-----------------------------------------------------------------------
272  * For_Run --
273  *      Run the for loop, immitating the actions of an include file
274  *
275  * Results:
276  *      None.
277  *
278  * Side Effects:
279  *      None.
280  *
281  *-----------------------------------------------------------------------
282  */
283 void
284 For_Run()
285 {
286     For arg;
287
288     if (forVar == NULL || forBuf == NULL || forLst == NULL)
289         return;
290     arg.var = forVar;
291     arg.buf = forBuf;
292     arg.lst = forLst;
293     forVar = NULL;
294     forBuf = NULL;
295     forLst = NULL;
296
297     Lst_ForEach(arg.lst, ForExec, (void *) &arg);
298
299     free(arg.var);
300     Lst_Destroy(arg.lst, (void (*)(void *)) free);
301     Buf_Destroy(arg.buf, TRUE);
302 }