for.c revision 146027
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 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/usr.bin/make/for.c 146027 2005-05-09 14:06:04Z harti $");
37
38/*-
39 * for.c --
40 *	Functions to handle loops in a makefile.
41 *
42 * Interface:
43 *	For_Eval 	Evaluate the loop in the passed line.
44 *	For_Run		Run accumulated loop
45 *
46 */
47
48#include <ctype.h>
49#include <stdlib.h>
50#include <string.h>
51
52#include "buf.h"
53#include "dir.h"
54#include "for.h"
55#include "globals.h"
56#include "lst.h"
57#include "make.h"
58#include "parse.h"
59#include "util.h"
60#include "var.h"
61
62/*
63 * For statements are of the form:
64 *
65 * .for <variable> in <varlist>
66 * ...
67 * .endfor
68 *
69 * The trick is to look for the matching end inside for for loop
70 * To do that, we count the current nesting level of the for loops.
71 * and the .endfor statements, accumulating all the statements between
72 * the initial .for loop and the matching .endfor;
73 * then we evaluate the for loop for each variable in the varlist.
74 */
75
76static int	forLevel = 0;	/* Nesting level */
77static char	*forVar;	/* Iteration variable */
78static Buffer	*forBuf;	/* Commands in loop */
79static Lst	forLst;		/* List of items */
80
81/**
82 * For_For
83 *	Evaluate the for loop in the passed line. The line
84 *	looks like this:
85 *	    .for <variable> in <varlist>
86 *	The line pointer points just behind the for.
87 *
88 * Results:
89 *	TRUE: Syntax ok.
90 *	FALSE: Syntax error.
91 */
92Boolean
93For_For(char *line)
94{
95	char	*ptr;
96	char	*wrd;
97	char	*sub;
98	Buffer	*buf;
99	size_t	varlen;
100
101	ptr = line;
102
103	/*
104	 * Skip space between for and the variable.
105	 */
106	for (ptr++; *ptr && isspace((u_char)*ptr); ptr++)
107		;
108
109	/*
110	 * Grab the variable
111	 */
112	for (wrd = ptr; *ptr && !isspace((u_char)*ptr); ptr++)
113		;
114
115	buf = Buf_Init(0);
116	Buf_AppendRange(buf, wrd, ptr);
117	forVar = Buf_GetAll(buf, &varlen);
118
119	if (varlen == 0) {
120		Buf_Destroy(buf, TRUE);
121		Parse_Error(PARSE_FATAL, "missing variable in for");
122		return (FALSE);
123	}
124	Buf_Destroy(buf, FALSE);
125
126	/*
127	 * Skip to 'in'.
128	 */
129	while (*ptr && isspace((u_char)*ptr))
130		ptr++;
131
132	/*
133	 * Grab the `in'
134	 */
135	if (ptr[0] != 'i' || ptr[1] != 'n' || !isspace((u_char)ptr[2])) {
136		free(forVar);
137		Parse_Error(PARSE_FATAL, "missing `in' in for");
138		fprintf(stderr, "%s\n", ptr);
139		return (FALSE);
140	}
141	ptr += 3;
142
143	/*
144	 * Skip to values
145	 */
146	while (*ptr && isspace((u_char)*ptr))
147		ptr++;
148
149	/*
150	 * Make a list with the remaining words
151	 * XXX should use brk_string here.
152	 */
153	sub = Buf_Peel(Var_Subst(ptr, VAR_CMD, FALSE));
154	for (ptr = sub; *ptr != '\0' && isspace((u_char)*ptr); ptr++)
155		;
156
157	Lst_Init(&forLst);
158	buf = Buf_Init(0);
159	for (wrd = ptr; *ptr != '\0'; ptr++) {
160		if (isspace((u_char)*ptr)) {
161			Buf_AppendRange(buf, wrd, ptr);
162			Lst_AtFront(&forLst, Buf_Peel(buf));
163
164			buf = Buf_Init(0);
165			while (*ptr != '\0' && isspace((u_char)*ptr))
166				ptr++;
167			wrd = ptr--;
168		}
169	}
170	DEBUGF(FOR, ("For: Iterator %s List %s\n", forVar, sub));
171
172	if (ptr - wrd > 0) {
173		Buf_AppendRange(buf, wrd, ptr);
174		Lst_AtFront(&forLst, Buf_Peel(buf));
175	} else {
176		Buf_Destroy(buf, TRUE);
177	}
178	free(sub);
179
180	forBuf = Buf_Init(0);
181	forLevel++;
182	return (TRUE);
183}
184
185/**
186 * For_Eval
187 *	Eat a line of the .for body looking for embedded .for loops
188 *	and the .endfor
189 */
190Boolean
191For_Eval(char *line)
192{
193	char *ptr;
194
195	ptr = line;
196
197	if (*ptr == '.') {
198		/*
199		 * Need to check for 'endfor' and 'for' to find the end
200		 * of our loop or to find embedded for loops.
201		 */
202		for (ptr++; *ptr != '\0' && isspace((u_char)*ptr); ptr++)
203			;
204
205		/* XXX the isspace is wrong */
206		if (strncmp(ptr, "endfor", 6) == 0 &&
207		    (isspace((u_char)ptr[6]) || ptr[6] == '\0')) {
208			DEBUGF(FOR, ("For: end for %d\n", forLevel));
209			if (forLevel == 0) {
210				/* should not be here */
211				abort();
212			}
213			forLevel--;
214
215		} else if (strncmp(ptr, "for", 3) == 0 &&
216		    isspace((u_char)ptr[3])) {
217			forLevel++;
218			DEBUGF(FOR, ("For: new loop %d\n", forLevel));
219		}
220	}
221
222	if (forLevel != 0) {
223		/*
224		 * Still in loop - append the line
225		 */
226		Buf_Append(forBuf, line);
227		Buf_AddByte(forBuf, (Byte)'\n');
228		return (TRUE);
229	}
230
231	return (FALSE);
232}
233
234/*-
235 *-----------------------------------------------------------------------
236 * For_Run --
237 *	Run the for loop, immitating the actions of an include file
238 *
239 * Results:
240 *	None.
241 *
242 * Side Effects:
243 *	The values of the variables forLst, forVar and forBuf are freed.
244 *
245 *-----------------------------------------------------------------------
246 */
247void
248For_Run(int lineno)
249{
250	Lst		values;	/* list of values for the variable */
251	char		*var;	/* the variable's name */
252	Buffer		*buf;	/* the contents of the for loop */
253	const char	*val;	/* current value of loop variable */
254	LstNode		*ln;
255	char		*str;
256
257	if (forVar == NULL || forBuf == NULL)
258		return;
259
260	/* copy the global variables to have them free for embedded fors */
261	var = forVar;
262	buf = forBuf;
263	Lst_Init(&values);
264	Lst_Concat(&values, &forLst, LST_CONCLINK);
265
266	forVar = NULL;
267	forBuf = NULL;
268
269	LST_FOREACH(ln, &values) {
270		val = Lst_Datum(ln);
271		Var_Set(var, val, VAR_GLOBAL);
272
273		DEBUGF(FOR, ("--- %s = %s\n", var, val));
274		str = Buf_Peel(Var_SubstOnly(var, Buf_Data(buf),
275		    VAR_GLOBAL, FALSE));
276
277		Parse_FromString(str, lineno);
278		Var_Delete(var, VAR_GLOBAL);
279	}
280
281	free(var);
282	Lst_Destroy(&values, free);
283	Buf_Destroy(buf, TRUE);
284}
285