aboutsummaryrefslogtreecommitdiff
path: root/var.c
diff options
context:
space:
mode:
Diffstat (limited to 'var.c')
-rw-r--r--var.c3045
1 files changed, 1648 insertions, 1397 deletions
diff --git a/var.c b/var.c
index eddca9ef6daa..6e5148bba968 100644
--- a/var.c
+++ b/var.c
@@ -1,4 +1,4 @@
-/* $NetBSD: var.c,v 1.807 2021/02/05 05:42:39 rillig Exp $ */
+/* $NetBSD: var.c,v 1.934 2021/06/21 08:40:44 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -120,7 +120,8 @@
*
* Var_Dump Print out all variables defined in the given scope.
*
- * XXX: There's a lot of duplication in these functions.
+ * XXX: There's a lot of almost duplicate code in these functions that only
+ * differs in subtle details that are not mentioned in the manual page.
*/
#include <sys/stat.h>
@@ -147,48 +148,7 @@
#include "metachar.h"
/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: var.c,v 1.807 2021/02/05 05:42:39 rillig Exp $");
-
-typedef enum VarFlags {
- VAR_NONE = 0,
-
- /*
- * The variable's value is currently being used by Var_Parse or
- * Var_Subst. This marker is used to avoid endless recursion.
- */
- VAR_IN_USE = 0x01,
-
- /*
- * The variable comes from the environment.
- * These variables are not registered in any GNode, therefore they
- * must be freed as soon as they are not used anymore.
- */
- VAR_FROM_ENV = 0x02,
-
- /*
- * The variable is exported to the environment, to be used by child
- * processes.
- */
- VAR_EXPORTED = 0x10,
-
- /*
- * At the point where this variable was exported, it contained an
- * unresolved reference to another variable. Before any child
- * process is started, it needs to be exported again, in the hope
- * that the referenced variable can then be resolved.
- */
- VAR_REEXPORT = 0x20,
-
- /* The variable came from the command line. */
- VAR_FROM_CMD = 0x40,
-
- /*
- * The variable value cannot be changed anymore, and the variable
- * cannot be deleted. Any attempts to do so are silently ignored,
- * they are logged with -dv though.
- */
- VAR_READONLY = 0x80
-} VarFlags;
+MAKE_RCSID("$NetBSD: var.c,v 1.934 2021/06/21 08:40:44 rillig Exp $");
/*
* Variables are defined using one of the VAR=value assignments. Their
@@ -219,12 +179,53 @@ typedef struct Var {
/* The unexpanded value of the variable. */
Buffer val;
- /* Miscellaneous status flags. */
- VarFlags flags;
+
+ /* The variable came from the command line. */
+ bool fromCmd: 1;
+
+ /*
+ * The variable comes from the environment.
+ * These variables are not registered in any GNode, therefore they
+ * must be freed as soon as they are not used anymore.
+ */
+ bool fromEnv: 1;
+
+ /*
+ * The variable value cannot be changed anymore, and the variable
+ * cannot be deleted. Any attempts to do so are silently ignored,
+ * they are logged with -dv though.
+ *
+ * See VAR_SET_READONLY.
+ */
+ bool readOnly: 1;
+
+ /*
+ * The variable's value is currently being used by Var_Parse or
+ * Var_Subst. This marker is used to avoid endless recursion.
+ */
+ bool inUse: 1;
+
+ /*
+ * The variable is exported to the environment, to be used by child
+ * processes.
+ */
+ bool exported: 1;
+
+ /*
+ * At the point where this variable was exported, it contained an
+ * unresolved reference to another variable. Before any child
+ * process is started, it needs to be exported again, in the hope
+ * that the referenced variable can then be resolved.
+ */
+ bool reexport: 1;
} Var;
/*
- * Exporting vars is expensive so skip it if we can
+ * Exporting variables is expensive and may leak memory, so skip it if we
+ * can.
+ *
+ * To avoid this, it might be worth encapsulating the environment variables
+ * in a separate data structure called EnvVars.
*/
typedef enum VarExportedMode {
VAR_EXPORTED_NONE,
@@ -233,37 +234,37 @@ typedef enum VarExportedMode {
} VarExportedMode;
typedef enum UnexportWhat {
+ /* Unexport the variables given by name. */
UNEXPORT_NAMED,
+ /*
+ * Unexport all globals previously exported, but keep the environment
+ * inherited from the parent.
+ */
UNEXPORT_ALL,
+ /*
+ * Unexport all globals previously exported and clear the environment
+ * inherited from the parent.
+ */
UNEXPORT_ENV
} UnexportWhat;
/* Flags for pattern matching in the :S and :C modifiers */
-typedef struct VarPatternFlags {
+typedef struct PatternFlags {
+ bool subGlobal: 1; /* 'g': replace as often as possible */
+ bool subOnce: 1; /* '1': replace only once */
+ bool anchorStart: 1; /* '^': match only at start of word */
+ bool anchorEnd: 1; /* '$': match only at end of word */
+} PatternFlags;
- /* Replace as often as possible ('g') */
- Boolean subGlobal: 1;
- /* Replace only once ('1') */
- Boolean subOnce: 1;
- /* Match at start of word ('^') */
- Boolean anchorStart: 1;
- /* Match at end of word ('$') */
- Boolean anchorEnd: 1;
-} VarPatternFlags;
-
-/* SepBuf is a string being built from words, interleaved with separators. */
+/* SepBuf builds a string from words interleaved with separators. */
typedef struct SepBuf {
Buffer buf;
- Boolean needSep;
+ bool needSep;
/* Usually ' ', but see the ':ts' modifier. */
char sep;
} SepBuf;
-ENUM_FLAGS_RTTI_4(VarEvalFlags,
- VARE_UNDEFERR, VARE_WANTRES, VARE_KEEP_DOLLAR,
- VARE_KEEP_UNDEF);
-
/*
* This lets us tell if we have replaced the original environ
* (which we cannot free).
@@ -282,6 +283,8 @@ char var_Error[] = "";
* a case where VARE_UNDEFERR is not set. This undefined variable is
* typically a dynamic variable such as ${.TARGET}, whose expansion needs to
* be deferred until it is defined in an actual target.
+ *
+ * See VARE_EVAL_KEEP_UNDEF.
*/
static char varUndefined[] = "";
@@ -289,12 +292,12 @@ static char varUndefined[] = "";
* Traditionally this make consumed $$ during := like any other expansion.
* Other make's do not, and this make follows straight since 2016-01-09.
*
- * This knob allows controlling the behavior.
- * FALSE to consume $$ during := assignment.
- * TRUE to preserve $$ during := assignment.
+ * This knob allows controlling the behavior:
+ * false to consume $$ during := assignment.
+ * true to preserve $$ during := assignment.
*/
#define MAKE_SAVE_DOLLARS ".MAKE.SAVE_DOLLARS"
-static Boolean save_dollars = FALSE;
+static bool save_dollars = false;
/*
* A scope collects variable names and their values.
@@ -321,64 +324,59 @@ GNode *SCOPE_CMDLINE;
GNode *SCOPE_GLOBAL;
GNode *SCOPE_INTERNAL;
-ENUM_FLAGS_RTTI_6(VarFlags,
- VAR_IN_USE, VAR_FROM_ENV,
- VAR_EXPORTED, VAR_REEXPORT, VAR_FROM_CMD, VAR_READONLY);
-
static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
+static const char *VarEvalMode_Name[] = {
+ "parse-only",
+ "eval",
+ "eval-defined",
+ "eval-keep-dollar",
+ "eval-keep-undefined",
+ "eval-keep-dollar-and-undefined",
+};
+
static Var *
-VarNew(FStr name, const char *value, VarFlags flags)
+VarNew(FStr name, const char *value, bool fromEnv, bool readOnly)
{
size_t value_len = strlen(value);
Var *var = bmake_malloc(sizeof *var);
var->name = name;
Buf_InitSize(&var->val, value_len + 1);
Buf_AddBytes(&var->val, value, value_len);
- var->flags = flags;
+ var->fromCmd = false;
+ var->fromEnv = fromEnv;
+ var->readOnly = readOnly;
+ var->inUse = false;
+ var->exported = false;
+ var->reexport = false;
return var;
}
-static const char *
-CanonicalVarname(const char *name)
+static Substring
+CanonicalVarname(Substring name)
{
- if (*name == '.' && ch_isupper(name[1])) {
- switch (name[1]) {
- case 'A':
- if (strcmp(name, ".ALLSRC") == 0)
- name = ALLSRC;
- if (strcmp(name, ".ARCHIVE") == 0)
- name = ARCHIVE;
- break;
- case 'I':
- if (strcmp(name, ".IMPSRC") == 0)
- name = IMPSRC;
- break;
- case 'M':
- if (strcmp(name, ".MEMBER") == 0)
- name = MEMBER;
- break;
- case 'O':
- if (strcmp(name, ".OODATE") == 0)
- name = OODATE;
- break;
- case 'P':
- if (strcmp(name, ".PREFIX") == 0)
- name = PREFIX;
- break;
- case 'S':
- if (strcmp(name, ".SHELL") == 0) {
- if (shellPath == NULL)
- Shell_Init();
- }
- break;
- case 'T':
- if (strcmp(name, ".TARGET") == 0)
- name = TARGET;
- break;
- }
- }
+
+ if (!(Substring_Length(name) > 0 && name.start[0] == '.'))
+ return name;
+
+ if (Substring_Equals(name, ".ALLSRC"))
+ return Substring_InitStr(ALLSRC);
+ if (Substring_Equals(name, ".ARCHIVE"))
+ return Substring_InitStr(ARCHIVE);
+ if (Substring_Equals(name, ".IMPSRC"))
+ return Substring_InitStr(IMPSRC);
+ if (Substring_Equals(name, ".MEMBER"))
+ return Substring_InitStr(MEMBER);
+ if (Substring_Equals(name, ".OODATE"))
+ return Substring_InitStr(OODATE);
+ if (Substring_Equals(name, ".PREFIX"))
+ return Substring_InitStr(PREFIX);
+ if (Substring_Equals(name, ".TARGET"))
+ return Substring_InitStr(TARGET);
+
+ if (Substring_Equals(name, ".SHELL") && shellPath == NULL)
+ Shell_Init();
/* GNU make has an additional alias $^ == ${.ALLSRC}. */
@@ -386,9 +384,9 @@ CanonicalVarname(const char *name)
}
static Var *
-GNode_FindVar(GNode *scope, const char *varname, unsigned int hash)
+GNode_FindVar(GNode *scope, Substring varname, unsigned int hash)
{
- return HashTable_FindValueHash(&scope->vars, varname, hash);
+ return HashTable_FindValueBySubstringHash(&scope->vars, varname, hash);
}
/*
@@ -397,7 +395,7 @@ GNode_FindVar(GNode *scope, const char *varname, unsigned int hash)
* Input:
* name name to find, is not expanded any further
* scope scope in which to look first
- * elsewhere TRUE to look in other scopes as well
+ * elsewhere true to look in other scopes as well
*
* Results:
* The found variable, or NULL if the variable does not exist.
@@ -405,29 +403,19 @@ GNode_FindVar(GNode *scope, const char *varname, unsigned int hash)
* VarFreeEnv after use.
*/
static Var *
-VarFind(const char *name, GNode *scope, Boolean elsewhere)
+VarFindSubstring(Substring name, GNode *scope, bool elsewhere)
{
Var *var;
unsigned int nameHash;
- /*
- * If the variable name begins with a '.', it could very well be
- * one of the local ones. We check the name against all the local
- * variables and substitute the short version in for 'name' if it
- * matches one of them.
- */
+ /* Replace '.TARGET' with '@', likewise for other local variables. */
name = CanonicalVarname(name);
- nameHash = Hash_Hash(name);
+ nameHash = Hash_Substring(name);
- /* First look for the variable in the given scope. */
var = GNode_FindVar(scope, name, nameHash);
if (!elsewhere)
return var;
- /*
- * The variable was not found in the given scope.
- * Now look for it in the other scopes as well.
- */
if (var == NULL && scope != SCOPE_CMDLINE)
var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash);
@@ -440,12 +428,19 @@ VarFind(const char *name, GNode *scope, Boolean elsewhere)
}
if (var == NULL) {
- char *env;
+ FStr envName;
+ const char *envValue;
- if ((env = getenv(name)) != NULL) {
- char *varname = bmake_strdup(name);
- return VarNew(FStr_InitOwn(varname), env, VAR_FROM_ENV);
- }
+ /*
+ * TODO: try setting an environment variable with the empty
+ * name, which should be technically possible, just to see
+ * how make reacts. All .for loops should be broken then.
+ */
+ envName = Substring_Str(name);
+ envValue = getenv(envName.str);
+ if (envValue != NULL)
+ return VarNew(envName, envValue, true, false);
+ FStr_Done(&envName);
if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) {
var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash);
@@ -461,43 +456,35 @@ VarFind(const char *name, GNode *scope, Boolean elsewhere)
return var;
}
-/*
- * If the variable is an environment variable, free it.
- *
- * Input:
- * v the variable
- * freeValue true if the variable value should be freed as well
- *
- * Results:
- * TRUE if it is an environment variable, FALSE otherwise.
- */
-static Boolean
-VarFreeEnv(Var *v, Boolean freeValue)
+/* TODO: Replace these calls with VarFindSubstring, as far as possible. */
+static Var *
+VarFind(const char *name, GNode *scope, bool elsewhere)
+{
+ return VarFindSubstring(Substring_InitStr(name), scope, elsewhere);
+}
+
+/* If the variable is an environment variable, free it, including its value. */
+static void
+VarFreeEnv(Var *v)
{
- if (!(v->flags & VAR_FROM_ENV))
- return FALSE;
+ if (!v->fromEnv)
+ return;
FStr_Done(&v->name);
- if (freeValue)
- Buf_Done(&v->val);
- else
- Buf_DoneData(&v->val);
+ Buf_Done(&v->val);
free(v);
- return TRUE;
}
-/*
- * Add a new variable of the given name and value to the given scope.
- * The name and val arguments are duplicated so they may safely be freed.
- */
-static void
-VarAdd(const char *name, const char *val, GNode *scope, VarSetFlags flags)
+/* Add a new variable of the given name and value to the given scope. */
+static Var *
+VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags)
{
HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL);
- Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), val,
- flags & VAR_SET_READONLY ? VAR_READONLY : VAR_NONE);
+ Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value,
+ false, (flags & VAR_SET_READONLY) != 0);
HashEntry_Set(he, v);
- DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val);
+ DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, value);
+ return v;
}
/*
@@ -516,8 +503,8 @@ Var_Delete(GNode *scope, const char *varname)
}
DEBUG2(VAR, "%s:delete %s\n", scope->name, varname);
- v = HashEntry_Get(he);
- if (v->flags & VAR_EXPORTED)
+ v = he->value;
+ if (v->exported)
unsetenv(v->name.str);
if (strcmp(v->name.str, MAKE_EXPORTED) == 0)
var_exportedVars = VAR_EXPORTED_NONE;
@@ -573,7 +560,7 @@ Var_Undef(const char *arg)
return;
}
- varnames = Str_Words(expanded, FALSE);
+ varnames = Str_Words(expanded, false);
if (varnames.len == 1 && varnames.words[0][0] == '\0')
varnames.len = 0;
@@ -586,13 +573,13 @@ Var_Undef(const char *arg)
free(expanded);
}
-static Boolean
+static bool
MayExport(const char *name)
{
if (name[0] == '.')
- return FALSE; /* skip internals */
+ return false; /* skip internals */
if (name[0] == '-')
- return FALSE; /* skip misnamed variables */
+ return false; /* skip misnamed variables */
if (name[1] == '\0') {
/*
* A single char.
@@ -605,34 +592,34 @@ MayExport(const char *name)
case '%':
case '*':
case '!':
- return FALSE;
+ return false;
}
}
- return TRUE;
+ return true;
}
-static Boolean
+static bool
ExportVarEnv(Var *v)
{
const char *name = v->name.str;
char *val = v->val.data;
char *expr;
- if ((v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT))
- return FALSE; /* nothing to do */
+ if (v->exported && !v->reexport)
+ return false; /* nothing to do */
if (strchr(val, '$') == NULL) {
- if (!(v->flags & VAR_EXPORTED))
+ if (!v->exported)
setenv(name, val, 1);
- return TRUE;
+ return true;
}
- if (v->flags & VAR_IN_USE) {
+ if (v->inUse) {
/*
* We recursed while exporting in a child.
* This isn't going to end well, just skip it.
*/
- return FALSE;
+ return false;
}
/* XXX: name is injected without escaping it */
@@ -642,59 +629,58 @@ ExportVarEnv(Var *v)
setenv(name, val, 1);
free(val);
free(expr);
- return TRUE;
+ return true;
}
-static Boolean
+static bool
ExportVarPlain(Var *v)
{
if (strchr(v->val.data, '$') == NULL) {
setenv(v->name.str, v->val.data, 1);
- v->flags |= VAR_EXPORTED;
- v->flags &= ~(unsigned)VAR_REEXPORT;
- return TRUE;
+ v->exported = true;
+ v->reexport = false;
+ return true;
}
/*
* Flag the variable as something we need to re-export.
* No point actually exporting it now though,
* the child process can do it at the last minute.
+ * Avoid calling setenv more often than necessary since it can leak.
*/
- v->flags |= VAR_EXPORTED | VAR_REEXPORT;
- return TRUE;
+ v->exported = true;
+ v->reexport = true;
+ return true;
}
-static Boolean
+static bool
ExportVarLiteral(Var *v)
{
- if ((v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT))
- return FALSE;
+ if (v->exported && !v->reexport)
+ return false;
- if (!(v->flags & VAR_EXPORTED))
+ if (!v->exported)
setenv(v->name.str, v->val.data, 1);
- return TRUE;
+ return true;
}
/*
- * Export a single variable.
+ * Mark a single variable to be exported later for subprocesses.
*
- * We ignore make internal variables (those which start with '.').
- * Also we jump through some hoops to avoid calling setenv
- * more than necessary since it can leak.
- * We only manipulate flags of vars if 'parent' is set.
+ * Internal variables (those starting with '.') are not exported.
*/
-static Boolean
+static bool
ExportVar(const char *name, VarExportMode mode)
{
Var *v;
if (!MayExport(name))
- return FALSE;
+ return false;
- v = VarFind(name, SCOPE_GLOBAL, FALSE);
+ v = VarFind(name, SCOPE_GLOBAL, false);
if (v == NULL)
- return FALSE;
+ return false;
if (mode == VEM_ENV)
return ExportVarEnv(v);
@@ -719,7 +705,7 @@ Var_ReexportVars(void)
* We allow the makefiles to update MAKELEVEL and ensure
* children see a correctly incremented value.
*/
- char tmp[BUFSIZ];
+ char tmp[21];
snprintf(tmp, sizeof tmp, "%d", makelevel + 1);
setenv(MAKE_LEVEL_ENV, tmp, 1);
@@ -729,7 +715,7 @@ Var_ReexportVars(void)
if (var_exportedVars == VAR_EXPORTED_ALL) {
HashIter hi;
- /* Ouch! Exporting all variables at once is crazy... */
+ /* Ouch! Exporting all variables at once is crazy. */
HashIter_Init(&hi, &SCOPE_GLOBAL->vars);
while (HashIter_Next(&hi) != NULL) {
Var *var = hi.entry->value;
@@ -742,7 +728,7 @@ Var_ReexportVars(void)
&xvarnames);
/* TODO: handle errors */
if (xvarnames[0] != '\0') {
- Words varnames = Str_Words(xvarnames, FALSE);
+ Words varnames = Str_Words(xvarnames, false);
size_t i;
for (i = 0; i < varnames.len; i++)
@@ -753,9 +739,10 @@ Var_ReexportVars(void)
}
static void
-ExportVars(const char *varnames, Boolean isExport, VarExportMode mode)
+ExportVars(const char *varnames, bool isExport, VarExportMode mode)
+/* TODO: try to combine the parameters 'isExport' and 'mode'. */
{
- Words words = Str_Words(varnames, FALSE);
+ Words words = Str_Words(varnames, false);
size_t i;
if (words.len == 1 && words.words[0][0] == '\0')
@@ -776,7 +763,7 @@ ExportVars(const char *varnames, Boolean isExport, VarExportMode mode)
}
static void
-ExportVarsExpand(const char *uvarnames, Boolean isExport, VarExportMode mode)
+ExportVarsExpand(const char *uvarnames, bool isExport, VarExportMode mode)
{
char *xvarnames;
@@ -795,13 +782,13 @@ Var_Export(VarExportMode mode, const char *varnames)
return;
}
- ExportVarsExpand(varnames, TRUE, mode);
+ ExportVarsExpand(varnames, true, mode);
}
void
Var_ExportVars(const char *varnames)
{
- ExportVarsExpand(varnames, FALSE, VEM_PLAIN);
+ ExportVarsExpand(varnames, false, VEM_PLAIN);
}
@@ -834,7 +821,7 @@ ClearEnv(void)
}
static void
-GetVarnamesToUnexport(Boolean isEnv, const char *arg,
+GetVarnamesToUnexport(bool isEnv, const char *arg,
FStr *out_varnames, UnexportWhat *out_what)
{
UnexportWhat what;
@@ -845,6 +832,7 @@ GetVarnamesToUnexport(Boolean isEnv, const char *arg,
Parse_Error(PARSE_FATAL,
"The directive .unexport-env does not take "
"arguments");
+ /* continue anyway */
}
what = UNEXPORT_ENV;
@@ -870,17 +858,17 @@ GetVarnamesToUnexport(Boolean isEnv, const char *arg,
static void
UnexportVar(const char *varname, UnexportWhat what)
{
- Var *v = VarFind(varname, SCOPE_GLOBAL, FALSE);
+ Var *v = VarFind(varname, SCOPE_GLOBAL, false);
if (v == NULL) {
DEBUG1(VAR, "Not unexporting \"%s\" (not found)\n", varname);
return;
}
DEBUG1(VAR, "Unexporting \"%s\"\n", varname);
- if (what != UNEXPORT_ENV &&
- (v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT))
+ if (what != UNEXPORT_ENV && v->exported && !v->reexport)
unsetenv(v->name.str);
- v->flags &= ~(unsigned)(VAR_EXPORTED | VAR_REEXPORT);
+ v->exported = false;
+ v->reexport = false;
if (what == UNEXPORT_NAMED) {
/* Remove the variable names from .MAKE.EXPORTED. */
@@ -905,7 +893,7 @@ UnexportVars(FStr *varnames, UnexportWhat what)
if (what == UNEXPORT_ENV)
ClearEnv();
- words = Str_Words(varnames->str, FALSE);
+ words = Str_Words(varnames->str, false);
for (i = 0; i < words.len; i++) {
const char *varname = words.words[i];
UnexportVar(varname, what);
@@ -922,7 +910,7 @@ UnexportVars(FStr *varnames, UnexportWhat what)
* str must have the form "unexport[-env] varname...".
*/
void
-Var_UnExport(Boolean isEnv, const char *arg)
+Var_UnExport(bool isEnv, const char *arg)
{
UnexportWhat what;
FStr varnames;
@@ -932,6 +920,32 @@ Var_UnExport(Boolean isEnv, const char *arg)
FStr_Done(&varnames);
}
+/*
+ * When there is a variable of the same name in the command line scope, the
+ * global variable would not be visible anywhere. Therefore there is no
+ * point in setting it at all.
+ *
+ * See 'scope == SCOPE_CMDLINE' in Var_SetWithFlags.
+ */
+static bool
+ExistsInCmdline(const char *name, const char *val)
+{
+ Var *v;
+
+ v = VarFind(name, SCOPE_CMDLINE, false);
+ if (v == NULL)
+ return false;
+
+ if (v->fromCmd) {
+ DEBUG3(VAR, "%s: %s = %s ignored!\n",
+ SCOPE_GLOBAL->name, name, val);
+ return true;
+ }
+
+ VarFreeEnv(v);
+ return false;
+}
+
/* Set the variable to the value; the name is not expanded. */
void
Var_SetWithFlags(GNode *scope, const char *name, const char *val,
@@ -945,58 +959,48 @@ Var_SetWithFlags(GNode *scope, const char *name, const char *val,
return;
}
- if (scope == SCOPE_GLOBAL) {
- v = VarFind(name, SCOPE_CMDLINE, FALSE);
- if (v != NULL) {
- if (v->flags & VAR_FROM_CMD) {
- DEBUG3(VAR, "%s:%s = %s ignored!\n",
- scope->name, name, val);
- return;
- }
- VarFreeEnv(v, TRUE);
- }
- }
+ if (scope == SCOPE_GLOBAL && ExistsInCmdline(name, val))
+ return;
/*
* Only look for a variable in the given scope since anything set
* here will override anything in a lower scope, so there's not much
- * point in searching them all just to save a bit of memory...
+ * point in searching them all.
*/
- v = VarFind(name, scope, FALSE);
+ v = VarFind(name, scope, false);
if (v == NULL) {
if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) {
/*
* This var would normally prevent the same name being
* added to SCOPE_GLOBAL, so delete it from there if
* needed. Otherwise -V name may show the wrong value.
+ *
+ * See ExistsInCmdline.
*/
- /* XXX: name is expanded for the second time */
- Var_DeleteExpand(SCOPE_GLOBAL, name);
+ Var_Delete(SCOPE_GLOBAL, name);
}
- VarAdd(name, val, scope, flags);
+ v = VarAdd(name, val, scope, flags);
} else {
- if ((v->flags & VAR_READONLY) && !(flags & VAR_SET_READONLY)) {
- DEBUG3(VAR, "%s:%s = %s ignored (read-only)\n",
+ if (v->readOnly && !(flags & VAR_SET_READONLY)) {
+ DEBUG3(VAR, "%s: %s = %s ignored (read-only)\n",
scope->name, name, val);
return;
}
Buf_Empty(&v->val);
Buf_AddStr(&v->val, val);
- DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val);
- if (v->flags & VAR_EXPORTED)
+ DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, val);
+ if (v->exported)
ExportVar(name, VEM_PLAIN);
}
+
/*
* Any variables given on the command line are automatically exported
- * to the environment (as per POSIX standard)
- * Other than internals.
+ * to the environment (as per POSIX standard), except for internals.
*/
if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) &&
name[0] != '.') {
- if (v == NULL)
- v = VarFind(name, scope, FALSE); /* we just added it */
- v->flags |= VAR_FROM_CMD;
+ v->fromCmd = true;
/*
* If requested, don't export these in the environment
@@ -1006,14 +1010,18 @@ Var_SetWithFlags(GNode *scope, const char *name, const char *val,
*/
if (!opts.varNoExportEnv)
setenv(name, val, 1);
+ /* XXX: What about .MAKE.EXPORTED? */
+ /* XXX: Why not just mark the variable for needing export,
+ * as in ExportVarPlain? */
Global_Append(MAKEOVERRIDES, name);
}
+
if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0)
save_dollars = ParseBoolean(val, save_dollars);
if (v != NULL)
- VarFreeEnv(v, TRUE);
+ VarFreeEnv(v);
}
/* See Var_Set for documentation. */
@@ -1034,8 +1042,9 @@ Var_SetExpandWithFlags(GNode *scope, const char *name, const char *val,
}
if (varname.str[0] == '\0') {
- DEBUG2(VAR, "Var_Set(\"%s\", \"%s\", ...) "
- "name expands to empty string - ignored\n",
+ DEBUG2(VAR,
+ "Var_SetExpand: variable name \"%s\" expands "
+ "to empty string, with value \"%s\" - ignored\n",
unexpanded_name, val);
} else
Var_SetWithFlags(scope, varname.str, val, flags);
@@ -1099,23 +1108,23 @@ Var_Append(GNode *scope, const char *name, const char *val)
if (v == NULL) {
Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
- } else if (v->flags & VAR_READONLY) {
+ } else if (v->readOnly) {
DEBUG1(VAR, "Ignoring append to %s since it is read-only\n",
name);
- } else if (scope == SCOPE_CMDLINE || !(v->flags & VAR_FROM_CMD)) {
+ } else if (scope == SCOPE_CMDLINE || !v->fromCmd) {
Buf_AddByte(&v->val, ' ');
Buf_AddStr(&v->val, val);
- DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, v->val.data);
+ DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, v->val.data);
- if (v->flags & VAR_FROM_ENV) {
+ if (v->fromEnv) {
/*
* If the original variable came from the environment,
* we have to install it in the global scope (we
* could place it in the environment, but then we
* should provide a way to export other variables...)
*/
- v->flags &= ~(unsigned)VAR_FROM_ENV;
+ v->fromEnv = false;
/*
* This is the only place where a variable is
* created whose v->name is not the same as
@@ -1149,28 +1158,28 @@ Var_Append(GNode *scope, const char *name, const char *val)
void
Var_AppendExpand(GNode *scope, const char *name, const char *val)
{
- char *name_freeIt = NULL;
+ FStr xname = FStr_InitRefer(name);
assert(val != NULL);
if (strchr(name, '$') != NULL) {
- const char *unexpanded_name = name;
- (void)Var_Subst(name, scope, VARE_WANTRES, &name_freeIt);
+ char *expanded;
+ (void)Var_Subst(name, scope, VARE_WANTRES, &expanded);
/* TODO: handle errors */
- name = name_freeIt;
- if (name[0] == '\0') {
- /* TODO: update function name in the debug message */
- DEBUG2(VAR, "Var_Append(\"%s\", \"%s\", ...) "
- "name expands to empty string - ignored\n",
- unexpanded_name, val);
- free(name_freeIt);
+ xname = FStr_InitOwn(expanded);
+ if (expanded[0] == '\0') {
+ DEBUG2(VAR,
+ "Var_AppendExpand: variable name \"%s\" expands "
+ "to empty string, with value \"%s\" - ignored\n",
+ name, val);
+ FStr_Done(&xname);
return;
}
}
- Var_Append(scope, name, val);
+ Var_Append(scope, xname.str, val);
- free(name_freeIt);
+ FStr_Done(&xname);
}
void
@@ -1179,15 +1188,15 @@ Global_Append(const char *name, const char *value)
Var_Append(SCOPE_GLOBAL, name, value);
}
-Boolean
+bool
Var_Exists(GNode *scope, const char *name)
{
- Var *v = VarFind(name, scope, TRUE);
+ Var *v = VarFind(name, scope, true);
if (v == NULL)
- return FALSE;
+ return false;
- (void)VarFreeEnv(v, TRUE);
- return TRUE;
+ VarFreeEnv(v);
+ return true;
}
/*
@@ -1198,11 +1207,11 @@ Var_Exists(GNode *scope, const char *name)
* name Variable to find, is expanded once
* scope Scope in which to start search
*/
-Boolean
+bool
Var_ExistsExpand(GNode *scope, const char *name)
{
FStr varname = FStr_InitRefer(name);
- Boolean exists;
+ bool exists;
if (strchr(varname.str, '$') != NULL) {
char *expanded;
@@ -1226,22 +1235,25 @@ Var_ExistsExpand(GNode *scope, const char *name)
*
* Results:
* The value if the variable exists, NULL if it doesn't.
- * If the returned value is not NULL, the caller must free
- * out_freeIt when the returned value is no longer needed.
+ * The value is valid until the next modification to any variable.
*/
FStr
Var_Value(GNode *scope, const char *name)
{
- Var *v = VarFind(name, scope, TRUE);
+ Var *v = VarFind(name, scope, true);
char *value;
if (v == NULL)
return FStr_InitRefer(NULL);
- value = v->val.data;
- return VarFreeEnv(v, FALSE)
- ? FStr_InitOwn(value)
- : FStr_InitRefer(value);
+ if (!v->fromEnv)
+ return FStr_InitRefer(v->val.data);
+
+ /* Since environment variables are short-lived, free it now. */
+ FStr_Done(&v->name);
+ value = Buf_DoneData(&v->val);
+ free(v);
+ return FStr_InitOwn(value);
}
/*
@@ -1251,23 +1263,59 @@ Var_Value(GNode *scope, const char *name)
const char *
GNode_ValueDirect(GNode *gn, const char *name)
{
- Var *v = VarFind(name, gn, FALSE);
+ Var *v = VarFind(name, gn, false);
return v != NULL ? v->val.data : NULL;
}
+static VarEvalMode
+VarEvalMode_WithoutKeepDollar(VarEvalMode emode)
+{
+ if (emode == VARE_KEEP_DOLLAR_UNDEF)
+ return VARE_EVAL_KEEP_UNDEF;
+ if (emode == VARE_EVAL_KEEP_DOLLAR)
+ return VARE_WANTRES;
+ return emode;
+}
+
+static VarEvalMode
+VarEvalMode_UndefOk(VarEvalMode emode)
+{
+ return emode == VARE_UNDEFERR ? VARE_WANTRES : emode;
+}
+
+static bool
+VarEvalMode_ShouldEval(VarEvalMode emode)
+{
+ return emode != VARE_PARSE_ONLY;
+}
+
+static bool
+VarEvalMode_ShouldKeepUndef(VarEvalMode emode)
+{
+ return emode == VARE_EVAL_KEEP_UNDEF ||
+ emode == VARE_KEEP_DOLLAR_UNDEF;
+}
+
+static bool
+VarEvalMode_ShouldKeepDollar(VarEvalMode emode)
+{
+ return emode == VARE_EVAL_KEEP_DOLLAR ||
+ emode == VARE_KEEP_DOLLAR_UNDEF;
+}
+
static void
SepBuf_Init(SepBuf *buf, char sep)
{
Buf_InitSize(&buf->buf, 32);
- buf->needSep = FALSE;
+ buf->needSep = false;
buf->sep = sep;
}
static void
SepBuf_Sep(SepBuf *buf)
{
- buf->needSep = TRUE;
+ buf->needSep = true;
}
static void
@@ -1277,7 +1325,7 @@ SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size)
return;
if (buf->needSep && buf->sep != '\0') {
Buf_AddByte(&buf->buf, buf->sep);
- buf->needSep = FALSE;
+ buf->needSep = false;
}
Buf_AddBytes(&buf->buf, mem, mem_size);
}
@@ -1294,6 +1342,12 @@ SepBuf_AddStr(SepBuf *buf, const char *str)
SepBuf_AddBytes(buf, str, strlen(str));
}
+static void
+SepBuf_AddSubstring(SepBuf *buf, Substring sub)
+{
+ SepBuf_AddBytesBetween(buf, sub.start, sub.end);
+}
+
static char *
SepBuf_DoneData(SepBuf *buf)
{
@@ -1306,10 +1360,14 @@ SepBuf_DoneData(SepBuf *buf)
* and typically adds a modification of this word to the buffer. It may also
* do nothing or add several words.
*
- * For example, in ${:Ua b c:M*2}, the callback is called 3 times, once for
- * each word of "a b c".
+ * For example, when evaluating the modifier ':M*b' in ${:Ua b c:M*b}, the
+ * callback is called 3 times, once for "a", "b" and "c".
+ *
+ * Some ModifyWord functions assume that they are always passed a
+ * null-terminated substring, which is currently guaranteed but may change in
+ * the future.
*/
-typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data);
+typedef void (*ModifyWordProc)(Substring word, SepBuf *buf, void *data);
/*
@@ -1318,13 +1376,9 @@ typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data);
*/
/*ARGSUSED*/
static void
-ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
+ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
- const char *slash = strrchr(word, '/');
- if (slash != NULL)
- SepBuf_AddBytesBetween(buf, word, slash);
- else
- SepBuf_AddStr(buf, ".");
+ SepBuf_AddSubstring(buf, Substring_Dirname(word));
}
/*
@@ -1333,9 +1387,9 @@ ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
*/
/*ARGSUSED*/
static void
-ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
+ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
- SepBuf_AddStr(buf, str_basename(word));
+ SepBuf_AddSubstring(buf, Substring_Basename(word));
}
/*
@@ -1344,24 +1398,26 @@ ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
*/
/*ARGSUSED*/
static void
-ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
+ModifyWord_Suffix(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
- const char *lastDot = strrchr(word, '.');
+ const char *lastDot = Substring_LastIndex(word, '.');
if (lastDot != NULL)
- SepBuf_AddStr(buf, lastDot + 1);
+ SepBuf_AddBytesBetween(buf, lastDot + 1, word.end);
}
/*
* Callback for ModifyWords to implement the :R modifier.
- * Add the basename of the given word to the buffer.
+ * Add the filename without extension of the given word to the buffer.
*/
/*ARGSUSED*/
static void
-ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
+ModifyWord_Root(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
- const char *lastDot = strrchr(word, '.');
- size_t len = lastDot != NULL ? (size_t)(lastDot - word) : strlen(word);
- SepBuf_AddBytes(buf, word, len);
+ const char *lastDot, *end;
+
+ lastDot = Substring_LastIndex(word, '.');
+ end = lastDot != NULL ? lastDot : word.end;
+ SepBuf_AddBytesBetween(buf, word.start, end);
}
/*
@@ -1369,12 +1425,13 @@ ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
* Place the word in the buffer if it matches the given pattern.
*/
static void
-ModifyWord_Match(const char *word, SepBuf *buf, void *data)
+ModifyWord_Match(Substring word, SepBuf *buf, void *data)
{
const char *pattern = data;
- DEBUG2(VAR, "VarMatch [%s] [%s]\n", word, pattern);
- if (Str_Match(word, pattern))
- SepBuf_AddStr(buf, word);
+
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ if (Str_Match(word.start, pattern))
+ SepBuf_AddSubstring(buf, word);
}
/*
@@ -1382,194 +1439,146 @@ ModifyWord_Match(const char *word, SepBuf *buf, void *data)
* Place the word in the buffer if it doesn't match the given pattern.
*/
static void
-ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data)
+ModifyWord_NoMatch(Substring word, SepBuf *buf, void *data)
{
const char *pattern = data;
- if (!Str_Match(word, pattern))
- SepBuf_AddStr(buf, word);
-}
-
-#ifdef SYSVVARSUB
-
-/*
- * Check word against pattern for a match (% is a wildcard).
- *
- * Input:
- * word Word to examine
- * pattern Pattern to examine against
- *
- * Results:
- * Returns the start of the match, or NULL.
- * out_match_len returns the length of the match, if any.
- * out_hasPercent returns whether the pattern contains a percent.
- */
-static const char *
-SysVMatch(const char *word, const char *pattern,
- size_t *out_match_len, Boolean *out_hasPercent)
-{
- const char *p = pattern;
- const char *w = word;
- const char *percent;
- size_t w_len;
- size_t p_len;
- const char *w_tail;
-
- *out_hasPercent = FALSE;
- percent = strchr(p, '%');
- if (percent != NULL) { /* ${VAR:...%...=...} */
- *out_hasPercent = TRUE;
- if (w[0] == '\0')
- return NULL; /* empty word does not match pattern */
-
- /* check that the prefix matches */
- for (; p != percent && *w != '\0' && *w == *p; w++, p++)
- continue;
- if (p != percent)
- return NULL; /* No match */
-
- p++; /* Skip the percent */
- if (*p == '\0') {
- /* No more pattern, return the rest of the string */
- *out_match_len = strlen(w);
- return w;
- }
- }
-
- /* Test whether the tail matches */
- w_len = strlen(w);
- p_len = strlen(p);
- if (w_len < p_len)
- return NULL;
-
- w_tail = w + w_len - p_len;
- if (memcmp(p, w_tail, p_len) != 0)
- return NULL;
- *out_match_len = (size_t)(w_tail - w);
- return w;
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ if (!Str_Match(word.start, pattern))
+ SepBuf_AddSubstring(buf, word);
}
-struct ModifyWord_SYSVSubstArgs {
+#ifdef SYSVVARSUB
+struct ModifyWord_SysVSubstArgs {
GNode *scope;
- const char *lhs;
+ Substring lhsPrefix;
+ bool lhsPercent;
+ Substring lhsSuffix;
const char *rhs;
};
/* Callback for ModifyWords to implement the :%.from=%.to modifier. */
static void
-ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data)
+ModifyWord_SysVSubst(Substring word, SepBuf *buf, void *data)
{
- const struct ModifyWord_SYSVSubstArgs *args = data;
- char *rhs_expanded;
- const char *rhs;
+ const struct ModifyWord_SysVSubstArgs *args = data;
+ FStr rhs;
+ char *rhsExp;
const char *percent;
- size_t match_len;
- Boolean lhsPercent;
- const char *match = SysVMatch(word, args->lhs, &match_len, &lhsPercent);
- if (match == NULL) {
- SepBuf_AddStr(buf, word);
+ if (Substring_IsEmpty(word))
return;
- }
- /*
- * Append rhs to the buffer, substituting the first '%' with the
- * match, but only if the lhs had a '%' as well.
- */
+ if (!Substring_HasPrefix(word, args->lhsPrefix))
+ goto no_match;
+ if (!Substring_HasSuffix(word, args->lhsSuffix))
+ goto no_match;
- (void)Var_Subst(args->rhs, args->scope, VARE_WANTRES, &rhs_expanded);
- /* TODO: handle errors */
+ rhs = FStr_InitRefer(args->rhs);
+ if (strchr(rhs.str, '$') != NULL) {
+ (void)Var_Subst(args->rhs, args->scope, VARE_WANTRES, &rhsExp);
+ /* TODO: handle errors */
+ rhs = FStr_InitOwn(rhsExp);
+ }
- rhs = rhs_expanded;
- percent = strchr(rhs, '%');
+ percent = args->lhsPercent ? strchr(rhs.str, '%') : NULL;
- if (percent != NULL && lhsPercent) {
- /* Copy the prefix of the replacement pattern */
- SepBuf_AddBytesBetween(buf, rhs, percent);
- rhs = percent + 1;
- }
- if (percent != NULL || !lhsPercent)
- SepBuf_AddBytes(buf, match, match_len);
+ if (percent != NULL)
+ SepBuf_AddBytesBetween(buf, rhs.str, percent);
+ if (percent != NULL || !args->lhsPercent)
+ SepBuf_AddBytesBetween(buf,
+ word.start + Substring_Length(args->lhsPrefix),
+ word.end - Substring_Length(args->lhsSuffix));
+ SepBuf_AddStr(buf, percent != NULL ? percent + 1 : rhs.str);
- /* Append the suffix of the replacement pattern */
- SepBuf_AddStr(buf, rhs);
+ FStr_Done(&rhs);
+ return;
- free(rhs_expanded);
+no_match:
+ SepBuf_AddSubstring(buf, word);
}
#endif
struct ModifyWord_SubstArgs {
- const char *lhs;
- size_t lhsLen;
- const char *rhs;
- size_t rhsLen;
- VarPatternFlags pflags;
- Boolean matched;
+ Substring lhs;
+ Substring rhs;
+ PatternFlags pflags;
+ bool matched;
};
+static const char *
+Substring_Find(Substring haystack, Substring needle)
+{
+ size_t len, needleLen, i;
+
+ len = Substring_Length(haystack);
+ needleLen = Substring_Length(needle);
+ for (i = 0; i + needleLen <= len; i++)
+ if (memcmp(haystack.start + i, needle.start, needleLen) == 0)
+ return haystack.start + i;
+ return NULL;
+}
+
/*
* Callback for ModifyWords to implement the :S,from,to, modifier.
* Perform a string substitution on the given word.
*/
static void
-ModifyWord_Subst(const char *word, SepBuf *buf, void *data)
+ModifyWord_Subst(Substring word, SepBuf *buf, void *data)
{
- size_t wordLen = strlen(word);
struct ModifyWord_SubstArgs *args = data;
- const char *match;
+ size_t wordLen, lhsLen;
+ const char *wordEnd, *match;
+ wordLen = Substring_Length(word);
+ wordEnd = word.end;
if (args->pflags.subOnce && args->matched)
goto nosub;
+ lhsLen = Substring_Length(args->lhs);
if (args->pflags.anchorStart) {
- if (wordLen < args->lhsLen ||
- memcmp(word, args->lhs, args->lhsLen) != 0)
+ if (wordLen < lhsLen ||
+ memcmp(word.start, args->lhs.start, lhsLen) != 0)
goto nosub;
- if ((args->pflags.anchorEnd) && wordLen != args->lhsLen)
+ if (args->pflags.anchorEnd && wordLen != lhsLen)
goto nosub;
/* :S,^prefix,replacement, or :S,^whole$,replacement, */
- SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
- SepBuf_AddBytes(buf, word + args->lhsLen,
- wordLen - args->lhsLen);
- args->matched = TRUE;
+ SepBuf_AddSubstring(buf, args->rhs);
+ SepBuf_AddBytesBetween(buf, word.start + lhsLen, wordEnd);
+ args->matched = true;
return;
}
if (args->pflags.anchorEnd) {
- const char *start;
-
- if (wordLen < args->lhsLen)
+ if (wordLen < lhsLen)
goto nosub;
-
- start = word + (wordLen - args->lhsLen);
- if (memcmp(start, args->lhs, args->lhsLen) != 0)
+ if (memcmp(wordEnd - lhsLen, args->lhs.start, lhsLen) != 0)
goto nosub;
/* :S,suffix$,replacement, */
- SepBuf_AddBytesBetween(buf, word, start);
- SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
- args->matched = TRUE;
+ SepBuf_AddBytesBetween(buf, word.start, wordEnd - lhsLen);
+ SepBuf_AddSubstring(buf, args->rhs);
+ args->matched = true;
return;
}
- if (args->lhs[0] == '\0')
+ if (Substring_IsEmpty(args->lhs))
goto nosub;
/* unanchored case, may match more than once */
- while ((match = strstr(word, args->lhs)) != NULL) {
- SepBuf_AddBytesBetween(buf, word, match);
- SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
- args->matched = TRUE;
- wordLen -= (size_t)(match - word) + args->lhsLen;
- word += (size_t)(match - word) + args->lhsLen;
- if (wordLen == 0 || !args->pflags.subGlobal)
+ while ((match = Substring_Find(word, args->lhs)) != NULL) {
+ SepBuf_AddBytesBetween(buf, word.start, match);
+ SepBuf_AddSubstring(buf, args->rhs);
+ args->matched = true;
+ word.start = match + lhsLen;
+ if (Substring_IsEmpty(word) || !args->pflags.subGlobal)
break;
}
nosub:
- SepBuf_AddBytes(buf, word, wordLen);
+ SepBuf_AddSubstring(buf, word);
}
#ifndef NO_REGEX
@@ -1587,9 +1596,9 @@ VarREError(int reerr, const regex_t *pat, const char *str)
struct ModifyWord_SubstRegexArgs {
regex_t re;
size_t nsub;
- char *replace;
- VarPatternFlags pflags;
- Boolean matched;
+ const char *replace;
+ PatternFlags pflags;
+ bool matched;
};
/*
@@ -1597,15 +1606,17 @@ struct ModifyWord_SubstRegexArgs {
* Perform a regex substitution on the given word.
*/
static void
-ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data)
+ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data)
{
struct ModifyWord_SubstRegexArgs *args = data;
int xrv;
- const char *wp = word;
- char *rp;
+ const char *wp;
+ const char *rp;
int flags = 0;
regmatch_t m[10];
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ wp = word.start;
if (args->pflags.subOnce && args->matched)
goto nosub;
@@ -1614,9 +1625,14 @@ tryagain:
switch (xrv) {
case 0:
- args->matched = TRUE;
+ args->matched = true;
SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so);
+ /*
+ * Replacement of regular expressions is not specified by
+ * POSIX, therefore implement it here.
+ */
+
for (rp = args->replace; *rp != '\0'; rp++) {
if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
SepBuf_AddBytes(buf, rp + 1, 1);
@@ -1643,9 +1659,11 @@ tryagain:
Error("No subexpression \\%u",
(unsigned)n);
} else if (m[n].rm_so == -1) {
- Error(
- "No match for subexpression \\%u",
- (unsigned)n);
+ if (opts.strict) {
+ Error(
+ "No match for subexpression \\%u",
+ (unsigned)n);
+ }
} else {
SepBuf_AddBytesBetween(buf,
wp + m[n].rm_so, wp + m[n].rm_eo);
@@ -1680,34 +1698,35 @@ tryagain:
struct ModifyWord_LoopArgs {
GNode *scope;
- char *tvar; /* name of temporary variable */
- char *str; /* string to expand */
- VarEvalFlags eflags;
+ const char *var; /* name of the temporary variable */
+ const char *body; /* string to expand */
+ VarEvalMode emode;
};
/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */
static void
-ModifyWord_Loop(const char *word, SepBuf *buf, void *data)
+ModifyWord_Loop(Substring word, SepBuf *buf, void *data)
{
const struct ModifyWord_LoopArgs *args;
char *s;
- if (word[0] == '\0')
+ if (Substring_IsEmpty(word))
return;
args = data;
- /* XXX: The variable name should not be expanded here. */
- Var_SetExpandWithFlags(args->scope, args->tvar, word,
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ Var_SetWithFlags(args->scope, args->var, word.start,
VAR_SET_NO_EXPORT);
- (void)Var_Subst(args->str, args->scope, args->eflags, &s);
+ (void)Var_Subst(args->body, args->scope, args->emode, &s);
/* TODO: handle errors */
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
DEBUG4(VAR, "ModifyWord_Loop: "
"in \"%s\", replace \"%s\" with \"%s\" to \"%s\"\n",
- word, args->tvar, args->str, s);
+ word.start, args->var, args->body, s);
if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n'))
- buf->needSep = FALSE;
+ buf->needSep = false;
SepBuf_AddStr(buf, s);
free(s);
}
@@ -1718,8 +1737,8 @@ ModifyWord_Loop(const char *word, SepBuf *buf, void *data)
* It can also reverse the words.
*/
static char *
-VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first,
- int last)
+VarSelectWords(const char *str, int first, int last,
+ char sep, bool oneBigWord)
{
Words words;
int len, start, end, step;
@@ -1737,7 +1756,7 @@ VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first,
words.words[0] = words.freeIt;
words.words[1] = NULL;
} else {
- words = Str_Words(str, FALSE);
+ words = Str_Words(str, false);
}
/*
@@ -1779,60 +1798,18 @@ VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first,
*/
/*ARGSUSED*/
static void
-ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
+ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
{
struct stat st;
char rbuf[MAXPATHLEN];
+ const char *rp;
- const char *rp = cached_realpath(word, rbuf);
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ rp = cached_realpath(word.start, rbuf);
if (rp != NULL && *rp == '/' && stat(rp, &st) == 0)
- word = rp;
-
- SepBuf_AddStr(buf, word);
-}
-
-/*
- * Modify each of the words of the passed string using the given function.
- *
- * Input:
- * str String whose words should be modified
- * modifyWord Function that modifies a single word
- * modifyWord_args Custom arguments for modifyWord
- *
- * Results:
- * A string of all the words modified appropriately.
- */
-static char *
-ModifyWords(const char *str,
- ModifyWordsCallback modifyWord, void *modifyWord_args,
- Boolean oneBigWord, char sep)
-{
- SepBuf result;
- Words words;
- size_t i;
-
- if (oneBigWord) {
- SepBuf_Init(&result, sep);
- modifyWord(str, &result, modifyWord_args);
- return SepBuf_DoneData(&result);
- }
-
- SepBuf_Init(&result, sep);
-
- words = Str_Words(str, FALSE);
-
- DEBUG2(VAR, "ModifyWords: split \"%s\" into %u words\n",
- str, (unsigned)words.len);
-
- for (i = 0; i < words.len; i++) {
- modifyWord(words.words[i], &result, modifyWord_args);
- if (result.buf.len > 0)
- SepBuf_Sep(&result);
- }
-
- Words_Free(words);
-
- return SepBuf_DoneData(&result);
+ SepBuf_AddStr(buf, rp);
+ else
+ SepBuf_AddSubstring(buf, word);
}
@@ -1846,7 +1823,7 @@ Words_JoinFree(Words words)
for (i = 0; i < words.len; i++) {
if (i != 0) {
- /* XXX: Use st->sep instead of ' ', for consistency. */
+ /* XXX: Use ch->sep instead of ' ', for consistency. */
Buf_AddByte(&buf, ' ');
}
Buf_AddStr(&buf, words.words[i]);
@@ -1857,51 +1834,31 @@ Words_JoinFree(Words words)
return Buf_DoneData(&buf);
}
-/* Remove adjacent duplicate words. */
-static char *
-VarUniq(const char *str)
-{
- Words words = Str_Words(str, FALSE);
-
- if (words.len > 1) {
- size_t i, j;
- for (j = 0, i = 1; i < words.len; i++)
- if (strcmp(words.words[i], words.words[j]) != 0 &&
- (++j != i))
- words.words[j] = words.words[i];
- words.len = j + 1;
- }
-
- return Words_JoinFree(words);
-}
-
/*
* Quote shell meta-characters and space characters in the string.
* If quoteDollar is set, also quote and double any '$' characters.
*/
-static char *
-VarQuote(const char *str, Boolean quoteDollar)
+static void
+VarQuote(const char *str, bool quoteDollar, LazyBuf *buf)
{
- Buffer buf;
- Buf_Init(&buf);
+ const char *p;
- for (; *str != '\0'; str++) {
- if (*str == '\n') {
+ LazyBuf_Init(buf, str);
+ for (p = str; *p != '\0'; p++) {
+ if (*p == '\n') {
const char *newline = Shell_GetNewline();
if (newline == NULL)
newline = "\\\n";
- Buf_AddStr(&buf, newline);
+ LazyBuf_AddStr(buf, newline);
continue;
}
- if (ch_isspace(*str) || is_shell_metachar((unsigned char)*str))
- Buf_AddByte(&buf, '\\');
- Buf_AddByte(&buf, *str);
- if (quoteDollar && *str == '$')
- Buf_AddStr(&buf, "\\$");
+ if (ch_isspace(*p) || is_shell_metachar((unsigned char)*p))
+ LazyBuf_Add(buf, '\\');
+ LazyBuf_Add(buf, *p);
+ if (quoteDollar && *p == '$')
+ LazyBuf_AddStr(buf, "\\$");
}
-
- return Buf_DoneData(&buf);
}
/*
@@ -1969,7 +1926,7 @@ VarHash(const char *str)
}
static char *
-VarStrftime(const char *fmt, Boolean zulu, time_t tim)
+VarStrftime(const char *fmt, bool zulu, time_t tim)
{
char buf[BUFSIZ];
@@ -1985,25 +1942,15 @@ VarStrftime(const char *fmt, Boolean zulu, time_t tim)
/*
* The ApplyModifier functions take an expression that is being evaluated.
- * Their task is to apply a single modifier to the expression.
- * To do this, they parse the modifier and its parameters from pp and apply
- * the parsed modifier to the current value of the expression, generating a
- * new value from it.
- *
- * The modifier typically lasts until the next ':', or a closing '}' or ')'
- * (taken from st->endc), or the end of the string (parse error).
- *
- * The high-level behavior of these functions is:
- *
- * 1. parse the modifier
- * 2. evaluate the modifier
- * 3. housekeeping
+ * Their task is to apply a single modifier to the expression. This involves
+ * parsing the modifier, evaluating it and finally updating the value of the
+ * expression.
*
* Parsing the modifier
*
* If parsing succeeds, the parsing position *pp is updated to point to the
* first character following the modifier, which typically is either ':' or
- * st->endc. The modifier doesn't have to check for this delimiter character,
+ * ch->endc. The modifier doesn't have to check for this delimiter character,
* this is done by ApplyModifiers.
*
* XXX: As of 2020-11-15, some modifiers such as :S, :C, :P, :L do not
@@ -2025,18 +1972,21 @@ VarStrftime(const char *fmt, Boolean zulu, time_t tim)
* message. Both of these return values will stop processing the variable
* expression. (XXX: As of 2020-08-23, evaluation of the whole string
* continues nevertheless after skipping a few bytes, which essentially is
- * undefined behavior. Not in the sense of C, but still it's impossible to
- * predict what happens in the parser.)
+ * undefined behavior. Not in the sense of C, but still the resulting string
+ * is garbage.)
*
* Evaluating the modifier
*
* After parsing, the modifier is evaluated. The side effects from evaluating
* nested variable expressions in the modifier text often already happen
- * during parsing though.
+ * during parsing though. For most modifiers this doesn't matter since their
+ * only noticeable effect is that the update the value of the expression.
+ * Some modifiers such as ':sh' or '::=' have noticeable side effects though.
*
* Evaluating the modifier usually takes the current value of the variable
- * expression from st->val, or the variable name from st->var->name and stores
- * the result in st->newVal.
+ * expression from ch->expr->value, or the variable name from ch->var->name
+ * and stores the result back in expr->value via Expr_SetValueOwn or
+ * Expr_SetValueRefer.
*
* If evaluating fails (as of 2020-08-23), an error message is printed using
* Error. This function has no side-effects, it really just prints the error
@@ -2047,69 +1997,134 @@ VarStrftime(const char *fmt, Boolean zulu, time_t tim)
* Housekeeping
*
* Some modifiers such as :D and :U turn undefined expressions into defined
- * expressions (see VEF_UNDEF, VEF_DEF).
+ * expressions (see Expr_Define).
*
* Some modifiers need to free some memory.
*/
-typedef enum VarExprStatus {
- /* The variable expression is based in a regular, defined variable. */
- VES_NONE,
+typedef enum ExprDefined {
+ /* The variable expression is based on a regular, defined variable. */
+ DEF_REGULAR,
/* The variable expression is based on an undefined variable. */
- VES_UNDEF,
+ DEF_UNDEF,
/*
* The variable expression started as an undefined expression, but one
- * of the modifiers (such as :D or :U) has turned the expression from
- * undefined to defined.
+ * of the modifiers (such as ':D' or ':U') has turned the expression
+ * from undefined to defined.
*/
- VES_DEF
-} VarExprStatus;
+ DEF_DEFINED
+} ExprDefined;
-static const char * const VarExprStatus_Name[] = {
- "none",
- "VES_UNDEF",
- "VES_DEF"
+static const char *const ExprDefined_Name[] = {
+ "regular",
+ "undefined",
+ "defined"
};
-typedef struct ApplyModifiersState {
+#if __STDC_VERSION__ >= 199901L
+#define const_member const
+#else
+#define const_member /* no const possible */
+#endif
+
+/* A variable expression such as $@ or ${VAR:Mpattern:Q}. */
+typedef struct Expr {
+ const char *name;
+ FStr value;
+ VarEvalMode const_member emode;
+ GNode *const_member scope;
+ ExprDefined defined;
+} Expr;
+
+/*
+ * The status of applying a chain of modifiers to an expression.
+ *
+ * The modifiers of an expression are broken into chains of modifiers,
+ * starting a new nested chain whenever an indirect modifier starts. There
+ * are at most 2 nesting levels: the outer one for the direct modifiers, and
+ * the inner one for the indirect modifiers.
+ *
+ * For example, the expression ${VAR:M*:${IND1}:${IND2}:O:u} has 3 chains of
+ * modifiers:
+ *
+ * Chain 1 starts with the single modifier ':M*'.
+ * Chain 2 starts with all modifiers from ${IND1}.
+ * Chain 2 ends at the ':' between ${IND1} and ${IND2}.
+ * Chain 3 starts with all modifiers from ${IND2}.
+ * Chain 3 ends at the ':' after ${IND2}.
+ * Chain 1 continues with the the 2 modifiers ':O' and ':u'.
+ * Chain 1 ends at the final '}' of the expression.
+ *
+ * After such a chain ends, its properties no longer have any effect.
+ *
+ * It may or may not have been intended that 'defined' has scope Expr while
+ * 'sep' and 'oneBigWord' have smaller scope.
+ *
+ * See varmod-indirect.mk.
+ */
+typedef struct ModChain {
+ Expr *expr;
/* '\0' or '{' or '(' */
- const char startc;
+ char const_member startc;
/* '\0' or '}' or ')' */
- const char endc;
- Var *const var;
- GNode *const scope;
- const VarEvalFlags eflags;
- /*
- * The new value of the expression, after applying the modifier,
- * never NULL.
- */
- FStr newVal;
+ char const_member endc;
/* Word separator in expansions (see the :ts modifier). */
char sep;
/*
- * TRUE if some modifiers that otherwise split the variable value
+ * True if some modifiers that otherwise split the variable value
* into words, like :S and :C, treat the variable value as a single
* big word, possibly containing spaces.
*/
- Boolean oneBigWord;
- VarExprStatus exprStatus;
-} ApplyModifiersState;
+ bool oneBigWord;
+} ModChain;
+
+static void
+Expr_Define(Expr *expr)
+{
+ if (expr->defined == DEF_UNDEF)
+ expr->defined = DEF_DEFINED;
+}
+
+static void
+Expr_SetValue(Expr *expr, FStr value)
+{
+ FStr_Done(&expr->value);
+ expr->value = value;
+}
+
+static void
+Expr_SetValueOwn(Expr *expr, char *value)
+{
+ Expr_SetValue(expr, FStr_InitOwn(value));
+}
static void
-ApplyModifiersState_Define(ApplyModifiersState *st)
+Expr_SetValueRefer(Expr *expr, const char *value)
+{
+ Expr_SetValue(expr, FStr_InitRefer(value));
+}
+
+static bool
+Expr_ShouldEval(const Expr *expr)
{
- if (st->exprStatus == VES_UNDEF)
- st->exprStatus = VES_DEF;
+ return VarEvalMode_ShouldEval(expr->emode);
}
+static bool
+ModChain_ShouldEval(const ModChain *ch)
+{
+ return Expr_ShouldEval(ch->expr);
+}
+
+
typedef enum ApplyModifierResult {
/* Continue parsing */
AMR_OK,
- /* Not a match, try other modifiers as well */
+ /* Not a match, try other modifiers as well. */
AMR_UNKNOWN,
- /* Error out with "Bad modifier" message */
+ /* Error out with "Bad modifier" message. */
AMR_BAD,
- /* Error out without error message */
+ /* Error out without the standard error message. */
AMR_CLEANUP
} ApplyModifierResult;
@@ -2117,83 +2132,78 @@ typedef enum ApplyModifierResult {
* Allow backslashes to escape the delimiter, $, and \, but don't touch other
* backslashes.
*/
-static Boolean
+static bool
IsEscapedModifierPart(const char *p, char delim,
struct ModifyWord_SubstArgs *subst)
{
if (p[0] != '\\')
- return FALSE;
+ return false;
if (p[1] == delim || p[1] == '\\' || p[1] == '$')
- return TRUE;
+ return true;
return p[1] == '&' && subst != NULL;
}
-/* See ParseModifierPart */
+/* See ParseModifierPart for the documentation. */
static VarParseResult
ParseModifierPartSubst(
const char **pp,
char delim,
- VarEvalFlags eflags,
- ApplyModifiersState *st,
- char **out_part,
- /* Optionally stores the length of the returned string, just to save
- * another strlen call. */
- size_t *out_length,
- /* For the first part of the :S modifier, sets the VARP_ANCHOR_END flag
- * if the last character of the pattern is a $. */
- VarPatternFlags *out_pflags,
+ VarEvalMode emode,
+ ModChain *ch,
+ LazyBuf *part,
+ /* For the first part of the modifier ':S', set anchorEnd if the last
+ * character of the pattern is a $. */
+ PatternFlags *out_pflags,
/* For the second part of the :S modifier, allow ampersands to be
* escaped and replace unescaped ampersands with subst->lhs. */
struct ModifyWord_SubstArgs *subst
)
{
- Buffer buf;
const char *p;
- Buf_Init(&buf);
+ p = *pp;
+ LazyBuf_Init(part, p);
/*
* Skim through until the matching delimiter is found; pick up
* variable expressions on the way.
*/
- p = *pp;
while (*p != '\0' && *p != delim) {
const char *varstart;
if (IsEscapedModifierPart(p, delim, subst)) {
- Buf_AddByte(&buf, p[1]);
+ LazyBuf_Add(part, p[1]);
p += 2;
continue;
}
if (*p != '$') { /* Unescaped, simple text */
if (subst != NULL && *p == '&')
- Buf_AddBytes(&buf, subst->lhs, subst->lhsLen);
+ LazyBuf_AddSubstring(part, subst->lhs);
else
- Buf_AddByte(&buf, *p);
+ LazyBuf_Add(part, *p);
p++;
continue;
}
if (p[1] == delim) { /* Unescaped $ at end of pattern */
if (out_pflags != NULL)
- out_pflags->anchorEnd = TRUE;
+ out_pflags->anchorEnd = true;
else
- Buf_AddByte(&buf, *p);
+ LazyBuf_Add(part, *p);
p++;
continue;
}
- if (eflags & VARE_WANTRES) { /* Nested variable, evaluated */
+ if (VarEvalMode_ShouldEval(emode)) {
+ /* Nested variable, evaluated */
const char *nested_p = p;
FStr nested_val;
- VarEvalFlags nested_eflags =
- eflags & ~(unsigned)VARE_KEEP_DOLLAR;
- (void)Var_Parse(&nested_p, st->scope, nested_eflags,
- &nested_val);
+ (void)Var_Parse(&nested_p, ch->expr->scope,
+ VarEvalMode_WithoutKeepDollar(emode), &nested_val);
/* TODO: handle errors */
- Buf_AddStr(&buf, nested_val.str);
+ LazyBuf_AddStr(part, nested_val.str);
FStr_Done(&nested_val);
p += nested_p - p;
continue;
@@ -2201,10 +2211,10 @@ ParseModifierPartSubst(
/*
* XXX: This whole block is very similar to Var_Parse without
- * VARE_WANTRES. There may be subtle edge cases though that
- * are not yet covered in the unit tests and that are parsed
- * differently, depending on whether they are evaluated or
- * not.
+ * VARE_WANTRES. There may be subtle edge cases
+ * though that are not yet covered in the unit tests and that
+ * are parsed differently, depending on whether they are
+ * evaluated or not.
*
* This subtle difference is not documented in the manual
* page, neither is the difference between parsing :D and
@@ -2231,27 +2241,29 @@ ParseModifierPartSubst(
depth--;
}
}
- Buf_AddBytesBetween(&buf, varstart, p);
+ LazyBuf_AddBytesBetween(part, varstart, p);
} else {
- Buf_AddByte(&buf, *varstart);
+ LazyBuf_Add(part, *varstart);
p++;
}
}
if (*p != delim) {
*pp = p;
- Error("Unfinished modifier for %s ('%c' missing)",
- st->var->name.str, delim);
- *out_part = NULL;
+ Error("Unfinished modifier for \"%s\" ('%c' missing)",
+ ch->expr->name, delim);
+ LazyBuf_Done(part);
return VPR_ERR;
}
*pp = p + 1;
- if (out_length != NULL)
- *out_length = buf.len;
- *out_part = Buf_DoneData(&buf);
- DEBUG1(VAR, "Modifier part: \"%s\"\n", *out_part);
+ {
+ Substring sub = LazyBuf_Get(part);
+ DEBUG2(VAR, "Modifier part: \"%.*s\"\n",
+ (int)Substring_Length(sub), sub.start);
+ }
+
return VPR_OK;
}
@@ -2261,10 +2273,9 @@ ParseModifierPartSubst(
* including the next unescaped delimiter. The delimiter, as well as the
* backslash or the dollar, can be escaped with a backslash.
*
- * Return the parsed (and possibly expanded) string, or NULL if no delimiter
- * was found. On successful return, the parsing position pp points right
- * after the delimiter. The delimiter is not included in the returned
- * value though.
+ * Return VPR_OK if parsing succeeded, together with the parsed (and possibly
+ * expanded) part. In that case, pp points right after the delimiter. The
+ * delimiter is not included in the part though.
*/
static VarParseResult
ParseModifierPart(
@@ -2272,36 +2283,39 @@ ParseModifierPart(
const char **pp,
/* Parsing stops at this delimiter */
char delim,
- /* Flags for evaluating nested variables; if VARE_WANTRES is not set,
- * the text is only parsed. */
- VarEvalFlags eflags,
- ApplyModifiersState *st,
- char **out_part
+ /* Mode for evaluating nested variables. */
+ VarEvalMode emode,
+ ModChain *ch,
+ LazyBuf *part
)
{
- return ParseModifierPartSubst(pp, delim, eflags, st, out_part,
- NULL, NULL, NULL);
+ return ParseModifierPartSubst(pp, delim, emode, ch, part, NULL, NULL);
+}
+
+MAKE_INLINE bool
+IsDelimiter(char c, const ModChain *ch)
+{
+ return c == ':' || c == ch->endc;
}
/* Test whether mod starts with modname, followed by a delimiter. */
-MAKE_INLINE Boolean
-ModMatch(const char *mod, const char *modname, char endc)
+MAKE_INLINE bool
+ModMatch(const char *mod, const char *modname, const ModChain *ch)
{
size_t n = strlen(modname);
- return strncmp(mod, modname, n) == 0 &&
- (mod[n] == endc || mod[n] == ':');
+ return strncmp(mod, modname, n) == 0 && IsDelimiter(mod[n], ch);
}
/* Test whether mod starts with modname, followed by a delimiter or '='. */
-MAKE_INLINE Boolean
-ModMatchEq(const char *mod, const char *modname, char endc)
+MAKE_INLINE bool
+ModMatchEq(const char *mod, const char *modname, const ModChain *ch)
{
size_t n = strlen(modname);
return strncmp(mod, modname, n) == 0 &&
- (mod[n] == endc || mod[n] == ':' || mod[n] == '=');
+ (IsDelimiter(mod[n], ch) || mod[n] == '=');
}
-static Boolean
+static bool
TryParseIntBase0(const char **pp, int *out_num)
{
char *end;
@@ -2309,127 +2323,180 @@ TryParseIntBase0(const char **pp, int *out_num)
errno = 0;
n = strtol(*pp, &end, 0);
+
+ if (end == *pp)
+ return false;
if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE)
- return FALSE;
+ return false;
if (n < INT_MIN || n > INT_MAX)
- return FALSE;
+ return false;
*pp = end;
*out_num = (int)n;
- return TRUE;
+ return true;
}
-static Boolean
+static bool
TryParseSize(const char **pp, size_t *out_num)
{
char *end;
unsigned long n;
if (!ch_isdigit(**pp))
- return FALSE;
+ return false;
errno = 0;
n = strtoul(*pp, &end, 10);
if (n == ULONG_MAX && errno == ERANGE)
- return FALSE;
+ return false;
if (n > SIZE_MAX)
- return FALSE;
+ return false;
*pp = end;
*out_num = (size_t)n;
- return TRUE;
+ return true;
}
-static Boolean
+static bool
TryParseChar(const char **pp, int base, char *out_ch)
{
char *end;
unsigned long n;
if (!ch_isalnum(**pp))
- return FALSE;
+ return false;
errno = 0;
n = strtoul(*pp, &end, base);
if (n == ULONG_MAX && errno == ERANGE)
- return FALSE;
+ return false;
if (n > UCHAR_MAX)
- return FALSE;
+ return false;
*pp = end;
*out_ch = (char)n;
- return TRUE;
+ return true;
+}
+
+/*
+ * Modify each word of the expression using the given function and place the
+ * result back in the expression.
+ */
+static void
+ModifyWords(ModChain *ch,
+ ModifyWordProc modifyWord, void *modifyWord_args,
+ bool oneBigWord)
+{
+ Expr *expr = ch->expr;
+ const char *val = expr->value.str;
+ SepBuf result;
+ SubstringWords words;
+ size_t i;
+ Substring word;
+
+ if (oneBigWord) {
+ SepBuf_Init(&result, ch->sep);
+ /* XXX: performance: Substring_InitStr calls strlen */
+ word = Substring_InitStr(val);
+ modifyWord(word, &result, modifyWord_args);
+ goto done;
+ }
+
+ words = Substring_Words(val, false);
+
+ DEBUG2(VAR, "ModifyWords: split \"%s\" into %u words\n",
+ val, (unsigned)words.len);
+
+ SepBuf_Init(&result, ch->sep);
+ for (i = 0; i < words.len; i++) {
+ modifyWord(words.words[i], &result, modifyWord_args);
+ if (result.buf.len > 0)
+ SepBuf_Sep(&result);
+ }
+
+ SubstringWords_Free(words);
+
+done:
+ Expr_SetValueOwn(expr, SepBuf_DoneData(&result));
}
/* :@var@...${var}...@ */
static ApplyModifierResult
-ApplyModifier_Loop(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Loop(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
struct ModifyWord_LoopArgs args;
char prev_sep;
VarParseResult res;
+ LazyBuf tvarBuf, strBuf;
+ FStr tvar, str;
- args.scope = st->scope;
+ args.scope = expr->scope;
(*pp)++; /* Skip the first '@' */
- res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.tvar);
+ res = ParseModifierPart(pp, '@', VARE_PARSE_ONLY, ch, &tvarBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
- if (opts.strict && strchr(args.tvar, '$') != NULL) {
+ tvar = LazyBuf_DoneGet(&tvarBuf);
+ args.var = tvar.str;
+ if (strchr(args.var, '$') != NULL) {
Parse_Error(PARSE_FATAL,
"In the :@ modifier of \"%s\", the variable name \"%s\" "
"must not contain a dollar.",
- st->var->name.str, args.tvar);
+ expr->name, args.var);
return AMR_CLEANUP;
}
- res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.str);
+ res = ParseModifierPart(pp, '@', VARE_PARSE_ONLY, ch, &strBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
+ str = LazyBuf_DoneGet(&strBuf);
+ args.body = str.str;
- args.eflags = st->eflags & ~(unsigned)VARE_KEEP_DOLLAR;
- prev_sep = st->sep;
- st->sep = ' '; /* XXX: should be st->sep for consistency */
- st->newVal = FStr_InitOwn(
- ModifyWords(val, ModifyWord_Loop, &args, st->oneBigWord, st->sep));
- st->sep = prev_sep;
- /* XXX: Consider restoring the previous variable instead of deleting. */
- /*
- * XXX: The variable name should not be expanded here, see
- * ModifyWord_Loop.
- */
- Var_DeleteExpand(st->scope, args.tvar);
- free(args.tvar);
- free(args.str);
+ if (!Expr_ShouldEval(expr))
+ goto done;
+
+ args.emode = VarEvalMode_WithoutKeepDollar(expr->emode);
+ prev_sep = ch->sep;
+ ch->sep = ' '; /* XXX: should be ch->sep for consistency */
+ ModifyWords(ch, ModifyWord_Loop, &args, ch->oneBigWord);
+ ch->sep = prev_sep;
+ /* XXX: Consider restoring the previous value instead of deleting. */
+ Var_Delete(expr->scope, args.var);
+
+done:
+ FStr_Done(&tvar);
+ FStr_Done(&str);
return AMR_OK;
}
/* :Ddefined or :Uundefined */
static ApplyModifierResult
-ApplyModifier_Defined(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Defined(const char **pp, ModChain *ch)
{
- Buffer buf;
+ Expr *expr = ch->expr;
+ LazyBuf buf;
const char *p;
- VarEvalFlags eflags = VARE_NONE;
- if (st->eflags & VARE_WANTRES)
- if ((**pp == 'D') == (st->exprStatus == VES_NONE))
- eflags = st->eflags;
+ VarEvalMode emode = VARE_PARSE_ONLY;
+ if (Expr_ShouldEval(expr))
+ if ((**pp == 'D') == (expr->defined == DEF_REGULAR))
+ emode = expr->emode;
- Buf_Init(&buf);
p = *pp + 1;
- while (*p != st->endc && *p != ':' && *p != '\0') {
+ LazyBuf_Init(&buf, p);
+ while (!IsDelimiter(*p, ch) && *p != '\0') {
/* XXX: This code is similar to the one in Var_Parse.
* See if the code can be merged.
- * See also ApplyModifier_Match. */
+ * See also ApplyModifier_Match and ParseModifierPart. */
/* Escaped delimiter or other special character */
+ /* See Buf_AddEscaped in for.c. */
if (*p == '\\') {
char c = p[1];
- if (c == st->endc || c == ':' || c == '$' ||
- c == '\\') {
- Buf_AddByte(&buf, c);
+ if (IsDelimiter(c, ch) || c == '$' || c == '\\') {
+ LazyBuf_Add(&buf, c);
p += 2;
continue;
}
@@ -2439,173 +2506,197 @@ ApplyModifier_Defined(const char **pp, const char *val, ApplyModifiersState *st)
if (*p == '$') {
FStr nested_val;
- (void)Var_Parse(&p, st->scope, eflags, &nested_val);
+ (void)Var_Parse(&p, expr->scope, emode, &nested_val);
/* TODO: handle errors */
- Buf_AddStr(&buf, nested_val.str);
+ if (Expr_ShouldEval(expr))
+ LazyBuf_AddStr(&buf, nested_val.str);
FStr_Done(&nested_val);
continue;
}
/* Ordinary text */
- Buf_AddByte(&buf, *p);
+ LazyBuf_Add(&buf, *p);
p++;
}
*pp = p;
- ApplyModifiersState_Define(st);
+ Expr_Define(expr);
+
+ if (VarEvalMode_ShouldEval(emode))
+ Expr_SetValue(expr, Substring_Str(LazyBuf_Get(&buf)));
+ else
+ LazyBuf_Done(&buf);
- if (eflags & VARE_WANTRES) {
- st->newVal = FStr_InitOwn(Buf_DoneData(&buf));
- } else {
- st->newVal = FStr_InitRefer(val);
- Buf_Done(&buf);
- }
return AMR_OK;
}
/* :L */
static ApplyModifierResult
-ApplyModifier_Literal(const char **pp, ApplyModifiersState *st)
+ApplyModifier_Literal(const char **pp, ModChain *ch)
{
- ApplyModifiersState_Define(st);
- st->newVal = FStr_InitOwn(bmake_strdup(st->var->name.str));
+ Expr *expr = ch->expr;
+
(*pp)++;
+
+ if (Expr_ShouldEval(expr)) {
+ Expr_Define(expr);
+ Expr_SetValueOwn(expr, bmake_strdup(expr->name));
+ }
+
return AMR_OK;
}
-static Boolean
+static bool
TryParseTime(const char **pp, time_t *out_time)
{
char *end;
unsigned long n;
if (!ch_isdigit(**pp))
- return FALSE;
+ return false;
errno = 0;
n = strtoul(*pp, &end, 10);
if (n == ULONG_MAX && errno == ERANGE)
- return FALSE;
+ return false;
*pp = end;
*out_time = (time_t)n; /* ignore possible truncation for now */
- return TRUE;
+ return true;
}
/* :gmtime */
static ApplyModifierResult
-ApplyModifier_Gmtime(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Gmtime(const char **pp, ModChain *ch)
{
time_t utc;
const char *mod = *pp;
- if (!ModMatchEq(mod, "gmtime", st->endc))
+ if (!ModMatchEq(mod, "gmtime", ch))
return AMR_UNKNOWN;
if (mod[6] == '=') {
- const char *arg = mod + 7;
- if (!TryParseTime(&arg, &utc)) {
+ const char *p = mod + 7;
+ if (!TryParseTime(&p, &utc)) {
Parse_Error(PARSE_FATAL,
"Invalid time value: %s", mod + 7);
return AMR_CLEANUP;
}
- *pp = arg;
+ *pp = p;
} else {
utc = 0;
*pp = mod + 6;
}
- st->newVal = FStr_InitOwn(VarStrftime(val, TRUE, utc));
+
+ if (ModChain_ShouldEval(ch))
+ Expr_SetValueOwn(ch->expr,
+ VarStrftime(ch->expr->value.str, true, utc));
+
return AMR_OK;
}
/* :localtime */
static ApplyModifierResult
-ApplyModifier_Localtime(const char **pp, const char *val,
- ApplyModifiersState *st)
+ApplyModifier_Localtime(const char **pp, ModChain *ch)
{
time_t utc;
const char *mod = *pp;
- if (!ModMatchEq(mod, "localtime", st->endc))
+ if (!ModMatchEq(mod, "localtime", ch))
return AMR_UNKNOWN;
if (mod[9] == '=') {
- const char *arg = mod + 10;
- if (!TryParseTime(&arg, &utc)) {
+ const char *p = mod + 10;
+ if (!TryParseTime(&p, &utc)) {
Parse_Error(PARSE_FATAL,
"Invalid time value: %s", mod + 10);
return AMR_CLEANUP;
}
- *pp = arg;
+ *pp = p;
} else {
utc = 0;
*pp = mod + 9;
}
- st->newVal = FStr_InitOwn(VarStrftime(val, FALSE, utc));
+
+ if (ModChain_ShouldEval(ch))
+ Expr_SetValueOwn(ch->expr,
+ VarStrftime(ch->expr->value.str, false, utc));
+
return AMR_OK;
}
/* :hash */
static ApplyModifierResult
-ApplyModifier_Hash(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Hash(const char **pp, ModChain *ch)
{
- if (!ModMatch(*pp, "hash", st->endc))
+ if (!ModMatch(*pp, "hash", ch))
return AMR_UNKNOWN;
-
- st->newVal = FStr_InitOwn(VarHash(val));
*pp += 4;
+
+ if (ModChain_ShouldEval(ch))
+ Expr_SetValueOwn(ch->expr, VarHash(ch->expr->value.str));
+
return AMR_OK;
}
/* :P */
static ApplyModifierResult
-ApplyModifier_Path(const char **pp, ApplyModifiersState *st)
+ApplyModifier_Path(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
GNode *gn;
char *path;
- ApplyModifiersState_Define(st);
+ (*pp)++;
+
+ if (!ModChain_ShouldEval(ch))
+ return AMR_OK;
+
+ Expr_Define(expr);
- gn = Targ_FindNode(st->var->name.str);
+ gn = Targ_FindNode(expr->name);
if (gn == NULL || gn->type & OP_NOPATH) {
path = NULL;
} else if (gn->path != NULL) {
path = bmake_strdup(gn->path);
} else {
SearchPath *searchPath = Suff_FindPath(gn);
- path = Dir_FindFile(st->var->name.str, searchPath);
+ path = Dir_FindFile(expr->name, searchPath);
}
if (path == NULL)
- path = bmake_strdup(st->var->name.str);
- st->newVal = FStr_InitOwn(path);
+ path = bmake_strdup(expr->name);
+ Expr_SetValueOwn(expr, path);
- (*pp)++;
return AMR_OK;
}
/* :!cmd! */
static ApplyModifierResult
-ApplyModifier_ShellCommand(const char **pp, ApplyModifiersState *st)
+ApplyModifier_ShellCommand(const char **pp, ModChain *ch)
{
- char *cmd;
+ Expr *expr = ch->expr;
const char *errfmt;
VarParseResult res;
+ LazyBuf cmdBuf;
+ FStr cmd;
(*pp)++;
- res = ParseModifierPart(pp, '!', st->eflags, st, &cmd);
+ res = ParseModifierPart(pp, '!', expr->emode, ch, &cmdBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
+ cmd = LazyBuf_DoneGet(&cmdBuf);
+
errfmt = NULL;
- if (st->eflags & VARE_WANTRES)
- st->newVal = FStr_InitOwn(Cmd_Exec(cmd, &errfmt));
+ if (Expr_ShouldEval(expr))
+ Expr_SetValueOwn(expr, Cmd_Exec(cmd.str, &errfmt));
else
- st->newVal = FStr_InitRefer("");
+ Expr_SetValueRefer(expr, "");
if (errfmt != NULL)
- Error(errfmt, cmd); /* XXX: why still return AMR_OK? */
- free(cmd);
+ Error(errfmt, cmd.str); /* XXX: why still return AMR_OK? */
+ FStr_Done(&cmd);
+ Expr_Define(expr);
- ApplyModifiersState_Define(st);
return AMR_OK;
}
@@ -2614,21 +2705,22 @@ ApplyModifier_ShellCommand(const char **pp, ApplyModifiersState *st)
* The :range=7 modifier generates an integer sequence from 1 to 7.
*/
static ApplyModifierResult
-ApplyModifier_Range(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Range(const char **pp, ModChain *ch)
{
size_t n;
Buffer buf;
size_t i;
const char *mod = *pp;
- if (!ModMatchEq(mod, "range", st->endc))
+ if (!ModMatchEq(mod, "range", ch))
return AMR_UNKNOWN;
if (mod[5] == '=') {
const char *p = mod + 6;
if (!TryParseSize(&p, &n)) {
Parse_Error(PARSE_FATAL,
- "Invalid number: %s", mod + 6);
+ "Invalid number \"%s\" for ':range' modifier",
+ mod + 6);
return AMR_CLEANUP;
}
*pp = p;
@@ -2637,8 +2729,11 @@ ApplyModifier_Range(const char **pp, const char *val, ApplyModifiersState *st)
*pp = mod + 5;
}
+ if (!ModChain_ShouldEval(ch))
+ return AMR_OK;
+
if (n == 0) {
- Words words = Str_Words(val, FALSE);
+ Words words = Str_Words(ch->expr->value.str, false);
n = words.len;
Words_Free(words);
}
@@ -2647,47 +2742,50 @@ ApplyModifier_Range(const char **pp, const char *val, ApplyModifiersState *st)
for (i = 0; i < n; i++) {
if (i != 0) {
- /* XXX: Use st->sep instead of ' ', for consistency. */
+ /* XXX: Use ch->sep instead of ' ', for consistency. */
Buf_AddByte(&buf, ' ');
}
Buf_AddInt(&buf, 1 + (int)i);
}
- st->newVal = FStr_InitOwn(Buf_DoneData(&buf));
+ Expr_SetValueOwn(ch->expr, Buf_DoneData(&buf));
return AMR_OK;
}
-/* :Mpattern or :Npattern */
-static ApplyModifierResult
-ApplyModifier_Match(const char **pp, const char *val, ApplyModifiersState *st)
+/* Parse a ':M' or ':N' modifier. */
+static void
+ParseModifier_Match(const char **pp, const ModChain *ch,
+ char **out_pattern)
{
const char *mod = *pp;
- Boolean copy = FALSE; /* pattern should be, or has been, copied */
- Boolean needSubst = FALSE;
+ Expr *expr = ch->expr;
+ bool copy = false; /* pattern should be, or has been, copied */
+ bool needSubst = false;
const char *endpat;
char *pattern;
- ModifyWordsCallback callback;
/*
* In the loop below, ignore ':' unless we are at (or back to) the
* original brace level.
* XXX: This will likely not work right if $() and ${} are intermixed.
*/
- /* XXX: This code is similar to the one in Var_Parse.
+ /*
+ * XXX: This code is similar to the one in Var_Parse.
* See if the code can be merged.
- * See also ApplyModifier_Defined. */
+ * See also ApplyModifier_Defined.
+ */
int nest = 0;
const char *p;
for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) {
if (*p == '\\' &&
- (p[1] == ':' || p[1] == st->endc || p[1] == st->startc)) {
+ (IsDelimiter(p[1], ch) || p[1] == ch->startc)) {
if (!needSubst)
- copy = TRUE;
+ copy = true;
p++;
continue;
}
if (*p == '$')
- needSubst = TRUE;
+ needSubst = true;
if (*p == '(' || *p == '{')
nest++;
if (*p == ')' || *p == '}') {
@@ -2709,8 +2807,8 @@ ApplyModifier_Match(const char **pp, const char *val, ApplyModifiersState *st)
src = mod + 1;
for (; src < endpat; src++, dst++) {
if (src[0] == '\\' && src + 1 < endpat &&
- /* XXX: st->startc is missing here; see above */
- (src[1] == ':' || src[1] == st->endc))
+ /* XXX: ch->startc is missing here; see above */
+ IsDelimiter(src[1], ch))
src++;
*dst = *src;
}
@@ -2721,84 +2819,104 @@ ApplyModifier_Match(const char **pp, const char *val, ApplyModifiersState *st)
if (needSubst) {
char *old_pattern = pattern;
- (void)Var_Subst(pattern, st->scope, st->eflags, &pattern);
+ (void)Var_Subst(pattern, expr->scope, expr->emode, &pattern);
/* TODO: handle errors */
free(old_pattern);
}
- DEBUG3(VAR, "Pattern[%s] for [%s] is [%s]\n",
- st->var->name.str, val, pattern);
+ DEBUG2(VAR, "Pattern for ':%c' is \"%s\"\n", mod[0], pattern);
+
+ *out_pattern = pattern;
+}
+
+/* :Mpattern or :Npattern */
+static ApplyModifierResult
+ApplyModifier_Match(const char **pp, ModChain *ch)
+{
+ const char mod = **pp;
+ char *pattern;
+
+ ParseModifier_Match(pp, ch, &pattern);
+
+ if (ModChain_ShouldEval(ch)) {
+ ModifyWordProc modifyWord =
+ mod == 'M' ? ModifyWord_Match : ModifyWord_NoMatch;
+ ModifyWords(ch, modifyWord, pattern, ch->oneBigWord);
+ }
- callback = mod[0] == 'M' ? ModifyWord_Match : ModifyWord_NoMatch;
- st->newVal = FStr_InitOwn(ModifyWords(val, callback, pattern,
- st->oneBigWord, st->sep));
free(pattern);
return AMR_OK;
}
+static void
+ParsePatternFlags(const char **pp, PatternFlags *pflags, bool *oneBigWord)
+{
+ for (;; (*pp)++) {
+ if (**pp == 'g')
+ pflags->subGlobal = true;
+ else if (**pp == '1')
+ pflags->subOnce = true;
+ else if (**pp == 'W')
+ *oneBigWord = true;
+ else
+ break;
+ }
+}
+
+MAKE_INLINE PatternFlags
+PatternFlags_None(void)
+{
+ PatternFlags pflags = { false, false, false, false };
+ return pflags;
+}
+
/* :S,from,to, */
static ApplyModifierResult
-ApplyModifier_Subst(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Subst(const char **pp, ModChain *ch)
{
struct ModifyWord_SubstArgs args;
- char *lhs, *rhs;
- Boolean oneBigWord;
+ bool oneBigWord;
VarParseResult res;
+ LazyBuf lhsBuf, rhsBuf;
char delim = (*pp)[1];
if (delim == '\0') {
- Error("Missing delimiter for :S modifier");
+ Error("Missing delimiter for modifier ':S'");
(*pp)++;
return AMR_CLEANUP;
}
*pp += 2;
- args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE };
- args.matched = FALSE;
+ args.pflags = PatternFlags_None();
+ args.matched = false;
- /*
- * If pattern begins with '^', it is anchored to the
- * start of the word -- skip over it and flag pattern.
- */
if (**pp == '^') {
- args.pflags.anchorStart = TRUE;
+ args.pflags.anchorStart = true;
(*pp)++;
}
- res = ParseModifierPartSubst(pp, delim, st->eflags, st, &lhs,
- &args.lhsLen, &args.pflags, NULL);
+ res = ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &lhsBuf,
+ &args.pflags, NULL);
if (res != VPR_OK)
return AMR_CLEANUP;
- args.lhs = lhs;
+ args.lhs = LazyBuf_Get(&lhsBuf);
- res = ParseModifierPartSubst(pp, delim, st->eflags, st, &rhs,
- &args.rhsLen, NULL, &args);
- if (res != VPR_OK)
+ res = ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &rhsBuf,
+ NULL, &args);
+ if (res != VPR_OK) {
+ LazyBuf_Done(&lhsBuf);
return AMR_CLEANUP;
- args.rhs = rhs;
-
- oneBigWord = st->oneBigWord;
- for (;; (*pp)++) {
- switch (**pp) {
- case 'g':
- args.pflags.subGlobal = TRUE;
- continue;
- case '1':
- args.pflags.subOnce = TRUE;
- continue;
- case 'W':
- oneBigWord = TRUE;
- continue;
- }
- break;
}
+ args.rhs = LazyBuf_Get(&rhsBuf);
+
+ oneBigWord = ch->oneBigWord;
+ ParsePatternFlags(pp, &args.pflags, &oneBigWord);
- st->newVal = FStr_InitOwn(ModifyWords(val, ModifyWord_Subst, &args,
- oneBigWord, st->sep));
+ ModifyWords(ch, ModifyWord_Subst, &args, oneBigWord);
- free(lhs);
- free(rhs);
+ LazyBuf_Done(&lhsBuf);
+ LazyBuf_Done(&rhsBuf);
return AMR_OK;
}
@@ -2806,13 +2924,14 @@ ApplyModifier_Subst(const char **pp, const char *val, ApplyModifiersState *st)
/* :C,from,to, */
static ApplyModifierResult
-ApplyModifier_Regex(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Regex(const char **pp, ModChain *ch)
{
- char *re;
struct ModifyWord_SubstRegexArgs args;
- Boolean oneBigWord;
+ bool oneBigWord;
int error;
VarParseResult res;
+ LazyBuf reBuf, replaceBuf;
+ FStr re, replace;
char delim = (*pp)[1];
if (delim == '\0') {
@@ -2823,50 +2942,47 @@ ApplyModifier_Regex(const char **pp, const char *val, ApplyModifiersState *st)
*pp += 2;
- res = ParseModifierPart(pp, delim, st->eflags, st, &re);
+ res = ParseModifierPart(pp, delim, ch->expr->emode, ch, &reBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
+ re = LazyBuf_DoneGet(&reBuf);
- res = ParseModifierPart(pp, delim, st->eflags, st, &args.replace);
- if (args.replace == NULL) {
- free(re);
+ res = ParseModifierPart(pp, delim, ch->expr->emode, ch, &replaceBuf);
+ if (res != VPR_OK) {
+ FStr_Done(&re);
return AMR_CLEANUP;
}
+ replace = LazyBuf_DoneGet(&replaceBuf);
+ args.replace = replace.str;
- args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE };
- args.matched = FALSE;
- oneBigWord = st->oneBigWord;
- for (;; (*pp)++) {
- switch (**pp) {
- case 'g':
- args.pflags.subGlobal = TRUE;
- continue;
- case '1':
- args.pflags.subOnce = TRUE;
- continue;
- case 'W':
- oneBigWord = TRUE;
- continue;
- }
- break;
+ args.pflags = PatternFlags_None();
+ args.matched = false;
+ oneBigWord = ch->oneBigWord;
+ ParsePatternFlags(pp, &args.pflags, &oneBigWord);
+
+ if (!ModChain_ShouldEval(ch)) {
+ FStr_Done(&replace);
+ FStr_Done(&re);
+ return AMR_OK;
}
- error = regcomp(&args.re, re, REG_EXTENDED);
- free(re);
+ error = regcomp(&args.re, re.str, REG_EXTENDED);
if (error != 0) {
VarREError(error, &args.re, "Regex compilation error");
- free(args.replace);
+ FStr_Done(&replace);
+ FStr_Done(&re);
return AMR_CLEANUP;
}
args.nsub = args.re.re_nsub + 1;
if (args.nsub > 10)
args.nsub = 10;
- st->newVal = FStr_InitOwn(
- ModifyWords(val, ModifyWord_SubstRegex, &args,
- oneBigWord, st->sep));
+
+ ModifyWords(ch, ModifyWord_SubstRegex, &args, oneBigWord);
+
regfree(&args.re);
- free(args.replace);
+ FStr_Done(&replace);
+ FStr_Done(&re);
return AMR_OK;
}
@@ -2874,40 +2990,59 @@ ApplyModifier_Regex(const char **pp, const char *val, ApplyModifiersState *st)
/* :Q, :q */
static ApplyModifierResult
-ApplyModifier_Quote(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Quote(const char **pp, ModChain *ch)
{
- if ((*pp)[1] == st->endc || (*pp)[1] == ':') {
- st->newVal = FStr_InitOwn(VarQuote(val, **pp == 'q'));
- (*pp)++;
- return AMR_OK;
- } else
+ LazyBuf buf;
+ bool quoteDollar;
+
+ quoteDollar = **pp == 'q';
+ if (!IsDelimiter((*pp)[1], ch))
return AMR_UNKNOWN;
+ (*pp)++;
+
+ if (!ModChain_ShouldEval(ch))
+ return AMR_OK;
+
+ VarQuote(ch->expr->value.str, quoteDollar, &buf);
+ if (buf.data != NULL)
+ Expr_SetValue(ch->expr, LazyBuf_DoneGet(&buf));
+ else
+ LazyBuf_Done(&buf);
+
+ return AMR_OK;
}
/*ARGSUSED*/
static void
-ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
+ModifyWord_Copy(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
{
- SepBuf_AddStr(buf, word);
+ SepBuf_AddSubstring(buf, word);
}
/* :ts<separator> */
static ApplyModifierResult
-ApplyModifier_ToSep(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_ToSep(const char **pp, ModChain *ch)
{
const char *sep = *pp + 2;
+ /*
+ * Even in parse-only mode, proceed as normal since there is
+ * neither any observable side effect nor a performance penalty.
+ * Checking for wantRes for every single piece of code in here
+ * would make the code in this function too hard to read.
+ */
+
/* ":ts<any><endc>" or ":ts<any>:" */
- if (sep[0] != st->endc && (sep[1] == st->endc || sep[1] == ':')) {
- st->sep = sep[0];
+ if (sep[0] != ch->endc && IsDelimiter(sep[1], ch)) {
*pp = sep + 1;
+ ch->sep = sep[0];
goto ok;
}
/* ":ts<endc>" or ":ts:" */
- if (sep[0] == st->endc || sep[0] == ':') {
- st->sep = '\0'; /* no separator */
+ if (IsDelimiter(sep[0], ch)) {
*pp = sep;
+ ch->sep = '\0'; /* no separator */
goto ok;
}
@@ -2919,15 +3054,15 @@ ApplyModifier_ToSep(const char **pp, const char *val, ApplyModifiersState *st)
/* ":ts\n" */
if (sep[1] == 'n') {
- st->sep = '\n';
*pp = sep + 2;
+ ch->sep = '\n';
goto ok;
}
/* ":ts\t" */
if (sep[1] == 't') {
- st->sep = '\t';
*pp = sep + 2;
+ ch->sep = '\t';
goto ok;
}
@@ -2944,12 +3079,12 @@ ApplyModifier_ToSep(const char **pp, const char *val, ApplyModifiersState *st)
return AMR_BAD; /* ":ts<backslash><unrecognised>". */
}
- if (!TryParseChar(&p, base, &st->sep)) {
+ if (!TryParseChar(&p, base, &ch->sep)) {
Parse_Error(PARSE_FATAL,
"Invalid character number: %s", p);
return AMR_CLEANUP;
}
- if (*p != ':' && *p != st->endc) {
+ if (!IsDelimiter(*p, ch)) {
(*pp)++; /* just for backwards compatibility */
return AMR_BAD;
}
@@ -2958,8 +3093,7 @@ ApplyModifier_ToSep(const char **pp, const char *val, ApplyModifiersState *st)
}
ok:
- st->newVal = FStr_InitOwn(
- ModifyWords(val, ModifyWord_Copy, NULL, st->oneBigWord, st->sep));
+ ModifyWords(ch, ModifyWord_Copy, NULL, ch->oneBigWord);
return AMR_OK;
}
@@ -2993,107 +3127,109 @@ str_tolower(const char *str)
/* :tA, :tu, :tl, :ts<separator>, etc. */
static ApplyModifierResult
-ApplyModifier_To(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_To(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
const char *mod = *pp;
assert(mod[0] == 't');
- if (mod[1] == st->endc || mod[1] == ':' || mod[1] == '\0') {
+ if (IsDelimiter(mod[1], ch) || mod[1] == '\0') {
*pp = mod + 1;
return AMR_BAD; /* Found ":t<endc>" or ":t:". */
}
if (mod[1] == 's')
- return ApplyModifier_ToSep(pp, val, st);
+ return ApplyModifier_ToSep(pp, ch);
- if (mod[2] != st->endc && mod[2] != ':') {
+ if (!IsDelimiter(mod[2], ch)) { /* :t<unrecognized> */
*pp = mod + 1;
- return AMR_BAD; /* Found ":t<unrecognised><unrecognised>". */
+ return AMR_BAD;
}
- /* Check for two-character options: ":tu", ":tl" */
- if (mod[1] == 'A') { /* absolute path */
- st->newVal = FStr_InitOwn(
- ModifyWords(val, ModifyWord_Realpath, NULL,
- st->oneBigWord, st->sep));
+ if (mod[1] == 'A') { /* :tA */
*pp = mod + 2;
+ ModifyWords(ch, ModifyWord_Realpath, NULL, ch->oneBigWord);
return AMR_OK;
}
- if (mod[1] == 'u') { /* :tu */
- st->newVal = FStr_InitOwn(str_toupper(val));
+ if (mod[1] == 'u') { /* :tu */
*pp = mod + 2;
+ if (ModChain_ShouldEval(ch))
+ Expr_SetValueOwn(expr, str_toupper(expr->value.str));
return AMR_OK;
}
- if (mod[1] == 'l') { /* :tl */
- st->newVal = FStr_InitOwn(str_tolower(val));
+ if (mod[1] == 'l') { /* :tl */
*pp = mod + 2;
+ if (ModChain_ShouldEval(ch))
+ Expr_SetValueOwn(expr, str_tolower(expr->value.str));
return AMR_OK;
}
- if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */
- st->oneBigWord = mod[1] == 'W';
- st->newVal = FStr_InitRefer(val);
+ if (mod[1] == 'W' || mod[1] == 'w') { /* :tW, :tw */
*pp = mod + 2;
+ ch->oneBigWord = mod[1] == 'W';
return AMR_OK;
}
/* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */
- *pp = mod + 1;
+ *pp = mod + 1; /* XXX: unnecessary but observable */
return AMR_BAD;
}
/* :[#], :[1], :[-1..1], etc. */
static ApplyModifierResult
-ApplyModifier_Words(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Words(const char **pp, ModChain *ch)
{
- char *estr;
+ Expr *expr = ch->expr;
+ const char *estr;
int first, last;
VarParseResult res;
const char *p;
+ LazyBuf estrBuf;
+ FStr festr;
(*pp)++; /* skip the '[' */
- res = ParseModifierPart(pp, ']', st->eflags, st, &estr);
+ res = ParseModifierPart(pp, ']', expr->emode, ch, &estrBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
+ festr = LazyBuf_DoneGet(&estrBuf);
+ estr = festr.str;
- /* now *pp points just after the closing ']' */
- if (**pp != ':' && **pp != st->endc)
- goto bad_modifier; /* Found junk after ']' */
+ if (!IsDelimiter(**pp, ch))
+ goto bad_modifier; /* Found junk after ']' */
+
+ if (!ModChain_ShouldEval(ch))
+ goto ok;
if (estr[0] == '\0')
- goto bad_modifier; /* empty square brackets in ":[]". */
+ goto bad_modifier; /* Found ":[]". */
- if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */
- if (st->oneBigWord) {
- st->newVal = FStr_InitRefer("1");
+ if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */
+ if (ch->oneBigWord) {
+ Expr_SetValueRefer(expr, "1");
} else {
Buffer buf;
- Words words = Str_Words(val, FALSE);
+ Words words = Str_Words(expr->value.str, false);
size_t ac = words.len;
Words_Free(words);
/* 3 digits + '\0' is usually enough */
Buf_InitSize(&buf, 4);
Buf_AddInt(&buf, (int)ac);
- st->newVal = FStr_InitOwn(Buf_DoneData(&buf));
+ Expr_SetValueOwn(expr, Buf_DoneData(&buf));
}
goto ok;
}
- if (estr[0] == '*' && estr[1] == '\0') {
- /* Found ":[*]" */
- st->oneBigWord = TRUE;
- st->newVal = FStr_InitRefer(val);
+ if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */
+ ch->oneBigWord = true;
goto ok;
}
- if (estr[0] == '@' && estr[1] == '\0') {
- /* Found ":[@]" */
- st->oneBigWord = FALSE;
- st->newVal = FStr_InitRefer(val);
+ if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */
+ ch->oneBigWord = false;
goto ok;
}
@@ -3121,8 +3257,7 @@ ApplyModifier_Words(const char **pp, const char *val, ApplyModifiersState *st)
*/
if (first == 0 && last == 0) {
/* ":[0]" or perhaps ":[0..0]" */
- st->oneBigWord = TRUE;
- st->newVal = FStr_InitRefer(val);
+ ch->oneBigWord = true;
goto ok;
}
@@ -3131,15 +3266,16 @@ ApplyModifier_Words(const char **pp, const char *val, ApplyModifiersState *st)
goto bad_modifier;
/* Normal case: select the words described by first and last. */
- st->newVal = FStr_InitOwn(
- VarSelectWords(st->sep, st->oneBigWord, val, first, last));
+ Expr_SetValueOwn(expr,
+ VarSelectWords(expr->value.str, first, last,
+ ch->sep, ch->oneBigWord));
ok:
- free(estr);
+ FStr_Done(&festr);
return AMR_OK;
bad_modifier:
- free(estr);
+ FStr_Done(&festr);
return AMR_BAD;
}
@@ -3170,94 +3306,107 @@ ShuffleStrings(char **strs, size_t n)
/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */
static ApplyModifierResult
-ApplyModifier_Order(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Order(const char **pp, ModChain *ch)
{
const char *mod = (*pp)++; /* skip past the 'O' in any case */
+ Words words;
+ enum SortMode {
+ ASC, DESC, SHUFFLE
+ } mode;
- Words words = Str_Words(val, FALSE);
-
- if (mod[1] == st->endc || mod[1] == ':') {
- /* :O sorts ascending */
- qsort(words.words, words.len, sizeof words.words[0],
- str_cmp_asc);
-
+ if (IsDelimiter(mod[1], ch)) {
+ mode = ASC;
} else if ((mod[1] == 'r' || mod[1] == 'x') &&
- (mod[2] == st->endc || mod[2] == ':')) {
+ IsDelimiter(mod[2], ch)) {
(*pp)++;
-
- if (mod[1] == 'r') { /* :Or sorts descending */
- qsort(words.words, words.len, sizeof words.words[0],
- str_cmp_desc);
- } else
- ShuffleStrings(words.words, words.len);
- } else {
- Words_Free(words);
+ mode = mod[1] == 'r' ? DESC : SHUFFLE;
+ } else
return AMR_BAD;
- }
- st->newVal = FStr_InitOwn(Words_JoinFree(words));
+ if (!ModChain_ShouldEval(ch))
+ return AMR_OK;
+
+ words = Str_Words(ch->expr->value.str, false);
+ if (mode == SHUFFLE)
+ ShuffleStrings(words.words, words.len);
+ else
+ qsort(words.words, words.len, sizeof words.words[0],
+ mode == ASC ? str_cmp_asc : str_cmp_desc);
+ Expr_SetValueOwn(ch->expr, Words_JoinFree(words));
+
return AMR_OK;
}
/* :? then : else */
static ApplyModifierResult
-ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st)
+ApplyModifier_IfElse(const char **pp, ModChain *ch)
{
- char *then_expr, *else_expr;
+ Expr *expr = ch->expr;
VarParseResult res;
+ LazyBuf buf;
+ FStr then_expr, else_expr;
- Boolean value = FALSE;
- VarEvalFlags then_eflags = VARE_NONE;
- VarEvalFlags else_eflags = VARE_NONE;
+ bool value = false;
+ VarEvalMode then_emode = VARE_PARSE_ONLY;
+ VarEvalMode else_emode = VARE_PARSE_ONLY;
int cond_rc = COND_PARSE; /* anything other than COND_INVALID */
- if (st->eflags & VARE_WANTRES) {
- cond_rc = Cond_EvalCondition(st->var->name.str, &value);
+ if (Expr_ShouldEval(expr)) {
+ cond_rc = Cond_EvalCondition(expr->name, &value);
if (cond_rc != COND_INVALID && value)
- then_eflags = st->eflags;
+ then_emode = expr->emode;
if (cond_rc != COND_INVALID && !value)
- else_eflags = st->eflags;
+ else_emode = expr->emode;
}
(*pp)++; /* skip past the '?' */
- res = ParseModifierPart(pp, ':', then_eflags, st, &then_expr);
+ res = ParseModifierPart(pp, ':', then_emode, ch, &buf);
if (res != VPR_OK)
return AMR_CLEANUP;
+ then_expr = LazyBuf_DoneGet(&buf);
- res = ParseModifierPart(pp, st->endc, else_eflags, st, &else_expr);
- if (res != VPR_OK)
+ res = ParseModifierPart(pp, ch->endc, else_emode, ch, &buf);
+ if (res != VPR_OK) {
+ FStr_Done(&then_expr);
return AMR_CLEANUP;
+ }
+ else_expr = LazyBuf_DoneGet(&buf);
+
+ (*pp)--; /* Go back to the ch->endc. */
- (*pp)--;
if (cond_rc == COND_INVALID) {
- Error("Bad conditional expression `%s' in %s?%s:%s",
- st->var->name.str, st->var->name.str, then_expr, else_expr);
+ Error("Bad conditional expression '%s' in '%s?%s:%s'",
+ expr->name, expr->name, then_expr.str, else_expr.str);
return AMR_CLEANUP;
}
- if (value) {
- st->newVal = FStr_InitOwn(then_expr);
- free(else_expr);
+ if (!ModChain_ShouldEval(ch)) {
+ FStr_Done(&then_expr);
+ FStr_Done(&else_expr);
+ } else if (value) {
+ Expr_SetValue(expr, then_expr);
+ FStr_Done(&else_expr);
} else {
- st->newVal = FStr_InitOwn(else_expr);
- free(then_expr);
+ FStr_Done(&then_expr);
+ Expr_SetValue(expr, else_expr);
}
- ApplyModifiersState_Define(st);
+ Expr_Define(expr);
return AMR_OK;
}
/*
- * The ::= modifiers actually assign a value to the variable.
- * Their main purpose is in supporting modifiers of .for loop
- * iterators and other obscure uses. They always expand to
- * nothing. In a target rule that would otherwise expand to an
- * empty line they can be preceded with @: to keep make happy.
- * Eg.
+ * The ::= modifiers are special in that they do not read the variable value
+ * but instead assign to that variable. They always expand to an empty
+ * string.
+ *
+ * Their main purpose is in supporting .for loops that generate shell commands
+ * since an ordinary variable assignment at that point would terminate the
+ * dependency group for these targets. For example:
*
- * foo: .USE
+ * list-targets: .USE
* .for i in ${.TARGET} ${.TARGET:R}.gz
- * @: ${t::=$i}
- * @echo blah ${t:T}
+ * @${t::=$i}
+ * @echo 'The target is ${t:T}.'
* .endfor
*
* ::=<str> Assigns <str> as the new value of variable.
@@ -3268,12 +3417,13 @@ ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st)
* variable.
*/
static ApplyModifierResult
-ApplyModifier_Assign(const char **pp, ApplyModifiersState *st)
+ApplyModifier_Assign(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
GNode *scope;
- char delim;
- char *val;
+ FStr val;
VarParseResult res;
+ LazyBuf buf;
const char *mod = *pp;
const char *op = mod + 1;
@@ -3283,22 +3433,13 @@ ApplyModifier_Assign(const char **pp, ApplyModifiersState *st)
if ((op[0] == '!' || op[0] == '+' || op[0] == '?') && op[1] == '=')
goto ok;
return AMR_UNKNOWN; /* "::<unrecognised>" */
-ok:
- if (st->var->name.str[0] == '\0') {
+ok:
+ if (expr->name[0] == '\0') {
*pp = mod + 1;
return AMR_BAD;
}
- scope = st->scope; /* scope where v belongs */
- if (st->exprStatus == VES_NONE && st->scope != SCOPE_GLOBAL) {
- Var *gv = VarFind(st->var->name.str, st->scope, FALSE);
- if (gv == NULL)
- scope = SCOPE_GLOBAL;
- else
- VarFreeEnv(gv, TRUE);
- }
-
switch (op[0]) {
case '+':
case '?':
@@ -3310,41 +3451,51 @@ ok:
break;
}
- delim = st->startc == '(' ? ')' : '}';
- res = ParseModifierPart(pp, delim, st->eflags, st, &val);
+ res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &buf);
if (res != VPR_OK)
return AMR_CLEANUP;
+ val = LazyBuf_DoneGet(&buf);
- (*pp)--;
+ (*pp)--; /* Go back to the ch->endc. */
- /* XXX: Expanding the variable name at this point sounds wrong. */
- if (st->eflags & VARE_WANTRES) {
- switch (op[0]) {
- case '+':
- Var_AppendExpand(scope, st->var->name.str, val);
- break;
- case '!': {
- const char *errfmt;
- char *cmd_output = Cmd_Exec(val, &errfmt);
- if (errfmt != NULL)
- Error(errfmt, val);
- else
- Var_SetExpand(scope,
- st->var->name.str, cmd_output);
- free(cmd_output);
- break;
- }
- case '?':
- if (st->exprStatus == VES_NONE)
- break;
- /* FALLTHROUGH */
- default:
- Var_SetExpand(scope, st->var->name.str, val);
+ if (!Expr_ShouldEval(expr))
+ goto done;
+
+ scope = expr->scope; /* scope where v belongs */
+ if (expr->defined == DEF_REGULAR && expr->scope != SCOPE_GLOBAL) {
+ Var *gv = VarFind(expr->name, expr->scope, false);
+ if (gv == NULL)
+ scope = SCOPE_GLOBAL;
+ else
+ VarFreeEnv(gv);
+ }
+
+ switch (op[0]) {
+ case '+':
+ Var_Append(scope, expr->name, val.str);
+ break;
+ case '!': {
+ const char *errfmt;
+ char *cmd_output = Cmd_Exec(val.str, &errfmt);
+ if (errfmt != NULL)
+ Error(errfmt, val.str);
+ else
+ Var_Set(scope, expr->name, cmd_output);
+ free(cmd_output);
+ break;
+ }
+ case '?':
+ if (expr->defined == DEF_REGULAR)
break;
- }
+ /* FALLTHROUGH */
+ default:
+ Var_Set(scope, expr->name, val.str);
+ break;
}
- free(val);
- st->newVal = FStr_InitRefer("");
+ Expr_SetValueRefer(expr, "");
+
+done:
+ FStr_Done(&val);
return AMR_OK;
}
@@ -3353,24 +3504,33 @@ ok:
* remember current value
*/
static ApplyModifierResult
-ApplyModifier_Remember(const char **pp, const char *val,
- ApplyModifiersState *st)
+ApplyModifier_Remember(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
const char *mod = *pp;
- if (!ModMatchEq(mod, "_", st->endc))
+ FStr name;
+
+ if (!ModMatchEq(mod, "_", ch))
return AMR_UNKNOWN;
+ name = FStr_InitRefer("_");
if (mod[1] == '=') {
- size_t n = strcspn(mod + 2, ":)}");
- char *name = bmake_strldup(mod + 2, n);
- Var_SetExpand(st->scope, name, val);
- free(name);
- *pp = mod + 2 + n;
- } else {
- Var_Set(st->scope, "_", val);
+ /*
+ * XXX: This ad-hoc call to strcspn deviates from the usual
+ * behavior defined in ParseModifierPart. This creates an
+ * unnecessary, undocumented inconsistency in make.
+ */
+ const char *arg = mod + 2;
+ size_t argLen = strcspn(arg, ":)}");
+ *pp = arg + argLen;
+ name = FStr_InitOwn(bmake_strldup(arg, argLen));
+ } else
*pp = mod + 1;
- }
- st->newVal = FStr_InitRefer(val);
+
+ if (Expr_ShouldEval(expr))
+ Var_Set(expr->scope, name.str, expr->value.str);
+ FStr_Done(&name);
+
return AMR_OK;
}
@@ -3379,40 +3539,68 @@ ApplyModifier_Remember(const char **pp, const char *val,
* for a single-letter modifier such as :H, :T.
*/
static ApplyModifierResult
-ApplyModifier_WordFunc(const char **pp, const char *val,
- ApplyModifiersState *st, ModifyWordsCallback modifyWord)
+ApplyModifier_WordFunc(const char **pp, ModChain *ch,
+ ModifyWordProc modifyWord)
{
- char delim = (*pp)[1];
- if (delim != st->endc && delim != ':')
+ if (!IsDelimiter((*pp)[1], ch))
return AMR_UNKNOWN;
-
- st->newVal = FStr_InitOwn(ModifyWords(val, modifyWord, NULL,
- st->oneBigWord, st->sep));
(*pp)++;
+
+ if (ModChain_ShouldEval(ch))
+ ModifyWords(ch, modifyWord, NULL, ch->oneBigWord);
+
return AMR_OK;
}
+/* Remove adjacent duplicate words. */
static ApplyModifierResult
-ApplyModifier_Unique(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_Unique(const char **pp, ModChain *ch)
{
- if ((*pp)[1] == st->endc || (*pp)[1] == ':') {
- st->newVal = FStr_InitOwn(VarUniq(val));
- (*pp)++;
- return AMR_OK;
- } else
+ Words words;
+
+ if (!IsDelimiter((*pp)[1], ch))
return AMR_UNKNOWN;
+ (*pp)++;
+
+ if (!ModChain_ShouldEval(ch))
+ return AMR_OK;
+
+ words = Str_Words(ch->expr->value.str, false);
+
+ if (words.len > 1) {
+ size_t si, di;
+
+ di = 0;
+ for (si = 1; si < words.len; si++) {
+ if (strcmp(words.words[si], words.words[di]) != 0) {
+ di++;
+ if (di != si)
+ words.words[di] = words.words[si];
+ }
+ }
+ words.len = di + 1;
+ }
+
+ Expr_SetValueOwn(ch->expr, Words_JoinFree(words));
+
+ return AMR_OK;
}
#ifdef SYSVVARSUB
/* :from=to */
static ApplyModifierResult
-ApplyModifier_SysV(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier_SysV(const char **pp, ModChain *ch)
{
- char *lhs, *rhs;
+ Expr *expr = ch->expr;
VarParseResult res;
+ LazyBuf lhsBuf, rhsBuf;
+ FStr rhs;
+ struct ModifyWord_SysVSubstArgs args;
+ Substring lhs;
+ const char *lhsSuffix;
const char *mod = *pp;
- Boolean eqFound = FALSE;
+ bool eqFound = false;
/*
* First we make a pass through the string trying to verify it is a
@@ -3422,38 +3610,50 @@ ApplyModifier_SysV(const char **pp, const char *val, ApplyModifiersState *st)
const char *p = mod;
while (*p != '\0' && depth > 0) {
if (*p == '=') { /* XXX: should also test depth == 1 */
- eqFound = TRUE;
- /* continue looking for st->endc */
- } else if (*p == st->endc)
+ eqFound = true;
+ /* continue looking for ch->endc */
+ } else if (*p == ch->endc)
depth--;
- else if (*p == st->startc)
+ else if (*p == ch->startc)
depth++;
if (depth > 0)
p++;
}
- if (*p != st->endc || !eqFound)
+ if (*p != ch->endc || !eqFound)
return AMR_UNKNOWN;
- res = ParseModifierPart(pp, '=', st->eflags, st, &lhs);
+ res = ParseModifierPart(pp, '=', expr->emode, ch, &lhsBuf);
if (res != VPR_OK)
return AMR_CLEANUP;
/* The SysV modifier lasts until the end of the variable expression. */
- res = ParseModifierPart(pp, st->endc, st->eflags, st, &rhs);
- if (res != VPR_OK)
+ res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &rhsBuf);
+ if (res != VPR_OK) {
+ LazyBuf_Done(&lhsBuf);
return AMR_CLEANUP;
-
- (*pp)--;
- if (lhs[0] == '\0' && val[0] == '\0') {
- st->newVal = FStr_InitRefer(val); /* special case */
- } else {
- struct ModifyWord_SYSVSubstArgs args = { st->scope, lhs, rhs };
- st->newVal = FStr_InitOwn(
- ModifyWords(val, ModifyWord_SYSVSubst, &args,
- st->oneBigWord, st->sep));
}
- free(lhs);
- free(rhs);
+ rhs = LazyBuf_DoneGet(&rhsBuf);
+
+ (*pp)--; /* Go back to the ch->endc. */
+
+ /* Do not turn an empty expression into non-empty. */
+ if (lhsBuf.len == 0 && expr->value.str[0] == '\0')
+ goto done;
+
+ lhs = LazyBuf_Get(&lhsBuf);
+ lhsSuffix = Substring_SkipFirst(lhs, '%');
+
+ args.scope = expr->scope;
+ args.lhsPrefix = Substring_Init(lhs.start,
+ lhsSuffix != lhs.start ? lhsSuffix - 1 : lhs.start);
+ args.lhsPercent = lhsSuffix != lhs.start;
+ args.lhsSuffix = Substring_Init(lhsSuffix, lhs.end);
+ args.rhs = rhs.str;
+
+ ModifyWords(ch, ModifyWord_SysVSubst, &args, ch->oneBigWord);
+
+done:
+ LazyBuf_Done(&lhsBuf);
return AMR_OK;
}
#endif
@@ -3461,133 +3661,155 @@ ApplyModifier_SysV(const char **pp, const char *val, ApplyModifiersState *st)
#ifdef SUNSHCMD
/* :sh */
static ApplyModifierResult
-ApplyModifier_SunShell(const char **pp, const char *val,
- ApplyModifiersState *st)
+ApplyModifier_SunShell(const char **pp, ModChain *ch)
{
+ Expr *expr = ch->expr;
const char *p = *pp;
- if (p[1] == 'h' && (p[2] == st->endc || p[2] == ':')) {
- if (st->eflags & VARE_WANTRES) {
- const char *errfmt;
- st->newVal = FStr_InitOwn(Cmd_Exec(val, &errfmt));
- if (errfmt != NULL)
- Error(errfmt, val);
- } else
- st->newVal = FStr_InitRefer("");
- *pp = p + 2;
- return AMR_OK;
- } else
+ if (!(p[1] == 'h' && IsDelimiter(p[2], ch)))
return AMR_UNKNOWN;
+ *pp = p + 2;
+
+ if (Expr_ShouldEval(expr)) {
+ const char *errfmt;
+ char *output = Cmd_Exec(expr->value.str, &errfmt);
+ if (errfmt != NULL)
+ Error(errfmt, expr->value.str);
+ Expr_SetValueOwn(expr, output);
+ }
+
+ return AMR_OK;
}
#endif
static void
-LogBeforeApply(const ApplyModifiersState *st, const char *mod, char endc,
- const char *val)
+LogBeforeApply(const ModChain *ch, const char *mod)
{
- char eflags_str[VarEvalFlags_ToStringSize];
- char vflags_str[VarFlags_ToStringSize];
- Boolean is_single_char = mod[0] != '\0' &&
- (mod[1] == endc || mod[1] == ':');
+ const Expr *expr = ch->expr;
+ bool is_single_char = mod[0] != '\0' && IsDelimiter(mod[1], ch);
- /* At this point, only the first character of the modifier can
- * be used since the end of the modifier is not yet known. */
- debug_printf("Applying ${%s:%c%s} to \"%s\" (%s, %s, %s)\n",
- st->var->name.str, mod[0], is_single_char ? "" : "...", val,
- VarEvalFlags_ToString(eflags_str, st->eflags),
- VarFlags_ToString(vflags_str, st->var->flags),
- VarExprStatus_Name[st->exprStatus]);
+ /*
+ * At this point, only the first character of the modifier can
+ * be used since the end of the modifier is not yet known.
+ */
+
+ if (!Expr_ShouldEval(expr)) {
+ debug_printf("Parsing modifier ${%s:%c%s}\n",
+ expr->name, mod[0], is_single_char ? "" : "...");
+ return;
+ }
+
+ if ((expr->emode == VARE_WANTRES || expr->emode == VARE_UNDEFERR) &&
+ expr->defined == DEF_REGULAR) {
+ debug_printf(
+ "Evaluating modifier ${%s:%c%s} on value \"%s\"\n",
+ expr->name, mod[0], is_single_char ? "" : "...",
+ expr->value.str);
+ return;
+ }
+
+ debug_printf(
+ "Evaluating modifier ${%s:%c%s} on value \"%s\" (%s, %s)\n",
+ expr->name, mod[0], is_single_char ? "" : "...", expr->value.str,
+ VarEvalMode_Name[expr->emode], ExprDefined_Name[expr->defined]);
}
static void
-LogAfterApply(ApplyModifiersState *st, const char *p, const char *mod)
+LogAfterApply(const ModChain *ch, const char *p, const char *mod)
{
- char eflags_str[VarEvalFlags_ToStringSize];
- char vflags_str[VarFlags_ToStringSize];
- const char *quot = st->newVal.str == var_Error ? "" : "\"";
- const char *newVal =
- st->newVal.str == var_Error ? "error" : st->newVal.str;
+ const Expr *expr = ch->expr;
+ const char *value = expr->value.str;
+ const char *quot = value == var_Error ? "" : "\"";
+
+ if ((expr->emode == VARE_WANTRES || expr->emode == VARE_UNDEFERR) &&
+ expr->defined == DEF_REGULAR) {
- debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s, %s)\n",
- st->var->name.str, (int)(p - mod), mod, quot, newVal, quot,
- VarEvalFlags_ToString(eflags_str, st->eflags),
- VarFlags_ToString(vflags_str, st->var->flags),
- VarExprStatus_Name[st->exprStatus]);
+ debug_printf("Result of ${%s:%.*s} is %s%s%s\n",
+ expr->name, (int)(p - mod), mod,
+ quot, value == var_Error ? "error" : value, quot);
+ return;
+ }
+
+ debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s)\n",
+ expr->name, (int)(p - mod), mod,
+ quot, value == var_Error ? "error" : value, quot,
+ VarEvalMode_Name[expr->emode],
+ ExprDefined_Name[expr->defined]);
}
static ApplyModifierResult
-ApplyModifier(const char **pp, const char *val, ApplyModifiersState *st)
+ApplyModifier(const char **pp, ModChain *ch)
{
switch (**pp) {
+ case '!':
+ return ApplyModifier_ShellCommand(pp, ch);
case ':':
- return ApplyModifier_Assign(pp, st);
+ return ApplyModifier_Assign(pp, ch);
+ case '?':
+ return ApplyModifier_IfElse(pp, ch);
case '@':
- return ApplyModifier_Loop(pp, val, st);
+ return ApplyModifier_Loop(pp, ch);
+ case '[':
+ return ApplyModifier_Words(pp, ch);
case '_':
- return ApplyModifier_Remember(pp, val, st);
+ return ApplyModifier_Remember(pp, ch);
+#ifndef NO_REGEX
+ case 'C':
+ return ApplyModifier_Regex(pp, ch);
+#endif
case 'D':
- case 'U':
- return ApplyModifier_Defined(pp, val, st);
- case 'L':
- return ApplyModifier_Literal(pp, st);
- case 'P':
- return ApplyModifier_Path(pp, st);
- case '!':
- return ApplyModifier_ShellCommand(pp, st);
- case '[':
- return ApplyModifier_Words(pp, val, st);
+ return ApplyModifier_Defined(pp, ch);
+ case 'E':
+ return ApplyModifier_WordFunc(pp, ch, ModifyWord_Suffix);
case 'g':
- return ApplyModifier_Gmtime(pp, val, st);
+ return ApplyModifier_Gmtime(pp, ch);
+ case 'H':
+ return ApplyModifier_WordFunc(pp, ch, ModifyWord_Head);
case 'h':
- return ApplyModifier_Hash(pp, val, st);
+ return ApplyModifier_Hash(pp, ch);
+ case 'L':
+ return ApplyModifier_Literal(pp, ch);
case 'l':
- return ApplyModifier_Localtime(pp, val, st);
- case 't':
- return ApplyModifier_To(pp, val, st);
- case 'N':
+ return ApplyModifier_Localtime(pp, ch);
case 'M':
- return ApplyModifier_Match(pp, val, st);
- case 'S':
- return ApplyModifier_Subst(pp, val, st);
- case '?':
- return ApplyModifier_IfElse(pp, st);
-#ifndef NO_REGEX
- case 'C':
- return ApplyModifier_Regex(pp, val, st);
-#endif
- case 'q':
+ case 'N':
+ return ApplyModifier_Match(pp, ch);
+ case 'O':
+ return ApplyModifier_Order(pp, ch);
+ case 'P':
+ return ApplyModifier_Path(pp, ch);
case 'Q':
- return ApplyModifier_Quote(pp, val, st);
- case 'T':
- return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Tail);
- case 'H':
- return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Head);
- case 'E':
- return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Suffix);
+ case 'q':
+ return ApplyModifier_Quote(pp, ch);
case 'R':
- return ApplyModifier_WordFunc(pp, val, st, ModifyWord_Root);
+ return ApplyModifier_WordFunc(pp, ch, ModifyWord_Root);
case 'r':
- return ApplyModifier_Range(pp, val, st);
- case 'O':
- return ApplyModifier_Order(pp, val, st);
- case 'u':
- return ApplyModifier_Unique(pp, val, st);
+ return ApplyModifier_Range(pp, ch);
+ case 'S':
+ return ApplyModifier_Subst(pp, ch);
#ifdef SUNSHCMD
case 's':
- return ApplyModifier_SunShell(pp, val, st);
+ return ApplyModifier_SunShell(pp, ch);
#endif
+ case 'T':
+ return ApplyModifier_WordFunc(pp, ch, ModifyWord_Tail);
+ case 't':
+ return ApplyModifier_To(pp, ch);
+ case 'U':
+ return ApplyModifier_Defined(pp, ch);
+ case 'u':
+ return ApplyModifier_Unique(pp, ch);
default:
return AMR_UNKNOWN;
}
}
-static FStr ApplyModifiers(const char **, FStr, char, char, Var *,
- VarExprStatus *, GNode *, VarEvalFlags);
+static void ApplyModifiers(Expr *, const char **, char, char);
typedef enum ApplyModifiersIndirectResult {
/* The indirect modifiers have been applied successfully. */
AMIR_CONTINUE,
/* Fall back to the SysV modifier. */
- AMIR_APPLY_MODS,
+ AMIR_SYSV,
/* Error out. */
AMIR_OUT
} ApplyModifiersIndirectResult;
@@ -3602,25 +3824,22 @@ typedef enum ApplyModifiersIndirectResult {
* Multiple groups of indirect modifiers can be chained by separating them
* with colons. ${VAR:${M1}:${M2}} contains 2 indirect modifiers.
*
- * If the variable expression is not followed by st->endc or ':', fall
+ * If the variable expression is not followed by ch->endc or ':', fall
* back to trying the SysV modifier, such as in ${VAR:${FROM}=${TO}}.
- *
- * The expression ${VAR:${M1}${M2}} is not treated as an indirect
- * modifier, and it is neither a SysV modifier but a parse error.
*/
static ApplyModifiersIndirectResult
-ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp,
- FStr *inout_value)
+ApplyModifiersIndirect(ModChain *ch, const char **pp)
{
+ Expr *expr = ch->expr;
const char *p = *pp;
FStr mods;
- (void)Var_Parse(&p, st->scope, st->eflags, &mods);
+ (void)Var_Parse(&p, expr->scope, expr->emode, &mods);
/* TODO: handle errors */
- if (mods.str[0] != '\0' && *p != '\0' && *p != ':' && *p != st->endc) {
+ if (mods.str[0] != '\0' && *p != '\0' && !IsDelimiter(*p, ch)) {
FStr_Done(&mods);
- return AMIR_APPLY_MODS;
+ return AMIR_SYSV;
}
DEBUG3(VAR, "Indirect modifier \"%s\" from \"%.*s\"\n",
@@ -3628,10 +3847,8 @@ ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp,
if (mods.str[0] != '\0') {
const char *modsp = mods.str;
- FStr newVal = ApplyModifiers(&modsp, *inout_value, '\0', '\0',
- st->var, &st->exprStatus, st->scope, st->eflags);
- *inout_value = newVal;
- if (newVal.str == var_Error || *modsp != '\0') {
+ ApplyModifiers(expr, &modsp, '\0', '\0');
+ if (expr->value.str == var_Error || *modsp != '\0') {
FStr_Done(&mods);
*pp = p;
return AMIR_OUT; /* error already reported */
@@ -3641,10 +3858,10 @@ ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp,
if (*p == ':')
p++;
- else if (*p == '\0' && st->endc != '\0') {
- Error("Unclosed variable specification after complex "
- "modifier (expecting '%c') for %s",
- st->endc, st->var->name.str);
+ else if (*p == '\0' && ch->endc != '\0') {
+ Error("Unclosed variable expression after indirect "
+ "modifier, expecting '%c' for variable \"%s\"",
+ ch->endc, expr->name);
*pp = p;
return AMIR_OUT;
}
@@ -3654,36 +3871,36 @@ ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp,
}
static ApplyModifierResult
-ApplySingleModifier(ApplyModifiersState *st, const char *mod, char endc,
- const char **pp, FStr *inout_value)
+ApplySingleModifier(const char **pp, ModChain *ch)
{
ApplyModifierResult res;
+ const char *mod = *pp;
const char *p = *pp;
- const char *const val = inout_value->str;
if (DEBUG(VAR))
- LogBeforeApply(st, mod, endc, val);
+ LogBeforeApply(ch, mod);
- res = ApplyModifier(&p, val, st);
+ res = ApplyModifier(&p, ch);
#ifdef SYSVVARSUB
if (res == AMR_UNKNOWN) {
assert(p == mod);
- res = ApplyModifier_SysV(&p, val, st);
+ res = ApplyModifier_SysV(&p, ch);
}
#endif
if (res == AMR_UNKNOWN) {
- Parse_Error(PARSE_FATAL, "Unknown modifier '%c'", *mod);
/*
* Guess the end of the current modifier.
* XXX: Skipping the rest of the modifier hides
* errors and leads to wrong results.
* Parsing should rather stop here.
*/
- for (p++; *p != ':' && *p != st->endc && *p != '\0'; p++)
+ for (p++; !IsDelimiter(*p, ch) && *p != '\0'; p++)
continue;
- st->newVal = FStr_InitRefer(var_Error);
+ Parse_Error(PARSE_FATAL, "Unknown modifier \"%.*s\"",
+ (int)(p - mod), mod);
+ Expr_SetValueRefer(ch->expr, var_Error);
}
if (res == AMR_CLEANUP || res == AMR_BAD) {
*pp = p;
@@ -3691,20 +3908,18 @@ ApplySingleModifier(ApplyModifiersState *st, const char *mod, char endc,
}
if (DEBUG(VAR))
- LogAfterApply(st, p, mod);
+ LogAfterApply(ch, p, mod);
- if (st->newVal.str != val) {
- FStr_Done(inout_value);
- *inout_value = st->newVal;
- }
- if (*p == '\0' && st->endc != '\0') {
+ if (*p == '\0' && ch->endc != '\0') {
Error(
- "Unclosed variable specification (expecting '%c') "
- "for \"%s\" (value \"%s\") modifier %c",
- st->endc, st->var->name.str, inout_value->str, *mod);
+ "Unclosed variable expression, expecting '%c' for "
+ "modifier \"%.*s\" of variable \"%s\" with value \"%s\"",
+ ch->endc,
+ (int)(p - mod), mod,
+ ch->expr->name, ch->expr->value.str);
} else if (*p == ':') {
p++;
- } else if (opts.strict && *p != '\0' && *p != endc) {
+ } else if (opts.strict && *p != '\0' && *p != ch->endc) {
Parse_Error(PARSE_FATAL,
"Missing delimiter ':' after modifier \"%.*s\"",
(int)(p - mod), mod);
@@ -3717,44 +3932,46 @@ ApplySingleModifier(ApplyModifiersState *st, const char *mod, char endc,
return AMR_OK;
}
+#if __STDC_VERSION__ >= 199901L
+#define ModChain_Literal(expr, startc, endc, sep, oneBigWord) \
+ (ModChain) { expr, startc, endc, sep, oneBigWord }
+#else
+MAKE_INLINE ModChain
+ModChain_Literal(Expr *expr, char startc, char endc, char sep, bool oneBigWord)
+{
+ ModChain ch;
+ ch.expr = expr;
+ ch.startc = startc;
+ ch.endc = endc;
+ ch.sep = sep;
+ ch.oneBigWord = oneBigWord;
+ return ch;
+}
+#endif
+
/* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */
-static FStr
+static void
ApplyModifiers(
- const char **pp, /* the parsing position, updated upon return */
- FStr value, /* the current value of the expression */
- char startc, /* '(' or '{', or '\0' for indirect modifiers */
- char endc, /* ')' or '}', or '\0' for indirect modifiers */
- Var *v,
- VarExprStatus *exprStatus,
- GNode *scope, /* for looking up and modifying variables */
- VarEvalFlags eflags
+ Expr *expr,
+ const char **pp, /* the parsing position, updated upon return */
+ char startc, /* '(' or '{'; or '\0' for indirect modifiers */
+ char endc /* ')' or '}'; or '\0' for indirect modifiers */
)
{
- ApplyModifiersState st = {
- startc, endc, v, scope, eflags,
-#if defined(lint)
- /* lint cannot parse C99 struct initializers yet. */
- { var_Error, NULL },
-#else
- FStr_InitRefer(var_Error), /* .newVal */
-#endif
- ' ', /* .sep */
- FALSE, /* .oneBigWord */
- *exprStatus /* .exprStatus */
- };
+ ModChain ch = ModChain_Literal(expr, startc, endc, ' ', false);
const char *p;
const char *mod;
assert(startc == '(' || startc == '{' || startc == '\0');
assert(endc == ')' || endc == '}' || endc == '\0');
- assert(value.str != NULL);
+ assert(expr->value.str != NULL);
p = *pp;
if (*p == '\0' && endc != '\0') {
Error(
"Unclosed variable expression (expecting '%c') for \"%s\"",
- st.endc, st.var->name.str);
+ ch.endc, expr->name);
goto cleanup;
}
@@ -3762,19 +3979,22 @@ ApplyModifiers(
ApplyModifierResult res;
if (*p == '$') {
- ApplyModifiersIndirectResult amir;
- amir = ApplyModifiersIndirect(&st, &p, &value);
+ ApplyModifiersIndirectResult amir =
+ ApplyModifiersIndirect(&ch, &p);
if (amir == AMIR_CONTINUE)
continue;
if (amir == AMIR_OUT)
break;
+ /*
+ * It's neither '${VAR}:' nor '${VAR}}'. Try to parse
+ * it as a SysV modifier, as that is the only modifier
+ * that can start with '$'.
+ */
}
- /* default value, in case of errors */
- st.newVal = FStr_InitRefer(var_Error);
mod = p;
- res = ApplySingleModifier(&st, mod, endc, &p, &value);
+ res = ApplySingleModifier(&p, &ch);
if (res == AMR_CLEANUP)
goto cleanup;
if (res == AMR_BAD)
@@ -3782,48 +4002,60 @@ ApplyModifiers(
}
*pp = p;
- assert(value.str != NULL); /* Use var_Error or varUndefined instead. */
- *exprStatus = st.exprStatus;
- return value;
+ assert(expr->value.str != NULL); /* Use var_Error or varUndefined. */
+ return;
bad_modifier:
/* XXX: The modifier end is only guessed. */
- Error("Bad modifier `:%.*s' for %s",
- (int)strcspn(mod, ":)}"), mod, st.var->name.str);
+ Error("Bad modifier \":%.*s\" for variable \"%s\"",
+ (int)strcspn(mod, ":)}"), mod, expr->name);
cleanup:
+ /*
+ * TODO: Use p + strlen(p) instead, to stop parsing immediately.
+ *
+ * In the unit tests, this generates a few unterminated strings in the
+ * shell commands though. Instead of producing these unfinished
+ * strings, commands with evaluation errors should not be run at all.
+ *
+ * To make that happen, Var_Subst must report the actual errors
+ * instead of returning VPR_OK unconditionally.
+ */
*pp = p;
- FStr_Done(&value);
- *exprStatus = st.exprStatus;
- return FStr_InitRefer(var_Error);
+ Expr_SetValueRefer(expr, var_Error);
}
/*
- * Only four of the local variables are treated specially as they are the
- * only four that will be set when dynamic sources are expanded.
+ * Only 4 of the 7 local variables are treated specially as they are the only
+ * ones that will be set when dynamic sources are expanded.
*/
-static Boolean
-VarnameIsDynamic(const char *name, size_t len)
+static bool
+VarnameIsDynamic(Substring varname)
{
+ const char *name;
+ size_t len;
+
+ name = varname.start;
+ len = Substring_Length(varname);
if (len == 1 || (len == 2 && (name[1] == 'F' || name[1] == 'D'))) {
switch (name[0]) {
case '@':
case '%':
case '*':
case '!':
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
if ((len == 7 || len == 8) && name[0] == '.' && ch_isupper(name[1])) {
- return strcmp(name, ".TARGET") == 0 ||
- strcmp(name, ".ARCHIVE") == 0 ||
- strcmp(name, ".PREFIX") == 0 ||
- strcmp(name, ".MEMBER") == 0;
+ return Substring_Equals(varname, ".TARGET") ||
+ Substring_Equals(varname, ".ARCHIVE") ||
+ Substring_Equals(varname, ".PREFIX") ||
+ Substring_Equals(varname, ".MEMBER");
}
- return FALSE;
+ return false;
}
static const char *
@@ -3857,58 +4089,45 @@ UndefinedShortVarValue(char varname, const GNode *scope)
* Parse a variable name, until the end character or a colon, whichever
* comes first.
*/
-static char *
+static void
ParseVarname(const char **pp, char startc, char endc,
- GNode *scope, VarEvalFlags eflags,
- size_t *out_varname_len)
+ GNode *scope, VarEvalMode emode,
+ LazyBuf *buf)
{
- Buffer buf;
const char *p = *pp;
- int depth = 1;
+ int depth = 0; /* Track depth so we can spot parse errors. */
- Buf_Init(&buf);
+ LazyBuf_Init(buf, p);
while (*p != '\0') {
- /* Track depth so we can spot parse errors. */
+ if ((*p == endc || *p == ':') && depth == 0)
+ break;
if (*p == startc)
depth++;
- if (*p == endc) {
- if (--depth == 0)
- break;
- }
- if (*p == ':' && depth == 1)
- break;
+ if (*p == endc)
+ depth--;
/* A variable inside a variable, expand. */
if (*p == '$') {
FStr nested_val;
- (void)Var_Parse(&p, scope, eflags, &nested_val);
+ (void)Var_Parse(&p, scope, emode, &nested_val);
/* TODO: handle errors */
- Buf_AddStr(&buf, nested_val.str);
+ LazyBuf_AddStr(buf, nested_val.str);
FStr_Done(&nested_val);
} else {
- Buf_AddByte(&buf, *p);
+ LazyBuf_Add(buf, *p);
p++;
}
}
*pp = p;
- *out_varname_len = buf.len;
- return Buf_DoneData(&buf);
}
static VarParseResult
ValidShortVarname(char varname, const char *start)
{
- switch (varname) {
- case '\0':
- case ')':
- case '}':
- case ':':
- case '$':
- break; /* and continue below */
- default:
+ if (varname != '$' && varname != ':' && varname != '}' &&
+ varname != ')' && varname != '\0')
return VPR_OK;
- }
if (!opts.strict)
return VPR_ERR; /* XXX: Missing error message */
@@ -3926,50 +4145,45 @@ ValidShortVarname(char varname, const char *start)
}
/*
- * Parse a single-character variable name such as $V or $@.
+ * Parse a single-character variable name such as in $V or $@.
* Return whether to continue parsing.
*/
-static Boolean
-ParseVarnameShort(char startc, const char **pp, GNode *scope,
- VarEvalFlags eflags,
- VarParseResult *out_FALSE_res, const char **out_FALSE_val,
- Var **out_TRUE_var)
+static bool
+ParseVarnameShort(char varname, const char **pp, GNode *scope,
+ VarEvalMode emode,
+ VarParseResult *out_false_res, const char **out_false_val,
+ Var **out_true_var)
{
char name[2];
Var *v;
VarParseResult vpr;
- /*
- * If it's not bounded by braces of some sort, life is much simpler.
- * We just need to check for the first character and return the
- * value if it exists.
- */
-
- vpr = ValidShortVarname(startc, *pp);
+ vpr = ValidShortVarname(varname, *pp);
if (vpr != VPR_OK) {
(*pp)++;
- *out_FALSE_val = var_Error;
- *out_FALSE_res = vpr;
- return FALSE;
+ *out_false_res = vpr;
+ *out_false_val = var_Error;
+ return false;
}
- name[0] = startc;
+ name[0] = varname;
name[1] = '\0';
- v = VarFind(name, scope, TRUE);
+ v = VarFind(name, scope, true);
if (v == NULL) {
const char *val;
*pp += 2;
- val = UndefinedShortVarValue(startc, scope);
+ val = UndefinedShortVarValue(varname, scope);
if (val == NULL)
- val = eflags & VARE_UNDEFERR ? var_Error : varUndefined;
+ val = emode == VARE_UNDEFERR
+ ? var_Error : varUndefined;
if (opts.strict && val == var_Error) {
Parse_Error(PARSE_FATAL,
"Variable \"%s\" is undefined", name);
- *out_FALSE_res = VPR_ERR;
- *out_FALSE_val = val;
- return FALSE;
+ *out_false_res = VPR_ERR;
+ *out_false_val = val;
+ return false;
}
/*
@@ -3982,72 +4196,64 @@ ParseVarnameShort(char startc, const char **pp, GNode *scope,
* If undefined expressions are allowed, this should rather
* be VPR_UNDEF instead of VPR_OK.
*/
- *out_FALSE_res = eflags & VARE_UNDEFERR ? VPR_UNDEF : VPR_OK;
- *out_FALSE_val = val;
- return FALSE;
+ *out_false_res = emode == VARE_UNDEFERR
+ ? VPR_UNDEF : VPR_OK;
+ *out_false_val = val;
+ return false;
}
- *out_TRUE_var = v;
- return TRUE;
+ *out_true_var = v;
+ return true;
}
/* Find variables like @F or <D. */
static Var *
-FindLocalLegacyVar(const char *varname, size_t namelen, GNode *scope,
+FindLocalLegacyVar(Substring varname, GNode *scope,
const char **out_extraModifiers)
{
+ Var *v;
+
/* Only resolve these variables if scope is a "real" target. */
if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL)
return NULL;
- if (namelen != 2)
+ if (Substring_Length(varname) != 2)
return NULL;
- if (varname[1] != 'F' && varname[1] != 'D')
+ if (varname.start[1] != 'F' && varname.start[1] != 'D')
return NULL;
- if (strchr("@%?*!<>", varname[0]) == NULL)
+ if (strchr("@%?*!<>", varname.start[0]) == NULL)
return NULL;
- {
- char name[] = { varname[0], '\0' };
- Var *v = VarFind(name, scope, FALSE);
+ v = VarFindSubstring(Substring_Sub(varname, 0, 1), scope, false);
+ if (v == NULL)
+ return NULL;
- if (v != NULL) {
- if (varname[1] == 'D') {
- *out_extraModifiers = "H:";
- } else { /* F */
- *out_extraModifiers = "T:";
- }
- }
- return v;
- }
+ *out_extraModifiers = varname.start[1] == 'D' ? "H:" : "T:";
+ return v;
}
static VarParseResult
-EvalUndefined(Boolean dynamic, const char *start, const char *p, char *varname,
- VarEvalFlags eflags,
- FStr *out_val)
+EvalUndefined(bool dynamic, const char *start, const char *p,
+ Substring varname, VarEvalMode emode, FStr *out_val)
{
if (dynamic) {
*out_val = FStr_InitOwn(bmake_strsedup(start, p));
- free(varname);
return VPR_OK;
}
- if ((eflags & VARE_UNDEFERR) && opts.strict) {
+ if (emode == VARE_UNDEFERR && opts.strict) {
Parse_Error(PARSE_FATAL,
- "Variable \"%s\" is undefined", varname);
- free(varname);
+ "Variable \"%.*s\" is undefined",
+ (int)Substring_Length(varname), varname.start);
*out_val = FStr_InitRefer(var_Error);
return VPR_ERR;
}
- if (eflags & VARE_UNDEFERR) {
- free(varname);
+ if (emode == VARE_UNDEFERR) {
*out_val = FStr_InitRefer(var_Error);
return VPR_UNDEF; /* XXX: Should be VPR_ERR instead. */
}
- free(varname);
*out_val = FStr_InitRefer(varUndefined);
return VPR_OK;
}
@@ -4058,59 +4264,60 @@ EvalUndefined(Boolean dynamic, const char *start, const char *p, char *varname,
* ${VAR:Modifiers}, up to the ':' that starts the modifiers.
* Return whether to continue parsing.
*/
-static Boolean
+static bool
ParseVarnameLong(
- const char *p,
+ const char **pp,
char startc,
GNode *scope,
- VarEvalFlags eflags,
+ VarEvalMode emode,
- const char **out_FALSE_pp,
- VarParseResult *out_FALSE_res,
- FStr *out_FALSE_val,
+ const char **out_false_pp,
+ VarParseResult *out_false_res,
+ FStr *out_false_val,
- char *out_TRUE_endc,
- const char **out_TRUE_p,
- Var **out_TRUE_v,
- Boolean *out_TRUE_haveModifier,
- const char **out_TRUE_extraModifiers,
- Boolean *out_TRUE_dynamic,
- VarExprStatus *out_TRUE_exprStatus
+ 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
)
{
- size_t namelen;
- char *varname;
+ LazyBuf varname;
Var *v;
- Boolean haveModifier;
- Boolean dynamic = FALSE;
+ bool haveModifier;
+ bool dynamic = false;
+ const char *p = *pp;
const char *const start = p;
char endc = startc == '(' ? ')' : '}';
p += 2; /* skip "${" or "$(" or "y(" */
- varname = ParseVarname(&p, startc, endc, scope, eflags, &namelen);
+ ParseVarname(&p, startc, endc, scope, emode, &varname);
if (*p == ':') {
- haveModifier = TRUE;
+ haveModifier = true;
} else if (*p == endc) {
- haveModifier = FALSE;
+ haveModifier = false;
} else {
- Parse_Error(PARSE_FATAL, "Unclosed variable \"%s\"", varname);
- free(varname);
- *out_FALSE_pp = p;
- *out_FALSE_val = FStr_InitRefer(var_Error);
- *out_FALSE_res = VPR_ERR;
- return FALSE;
+ Substring name = LazyBuf_Get(&varname);
+ Parse_Error(PARSE_FATAL, "Unclosed variable \"%.*s\"",
+ (int)Substring_Length(name), name.start);
+ LazyBuf_Done(&varname);
+ *out_false_pp = p;
+ *out_false_val = FStr_InitRefer(var_Error);
+ *out_false_res = VPR_ERR;
+ return false;
}
- v = VarFind(varname, scope, TRUE);
+ v = VarFindSubstring(LazyBuf_Get(&varname), scope, true);
/* At this point, p points just after the variable name,
* either at ':' or at endc. */
if (v == NULL) {
- v = FindLocalLegacyVar(varname, namelen, scope,
- out_TRUE_extraModifiers);
+ v = FindLocalLegacyVar(LazyBuf_Get(&varname), scope,
+ out_true_extraModifiers);
}
if (v == NULL) {
@@ -4118,15 +4325,15 @@ ParseVarnameLong(
* Defer expansion of dynamic variables if they appear in
* non-local scope since they are not defined there.
*/
- dynamic = VarnameIsDynamic(varname, namelen) &&
+ dynamic = VarnameIsDynamic(LazyBuf_Get(&varname)) &&
(scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL);
if (!haveModifier) {
p++; /* skip endc */
- *out_FALSE_pp = p;
- *out_FALSE_res = EvalUndefined(dynamic, start, p,
- varname, eflags, out_FALSE_val);
- return FALSE;
+ *out_false_pp = p;
+ *out_false_res = EvalUndefined(dynamic, start, p,
+ LazyBuf_Get(&varname), emode, out_false_val);
+ return false;
}
/*
@@ -4135,34 +4342,34 @@ ParseVarnameLong(
* variable name, such as :L or :?.
*
* Most modifiers leave this expression in the "undefined"
- * state (VEF_UNDEF), only a few modifiers like :D, :U, :L,
+ * state (VES_UNDEF), only a few modifiers like :D, :U, :L,
* :P turn this undefined expression into a defined
- * expression (VEF_DEF).
+ * expression (VES_DEF).
*
- * At the end, after applying all modifiers, if the expression
+ * In the end, after applying all modifiers, if the expression
* is still undefined, Var_Parse will return an empty string
* instead of the actually computed value.
*/
- v = VarNew(FStr_InitOwn(varname), "", VAR_NONE);
- *out_TRUE_exprStatus = VES_UNDEF;
+ v = VarNew(LazyBuf_DoneGet(&varname), "", false, false);
+ *out_true_exprDefined = DEF_UNDEF;
} else
- free(varname);
+ LazyBuf_Done(&varname);
- *out_TRUE_endc = endc;
- *out_TRUE_p = p;
- *out_TRUE_v = v;
- *out_TRUE_haveModifier = haveModifier;
- *out_TRUE_dynamic = dynamic;
- return TRUE;
+ *pp = p;
+ *out_true_endc = endc;
+ *out_true_v = v;
+ *out_true_haveModifier = haveModifier;
+ *out_true_dynamic = dynamic;
+ return true;
}
/* Free the environment variable now since we own it. */
static void
-FreeEnvVar(void **out_val_freeIt, Var *v, const char *value)
+FreeEnvVar(Var *v, FStr *inout_val)
{
char *varValue = Buf_DoneData(&v->val);
- if (value == varValue)
- *out_val_freeIt = varValue;
+ if (inout_val->str == varValue)
+ inout_val->freeIt = varValue;
else
free(varValue);
@@ -4170,6 +4377,54 @@ FreeEnvVar(void **out_val_freeIt, Var *v, const char *value)
free(v);
}
+#if __STDC_VERSION__ >= 199901L
+#define Expr_Literal(name, value, emode, scope, defined) \
+ { name, value, emode, scope, defined }
+#else
+MAKE_INLINE Expr
+Expr_Literal(const char *name, FStr value,
+ VarEvalMode emode, GNode *scope, ExprDefined defined)
+{
+ Expr expr;
+
+ expr.name = name;
+ expr.value = value;
+ expr.emode = emode;
+ expr.scope = scope;
+ expr.defined = defined;
+ return expr;
+}
+#endif
+
+/*
+ * Expressions of the form ${:U...} with a trivial value are often generated
+ * by .for loops and are boring, therefore parse and evaluate them in a fast
+ * lane without debug logging.
+ */
+static bool
+Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value)
+{
+ const char *p;
+
+ p = *pp;
+ if (!(p[0] == '$' && p[1] == '{' && p[2] == ':' && p[3] == 'U'))
+ return false;
+
+ p += 4;
+ while (*p != '$' && *p != '{' && *p != ':' && *p != '\\' &&
+ *p != '}' && *p != '\0')
+ p++;
+ if (*p != '}')
+ return false;
+
+ if (emode == VARE_PARSE_ONLY)
+ *out_value = FStr_InitRefer("");
+ else
+ *out_value = FStr_InitOwn(bmake_strsedup(*pp + 4, p));
+ *pp = p + 1;
+ return true;
+}
+
/*
* Given the start of a variable expression (such as $v, $(VAR),
* ${VAR:Mpattern}), extract the variable name and value, and the modifiers,
@@ -4183,7 +4438,7 @@ FreeEnvVar(void **out_val_freeIt, Var *v, const char *value)
* point to the "y" of "empty(VARNAME:Modifiers)", which
* is syntactically the same.
* scope The scope for finding variables
- * eflags Control the exact details of parsing
+ * emode Controls the exact details of parsing and evaluation
*
* Output:
* *pp The position where to continue parsing.
@@ -4195,49 +4450,48 @@ FreeEnvVar(void **out_val_freeIt, Var *v, const char *value)
* *out_val The value of the variable expression, never NULL.
* *out_val var_Error if there was a parse error.
* *out_val var_Error if the base variable of the expression was
- * undefined, eflags contains VARE_UNDEFERR, and none of
+ * undefined, emode is VARE_UNDEFERR, and none of
* the modifiers turned the undefined expression into a
* defined expression.
* XXX: It is not guaranteed that an error message has
* been printed.
* *out_val varUndefined if the base variable of the expression
- * was undefined, eflags did not contain VARE_UNDEFERR,
+ * was undefined, emode was not VARE_UNDEFERR,
* and none of the modifiers turned the undefined
* expression into a defined expression.
* XXX: It is not guaranteed that an error message has
* been printed.
- * *out_val_freeIt Must be freed by the caller after using *out_val.
*/
-/* coverity[+alloc : arg-*4] */
VarParseResult
-Var_Parse(const char **pp, GNode *scope, VarEvalFlags eflags, FStr *out_val)
+Var_Parse(const char **pp, GNode *scope, VarEvalMode emode, FStr *out_val)
{
const char *p = *pp;
const char *const start = p;
- /* TRUE if have modifiers for the variable. */
- Boolean haveModifier;
+ /* true if have modifiers for the variable. */
+ bool haveModifier;
/* Starting character if variable in parens or braces. */
char startc;
/* Ending character if variable in parens or braces. */
char endc;
/*
- * TRUE if the variable is local and we're expanding it in a
+ * true if the variable is local and we're expanding it in a
* non-local scope. This is done to support dynamic sources.
* The result is just the expression, unaltered.
*/
- Boolean dynamic;
+ bool dynamic;
const char *extramodifiers;
Var *v;
- FStr value;
- char eflags_str[VarEvalFlags_ToStringSize];
- VarExprStatus exprStatus = VES_NONE;
+ Expr expr = Expr_Literal(NULL, FStr_InitRefer(NULL), emode,
+ scope, DEF_REGULAR);
- DEBUG2(VAR, "Var_Parse: %s with %s\n", start,
- VarEvalFlags_ToString(eflags_str, eflags));
+ if (Var_Parse_FastLane(pp, emode, out_val))
+ return VPR_OK;
+
+ DEBUG2(VAR, "Var_Parse: %s (%s)\n", start, VarEvalMode_Name[emode]);
*out_val = FStr_InitRefer(NULL);
extramodifiers = NULL; /* extra modifiers to apply first */
- dynamic = FALSE;
+ dynamic = false;
/*
* Appease GCC, which thinks that the variable might not be
@@ -4248,21 +4502,22 @@ Var_Parse(const char **pp, GNode *scope, VarEvalFlags eflags, FStr *out_val)
startc = p[1];
if (startc != '(' && startc != '{') {
VarParseResult res;
- if (!ParseVarnameShort(startc, pp, scope, eflags, &res,
+ if (!ParseVarnameShort(startc, pp, scope, emode, &res,
&out_val->str, &v))
return res;
- haveModifier = FALSE;
+ haveModifier = false;
p++;
} else {
VarParseResult res;
- if (!ParseVarnameLong(p, startc, scope, eflags,
+ if (!ParseVarnameLong(&p, startc, scope, emode,
pp, &res, out_val,
- &endc, &p, &v, &haveModifier, &extramodifiers,
- &dynamic, &exprStatus))
+ &endc, &v, &haveModifier, &extramodifiers,
+ &dynamic, &expr.defined))
return res;
}
- if (v->flags & VAR_IN_USE)
+ expr.name = v->name.str;
+ if (v->inUse)
Fatal("Variable %s is recursive.", v->name.str);
/*
@@ -4274,37 +4529,34 @@ Var_Parse(const char **pp, GNode *scope, VarEvalFlags eflags, FStr *out_val)
* the then-current value of the variable. This might also invoke
* undefined behavior.
*/
- value = FStr_InitRefer(v->val.data);
+ expr.value = FStr_InitRefer(v->val.data);
/*
* Before applying any modifiers, expand any nested expressions from
* the variable value.
*/
- if (strchr(value.str, '$') != NULL && (eflags & VARE_WANTRES)) {
+ if (strchr(expr.value.str, '$') != NULL &&
+ VarEvalMode_ShouldEval(emode)) {
char *expanded;
- VarEvalFlags nested_eflags = eflags;
+ VarEvalMode nested_emode = emode;
if (opts.strict)
- nested_eflags &= ~(unsigned)VARE_UNDEFERR;
- v->flags |= VAR_IN_USE;
- (void)Var_Subst(value.str, scope, nested_eflags, &expanded);
- v->flags &= ~(unsigned)VAR_IN_USE;
+ nested_emode = VarEvalMode_UndefOk(nested_emode);
+ v->inUse = true;
+ (void)Var_Subst(expr.value.str, scope, nested_emode,
+ &expanded);
+ v->inUse = false;
/* TODO: handle errors */
- value = FStr_InitOwn(expanded);
+ Expr_SetValueOwn(&expr, expanded);
}
- if (haveModifier || extramodifiers != NULL) {
- if (extramodifiers != NULL) {
- const char *em = extramodifiers;
- value = ApplyModifiers(&em, value, '\0', '\0',
- v, &exprStatus, scope, eflags);
- }
-
- if (haveModifier) {
- p++; /* Skip initial colon. */
+ if (extramodifiers != NULL) {
+ const char *em = extramodifiers;
+ ApplyModifiers(&expr, &em, '\0', '\0');
+ }
- value = ApplyModifiers(&p, value, startc, endc,
- v, &exprStatus, scope, eflags);
- }
+ if (haveModifier) {
+ p++; /* Skip initial colon. */
+ ApplyModifiers(&expr, &p, startc, endc);
}
if (*p != '\0') /* Skip past endc if possible. */
@@ -4312,41 +4564,40 @@ Var_Parse(const char **pp, GNode *scope, VarEvalFlags eflags, FStr *out_val)
*pp = p;
- if (v->flags & VAR_FROM_ENV) {
- FreeEnvVar(&value.freeIt, v, value.str);
+ if (v->fromEnv) {
+ FreeEnvVar(v, &expr.value);
- } else if (exprStatus != VES_NONE) {
- if (exprStatus != VES_DEF) {
- FStr_Done(&value);
+ } else if (expr.defined != DEF_REGULAR) {
+ if (expr.defined == DEF_UNDEF) {
if (dynamic) {
- value = FStr_InitOwn(bmake_strsedup(start, p));
+ Expr_SetValueOwn(&expr,
+ bmake_strsedup(start, p));
} else {
/*
* The expression is still undefined,
* therefore discard the actual value and
* return an error marker instead.
*/
- value = FStr_InitRefer(eflags & VARE_UNDEFERR
- ? var_Error : varUndefined);
+ Expr_SetValueRefer(&expr,
+ emode == VARE_UNDEFERR
+ ? var_Error : varUndefined);
}
}
- if (value.str != v->val.data)
+ /* XXX: This is not standard memory management. */
+ if (expr.value.str != v->val.data)
Buf_Done(&v->val);
FStr_Done(&v->name);
free(v);
}
- *out_val = (FStr){ value.str, value.freeIt };
+ *out_val = expr.value;
return VPR_OK; /* XXX: Is not correct in all cases */
}
static void
-VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalFlags eflags)
+VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalMode emode)
{
- /*
- * A dollar sign may be escaped with another dollar
- * sign.
- */
- if (save_dollars && (eflags & VARE_KEEP_DOLLAR))
+ /* A dollar sign may be escaped with another dollar sign. */
+ if (save_dollars && VarEvalMode_ShouldKeepDollar(emode))
Buf_AddByte(res, '$');
Buf_AddByte(res, '$');
*pp += 2;
@@ -4354,19 +4605,19 @@ VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalFlags eflags)
static void
VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
- VarEvalFlags eflags, Boolean *inout_errorReported)
+ VarEvalMode emode, bool *inout_errorReported)
{
const char *p = *pp;
const char *nested_p = p;
FStr val;
- (void)Var_Parse(&nested_p, scope, eflags, &val);
+ (void)Var_Parse(&nested_p, scope, emode, &val);
/* TODO: handle errors */
if (val.str == var_Error || val.str == varUndefined) {
- if (!(eflags & VARE_KEEP_UNDEF)) {
+ if (!VarEvalMode_ShouldKeepUndef(emode)) {
p = nested_p;
- } else if ((eflags & VARE_UNDEFERR) || val.str == var_Error) {
+ } else if (emode == VARE_UNDEFERR || val.str == var_Error) {
/*
* XXX: This condition is wrong. If val == var_Error,
@@ -4386,7 +4637,7 @@ VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
(int)(size_t)(nested_p - p), p);
}
p = nested_p;
- *inout_errorReported = TRUE;
+ *inout_errorReported = true;
} else {
/* Copy the initial '$' of the undefined expression,
* thereby deferring expansion of the expression, but
@@ -4430,10 +4681,10 @@ VarSubstPlain(const char **pp, Buffer *res)
* expanded.
* scope The scope in which to start searching for
* variables. The other scopes are searched as well.
- * eflags Special effects during expansion.
+ * emode The mode for parsing or evaluating subexpressions.
*/
VarParseResult
-Var_Subst(const char *str, GNode *scope, VarEvalFlags eflags, char **out_res)
+Var_Subst(const char *str, GNode *scope, VarEvalMode emode, char **out_res)
{
const char *p = str;
Buffer res;
@@ -4441,16 +4692,16 @@ Var_Subst(const char *str, GNode *scope, VarEvalFlags eflags, char **out_res)
/* Set true if an error has already been reported,
* to prevent a plethora of messages when recursing */
/* XXX: Why is the 'static' necessary here? */
- static Boolean errorReported;
+ static bool errorReported;
Buf_Init(&res);
- errorReported = FALSE;
+ errorReported = false;
while (*p != '\0') {
if (p[0] == '$' && p[1] == '$')
- VarSubstDollarDollar(&p, &res, eflags);
+ VarSubstDollarDollar(&p, &res, emode);
else if (p[0] == '$')
- VarSubstExpr(&p, &res, scope, eflags, &errorReported);
+ VarSubstExpr(&p, &res, scope, emode, &errorReported);
else
VarSubstPlain(&p, &res);
}