version 1.65, 2007/07/20 12:18:47 |
version 1.66, 2007/07/20 12:32:45 |
|
|
* identical string instances... |
* identical string instances... |
*/ |
*/ |
static char varNoError[] = ""; |
static char varNoError[] = ""; |
bool errorIsOkay; |
bool errorIsOkay; |
static bool checkEnvFirst; /* true if environment should be searched for |
static bool checkEnvFirst; /* true if environment should be searched for |
* variables before the global context */ |
* variables before the global context */ |
|
|
void |
void |
Var_setCheckEnvFirst(bool yes) |
Var_setCheckEnvFirst(bool yes) |
|
|
} |
} |
|
|
/* |
/* |
* Variable values are obtained from four different contexts: |
* The rules for variable look-up are complicated. |
* 1) the process environment. The process environment itself |
* |
* may not be changed, but these variables may be modified, |
* - Dynamic variables like $@ and $* are special. They always pertain to |
* unless make is invoked with -e, in which case those variables |
* a given variable. In this implementation of make, it is an error to |
* are unmodifiable and supersede the global context. |
* try to affect them manually. They are stored in a local symtable directly |
* 2) the global context. Variables set in the Makefile are located in |
* inside the gnode. |
* the global context. It is the penultimate context searched when |
* |
* substituting. |
* Global variables can be obtained: |
* 3) the command-line context. All variables set on the command line |
* - from the command line |
* are placed in this context. They are UNALTERABLE once placed here. |
* - from the environment |
* 4) the local context. Each target has associated with it a context |
* - from the Makefile proper. |
* list. On this list are located the structures describing such |
* All of these are stored in a hash global_variables. |
* local variables as $(@) and $(*) |
* |
* The four contexts are searched in the reverse order from which they are |
* Variables set on the command line override Makefile contents, are |
* listed. |
* passed to submakes (see Var_AddCmdLine), and are also exported to the |
|
* environment. |
|
* |
|
* Without -e (!checkEnvFirst), make will see variables set in the |
|
* Makefile, and default to the environment otherwise. |
|
* |
|
* With -e (checkEnvFirst), make will see the environment first, and that |
|
* will override anything that's set in the Makefile (but not set on |
|
* the command line). |
|
* |
|
* The SHELL variable is very special: it is never obtained from the |
|
* environment, and never passed to the environment. |
*/ |
*/ |
|
|
|
/* definitions pertaining to dynamic variables */ |
|
|
|
/* full names of dynamic variables */ |
static char *varnames[] = { |
static char *varnames[] = { |
TARGET, |
TARGET, |
PREFIX, |
PREFIX, |
ARCHIVE, |
ARCHIVE, |
MEMBER, |
MEMBER, |
OODATE, |
OODATE, |
ALLSRC, |
ALLSRC, |
IMPSRC, |
IMPSRC, |
FTARGET, |
FTARGET, |
DTARGET, |
DTARGET, |
FPREFIX, |
FPREFIX, |
DPREFIX, |
DPREFIX, |
FARCHIVE, |
FARCHIVE, |
DARCHIVE, |
DARCHIVE, |
FMEMBER, |
FMEMBER, |
DMEMBER |
DMEMBER |
}; |
}; |
|
|
/* retrieve the hashed values for well-known variables. */ |
/* hashed names of dynamic variables */ |
#include "varhashconsts.h" |
#include "varhashconsts.h" |
|
|
/* extended indices for System V stuff */ |
/* extended indices for System V stuff */ |
|
|
#define FMEMBER_INDEX 13 |
#define FMEMBER_INDEX 13 |
#define DMEMBER_INDEX 14 |
#define DMEMBER_INDEX 14 |
|
|
|
#define GLOBAL_INDEX -1 |
|
|
#define EXTENDED2SIMPLE(i) (((i)-LOCAL_SIZE)/2) |
#define EXTENDED2SIMPLE(i) (((i)-LOCAL_SIZE)/2) |
#define IS_EXTENDED_F(i) ((i)%2 == 1) |
#define IS_EXTENDED_F(i) ((i)%2 == 1) |
|
|
|
|
static struct ohash global_variables; |
static struct ohash global_variables; |
|
|
|
|
typedef struct Var_ { |
typedef struct Var_ { |
BUFFER val; /* its value */ |
BUFFER val; /* the variable value */ |
unsigned int flags; /* miscellaneous status flags */ |
unsigned int flags; /* miscellaneous status flags */ |
#define VAR_IN_USE 1 /* Variable's value currently being used. */ |
#define VAR_IN_USE 1 /* Variable's value currently being used. */ |
/* Used to avoid recursion */ |
/* (Used to avoid recursion) */ |
#define VAR_DUMMY 2 /* Placeholder: already looked up */ |
#define VAR_DUMMY 2 /* Variable is currently just a name */ |
#define VAR_FROM_CMD 4 /* From the command line */ |
/* In particular: BUFFER is invalid */ |
#define VAR_FROM_ENV 8 /* Read from environment */ |
#define VAR_FROM_CMD 4 /* Special source: command line */ |
#define VAR_SEEN_ENV 16 /* Already seen environment */ |
#define VAR_FROM_ENV 8 /* Special source: environment */ |
#define VAR_SHELL 32 /* magic, see posix */ |
#define VAR_SEEN_ENV 16 /* No need to go look up environment again */ |
|
#define VAR_SHELL 32 /* Magic behavior */ |
|
|
#define POISONS (POISON_NORMAL | POISON_EMPTY | POISON_NOT_DEFINED) |
#define POISONS (POISON_NORMAL | POISON_EMPTY | POISON_NOT_DEFINED) |
char name[1]; /* the variable's name */ |
/* Defined in var.h */ |
|
char name[1]; /* the variable's name */ |
} Var; |
} Var; |
|
|
|
|
static struct ohash_info var_info = { |
static struct ohash_info var_info = { |
offsetof(Var, name), |
offsetof(Var, name), |
NULL, hash_alloc, hash_free, element_alloc }; |
NULL, |
static int quick_lookup(const char *, const char **, uint32_t *); |
hash_alloc, hash_free, element_alloc |
#define VarValue(v) Buf_Retrieve(&((v)->val)) |
}; |
static Var *varfind(const char *, const char *, SymTable *, int, uint32_t); |
|
static Var *find_global_var(const char *, const char *, uint32_t); |
|
static void VarDelete(Var *); |
|
static void VarPrintVar(Var *); |
|
|
|
static Var *obtain_global_var(const char *, const char *, uint32_t); |
static int classify_var(const char *, const char **, uint32_t *); |
|
static Var *find_any_var(const char *, const char *, SymTable *, int, uint32_t); |
|
static Var *find_global_var(const char *, const char *, uint32_t); |
|
static Var *find_global_var_without_env(const char *, const char *, uint32_t); |
static void fill_from_env(Var *); |
static void fill_from_env(Var *); |
static Var *create_var(const char *, const char *); |
static Var *create_var(const char *, const char *); |
|
static void var_set_initial_value(Var *, const char *); |
|
static void var_set_value(Var *, const char *); |
|
#define var_get_value(v) Buf_Retrieve(&((v)->val)) |
|
static void var_append_value(Var *, const char *); |
|
static void poison_check(Var *); |
static void varq_set_append(int, const char *, GNode *, bool); |
static void varq_set_append(int, const char *, GNode *, bool); |
static void var_init_string(Var *, const char *); |
|
static void var_set_string(Var *, const char *); |
|
static void var_append_string(Var *, const char *); |
|
static void var_set_append(const char *, const char *, const char *, int, bool); |
static void var_set_append(const char *, const char *, const char *, int, bool); |
static void set_magic_shell_variable(void); |
static void set_magic_shell_variable(void); |
static void poison_check(Var *); |
|
static const char *find_0(const char *); |
static void delete_var(Var *); |
|
static void print_var(Var *); |
|
|
|
|
static const char *find_rparen(const char *); |
static const char *find_rparen(const char *); |
static const char *find_ket(const char *); |
static const char *find_ket(const char *); |
typedef const char * (*find_t)(const char *); |
typedef const char * (*find_t)(const char *); |
static find_t find_pos(int); |
static find_t find_pos(int); |
|
|
|
|
|
|
|
/* Variable lookup function: return idx for dynamic variable, or |
|
* GLOBAL_INDEX if name is not dynamic. Set up *pk for further use. |
|
*/ |
static int |
static int |
quick_lookup(const char *name, const char **enamePtr, uint32_t *pk) |
classify_var(const char *name, const char **enamePtr, uint32_t *pk) |
{ |
{ |
size_t len; |
size_t len; |
|
|
*pk = ohash_interval(name, enamePtr); |
*pk = ohash_interval(name, enamePtr); |
len = *enamePtr - name; |
len = *enamePtr - name; |
/* substitute short version for long local name */ |
/* substitute short version for long local name */ |
switch (*pk % MAGICSLOTS1) { /* MAGICSLOTS should be the */ |
switch (*pk % MAGICSLOTS1) { /* MAGICSLOTS should be the */ |
case K_LONGALLSRC % MAGICSLOTS1: /* smallest constant yielding */ |
case K_LONGALLSRC % MAGICSLOTS1:/* smallest constant yielding */ |
/* distinct case values */ |
/* distinct case values */ |
if (*pk == K_LONGALLSRC && len == strlen(LONGALLSRC) && |
if (*pk == K_LONGALLSRC && len == strlen(LONGALLSRC) && |
strncmp(name, LONGALLSRC, len) == 0) |
strncmp(name, LONGALLSRC, len) == 0) |
return ALLSRC_INDEX; |
return ALLSRC_INDEX; |
break; |
break; |
case K_LONGARCHIVE % MAGICSLOTS1: |
case K_LONGARCHIVE % MAGICSLOTS1: |
if (*pk == K_LONGARCHIVE && len == strlen(LONGARCHIVE) && |
if (*pk == K_LONGARCHIVE && len == strlen(LONGARCHIVE) && |
strncmp(name, LONGARCHIVE, len) == 0) |
strncmp(name, LONGARCHIVE, len) == 0) |
return ARCHIVE_INDEX; |
return ARCHIVE_INDEX; |
break; |
break; |
case K_LONGIMPSRC % MAGICSLOTS1: |
case K_LONGIMPSRC % MAGICSLOTS1: |
if (*pk == K_LONGIMPSRC && len == strlen(LONGIMPSRC) && |
if (*pk == K_LONGIMPSRC && len == strlen(LONGIMPSRC) && |
strncmp(name, LONGIMPSRC, len) == 0) |
strncmp(name, LONGIMPSRC, len) == 0) |
return IMPSRC_INDEX; |
return IMPSRC_INDEX; |
break; |
break; |
case K_LONGMEMBER % MAGICSLOTS1: |
case K_LONGMEMBER % MAGICSLOTS1: |
if (*pk == K_LONGMEMBER && len == strlen(LONGMEMBER) && |
if (*pk == K_LONGMEMBER && len == strlen(LONGMEMBER) && |
strncmp(name, LONGMEMBER, len) == 0) |
strncmp(name, LONGMEMBER, len) == 0) |
return MEMBER_INDEX; |
return MEMBER_INDEX; |
break; |
break; |
case K_LONGOODATE % MAGICSLOTS1: |
case K_LONGOODATE % MAGICSLOTS1: |
if (*pk == K_LONGOODATE && len == strlen(LONGOODATE) && |
if (*pk == K_LONGOODATE && len == strlen(LONGOODATE) && |
strncmp(name, LONGOODATE, len) == 0) |
strncmp(name, LONGOODATE, len) == 0) |
return OODATE_INDEX; |
return OODATE_INDEX; |
break; |
break; |
case K_LONGPREFIX % MAGICSLOTS1: |
case K_LONGPREFIX % MAGICSLOTS1: |
if (*pk == K_LONGPREFIX && len == strlen(LONGPREFIX) && |
if (*pk == K_LONGPREFIX && len == strlen(LONGPREFIX) && |
strncmp(name, LONGPREFIX, len) == 0) |
strncmp(name, LONGPREFIX, len) == 0) |
return PREFIX_INDEX; |
return PREFIX_INDEX; |
break; |
break; |
case K_LONGTARGET % MAGICSLOTS1: |
case K_LONGTARGET % MAGICSLOTS1: |
if (*pk == K_LONGTARGET && len == strlen(LONGTARGET) && |
if (*pk == K_LONGTARGET && len == strlen(LONGTARGET) && |
strncmp(name, LONGTARGET, len) == 0) |
strncmp(name, LONGTARGET, len) == 0) |
return TARGET_INDEX; |
return TARGET_INDEX; |
break; |
break; |
case K_TARGET % MAGICSLOTS1: |
case K_TARGET % MAGICSLOTS1: |
if (name[0] == TARGET[0] && len == 1) |
if (name[0] == TARGET[0] && len == 1) |
return TARGET_INDEX; |
return TARGET_INDEX; |
break; |
break; |
case K_OODATE % MAGICSLOTS1: |
case K_OODATE % MAGICSLOTS1: |
if (name[0] == OODATE[0] && len == 1) |
if (name[0] == OODATE[0] && len == 1) |
return OODATE_INDEX; |
return OODATE_INDEX; |
break; |
break; |
case K_ALLSRC % MAGICSLOTS1: |
case K_ALLSRC % MAGICSLOTS1: |
if (name[0] == ALLSRC[0] && len == 1) |
if (name[0] == ALLSRC[0] && len == 1) |
return ALLSRC_INDEX; |
return ALLSRC_INDEX; |
break; |
break; |
case K_IMPSRC % MAGICSLOTS1: |
case K_IMPSRC % MAGICSLOTS1: |
if (name[0] == IMPSRC[0] && len == 1) |
if (name[0] == IMPSRC[0] && len == 1) |
return IMPSRC_INDEX; |
return IMPSRC_INDEX; |
break; |
break; |
case K_PREFIX % MAGICSLOTS1: |
case K_PREFIX % MAGICSLOTS1: |
if (name[0] == PREFIX[0] && len == 1) |
if (name[0] == PREFIX[0] && len == 1) |
return PREFIX_INDEX; |
return PREFIX_INDEX; |
break; |
break; |
case K_ARCHIVE % MAGICSLOTS1: |
case K_ARCHIVE % MAGICSLOTS1: |
if (name[0] == ARCHIVE[0] && len == 1) |
if (name[0] == ARCHIVE[0] && len == 1) |
return ARCHIVE_INDEX; |
return ARCHIVE_INDEX; |
break; |
break; |
case K_MEMBER % MAGICSLOTS1: |
case K_MEMBER % MAGICSLOTS1: |
if (name[0] == MEMBER[0] && len == 1) |
if (name[0] == MEMBER[0] && len == 1) |
return MEMBER_INDEX; |
return MEMBER_INDEX; |
break; |
break; |
case K_FTARGET % MAGICSLOTS1: |
case K_FTARGET % MAGICSLOTS1: |
if (name[0] == FTARGET[0] && name[1] == FTARGET[1] && len == 2) |
if (name[0] == FTARGET[0] && name[1] == FTARGET[1] && len == 2) |
return FTARGET_INDEX; |
return FTARGET_INDEX; |
break; |
break; |
case K_DTARGET % MAGICSLOTS1: |
case K_DTARGET % MAGICSLOTS1: |
if (name[0] == DTARGET[0] && name[1] == DTARGET[1] && len == 2) |
if (name[0] == DTARGET[0] && name[1] == DTARGET[1] && len == 2) |
return DTARGET_INDEX; |
return DTARGET_INDEX; |
break; |
break; |
case K_FPREFIX % MAGICSLOTS1: |
case K_FPREFIX % MAGICSLOTS1: |
if (name[0] == FPREFIX[0] && name[1] == FPREFIX[1] && len == 2) |
if (name[0] == FPREFIX[0] && name[1] == FPREFIX[1] && len == 2) |
return FPREFIX_INDEX; |
return FPREFIX_INDEX; |
break; |
break; |
case K_DPREFIX % MAGICSLOTS1: |
case K_DPREFIX % MAGICSLOTS1: |
if (name[0] == DPREFIX[0] && name[1] == DPREFIX[1] && len == 2) |
if (name[0] == DPREFIX[0] && name[1] == DPREFIX[1] && len == 2) |
return DPREFIX_INDEX; |
return DPREFIX_INDEX; |
break; |
break; |
case K_FARCHIVE % MAGICSLOTS1: |
case K_FARCHIVE % MAGICSLOTS1: |
if (name[0] == FARCHIVE[0] && name[1] == FARCHIVE[1] && len == 2) |
if (name[0] == FARCHIVE[0] && name[1] == FARCHIVE[1] && |
return FARCHIVE_INDEX; |
len == 2) |
break; |
return FARCHIVE_INDEX; |
case K_DARCHIVE % MAGICSLOTS1: |
break; |
if (name[0] == DARCHIVE[0] && name[1] == DARCHIVE[1] && len == 2) |
case K_DARCHIVE % MAGICSLOTS1: |
return DARCHIVE_INDEX; |
if (name[0] == DARCHIVE[0] && name[1] == DARCHIVE[1] && |
break; |
len == 2) |
case K_FMEMBER % MAGICSLOTS1: |
return DARCHIVE_INDEX; |
if (name[0] == FMEMBER[0] && name[1] == FMEMBER[1] && len == 2) |
break; |
return FMEMBER_INDEX; |
case K_FMEMBER % MAGICSLOTS1: |
break; |
if (name[0] == FMEMBER[0] && name[1] == FMEMBER[1] && len == 2) |
case K_DMEMBER % MAGICSLOTS1: |
return FMEMBER_INDEX; |
if (name[0] == DMEMBER[0] && name[1] == DMEMBER[1] && len == 2) |
break; |
return DMEMBER_INDEX; |
case K_DMEMBER % MAGICSLOTS1: |
break; |
if (name[0] == DMEMBER[0] && name[1] == DMEMBER[1] && len == 2) |
default: |
return DMEMBER_INDEX; |
break; |
break; |
} |
default: |
return -1; |
break; |
|
} |
|
return GLOBAL_INDEX; |
} |
} |
|
|
|
|
|
/*** |
|
*** Internal handling of variables. |
|
***/ |
|
|
|
|
|
/* Create a new variable, does not initialize anything except the name. |
|
* in particular, buffer is invalid, and flag value is invalid. Accordingly, |
|
* must either: |
|
* - set flags to VAR_DUMMY |
|
* - set flags to !VAR_DUMMY, and initialize buffer, for instance with |
|
* var_set_initial_value(). |
|
*/ |
static Var * |
static Var * |
create_var(const char *name, const char *ename) |
create_var(const char *name, const char *ename) |
{ |
{ |
return ohash_create_entry(&var_info, name, &ename); |
return ohash_create_entry(&var_info, name, &ename); |
} |
} |
|
|
/* Set the initial value a var should have */ |
/* Initial version of var_set_value(), to be called after create_var(). |
|
*/ |
static void |
static void |
var_init_string(Var *v, const char *val) |
var_set_initial_value(Var *v, const char *val) |
{ |
{ |
size_t len; |
size_t len; |
|
|
len = strlen(val); |
len = strlen(val); |
Buf_Init(&(v->val), len+1); |
Buf_Init(&(v->val), len+1); |
Buf_AddChars(&(v->val), len, val); |
Buf_AddChars(&(v->val), len, val); |
} |
} |
|
|
|
/* Normal version of var_set_value(), to be called after variable is fully |
|
* initialized. |
|
*/ |
static void |
static void |
var_set_string(Var *v, const char *val) |
var_set_value(Var *v, const char *val) |
{ |
{ |
if ((v->flags & VAR_DUMMY) == 0) { |
if ((v->flags & VAR_DUMMY) == 0) { |
Buf_Reset(&(v->val)); |
Buf_Reset(&(v->val)); |
Buf_AddString(&(v->val), val); |
Buf_AddString(&(v->val), val); |
} else { |
} else { |
var_init_string(v, val); |
var_set_initial_value(v, val); |
v->flags &= ~VAR_DUMMY; |
v->flags &= ~VAR_DUMMY; |
} |
} |
} |
} |
|
|
|
/* Add to a variable, insert a separating space if the variable was already |
|
* defined. |
|
*/ |
static void |
static void |
var_append_string(Var *v, const char *val) |
var_append_value(Var *v, const char *val) |
{ |
{ |
if ((v->flags & VAR_DUMMY) == 0) { |
if ((v->flags & VAR_DUMMY) == 0) { |
Buf_AddSpace(&(v->val)); |
Buf_AddSpace(&(v->val)); |
Buf_AddString(&(v->val), val); |
Buf_AddString(&(v->val), val); |
} else { |
} else { |
var_init_string(v, val); |
var_set_initial_value(v, val); |
v->flags &= ~VAR_DUMMY; |
v->flags &= ~VAR_DUMMY; |
} |
} |
} |
} |
|
|
/*- |
|
*----------------------------------------------------------------------- |
/* Delete a variable and all the space associated with it. |
* VarDelete -- |
|
* Delete a variable and all the space associated with it. |
|
*----------------------------------------------------------------------- |
|
*/ |
*/ |
static void |
static void |
VarDelete(Var *v) |
delete_var(Var *v) |
{ |
{ |
if ((v->flags & VAR_DUMMY) == 0) |
if ((v->flags & VAR_DUMMY) == 0) |
Buf_Destroy(&(v->val)); |
Buf_Destroy(&(v->val)); |
free(v); |
free(v); |
} |
} |
|
|
|
|
|
|
|
|
|
/*** |
|
*** Dynamic variable handling. |
|
***/ |
|
|
|
|
|
|
|
/* create empty symtable. |
|
* XXX: to save space, dynamic variables may be NULL pointers. |
|
*/ |
void |
void |
SymTable_Init(SymTable *ctxt) |
SymTable_Init(SymTable *ctxt) |
{ |
{ |
static SymTable sym_template; |
static SymTable sym_template; |
memcpy(ctxt, &sym_template, sizeof(*ctxt)); |
memcpy(ctxt, &sym_template, sizeof(*ctxt)); |
} |
} |
|
|
|
/* free symtable. |
|
*/ |
#ifdef CLEANUP |
#ifdef CLEANUP |
void |
void |
SymTable_Destroy(SymTable *ctxt) |
SymTable_Destroy(SymTable *ctxt) |
{ |
{ |
int i; |
int i; |
|
|
for (i = 0; i < LOCAL_SIZE; i++) |
for (i = 0; i < LOCAL_SIZE; i++) |
if (ctxt->locals[i] != NULL) |
if (ctxt->locals[i] != NULL) |
VarDelete(ctxt->locals[i]); |
delete_var(ctxt->locals[i]); |
} |
} |
#endif |
#endif |
|
|
|
/* set or append to dynamic variable. |
|
*/ |
static void |
static void |
varq_set_append(int idx, const char *val, GNode *gn, bool append) |
varq_set_append(int idx, const char *val, GNode *gn, bool append) |
{ |
{ |
Var *v = gn->context.locals[idx]; |
Var *v = gn->context.locals[idx]; |
|
|
if (v == NULL) { |
if (v == NULL) { |
v = create_var(varnames[idx], NULL); |
v = create_var(varnames[idx], NULL); |
#ifdef STATS_VAR_LOOKUP |
#ifdef STATS_VAR_LOOKUP |
STAT_VAR_CREATION++; |
STAT_VAR_CREATION++; |
#endif |
#endif |
if (val != NULL) |
if (val != NULL) |
var_init_string(v, val); |
var_set_initial_value(v, val); |
else |
else |
Buf_Init(&(v->val), 1); |
Buf_Init(&(v->val), 1); |
v->flags = 0; |
v->flags = 0; |
gn->context.locals[idx] = v; |
gn->context.locals[idx] = v; |
} else { |
} else { |
if (append) |
if (append) |
Buf_AddSpace(&(v->val)); |
Buf_AddSpace(&(v->val)); |
else |
else |
Buf_Reset(&(v->val)); |
Buf_Reset(&(v->val)); |
Buf_AddString(&(v->val), val); |
Buf_AddString(&(v->val), val); |
} |
} |
if (DEBUG(VAR)) |
if (DEBUG(VAR)) |
printf("%s:%s = %s\n", gn->name, varnames[idx], VarValue(v)); |
printf("%s:%s = %s\n", gn->name, varnames[idx], |
|
var_get_value(v)); |
} |
} |
|
|
void |
void |
Varq_Set(int idx, const char *val, GNode *gn) |
Varq_Set(int idx, const char *val, GNode *gn) |
{ |
{ |
varq_set_append(idx, val, gn, false); |
varq_set_append(idx, val, gn, false); |
} |
} |
|
|
void |
void |
Varq_Append(int idx, const char *val, GNode *gn) |
Varq_Append(int idx, const char *val, GNode *gn) |
{ |
{ |
varq_set_append(idx, val, gn, true); |
varq_set_append(idx, val, gn, true); |
} |
} |
|
|
char * |
char * |
Varq_Value(int idx, GNode *gn) |
Varq_Value(int idx, GNode *gn) |
{ |
{ |
Var *v = gn->context.locals[idx]; |
Var *v = gn->context.locals[idx]; |
|
|
if (v == NULL) |
if (v == NULL) |
return NULL; |
return NULL; |
else |
else |
return VarValue(v); |
return var_get_value(v); |
} |
} |
|
|
|
/*** |
|
*** Global variable handling. |
|
***/ |
|
|
|
/* Create a new global var if necessary, and set it up correctly. |
|
* Do not take environment into account. |
|
*/ |
static Var * |
static Var * |
obtain_global_var(const char *name, const char *ename, uint32_t k) |
find_global_var_without_env(const char *name, const char *ename, uint32_t k) |
{ |
{ |
unsigned int slot; |
unsigned int slot; |
Var *v; |
Var *v; |
|
|
return v; |
return v; |
} |
} |
|
|
|
/* Helper for find_global_var(): grab environment value if needed. |
|
*/ |
static void |
static void |
fill_from_env(Var *v) |
fill_from_env(Var *v) |
{ |
{ |
char *env; |
char *env; |
|
|
env = getenv(v->name); |
env = getenv(v->name); |
if (env == NULL) |
if (env == NULL) |
v->flags |= VAR_SEEN_ENV; |
v->flags |= VAR_SEEN_ENV; |
else { |
else { |
var_set_string(v, env); |
var_set_value(v, env); |
v->flags |= VAR_FROM_ENV | VAR_SEEN_ENV; |
v->flags |= VAR_FROM_ENV | VAR_SEEN_ENV; |
} |
} |
|
|
#ifdef STATS_VAR_LOOKUP |
#ifdef STATS_VAR_LOOKUP |
STAT_VAR_FROM_ENV++; |
STAT_VAR_FROM_ENV++; |
#endif |
#endif |
} |
} |
|
|
|
/* Find global var, and obtain its value from the environment if needed. |
|
*/ |
static Var * |
static Var * |
find_global_var(const char *name, const char *ename, uint32_t k) |
find_global_var(const char *name, const char *ename, uint32_t k) |
{ |
{ |
Var *v; |
Var *v; |
|
|
v = obtain_global_var(name, ename, k); |
v = find_global_var_without_env(name, ename, k); |
|
|
if ((v->flags & VAR_SEEN_ENV) == 0 && |
if ((v->flags & VAR_SEEN_ENV) == 0 && |
(checkEnvFirst && (v->flags & VAR_FROM_CMD) == 0 || |
(checkEnvFirst && (v->flags & VAR_FROM_CMD) == 0 || |
(v->flags & VAR_DUMMY) != 0)) |
(v->flags & VAR_DUMMY) != 0)) |
fill_from_env(v); |
fill_from_env(v); |
|
|
return v; |
return v; |
} |
} |
|
|
|
/* mark variable as poisoned, in a given setup. |
|
*/ |
void |
void |
Var_MarkPoisoned(const char *name, const char *ename, unsigned int type) |
Var_MarkPoisoned(const char *name, const char *ename, unsigned int type) |
{ |
{ |
Var *v; |
Var *v; |
uint32_t k; |
uint32_t k; |
int idx; |
int idx; |
idx = quick_lookup(name, &ename, &k); |
idx = classify_var(name, &ename, &k); |
|
|
if (idx != -1) { |
if (idx != GLOBAL_INDEX) { |
Parse_Error(PARSE_FATAL, |
Parse_Error(PARSE_FATAL, |
"Trying to poison dynamic variable $%s", |
"Trying to poison dynamic variable $%s", |
varnames[idx]); |
varnames[idx]); |
return; |
return; |
|
|
|
|
v = find_global_var(name, ename, k); |
v = find_global_var(name, ename, k); |
v->flags |= type; |
v->flags |= type; |
|
/* POISON_NORMAL is not lazy: if the variable already exists in |
|
* the Makefile, then it's a mistake. |
|
*/ |
if (v->flags & POISON_NORMAL) { |
if (v->flags & POISON_NORMAL) { |
if (v->flags & VAR_DUMMY) |
if (v->flags & VAR_DUMMY) |
return; |
return; |
|
|
} |
} |
} |
} |
|
|
|
/* Check if there's any reason not to use the variable in this context. |
|
*/ |
static void |
static void |
poison_check(Var *v) |
poison_check(Var *v) |
{ |
{ |
if (v->flags & POISON_NORMAL) { |
if (v->flags & POISON_NORMAL) { |
Parse_Error(PARSE_FATAL, |
Parse_Error(PARSE_FATAL, |
"Poisoned variable %s has been referenced\n", v->name); |
"Poisoned variable %s has been referenced\n", v->name); |
return; |
return; |
} |
} |
|
|
return; |
return; |
} |
} |
if (v->flags & POISON_EMPTY) |
if (v->flags & POISON_EMPTY) |
if (strcmp(VarValue(v), "") == 0) |
if (strcmp(var_get_value(v), "") == 0) |
Parse_Error(PARSE_FATAL, |
Parse_Error(PARSE_FATAL, |
"Poisoned variable %s is empty\n", v->name); |
"Poisoned variable %s is empty\n", v->name); |
} |
} |
|
|
|
/* Delete global variable. |
|
*/ |
void |
void |
Var_Delete(const char *name) |
Var_Deletei(const char *name, const char *ename) |
{ |
{ |
Var *v; |
Var *v; |
uint32_t k; |
uint32_t k; |
unsigned int slot; |
unsigned int slot; |
const char *ename = NULL; |
int idx; |
int idx; |
|
|
|
|
idx = classify_var(name, &ename, &k); |
if (DEBUG(VAR)) |
if (idx != GLOBAL_INDEX) { |
printf("delete %s\n", name); |
Parse_Error(PARSE_FATAL, |
|
"Trying to delete dynamic variable $%s", varnames[idx]); |
idx = quick_lookup(name, &ename, &k); |
return; |
if (idx != -1) |
} |
Parse_Error(PARSE_FATAL, "Trying to delete dynamic variable"); |
|
slot = ohash_lookup_interval(&global_variables, name, ename, k); |
slot = ohash_lookup_interval(&global_variables, name, ename, k); |
v = ohash_find(&global_variables, slot); |
v = ohash_find(&global_variables, slot); |
|
|
if (v == NULL) |
if (v == NULL) |
return; |
return; |
|
|
if (checkEnvFirst && (v->flags & VAR_FROM_ENV)) |
if (checkEnvFirst && (v->flags & VAR_FROM_ENV)) |
return; |
return; |
|
|
|
|
return; |
return; |
|
|
ohash_remove(&global_variables, slot); |
ohash_remove(&global_variables, slot); |
VarDelete(v); |
delete_var(v); |
} |
} |
|
|
|
/* Set or add a global variable, in VAR_CMD or VAR_GLOBAL context. |
|
*/ |
static void |
static void |
var_set_append(const char *name, const char *ename, const char *val, int ctxt, |
var_set_append(const char *name, const char *ename, const char *val, int ctxt, |
bool append) |
bool append) |
{ |
{ |
Var *v; |
Var *v; |
uint32_t k; |
uint32_t k; |
int idx; |
int idx; |
|
|
idx = quick_lookup(name, &ename, &k); |
idx = classify_var(name, &ename, &k); |
if (idx != -1) { |
if (idx != GLOBAL_INDEX) { |
Parse_Error(PARSE_FATAL, "Trying to %s dynamic variable $%s", |
Parse_Error(PARSE_FATAL, "Trying to %s dynamic variable $%s", |
append ? "append to" : "set", varnames[idx]); |
append ? "append to" : "set", varnames[idx]); |
return; |
return; |
|
|
Parse_Error(PARSE_FATAL, "Trying to %s poisoned variable %s\n", |
Parse_Error(PARSE_FATAL, "Trying to %s poisoned variable %s\n", |
append ? "append to" : "set", v->name); |
append ? "append to" : "set", v->name); |
/* so can we write to it ? */ |
/* so can we write to it ? */ |
if (ctxt == VAR_CMD) { /* always for command line */ |
if (ctxt == VAR_CMD) { /* always for command line */ |
(append ? var_append_string : var_set_string)(v, val); |
(append ? var_append_value : var_set_value)(v, val); |
v->flags |= VAR_FROM_CMD; |
v->flags |= VAR_FROM_CMD; |
if ((v->flags & VAR_SHELL) == 0) { |
if ((v->flags & VAR_SHELL) == 0) { |
/* Any variables given on the command line are |
/* Any variables given on the command line are |
* automatically exported to the environment, |
* automatically exported to the environment, |
* except for SHELL (as per POSIX standard). |
* except for SHELL (as per POSIX standard). |
*/ |
*/ |
esetenv(v->name, val); |
esetenv(v->name, val); |
} |
} |
if (DEBUG(VAR)) |
if (DEBUG(VAR)) |
printf("command:%s = %s\n", v->name, VarValue(v)); |
printf("command:%s = %s\n", v->name, var_get_value(v)); |
} else if ((v->flags & VAR_FROM_CMD) == 0 && |
} else if ((v->flags & VAR_FROM_CMD) == 0 && |
(!checkEnvFirst || (v->flags & VAR_FROM_ENV) == 0)) { |
(!checkEnvFirst || (v->flags & VAR_FROM_ENV) == 0)) { |
(append ? var_append_string : var_set_string)(v, val); |
(append ? var_append_value : var_set_value)(v, val); |
if (DEBUG(VAR)) |
if (DEBUG(VAR)) |
printf("global:%s = %s\n", v->name, VarValue(v)); |
printf("global:%s = %s\n", v->name, var_get_value(v)); |
} else if (DEBUG(VAR)) |
} else if (DEBUG(VAR)) |
printf("overriden:%s = %s\n", v->name, VarValue(v)); |
printf("overriden:%s = %s\n", v->name, var_get_value(v)); |
} |
} |
|
|
void |
void |
|
|
var_set_append(name, ename, val, ctxt, true); |
var_set_append(name, ename, val, ctxt, true); |
} |
} |
|
|
|
/* XXX different semantics for Var_Valuei() and Var_Definedi(): |
|
* references to poisoned value variables will error out in Var_Valuei(), |
|
* but not in Var_Definedi(), so the following construct works: |
|
* .poison BINDIR |
|
* BINDIR ?= /usr/bin |
|
*/ |
char * |
char * |
Var_Valuei(const char *name, const char *ename) |
Var_Valuei(const char *name, const char *ename) |
{ |
{ |
Var *v; |
Var *v; |
uint32_t k; |
uint32_t k; |
int idx; |
int idx; |
|
|
idx = quick_lookup(name, &ename, &k); |
idx = classify_var(name, &ename, &k); |
if (idx == -1) { |
if (idx != GLOBAL_INDEX) { |
v = find_global_var(name, ename, k); |
Parse_Error(PARSE_FATAL, |
if (v->flags & POISONS) |
"Trying to get value of dynamic variable $%s", |
poison_check(v); |
varnames[idx]); |
if ((v->flags & VAR_DUMMY) == 0) |
return NULL; |
return VarValue(v); |
|
} |
} |
|
v = find_global_var(name, ename, k); |
return NULL; |
if (v->flags & POISONS) |
|
poison_check(v); |
|
if ((v->flags & VAR_DUMMY) == 0) |
|
return var_get_value(v); |
|
else |
|
return NULL; |
} |
} |
|
|
bool |
bool |
Var_Definedi(const char *name, const char *ename) |
Var_Definedi(const char *name, const char *ename) |
{ |
{ |
Var *v; |
Var *v; |
uint32_t k; |
uint32_t k; |
int idx; |
int idx; |
|
|
idx = quick_lookup(name, &ename, &k); |
idx = classify_var(name, &ename, &k); |
if (idx == -1) { |
/* We don't bother writing an error message for dynamic variables, |
|
* these will be caught when getting set later, usually. |
|
*/ |
|
if (idx == GLOBAL_INDEX) { |
v = find_global_var(name, ename, k); |
v = find_global_var(name, ename, k); |
if (v->flags & POISON_NORMAL) |
if (v->flags & POISON_NORMAL) |
poison_check(v); |
poison_check(v); |
if ((v->flags & VAR_DUMMY) == 0) |
if ((v->flags & VAR_DUMMY) == 0) |
return true; |
return true; |
} |
} |
|
|
return false; |
return false; |
} |
} |
|
|
|
|
|
/*** |
|
*** Substitution functions, handling both global and dynamic variables. |
|
***/ |
|
|
|
|
|
/* XXX contrary to find_global_var(), find_any_var() can return NULL pointers. |
|
*/ |
static Var * |
static Var * |
varfind(const char *name, const char *ename, SymTable *ctxt, |
find_any_var(const char *name, const char *ename, SymTable *ctxt, |
int idx, uint32_t k) |
int idx, uint32_t k) |
{ |
{ |
/* Handle local variables first */ |
/* Handle local variables first */ |
if (idx != -1) { |
if (idx != GLOBAL_INDEX) { |
if (ctxt != NULL) { |
if (ctxt != NULL) { |
if (idx < LOCAL_SIZE) |
if (idx < LOCAL_SIZE) |
return ctxt->locals[idx]; |
return ctxt->locals[idx]; |
else |
else |
return ctxt->locals[EXTENDED2SIMPLE(idx)]; |
return ctxt->locals[EXTENDED2SIMPLE(idx)]; |
} else |
} else |
return NULL; |
return NULL; |
} else { |
} else { |
return find_global_var(name, ename, k); |
return find_global_var(name, ename, k); |
} |
} |
} |
} |
|
|
static const char * |
/* All the scanning functions needed to account for all the forms of |
find_0(const char *p) |
* variable names that exist: |
{ |
* $A, ${AB}, $(ABC), ${A:mod}, $(A:mod) |
while (*p != '$' && *p != '\0' && *p != ':') |
*/ |
p++; |
|
return p; |
|
} |
|
|
|
static const char * |
static const char * |
find_rparen(const char *p) |
find_rparen(const char *p) |
|
|
return p; |
return p; |
} |
} |
|
|
|
/* Figure out what kind of name we're looking for from a start character. |
|
*/ |
static find_t |
static find_t |
find_pos(int c) |
find_pos(int c) |
{ |
{ |
switch(c) { |
switch(c) { |
case '\0': |
case '(': |
return find_0; |
|
case ')': |
|
return find_rparen; |
return find_rparen; |
case '}': |
case '{': |
return find_ket; |
return find_ket; |
default: |
default: |
return 0; |
Parse_Error(PARSE_FATAL, |
|
"Wrong character in variable spec %c (can't happen)"); |
|
return find_rparen; |
} |
} |
} |
} |
|
|
size_t |
size_t |
Var_ParseSkip(const char *str, SymTable *ctxt, bool *result) |
Var_ParseSkip(const char *str, SymTable *ctxt, bool *result) |
{ |
{ |
const char *tstr; /* Pointer into str */ |
const char *tstr; /* Pointer into str */ |
Var *v; /* Variable in invocation */ |
Var *v; /* Variable in invocation */ |
char endc; /* Ending character when variable in parens |
char paren; /* Parenthesis or brace or nothing */ |
* or braces */ |
const char *start; |
const char *start; |
size_t length; |
size_t length; |
struct Name name; |
struct Name name; |
|
|
|
v = NULL; |
v = NULL; |
start = str; |
start = str; |
str++; |
|
|
|
if (*str != '(' && *str != '{') { |
|
name.tofree = false; |
|
tstr = str + 1; |
|
length = 2; |
|
endc = '\0'; |
|
} else { |
|
endc = *str == '(' ? ')' : '}'; |
|
str++; |
str++; |
|
|
/* Find eventual modifiers in the variable */ |
if (*str != '(' && *str != '{') { |
tstr = VarName_Get(str, &name, ctxt, false, find_pos(endc)); |
name.tofree = false; |
VarName_Free(&name); |
tstr = str + 1; |
length = tstr - start; |
length = 2; |
if (*tstr != 0) |
paren = '\0'; |
length++; |
} else { |
} |
paren = *str; |
|
str++; |
|
|
if (result != NULL) |
/* Find eventual modifiers in the variable */ |
*result = true; |
tstr = VarName_Get(str, &name, ctxt, false, find_pos(paren)); |
if (*tstr == ':' && endc != '\0') |
VarName_Free(&name); |
if (VarModifiers_Apply(NULL, NULL, ctxt, true, NULL, tstr, endc, |
length = tstr - start; |
&length) == var_Error) |
if (*tstr != 0) |
if (result != NULL) |
length++; |
*result = false; |
} |
return length; |
|
|
if (result != NULL) |
|
*result = true; |
|
if (*tstr == ':' && paren != '\0') |
|
if (VarModifiers_Apply(NULL, NULL, ctxt, true, NULL, tstr, |
|
paren, &length) == var_Error) |
|
if (result != NULL) |
|
*result = false; |
|
return length; |
} |
} |
|
|
/* As of now, Var_ParseBuffer is just a wrapper around Var_Parse. For |
/* As of now, Var_ParseBuffer is just a wrapper around Var_Parse. For |
* speed, it may be better to revisit the implementation to do things |
* speed, it may be better to revisit the implementation to do things |
* directly. */ |
* directly. */ |
bool |
bool |
Var_ParseBuffer(Buffer buf, const char *str, SymTable *ctxt, bool err, |
Var_ParseBuffer(Buffer buf, const char *str, SymTable *ctxt, bool err, |
size_t *lengthPtr) |
size_t *lengthPtr) |
{ |
{ |
char *result; |
char *result; |
bool freeIt; |
bool freeIt; |
|
|
result = Var_Parse(str, ctxt, err, lengthPtr, &freeIt); |
result = Var_Parse(str, ctxt, err, lengthPtr, &freeIt); |
if (result == var_Error) |
if (result == var_Error) |
return false; |
return false; |
|
|
Buf_AddString(buf, result); |
Buf_AddString(buf, result); |
if (freeIt) |
if (freeIt) |
free(result); |
free(result); |
return true; |
return true; |
} |
} |
|
|
char * |
char * |
Var_Parse(const char *str, /* The string to parse */ |
Var_Parse(const char *str, /* The string to parse */ |
SymTable *ctxt, /* The context for the variable */ |
SymTable *ctxt, /* The context for the variable */ |
bool err, /* true if undefined variables are an error */ |
bool err, /* true if undefined variables are an error */ |
size_t *lengthPtr, /* OUT: The length of the specification */ |
size_t *lengthPtr, /* OUT: The length of the specification */ |
bool *freePtr) /* OUT: true if caller should free result */ |
bool *freePtr) /* OUT: true if caller should free result */ |
{ |
{ |
const char *tstr; /* Pointer into str */ |
const char *tstr; /* Pointer into str */ |
Var *v; /* Variable in invocation */ |
Var *v; /* Variable in invocation */ |
char endc; /* Ending character when variable in parens |
char paren; /* Parenthesis or brace or nothing */ |
* or braces */ |
struct Name name; |
struct Name name; |
const char *start; |
const char *start; |
char *val; /* Variable value */ |
char *val; /* Variable value */ |
uint32_t k; |
uint32_t k; |
int idx; |
int idx; |
|
|
|
*freePtr = false; |
*freePtr = false; |
start = str++; |
start = str++; |
|
|
val = NULL; |
val = NULL; |
v = NULL; |
v = NULL; |
idx = -1; |
idx = GLOBAL_INDEX; |
|
|
if (*str != '(' && *str != '{') { |
if (*str != '(' && *str != '{') { |
name.s = str; |
name.s = str; |
name.e = str+1; |
name.e = str+1; |
name.tofree = false; |
name.tofree = false; |
tstr = str + 1; |
tstr = str + 1; |
*lengthPtr = 2; |
*lengthPtr = 2; |
endc = '\0'; |
paren = '\0'; |
} else { |
} else { |
endc = *str == '(' ? ')' : '}'; |
paren = *str; |
str++; |
str++; |
|
|
/* Find eventual modifiers in the variable */ |
/* Find eventual modifiers in the variable */ |
tstr = VarName_Get(str, &name, ctxt, false, find_pos(endc)); |
tstr = VarName_Get(str, &name, ctxt, false, find_pos(paren)); |
*lengthPtr = tstr - start; |
*lengthPtr = tstr - start; |
if (*tstr != '\0') |
if (*tstr != '\0') |
(*lengthPtr)++; |
(*lengthPtr)++; |
} |
} |
|
|
idx = quick_lookup(name.s, &name.e, &k); |
idx = classify_var(name.s, &name.e, &k); |
v = varfind(name.s, name.e, ctxt, idx, k); |
v = find_any_var(name.s, name.e, ctxt, idx, k); |
if (v != NULL && (v->flags & POISONS) != 0) |
if (v != NULL && (v->flags & POISONS) != 0) |
poison_check(v); |
poison_check(v); |
if (v != NULL && (v->flags & VAR_DUMMY) == 0) { |
if (v != NULL && (v->flags & VAR_DUMMY) == 0) { |
if (v->flags & VAR_IN_USE) |
if (v->flags & VAR_IN_USE) |
Fatal("Variable %s is recursive.", v->name); |
Fatal("Variable %s is recursive.", v->name); |
/*NOTREACHED*/ |
/*NOTREACHED*/ |
else |
else |
v->flags |= VAR_IN_USE; |
v->flags |= VAR_IN_USE; |
|
|
/* Before doing any modification, we have to make sure the value |
/* Before doing any modification, we have to make sure the |
* has been fully expanded. If it looks like recursion might be |
* value has been fully expanded. If it looks like recursion |
* necessary (there's a dollar sign somewhere in the variable's value) |
* might be necessary (there's a dollar sign somewhere in |
* we just call Var_Subst to do any other substitutions that are |
* the variable's value) we just call Var_Subst to do any |
* necessary. Note that the value returned by Var_Subst will have |
* other substitutions that are necessary. Note that the |
* been dynamically-allocated, so it will need freeing when we |
* value returned by Var_Subst will have been dynamically |
* return. */ |
* allocated, so it will need freeing when we return. |
val = VarValue(v); |
*/ |
if (idx == -1) { |
val = var_get_value(v); |
if (strchr(val, '$') != NULL) { |
if (idx == GLOBAL_INDEX) { |
val = Var_Subst(val, ctxt, err); |
if (strchr(val, '$') != NULL) { |
*freePtr = true; |
val = Var_Subst(val, ctxt, err); |
} |
*freePtr = true; |
} else if (idx >= LOCAL_SIZE) { |
} |
if (IS_EXTENDED_F(idx)) |
} else if (idx >= LOCAL_SIZE) { |
val = Var_GetTail(val); |
if (IS_EXTENDED_F(idx)) |
else |
val = Var_GetTail(val); |
val = Var_GetHead(val); |
else |
*freePtr = true; |
val = Var_GetHead(val); |
|
*freePtr = true; |
|
} |
|
v->flags &= ~VAR_IN_USE; |
} |
} |
v->flags &= ~VAR_IN_USE; |
if (*tstr == ':' && paren != '\0') |
} |
val = VarModifiers_Apply(val, &name, ctxt, err, freePtr, |
if (*tstr == ':' && endc != '\0') |
tstr, paren, lengthPtr); |
val = VarModifiers_Apply(val, &name, ctxt, err, freePtr, tstr, endc, |
if (val == NULL) { |
lengthPtr); |
val = err ? var_Error : varNoError; |
if (val == NULL) { |
/* Dynamic source */ |
val = err ? var_Error : varNoError; |
if (idx != GLOBAL_INDEX) { |
/* Dynamic source */ |
/* can't be expanded for now: copy the spec instead. */ |
if (idx != -1) { |
if (ctxt == NULL) { |
/* can't be expanded for now: copy the var spec instead. */ |
*freePtr = true; |
if (ctxt == NULL) { |
val = Str_dupi(start, start+ *lengthPtr); |
*freePtr = true; |
} else { |
val = Str_dupi(start, start+ *lengthPtr); |
/* somehow, this should have been expanded already. */ |
} else { |
GNode *n; |
/* somehow, this should have been expanded already. */ |
|
GNode *n; |
|
|
|
n = (GNode *)(((char *)ctxt) - offsetof(GNode, context)); |
/* XXX */ |
if (idx >= LOCAL_SIZE) |
n = (GNode *)(((char *)ctxt) - |
idx = EXTENDED2SIMPLE(idx); |
offsetof(GNode, context)); |
switch(idx) { |
if (idx >= LOCAL_SIZE) |
case IMPSRC_INDEX: |
idx = EXTENDED2SIMPLE(idx); |
Fatal("Using $< in a non-suffix rule context is a GNUmake idiom (line %lu of %s)", |
switch(idx) { |
n->lineno, n->fname); |
case IMPSRC_INDEX: |
default: |
Fatal( |
Error("Using undefined dynamic variable $%s (line %lu of %s)", |
"Using $< in a non-suffix rule context is a GNUmake idiom (line %lu of %s)", |
varnames[idx], n->lineno, n->fname); |
n->lineno, n->fname); |
break; |
default: |
|
Error( |
|
"Using undefined dynamic variable $%s (line %lu of %s)", |
|
varnames[idx], n->lineno, n->fname); |
|
break; |
|
} |
|
} |
} |
} |
} |
|
} |
} |
} |
VarName_Free(&name); |
VarName_Free(&name); |
return val; |
return val; |
|
} |
} |
|
|
|
|
char * |
char * |
Var_Subst(const char *str, /* the string in which to substitute */ |
Var_Subst(const char *str, /* the string in which to substitute */ |
SymTable *ctxt, /* the context wherein to find variables */ |
SymTable *ctxt, /* the context wherein to find variables */ |
bool undefErr) /* true if undefineds are an error */ |
bool undefErr) /* true if undefineds are an error */ |
{ |
{ |
BUFFER buf; /* Buffer for forming things */ |
BUFFER buf; /* Buffer for forming things */ |
static bool errorReported; /* Set true if an error has already |
static bool errorReported; |
* been reported to prevent a plethora |
|
* of messages when recursing */ |
|
|
|
Buf_Init(&buf, MAKE_BSIZE); |
Buf_Init(&buf, MAKE_BSIZE); |
errorReported = false; |
errorReported = false; |
|
|
for (;;) { |
for (;;) { |
char *val; /* Value to substitute for a variable */ |
char *val; /* Value to substitute for a variable */ |
size_t length; /* Length of the variable invocation */ |
size_t length; /* Length of the variable invocation */ |
bool doFree; /* Set true if val should be freed */ |
bool doFree; /* Set true if val should be freed */ |
const char *cp; |
const char *cp; |
|
|
/* copy uninteresting stuff */ |
/* copy uninteresting stuff */ |
for (cp = str; *str != '\0' && *str != '$'; str++) |
for (cp = str; *str != '\0' && *str != '$'; str++) |
; |
; |
Buf_Addi(&buf, cp, str); |
Buf_Addi(&buf, cp, str); |
if (*str == '\0') |
if (*str == '\0') |
break; |
break; |
if (str[1] == '$') { |
if (str[1] == '$') { |
/* A dollar sign may be escaped with another dollar sign. */ |
/* A $ may be escaped with another $. */ |
Buf_AddChar(&buf, '$'); |
Buf_AddChar(&buf, '$'); |
str += 2; |
str += 2; |
continue; |
continue; |
} |
} |
val = Var_Parse(str, ctxt, undefErr, &length, &doFree); |
val = Var_Parse(str, ctxt, undefErr, &length, &doFree); |
/* When we come down here, val should either point to the |
/* When we come down here, val should either point to the |
* value of this variable, suitably modified, or be NULL. |
* value of this variable, suitably modified, or be NULL. |
* Length should be the total length of the potential |
* Length should be the total length of the potential |
* variable invocation (from $ to end character...) */ |
* variable invocation (from $ to end character...) */ |
if (val == var_Error || val == varNoError) { |
if (val == var_Error || val == varNoError) { |
/* If performing old-time variable substitution, skip over |
/* If errors are not an issue, skip over the variable |
* the variable and continue with the substitution. Otherwise, |
* and continue with the substitution. Otherwise, store |
* store the dollar sign and advance str so we continue with |
* the dollar sign and advance str so we continue with |
* the string... */ |
* the string... */ |
if (errorIsOkay) |
if (errorIsOkay) |
str += length; |
str += length; |
else if (undefErr) { |
else if (undefErr) { |
/* If variable is undefined, complain and skip the |
/* If variable is undefined, complain and |
* variable. The complaint will stop us from doing anything |
* skip the variable name. The complaint |
* when the file is parsed. */ |
* will stop us from doing anything when |
if (!errorReported) |
* the file is parsed. */ |
Parse_Error(PARSE_FATAL, |
if (!errorReported) |
"Undefined variable \"%.*s\"",length,str); |
Parse_Error(PARSE_FATAL, |
str += length; |
"Undefined variable \"%.*s\"", |
errorReported = true; |
length, str); |
} else { |
str += length; |
Buf_AddChar(&buf, *str); |
errorReported = true; |
str++; |
} else { |
} |
Buf_AddChar(&buf, *str); |
} else { |
str++; |
/* We've now got a variable structure to store in. But first, |
} |
* advance the string pointer. */ |
} else { |
str += length; |
/* We've now got a variable structure to store in. |
|
* But first, advance the string pointer. */ |
|
str += length; |
|
|
/* Copy all the characters from the variable value straight |
/* Copy all the characters from the variable value |
* into the new string. */ |
* straight into the new string. */ |
Buf_AddString(&buf, val); |
Buf_AddString(&buf, val); |
if (doFree) |
if (doFree) |
free(val); |
free(val); |
|
} |
} |
} |
} |
return Buf_Retrieve(&buf); |
return Buf_Retrieve(&buf); |
|
} |
} |
|
|
|
|
|
/*** |
|
*** Supplementary support for .for loops. |
|
***/ |
|
|
|
|
|
|
|
struct LoopVar |
|
{ |
|
Var old; /* keep old variable value (before the loop) */ |
|
Var *me; /* the variable we're dealing with */ |
|
}; |
|
|
|
|
|
struct LoopVar * |
|
Var_NewLoopVar(const char *name, const char *ename) |
|
{ |
|
struct LoopVar *l; |
|
uint32_t k; |
|
|
|
l = emalloc(sizeof(struct LoopVar)); |
|
|
|
/* we obtain a new variable quickly, make a snapshot of its old |
|
* value, and make sure the environment cannot touch us. |
|
*/ |
|
/* XXX: should we avoid dynamic variables ? */ |
|
k = ohash_interval(name, &ename); |
|
|
|
l->me = find_global_var_without_env(name, ename, k); |
|
l->old = *(l->me); |
|
l->me->flags |= VAR_SEEN_ENV; |
|
return l; |
|
} |
|
|
void |
void |
Var_SubstVar(Buffer buf, /* To store result */ |
Var_DeleteLoopVar(struct LoopVar *l) |
const char *str, /* The string in which to substitute */ |
{ |
const char *var, /* Named variable */ |
*(l->me) = l->old; |
|
free(l); |
|
} |
|
|
|
void |
|
Var_SubstVar(Buffer buf, /* To store result */ |
|
const char *str, /* The string in which to substitute */ |
|
struct LoopVar *l, /* Handle */ |
const char *val) /* Its value */ |
const char *val) /* Its value */ |
{ |
{ |
/* we save the old value and affect the new value temporarily */ |
const char *var = l->me->name; |
Var old; |
|
const char *ename = NULL; |
|
uint32_t k; |
|
Var *me; |
|
k = ohash_interval(var, &ename); |
|
me = obtain_global_var(var, ename, k); |
|
old = *me; |
|
var_init_string(me, val); |
|
me->flags = VAR_SEEN_ENV; |
|
|
|
assert(*var != '\0'); |
var_set_value(l->me, val); |
|
|
for (;;) { |
for (;;) { |
const char *start; |
const char *start; |
/* Copy uninteresting stuff */ |
/* Copy uninteresting stuff */ |
for (start = str; *str != '\0' && *str != '$'; str++) |
for (start = str; *str != '\0' && *str != '$'; str++) |
; |
; |
Buf_Addi(buf, start, str); |
Buf_Addi(buf, start, str); |
|
|
start = str; |
start = str; |
if (*str++ == '\0') |
if (*str++ == '\0') |
break; |
break; |
str++; |
str++; |
/* and escaped dollars */ |
/* and escaped dollars */ |
if (start[1] == '$') { |
if (start[1] == '$') { |
Buf_Addi(buf, start, start+2); |
Buf_Addi(buf, start, start+2); |
continue; |
continue; |
} |
} |
/* Simple variable, if it's not us, copy. */ |
/* Simple variable, if it's not us, copy. */ |
if (start[1] != '(' && start[1] != '{') { |
if (start[1] != '(' && start[1] != '{') { |
if (start[1] != *var || var[1] != '\0') { |
if (start[1] != *var || var[1] != '\0') { |
Buf_AddChars(buf, 2, start); |
Buf_AddChars(buf, 2, start); |
continue; |
continue; |
} |
} |
} else { |
} else { |
const char *p; |
const char *p; |
char endc; |
char paren = start[1]; |
|
|
if (start[1] == '(') |
|
endc = ')'; |
|
else |
|
endc = '}'; |
|
|
|
/* Find the end of the variable specification. */ |
/* Find the end of the variable specification. */ |
p = str; |
p = find_pos(paren)(str); |
while (*p != '\0' && *p != ':' && *p != endc && *p != '$') |
/* A variable inside the variable. We don't know how to |
p++; |
* expand the external variable at this point, so we |
/* A variable inside the variable. We don't know how to |
* try again with the nested variable. */ |
* expand the external variable at this point, so we try |
if (*p == '$') { |
* again with the nested variable. */ |
Buf_Addi(buf, start, p); |
if (*p == '$') { |
str = p; |
Buf_Addi(buf, start, p); |
continue; |
str = p; |
} |
continue; |
|
} |
|
|
|
if (strncmp(var, str, p - str) != 0 || |
if (strncmp(var, str, p - str) != 0 || |
var[p - str] != '\0') { |
var[p - str] != '\0') { |
/* Not the variable we want to expand. */ |
/* Not the variable we want to expand. */ |
Buf_Addi(buf, start, p); |
Buf_Addi(buf, start, p); |
str = p; |
str = p; |
continue; |
continue; |
} |
} |
if (*p == ':') { |
if (*p == ':') { |
size_t length; /* Length of the variable invocation */ |
size_t length; /* Length of variable name */ |
bool doFree; /* Set true if val should be freed */ |
bool doFree; /* should val be freed ? */ |
char *newval; /* Value substituted for a variable */ |
char *newval; |
struct Name name; |
struct Name name; |
|
|
length = p - str + 1; |
length = p - str + 1; |
doFree = false; |
doFree = false; |
name.s = var; |
name.s = var; |
name.e = var + (p-str); |
name.e = var + (p-str); |
|
|
/* val won't be freed since doFree == false, but |
/* val won't be freed since !doFree, but |
* VarModifiers_Apply doesn't know that, hence the cast. */ |
* VarModifiers_Apply doesn't know that, |
newval = VarModifiers_Apply((char *)val, &name, NULL, false, |
* hence the cast. */ |
&doFree, p, endc, &length); |
newval = VarModifiers_Apply((char *)val, &name, |
Buf_AddString(buf, newval); |
NULL, false, &doFree, p, paren, &length); |
if (doFree) |
Buf_AddString(buf, newval); |
free(newval); |
if (doFree) |
str += length; |
free(newval); |
continue; |
str += length; |
} else |
continue; |
str = p+1; |
} else |
|
str = p+1; |
|
} |
|
Buf_AddString(buf, val); |
} |
} |
Buf_AddString(buf, val); |
|
} |
|
*me = old; |
|
} |
} |
|
|
|
/*** |
|
*** Odds and ends |
|
***/ |
|
|
static void |
static void |
set_magic_shell_variable() |
set_magic_shell_variable() |
{ |
{ |
const char *name = "SHELL"; |
const char *name = "SHELL"; |
const char *ename = NULL; |
const char *ename = NULL; |
uint32_t k; |
uint32_t k; |
Var *v; |
Var *v; |
k = ohash_interval(name, &ename); |
|
v = create_var(name, ename); |
k = ohash_interval(name, &ename); |
ohash_insert(&global_variables, |
v = find_global_var_without_env(name, ename, k); |
ohash_lookup_interval(&global_variables, name, ename, k), v); |
var_set_value(v, _PATH_BSHELL); |
/* the environment shall not affect it */ |
/* XXX the environment shall never affect it */ |
v->flags = VAR_SHELL | VAR_SEEN_ENV; |
v->flags = VAR_SHELL | VAR_SEEN_ENV; |
var_init_string(v, _PATH_BSHELL); |
|
} |
} |
|
|
/*- |
/* |
*----------------------------------------------------------------------- |
* Var_Init |
* Var_Init -- |
|
* Initialize the module |
* Initialize the module |
* |
|
* Side Effects: |
|
* The CTXT_CMD and CTXT_GLOBAL contexts are initialized |
|
*----------------------------------------------------------------------- |
|
*/ |
*/ |
void |
void |
Var_Init(void) |
Var_Init(void) |
{ |
{ |
ohash_init(&global_variables, 10, &var_info); |
ohash_init(&global_variables, 10, &var_info); |
set_magic_shell_variable(); |
set_magic_shell_variable(); |
|
|
|
|
errorIsOkay = true; |
errorIsOkay = true; |
Var_setCheckEnvFirst(false); |
Var_setCheckEnvFirst(false); |
|
|
VarModifiers_Init(); |
VarModifiers_Init(); |
} |
} |
|
|
|
|
|
|
void |
void |
Var_End(void) |
Var_End(void) |
{ |
{ |
Var *v; |
Var *v; |
unsigned int i; |
unsigned int i; |
|
|
for (v = ohash_first(&global_variables, &i); v != NULL; |
for (v = ohash_first(&global_variables, &i); v != NULL; |
v = ohash_next(&global_variables, &i)) |
v = ohash_next(&global_variables, &i)) |
VarDelete(v); |
delete_var(v); |
} |
} |
#endif |
#endif |
|
|
|
|
static const char * |
static const char * |
interpret(int f) |
interpret(int f) |
{ |
{ |
if (f & VAR_DUMMY) |
if (f & VAR_DUMMY) |
return "(D)"; |
return "(D)"; |
return ""; |
return ""; |
} |
} |
|
|
|
|
/****************** PRINT DEBUGGING INFO *****************/ |
|
static void |
static void |
VarPrintVar(Var *v) |
print_var(Var *v) |
{ |
{ |
printf("%-16s%s = %s\n", v->name, interpret(v->flags), |
printf("%-16s%s = %s\n", v->name, interpret(v->flags), |
(v->flags & VAR_DUMMY) == 0 ? VarValue(v) : "(none)"); |
(v->flags & VAR_DUMMY) == 0 ? var_get_value(v) : "(none)"); |
} |
} |
|
|
void |
void |
Var_Dump(void) |
Var_Dump(void) |
{ |
{ |
Var *v; |
Var *v; |
unsigned int i; |
unsigned int i; |
|
|
printf("#*** Global Variables:\n"); |
printf("#*** Global Variables:\n"); |
|
|
for (v = ohash_first(&global_variables, &i); v != NULL; |
for (v = ohash_first(&global_variables, &i); v != NULL; |
v = ohash_next(&global_variables, &i)) |
v = ohash_next(&global_variables, &i)) |
VarPrintVar(v); |
print_var(v); |
|
|
} |
} |
|
|
static const char *quotable = " \t\n\\'\""; |
static const char *quotable = " \t\n\\'\""; |
|
|
/* In POSIX mode, variable assignments passed on the command line are |
/* POSIX says that variable assignments passed on the command line should be |
* propagated to sub makes through MAKEFLAGS. |
* propagated to sub makes through MAKEFLAGS. |
*/ |
*/ |
void |
void |
Var_AddCmdline(const char *name) |
Var_AddCmdline(const char *name) |
{ |
{ |
Var *v; |
Var *v; |
unsigned int i; |
unsigned int i; |
BUFFER buf; |
BUFFER buf; |
char *s; |
char *s; |
|
|
Buf_Init(&buf, MAKE_BSIZE); |
Buf_Init(&buf, MAKE_BSIZE); |
|
|
for (v = ohash_first(&global_variables, &i); v != NULL; |
for (v = ohash_first(&global_variables, &i); v != NULL; |
v = ohash_next(&global_variables, &i)) { |
v = ohash_next(&global_variables, &i)) { |
|
/* This is not as expensive as it looks: this function is |
|
* called before parsing Makefiles, so there are just a |
|
* few non cmdling variables in there. |
|
*/ |
if (!(v->flags & VAR_FROM_CMD)) { |
if (!(v->flags & VAR_FROM_CMD)) { |
continue; |
continue; |
} |
} |
/* We assume variable names don't need quoting */ |
/* We assume variable names don't need quoting */ |
Buf_AddString(&buf, v->name); |
Buf_AddString(&buf, v->name); |
Buf_AddChar(&buf, '='); |
Buf_AddChar(&buf, '='); |
for (s = VarValue(v); *s != '\0'; s++) { |
for (s = var_get_value(v); *s != '\0'; s++) { |
if (strchr(quotable, *s)) |
if (strchr(quotable, *s)) |
Buf_AddChar(&buf, '\\'); |
Buf_AddChar(&buf, '\\'); |
Buf_AddChar(&buf, *s); |
Buf_AddChar(&buf, *s); |
} |
} |
Buf_AddSpace(&buf); |
Buf_AddSpace(&buf); |
} |
} |
Var_Append(name, Buf_Retrieve(&buf), VAR_GLOBAL); |
Var_Append(name, Buf_Retrieve(&buf), VAR_GLOBAL); |
Buf_Destroy(&buf); |
Buf_Destroy(&buf); |
} |
} |