diff options
Diffstat (limited to 'contrib/bmake/var.c')
-rw-r--r-- | contrib/bmake/var.c | 3577 |
1 files changed, 1670 insertions, 1907 deletions
diff --git a/contrib/bmake/var.c b/contrib/bmake/var.c index 441aae9c9371..c7e6b1b34fd4 100644 --- a/contrib/bmake/var.c +++ b/contrib/bmake/var.c @@ -1,4 +1,4 @@ -/* $NetBSD: var.c,v 1.224 2020/06/05 19:20:46 sjg Exp $ */ +/* $NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: var.c,v 1.224 2020/06/05 19:20:46 sjg Exp $"; +static char rcsid[] = "$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: var.c,v 1.224 2020/06/05 19:20:46 sjg Exp $"); +__RCSID("$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -102,11 +102,9 @@ __RCSID("$NetBSD: var.c,v 1.224 2020/06/05 19:20:46 sjg Exp $"); * Var_Value Return the value of a variable in a context or * NULL if the variable is undefined. * - * Var_Subst Substitute named variable, or all variables if - * NULL in a string using - * the given context as the top-most one. If the - * third argument is non-zero, Parse_Error is - * called if any variables are undefined. + * Var_Subst Substitute either a single variable or all + * variables in a string, using the given context as + * the top-most one. * * Var_Parse Parse a variable expansion from a string and * return the result and the number of characters @@ -124,8 +122,8 @@ __RCSID("$NetBSD: var.c,v 1.224 2020/06/05 19:20:46 sjg Exp $"); */ #include <sys/stat.h> -#ifndef NO_REGEX #include <sys/types.h> +#ifndef NO_REGEX #include <regex.h> #endif #include <ctype.h> @@ -134,6 +132,11 @@ __RCSID("$NetBSD: var.c,v 1.224 2020/06/05 19:20:46 sjg Exp $"); #include <time.h> #include "make.h" + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + #include "buf.h" #include "dir.h" #include "job.h" @@ -151,14 +154,14 @@ char **savedEnv = NULL; * to determine if there was an error in parsing -- easier than returning * a flag, as things outside this module don't give a hoot. */ -char var_Error[] = ""; +char var_Error[] = ""; /* * Similar to var_Error, but returned when the 'VARF_UNDEFERR' flag for * Var_Parse is not set. Why not just use a constant? Well, gcc likes * to condense identical string instances... */ -static char varNoError[] = ""; +static char varNoError[] = ""; /* * Traditionally we consume $$ during := like any other expansion. @@ -186,33 +189,35 @@ static Boolean save_dollars = FALSE; * The four contexts are searched in the reverse order from which they are * listed. */ -GNode *VAR_INTERNAL; /* variables from make itself */ -GNode *VAR_GLOBAL; /* variables from the makefile */ -GNode *VAR_CMD; /* variables defined on the command-line */ - -#define FIND_CMD 0x1 /* look in VAR_CMD when searching */ -#define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */ -#define FIND_ENV 0x4 /* look in the environment also */ +GNode *VAR_INTERNAL; /* variables from make itself */ +GNode *VAR_GLOBAL; /* variables from the makefile */ +GNode *VAR_CMD; /* variables defined on the command-line */ + +#define FIND_CMD 0x1 /* look in VAR_CMD when searching */ +#define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */ +#define FIND_ENV 0x4 /* look in the environment also */ + +typedef enum { + VAR_IN_USE = 0x01, /* Variable's value is currently being used. + * Used to avoid endless recursion */ + VAR_FROM_ENV = 0x02, /* Variable comes from the environment */ + VAR_JUNK = 0x04, /* Variable is a junk variable that + * should be destroyed when done with + * it. Used by Var_Parse for undefined, + * modified variables */ + VAR_KEEP = 0x08, /* Variable is VAR_JUNK, but we found + * a use for it in some modifier and + * the value is therefore valid */ + VAR_EXPORTED = 0x10, /* Variable is exported */ + VAR_REEXPORT = 0x20, /* Indicate if var needs re-export. + * This would be true if it contains $'s */ + VAR_FROM_CMD = 0x40 /* Variable came from command line */ +} Var_Flags; typedef struct Var { char *name; /* the variable's name */ Buffer val; /* its value */ - int flags; /* miscellaneous status flags */ -#define VAR_IN_USE 1 /* Variable's value currently being used. - * Used to avoid recursion */ -#define VAR_FROM_ENV 2 /* Variable comes from the environment */ -#define VAR_JUNK 4 /* Variable is a junk variable that - * should be destroyed when done with - * it. Used by Var_Parse for undefined, - * modified variables */ -#define VAR_KEEP 8 /* Variable is VAR_JUNK, but we found - * a use for it in some modifier and - * the value is therefore valid */ -#define VAR_EXPORTED 16 /* Variable is exported */ -#define VAR_REEXPORT 32 /* Indicate if var needs re-export. - * This would be true if it contains $'s - */ -#define VAR_FROM_CMD 64 /* Variable came from command line */ + Var_Flags flags; /* miscellaneous status flags */ } Var; /* @@ -232,16 +237,18 @@ static int var_exportedVars = VAR_EXPORTED_NONE; */ #define VAR_EXPORT_LITERAL 2 -/* Var*Pattern flags */ -#define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */ -#define VAR_SUB_ONE 0x02 /* Apply substitution to one word */ -#define VAR_SUB_MATCHED 0x04 /* There was a match */ -#define VAR_MATCH_START 0x08 /* Match at start of word */ -#define VAR_MATCH_END 0x10 /* Match at end of word */ -#define VAR_NOSUBST 0x20 /* don't expand vars in VarGetPattern */ +typedef enum { + VAR_SUB_GLOBAL = 0x01, /* Apply substitution globally */ + VAR_SUB_ONE = 0x02, /* Apply substitution to one word */ + VAR_SUB_MATCHED = 0x04, /* There was a match */ + VAR_MATCH_START = 0x08, /* Match at start of word */ + VAR_MATCH_END = 0x10, /* Match at end of word */ + VAR_NOSUBST = 0x20 /* don't expand vars in VarGetPattern */ +} VarPattern_Flags; -/* Var_Set flags */ -#define VAR_NO_EXPORT 0x01 /* do not export */ +typedef enum { + VAR_NO_EXPORT = 0x01 /* do not export */ +} VarSet_Flags; typedef struct { /* @@ -260,11 +267,11 @@ typedef struct { /* struct passed as 'void *' to VarSubstitute() for ":S/lhs/rhs/", * to VarSYSVMatch() for ":lhs=rhs". */ typedef struct { - const char *lhs; /* String to match */ - int leftLen; /* Length of string */ - const char *rhs; /* Replacement string (w/ &'s removed) */ - int rightLen; /* Length of replacement */ - int flags; + const char *lhs; /* String to match */ + int leftLen; /* Length of string */ + const char *rhs; /* Replacement string (w/ &'s removed) */ + int rightLen; /* Length of replacement */ + VarPattern_Flags flags; } VarPattern; /* struct passed as 'void *' to VarLoopExpand() for ":@tvar@str@" */ @@ -274,8 +281,8 @@ typedef struct { int tvarLen; char *str; /* string to expand */ int strLen; - int errnum; /* errnum for not defined */ -} VarLoop_t; + Varf_Flags flags; +} VarLoop; #ifndef NO_REGEX /* struct passed as 'void *' to VarRESubstitute() for ":C///" */ @@ -294,47 +301,6 @@ typedef struct { int end; /* last word to select */ } VarSelectWords_t; -static Var *VarFind(const char *, GNode *, int); -static void VarAdd(const char *, const char *, GNode *); -static Boolean VarHead(GNode *, Var_Parse_State *, - char *, Boolean, Buffer *, void *); -static Boolean VarTail(GNode *, Var_Parse_State *, - char *, Boolean, Buffer *, void *); -static Boolean VarSuffix(GNode *, Var_Parse_State *, - char *, Boolean, Buffer *, void *); -static Boolean VarRoot(GNode *, Var_Parse_State *, - char *, Boolean, Buffer *, void *); -static Boolean VarMatch(GNode *, Var_Parse_State *, - char *, Boolean, Buffer *, void *); -#ifdef SYSVVARSUB -static Boolean VarSYSVMatch(GNode *, Var_Parse_State *, - char *, Boolean, Buffer *, void *); -#endif -static Boolean VarNoMatch(GNode *, Var_Parse_State *, - char *, Boolean, Buffer *, void *); -#ifndef NO_REGEX -static void VarREError(int, regex_t *, const char *); -static Boolean VarRESubstitute(GNode *, Var_Parse_State *, - char *, Boolean, Buffer *, void *); -#endif -static Boolean VarSubstitute(GNode *, Var_Parse_State *, - char *, Boolean, Buffer *, void *); -static Boolean VarLoopExpand(GNode *, Var_Parse_State *, - char *, Boolean, Buffer *, void *); -static char *VarGetPattern(GNode *, Var_Parse_State *, - int, const char **, int, int *, int *, - VarPattern *); -static char *VarQuote(char *, Boolean); -static char *VarHash(char *); -static char *VarModify(GNode *, Var_Parse_State *, - const char *, - Boolean (*)(GNode *, Var_Parse_State *, char *, Boolean, Buffer *, void *), - void *); -static char *VarOrder(const char *, const char); -static char *VarUniq(const char *); -static int VarWordCompare(const void *, const void *); -static void VarPrintVar(void *); - #define BROPEN '{' #define BRCLOSE '}' #define PROPEN '(' @@ -368,45 +334,47 @@ VarFind(const char *name, GNode *ctxt, int flags) Hash_Entry *var; Var *v; - /* - * If the variable name begins with a '.', it could very well be one of - * the local ones. We check the name against all the local variables - * and substitute the short version in for 'name' if it matches one of - * them. - */ - if (*name == '.' && isupper((unsigned char) name[1])) - switch (name[1]) { - case 'A': - if (!strcmp(name, ".ALLSRC")) - name = ALLSRC; - if (!strcmp(name, ".ARCHIVE")) - name = ARCHIVE; - break; - case 'I': - if (!strcmp(name, ".IMPSRC")) - name = IMPSRC; - break; - case 'M': - if (!strcmp(name, ".MEMBER")) - name = MEMBER; - break; - case 'O': - if (!strcmp(name, ".OODATE")) - name = OODATE; - break; - case 'P': - if (!strcmp(name, ".PREFIX")) - name = PREFIX; - break; - case 'T': - if (!strcmp(name, ".TARGET")) - name = TARGET; - break; - } + /* + * If the variable name begins with a '.', it could very well be one of + * the local ones. We check the name against all the local variables + * and substitute the short version in for 'name' if it matches one of + * them. + */ + if (*name == '.' && isupper((unsigned char) name[1])) { + switch (name[1]) { + case 'A': + if (!strcmp(name, ".ALLSRC")) + name = ALLSRC; + if (!strcmp(name, ".ARCHIVE")) + name = ARCHIVE; + break; + case 'I': + if (!strcmp(name, ".IMPSRC")) + name = IMPSRC; + break; + case 'M': + if (!strcmp(name, ".MEMBER")) + name = MEMBER; + break; + case 'O': + if (!strcmp(name, ".OODATE")) + name = OODATE; + break; + case 'P': + if (!strcmp(name, ".PREFIX")) + name = PREFIX; + break; + case 'T': + if (!strcmp(name, ".TARGET")) + name = TARGET; + break; + } + } + #ifdef notyet /* for compatibility with gmake */ if (name[0] == '^' && name[1] == '\0') - name = ALLSRC; + name = ALLSRC; #endif /* @@ -416,19 +384,19 @@ VarFind(const char *name, GNode *ctxt, int flags) */ var = Hash_FindEntry(&ctxt->context, name); - if ((var == NULL) && (flags & FIND_CMD) && (ctxt != VAR_CMD)) { + if (var == NULL && (flags & FIND_CMD) && ctxt != VAR_CMD) { var = Hash_FindEntry(&VAR_CMD->context, name); } - if (!checkEnvFirst && (var == NULL) && (flags & FIND_GLOBAL) && - (ctxt != VAR_GLOBAL)) + if (!checkEnvFirst && var == NULL && (flags & FIND_GLOBAL) && + ctxt != VAR_GLOBAL) { var = Hash_FindEntry(&VAR_GLOBAL->context, name); - if ((var == NULL) && (ctxt != VAR_INTERNAL)) { + if (var == NULL && ctxt != VAR_INTERNAL) { /* VAR_INTERNAL is subordinate to VAR_GLOBAL */ var = Hash_FindEntry(&VAR_INTERNAL->context, name); } } - if ((var == NULL) && (flags & FIND_ENV)) { + if (var == NULL && (flags & FIND_ENV)) { char *env; if ((env = getenv(name)) != NULL) { @@ -443,18 +411,18 @@ VarFind(const char *name, GNode *ctxt, int flags) Buf_AddBytes(&v->val, len, env); v->flags = VAR_FROM_ENV; - return (v); + return v; } else if (checkEnvFirst && (flags & FIND_GLOBAL) && - (ctxt != VAR_GLOBAL)) + ctxt != VAR_GLOBAL) { var = Hash_FindEntry(&VAR_GLOBAL->context, name); - if ((var == NULL) && (ctxt != VAR_INTERNAL)) { + if (var == NULL && ctxt != VAR_INTERNAL) { var = Hash_FindEntry(&VAR_INTERNAL->context, name); } if (var == NULL) { return NULL; } else { - return ((Var *)Hash_GetValue(var)); + return (Var *)Hash_GetValue(var); } } else { return NULL; @@ -462,7 +430,7 @@ VarFind(const char *name, GNode *ctxt, int flags) } else if (var == NULL) { return NULL; } else { - return ((Var *)Hash_GetValue(var)); + return (Var *)Hash_GetValue(var); } } @@ -503,9 +471,6 @@ VarFreeEnv(Var *v, Boolean destroy) * val value to set it to * ctxt context in which to set it * - * Results: - * None - * * Side Effects: * The new variable is placed at the front of the given context * The name and val arguments are duplicated so they may @@ -522,7 +487,7 @@ VarAdd(const char *name, const char *val, GNode *ctxt) v = bmake_malloc(sizeof(Var)); len = val ? strlen(val) : 0; - Buf_Init(&v->val, len+1); + Buf_Init(&v->val, len + 1); Buf_AddBytes(&v->val, len, val); v->flags = 0; @@ -540,9 +505,6 @@ VarAdd(const char *name, const char *val, GNode *ctxt) * Var_Delete -- * Remove a variable from a context. * - * Results: - * None. - * * Side Effects: * The Var structure is removed and freed. * @@ -553,7 +515,7 @@ Var_Delete(const char *name, GNode *ctxt) { Hash_Entry *ln; char *cp; - + if (strchr(name, '$')) { cp = Var_Subst(NULL, name, VAR_GLOBAL, VARF_WANTRES); } else { @@ -578,7 +540,7 @@ Var_Delete(const char *name, GNode *ctxt) var_exportedVars = VAR_EXPORTED_NONE; } if (v->name != ln->name) - free(v->name); + free(v->name); Hash_DeleteEntry(&ctxt->context, ln); Buf_Destroy(&v->val, TRUE); free(v); @@ -603,7 +565,7 @@ Var_Export1(const char *name, int flags) int parent = (flags & VAR_EXPORT_PARENT); if (*name == '.') - return 0; /* skip internals */ + return 0; /* skip internals */ if (!name[1]) { /* * A single char. @@ -668,6 +630,13 @@ Var_Export1(const char *name, int flags) return 1; } +static void +Var_ExportVars_callback(void *entry, void *unused MAKE_ATTR_UNUSED) +{ + Var *var = entry; + Var_Export1(var->name, 0); +} + /* * This gets called from our children. */ @@ -675,9 +644,6 @@ void Var_ExportVars(void) { char tmp[BUFSIZ]; - Hash_Entry *var; - Hash_Search state; - Var *v; char *val; int n; @@ -694,15 +660,8 @@ Var_ExportVars(void) return; if (VAR_EXPORTED_ALL == var_exportedVars) { - /* - * Ouch! This is crazy... - */ - for (var = Hash_EnumFirst(&VAR_GLOBAL->context, &state); - var != NULL; - var = Hash_EnumNext(&state)) { - v = (Var *)Hash_GetValue(var); - Var_Export1(v->name, 0); - } + /* Ouch! This is crazy... */ + Hash_ForEach(&VAR_GLOBAL->context, Var_ExportVars_callback, NULL); return; } /* @@ -808,7 +767,7 @@ Var_UnExport(char *str) int n; if (!str || !str[0]) { - return; /* assert? */ + return; /* assert? */ } vlist = NULL; @@ -880,7 +839,7 @@ Var_UnExport(char *str) "${" MAKE_EXPORTED ":N%s}", v->name); if (n < (int)sizeof(tmp)) { cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL, 0); + Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL); free(cp); } } @@ -894,38 +853,11 @@ Var_UnExport(char *str) } } -/*- - *----------------------------------------------------------------------- - * Var_Set -- - * Set the variable name to the value val in the given context. - * - * Input: - * name name of variable to set - * val value to give to the variable - * ctxt context in which to set it - * - * 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 $(<). - * If the context is VAR_GLOBAL though, we check if the variable - * was set in VAR_CMD from the command line and skip it if so. - *----------------------------------------------------------------------- - */ -void -Var_Set(const char *name, const char *val, GNode *ctxt, int flags) +static void +Var_Set_with_flags(const char *name, const char *val, GNode *ctxt, + VarSet_Flags flags) { - Var *v; + Var *v; char *expanded_name = NULL; /* @@ -1008,7 +940,7 @@ Var_Set(const char *name, const char *val, GNode *ctxt, int flags) save_dollars = s2Boolean(val, save_dollars); } - out: +out: free(expanded_name); if (v != NULL) VarFreeEnv(v, TRUE); @@ -1016,6 +948,37 @@ Var_Set(const char *name, const char *val, GNode *ctxt, int flags) /*- *----------------------------------------------------------------------- + * Var_Set -- + * Set the variable name to the value val in the given context. + * + * Input: + * name name of variable to set + * val value to give to the variable + * ctxt context in which to set it + * + * 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 $(<). + * If the context is VAR_GLOBAL though, we check if the variable + * was set in VAR_CMD from the command line and skip it if so. + *----------------------------------------------------------------------- + */ +void +Var_Set(const char *name, const char *val, GNode *ctxt) +{ + Var_Set_with_flags(name, val, ctxt, 0); +} + +/*- + *----------------------------------------------------------------------- * Var_Append -- * The variable of the given name has the given value appended to it in * the given context. @@ -1025,9 +988,6 @@ Var_Set(const char *name, const char *val, GNode *ctxt, int flags) * val String to append to it * ctxt Context in which this should occur * - * Results: - * None - * * Side Effects: * If the variable doesn't exist, it is created. Else the strings * are concatenated (with a space in between). @@ -1044,8 +1004,8 @@ Var_Set(const char *name, const char *val, GNode *ctxt, int flags) void Var_Append(const char *name, const char *val, GNode *ctxt) { - Var *v; - Hash_Entry *h; + Var *v; + Hash_Entry *h; char *expanded_name = NULL; if (strchr(name, '$') != NULL) { @@ -1062,17 +1022,17 @@ Var_Append(const char *name, const char *val, GNode *ctxt) name = expanded_name; } - v = VarFind(name, ctxt, (ctxt == VAR_GLOBAL) ? (FIND_CMD|FIND_ENV) : 0); + v = VarFind(name, ctxt, ctxt == VAR_GLOBAL ? (FIND_CMD|FIND_ENV) : 0); if (v == NULL) { - Var_Set(name, val, ctxt, 0); + Var_Set(name, val, ctxt); } else if (ctxt == VAR_CMD || !(v->flags & VAR_FROM_CMD)) { Buf_AddByte(&v->val, ' '); Buf_AddBytes(&v->val, strlen(val), val); if (DEBUG(VAR)) { fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, - Buf_GetAll(&v->val, NULL)); + Buf_GetAll(&v->val, NULL)); } if (v->flags & VAR_FROM_ENV) { @@ -1119,11 +1079,11 @@ Var_Exists(const char *name, GNode *ctxt) v = VarFind(cp ? cp : name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV); free(cp); if (v == NULL) { - return(FALSE); - } else { - (void)VarFreeEnv(v, TRUE); + return FALSE; } - return(TRUE); + + (void)VarFreeEnv(v, TRUE); + return TRUE; } /*- @@ -1145,355 +1105,173 @@ Var_Exists(const char *name, GNode *ctxt) char * Var_Value(const char *name, GNode *ctxt, char **frp) { - Var *v; + Var *v; v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); *frp = NULL; - if (v != NULL) { - char *p = (Buf_GetAll(&v->val, NULL)); - if (VarFreeEnv(v, FALSE)) - *frp = p; - return p; - } else { + if (v == NULL) return NULL; - } + + char *p = (Buf_GetAll(&v->val, NULL)); + if (VarFreeEnv(v, FALSE)) + *frp = p; + return p; } -/*- - *----------------------------------------------------------------------- - * VarHead -- - * Remove the tail of the given word and place the result in the given - * buffer. - * - * Input: - * word Word to trim - * addSpace True if need to add a space to the buffer - * before sticking in the head - * buf Buffer in which to store it - * - * Results: - * TRUE if characters were added to the buffer (a space needs to be - * added to the buffer before the next word). + +/* This callback for VarModify gets a single word from an expression and + * typically adds a modification of this word to the buffer. It may also do + * nothing or add several words. * - * Side Effects: - * The trimmed word is added to the buffer. + * If addSpaces is TRUE, it must add a space before adding anything else to + * the buffer. * - *----------------------------------------------------------------------- - */ + * It returns the addSpace value for the next call of this callback. Typical + * return values are the current addSpaces or TRUE. */ +typedef Boolean (*VarModifyCallback)(GNode *ctxt, Var_Parse_State *vpstate, + const char *word, Boolean addSpace, Buffer *buf, void *data); + + +/* Callback function for VarModify to implement the :H modifier. + * Add the dirname of the given word to the buffer. */ static Boolean VarHead(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - char *word, Boolean addSpace, Buffer *buf, + const char *word, Boolean addSpace, Buffer *buf, void *dummy MAKE_ATTR_UNUSED) { - char *slash; + const char *slash = strrchr(word, '/'); - slash = strrchr(word, '/'); - if (slash != NULL) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - *slash = '\0'; - Buf_AddBytes(buf, strlen(word), word); - *slash = '/'; - return (TRUE); - } else { - /* - * If no directory part, give . (q.v. the POSIX standard) - */ - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); + if (addSpace && vpstate->varSpace) + Buf_AddByte(buf, vpstate->varSpace); + if (slash != NULL) + Buf_AddBytes(buf, slash - word, word); + else Buf_AddByte(buf, '.'); - } + return TRUE; } -/*- - *----------------------------------------------------------------------- - * VarTail -- - * Remove the head of the given word and place the result in the given - * buffer. - * - * Input: - * word Word to trim - * addSpace True if need to add a space to the buffer - * before adding the tail - * buf Buffer in which to store it - * - * Results: - * TRUE if characters were added to the buffer (a space needs to be - * added to the buffer before the next word). - * - * Side Effects: - * The trimmed word is added to the buffer. - * - *----------------------------------------------------------------------- - */ +/* Callback function for VarModify to implement the :T modifier. + * Add the basename of the given word to the buffer. */ static Boolean VarTail(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - char *word, Boolean addSpace, Buffer *buf, + const char *word, Boolean addSpace, Buffer *buf, void *dummy MAKE_ATTR_UNUSED) { - char *slash; + const char *slash = strrchr(word, '/'); + const char *base = slash != NULL ? slash + 1 : word; - if (addSpace && vpstate->varSpace) { + if (addSpace && vpstate->varSpace) Buf_AddByte(buf, vpstate->varSpace); - } - - slash = strrchr(word, '/'); - if (slash != NULL) { - *slash++ = '\0'; - Buf_AddBytes(buf, strlen(slash), slash); - slash[-1] = '/'; - } else { - Buf_AddBytes(buf, strlen(word), word); - } + Buf_AddBytes(buf, strlen(base), base); return TRUE; } -/*- - *----------------------------------------------------------------------- - * VarSuffix -- - * Place the suffix of the given word in the given buffer. - * - * Input: - * word Word to trim - * addSpace TRUE if need to add a space before placing the - * suffix in the buffer - * buf Buffer in which to store it - * - * Results: - * TRUE if characters were added to the buffer (a space needs to be - * added to the buffer before the next word). - * - * Side Effects: - * The suffix from the word is placed in the buffer. - * - *----------------------------------------------------------------------- - */ +/* Callback function for VarModify to implement the :E modifier. + * Add the filename suffix of the given word to the buffer, if it exists. */ static Boolean VarSuffix(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - char *word, Boolean addSpace, Buffer *buf, + const char *word, Boolean addSpace, Buffer *buf, void *dummy MAKE_ATTR_UNUSED) { - char *dot; + const char *dot = strrchr(word, '.'); + if (dot == NULL) + return addSpace; - dot = strrchr(word, '.'); - if (dot != NULL) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - *dot++ = '\0'; - Buf_AddBytes(buf, strlen(dot), dot); - dot[-1] = '.'; - addSpace = TRUE; - } - return addSpace; + if (addSpace && vpstate->varSpace) + Buf_AddByte(buf, vpstate->varSpace); + Buf_AddBytes(buf, strlen(dot + 1), dot + 1); + return TRUE; } -/*- - *----------------------------------------------------------------------- - * VarRoot -- - * Remove the suffix of the given word and place the result in the - * buffer. - * - * Input: - * word Word to trim - * addSpace TRUE if need to add a space to the buffer - * before placing the root in it - * buf Buffer in which to store it - * - * Results: - * TRUE if characters were added to the buffer (a space needs to be - * added to the buffer before the next word). - * - * Side Effects: - * The trimmed word is added to the buffer. - * - *----------------------------------------------------------------------- - */ +/* Callback function for VarModify to implement the :R modifier. + * Add the filename basename of the given word to the buffer. */ static Boolean VarRoot(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - char *word, Boolean addSpace, Buffer *buf, + const char *word, Boolean addSpace, Buffer *buf, void *dummy MAKE_ATTR_UNUSED) { - char *dot; + char *dot = strrchr(word, '.'); + size_t len = dot != NULL ? (size_t)(dot - word) : strlen(word); - if (addSpace && vpstate->varSpace) { + if (addSpace && vpstate->varSpace) Buf_AddByte(buf, vpstate->varSpace); - } - - dot = strrchr(word, '.'); - if (dot != NULL) { - *dot = '\0'; - Buf_AddBytes(buf, strlen(word), word); - *dot = '.'; - } else { - Buf_AddBytes(buf, strlen(word), word); - } + Buf_AddBytes(buf, len, word); return TRUE; } -/*- - *----------------------------------------------------------------------- - * VarMatch -- - * Place the word in the buffer if it matches the given pattern. - * Callback function for VarModify to implement the :M modifier. - * - * Input: - * word Word to examine - * addSpace TRUE if need to add a space to the buffer - * before adding the word, if it matches - * buf Buffer in which to store it - * pattern Pattern the word must match - * - * Results: - * TRUE if a space should be placed in the buffer before the next - * word. - * - * Side Effects: - * The word may be copied to the buffer. - * - *----------------------------------------------------------------------- - */ +/* Callback function for VarModify to implement the :M modifier. + * Place the word in the buffer if it matches the given pattern. */ static Boolean VarMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - char *word, Boolean addSpace, Buffer *buf, - void *pattern) + const char *word, Boolean addSpace, Buffer *buf, + void *data) { + const char *pattern = data; if (DEBUG(VAR)) - fprintf(debug_file, "VarMatch [%s] [%s]\n", word, (char *)pattern); - if (Str_Match(word, (char *)pattern)) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - Buf_AddBytes(buf, strlen(word), word); - } - return(addSpace); + fprintf(debug_file, "VarMatch [%s] [%s]\n", word, pattern); + if (!Str_Match(word, pattern)) + return addSpace; + if (addSpace && vpstate->varSpace) + Buf_AddByte(buf, vpstate->varSpace); + Buf_AddBytes(buf, strlen(word), word); + return TRUE; } #ifdef SYSVVARSUB -/*- - *----------------------------------------------------------------------- - * VarSYSVMatch -- - * Place the word in the buffer if it matches the given pattern. - * Callback function for VarModify to implement the System V % - * modifiers. - * - * Input: - * word Word to examine - * addSpace TRUE if need to add a space to the buffer - * before adding the word, if it matches - * buf Buffer in which to store it - * patp Pattern the word must match - * - * Results: - * TRUE if a space should be placed in the buffer before the next - * word. - * - * Side Effects: - * The word may be copied to the buffer. - * - *----------------------------------------------------------------------- - */ +/* Callback function for VarModify to implement the :%.from=%.to modifier. */ static Boolean VarSYSVMatch(GNode *ctx, Var_Parse_State *vpstate, - char *word, Boolean addSpace, Buffer *buf, - void *patp) + const char *word, Boolean addSpace, Buffer *buf, + void *data) { size_t len; char *ptr; Boolean hasPercent; - VarPattern *pat = (VarPattern *)patp; - char *varexp; + VarPattern *pat = data; if (addSpace && vpstate->varSpace) Buf_AddByte(buf, vpstate->varSpace); - addSpace = TRUE; - if ((ptr = Str_SYSVMatch(word, pat->lhs, &len, &hasPercent)) != NULL) { - varexp = Var_Subst(NULL, pat->rhs, ctx, VARF_WANTRES); + char *varexp = Var_Subst(NULL, pat->rhs, ctx, VARF_WANTRES); Str_SYSVSubst(buf, varexp, ptr, len, hasPercent); free(varexp); } else { Buf_AddBytes(buf, strlen(word), word); } - return(addSpace); + return TRUE; } #endif - -/*- - *----------------------------------------------------------------------- - * VarNoMatch -- - * Place the word in the buffer if it doesn't match the given pattern. - * Callback function for VarModify to implement the :N modifier. - * - * Input: - * word Word to examine - * addSpace TRUE if need to add a space to the buffer - * before adding the word, if it matches - * buf Buffer in which to store it - * pattern Pattern the word must match - * - * Results: - * TRUE if a space should be placed in the buffer before the next - * word. - * - * Side Effects: - * The word may be copied to the buffer. - * - *----------------------------------------------------------------------- - */ +/* Callback function for VarModify to implement the :N modifier. + * Place the word in the buffer if it doesn't match the given pattern. */ static Boolean VarNoMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - char *word, Boolean addSpace, Buffer *buf, - void *pattern) + const char *word, Boolean addSpace, Buffer *buf, + void *data) { - if (!Str_Match(word, (char *)pattern)) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - Buf_AddBytes(buf, strlen(word), word); - } - return(addSpace); + const char *pattern = data; + if (Str_Match(word, pattern)) + return addSpace; + if (addSpace && vpstate->varSpace) + Buf_AddByte(buf, vpstate->varSpace); + Buf_AddBytes(buf, strlen(word), word); + return TRUE; } - -/*- - *----------------------------------------------------------------------- - * VarSubstitute -- - * Perform a string-substitution on the given word, placing the - * result in the passed buffer. - * - * Input: - * word Word to modify - * addSpace True if space should be added before - * other characters - * buf Buffer for result - * patternp Pattern for substitution - * - * Results: - * TRUE if a space is needed before more characters are added. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* Callback function for VarModify to implement the :S,from,to, modifier. + * Perform a string substitution on the given word. */ static Boolean VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - char *word, Boolean addSpace, Buffer *buf, - void *patternp) + const char *word, Boolean addSpace, Buffer *buf, + void *data) { - int wordLen; /* Length of word */ - char *cp; /* General pointer */ - VarPattern *pattern = (VarPattern *)patternp; + int wordLen = strlen(word); + const char *cp; /* General pointer */ + VarPattern *pattern = data; - wordLen = strlen(word); if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) != (VAR_SUB_ONE|VAR_SUB_MATCHED)) { /* @@ -1502,44 +1280,44 @@ VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, */ if ((pattern->flags & VAR_MATCH_START) && (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) { + /* + * Anchored at start and beginning of word matches pattern + */ + if ((pattern->flags & VAR_MATCH_END) && + (wordLen == pattern->leftLen)) { /* - * Anchored at start and beginning of word matches pattern + * Also anchored at end and matches to the end (word + * is same length as pattern) add space and rhs only + * if rhs is non-null. */ - if ((pattern->flags & VAR_MATCH_END) && - (wordLen == pattern->leftLen)) { - /* - * Also anchored at end and matches to the end (word - * is same length as pattern) add space and rhs only - * if rhs is non-null. - */ - if (pattern->rightLen != 0) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - } - pattern->flags |= VAR_SUB_MATCHED; - } else if (pattern->flags & VAR_MATCH_END) { - /* - * Doesn't match to end -- copy word wholesale - */ - goto nosub; - } else { - /* - * Matches at start but need to copy in trailing characters - */ - if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){ - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; + if (pattern->rightLen != 0) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); } + addSpace = TRUE; Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - Buf_AddBytes(buf, wordLen - pattern->leftLen, - (word + pattern->leftLen)); - pattern->flags |= VAR_SUB_MATCHED; } + pattern->flags |= VAR_SUB_MATCHED; + } else if (pattern->flags & VAR_MATCH_END) { + /* + * Doesn't match to end -- copy word wholesale + */ + goto nosub; + } else { + /* + * Matches at start but need to copy in trailing characters + */ + if ((pattern->rightLen + wordLen - pattern->leftLen) != 0) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + } + Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); + Buf_AddBytes(buf, wordLen - pattern->leftLen, + (word + pattern->leftLen)); + pattern->flags |= VAR_SUB_MATCHED; + } } else if (pattern->flags & VAR_MATCH_START) { /* * Had to match at start of word and didn't -- copy whole word. @@ -1596,11 +1374,11 @@ VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, while (!done) { cp = Str_FindSubstring(word, pattern->lhs); if (cp != NULL) { - if (addSpace && (((cp - word) + pattern->rightLen) != 0)){ + if (addSpace && (((cp - word) + pattern->rightLen) != 0)) { Buf_AddByte(buf, vpstate->varSpace); addSpace = FALSE; } - Buf_AddBytes(buf, cp-word, word); + Buf_AddBytes(buf, cp - word, word); Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); wordLen -= (cp - word) + pattern->leftLen; word = cp + pattern->leftLen; @@ -1626,16 +1404,16 @@ VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, * before we add any more. If we didn't add any, just return * the previous value of addSpace. */ - return ((Buf_Size(buf) != origSize) || addSpace); + return (Buf_Size(buf) != origSize) || addSpace; } - return (addSpace); + return addSpace; } - nosub: +nosub: if (addSpace && vpstate->varSpace) { Buf_AddByte(buf, vpstate->varSpace); } Buf_AddBytes(buf, wordLen, word); - return(TRUE); + return TRUE; } #ifndef NO_REGEX @@ -1644,9 +1422,6 @@ VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, * VarREError -- * Print the error caused by a regcomp or regexec call. * - * Results: - * None. - * * Side Effects: * An error gets printed. * @@ -1665,32 +1440,19 @@ VarREError(int reerr, regex_t *pat, const char *str) free(errbuf); } - -/*- - *----------------------------------------------------------------------- - * VarRESubstitute -- - * Perform a regex substitution on the given word, placing the - * result in the passed buffer. - * - * Results: - * TRUE if a space is needed before more characters are added. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* Callback function for VarModify to implement the :C/from/to/ modifier. + * Perform a regex substitution on the given word. */ static Boolean VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate MAKE_ATTR_UNUSED, - char *word, Boolean addSpace, Buffer *buf, - void *patternp) + const char *word, Boolean addSpace, Buffer *buf, + void *data) { - VarREPattern *pat; + VarREPattern *pat = data; int xrv; - char *wp; + const char *wp = word; char *rp; - int added; + int added = 0; int flags = 0; #define MAYBE_ADD_SPACE() \ @@ -1698,10 +1460,6 @@ VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED, Buf_AddByte(buf, ' '); \ added = 1 - added = 0; - wp = word; - pat = patternp; - if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == (VAR_SUB_ONE|VAR_SUB_MATCHED)) xrv = REG_NOMATCH; @@ -1721,10 +1479,9 @@ VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED, for (rp = pat->replace; *rp; rp++) { if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) { MAYBE_ADD_SPACE(); - Buf_AddByte(buf,rp[1]); + Buf_AddByte(buf, rp[1]); rp++; - } - else if ((*rp == '&') || + } else if ((*rp == '&') || ((*rp == '\\') && isdigit((unsigned char)rp[1]))) { int n; const char *subbuf; @@ -1752,7 +1509,7 @@ VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED, Error("No match for subexpression %s", &errstr[0]); subbuf = ""; sublen = 0; - } else { + } else { subbuf = wp + pat->matches[n].rm_so; sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so; } @@ -1785,61 +1542,47 @@ VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED, break; default: VarREError(xrv, &pat->re, "Unexpected regex error"); - /* fall through */ + /* fall through */ case REG_NOMATCH: if (*wp) { MAYBE_ADD_SPACE(); - Buf_AddBytes(buf,strlen(wp),wp); + Buf_AddBytes(buf, strlen(wp), wp); } break; } - return(addSpace||added); + return addSpace || added; } #endif - -/*- - *----------------------------------------------------------------------- - * VarLoopExpand -- - * Implements the :@<temp>@<string>@ modifier of ODE make. - * We set the temp variable named in pattern.lhs to word and expand - * pattern.rhs storing the result in the passed buffer. - * - * Input: - * word Word to modify - * addSpace True if space should be added before - * other characters - * buf Buffer for result - * pattern Datafor substitution - * - * Results: - * TRUE if a space is needed before more characters are added. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* Callback function for VarModify to implement the :@var@...@ modifier of + * ODE make. We set the temp variable named in pattern.lhs to word and + * expand pattern.rhs. */ static Boolean VarLoopExpand(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate MAKE_ATTR_UNUSED, - char *word, Boolean addSpace, Buffer *buf, - void *loopp) + const char *word, Boolean addSpace, Buffer *buf, + void *data) { - VarLoop_t *loop = (VarLoop_t *)loopp; + VarLoop *loop = data; char *s; int slen; - if (word && *word) { - Var_Set(loop->tvar, word, loop->ctxt, VAR_NO_EXPORT); - s = Var_Subst(NULL, loop->str, loop->ctxt, loop->errnum | VARF_WANTRES); - if (s != NULL && *s != '\0') { - if (addSpace && *s != '\n') - Buf_AddByte(buf, ' '); - Buf_AddBytes(buf, (slen = strlen(s)), s); - addSpace = (slen > 0 && s[slen - 1] != '\n'); - } + if (*word) { + Var_Set_with_flags(loop->tvar, word, loop->ctxt, VAR_NO_EXPORT); + s = Var_Subst(NULL, loop->str, loop->ctxt, loop->flags); + if (DEBUG(VAR)) { + fprintf(debug_file, + "VarLoopExpand: in \"%s\", replace \"%s\" with \"%s\" " + "to \"%s\"\n", + word, loop->tvar, loop->str, s ? s : "(null)"); + } + if (s != NULL && *s != '\0') { + if (addSpace && *s != '\n') + Buf_AddByte(buf, ' '); + Buf_AddBytes(buf, (slen = strlen(s)), s); + addSpace = (slen > 0 && s[slen - 1] != '\n'); + } free(s); } return addSpace; @@ -1869,12 +1612,12 @@ static char * VarSelectWords(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, const char *str, VarSelectWords_t *seldata) { - Buffer buf; /* Buffer for the new string */ - Boolean addSpace; /* TRUE if need to add a space to the - * buffer before adding the trimmed - * word */ - char **av; /* word list */ - char *as; /* word list memory */ + Buffer buf; /* Buffer for the new string */ + Boolean addSpace; /* TRUE if need to add a space to the + * buffer before adding the trimmed + * word */ + char **av; /* word list */ + char *as; /* word list memory */ int ac, i; int start, end, step; @@ -1884,7 +1627,7 @@ VarSelectWords(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, if (vpstate->oneBigWord) { /* fake what brk_string() would do if there were only one word */ ac = 1; - av = bmake_malloc((ac + 1) * sizeof(char *)); + av = bmake_malloc((ac + 1) * sizeof(char *)); as = bmake_strdup(str); av[0] = as; av[1] = NULL; @@ -1935,42 +1678,35 @@ VarSelectWords(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, } -/*- - * VarRealpath -- - * Replace each word with the result of realpath() - * if successful. - */ +/* Callback function for VarModify to implement the :tA modifier. + * Replace each word with the result of realpath() if successful. */ static Boolean VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - char *word, Boolean addSpace, Buffer *buf, + const char *word, Boolean addSpace, Buffer *buf, void *patternp MAKE_ATTR_UNUSED) { - struct stat st; - char rbuf[MAXPATHLEN]; - char *rp; - - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - rp = cached_realpath(word, rbuf); - if (rp && *rp == '/' && stat(rp, &st) == 0) - word = rp; - - Buf_AddBytes(buf, strlen(word), word); - return(addSpace); + struct stat st; + char rbuf[MAXPATHLEN]; + char *rp; + + if (addSpace && vpstate->varSpace) + Buf_AddByte(buf, vpstate->varSpace); + rp = cached_realpath(word, rbuf); + if (rp && *rp == '/' && stat(rp, &st) == 0) + word = rp; + + Buf_AddBytes(buf, strlen(word), word); + return TRUE; } /*- *----------------------------------------------------------------------- - * VarModify -- - * Modify each of the words of the passed string using the given - * function. Used to implement all modifiers. + * Modify each of the words of the passed string using the given function. * * Input: * str String whose words should be trimmed * modProc Function to use to modify them - * datum Datum to pass it + * data Custom data for the modProc * * Results: * A string of all the words modified appropriately. @@ -1982,17 +1718,13 @@ VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, */ static char * VarModify(GNode *ctx, Var_Parse_State *vpstate, - const char *str, - Boolean (*modProc)(GNode *, Var_Parse_State *, char *, - Boolean, Buffer *, void *), - void *datum) + const char *str, VarModifyCallback modProc, void *datum) { - Buffer buf; /* Buffer for the new string */ - Boolean addSpace; /* TRUE if need to add a space to the - * buffer before adding the trimmed - * word */ - char **av; /* word list */ - char *as; /* word list memory */ + Buffer buf; /* Buffer for the new string */ + Boolean addSpace; /* TRUE if need to add a space to the + * buffer before adding the trimmed word */ + char **av; /* word list */ + char *as; /* word list memory */ int ac, i; Buf_Init(&buf, 0); @@ -2001,7 +1733,7 @@ VarModify(GNode *ctx, Var_Parse_State *vpstate, if (vpstate->oneBigWord) { /* fake what brk_string() would do if there were only one word */ ac = 1; - av = bmake_malloc((ac + 1) * sizeof(char *)); + av = bmake_malloc((ac + 1) * sizeof(char *)); as = bmake_strdup(str); av[0] = as; av[1] = NULL; @@ -2009,10 +1741,14 @@ VarModify(GNode *ctx, Var_Parse_State *vpstate, av = brk_string(str, &ac, FALSE, &as); } - for (i = 0; i < ac; i++) { - addSpace = (*modProc)(ctx, vpstate, av[i], addSpace, &buf, datum); + if (DEBUG(VAR)) { + fprintf(debug_file, "VarModify: split \"%s\" into %d words\n", + str, ac); } + for (i = 0; i < ac; i++) + addSpace = modProc(ctx, vpstate, av[i], addSpace, &buf, datum); + free(as); free(av); @@ -2023,15 +1759,15 @@ VarModify(GNode *ctx, Var_Parse_State *vpstate, static int VarWordCompare(const void *a, const void *b) { - int r = strcmp(*(const char * const *)a, *(const char * const *)b); - return r; + int r = strcmp(*(const char * const *)a, *(const char * const *)b); + return r; } static int VarWordCompareReverse(const void *a, const void *b) { - int r = strcmp(*(const char * const *)b, *(const char * const *)a); - return r; + int r = strcmp(*(const char * const *)b, *(const char * const *)a); + return r; } /*- @@ -2054,44 +1790,40 @@ VarWordCompareReverse(const void *a, const void *b) static char * VarOrder(const char *str, const char otype) { - Buffer buf; /* Buffer for the new string */ - char **av; /* word list [first word does not count] */ - char *as; /* word list memory */ + Buffer buf; /* Buffer for the new string */ + char **av; /* word list [first word does not count] */ + char *as; /* word list memory */ int ac, i; Buf_Init(&buf, 0); av = brk_string(str, &ac, FALSE, &as); - if (ac > 0) + if (ac > 0) { switch (otype) { - case 'r': /* reverse sort alphabetically */ + case 'r': /* reverse sort alphabetically */ qsort(av, ac, sizeof(char *), VarWordCompareReverse); break; - case 's': /* sort alphabetically */ + case 's': /* sort alphabetically */ qsort(av, ac, sizeof(char *), VarWordCompare); break; - case 'x': /* randomize */ - { - int rndidx; - char *t; - - /* - * We will use [ac..2] range for mod factors. This will produce - * random numbers in [(ac-1)..0] interval, and minimal - * reasonable value for mod factor is 2 (the mod 1 will produce - * 0 with probability 1). - */ - for (i = ac-1; i > 0; i--) { - rndidx = random() % (i + 1); - if (i != rndidx) { - t = av[i]; + case 'x': /* randomize */ + { + /* + * We will use [ac..2] range for mod factors. This will produce + * random numbers in [(ac-1)..0] interval, and minimal + * reasonable value for mod factor is 2 (the mod 1 will produce + * 0 with probability 1). + */ + for (i = ac - 1; i > 0; i--) { + int rndidx = random() % (i + 1); + char *t = av[i]; av[i] = av[rndidx]; av[rndidx] = t; } } } - } /* end of switch */ + } for (i = 0; i < ac; i++) { Buf_AddBytes(&buf, strlen(av[i]), av[i]); @@ -2125,9 +1857,9 @@ VarOrder(const char *str, const char otype) static char * VarUniq(const char *str) { - Buffer buf; /* Buffer for new string */ - char **av; /* List of words to affect */ - char *as; /* Word list memory */ + Buffer buf; /* Buffer for new string */ + char **av; /* List of words to affect */ + char *as; /* Word list memory */ int ac, i, j; Buf_Init(&buf, 0); @@ -2169,10 +1901,10 @@ VarUniq(const char *str) static char * VarRange(const char *str, int ac) { - Buffer buf; /* Buffer for new string */ - char tmp[32]; /* each element */ - char **av; /* List of words to affect */ - char *as; /* Word list memory */ + Buffer buf; /* Buffer for new string */ + char tmp[32]; /* each element */ + char **av; /* List of words to affect */ + char *as; /* Word list memory */ int i, n; Buf_Init(&buf, 0); @@ -2201,7 +1933,8 @@ VarRange(const char *str, int ac) /*- *----------------------------------------------------------------------- * VarGetPattern -- - * Pass through the tstr looking for 1) escaped delimiters, + * During the parsing of a part of a modifier such as :S or :@, + * pass through the tstr looking for 1) escaped delimiters, * '$'s and backslashes (place the escaped character in * uninterpreted) and 2) unescaped $'s that aren't before * the delimiter (expand the variable substitution unless flags @@ -2222,8 +1955,8 @@ VarRange(const char *str, int ac) */ static char * VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate MAKE_ATTR_UNUSED, - int flags, const char **tstr, int delim, int *vflags, - int *length, VarPattern *pattern) + VarPattern_Flags flags, const char **tstr, int delim, + VarPattern_Flags *vflags, int *length, VarPattern *pattern) { const char *cp; char *rstr; @@ -2270,8 +2003,8 @@ VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate MAKE_ATTR_UNUSED, * delimiter, assume it's a variable * substitution and recurse. */ - cp2 = Var_Parse(cp, ctxt, errnum | VARF_WANTRES, &len, - &freeIt); + cp2 = Var_Parse(cp, ctxt, errnum | (flags & VARF_WANTRES), + &len, &freeIt); Buf_AddBytes(&buf, strlen(cp2), cp2); free(freeIt); cp += len - 1; @@ -2282,7 +2015,7 @@ VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate MAKE_ATTR_UNUSED, /* * Find the end of this variable reference * and suck it in without further ado. - * It will be interperated later. + * It will be interpreted later. */ int have = *cp2; int want = (*cp2 == PROPEN) ? PRCLOSE : BRCLOSE; @@ -2302,8 +2035,7 @@ VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate MAKE_ATTR_UNUSED, Buf_AddByte(&buf, *cp); } } - } - else if (pattern && *cp == '&') + } else if (pattern && *cp == '&') Buf_AddBytes(&buf, pattern->leftLen, pattern->lhs); else Buf_AddByte(&buf, *cp); @@ -2346,7 +2078,7 @@ VarQuote(char *str, Boolean quoteDollar) size_t nlen; if ((newline = Shell_GetNewline()) == NULL) - newline = "\\\n"; + newline = "\\\n"; nlen = strlen(newline); Buf_Init(&buf, 0); @@ -2387,13 +2119,13 @@ VarQuote(char *str, Boolean quoteDollar) *----------------------------------------------------------------------- */ static char * -VarHash(char *str) +VarHash(const char *str) { static const char hexdigits[16] = "0123456789abcdef"; Buffer buf; size_t len, len2; - unsigned char *ustr = (unsigned char *)str; - unsigned int h, k, c1, c2; + const unsigned char *ustr = (const unsigned char *)str; + uint32_t h, k, c1, c2; h = 0x971e137bU; c1 = 0x95543787U; @@ -2404,18 +2136,21 @@ VarHash(char *str) k = 0; switch (len) { default: - k = (ustr[3] << 24) | (ustr[2] << 16) | (ustr[1] << 8) | ustr[0]; + k = ((uint32_t)ustr[3] << 24) | + ((uint32_t)ustr[2] << 16) | + ((uint32_t)ustr[1] << 8) | + (uint32_t)ustr[0]; len -= 4; ustr += 4; break; case 3: - k |= (ustr[2] << 16); + k |= (uint32_t)ustr[2] << 16; /* FALLTHROUGH */ case 2: - k |= (ustr[1] << 8); + k |= (uint32_t)ustr[1] << 8; /* FALLTHROUGH */ case 1: - k |= ustr[0]; + k |= (uint32_t)ustr[0]; len = 0; } c1 = c1 * 5 + 0x7b7d159cU; @@ -2426,20 +2161,20 @@ VarHash(char *str) h = (h << 13) ^ (h >> 19); h = h * 5 + 0x52dce729U; h ^= k; - } - h ^= len2; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - - Buf_Init(&buf, 0); - for (len = 0; len < 8; ++len) { - Buf_AddByte(&buf, hexdigits[h & 15]); - h >>= 4; - } - - return Buf_Destroy(&buf, FALSE); + } + h ^= len2; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + Buf_Init(&buf, 0); + for (len = 0; len < 8; ++len) { + Buf_AddByte(&buf, hexdigits[h & 15]); + h >>= 4; + } + + return Buf_Destroy(&buf, FALSE); } static char * @@ -2452,11 +2187,1022 @@ VarStrftime(const char *fmt, int zulu, time_t utc) if (!*fmt) fmt = "%c"; strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc)); - + buf[sizeof(buf) - 1] = '\0'; return bmake_strdup(buf); } +typedef struct { + /* const parameters */ + int startc; + int endc; + Var *v; + GNode *ctxt; + int flags; + int *lengthPtr; + void **freePtr; + + /* read-write */ + char *nstr; + const char *tstr; + const char *start; + const char *cp; /* Secondary pointer into str (place marker + * for tstr) */ + char termc; /* Character which terminated scan */ + int cnt; /* Used to count brace pairs when variable in + * in parens or braces */ + char delim; + int modifier; /* that we are processing */ + Var_Parse_State parsestate; /* Flags passed to helper functions */ + + /* result */ + char *newStr; /* New value to return */ +} ApplyModifiersState; + +/* we now have some modifiers with long names */ +#define STRMOD_MATCH(s, want, n) \ + (strncmp(s, want, n) == 0 && (s[n] == st->endc || s[n] == ':')) +#define STRMOD_MATCHX(s, want, n) \ + (strncmp(s, want, n) == 0 && \ + (s[n] == st->endc || s[n] == ':' || s[n] == '=')) +#define CHARMOD_MATCH(c) (c == st->endc || c == ':') + +/* :@var@...${var}...@ */ +static Boolean +ApplyModifier_At(ApplyModifiersState *st) { + VarLoop loop; + VarPattern_Flags vflags = VAR_NOSUBST; + + st->cp = ++(st->tstr); + st->delim = '@'; + loop.tvar = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + &vflags, &loop.tvarLen, NULL); + if (loop.tvar == NULL) + return FALSE; + + loop.str = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + &vflags, &loop.strLen, NULL); + if (loop.str == NULL) + return FALSE; + + st->termc = *st->cp; + st->delim = '\0'; + + loop.flags = st->flags & (VARF_UNDEFERR | VARF_WANTRES); + loop.ctxt = st->ctxt; + st->newStr = VarModify( + st->ctxt, &st->parsestate, st->nstr, VarLoopExpand, &loop); + Var_Delete(loop.tvar, st->ctxt); + free(loop.tvar); + free(loop.str); + return TRUE; +} + +/* :Ddefined or :Uundefined */ +static void +ApplyModifier_Defined(ApplyModifiersState *st) +{ + Buffer buf; /* Buffer for patterns */ + int nflags; + + if (st->flags & VARF_WANTRES) { + int wantres; + if (*st->tstr == 'U') + wantres = ((st->v->flags & VAR_JUNK) != 0); + else + wantres = ((st->v->flags & VAR_JUNK) == 0); + nflags = st->flags & ~VARF_WANTRES; + if (wantres) + nflags |= VARF_WANTRES; + } else + nflags = st->flags; + + /* + * Pass through tstr looking for 1) escaped delimiters, + * '$'s and backslashes (place the escaped character in + * uninterpreted) and 2) unescaped $'s that aren't before + * the delimiter (expand the variable substitution). + * The result is left in the Buffer buf. + */ + Buf_Init(&buf, 0); + for (st->cp = st->tstr + 1; + *st->cp != st->endc && *st->cp != ':' && *st->cp != '\0'; + st->cp++) { + if (*st->cp == '\\' && + (st->cp[1] == ':' || st->cp[1] == '$' || st->cp[1] == st->endc || + st->cp[1] == '\\')) { + Buf_AddByte(&buf, st->cp[1]); + st->cp++; + } else if (*st->cp == '$') { + /* + * If unescaped dollar sign, assume it's a + * variable substitution and recurse. + */ + char *cp2; + int len; + void *freeIt; + + cp2 = Var_Parse(st->cp, st->ctxt, nflags, &len, &freeIt); + Buf_AddBytes(&buf, strlen(cp2), cp2); + free(freeIt); + st->cp += len - 1; + } else { + Buf_AddByte(&buf, *st->cp); + } + } + + st->termc = *st->cp; + + if ((st->v->flags & VAR_JUNK) != 0) + st->v->flags |= VAR_KEEP; + if (nflags & VARF_WANTRES) { + st->newStr = Buf_Destroy(&buf, FALSE); + } else { + st->newStr = st->nstr; + Buf_Destroy(&buf, TRUE); + } +} + +/* :gmtime */ +static Boolean +ApplyModifier_Gmtime(ApplyModifiersState *st) +{ + time_t utc; + char *ep; + + st->cp = st->tstr + 1; /* make sure it is set */ + if (!STRMOD_MATCHX(st->tstr, "gmtime", 6)) + return FALSE; + if (st->tstr[6] == '=') { + utc = strtoul(&st->tstr[7], &ep, 10); + st->cp = ep; + } else { + utc = 0; + st->cp = st->tstr + 6; + } + st->newStr = VarStrftime(st->nstr, 1, utc); + st->termc = *st->cp; + return TRUE; +} + +/* :localtime */ +static Boolean +ApplyModifier_Localtime(ApplyModifiersState *st) +{ + time_t utc; + char *ep; + + st->cp = st->tstr + 1; /* make sure it is set */ + if (!STRMOD_MATCHX(st->tstr, "localtime", 9)) + return FALSE; + + if (st->tstr[9] == '=') { + utc = strtoul(&st->tstr[10], &ep, 10); + st->cp = ep; + } else { + utc = 0; + st->cp = st->tstr + 9; + } + st->newStr = VarStrftime(st->nstr, 0, utc); + st->termc = *st->cp; + return TRUE; +} + +/* :hash */ +static Boolean +ApplyModifier_Hash(ApplyModifiersState *st) +{ + st->cp = st->tstr + 1; /* make sure it is set */ + if (!STRMOD_MATCH(st->tstr, "hash", 4)) + return FALSE; + st->newStr = VarHash(st->nstr); + st->cp = st->tstr + 4; + st->termc = *st->cp; + return TRUE; +} + +/* :P */ +static void +ApplyModifier_Path(ApplyModifiersState *st) +{ + GNode *gn; + + if ((st->v->flags & VAR_JUNK) != 0) + st->v->flags |= VAR_KEEP; + gn = Targ_FindNode(st->v->name, TARG_NOCREATE); + if (gn == NULL || gn->type & OP_NOPATH) { + st->newStr = NULL; + } else if (gn->path) { + st->newStr = bmake_strdup(gn->path); + } else { + st->newStr = Dir_FindFile(st->v->name, Suff_FindPath(gn)); + } + if (!st->newStr) + st->newStr = bmake_strdup(st->v->name); + st->cp = ++st->tstr; + st->termc = *st->tstr; +} + +/* :!cmd! */ +static Boolean +ApplyModifier_Exclam(ApplyModifiersState *st) +{ + const char *emsg; + VarPattern pattern; + + pattern.flags = 0; + + st->delim = '!'; + emsg = NULL; + st->cp = ++st->tstr; + pattern.rhs = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + NULL, &pattern.rightLen, NULL); + if (pattern.rhs == NULL) + return FALSE; + if (st->flags & VARF_WANTRES) + st->newStr = Cmd_Exec(pattern.rhs, &emsg); + else + st->newStr = varNoError; + free(UNCONST(pattern.rhs)); + if (emsg) + Error(emsg, st->nstr); + st->termc = *st->cp; + st->delim = '\0'; + if (st->v->flags & VAR_JUNK) + st->v->flags |= VAR_KEEP; + return TRUE; +} + +/* :range */ +static Boolean +ApplyModifier_Range(ApplyModifiersState *st) +{ + int n; + char *ep; + + st->cp = st->tstr + 1; /* make sure it is set */ + if (!STRMOD_MATCHX(st->tstr, "range", 5)) + return FALSE; + + if (st->tstr[5] == '=') { + n = strtoul(&st->tstr[6], &ep, 10); + st->cp = ep; + } else { + n = 0; + st->cp = st->tstr + 5; + } + st->newStr = VarRange(st->nstr, n); + st->termc = *st->cp; + return TRUE; +} + +/* :Mpattern or :Npattern */ +static void +ApplyModifier_Match(ApplyModifiersState *st) +{ + char *pattern; + const char *endpat; /* points just after end of pattern */ + char *cp2; + Boolean copy; /* pattern should be, or has been, copied */ + Boolean needSubst; + int nest; + + copy = FALSE; + needSubst = FALSE; + nest = 1; + /* + * In the loop below, ignore ':' unless we are at + * (or back to) the original brace level. + * XXX This will likely not work right if $() and ${} + * are intermixed. + */ + for (st->cp = st->tstr + 1; + *st->cp != '\0' && !(*st->cp == ':' && nest == 1); + st->cp++) { + if (*st->cp == '\\' && + (st->cp[1] == ':' || st->cp[1] == st->endc || + st->cp[1] == st->startc)) { + if (!needSubst) + copy = TRUE; + st->cp++; + continue; + } + if (*st->cp == '$') + needSubst = TRUE; + if (*st->cp == '(' || *st->cp == '{') + ++nest; + if (*st->cp == ')' || *st->cp == '}') { + --nest; + if (nest == 0) + break; + } + } + st->termc = *st->cp; + endpat = st->cp; + if (copy) { + /* + * Need to compress the \:'s out of the pattern, so + * allocate enough room to hold the uncompressed + * pattern (note that st->cp started at st->tstr+1, so + * st->cp - st->tstr takes the null byte into account) and + * compress the pattern into the space. + */ + pattern = bmake_malloc(st->cp - st->tstr); + for (cp2 = pattern, st->cp = st->tstr + 1; + st->cp < endpat; + st->cp++, cp2++) { + if ((*st->cp == '\\') && (st->cp+1 < endpat) && + (st->cp[1] == ':' || st->cp[1] == st->endc)) + st->cp++; + *cp2 = *st->cp; + } + *cp2 = '\0'; + endpat = cp2; + } else { + /* + * Either Var_Subst or VarModify will need a + * nul-terminated string soon, so construct one now. + */ + pattern = bmake_strndup(st->tstr+1, endpat - (st->tstr + 1)); + } + if (needSubst) { + /* pattern contains embedded '$', so use Var_Subst to expand it. */ + cp2 = pattern; + pattern = Var_Subst(NULL, cp2, st->ctxt, st->flags); + free(cp2); + } + if (DEBUG(VAR)) + fprintf(debug_file, "Pattern[%s] for [%s] is [%s]\n", + st->v->name, st->nstr, pattern); + if (*st->tstr == 'M') { + st->newStr = VarModify(st->ctxt, &st->parsestate, st->nstr, VarMatch, + pattern); + } else { + st->newStr = VarModify(st->ctxt, &st->parsestate, st->nstr, VarNoMatch, + pattern); + } + free(pattern); +} + +/* :S,from,to, */ +static Boolean +ApplyModifier_Subst(ApplyModifiersState *st) +{ + VarPattern pattern; + Var_Parse_State tmpparsestate; + + pattern.flags = 0; + tmpparsestate = st->parsestate; + st->delim = st->tstr[1]; + st->tstr += 2; + + /* + * If pattern begins with '^', it is anchored to the + * start of the word -- skip over it and flag pattern. + */ + if (*st->tstr == '^') { + pattern.flags |= VAR_MATCH_START; + st->tstr += 1; + } + + st->cp = st->tstr; + pattern.lhs = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + &pattern.flags, &pattern.leftLen, NULL); + if (pattern.lhs == NULL) + return FALSE; + + pattern.rhs = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + NULL, &pattern.rightLen, &pattern); + if (pattern.rhs == NULL) + return FALSE; + + /* + * Check for global substitution. If 'g' after the final + * delimiter, substitution is global and is marked that + * way. + */ + for (;; st->cp++) { + switch (*st->cp) { + case 'g': + pattern.flags |= VAR_SUB_GLOBAL; + continue; + case '1': + pattern.flags |= VAR_SUB_ONE; + continue; + case 'W': + tmpparsestate.oneBigWord = TRUE; + continue; + } + break; + } + + st->termc = *st->cp; + st->newStr = VarModify( + st->ctxt, &tmpparsestate, st->nstr, VarSubstitute, &pattern); + + /* Free the two strings. */ + free(UNCONST(pattern.lhs)); + free(UNCONST(pattern.rhs)); + st->delim = '\0'; + return TRUE; +} + +#ifndef NO_REGEX +/* :C,from,to, */ +static Boolean +ApplyModifier_Regex(ApplyModifiersState *st) +{ + VarREPattern pattern; + char *re; + int error; + Var_Parse_State tmpparsestate; + + pattern.flags = 0; + tmpparsestate = st->parsestate; + st->delim = st->tstr[1]; + st->tstr += 2; + + st->cp = st->tstr; + + re = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + NULL, NULL, NULL); + if (re == NULL) + return FALSE; + + pattern.replace = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + NULL, NULL, NULL); + if (pattern.replace == NULL) { + free(re); + return FALSE; + } + + for (;; st->cp++) { + switch (*st->cp) { + case 'g': + pattern.flags |= VAR_SUB_GLOBAL; + continue; + case '1': + pattern.flags |= VAR_SUB_ONE; + continue; + case 'W': + tmpparsestate.oneBigWord = TRUE; + continue; + } + break; + } + + st->termc = *st->cp; + + error = regcomp(&pattern.re, re, REG_EXTENDED); + free(re); + if (error) { + *st->lengthPtr = st->cp - st->start + 1; + VarREError(error, &pattern.re, "RE substitution error"); + free(pattern.replace); + return FALSE; + } + + pattern.nsub = pattern.re.re_nsub + 1; + if (pattern.nsub < 1) + pattern.nsub = 1; + if (pattern.nsub > 10) + pattern.nsub = 10; + pattern.matches = bmake_malloc(pattern.nsub * sizeof(regmatch_t)); + st->newStr = VarModify( + st->ctxt, &tmpparsestate, st->nstr, VarRESubstitute, &pattern); + regfree(&pattern.re); + free(pattern.replace); + free(pattern.matches); + st->delim = '\0'; + return TRUE; +} +#endif + +/* :tA, :tu, :tl, etc. */ +static Boolean +ApplyModifier_To(ApplyModifiersState *st) +{ + st->cp = st->tstr + 1; /* make sure it is set */ + if (st->tstr[1] != st->endc && st->tstr[1] != ':') { + if (st->tstr[1] == 's') { + /* Use the char (if any) at st->tstr[2] as the word separator. */ + VarPattern pattern; + + if (st->tstr[2] != st->endc && + (st->tstr[3] == st->endc || st->tstr[3] == ':')) { + /* ":ts<unrecognised><endc>" or + * ":ts<unrecognised>:" */ + st->parsestate.varSpace = st->tstr[2]; + st->cp = st->tstr + 3; + } else if (st->tstr[2] == st->endc || st->tstr[2] == ':') { + /* ":ts<endc>" or ":ts:" */ + st->parsestate.varSpace = 0; /* no separator */ + st->cp = st->tstr + 2; + } else if (st->tstr[2] == '\\') { + const char *xp = &st->tstr[3]; + int base = 8; /* assume octal */ + + switch (st->tstr[3]) { + case 'n': + st->parsestate.varSpace = '\n'; + st->cp = st->tstr + 4; + break; + case 't': + st->parsestate.varSpace = '\t'; + st->cp = st->tstr + 4; + break; + case 'x': + base = 16; + xp++; + goto get_numeric; + case '0': + base = 0; + goto get_numeric; + default: + if (isdigit((unsigned char)st->tstr[3])) { + char *ep; + get_numeric: + st->parsestate.varSpace = strtoul(xp, &ep, base); + if (*ep != ':' && *ep != st->endc) + return FALSE; + st->cp = ep; + } else { + /* ":ts<backslash><unrecognised>". */ + return FALSE; + } + break; + } + } else { + /* Found ":ts<unrecognised><unrecognised>". */ + return FALSE; + } + + st->termc = *st->cp; + + /* + * We cannot be certain that VarModify will be used - even if there + * is a subsequent modifier, so do a no-op VarSubstitute now to for + * str to be re-expanded without the spaces. + */ + pattern.flags = VAR_SUB_ONE; + pattern.lhs = pattern.rhs = "\032"; + pattern.leftLen = pattern.rightLen = 1; + + st->newStr = VarModify( + st->ctxt, &st->parsestate, st->nstr, VarSubstitute, &pattern); + } else if (st->tstr[2] == st->endc || st->tstr[2] == ':') { + /* Check for two-character options: ":tu", ":tl" */ + if (st->tstr[1] == 'A') { /* absolute path */ + st->newStr = VarModify( + st->ctxt, &st->parsestate, st->nstr, VarRealpath, NULL); + st->cp = st->tstr + 2; + st->termc = *st->cp; + } else if (st->tstr[1] == 'u') { + char *dp = bmake_strdup(st->nstr); + for (st->newStr = dp; *dp; dp++) + *dp = toupper((unsigned char)*dp); + st->cp = st->tstr + 2; + st->termc = *st->cp; + } else if (st->tstr[1] == 'l') { + char *dp = bmake_strdup(st->nstr); + for (st->newStr = dp; *dp; dp++) + *dp = tolower((unsigned char)*dp); + st->cp = st->tstr + 2; + st->termc = *st->cp; + } else if (st->tstr[1] == 'W' || st->tstr[1] == 'w') { + st->parsestate.oneBigWord = (st->tstr[1] == 'W'); + st->newStr = st->nstr; + st->cp = st->tstr + 2; + st->termc = *st->cp; + } else { + /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */ + return FALSE; + } + } else { + /* Found ":t<unrecognised><unrecognised>". */ + return FALSE; + } + } else { + /* Found ":t<endc>" or ":t:". */ + return FALSE; + } + return TRUE; +} + +/* :[#], :[1], etc. */ +static int +ApplyModifier_Words(ApplyModifiersState *st) +{ + /* + * Look for the closing ']', recursively + * expanding any embedded variables. + * + * estr is a pointer to the expanded result, + * which we must free(). + */ + char *estr; + + st->cp = st->tstr + 1; /* point to char after '[' */ + st->delim = ']'; /* look for closing ']' */ + estr = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + NULL, NULL, NULL); + if (estr == NULL) + return 'c'; /* report missing ']' */ + /* now st->cp points just after the closing ']' */ + st->delim = '\0'; + if (st->cp[0] != ':' && st->cp[0] != st->endc) { + /* Found junk after ']' */ + free(estr); + return 'b'; + } + if (estr[0] == '\0') { + /* Found empty square brackets in ":[]". */ + free(estr); + return 'b'; + } else if (estr[0] == '#' && estr[1] == '\0') { + /* Found ":[#]" */ + + /* + * We will need enough space for the decimal + * representation of an int. We calculate the + * space needed for the octal representation, + * and add enough slop to cope with a '-' sign + * (which should never be needed) and a '\0' + * string terminator. + */ + int newStrSize = (sizeof(int) * CHAR_BIT + 2) / 3 + 2; + + st->newStr = bmake_malloc(newStrSize); + if (st->parsestate.oneBigWord) { + strncpy(st->newStr, "1", newStrSize); + } else { + /* XXX: brk_string() is a rather expensive + * way of counting words. */ + char **av; + char *as; + int ac; + + av = brk_string(st->nstr, &ac, FALSE, &as); + snprintf(st->newStr, newStrSize, "%d", ac); + free(as); + free(av); + } + st->termc = *st->cp; + free(estr); + return 0; + } else if (estr[0] == '*' && estr[1] == '\0') { + /* Found ":[*]" */ + st->parsestate.oneBigWord = TRUE; + st->newStr = st->nstr; + st->termc = *st->cp; + free(estr); + return 0; + } else if (estr[0] == '@' && estr[1] == '\0') { + /* Found ":[@]" */ + st->parsestate.oneBigWord = FALSE; + st->newStr = st->nstr; + st->termc = *st->cp; + free(estr); + return 0; + } else { + char *ep; + /* + * We expect estr to contain a single + * integer for :[N], or two integers + * separated by ".." for :[start..end]. + */ + VarSelectWords_t seldata = { 0, 0 }; + + seldata.start = strtol(estr, &ep, 0); + if (ep == estr) { + /* Found junk instead of a number */ + free(estr); + return 'b'; + } else if (ep[0] == '\0') { + /* Found only one integer in :[N] */ + seldata.end = seldata.start; + } else if (ep[0] == '.' && ep[1] == '.' && ep[2] != '\0') { + /* Expecting another integer after ".." */ + ep += 2; + seldata.end = strtol(ep, &ep, 0); + if (ep[0] != '\0') { + /* Found junk after ".." */ + free(estr); + return 'b'; + } + } else { + /* Found junk instead of ".." */ + free(estr); + return 'b'; + } + /* + * Now seldata is properly filled in, + * but we still have to check for 0 as + * a special case. + */ + if (seldata.start == 0 && seldata.end == 0) { + /* ":[0]" or perhaps ":[0..0]" */ + st->parsestate.oneBigWord = TRUE; + st->newStr = st->nstr; + st->termc = *st->cp; + free(estr); + return 0; + } else if (seldata.start == 0 || seldata.end == 0) { + /* ":[0..N]" or ":[N..0]" */ + free(estr); + return 'b'; + } + /* Normal case: select the words described by seldata. */ + st->newStr = VarSelectWords( + st->ctxt, &st->parsestate, st->nstr, &seldata); + + st->termc = *st->cp; + free(estr); + return 0; + } +} + +/* :O or :Ox */ +static Boolean +ApplyModifier_Order(ApplyModifiersState *st) +{ + char otype; + + st->cp = st->tstr + 1; /* skip to the rest in any case */ + if (st->tstr[1] == st->endc || st->tstr[1] == ':') { + otype = 's'; + st->termc = *st->cp; + } else if ((st->tstr[1] == 'r' || st->tstr[1] == 'x') && + (st->tstr[2] == st->endc || st->tstr[2] == ':')) { + otype = st->tstr[1]; + st->cp = st->tstr + 2; + st->termc = *st->cp; + } else { + return FALSE; + } + st->newStr = VarOrder(st->nstr, otype); + return TRUE; +} + +/* :? then : else */ +static Boolean +ApplyModifier_IfElse(ApplyModifiersState *st) +{ + VarPattern pattern; + Boolean value; + int cond_rc; + VarPattern_Flags lhs_flags, rhs_flags; + + /* find ':', and then substitute accordingly */ + if (st->flags & VARF_WANTRES) { + cond_rc = Cond_EvalExpression(NULL, st->v->name, &value, 0, FALSE); + if (cond_rc == COND_INVALID) { + lhs_flags = rhs_flags = VAR_NOSUBST; + } else if (value) { + lhs_flags = 0; + rhs_flags = VAR_NOSUBST; + } else { + lhs_flags = VAR_NOSUBST; + rhs_flags = 0; + } + } else { + /* we are just consuming and discarding */ + cond_rc = value = 0; + lhs_flags = rhs_flags = VAR_NOSUBST; + } + pattern.flags = 0; + + st->cp = ++st->tstr; + st->delim = ':'; + pattern.lhs = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + &lhs_flags, &pattern.leftLen, NULL); + if (pattern.lhs == NULL) + return FALSE; + + /* BROPEN or PROPEN */ + st->delim = st->endc; + pattern.rhs = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + &rhs_flags, &pattern.rightLen, NULL); + if (pattern.rhs == NULL) + return FALSE; + + st->termc = *--st->cp; + st->delim = '\0'; + if (cond_rc == COND_INVALID) { + Error("Bad conditional expression `%s' in %s?%s:%s", + st->v->name, st->v->name, pattern.lhs, pattern.rhs); + return FALSE; + } + + if (value) { + st->newStr = UNCONST(pattern.lhs); + free(UNCONST(pattern.rhs)); + } else { + st->newStr = UNCONST(pattern.rhs); + free(UNCONST(pattern.lhs)); + } + if (st->v->flags & VAR_JUNK) + st->v->flags |= VAR_KEEP; + return TRUE; +} + +/* "::=", "::!=", "::+=", or "::?=" */ +static int +ApplyModifier_Assign(ApplyModifiersState *st) +{ + if (st->tstr[1] == '=' || + (st->tstr[2] == '=' && + (st->tstr[1] == '!' || st->tstr[1] == '+' || st->tstr[1] == '?'))) { + GNode *v_ctxt; /* context where v belongs */ + const char *emsg; + char *sv_name; + VarPattern pattern; + int how; + VarPattern_Flags vflags; + + if (st->v->name[0] == 0) + return 'b'; + + v_ctxt = st->ctxt; + sv_name = NULL; + ++st->tstr; + if (st->v->flags & VAR_JUNK) { + /* + * We need to bmake_strdup() it incase + * VarGetPattern() recurses. + */ + sv_name = st->v->name; + st->v->name = bmake_strdup(st->v->name); + } else if (st->ctxt != VAR_GLOBAL) { + Var *gv = VarFind(st->v->name, st->ctxt, 0); + if (gv == NULL) + v_ctxt = VAR_GLOBAL; + else + VarFreeEnv(gv, TRUE); + } + + switch ((how = *st->tstr)) { + case '+': + case '?': + case '!': + st->cp = &st->tstr[2]; + break; + default: + st->cp = ++st->tstr; + break; + } + st->delim = st->startc == PROPEN ? PRCLOSE : BRCLOSE; + pattern.flags = 0; + + vflags = (st->flags & VARF_WANTRES) ? 0 : VAR_NOSUBST; + pattern.rhs = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + &vflags, &pattern.rightLen, NULL); + if (st->v->flags & VAR_JUNK) { + /* restore original name */ + free(st->v->name); + st->v->name = sv_name; + } + if (pattern.rhs == NULL) + return 'c'; + + st->termc = *--st->cp; + st->delim = '\0'; + + if (st->flags & VARF_WANTRES) { + switch (how) { + case '+': + Var_Append(st->v->name, pattern.rhs, v_ctxt); + break; + case '!': + st->newStr = Cmd_Exec(pattern.rhs, &emsg); + if (emsg) + Error(emsg, st->nstr); + else + Var_Set(st->v->name, st->newStr, v_ctxt); + free(st->newStr); + break; + case '?': + if ((st->v->flags & VAR_JUNK) == 0) + break; + /* FALLTHROUGH */ + default: + Var_Set(st->v->name, pattern.rhs, v_ctxt); + break; + } + } + free(UNCONST(pattern.rhs)); + st->newStr = varNoError; + return 0; + } + return 'd'; /* "::<unrecognised>" */ +} + +/* remember current value */ +static Boolean +ApplyModifier_Remember(ApplyModifiersState *st) +{ + st->cp = st->tstr + 1; /* make sure it is set */ + if (!STRMOD_MATCHX(st->tstr, "_", 1)) + return FALSE; + + if (st->tstr[1] == '=') { + char *np; + int n; + + st->cp++; + n = strcspn(st->cp, ":)}"); + np = bmake_strndup(st->cp, n + 1); + np[n] = '\0'; + st->cp = st->tstr + 2 + n; + Var_Set(np, st->nstr, st->ctxt); + free(np); + } else { + Var_Set("_", st->nstr, st->ctxt); + } + st->newStr = st->nstr; + st->termc = *st->cp; + return TRUE; +} + +#ifdef SYSVVARSUB +/* :from=to */ +static int +ApplyModifier_SysV(ApplyModifiersState *st) +{ + /* + * This can either be a bogus modifier or a System-V + * substitution command. + */ + VarPattern pattern; + Boolean eqFound = FALSE; + + pattern.flags = 0; + + /* + * First we make a pass through the string trying + * to verify it is a SYSV-make-style translation: + * it must be: <string1>=<string2>) + */ + st->cp = st->tstr; + st->cnt = 1; + while (*st->cp != '\0' && st->cnt) { + if (*st->cp == '=') { + eqFound = TRUE; + /* continue looking for st->endc */ + } else if (*st->cp == st->endc) + st->cnt--; + else if (*st->cp == st->startc) + st->cnt++; + if (st->cnt) + st->cp++; + } + if (*st->cp != st->endc || !eqFound) + return 0; + + st->delim = '='; + st->cp = st->tstr; + pattern.lhs = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + &pattern.flags, &pattern.leftLen, NULL); + if (pattern.lhs == NULL) + return 'c'; + + st->delim = st->endc; + pattern.rhs = VarGetPattern( + st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, + NULL, &pattern.rightLen, &pattern); + if (pattern.rhs == NULL) + return 'c'; + + /* + * SYSV modifications happen through the whole + * string. Note the pattern is anchored at the end. + */ + st->termc = *--st->cp; + st->delim = '\0'; + if (pattern.leftLen == 0 && *st->nstr == '\0') { + st->newStr = st->nstr; /* special case */ + } else { + st->newStr = VarModify( + st->ctxt, &st->parsestate, st->nstr, VarSYSVMatch, &pattern); + } + free(UNCONST(pattern.lhs)); + free(UNCONST(pattern.rhs)); + return '='; +} +#endif + /* * Now we need to apply any modifiers the user wants applied. * These are: @@ -2541,42 +3287,21 @@ VarStrftime(const char *fmt, int zulu, time_t utc) * ::!=<cmd> Assigns output of <cmd> as the new value of * variable. */ - -/* we now have some modifiers with long names */ -#define STRMOD_MATCH(s, want, n) \ - (strncmp(s, want, n) == 0 && (s[n] == endc || s[n] == ':')) -#define STRMOD_MATCHX(s, want, n) \ - (strncmp(s, want, n) == 0 && (s[n] == endc || s[n] == ':' || s[n] == '=')) -#define CHARMOD_MATCH(c) (c == endc || c == ':') - static char * ApplyModifiers(char *nstr, const char *tstr, - int startc, int endc, - Var *v, GNode *ctxt, int flags, - int *lengthPtr, void **freePtr) + int const startc, int const endc, + Var * const v, GNode * const ctxt, int const flags, + int * const lengthPtr, void ** const freePtr) { - const char *start; - const char *cp; /* Secondary pointer into str (place marker - * for tstr) */ - char *newStr; /* New value to return */ - char *ep; - char termc; /* Character which terminated scan */ - int cnt; /* Used to count brace pairs when variable in - * in parens or braces */ - char delim; - int modifier; /* that we are processing */ - Var_Parse_State parsestate; /* Flags passed to helper functions */ - time_t utc; /* for VarStrftime */ - - delim = '\0'; - parsestate.oneBigWord = FALSE; - parsestate.varSpace = ' '; /* word separator */ + ApplyModifiersState st = { + startc, endc, v, ctxt, flags, lengthPtr, freePtr, + nstr, tstr, tstr, tstr, + '\0', 0, '\0', 0, {' ', FALSE}, NULL + }; - start = cp = tstr; + while (*st.tstr && *st.tstr != st.endc) { - while (*tstr && *tstr != endc) { - - if (*tstr == '$') { + if (*st.tstr == '$') { /* * We may have some complex modifiers in a variable. */ @@ -2585,1145 +3310,275 @@ ApplyModifiers(char *nstr, const char *tstr, int rlen; int c; - rval = Var_Parse(tstr, ctxt, flags, &rlen, &freeIt); + rval = Var_Parse(st.tstr, st.ctxt, st.flags, &rlen, &freeIt); /* - * If we have not parsed up to endc or ':', + * If we have not parsed up to st.endc or ':', * we are not interested. */ if (rval != NULL && *rval && - (c = tstr[rlen]) != '\0' && + (c = st.tstr[rlen]) != '\0' && c != ':' && - c != endc) { + c != st.endc) { free(freeIt); goto apply_mods; } if (DEBUG(VAR)) { fprintf(debug_file, "Got '%s' from '%.*s'%.*s\n", - rval, rlen, tstr, rlen, tstr + rlen); + rval, rlen, st.tstr, rlen, st.tstr + rlen); } - tstr += rlen; + st.tstr += rlen; if (rval != NULL && *rval) { int used; - nstr = ApplyModifiers(nstr, rval, - 0, 0, v, ctxt, flags, &used, freePtr); - if (nstr == var_Error - || (nstr == varNoError && (flags & VARF_UNDEFERR) == 0) + st.nstr = ApplyModifiers(st.nstr, rval, 0, 0, st.v, + st.ctxt, st.flags, &used, st.freePtr); + if (st.nstr == var_Error + || (st.nstr == varNoError && (st.flags & VARF_UNDEFERR) == 0) || strlen(rval) != (size_t) used) { free(freeIt); - goto out; /* error already reported */ + goto out; /* error already reported */ } } free(freeIt); - if (*tstr == ':') - tstr++; - else if (!*tstr && endc) { - Error("Unclosed variable specification after complex modifier (expecting '%c') for %s", endc, v->name); + if (*st.tstr == ':') + st.tstr++; + else if (!*st.tstr && st.endc) { + Error("Unclosed variable specification after complex " + "modifier (expecting '%c') for %s", st.endc, st.v->name); goto out; } continue; } apply_mods: if (DEBUG(VAR)) { - fprintf(debug_file, "Applying[%s] :%c to \"%s\"\n", v->name, - *tstr, nstr); + fprintf(debug_file, "Applying[%s] :%c to \"%s\"\n", st.v->name, + *st.tstr, st.nstr); } - newStr = var_Error; - switch ((modifier = *tstr)) { + st.newStr = var_Error; + switch ((st.modifier = *st.tstr)) { case ':': { - if (tstr[1] == '=' || - (tstr[2] == '=' && - (tstr[1] == '!' || tstr[1] == '+' || tstr[1] == '?'))) { - /* - * "::=", "::!=", "::+=", or "::?=" - */ - GNode *v_ctxt; /* context where v belongs */ - const char *emsg; - char *sv_name; - VarPattern pattern; - int how; - int vflags; - - if (v->name[0] == 0) - goto bad_modifier; - - v_ctxt = ctxt; - sv_name = NULL; - ++tstr; - if (v->flags & VAR_JUNK) { - /* - * We need to bmake_strdup() it incase - * VarGetPattern() recurses. - */ - sv_name = v->name; - v->name = bmake_strdup(v->name); - } else if (ctxt != VAR_GLOBAL) { - Var *gv = VarFind(v->name, ctxt, 0); - if (gv == NULL) - v_ctxt = VAR_GLOBAL; - else - VarFreeEnv(gv, TRUE); - } - - switch ((how = *tstr)) { - case '+': - case '?': - case '!': - cp = &tstr[2]; - break; - default: - cp = ++tstr; - break; - } - delim = startc == PROPEN ? PRCLOSE : BRCLOSE; - pattern.flags = 0; - - vflags = (flags & VARF_WANTRES) ? 0 : VAR_NOSUBST; - pattern.rhs = VarGetPattern(ctxt, &parsestate, flags, - &cp, delim, &vflags, - &pattern.rightLen, - NULL); - if (v->flags & VAR_JUNK) { - /* restore original name */ - free(v->name); - v->name = sv_name; - } - if (pattern.rhs == NULL) - goto cleanup; - - termc = *--cp; - delim = '\0'; - - if (flags & VARF_WANTRES) { - switch (how) { - case '+': - Var_Append(v->name, pattern.rhs, v_ctxt); - break; - case '!': - newStr = Cmd_Exec(pattern.rhs, &emsg); - if (emsg) - Error(emsg, nstr); - else - Var_Set(v->name, newStr, v_ctxt, 0); - free(newStr); - break; - case '?': - if ((v->flags & VAR_JUNK) == 0) - break; - /* FALLTHROUGH */ - default: - Var_Set(v->name, pattern.rhs, v_ctxt, 0); - break; - } - } - free(UNCONST(pattern.rhs)); - newStr = varNoError; - break; - } - goto default_case; /* "::<unrecognised>" */ - } - case '@': - { - VarLoop_t loop; - int vflags = VAR_NOSUBST; - - cp = ++tstr; - delim = '@'; - if ((loop.tvar = VarGetPattern(ctxt, &parsestate, flags, - &cp, delim, - &vflags, &loop.tvarLen, - NULL)) == NULL) - goto cleanup; - - if ((loop.str = VarGetPattern(ctxt, &parsestate, flags, - &cp, delim, - &vflags, &loop.strLen, - NULL)) == NULL) + int res = ApplyModifier_Assign(&st); + if (res == 'b') + goto bad_modifier; + if (res == 'c') goto cleanup; - - termc = *cp; - delim = '\0'; - - loop.errnum = flags & VARF_UNDEFERR; - loop.ctxt = ctxt; - newStr = VarModify(ctxt, &parsestate, nstr, VarLoopExpand, - &loop); - Var_Delete(loop.tvar, ctxt); - free(loop.tvar); - free(loop.str); - break; - } - case '_': /* remember current value */ - cp = tstr + 1; /* make sure it is set */ - if (STRMOD_MATCHX(tstr, "_", 1)) { - if (tstr[1] == '=') { - char *np; - int n; - - cp++; - n = strcspn(cp, ":)}"); - np = bmake_strndup(cp, n+1); - np[n] = '\0'; - cp = tstr + 2 + n; - Var_Set(np, nstr, ctxt, 0); - free(np); - } else { - Var_Set("_", nstr, ctxt, 0); - } - newStr = nstr; - termc = *cp; + if (res == 'd') + goto default_case; break; } - goto default_case; + case '@': + ApplyModifier_At(&st); + break; + case '_': + if (!ApplyModifier_Remember(&st)) + goto default_case; + break; case 'D': case 'U': - { - Buffer buf; /* Buffer for patterns */ - int nflags; - - if (flags & VARF_WANTRES) { - int wantres; - if (*tstr == 'U') - wantres = ((v->flags & VAR_JUNK) != 0); - else - wantres = ((v->flags & VAR_JUNK) == 0); - nflags = flags & ~VARF_WANTRES; - if (wantres) - nflags |= VARF_WANTRES; - } else - nflags = flags; - /* - * Pass through tstr looking for 1) escaped delimiters, - * '$'s and backslashes (place the escaped character in - * uninterpreted) and 2) unescaped $'s that aren't before - * the delimiter (expand the variable substitution). - * The result is left in the Buffer buf. - */ - Buf_Init(&buf, 0); - for (cp = tstr + 1; - *cp != endc && *cp != ':' && *cp != '\0'; - cp++) { - if ((*cp == '\\') && - ((cp[1] == ':') || - (cp[1] == '$') || - (cp[1] == endc) || - (cp[1] == '\\'))) - { - Buf_AddByte(&buf, cp[1]); - cp++; - } else if (*cp == '$') { - /* - * If unescaped dollar sign, assume it's a - * variable substitution and recurse. - */ - char *cp2; - int len; - void *freeIt; - - cp2 = Var_Parse(cp, ctxt, nflags, &len, &freeIt); - Buf_AddBytes(&buf, strlen(cp2), cp2); - free(freeIt); - cp += len - 1; - } else { - Buf_AddByte(&buf, *cp); - } - } - - termc = *cp; - - if ((v->flags & VAR_JUNK) != 0) - v->flags |= VAR_KEEP; - if (nflags & VARF_WANTRES) { - newStr = Buf_Destroy(&buf, FALSE); - } else { - newStr = nstr; - Buf_Destroy(&buf, TRUE); - } - break; - } + ApplyModifier_Defined(&st); + break; case 'L': { - if ((v->flags & VAR_JUNK) != 0) - v->flags |= VAR_KEEP; - newStr = bmake_strdup(v->name); - cp = ++tstr; - termc = *tstr; + if ((st.v->flags & VAR_JUNK) != 0) + st.v->flags |= VAR_KEEP; + st.newStr = bmake_strdup(st.v->name); + st.cp = ++st.tstr; + st.termc = *st.tstr; break; } case 'P': - { - GNode *gn; - - if ((v->flags & VAR_JUNK) != 0) - v->flags |= VAR_KEEP; - gn = Targ_FindNode(v->name, TARG_NOCREATE); - if (gn == NULL || gn->type & OP_NOPATH) { - newStr = NULL; - } else if (gn->path) { - newStr = bmake_strdup(gn->path); - } else { - newStr = Dir_FindFile(v->name, Suff_FindPath(gn)); - } - if (!newStr) { - newStr = bmake_strdup(v->name); - } - cp = ++tstr; - termc = *tstr; - break; - } + ApplyModifier_Path(&st); + break; case '!': - { - const char *emsg; - VarPattern pattern; - pattern.flags = 0; - - delim = '!'; - emsg = NULL; - cp = ++tstr; - if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, flags, - &cp, delim, - NULL, &pattern.rightLen, - NULL)) == NULL) - goto cleanup; - if (flags & VARF_WANTRES) - newStr = Cmd_Exec(pattern.rhs, &emsg); - else - newStr = varNoError; - free(UNCONST(pattern.rhs)); - if (emsg) - Error(emsg, nstr); - termc = *cp; - delim = '\0'; - if (v->flags & VAR_JUNK) { - v->flags |= VAR_KEEP; - } - break; - } + if (!ApplyModifier_Exclam(&st)) + goto cleanup; + break; case '[': { - /* - * Look for the closing ']', recursively - * expanding any embedded variables. - * - * estr is a pointer to the expanded result, - * which we must free(). - */ - char *estr; - - cp = tstr+1; /* point to char after '[' */ - delim = ']'; /* look for closing ']' */ - estr = VarGetPattern(ctxt, &parsestate, - flags, &cp, delim, - NULL, NULL, NULL); - if (estr == NULL) - goto cleanup; /* report missing ']' */ - /* now cp points just after the closing ']' */ - delim = '\0'; - if (cp[0] != ':' && cp[0] != endc) { - /* Found junk after ']' */ - free(estr); + int res = ApplyModifier_Words(&st); + if (res == 'b') goto bad_modifier; - } - if (estr[0] == '\0') { - /* Found empty square brackets in ":[]". */ - free(estr); - goto bad_modifier; - } else if (estr[0] == '#' && estr[1] == '\0') { - /* Found ":[#]" */ - - /* - * We will need enough space for the decimal - * representation of an int. We calculate the - * space needed for the octal representation, - * and add enough slop to cope with a '-' sign - * (which should never be needed) and a '\0' - * string terminator. - */ - int newStrSize = - (sizeof(int) * CHAR_BIT + 2) / 3 + 2; - - newStr = bmake_malloc(newStrSize); - if (parsestate.oneBigWord) { - strncpy(newStr, "1", newStrSize); - } else { - /* XXX: brk_string() is a rather expensive - * way of counting words. */ - char **av; - char *as; - int ac; - - av = brk_string(nstr, &ac, FALSE, &as); - snprintf(newStr, newStrSize, "%d", ac); - free(as); - free(av); - } - termc = *cp; - free(estr); - break; - } else if (estr[0] == '*' && estr[1] == '\0') { - /* Found ":[*]" */ - parsestate.oneBigWord = TRUE; - newStr = nstr; - termc = *cp; - free(estr); - break; - } else if (estr[0] == '@' && estr[1] == '\0') { - /* Found ":[@]" */ - parsestate.oneBigWord = FALSE; - newStr = nstr; - termc = *cp; - free(estr); - break; - } else { - /* - * We expect estr to contain a single - * integer for :[N], or two integers - * separated by ".." for :[start..end]. - */ - VarSelectWords_t seldata = { 0, 0 }; - - seldata.start = strtol(estr, &ep, 0); - if (ep == estr) { - /* Found junk instead of a number */ - free(estr); - goto bad_modifier; - } else if (ep[0] == '\0') { - /* Found only one integer in :[N] */ - seldata.end = seldata.start; - } else if (ep[0] == '.' && ep[1] == '.' && - ep[2] != '\0') { - /* Expecting another integer after ".." */ - ep += 2; - seldata.end = strtol(ep, &ep, 0); - if (ep[0] != '\0') { - /* Found junk after ".." */ - free(estr); - goto bad_modifier; - } - } else { - /* Found junk instead of ".." */ - free(estr); - goto bad_modifier; - } - /* - * Now seldata is properly filled in, - * but we still have to check for 0 as - * a special case. - */ - if (seldata.start == 0 && seldata.end == 0) { - /* ":[0]" or perhaps ":[0..0]" */ - parsestate.oneBigWord = TRUE; - newStr = nstr; - termc = *cp; - free(estr); - break; - } else if (seldata.start == 0 || - seldata.end == 0) { - /* ":[0..N]" or ":[N..0]" */ - free(estr); - goto bad_modifier; - } - /* - * Normal case: select the words - * described by seldata. - */ - newStr = VarSelectWords(ctxt, &parsestate, - nstr, &seldata); - - termc = *cp; - free(estr); - break; - } - + if (res == 'c') + goto cleanup; + break; } case 'g': - cp = tstr + 1; /* make sure it is set */ - if (STRMOD_MATCHX(tstr, "gmtime", 6)) { - if (tstr[6] == '=') { - utc = strtoul(&tstr[7], &ep, 10); - cp = ep; - } else { - utc = 0; - cp = tstr + 6; - } - newStr = VarStrftime(nstr, 1, utc); - termc = *cp; - } else { + if (!ApplyModifier_Gmtime(&st)) goto default_case; - } break; case 'h': - cp = tstr + 1; /* make sure it is set */ - if (STRMOD_MATCH(tstr, "hash", 4)) { - newStr = VarHash(nstr); - cp = tstr + 4; - termc = *cp; - } else { + if (!ApplyModifier_Hash(&st)) goto default_case; - } break; case 'l': - cp = tstr + 1; /* make sure it is set */ - if (STRMOD_MATCHX(tstr, "localtime", 9)) { - if (tstr[9] == '=') { - utc = strtoul(&tstr[10], &ep, 10); - cp = ep; - } else { - utc = 0; - cp = tstr + 9; - } - newStr = VarStrftime(nstr, 0, utc); - termc = *cp; - } else { + if (!ApplyModifier_Localtime(&st)) goto default_case; - } break; case 't': - { - cp = tstr + 1; /* make sure it is set */ - if (tstr[1] != endc && tstr[1] != ':') { - if (tstr[1] == 's') { - /* - * Use the char (if any) at tstr[2] - * as the word separator. - */ - VarPattern pattern; - - if (tstr[2] != endc && - (tstr[3] == endc || tstr[3] == ':')) { - /* ":ts<unrecognised><endc>" or - * ":ts<unrecognised>:" */ - parsestate.varSpace = tstr[2]; - cp = tstr + 3; - } else if (tstr[2] == endc || tstr[2] == ':') { - /* ":ts<endc>" or ":ts:" */ - parsestate.varSpace = 0; /* no separator */ - cp = tstr + 2; - } else if (tstr[2] == '\\') { - const char *xp = &tstr[3]; - int base = 8; /* assume octal */ - - switch (tstr[3]) { - case 'n': - parsestate.varSpace = '\n'; - cp = tstr + 4; - break; - case 't': - parsestate.varSpace = '\t'; - cp = tstr + 4; - break; - case 'x': - base = 16; - xp++; - goto get_numeric; - case '0': - base = 0; - goto get_numeric; - default: - if (isdigit((unsigned char)tstr[3])) { - - get_numeric: - parsestate.varSpace = - strtoul(xp, &ep, base); - if (*ep != ':' && *ep != endc) - goto bad_modifier; - cp = ep; - } else { - /* - * ":ts<backslash><unrecognised>". - */ - goto bad_modifier; - } - break; - } - } else { - /* - * Found ":ts<unrecognised><unrecognised>". - */ - goto bad_modifier; - } - - termc = *cp; - - /* - * We cannot be certain that VarModify - * will be used - even if there is a - * subsequent modifier, so do a no-op - * VarSubstitute now to for str to be - * re-expanded without the spaces. - */ - pattern.flags = VAR_SUB_ONE; - pattern.lhs = pattern.rhs = "\032"; - pattern.leftLen = pattern.rightLen = 1; - - newStr = VarModify(ctxt, &parsestate, nstr, - VarSubstitute, - &pattern); - } else if (tstr[2] == endc || tstr[2] == ':') { - /* - * Check for two-character options: - * ":tu", ":tl" - */ - if (tstr[1] == 'A') { /* absolute path */ - newStr = VarModify(ctxt, &parsestate, nstr, - VarRealpath, NULL); - cp = tstr + 2; - termc = *cp; - } else if (tstr[1] == 'u') { - char *dp = bmake_strdup(nstr); - for (newStr = dp; *dp; dp++) - *dp = toupper((unsigned char)*dp); - cp = tstr + 2; - termc = *cp; - } else if (tstr[1] == 'l') { - char *dp = bmake_strdup(nstr); - for (newStr = dp; *dp; dp++) - *dp = tolower((unsigned char)*dp); - cp = tstr + 2; - termc = *cp; - } else if (tstr[1] == 'W' || tstr[1] == 'w') { - parsestate.oneBigWord = (tstr[1] == 'W'); - newStr = nstr; - cp = tstr + 2; - termc = *cp; - } else { - /* Found ":t<unrecognised>:" or - * ":t<unrecognised><endc>". */ - goto bad_modifier; - } - } else { - /* - * Found ":t<unrecognised><unrecognised>". - */ - goto bad_modifier; - } - } else { - /* - * Found ":t<endc>" or ":t:". - */ - goto bad_modifier; - } - break; - } + if (!ApplyModifier_To(&st)) + goto bad_modifier; + break; case 'N': case 'M': - { - char *pattern; - const char *endpat; /* points just after end of pattern */ - char *cp2; - Boolean copy; /* pattern should be, or has been, copied */ - Boolean needSubst; - int nest; - - copy = FALSE; - needSubst = FALSE; - nest = 1; - /* - * In the loop below, ignore ':' unless we are at - * (or back to) the original brace level. - * XXX This will likely not work right if $() and ${} - * are intermixed. - */ - for (cp = tstr + 1; - *cp != '\0' && !(*cp == ':' && nest == 1); - cp++) - { - if (*cp == '\\' && - (cp[1] == ':' || - cp[1] == endc || cp[1] == startc)) { - if (!needSubst) { - copy = TRUE; - } - cp++; - continue; - } - if (*cp == '$') { - needSubst = TRUE; - } - if (*cp == '(' || *cp == '{') - ++nest; - if (*cp == ')' || *cp == '}') { - --nest; - if (nest == 0) - break; - } - } - termc = *cp; - endpat = cp; - if (copy) { - /* - * Need to compress the \:'s out of the pattern, so - * allocate enough room to hold the uncompressed - * pattern (note that cp started at tstr+1, so - * cp - tstr takes the null byte into account) and - * compress the pattern into the space. - */ - pattern = bmake_malloc(cp - tstr); - for (cp2 = pattern, cp = tstr + 1; - cp < endpat; - cp++, cp2++) - { - if ((*cp == '\\') && (cp+1 < endpat) && - (cp[1] == ':' || cp[1] == endc)) { - cp++; - } - *cp2 = *cp; - } - *cp2 = '\0'; - endpat = cp2; - } else { - /* - * Either Var_Subst or VarModify will need a - * nul-terminated string soon, so construct one now. - */ - pattern = bmake_strndup(tstr+1, endpat - (tstr + 1)); - } - if (needSubst) { - /* - * pattern contains embedded '$', so use Var_Subst to - * expand it. - */ - cp2 = pattern; - pattern = Var_Subst(NULL, cp2, ctxt, flags | VARF_WANTRES); - free(cp2); - } - if (DEBUG(VAR)) - fprintf(debug_file, "Pattern[%s] for [%s] is [%s]\n", - v->name, nstr, pattern); - if (*tstr == 'M') { - newStr = VarModify(ctxt, &parsestate, nstr, VarMatch, - pattern); - } else { - newStr = VarModify(ctxt, &parsestate, nstr, VarNoMatch, - pattern); - } - free(pattern); - break; - } + ApplyModifier_Match(&st); + break; case 'S': - { - VarPattern pattern; - Var_Parse_State tmpparsestate; - - pattern.flags = 0; - tmpparsestate = parsestate; - delim = tstr[1]; - tstr += 2; - - /* - * If pattern begins with '^', it is anchored to the - * start of the word -- skip over it and flag pattern. - */ - if (*tstr == '^') { - pattern.flags |= VAR_MATCH_START; - tstr += 1; - } - - cp = tstr; - if ((pattern.lhs = VarGetPattern(ctxt, &parsestate, flags, - &cp, delim, - &pattern.flags, - &pattern.leftLen, - NULL)) == NULL) - goto cleanup; - - if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, flags, - &cp, delim, NULL, - &pattern.rightLen, - &pattern)) == NULL) - goto cleanup; - - /* - * Check for global substitution. If 'g' after the final - * delimiter, substitution is global and is marked that - * way. - */ - for (;; cp++) { - switch (*cp) { - case 'g': - pattern.flags |= VAR_SUB_GLOBAL; - continue; - case '1': - pattern.flags |= VAR_SUB_ONE; - continue; - case 'W': - tmpparsestate.oneBigWord = TRUE; - continue; - } - break; - } - - termc = *cp; - newStr = VarModify(ctxt, &tmpparsestate, nstr, - VarSubstitute, - &pattern); - - /* - * Free the two strings. - */ - free(UNCONST(pattern.lhs)); - free(UNCONST(pattern.rhs)); - delim = '\0'; - break; - } + if (!ApplyModifier_Subst(&st)) + goto cleanup; + break; case '?': - { - VarPattern pattern; - Boolean value; - int cond_rc; - int lhs_flags, rhs_flags; - - /* find ':', and then substitute accordingly */ - if (flags & VARF_WANTRES) { - cond_rc = Cond_EvalExpression(NULL, v->name, &value, 0, FALSE); - if (cond_rc == COND_INVALID) { - lhs_flags = rhs_flags = VAR_NOSUBST; - } else if (value) { - lhs_flags = 0; - rhs_flags = VAR_NOSUBST; - } else { - lhs_flags = VAR_NOSUBST; - rhs_flags = 0; - } - } else { - /* we are just consuming and discarding */ - cond_rc = value = 0; - lhs_flags = rhs_flags = VAR_NOSUBST; - } - pattern.flags = 0; - - cp = ++tstr; - delim = ':'; - if ((pattern.lhs = VarGetPattern(ctxt, &parsestate, flags, - &cp, delim, &lhs_flags, - &pattern.leftLen, - NULL)) == NULL) - goto cleanup; - - /* BROPEN or PROPEN */ - delim = endc; - if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, flags, - &cp, delim, &rhs_flags, - &pattern.rightLen, - NULL)) == NULL) - goto cleanup; - - termc = *--cp; - delim = '\0'; - if (cond_rc == COND_INVALID) { - Error("Bad conditional expression `%s' in %s?%s:%s", - v->name, v->name, pattern.lhs, pattern.rhs); - goto cleanup; - } - - if (value) { - newStr = UNCONST(pattern.lhs); - free(UNCONST(pattern.rhs)); - } else { - newStr = UNCONST(pattern.rhs); - free(UNCONST(pattern.lhs)); - } - if (v->flags & VAR_JUNK) { - v->flags |= VAR_KEEP; - } - break; - } + if (!ApplyModifier_IfElse(&st)) + goto cleanup; + break; #ifndef NO_REGEX case 'C': - { - VarREPattern pattern; - char *re; - int error; - Var_Parse_State tmpparsestate; - - pattern.flags = 0; - tmpparsestate = parsestate; - delim = tstr[1]; - tstr += 2; - - cp = tstr; - - if ((re = VarGetPattern(ctxt, &parsestate, flags, &cp, delim, - NULL, NULL, NULL)) == NULL) - goto cleanup; - - if ((pattern.replace = VarGetPattern(ctxt, &parsestate, - flags, &cp, delim, NULL, - NULL, NULL)) == NULL){ - free(re); - goto cleanup; - } - - for (;; cp++) { - switch (*cp) { - case 'g': - pattern.flags |= VAR_SUB_GLOBAL; - continue; - case '1': - pattern.flags |= VAR_SUB_ONE; - continue; - case 'W': - tmpparsestate.oneBigWord = TRUE; - continue; - } - break; - } - - termc = *cp; - - error = regcomp(&pattern.re, re, REG_EXTENDED); - free(re); - if (error) { - *lengthPtr = cp - start + 1; - VarREError(error, &pattern.re, "RE substitution error"); - free(pattern.replace); - goto cleanup; - } - - pattern.nsub = pattern.re.re_nsub + 1; - if (pattern.nsub < 1) - pattern.nsub = 1; - if (pattern.nsub > 10) - pattern.nsub = 10; - pattern.matches = bmake_malloc(pattern.nsub * - sizeof(regmatch_t)); - newStr = VarModify(ctxt, &tmpparsestate, nstr, - VarRESubstitute, - &pattern); - regfree(&pattern.re); - free(pattern.replace); - free(pattern.matches); - delim = '\0'; - break; - } + if (!ApplyModifier_Regex(&st)) + goto cleanup; + break; #endif case 'q': case 'Q': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarQuote(nstr, modifier == 'q'); - cp = tstr + 1; - termc = *cp; + if (st.tstr[1] == st.endc || st.tstr[1] == ':') { + st.newStr = VarQuote(st.nstr, st.modifier == 'q'); + st.cp = st.tstr + 1; + st.termc = *st.cp; break; } goto default_case; case 'T': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify(ctxt, &parsestate, nstr, VarTail, + if (st.tstr[1] == st.endc || st.tstr[1] == ':') { + st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarTail, NULL); - cp = tstr + 1; - termc = *cp; + st.cp = st.tstr + 1; + st.termc = *st.cp; break; } goto default_case; case 'H': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify(ctxt, &parsestate, nstr, VarHead, + if (st.tstr[1] == st.endc || st.tstr[1] == ':') { + st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarHead, NULL); - cp = tstr + 1; - termc = *cp; + st.cp = st.tstr + 1; + st.termc = *st.cp; break; } goto default_case; case 'E': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify(ctxt, &parsestate, nstr, VarSuffix, + if (st.tstr[1] == st.endc || st.tstr[1] == ':') { + st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarSuffix, NULL); - cp = tstr + 1; - termc = *cp; + st.cp = st.tstr + 1; + st.termc = *st.cp; break; } goto default_case; case 'R': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify(ctxt, &parsestate, nstr, VarRoot, + if (st.tstr[1] == st.endc || st.tstr[1] == ':') { + st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarRoot, NULL); - cp = tstr + 1; - termc = *cp; + st.cp = st.tstr + 1; + st.termc = *st.cp; break; } goto default_case; case 'r': - cp = tstr + 1; /* make sure it is set */ - if (STRMOD_MATCHX(tstr, "range", 5)) { - int n; - - if (tstr[5] == '=') { - n = strtoul(&tstr[6], &ep, 10); - cp = ep; - } else { - n = 0; - cp = tstr + 5; - } - newStr = VarRange(nstr, n); - termc = *cp; - break; - } - goto default_case; + if (!ApplyModifier_Range(&st)) + goto default_case; + break; case 'O': - { - char otype; - - cp = tstr + 1; /* skip to the rest in any case */ - if (tstr[1] == endc || tstr[1] == ':') { - otype = 's'; - termc = *cp; - } else if ( (tstr[1] == 'r' || tstr[1] == 'x') && - (tstr[2] == endc || tstr[2] == ':') ) { - otype = tstr[1]; - cp = tstr + 2; - termc = *cp; - } else { - goto bad_modifier; - } - newStr = VarOrder(nstr, otype); - break; - } + if (!ApplyModifier_Order(&st)) + goto bad_modifier; + break; case 'u': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarUniq(nstr); - cp = tstr + 1; - termc = *cp; + if (st.tstr[1] == st.endc || st.tstr[1] == ':') { + st.newStr = VarUniq(st.nstr); + st.cp = st.tstr + 1; + st.termc = *st.cp; break; } goto default_case; #ifdef SUNSHCMD case 's': - if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) { + if (st.tstr[1] == 'h' && (st.tstr[2] == st.endc || st.tstr[2] == ':')) { const char *emsg; - if (flags & VARF_WANTRES) { - newStr = Cmd_Exec(nstr, &emsg); + if (st.flags & VARF_WANTRES) { + st.newStr = Cmd_Exec(st.nstr, &emsg); if (emsg) - Error(emsg, nstr); + Error(emsg, st.nstr); } else - newStr = varNoError; - cp = tstr + 2; - termc = *cp; + st.newStr = varNoError; + st.cp = st.tstr + 2; + st.termc = *st.cp; break; } goto default_case; #endif default: default_case: - { + { #ifdef SYSVVARSUB - /* - * This can either be a bogus modifier or a System-V - * substitution command. - */ - VarPattern pattern; - Boolean eqFound; - - pattern.flags = 0; - eqFound = FALSE; - /* - * First we make a pass through the string trying - * to verify it is a SYSV-make-style translation: - * it must be: <string1>=<string2>) - */ - cp = tstr; - cnt = 1; - while (*cp != '\0' && cnt) { - if (*cp == '=') { - eqFound = TRUE; - /* continue looking for endc */ - } - else if (*cp == endc) - cnt--; - else if (*cp == startc) - cnt++; - if (cnt) - cp++; - } - if (*cp == endc && eqFound) { - - /* - * Now we break this sucker into the lhs and - * rhs. We must null terminate them of course. - */ - delim='='; - cp = tstr; - if ((pattern.lhs = VarGetPattern(ctxt, &parsestate, - flags, &cp, delim, &pattern.flags, - &pattern.leftLen, NULL)) == NULL) + int res = ApplyModifier_SysV(&st); + if (res == 'c') goto cleanup; - delim = endc; - if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, - flags, &cp, delim, NULL, &pattern.rightLen, - &pattern)) == NULL) - goto cleanup; - - /* - * SYSV modifications happen through the whole - * string. Note the pattern is anchored at the end. - */ - termc = *--cp; - delim = '\0'; - if (pattern.leftLen == 0 && *nstr == '\0') { - newStr = nstr; /* special case */ - } else { - newStr = VarModify(ctxt, &parsestate, nstr, - VarSYSVMatch, - &pattern); - } - free(UNCONST(pattern.lhs)); - free(UNCONST(pattern.rhs)); - } else + if (res != '=') #endif { - Error("Unknown modifier '%c'", *tstr); - for (cp = tstr+1; - *cp != ':' && *cp != endc && *cp != '\0'; - cp++) + Error("Unknown modifier '%c'", *st.tstr); + for (st.cp = st.tstr+1; + *st.cp != ':' && *st.cp != st.endc && *st.cp != '\0'; + st.cp++) continue; - termc = *cp; - newStr = var_Error; + st.termc = *st.cp; + st.newStr = var_Error; } } } if (DEBUG(VAR)) { fprintf(debug_file, "Result[%s] of :%c is \"%s\"\n", - v->name, modifier, newStr); + st.v->name, st.modifier, st.newStr); } - if (newStr != nstr) { - if (*freePtr) { - free(nstr); - *freePtr = NULL; + if (st.newStr != st.nstr) { + if (*st.freePtr) { + free(st.nstr); + *st.freePtr = NULL; } - nstr = newStr; - if (nstr != var_Error && nstr != varNoError) { - *freePtr = nstr; + st.nstr = st.newStr; + if (st.nstr != var_Error && st.nstr != varNoError) { + *st.freePtr = st.nstr; } } - if (termc == '\0' && endc != '\0') { - Error("Unclosed variable specification (expecting '%c') for \"%s\" (value \"%s\") modifier %c", endc, v->name, nstr, modifier); - } else if (termc == ':') { - cp++; + if (st.termc == '\0' && st.endc != '\0') { + Error("Unclosed variable specification (expecting '%c') " + "for \"%s\" (value \"%s\") modifier %c", + st.endc, st.v->name, st.nstr, st.modifier); + } else if (st.termc == ':') { + st.cp++; } - tstr = cp; + st.tstr = st.cp; } - out: - *lengthPtr = tstr - start; - return (nstr); +out: + *st.lengthPtr = st.tstr - st.start; + return st.nstr; - bad_modifier: +bad_modifier: /* "{(" */ - Error("Bad modifier `:%.*s' for %s", (int)strcspn(tstr, ":)}"), tstr, - v->name); + Error("Bad modifier `:%.*s' for %s", (int)strcspn(st.tstr, ":)}"), st.tstr, + st.v->name); - cleanup: - *lengthPtr = cp - start; - if (delim != '\0') +cleanup: + *st.lengthPtr = st.cp - st.start; + if (st.delim != '\0') Error("Unclosed substitution for %s (%c missing)", - v->name, delim); - free(*freePtr); - *freePtr = NULL; - return (var_Error); + st.v->name, st.delim); + free(*st.freePtr); + *st.freePtr = NULL; + return var_Error; } /*- @@ -3757,25 +3612,25 @@ ApplyModifiers(char *nstr, const char *tstr, */ /* coverity[+alloc : arg-*4] */ char * -Var_Parse(const char *str, GNode *ctxt, int flags, +Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, int *lengthPtr, void **freePtr) { - const char *tstr; /* Pointer into str */ - Var *v; /* Variable in invocation */ - Boolean haveModifier;/* TRUE if have modifiers for the variable */ - char endc; /* Ending character when variable in parens + const char *tstr; /* Pointer into str */ + Var *v; /* Variable in invocation */ + Boolean haveModifier; /* TRUE if have modifiers for the variable */ + char endc; /* Ending character when variable in parens * or braces */ - char startc; /* Starting character when variable in parens + char startc; /* Starting character when variable in parens * or braces */ - int vlen; /* Length of variable name */ - const char *start; /* Points to original start of str */ - char *nstr; /* New string, used during expansion */ - Boolean dynamic; /* TRUE if the variable is local and we're + int vlen; /* Length of variable name */ + const char *start; /* Points to original start of str */ + char *nstr; /* New string, used during expansion */ + Boolean dynamic; /* TRUE if the variable is local and we're * expanding it in a non-local context. This * is done to support dynamic sources. The * result is just the invocation, unaltered */ - const char *extramodifiers; /* extra modifiers to apply first */ - char name[2]; + const char *extramodifiers; /* extra modifiers to apply first */ + char name[2]; *freePtr = NULL; extramodifiers = NULL; @@ -3813,19 +3668,16 @@ Var_Parse(const char *str, GNode *ctxt, int flags, * when dynamic sources are expanded. */ switch (str[1]) { - case '@': - return UNCONST("$(.TARGET)"); - case '%': - return UNCONST("$(.MEMBER)"); - case '*': - return UNCONST("$(.PREFIX)"); - case '!': - return UNCONST("$(.ARCHIVE)"); + case '@': + return UNCONST("$(.TARGET)"); + case '%': + return UNCONST("$(.MEMBER)"); + case '*': + return UNCONST("$(.PREFIX)"); + case '!': + return UNCONST("$(.ARCHIVE)"); } } - /* - * Error - */ return (flags & VARF_UNDEFERR) ? var_Error : varNoError; } else { haveModifier = FALSE; @@ -3833,7 +3685,7 @@ Var_Parse(const char *str, GNode *ctxt, int flags, endc = str[1]; } } else { - Buffer buf; /* Holds the variable name */ + Buffer buf; /* Holds the variable name */ int depth = 1; endc = startc == PROPEN ? PRCLOSE : BRCLOSE; @@ -3842,35 +3694,26 @@ Var_Parse(const char *str, GNode *ctxt, int flags, /* * Skip to the end character or a colon, whichever comes first. */ - for (tstr = str + 2; *tstr != '\0'; tstr++) - { - /* - * Track depth so we can spot parse errors. - */ - if (*tstr == startc) { + for (tstr = str + 2; *tstr != '\0'; tstr++) { + /* Track depth so we can spot parse errors. */ + if (*tstr == startc) depth++; - } if (*tstr == endc) { if (--depth == 0) break; } - if (depth == 1 && *tstr == ':') { + if (depth == 1 && *tstr == ':') break; - } - /* - * A variable inside a variable, expand - */ + /* A variable inside a variable, expand. */ if (*tstr == '$') { int rlen; void *freeIt; - char *rval = Var_Parse(tstr, ctxt, flags, &rlen, &freeIt); - if (rval != NULL) { + char *rval = Var_Parse(tstr, ctxt, flags, &rlen, &freeIt); + if (rval != NULL) Buf_AddBytes(&buf, strlen(rval), rval); - } free(freeIt); tstr += rlen - 1; - } - else + } else Buf_AddByte(&buf, *tstr); } if (*tstr == ':') { @@ -3885,7 +3728,7 @@ Var_Parse(const char *str, GNode *ctxt, int flags, */ *lengthPtr = tstr - str; Buf_Destroy(&buf, TRUE); - return (var_Error); + return var_Error; } str = Buf_GetAll(&buf, &vlen); @@ -3917,10 +3760,9 @@ Var_Parse(const char *str, GNode *ctxt, int flags, if (v != NULL) { if (str[1] == 'D') { - extramodifiers = "H:"; - } - else { /* F */ - extramodifiers = "T:"; + extramodifiers = "H:"; + } else { /* F */ + extramodifiers = "T:"; } } } @@ -3940,20 +3782,18 @@ Var_Parse(const char *str, GNode *ctxt, int flags, * when dynamic sources are expanded. */ switch (*str) { - case '@': - case '%': - case '*': - case '!': - dynamic = TRUE; - break; + case '@': + case '%': + case '*': + case '!': + dynamic = TRUE; + break; } - } else if ((vlen > 2) && (*str == '.') && + } else if (vlen > 2 && *str == '.' && isupper((unsigned char) str[1]) && - ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) + (ctxt == VAR_CMD || ctxt == VAR_GLOBAL)) { - int len; - - len = vlen - 1; + int len = vlen - 1; if ((strncmp(str, ".TARGET", len) == 0) || (strncmp(str, ".ARCHIVE", len) == 0) || (strncmp(str, ".PREFIX", len) == 0) || @@ -3973,7 +3813,7 @@ Var_Parse(const char *str, GNode *ctxt, int flags, char *pstr = bmake_strndup(start, *lengthPtr); *freePtr = pstr; Buf_Destroy(&buf, TRUE); - return(pstr); + return pstr; } else { Buf_Destroy(&buf, TRUE); return (flags & VARF_UNDEFERR) ? var_Error : varNoError; @@ -4009,43 +3849,39 @@ Var_Parse(const char *str, GNode *ctxt, int flags, * return. */ nstr = Buf_GetAll(&v->val, NULL); - if (strchr(nstr, '$') != NULL) { + if (strchr(nstr, '$') != NULL && (flags & VARF_WANTRES) != 0) { nstr = Var_Subst(NULL, nstr, ctxt, flags); *freePtr = nstr; } v->flags &= ~VAR_IN_USE; - if ((nstr != NULL) && (haveModifier || extramodifiers != NULL)) { + if (nstr != NULL && (haveModifier || extramodifiers != NULL)) { void *extraFree; int used; extraFree = NULL; if (extramodifiers != NULL) { - nstr = ApplyModifiers(nstr, extramodifiers, '(', ')', - v, ctxt, flags, &used, &extraFree); + nstr = ApplyModifiers(nstr, extramodifiers, '(', ')', + v, ctxt, flags, &used, &extraFree); } if (haveModifier) { - /* Skip initial colon. */ - tstr++; + /* Skip initial colon. */ + tstr++; - nstr = ApplyModifiers(nstr, tstr, startc, endc, - v, ctxt, flags, &used, freePtr); - tstr += used; - free(extraFree); + nstr = ApplyModifiers(nstr, tstr, startc, endc, + v, ctxt, flags, &used, freePtr); + tstr += used; + free(extraFree); } else { - *freePtr = extraFree; + *freePtr = extraFree; } } - if (*tstr) { - *lengthPtr = tstr - start + 1; - } else { - *lengthPtr = tstr - start; - } + *lengthPtr = tstr - start + (*tstr ? 1 : 0); if (v->flags & VAR_FROM_ENV) { - Boolean destroy = FALSE; + Boolean destroy = FALSE; if (nstr != Buf_GetAll(&v->val, NULL)) { destroy = TRUE; @@ -4080,13 +3916,13 @@ Var_Parse(const char *str, GNode *ctxt, int flags, free(v->name); free(v); } - return (nstr); + return nstr; } /*- *----------------------------------------------------------------------- * Var_Subst -- - * Substitute for all variables in the given string in the given context + * Substitute for all variables in the given string in the given context. * If flags & VARF_UNDEFERR, Parse_Error will be called when an undefined * variable is encountered. * @@ -4102,20 +3938,20 @@ Var_Parse(const char *str, GNode *ctxt, int flags, * The resulting string. * * Side Effects: - * None. The old string must be freed by the caller + * None. *----------------------------------------------------------------------- */ char * -Var_Subst(const char *var, const char *str, GNode *ctxt, int flags) +Var_Subst(const char *var, const char *str, GNode *ctxt, Varf_Flags flags) { - Buffer buf; /* Buffer for forming things */ - char *val; /* Value to substitute for a variable */ - int length; /* Length of the variable invocation */ - Boolean trailingBslash; /* variable ends in \ */ - void *freeIt = NULL; /* Set if it should be freed */ - static Boolean errorReported; /* Set true if an error has already - * been reported to prevent a plethora - * of messages when recursing */ + Buffer buf; /* Buffer for forming things */ + char *val; /* Value to substitute for a variable */ + int length; /* Length of the variable invocation */ + Boolean trailingBslash; /* variable ends in \ */ + void *freeIt = NULL; /* Set if it should be freed */ + static Boolean errorReported; /* Set true if an error has already + * been reported to prevent a plethora + * of messages when recursing */ Buf_Init(&buf, 0); errorReported = FALSE; @@ -4140,7 +3976,7 @@ Var_Subst(const char *var, const char *str, GNode *ctxt, int flags) * Skip as many characters as possible -- either to the end of * the string or to the next dollar sign (variable invocation). */ - const char *cp; + const char *cp; for (cp = str++; *str != '$' && *str != '\0'; str++) continue; @@ -4159,17 +3995,13 @@ Var_Subst(const char *var, const char *str, GNode *ctxt, int flags) Buf_AddBytes(&buf, 2, str); str += 2; expand = FALSE; - } - else + } else expand = TRUE; break; - } - else { + } else { const char *p; - /* - * Scan up to the end of the variable name. - */ + /* Scan up to the end of the variable name. */ for (p = &str[2]; *p && *p != ':' && *p != PRCLOSE && *p != BRCLOSE; p++) if (*p == '$') @@ -4191,13 +4023,12 @@ Var_Subst(const char *var, const char *str, GNode *ctxt, int flags) * Not the variable we want to expand, scan * until the next variable */ - for (;*p != '$' && *p != '\0'; p++) + for (; *p != '$' && *p != '\0'; p++) continue; Buf_AddBytes(&buf, p - str, str); str = p; expand = FALSE; - } - else + } else expand = TRUE; break; } @@ -4230,8 +4061,8 @@ Var_Subst(const char *var, const char *str, GNode *ctxt, int flags) * when the file is parsed. */ if (!errorReported) { - Parse_Error(PARSE_FATAL, - "Undefined variable \"%.*s\"",length,str); + Parse_Error(PARSE_FATAL, "Undefined variable \"%.*s\"", + length, str); } str += length; errorReported = TRUE; @@ -4262,74 +4093,13 @@ Var_Subst(const char *var, const char *str, GNode *ctxt, int flags) return Buf_DestroyCompact(&buf); } -/*- - *----------------------------------------------------------------------- - * Var_GetTail -- - * Return the tail from each of a list of words. Used to set the - * System V local variables. - * - * Input: - * file Filename to modify - * - * Results: - * The resulting string. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -#if 0 -char * -Var_GetTail(char *file) -{ - return(VarModify(file, VarTail, NULL)); -} - -/*- - *----------------------------------------------------------------------- - * Var_GetHead -- - * Find the leading components of a (list of) filename(s). - * XXX: VarHead does not replace foo by ., as (sun) System V make - * does. - * - * Input: - * file Filename to manipulate - * - * Results: - * The leading components. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -char * -Var_GetHead(char *file) -{ - return(VarModify(file, VarHead, NULL)); -} -#endif - -/*- - *----------------------------------------------------------------------- - * Var_Init -- - * Initialize the module - * - * Results: - * None - * - * Side Effects: - * The VAR_CMD and VAR_GLOBAL contexts are created - *----------------------------------------------------------------------- - */ +/* Initialize the module. */ void Var_Init(void) { VAR_INTERNAL = Targ_NewGN("Internal"); VAR_GLOBAL = Targ_NewGN("Global"); VAR_CMD = Targ_NewGN("Command"); - } @@ -4341,9 +4111,9 @@ Var_End(void) /****************** PRINT DEBUGGING INFO *****************/ static void -VarPrintVar(void *vp) +VarPrintVar(void *vp, void *data MAKE_ATTR_UNUSED) { - Var *v = (Var *)vp; + Var *v = (Var *)vp; fprintf(debug_file, "%-16s = %s\n", v->name, Buf_GetAll(&v->val, NULL)); } @@ -4356,12 +4126,5 @@ VarPrintVar(void *vp) void Var_Dump(GNode *ctxt) { - Hash_Search search; - Hash_Entry *h; - - for (h = Hash_EnumFirst(&ctxt->context, &search); - h != NULL; - h = Hash_EnumNext(&search)) { - VarPrintVar(Hash_GetValue(h)); - } + Hash_ForEach(&ctxt->context, VarPrintVar, NULL); } |