#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ohash.h>
#include "defines.h"
#include "buf.h"
#include "cmd_exec.h"
#include "stats.h"
#include "pathnames.h"
#include "varmodifiers.h"
#include "var.h"
#include "varname.h"
#include "error.h"
#include "str.h"
#include "var_int.h"
#include "memory.h"
#include "symtable.h"
#include "gnode.h"
#include "dump.h"
#include "lowparse.h"
char var_Error[] = "";
GNode *current_node = NULL;
static char varNoError[] = "";
bool errorIsOkay;
static bool checkEnvFirst;
static bool varname_list_changed = true;
void
Var_setCheckEnvFirst(bool yes)
{
checkEnvFirst = yes;
}
static char *varnames[] = {
TARGET,
PREFIX,
ARCHIVE,
MEMBER,
IMPSRC,
OODATE,
ALLSRC,
};
static bool xtlist[] = {
false,
true,
false,
false,
true,
true,
false,
false,
};
static bool *tlist = xtlist+1;
#include "varhashconsts.h"
#define GLOBAL_INDEX -1
static struct ohash global_variables;
typedef struct Var_ {
BUFFER val;
unsigned int flags;
#define VAR_IN_USE 1
#define VAR_DUMMY 2
#define VAR_FROM_CMD 4
#define VAR_FROM_ENV 8
#define VAR_SEEN_ENV 16
#define VAR_IS_SHELL 32
#define VAR_IS_NAMES 1024
#define POISONS (POISON_NORMAL | POISON_EMPTY | POISON_NOT_DEFINED)
char name[1];
} Var;
#define VARNAME_LIST ".VARIABLES"
static struct ohash_info var_info = {
offsetof(Var, name),
NULL,
hash_calloc, hash_free, element_alloc
};
static int classify_var(const char *, const char **, uint32_t *, char *);
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 Var *create_var(const char *, const char *);
static void var_set_initial_value(Var *, const char *);
static void var_set_value(Var *, const char *);
static char *var_get_value(Var *);
static void var_exec_cmd(Var *);
static void varname_list_retrieve(Var *);
static void var_append_value(Var *, const char *);
static void poison_check(Var *);
static void var_set_append(const char *, const char *, const char *, int, bool);
static void set_magic_shell_variable(void);
static void delete_var(Var *);
static void print_var(Var *);
static const char *find_rparen(const char *);
static const char *find_ket(const char *);
typedef const char * (*find_t)(const char *);
static find_t find_pos(int);
static void push_used(Var *);
static void pop_used(Var *);
static char *get_expanded_value(const char *, const char *, int, uint32_t, char,
SymTable *, bool, bool *);
static bool parse_base_variable_name(const char **, struct Name *, SymTable *);
static int
classify_var(const char *name, const char **enamePtr, uint32_t *pk, char *ext)
{
size_t len;
uint32_t k;
*ext = 0;
*pk = ohash_interval(name, enamePtr);
len = *enamePtr - name;
k = *pk;
if (len == 2 && (name[1] == 'D' || name[1] == 'F')) {
*ext = name[1];
len--;
k = name[0];
}
switch (k % MAGICSLOTS1) {
case K_LONGALLSRC % MAGICSLOTS1:
if (*pk == K_LONGALLSRC && len == strlen(LONGALLSRC) &&
strncmp(name, LONGALLSRC, len) == 0)
return ALLSRC_INDEX;
break;
case K_LONGARCHIVE % MAGICSLOTS1:
if (*pk == K_LONGARCHIVE && len == strlen(LONGARCHIVE) &&
strncmp(name, LONGARCHIVE, len) == 0)
return ARCHIVE_INDEX;
break;
case K_LONGIMPSRC % MAGICSLOTS1:
if (*pk == K_LONGIMPSRC && len == strlen(LONGIMPSRC) &&
strncmp(name, LONGIMPSRC, len) == 0)
return IMPSRC_INDEX;
break;
case K_LONGMEMBER % MAGICSLOTS1:
if (*pk == K_LONGMEMBER && len == strlen(LONGMEMBER) &&
strncmp(name, LONGMEMBER, len) == 0)
return MEMBER_INDEX;
break;
case K_LONGOODATE % MAGICSLOTS1:
if (*pk == K_LONGOODATE && len == strlen(LONGOODATE) &&
strncmp(name, LONGOODATE, len) == 0)
return OODATE_INDEX;
break;
case K_LONGPREFIX % MAGICSLOTS1:
if (*pk == K_LONGPREFIX && len == strlen(LONGPREFIX) &&
strncmp(name, LONGPREFIX, len) == 0)
return PREFIX_INDEX;
break;
case K_LONGTARGET % MAGICSLOTS1:
if (*pk == K_LONGTARGET && len == strlen(LONGTARGET) &&
strncmp(name, LONGTARGET, len) == 0)
return TARGET_INDEX;
break;
case K_TARGET % MAGICSLOTS1:
if (name[0] == TARGET[0] && len == 1)
return TARGET_INDEX;
break;
case K_OODATE % MAGICSLOTS1:
if (name[0] == OODATE[0] && len == 1)
return OODATE_INDEX;
break;
case K_ALLSRC % MAGICSLOTS1:
if (name[0] == ALLSRC[0] && len == 1)
return ALLSRC_INDEX;
break;
case K_IMPSRC % MAGICSLOTS1:
if (name[0] == IMPSRC[0] && len == 1)
return IMPSRC_INDEX;
break;
case K_PREFIX % MAGICSLOTS1:
if (name[0] == PREFIX[0] && len == 1)
return PREFIX_INDEX;
break;
case K_ARCHIVE % MAGICSLOTS1:
if (name[0] == ARCHIVE[0] && len == 1)
return ARCHIVE_INDEX;
break;
case K_MEMBER % MAGICSLOTS1:
if (name[0] == MEMBER[0] && len == 1)
return MEMBER_INDEX;
break;
default:
break;
}
return GLOBAL_INDEX;
}
static Var *
create_var(const char *name, const char *ename)
{
return ohash_create_entry(&var_info, name, &ename);
}
static void
var_set_initial_value(Var *v, const char *val)
{
size_t len;
len = strlen(val);
Buf_Init(&(v->val), len+1);
Buf_AddChars(&(v->val), len, val);
varname_list_changed = true;
}
static void
var_set_value(Var *v, const char *val)
{
if ((v->flags & VAR_DUMMY) == 0) {
Buf_Reset(&(v->val));
Buf_AddString(&(v->val), val);
} else {
var_set_initial_value(v, val);
v->flags &= ~VAR_DUMMY;
}
}
static char *
var_get_value(Var *v)
{
if (v->flags & VAR_IS_NAMES)
varname_list_retrieve(v);
else if (v->flags & VAR_EXEC_LATER)
var_exec_cmd(v);
return Buf_Retrieve(&(v->val));
}
static void
var_append_value(Var *v, const char *val)
{
if ((v->flags & VAR_DUMMY) == 0) {
Buf_AddSpace(&(v->val));
Buf_AddString(&(v->val), val);
} else {
var_set_initial_value(v, val);
v->flags &= ~VAR_DUMMY;
}
}
static void
delete_var(Var *v)
{
if ((v->flags & VAR_DUMMY) == 0)
Buf_Destroy(&(v->val));
free(v);
}
void
SymTable_Init(SymTable *ctxt)
{
static SymTable sym_template;
memcpy(ctxt, &sym_template, sizeof(*ctxt));
}
static Var *
find_global_var_without_env(const char *name, const char *ename, uint32_t k)
{
unsigned int slot;
Var *v;
slot = ohash_lookup_interval(&global_variables, name, ename, k);
v = ohash_find(&global_variables, slot);
if (v == NULL) {
v = create_var(name, ename);
v->flags = VAR_DUMMY;
ohash_insert(&global_variables, slot, v);
}
return v;
}
static void
fill_from_env(Var *v)
{
char *env;
env = getenv(v->name);
if (env == NULL)
v->flags |= VAR_SEEN_ENV;
else {
var_set_value(v, env);
v->flags |= VAR_FROM_ENV | VAR_SEEN_ENV;
}
#ifdef STATS_VAR_LOOKUP
STAT_VAR_FROM_ENV++;
#endif
}
static Var *
find_global_var(const char *name, const char *ename, uint32_t k)
{
Var *v;
v = find_global_var_without_env(name, ename, k);
if ((v->flags & VAR_SEEN_ENV) == 0)
if ((checkEnvFirst && (v->flags & VAR_FROM_CMD) == 0) ||
(v->flags & VAR_DUMMY) != 0)
fill_from_env(v);
return v;
}
void
Var_Mark(const char *name, const char *ename, unsigned int type)
{
Var *v;
uint32_t k;
int idx;
char ext;
idx = classify_var(name, &ename, &k, &ext);
if (idx != GLOBAL_INDEX) {
Parse_Error(PARSE_FATAL,
"Trying to poison dynamic variable $%s",
varnames[idx]);
return;
}
v = find_global_var(name, ename, k);
v->flags |= type;
if (v->flags & POISON_NORMAL) {
if (v->flags & VAR_DUMMY)
return;
if (v->flags & VAR_FROM_ENV)
return;
Parse_Error(PARSE_FATAL,
"Poisoned variable %s is already set\n", v->name);
}
}
static void
poison_check(Var *v)
{
if (v->flags & POISON_NORMAL) {
Parse_Error(PARSE_FATAL,
"Poisoned variable %s has been referenced\n", v->name);
return;
}
if (v->flags & VAR_DUMMY) {
Parse_Error(PARSE_FATAL,
"Poisoned variable %s is not defined\n", v->name);
return;
}
if (v->flags & POISON_EMPTY)
if (strcmp(var_get_value(v), "") == 0)
Parse_Error(PARSE_FATAL,
"Poisoned variable %s is empty\n", v->name);
}
void
Var_Deletei(const char *name, const char *ename)
{
Var *v;
uint32_t k;
unsigned int slot;
int idx;
char ext;
idx = classify_var(name, &ename, &k, &ext);
if (idx != GLOBAL_INDEX) {
Parse_Error(PARSE_FATAL,
"Trying to delete dynamic variable $%s", varnames[idx]);
return;
}
slot = ohash_lookup_interval(&global_variables, name, ename, k);
v = ohash_find(&global_variables, slot);
if (v == NULL)
return;
if (checkEnvFirst && (v->flags & VAR_FROM_ENV))
return;
if (v->flags & VAR_FROM_CMD)
return;
ohash_remove(&global_variables, slot);
delete_var(v);
varname_list_changed = true;
}
static void
var_set_append(const char *name, const char *ename, const char *val, int ctxt,
bool append)
{
Var *v;
uint32_t k;
int idx;
char ext;
idx = classify_var(name, &ename, &k, &ext);
if (idx != GLOBAL_INDEX) {
Parse_Error(PARSE_FATAL, "Trying to %s dynamic variable $%s",
append ? "append to" : "set", varnames[idx]);
return;
}
v = find_global_var(name, ename, k);
if (v->flags & POISON_NORMAL)
Parse_Error(PARSE_FATAL, "Trying to %s poisoned variable %s\n",
append ? "append to" : "set", v->name);
if (ctxt == VAR_CMD) {
(append ? var_append_value : var_set_value)(v, val);
v->flags |= VAR_FROM_CMD;
if ((v->flags & VAR_IS_SHELL) == 0) {
esetenv(v->name, val);
}
if (DEBUG(VAR))
printf("command:%s = %s\n", v->name, var_get_value(v));
} else if ((v->flags & VAR_FROM_CMD) == 0 &&
(!checkEnvFirst || (v->flags & VAR_FROM_ENV) == 0)) {
(append ? var_append_value : var_set_value)(v, val);
if (DEBUG(VAR))
printf("global:%s = %s\n", v->name, var_get_value(v));
} else if (DEBUG(VAR))
printf("overridden:%s = %s\n", v->name, var_get_value(v));
}
void
Var_Seti_with_ctxt(const char *name, const char *ename, const char *val,
int ctxt)
{
var_set_append(name, ename, val, ctxt, false);
}
void
Var_Appendi_with_ctxt(const char *name, const char *ename, const char *val,
int ctxt)
{
var_set_append(name, ename, val, ctxt, true);
}
static void
var_exec_cmd(Var *v)
{
char *arg = Buf_Retrieve(&(v->val));
char *err;
char *res1;
res1 = Cmd_Exec(arg, &err);
if (err)
Parse_Error(PARSE_WARNING, err, arg);
var_set_value(v, res1);
free(res1);
v->flags &= ~VAR_EXEC_LATER;
}
static void
varname_list_retrieve(Var *v)
{
unsigned int i;
void *e;
bool first = true;
if (!varname_list_changed)
return;
for (e = ohash_first(&global_variables, &i); e != NULL;
e = ohash_next(&global_variables, &i)) {
Var *v2 = e;
if (v2->flags & VAR_DUMMY)
continue;
if (first)
var_set_value(v, v2->name);
else
var_append_value(v, v2->name);
first = false;
}
varname_list_changed = false;
}
char *
Var_Valuei(const char *name, const char *ename)
{
Var *v;
uint32_t k;
int idx;
char ext;
idx = classify_var(name, &ename, &k, &ext);
if (idx != GLOBAL_INDEX) {
Parse_Error(PARSE_FATAL,
"Trying to get value of dynamic variable $%s",
varnames[idx]);
return NULL;
}
v = find_global_var(name, ename, k);
if (v->flags & POISONS)
poison_check(v);
if ((v->flags & VAR_DUMMY) == 0)
return var_get_value(v);
else
return NULL;
}
bool
Var_Definedi(const char *name, const char *ename)
{
Var *v;
uint32_t k;
int idx;
char ext;
idx = classify_var(name, &ename, &k, &ext);
if (idx == GLOBAL_INDEX) {
v = find_global_var(name, ename, k);
if (v->flags & POISON_NORMAL)
poison_check(v);
if ((v->flags & VAR_DUMMY) == 0)
return true;
}
return false;
}
static const char *
find_rparen(const char *p)
{
while (*p != '$' && *p != '\0' && *p != ')' && *p != ':')
p++;
return p;
}
static const char *
find_ket(const char *p)
{
while (*p != '$' && *p != '\0' && *p != '}' && *p != ':')
p++;
return p;
}
static find_t
find_pos(int c)
{
switch(c) {
case '(':
return find_rparen;
case '{':
return find_ket;
default:
Parse_Error(PARSE_FATAL,
"Wrong character in variable spec %c (can't happen)", c);
return find_rparen;
}
}
static bool
parse_base_variable_name(const char **pstr, struct Name *name, SymTable *ctxt)
{
const char *str = *pstr;
const char *tstr;
bool has_modifier = false;
switch(str[1]) {
case '(':
case '{':
tstr = VarName_Get(str+2, name, ctxt, false, find_pos(str[1]));
if (*tstr == '\0')
Parse_Error(PARSE_FATAL, "Unterminated variable spec in %s", *pstr);
else if (*tstr == ':')
has_modifier = true;
else
tstr++;
break;
default:
name->s = str+1;
name->e = str+2;
name->tofree = false;
tstr = str + 2;
break;
}
*pstr = tstr;
return has_modifier;
}
bool
Var_ParseSkip(const char **pstr, SymTable *ctxt)
{
const char *str = *pstr;
struct Name name;
bool result;
bool has_modifier;
const char *tstr = str;
if (str[1] == 0) {
*pstr = str+1;
return false;
}
has_modifier = parse_base_variable_name(&tstr, &name, ctxt);
VarName_Free(&name);
result = true;
if (has_modifier) {
bool freePtr = false;
char *s = VarModifiers_Apply(NULL, NULL, ctxt, true, &freePtr,
&tstr, str[1]);
if (s == var_Error)
result = false;
if (freePtr)
free(s);
}
*pstr = tstr;
return result;
}
bool
Var_ParseBuffer(Buffer buf, const char *str, SymTable *ctxt, bool err,
size_t *lengthPtr)
{
char *result;
bool freeIt;
result = Var_Parse(str, ctxt, err, lengthPtr, &freeIt);
if (result == var_Error)
return false;
Buf_AddString(buf, result);
if (freeIt)
free(result);
return true;
}
#define MAX_DEPTH 350
static Var *call_trace[MAX_DEPTH];
static int current_depth = 0;
static void
push_used(Var *v)
{
if (v->flags & VAR_IN_USE) {
int i;
fprintf(stderr, "Problem with variable expansion chain: ");
for (i = 0;
i < (current_depth > MAX_DEPTH ? MAX_DEPTH : current_depth);
i++)
fprintf(stderr, "%s -> ", call_trace[i]->name);
fprintf(stderr, "%s\n", v->name);
Fatal("\tVariable %s is recursive.", v->name);
}
v->flags |= VAR_IN_USE;
if (current_depth < MAX_DEPTH)
call_trace[current_depth] = v;
current_depth++;
}
static void
pop_used(Var *v)
{
v->flags &= ~VAR_IN_USE;
current_depth--;
}
static char *
get_expanded_value(const char *name, const char *ename, int idx, uint32_t k,
char ext, SymTable *ctxt, bool err, bool *freePtr)
{
char *val;
if (idx == GLOBAL_INDEX) {
Var *v = find_global_var(name, ename, k);
if (v == NULL)
return NULL;
if ((v->flags & POISONS) != 0)
poison_check(v);
if ((v->flags & VAR_DUMMY) != 0)
return NULL;
val = var_get_value(v);
if (strchr(val, '$') != NULL) {
push_used(v);
val = Var_Subst(val, ctxt, err);
pop_used(v);
*freePtr = true;
}
} else {
if (ctxt != NULL) {
val = ctxt->locals[idx];
} else
val = NULL;
if (val == NULL)
return NULL;
if (ext == 'F') {
val = Var_GetTail(val);
*freePtr = true;
} else if (ext == 'D') {
val = Var_GetHead(val);
*freePtr = true;
}
}
return val;
}
#define ERRMSG1 "Using $< in a non-suffix rule context is a GNUmake idiom "
#define ERRMSG2 "Using undefined dynamic variable $%s "
static void
bad_dynamic_variable(int idx)
{
Location origin;
Parse_FillLocation(&origin);
switch(idx) {
case IMPSRC_INDEX:
if (origin.fname)
Fatal(ERRMSG1 "(%s:%lu)",
origin.fname, origin.lineno);
else if (current_node)
Fatal(ERRMSG1 "(prereq of %s)", current_node->name);
else
Fatal(ERRMSG1 "(?)");
break;
default:
if (origin.fname)
Error(ERRMSG2 "(%s:%lu)", varnames[idx],
origin.fname, origin.lineno);
else if (current_node)
Error(ERRMSG2 "(prereq of %s)", varnames[idx],
current_node->name);
else
Error(ERRMSG2 "(?)", varnames[idx]);
break;
}
}
char *
Var_Parse(const char *str,
SymTable *ctxt,
bool err,
size_t *lengthPtr,
bool *freePtr)
{
const char *tstr;
struct Name name;
char *val;
uint32_t k;
int idx;
bool has_modifier;
char ext;
*freePtr = false;
tstr = str;
if (str[1] == 0) {
*lengthPtr = 1;
*freePtr = false;
return err ? var_Error : varNoError;
}
has_modifier = parse_base_variable_name(&tstr, &name, ctxt);
idx = classify_var(name.s, &name.e, &k, &ext);
val = get_expanded_value(name.s, name.e, idx, k, ext, ctxt, err,
freePtr);
if (has_modifier) {
val = VarModifiers_Apply(val, &name, ctxt, err, freePtr,
&tstr, str[1]);
}
if (val == NULL) {
val = err ? var_Error : varNoError;
if (idx != GLOBAL_INDEX) {
if (ctxt == NULL) {
*freePtr = true;
val = Str_dupi(str, tstr);
} else {
bad_dynamic_variable(idx);
}
}
}
VarName_Free(&name);
*lengthPtr = tstr - str;
return val;
}
char *
Var_Subst(const char *str,
SymTable *ctxt,
bool undefErr)
{
BUFFER buf;
static bool errorReported;
Buf_Init(&buf, MAKE_BSIZE);
errorReported = false;
for (;;) {
char *val;
size_t length;
bool doFree;
const char *cp;
for (cp = str; *str != '\0' && *str != '$'; str++)
;
Buf_Addi(&buf, cp, str);
if (*str == '\0')
break;
if (str[1] == '$') {
Buf_AddChar(&buf, '$');
str += 2;
continue;
}
val = Var_Parse(str, ctxt, undefErr, &length, &doFree);
if (val == var_Error || val == varNoError) {
if (errorIsOkay)
str += length;
else if (undefErr) {
if (!errorReported)
Parse_Error(PARSE_FATAL,
"Undefined variable \"%.*s\"",
(int)length, str);
str += length;
errorReported = true;
} else {
Buf_AddChar(&buf, *str);
str++;
}
} else {
str += length;
Buf_AddString(&buf, val);
if (doFree)
free(val);
}
}
return Buf_Retrieve(&buf);
}
bool
Var_Check_for_target(const char *str)
{
bool seen_target = false;
for (;;) {
const char *tstr;
uint32_t k;
int idx;
bool has_modifier;
struct Name name;
char ext;
for (; *str != '\0' && *str != '$'; str++)
;
if (*str == '\0')
break;
if (str[1] == '$') {
str += 2;
continue;
}
tstr = str;
has_modifier = parse_base_variable_name(&tstr, &name, NULL);
idx = classify_var(name.s, &name.e, &k, &ext);
if (has_modifier) {
bool doFree = false;
char *val = VarModifiers_Apply(NULL, NULL, NULL, false,
&doFree, &tstr, str[1]);
if (doFree)
free(val);
}
if (tlist[idx])
seen_target = true;
VarName_Free(&name);
str = tstr;
}
return seen_target;
}
static BUFFER subst_buffer;
char *
Var_Substi(const char *str, const char *estr, SymTable *ctxt, bool undefErr)
{
if (estr == NULL || *estr == '\0')
return Var_Subst(str, ctxt, undefErr);
Buf_Reset(&subst_buffer);
Buf_Addi(&subst_buffer, str, estr);
return Var_Subst(Buf_Retrieve(&subst_buffer), ctxt, undefErr);
}
struct LoopVar
{
Var old;
Var *me;
};
struct LoopVar *
Var_NewLoopVar(const char *name, const char *ename)
{
struct LoopVar *l;
uint32_t k;
l = emalloc(sizeof(struct LoopVar));
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 | VAR_DUMMY;
return l;
}
char *
Var_LoopVarName(struct LoopVar *v)
{
return v->me->name;
}
void
Var_DeleteLoopVar(struct LoopVar *l)
{
if ((l->me->flags & VAR_DUMMY) == 0)
Buf_Destroy(&(l->me->val));
*(l->me) = l->old;
free(l);
}
void
Var_SubstVar(Buffer buf,
const char *str,
struct LoopVar *l,
const char *val)
{
const char *var = l->me->name;
var_set_value(l->me, val);
for (;;) {
const char *start;
for (start = str; *str != '\0' && *str != '$'; str++)
;
Buf_Addi(buf, start, str);
start = str;
if (*str++ == '\0')
break;
str++;
if (start[1] == '$') {
Buf_Addi(buf, start, start+2);
continue;
}
if (start[1] != '(' && start[1] != '{') {
if (start[1] != *var || var[1] != '\0') {
Buf_AddChars(buf, 2, start);
continue;
}
} else {
const char *p;
char paren = start[1];
p = find_pos(paren)(str);
if (*p == '$') {
Buf_Addi(buf, start, p);
str = p;
continue;
}
if (strncmp(var, str, p - str) != 0 ||
var[p - str] != '\0') {
Buf_Addi(buf, start, p);
str = p;
continue;
}
if (*p == ':') {
bool doFree;
char *newval;
struct Name name;
doFree = false;
name.s = var;
name.e = var + (p-str);
newval = VarModifiers_Apply((char *)val,
&name, NULL, false, &doFree, &p, paren);
Buf_AddString(buf, newval);
if (doFree)
free(newval);
str = p;
continue;
} else
str = p+1;
}
Buf_AddString(buf, val);
}
}
static void
set_magic_shell_variable(void)
{
const char *name = "SHELL";
const char *ename = NULL;
uint32_t k;
Var *v;
k = ohash_interval(name, &ename);
v = find_global_var_without_env(name, ename, k);
var_set_value(v, _PATH_BSHELL);
v->flags = VAR_IS_SHELL | VAR_SEEN_ENV;
}
static void
set_magic_name_list_variable(void)
{
const char *name = VARNAME_LIST;
const char *ename = NULL;
uint32_t k;
Var *v;
k = ohash_interval(name, &ename);
v = find_global_var_without_env(name, ename, k);
var_set_initial_value(v, "");
v->flags = VAR_IS_NAMES;
}
void
Var_Init(void)
{
ohash_init(&global_variables, 10, &var_info);
set_magic_shell_variable();
set_magic_name_list_variable();
errorIsOkay = true;
Var_setCheckEnvFirst(false);
VarModifiers_Init();
Buf_Init(&subst_buffer, MAKE_BSIZE);
}
static const char *interpret(int);
static const char *
interpret(int f)
{
if (f & VAR_DUMMY)
return "(D)";
return "";
}
static void
print_var(Var *v)
{
printf("%-16s%s = %s\n", v->name, interpret(v->flags),
(v->flags & VAR_DUMMY) == 0 ? var_get_value(v) : "(none)");
}
void
Var_Dump(void)
{
Var **t;
unsigned int i;
const char *banner;
bool first = true;
t = sort_ohash_by_name(&global_variables);
#define LOOP(mask, value, do_stuff) \
for (i = 0; t[i] != NULL; i++) \
if ((t[i]->flags & (mask)) == (value)) { \
if (banner) { \
if (first) \
first = false; \
else \
putchar('\n'); \
fputs(banner, stdout); \
banner = NULL; \
} \
do_stuff; \
}
banner = "#variables from command line:\n";
LOOP(VAR_FROM_CMD | VAR_DUMMY, VAR_FROM_CMD, print_var(t[i]));
banner = "#global variables:\n";
LOOP(VAR_FROM_ENV| VAR_FROM_CMD | VAR_DUMMY, 0, print_var(t[i]));
banner = "#variables from env:\n";
LOOP(VAR_FROM_ENV|VAR_DUMMY, VAR_FROM_ENV, print_var(t[i]));
banner = "#variable name seen, but not defined:";
LOOP(VAR_DUMMY|POISONS, VAR_DUMMY, printf(" %s", t[i]->name));
#undef LOOP
printf("\n\n");
for (i = 0; t[i] != NULL; i++)
switch(t[i]->flags & POISONS) {
case POISON_NORMAL:
printf(".poison %s\n", t[i]->name);
break;
case POISON_EMPTY:
printf(".poison empty(%s)\n", t[i]->name);
break;
case POISON_NOT_DEFINED:
printf(".poison !defined(%s)\n", t[i]->name);
break;
default:
break;
}
free(t);
printf("\n");
}
static const char *quotable = " \t\n\\'\"";
void
Var_AddCmdline(const char *name)
{
Var *v;
unsigned int i;
BUFFER buf;
char *s;
Buf_Init(&buf, MAKE_BSIZE);
for (v = ohash_first(&global_variables, &i); v != NULL;
v = ohash_next(&global_variables, &i)) {
if (!(v->flags & VAR_FROM_CMD)) {
continue;
}
Buf_AddString(&buf, v->name);
Buf_AddChar(&buf, '=');
for (s = var_get_value(v); *s != '\0'; s++) {
if (strchr(quotable, *s))
Buf_AddChar(&buf, '\\');
Buf_AddChar(&buf, *s);
}
Buf_AddSpace(&buf);
}
Var_Append(name, Buf_Retrieve(&buf));
Buf_Destroy(&buf);
}