(void)VarAdd(name, val, ctxt);
v = VarAdd(name, val, ctxt);
static Var *var_name_with_dollar __P((char *, char **, SymTable *, Boolean, char));voidSymTable_Init(ctxt)    SymTable	*ctxt;{    static SymTable sym_template;    memcpy(ctxt, &sym_template, sizeof(*ctxt));}voidSymTable_Destroy(ctxt)    SymTable	*ctxt;{    int i;    for (i = 0; i < LOCAL_SIZE; i++)	if (ctxt->locals[i] != NULL)	    VarDelete(ctxt->locals[i]);}static intquick_lookup(name, end, pk)    const char *name;    const char **end;    u_int32_t *pk;{    size_t len;    *pk = hash_interval(name, end);    len = *end - name;	/* substitute short version for long local name */    switch (*pk % MAGICSLOTS) {		    /* MAGICSLOTS should be the    */    case K_LONGALLSRC % MAGICSLOTS:	    /* smallest constant yielding  */					    /* distinct case values        */	if (*pk == K_LONGALLSRC && strncmp(name, LONGALLSRC, len) == 0 &&	    len == strlen(LONGALLSRC))	    return ALLSRC_INDEX;	break;    case K_LONGARCHIVE % MAGICSLOTS:	if (*pk == K_LONGARCHIVE && strncmp(name, LONGARCHIVE, len) == 0 &&	    len == strlen(LONGARCHIVE))	    return ARCHIVE_INDEX;	break;    case K_LONGIMPSRC % MAGICSLOTS:	if (*pk == K_LONGIMPSRC && strncmp(name, LONGIMPSRC, len) == 0 &&	    len == strlen(LONGIMPSRC))	    return IMPSRC_INDEX;	break;    case K_LONGMEMBER % MAGICSLOTS:	if (*pk == K_LONGMEMBER && strncmp(name, LONGMEMBER, len) == 0 &&	    len == strlen(LONGMEMBER))	    return MEMBER_INDEX;	break;    case K_LONGOODATE % MAGICSLOTS:	if (*pk == K_LONGOODATE && strncmp(name, LONGOODATE, len) == 0 &&	    len == strlen(LONGOODATE))	    return OODATE_INDEX;	break;    case K_LONGPREFIX % MAGICSLOTS:	if (*pk == K_LONGPREFIX && strncmp(name, LONGPREFIX, len) == 0 &&	    len == strlen(LONGPREFIX))	    return PREFIX_INDEX;	break;    case K_LONGTARGET % MAGICSLOTS:	if (*pk == K_LONGTARGET && strncmp(name, LONGTARGET, len) == 0 &&	    len == strlen(LONGTARGET))	    return TARGET_INDEX;	break;    case K_TARGET % MAGICSLOTS:	if (name[0] == TARGET[0] && len == 1)	    return TARGET_INDEX;	break;    case K_OODATE % MAGICSLOTS:	if (name[0] == OODATE[0] && len == 1)	    return OODATE_INDEX;	break;    case K_ALLSRC % MAGICSLOTS:	if (name[0] == ALLSRC[0] && len == 1)	    return ALLSRC_INDEX;	break;    case K_IMPSRC % MAGICSLOTS:	if (name[0] == IMPSRC[0] && len == 1)	    return IMPSRC_INDEX;	break;    case K_PREFIX % MAGICSLOTS:	if (name[0] == PREFIX[0] && len == 1)	    return PREFIX_INDEX;	break;    case K_ARCHIVE % MAGICSLOTS:	if (name[0] == ARCHIVE[0] && len == 1)	    return ARCHIVE_INDEX;	break;    case K_MEMBER % MAGICSLOTS:	if (name[0] == MEMBER[0] && len == 1)	    return MEMBER_INDEX;	break;    default:	break;    }    return -1;}void Varq_Set(idx, val, gn)    int 	idx;    char 	*val;    GNode 	*gn;{    /* 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     * point in searching them all just to save a bit of memory...  */    Var *v = gn->context.locals[idx];    if (v == NULL) {    	v = new_var(varnames[idx], val);	v->flags = 0;	gn->context.locals[idx] = v;    } else {	Buf_Reset(&(v->val));	Buf_AddString(&(v->val), val);    }    if (DEBUG(VAR))	printf("%s:%s = %s\n", gn->name, varnames[idx], val);}void Varq_Append(idx, val, gn)    int		idx;    char	*val;    GNode	*gn;{    Var *v = gn->context.locals[idx];    if (v == NULL) {    	v = new_var(varnames[idx], val);	v->flags = 0;	gn->context.locals[idx] = v;    } else {	Buf_AddSpace(&(v->val));	Buf_AddString(&(v->val), val);    }    if (DEBUG(VAR))	printf("%s:%s = %s\n", gn->name, varnames[idx], VarValue(v));}char *Varq_Value(idx, gn)    int		idx;    GNode	*gn;{    Var *v = gn->context.locals[idx];    if (v != NULL)    	return VarValue(v);    else    	return NULL;}BooleanVarq_Exists(idx, gn)    int		idx;    GNode	*gn;{    return gn->context.locals[idx] != NULL;}static const char *context_name(ctxt)    GSymT *ctxt;{    if (ctxt == VAR_GLOBAL)    	return "Global";    else if (ctxt == VAR_CMD)    	return "Command";    else    	return "Environment";}    /* Create a variable, to pass to VarAdd.  */static Var *new_var(name, val)    const char *name;    const char *val;{    Var *v;    const char *end = NULL;    v = hash_create_entry(&var_info, name, &end);    if (val != NULL) {    	size_t len = strlen(val);	Buf_Init(&(v->val), len+1);	Buf_AddChars(&(v->val), len, val);    } else    	Buf_Init(&(v->val), 1);    return v;}static Var *getvar(ctxt, name, end, k)    GSymT	*ctxt;    const char 	*name;    const char	*end;    u_int32_t	k;{    return hash_find(ctxt, hash_lookup_interval(ctxt, name, end, k));}/*- *----------------------------------------------------------------------- * VarFind_interval -- *	Find the given variable in the given context and any other contexts *	indicated.  if end is NULL, name is a string, otherwise, only *      the interval name - end  is concerned. * * Results: *	A pointer to the structure describing the desired variable or *	NULL if the variable does not exist. * * Side Effects: *	Caches env variables in the VAR_ENV context. *----------------------------------------------------------------------- */static Var *VarFind_interval(name, end, ctxt, flags)    const char          *name;	/* name to find */    const char		*end;	/* end of name */    SymTable          	*ctxt;	/* context in which to find it */    int             	flags;	/* FIND_MINE set means to look in the				 * CTXT_GLOBAL and CTXT_CMD contexts also.				 * FIND_ENV set means to look in the				 * environment */{    int 		idx;    u_int32_t		k;    idx = quick_lookup(name, &end, &k);    return varfind(name, end, ctxt, flags, idx, k);}static Var *varfind(name, end, ctxt, flags, idx, k)    const char		*name;    const char		*end;    SymTable		*ctxt;    int			flags;    int			idx;    u_int32_t		k;{    Var			*v;    /*     * First look for the variable in the given context. If it's not there,     * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order,     * depending on the FIND_* flags in 'flags'     */    if (ctxt == NULL)    	v = NULL;    else if (ctxt == CTXT_GLOBAL || ctxt == CTXT_CMD || ctxt == CTXT_ENV)	v = getvar((GSymT *)ctxt, name, end, k);    else {    	if (idx == -1)	    v = NULL;	else	    v = ctxt->locals[idx];    }    if (v != NULL)    	return v;	        if ((flags & FIND_MINE) && ctxt != CTXT_CMD)	v = getvar(VAR_CMD, name, end, k);    if (v != NULL)    	return v;    if (!checkEnvFirst && (flags & FIND_MINE) && ctxt != CTXT_GLOBAL)	v = getvar(VAR_GLOBAL, name, end, k);    if (v != NULL)    	return v;    if ((flags & FIND_ENV)) {	char *env;	char *n;    	v = getvar(VAR_ENV, name, end, k);	if (v != NULL)	    return v;	/* getenv requires a null-terminated name */	n = interval_dup(name, end);	env = getenv(n);	free(n);	if (env != NULL)	    return VarAdd(name, env, VAR_ENV);    }    if (checkEnvFirst && (flags & FIND_MINE) && ctxt != CTXT_GLOBAL) 	v = getvar(VAR_GLOBAL, name, end, k);    return v;}/*- *----------------------------------------------------------------------- * VarAdd  -- *	Add a new variable of name name and value val to the given context * * Results: *	The added variable * * Side Effects: *	The new variable is placed at the front of the given context *	The name and val arguments are duplicated so they may *	safely be freed. *----------------------------------------------------------------------- */static Var *VarAdd(name, val, ctxt)    const char	*name;	/* name of variable to add */    const char	*val;	/* value to set it to */    GSymT	*ctxt;	/* context in which to set it */{    Var   	*v;    const char 	*end = NULL;    int 	idx;    u_int32_t 	k;    v = new_var(name, val);    v->flags = 0;    idx = quick_lookup(name, &end, &k);    if (idx != -1) {    	Parse_Error(PARSE_FATAL, "Trying to set dynamic variable %s",	    v->name);    } else	hash_insert(ctxt, hash_lookup_interval(ctxt, name, end, k), v);    return v;}/*- *----------------------------------------------------------------------- * VarDelete  -- *	Delete a variable and all the space associated with it. * * Results: *	None * * Side Effects: *	None *----------------------------------------------------------------------- */static voidVarDelete(vp)    void *vp;{    Var *v = (Var *) vp;    Buf_Destroy(&(v->val));    free(v);}/*- *----------------------------------------------------------------------- * Var_Delete -- *	Remove a variable from a context. * * Results: *	None. * * Side Effects: *	The Var structure is removed and freed. * *----------------------------------------------------------------------- */voidVar_Delete(name, ctxt)    char    	  *name;    GSymT	  *ctxt;{    Var *v;    u_int32_t k;    const char *end = NULL;    if (DEBUG(VAR))	printf("%s:delete %s\n", context_name(ctxt), name);    (void)quick_lookup(name, &end, &k);    v = hash_remove(ctxt, hash_lookup_interval(ctxt, name, end, k));    if (v != NULL)	VarDelete(v);}/*- *----------------------------------------------------------------------- * Var_Set -- *	Set the variable name to the value val in the given context. * * Results: *	None. * * Side Effects: *	If the variable doesn't yet exist, a new record is created for it. *	Else the old value is freed and the new one stuck in its place * * Notes: *	The variable is searched for only in its context before being *	created in that context. I.e. if the context is VAR_GLOBAL, *	only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only *	VAR_CMD->context is searched. This is done to avoid the literally *	thousands of unnecessary strcmp's that used to be done to *	set, say, $(@) or $(<). *----------------------------------------------------------------------- */voidVar_Set(name, val, ctxt)    char	*name;	/* name of variable to set */    char	*val;	/* value to give to the variable */    GSymT       *ctxt;	/* context in which to set it */{    register Var   *v;    /*     * 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     * point in searching them all just to save a bit of memory...     */    v = VarFind(name, (SymTable *)ctxt, 0);    if (v == NULL)	(void)VarAdd(name, val, ctxt);    else {	Buf_Reset(&(v->val));	Buf_AddString(&(v->val), val);    }    if (DEBUG(VAR))	printf("%s:%s = %s\n", context_name(ctxt), name, val);    /*     * Any variables given on the command line are automatically exported     * to the environment (as per POSIX standard).       * We put them into the env cache directly.     * (Note that additions to VAR_CMD occur very early, so VAR_ENV is     * actually empty at this point).     */    if (ctxt == VAR_CMD) {	setenv(name, val, 1);	(void)VarAdd(name, val, VAR_ENV);    }}/*- *----------------------------------------------------------------------- * Var_Append -- *	The variable of the given name has the given value appended to it in *	the given context. * * Results: *	None * * Side Effects: *	If the variable doesn't exist, it is created. Else the strings *	are concatenated (with a space in between). * * Notes: *	Only if the variable is being sought in the global context 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 *	a local variable could be being appended to. This is actually *	a big win and must be tolerated. *----------------------------------------------------------------------- */voidVar_Append(name, val, ctxt)    char	*name;	/* Name of variable to modify */    char	*val;	/* String to append to it */    GSymT	*ctxt;	/* Context in which this should occur */{    register Var   *v;    v = VarFind(name, (SymTable *)ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0);    if (v == NULL) {	(void)VarAdd(name, val, ctxt);    } else {	Buf_AddSpace(&(v->val));	Buf_AddString(&(v->val), val);    }    if (DEBUG(VAR))	printf("%s:%s = %s\n", context_name(ctxt), name, VarValue(v));}/*- *----------------------------------------------------------------------- * Var_Exists -- *	See if the given variable exists. * * Results: *	TRUE if it does, FALSE if it doesn't * * Side Effects: *	None. * *----------------------------------------------------------------------- */BooleanVar_Exists(name, ctxt)    char	*name;    	/* Variable to find */    GSymT	*ctxt;    	/* Context in which to start search */{    Var	    	  *v;    v = VarFind(name, (SymTable *)ctxt, FIND_MINE|FIND_ENV);    if (v == NULL)	return FALSE;    else	return TRUE;}/*- *----------------------------------------------------------------------- * Var_Value -- *	Return the value of the named variable in the given context * * Results: *	The value if the variable exists, NULL if it doesn't * * Side Effects: *	None *----------------------------------------------------------------------- */char *Var_Value(name, ctxt)    char	*name;	/* name to find */    GSymT	*ctxt;	/* context in which to search for it */{    Var            *v;    v = VarFind(name, (SymTable *)ctxt, FIND_ENV | FIND_MINE);    if (v != NULL) 	return VarValue(v);    else	return NULL;}/* *----------------------------------------------------------------------- * v = var_name_with_dollar(str, &pos, ctxt, err, endc) *   handle variable name that contains a dollar *   str points to the first letter of the variable name, *   &pos to the current position in the name (first dollar at invocation, *   end of name specification at return). *   returns the corresponding variable. *----------------------------------------------------------------------- */static Var *var_name_with_dollar(str, pos, ctxt, err, endc)    char *str;			/* First dollar in variable name */    char **pos;			/* Current position in variable spec */    SymTable   	*ctxt;    	/* The context for the variable */    Boolean 	err;    	/* TRUE if undefined variables are an error */    char	endc;		/* End character for spec */{    BUFFER buf;			/* Store the variable name */    size_t sublen;		/* Deal with recursive expansions */    Boolean subfree;		    char *n; 			/* Sub name */    Var *v;    Buf_Init(&buf, MAKE_BSIZE);    while (1) {	Buf_AddInterval(&buf, str, *pos);	n = Var_Parse(*pos, ctxt, err, &sublen, &subfree);	if (n != NULL)	    Buf_AddString(&buf, n);	if (subfree)	    free(n);	*pos += sublen;	str = *pos;	for (; **pos != '$'; (*pos)++) {	    if (**pos == '\0' || **pos == endc || **pos == ':') {		v = VarFind(Buf_Retrieve(&buf), ctxt, FIND_ENV | FIND_MINE);		Buf_Destroy(&buf);		return v;	    }	}    }}