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