xref: /freebsd/contrib/bmake/for.c (revision 06b9b3e0ad0dc3f0166b3e8f26ced68c271cf527)
1*06b9b3e0SSimon J. Gerraty /*	$NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $	*/
23955d011SMarcel Moolenaar 
33955d011SMarcel Moolenaar /*
43955d011SMarcel Moolenaar  * Copyright (c) 1992, The Regents of the University of California.
53955d011SMarcel Moolenaar  * All rights reserved.
63955d011SMarcel Moolenaar  *
73955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
83955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
93955d011SMarcel Moolenaar  * are met:
103955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
113955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
123955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
133955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
143955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
153955d011SMarcel Moolenaar  * 3. Neither the name of the University nor the names of its contributors
163955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
173955d011SMarcel Moolenaar  *    without specific prior written permission.
183955d011SMarcel Moolenaar  *
193955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
203955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
213955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
223955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
233955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
243955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
253955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
263955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
273955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
283955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
293955d011SMarcel Moolenaar  * SUCH DAMAGE.
303955d011SMarcel Moolenaar  */
313955d011SMarcel Moolenaar 
323955d011SMarcel Moolenaar /*-
33956e45f6SSimon J. Gerraty  * Handling of .for/.endfor loops in a makefile.
343955d011SMarcel Moolenaar  *
35*06b9b3e0SSimon J. Gerraty  * For loops have the form:
363955d011SMarcel Moolenaar  *
37956e45f6SSimon J. Gerraty  *	.for <varname...> in <value...>
38*06b9b3e0SSimon J. Gerraty  *	# the body
393955d011SMarcel Moolenaar  *	.endfor
403955d011SMarcel Moolenaar  *
41*06b9b3e0SSimon J. Gerraty  * When a .for line is parsed, the following lines are copied to the body of
42*06b9b3e0SSimon J. Gerraty  * the .for loop, until the corresponding .endfor line is reached.  In this
43*06b9b3e0SSimon J. Gerraty  * phase, the body is not yet evaluated.  This also applies to any nested
44*06b9b3e0SSimon J. Gerraty  * .for loops.
453955d011SMarcel Moolenaar  *
46*06b9b3e0SSimon J. Gerraty  * After reaching the .endfor, the values from the .for line are grouped
47*06b9b3e0SSimon J. Gerraty  * according to the number of variables.  For each such group, the unexpanded
48*06b9b3e0SSimon J. Gerraty  * body is scanned for variable expressions, and those that match the variable
49*06b9b3e0SSimon J. Gerraty  * names are replaced with expressions of the form ${:U...} or $(:U...).
50*06b9b3e0SSimon J. Gerraty  * After that, the body is treated like a file from an .include directive.
51956e45f6SSimon J. Gerraty  *
52956e45f6SSimon J. Gerraty  * Interface:
53956e45f6SSimon J. Gerraty  *	For_Eval	Evaluate the loop in the passed line.
54956e45f6SSimon J. Gerraty  *
55956e45f6SSimon J. Gerraty  *	For_Run		Run accumulated loop
563955d011SMarcel Moolenaar  */
573955d011SMarcel Moolenaar 
58956e45f6SSimon J. Gerraty #include "make.h"
59956e45f6SSimon J. Gerraty 
60956e45f6SSimon J. Gerraty /*	"@(#)for.c	8.1 (Berkeley) 6/6/93"	*/
61*06b9b3e0SSimon J. Gerraty MAKE_RCSID("$NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $");
62956e45f6SSimon J. Gerraty 
633955d011SMarcel Moolenaar static int forLevel = 0;	/* Nesting level */
643955d011SMarcel Moolenaar 
65956e45f6SSimon J. Gerraty /* One of the variables to the left of the "in" in a .for loop. */
66956e45f6SSimon J. Gerraty typedef struct ForVar {
67956e45f6SSimon J. Gerraty 	char *name;
68*06b9b3e0SSimon J. Gerraty 	size_t nameLen;
69956e45f6SSimon J. Gerraty } ForVar;
70956e45f6SSimon J. Gerraty 
713955d011SMarcel Moolenaar /*
723955d011SMarcel Moolenaar  * State of a for loop.
733955d011SMarcel Moolenaar  */
74956e45f6SSimon J. Gerraty typedef struct For {
75956e45f6SSimon J. Gerraty 	Buffer body;		/* Unexpanded body of the loop */
76956e45f6SSimon J. Gerraty 	Vector /* of ForVar */ vars; /* Iteration variables */
77956e45f6SSimon J. Gerraty 	Words items;		/* Substitution items */
78956e45f6SSimon J. Gerraty 	Buffer curBody;		/* Expanded body of the current iteration */
79956e45f6SSimon J. Gerraty 	/* Is any of the names 1 character long? If so, when the variable values
80956e45f6SSimon J. Gerraty 	 * are substituted, the parser must handle $V expressions as well, not
81956e45f6SSimon J. Gerraty 	 * only ${V} and $(V). */
82956e45f6SSimon J. Gerraty 	Boolean short_var;
83956e45f6SSimon J. Gerraty 	unsigned int sub_next;	/* Where to continue iterating */
843955d011SMarcel Moolenaar } For;
853955d011SMarcel Moolenaar 
863955d011SMarcel Moolenaar static For *accumFor;		/* Loop being accumulated */
873955d011SMarcel Moolenaar 
88956e45f6SSimon J. Gerraty static void
89956e45f6SSimon J. Gerraty ForAddVar(For *f, const char *name, size_t len)
90956e45f6SSimon J. Gerraty {
91956e45f6SSimon J. Gerraty 	ForVar *var = Vector_Push(&f->vars);
92956e45f6SSimon J. Gerraty 	var->name = bmake_strldup(name, len);
93*06b9b3e0SSimon J. Gerraty 	var->nameLen = len;
94956e45f6SSimon J. Gerraty }
953955d011SMarcel Moolenaar 
963955d011SMarcel Moolenaar static void
97956e45f6SSimon J. Gerraty For_Free(For *f)
983955d011SMarcel Moolenaar {
99956e45f6SSimon J. Gerraty 	Buf_Destroy(&f->body, TRUE);
1003955d011SMarcel Moolenaar 
101956e45f6SSimon J. Gerraty 	while (f->vars.len > 0) {
102956e45f6SSimon J. Gerraty 		ForVar *var = Vector_Pop(&f->vars);
103956e45f6SSimon J. Gerraty 		free(var->name);
104956e45f6SSimon J. Gerraty 	}
105956e45f6SSimon J. Gerraty 	Vector_Done(&f->vars);
106956e45f6SSimon J. Gerraty 
107956e45f6SSimon J. Gerraty 	Words_Free(f->items);
108956e45f6SSimon J. Gerraty 	Buf_Destroy(&f->curBody, TRUE);
109956e45f6SSimon J. Gerraty 
110956e45f6SSimon J. Gerraty 	free(f);
1113955d011SMarcel Moolenaar }
1123955d011SMarcel Moolenaar 
113956e45f6SSimon J. Gerraty static Boolean
114956e45f6SSimon J. Gerraty IsFor(const char *p)
115956e45f6SSimon J. Gerraty {
116956e45f6SSimon J. Gerraty 	return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]);
117956e45f6SSimon J. Gerraty }
1183955d011SMarcel Moolenaar 
119956e45f6SSimon J. Gerraty static Boolean
120956e45f6SSimon J. Gerraty IsEndfor(const char *p)
121956e45f6SSimon J. Gerraty {
122956e45f6SSimon J. Gerraty 	return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 &&
123956e45f6SSimon J. Gerraty 	       (p[6] == '\0' || ch_isspace(p[6]));
124956e45f6SSimon J. Gerraty }
125956e45f6SSimon J. Gerraty 
126*06b9b3e0SSimon J. Gerraty /*
127*06b9b3e0SSimon J. Gerraty  * Evaluate the for loop in the passed line. The line looks like this:
128956e45f6SSimon J. Gerraty  *	.for <varname...> in <value...>
129956e45f6SSimon J. Gerraty  *
130956e45f6SSimon J. Gerraty  * Input:
131956e45f6SSimon J. Gerraty  *	line		Line to parse
132956e45f6SSimon J. Gerraty  *
133956e45f6SSimon J. Gerraty  * Results:
134956e45f6SSimon J. Gerraty  *      0: Not a .for statement, parse the line
135956e45f6SSimon J. Gerraty  *	1: We found a for loop
136956e45f6SSimon J. Gerraty  *     -1: A .for statement with a bad syntax error, discard.
137956e45f6SSimon J. Gerraty  */
138956e45f6SSimon J. Gerraty int
139956e45f6SSimon J. Gerraty For_Eval(const char *line)
140956e45f6SSimon J. Gerraty {
141956e45f6SSimon J. Gerraty 	For *f;
142956e45f6SSimon J. Gerraty 	const char *p;
143956e45f6SSimon J. Gerraty 
144956e45f6SSimon J. Gerraty 	p = line + 1;		/* skip the '.' */
145956e45f6SSimon J. Gerraty 	cpp_skip_whitespace(&p);
146956e45f6SSimon J. Gerraty 
147956e45f6SSimon J. Gerraty 	if (!IsFor(p)) {
148956e45f6SSimon J. Gerraty 		if (IsEndfor(p)) {
149956e45f6SSimon J. Gerraty 			Parse_Error(PARSE_FATAL, "for-less endfor");
150956e45f6SSimon J. Gerraty 			return -1;
151956e45f6SSimon J. Gerraty 		}
152956e45f6SSimon J. Gerraty 		return 0;
153956e45f6SSimon J. Gerraty 	}
154956e45f6SSimon J. Gerraty 	p += 3;
155956e45f6SSimon J. Gerraty 
156956e45f6SSimon J. Gerraty 	/*
157956e45f6SSimon J. Gerraty 	 * we found a for loop, and now we are going to parse it.
158956e45f6SSimon J. Gerraty 	 */
159956e45f6SSimon J. Gerraty 
160956e45f6SSimon J. Gerraty 	f = bmake_malloc(sizeof *f);
161e2eeea75SSimon J. Gerraty 	Buf_Init(&f->body);
162956e45f6SSimon J. Gerraty 	Vector_Init(&f->vars, sizeof(ForVar));
163956e45f6SSimon J. Gerraty 	f->items.words = NULL;
164956e45f6SSimon J. Gerraty 	f->items.freeIt = NULL;
165e2eeea75SSimon J. Gerraty 	Buf_Init(&f->curBody);
166956e45f6SSimon J. Gerraty 	f->short_var = FALSE;
167956e45f6SSimon J. Gerraty 	f->sub_next = 0;
168956e45f6SSimon J. Gerraty 
169956e45f6SSimon J. Gerraty 	/* Grab the variables. Terminate on "in". */
170956e45f6SSimon J. Gerraty 	for (;;) {
171956e45f6SSimon J. Gerraty 		size_t len;
172956e45f6SSimon J. Gerraty 
173956e45f6SSimon J. Gerraty 		cpp_skip_whitespace(&p);
174956e45f6SSimon J. Gerraty 		if (*p == '\0') {
175956e45f6SSimon J. Gerraty 			Parse_Error(PARSE_FATAL, "missing `in' in for");
176956e45f6SSimon J. Gerraty 			For_Free(f);
177956e45f6SSimon J. Gerraty 			return -1;
178956e45f6SSimon J. Gerraty 		}
179956e45f6SSimon J. Gerraty 
180*06b9b3e0SSimon J. Gerraty 		/*
181*06b9b3e0SSimon J. Gerraty 		 * XXX: This allows arbitrary variable names;
182*06b9b3e0SSimon J. Gerraty 		 * see directive-for.mk.
183*06b9b3e0SSimon J. Gerraty 		 */
184956e45f6SSimon J. Gerraty 		for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++)
185956e45f6SSimon J. Gerraty 			continue;
186956e45f6SSimon J. Gerraty 
187956e45f6SSimon J. Gerraty 		if (len == 2 && p[0] == 'i' && p[1] == 'n') {
188956e45f6SSimon J. Gerraty 			p += 2;
189956e45f6SSimon J. Gerraty 			break;
190956e45f6SSimon J. Gerraty 		}
191956e45f6SSimon J. Gerraty 		if (len == 1)
192956e45f6SSimon J. Gerraty 			f->short_var = TRUE;
193956e45f6SSimon J. Gerraty 
194956e45f6SSimon J. Gerraty 		ForAddVar(f, p, len);
195956e45f6SSimon J. Gerraty 		p += len;
196956e45f6SSimon J. Gerraty 	}
197956e45f6SSimon J. Gerraty 
198956e45f6SSimon J. Gerraty 	if (f->vars.len == 0) {
199956e45f6SSimon J. Gerraty 		Parse_Error(PARSE_FATAL, "no iteration variables in for");
200956e45f6SSimon J. Gerraty 		For_Free(f);
201956e45f6SSimon J. Gerraty 		return -1;
202956e45f6SSimon J. Gerraty 	}
203956e45f6SSimon J. Gerraty 
204956e45f6SSimon J. Gerraty 	cpp_skip_whitespace(&p);
205956e45f6SSimon J. Gerraty 
206956e45f6SSimon J. Gerraty 	{
207956e45f6SSimon J. Gerraty 		char *items;
208*06b9b3e0SSimon J. Gerraty 		if (Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items) != VPR_OK) {
209*06b9b3e0SSimon J. Gerraty 			Parse_Error(PARSE_FATAL, "Error in .for loop items");
210*06b9b3e0SSimon J. Gerraty 			f->items.len = 0;
211*06b9b3e0SSimon J. Gerraty 			goto done;
212*06b9b3e0SSimon J. Gerraty 		}
213*06b9b3e0SSimon J. Gerraty 
214956e45f6SSimon J. Gerraty 		f->items = Str_Words(items, FALSE);
215956e45f6SSimon J. Gerraty 		free(items);
216956e45f6SSimon J. Gerraty 
217956e45f6SSimon J. Gerraty 		if (f->items.len == 1 && f->items.words[0][0] == '\0')
218956e45f6SSimon J. Gerraty 			f->items.len = 0; /* .for var in ${:U} */
219956e45f6SSimon J. Gerraty 	}
220956e45f6SSimon J. Gerraty 
221956e45f6SSimon J. Gerraty 	{
222956e45f6SSimon J. Gerraty 		size_t nitems, nvars;
223956e45f6SSimon J. Gerraty 
224*06b9b3e0SSimon J. Gerraty 		if ((nitems = f->items.len) > 0 &&
225*06b9b3e0SSimon J. Gerraty 		    nitems % (nvars = f->vars.len) != 0) {
2263955d011SMarcel Moolenaar 			Parse_Error(PARSE_FATAL,
227*06b9b3e0SSimon J. Gerraty 			    "Wrong number of words (%u) in .for "
228*06b9b3e0SSimon J. Gerraty 			    "substitution list with %u variables",
229*06b9b3e0SSimon J. Gerraty 			    (unsigned)nitems, (unsigned)nvars);
2303955d011SMarcel Moolenaar 			/*
231*06b9b3e0SSimon J. Gerraty 			 * Return 'success' so that the body of the .for loop
232*06b9b3e0SSimon J. Gerraty 			 * is accumulated.
2333955d011SMarcel Moolenaar 			 * Remove all items so that the loop doesn't iterate.
2343955d011SMarcel Moolenaar 			 */
235956e45f6SSimon J. Gerraty 			f->items.len = 0;
2363955d011SMarcel Moolenaar 		}
2373955d011SMarcel Moolenaar 	}
2383955d011SMarcel Moolenaar 
239*06b9b3e0SSimon J. Gerraty done:
240956e45f6SSimon J. Gerraty 	accumFor = f;
2413955d011SMarcel Moolenaar 	forLevel = 1;
2423955d011SMarcel Moolenaar 	return 1;
2433955d011SMarcel Moolenaar }
2443955d011SMarcel Moolenaar 
2453955d011SMarcel Moolenaar /*
2463955d011SMarcel Moolenaar  * Add another line to a .for loop.
247956e45f6SSimon J. Gerraty  * Returns FALSE when the matching .endfor is reached.
2483955d011SMarcel Moolenaar  */
249956e45f6SSimon J. Gerraty Boolean
250956e45f6SSimon J. Gerraty For_Accum(const char *line)
2513955d011SMarcel Moolenaar {
252956e45f6SSimon J. Gerraty 	const char *ptr = line;
2533955d011SMarcel Moolenaar 
2543955d011SMarcel Moolenaar 	if (*ptr == '.') {
255956e45f6SSimon J. Gerraty 		ptr++;
256956e45f6SSimon J. Gerraty 		cpp_skip_whitespace(&ptr);
2573955d011SMarcel Moolenaar 
258956e45f6SSimon J. Gerraty 		if (IsEndfor(ptr)) {
259956e45f6SSimon J. Gerraty 			DEBUG1(FOR, "For: end for %d\n", forLevel);
2603955d011SMarcel Moolenaar 			if (--forLevel <= 0)
261956e45f6SSimon J. Gerraty 				return FALSE;
262956e45f6SSimon J. Gerraty 		} else if (IsFor(ptr)) {
2633955d011SMarcel Moolenaar 			forLevel++;
264956e45f6SSimon J. Gerraty 			DEBUG1(FOR, "For: new loop %d\n", forLevel);
2653955d011SMarcel Moolenaar 		}
2663955d011SMarcel Moolenaar 	}
2673955d011SMarcel Moolenaar 
268956e45f6SSimon J. Gerraty 	Buf_AddStr(&accumFor->body, line);
269956e45f6SSimon J. Gerraty 	Buf_AddByte(&accumFor->body, '\n');
270956e45f6SSimon J. Gerraty 	return TRUE;
2713955d011SMarcel Moolenaar }
2723955d011SMarcel Moolenaar 
2733955d011SMarcel Moolenaar 
2742c3632d1SSimon J. Gerraty static size_t
2753955d011SMarcel Moolenaar for_var_len(const char *var)
2763955d011SMarcel Moolenaar {
2773955d011SMarcel Moolenaar 	char ch, var_start, var_end;
2783955d011SMarcel Moolenaar 	int depth;
2792c3632d1SSimon J. Gerraty 	size_t len;
2803955d011SMarcel Moolenaar 
2813955d011SMarcel Moolenaar 	var_start = *var;
282e2eeea75SSimon J. Gerraty 	if (var_start == '\0')
2833955d011SMarcel Moolenaar 		/* just escape the $ */
2843955d011SMarcel Moolenaar 		return 0;
2853955d011SMarcel Moolenaar 
2863955d011SMarcel Moolenaar 	if (var_start == '(')
2873955d011SMarcel Moolenaar 		var_end = ')';
2883955d011SMarcel Moolenaar 	else if (var_start == '{')
2893955d011SMarcel Moolenaar 		var_end = '}';
2903955d011SMarcel Moolenaar 	else
291*06b9b3e0SSimon J. Gerraty 		return 1;	/* Single char variable */
2923955d011SMarcel Moolenaar 
2933955d011SMarcel Moolenaar 	depth = 1;
294e2eeea75SSimon J. Gerraty 	for (len = 1; (ch = var[len++]) != '\0';) {
2953955d011SMarcel Moolenaar 		if (ch == var_start)
2963955d011SMarcel Moolenaar 			depth++;
2973955d011SMarcel Moolenaar 		else if (ch == var_end && --depth == 0)
2983955d011SMarcel Moolenaar 			return len;
2993955d011SMarcel Moolenaar 	}
3003955d011SMarcel Moolenaar 
3013955d011SMarcel Moolenaar 	/* Variable end not found, escape the $ */
3023955d011SMarcel Moolenaar 	return 0;
3033955d011SMarcel Moolenaar }
3043955d011SMarcel Moolenaar 
305*06b9b3e0SSimon J. Gerraty /*
306*06b9b3e0SSimon J. Gerraty  * The .for loop substitutes the items as ${:U<value>...}, which means
307*06b9b3e0SSimon J. Gerraty  * that characters that break this syntax must be backslash-escaped.
308*06b9b3e0SSimon J. Gerraty  */
309e2eeea75SSimon J. Gerraty static Boolean
310e2eeea75SSimon J. Gerraty NeedsEscapes(const char *word, char endc)
311e2eeea75SSimon J. Gerraty {
312e2eeea75SSimon J. Gerraty 	const char *p;
313e2eeea75SSimon J. Gerraty 
314e2eeea75SSimon J. Gerraty 	for (p = word; *p != '\0'; p++) {
315e2eeea75SSimon J. Gerraty 		if (*p == ':' || *p == '$' || *p == '\\' || *p == endc)
316e2eeea75SSimon J. Gerraty 			return TRUE;
317e2eeea75SSimon J. Gerraty 	}
318e2eeea75SSimon J. Gerraty 	return FALSE;
319e2eeea75SSimon J. Gerraty }
320e2eeea75SSimon J. Gerraty 
321*06b9b3e0SSimon J. Gerraty /*
322*06b9b3e0SSimon J. Gerraty  * While expanding the body of a .for loop, write the item in the ${:U...}
323e2eeea75SSimon J. Gerraty  * expression, escaping characters as needed.
324e2eeea75SSimon J. Gerraty  *
325*06b9b3e0SSimon J. Gerraty  * The result is later unescaped by ApplyModifier_Defined.
326*06b9b3e0SSimon J. Gerraty  */
3273955d011SMarcel Moolenaar static void
328*06b9b3e0SSimon J. Gerraty Buf_AddEscaped(Buffer *cmds, const char *item, char endc)
3293955d011SMarcel Moolenaar {
3303955d011SMarcel Moolenaar 	char ch;
3313955d011SMarcel Moolenaar 
332*06b9b3e0SSimon J. Gerraty 	if (!NeedsEscapes(item, endc)) {
3332c3632d1SSimon J. Gerraty 		Buf_AddStr(cmds, item);
3343955d011SMarcel Moolenaar 		return;
3353955d011SMarcel Moolenaar 	}
3363955d011SMarcel Moolenaar 
337*06b9b3e0SSimon J. Gerraty 	/* Escape ':', '$', '\\' and 'endc' - these will be removed later by
338956e45f6SSimon J. Gerraty 	 * :U processing, see ApplyModifier_Defined. */
339956e45f6SSimon J. Gerraty 	while ((ch = *item++) != '\0') {
3403955d011SMarcel Moolenaar 		if (ch == '$') {
3412c3632d1SSimon J. Gerraty 			size_t len = for_var_len(item);
3423955d011SMarcel Moolenaar 			if (len != 0) {
3432c3632d1SSimon J. Gerraty 				Buf_AddBytes(cmds, item - 1, len + 1);
3443955d011SMarcel Moolenaar 				item += len;
3453955d011SMarcel Moolenaar 				continue;
3463955d011SMarcel Moolenaar 			}
3473955d011SMarcel Moolenaar 			Buf_AddByte(cmds, '\\');
348*06b9b3e0SSimon J. Gerraty 		} else if (ch == ':' || ch == '\\' || ch == endc)
3493955d011SMarcel Moolenaar 			Buf_AddByte(cmds, '\\');
3503955d011SMarcel Moolenaar 		Buf_AddByte(cmds, ch);
3513955d011SMarcel Moolenaar 	}
3523955d011SMarcel Moolenaar }
3533955d011SMarcel Moolenaar 
354*06b9b3e0SSimon J. Gerraty /*
355*06b9b3e0SSimon J. Gerraty  * While expanding the body of a .for loop, replace the variable name of an
356*06b9b3e0SSimon J. Gerraty  * expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue".
357*06b9b3e0SSimon J. Gerraty  */
358956e45f6SSimon J. Gerraty static void
359*06b9b3e0SSimon J. Gerraty SubstVarLong(For *f, const char **pp, const char *bodyEnd, char endc,
360*06b9b3e0SSimon J. Gerraty 	     const char **inout_mark)
3613955d011SMarcel Moolenaar {
362956e45f6SSimon J. Gerraty 	size_t i;
363956e45f6SSimon J. Gerraty 	const char *p = *pp;
3643955d011SMarcel Moolenaar 
365956e45f6SSimon J. Gerraty 	for (i = 0; i < f->vars.len; i++) {
366956e45f6SSimon J. Gerraty 		ForVar *forVar = Vector_Get(&f->vars, i);
367*06b9b3e0SSimon J. Gerraty 		char *varname = forVar->name;
368*06b9b3e0SSimon J. Gerraty 		size_t varnameLen = forVar->nameLen;
369956e45f6SSimon J. Gerraty 
370*06b9b3e0SSimon J. Gerraty 		if (varnameLen >= (size_t)(bodyEnd - p))
371*06b9b3e0SSimon J. Gerraty 			continue;
372*06b9b3e0SSimon J. Gerraty 		if (memcmp(p, varname, varnameLen) != 0)
373956e45f6SSimon J. Gerraty 			continue;
374956e45f6SSimon J. Gerraty 		/* XXX: why test for backslash here? */
375*06b9b3e0SSimon J. Gerraty 		if (p[varnameLen] != ':' && p[varnameLen] != endc &&
376*06b9b3e0SSimon J. Gerraty 		    p[varnameLen] != '\\')
377956e45f6SSimon J. Gerraty 			continue;
378956e45f6SSimon J. Gerraty 
379*06b9b3e0SSimon J. Gerraty 		/*
380*06b9b3e0SSimon J. Gerraty 		 * Found a variable match.  Skip over the variable name and
381*06b9b3e0SSimon J. Gerraty 		 * instead add ':U<value>' to the current body.
382*06b9b3e0SSimon J. Gerraty 		 */
383956e45f6SSimon J. Gerraty 		Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
384956e45f6SSimon J. Gerraty 		Buf_AddStr(&f->curBody, ":U");
385*06b9b3e0SSimon J. Gerraty 		Buf_AddEscaped(&f->curBody,
386*06b9b3e0SSimon J. Gerraty 		    f->items.words[f->sub_next + i], endc);
387956e45f6SSimon J. Gerraty 
388*06b9b3e0SSimon J. Gerraty 		p += varnameLen;
389956e45f6SSimon J. Gerraty 		*inout_mark = p;
390956e45f6SSimon J. Gerraty 		*pp = p;
391956e45f6SSimon J. Gerraty 		return;
392956e45f6SSimon J. Gerraty 	}
393956e45f6SSimon J. Gerraty }
3943955d011SMarcel Moolenaar 
3953955d011SMarcel Moolenaar /*
396*06b9b3e0SSimon J. Gerraty  * While expanding the body of a .for loop, replace single-character
397*06b9b3e0SSimon J. Gerraty  * variable expressions like $i with their ${:U...} expansion.
398*06b9b3e0SSimon J. Gerraty  */
399*06b9b3e0SSimon J. Gerraty static void
400*06b9b3e0SSimon J. Gerraty SubstVarShort(For *f, const char *p, const char **inout_mark)
401*06b9b3e0SSimon J. Gerraty {
402*06b9b3e0SSimon J. Gerraty 	const char ch = *p;
403*06b9b3e0SSimon J. Gerraty 	ForVar *vars;
404*06b9b3e0SSimon J. Gerraty 	size_t i;
405*06b9b3e0SSimon J. Gerraty 
406*06b9b3e0SSimon J. Gerraty 	/* Skip $$ and stupid ones. */
407*06b9b3e0SSimon J. Gerraty 	if (!f->short_var || strchr("}):$", ch) != NULL)
408*06b9b3e0SSimon J. Gerraty 		return;
409*06b9b3e0SSimon J. Gerraty 
410*06b9b3e0SSimon J. Gerraty 	vars = Vector_Get(&f->vars, 0);
411*06b9b3e0SSimon J. Gerraty 	for (i = 0; i < f->vars.len; i++) {
412*06b9b3e0SSimon J. Gerraty 		const char *varname = vars[i].name;
413*06b9b3e0SSimon J. Gerraty 		if (varname[0] == ch && varname[1] == '\0')
414*06b9b3e0SSimon J. Gerraty 			goto found;
415*06b9b3e0SSimon J. Gerraty 	}
416*06b9b3e0SSimon J. Gerraty 	return;
417*06b9b3e0SSimon J. Gerraty 
418*06b9b3e0SSimon J. Gerraty found:
419*06b9b3e0SSimon J. Gerraty 	/* Replace $<ch> with ${:U<value>} */
420*06b9b3e0SSimon J. Gerraty 	Buf_AddBytesBetween(&f->curBody, *inout_mark, p), *inout_mark = p + 1;
421*06b9b3e0SSimon J. Gerraty 	Buf_AddStr(&f->curBody, "{:U");
422*06b9b3e0SSimon J. Gerraty 	Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], '}');
423*06b9b3e0SSimon J. Gerraty 	Buf_AddByte(&f->curBody, '}');
424*06b9b3e0SSimon J. Gerraty }
425*06b9b3e0SSimon J. Gerraty 
426*06b9b3e0SSimon J. Gerraty /*
427*06b9b3e0SSimon J. Gerraty  * Compute the body for the current iteration by copying the unexpanded body,
428*06b9b3e0SSimon J. Gerraty  * replacing the expressions for the iteration variables on the way.
429956e45f6SSimon J. Gerraty  *
430*06b9b3e0SSimon J. Gerraty  * Using variable expressions ensures that the .for loop can't generate
4313955d011SMarcel Moolenaar  * syntax, and that the later parsing will still see a variable.
432*06b9b3e0SSimon J. Gerraty  * This code assumes that the variable with the empty name will never be
433*06b9b3e0SSimon J. Gerraty  * defined, see unit-tests/varname-empty.mk for more details.
4343955d011SMarcel Moolenaar  *
435956e45f6SSimon J. Gerraty  * The detection of substitutions of the loop control variable is naive.
4363955d011SMarcel Moolenaar  * Many of the modifiers use \ to escape $ (not $) so it is possible
4373955d011SMarcel Moolenaar  * to contrive a makefile where an unwanted substitution happens.
4383955d011SMarcel Moolenaar  */
439*06b9b3e0SSimon J. Gerraty static void
440*06b9b3e0SSimon J. Gerraty ForSubstBody(For *f)
441*06b9b3e0SSimon J. Gerraty {
442*06b9b3e0SSimon J. Gerraty 	const char *p, *bodyEnd;
443*06b9b3e0SSimon J. Gerraty 	const char *mark;	/* where the last replacement left off */
444*06b9b3e0SSimon J. Gerraty 
445*06b9b3e0SSimon J. Gerraty 	Buf_Empty(&f->curBody);
446*06b9b3e0SSimon J. Gerraty 
447*06b9b3e0SSimon J. Gerraty 	mark = f->body.data;
448*06b9b3e0SSimon J. Gerraty 	bodyEnd = f->body.data + f->body.len;
449*06b9b3e0SSimon J. Gerraty 	for (p = mark; (p = strchr(p, '$')) != NULL;) {
450*06b9b3e0SSimon J. Gerraty 		if (p[1] == '{' || p[1] == '(') {
451*06b9b3e0SSimon J. Gerraty 			p += 2;
452*06b9b3e0SSimon J. Gerraty 			SubstVarLong(f, &p, bodyEnd, p[-1] == '{' ? '}' : ')',
453*06b9b3e0SSimon J. Gerraty 			    &mark);
454*06b9b3e0SSimon J. Gerraty 		} else if (p[1] != '\0') {
455*06b9b3e0SSimon J. Gerraty 			SubstVarShort(f, p + 1, &mark);
456*06b9b3e0SSimon J. Gerraty 			p += 2;
457*06b9b3e0SSimon J. Gerraty 		} else
458*06b9b3e0SSimon J. Gerraty 			break;
459*06b9b3e0SSimon J. Gerraty 	}
460*06b9b3e0SSimon J. Gerraty 
461*06b9b3e0SSimon J. Gerraty 	Buf_AddBytesBetween(&f->curBody, mark, bodyEnd);
462*06b9b3e0SSimon J. Gerraty }
463*06b9b3e0SSimon J. Gerraty 
464*06b9b3e0SSimon J. Gerraty /*
465*06b9b3e0SSimon J. Gerraty  * Compute the body for the current iteration by copying the unexpanded body,
466*06b9b3e0SSimon J. Gerraty  * replacing the expressions for the iteration variables on the way.
467*06b9b3e0SSimon J. Gerraty  */
468956e45f6SSimon J. Gerraty static char *
469*06b9b3e0SSimon J. Gerraty ForReadMore(void *v_arg, size_t *out_len)
470956e45f6SSimon J. Gerraty {
471956e45f6SSimon J. Gerraty 	For *f = v_arg;
4723955d011SMarcel Moolenaar 
473*06b9b3e0SSimon J. Gerraty 	if (f->sub_next == f->items.len) {
474956e45f6SSimon J. Gerraty 		/* No more iterations */
475956e45f6SSimon J. Gerraty 		For_Free(f);
476956e45f6SSimon J. Gerraty 		return NULL;
477956e45f6SSimon J. Gerraty 	}
478956e45f6SSimon J. Gerraty 
479*06b9b3e0SSimon J. Gerraty 	ForSubstBody(f);
480*06b9b3e0SSimon J. Gerraty 	DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data);
481*06b9b3e0SSimon J. Gerraty 	f->sub_next += (unsigned int)f->vars.len;
482956e45f6SSimon J. Gerraty 
483*06b9b3e0SSimon J. Gerraty 	*out_len = f->curBody.len;
484*06b9b3e0SSimon J. Gerraty 	return f->curBody.data;
4853955d011SMarcel Moolenaar }
4863955d011SMarcel Moolenaar 
487*06b9b3e0SSimon J. Gerraty /* Run the .for loop, imitating the actions of an include file. */
4883955d011SMarcel Moolenaar void
4893955d011SMarcel Moolenaar For_Run(int lineno)
4903955d011SMarcel Moolenaar {
491956e45f6SSimon J. Gerraty 	For *f = accumFor;
4923955d011SMarcel Moolenaar 	accumFor = NULL;
4933955d011SMarcel Moolenaar 
494956e45f6SSimon J. Gerraty 	if (f->items.len == 0) {
495*06b9b3e0SSimon J. Gerraty 		/*
496*06b9b3e0SSimon J. Gerraty 		 * Nothing to expand - possibly due to an earlier syntax
497*06b9b3e0SSimon J. Gerraty 		 * error.
498*06b9b3e0SSimon J. Gerraty 		 */
499956e45f6SSimon J. Gerraty 		For_Free(f);
5003955d011SMarcel Moolenaar 		return;
5013955d011SMarcel Moolenaar 	}
5023955d011SMarcel Moolenaar 
503*06b9b3e0SSimon J. Gerraty 	Parse_SetInput(NULL, lineno, -1, ForReadMore, f);
5043955d011SMarcel Moolenaar }
505