Move ProcExec() into proc.c
[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.41 2005/05/05 09:08:42 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  * For_For
82  *      Evaluate the for loop in the passed line. The line
83  *      looks like this:
84  *          .for <variable> in <varlist>
85  *      The line pointer points just behind the for.
86  *
87  * Results:
88  *      TRUE: Syntax ok.
89  *      FALSE: Syntax error.
90  */
91 Boolean
92 For_For(char *line)
93 {
94         char    *ptr;
95         char    *wrd;
96         char    *sub;
97         Buffer  *buf;
98         size_t  varlen;
99
100         ptr = line;
101
102         /*
103          * Skip space between for and the variable.
104          */
105         for (ptr++; *ptr && isspace((u_char)*ptr); ptr++)
106                 ;
107
108         /*
109          * Grab the variable
110          */
111         for (wrd = ptr; *ptr && !isspace((u_char)*ptr); ptr++)
112                 ;
113
114         buf = Buf_Init(0);
115         Buf_AppendRange(buf, wrd, ptr);
116         forVar = Buf_GetAll(buf, &varlen);
117
118         if (varlen == 0) {
119                 Buf_Destroy(buf, TRUE);
120                 Parse_Error(PARSE_FATAL, "missing variable in for");
121                 return (FALSE);
122         }
123         Buf_Destroy(buf, FALSE);
124
125         /*
126          * Skip to 'in'.
127          */
128         while (*ptr && isspace((u_char)*ptr))
129                 ptr++;
130
131         /*
132          * Grab the `in'
133          */
134         if (ptr[0] != 'i' || ptr[1] != 'n' || !isspace((u_char)ptr[2])) {
135                 free(forVar);
136                 Parse_Error(PARSE_FATAL, "missing `in' in for");
137                 fprintf(stderr, "%s\n", ptr);
138                 return (FALSE);
139         }
140         ptr += 3;
141
142         /*
143          * Skip to values
144          */
145         while (*ptr && isspace((u_char)*ptr))
146                 ptr++;
147
148         /*
149          * Make a list with the remaining words
150          */
151         sub = Buf_Peel(Var_Subst(ptr, VAR_CMD, FALSE));
152         for (ptr = sub; *ptr != '\0' && isspace((u_char)*ptr); ptr++)
153                 ;
154
155         Lst_Init(&forLst);
156         buf = Buf_Init(0);
157         for (wrd = ptr; *ptr != '\0'; ptr++) {
158                 if (isspace((u_char)*ptr)) {
159                         Buf_AppendRange(buf, wrd, ptr);
160                         Lst_AtFront(&forLst, Buf_Peel(buf));
161
162                         buf = Buf_Init(0);
163                         while (*ptr != '\0' && isspace((u_char)*ptr))
164                                 ptr++;
165                         wrd = ptr--;
166                 }
167         }
168         DEBUGF(FOR, ("For: Iterator %s List %s\n", forVar, sub));
169
170         if (ptr - wrd > 0) {
171                 Buf_AppendRange(buf, wrd, ptr);
172                 Lst_AtFront(&forLst, Buf_Peel(buf));
173         } else {
174                 Buf_Destroy(buf, TRUE);
175         }
176         free(sub);
177
178         forBuf = Buf_Init(0);
179         forLevel++;
180         return (TRUE);
181 }
182
183 /**
184  * For_Eval
185  *      Eat a line of the .for body looking for embedded .for loops
186  *      and the .endfor
187  */
188 Boolean
189 For_Eval(char *line)
190 {
191         char *ptr;
192
193         ptr = line;
194
195         if (*ptr == '.') {
196                 /*
197                  * Need to check for 'endfor' and 'for' to find the end
198                  * of our loop or to find embedded for loops.
199                  */
200                 for (ptr++; *ptr != '\0' && isspace((u_char)*ptr); ptr++)
201                         ;
202
203                 /* XXX the isspace is wrong */
204                 if (strncmp(ptr, "endfor", 6) == 0 &&
205                     (isspace((u_char)ptr[6]) || ptr[6] == '\0')) {
206                         DEBUGF(FOR, ("For: end for %d\n", forLevel));
207                         if (forLevel == 0) {
208                                 /* should not be here */
209                                 abort();
210                         }
211                         forLevel--;
212
213                 } else if (strncmp(ptr, "for", 3) == 0 &&
214                     isspace((u_char)ptr[3])) {
215                         forLevel++;
216                         DEBUGF(FOR, ("For: new loop %d\n", forLevel));
217                 }
218         }
219
220         if (forLevel != 0) {
221                 /*
222                  * Still in loop - append the line
223                  */
224                 Buf_Append(forBuf, line);
225                 Buf_AddByte(forBuf, (Byte)'\n');
226                 return (TRUE);
227         }
228
229         return (FALSE);
230 }
231
232 /*-
233  *-----------------------------------------------------------------------
234  * For_Run --
235  *      Run the for loop, immitating the actions of an include file
236  *
237  * Results:
238  *      None.
239  *
240  * Side Effects:
241  *      The values of the variables forLst, forVar and forBuf are freed.
242  *
243  *-----------------------------------------------------------------------
244  */
245 void
246 For_Run(int lineno)
247 {
248         Lst             values; /* list of values for the variable */
249         char            *var;   /* the variable's name */
250         Buffer          *buf;   /* the contents of the for loop */
251         const char      *val;   /* current value of loop variable */
252         LstNode         *ln;
253         char            *str;
254
255         if (forVar == NULL || forBuf == NULL)
256                 return;
257
258         /* copy the global variables to have them free for embedded fors */
259         var = forVar;
260         buf = forBuf;
261         Lst_Init(&values);
262         Lst_Concat(&values, &forLst, LST_CONCLINK);
263
264         forVar = NULL;
265         forBuf = NULL;
266
267         LST_FOREACH(ln, &values) {
268                 val = Lst_Datum(ln);
269                 Var_Set(var, val, VAR_GLOBAL);
270
271                 DEBUGF(FOR, ("--- %s = %s\n", var, val));
272                 str = Buf_Peel(Var_SubstOnly(var, Buf_Data(buf), FALSE));
273
274                 Parse_FromString(str, lineno);
275                 Var_Delete(var, VAR_GLOBAL);
276         }
277
278         free(var);
279         Lst_Destroy(&values, free);
280         Buf_Destroy(buf, TRUE);
281 }