xref: /freebsd/contrib/bmake/var.c (revision a8c56be47166295d37600ff81fc1857db87b3a9b)
1*a8c56be4SSimon J. Gerraty /*	$NetBSD: var.c,v 1.1171 2025/06/29 11:02:17 rillig Exp $	*/
23955d011SMarcel Moolenaar 
33955d011SMarcel Moolenaar /*
43955d011SMarcel Moolenaar  * Copyright (c) 1988, 1989, 1990, 1993
53955d011SMarcel Moolenaar  *	The Regents of the University of California.  All rights reserved.
63955d011SMarcel Moolenaar  *
73955d011SMarcel Moolenaar  * This code is derived from software contributed to Berkeley by
83955d011SMarcel Moolenaar  * Adam de Boor.
93955d011SMarcel Moolenaar  *
103955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
113955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
123955d011SMarcel Moolenaar  * are met:
133955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
143955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
153955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
163955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
173955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
183955d011SMarcel Moolenaar  * 3. Neither the name of the University nor the names of its contributors
193955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
203955d011SMarcel Moolenaar  *    without specific prior written permission.
213955d011SMarcel Moolenaar  *
223955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
233955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
243955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
253955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
263955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
273955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
283955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
293955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
303955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
313955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323955d011SMarcel Moolenaar  * SUCH DAMAGE.
333955d011SMarcel Moolenaar  */
343955d011SMarcel Moolenaar 
353955d011SMarcel Moolenaar /*
363955d011SMarcel Moolenaar  * Copyright (c) 1989 by Berkeley Softworks
373955d011SMarcel Moolenaar  * All rights reserved.
383955d011SMarcel Moolenaar  *
393955d011SMarcel Moolenaar  * This code is derived from software contributed to Berkeley by
403955d011SMarcel Moolenaar  * Adam de Boor.
413955d011SMarcel Moolenaar  *
423955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
433955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
443955d011SMarcel Moolenaar  * are met:
453955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
463955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
473955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
483955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
493955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
503955d011SMarcel Moolenaar  * 3. All advertising materials mentioning features or use of this software
513955d011SMarcel Moolenaar  *    must display the following acknowledgement:
523955d011SMarcel Moolenaar  *	This product includes software developed by the University of
533955d011SMarcel Moolenaar  *	California, Berkeley and its contributors.
543955d011SMarcel Moolenaar  * 4. Neither the name of the University nor the names of its contributors
553955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
563955d011SMarcel Moolenaar  *    without specific prior written permission.
573955d011SMarcel Moolenaar  *
583955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
593955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
603955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
613955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
623955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
633955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
643955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
653955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
663955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
673955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
683955d011SMarcel Moolenaar  * SUCH DAMAGE.
693955d011SMarcel Moolenaar  */
703955d011SMarcel Moolenaar 
71956e45f6SSimon J. Gerraty /*
72956e45f6SSimon J. Gerraty  * Handling of variables and the expressions formed from them.
73956e45f6SSimon J. Gerraty  *
74956e45f6SSimon J. Gerraty  * Variables are set using lines of the form VAR=value.  Both the variable
75956e45f6SSimon J. Gerraty  * name and the value can contain references to other variables, by using
76956e45f6SSimon J. Gerraty  * expressions like ${VAR}, ${VAR:Modifiers}, ${${VARNAME}} or ${VAR:${MODS}}.
773955d011SMarcel Moolenaar  *
783955d011SMarcel Moolenaar  * Interface:
79dba7b0efSSimon J. Gerraty  *	Var_Set
80548bfc56SSimon J. Gerraty  *	Var_SetExpand	Set the value of the variable, creating it if
81956e45f6SSimon J. Gerraty  *			necessary.
82956e45f6SSimon J. Gerraty  *
83dba7b0efSSimon J. Gerraty  *	Var_Append
84dba7b0efSSimon J. Gerraty  *	Var_AppendExpand
85dba7b0efSSimon J. Gerraty  *			Append more characters to the variable, creating it if
86956e45f6SSimon J. Gerraty  *			necessary. A space is placed between the old value and
87956e45f6SSimon J. Gerraty  *			the new one.
883955d011SMarcel Moolenaar  *
89dba7b0efSSimon J. Gerraty  *	Var_Exists
90dba7b0efSSimon J. Gerraty  *	Var_ExistsExpand
91dba7b0efSSimon J. Gerraty  *			See if a variable exists.
923955d011SMarcel Moolenaar  *
93956e45f6SSimon J. Gerraty  *	Var_Value	Return the unexpanded value of a variable, or NULL if
94956e45f6SSimon J. Gerraty  *			the variable is undefined.
953955d011SMarcel Moolenaar  *
96d5e0a182SSimon J. Gerraty  *	Var_Subst	Substitute all expressions in a string.
973955d011SMarcel Moolenaar  *
98d5e0a182SSimon J. Gerraty  *	Var_Parse	Parse an expression such as ${VAR:Mpattern}.
993955d011SMarcel Moolenaar  *
100548bfc56SSimon J. Gerraty  *	Var_Delete	Delete a variable.
1013955d011SMarcel Moolenaar  *
10206b9b3e0SSimon J. Gerraty  *	Var_ReexportVars
10306b9b3e0SSimon J. Gerraty  *			Export some or even all variables to the environment
104956e45f6SSimon J. Gerraty  *			of this process and its child processes.
105956e45f6SSimon J. Gerraty  *
106956e45f6SSimon J. Gerraty  *	Var_Export	Export the variable to the environment of this process
107956e45f6SSimon J. Gerraty  *			and its child processes.
108956e45f6SSimon J. Gerraty  *
109956e45f6SSimon J. Gerraty  *	Var_UnExport	Don't export the variable anymore.
1103955d011SMarcel Moolenaar  *
1113955d011SMarcel Moolenaar  * Debugging:
112956e45f6SSimon J. Gerraty  *	Var_Stats	Print out hashing statistics if in -dh mode.
113956e45f6SSimon J. Gerraty  *
114dba7b0efSSimon J. Gerraty  *	Var_Dump	Print out all variables defined in the given scope.
1153955d011SMarcel Moolenaar  */
1163955d011SMarcel Moolenaar 
1173955d011SMarcel Moolenaar #include <sys/stat.h>
1189093286bSSimon J. Gerraty #include <sys/types.h>
1193955d011SMarcel Moolenaar 
1203955d011SMarcel Moolenaar #include "make.h"
1213841c287SSimon J. Gerraty 
122956e45f6SSimon J. Gerraty #include <errno.h>
123c59c3bf3SSimon J. Gerraty #ifdef HAVE_REGEX_H
124c59c3bf3SSimon J. Gerraty #include <regex.h>
125c59c3bf3SSimon J. Gerraty #endif
1262c3632d1SSimon J. Gerraty #ifdef HAVE_INTTYPES_H
1272c3632d1SSimon J. Gerraty #include <inttypes.h>
128c59c3bf3SSimon J. Gerraty #endif
129c59c3bf3SSimon J. Gerraty #ifdef HAVE_STDINT_H
1303841c287SSimon J. Gerraty #include <stdint.h>
1313841c287SSimon J. Gerraty #endif
132956e45f6SSimon J. Gerraty #ifdef HAVE_LIMITS_H
133956e45f6SSimon J. Gerraty #include <limits.h>
134956e45f6SSimon J. Gerraty #endif
135956e45f6SSimon J. Gerraty #include <time.h>
1363841c287SSimon J. Gerraty 
1373955d011SMarcel Moolenaar #include "dir.h"
1383955d011SMarcel Moolenaar #include "job.h"
1394c620fe5SSimon J. Gerraty #include "metachar.h"
1403955d011SMarcel Moolenaar 
14122619282SSimon J. Gerraty #ifndef SIZE_MAX
14222619282SSimon J. Gerraty #define SIZE_MAX 0xffffffffUL
14322619282SSimon J. Gerraty #endif
14422619282SSimon J. Gerraty 
145956e45f6SSimon J. Gerraty /*	"@(#)var.c	8.3 (Berkeley) 3/19/94" */
146*a8c56be4SSimon J. Gerraty MAKE_RCSID("$NetBSD: var.c,v 1.1171 2025/06/29 11:02:17 rillig Exp $");
14706b9b3e0SSimon J. Gerraty 
14806b9b3e0SSimon J. Gerraty /*
14906b9b3e0SSimon J. Gerraty  * Variables are defined using one of the VAR=value assignments.  Their
15006b9b3e0SSimon J. Gerraty  * value can be queried by expressions such as $V, ${VAR}, or with modifiers
15106b9b3e0SSimon J. Gerraty  * such as ${VAR:S,from,to,g:Q}.
15206b9b3e0SSimon J. Gerraty  *
153dba7b0efSSimon J. Gerraty  * There are 3 kinds of variables: scope variables, environment variables,
15406b9b3e0SSimon J. Gerraty  * undefined variables.
15506b9b3e0SSimon J. Gerraty  *
156548bfc56SSimon J. Gerraty  * Scope variables are stored in GNode.vars.  The only way to undefine
157dba7b0efSSimon J. Gerraty  * a scope variable is using the .undef directive.  In particular, it must
15806b9b3e0SSimon J. Gerraty  * not be possible to undefine a variable during the evaluation of an
1599f45a3c8SSimon J. Gerraty  * expression, or Var.name might point nowhere.  (There is another,
1609f45a3c8SSimon J. Gerraty  * unintended way to undefine a scope variable, see varmod-loop-delete.mk.)
16106b9b3e0SSimon J. Gerraty  *
1629f45a3c8SSimon J. Gerraty  * Environment variables are short-lived.  They are returned by VarFind, and
1639f45a3c8SSimon J. Gerraty  * after using them, they must be freed using VarFreeShortLived.
16406b9b3e0SSimon J. Gerraty  *
165d5e0a182SSimon J. Gerraty  * Undefined variables occur during evaluation of expressions such
16606b9b3e0SSimon J. Gerraty  * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers.
16706b9b3e0SSimon J. Gerraty  */
16806b9b3e0SSimon J. Gerraty typedef struct Var {
16906b9b3e0SSimon J. Gerraty 	/*
17006b9b3e0SSimon J. Gerraty 	 * The name of the variable, once set, doesn't change anymore.
171dba7b0efSSimon J. Gerraty 	 * For scope variables, it aliases the corresponding HashEntry name.
17206b9b3e0SSimon J. Gerraty 	 * For environment and undefined variables, it is allocated.
17306b9b3e0SSimon J. Gerraty 	 */
17406b9b3e0SSimon J. Gerraty 	FStr name;
17506b9b3e0SSimon J. Gerraty 
17606b9b3e0SSimon J. Gerraty 	/* The unexpanded value of the variable. */
17706b9b3e0SSimon J. Gerraty 	Buffer val;
178b0c40a00SSimon J. Gerraty 
179b0c40a00SSimon J. Gerraty 	/* The variable came from the command line. */
180b0c40a00SSimon J. Gerraty 	bool fromCmd:1;
181b0c40a00SSimon J. Gerraty 
182b0c40a00SSimon J. Gerraty 	/*
1839f45a3c8SSimon J. Gerraty 	 * The variable is short-lived.
184b0c40a00SSimon J. Gerraty 	 * These variables are not registered in any GNode, therefore they
1859f45a3c8SSimon J. Gerraty 	 * must be freed after use.
186b0c40a00SSimon J. Gerraty 	 */
1879f45a3c8SSimon J. Gerraty 	bool shortLived:1;
1889f45a3c8SSimon J. Gerraty 
1899f45a3c8SSimon J. Gerraty 	/*
1909f45a3c8SSimon J. Gerraty 	 * The variable comes from the environment.
191548bfc56SSimon J. Gerraty 	 * Appending to its value depends on the scope, see var-op-append.mk.
1929f45a3c8SSimon J. Gerraty 	 */
1939f45a3c8SSimon J. Gerraty 	bool fromEnvironment:1;
194b0c40a00SSimon J. Gerraty 
195b0c40a00SSimon J. Gerraty 	/*
196b0c40a00SSimon J. Gerraty 	 * The variable value cannot be changed anymore, and the variable
197b0c40a00SSimon J. Gerraty 	 * cannot be deleted.  Any attempts to do so are silently ignored,
198b0c40a00SSimon J. Gerraty 	 * they are logged with -dv though.
1994fde40d9SSimon J. Gerraty 	 * Use .[NO]READONLY: to adjust.
200b0c40a00SSimon J. Gerraty 	 *
201b0c40a00SSimon J. Gerraty 	 * See VAR_SET_READONLY.
202b0c40a00SSimon J. Gerraty 	 */
203b0c40a00SSimon J. Gerraty 	bool readOnly:1;
204b0c40a00SSimon J. Gerraty 
205b0c40a00SSimon J. Gerraty 	/*
2068d5c8e21SSimon J. Gerraty 	 * The variable is read-only and immune to the .NOREADONLY special
2078d5c8e21SSimon J. Gerraty 	 * target.  Any attempt to modify it results in an error.
2088d5c8e21SSimon J. Gerraty 	 */
2098d5c8e21SSimon J. Gerraty 	bool readOnlyLoud:1;
2108d5c8e21SSimon J. Gerraty 
2118d5c8e21SSimon J. Gerraty 	/*
2124fde40d9SSimon J. Gerraty 	 * The variable is currently being accessed by Var_Parse or Var_Subst.
2134fde40d9SSimon J. Gerraty 	 * This temporary marker is used to avoid endless recursion.
214b0c40a00SSimon J. Gerraty 	 */
215b0c40a00SSimon J. Gerraty 	bool inUse:1;
216b0c40a00SSimon J. Gerraty 
217b0c40a00SSimon J. Gerraty 	/*
218b0c40a00SSimon J. Gerraty 	 * The variable is exported to the environment, to be used by child
219b0c40a00SSimon J. Gerraty 	 * processes.
220b0c40a00SSimon J. Gerraty 	 */
221b0c40a00SSimon J. Gerraty 	bool exported:1;
222b0c40a00SSimon J. Gerraty 
223b0c40a00SSimon J. Gerraty 	/*
224b0c40a00SSimon J. Gerraty 	 * At the point where this variable was exported, it contained an
225b0c40a00SSimon J. Gerraty 	 * unresolved reference to another variable.  Before any child
226d5e0a182SSimon J. Gerraty 	 * process is started, it needs to be actually exported, resolving
227d5e0a182SSimon J. Gerraty 	 * the referenced variable just in time.
228b0c40a00SSimon J. Gerraty 	 */
229b0c40a00SSimon J. Gerraty 	bool reexport:1;
23006b9b3e0SSimon J. Gerraty } Var;
23106b9b3e0SSimon J. Gerraty 
23206b9b3e0SSimon J. Gerraty /*
233b0c40a00SSimon J. Gerraty  * Exporting variables is expensive and may leak memory, so skip it if we
234b0c40a00SSimon J. Gerraty  * can.
23506b9b3e0SSimon J. Gerraty  */
23606b9b3e0SSimon J. Gerraty typedef enum VarExportedMode {
23706b9b3e0SSimon J. Gerraty 	VAR_EXPORTED_NONE,
23806b9b3e0SSimon J. Gerraty 	VAR_EXPORTED_SOME,
23906b9b3e0SSimon J. Gerraty 	VAR_EXPORTED_ALL
24006b9b3e0SSimon J. Gerraty } VarExportedMode;
24106b9b3e0SSimon J. Gerraty 
24206b9b3e0SSimon J. Gerraty typedef enum UnexportWhat {
243b0c40a00SSimon J. Gerraty 	/* Unexport the variables given by name. */
24406b9b3e0SSimon J. Gerraty 	UNEXPORT_NAMED,
245b0c40a00SSimon J. Gerraty 	/*
246b0c40a00SSimon J. Gerraty 	 * Unexport all globals previously exported, but keep the environment
247b0c40a00SSimon J. Gerraty 	 * inherited from the parent.
248b0c40a00SSimon J. Gerraty 	 */
24906b9b3e0SSimon J. Gerraty 	UNEXPORT_ALL,
250b0c40a00SSimon J. Gerraty 	/*
251b0c40a00SSimon J. Gerraty 	 * Unexport all globals previously exported and clear the environment
252b0c40a00SSimon J. Gerraty 	 * inherited from the parent.
253b0c40a00SSimon J. Gerraty 	 */
25406b9b3e0SSimon J. Gerraty 	UNEXPORT_ENV
25506b9b3e0SSimon J. Gerraty } UnexportWhat;
25606b9b3e0SSimon J. Gerraty 
25706b9b3e0SSimon J. Gerraty /* Flags for pattern matching in the :S and :C modifiers */
258b0c40a00SSimon J. Gerraty typedef struct PatternFlags {
259b0c40a00SSimon J. Gerraty 	bool subGlobal:1;	/* 'g': replace as often as possible */
260b0c40a00SSimon J. Gerraty 	bool subOnce:1;		/* '1': replace only once */
261b0c40a00SSimon J. Gerraty 	bool anchorStart:1;	/* '^': match only at start of word */
262b0c40a00SSimon J. Gerraty 	bool anchorEnd:1;	/* '$': match only at end of word */
263b0c40a00SSimon J. Gerraty } PatternFlags;
264dba7b0efSSimon J. Gerraty 
265b0c40a00SSimon J. Gerraty /* SepBuf builds a string from words interleaved with separators. */
26606b9b3e0SSimon J. Gerraty typedef struct SepBuf {
26706b9b3e0SSimon J. Gerraty 	Buffer buf;
268b0c40a00SSimon J. Gerraty 	bool needSep;
26906b9b3e0SSimon J. Gerraty 	/* Usually ' ', but see the ':ts' modifier. */
27006b9b3e0SSimon J. Gerraty 	char sep;
27106b9b3e0SSimon J. Gerraty } SepBuf;
27206b9b3e0SSimon J. Gerraty 
2738d5c8e21SSimon J. Gerraty typedef enum {
2740b46a53aSSimon J. Gerraty 	VSK_MAKEFLAGS,
2758d5c8e21SSimon J. Gerraty 	VSK_TARGET,
276759b177aSSimon J. Gerraty 	VSK_COMMAND,
2778d5c8e21SSimon J. Gerraty 	VSK_VARNAME,
278759b177aSSimon J. Gerraty 	VSK_INDIRECT_MODIFIERS,
27922619282SSimon J. Gerraty 	VSK_COND,
28022619282SSimon J. Gerraty 	VSK_COND_THEN,
28122619282SSimon J. Gerraty 	VSK_COND_ELSE,
28222619282SSimon J. Gerraty 	VSK_EXPR,
28322619282SSimon J. Gerraty 	VSK_EXPR_PARSE
2848d5c8e21SSimon J. Gerraty } EvalStackElementKind;
2858d5c8e21SSimon J. Gerraty 
286548bfc56SSimon J. Gerraty typedef struct {
2878d5c8e21SSimon J. Gerraty 	EvalStackElementKind kind;
2888d5c8e21SSimon J. Gerraty 	const char *str;
28922619282SSimon J. Gerraty 	const FStr *value;
290548bfc56SSimon J. Gerraty } EvalStackElement;
291548bfc56SSimon J. Gerraty 
292548bfc56SSimon J. Gerraty typedef struct {
293548bfc56SSimon J. Gerraty 	EvalStackElement *elems;
294548bfc56SSimon J. Gerraty 	size_t len;
295548bfc56SSimon J. Gerraty 	size_t cap;
296548bfc56SSimon J. Gerraty } EvalStack;
29706b9b3e0SSimon J. Gerraty 
298d5e0a182SSimon J. Gerraty /* Whether we have replaced the original environ (which we cannot free). */
2993955d011SMarcel Moolenaar char **savedEnv = NULL;
3003955d011SMarcel Moolenaar 
30106b9b3e0SSimon J. Gerraty /*
30206b9b3e0SSimon J. Gerraty  * Special return value for Var_Parse, indicating a parse error.  It may be
303956e45f6SSimon J. Gerraty  * caused by an undefined variable, a syntax error in a modifier or
30406b9b3e0SSimon J. Gerraty  * something entirely different.
30506b9b3e0SSimon J. Gerraty  */
3063955d011SMarcel Moolenaar char var_Error[] = "";
3073955d011SMarcel Moolenaar 
30806b9b3e0SSimon J. Gerraty /*
30906b9b3e0SSimon J. Gerraty  * Special return value for Var_Parse, indicating an undefined variable in
3108d5c8e21SSimon J. Gerraty  * a case where VARE_EVAL_DEFINED is not set.  This undefined variable is
311956e45f6SSimon J. Gerraty  * typically a dynamic variable such as ${.TARGET}, whose expansion needs to
31206b9b3e0SSimon J. Gerraty  * be deferred until it is defined in an actual target.
313b0c40a00SSimon J. Gerraty  *
3148d5c8e21SSimon J. Gerraty  * See VARE_EVAL_KEEP_UNDEFINED.
31506b9b3e0SSimon J. Gerraty  */
316956e45f6SSimon J. Gerraty static char varUndefined[] = "";
317956e45f6SSimon J. Gerraty 
3183955d011SMarcel Moolenaar /*
319956e45f6SSimon J. Gerraty  * Traditionally this make consumed $$ during := like any other expansion.
320956e45f6SSimon J. Gerraty  * Other make's do not, and this make follows straight since 2016-01-09.
321956e45f6SSimon J. Gerraty  *
322b0c40a00SSimon J. Gerraty  * This knob allows controlling the behavior:
323b0c40a00SSimon J. Gerraty  *	false to consume $$ during := assignment.
324b0c40a00SSimon J. Gerraty  *	true to preserve $$ during := assignment.
325be19d90bSSimon J. Gerraty  */
326956e45f6SSimon J. Gerraty #define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS"
327b0c40a00SSimon J. Gerraty static bool save_dollars = false;
328be19d90bSSimon J. Gerraty 
329be19d90bSSimon J. Gerraty /*
330dba7b0efSSimon J. Gerraty  * A scope collects variable names and their values.
331dba7b0efSSimon J. Gerraty  *
332dba7b0efSSimon J. Gerraty  * The main scope is SCOPE_GLOBAL, which contains the variables that are set
333dba7b0efSSimon J. Gerraty  * in the makefiles.  SCOPE_INTERNAL acts as a fallback for SCOPE_GLOBAL and
334dba7b0efSSimon J. Gerraty  * contains some internal make variables.  These internal variables can thus
335dba7b0efSSimon J. Gerraty  * be overridden, they can also be restored by undefining the overriding
336dba7b0efSSimon J. Gerraty  * variable.
337dba7b0efSSimon J. Gerraty  *
338dba7b0efSSimon J. Gerraty  * SCOPE_CMDLINE contains variables from the command line arguments.  These
339dba7b0efSSimon J. Gerraty  * override variables from SCOPE_GLOBAL.
340dba7b0efSSimon J. Gerraty  *
341dba7b0efSSimon J. Gerraty  * There is no scope for environment variables, these are generated on-the-fly
342d5e0a182SSimon J. Gerraty  * whenever they are referenced.
343dba7b0efSSimon J. Gerraty  *
344dba7b0efSSimon J. Gerraty  * Each target has its own scope, containing the 7 target-local variables
3459f45a3c8SSimon J. Gerraty  * .TARGET, .ALLSRC, etc.  Variables set on dependency lines also go in
3469f45a3c8SSimon J. Gerraty  * this scope.
3473955d011SMarcel Moolenaar  */
348dba7b0efSSimon J. Gerraty 
349dba7b0efSSimon J. Gerraty GNode *SCOPE_CMDLINE;
350dba7b0efSSimon J. Gerraty GNode *SCOPE_GLOBAL;
351dba7b0efSSimon J. Gerraty GNode *SCOPE_INTERNAL;
3523955d011SMarcel Moolenaar 
3532c3632d1SSimon J. Gerraty static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
3542c3632d1SSimon J. Gerraty 
35512904384SSimon J. Gerraty static const char VarEvalMode_Name[][32] = {
3568d5c8e21SSimon J. Gerraty 	"parse",
3578c973ee2SSimon J. Gerraty 	"parse-balanced",
358b0c40a00SSimon J. Gerraty 	"eval",
3596a7405f5SSimon J. Gerraty 	"eval-defined-loud",
360b0c40a00SSimon J. Gerraty 	"eval-defined",
361b0c40a00SSimon J. Gerraty 	"eval-keep-undefined",
362b0c40a00SSimon J. Gerraty 	"eval-keep-dollar-and-undefined",
363b0c40a00SSimon J. Gerraty };
364b0c40a00SSimon J. Gerraty 
365548bfc56SSimon J. Gerraty static EvalStack evalStack;
366548bfc56SSimon J. Gerraty 
367548bfc56SSimon J. Gerraty 
3688d5c8e21SSimon J. Gerraty static void
EvalStack_Push(EvalStackElementKind kind,const char * str,const FStr * value)36922619282SSimon J. Gerraty EvalStack_Push(EvalStackElementKind kind, const char *str, const FStr *value)
370548bfc56SSimon J. Gerraty {
371548bfc56SSimon J. Gerraty 	if (evalStack.len >= evalStack.cap) {
372548bfc56SSimon J. Gerraty 		evalStack.cap = 16 + 2 * evalStack.cap;
373548bfc56SSimon J. Gerraty 		evalStack.elems = bmake_realloc(evalStack.elems,
374548bfc56SSimon J. Gerraty 		    evalStack.cap * sizeof(*evalStack.elems));
375548bfc56SSimon J. Gerraty 	}
3768d5c8e21SSimon J. Gerraty 	evalStack.elems[evalStack.len].kind = kind;
3778d5c8e21SSimon J. Gerraty 	evalStack.elems[evalStack.len].str = str;
37822619282SSimon J. Gerraty 	evalStack.elems[evalStack.len].value = value;
379548bfc56SSimon J. Gerraty 	evalStack.len++;
380548bfc56SSimon J. Gerraty }
381548bfc56SSimon J. Gerraty 
3820b46a53aSSimon J. Gerraty void
EvalStack_PushMakeflags(const char * makeflags)3830b46a53aSSimon J. Gerraty EvalStack_PushMakeflags(const char *makeflags)
3840b46a53aSSimon J. Gerraty {
3850b46a53aSSimon J. Gerraty 	EvalStack_Push(VSK_MAKEFLAGS, makeflags, NULL);
3860b46a53aSSimon J. Gerraty }
3870b46a53aSSimon J. Gerraty 
3880b46a53aSSimon J. Gerraty void
EvalStack_Pop(void)389548bfc56SSimon J. Gerraty EvalStack_Pop(void)
390548bfc56SSimon J. Gerraty {
391548bfc56SSimon J. Gerraty 	assert(evalStack.len > 0);
392548bfc56SSimon J. Gerraty 	evalStack.len--;
393548bfc56SSimon J. Gerraty }
394548bfc56SSimon J. Gerraty 
395759b177aSSimon J. Gerraty bool
EvalStack_Details(Buffer * buf)3960b46a53aSSimon J. Gerraty EvalStack_Details(Buffer *buf)
397548bfc56SSimon J. Gerraty {
398548bfc56SSimon J. Gerraty 	size_t i;
399548bfc56SSimon J. Gerraty 
4006a7405f5SSimon J. Gerraty 	for (i = evalStack.len; i > 0; i--) {
40122619282SSimon J. Gerraty 		static const char descr[][42] = {
4020b46a53aSSimon J. Gerraty 			"while evaluating MAKEFLAGS",
40322619282SSimon J. Gerraty 			"in target",
404759b177aSSimon J. Gerraty 			"in command",
40522619282SSimon J. Gerraty 			"while evaluating variable",
406759b177aSSimon J. Gerraty 			"while evaluating indirect modifiers",
40722619282SSimon J. Gerraty 			"while evaluating condition",
40822619282SSimon J. Gerraty 			"while evaluating then-branch of condition",
40922619282SSimon J. Gerraty 			"while evaluating else-branch of condition",
41022619282SSimon J. Gerraty 			"while evaluating",
41122619282SSimon J. Gerraty 			"while parsing",
41222619282SSimon J. Gerraty 		};
4136a7405f5SSimon J. Gerraty 		EvalStackElement *elem = evalStack.elems + i - 1;
41422619282SSimon J. Gerraty 		EvalStackElementKind kind = elem->kind;
4156a7405f5SSimon J. Gerraty 		const char* value = elem->value != NULL
4166a7405f5SSimon J. Gerraty 		    && (kind == VSK_VARNAME || kind == VSK_EXPR)
4176a7405f5SSimon J. Gerraty 		    ? elem->value->str : NULL;
4186a7405f5SSimon J. Gerraty 
4190b46a53aSSimon J. Gerraty 		Buf_AddStr(buf, "\t");
4200b46a53aSSimon J. Gerraty 		Buf_AddStr(buf, descr[kind]);
4210b46a53aSSimon J. Gerraty 		Buf_AddStr(buf, " \"");
4220b46a53aSSimon J. Gerraty 		Buf_AddStr(buf, elem->str);
4230b46a53aSSimon J. Gerraty 		if (value != NULL) {
4240b46a53aSSimon J. Gerraty 			Buf_AddStr(buf, "\" with value \"");
4250b46a53aSSimon J. Gerraty 			Buf_AddStr(buf, value);
4260b46a53aSSimon J. Gerraty 		}
4270b46a53aSSimon J. Gerraty 		Buf_AddStr(buf, "\"\n");
42822619282SSimon J. Gerraty 	}
429759b177aSSimon J. Gerraty 	return evalStack.len > 0;
430548bfc56SSimon J. Gerraty }
4313955d011SMarcel Moolenaar 
4323955d011SMarcel Moolenaar static Var *
VarNew(FStr name,const char * value,bool shortLived,bool fromEnvironment,bool readOnly)4339f45a3c8SSimon J. Gerraty VarNew(FStr name, const char *value,
4349f45a3c8SSimon J. Gerraty        bool shortLived, bool fromEnvironment, bool readOnly)
4353955d011SMarcel Moolenaar {
436956e45f6SSimon J. Gerraty 	size_t value_len = strlen(value);
437956e45f6SSimon J. Gerraty 	Var *var = bmake_malloc(sizeof *var);
438956e45f6SSimon J. Gerraty 	var->name = name;
439e2eeea75SSimon J. Gerraty 	Buf_InitSize(&var->val, value_len + 1);
440956e45f6SSimon J. Gerraty 	Buf_AddBytes(&var->val, value, value_len);
441b0c40a00SSimon J. Gerraty 	var->fromCmd = false;
4429f45a3c8SSimon J. Gerraty 	var->shortLived = shortLived;
4439f45a3c8SSimon J. Gerraty 	var->fromEnvironment = fromEnvironment;
444b0c40a00SSimon J. Gerraty 	var->readOnly = readOnly;
4458d5c8e21SSimon J. Gerraty 	var->readOnlyLoud = false;
446b0c40a00SSimon J. Gerraty 	var->inUse = false;
447b0c40a00SSimon J. Gerraty 	var->exported = false;
448b0c40a00SSimon J. Gerraty 	var->reexport = false;
449956e45f6SSimon J. Gerraty 	return var;
450956e45f6SSimon J. Gerraty }
4513955d011SMarcel Moolenaar 
452b0c40a00SSimon J. Gerraty static Substring
CanonicalVarname(Substring name)453b0c40a00SSimon J. Gerraty CanonicalVarname(Substring name)
454956e45f6SSimon J. Gerraty {
455*a8c56be4SSimon J. Gerraty 	if (Substring_Equals(name, "^"))
456*a8c56be4SSimon J. Gerraty 		return Substring_InitStr(ALLSRC);
457b0c40a00SSimon J. Gerraty 
458b0c40a00SSimon J. Gerraty 	if (!(Substring_Length(name) > 0 && name.start[0] == '.'))
459b0c40a00SSimon J. Gerraty 		return name;
460b0c40a00SSimon J. Gerraty 
461b0c40a00SSimon J. Gerraty 	if (Substring_Equals(name, ".ALLSRC"))
462b0c40a00SSimon J. Gerraty 		return Substring_InitStr(ALLSRC);
463b0c40a00SSimon J. Gerraty 	if (Substring_Equals(name, ".ARCHIVE"))
464b0c40a00SSimon J. Gerraty 		return Substring_InitStr(ARCHIVE);
465b0c40a00SSimon J. Gerraty 	if (Substring_Equals(name, ".IMPSRC"))
466b0c40a00SSimon J. Gerraty 		return Substring_InitStr(IMPSRC);
467b0c40a00SSimon J. Gerraty 	if (Substring_Equals(name, ".MEMBER"))
468b0c40a00SSimon J. Gerraty 		return Substring_InitStr(MEMBER);
469b0c40a00SSimon J. Gerraty 	if (Substring_Equals(name, ".OODATE"))
470b0c40a00SSimon J. Gerraty 		return Substring_InitStr(OODATE);
471b0c40a00SSimon J. Gerraty 	if (Substring_Equals(name, ".PREFIX"))
472b0c40a00SSimon J. Gerraty 		return Substring_InitStr(PREFIX);
473b0c40a00SSimon J. Gerraty 	if (Substring_Equals(name, ".TARGET"))
474b0c40a00SSimon J. Gerraty 		return Substring_InitStr(TARGET);
475b0c40a00SSimon J. Gerraty 
476b0c40a00SSimon J. Gerraty 	if (Substring_Equals(name, ".SHELL") && shellPath == NULL)
4772c3632d1SSimon J. Gerraty 		Shell_Init();
4783841c287SSimon J. Gerraty 
479956e45f6SSimon J. Gerraty 	return name;
480956e45f6SSimon J. Gerraty }
481956e45f6SSimon J. Gerraty 
482956e45f6SSimon J. Gerraty static Var *
GNode_FindVar(GNode * scope,Substring varname,unsigned hash)4830b46a53aSSimon J. Gerraty GNode_FindVar(GNode *scope, Substring varname, unsigned hash)
484956e45f6SSimon J. Gerraty {
485b0c40a00SSimon J. Gerraty 	return HashTable_FindValueBySubstringHash(&scope->vars, varname, hash);
486956e45f6SSimon J. Gerraty }
487956e45f6SSimon J. Gerraty 
48806b9b3e0SSimon J. Gerraty /*
489dba7b0efSSimon J. Gerraty  * Find the variable in the scope, and maybe in other scopes as well.
490956e45f6SSimon J. Gerraty  *
491956e45f6SSimon J. Gerraty  * Input:
492956e45f6SSimon J. Gerraty  *	name		name to find, is not expanded any further
493dba7b0efSSimon J. Gerraty  *	scope		scope in which to look first
494b0c40a00SSimon J. Gerraty  *	elsewhere	true to look in other scopes as well
495956e45f6SSimon J. Gerraty  *
496956e45f6SSimon J. Gerraty  * Results:
497956e45f6SSimon J. Gerraty  *	The found variable, or NULL if the variable does not exist.
4989f45a3c8SSimon J. Gerraty  *	If the variable is short-lived (such as environment variables), it
4999f45a3c8SSimon J. Gerraty  *	must be freed using VarFreeShortLived after use.
500956e45f6SSimon J. Gerraty  */
501956e45f6SSimon J. Gerraty static Var *
VarFindSubstring(Substring name,GNode * scope,bool elsewhere)502b0c40a00SSimon J. Gerraty VarFindSubstring(Substring name, GNode *scope, bool elsewhere)
503956e45f6SSimon J. Gerraty {
504956e45f6SSimon J. Gerraty 	Var *var;
5050b46a53aSSimon J. Gerraty 	unsigned nameHash;
5063955d011SMarcel Moolenaar 
507b0c40a00SSimon J. Gerraty 	/* Replace '.TARGET' with '@', likewise for other local variables. */
508956e45f6SSimon J. Gerraty 	name = CanonicalVarname(name);
509b0c40a00SSimon J. Gerraty 	nameHash = Hash_Substring(name);
5103955d011SMarcel Moolenaar 
511dba7b0efSSimon J. Gerraty 	var = GNode_FindVar(scope, name, nameHash);
512956e45f6SSimon J. Gerraty 	if (!elsewhere)
513956e45f6SSimon J. Gerraty 		return var;
5142c3632d1SSimon J. Gerraty 
515dba7b0efSSimon J. Gerraty 	if (var == NULL && scope != SCOPE_CMDLINE)
516dba7b0efSSimon J. Gerraty 		var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash);
517956e45f6SSimon J. Gerraty 
518dba7b0efSSimon J. Gerraty 	if (!opts.checkEnvFirst && var == NULL && scope != SCOPE_GLOBAL) {
519dba7b0efSSimon J. Gerraty 		var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash);
520dba7b0efSSimon J. Gerraty 		if (var == NULL && scope != SCOPE_INTERNAL) {
521dba7b0efSSimon J. Gerraty 			/* SCOPE_INTERNAL is subordinate to SCOPE_GLOBAL */
522dba7b0efSSimon J. Gerraty 			var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash);
5231bbe5942SSimon J. Gerraty 		}
5243955d011SMarcel Moolenaar 	}
5252c3632d1SSimon J. Gerraty 
526956e45f6SSimon J. Gerraty 	if (var == NULL) {
527548bfc56SSimon J. Gerraty 		FStr envName = Substring_Str(name);
528548bfc56SSimon J. Gerraty 		const char *envValue = getenv(envName.str);
529b0c40a00SSimon J. Gerraty 		if (envValue != NULL)
5309f45a3c8SSimon J. Gerraty 			return VarNew(envName, envValue, true, true, false);
531b0c40a00SSimon J. Gerraty 		FStr_Done(&envName);
5322c3632d1SSimon J. Gerraty 
533dba7b0efSSimon J. Gerraty 		if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) {
534dba7b0efSSimon J. Gerraty 			var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash);
535dba7b0efSSimon J. Gerraty 			if (var == NULL && scope != SCOPE_INTERNAL)
536dba7b0efSSimon J. Gerraty 				var = GNode_FindVar(SCOPE_INTERNAL, name,
53706b9b3e0SSimon J. Gerraty 				    nameHash);
538956e45f6SSimon J. Gerraty 			return var;
5393955d011SMarcel Moolenaar 		}
5402c3632d1SSimon J. Gerraty 
5413955d011SMarcel Moolenaar 		return NULL;
5423955d011SMarcel Moolenaar 	}
5432c3632d1SSimon J. Gerraty 
544956e45f6SSimon J. Gerraty 	return var;
5453955d011SMarcel Moolenaar }
5463955d011SMarcel Moolenaar 
547b0c40a00SSimon J. Gerraty static Var *
VarFind(const char * name,GNode * scope,bool elsewhere)548b0c40a00SSimon J. Gerraty VarFind(const char *name, GNode *scope, bool elsewhere)
5493955d011SMarcel Moolenaar {
550b0c40a00SSimon J. Gerraty 	return VarFindSubstring(Substring_InitStr(name), scope, elsewhere);
5513955d011SMarcel Moolenaar }
5523955d011SMarcel Moolenaar 
5539f45a3c8SSimon J. Gerraty /* If the variable is short-lived, free it, including its value. */
5543955d011SMarcel Moolenaar static void
VarFreeShortLived(Var * v)5559f45a3c8SSimon J. Gerraty VarFreeShortLived(Var *v)
556b0c40a00SSimon J. Gerraty {
5579f45a3c8SSimon J. Gerraty 	if (!v->shortLived)
558b0c40a00SSimon J. Gerraty 		return;
559b0c40a00SSimon J. Gerraty 
560b0c40a00SSimon J. Gerraty 	FStr_Done(&v->name);
561b0c40a00SSimon J. Gerraty 	Buf_Done(&v->val);
562b0c40a00SSimon J. Gerraty 	free(v);
563b0c40a00SSimon J. Gerraty }
564b0c40a00SSimon J. Gerraty 
5651d3f2ddcSSimon J. Gerraty static const char *
ValueDescription(const char * value)5661d3f2ddcSSimon J. Gerraty ValueDescription(const char *value)
5671d3f2ddcSSimon J. Gerraty {
5681d3f2ddcSSimon J. Gerraty 	if (value[0] == '\0')
5691d3f2ddcSSimon J. Gerraty 		return "# (empty)";
5701d3f2ddcSSimon J. Gerraty 	if (ch_isspace(value[strlen(value) - 1]))
5711d3f2ddcSSimon J. Gerraty 		return "# (ends with space)";
5721d3f2ddcSSimon J. Gerraty 	return "";
5731d3f2ddcSSimon J. Gerraty }
5741d3f2ddcSSimon J. Gerraty 
575b0c40a00SSimon J. Gerraty /* Add a new variable of the given name and value to the given scope. */
576b0c40a00SSimon J. Gerraty static Var *
VarAdd(const char * name,const char * value,GNode * scope,VarSetFlags flags)577b0c40a00SSimon J. Gerraty VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags)
5783955d011SMarcel Moolenaar {
579dba7b0efSSimon J. Gerraty 	HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL);
580b0c40a00SSimon J. Gerraty 	Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value,
5819f45a3c8SSimon J. Gerraty 	    false, false, (flags & VAR_SET_READONLY) != 0);
582956e45f6SSimon J. Gerraty 	HashEntry_Set(he, v);
5831d3f2ddcSSimon J. Gerraty 	DEBUG4(VAR, "%s: %s = %s%s\n",
5841d3f2ddcSSimon J. Gerraty 	    scope->name, name, value, ValueDescription(value));
585b0c40a00SSimon J. Gerraty 	return v;
5863955d011SMarcel Moolenaar }
5873955d011SMarcel Moolenaar 
58806b9b3e0SSimon J. Gerraty /*
589dba7b0efSSimon J. Gerraty  * Remove a variable from a scope, freeing all related memory as well.
59006b9b3e0SSimon J. Gerraty  * The variable name is kept as-is, it is not expanded.
59106b9b3e0SSimon J. Gerraty  */
5923955d011SMarcel Moolenaar void
Var_Delete(GNode * scope,const char * varname)593dba7b0efSSimon J. Gerraty Var_Delete(GNode *scope, const char *varname)
5943955d011SMarcel Moolenaar {
595dba7b0efSSimon J. Gerraty 	HashEntry *he = HashTable_FindEntry(&scope->vars, varname);
59606b9b3e0SSimon J. Gerraty 	Var *v;
5973955d011SMarcel Moolenaar 
59806b9b3e0SSimon J. Gerraty 	if (he == NULL) {
599d5e0a182SSimon J. Gerraty 		DEBUG2(VAR, "%s: ignoring delete '%s' as it is not found\n",
6001d3f2ddcSSimon J. Gerraty 		    scope->name, varname);
60106b9b3e0SSimon J. Gerraty 		return;
602956e45f6SSimon J. Gerraty 	}
6033955d011SMarcel Moolenaar 
604b0c40a00SSimon J. Gerraty 	v = he->value;
6058d5c8e21SSimon J. Gerraty 	if (v->readOnlyLoud) {
6068d5c8e21SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
6078d5c8e21SSimon J. Gerraty 		    "Cannot delete \"%s\" as it is read-only",
6088d5c8e21SSimon J. Gerraty 		    v->name.str);
6098d5c8e21SSimon J. Gerraty 		return;
6108d5c8e21SSimon J. Gerraty 	}
611148ee845SSimon J. Gerraty 	if (v->readOnly) {
612d5e0a182SSimon J. Gerraty 		DEBUG2(VAR, "%s: ignoring delete '%s' as it is read-only\n",
613148ee845SSimon J. Gerraty 		    scope->name, varname);
614148ee845SSimon J. Gerraty 		return;
615148ee845SSimon J. Gerraty 	}
61612904384SSimon J. Gerraty 	if (v->inUse) {
61712904384SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
61812904384SSimon J. Gerraty 		    "Cannot delete variable \"%s\" while it is used",
61912904384SSimon J. Gerraty 		    v->name.str);
62012904384SSimon J. Gerraty 		return;
62112904384SSimon J. Gerraty 	}
6221d3f2ddcSSimon J. Gerraty 
623d5e0a182SSimon J. Gerraty 	DEBUG2(VAR, "%s: delete %s\n", scope->name, varname);
624b0c40a00SSimon J. Gerraty 	if (v->exported)
62506b9b3e0SSimon J. Gerraty 		unsetenv(v->name.str);
6268c973ee2SSimon J. Gerraty 	if (strcmp(v->name.str, ".MAKE.EXPORTED") == 0)
6273955d011SMarcel Moolenaar 		var_exportedVars = VAR_EXPORTED_NONE;
6281d3f2ddcSSimon J. Gerraty 
62906b9b3e0SSimon J. Gerraty 	assert(v->name.freeIt == NULL);
630dba7b0efSSimon J. Gerraty 	HashTable_DeleteEntry(&scope->vars, he);
631dba7b0efSSimon J. Gerraty 	Buf_Done(&v->val);
6323955d011SMarcel Moolenaar 	free(v);
6333955d011SMarcel Moolenaar }
63406b9b3e0SSimon J. Gerraty 
6358d5c8e21SSimon J. Gerraty #ifdef CLEANUP
6368d5c8e21SSimon J. Gerraty void
Var_DeleteAll(GNode * scope)6378d5c8e21SSimon J. Gerraty Var_DeleteAll(GNode *scope)
6388d5c8e21SSimon J. Gerraty {
6398d5c8e21SSimon J. Gerraty 	HashIter hi;
6408d5c8e21SSimon J. Gerraty 	HashIter_Init(&hi, &scope->vars);
6418d5c8e21SSimon J. Gerraty 	while (HashIter_Next(&hi)) {
6428d5c8e21SSimon J. Gerraty 		Var *v = hi.entry->value;
6438d5c8e21SSimon J. Gerraty 		Buf_Done(&v->val);
6448d5c8e21SSimon J. Gerraty 		free(v);
6458d5c8e21SSimon J. Gerraty 	}
6468d5c8e21SSimon J. Gerraty }
6478d5c8e21SSimon J. Gerraty #endif
6488d5c8e21SSimon J. Gerraty 
64906b9b3e0SSimon J. Gerraty /*
65006b9b3e0SSimon J. Gerraty  * Undefine one or more variables from the global scope.
65106b9b3e0SSimon J. Gerraty  * The argument is expanded exactly once and then split into words.
65206b9b3e0SSimon J. Gerraty  */
65306b9b3e0SSimon J. Gerraty void
Var_Undef(const char * arg)65406b9b3e0SSimon J. Gerraty Var_Undef(const char *arg)
65506b9b3e0SSimon J. Gerraty {
65606b9b3e0SSimon J. Gerraty 	char *expanded;
65706b9b3e0SSimon J. Gerraty 	Words varnames;
65806b9b3e0SSimon J. Gerraty 	size_t i;
65906b9b3e0SSimon J. Gerraty 
66006b9b3e0SSimon J. Gerraty 	if (arg[0] == '\0') {
66106b9b3e0SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
66206b9b3e0SSimon J. Gerraty 		    "The .undef directive requires an argument");
66306b9b3e0SSimon J. Gerraty 		return;
66406b9b3e0SSimon J. Gerraty 	}
66506b9b3e0SSimon J. Gerraty 
6668d5c8e21SSimon J. Gerraty 	expanded = Var_Subst(arg, SCOPE_GLOBAL, VARE_EVAL);
6678c973ee2SSimon J. Gerraty 	if (expanded == var_Error) {
6688c973ee2SSimon J. Gerraty 		/* TODO: Make this part of the code reachable. */
66906b9b3e0SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
67006b9b3e0SSimon J. Gerraty 		    "Error in variable names to be undefined");
67106b9b3e0SSimon J. Gerraty 		return;
67206b9b3e0SSimon J. Gerraty 	}
67306b9b3e0SSimon J. Gerraty 
674b0c40a00SSimon J. Gerraty 	varnames = Str_Words(expanded, false);
67506b9b3e0SSimon J. Gerraty 	if (varnames.len == 1 && varnames.words[0][0] == '\0')
67606b9b3e0SSimon J. Gerraty 		varnames.len = 0;
67706b9b3e0SSimon J. Gerraty 
67806b9b3e0SSimon J. Gerraty 	for (i = 0; i < varnames.len; i++) {
67906b9b3e0SSimon J. Gerraty 		const char *varname = varnames.words[i];
680dba7b0efSSimon J. Gerraty 		Global_Delete(varname);
68106b9b3e0SSimon J. Gerraty 	}
68206b9b3e0SSimon J. Gerraty 
68306b9b3e0SSimon J. Gerraty 	Words_Free(varnames);
68406b9b3e0SSimon J. Gerraty 	free(expanded);
6853955d011SMarcel Moolenaar }
6863955d011SMarcel Moolenaar 
687b0c40a00SSimon J. Gerraty static bool
MayExport(const char * name)688956e45f6SSimon J. Gerraty MayExport(const char *name)
6893955d011SMarcel Moolenaar {
6902c3632d1SSimon J. Gerraty 	if (name[0] == '.')
691b0c40a00SSimon J. Gerraty 		return false;	/* skip internals */
692956e45f6SSimon J. Gerraty 	if (name[0] == '-')
693b0c40a00SSimon J. Gerraty 		return false;	/* skip misnamed variables */
6942c3632d1SSimon J. Gerraty 	if (name[1] == '\0') {
6953955d011SMarcel Moolenaar 		/*
6963955d011SMarcel Moolenaar 		 * A single char.
697dba7b0efSSimon J. Gerraty 		 * If it is one of the variables that should only appear in
698dba7b0efSSimon J. Gerraty 		 * local scope, skip it, else we can get Var_Subst
6993955d011SMarcel Moolenaar 		 * into a loop.
7003955d011SMarcel Moolenaar 		 */
7013955d011SMarcel Moolenaar 		switch (name[0]) {
7023955d011SMarcel Moolenaar 		case '@':
7033955d011SMarcel Moolenaar 		case '%':
7043955d011SMarcel Moolenaar 		case '*':
7053955d011SMarcel Moolenaar 		case '!':
706b0c40a00SSimon J. Gerraty 			return false;
7073955d011SMarcel Moolenaar 		}
7083955d011SMarcel Moolenaar 	}
709b0c40a00SSimon J. Gerraty 	return true;
710956e45f6SSimon J. Gerraty }
711956e45f6SSimon J. Gerraty 
712b0c40a00SSimon J. Gerraty static bool
ExportVarEnv(Var * v,GNode * scope)713c59c3bf3SSimon J. Gerraty ExportVarEnv(Var *v, GNode *scope)
714956e45f6SSimon J. Gerraty {
71506b9b3e0SSimon J. Gerraty 	const char *name = v->name.str;
71606b9b3e0SSimon J. Gerraty 	char *val = v->val.data;
7172c3632d1SSimon J. Gerraty 	char *expr;
7182c3632d1SSimon J. Gerraty 
719b0c40a00SSimon J. Gerraty 	if (v->exported && !v->reexport)
720b0c40a00SSimon J. Gerraty 		return false;	/* nothing to do */
72106b9b3e0SSimon J. Gerraty 
72206b9b3e0SSimon J. Gerraty 	if (strchr(val, '$') == NULL) {
723b0c40a00SSimon J. Gerraty 		if (!v->exported)
72406b9b3e0SSimon J. Gerraty 			setenv(name, val, 1);
725b0c40a00SSimon J. Gerraty 		return true;
7263955d011SMarcel Moolenaar 	}
72706b9b3e0SSimon J. Gerraty 
728d5e0a182SSimon J. Gerraty 	if (v->inUse)
729d5e0a182SSimon J. Gerraty 		return false;	/* see EMPTY_SHELL in directive-export.mk */
7302c3632d1SSimon J. Gerraty 
731956e45f6SSimon J. Gerraty 	/* XXX: name is injected without escaping it */
7322c3632d1SSimon J. Gerraty 	expr = str_concat3("${", name, "}");
7338d5c8e21SSimon J. Gerraty 	val = Var_Subst(expr, scope, VARE_EVAL);
734c59c3bf3SSimon J. Gerraty 	if (scope != SCOPE_GLOBAL) {
735c59c3bf3SSimon J. Gerraty 		/* we will need to re-export the global version */
736c59c3bf3SSimon J. Gerraty 		v = VarFind(name, SCOPE_GLOBAL, false);
737c59c3bf3SSimon J. Gerraty 		if (v != NULL)
738c59c3bf3SSimon J. Gerraty 			v->exported = false;
739c59c3bf3SSimon J. Gerraty 	}
740956e45f6SSimon J. Gerraty 	/* TODO: handle errors */
7413955d011SMarcel Moolenaar 	setenv(name, val, 1);
7423955d011SMarcel Moolenaar 	free(val);
7432c3632d1SSimon J. Gerraty 	free(expr);
744b0c40a00SSimon J. Gerraty 	return true;
7453955d011SMarcel Moolenaar }
746956e45f6SSimon J. Gerraty 
747b0c40a00SSimon J. Gerraty static bool
ExportVarPlain(Var * v)74806b9b3e0SSimon J. Gerraty ExportVarPlain(Var *v)
74906b9b3e0SSimon J. Gerraty {
75006b9b3e0SSimon J. Gerraty 	if (strchr(v->val.data, '$') == NULL) {
75106b9b3e0SSimon J. Gerraty 		setenv(v->name.str, v->val.data, 1);
752b0c40a00SSimon J. Gerraty 		v->exported = true;
753b0c40a00SSimon J. Gerraty 		v->reexport = false;
754b0c40a00SSimon J. Gerraty 		return true;
7553955d011SMarcel Moolenaar 	}
7563955d011SMarcel Moolenaar 
7573955d011SMarcel Moolenaar 	/*
75806b9b3e0SSimon J. Gerraty 	 * Flag the variable as something we need to re-export.
75906b9b3e0SSimon J. Gerraty 	 * No point actually exporting it now though,
76006b9b3e0SSimon J. Gerraty 	 * the child process can do it at the last minute.
761b0c40a00SSimon J. Gerraty 	 * Avoid calling setenv more often than necessary since it can leak.
7623955d011SMarcel Moolenaar 	 */
763b0c40a00SSimon J. Gerraty 	v->exported = true;
764b0c40a00SSimon J. Gerraty 	v->reexport = true;
765b0c40a00SSimon J. Gerraty 	return true;
76606b9b3e0SSimon J. Gerraty }
76706b9b3e0SSimon J. Gerraty 
768b0c40a00SSimon J. Gerraty static bool
ExportVarLiteral(Var * v)76906b9b3e0SSimon J. Gerraty ExportVarLiteral(Var *v)
7703955d011SMarcel Moolenaar {
771b0c40a00SSimon J. Gerraty 	if (v->exported && !v->reexport)
772b0c40a00SSimon J. Gerraty 		return false;
77306b9b3e0SSimon J. Gerraty 
774b0c40a00SSimon J. Gerraty 	if (!v->exported)
77506b9b3e0SSimon J. Gerraty 		setenv(v->name.str, v->val.data, 1);
77606b9b3e0SSimon J. Gerraty 
777b0c40a00SSimon J. Gerraty 	return true;
77806b9b3e0SSimon J. Gerraty }
7793955d011SMarcel Moolenaar 
78051ee2c1cSSimon J. Gerraty /*
781b0c40a00SSimon J. Gerraty  * Mark a single variable to be exported later for subprocesses.
78206b9b3e0SSimon J. Gerraty  *
783d5e0a182SSimon J. Gerraty  * Internal variables are not exported.
78406b9b3e0SSimon J. Gerraty  */
785b0c40a00SSimon J. Gerraty static bool
ExportVar(const char * name,GNode * scope,VarExportMode mode)786c59c3bf3SSimon J. Gerraty ExportVar(const char *name, GNode *scope, VarExportMode mode)
78706b9b3e0SSimon J. Gerraty {
78806b9b3e0SSimon J. Gerraty 	Var *v;
78906b9b3e0SSimon J. Gerraty 
79006b9b3e0SSimon J. Gerraty 	if (!MayExport(name))
791b0c40a00SSimon J. Gerraty 		return false;
79206b9b3e0SSimon J. Gerraty 
793c59c3bf3SSimon J. Gerraty 	v = VarFind(name, scope, false);
794c59c3bf3SSimon J. Gerraty 	if (v == NULL && scope != SCOPE_GLOBAL)
795b0c40a00SSimon J. Gerraty 		v = VarFind(name, SCOPE_GLOBAL, false);
79606b9b3e0SSimon J. Gerraty 	if (v == NULL)
797b0c40a00SSimon J. Gerraty 		return false;
79806b9b3e0SSimon J. Gerraty 
79906b9b3e0SSimon J. Gerraty 	if (mode == VEM_ENV)
800c59c3bf3SSimon J. Gerraty 		return ExportVarEnv(v, scope);
80106b9b3e0SSimon J. Gerraty 	else if (mode == VEM_PLAIN)
80206b9b3e0SSimon J. Gerraty 		return ExportVarPlain(v);
80306b9b3e0SSimon J. Gerraty 	else
80406b9b3e0SSimon J. Gerraty 		return ExportVarLiteral(v);
80506b9b3e0SSimon J. Gerraty }
80606b9b3e0SSimon J. Gerraty 
80706b9b3e0SSimon J. Gerraty /*
80806b9b3e0SSimon J. Gerraty  * Actually export the variables that have been marked as needing to be
80906b9b3e0SSimon J. Gerraty  * re-exported.
81006b9b3e0SSimon J. Gerraty  */
81106b9b3e0SSimon J. Gerraty void
Var_ReexportVars(GNode * scope)812c59c3bf3SSimon J. Gerraty Var_ReexportVars(GNode *scope)
81306b9b3e0SSimon J. Gerraty {
81406b9b3e0SSimon J. Gerraty 	char *xvarnames;
81506b9b3e0SSimon J. Gerraty 
81606b9b3e0SSimon J. Gerraty 	/*
81706b9b3e0SSimon J. Gerraty 	 * Several make implementations support this sort of mechanism for
81806b9b3e0SSimon J. Gerraty 	 * tracking recursion - but each uses a different name.
81951ee2c1cSSimon J. Gerraty 	 * We allow the makefiles to update MAKELEVEL and ensure
82051ee2c1cSSimon J. Gerraty 	 * children see a correctly incremented value.
82151ee2c1cSSimon J. Gerraty 	 */
822d5e0a182SSimon J. Gerraty 	char level_buf[21];
823d5e0a182SSimon J. Gerraty 	snprintf(level_buf, sizeof level_buf, "%d", makelevel + 1);
824d5e0a182SSimon J. Gerraty 	setenv(MAKE_LEVEL_ENV, level_buf, 1);
82551ee2c1cSSimon J. Gerraty 
8262c3632d1SSimon J. Gerraty 	if (var_exportedVars == VAR_EXPORTED_NONE)
8273955d011SMarcel Moolenaar 		return;
8283955d011SMarcel Moolenaar 
8292c3632d1SSimon J. Gerraty 	if (var_exportedVars == VAR_EXPORTED_ALL) {
830956e45f6SSimon J. Gerraty 		HashIter hi;
831956e45f6SSimon J. Gerraty 
832b0c40a00SSimon J. Gerraty 		/* Ouch! Exporting all variables at once is crazy. */
833dba7b0efSSimon J. Gerraty 		HashIter_Init(&hi, &SCOPE_GLOBAL->vars);
8348d5c8e21SSimon J. Gerraty 		while (HashIter_Next(&hi)) {
835956e45f6SSimon J. Gerraty 			Var *var = hi.entry->value;
836c59c3bf3SSimon J. Gerraty 			ExportVar(var->name.str, scope, VEM_ENV);
837956e45f6SSimon J. Gerraty 		}
8383955d011SMarcel Moolenaar 		return;
8393955d011SMarcel Moolenaar 	}
8403955d011SMarcel Moolenaar 
8418c973ee2SSimon J. Gerraty 	xvarnames = Var_Subst("${.MAKE.EXPORTED:O:u}", SCOPE_GLOBAL,
8428d5c8e21SSimon J. Gerraty 	    VARE_EVAL);
843956e45f6SSimon J. Gerraty 	/* TODO: handle errors */
84406b9b3e0SSimon J. Gerraty 	if (xvarnames[0] != '\0') {
845b0c40a00SSimon J. Gerraty 		Words varnames = Str_Words(xvarnames, false);
8462c3632d1SSimon J. Gerraty 		size_t i;
8472c3632d1SSimon J. Gerraty 
84806b9b3e0SSimon J. Gerraty 		for (i = 0; i < varnames.len; i++)
849c59c3bf3SSimon J. Gerraty 			ExportVar(varnames.words[i], scope, VEM_ENV);
85006b9b3e0SSimon J. Gerraty 		Words_Free(varnames);
85106b9b3e0SSimon J. Gerraty 	}
85206b9b3e0SSimon J. Gerraty 	free(xvarnames);
85306b9b3e0SSimon J. Gerraty }
85406b9b3e0SSimon J. Gerraty 
85506b9b3e0SSimon J. Gerraty static void
ExportVars(const char * varnames,bool isExport,VarExportMode mode)856b0c40a00SSimon J. Gerraty ExportVars(const char *varnames, bool isExport, VarExportMode mode)
857b0c40a00SSimon J. Gerraty /* TODO: try to combine the parameters 'isExport' and 'mode'. */
85806b9b3e0SSimon J. Gerraty {
859b0c40a00SSimon J. Gerraty 	Words words = Str_Words(varnames, false);
86006b9b3e0SSimon J. Gerraty 	size_t i;
86106b9b3e0SSimon J. Gerraty 
86206b9b3e0SSimon J. Gerraty 	if (words.len == 1 && words.words[0][0] == '\0')
86306b9b3e0SSimon J. Gerraty 		words.len = 0;
86406b9b3e0SSimon J. Gerraty 
86506b9b3e0SSimon J. Gerraty 	for (i = 0; i < words.len; i++) {
86606b9b3e0SSimon J. Gerraty 		const char *varname = words.words[i];
867c59c3bf3SSimon J. Gerraty 		if (!ExportVar(varname, SCOPE_GLOBAL, mode))
86806b9b3e0SSimon J. Gerraty 			continue;
86906b9b3e0SSimon J. Gerraty 
87006b9b3e0SSimon J. Gerraty 		if (var_exportedVars == VAR_EXPORTED_NONE)
87106b9b3e0SSimon J. Gerraty 			var_exportedVars = VAR_EXPORTED_SOME;
87206b9b3e0SSimon J. Gerraty 
87306b9b3e0SSimon J. Gerraty 		if (isExport && mode == VEM_PLAIN)
8748c973ee2SSimon J. Gerraty 			Global_Append(".MAKE.EXPORTED", varname);
87506b9b3e0SSimon J. Gerraty 	}
8762c3632d1SSimon J. Gerraty 	Words_Free(words);
8773955d011SMarcel Moolenaar }
87806b9b3e0SSimon J. Gerraty 
87906b9b3e0SSimon J. Gerraty static void
ExportVarsExpand(const char * uvarnames,bool isExport,VarExportMode mode)880b0c40a00SSimon J. Gerraty ExportVarsExpand(const char *uvarnames, bool isExport, VarExportMode mode)
88106b9b3e0SSimon J. Gerraty {
8828d5c8e21SSimon J. Gerraty 	char *xvarnames = Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_EVAL);
88306b9b3e0SSimon J. Gerraty 	/* TODO: handle errors */
88406b9b3e0SSimon J. Gerraty 	ExportVars(xvarnames, isExport, mode);
88506b9b3e0SSimon J. Gerraty 	free(xvarnames);
886ac3446e9SSimon J. Gerraty }
8873955d011SMarcel Moolenaar 
88806b9b3e0SSimon J. Gerraty /* Export the named variables, or all variables. */
8893955d011SMarcel Moolenaar void
Var_Export(VarExportMode mode,const char * varnames)89006b9b3e0SSimon J. Gerraty Var_Export(VarExportMode mode, const char *varnames)
8913955d011SMarcel Moolenaar {
8928d5c8e21SSimon J. Gerraty 	if (mode == VEM_ALL) {
8933955d011SMarcel Moolenaar 		var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */
8943955d011SMarcel Moolenaar 		return;
8958d5c8e21SSimon J. Gerraty 	} else if (mode == VEM_PLAIN && varnames[0] == '\0') {
8968d5c8e21SSimon J. Gerraty 		Parse_Error(PARSE_WARNING, ".export requires an argument.");
8978d5c8e21SSimon J. Gerraty 		return;
8983955d011SMarcel Moolenaar 	}
8993955d011SMarcel Moolenaar 
900b0c40a00SSimon J. Gerraty 	ExportVarsExpand(varnames, true, mode);
9013955d011SMarcel Moolenaar }
9022c3632d1SSimon J. Gerraty 
90306b9b3e0SSimon J. Gerraty void
Var_ExportVars(const char * varnames)90406b9b3e0SSimon J. Gerraty Var_ExportVars(const char *varnames)
90506b9b3e0SSimon J. Gerraty {
906b0c40a00SSimon J. Gerraty 	ExportVarsExpand(varnames, false, VEM_PLAIN);
907ac3446e9SSimon J. Gerraty }
9083955d011SMarcel Moolenaar 
9093955d011SMarcel Moolenaar 
91006b9b3e0SSimon J. Gerraty static void
ClearEnv(void)91106b9b3e0SSimon J. Gerraty ClearEnv(void)
9123955d011SMarcel Moolenaar {
913d5e0a182SSimon J. Gerraty 	const char *level;
9143955d011SMarcel Moolenaar 	char **newenv;
9153955d011SMarcel Moolenaar 
916d5e0a182SSimon J. Gerraty 	level = getenv(MAKE_LEVEL_ENV);	/* we should preserve this */
9173955d011SMarcel Moolenaar 	if (environ == savedEnv) {
9183955d011SMarcel Moolenaar 		/* we have been here before! */
9193955d011SMarcel Moolenaar 		newenv = bmake_realloc(environ, 2 * sizeof(char *));
9203955d011SMarcel Moolenaar 	} else {
92106b9b3e0SSimon J. Gerraty 		if (savedEnv != NULL) {
9223955d011SMarcel Moolenaar 			free(savedEnv);
9233955d011SMarcel Moolenaar 			savedEnv = NULL;
9243955d011SMarcel Moolenaar 		}
9253955d011SMarcel Moolenaar 		newenv = bmake_malloc(2 * sizeof(char *));
9263955d011SMarcel Moolenaar 	}
9272c3632d1SSimon J. Gerraty 
9283955d011SMarcel Moolenaar 	/* Note: we cannot safely free() the original environ. */
9293955d011SMarcel Moolenaar 	environ = savedEnv = newenv;
9303955d011SMarcel Moolenaar 	newenv[0] = NULL;
9313955d011SMarcel Moolenaar 	newenv[1] = NULL;
932d5e0a182SSimon J. Gerraty 	if (level != NULL && *level != '\0')
933d5e0a182SSimon J. Gerraty 		setenv(MAKE_LEVEL_ENV, level, 1);
9343955d011SMarcel Moolenaar }
9353955d011SMarcel Moolenaar 
93606b9b3e0SSimon J. Gerraty static void
GetVarnamesToUnexport(bool isEnv,const char * arg,FStr * out_varnames,UnexportWhat * out_what)937b0c40a00SSimon J. Gerraty GetVarnamesToUnexport(bool isEnv, const char *arg,
93806b9b3e0SSimon J. Gerraty 		      FStr *out_varnames, UnexportWhat *out_what)
9392c3632d1SSimon J. Gerraty {
94006b9b3e0SSimon J. Gerraty 	UnexportWhat what;
94106b9b3e0SSimon J. Gerraty 	FStr varnames = FStr_InitRefer("");
9422c3632d1SSimon J. Gerraty 
94306b9b3e0SSimon J. Gerraty 	if (isEnv) {
94406b9b3e0SSimon J. Gerraty 		if (arg[0] != '\0') {
94506b9b3e0SSimon J. Gerraty 			Parse_Error(PARSE_FATAL,
94606b9b3e0SSimon J. Gerraty 			    "The directive .unexport-env does not take "
94706b9b3e0SSimon J. Gerraty 			    "arguments");
948b0c40a00SSimon J. Gerraty 			/* continue anyway */
94906b9b3e0SSimon J. Gerraty 		}
95006b9b3e0SSimon J. Gerraty 		what = UNEXPORT_ENV;
95106b9b3e0SSimon J. Gerraty 
95206b9b3e0SSimon J. Gerraty 	} else {
95306b9b3e0SSimon J. Gerraty 		what = arg[0] != '\0' ? UNEXPORT_NAMED : UNEXPORT_ALL;
95406b9b3e0SSimon J. Gerraty 		if (what == UNEXPORT_NAMED)
95506b9b3e0SSimon J. Gerraty 			varnames = FStr_InitRefer(arg);
9563955d011SMarcel Moolenaar 	}
9572c3632d1SSimon J. Gerraty 
95806b9b3e0SSimon J. Gerraty 	if (what != UNEXPORT_NAMED) {
9598c973ee2SSimon J. Gerraty 		char *expanded = Var_Subst("${.MAKE.EXPORTED:O:u}",
9608d5c8e21SSimon J. Gerraty 		    SCOPE_GLOBAL, VARE_EVAL);
96106b9b3e0SSimon J. Gerraty 		/* TODO: handle errors */
96206b9b3e0SSimon J. Gerraty 		varnames = FStr_InitOwn(expanded);
96306b9b3e0SSimon J. Gerraty 	}
96406b9b3e0SSimon J. Gerraty 
96506b9b3e0SSimon J. Gerraty 	*out_varnames = varnames;
96606b9b3e0SSimon J. Gerraty 	*out_what = what;
96706b9b3e0SSimon J. Gerraty }
96806b9b3e0SSimon J. Gerraty 
96906b9b3e0SSimon J. Gerraty static void
UnexportVar(Substring varname,UnexportWhat what)97012904384SSimon J. Gerraty UnexportVar(Substring varname, UnexportWhat what)
97106b9b3e0SSimon J. Gerraty {
97212904384SSimon J. Gerraty 	Var *v = VarFindSubstring(varname, SCOPE_GLOBAL, false);
97306b9b3e0SSimon J. Gerraty 	if (v == NULL) {
97412904384SSimon J. Gerraty 		DEBUG2(VAR, "Not unexporting \"%.*s\" (not found)\n",
97512904384SSimon J. Gerraty 		    (int)Substring_Length(varname), varname.start);
97606b9b3e0SSimon J. Gerraty 		return;
97706b9b3e0SSimon J. Gerraty 	}
97806b9b3e0SSimon J. Gerraty 
97912904384SSimon J. Gerraty 	DEBUG2(VAR, "Unexporting \"%.*s\"\n",
98012904384SSimon J. Gerraty 	    (int)Substring_Length(varname), varname.start);
981b0c40a00SSimon J. Gerraty 	if (what != UNEXPORT_ENV && v->exported && !v->reexport)
98206b9b3e0SSimon J. Gerraty 		unsetenv(v->name.str);
983b0c40a00SSimon J. Gerraty 	v->exported = false;
984b0c40a00SSimon J. Gerraty 	v->reexport = false;
9852c3632d1SSimon J. Gerraty 
98606b9b3e0SSimon J. Gerraty 	if (what == UNEXPORT_NAMED) {
98706b9b3e0SSimon J. Gerraty 		/* Remove the variable names from .MAKE.EXPORTED. */
988956e45f6SSimon J. Gerraty 		/* XXX: v->name is injected without escaping it */
989d5e0a182SSimon J. Gerraty 		char *expr = str_concat3(
990d5e0a182SSimon J. Gerraty 		    "${.MAKE.EXPORTED:N", v->name.str, "}");
9918d5c8e21SSimon J. Gerraty 		char *filtered = Var_Subst(expr, SCOPE_GLOBAL, VARE_EVAL);
992956e45f6SSimon J. Gerraty 		/* TODO: handle errors */
993d5e0a182SSimon J. Gerraty 		Global_Set(".MAKE.EXPORTED", filtered);
994d5e0a182SSimon J. Gerraty 		free(filtered);
9952c3632d1SSimon J. Gerraty 		free(expr);
9963955d011SMarcel Moolenaar 	}
9973955d011SMarcel Moolenaar }
9983955d011SMarcel Moolenaar 
99906b9b3e0SSimon J. Gerraty static void
UnexportVars(const char * varnames,UnexportWhat what)10006a7405f5SSimon J. Gerraty UnexportVars(const char *varnames, UnexportWhat what)
10013955d011SMarcel Moolenaar {
100206b9b3e0SSimon J. Gerraty 	size_t i;
100312904384SSimon J. Gerraty 	SubstringWords words;
100406b9b3e0SSimon J. Gerraty 
100506b9b3e0SSimon J. Gerraty 	if (what == UNEXPORT_ENV)
100606b9b3e0SSimon J. Gerraty 		ClearEnv();
100706b9b3e0SSimon J. Gerraty 
10086a7405f5SSimon J. Gerraty 	words = Substring_Words(varnames, false);
100912904384SSimon J. Gerraty 	for (i = 0; i < words.len; i++)
101012904384SSimon J. Gerraty 		UnexportVar(words.words[i], what);
101112904384SSimon J. Gerraty 	SubstringWords_Free(words);
101206b9b3e0SSimon J. Gerraty 
101306b9b3e0SSimon J. Gerraty 	if (what != UNEXPORT_NAMED)
10148c973ee2SSimon J. Gerraty 		Global_Delete(".MAKE.EXPORTED");
101506b9b3e0SSimon J. Gerraty }
101606b9b3e0SSimon J. Gerraty 
1017d5e0a182SSimon J. Gerraty /* Handle the .unexport and .unexport-env directives. */
101806b9b3e0SSimon J. Gerraty void
Var_UnExport(bool isEnv,const char * arg)1019b0c40a00SSimon J. Gerraty Var_UnExport(bool isEnv, const char *arg)
102006b9b3e0SSimon J. Gerraty {
102106b9b3e0SSimon J. Gerraty 	UnexportWhat what;
102206b9b3e0SSimon J. Gerraty 	FStr varnames;
102306b9b3e0SSimon J. Gerraty 
102406b9b3e0SSimon J. Gerraty 	GetVarnamesToUnexport(isEnv, arg, &varnames, &what);
10256a7405f5SSimon J. Gerraty 	UnexportVars(varnames.str, what);
102606b9b3e0SSimon J. Gerraty 	FStr_Done(&varnames);
102706b9b3e0SSimon J. Gerraty }
102806b9b3e0SSimon J. Gerraty 
102906b9b3e0SSimon J. Gerraty /* Set the variable to the value; the name is not expanded. */
1030dba7b0efSSimon J. Gerraty void
Var_SetWithFlags(GNode * scope,const char * name,const char * val,VarSetFlags flags)1031dba7b0efSSimon J. Gerraty Var_SetWithFlags(GNode *scope, const char *name, const char *val,
1032dba7b0efSSimon J. Gerraty 		 VarSetFlags flags)
103306b9b3e0SSimon J. Gerraty {
10343955d011SMarcel Moolenaar 	Var *v;
10352c3632d1SSimon J. Gerraty 
1036dba7b0efSSimon J. Gerraty 	assert(val != NULL);
1037dba7b0efSSimon J. Gerraty 	if (name[0] == '\0') {
1038d5e0a182SSimon J. Gerraty 		DEBUG3(VAR,
1039d5e0a182SSimon J. Gerraty 		    "%s: ignoring '%s = %s' as the variable name is empty\n",
1040d5e0a182SSimon J. Gerraty 		    scope->name, name, val);
1041dba7b0efSSimon J. Gerraty 		return;
1042dba7b0efSSimon J. Gerraty 	}
1043dba7b0efSSimon J. Gerraty 
1044d5e0a182SSimon J. Gerraty 	if (scope == SCOPE_GLOBAL
1045d5e0a182SSimon J. Gerraty 	    && VarFind(name, SCOPE_CMDLINE, false) != NULL) {
1046d5e0a182SSimon J. Gerraty 		/*
1047d5e0a182SSimon J. Gerraty 		 * The global variable would not be visible anywhere.
1048d5e0a182SSimon J. Gerraty 		 * Therefore, there is no point in setting it at all.
1049d5e0a182SSimon J. Gerraty 		 */
1050d5e0a182SSimon J. Gerraty 		DEBUG3(VAR,
1051d5e0a182SSimon J. Gerraty 		    "%s: ignoring '%s = %s' "
1052d5e0a182SSimon J. Gerraty 		    "due to a command line variable of the same name\n",
1053d5e0a182SSimon J. Gerraty 		    scope->name, name, val);
105406b9b3e0SSimon J. Gerraty 		return;
1055d5e0a182SSimon J. Gerraty 	}
10562c3632d1SSimon J. Gerraty 
1057956e45f6SSimon J. Gerraty 	/*
1058dba7b0efSSimon J. Gerraty 	 * Only look for a variable in the given scope since anything set
1059dba7b0efSSimon J. Gerraty 	 * here will override anything in a lower scope, so there's not much
1060b0c40a00SSimon J. Gerraty 	 * point in searching them all.
1061956e45f6SSimon J. Gerraty 	 */
1062b0c40a00SSimon J. Gerraty 	v = VarFind(name, scope, false);
10633955d011SMarcel Moolenaar 	if (v == NULL) {
1064dba7b0efSSimon J. Gerraty 		if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) {
106551ee2c1cSSimon J. Gerraty 			/*
1066548bfc56SSimon J. Gerraty 			 * This variable would normally prevent the same name
1067548bfc56SSimon J. Gerraty 			 * being added to SCOPE_GLOBAL, so delete it from
1068548bfc56SSimon J. Gerraty 			 * there if needed. Otherwise -V name may show the
1069548bfc56SSimon J. Gerraty 			 * wrong value.
1070b0c40a00SSimon J. Gerraty 			 *
1071b0c40a00SSimon J. Gerraty 			 * See ExistsInCmdline.
107251ee2c1cSSimon J. Gerraty 			 */
10736a7405f5SSimon J. Gerraty 			Var *gl = VarFind(name, SCOPE_GLOBAL, false);
1074759b177aSSimon J. Gerraty 			if (gl != NULL && strcmp(gl->val.data, val) == 0) {
1075759b177aSSimon J. Gerraty 				DEBUG3(VAR,
1076759b177aSSimon J. Gerraty 				    "%s: ignoring to override the global "
1077759b177aSSimon J. Gerraty 				    "'%s = %s' from a command line variable "
1078759b177aSSimon J. Gerraty 				    "as the value wouldn't change\n",
1079759b177aSSimon J. Gerraty 				    scope->name, name, val);
1080759b177aSSimon J. Gerraty 			} else if (gl != NULL && gl->readOnlyLoud)
10816a7405f5SSimon J. Gerraty 				Parse_Error(PARSE_FATAL,
10826a7405f5SSimon J. Gerraty 				    "Cannot override "
10836a7405f5SSimon J. Gerraty 				    "read-only global variable \"%s\" "
10846a7405f5SSimon J. Gerraty 				    "with a command line variable", name);
10856a7405f5SSimon J. Gerraty 			else
1086b0c40a00SSimon J. Gerraty 				Var_Delete(SCOPE_GLOBAL, name);
108751ee2c1cSSimon J. Gerraty 		}
108812904384SSimon J. Gerraty 		if (strcmp(name, ".SUFFIXES") == 0) {
1089d5e0a182SSimon J. Gerraty 			/* special: treat as read-only */
1090d5e0a182SSimon J. Gerraty 			DEBUG3(VAR,
1091d5e0a182SSimon J. Gerraty 			    "%s: ignoring '%s = %s' as it is read-only\n",
109212904384SSimon J. Gerraty 			    scope->name, name, val);
109312904384SSimon J. Gerraty 			return;
109412904384SSimon J. Gerraty 		}
1095b0c40a00SSimon J. Gerraty 		v = VarAdd(name, val, scope, flags);
10963955d011SMarcel Moolenaar 	} else {
10978d5c8e21SSimon J. Gerraty 		if (v->readOnlyLoud) {
10988d5c8e21SSimon J. Gerraty 			Parse_Error(PARSE_FATAL,
10998d5c8e21SSimon J. Gerraty 			    "Cannot overwrite \"%s\" as it is read-only",
11008d5c8e21SSimon J. Gerraty 			    name);
11018d5c8e21SSimon J. Gerraty 			return;
11028d5c8e21SSimon J. Gerraty 		}
1103b0c40a00SSimon J. Gerraty 		if (v->readOnly && !(flags & VAR_SET_READONLY)) {
1104d5e0a182SSimon J. Gerraty 			DEBUG3(VAR,
1105d5e0a182SSimon J. Gerraty 			    "%s: ignoring '%s = %s' as it is read-only\n",
1106dba7b0efSSimon J. Gerraty 			    scope->name, name, val);
110706b9b3e0SSimon J. Gerraty 			return;
11082c3632d1SSimon J. Gerraty 		}
11099f45a3c8SSimon J. Gerraty 		Buf_Clear(&v->val);
11102c3632d1SSimon J. Gerraty 		Buf_AddStr(&v->val, val);
11113955d011SMarcel Moolenaar 
11121d3f2ddcSSimon J. Gerraty 		DEBUG4(VAR, "%s: %s = %s%s\n",
11131d3f2ddcSSimon J. Gerraty 		    scope->name, name, val, ValueDescription(val));
1114b0c40a00SSimon J. Gerraty 		if (v->exported)
1115c59c3bf3SSimon J. Gerraty 			ExportVar(name, scope, VEM_PLAIN);
11163955d011SMarcel Moolenaar 	}
1117b0c40a00SSimon J. Gerraty 
1118d5e0a182SSimon J. Gerraty 	if (scope == SCOPE_CMDLINE) {
1119b0c40a00SSimon J. Gerraty 		v->fromCmd = true;
1120956e45f6SSimon J. Gerraty 
11213955d011SMarcel Moolenaar 		/*
1122d5e0a182SSimon J. Gerraty 		 * Any variables given on the command line are automatically
1123d5e0a182SSimon J. Gerraty 		 * exported to the environment (as per POSIX standard), except
1124d5e0a182SSimon J. Gerraty 		 * for internals.
1125d5e0a182SSimon J. Gerraty 		 */
1126c9f4001fSSimon J. Gerraty 		if (!(flags & VAR_SET_NO_EXPORT)) {
1127d5e0a182SSimon J. Gerraty 
1128d5e0a182SSimon J. Gerraty 			/*
1129d5e0a182SSimon J. Gerraty 			 * If requested, don't export these in the
1130d5e0a182SSimon J. Gerraty 			 * environment individually.  We still put
1131d5e0a182SSimon J. Gerraty 			 * them in .MAKEOVERRIDES so that the
1132d5e0a182SSimon J. Gerraty 			 * command-line settings continue to override
11333955d011SMarcel Moolenaar 			 * Makefile settings.
11343955d011SMarcel Moolenaar 			 */
1135c9f4001fSSimon J. Gerraty 			if (!opts.varNoExportEnv && name[0] != '.')
1136956e45f6SSimon J. Gerraty 				setenv(name, val, 1);
1137c9f4001fSSimon J. Gerraty 
1138c9f4001fSSimon J. Gerraty 			if (!(flags & VAR_SET_INTERNAL))
11398c973ee2SSimon J. Gerraty 				Global_Append(".MAKEOVERRIDES", name);
11403955d011SMarcel Moolenaar 		}
1141d5e0a182SSimon J. Gerraty 	}
1142b0c40a00SSimon J. Gerraty 
1143956e45f6SSimon J. Gerraty 	if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0)
1144e2eeea75SSimon J. Gerraty 		save_dollars = ParseBoolean(val, save_dollars);
11453955d011SMarcel Moolenaar 
11463955d011SMarcel Moolenaar 	if (v != NULL)
11479f45a3c8SSimon J. Gerraty 		VarFreeShortLived(v);
114806b9b3e0SSimon J. Gerraty }
114906b9b3e0SSimon J. Gerraty 
1150dba7b0efSSimon J. Gerraty void
Var_Set(GNode * scope,const char * name,const char * val)1151dba7b0efSSimon J. Gerraty Var_Set(GNode *scope, const char *name, const char *val)
1152dba7b0efSSimon J. Gerraty {
1153dba7b0efSSimon J. Gerraty 	Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
1154dba7b0efSSimon J. Gerraty }
1155dba7b0efSSimon J. Gerraty 
115606b9b3e0SSimon J. Gerraty /*
1157d5e0a182SSimon J. Gerraty  * In the scope, expand the variable name once, then create the variable or
1158d5e0a182SSimon J. Gerraty  * replace its value.
11593841c287SSimon J. Gerraty  */
11603841c287SSimon J. Gerraty void
Var_SetExpand(GNode * scope,const char * name,const char * val)1161dba7b0efSSimon J. Gerraty Var_SetExpand(GNode *scope, const char *name, const char *val)
11623841c287SSimon J. Gerraty {
11639f45a3c8SSimon J. Gerraty 	FStr varname = FStr_InitRefer(name);
11649f45a3c8SSimon J. Gerraty 
11659f45a3c8SSimon J. Gerraty 	assert(val != NULL);
11669f45a3c8SSimon J. Gerraty 
11678d5c8e21SSimon J. Gerraty 	Var_Expand(&varname, scope, VARE_EVAL);
11689f45a3c8SSimon J. Gerraty 
11699f45a3c8SSimon J. Gerraty 	if (varname.str[0] == '\0') {
1170d5e0a182SSimon J. Gerraty 		DEBUG4(VAR,
1171d5e0a182SSimon J. Gerraty 		    "%s: ignoring '%s = %s' "
1172d5e0a182SSimon J. Gerraty 		    "as the variable name '%s' expands to empty\n",
1173d5e0a182SSimon J. Gerraty 		    scope->name, varname.str, val, name);
11749f45a3c8SSimon J. Gerraty 	} else
11759f45a3c8SSimon J. Gerraty 		Var_SetWithFlags(scope, varname.str, val, VAR_SET_NONE);
11769f45a3c8SSimon J. Gerraty 
11779f45a3c8SSimon J. Gerraty 	FStr_Done(&varname);
1178dba7b0efSSimon J. Gerraty }
1179dba7b0efSSimon J. Gerraty 
1180dba7b0efSSimon J. Gerraty void
Global_Set(const char * name,const char * value)1181dba7b0efSSimon J. Gerraty Global_Set(const char *name, const char *value)
1182dba7b0efSSimon J. Gerraty {
1183dba7b0efSSimon J. Gerraty 	Var_Set(SCOPE_GLOBAL, name, value);
1184dba7b0efSSimon J. Gerraty }
1185dba7b0efSSimon J. Gerraty 
1186dba7b0efSSimon J. Gerraty void
Global_Delete(const char * name)1187dba7b0efSSimon J. Gerraty Global_Delete(const char *name)
1188dba7b0efSSimon J. Gerraty {
1189dba7b0efSSimon J. Gerraty 	Var_Delete(SCOPE_GLOBAL, name);
1190dba7b0efSSimon J. Gerraty }
1191dba7b0efSSimon J. Gerraty 
11924fde40d9SSimon J. Gerraty void
Global_Set_ReadOnly(const char * name,const char * value)11934fde40d9SSimon J. Gerraty Global_Set_ReadOnly(const char *name, const char *value)
11944fde40d9SSimon J. Gerraty {
11958d5c8e21SSimon J. Gerraty 	Var_SetWithFlags(SCOPE_GLOBAL, name, value, VAR_SET_NONE);
11968d5c8e21SSimon J. Gerraty 	VarFind(name, SCOPE_GLOBAL, false)->readOnlyLoud = true;
11974fde40d9SSimon J. Gerraty }
11984fde40d9SSimon J. Gerraty 
1199dba7b0efSSimon J. Gerraty /*
1200dba7b0efSSimon J. Gerraty  * Append the value to the named variable.
1201dba7b0efSSimon J. Gerraty  *
1202dba7b0efSSimon J. Gerraty  * If the variable doesn't exist, it is created.  Otherwise a single space
1203dba7b0efSSimon J. Gerraty  * and the given value are appended.
1204dba7b0efSSimon J. Gerraty  */
1205dba7b0efSSimon J. Gerraty void
Var_Append(GNode * scope,const char * name,const char * val)1206dba7b0efSSimon J. Gerraty Var_Append(GNode *scope, const char *name, const char *val)
1207dba7b0efSSimon J. Gerraty {
1208dba7b0efSSimon J. Gerraty 	Var *v;
1209dba7b0efSSimon J. Gerraty 
1210dba7b0efSSimon J. Gerraty 	v = VarFind(name, scope, scope == SCOPE_GLOBAL);
1211dba7b0efSSimon J. Gerraty 
1212dba7b0efSSimon J. Gerraty 	if (v == NULL) {
1213dba7b0efSSimon J. Gerraty 		Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
12148d5c8e21SSimon J. Gerraty 	} else if (v->readOnlyLoud) {
12158d5c8e21SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
12168d5c8e21SSimon J. Gerraty 		    "Cannot append to \"%s\" as it is read-only", name);
12178d5c8e21SSimon J. Gerraty 		return;
1218b0c40a00SSimon J. Gerraty 	} else if (v->readOnly) {
1219d5e0a182SSimon J. Gerraty 		DEBUG3(VAR, "%s: ignoring '%s += %s' as it is read-only\n",
1220d5e0a182SSimon J. Gerraty 		    scope->name, name, val);
1221b0c40a00SSimon J. Gerraty 	} else if (scope == SCOPE_CMDLINE || !v->fromCmd) {
1222dba7b0efSSimon J. Gerraty 		Buf_AddByte(&v->val, ' ');
1223dba7b0efSSimon J. Gerraty 		Buf_AddStr(&v->val, val);
1224dba7b0efSSimon J. Gerraty 
1225dba7b0efSSimon J. Gerraty 		DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, v->val.data);
1226dba7b0efSSimon J. Gerraty 
12279f45a3c8SSimon J. Gerraty 		if (v->fromEnvironment) {
12281d3f2ddcSSimon J. Gerraty 			/* See VarAdd. */
12291d3f2ddcSSimon J. Gerraty 			HashEntry *he =
12301d3f2ddcSSimon J. Gerraty 			    HashTable_CreateEntry(&scope->vars, name, NULL);
12311d3f2ddcSSimon J. Gerraty 			HashEntry_Set(he, v);
12321d3f2ddcSSimon J. Gerraty 			FStr_Done(&v->name);
12331d3f2ddcSSimon J. Gerraty 			v->name = FStr_InitRefer(/* aliased to */ he->key);
12349f45a3c8SSimon J. Gerraty 			v->shortLived = false;
12351d3f2ddcSSimon J. Gerraty 			v->fromEnvironment = false;
1236dba7b0efSSimon J. Gerraty 		}
1237dba7b0efSSimon J. Gerraty 	}
12383841c287SSimon J. Gerraty }
12393841c287SSimon J. Gerraty 
124006b9b3e0SSimon J. Gerraty /*
1241d5e0a182SSimon J. Gerraty  * In the scope, expand the variable name once.  If the variable exists in the
1242d5e0a182SSimon J. Gerraty  * scope, add a space and the value, otherwise set the variable to the value.
12433955d011SMarcel Moolenaar  *
1244d5e0a182SSimon J. Gerraty  * Appending to an environment variable only works in the global scope, that
1245d5e0a182SSimon J. Gerraty  * is, for variable assignments in makefiles, but not inside conditions or the
1246d5e0a182SSimon J. Gerraty  * commands of a target.
12473955d011SMarcel Moolenaar  */
12483955d011SMarcel Moolenaar void
Var_AppendExpand(GNode * scope,const char * name,const char * val)1249dba7b0efSSimon J. Gerraty Var_AppendExpand(GNode *scope, const char *name, const char *val)
12503955d011SMarcel Moolenaar {
1251b0c40a00SSimon J. Gerraty 	FStr xname = FStr_InitRefer(name);
12522c3632d1SSimon J. Gerraty 
12532c3632d1SSimon J. Gerraty 	assert(val != NULL);
12543955d011SMarcel Moolenaar 
12558d5c8e21SSimon J. Gerraty 	Var_Expand(&xname, scope, VARE_EVAL);
12569f45a3c8SSimon J. Gerraty 	if (xname.str != name && xname.str[0] == '\0')
1257d5e0a182SSimon J. Gerraty 		DEBUG4(VAR,
1258d5e0a182SSimon J. Gerraty 		    "%s: ignoring '%s += %s' "
1259d5e0a182SSimon J. Gerraty 		    "as the variable name '%s' expands to empty\n",
1260d5e0a182SSimon J. Gerraty 		    scope->name, xname.str, val, name);
12619f45a3c8SSimon J. Gerraty 	else
1262b0c40a00SSimon J. Gerraty 		Var_Append(scope, xname.str, val);
12633955d011SMarcel Moolenaar 
1264b0c40a00SSimon J. Gerraty 	FStr_Done(&xname);
12653955d011SMarcel Moolenaar }
12663955d011SMarcel Moolenaar 
1267dba7b0efSSimon J. Gerraty void
Global_Append(const char * name,const char * value)1268dba7b0efSSimon J. Gerraty Global_Append(const char *name, const char *value)
12693955d011SMarcel Moolenaar {
1270dba7b0efSSimon J. Gerraty 	Var_Append(SCOPE_GLOBAL, name, value);
1271956e45f6SSimon J. Gerraty }
12722c3632d1SSimon J. Gerraty 
1273b0c40a00SSimon J. Gerraty bool
Var_Exists(GNode * scope,const char * name)1274dba7b0efSSimon J. Gerraty Var_Exists(GNode *scope, const char *name)
1275dba7b0efSSimon J. Gerraty {
1276b0c40a00SSimon J. Gerraty 	Var *v = VarFind(name, scope, true);
12772c3632d1SSimon J. Gerraty 	if (v == NULL)
1278b0c40a00SSimon J. Gerraty 		return false;
12793841c287SSimon J. Gerraty 
12809f45a3c8SSimon J. Gerraty 	VarFreeShortLived(v);
1281b0c40a00SSimon J. Gerraty 	return true;
12823955d011SMarcel Moolenaar }
12833955d011SMarcel Moolenaar 
128406b9b3e0SSimon J. Gerraty /*
1285dba7b0efSSimon J. Gerraty  * See if the given variable exists, in the given scope or in other
1286dba7b0efSSimon J. Gerraty  * fallback scopes.
1287dba7b0efSSimon J. Gerraty  *
1288dba7b0efSSimon J. Gerraty  * Input:
128912904384SSimon J. Gerraty  *	scope		scope in which to start search
129012904384SSimon J. Gerraty  *	name		name of the variable to find, is expanded once
1291dba7b0efSSimon J. Gerraty  */
1292b0c40a00SSimon J. Gerraty bool
Var_ExistsExpand(GNode * scope,const char * name)1293dba7b0efSSimon J. Gerraty Var_ExistsExpand(GNode *scope, const char *name)
1294dba7b0efSSimon J. Gerraty {
1295dba7b0efSSimon J. Gerraty 	FStr varname = FStr_InitRefer(name);
1296b0c40a00SSimon J. Gerraty 	bool exists;
1297dba7b0efSSimon J. Gerraty 
12988d5c8e21SSimon J. Gerraty 	Var_Expand(&varname, scope, VARE_EVAL);
1299dba7b0efSSimon J. Gerraty 	exists = Var_Exists(scope, varname.str);
1300dba7b0efSSimon J. Gerraty 	FStr_Done(&varname);
1301dba7b0efSSimon J. Gerraty 	return exists;
1302dba7b0efSSimon J. Gerraty }
1303dba7b0efSSimon J. Gerraty 
1304dba7b0efSSimon J. Gerraty /*
1305dba7b0efSSimon J. Gerraty  * Return the unexpanded value of the given variable in the given scope,
1306d5e0a182SSimon J. Gerraty  * falling back to the command, global and environment scopes, in this order,
1307d5e0a182SSimon J. Gerraty  * but see the -e option.
13083955d011SMarcel Moolenaar  *
13093955d011SMarcel Moolenaar  * Input:
1310d5e0a182SSimon J. Gerraty  *	name		the name to find, is not expanded any further
13113955d011SMarcel Moolenaar  *
13123955d011SMarcel Moolenaar  * Results:
13132c3632d1SSimon J. Gerraty  *	The value if the variable exists, NULL if it doesn't.
1314b0c40a00SSimon J. Gerraty  *	The value is valid until the next modification to any variable.
13153955d011SMarcel Moolenaar  */
131606b9b3e0SSimon J. Gerraty FStr
Var_Value(GNode * scope,const char * name)1317dba7b0efSSimon J. Gerraty Var_Value(GNode *scope, const char *name)
13183955d011SMarcel Moolenaar {
1319b0c40a00SSimon J. Gerraty 	Var *v = VarFind(name, scope, true);
1320956e45f6SSimon J. Gerraty 	char *value;
13213955d011SMarcel Moolenaar 
13223841c287SSimon J. Gerraty 	if (v == NULL)
132306b9b3e0SSimon J. Gerraty 		return FStr_InitRefer(NULL);
13243841c287SSimon J. Gerraty 
13259f45a3c8SSimon J. Gerraty 	if (!v->shortLived)
1326b0c40a00SSimon J. Gerraty 		return FStr_InitRefer(v->val.data);
1327b0c40a00SSimon J. Gerraty 
13289f45a3c8SSimon J. Gerraty 	value = v->val.data;
13299f45a3c8SSimon J. Gerraty 	v->val.data = NULL;
13309f45a3c8SSimon J. Gerraty 	VarFreeShortLived(v);
13319f45a3c8SSimon J. Gerraty 
1332b0c40a00SSimon J. Gerraty 	return FStr_InitOwn(value);
1333956e45f6SSimon J. Gerraty }
1334956e45f6SSimon J. Gerraty 
1335d5e0a182SSimon J. Gerraty /* Set or clear the read-only attribute of the variable if it exists. */
13364fde40d9SSimon J. Gerraty void
Var_ReadOnly(const char * name,bool bf)13374fde40d9SSimon J. Gerraty Var_ReadOnly(const char *name, bool bf)
13384fde40d9SSimon J. Gerraty {
13394fde40d9SSimon J. Gerraty 	Var *v;
13404fde40d9SSimon J. Gerraty 
13414fde40d9SSimon J. Gerraty 	v = VarFind(name, SCOPE_GLOBAL, false);
13424fde40d9SSimon J. Gerraty 	if (v == NULL) {
13434fde40d9SSimon J. Gerraty 		DEBUG1(VAR, "Var_ReadOnly: %s not found\n", name);
13444fde40d9SSimon J. Gerraty 		return;
13454fde40d9SSimon J. Gerraty 	}
13464fde40d9SSimon J. Gerraty 	v->readOnly = bf;
13474fde40d9SSimon J. Gerraty 	DEBUG2(VAR, "Var_ReadOnly: %s %s\n", name, bf ? "true" : "false");
13484fde40d9SSimon J. Gerraty }
13494fde40d9SSimon J. Gerraty 
13504fde40d9SSimon J. Gerraty /*
135106b9b3e0SSimon J. Gerraty  * Return the unexpanded variable value from this node, without trying to look
1352dba7b0efSSimon J. Gerraty  * up the variable in any other scope.
135306b9b3e0SSimon J. Gerraty  */
1354956e45f6SSimon J. Gerraty const char *
GNode_ValueDirect(GNode * gn,const char * name)1355dba7b0efSSimon J. Gerraty GNode_ValueDirect(GNode *gn, const char *name)
1356956e45f6SSimon J. Gerraty {
1357b0c40a00SSimon J. Gerraty 	Var *v = VarFind(name, gn, false);
1358dba7b0efSSimon J. Gerraty 	return v != NULL ? v->val.data : NULL;
13593955d011SMarcel Moolenaar }
13603955d011SMarcel Moolenaar 
1361b0c40a00SSimon J. Gerraty static VarEvalMode
VarEvalMode_WithoutKeepDollar(VarEvalMode emode)1362b0c40a00SSimon J. Gerraty VarEvalMode_WithoutKeepDollar(VarEvalMode emode)
1363b0c40a00SSimon J. Gerraty {
13648d5c8e21SSimon J. Gerraty 	return emode == VARE_EVAL_KEEP_DOLLAR_AND_UNDEFINED
13658d5c8e21SSimon J. Gerraty 	    ? VARE_EVAL_KEEP_UNDEFINED : emode;
1366b0c40a00SSimon J. Gerraty }
1367b0c40a00SSimon J. Gerraty 
1368b0c40a00SSimon J. Gerraty static bool
VarEvalMode_ShouldEval(VarEvalMode emode)1369b0c40a00SSimon J. Gerraty VarEvalMode_ShouldEval(VarEvalMode emode)
1370b0c40a00SSimon J. Gerraty {
13718d5c8e21SSimon J. Gerraty 	return emode != VARE_PARSE;
1372b0c40a00SSimon J. Gerraty }
1373b0c40a00SSimon J. Gerraty 
1374b0c40a00SSimon J. Gerraty static bool
VarEvalMode_ShouldKeepUndef(VarEvalMode emode)1375b0c40a00SSimon J. Gerraty VarEvalMode_ShouldKeepUndef(VarEvalMode emode)
1376b0c40a00SSimon J. Gerraty {
13778d5c8e21SSimon J. Gerraty 	return emode == VARE_EVAL_KEEP_UNDEFINED ||
13788d5c8e21SSimon J. Gerraty 	       emode == VARE_EVAL_KEEP_DOLLAR_AND_UNDEFINED;
1379b0c40a00SSimon J. Gerraty }
1380b0c40a00SSimon J. Gerraty 
1381b0c40a00SSimon J. Gerraty static bool
VarEvalMode_ShouldKeepDollar(VarEvalMode emode)1382b0c40a00SSimon J. Gerraty VarEvalMode_ShouldKeepDollar(VarEvalMode emode)
1383b0c40a00SSimon J. Gerraty {
13848d5c8e21SSimon J. Gerraty 	return emode == VARE_EVAL_KEEP_DOLLAR_AND_UNDEFINED;
1385b0c40a00SSimon J. Gerraty }
1386b0c40a00SSimon J. Gerraty 
13873841c287SSimon J. Gerraty 
13882c3632d1SSimon J. Gerraty static void
SepBuf_Init(SepBuf * buf,char sep)13892c3632d1SSimon J. Gerraty SepBuf_Init(SepBuf *buf, char sep)
13903955d011SMarcel Moolenaar {
1391e2eeea75SSimon J. Gerraty 	Buf_InitSize(&buf->buf, 32);
1392b0c40a00SSimon J. Gerraty 	buf->needSep = false;
13932c3632d1SSimon J. Gerraty 	buf->sep = sep;
13943955d011SMarcel Moolenaar }
13953955d011SMarcel Moolenaar 
13962c3632d1SSimon J. Gerraty static void
SepBuf_Sep(SepBuf * buf)13972c3632d1SSimon J. Gerraty SepBuf_Sep(SepBuf *buf)
13982c3632d1SSimon J. Gerraty {
1399b0c40a00SSimon J. Gerraty 	buf->needSep = true;
14002c3632d1SSimon J. Gerraty }
14012c3632d1SSimon J. Gerraty 
14022c3632d1SSimon J. Gerraty static void
SepBuf_AddBytes(SepBuf * buf,const char * mem,size_t mem_size)14032c3632d1SSimon J. Gerraty SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size)
14042c3632d1SSimon J. Gerraty {
14052c3632d1SSimon J. Gerraty 	if (mem_size == 0)
14062c3632d1SSimon J. Gerraty 		return;
14072c3632d1SSimon J. Gerraty 	if (buf->needSep && buf->sep != '\0') {
14082c3632d1SSimon J. Gerraty 		Buf_AddByte(&buf->buf, buf->sep);
1409b0c40a00SSimon J. Gerraty 		buf->needSep = false;
14102c3632d1SSimon J. Gerraty 	}
14112c3632d1SSimon J. Gerraty 	Buf_AddBytes(&buf->buf, mem, mem_size);
14122c3632d1SSimon J. Gerraty }
14132c3632d1SSimon J. Gerraty 
14142c3632d1SSimon J. Gerraty static void
SepBuf_AddRange(SepBuf * buf,const char * start,const char * end)1415148ee845SSimon J. Gerraty SepBuf_AddRange(SepBuf *buf, const char *start, const char *end)
14162c3632d1SSimon J. Gerraty {
14172c3632d1SSimon J. Gerraty 	SepBuf_AddBytes(buf, start, (size_t)(end - start));
14182c3632d1SSimon J. Gerraty }
14192c3632d1SSimon J. Gerraty 
14202c3632d1SSimon J. Gerraty static void
SepBuf_AddStr(SepBuf * buf,const char * str)14212c3632d1SSimon J. Gerraty SepBuf_AddStr(SepBuf *buf, const char *str)
14222c3632d1SSimon J. Gerraty {
14232c3632d1SSimon J. Gerraty 	SepBuf_AddBytes(buf, str, strlen(str));
14242c3632d1SSimon J. Gerraty }
14252c3632d1SSimon J. Gerraty 
1426b0c40a00SSimon J. Gerraty static void
SepBuf_AddSubstring(SepBuf * buf,Substring sub)1427b0c40a00SSimon J. Gerraty SepBuf_AddSubstring(SepBuf *buf, Substring sub)
1428b0c40a00SSimon J. Gerraty {
1429148ee845SSimon J. Gerraty 	SepBuf_AddRange(buf, sub.start, sub.end);
1430b0c40a00SSimon J. Gerraty }
1431b0c40a00SSimon J. Gerraty 
14322c3632d1SSimon J. Gerraty static char *
SepBuf_DoneData(SepBuf * buf)1433dba7b0efSSimon J. Gerraty SepBuf_DoneData(SepBuf *buf)
14342c3632d1SSimon J. Gerraty {
1435dba7b0efSSimon J. Gerraty 	return Buf_DoneData(&buf->buf);
14362c3632d1SSimon J. Gerraty }
14372c3632d1SSimon J. Gerraty 
14382c3632d1SSimon J. Gerraty 
143906b9b3e0SSimon J. Gerraty /*
1440d5e0a182SSimon J. Gerraty  * This callback for ModifyWords gets a single word from an expression
1441956e45f6SSimon J. Gerraty  * and typically adds a modification of this word to the buffer. It may also
1442956e45f6SSimon J. Gerraty  * do nothing or add several words.
1443956e45f6SSimon J. Gerraty  *
1444b0c40a00SSimon J. Gerraty  * For example, when evaluating the modifier ':M*b' in ${:Ua b c:M*b}, the
1445b0c40a00SSimon J. Gerraty  * callback is called 3 times, once for "a", "b" and "c".
1446b0c40a00SSimon J. Gerraty  *
1447b0c40a00SSimon J. Gerraty  * Some ModifyWord functions assume that they are always passed a
1448b0c40a00SSimon J. Gerraty  * null-terminated substring, which is currently guaranteed but may change in
1449b0c40a00SSimon J. Gerraty  * the future.
145006b9b3e0SSimon J. Gerraty  */
1451b0c40a00SSimon J. Gerraty typedef void (*ModifyWordProc)(Substring word, SepBuf *buf, void *data);
14522c3632d1SSimon J. Gerraty 
14532c3632d1SSimon J. Gerraty 
14542c3632d1SSimon J. Gerraty static void
ModifyWord_Head(Substring word,SepBuf * buf,void * dummy MAKE_ATTR_UNUSED)1455b0c40a00SSimon J. Gerraty ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
14562c3632d1SSimon J. Gerraty {
1457b0c40a00SSimon J. Gerraty 	SepBuf_AddSubstring(buf, Substring_Dirname(word));
14582c3632d1SSimon J. Gerraty }
14592c3632d1SSimon J. Gerraty 
14602c3632d1SSimon J. Gerraty static void
ModifyWord_Tail(Substring word,SepBuf * buf,void * dummy MAKE_ATTR_UNUSED)1461b0c40a00SSimon J. Gerraty ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
14623955d011SMarcel Moolenaar {
1463b0c40a00SSimon J. Gerraty 	SepBuf_AddSubstring(buf, Substring_Basename(word));
14643955d011SMarcel Moolenaar }
14653955d011SMarcel Moolenaar 
14662c3632d1SSimon J. Gerraty static void
ModifyWord_Suffix(Substring word,SepBuf * buf,void * dummy MAKE_ATTR_UNUSED)1467b0c40a00SSimon J. Gerraty ModifyWord_Suffix(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
14683955d011SMarcel Moolenaar {
1469d5e0a182SSimon J. Gerraty 	const char *lastDot = Substring_FindLast(word, '.');
147006b9b3e0SSimon J. Gerraty 	if (lastDot != NULL)
1471148ee845SSimon J. Gerraty 		SepBuf_AddRange(buf, lastDot + 1, word.end);
14722d147b47SSimon J. Gerraty }
14732d147b47SSimon J. Gerraty 
14742c3632d1SSimon J. Gerraty static void
ModifyWord_Root(Substring word,SepBuf * buf,void * dummy MAKE_ATTR_UNUSED)1475b0c40a00SSimon J. Gerraty ModifyWord_Root(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
14763841c287SSimon J. Gerraty {
1477b0c40a00SSimon J. Gerraty 	const char *lastDot, *end;
1478b0c40a00SSimon J. Gerraty 
1479d5e0a182SSimon J. Gerraty 	lastDot = Substring_FindLast(word, '.');
1480b0c40a00SSimon J. Gerraty 	end = lastDot != NULL ? lastDot : word.end;
1481148ee845SSimon J. Gerraty 	SepBuf_AddRange(buf, word.start, end);
14823955d011SMarcel Moolenaar }
14833955d011SMarcel Moolenaar 
1484b0c40a00SSimon J. Gerraty struct ModifyWord_SysVSubstArgs {
1485dba7b0efSSimon J. Gerraty 	GNode *scope;
1486b0c40a00SSimon J. Gerraty 	Substring lhsPrefix;
1487b0c40a00SSimon J. Gerraty 	bool lhsPercent;
1488b0c40a00SSimon J. Gerraty 	Substring lhsSuffix;
14892c3632d1SSimon J. Gerraty 	const char *rhs;
1490956e45f6SSimon J. Gerraty };
14912c3632d1SSimon J. Gerraty 
14922c3632d1SSimon J. Gerraty static void
ModifyWord_SysVSubst(Substring word,SepBuf * buf,void * data)1493b0c40a00SSimon J. Gerraty ModifyWord_SysVSubst(Substring word, SepBuf *buf, void *data)
14942c3632d1SSimon J. Gerraty {
1495b0c40a00SSimon J. Gerraty 	const struct ModifyWord_SysVSubstArgs *args = data;
1496b0c40a00SSimon J. Gerraty 	FStr rhs;
14972c3632d1SSimon J. Gerraty 	const char *percent;
14982c3632d1SSimon J. Gerraty 
1499b0c40a00SSimon J. Gerraty 	if (Substring_IsEmpty(word))
15002c3632d1SSimon J. Gerraty 		return;
15012c3632d1SSimon J. Gerraty 
15021d3f2ddcSSimon J. Gerraty 	if (!Substring_HasPrefix(word, args->lhsPrefix) ||
15031d3f2ddcSSimon J. Gerraty 	    !Substring_HasSuffix(word, args->lhsSuffix)) {
15041d3f2ddcSSimon J. Gerraty 		SepBuf_AddSubstring(buf, word);
15051d3f2ddcSSimon J. Gerraty 		return;
15061d3f2ddcSSimon J. Gerraty 	}
15072c3632d1SSimon J. Gerraty 
1508b0c40a00SSimon J. Gerraty 	rhs = FStr_InitRefer(args->rhs);
15098d5c8e21SSimon J. Gerraty 	Var_Expand(&rhs, args->scope, VARE_EVAL);
15102c3632d1SSimon J. Gerraty 
1511b0c40a00SSimon J. Gerraty 	percent = args->lhsPercent ? strchr(rhs.str, '%') : NULL;
15122c3632d1SSimon J. Gerraty 
1513b0c40a00SSimon J. Gerraty 	if (percent != NULL)
1514148ee845SSimon J. Gerraty 		SepBuf_AddRange(buf, rhs.str, percent);
1515b0c40a00SSimon J. Gerraty 	if (percent != NULL || !args->lhsPercent)
1516148ee845SSimon J. Gerraty 		SepBuf_AddRange(buf,
1517b0c40a00SSimon J. Gerraty 		    word.start + Substring_Length(args->lhsPrefix),
1518b0c40a00SSimon J. Gerraty 		    word.end - Substring_Length(args->lhsSuffix));
1519b0c40a00SSimon J. Gerraty 	SepBuf_AddStr(buf, percent != NULL ? percent + 1 : rhs.str);
1520b0c40a00SSimon J. Gerraty 
1521b0c40a00SSimon J. Gerraty 	FStr_Done(&rhs);
15223955d011SMarcel Moolenaar }
15233955d011SMarcel Moolenaar 
1524b0c40a00SSimon J. Gerraty static const char *
Substring_Find(Substring haystack,Substring needle)1525b0c40a00SSimon J. Gerraty Substring_Find(Substring haystack, Substring needle)
1526b0c40a00SSimon J. Gerraty {
1527b0c40a00SSimon J. Gerraty 	size_t len, needleLen, i;
1528b0c40a00SSimon J. Gerraty 
1529b0c40a00SSimon J. Gerraty 	len = Substring_Length(haystack);
1530b0c40a00SSimon J. Gerraty 	needleLen = Substring_Length(needle);
1531b0c40a00SSimon J. Gerraty 	for (i = 0; i + needleLen <= len; i++)
1532b0c40a00SSimon J. Gerraty 		if (memcmp(haystack.start + i, needle.start, needleLen) == 0)
1533b0c40a00SSimon J. Gerraty 			return haystack.start + i;
1534b0c40a00SSimon J. Gerraty 	return NULL;
1535b0c40a00SSimon J. Gerraty }
1536b0c40a00SSimon J. Gerraty 
1537d5e0a182SSimon J. Gerraty struct ModifyWord_SubstArgs {
1538d5e0a182SSimon J. Gerraty 	Substring lhs;
1539d5e0a182SSimon J. Gerraty 	Substring rhs;
1540d5e0a182SSimon J. Gerraty 	PatternFlags pflags;
1541d5e0a182SSimon J. Gerraty 	bool matched;
1542d5e0a182SSimon J. Gerraty };
1543d5e0a182SSimon J. Gerraty 
15442c3632d1SSimon J. Gerraty static void
ModifyWord_Subst(Substring word,SepBuf * buf,void * data)1545b0c40a00SSimon J. Gerraty ModifyWord_Subst(Substring word, SepBuf *buf, void *data)
15463955d011SMarcel Moolenaar {
1547956e45f6SSimon J. Gerraty 	struct ModifyWord_SubstArgs *args = data;
1548b0c40a00SSimon J. Gerraty 	size_t wordLen, lhsLen;
1549d5e0a182SSimon J. Gerraty 	const char *match;
15503955d011SMarcel Moolenaar 
1551b0c40a00SSimon J. Gerraty 	wordLen = Substring_Length(word);
1552dba7b0efSSimon J. Gerraty 	if (args->pflags.subOnce && args->matched)
15533955d011SMarcel Moolenaar 		goto nosub;
15543955d011SMarcel Moolenaar 
1555b0c40a00SSimon J. Gerraty 	lhsLen = Substring_Length(args->lhs);
1556dba7b0efSSimon J. Gerraty 	if (args->pflags.anchorStart) {
1557b0c40a00SSimon J. Gerraty 		if (wordLen < lhsLen ||
1558b0c40a00SSimon J. Gerraty 		    memcmp(word.start, args->lhs.start, lhsLen) != 0)
15592c3632d1SSimon J. Gerraty 			goto nosub;
15602c3632d1SSimon J. Gerraty 
1561b0c40a00SSimon J. Gerraty 		if (args->pflags.anchorEnd && wordLen != lhsLen)
15622c3632d1SSimon J. Gerraty 			goto nosub;
15632c3632d1SSimon J. Gerraty 
1564956e45f6SSimon J. Gerraty 		/* :S,^prefix,replacement, or :S,^whole$,replacement, */
1565b0c40a00SSimon J. Gerraty 		SepBuf_AddSubstring(buf, args->rhs);
1566d5e0a182SSimon J. Gerraty 		SepBuf_AddRange(buf, word.start + lhsLen, word.end);
1567b0c40a00SSimon J. Gerraty 		args->matched = true;
15682c3632d1SSimon J. Gerraty 		return;
15693955d011SMarcel Moolenaar 	}
15702c3632d1SSimon J. Gerraty 
1571dba7b0efSSimon J. Gerraty 	if (args->pflags.anchorEnd) {
1572b0c40a00SSimon J. Gerraty 		if (wordLen < lhsLen)
15732c3632d1SSimon J. Gerraty 			goto nosub;
1574d5e0a182SSimon J. Gerraty 		if (memcmp(word.end - lhsLen, args->lhs.start, lhsLen) != 0)
15752c3632d1SSimon J. Gerraty 			goto nosub;
15762c3632d1SSimon J. Gerraty 
15772c3632d1SSimon J. Gerraty 		/* :S,suffix$,replacement, */
1578d5e0a182SSimon J. Gerraty 		SepBuf_AddRange(buf, word.start, word.end - lhsLen);
1579b0c40a00SSimon J. Gerraty 		SepBuf_AddSubstring(buf, args->rhs);
1580b0c40a00SSimon J. Gerraty 		args->matched = true;
15812c3632d1SSimon J. Gerraty 		return;
15823955d011SMarcel Moolenaar 	}
15832c3632d1SSimon J. Gerraty 
1584b0c40a00SSimon J. Gerraty 	if (Substring_IsEmpty(args->lhs))
1585956e45f6SSimon J. Gerraty 		goto nosub;
1586956e45f6SSimon J. Gerraty 
15872c3632d1SSimon J. Gerraty 	/* unanchored case, may match more than once */
1588b0c40a00SSimon J. Gerraty 	while ((match = Substring_Find(word, args->lhs)) != NULL) {
1589148ee845SSimon J. Gerraty 		SepBuf_AddRange(buf, word.start, match);
1590b0c40a00SSimon J. Gerraty 		SepBuf_AddSubstring(buf, args->rhs);
1591b0c40a00SSimon J. Gerraty 		args->matched = true;
1592b0c40a00SSimon J. Gerraty 		word.start = match + lhsLen;
1593b0c40a00SSimon J. Gerraty 		if (Substring_IsEmpty(word) || !args->pflags.subGlobal)
15942c3632d1SSimon J. Gerraty 			break;
15953955d011SMarcel Moolenaar 	}
15963955d011SMarcel Moolenaar nosub:
1597b0c40a00SSimon J. Gerraty 	SepBuf_AddSubstring(buf, word);
15983955d011SMarcel Moolenaar }
15993955d011SMarcel Moolenaar 
1600c59c3bf3SSimon J. Gerraty #ifdef HAVE_REGEX_H
16012c3632d1SSimon J. Gerraty /* Print the error caused by a regcomp or regexec call. */
16023955d011SMarcel Moolenaar static void
RegexError(int reerr,const regex_t * pat,const char * str)1603148ee845SSimon J. Gerraty RegexError(int reerr, const regex_t *pat, const char *str)
16043955d011SMarcel Moolenaar {
1605e2eeea75SSimon J. Gerraty 	size_t errlen = regerror(reerr, pat, NULL, 0);
16062c3632d1SSimon J. Gerraty 	char *errbuf = bmake_malloc(errlen);
1607be19d90bSSimon J. Gerraty 	regerror(reerr, pat, errbuf, errlen);
160822619282SSimon J. Gerraty 	Parse_Error(PARSE_FATAL, "%s: %s", str, errbuf);
16093955d011SMarcel Moolenaar 	free(errbuf);
16103955d011SMarcel Moolenaar }
16113955d011SMarcel Moolenaar 
16129f45a3c8SSimon J. Gerraty /* In the modifier ':C', replace a backreference from \0 to \9. */
161312904384SSimon J. Gerraty static void
RegexReplaceBackref(char ref,SepBuf * buf,const char * wp,const regmatch_t * m,size_t nsub)16149f45a3c8SSimon J. Gerraty RegexReplaceBackref(char ref, SepBuf *buf, const char *wp,
161512904384SSimon J. Gerraty 		    const regmatch_t *m, size_t nsub)
161612904384SSimon J. Gerraty {
16170b46a53aSSimon J. Gerraty 	unsigned n = (unsigned)ref - '0';
161812904384SSimon J. Gerraty 
16199f45a3c8SSimon J. Gerraty 	if (n >= nsub)
162022619282SSimon J. Gerraty 		Parse_Error(PARSE_FATAL, "No subexpression \\%u", n);
16219f45a3c8SSimon J. Gerraty 	else if (m[n].rm_so == -1) {
16229f45a3c8SSimon J. Gerraty 		if (opts.strict)
162312904384SSimon J. Gerraty 			Error("No match for subexpression \\%u", n);
162412904384SSimon J. Gerraty 	} else {
1625148ee845SSimon J. Gerraty 		SepBuf_AddRange(buf,
162612904384SSimon J. Gerraty 		    wp + (size_t)m[n].rm_so,
162712904384SSimon J. Gerraty 		    wp + (size_t)m[n].rm_eo);
162812904384SSimon J. Gerraty 	}
162912904384SSimon J. Gerraty }
16309f45a3c8SSimon J. Gerraty 
16319f45a3c8SSimon J. Gerraty /*
16329f45a3c8SSimon J. Gerraty  * The regular expression matches the word; now add the replacement to the
16339f45a3c8SSimon J. Gerraty  * buffer, taking back-references from 'wp'.
16349f45a3c8SSimon J. Gerraty  */
16359f45a3c8SSimon J. Gerraty static void
RegexReplace(Substring replace,SepBuf * buf,const char * wp,const regmatch_t * m,size_t nsub)16369f45a3c8SSimon J. Gerraty RegexReplace(Substring replace, SepBuf *buf, const char *wp,
16379f45a3c8SSimon J. Gerraty 	     const regmatch_t *m, size_t nsub)
16389f45a3c8SSimon J. Gerraty {
16399f45a3c8SSimon J. Gerraty 	const char *rp;
16409f45a3c8SSimon J. Gerraty 
16419f45a3c8SSimon J. Gerraty 	for (rp = replace.start; rp != replace.end; rp++) {
16429f45a3c8SSimon J. Gerraty 		if (*rp == '\\' && rp + 1 != replace.end &&
16439f45a3c8SSimon J. Gerraty 		    (rp[1] == '&' || rp[1] == '\\'))
16449f45a3c8SSimon J. Gerraty 			SepBuf_AddBytes(buf, ++rp, 1);
16459f45a3c8SSimon J. Gerraty 		else if (*rp == '\\' && rp + 1 != replace.end &&
16469f45a3c8SSimon J. Gerraty 			 ch_isdigit(rp[1]))
16479f45a3c8SSimon J. Gerraty 			RegexReplaceBackref(*++rp, buf, wp, m, nsub);
16489f45a3c8SSimon J. Gerraty 		else if (*rp == '&') {
1649148ee845SSimon J. Gerraty 			SepBuf_AddRange(buf,
16509f45a3c8SSimon J. Gerraty 			    wp + (size_t)m[0].rm_so,
16519f45a3c8SSimon J. Gerraty 			    wp + (size_t)m[0].rm_eo);
16529f45a3c8SSimon J. Gerraty 		} else
16539f45a3c8SSimon J. Gerraty 			SepBuf_AddBytes(buf, rp, 1);
16549f45a3c8SSimon J. Gerraty 	}
165512904384SSimon J. Gerraty }
165612904384SSimon J. Gerraty 
1657956e45f6SSimon J. Gerraty struct ModifyWord_SubstRegexArgs {
16582c3632d1SSimon J. Gerraty 	regex_t re;
16592c3632d1SSimon J. Gerraty 	size_t nsub;
16609f45a3c8SSimon J. Gerraty 	Substring replace;
1661b0c40a00SSimon J. Gerraty 	PatternFlags pflags;
1662b0c40a00SSimon J. Gerraty 	bool matched;
1663956e45f6SSimon J. Gerraty };
16642c3632d1SSimon J. Gerraty 
16652c3632d1SSimon J. Gerraty static void
ModifyWord_SubstRegex(Substring word,SepBuf * buf,void * data)1666b0c40a00SSimon J. Gerraty ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data)
16673955d011SMarcel Moolenaar {
1668956e45f6SSimon J. Gerraty 	struct ModifyWord_SubstRegexArgs *args = data;
16693955d011SMarcel Moolenaar 	int xrv;
1670b0c40a00SSimon J. Gerraty 	const char *wp;
16713955d011SMarcel Moolenaar 	int flags = 0;
16722c3632d1SSimon J. Gerraty 	regmatch_t m[10];
16733955d011SMarcel Moolenaar 
1674b0c40a00SSimon J. Gerraty 	assert(word.end[0] == '\0');	/* assume null-terminated word */
1675b0c40a00SSimon J. Gerraty 	wp = word.start;
1676dba7b0efSSimon J. Gerraty 	if (args->pflags.subOnce && args->matched)
167712904384SSimon J. Gerraty 		goto no_match;
16783955d011SMarcel Moolenaar 
167912904384SSimon J. Gerraty again:
16802c3632d1SSimon J. Gerraty 	xrv = regexec(&args->re, wp, args->nsub, m, flags);
168112904384SSimon J. Gerraty 	if (xrv == 0)
168212904384SSimon J. Gerraty 		goto ok;
168312904384SSimon J. Gerraty 	if (xrv != REG_NOMATCH)
1684148ee845SSimon J. Gerraty 		RegexError(xrv, &args->re, "Unexpected regex error");
168512904384SSimon J. Gerraty no_match:
1686148ee845SSimon J. Gerraty 	SepBuf_AddRange(buf, wp, word.end);
168712904384SSimon J. Gerraty 	return;
16883955d011SMarcel Moolenaar 
168912904384SSimon J. Gerraty ok:
1690b0c40a00SSimon J. Gerraty 	args->matched = true;
16912c3632d1SSimon J. Gerraty 	SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so);
16923955d011SMarcel Moolenaar 
169312904384SSimon J. Gerraty 	RegexReplace(args->replace, buf, wp, m, args->nsub);
1694b0c40a00SSimon J. Gerraty 
169512904384SSimon J. Gerraty 	wp += (size_t)m[0].rm_eo;
1696dba7b0efSSimon J. Gerraty 	if (args->pflags.subGlobal) {
16973955d011SMarcel Moolenaar 		flags |= REG_NOTBOL;
1698d5e0a182SSimon J. Gerraty 		if (m[0].rm_so == 0 && m[0].rm_eo == 0 && *wp != '\0') {
16992c3632d1SSimon J. Gerraty 			SepBuf_AddBytes(buf, wp, 1);
17003955d011SMarcel Moolenaar 			wp++;
17013955d011SMarcel Moolenaar 		}
170206b9b3e0SSimon J. Gerraty 		if (*wp != '\0')
170312904384SSimon J. Gerraty 			goto again;
17043955d011SMarcel Moolenaar 	}
170506b9b3e0SSimon J. Gerraty 	if (*wp != '\0')
17062c3632d1SSimon J. Gerraty 		SepBuf_AddStr(buf, wp);
17073955d011SMarcel Moolenaar }
17083955d011SMarcel Moolenaar #endif
17093955d011SMarcel Moolenaar 
1710956e45f6SSimon J. Gerraty struct ModifyWord_LoopArgs {
1711dba7b0efSSimon J. Gerraty 	GNode *scope;
1712b0c40a00SSimon J. Gerraty 	const char *var;	/* name of the temporary variable */
1713b0c40a00SSimon J. Gerraty 	const char *body;	/* string to expand */
1714b0c40a00SSimon J. Gerraty 	VarEvalMode emode;
1715956e45f6SSimon J. Gerraty };
17163955d011SMarcel Moolenaar 
17172c3632d1SSimon J. Gerraty static void
ModifyWord_Loop(Substring word,SepBuf * buf,void * data)1718b0c40a00SSimon J. Gerraty ModifyWord_Loop(Substring word, SepBuf *buf, void *data)
17192c3632d1SSimon J. Gerraty {
1720956e45f6SSimon J. Gerraty 	const struct ModifyWord_LoopArgs *args;
17212c3632d1SSimon J. Gerraty 	char *s;
17222c3632d1SSimon J. Gerraty 
1723b0c40a00SSimon J. Gerraty 	if (Substring_IsEmpty(word))
17242c3632d1SSimon J. Gerraty 		return;
17252c3632d1SSimon J. Gerraty 
17262c3632d1SSimon J. Gerraty 	args = data;
1727b0c40a00SSimon J. Gerraty 	assert(word.end[0] == '\0');	/* assume null-terminated word */
1728b0c40a00SSimon J. Gerraty 	Var_SetWithFlags(args->scope, args->var, word.start,
1729dba7b0efSSimon J. Gerraty 	    VAR_SET_NO_EXPORT);
17308c973ee2SSimon J. Gerraty 	s = Var_Subst(args->body, args->scope, args->emode);
1731956e45f6SSimon J. Gerraty 	/* TODO: handle errors */
17322c3632d1SSimon J. Gerraty 
1733d5e0a182SSimon J. Gerraty 	DEBUG2(VAR, "ModifyWord_Loop: expand \"%s\" to \"%s\"\n",
1734d5e0a182SSimon J. Gerraty 	    args->body, s);
17352c3632d1SSimon J. Gerraty 
1736956e45f6SSimon J. Gerraty 	if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n'))
1737b0c40a00SSimon J. Gerraty 		buf->needSep = false;
17382c3632d1SSimon J. Gerraty 	SepBuf_AddStr(buf, s);
1739e1cee40dSSimon J. Gerraty 	free(s);
17403955d011SMarcel Moolenaar }
17413955d011SMarcel Moolenaar 
17423955d011SMarcel Moolenaar 
174306b9b3e0SSimon J. Gerraty /*
174406b9b3e0SSimon J. Gerraty  * The :[first..last] modifier selects words from the expression.
174506b9b3e0SSimon J. Gerraty  * It can also reverse the words.
174606b9b3e0SSimon J. Gerraty  */
17473955d011SMarcel Moolenaar static char *
VarSelectWords(const char * str,int first,int last,char sep,bool oneBigWord)1748b0c40a00SSimon J. Gerraty VarSelectWords(const char *str, int first, int last,
1749b0c40a00SSimon J. Gerraty 	       char sep, bool oneBigWord)
17503955d011SMarcel Moolenaar {
175112904384SSimon J. Gerraty 	SubstringWords words;
1752956e45f6SSimon J. Gerraty 	int len, start, end, step;
17532c3632d1SSimon J. Gerraty 	int i;
17543955d011SMarcel Moolenaar 
17552c3632d1SSimon J. Gerraty 	SepBuf buf;
17562c3632d1SSimon J. Gerraty 	SepBuf_Init(&buf, sep);
17573955d011SMarcel Moolenaar 
17582c3632d1SSimon J. Gerraty 	if (oneBigWord) {
175912904384SSimon J. Gerraty 		/* fake what Substring_Words() would do */
17602c3632d1SSimon J. Gerraty 		words.len = 1;
176112904384SSimon J. Gerraty 		words.words = bmake_malloc(sizeof(words.words[0]));
176212904384SSimon J. Gerraty 		words.freeIt = NULL;
176312904384SSimon J. Gerraty 		words.words[0] = Substring_InitStr(str); /* no need to copy */
17643955d011SMarcel Moolenaar 	} else {
176512904384SSimon J. Gerraty 		words = Substring_Words(str, false);
17663955d011SMarcel Moolenaar 	}
17673955d011SMarcel Moolenaar 
1768d5e0a182SSimon J. Gerraty 	/* Convert -1 to len, -2 to (len - 1), etc. */
1769956e45f6SSimon J. Gerraty 	len = (int)words.len;
17702c3632d1SSimon J. Gerraty 	if (first < 0)
1771956e45f6SSimon J. Gerraty 		first += len + 1;
17722c3632d1SSimon J. Gerraty 	if (last < 0)
1773956e45f6SSimon J. Gerraty 		last += len + 1;
17743955d011SMarcel Moolenaar 
17752c3632d1SSimon J. Gerraty 	if (first > last) {
1776956e45f6SSimon J. Gerraty 		start = (first > len ? len : first) - 1;
1777956e45f6SSimon J. Gerraty 		end = last < 1 ? 0 : last - 1;
17783955d011SMarcel Moolenaar 		step = -1;
17793955d011SMarcel Moolenaar 	} else {
1780956e45f6SSimon J. Gerraty 		start = first < 1 ? 0 : first - 1;
1781956e45f6SSimon J. Gerraty 		end = last > len ? len : last;
17823955d011SMarcel Moolenaar 		step = 1;
17833955d011SMarcel Moolenaar 	}
17843955d011SMarcel Moolenaar 
17852c3632d1SSimon J. Gerraty 	for (i = start; (step < 0) == (i >= end); i += step) {
178612904384SSimon J. Gerraty 		SepBuf_AddSubstring(&buf, words.words[i]);
17872c3632d1SSimon J. Gerraty 		SepBuf_Sep(&buf);
17883955d011SMarcel Moolenaar 	}
17893955d011SMarcel Moolenaar 
179012904384SSimon J. Gerraty 	SubstringWords_Free(words);
17913955d011SMarcel Moolenaar 
1792dba7b0efSSimon J. Gerraty 	return SepBuf_DoneData(&buf);
17933955d011SMarcel Moolenaar }
17943955d011SMarcel Moolenaar 
17953955d011SMarcel Moolenaar 
17962c3632d1SSimon J. Gerraty static void
ModifyWord_Realpath(Substring word,SepBuf * buf,void * data MAKE_ATTR_UNUSED)1797b0c40a00SSimon J. Gerraty ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
17983955d011SMarcel Moolenaar {
17993955d011SMarcel Moolenaar 	struct stat st;
18003955d011SMarcel Moolenaar 	char rbuf[MAXPATHLEN];
1801b0c40a00SSimon J. Gerraty 	const char *rp;
18023955d011SMarcel Moolenaar 
1803b0c40a00SSimon J. Gerraty 	assert(word.end[0] == '\0');	/* assume null-terminated word */
1804b0c40a00SSimon J. Gerraty 	rp = cached_realpath(word.start, rbuf);
18052c3632d1SSimon J. Gerraty 	if (rp != NULL && *rp == '/' && stat(rp, &st) == 0)
1806b0c40a00SSimon J. Gerraty 		SepBuf_AddStr(buf, rp);
1807b0c40a00SSimon J. Gerraty 	else
1808b0c40a00SSimon J. Gerraty 		SepBuf_AddSubstring(buf, word);
18092c3632d1SSimon J. Gerraty }
18102c3632d1SSimon J. Gerraty 
18112c3632d1SSimon J. Gerraty 
18122c3632d1SSimon J. Gerraty static char *
SubstringWords_JoinFree(SubstringWords words)181312904384SSimon J. Gerraty SubstringWords_JoinFree(SubstringWords words)
18142c3632d1SSimon J. Gerraty {
18152c3632d1SSimon J. Gerraty 	Buffer buf;
18162c3632d1SSimon J. Gerraty 	size_t i;
18173955d011SMarcel Moolenaar 
1818e2eeea75SSimon J. Gerraty 	Buf_Init(&buf);
18193955d011SMarcel Moolenaar 
18202c3632d1SSimon J. Gerraty 	for (i = 0; i < words.len; i++) {
182106b9b3e0SSimon J. Gerraty 		if (i != 0) {
18229f45a3c8SSimon J. Gerraty 			/*
18239f45a3c8SSimon J. Gerraty 			 * XXX: Use ch->sep instead of ' ', for consistency.
18249f45a3c8SSimon J. Gerraty 			 */
182506b9b3e0SSimon J. Gerraty 			Buf_AddByte(&buf, ' ');
182606b9b3e0SSimon J. Gerraty 		}
1827148ee845SSimon J. Gerraty 		Buf_AddRange(&buf, words.words[i].start, words.words[i].end);
18283955d011SMarcel Moolenaar 	}
18293955d011SMarcel Moolenaar 
183012904384SSimon J. Gerraty 	SubstringWords_Free(words);
18313955d011SMarcel Moolenaar 
1832dba7b0efSSimon J. Gerraty 	return Buf_DoneData(&buf);
18333955d011SMarcel Moolenaar }
18343955d011SMarcel Moolenaar 
18353955d011SMarcel Moolenaar 
183606b9b3e0SSimon J. Gerraty /*
183706b9b3e0SSimon J. Gerraty  * Quote shell meta-characters and space characters in the string.
183806b9b3e0SSimon J. Gerraty  * If quoteDollar is set, also quote and double any '$' characters.
183906b9b3e0SSimon J. Gerraty  */
1840b0c40a00SSimon J. Gerraty static void
QuoteShell(const char * str,bool quoteDollar,LazyBuf * buf)1841148ee845SSimon J. Gerraty QuoteShell(const char *str, bool quoteDollar, LazyBuf *buf)
18423955d011SMarcel Moolenaar {
1843b0c40a00SSimon J. Gerraty 	const char *p;
18444c620fe5SSimon J. Gerraty 
1845b0c40a00SSimon J. Gerraty 	LazyBuf_Init(buf, str);
1846b0c40a00SSimon J. Gerraty 	for (p = str; *p != '\0'; p++) {
1847b0c40a00SSimon J. Gerraty 		if (*p == '\n') {
18482c3632d1SSimon J. Gerraty 			const char *newline = Shell_GetNewline();
18492c3632d1SSimon J. Gerraty 			if (newline == NULL)
18502c3632d1SSimon J. Gerraty 				newline = "\\\n";
1851b0c40a00SSimon J. Gerraty 			LazyBuf_AddStr(buf, newline);
18524c620fe5SSimon J. Gerraty 			continue;
18534c620fe5SSimon J. Gerraty 		}
18549f45a3c8SSimon J. Gerraty 		if (ch_isspace(*p) || ch_is_shell_meta(*p))
1855b0c40a00SSimon J. Gerraty 			LazyBuf_Add(buf, '\\');
1856b0c40a00SSimon J. Gerraty 		LazyBuf_Add(buf, *p);
1857b0c40a00SSimon J. Gerraty 		if (quoteDollar && *p == '$')
1858b0c40a00SSimon J. Gerraty 			LazyBuf_AddStr(buf, "\\$");
18593955d011SMarcel Moolenaar 	}
18603955d011SMarcel Moolenaar }
18613955d011SMarcel Moolenaar 
186206b9b3e0SSimon J. Gerraty /*
186306b9b3e0SSimon J. Gerraty  * Compute the 32-bit hash of the given string, using the MurmurHash3
186406b9b3e0SSimon J. Gerraty  * algorithm. Output is encoded as 8 hex digits, in Little Endian order.
186506b9b3e0SSimon J. Gerraty  */
18663955d011SMarcel Moolenaar static char *
Hash(const char * str)1867148ee845SSimon J. Gerraty Hash(const char *str)
18683955d011SMarcel Moolenaar {
18693955d011SMarcel Moolenaar 	static const char hexdigits[16] = "0123456789abcdef";
18703841c287SSimon J. Gerraty 	const unsigned char *ustr = (const unsigned char *)str;
18713955d011SMarcel Moolenaar 
18722c3632d1SSimon J. Gerraty 	uint32_t h = 0x971e137bU;
18732c3632d1SSimon J. Gerraty 	uint32_t c1 = 0x95543787U;
18742c3632d1SSimon J. Gerraty 	uint32_t c2 = 0x2ad7eb25U;
18752c3632d1SSimon J. Gerraty 	size_t len2 = strlen(str);
18763955d011SMarcel Moolenaar 
18772c3632d1SSimon J. Gerraty 	char *buf;
18782c3632d1SSimon J. Gerraty 	size_t i;
18792c3632d1SSimon J. Gerraty 
18802c3632d1SSimon J. Gerraty 	size_t len;
188106b9b3e0SSimon J. Gerraty 	for (len = len2; len != 0;) {
18822c3632d1SSimon J. Gerraty 		uint32_t k = 0;
18833955d011SMarcel Moolenaar 		switch (len) {
18843955d011SMarcel Moolenaar 		default:
18853841c287SSimon J. Gerraty 			k = ((uint32_t)ustr[3] << 24) |
18863841c287SSimon J. Gerraty 			    ((uint32_t)ustr[2] << 16) |
18873841c287SSimon J. Gerraty 			    ((uint32_t)ustr[1] << 8) |
18883841c287SSimon J. Gerraty 			    (uint32_t)ustr[0];
18893955d011SMarcel Moolenaar 			len -= 4;
18903955d011SMarcel Moolenaar 			ustr += 4;
18913955d011SMarcel Moolenaar 			break;
18923955d011SMarcel Moolenaar 		case 3:
18933841c287SSimon J. Gerraty 			k |= (uint32_t)ustr[2] << 16;
189449caa483SSimon J. Gerraty 			/* FALLTHROUGH */
18953955d011SMarcel Moolenaar 		case 2:
18963841c287SSimon J. Gerraty 			k |= (uint32_t)ustr[1] << 8;
189749caa483SSimon J. Gerraty 			/* FALLTHROUGH */
18983955d011SMarcel Moolenaar 		case 1:
18993841c287SSimon J. Gerraty 			k |= (uint32_t)ustr[0];
19003955d011SMarcel Moolenaar 			len = 0;
19013955d011SMarcel Moolenaar 		}
19023955d011SMarcel Moolenaar 		c1 = c1 * 5 + 0x7b7d159cU;
19033955d011SMarcel Moolenaar 		c2 = c2 * 5 + 0x6bce6396U;
19043955d011SMarcel Moolenaar 		k *= c1;
19053955d011SMarcel Moolenaar 		k = (k << 11) ^ (k >> 21);
19063955d011SMarcel Moolenaar 		k *= c2;
19073955d011SMarcel Moolenaar 		h = (h << 13) ^ (h >> 19);
19083955d011SMarcel Moolenaar 		h = h * 5 + 0x52dce729U;
19093955d011SMarcel Moolenaar 		h ^= k;
191074d2e02bSSimon J. Gerraty 	}
19112c3632d1SSimon J. Gerraty 	h ^= (uint32_t)len2;
19123955d011SMarcel Moolenaar 	h *= 0x85ebca6b;
19133955d011SMarcel Moolenaar 	h ^= h >> 13;
19143955d011SMarcel Moolenaar 	h *= 0xc2b2ae35;
19153955d011SMarcel Moolenaar 	h ^= h >> 16;
19163955d011SMarcel Moolenaar 
19172c3632d1SSimon J. Gerraty 	buf = bmake_malloc(9);
19182c3632d1SSimon J. Gerraty 	for (i = 0; i < 8; i++) {
19192c3632d1SSimon J. Gerraty 		buf[i] = hexdigits[h & 0x0f];
19203955d011SMarcel Moolenaar 		h >>= 4;
19213955d011SMarcel Moolenaar 	}
19222c3632d1SSimon J. Gerraty 	buf[8] = '\0';
19232c3632d1SSimon J. Gerraty 	return buf;
19243955d011SMarcel Moolenaar }
19253955d011SMarcel Moolenaar 
19263955d011SMarcel Moolenaar static char *
FormatTime(const char * fmt,time_t t,bool gmt)1927148ee845SSimon J. Gerraty FormatTime(const char *fmt, time_t t, bool gmt)
19283955d011SMarcel Moolenaar {
19293955d011SMarcel Moolenaar 	char buf[BUFSIZ];
19303955d011SMarcel Moolenaar 
19319f45a3c8SSimon J. Gerraty 	if (t == 0)
19329f45a3c8SSimon J. Gerraty 		time(&t);
1933e2eeea75SSimon J. Gerraty 	if (*fmt == '\0')
19343955d011SMarcel Moolenaar 		fmt = "%c";
193598875883SSimon J. Gerraty 	if (gmt && strchr(fmt, 's') != NULL) {
193698875883SSimon J. Gerraty 		/* strftime "%s" only works with localtime, not with gmtime. */
193798875883SSimon J. Gerraty 		const char *prev_tz_env = getenv("TZ");
193898875883SSimon J. Gerraty 		char *prev_tz = prev_tz_env != NULL
193998875883SSimon J. Gerraty 		    ? bmake_strdup(prev_tz_env) : NULL;
194098875883SSimon J. Gerraty 		setenv("TZ", "UTC", 1);
194198875883SSimon J. Gerraty 		strftime(buf, sizeof buf, fmt, localtime(&t));
194298875883SSimon J. Gerraty 		if (prev_tz != NULL) {
194398875883SSimon J. Gerraty 			setenv("TZ", prev_tz, 1);
194498875883SSimon J. Gerraty 			free(prev_tz);
194598875883SSimon J. Gerraty 		} else
194698875883SSimon J. Gerraty 			unsetenv("TZ");
194798875883SSimon J. Gerraty 	} else
194898875883SSimon J. Gerraty 		strftime(buf, sizeof buf, fmt, (gmt ? gmtime : localtime)(&t));
19493955d011SMarcel Moolenaar 
1950e2eeea75SSimon J. Gerraty 	buf[sizeof buf - 1] = '\0';
19513955d011SMarcel Moolenaar 	return bmake_strdup(buf);
19523955d011SMarcel Moolenaar }
19533955d011SMarcel Moolenaar 
1954e2eeea75SSimon J. Gerraty /*
1955e2eeea75SSimon J. Gerraty  * The ApplyModifier functions take an expression that is being evaluated.
1956b0c40a00SSimon J. Gerraty  * Their task is to apply a single modifier to the expression.  This involves
1957b0c40a00SSimon J. Gerraty  * parsing the modifier, evaluating it and finally updating the value of the
1958b0c40a00SSimon J. Gerraty  * expression.
19592c3632d1SSimon J. Gerraty  *
19602c3632d1SSimon J. Gerraty  * Parsing the modifier
19612c3632d1SSimon J. Gerraty  *
19622c3632d1SSimon J. Gerraty  * If parsing succeeds, the parsing position *pp is updated to point to the
19632c3632d1SSimon J. Gerraty  * first character following the modifier, which typically is either ':' or
1964b0c40a00SSimon J. Gerraty  * ch->endc.  The modifier doesn't have to check for this delimiter character,
1965e2eeea75SSimon J. Gerraty  * this is done by ApplyModifiers.
1966e2eeea75SSimon J. Gerraty  *
1967e2eeea75SSimon J. Gerraty  * XXX: As of 2020-11-15, some modifiers such as :S, :C, :P, :L do not
1968e2eeea75SSimon J. Gerraty  * need to be followed by a ':' or endc; this was an unintended mistake.
19692c3632d1SSimon J. Gerraty  *
1970d5e0a182SSimon J. Gerraty  * If parsing fails because of a missing delimiter after a modifier part (as
1971d5e0a182SSimon J. Gerraty  * in the :S, :C or :@ modifiers), return AMR_CLEANUP.
19722c3632d1SSimon J. Gerraty  *
19732c3632d1SSimon J. Gerraty  * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to
1974d5e0a182SSimon J. Gerraty  * try the SysV modifier ':from=to' as fallback.  This should only be
19752c3632d1SSimon J. Gerraty  * done as long as there have been no side effects from evaluating nested
19762c3632d1SSimon J. Gerraty  * variables, to avoid evaluating them more than once.  In this case, the
1977e2eeea75SSimon J. Gerraty  * parsing position may or may not be updated.  (XXX: Why not? The original
1978e2eeea75SSimon J. Gerraty  * parsing position is well-known in ApplyModifiers.)
19792c3632d1SSimon J. Gerraty  *
19802c3632d1SSimon J. Gerraty  * If parsing fails and the SysV modifier ${VAR:from=to} should not be used
1981d5e0a182SSimon J. Gerraty  * as a fallback, issue an error message using Parse_Error (preferred over
1982d5e0a182SSimon J. Gerraty  * Error) and then return AMR_CLEANUP, which stops processing the expression.
1983d5e0a182SSimon J. Gerraty  * (XXX: As of 2020-08-23, evaluation of the string continues nevertheless
1984d5e0a182SSimon J. Gerraty  * after skipping a few bytes, which results in garbage.)
19852c3632d1SSimon J. Gerraty  *
19862c3632d1SSimon J. Gerraty  * Evaluating the modifier
19872c3632d1SSimon J. Gerraty  *
19882c3632d1SSimon J. Gerraty  * After parsing, the modifier is evaluated.  The side effects from evaluating
1989d5e0a182SSimon J. Gerraty  * nested expressions in the modifier text often already happen
1990b0c40a00SSimon J. Gerraty  * during parsing though.  For most modifiers this doesn't matter since their
199112904384SSimon J. Gerraty  * only noticeable effect is that they update the value of the expression.
1992b0c40a00SSimon J. Gerraty  * Some modifiers such as ':sh' or '::=' have noticeable side effects though.
19932c3632d1SSimon J. Gerraty  *
1994d5e0a182SSimon J. Gerraty  * Evaluating the modifier usually takes the current value of the
1995d5e0a182SSimon J. Gerraty  * expression from ch->expr->value, or the variable name from ch->var->name,
1996d5e0a182SSimon J. Gerraty  * and stores the result back in ch->expr->value via Expr_SetValueOwn or
1997b0c40a00SSimon J. Gerraty  * Expr_SetValueRefer.
19982c3632d1SSimon J. Gerraty  *
1999956e45f6SSimon J. Gerraty  * Some modifiers such as :D and :U turn undefined expressions into defined
2000d5e0a182SSimon J. Gerraty  * expressions using Expr_Define.
20012c3632d1SSimon J. Gerraty  */
20022c3632d1SSimon J. Gerraty 
2003b0c40a00SSimon J. Gerraty typedef enum ExprDefined {
2004d5e0a182SSimon J. Gerraty 	/* The expression is based on a regular, defined variable. */
2005b0c40a00SSimon J. Gerraty 	DEF_REGULAR,
2006d5e0a182SSimon J. Gerraty 	/* The expression is based on an undefined variable. */
2007b0c40a00SSimon J. Gerraty 	DEF_UNDEF,
200806b9b3e0SSimon J. Gerraty 	/*
2009d5e0a182SSimon J. Gerraty 	 * The expression started as an undefined expression, but one
2010b0c40a00SSimon J. Gerraty 	 * of the modifiers (such as ':D' or ':U') has turned the expression
2011b0c40a00SSimon J. Gerraty 	 * from undefined to defined.
201206b9b3e0SSimon J. Gerraty 	 */
2013b0c40a00SSimon J. Gerraty 	DEF_DEFINED
2014b0c40a00SSimon J. Gerraty } ExprDefined;
2015956e45f6SSimon J. Gerraty 
201612904384SSimon J. Gerraty static const char ExprDefined_Name[][10] = {
2017b0c40a00SSimon J. Gerraty 	"regular",
2018b0c40a00SSimon J. Gerraty 	"undefined",
2019b0c40a00SSimon J. Gerraty 	"defined"
2020dba7b0efSSimon J. Gerraty };
2021956e45f6SSimon J. Gerraty 
2022b0c40a00SSimon J. Gerraty #if __STDC_VERSION__ >= 199901L
2023b0c40a00SSimon J. Gerraty #define const_member		const
2024b0c40a00SSimon J. Gerraty #else
2025b0c40a00SSimon J. Gerraty #define const_member		/* no const possible */
2026b0c40a00SSimon J. Gerraty #endif
2027b0c40a00SSimon J. Gerraty 
202812904384SSimon J. Gerraty /* An expression based on a variable, such as $@ or ${VAR:Mpattern:Q}. */
2029b0c40a00SSimon J. Gerraty typedef struct Expr {
2030b0c40a00SSimon J. Gerraty 	const char *name;
2031b0c40a00SSimon J. Gerraty 	FStr value;
2032b0c40a00SSimon J. Gerraty 	VarEvalMode const_member emode;
2033b0c40a00SSimon J. Gerraty 	GNode *const_member scope;
2034b0c40a00SSimon J. Gerraty 	ExprDefined defined;
2035b0c40a00SSimon J. Gerraty } Expr;
2036b0c40a00SSimon J. Gerraty 
203706b9b3e0SSimon J. Gerraty /*
2038b0c40a00SSimon J. Gerraty  * The status of applying a chain of modifiers to an expression.
2039b0c40a00SSimon J. Gerraty  *
2040b0c40a00SSimon J. Gerraty  * The modifiers of an expression are broken into chains of modifiers,
2041b0c40a00SSimon J. Gerraty  * starting a new nested chain whenever an indirect modifier starts.  There
2042b0c40a00SSimon J. Gerraty  * are at most 2 nesting levels: the outer one for the direct modifiers, and
2043b0c40a00SSimon J. Gerraty  * the inner one for the indirect modifiers.
2044b0c40a00SSimon J. Gerraty  *
2045b0c40a00SSimon J. Gerraty  * For example, the expression ${VAR:M*:${IND1}:${IND2}:O:u} has 3 chains of
2046b0c40a00SSimon J. Gerraty  * modifiers:
2047b0c40a00SSimon J. Gerraty  *
2048b0c40a00SSimon J. Gerraty  *	Chain 1 starts with the single modifier ':M*'.
2049b0c40a00SSimon J. Gerraty  *	  Chain 2 starts with all modifiers from ${IND1}.
2050b0c40a00SSimon J. Gerraty  *	  Chain 2 ends at the ':' between ${IND1} and ${IND2}.
2051b0c40a00SSimon J. Gerraty  *	  Chain 3 starts with all modifiers from ${IND2}.
2052b0c40a00SSimon J. Gerraty  *	  Chain 3 ends at the ':' after ${IND2}.
205312904384SSimon J. Gerraty  *	Chain 1 continues with the 2 modifiers ':O' and ':u'.
2054b0c40a00SSimon J. Gerraty  *	Chain 1 ends at the final '}' of the expression.
2055b0c40a00SSimon J. Gerraty  *
2056b0c40a00SSimon J. Gerraty  * After such a chain ends, its properties no longer have any effect.
2057b0c40a00SSimon J. Gerraty  *
2058b0c40a00SSimon J. Gerraty  * See varmod-indirect.mk.
205906b9b3e0SSimon J. Gerraty  */
2060b0c40a00SSimon J. Gerraty typedef struct ModChain {
2061b0c40a00SSimon J. Gerraty 	Expr *expr;
2062b0c40a00SSimon J. Gerraty 	/* '\0' or '{' or '(' */
2063b0c40a00SSimon J. Gerraty 	char const_member startc;
2064b0c40a00SSimon J. Gerraty 	/* '\0' or '}' or ')' */
2065b0c40a00SSimon J. Gerraty 	char const_member endc;
2066d5e0a182SSimon J. Gerraty 	/* Separator when joining words (see the :ts modifier). */
206706b9b3e0SSimon J. Gerraty 	char sep;
206806b9b3e0SSimon J. Gerraty 	/*
2069d5e0a182SSimon J. Gerraty 	 * Whether some modifiers that otherwise split the variable value
207006b9b3e0SSimon J. Gerraty 	 * into words, like :S and :C, treat the variable value as a single
207106b9b3e0SSimon J. Gerraty 	 * big word, possibly containing spaces.
207206b9b3e0SSimon J. Gerraty 	 */
2073b0c40a00SSimon J. Gerraty 	bool oneBigWord;
2074b0c40a00SSimon J. Gerraty } ModChain;
20753841c287SSimon J. Gerraty 
2076956e45f6SSimon J. Gerraty static void
Expr_Define(Expr * expr)2077b0c40a00SSimon J. Gerraty Expr_Define(Expr *expr)
2078956e45f6SSimon J. Gerraty {
2079b0c40a00SSimon J. Gerraty 	if (expr->defined == DEF_UNDEF)
2080b0c40a00SSimon J. Gerraty 		expr->defined = DEF_DEFINED;
2081956e45f6SSimon J. Gerraty }
2082956e45f6SSimon J. Gerraty 
208312904384SSimon J. Gerraty static const char *
Expr_Str(const Expr * expr)208412904384SSimon J. Gerraty Expr_Str(const Expr *expr)
208512904384SSimon J. Gerraty {
208612904384SSimon J. Gerraty 	return expr->value.str;
208712904384SSimon J. Gerraty }
208812904384SSimon J. Gerraty 
208912904384SSimon J. Gerraty static SubstringWords
Expr_Words(const Expr * expr)209012904384SSimon J. Gerraty Expr_Words(const Expr *expr)
209112904384SSimon J. Gerraty {
209212904384SSimon J. Gerraty 	return Substring_Words(Expr_Str(expr), false);
209312904384SSimon J. Gerraty }
209412904384SSimon J. Gerraty 
2095b0c40a00SSimon J. Gerraty static void
Expr_SetValue(Expr * expr,FStr value)2096b0c40a00SSimon J. Gerraty Expr_SetValue(Expr *expr, FStr value)
2097b0c40a00SSimon J. Gerraty {
2098b0c40a00SSimon J. Gerraty 	FStr_Done(&expr->value);
2099b0c40a00SSimon J. Gerraty 	expr->value = value;
2100b0c40a00SSimon J. Gerraty }
2101b0c40a00SSimon J. Gerraty 
2102b0c40a00SSimon J. Gerraty static void
Expr_SetValueOwn(Expr * expr,char * value)2103b0c40a00SSimon J. Gerraty Expr_SetValueOwn(Expr *expr, char *value)
2104b0c40a00SSimon J. Gerraty {
2105b0c40a00SSimon J. Gerraty 	Expr_SetValue(expr, FStr_InitOwn(value));
2106b0c40a00SSimon J. Gerraty }
2107b0c40a00SSimon J. Gerraty 
2108b0c40a00SSimon J. Gerraty static void
Expr_SetValueRefer(Expr * expr,const char * value)2109b0c40a00SSimon J. Gerraty Expr_SetValueRefer(Expr *expr, const char *value)
2110b0c40a00SSimon J. Gerraty {
2111b0c40a00SSimon J. Gerraty 	Expr_SetValue(expr, FStr_InitRefer(value));
2112b0c40a00SSimon J. Gerraty }
2113b0c40a00SSimon J. Gerraty 
2114b0c40a00SSimon J. Gerraty static bool
Expr_ShouldEval(const Expr * expr)2115b0c40a00SSimon J. Gerraty Expr_ShouldEval(const Expr *expr)
2116b0c40a00SSimon J. Gerraty {
2117b0c40a00SSimon J. Gerraty 	return VarEvalMode_ShouldEval(expr->emode);
2118b0c40a00SSimon J. Gerraty }
2119b0c40a00SSimon J. Gerraty 
2120b0c40a00SSimon J. Gerraty static bool
ModChain_ShouldEval(const ModChain * ch)2121b0c40a00SSimon J. Gerraty ModChain_ShouldEval(const ModChain *ch)
2122b0c40a00SSimon J. Gerraty {
2123b0c40a00SSimon J. Gerraty 	return Expr_ShouldEval(ch->expr);
2124b0c40a00SSimon J. Gerraty }
2125b0c40a00SSimon J. Gerraty 
2126b0c40a00SSimon J. Gerraty 
2127956e45f6SSimon J. Gerraty typedef enum ApplyModifierResult {
212806b9b3e0SSimon J. Gerraty 	/* Continue parsing */
212906b9b3e0SSimon J. Gerraty 	AMR_OK,
2130d5e0a182SSimon J. Gerraty 	/* Not a match, try the ':from=to' modifier as well. */
213106b9b3e0SSimon J. Gerraty 	AMR_UNKNOWN,
2132759b177aSSimon J. Gerraty 	/* Error out without further error message. */
213306b9b3e0SSimon J. Gerraty 	AMR_CLEANUP
21342c3632d1SSimon J. Gerraty } ApplyModifierResult;
21352c3632d1SSimon J. Gerraty 
213606b9b3e0SSimon J. Gerraty /*
2137*a8c56be4SSimon J. Gerraty  * Allow backslashes to escape the delimiters, $, and \, but don't touch other
213806b9b3e0SSimon J. Gerraty  * backslashes.
213906b9b3e0SSimon J. Gerraty  */
2140b0c40a00SSimon J. Gerraty static bool
IsEscapedModifierPart(const char * p,char end1,char end2,struct ModifyWord_SubstArgs * subst)2141*a8c56be4SSimon J. Gerraty IsEscapedModifierPart(const char *p, char end1, char end2,
2142956e45f6SSimon J. Gerraty 		      struct ModifyWord_SubstArgs *subst)
2143956e45f6SSimon J. Gerraty {
21448d5c8e21SSimon J. Gerraty 	if (p[0] != '\\' || p[1] == '\0')
2145b0c40a00SSimon J. Gerraty 		return false;
2146*a8c56be4SSimon J. Gerraty 	if (p[1] == end1 || p[1] == end2 || p[1] == '\\' || p[1] == '$')
2147b0c40a00SSimon J. Gerraty 		return true;
2148956e45f6SSimon J. Gerraty 	return p[1] == '&' && subst != NULL;
2149956e45f6SSimon J. Gerraty }
2150956e45f6SSimon J. Gerraty 
215112904384SSimon J. Gerraty /*
215212904384SSimon J. Gerraty  * In a part of a modifier, parse a subexpression and evaluate it.
215312904384SSimon J. Gerraty  */
215412904384SSimon J. Gerraty static void
ParseModifierPartExpr(const char ** pp,LazyBuf * part,const ModChain * ch,VarEvalMode emode)215512904384SSimon J. Gerraty ParseModifierPartExpr(const char **pp, LazyBuf *part, const ModChain *ch,
215612904384SSimon J. Gerraty 		      VarEvalMode emode)
215712904384SSimon J. Gerraty {
215812904384SSimon J. Gerraty 	const char *p = *pp;
21598c973ee2SSimon J. Gerraty 	FStr nested_val = Var_Parse(&p, ch->expr->scope,
21608c973ee2SSimon J. Gerraty 	    VarEvalMode_WithoutKeepDollar(emode));
216112904384SSimon J. Gerraty 	/* TODO: handle errors */
21628c973ee2SSimon J. Gerraty 	if (VarEvalMode_ShouldEval(emode))
216312904384SSimon J. Gerraty 		LazyBuf_AddStr(part, nested_val.str);
21648c973ee2SSimon J. Gerraty 	else
21658c973ee2SSimon J. Gerraty 		LazyBuf_AddSubstring(part, Substring_Init(*pp, p));
216612904384SSimon J. Gerraty 	FStr_Done(&nested_val);
216712904384SSimon J. Gerraty 	*pp = p;
216812904384SSimon J. Gerraty }
216912904384SSimon J. Gerraty 
217012904384SSimon J. Gerraty /*
21718c973ee2SSimon J. Gerraty  * In a part of a modifier, parse some text that looks like a subexpression.
21728c973ee2SSimon J. Gerraty  * If the text starts with '$(', any '(' and ')' must be balanced.
21738c973ee2SSimon J. Gerraty  * If the text starts with '${', any '{' and '}' must be balanced.
2174d5e0a182SSimon J. Gerraty  * If the text starts with '$', that '$' is copied verbatim, it is not parsed
2175d5e0a182SSimon J. Gerraty  * as a short-name expression.
217612904384SSimon J. Gerraty  */
217712904384SSimon J. Gerraty static void
ParseModifierPartBalanced(const char ** pp,LazyBuf * part)21788c973ee2SSimon J. Gerraty ParseModifierPartBalanced(const char **pp, LazyBuf *part)
217912904384SSimon J. Gerraty {
218012904384SSimon J. Gerraty 	const char *p = *pp;
218112904384SSimon J. Gerraty 
218212904384SSimon J. Gerraty 	if (p[1] == '(' || p[1] == '{') {
218312904384SSimon J. Gerraty 		char startc = p[1];
218412904384SSimon J. Gerraty 		int endc = startc == '(' ? ')' : '}';
218512904384SSimon J. Gerraty 		int depth = 1;
218612904384SSimon J. Gerraty 
218712904384SSimon J. Gerraty 		for (p += 2; *p != '\0' && depth > 0; p++) {
218812904384SSimon J. Gerraty 			if (p[-1] != '\\') {
218912904384SSimon J. Gerraty 				if (*p == startc)
219012904384SSimon J. Gerraty 					depth++;
219112904384SSimon J. Gerraty 				if (*p == endc)
219212904384SSimon J. Gerraty 					depth--;
219312904384SSimon J. Gerraty 			}
219412904384SSimon J. Gerraty 		}
2195d5e0a182SSimon J. Gerraty 		LazyBuf_AddSubstring(part, Substring_Init(*pp, p));
219612904384SSimon J. Gerraty 		*pp = p;
219712904384SSimon J. Gerraty 	} else {
2198d5e0a182SSimon J. Gerraty 		LazyBuf_Add(part, *p);
219912904384SSimon J. Gerraty 		*pp = p + 1;
220012904384SSimon J. Gerraty 	}
220112904384SSimon J. Gerraty }
220212904384SSimon J. Gerraty 
22038d5c8e21SSimon J. Gerraty /*
22048d5c8e21SSimon J. Gerraty  * Parse a part of a modifier such as the "from" and "to" in :S/from/to/ or
22058d5c8e21SSimon J. Gerraty  * the "var" or "replacement ${var}" in :@var@replacement ${var}@, up to and
22068d5c8e21SSimon J. Gerraty  * including the next unescaped delimiter.  The delimiter, as well as the
22078d5c8e21SSimon J. Gerraty  * backslash or the dollar, can be escaped with a backslash.
22088d5c8e21SSimon J. Gerraty  *
22098d5c8e21SSimon J. Gerraty  * Return true if parsing succeeded, together with the parsed (and possibly
22108d5c8e21SSimon J. Gerraty  * expanded) part.  In that case, pp points right after the delimiter.  The
22118d5c8e21SSimon J. Gerraty  * delimiter is not included in the part though.
22128d5c8e21SSimon J. Gerraty  */
22138c973ee2SSimon J. Gerraty static bool
ParseModifierPart(const char ** pp,char end1,char end2,VarEvalMode emode,ModChain * ch,LazyBuf * part,PatternFlags * out_pflags,struct ModifyWord_SubstArgs * subst)22148d5c8e21SSimon J. Gerraty ParseModifierPart(
22158d5c8e21SSimon J. Gerraty     /* The parsing position, updated upon return */
221606b9b3e0SSimon J. Gerraty     const char **pp,
22178d5c8e21SSimon J. Gerraty     char end1,
22188d5c8e21SSimon J. Gerraty     char end2,
22198d5c8e21SSimon J. Gerraty     /* Mode for evaluating nested expressions. */
2220b0c40a00SSimon J. Gerraty     VarEvalMode emode,
2221b0c40a00SSimon J. Gerraty     ModChain *ch,
2222b0c40a00SSimon J. Gerraty     LazyBuf *part,
22239f45a3c8SSimon J. Gerraty     /*
2224d5e0a182SSimon J. Gerraty      * For the first part of the ':S' modifier, set anchorEnd if the last
22259f45a3c8SSimon J. Gerraty      * character of the pattern is a $.
22269f45a3c8SSimon J. Gerraty      */
2227b0c40a00SSimon J. Gerraty     PatternFlags *out_pflags,
22289f45a3c8SSimon J. Gerraty     /*
2229d5e0a182SSimon J. Gerraty      * For the second part of the ':S' modifier, allow ampersands to be
2230d5e0a182SSimon J. Gerraty      * escaped and replace unescaped ampersands with subst->lhs.
22319f45a3c8SSimon J. Gerraty      */
2232956e45f6SSimon J. Gerraty     struct ModifyWord_SubstArgs *subst
223306b9b3e0SSimon J. Gerraty )
223406b9b3e0SSimon J. Gerraty {
22358d5c8e21SSimon J. Gerraty 	const char *p = *pp;
2236956e45f6SSimon J. Gerraty 
2237b0c40a00SSimon J. Gerraty 	LazyBuf_Init(part, p);
2238c1d01b5fSSimon J. Gerraty 	while (*p != '\0' && *p != end1 && *p != end2) {
2239*a8c56be4SSimon J. Gerraty 		if (IsEscapedModifierPart(p, end1, end2, subst)) {
2240b0c40a00SSimon J. Gerraty 			LazyBuf_Add(part, p[1]);
2241956e45f6SSimon J. Gerraty 			p += 2;
224212904384SSimon J. Gerraty 		} else if (*p != '$') {	/* Unescaped, simple text */
2243956e45f6SSimon J. Gerraty 			if (subst != NULL && *p == '&')
2244b0c40a00SSimon J. Gerraty 				LazyBuf_AddSubstring(part, subst->lhs);
2245956e45f6SSimon J. Gerraty 			else
2246b0c40a00SSimon J. Gerraty 				LazyBuf_Add(part, *p);
2247956e45f6SSimon J. Gerraty 			p++;
22488d5c8e21SSimon J. Gerraty 		} else if (p[1] == end2) {	/* Unescaped '$' at end */
2249956e45f6SSimon J. Gerraty 			if (out_pflags != NULL)
2250b0c40a00SSimon J. Gerraty 				out_pflags->anchorEnd = true;
2251956e45f6SSimon J. Gerraty 			else
2252b0c40a00SSimon J. Gerraty 				LazyBuf_Add(part, *p);
2253956e45f6SSimon J. Gerraty 			p++;
22548c973ee2SSimon J. Gerraty 		} else if (emode == VARE_PARSE_BALANCED)
22558c973ee2SSimon J. Gerraty 			ParseModifierPartBalanced(&p, part);
225612904384SSimon J. Gerraty 		else
22578c973ee2SSimon J. Gerraty 			ParseModifierPartExpr(&p, part, ch, emode);
2258956e45f6SSimon J. Gerraty 	}
2259956e45f6SSimon J. Gerraty 
2260c1d01b5fSSimon J. Gerraty 	if (*p != end1 && *p != end2) {
226122619282SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
2262759b177aSSimon J. Gerraty 		    "Unfinished modifier after \"%.*s\", expecting \"%c\"",
2263759b177aSSimon J. Gerraty 		    (int)(p - *pp), *pp, end2);
2264b0c40a00SSimon J. Gerraty 		LazyBuf_Done(part);
2265759b177aSSimon J. Gerraty 		*pp = p;
22668c973ee2SSimon J. Gerraty 		return false;
2267956e45f6SSimon J. Gerraty 	}
2268759b177aSSimon J. Gerraty 	*pp = p;
22698d5c8e21SSimon J. Gerraty 	if (end1 == end2)
2270c1d01b5fSSimon J. Gerraty 		(*pp)++;
2271956e45f6SSimon J. Gerraty 
2272b0c40a00SSimon J. Gerraty 	{
2273b0c40a00SSimon J. Gerraty 		Substring sub = LazyBuf_Get(part);
2274b0c40a00SSimon J. Gerraty 		DEBUG2(VAR, "Modifier part: \"%.*s\"\n",
2275b0c40a00SSimon J. Gerraty 		    (int)Substring_Length(sub), sub.start);
2276b0c40a00SSimon J. Gerraty 	}
2277b0c40a00SSimon J. Gerraty 
22788c973ee2SSimon J. Gerraty 	return true;
2279956e45f6SSimon J. Gerraty }
2280956e45f6SSimon J. Gerraty 
2281b0c40a00SSimon J. Gerraty MAKE_INLINE bool
IsDelimiter(char c,const ModChain * ch)2282b0c40a00SSimon J. Gerraty IsDelimiter(char c, const ModChain *ch)
2283b0c40a00SSimon J. Gerraty {
22844fde40d9SSimon J. Gerraty 	return c == ':' || c == ch->endc || c == '\0';
228506b9b3e0SSimon J. Gerraty }
228606b9b3e0SSimon J. Gerraty 
22872c3632d1SSimon J. Gerraty /* Test whether mod starts with modname, followed by a delimiter. */
2288b0c40a00SSimon J. Gerraty MAKE_INLINE bool
ModMatch(const char * mod,const char * modname,const ModChain * ch)2289b0c40a00SSimon J. Gerraty ModMatch(const char *mod, const char *modname, const ModChain *ch)
22902c3632d1SSimon J. Gerraty {
22912c3632d1SSimon J. Gerraty 	size_t n = strlen(modname);
2292b0c40a00SSimon J. Gerraty 	return strncmp(mod, modname, n) == 0 && IsDelimiter(mod[n], ch);
22932c3632d1SSimon J. Gerraty }
22942c3632d1SSimon J. Gerraty 
22952c3632d1SSimon J. Gerraty /* Test whether mod starts with modname, followed by a delimiter or '='. */
2296b0c40a00SSimon J. Gerraty MAKE_INLINE bool
ModMatchEq(const char * mod,const char * modname,const ModChain * ch)2297b0c40a00SSimon J. Gerraty ModMatchEq(const char *mod, const char *modname, const ModChain *ch)
22982c3632d1SSimon J. Gerraty {
22992c3632d1SSimon J. Gerraty 	size_t n = strlen(modname);
23002c3632d1SSimon J. Gerraty 	return strncmp(mod, modname, n) == 0 &&
2301b0c40a00SSimon J. Gerraty 	       (IsDelimiter(mod[n], ch) || mod[n] == '=');
23022c3632d1SSimon J. Gerraty }
23033841c287SSimon J. Gerraty 
2304b0c40a00SSimon J. Gerraty static bool
TryParseIntBase0(const char ** pp,int * out_num)2305956e45f6SSimon J. Gerraty TryParseIntBase0(const char **pp, int *out_num)
2306956e45f6SSimon J. Gerraty {
2307956e45f6SSimon J. Gerraty 	char *end;
2308956e45f6SSimon J. Gerraty 	long n;
2309956e45f6SSimon J. Gerraty 
2310956e45f6SSimon J. Gerraty 	errno = 0;
2311956e45f6SSimon J. Gerraty 	n = strtol(*pp, &end, 0);
2312b0c40a00SSimon J. Gerraty 
2313b0c40a00SSimon J. Gerraty 	if (end == *pp)
2314b0c40a00SSimon J. Gerraty 		return false;
2315956e45f6SSimon J. Gerraty 	if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE)
2316b0c40a00SSimon J. Gerraty 		return false;
2317956e45f6SSimon J. Gerraty 	if (n < INT_MIN || n > INT_MAX)
2318b0c40a00SSimon J. Gerraty 		return false;
2319956e45f6SSimon J. Gerraty 
2320956e45f6SSimon J. Gerraty 	*pp = end;
2321956e45f6SSimon J. Gerraty 	*out_num = (int)n;
2322b0c40a00SSimon J. Gerraty 	return true;
2323956e45f6SSimon J. Gerraty }
2324956e45f6SSimon J. Gerraty 
2325b0c40a00SSimon J. Gerraty static bool
TryParseSize(const char ** pp,size_t * out_num)2326956e45f6SSimon J. Gerraty TryParseSize(const char **pp, size_t *out_num)
2327956e45f6SSimon J. Gerraty {
2328956e45f6SSimon J. Gerraty 	char *end;
2329956e45f6SSimon J. Gerraty 	unsigned long n;
2330956e45f6SSimon J. Gerraty 
2331956e45f6SSimon J. Gerraty 	if (!ch_isdigit(**pp))
2332b0c40a00SSimon J. Gerraty 		return false;
2333956e45f6SSimon J. Gerraty 
2334956e45f6SSimon J. Gerraty 	errno = 0;
2335956e45f6SSimon J. Gerraty 	n = strtoul(*pp, &end, 10);
2336956e45f6SSimon J. Gerraty 	if (n == ULONG_MAX && errno == ERANGE)
2337b0c40a00SSimon J. Gerraty 		return false;
2338956e45f6SSimon J. Gerraty 	if (n > SIZE_MAX)
2339b0c40a00SSimon J. Gerraty 		return false;
2340956e45f6SSimon J. Gerraty 
2341956e45f6SSimon J. Gerraty 	*pp = end;
2342956e45f6SSimon J. Gerraty 	*out_num = (size_t)n;
2343b0c40a00SSimon J. Gerraty 	return true;
2344956e45f6SSimon J. Gerraty }
2345956e45f6SSimon J. Gerraty 
2346b0c40a00SSimon J. Gerraty static bool
TryParseChar(const char ** pp,int base,char * out_ch)2347956e45f6SSimon J. Gerraty TryParseChar(const char **pp, int base, char *out_ch)
2348956e45f6SSimon J. Gerraty {
2349956e45f6SSimon J. Gerraty 	char *end;
2350956e45f6SSimon J. Gerraty 	unsigned long n;
2351956e45f6SSimon J. Gerraty 
2352956e45f6SSimon J. Gerraty 	if (!ch_isalnum(**pp))
2353b0c40a00SSimon J. Gerraty 		return false;
2354956e45f6SSimon J. Gerraty 
2355956e45f6SSimon J. Gerraty 	errno = 0;
2356956e45f6SSimon J. Gerraty 	n = strtoul(*pp, &end, base);
2357956e45f6SSimon J. Gerraty 	if (n == ULONG_MAX && errno == ERANGE)
2358b0c40a00SSimon J. Gerraty 		return false;
2359956e45f6SSimon J. Gerraty 	if (n > UCHAR_MAX)
2360b0c40a00SSimon J. Gerraty 		return false;
2361956e45f6SSimon J. Gerraty 
2362956e45f6SSimon J. Gerraty 	*pp = end;
2363956e45f6SSimon J. Gerraty 	*out_ch = (char)n;
2364b0c40a00SSimon J. Gerraty 	return true;
2365b0c40a00SSimon J. Gerraty }
2366b0c40a00SSimon J. Gerraty 
2367b0c40a00SSimon J. Gerraty /*
2368b0c40a00SSimon J. Gerraty  * Modify each word of the expression using the given function and place the
2369b0c40a00SSimon J. Gerraty  * result back in the expression.
2370b0c40a00SSimon J. Gerraty  */
2371b0c40a00SSimon J. Gerraty static void
ModifyWords(ModChain * ch,ModifyWordProc modifyWord,void * modifyWord_args,bool oneBigWord)2372b0c40a00SSimon J. Gerraty ModifyWords(ModChain *ch,
2373b0c40a00SSimon J. Gerraty 	    ModifyWordProc modifyWord, void *modifyWord_args,
2374b0c40a00SSimon J. Gerraty 	    bool oneBigWord)
2375b0c40a00SSimon J. Gerraty {
2376b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
237712904384SSimon J. Gerraty 	const char *val = Expr_Str(expr);
2378b0c40a00SSimon J. Gerraty 	SepBuf result;
2379b0c40a00SSimon J. Gerraty 	SubstringWords words;
2380b0c40a00SSimon J. Gerraty 	size_t i;
2381b0c40a00SSimon J. Gerraty 	Substring word;
2382b0c40a00SSimon J. Gerraty 
2383c59c3bf3SSimon J. Gerraty 	if (!ModChain_ShouldEval(ch))
2384c59c3bf3SSimon J. Gerraty 		return;
2385c59c3bf3SSimon J. Gerraty 
2386b0c40a00SSimon J. Gerraty 	if (oneBigWord) {
2387b0c40a00SSimon J. Gerraty 		SepBuf_Init(&result, ch->sep);
2388b0c40a00SSimon J. Gerraty 		/* XXX: performance: Substring_InitStr calls strlen */
2389b0c40a00SSimon J. Gerraty 		word = Substring_InitStr(val);
2390b0c40a00SSimon J. Gerraty 		modifyWord(word, &result, modifyWord_args);
2391b0c40a00SSimon J. Gerraty 		goto done;
2392b0c40a00SSimon J. Gerraty 	}
2393b0c40a00SSimon J. Gerraty 
2394b0c40a00SSimon J. Gerraty 	words = Substring_Words(val, false);
2395b0c40a00SSimon J. Gerraty 
239612904384SSimon J. Gerraty 	DEBUG3(VAR, "ModifyWords: split \"%s\" into %u %s\n",
239712904384SSimon J. Gerraty 	    val, (unsigned)words.len, words.len != 1 ? "words" : "word");
2398b0c40a00SSimon J. Gerraty 
2399b0c40a00SSimon J. Gerraty 	SepBuf_Init(&result, ch->sep);
2400b0c40a00SSimon J. Gerraty 	for (i = 0; i < words.len; i++) {
2401b0c40a00SSimon J. Gerraty 		modifyWord(words.words[i], &result, modifyWord_args);
2402b0c40a00SSimon J. Gerraty 		if (result.buf.len > 0)
2403b0c40a00SSimon J. Gerraty 			SepBuf_Sep(&result);
2404b0c40a00SSimon J. Gerraty 	}
2405b0c40a00SSimon J. Gerraty 
2406b0c40a00SSimon J. Gerraty 	SubstringWords_Free(words);
2407b0c40a00SSimon J. Gerraty 
2408b0c40a00SSimon J. Gerraty done:
2409b0c40a00SSimon J. Gerraty 	Expr_SetValueOwn(expr, SepBuf_DoneData(&result));
2410956e45f6SSimon J. Gerraty }
2411956e45f6SSimon J. Gerraty 
24123841c287SSimon J. Gerraty /* :@var@...${var}...@ */
24132c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Loop(const char ** pp,ModChain * ch)2414b0c40a00SSimon J. Gerraty ApplyModifier_Loop(const char **pp, ModChain *ch)
24152c3632d1SSimon J. Gerraty {
2416b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
2417956e45f6SSimon J. Gerraty 	struct ModifyWord_LoopArgs args;
24182c3632d1SSimon J. Gerraty 	char prev_sep;
2419b0c40a00SSimon J. Gerraty 	LazyBuf tvarBuf, strBuf;
2420b0c40a00SSimon J. Gerraty 	FStr tvar, str;
24213841c287SSimon J. Gerraty 
2422b0c40a00SSimon J. Gerraty 	args.scope = expr->scope;
24233841c287SSimon J. Gerraty 
24242c3632d1SSimon J. Gerraty 	(*pp)++;		/* Skip the first '@' */
24258d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, '@', '@', VARE_PARSE,
24268d5c8e21SSimon J. Gerraty 	    ch, &tvarBuf, NULL, NULL))
24272c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
2428b0c40a00SSimon J. Gerraty 	tvar = LazyBuf_DoneGet(&tvarBuf);
2429b0c40a00SSimon J. Gerraty 	args.var = tvar.str;
2430b0c40a00SSimon J. Gerraty 	if (strchr(args.var, '$') != NULL) {
24312c3632d1SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
2432548bfc56SSimon J. Gerraty 		    "In the :@ modifier, the variable name \"%s\" "
243312904384SSimon J. Gerraty 		    "must not contain a dollar",
2434548bfc56SSimon J. Gerraty 		    args.var);
24358d5c8e21SSimon J. Gerraty 		goto cleanup_tvar;
24362c3632d1SSimon J. Gerraty 	}
24373841c287SSimon J. Gerraty 
24388d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, '@', '@', VARE_PARSE_BALANCED,
24398d5c8e21SSimon J. Gerraty 	    ch, &strBuf, NULL, NULL))
24408d5c8e21SSimon J. Gerraty 		goto cleanup_tvar;
2441b0c40a00SSimon J. Gerraty 	str = LazyBuf_DoneGet(&strBuf);
2442b0c40a00SSimon J. Gerraty 	args.body = str.str;
24433841c287SSimon J. Gerraty 
2444b0c40a00SSimon J. Gerraty 	if (!Expr_ShouldEval(expr))
2445b0c40a00SSimon J. Gerraty 		goto done;
2446b0c40a00SSimon J. Gerraty 
2447b0c40a00SSimon J. Gerraty 	args.emode = VarEvalMode_WithoutKeepDollar(expr->emode);
2448b0c40a00SSimon J. Gerraty 	prev_sep = ch->sep;
2449b0c40a00SSimon J. Gerraty 	ch->sep = ' ';		/* XXX: should be ch->sep for consistency */
2450b0c40a00SSimon J. Gerraty 	ModifyWords(ch, ModifyWord_Loop, &args, ch->oneBigWord);
2451b0c40a00SSimon J. Gerraty 	ch->sep = prev_sep;
2452b0c40a00SSimon J. Gerraty 	/* XXX: Consider restoring the previous value instead of deleting. */
2453b0c40a00SSimon J. Gerraty 	Var_Delete(expr->scope, args.var);
2454b0c40a00SSimon J. Gerraty 
2455b0c40a00SSimon J. Gerraty done:
2456b0c40a00SSimon J. Gerraty 	FStr_Done(&tvar);
2457b0c40a00SSimon J. Gerraty 	FStr_Done(&str);
24582c3632d1SSimon J. Gerraty 	return AMR_OK;
24598d5c8e21SSimon J. Gerraty 
24608d5c8e21SSimon J. Gerraty cleanup_tvar:
24618d5c8e21SSimon J. Gerraty 	FStr_Done(&tvar);
24628d5c8e21SSimon J. Gerraty 	return AMR_CLEANUP;
24633841c287SSimon J. Gerraty }
24643841c287SSimon J. Gerraty 
24654fde40d9SSimon J. Gerraty static void
ParseModifier_Defined(const char ** pp,ModChain * ch,bool shouldEval,LazyBuf * buf)24664fde40d9SSimon J. Gerraty ParseModifier_Defined(const char **pp, ModChain *ch, bool shouldEval,
24674fde40d9SSimon J. Gerraty 		      LazyBuf *buf)
24683841c287SSimon J. Gerraty {
24692c3632d1SSimon J. Gerraty 	const char *p;
24703841c287SSimon J. Gerraty 
24712c3632d1SSimon J. Gerraty 	p = *pp + 1;
24724fde40d9SSimon J. Gerraty 	LazyBuf_Init(buf, p);
24734fde40d9SSimon J. Gerraty 	while (!IsDelimiter(*p, ch)) {
24742c3632d1SSimon J. Gerraty 
24759f45a3c8SSimon J. Gerraty 		/*
24769f45a3c8SSimon J. Gerraty 		 * XXX: This code is similar to the one in Var_Parse. See if
2477d5e0a182SSimon J. Gerraty 		 * the code can be merged. See also ParseModifier_Match and
24789f45a3c8SSimon J. Gerraty 		 * ParseModifierPart.
24799f45a3c8SSimon J. Gerraty 		 */
2480e2eeea75SSimon J. Gerraty 
2481d5e0a182SSimon J. Gerraty 		/* See Buf_AddEscaped in for.c for the counterpart. */
24822c3632d1SSimon J. Gerraty 		if (*p == '\\') {
24832c3632d1SSimon J. Gerraty 			char c = p[1];
24844fde40d9SSimon J. Gerraty 			if ((IsDelimiter(c, ch) && c != '\0') ||
24854fde40d9SSimon J. Gerraty 			    c == '$' || c == '\\') {
24864fde40d9SSimon J. Gerraty 				if (shouldEval)
24874fde40d9SSimon J. Gerraty 					LazyBuf_Add(buf, c);
24882c3632d1SSimon J. Gerraty 				p += 2;
24892c3632d1SSimon J. Gerraty 				continue;
24902c3632d1SSimon J. Gerraty 			}
24912c3632d1SSimon J. Gerraty 		}
24922c3632d1SSimon J. Gerraty 
24932c3632d1SSimon J. Gerraty 		if (*p == '$') {
24948c973ee2SSimon J. Gerraty 			FStr val = Var_Parse(&p, ch->expr->scope,
24958d5c8e21SSimon J. Gerraty 			    shouldEval ? ch->expr->emode : VARE_PARSE);
2496956e45f6SSimon J. Gerraty 			/* TODO: handle errors */
24974fde40d9SSimon J. Gerraty 			if (shouldEval)
24984fde40d9SSimon J. Gerraty 				LazyBuf_AddStr(buf, val.str);
24994fde40d9SSimon J. Gerraty 			FStr_Done(&val);
25002c3632d1SSimon J. Gerraty 			continue;
25013841c287SSimon J. Gerraty 		}
25023841c287SSimon J. Gerraty 
25034fde40d9SSimon J. Gerraty 		if (shouldEval)
25044fde40d9SSimon J. Gerraty 			LazyBuf_Add(buf, *p);
25052c3632d1SSimon J. Gerraty 		p++;
25062c3632d1SSimon J. Gerraty 	}
25072c3632d1SSimon J. Gerraty 	*pp = p;
25084fde40d9SSimon J. Gerraty }
25094fde40d9SSimon J. Gerraty 
25104fde40d9SSimon J. Gerraty /* :Ddefined or :Uundefined */
25114fde40d9SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Defined(const char ** pp,ModChain * ch)25124fde40d9SSimon J. Gerraty ApplyModifier_Defined(const char **pp, ModChain *ch)
25134fde40d9SSimon J. Gerraty {
25144fde40d9SSimon J. Gerraty 	Expr *expr = ch->expr;
25154fde40d9SSimon J. Gerraty 	LazyBuf buf;
25164fde40d9SSimon J. Gerraty 	bool shouldEval =
25174fde40d9SSimon J. Gerraty 	    Expr_ShouldEval(expr) &&
25184fde40d9SSimon J. Gerraty 	    (**pp == 'D') == (expr->defined == DEF_REGULAR);
25194fde40d9SSimon J. Gerraty 
25204fde40d9SSimon J. Gerraty 	ParseModifier_Defined(pp, ch, shouldEval, &buf);
25213841c287SSimon J. Gerraty 
2522b0c40a00SSimon J. Gerraty 	Expr_Define(expr);
25234fde40d9SSimon J. Gerraty 	if (shouldEval)
2524b0c40a00SSimon J. Gerraty 		Expr_SetValue(expr, Substring_Str(LazyBuf_Get(&buf)));
25258d5c8e21SSimon J. Gerraty 	LazyBuf_Done(&buf);
2526b0c40a00SSimon J. Gerraty 
25272c3632d1SSimon J. Gerraty 	return AMR_OK;
25283841c287SSimon J. Gerraty }
25293841c287SSimon J. Gerraty 
2530956e45f6SSimon J. Gerraty /* :L */
2531956e45f6SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Literal(const char ** pp,ModChain * ch)2532b0c40a00SSimon J. Gerraty ApplyModifier_Literal(const char **pp, ModChain *ch)
2533956e45f6SSimon J. Gerraty {
2534b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
2535b0c40a00SSimon J. Gerraty 
2536956e45f6SSimon J. Gerraty 	(*pp)++;
2537b0c40a00SSimon J. Gerraty 
2538b0c40a00SSimon J. Gerraty 	if (Expr_ShouldEval(expr)) {
2539b0c40a00SSimon J. Gerraty 		Expr_Define(expr);
2540b0c40a00SSimon J. Gerraty 		Expr_SetValueOwn(expr, bmake_strdup(expr->name));
2541b0c40a00SSimon J. Gerraty 	}
2542b0c40a00SSimon J. Gerraty 
2543956e45f6SSimon J. Gerraty 	return AMR_OK;
2544956e45f6SSimon J. Gerraty }
2545956e45f6SSimon J. Gerraty 
2546b0c40a00SSimon J. Gerraty static bool
TryParseTime(const char ** pp,time_t * out_time)2547956e45f6SSimon J. Gerraty TryParseTime(const char **pp, time_t *out_time)
2548956e45f6SSimon J. Gerraty {
2549956e45f6SSimon J. Gerraty 	char *end;
2550956e45f6SSimon J. Gerraty 	unsigned long n;
2551956e45f6SSimon J. Gerraty 
2552956e45f6SSimon J. Gerraty 	if (!ch_isdigit(**pp))
2553b0c40a00SSimon J. Gerraty 		return false;
2554956e45f6SSimon J. Gerraty 
2555956e45f6SSimon J. Gerraty 	errno = 0;
2556956e45f6SSimon J. Gerraty 	n = strtoul(*pp, &end, 10);
2557956e45f6SSimon J. Gerraty 	if (n == ULONG_MAX && errno == ERANGE)
2558b0c40a00SSimon J. Gerraty 		return false;
2559956e45f6SSimon J. Gerraty 
2560956e45f6SSimon J. Gerraty 	*pp = end;
2561956e45f6SSimon J. Gerraty 	*out_time = (time_t)n;	/* ignore possible truncation for now */
2562b0c40a00SSimon J. Gerraty 	return true;
2563956e45f6SSimon J. Gerraty }
2564956e45f6SSimon J. Gerraty 
25659f45a3c8SSimon J. Gerraty /* :gmtime and :localtime */
25662c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Time(const char ** pp,ModChain * ch)25679f45a3c8SSimon J. Gerraty ApplyModifier_Time(const char **pp, ModChain *ch)
25683841c287SSimon J. Gerraty {
256912904384SSimon J. Gerraty 	Expr *expr;
25709f45a3c8SSimon J. Gerraty 	time_t t;
25719f45a3c8SSimon J. Gerraty 	const char *args;
25722c3632d1SSimon J. Gerraty 	const char *mod = *pp;
25739f45a3c8SSimon J. Gerraty 	bool gmt = mod[0] == 'g';
25742c3632d1SSimon J. Gerraty 
25759f45a3c8SSimon J. Gerraty 	if (!ModMatchEq(mod, gmt ? "gmtime" : "localtime", ch))
25769f45a3c8SSimon J. Gerraty 		return AMR_UNKNOWN;
25779f45a3c8SSimon J. Gerraty 	args = mod + (gmt ? 6 : 9);
25789f45a3c8SSimon J. Gerraty 
25799f45a3c8SSimon J. Gerraty 	if (args[0] == '=') {
25809f45a3c8SSimon J. Gerraty 		const char *p = args + 1;
2581c1d01b5fSSimon J. Gerraty 		LazyBuf buf;
2582548bfc56SSimon J. Gerraty 		FStr arg;
25838d5c8e21SSimon J. Gerraty 		if (!ParseModifierPart(&p, ':', ch->endc, ch->expr->emode,
2584c1d01b5fSSimon J. Gerraty 		    ch, &buf, NULL, NULL))
2585c1d01b5fSSimon J. Gerraty 			return AMR_CLEANUP;
2586548bfc56SSimon J. Gerraty 		arg = LazyBuf_DoneGet(&buf);
2587c1d01b5fSSimon J. Gerraty 		if (ModChain_ShouldEval(ch)) {
2588548bfc56SSimon J. Gerraty 			const char *arg_p = arg.str;
2589548bfc56SSimon J. Gerraty 			if (!TryParseTime(&arg_p, &t) || *arg_p != '\0') {
259006b9b3e0SSimon J. Gerraty 				Parse_Error(PARSE_FATAL,
2591548bfc56SSimon J. Gerraty 				    "Invalid time value \"%s\"", arg.str);
2592548bfc56SSimon J. Gerraty 				FStr_Done(&arg);
2593956e45f6SSimon J. Gerraty 				return AMR_CLEANUP;
2594956e45f6SSimon J. Gerraty 			}
2595c1d01b5fSSimon J. Gerraty 		} else
2596c1d01b5fSSimon J. Gerraty 			t = 0;
2597548bfc56SSimon J. Gerraty 		FStr_Done(&arg);
2598b0c40a00SSimon J. Gerraty 		*pp = p;
25993841c287SSimon J. Gerraty 	} else {
26009f45a3c8SSimon J. Gerraty 		t = 0;
26019f45a3c8SSimon J. Gerraty 		*pp = args;
26023841c287SSimon J. Gerraty 	}
2603b0c40a00SSimon J. Gerraty 
260412904384SSimon J. Gerraty 	expr = ch->expr;
260512904384SSimon J. Gerraty 	if (Expr_ShouldEval(expr))
2606148ee845SSimon J. Gerraty 		Expr_SetValueOwn(expr, FormatTime(Expr_Str(expr), t, gmt));
2607b0c40a00SSimon J. Gerraty 
26082c3632d1SSimon J. Gerraty 	return AMR_OK;
26093841c287SSimon J. Gerraty }
26103841c287SSimon J. Gerraty 
26113841c287SSimon J. Gerraty /* :hash */
26122c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Hash(const char ** pp,ModChain * ch)2613b0c40a00SSimon J. Gerraty ApplyModifier_Hash(const char **pp, ModChain *ch)
26143841c287SSimon J. Gerraty {
2615b0c40a00SSimon J. Gerraty 	if (!ModMatch(*pp, "hash", ch))
26162c3632d1SSimon J. Gerraty 		return AMR_UNKNOWN;
26172c3632d1SSimon J. Gerraty 	*pp += 4;
2618b0c40a00SSimon J. Gerraty 
2619b0c40a00SSimon J. Gerraty 	if (ModChain_ShouldEval(ch))
2620148ee845SSimon J. Gerraty 		Expr_SetValueOwn(ch->expr, Hash(Expr_Str(ch->expr)));
2621b0c40a00SSimon J. Gerraty 
26222c3632d1SSimon J. Gerraty 	return AMR_OK;
26233841c287SSimon J. Gerraty }
26243841c287SSimon J. Gerraty 
26253841c287SSimon J. Gerraty /* :P */
26262c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Path(const char ** pp,ModChain * ch)2627b0c40a00SSimon J. Gerraty ApplyModifier_Path(const char **pp, ModChain *ch)
26283841c287SSimon J. Gerraty {
2629b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
26303841c287SSimon J. Gerraty 	GNode *gn;
26312c3632d1SSimon J. Gerraty 	char *path;
26323841c287SSimon J. Gerraty 
2633b0c40a00SSimon J. Gerraty 	(*pp)++;
26342c3632d1SSimon J. Gerraty 
263512904384SSimon J. Gerraty 	if (!Expr_ShouldEval(expr))
2636b0c40a00SSimon J. Gerraty 		return AMR_OK;
2637b0c40a00SSimon J. Gerraty 
2638b0c40a00SSimon J. Gerraty 	Expr_Define(expr);
2639b0c40a00SSimon J. Gerraty 
2640b0c40a00SSimon J. Gerraty 	gn = Targ_FindNode(expr->name);
2641d5e0a182SSimon J. Gerraty 	if (gn == NULL || gn->type & OP_NOPATH)
26422c3632d1SSimon J. Gerraty 		path = NULL;
2643d5e0a182SSimon J. Gerraty 	else if (gn->path != NULL)
26442c3632d1SSimon J. Gerraty 		path = bmake_strdup(gn->path);
2645d5e0a182SSimon J. Gerraty 	else {
2646956e45f6SSimon J. Gerraty 		SearchPath *searchPath = Suff_FindPath(gn);
2647b0c40a00SSimon J. Gerraty 		path = Dir_FindFile(expr->name, searchPath);
26483841c287SSimon J. Gerraty 	}
26492c3632d1SSimon J. Gerraty 	if (path == NULL)
2650b0c40a00SSimon J. Gerraty 		path = bmake_strdup(expr->name);
2651b0c40a00SSimon J. Gerraty 	Expr_SetValueOwn(expr, path);
26522c3632d1SSimon J. Gerraty 
26532c3632d1SSimon J. Gerraty 	return AMR_OK;
26543841c287SSimon J. Gerraty }
26553841c287SSimon J. Gerraty 
26563841c287SSimon J. Gerraty /* :!cmd! */
26572c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_ShellCommand(const char ** pp,ModChain * ch)2658b0c40a00SSimon J. Gerraty ApplyModifier_ShellCommand(const char **pp, ModChain *ch)
26593841c287SSimon J. Gerraty {
2660b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
2661b0c40a00SSimon J. Gerraty 	LazyBuf cmdBuf;
2662b0c40a00SSimon J. Gerraty 	FStr cmd;
26633841c287SSimon J. Gerraty 
26642c3632d1SSimon J. Gerraty 	(*pp)++;
26658d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, '!', '!', expr->emode,
26668d5c8e21SSimon J. Gerraty 	    ch, &cmdBuf, NULL, NULL))
26672c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
2668b0c40a00SSimon J. Gerraty 	cmd = LazyBuf_DoneGet(&cmdBuf);
2669b0c40a00SSimon J. Gerraty 
26709f45a3c8SSimon J. Gerraty 	if (Expr_ShouldEval(expr)) {
26719f45a3c8SSimon J. Gerraty 		char *output, *error;
26729f45a3c8SSimon J. Gerraty 		output = Cmd_Exec(cmd.str, &error);
26739f45a3c8SSimon J. Gerraty 		Expr_SetValueOwn(expr, output);
26749f45a3c8SSimon J. Gerraty 		if (error != NULL) {
267522619282SSimon J. Gerraty 			Parse_Error(PARSE_WARNING, "%s", error);
26769f45a3c8SSimon J. Gerraty 			free(error);
26779f45a3c8SSimon J. Gerraty 		}
26789f45a3c8SSimon J. Gerraty 	} else
2679b0c40a00SSimon J. Gerraty 		Expr_SetValueRefer(expr, "");
26809f45a3c8SSimon J. Gerraty 
2681b0c40a00SSimon J. Gerraty 	FStr_Done(&cmd);
2682b0c40a00SSimon J. Gerraty 	Expr_Define(expr);
26832c3632d1SSimon J. Gerraty 
26842c3632d1SSimon J. Gerraty 	return AMR_OK;
26853841c287SSimon J. Gerraty }
26863841c287SSimon J. Gerraty 
268706b9b3e0SSimon J. Gerraty /*
268806b9b3e0SSimon J. Gerraty  * The :range modifier generates an integer sequence as long as the words.
268906b9b3e0SSimon J. Gerraty  * The :range=7 modifier generates an integer sequence from 1 to 7.
269006b9b3e0SSimon J. Gerraty  */
26912c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Range(const char ** pp,ModChain * ch)2692b0c40a00SSimon J. Gerraty ApplyModifier_Range(const char **pp, ModChain *ch)
26933841c287SSimon J. Gerraty {
26942c3632d1SSimon J. Gerraty 	size_t n;
26952c3632d1SSimon J. Gerraty 	Buffer buf;
26962c3632d1SSimon J. Gerraty 	size_t i;
26972c3632d1SSimon J. Gerraty 
26982c3632d1SSimon J. Gerraty 	const char *mod = *pp;
2699b0c40a00SSimon J. Gerraty 	if (!ModMatchEq(mod, "range", ch))
27002c3632d1SSimon J. Gerraty 		return AMR_UNKNOWN;
27012c3632d1SSimon J. Gerraty 
27022c3632d1SSimon J. Gerraty 	if (mod[5] == '=') {
2703956e45f6SSimon J. Gerraty 		const char *p = mod + 6;
2704956e45f6SSimon J. Gerraty 		if (!TryParseSize(&p, &n)) {
270506b9b3e0SSimon J. Gerraty 			Parse_Error(PARSE_FATAL,
2706*a8c56be4SSimon J. Gerraty 			    "Invalid number \"%s\" for modifier \":range\"",
2707b0c40a00SSimon J. Gerraty 			    mod + 6);
2708956e45f6SSimon J. Gerraty 			return AMR_CLEANUP;
2709956e45f6SSimon J. Gerraty 		}
2710956e45f6SSimon J. Gerraty 		*pp = p;
27113841c287SSimon J. Gerraty 	} else {
27123841c287SSimon J. Gerraty 		n = 0;
27132c3632d1SSimon J. Gerraty 		*pp = mod + 5;
27143841c287SSimon J. Gerraty 	}
27152c3632d1SSimon J. Gerraty 
2716b0c40a00SSimon J. Gerraty 	if (!ModChain_ShouldEval(ch))
2717b0c40a00SSimon J. Gerraty 		return AMR_OK;
2718b0c40a00SSimon J. Gerraty 
27192c3632d1SSimon J. Gerraty 	if (n == 0) {
272012904384SSimon J. Gerraty 		SubstringWords words = Expr_Words(ch->expr);
27212c3632d1SSimon J. Gerraty 		n = words.len;
272212904384SSimon J. Gerraty 		SubstringWords_Free(words);
27232c3632d1SSimon J. Gerraty 	}
27242c3632d1SSimon J. Gerraty 
2725e2eeea75SSimon J. Gerraty 	Buf_Init(&buf);
27262c3632d1SSimon J. Gerraty 
27272c3632d1SSimon J. Gerraty 	for (i = 0; i < n; i++) {
272806b9b3e0SSimon J. Gerraty 		if (i != 0) {
27299f45a3c8SSimon J. Gerraty 			/*
27309f45a3c8SSimon J. Gerraty 			 * XXX: Use ch->sep instead of ' ', for consistency.
27319f45a3c8SSimon J. Gerraty 			 */
273206b9b3e0SSimon J. Gerraty 			Buf_AddByte(&buf, ' ');
273306b9b3e0SSimon J. Gerraty 		}
27342c3632d1SSimon J. Gerraty 		Buf_AddInt(&buf, 1 + (int)i);
27352c3632d1SSimon J. Gerraty 	}
27362c3632d1SSimon J. Gerraty 
2737b0c40a00SSimon J. Gerraty 	Expr_SetValueOwn(ch->expr, Buf_DoneData(&buf));
27382c3632d1SSimon J. Gerraty 	return AMR_OK;
27393841c287SSimon J. Gerraty }
27403841c287SSimon J. Gerraty 
2741b0c40a00SSimon J. Gerraty /* Parse a ':M' or ':N' modifier. */
27421d3f2ddcSSimon J. Gerraty static char *
ParseModifier_Match(const char ** pp,const ModChain * ch)27431d3f2ddcSSimon J. Gerraty ParseModifier_Match(const char **pp, const ModChain *ch)
27443841c287SSimon J. Gerraty {
27452c3632d1SSimon J. Gerraty 	const char *mod = *pp;
2746b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
2747b0c40a00SSimon J. Gerraty 	bool copy = false;	/* pattern should be, or has been, copied */
2748b0c40a00SSimon J. Gerraty 	bool needSubst = false;
27492c3632d1SSimon J. Gerraty 	const char *endpat;
27503841c287SSimon J. Gerraty 	char *pattern;
27513841c287SSimon J. Gerraty 
27523841c287SSimon J. Gerraty 	/*
27532c3632d1SSimon J. Gerraty 	 * In the loop below, ignore ':' unless we are at (or back to) the
27542c3632d1SSimon J. Gerraty 	 * original brace level.
2755e2eeea75SSimon J. Gerraty 	 * XXX: This will likely not work right if $() and ${} are intermixed.
27563841c287SSimon J. Gerraty 	 */
2757b0c40a00SSimon J. Gerraty 	/*
2758b0c40a00SSimon J. Gerraty 	 * XXX: This code is similar to the one in Var_Parse.
2759e2eeea75SSimon J. Gerraty 	 * See if the code can be merged.
2760b0c40a00SSimon J. Gerraty 	 * See also ApplyModifier_Defined.
2761b0c40a00SSimon J. Gerraty 	 */
2762d5e0a182SSimon J. Gerraty 	int depth = 0;
27632c3632d1SSimon J. Gerraty 	const char *p;
2764d5e0a182SSimon J. Gerraty 	for (p = mod + 1; *p != '\0' && !(*p == ':' && depth == 0); p++) {
27654fde40d9SSimon J. Gerraty 		if (*p == '\\' && p[1] != '\0' &&
2766b0c40a00SSimon J. Gerraty 		    (IsDelimiter(p[1], ch) || p[1] == ch->startc)) {
27673841c287SSimon J. Gerraty 			if (!needSubst)
2768b0c40a00SSimon J. Gerraty 				copy = true;
27692c3632d1SSimon J. Gerraty 			p++;
27703841c287SSimon J. Gerraty 			continue;
27713841c287SSimon J. Gerraty 		}
27722c3632d1SSimon J. Gerraty 		if (*p == '$')
2773b0c40a00SSimon J. Gerraty 			needSubst = true;
27742c3632d1SSimon J. Gerraty 		if (*p == '(' || *p == '{')
2775d5e0a182SSimon J. Gerraty 			depth++;
27762c3632d1SSimon J. Gerraty 		if (*p == ')' || *p == '}') {
2777d5e0a182SSimon J. Gerraty 			depth--;
2778d5e0a182SSimon J. Gerraty 			if (depth < 0)
27793841c287SSimon J. Gerraty 				break;
27803841c287SSimon J. Gerraty 		}
27813841c287SSimon J. Gerraty 	}
27822c3632d1SSimon J. Gerraty 	*pp = p;
27832c3632d1SSimon J. Gerraty 	endpat = p;
27842c3632d1SSimon J. Gerraty 
27853841c287SSimon J. Gerraty 	if (copy) {
27862c3632d1SSimon J. Gerraty 		char *dst;
27872c3632d1SSimon J. Gerraty 		const char *src;
27882c3632d1SSimon J. Gerraty 
27892c3632d1SSimon J. Gerraty 		/* Compress the \:'s out of the pattern. */
27902c3632d1SSimon J. Gerraty 		pattern = bmake_malloc((size_t)(endpat - (mod + 1)) + 1);
27912c3632d1SSimon J. Gerraty 		dst = pattern;
27922c3632d1SSimon J. Gerraty 		src = mod + 1;
27932c3632d1SSimon J. Gerraty 		for (; src < endpat; src++, dst++) {
27942c3632d1SSimon J. Gerraty 			if (src[0] == '\\' && src + 1 < endpat &&
2795b0c40a00SSimon J. Gerraty 			    /* XXX: ch->startc is missing here; see above */
2796b0c40a00SSimon J. Gerraty 			    IsDelimiter(src[1], ch))
27972c3632d1SSimon J. Gerraty 				src++;
27982c3632d1SSimon J. Gerraty 			*dst = *src;
27993841c287SSimon J. Gerraty 		}
28002c3632d1SSimon J. Gerraty 		*dst = '\0';
28013841c287SSimon J. Gerraty 	} else {
28022c3632d1SSimon J. Gerraty 		pattern = bmake_strsedup(mod + 1, endpat);
28033841c287SSimon J. Gerraty 	}
28042c3632d1SSimon J. Gerraty 
28053841c287SSimon J. Gerraty 	if (needSubst) {
28062c3632d1SSimon J. Gerraty 		char *old_pattern = pattern;
28071d3f2ddcSSimon J. Gerraty 		/*
28081d3f2ddcSSimon J. Gerraty 		 * XXX: Contrary to ParseModifierPart, a dollar in a ':M' or
28091d3f2ddcSSimon J. Gerraty 		 * ':N' modifier must be escaped as '$$', not as '\$'.
28101d3f2ddcSSimon J. Gerraty 		 */
28118c973ee2SSimon J. Gerraty 		pattern = Var_Subst(pattern, expr->scope, expr->emode);
2812956e45f6SSimon J. Gerraty 		/* TODO: handle errors */
28132c3632d1SSimon J. Gerraty 		free(old_pattern);
28143841c287SSimon J. Gerraty 	}
28152c3632d1SSimon J. Gerraty 
2816b0c40a00SSimon J. Gerraty 	DEBUG2(VAR, "Pattern for ':%c' is \"%s\"\n", mod[0], pattern);
28172c3632d1SSimon J. Gerraty 
28181d3f2ddcSSimon J. Gerraty 	return pattern;
2819b0c40a00SSimon J. Gerraty }
2820b0c40a00SSimon J. Gerraty 
2821148ee845SSimon J. Gerraty struct ModifyWord_MatchArgs {
2822148ee845SSimon J. Gerraty 	const char *pattern;
2823148ee845SSimon J. Gerraty 	bool neg;
2824148ee845SSimon J. Gerraty 	bool error_reported;
2825148ee845SSimon J. Gerraty };
2826148ee845SSimon J. Gerraty 
2827148ee845SSimon J. Gerraty static void
ModifyWord_Match(Substring word,SepBuf * buf,void * data)2828148ee845SSimon J. Gerraty ModifyWord_Match(Substring word, SepBuf *buf, void *data)
2829148ee845SSimon J. Gerraty {
2830148ee845SSimon J. Gerraty 	struct ModifyWord_MatchArgs *args = data;
2831148ee845SSimon J. Gerraty 	StrMatchResult res;
2832148ee845SSimon J. Gerraty 	assert(word.end[0] == '\0');	/* assume null-terminated word */
2833148ee845SSimon J. Gerraty 	res = Str_Match(word.start, args->pattern);
2834148ee845SSimon J. Gerraty 	if (res.error != NULL && !args->error_reported) {
2835148ee845SSimon J. Gerraty 		args->error_reported = true;
283622619282SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
2837*a8c56be4SSimon J. Gerraty 		    "%s in pattern \"%s\" of modifier \"%s\"",
2838148ee845SSimon J. Gerraty 		    res.error, args->pattern, args->neg ? ":N" : ":M");
2839148ee845SSimon J. Gerraty 	}
2840148ee845SSimon J. Gerraty 	if (res.matched != args->neg)
2841148ee845SSimon J. Gerraty 		SepBuf_AddSubstring(buf, word);
2842148ee845SSimon J. Gerraty }
2843148ee845SSimon J. Gerraty 
2844b0c40a00SSimon J. Gerraty /* :Mpattern or :Npattern */
2845b0c40a00SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Match(const char ** pp,ModChain * ch)2846b0c40a00SSimon J. Gerraty ApplyModifier_Match(const char **pp, ModChain *ch)
2847b0c40a00SSimon J. Gerraty {
28489f45a3c8SSimon J. Gerraty 	char mod = **pp;
2849b0c40a00SSimon J. Gerraty 	char *pattern;
2850b0c40a00SSimon J. Gerraty 
28511d3f2ddcSSimon J. Gerraty 	pattern = ParseModifier_Match(pp, ch);
2852b0c40a00SSimon J. Gerraty 
2853b0c40a00SSimon J. Gerraty 	if (ModChain_ShouldEval(ch)) {
2854148ee845SSimon J. Gerraty 		struct ModifyWord_MatchArgs args;
2855148ee845SSimon J. Gerraty 		args.pattern = pattern;
2856148ee845SSimon J. Gerraty 		args.neg = mod == 'N';
2857148ee845SSimon J. Gerraty 		args.error_reported = false;
2858148ee845SSimon J. Gerraty 		ModifyWords(ch, ModifyWord_Match, &args, ch->oneBigWord);
2859b0c40a00SSimon J. Gerraty 	}
2860b0c40a00SSimon J. Gerraty 
28613841c287SSimon J. Gerraty 	free(pattern);
28622c3632d1SSimon J. Gerraty 	return AMR_OK;
28633841c287SSimon J. Gerraty }
28643841c287SSimon J. Gerraty 
2865c1d01b5fSSimon J. Gerraty struct ModifyWord_MtimeArgs {
2866c1d01b5fSSimon J. Gerraty 	bool error;
286798875883SSimon J. Gerraty 	bool use_fallback;
2868c1d01b5fSSimon J. Gerraty 	ApplyModifierResult rc;
286998875883SSimon J. Gerraty 	time_t fallback;
2870c1d01b5fSSimon J. Gerraty };
2871c1d01b5fSSimon J. Gerraty 
2872c1d01b5fSSimon J. Gerraty static void
ModifyWord_Mtime(Substring word,SepBuf * buf,void * data)2873c1d01b5fSSimon J. Gerraty ModifyWord_Mtime(Substring word, SepBuf *buf, void *data)
2874c1d01b5fSSimon J. Gerraty {
2875c1d01b5fSSimon J. Gerraty 	struct ModifyWord_MtimeArgs *args = data;
287698875883SSimon J. Gerraty 	struct stat st;
287798875883SSimon J. Gerraty 	char tbuf[21];
2878c1d01b5fSSimon J. Gerraty 
2879c1d01b5fSSimon J. Gerraty 	if (Substring_IsEmpty(word))
2880c1d01b5fSSimon J. Gerraty 		return;
2881c1d01b5fSSimon J. Gerraty 	assert(word.end[0] == '\0');	/* assume null-terminated word */
2882c1d01b5fSSimon J. Gerraty 	if (stat(word.start, &st) < 0) {
2883c1d01b5fSSimon J. Gerraty 		if (args->error) {
2884c1d01b5fSSimon J. Gerraty 			Parse_Error(PARSE_FATAL,
28850b46a53aSSimon J. Gerraty 			    "Cannot determine mtime for \"%s\": %s",
2886c1d01b5fSSimon J. Gerraty 			    word.start, strerror(errno));
2887c1d01b5fSSimon J. Gerraty 			args->rc = AMR_CLEANUP;
2888c1d01b5fSSimon J. Gerraty 			return;
2889c1d01b5fSSimon J. Gerraty 		}
289098875883SSimon J. Gerraty 		if (args->use_fallback)
289198875883SSimon J. Gerraty 			st.st_mtime = args->fallback;
2892c1d01b5fSSimon J. Gerraty 		else
2893c1d01b5fSSimon J. Gerraty 			time(&st.st_mtime);
2894c1d01b5fSSimon J. Gerraty 	}
2895c1d01b5fSSimon J. Gerraty 	snprintf(tbuf, sizeof(tbuf), "%u", (unsigned)st.st_mtime);
2896c1d01b5fSSimon J. Gerraty 	SepBuf_AddStr(buf, tbuf);
2897c1d01b5fSSimon J. Gerraty }
2898c1d01b5fSSimon J. Gerraty 
2899c1d01b5fSSimon J. Gerraty /* :mtime */
2900c1d01b5fSSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Mtime(const char ** pp,ModChain * ch)2901c1d01b5fSSimon J. Gerraty ApplyModifier_Mtime(const char **pp, ModChain *ch)
2902c1d01b5fSSimon J. Gerraty {
2903c1d01b5fSSimon J. Gerraty 	const char *p, *mod = *pp;
2904c1d01b5fSSimon J. Gerraty 	struct ModifyWord_MtimeArgs args;
2905c1d01b5fSSimon J. Gerraty 
2906c1d01b5fSSimon J. Gerraty 	if (!ModMatchEq(mod, "mtime", ch))
2907c1d01b5fSSimon J. Gerraty 		return AMR_UNKNOWN;
2908c1d01b5fSSimon J. Gerraty 	*pp += 5;
2909c1d01b5fSSimon J. Gerraty 	p = *pp;
291098875883SSimon J. Gerraty 	args.error = false;
291198875883SSimon J. Gerraty 	args.use_fallback = p[0] == '=';
2912c1d01b5fSSimon J. Gerraty 	args.rc = AMR_OK;
291398875883SSimon J. Gerraty 	if (args.use_fallback) {
2914c1d01b5fSSimon J. Gerraty 		p++;
291598875883SSimon J. Gerraty 		if (TryParseTime(&p, &args.fallback)) {
2916d5e0a182SSimon J. Gerraty 		} else if (strncmp(p, "error", 5) == 0) {
2917c1d01b5fSSimon J. Gerraty 			p += 5;
291898875883SSimon J. Gerraty 			args.error = true;
2919d5e0a182SSimon J. Gerraty 		} else
2920d5e0a182SSimon J. Gerraty 			goto invalid_argument;
2921d5e0a182SSimon J. Gerraty 		if (!IsDelimiter(*p, ch))
2922d5e0a182SSimon J. Gerraty 			goto invalid_argument;
2923c1d01b5fSSimon J. Gerraty 		*pp = p;
2924c1d01b5fSSimon J. Gerraty 	}
2925c1d01b5fSSimon J. Gerraty 	ModifyWords(ch, ModifyWord_Mtime, &args, ch->oneBigWord);
2926c1d01b5fSSimon J. Gerraty 	return args.rc;
2927d5e0a182SSimon J. Gerraty 
2928d5e0a182SSimon J. Gerraty invalid_argument:
2929d5e0a182SSimon J. Gerraty 	Parse_Error(PARSE_FATAL,
2930*a8c56be4SSimon J. Gerraty 	    "Invalid argument \"%.*s\" for modifier \":mtime\"",
2931d5e0a182SSimon J. Gerraty 	    (int)strcspn(*pp + 1, ":{}()"), *pp + 1);
2932d5e0a182SSimon J. Gerraty 	return AMR_CLEANUP;
2933c1d01b5fSSimon J. Gerraty }
2934c1d01b5fSSimon J. Gerraty 
2935b0c40a00SSimon J. Gerraty static void
ParsePatternFlags(const char ** pp,PatternFlags * pflags,bool * oneBigWord)2936b0c40a00SSimon J. Gerraty ParsePatternFlags(const char **pp, PatternFlags *pflags, bool *oneBigWord)
2937b0c40a00SSimon J. Gerraty {
2938b0c40a00SSimon J. Gerraty 	for (;; (*pp)++) {
2939b0c40a00SSimon J. Gerraty 		if (**pp == 'g')
2940b0c40a00SSimon J. Gerraty 			pflags->subGlobal = true;
2941b0c40a00SSimon J. Gerraty 		else if (**pp == '1')
2942b0c40a00SSimon J. Gerraty 			pflags->subOnce = true;
2943b0c40a00SSimon J. Gerraty 		else if (**pp == 'W')
2944b0c40a00SSimon J. Gerraty 			*oneBigWord = true;
2945b0c40a00SSimon J. Gerraty 		else
2946b0c40a00SSimon J. Gerraty 			break;
2947b0c40a00SSimon J. Gerraty 	}
2948b0c40a00SSimon J. Gerraty }
2949b0c40a00SSimon J. Gerraty 
2950b0c40a00SSimon J. Gerraty MAKE_INLINE PatternFlags
PatternFlags_None(void)2951b0c40a00SSimon J. Gerraty PatternFlags_None(void)
2952b0c40a00SSimon J. Gerraty {
2953b0c40a00SSimon J. Gerraty 	PatternFlags pflags = { false, false, false, false };
2954b0c40a00SSimon J. Gerraty 	return pflags;
2955b0c40a00SSimon J. Gerraty }
2956b0c40a00SSimon J. Gerraty 
29573841c287SSimon J. Gerraty /* :S,from,to, */
29582c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Subst(const char ** pp,ModChain * ch)2959b0c40a00SSimon J. Gerraty ApplyModifier_Subst(const char **pp, ModChain *ch)
29603841c287SSimon J. Gerraty {
2961956e45f6SSimon J. Gerraty 	struct ModifyWord_SubstArgs args;
2962b0c40a00SSimon J. Gerraty 	bool oneBigWord;
2963b0c40a00SSimon J. Gerraty 	LazyBuf lhsBuf, rhsBuf;
29643841c287SSimon J. Gerraty 
29652c3632d1SSimon J. Gerraty 	char delim = (*pp)[1];
29662c3632d1SSimon J. Gerraty 	if (delim == '\0') {
296722619282SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
2968*a8c56be4SSimon J. Gerraty 		    "Missing delimiter for modifier \":S\"");
29692c3632d1SSimon J. Gerraty 		(*pp)++;
29702c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
29712c3632d1SSimon J. Gerraty 	}
29722c3632d1SSimon J. Gerraty 
29732c3632d1SSimon J. Gerraty 	*pp += 2;
29742c3632d1SSimon J. Gerraty 
2975b0c40a00SSimon J. Gerraty 	args.pflags = PatternFlags_None();
2976b0c40a00SSimon J. Gerraty 	args.matched = false;
29773841c287SSimon J. Gerraty 
29782c3632d1SSimon J. Gerraty 	if (**pp == '^') {
2979b0c40a00SSimon J. Gerraty 		args.pflags.anchorStart = true;
29802c3632d1SSimon J. Gerraty 		(*pp)++;
29813841c287SSimon J. Gerraty 	}
29823841c287SSimon J. Gerraty 
29838d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, delim, delim, ch->expr->emode,
29848d5c8e21SSimon J. Gerraty 	    ch, &lhsBuf, &args.pflags, NULL))
29852c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
2986b0c40a00SSimon J. Gerraty 	args.lhs = LazyBuf_Get(&lhsBuf);
29873841c287SSimon J. Gerraty 
29888d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, delim, delim, ch->expr->emode,
29898d5c8e21SSimon J. Gerraty 	    ch, &rhsBuf, NULL, &args)) {
2990b0c40a00SSimon J. Gerraty 		LazyBuf_Done(&lhsBuf);
29912c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
29923841c287SSimon J. Gerraty 	}
2993b0c40a00SSimon J. Gerraty 	args.rhs = LazyBuf_Get(&rhsBuf);
29943841c287SSimon J. Gerraty 
2995b0c40a00SSimon J. Gerraty 	oneBigWord = ch->oneBigWord;
2996b0c40a00SSimon J. Gerraty 	ParsePatternFlags(pp, &args.pflags, &oneBigWord);
29973841c287SSimon J. Gerraty 
2998b0c40a00SSimon J. Gerraty 	ModifyWords(ch, ModifyWord_Subst, &args, oneBigWord);
2999b0c40a00SSimon J. Gerraty 
3000b0c40a00SSimon J. Gerraty 	LazyBuf_Done(&lhsBuf);
3001b0c40a00SSimon J. Gerraty 	LazyBuf_Done(&rhsBuf);
30022c3632d1SSimon J. Gerraty 	return AMR_OK;
30033841c287SSimon J. Gerraty }
30043841c287SSimon J. Gerraty 
3005c59c3bf3SSimon J. Gerraty #ifdef HAVE_REGEX_H
30062c3632d1SSimon J. Gerraty 
30073841c287SSimon J. Gerraty /* :C,from,to, */
30082c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Regex(const char ** pp,ModChain * ch)3009b0c40a00SSimon J. Gerraty ApplyModifier_Regex(const char **pp, ModChain *ch)
30103841c287SSimon J. Gerraty {
3011956e45f6SSimon J. Gerraty 	struct ModifyWord_SubstRegexArgs args;
3012b0c40a00SSimon J. Gerraty 	bool oneBigWord;
30133841c287SSimon J. Gerraty 	int error;
3014b0c40a00SSimon J. Gerraty 	LazyBuf reBuf, replaceBuf;
30159f45a3c8SSimon J. Gerraty 	FStr re;
30163841c287SSimon J. Gerraty 
30172c3632d1SSimon J. Gerraty 	char delim = (*pp)[1];
30182c3632d1SSimon J. Gerraty 	if (delim == '\0') {
301922619282SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
3020*a8c56be4SSimon J. Gerraty 		    "Missing delimiter for modifier \":C\"");
30212c3632d1SSimon J. Gerraty 		(*pp)++;
30222c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
30233841c287SSimon J. Gerraty 	}
30243841c287SSimon J. Gerraty 
30252c3632d1SSimon J. Gerraty 	*pp += 2;
30262c3632d1SSimon J. Gerraty 
30278d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, delim, delim, ch->expr->emode,
30288d5c8e21SSimon J. Gerraty 	    ch, &reBuf, NULL, NULL))
30292c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
3030b0c40a00SSimon J. Gerraty 	re = LazyBuf_DoneGet(&reBuf);
30312c3632d1SSimon J. Gerraty 
30328d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, delim, delim, ch->expr->emode,
30338d5c8e21SSimon J. Gerraty 	    ch, &replaceBuf, NULL, NULL)) {
3034b0c40a00SSimon J. Gerraty 		FStr_Done(&re);
30352c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
30362c3632d1SSimon J. Gerraty 	}
30379f45a3c8SSimon J. Gerraty 	args.replace = LazyBuf_Get(&replaceBuf);
30382c3632d1SSimon J. Gerraty 
3039b0c40a00SSimon J. Gerraty 	args.pflags = PatternFlags_None();
3040b0c40a00SSimon J. Gerraty 	args.matched = false;
3041b0c40a00SSimon J. Gerraty 	oneBigWord = ch->oneBigWord;
3042b0c40a00SSimon J. Gerraty 	ParsePatternFlags(pp, &args.pflags, &oneBigWord);
3043b0c40a00SSimon J. Gerraty 
30444fde40d9SSimon J. Gerraty 	if (!ModChain_ShouldEval(ch))
30454fde40d9SSimon J. Gerraty 		goto done;
30463841c287SSimon J. Gerraty 
3047b0c40a00SSimon J. Gerraty 	error = regcomp(&args.re, re.str, REG_EXTENDED);
304806b9b3e0SSimon J. Gerraty 	if (error != 0) {
3049148ee845SSimon J. Gerraty 		RegexError(error, &args.re, "Regex compilation error");
30509f45a3c8SSimon J. Gerraty 		LazyBuf_Done(&replaceBuf);
3051b0c40a00SSimon J. Gerraty 		FStr_Done(&re);
30522c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
30533841c287SSimon J. Gerraty 	}
30543841c287SSimon J. Gerraty 
30552c3632d1SSimon J. Gerraty 	args.nsub = args.re.re_nsub + 1;
30562c3632d1SSimon J. Gerraty 	if (args.nsub > 10)
30572c3632d1SSimon J. Gerraty 		args.nsub = 10;
3058b0c40a00SSimon J. Gerraty 
3059b0c40a00SSimon J. Gerraty 	ModifyWords(ch, ModifyWord_SubstRegex, &args, oneBigWord);
3060b0c40a00SSimon J. Gerraty 
30612c3632d1SSimon J. Gerraty 	regfree(&args.re);
30624fde40d9SSimon J. Gerraty done:
30639f45a3c8SSimon J. Gerraty 	LazyBuf_Done(&replaceBuf);
3064b0c40a00SSimon J. Gerraty 	FStr_Done(&re);
30652c3632d1SSimon J. Gerraty 	return AMR_OK;
30663841c287SSimon J. Gerraty }
306706b9b3e0SSimon J. Gerraty 
30683841c287SSimon J. Gerraty #endif
30693841c287SSimon J. Gerraty 
3070956e45f6SSimon J. Gerraty /* :Q, :q */
3071956e45f6SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Quote(const char ** pp,ModChain * ch)3072b0c40a00SSimon J. Gerraty ApplyModifier_Quote(const char **pp, ModChain *ch)
3073956e45f6SSimon J. Gerraty {
3074b0c40a00SSimon J. Gerraty 	LazyBuf buf;
3075b0c40a00SSimon J. Gerraty 	bool quoteDollar;
3076b0c40a00SSimon J. Gerraty 
3077b0c40a00SSimon J. Gerraty 	quoteDollar = **pp == 'q';
3078b0c40a00SSimon J. Gerraty 	if (!IsDelimiter((*pp)[1], ch))
3079956e45f6SSimon J. Gerraty 		return AMR_UNKNOWN;
3080b0c40a00SSimon J. Gerraty 	(*pp)++;
3081b0c40a00SSimon J. Gerraty 
3082b0c40a00SSimon J. Gerraty 	if (!ModChain_ShouldEval(ch))
3083b0c40a00SSimon J. Gerraty 		return AMR_OK;
3084b0c40a00SSimon J. Gerraty 
3085148ee845SSimon J. Gerraty 	QuoteShell(Expr_Str(ch->expr), quoteDollar, &buf);
3086b0c40a00SSimon J. Gerraty 	if (buf.data != NULL)
3087b0c40a00SSimon J. Gerraty 		Expr_SetValue(ch->expr, LazyBuf_DoneGet(&buf));
3088b0c40a00SSimon J. Gerraty 	else
3089b0c40a00SSimon J. Gerraty 		LazyBuf_Done(&buf);
3090b0c40a00SSimon J. Gerraty 
3091b0c40a00SSimon J. Gerraty 	return AMR_OK;
3092956e45f6SSimon J. Gerraty }
3093956e45f6SSimon J. Gerraty 
30942c3632d1SSimon J. Gerraty static void
ModifyWord_Copy(Substring word,SepBuf * buf,void * data MAKE_ATTR_UNUSED)3095b0c40a00SSimon J. Gerraty ModifyWord_Copy(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
30963841c287SSimon J. Gerraty {
3097b0c40a00SSimon J. Gerraty 	SepBuf_AddSubstring(buf, word);
30982c3632d1SSimon J. Gerraty }
30993841c287SSimon J. Gerraty 
31002c3632d1SSimon J. Gerraty /* :ts<separator> */
31012c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_ToSep(const char ** pp,ModChain * ch)3102b0c40a00SSimon J. Gerraty ApplyModifier_ToSep(const char **pp, ModChain *ch)
31032c3632d1SSimon J. Gerraty {
3104956e45f6SSimon J. Gerraty 	const char *sep = *pp + 2;
31052c3632d1SSimon J. Gerraty 
3106b0c40a00SSimon J. Gerraty 	/*
3107d5e0a182SSimon J. Gerraty 	 * Even in parse-only mode, apply the side effects, since the side
3108d5e0a182SSimon J. Gerraty 	 * effects are neither observable nor is there a performance penalty.
31098d5c8e21SSimon J. Gerraty 	 * Checking for VARE_EVAL for every single piece of code in here
3110b0c40a00SSimon J. Gerraty 	 * would make the code in this function too hard to read.
3111b0c40a00SSimon J. Gerraty 	 */
3112b0c40a00SSimon J. Gerraty 
31132c3632d1SSimon J. Gerraty 	/* ":ts<any><endc>" or ":ts<any>:" */
3114b0c40a00SSimon J. Gerraty 	if (sep[0] != ch->endc && IsDelimiter(sep[1], ch)) {
31152c3632d1SSimon J. Gerraty 		*pp = sep + 1;
3116b0c40a00SSimon J. Gerraty 		ch->sep = sep[0];
31172c3632d1SSimon J. Gerraty 		goto ok;
31182c3632d1SSimon J. Gerraty 	}
31192c3632d1SSimon J. Gerraty 
31203841c287SSimon J. Gerraty 	/* ":ts<endc>" or ":ts:" */
3121b0c40a00SSimon J. Gerraty 	if (IsDelimiter(sep[0], ch)) {
31222c3632d1SSimon J. Gerraty 		*pp = sep;
3123b0c40a00SSimon J. Gerraty 		ch->sep = '\0';	/* no separator */
31242c3632d1SSimon J. Gerraty 		goto ok;
31252c3632d1SSimon J. Gerraty 	}
31262c3632d1SSimon J. Gerraty 
3127759b177aSSimon J. Gerraty 	if (sep[0] != '\\')
3128759b177aSSimon J. Gerraty 		return AMR_UNKNOWN;
31292c3632d1SSimon J. Gerraty 
31302c3632d1SSimon J. Gerraty 	/* ":ts\n" */
31312c3632d1SSimon J. Gerraty 	if (sep[1] == 'n') {
31322c3632d1SSimon J. Gerraty 		*pp = sep + 2;
3133b0c40a00SSimon J. Gerraty 		ch->sep = '\n';
31342c3632d1SSimon J. Gerraty 		goto ok;
31352c3632d1SSimon J. Gerraty 	}
31362c3632d1SSimon J. Gerraty 
31372c3632d1SSimon J. Gerraty 	/* ":ts\t" */
31382c3632d1SSimon J. Gerraty 	if (sep[1] == 't') {
31392c3632d1SSimon J. Gerraty 		*pp = sep + 2;
3140b0c40a00SSimon J. Gerraty 		ch->sep = '\t';
31412c3632d1SSimon J. Gerraty 		goto ok;
31422c3632d1SSimon J. Gerraty 	}
31432c3632d1SSimon J. Gerraty 
31442c3632d1SSimon J. Gerraty 	/* ":ts\x40" or ":ts\100" */
31452c3632d1SSimon J. Gerraty 	{
3146956e45f6SSimon J. Gerraty 		const char *p = sep + 1;
31473841c287SSimon J. Gerraty 		int base = 8;	/* assume octal */
31483841c287SSimon J. Gerraty 
31492c3632d1SSimon J. Gerraty 		if (sep[1] == 'x') {
31503841c287SSimon J. Gerraty 			base = 16;
3151956e45f6SSimon J. Gerraty 			p++;
3152759b177aSSimon J. Gerraty 		} else if (!ch_isdigit(sep[1]))
3153759b177aSSimon J. Gerraty 			return AMR_UNKNOWN;	/* ":ts\..." */
31542c3632d1SSimon J. Gerraty 
3155b0c40a00SSimon J. Gerraty 		if (!TryParseChar(&p, base, &ch->sep)) {
315606b9b3e0SSimon J. Gerraty 			Parse_Error(PARSE_FATAL,
315712904384SSimon J. Gerraty 			    "Invalid character number at \"%s\"", p);
3158956e45f6SSimon J. Gerraty 			return AMR_CLEANUP;
3159956e45f6SSimon J. Gerraty 		}
3160759b177aSSimon J. Gerraty 		if (!IsDelimiter(*p, ch))
3161759b177aSSimon J. Gerraty 			return AMR_UNKNOWN;
3162956e45f6SSimon J. Gerraty 
3163956e45f6SSimon J. Gerraty 		*pp = p;
31643841c287SSimon J. Gerraty 	}
31653841c287SSimon J. Gerraty 
31662c3632d1SSimon J. Gerraty ok:
3167b0c40a00SSimon J. Gerraty 	ModifyWords(ch, ModifyWord_Copy, NULL, ch->oneBigWord);
31682c3632d1SSimon J. Gerraty 	return AMR_OK;
31692c3632d1SSimon J. Gerraty }
31703841c287SSimon J. Gerraty 
317106b9b3e0SSimon J. Gerraty static char *
str_totitle(const char * str)317222619282SSimon J. Gerraty str_totitle(const char *str)
317322619282SSimon J. Gerraty {
317422619282SSimon J. Gerraty 	size_t i, n = strlen(str) + 1;
317522619282SSimon J. Gerraty 	char *res = bmake_malloc(n);
317622619282SSimon J. Gerraty 	for (i = 0; i < n; i++) {
317722619282SSimon J. Gerraty 		if (i == 0 || ch_isspace(res[i - 1]))
317822619282SSimon J. Gerraty 			res[i] = ch_toupper(str[i]);
317922619282SSimon J. Gerraty 		else
318022619282SSimon J. Gerraty 			res[i] = ch_tolower(str[i]);
318122619282SSimon J. Gerraty 	}
318222619282SSimon J. Gerraty 	return res;
318322619282SSimon J. Gerraty }
318422619282SSimon J. Gerraty 
318522619282SSimon J. Gerraty 
318622619282SSimon J. Gerraty static char *
str_toupper(const char * str)318706b9b3e0SSimon J. Gerraty str_toupper(const char *str)
318806b9b3e0SSimon J. Gerraty {
3189548bfc56SSimon J. Gerraty 	size_t i, n = strlen(str) + 1;
3190548bfc56SSimon J. Gerraty 	char *res = bmake_malloc(n);
3191548bfc56SSimon J. Gerraty 	for (i = 0; i < n; i++)
319206b9b3e0SSimon J. Gerraty 		res[i] = ch_toupper(str[i]);
319306b9b3e0SSimon J. Gerraty 	return res;
319406b9b3e0SSimon J. Gerraty }
319506b9b3e0SSimon J. Gerraty 
319606b9b3e0SSimon J. Gerraty static char *
str_tolower(const char * str)319706b9b3e0SSimon J. Gerraty str_tolower(const char *str)
319806b9b3e0SSimon J. Gerraty {
3199548bfc56SSimon J. Gerraty 	size_t i, n = strlen(str) + 1;
3200548bfc56SSimon J. Gerraty 	char *res = bmake_malloc(n);
3201548bfc56SSimon J. Gerraty 	for (i = 0; i < n; i++)
320206b9b3e0SSimon J. Gerraty 		res[i] = ch_tolower(str[i]);
320306b9b3e0SSimon J. Gerraty 	return res;
320406b9b3e0SSimon J. Gerraty }
320506b9b3e0SSimon J. Gerraty 
32062c3632d1SSimon J. Gerraty /* :tA, :tu, :tl, :ts<separator>, etc. */
32072c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_To(const char ** pp,ModChain * ch)3208b0c40a00SSimon J. Gerraty ApplyModifier_To(const char **pp, ModChain *ch)
32092c3632d1SSimon J. Gerraty {
3210b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
32112c3632d1SSimon J. Gerraty 	const char *mod = *pp;
32122c3632d1SSimon J. Gerraty 	assert(mod[0] == 't');
32133841c287SSimon J. Gerraty 
3214759b177aSSimon J. Gerraty 	if (IsDelimiter(mod[1], ch))
3215759b177aSSimon J. Gerraty 		return AMR_UNKNOWN;		/* ":t<endc>" or ":t:" */
32162c3632d1SSimon J. Gerraty 
32172c3632d1SSimon J. Gerraty 	if (mod[1] == 's')
3218b0c40a00SSimon J. Gerraty 		return ApplyModifier_ToSep(pp, ch);
32192c3632d1SSimon J. Gerraty 
3220759b177aSSimon J. Gerraty 	if (!IsDelimiter(mod[2], ch))
3221759b177aSSimon J. Gerraty 		return AMR_UNKNOWN;
32222c3632d1SSimon J. Gerraty 
3223b0c40a00SSimon J. Gerraty 	if (mod[1] == 'A') {				/* :tA */
32242c3632d1SSimon J. Gerraty 		*pp = mod + 2;
3225b0c40a00SSimon J. Gerraty 		ModifyWords(ch, ModifyWord_Realpath, NULL, ch->oneBigWord);
32262c3632d1SSimon J. Gerraty 		return AMR_OK;
32272c3632d1SSimon J. Gerraty 	}
32282c3632d1SSimon J. Gerraty 
322922619282SSimon J. Gerraty 	if (mod[1] == 't') {				/* :tt */
323022619282SSimon J. Gerraty 		*pp = mod + 2;
323122619282SSimon J. Gerraty 		if (Expr_ShouldEval(expr))
323222619282SSimon J. Gerraty 			Expr_SetValueOwn(expr, str_totitle(Expr_Str(expr)));
323322619282SSimon J. Gerraty 		return AMR_OK;
323422619282SSimon J. Gerraty 	}
323522619282SSimon J. Gerraty 
3236956e45f6SSimon J. Gerraty 	if (mod[1] == 'u') {				/* :tu */
32372c3632d1SSimon J. Gerraty 		*pp = mod + 2;
323812904384SSimon J. Gerraty 		if (Expr_ShouldEval(expr))
323912904384SSimon J. Gerraty 			Expr_SetValueOwn(expr, str_toupper(Expr_Str(expr)));
32402c3632d1SSimon J. Gerraty 		return AMR_OK;
32412c3632d1SSimon J. Gerraty 	}
32422c3632d1SSimon J. Gerraty 
3243956e45f6SSimon J. Gerraty 	if (mod[1] == 'l') {				/* :tl */
32442c3632d1SSimon J. Gerraty 		*pp = mod + 2;
324512904384SSimon J. Gerraty 		if (Expr_ShouldEval(expr))
324612904384SSimon J. Gerraty 			Expr_SetValueOwn(expr, str_tolower(Expr_Str(expr)));
32472c3632d1SSimon J. Gerraty 		return AMR_OK;
32482c3632d1SSimon J. Gerraty 	}
32492c3632d1SSimon J. Gerraty 
3250956e45f6SSimon J. Gerraty 	if (mod[1] == 'W' || mod[1] == 'w') {		/* :tW, :tw */
32512c3632d1SSimon J. Gerraty 		*pp = mod + 2;
3252b0c40a00SSimon J. Gerraty 		ch->oneBigWord = mod[1] == 'W';
32532c3632d1SSimon J. Gerraty 		return AMR_OK;
32542c3632d1SSimon J. Gerraty 	}
32552c3632d1SSimon J. Gerraty 
3256759b177aSSimon J. Gerraty 	return AMR_UNKNOWN;		/* ":t<any>:" or ":t<any><endc>" */
32573841c287SSimon J. Gerraty }
32583841c287SSimon J. Gerraty 
3259956e45f6SSimon J. Gerraty /* :[#], :[1], :[-1..1], etc. */
32602c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Words(const char ** pp,ModChain * ch)3261b0c40a00SSimon J. Gerraty ApplyModifier_Words(const char **pp, ModChain *ch)
32623841c287SSimon J. Gerraty {
3263b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
32642c3632d1SSimon J. Gerraty 	int first, last;
3265956e45f6SSimon J. Gerraty 	const char *p;
3266d5e0a182SSimon J. Gerraty 	LazyBuf argBuf;
3267d5e0a182SSimon J. Gerraty 	FStr arg;
32682c3632d1SSimon J. Gerraty 
32692c3632d1SSimon J. Gerraty 	(*pp)++;		/* skip the '[' */
32708d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, ']', ']', expr->emode,
32718d5c8e21SSimon J. Gerraty 	    ch, &argBuf, NULL, NULL))
32722c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
3273d5e0a182SSimon J. Gerraty 	arg = LazyBuf_DoneGet(&argBuf);
3274d5e0a182SSimon J. Gerraty 	p = arg.str;
32752c3632d1SSimon J. Gerraty 
3276759b177aSSimon J. Gerraty 	if (!IsDelimiter(**pp, ch)) {
3277759b177aSSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
3278759b177aSSimon J. Gerraty 		    "Extra text after \"[%s]\"", arg.str);
3279759b177aSSimon J. Gerraty 		FStr_Done(&arg);
3280759b177aSSimon J. Gerraty 		return AMR_CLEANUP;
3281759b177aSSimon J. Gerraty 	}
32822c3632d1SSimon J. Gerraty 
3283b0c40a00SSimon J. Gerraty 	if (!ModChain_ShouldEval(ch))
3284b0c40a00SSimon J. Gerraty 		goto ok;
3285b0c40a00SSimon J. Gerraty 
3286d5e0a182SSimon J. Gerraty 	if (p[0] == '\0')
3287b0c40a00SSimon J. Gerraty 		goto bad_modifier;		/* Found ":[]". */
32882c3632d1SSimon J. Gerraty 
3289d5e0a182SSimon J. Gerraty 	if (strcmp(p, "#") == 0) {		/* Found ":[#]" */
3290d5e0a182SSimon J. Gerraty 		if (ch->oneBigWord)
3291b0c40a00SSimon J. Gerraty 			Expr_SetValueRefer(expr, "1");
3292d5e0a182SSimon J. Gerraty 		else {
32932c3632d1SSimon J. Gerraty 			Buffer buf;
32942c3632d1SSimon J. Gerraty 
329512904384SSimon J. Gerraty 			SubstringWords words = Expr_Words(expr);
32962c3632d1SSimon J. Gerraty 			size_t ac = words.len;
329712904384SSimon J. Gerraty 			SubstringWords_Free(words);
32982c3632d1SSimon J. Gerraty 
3299d5e0a182SSimon J. Gerraty 			Buf_Init(&buf);
33002c3632d1SSimon J. Gerraty 			Buf_AddInt(&buf, (int)ac);
3301b0c40a00SSimon J. Gerraty 			Expr_SetValueOwn(expr, Buf_DoneData(&buf));
33022c3632d1SSimon J. Gerraty 		}
33032c3632d1SSimon J. Gerraty 		goto ok;
33042c3632d1SSimon J. Gerraty 	}
33052c3632d1SSimon J. Gerraty 
3306d5e0a182SSimon J. Gerraty 	if (strcmp(p, "*") == 0) {		/* ":[*]" */
3307b0c40a00SSimon J. Gerraty 		ch->oneBigWord = true;
33082c3632d1SSimon J. Gerraty 		goto ok;
33092c3632d1SSimon J. Gerraty 	}
33102c3632d1SSimon J. Gerraty 
3311d5e0a182SSimon J. Gerraty 	if (strcmp(p, "@") == 0) {		/* ":[@]" */
3312b0c40a00SSimon J. Gerraty 		ch->oneBigWord = false;
33132c3632d1SSimon J. Gerraty 		goto ok;
33142c3632d1SSimon J. Gerraty 	}
33152c3632d1SSimon J. Gerraty 
3316d5e0a182SSimon J. Gerraty 	/* Expect ":[N]" or ":[start..end]" */
3317956e45f6SSimon J. Gerraty 	if (!TryParseIntBase0(&p, &first))
3318d5e0a182SSimon J. Gerraty 		goto bad_modifier;
33193841c287SSimon J. Gerraty 
3320d5e0a182SSimon J. Gerraty 	if (p[0] == '\0')			/* ":[N]" */
33212c3632d1SSimon J. Gerraty 		last = first;
3322d5e0a182SSimon J. Gerraty 	else if (strncmp(p, "..", 2) == 0) {
3323956e45f6SSimon J. Gerraty 		p += 2;
3324956e45f6SSimon J. Gerraty 		if (!TryParseIntBase0(&p, &last) || *p != '\0')
3325d5e0a182SSimon J. Gerraty 			goto bad_modifier;
33262c3632d1SSimon J. Gerraty 	} else
3327d5e0a182SSimon J. Gerraty 		goto bad_modifier;
33282c3632d1SSimon J. Gerraty 
3329d5e0a182SSimon J. Gerraty 	if (first == 0 && last == 0) {		/* ":[0]" or ":[0..0]" */
3330b0c40a00SSimon J. Gerraty 		ch->oneBigWord = true;
33312c3632d1SSimon J. Gerraty 		goto ok;
33322c3632d1SSimon J. Gerraty 	}
33332c3632d1SSimon J. Gerraty 
3334d5e0a182SSimon J. Gerraty 	if (first == 0 || last == 0)		/* ":[0..N]" or ":[N..0]" */
33352c3632d1SSimon J. Gerraty 		goto bad_modifier;
33362c3632d1SSimon J. Gerraty 
3337b0c40a00SSimon J. Gerraty 	Expr_SetValueOwn(expr,
333812904384SSimon J. Gerraty 	    VarSelectWords(Expr_Str(expr), first, last,
3339b0c40a00SSimon J. Gerraty 		ch->sep, ch->oneBigWord));
33403841c287SSimon J. Gerraty 
33412c3632d1SSimon J. Gerraty ok:
3342d5e0a182SSimon J. Gerraty 	FStr_Done(&arg);
33432c3632d1SSimon J. Gerraty 	return AMR_OK;
33442c3632d1SSimon J. Gerraty 
33452c3632d1SSimon J. Gerraty bad_modifier:
3346759b177aSSimon J. Gerraty 	Parse_Error(PARSE_FATAL, "Invalid modifier \":[%s]\"", arg.str);
3347d5e0a182SSimon J. Gerraty 	FStr_Done(&arg);
3348759b177aSSimon J. Gerraty 	return AMR_CLEANUP;
33493841c287SSimon J. Gerraty }
33503841c287SSimon J. Gerraty 
335122619282SSimon J. Gerraty #if __STDC_VERSION__ >= 199901L || defined(HAVE_LONG_LONG_INT)
335212904384SSimon J. Gerraty # define NUM_TYPE long long
33539f45a3c8SSimon J. Gerraty # define PARSE_NUM_TYPE strtoll
335412904384SSimon J. Gerraty #else
335512904384SSimon J. Gerraty # define NUM_TYPE long
33569f45a3c8SSimon J. Gerraty # define PARSE_NUM_TYPE strtol
335712904384SSimon J. Gerraty #endif
335812904384SSimon J. Gerraty 
335912904384SSimon J. Gerraty static NUM_TYPE
num_val(Substring s)336012904384SSimon J. Gerraty num_val(Substring s)
33613841c287SSimon J. Gerraty {
336212904384SSimon J. Gerraty 	NUM_TYPE val;
336312904384SSimon J. Gerraty 	char *ep;
336412904384SSimon J. Gerraty 
33659f45a3c8SSimon J. Gerraty 	val = PARSE_NUM_TYPE(s.start, &ep, 0);
336612904384SSimon J. Gerraty 	if (ep != s.start) {
336712904384SSimon J. Gerraty 		switch (*ep) {
336812904384SSimon J. Gerraty 		case 'K':
336912904384SSimon J. Gerraty 		case 'k':
337012904384SSimon J. Gerraty 			val <<= 10;
337112904384SSimon J. Gerraty 			break;
337212904384SSimon J. Gerraty 		case 'M':
337312904384SSimon J. Gerraty 		case 'm':
337412904384SSimon J. Gerraty 			val <<= 20;
337512904384SSimon J. Gerraty 			break;
337612904384SSimon J. Gerraty 		case 'G':
337712904384SSimon J. Gerraty 		case 'g':
337812904384SSimon J. Gerraty 			val <<= 30;
337912904384SSimon J. Gerraty 			break;
338012904384SSimon J. Gerraty 		}
338112904384SSimon J. Gerraty 	}
338212904384SSimon J. Gerraty 	return val;
33833841c287SSimon J. Gerraty }
33842c3632d1SSimon J. Gerraty 
33852c3632d1SSimon J. Gerraty static int
SubNumAsc(const void * sa,const void * sb)338612904384SSimon J. Gerraty SubNumAsc(const void *sa, const void *sb)
33872c3632d1SSimon J. Gerraty {
338812904384SSimon J. Gerraty 	NUM_TYPE a, b;
338912904384SSimon J. Gerraty 
339012904384SSimon J. Gerraty 	a = num_val(*((const Substring *)sa));
339112904384SSimon J. Gerraty 	b = num_val(*((const Substring *)sb));
3392d5e0a182SSimon J. Gerraty 	return a > b ? 1 : b > a ? -1 : 0;
339312904384SSimon J. Gerraty }
339412904384SSimon J. Gerraty 
339512904384SSimon J. Gerraty static int
SubNumDesc(const void * sa,const void * sb)339612904384SSimon J. Gerraty SubNumDesc(const void *sa, const void *sb)
339712904384SSimon J. Gerraty {
339812904384SSimon J. Gerraty 	return SubNumAsc(sb, sa);
339912904384SSimon J. Gerraty }
340012904384SSimon J. Gerraty 
340112904384SSimon J. Gerraty static int
Substring_Cmp(Substring a,Substring b)3402d5e0a182SSimon J. Gerraty Substring_Cmp(Substring a, Substring b)
3403d5e0a182SSimon J. Gerraty {
3404d5e0a182SSimon J. Gerraty 	for (; a.start < a.end && b.start < b.end; a.start++, b.start++)
3405d5e0a182SSimon J. Gerraty 		if (a.start[0] != b.start[0])
3406d5e0a182SSimon J. Gerraty 			return (unsigned char)a.start[0]
3407d5e0a182SSimon J. Gerraty 			    - (unsigned char)b.start[0];
3408d5e0a182SSimon J. Gerraty 	return (int)((a.end - a.start) - (b.end - b.start));
3409d5e0a182SSimon J. Gerraty }
3410d5e0a182SSimon J. Gerraty 
3411d5e0a182SSimon J. Gerraty static int
SubStrAsc(const void * sa,const void * sb)341212904384SSimon J. Gerraty SubStrAsc(const void *sa, const void *sb)
341312904384SSimon J. Gerraty {
3414d5e0a182SSimon J. Gerraty 	return Substring_Cmp(*(const Substring *)sa, *(const Substring *)sb);
341512904384SSimon J. Gerraty }
341612904384SSimon J. Gerraty 
341712904384SSimon J. Gerraty static int
SubStrDesc(const void * sa,const void * sb)341812904384SSimon J. Gerraty SubStrDesc(const void *sa, const void *sb)
341912904384SSimon J. Gerraty {
342012904384SSimon J. Gerraty 	return SubStrAsc(sb, sa);
34212c3632d1SSimon J. Gerraty }
34222c3632d1SSimon J. Gerraty 
342306b9b3e0SSimon J. Gerraty static void
ShuffleSubstrings(Substring * strs,size_t n)342412904384SSimon J. Gerraty ShuffleSubstrings(Substring *strs, size_t n)
342506b9b3e0SSimon J. Gerraty {
342606b9b3e0SSimon J. Gerraty 	size_t i;
342706b9b3e0SSimon J. Gerraty 
342806b9b3e0SSimon J. Gerraty 	for (i = n - 1; i > 0; i--) {
342906b9b3e0SSimon J. Gerraty 		size_t rndidx = (size_t)random() % (i + 1);
343012904384SSimon J. Gerraty 		Substring t = strs[i];
343106b9b3e0SSimon J. Gerraty 		strs[i] = strs[rndidx];
343206b9b3e0SSimon J. Gerraty 		strs[rndidx] = t;
343306b9b3e0SSimon J. Gerraty 	}
343406b9b3e0SSimon J. Gerraty }
343506b9b3e0SSimon J. Gerraty 
343612904384SSimon J. Gerraty /*
343712904384SSimon J. Gerraty  * :O		order ascending
343812904384SSimon J. Gerraty  * :Or		order descending
343912904384SSimon J. Gerraty  * :Ox		shuffle
344012904384SSimon J. Gerraty  * :On		numeric ascending
344112904384SSimon J. Gerraty  * :Onr, :Orn	numeric descending
344212904384SSimon J. Gerraty  */
34432c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Order(const char ** pp,ModChain * ch)3444b0c40a00SSimon J. Gerraty ApplyModifier_Order(const char **pp, ModChain *ch)
34452c3632d1SSimon J. Gerraty {
344612904384SSimon J. Gerraty 	const char *mod = *pp;
344712904384SSimon J. Gerraty 	SubstringWords words;
344812904384SSimon J. Gerraty 	int (*cmp)(const void *, const void *);
34492c3632d1SSimon J. Gerraty 
34504fde40d9SSimon J. Gerraty 	if (IsDelimiter(mod[1], ch)) {
345112904384SSimon J. Gerraty 		cmp = SubStrAsc;
34522c3632d1SSimon J. Gerraty 		(*pp)++;
34534fde40d9SSimon J. Gerraty 	} else if (IsDelimiter(mod[2], ch)) {
345412904384SSimon J. Gerraty 		if (mod[1] == 'n')
345512904384SSimon J. Gerraty 			cmp = SubNumAsc;
345612904384SSimon J. Gerraty 		else if (mod[1] == 'r')
345712904384SSimon J. Gerraty 			cmp = SubStrDesc;
345812904384SSimon J. Gerraty 		else if (mod[1] == 'x')
345912904384SSimon J. Gerraty 			cmp = NULL;
346012904384SSimon J. Gerraty 		else
3461759b177aSSimon J. Gerraty 			return AMR_UNKNOWN;
346212904384SSimon J. Gerraty 		*pp += 2;
34634fde40d9SSimon J. Gerraty 	} else if (IsDelimiter(mod[3], ch)) {
346412904384SSimon J. Gerraty 		if ((mod[1] == 'n' && mod[2] == 'r') ||
346512904384SSimon J. Gerraty 		    (mod[1] == 'r' && mod[2] == 'n'))
346612904384SSimon J. Gerraty 			cmp = SubNumDesc;
346712904384SSimon J. Gerraty 		else
3468759b177aSSimon J. Gerraty 			return AMR_UNKNOWN;
346912904384SSimon J. Gerraty 		*pp += 3;
34709f45a3c8SSimon J. Gerraty 	} else
3471759b177aSSimon J. Gerraty 		return AMR_UNKNOWN;
34722c3632d1SSimon J. Gerraty 
3473b0c40a00SSimon J. Gerraty 	if (!ModChain_ShouldEval(ch))
3474b0c40a00SSimon J. Gerraty 		return AMR_OK;
3475b0c40a00SSimon J. Gerraty 
347612904384SSimon J. Gerraty 	words = Expr_Words(ch->expr);
347712904384SSimon J. Gerraty 	if (cmp == NULL)
347812904384SSimon J. Gerraty 		ShuffleSubstrings(words.words, words.len);
347912904384SSimon J. Gerraty 	else {
348012904384SSimon J. Gerraty 		assert(words.words[0].end[0] == '\0');
348112904384SSimon J. Gerraty 		qsort(words.words, words.len, sizeof(words.words[0]), cmp);
348212904384SSimon J. Gerraty 	}
348312904384SSimon J. Gerraty 	Expr_SetValueOwn(ch->expr, SubstringWords_JoinFree(words));
3484b0c40a00SSimon J. Gerraty 
34852c3632d1SSimon J. Gerraty 	return AMR_OK;
34863841c287SSimon J. Gerraty }
34873841c287SSimon J. Gerraty 
34883841c287SSimon J. Gerraty /* :? then : else */
34892c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_IfElse(const char ** pp,ModChain * ch)3490b0c40a00SSimon J. Gerraty ApplyModifier_IfElse(const char **pp, ModChain *ch)
34913841c287SSimon J. Gerraty {
3492b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
34939f45a3c8SSimon J. Gerraty 	LazyBuf thenBuf;
34949f45a3c8SSimon J. Gerraty 	LazyBuf elseBuf;
34953841c287SSimon J. Gerraty 
34968d5c8e21SSimon J. Gerraty 	VarEvalMode then_emode = VARE_PARSE;
34978d5c8e21SSimon J. Gerraty 	VarEvalMode else_emode = VARE_PARSE;
34980b46a53aSSimon J. Gerraty 	int parseErrorsBefore = parseErrors;
34992c3632d1SSimon J. Gerraty 
35006a7405f5SSimon J. Gerraty 	CondResult cond_rc = CR_TRUE;	/* anything other than CR_ERROR */
3501b0c40a00SSimon J. Gerraty 	if (Expr_ShouldEval(expr)) {
350222619282SSimon J. Gerraty 		evalStack.elems[evalStack.len - 1].kind = VSK_COND;
35039f45a3c8SSimon J. Gerraty 		cond_rc = Cond_EvalCondition(expr->name);
35049f45a3c8SSimon J. Gerraty 		if (cond_rc == CR_TRUE)
3505b0c40a00SSimon J. Gerraty 			then_emode = expr->emode;
35060b46a53aSSimon J. Gerraty 		else if (cond_rc == CR_FALSE)
3507b0c40a00SSimon J. Gerraty 			else_emode = expr->emode;
35080b46a53aSSimon J. Gerraty 		else if (parseErrors == parseErrorsBefore)
35090b46a53aSSimon J. Gerraty 			Parse_Error(PARSE_FATAL, "Bad condition");
35103841c287SSimon J. Gerraty 	}
35112c3632d1SSimon J. Gerraty 
351222619282SSimon J. Gerraty 	evalStack.elems[evalStack.len - 1].kind = VSK_COND_THEN;
35132c3632d1SSimon J. Gerraty 	(*pp)++;		/* skip past the '?' */
35148d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, ':', ':', then_emode,
35158d5c8e21SSimon J. Gerraty 	    ch, &thenBuf, NULL, NULL))
35162c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
35173841c287SSimon J. Gerraty 
351822619282SSimon J. Gerraty 	evalStack.elems[evalStack.len - 1].kind = VSK_COND_ELSE;
35198d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, ch->endc, ch->endc, else_emode,
35208d5c8e21SSimon J. Gerraty 	    ch, &elseBuf, NULL, NULL)) {
35219f45a3c8SSimon J. Gerraty 		LazyBuf_Done(&thenBuf);
35222c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
3523b0c40a00SSimon J. Gerraty 	}
35243841c287SSimon J. Gerraty 
3525b0c40a00SSimon J. Gerraty 	(*pp)--;		/* Go back to the ch->endc. */
3526b0c40a00SSimon J. Gerraty 
35279f45a3c8SSimon J. Gerraty 	if (cond_rc == CR_ERROR) {
35289f45a3c8SSimon J. Gerraty 		LazyBuf_Done(&thenBuf);
35299f45a3c8SSimon J. Gerraty 		LazyBuf_Done(&elseBuf);
35302c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
35313841c287SSimon J. Gerraty 	}
35323841c287SSimon J. Gerraty 
353312904384SSimon J. Gerraty 	if (!Expr_ShouldEval(expr)) {
35349f45a3c8SSimon J. Gerraty 		LazyBuf_Done(&thenBuf);
35359f45a3c8SSimon J. Gerraty 		LazyBuf_Done(&elseBuf);
35369f45a3c8SSimon J. Gerraty 	} else if (cond_rc == CR_TRUE) {
35379f45a3c8SSimon J. Gerraty 		Expr_SetValue(expr, LazyBuf_DoneGet(&thenBuf));
35389f45a3c8SSimon J. Gerraty 		LazyBuf_Done(&elseBuf);
35393841c287SSimon J. Gerraty 	} else {
35409f45a3c8SSimon J. Gerraty 		LazyBuf_Done(&thenBuf);
35419f45a3c8SSimon J. Gerraty 		Expr_SetValue(expr, LazyBuf_DoneGet(&elseBuf));
35423841c287SSimon J. Gerraty 	}
3543b0c40a00SSimon J. Gerraty 	Expr_Define(expr);
35442c3632d1SSimon J. Gerraty 	return AMR_OK;
35453841c287SSimon J. Gerraty }
35463841c287SSimon J. Gerraty 
35473841c287SSimon J. Gerraty /*
3548b0c40a00SSimon J. Gerraty  * The ::= modifiers are special in that they do not read the variable value
3549b0c40a00SSimon J. Gerraty  * but instead assign to that variable.  They always expand to an empty
3550b0c40a00SSimon J. Gerraty  * string.
35513955d011SMarcel Moolenaar  *
3552b0c40a00SSimon J. Gerraty  * Their main purpose is in supporting .for loops that generate shell commands
3553b0c40a00SSimon J. Gerraty  * since an ordinary variable assignment at that point would terminate the
3554b0c40a00SSimon J. Gerraty  * dependency group for these targets.  For example:
3555b0c40a00SSimon J. Gerraty  *
3556b0c40a00SSimon J. Gerraty  * list-targets: .USE
35573955d011SMarcel Moolenaar  * .for i in ${.TARGET} ${.TARGET:R}.gz
3558b0c40a00SSimon J. Gerraty  *	@${t::=$i}
3559b0c40a00SSimon J. Gerraty  *	@echo 'The target is ${t:T}.'
35603955d011SMarcel Moolenaar  * .endfor
35613955d011SMarcel Moolenaar  *
35623955d011SMarcel Moolenaar  *	  ::=<str>	Assigns <str> as the new value of variable.
35633955d011SMarcel Moolenaar  *	  ::?=<str>	Assigns <str> as value of variable if
35643955d011SMarcel Moolenaar  *			it was not already set.
35653955d011SMarcel Moolenaar  *	  ::+=<str>	Appends <str> to variable.
35663955d011SMarcel Moolenaar  *	  ::!=<cmd>	Assigns output of <cmd> as the new value of
35673955d011SMarcel Moolenaar  *			variable.
35683955d011SMarcel Moolenaar  */
35692c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Assign(const char ** pp,ModChain * ch)3570b0c40a00SSimon J. Gerraty ApplyModifier_Assign(const char **pp, ModChain *ch)
35713955d011SMarcel Moolenaar {
3572b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
3573dba7b0efSSimon J. Gerraty 	GNode *scope;
3574b0c40a00SSimon J. Gerraty 	FStr val;
3575b0c40a00SSimon J. Gerraty 	LazyBuf buf;
35762c3632d1SSimon J. Gerraty 
35772c3632d1SSimon J. Gerraty 	const char *mod = *pp;
35782c3632d1SSimon J. Gerraty 	const char *op = mod + 1;
3579956e45f6SSimon J. Gerraty 
3580956e45f6SSimon J. Gerraty 	if (op[0] == '=')
35819f45a3c8SSimon J. Gerraty 		goto found_op;
35829f45a3c8SSimon J. Gerraty 	if ((op[0] == '+' || op[0] == '?' || op[0] == '!') && op[1] == '=')
35839f45a3c8SSimon J. Gerraty 		goto found_op;
3584d5e0a182SSimon J. Gerraty 	return AMR_UNKNOWN;	/* "::<unrecognized>" */
35852c3632d1SSimon J. Gerraty 
35869f45a3c8SSimon J. Gerraty found_op:
3587b0c40a00SSimon J. Gerraty 	if (expr->name[0] == '\0') {
3588759b177aSSimon J. Gerraty 		const char *value = op[0] == '=' ? op + 1 : op + 2;
35892c3632d1SSimon J. Gerraty 		*pp = mod + 1;
3590759b177aSSimon J. Gerraty 		/* Take a guess at where the modifier ends. */
3591759b177aSSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
3592759b177aSSimon J. Gerraty 		    "Invalid attempt to assign \"%.*s\" to variable \"\" "
35930b46a53aSSimon J. Gerraty 		    "via modifier \":%.*s\"",
3594759b177aSSimon J. Gerraty 		    (int)strcspn(value, ":)}"), value,
35950b46a53aSSimon J. Gerraty 		    (int)(value - mod), mod);
3596759b177aSSimon J. Gerraty 		return AMR_CLEANUP;
35972c3632d1SSimon J. Gerraty 	}
35982c3632d1SSimon J. Gerraty 
3599d5e0a182SSimon J. Gerraty 	*pp = mod + (op[0] != '=' ? 3 : 2);
36002c3632d1SSimon J. Gerraty 
36018d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, ch->endc, ch->endc, expr->emode,
36028d5c8e21SSimon J. Gerraty 	    ch, &buf, NULL, NULL))
36032c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
3604b0c40a00SSimon J. Gerraty 	val = LazyBuf_DoneGet(&buf);
36052c3632d1SSimon J. Gerraty 
3606b0c40a00SSimon J. Gerraty 	(*pp)--;		/* Go back to the ch->endc. */
36072c3632d1SSimon J. Gerraty 
3608b0c40a00SSimon J. Gerraty 	if (!Expr_ShouldEval(expr))
3609b0c40a00SSimon J. Gerraty 		goto done;
3610b0c40a00SSimon J. Gerraty 
3611b0c40a00SSimon J. Gerraty 	scope = expr->scope;	/* scope where v belongs */
3612d5e0a182SSimon J. Gerraty 	if (expr->defined == DEF_REGULAR && expr->scope != SCOPE_GLOBAL
3613d5e0a182SSimon J. Gerraty 	    && VarFind(expr->name, expr->scope, false) == NULL)
3614b0c40a00SSimon J. Gerraty 		scope = SCOPE_GLOBAL;
3615b0c40a00SSimon J. Gerraty 
36169f45a3c8SSimon J. Gerraty 	if (op[0] == '+')
3617b0c40a00SSimon J. Gerraty 		Var_Append(scope, expr->name, val.str);
36189f45a3c8SSimon J. Gerraty 	else if (op[0] == '!') {
36199f45a3c8SSimon J. Gerraty 		char *output, *error;
36209f45a3c8SSimon J. Gerraty 		output = Cmd_Exec(val.str, &error);
36219f45a3c8SSimon J. Gerraty 		if (error != NULL) {
362222619282SSimon J. Gerraty 			Parse_Error(PARSE_WARNING, "%s", error);
36239f45a3c8SSimon J. Gerraty 			free(error);
36249f45a3c8SSimon J. Gerraty 		} else
36259f45a3c8SSimon J. Gerraty 			Var_Set(scope, expr->name, output);
36269f45a3c8SSimon J. Gerraty 		free(output);
36279f45a3c8SSimon J. Gerraty 	} else if (op[0] == '?' && expr->defined == DEF_REGULAR) {
36289f45a3c8SSimon J. Gerraty 		/* Do nothing. */
36299f45a3c8SSimon J. Gerraty 	} else
3630b0c40a00SSimon J. Gerraty 		Var_Set(scope, expr->name, val.str);
36319f45a3c8SSimon J. Gerraty 
3632b0c40a00SSimon J. Gerraty 	Expr_SetValueRefer(expr, "");
3633b0c40a00SSimon J. Gerraty 
3634b0c40a00SSimon J. Gerraty done:
3635b0c40a00SSimon J. Gerraty 	FStr_Done(&val);
36362c3632d1SSimon J. Gerraty 	return AMR_OK;
36372c3632d1SSimon J. Gerraty }
36382c3632d1SSimon J. Gerraty 
363906b9b3e0SSimon J. Gerraty /*
364006b9b3e0SSimon J. Gerraty  * :_=...
364106b9b3e0SSimon J. Gerraty  * remember current value
364206b9b3e0SSimon J. Gerraty  */
36432c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Remember(const char ** pp,ModChain * ch)3644b0c40a00SSimon J. Gerraty ApplyModifier_Remember(const char **pp, ModChain *ch)
36452c3632d1SSimon J. Gerraty {
3646b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
36472c3632d1SSimon J. Gerraty 	const char *mod = *pp;
3648b0c40a00SSimon J. Gerraty 	FStr name;
3649b0c40a00SSimon J. Gerraty 
3650b0c40a00SSimon J. Gerraty 	if (!ModMatchEq(mod, "_", ch))
36512c3632d1SSimon J. Gerraty 		return AMR_UNKNOWN;
36522c3632d1SSimon J. Gerraty 
3653b0c40a00SSimon J. Gerraty 	name = FStr_InitRefer("_");
36542c3632d1SSimon J. Gerraty 	if (mod[1] == '=') {
3655b0c40a00SSimon J. Gerraty 		/*
3656b0c40a00SSimon J. Gerraty 		 * XXX: This ad-hoc call to strcspn deviates from the usual
3657b0c40a00SSimon J. Gerraty 		 * behavior defined in ParseModifierPart.  This creates an
3658d5e0a182SSimon J. Gerraty 		 * unnecessary and undocumented inconsistency in make.
3659b0c40a00SSimon J. Gerraty 		 */
3660b0c40a00SSimon J. Gerraty 		const char *arg = mod + 2;
3661b0c40a00SSimon J. Gerraty 		size_t argLen = strcspn(arg, ":)}");
3662b0c40a00SSimon J. Gerraty 		*pp = arg + argLen;
3663b0c40a00SSimon J. Gerraty 		name = FStr_InitOwn(bmake_strldup(arg, argLen));
3664b0c40a00SSimon J. Gerraty 	} else
36652c3632d1SSimon J. Gerraty 		*pp = mod + 1;
3666b0c40a00SSimon J. Gerraty 
3667b0c40a00SSimon J. Gerraty 	if (Expr_ShouldEval(expr))
3668dc1b8c9aSSimon J. Gerraty 		Var_Set(SCOPE_GLOBAL, name.str, Expr_Str(expr));
3669b0c40a00SSimon J. Gerraty 	FStr_Done(&name);
3670b0c40a00SSimon J. Gerraty 
36712c3632d1SSimon J. Gerraty 	return AMR_OK;
36722c3632d1SSimon J. Gerraty }
36732c3632d1SSimon J. Gerraty 
367406b9b3e0SSimon J. Gerraty /*
367506b9b3e0SSimon J. Gerraty  * Apply the given function to each word of the variable value,
367606b9b3e0SSimon J. Gerraty  * for a single-letter modifier such as :H, :T.
367706b9b3e0SSimon J. Gerraty  */
36782c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_WordFunc(const char ** pp,ModChain * ch,ModifyWordProc modifyWord)3679b0c40a00SSimon J. Gerraty ApplyModifier_WordFunc(const char **pp, ModChain *ch,
3680b0c40a00SSimon J. Gerraty 		       ModifyWordProc modifyWord)
36812c3632d1SSimon J. Gerraty {
3682b0c40a00SSimon J. Gerraty 	if (!IsDelimiter((*pp)[1], ch))
36832c3632d1SSimon J. Gerraty 		return AMR_UNKNOWN;
36842c3632d1SSimon J. Gerraty 	(*pp)++;
3685b0c40a00SSimon J. Gerraty 
3686b0c40a00SSimon J. Gerraty 	ModifyWords(ch, modifyWord, NULL, ch->oneBigWord);
3687b0c40a00SSimon J. Gerraty 
36882c3632d1SSimon J. Gerraty 	return AMR_OK;
36892c3632d1SSimon J. Gerraty }
36902c3632d1SSimon J. Gerraty 
3691b0c40a00SSimon J. Gerraty /* Remove adjacent duplicate words. */
3692956e45f6SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_Unique(const char ** pp,ModChain * ch)3693b0c40a00SSimon J. Gerraty ApplyModifier_Unique(const char **pp, ModChain *ch)
3694956e45f6SSimon J. Gerraty {
369512904384SSimon J. Gerraty 	SubstringWords words;
3696b0c40a00SSimon J. Gerraty 
3697b0c40a00SSimon J. Gerraty 	if (!IsDelimiter((*pp)[1], ch))
3698956e45f6SSimon J. Gerraty 		return AMR_UNKNOWN;
3699b0c40a00SSimon J. Gerraty 	(*pp)++;
3700b0c40a00SSimon J. Gerraty 
3701b0c40a00SSimon J. Gerraty 	if (!ModChain_ShouldEval(ch))
3702b0c40a00SSimon J. Gerraty 		return AMR_OK;
3703b0c40a00SSimon J. Gerraty 
370412904384SSimon J. Gerraty 	words = Expr_Words(ch->expr);
3705b0c40a00SSimon J. Gerraty 
3706b0c40a00SSimon J. Gerraty 	if (words.len > 1) {
3707d5e0a182SSimon J. Gerraty 		size_t di, si;
37080b46a53aSSimon J. Gerraty 		for (di = 0, si = 1; si < words.len; si++)
37090b46a53aSSimon J. Gerraty 			if (!Substring_Eq(words.words[di], words.words[si]))
37100b46a53aSSimon J. Gerraty 				words.words[++di] = words.words[si];
3711b0c40a00SSimon J. Gerraty 		words.len = di + 1;
3712b0c40a00SSimon J. Gerraty 	}
3713b0c40a00SSimon J. Gerraty 
371412904384SSimon J. Gerraty 	Expr_SetValueOwn(ch->expr, SubstringWords_JoinFree(words));
3715b0c40a00SSimon J. Gerraty 
3716b0c40a00SSimon J. Gerraty 	return AMR_OK;
3717956e45f6SSimon J. Gerraty }
3718956e45f6SSimon J. Gerraty 
3719d5e0a182SSimon J. Gerraty /* Test whether the modifier has the form '<lhs>=<rhs>'. */
3720d5e0a182SSimon J. Gerraty static bool
IsSysVModifier(const char * p,char startc,char endc)3721d5e0a182SSimon J. Gerraty IsSysVModifier(const char *p, char startc, char endc)
3722d5e0a182SSimon J. Gerraty {
3723d5e0a182SSimon J. Gerraty 	bool eqFound = false;
3724d5e0a182SSimon J. Gerraty 
3725d5e0a182SSimon J. Gerraty 	int depth = 1;
3726759b177aSSimon J. Gerraty 	while (*p != '\0') {
3727d5e0a182SSimon J. Gerraty 		if (*p == '=')	/* XXX: should also test depth == 1 */
3728d5e0a182SSimon J. Gerraty 			eqFound = true;
3729759b177aSSimon J. Gerraty 		else if (*p == endc) {
3730759b177aSSimon J. Gerraty 			if (--depth == 0)
3731759b177aSSimon J. Gerraty 				break;
3732759b177aSSimon J. Gerraty 		} else if (*p == startc)
3733d5e0a182SSimon J. Gerraty 			depth++;
3734d5e0a182SSimon J. Gerraty 		p++;
3735d5e0a182SSimon J. Gerraty 	}
3736759b177aSSimon J. Gerraty 	return eqFound;
3737d5e0a182SSimon J. Gerraty }
3738d5e0a182SSimon J. Gerraty 
37392c3632d1SSimon J. Gerraty /* :from=to */
37402c3632d1SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_SysV(const char ** pp,ModChain * ch)3741b0c40a00SSimon J. Gerraty ApplyModifier_SysV(const char **pp, ModChain *ch)
37422c3632d1SSimon J. Gerraty {
3743b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
3744b0c40a00SSimon J. Gerraty 	LazyBuf lhsBuf, rhsBuf;
3745b0c40a00SSimon J. Gerraty 	FStr rhs;
3746b0c40a00SSimon J. Gerraty 	struct ModifyWord_SysVSubstArgs args;
3747b0c40a00SSimon J. Gerraty 	Substring lhs;
3748b0c40a00SSimon J. Gerraty 	const char *lhsSuffix;
37492c3632d1SSimon J. Gerraty 
37502c3632d1SSimon J. Gerraty 	const char *mod = *pp;
37512c3632d1SSimon J. Gerraty 
3752d5e0a182SSimon J. Gerraty 	if (!IsSysVModifier(mod, ch->startc, ch->endc))
37532c3632d1SSimon J. Gerraty 		return AMR_UNKNOWN;
37542c3632d1SSimon J. Gerraty 
37558d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, '=', '=', expr->emode,
37568d5c8e21SSimon J. Gerraty 	    ch, &lhsBuf, NULL, NULL))
37572c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
37582c3632d1SSimon J. Gerraty 
37598d5c8e21SSimon J. Gerraty 	if (!ParseModifierPart(pp, ch->endc, ch->endc, expr->emode,
37608d5c8e21SSimon J. Gerraty 	    ch, &rhsBuf, NULL, NULL)) {
3761b0c40a00SSimon J. Gerraty 		LazyBuf_Done(&lhsBuf);
37622c3632d1SSimon J. Gerraty 		return AMR_CLEANUP;
37632c3632d1SSimon J. Gerraty 	}
3764b0c40a00SSimon J. Gerraty 	rhs = LazyBuf_DoneGet(&rhsBuf);
3765b0c40a00SSimon J. Gerraty 
3766b0c40a00SSimon J. Gerraty 	(*pp)--;		/* Go back to the ch->endc. */
3767b0c40a00SSimon J. Gerraty 
3768b0c40a00SSimon J. Gerraty 	/* Do not turn an empty expression into non-empty. */
376912904384SSimon J. Gerraty 	if (lhsBuf.len == 0 && Expr_Str(expr)[0] == '\0')
3770b0c40a00SSimon J. Gerraty 		goto done;
3771b0c40a00SSimon J. Gerraty 
3772b0c40a00SSimon J. Gerraty 	lhs = LazyBuf_Get(&lhsBuf);
3773b0c40a00SSimon J. Gerraty 	lhsSuffix = Substring_SkipFirst(lhs, '%');
3774b0c40a00SSimon J. Gerraty 
3775b0c40a00SSimon J. Gerraty 	args.scope = expr->scope;
3776b0c40a00SSimon J. Gerraty 	args.lhsPrefix = Substring_Init(lhs.start,
3777b0c40a00SSimon J. Gerraty 	    lhsSuffix != lhs.start ? lhsSuffix - 1 : lhs.start);
3778b0c40a00SSimon J. Gerraty 	args.lhsPercent = lhsSuffix != lhs.start;
3779b0c40a00SSimon J. Gerraty 	args.lhsSuffix = Substring_Init(lhsSuffix, lhs.end);
3780b0c40a00SSimon J. Gerraty 	args.rhs = rhs.str;
3781b0c40a00SSimon J. Gerraty 
3782b0c40a00SSimon J. Gerraty 	ModifyWords(ch, ModifyWord_SysVSubst, &args, ch->oneBigWord);
3783b0c40a00SSimon J. Gerraty 
3784b0c40a00SSimon J. Gerraty done:
3785b0c40a00SSimon J. Gerraty 	LazyBuf_Done(&lhsBuf);
37868c973ee2SSimon J. Gerraty 	FStr_Done(&rhs);
37872c3632d1SSimon J. Gerraty 	return AMR_OK;
37882c3632d1SSimon J. Gerraty }
37892c3632d1SSimon J. Gerraty 
3790956e45f6SSimon J. Gerraty /* :sh */
3791956e45f6SSimon J. Gerraty static ApplyModifierResult
ApplyModifier_SunShell(const char ** pp,ModChain * ch)3792b0c40a00SSimon J. Gerraty ApplyModifier_SunShell(const char **pp, ModChain *ch)
3793956e45f6SSimon J. Gerraty {
3794b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
3795956e45f6SSimon J. Gerraty 	const char *p = *pp;
3796b0c40a00SSimon J. Gerraty 	if (!(p[1] == 'h' && IsDelimiter(p[2], ch)))
3797956e45f6SSimon J. Gerraty 		return AMR_UNKNOWN;
3798b0c40a00SSimon J. Gerraty 	*pp = p + 2;
3799b0c40a00SSimon J. Gerraty 
3800b0c40a00SSimon J. Gerraty 	if (Expr_ShouldEval(expr)) {
38019f45a3c8SSimon J. Gerraty 		char *output, *error;
38029f45a3c8SSimon J. Gerraty 		output = Cmd_Exec(Expr_Str(expr), &error);
38039f45a3c8SSimon J. Gerraty 		if (error != NULL) {
380422619282SSimon J. Gerraty 			Parse_Error(PARSE_WARNING, "%s", error);
38059f45a3c8SSimon J. Gerraty 			free(error);
38069f45a3c8SSimon J. Gerraty 		}
3807b0c40a00SSimon J. Gerraty 		Expr_SetValueOwn(expr, output);
3808b0c40a00SSimon J. Gerraty 	}
3809b0c40a00SSimon J. Gerraty 
3810b0c40a00SSimon J. Gerraty 	return AMR_OK;
3811956e45f6SSimon J. Gerraty }
3812956e45f6SSimon J. Gerraty 
38138c973ee2SSimon J. Gerraty /*
38148c973ee2SSimon J. Gerraty  * In cases where the evaluation mode and the definedness are the "standard"
38158c973ee2SSimon J. Gerraty  * ones, don't log them, to keep the logs readable.
38168c973ee2SSimon J. Gerraty  */
38178c973ee2SSimon J. Gerraty static bool
ShouldLogInSimpleFormat(const Expr * expr)38188c973ee2SSimon J. Gerraty ShouldLogInSimpleFormat(const Expr *expr)
38198c973ee2SSimon J. Gerraty {
38206a7405f5SSimon J. Gerraty 	return (expr->emode == VARE_EVAL
38216a7405f5SSimon J. Gerraty 		|| expr->emode == VARE_EVAL_DEFINED
38226a7405f5SSimon J. Gerraty 		|| expr->emode == VARE_EVAL_DEFINED_LOUD)
38238d5c8e21SSimon J. Gerraty 	    && expr->defined == DEF_REGULAR;
38248c973ee2SSimon J. Gerraty }
38258c973ee2SSimon J. Gerraty 
3826956e45f6SSimon J. Gerraty static void
LogBeforeApply(const ModChain * ch,const char * mod)3827b0c40a00SSimon J. Gerraty LogBeforeApply(const ModChain *ch, const char *mod)
3828956e45f6SSimon J. Gerraty {
3829b0c40a00SSimon J. Gerraty 	const Expr *expr = ch->expr;
3830b0c40a00SSimon J. Gerraty 	bool is_single_char = mod[0] != '\0' && IsDelimiter(mod[1], ch);
3831956e45f6SSimon J. Gerraty 
3832b0c40a00SSimon J. Gerraty 	/*
3833b0c40a00SSimon J. Gerraty 	 * At this point, only the first character of the modifier can
3834b0c40a00SSimon J. Gerraty 	 * be used since the end of the modifier is not yet known.
3835b0c40a00SSimon J. Gerraty 	 */
3836b0c40a00SSimon J. Gerraty 
3837b0c40a00SSimon J. Gerraty 	if (!Expr_ShouldEval(expr)) {
3838b0c40a00SSimon J. Gerraty 		debug_printf("Parsing modifier ${%s:%c%s}\n",
3839b0c40a00SSimon J. Gerraty 		    expr->name, mod[0], is_single_char ? "" : "...");
3840b0c40a00SSimon J. Gerraty 		return;
3841b0c40a00SSimon J. Gerraty 	}
3842b0c40a00SSimon J. Gerraty 
38438c973ee2SSimon J. Gerraty 	if (ShouldLogInSimpleFormat(expr)) {
3844b0c40a00SSimon J. Gerraty 		debug_printf(
3845b0c40a00SSimon J. Gerraty 		    "Evaluating modifier ${%s:%c%s} on value \"%s\"\n",
3846b0c40a00SSimon J. Gerraty 		    expr->name, mod[0], is_single_char ? "" : "...",
384712904384SSimon J. Gerraty 		    Expr_Str(expr));
3848b0c40a00SSimon J. Gerraty 		return;
3849b0c40a00SSimon J. Gerraty 	}
3850b0c40a00SSimon J. Gerraty 
3851b0c40a00SSimon J. Gerraty 	debug_printf(
3852b0c40a00SSimon J. Gerraty 	    "Evaluating modifier ${%s:%c%s} on value \"%s\" (%s, %s)\n",
385312904384SSimon J. Gerraty 	    expr->name, mod[0], is_single_char ? "" : "...", Expr_Str(expr),
3854b0c40a00SSimon J. Gerraty 	    VarEvalMode_Name[expr->emode], ExprDefined_Name[expr->defined]);
3855956e45f6SSimon J. Gerraty }
3856956e45f6SSimon J. Gerraty 
3857956e45f6SSimon J. Gerraty static void
LogAfterApply(const ModChain * ch,const char * p,const char * mod)3858b0c40a00SSimon J. Gerraty LogAfterApply(const ModChain *ch, const char *p, const char *mod)
3859956e45f6SSimon J. Gerraty {
3860b0c40a00SSimon J. Gerraty 	const Expr *expr = ch->expr;
386112904384SSimon J. Gerraty 	const char *value = Expr_Str(expr);
3862956e45f6SSimon J. Gerraty 
38638c973ee2SSimon J. Gerraty 	if (ShouldLogInSimpleFormat(expr)) {
3864759b177aSSimon J. Gerraty 		debug_printf("Result of ${%s:%.*s} is \"%s\"\n",
3865759b177aSSimon J. Gerraty 		    expr->name, (int)(p - mod), mod, value);
3866b0c40a00SSimon J. Gerraty 		return;
3867b0c40a00SSimon J. Gerraty 	}
3868b0c40a00SSimon J. Gerraty 
3869759b177aSSimon J. Gerraty 	debug_printf("Result of ${%s:%.*s} is \"%s\" (%s, %s)\n",
3870759b177aSSimon J. Gerraty 	    expr->name, (int)(p - mod), mod, value,
3871b0c40a00SSimon J. Gerraty 	    VarEvalMode_Name[expr->emode],
3872b0c40a00SSimon J. Gerraty 	    ExprDefined_Name[expr->defined]);
3873956e45f6SSimon J. Gerraty }
3874956e45f6SSimon J. Gerraty 
3875956e45f6SSimon J. Gerraty static ApplyModifierResult
ApplyModifier(const char ** pp,ModChain * ch)3876b0c40a00SSimon J. Gerraty ApplyModifier(const char **pp, ModChain *ch)
3877956e45f6SSimon J. Gerraty {
3878956e45f6SSimon J. Gerraty 	switch (**pp) {
3879956e45f6SSimon J. Gerraty 	case '!':
3880b0c40a00SSimon J. Gerraty 		return ApplyModifier_ShellCommand(pp, ch);
3881b0c40a00SSimon J. Gerraty 	case ':':
3882b0c40a00SSimon J. Gerraty 		return ApplyModifier_Assign(pp, ch);
3883956e45f6SSimon J. Gerraty 	case '?':
3884b0c40a00SSimon J. Gerraty 		return ApplyModifier_IfElse(pp, ch);
3885b0c40a00SSimon J. Gerraty 	case '@':
3886b0c40a00SSimon J. Gerraty 		return ApplyModifier_Loop(pp, ch);
3887b0c40a00SSimon J. Gerraty 	case '[':
3888b0c40a00SSimon J. Gerraty 		return ApplyModifier_Words(pp, ch);
3889b0c40a00SSimon J. Gerraty 	case '_':
3890b0c40a00SSimon J. Gerraty 		return ApplyModifier_Remember(pp, ch);
3891c59c3bf3SSimon J. Gerraty #ifdef HAVE_REGEX_H
3892956e45f6SSimon J. Gerraty 	case 'C':
3893b0c40a00SSimon J. Gerraty 		return ApplyModifier_Regex(pp, ch);
3894956e45f6SSimon J. Gerraty #endif
3895b0c40a00SSimon J. Gerraty 	case 'D':
389612904384SSimon J. Gerraty 	case 'U':
3897b0c40a00SSimon J. Gerraty 		return ApplyModifier_Defined(pp, ch);
3898956e45f6SSimon J. Gerraty 	case 'E':
3899b0c40a00SSimon J. Gerraty 		return ApplyModifier_WordFunc(pp, ch, ModifyWord_Suffix);
3900b0c40a00SSimon J. Gerraty 	case 'g':
39019f45a3c8SSimon J. Gerraty 	case 'l':
39029f45a3c8SSimon J. Gerraty 		return ApplyModifier_Time(pp, ch);
3903b0c40a00SSimon J. Gerraty 	case 'H':
3904b0c40a00SSimon J. Gerraty 		return ApplyModifier_WordFunc(pp, ch, ModifyWord_Head);
3905b0c40a00SSimon J. Gerraty 	case 'h':
3906b0c40a00SSimon J. Gerraty 		return ApplyModifier_Hash(pp, ch);
3907b0c40a00SSimon J. Gerraty 	case 'L':
3908b0c40a00SSimon J. Gerraty 		return ApplyModifier_Literal(pp, ch);
3909b0c40a00SSimon J. Gerraty 	case 'M':
3910b0c40a00SSimon J. Gerraty 	case 'N':
3911b0c40a00SSimon J. Gerraty 		return ApplyModifier_Match(pp, ch);
3912c1d01b5fSSimon J. Gerraty 	case 'm':
3913c1d01b5fSSimon J. Gerraty 		return ApplyModifier_Mtime(pp, ch);
3914956e45f6SSimon J. Gerraty 	case 'O':
3915b0c40a00SSimon J. Gerraty 		return ApplyModifier_Order(pp, ch);
3916b0c40a00SSimon J. Gerraty 	case 'P':
3917b0c40a00SSimon J. Gerraty 		return ApplyModifier_Path(pp, ch);
3918b0c40a00SSimon J. Gerraty 	case 'Q':
3919b0c40a00SSimon J. Gerraty 	case 'q':
3920b0c40a00SSimon J. Gerraty 		return ApplyModifier_Quote(pp, ch);
3921b0c40a00SSimon J. Gerraty 	case 'R':
3922b0c40a00SSimon J. Gerraty 		return ApplyModifier_WordFunc(pp, ch, ModifyWord_Root);
3923b0c40a00SSimon J. Gerraty 	case 'r':
3924b0c40a00SSimon J. Gerraty 		return ApplyModifier_Range(pp, ch);
3925b0c40a00SSimon J. Gerraty 	case 'S':
3926b0c40a00SSimon J. Gerraty 		return ApplyModifier_Subst(pp, ch);
3927956e45f6SSimon J. Gerraty 	case 's':
3928b0c40a00SSimon J. Gerraty 		return ApplyModifier_SunShell(pp, ch);
3929b0c40a00SSimon J. Gerraty 	case 'T':
3930b0c40a00SSimon J. Gerraty 		return ApplyModifier_WordFunc(pp, ch, ModifyWord_Tail);
3931b0c40a00SSimon J. Gerraty 	case 't':
3932b0c40a00SSimon J. Gerraty 		return ApplyModifier_To(pp, ch);
3933b0c40a00SSimon J. Gerraty 	case 'u':
3934b0c40a00SSimon J. Gerraty 		return ApplyModifier_Unique(pp, ch);
3935956e45f6SSimon J. Gerraty 	default:
3936956e45f6SSimon J. Gerraty 		return AMR_UNKNOWN;
3937956e45f6SSimon J. Gerraty 	}
3938956e45f6SSimon J. Gerraty }
3939956e45f6SSimon J. Gerraty 
3940b0c40a00SSimon J. Gerraty static void ApplyModifiers(Expr *, const char **, char, char);
3941956e45f6SSimon J. Gerraty 
3942956e45f6SSimon J. Gerraty typedef enum ApplyModifiersIndirectResult {
394306b9b3e0SSimon J. Gerraty 	/* The indirect modifiers have been applied successfully. */
3944956e45f6SSimon J. Gerraty 	AMIR_CONTINUE,
394506b9b3e0SSimon J. Gerraty 	/* Fall back to the SysV modifier. */
3946b0c40a00SSimon J. Gerraty 	AMIR_SYSV,
394706b9b3e0SSimon J. Gerraty 	/* Error out. */
3948956e45f6SSimon J. Gerraty 	AMIR_OUT
3949956e45f6SSimon J. Gerraty } ApplyModifiersIndirectResult;
3950956e45f6SSimon J. Gerraty 
395106b9b3e0SSimon J. Gerraty /*
3952d5e0a182SSimon J. Gerraty  * While expanding an expression, expand and apply indirect modifiers,
395306b9b3e0SSimon J. Gerraty  * such as in ${VAR:${M_indirect}}.
395406b9b3e0SSimon J. Gerraty  *
3955d5e0a182SSimon J. Gerraty  * All indirect modifiers of a group must come from a single
395606b9b3e0SSimon J. Gerraty  * expression.  ${VAR:${M1}} is valid but ${VAR:${M1}${M2}} is not.
395706b9b3e0SSimon J. Gerraty  *
395806b9b3e0SSimon J. Gerraty  * Multiple groups of indirect modifiers can be chained by separating them
395906b9b3e0SSimon J. Gerraty  * with colons.  ${VAR:${M1}:${M2}} contains 2 indirect modifiers.
396006b9b3e0SSimon J. Gerraty  *
3961d5e0a182SSimon J. Gerraty  * If the expression is not followed by ch->endc or ':', fall
396206b9b3e0SSimon J. Gerraty  * back to trying the SysV modifier, such as in ${VAR:${FROM}=${TO}}.
396306b9b3e0SSimon J. Gerraty  */
3964956e45f6SSimon J. Gerraty static ApplyModifiersIndirectResult
ApplyModifiersIndirect(ModChain * ch,const char ** pp)3965b0c40a00SSimon J. Gerraty ApplyModifiersIndirect(ModChain *ch, const char **pp)
396606b9b3e0SSimon J. Gerraty {
3967b0c40a00SSimon J. Gerraty 	Expr *expr = ch->expr;
396806b9b3e0SSimon J. Gerraty 	const char *p = *pp;
39698c973ee2SSimon J. Gerraty 	FStr mods = Var_Parse(&p, expr->scope, expr->emode);
3970956e45f6SSimon J. Gerraty 	/* TODO: handle errors */
3971956e45f6SSimon J. Gerraty 
39724fde40d9SSimon J. Gerraty 	if (mods.str[0] != '\0' && !IsDelimiter(*p, ch)) {
397306b9b3e0SSimon J. Gerraty 		FStr_Done(&mods);
3974b0c40a00SSimon J. Gerraty 		return AMIR_SYSV;
3975956e45f6SSimon J. Gerraty 	}
3976956e45f6SSimon J. Gerraty 
397706b9b3e0SSimon J. Gerraty 	DEBUG3(VAR, "Indirect modifier \"%s\" from \"%.*s\"\n",
397806b9b3e0SSimon J. Gerraty 	    mods.str, (int)(p - *pp), *pp);
3979956e45f6SSimon J. Gerraty 
3980c59c3bf3SSimon J. Gerraty 	if (ModChain_ShouldEval(ch) && mods.str[0] != '\0') {
398106b9b3e0SSimon J. Gerraty 		const char *modsp = mods.str;
3982759b177aSSimon J. Gerraty 		EvalStack_Push(VSK_INDIRECT_MODIFIERS, mods.str, NULL);
3983b0c40a00SSimon J. Gerraty 		ApplyModifiers(expr, &modsp, '\0', '\0');
3984759b177aSSimon J. Gerraty 		EvalStack_Pop();
398512904384SSimon J. Gerraty 		if (Expr_Str(expr) == var_Error || *modsp != '\0') {
398606b9b3e0SSimon J. Gerraty 			FStr_Done(&mods);
398706b9b3e0SSimon J. Gerraty 			*pp = p;
3988956e45f6SSimon J. Gerraty 			return AMIR_OUT;	/* error already reported */
3989956e45f6SSimon J. Gerraty 		}
3990956e45f6SSimon J. Gerraty 	}
399106b9b3e0SSimon J. Gerraty 	FStr_Done(&mods);
3992956e45f6SSimon J. Gerraty 
3993956e45f6SSimon J. Gerraty 	if (*p == ':')
3994956e45f6SSimon J. Gerraty 		p++;
3995b0c40a00SSimon J. Gerraty 	else if (*p == '\0' && ch->endc != '\0') {
399622619282SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
399722619282SSimon J. Gerraty 		    "Unclosed expression after indirect modifier, "
3998*a8c56be4SSimon J. Gerraty 		    "expecting \"%c\"",
399922619282SSimon J. Gerraty 		    ch->endc);
400006b9b3e0SSimon J. Gerraty 		*pp = p;
4001956e45f6SSimon J. Gerraty 		return AMIR_OUT;
4002956e45f6SSimon J. Gerraty 	}
4003956e45f6SSimon J. Gerraty 
400406b9b3e0SSimon J. Gerraty 	*pp = p;
4005956e45f6SSimon J. Gerraty 	return AMIR_CONTINUE;
4006956e45f6SSimon J. Gerraty }
4007956e45f6SSimon J. Gerraty 
400806b9b3e0SSimon J. Gerraty static ApplyModifierResult
ApplySingleModifier(const char ** pp,ModChain * ch)4009b0c40a00SSimon J. Gerraty ApplySingleModifier(const char **pp, ModChain *ch)
401006b9b3e0SSimon J. Gerraty {
401106b9b3e0SSimon J. Gerraty 	ApplyModifierResult res;
4012b0c40a00SSimon J. Gerraty 	const char *mod = *pp;
401306b9b3e0SSimon J. Gerraty 	const char *p = *pp;
401406b9b3e0SSimon J. Gerraty 
401506b9b3e0SSimon J. Gerraty 	if (DEBUG(VAR))
4016b0c40a00SSimon J. Gerraty 		LogBeforeApply(ch, mod);
401706b9b3e0SSimon J. Gerraty 
4018759b177aSSimon J. Gerraty 	if (posix_state == PS_SET)
4019759b177aSSimon J. Gerraty 		res = ApplyModifier_SysV(&p, ch);
4020759b177aSSimon J. Gerraty 	else
4021759b177aSSimon J. Gerraty 		res = AMR_UNKNOWN;
4022759b177aSSimon J. Gerraty 	if (res == AMR_UNKNOWN)
4023b0c40a00SSimon J. Gerraty 		res = ApplyModifier(&p, ch);
402406b9b3e0SSimon J. Gerraty 
4025759b177aSSimon J. Gerraty 	if (res == AMR_UNKNOWN && posix_state != PS_SET) {
402606b9b3e0SSimon J. Gerraty 		assert(p == mod);
4027b0c40a00SSimon J. Gerraty 		res = ApplyModifier_SysV(&p, ch);
402806b9b3e0SSimon J. Gerraty 	}
402906b9b3e0SSimon J. Gerraty 
403006b9b3e0SSimon J. Gerraty 	if (res == AMR_UNKNOWN) {
403106b9b3e0SSimon J. Gerraty 		/*
403206b9b3e0SSimon J. Gerraty 		 * Guess the end of the current modifier.
403306b9b3e0SSimon J. Gerraty 		 * XXX: Skipping the rest of the modifier hides
403406b9b3e0SSimon J. Gerraty 		 * errors and leads to wrong results.
403506b9b3e0SSimon J. Gerraty 		 * Parsing should rather stop here.
403606b9b3e0SSimon J. Gerraty 		 */
40374fde40d9SSimon J. Gerraty 		for (p++; !IsDelimiter(*p, ch); p++)
403806b9b3e0SSimon J. Gerraty 			continue;
4039759b177aSSimon J. Gerraty 		Parse_Error(PARSE_FATAL, "Unknown modifier \":%.*s\"",
4040b0c40a00SSimon J. Gerraty 		    (int)(p - mod), mod);
4041b0c40a00SSimon J. Gerraty 		Expr_SetValueRefer(ch->expr, var_Error);
4042759b177aSSimon J. Gerraty 		res = AMR_CLEANUP;
404306b9b3e0SSimon J. Gerraty 	}
4044759b177aSSimon J. Gerraty 	if (res != AMR_OK) {
404506b9b3e0SSimon J. Gerraty 		*pp = p;
404606b9b3e0SSimon J. Gerraty 		return res;
404706b9b3e0SSimon J. Gerraty 	}
404806b9b3e0SSimon J. Gerraty 
404906b9b3e0SSimon J. Gerraty 	if (DEBUG(VAR))
4050b0c40a00SSimon J. Gerraty 		LogAfterApply(ch, p, mod);
405106b9b3e0SSimon J. Gerraty 
4052b0c40a00SSimon J. Gerraty 	if (*p == '\0' && ch->endc != '\0') {
405322619282SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
4054*a8c56be4SSimon J. Gerraty 		    "Unclosed expression, expecting \"%c\" for "
405522619282SSimon J. Gerraty 		    "modifier \"%.*s\"",
405622619282SSimon J. Gerraty 		    ch->endc, (int)(p - mod), mod);
405706b9b3e0SSimon J. Gerraty 	} else if (*p == ':') {
405806b9b3e0SSimon J. Gerraty 		p++;
4059b0c40a00SSimon J. Gerraty 	} else if (opts.strict && *p != '\0' && *p != ch->endc) {
406006b9b3e0SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
4061*a8c56be4SSimon J. Gerraty 		    "Missing delimiter \":\" after modifier \"%.*s\"",
406206b9b3e0SSimon J. Gerraty 		    (int)(p - mod), mod);
406306b9b3e0SSimon J. Gerraty 		/*
406406b9b3e0SSimon J. Gerraty 		 * TODO: propagate parse error to the enclosing
406506b9b3e0SSimon J. Gerraty 		 * expression
406606b9b3e0SSimon J. Gerraty 		 */
406706b9b3e0SSimon J. Gerraty 	}
406806b9b3e0SSimon J. Gerraty 	*pp = p;
406906b9b3e0SSimon J. Gerraty 	return AMR_OK;
407006b9b3e0SSimon J. Gerraty }
407106b9b3e0SSimon J. Gerraty 
4072b0c40a00SSimon J. Gerraty #if __STDC_VERSION__ >= 199901L
4073d5e0a182SSimon J. Gerraty #define ModChain_Init(expr, startc, endc, sep, oneBigWord) \
4074b0c40a00SSimon J. Gerraty 	(ModChain) { expr, startc, endc, sep, oneBigWord }
4075b0c40a00SSimon J. Gerraty #else
4076b0c40a00SSimon J. Gerraty MAKE_INLINE ModChain
ModChain_Init(Expr * expr,char startc,char endc,char sep,bool oneBigWord)4077d5e0a182SSimon J. Gerraty ModChain_Init(Expr *expr, char startc, char endc, char sep, bool oneBigWord)
4078b0c40a00SSimon J. Gerraty {
4079b0c40a00SSimon J. Gerraty 	ModChain ch;
4080b0c40a00SSimon J. Gerraty 	ch.expr = expr;
4081b0c40a00SSimon J. Gerraty 	ch.startc = startc;
4082b0c40a00SSimon J. Gerraty 	ch.endc = endc;
4083b0c40a00SSimon J. Gerraty 	ch.sep = sep;
4084b0c40a00SSimon J. Gerraty 	ch.oneBigWord = oneBigWord;
4085b0c40a00SSimon J. Gerraty 	return ch;
4086b0c40a00SSimon J. Gerraty }
4087b0c40a00SSimon J. Gerraty #endif
4088b0c40a00SSimon J. Gerraty 
40892c3632d1SSimon J. Gerraty /* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */
4090b0c40a00SSimon J. Gerraty static void
ApplyModifiers(Expr * expr,const char ** pp,char startc,char endc)40912c3632d1SSimon J. Gerraty ApplyModifiers(
4092b0c40a00SSimon J. Gerraty     Expr *expr,
409306b9b3e0SSimon J. Gerraty     const char **pp,	/* the parsing position, updated upon return */
4094b0c40a00SSimon J. Gerraty     char startc,	/* '(' or '{'; or '\0' for indirect modifiers */
4095b0c40a00SSimon J. Gerraty     char endc		/* ')' or '}'; or '\0' for indirect modifiers */
409606b9b3e0SSimon J. Gerraty )
409706b9b3e0SSimon J. Gerraty {
4098d5e0a182SSimon J. Gerraty 	ModChain ch = ModChain_Init(expr, startc, endc, ' ', false);
40992c3632d1SSimon J. Gerraty 	const char *p;
41003955d011SMarcel Moolenaar 
41012c3632d1SSimon J. Gerraty 	assert(startc == '(' || startc == '{' || startc == '\0');
41022c3632d1SSimon J. Gerraty 	assert(endc == ')' || endc == '}' || endc == '\0');
410312904384SSimon J. Gerraty 	assert(Expr_Str(expr) != NULL);
41043955d011SMarcel Moolenaar 
41052c3632d1SSimon J. Gerraty 	p = *pp;
4106e2eeea75SSimon J. Gerraty 
4107e2eeea75SSimon J. Gerraty 	if (*p == '\0' && endc != '\0') {
410822619282SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
4109*a8c56be4SSimon J. Gerraty 		    "Unclosed expression, expecting \"%c\"", ch.endc);
4110e2eeea75SSimon J. Gerraty 		goto cleanup;
4111e2eeea75SSimon J. Gerraty 	}
4112e2eeea75SSimon J. Gerraty 
41132c3632d1SSimon J. Gerraty 	while (*p != '\0' && *p != endc) {
411406b9b3e0SSimon J. Gerraty 		ApplyModifierResult res;
41152c3632d1SSimon J. Gerraty 
41162c3632d1SSimon J. Gerraty 		if (*p == '$') {
4117d5e0a182SSimon J. Gerraty 			/*
4118d5e0a182SSimon J. Gerraty 			 * TODO: Only evaluate the expression once, no matter
4119d5e0a182SSimon J. Gerraty 			 * whether it's an indirect modifier or the initial
4120d5e0a182SSimon J. Gerraty 			 * part of a SysV modifier.
4121d5e0a182SSimon J. Gerraty 			 */
4122b0c40a00SSimon J. Gerraty 			ApplyModifiersIndirectResult amir =
4123b0c40a00SSimon J. Gerraty 			    ApplyModifiersIndirect(&ch, &p);
4124956e45f6SSimon J. Gerraty 			if (amir == AMIR_CONTINUE)
4125956e45f6SSimon J. Gerraty 				continue;
4126956e45f6SSimon J. Gerraty 			if (amir == AMIR_OUT)
412706b9b3e0SSimon J. Gerraty 				break;
41283955d011SMarcel Moolenaar 		}
412906b9b3e0SSimon J. Gerraty 
4130b0c40a00SSimon J. Gerraty 		res = ApplySingleModifier(&p, &ch);
41312c3632d1SSimon J. Gerraty 		if (res == AMR_CLEANUP)
41322c3632d1SSimon J. Gerraty 			goto cleanup;
413306b9b3e0SSimon J. Gerraty 	}
41342c3632d1SSimon J. Gerraty 
41352c3632d1SSimon J. Gerraty 	*pp = p;
413612904384SSimon J. Gerraty 	assert(Expr_Str(expr) != NULL);	/* Use var_Error or varUndefined. */
4137b0c40a00SSimon J. Gerraty 	return;
41383955d011SMarcel Moolenaar 
41393955d011SMarcel Moolenaar cleanup:
4140b0c40a00SSimon J. Gerraty 	/*
4141b0c40a00SSimon J. Gerraty 	 * TODO: Use p + strlen(p) instead, to stop parsing immediately.
4142b0c40a00SSimon J. Gerraty 	 *
41431d3f2ddcSSimon J. Gerraty 	 * In the unit tests, this generates a few shell commands with
41441d3f2ddcSSimon J. Gerraty 	 * unbalanced quotes.  Instead of producing these incomplete strings,
41451d3f2ddcSSimon J. Gerraty 	 * commands with evaluation errors should not be run at all.
4146b0c40a00SSimon J. Gerraty 	 *
4147b0c40a00SSimon J. Gerraty 	 * To make that happen, Var_Subst must report the actual errors
41488c973ee2SSimon J. Gerraty 	 * instead of returning the resulting string unconditionally.
4149b0c40a00SSimon J. Gerraty 	 */
41502c3632d1SSimon J. Gerraty 	*pp = p;
4151b0c40a00SSimon J. Gerraty 	Expr_SetValueRefer(expr, var_Error);
41523955d011SMarcel Moolenaar }
41533955d011SMarcel Moolenaar 
415406b9b3e0SSimon J. Gerraty /*
41551d3f2ddcSSimon J. Gerraty  * Only 4 of the 7 built-in local variables are treated specially as they are
41561d3f2ddcSSimon J. Gerraty  * the only ones that will be set when dynamic sources are expanded.
415706b9b3e0SSimon J. Gerraty  */
4158b0c40a00SSimon J. Gerraty static bool
VarnameIsDynamic(Substring varname)4159b0c40a00SSimon J. Gerraty VarnameIsDynamic(Substring varname)
41602c3632d1SSimon J. Gerraty {
4161b0c40a00SSimon J. Gerraty 	const char *name;
4162b0c40a00SSimon J. Gerraty 	size_t len;
4163b0c40a00SSimon J. Gerraty 
4164b0c40a00SSimon J. Gerraty 	name = varname.start;
4165b0c40a00SSimon J. Gerraty 	len = Substring_Length(varname);
4166956e45f6SSimon J. Gerraty 	if (len == 1 || (len == 2 && (name[1] == 'F' || name[1] == 'D'))) {
4167956e45f6SSimon J. Gerraty 		switch (name[0]) {
41682c3632d1SSimon J. Gerraty 		case '@':
41692c3632d1SSimon J. Gerraty 		case '%':
41702c3632d1SSimon J. Gerraty 		case '*':
41712c3632d1SSimon J. Gerraty 		case '!':
4172b0c40a00SSimon J. Gerraty 			return true;
41732c3632d1SSimon J. Gerraty 		}
4174b0c40a00SSimon J. Gerraty 		return false;
41752c3632d1SSimon J. Gerraty 	}
41762c3632d1SSimon J. Gerraty 
4177956e45f6SSimon J. Gerraty 	if ((len == 7 || len == 8) && name[0] == '.' && ch_isupper(name[1])) {
4178b0c40a00SSimon J. Gerraty 		return Substring_Equals(varname, ".TARGET") ||
4179b0c40a00SSimon J. Gerraty 		       Substring_Equals(varname, ".ARCHIVE") ||
4180b0c40a00SSimon J. Gerraty 		       Substring_Equals(varname, ".PREFIX") ||
4181b0c40a00SSimon J. Gerraty 		       Substring_Equals(varname, ".MEMBER");
41822c3632d1SSimon J. Gerraty 	}
41832c3632d1SSimon J. Gerraty 
4184b0c40a00SSimon J. Gerraty 	return false;
41852c3632d1SSimon J. Gerraty }
41862c3632d1SSimon J. Gerraty 
4187956e45f6SSimon J. Gerraty static const char *
UndefinedShortVarValue(char varname,const GNode * scope)4188dba7b0efSSimon J. Gerraty UndefinedShortVarValue(char varname, const GNode *scope)
41893955d011SMarcel Moolenaar {
4190dba7b0efSSimon J. Gerraty 	if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL) {
41913955d011SMarcel Moolenaar 		/*
4192dba7b0efSSimon J. Gerraty 		 * If substituting a local variable in a non-local scope,
41933955d011SMarcel Moolenaar 		 * assume it's for dynamic source stuff. We have to handle
41943955d011SMarcel Moolenaar 		 * this specially and return the longhand for the variable
41953955d011SMarcel Moolenaar 		 * with the dollar sign escaped so it makes it back to the
41963955d011SMarcel Moolenaar 		 * caller. Only four of the local variables are treated
41973955d011SMarcel Moolenaar 		 * specially as they are the only four that will be set
41983955d011SMarcel Moolenaar 		 * when dynamic sources are expanded.
41993955d011SMarcel Moolenaar 		 */
4200956e45f6SSimon J. Gerraty 		switch (varname) {
42013955d011SMarcel Moolenaar 		case '@':
42022c3632d1SSimon J. Gerraty 			return "$(.TARGET)";
42033955d011SMarcel Moolenaar 		case '%':
42042c3632d1SSimon J. Gerraty 			return "$(.MEMBER)";
42053955d011SMarcel Moolenaar 		case '*':
42062c3632d1SSimon J. Gerraty 			return "$(.PREFIX)";
42073955d011SMarcel Moolenaar 		case '!':
42082c3632d1SSimon J. Gerraty 			return "$(.ARCHIVE)";
42093955d011SMarcel Moolenaar 		}
42103955d011SMarcel Moolenaar 	}
421106b9b3e0SSimon J. Gerraty 	return NULL;
42123955d011SMarcel Moolenaar }
42133955d011SMarcel Moolenaar 
421406b9b3e0SSimon J. Gerraty /*
421506b9b3e0SSimon J. Gerraty  * Parse a variable name, until the end character or a colon, whichever
421606b9b3e0SSimon J. Gerraty  * comes first.
421706b9b3e0SSimon J. Gerraty  */
4218b0c40a00SSimon J. Gerraty static void
ParseVarname(const char ** pp,char startc,char endc,GNode * scope,VarEvalMode emode,LazyBuf * buf)4219956e45f6SSimon J. Gerraty ParseVarname(const char **pp, char startc, char endc,
4220b0c40a00SSimon J. Gerraty 	     GNode *scope, VarEvalMode emode,
4221b0c40a00SSimon J. Gerraty 	     LazyBuf *buf)
4222956e45f6SSimon J. Gerraty {
4223956e45f6SSimon J. Gerraty 	const char *p = *pp;
4224d5e0a182SSimon J. Gerraty 	int depth = 0;
42252c3632d1SSimon J. Gerraty 
4226b0c40a00SSimon J. Gerraty 	LazyBuf_Init(buf, p);
42273955d011SMarcel Moolenaar 
4228956e45f6SSimon J. Gerraty 	while (*p != '\0') {
4229b0c40a00SSimon J. Gerraty 		if ((*p == endc || *p == ':') && depth == 0)
4230b0c40a00SSimon J. Gerraty 			break;
4231956e45f6SSimon J. Gerraty 		if (*p == startc)
42325bcb7424SSimon J. Gerraty 			depth++;
4233b0c40a00SSimon J. Gerraty 		if (*p == endc)
4234b0c40a00SSimon J. Gerraty 			depth--;
4235956e45f6SSimon J. Gerraty 
4236956e45f6SSimon J. Gerraty 		if (*p == '$') {
42378c973ee2SSimon J. Gerraty 			FStr nested_val = Var_Parse(&p, scope, emode);
4238956e45f6SSimon J. Gerraty 			/* TODO: handle errors */
4239b0c40a00SSimon J. Gerraty 			LazyBuf_AddStr(buf, nested_val.str);
424006b9b3e0SSimon J. Gerraty 			FStr_Done(&nested_val);
4241956e45f6SSimon J. Gerraty 		} else {
4242b0c40a00SSimon J. Gerraty 			LazyBuf_Add(buf, *p);
4243956e45f6SSimon J. Gerraty 			p++;
42443955d011SMarcel Moolenaar 		}
4245956e45f6SSimon J. Gerraty 	}
4246956e45f6SSimon J. Gerraty 	*pp = p;
4247956e45f6SSimon J. Gerraty }
4248956e45f6SSimon J. Gerraty 
4249954401e6SSimon J. Gerraty static bool
IsShortVarnameValid(char varname,const char * start)4250954401e6SSimon J. Gerraty IsShortVarnameValid(char varname, const char *start)
4251956e45f6SSimon J. Gerraty {
4252b0c40a00SSimon J. Gerraty 	if (varname != '$' && varname != ':' && varname != '}' &&
4253b0c40a00SSimon J. Gerraty 	    varname != ')' && varname != '\0')
4254954401e6SSimon J. Gerraty 		return true;
4255956e45f6SSimon J. Gerraty 
425606b9b3e0SSimon J. Gerraty 	if (!opts.strict)
4257954401e6SSimon J. Gerraty 		return false;	/* XXX: Missing error message */
4258956e45f6SSimon J. Gerraty 
4259c59c3bf3SSimon J. Gerraty 	if (varname == '$' && save_dollars)
4260956e45f6SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
4261956e45f6SSimon J. Gerraty 		    "To escape a dollar, use \\$, not $$, at \"%s\"", start);
4262956e45f6SSimon J. Gerraty 	else if (varname == '\0')
4263956e45f6SSimon J. Gerraty 		Parse_Error(PARSE_FATAL, "Dollar followed by nothing");
4264c59c3bf3SSimon J. Gerraty 	else if (save_dollars)
4265956e45f6SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
4266*a8c56be4SSimon J. Gerraty 		    "Invalid variable name \"%c\", at \"%s\"", varname, start);
4267956e45f6SSimon J. Gerraty 
4268954401e6SSimon J. Gerraty 	return false;
4269956e45f6SSimon J. Gerraty }
4270956e45f6SSimon J. Gerraty 
427106b9b3e0SSimon J. Gerraty /*
4272b0c40a00SSimon J. Gerraty  * Parse a single-character variable name such as in $V or $@.
427306b9b3e0SSimon J. Gerraty  * Return whether to continue parsing.
427406b9b3e0SSimon J. Gerraty  */
4275b0c40a00SSimon J. Gerraty static bool
ParseVarnameShort(char varname,const char ** pp,GNode * scope,VarEvalMode emode,const char ** out_false_val,Var ** out_true_var)4276b0c40a00SSimon J. Gerraty ParseVarnameShort(char varname, const char **pp, GNode *scope,
4277b0c40a00SSimon J. Gerraty 		  VarEvalMode emode,
42788c973ee2SSimon J. Gerraty 		  const char **out_false_val,
4279b0c40a00SSimon J. Gerraty 		  Var **out_true_var)
4280e2eeea75SSimon J. Gerraty {
4281956e45f6SSimon J. Gerraty 	char name[2];
4282956e45f6SSimon J. Gerraty 	Var *v;
4283954401e6SSimon J. Gerraty 	const char *val;
4284956e45f6SSimon J. Gerraty 
4285954401e6SSimon J. Gerraty 	if (!IsShortVarnameValid(varname, *pp)) {
4286954401e6SSimon J. Gerraty 		(*pp)++;	/* only skip the '$' */
4287b0c40a00SSimon J. Gerraty 		*out_false_val = var_Error;
4288b0c40a00SSimon J. Gerraty 		return false;
4289956e45f6SSimon J. Gerraty 	}
4290956e45f6SSimon J. Gerraty 
4291b0c40a00SSimon J. Gerraty 	name[0] = varname;
4292956e45f6SSimon J. Gerraty 	name[1] = '\0';
4293b0c40a00SSimon J. Gerraty 	v = VarFind(name, scope, true);
4294954401e6SSimon J. Gerraty 	if (v != NULL) {
4295954401e6SSimon J. Gerraty 		/* No need to advance *pp, the calling code handles this. */
4296954401e6SSimon J. Gerraty 		*out_true_var = v;
4297954401e6SSimon J. Gerraty 		return true;
4298954401e6SSimon J. Gerraty 	}
4299954401e6SSimon J. Gerraty 
4300956e45f6SSimon J. Gerraty 	*pp += 2;
4301956e45f6SSimon J. Gerraty 
4302b0c40a00SSimon J. Gerraty 	val = UndefinedShortVarValue(varname, scope);
430306b9b3e0SSimon J. Gerraty 	if (val == NULL)
43046a7405f5SSimon J. Gerraty 		val = emode == VARE_EVAL_DEFINED
43056a7405f5SSimon J. Gerraty 		    || emode == VARE_EVAL_DEFINED_LOUD
43066a7405f5SSimon J. Gerraty 		    ? var_Error : varUndefined;
430706b9b3e0SSimon J. Gerraty 
43086a7405f5SSimon J. Gerraty 	if ((opts.strict || emode == VARE_EVAL_DEFINED_LOUD)
43096a7405f5SSimon J. Gerraty 	    && val == var_Error) {
431006b9b3e0SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
431106b9b3e0SSimon J. Gerraty 		    "Variable \"%s\" is undefined", name);
4312956e45f6SSimon J. Gerraty 	}
431306b9b3e0SSimon J. Gerraty 
4314b0c40a00SSimon J. Gerraty 	*out_false_val = val;
4315b0c40a00SSimon J. Gerraty 	return false;
4316956e45f6SSimon J. Gerraty }
4317956e45f6SSimon J. Gerraty 
4318e2eeea75SSimon J. Gerraty /* Find variables like @F or <D. */
4319e2eeea75SSimon J. Gerraty static Var *
FindLocalLegacyVar(Substring varname,GNode * scope,const char ** out_extraModifiers)4320b0c40a00SSimon J. Gerraty FindLocalLegacyVar(Substring varname, GNode *scope,
4321e2eeea75SSimon J. Gerraty 		   const char **out_extraModifiers)
4322e2eeea75SSimon J. Gerraty {
4323b0c40a00SSimon J. Gerraty 	Var *v;
4324b0c40a00SSimon J. Gerraty 
4325dba7b0efSSimon J. Gerraty 	/* Only resolve these variables if scope is a "real" target. */
4326dba7b0efSSimon J. Gerraty 	if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL)
4327e2eeea75SSimon J. Gerraty 		return NULL;
4328e2eeea75SSimon J. Gerraty 
4329b0c40a00SSimon J. Gerraty 	if (Substring_Length(varname) != 2)
4330e2eeea75SSimon J. Gerraty 		return NULL;
4331b0c40a00SSimon J. Gerraty 	if (varname.start[1] != 'F' && varname.start[1] != 'D')
4332e2eeea75SSimon J. Gerraty 		return NULL;
4333*a8c56be4SSimon J. Gerraty 	if (strchr("@%?*!<>^", varname.start[0]) == NULL)
4334e2eeea75SSimon J. Gerraty 		return NULL;
4335e2eeea75SSimon J. Gerraty 
4336d5e0a182SSimon J. Gerraty 	v = VarFindSubstring(Substring_Init(varname.start, varname.start + 1),
4337d5e0a182SSimon J. Gerraty 	    scope, false);
4338b0c40a00SSimon J. Gerraty 	if (v == NULL)
4339b0c40a00SSimon J. Gerraty 		return NULL;
4340e2eeea75SSimon J. Gerraty 
4341b0c40a00SSimon J. Gerraty 	*out_extraModifiers = varname.start[1] == 'D' ? "H:" : "T:";
4342e2eeea75SSimon J. Gerraty 	return v;
4343e2eeea75SSimon J. Gerraty }
4344e2eeea75SSimon J. Gerraty 
43458c973ee2SSimon J. Gerraty static FStr
EvalUndefined(bool dynamic,const char * start,const char * p,Substring varname,VarEvalMode emode,int parseErrorsBefore)4346b0c40a00SSimon J. Gerraty EvalUndefined(bool dynamic, const char *start, const char *p,
4347759b177aSSimon J. Gerraty 	      Substring varname, VarEvalMode emode, int parseErrorsBefore)
4348e2eeea75SSimon J. Gerraty {
43498c973ee2SSimon J. Gerraty 	if (dynamic)
43508c973ee2SSimon J. Gerraty 		return FStr_InitOwn(bmake_strsedup(start, p));
4351e2eeea75SSimon J. Gerraty 
43526a7405f5SSimon J. Gerraty 	if (emode == VARE_EVAL_DEFINED_LOUD
43536a7405f5SSimon J. Gerraty 	    || (emode == VARE_EVAL_DEFINED && opts.strict)) {
4354759b177aSSimon J. Gerraty 		if (parseErrors == parseErrorsBefore) {
435506b9b3e0SSimon J. Gerraty 			Parse_Error(PARSE_FATAL,
4356b0c40a00SSimon J. Gerraty 			    "Variable \"%.*s\" is undefined",
4357b0c40a00SSimon J. Gerraty 			    (int) Substring_Length(varname), varname.start);
4358759b177aSSimon J. Gerraty 		}
43598c973ee2SSimon J. Gerraty 		return FStr_InitRefer(var_Error);
4360e2eeea75SSimon J. Gerraty 	}
4361e2eeea75SSimon J. Gerraty 
43628c973ee2SSimon J. Gerraty 	return FStr_InitRefer(
43636a7405f5SSimon J. Gerraty 	    emode == VARE_EVAL_DEFINED_LOUD || emode == VARE_EVAL_DEFINED
43646a7405f5SSimon J. Gerraty 		? var_Error : varUndefined);
4365e2eeea75SSimon J. Gerraty }
4366e2eeea75SSimon J. Gerraty 
43670b46a53aSSimon J. Gerraty static void
CheckVarname(Substring name)43680b46a53aSSimon J. Gerraty CheckVarname(Substring name)
43690b46a53aSSimon J. Gerraty {
43700b46a53aSSimon J. Gerraty 	const char *p;
43710b46a53aSSimon J. Gerraty 
43720b46a53aSSimon J. Gerraty 	for (p = name.start; p < name.end; p++) {
43730b46a53aSSimon J. Gerraty 		if (ch_isspace(*p))
43740b46a53aSSimon J. Gerraty 			break;
43750b46a53aSSimon J. Gerraty 	}
43760b46a53aSSimon J. Gerraty 	if (p < name.end) {
43770b46a53aSSimon J. Gerraty 		Parse_Error(PARSE_WARNING,
43780b46a53aSSimon J. Gerraty 		    ch_isprint(*p)
43790b46a53aSSimon J. Gerraty 		    ? "Invalid character \"%c\" in variable name \"%.*s\""
43800b46a53aSSimon J. Gerraty 		    : "Invalid character \"\\x%02x\" in variable name \"%.*s\"",
43810b46a53aSSimon J. Gerraty 		    (int)(*p),
43820b46a53aSSimon J. Gerraty 		    (int)Substring_Length(name), name.start);
43830b46a53aSSimon J. Gerraty 	}
43840b46a53aSSimon J. Gerraty }
43850b46a53aSSimon J. Gerraty 
438606b9b3e0SSimon J. Gerraty /*
438706b9b3e0SSimon J. Gerraty  * Parse a long variable name enclosed in braces or parentheses such as $(VAR)
4388956e45f6SSimon J. Gerraty  * or ${VAR}, up to the closing brace or parenthesis, or in the case of
4389956e45f6SSimon J. Gerraty  * ${VAR:Modifiers}, up to the ':' that starts the modifiers.
439006b9b3e0SSimon J. Gerraty  * Return whether to continue parsing.
439106b9b3e0SSimon J. Gerraty  */
4392b0c40a00SSimon J. Gerraty static bool
ParseVarnameLong(const char ** pp,char startc,GNode * scope,VarEvalMode emode,VarEvalMode nested_emode,int parseErrorsBefore,const char ** out_false_pp,FStr * out_false_val,char * out_true_endc,Var ** out_true_v,bool * out_true_haveModifier,const char ** out_true_extraModifiers,bool * out_true_dynamic,ExprDefined * out_true_exprDefined)4393956e45f6SSimon J. Gerraty ParseVarnameLong(
4394b0c40a00SSimon J. Gerraty 	const char **pp,
4395956e45f6SSimon J. Gerraty 	char startc,
4396dba7b0efSSimon J. Gerraty 	GNode *scope,
4397b0c40a00SSimon J. Gerraty 	VarEvalMode emode,
43986a7405f5SSimon J. Gerraty 	VarEvalMode nested_emode,
4399759b177aSSimon J. Gerraty 	int parseErrorsBefore,
4400956e45f6SSimon J. Gerraty 
4401b0c40a00SSimon J. Gerraty 	const char **out_false_pp,
4402b0c40a00SSimon J. Gerraty 	FStr *out_false_val,
4403956e45f6SSimon J. Gerraty 
4404b0c40a00SSimon J. Gerraty 	char *out_true_endc,
4405b0c40a00SSimon J. Gerraty 	Var **out_true_v,
4406b0c40a00SSimon J. Gerraty 	bool *out_true_haveModifier,
4407b0c40a00SSimon J. Gerraty 	const char **out_true_extraModifiers,
4408b0c40a00SSimon J. Gerraty 	bool *out_true_dynamic,
4409b0c40a00SSimon J. Gerraty 	ExprDefined *out_true_exprDefined
441006b9b3e0SSimon J. Gerraty )
441106b9b3e0SSimon J. Gerraty {
4412b0c40a00SSimon J. Gerraty 	LazyBuf varname;
441312904384SSimon J. Gerraty 	Substring name;
4414956e45f6SSimon J. Gerraty 	Var *v;
4415b0c40a00SSimon J. Gerraty 	bool haveModifier;
4416b0c40a00SSimon J. Gerraty 	bool dynamic = false;
4417956e45f6SSimon J. Gerraty 
4418b0c40a00SSimon J. Gerraty 	const char *p = *pp;
4419c59c3bf3SSimon J. Gerraty 	const char *start = p;
4420956e45f6SSimon J. Gerraty 	char endc = startc == '(' ? ')' : '}';
4421956e45f6SSimon J. Gerraty 
4422e2eeea75SSimon J. Gerraty 	p += 2;			/* skip "${" or "$(" or "y(" */
44236a7405f5SSimon J. Gerraty 	ParseVarname(&p, startc, endc, scope, nested_emode, &varname);
442412904384SSimon J. Gerraty 	name = LazyBuf_Get(&varname);
4425956e45f6SSimon J. Gerraty 
4426d5e0a182SSimon J. Gerraty 	if (*p == ':')
4427b0c40a00SSimon J. Gerraty 		haveModifier = true;
4428d5e0a182SSimon J. Gerraty 	else if (*p == endc)
4429b0c40a00SSimon J. Gerraty 		haveModifier = false;
4430d5e0a182SSimon J. Gerraty 	else {
4431b0c40a00SSimon J. Gerraty 		Parse_Error(PARSE_FATAL, "Unclosed variable \"%.*s\"",
4432b0c40a00SSimon J. Gerraty 		    (int)Substring_Length(name), name.start);
4433b0c40a00SSimon J. Gerraty 		LazyBuf_Done(&varname);
4434b0c40a00SSimon J. Gerraty 		*out_false_pp = p;
4435b0c40a00SSimon J. Gerraty 		*out_false_val = FStr_InitRefer(var_Error);
4436b0c40a00SSimon J. Gerraty 		return false;
44373955d011SMarcel Moolenaar 	}
44382c3632d1SSimon J. Gerraty 
443912904384SSimon J. Gerraty 	v = VarFindSubstring(name, scope, true);
44403955d011SMarcel Moolenaar 
44419f45a3c8SSimon J. Gerraty 	/*
44429f45a3c8SSimon J. Gerraty 	 * At this point, p points just after the variable name, either at
44439f45a3c8SSimon J. Gerraty 	 * ':' or at endc.
44449f45a3c8SSimon J. Gerraty 	 */
44453955d011SMarcel Moolenaar 
44469f45a3c8SSimon J. Gerraty 	if (v == NULL && Substring_Equals(name, ".SUFFIXES")) {
44479f45a3c8SSimon J. Gerraty 		char *suffixes = Suff_NamesStr();
44489f45a3c8SSimon J. Gerraty 		v = VarNew(FStr_InitRefer(".SUFFIXES"), suffixes,
44499f45a3c8SSimon J. Gerraty 		    true, false, true);
44509f45a3c8SSimon J. Gerraty 		free(suffixes);
44519f45a3c8SSimon J. Gerraty 	} else if (v == NULL)
44529f45a3c8SSimon J. Gerraty 		v = FindLocalLegacyVar(name, scope, out_true_extraModifiers);
44533955d011SMarcel Moolenaar 
44543955d011SMarcel Moolenaar 	if (v == NULL) {
445506b9b3e0SSimon J. Gerraty 		/*
445606b9b3e0SSimon J. Gerraty 		 * Defer expansion of dynamic variables if they appear in
4457dba7b0efSSimon J. Gerraty 		 * non-local scope since they are not defined there.
445806b9b3e0SSimon J. Gerraty 		 */
445912904384SSimon J. Gerraty 		dynamic = VarnameIsDynamic(name) &&
4460dba7b0efSSimon J. Gerraty 			  (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL);
44613955d011SMarcel Moolenaar 
44623955d011SMarcel Moolenaar 		if (!haveModifier) {
44630b46a53aSSimon J. Gerraty 			CheckVarname(name);
4464956e45f6SSimon J. Gerraty 			p++;	/* skip endc */
4465b0c40a00SSimon J. Gerraty 			*out_false_pp = p;
44668c973ee2SSimon J. Gerraty 			*out_false_val = EvalUndefined(dynamic, start, p,
4467759b177aSSimon J. Gerraty 			    name, emode, parseErrorsBefore);
44689f45a3c8SSimon J. Gerraty 			LazyBuf_Done(&varname);
4469b0c40a00SSimon J. Gerraty 			return false;
4470956e45f6SSimon J. Gerraty 		}
4471956e45f6SSimon J. Gerraty 
447206b9b3e0SSimon J. Gerraty 		/*
4473d5e0a182SSimon J. Gerraty 		 * The expression is based on an undefined variable.
4474956e45f6SSimon J. Gerraty 		 * Nevertheless it needs a Var, for modifiers that access the
4475956e45f6SSimon J. Gerraty 		 * variable name, such as :L or :?.
4476956e45f6SSimon J. Gerraty 		 *
447706b9b3e0SSimon J. Gerraty 		 * Most modifiers leave this expression in the "undefined"
4478d5e0a182SSimon J. Gerraty 		 * state (DEF_UNDEF), only a few modifiers like :D, :U, :L,
447906b9b3e0SSimon J. Gerraty 		 * :P turn this undefined expression into a defined
4480d5e0a182SSimon J. Gerraty 		 * expression (DEF_DEFINED).
4481956e45f6SSimon J. Gerraty 		 *
4482b0c40a00SSimon J. Gerraty 		 * In the end, after applying all modifiers, if the expression
4483956e45f6SSimon J. Gerraty 		 * is still undefined, Var_Parse will return an empty string
448406b9b3e0SSimon J. Gerraty 		 * instead of the actually computed value.
448506b9b3e0SSimon J. Gerraty 		 */
44869f45a3c8SSimon J. Gerraty 		v = VarNew(LazyBuf_DoneGet(&varname), "",
44879f45a3c8SSimon J. Gerraty 		    true, false, false);
4488b0c40a00SSimon J. Gerraty 		*out_true_exprDefined = DEF_UNDEF;
4489956e45f6SSimon J. Gerraty 	} else
4490b0c40a00SSimon J. Gerraty 		LazyBuf_Done(&varname);
4491956e45f6SSimon J. Gerraty 
4492b0c40a00SSimon J. Gerraty 	*pp = p;
4493b0c40a00SSimon J. Gerraty 	*out_true_endc = endc;
4494b0c40a00SSimon J. Gerraty 	*out_true_v = v;
4495b0c40a00SSimon J. Gerraty 	*out_true_haveModifier = haveModifier;
4496b0c40a00SSimon J. Gerraty 	*out_true_dynamic = dynamic;
4497b0c40a00SSimon J. Gerraty 	return true;
4498956e45f6SSimon J. Gerraty }
4499956e45f6SSimon J. Gerraty 
4500b0c40a00SSimon J. Gerraty #if __STDC_VERSION__ >= 199901L
4501d5e0a182SSimon J. Gerraty #define Expr_Init(name, value, emode, scope, defined) \
4502d5e0a182SSimon J. Gerraty 	(Expr) { name, value, emode, scope, defined }
4503b0c40a00SSimon J. Gerraty #else
4504b0c40a00SSimon J. Gerraty MAKE_INLINE Expr
Expr_Init(const char * name,FStr value,VarEvalMode emode,GNode * scope,ExprDefined defined)4505d5e0a182SSimon J. Gerraty Expr_Init(const char *name, FStr value,
4506b0c40a00SSimon J. Gerraty 	  VarEvalMode emode, GNode *scope, ExprDefined defined)
4507b0c40a00SSimon J. Gerraty {
4508b0c40a00SSimon J. Gerraty 	Expr expr;
4509b0c40a00SSimon J. Gerraty 
4510b0c40a00SSimon J. Gerraty 	expr.name = name;
4511b0c40a00SSimon J. Gerraty 	expr.value = value;
4512b0c40a00SSimon J. Gerraty 	expr.emode = emode;
4513b0c40a00SSimon J. Gerraty 	expr.scope = scope;
4514b0c40a00SSimon J. Gerraty 	expr.defined = defined;
4515b0c40a00SSimon J. Gerraty 	return expr;
4516b0c40a00SSimon J. Gerraty }
4517b0c40a00SSimon J. Gerraty #endif
4518b0c40a00SSimon J. Gerraty 
4519b0c40a00SSimon J. Gerraty /*
4520b0c40a00SSimon J. Gerraty  * Expressions of the form ${:U...} with a trivial value are often generated
4521d5e0a182SSimon J. Gerraty  * by .for loops and are boring, so evaluate them without debug logging.
4522b0c40a00SSimon J. Gerraty  */
4523b0c40a00SSimon J. Gerraty static bool
Var_Parse_U(const char ** pp,VarEvalMode emode,FStr * out_value)4524548bfc56SSimon J. Gerraty Var_Parse_U(const char **pp, VarEvalMode emode, FStr *out_value)
4525b0c40a00SSimon J. Gerraty {
4526b0c40a00SSimon J. Gerraty 	const char *p;
4527b0c40a00SSimon J. Gerraty 
4528b0c40a00SSimon J. Gerraty 	p = *pp;
4529b0c40a00SSimon J. Gerraty 	if (!(p[0] == '$' && p[1] == '{' && p[2] == ':' && p[3] == 'U'))
4530b0c40a00SSimon J. Gerraty 		return false;
4531b0c40a00SSimon J. Gerraty 
4532b0c40a00SSimon J. Gerraty 	p += 4;
4533b0c40a00SSimon J. Gerraty 	while (*p != '$' && *p != '{' && *p != ':' && *p != '\\' &&
4534b0c40a00SSimon J. Gerraty 	       *p != '}' && *p != '\0')
4535b0c40a00SSimon J. Gerraty 		p++;
4536b0c40a00SSimon J. Gerraty 	if (*p != '}')
4537b0c40a00SSimon J. Gerraty 		return false;
4538b0c40a00SSimon J. Gerraty 
45398d5c8e21SSimon J. Gerraty 	*out_value = emode == VARE_PARSE
4540d5e0a182SSimon J. Gerraty 	    ? FStr_InitRefer("")
4541d5e0a182SSimon J. Gerraty 	    : FStr_InitOwn(bmake_strsedup(*pp + 4, p));
4542b0c40a00SSimon J. Gerraty 	*pp = p + 1;
4543b0c40a00SSimon J. Gerraty 	return true;
4544b0c40a00SSimon J. Gerraty }
4545b0c40a00SSimon J. Gerraty 
4546e2eeea75SSimon J. Gerraty /*
4547d5e0a182SSimon J. Gerraty  * Given the start of an expression (such as $v, $(VAR), ${VAR:Mpattern}),
4548d5e0a182SSimon J. Gerraty  * extract the variable name and the modifiers, if any.  While parsing, apply
4549d5e0a182SSimon J. Gerraty  * the modifiers to the value of the expression.
4550956e45f6SSimon J. Gerraty  *
4551956e45f6SSimon J. Gerraty  * Input:
4552e2eeea75SSimon J. Gerraty  *	*pp		The string to parse.
4553954401e6SSimon J. Gerraty  *			When called from CondParser_FuncCallEmpty, it can
4554954401e6SSimon J. Gerraty  *			also point to the "y" of "empty(VARNAME:Modifiers)".
4555d5e0a182SSimon J. Gerraty  *	scope		The scope for finding variables.
4556d5e0a182SSimon J. Gerraty  *	emode		Controls the exact details of parsing and evaluation.
4557956e45f6SSimon J. Gerraty  *
4558e2eeea75SSimon J. Gerraty  * Output:
4559e2eeea75SSimon J. Gerraty  *	*pp		The position where to continue parsing.
4560e2eeea75SSimon J. Gerraty  *			TODO: After a parse error, the value of *pp is
4561e2eeea75SSimon J. Gerraty  *			unspecified.  It may not have been updated at all,
4562e2eeea75SSimon J. Gerraty  *			point to some random character in the string, to the
4563e2eeea75SSimon J. Gerraty  *			location of the parse error, or at the end of the
4564e2eeea75SSimon J. Gerraty  *			string.
4565d5e0a182SSimon J. Gerraty  *	return		The value of the expression, never NULL.
45668c973ee2SSimon J. Gerraty  *	return		var_Error if there was a parse error.
45678c973ee2SSimon J. Gerraty  *	return		var_Error if the base variable of the expression was
45688d5c8e21SSimon J. Gerraty  *			undefined, emode is VARE_EVAL_DEFINED, and none of
4569e2eeea75SSimon J. Gerraty  *			the modifiers turned the undefined expression into a
4570e2eeea75SSimon J. Gerraty  *			defined expression.
4571e2eeea75SSimon J. Gerraty  *			XXX: It is not guaranteed that an error message has
4572e2eeea75SSimon J. Gerraty  *			been printed.
45738c973ee2SSimon J. Gerraty  *	return		varUndefined if the base variable of the expression
45748d5c8e21SSimon J. Gerraty  *			was undefined, emode was not VARE_EVAL_DEFINED,
4575e2eeea75SSimon J. Gerraty  *			and none of the modifiers turned the undefined
4576e2eeea75SSimon J. Gerraty  *			expression into a defined expression.
4577956e45f6SSimon J. Gerraty  */
45788c973ee2SSimon J. Gerraty FStr
Var_Parse(const char ** pp,GNode * scope,VarEvalMode emode)45798c973ee2SSimon J. Gerraty Var_Parse(const char **pp, GNode *scope, VarEvalMode emode)
4580956e45f6SSimon J. Gerraty {
4581548bfc56SSimon J. Gerraty 	const char *start, *p;
4582954401e6SSimon J. Gerraty 	bool haveModifier;	/* true for ${VAR:...}, false for ${VAR} */
4583954401e6SSimon J. Gerraty 	char startc;		/* the actual '{' or '(' or '\0' */
4584954401e6SSimon J. Gerraty 	char endc;		/* the expected '}' or ')' or '\0' */
458506b9b3e0SSimon J. Gerraty 	/*
4586954401e6SSimon J. Gerraty 	 * true if the expression is based on one of the 7 predefined
4587954401e6SSimon J. Gerraty 	 * variables that are local to a target, and the expression is
4588954401e6SSimon J. Gerraty 	 * expanded in a non-local scope.  The result is the text of the
4589954401e6SSimon J. Gerraty 	 * expression, unaltered.  This is needed to support dynamic sources.
459006b9b3e0SSimon J. Gerraty 	 */
4591b0c40a00SSimon J. Gerraty 	bool dynamic;
4592956e45f6SSimon J. Gerraty 	const char *extramodifiers;
4593956e45f6SSimon J. Gerraty 	Var *v;
45946a7405f5SSimon J. Gerraty 	Expr expr = Expr_Init(NULL, FStr_InitRefer(NULL),
45956a7405f5SSimon J. Gerraty 	    emode == VARE_EVAL_DEFINED || emode == VARE_EVAL_DEFINED_LOUD
45966a7405f5SSimon J. Gerraty 		? VARE_EVAL : emode,
4597b0c40a00SSimon J. Gerraty 	    scope, DEF_REGULAR);
45988c973ee2SSimon J. Gerraty 	FStr val;
4599759b177aSSimon J. Gerraty 	int parseErrorsBefore = parseErrors;
4600956e45f6SSimon J. Gerraty 
4601548bfc56SSimon J. Gerraty 	if (Var_Parse_U(pp, emode, &val))
46028c973ee2SSimon J. Gerraty 		return val;
4603b0c40a00SSimon J. Gerraty 
4604548bfc56SSimon J. Gerraty 	p = *pp;
4605548bfc56SSimon J. Gerraty 	start = p;
4606b0c40a00SSimon J. Gerraty 	DEBUG2(VAR, "Var_Parse: %s (%s)\n", start, VarEvalMode_Name[emode]);
4607956e45f6SSimon J. Gerraty 
46088c973ee2SSimon J. Gerraty 	val = FStr_InitRefer(NULL);
4609956e45f6SSimon J. Gerraty 	extramodifiers = NULL;	/* extra modifiers to apply first */
4610b0c40a00SSimon J. Gerraty 	dynamic = false;
4611956e45f6SSimon J. Gerraty 
4612954401e6SSimon J. Gerraty 	endc = '\0';		/* Appease GCC. */
4613956e45f6SSimon J. Gerraty 
4614e2eeea75SSimon J. Gerraty 	startc = p[1];
4615956e45f6SSimon J. Gerraty 	if (startc != '(' && startc != '{') {
46168c973ee2SSimon J. Gerraty 		if (!ParseVarnameShort(startc, pp, scope, emode, &val.str, &v))
46178c973ee2SSimon J. Gerraty 			return val;
4618b0c40a00SSimon J. Gerraty 		haveModifier = false;
4619e2eeea75SSimon J. Gerraty 		p++;
4620956e45f6SSimon J. Gerraty 	} else {
46216a7405f5SSimon J. Gerraty 		if (!ParseVarnameLong(&p, startc, scope, emode, expr.emode,
4622759b177aSSimon J. Gerraty 		    parseErrorsBefore,
46238c973ee2SSimon J. Gerraty 		    pp, &val,
4624b0c40a00SSimon J. Gerraty 		    &endc, &v, &haveModifier, &extramodifiers,
4625b0c40a00SSimon J. Gerraty 		    &dynamic, &expr.defined))
46268c973ee2SSimon J. Gerraty 			return val;
4627956e45f6SSimon J. Gerraty 	}
4628956e45f6SSimon J. Gerraty 
4629b0c40a00SSimon J. Gerraty 	expr.name = v->name.str;
46304fde40d9SSimon J. Gerraty 	if (v->inUse && VarEvalMode_ShouldEval(emode)) {
46316a7405f5SSimon J. Gerraty 		Parse_Error(PARSE_FATAL, "Variable %s is recursive.",
46326a7405f5SSimon J. Gerraty 		    v->name.str);
46336a7405f5SSimon J. Gerraty 		FStr_Done(&val);
46346a7405f5SSimon J. Gerraty 		if (*p != '\0')
46356a7405f5SSimon J. Gerraty 			p++;
46366a7405f5SSimon J. Gerraty 		*pp = p;
46376a7405f5SSimon J. Gerraty 		return FStr_InitRefer(var_Error);
46389f45a3c8SSimon J. Gerraty 	}
4639956e45f6SSimon J. Gerraty 
464006b9b3e0SSimon J. Gerraty 	/*
4641d5e0a182SSimon J. Gerraty 	 * FIXME: This assignment creates an alias to the current value of the
464206b9b3e0SSimon J. Gerraty 	 * variable.  This means that as long as the value of the expression
4643d5e0a182SSimon J. Gerraty 	 * stays the same, the value of the variable must not change, and the
4644d5e0a182SSimon J. Gerraty 	 * variable must not be deleted.  Using the ':@' modifier, it is
4645d5e0a182SSimon J. Gerraty 	 * possible (since var.c 1.212 from 2017-02-01) to delete the variable
4646d5e0a182SSimon J. Gerraty 	 * while its value is still being used:
4647d5e0a182SSimon J. Gerraty 	 *
4648d5e0a182SSimon J. Gerraty 	 *	VAR=	value
4649548bfc56SSimon J. Gerraty 	 *	_:=	${VAR:${:U:@VAR@@}:S,^,prefix,}
4650d5e0a182SSimon J. Gerraty 	 *
4651d5e0a182SSimon J. Gerraty 	 * The same effect might be achievable using the '::=' or the ':_'
4652d5e0a182SSimon J. Gerraty 	 * modifiers.
4653954401e6SSimon J. Gerraty 	 *
465406b9b3e0SSimon J. Gerraty 	 * At the bottom of this function, the resulting value is compared to
465506b9b3e0SSimon J. Gerraty 	 * the then-current value of the variable.  This might also invoke
465606b9b3e0SSimon J. Gerraty 	 * undefined behavior.
465706b9b3e0SSimon J. Gerraty 	 */
4658b0c40a00SSimon J. Gerraty 	expr.value = FStr_InitRefer(v->val.data);
4659e2eeea75SSimon J. Gerraty 
466022619282SSimon J. Gerraty 	if (!VarEvalMode_ShouldEval(emode))
466122619282SSimon J. Gerraty 		EvalStack_Push(VSK_EXPR_PARSE, start, NULL);
466222619282SSimon J. Gerraty 	else if (expr.name[0] != '\0')
466322619282SSimon J. Gerraty 		EvalStack_Push(VSK_VARNAME, expr.name, &expr.value);
4664548bfc56SSimon J. Gerraty 	else
466522619282SSimon J. Gerraty 		EvalStack_Push(VSK_EXPR, start, &expr.value);
4666548bfc56SSimon J. Gerraty 
466706b9b3e0SSimon J. Gerraty 	/*
466806b9b3e0SSimon J. Gerraty 	 * Before applying any modifiers, expand any nested expressions from
466906b9b3e0SSimon J. Gerraty 	 * the variable value.
467006b9b3e0SSimon J. Gerraty 	 */
46719f45a3c8SSimon J. Gerraty 	if (VarEvalMode_ShouldEval(emode) &&
46729f45a3c8SSimon J. Gerraty 	    strchr(Expr_Str(&expr), '$') != NULL) {
467306b9b3e0SSimon J. Gerraty 		char *expanded;
4674b0c40a00SSimon J. Gerraty 		v->inUse = true;
46756a7405f5SSimon J. Gerraty 		expanded = Var_Subst(Expr_Str(&expr), scope, expr.emode);
4676b0c40a00SSimon J. Gerraty 		v->inUse = false;
4677956e45f6SSimon J. Gerraty 		/* TODO: handle errors */
4678b0c40a00SSimon J. Gerraty 		Expr_SetValueOwn(&expr, expanded);
4679956e45f6SSimon J. Gerraty 	}
46803955d011SMarcel Moolenaar 
4681db29cad8SSimon J. Gerraty 	if (extramodifiers != NULL) {
46822c3632d1SSimon J. Gerraty 		const char *em = extramodifiers;
4683b0c40a00SSimon J. Gerraty 		ApplyModifiers(&expr, &em, '\0', '\0');
4684db29cad8SSimon J. Gerraty 	}
4685db29cad8SSimon J. Gerraty 
4686db29cad8SSimon J. Gerraty 	if (haveModifier) {
468706b9b3e0SSimon J. Gerraty 		p++;		/* Skip initial colon. */
4688b0c40a00SSimon J. Gerraty 		ApplyModifiers(&expr, &p, startc, endc);
46893955d011SMarcel Moolenaar 	}
46902c3632d1SSimon J. Gerraty 
4691956e45f6SSimon J. Gerraty 	if (*p != '\0')		/* Skip past endc if possible. */
4692956e45f6SSimon J. Gerraty 		p++;
4693956e45f6SSimon J. Gerraty 
4694956e45f6SSimon J. Gerraty 	*pp = p;
46953955d011SMarcel Moolenaar 
4696b0c40a00SSimon J. Gerraty 	if (expr.defined == DEF_UNDEF) {
4697759b177aSSimon J. Gerraty 		Substring varname = Substring_InitStr(expr.name);
4698759b177aSSimon J. Gerraty 		FStr value = EvalUndefined(dynamic, start, p, varname, emode,
4699759b177aSSimon J. Gerraty 		    parseErrorsBefore);
4700759b177aSSimon J. Gerraty 		Expr_SetValue(&expr, value);
47013955d011SMarcel Moolenaar 	}
47029f45a3c8SSimon J. Gerraty 
47030b46a53aSSimon J. Gerraty 	EvalStack_Pop();
47040b46a53aSSimon J. Gerraty 
47059f45a3c8SSimon J. Gerraty 	if (v->shortLived) {
47069f45a3c8SSimon J. Gerraty 		if (expr.value.str == v->val.data) {
47079f45a3c8SSimon J. Gerraty 			/* move ownership */
47089f45a3c8SSimon J. Gerraty 			expr.value.freeIt = v->val.data;
47099f45a3c8SSimon J. Gerraty 			v->val.data = NULL;
47103955d011SMarcel Moolenaar 		}
47119f45a3c8SSimon J. Gerraty 		VarFreeShortLived(v);
47129f45a3c8SSimon J. Gerraty 	}
47139f45a3c8SSimon J. Gerraty 
47148c973ee2SSimon J. Gerraty 	return expr.value;
47153955d011SMarcel Moolenaar }
47163955d011SMarcel Moolenaar 
4717e2eeea75SSimon J. Gerraty static void
VarSubstDollarDollar(const char ** pp,Buffer * res,VarEvalMode emode)4718b0c40a00SSimon J. Gerraty VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalMode emode)
471906b9b3e0SSimon J. Gerraty {
4720b0c40a00SSimon J. Gerraty 	/* A dollar sign may be escaped with another dollar sign. */
4721b0c40a00SSimon J. Gerraty 	if (save_dollars && VarEvalMode_ShouldKeepDollar(emode))
472206b9b3e0SSimon J. Gerraty 		Buf_AddByte(res, '$');
472306b9b3e0SSimon J. Gerraty 	Buf_AddByte(res, '$');
472406b9b3e0SSimon J. Gerraty 	*pp += 2;
472506b9b3e0SSimon J. Gerraty }
472606b9b3e0SSimon J. Gerraty 
472706b9b3e0SSimon J. Gerraty static void
VarSubstExpr(const char ** pp,Buffer * buf,GNode * scope,VarEvalMode emode)47286a7405f5SSimon J. Gerraty VarSubstExpr(const char **pp, Buffer *buf, GNode *scope, VarEvalMode emode)
4729e2eeea75SSimon J. Gerraty {
4730e2eeea75SSimon J. Gerraty 	const char *p = *pp;
4731e2eeea75SSimon J. Gerraty 	const char *nested_p = p;
47328c973ee2SSimon J. Gerraty 	FStr val = Var_Parse(&nested_p, scope, emode);
4733e2eeea75SSimon J. Gerraty 	/* TODO: handle errors */
4734e2eeea75SSimon J. Gerraty 
473506b9b3e0SSimon J. Gerraty 	if (val.str == var_Error || val.str == varUndefined) {
47366a7405f5SSimon J. Gerraty 		if (!VarEvalMode_ShouldKeepUndef(emode)
47376a7405f5SSimon J. Gerraty 		    || val.str == var_Error) {
4738e2eeea75SSimon J. Gerraty 			p = nested_p;
4739e2eeea75SSimon J. Gerraty 		} else {
47409f45a3c8SSimon J. Gerraty 			/*
47419f45a3c8SSimon J. Gerraty 			 * Copy the initial '$' of the undefined expression,
4742e2eeea75SSimon J. Gerraty 			 * thereby deferring expansion of the expression, but
47439f45a3c8SSimon J. Gerraty 			 * expand nested expressions if already possible. See
47449f45a3c8SSimon J. Gerraty 			 * unit-tests/varparse-undef-partial.mk.
47459f45a3c8SSimon J. Gerraty 			 */
4746e2eeea75SSimon J. Gerraty 			Buf_AddByte(buf, *p);
4747e2eeea75SSimon J. Gerraty 			p++;
4748e2eeea75SSimon J. Gerraty 		}
4749e2eeea75SSimon J. Gerraty 	} else {
4750e2eeea75SSimon J. Gerraty 		p = nested_p;
475106b9b3e0SSimon J. Gerraty 		Buf_AddStr(buf, val.str);
4752e2eeea75SSimon J. Gerraty 	}
4753e2eeea75SSimon J. Gerraty 
475406b9b3e0SSimon J. Gerraty 	FStr_Done(&val);
4755e2eeea75SSimon J. Gerraty 
4756e2eeea75SSimon J. Gerraty 	*pp = p;
4757e2eeea75SSimon J. Gerraty }
4758e2eeea75SSimon J. Gerraty 
475906b9b3e0SSimon J. Gerraty /*
4760d5e0a182SSimon J. Gerraty  * Skip as many characters as possible -- either to the end of the string,
4761d5e0a182SSimon J. Gerraty  * or to the next dollar sign, which may start an expression.
476206b9b3e0SSimon J. Gerraty  */
476306b9b3e0SSimon J. Gerraty static void
VarSubstPlain(const char ** pp,Buffer * res)476406b9b3e0SSimon J. Gerraty VarSubstPlain(const char **pp, Buffer *res)
476506b9b3e0SSimon J. Gerraty {
476606b9b3e0SSimon J. Gerraty 	const char *p = *pp;
476706b9b3e0SSimon J. Gerraty 	const char *start = p;
476806b9b3e0SSimon J. Gerraty 
476906b9b3e0SSimon J. Gerraty 	for (p++; *p != '$' && *p != '\0'; p++)
477006b9b3e0SSimon J. Gerraty 		continue;
4771148ee845SSimon J. Gerraty 	Buf_AddRange(res, start, p);
477206b9b3e0SSimon J. Gerraty 	*pp = p;
477306b9b3e0SSimon J. Gerraty }
477406b9b3e0SSimon J. Gerraty 
477506b9b3e0SSimon J. Gerraty /*
4776d5e0a182SSimon J. Gerraty  * Expand all expressions like $V, ${VAR}, $(VAR:Modifiers) in the
4777e2eeea75SSimon J. Gerraty  * given string.
47782c3632d1SSimon J. Gerraty  *
47793955d011SMarcel Moolenaar  * Input:
4780548bfc56SSimon J. Gerraty  *	str		The string in which the expressions are expanded.
4781548bfc56SSimon J. Gerraty  *	scope		The scope in which to start searching for variables.
4782548bfc56SSimon J. Gerraty  *			The other scopes are searched as well.
4783b0c40a00SSimon J. Gerraty  *	emode		The mode for parsing or evaluating subexpressions.
47843955d011SMarcel Moolenaar  */
47858c973ee2SSimon J. Gerraty char *
Var_Subst(const char * str,GNode * scope,VarEvalMode emode)47868c973ee2SSimon J. Gerraty Var_Subst(const char *str, GNode *scope, VarEvalMode emode)
47873955d011SMarcel Moolenaar {
4788956e45f6SSimon J. Gerraty 	const char *p = str;
478906b9b3e0SSimon J. Gerraty 	Buffer res;
47902c3632d1SSimon J. Gerraty 
479106b9b3e0SSimon J. Gerraty 	Buf_Init(&res);
47923955d011SMarcel Moolenaar 
4793956e45f6SSimon J. Gerraty 	while (*p != '\0') {
479406b9b3e0SSimon J. Gerraty 		if (p[0] == '$' && p[1] == '$')
4795b0c40a00SSimon J. Gerraty 			VarSubstDollarDollar(&p, &res, emode);
479606b9b3e0SSimon J. Gerraty 		else if (p[0] == '$')
47976a7405f5SSimon J. Gerraty 			VarSubstExpr(&p, &res, scope, emode);
479806b9b3e0SSimon J. Gerraty 		else
479906b9b3e0SSimon J. Gerraty 			VarSubstPlain(&p, &res);
48003955d011SMarcel Moolenaar 	}
48013955d011SMarcel Moolenaar 
4802548bfc56SSimon J. Gerraty 	return Buf_DoneData(&res);
48033955d011SMarcel Moolenaar }
48043955d011SMarcel Moolenaar 
48058d5c8e21SSimon J. Gerraty char *
Var_SubstInTarget(const char * str,GNode * scope)48068d5c8e21SSimon J. Gerraty Var_SubstInTarget(const char *str, GNode *scope)
48078d5c8e21SSimon J. Gerraty {
48088d5c8e21SSimon J. Gerraty 	char *res;
480922619282SSimon J. Gerraty 	EvalStack_Push(VSK_TARGET, scope->name, NULL);
4810759b177aSSimon J. Gerraty 	EvalStack_Push(VSK_COMMAND, str, NULL);
48118d5c8e21SSimon J. Gerraty 	res = Var_Subst(str, scope, VARE_EVAL);
48128d5c8e21SSimon J. Gerraty 	EvalStack_Pop();
4813759b177aSSimon J. Gerraty 	EvalStack_Pop();
48148d5c8e21SSimon J. Gerraty 	return res;
48158d5c8e21SSimon J. Gerraty }
48168d5c8e21SSimon J. Gerraty 
48179f45a3c8SSimon J. Gerraty void
Var_ExportStackTrace(const char * target,const char * cmd)48180b46a53aSSimon J. Gerraty Var_ExportStackTrace(const char *target, const char *cmd)
48190b46a53aSSimon J. Gerraty {
48200b46a53aSSimon J. Gerraty 	char *stackTrace;
48210b46a53aSSimon J. Gerraty 
48220b46a53aSSimon J. Gerraty 	if (GetParentStackTrace() == NULL)
48230b46a53aSSimon J. Gerraty 		return;
48240b46a53aSSimon J. Gerraty 
48250b46a53aSSimon J. Gerraty 	if (target != NULL)
48260b46a53aSSimon J. Gerraty 		EvalStack_Push(VSK_TARGET, target, NULL);
48270b46a53aSSimon J. Gerraty 	if (cmd != NULL)
48280b46a53aSSimon J. Gerraty 		EvalStack_Push(VSK_COMMAND, cmd, NULL);
48290b46a53aSSimon J. Gerraty 
48300b46a53aSSimon J. Gerraty 	stackTrace = GetStackTrace(true);
48310b46a53aSSimon J. Gerraty 	(void)setenv("MAKE_STACK_TRACE", stackTrace, 1);
48320b46a53aSSimon J. Gerraty 	free(stackTrace);
48330b46a53aSSimon J. Gerraty 
48340b46a53aSSimon J. Gerraty 	if (cmd != NULL)
48350b46a53aSSimon J. Gerraty 		EvalStack_Pop();
48360b46a53aSSimon J. Gerraty 	if (target != NULL)
48370b46a53aSSimon J. Gerraty 		EvalStack_Pop();
48380b46a53aSSimon J. Gerraty }
48390b46a53aSSimon J. Gerraty 
48400b46a53aSSimon J. Gerraty void
Var_Expand(FStr * str,GNode * scope,VarEvalMode emode)48419f45a3c8SSimon J. Gerraty Var_Expand(FStr *str, GNode *scope, VarEvalMode emode)
48429f45a3c8SSimon J. Gerraty {
48439f45a3c8SSimon J. Gerraty 	char *expanded;
48449f45a3c8SSimon J. Gerraty 
48459f45a3c8SSimon J. Gerraty 	if (strchr(str->str, '$') == NULL)
48469f45a3c8SSimon J. Gerraty 		return;
48478c973ee2SSimon J. Gerraty 	expanded = Var_Subst(str->str, scope, emode);
48489f45a3c8SSimon J. Gerraty 	/* TODO: handle errors */
48499f45a3c8SSimon J. Gerraty 	FStr_Done(str);
48509f45a3c8SSimon J. Gerraty 	*str = FStr_InitOwn(expanded);
48519f45a3c8SSimon J. Gerraty }
48529f45a3c8SSimon J. Gerraty 
48532c3632d1SSimon J. Gerraty void
Var_Stats(void)48542c3632d1SSimon J. Gerraty Var_Stats(void)
48552c3632d1SSimon J. Gerraty {
4856dba7b0efSSimon J. Gerraty 	HashTable_DebugStats(&SCOPE_GLOBAL->vars, "Global variables");
48573955d011SMarcel Moolenaar }
48583955d011SMarcel Moolenaar 
485912904384SSimon J. Gerraty static int
StrAsc(const void * sa,const void * sb)486012904384SSimon J. Gerraty StrAsc(const void *sa, const void *sb)
486112904384SSimon J. Gerraty {
486212904384SSimon J. Gerraty 	return strcmp(
486312904384SSimon J. Gerraty 	    *((const char *const *)sa), *((const char *const *)sb));
486412904384SSimon J. Gerraty }
486512904384SSimon J. Gerraty 
486612904384SSimon J. Gerraty 
4867dba7b0efSSimon J. Gerraty /* Print all variables in a scope, sorted by name. */
48683955d011SMarcel Moolenaar void
Var_Dump(GNode * scope)4869dba7b0efSSimon J. Gerraty Var_Dump(GNode *scope)
48703955d011SMarcel Moolenaar {
4871956e45f6SSimon J. Gerraty 	Vector /* of const char * */ vec;
4872956e45f6SSimon J. Gerraty 	HashIter hi;
4873956e45f6SSimon J. Gerraty 	size_t i;
4874956e45f6SSimon J. Gerraty 	const char **varnames;
4875956e45f6SSimon J. Gerraty 
4876956e45f6SSimon J. Gerraty 	Vector_Init(&vec, sizeof(const char *));
4877956e45f6SSimon J. Gerraty 
4878dba7b0efSSimon J. Gerraty 	HashIter_Init(&hi, &scope->vars);
48798d5c8e21SSimon J. Gerraty 	while (HashIter_Next(&hi))
4880956e45f6SSimon J. Gerraty 		*(const char **)Vector_Push(&vec) = hi.entry->key;
4881956e45f6SSimon J. Gerraty 	varnames = vec.items;
4882956e45f6SSimon J. Gerraty 
488312904384SSimon J. Gerraty 	qsort(varnames, vec.len, sizeof varnames[0], StrAsc);
4884956e45f6SSimon J. Gerraty 
4885956e45f6SSimon J. Gerraty 	for (i = 0; i < vec.len; i++) {
4886956e45f6SSimon J. Gerraty 		const char *varname = varnames[i];
48874fde40d9SSimon J. Gerraty 		const Var *var = HashTable_FindValue(&scope->vars, varname);
48881d3f2ddcSSimon J. Gerraty 		debug_printf("%-16s = %s%s\n", varname,
48891d3f2ddcSSimon J. Gerraty 		    var->val.data, ValueDescription(var->val.data));
4890956e45f6SSimon J. Gerraty 	}
4891956e45f6SSimon J. Gerraty 
4892956e45f6SSimon J. Gerraty 	Vector_Done(&vec);
48933955d011SMarcel Moolenaar }
4894