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