aboutsummaryrefslogtreecommitdiff
path: root/var.c
diff options
context:
space:
mode:
Diffstat (limited to 'var.c')
-rw-r--r--var.c713
1 files changed, 395 insertions, 318 deletions
diff --git a/var.c b/var.c
index cecd217b47be..eddca9ef6daa 100644
--- a/var.c
+++ b/var.c
@@ -1,4 +1,4 @@
-/* $NetBSD: var.c,v 1.781 2021/01/10 23:59:53 rillig Exp $ */
+/* $NetBSD: var.c,v 1.807 2021/02/05 05:42:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -80,14 +80,20 @@
*
* Var_End Clean up the module.
*
- * Var_Set Set the value of the variable, creating it if
+ * Var_Set
+ * Var_SetExpand
+ * Set the value of the variable, creating it if
* necessary.
*
- * Var_Append Append more characters to the variable, creating it if
+ * Var_Append
+ * Var_AppendExpand
+ * Append more characters to the variable, creating it if
* necessary. A space is placed between the old value and
* the new one.
*
- * Var_Exists See if a variable exists.
+ * Var_Exists
+ * Var_ExistsExpand
+ * See if a variable exists.
*
* Var_Value Return the unexpanded value of a variable, or NULL if
* the variable is undefined.
@@ -96,7 +102,9 @@
*
* Var_Parse Parse a variable expression such as ${VAR:Mpattern}.
*
- * Var_Delete Delete a variable.
+ * Var_Delete
+ * Var_DeleteExpand
+ * Delete a variable.
*
* Var_ReexportVars
* Export some or even all variables to the environment
@@ -110,7 +118,7 @@
* Debugging:
* Var_Stats Print out hashing statistics if in -dh mode.
*
- * Var_Dump Print out all variables defined in the given context.
+ * Var_Dump Print out all variables defined in the given scope.
*
* XXX: There's a lot of duplication in these functions.
*/
@@ -139,7 +147,7 @@
#include "metachar.h"
/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: var.c,v 1.781 2021/01/10 23:59:53 rillig Exp $");
+MAKE_RCSID("$NetBSD: var.c,v 1.807 2021/02/05 05:42:39 rillig Exp $");
typedef enum VarFlags {
VAR_NONE = 0,
@@ -187,11 +195,11 @@ typedef enum VarFlags {
* value can be queried by expressions such as $V, ${VAR}, or with modifiers
* such as ${VAR:S,from,to,g:Q}.
*
- * There are 3 kinds of variables: context variables, environment variables,
+ * There are 3 kinds of variables: scope variables, environment variables,
* undefined variables.
*
- * Context variables are stored in a GNode.context. The only way to undefine
- * a context variable is using the .undef directive. In particular, it must
+ * Scope variables are stored in a GNode.scope. The only way to undefine
+ * a scope variable is using the .undef directive. In particular, it must
* not be possible to undefine a variable during the evaluation of an
* expression, or Var.name might point nowhere.
*
@@ -204,7 +212,7 @@ typedef enum VarFlags {
typedef struct Var {
/*
* The name of the variable, once set, doesn't change anymore.
- * For context variables, it aliases the corresponding HashEntry name.
+ * For scope variables, it aliases the corresponding HashEntry name.
* For environment and undefined variables, it is allocated.
*/
FStr name;
@@ -231,16 +239,16 @@ typedef enum UnexportWhat {
} UnexportWhat;
/* Flags for pattern matching in the :S and :C modifiers */
-typedef enum VarPatternFlags {
- VARP_NONE = 0,
+typedef struct VarPatternFlags {
+
/* Replace as often as possible ('g') */
- VARP_SUB_GLOBAL = 1 << 0,
+ Boolean subGlobal: 1;
/* Replace only once ('1') */
- VARP_SUB_ONE = 1 << 1,
+ Boolean subOnce: 1;
/* Match at start of word ('^') */
- VARP_ANCHOR_START = 1 << 2,
+ Boolean anchorStart: 1;
/* Match at end of word ('$') */
- VARP_ANCHOR_END = 1 << 3
+ Boolean anchorEnd: 1;
} VarPatternFlags;
/* SepBuf is a string being built from words, interleaved with separators. */
@@ -289,23 +297,29 @@ static char varUndefined[] = "";
static Boolean save_dollars = FALSE;
/*
- * Internally, variables are contained in four different contexts.
- * 1) the environment. They cannot be changed. If an environment
- * variable is appended to, the result is placed in the global
- * context.
- * 2) the global context. Variables set in the makefiles are located
- * here.
- * 3) the command-line context. All variables set on the command line
- * are placed in this context.
- * 4) the local context. Each target has associated with it a context
- * list. On this list are located the structures describing such
- * local variables as $(@) and $(*)
- * The four contexts are searched in the reverse order from which they are
- * listed (but see opts.checkEnvFirst).
+ * A scope collects variable names and their values.
+ *
+ * The main scope is SCOPE_GLOBAL, which contains the variables that are set
+ * in the makefiles. SCOPE_INTERNAL acts as a fallback for SCOPE_GLOBAL and
+ * contains some internal make variables. These internal variables can thus
+ * be overridden, they can also be restored by undefining the overriding
+ * variable.
+ *
+ * SCOPE_CMDLINE contains variables from the command line arguments. These
+ * override variables from SCOPE_GLOBAL.
+ *
+ * There is no scope for environment variables, these are generated on-the-fly
+ * whenever they are referenced. If there were such a scope, each change to
+ * environment variables would have to be reflected in that scope, which may
+ * be simpler or more complex than the current implementation.
+ *
+ * Each target has its own scope, containing the 7 target-local variables
+ * .TARGET, .ALLSRC, etc. No other variables are in these scopes.
*/
-GNode *VAR_INTERNAL; /* variables from make itself */
-GNode *VAR_GLOBAL; /* variables from the makefile */
-GNode *VAR_CMDLINE; /* variables defined on the command-line */
+
+GNode *SCOPE_CMDLINE;
+GNode *SCOPE_GLOBAL;
+GNode *SCOPE_INTERNAL;
ENUM_FLAGS_RTTI_6(VarFlags,
VAR_IN_USE, VAR_FROM_ENV,
@@ -372,18 +386,18 @@ CanonicalVarname(const char *name)
}
static Var *
-GNode_FindVar(GNode *ctxt, const char *varname, unsigned int hash)
+GNode_FindVar(GNode *scope, const char *varname, unsigned int hash)
{
- return HashTable_FindValueHash(&ctxt->vars, varname, hash);
+ return HashTable_FindValueHash(&scope->vars, varname, hash);
}
/*
- * Find the variable in the context, and maybe in other contexts as well.
+ * Find the variable in the scope, and maybe in other scopes as well.
*
* Input:
* name name to find, is not expanded any further
- * ctxt context in which to look first
- * elsewhere TRUE to look in other contexts as well
+ * scope scope in which to look first
+ * elsewhere TRUE to look in other scopes as well
*
* Results:
* The found variable, or NULL if the variable does not exist.
@@ -391,7 +405,7 @@ GNode_FindVar(GNode *ctxt, const char *varname, unsigned int hash)
* VarFreeEnv after use.
*/
static Var *
-VarFind(const char *name, GNode *ctxt, Boolean elsewhere)
+VarFind(const char *name, GNode *scope, Boolean elsewhere)
{
Var *var;
unsigned int nameHash;
@@ -405,23 +419,23 @@ VarFind(const char *name, GNode *ctxt, Boolean elsewhere)
name = CanonicalVarname(name);
nameHash = Hash_Hash(name);
- /* First look for the variable in the given context. */
- var = GNode_FindVar(ctxt, name, nameHash);
+ /* 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 context.
- * Now look for it in the other contexts as well.
+ * The variable was not found in the given scope.
+ * Now look for it in the other scopes as well.
*/
- if (var == NULL && ctxt != VAR_CMDLINE)
- var = GNode_FindVar(VAR_CMDLINE, name, nameHash);
-
- if (!opts.checkEnvFirst && var == NULL && ctxt != VAR_GLOBAL) {
- var = GNode_FindVar(VAR_GLOBAL, name, nameHash);
- if (var == NULL && ctxt != VAR_INTERNAL) {
- /* VAR_INTERNAL is subordinate to VAR_GLOBAL */
- var = GNode_FindVar(VAR_INTERNAL, name, nameHash);
+ if (var == NULL && scope != SCOPE_CMDLINE)
+ var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash);
+
+ if (!opts.checkEnvFirst && var == NULL && scope != SCOPE_GLOBAL) {
+ var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash);
+ if (var == NULL && scope != SCOPE_INTERNAL) {
+ /* SCOPE_INTERNAL is subordinate to SCOPE_GLOBAL */
+ var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash);
}
}
@@ -433,10 +447,10 @@ VarFind(const char *name, GNode *ctxt, Boolean elsewhere)
return VarNew(FStr_InitOwn(varname), env, VAR_FROM_ENV);
}
- if (opts.checkEnvFirst && ctxt != VAR_GLOBAL) {
- var = GNode_FindVar(VAR_GLOBAL, name, nameHash);
- if (var == NULL && ctxt != VAR_INTERNAL)
- var = GNode_FindVar(VAR_INTERNAL, name,
+ if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) {
+ var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash);
+ if (var == NULL && scope != SCOPE_INTERNAL)
+ var = GNode_FindVar(SCOPE_INTERNAL, name,
nameHash);
return var;
}
@@ -464,71 +478,73 @@ VarFreeEnv(Var *v, Boolean freeValue)
return FALSE;
FStr_Done(&v->name);
- Buf_Destroy(&v->val, freeValue);
+ if (freeValue)
+ Buf_Done(&v->val);
+ else
+ Buf_DoneData(&v->val);
free(v);
return TRUE;
}
/*
- * Add a new variable of the given name and value to the given context.
+ * 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 *ctxt, VarSetFlags flags)
+VarAdd(const char *name, const char *val, GNode *scope, VarSetFlags flags)
{
- HashEntry *he = HashTable_CreateEntry(&ctxt->vars, name, NULL);
+ 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);
HashEntry_Set(he, v);
- if (!(ctxt->flags & INTERNAL))
- DEBUG3(VAR, "%s:%s = %s\n", ctxt->name, name, val);
+ DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val);
}
/*
- * Remove a variable from a context, freeing all related memory as well.
+ * Remove a variable from a scope, freeing all related memory as well.
* The variable name is kept as-is, it is not expanded.
*/
void
-Var_DeleteVar(const char *varname, GNode *ctxt)
+Var_Delete(GNode *scope, const char *varname)
{
- HashEntry *he = HashTable_FindEntry(&ctxt->vars, varname);
+ HashEntry *he = HashTable_FindEntry(&scope->vars, varname);
Var *v;
if (he == NULL) {
- DEBUG2(VAR, "%s:delete %s (not found)\n", ctxt->name, varname);
+ DEBUG2(VAR, "%s:delete %s (not found)\n", scope->name, varname);
return;
}
- DEBUG2(VAR, "%s:delete %s\n", ctxt->name, varname);
+ DEBUG2(VAR, "%s:delete %s\n", scope->name, varname);
v = HashEntry_Get(he);
if (v->flags & VAR_EXPORTED)
unsetenv(v->name.str);
if (strcmp(v->name.str, MAKE_EXPORTED) == 0)
var_exportedVars = VAR_EXPORTED_NONE;
assert(v->name.freeIt == NULL);
- HashTable_DeleteEntry(&ctxt->vars, he);
- Buf_Destroy(&v->val, TRUE);
+ HashTable_DeleteEntry(&scope->vars, he);
+ Buf_Done(&v->val);
free(v);
}
/*
- * Remove a variable from a context, freeing all related memory as well.
+ * Remove a variable from a scope, freeing all related memory as well.
* The variable name is expanded once.
*/
void
-Var_Delete(const char *name, GNode *ctxt)
+Var_DeleteExpand(GNode *scope, const char *name)
{
FStr varname = FStr_InitRefer(name);
if (strchr(varname.str, '$') != NULL) {
char *expanded;
- (void)Var_Subst(varname.str, VAR_GLOBAL, VARE_WANTRES,
+ (void)Var_Subst(varname.str, SCOPE_GLOBAL, VARE_WANTRES,
&expanded);
/* TODO: handle errors */
varname = FStr_InitOwn(expanded);
}
- Var_DeleteVar(varname.str, ctxt);
+ Var_Delete(scope, varname.str);
FStr_Done(&varname);
}
@@ -550,7 +566,7 @@ Var_Undef(const char *arg)
return;
}
- vpr = Var_Subst(arg, VAR_GLOBAL, VARE_WANTRES, &expanded);
+ vpr = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES, &expanded);
if (vpr != VPR_OK) {
Parse_Error(PARSE_FATAL,
"Error in variable names to be undefined");
@@ -563,7 +579,7 @@ Var_Undef(const char *arg)
for (i = 0; i < varnames.len; i++) {
const char *varname = varnames.words[i];
- Var_DeleteVar(varname, VAR_GLOBAL);
+ Global_Delete(varname);
}
Words_Free(varnames);
@@ -580,8 +596,8 @@ MayExport(const char *name)
if (name[1] == '\0') {
/*
* A single char.
- * If it is one of the vars that should only appear in
- * local context, skip it, else we can get Var_Subst
+ * If it is one of the variables that should only appear in
+ * local scope, skip it, else we can get Var_Subst
* into a loop.
*/
switch (name[0]) {
@@ -621,7 +637,7 @@ ExportVarEnv(Var *v)
/* XXX: name is injected without escaping it */
expr = str_concat3("${", name, "}");
- (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &val);
+ (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &val);
/* TODO: handle errors */
setenv(name, val, 1);
free(val);
@@ -676,7 +692,7 @@ ExportVar(const char *name, VarExportMode mode)
if (!MayExport(name))
return FALSE;
- v = VarFind(name, VAR_GLOBAL, FALSE);
+ v = VarFind(name, SCOPE_GLOBAL, FALSE);
if (v == NULL)
return FALSE;
@@ -714,7 +730,7 @@ Var_ReexportVars(void)
HashIter hi;
/* Ouch! Exporting all variables at once is crazy... */
- HashIter_Init(&hi, &VAR_GLOBAL->vars);
+ HashIter_Init(&hi, &SCOPE_GLOBAL->vars);
while (HashIter_Next(&hi) != NULL) {
Var *var = hi.entry->value;
ExportVar(var->name.str, VEM_ENV);
@@ -722,7 +738,7 @@ Var_ReexportVars(void)
return;
}
- (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES,
+ (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, VARE_WANTRES,
&xvarnames);
/* TODO: handle errors */
if (xvarnames[0] != '\0') {
@@ -754,7 +770,7 @@ ExportVars(const char *varnames, Boolean isExport, VarExportMode mode)
var_exportedVars = VAR_EXPORTED_SOME;
if (isExport && mode == VEM_PLAIN)
- Var_Append(MAKE_EXPORTED, varname, VAR_GLOBAL);
+ Global_Append(MAKE_EXPORTED, varname);
}
Words_Free(words);
}
@@ -764,7 +780,7 @@ ExportVarsExpand(const char *uvarnames, Boolean isExport, VarExportMode mode)
{
char *xvarnames;
- (void)Var_Subst(uvarnames, VAR_GLOBAL, VARE_WANTRES, &xvarnames);
+ (void)Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES, &xvarnames);
/* TODO: handle errors */
ExportVars(xvarnames, isExport, mode);
free(xvarnames);
@@ -841,7 +857,7 @@ GetVarnamesToUnexport(Boolean isEnv, const char *arg,
if (what != UNEXPORT_NAMED) {
char *expanded;
/* Using .MAKE.EXPORTED */
- (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL,
+ (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL,
VARE_WANTRES, &expanded);
/* TODO: handle errors */
varnames = FStr_InitOwn(expanded);
@@ -854,7 +870,7 @@ GetVarnamesToUnexport(Boolean isEnv, const char *arg,
static void
UnexportVar(const char *varname, UnexportWhat what)
{
- Var *v = VarFind(varname, VAR_GLOBAL, FALSE);
+ Var *v = VarFind(varname, SCOPE_GLOBAL, FALSE);
if (v == NULL) {
DEBUG1(VAR, "Not unexporting \"%s\" (not found)\n", varname);
return;
@@ -872,9 +888,9 @@ UnexportVar(const char *varname, UnexportWhat what)
char *expr = str_concat3("${" MAKE_EXPORTED ":N",
v->name.str, "}");
char *cp;
- (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &cp);
+ (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &cp);
/* TODO: handle errors */
- Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL);
+ Global_Set(MAKE_EXPORTED, cp);
free(cp);
free(expr);
}
@@ -897,7 +913,7 @@ UnexportVars(FStr *varnames, UnexportWhat what)
Words_Free(words);
if (what != UNEXPORT_NAMED)
- Var_Delete(MAKE_EXPORTED, VAR_GLOBAL);
+ Global_Delete(MAKE_EXPORTED);
}
/*
@@ -917,17 +933,24 @@ Var_UnExport(Boolean isEnv, const char *arg)
}
/* Set the variable to the value; the name is not expanded. */
-static void
-SetVar(const char *name, const char *val, GNode *ctxt, VarSetFlags flags)
+void
+Var_SetWithFlags(GNode *scope, const char *name, const char *val,
+ VarSetFlags flags)
{
Var *v;
- if (ctxt == VAR_GLOBAL) {
- v = VarFind(name, VAR_CMDLINE, FALSE);
+ assert(val != NULL);
+ if (name[0] == '\0') {
+ DEBUG0(VAR, "SetVar: variable name is empty - ignored\n");
+ 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",
- ctxt->name, name, val);
+ scope->name, name, val);
return;
}
VarFreeEnv(v, TRUE);
@@ -935,32 +958,32 @@ SetVar(const char *name, const char *val, GNode *ctxt, VarSetFlags flags)
}
/*
- * We only look for a variable in the given context since anything set
- * here will override anything in a lower context, so there's not much
+ * 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...
*/
- v = VarFind(name, ctxt, FALSE);
+ v = VarFind(name, scope, FALSE);
if (v == NULL) {
- if (ctxt == VAR_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) {
+ if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) {
/*
* This var would normally prevent the same name being
- * added to VAR_GLOBAL, so delete it from there if
+ * added to SCOPE_GLOBAL, so delete it from there if
* needed. Otherwise -V name may show the wrong value.
*/
/* XXX: name is expanded for the second time */
- Var_Delete(name, VAR_GLOBAL);
+ Var_DeleteExpand(SCOPE_GLOBAL, name);
}
- VarAdd(name, val, ctxt, flags);
+ VarAdd(name, val, scope, flags);
} else {
if ((v->flags & VAR_READONLY) && !(flags & VAR_SET_READONLY)) {
DEBUG3(VAR, "%s:%s = %s ignored (read-only)\n",
- ctxt->name, name, val);
+ scope->name, name, val);
return;
}
Buf_Empty(&v->val);
Buf_AddStr(&v->val, val);
- DEBUG3(VAR, "%s:%s = %s\n", ctxt->name, name, val);
+ DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val);
if (v->flags & VAR_EXPORTED)
ExportVar(name, VEM_PLAIN);
}
@@ -969,10 +992,10 @@ SetVar(const char *name, const char *val, GNode *ctxt, VarSetFlags flags)
* to the environment (as per POSIX standard)
* Other than internals.
*/
- if (ctxt == VAR_CMDLINE && !(flags & VAR_SET_NO_EXPORT) &&
+ if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) &&
name[0] != '.') {
if (v == NULL)
- v = VarFind(name, ctxt, FALSE); /* we just added it */
+ v = VarFind(name, scope, FALSE); /* we just added it */
v->flags |= VAR_FROM_CMD;
/*
@@ -984,7 +1007,7 @@ SetVar(const char *name, const char *val, GNode *ctxt, VarSetFlags flags)
if (!opts.varNoExportEnv)
setenv(name, val, 1);
- Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL);
+ Global_Append(MAKEOVERRIDES, name);
}
if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0)
save_dollars = ParseBoolean(val, save_dollars);
@@ -995,8 +1018,8 @@ SetVar(const char *name, const char *val, GNode *ctxt, VarSetFlags flags)
/* See Var_Set for documentation. */
void
-Var_SetWithFlags(const char *name, const char *val, GNode *ctxt,
- VarSetFlags flags)
+Var_SetExpandWithFlags(GNode *scope, const char *name, const char *val,
+ VarSetFlags flags)
{
const char *unexpanded_name = name;
FStr varname = FStr_InitRefer(name);
@@ -1005,7 +1028,7 @@ Var_SetWithFlags(const char *name, const char *val, GNode *ctxt,
if (strchr(varname.str, '$') != NULL) {
char *expanded;
- (void)Var_Subst(varname.str, ctxt, VARE_WANTRES, &expanded);
+ (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded);
/* TODO: handle errors */
varname = FStr_InitOwn(expanded);
}
@@ -1015,13 +1038,19 @@ Var_SetWithFlags(const char *name, const char *val, GNode *ctxt,
"name expands to empty string - ignored\n",
unexpanded_name, val);
} else
- SetVar(varname.str, val, ctxt, flags);
+ Var_SetWithFlags(scope, varname.str, val, flags);
FStr_Done(&varname);
}
+void
+Var_Set(GNode *scope, const char *name, const char *val)
+{
+ Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
+}
+
/*
- * Set the variable name to the value val in the given context.
+ * Set the variable name to the value val in the given scope.
*
* If the variable doesn't yet exist, it is created.
* Otherwise the new value overwrites and replaces the old value.
@@ -1029,17 +1058,77 @@ Var_SetWithFlags(const char *name, const char *val, GNode *ctxt,
* Input:
* name name of the variable to set, is expanded once
* val value to give to the variable
- * ctxt context in which to set it
+ * scope scope in which to set it
*/
void
-Var_Set(const char *name, const char *val, GNode *ctxt)
+Var_SetExpand(GNode *scope, const char *name, const char *val)
+{
+ Var_SetExpandWithFlags(scope, name, val, VAR_SET_NONE);
+}
+
+void
+Global_Set(const char *name, const char *value)
+{
+ Var_Set(SCOPE_GLOBAL, name, value);
+}
+
+void
+Global_SetExpand(const char *name, const char *value)
{
- Var_SetWithFlags(name, val, ctxt, VAR_SET_NONE);
+ Var_SetExpand(SCOPE_GLOBAL, name, value);
+}
+
+void
+Global_Delete(const char *name)
+{
+ Var_Delete(SCOPE_GLOBAL, name);
+}
+
+/*
+ * Append the value to the named variable.
+ *
+ * If the variable doesn't exist, it is created. Otherwise a single space
+ * and the given value are appended.
+ */
+void
+Var_Append(GNode *scope, const char *name, const char *val)
+{
+ Var *v;
+
+ v = VarFind(name, scope, scope == SCOPE_GLOBAL);
+
+ if (v == NULL) {
+ Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
+ } else if (v->flags & VAR_READONLY) {
+ DEBUG1(VAR, "Ignoring append to %s since it is read-only\n",
+ name);
+ } else if (scope == SCOPE_CMDLINE || !(v->flags & VAR_FROM_CMD)) {
+ Buf_AddByte(&v->val, ' ');
+ Buf_AddStr(&v->val, val);
+
+ DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, v->val.data);
+
+ if (v->flags & VAR_FROM_ENV) {
+ /*
+ * 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;
+ /*
+ * This is the only place where a variable is
+ * created whose v->name is not the same as
+ * scope->vars->key.
+ */
+ HashTable_Set(&scope->vars, name, v);
+ }
+ }
}
/*
* The variable of the given name has the given value appended to it in the
- * given context.
+ * given scope.
*
* If the variable doesn't exist, it is created. Otherwise the strings are
* concatenated, with a space in between.
@@ -1047,30 +1136,30 @@ Var_Set(const char *name, const char *val, GNode *ctxt)
* Input:
* name name of the variable to modify, is expanded once
* val string to append to it
- * ctxt context in which this should occur
+ * scope scope in which this should occur
*
* Notes:
- * Only if the variable is being sought in the global context is the
+ * Only if the variable is being sought in the global scope is the
* environment searched.
- * XXX: Knows its calling circumstances in that if called with ctxt
- * an actual target, it will only search that context since only
+ * XXX: Knows its calling circumstances in that if called with scope
+ * an actual target, it will only search that scope since only
* a local variable could be being appended to. This is actually
* a big win and must be tolerated.
*/
void
-Var_Append(const char *name, const char *val, GNode *ctxt)
+Var_AppendExpand(GNode *scope, const char *name, const char *val)
{
char *name_freeIt = NULL;
- Var *v;
assert(val != NULL);
if (strchr(name, '$') != NULL) {
const char *unexpanded_name = name;
- (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt);
+ (void)Var_Subst(name, scope, VARE_WANTRES, &name_freeIt);
/* 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);
@@ -1079,77 +1168,61 @@ Var_Append(const char *name, const char *val, GNode *ctxt)
}
}
- v = VarFind(name, ctxt, ctxt == VAR_GLOBAL);
+ Var_Append(scope, name, val);
- if (v == NULL) {
- /* XXX: name is expanded for the second time */
- Var_Set(name, val, ctxt);
- } else if (v->flags & VAR_READONLY) {
- DEBUG1(VAR, "Ignoring append to %s since it is read-only\n",
- name);
- } else if (ctxt == VAR_CMDLINE || !(v->flags & VAR_FROM_CMD)) {
- Buf_AddByte(&v->val, ' ');
- Buf_AddStr(&v->val, val);
+ free(name_freeIt);
+}
- DEBUG3(VAR, "%s:%s = %s\n",
- ctxt->name, name, Buf_GetAll(&v->val, NULL));
+void
+Global_Append(const char *name, const char *value)
+{
+ Var_Append(SCOPE_GLOBAL, name, value);
+}
- if (v->flags & VAR_FROM_ENV) {
- /*
- * If the original variable came from the environment,
- * we have to install it in the global context (we
- * could place it in the environment, but then we
- * should provide a way to export other variables...)
- */
- v->flags &= ~(unsigned)VAR_FROM_ENV;
- /*
- * This is the only place where a variable is
- * created whose v->name is not the same as
- * ctxt->context->key.
- */
- HashTable_Set(&ctxt->vars, name, v);
- }
- }
- free(name_freeIt);
+Boolean
+Var_Exists(GNode *scope, const char *name)
+{
+ Var *v = VarFind(name, scope, TRUE);
+ if (v == NULL)
+ return FALSE;
+
+ (void)VarFreeEnv(v, TRUE);
+ return TRUE;
}
/*
- * See if the given variable exists, in the given context or in other
- * fallback contexts.
+ * See if the given variable exists, in the given scope or in other
+ * fallback scopes.
*
* Input:
* name Variable to find, is expanded once
- * ctxt Context in which to start search
+ * scope Scope in which to start search
*/
Boolean
-Var_Exists(const char *name, GNode *ctxt)
+Var_ExistsExpand(GNode *scope, const char *name)
{
FStr varname = FStr_InitRefer(name);
- Var *v;
+ Boolean exists;
if (strchr(varname.str, '$') != NULL) {
char *expanded;
- (void)Var_Subst(varname.str, ctxt, VARE_WANTRES, &expanded);
+ (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded);
/* TODO: handle errors */
varname = FStr_InitOwn(expanded);
}
- v = VarFind(varname.str, ctxt, TRUE);
+ exists = Var_Exists(scope, varname.str);
FStr_Done(&varname);
- if (v == NULL)
- return FALSE;
-
- (void)VarFreeEnv(v, TRUE);
- return TRUE;
+ return exists;
}
/*
- * Return the unexpanded value of the given variable in the given context,
- * or the usual contexts.
+ * Return the unexpanded value of the given variable in the given scope,
+ * or the usual scopes.
*
* Input:
* name name to find, is not expanded any further
- * ctxt context in which to search for it
+ * scope scope in which to search for it
*
* Results:
* The value if the variable exists, NULL if it doesn't.
@@ -1157,15 +1230,15 @@ Var_Exists(const char *name, GNode *ctxt)
* out_freeIt when the returned value is no longer needed.
*/
FStr
-Var_Value(const char *name, GNode *ctxt)
+Var_Value(GNode *scope, const char *name)
{
- Var *v = VarFind(name, ctxt, TRUE);
+ Var *v = VarFind(name, scope, TRUE);
char *value;
if (v == NULL)
return FStr_InitRefer(NULL);
- value = Buf_GetAll(&v->val, NULL);
+ value = v->val.data;
return VarFreeEnv(v, FALSE)
? FStr_InitOwn(value)
: FStr_InitRefer(value);
@@ -1173,13 +1246,13 @@ Var_Value(const char *name, GNode *ctxt)
/*
* Return the unexpanded variable value from this node, without trying to look
- * up the variable in any other context.
+ * up the variable in any other scope.
*/
const char *
-Var_ValueDirect(const char *name, GNode *ctxt)
+GNode_ValueDirect(GNode *gn, const char *name)
{
- Var *v = VarFind(name, ctxt, FALSE);
- return v != NULL ? Buf_GetAll(&v->val, NULL) : NULL;
+ Var *v = VarFind(name, gn, FALSE);
+ return v != NULL ? v->val.data : NULL;
}
@@ -1222,9 +1295,9 @@ SepBuf_AddStr(SepBuf *buf, const char *str)
}
static char *
-SepBuf_Destroy(SepBuf *buf, Boolean free_buf)
+SepBuf_DoneData(SepBuf *buf)
{
- return Buf_Destroy(&buf->buf, free_buf);
+ return Buf_DoneData(&buf->buf);
}
@@ -1377,7 +1450,7 @@ SysVMatch(const char *word, const char *pattern,
}
struct ModifyWord_SYSVSubstArgs {
- GNode *ctx;
+ GNode *scope;
const char *lhs;
const char *rhs;
};
@@ -1404,7 +1477,7 @@ ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data)
* match, but only if the lhs had a '%' as well.
*/
- (void)Var_Subst(args->rhs, args->ctx, VARE_WANTRES, &rhs_expanded);
+ (void)Var_Subst(args->rhs, args->scope, VARE_WANTRES, &rhs_expanded);
/* TODO: handle errors */
rhs = rhs_expanded;
@@ -1446,15 +1519,15 @@ ModifyWord_Subst(const char *word, SepBuf *buf, void *data)
struct ModifyWord_SubstArgs *args = data;
const char *match;
- if ((args->pflags & VARP_SUB_ONE) && args->matched)
+ if (args->pflags.subOnce && args->matched)
goto nosub;
- if (args->pflags & VARP_ANCHOR_START) {
+ if (args->pflags.anchorStart) {
if (wordLen < args->lhsLen ||
memcmp(word, args->lhs, args->lhsLen) != 0)
goto nosub;
- if ((args->pflags & VARP_ANCHOR_END) && wordLen != args->lhsLen)
+ if ((args->pflags.anchorEnd) && wordLen != args->lhsLen)
goto nosub;
/* :S,^prefix,replacement, or :S,^whole$,replacement, */
@@ -1465,7 +1538,7 @@ ModifyWord_Subst(const char *word, SepBuf *buf, void *data)
return;
}
- if (args->pflags & VARP_ANCHOR_END) {
+ if (args->pflags.anchorEnd) {
const char *start;
if (wordLen < args->lhsLen)
@@ -1492,7 +1565,7 @@ ModifyWord_Subst(const char *word, SepBuf *buf, void *data)
args->matched = TRUE;
wordLen -= (size_t)(match - word) + args->lhsLen;
word += (size_t)(match - word) + args->lhsLen;
- if (wordLen == 0 || !(args->pflags & VARP_SUB_GLOBAL))
+ if (wordLen == 0 || !args->pflags.subGlobal)
break;
}
nosub:
@@ -1533,7 +1606,7 @@ ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data)
int flags = 0;
regmatch_t m[10];
- if ((args->pflags & VARP_SUB_ONE) && args->matched)
+ if (args->pflags.subOnce && args->matched)
goto nosub;
tryagain:
@@ -1581,7 +1654,7 @@ tryagain:
}
wp += m[0].rm_eo;
- if (args->pflags & VARP_SUB_GLOBAL) {
+ if (args->pflags.subGlobal) {
flags |= REG_NOTBOL;
if (m[0].rm_so == 0 && m[0].rm_eo == 0) {
SepBuf_AddBytes(buf, wp, 1);
@@ -1606,7 +1679,7 @@ tryagain:
struct ModifyWord_LoopArgs {
- GNode *ctx;
+ GNode *scope;
char *tvar; /* name of temporary variable */
char *str; /* string to expand */
VarEvalFlags eflags;
@@ -1623,8 +1696,10 @@ ModifyWord_Loop(const char *word, SepBuf *buf, void *data)
return;
args = data;
- Var_SetWithFlags(args->tvar, word, args->ctx, VAR_SET_NO_EXPORT);
- (void)Var_Subst(args->str, args->ctx, args->eflags, &s);
+ /* XXX: The variable name should not be expanded here. */
+ Var_SetExpandWithFlags(args->scope, args->tvar, word,
+ VAR_SET_NO_EXPORT);
+ (void)Var_Subst(args->str, args->scope, args->eflags, &s);
/* TODO: handle errors */
DEBUG4(VAR, "ModifyWord_Loop: "
@@ -1694,7 +1769,7 @@ VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first,
Words_Free(words);
- return SepBuf_Destroy(&buf, FALSE);
+ return SepBuf_DoneData(&buf);
}
@@ -1739,7 +1814,7 @@ ModifyWords(const char *str,
if (oneBigWord) {
SepBuf_Init(&result, sep);
modifyWord(str, &result, modifyWord_args);
- return SepBuf_Destroy(&result, FALSE);
+ return SepBuf_DoneData(&result);
}
SepBuf_Init(&result, sep);
@@ -1751,13 +1826,13 @@ ModifyWords(const char *str,
for (i = 0; i < words.len; i++) {
modifyWord(words.words[i], &result, modifyWord_args);
- if (Buf_Len(&result.buf) > 0)
+ if (result.buf.len > 0)
SepBuf_Sep(&result);
}
Words_Free(words);
- return SepBuf_Destroy(&result, FALSE);
+ return SepBuf_DoneData(&result);
}
@@ -1779,7 +1854,7 @@ Words_JoinFree(Words words)
Words_Free(words);
- return Buf_Destroy(&buf, FALSE);
+ return Buf_DoneData(&buf);
}
/* Remove adjacent duplicate words. */
@@ -1826,7 +1901,7 @@ VarQuote(const char *str, Boolean quoteDollar)
Buf_AddStr(&buf, "\\$");
}
- return Buf_Destroy(&buf, FALSE);
+ return Buf_DoneData(&buf);
}
/*
@@ -1977,21 +2052,24 @@ VarStrftime(const char *fmt, Boolean zulu, time_t tim)
* Some modifiers need to free some memory.
*/
-typedef enum VarExprFlags {
- VEF_NONE = 0,
+typedef enum VarExprStatus {
+ /* The variable expression is based in a regular, defined variable. */
+ VES_NONE,
/* The variable expression is based on an undefined variable. */
- VEF_UNDEF = 0x01,
+ VES_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.
*/
- VEF_DEF = 0x02
-} VarExprFlags;
-
-ENUM_FLAGS_RTTI_2(VarExprFlags,
- VEF_UNDEF, VEF_DEF);
+ VES_DEF
+} VarExprStatus;
+static const char * const VarExprStatus_Name[] = {
+ "none",
+ "VES_UNDEF",
+ "VES_DEF"
+};
typedef struct ApplyModifiersState {
/* '\0' or '{' or '(' */
@@ -1999,7 +2077,7 @@ typedef struct ApplyModifiersState {
/* '\0' or '}' or ')' */
const char endc;
Var *const var;
- GNode *const ctxt;
+ GNode *const scope;
const VarEvalFlags eflags;
/*
* The new value of the expression, after applying the modifier,
@@ -2014,14 +2092,14 @@ typedef struct ApplyModifiersState {
* big word, possibly containing spaces.
*/
Boolean oneBigWord;
- VarExprFlags exprFlags;
+ VarExprStatus exprStatus;
} ApplyModifiersState;
static void
ApplyModifiersState_Define(ApplyModifiersState *st)
{
- if (st->exprFlags & VEF_UNDEF)
- st->exprFlags |= VEF_DEF;
+ if (st->exprStatus == VES_UNDEF)
+ st->exprStatus = VES_DEF;
}
typedef enum ApplyModifierResult {
@@ -2099,7 +2177,7 @@ ParseModifierPartSubst(
if (p[1] == delim) { /* Unescaped $ at end of pattern */
if (out_pflags != NULL)
- *out_pflags |= VARP_ANCHOR_END;
+ out_pflags->anchorEnd = TRUE;
else
Buf_AddByte(&buf, *p);
p++;
@@ -2112,7 +2190,7 @@ ParseModifierPartSubst(
VarEvalFlags nested_eflags =
eflags & ~(unsigned)VARE_KEEP_DOLLAR;
- (void)Var_Parse(&nested_p, st->ctxt, nested_eflags,
+ (void)Var_Parse(&nested_p, st->scope, nested_eflags,
&nested_val);
/* TODO: handle errors */
Buf_AddStr(&buf, nested_val.str);
@@ -2168,11 +2246,11 @@ ParseModifierPartSubst(
return VPR_ERR;
}
- *pp = ++p;
+ *pp = p + 1;
if (out_length != NULL)
- *out_length = Buf_Len(&buf);
+ *out_length = buf.len;
- *out_part = Buf_Destroy(&buf, FALSE);
+ *out_part = Buf_DoneData(&buf);
DEBUG1(VAR, "Modifier part: \"%s\"\n", *out_part);
return VPR_OK;
}
@@ -2291,7 +2369,7 @@ ApplyModifier_Loop(const char **pp, const char *val, ApplyModifiersState *st)
char prev_sep;
VarParseResult res;
- args.ctx = st->ctxt;
+ args.scope = st->scope;
(*pp)++; /* Skip the first '@' */
res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.tvar);
@@ -2316,7 +2394,11 @@ ApplyModifier_Loop(const char **pp, const char *val, ApplyModifiersState *st)
ModifyWords(val, ModifyWord_Loop, &args, st->oneBigWord, st->sep));
st->sep = prev_sep;
/* XXX: Consider restoring the previous variable instead of deleting. */
- Var_Delete(args.tvar, st->ctxt);
+ /*
+ * XXX: The variable name should not be expanded here, see
+ * ModifyWord_Loop.
+ */
+ Var_DeleteExpand(st->scope, args.tvar);
free(args.tvar);
free(args.str);
return AMR_OK;
@@ -2331,7 +2413,7 @@ ApplyModifier_Defined(const char **pp, const char *val, ApplyModifiersState *st)
VarEvalFlags eflags = VARE_NONE;
if (st->eflags & VARE_WANTRES)
- if ((**pp == 'D') == !(st->exprFlags & VEF_UNDEF))
+ if ((**pp == 'D') == (st->exprStatus == VES_NONE))
eflags = st->eflags;
Buf_Init(&buf);
@@ -2357,7 +2439,7 @@ ApplyModifier_Defined(const char **pp, const char *val, ApplyModifiersState *st)
if (*p == '$') {
FStr nested_val;
- (void)Var_Parse(&p, st->ctxt, eflags, &nested_val);
+ (void)Var_Parse(&p, st->scope, eflags, &nested_val);
/* TODO: handle errors */
Buf_AddStr(&buf, nested_val.str);
FStr_Done(&nested_val);
@@ -2373,10 +2455,10 @@ ApplyModifier_Defined(const char **pp, const char *val, ApplyModifiersState *st)
ApplyModifiersState_Define(st);
if (eflags & VARE_WANTRES) {
- st->newVal = FStr_InitOwn(Buf_Destroy(&buf, FALSE));
+ st->newVal = FStr_InitOwn(Buf_DoneData(&buf));
} else {
st->newVal = FStr_InitRefer(val);
- Buf_Destroy(&buf, TRUE);
+ Buf_Done(&buf);
}
return AMR_OK;
}
@@ -2571,7 +2653,7 @@ ApplyModifier_Range(const char **pp, const char *val, ApplyModifiersState *st)
Buf_AddInt(&buf, 1 + (int)i);
}
- st->newVal = FStr_InitOwn(Buf_Destroy(&buf, FALSE));
+ st->newVal = FStr_InitOwn(Buf_DoneData(&buf));
return AMR_OK;
}
@@ -2639,7 +2721,7 @@ ApplyModifier_Match(const char **pp, const char *val, ApplyModifiersState *st)
if (needSubst) {
char *old_pattern = pattern;
- (void)Var_Subst(pattern, st->ctxt, st->eflags, &pattern);
+ (void)Var_Subst(pattern, st->scope, st->eflags, &pattern);
/* TODO: handle errors */
free(old_pattern);
}
@@ -2672,7 +2754,7 @@ ApplyModifier_Subst(const char **pp, const char *val, ApplyModifiersState *st)
*pp += 2;
- args.pflags = VARP_NONE;
+ args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE };
args.matched = FALSE;
/*
@@ -2680,7 +2762,7 @@ ApplyModifier_Subst(const char **pp, const char *val, ApplyModifiersState *st)
* start of the word -- skip over it and flag pattern.
*/
if (**pp == '^') {
- args.pflags |= VARP_ANCHOR_START;
+ args.pflags.anchorStart = TRUE;
(*pp)++;
}
@@ -2700,10 +2782,10 @@ ApplyModifier_Subst(const char **pp, const char *val, ApplyModifiersState *st)
for (;; (*pp)++) {
switch (**pp) {
case 'g':
- args.pflags |= VARP_SUB_GLOBAL;
+ args.pflags.subGlobal = TRUE;
continue;
case '1':
- args.pflags |= VARP_SUB_ONE;
+ args.pflags.subOnce = TRUE;
continue;
case 'W':
oneBigWord = TRUE;
@@ -2751,16 +2833,16 @@ ApplyModifier_Regex(const char **pp, const char *val, ApplyModifiersState *st)
return AMR_CLEANUP;
}
- args.pflags = VARP_NONE;
+ args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE };
args.matched = FALSE;
oneBigWord = st->oneBigWord;
for (;; (*pp)++) {
switch (**pp) {
case 'g':
- args.pflags |= VARP_SUB_GLOBAL;
+ args.pflags.subGlobal = TRUE;
continue;
case '1':
- args.pflags |= VARP_SUB_ONE;
+ args.pflags.subOnce = TRUE;
continue;
case 'W':
oneBigWord = TRUE;
@@ -2996,7 +3078,7 @@ ApplyModifier_Words(const char **pp, const char *val, ApplyModifiersState *st)
/* 3 digits + '\0' is usually enough */
Buf_InitSize(&buf, 4);
Buf_AddInt(&buf, (int)ac);
- st->newVal = FStr_InitOwn(Buf_Destroy(&buf, FALSE));
+ st->newVal = FStr_InitOwn(Buf_DoneData(&buf));
}
goto ok;
}
@@ -3188,7 +3270,7 @@ ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st)
static ApplyModifierResult
ApplyModifier_Assign(const char **pp, ApplyModifiersState *st)
{
- GNode *ctxt;
+ GNode *scope;
char delim;
char *val;
VarParseResult res;
@@ -3208,11 +3290,11 @@ ok:
return AMR_BAD;
}
- ctxt = st->ctxt; /* context where v belongs */
- if (!(st->exprFlags & VEF_UNDEF) && st->ctxt != VAR_GLOBAL) {
- Var *gv = VarFind(st->var->name.str, st->ctxt, FALSE);
+ 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)
- ctxt = VAR_GLOBAL;
+ scope = SCOPE_GLOBAL;
else
VarFreeEnv(gv, TRUE);
}
@@ -3235,10 +3317,11 @@ ok:
(*pp)--;
+ /* XXX: Expanding the variable name at this point sounds wrong. */
if (st->eflags & VARE_WANTRES) {
switch (op[0]) {
case '+':
- Var_Append(st->var->name.str, val, ctxt);
+ Var_AppendExpand(scope, st->var->name.str, val);
break;
case '!': {
const char *errfmt;
@@ -3246,16 +3329,17 @@ ok:
if (errfmt != NULL)
Error(errfmt, val);
else
- Var_Set(st->var->name.str, cmd_output, ctxt);
+ Var_SetExpand(scope,
+ st->var->name.str, cmd_output);
free(cmd_output);
break;
}
case '?':
- if (!(st->exprFlags & VEF_UNDEF))
+ if (st->exprStatus == VES_NONE)
break;
/* FALLTHROUGH */
default:
- Var_Set(st->var->name.str, val, ctxt);
+ Var_SetExpand(scope, st->var->name.str, val);
break;
}
}
@@ -3279,11 +3363,11 @@ ApplyModifier_Remember(const char **pp, const char *val,
if (mod[1] == '=') {
size_t n = strcspn(mod + 2, ":)}");
char *name = bmake_strldup(mod + 2, n);
- Var_Set(name, val, st->ctxt);
+ Var_SetExpand(st->scope, name, val);
free(name);
*pp = mod + 2 + n;
} else {
- Var_Set("_", val, st->ctxt);
+ Var_Set(st->scope, "_", val);
*pp = mod + 1;
}
st->newVal = FStr_InitRefer(val);
@@ -3363,7 +3447,7 @@ ApplyModifier_SysV(const char **pp, const char *val, ApplyModifiersState *st)
if (lhs[0] == '\0' && val[0] == '\0') {
st->newVal = FStr_InitRefer(val); /* special case */
} else {
- struct ModifyWord_SYSVSubstArgs args = { st->ctxt, lhs, rhs };
+ struct ModifyWord_SYSVSubstArgs args = { st->scope, lhs, rhs };
st->newVal = FStr_InitOwn(
ModifyWords(val, ModifyWord_SYSVSubst, &args,
st->oneBigWord, st->sep));
@@ -3402,7 +3486,6 @@ LogBeforeApply(const ApplyModifiersState *st, const char *mod, char endc,
{
char eflags_str[VarEvalFlags_ToStringSize];
char vflags_str[VarFlags_ToStringSize];
- char exprflags_str[VarExprFlags_ToStringSize];
Boolean is_single_char = mod[0] != '\0' &&
(mod[1] == endc || mod[1] == ':');
@@ -3410,13 +3493,9 @@ LogBeforeApply(const ApplyModifiersState *st, const char *mod, char endc,
* 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,
- Enum_FlagsToString(eflags_str, sizeof eflags_str,
- st->eflags, VarEvalFlags_ToStringSpecs),
- Enum_FlagsToString(vflags_str, sizeof vflags_str,
- st->var->flags, VarFlags_ToStringSpecs),
- Enum_FlagsToString(exprflags_str, sizeof exprflags_str,
- st->exprFlags,
- VarExprFlags_ToStringSpecs));
+ VarEvalFlags_ToString(eflags_str, st->eflags),
+ VarFlags_ToString(vflags_str, st->var->flags),
+ VarExprStatus_Name[st->exprStatus]);
}
static void
@@ -3424,20 +3503,15 @@ LogAfterApply(ApplyModifiersState *st, const char *p, const char *mod)
{
char eflags_str[VarEvalFlags_ToStringSize];
char vflags_str[VarFlags_ToStringSize];
- char exprflags_str[VarExprFlags_ToStringSize];
const char *quot = st->newVal.str == var_Error ? "" : "\"";
const char *newVal =
st->newVal.str == var_Error ? "error" : st->newVal.str;
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,
- Enum_FlagsToString(eflags_str, sizeof eflags_str,
- st->eflags, VarEvalFlags_ToStringSpecs),
- Enum_FlagsToString(vflags_str, sizeof vflags_str,
- st->var->flags, VarFlags_ToStringSpecs),
- Enum_FlagsToString(exprflags_str, sizeof exprflags_str,
- st->exprFlags,
- VarExprFlags_ToStringSpecs));
+ VarEvalFlags_ToString(eflags_str, st->eflags),
+ VarFlags_ToString(vflags_str, st->var->flags),
+ VarExprStatus_Name[st->exprStatus]);
}
static ApplyModifierResult
@@ -3507,7 +3581,7 @@ ApplyModifier(const char **pp, const char *val, ApplyModifiersState *st)
}
static FStr ApplyModifiers(const char **, FStr, char, char, Var *,
- VarExprFlags *, GNode *, VarEvalFlags);
+ VarExprStatus *, GNode *, VarEvalFlags);
typedef enum ApplyModifiersIndirectResult {
/* The indirect modifiers have been applied successfully. */
@@ -3541,7 +3615,7 @@ ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp,
const char *p = *pp;
FStr mods;
- (void)Var_Parse(&p, st->ctxt, st->eflags, &mods);
+ (void)Var_Parse(&p, st->scope, st->eflags, &mods);
/* TODO: handle errors */
if (mods.str[0] != '\0' && *p != '\0' && *p != ':' && *p != st->endc) {
@@ -3555,7 +3629,7 @@ 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->exprFlags, st->ctxt, st->eflags);
+ st->var, &st->exprStatus, st->scope, st->eflags);
*inout_value = newVal;
if (newVal.str == var_Error || *modsp != '\0') {
FStr_Done(&mods);
@@ -3651,17 +3725,22 @@ ApplyModifiers(
char startc, /* '(' or '{', or '\0' for indirect modifiers */
char endc, /* ')' or '}', or '\0' for indirect modifiers */
Var *v,
- VarExprFlags *exprFlags,
- GNode *ctxt, /* for looking up and modifying variables */
+ VarExprStatus *exprStatus,
+ GNode *scope, /* for looking up and modifying variables */
VarEvalFlags eflags
)
{
ApplyModifiersState st = {
- startc, endc, v, ctxt, eflags,
+ 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 */
- *exprFlags /* .exprFlags */
+ *exprStatus /* .exprStatus */
};
const char *p;
const char *mod;
@@ -3704,7 +3783,7 @@ ApplyModifiers(
*pp = p;
assert(value.str != NULL); /* Use var_Error or varUndefined instead. */
- *exprFlags = st.exprFlags;
+ *exprStatus = st.exprStatus;
return value;
bad_modifier:
@@ -3715,7 +3794,7 @@ bad_modifier:
cleanup:
*pp = p;
FStr_Done(&value);
- *exprFlags = st.exprFlags;
+ *exprStatus = st.exprStatus;
return FStr_InitRefer(var_Error);
}
@@ -3748,11 +3827,11 @@ VarnameIsDynamic(const char *name, size_t len)
}
static const char *
-UndefinedShortVarValue(char varname, const GNode *ctxt)
+UndefinedShortVarValue(char varname, const GNode *scope)
{
- if (ctxt == VAR_CMDLINE || ctxt == VAR_GLOBAL) {
+ if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL) {
/*
- * If substituting a local variable in a non-local context,
+ * If substituting a local variable in a non-local scope,
* assume it's for dynamic source stuff. We have to handle
* this specially and return the longhand for the variable
* with the dollar sign escaped so it makes it back to the
@@ -3780,7 +3859,7 @@ UndefinedShortVarValue(char varname, const GNode *ctxt)
*/
static char *
ParseVarname(const char **pp, char startc, char endc,
- GNode *ctxt, VarEvalFlags eflags,
+ GNode *scope, VarEvalFlags eflags,
size_t *out_varname_len)
{
Buffer buf;
@@ -3803,7 +3882,7 @@ ParseVarname(const char **pp, char startc, char endc,
/* A variable inside a variable, expand. */
if (*p == '$') {
FStr nested_val;
- (void)Var_Parse(&p, ctxt, eflags, &nested_val);
+ (void)Var_Parse(&p, scope, eflags, &nested_val);
/* TODO: handle errors */
Buf_AddStr(&buf, nested_val.str);
FStr_Done(&nested_val);
@@ -3813,8 +3892,8 @@ ParseVarname(const char **pp, char startc, char endc,
}
}
*pp = p;
- *out_varname_len = Buf_Len(&buf);
- return Buf_Destroy(&buf, FALSE);
+ *out_varname_len = buf.len;
+ return Buf_DoneData(&buf);
}
static VarParseResult
@@ -3851,7 +3930,7 @@ ValidShortVarname(char varname, const char *start)
* Return whether to continue parsing.
*/
static Boolean
-ParseVarnameShort(char startc, const char **pp, GNode *ctxt,
+ParseVarnameShort(char startc, const char **pp, GNode *scope,
VarEvalFlags eflags,
VarParseResult *out_FALSE_res, const char **out_FALSE_val,
Var **out_TRUE_var)
@@ -3876,12 +3955,12 @@ ParseVarnameShort(char startc, const char **pp, GNode *ctxt,
name[0] = startc;
name[1] = '\0';
- v = VarFind(name, ctxt, TRUE);
+ v = VarFind(name, scope, TRUE);
if (v == NULL) {
const char *val;
*pp += 2;
- val = UndefinedShortVarValue(startc, ctxt);
+ val = UndefinedShortVarValue(startc, scope);
if (val == NULL)
val = eflags & VARE_UNDEFERR ? var_Error : varUndefined;
@@ -3914,11 +3993,11 @@ ParseVarnameShort(char startc, const char **pp, GNode *ctxt,
/* Find variables like @F or <D. */
static Var *
-FindLocalLegacyVar(const char *varname, size_t namelen, GNode *ctxt,
+FindLocalLegacyVar(const char *varname, size_t namelen, GNode *scope,
const char **out_extraModifiers)
{
- /* Only resolve these variables if ctxt is a "real" target. */
- if (ctxt == VAR_CMDLINE || ctxt == VAR_GLOBAL)
+ /* Only resolve these variables if scope is a "real" target. */
+ if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL)
return NULL;
if (namelen != 2)
@@ -3930,7 +4009,7 @@ FindLocalLegacyVar(const char *varname, size_t namelen, GNode *ctxt,
{
char name[] = { varname[0], '\0' };
- Var *v = VarFind(name, ctxt, FALSE);
+ Var *v = VarFind(name, scope, FALSE);
if (v != NULL) {
if (varname[1] == 'D') {
@@ -3983,7 +4062,7 @@ static Boolean
ParseVarnameLong(
const char *p,
char startc,
- GNode *ctxt,
+ GNode *scope,
VarEvalFlags eflags,
const char **out_FALSE_pp,
@@ -3996,7 +4075,7 @@ ParseVarnameLong(
Boolean *out_TRUE_haveModifier,
const char **out_TRUE_extraModifiers,
Boolean *out_TRUE_dynamic,
- VarExprFlags *out_TRUE_exprFlags
+ VarExprStatus *out_TRUE_exprStatus
)
{
size_t namelen;
@@ -4009,7 +4088,7 @@ ParseVarnameLong(
char endc = startc == '(' ? ')' : '}';
p += 2; /* skip "${" or "$(" or "y(" */
- varname = ParseVarname(&p, startc, endc, ctxt, eflags, &namelen);
+ varname = ParseVarname(&p, startc, endc, scope, eflags, &namelen);
if (*p == ':') {
haveModifier = TRUE;
@@ -4024,23 +4103,23 @@ ParseVarnameLong(
return FALSE;
}
- v = VarFind(varname, ctxt, TRUE);
+ v = VarFind(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, ctxt,
+ v = FindLocalLegacyVar(varname, namelen, scope,
out_TRUE_extraModifiers);
}
if (v == NULL) {
/*
* Defer expansion of dynamic variables if they appear in
- * non-local context since they are not defined there.
+ * non-local scope since they are not defined there.
*/
dynamic = VarnameIsDynamic(varname, namelen) &&
- (ctxt == VAR_CMDLINE || ctxt == VAR_GLOBAL);
+ (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL);
if (!haveModifier) {
p++; /* skip endc */
@@ -4065,7 +4144,7 @@ ParseVarnameLong(
* instead of the actually computed value.
*/
v = VarNew(FStr_InitOwn(varname), "", VAR_NONE);
- *out_TRUE_exprFlags = VEF_UNDEF;
+ *out_TRUE_exprStatus = VES_UNDEF;
} else
free(varname);
@@ -4081,7 +4160,7 @@ ParseVarnameLong(
static void
FreeEnvVar(void **out_val_freeIt, Var *v, const char *value)
{
- char *varValue = Buf_Destroy(&v->val, FALSE);
+ char *varValue = Buf_DoneData(&v->val);
if (value == varValue)
*out_val_freeIt = varValue;
else
@@ -4103,7 +4182,7 @@ FreeEnvVar(void **out_val_freeIt, Var *v, const char *value)
* When parsing a condition in ParseEmptyArg, it may also
* point to the "y" of "empty(VARNAME:Modifiers)", which
* is syntactically the same.
- * ctxt The context for finding variables
+ * scope The scope for finding variables
* eflags Control the exact details of parsing
*
* Output:
@@ -4131,7 +4210,7 @@ FreeEnvVar(void **out_val_freeIt, Var *v, const char *value)
*/
/* coverity[+alloc : arg-*4] */
VarParseResult
-Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val)
+Var_Parse(const char **pp, GNode *scope, VarEvalFlags eflags, FStr *out_val)
{
const char *p = *pp;
const char *const start = p;
@@ -4143,7 +4222,7 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val)
char endc;
/*
* TRUE if the variable is local and we're expanding it in a
- * non-local context. This is done to support dynamic sources.
+ * non-local scope. This is done to support dynamic sources.
* The result is just the expression, unaltered.
*/
Boolean dynamic;
@@ -4151,11 +4230,10 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val)
Var *v;
FStr value;
char eflags_str[VarEvalFlags_ToStringSize];
- VarExprFlags exprFlags = VEF_NONE;
+ VarExprStatus exprStatus = VES_NONE;
DEBUG2(VAR, "Var_Parse: %s with %s\n", start,
- Enum_FlagsToString(eflags_str, sizeof eflags_str, eflags,
- VarEvalFlags_ToStringSpecs));
+ VarEvalFlags_ToString(eflags_str, eflags));
*out_val = FStr_InitRefer(NULL);
extramodifiers = NULL; /* extra modifiers to apply first */
@@ -4170,17 +4248,17 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val)
startc = p[1];
if (startc != '(' && startc != '{') {
VarParseResult res;
- if (!ParseVarnameShort(startc, pp, ctxt, eflags, &res,
+ if (!ParseVarnameShort(startc, pp, scope, eflags, &res,
&out_val->str, &v))
return res;
haveModifier = FALSE;
p++;
} else {
VarParseResult res;
- if (!ParseVarnameLong(p, startc, ctxt, eflags,
+ if (!ParseVarnameLong(p, startc, scope, eflags,
pp, &res, out_val,
&endc, &p, &v, &haveModifier, &extramodifiers,
- &dynamic, &exprFlags))
+ &dynamic, &exprStatus))
return res;
}
@@ -4196,7 +4274,7 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val)
* the then-current value of the variable. This might also invoke
* undefined behavior.
*/
- value = FStr_InitRefer(Buf_GetAll(&v->val, NULL));
+ value = FStr_InitRefer(v->val.data);
/*
* Before applying any modifiers, expand any nested expressions from
@@ -4208,7 +4286,7 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val)
if (opts.strict)
nested_eflags &= ~(unsigned)VARE_UNDEFERR;
v->flags |= VAR_IN_USE;
- (void)Var_Subst(value.str, ctxt, nested_eflags, &expanded);
+ (void)Var_Subst(value.str, scope, nested_eflags, &expanded);
v->flags &= ~(unsigned)VAR_IN_USE;
/* TODO: handle errors */
value = FStr_InitOwn(expanded);
@@ -4218,14 +4296,14 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val)
if (extramodifiers != NULL) {
const char *em = extramodifiers;
value = ApplyModifiers(&em, value, '\0', '\0',
- v, &exprFlags, ctxt, eflags);
+ v, &exprStatus, scope, eflags);
}
if (haveModifier) {
p++; /* Skip initial colon. */
value = ApplyModifiers(&p, value, startc, endc,
- v, &exprFlags, ctxt, eflags);
+ v, &exprStatus, scope, eflags);
}
}
@@ -4237,8 +4315,8 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val)
if (v->flags & VAR_FROM_ENV) {
FreeEnvVar(&value.freeIt, v, value.str);
- } else if (exprFlags & VEF_UNDEF) {
- if (!(exprFlags & VEF_DEF)) {
+ } else if (exprStatus != VES_NONE) {
+ if (exprStatus != VES_DEF) {
FStr_Done(&value);
if (dynamic) {
value = FStr_InitOwn(bmake_strsedup(start, p));
@@ -4252,8 +4330,8 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val)
? var_Error : varUndefined);
}
}
- if (value.str != Buf_GetAll(&v->val, NULL))
- Buf_Destroy(&v->val, TRUE);
+ if (value.str != v->val.data)
+ Buf_Done(&v->val);
FStr_Done(&v->name);
free(v);
}
@@ -4275,14 +4353,14 @@ VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalFlags eflags)
}
static void
-VarSubstExpr(const char **pp, Buffer *buf, GNode *ctxt,
+VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
VarEvalFlags eflags, Boolean *inout_errorReported)
{
const char *p = *pp;
const char *nested_p = p;
FStr val;
- (void)Var_Parse(&nested_p, ctxt, eflags, &val);
+ (void)Var_Parse(&nested_p, scope, eflags, &val);
/* TODO: handle errors */
if (val.str == var_Error || val.str == varUndefined) {
@@ -4350,12 +4428,12 @@ VarSubstPlain(const char **pp, Buffer *res)
* Input:
* str The string in which the variable expressions are
* expanded.
- * ctxt The context in which to start searching for
- * variables. The other contexts are searched as well.
+ * scope The scope in which to start searching for
+ * variables. The other scopes are searched as well.
* eflags Special effects during expansion.
*/
VarParseResult
-Var_Subst(const char *str, GNode *ctxt, VarEvalFlags eflags, char **out_res)
+Var_Subst(const char *str, GNode *scope, VarEvalFlags eflags, char **out_res)
{
const char *p = str;
Buffer res;
@@ -4372,12 +4450,12 @@ Var_Subst(const char *str, GNode *ctxt, VarEvalFlags eflags, char **out_res)
if (p[0] == '$' && p[1] == '$')
VarSubstDollarDollar(&p, &res, eflags);
else if (p[0] == '$')
- VarSubstExpr(&p, &res, ctxt, eflags, &errorReported);
+ VarSubstExpr(&p, &res, scope, eflags, &errorReported);
else
VarSubstPlain(&p, &res);
}
- *out_res = Buf_DestroyCompact(&res);
+ *out_res = Buf_DoneDataCompact(&res);
return VPR_OK;
}
@@ -4385,9 +4463,9 @@ Var_Subst(const char *str, GNode *ctxt, VarEvalFlags eflags, char **out_res)
void
Var_Init(void)
{
- VAR_INTERNAL = GNode_New("Internal");
- VAR_GLOBAL = GNode_New("Global");
- VAR_CMDLINE = GNode_New("Command");
+ SCOPE_INTERNAL = GNode_New("Internal");
+ SCOPE_GLOBAL = GNode_New("Global");
+ SCOPE_CMDLINE = GNode_New("Command");
}
/* Clean up the variables module. */
@@ -4400,12 +4478,12 @@ Var_End(void)
void
Var_Stats(void)
{
- HashTable_DebugStats(&VAR_GLOBAL->vars, "VAR_GLOBAL");
+ HashTable_DebugStats(&SCOPE_GLOBAL->vars, "Global variables");
}
-/* Print all variables in a context, sorted by name. */
+/* Print all variables in a scope, sorted by name. */
void
-Var_Dump(GNode *ctxt)
+Var_Dump(GNode *scope)
{
Vector /* of const char * */ vec;
HashIter hi;
@@ -4414,7 +4492,7 @@ Var_Dump(GNode *ctxt)
Vector_Init(&vec, sizeof(const char *));
- HashIter_Init(&hi, &ctxt->vars);
+ HashIter_Init(&hi, &scope->vars);
while (HashIter_Next(&hi) != NULL)
*(const char **)Vector_Push(&vec) = hi.entry->key;
varnames = vec.items;
@@ -4423,9 +4501,8 @@ Var_Dump(GNode *ctxt)
for (i = 0; i < vec.len; i++) {
const char *varname = varnames[i];
- Var *var = HashTable_FindValue(&ctxt->vars, varname);
- debug_printf("%-16s = %s\n",
- varname, Buf_GetAll(&var->val, NULL));
+ Var *var = HashTable_FindValue(&scope->vars, varname);
+ debug_printf("%-16s = %s\n", varname, var->val.data);
}
Vector_Done(&vec);