1*6eef5f0cSAntonio Huete Jimenez /* $NetBSD: var.c,v 1.1033 2022/09/27 17:46:58 rillig Exp $ */
201e196c8SJohn Marino
301e196c8SJohn Marino /*
401e196c8SJohn Marino * Copyright (c) 1988, 1989, 1990, 1993
501e196c8SJohn Marino * The Regents of the University of California. All rights reserved.
601e196c8SJohn Marino *
701e196c8SJohn Marino * This code is derived from software contributed to Berkeley by
801e196c8SJohn Marino * Adam de Boor.
901e196c8SJohn Marino *
1001e196c8SJohn Marino * Redistribution and use in source and binary forms, with or without
1101e196c8SJohn Marino * modification, are permitted provided that the following conditions
1201e196c8SJohn Marino * are met:
1301e196c8SJohn Marino * 1. Redistributions of source code must retain the above copyright
1401e196c8SJohn Marino * notice, this list of conditions and the following disclaimer.
1501e196c8SJohn Marino * 2. Redistributions in binary form must reproduce the above copyright
1601e196c8SJohn Marino * notice, this list of conditions and the following disclaimer in the
1701e196c8SJohn Marino * documentation and/or other materials provided with the distribution.
1801e196c8SJohn Marino * 3. Neither the name of the University nor the names of its contributors
1901e196c8SJohn Marino * may be used to endorse or promote products derived from this software
2001e196c8SJohn Marino * without specific prior written permission.
2101e196c8SJohn Marino *
2201e196c8SJohn Marino * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2301e196c8SJohn Marino * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2401e196c8SJohn Marino * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2501e196c8SJohn Marino * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2601e196c8SJohn Marino * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2701e196c8SJohn Marino * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2801e196c8SJohn Marino * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2901e196c8SJohn Marino * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3001e196c8SJohn Marino * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3101e196c8SJohn Marino * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3201e196c8SJohn Marino * SUCH DAMAGE.
3301e196c8SJohn Marino */
3401e196c8SJohn Marino
3501e196c8SJohn Marino /*
3601e196c8SJohn Marino * Copyright (c) 1989 by Berkeley Softworks
3701e196c8SJohn Marino * All rights reserved.
3801e196c8SJohn Marino *
3901e196c8SJohn Marino * This code is derived from software contributed to Berkeley by
4001e196c8SJohn Marino * Adam de Boor.
4101e196c8SJohn Marino *
4201e196c8SJohn Marino * Redistribution and use in source and binary forms, with or without
4301e196c8SJohn Marino * modification, are permitted provided that the following conditions
4401e196c8SJohn Marino * are met:
4501e196c8SJohn Marino * 1. Redistributions of source code must retain the above copyright
4601e196c8SJohn Marino * notice, this list of conditions and the following disclaimer.
4701e196c8SJohn Marino * 2. Redistributions in binary form must reproduce the above copyright
4801e196c8SJohn Marino * notice, this list of conditions and the following disclaimer in the
4901e196c8SJohn Marino * documentation and/or other materials provided with the distribution.
5001e196c8SJohn Marino * 3. All advertising materials mentioning features or use of this software
5101e196c8SJohn Marino * must display the following acknowledgement:
5201e196c8SJohn Marino * This product includes software developed by the University of
5301e196c8SJohn Marino * California, Berkeley and its contributors.
5401e196c8SJohn Marino * 4. Neither the name of the University nor the names of its contributors
5501e196c8SJohn Marino * may be used to endorse or promote products derived from this software
5601e196c8SJohn Marino * without specific prior written permission.
5701e196c8SJohn Marino *
5801e196c8SJohn Marino * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5901e196c8SJohn Marino * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
6001e196c8SJohn Marino * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
6101e196c8SJohn Marino * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
6201e196c8SJohn Marino * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
6301e196c8SJohn Marino * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
6401e196c8SJohn Marino * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6501e196c8SJohn Marino * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6601e196c8SJohn Marino * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6701e196c8SJohn Marino * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6801e196c8SJohn Marino * SUCH DAMAGE.
6901e196c8SJohn Marino */
7001e196c8SJohn Marino
71a34d5fb1SAntonio Huete Jimenez /*
72a34d5fb1SAntonio Huete Jimenez * Handling of variables and the expressions formed from them.
73a34d5fb1SAntonio Huete Jimenez *
74a34d5fb1SAntonio Huete Jimenez * Variables are set using lines of the form VAR=value. Both the variable
75a34d5fb1SAntonio Huete Jimenez * name and the value can contain references to other variables, by using
76a34d5fb1SAntonio Huete Jimenez * expressions like ${VAR}, ${VAR:Modifiers}, ${${VARNAME}} or ${VAR:${MODS}}.
7701e196c8SJohn Marino *
7801e196c8SJohn Marino * Interface:
7901e196c8SJohn Marino * Var_Init Initialize this module.
8001e196c8SJohn Marino *
81a34d5fb1SAntonio Huete Jimenez * Var_End Clean up the module.
82a34d5fb1SAntonio Huete Jimenez *
83a34d5fb1SAntonio Huete Jimenez * Var_Set
84a34d5fb1SAntonio Huete Jimenez * Var_SetExpand
85a34d5fb1SAntonio Huete Jimenez * Set the value of the variable, creating it if
86a34d5fb1SAntonio Huete Jimenez * necessary.
87a34d5fb1SAntonio Huete Jimenez *
88a34d5fb1SAntonio Huete Jimenez * Var_Append
89a34d5fb1SAntonio Huete Jimenez * Var_AppendExpand
90a34d5fb1SAntonio Huete Jimenez * Append more characters to the variable, creating it if
91a34d5fb1SAntonio Huete Jimenez * necessary. A space is placed between the old value and
92a34d5fb1SAntonio Huete Jimenez * the new one.
93a34d5fb1SAntonio Huete Jimenez *
94a34d5fb1SAntonio Huete Jimenez * Var_Exists
95a34d5fb1SAntonio Huete Jimenez * Var_ExistsExpand
96a34d5fb1SAntonio Huete Jimenez * See if a variable exists.
97a34d5fb1SAntonio Huete Jimenez *
98a34d5fb1SAntonio Huete Jimenez * Var_Value Return the unexpanded value of a variable, or NULL if
99a34d5fb1SAntonio Huete Jimenez * the variable is undefined.
100a34d5fb1SAntonio Huete Jimenez *
101a34d5fb1SAntonio Huete Jimenez * Var_Subst Substitute all variable expressions in a string.
102a34d5fb1SAntonio Huete Jimenez *
103a34d5fb1SAntonio Huete Jimenez * Var_Parse Parse a variable expression such as ${VAR:Mpattern}.
104a34d5fb1SAntonio Huete Jimenez *
105a34d5fb1SAntonio Huete Jimenez * Var_Delete
106a34d5fb1SAntonio Huete Jimenez * Delete a variable.
107a34d5fb1SAntonio Huete Jimenez *
108a34d5fb1SAntonio Huete Jimenez * Var_ReexportVars
109a34d5fb1SAntonio Huete Jimenez * Export some or even all variables to the environment
110a34d5fb1SAntonio Huete Jimenez * of this process and its child processes.
111a34d5fb1SAntonio Huete Jimenez *
112a34d5fb1SAntonio Huete Jimenez * Var_Export Export the variable to the environment of this process
113a34d5fb1SAntonio Huete Jimenez * and its child processes.
114a34d5fb1SAntonio Huete Jimenez *
115a34d5fb1SAntonio Huete Jimenez * Var_UnExport Don't export the variable anymore.
116a34d5fb1SAntonio Huete Jimenez *
11701e196c8SJohn Marino * Debugging:
118a34d5fb1SAntonio Huete Jimenez * Var_Stats Print out hashing statistics if in -dh mode.
119a34d5fb1SAntonio Huete Jimenez *
120a34d5fb1SAntonio Huete Jimenez * Var_Dump Print out all variables defined in the given scope.
12101e196c8SJohn Marino *
122ec533708SSascha Wildner * XXX: There's a lot of almost duplicate code in these functions that only
123ec533708SSascha Wildner * differs in subtle details that are not mentioned in the manual page.
12401e196c8SJohn Marino */
12501e196c8SJohn Marino
12601e196c8SJohn Marino #include <sys/stat.h>
12701e196c8SJohn Marino #include <sys/types.h>
128ca58f742SDaniel Fojt #ifndef NO_REGEX
12901e196c8SJohn Marino #include <regex.h>
13001e196c8SJohn Marino #endif
13101e196c8SJohn Marino
13201e196c8SJohn Marino #include "make.h"
133ca58f742SDaniel Fojt
134a34d5fb1SAntonio Huete Jimenez #include <errno.h>
135a34d5fb1SAntonio Huete Jimenez #ifdef HAVE_INTTYPES_H
136a34d5fb1SAntonio Huete Jimenez #include <inttypes.h>
137a34d5fb1SAntonio Huete Jimenez #elif defined(HAVE_STDINT_H)
138ca58f742SDaniel Fojt #include <stdint.h>
139ca58f742SDaniel Fojt #endif
140a34d5fb1SAntonio Huete Jimenez #ifdef HAVE_LIMITS_H
141a34d5fb1SAntonio Huete Jimenez #include <limits.h>
142a34d5fb1SAntonio Huete Jimenez #endif
143a34d5fb1SAntonio Huete Jimenez #include <time.h>
144ca58f742SDaniel Fojt
14501e196c8SJohn Marino #include "dir.h"
14601e196c8SJohn Marino #include "job.h"
147f445c897SJohn Marino #include "metachar.h"
14801e196c8SJohn Marino
149a34d5fb1SAntonio Huete Jimenez /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
150*6eef5f0cSAntonio Huete Jimenez MAKE_RCSID("$NetBSD: var.c,v 1.1033 2022/09/27 17:46:58 rillig Exp $");
151a34d5fb1SAntonio Huete Jimenez
152a34d5fb1SAntonio Huete Jimenez /*
153a34d5fb1SAntonio Huete Jimenez * Variables are defined using one of the VAR=value assignments. Their
154a34d5fb1SAntonio Huete Jimenez * value can be queried by expressions such as $V, ${VAR}, or with modifiers
155a34d5fb1SAntonio Huete Jimenez * such as ${VAR:S,from,to,g:Q}.
156a34d5fb1SAntonio Huete Jimenez *
157a34d5fb1SAntonio Huete Jimenez * There are 3 kinds of variables: scope variables, environment variables,
158a34d5fb1SAntonio Huete Jimenez * undefined variables.
159a34d5fb1SAntonio Huete Jimenez *
160a34d5fb1SAntonio Huete Jimenez * Scope variables are stored in a GNode.scope. The only way to undefine
161a34d5fb1SAntonio Huete Jimenez * a scope variable is using the .undef directive. In particular, it must
162a34d5fb1SAntonio Huete Jimenez * not be possible to undefine a variable during the evaluation of an
163*6eef5f0cSAntonio Huete Jimenez * expression, or Var.name might point nowhere. (There is another,
164*6eef5f0cSAntonio Huete Jimenez * unintended way to undefine a scope variable, see varmod-loop-delete.mk.)
165a34d5fb1SAntonio Huete Jimenez *
166*6eef5f0cSAntonio Huete Jimenez * Environment variables are short-lived. They are returned by VarFind, and
167*6eef5f0cSAntonio Huete Jimenez * after using them, they must be freed using VarFreeShortLived.
168a34d5fb1SAntonio Huete Jimenez *
169a34d5fb1SAntonio Huete Jimenez * Undefined variables occur during evaluation of variable expressions such
170a34d5fb1SAntonio Huete Jimenez * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers.
171a34d5fb1SAntonio Huete Jimenez */
172a34d5fb1SAntonio Huete Jimenez typedef struct Var {
173a34d5fb1SAntonio Huete Jimenez /*
174a34d5fb1SAntonio Huete Jimenez * The name of the variable, once set, doesn't change anymore.
175a34d5fb1SAntonio Huete Jimenez * For scope variables, it aliases the corresponding HashEntry name.
176a34d5fb1SAntonio Huete Jimenez * For environment and undefined variables, it is allocated.
177a34d5fb1SAntonio Huete Jimenez */
178a34d5fb1SAntonio Huete Jimenez FStr name;
179a34d5fb1SAntonio Huete Jimenez
180a34d5fb1SAntonio Huete Jimenez /* The unexpanded value of the variable. */
181a34d5fb1SAntonio Huete Jimenez Buffer val;
182*6eef5f0cSAntonio Huete Jimenez
183*6eef5f0cSAntonio Huete Jimenez /* The variable came from the command line. */
184*6eef5f0cSAntonio Huete Jimenez bool fromCmd:1;
185*6eef5f0cSAntonio Huete Jimenez
186*6eef5f0cSAntonio Huete Jimenez /*
187*6eef5f0cSAntonio Huete Jimenez * The variable is short-lived.
188*6eef5f0cSAntonio Huete Jimenez * These variables are not registered in any GNode, therefore they
189*6eef5f0cSAntonio Huete Jimenez * must be freed after use.
190*6eef5f0cSAntonio Huete Jimenez */
191*6eef5f0cSAntonio Huete Jimenez bool shortLived:1;
192*6eef5f0cSAntonio Huete Jimenez
193*6eef5f0cSAntonio Huete Jimenez /*
194*6eef5f0cSAntonio Huete Jimenez * The variable comes from the environment.
195*6eef5f0cSAntonio Huete Jimenez * Appending to its value moves the variable to the global scope.
196*6eef5f0cSAntonio Huete Jimenez */
197*6eef5f0cSAntonio Huete Jimenez bool fromEnvironment:1;
198*6eef5f0cSAntonio Huete Jimenez
199*6eef5f0cSAntonio Huete Jimenez /*
200*6eef5f0cSAntonio Huete Jimenez * The variable value cannot be changed anymore, and the variable
201*6eef5f0cSAntonio Huete Jimenez * cannot be deleted. Any attempts to do so are silently ignored,
202*6eef5f0cSAntonio Huete Jimenez * they are logged with -dv though.
203*6eef5f0cSAntonio Huete Jimenez *
204*6eef5f0cSAntonio Huete Jimenez * See VAR_SET_READONLY.
205*6eef5f0cSAntonio Huete Jimenez */
206*6eef5f0cSAntonio Huete Jimenez bool readOnly:1;
207*6eef5f0cSAntonio Huete Jimenez
208*6eef5f0cSAntonio Huete Jimenez /*
209*6eef5f0cSAntonio Huete Jimenez * The variable's value is currently being used by Var_Parse or
210*6eef5f0cSAntonio Huete Jimenez * Var_Subst. This marker is used to avoid endless recursion.
211*6eef5f0cSAntonio Huete Jimenez */
212*6eef5f0cSAntonio Huete Jimenez bool inUse:1;
213*6eef5f0cSAntonio Huete Jimenez
214*6eef5f0cSAntonio Huete Jimenez /*
215*6eef5f0cSAntonio Huete Jimenez * The variable is exported to the environment, to be used by child
216*6eef5f0cSAntonio Huete Jimenez * processes.
217*6eef5f0cSAntonio Huete Jimenez */
218*6eef5f0cSAntonio Huete Jimenez bool exported:1;
219*6eef5f0cSAntonio Huete Jimenez
220*6eef5f0cSAntonio Huete Jimenez /*
221*6eef5f0cSAntonio Huete Jimenez * At the point where this variable was exported, it contained an
222*6eef5f0cSAntonio Huete Jimenez * unresolved reference to another variable. Before any child
223*6eef5f0cSAntonio Huete Jimenez * process is started, it needs to be exported again, in the hope
224*6eef5f0cSAntonio Huete Jimenez * that the referenced variable can then be resolved.
225*6eef5f0cSAntonio Huete Jimenez */
226*6eef5f0cSAntonio Huete Jimenez bool reexport:1;
227a34d5fb1SAntonio Huete Jimenez } Var;
228a34d5fb1SAntonio Huete Jimenez
229a34d5fb1SAntonio Huete Jimenez /*
230ec533708SSascha Wildner * Exporting variables is expensive and may leak memory, so skip it if we
231ec533708SSascha Wildner * can.
232ec533708SSascha Wildner *
233ec533708SSascha Wildner * To avoid this, it might be worth encapsulating the environment variables
234ec533708SSascha Wildner * in a separate data structure called EnvVars.
235a34d5fb1SAntonio Huete Jimenez */
236a34d5fb1SAntonio Huete Jimenez typedef enum VarExportedMode {
237a34d5fb1SAntonio Huete Jimenez VAR_EXPORTED_NONE,
238a34d5fb1SAntonio Huete Jimenez VAR_EXPORTED_SOME,
239a34d5fb1SAntonio Huete Jimenez VAR_EXPORTED_ALL
240a34d5fb1SAntonio Huete Jimenez } VarExportedMode;
241a34d5fb1SAntonio Huete Jimenez
242a34d5fb1SAntonio Huete Jimenez typedef enum UnexportWhat {
243ec533708SSascha Wildner /* Unexport the variables given by name. */
244a34d5fb1SAntonio Huete Jimenez UNEXPORT_NAMED,
245ec533708SSascha Wildner /*
246ec533708SSascha Wildner * Unexport all globals previously exported, but keep the environment
247ec533708SSascha Wildner * inherited from the parent.
248ec533708SSascha Wildner */
249a34d5fb1SAntonio Huete Jimenez UNEXPORT_ALL,
250ec533708SSascha Wildner /*
251ec533708SSascha Wildner * Unexport all globals previously exported and clear the environment
252ec533708SSascha Wildner * inherited from the parent.
253ec533708SSascha Wildner */
254a34d5fb1SAntonio Huete Jimenez UNEXPORT_ENV
255a34d5fb1SAntonio Huete Jimenez } UnexportWhat;
256a34d5fb1SAntonio Huete Jimenez
257a34d5fb1SAntonio Huete Jimenez /* Flags for pattern matching in the :S and :C modifiers */
258*6eef5f0cSAntonio Huete Jimenez typedef struct PatternFlags {
259*6eef5f0cSAntonio Huete Jimenez bool subGlobal:1; /* 'g': replace as often as possible */
260*6eef5f0cSAntonio Huete Jimenez bool subOnce:1; /* '1': replace only once */
261*6eef5f0cSAntonio Huete Jimenez bool anchorStart:1; /* '^': match only at start of word */
262*6eef5f0cSAntonio Huete Jimenez bool anchorEnd:1; /* '$': match only at end of word */
263*6eef5f0cSAntonio Huete Jimenez } PatternFlags;
264a34d5fb1SAntonio Huete Jimenez
265ec533708SSascha Wildner /* SepBuf builds a string from words interleaved with separators. */
266a34d5fb1SAntonio Huete Jimenez typedef struct SepBuf {
267a34d5fb1SAntonio Huete Jimenez Buffer buf;
268*6eef5f0cSAntonio Huete Jimenez bool needSep;
269a34d5fb1SAntonio Huete Jimenez /* Usually ' ', but see the ':ts' modifier. */
270a34d5fb1SAntonio Huete Jimenez char sep;
271a34d5fb1SAntonio Huete Jimenez } SepBuf;
272a34d5fb1SAntonio Huete Jimenez
273a34d5fb1SAntonio Huete Jimenez
27401e196c8SJohn Marino /*
27501e196c8SJohn Marino * This lets us tell if we have replaced the original environ
27601e196c8SJohn Marino * (which we cannot free).
27701e196c8SJohn Marino */
27801e196c8SJohn Marino char **savedEnv = NULL;
27901e196c8SJohn Marino
28001e196c8SJohn Marino /*
281a34d5fb1SAntonio Huete Jimenez * Special return value for Var_Parse, indicating a parse error. It may be
282a34d5fb1SAntonio Huete Jimenez * caused by an undefined variable, a syntax error in a modifier or
283a34d5fb1SAntonio Huete Jimenez * something entirely different.
28401e196c8SJohn Marino */
28501e196c8SJohn Marino char var_Error[] = "";
28601e196c8SJohn Marino
28701e196c8SJohn Marino /*
288a34d5fb1SAntonio Huete Jimenez * Special return value for Var_Parse, indicating an undefined variable in
289a34d5fb1SAntonio Huete Jimenez * a case where VARE_UNDEFERR is not set. This undefined variable is
290a34d5fb1SAntonio Huete Jimenez * typically a dynamic variable such as ${.TARGET}, whose expansion needs to
291a34d5fb1SAntonio Huete Jimenez * be deferred until it is defined in an actual target.
292ec533708SSascha Wildner *
293*6eef5f0cSAntonio Huete Jimenez * See VARE_EVAL_KEEP_UNDEF.
29401e196c8SJohn Marino */
295a34d5fb1SAntonio Huete Jimenez static char varUndefined[] = "";
29601e196c8SJohn Marino
29701e196c8SJohn Marino /*
298a34d5fb1SAntonio Huete Jimenez * Traditionally this make consumed $$ during := like any other expansion.
299a34d5fb1SAntonio Huete Jimenez * Other make's do not, and this make follows straight since 2016-01-09.
300a34d5fb1SAntonio Huete Jimenez *
301*6eef5f0cSAntonio Huete Jimenez * This knob allows controlling the behavior:
302*6eef5f0cSAntonio Huete Jimenez * false to consume $$ during := assignment.
303*6eef5f0cSAntonio Huete Jimenez * true to preserve $$ during := assignment.
304f445c897SJohn Marino */
305a34d5fb1SAntonio Huete Jimenez #define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS"
306*6eef5f0cSAntonio Huete Jimenez static bool save_dollars = false;
307f445c897SJohn Marino
308f445c897SJohn Marino /*
309a34d5fb1SAntonio Huete Jimenez * A scope collects variable names and their values.
31001e196c8SJohn Marino *
311a34d5fb1SAntonio Huete Jimenez * The main scope is SCOPE_GLOBAL, which contains the variables that are set
312a34d5fb1SAntonio Huete Jimenez * in the makefiles. SCOPE_INTERNAL acts as a fallback for SCOPE_GLOBAL and
313a34d5fb1SAntonio Huete Jimenez * contains some internal make variables. These internal variables can thus
314a34d5fb1SAntonio Huete Jimenez * be overridden, they can also be restored by undefining the overriding
315a34d5fb1SAntonio Huete Jimenez * variable.
31601e196c8SJohn Marino *
317a34d5fb1SAntonio Huete Jimenez * SCOPE_CMDLINE contains variables from the command line arguments. These
318a34d5fb1SAntonio Huete Jimenez * override variables from SCOPE_GLOBAL.
31901e196c8SJohn Marino *
320a34d5fb1SAntonio Huete Jimenez * There is no scope for environment variables, these are generated on-the-fly
321a34d5fb1SAntonio Huete Jimenez * whenever they are referenced. If there were such a scope, each change to
322a34d5fb1SAntonio Huete Jimenez * environment variables would have to be reflected in that scope, which may
323a34d5fb1SAntonio Huete Jimenez * be simpler or more complex than the current implementation.
324a34d5fb1SAntonio Huete Jimenez *
325a34d5fb1SAntonio Huete Jimenez * Each target has its own scope, containing the 7 target-local variables
326*6eef5f0cSAntonio Huete Jimenez * .TARGET, .ALLSRC, etc. Variables set on dependency lines also go in
327*6eef5f0cSAntonio Huete Jimenez * this scope.
32801e196c8SJohn Marino */
329a34d5fb1SAntonio Huete Jimenez
330a34d5fb1SAntonio Huete Jimenez GNode *SCOPE_CMDLINE;
331a34d5fb1SAntonio Huete Jimenez GNode *SCOPE_GLOBAL;
332a34d5fb1SAntonio Huete Jimenez GNode *SCOPE_INTERNAL;
333a34d5fb1SAntonio Huete Jimenez
334a34d5fb1SAntonio Huete Jimenez static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
335a34d5fb1SAntonio Huete Jimenez
336*6eef5f0cSAntonio Huete Jimenez static const char VarEvalMode_Name[][32] = {
337*6eef5f0cSAntonio Huete Jimenez "parse-only",
338*6eef5f0cSAntonio Huete Jimenez "eval",
339*6eef5f0cSAntonio Huete Jimenez "eval-defined",
340*6eef5f0cSAntonio Huete Jimenez "eval-keep-dollar",
341*6eef5f0cSAntonio Huete Jimenez "eval-keep-undefined",
342*6eef5f0cSAntonio Huete Jimenez "eval-keep-dollar-and-undefined",
343*6eef5f0cSAntonio Huete Jimenez };
344*6eef5f0cSAntonio Huete Jimenez
345a34d5fb1SAntonio Huete Jimenez
34601e196c8SJohn Marino static Var *
VarNew(FStr name,const char * value,bool shortLived,bool fromEnvironment,bool readOnly)347*6eef5f0cSAntonio Huete Jimenez VarNew(FStr name, const char *value,
348*6eef5f0cSAntonio Huete Jimenez bool shortLived, bool fromEnvironment, bool readOnly)
34901e196c8SJohn Marino {
350a34d5fb1SAntonio Huete Jimenez size_t value_len = strlen(value);
351a34d5fb1SAntonio Huete Jimenez Var *var = bmake_malloc(sizeof *var);
352a34d5fb1SAntonio Huete Jimenez var->name = name;
353a34d5fb1SAntonio Huete Jimenez Buf_InitSize(&var->val, value_len + 1);
354a34d5fb1SAntonio Huete Jimenez Buf_AddBytes(&var->val, value, value_len);
355*6eef5f0cSAntonio Huete Jimenez var->fromCmd = false;
356*6eef5f0cSAntonio Huete Jimenez var->shortLived = shortLived;
357*6eef5f0cSAntonio Huete Jimenez var->fromEnvironment = fromEnvironment;
358*6eef5f0cSAntonio Huete Jimenez var->readOnly = readOnly;
359*6eef5f0cSAntonio Huete Jimenez var->inUse = false;
360*6eef5f0cSAntonio Huete Jimenez var->exported = false;
361*6eef5f0cSAntonio Huete Jimenez var->reexport = false;
362a34d5fb1SAntonio Huete Jimenez return var;
363a34d5fb1SAntonio Huete Jimenez }
36401e196c8SJohn Marino
365*6eef5f0cSAntonio Huete Jimenez static Substring
CanonicalVarname(Substring name)366*6eef5f0cSAntonio Huete Jimenez CanonicalVarname(Substring name)
367a34d5fb1SAntonio Huete Jimenez {
368*6eef5f0cSAntonio Huete Jimenez
369*6eef5f0cSAntonio Huete Jimenez if (!(Substring_Length(name) > 0 && name.start[0] == '.'))
370*6eef5f0cSAntonio Huete Jimenez return name;
371*6eef5f0cSAntonio Huete Jimenez
372*6eef5f0cSAntonio Huete Jimenez if (Substring_Equals(name, ".ALLSRC"))
373*6eef5f0cSAntonio Huete Jimenez return Substring_InitStr(ALLSRC);
374*6eef5f0cSAntonio Huete Jimenez if (Substring_Equals(name, ".ARCHIVE"))
375*6eef5f0cSAntonio Huete Jimenez return Substring_InitStr(ARCHIVE);
376*6eef5f0cSAntonio Huete Jimenez if (Substring_Equals(name, ".IMPSRC"))
377*6eef5f0cSAntonio Huete Jimenez return Substring_InitStr(IMPSRC);
378*6eef5f0cSAntonio Huete Jimenez if (Substring_Equals(name, ".MEMBER"))
379*6eef5f0cSAntonio Huete Jimenez return Substring_InitStr(MEMBER);
380*6eef5f0cSAntonio Huete Jimenez if (Substring_Equals(name, ".OODATE"))
381*6eef5f0cSAntonio Huete Jimenez return Substring_InitStr(OODATE);
382*6eef5f0cSAntonio Huete Jimenez if (Substring_Equals(name, ".PREFIX"))
383*6eef5f0cSAntonio Huete Jimenez return Substring_InitStr(PREFIX);
384*6eef5f0cSAntonio Huete Jimenez if (Substring_Equals(name, ".TARGET"))
385*6eef5f0cSAntonio Huete Jimenez return Substring_InitStr(TARGET);
386*6eef5f0cSAntonio Huete Jimenez
387*6eef5f0cSAntonio Huete Jimenez if (Substring_Equals(name, ".SHELL") && shellPath == NULL)
388a34d5fb1SAntonio Huete Jimenez Shell_Init();
389ca58f742SDaniel Fojt
390a34d5fb1SAntonio Huete Jimenez /* GNU make has an additional alias $^ == ${.ALLSRC}. */
391a34d5fb1SAntonio Huete Jimenez
392a34d5fb1SAntonio Huete Jimenez return name;
393a34d5fb1SAntonio Huete Jimenez }
394a34d5fb1SAntonio Huete Jimenez
395a34d5fb1SAntonio Huete Jimenez static Var *
GNode_FindVar(GNode * scope,Substring varname,unsigned int hash)396*6eef5f0cSAntonio Huete Jimenez GNode_FindVar(GNode *scope, Substring varname, unsigned int hash)
397a34d5fb1SAntonio Huete Jimenez {
398*6eef5f0cSAntonio Huete Jimenez return HashTable_FindValueBySubstringHash(&scope->vars, varname, hash);
399a34d5fb1SAntonio Huete Jimenez }
40001e196c8SJohn Marino
40101e196c8SJohn Marino /*
402a34d5fb1SAntonio Huete Jimenez * Find the variable in the scope, and maybe in other scopes as well.
403a34d5fb1SAntonio Huete Jimenez *
404a34d5fb1SAntonio Huete Jimenez * Input:
405a34d5fb1SAntonio Huete Jimenez * name name to find, is not expanded any further
406a34d5fb1SAntonio Huete Jimenez * scope scope in which to look first
407*6eef5f0cSAntonio Huete Jimenez * elsewhere true to look in other scopes as well
408a34d5fb1SAntonio Huete Jimenez *
409a34d5fb1SAntonio Huete Jimenez * Results:
410a34d5fb1SAntonio Huete Jimenez * The found variable, or NULL if the variable does not exist.
411*6eef5f0cSAntonio Huete Jimenez * If the variable is short-lived (such as environment variables), it
412*6eef5f0cSAntonio Huete Jimenez * must be freed using VarFreeShortLived after use.
41301e196c8SJohn Marino */
414a34d5fb1SAntonio Huete Jimenez static Var *
VarFindSubstring(Substring name,GNode * scope,bool elsewhere)415*6eef5f0cSAntonio Huete Jimenez VarFindSubstring(Substring name, GNode *scope, bool elsewhere)
41601e196c8SJohn Marino {
417a34d5fb1SAntonio Huete Jimenez Var *var;
418a34d5fb1SAntonio Huete Jimenez unsigned int nameHash;
419a34d5fb1SAntonio Huete Jimenez
420ec533708SSascha Wildner /* Replace '.TARGET' with '@', likewise for other local variables. */
421a34d5fb1SAntonio Huete Jimenez name = CanonicalVarname(name);
422*6eef5f0cSAntonio Huete Jimenez nameHash = Hash_Substring(name);
423a34d5fb1SAntonio Huete Jimenez
424a34d5fb1SAntonio Huete Jimenez var = GNode_FindVar(scope, name, nameHash);
425a34d5fb1SAntonio Huete Jimenez if (!elsewhere)
426a34d5fb1SAntonio Huete Jimenez return var;
427a34d5fb1SAntonio Huete Jimenez
428a34d5fb1SAntonio Huete Jimenez if (var == NULL && scope != SCOPE_CMDLINE)
429a34d5fb1SAntonio Huete Jimenez var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash);
430a34d5fb1SAntonio Huete Jimenez
431a34d5fb1SAntonio Huete Jimenez if (!opts.checkEnvFirst && var == NULL && scope != SCOPE_GLOBAL) {
432a34d5fb1SAntonio Huete Jimenez var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash);
433a34d5fb1SAntonio Huete Jimenez if (var == NULL && scope != SCOPE_INTERNAL) {
434a34d5fb1SAntonio Huete Jimenez /* SCOPE_INTERNAL is subordinate to SCOPE_GLOBAL */
435a34d5fb1SAntonio Huete Jimenez var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash);
4365f1e34d9SAlexandre Perrin }
43701e196c8SJohn Marino }
438a34d5fb1SAntonio Huete Jimenez
439a34d5fb1SAntonio Huete Jimenez if (var == NULL) {
440*6eef5f0cSAntonio Huete Jimenez FStr envName;
441*6eef5f0cSAntonio Huete Jimenez const char *envValue;
44201e196c8SJohn Marino
443*6eef5f0cSAntonio Huete Jimenez envName = Substring_Str(name);
444*6eef5f0cSAntonio Huete Jimenez envValue = getenv(envName.str);
445*6eef5f0cSAntonio Huete Jimenez if (envValue != NULL)
446*6eef5f0cSAntonio Huete Jimenez return VarNew(envName, envValue, true, true, false);
447*6eef5f0cSAntonio Huete Jimenez FStr_Done(&envName);
448a34d5fb1SAntonio Huete Jimenez
449a34d5fb1SAntonio Huete Jimenez if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) {
450a34d5fb1SAntonio Huete Jimenez var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash);
451a34d5fb1SAntonio Huete Jimenez if (var == NULL && scope != SCOPE_INTERNAL)
452a34d5fb1SAntonio Huete Jimenez var = GNode_FindVar(SCOPE_INTERNAL, name,
453a34d5fb1SAntonio Huete Jimenez nameHash);
454a34d5fb1SAntonio Huete Jimenez return var;
45501e196c8SJohn Marino }
456a34d5fb1SAntonio Huete Jimenez
45701e196c8SJohn Marino return NULL;
45801e196c8SJohn Marino }
459a34d5fb1SAntonio Huete Jimenez
460a34d5fb1SAntonio Huete Jimenez return var;
46101e196c8SJohn Marino }
46201e196c8SJohn Marino
463*6eef5f0cSAntonio Huete Jimenez static Var *
VarFind(const char * name,GNode * scope,bool elsewhere)464*6eef5f0cSAntonio Huete Jimenez VarFind(const char *name, GNode *scope, bool elsewhere)
46501e196c8SJohn Marino {
466*6eef5f0cSAntonio Huete Jimenez return VarFindSubstring(Substring_InitStr(name), scope, elsewhere);
467*6eef5f0cSAntonio Huete Jimenez }
468*6eef5f0cSAntonio Huete Jimenez
469*6eef5f0cSAntonio Huete Jimenez /* If the variable is short-lived, free it, including its value. */
470*6eef5f0cSAntonio Huete Jimenez static void
VarFreeShortLived(Var * v)471*6eef5f0cSAntonio Huete Jimenez VarFreeShortLived(Var *v)
472*6eef5f0cSAntonio Huete Jimenez {
473*6eef5f0cSAntonio Huete Jimenez if (!v->shortLived)
474ec533708SSascha Wildner return;
475a34d5fb1SAntonio Huete Jimenez
476a34d5fb1SAntonio Huete Jimenez FStr_Done(&v->name);
477a34d5fb1SAntonio Huete Jimenez Buf_Done(&v->val);
47801e196c8SJohn Marino free(v);
47901e196c8SJohn Marino }
48001e196c8SJohn Marino
481*6eef5f0cSAntonio Huete Jimenez static const char *
ValueDescription(const char * value)482*6eef5f0cSAntonio Huete Jimenez ValueDescription(const char *value)
483*6eef5f0cSAntonio Huete Jimenez {
484*6eef5f0cSAntonio Huete Jimenez if (value[0] == '\0')
485*6eef5f0cSAntonio Huete Jimenez return "# (empty)";
486*6eef5f0cSAntonio Huete Jimenez if (ch_isspace(value[strlen(value) - 1]))
487*6eef5f0cSAntonio Huete Jimenez return "# (ends with space)";
488*6eef5f0cSAntonio Huete Jimenez return "";
489*6eef5f0cSAntonio Huete Jimenez }
490*6eef5f0cSAntonio Huete Jimenez
491ec533708SSascha Wildner /* Add a new variable of the given name and value to the given scope. */
492ec533708SSascha Wildner static Var *
VarAdd(const char * name,const char * value,GNode * scope,VarSetFlags flags)493ec533708SSascha Wildner VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags)
49401e196c8SJohn Marino {
495a34d5fb1SAntonio Huete Jimenez HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL);
496ec533708SSascha Wildner Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value,
497*6eef5f0cSAntonio Huete Jimenez false, false, (flags & VAR_SET_READONLY) != 0);
498a34d5fb1SAntonio Huete Jimenez HashEntry_Set(he, v);
499*6eef5f0cSAntonio Huete Jimenez DEBUG4(VAR, "%s: %s = %s%s\n",
500*6eef5f0cSAntonio Huete Jimenez scope->name, name, value, ValueDescription(value));
501ec533708SSascha Wildner return v;
50201e196c8SJohn Marino }
50301e196c8SJohn Marino
504a34d5fb1SAntonio Huete Jimenez /*
505a34d5fb1SAntonio Huete Jimenez * Remove a variable from a scope, freeing all related memory as well.
506a34d5fb1SAntonio Huete Jimenez * The variable name is kept as-is, it is not expanded.
50701e196c8SJohn Marino */
50801e196c8SJohn Marino void
Var_Delete(GNode * scope,const char * varname)509a34d5fb1SAntonio Huete Jimenez Var_Delete(GNode *scope, const char *varname)
51001e196c8SJohn Marino {
511a34d5fb1SAntonio Huete Jimenez HashEntry *he = HashTable_FindEntry(&scope->vars, varname);
51201e196c8SJohn Marino Var *v;
51301e196c8SJohn Marino
514a34d5fb1SAntonio Huete Jimenez if (he == NULL) {
515*6eef5f0cSAntonio Huete Jimenez DEBUG2(VAR, "%s: delete %s (not found)\n",
516*6eef5f0cSAntonio Huete Jimenez scope->name, varname);
517a34d5fb1SAntonio Huete Jimenez return;
51801e196c8SJohn Marino }
519a34d5fb1SAntonio Huete Jimenez
520a34d5fb1SAntonio Huete Jimenez DEBUG2(VAR, "%s: delete %s\n", scope->name, varname);
521ec533708SSascha Wildner v = he->value;
522*6eef5f0cSAntonio Huete Jimenez if (v->inUse) {
523*6eef5f0cSAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
524*6eef5f0cSAntonio Huete Jimenez "Cannot delete variable \"%s\" while it is used",
525*6eef5f0cSAntonio Huete Jimenez v->name.str);
526*6eef5f0cSAntonio Huete Jimenez return;
527*6eef5f0cSAntonio Huete Jimenez }
528*6eef5f0cSAntonio Huete Jimenez
529*6eef5f0cSAntonio Huete Jimenez if (v->exported)
530a34d5fb1SAntonio Huete Jimenez unsetenv(v->name.str);
531a34d5fb1SAntonio Huete Jimenez if (strcmp(v->name.str, MAKE_EXPORTED) == 0)
53201e196c8SJohn Marino var_exportedVars = VAR_EXPORTED_NONE;
533*6eef5f0cSAntonio Huete Jimenez
534a34d5fb1SAntonio Huete Jimenez assert(v->name.freeIt == NULL);
535a34d5fb1SAntonio Huete Jimenez HashTable_DeleteEntry(&scope->vars, he);
536a34d5fb1SAntonio Huete Jimenez Buf_Done(&v->val);
53701e196c8SJohn Marino free(v);
53801e196c8SJohn Marino }
539a34d5fb1SAntonio Huete Jimenez
540a34d5fb1SAntonio Huete Jimenez /*
541a34d5fb1SAntonio Huete Jimenez * Undefine one or more variables from the global scope.
542a34d5fb1SAntonio Huete Jimenez * The argument is expanded exactly once and then split into words.
54301e196c8SJohn Marino */
544a34d5fb1SAntonio Huete Jimenez void
Var_Undef(const char * arg)545a34d5fb1SAntonio Huete Jimenez Var_Undef(const char *arg)
54601e196c8SJohn Marino {
547a34d5fb1SAntonio Huete Jimenez VarParseResult vpr;
548a34d5fb1SAntonio Huete Jimenez char *expanded;
549a34d5fb1SAntonio Huete Jimenez Words varnames;
550a34d5fb1SAntonio Huete Jimenez size_t i;
55101e196c8SJohn Marino
552a34d5fb1SAntonio Huete Jimenez if (arg[0] == '\0') {
553a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
554a34d5fb1SAntonio Huete Jimenez "The .undef directive requires an argument");
555a34d5fb1SAntonio Huete Jimenez return;
556a34d5fb1SAntonio Huete Jimenez }
557a34d5fb1SAntonio Huete Jimenez
558a34d5fb1SAntonio Huete Jimenez vpr = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES, &expanded);
559a34d5fb1SAntonio Huete Jimenez if (vpr != VPR_OK) {
560a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
561a34d5fb1SAntonio Huete Jimenez "Error in variable names to be undefined");
562a34d5fb1SAntonio Huete Jimenez return;
563a34d5fb1SAntonio Huete Jimenez }
564a34d5fb1SAntonio Huete Jimenez
565*6eef5f0cSAntonio Huete Jimenez varnames = Str_Words(expanded, false);
566a34d5fb1SAntonio Huete Jimenez if (varnames.len == 1 && varnames.words[0][0] == '\0')
567a34d5fb1SAntonio Huete Jimenez varnames.len = 0;
568a34d5fb1SAntonio Huete Jimenez
569a34d5fb1SAntonio Huete Jimenez for (i = 0; i < varnames.len; i++) {
570a34d5fb1SAntonio Huete Jimenez const char *varname = varnames.words[i];
571a34d5fb1SAntonio Huete Jimenez Global_Delete(varname);
572a34d5fb1SAntonio Huete Jimenez }
573a34d5fb1SAntonio Huete Jimenez
574a34d5fb1SAntonio Huete Jimenez Words_Free(varnames);
575a34d5fb1SAntonio Huete Jimenez free(expanded);
576a34d5fb1SAntonio Huete Jimenez }
577a34d5fb1SAntonio Huete Jimenez
578*6eef5f0cSAntonio Huete Jimenez static bool
MayExport(const char * name)579a34d5fb1SAntonio Huete Jimenez MayExport(const char *name)
580a34d5fb1SAntonio Huete Jimenez {
581a34d5fb1SAntonio Huete Jimenez if (name[0] == '.')
582*6eef5f0cSAntonio Huete Jimenez return false; /* skip internals */
583a34d5fb1SAntonio Huete Jimenez if (name[0] == '-')
584*6eef5f0cSAntonio Huete Jimenez return false; /* skip misnamed variables */
585a34d5fb1SAntonio Huete Jimenez if (name[1] == '\0') {
58601e196c8SJohn Marino /*
58701e196c8SJohn Marino * A single char.
588a34d5fb1SAntonio Huete Jimenez * If it is one of the variables that should only appear in
589a34d5fb1SAntonio Huete Jimenez * local scope, skip it, else we can get Var_Subst
59001e196c8SJohn Marino * into a loop.
59101e196c8SJohn Marino */
59201e196c8SJohn Marino switch (name[0]) {
59301e196c8SJohn Marino case '@':
59401e196c8SJohn Marino case '%':
59501e196c8SJohn Marino case '*':
59601e196c8SJohn Marino case '!':
597*6eef5f0cSAntonio Huete Jimenez return false;
59801e196c8SJohn Marino }
59901e196c8SJohn Marino }
600*6eef5f0cSAntonio Huete Jimenez return true;
60101e196c8SJohn Marino }
602a34d5fb1SAntonio Huete Jimenez
603*6eef5f0cSAntonio Huete Jimenez static bool
ExportVarEnv(Var * v)604a34d5fb1SAntonio Huete Jimenez ExportVarEnv(Var *v)
605a34d5fb1SAntonio Huete Jimenez {
606a34d5fb1SAntonio Huete Jimenez const char *name = v->name.str;
607a34d5fb1SAntonio Huete Jimenez char *val = v->val.data;
608a34d5fb1SAntonio Huete Jimenez char *expr;
609a34d5fb1SAntonio Huete Jimenez
610*6eef5f0cSAntonio Huete Jimenez if (v->exported && !v->reexport)
611*6eef5f0cSAntonio Huete Jimenez return false; /* nothing to do */
612a34d5fb1SAntonio Huete Jimenez
613a34d5fb1SAntonio Huete Jimenez if (strchr(val, '$') == NULL) {
614*6eef5f0cSAntonio Huete Jimenez if (!v->exported)
615a34d5fb1SAntonio Huete Jimenez setenv(name, val, 1);
616*6eef5f0cSAntonio Huete Jimenez return true;
61701e196c8SJohn Marino }
618a34d5fb1SAntonio Huete Jimenez
619*6eef5f0cSAntonio Huete Jimenez if (v->inUse) {
62001e196c8SJohn Marino /*
62101e196c8SJohn Marino * We recursed while exporting in a child.
62201e196c8SJohn Marino * This isn't going to end well, just skip it.
62301e196c8SJohn Marino */
624*6eef5f0cSAntonio Huete Jimenez return false;
62501e196c8SJohn Marino }
626a34d5fb1SAntonio Huete Jimenez
627a34d5fb1SAntonio Huete Jimenez /* XXX: name is injected without escaping it */
628a34d5fb1SAntonio Huete Jimenez expr = str_concat3("${", name, "}");
629a34d5fb1SAntonio Huete Jimenez (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &val);
630a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
63101e196c8SJohn Marino setenv(name, val, 1);
63201e196c8SJohn Marino free(val);
633a34d5fb1SAntonio Huete Jimenez free(expr);
634*6eef5f0cSAntonio Huete Jimenez return true;
63501e196c8SJohn Marino }
63601e196c8SJohn Marino
637*6eef5f0cSAntonio Huete Jimenez static bool
ExportVarPlain(Var * v)638a34d5fb1SAntonio Huete Jimenez ExportVarPlain(Var *v)
639ca58f742SDaniel Fojt {
640a34d5fb1SAntonio Huete Jimenez if (strchr(v->val.data, '$') == NULL) {
641a34d5fb1SAntonio Huete Jimenez setenv(v->name.str, v->val.data, 1);
642*6eef5f0cSAntonio Huete Jimenez v->exported = true;
643*6eef5f0cSAntonio Huete Jimenez v->reexport = false;
644*6eef5f0cSAntonio Huete Jimenez return true;
645ca58f742SDaniel Fojt }
646ca58f742SDaniel Fojt
64701e196c8SJohn Marino /*
648a34d5fb1SAntonio Huete Jimenez * Flag the variable as something we need to re-export.
649a34d5fb1SAntonio Huete Jimenez * No point actually exporting it now though,
650a34d5fb1SAntonio Huete Jimenez * the child process can do it at the last minute.
651ec533708SSascha Wildner * Avoid calling setenv more often than necessary since it can leak.
652a34d5fb1SAntonio Huete Jimenez */
653*6eef5f0cSAntonio Huete Jimenez v->exported = true;
654*6eef5f0cSAntonio Huete Jimenez v->reexport = true;
655*6eef5f0cSAntonio Huete Jimenez return true;
656a34d5fb1SAntonio Huete Jimenez }
657a34d5fb1SAntonio Huete Jimenez
658*6eef5f0cSAntonio Huete Jimenez static bool
ExportVarLiteral(Var * v)659a34d5fb1SAntonio Huete Jimenez ExportVarLiteral(Var *v)
660a34d5fb1SAntonio Huete Jimenez {
661*6eef5f0cSAntonio Huete Jimenez if (v->exported && !v->reexport)
662*6eef5f0cSAntonio Huete Jimenez return false;
663a34d5fb1SAntonio Huete Jimenez
664*6eef5f0cSAntonio Huete Jimenez if (!v->exported)
665a34d5fb1SAntonio Huete Jimenez setenv(v->name.str, v->val.data, 1);
666a34d5fb1SAntonio Huete Jimenez
667*6eef5f0cSAntonio Huete Jimenez return true;
668a34d5fb1SAntonio Huete Jimenez }
669a34d5fb1SAntonio Huete Jimenez
670a34d5fb1SAntonio Huete Jimenez /*
671ec533708SSascha Wildner * Mark a single variable to be exported later for subprocesses.
672a34d5fb1SAntonio Huete Jimenez *
673ec533708SSascha Wildner * Internal variables (those starting with '.') are not exported.
674a34d5fb1SAntonio Huete Jimenez */
675*6eef5f0cSAntonio Huete Jimenez static bool
ExportVar(const char * name,VarExportMode mode)676a34d5fb1SAntonio Huete Jimenez ExportVar(const char *name, VarExportMode mode)
677a34d5fb1SAntonio Huete Jimenez {
678a34d5fb1SAntonio Huete Jimenez Var *v;
679a34d5fb1SAntonio Huete Jimenez
680a34d5fb1SAntonio Huete Jimenez if (!MayExport(name))
681*6eef5f0cSAntonio Huete Jimenez return false;
682a34d5fb1SAntonio Huete Jimenez
683*6eef5f0cSAntonio Huete Jimenez v = VarFind(name, SCOPE_GLOBAL, false);
684a34d5fb1SAntonio Huete Jimenez if (v == NULL)
685*6eef5f0cSAntonio Huete Jimenez return false;
686a34d5fb1SAntonio Huete Jimenez
687a34d5fb1SAntonio Huete Jimenez if (mode == VEM_ENV)
688a34d5fb1SAntonio Huete Jimenez return ExportVarEnv(v);
689a34d5fb1SAntonio Huete Jimenez else if (mode == VEM_PLAIN)
690a34d5fb1SAntonio Huete Jimenez return ExportVarPlain(v);
691a34d5fb1SAntonio Huete Jimenez else
692a34d5fb1SAntonio Huete Jimenez return ExportVarLiteral(v);
693a34d5fb1SAntonio Huete Jimenez }
694a34d5fb1SAntonio Huete Jimenez
695a34d5fb1SAntonio Huete Jimenez /*
696a34d5fb1SAntonio Huete Jimenez * Actually export the variables that have been marked as needing to be
697a34d5fb1SAntonio Huete Jimenez * re-exported.
69801e196c8SJohn Marino */
69901e196c8SJohn Marino void
Var_ReexportVars(void)700a34d5fb1SAntonio Huete Jimenez Var_ReexportVars(void)
70101e196c8SJohn Marino {
702a34d5fb1SAntonio Huete Jimenez char *xvarnames;
70301e196c8SJohn Marino
7045f1e34d9SAlexandre Perrin /*
705a34d5fb1SAntonio Huete Jimenez * Several make implementations support this sort of mechanism for
706a34d5fb1SAntonio Huete Jimenez * tracking recursion - but each uses a different name.
7075f1e34d9SAlexandre Perrin * We allow the makefiles to update MAKELEVEL and ensure
7085f1e34d9SAlexandre Perrin * children see a correctly incremented value.
7095f1e34d9SAlexandre Perrin */
710ec533708SSascha Wildner char tmp[21];
711a34d5fb1SAntonio Huete Jimenez snprintf(tmp, sizeof tmp, "%d", makelevel + 1);
7125f1e34d9SAlexandre Perrin setenv(MAKE_LEVEL_ENV, tmp, 1);
7135f1e34d9SAlexandre Perrin
714a34d5fb1SAntonio Huete Jimenez if (var_exportedVars == VAR_EXPORTED_NONE)
71501e196c8SJohn Marino return;
71601e196c8SJohn Marino
717a34d5fb1SAntonio Huete Jimenez if (var_exportedVars == VAR_EXPORTED_ALL) {
718a34d5fb1SAntonio Huete Jimenez HashIter hi;
719a34d5fb1SAntonio Huete Jimenez
720ec533708SSascha Wildner /* Ouch! Exporting all variables at once is crazy. */
721a34d5fb1SAntonio Huete Jimenez HashIter_Init(&hi, &SCOPE_GLOBAL->vars);
722a34d5fb1SAntonio Huete Jimenez while (HashIter_Next(&hi) != NULL) {
723a34d5fb1SAntonio Huete Jimenez Var *var = hi.entry->value;
724a34d5fb1SAntonio Huete Jimenez ExportVar(var->name.str, VEM_ENV);
725a34d5fb1SAntonio Huete Jimenez }
72601e196c8SJohn Marino return;
72701e196c8SJohn Marino }
72801e196c8SJohn Marino
729a34d5fb1SAntonio Huete Jimenez (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, VARE_WANTRES,
730a34d5fb1SAntonio Huete Jimenez &xvarnames);
731a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
732a34d5fb1SAntonio Huete Jimenez if (xvarnames[0] != '\0') {
733*6eef5f0cSAntonio Huete Jimenez Words varnames = Str_Words(xvarnames, false);
734a34d5fb1SAntonio Huete Jimenez size_t i;
735a34d5fb1SAntonio Huete Jimenez
736a34d5fb1SAntonio Huete Jimenez for (i = 0; i < varnames.len; i++)
737a34d5fb1SAntonio Huete Jimenez ExportVar(varnames.words[i], VEM_ENV);
738a34d5fb1SAntonio Huete Jimenez Words_Free(varnames);
73901e196c8SJohn Marino }
740a34d5fb1SAntonio Huete Jimenez free(xvarnames);
74101e196c8SJohn Marino }
74201e196c8SJohn Marino
743a34d5fb1SAntonio Huete Jimenez static void
ExportVars(const char * varnames,bool isExport,VarExportMode mode)744*6eef5f0cSAntonio Huete Jimenez ExportVars(const char *varnames, bool isExport, VarExportMode mode)
745ec533708SSascha Wildner /* TODO: try to combine the parameters 'isExport' and 'mode'. */
74601e196c8SJohn Marino {
747*6eef5f0cSAntonio Huete Jimenez Words words = Str_Words(varnames, false);
748a34d5fb1SAntonio Huete Jimenez size_t i;
74901e196c8SJohn Marino
750a34d5fb1SAntonio Huete Jimenez if (words.len == 1 && words.words[0][0] == '\0')
751a34d5fb1SAntonio Huete Jimenez words.len = 0;
752a34d5fb1SAntonio Huete Jimenez
753a34d5fb1SAntonio Huete Jimenez for (i = 0; i < words.len; i++) {
754a34d5fb1SAntonio Huete Jimenez const char *varname = words.words[i];
755a34d5fb1SAntonio Huete Jimenez if (!ExportVar(varname, mode))
756a34d5fb1SAntonio Huete Jimenez continue;
757a34d5fb1SAntonio Huete Jimenez
758a34d5fb1SAntonio Huete Jimenez if (var_exportedVars == VAR_EXPORTED_NONE)
759a34d5fb1SAntonio Huete Jimenez var_exportedVars = VAR_EXPORTED_SOME;
760a34d5fb1SAntonio Huete Jimenez
761a34d5fb1SAntonio Huete Jimenez if (isExport && mode == VEM_PLAIN)
762a34d5fb1SAntonio Huete Jimenez Global_Append(MAKE_EXPORTED, varname);
763a34d5fb1SAntonio Huete Jimenez }
764a34d5fb1SAntonio Huete Jimenez Words_Free(words);
765a34d5fb1SAntonio Huete Jimenez }
766a34d5fb1SAntonio Huete Jimenez
767a34d5fb1SAntonio Huete Jimenez static void
ExportVarsExpand(const char * uvarnames,bool isExport,VarExportMode mode)768*6eef5f0cSAntonio Huete Jimenez ExportVarsExpand(const char *uvarnames, bool isExport, VarExportMode mode)
769a34d5fb1SAntonio Huete Jimenez {
770a34d5fb1SAntonio Huete Jimenez char *xvarnames;
771a34d5fb1SAntonio Huete Jimenez
772a34d5fb1SAntonio Huete Jimenez (void)Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES, &xvarnames);
773a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
774a34d5fb1SAntonio Huete Jimenez ExportVars(xvarnames, isExport, mode);
775a34d5fb1SAntonio Huete Jimenez free(xvarnames);
776a34d5fb1SAntonio Huete Jimenez }
777a34d5fb1SAntonio Huete Jimenez
778a34d5fb1SAntonio Huete Jimenez /* Export the named variables, or all variables. */
779a34d5fb1SAntonio Huete Jimenez void
Var_Export(VarExportMode mode,const char * varnames)780a34d5fb1SAntonio Huete Jimenez Var_Export(VarExportMode mode, const char *varnames)
781a34d5fb1SAntonio Huete Jimenez {
782a34d5fb1SAntonio Huete Jimenez if (mode == VEM_PLAIN && varnames[0] == '\0') {
78301e196c8SJohn Marino var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */
78401e196c8SJohn Marino return;
78501e196c8SJohn Marino }
78601e196c8SJohn Marino
787*6eef5f0cSAntonio Huete Jimenez ExportVarsExpand(varnames, true, mode);
78801e196c8SJohn Marino }
78901e196c8SJohn Marino
79001e196c8SJohn Marino void
Var_ExportVars(const char * varnames)791a34d5fb1SAntonio Huete Jimenez Var_ExportVars(const char *varnames)
79201e196c8SJohn Marino {
793*6eef5f0cSAntonio Huete Jimenez ExportVarsExpand(varnames, false, VEM_PLAIN);
79401e196c8SJohn Marino }
79501e196c8SJohn Marino
79601e196c8SJohn Marino
797a34d5fb1SAntonio Huete Jimenez extern char **environ;
798a34d5fb1SAntonio Huete Jimenez
799a34d5fb1SAntonio Huete Jimenez static void
ClearEnv(void)800a34d5fb1SAntonio Huete Jimenez ClearEnv(void)
801a34d5fb1SAntonio Huete Jimenez {
802a34d5fb1SAntonio Huete Jimenez const char *cp;
80301e196c8SJohn Marino char **newenv;
80401e196c8SJohn Marino
8055f1e34d9SAlexandre Perrin cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */
80601e196c8SJohn Marino if (environ == savedEnv) {
80701e196c8SJohn Marino /* we have been here before! */
80801e196c8SJohn Marino newenv = bmake_realloc(environ, 2 * sizeof(char *));
80901e196c8SJohn Marino } else {
810a34d5fb1SAntonio Huete Jimenez if (savedEnv != NULL) {
81101e196c8SJohn Marino free(savedEnv);
81201e196c8SJohn Marino savedEnv = NULL;
81301e196c8SJohn Marino }
81401e196c8SJohn Marino newenv = bmake_malloc(2 * sizeof(char *));
81501e196c8SJohn Marino }
816a34d5fb1SAntonio Huete Jimenez
81701e196c8SJohn Marino /* Note: we cannot safely free() the original environ. */
81801e196c8SJohn Marino environ = savedEnv = newenv;
81901e196c8SJohn Marino newenv[0] = NULL;
82001e196c8SJohn Marino newenv[1] = NULL;
821a34d5fb1SAntonio Huete Jimenez if (cp != NULL && *cp != '\0')
8225f1e34d9SAlexandre Perrin setenv(MAKE_LEVEL_ENV, cp, 1);
823a34d5fb1SAntonio Huete Jimenez }
824a34d5fb1SAntonio Huete Jimenez
825a34d5fb1SAntonio Huete Jimenez static void
GetVarnamesToUnexport(bool isEnv,const char * arg,FStr * out_varnames,UnexportWhat * out_what)826*6eef5f0cSAntonio Huete Jimenez GetVarnamesToUnexport(bool isEnv, const char *arg,
827a34d5fb1SAntonio Huete Jimenez FStr *out_varnames, UnexportWhat *out_what)
828a34d5fb1SAntonio Huete Jimenez {
829a34d5fb1SAntonio Huete Jimenez UnexportWhat what;
830a34d5fb1SAntonio Huete Jimenez FStr varnames = FStr_InitRefer("");
831a34d5fb1SAntonio Huete Jimenez
832a34d5fb1SAntonio Huete Jimenez if (isEnv) {
833a34d5fb1SAntonio Huete Jimenez if (arg[0] != '\0') {
834a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
835a34d5fb1SAntonio Huete Jimenez "The directive .unexport-env does not take "
836a34d5fb1SAntonio Huete Jimenez "arguments");
837ec533708SSascha Wildner /* continue anyway */
838a34d5fb1SAntonio Huete Jimenez }
839a34d5fb1SAntonio Huete Jimenez what = UNEXPORT_ENV;
840a34d5fb1SAntonio Huete Jimenez
84101e196c8SJohn Marino } else {
842a34d5fb1SAntonio Huete Jimenez what = arg[0] != '\0' ? UNEXPORT_NAMED : UNEXPORT_ALL;
843a34d5fb1SAntonio Huete Jimenez if (what == UNEXPORT_NAMED)
844a34d5fb1SAntonio Huete Jimenez varnames = FStr_InitRefer(arg);
84501e196c8SJohn Marino }
84601e196c8SJohn Marino
847a34d5fb1SAntonio Huete Jimenez if (what != UNEXPORT_NAMED) {
848a34d5fb1SAntonio Huete Jimenez char *expanded;
84901e196c8SJohn Marino /* Using .MAKE.EXPORTED */
850a34d5fb1SAntonio Huete Jimenez (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL,
851a34d5fb1SAntonio Huete Jimenez VARE_WANTRES, &expanded);
852a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
853a34d5fb1SAntonio Huete Jimenez varnames = FStr_InitOwn(expanded);
85401e196c8SJohn Marino }
85501e196c8SJohn Marino
856a34d5fb1SAntonio Huete Jimenez *out_varnames = varnames;
857a34d5fb1SAntonio Huete Jimenez *out_what = what;
85801e196c8SJohn Marino }
859a34d5fb1SAntonio Huete Jimenez
860a34d5fb1SAntonio Huete Jimenez static void
UnexportVar(Substring varname,UnexportWhat what)861*6eef5f0cSAntonio Huete Jimenez UnexportVar(Substring varname, UnexportWhat what)
862a34d5fb1SAntonio Huete Jimenez {
863*6eef5f0cSAntonio Huete Jimenez Var *v = VarFindSubstring(varname, SCOPE_GLOBAL, false);
864a34d5fb1SAntonio Huete Jimenez if (v == NULL) {
865*6eef5f0cSAntonio Huete Jimenez DEBUG2(VAR, "Not unexporting \"%.*s\" (not found)\n",
866*6eef5f0cSAntonio Huete Jimenez (int)Substring_Length(varname), varname.start);
867a34d5fb1SAntonio Huete Jimenez return;
868a34d5fb1SAntonio Huete Jimenez }
869a34d5fb1SAntonio Huete Jimenez
870*6eef5f0cSAntonio Huete Jimenez DEBUG2(VAR, "Unexporting \"%.*s\"\n",
871*6eef5f0cSAntonio Huete Jimenez (int)Substring_Length(varname), varname.start);
872*6eef5f0cSAntonio Huete Jimenez if (what != UNEXPORT_ENV && v->exported && !v->reexport)
873a34d5fb1SAntonio Huete Jimenez unsetenv(v->name.str);
874*6eef5f0cSAntonio Huete Jimenez v->exported = false;
875*6eef5f0cSAntonio Huete Jimenez v->reexport = false;
876a34d5fb1SAntonio Huete Jimenez
877a34d5fb1SAntonio Huete Jimenez if (what == UNEXPORT_NAMED) {
878a34d5fb1SAntonio Huete Jimenez /* Remove the variable names from .MAKE.EXPORTED. */
879a34d5fb1SAntonio Huete Jimenez /* XXX: v->name is injected without escaping it */
880a34d5fb1SAntonio Huete Jimenez char *expr = str_concat3("${" MAKE_EXPORTED ":N",
881a34d5fb1SAntonio Huete Jimenez v->name.str, "}");
882a34d5fb1SAntonio Huete Jimenez char *cp;
883a34d5fb1SAntonio Huete Jimenez (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &cp);
884a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
885a34d5fb1SAntonio Huete Jimenez Global_Set(MAKE_EXPORTED, cp);
88601e196c8SJohn Marino free(cp);
887a34d5fb1SAntonio Huete Jimenez free(expr);
88801e196c8SJohn Marino }
88901e196c8SJohn Marino }
89001e196c8SJohn Marino
891ca58f742SDaniel Fojt static void
UnexportVars(FStr * varnames,UnexportWhat what)892a34d5fb1SAntonio Huete Jimenez UnexportVars(FStr *varnames, UnexportWhat what)
89301e196c8SJohn Marino {
894a34d5fb1SAntonio Huete Jimenez size_t i;
895*6eef5f0cSAntonio Huete Jimenez SubstringWords words;
896a34d5fb1SAntonio Huete Jimenez
897a34d5fb1SAntonio Huete Jimenez if (what == UNEXPORT_ENV)
898a34d5fb1SAntonio Huete Jimenez ClearEnv();
899a34d5fb1SAntonio Huete Jimenez
900*6eef5f0cSAntonio Huete Jimenez words = Substring_Words(varnames->str, false);
901*6eef5f0cSAntonio Huete Jimenez for (i = 0; i < words.len; i++)
902*6eef5f0cSAntonio Huete Jimenez UnexportVar(words.words[i], what);
903*6eef5f0cSAntonio Huete Jimenez SubstringWords_Free(words);
904a34d5fb1SAntonio Huete Jimenez
905a34d5fb1SAntonio Huete Jimenez if (what != UNEXPORT_NAMED)
906a34d5fb1SAntonio Huete Jimenez Global_Delete(MAKE_EXPORTED);
907a34d5fb1SAntonio Huete Jimenez }
90801e196c8SJohn Marino
90901e196c8SJohn Marino /*
910a34d5fb1SAntonio Huete Jimenez * This is called when .unexport[-env] is seen.
911a34d5fb1SAntonio Huete Jimenez *
912a34d5fb1SAntonio Huete Jimenez * str must have the form "unexport[-env] varname...".
91301e196c8SJohn Marino */
914a34d5fb1SAntonio Huete Jimenez void
Var_UnExport(bool isEnv,const char * arg)915*6eef5f0cSAntonio Huete Jimenez Var_UnExport(bool isEnv, const char *arg)
916a34d5fb1SAntonio Huete Jimenez {
917a34d5fb1SAntonio Huete Jimenez UnexportWhat what;
918a34d5fb1SAntonio Huete Jimenez FStr varnames;
919a34d5fb1SAntonio Huete Jimenez
920a34d5fb1SAntonio Huete Jimenez GetVarnamesToUnexport(isEnv, arg, &varnames, &what);
921a34d5fb1SAntonio Huete Jimenez UnexportVars(&varnames, what);
922a34d5fb1SAntonio Huete Jimenez FStr_Done(&varnames);
92301e196c8SJohn Marino }
924a34d5fb1SAntonio Huete Jimenez
925ec533708SSascha Wildner /*
926ec533708SSascha Wildner * When there is a variable of the same name in the command line scope, the
927ec533708SSascha Wildner * global variable would not be visible anywhere. Therefore there is no
928ec533708SSascha Wildner * point in setting it at all.
929ec533708SSascha Wildner *
930ec533708SSascha Wildner * See 'scope == SCOPE_CMDLINE' in Var_SetWithFlags.
931ec533708SSascha Wildner */
932*6eef5f0cSAntonio Huete Jimenez static bool
ExistsInCmdline(const char * name,const char * val)933ec533708SSascha Wildner ExistsInCmdline(const char *name, const char *val)
934ec533708SSascha Wildner {
935ec533708SSascha Wildner Var *v;
936ec533708SSascha Wildner
937*6eef5f0cSAntonio Huete Jimenez v = VarFind(name, SCOPE_CMDLINE, false);
938ec533708SSascha Wildner if (v == NULL)
939*6eef5f0cSAntonio Huete Jimenez return false;
940ec533708SSascha Wildner
941*6eef5f0cSAntonio Huete Jimenez if (v->fromCmd) {
942ec533708SSascha Wildner DEBUG3(VAR, "%s: %s = %s ignored!\n",
943ec533708SSascha Wildner SCOPE_GLOBAL->name, name, val);
944*6eef5f0cSAntonio Huete Jimenez return true;
945ec533708SSascha Wildner }
946ec533708SSascha Wildner
947*6eef5f0cSAntonio Huete Jimenez VarFreeShortLived(v);
948*6eef5f0cSAntonio Huete Jimenez return false;
949ec533708SSascha Wildner }
950ec533708SSascha Wildner
951a34d5fb1SAntonio Huete Jimenez /* Set the variable to the value; the name is not expanded. */
952a34d5fb1SAntonio Huete Jimenez void
Var_SetWithFlags(GNode * scope,const char * name,const char * val,VarSetFlags flags)953a34d5fb1SAntonio Huete Jimenez Var_SetWithFlags(GNode *scope, const char *name, const char *val,
954a34d5fb1SAntonio Huete Jimenez VarSetFlags flags)
955a34d5fb1SAntonio Huete Jimenez {
956a34d5fb1SAntonio Huete Jimenez Var *v;
957a34d5fb1SAntonio Huete Jimenez
958a34d5fb1SAntonio Huete Jimenez assert(val != NULL);
959a34d5fb1SAntonio Huete Jimenez if (name[0] == '\0') {
960a34d5fb1SAntonio Huete Jimenez DEBUG0(VAR, "SetVar: variable name is empty - ignored\n");
96101e196c8SJohn Marino return;
96201e196c8SJohn Marino }
963a34d5fb1SAntonio Huete Jimenez
964ec533708SSascha Wildner if (scope == SCOPE_GLOBAL && ExistsInCmdline(name, val))
965a34d5fb1SAntonio Huete Jimenez return;
96601e196c8SJohn Marino
967a34d5fb1SAntonio Huete Jimenez /*
968a34d5fb1SAntonio Huete Jimenez * Only look for a variable in the given scope since anything set
969a34d5fb1SAntonio Huete Jimenez * here will override anything in a lower scope, so there's not much
970ec533708SSascha Wildner * point in searching them all.
971a34d5fb1SAntonio Huete Jimenez */
972*6eef5f0cSAntonio Huete Jimenez v = VarFind(name, scope, false);
973a34d5fb1SAntonio Huete Jimenez if (v == NULL) {
974a34d5fb1SAntonio Huete Jimenez if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) {
975a34d5fb1SAntonio Huete Jimenez /*
976a34d5fb1SAntonio Huete Jimenez * This var would normally prevent the same name being
977a34d5fb1SAntonio Huete Jimenez * added to SCOPE_GLOBAL, so delete it from there if
978a34d5fb1SAntonio Huete Jimenez * needed. Otherwise -V name may show the wrong value.
979ec533708SSascha Wildner *
980ec533708SSascha Wildner * See ExistsInCmdline.
981a34d5fb1SAntonio Huete Jimenez */
982ec533708SSascha Wildner Var_Delete(SCOPE_GLOBAL, name);
98301e196c8SJohn Marino }
984*6eef5f0cSAntonio Huete Jimenez if (strcmp(name, ".SUFFIXES") == 0) {
985*6eef5f0cSAntonio Huete Jimenez /* special: treat as readOnly */
986a34d5fb1SAntonio Huete Jimenez DEBUG3(VAR, "%s: %s = %s ignored (read-only)\n",
987a34d5fb1SAntonio Huete Jimenez scope->name, name, val);
988a34d5fb1SAntonio Huete Jimenez return;
98901e196c8SJohn Marino }
990*6eef5f0cSAntonio Huete Jimenez v = VarAdd(name, val, scope, flags);
991*6eef5f0cSAntonio Huete Jimenez } else {
992*6eef5f0cSAntonio Huete Jimenez if (v->readOnly && !(flags & VAR_SET_READONLY)) {
993*6eef5f0cSAntonio Huete Jimenez DEBUG3(VAR, "%s: %s = %s ignored (read-only)\n",
994*6eef5f0cSAntonio Huete Jimenez scope->name, name, val);
995*6eef5f0cSAntonio Huete Jimenez return;
996*6eef5f0cSAntonio Huete Jimenez }
997*6eef5f0cSAntonio Huete Jimenez Buf_Clear(&v->val);
998a34d5fb1SAntonio Huete Jimenez Buf_AddStr(&v->val, val);
999a34d5fb1SAntonio Huete Jimenez
1000*6eef5f0cSAntonio Huete Jimenez DEBUG4(VAR, "%s: %s = %s%s\n",
1001*6eef5f0cSAntonio Huete Jimenez scope->name, name, val, ValueDescription(val));
1002*6eef5f0cSAntonio Huete Jimenez if (v->exported)
1003a34d5fb1SAntonio Huete Jimenez ExportVar(name, VEM_PLAIN);
100401e196c8SJohn Marino }
1005ec533708SSascha Wildner
100601e196c8SJohn Marino /*
100701e196c8SJohn Marino * Any variables given on the command line are automatically exported
1008ec533708SSascha Wildner * to the environment (as per POSIX standard), except for internals.
100901e196c8SJohn Marino */
1010a34d5fb1SAntonio Huete Jimenez if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) &&
1011a34d5fb1SAntonio Huete Jimenez name[0] != '.') {
1012*6eef5f0cSAntonio Huete Jimenez v->fromCmd = true;
1013a34d5fb1SAntonio Huete Jimenez
101401e196c8SJohn Marino /*
101501e196c8SJohn Marino * If requested, don't export these in the environment
101601e196c8SJohn Marino * individually. We still put them in MAKEOVERRIDES so
101701e196c8SJohn Marino * that the command-line settings continue to override
101801e196c8SJohn Marino * Makefile settings.
101901e196c8SJohn Marino */
1020a34d5fb1SAntonio Huete Jimenez if (!opts.varNoExportEnv)
1021a34d5fb1SAntonio Huete Jimenez setenv(name, val, 1);
1022ec533708SSascha Wildner /* XXX: What about .MAKE.EXPORTED? */
1023*6eef5f0cSAntonio Huete Jimenez /*
1024*6eef5f0cSAntonio Huete Jimenez * XXX: Why not just mark the variable for needing export, as
1025*6eef5f0cSAntonio Huete Jimenez * in ExportVarPlain?
1026*6eef5f0cSAntonio Huete Jimenez */
102701e196c8SJohn Marino
1028a34d5fb1SAntonio Huete Jimenez Global_Append(MAKEOVERRIDES, name);
102901e196c8SJohn Marino }
1030ec533708SSascha Wildner
1031a34d5fb1SAntonio Huete Jimenez if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0)
1032a34d5fb1SAntonio Huete Jimenez save_dollars = ParseBoolean(val, save_dollars);
103301e196c8SJohn Marino
103401e196c8SJohn Marino if (v != NULL)
1035*6eef5f0cSAntonio Huete Jimenez VarFreeShortLived(v);
1036a34d5fb1SAntonio Huete Jimenez }
1037a34d5fb1SAntonio Huete Jimenez
1038a34d5fb1SAntonio Huete Jimenez void
Var_Set(GNode * scope,const char * name,const char * val)1039a34d5fb1SAntonio Huete Jimenez Var_Set(GNode *scope, const char *name, const char *val)
1040a34d5fb1SAntonio Huete Jimenez {
1041a34d5fb1SAntonio Huete Jimenez Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
1042a34d5fb1SAntonio Huete Jimenez }
1043a34d5fb1SAntonio Huete Jimenez
1044a34d5fb1SAntonio Huete Jimenez /*
1045a34d5fb1SAntonio Huete Jimenez * Set the variable name to the value val in the given scope.
1046a34d5fb1SAntonio Huete Jimenez *
1047a34d5fb1SAntonio Huete Jimenez * If the variable doesn't yet exist, it is created.
1048a34d5fb1SAntonio Huete Jimenez * Otherwise the new value overwrites and replaces the old value.
104901e196c8SJohn Marino *
105001e196c8SJohn Marino * Input:
1051*6eef5f0cSAntonio Huete Jimenez * scope scope in which to set it
1052a34d5fb1SAntonio Huete Jimenez * name name of the variable to set, is expanded once
1053a34d5fb1SAntonio Huete Jimenez * val value to give to the variable
105401e196c8SJohn Marino */
105501e196c8SJohn Marino void
Var_SetExpand(GNode * scope,const char * name,const char * val)1056a34d5fb1SAntonio Huete Jimenez Var_SetExpand(GNode *scope, const char *name, const char *val)
1057a34d5fb1SAntonio Huete Jimenez {
1058*6eef5f0cSAntonio Huete Jimenez const char *unexpanded_name = name;
1059*6eef5f0cSAntonio Huete Jimenez FStr varname = FStr_InitRefer(name);
1060*6eef5f0cSAntonio Huete Jimenez
1061*6eef5f0cSAntonio Huete Jimenez assert(val != NULL);
1062*6eef5f0cSAntonio Huete Jimenez
1063*6eef5f0cSAntonio Huete Jimenez Var_Expand(&varname, scope, VARE_WANTRES);
1064*6eef5f0cSAntonio Huete Jimenez
1065*6eef5f0cSAntonio Huete Jimenez if (varname.str[0] == '\0') {
1066*6eef5f0cSAntonio Huete Jimenez DEBUG2(VAR,
1067*6eef5f0cSAntonio Huete Jimenez "Var_SetExpand: variable name \"%s\" expands "
1068*6eef5f0cSAntonio Huete Jimenez "to empty string, with value \"%s\" - ignored\n",
1069*6eef5f0cSAntonio Huete Jimenez unexpanded_name, val);
1070*6eef5f0cSAntonio Huete Jimenez } else
1071*6eef5f0cSAntonio Huete Jimenez Var_SetWithFlags(scope, varname.str, val, VAR_SET_NONE);
1072*6eef5f0cSAntonio Huete Jimenez
1073*6eef5f0cSAntonio Huete Jimenez FStr_Done(&varname);
1074a34d5fb1SAntonio Huete Jimenez }
1075a34d5fb1SAntonio Huete Jimenez
1076a34d5fb1SAntonio Huete Jimenez void
Global_Set(const char * name,const char * value)1077a34d5fb1SAntonio Huete Jimenez Global_Set(const char *name, const char *value)
1078a34d5fb1SAntonio Huete Jimenez {
1079a34d5fb1SAntonio Huete Jimenez Var_Set(SCOPE_GLOBAL, name, value);
1080a34d5fb1SAntonio Huete Jimenez }
1081a34d5fb1SAntonio Huete Jimenez
1082a34d5fb1SAntonio Huete Jimenez void
Global_Delete(const char * name)1083a34d5fb1SAntonio Huete Jimenez Global_Delete(const char *name)
1084a34d5fb1SAntonio Huete Jimenez {
1085a34d5fb1SAntonio Huete Jimenez Var_Delete(SCOPE_GLOBAL, name);
1086a34d5fb1SAntonio Huete Jimenez }
1087a34d5fb1SAntonio Huete Jimenez
1088a34d5fb1SAntonio Huete Jimenez /*
1089a34d5fb1SAntonio Huete Jimenez * Append the value to the named variable.
1090a34d5fb1SAntonio Huete Jimenez *
1091a34d5fb1SAntonio Huete Jimenez * If the variable doesn't exist, it is created. Otherwise a single space
1092a34d5fb1SAntonio Huete Jimenez * and the given value are appended.
1093a34d5fb1SAntonio Huete Jimenez */
1094a34d5fb1SAntonio Huete Jimenez void
Var_Append(GNode * scope,const char * name,const char * val)1095a34d5fb1SAntonio Huete Jimenez Var_Append(GNode *scope, const char *name, const char *val)
109601e196c8SJohn Marino {
109701e196c8SJohn Marino Var *v;
109801e196c8SJohn Marino
1099a34d5fb1SAntonio Huete Jimenez v = VarFind(name, scope, scope == SCOPE_GLOBAL);
110001e196c8SJohn Marino
110101e196c8SJohn Marino if (v == NULL) {
1102a34d5fb1SAntonio Huete Jimenez Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
1103*6eef5f0cSAntonio Huete Jimenez } else if (v->readOnly) {
1104a34d5fb1SAntonio Huete Jimenez DEBUG1(VAR, "Ignoring append to %s since it is read-only\n",
1105a34d5fb1SAntonio Huete Jimenez name);
1106*6eef5f0cSAntonio Huete Jimenez } else if (scope == SCOPE_CMDLINE || !v->fromCmd) {
110701e196c8SJohn Marino Buf_AddByte(&v->val, ' ');
1108a34d5fb1SAntonio Huete Jimenez Buf_AddStr(&v->val, val);
110901e196c8SJohn Marino
1110a34d5fb1SAntonio Huete Jimenez DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, v->val.data);
111101e196c8SJohn Marino
1112*6eef5f0cSAntonio Huete Jimenez if (v->fromEnvironment) {
1113*6eef5f0cSAntonio Huete Jimenez /* See VarAdd. */
1114*6eef5f0cSAntonio Huete Jimenez HashEntry *he =
1115*6eef5f0cSAntonio Huete Jimenez HashTable_CreateEntry(&scope->vars, name, NULL);
1116*6eef5f0cSAntonio Huete Jimenez HashEntry_Set(he, v);
1117*6eef5f0cSAntonio Huete Jimenez FStr_Done(&v->name);
1118*6eef5f0cSAntonio Huete Jimenez v->name = FStr_InitRefer(/* aliased to */ he->key);
1119*6eef5f0cSAntonio Huete Jimenez v->shortLived = false;
1120*6eef5f0cSAntonio Huete Jimenez v->fromEnvironment = false;
112101e196c8SJohn Marino }
112201e196c8SJohn Marino }
112301e196c8SJohn Marino }
112401e196c8SJohn Marino
1125a34d5fb1SAntonio Huete Jimenez /*
1126a34d5fb1SAntonio Huete Jimenez * The variable of the given name has the given value appended to it in the
1127a34d5fb1SAntonio Huete Jimenez * given scope.
1128a34d5fb1SAntonio Huete Jimenez *
1129a34d5fb1SAntonio Huete Jimenez * If the variable doesn't exist, it is created. Otherwise the strings are
1130a34d5fb1SAntonio Huete Jimenez * concatenated, with a space in between.
113101e196c8SJohn Marino *
113201e196c8SJohn Marino * Input:
1133*6eef5f0cSAntonio Huete Jimenez * scope scope in which this should occur
1134a34d5fb1SAntonio Huete Jimenez * name name of the variable to modify, is expanded once
1135a34d5fb1SAntonio Huete Jimenez * val string to append to it
113601e196c8SJohn Marino *
1137a34d5fb1SAntonio Huete Jimenez * Notes:
1138a34d5fb1SAntonio Huete Jimenez * Only if the variable is being sought in the global scope is the
1139a34d5fb1SAntonio Huete Jimenez * environment searched.
1140a34d5fb1SAntonio Huete Jimenez * XXX: Knows its calling circumstances in that if called with scope
1141a34d5fb1SAntonio Huete Jimenez * an actual target, it will only search that scope since only
1142a34d5fb1SAntonio Huete Jimenez * a local variable could be being appended to. This is actually
1143a34d5fb1SAntonio Huete Jimenez * a big win and must be tolerated.
114401e196c8SJohn Marino */
1145a34d5fb1SAntonio Huete Jimenez void
Var_AppendExpand(GNode * scope,const char * name,const char * val)1146a34d5fb1SAntonio Huete Jimenez Var_AppendExpand(GNode *scope, const char *name, const char *val)
114701e196c8SJohn Marino {
1148ec533708SSascha Wildner FStr xname = FStr_InitRefer(name);
114901e196c8SJohn Marino
1150a34d5fb1SAntonio Huete Jimenez assert(val != NULL);
1151a34d5fb1SAntonio Huete Jimenez
1152*6eef5f0cSAntonio Huete Jimenez Var_Expand(&xname, scope, VARE_WANTRES);
1153*6eef5f0cSAntonio Huete Jimenez if (xname.str != name && xname.str[0] == '\0')
1154*6eef5f0cSAntonio Huete Jimenez DEBUG2(VAR,
1155*6eef5f0cSAntonio Huete Jimenez "Var_AppendExpand: variable name \"%s\" expands "
1156*6eef5f0cSAntonio Huete Jimenez "to empty string, with value \"%s\" - ignored\n",
1157ec533708SSascha Wildner name, val);
1158*6eef5f0cSAntonio Huete Jimenez else
1159ec533708SSascha Wildner Var_Append(scope, xname.str, val);
1160a34d5fb1SAntonio Huete Jimenez
1161ec533708SSascha Wildner FStr_Done(&xname);
1162a34d5fb1SAntonio Huete Jimenez }
1163a34d5fb1SAntonio Huete Jimenez
1164a34d5fb1SAntonio Huete Jimenez void
Global_Append(const char * name,const char * value)1165a34d5fb1SAntonio Huete Jimenez Global_Append(const char *name, const char *value)
1166a34d5fb1SAntonio Huete Jimenez {
1167a34d5fb1SAntonio Huete Jimenez Var_Append(SCOPE_GLOBAL, name, value);
1168a34d5fb1SAntonio Huete Jimenez }
1169a34d5fb1SAntonio Huete Jimenez
1170*6eef5f0cSAntonio Huete Jimenez bool
Var_Exists(GNode * scope,const char * name)1171a34d5fb1SAntonio Huete Jimenez Var_Exists(GNode *scope, const char *name)
1172a34d5fb1SAntonio Huete Jimenez {
1173*6eef5f0cSAntonio Huete Jimenez Var *v = VarFind(name, scope, true);
1174a34d5fb1SAntonio Huete Jimenez if (v == NULL)
1175*6eef5f0cSAntonio Huete Jimenez return false;
1176ca58f742SDaniel Fojt
1177*6eef5f0cSAntonio Huete Jimenez VarFreeShortLived(v);
1178*6eef5f0cSAntonio Huete Jimenez return true;
117901e196c8SJohn Marino }
118001e196c8SJohn Marino
1181a34d5fb1SAntonio Huete Jimenez /*
1182a34d5fb1SAntonio Huete Jimenez * See if the given variable exists, in the given scope or in other
1183a34d5fb1SAntonio Huete Jimenez * fallback scopes.
118401e196c8SJohn Marino *
118501e196c8SJohn Marino * Input:
1186*6eef5f0cSAntonio Huete Jimenez * scope scope in which to start search
1187*6eef5f0cSAntonio Huete Jimenez * name name of the variable to find, is expanded once
1188a34d5fb1SAntonio Huete Jimenez */
1189*6eef5f0cSAntonio Huete Jimenez bool
Var_ExistsExpand(GNode * scope,const char * name)1190a34d5fb1SAntonio Huete Jimenez Var_ExistsExpand(GNode *scope, const char *name)
1191a34d5fb1SAntonio Huete Jimenez {
1192a34d5fb1SAntonio Huete Jimenez FStr varname = FStr_InitRefer(name);
1193*6eef5f0cSAntonio Huete Jimenez bool exists;
1194a34d5fb1SAntonio Huete Jimenez
1195*6eef5f0cSAntonio Huete Jimenez Var_Expand(&varname, scope, VARE_WANTRES);
1196a34d5fb1SAntonio Huete Jimenez exists = Var_Exists(scope, varname.str);
1197a34d5fb1SAntonio Huete Jimenez FStr_Done(&varname);
1198a34d5fb1SAntonio Huete Jimenez return exists;
1199a34d5fb1SAntonio Huete Jimenez }
1200a34d5fb1SAntonio Huete Jimenez
1201a34d5fb1SAntonio Huete Jimenez /*
1202a34d5fb1SAntonio Huete Jimenez * Return the unexpanded value of the given variable in the given scope,
1203a34d5fb1SAntonio Huete Jimenez * or the usual scopes.
1204a34d5fb1SAntonio Huete Jimenez *
1205a34d5fb1SAntonio Huete Jimenez * Input:
1206a34d5fb1SAntonio Huete Jimenez * scope scope in which to search for it
1207*6eef5f0cSAntonio Huete Jimenez * name name to find, is not expanded any further
120801e196c8SJohn Marino *
120901e196c8SJohn Marino * Results:
1210a34d5fb1SAntonio Huete Jimenez * The value if the variable exists, NULL if it doesn't.
1211ec533708SSascha Wildner * The value is valid until the next modification to any variable.
121201e196c8SJohn Marino */
1213a34d5fb1SAntonio Huete Jimenez FStr
Var_Value(GNode * scope,const char * name)1214a34d5fb1SAntonio Huete Jimenez Var_Value(GNode *scope, const char *name)
121501e196c8SJohn Marino {
1216*6eef5f0cSAntonio Huete Jimenez Var *v = VarFind(name, scope, true);
1217a34d5fb1SAntonio Huete Jimenez char *value;
121801e196c8SJohn Marino
1219ca58f742SDaniel Fojt if (v == NULL)
1220a34d5fb1SAntonio Huete Jimenez return FStr_InitRefer(NULL);
1221ca58f742SDaniel Fojt
1222*6eef5f0cSAntonio Huete Jimenez if (!v->shortLived)
1223ec533708SSascha Wildner return FStr_InitRefer(v->val.data);
1224ec533708SSascha Wildner
1225*6eef5f0cSAntonio Huete Jimenez value = v->val.data;
1226*6eef5f0cSAntonio Huete Jimenez v->val.data = NULL;
1227*6eef5f0cSAntonio Huete Jimenez VarFreeShortLived(v);
1228*6eef5f0cSAntonio Huete Jimenez
1229ec533708SSascha Wildner return FStr_InitOwn(value);
1230a34d5fb1SAntonio Huete Jimenez }
1231a34d5fb1SAntonio Huete Jimenez
1232a34d5fb1SAntonio Huete Jimenez /*
1233a34d5fb1SAntonio Huete Jimenez * Return the unexpanded variable value from this node, without trying to look
1234a34d5fb1SAntonio Huete Jimenez * up the variable in any other scope.
1235a34d5fb1SAntonio Huete Jimenez */
1236a34d5fb1SAntonio Huete Jimenez const char *
GNode_ValueDirect(GNode * gn,const char * name)1237a34d5fb1SAntonio Huete Jimenez GNode_ValueDirect(GNode *gn, const char *name)
1238a34d5fb1SAntonio Huete Jimenez {
1239*6eef5f0cSAntonio Huete Jimenez Var *v = VarFind(name, gn, false);
1240a34d5fb1SAntonio Huete Jimenez return v != NULL ? v->val.data : NULL;
124101e196c8SJohn Marino }
124201e196c8SJohn Marino
1243*6eef5f0cSAntonio Huete Jimenez static VarEvalMode
VarEvalMode_WithoutKeepDollar(VarEvalMode emode)1244*6eef5f0cSAntonio Huete Jimenez VarEvalMode_WithoutKeepDollar(VarEvalMode emode)
1245*6eef5f0cSAntonio Huete Jimenez {
1246*6eef5f0cSAntonio Huete Jimenez if (emode == VARE_KEEP_DOLLAR_UNDEF)
1247*6eef5f0cSAntonio Huete Jimenez return VARE_EVAL_KEEP_UNDEF;
1248*6eef5f0cSAntonio Huete Jimenez if (emode == VARE_EVAL_KEEP_DOLLAR)
1249*6eef5f0cSAntonio Huete Jimenez return VARE_WANTRES;
1250*6eef5f0cSAntonio Huete Jimenez return emode;
1251*6eef5f0cSAntonio Huete Jimenez }
1252*6eef5f0cSAntonio Huete Jimenez
1253*6eef5f0cSAntonio Huete Jimenez static VarEvalMode
VarEvalMode_UndefOk(VarEvalMode emode)1254*6eef5f0cSAntonio Huete Jimenez VarEvalMode_UndefOk(VarEvalMode emode)
1255*6eef5f0cSAntonio Huete Jimenez {
1256*6eef5f0cSAntonio Huete Jimenez return emode == VARE_UNDEFERR ? VARE_WANTRES : emode;
1257*6eef5f0cSAntonio Huete Jimenez }
1258*6eef5f0cSAntonio Huete Jimenez
1259*6eef5f0cSAntonio Huete Jimenez static bool
VarEvalMode_ShouldEval(VarEvalMode emode)1260*6eef5f0cSAntonio Huete Jimenez VarEvalMode_ShouldEval(VarEvalMode emode)
1261*6eef5f0cSAntonio Huete Jimenez {
1262*6eef5f0cSAntonio Huete Jimenez return emode != VARE_PARSE_ONLY;
1263*6eef5f0cSAntonio Huete Jimenez }
1264*6eef5f0cSAntonio Huete Jimenez
1265*6eef5f0cSAntonio Huete Jimenez static bool
VarEvalMode_ShouldKeepUndef(VarEvalMode emode)1266*6eef5f0cSAntonio Huete Jimenez VarEvalMode_ShouldKeepUndef(VarEvalMode emode)
1267*6eef5f0cSAntonio Huete Jimenez {
1268*6eef5f0cSAntonio Huete Jimenez return emode == VARE_EVAL_KEEP_UNDEF ||
1269*6eef5f0cSAntonio Huete Jimenez emode == VARE_KEEP_DOLLAR_UNDEF;
1270*6eef5f0cSAntonio Huete Jimenez }
1271*6eef5f0cSAntonio Huete Jimenez
1272*6eef5f0cSAntonio Huete Jimenez static bool
VarEvalMode_ShouldKeepDollar(VarEvalMode emode)1273*6eef5f0cSAntonio Huete Jimenez VarEvalMode_ShouldKeepDollar(VarEvalMode emode)
1274*6eef5f0cSAntonio Huete Jimenez {
1275*6eef5f0cSAntonio Huete Jimenez return emode == VARE_EVAL_KEEP_DOLLAR ||
1276*6eef5f0cSAntonio Huete Jimenez emode == VARE_KEEP_DOLLAR_UNDEF;
1277*6eef5f0cSAntonio Huete Jimenez }
1278*6eef5f0cSAntonio Huete Jimenez
1279ca58f742SDaniel Fojt
1280a34d5fb1SAntonio Huete Jimenez static void
SepBuf_Init(SepBuf * buf,char sep)1281a34d5fb1SAntonio Huete Jimenez SepBuf_Init(SepBuf *buf, char sep)
1282a34d5fb1SAntonio Huete Jimenez {
1283a34d5fb1SAntonio Huete Jimenez Buf_InitSize(&buf->buf, 32);
1284*6eef5f0cSAntonio Huete Jimenez buf->needSep = false;
1285a34d5fb1SAntonio Huete Jimenez buf->sep = sep;
1286a34d5fb1SAntonio Huete Jimenez }
1287a34d5fb1SAntonio Huete Jimenez
1288a34d5fb1SAntonio Huete Jimenez static void
SepBuf_Sep(SepBuf * buf)1289a34d5fb1SAntonio Huete Jimenez SepBuf_Sep(SepBuf *buf)
1290a34d5fb1SAntonio Huete Jimenez {
1291*6eef5f0cSAntonio Huete Jimenez buf->needSep = true;
1292a34d5fb1SAntonio Huete Jimenez }
1293a34d5fb1SAntonio Huete Jimenez
1294a34d5fb1SAntonio Huete Jimenez static void
SepBuf_AddBytes(SepBuf * buf,const char * mem,size_t mem_size)1295a34d5fb1SAntonio Huete Jimenez SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size)
1296a34d5fb1SAntonio Huete Jimenez {
1297a34d5fb1SAntonio Huete Jimenez if (mem_size == 0)
1298a34d5fb1SAntonio Huete Jimenez return;
1299a34d5fb1SAntonio Huete Jimenez if (buf->needSep && buf->sep != '\0') {
1300a34d5fb1SAntonio Huete Jimenez Buf_AddByte(&buf->buf, buf->sep);
1301*6eef5f0cSAntonio Huete Jimenez buf->needSep = false;
1302a34d5fb1SAntonio Huete Jimenez }
1303a34d5fb1SAntonio Huete Jimenez Buf_AddBytes(&buf->buf, mem, mem_size);
1304a34d5fb1SAntonio Huete Jimenez }
1305a34d5fb1SAntonio Huete Jimenez
1306a34d5fb1SAntonio Huete Jimenez static void
SepBuf_AddBytesBetween(SepBuf * buf,const char * start,const char * end)1307a34d5fb1SAntonio Huete Jimenez SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end)
1308a34d5fb1SAntonio Huete Jimenez {
1309a34d5fb1SAntonio Huete Jimenez SepBuf_AddBytes(buf, start, (size_t)(end - start));
1310a34d5fb1SAntonio Huete Jimenez }
1311a34d5fb1SAntonio Huete Jimenez
1312a34d5fb1SAntonio Huete Jimenez static void
SepBuf_AddStr(SepBuf * buf,const char * str)1313a34d5fb1SAntonio Huete Jimenez SepBuf_AddStr(SepBuf *buf, const char *str)
1314a34d5fb1SAntonio Huete Jimenez {
1315a34d5fb1SAntonio Huete Jimenez SepBuf_AddBytes(buf, str, strlen(str));
1316a34d5fb1SAntonio Huete Jimenez }
1317a34d5fb1SAntonio Huete Jimenez
1318*6eef5f0cSAntonio Huete Jimenez static void
SepBuf_AddSubstring(SepBuf * buf,Substring sub)1319*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(SepBuf *buf, Substring sub)
1320*6eef5f0cSAntonio Huete Jimenez {
1321*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytesBetween(buf, sub.start, sub.end);
1322*6eef5f0cSAntonio Huete Jimenez }
1323*6eef5f0cSAntonio Huete Jimenez
1324a34d5fb1SAntonio Huete Jimenez static char *
SepBuf_DoneData(SepBuf * buf)1325a34d5fb1SAntonio Huete Jimenez SepBuf_DoneData(SepBuf *buf)
1326a34d5fb1SAntonio Huete Jimenez {
1327a34d5fb1SAntonio Huete Jimenez return Buf_DoneData(&buf->buf);
1328a34d5fb1SAntonio Huete Jimenez }
1329ca58f742SDaniel Fojt
1330ca58f742SDaniel Fojt
1331a34d5fb1SAntonio Huete Jimenez /*
1332a34d5fb1SAntonio Huete Jimenez * This callback for ModifyWords gets a single word from a variable expression
1333a34d5fb1SAntonio Huete Jimenez * and typically adds a modification of this word to the buffer. It may also
1334a34d5fb1SAntonio Huete Jimenez * do nothing or add several words.
1335a34d5fb1SAntonio Huete Jimenez *
1336ec533708SSascha Wildner * For example, when evaluating the modifier ':M*b' in ${:Ua b c:M*b}, the
1337ec533708SSascha Wildner * callback is called 3 times, once for "a", "b" and "c".
1338*6eef5f0cSAntonio Huete Jimenez *
1339*6eef5f0cSAntonio Huete Jimenez * Some ModifyWord functions assume that they are always passed a
1340*6eef5f0cSAntonio Huete Jimenez * null-terminated substring, which is currently guaranteed but may change in
1341*6eef5f0cSAntonio Huete Jimenez * the future.
1342a34d5fb1SAntonio Huete Jimenez */
1343*6eef5f0cSAntonio Huete Jimenez typedef void (*ModifyWordProc)(Substring word, SepBuf *buf, void *data);
1344a34d5fb1SAntonio Huete Jimenez
1345a34d5fb1SAntonio Huete Jimenez
1346a34d5fb1SAntonio Huete Jimenez /*
1347a34d5fb1SAntonio Huete Jimenez * Callback for ModifyWords to implement the :H modifier.
1348a34d5fb1SAntonio Huete Jimenez * Add the dirname of the given word to the buffer.
1349a34d5fb1SAntonio Huete Jimenez */
1350a34d5fb1SAntonio Huete Jimenez /*ARGSUSED*/
1351a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_Head(Substring word,SepBuf * buf,void * dummy MAKE_ATTR_UNUSED)1352*6eef5f0cSAntonio Huete Jimenez ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
135301e196c8SJohn Marino {
1354*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(buf, Substring_Dirname(word));
135501e196c8SJohn Marino }
135601e196c8SJohn Marino
1357a34d5fb1SAntonio Huete Jimenez /*
1358a34d5fb1SAntonio Huete Jimenez * Callback for ModifyWords to implement the :T modifier.
1359a34d5fb1SAntonio Huete Jimenez * Add the basename of the given word to the buffer.
1360a34d5fb1SAntonio Huete Jimenez */
1361a34d5fb1SAntonio Huete Jimenez /*ARGSUSED*/
1362a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_Tail(Substring word,SepBuf * buf,void * dummy MAKE_ATTR_UNUSED)1363*6eef5f0cSAntonio Huete Jimenez ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
136401e196c8SJohn Marino {
1365*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(buf, Substring_Basename(word));
136601e196c8SJohn Marino }
136701e196c8SJohn Marino
1368a34d5fb1SAntonio Huete Jimenez /*
1369a34d5fb1SAntonio Huete Jimenez * Callback for ModifyWords to implement the :E modifier.
1370a34d5fb1SAntonio Huete Jimenez * Add the filename suffix of the given word to the buffer, if it exists.
1371a34d5fb1SAntonio Huete Jimenez */
1372a34d5fb1SAntonio Huete Jimenez /*ARGSUSED*/
1373a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_Suffix(Substring word,SepBuf * buf,void * dummy MAKE_ATTR_UNUSED)1374*6eef5f0cSAntonio Huete Jimenez ModifyWord_Suffix(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
137501e196c8SJohn Marino {
1376*6eef5f0cSAntonio Huete Jimenez const char *lastDot = Substring_LastIndex(word, '.');
1377a34d5fb1SAntonio Huete Jimenez if (lastDot != NULL)
1378*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytesBetween(buf, lastDot + 1, word.end);
137901e196c8SJohn Marino }
138001e196c8SJohn Marino
1381a34d5fb1SAntonio Huete Jimenez /*
1382a34d5fb1SAntonio Huete Jimenez * Callback for ModifyWords to implement the :R modifier.
1383ec533708SSascha Wildner * Add the filename without extension of the given word to the buffer.
1384a34d5fb1SAntonio Huete Jimenez */
1385a34d5fb1SAntonio Huete Jimenez /*ARGSUSED*/
1386a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_Root(Substring word,SepBuf * buf,void * dummy MAKE_ATTR_UNUSED)1387*6eef5f0cSAntonio Huete Jimenez ModifyWord_Root(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
138801e196c8SJohn Marino {
1389*6eef5f0cSAntonio Huete Jimenez const char *lastDot, *end;
1390*6eef5f0cSAntonio Huete Jimenez
1391*6eef5f0cSAntonio Huete Jimenez lastDot = Substring_LastIndex(word, '.');
1392*6eef5f0cSAntonio Huete Jimenez end = lastDot != NULL ? lastDot : word.end;
1393*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytesBetween(buf, word.start, end);
139401e196c8SJohn Marino }
139501e196c8SJohn Marino
1396a34d5fb1SAntonio Huete Jimenez /*
1397a34d5fb1SAntonio Huete Jimenez * Callback for ModifyWords to implement the :M modifier.
1398a34d5fb1SAntonio Huete Jimenez * Place the word in the buffer if it matches the given pattern.
1399a34d5fb1SAntonio Huete Jimenez */
1400a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_Match(Substring word,SepBuf * buf,void * data)1401*6eef5f0cSAntonio Huete Jimenez ModifyWord_Match(Substring word, SepBuf *buf, void *data)
140201e196c8SJohn Marino {
1403ca58f742SDaniel Fojt const char *pattern = data;
1404*6eef5f0cSAntonio Huete Jimenez
1405*6eef5f0cSAntonio Huete Jimenez assert(word.end[0] == '\0'); /* assume null-terminated word */
1406*6eef5f0cSAntonio Huete Jimenez if (Str_Match(word.start, pattern))
1407*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(buf, word);
1408a34d5fb1SAntonio Huete Jimenez }
1409a34d5fb1SAntonio Huete Jimenez
1410a34d5fb1SAntonio Huete Jimenez /*
1411a34d5fb1SAntonio Huete Jimenez * Callback for ModifyWords to implement the :N modifier.
1412a34d5fb1SAntonio Huete Jimenez * Place the word in the buffer if it doesn't match the given pattern.
1413a34d5fb1SAntonio Huete Jimenez */
1414a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_NoMatch(Substring word,SepBuf * buf,void * data)1415*6eef5f0cSAntonio Huete Jimenez ModifyWord_NoMatch(Substring word, SepBuf *buf, void *data)
1416a34d5fb1SAntonio Huete Jimenez {
1417a34d5fb1SAntonio Huete Jimenez const char *pattern = data;
1418*6eef5f0cSAntonio Huete Jimenez
1419*6eef5f0cSAntonio Huete Jimenez assert(word.end[0] == '\0'); /* assume null-terminated word */
1420*6eef5f0cSAntonio Huete Jimenez if (!Str_Match(word.start, pattern))
1421*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(buf, word);
142201e196c8SJohn Marino }
142301e196c8SJohn Marino
142401e196c8SJohn Marino #ifdef SYSVVARSUB
1425*6eef5f0cSAntonio Huete Jimenez struct ModifyWord_SysVSubstArgs {
1426a34d5fb1SAntonio Huete Jimenez GNode *scope;
1427*6eef5f0cSAntonio Huete Jimenez Substring lhsPrefix;
1428*6eef5f0cSAntonio Huete Jimenez bool lhsPercent;
1429*6eef5f0cSAntonio Huete Jimenez Substring lhsSuffix;
1430a34d5fb1SAntonio Huete Jimenez const char *rhs;
1431a34d5fb1SAntonio Huete Jimenez };
1432a34d5fb1SAntonio Huete Jimenez
1433a34d5fb1SAntonio Huete Jimenez /* Callback for ModifyWords to implement the :%.from=%.to modifier. */
1434a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_SysVSubst(Substring word,SepBuf * buf,void * data)1435*6eef5f0cSAntonio Huete Jimenez ModifyWord_SysVSubst(Substring word, SepBuf *buf, void *data)
1436a34d5fb1SAntonio Huete Jimenez {
1437*6eef5f0cSAntonio Huete Jimenez const struct ModifyWord_SysVSubstArgs *args = data;
1438*6eef5f0cSAntonio Huete Jimenez FStr rhs;
1439a34d5fb1SAntonio Huete Jimenez const char *percent;
1440a34d5fb1SAntonio Huete Jimenez
1441*6eef5f0cSAntonio Huete Jimenez if (Substring_IsEmpty(word))
1442*6eef5f0cSAntonio Huete Jimenez return;
1443*6eef5f0cSAntonio Huete Jimenez
1444*6eef5f0cSAntonio Huete Jimenez if (!Substring_HasPrefix(word, args->lhsPrefix) ||
1445*6eef5f0cSAntonio Huete Jimenez !Substring_HasSuffix(word, args->lhsSuffix)) {
1446*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(buf, word);
1447a34d5fb1SAntonio Huete Jimenez return;
1448a34d5fb1SAntonio Huete Jimenez }
1449a34d5fb1SAntonio Huete Jimenez
1450*6eef5f0cSAntonio Huete Jimenez rhs = FStr_InitRefer(args->rhs);
1451*6eef5f0cSAntonio Huete Jimenez Var_Expand(&rhs, args->scope, VARE_WANTRES);
1452a34d5fb1SAntonio Huete Jimenez
1453*6eef5f0cSAntonio Huete Jimenez percent = args->lhsPercent ? strchr(rhs.str, '%') : NULL;
1454a34d5fb1SAntonio Huete Jimenez
1455*6eef5f0cSAntonio Huete Jimenez if (percent != NULL)
1456*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytesBetween(buf, rhs.str, percent);
1457*6eef5f0cSAntonio Huete Jimenez if (percent != NULL || !args->lhsPercent)
1458*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytesBetween(buf,
1459*6eef5f0cSAntonio Huete Jimenez word.start + Substring_Length(args->lhsPrefix),
1460*6eef5f0cSAntonio Huete Jimenez word.end - Substring_Length(args->lhsSuffix));
1461*6eef5f0cSAntonio Huete Jimenez SepBuf_AddStr(buf, percent != NULL ? percent + 1 : rhs.str);
1462a34d5fb1SAntonio Huete Jimenez
1463*6eef5f0cSAntonio Huete Jimenez FStr_Done(&rhs);
146401e196c8SJohn Marino }
146501e196c8SJohn Marino #endif
146601e196c8SJohn Marino
1467a34d5fb1SAntonio Huete Jimenez
1468a34d5fb1SAntonio Huete Jimenez struct ModifyWord_SubstArgs {
1469*6eef5f0cSAntonio Huete Jimenez Substring lhs;
1470*6eef5f0cSAntonio Huete Jimenez Substring rhs;
1471*6eef5f0cSAntonio Huete Jimenez PatternFlags pflags;
1472*6eef5f0cSAntonio Huete Jimenez bool matched;
1473a34d5fb1SAntonio Huete Jimenez };
1474a34d5fb1SAntonio Huete Jimenez
1475*6eef5f0cSAntonio Huete Jimenez static const char *
Substring_Find(Substring haystack,Substring needle)1476*6eef5f0cSAntonio Huete Jimenez Substring_Find(Substring haystack, Substring needle)
1477*6eef5f0cSAntonio Huete Jimenez {
1478*6eef5f0cSAntonio Huete Jimenez size_t len, needleLen, i;
1479*6eef5f0cSAntonio Huete Jimenez
1480*6eef5f0cSAntonio Huete Jimenez len = Substring_Length(haystack);
1481*6eef5f0cSAntonio Huete Jimenez needleLen = Substring_Length(needle);
1482*6eef5f0cSAntonio Huete Jimenez for (i = 0; i + needleLen <= len; i++)
1483*6eef5f0cSAntonio Huete Jimenez if (memcmp(haystack.start + i, needle.start, needleLen) == 0)
1484*6eef5f0cSAntonio Huete Jimenez return haystack.start + i;
1485*6eef5f0cSAntonio Huete Jimenez return NULL;
1486*6eef5f0cSAntonio Huete Jimenez }
1487*6eef5f0cSAntonio Huete Jimenez
1488a34d5fb1SAntonio Huete Jimenez /*
1489a34d5fb1SAntonio Huete Jimenez * Callback for ModifyWords to implement the :S,from,to, modifier.
1490a34d5fb1SAntonio Huete Jimenez * Perform a string substitution on the given word.
1491a34d5fb1SAntonio Huete Jimenez */
1492a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_Subst(Substring word,SepBuf * buf,void * data)1493*6eef5f0cSAntonio Huete Jimenez ModifyWord_Subst(Substring word, SepBuf *buf, void *data)
149401e196c8SJohn Marino {
1495a34d5fb1SAntonio Huete Jimenez struct ModifyWord_SubstArgs *args = data;
1496*6eef5f0cSAntonio Huete Jimenez size_t wordLen, lhsLen;
1497*6eef5f0cSAntonio Huete Jimenez const char *wordEnd, *match;
1498a34d5fb1SAntonio Huete Jimenez
1499*6eef5f0cSAntonio Huete Jimenez wordLen = Substring_Length(word);
1500*6eef5f0cSAntonio Huete Jimenez wordEnd = word.end;
1501a34d5fb1SAntonio Huete Jimenez if (args->pflags.subOnce && args->matched)
1502a34d5fb1SAntonio Huete Jimenez goto nosub;
1503a34d5fb1SAntonio Huete Jimenez
1504*6eef5f0cSAntonio Huete Jimenez lhsLen = Substring_Length(args->lhs);
1505a34d5fb1SAntonio Huete Jimenez if (args->pflags.anchorStart) {
1506*6eef5f0cSAntonio Huete Jimenez if (wordLen < lhsLen ||
1507*6eef5f0cSAntonio Huete Jimenez memcmp(word.start, args->lhs.start, lhsLen) != 0)
1508a34d5fb1SAntonio Huete Jimenez goto nosub;
1509a34d5fb1SAntonio Huete Jimenez
1510*6eef5f0cSAntonio Huete Jimenez if (args->pflags.anchorEnd && wordLen != lhsLen)
1511a34d5fb1SAntonio Huete Jimenez goto nosub;
1512a34d5fb1SAntonio Huete Jimenez
1513a34d5fb1SAntonio Huete Jimenez /* :S,^prefix,replacement, or :S,^whole$,replacement, */
1514*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(buf, args->rhs);
1515*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytesBetween(buf, word.start + lhsLen, wordEnd);
1516*6eef5f0cSAntonio Huete Jimenez args->matched = true;
1517a34d5fb1SAntonio Huete Jimenez return;
151801e196c8SJohn Marino }
151901e196c8SJohn Marino
1520a34d5fb1SAntonio Huete Jimenez if (args->pflags.anchorEnd) {
1521*6eef5f0cSAntonio Huete Jimenez if (wordLen < lhsLen)
152201e196c8SJohn Marino goto nosub;
1523*6eef5f0cSAntonio Huete Jimenez if (memcmp(wordEnd - lhsLen, args->lhs.start, lhsLen) != 0)
1524a34d5fb1SAntonio Huete Jimenez goto nosub;
1525a34d5fb1SAntonio Huete Jimenez
1526a34d5fb1SAntonio Huete Jimenez /* :S,suffix$,replacement, */
1527*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytesBetween(buf, word.start, wordEnd - lhsLen);
1528*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(buf, args->rhs);
1529*6eef5f0cSAntonio Huete Jimenez args->matched = true;
1530a34d5fb1SAntonio Huete Jimenez return;
153101e196c8SJohn Marino }
1532a34d5fb1SAntonio Huete Jimenez
1533*6eef5f0cSAntonio Huete Jimenez if (Substring_IsEmpty(args->lhs))
1534a34d5fb1SAntonio Huete Jimenez goto nosub;
1535a34d5fb1SAntonio Huete Jimenez
1536a34d5fb1SAntonio Huete Jimenez /* unanchored case, may match more than once */
1537*6eef5f0cSAntonio Huete Jimenez while ((match = Substring_Find(word, args->lhs)) != NULL) {
1538*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytesBetween(buf, word.start, match);
1539*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(buf, args->rhs);
1540*6eef5f0cSAntonio Huete Jimenez args->matched = true;
1541*6eef5f0cSAntonio Huete Jimenez word.start = match + lhsLen;
1542*6eef5f0cSAntonio Huete Jimenez if (Substring_IsEmpty(word) || !args->pflags.subGlobal)
1543a34d5fb1SAntonio Huete Jimenez break;
154401e196c8SJohn Marino }
154501e196c8SJohn Marino nosub:
1546*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(buf, word);
154701e196c8SJohn Marino }
154801e196c8SJohn Marino
154901e196c8SJohn Marino #ifndef NO_REGEX
1550a34d5fb1SAntonio Huete Jimenez /* Print the error caused by a regcomp or regexec call. */
155101e196c8SJohn Marino static void
VarREError(int reerr,const regex_t * pat,const char * str)1552a34d5fb1SAntonio Huete Jimenez VarREError(int reerr, const regex_t *pat, const char *str)
155301e196c8SJohn Marino {
1554a34d5fb1SAntonio Huete Jimenez size_t errlen = regerror(reerr, pat, NULL, 0);
1555a34d5fb1SAntonio Huete Jimenez char *errbuf = bmake_malloc(errlen);
1556f445c897SJohn Marino regerror(reerr, pat, errbuf, errlen);
155701e196c8SJohn Marino Error("%s: %s", str, errbuf);
155801e196c8SJohn Marino free(errbuf);
155901e196c8SJohn Marino }
156001e196c8SJohn Marino
1561*6eef5f0cSAntonio Huete Jimenez /* In the modifier ':C', replace a backreference from \0 to \9. */
1562*6eef5f0cSAntonio Huete Jimenez static void
RegexReplaceBackref(char ref,SepBuf * buf,const char * wp,const regmatch_t * m,size_t nsub)1563*6eef5f0cSAntonio Huete Jimenez RegexReplaceBackref(char ref, SepBuf *buf, const char *wp,
1564*6eef5f0cSAntonio Huete Jimenez const regmatch_t *m, size_t nsub)
1565*6eef5f0cSAntonio Huete Jimenez {
1566*6eef5f0cSAntonio Huete Jimenez unsigned int n = (unsigned)ref - '0';
1567*6eef5f0cSAntonio Huete Jimenez
1568*6eef5f0cSAntonio Huete Jimenez if (n >= nsub)
1569*6eef5f0cSAntonio Huete Jimenez Error("No subexpression \\%u", n);
1570*6eef5f0cSAntonio Huete Jimenez else if (m[n].rm_so == -1) {
1571*6eef5f0cSAntonio Huete Jimenez if (opts.strict)
1572*6eef5f0cSAntonio Huete Jimenez Error("No match for subexpression \\%u", n);
1573*6eef5f0cSAntonio Huete Jimenez } else {
1574*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytesBetween(buf,
1575*6eef5f0cSAntonio Huete Jimenez wp + (size_t)m[n].rm_so,
1576*6eef5f0cSAntonio Huete Jimenez wp + (size_t)m[n].rm_eo);
1577*6eef5f0cSAntonio Huete Jimenez }
1578*6eef5f0cSAntonio Huete Jimenez }
1579*6eef5f0cSAntonio Huete Jimenez
1580*6eef5f0cSAntonio Huete Jimenez /*
1581*6eef5f0cSAntonio Huete Jimenez * The regular expression matches the word; now add the replacement to the
1582*6eef5f0cSAntonio Huete Jimenez * buffer, taking back-references from 'wp'.
1583*6eef5f0cSAntonio Huete Jimenez */
1584*6eef5f0cSAntonio Huete Jimenez static void
RegexReplace(Substring replace,SepBuf * buf,const char * wp,const regmatch_t * m,size_t nsub)1585*6eef5f0cSAntonio Huete Jimenez RegexReplace(Substring replace, SepBuf *buf, const char *wp,
1586*6eef5f0cSAntonio Huete Jimenez const regmatch_t *m, size_t nsub)
1587*6eef5f0cSAntonio Huete Jimenez {
1588*6eef5f0cSAntonio Huete Jimenez const char *rp;
1589*6eef5f0cSAntonio Huete Jimenez
1590*6eef5f0cSAntonio Huete Jimenez for (rp = replace.start; rp != replace.end; rp++) {
1591*6eef5f0cSAntonio Huete Jimenez if (*rp == '\\' && rp + 1 != replace.end &&
1592*6eef5f0cSAntonio Huete Jimenez (rp[1] == '&' || rp[1] == '\\'))
1593*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytes(buf, ++rp, 1);
1594*6eef5f0cSAntonio Huete Jimenez else if (*rp == '\\' && rp + 1 != replace.end &&
1595*6eef5f0cSAntonio Huete Jimenez ch_isdigit(rp[1]))
1596*6eef5f0cSAntonio Huete Jimenez RegexReplaceBackref(*++rp, buf, wp, m, nsub);
1597*6eef5f0cSAntonio Huete Jimenez else if (*rp == '&') {
1598*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytesBetween(buf,
1599*6eef5f0cSAntonio Huete Jimenez wp + (size_t)m[0].rm_so,
1600*6eef5f0cSAntonio Huete Jimenez wp + (size_t)m[0].rm_eo);
1601*6eef5f0cSAntonio Huete Jimenez } else
1602*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytes(buf, rp, 1);
1603*6eef5f0cSAntonio Huete Jimenez }
1604*6eef5f0cSAntonio Huete Jimenez }
1605*6eef5f0cSAntonio Huete Jimenez
1606a34d5fb1SAntonio Huete Jimenez struct ModifyWord_SubstRegexArgs {
1607a34d5fb1SAntonio Huete Jimenez regex_t re;
1608a34d5fb1SAntonio Huete Jimenez size_t nsub;
1609*6eef5f0cSAntonio Huete Jimenez Substring replace;
1610*6eef5f0cSAntonio Huete Jimenez PatternFlags pflags;
1611*6eef5f0cSAntonio Huete Jimenez bool matched;
1612a34d5fb1SAntonio Huete Jimenez };
1613a34d5fb1SAntonio Huete Jimenez
1614a34d5fb1SAntonio Huete Jimenez /*
1615a34d5fb1SAntonio Huete Jimenez * Callback for ModifyWords to implement the :C/from/to/ modifier.
1616a34d5fb1SAntonio Huete Jimenez * Perform a regex substitution on the given word.
1617a34d5fb1SAntonio Huete Jimenez */
1618a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_SubstRegex(Substring word,SepBuf * buf,void * data)1619*6eef5f0cSAntonio Huete Jimenez ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data)
162001e196c8SJohn Marino {
1621a34d5fb1SAntonio Huete Jimenez struct ModifyWord_SubstRegexArgs *args = data;
162201e196c8SJohn Marino int xrv;
1623*6eef5f0cSAntonio Huete Jimenez const char *wp;
162401e196c8SJohn Marino int flags = 0;
1625a34d5fb1SAntonio Huete Jimenez regmatch_t m[10];
162601e196c8SJohn Marino
1627*6eef5f0cSAntonio Huete Jimenez assert(word.end[0] == '\0'); /* assume null-terminated word */
1628*6eef5f0cSAntonio Huete Jimenez wp = word.start;
1629a34d5fb1SAntonio Huete Jimenez if (args->pflags.subOnce && args->matched)
1630*6eef5f0cSAntonio Huete Jimenez goto no_match;
163101e196c8SJohn Marino
1632*6eef5f0cSAntonio Huete Jimenez again:
1633a34d5fb1SAntonio Huete Jimenez xrv = regexec(&args->re, wp, args->nsub, m, flags);
1634*6eef5f0cSAntonio Huete Jimenez if (xrv == 0)
1635*6eef5f0cSAntonio Huete Jimenez goto ok;
1636*6eef5f0cSAntonio Huete Jimenez if (xrv != REG_NOMATCH)
1637*6eef5f0cSAntonio Huete Jimenez VarREError(xrv, &args->re, "Unexpected regex error");
1638*6eef5f0cSAntonio Huete Jimenez no_match:
1639*6eef5f0cSAntonio Huete Jimenez SepBuf_AddBytesBetween(buf, wp, word.end);
1640*6eef5f0cSAntonio Huete Jimenez return;
164101e196c8SJohn Marino
1642*6eef5f0cSAntonio Huete Jimenez ok:
1643*6eef5f0cSAntonio Huete Jimenez args->matched = true;
1644a34d5fb1SAntonio Huete Jimenez SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so);
164501e196c8SJohn Marino
1646*6eef5f0cSAntonio Huete Jimenez RegexReplace(args->replace, buf, wp, m, args->nsub);
1647ec533708SSascha Wildner
1648*6eef5f0cSAntonio Huete Jimenez wp += (size_t)m[0].rm_eo;
1649a34d5fb1SAntonio Huete Jimenez if (args->pflags.subGlobal) {
165001e196c8SJohn Marino flags |= REG_NOTBOL;
1651a34d5fb1SAntonio Huete Jimenez if (m[0].rm_so == 0 && m[0].rm_eo == 0) {
1652a34d5fb1SAntonio Huete Jimenez SepBuf_AddBytes(buf, wp, 1);
165301e196c8SJohn Marino wp++;
165401e196c8SJohn Marino }
1655a34d5fb1SAntonio Huete Jimenez if (*wp != '\0')
1656*6eef5f0cSAntonio Huete Jimenez goto again;
165701e196c8SJohn Marino }
1658a34d5fb1SAntonio Huete Jimenez if (*wp != '\0')
1659a34d5fb1SAntonio Huete Jimenez SepBuf_AddStr(buf, wp);
166001e196c8SJohn Marino }
166101e196c8SJohn Marino #endif
166201e196c8SJohn Marino
166301e196c8SJohn Marino
1664a34d5fb1SAntonio Huete Jimenez struct ModifyWord_LoopArgs {
1665a34d5fb1SAntonio Huete Jimenez GNode *scope;
1666*6eef5f0cSAntonio Huete Jimenez const char *var; /* name of the temporary variable */
1667*6eef5f0cSAntonio Huete Jimenez const char *body; /* string to expand */
1668*6eef5f0cSAntonio Huete Jimenez VarEvalMode emode;
1669a34d5fb1SAntonio Huete Jimenez };
167001e196c8SJohn Marino
1671a34d5fb1SAntonio Huete Jimenez /* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */
1672a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_Loop(Substring word,SepBuf * buf,void * data)1673*6eef5f0cSAntonio Huete Jimenez ModifyWord_Loop(Substring word, SepBuf *buf, void *data)
1674a34d5fb1SAntonio Huete Jimenez {
1675a34d5fb1SAntonio Huete Jimenez const struct ModifyWord_LoopArgs *args;
1676a34d5fb1SAntonio Huete Jimenez char *s;
1677a34d5fb1SAntonio Huete Jimenez
1678*6eef5f0cSAntonio Huete Jimenez if (Substring_IsEmpty(word))
1679a34d5fb1SAntonio Huete Jimenez return;
1680a34d5fb1SAntonio Huete Jimenez
1681a34d5fb1SAntonio Huete Jimenez args = data;
1682*6eef5f0cSAntonio Huete Jimenez assert(word.end[0] == '\0'); /* assume null-terminated word */
1683*6eef5f0cSAntonio Huete Jimenez Var_SetWithFlags(args->scope, args->var, word.start,
1684a34d5fb1SAntonio Huete Jimenez VAR_SET_NO_EXPORT);
1685*6eef5f0cSAntonio Huete Jimenez (void)Var_Subst(args->body, args->scope, args->emode, &s);
1686a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
1687a34d5fb1SAntonio Huete Jimenez
1688*6eef5f0cSAntonio Huete Jimenez assert(word.end[0] == '\0'); /* assume null-terminated word */
1689a34d5fb1SAntonio Huete Jimenez DEBUG4(VAR, "ModifyWord_Loop: "
1690a34d5fb1SAntonio Huete Jimenez "in \"%s\", replace \"%s\" with \"%s\" to \"%s\"\n",
1691*6eef5f0cSAntonio Huete Jimenez word.start, args->var, args->body, s);
1692a34d5fb1SAntonio Huete Jimenez
1693a34d5fb1SAntonio Huete Jimenez if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n'))
1694*6eef5f0cSAntonio Huete Jimenez buf->needSep = false;
1695a34d5fb1SAntonio Huete Jimenez SepBuf_AddStr(buf, s);
1696ca58f742SDaniel Fojt free(s);
169701e196c8SJohn Marino }
169801e196c8SJohn Marino
169901e196c8SJohn Marino
1700a34d5fb1SAntonio Huete Jimenez /*
1701a34d5fb1SAntonio Huete Jimenez * The :[first..last] modifier selects words from the expression.
1702a34d5fb1SAntonio Huete Jimenez * It can also reverse the words.
170301e196c8SJohn Marino */
170401e196c8SJohn Marino static char *
VarSelectWords(const char * str,int first,int last,char sep,bool oneBigWord)1705ec533708SSascha Wildner VarSelectWords(const char *str, int first, int last,
1706*6eef5f0cSAntonio Huete Jimenez char sep, bool oneBigWord)
170701e196c8SJohn Marino {
1708*6eef5f0cSAntonio Huete Jimenez SubstringWords words;
1709a34d5fb1SAntonio Huete Jimenez int len, start, end, step;
1710a34d5fb1SAntonio Huete Jimenez int i;
171101e196c8SJohn Marino
1712a34d5fb1SAntonio Huete Jimenez SepBuf buf;
1713a34d5fb1SAntonio Huete Jimenez SepBuf_Init(&buf, sep);
171401e196c8SJohn Marino
1715a34d5fb1SAntonio Huete Jimenez if (oneBigWord) {
1716*6eef5f0cSAntonio Huete Jimenez /* fake what Substring_Words() would do */
1717a34d5fb1SAntonio Huete Jimenez words.len = 1;
1718*6eef5f0cSAntonio Huete Jimenez words.words = bmake_malloc(sizeof(words.words[0]));
1719*6eef5f0cSAntonio Huete Jimenez words.freeIt = NULL;
1720*6eef5f0cSAntonio Huete Jimenez words.words[0] = Substring_InitStr(str); /* no need to copy */
172101e196c8SJohn Marino } else {
1722*6eef5f0cSAntonio Huete Jimenez words = Substring_Words(str, false);
172301e196c8SJohn Marino }
172401e196c8SJohn Marino
172501e196c8SJohn Marino /*
1726a34d5fb1SAntonio Huete Jimenez * Now sanitize the given range. If first or last are negative,
1727a34d5fb1SAntonio Huete Jimenez * convert them to the positive equivalents (-1 gets converted to len,
1728a34d5fb1SAntonio Huete Jimenez * -2 gets converted to (len - 1), etc.).
172901e196c8SJohn Marino */
1730a34d5fb1SAntonio Huete Jimenez len = (int)words.len;
1731a34d5fb1SAntonio Huete Jimenez if (first < 0)
1732a34d5fb1SAntonio Huete Jimenez first += len + 1;
1733a34d5fb1SAntonio Huete Jimenez if (last < 0)
1734a34d5fb1SAntonio Huete Jimenez last += len + 1;
173501e196c8SJohn Marino
1736a34d5fb1SAntonio Huete Jimenez /* We avoid scanning more of the list than we need to. */
1737a34d5fb1SAntonio Huete Jimenez if (first > last) {
1738a34d5fb1SAntonio Huete Jimenez start = (first > len ? len : first) - 1;
1739a34d5fb1SAntonio Huete Jimenez end = last < 1 ? 0 : last - 1;
174001e196c8SJohn Marino step = -1;
174101e196c8SJohn Marino } else {
1742a34d5fb1SAntonio Huete Jimenez start = first < 1 ? 0 : first - 1;
1743a34d5fb1SAntonio Huete Jimenez end = last > len ? len : last;
174401e196c8SJohn Marino step = 1;
174501e196c8SJohn Marino }
174601e196c8SJohn Marino
1747a34d5fb1SAntonio Huete Jimenez for (i = start; (step < 0) == (i >= end); i += step) {
1748*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(&buf, words.words[i]);
1749a34d5fb1SAntonio Huete Jimenez SepBuf_Sep(&buf);
175001e196c8SJohn Marino }
175101e196c8SJohn Marino
1752*6eef5f0cSAntonio Huete Jimenez SubstringWords_Free(words);
175301e196c8SJohn Marino
1754a34d5fb1SAntonio Huete Jimenez return SepBuf_DoneData(&buf);
175501e196c8SJohn Marino }
175601e196c8SJohn Marino
175701e196c8SJohn Marino
1758a34d5fb1SAntonio Huete Jimenez /*
1759a34d5fb1SAntonio Huete Jimenez * Callback for ModifyWords to implement the :tA modifier.
1760a34d5fb1SAntonio Huete Jimenez * Replace each word with the result of realpath() if successful.
1761a34d5fb1SAntonio Huete Jimenez */
1762a34d5fb1SAntonio Huete Jimenez /*ARGSUSED*/
1763a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_Realpath(Substring word,SepBuf * buf,void * data MAKE_ATTR_UNUSED)1764*6eef5f0cSAntonio Huete Jimenez ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
176501e196c8SJohn Marino {
176601e196c8SJohn Marino struct stat st;
176701e196c8SJohn Marino char rbuf[MAXPATHLEN];
1768*6eef5f0cSAntonio Huete Jimenez const char *rp;
176901e196c8SJohn Marino
1770*6eef5f0cSAntonio Huete Jimenez assert(word.end[0] == '\0'); /* assume null-terminated word */
1771*6eef5f0cSAntonio Huete Jimenez rp = cached_realpath(word.start, rbuf);
1772a34d5fb1SAntonio Huete Jimenez if (rp != NULL && *rp == '/' && stat(rp, &st) == 0)
1773*6eef5f0cSAntonio Huete Jimenez SepBuf_AddStr(buf, rp);
1774*6eef5f0cSAntonio Huete Jimenez else
1775*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(buf, word);
177601e196c8SJohn Marino }
177701e196c8SJohn Marino
177801e196c8SJohn Marino
177901e196c8SJohn Marino static char *
SubstringWords_JoinFree(SubstringWords words)1780*6eef5f0cSAntonio Huete Jimenez SubstringWords_JoinFree(SubstringWords words)
178101e196c8SJohn Marino {
1782a34d5fb1SAntonio Huete Jimenez Buffer buf;
1783a34d5fb1SAntonio Huete Jimenez size_t i;
178401e196c8SJohn Marino
1785a34d5fb1SAntonio Huete Jimenez Buf_Init(&buf);
178601e196c8SJohn Marino
1787a34d5fb1SAntonio Huete Jimenez for (i = 0; i < words.len; i++) {
1788a34d5fb1SAntonio Huete Jimenez if (i != 0) {
1789*6eef5f0cSAntonio Huete Jimenez /*
1790*6eef5f0cSAntonio Huete Jimenez * XXX: Use ch->sep instead of ' ', for consistency.
1791*6eef5f0cSAntonio Huete Jimenez */
179201e196c8SJohn Marino Buf_AddByte(&buf, ' ');
179301e196c8SJohn Marino }
1794*6eef5f0cSAntonio Huete Jimenez Buf_AddBytesBetween(&buf,
1795*6eef5f0cSAntonio Huete Jimenez words.words[i].start, words.words[i].end);
179601e196c8SJohn Marino }
179701e196c8SJohn Marino
1798*6eef5f0cSAntonio Huete Jimenez SubstringWords_Free(words);
179901e196c8SJohn Marino
1800a34d5fb1SAntonio Huete Jimenez return Buf_DoneData(&buf);
1801a34d5fb1SAntonio Huete Jimenez }
1802a34d5fb1SAntonio Huete Jimenez
180301e196c8SJohn Marino
1804a34d5fb1SAntonio Huete Jimenez /*
1805a34d5fb1SAntonio Huete Jimenez * Quote shell meta-characters and space characters in the string.
1806a34d5fb1SAntonio Huete Jimenez * If quoteDollar is set, also quote and double any '$' characters.
1807ca58f742SDaniel Fojt */
1808*6eef5f0cSAntonio Huete Jimenez static void
VarQuote(const char * str,bool quoteDollar,LazyBuf * buf)1809*6eef5f0cSAntonio Huete Jimenez VarQuote(const char *str, bool quoteDollar, LazyBuf *buf)
1810ca58f742SDaniel Fojt {
1811*6eef5f0cSAntonio Huete Jimenez const char *p;
1812f445c897SJohn Marino
1813*6eef5f0cSAntonio Huete Jimenez LazyBuf_Init(buf, str);
1814*6eef5f0cSAntonio Huete Jimenez for (p = str; *p != '\0'; p++) {
1815*6eef5f0cSAntonio Huete Jimenez if (*p == '\n') {
1816a34d5fb1SAntonio Huete Jimenez const char *newline = Shell_GetNewline();
1817a34d5fb1SAntonio Huete Jimenez if (newline == NULL)
1818a34d5fb1SAntonio Huete Jimenez newline = "\\\n";
1819*6eef5f0cSAntonio Huete Jimenez LazyBuf_AddStr(buf, newline);
1820f445c897SJohn Marino continue;
1821f445c897SJohn Marino }
1822*6eef5f0cSAntonio Huete Jimenez if (ch_isspace(*p) || ch_is_shell_meta(*p))
1823*6eef5f0cSAntonio Huete Jimenez LazyBuf_Add(buf, '\\');
1824*6eef5f0cSAntonio Huete Jimenez LazyBuf_Add(buf, *p);
1825*6eef5f0cSAntonio Huete Jimenez if (quoteDollar && *p == '$')
1826*6eef5f0cSAntonio Huete Jimenez LazyBuf_AddStr(buf, "\\$");
182701e196c8SJohn Marino }
182801e196c8SJohn Marino }
182901e196c8SJohn Marino
1830a34d5fb1SAntonio Huete Jimenez /*
1831a34d5fb1SAntonio Huete Jimenez * Compute the 32-bit hash of the given string, using the MurmurHash3
1832a34d5fb1SAntonio Huete Jimenez * algorithm. Output is encoded as 8 hex digits, in Little Endian order.
183301e196c8SJohn Marino */
183401e196c8SJohn Marino static char *
VarHash(const char * str)1835ca58f742SDaniel Fojt VarHash(const char *str)
183601e196c8SJohn Marino {
183701e196c8SJohn Marino static const char hexdigits[16] = "0123456789abcdef";
1838ca58f742SDaniel Fojt const unsigned char *ustr = (const unsigned char *)str;
183901e196c8SJohn Marino
1840a34d5fb1SAntonio Huete Jimenez uint32_t h = 0x971e137bU;
1841a34d5fb1SAntonio Huete Jimenez uint32_t c1 = 0x95543787U;
1842a34d5fb1SAntonio Huete Jimenez uint32_t c2 = 0x2ad7eb25U;
1843a34d5fb1SAntonio Huete Jimenez size_t len2 = strlen(str);
184401e196c8SJohn Marino
1845a34d5fb1SAntonio Huete Jimenez char *buf;
1846a34d5fb1SAntonio Huete Jimenez size_t i;
1847a34d5fb1SAntonio Huete Jimenez
1848a34d5fb1SAntonio Huete Jimenez size_t len;
1849a34d5fb1SAntonio Huete Jimenez for (len = len2; len != 0;) {
1850a34d5fb1SAntonio Huete Jimenez uint32_t k = 0;
185101e196c8SJohn Marino switch (len) {
185201e196c8SJohn Marino default:
1853ca58f742SDaniel Fojt k = ((uint32_t)ustr[3] << 24) |
1854ca58f742SDaniel Fojt ((uint32_t)ustr[2] << 16) |
1855ca58f742SDaniel Fojt ((uint32_t)ustr[1] << 8) |
1856ca58f742SDaniel Fojt (uint32_t)ustr[0];
185701e196c8SJohn Marino len -= 4;
185801e196c8SJohn Marino ustr += 4;
185901e196c8SJohn Marino break;
186001e196c8SJohn Marino case 3:
1861ca58f742SDaniel Fojt k |= (uint32_t)ustr[2] << 16;
1862ca58f742SDaniel Fojt /* FALLTHROUGH */
186301e196c8SJohn Marino case 2:
1864ca58f742SDaniel Fojt k |= (uint32_t)ustr[1] << 8;
1865ca58f742SDaniel Fojt /* FALLTHROUGH */
186601e196c8SJohn Marino case 1:
1867ca58f742SDaniel Fojt k |= (uint32_t)ustr[0];
186801e196c8SJohn Marino len = 0;
186901e196c8SJohn Marino }
187001e196c8SJohn Marino c1 = c1 * 5 + 0x7b7d159cU;
187101e196c8SJohn Marino c2 = c2 * 5 + 0x6bce6396U;
187201e196c8SJohn Marino k *= c1;
187301e196c8SJohn Marino k = (k << 11) ^ (k >> 21);
187401e196c8SJohn Marino k *= c2;
187501e196c8SJohn Marino h = (h << 13) ^ (h >> 19);
187601e196c8SJohn Marino h = h * 5 + 0x52dce729U;
187701e196c8SJohn Marino h ^= k;
18785f1e34d9SAlexandre Perrin }
1879a34d5fb1SAntonio Huete Jimenez h ^= (uint32_t)len2;
188001e196c8SJohn Marino h *= 0x85ebca6b;
188101e196c8SJohn Marino h ^= h >> 13;
188201e196c8SJohn Marino h *= 0xc2b2ae35;
188301e196c8SJohn Marino h ^= h >> 16;
188401e196c8SJohn Marino
1885a34d5fb1SAntonio Huete Jimenez buf = bmake_malloc(9);
1886a34d5fb1SAntonio Huete Jimenez for (i = 0; i < 8; i++) {
1887a34d5fb1SAntonio Huete Jimenez buf[i] = hexdigits[h & 0x0f];
188801e196c8SJohn Marino h >>= 4;
188901e196c8SJohn Marino }
1890a34d5fb1SAntonio Huete Jimenez buf[8] = '\0';
1891a34d5fb1SAntonio Huete Jimenez return buf;
189201e196c8SJohn Marino }
189301e196c8SJohn Marino
189401e196c8SJohn Marino static char *
VarStrftime(const char * fmt,time_t t,bool gmt)1895*6eef5f0cSAntonio Huete Jimenez VarStrftime(const char *fmt, time_t t, bool gmt)
189601e196c8SJohn Marino {
189701e196c8SJohn Marino char buf[BUFSIZ];
189801e196c8SJohn Marino
1899*6eef5f0cSAntonio Huete Jimenez if (t == 0)
1900*6eef5f0cSAntonio Huete Jimenez time(&t);
1901a34d5fb1SAntonio Huete Jimenez if (*fmt == '\0')
190201e196c8SJohn Marino fmt = "%c";
1903*6eef5f0cSAntonio Huete Jimenez strftime(buf, sizeof buf, fmt, gmt ? gmtime(&t) : localtime(&t));
190401e196c8SJohn Marino
1905a34d5fb1SAntonio Huete Jimenez buf[sizeof buf - 1] = '\0';
190601e196c8SJohn Marino return bmake_strdup(buf);
190701e196c8SJohn Marino }
190801e196c8SJohn Marino
1909a34d5fb1SAntonio Huete Jimenez /*
1910a34d5fb1SAntonio Huete Jimenez * The ApplyModifier functions take an expression that is being evaluated.
1911ec533708SSascha Wildner * Their task is to apply a single modifier to the expression. This involves
1912ec533708SSascha Wildner * parsing the modifier, evaluating it and finally updating the value of the
1913ec533708SSascha Wildner * expression.
1914a34d5fb1SAntonio Huete Jimenez *
1915a34d5fb1SAntonio Huete Jimenez * Parsing the modifier
1916a34d5fb1SAntonio Huete Jimenez *
1917a34d5fb1SAntonio Huete Jimenez * If parsing succeeds, the parsing position *pp is updated to point to the
1918a34d5fb1SAntonio Huete Jimenez * first character following the modifier, which typically is either ':' or
1919*6eef5f0cSAntonio Huete Jimenez * ch->endc. The modifier doesn't have to check for this delimiter character,
1920a34d5fb1SAntonio Huete Jimenez * this is done by ApplyModifiers.
1921a34d5fb1SAntonio Huete Jimenez *
1922a34d5fb1SAntonio Huete Jimenez * XXX: As of 2020-11-15, some modifiers such as :S, :C, :P, :L do not
1923a34d5fb1SAntonio Huete Jimenez * need to be followed by a ':' or endc; this was an unintended mistake.
1924a34d5fb1SAntonio Huete Jimenez *
1925a34d5fb1SAntonio Huete Jimenez * If parsing fails because of a missing delimiter (as in the :S, :C or :@
1926a34d5fb1SAntonio Huete Jimenez * modifiers), return AMR_CLEANUP.
1927a34d5fb1SAntonio Huete Jimenez *
1928a34d5fb1SAntonio Huete Jimenez * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to
1929a34d5fb1SAntonio Huete Jimenez * try the SysV modifier ${VAR:from=to} as fallback. This should only be
1930a34d5fb1SAntonio Huete Jimenez * done as long as there have been no side effects from evaluating nested
1931a34d5fb1SAntonio Huete Jimenez * variables, to avoid evaluating them more than once. In this case, the
1932a34d5fb1SAntonio Huete Jimenez * parsing position may or may not be updated. (XXX: Why not? The original
1933a34d5fb1SAntonio Huete Jimenez * parsing position is well-known in ApplyModifiers.)
1934a34d5fb1SAntonio Huete Jimenez *
1935a34d5fb1SAntonio Huete Jimenez * If parsing fails and the SysV modifier ${VAR:from=to} should not be used
1936a34d5fb1SAntonio Huete Jimenez * as a fallback, either issue an error message using Error or Parse_Error
1937a34d5fb1SAntonio Huete Jimenez * and then return AMR_CLEANUP, or return AMR_BAD for the default error
1938a34d5fb1SAntonio Huete Jimenez * message. Both of these return values will stop processing the variable
1939a34d5fb1SAntonio Huete Jimenez * expression. (XXX: As of 2020-08-23, evaluation of the whole string
1940a34d5fb1SAntonio Huete Jimenez * continues nevertheless after skipping a few bytes, which essentially is
1941ec533708SSascha Wildner * undefined behavior. Not in the sense of C, but still the resulting string
1942ec533708SSascha Wildner * is garbage.)
1943a34d5fb1SAntonio Huete Jimenez *
1944a34d5fb1SAntonio Huete Jimenez * Evaluating the modifier
1945a34d5fb1SAntonio Huete Jimenez *
1946a34d5fb1SAntonio Huete Jimenez * After parsing, the modifier is evaluated. The side effects from evaluating
1947a34d5fb1SAntonio Huete Jimenez * nested variable expressions in the modifier text often already happen
1948ec533708SSascha Wildner * during parsing though. For most modifiers this doesn't matter since their
1949*6eef5f0cSAntonio Huete Jimenez * only noticeable effect is that they update the value of the expression.
1950ec533708SSascha Wildner * Some modifiers such as ':sh' or '::=' have noticeable side effects though.
1951a34d5fb1SAntonio Huete Jimenez *
1952a34d5fb1SAntonio Huete Jimenez * Evaluating the modifier usually takes the current value of the variable
1953*6eef5f0cSAntonio Huete Jimenez * expression from ch->expr->value, or the variable name from ch->var->name
1954ec533708SSascha Wildner * and stores the result back in expr->value via Expr_SetValueOwn or
1955ec533708SSascha Wildner * Expr_SetValueRefer.
1956a34d5fb1SAntonio Huete Jimenez *
1957a34d5fb1SAntonio Huete Jimenez * If evaluating fails (as of 2020-08-23), an error message is printed using
1958a34d5fb1SAntonio Huete Jimenez * Error. This function has no side-effects, it really just prints the error
1959a34d5fb1SAntonio Huete Jimenez * message. Processing the expression continues as if everything were ok.
1960a34d5fb1SAntonio Huete Jimenez * XXX: This should be fixed by adding proper error handling to Var_Subst,
1961a34d5fb1SAntonio Huete Jimenez * Var_Parse, ApplyModifiers and ModifyWords.
1962a34d5fb1SAntonio Huete Jimenez *
1963a34d5fb1SAntonio Huete Jimenez * Housekeeping
1964a34d5fb1SAntonio Huete Jimenez *
1965a34d5fb1SAntonio Huete Jimenez * Some modifiers such as :D and :U turn undefined expressions into defined
1966ec533708SSascha Wildner * expressions (see Expr_Define).
1967a34d5fb1SAntonio Huete Jimenez *
1968a34d5fb1SAntonio Huete Jimenez * Some modifiers need to free some memory.
1969a34d5fb1SAntonio Huete Jimenez */
1970ca58f742SDaniel Fojt
1971ec533708SSascha Wildner typedef enum ExprDefined {
1972ec533708SSascha Wildner /* The variable expression is based on a regular, defined variable. */
1973ec533708SSascha Wildner DEF_REGULAR,
1974a34d5fb1SAntonio Huete Jimenez /* The variable expression is based on an undefined variable. */
1975ec533708SSascha Wildner DEF_UNDEF,
1976a34d5fb1SAntonio Huete Jimenez /*
1977a34d5fb1SAntonio Huete Jimenez * The variable expression started as an undefined expression, but one
1978ec533708SSascha Wildner * of the modifiers (such as ':D' or ':U') has turned the expression
1979ec533708SSascha Wildner * from undefined to defined.
1980a34d5fb1SAntonio Huete Jimenez */
1981ec533708SSascha Wildner DEF_DEFINED
1982ec533708SSascha Wildner } ExprDefined;
1983ca58f742SDaniel Fojt
1984*6eef5f0cSAntonio Huete Jimenez static const char ExprDefined_Name[][10] = {
1985ec533708SSascha Wildner "regular",
1986ec533708SSascha Wildner "undefined",
1987ec533708SSascha Wildner "defined"
1988a34d5fb1SAntonio Huete Jimenez };
1989a34d5fb1SAntonio Huete Jimenez
1990*6eef5f0cSAntonio Huete Jimenez #if __STDC_VERSION__ >= 199901L
1991*6eef5f0cSAntonio Huete Jimenez #define const_member const
1992*6eef5f0cSAntonio Huete Jimenez #else
1993*6eef5f0cSAntonio Huete Jimenez #define const_member /* no const possible */
1994*6eef5f0cSAntonio Huete Jimenez #endif
1995*6eef5f0cSAntonio Huete Jimenez
1996*6eef5f0cSAntonio Huete Jimenez /* An expression based on a variable, such as $@ or ${VAR:Mpattern:Q}. */
1997ec533708SSascha Wildner typedef struct Expr {
1998*6eef5f0cSAntonio Huete Jimenez const char *name;
1999ec533708SSascha Wildner FStr value;
2000*6eef5f0cSAntonio Huete Jimenez VarEvalMode const_member emode;
2001*6eef5f0cSAntonio Huete Jimenez GNode *const_member scope;
2002ec533708SSascha Wildner ExprDefined defined;
2003ec533708SSascha Wildner } Expr;
2004ec533708SSascha Wildner
2005ec533708SSascha Wildner /*
2006*6eef5f0cSAntonio Huete Jimenez * The status of applying a chain of modifiers to an expression.
2007ec533708SSascha Wildner *
2008*6eef5f0cSAntonio Huete Jimenez * The modifiers of an expression are broken into chains of modifiers,
2009*6eef5f0cSAntonio Huete Jimenez * starting a new nested chain whenever an indirect modifier starts. There
2010*6eef5f0cSAntonio Huete Jimenez * are at most 2 nesting levels: the outer one for the direct modifiers, and
2011*6eef5f0cSAntonio Huete Jimenez * the inner one for the indirect modifiers.
2012*6eef5f0cSAntonio Huete Jimenez *
2013*6eef5f0cSAntonio Huete Jimenez * For example, the expression ${VAR:M*:${IND1}:${IND2}:O:u} has 3 chains of
2014*6eef5f0cSAntonio Huete Jimenez * modifiers:
2015*6eef5f0cSAntonio Huete Jimenez *
2016*6eef5f0cSAntonio Huete Jimenez * Chain 1 starts with the single modifier ':M*'.
2017*6eef5f0cSAntonio Huete Jimenez * Chain 2 starts with all modifiers from ${IND1}.
2018*6eef5f0cSAntonio Huete Jimenez * Chain 2 ends at the ':' between ${IND1} and ${IND2}.
2019*6eef5f0cSAntonio Huete Jimenez * Chain 3 starts with all modifiers from ${IND2}.
2020*6eef5f0cSAntonio Huete Jimenez * Chain 3 ends at the ':' after ${IND2}.
2021*6eef5f0cSAntonio Huete Jimenez * Chain 1 continues with the 2 modifiers ':O' and ':u'.
2022*6eef5f0cSAntonio Huete Jimenez * Chain 1 ends at the final '}' of the expression.
2023*6eef5f0cSAntonio Huete Jimenez *
2024*6eef5f0cSAntonio Huete Jimenez * After such a chain ends, its properties no longer have any effect.
2025*6eef5f0cSAntonio Huete Jimenez *
2026*6eef5f0cSAntonio Huete Jimenez * It may or may not have been intended that 'defined' has scope Expr while
2027*6eef5f0cSAntonio Huete Jimenez * 'sep' and 'oneBigWord' have smaller scope.
2028ec533708SSascha Wildner *
2029ec533708SSascha Wildner * See varmod-indirect.mk.
2030ec533708SSascha Wildner */
2031*6eef5f0cSAntonio Huete Jimenez typedef struct ModChain {
2032ec533708SSascha Wildner Expr *expr;
2033a34d5fb1SAntonio Huete Jimenez /* '\0' or '{' or '(' */
2034*6eef5f0cSAntonio Huete Jimenez char const_member startc;
2035a34d5fb1SAntonio Huete Jimenez /* '\0' or '}' or ')' */
2036*6eef5f0cSAntonio Huete Jimenez char const_member endc;
2037a34d5fb1SAntonio Huete Jimenez /* Word separator in expansions (see the :ts modifier). */
2038a34d5fb1SAntonio Huete Jimenez char sep;
2039a34d5fb1SAntonio Huete Jimenez /*
2040*6eef5f0cSAntonio Huete Jimenez * True if some modifiers that otherwise split the variable value
2041a34d5fb1SAntonio Huete Jimenez * into words, like :S and :C, treat the variable value as a single
2042a34d5fb1SAntonio Huete Jimenez * big word, possibly containing spaces.
2043a34d5fb1SAntonio Huete Jimenez */
2044*6eef5f0cSAntonio Huete Jimenez bool oneBigWord;
2045*6eef5f0cSAntonio Huete Jimenez } ModChain;
2046ca58f742SDaniel Fojt
2047a34d5fb1SAntonio Huete Jimenez static void
Expr_Define(Expr * expr)2048ec533708SSascha Wildner Expr_Define(Expr *expr)
2049a34d5fb1SAntonio Huete Jimenez {
2050ec533708SSascha Wildner if (expr->defined == DEF_UNDEF)
2051ec533708SSascha Wildner expr->defined = DEF_DEFINED;
2052ec533708SSascha Wildner }
2053ec533708SSascha Wildner
2054*6eef5f0cSAntonio Huete Jimenez static const char *
Expr_Str(const Expr * expr)2055*6eef5f0cSAntonio Huete Jimenez Expr_Str(const Expr *expr)
2056*6eef5f0cSAntonio Huete Jimenez {
2057*6eef5f0cSAntonio Huete Jimenez return expr->value.str;
2058*6eef5f0cSAntonio Huete Jimenez }
2059*6eef5f0cSAntonio Huete Jimenez
2060*6eef5f0cSAntonio Huete Jimenez static SubstringWords
Expr_Words(const Expr * expr)2061*6eef5f0cSAntonio Huete Jimenez Expr_Words(const Expr *expr)
2062*6eef5f0cSAntonio Huete Jimenez {
2063*6eef5f0cSAntonio Huete Jimenez return Substring_Words(Expr_Str(expr), false);
2064*6eef5f0cSAntonio Huete Jimenez }
2065*6eef5f0cSAntonio Huete Jimenez
2066*6eef5f0cSAntonio Huete Jimenez static void
Expr_SetValue(Expr * expr,FStr value)2067*6eef5f0cSAntonio Huete Jimenez Expr_SetValue(Expr *expr, FStr value)
2068*6eef5f0cSAntonio Huete Jimenez {
2069*6eef5f0cSAntonio Huete Jimenez FStr_Done(&expr->value);
2070*6eef5f0cSAntonio Huete Jimenez expr->value = value;
2071*6eef5f0cSAntonio Huete Jimenez }
2072*6eef5f0cSAntonio Huete Jimenez
2073ec533708SSascha Wildner static void
Expr_SetValueOwn(Expr * expr,char * value)2074ec533708SSascha Wildner Expr_SetValueOwn(Expr *expr, char *value)
2075ec533708SSascha Wildner {
2076*6eef5f0cSAntonio Huete Jimenez Expr_SetValue(expr, FStr_InitOwn(value));
2077ec533708SSascha Wildner }
2078ec533708SSascha Wildner
2079ec533708SSascha Wildner static void
Expr_SetValueRefer(Expr * expr,const char * value)2080ec533708SSascha Wildner Expr_SetValueRefer(Expr *expr, const char *value)
2081ec533708SSascha Wildner {
2082*6eef5f0cSAntonio Huete Jimenez Expr_SetValue(expr, FStr_InitRefer(value));
2083a34d5fb1SAntonio Huete Jimenez }
2084a34d5fb1SAntonio Huete Jimenez
2085*6eef5f0cSAntonio Huete Jimenez static bool
Expr_ShouldEval(const Expr * expr)2086*6eef5f0cSAntonio Huete Jimenez Expr_ShouldEval(const Expr *expr)
2087*6eef5f0cSAntonio Huete Jimenez {
2088*6eef5f0cSAntonio Huete Jimenez return VarEvalMode_ShouldEval(expr->emode);
2089*6eef5f0cSAntonio Huete Jimenez }
2090*6eef5f0cSAntonio Huete Jimenez
2091*6eef5f0cSAntonio Huete Jimenez static bool
ModChain_ShouldEval(const ModChain * ch)2092*6eef5f0cSAntonio Huete Jimenez ModChain_ShouldEval(const ModChain *ch)
2093*6eef5f0cSAntonio Huete Jimenez {
2094*6eef5f0cSAntonio Huete Jimenez return Expr_ShouldEval(ch->expr);
2095*6eef5f0cSAntonio Huete Jimenez }
2096*6eef5f0cSAntonio Huete Jimenez
2097*6eef5f0cSAntonio Huete Jimenez
2098a34d5fb1SAntonio Huete Jimenez typedef enum ApplyModifierResult {
2099a34d5fb1SAntonio Huete Jimenez /* Continue parsing */
2100a34d5fb1SAntonio Huete Jimenez AMR_OK,
2101ec533708SSascha Wildner /* Not a match, try other modifiers as well. */
2102a34d5fb1SAntonio Huete Jimenez AMR_UNKNOWN,
2103ec533708SSascha Wildner /* Error out with "Bad modifier" message. */
2104a34d5fb1SAntonio Huete Jimenez AMR_BAD,
2105ec533708SSascha Wildner /* Error out without the standard error message. */
2106a34d5fb1SAntonio Huete Jimenez AMR_CLEANUP
2107a34d5fb1SAntonio Huete Jimenez } ApplyModifierResult;
2108a34d5fb1SAntonio Huete Jimenez
2109a34d5fb1SAntonio Huete Jimenez /*
2110a34d5fb1SAntonio Huete Jimenez * Allow backslashes to escape the delimiter, $, and \, but don't touch other
2111a34d5fb1SAntonio Huete Jimenez * backslashes.
2112a34d5fb1SAntonio Huete Jimenez */
2113*6eef5f0cSAntonio Huete Jimenez static bool
IsEscapedModifierPart(const char * p,char delim,struct ModifyWord_SubstArgs * subst)2114a34d5fb1SAntonio Huete Jimenez IsEscapedModifierPart(const char *p, char delim,
2115a34d5fb1SAntonio Huete Jimenez struct ModifyWord_SubstArgs *subst)
2116a34d5fb1SAntonio Huete Jimenez {
2117a34d5fb1SAntonio Huete Jimenez if (p[0] != '\\')
2118*6eef5f0cSAntonio Huete Jimenez return false;
2119a34d5fb1SAntonio Huete Jimenez if (p[1] == delim || p[1] == '\\' || p[1] == '$')
2120*6eef5f0cSAntonio Huete Jimenez return true;
2121a34d5fb1SAntonio Huete Jimenez return p[1] == '&' && subst != NULL;
2122a34d5fb1SAntonio Huete Jimenez }
2123a34d5fb1SAntonio Huete Jimenez
2124*6eef5f0cSAntonio Huete Jimenez /*
2125*6eef5f0cSAntonio Huete Jimenez * In a part of a modifier, parse a subexpression and evaluate it.
2126*6eef5f0cSAntonio Huete Jimenez */
2127*6eef5f0cSAntonio Huete Jimenez static void
ParseModifierPartExpr(const char ** pp,LazyBuf * part,const ModChain * ch,VarEvalMode emode)2128*6eef5f0cSAntonio Huete Jimenez ParseModifierPartExpr(const char **pp, LazyBuf *part, const ModChain *ch,
2129*6eef5f0cSAntonio Huete Jimenez VarEvalMode emode)
2130a34d5fb1SAntonio Huete Jimenez {
2131*6eef5f0cSAntonio Huete Jimenez const char *p = *pp;
2132a34d5fb1SAntonio Huete Jimenez FStr nested_val;
2133a34d5fb1SAntonio Huete Jimenez
2134*6eef5f0cSAntonio Huete Jimenez (void)Var_Parse(&p, ch->expr->scope,
2135*6eef5f0cSAntonio Huete Jimenez VarEvalMode_WithoutKeepDollar(emode), &nested_val);
2136a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
2137*6eef5f0cSAntonio Huete Jimenez LazyBuf_AddStr(part, nested_val.str);
2138a34d5fb1SAntonio Huete Jimenez FStr_Done(&nested_val);
2139*6eef5f0cSAntonio Huete Jimenez *pp = p;
2140a34d5fb1SAntonio Huete Jimenez }
2141a34d5fb1SAntonio Huete Jimenez
2142a34d5fb1SAntonio Huete Jimenez /*
2143*6eef5f0cSAntonio Huete Jimenez * In a part of a modifier, parse a subexpression but don't evaluate it.
2144a34d5fb1SAntonio Huete Jimenez *
2145*6eef5f0cSAntonio Huete Jimenez * XXX: This whole block is very similar to Var_Parse with VARE_PARSE_ONLY.
2146*6eef5f0cSAntonio Huete Jimenez * There may be subtle edge cases though that are not yet covered in the unit
2147*6eef5f0cSAntonio Huete Jimenez * tests and that are parsed differently, depending on whether they are
2148*6eef5f0cSAntonio Huete Jimenez * evaluated or not.
2149*6eef5f0cSAntonio Huete Jimenez *
2150*6eef5f0cSAntonio Huete Jimenez * This subtle difference is not documented in the manual page, neither is
2151*6eef5f0cSAntonio Huete Jimenez * the difference between parsing ':D' and ':M' documented. No code should
2152*6eef5f0cSAntonio Huete Jimenez * ever depend on these details, but who knows.
2153*6eef5f0cSAntonio Huete Jimenez *
2154*6eef5f0cSAntonio Huete Jimenez * TODO: Before trying to replace this code with Var_Parse, there need to be
2155*6eef5f0cSAntonio Huete Jimenez * more unit tests in varmod-loop.mk. The modifier ':@' uses Var_Subst
2156*6eef5f0cSAntonio Huete Jimenez * internally, in which a '$' is escaped as '$$', not as '\$' like in other
2157*6eef5f0cSAntonio Huete Jimenez * modifiers. When parsing the body text '$${var}', skipping over the first
2158*6eef5f0cSAntonio Huete Jimenez * '$' would treat '${var}' as a make expression, not as a shell variable.
2159a34d5fb1SAntonio Huete Jimenez */
2160*6eef5f0cSAntonio Huete Jimenez static void
ParseModifierPartDollar(const char ** pp,LazyBuf * part)2161*6eef5f0cSAntonio Huete Jimenez ParseModifierPartDollar(const char **pp, LazyBuf *part)
2162*6eef5f0cSAntonio Huete Jimenez {
2163*6eef5f0cSAntonio Huete Jimenez const char *p = *pp;
2164*6eef5f0cSAntonio Huete Jimenez const char *start = *pp;
2165a34d5fb1SAntonio Huete Jimenez
2166a34d5fb1SAntonio Huete Jimenez if (p[1] == '(' || p[1] == '{') {
2167a34d5fb1SAntonio Huete Jimenez char startc = p[1];
2168a34d5fb1SAntonio Huete Jimenez int endc = startc == '(' ? ')' : '}';
2169a34d5fb1SAntonio Huete Jimenez int depth = 1;
2170a34d5fb1SAntonio Huete Jimenez
2171a34d5fb1SAntonio Huete Jimenez for (p += 2; *p != '\0' && depth > 0; p++) {
2172a34d5fb1SAntonio Huete Jimenez if (p[-1] != '\\') {
2173a34d5fb1SAntonio Huete Jimenez if (*p == startc)
2174a34d5fb1SAntonio Huete Jimenez depth++;
2175a34d5fb1SAntonio Huete Jimenez if (*p == endc)
2176a34d5fb1SAntonio Huete Jimenez depth--;
2177a34d5fb1SAntonio Huete Jimenez }
2178a34d5fb1SAntonio Huete Jimenez }
2179*6eef5f0cSAntonio Huete Jimenez LazyBuf_AddBytesBetween(part, start, p);
2180*6eef5f0cSAntonio Huete Jimenez *pp = p;
2181a34d5fb1SAntonio Huete Jimenez } else {
2182*6eef5f0cSAntonio Huete Jimenez LazyBuf_Add(part, *start);
2183*6eef5f0cSAntonio Huete Jimenez *pp = p + 1;
2184a34d5fb1SAntonio Huete Jimenez }
2185a34d5fb1SAntonio Huete Jimenez }
2186a34d5fb1SAntonio Huete Jimenez
2187*6eef5f0cSAntonio Huete Jimenez /* See ParseModifierPart for the documentation. */
2188*6eef5f0cSAntonio Huete Jimenez static VarParseResult
ParseModifierPartSubst(const char ** pp,char delim,VarEvalMode emode,ModChain * ch,LazyBuf * part,PatternFlags * out_pflags,struct ModifyWord_SubstArgs * subst)2189*6eef5f0cSAntonio Huete Jimenez ParseModifierPartSubst(
2190*6eef5f0cSAntonio Huete Jimenez const char **pp,
2191*6eef5f0cSAntonio Huete Jimenez char delim,
2192*6eef5f0cSAntonio Huete Jimenez VarEvalMode emode,
2193*6eef5f0cSAntonio Huete Jimenez ModChain *ch,
2194*6eef5f0cSAntonio Huete Jimenez LazyBuf *part,
2195*6eef5f0cSAntonio Huete Jimenez /*
2196*6eef5f0cSAntonio Huete Jimenez * For the first part of the modifier ':S', set anchorEnd if the last
2197*6eef5f0cSAntonio Huete Jimenez * character of the pattern is a $.
2198*6eef5f0cSAntonio Huete Jimenez */
2199*6eef5f0cSAntonio Huete Jimenez PatternFlags *out_pflags,
2200*6eef5f0cSAntonio Huete Jimenez /*
2201*6eef5f0cSAntonio Huete Jimenez * For the second part of the :S modifier, allow ampersands to be escaped
2202*6eef5f0cSAntonio Huete Jimenez * and replace unescaped ampersands with subst->lhs.
2203*6eef5f0cSAntonio Huete Jimenez */
2204*6eef5f0cSAntonio Huete Jimenez struct ModifyWord_SubstArgs *subst
2205*6eef5f0cSAntonio Huete Jimenez )
2206*6eef5f0cSAntonio Huete Jimenez {
2207*6eef5f0cSAntonio Huete Jimenez const char *p;
2208*6eef5f0cSAntonio Huete Jimenez
2209*6eef5f0cSAntonio Huete Jimenez p = *pp;
2210*6eef5f0cSAntonio Huete Jimenez LazyBuf_Init(part, p);
2211*6eef5f0cSAntonio Huete Jimenez
2212*6eef5f0cSAntonio Huete Jimenez while (*p != '\0' && *p != delim) {
2213*6eef5f0cSAntonio Huete Jimenez if (IsEscapedModifierPart(p, delim, subst)) {
2214*6eef5f0cSAntonio Huete Jimenez LazyBuf_Add(part, p[1]);
2215*6eef5f0cSAntonio Huete Jimenez p += 2;
2216*6eef5f0cSAntonio Huete Jimenez } else if (*p != '$') { /* Unescaped, simple text */
2217*6eef5f0cSAntonio Huete Jimenez if (subst != NULL && *p == '&')
2218*6eef5f0cSAntonio Huete Jimenez LazyBuf_AddSubstring(part, subst->lhs);
2219*6eef5f0cSAntonio Huete Jimenez else
2220*6eef5f0cSAntonio Huete Jimenez LazyBuf_Add(part, *p);
2221*6eef5f0cSAntonio Huete Jimenez p++;
2222*6eef5f0cSAntonio Huete Jimenez } else if (p[1] == delim) { /* Unescaped '$' at end */
2223*6eef5f0cSAntonio Huete Jimenez if (out_pflags != NULL)
2224*6eef5f0cSAntonio Huete Jimenez out_pflags->anchorEnd = true;
2225*6eef5f0cSAntonio Huete Jimenez else
2226*6eef5f0cSAntonio Huete Jimenez LazyBuf_Add(part, *p);
2227*6eef5f0cSAntonio Huete Jimenez p++;
2228*6eef5f0cSAntonio Huete Jimenez } else if (VarEvalMode_ShouldEval(emode))
2229*6eef5f0cSAntonio Huete Jimenez ParseModifierPartExpr(&p, part, ch, emode);
2230*6eef5f0cSAntonio Huete Jimenez else
2231*6eef5f0cSAntonio Huete Jimenez ParseModifierPartDollar(&p, part);
2232*6eef5f0cSAntonio Huete Jimenez }
2233*6eef5f0cSAntonio Huete Jimenez
2234a34d5fb1SAntonio Huete Jimenez if (*p != delim) {
2235a34d5fb1SAntonio Huete Jimenez *pp = p;
2236ec533708SSascha Wildner Error("Unfinished modifier for \"%s\" ('%c' missing)",
2237*6eef5f0cSAntonio Huete Jimenez ch->expr->name, delim);
2238*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(part);
2239a34d5fb1SAntonio Huete Jimenez return VPR_ERR;
2240a34d5fb1SAntonio Huete Jimenez }
2241a34d5fb1SAntonio Huete Jimenez
2242a34d5fb1SAntonio Huete Jimenez *pp = p + 1;
2243a34d5fb1SAntonio Huete Jimenez
2244*6eef5f0cSAntonio Huete Jimenez {
2245*6eef5f0cSAntonio Huete Jimenez Substring sub = LazyBuf_Get(part);
2246*6eef5f0cSAntonio Huete Jimenez DEBUG2(VAR, "Modifier part: \"%.*s\"\n",
2247*6eef5f0cSAntonio Huete Jimenez (int)Substring_Length(sub), sub.start);
2248*6eef5f0cSAntonio Huete Jimenez }
2249*6eef5f0cSAntonio Huete Jimenez
2250a34d5fb1SAntonio Huete Jimenez return VPR_OK;
2251a34d5fb1SAntonio Huete Jimenez }
2252a34d5fb1SAntonio Huete Jimenez
2253a34d5fb1SAntonio Huete Jimenez /*
2254a34d5fb1SAntonio Huete Jimenez * Parse a part of a modifier such as the "from" and "to" in :S/from/to/ or
2255a34d5fb1SAntonio Huete Jimenez * the "var" or "replacement ${var}" in :@var@replacement ${var}@, up to and
2256a34d5fb1SAntonio Huete Jimenez * including the next unescaped delimiter. The delimiter, as well as the
2257a34d5fb1SAntonio Huete Jimenez * backslash or the dollar, can be escaped with a backslash.
2258a34d5fb1SAntonio Huete Jimenez *
2259*6eef5f0cSAntonio Huete Jimenez * Return VPR_OK if parsing succeeded, together with the parsed (and possibly
2260*6eef5f0cSAntonio Huete Jimenez * expanded) part. In that case, pp points right after the delimiter. The
2261*6eef5f0cSAntonio Huete Jimenez * delimiter is not included in the part though.
2262a34d5fb1SAntonio Huete Jimenez */
2263a34d5fb1SAntonio Huete Jimenez static VarParseResult
ParseModifierPart(const char ** pp,char delim,VarEvalMode emode,ModChain * ch,LazyBuf * part)2264a34d5fb1SAntonio Huete Jimenez ParseModifierPart(
2265a34d5fb1SAntonio Huete Jimenez /* The parsing position, updated upon return */
2266a34d5fb1SAntonio Huete Jimenez const char **pp,
2267a34d5fb1SAntonio Huete Jimenez /* Parsing stops at this delimiter */
2268a34d5fb1SAntonio Huete Jimenez char delim,
2269*6eef5f0cSAntonio Huete Jimenez /* Mode for evaluating nested variables. */
2270*6eef5f0cSAntonio Huete Jimenez VarEvalMode emode,
2271*6eef5f0cSAntonio Huete Jimenez ModChain *ch,
2272*6eef5f0cSAntonio Huete Jimenez LazyBuf *part
2273a34d5fb1SAntonio Huete Jimenez )
2274a34d5fb1SAntonio Huete Jimenez {
2275*6eef5f0cSAntonio Huete Jimenez return ParseModifierPartSubst(pp, delim, emode, ch, part, NULL, NULL);
2276a34d5fb1SAntonio Huete Jimenez }
2277a34d5fb1SAntonio Huete Jimenez
2278*6eef5f0cSAntonio Huete Jimenez MAKE_INLINE bool
IsDelimiter(char c,const ModChain * ch)2279*6eef5f0cSAntonio Huete Jimenez IsDelimiter(char c, const ModChain *ch)
2280ec533708SSascha Wildner {
2281*6eef5f0cSAntonio Huete Jimenez return c == ':' || c == ch->endc || c == '\0';
2282ec533708SSascha Wildner }
2283ec533708SSascha Wildner
2284a34d5fb1SAntonio Huete Jimenez /* Test whether mod starts with modname, followed by a delimiter. */
2285*6eef5f0cSAntonio Huete Jimenez MAKE_INLINE bool
ModMatch(const char * mod,const char * modname,const ModChain * ch)2286*6eef5f0cSAntonio Huete Jimenez ModMatch(const char *mod, const char *modname, const ModChain *ch)
2287a34d5fb1SAntonio Huete Jimenez {
2288a34d5fb1SAntonio Huete Jimenez size_t n = strlen(modname);
2289*6eef5f0cSAntonio Huete Jimenez return strncmp(mod, modname, n) == 0 && IsDelimiter(mod[n], ch);
2290a34d5fb1SAntonio Huete Jimenez }
2291a34d5fb1SAntonio Huete Jimenez
2292a34d5fb1SAntonio Huete Jimenez /* Test whether mod starts with modname, followed by a delimiter or '='. */
2293*6eef5f0cSAntonio Huete Jimenez MAKE_INLINE bool
ModMatchEq(const char * mod,const char * modname,const ModChain * ch)2294*6eef5f0cSAntonio Huete Jimenez ModMatchEq(const char *mod, const char *modname, const ModChain *ch)
2295a34d5fb1SAntonio Huete Jimenez {
2296a34d5fb1SAntonio Huete Jimenez size_t n = strlen(modname);
2297a34d5fb1SAntonio Huete Jimenez return strncmp(mod, modname, n) == 0 &&
2298*6eef5f0cSAntonio Huete Jimenez (IsDelimiter(mod[n], ch) || mod[n] == '=');
2299a34d5fb1SAntonio Huete Jimenez }
2300a34d5fb1SAntonio Huete Jimenez
2301*6eef5f0cSAntonio Huete Jimenez static bool
TryParseIntBase0(const char ** pp,int * out_num)2302a34d5fb1SAntonio Huete Jimenez TryParseIntBase0(const char **pp, int *out_num)
2303a34d5fb1SAntonio Huete Jimenez {
2304a34d5fb1SAntonio Huete Jimenez char *end;
2305a34d5fb1SAntonio Huete Jimenez long n;
2306a34d5fb1SAntonio Huete Jimenez
2307a34d5fb1SAntonio Huete Jimenez errno = 0;
2308a34d5fb1SAntonio Huete Jimenez n = strtol(*pp, &end, 0);
2309ec533708SSascha Wildner
2310ec533708SSascha Wildner if (end == *pp)
2311*6eef5f0cSAntonio Huete Jimenez return false;
2312a34d5fb1SAntonio Huete Jimenez if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE)
2313*6eef5f0cSAntonio Huete Jimenez return false;
2314a34d5fb1SAntonio Huete Jimenez if (n < INT_MIN || n > INT_MAX)
2315*6eef5f0cSAntonio Huete Jimenez return false;
2316a34d5fb1SAntonio Huete Jimenez
2317a34d5fb1SAntonio Huete Jimenez *pp = end;
2318a34d5fb1SAntonio Huete Jimenez *out_num = (int)n;
2319*6eef5f0cSAntonio Huete Jimenez return true;
2320a34d5fb1SAntonio Huete Jimenez }
2321a34d5fb1SAntonio Huete Jimenez
2322*6eef5f0cSAntonio Huete Jimenez static bool
TryParseSize(const char ** pp,size_t * out_num)2323a34d5fb1SAntonio Huete Jimenez TryParseSize(const char **pp, size_t *out_num)
2324a34d5fb1SAntonio Huete Jimenez {
2325a34d5fb1SAntonio Huete Jimenez char *end;
2326a34d5fb1SAntonio Huete Jimenez unsigned long n;
2327a34d5fb1SAntonio Huete Jimenez
2328a34d5fb1SAntonio Huete Jimenez if (!ch_isdigit(**pp))
2329*6eef5f0cSAntonio Huete Jimenez return false;
2330a34d5fb1SAntonio Huete Jimenez
2331a34d5fb1SAntonio Huete Jimenez errno = 0;
2332a34d5fb1SAntonio Huete Jimenez n = strtoul(*pp, &end, 10);
2333a34d5fb1SAntonio Huete Jimenez if (n == ULONG_MAX && errno == ERANGE)
2334*6eef5f0cSAntonio Huete Jimenez return false;
2335a34d5fb1SAntonio Huete Jimenez if (n > SIZE_MAX)
2336*6eef5f0cSAntonio Huete Jimenez return false;
2337a34d5fb1SAntonio Huete Jimenez
2338a34d5fb1SAntonio Huete Jimenez *pp = end;
2339a34d5fb1SAntonio Huete Jimenez *out_num = (size_t)n;
2340*6eef5f0cSAntonio Huete Jimenez return true;
2341a34d5fb1SAntonio Huete Jimenez }
2342a34d5fb1SAntonio Huete Jimenez
2343*6eef5f0cSAntonio Huete Jimenez static bool
TryParseChar(const char ** pp,int base,char * out_ch)2344a34d5fb1SAntonio Huete Jimenez TryParseChar(const char **pp, int base, char *out_ch)
2345a34d5fb1SAntonio Huete Jimenez {
2346a34d5fb1SAntonio Huete Jimenez char *end;
2347a34d5fb1SAntonio Huete Jimenez unsigned long n;
2348a34d5fb1SAntonio Huete Jimenez
2349a34d5fb1SAntonio Huete Jimenez if (!ch_isalnum(**pp))
2350*6eef5f0cSAntonio Huete Jimenez return false;
2351a34d5fb1SAntonio Huete Jimenez
2352a34d5fb1SAntonio Huete Jimenez errno = 0;
2353a34d5fb1SAntonio Huete Jimenez n = strtoul(*pp, &end, base);
2354a34d5fb1SAntonio Huete Jimenez if (n == ULONG_MAX && errno == ERANGE)
2355*6eef5f0cSAntonio Huete Jimenez return false;
2356a34d5fb1SAntonio Huete Jimenez if (n > UCHAR_MAX)
2357*6eef5f0cSAntonio Huete Jimenez return false;
2358a34d5fb1SAntonio Huete Jimenez
2359a34d5fb1SAntonio Huete Jimenez *pp = end;
2360a34d5fb1SAntonio Huete Jimenez *out_ch = (char)n;
2361*6eef5f0cSAntonio Huete Jimenez return true;
2362a34d5fb1SAntonio Huete Jimenez }
2363ca58f742SDaniel Fojt
2364ec533708SSascha Wildner /*
2365ec533708SSascha Wildner * Modify each word of the expression using the given function and place the
2366ec533708SSascha Wildner * result back in the expression.
2367ec533708SSascha Wildner */
2368ec533708SSascha Wildner static void
ModifyWords(ModChain * ch,ModifyWordProc modifyWord,void * modifyWord_args,bool oneBigWord)2369*6eef5f0cSAntonio Huete Jimenez ModifyWords(ModChain *ch,
2370ec533708SSascha Wildner ModifyWordProc modifyWord, void *modifyWord_args,
2371*6eef5f0cSAntonio Huete Jimenez bool oneBigWord)
2372ec533708SSascha Wildner {
2373*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
2374*6eef5f0cSAntonio Huete Jimenez const char *val = Expr_Str(expr);
2375ec533708SSascha Wildner SepBuf result;
2376*6eef5f0cSAntonio Huete Jimenez SubstringWords words;
2377ec533708SSascha Wildner size_t i;
2378*6eef5f0cSAntonio Huete Jimenez Substring word;
2379ec533708SSascha Wildner
2380ec533708SSascha Wildner if (oneBigWord) {
2381*6eef5f0cSAntonio Huete Jimenez SepBuf_Init(&result, ch->sep);
2382*6eef5f0cSAntonio Huete Jimenez /* XXX: performance: Substring_InitStr calls strlen */
2383*6eef5f0cSAntonio Huete Jimenez word = Substring_InitStr(val);
2384*6eef5f0cSAntonio Huete Jimenez modifyWord(word, &result, modifyWord_args);
2385ec533708SSascha Wildner goto done;
2386ec533708SSascha Wildner }
2387ec533708SSascha Wildner
2388*6eef5f0cSAntonio Huete Jimenez words = Substring_Words(val, false);
2389ec533708SSascha Wildner
2390*6eef5f0cSAntonio Huete Jimenez DEBUG3(VAR, "ModifyWords: split \"%s\" into %u %s\n",
2391*6eef5f0cSAntonio Huete Jimenez val, (unsigned)words.len, words.len != 1 ? "words" : "word");
2392ec533708SSascha Wildner
2393*6eef5f0cSAntonio Huete Jimenez SepBuf_Init(&result, ch->sep);
2394ec533708SSascha Wildner for (i = 0; i < words.len; i++) {
2395ec533708SSascha Wildner modifyWord(words.words[i], &result, modifyWord_args);
2396ec533708SSascha Wildner if (result.buf.len > 0)
2397ec533708SSascha Wildner SepBuf_Sep(&result);
2398ec533708SSascha Wildner }
2399ec533708SSascha Wildner
2400*6eef5f0cSAntonio Huete Jimenez SubstringWords_Free(words);
2401ec533708SSascha Wildner
2402ec533708SSascha Wildner done:
2403ec533708SSascha Wildner Expr_SetValueOwn(expr, SepBuf_DoneData(&result));
2404ec533708SSascha Wildner }
2405ec533708SSascha Wildner
2406ca58f742SDaniel Fojt /* :@var@...${var}...@ */
2407a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Loop(const char ** pp,ModChain * ch)2408*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Loop(const char **pp, ModChain *ch)
2409a34d5fb1SAntonio Huete Jimenez {
2410*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
2411a34d5fb1SAntonio Huete Jimenez struct ModifyWord_LoopArgs args;
2412a34d5fb1SAntonio Huete Jimenez char prev_sep;
2413a34d5fb1SAntonio Huete Jimenez VarParseResult res;
2414*6eef5f0cSAntonio Huete Jimenez LazyBuf tvarBuf, strBuf;
2415*6eef5f0cSAntonio Huete Jimenez FStr tvar, str;
2416ca58f742SDaniel Fojt
2417ec533708SSascha Wildner args.scope = expr->scope;
2418ca58f742SDaniel Fojt
2419a34d5fb1SAntonio Huete Jimenez (*pp)++; /* Skip the first '@' */
2420*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPart(pp, '@', VARE_PARSE_ONLY, ch, &tvarBuf);
2421a34d5fb1SAntonio Huete Jimenez if (res != VPR_OK)
2422a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2423*6eef5f0cSAntonio Huete Jimenez tvar = LazyBuf_DoneGet(&tvarBuf);
2424*6eef5f0cSAntonio Huete Jimenez args.var = tvar.str;
2425*6eef5f0cSAntonio Huete Jimenez if (strchr(args.var, '$') != NULL) {
2426a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
2427a34d5fb1SAntonio Huete Jimenez "In the :@ modifier of \"%s\", the variable name \"%s\" "
2428*6eef5f0cSAntonio Huete Jimenez "must not contain a dollar",
2429*6eef5f0cSAntonio Huete Jimenez expr->name, args.var);
2430a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2431a34d5fb1SAntonio Huete Jimenez }
2432ca58f742SDaniel Fojt
2433*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPart(pp, '@', VARE_PARSE_ONLY, ch, &strBuf);
2434a34d5fb1SAntonio Huete Jimenez if (res != VPR_OK)
2435a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2436*6eef5f0cSAntonio Huete Jimenez str = LazyBuf_DoneGet(&strBuf);
2437*6eef5f0cSAntonio Huete Jimenez args.body = str.str;
2438ca58f742SDaniel Fojt
2439*6eef5f0cSAntonio Huete Jimenez if (!Expr_ShouldEval(expr))
2440ec533708SSascha Wildner goto done;
2441ec533708SSascha Wildner
2442*6eef5f0cSAntonio Huete Jimenez args.emode = VarEvalMode_WithoutKeepDollar(expr->emode);
2443*6eef5f0cSAntonio Huete Jimenez prev_sep = ch->sep;
2444*6eef5f0cSAntonio Huete Jimenez ch->sep = ' '; /* XXX: should be ch->sep for consistency */
2445*6eef5f0cSAntonio Huete Jimenez ModifyWords(ch, ModifyWord_Loop, &args, ch->oneBigWord);
2446*6eef5f0cSAntonio Huete Jimenez ch->sep = prev_sep;
2447*6eef5f0cSAntonio Huete Jimenez /* XXX: Consider restoring the previous value instead of deleting. */
2448*6eef5f0cSAntonio Huete Jimenez Var_Delete(expr->scope, args.var);
2449ec533708SSascha Wildner
2450ec533708SSascha Wildner done:
2451*6eef5f0cSAntonio Huete Jimenez FStr_Done(&tvar);
2452*6eef5f0cSAntonio Huete Jimenez FStr_Done(&str);
2453a34d5fb1SAntonio Huete Jimenez return AMR_OK;
2454ca58f742SDaniel Fojt }
2455ca58f742SDaniel Fojt
2456*6eef5f0cSAntonio Huete Jimenez static void
ParseModifier_Defined(const char ** pp,ModChain * ch,bool shouldEval,LazyBuf * buf)2457*6eef5f0cSAntonio Huete Jimenez ParseModifier_Defined(const char **pp, ModChain *ch, bool shouldEval,
2458*6eef5f0cSAntonio Huete Jimenez LazyBuf *buf)
2459ca58f742SDaniel Fojt {
2460a34d5fb1SAntonio Huete Jimenez const char *p;
2461ca58f742SDaniel Fojt
2462a34d5fb1SAntonio Huete Jimenez p = *pp + 1;
2463*6eef5f0cSAntonio Huete Jimenez LazyBuf_Init(buf, p);
2464*6eef5f0cSAntonio Huete Jimenez while (!IsDelimiter(*p, ch)) {
2465ca58f742SDaniel Fojt
2466*6eef5f0cSAntonio Huete Jimenez /*
2467*6eef5f0cSAntonio Huete Jimenez * XXX: This code is similar to the one in Var_Parse. See if
2468*6eef5f0cSAntonio Huete Jimenez * the code can be merged. See also ApplyModifier_Match and
2469*6eef5f0cSAntonio Huete Jimenez * ParseModifierPart.
2470*6eef5f0cSAntonio Huete Jimenez */
2471a34d5fb1SAntonio Huete Jimenez
2472a34d5fb1SAntonio Huete Jimenez /* Escaped delimiter or other special character */
2473ec533708SSascha Wildner /* See Buf_AddEscaped in for.c. */
2474a34d5fb1SAntonio Huete Jimenez if (*p == '\\') {
2475a34d5fb1SAntonio Huete Jimenez char c = p[1];
2476*6eef5f0cSAntonio Huete Jimenez if ((IsDelimiter(c, ch) && c != '\0') ||
2477*6eef5f0cSAntonio Huete Jimenez c == '$' || c == '\\') {
2478*6eef5f0cSAntonio Huete Jimenez if (shouldEval)
2479*6eef5f0cSAntonio Huete Jimenez LazyBuf_Add(buf, c);
2480a34d5fb1SAntonio Huete Jimenez p += 2;
2481a34d5fb1SAntonio Huete Jimenez continue;
2482a34d5fb1SAntonio Huete Jimenez }
2483a34d5fb1SAntonio Huete Jimenez }
2484a34d5fb1SAntonio Huete Jimenez
2485a34d5fb1SAntonio Huete Jimenez /* Nested variable expression */
2486a34d5fb1SAntonio Huete Jimenez if (*p == '$') {
2487*6eef5f0cSAntonio Huete Jimenez FStr val;
2488a34d5fb1SAntonio Huete Jimenez
2489*6eef5f0cSAntonio Huete Jimenez (void)Var_Parse(&p, ch->expr->scope,
2490*6eef5f0cSAntonio Huete Jimenez shouldEval ? ch->expr->emode : VARE_PARSE_ONLY,
2491*6eef5f0cSAntonio Huete Jimenez &val);
2492a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
2493*6eef5f0cSAntonio Huete Jimenez if (shouldEval)
2494*6eef5f0cSAntonio Huete Jimenez LazyBuf_AddStr(buf, val.str);
2495*6eef5f0cSAntonio Huete Jimenez FStr_Done(&val);
2496a34d5fb1SAntonio Huete Jimenez continue;
2497a34d5fb1SAntonio Huete Jimenez }
2498a34d5fb1SAntonio Huete Jimenez
2499a34d5fb1SAntonio Huete Jimenez /* Ordinary text */
2500*6eef5f0cSAntonio Huete Jimenez if (shouldEval)
2501*6eef5f0cSAntonio Huete Jimenez LazyBuf_Add(buf, *p);
2502a34d5fb1SAntonio Huete Jimenez p++;
2503a34d5fb1SAntonio Huete Jimenez }
2504a34d5fb1SAntonio Huete Jimenez *pp = p;
2505*6eef5f0cSAntonio Huete Jimenez }
2506*6eef5f0cSAntonio Huete Jimenez
2507*6eef5f0cSAntonio Huete Jimenez /* :Ddefined or :Uundefined */
2508*6eef5f0cSAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Defined(const char ** pp,ModChain * ch)2509*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Defined(const char **pp, ModChain *ch)
2510*6eef5f0cSAntonio Huete Jimenez {
2511*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
2512*6eef5f0cSAntonio Huete Jimenez LazyBuf buf;
2513*6eef5f0cSAntonio Huete Jimenez bool shouldEval =
2514*6eef5f0cSAntonio Huete Jimenez Expr_ShouldEval(expr) &&
2515*6eef5f0cSAntonio Huete Jimenez (**pp == 'D') == (expr->defined == DEF_REGULAR);
2516*6eef5f0cSAntonio Huete Jimenez
2517*6eef5f0cSAntonio Huete Jimenez ParseModifier_Defined(pp, ch, shouldEval, &buf);
2518a34d5fb1SAntonio Huete Jimenez
2519ec533708SSascha Wildner Expr_Define(expr);
2520*6eef5f0cSAntonio Huete Jimenez if (shouldEval)
2521*6eef5f0cSAntonio Huete Jimenez Expr_SetValue(expr, Substring_Str(LazyBuf_Get(&buf)));
2522ec533708SSascha Wildner
2523a34d5fb1SAntonio Huete Jimenez return AMR_OK;
2524ca58f742SDaniel Fojt }
2525ca58f742SDaniel Fojt
2526a34d5fb1SAntonio Huete Jimenez /* :L */
2527a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Literal(const char ** pp,ModChain * ch)2528*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Literal(const char **pp, ModChain *ch)
2529a34d5fb1SAntonio Huete Jimenez {
2530*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
2531ec533708SSascha Wildner
2532a34d5fb1SAntonio Huete Jimenez (*pp)++;
2533ec533708SSascha Wildner
2534*6eef5f0cSAntonio Huete Jimenez if (Expr_ShouldEval(expr)) {
2535ec533708SSascha Wildner Expr_Define(expr);
2536*6eef5f0cSAntonio Huete Jimenez Expr_SetValueOwn(expr, bmake_strdup(expr->name));
2537ec533708SSascha Wildner }
2538ec533708SSascha Wildner
2539a34d5fb1SAntonio Huete Jimenez return AMR_OK;
2540ca58f742SDaniel Fojt }
2541a34d5fb1SAntonio Huete Jimenez
2542*6eef5f0cSAntonio Huete Jimenez static bool
TryParseTime(const char ** pp,time_t * out_time)2543a34d5fb1SAntonio Huete Jimenez TryParseTime(const char **pp, time_t *out_time)
2544a34d5fb1SAntonio Huete Jimenez {
2545a34d5fb1SAntonio Huete Jimenez char *end;
2546a34d5fb1SAntonio Huete Jimenez unsigned long n;
2547a34d5fb1SAntonio Huete Jimenez
2548a34d5fb1SAntonio Huete Jimenez if (!ch_isdigit(**pp))
2549*6eef5f0cSAntonio Huete Jimenez return false;
2550a34d5fb1SAntonio Huete Jimenez
2551a34d5fb1SAntonio Huete Jimenez errno = 0;
2552a34d5fb1SAntonio Huete Jimenez n = strtoul(*pp, &end, 10);
2553a34d5fb1SAntonio Huete Jimenez if (n == ULONG_MAX && errno == ERANGE)
2554*6eef5f0cSAntonio Huete Jimenez return false;
2555a34d5fb1SAntonio Huete Jimenez
2556a34d5fb1SAntonio Huete Jimenez *pp = end;
2557a34d5fb1SAntonio Huete Jimenez *out_time = (time_t)n; /* ignore possible truncation for now */
2558*6eef5f0cSAntonio Huete Jimenez return true;
2559ca58f742SDaniel Fojt }
2560ca58f742SDaniel Fojt
2561*6eef5f0cSAntonio Huete Jimenez /* :gmtime and :localtime */
2562a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Time(const char ** pp,ModChain * ch)2563*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Time(const char **pp, ModChain *ch)
2564ca58f742SDaniel Fojt {
2565*6eef5f0cSAntonio Huete Jimenez Expr *expr;
2566*6eef5f0cSAntonio Huete Jimenez time_t t;
2567*6eef5f0cSAntonio Huete Jimenez const char *args;
2568a34d5fb1SAntonio Huete Jimenez const char *mod = *pp;
2569*6eef5f0cSAntonio Huete Jimenez bool gmt = mod[0] == 'g';
2570a34d5fb1SAntonio Huete Jimenez
2571*6eef5f0cSAntonio Huete Jimenez if (!ModMatchEq(mod, gmt ? "gmtime" : "localtime", ch))
2572*6eef5f0cSAntonio Huete Jimenez return AMR_UNKNOWN;
2573*6eef5f0cSAntonio Huete Jimenez args = mod + (gmt ? 6 : 9);
2574*6eef5f0cSAntonio Huete Jimenez
2575*6eef5f0cSAntonio Huete Jimenez if (args[0] == '=') {
2576*6eef5f0cSAntonio Huete Jimenez const char *p = args + 1;
2577*6eef5f0cSAntonio Huete Jimenez if (!TryParseTime(&p, &t)) {
2578a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
2579*6eef5f0cSAntonio Huete Jimenez "Invalid time value at \"%s\"", p);
2580a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2581a34d5fb1SAntonio Huete Jimenez }
2582ec533708SSascha Wildner *pp = p;
2583ca58f742SDaniel Fojt } else {
2584*6eef5f0cSAntonio Huete Jimenez t = 0;
2585*6eef5f0cSAntonio Huete Jimenez *pp = args;
2586ca58f742SDaniel Fojt }
2587ec533708SSascha Wildner
2588*6eef5f0cSAntonio Huete Jimenez expr = ch->expr;
2589*6eef5f0cSAntonio Huete Jimenez if (Expr_ShouldEval(expr))
2590*6eef5f0cSAntonio Huete Jimenez Expr_SetValueOwn(expr, VarStrftime(Expr_Str(expr), t, gmt));
2591ec533708SSascha Wildner
2592a34d5fb1SAntonio Huete Jimenez return AMR_OK;
2593ca58f742SDaniel Fojt }
2594ca58f742SDaniel Fojt
2595ca58f742SDaniel Fojt /* :hash */
2596a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Hash(const char ** pp,ModChain * ch)2597*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Hash(const char **pp, ModChain *ch)
2598ca58f742SDaniel Fojt {
2599*6eef5f0cSAntonio Huete Jimenez if (!ModMatch(*pp, "hash", ch))
2600a34d5fb1SAntonio Huete Jimenez return AMR_UNKNOWN;
2601a34d5fb1SAntonio Huete Jimenez *pp += 4;
2602ec533708SSascha Wildner
2603*6eef5f0cSAntonio Huete Jimenez if (ModChain_ShouldEval(ch))
2604*6eef5f0cSAntonio Huete Jimenez Expr_SetValueOwn(ch->expr, VarHash(Expr_Str(ch->expr)));
2605ec533708SSascha Wildner
2606a34d5fb1SAntonio Huete Jimenez return AMR_OK;
2607ca58f742SDaniel Fojt }
2608ca58f742SDaniel Fojt
2609ca58f742SDaniel Fojt /* :P */
2610a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Path(const char ** pp,ModChain * ch)2611*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Path(const char **pp, ModChain *ch)
2612ca58f742SDaniel Fojt {
2613*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
2614ca58f742SDaniel Fojt GNode *gn;
2615a34d5fb1SAntonio Huete Jimenez char *path;
2616ca58f742SDaniel Fojt
2617ec533708SSascha Wildner (*pp)++;
2618a34d5fb1SAntonio Huete Jimenez
2619*6eef5f0cSAntonio Huete Jimenez if (!Expr_ShouldEval(expr))
2620ec533708SSascha Wildner return AMR_OK;
2621ec533708SSascha Wildner
2622ec533708SSascha Wildner Expr_Define(expr);
2623ec533708SSascha Wildner
2624*6eef5f0cSAntonio Huete Jimenez gn = Targ_FindNode(expr->name);
2625ca58f742SDaniel Fojt if (gn == NULL || gn->type & OP_NOPATH) {
2626a34d5fb1SAntonio Huete Jimenez path = NULL;
2627a34d5fb1SAntonio Huete Jimenez } else if (gn->path != NULL) {
2628a34d5fb1SAntonio Huete Jimenez path = bmake_strdup(gn->path);
2629ca58f742SDaniel Fojt } else {
2630a34d5fb1SAntonio Huete Jimenez SearchPath *searchPath = Suff_FindPath(gn);
2631*6eef5f0cSAntonio Huete Jimenez path = Dir_FindFile(expr->name, searchPath);
2632ca58f742SDaniel Fojt }
2633a34d5fb1SAntonio Huete Jimenez if (path == NULL)
2634*6eef5f0cSAntonio Huete Jimenez path = bmake_strdup(expr->name);
2635ec533708SSascha Wildner Expr_SetValueOwn(expr, path);
2636a34d5fb1SAntonio Huete Jimenez
2637a34d5fb1SAntonio Huete Jimenez return AMR_OK;
2638ca58f742SDaniel Fojt }
2639ca58f742SDaniel Fojt
2640ca58f742SDaniel Fojt /* :!cmd! */
2641a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_ShellCommand(const char ** pp,ModChain * ch)2642*6eef5f0cSAntonio Huete Jimenez ApplyModifier_ShellCommand(const char **pp, ModChain *ch)
2643ca58f742SDaniel Fojt {
2644*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
2645a34d5fb1SAntonio Huete Jimenez VarParseResult res;
2646*6eef5f0cSAntonio Huete Jimenez LazyBuf cmdBuf;
2647*6eef5f0cSAntonio Huete Jimenez FStr cmd;
2648ca58f742SDaniel Fojt
2649a34d5fb1SAntonio Huete Jimenez (*pp)++;
2650*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPart(pp, '!', expr->emode, ch, &cmdBuf);
2651a34d5fb1SAntonio Huete Jimenez if (res != VPR_OK)
2652a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2653*6eef5f0cSAntonio Huete Jimenez cmd = LazyBuf_DoneGet(&cmdBuf);
2654ca58f742SDaniel Fojt
2655*6eef5f0cSAntonio Huete Jimenez if (Expr_ShouldEval(expr)) {
2656*6eef5f0cSAntonio Huete Jimenez char *output, *error;
2657*6eef5f0cSAntonio Huete Jimenez output = Cmd_Exec(cmd.str, &error);
2658*6eef5f0cSAntonio Huete Jimenez Expr_SetValueOwn(expr, output);
2659*6eef5f0cSAntonio Huete Jimenez if (error != NULL) {
2660*6eef5f0cSAntonio Huete Jimenez /* XXX: why still return AMR_OK? */
2661*6eef5f0cSAntonio Huete Jimenez Error("%s", error);
2662*6eef5f0cSAntonio Huete Jimenez free(error);
2663*6eef5f0cSAntonio Huete Jimenez }
2664*6eef5f0cSAntonio Huete Jimenez } else
2665ec533708SSascha Wildner Expr_SetValueRefer(expr, "");
2666*6eef5f0cSAntonio Huete Jimenez
2667*6eef5f0cSAntonio Huete Jimenez FStr_Done(&cmd);
2668ec533708SSascha Wildner Expr_Define(expr);
2669a34d5fb1SAntonio Huete Jimenez
2670a34d5fb1SAntonio Huete Jimenez return AMR_OK;
2671ca58f742SDaniel Fojt }
2672ca58f742SDaniel Fojt
2673a34d5fb1SAntonio Huete Jimenez /*
2674a34d5fb1SAntonio Huete Jimenez * The :range modifier generates an integer sequence as long as the words.
2675a34d5fb1SAntonio Huete Jimenez * The :range=7 modifier generates an integer sequence from 1 to 7.
2676a34d5fb1SAntonio Huete Jimenez */
2677a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Range(const char ** pp,ModChain * ch)2678*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Range(const char **pp, ModChain *ch)
2679ca58f742SDaniel Fojt {
2680a34d5fb1SAntonio Huete Jimenez size_t n;
2681a34d5fb1SAntonio Huete Jimenez Buffer buf;
2682a34d5fb1SAntonio Huete Jimenez size_t i;
2683ca58f742SDaniel Fojt
2684a34d5fb1SAntonio Huete Jimenez const char *mod = *pp;
2685*6eef5f0cSAntonio Huete Jimenez if (!ModMatchEq(mod, "range", ch))
2686a34d5fb1SAntonio Huete Jimenez return AMR_UNKNOWN;
2687ca58f742SDaniel Fojt
2688a34d5fb1SAntonio Huete Jimenez if (mod[5] == '=') {
2689a34d5fb1SAntonio Huete Jimenez const char *p = mod + 6;
2690a34d5fb1SAntonio Huete Jimenez if (!TryParseSize(&p, &n)) {
2691a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
2692ec533708SSascha Wildner "Invalid number \"%s\" for ':range' modifier",
2693ec533708SSascha Wildner mod + 6);
2694a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2695a34d5fb1SAntonio Huete Jimenez }
2696a34d5fb1SAntonio Huete Jimenez *pp = p;
2697ca58f742SDaniel Fojt } else {
2698ca58f742SDaniel Fojt n = 0;
2699a34d5fb1SAntonio Huete Jimenez *pp = mod + 5;
2700ca58f742SDaniel Fojt }
2701a34d5fb1SAntonio Huete Jimenez
2702*6eef5f0cSAntonio Huete Jimenez if (!ModChain_ShouldEval(ch))
2703ec533708SSascha Wildner return AMR_OK;
2704ec533708SSascha Wildner
2705a34d5fb1SAntonio Huete Jimenez if (n == 0) {
2706*6eef5f0cSAntonio Huete Jimenez SubstringWords words = Expr_Words(ch->expr);
2707a34d5fb1SAntonio Huete Jimenez n = words.len;
2708*6eef5f0cSAntonio Huete Jimenez SubstringWords_Free(words);
2709a34d5fb1SAntonio Huete Jimenez }
2710a34d5fb1SAntonio Huete Jimenez
2711a34d5fb1SAntonio Huete Jimenez Buf_Init(&buf);
2712a34d5fb1SAntonio Huete Jimenez
2713a34d5fb1SAntonio Huete Jimenez for (i = 0; i < n; i++) {
2714a34d5fb1SAntonio Huete Jimenez if (i != 0) {
2715*6eef5f0cSAntonio Huete Jimenez /*
2716*6eef5f0cSAntonio Huete Jimenez * XXX: Use ch->sep instead of ' ', for consistency.
2717*6eef5f0cSAntonio Huete Jimenez */
2718a34d5fb1SAntonio Huete Jimenez Buf_AddByte(&buf, ' ');
2719a34d5fb1SAntonio Huete Jimenez }
2720a34d5fb1SAntonio Huete Jimenez Buf_AddInt(&buf, 1 + (int)i);
2721a34d5fb1SAntonio Huete Jimenez }
2722a34d5fb1SAntonio Huete Jimenez
2723*6eef5f0cSAntonio Huete Jimenez Expr_SetValueOwn(ch->expr, Buf_DoneData(&buf));
2724a34d5fb1SAntonio Huete Jimenez return AMR_OK;
2725ca58f742SDaniel Fojt }
2726ca58f742SDaniel Fojt
2727ec533708SSascha Wildner /* Parse a ':M' or ':N' modifier. */
2728*6eef5f0cSAntonio Huete Jimenez static char *
ParseModifier_Match(const char ** pp,const ModChain * ch)2729*6eef5f0cSAntonio Huete Jimenez ParseModifier_Match(const char **pp, const ModChain *ch)
2730ca58f742SDaniel Fojt {
2731a34d5fb1SAntonio Huete Jimenez const char *mod = *pp;
2732*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
2733*6eef5f0cSAntonio Huete Jimenez bool copy = false; /* pattern should be, or has been, copied */
2734*6eef5f0cSAntonio Huete Jimenez bool needSubst = false;
2735a34d5fb1SAntonio Huete Jimenez const char *endpat;
2736ca58f742SDaniel Fojt char *pattern;
2737ca58f742SDaniel Fojt
2738ca58f742SDaniel Fojt /*
2739a34d5fb1SAntonio Huete Jimenez * In the loop below, ignore ':' unless we are at (or back to) the
2740a34d5fb1SAntonio Huete Jimenez * original brace level.
2741a34d5fb1SAntonio Huete Jimenez * XXX: This will likely not work right if $() and ${} are intermixed.
2742ca58f742SDaniel Fojt */
2743ec533708SSascha Wildner /*
2744ec533708SSascha Wildner * XXX: This code is similar to the one in Var_Parse.
2745a34d5fb1SAntonio Huete Jimenez * See if the code can be merged.
2746ec533708SSascha Wildner * See also ApplyModifier_Defined.
2747ec533708SSascha Wildner */
2748a34d5fb1SAntonio Huete Jimenez int nest = 0;
2749a34d5fb1SAntonio Huete Jimenez const char *p;
2750a34d5fb1SAntonio Huete Jimenez for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) {
2751*6eef5f0cSAntonio Huete Jimenez if (*p == '\\' && p[1] != '\0' &&
2752*6eef5f0cSAntonio Huete Jimenez (IsDelimiter(p[1], ch) || p[1] == ch->startc)) {
2753ca58f742SDaniel Fojt if (!needSubst)
2754*6eef5f0cSAntonio Huete Jimenez copy = true;
2755a34d5fb1SAntonio Huete Jimenez p++;
2756ca58f742SDaniel Fojt continue;
2757ca58f742SDaniel Fojt }
2758a34d5fb1SAntonio Huete Jimenez if (*p == '$')
2759*6eef5f0cSAntonio Huete Jimenez needSubst = true;
2760a34d5fb1SAntonio Huete Jimenez if (*p == '(' || *p == '{')
2761a34d5fb1SAntonio Huete Jimenez nest++;
2762a34d5fb1SAntonio Huete Jimenez if (*p == ')' || *p == '}') {
2763a34d5fb1SAntonio Huete Jimenez nest--;
2764a34d5fb1SAntonio Huete Jimenez if (nest < 0)
2765ca58f742SDaniel Fojt break;
2766ca58f742SDaniel Fojt }
2767ca58f742SDaniel Fojt }
2768a34d5fb1SAntonio Huete Jimenez *pp = p;
2769a34d5fb1SAntonio Huete Jimenez endpat = p;
2770a34d5fb1SAntonio Huete Jimenez
2771ca58f742SDaniel Fojt if (copy) {
2772a34d5fb1SAntonio Huete Jimenez char *dst;
2773a34d5fb1SAntonio Huete Jimenez const char *src;
2774a34d5fb1SAntonio Huete Jimenez
2775a34d5fb1SAntonio Huete Jimenez /* Compress the \:'s out of the pattern. */
2776a34d5fb1SAntonio Huete Jimenez pattern = bmake_malloc((size_t)(endpat - (mod + 1)) + 1);
2777a34d5fb1SAntonio Huete Jimenez dst = pattern;
2778a34d5fb1SAntonio Huete Jimenez src = mod + 1;
2779a34d5fb1SAntonio Huete Jimenez for (; src < endpat; src++, dst++) {
2780a34d5fb1SAntonio Huete Jimenez if (src[0] == '\\' && src + 1 < endpat &&
2781*6eef5f0cSAntonio Huete Jimenez /* XXX: ch->startc is missing here; see above */
2782*6eef5f0cSAntonio Huete Jimenez IsDelimiter(src[1], ch))
2783a34d5fb1SAntonio Huete Jimenez src++;
2784a34d5fb1SAntonio Huete Jimenez *dst = *src;
2785ca58f742SDaniel Fojt }
2786a34d5fb1SAntonio Huete Jimenez *dst = '\0';
2787ca58f742SDaniel Fojt } else {
2788a34d5fb1SAntonio Huete Jimenez pattern = bmake_strsedup(mod + 1, endpat);
2789ca58f742SDaniel Fojt }
2790a34d5fb1SAntonio Huete Jimenez
2791ca58f742SDaniel Fojt if (needSubst) {
2792a34d5fb1SAntonio Huete Jimenez char *old_pattern = pattern;
2793*6eef5f0cSAntonio Huete Jimenez /*
2794*6eef5f0cSAntonio Huete Jimenez * XXX: Contrary to ParseModifierPart, a dollar in a ':M' or
2795*6eef5f0cSAntonio Huete Jimenez * ':N' modifier must be escaped as '$$', not as '\$'.
2796*6eef5f0cSAntonio Huete Jimenez */
2797*6eef5f0cSAntonio Huete Jimenez (void)Var_Subst(pattern, expr->scope, expr->emode, &pattern);
2798a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
2799a34d5fb1SAntonio Huete Jimenez free(old_pattern);
2800ca58f742SDaniel Fojt }
2801a34d5fb1SAntonio Huete Jimenez
2802*6eef5f0cSAntonio Huete Jimenez DEBUG2(VAR, "Pattern for ':%c' is \"%s\"\n", mod[0], pattern);
2803a34d5fb1SAntonio Huete Jimenez
2804*6eef5f0cSAntonio Huete Jimenez return pattern;
2805ec533708SSascha Wildner }
2806ec533708SSascha Wildner
2807ec533708SSascha Wildner /* :Mpattern or :Npattern */
2808ec533708SSascha Wildner static ApplyModifierResult
ApplyModifier_Match(const char ** pp,ModChain * ch)2809*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Match(const char **pp, ModChain *ch)
2810ec533708SSascha Wildner {
2811*6eef5f0cSAntonio Huete Jimenez char mod = **pp;
2812ec533708SSascha Wildner char *pattern;
2813ec533708SSascha Wildner
2814*6eef5f0cSAntonio Huete Jimenez pattern = ParseModifier_Match(pp, ch);
2815ec533708SSascha Wildner
2816*6eef5f0cSAntonio Huete Jimenez if (ModChain_ShouldEval(ch)) {
2817ec533708SSascha Wildner ModifyWordProc modifyWord =
2818ec533708SSascha Wildner mod == 'M' ? ModifyWord_Match : ModifyWord_NoMatch;
2819*6eef5f0cSAntonio Huete Jimenez ModifyWords(ch, modifyWord, pattern, ch->oneBigWord);
2820ec533708SSascha Wildner }
2821ec533708SSascha Wildner
2822ca58f742SDaniel Fojt free(pattern);
2823a34d5fb1SAntonio Huete Jimenez return AMR_OK;
2824ca58f742SDaniel Fojt }
2825ca58f742SDaniel Fojt
2826ec533708SSascha Wildner static void
ParsePatternFlags(const char ** pp,PatternFlags * pflags,bool * oneBigWord)2827*6eef5f0cSAntonio Huete Jimenez ParsePatternFlags(const char **pp, PatternFlags *pflags, bool *oneBigWord)
2828ec533708SSascha Wildner {
2829ec533708SSascha Wildner for (;; (*pp)++) {
2830ec533708SSascha Wildner if (**pp == 'g')
2831*6eef5f0cSAntonio Huete Jimenez pflags->subGlobal = true;
2832ec533708SSascha Wildner else if (**pp == '1')
2833*6eef5f0cSAntonio Huete Jimenez pflags->subOnce = true;
2834ec533708SSascha Wildner else if (**pp == 'W')
2835*6eef5f0cSAntonio Huete Jimenez *oneBigWord = true;
2836ec533708SSascha Wildner else
2837ec533708SSascha Wildner break;
2838ec533708SSascha Wildner }
2839ec533708SSascha Wildner }
2840ec533708SSascha Wildner
2841*6eef5f0cSAntonio Huete Jimenez MAKE_INLINE PatternFlags
PatternFlags_None(void)2842*6eef5f0cSAntonio Huete Jimenez PatternFlags_None(void)
2843*6eef5f0cSAntonio Huete Jimenez {
2844*6eef5f0cSAntonio Huete Jimenez PatternFlags pflags = { false, false, false, false };
2845*6eef5f0cSAntonio Huete Jimenez return pflags;
2846*6eef5f0cSAntonio Huete Jimenez }
2847*6eef5f0cSAntonio Huete Jimenez
2848ca58f742SDaniel Fojt /* :S,from,to, */
2849a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Subst(const char ** pp,ModChain * ch)2850*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Subst(const char **pp, ModChain *ch)
2851ca58f742SDaniel Fojt {
2852a34d5fb1SAntonio Huete Jimenez struct ModifyWord_SubstArgs args;
2853*6eef5f0cSAntonio Huete Jimenez bool oneBigWord;
2854a34d5fb1SAntonio Huete Jimenez VarParseResult res;
2855*6eef5f0cSAntonio Huete Jimenez LazyBuf lhsBuf, rhsBuf;
2856ca58f742SDaniel Fojt
2857a34d5fb1SAntonio Huete Jimenez char delim = (*pp)[1];
2858a34d5fb1SAntonio Huete Jimenez if (delim == '\0') {
2859ec533708SSascha Wildner Error("Missing delimiter for modifier ':S'");
2860a34d5fb1SAntonio Huete Jimenez (*pp)++;
2861a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2862a34d5fb1SAntonio Huete Jimenez }
2863a34d5fb1SAntonio Huete Jimenez
2864a34d5fb1SAntonio Huete Jimenez *pp += 2;
2865a34d5fb1SAntonio Huete Jimenez
2866*6eef5f0cSAntonio Huete Jimenez args.pflags = PatternFlags_None();
2867*6eef5f0cSAntonio Huete Jimenez args.matched = false;
2868ca58f742SDaniel Fojt
2869a34d5fb1SAntonio Huete Jimenez if (**pp == '^') {
2870*6eef5f0cSAntonio Huete Jimenez args.pflags.anchorStart = true;
2871a34d5fb1SAntonio Huete Jimenez (*pp)++;
2872ca58f742SDaniel Fojt }
2873ca58f742SDaniel Fojt
2874*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &lhsBuf,
2875*6eef5f0cSAntonio Huete Jimenez &args.pflags, NULL);
2876a34d5fb1SAntonio Huete Jimenez if (res != VPR_OK)
2877a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2878*6eef5f0cSAntonio Huete Jimenez args.lhs = LazyBuf_Get(&lhsBuf);
2879ca58f742SDaniel Fojt
2880*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &rhsBuf,
2881*6eef5f0cSAntonio Huete Jimenez NULL, &args);
2882*6eef5f0cSAntonio Huete Jimenez if (res != VPR_OK) {
2883*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&lhsBuf);
2884a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2885*6eef5f0cSAntonio Huete Jimenez }
2886*6eef5f0cSAntonio Huete Jimenez args.rhs = LazyBuf_Get(&rhsBuf);
2887ca58f742SDaniel Fojt
2888*6eef5f0cSAntonio Huete Jimenez oneBigWord = ch->oneBigWord;
2889ec533708SSascha Wildner ParsePatternFlags(pp, &args.pflags, &oneBigWord);
2890ca58f742SDaniel Fojt
2891*6eef5f0cSAntonio Huete Jimenez ModifyWords(ch, ModifyWord_Subst, &args, oneBigWord);
2892ca58f742SDaniel Fojt
2893*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&lhsBuf);
2894*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&rhsBuf);
2895a34d5fb1SAntonio Huete Jimenez return AMR_OK;
2896ca58f742SDaniel Fojt }
2897ca58f742SDaniel Fojt
2898ca58f742SDaniel Fojt #ifndef NO_REGEX
2899a34d5fb1SAntonio Huete Jimenez
2900ca58f742SDaniel Fojt /* :C,from,to, */
2901a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Regex(const char ** pp,ModChain * ch)2902*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Regex(const char **pp, ModChain *ch)
2903ca58f742SDaniel Fojt {
2904a34d5fb1SAntonio Huete Jimenez struct ModifyWord_SubstRegexArgs args;
2905*6eef5f0cSAntonio Huete Jimenez bool oneBigWord;
2906ca58f742SDaniel Fojt int error;
2907a34d5fb1SAntonio Huete Jimenez VarParseResult res;
2908*6eef5f0cSAntonio Huete Jimenez LazyBuf reBuf, replaceBuf;
2909*6eef5f0cSAntonio Huete Jimenez FStr re;
2910ca58f742SDaniel Fojt
2911a34d5fb1SAntonio Huete Jimenez char delim = (*pp)[1];
2912a34d5fb1SAntonio Huete Jimenez if (delim == '\0') {
2913a34d5fb1SAntonio Huete Jimenez Error("Missing delimiter for :C modifier");
2914a34d5fb1SAntonio Huete Jimenez (*pp)++;
2915a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2916ca58f742SDaniel Fojt }
2917ca58f742SDaniel Fojt
2918a34d5fb1SAntonio Huete Jimenez *pp += 2;
2919a34d5fb1SAntonio Huete Jimenez
2920*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPart(pp, delim, ch->expr->emode, ch, &reBuf);
2921a34d5fb1SAntonio Huete Jimenez if (res != VPR_OK)
2922a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2923*6eef5f0cSAntonio Huete Jimenez re = LazyBuf_DoneGet(&reBuf);
2924a34d5fb1SAntonio Huete Jimenez
2925*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPart(pp, delim, ch->expr->emode, ch, &replaceBuf);
2926*6eef5f0cSAntonio Huete Jimenez if (res != VPR_OK) {
2927*6eef5f0cSAntonio Huete Jimenez FStr_Done(&re);
2928a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2929a34d5fb1SAntonio Huete Jimenez }
2930*6eef5f0cSAntonio Huete Jimenez args.replace = LazyBuf_Get(&replaceBuf);
2931a34d5fb1SAntonio Huete Jimenez
2932*6eef5f0cSAntonio Huete Jimenez args.pflags = PatternFlags_None();
2933*6eef5f0cSAntonio Huete Jimenez args.matched = false;
2934*6eef5f0cSAntonio Huete Jimenez oneBigWord = ch->oneBigWord;
2935ec533708SSascha Wildner ParsePatternFlags(pp, &args.pflags, &oneBigWord);
2936ec533708SSascha Wildner
2937*6eef5f0cSAntonio Huete Jimenez if (!ModChain_ShouldEval(ch)) {
2938*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&replaceBuf);
2939*6eef5f0cSAntonio Huete Jimenez FStr_Done(&re);
2940ec533708SSascha Wildner return AMR_OK;
2941ca58f742SDaniel Fojt }
2942ca58f742SDaniel Fojt
2943*6eef5f0cSAntonio Huete Jimenez error = regcomp(&args.re, re.str, REG_EXTENDED);
2944a34d5fb1SAntonio Huete Jimenez if (error != 0) {
2945a34d5fb1SAntonio Huete Jimenez VarREError(error, &args.re, "Regex compilation error");
2946*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&replaceBuf);
2947*6eef5f0cSAntonio Huete Jimenez FStr_Done(&re);
2948a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
2949ca58f742SDaniel Fojt }
2950ca58f742SDaniel Fojt
2951a34d5fb1SAntonio Huete Jimenez args.nsub = args.re.re_nsub + 1;
2952a34d5fb1SAntonio Huete Jimenez if (args.nsub > 10)
2953a34d5fb1SAntonio Huete Jimenez args.nsub = 10;
2954ec533708SSascha Wildner
2955*6eef5f0cSAntonio Huete Jimenez ModifyWords(ch, ModifyWord_SubstRegex, &args, oneBigWord);
2956ec533708SSascha Wildner
2957a34d5fb1SAntonio Huete Jimenez regfree(&args.re);
2958*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&replaceBuf);
2959*6eef5f0cSAntonio Huete Jimenez FStr_Done(&re);
2960a34d5fb1SAntonio Huete Jimenez return AMR_OK;
2961ca58f742SDaniel Fojt }
2962a34d5fb1SAntonio Huete Jimenez
2963ca58f742SDaniel Fojt #endif
2964ca58f742SDaniel Fojt
2965a34d5fb1SAntonio Huete Jimenez /* :Q, :q */
2966a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Quote(const char ** pp,ModChain * ch)2967*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Quote(const char **pp, ModChain *ch)
2968ca58f742SDaniel Fojt {
2969*6eef5f0cSAntonio Huete Jimenez LazyBuf buf;
2970*6eef5f0cSAntonio Huete Jimenez bool quoteDollar;
2971*6eef5f0cSAntonio Huete Jimenez
2972*6eef5f0cSAntonio Huete Jimenez quoteDollar = **pp == 'q';
2973*6eef5f0cSAntonio Huete Jimenez if (!IsDelimiter((*pp)[1], ch))
2974a34d5fb1SAntonio Huete Jimenez return AMR_UNKNOWN;
2975ec533708SSascha Wildner (*pp)++;
2976ec533708SSascha Wildner
2977*6eef5f0cSAntonio Huete Jimenez if (!ModChain_ShouldEval(ch))
2978*6eef5f0cSAntonio Huete Jimenez return AMR_OK;
2979*6eef5f0cSAntonio Huete Jimenez
2980*6eef5f0cSAntonio Huete Jimenez VarQuote(Expr_Str(ch->expr), quoteDollar, &buf);
2981*6eef5f0cSAntonio Huete Jimenez if (buf.data != NULL)
2982*6eef5f0cSAntonio Huete Jimenez Expr_SetValue(ch->expr, LazyBuf_DoneGet(&buf));
2983*6eef5f0cSAntonio Huete Jimenez else
2984*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&buf);
2985ec533708SSascha Wildner
2986ec533708SSascha Wildner return AMR_OK;
2987a34d5fb1SAntonio Huete Jimenez }
2988ca58f742SDaniel Fojt
2989a34d5fb1SAntonio Huete Jimenez /*ARGSUSED*/
2990a34d5fb1SAntonio Huete Jimenez static void
ModifyWord_Copy(Substring word,SepBuf * buf,void * data MAKE_ATTR_UNUSED)2991*6eef5f0cSAntonio Huete Jimenez ModifyWord_Copy(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
2992a34d5fb1SAntonio Huete Jimenez {
2993*6eef5f0cSAntonio Huete Jimenez SepBuf_AddSubstring(buf, word);
2994a34d5fb1SAntonio Huete Jimenez }
2995a34d5fb1SAntonio Huete Jimenez
2996a34d5fb1SAntonio Huete Jimenez /* :ts<separator> */
2997a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_ToSep(const char ** pp,ModChain * ch)2998*6eef5f0cSAntonio Huete Jimenez ApplyModifier_ToSep(const char **pp, ModChain *ch)
2999a34d5fb1SAntonio Huete Jimenez {
3000a34d5fb1SAntonio Huete Jimenez const char *sep = *pp + 2;
3001a34d5fb1SAntonio Huete Jimenez
3002ec533708SSascha Wildner /*
3003*6eef5f0cSAntonio Huete Jimenez * Even in parse-only mode, proceed as normal since there is
3004ec533708SSascha Wildner * neither any observable side effect nor a performance penalty.
3005*6eef5f0cSAntonio Huete Jimenez * Checking for wantRes for every single piece of code in here
3006ec533708SSascha Wildner * would make the code in this function too hard to read.
3007ec533708SSascha Wildner */
3008ec533708SSascha Wildner
3009a34d5fb1SAntonio Huete Jimenez /* ":ts<any><endc>" or ":ts<any>:" */
3010*6eef5f0cSAntonio Huete Jimenez if (sep[0] != ch->endc && IsDelimiter(sep[1], ch)) {
3011a34d5fb1SAntonio Huete Jimenez *pp = sep + 1;
3012*6eef5f0cSAntonio Huete Jimenez ch->sep = sep[0];
3013a34d5fb1SAntonio Huete Jimenez goto ok;
3014a34d5fb1SAntonio Huete Jimenez }
3015a34d5fb1SAntonio Huete Jimenez
3016ca58f742SDaniel Fojt /* ":ts<endc>" or ":ts:" */
3017*6eef5f0cSAntonio Huete Jimenez if (IsDelimiter(sep[0], ch)) {
3018a34d5fb1SAntonio Huete Jimenez *pp = sep;
3019*6eef5f0cSAntonio Huete Jimenez ch->sep = '\0'; /* no separator */
3020a34d5fb1SAntonio Huete Jimenez goto ok;
3021a34d5fb1SAntonio Huete Jimenez }
3022a34d5fb1SAntonio Huete Jimenez
3023a34d5fb1SAntonio Huete Jimenez /* ":ts<unrecognised><unrecognised>". */
3024a34d5fb1SAntonio Huete Jimenez if (sep[0] != '\\') {
3025a34d5fb1SAntonio Huete Jimenez (*pp)++; /* just for backwards compatibility */
3026a34d5fb1SAntonio Huete Jimenez return AMR_BAD;
3027a34d5fb1SAntonio Huete Jimenez }
3028a34d5fb1SAntonio Huete Jimenez
3029a34d5fb1SAntonio Huete Jimenez /* ":ts\n" */
3030a34d5fb1SAntonio Huete Jimenez if (sep[1] == 'n') {
3031a34d5fb1SAntonio Huete Jimenez *pp = sep + 2;
3032*6eef5f0cSAntonio Huete Jimenez ch->sep = '\n';
3033a34d5fb1SAntonio Huete Jimenez goto ok;
3034a34d5fb1SAntonio Huete Jimenez }
3035a34d5fb1SAntonio Huete Jimenez
3036a34d5fb1SAntonio Huete Jimenez /* ":ts\t" */
3037a34d5fb1SAntonio Huete Jimenez if (sep[1] == 't') {
3038a34d5fb1SAntonio Huete Jimenez *pp = sep + 2;
3039*6eef5f0cSAntonio Huete Jimenez ch->sep = '\t';
3040a34d5fb1SAntonio Huete Jimenez goto ok;
3041a34d5fb1SAntonio Huete Jimenez }
3042a34d5fb1SAntonio Huete Jimenez
3043a34d5fb1SAntonio Huete Jimenez /* ":ts\x40" or ":ts\100" */
3044a34d5fb1SAntonio Huete Jimenez {
3045a34d5fb1SAntonio Huete Jimenez const char *p = sep + 1;
3046ca58f742SDaniel Fojt int base = 8; /* assume octal */
3047ca58f742SDaniel Fojt
3048a34d5fb1SAntonio Huete Jimenez if (sep[1] == 'x') {
3049ca58f742SDaniel Fojt base = 16;
3050a34d5fb1SAntonio Huete Jimenez p++;
3051a34d5fb1SAntonio Huete Jimenez } else if (!ch_isdigit(sep[1])) {
3052a34d5fb1SAntonio Huete Jimenez (*pp)++; /* just for backwards compatibility */
3053a34d5fb1SAntonio Huete Jimenez return AMR_BAD; /* ":ts<backslash><unrecognised>". */
3054ca58f742SDaniel Fojt }
3055ca58f742SDaniel Fojt
3056*6eef5f0cSAntonio Huete Jimenez if (!TryParseChar(&p, base, &ch->sep)) {
3057a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
3058*6eef5f0cSAntonio Huete Jimenez "Invalid character number at \"%s\"", p);
3059a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
3060a34d5fb1SAntonio Huete Jimenez }
3061*6eef5f0cSAntonio Huete Jimenez if (!IsDelimiter(*p, ch)) {
3062a34d5fb1SAntonio Huete Jimenez (*pp)++; /* just for backwards compatibility */
3063a34d5fb1SAntonio Huete Jimenez return AMR_BAD;
3064a34d5fb1SAntonio Huete Jimenez }
3065ca58f742SDaniel Fojt
3066a34d5fb1SAntonio Huete Jimenez *pp = p;
3067a34d5fb1SAntonio Huete Jimenez }
3068ca58f742SDaniel Fojt
3069a34d5fb1SAntonio Huete Jimenez ok:
3070*6eef5f0cSAntonio Huete Jimenez ModifyWords(ch, ModifyWord_Copy, NULL, ch->oneBigWord);
3071a34d5fb1SAntonio Huete Jimenez return AMR_OK;
3072a34d5fb1SAntonio Huete Jimenez }
3073a34d5fb1SAntonio Huete Jimenez
3074a34d5fb1SAntonio Huete Jimenez static char *
str_toupper(const char * str)3075a34d5fb1SAntonio Huete Jimenez str_toupper(const char *str)
3076a34d5fb1SAntonio Huete Jimenez {
3077a34d5fb1SAntonio Huete Jimenez char *res;
3078a34d5fb1SAntonio Huete Jimenez size_t i, len;
3079a34d5fb1SAntonio Huete Jimenez
3080a34d5fb1SAntonio Huete Jimenez len = strlen(str);
3081a34d5fb1SAntonio Huete Jimenez res = bmake_malloc(len + 1);
3082a34d5fb1SAntonio Huete Jimenez for (i = 0; i < len + 1; i++)
3083a34d5fb1SAntonio Huete Jimenez res[i] = ch_toupper(str[i]);
3084a34d5fb1SAntonio Huete Jimenez
3085a34d5fb1SAntonio Huete Jimenez return res;
3086a34d5fb1SAntonio Huete Jimenez }
3087a34d5fb1SAntonio Huete Jimenez
3088a34d5fb1SAntonio Huete Jimenez static char *
str_tolower(const char * str)3089a34d5fb1SAntonio Huete Jimenez str_tolower(const char *str)
3090a34d5fb1SAntonio Huete Jimenez {
3091a34d5fb1SAntonio Huete Jimenez char *res;
3092a34d5fb1SAntonio Huete Jimenez size_t i, len;
3093a34d5fb1SAntonio Huete Jimenez
3094a34d5fb1SAntonio Huete Jimenez len = strlen(str);
3095a34d5fb1SAntonio Huete Jimenez res = bmake_malloc(len + 1);
3096a34d5fb1SAntonio Huete Jimenez for (i = 0; i < len + 1; i++)
3097a34d5fb1SAntonio Huete Jimenez res[i] = ch_tolower(str[i]);
3098a34d5fb1SAntonio Huete Jimenez
3099a34d5fb1SAntonio Huete Jimenez return res;
3100a34d5fb1SAntonio Huete Jimenez }
3101a34d5fb1SAntonio Huete Jimenez
3102a34d5fb1SAntonio Huete Jimenez /* :tA, :tu, :tl, :ts<separator>, etc. */
3103a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_To(const char ** pp,ModChain * ch)3104*6eef5f0cSAntonio Huete Jimenez ApplyModifier_To(const char **pp, ModChain *ch)
3105a34d5fb1SAntonio Huete Jimenez {
3106*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
3107a34d5fb1SAntonio Huete Jimenez const char *mod = *pp;
3108a34d5fb1SAntonio Huete Jimenez assert(mod[0] == 't');
3109a34d5fb1SAntonio Huete Jimenez
3110*6eef5f0cSAntonio Huete Jimenez if (IsDelimiter(mod[1], ch)) {
3111a34d5fb1SAntonio Huete Jimenez *pp = mod + 1;
3112a34d5fb1SAntonio Huete Jimenez return AMR_BAD; /* Found ":t<endc>" or ":t:". */
3113a34d5fb1SAntonio Huete Jimenez }
3114a34d5fb1SAntonio Huete Jimenez
3115a34d5fb1SAntonio Huete Jimenez if (mod[1] == 's')
3116*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_ToSep(pp, ch);
3117a34d5fb1SAntonio Huete Jimenez
3118*6eef5f0cSAntonio Huete Jimenez if (!IsDelimiter(mod[2], ch)) { /* :t<any><any> */
3119a34d5fb1SAntonio Huete Jimenez *pp = mod + 1;
3120ec533708SSascha Wildner return AMR_BAD;
3121a34d5fb1SAntonio Huete Jimenez }
3122a34d5fb1SAntonio Huete Jimenez
3123ec533708SSascha Wildner if (mod[1] == 'A') { /* :tA */
3124a34d5fb1SAntonio Huete Jimenez *pp = mod + 2;
3125*6eef5f0cSAntonio Huete Jimenez ModifyWords(ch, ModifyWord_Realpath, NULL, ch->oneBigWord);
3126a34d5fb1SAntonio Huete Jimenez return AMR_OK;
3127a34d5fb1SAntonio Huete Jimenez }
3128a34d5fb1SAntonio Huete Jimenez
3129a34d5fb1SAntonio Huete Jimenez if (mod[1] == 'u') { /* :tu */
3130a34d5fb1SAntonio Huete Jimenez *pp = mod + 2;
3131*6eef5f0cSAntonio Huete Jimenez if (Expr_ShouldEval(expr))
3132*6eef5f0cSAntonio Huete Jimenez Expr_SetValueOwn(expr, str_toupper(Expr_Str(expr)));
3133a34d5fb1SAntonio Huete Jimenez return AMR_OK;
3134a34d5fb1SAntonio Huete Jimenez }
3135a34d5fb1SAntonio Huete Jimenez
3136a34d5fb1SAntonio Huete Jimenez if (mod[1] == 'l') { /* :tl */
3137a34d5fb1SAntonio Huete Jimenez *pp = mod + 2;
3138*6eef5f0cSAntonio Huete Jimenez if (Expr_ShouldEval(expr))
3139*6eef5f0cSAntonio Huete Jimenez Expr_SetValueOwn(expr, str_tolower(Expr_Str(expr)));
3140a34d5fb1SAntonio Huete Jimenez return AMR_OK;
3141a34d5fb1SAntonio Huete Jimenez }
3142a34d5fb1SAntonio Huete Jimenez
3143a34d5fb1SAntonio Huete Jimenez if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */
3144a34d5fb1SAntonio Huete Jimenez *pp = mod + 2;
3145*6eef5f0cSAntonio Huete Jimenez ch->oneBigWord = mod[1] == 'W';
3146a34d5fb1SAntonio Huete Jimenez return AMR_OK;
3147a34d5fb1SAntonio Huete Jimenez }
3148a34d5fb1SAntonio Huete Jimenez
3149ca58f742SDaniel Fojt /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */
3150ec533708SSascha Wildner *pp = mod + 1; /* XXX: unnecessary but observable */
3151a34d5fb1SAntonio Huete Jimenez return AMR_BAD;
3152ca58f742SDaniel Fojt }
3153ca58f742SDaniel Fojt
3154a34d5fb1SAntonio Huete Jimenez /* :[#], :[1], :[-1..1], etc. */
3155a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Words(const char ** pp,ModChain * ch)3156*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Words(const char **pp, ModChain *ch)
3157ca58f742SDaniel Fojt {
3158*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
3159*6eef5f0cSAntonio Huete Jimenez const char *estr;
3160a34d5fb1SAntonio Huete Jimenez int first, last;
3161a34d5fb1SAntonio Huete Jimenez VarParseResult res;
3162a34d5fb1SAntonio Huete Jimenez const char *p;
3163*6eef5f0cSAntonio Huete Jimenez LazyBuf estrBuf;
3164*6eef5f0cSAntonio Huete Jimenez FStr festr;
3165ca58f742SDaniel Fojt
3166a34d5fb1SAntonio Huete Jimenez (*pp)++; /* skip the '[' */
3167*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPart(pp, ']', expr->emode, ch, &estrBuf);
3168a34d5fb1SAntonio Huete Jimenez if (res != VPR_OK)
3169a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
3170*6eef5f0cSAntonio Huete Jimenez festr = LazyBuf_DoneGet(&estrBuf);
3171*6eef5f0cSAntonio Huete Jimenez estr = festr.str;
3172ca58f742SDaniel Fojt
3173*6eef5f0cSAntonio Huete Jimenez if (!IsDelimiter(**pp, ch))
3174a34d5fb1SAntonio Huete Jimenez goto bad_modifier; /* Found junk after ']' */
3175ca58f742SDaniel Fojt
3176*6eef5f0cSAntonio Huete Jimenez if (!ModChain_ShouldEval(ch))
3177ec533708SSascha Wildner goto ok;
3178ec533708SSascha Wildner
3179a34d5fb1SAntonio Huete Jimenez if (estr[0] == '\0')
3180ec533708SSascha Wildner goto bad_modifier; /* Found ":[]". */
3181a34d5fb1SAntonio Huete Jimenez
3182a34d5fb1SAntonio Huete Jimenez if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */
3183*6eef5f0cSAntonio Huete Jimenez if (ch->oneBigWord) {
3184ec533708SSascha Wildner Expr_SetValueRefer(expr, "1");
3185ca58f742SDaniel Fojt } else {
3186a34d5fb1SAntonio Huete Jimenez Buffer buf;
3187ca58f742SDaniel Fojt
3188*6eef5f0cSAntonio Huete Jimenez SubstringWords words = Expr_Words(expr);
3189a34d5fb1SAntonio Huete Jimenez size_t ac = words.len;
3190*6eef5f0cSAntonio Huete Jimenez SubstringWords_Free(words);
3191a34d5fb1SAntonio Huete Jimenez
3192a34d5fb1SAntonio Huete Jimenez /* 3 digits + '\0' is usually enough */
3193a34d5fb1SAntonio Huete Jimenez Buf_InitSize(&buf, 4);
3194a34d5fb1SAntonio Huete Jimenez Buf_AddInt(&buf, (int)ac);
3195ec533708SSascha Wildner Expr_SetValueOwn(expr, Buf_DoneData(&buf));
3196ca58f742SDaniel Fojt }
3197a34d5fb1SAntonio Huete Jimenez goto ok;
3198a34d5fb1SAntonio Huete Jimenez }
3199a34d5fb1SAntonio Huete Jimenez
3200ec533708SSascha Wildner if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */
3201*6eef5f0cSAntonio Huete Jimenez ch->oneBigWord = true;
3202a34d5fb1SAntonio Huete Jimenez goto ok;
3203a34d5fb1SAntonio Huete Jimenez }
3204a34d5fb1SAntonio Huete Jimenez
3205ec533708SSascha Wildner if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */
3206*6eef5f0cSAntonio Huete Jimenez ch->oneBigWord = false;
3207a34d5fb1SAntonio Huete Jimenez goto ok;
3208a34d5fb1SAntonio Huete Jimenez }
3209ca58f742SDaniel Fojt
3210a34d5fb1SAntonio Huete Jimenez /*
3211a34d5fb1SAntonio Huete Jimenez * We expect estr to contain a single integer for :[N], or two
3212a34d5fb1SAntonio Huete Jimenez * integers separated by ".." for :[start..end].
3213a34d5fb1SAntonio Huete Jimenez */
3214a34d5fb1SAntonio Huete Jimenez p = estr;
3215a34d5fb1SAntonio Huete Jimenez if (!TryParseIntBase0(&p, &first))
3216a34d5fb1SAntonio Huete Jimenez goto bad_modifier; /* Found junk instead of a number */
3217a34d5fb1SAntonio Huete Jimenez
3218a34d5fb1SAntonio Huete Jimenez if (p[0] == '\0') { /* Found only one integer in :[N] */
3219a34d5fb1SAntonio Huete Jimenez last = first;
3220a34d5fb1SAntonio Huete Jimenez } else if (p[0] == '.' && p[1] == '.' && p[2] != '\0') {
3221ca58f742SDaniel Fojt /* Expecting another integer after ".." */
3222a34d5fb1SAntonio Huete Jimenez p += 2;
3223a34d5fb1SAntonio Huete Jimenez if (!TryParseIntBase0(&p, &last) || *p != '\0')
3224a34d5fb1SAntonio Huete Jimenez goto bad_modifier; /* Found junk after ".." */
3225a34d5fb1SAntonio Huete Jimenez } else
3226a34d5fb1SAntonio Huete Jimenez goto bad_modifier; /* Found junk instead of ".." */
3227a34d5fb1SAntonio Huete Jimenez
3228ca58f742SDaniel Fojt /*
3229a34d5fb1SAntonio Huete Jimenez * Now first and last are properly filled in, but we still have to
3230a34d5fb1SAntonio Huete Jimenez * check for 0 as a special case.
3231ca58f742SDaniel Fojt */
3232a34d5fb1SAntonio Huete Jimenez if (first == 0 && last == 0) {
3233ca58f742SDaniel Fojt /* ":[0]" or perhaps ":[0..0]" */
3234*6eef5f0cSAntonio Huete Jimenez ch->oneBigWord = true;
3235a34d5fb1SAntonio Huete Jimenez goto ok;
3236a34d5fb1SAntonio Huete Jimenez }
3237a34d5fb1SAntonio Huete Jimenez
3238ca58f742SDaniel Fojt /* ":[0..N]" or ":[N..0]" */
3239a34d5fb1SAntonio Huete Jimenez if (first == 0 || last == 0)
3240a34d5fb1SAntonio Huete Jimenez goto bad_modifier;
3241ca58f742SDaniel Fojt
3242a34d5fb1SAntonio Huete Jimenez /* Normal case: select the words described by first and last. */
3243ec533708SSascha Wildner Expr_SetValueOwn(expr,
3244*6eef5f0cSAntonio Huete Jimenez VarSelectWords(Expr_Str(expr), first, last,
3245*6eef5f0cSAntonio Huete Jimenez ch->sep, ch->oneBigWord));
3246a34d5fb1SAntonio Huete Jimenez
3247a34d5fb1SAntonio Huete Jimenez ok:
3248*6eef5f0cSAntonio Huete Jimenez FStr_Done(&festr);
3249a34d5fb1SAntonio Huete Jimenez return AMR_OK;
3250a34d5fb1SAntonio Huete Jimenez
3251a34d5fb1SAntonio Huete Jimenez bad_modifier:
3252*6eef5f0cSAntonio Huete Jimenez FStr_Done(&festr);
3253a34d5fb1SAntonio Huete Jimenez return AMR_BAD;
3254ca58f742SDaniel Fojt }
3255ca58f742SDaniel Fojt
3256*6eef5f0cSAntonio Huete Jimenez #if __STDC__ >= 199901L || defined(HAVE_LONG_LONG_INT)
3257*6eef5f0cSAntonio Huete Jimenez # define NUM_TYPE long long
3258*6eef5f0cSAntonio Huete Jimenez # define PARSE_NUM_TYPE strtoll
3259*6eef5f0cSAntonio Huete Jimenez #else
3260*6eef5f0cSAntonio Huete Jimenez # define NUM_TYPE long
3261*6eef5f0cSAntonio Huete Jimenez # define PARSE_NUM_TYPE strtol
3262*6eef5f0cSAntonio Huete Jimenez #endif
3263*6eef5f0cSAntonio Huete Jimenez
3264*6eef5f0cSAntonio Huete Jimenez static NUM_TYPE
num_val(Substring s)3265*6eef5f0cSAntonio Huete Jimenez num_val(Substring s)
3266ca58f742SDaniel Fojt {
3267*6eef5f0cSAntonio Huete Jimenez NUM_TYPE val;
3268*6eef5f0cSAntonio Huete Jimenez char *ep;
3269*6eef5f0cSAntonio Huete Jimenez
3270*6eef5f0cSAntonio Huete Jimenez val = PARSE_NUM_TYPE(s.start, &ep, 0);
3271*6eef5f0cSAntonio Huete Jimenez if (ep != s.start) {
3272*6eef5f0cSAntonio Huete Jimenez switch (*ep) {
3273*6eef5f0cSAntonio Huete Jimenez case 'K':
3274*6eef5f0cSAntonio Huete Jimenez case 'k':
3275*6eef5f0cSAntonio Huete Jimenez val <<= 10;
3276*6eef5f0cSAntonio Huete Jimenez break;
3277*6eef5f0cSAntonio Huete Jimenez case 'M':
3278*6eef5f0cSAntonio Huete Jimenez case 'm':
3279*6eef5f0cSAntonio Huete Jimenez val <<= 20;
3280*6eef5f0cSAntonio Huete Jimenez break;
3281*6eef5f0cSAntonio Huete Jimenez case 'G':
3282*6eef5f0cSAntonio Huete Jimenez case 'g':
3283*6eef5f0cSAntonio Huete Jimenez val <<= 30;
3284*6eef5f0cSAntonio Huete Jimenez break;
3285*6eef5f0cSAntonio Huete Jimenez }
3286*6eef5f0cSAntonio Huete Jimenez }
3287*6eef5f0cSAntonio Huete Jimenez return val;
3288ca58f742SDaniel Fojt }
3289a34d5fb1SAntonio Huete Jimenez
3290a34d5fb1SAntonio Huete Jimenez static int
SubNumAsc(const void * sa,const void * sb)3291*6eef5f0cSAntonio Huete Jimenez SubNumAsc(const void *sa, const void *sb)
3292a34d5fb1SAntonio Huete Jimenez {
3293*6eef5f0cSAntonio Huete Jimenez NUM_TYPE a, b;
3294*6eef5f0cSAntonio Huete Jimenez
3295*6eef5f0cSAntonio Huete Jimenez a = num_val(*((const Substring *)sa));
3296*6eef5f0cSAntonio Huete Jimenez b = num_val(*((const Substring *)sb));
3297*6eef5f0cSAntonio Huete Jimenez return (a > b) ? 1 : (b > a) ? -1 : 0;
3298*6eef5f0cSAntonio Huete Jimenez }
3299*6eef5f0cSAntonio Huete Jimenez
3300*6eef5f0cSAntonio Huete Jimenez static int
SubNumDesc(const void * sa,const void * sb)3301*6eef5f0cSAntonio Huete Jimenez SubNumDesc(const void *sa, const void *sb)
3302*6eef5f0cSAntonio Huete Jimenez {
3303*6eef5f0cSAntonio Huete Jimenez return SubNumAsc(sb, sa);
3304*6eef5f0cSAntonio Huete Jimenez }
3305*6eef5f0cSAntonio Huete Jimenez
3306*6eef5f0cSAntonio Huete Jimenez static int
SubStrAsc(const void * sa,const void * sb)3307*6eef5f0cSAntonio Huete Jimenez SubStrAsc(const void *sa, const void *sb)
3308*6eef5f0cSAntonio Huete Jimenez {
3309*6eef5f0cSAntonio Huete Jimenez return strcmp(
3310*6eef5f0cSAntonio Huete Jimenez ((const Substring *)sa)->start, ((const Substring *)sb)->start);
3311*6eef5f0cSAntonio Huete Jimenez }
3312*6eef5f0cSAntonio Huete Jimenez
3313*6eef5f0cSAntonio Huete Jimenez static int
SubStrDesc(const void * sa,const void * sb)3314*6eef5f0cSAntonio Huete Jimenez SubStrDesc(const void *sa, const void *sb)
3315*6eef5f0cSAntonio Huete Jimenez {
3316*6eef5f0cSAntonio Huete Jimenez return SubStrAsc(sb, sa);
3317a34d5fb1SAntonio Huete Jimenez }
3318a34d5fb1SAntonio Huete Jimenez
3319a34d5fb1SAntonio Huete Jimenez static void
ShuffleSubstrings(Substring * strs,size_t n)3320*6eef5f0cSAntonio Huete Jimenez ShuffleSubstrings(Substring *strs, size_t n)
3321a34d5fb1SAntonio Huete Jimenez {
3322a34d5fb1SAntonio Huete Jimenez size_t i;
3323a34d5fb1SAntonio Huete Jimenez
3324a34d5fb1SAntonio Huete Jimenez for (i = n - 1; i > 0; i--) {
3325a34d5fb1SAntonio Huete Jimenez size_t rndidx = (size_t)random() % (i + 1);
3326*6eef5f0cSAntonio Huete Jimenez Substring t = strs[i];
3327a34d5fb1SAntonio Huete Jimenez strs[i] = strs[rndidx];
3328a34d5fb1SAntonio Huete Jimenez strs[rndidx] = t;
3329a34d5fb1SAntonio Huete Jimenez }
3330a34d5fb1SAntonio Huete Jimenez }
3331a34d5fb1SAntonio Huete Jimenez
3332*6eef5f0cSAntonio Huete Jimenez /*
3333*6eef5f0cSAntonio Huete Jimenez * :O order ascending
3334*6eef5f0cSAntonio Huete Jimenez * :Or order descending
3335*6eef5f0cSAntonio Huete Jimenez * :Ox shuffle
3336*6eef5f0cSAntonio Huete Jimenez * :On numeric ascending
3337*6eef5f0cSAntonio Huete Jimenez * :Onr, :Orn numeric descending
3338*6eef5f0cSAntonio Huete Jimenez */
3339a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Order(const char ** pp,ModChain * ch)3340*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Order(const char **pp, ModChain *ch)
3341a34d5fb1SAntonio Huete Jimenez {
3342*6eef5f0cSAntonio Huete Jimenez const char *mod = *pp;
3343*6eef5f0cSAntonio Huete Jimenez SubstringWords words;
3344*6eef5f0cSAntonio Huete Jimenez int (*cmp)(const void *, const void *);
3345a34d5fb1SAntonio Huete Jimenez
3346*6eef5f0cSAntonio Huete Jimenez if (IsDelimiter(mod[1], ch)) {
3347*6eef5f0cSAntonio Huete Jimenez cmp = SubStrAsc;
3348a34d5fb1SAntonio Huete Jimenez (*pp)++;
3349*6eef5f0cSAntonio Huete Jimenez } else if (IsDelimiter(mod[2], ch)) {
3350*6eef5f0cSAntonio Huete Jimenez if (mod[1] == 'n')
3351*6eef5f0cSAntonio Huete Jimenez cmp = SubNumAsc;
3352*6eef5f0cSAntonio Huete Jimenez else if (mod[1] == 'r')
3353*6eef5f0cSAntonio Huete Jimenez cmp = SubStrDesc;
3354*6eef5f0cSAntonio Huete Jimenez else if (mod[1] == 'x')
3355*6eef5f0cSAntonio Huete Jimenez cmp = NULL;
3356ec533708SSascha Wildner else
3357*6eef5f0cSAntonio Huete Jimenez goto bad;
3358*6eef5f0cSAntonio Huete Jimenez *pp += 2;
3359*6eef5f0cSAntonio Huete Jimenez } else if (IsDelimiter(mod[3], ch)) {
3360*6eef5f0cSAntonio Huete Jimenez if ((mod[1] == 'n' && mod[2] == 'r') ||
3361*6eef5f0cSAntonio Huete Jimenez (mod[1] == 'r' && mod[2] == 'n'))
3362*6eef5f0cSAntonio Huete Jimenez cmp = SubNumDesc;
3363*6eef5f0cSAntonio Huete Jimenez else
3364*6eef5f0cSAntonio Huete Jimenez goto bad;
3365*6eef5f0cSAntonio Huete Jimenez *pp += 3;
3366*6eef5f0cSAntonio Huete Jimenez } else
3367*6eef5f0cSAntonio Huete Jimenez goto bad;
3368*6eef5f0cSAntonio Huete Jimenez
3369*6eef5f0cSAntonio Huete Jimenez if (!ModChain_ShouldEval(ch))
3370*6eef5f0cSAntonio Huete Jimenez return AMR_OK;
3371*6eef5f0cSAntonio Huete Jimenez
3372*6eef5f0cSAntonio Huete Jimenez words = Expr_Words(ch->expr);
3373*6eef5f0cSAntonio Huete Jimenez if (cmp == NULL)
3374*6eef5f0cSAntonio Huete Jimenez ShuffleSubstrings(words.words, words.len);
3375*6eef5f0cSAntonio Huete Jimenez else {
3376*6eef5f0cSAntonio Huete Jimenez assert(words.words[0].end[0] == '\0');
3377*6eef5f0cSAntonio Huete Jimenez qsort(words.words, words.len, sizeof(words.words[0]), cmp);
3378*6eef5f0cSAntonio Huete Jimenez }
3379*6eef5f0cSAntonio Huete Jimenez Expr_SetValueOwn(ch->expr, SubstringWords_JoinFree(words));
3380ec533708SSascha Wildner
3381a34d5fb1SAntonio Huete Jimenez return AMR_OK;
3382*6eef5f0cSAntonio Huete Jimenez
3383*6eef5f0cSAntonio Huete Jimenez bad:
3384*6eef5f0cSAntonio Huete Jimenez (*pp)++;
3385*6eef5f0cSAntonio Huete Jimenez return AMR_BAD;
3386ca58f742SDaniel Fojt }
3387ca58f742SDaniel Fojt
3388ca58f742SDaniel Fojt /* :? then : else */
3389a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_IfElse(const char ** pp,ModChain * ch)3390*6eef5f0cSAntonio Huete Jimenez ApplyModifier_IfElse(const char **pp, ModChain *ch)
3391ca58f742SDaniel Fojt {
3392*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
3393a34d5fb1SAntonio Huete Jimenez VarParseResult res;
3394*6eef5f0cSAntonio Huete Jimenez LazyBuf thenBuf;
3395*6eef5f0cSAntonio Huete Jimenez LazyBuf elseBuf;
3396ca58f742SDaniel Fojt
3397*6eef5f0cSAntonio Huete Jimenez VarEvalMode then_emode = VARE_PARSE_ONLY;
3398*6eef5f0cSAntonio Huete Jimenez VarEvalMode else_emode = VARE_PARSE_ONLY;
3399a34d5fb1SAntonio Huete Jimenez
3400*6eef5f0cSAntonio Huete Jimenez CondResult cond_rc = CR_TRUE; /* just not CR_ERROR */
3401*6eef5f0cSAntonio Huete Jimenez if (Expr_ShouldEval(expr)) {
3402*6eef5f0cSAntonio Huete Jimenez cond_rc = Cond_EvalCondition(expr->name);
3403*6eef5f0cSAntonio Huete Jimenez if (cond_rc == CR_TRUE)
3404*6eef5f0cSAntonio Huete Jimenez then_emode = expr->emode;
3405*6eef5f0cSAntonio Huete Jimenez if (cond_rc == CR_FALSE)
3406*6eef5f0cSAntonio Huete Jimenez else_emode = expr->emode;
3407ca58f742SDaniel Fojt }
3408ca58f742SDaniel Fojt
3409a34d5fb1SAntonio Huete Jimenez (*pp)++; /* skip past the '?' */
3410*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPart(pp, ':', then_emode, ch, &thenBuf);
3411a34d5fb1SAntonio Huete Jimenez if (res != VPR_OK)
3412a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
3413ca58f742SDaniel Fojt
3414*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPart(pp, ch->endc, else_emode, ch, &elseBuf);
3415*6eef5f0cSAntonio Huete Jimenez if (res != VPR_OK) {
3416*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&thenBuf);
3417a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
3418ca58f742SDaniel Fojt }
3419ca58f742SDaniel Fojt
3420*6eef5f0cSAntonio Huete Jimenez (*pp)--; /* Go back to the ch->endc. */
3421*6eef5f0cSAntonio Huete Jimenez
3422*6eef5f0cSAntonio Huete Jimenez if (cond_rc == CR_ERROR) {
3423*6eef5f0cSAntonio Huete Jimenez Substring thenExpr = LazyBuf_Get(&thenBuf);
3424*6eef5f0cSAntonio Huete Jimenez Substring elseExpr = LazyBuf_Get(&elseBuf);
3425*6eef5f0cSAntonio Huete Jimenez Error("Bad conditional expression '%s' in '%s?%.*s:%.*s'",
3426*6eef5f0cSAntonio Huete Jimenez expr->name, expr->name,
3427*6eef5f0cSAntonio Huete Jimenez (int)Substring_Length(thenExpr), thenExpr.start,
3428*6eef5f0cSAntonio Huete Jimenez (int)Substring_Length(elseExpr), elseExpr.start);
3429*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&thenBuf);
3430*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&elseBuf);
3431*6eef5f0cSAntonio Huete Jimenez return AMR_CLEANUP;
3432*6eef5f0cSAntonio Huete Jimenez }
3433*6eef5f0cSAntonio Huete Jimenez
3434*6eef5f0cSAntonio Huete Jimenez if (!Expr_ShouldEval(expr)) {
3435*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&thenBuf);
3436*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&elseBuf);
3437*6eef5f0cSAntonio Huete Jimenez } else if (cond_rc == CR_TRUE) {
3438*6eef5f0cSAntonio Huete Jimenez Expr_SetValue(expr, LazyBuf_DoneGet(&thenBuf));
3439*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&elseBuf);
3440ca58f742SDaniel Fojt } else {
3441*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&thenBuf);
3442*6eef5f0cSAntonio Huete Jimenez Expr_SetValue(expr, LazyBuf_DoneGet(&elseBuf));
3443ca58f742SDaniel Fojt }
3444ec533708SSascha Wildner Expr_Define(expr);
3445a34d5fb1SAntonio Huete Jimenez return AMR_OK;
3446ca58f742SDaniel Fojt }
3447ca58f742SDaniel Fojt
3448ca58f742SDaniel Fojt /*
3449ec533708SSascha Wildner * The ::= modifiers are special in that they do not read the variable value
3450ec533708SSascha Wildner * but instead assign to that variable. They always expand to an empty
3451ec533708SSascha Wildner * string.
345201e196c8SJohn Marino *
3453ec533708SSascha Wildner * Their main purpose is in supporting .for loops that generate shell commands
3454ec533708SSascha Wildner * since an ordinary variable assignment at that point would terminate the
3455ec533708SSascha Wildner * dependency group for these targets. For example:
3456ec533708SSascha Wildner *
3457ec533708SSascha Wildner * list-targets: .USE
345801e196c8SJohn Marino * .for i in ${.TARGET} ${.TARGET:R}.gz
3459ec533708SSascha Wildner * @${t::=$i}
3460ec533708SSascha Wildner * @echo 'The target is ${t:T}.'
346101e196c8SJohn Marino * .endfor
346201e196c8SJohn Marino *
346301e196c8SJohn Marino * ::=<str> Assigns <str> as the new value of variable.
346401e196c8SJohn Marino * ::?=<str> Assigns <str> as value of variable if
346501e196c8SJohn Marino * it was not already set.
346601e196c8SJohn Marino * ::+=<str> Appends <str> to variable.
346701e196c8SJohn Marino * ::!=<cmd> Assigns output of <cmd> as the new value of
346801e196c8SJohn Marino * variable.
346901e196c8SJohn Marino */
3470a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Assign(const char ** pp,ModChain * ch)3471*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Assign(const char **pp, ModChain *ch)
347201e196c8SJohn Marino {
3473*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
3474a34d5fb1SAntonio Huete Jimenez GNode *scope;
3475*6eef5f0cSAntonio Huete Jimenez FStr val;
3476a34d5fb1SAntonio Huete Jimenez VarParseResult res;
3477*6eef5f0cSAntonio Huete Jimenez LazyBuf buf;
347801e196c8SJohn Marino
3479a34d5fb1SAntonio Huete Jimenez const char *mod = *pp;
3480a34d5fb1SAntonio Huete Jimenez const char *op = mod + 1;
348101e196c8SJohn Marino
3482a34d5fb1SAntonio Huete Jimenez if (op[0] == '=')
3483*6eef5f0cSAntonio Huete Jimenez goto found_op;
3484*6eef5f0cSAntonio Huete Jimenez if ((op[0] == '+' || op[0] == '?' || op[0] == '!') && op[1] == '=')
3485*6eef5f0cSAntonio Huete Jimenez goto found_op;
3486a34d5fb1SAntonio Huete Jimenez return AMR_UNKNOWN; /* "::<unrecognised>" */
348701e196c8SJohn Marino
3488*6eef5f0cSAntonio Huete Jimenez found_op:
3489*6eef5f0cSAntonio Huete Jimenez if (expr->name[0] == '\0') {
3490a34d5fb1SAntonio Huete Jimenez *pp = mod + 1;
3491a34d5fb1SAntonio Huete Jimenez return AMR_BAD;
3492a34d5fb1SAntonio Huete Jimenez }
3493a34d5fb1SAntonio Huete Jimenez
3494*6eef5f0cSAntonio Huete Jimenez *pp = mod + (op[0] == '+' || op[0] == '?' || op[0] == '!' ? 3 : 2);
3495a34d5fb1SAntonio Huete Jimenez
3496*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &buf);
3497a34d5fb1SAntonio Huete Jimenez if (res != VPR_OK)
3498a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
3499*6eef5f0cSAntonio Huete Jimenez val = LazyBuf_DoneGet(&buf);
3500a34d5fb1SAntonio Huete Jimenez
3501*6eef5f0cSAntonio Huete Jimenez (*pp)--; /* Go back to the ch->endc. */
3502ec533708SSascha Wildner
3503*6eef5f0cSAntonio Huete Jimenez if (!Expr_ShouldEval(expr))
3504ec533708SSascha Wildner goto done;
3505ec533708SSascha Wildner
3506ec533708SSascha Wildner scope = expr->scope; /* scope where v belongs */
3507ec533708SSascha Wildner if (expr->defined == DEF_REGULAR && expr->scope != SCOPE_GLOBAL) {
3508*6eef5f0cSAntonio Huete Jimenez Var *v = VarFind(expr->name, expr->scope, false);
3509*6eef5f0cSAntonio Huete Jimenez if (v == NULL)
3510ec533708SSascha Wildner scope = SCOPE_GLOBAL;
3511ec533708SSascha Wildner else
3512*6eef5f0cSAntonio Huete Jimenez VarFreeShortLived(v);
3513ec533708SSascha Wildner }
3514a34d5fb1SAntonio Huete Jimenez
3515*6eef5f0cSAntonio Huete Jimenez if (op[0] == '+')
3516*6eef5f0cSAntonio Huete Jimenez Var_Append(scope, expr->name, val.str);
3517*6eef5f0cSAntonio Huete Jimenez else if (op[0] == '!') {
3518*6eef5f0cSAntonio Huete Jimenez char *output, *error;
3519*6eef5f0cSAntonio Huete Jimenez output = Cmd_Exec(val.str, &error);
3520*6eef5f0cSAntonio Huete Jimenez if (error != NULL) {
3521*6eef5f0cSAntonio Huete Jimenez Error("%s", error);
3522*6eef5f0cSAntonio Huete Jimenez free(error);
3523*6eef5f0cSAntonio Huete Jimenez } else
3524*6eef5f0cSAntonio Huete Jimenez Var_Set(scope, expr->name, output);
3525*6eef5f0cSAntonio Huete Jimenez free(output);
3526*6eef5f0cSAntonio Huete Jimenez } else if (op[0] == '?' && expr->defined == DEF_REGULAR) {
3527*6eef5f0cSAntonio Huete Jimenez /* Do nothing. */
3528*6eef5f0cSAntonio Huete Jimenez } else
3529*6eef5f0cSAntonio Huete Jimenez Var_Set(scope, expr->name, val.str);
3530*6eef5f0cSAntonio Huete Jimenez
3531ec533708SSascha Wildner Expr_SetValueRefer(expr, "");
3532ec533708SSascha Wildner
3533ec533708SSascha Wildner done:
3534*6eef5f0cSAntonio Huete Jimenez FStr_Done(&val);
3535a34d5fb1SAntonio Huete Jimenez return AMR_OK;
3536a34d5fb1SAntonio Huete Jimenez }
353701e196c8SJohn Marino
353801e196c8SJohn Marino /*
3539a34d5fb1SAntonio Huete Jimenez * :_=...
3540a34d5fb1SAntonio Huete Jimenez * remember current value
354101e196c8SJohn Marino */
3542a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Remember(const char ** pp,ModChain * ch)3543*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Remember(const char **pp, ModChain *ch)
3544a34d5fb1SAntonio Huete Jimenez {
3545*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
3546a34d5fb1SAntonio Huete Jimenez const char *mod = *pp;
3547ec533708SSascha Wildner FStr name;
3548ec533708SSascha Wildner
3549*6eef5f0cSAntonio Huete Jimenez if (!ModMatchEq(mod, "_", ch))
3550a34d5fb1SAntonio Huete Jimenez return AMR_UNKNOWN;
3551a34d5fb1SAntonio Huete Jimenez
3552ec533708SSascha Wildner name = FStr_InitRefer("_");
3553a34d5fb1SAntonio Huete Jimenez if (mod[1] == '=') {
3554ec533708SSascha Wildner /*
3555ec533708SSascha Wildner * XXX: This ad-hoc call to strcspn deviates from the usual
3556ec533708SSascha Wildner * behavior defined in ParseModifierPart. This creates an
3557ec533708SSascha Wildner * unnecessary, undocumented inconsistency in make.
3558ec533708SSascha Wildner */
3559ec533708SSascha Wildner const char *arg = mod + 2;
3560ec533708SSascha Wildner size_t argLen = strcspn(arg, ":)}");
3561ec533708SSascha Wildner *pp = arg + argLen;
3562ec533708SSascha Wildner name = FStr_InitOwn(bmake_strldup(arg, argLen));
3563ec533708SSascha Wildner } else
3564a34d5fb1SAntonio Huete Jimenez *pp = mod + 1;
3565ec533708SSascha Wildner
3566*6eef5f0cSAntonio Huete Jimenez if (Expr_ShouldEval(expr))
3567*6eef5f0cSAntonio Huete Jimenez Var_Set(expr->scope, name.str, Expr_Str(expr));
3568ec533708SSascha Wildner FStr_Done(&name);
3569ec533708SSascha Wildner
3570a34d5fb1SAntonio Huete Jimenez return AMR_OK;
357101e196c8SJohn Marino }
357201e196c8SJohn Marino
3573a34d5fb1SAntonio Huete Jimenez /*
3574a34d5fb1SAntonio Huete Jimenez * Apply the given function to each word of the variable value,
3575a34d5fb1SAntonio Huete Jimenez * for a single-letter modifier such as :H, :T.
3576a34d5fb1SAntonio Huete Jimenez */
3577a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_WordFunc(const char ** pp,ModChain * ch,ModifyWordProc modifyWord)3578*6eef5f0cSAntonio Huete Jimenez ApplyModifier_WordFunc(const char **pp, ModChain *ch,
3579ec533708SSascha Wildner ModifyWordProc modifyWord)
3580a34d5fb1SAntonio Huete Jimenez {
3581*6eef5f0cSAntonio Huete Jimenez if (!IsDelimiter((*pp)[1], ch))
3582a34d5fb1SAntonio Huete Jimenez return AMR_UNKNOWN;
3583a34d5fb1SAntonio Huete Jimenez (*pp)++;
3584ec533708SSascha Wildner
3585*6eef5f0cSAntonio Huete Jimenez if (ModChain_ShouldEval(ch))
3586*6eef5f0cSAntonio Huete Jimenez ModifyWords(ch, modifyWord, NULL, ch->oneBigWord);
3587ec533708SSascha Wildner
3588a34d5fb1SAntonio Huete Jimenez return AMR_OK;
358901e196c8SJohn Marino }
359001e196c8SJohn Marino
3591*6eef5f0cSAntonio Huete Jimenez /* Remove adjacent duplicate words. */
3592a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_Unique(const char ** pp,ModChain * ch)3593*6eef5f0cSAntonio Huete Jimenez ApplyModifier_Unique(const char **pp, ModChain *ch)
3594a34d5fb1SAntonio Huete Jimenez {
3595*6eef5f0cSAntonio Huete Jimenez SubstringWords words;
3596*6eef5f0cSAntonio Huete Jimenez
3597*6eef5f0cSAntonio Huete Jimenez if (!IsDelimiter((*pp)[1], ch))
3598a34d5fb1SAntonio Huete Jimenez return AMR_UNKNOWN;
3599ec533708SSascha Wildner (*pp)++;
3600ec533708SSascha Wildner
3601*6eef5f0cSAntonio Huete Jimenez if (!ModChain_ShouldEval(ch))
3602*6eef5f0cSAntonio Huete Jimenez return AMR_OK;
3603*6eef5f0cSAntonio Huete Jimenez
3604*6eef5f0cSAntonio Huete Jimenez words = Expr_Words(ch->expr);
3605*6eef5f0cSAntonio Huete Jimenez
3606*6eef5f0cSAntonio Huete Jimenez if (words.len > 1) {
3607*6eef5f0cSAntonio Huete Jimenez size_t si, di;
3608*6eef5f0cSAntonio Huete Jimenez
3609*6eef5f0cSAntonio Huete Jimenez di = 0;
3610*6eef5f0cSAntonio Huete Jimenez for (si = 1; si < words.len; si++) {
3611*6eef5f0cSAntonio Huete Jimenez if (!Substring_Eq(words.words[si], words.words[di])) {
3612*6eef5f0cSAntonio Huete Jimenez di++;
3613*6eef5f0cSAntonio Huete Jimenez if (di != si)
3614*6eef5f0cSAntonio Huete Jimenez words.words[di] = words.words[si];
3615*6eef5f0cSAntonio Huete Jimenez }
3616*6eef5f0cSAntonio Huete Jimenez }
3617*6eef5f0cSAntonio Huete Jimenez words.len = di + 1;
3618*6eef5f0cSAntonio Huete Jimenez }
3619*6eef5f0cSAntonio Huete Jimenez
3620*6eef5f0cSAntonio Huete Jimenez Expr_SetValueOwn(ch->expr, SubstringWords_JoinFree(words));
3621ec533708SSascha Wildner
3622ec533708SSascha Wildner return AMR_OK;
3623a34d5fb1SAntonio Huete Jimenez }
362401e196c8SJohn Marino
3625a34d5fb1SAntonio Huete Jimenez #ifdef SYSVVARSUB
3626a34d5fb1SAntonio Huete Jimenez /* :from=to */
3627a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_SysV(const char ** pp,ModChain * ch)3628*6eef5f0cSAntonio Huete Jimenez ApplyModifier_SysV(const char **pp, ModChain *ch)
3629a34d5fb1SAntonio Huete Jimenez {
3630*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
3631a34d5fb1SAntonio Huete Jimenez VarParseResult res;
3632*6eef5f0cSAntonio Huete Jimenez LazyBuf lhsBuf, rhsBuf;
3633*6eef5f0cSAntonio Huete Jimenez FStr rhs;
3634*6eef5f0cSAntonio Huete Jimenez struct ModifyWord_SysVSubstArgs args;
3635*6eef5f0cSAntonio Huete Jimenez Substring lhs;
3636*6eef5f0cSAntonio Huete Jimenez const char *lhsSuffix;
363701e196c8SJohn Marino
3638a34d5fb1SAntonio Huete Jimenez const char *mod = *pp;
3639*6eef5f0cSAntonio Huete Jimenez bool eqFound = false;
3640a34d5fb1SAntonio Huete Jimenez
3641a34d5fb1SAntonio Huete Jimenez /*
3642a34d5fb1SAntonio Huete Jimenez * First we make a pass through the string trying to verify it is a
3643a34d5fb1SAntonio Huete Jimenez * SysV-make-style translation. It must be: <lhs>=<rhs>
3644a34d5fb1SAntonio Huete Jimenez */
3645a34d5fb1SAntonio Huete Jimenez int depth = 1;
3646a34d5fb1SAntonio Huete Jimenez const char *p = mod;
3647a34d5fb1SAntonio Huete Jimenez while (*p != '\0' && depth > 0) {
3648a34d5fb1SAntonio Huete Jimenez if (*p == '=') { /* XXX: should also test depth == 1 */
3649*6eef5f0cSAntonio Huete Jimenez eqFound = true;
3650*6eef5f0cSAntonio Huete Jimenez /* continue looking for ch->endc */
3651*6eef5f0cSAntonio Huete Jimenez } else if (*p == ch->endc)
3652a34d5fb1SAntonio Huete Jimenez depth--;
3653*6eef5f0cSAntonio Huete Jimenez else if (*p == ch->startc)
3654a34d5fb1SAntonio Huete Jimenez depth++;
3655a34d5fb1SAntonio Huete Jimenez if (depth > 0)
3656a34d5fb1SAntonio Huete Jimenez p++;
365701e196c8SJohn Marino }
3658*6eef5f0cSAntonio Huete Jimenez if (*p != ch->endc || !eqFound)
3659a34d5fb1SAntonio Huete Jimenez return AMR_UNKNOWN;
3660a34d5fb1SAntonio Huete Jimenez
3661*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPart(pp, '=', expr->emode, ch, &lhsBuf);
3662a34d5fb1SAntonio Huete Jimenez if (res != VPR_OK)
3663a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
3664a34d5fb1SAntonio Huete Jimenez
3665*6eef5f0cSAntonio Huete Jimenez /*
3666*6eef5f0cSAntonio Huete Jimenez * The SysV modifier lasts until the end of the variable expression.
3667*6eef5f0cSAntonio Huete Jimenez */
3668*6eef5f0cSAntonio Huete Jimenez res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &rhsBuf);
3669*6eef5f0cSAntonio Huete Jimenez if (res != VPR_OK) {
3670*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&lhsBuf);
3671a34d5fb1SAntonio Huete Jimenez return AMR_CLEANUP;
367201e196c8SJohn Marino }
3673*6eef5f0cSAntonio Huete Jimenez rhs = LazyBuf_DoneGet(&rhsBuf);
3674*6eef5f0cSAntonio Huete Jimenez
3675*6eef5f0cSAntonio Huete Jimenez (*pp)--; /* Go back to the ch->endc. */
3676*6eef5f0cSAntonio Huete Jimenez
3677*6eef5f0cSAntonio Huete Jimenez /* Do not turn an empty expression into non-empty. */
3678*6eef5f0cSAntonio Huete Jimenez if (lhsBuf.len == 0 && Expr_Str(expr)[0] == '\0')
3679*6eef5f0cSAntonio Huete Jimenez goto done;
3680*6eef5f0cSAntonio Huete Jimenez
3681*6eef5f0cSAntonio Huete Jimenez lhs = LazyBuf_Get(&lhsBuf);
3682*6eef5f0cSAntonio Huete Jimenez lhsSuffix = Substring_SkipFirst(lhs, '%');
3683*6eef5f0cSAntonio Huete Jimenez
3684*6eef5f0cSAntonio Huete Jimenez args.scope = expr->scope;
3685*6eef5f0cSAntonio Huete Jimenez args.lhsPrefix = Substring_Init(lhs.start,
3686*6eef5f0cSAntonio Huete Jimenez lhsSuffix != lhs.start ? lhsSuffix - 1 : lhs.start);
3687*6eef5f0cSAntonio Huete Jimenez args.lhsPercent = lhsSuffix != lhs.start;
3688*6eef5f0cSAntonio Huete Jimenez args.lhsSuffix = Substring_Init(lhsSuffix, lhs.end);
3689*6eef5f0cSAntonio Huete Jimenez args.rhs = rhs.str;
3690*6eef5f0cSAntonio Huete Jimenez
3691*6eef5f0cSAntonio Huete Jimenez ModifyWords(ch, ModifyWord_SysVSubst, &args, ch->oneBigWord);
3692*6eef5f0cSAntonio Huete Jimenez
3693*6eef5f0cSAntonio Huete Jimenez done:
3694*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&lhsBuf);
3695a34d5fb1SAntonio Huete Jimenez return AMR_OK;
369601e196c8SJohn Marino }
3697a34d5fb1SAntonio Huete Jimenez #endif
3698a34d5fb1SAntonio Huete Jimenez
3699a34d5fb1SAntonio Huete Jimenez #ifdef SUNSHCMD
3700a34d5fb1SAntonio Huete Jimenez /* :sh */
3701a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier_SunShell(const char ** pp,ModChain * ch)3702*6eef5f0cSAntonio Huete Jimenez ApplyModifier_SunShell(const char **pp, ModChain *ch)
3703a34d5fb1SAntonio Huete Jimenez {
3704*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
3705a34d5fb1SAntonio Huete Jimenez const char *p = *pp;
3706*6eef5f0cSAntonio Huete Jimenez if (!(p[1] == 'h' && IsDelimiter(p[2], ch)))
3707a34d5fb1SAntonio Huete Jimenez return AMR_UNKNOWN;
3708ec533708SSascha Wildner *pp = p + 2;
3709ec533708SSascha Wildner
3710*6eef5f0cSAntonio Huete Jimenez if (Expr_ShouldEval(expr)) {
3711*6eef5f0cSAntonio Huete Jimenez char *output, *error;
3712*6eef5f0cSAntonio Huete Jimenez output = Cmd_Exec(Expr_Str(expr), &error);
3713*6eef5f0cSAntonio Huete Jimenez if (error != NULL) {
3714*6eef5f0cSAntonio Huete Jimenez Error("%s", error);
3715*6eef5f0cSAntonio Huete Jimenez free(error);
3716*6eef5f0cSAntonio Huete Jimenez }
3717ec533708SSascha Wildner Expr_SetValueOwn(expr, output);
3718ec533708SSascha Wildner }
3719ec533708SSascha Wildner
3720ec533708SSascha Wildner return AMR_OK;
372101e196c8SJohn Marino }
3722a34d5fb1SAntonio Huete Jimenez #endif
3723a34d5fb1SAntonio Huete Jimenez
3724a34d5fb1SAntonio Huete Jimenez static void
LogBeforeApply(const ModChain * ch,const char * mod)3725*6eef5f0cSAntonio Huete Jimenez LogBeforeApply(const ModChain *ch, const char *mod)
3726a34d5fb1SAntonio Huete Jimenez {
3727*6eef5f0cSAntonio Huete Jimenez const Expr *expr = ch->expr;
3728*6eef5f0cSAntonio Huete Jimenez bool is_single_char = mod[0] != '\0' && IsDelimiter(mod[1], ch);
3729a34d5fb1SAntonio Huete Jimenez
3730*6eef5f0cSAntonio Huete Jimenez /*
3731*6eef5f0cSAntonio Huete Jimenez * At this point, only the first character of the modifier can
3732*6eef5f0cSAntonio Huete Jimenez * be used since the end of the modifier is not yet known.
3733*6eef5f0cSAntonio Huete Jimenez */
3734*6eef5f0cSAntonio Huete Jimenez
3735*6eef5f0cSAntonio Huete Jimenez if (!Expr_ShouldEval(expr)) {
3736*6eef5f0cSAntonio Huete Jimenez debug_printf("Parsing modifier ${%s:%c%s}\n",
3737*6eef5f0cSAntonio Huete Jimenez expr->name, mod[0], is_single_char ? "" : "...");
3738*6eef5f0cSAntonio Huete Jimenez return;
3739*6eef5f0cSAntonio Huete Jimenez }
3740*6eef5f0cSAntonio Huete Jimenez
3741*6eef5f0cSAntonio Huete Jimenez if ((expr->emode == VARE_WANTRES || expr->emode == VARE_UNDEFERR) &&
3742*6eef5f0cSAntonio Huete Jimenez expr->defined == DEF_REGULAR) {
3743*6eef5f0cSAntonio Huete Jimenez debug_printf(
3744*6eef5f0cSAntonio Huete Jimenez "Evaluating modifier ${%s:%c%s} on value \"%s\"\n",
3745*6eef5f0cSAntonio Huete Jimenez expr->name, mod[0], is_single_char ? "" : "...",
3746*6eef5f0cSAntonio Huete Jimenez Expr_Str(expr));
3747*6eef5f0cSAntonio Huete Jimenez return;
3748*6eef5f0cSAntonio Huete Jimenez }
3749*6eef5f0cSAntonio Huete Jimenez
3750*6eef5f0cSAntonio Huete Jimenez debug_printf(
3751*6eef5f0cSAntonio Huete Jimenez "Evaluating modifier ${%s:%c%s} on value \"%s\" (%s, %s)\n",
3752*6eef5f0cSAntonio Huete Jimenez expr->name, mod[0], is_single_char ? "" : "...", Expr_Str(expr),
3753*6eef5f0cSAntonio Huete Jimenez VarEvalMode_Name[expr->emode], ExprDefined_Name[expr->defined]);
375401e196c8SJohn Marino }
3755a34d5fb1SAntonio Huete Jimenez
3756a34d5fb1SAntonio Huete Jimenez static void
LogAfterApply(const ModChain * ch,const char * p,const char * mod)3757*6eef5f0cSAntonio Huete Jimenez LogAfterApply(const ModChain *ch, const char *p, const char *mod)
3758a34d5fb1SAntonio Huete Jimenez {
3759*6eef5f0cSAntonio Huete Jimenez const Expr *expr = ch->expr;
3760*6eef5f0cSAntonio Huete Jimenez const char *value = Expr_Str(expr);
3761ec533708SSascha Wildner const char *quot = value == var_Error ? "" : "\"";
3762a34d5fb1SAntonio Huete Jimenez
3763*6eef5f0cSAntonio Huete Jimenez if ((expr->emode == VARE_WANTRES || expr->emode == VARE_UNDEFERR) &&
3764*6eef5f0cSAntonio Huete Jimenez expr->defined == DEF_REGULAR) {
3765*6eef5f0cSAntonio Huete Jimenez
3766*6eef5f0cSAntonio Huete Jimenez debug_printf("Result of ${%s:%.*s} is %s%s%s\n",
3767*6eef5f0cSAntonio Huete Jimenez expr->name, (int)(p - mod), mod,
3768*6eef5f0cSAntonio Huete Jimenez quot, value == var_Error ? "error" : value, quot);
3769*6eef5f0cSAntonio Huete Jimenez return;
3770*6eef5f0cSAntonio Huete Jimenez }
3771*6eef5f0cSAntonio Huete Jimenez
3772*6eef5f0cSAntonio Huete Jimenez debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s)\n",
3773*6eef5f0cSAntonio Huete Jimenez expr->name, (int)(p - mod), mod,
3774ec533708SSascha Wildner quot, value == var_Error ? "error" : value, quot,
3775*6eef5f0cSAntonio Huete Jimenez VarEvalMode_Name[expr->emode],
3776ec533708SSascha Wildner ExprDefined_Name[expr->defined]);
3777a34d5fb1SAntonio Huete Jimenez }
3778a34d5fb1SAntonio Huete Jimenez
3779a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplyModifier(const char ** pp,ModChain * ch)3780*6eef5f0cSAntonio Huete Jimenez ApplyModifier(const char **pp, ModChain *ch)
3781a34d5fb1SAntonio Huete Jimenez {
3782a34d5fb1SAntonio Huete Jimenez switch (**pp) {
378301e196c8SJohn Marino case '!':
3784*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_ShellCommand(pp, ch);
3785ec533708SSascha Wildner case ':':
3786*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Assign(pp, ch);
378701e196c8SJohn Marino case '?':
3788*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_IfElse(pp, ch);
3789ec533708SSascha Wildner case '@':
3790*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Loop(pp, ch);
3791ec533708SSascha Wildner case '[':
3792*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Words(pp, ch);
3793ec533708SSascha Wildner case '_':
3794*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Remember(pp, ch);
379501e196c8SJohn Marino #ifndef NO_REGEX
379601e196c8SJohn Marino case 'C':
3797*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Regex(pp, ch);
379801e196c8SJohn Marino #endif
3799ec533708SSascha Wildner case 'D':
3800*6eef5f0cSAntonio Huete Jimenez case 'U':
3801*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Defined(pp, ch);
380201e196c8SJohn Marino case 'E':
3803*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_WordFunc(pp, ch, ModifyWord_Suffix);
3804ec533708SSascha Wildner case 'g':
3805ec533708SSascha Wildner case 'l':
3806*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Time(pp, ch);
3807*6eef5f0cSAntonio Huete Jimenez case 'H':
3808*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_WordFunc(pp, ch, ModifyWord_Head);
3809*6eef5f0cSAntonio Huete Jimenez case 'h':
3810*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Hash(pp, ch);
3811*6eef5f0cSAntonio Huete Jimenez case 'L':
3812*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Literal(pp, ch);
3813ec533708SSascha Wildner case 'M':
3814ec533708SSascha Wildner case 'N':
3815*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Match(pp, ch);
3816ca58f742SDaniel Fojt case 'O':
3817*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Order(pp, ch);
3818ec533708SSascha Wildner case 'P':
3819*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Path(pp, ch);
3820ec533708SSascha Wildner case 'Q':
3821ec533708SSascha Wildner case 'q':
3822*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Quote(pp, ch);
3823ec533708SSascha Wildner case 'R':
3824*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_WordFunc(pp, ch, ModifyWord_Root);
3825ec533708SSascha Wildner case 'r':
3826*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Range(pp, ch);
3827ec533708SSascha Wildner case 'S':
3828*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Subst(pp, ch);
382901e196c8SJohn Marino #ifdef SUNSHCMD
383001e196c8SJohn Marino case 's':
3831*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_SunShell(pp, ch);
383201e196c8SJohn Marino #endif
3833ec533708SSascha Wildner case 'T':
3834*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_WordFunc(pp, ch, ModifyWord_Tail);
3835ec533708SSascha Wildner case 't':
3836*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_To(pp, ch);
3837ec533708SSascha Wildner case 'u':
3838*6eef5f0cSAntonio Huete Jimenez return ApplyModifier_Unique(pp, ch);
383901e196c8SJohn Marino default:
3840a34d5fb1SAntonio Huete Jimenez return AMR_UNKNOWN;
384101e196c8SJohn Marino }
384201e196c8SJohn Marino }
384301e196c8SJohn Marino
3844ec533708SSascha Wildner static void ApplyModifiers(Expr *, const char **, char, char);
3845a34d5fb1SAntonio Huete Jimenez
3846a34d5fb1SAntonio Huete Jimenez typedef enum ApplyModifiersIndirectResult {
3847a34d5fb1SAntonio Huete Jimenez /* The indirect modifiers have been applied successfully. */
3848a34d5fb1SAntonio Huete Jimenez AMIR_CONTINUE,
3849a34d5fb1SAntonio Huete Jimenez /* Fall back to the SysV modifier. */
3850ec533708SSascha Wildner AMIR_SYSV,
3851a34d5fb1SAntonio Huete Jimenez /* Error out. */
3852a34d5fb1SAntonio Huete Jimenez AMIR_OUT
3853a34d5fb1SAntonio Huete Jimenez } ApplyModifiersIndirectResult;
3854a34d5fb1SAntonio Huete Jimenez
3855a34d5fb1SAntonio Huete Jimenez /*
3856a34d5fb1SAntonio Huete Jimenez * While expanding a variable expression, expand and apply indirect modifiers,
3857a34d5fb1SAntonio Huete Jimenez * such as in ${VAR:${M_indirect}}.
3858a34d5fb1SAntonio Huete Jimenez *
3859a34d5fb1SAntonio Huete Jimenez * All indirect modifiers of a group must come from a single variable
3860a34d5fb1SAntonio Huete Jimenez * expression. ${VAR:${M1}} is valid but ${VAR:${M1}${M2}} is not.
3861a34d5fb1SAntonio Huete Jimenez *
3862a34d5fb1SAntonio Huete Jimenez * Multiple groups of indirect modifiers can be chained by separating them
3863a34d5fb1SAntonio Huete Jimenez * with colons. ${VAR:${M1}:${M2}} contains 2 indirect modifiers.
3864a34d5fb1SAntonio Huete Jimenez *
3865*6eef5f0cSAntonio Huete Jimenez * If the variable expression is not followed by ch->endc or ':', fall
3866a34d5fb1SAntonio Huete Jimenez * back to trying the SysV modifier, such as in ${VAR:${FROM}=${TO}}.
3867a34d5fb1SAntonio Huete Jimenez */
3868a34d5fb1SAntonio Huete Jimenez static ApplyModifiersIndirectResult
ApplyModifiersIndirect(ModChain * ch,const char ** pp)3869*6eef5f0cSAntonio Huete Jimenez ApplyModifiersIndirect(ModChain *ch, const char **pp)
3870a34d5fb1SAntonio Huete Jimenez {
3871*6eef5f0cSAntonio Huete Jimenez Expr *expr = ch->expr;
3872a34d5fb1SAntonio Huete Jimenez const char *p = *pp;
3873a34d5fb1SAntonio Huete Jimenez FStr mods;
3874a34d5fb1SAntonio Huete Jimenez
3875*6eef5f0cSAntonio Huete Jimenez (void)Var_Parse(&p, expr->scope, expr->emode, &mods);
3876a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
3877a34d5fb1SAntonio Huete Jimenez
3878*6eef5f0cSAntonio Huete Jimenez if (mods.str[0] != '\0' && !IsDelimiter(*p, ch)) {
3879a34d5fb1SAntonio Huete Jimenez FStr_Done(&mods);
3880ec533708SSascha Wildner return AMIR_SYSV;
388101e196c8SJohn Marino }
3882a34d5fb1SAntonio Huete Jimenez
3883a34d5fb1SAntonio Huete Jimenez DEBUG3(VAR, "Indirect modifier \"%s\" from \"%.*s\"\n",
3884a34d5fb1SAntonio Huete Jimenez mods.str, (int)(p - *pp), *pp);
3885a34d5fb1SAntonio Huete Jimenez
3886a34d5fb1SAntonio Huete Jimenez if (mods.str[0] != '\0') {
3887a34d5fb1SAntonio Huete Jimenez const char *modsp = mods.str;
3888ec533708SSascha Wildner ApplyModifiers(expr, &modsp, '\0', '\0');
3889*6eef5f0cSAntonio Huete Jimenez if (Expr_Str(expr) == var_Error || *modsp != '\0') {
3890a34d5fb1SAntonio Huete Jimenez FStr_Done(&mods);
3891a34d5fb1SAntonio Huete Jimenez *pp = p;
3892a34d5fb1SAntonio Huete Jimenez return AMIR_OUT; /* error already reported */
389301e196c8SJohn Marino }
389401e196c8SJohn Marino }
3895a34d5fb1SAntonio Huete Jimenez FStr_Done(&mods);
3896a34d5fb1SAntonio Huete Jimenez
3897a34d5fb1SAntonio Huete Jimenez if (*p == ':')
3898a34d5fb1SAntonio Huete Jimenez p++;
3899*6eef5f0cSAntonio Huete Jimenez else if (*p == '\0' && ch->endc != '\0') {
3900ec533708SSascha Wildner Error("Unclosed variable expression after indirect "
3901ec533708SSascha Wildner "modifier, expecting '%c' for variable \"%s\"",
3902*6eef5f0cSAntonio Huete Jimenez ch->endc, expr->name);
3903a34d5fb1SAntonio Huete Jimenez *pp = p;
3904a34d5fb1SAntonio Huete Jimenez return AMIR_OUT;
3905a34d5fb1SAntonio Huete Jimenez }
3906a34d5fb1SAntonio Huete Jimenez
3907a34d5fb1SAntonio Huete Jimenez *pp = p;
3908a34d5fb1SAntonio Huete Jimenez return AMIR_CONTINUE;
3909a34d5fb1SAntonio Huete Jimenez }
3910a34d5fb1SAntonio Huete Jimenez
3911a34d5fb1SAntonio Huete Jimenez static ApplyModifierResult
ApplySingleModifier(const char ** pp,ModChain * ch)3912*6eef5f0cSAntonio Huete Jimenez ApplySingleModifier(const char **pp, ModChain *ch)
3913a34d5fb1SAntonio Huete Jimenez {
3914a34d5fb1SAntonio Huete Jimenez ApplyModifierResult res;
3915ec533708SSascha Wildner const char *mod = *pp;
3916a34d5fb1SAntonio Huete Jimenez const char *p = *pp;
3917a34d5fb1SAntonio Huete Jimenez
3918a34d5fb1SAntonio Huete Jimenez if (DEBUG(VAR))
3919*6eef5f0cSAntonio Huete Jimenez LogBeforeApply(ch, mod);
3920a34d5fb1SAntonio Huete Jimenez
3921*6eef5f0cSAntonio Huete Jimenez res = ApplyModifier(&p, ch);
3922a34d5fb1SAntonio Huete Jimenez
3923a34d5fb1SAntonio Huete Jimenez #ifdef SYSVVARSUB
3924a34d5fb1SAntonio Huete Jimenez if (res == AMR_UNKNOWN) {
3925a34d5fb1SAntonio Huete Jimenez assert(p == mod);
3926*6eef5f0cSAntonio Huete Jimenez res = ApplyModifier_SysV(&p, ch);
3927a34d5fb1SAntonio Huete Jimenez }
3928a34d5fb1SAntonio Huete Jimenez #endif
3929a34d5fb1SAntonio Huete Jimenez
3930a34d5fb1SAntonio Huete Jimenez if (res == AMR_UNKNOWN) {
3931a34d5fb1SAntonio Huete Jimenez /*
3932a34d5fb1SAntonio Huete Jimenez * Guess the end of the current modifier.
3933a34d5fb1SAntonio Huete Jimenez * XXX: Skipping the rest of the modifier hides
3934a34d5fb1SAntonio Huete Jimenez * errors and leads to wrong results.
3935a34d5fb1SAntonio Huete Jimenez * Parsing should rather stop here.
3936a34d5fb1SAntonio Huete Jimenez */
3937*6eef5f0cSAntonio Huete Jimenez for (p++; !IsDelimiter(*p, ch); p++)
3938a34d5fb1SAntonio Huete Jimenez continue;
3939ec533708SSascha Wildner Parse_Error(PARSE_FATAL, "Unknown modifier \"%.*s\"",
3940ec533708SSascha Wildner (int)(p - mod), mod);
3941*6eef5f0cSAntonio Huete Jimenez Expr_SetValueRefer(ch->expr, var_Error);
3942a34d5fb1SAntonio Huete Jimenez }
3943a34d5fb1SAntonio Huete Jimenez if (res == AMR_CLEANUP || res == AMR_BAD) {
3944a34d5fb1SAntonio Huete Jimenez *pp = p;
3945a34d5fb1SAntonio Huete Jimenez return res;
3946a34d5fb1SAntonio Huete Jimenez }
3947a34d5fb1SAntonio Huete Jimenez
3948a34d5fb1SAntonio Huete Jimenez if (DEBUG(VAR))
3949*6eef5f0cSAntonio Huete Jimenez LogAfterApply(ch, p, mod);
3950a34d5fb1SAntonio Huete Jimenez
3951*6eef5f0cSAntonio Huete Jimenez if (*p == '\0' && ch->endc != '\0') {
3952a34d5fb1SAntonio Huete Jimenez Error(
3953ec533708SSascha Wildner "Unclosed variable expression, expecting '%c' for "
3954ec533708SSascha Wildner "modifier \"%.*s\" of variable \"%s\" with value \"%s\"",
3955*6eef5f0cSAntonio Huete Jimenez ch->endc,
3956ec533708SSascha Wildner (int)(p - mod), mod,
3957*6eef5f0cSAntonio Huete Jimenez ch->expr->name, Expr_Str(ch->expr));
3958a34d5fb1SAntonio Huete Jimenez } else if (*p == ':') {
3959a34d5fb1SAntonio Huete Jimenez p++;
3960*6eef5f0cSAntonio Huete Jimenez } else if (opts.strict && *p != '\0' && *p != ch->endc) {
3961a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
3962a34d5fb1SAntonio Huete Jimenez "Missing delimiter ':' after modifier \"%.*s\"",
3963a34d5fb1SAntonio Huete Jimenez (int)(p - mod), mod);
3964a34d5fb1SAntonio Huete Jimenez /*
3965a34d5fb1SAntonio Huete Jimenez * TODO: propagate parse error to the enclosing
3966a34d5fb1SAntonio Huete Jimenez * expression
3967a34d5fb1SAntonio Huete Jimenez */
396801e196c8SJohn Marino }
3969a34d5fb1SAntonio Huete Jimenez *pp = p;
3970a34d5fb1SAntonio Huete Jimenez return AMR_OK;
397101e196c8SJohn Marino }
3972a34d5fb1SAntonio Huete Jimenez
3973*6eef5f0cSAntonio Huete Jimenez #if __STDC_VERSION__ >= 199901L
3974*6eef5f0cSAntonio Huete Jimenez #define ModChain_Literal(expr, startc, endc, sep, oneBigWord) \
3975*6eef5f0cSAntonio Huete Jimenez (ModChain) { expr, startc, endc, sep, oneBigWord }
3976*6eef5f0cSAntonio Huete Jimenez #else
3977*6eef5f0cSAntonio Huete Jimenez MAKE_INLINE ModChain
ModChain_Literal(Expr * expr,char startc,char endc,char sep,bool oneBigWord)3978*6eef5f0cSAntonio Huete Jimenez ModChain_Literal(Expr *expr, char startc, char endc, char sep, bool oneBigWord)
3979*6eef5f0cSAntonio Huete Jimenez {
3980*6eef5f0cSAntonio Huete Jimenez ModChain ch;
3981*6eef5f0cSAntonio Huete Jimenez ch.expr = expr;
3982*6eef5f0cSAntonio Huete Jimenez ch.startc = startc;
3983*6eef5f0cSAntonio Huete Jimenez ch.endc = endc;
3984*6eef5f0cSAntonio Huete Jimenez ch.sep = sep;
3985*6eef5f0cSAntonio Huete Jimenez ch.oneBigWord = oneBigWord;
3986*6eef5f0cSAntonio Huete Jimenez return ch;
3987*6eef5f0cSAntonio Huete Jimenez }
3988*6eef5f0cSAntonio Huete Jimenez #endif
3989*6eef5f0cSAntonio Huete Jimenez
3990a34d5fb1SAntonio Huete Jimenez /* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */
3991ec533708SSascha Wildner static void
ApplyModifiers(Expr * expr,const char ** pp,char startc,char endc)3992a34d5fb1SAntonio Huete Jimenez ApplyModifiers(
3993ec533708SSascha Wildner Expr *expr,
3994a34d5fb1SAntonio Huete Jimenez const char **pp, /* the parsing position, updated upon return */
3995ec533708SSascha Wildner char startc, /* '(' or '{'; or '\0' for indirect modifiers */
3996ec533708SSascha Wildner char endc /* ')' or '}'; or '\0' for indirect modifiers */
3997a34d5fb1SAntonio Huete Jimenez )
3998a34d5fb1SAntonio Huete Jimenez {
3999*6eef5f0cSAntonio Huete Jimenez ModChain ch = ModChain_Literal(expr, startc, endc, ' ', false);
4000a34d5fb1SAntonio Huete Jimenez const char *p;
4001a34d5fb1SAntonio Huete Jimenez const char *mod;
4002a34d5fb1SAntonio Huete Jimenez
4003a34d5fb1SAntonio Huete Jimenez assert(startc == '(' || startc == '{' || startc == '\0');
4004a34d5fb1SAntonio Huete Jimenez assert(endc == ')' || endc == '}' || endc == '\0');
4005*6eef5f0cSAntonio Huete Jimenez assert(Expr_Str(expr) != NULL);
4006a34d5fb1SAntonio Huete Jimenez
4007a34d5fb1SAntonio Huete Jimenez p = *pp;
4008a34d5fb1SAntonio Huete Jimenez
4009a34d5fb1SAntonio Huete Jimenez if (*p == '\0' && endc != '\0') {
4010a34d5fb1SAntonio Huete Jimenez Error(
4011a34d5fb1SAntonio Huete Jimenez "Unclosed variable expression (expecting '%c') for \"%s\"",
4012*6eef5f0cSAntonio Huete Jimenez ch.endc, expr->name);
4013a34d5fb1SAntonio Huete Jimenez goto cleanup;
4014a34d5fb1SAntonio Huete Jimenez }
4015a34d5fb1SAntonio Huete Jimenez
4016a34d5fb1SAntonio Huete Jimenez while (*p != '\0' && *p != endc) {
4017a34d5fb1SAntonio Huete Jimenez ApplyModifierResult res;
4018a34d5fb1SAntonio Huete Jimenez
4019a34d5fb1SAntonio Huete Jimenez if (*p == '$') {
4020ec533708SSascha Wildner ApplyModifiersIndirectResult amir =
4021*6eef5f0cSAntonio Huete Jimenez ApplyModifiersIndirect(&ch, &p);
4022a34d5fb1SAntonio Huete Jimenez if (amir == AMIR_CONTINUE)
4023a34d5fb1SAntonio Huete Jimenez continue;
4024a34d5fb1SAntonio Huete Jimenez if (amir == AMIR_OUT)
4025a34d5fb1SAntonio Huete Jimenez break;
4026ec533708SSascha Wildner /*
4027ec533708SSascha Wildner * It's neither '${VAR}:' nor '${VAR}}'. Try to parse
4028ec533708SSascha Wildner * it as a SysV modifier, as that is the only modifier
4029ec533708SSascha Wildner * that can start with '$'.
4030ec533708SSascha Wildner */
4031a34d5fb1SAntonio Huete Jimenez }
4032a34d5fb1SAntonio Huete Jimenez
4033a34d5fb1SAntonio Huete Jimenez mod = p;
4034a34d5fb1SAntonio Huete Jimenez
4035*6eef5f0cSAntonio Huete Jimenez res = ApplySingleModifier(&p, &ch);
4036a34d5fb1SAntonio Huete Jimenez if (res == AMR_CLEANUP)
4037a34d5fb1SAntonio Huete Jimenez goto cleanup;
4038a34d5fb1SAntonio Huete Jimenez if (res == AMR_BAD)
4039a34d5fb1SAntonio Huete Jimenez goto bad_modifier;
4040a34d5fb1SAntonio Huete Jimenez }
4041a34d5fb1SAntonio Huete Jimenez
4042a34d5fb1SAntonio Huete Jimenez *pp = p;
4043*6eef5f0cSAntonio Huete Jimenez assert(Expr_Str(expr) != NULL); /* Use var_Error or varUndefined. */
4044ec533708SSascha Wildner return;
404501e196c8SJohn Marino
404601e196c8SJohn Marino bad_modifier:
4047a34d5fb1SAntonio Huete Jimenez /* XXX: The modifier end is only guessed. */
4048ec533708SSascha Wildner Error("Bad modifier \":%.*s\" for variable \"%s\"",
4049*6eef5f0cSAntonio Huete Jimenez (int)strcspn(mod, ":)}"), mod, expr->name);
405001e196c8SJohn Marino
405101e196c8SJohn Marino cleanup:
4052ec533708SSascha Wildner /*
4053ec533708SSascha Wildner * TODO: Use p + strlen(p) instead, to stop parsing immediately.
4054ec533708SSascha Wildner *
4055*6eef5f0cSAntonio Huete Jimenez * In the unit tests, this generates a few shell commands with
4056*6eef5f0cSAntonio Huete Jimenez * unbalanced quotes. Instead of producing these incomplete strings,
4057*6eef5f0cSAntonio Huete Jimenez * commands with evaluation errors should not be run at all.
4058ec533708SSascha Wildner *
4059ec533708SSascha Wildner * To make that happen, Var_Subst must report the actual errors
4060ec533708SSascha Wildner * instead of returning VPR_OK unconditionally.
4061ec533708SSascha Wildner */
4062a34d5fb1SAntonio Huete Jimenez *pp = p;
4063ec533708SSascha Wildner Expr_SetValueRefer(expr, var_Error);
406401e196c8SJohn Marino }
406501e196c8SJohn Marino
4066a34d5fb1SAntonio Huete Jimenez /*
4067*6eef5f0cSAntonio Huete Jimenez * Only 4 of the 7 built-in local variables are treated specially as they are
4068*6eef5f0cSAntonio Huete Jimenez * the only ones that will be set when dynamic sources are expanded.
406901e196c8SJohn Marino */
4070*6eef5f0cSAntonio Huete Jimenez static bool
VarnameIsDynamic(Substring varname)4071*6eef5f0cSAntonio Huete Jimenez VarnameIsDynamic(Substring varname)
407201e196c8SJohn Marino {
4073*6eef5f0cSAntonio Huete Jimenez const char *name;
4074*6eef5f0cSAntonio Huete Jimenez size_t len;
4075*6eef5f0cSAntonio Huete Jimenez
4076*6eef5f0cSAntonio Huete Jimenez name = varname.start;
4077*6eef5f0cSAntonio Huete Jimenez len = Substring_Length(varname);
4078a34d5fb1SAntonio Huete Jimenez if (len == 1 || (len == 2 && (name[1] == 'F' || name[1] == 'D'))) {
4079a34d5fb1SAntonio Huete Jimenez switch (name[0]) {
4080a34d5fb1SAntonio Huete Jimenez case '@':
4081a34d5fb1SAntonio Huete Jimenez case '%':
4082a34d5fb1SAntonio Huete Jimenez case '*':
4083a34d5fb1SAntonio Huete Jimenez case '!':
4084*6eef5f0cSAntonio Huete Jimenez return true;
4085a34d5fb1SAntonio Huete Jimenez }
4086*6eef5f0cSAntonio Huete Jimenez return false;
4087a34d5fb1SAntonio Huete Jimenez }
4088a34d5fb1SAntonio Huete Jimenez
4089a34d5fb1SAntonio Huete Jimenez if ((len == 7 || len == 8) && name[0] == '.' && ch_isupper(name[1])) {
4090*6eef5f0cSAntonio Huete Jimenez return Substring_Equals(varname, ".TARGET") ||
4091*6eef5f0cSAntonio Huete Jimenez Substring_Equals(varname, ".ARCHIVE") ||
4092*6eef5f0cSAntonio Huete Jimenez Substring_Equals(varname, ".PREFIX") ||
4093*6eef5f0cSAntonio Huete Jimenez Substring_Equals(varname, ".MEMBER");
4094a34d5fb1SAntonio Huete Jimenez }
4095a34d5fb1SAntonio Huete Jimenez
4096*6eef5f0cSAntonio Huete Jimenez return false;
4097a34d5fb1SAntonio Huete Jimenez }
4098a34d5fb1SAntonio Huete Jimenez
4099a34d5fb1SAntonio Huete Jimenez static const char *
UndefinedShortVarValue(char varname,const GNode * scope)4100a34d5fb1SAntonio Huete Jimenez UndefinedShortVarValue(char varname, const GNode *scope)
4101a34d5fb1SAntonio Huete Jimenez {
4102a34d5fb1SAntonio Huete Jimenez if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL) {
4103a34d5fb1SAntonio Huete Jimenez /*
4104a34d5fb1SAntonio Huete Jimenez * If substituting a local variable in a non-local scope,
4105a34d5fb1SAntonio Huete Jimenez * assume it's for dynamic source stuff. We have to handle
4106a34d5fb1SAntonio Huete Jimenez * this specially and return the longhand for the variable
4107a34d5fb1SAntonio Huete Jimenez * with the dollar sign escaped so it makes it back to the
4108a34d5fb1SAntonio Huete Jimenez * caller. Only four of the local variables are treated
4109a34d5fb1SAntonio Huete Jimenez * specially as they are the only four that will be set
4110a34d5fb1SAntonio Huete Jimenez * when dynamic sources are expanded.
4111a34d5fb1SAntonio Huete Jimenez */
4112a34d5fb1SAntonio Huete Jimenez switch (varname) {
4113a34d5fb1SAntonio Huete Jimenez case '@':
4114a34d5fb1SAntonio Huete Jimenez return "$(.TARGET)";
4115a34d5fb1SAntonio Huete Jimenez case '%':
4116a34d5fb1SAntonio Huete Jimenez return "$(.MEMBER)";
4117a34d5fb1SAntonio Huete Jimenez case '*':
4118a34d5fb1SAntonio Huete Jimenez return "$(.PREFIX)";
4119a34d5fb1SAntonio Huete Jimenez case '!':
4120a34d5fb1SAntonio Huete Jimenez return "$(.ARCHIVE)";
4121a34d5fb1SAntonio Huete Jimenez }
4122a34d5fb1SAntonio Huete Jimenez }
4123a34d5fb1SAntonio Huete Jimenez return NULL;
4124a34d5fb1SAntonio Huete Jimenez }
4125a34d5fb1SAntonio Huete Jimenez
4126a34d5fb1SAntonio Huete Jimenez /*
4127a34d5fb1SAntonio Huete Jimenez * Parse a variable name, until the end character or a colon, whichever
4128a34d5fb1SAntonio Huete Jimenez * comes first.
4129a34d5fb1SAntonio Huete Jimenez */
4130*6eef5f0cSAntonio Huete Jimenez static void
ParseVarname(const char ** pp,char startc,char endc,GNode * scope,VarEvalMode emode,LazyBuf * buf)4131a34d5fb1SAntonio Huete Jimenez ParseVarname(const char **pp, char startc, char endc,
4132*6eef5f0cSAntonio Huete Jimenez GNode *scope, VarEvalMode emode,
4133*6eef5f0cSAntonio Huete Jimenez LazyBuf *buf)
4134a34d5fb1SAntonio Huete Jimenez {
4135a34d5fb1SAntonio Huete Jimenez const char *p = *pp;
4136ec533708SSascha Wildner int depth = 0; /* Track depth so we can spot parse errors. */
4137a34d5fb1SAntonio Huete Jimenez
4138*6eef5f0cSAntonio Huete Jimenez LazyBuf_Init(buf, p);
4139a34d5fb1SAntonio Huete Jimenez
4140a34d5fb1SAntonio Huete Jimenez while (*p != '\0') {
4141ec533708SSascha Wildner if ((*p == endc || *p == ':') && depth == 0)
4142ec533708SSascha Wildner break;
4143a34d5fb1SAntonio Huete Jimenez if (*p == startc)
4144a34d5fb1SAntonio Huete Jimenez depth++;
4145ec533708SSascha Wildner if (*p == endc)
4146ec533708SSascha Wildner depth--;
4147a34d5fb1SAntonio Huete Jimenez
4148a34d5fb1SAntonio Huete Jimenez /* A variable inside a variable, expand. */
4149a34d5fb1SAntonio Huete Jimenez if (*p == '$') {
4150a34d5fb1SAntonio Huete Jimenez FStr nested_val;
4151*6eef5f0cSAntonio Huete Jimenez (void)Var_Parse(&p, scope, emode, &nested_val);
4152a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
4153*6eef5f0cSAntonio Huete Jimenez LazyBuf_AddStr(buf, nested_val.str);
4154a34d5fb1SAntonio Huete Jimenez FStr_Done(&nested_val);
4155a34d5fb1SAntonio Huete Jimenez } else {
4156*6eef5f0cSAntonio Huete Jimenez LazyBuf_Add(buf, *p);
4157a34d5fb1SAntonio Huete Jimenez p++;
4158a34d5fb1SAntonio Huete Jimenez }
4159a34d5fb1SAntonio Huete Jimenez }
4160a34d5fb1SAntonio Huete Jimenez *pp = p;
4161a34d5fb1SAntonio Huete Jimenez }
4162a34d5fb1SAntonio Huete Jimenez
4163*6eef5f0cSAntonio Huete Jimenez static bool
IsShortVarnameValid(char varname,const char * start)4164*6eef5f0cSAntonio Huete Jimenez IsShortVarnameValid(char varname, const char *start)
4165a34d5fb1SAntonio Huete Jimenez {
4166ec533708SSascha Wildner if (varname != '$' && varname != ':' && varname != '}' &&
4167ec533708SSascha Wildner varname != ')' && varname != '\0')
4168*6eef5f0cSAntonio Huete Jimenez return true;
4169a34d5fb1SAntonio Huete Jimenez
4170a34d5fb1SAntonio Huete Jimenez if (!opts.strict)
4171*6eef5f0cSAntonio Huete Jimenez return false; /* XXX: Missing error message */
4172a34d5fb1SAntonio Huete Jimenez
4173a34d5fb1SAntonio Huete Jimenez if (varname == '$')
4174a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
4175a34d5fb1SAntonio Huete Jimenez "To escape a dollar, use \\$, not $$, at \"%s\"", start);
4176a34d5fb1SAntonio Huete Jimenez else if (varname == '\0')
4177a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL, "Dollar followed by nothing");
4178a34d5fb1SAntonio Huete Jimenez else
4179a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
4180a34d5fb1SAntonio Huete Jimenez "Invalid variable name '%c', at \"%s\"", varname, start);
4181a34d5fb1SAntonio Huete Jimenez
4182*6eef5f0cSAntonio Huete Jimenez return false;
4183a34d5fb1SAntonio Huete Jimenez }
4184a34d5fb1SAntonio Huete Jimenez
4185a34d5fb1SAntonio Huete Jimenez /*
4186ec533708SSascha Wildner * Parse a single-character variable name such as in $V or $@.
4187a34d5fb1SAntonio Huete Jimenez * Return whether to continue parsing.
4188a34d5fb1SAntonio Huete Jimenez */
4189*6eef5f0cSAntonio Huete Jimenez static bool
ParseVarnameShort(char varname,const char ** pp,GNode * scope,VarEvalMode emode,VarParseResult * out_false_res,const char ** out_false_val,Var ** out_true_var)4190ec533708SSascha Wildner ParseVarnameShort(char varname, const char **pp, GNode *scope,
4191*6eef5f0cSAntonio Huete Jimenez VarEvalMode emode,
4192*6eef5f0cSAntonio Huete Jimenez VarParseResult *out_false_res, const char **out_false_val,
4193*6eef5f0cSAntonio Huete Jimenez Var **out_true_var)
4194a34d5fb1SAntonio Huete Jimenez {
419501e196c8SJohn Marino char name[2];
4196a34d5fb1SAntonio Huete Jimenez Var *v;
4197*6eef5f0cSAntonio Huete Jimenez const char *val;
419801e196c8SJohn Marino
4199*6eef5f0cSAntonio Huete Jimenez if (!IsShortVarnameValid(varname, *pp)) {
4200*6eef5f0cSAntonio Huete Jimenez (*pp)++; /* only skip the '$' */
4201*6eef5f0cSAntonio Huete Jimenez *out_false_res = VPR_ERR;
4202*6eef5f0cSAntonio Huete Jimenez *out_false_val = var_Error;
4203*6eef5f0cSAntonio Huete Jimenez return false;
420401e196c8SJohn Marino }
4205a34d5fb1SAntonio Huete Jimenez
4206ec533708SSascha Wildner name[0] = varname;
420701e196c8SJohn Marino name[1] = '\0';
4208*6eef5f0cSAntonio Huete Jimenez v = VarFind(name, scope, true);
4209*6eef5f0cSAntonio Huete Jimenez if (v != NULL) {
4210*6eef5f0cSAntonio Huete Jimenez /* No need to advance *pp, the calling code handles this. */
4211*6eef5f0cSAntonio Huete Jimenez *out_true_var = v;
4212*6eef5f0cSAntonio Huete Jimenez return true;
4213*6eef5f0cSAntonio Huete Jimenez }
4214*6eef5f0cSAntonio Huete Jimenez
4215a34d5fb1SAntonio Huete Jimenez *pp += 2;
421601e196c8SJohn Marino
4217ec533708SSascha Wildner val = UndefinedShortVarValue(varname, scope);
4218a34d5fb1SAntonio Huete Jimenez if (val == NULL)
4219*6eef5f0cSAntonio Huete Jimenez val = emode == VARE_UNDEFERR ? var_Error : varUndefined;
422001e196c8SJohn Marino
4221a34d5fb1SAntonio Huete Jimenez if (opts.strict && val == var_Error) {
4222a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
4223a34d5fb1SAntonio Huete Jimenez "Variable \"%s\" is undefined", name);
4224*6eef5f0cSAntonio Huete Jimenez *out_false_res = VPR_ERR;
4225*6eef5f0cSAntonio Huete Jimenez *out_false_val = val;
4226*6eef5f0cSAntonio Huete Jimenez return false;
4227a34d5fb1SAntonio Huete Jimenez }
422801e196c8SJohn Marino
422901e196c8SJohn Marino /*
4230a34d5fb1SAntonio Huete Jimenez * XXX: This looks completely wrong.
423101e196c8SJohn Marino *
4232a34d5fb1SAntonio Huete Jimenez * If undefined expressions are not allowed, this should
4233a34d5fb1SAntonio Huete Jimenez * rather be VPR_ERR instead of VPR_UNDEF, together with an
4234a34d5fb1SAntonio Huete Jimenez * error message.
4235a34d5fb1SAntonio Huete Jimenez *
4236a34d5fb1SAntonio Huete Jimenez * If undefined expressions are allowed, this should rather
4237a34d5fb1SAntonio Huete Jimenez * be VPR_UNDEF instead of VPR_OK.
423801e196c8SJohn Marino */
4239*6eef5f0cSAntonio Huete Jimenez *out_false_res = emode == VARE_UNDEFERR ? VPR_UNDEF : VPR_OK;
4240*6eef5f0cSAntonio Huete Jimenez *out_false_val = val;
4241*6eef5f0cSAntonio Huete Jimenez return false;
4242a34d5fb1SAntonio Huete Jimenez }
4243a34d5fb1SAntonio Huete Jimenez
4244a34d5fb1SAntonio Huete Jimenez /* Find variables like @F or <D. */
4245a34d5fb1SAntonio Huete Jimenez static Var *
FindLocalLegacyVar(Substring varname,GNode * scope,const char ** out_extraModifiers)4246*6eef5f0cSAntonio Huete Jimenez FindLocalLegacyVar(Substring varname, GNode *scope,
4247a34d5fb1SAntonio Huete Jimenez const char **out_extraModifiers)
4248a34d5fb1SAntonio Huete Jimenez {
4249*6eef5f0cSAntonio Huete Jimenez Var *v;
4250*6eef5f0cSAntonio Huete Jimenez
4251a34d5fb1SAntonio Huete Jimenez /* Only resolve these variables if scope is a "real" target. */
4252a34d5fb1SAntonio Huete Jimenez if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL)
4253a34d5fb1SAntonio Huete Jimenez return NULL;
4254a34d5fb1SAntonio Huete Jimenez
4255*6eef5f0cSAntonio Huete Jimenez if (Substring_Length(varname) != 2)
4256a34d5fb1SAntonio Huete Jimenez return NULL;
4257*6eef5f0cSAntonio Huete Jimenez if (varname.start[1] != 'F' && varname.start[1] != 'D')
4258a34d5fb1SAntonio Huete Jimenez return NULL;
4259*6eef5f0cSAntonio Huete Jimenez if (strchr("@%?*!<>", varname.start[0]) == NULL)
4260a34d5fb1SAntonio Huete Jimenez return NULL;
4261a34d5fb1SAntonio Huete Jimenez
4262*6eef5f0cSAntonio Huete Jimenez v = VarFindSubstring(Substring_Sub(varname, 0, 1), scope, false);
4263*6eef5f0cSAntonio Huete Jimenez if (v == NULL)
4264*6eef5f0cSAntonio Huete Jimenez return NULL;
426501e196c8SJohn Marino
4266*6eef5f0cSAntonio Huete Jimenez *out_extraModifiers = varname.start[1] == 'D' ? "H:" : "T:";
4267a34d5fb1SAntonio Huete Jimenez return v;
4268a34d5fb1SAntonio Huete Jimenez }
4269a34d5fb1SAntonio Huete Jimenez
4270a34d5fb1SAntonio Huete Jimenez static VarParseResult
EvalUndefined(bool dynamic,const char * start,const char * p,Substring varname,VarEvalMode emode,FStr * out_val)4271*6eef5f0cSAntonio Huete Jimenez EvalUndefined(bool dynamic, const char *start, const char *p,
4272*6eef5f0cSAntonio Huete Jimenez Substring varname, VarEvalMode emode, FStr *out_val)
4273a34d5fb1SAntonio Huete Jimenez {
4274a34d5fb1SAntonio Huete Jimenez if (dynamic) {
4275a34d5fb1SAntonio Huete Jimenez *out_val = FStr_InitOwn(bmake_strsedup(start, p));
4276a34d5fb1SAntonio Huete Jimenez return VPR_OK;
4277a34d5fb1SAntonio Huete Jimenez }
4278a34d5fb1SAntonio Huete Jimenez
4279*6eef5f0cSAntonio Huete Jimenez if (emode == VARE_UNDEFERR && opts.strict) {
4280a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
4281*6eef5f0cSAntonio Huete Jimenez "Variable \"%.*s\" is undefined",
4282*6eef5f0cSAntonio Huete Jimenez (int)Substring_Length(varname), varname.start);
4283a34d5fb1SAntonio Huete Jimenez *out_val = FStr_InitRefer(var_Error);
4284a34d5fb1SAntonio Huete Jimenez return VPR_ERR;
4285a34d5fb1SAntonio Huete Jimenez }
4286a34d5fb1SAntonio Huete Jimenez
4287*6eef5f0cSAntonio Huete Jimenez if (emode == VARE_UNDEFERR) {
4288a34d5fb1SAntonio Huete Jimenez *out_val = FStr_InitRefer(var_Error);
4289a34d5fb1SAntonio Huete Jimenez return VPR_UNDEF; /* XXX: Should be VPR_ERR instead. */
4290a34d5fb1SAntonio Huete Jimenez }
4291a34d5fb1SAntonio Huete Jimenez
4292a34d5fb1SAntonio Huete Jimenez *out_val = FStr_InitRefer(varUndefined);
4293a34d5fb1SAntonio Huete Jimenez return VPR_OK;
4294a34d5fb1SAntonio Huete Jimenez }
4295a34d5fb1SAntonio Huete Jimenez
4296a34d5fb1SAntonio Huete Jimenez /*
4297a34d5fb1SAntonio Huete Jimenez * Parse a long variable name enclosed in braces or parentheses such as $(VAR)
4298a34d5fb1SAntonio Huete Jimenez * or ${VAR}, up to the closing brace or parenthesis, or in the case of
4299a34d5fb1SAntonio Huete Jimenez * ${VAR:Modifiers}, up to the ':' that starts the modifiers.
4300a34d5fb1SAntonio Huete Jimenez * Return whether to continue parsing.
4301a34d5fb1SAntonio Huete Jimenez */
4302*6eef5f0cSAntonio Huete Jimenez static bool
ParseVarnameLong(const char ** pp,char startc,GNode * scope,VarEvalMode emode,const char ** out_false_pp,VarParseResult * out_false_res,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)4303a34d5fb1SAntonio Huete Jimenez ParseVarnameLong(
4304*6eef5f0cSAntonio Huete Jimenez const char **pp,
4305a34d5fb1SAntonio Huete Jimenez char startc,
4306a34d5fb1SAntonio Huete Jimenez GNode *scope,
4307*6eef5f0cSAntonio Huete Jimenez VarEvalMode emode,
4308a34d5fb1SAntonio Huete Jimenez
4309*6eef5f0cSAntonio Huete Jimenez const char **out_false_pp,
4310*6eef5f0cSAntonio Huete Jimenez VarParseResult *out_false_res,
4311*6eef5f0cSAntonio Huete Jimenez FStr *out_false_val,
4312a34d5fb1SAntonio Huete Jimenez
4313*6eef5f0cSAntonio Huete Jimenez char *out_true_endc,
4314*6eef5f0cSAntonio Huete Jimenez Var **out_true_v,
4315*6eef5f0cSAntonio Huete Jimenez bool *out_true_haveModifier,
4316*6eef5f0cSAntonio Huete Jimenez const char **out_true_extraModifiers,
4317*6eef5f0cSAntonio Huete Jimenez bool *out_true_dynamic,
4318*6eef5f0cSAntonio Huete Jimenez ExprDefined *out_true_exprDefined
4319a34d5fb1SAntonio Huete Jimenez )
4320a34d5fb1SAntonio Huete Jimenez {
4321*6eef5f0cSAntonio Huete Jimenez LazyBuf varname;
4322*6eef5f0cSAntonio Huete Jimenez Substring name;
4323a34d5fb1SAntonio Huete Jimenez Var *v;
4324*6eef5f0cSAntonio Huete Jimenez bool haveModifier;
4325*6eef5f0cSAntonio Huete Jimenez bool dynamic = false;
4326a34d5fb1SAntonio Huete Jimenez
4327*6eef5f0cSAntonio Huete Jimenez const char *p = *pp;
4328a34d5fb1SAntonio Huete Jimenez const char *const start = p;
4329a34d5fb1SAntonio Huete Jimenez char endc = startc == '(' ? ')' : '}';
4330a34d5fb1SAntonio Huete Jimenez
4331a34d5fb1SAntonio Huete Jimenez p += 2; /* skip "${" or "$(" or "y(" */
4332*6eef5f0cSAntonio Huete Jimenez ParseVarname(&p, startc, endc, scope, emode, &varname);
4333*6eef5f0cSAntonio Huete Jimenez name = LazyBuf_Get(&varname);
4334a34d5fb1SAntonio Huete Jimenez
4335a34d5fb1SAntonio Huete Jimenez if (*p == ':') {
4336*6eef5f0cSAntonio Huete Jimenez haveModifier = true;
4337a34d5fb1SAntonio Huete Jimenez } else if (*p == endc) {
4338*6eef5f0cSAntonio Huete Jimenez haveModifier = false;
4339a34d5fb1SAntonio Huete Jimenez } else {
4340*6eef5f0cSAntonio Huete Jimenez Parse_Error(PARSE_FATAL, "Unclosed variable \"%.*s\"",
4341*6eef5f0cSAntonio Huete Jimenez (int)Substring_Length(name), name.start);
4342*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&varname);
4343*6eef5f0cSAntonio Huete Jimenez *out_false_pp = p;
4344*6eef5f0cSAntonio Huete Jimenez *out_false_val = FStr_InitRefer(var_Error);
4345*6eef5f0cSAntonio Huete Jimenez *out_false_res = VPR_ERR;
4346*6eef5f0cSAntonio Huete Jimenez return false;
4347a34d5fb1SAntonio Huete Jimenez }
4348a34d5fb1SAntonio Huete Jimenez
4349*6eef5f0cSAntonio Huete Jimenez v = VarFindSubstring(name, scope, true);
4350a34d5fb1SAntonio Huete Jimenez
4351*6eef5f0cSAntonio Huete Jimenez /*
4352*6eef5f0cSAntonio Huete Jimenez * At this point, p points just after the variable name, either at
4353*6eef5f0cSAntonio Huete Jimenez * ':' or at endc.
4354*6eef5f0cSAntonio Huete Jimenez */
4355a34d5fb1SAntonio Huete Jimenez
4356*6eef5f0cSAntonio Huete Jimenez if (v == NULL && Substring_Equals(name, ".SUFFIXES")) {
4357*6eef5f0cSAntonio Huete Jimenez char *suffixes = Suff_NamesStr();
4358*6eef5f0cSAntonio Huete Jimenez v = VarNew(FStr_InitRefer(".SUFFIXES"), suffixes,
4359*6eef5f0cSAntonio Huete Jimenez true, false, true);
4360*6eef5f0cSAntonio Huete Jimenez free(suffixes);
4361*6eef5f0cSAntonio Huete Jimenez } else if (v == NULL)
4362*6eef5f0cSAntonio Huete Jimenez v = FindLocalLegacyVar(name, scope, out_true_extraModifiers);
436301e196c8SJohn Marino
436401e196c8SJohn Marino if (v == NULL) {
436501e196c8SJohn Marino /*
4366a34d5fb1SAntonio Huete Jimenez * Defer expansion of dynamic variables if they appear in
4367a34d5fb1SAntonio Huete Jimenez * non-local scope since they are not defined there.
436801e196c8SJohn Marino */
4369*6eef5f0cSAntonio Huete Jimenez dynamic = VarnameIsDynamic(name) &&
4370a34d5fb1SAntonio Huete Jimenez (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL);
437101e196c8SJohn Marino
437201e196c8SJohn Marino if (!haveModifier) {
4373a34d5fb1SAntonio Huete Jimenez p++; /* skip endc */
4374*6eef5f0cSAntonio Huete Jimenez *out_false_pp = p;
4375*6eef5f0cSAntonio Huete Jimenez *out_false_res = EvalUndefined(dynamic, start, p,
4376*6eef5f0cSAntonio Huete Jimenez name, emode, out_false_val);
4377*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&varname);
4378*6eef5f0cSAntonio Huete Jimenez return false;
437901e196c8SJohn Marino }
4380a34d5fb1SAntonio Huete Jimenez
438101e196c8SJohn Marino /*
4382a34d5fb1SAntonio Huete Jimenez * The variable expression is based on an undefined variable.
4383a34d5fb1SAntonio Huete Jimenez * Nevertheless it needs a Var, for modifiers that access the
4384a34d5fb1SAntonio Huete Jimenez * variable name, such as :L or :?.
4385a34d5fb1SAntonio Huete Jimenez *
4386a34d5fb1SAntonio Huete Jimenez * Most modifiers leave this expression in the "undefined"
4387ec533708SSascha Wildner * state (VES_UNDEF), only a few modifiers like :D, :U, :L,
4388a34d5fb1SAntonio Huete Jimenez * :P turn this undefined expression into a defined
4389ec533708SSascha Wildner * expression (VES_DEF).
4390a34d5fb1SAntonio Huete Jimenez *
4391ec533708SSascha Wildner * In the end, after applying all modifiers, if the expression
4392a34d5fb1SAntonio Huete Jimenez * is still undefined, Var_Parse will return an empty string
4393a34d5fb1SAntonio Huete Jimenez * instead of the actually computed value.
439401e196c8SJohn Marino */
4395*6eef5f0cSAntonio Huete Jimenez v = VarNew(LazyBuf_DoneGet(&varname), "",
4396*6eef5f0cSAntonio Huete Jimenez true, false, false);
4397*6eef5f0cSAntonio Huete Jimenez *out_true_exprDefined = DEF_UNDEF;
439801e196c8SJohn Marino } else
4399*6eef5f0cSAntonio Huete Jimenez LazyBuf_Done(&varname);
4400a34d5fb1SAntonio Huete Jimenez
4401*6eef5f0cSAntonio Huete Jimenez *pp = p;
4402*6eef5f0cSAntonio Huete Jimenez *out_true_endc = endc;
4403*6eef5f0cSAntonio Huete Jimenez *out_true_v = v;
4404*6eef5f0cSAntonio Huete Jimenez *out_true_haveModifier = haveModifier;
4405*6eef5f0cSAntonio Huete Jimenez *out_true_dynamic = dynamic;
4406*6eef5f0cSAntonio Huete Jimenez return true;
440701e196c8SJohn Marino }
440801e196c8SJohn Marino
4409*6eef5f0cSAntonio Huete Jimenez #if __STDC_VERSION__ >= 199901L
4410*6eef5f0cSAntonio Huete Jimenez #define Expr_Literal(name, value, emode, scope, defined) \
4411*6eef5f0cSAntonio Huete Jimenez { name, value, emode, scope, defined }
4412*6eef5f0cSAntonio Huete Jimenez #else
4413*6eef5f0cSAntonio Huete Jimenez MAKE_INLINE Expr
Expr_Literal(const char * name,FStr value,VarEvalMode emode,GNode * scope,ExprDefined defined)4414*6eef5f0cSAntonio Huete Jimenez Expr_Literal(const char *name, FStr value,
4415*6eef5f0cSAntonio Huete Jimenez VarEvalMode emode, GNode *scope, ExprDefined defined)
4416a34d5fb1SAntonio Huete Jimenez {
4417*6eef5f0cSAntonio Huete Jimenez Expr expr;
4418a34d5fb1SAntonio Huete Jimenez
4419*6eef5f0cSAntonio Huete Jimenez expr.name = name;
4420*6eef5f0cSAntonio Huete Jimenez expr.value = value;
4421*6eef5f0cSAntonio Huete Jimenez expr.emode = emode;
4422*6eef5f0cSAntonio Huete Jimenez expr.scope = scope;
4423*6eef5f0cSAntonio Huete Jimenez expr.defined = defined;
4424*6eef5f0cSAntonio Huete Jimenez return expr;
4425*6eef5f0cSAntonio Huete Jimenez }
4426*6eef5f0cSAntonio Huete Jimenez #endif
4427*6eef5f0cSAntonio Huete Jimenez
4428*6eef5f0cSAntonio Huete Jimenez /*
4429*6eef5f0cSAntonio Huete Jimenez * Expressions of the form ${:U...} with a trivial value are often generated
4430*6eef5f0cSAntonio Huete Jimenez * by .for loops and are boring, therefore parse and evaluate them in a fast
4431*6eef5f0cSAntonio Huete Jimenez * lane without debug logging.
4432*6eef5f0cSAntonio Huete Jimenez */
4433*6eef5f0cSAntonio Huete Jimenez static bool
Var_Parse_FastLane(const char ** pp,VarEvalMode emode,FStr * out_value)4434*6eef5f0cSAntonio Huete Jimenez Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value)
4435*6eef5f0cSAntonio Huete Jimenez {
4436*6eef5f0cSAntonio Huete Jimenez const char *p;
4437*6eef5f0cSAntonio Huete Jimenez
4438*6eef5f0cSAntonio Huete Jimenez p = *pp;
4439*6eef5f0cSAntonio Huete Jimenez if (!(p[0] == '$' && p[1] == '{' && p[2] == ':' && p[3] == 'U'))
4440*6eef5f0cSAntonio Huete Jimenez return false;
4441*6eef5f0cSAntonio Huete Jimenez
4442*6eef5f0cSAntonio Huete Jimenez p += 4;
4443*6eef5f0cSAntonio Huete Jimenez while (*p != '$' && *p != '{' && *p != ':' && *p != '\\' &&
4444*6eef5f0cSAntonio Huete Jimenez *p != '}' && *p != '\0')
4445*6eef5f0cSAntonio Huete Jimenez p++;
4446*6eef5f0cSAntonio Huete Jimenez if (*p != '}')
4447*6eef5f0cSAntonio Huete Jimenez return false;
4448*6eef5f0cSAntonio Huete Jimenez
4449*6eef5f0cSAntonio Huete Jimenez if (emode == VARE_PARSE_ONLY)
4450*6eef5f0cSAntonio Huete Jimenez *out_value = FStr_InitRefer("");
4451*6eef5f0cSAntonio Huete Jimenez else
4452*6eef5f0cSAntonio Huete Jimenez *out_value = FStr_InitOwn(bmake_strsedup(*pp + 4, p));
4453*6eef5f0cSAntonio Huete Jimenez *pp = p + 1;
4454*6eef5f0cSAntonio Huete Jimenez return true;
445501e196c8SJohn Marino }
4456a34d5fb1SAntonio Huete Jimenez
445701e196c8SJohn Marino /*
4458a34d5fb1SAntonio Huete Jimenez * Given the start of a variable expression (such as $v, $(VAR),
4459a34d5fb1SAntonio Huete Jimenez * ${VAR:Mpattern}), extract the variable name and value, and the modifiers,
4460a34d5fb1SAntonio Huete Jimenez * if any. While doing that, apply the modifiers to the value of the
4461a34d5fb1SAntonio Huete Jimenez * expression, forming its final value. A few of the modifiers such as :!cmd!
4462a34d5fb1SAntonio Huete Jimenez * or ::= have side effects.
4463a34d5fb1SAntonio Huete Jimenez *
4464a34d5fb1SAntonio Huete Jimenez * Input:
4465a34d5fb1SAntonio Huete Jimenez * *pp The string to parse.
4466*6eef5f0cSAntonio Huete Jimenez * When called from CondParser_FuncCallEmpty, it can
4467*6eef5f0cSAntonio Huete Jimenez * also point to the "y" of "empty(VARNAME:Modifiers)".
4468a34d5fb1SAntonio Huete Jimenez * scope The scope for finding variables
4469*6eef5f0cSAntonio Huete Jimenez * emode Controls the exact details of parsing and evaluation
4470a34d5fb1SAntonio Huete Jimenez *
4471a34d5fb1SAntonio Huete Jimenez * Output:
4472a34d5fb1SAntonio Huete Jimenez * *pp The position where to continue parsing.
4473a34d5fb1SAntonio Huete Jimenez * TODO: After a parse error, the value of *pp is
4474a34d5fb1SAntonio Huete Jimenez * unspecified. It may not have been updated at all,
4475a34d5fb1SAntonio Huete Jimenez * point to some random character in the string, to the
4476a34d5fb1SAntonio Huete Jimenez * location of the parse error, or at the end of the
4477a34d5fb1SAntonio Huete Jimenez * string.
4478a34d5fb1SAntonio Huete Jimenez * *out_val The value of the variable expression, never NULL.
4479a34d5fb1SAntonio Huete Jimenez * *out_val var_Error if there was a parse error.
4480a34d5fb1SAntonio Huete Jimenez * *out_val var_Error if the base variable of the expression was
4481*6eef5f0cSAntonio Huete Jimenez * undefined, emode is VARE_UNDEFERR, and none of
4482a34d5fb1SAntonio Huete Jimenez * the modifiers turned the undefined expression into a
4483a34d5fb1SAntonio Huete Jimenez * defined expression.
4484a34d5fb1SAntonio Huete Jimenez * XXX: It is not guaranteed that an error message has
4485a34d5fb1SAntonio Huete Jimenez * been printed.
4486a34d5fb1SAntonio Huete Jimenez * *out_val varUndefined if the base variable of the expression
4487*6eef5f0cSAntonio Huete Jimenez * was undefined, emode was not VARE_UNDEFERR,
4488a34d5fb1SAntonio Huete Jimenez * and none of the modifiers turned the undefined
4489a34d5fb1SAntonio Huete Jimenez * expression into a defined expression.
4490a34d5fb1SAntonio Huete Jimenez * XXX: It is not guaranteed that an error message has
4491a34d5fb1SAntonio Huete Jimenez * been printed.
449201e196c8SJohn Marino */
4493a34d5fb1SAntonio Huete Jimenez VarParseResult
Var_Parse(const char ** pp,GNode * scope,VarEvalMode emode,FStr * out_val)4494*6eef5f0cSAntonio Huete Jimenez Var_Parse(const char **pp, GNode *scope, VarEvalMode emode, FStr *out_val)
4495a34d5fb1SAntonio Huete Jimenez {
4496a34d5fb1SAntonio Huete Jimenez const char *p = *pp;
4497a34d5fb1SAntonio Huete Jimenez const char *const start = p;
4498*6eef5f0cSAntonio Huete Jimenez bool haveModifier; /* true for ${VAR:...}, false for ${VAR} */
4499*6eef5f0cSAntonio Huete Jimenez char startc; /* the actual '{' or '(' or '\0' */
4500*6eef5f0cSAntonio Huete Jimenez char endc; /* the expected '}' or ')' or '\0' */
4501a34d5fb1SAntonio Huete Jimenez /*
4502*6eef5f0cSAntonio Huete Jimenez * true if the expression is based on one of the 7 predefined
4503*6eef5f0cSAntonio Huete Jimenez * variables that are local to a target, and the expression is
4504*6eef5f0cSAntonio Huete Jimenez * expanded in a non-local scope. The result is the text of the
4505*6eef5f0cSAntonio Huete Jimenez * expression, unaltered. This is needed to support dynamic sources.
4506a34d5fb1SAntonio Huete Jimenez */
4507*6eef5f0cSAntonio Huete Jimenez bool dynamic;
4508a34d5fb1SAntonio Huete Jimenez const char *extramodifiers;
4509ec533708SSascha Wildner Var *v;
4510*6eef5f0cSAntonio Huete Jimenez Expr expr = Expr_Literal(NULL, FStr_InitRefer(NULL), emode,
4511*6eef5f0cSAntonio Huete Jimenez scope, DEF_REGULAR);
4512ec533708SSascha Wildner
4513*6eef5f0cSAntonio Huete Jimenez if (Var_Parse_FastLane(pp, emode, out_val))
4514*6eef5f0cSAntonio Huete Jimenez return VPR_OK;
4515a34d5fb1SAntonio Huete Jimenez
4516*6eef5f0cSAntonio Huete Jimenez /* TODO: Reduce computations in parse-only mode. */
4517*6eef5f0cSAntonio Huete Jimenez
4518*6eef5f0cSAntonio Huete Jimenez DEBUG2(VAR, "Var_Parse: %s (%s)\n", start, VarEvalMode_Name[emode]);
4519a34d5fb1SAntonio Huete Jimenez
4520a34d5fb1SAntonio Huete Jimenez *out_val = FStr_InitRefer(NULL);
4521a34d5fb1SAntonio Huete Jimenez extramodifiers = NULL; /* extra modifiers to apply first */
4522*6eef5f0cSAntonio Huete Jimenez dynamic = false;
4523a34d5fb1SAntonio Huete Jimenez
4524*6eef5f0cSAntonio Huete Jimenez endc = '\0'; /* Appease GCC. */
4525a34d5fb1SAntonio Huete Jimenez
4526a34d5fb1SAntonio Huete Jimenez startc = p[1];
4527a34d5fb1SAntonio Huete Jimenez if (startc != '(' && startc != '{') {
4528a34d5fb1SAntonio Huete Jimenez VarParseResult res;
4529*6eef5f0cSAntonio Huete Jimenez if (!ParseVarnameShort(startc, pp, scope, emode, &res,
4530*6eef5f0cSAntonio Huete Jimenez &out_val->str, &v))
4531a34d5fb1SAntonio Huete Jimenez return res;
4532*6eef5f0cSAntonio Huete Jimenez haveModifier = false;
4533a34d5fb1SAntonio Huete Jimenez p++;
4534a34d5fb1SAntonio Huete Jimenez } else {
4535a34d5fb1SAntonio Huete Jimenez VarParseResult res;
4536*6eef5f0cSAntonio Huete Jimenez if (!ParseVarnameLong(&p, startc, scope, emode,
4537a34d5fb1SAntonio Huete Jimenez pp, &res, out_val,
4538*6eef5f0cSAntonio Huete Jimenez &endc, &v, &haveModifier, &extramodifiers,
4539ec533708SSascha Wildner &dynamic, &expr.defined))
4540a34d5fb1SAntonio Huete Jimenez return res;
454101e196c8SJohn Marino }
454201e196c8SJohn Marino
4543*6eef5f0cSAntonio Huete Jimenez expr.name = v->name.str;
4544*6eef5f0cSAntonio Huete Jimenez if (v->inUse && VarEvalMode_ShouldEval(emode)) {
4545*6eef5f0cSAntonio Huete Jimenez if (scope->fname != NULL) {
4546*6eef5f0cSAntonio Huete Jimenez fprintf(stderr, "In a command near ");
4547*6eef5f0cSAntonio Huete Jimenez PrintLocation(stderr, false, scope);
4548*6eef5f0cSAntonio Huete Jimenez }
4549a34d5fb1SAntonio Huete Jimenez Fatal("Variable %s is recursive.", v->name.str);
4550*6eef5f0cSAntonio Huete Jimenez }
455101e196c8SJohn Marino
4552a34d5fb1SAntonio Huete Jimenez /*
4553a34d5fb1SAntonio Huete Jimenez * XXX: This assignment creates an alias to the current value of the
4554a34d5fb1SAntonio Huete Jimenez * variable. This means that as long as the value of the expression
4555a34d5fb1SAntonio Huete Jimenez * stays the same, the value of the variable must not change.
4556*6eef5f0cSAntonio Huete Jimenez * Using the '::=' modifier, it could be possible to trigger exactly
4557*6eef5f0cSAntonio Huete Jimenez * this situation.
4558*6eef5f0cSAntonio Huete Jimenez *
4559a34d5fb1SAntonio Huete Jimenez * At the bottom of this function, the resulting value is compared to
4560a34d5fb1SAntonio Huete Jimenez * the then-current value of the variable. This might also invoke
4561a34d5fb1SAntonio Huete Jimenez * undefined behavior.
4562a34d5fb1SAntonio Huete Jimenez */
4563ec533708SSascha Wildner expr.value = FStr_InitRefer(v->val.data);
45646a91b982SJohn Marino
4565a34d5fb1SAntonio Huete Jimenez /*
4566a34d5fb1SAntonio Huete Jimenez * Before applying any modifiers, expand any nested expressions from
4567a34d5fb1SAntonio Huete Jimenez * the variable value.
4568a34d5fb1SAntonio Huete Jimenez */
4569*6eef5f0cSAntonio Huete Jimenez if (VarEvalMode_ShouldEval(emode) &&
4570*6eef5f0cSAntonio Huete Jimenez strchr(Expr_Str(&expr), '$') != NULL) {
4571a34d5fb1SAntonio Huete Jimenez char *expanded;
4572*6eef5f0cSAntonio Huete Jimenez VarEvalMode nested_emode = emode;
4573a34d5fb1SAntonio Huete Jimenez if (opts.strict)
4574*6eef5f0cSAntonio Huete Jimenez nested_emode = VarEvalMode_UndefOk(nested_emode);
4575*6eef5f0cSAntonio Huete Jimenez v->inUse = true;
4576*6eef5f0cSAntonio Huete Jimenez (void)Var_Subst(Expr_Str(&expr), scope, nested_emode,
4577ec533708SSascha Wildner &expanded);
4578*6eef5f0cSAntonio Huete Jimenez v->inUse = false;
4579a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
4580ec533708SSascha Wildner Expr_SetValueOwn(&expr, expanded);
4581a34d5fb1SAntonio Huete Jimenez }
4582a34d5fb1SAntonio Huete Jimenez
45836a91b982SJohn Marino if (extramodifiers != NULL) {
4584a34d5fb1SAntonio Huete Jimenez const char *em = extramodifiers;
4585ec533708SSascha Wildner ApplyModifiers(&expr, &em, '\0', '\0');
45866a91b982SJohn Marino }
45876a91b982SJohn Marino
45886a91b982SJohn Marino if (haveModifier) {
4589a34d5fb1SAntonio Huete Jimenez p++; /* Skip initial colon. */
4590ec533708SSascha Wildner ApplyModifiers(&expr, &p, startc, endc);
459101e196c8SJohn Marino }
4592a34d5fb1SAntonio Huete Jimenez
4593a34d5fb1SAntonio Huete Jimenez if (*p != '\0') /* Skip past endc if possible. */
4594a34d5fb1SAntonio Huete Jimenez p++;
4595a34d5fb1SAntonio Huete Jimenez
4596a34d5fb1SAntonio Huete Jimenez *pp = p;
459701e196c8SJohn Marino
4598ec533708SSascha Wildner if (expr.defined == DEF_UNDEF) {
4599*6eef5f0cSAntonio Huete Jimenez if (dynamic)
4600*6eef5f0cSAntonio Huete Jimenez Expr_SetValueOwn(&expr, bmake_strsedup(start, p));
4601*6eef5f0cSAntonio Huete Jimenez else {
4602a34d5fb1SAntonio Huete Jimenez /*
4603*6eef5f0cSAntonio Huete Jimenez * The expression is still undefined, therefore
4604*6eef5f0cSAntonio Huete Jimenez * discard the actual value and return an error marker
4605*6eef5f0cSAntonio Huete Jimenez * instead.
4606a34d5fb1SAntonio Huete Jimenez */
4607ec533708SSascha Wildner Expr_SetValueRefer(&expr,
4608*6eef5f0cSAntonio Huete Jimenez emode == VARE_UNDEFERR
4609a34d5fb1SAntonio Huete Jimenez ? var_Error : varUndefined);
461001e196c8SJohn Marino }
461101e196c8SJohn Marino }
4612*6eef5f0cSAntonio Huete Jimenez
4613*6eef5f0cSAntonio Huete Jimenez if (v->shortLived) {
4614*6eef5f0cSAntonio Huete Jimenez if (expr.value.str == v->val.data) {
4615*6eef5f0cSAntonio Huete Jimenez /* move ownership */
4616*6eef5f0cSAntonio Huete Jimenez expr.value.freeIt = v->val.data;
4617*6eef5f0cSAntonio Huete Jimenez v->val.data = NULL;
461801e196c8SJohn Marino }
4619*6eef5f0cSAntonio Huete Jimenez VarFreeShortLived(v);
4620*6eef5f0cSAntonio Huete Jimenez }
4621*6eef5f0cSAntonio Huete Jimenez
4622ec533708SSascha Wildner *out_val = expr.value;
4623a34d5fb1SAntonio Huete Jimenez return VPR_OK; /* XXX: Is not correct in all cases */
462401e196c8SJohn Marino }
462501e196c8SJohn Marino
4626a34d5fb1SAntonio Huete Jimenez static void
VarSubstDollarDollar(const char ** pp,Buffer * res,VarEvalMode emode)4627*6eef5f0cSAntonio Huete Jimenez VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalMode emode)
462801e196c8SJohn Marino {
4629ec533708SSascha Wildner /* A dollar sign may be escaped with another dollar sign. */
4630*6eef5f0cSAntonio Huete Jimenez if (save_dollars && VarEvalMode_ShouldKeepDollar(emode))
4631a34d5fb1SAntonio Huete Jimenez Buf_AddByte(res, '$');
4632a34d5fb1SAntonio Huete Jimenez Buf_AddByte(res, '$');
4633a34d5fb1SAntonio Huete Jimenez *pp += 2;
463401e196c8SJohn Marino }
463501e196c8SJohn Marino
4636a34d5fb1SAntonio Huete Jimenez static void
VarSubstExpr(const char ** pp,Buffer * buf,GNode * scope,VarEvalMode emode,bool * inout_errorReported)4637a34d5fb1SAntonio Huete Jimenez VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
4638*6eef5f0cSAntonio Huete Jimenez VarEvalMode emode, bool *inout_errorReported)
4639a34d5fb1SAntonio Huete Jimenez {
4640a34d5fb1SAntonio Huete Jimenez const char *p = *pp;
4641a34d5fb1SAntonio Huete Jimenez const char *nested_p = p;
4642a34d5fb1SAntonio Huete Jimenez FStr val;
464301e196c8SJohn Marino
4644*6eef5f0cSAntonio Huete Jimenez (void)Var_Parse(&nested_p, scope, emode, &val);
4645a34d5fb1SAntonio Huete Jimenez /* TODO: handle errors */
4646a34d5fb1SAntonio Huete Jimenez
4647a34d5fb1SAntonio Huete Jimenez if (val.str == var_Error || val.str == varUndefined) {
4648*6eef5f0cSAntonio Huete Jimenez if (!VarEvalMode_ShouldKeepUndef(emode)) {
4649a34d5fb1SAntonio Huete Jimenez p = nested_p;
4650*6eef5f0cSAntonio Huete Jimenez } else if (val.str == var_Error) {
465101e196c8SJohn Marino
465201e196c8SJohn Marino /*
4653a34d5fb1SAntonio Huete Jimenez * XXX: This condition is wrong. If val == var_Error,
4654a34d5fb1SAntonio Huete Jimenez * this doesn't necessarily mean there was an undefined
4655a34d5fb1SAntonio Huete Jimenez * variable. It could equally well be a parse error;
4656a34d5fb1SAntonio Huete Jimenez * see unit-tests/varmod-order.exp.
465701e196c8SJohn Marino */
4658a34d5fb1SAntonio Huete Jimenez
465901e196c8SJohn Marino /*
466001e196c8SJohn Marino * If variable is undefined, complain and skip the
4661a34d5fb1SAntonio Huete Jimenez * variable. The complaint will stop us from doing
4662a34d5fb1SAntonio Huete Jimenez * anything when the file is parsed.
466301e196c8SJohn Marino */
4664a34d5fb1SAntonio Huete Jimenez if (!*inout_errorReported) {
4665a34d5fb1SAntonio Huete Jimenez Parse_Error(PARSE_FATAL,
4666a34d5fb1SAntonio Huete Jimenez "Undefined variable \"%.*s\"",
4667a34d5fb1SAntonio Huete Jimenez (int)(size_t)(nested_p - p), p);
466801e196c8SJohn Marino }
4669a34d5fb1SAntonio Huete Jimenez p = nested_p;
4670*6eef5f0cSAntonio Huete Jimenez *inout_errorReported = true;
467101e196c8SJohn Marino } else {
4672*6eef5f0cSAntonio Huete Jimenez /*
4673*6eef5f0cSAntonio Huete Jimenez * Copy the initial '$' of the undefined expression,
4674a34d5fb1SAntonio Huete Jimenez * thereby deferring expansion of the expression, but
4675*6eef5f0cSAntonio Huete Jimenez * expand nested expressions if already possible. See
4676*6eef5f0cSAntonio Huete Jimenez * unit-tests/varparse-undef-partial.mk.
4677*6eef5f0cSAntonio Huete Jimenez */
4678a34d5fb1SAntonio Huete Jimenez Buf_AddByte(buf, *p);
4679a34d5fb1SAntonio Huete Jimenez p++;
468001e196c8SJohn Marino }
468101e196c8SJohn Marino } else {
4682a34d5fb1SAntonio Huete Jimenez p = nested_p;
4683a34d5fb1SAntonio Huete Jimenez Buf_AddStr(buf, val.str);
4684a34d5fb1SAntonio Huete Jimenez }
4685a34d5fb1SAntonio Huete Jimenez
4686a34d5fb1SAntonio Huete Jimenez FStr_Done(&val);
4687a34d5fb1SAntonio Huete Jimenez
4688a34d5fb1SAntonio Huete Jimenez *pp = p;
4689a34d5fb1SAntonio Huete Jimenez }
469001e196c8SJohn Marino
469101e196c8SJohn Marino /*
4692a34d5fb1SAntonio Huete Jimenez * Skip as many characters as possible -- either to the end of the string
4693a34d5fb1SAntonio Huete Jimenez * or to the next dollar sign (variable expression).
469401e196c8SJohn Marino */
4695a34d5fb1SAntonio Huete Jimenez static void
VarSubstPlain(const char ** pp,Buffer * res)4696a34d5fb1SAntonio Huete Jimenez VarSubstPlain(const char **pp, Buffer *res)
4697a34d5fb1SAntonio Huete Jimenez {
4698a34d5fb1SAntonio Huete Jimenez const char *p = *pp;
4699a34d5fb1SAntonio Huete Jimenez const char *start = p;
4700a34d5fb1SAntonio Huete Jimenez
4701a34d5fb1SAntonio Huete Jimenez for (p++; *p != '$' && *p != '\0'; p++)
4702a34d5fb1SAntonio Huete Jimenez continue;
4703a34d5fb1SAntonio Huete Jimenez Buf_AddBytesBetween(res, start, p);
4704a34d5fb1SAntonio Huete Jimenez *pp = p;
470501e196c8SJohn Marino }
470601e196c8SJohn Marino
4707a34d5fb1SAntonio Huete Jimenez /*
4708a34d5fb1SAntonio Huete Jimenez * Expand all variable expressions like $V, ${VAR}, $(VAR:Modifiers) in the
4709a34d5fb1SAntonio Huete Jimenez * given string.
4710a34d5fb1SAntonio Huete Jimenez *
4711a34d5fb1SAntonio Huete Jimenez * Input:
4712a34d5fb1SAntonio Huete Jimenez * str The string in which the variable expressions are
4713a34d5fb1SAntonio Huete Jimenez * expanded.
4714a34d5fb1SAntonio Huete Jimenez * scope The scope in which to start searching for
4715a34d5fb1SAntonio Huete Jimenez * variables. The other scopes are searched as well.
4716*6eef5f0cSAntonio Huete Jimenez * emode The mode for parsing or evaluating subexpressions.
4717a34d5fb1SAntonio Huete Jimenez */
4718a34d5fb1SAntonio Huete Jimenez VarParseResult
Var_Subst(const char * str,GNode * scope,VarEvalMode emode,char ** out_res)4719*6eef5f0cSAntonio Huete Jimenez Var_Subst(const char *str, GNode *scope, VarEvalMode emode, char **out_res)
4720a34d5fb1SAntonio Huete Jimenez {
4721a34d5fb1SAntonio Huete Jimenez const char *p = str;
4722a34d5fb1SAntonio Huete Jimenez Buffer res;
4723a34d5fb1SAntonio Huete Jimenez
4724*6eef5f0cSAntonio Huete Jimenez /*
4725*6eef5f0cSAntonio Huete Jimenez * Set true if an error has already been reported, to prevent a
4726*6eef5f0cSAntonio Huete Jimenez * plethora of messages when recursing
4727*6eef5f0cSAntonio Huete Jimenez */
4728*6eef5f0cSAntonio Huete Jimenez /* See varparse-errors.mk for why the 'static' is necessary here. */
4729*6eef5f0cSAntonio Huete Jimenez static bool errorReported;
4730a34d5fb1SAntonio Huete Jimenez
4731a34d5fb1SAntonio Huete Jimenez Buf_Init(&res);
4732*6eef5f0cSAntonio Huete Jimenez errorReported = false;
4733a34d5fb1SAntonio Huete Jimenez
4734a34d5fb1SAntonio Huete Jimenez while (*p != '\0') {
4735a34d5fb1SAntonio Huete Jimenez if (p[0] == '$' && p[1] == '$')
4736*6eef5f0cSAntonio Huete Jimenez VarSubstDollarDollar(&p, &res, emode);
4737a34d5fb1SAntonio Huete Jimenez else if (p[0] == '$')
4738*6eef5f0cSAntonio Huete Jimenez VarSubstExpr(&p, &res, scope, emode, &errorReported);
4739a34d5fb1SAntonio Huete Jimenez else
4740a34d5fb1SAntonio Huete Jimenez VarSubstPlain(&p, &res);
474101e196c8SJohn Marino }
474201e196c8SJohn Marino
4743a34d5fb1SAntonio Huete Jimenez *out_res = Buf_DoneDataCompact(&res);
4744a34d5fb1SAntonio Huete Jimenez return VPR_OK;
4745a34d5fb1SAntonio Huete Jimenez }
4746a34d5fb1SAntonio Huete Jimenez
4747*6eef5f0cSAntonio Huete Jimenez void
Var_Expand(FStr * str,GNode * scope,VarEvalMode emode)4748*6eef5f0cSAntonio Huete Jimenez Var_Expand(FStr *str, GNode *scope, VarEvalMode emode)
4749*6eef5f0cSAntonio Huete Jimenez {
4750*6eef5f0cSAntonio Huete Jimenez char *expanded;
4751*6eef5f0cSAntonio Huete Jimenez
4752*6eef5f0cSAntonio Huete Jimenez if (strchr(str->str, '$') == NULL)
4753*6eef5f0cSAntonio Huete Jimenez return;
4754*6eef5f0cSAntonio Huete Jimenez (void)Var_Subst(str->str, scope, emode, &expanded);
4755*6eef5f0cSAntonio Huete Jimenez /* TODO: handle errors */
4756*6eef5f0cSAntonio Huete Jimenez FStr_Done(str);
4757*6eef5f0cSAntonio Huete Jimenez *str = FStr_InitOwn(expanded);
4758*6eef5f0cSAntonio Huete Jimenez }
4759*6eef5f0cSAntonio Huete Jimenez
4760a34d5fb1SAntonio Huete Jimenez /* Initialize the variables module. */
476101e196c8SJohn Marino void
Var_Init(void)476201e196c8SJohn Marino Var_Init(void)
476301e196c8SJohn Marino {
4764a34d5fb1SAntonio Huete Jimenez SCOPE_INTERNAL = GNode_New("Internal");
4765a34d5fb1SAntonio Huete Jimenez SCOPE_GLOBAL = GNode_New("Global");
4766a34d5fb1SAntonio Huete Jimenez SCOPE_CMDLINE = GNode_New("Command");
476701e196c8SJohn Marino }
476801e196c8SJohn Marino
4769a34d5fb1SAntonio Huete Jimenez /* Clean up the variables module. */
477001e196c8SJohn Marino void
Var_End(void)477101e196c8SJohn Marino Var_End(void)
477201e196c8SJohn Marino {
4773a34d5fb1SAntonio Huete Jimenez Var_Stats();
477401e196c8SJohn Marino }
477501e196c8SJohn Marino
477601e196c8SJohn Marino void
Var_Stats(void)4777a34d5fb1SAntonio Huete Jimenez Var_Stats(void)
477801e196c8SJohn Marino {
4779a34d5fb1SAntonio Huete Jimenez HashTable_DebugStats(&SCOPE_GLOBAL->vars, "Global variables");
4780a34d5fb1SAntonio Huete Jimenez }
4781a34d5fb1SAntonio Huete Jimenez
4782*6eef5f0cSAntonio Huete Jimenez static int
StrAsc(const void * sa,const void * sb)4783*6eef5f0cSAntonio Huete Jimenez StrAsc(const void *sa, const void *sb)
4784*6eef5f0cSAntonio Huete Jimenez {
4785*6eef5f0cSAntonio Huete Jimenez return strcmp(
4786*6eef5f0cSAntonio Huete Jimenez *((const char *const *)sa), *((const char *const *)sb));
4787*6eef5f0cSAntonio Huete Jimenez }
4788*6eef5f0cSAntonio Huete Jimenez
4789*6eef5f0cSAntonio Huete Jimenez
4790a34d5fb1SAntonio Huete Jimenez /* Print all variables in a scope, sorted by name. */
4791a34d5fb1SAntonio Huete Jimenez void
Var_Dump(GNode * scope)4792a34d5fb1SAntonio Huete Jimenez Var_Dump(GNode *scope)
4793a34d5fb1SAntonio Huete Jimenez {
4794a34d5fb1SAntonio Huete Jimenez Vector /* of const char * */ vec;
4795a34d5fb1SAntonio Huete Jimenez HashIter hi;
4796a34d5fb1SAntonio Huete Jimenez size_t i;
4797a34d5fb1SAntonio Huete Jimenez const char **varnames;
4798a34d5fb1SAntonio Huete Jimenez
4799a34d5fb1SAntonio Huete Jimenez Vector_Init(&vec, sizeof(const char *));
4800a34d5fb1SAntonio Huete Jimenez
4801a34d5fb1SAntonio Huete Jimenez HashIter_Init(&hi, &scope->vars);
4802a34d5fb1SAntonio Huete Jimenez while (HashIter_Next(&hi) != NULL)
4803a34d5fb1SAntonio Huete Jimenez *(const char **)Vector_Push(&vec) = hi.entry->key;
4804a34d5fb1SAntonio Huete Jimenez varnames = vec.items;
4805a34d5fb1SAntonio Huete Jimenez
4806*6eef5f0cSAntonio Huete Jimenez qsort(varnames, vec.len, sizeof varnames[0], StrAsc);
4807a34d5fb1SAntonio Huete Jimenez
4808a34d5fb1SAntonio Huete Jimenez for (i = 0; i < vec.len; i++) {
4809a34d5fb1SAntonio Huete Jimenez const char *varname = varnames[i];
4810a34d5fb1SAntonio Huete Jimenez Var *var = HashTable_FindValue(&scope->vars, varname);
4811*6eef5f0cSAntonio Huete Jimenez debug_printf("%-16s = %s%s\n", varname,
4812*6eef5f0cSAntonio Huete Jimenez var->val.data, ValueDescription(var->val.data));
4813a34d5fb1SAntonio Huete Jimenez }
4814a34d5fb1SAntonio Huete Jimenez
4815a34d5fb1SAntonio Huete Jimenez Vector_Done(&vec);
481601e196c8SJohn Marino }
4817