Module Name: src Committed By: rillig Date: Tue Oct 6 08:13:28 UTC 2020
Modified Files: src/usr.bin/make: var.c Log Message: make(1): rework memory allocation for the name of variables There's more to know about variable names than fits in a one-liner. While here, enforce that the name is not modified by splitting it into the established (var + var_freeIt) pattern. Since the name is not modified and not freed in the middle of evaluating an expression, there is no need to make a backup copy of it. That code had been necessary more than 12 years ago, but not anymore since the code got a lot cleaner since then. To generate a diff of this commit: cvs rdiff -u -r1.569 -r1.570 src/usr.bin/make/var.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/usr.bin/make/var.c diff -u src/usr.bin/make/var.c:1.569 src/usr.bin/make/var.c:1.570 --- src/usr.bin/make/var.c:1.569 Tue Oct 6 07:52:47 2020 +++ src/usr.bin/make/var.c Tue Oct 6 08:13:27 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: var.c,v 1.569 2020/10/06 07:52:47 rillig Exp $ */ +/* $NetBSD: var.c,v 1.570 2020/10/06 08:13:27 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -121,7 +121,7 @@ #include "metachar.h" /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: var.c,v 1.569 2020/10/06 07:52:47 rillig Exp $"); +MAKE_RCSID("$NetBSD: var.c,v 1.570 2020/10/06 08:13:27 rillig Exp $"); #define VAR_DEBUG1(fmt, arg1) DEBUG1(VAR, fmt, arg1) #define VAR_DEBUG2(fmt, arg1, arg2) DEBUG2(VAR, fmt, arg1, arg2) @@ -213,11 +213,31 @@ ENUM_FLAGS_RTTI_6(VarFlags, VAR_IN_USE, VAR_FROM_ENV, VAR_EXPORTED, VAR_REEXPORT, VAR_FROM_CMD, VAR_READONLY); +/* Variables are defined using one of the VAR=value assignments. Their + * 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, + * 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 + * not be possible to undefine a variable during the evaluation of an + * expression, or Var.name might point nowhere. + * + * Environment variables are temporary. They are returned by VarFind, and + * after using them, they must be freed using VarFreeEnv. + * + * Undefined variables occur during evaluation of variable expressions such + * as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers. + */ typedef struct Var { - char *name; /* the variable's name; it is allocated for - * environment variables and aliased to the - * Hash_Entry name for all other variables, - * and thus must not be modified */ + /* The name of the variable, once set, doesn't change anymore. + * For context variables, it aliases the corresponding Hash_Entry name. + * For environment and undefined variables, it is allocated. */ + const char *name; + void *name_freeIt; + Buffer val; /* its value */ VarFlags flags; /* miscellaneous status flags */ } Var; @@ -254,11 +274,12 @@ typedef enum { } VarPatternFlags; static Var * -VarNew(char *name, const char *value, VarFlags flags) +VarNew(const char *name, void *name_freeIt, const char *value, VarFlags flags) { size_t value_len = strlen(value); Var *var = bmake_malloc(sizeof *var); var->name = name; + var->name_freeIt = name_freeIt; Buf_Init(&var->val, value_len + 1); Buf_AddBytes(&var->val, value, value_len); var->flags = flags; @@ -360,8 +381,10 @@ VarFind(const char *name, GNode *ctxt, V if (var == NULL && (flags & FIND_ENV)) { char *env; - if ((env = getenv(name)) != NULL) - return VarNew(bmake_strdup(name), env, VAR_FROM_ENV); + if ((env = getenv(name)) != NULL) { + char *varname = bmake_strdup(name); + return VarNew(varname, varname, env, VAR_FROM_ENV); + } if (checkEnvFirst && (flags & FIND_GLOBAL) && ctxt != VAR_GLOBAL) { var = Hash_FindValue(&VAR_GLOBAL->context, name); @@ -394,7 +417,7 @@ VarFreeEnv(Var *v, Boolean destroy) { if (!(v->flags & VAR_FROM_ENV)) return FALSE; - free(v->name); + free(v->name_freeIt); Buf_Destroy(&v->val, destroy); free(v); return TRUE; @@ -406,7 +429,8 @@ static void VarAdd(const char *name, const char *val, GNode *ctxt, VarSet_Flags flags) { Hash_Entry *he = Hash_CreateEntry(&ctxt->context, name, NULL); - Var *v = VarNew(he->name, val, flags & VAR_SET_READONLY ? VAR_READONLY : 0); + Var *v = VarNew(he->name, NULL, val, + flags & VAR_SET_READONLY ? VAR_READONLY : 0); Hash_SetValue(he, v); if (!(ctxt->flags & INTERNAL)) { VAR_DEBUG3("%s:%s = %s\n", ctxt->name, name, val); @@ -436,8 +460,7 @@ Var_Delete(const char *name, GNode *ctxt unsetenv(v->name); if (strcmp(v->name, MAKE_EXPORTED) == 0) var_exportedVars = VAR_EXPORTED_NONE; - if (v->name != he->name) - free(v->name); + assert(v->name_freeIt == NULL); Hash_DeleteEntry(&ctxt->context, he); Buf_Destroy(&v->val, TRUE); free(v); @@ -2807,7 +2830,6 @@ static ApplyModifierResult ApplyModifier_Assign(const char **pp, ApplyModifiersState *st) { GNode *v_ctxt; - char *sv_name; char delim; char *val; VarParseResult res; @@ -2820,20 +2842,13 @@ ApplyModifier_Assign(const char **pp, Ap return AMR_UNKNOWN; /* "::<unrecognised>" */ - if (st->v->name[0] == 0) { + if (st->v->name[0] == '\0') { *pp = mod + 1; return AMR_BAD; } v_ctxt = st->ctxt; /* context where v belongs */ - sv_name = NULL; - if (st->exprFlags & VEF_UNDEF) { - /* - * We need to bmake_strdup() it in case ParseModifierPart() recurses. - */ - sv_name = st->v->name; - st->v->name = bmake_strdup(st->v->name); - } else if (st->ctxt != VAR_GLOBAL) { + if (!(st->exprFlags & VEF_UNDEF) && st->ctxt != VAR_GLOBAL) { Var *gv = VarFind(st->v->name, st->ctxt, 0); if (gv == NULL) v_ctxt = VAR_GLOBAL; @@ -2854,11 +2869,6 @@ ApplyModifier_Assign(const char **pp, Ap delim = st->startc == '(' ? ')' : '}'; res = ParseModifierPart(pp, delim, st->eflags, st, &val, NULL, NULL, NULL); - if (st->exprFlags & VEF_UNDEF) { - /* restore original name */ - free(st->v->name); - st->v->name = sv_name; - } if (res != VPR_OK) return AMR_CLEANUP; @@ -3630,7 +3640,7 @@ Var_Parse(const char **pp, GNode *ctxt, * At the end, after applying all modifiers, if the expression * is still undefined, Var_Parse will return an empty string * instead of the actually computed value. */ - v = VarNew(varname, "", 0); + v = VarNew(varname, varname, "", 0); exprFlags = VEF_UNDEF; } else free(varname); @@ -3716,7 +3726,7 @@ Var_Parse(const char **pp, GNode *ctxt, } if (nstr != Buf_GetAll(&v->val, NULL)) Buf_Destroy(&v->val, TRUE); - free(v->name); + free(v->name_freeIt); free(v); } *out_val = nstr;