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