xref: /freebsd/contrib/bmake/for.c (revision dba7b0ef928af88caa38728a73657b837aeeac93)
1*dba7b0efSSimon J. Gerraty /*	$NetBSD: for.c,v 1.141 2021/02/04 21:33:13 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 
32*dba7b0efSSimon J. Gerraty /*
33956e45f6SSimon J. Gerraty  * Handling of .for/.endfor loops in a makefile.
343955d011SMarcel Moolenaar  *
3506b9b3e0SSimon J. Gerraty  * For loops have the form:
363955d011SMarcel Moolenaar  *
37956e45f6SSimon J. Gerraty  *	.for <varname...> in <value...>
3806b9b3e0SSimon J. Gerraty  *	# the body
393955d011SMarcel Moolenaar  *	.endfor
403955d011SMarcel Moolenaar  *
4106b9b3e0SSimon J. Gerraty  * When a .for line is parsed, the following lines are copied to the body of
4206b9b3e0SSimon J. Gerraty  * the .for loop, until the corresponding .endfor line is reached.  In this
4306b9b3e0SSimon J. Gerraty  * phase, the body is not yet evaluated.  This also applies to any nested
4406b9b3e0SSimon J. Gerraty  * .for loops.
453955d011SMarcel Moolenaar  *
4606b9b3e0SSimon J. Gerraty  * After reaching the .endfor, the values from the .for line are grouped
4706b9b3e0SSimon J. Gerraty  * according to the number of variables.  For each such group, the unexpanded
4806b9b3e0SSimon J. Gerraty  * body is scanned for variable expressions, and those that match the variable
4906b9b3e0SSimon J. Gerraty  * names are replaced with expressions of the form ${:U...} or $(:U...).
5006b9b3e0SSimon 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*dba7b0efSSimon J. Gerraty MAKE_RCSID("$NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $");
62956e45f6SSimon J. Gerraty 
633955d011SMarcel Moolenaar 
64956e45f6SSimon J. Gerraty /* One of the variables to the left of the "in" in a .for loop. */
65956e45f6SSimon J. Gerraty typedef struct ForVar {
66956e45f6SSimon J. Gerraty 	char *name;
6706b9b3e0SSimon J. Gerraty 	size_t nameLen;
68956e45f6SSimon J. Gerraty } ForVar;
69956e45f6SSimon J. Gerraty 
70*dba7b0efSSimon J. Gerraty typedef struct ForLoop {
71956e45f6SSimon J. Gerraty 	Buffer body;		/* Unexpanded body of the loop */
72956e45f6SSimon J. Gerraty 	Vector /* of ForVar */ vars; /* Iteration variables */
73956e45f6SSimon J. Gerraty 	Words items;		/* Substitution items */
74956e45f6SSimon J. Gerraty 	Buffer curBody;		/* Expanded body of the current iteration */
75956e45f6SSimon J. Gerraty 	/* Is any of the names 1 character long? If so, when the variable values
76956e45f6SSimon J. Gerraty 	 * are substituted, the parser must handle $V expressions as well, not
77956e45f6SSimon J. Gerraty 	 * only ${V} and $(V). */
78956e45f6SSimon J. Gerraty 	Boolean short_var;
79956e45f6SSimon J. Gerraty 	unsigned int sub_next;	/* Where to continue iterating */
80*dba7b0efSSimon J. Gerraty } ForLoop;
813955d011SMarcel Moolenaar 
823955d011SMarcel Moolenaar 
83*dba7b0efSSimon J. Gerraty static ForLoop *accumFor;		/* Loop being accumulated */
84*dba7b0efSSimon J. Gerraty static int forLevel = 0;	/* Nesting level */
85*dba7b0efSSimon J. Gerraty 
86*dba7b0efSSimon J. Gerraty 
87*dba7b0efSSimon J. Gerraty static ForLoop *
88*dba7b0efSSimon J. Gerraty ForLoop_New(void)
89956e45f6SSimon J. Gerraty {
90*dba7b0efSSimon J. Gerraty 	ForLoop *f = bmake_malloc(sizeof *f);
91*dba7b0efSSimon J. Gerraty 
92*dba7b0efSSimon J. Gerraty 	Buf_Init(&f->body);
93*dba7b0efSSimon J. Gerraty 	Vector_Init(&f->vars, sizeof(ForVar));
94*dba7b0efSSimon J. Gerraty 	f->items.words = NULL;
95*dba7b0efSSimon J. Gerraty 	f->items.freeIt = NULL;
96*dba7b0efSSimon J. Gerraty 	Buf_Init(&f->curBody);
97*dba7b0efSSimon J. Gerraty 	f->short_var = FALSE;
98*dba7b0efSSimon J. Gerraty 	f->sub_next = 0;
99*dba7b0efSSimon J. Gerraty 
100*dba7b0efSSimon J. Gerraty 	return f;
101956e45f6SSimon J. Gerraty }
1023955d011SMarcel Moolenaar 
1033955d011SMarcel Moolenaar static void
104*dba7b0efSSimon J. Gerraty ForLoop_Free(ForLoop *f)
1053955d011SMarcel Moolenaar {
106*dba7b0efSSimon J. Gerraty 	Buf_Done(&f->body);
1073955d011SMarcel Moolenaar 
108956e45f6SSimon J. Gerraty 	while (f->vars.len > 0) {
109956e45f6SSimon J. Gerraty 		ForVar *var = Vector_Pop(&f->vars);
110956e45f6SSimon J. Gerraty 		free(var->name);
111956e45f6SSimon J. Gerraty 	}
112956e45f6SSimon J. Gerraty 	Vector_Done(&f->vars);
113956e45f6SSimon J. Gerraty 
114956e45f6SSimon J. Gerraty 	Words_Free(f->items);
115*dba7b0efSSimon J. Gerraty 	Buf_Done(&f->curBody);
116956e45f6SSimon J. Gerraty 
117956e45f6SSimon J. Gerraty 	free(f);
1183955d011SMarcel Moolenaar }
1193955d011SMarcel Moolenaar 
120*dba7b0efSSimon J. Gerraty static void
121*dba7b0efSSimon J. Gerraty ForLoop_AddVar(ForLoop *f, const char *name, size_t len)
122*dba7b0efSSimon J. Gerraty {
123*dba7b0efSSimon J. Gerraty 	ForVar *var = Vector_Push(&f->vars);
124*dba7b0efSSimon J. Gerraty 	var->name = bmake_strldup(name, len);
125*dba7b0efSSimon J. Gerraty 	var->nameLen = len;
126*dba7b0efSSimon J. Gerraty }
127*dba7b0efSSimon J. Gerraty 
128*dba7b0efSSimon J. Gerraty static Boolean
129*dba7b0efSSimon J. Gerraty ForLoop_ParseVarnames(ForLoop *f, const char **pp)
130*dba7b0efSSimon J. Gerraty {
131*dba7b0efSSimon J. Gerraty 	const char *p = *pp;
132*dba7b0efSSimon J. Gerraty 
133*dba7b0efSSimon J. Gerraty 	for (;;) {
134*dba7b0efSSimon J. Gerraty 		size_t len;
135*dba7b0efSSimon J. Gerraty 
136*dba7b0efSSimon J. Gerraty 		cpp_skip_whitespace(&p);
137*dba7b0efSSimon J. Gerraty 		if (*p == '\0') {
138*dba7b0efSSimon J. Gerraty 			Parse_Error(PARSE_FATAL, "missing `in' in for");
139*dba7b0efSSimon J. Gerraty 			return FALSE;
140*dba7b0efSSimon J. Gerraty 		}
141*dba7b0efSSimon J. Gerraty 
142*dba7b0efSSimon J. Gerraty 		/*
143*dba7b0efSSimon J. Gerraty 		 * XXX: This allows arbitrary variable names;
144*dba7b0efSSimon J. Gerraty 		 * see directive-for.mk.
145*dba7b0efSSimon J. Gerraty 		 */
146*dba7b0efSSimon J. Gerraty 		for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++)
147*dba7b0efSSimon J. Gerraty 			continue;
148*dba7b0efSSimon J. Gerraty 
149*dba7b0efSSimon J. Gerraty 		if (len == 2 && p[0] == 'i' && p[1] == 'n') {
150*dba7b0efSSimon J. Gerraty 			p += 2;
151*dba7b0efSSimon J. Gerraty 			break;
152*dba7b0efSSimon J. Gerraty 		}
153*dba7b0efSSimon J. Gerraty 		if (len == 1)
154*dba7b0efSSimon J. Gerraty 			f->short_var = TRUE;
155*dba7b0efSSimon J. Gerraty 
156*dba7b0efSSimon J. Gerraty 		ForLoop_AddVar(f, p, len);
157*dba7b0efSSimon J. Gerraty 		p += len;
158*dba7b0efSSimon J. Gerraty 	}
159*dba7b0efSSimon J. Gerraty 
160*dba7b0efSSimon J. Gerraty 	if (f->vars.len == 0) {
161*dba7b0efSSimon J. Gerraty 		Parse_Error(PARSE_FATAL, "no iteration variables in for");
162*dba7b0efSSimon J. Gerraty 		return FALSE;
163*dba7b0efSSimon J. Gerraty 	}
164*dba7b0efSSimon J. Gerraty 
165*dba7b0efSSimon J. Gerraty 	*pp = p;
166*dba7b0efSSimon J. Gerraty 	return TRUE;
167*dba7b0efSSimon J. Gerraty }
168*dba7b0efSSimon J. Gerraty 
169*dba7b0efSSimon J. Gerraty static Boolean
170*dba7b0efSSimon J. Gerraty ForLoop_ParseItems(ForLoop *f, const char *p)
171*dba7b0efSSimon J. Gerraty {
172*dba7b0efSSimon J. Gerraty 	char *items;
173*dba7b0efSSimon J. Gerraty 
174*dba7b0efSSimon J. Gerraty 	cpp_skip_whitespace(&p);
175*dba7b0efSSimon J. Gerraty 
176*dba7b0efSSimon J. Gerraty 	if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) {
177*dba7b0efSSimon J. Gerraty 		Parse_Error(PARSE_FATAL, "Error in .for loop items");
178*dba7b0efSSimon J. Gerraty 		return FALSE;
179*dba7b0efSSimon J. Gerraty 	}
180*dba7b0efSSimon J. Gerraty 
181*dba7b0efSSimon J. Gerraty 	f->items = Str_Words(items, FALSE);
182*dba7b0efSSimon J. Gerraty 	free(items);
183*dba7b0efSSimon J. Gerraty 
184*dba7b0efSSimon J. Gerraty 	if (f->items.len == 1 && f->items.words[0][0] == '\0')
185*dba7b0efSSimon J. Gerraty 		f->items.len = 0; /* .for var in ${:U} */
186*dba7b0efSSimon J. Gerraty 
187*dba7b0efSSimon J. Gerraty 	if (f->items.len != 0 && f->items.len % f->vars.len != 0) {
188*dba7b0efSSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
189*dba7b0efSSimon J. Gerraty 		    "Wrong number of words (%u) in .for "
190*dba7b0efSSimon J. Gerraty 		    "substitution list with %u variables",
191*dba7b0efSSimon J. Gerraty 		    (unsigned)f->items.len, (unsigned)f->vars.len);
192*dba7b0efSSimon J. Gerraty 		return FALSE;
193*dba7b0efSSimon J. Gerraty 	}
194*dba7b0efSSimon J. Gerraty 
195*dba7b0efSSimon J. Gerraty 	return TRUE;
196*dba7b0efSSimon J. Gerraty }
197*dba7b0efSSimon J. Gerraty 
198956e45f6SSimon J. Gerraty static Boolean
199956e45f6SSimon J. Gerraty IsFor(const char *p)
200956e45f6SSimon J. Gerraty {
201956e45f6SSimon J. Gerraty 	return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]);
202956e45f6SSimon J. Gerraty }
2033955d011SMarcel Moolenaar 
204956e45f6SSimon J. Gerraty static Boolean
205956e45f6SSimon J. Gerraty IsEndfor(const char *p)
206956e45f6SSimon J. Gerraty {
207956e45f6SSimon J. Gerraty 	return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 &&
208956e45f6SSimon J. Gerraty 	       (p[6] == '\0' || ch_isspace(p[6]));
209956e45f6SSimon J. Gerraty }
210956e45f6SSimon J. Gerraty 
21106b9b3e0SSimon J. Gerraty /*
21206b9b3e0SSimon J. Gerraty  * Evaluate the for loop in the passed line. The line looks like this:
213956e45f6SSimon J. Gerraty  *	.for <varname...> in <value...>
214956e45f6SSimon J. Gerraty  *
215956e45f6SSimon J. Gerraty  * Input:
216956e45f6SSimon J. Gerraty  *	line		Line to parse
217956e45f6SSimon J. Gerraty  *
218956e45f6SSimon J. Gerraty  * Results:
219956e45f6SSimon J. Gerraty  *      0: Not a .for statement, parse the line
220956e45f6SSimon J. Gerraty  *	1: We found a for loop
221956e45f6SSimon J. Gerraty  *     -1: A .for statement with a bad syntax error, discard.
222956e45f6SSimon J. Gerraty  */
223956e45f6SSimon J. Gerraty int
224956e45f6SSimon J. Gerraty For_Eval(const char *line)
225956e45f6SSimon J. Gerraty {
226*dba7b0efSSimon J. Gerraty 	ForLoop *f;
227956e45f6SSimon J. Gerraty 	const char *p;
228956e45f6SSimon J. Gerraty 
229956e45f6SSimon J. Gerraty 	p = line + 1;		/* skip the '.' */
230956e45f6SSimon J. Gerraty 	cpp_skip_whitespace(&p);
231956e45f6SSimon J. Gerraty 
232956e45f6SSimon J. Gerraty 	if (!IsFor(p)) {
233956e45f6SSimon J. Gerraty 		if (IsEndfor(p)) {
234956e45f6SSimon J. Gerraty 			Parse_Error(PARSE_FATAL, "for-less endfor");
235956e45f6SSimon J. Gerraty 			return -1;
236956e45f6SSimon J. Gerraty 		}
237956e45f6SSimon J. Gerraty 		return 0;
238956e45f6SSimon J. Gerraty 	}
239956e45f6SSimon J. Gerraty 	p += 3;
240956e45f6SSimon J. Gerraty 
241*dba7b0efSSimon J. Gerraty 	f = ForLoop_New();
242956e45f6SSimon J. Gerraty 
243*dba7b0efSSimon J. Gerraty 	if (!ForLoop_ParseVarnames(f, &p)) {
244*dba7b0efSSimon J. Gerraty 		ForLoop_Free(f);
245956e45f6SSimon J. Gerraty 		return -1;
246956e45f6SSimon J. Gerraty 	}
247956e45f6SSimon J. Gerraty 
248*dba7b0efSSimon J. Gerraty 	if (!ForLoop_ParseItems(f, p)) {
249*dba7b0efSSimon J. Gerraty 		/* Continue parsing the .for loop, but don't iterate. */
250956e45f6SSimon J. Gerraty 		f->items.len = 0;
2513955d011SMarcel Moolenaar 	}
2523955d011SMarcel Moolenaar 
253956e45f6SSimon J. Gerraty 	accumFor = f;
2543955d011SMarcel Moolenaar 	forLevel = 1;
2553955d011SMarcel Moolenaar 	return 1;
2563955d011SMarcel Moolenaar }
2573955d011SMarcel Moolenaar 
2583955d011SMarcel Moolenaar /*
259*dba7b0efSSimon J. Gerraty  * Add another line to the .for loop that is being built up.
260956e45f6SSimon J. Gerraty  * Returns FALSE when the matching .endfor is reached.
2613955d011SMarcel Moolenaar  */
262956e45f6SSimon J. Gerraty Boolean
263956e45f6SSimon J. Gerraty For_Accum(const char *line)
2643955d011SMarcel Moolenaar {
265*dba7b0efSSimon J. Gerraty 	const char *p = line;
2663955d011SMarcel Moolenaar 
267*dba7b0efSSimon J. Gerraty 	if (*p == '.') {
268*dba7b0efSSimon J. Gerraty 		p++;
269*dba7b0efSSimon J. Gerraty 		cpp_skip_whitespace(&p);
2703955d011SMarcel Moolenaar 
271*dba7b0efSSimon J. Gerraty 		if (IsEndfor(p)) {
272956e45f6SSimon J. Gerraty 			DEBUG1(FOR, "For: end for %d\n", forLevel);
2733955d011SMarcel Moolenaar 			if (--forLevel <= 0)
274956e45f6SSimon J. Gerraty 				return FALSE;
275*dba7b0efSSimon J. Gerraty 		} else if (IsFor(p)) {
2763955d011SMarcel Moolenaar 			forLevel++;
277956e45f6SSimon J. Gerraty 			DEBUG1(FOR, "For: new loop %d\n", forLevel);
2783955d011SMarcel Moolenaar 		}
2793955d011SMarcel Moolenaar 	}
2803955d011SMarcel Moolenaar 
281956e45f6SSimon J. Gerraty 	Buf_AddStr(&accumFor->body, line);
282956e45f6SSimon J. Gerraty 	Buf_AddByte(&accumFor->body, '\n');
283956e45f6SSimon J. Gerraty 	return TRUE;
2843955d011SMarcel Moolenaar }
2853955d011SMarcel Moolenaar 
2863955d011SMarcel Moolenaar 
2872c3632d1SSimon J. Gerraty static size_t
2883955d011SMarcel Moolenaar for_var_len(const char *var)
2893955d011SMarcel Moolenaar {
2903955d011SMarcel Moolenaar 	char ch, var_start, var_end;
2913955d011SMarcel Moolenaar 	int depth;
2922c3632d1SSimon J. Gerraty 	size_t len;
2933955d011SMarcel Moolenaar 
2943955d011SMarcel Moolenaar 	var_start = *var;
295e2eeea75SSimon J. Gerraty 	if (var_start == '\0')
2963955d011SMarcel Moolenaar 		/* just escape the $ */
2973955d011SMarcel Moolenaar 		return 0;
2983955d011SMarcel Moolenaar 
2993955d011SMarcel Moolenaar 	if (var_start == '(')
3003955d011SMarcel Moolenaar 		var_end = ')';
3013955d011SMarcel Moolenaar 	else if (var_start == '{')
3023955d011SMarcel Moolenaar 		var_end = '}';
3033955d011SMarcel Moolenaar 	else
30406b9b3e0SSimon J. Gerraty 		return 1;	/* Single char variable */
3053955d011SMarcel Moolenaar 
3063955d011SMarcel Moolenaar 	depth = 1;
307e2eeea75SSimon J. Gerraty 	for (len = 1; (ch = var[len++]) != '\0';) {
3083955d011SMarcel Moolenaar 		if (ch == var_start)
3093955d011SMarcel Moolenaar 			depth++;
3103955d011SMarcel Moolenaar 		else if (ch == var_end && --depth == 0)
3113955d011SMarcel Moolenaar 			return len;
3123955d011SMarcel Moolenaar 	}
3133955d011SMarcel Moolenaar 
3143955d011SMarcel Moolenaar 	/* Variable end not found, escape the $ */
3153955d011SMarcel Moolenaar 	return 0;
3163955d011SMarcel Moolenaar }
3173955d011SMarcel Moolenaar 
31806b9b3e0SSimon J. Gerraty /*
31906b9b3e0SSimon J. Gerraty  * The .for loop substitutes the items as ${:U<value>...}, which means
32006b9b3e0SSimon J. Gerraty  * that characters that break this syntax must be backslash-escaped.
32106b9b3e0SSimon J. Gerraty  */
322e2eeea75SSimon J. Gerraty static Boolean
323*dba7b0efSSimon J. Gerraty NeedsEscapes(const char *value, char endc)
324e2eeea75SSimon J. Gerraty {
325e2eeea75SSimon J. Gerraty 	const char *p;
326e2eeea75SSimon J. Gerraty 
327*dba7b0efSSimon J. Gerraty 	for (p = value; *p != '\0'; p++) {
328e2eeea75SSimon J. Gerraty 		if (*p == ':' || *p == '$' || *p == '\\' || *p == endc)
329e2eeea75SSimon J. Gerraty 			return TRUE;
330e2eeea75SSimon J. Gerraty 	}
331e2eeea75SSimon J. Gerraty 	return FALSE;
332e2eeea75SSimon J. Gerraty }
333e2eeea75SSimon J. Gerraty 
33406b9b3e0SSimon J. Gerraty /*
33506b9b3e0SSimon J. Gerraty  * While expanding the body of a .for loop, write the item in the ${:U...}
336e2eeea75SSimon J. Gerraty  * expression, escaping characters as needed.
337e2eeea75SSimon J. Gerraty  *
33806b9b3e0SSimon J. Gerraty  * The result is later unescaped by ApplyModifier_Defined.
33906b9b3e0SSimon J. Gerraty  */
3403955d011SMarcel Moolenaar static void
34106b9b3e0SSimon J. Gerraty Buf_AddEscaped(Buffer *cmds, const char *item, char endc)
3423955d011SMarcel Moolenaar {
3433955d011SMarcel Moolenaar 	char ch;
3443955d011SMarcel Moolenaar 
34506b9b3e0SSimon J. Gerraty 	if (!NeedsEscapes(item, endc)) {
3462c3632d1SSimon J. Gerraty 		Buf_AddStr(cmds, item);
3473955d011SMarcel Moolenaar 		return;
3483955d011SMarcel Moolenaar 	}
3493955d011SMarcel Moolenaar 
35006b9b3e0SSimon J. Gerraty 	/* Escape ':', '$', '\\' and 'endc' - these will be removed later by
351956e45f6SSimon J. Gerraty 	 * :U processing, see ApplyModifier_Defined. */
352956e45f6SSimon J. Gerraty 	while ((ch = *item++) != '\0') {
3533955d011SMarcel Moolenaar 		if (ch == '$') {
3542c3632d1SSimon J. Gerraty 			size_t len = for_var_len(item);
3553955d011SMarcel Moolenaar 			if (len != 0) {
3562c3632d1SSimon J. Gerraty 				Buf_AddBytes(cmds, item - 1, len + 1);
3573955d011SMarcel Moolenaar 				item += len;
3583955d011SMarcel Moolenaar 				continue;
3593955d011SMarcel Moolenaar 			}
3603955d011SMarcel Moolenaar 			Buf_AddByte(cmds, '\\');
36106b9b3e0SSimon J. Gerraty 		} else if (ch == ':' || ch == '\\' || ch == endc)
3623955d011SMarcel Moolenaar 			Buf_AddByte(cmds, '\\');
3633955d011SMarcel Moolenaar 		Buf_AddByte(cmds, ch);
3643955d011SMarcel Moolenaar 	}
3653955d011SMarcel Moolenaar }
3663955d011SMarcel Moolenaar 
36706b9b3e0SSimon J. Gerraty /*
36806b9b3e0SSimon J. Gerraty  * While expanding the body of a .for loop, replace the variable name of an
36906b9b3e0SSimon J. Gerraty  * expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue".
37006b9b3e0SSimon J. Gerraty  */
371956e45f6SSimon J. Gerraty static void
372*dba7b0efSSimon J. Gerraty ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd,
373*dba7b0efSSimon J. Gerraty 		     char endc, const char **inout_mark)
3743955d011SMarcel Moolenaar {
375956e45f6SSimon J. Gerraty 	size_t i;
376956e45f6SSimon J. Gerraty 	const char *p = *pp;
3773955d011SMarcel Moolenaar 
378956e45f6SSimon J. Gerraty 	for (i = 0; i < f->vars.len; i++) {
379956e45f6SSimon J. Gerraty 		ForVar *forVar = Vector_Get(&f->vars, i);
38006b9b3e0SSimon J. Gerraty 		char *varname = forVar->name;
38106b9b3e0SSimon J. Gerraty 		size_t varnameLen = forVar->nameLen;
382956e45f6SSimon J. Gerraty 
38306b9b3e0SSimon J. Gerraty 		if (varnameLen >= (size_t)(bodyEnd - p))
38406b9b3e0SSimon J. Gerraty 			continue;
38506b9b3e0SSimon J. Gerraty 		if (memcmp(p, varname, varnameLen) != 0)
386956e45f6SSimon J. Gerraty 			continue;
387956e45f6SSimon J. Gerraty 		/* XXX: why test for backslash here? */
38806b9b3e0SSimon J. Gerraty 		if (p[varnameLen] != ':' && p[varnameLen] != endc &&
38906b9b3e0SSimon J. Gerraty 		    p[varnameLen] != '\\')
390956e45f6SSimon J. Gerraty 			continue;
391956e45f6SSimon J. Gerraty 
39206b9b3e0SSimon J. Gerraty 		/*
39306b9b3e0SSimon J. Gerraty 		 * Found a variable match.  Skip over the variable name and
39406b9b3e0SSimon J. Gerraty 		 * instead add ':U<value>' to the current body.
39506b9b3e0SSimon J. Gerraty 		 */
396956e45f6SSimon J. Gerraty 		Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
397956e45f6SSimon J. Gerraty 		Buf_AddStr(&f->curBody, ":U");
39806b9b3e0SSimon J. Gerraty 		Buf_AddEscaped(&f->curBody,
39906b9b3e0SSimon J. Gerraty 		    f->items.words[f->sub_next + i], endc);
400956e45f6SSimon J. Gerraty 
40106b9b3e0SSimon J. Gerraty 		p += varnameLen;
402956e45f6SSimon J. Gerraty 		*inout_mark = p;
403956e45f6SSimon J. Gerraty 		*pp = p;
404956e45f6SSimon J. Gerraty 		return;
405956e45f6SSimon J. Gerraty 	}
406956e45f6SSimon J. Gerraty }
4073955d011SMarcel Moolenaar 
4083955d011SMarcel Moolenaar /*
40906b9b3e0SSimon J. Gerraty  * While expanding the body of a .for loop, replace single-character
41006b9b3e0SSimon J. Gerraty  * variable expressions like $i with their ${:U...} expansion.
41106b9b3e0SSimon J. Gerraty  */
41206b9b3e0SSimon J. Gerraty static void
413*dba7b0efSSimon J. Gerraty ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark)
41406b9b3e0SSimon J. Gerraty {
41506b9b3e0SSimon J. Gerraty 	const char ch = *p;
41606b9b3e0SSimon J. Gerraty 	ForVar *vars;
41706b9b3e0SSimon J. Gerraty 	size_t i;
41806b9b3e0SSimon J. Gerraty 
41906b9b3e0SSimon J. Gerraty 	/* Skip $$ and stupid ones. */
42006b9b3e0SSimon J. Gerraty 	if (!f->short_var || strchr("}):$", ch) != NULL)
42106b9b3e0SSimon J. Gerraty 		return;
42206b9b3e0SSimon J. Gerraty 
42306b9b3e0SSimon J. Gerraty 	vars = Vector_Get(&f->vars, 0);
42406b9b3e0SSimon J. Gerraty 	for (i = 0; i < f->vars.len; i++) {
42506b9b3e0SSimon J. Gerraty 		const char *varname = vars[i].name;
42606b9b3e0SSimon J. Gerraty 		if (varname[0] == ch && varname[1] == '\0')
42706b9b3e0SSimon J. Gerraty 			goto found;
42806b9b3e0SSimon J. Gerraty 	}
42906b9b3e0SSimon J. Gerraty 	return;
43006b9b3e0SSimon J. Gerraty 
43106b9b3e0SSimon J. Gerraty found:
43206b9b3e0SSimon J. Gerraty 	/* Replace $<ch> with ${:U<value>} */
43306b9b3e0SSimon J. Gerraty 	Buf_AddBytesBetween(&f->curBody, *inout_mark, p), *inout_mark = p + 1;
43406b9b3e0SSimon J. Gerraty 	Buf_AddStr(&f->curBody, "{:U");
43506b9b3e0SSimon J. Gerraty 	Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], '}');
43606b9b3e0SSimon J. Gerraty 	Buf_AddByte(&f->curBody, '}');
43706b9b3e0SSimon J. Gerraty }
43806b9b3e0SSimon J. Gerraty 
43906b9b3e0SSimon J. Gerraty /*
44006b9b3e0SSimon J. Gerraty  * Compute the body for the current iteration by copying the unexpanded body,
44106b9b3e0SSimon J. Gerraty  * replacing the expressions for the iteration variables on the way.
442956e45f6SSimon J. Gerraty  *
44306b9b3e0SSimon J. Gerraty  * Using variable expressions ensures that the .for loop can't generate
4443955d011SMarcel Moolenaar  * syntax, and that the later parsing will still see a variable.
44506b9b3e0SSimon J. Gerraty  * This code assumes that the variable with the empty name will never be
44606b9b3e0SSimon J. Gerraty  * defined, see unit-tests/varname-empty.mk for more details.
4473955d011SMarcel Moolenaar  *
448*dba7b0efSSimon J. Gerraty  * The detection of substitutions of the loop control variables is naive.
449*dba7b0efSSimon J. Gerraty  * Many of the modifiers use '\' to escape '$' (not '$'), so it is possible
4503955d011SMarcel Moolenaar  * to contrive a makefile where an unwanted substitution happens.
4513955d011SMarcel Moolenaar  */
45206b9b3e0SSimon J. Gerraty static void
453*dba7b0efSSimon J. Gerraty ForLoop_SubstBody(ForLoop *f)
45406b9b3e0SSimon J. Gerraty {
45506b9b3e0SSimon J. Gerraty 	const char *p, *bodyEnd;
45606b9b3e0SSimon J. Gerraty 	const char *mark;	/* where the last replacement left off */
45706b9b3e0SSimon J. Gerraty 
45806b9b3e0SSimon J. Gerraty 	Buf_Empty(&f->curBody);
45906b9b3e0SSimon J. Gerraty 
46006b9b3e0SSimon J. Gerraty 	mark = f->body.data;
46106b9b3e0SSimon J. Gerraty 	bodyEnd = f->body.data + f->body.len;
46206b9b3e0SSimon J. Gerraty 	for (p = mark; (p = strchr(p, '$')) != NULL;) {
46306b9b3e0SSimon J. Gerraty 		if (p[1] == '{' || p[1] == '(') {
46406b9b3e0SSimon J. Gerraty 			p += 2;
465*dba7b0efSSimon J. Gerraty 			ForLoop_SubstVarLong(f, &p, bodyEnd,
466*dba7b0efSSimon J. Gerraty 			    p[-1] == '{' ? '}' : ')', &mark);
46706b9b3e0SSimon J. Gerraty 		} else if (p[1] != '\0') {
468*dba7b0efSSimon J. Gerraty 			ForLoop_SubstVarShort(f, p + 1, &mark);
46906b9b3e0SSimon J. Gerraty 			p += 2;
47006b9b3e0SSimon J. Gerraty 		} else
47106b9b3e0SSimon J. Gerraty 			break;
47206b9b3e0SSimon J. Gerraty 	}
47306b9b3e0SSimon J. Gerraty 
47406b9b3e0SSimon J. Gerraty 	Buf_AddBytesBetween(&f->curBody, mark, bodyEnd);
47506b9b3e0SSimon J. Gerraty }
47606b9b3e0SSimon J. Gerraty 
47706b9b3e0SSimon J. Gerraty /*
47806b9b3e0SSimon J. Gerraty  * Compute the body for the current iteration by copying the unexpanded body,
47906b9b3e0SSimon J. Gerraty  * replacing the expressions for the iteration variables on the way.
48006b9b3e0SSimon J. Gerraty  */
481956e45f6SSimon J. Gerraty static char *
48206b9b3e0SSimon J. Gerraty ForReadMore(void *v_arg, size_t *out_len)
483956e45f6SSimon J. Gerraty {
484*dba7b0efSSimon J. Gerraty 	ForLoop *f = v_arg;
4853955d011SMarcel Moolenaar 
48606b9b3e0SSimon J. Gerraty 	if (f->sub_next == f->items.len) {
487956e45f6SSimon J. Gerraty 		/* No more iterations */
488*dba7b0efSSimon J. Gerraty 		ForLoop_Free(f);
489956e45f6SSimon J. Gerraty 		return NULL;
490956e45f6SSimon J. Gerraty 	}
491956e45f6SSimon J. Gerraty 
492*dba7b0efSSimon J. Gerraty 	ForLoop_SubstBody(f);
49306b9b3e0SSimon J. Gerraty 	DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data);
49406b9b3e0SSimon J. Gerraty 	f->sub_next += (unsigned int)f->vars.len;
495956e45f6SSimon J. Gerraty 
49606b9b3e0SSimon J. Gerraty 	*out_len = f->curBody.len;
49706b9b3e0SSimon J. Gerraty 	return f->curBody.data;
4983955d011SMarcel Moolenaar }
4993955d011SMarcel Moolenaar 
50006b9b3e0SSimon J. Gerraty /* Run the .for loop, imitating the actions of an include file. */
5013955d011SMarcel Moolenaar void
5023955d011SMarcel Moolenaar For_Run(int lineno)
5033955d011SMarcel Moolenaar {
504*dba7b0efSSimon J. Gerraty 	ForLoop *f = accumFor;
5053955d011SMarcel Moolenaar 	accumFor = NULL;
5063955d011SMarcel Moolenaar 
507956e45f6SSimon J. Gerraty 	if (f->items.len == 0) {
50806b9b3e0SSimon J. Gerraty 		/*
50906b9b3e0SSimon J. Gerraty 		 * Nothing to expand - possibly due to an earlier syntax
51006b9b3e0SSimon J. Gerraty 		 * error.
51106b9b3e0SSimon J. Gerraty 		 */
512*dba7b0efSSimon J. Gerraty 		ForLoop_Free(f);
5133955d011SMarcel Moolenaar 		return;
5143955d011SMarcel Moolenaar 	}
5153955d011SMarcel Moolenaar 
51606b9b3e0SSimon J. Gerraty 	Parse_SetInput(NULL, lineno, -1, ForReadMore, f);
5173955d011SMarcel Moolenaar }
518