1*0a6a1f1dSLionel Sambuc /* $NetBSD: var.c,v 1.196 2015/10/06 17:36:25 christos Exp $ */
22e2caf59SThomas Veerman
32e2caf59SThomas Veerman /*
42e2caf59SThomas Veerman * Copyright (c) 1988, 1989, 1990, 1993
52e2caf59SThomas Veerman * The Regents of the University of California. All rights reserved.
62e2caf59SThomas Veerman *
72e2caf59SThomas Veerman * This code is derived from software contributed to Berkeley by
82e2caf59SThomas Veerman * Adam de Boor.
92e2caf59SThomas Veerman *
102e2caf59SThomas Veerman * Redistribution and use in source and binary forms, with or without
112e2caf59SThomas Veerman * modification, are permitted provided that the following conditions
122e2caf59SThomas Veerman * are met:
132e2caf59SThomas Veerman * 1. Redistributions of source code must retain the above copyright
142e2caf59SThomas Veerman * notice, this list of conditions and the following disclaimer.
152e2caf59SThomas Veerman * 2. Redistributions in binary form must reproduce the above copyright
162e2caf59SThomas Veerman * notice, this list of conditions and the following disclaimer in the
172e2caf59SThomas Veerman * documentation and/or other materials provided with the distribution.
182e2caf59SThomas Veerman * 3. Neither the name of the University nor the names of its contributors
192e2caf59SThomas Veerman * may be used to endorse or promote products derived from this software
202e2caf59SThomas Veerman * without specific prior written permission.
212e2caf59SThomas Veerman *
222e2caf59SThomas Veerman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
232e2caf59SThomas Veerman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
242e2caf59SThomas Veerman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
252e2caf59SThomas Veerman * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
262e2caf59SThomas Veerman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
272e2caf59SThomas Veerman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
282e2caf59SThomas Veerman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
292e2caf59SThomas Veerman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
302e2caf59SThomas Veerman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
312e2caf59SThomas Veerman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
322e2caf59SThomas Veerman * SUCH DAMAGE.
332e2caf59SThomas Veerman */
342e2caf59SThomas Veerman
352e2caf59SThomas Veerman /*
362e2caf59SThomas Veerman * Copyright (c) 1989 by Berkeley Softworks
372e2caf59SThomas Veerman * All rights reserved.
382e2caf59SThomas Veerman *
392e2caf59SThomas Veerman * This code is derived from software contributed to Berkeley by
402e2caf59SThomas Veerman * Adam de Boor.
412e2caf59SThomas Veerman *
422e2caf59SThomas Veerman * Redistribution and use in source and binary forms, with or without
432e2caf59SThomas Veerman * modification, are permitted provided that the following conditions
442e2caf59SThomas Veerman * are met:
452e2caf59SThomas Veerman * 1. Redistributions of source code must retain the above copyright
462e2caf59SThomas Veerman * notice, this list of conditions and the following disclaimer.
472e2caf59SThomas Veerman * 2. Redistributions in binary form must reproduce the above copyright
482e2caf59SThomas Veerman * notice, this list of conditions and the following disclaimer in the
492e2caf59SThomas Veerman * documentation and/or other materials provided with the distribution.
502e2caf59SThomas Veerman * 3. All advertising materials mentioning features or use of this software
512e2caf59SThomas Veerman * must display the following acknowledgement:
522e2caf59SThomas Veerman * This product includes software developed by the University of
532e2caf59SThomas Veerman * California, Berkeley and its contributors.
542e2caf59SThomas Veerman * 4. Neither the name of the University nor the names of its contributors
552e2caf59SThomas Veerman * may be used to endorse or promote products derived from this software
562e2caf59SThomas Veerman * without specific prior written permission.
572e2caf59SThomas Veerman *
582e2caf59SThomas Veerman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
592e2caf59SThomas Veerman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
602e2caf59SThomas Veerman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
612e2caf59SThomas Veerman * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
622e2caf59SThomas Veerman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
632e2caf59SThomas Veerman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
642e2caf59SThomas Veerman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
652e2caf59SThomas Veerman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
662e2caf59SThomas Veerman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
672e2caf59SThomas Veerman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
682e2caf59SThomas Veerman * SUCH DAMAGE.
692e2caf59SThomas Veerman */
702e2caf59SThomas Veerman
712e2caf59SThomas Veerman #ifndef MAKE_NATIVE
72*0a6a1f1dSLionel Sambuc static char rcsid[] = "$NetBSD: var.c,v 1.196 2015/10/06 17:36:25 christos Exp $";
732e2caf59SThomas Veerman #else
742e2caf59SThomas Veerman #include <sys/cdefs.h>
752e2caf59SThomas Veerman #ifndef lint
762e2caf59SThomas Veerman #if 0
772e2caf59SThomas Veerman static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94";
782e2caf59SThomas Veerman #else
79*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: var.c,v 1.196 2015/10/06 17:36:25 christos Exp $");
802e2caf59SThomas Veerman #endif
812e2caf59SThomas Veerman #endif /* not lint */
822e2caf59SThomas Veerman #endif
832e2caf59SThomas Veerman
842e2caf59SThomas Veerman /*-
852e2caf59SThomas Veerman * var.c --
862e2caf59SThomas Veerman * Variable-handling functions
872e2caf59SThomas Veerman *
882e2caf59SThomas Veerman * Interface:
892e2caf59SThomas Veerman * Var_Set Set the value of a variable in the given
902e2caf59SThomas Veerman * context. The variable is created if it doesn't
912e2caf59SThomas Veerman * yet exist. The value and variable name need not
922e2caf59SThomas Veerman * be preserved.
932e2caf59SThomas Veerman *
942e2caf59SThomas Veerman * Var_Append Append more characters to an existing variable
952e2caf59SThomas Veerman * in the given context. The variable needn't
962e2caf59SThomas Veerman * exist already -- it will be created if it doesn't.
972e2caf59SThomas Veerman * A space is placed between the old value and the
982e2caf59SThomas Veerman * new one.
992e2caf59SThomas Veerman *
1002e2caf59SThomas Veerman * Var_Exists See if a variable exists.
1012e2caf59SThomas Veerman *
1022e2caf59SThomas Veerman * Var_Value Return the value of a variable in a context or
1032e2caf59SThomas Veerman * NULL if the variable is undefined.
1042e2caf59SThomas Veerman *
1052e2caf59SThomas Veerman * Var_Subst Substitute named variable, or all variables if
1062e2caf59SThomas Veerman * NULL in a string using
1072e2caf59SThomas Veerman * the given context as the top-most one. If the
1082e2caf59SThomas Veerman * third argument is non-zero, Parse_Error is
1092e2caf59SThomas Veerman * called if any variables are undefined.
1102e2caf59SThomas Veerman *
1112e2caf59SThomas Veerman * Var_Parse Parse a variable expansion from a string and
1122e2caf59SThomas Veerman * return the result and the number of characters
1132e2caf59SThomas Veerman * consumed.
1142e2caf59SThomas Veerman *
1152e2caf59SThomas Veerman * Var_Delete Delete a variable in a context.
1162e2caf59SThomas Veerman *
1172e2caf59SThomas Veerman * Var_Init Initialize this module.
1182e2caf59SThomas Veerman *
1192e2caf59SThomas Veerman * Debugging:
1202e2caf59SThomas Veerman * Var_Dump Print out all variables defined in the given
1212e2caf59SThomas Veerman * context.
1222e2caf59SThomas Veerman *
1232e2caf59SThomas Veerman * XXX: There's a lot of duplication in these functions.
1242e2caf59SThomas Veerman */
1252e2caf59SThomas Veerman
1262e2caf59SThomas Veerman #include <sys/stat.h>
1272e2caf59SThomas Veerman #ifndef NO_REGEX
1282e2caf59SThomas Veerman #include <sys/types.h>
1292e2caf59SThomas Veerman #include <regex.h>
1302e2caf59SThomas Veerman #endif
1312e2caf59SThomas Veerman #include <ctype.h>
1322e2caf59SThomas Veerman #include <inttypes.h>
1332e2caf59SThomas Veerman #include <stdlib.h>
1342e2caf59SThomas Veerman #include <limits.h>
1352e2caf59SThomas Veerman #include <time.h>
1362e2caf59SThomas Veerman
1372e2caf59SThomas Veerman #include "make.h"
1382e2caf59SThomas Veerman #include "buf.h"
1392e2caf59SThomas Veerman #include "dir.h"
1402e2caf59SThomas Veerman #include "job.h"
141*0a6a1f1dSLionel Sambuc #include "metachar.h"
1422e2caf59SThomas Veerman
14384d9c625SLionel Sambuc extern int makelevel;
1442e2caf59SThomas Veerman /*
1452bc7c627SLionel Sambuc * This lets us tell if we have replaced the original environ
1462bc7c627SLionel Sambuc * (which we cannot free).
1472bc7c627SLionel Sambuc */
1482bc7c627SLionel Sambuc char **savedEnv = NULL;
1492bc7c627SLionel Sambuc
1502bc7c627SLionel Sambuc /*
1512e2caf59SThomas Veerman * This is a harmless return value for Var_Parse that can be used by Var_Subst
1522e2caf59SThomas Veerman * to determine if there was an error in parsing -- easier than returning
1532e2caf59SThomas Veerman * a flag, as things outside this module don't give a hoot.
1542e2caf59SThomas Veerman */
1552e2caf59SThomas Veerman char var_Error[] = "";
1562e2caf59SThomas Veerman
1572e2caf59SThomas Veerman /*
1582e2caf59SThomas Veerman * Similar to var_Error, but returned when the 'errnum' flag for Var_Parse is
1592e2caf59SThomas Veerman * set false. Why not just use a constant? Well, gcc likes to condense
1602e2caf59SThomas Veerman * identical string instances...
1612e2caf59SThomas Veerman */
1622e2caf59SThomas Veerman static char varNoError[] = "";
1632e2caf59SThomas Veerman
1642e2caf59SThomas Veerman /*
1652e2caf59SThomas Veerman * Internally, variables are contained in four different contexts.
1662e2caf59SThomas Veerman * 1) the environment. They may not be changed. If an environment
1672e2caf59SThomas Veerman * variable is appended-to, the result is placed in the global
1682e2caf59SThomas Veerman * context.
1692e2caf59SThomas Veerman * 2) the global context. Variables set in the Makefile are located in
1702e2caf59SThomas Veerman * the global context. It is the penultimate context searched when
1712e2caf59SThomas Veerman * substituting.
1722e2caf59SThomas Veerman * 3) the command-line context. All variables set on the command line
1732e2caf59SThomas Veerman * are placed in this context. They are UNALTERABLE once placed here.
1742e2caf59SThomas Veerman * 4) the local context. Each target has associated with it a context
1752e2caf59SThomas Veerman * list. On this list are located the structures describing such
1762e2caf59SThomas Veerman * local variables as $(@) and $(*)
1772e2caf59SThomas Veerman * The four contexts are searched in the reverse order from which they are
1782e2caf59SThomas Veerman * listed.
1792e2caf59SThomas Veerman */
18084d9c625SLionel Sambuc GNode *VAR_INTERNAL; /* variables from make itself */
1812e2caf59SThomas Veerman GNode *VAR_GLOBAL; /* variables from the makefile */
1822e2caf59SThomas Veerman GNode *VAR_CMD; /* variables defined on the command-line */
1832e2caf59SThomas Veerman
1842e2caf59SThomas Veerman #define FIND_CMD 0x1 /* look in VAR_CMD when searching */
1852e2caf59SThomas Veerman #define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */
1862e2caf59SThomas Veerman #define FIND_ENV 0x4 /* look in the environment also */
1872e2caf59SThomas Veerman
1882e2caf59SThomas Veerman typedef struct Var {
1892e2caf59SThomas Veerman char *name; /* the variable's name */
1902e2caf59SThomas Veerman Buffer val; /* its value */
1912e2caf59SThomas Veerman int flags; /* miscellaneous status flags */
1922e2caf59SThomas Veerman #define VAR_IN_USE 1 /* Variable's value currently being used.
1932e2caf59SThomas Veerman * Used to avoid recursion */
1942e2caf59SThomas Veerman #define VAR_FROM_ENV 2 /* Variable comes from the environment */
1952e2caf59SThomas Veerman #define VAR_JUNK 4 /* Variable is a junk variable that
1962e2caf59SThomas Veerman * should be destroyed when done with
1972e2caf59SThomas Veerman * it. Used by Var_Parse for undefined,
1982e2caf59SThomas Veerman * modified variables */
1992e2caf59SThomas Veerman #define VAR_KEEP 8 /* Variable is VAR_JUNK, but we found
2002e2caf59SThomas Veerman * a use for it in some modifier and
2012e2caf59SThomas Veerman * the value is therefore valid */
2022e2caf59SThomas Veerman #define VAR_EXPORTED 16 /* Variable is exported */
2032e2caf59SThomas Veerman #define VAR_REEXPORT 32 /* Indicate if var needs re-export.
2042e2caf59SThomas Veerman * This would be true if it contains $'s
2052e2caf59SThomas Veerman */
2062e2caf59SThomas Veerman #define VAR_FROM_CMD 64 /* Variable came from command line */
2072e2caf59SThomas Veerman } Var;
2082e2caf59SThomas Veerman
2092e2caf59SThomas Veerman /*
2102e2caf59SThomas Veerman * Exporting vars is expensive so skip it if we can
2112e2caf59SThomas Veerman */
2122e2caf59SThomas Veerman #define VAR_EXPORTED_NONE 0
2132e2caf59SThomas Veerman #define VAR_EXPORTED_YES 1
2142e2caf59SThomas Veerman #define VAR_EXPORTED_ALL 2
2152e2caf59SThomas Veerman static int var_exportedVars = VAR_EXPORTED_NONE;
2162e2caf59SThomas Veerman /*
2172e2caf59SThomas Veerman * We pass this to Var_Export when doing the initial export
2182e2caf59SThomas Veerman * or after updating an exported var.
2192e2caf59SThomas Veerman */
2202e2caf59SThomas Veerman #define VAR_EXPORT_PARENT 1
2212e2caf59SThomas Veerman
2222e2caf59SThomas Veerman /* Var*Pattern flags */
2232e2caf59SThomas Veerman #define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */
2242e2caf59SThomas Veerman #define VAR_SUB_ONE 0x02 /* Apply substitution to one word */
2252e2caf59SThomas Veerman #define VAR_SUB_MATCHED 0x04 /* There was a match */
2262e2caf59SThomas Veerman #define VAR_MATCH_START 0x08 /* Match at start of word */
2272e2caf59SThomas Veerman #define VAR_MATCH_END 0x10 /* Match at end of word */
2282e2caf59SThomas Veerman #define VAR_NOSUBST 0x20 /* don't expand vars in VarGetPattern */
2292e2caf59SThomas Veerman
2302e2caf59SThomas Veerman /* Var_Set flags */
2312e2caf59SThomas Veerman #define VAR_NO_EXPORT 0x01 /* do not export */
2322e2caf59SThomas Veerman
2332e2caf59SThomas Veerman typedef struct {
2342e2caf59SThomas Veerman /*
2352e2caf59SThomas Veerman * The following fields are set by Var_Parse() when it
2362e2caf59SThomas Veerman * encounters modifiers that need to keep state for use by
2372e2caf59SThomas Veerman * subsequent modifiers within the same variable expansion.
2382e2caf59SThomas Veerman */
2392e2caf59SThomas Veerman Byte varSpace; /* Word separator in expansions */
2402e2caf59SThomas Veerman Boolean oneBigWord; /* TRUE if we will treat the variable as a
2412e2caf59SThomas Veerman * single big word, even if it contains
2422e2caf59SThomas Veerman * embedded spaces (as opposed to the
2432e2caf59SThomas Veerman * usual behaviour of treating it as
2442e2caf59SThomas Veerman * several space-separated words). */
2452e2caf59SThomas Veerman } Var_Parse_State;
2462e2caf59SThomas Veerman
2472e2caf59SThomas Veerman /* struct passed as 'void *' to VarSubstitute() for ":S/lhs/rhs/",
2482e2caf59SThomas Veerman * to VarSYSVMatch() for ":lhs=rhs". */
2492e2caf59SThomas Veerman typedef struct {
2502e2caf59SThomas Veerman const char *lhs; /* String to match */
2512e2caf59SThomas Veerman int leftLen; /* Length of string */
2522e2caf59SThomas Veerman const char *rhs; /* Replacement string (w/ &'s removed) */
2532e2caf59SThomas Veerman int rightLen; /* Length of replacement */
2542e2caf59SThomas Veerman int flags;
2552e2caf59SThomas Veerman } VarPattern;
2562e2caf59SThomas Veerman
2572e2caf59SThomas Veerman /* struct passed as 'void *' to VarLoopExpand() for ":@tvar@str@" */
2582e2caf59SThomas Veerman typedef struct {
2592e2caf59SThomas Veerman GNode *ctxt; /* variable context */
2602e2caf59SThomas Veerman char *tvar; /* name of temp var */
2612e2caf59SThomas Veerman int tvarLen;
2622e2caf59SThomas Veerman char *str; /* string to expand */
2632e2caf59SThomas Veerman int strLen;
2642e2caf59SThomas Veerman int errnum; /* errnum for not defined */
2652e2caf59SThomas Veerman } VarLoop_t;
2662e2caf59SThomas Veerman
2672e2caf59SThomas Veerman #ifndef NO_REGEX
2682e2caf59SThomas Veerman /* struct passed as 'void *' to VarRESubstitute() for ":C///" */
2692e2caf59SThomas Veerman typedef struct {
2702e2caf59SThomas Veerman regex_t re;
2712e2caf59SThomas Veerman int nsub;
2722e2caf59SThomas Veerman regmatch_t *matches;
2732e2caf59SThomas Veerman char *replace;
2742e2caf59SThomas Veerman int flags;
2752e2caf59SThomas Veerman } VarREPattern;
2762e2caf59SThomas Veerman #endif
2772e2caf59SThomas Veerman
2782e2caf59SThomas Veerman /* struct passed to VarSelectWords() for ":[start..end]" */
2792e2caf59SThomas Veerman typedef struct {
2802e2caf59SThomas Veerman int start; /* first word to select */
2812e2caf59SThomas Veerman int end; /* last word to select */
2822e2caf59SThomas Veerman } VarSelectWords_t;
2832e2caf59SThomas Veerman
2842e2caf59SThomas Veerman static Var *VarFind(const char *, GNode *, int);
2852e2caf59SThomas Veerman static void VarAdd(const char *, const char *, GNode *);
2862e2caf59SThomas Veerman static Boolean VarHead(GNode *, Var_Parse_State *,
2872e2caf59SThomas Veerman char *, Boolean, Buffer *, void *);
2882e2caf59SThomas Veerman static Boolean VarTail(GNode *, Var_Parse_State *,
2892e2caf59SThomas Veerman char *, Boolean, Buffer *, void *);
2902e2caf59SThomas Veerman static Boolean VarSuffix(GNode *, Var_Parse_State *,
2912e2caf59SThomas Veerman char *, Boolean, Buffer *, void *);
2922e2caf59SThomas Veerman static Boolean VarRoot(GNode *, Var_Parse_State *,
2932e2caf59SThomas Veerman char *, Boolean, Buffer *, void *);
2942e2caf59SThomas Veerman static Boolean VarMatch(GNode *, Var_Parse_State *,
2952e2caf59SThomas Veerman char *, Boolean, Buffer *, void *);
2962e2caf59SThomas Veerman #ifdef SYSVVARSUB
2972e2caf59SThomas Veerman static Boolean VarSYSVMatch(GNode *, Var_Parse_State *,
2982e2caf59SThomas Veerman char *, Boolean, Buffer *, void *);
2992e2caf59SThomas Veerman #endif
3002e2caf59SThomas Veerman static Boolean VarNoMatch(GNode *, Var_Parse_State *,
3012e2caf59SThomas Veerman char *, Boolean, Buffer *, void *);
3022e2caf59SThomas Veerman #ifndef NO_REGEX
3032e2caf59SThomas Veerman static void VarREError(int, regex_t *, const char *);
3042e2caf59SThomas Veerman static Boolean VarRESubstitute(GNode *, Var_Parse_State *,
3052e2caf59SThomas Veerman char *, Boolean, Buffer *, void *);
3062e2caf59SThomas Veerman #endif
3072e2caf59SThomas Veerman static Boolean VarSubstitute(GNode *, Var_Parse_State *,
3082e2caf59SThomas Veerman char *, Boolean, Buffer *, void *);
3092e2caf59SThomas Veerman static Boolean VarLoopExpand(GNode *, Var_Parse_State *,
3102e2caf59SThomas Veerman char *, Boolean, Buffer *, void *);
3112e2caf59SThomas Veerman static char *VarGetPattern(GNode *, Var_Parse_State *,
3122e2caf59SThomas Veerman int, const char **, int, int *, int *,
3132e2caf59SThomas Veerman VarPattern *);
3142e2caf59SThomas Veerman static char *VarQuote(char *);
3152e2caf59SThomas Veerman static char *VarHash(char *);
3162e2caf59SThomas Veerman static char *VarModify(GNode *, Var_Parse_State *,
3172e2caf59SThomas Veerman const char *,
3182e2caf59SThomas Veerman Boolean (*)(GNode *, Var_Parse_State *, char *, Boolean, Buffer *, void *),
3192e2caf59SThomas Veerman void *);
3202e2caf59SThomas Veerman static char *VarOrder(const char *, const char);
3212e2caf59SThomas Veerman static char *VarUniq(const char *);
3222e2caf59SThomas Veerman static int VarWordCompare(const void *, const void *);
3232e2caf59SThomas Veerman static void VarPrintVar(void *);
3242e2caf59SThomas Veerman
3252e2caf59SThomas Veerman #define BROPEN '{'
3262e2caf59SThomas Veerman #define BRCLOSE '}'
3272e2caf59SThomas Veerman #define PROPEN '('
3282e2caf59SThomas Veerman #define PRCLOSE ')'
3292e2caf59SThomas Veerman
3302e2caf59SThomas Veerman /*-
3312e2caf59SThomas Veerman *-----------------------------------------------------------------------
3322e2caf59SThomas Veerman * VarFind --
3332e2caf59SThomas Veerman * Find the given variable in the given context and any other contexts
3342e2caf59SThomas Veerman * indicated.
3352e2caf59SThomas Veerman *
3362e2caf59SThomas Veerman * Input:
3372e2caf59SThomas Veerman * name name to find
3382e2caf59SThomas Veerman * ctxt context in which to find it
3392e2caf59SThomas Veerman * flags FIND_GLOBAL set means to look in the
3402e2caf59SThomas Veerman * VAR_GLOBAL context as well. FIND_CMD set means
3412e2caf59SThomas Veerman * to look in the VAR_CMD context also. FIND_ENV
3422e2caf59SThomas Veerman * set means to look in the environment
3432e2caf59SThomas Veerman *
3442e2caf59SThomas Veerman * Results:
3452e2caf59SThomas Veerman * A pointer to the structure describing the desired variable or
3462e2caf59SThomas Veerman * NULL if the variable does not exist.
3472e2caf59SThomas Veerman *
3482e2caf59SThomas Veerman * Side Effects:
3492e2caf59SThomas Veerman * None
3502e2caf59SThomas Veerman *-----------------------------------------------------------------------
3512e2caf59SThomas Veerman */
3522e2caf59SThomas Veerman static Var *
VarFind(const char * name,GNode * ctxt,int flags)3532e2caf59SThomas Veerman VarFind(const char *name, GNode *ctxt, int flags)
3542e2caf59SThomas Veerman {
3552e2caf59SThomas Veerman Hash_Entry *var;
3562e2caf59SThomas Veerman Var *v;
3572e2caf59SThomas Veerman
3582e2caf59SThomas Veerman /*
3592e2caf59SThomas Veerman * If the variable name begins with a '.', it could very well be one of
3602e2caf59SThomas Veerman * the local ones. We check the name against all the local variables
3612e2caf59SThomas Veerman * and substitute the short version in for 'name' if it matches one of
3622e2caf59SThomas Veerman * them.
3632e2caf59SThomas Veerman */
3642e2caf59SThomas Veerman if (*name == '.' && isupper((unsigned char) name[1]))
3652e2caf59SThomas Veerman switch (name[1]) {
3662e2caf59SThomas Veerman case 'A':
3672e2caf59SThomas Veerman if (!strcmp(name, ".ALLSRC"))
3682e2caf59SThomas Veerman name = ALLSRC;
3692e2caf59SThomas Veerman if (!strcmp(name, ".ARCHIVE"))
3702e2caf59SThomas Veerman name = ARCHIVE;
3712e2caf59SThomas Veerman break;
3722e2caf59SThomas Veerman case 'I':
3732e2caf59SThomas Veerman if (!strcmp(name, ".IMPSRC"))
3742e2caf59SThomas Veerman name = IMPSRC;
3752e2caf59SThomas Veerman break;
3762e2caf59SThomas Veerman case 'M':
3772e2caf59SThomas Veerman if (!strcmp(name, ".MEMBER"))
3782e2caf59SThomas Veerman name = MEMBER;
3792e2caf59SThomas Veerman break;
3802e2caf59SThomas Veerman case 'O':
3812e2caf59SThomas Veerman if (!strcmp(name, ".OODATE"))
3822e2caf59SThomas Veerman name = OODATE;
3832e2caf59SThomas Veerman break;
3842e2caf59SThomas Veerman case 'P':
3852e2caf59SThomas Veerman if (!strcmp(name, ".PREFIX"))
3862e2caf59SThomas Veerman name = PREFIX;
3872e2caf59SThomas Veerman break;
3882e2caf59SThomas Veerman case 'T':
3892e2caf59SThomas Veerman if (!strcmp(name, ".TARGET"))
3902e2caf59SThomas Veerman name = TARGET;
3912e2caf59SThomas Veerman break;
3922e2caf59SThomas Veerman }
3932e2caf59SThomas Veerman #ifdef notyet
3942e2caf59SThomas Veerman /* for compatibility with gmake */
3952e2caf59SThomas Veerman if (name[0] == '^' && name[1] == '\0')
3962e2caf59SThomas Veerman name = ALLSRC;
3972e2caf59SThomas Veerman #endif
3982e2caf59SThomas Veerman
3992e2caf59SThomas Veerman /*
4002e2caf59SThomas Veerman * First look for the variable in the given context. If it's not there,
4012e2caf59SThomas Veerman * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order,
4022e2caf59SThomas Veerman * depending on the FIND_* flags in 'flags'
4032e2caf59SThomas Veerman */
4042e2caf59SThomas Veerman var = Hash_FindEntry(&ctxt->context, name);
4052e2caf59SThomas Veerman
4062e2caf59SThomas Veerman if ((var == NULL) && (flags & FIND_CMD) && (ctxt != VAR_CMD)) {
4072e2caf59SThomas Veerman var = Hash_FindEntry(&VAR_CMD->context, name);
4082e2caf59SThomas Veerman }
4092e2caf59SThomas Veerman if (!checkEnvFirst && (var == NULL) && (flags & FIND_GLOBAL) &&
4102e2caf59SThomas Veerman (ctxt != VAR_GLOBAL))
4112e2caf59SThomas Veerman {
4122e2caf59SThomas Veerman var = Hash_FindEntry(&VAR_GLOBAL->context, name);
41384d9c625SLionel Sambuc if ((var == NULL) && (ctxt != VAR_INTERNAL)) {
41484d9c625SLionel Sambuc /* VAR_INTERNAL is subordinate to VAR_GLOBAL */
41584d9c625SLionel Sambuc var = Hash_FindEntry(&VAR_INTERNAL->context, name);
41684d9c625SLionel Sambuc }
4172e2caf59SThomas Veerman }
4182e2caf59SThomas Veerman if ((var == NULL) && (flags & FIND_ENV)) {
4192e2caf59SThomas Veerman char *env;
4202e2caf59SThomas Veerman
4212e2caf59SThomas Veerman if ((env = getenv(name)) != NULL) {
4222e2caf59SThomas Veerman int len;
4232e2caf59SThomas Veerman
4242e2caf59SThomas Veerman v = bmake_malloc(sizeof(Var));
4252e2caf59SThomas Veerman v->name = bmake_strdup(name);
4262e2caf59SThomas Veerman
4272e2caf59SThomas Veerman len = strlen(env);
4282e2caf59SThomas Veerman
4292e2caf59SThomas Veerman Buf_Init(&v->val, len + 1);
4302e2caf59SThomas Veerman Buf_AddBytes(&v->val, len, env);
4312e2caf59SThomas Veerman
4322e2caf59SThomas Veerman v->flags = VAR_FROM_ENV;
4332e2caf59SThomas Veerman return (v);
4342e2caf59SThomas Veerman } else if (checkEnvFirst && (flags & FIND_GLOBAL) &&
4352e2caf59SThomas Veerman (ctxt != VAR_GLOBAL))
4362e2caf59SThomas Veerman {
4372e2caf59SThomas Veerman var = Hash_FindEntry(&VAR_GLOBAL->context, name);
43884d9c625SLionel Sambuc if ((var == NULL) && (ctxt != VAR_INTERNAL)) {
43984d9c625SLionel Sambuc var = Hash_FindEntry(&VAR_INTERNAL->context, name);
44084d9c625SLionel Sambuc }
4412e2caf59SThomas Veerman if (var == NULL) {
4422e2caf59SThomas Veerman return NULL;
4432e2caf59SThomas Veerman } else {
4442e2caf59SThomas Veerman return ((Var *)Hash_GetValue(var));
4452e2caf59SThomas Veerman }
4462e2caf59SThomas Veerman } else {
4472e2caf59SThomas Veerman return NULL;
4482e2caf59SThomas Veerman }
4492e2caf59SThomas Veerman } else if (var == NULL) {
4502e2caf59SThomas Veerman return NULL;
4512e2caf59SThomas Veerman } else {
4522e2caf59SThomas Veerman return ((Var *)Hash_GetValue(var));
4532e2caf59SThomas Veerman }
4542e2caf59SThomas Veerman }
4552e2caf59SThomas Veerman
4562e2caf59SThomas Veerman /*-
4572e2caf59SThomas Veerman *-----------------------------------------------------------------------
4582e2caf59SThomas Veerman * VarFreeEnv --
4592e2caf59SThomas Veerman * If the variable is an environment variable, free it
4602e2caf59SThomas Veerman *
4612e2caf59SThomas Veerman * Input:
4622e2caf59SThomas Veerman * v the variable
4632e2caf59SThomas Veerman * destroy true if the value buffer should be destroyed.
4642e2caf59SThomas Veerman *
4652e2caf59SThomas Veerman * Results:
4662e2caf59SThomas Veerman * 1 if it is an environment variable 0 ow.
4672e2caf59SThomas Veerman *
4682e2caf59SThomas Veerman * Side Effects:
4692e2caf59SThomas Veerman * The variable is free'ed if it is an environent variable.
4702e2caf59SThomas Veerman *-----------------------------------------------------------------------
4712e2caf59SThomas Veerman */
4722e2caf59SThomas Veerman static Boolean
VarFreeEnv(Var * v,Boolean destroy)4732e2caf59SThomas Veerman VarFreeEnv(Var *v, Boolean destroy)
4742e2caf59SThomas Veerman {
4752e2caf59SThomas Veerman if ((v->flags & VAR_FROM_ENV) == 0)
4762e2caf59SThomas Veerman return FALSE;
4772e2caf59SThomas Veerman free(v->name);
4782e2caf59SThomas Veerman Buf_Destroy(&v->val, destroy);
4792e2caf59SThomas Veerman free(v);
4802e2caf59SThomas Veerman return TRUE;
4812e2caf59SThomas Veerman }
4822e2caf59SThomas Veerman
4832e2caf59SThomas Veerman /*-
4842e2caf59SThomas Veerman *-----------------------------------------------------------------------
4852e2caf59SThomas Veerman * VarAdd --
4862e2caf59SThomas Veerman * Add a new variable of name name and value val to the given context
4872e2caf59SThomas Veerman *
4882e2caf59SThomas Veerman * Input:
4892e2caf59SThomas Veerman * name name of variable to add
4902e2caf59SThomas Veerman * val value to set it to
4912e2caf59SThomas Veerman * ctxt context in which to set it
4922e2caf59SThomas Veerman *
4932e2caf59SThomas Veerman * Results:
4942e2caf59SThomas Veerman * None
4952e2caf59SThomas Veerman *
4962e2caf59SThomas Veerman * Side Effects:
4972e2caf59SThomas Veerman * The new variable is placed at the front of the given context
4982e2caf59SThomas Veerman * The name and val arguments are duplicated so they may
4992e2caf59SThomas Veerman * safely be freed.
5002e2caf59SThomas Veerman *-----------------------------------------------------------------------
5012e2caf59SThomas Veerman */
5022e2caf59SThomas Veerman static void
VarAdd(const char * name,const char * val,GNode * ctxt)5032e2caf59SThomas Veerman VarAdd(const char *name, const char *val, GNode *ctxt)
5042e2caf59SThomas Veerman {
5052e2caf59SThomas Veerman Var *v;
5062e2caf59SThomas Veerman int len;
5072e2caf59SThomas Veerman Hash_Entry *h;
5082e2caf59SThomas Veerman
5092e2caf59SThomas Veerman v = bmake_malloc(sizeof(Var));
5102e2caf59SThomas Veerman
5112e2caf59SThomas Veerman len = val ? strlen(val) : 0;
5122e2caf59SThomas Veerman Buf_Init(&v->val, len+1);
5132e2caf59SThomas Veerman Buf_AddBytes(&v->val, len, val);
5142e2caf59SThomas Veerman
5152e2caf59SThomas Veerman v->flags = 0;
5162e2caf59SThomas Veerman
5172e2caf59SThomas Veerman h = Hash_CreateEntry(&ctxt->context, name, NULL);
5182e2caf59SThomas Veerman Hash_SetValue(h, v);
5192e2caf59SThomas Veerman v->name = h->name;
5202e2caf59SThomas Veerman if (DEBUG(VAR)) {
5212e2caf59SThomas Veerman fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val);
5222e2caf59SThomas Veerman }
5232e2caf59SThomas Veerman }
5242e2caf59SThomas Veerman
5252e2caf59SThomas Veerman /*-
5262e2caf59SThomas Veerman *-----------------------------------------------------------------------
5272e2caf59SThomas Veerman * Var_Delete --
5282e2caf59SThomas Veerman * Remove a variable from a context.
5292e2caf59SThomas Veerman *
5302e2caf59SThomas Veerman * Results:
5312e2caf59SThomas Veerman * None.
5322e2caf59SThomas Veerman *
5332e2caf59SThomas Veerman * Side Effects:
5342e2caf59SThomas Veerman * The Var structure is removed and freed.
5352e2caf59SThomas Veerman *
5362e2caf59SThomas Veerman *-----------------------------------------------------------------------
5372e2caf59SThomas Veerman */
5382e2caf59SThomas Veerman void
Var_Delete(const char * name,GNode * ctxt)5392e2caf59SThomas Veerman Var_Delete(const char *name, GNode *ctxt)
5402e2caf59SThomas Veerman {
5412e2caf59SThomas Veerman Hash_Entry *ln;
54284d9c625SLionel Sambuc char *cp;
5432e2caf59SThomas Veerman
54484d9c625SLionel Sambuc if (strchr(name, '$')) {
54584d9c625SLionel Sambuc cp = Var_Subst(NULL, name, VAR_GLOBAL, 0);
54684d9c625SLionel Sambuc } else {
54784d9c625SLionel Sambuc cp = (char *)name;
54884d9c625SLionel Sambuc }
54984d9c625SLionel Sambuc ln = Hash_FindEntry(&ctxt->context, cp);
5502e2caf59SThomas Veerman if (DEBUG(VAR)) {
5512e2caf59SThomas Veerman fprintf(debug_file, "%s:delete %s%s\n",
55284d9c625SLionel Sambuc ctxt->name, cp, ln ? "" : " (not found)");
55384d9c625SLionel Sambuc }
55484d9c625SLionel Sambuc if (cp != name) {
55584d9c625SLionel Sambuc free(cp);
5562e2caf59SThomas Veerman }
5572e2caf59SThomas Veerman if (ln != NULL) {
5582e2caf59SThomas Veerman Var *v;
5592e2caf59SThomas Veerman
5602e2caf59SThomas Veerman v = (Var *)Hash_GetValue(ln);
5612e2caf59SThomas Veerman if ((v->flags & VAR_EXPORTED)) {
5622e2caf59SThomas Veerman unsetenv(v->name);
5632e2caf59SThomas Veerman }
5642e2caf59SThomas Veerman if (strcmp(MAKE_EXPORTED, v->name) == 0) {
5652e2caf59SThomas Veerman var_exportedVars = VAR_EXPORTED_NONE;
5662e2caf59SThomas Veerman }
5672e2caf59SThomas Veerman if (v->name != ln->name)
5682e2caf59SThomas Veerman free(v->name);
5692e2caf59SThomas Veerman Hash_DeleteEntry(&ctxt->context, ln);
5702e2caf59SThomas Veerman Buf_Destroy(&v->val, TRUE);
5712e2caf59SThomas Veerman free(v);
5722e2caf59SThomas Veerman }
5732e2caf59SThomas Veerman }
5742e2caf59SThomas Veerman
5752e2caf59SThomas Veerman
5762e2caf59SThomas Veerman /*
5772e2caf59SThomas Veerman * Export a var.
5782e2caf59SThomas Veerman * We ignore make internal variables (those which start with '.')
5792e2caf59SThomas Veerman * Also we jump through some hoops to avoid calling setenv
5802e2caf59SThomas Veerman * more than necessary since it can leak.
5812e2caf59SThomas Veerman * We only manipulate flags of vars if 'parent' is set.
5822e2caf59SThomas Veerman */
5832e2caf59SThomas Veerman static int
Var_Export1(const char * name,int parent)5842e2caf59SThomas Veerman Var_Export1(const char *name, int parent)
5852e2caf59SThomas Veerman {
5862e2caf59SThomas Veerman char tmp[BUFSIZ];
5872e2caf59SThomas Veerman Var *v;
5882e2caf59SThomas Veerman char *val = NULL;
5892e2caf59SThomas Veerman int n;
5902e2caf59SThomas Veerman
5912e2caf59SThomas Veerman if (*name == '.')
5922e2caf59SThomas Veerman return 0; /* skip internals */
5932e2caf59SThomas Veerman if (!name[1]) {
5942e2caf59SThomas Veerman /*
5952e2caf59SThomas Veerman * A single char.
5962e2caf59SThomas Veerman * If it is one of the vars that should only appear in
5972e2caf59SThomas Veerman * local context, skip it, else we can get Var_Subst
5982e2caf59SThomas Veerman * into a loop.
5992e2caf59SThomas Veerman */
6002e2caf59SThomas Veerman switch (name[0]) {
6012e2caf59SThomas Veerman case '@':
6022e2caf59SThomas Veerman case '%':
6032e2caf59SThomas Veerman case '*':
6042e2caf59SThomas Veerman case '!':
6052e2caf59SThomas Veerman return 0;
6062e2caf59SThomas Veerman }
6072e2caf59SThomas Veerman }
6082e2caf59SThomas Veerman v = VarFind(name, VAR_GLOBAL, 0);
6092e2caf59SThomas Veerman if (v == NULL) {
6102e2caf59SThomas Veerman return 0;
6112e2caf59SThomas Veerman }
6122e2caf59SThomas Veerman if (!parent &&
6132e2caf59SThomas Veerman (v->flags & (VAR_EXPORTED|VAR_REEXPORT)) == VAR_EXPORTED) {
6142e2caf59SThomas Veerman return 0; /* nothing to do */
6152e2caf59SThomas Veerman }
6162e2caf59SThomas Veerman val = Buf_GetAll(&v->val, NULL);
6172e2caf59SThomas Veerman if (strchr(val, '$')) {
6182e2caf59SThomas Veerman if (parent) {
6192e2caf59SThomas Veerman /*
6202e2caf59SThomas Veerman * Flag this as something we need to re-export.
6212e2caf59SThomas Veerman * No point actually exporting it now though,
6222e2caf59SThomas Veerman * the child can do it at the last minute.
6232e2caf59SThomas Veerman */
6242e2caf59SThomas Veerman v->flags |= (VAR_EXPORTED|VAR_REEXPORT);
6252e2caf59SThomas Veerman return 1;
6262e2caf59SThomas Veerman }
6272e2caf59SThomas Veerman if (v->flags & VAR_IN_USE) {
6282e2caf59SThomas Veerman /*
6292e2caf59SThomas Veerman * We recursed while exporting in a child.
6302e2caf59SThomas Veerman * This isn't going to end well, just skip it.
6312e2caf59SThomas Veerman */
6322e2caf59SThomas Veerman return 0;
6332e2caf59SThomas Veerman }
6342e2caf59SThomas Veerman n = snprintf(tmp, sizeof(tmp), "${%s}", name);
6352e2caf59SThomas Veerman if (n < (int)sizeof(tmp)) {
6362e2caf59SThomas Veerman val = Var_Subst(NULL, tmp, VAR_GLOBAL, 0);
6372e2caf59SThomas Veerman setenv(name, val, 1);
6382e2caf59SThomas Veerman free(val);
6392e2caf59SThomas Veerman }
6402e2caf59SThomas Veerman } else {
6412e2caf59SThomas Veerman if (parent) {
6422e2caf59SThomas Veerman v->flags &= ~VAR_REEXPORT; /* once will do */
6432e2caf59SThomas Veerman }
6442e2caf59SThomas Veerman if (parent || !(v->flags & VAR_EXPORTED)) {
6452e2caf59SThomas Veerman setenv(name, val, 1);
6462e2caf59SThomas Veerman }
6472e2caf59SThomas Veerman }
6482e2caf59SThomas Veerman /*
6492e2caf59SThomas Veerman * This is so Var_Set knows to call Var_Export again...
6502e2caf59SThomas Veerman */
6512e2caf59SThomas Veerman if (parent) {
6522e2caf59SThomas Veerman v->flags |= VAR_EXPORTED;
6532e2caf59SThomas Veerman }
6542e2caf59SThomas Veerman return 1;
6552e2caf59SThomas Veerman }
6562e2caf59SThomas Veerman
6572e2caf59SThomas Veerman /*
6582e2caf59SThomas Veerman * This gets called from our children.
6592e2caf59SThomas Veerman */
6602e2caf59SThomas Veerman void
Var_ExportVars(void)6612e2caf59SThomas Veerman Var_ExportVars(void)
6622e2caf59SThomas Veerman {
6632e2caf59SThomas Veerman char tmp[BUFSIZ];
6642e2caf59SThomas Veerman Hash_Entry *var;
6652e2caf59SThomas Veerman Hash_Search state;
6662e2caf59SThomas Veerman Var *v;
6672e2caf59SThomas Veerman char *val;
6682e2caf59SThomas Veerman int n;
6692e2caf59SThomas Veerman
67084d9c625SLionel Sambuc /*
67184d9c625SLionel Sambuc * Several make's support this sort of mechanism for tracking
67284d9c625SLionel Sambuc * recursion - but each uses a different name.
67384d9c625SLionel Sambuc * We allow the makefiles to update MAKELEVEL and ensure
67484d9c625SLionel Sambuc * children see a correctly incremented value.
67584d9c625SLionel Sambuc */
67684d9c625SLionel Sambuc snprintf(tmp, sizeof(tmp), "%d", makelevel + 1);
67784d9c625SLionel Sambuc setenv(MAKE_LEVEL_ENV, tmp, 1);
67884d9c625SLionel Sambuc
6792e2caf59SThomas Veerman if (VAR_EXPORTED_NONE == var_exportedVars)
6802e2caf59SThomas Veerman return;
6812e2caf59SThomas Veerman
6822e2caf59SThomas Veerman if (VAR_EXPORTED_ALL == var_exportedVars) {
6832e2caf59SThomas Veerman /*
6842e2caf59SThomas Veerman * Ouch! This is crazy...
6852e2caf59SThomas Veerman */
6862e2caf59SThomas Veerman for (var = Hash_EnumFirst(&VAR_GLOBAL->context, &state);
6872e2caf59SThomas Veerman var != NULL;
6882e2caf59SThomas Veerman var = Hash_EnumNext(&state)) {
6892e2caf59SThomas Veerman v = (Var *)Hash_GetValue(var);
6902e2caf59SThomas Veerman Var_Export1(v->name, 0);
6912e2caf59SThomas Veerman }
6922e2caf59SThomas Veerman return;
6932e2caf59SThomas Veerman }
6942e2caf59SThomas Veerman /*
6952e2caf59SThomas Veerman * We have a number of exported vars,
6962e2caf59SThomas Veerman */
6972e2caf59SThomas Veerman n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}");
6982e2caf59SThomas Veerman if (n < (int)sizeof(tmp)) {
6992e2caf59SThomas Veerman char **av;
7002e2caf59SThomas Veerman char *as;
7012e2caf59SThomas Veerman int ac;
7022e2caf59SThomas Veerman int i;
7032e2caf59SThomas Veerman
7042e2caf59SThomas Veerman val = Var_Subst(NULL, tmp, VAR_GLOBAL, 0);
7052e2caf59SThomas Veerman av = brk_string(val, &ac, FALSE, &as);
7062e2caf59SThomas Veerman for (i = 0; i < ac; i++) {
7072e2caf59SThomas Veerman Var_Export1(av[i], 0);
7082e2caf59SThomas Veerman }
7092e2caf59SThomas Veerman free(val);
7102e2caf59SThomas Veerman free(as);
7112e2caf59SThomas Veerman free(av);
7122e2caf59SThomas Veerman }
7132e2caf59SThomas Veerman }
7142e2caf59SThomas Veerman
7152e2caf59SThomas Veerman /*
7162e2caf59SThomas Veerman * This is called when .export is seen or
7172e2caf59SThomas Veerman * .MAKE.EXPORTED is modified.
7182e2caf59SThomas Veerman * It is also called when any exported var is modified.
7192e2caf59SThomas Veerman */
7202e2caf59SThomas Veerman void
Var_Export(char * str,int isExport)7212e2caf59SThomas Veerman Var_Export(char *str, int isExport)
7222e2caf59SThomas Veerman {
7232e2caf59SThomas Veerman char *name;
7242e2caf59SThomas Veerman char *val;
7252e2caf59SThomas Veerman char **av;
7262e2caf59SThomas Veerman char *as;
7272e2caf59SThomas Veerman int track;
7282e2caf59SThomas Veerman int ac;
7292e2caf59SThomas Veerman int i;
7302e2caf59SThomas Veerman
7312e2caf59SThomas Veerman if (isExport && (!str || !str[0])) {
7322e2caf59SThomas Veerman var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */
7332e2caf59SThomas Veerman return;
7342e2caf59SThomas Veerman }
7352e2caf59SThomas Veerman
7362e2caf59SThomas Veerman if (strncmp(str, "-env", 4) == 0) {
7372e2caf59SThomas Veerman track = 0;
7382e2caf59SThomas Veerman str += 4;
7392e2caf59SThomas Veerman } else {
7402e2caf59SThomas Veerman track = VAR_EXPORT_PARENT;
7412e2caf59SThomas Veerman }
7422e2caf59SThomas Veerman val = Var_Subst(NULL, str, VAR_GLOBAL, 0);
7432e2caf59SThomas Veerman av = brk_string(val, &ac, FALSE, &as);
7442e2caf59SThomas Veerman for (i = 0; i < ac; i++) {
7452e2caf59SThomas Veerman name = av[i];
7462e2caf59SThomas Veerman if (!name[1]) {
7472e2caf59SThomas Veerman /*
7482e2caf59SThomas Veerman * A single char.
7492e2caf59SThomas Veerman * If it is one of the vars that should only appear in
7502e2caf59SThomas Veerman * local context, skip it, else we can get Var_Subst
7512e2caf59SThomas Veerman * into a loop.
7522e2caf59SThomas Veerman */
7532e2caf59SThomas Veerman switch (name[0]) {
7542e2caf59SThomas Veerman case '@':
7552e2caf59SThomas Veerman case '%':
7562e2caf59SThomas Veerman case '*':
7572e2caf59SThomas Veerman case '!':
7582e2caf59SThomas Veerman continue;
7592e2caf59SThomas Veerman }
7602e2caf59SThomas Veerman }
7612e2caf59SThomas Veerman if (Var_Export1(name, track)) {
7622e2caf59SThomas Veerman if (VAR_EXPORTED_ALL != var_exportedVars)
7632e2caf59SThomas Veerman var_exportedVars = VAR_EXPORTED_YES;
7642e2caf59SThomas Veerman if (isExport && track) {
7652e2caf59SThomas Veerman Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL);
7662e2caf59SThomas Veerman }
7672e2caf59SThomas Veerman }
7682e2caf59SThomas Veerman }
7692e2caf59SThomas Veerman free(val);
7702e2caf59SThomas Veerman free(as);
7712e2caf59SThomas Veerman free(av);
7722e2caf59SThomas Veerman }
7732e2caf59SThomas Veerman
7742e2caf59SThomas Veerman
7752e2caf59SThomas Veerman /*
7762e2caf59SThomas Veerman * This is called when .unexport[-env] is seen.
7772e2caf59SThomas Veerman */
7782bc7c627SLionel Sambuc extern char **environ;
7792bc7c627SLionel Sambuc
7802e2caf59SThomas Veerman void
Var_UnExport(char * str)7812e2caf59SThomas Veerman Var_UnExport(char *str)
7822e2caf59SThomas Veerman {
7832e2caf59SThomas Veerman char tmp[BUFSIZ];
7842e2caf59SThomas Veerman char *vlist;
7852e2caf59SThomas Veerman char *cp;
7862e2caf59SThomas Veerman Boolean unexport_env;
7872e2caf59SThomas Veerman int n;
7882e2caf59SThomas Veerman
7892e2caf59SThomas Veerman if (!str || !str[0]) {
7902e2caf59SThomas Veerman return; /* assert? */
7912e2caf59SThomas Veerman }
7922e2caf59SThomas Veerman
7932e2caf59SThomas Veerman vlist = NULL;
7942e2caf59SThomas Veerman
7952e2caf59SThomas Veerman str += 8;
7962e2caf59SThomas Veerman unexport_env = (strncmp(str, "-env", 4) == 0);
7972e2caf59SThomas Veerman if (unexport_env) {
7982e2caf59SThomas Veerman char **newenv;
7992e2caf59SThomas Veerman
80084d9c625SLionel Sambuc cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */
8012bc7c627SLionel Sambuc if (environ == savedEnv) {
8022e2caf59SThomas Veerman /* we have been here before! */
8032e2caf59SThomas Veerman newenv = bmake_realloc(environ, 2 * sizeof(char *));
8042e2caf59SThomas Veerman } else {
8052bc7c627SLionel Sambuc if (savedEnv) {
8062bc7c627SLionel Sambuc free(savedEnv);
8072bc7c627SLionel Sambuc savedEnv = NULL;
8082e2caf59SThomas Veerman }
8092e2caf59SThomas Veerman newenv = bmake_malloc(2 * sizeof(char *));
8102e2caf59SThomas Veerman }
8112e2caf59SThomas Veerman if (!newenv)
8122e2caf59SThomas Veerman return;
8132e2caf59SThomas Veerman /* Note: we cannot safely free() the original environ. */
8142bc7c627SLionel Sambuc environ = savedEnv = newenv;
8152e2caf59SThomas Veerman newenv[0] = NULL;
8162e2caf59SThomas Veerman newenv[1] = NULL;
81784d9c625SLionel Sambuc setenv(MAKE_LEVEL_ENV, cp, 1);
8182e2caf59SThomas Veerman } else {
8192e2caf59SThomas Veerman for (; *str != '\n' && isspace((unsigned char) *str); str++)
8202e2caf59SThomas Veerman continue;
8212e2caf59SThomas Veerman if (str[0] && str[0] != '\n') {
8222e2caf59SThomas Veerman vlist = str;
8232e2caf59SThomas Veerman }
8242e2caf59SThomas Veerman }
8252e2caf59SThomas Veerman
8262e2caf59SThomas Veerman if (!vlist) {
8272e2caf59SThomas Veerman /* Using .MAKE.EXPORTED */
8282e2caf59SThomas Veerman n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}");
8292e2caf59SThomas Veerman if (n < (int)sizeof(tmp)) {
8302e2caf59SThomas Veerman vlist = Var_Subst(NULL, tmp, VAR_GLOBAL, 0);
8312e2caf59SThomas Veerman }
8322e2caf59SThomas Veerman }
8332e2caf59SThomas Veerman if (vlist) {
8342e2caf59SThomas Veerman Var *v;
8352e2caf59SThomas Veerman char **av;
8362e2caf59SThomas Veerman char *as;
8372e2caf59SThomas Veerman int ac;
8382e2caf59SThomas Veerman int i;
8392e2caf59SThomas Veerman
8402e2caf59SThomas Veerman av = brk_string(vlist, &ac, FALSE, &as);
8412e2caf59SThomas Veerman for (i = 0; i < ac; i++) {
8422e2caf59SThomas Veerman v = VarFind(av[i], VAR_GLOBAL, 0);
8432e2caf59SThomas Veerman if (!v)
8442e2caf59SThomas Veerman continue;
8452e2caf59SThomas Veerman if (!unexport_env &&
8462e2caf59SThomas Veerman (v->flags & (VAR_EXPORTED|VAR_REEXPORT)) == VAR_EXPORTED) {
8472e2caf59SThomas Veerman unsetenv(v->name);
8482e2caf59SThomas Veerman }
8492e2caf59SThomas Veerman v->flags &= ~(VAR_EXPORTED|VAR_REEXPORT);
8502e2caf59SThomas Veerman /*
8512e2caf59SThomas Veerman * If we are unexporting a list,
8522e2caf59SThomas Veerman * remove each one from .MAKE.EXPORTED.
8532e2caf59SThomas Veerman * If we are removing them all,
8542e2caf59SThomas Veerman * just delete .MAKE.EXPORTED below.
8552e2caf59SThomas Veerman */
8562e2caf59SThomas Veerman if (vlist == str) {
8572e2caf59SThomas Veerman n = snprintf(tmp, sizeof(tmp),
8582e2caf59SThomas Veerman "${" MAKE_EXPORTED ":N%s}", v->name);
8592e2caf59SThomas Veerman if (n < (int)sizeof(tmp)) {
8602e2caf59SThomas Veerman cp = Var_Subst(NULL, tmp, VAR_GLOBAL, 0);
8612e2caf59SThomas Veerman Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL, 0);
8622e2caf59SThomas Veerman free(cp);
8632e2caf59SThomas Veerman }
8642e2caf59SThomas Veerman }
8652e2caf59SThomas Veerman }
8662e2caf59SThomas Veerman free(as);
8672e2caf59SThomas Veerman free(av);
8682e2caf59SThomas Veerman if (vlist != str) {
8692e2caf59SThomas Veerman Var_Delete(MAKE_EXPORTED, VAR_GLOBAL);
8702e2caf59SThomas Veerman free(vlist);
8712e2caf59SThomas Veerman }
8722e2caf59SThomas Veerman }
8732e2caf59SThomas Veerman }
8742e2caf59SThomas Veerman
8752e2caf59SThomas Veerman /*-
8762e2caf59SThomas Veerman *-----------------------------------------------------------------------
8772e2caf59SThomas Veerman * Var_Set --
8782e2caf59SThomas Veerman * Set the variable name to the value val in the given context.
8792e2caf59SThomas Veerman *
8802e2caf59SThomas Veerman * Input:
8812e2caf59SThomas Veerman * name name of variable to set
8822e2caf59SThomas Veerman * val value to give to the variable
8832e2caf59SThomas Veerman * ctxt context in which to set it
8842e2caf59SThomas Veerman *
8852e2caf59SThomas Veerman * Results:
8862e2caf59SThomas Veerman * None.
8872e2caf59SThomas Veerman *
8882e2caf59SThomas Veerman * Side Effects:
8892e2caf59SThomas Veerman * If the variable doesn't yet exist, a new record is created for it.
8902e2caf59SThomas Veerman * Else the old value is freed and the new one stuck in its place
8912e2caf59SThomas Veerman *
8922e2caf59SThomas Veerman * Notes:
8932e2caf59SThomas Veerman * The variable is searched for only in its context before being
8942e2caf59SThomas Veerman * created in that context. I.e. if the context is VAR_GLOBAL,
8952e2caf59SThomas Veerman * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only
8962e2caf59SThomas Veerman * VAR_CMD->context is searched. This is done to avoid the literally
8972e2caf59SThomas Veerman * thousands of unnecessary strcmp's that used to be done to
8982e2caf59SThomas Veerman * set, say, $(@) or $(<).
8992e2caf59SThomas Veerman * If the context is VAR_GLOBAL though, we check if the variable
9002e2caf59SThomas Veerman * was set in VAR_CMD from the command line and skip it if so.
9012e2caf59SThomas Veerman *-----------------------------------------------------------------------
9022e2caf59SThomas Veerman */
9032e2caf59SThomas Veerman void
Var_Set(const char * name,const char * val,GNode * ctxt,int flags)9042e2caf59SThomas Veerman Var_Set(const char *name, const char *val, GNode *ctxt, int flags)
9052e2caf59SThomas Veerman {
9062e2caf59SThomas Veerman Var *v;
9072e2caf59SThomas Veerman char *expanded_name = NULL;
9082e2caf59SThomas Veerman
9092e2caf59SThomas Veerman /*
9102e2caf59SThomas Veerman * We only look for a variable in the given context since anything set
9112e2caf59SThomas Veerman * here will override anything in a lower context, so there's not much
9122e2caf59SThomas Veerman * point in searching them all just to save a bit of memory...
9132e2caf59SThomas Veerman */
9142e2caf59SThomas Veerman if (strchr(name, '$') != NULL) {
9152e2caf59SThomas Veerman expanded_name = Var_Subst(NULL, name, ctxt, 0);
9162e2caf59SThomas Veerman if (expanded_name[0] == 0) {
9172e2caf59SThomas Veerman if (DEBUG(VAR)) {
9182e2caf59SThomas Veerman fprintf(debug_file, "Var_Set(\"%s\", \"%s\", ...) "
9192e2caf59SThomas Veerman "name expands to empty string - ignored\n",
9202e2caf59SThomas Veerman name, val);
9212e2caf59SThomas Veerman }
9222e2caf59SThomas Veerman free(expanded_name);
9232e2caf59SThomas Veerman return;
9242e2caf59SThomas Veerman }
9252e2caf59SThomas Veerman name = expanded_name;
9262e2caf59SThomas Veerman }
9272e2caf59SThomas Veerman if (ctxt == VAR_GLOBAL) {
9282e2caf59SThomas Veerman v = VarFind(name, VAR_CMD, 0);
9292e2caf59SThomas Veerman if (v != NULL) {
9302e2caf59SThomas Veerman if ((v->flags & VAR_FROM_CMD)) {
9312e2caf59SThomas Veerman if (DEBUG(VAR)) {
9322e2caf59SThomas Veerman fprintf(debug_file, "%s:%s = %s ignored!\n", ctxt->name, name, val);
9332e2caf59SThomas Veerman }
9342e2caf59SThomas Veerman goto out;
9352e2caf59SThomas Veerman }
9362e2caf59SThomas Veerman VarFreeEnv(v, TRUE);
9372e2caf59SThomas Veerman }
9382e2caf59SThomas Veerman }
9392e2caf59SThomas Veerman v = VarFind(name, ctxt, 0);
9402e2caf59SThomas Veerman if (v == NULL) {
94184d9c625SLionel Sambuc if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) {
94284d9c625SLionel Sambuc /*
94384d9c625SLionel Sambuc * This var would normally prevent the same name being added
94484d9c625SLionel Sambuc * to VAR_GLOBAL, so delete it from there if needed.
94584d9c625SLionel Sambuc * Otherwise -V name may show the wrong value.
94684d9c625SLionel Sambuc */
94784d9c625SLionel Sambuc Var_Delete(name, VAR_GLOBAL);
94884d9c625SLionel Sambuc }
9492e2caf59SThomas Veerman VarAdd(name, val, ctxt);
9502e2caf59SThomas Veerman } else {
9512e2caf59SThomas Veerman Buf_Empty(&v->val);
9522e2caf59SThomas Veerman Buf_AddBytes(&v->val, strlen(val), val);
9532e2caf59SThomas Veerman
9542e2caf59SThomas Veerman if (DEBUG(VAR)) {
9552e2caf59SThomas Veerman fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val);
9562e2caf59SThomas Veerman }
9572e2caf59SThomas Veerman if ((v->flags & VAR_EXPORTED)) {
9582e2caf59SThomas Veerman Var_Export1(name, VAR_EXPORT_PARENT);
9592e2caf59SThomas Veerman }
9602e2caf59SThomas Veerman }
9612e2caf59SThomas Veerman /*
9622e2caf59SThomas Veerman * Any variables given on the command line are automatically exported
9632e2caf59SThomas Veerman * to the environment (as per POSIX standard)
9642e2caf59SThomas Veerman */
9652e2caf59SThomas Veerman if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) {
9662e2caf59SThomas Veerman if (v == NULL) {
9672e2caf59SThomas Veerman /* we just added it */
9682e2caf59SThomas Veerman v = VarFind(name, ctxt, 0);
9692e2caf59SThomas Veerman }
9702e2caf59SThomas Veerman if (v != NULL)
9712e2caf59SThomas Veerman v->flags |= VAR_FROM_CMD;
9722e2caf59SThomas Veerman /*
9732e2caf59SThomas Veerman * If requested, don't export these in the environment
9742e2caf59SThomas Veerman * individually. We still put them in MAKEOVERRIDES so
9752e2caf59SThomas Veerman * that the command-line settings continue to override
9762e2caf59SThomas Veerman * Makefile settings.
9772e2caf59SThomas Veerman */
9782e2caf59SThomas Veerman if (varNoExportEnv != TRUE)
9792e2caf59SThomas Veerman setenv(name, val, 1);
9802e2caf59SThomas Veerman
9812e2caf59SThomas Veerman Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL);
9822e2caf59SThomas Veerman }
9832e2caf59SThomas Veerman
9842e2caf59SThomas Veerman out:
9852e2caf59SThomas Veerman free(expanded_name);
9862e2caf59SThomas Veerman if (v != NULL)
9872e2caf59SThomas Veerman VarFreeEnv(v, TRUE);
9882e2caf59SThomas Veerman }
9892e2caf59SThomas Veerman
9902e2caf59SThomas Veerman /*-
9912e2caf59SThomas Veerman *-----------------------------------------------------------------------
9922e2caf59SThomas Veerman * Var_Append --
9932e2caf59SThomas Veerman * The variable of the given name has the given value appended to it in
9942e2caf59SThomas Veerman * the given context.
9952e2caf59SThomas Veerman *
9962e2caf59SThomas Veerman * Input:
9972e2caf59SThomas Veerman * name name of variable to modify
9982e2caf59SThomas Veerman * val String to append to it
9992e2caf59SThomas Veerman * ctxt Context in which this should occur
10002e2caf59SThomas Veerman *
10012e2caf59SThomas Veerman * Results:
10022e2caf59SThomas Veerman * None
10032e2caf59SThomas Veerman *
10042e2caf59SThomas Veerman * Side Effects:
10052e2caf59SThomas Veerman * If the variable doesn't exist, it is created. Else the strings
10062e2caf59SThomas Veerman * are concatenated (with a space in between).
10072e2caf59SThomas Veerman *
10082e2caf59SThomas Veerman * Notes:
10092e2caf59SThomas Veerman * Only if the variable is being sought in the global context is the
10102e2caf59SThomas Veerman * environment searched.
10112e2caf59SThomas Veerman * XXX: Knows its calling circumstances in that if called with ctxt
10122e2caf59SThomas Veerman * an actual target, it will only search that context since only
10132e2caf59SThomas Veerman * a local variable could be being appended to. This is actually
10142e2caf59SThomas Veerman * a big win and must be tolerated.
10152e2caf59SThomas Veerman *-----------------------------------------------------------------------
10162e2caf59SThomas Veerman */
10172e2caf59SThomas Veerman void
Var_Append(const char * name,const char * val,GNode * ctxt)10182e2caf59SThomas Veerman Var_Append(const char *name, const char *val, GNode *ctxt)
10192e2caf59SThomas Veerman {
10202e2caf59SThomas Veerman Var *v;
10212e2caf59SThomas Veerman Hash_Entry *h;
10222e2caf59SThomas Veerman char *expanded_name = NULL;
10232e2caf59SThomas Veerman
10242e2caf59SThomas Veerman if (strchr(name, '$') != NULL) {
10252e2caf59SThomas Veerman expanded_name = Var_Subst(NULL, name, ctxt, 0);
10262e2caf59SThomas Veerman if (expanded_name[0] == 0) {
10272e2caf59SThomas Veerman if (DEBUG(VAR)) {
10282e2caf59SThomas Veerman fprintf(debug_file, "Var_Append(\"%s\", \"%s\", ...) "
10292e2caf59SThomas Veerman "name expands to empty string - ignored\n",
10302e2caf59SThomas Veerman name, val);
10312e2caf59SThomas Veerman }
10322e2caf59SThomas Veerman free(expanded_name);
10332e2caf59SThomas Veerman return;
10342e2caf59SThomas Veerman }
10352e2caf59SThomas Veerman name = expanded_name;
10362e2caf59SThomas Veerman }
10372e2caf59SThomas Veerman
10382e2caf59SThomas Veerman v = VarFind(name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0);
10392e2caf59SThomas Veerman
10402e2caf59SThomas Veerman if (v == NULL) {
10412e2caf59SThomas Veerman VarAdd(name, val, ctxt);
10422e2caf59SThomas Veerman } else {
10432e2caf59SThomas Veerman Buf_AddByte(&v->val, ' ');
10442e2caf59SThomas Veerman Buf_AddBytes(&v->val, strlen(val), val);
10452e2caf59SThomas Veerman
10462e2caf59SThomas Veerman if (DEBUG(VAR)) {
10472e2caf59SThomas Veerman fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name,
10482e2caf59SThomas Veerman Buf_GetAll(&v->val, NULL));
10492e2caf59SThomas Veerman }
10502e2caf59SThomas Veerman
10512e2caf59SThomas Veerman if (v->flags & VAR_FROM_ENV) {
10522e2caf59SThomas Veerman /*
10532e2caf59SThomas Veerman * If the original variable came from the environment, we
10542e2caf59SThomas Veerman * have to install it in the global context (we could place
10552e2caf59SThomas Veerman * it in the environment, but then we should provide a way to
10562e2caf59SThomas Veerman * export other variables...)
10572e2caf59SThomas Veerman */
10582e2caf59SThomas Veerman v->flags &= ~VAR_FROM_ENV;
10592e2caf59SThomas Veerman h = Hash_CreateEntry(&ctxt->context, name, NULL);
10602e2caf59SThomas Veerman Hash_SetValue(h, v);
10612e2caf59SThomas Veerman }
10622e2caf59SThomas Veerman }
10632e2caf59SThomas Veerman free(expanded_name);
10642e2caf59SThomas Veerman }
10652e2caf59SThomas Veerman
10662e2caf59SThomas Veerman /*-
10672e2caf59SThomas Veerman *-----------------------------------------------------------------------
10682e2caf59SThomas Veerman * Var_Exists --
10692e2caf59SThomas Veerman * See if the given variable exists.
10702e2caf59SThomas Veerman *
10712e2caf59SThomas Veerman * Input:
10722e2caf59SThomas Veerman * name Variable to find
10732e2caf59SThomas Veerman * ctxt Context in which to start search
10742e2caf59SThomas Veerman *
10752e2caf59SThomas Veerman * Results:
10762e2caf59SThomas Veerman * TRUE if it does, FALSE if it doesn't
10772e2caf59SThomas Veerman *
10782e2caf59SThomas Veerman * Side Effects:
10792e2caf59SThomas Veerman * None.
10802e2caf59SThomas Veerman *
10812e2caf59SThomas Veerman *-----------------------------------------------------------------------
10822e2caf59SThomas Veerman */
10832e2caf59SThomas Veerman Boolean
Var_Exists(const char * name,GNode * ctxt)10842e2caf59SThomas Veerman Var_Exists(const char *name, GNode *ctxt)
10852e2caf59SThomas Veerman {
10862e2caf59SThomas Veerman Var *v;
10872e2caf59SThomas Veerman char *cp;
10882e2caf59SThomas Veerman
10892e2caf59SThomas Veerman if ((cp = strchr(name, '$')) != NULL) {
10902e2caf59SThomas Veerman cp = Var_Subst(NULL, name, ctxt, FALSE);
10912e2caf59SThomas Veerman }
10922e2caf59SThomas Veerman v = VarFind(cp ? cp : name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV);
10932e2caf59SThomas Veerman free(cp);
10942e2caf59SThomas Veerman if (v == NULL) {
10952e2caf59SThomas Veerman return(FALSE);
10962e2caf59SThomas Veerman } else {
10972e2caf59SThomas Veerman (void)VarFreeEnv(v, TRUE);
10982e2caf59SThomas Veerman }
10992e2caf59SThomas Veerman return(TRUE);
11002e2caf59SThomas Veerman }
11012e2caf59SThomas Veerman
11022e2caf59SThomas Veerman /*-
11032e2caf59SThomas Veerman *-----------------------------------------------------------------------
11042e2caf59SThomas Veerman * Var_Value --
11052e2caf59SThomas Veerman * Return the value of the named variable in the given context
11062e2caf59SThomas Veerman *
11072e2caf59SThomas Veerman * Input:
11082e2caf59SThomas Veerman * name name to find
11092e2caf59SThomas Veerman * ctxt context in which to search for it
11102e2caf59SThomas Veerman *
11112e2caf59SThomas Veerman * Results:
11122e2caf59SThomas Veerman * The value if the variable exists, NULL if it doesn't
11132e2caf59SThomas Veerman *
11142e2caf59SThomas Veerman * Side Effects:
11152e2caf59SThomas Veerman * None
11162e2caf59SThomas Veerman *-----------------------------------------------------------------------
11172e2caf59SThomas Veerman */
11182e2caf59SThomas Veerman char *
Var_Value(const char * name,GNode * ctxt,char ** frp)11192e2caf59SThomas Veerman Var_Value(const char *name, GNode *ctxt, char **frp)
11202e2caf59SThomas Veerman {
11212e2caf59SThomas Veerman Var *v;
11222e2caf59SThomas Veerman
11232e2caf59SThomas Veerman v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
11242e2caf59SThomas Veerman *frp = NULL;
11252e2caf59SThomas Veerman if (v != NULL) {
11262e2caf59SThomas Veerman char *p = (Buf_GetAll(&v->val, NULL));
11272e2caf59SThomas Veerman if (VarFreeEnv(v, FALSE))
11282e2caf59SThomas Veerman *frp = p;
11292e2caf59SThomas Veerman return p;
11302e2caf59SThomas Veerman } else {
11312e2caf59SThomas Veerman return NULL;
11322e2caf59SThomas Veerman }
11332e2caf59SThomas Veerman }
11342e2caf59SThomas Veerman
11352e2caf59SThomas Veerman /*-
11362e2caf59SThomas Veerman *-----------------------------------------------------------------------
11372e2caf59SThomas Veerman * VarHead --
11382e2caf59SThomas Veerman * Remove the tail of the given word and place the result in the given
11392e2caf59SThomas Veerman * buffer.
11402e2caf59SThomas Veerman *
11412e2caf59SThomas Veerman * Input:
11422e2caf59SThomas Veerman * word Word to trim
11432e2caf59SThomas Veerman * addSpace True if need to add a space to the buffer
11442e2caf59SThomas Veerman * before sticking in the head
11452e2caf59SThomas Veerman * buf Buffer in which to store it
11462e2caf59SThomas Veerman *
11472e2caf59SThomas Veerman * Results:
11482e2caf59SThomas Veerman * TRUE if characters were added to the buffer (a space needs to be
11492e2caf59SThomas Veerman * added to the buffer before the next word).
11502e2caf59SThomas Veerman *
11512e2caf59SThomas Veerman * Side Effects:
11522e2caf59SThomas Veerman * The trimmed word is added to the buffer.
11532e2caf59SThomas Veerman *
11542e2caf59SThomas Veerman *-----------------------------------------------------------------------
11552e2caf59SThomas Veerman */
11562e2caf59SThomas Veerman static Boolean
VarHead(GNode * ctx MAKE_ATTR_UNUSED,Var_Parse_State * vpstate,char * word,Boolean addSpace,Buffer * buf,void * dummy)11572bc7c627SLionel Sambuc VarHead(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
11582e2caf59SThomas Veerman char *word, Boolean addSpace, Buffer *buf,
11592e2caf59SThomas Veerman void *dummy)
11602e2caf59SThomas Veerman {
11612e2caf59SThomas Veerman char *slash;
11622e2caf59SThomas Veerman
11632e2caf59SThomas Veerman slash = strrchr(word, '/');
11642e2caf59SThomas Veerman if (slash != NULL) {
11652e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
11662e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
11672e2caf59SThomas Veerman }
11682e2caf59SThomas Veerman *slash = '\0';
11692e2caf59SThomas Veerman Buf_AddBytes(buf, strlen(word), word);
11702e2caf59SThomas Veerman *slash = '/';
11712e2caf59SThomas Veerman return (TRUE);
11722e2caf59SThomas Veerman } else {
11732e2caf59SThomas Veerman /*
11742e2caf59SThomas Veerman * If no directory part, give . (q.v. the POSIX standard)
11752e2caf59SThomas Veerman */
11762e2caf59SThomas Veerman if (addSpace && vpstate->varSpace)
11772e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
11782e2caf59SThomas Veerman Buf_AddByte(buf, '.');
11792e2caf59SThomas Veerman }
11802e2caf59SThomas Veerman return(dummy ? TRUE : TRUE);
11812e2caf59SThomas Veerman }
11822e2caf59SThomas Veerman
11832e2caf59SThomas Veerman /*-
11842e2caf59SThomas Veerman *-----------------------------------------------------------------------
11852e2caf59SThomas Veerman * VarTail --
11862e2caf59SThomas Veerman * Remove the head of the given word and place the result in the given
11872e2caf59SThomas Veerman * buffer.
11882e2caf59SThomas Veerman *
11892e2caf59SThomas Veerman * Input:
11902e2caf59SThomas Veerman * word Word to trim
11912e2caf59SThomas Veerman * addSpace True if need to add a space to the buffer
11922e2caf59SThomas Veerman * before adding the tail
11932e2caf59SThomas Veerman * buf Buffer in which to store it
11942e2caf59SThomas Veerman *
11952e2caf59SThomas Veerman * Results:
11962e2caf59SThomas Veerman * TRUE if characters were added to the buffer (a space needs to be
11972e2caf59SThomas Veerman * added to the buffer before the next word).
11982e2caf59SThomas Veerman *
11992e2caf59SThomas Veerman * Side Effects:
12002e2caf59SThomas Veerman * The trimmed word is added to the buffer.
12012e2caf59SThomas Veerman *
12022e2caf59SThomas Veerman *-----------------------------------------------------------------------
12032e2caf59SThomas Veerman */
12042e2caf59SThomas Veerman static Boolean
VarTail(GNode * ctx MAKE_ATTR_UNUSED,Var_Parse_State * vpstate,char * word,Boolean addSpace,Buffer * buf,void * dummy)12052bc7c627SLionel Sambuc VarTail(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
12062e2caf59SThomas Veerman char *word, Boolean addSpace, Buffer *buf,
12072e2caf59SThomas Veerman void *dummy)
12082e2caf59SThomas Veerman {
12092e2caf59SThomas Veerman char *slash;
12102e2caf59SThomas Veerman
12112e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
12122e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
12132e2caf59SThomas Veerman }
12142e2caf59SThomas Veerman
12152e2caf59SThomas Veerman slash = strrchr(word, '/');
12162e2caf59SThomas Veerman if (slash != NULL) {
12172e2caf59SThomas Veerman *slash++ = '\0';
12182e2caf59SThomas Veerman Buf_AddBytes(buf, strlen(slash), slash);
12192e2caf59SThomas Veerman slash[-1] = '/';
12202e2caf59SThomas Veerman } else {
12212e2caf59SThomas Veerman Buf_AddBytes(buf, strlen(word), word);
12222e2caf59SThomas Veerman }
12232e2caf59SThomas Veerman return (dummy ? TRUE : TRUE);
12242e2caf59SThomas Veerman }
12252e2caf59SThomas Veerman
12262e2caf59SThomas Veerman /*-
12272e2caf59SThomas Veerman *-----------------------------------------------------------------------
12282e2caf59SThomas Veerman * VarSuffix --
12292e2caf59SThomas Veerman * Place the suffix of the given word in the given buffer.
12302e2caf59SThomas Veerman *
12312e2caf59SThomas Veerman * Input:
12322e2caf59SThomas Veerman * word Word to trim
12332e2caf59SThomas Veerman * addSpace TRUE if need to add a space before placing the
12342e2caf59SThomas Veerman * suffix in the buffer
12352e2caf59SThomas Veerman * buf Buffer in which to store it
12362e2caf59SThomas Veerman *
12372e2caf59SThomas Veerman * Results:
12382e2caf59SThomas Veerman * TRUE if characters were added to the buffer (a space needs to be
12392e2caf59SThomas Veerman * added to the buffer before the next word).
12402e2caf59SThomas Veerman *
12412e2caf59SThomas Veerman * Side Effects:
12422e2caf59SThomas Veerman * The suffix from the word is placed in the buffer.
12432e2caf59SThomas Veerman *
12442e2caf59SThomas Veerman *-----------------------------------------------------------------------
12452e2caf59SThomas Veerman */
12462e2caf59SThomas Veerman static Boolean
VarSuffix(GNode * ctx MAKE_ATTR_UNUSED,Var_Parse_State * vpstate,char * word,Boolean addSpace,Buffer * buf,void * dummy)12472bc7c627SLionel Sambuc VarSuffix(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
12482e2caf59SThomas Veerman char *word, Boolean addSpace, Buffer *buf,
12492e2caf59SThomas Veerman void *dummy)
12502e2caf59SThomas Veerman {
12512e2caf59SThomas Veerman char *dot;
12522e2caf59SThomas Veerman
12532e2caf59SThomas Veerman dot = strrchr(word, '.');
12542e2caf59SThomas Veerman if (dot != NULL) {
12552e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
12562e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
12572e2caf59SThomas Veerman }
12582e2caf59SThomas Veerman *dot++ = '\0';
12592e2caf59SThomas Veerman Buf_AddBytes(buf, strlen(dot), dot);
12602e2caf59SThomas Veerman dot[-1] = '.';
12612e2caf59SThomas Veerman addSpace = TRUE;
12622e2caf59SThomas Veerman }
12632e2caf59SThomas Veerman return (dummy ? addSpace : addSpace);
12642e2caf59SThomas Veerman }
12652e2caf59SThomas Veerman
12662e2caf59SThomas Veerman /*-
12672e2caf59SThomas Veerman *-----------------------------------------------------------------------
12682e2caf59SThomas Veerman * VarRoot --
12692e2caf59SThomas Veerman * Remove the suffix of the given word and place the result in the
12702e2caf59SThomas Veerman * buffer.
12712e2caf59SThomas Veerman *
12722e2caf59SThomas Veerman * Input:
12732e2caf59SThomas Veerman * word Word to trim
12742e2caf59SThomas Veerman * addSpace TRUE if need to add a space to the buffer
12752e2caf59SThomas Veerman * before placing the root in it
12762e2caf59SThomas Veerman * buf Buffer in which to store it
12772e2caf59SThomas Veerman *
12782e2caf59SThomas Veerman * Results:
12792e2caf59SThomas Veerman * TRUE if characters were added to the buffer (a space needs to be
12802e2caf59SThomas Veerman * added to the buffer before the next word).
12812e2caf59SThomas Veerman *
12822e2caf59SThomas Veerman * Side Effects:
12832e2caf59SThomas Veerman * The trimmed word is added to the buffer.
12842e2caf59SThomas Veerman *
12852e2caf59SThomas Veerman *-----------------------------------------------------------------------
12862e2caf59SThomas Veerman */
12872e2caf59SThomas Veerman static Boolean
VarRoot(GNode * ctx MAKE_ATTR_UNUSED,Var_Parse_State * vpstate,char * word,Boolean addSpace,Buffer * buf,void * dummy)12882bc7c627SLionel Sambuc VarRoot(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
12892e2caf59SThomas Veerman char *word, Boolean addSpace, Buffer *buf,
12902e2caf59SThomas Veerman void *dummy)
12912e2caf59SThomas Veerman {
12922e2caf59SThomas Veerman char *dot;
12932e2caf59SThomas Veerman
12942e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
12952e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
12962e2caf59SThomas Veerman }
12972e2caf59SThomas Veerman
12982e2caf59SThomas Veerman dot = strrchr(word, '.');
12992e2caf59SThomas Veerman if (dot != NULL) {
13002e2caf59SThomas Veerman *dot = '\0';
13012e2caf59SThomas Veerman Buf_AddBytes(buf, strlen(word), word);
13022e2caf59SThomas Veerman *dot = '.';
13032e2caf59SThomas Veerman } else {
13042e2caf59SThomas Veerman Buf_AddBytes(buf, strlen(word), word);
13052e2caf59SThomas Veerman }
13062e2caf59SThomas Veerman return (dummy ? TRUE : TRUE);
13072e2caf59SThomas Veerman }
13082e2caf59SThomas Veerman
13092e2caf59SThomas Veerman /*-
13102e2caf59SThomas Veerman *-----------------------------------------------------------------------
13112e2caf59SThomas Veerman * VarMatch --
13122e2caf59SThomas Veerman * Place the word in the buffer if it matches the given pattern.
13132e2caf59SThomas Veerman * Callback function for VarModify to implement the :M modifier.
13142e2caf59SThomas Veerman *
13152e2caf59SThomas Veerman * Input:
13162e2caf59SThomas Veerman * word Word to examine
13172e2caf59SThomas Veerman * addSpace TRUE if need to add a space to the buffer
13182e2caf59SThomas Veerman * before adding the word, if it matches
13192e2caf59SThomas Veerman * buf Buffer in which to store it
13202e2caf59SThomas Veerman * pattern Pattern the word must match
13212e2caf59SThomas Veerman *
13222e2caf59SThomas Veerman * Results:
13232e2caf59SThomas Veerman * TRUE if a space should be placed in the buffer before the next
13242e2caf59SThomas Veerman * word.
13252e2caf59SThomas Veerman *
13262e2caf59SThomas Veerman * Side Effects:
13272e2caf59SThomas Veerman * The word may be copied to the buffer.
13282e2caf59SThomas Veerman *
13292e2caf59SThomas Veerman *-----------------------------------------------------------------------
13302e2caf59SThomas Veerman */
13312e2caf59SThomas Veerman static Boolean
VarMatch(GNode * ctx MAKE_ATTR_UNUSED,Var_Parse_State * vpstate,char * word,Boolean addSpace,Buffer * buf,void * pattern)13322bc7c627SLionel Sambuc VarMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
13332e2caf59SThomas Veerman char *word, Boolean addSpace, Buffer *buf,
13342e2caf59SThomas Veerman void *pattern)
13352e2caf59SThomas Veerman {
13362e2caf59SThomas Veerman if (DEBUG(VAR))
13372e2caf59SThomas Veerman fprintf(debug_file, "VarMatch [%s] [%s]\n", word, (char *)pattern);
13382e2caf59SThomas Veerman if (Str_Match(word, (char *)pattern)) {
13392e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
13402e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
13412e2caf59SThomas Veerman }
13422e2caf59SThomas Veerman addSpace = TRUE;
13432e2caf59SThomas Veerman Buf_AddBytes(buf, strlen(word), word);
13442e2caf59SThomas Veerman }
13452e2caf59SThomas Veerman return(addSpace);
13462e2caf59SThomas Veerman }
13472e2caf59SThomas Veerman
13482e2caf59SThomas Veerman #ifdef SYSVVARSUB
13492e2caf59SThomas Veerman /*-
13502e2caf59SThomas Veerman *-----------------------------------------------------------------------
13512e2caf59SThomas Veerman * VarSYSVMatch --
13522e2caf59SThomas Veerman * Place the word in the buffer if it matches the given pattern.
13532e2caf59SThomas Veerman * Callback function for VarModify to implement the System V %
13542e2caf59SThomas Veerman * modifiers.
13552e2caf59SThomas Veerman *
13562e2caf59SThomas Veerman * Input:
13572e2caf59SThomas Veerman * word Word to examine
13582e2caf59SThomas Veerman * addSpace TRUE if need to add a space to the buffer
13592e2caf59SThomas Veerman * before adding the word, if it matches
13602e2caf59SThomas Veerman * buf Buffer in which to store it
13612e2caf59SThomas Veerman * patp Pattern the word must match
13622e2caf59SThomas Veerman *
13632e2caf59SThomas Veerman * Results:
13642e2caf59SThomas Veerman * TRUE if a space should be placed in the buffer before the next
13652e2caf59SThomas Veerman * word.
13662e2caf59SThomas Veerman *
13672e2caf59SThomas Veerman * Side Effects:
13682e2caf59SThomas Veerman * The word may be copied to the buffer.
13692e2caf59SThomas Veerman *
13702e2caf59SThomas Veerman *-----------------------------------------------------------------------
13712e2caf59SThomas Veerman */
13722e2caf59SThomas Veerman static Boolean
VarSYSVMatch(GNode * ctx,Var_Parse_State * vpstate,char * word,Boolean addSpace,Buffer * buf,void * patp)13732e2caf59SThomas Veerman VarSYSVMatch(GNode *ctx, Var_Parse_State *vpstate,
13742e2caf59SThomas Veerman char *word, Boolean addSpace, Buffer *buf,
13752e2caf59SThomas Veerman void *patp)
13762e2caf59SThomas Veerman {
13772e2caf59SThomas Veerman int len;
13782e2caf59SThomas Veerman char *ptr;
13792e2caf59SThomas Veerman VarPattern *pat = (VarPattern *)patp;
13802e2caf59SThomas Veerman char *varexp;
13812e2caf59SThomas Veerman
13822e2caf59SThomas Veerman if (addSpace && vpstate->varSpace)
13832e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
13842e2caf59SThomas Veerman
13852e2caf59SThomas Veerman addSpace = TRUE;
13862e2caf59SThomas Veerman
13872e2caf59SThomas Veerman if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL) {
13882e2caf59SThomas Veerman varexp = Var_Subst(NULL, pat->rhs, ctx, 0);
13892e2caf59SThomas Veerman Str_SYSVSubst(buf, varexp, ptr, len);
13902e2caf59SThomas Veerman free(varexp);
13912e2caf59SThomas Veerman } else {
13922e2caf59SThomas Veerman Buf_AddBytes(buf, strlen(word), word);
13932e2caf59SThomas Veerman }
13942e2caf59SThomas Veerman
13952e2caf59SThomas Veerman return(addSpace);
13962e2caf59SThomas Veerman }
13972e2caf59SThomas Veerman #endif
13982e2caf59SThomas Veerman
13992e2caf59SThomas Veerman
14002e2caf59SThomas Veerman /*-
14012e2caf59SThomas Veerman *-----------------------------------------------------------------------
14022e2caf59SThomas Veerman * VarNoMatch --
14032e2caf59SThomas Veerman * Place the word in the buffer if it doesn't match the given pattern.
14042e2caf59SThomas Veerman * Callback function for VarModify to implement the :N modifier.
14052e2caf59SThomas Veerman *
14062e2caf59SThomas Veerman * Input:
14072e2caf59SThomas Veerman * word Word to examine
14082e2caf59SThomas Veerman * addSpace TRUE if need to add a space to the buffer
14092e2caf59SThomas Veerman * before adding the word, if it matches
14102e2caf59SThomas Veerman * buf Buffer in which to store it
14112e2caf59SThomas Veerman * pattern Pattern the word must match
14122e2caf59SThomas Veerman *
14132e2caf59SThomas Veerman * Results:
14142e2caf59SThomas Veerman * TRUE if a space should be placed in the buffer before the next
14152e2caf59SThomas Veerman * word.
14162e2caf59SThomas Veerman *
14172e2caf59SThomas Veerman * Side Effects:
14182e2caf59SThomas Veerman * The word may be copied to the buffer.
14192e2caf59SThomas Veerman *
14202e2caf59SThomas Veerman *-----------------------------------------------------------------------
14212e2caf59SThomas Veerman */
14222e2caf59SThomas Veerman static Boolean
VarNoMatch(GNode * ctx MAKE_ATTR_UNUSED,Var_Parse_State * vpstate,char * word,Boolean addSpace,Buffer * buf,void * pattern)14232bc7c627SLionel Sambuc VarNoMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
14242e2caf59SThomas Veerman char *word, Boolean addSpace, Buffer *buf,
14252e2caf59SThomas Veerman void *pattern)
14262e2caf59SThomas Veerman {
14272e2caf59SThomas Veerman if (!Str_Match(word, (char *)pattern)) {
14282e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
14292e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
14302e2caf59SThomas Veerman }
14312e2caf59SThomas Veerman addSpace = TRUE;
14322e2caf59SThomas Veerman Buf_AddBytes(buf, strlen(word), word);
14332e2caf59SThomas Veerman }
14342e2caf59SThomas Veerman return(addSpace);
14352e2caf59SThomas Veerman }
14362e2caf59SThomas Veerman
14372e2caf59SThomas Veerman
14382e2caf59SThomas Veerman /*-
14392e2caf59SThomas Veerman *-----------------------------------------------------------------------
14402e2caf59SThomas Veerman * VarSubstitute --
14412e2caf59SThomas Veerman * Perform a string-substitution on the given word, placing the
14422e2caf59SThomas Veerman * result in the passed buffer.
14432e2caf59SThomas Veerman *
14442e2caf59SThomas Veerman * Input:
14452e2caf59SThomas Veerman * word Word to modify
14462e2caf59SThomas Veerman * addSpace True if space should be added before
14472e2caf59SThomas Veerman * other characters
14482e2caf59SThomas Veerman * buf Buffer for result
14492e2caf59SThomas Veerman * patternp Pattern for substitution
14502e2caf59SThomas Veerman *
14512e2caf59SThomas Veerman * Results:
14522e2caf59SThomas Veerman * TRUE if a space is needed before more characters are added.
14532e2caf59SThomas Veerman *
14542e2caf59SThomas Veerman * Side Effects:
14552e2caf59SThomas Veerman * None.
14562e2caf59SThomas Veerman *
14572e2caf59SThomas Veerman *-----------------------------------------------------------------------
14582e2caf59SThomas Veerman */
14592e2caf59SThomas Veerman static Boolean
VarSubstitute(GNode * ctx MAKE_ATTR_UNUSED,Var_Parse_State * vpstate,char * word,Boolean addSpace,Buffer * buf,void * patternp)14602bc7c627SLionel Sambuc VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
14612e2caf59SThomas Veerman char *word, Boolean addSpace, Buffer *buf,
14622e2caf59SThomas Veerman void *patternp)
14632e2caf59SThomas Veerman {
14642e2caf59SThomas Veerman int wordLen; /* Length of word */
14652e2caf59SThomas Veerman char *cp; /* General pointer */
14662e2caf59SThomas Veerman VarPattern *pattern = (VarPattern *)patternp;
14672e2caf59SThomas Veerman
14682e2caf59SThomas Veerman wordLen = strlen(word);
14692e2caf59SThomas Veerman if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) !=
14702e2caf59SThomas Veerman (VAR_SUB_ONE|VAR_SUB_MATCHED)) {
14712e2caf59SThomas Veerman /*
14722e2caf59SThomas Veerman * Still substituting -- break it down into simple anchored cases
14732e2caf59SThomas Veerman * and if none of them fits, perform the general substitution case.
14742e2caf59SThomas Veerman */
14752e2caf59SThomas Veerman if ((pattern->flags & VAR_MATCH_START) &&
14762e2caf59SThomas Veerman (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) {
14772e2caf59SThomas Veerman /*
14782e2caf59SThomas Veerman * Anchored at start and beginning of word matches pattern
14792e2caf59SThomas Veerman */
14802e2caf59SThomas Veerman if ((pattern->flags & VAR_MATCH_END) &&
14812e2caf59SThomas Veerman (wordLen == pattern->leftLen)) {
14822e2caf59SThomas Veerman /*
14832e2caf59SThomas Veerman * Also anchored at end and matches to the end (word
14842e2caf59SThomas Veerman * is same length as pattern) add space and rhs only
14852e2caf59SThomas Veerman * if rhs is non-null.
14862e2caf59SThomas Veerman */
14872e2caf59SThomas Veerman if (pattern->rightLen != 0) {
14882e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
14892e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
14902e2caf59SThomas Veerman }
14912e2caf59SThomas Veerman addSpace = TRUE;
14922e2caf59SThomas Veerman Buf_AddBytes(buf, pattern->rightLen, pattern->rhs);
14932e2caf59SThomas Veerman }
14942e2caf59SThomas Veerman pattern->flags |= VAR_SUB_MATCHED;
14952e2caf59SThomas Veerman } else if (pattern->flags & VAR_MATCH_END) {
14962e2caf59SThomas Veerman /*
14972e2caf59SThomas Veerman * Doesn't match to end -- copy word wholesale
14982e2caf59SThomas Veerman */
14992e2caf59SThomas Veerman goto nosub;
15002e2caf59SThomas Veerman } else {
15012e2caf59SThomas Veerman /*
15022e2caf59SThomas Veerman * Matches at start but need to copy in trailing characters
15032e2caf59SThomas Veerman */
15042e2caf59SThomas Veerman if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
15052e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
15062e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
15072e2caf59SThomas Veerman }
15082e2caf59SThomas Veerman addSpace = TRUE;
15092e2caf59SThomas Veerman }
15102e2caf59SThomas Veerman Buf_AddBytes(buf, pattern->rightLen, pattern->rhs);
15112e2caf59SThomas Veerman Buf_AddBytes(buf, wordLen - pattern->leftLen,
15122e2caf59SThomas Veerman (word + pattern->leftLen));
15132e2caf59SThomas Veerman pattern->flags |= VAR_SUB_MATCHED;
15142e2caf59SThomas Veerman }
15152e2caf59SThomas Veerman } else if (pattern->flags & VAR_MATCH_START) {
15162e2caf59SThomas Veerman /*
15172e2caf59SThomas Veerman * Had to match at start of word and didn't -- copy whole word.
15182e2caf59SThomas Veerman */
15192e2caf59SThomas Veerman goto nosub;
15202e2caf59SThomas Veerman } else if (pattern->flags & VAR_MATCH_END) {
15212e2caf59SThomas Veerman /*
15222e2caf59SThomas Veerman * Anchored at end, Find only place match could occur (leftLen
15232e2caf59SThomas Veerman * characters from the end of the word) and see if it does. Note
15242e2caf59SThomas Veerman * that because the $ will be left at the end of the lhs, we have
15252e2caf59SThomas Veerman * to use strncmp.
15262e2caf59SThomas Veerman */
15272e2caf59SThomas Veerman cp = word + (wordLen - pattern->leftLen);
15282e2caf59SThomas Veerman if ((cp >= word) &&
15292e2caf59SThomas Veerman (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) {
15302e2caf59SThomas Veerman /*
15312e2caf59SThomas Veerman * Match found. If we will place characters in the buffer,
15322e2caf59SThomas Veerman * add a space before hand as indicated by addSpace, then
15332e2caf59SThomas Veerman * stuff in the initial, unmatched part of the word followed
15342e2caf59SThomas Veerman * by the right-hand-side.
15352e2caf59SThomas Veerman */
15362e2caf59SThomas Veerman if (((cp - word) + pattern->rightLen) != 0) {
15372e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
15382e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
15392e2caf59SThomas Veerman }
15402e2caf59SThomas Veerman addSpace = TRUE;
15412e2caf59SThomas Veerman }
15422e2caf59SThomas Veerman Buf_AddBytes(buf, cp - word, word);
15432e2caf59SThomas Veerman Buf_AddBytes(buf, pattern->rightLen, pattern->rhs);
15442e2caf59SThomas Veerman pattern->flags |= VAR_SUB_MATCHED;
15452e2caf59SThomas Veerman } else {
15462e2caf59SThomas Veerman /*
15472e2caf59SThomas Veerman * Had to match at end and didn't. Copy entire word.
15482e2caf59SThomas Veerman */
15492e2caf59SThomas Veerman goto nosub;
15502e2caf59SThomas Veerman }
15512e2caf59SThomas Veerman } else {
15522e2caf59SThomas Veerman /*
15532e2caf59SThomas Veerman * Pattern is unanchored: search for the pattern in the word using
15542e2caf59SThomas Veerman * String_FindSubstring, copying unmatched portions and the
15552e2caf59SThomas Veerman * right-hand-side for each match found, handling non-global
15562e2caf59SThomas Veerman * substitutions correctly, etc. When the loop is done, any
15572e2caf59SThomas Veerman * remaining part of the word (word and wordLen are adjusted
15582e2caf59SThomas Veerman * accordingly through the loop) is copied straight into the
15592e2caf59SThomas Veerman * buffer.
15602e2caf59SThomas Veerman * addSpace is set FALSE as soon as a space is added to the
15612e2caf59SThomas Veerman * buffer.
15622e2caf59SThomas Veerman */
15632e2caf59SThomas Veerman Boolean done;
15642e2caf59SThomas Veerman int origSize;
15652e2caf59SThomas Veerman
15662e2caf59SThomas Veerman done = FALSE;
15672e2caf59SThomas Veerman origSize = Buf_Size(buf);
15682e2caf59SThomas Veerman while (!done) {
15692e2caf59SThomas Veerman cp = Str_FindSubstring(word, pattern->lhs);
15702e2caf59SThomas Veerman if (cp != NULL) {
15712e2caf59SThomas Veerman if (addSpace && (((cp - word) + pattern->rightLen) != 0)){
15722e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
15732e2caf59SThomas Veerman addSpace = FALSE;
15742e2caf59SThomas Veerman }
15752e2caf59SThomas Veerman Buf_AddBytes(buf, cp-word, word);
15762e2caf59SThomas Veerman Buf_AddBytes(buf, pattern->rightLen, pattern->rhs);
15772e2caf59SThomas Veerman wordLen -= (cp - word) + pattern->leftLen;
15782e2caf59SThomas Veerman word = cp + pattern->leftLen;
15792e2caf59SThomas Veerman if (wordLen == 0) {
15802e2caf59SThomas Veerman done = TRUE;
15812e2caf59SThomas Veerman }
15822e2caf59SThomas Veerman if ((pattern->flags & VAR_SUB_GLOBAL) == 0) {
15832e2caf59SThomas Veerman done = TRUE;
15842e2caf59SThomas Veerman }
15852e2caf59SThomas Veerman pattern->flags |= VAR_SUB_MATCHED;
15862e2caf59SThomas Veerman } else {
15872e2caf59SThomas Veerman done = TRUE;
15882e2caf59SThomas Veerman }
15892e2caf59SThomas Veerman }
15902e2caf59SThomas Veerman if (wordLen != 0) {
15912e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
15922e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
15932e2caf59SThomas Veerman }
15942e2caf59SThomas Veerman Buf_AddBytes(buf, wordLen, word);
15952e2caf59SThomas Veerman }
15962e2caf59SThomas Veerman /*
15972e2caf59SThomas Veerman * If added characters to the buffer, need to add a space
15982e2caf59SThomas Veerman * before we add any more. If we didn't add any, just return
15992e2caf59SThomas Veerman * the previous value of addSpace.
16002e2caf59SThomas Veerman */
16012e2caf59SThomas Veerman return ((Buf_Size(buf) != origSize) || addSpace);
16022e2caf59SThomas Veerman }
16032e2caf59SThomas Veerman return (addSpace);
16042e2caf59SThomas Veerman }
16052e2caf59SThomas Veerman nosub:
16062e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
16072e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
16082e2caf59SThomas Veerman }
16092e2caf59SThomas Veerman Buf_AddBytes(buf, wordLen, word);
16102e2caf59SThomas Veerman return(TRUE);
16112e2caf59SThomas Veerman }
16122e2caf59SThomas Veerman
16132e2caf59SThomas Veerman #ifndef NO_REGEX
16142e2caf59SThomas Veerman /*-
16152e2caf59SThomas Veerman *-----------------------------------------------------------------------
16162e2caf59SThomas Veerman * VarREError --
16172e2caf59SThomas Veerman * Print the error caused by a regcomp or regexec call.
16182e2caf59SThomas Veerman *
16192e2caf59SThomas Veerman * Results:
16202e2caf59SThomas Veerman * None.
16212e2caf59SThomas Veerman *
16222e2caf59SThomas Veerman * Side Effects:
16232e2caf59SThomas Veerman * An error gets printed.
16242e2caf59SThomas Veerman *
16252e2caf59SThomas Veerman *-----------------------------------------------------------------------
16262e2caf59SThomas Veerman */
16272e2caf59SThomas Veerman static void
VarREError(int errnum,regex_t * pat,const char * str)16282e2caf59SThomas Veerman VarREError(int errnum, regex_t *pat, const char *str)
16292e2caf59SThomas Veerman {
16302e2caf59SThomas Veerman char *errbuf;
16312e2caf59SThomas Veerman int errlen;
16322e2caf59SThomas Veerman
16332e2caf59SThomas Veerman errlen = regerror(errnum, pat, 0, 0);
16342e2caf59SThomas Veerman errbuf = bmake_malloc(errlen);
16352e2caf59SThomas Veerman regerror(errnum, pat, errbuf, errlen);
16362e2caf59SThomas Veerman Error("%s: %s", str, errbuf);
16372e2caf59SThomas Veerman free(errbuf);
16382e2caf59SThomas Veerman }
16392e2caf59SThomas Veerman
16402e2caf59SThomas Veerman
16412e2caf59SThomas Veerman /*-
16422e2caf59SThomas Veerman *-----------------------------------------------------------------------
16432e2caf59SThomas Veerman * VarRESubstitute --
16442e2caf59SThomas Veerman * Perform a regex substitution on the given word, placing the
16452e2caf59SThomas Veerman * result in the passed buffer.
16462e2caf59SThomas Veerman *
16472e2caf59SThomas Veerman * Results:
16482e2caf59SThomas Veerman * TRUE if a space is needed before more characters are added.
16492e2caf59SThomas Veerman *
16502e2caf59SThomas Veerman * Side Effects:
16512e2caf59SThomas Veerman * None.
16522e2caf59SThomas Veerman *
16532e2caf59SThomas Veerman *-----------------------------------------------------------------------
16542e2caf59SThomas Veerman */
16552e2caf59SThomas Veerman static Boolean
VarRESubstitute(GNode * ctx MAKE_ATTR_UNUSED,Var_Parse_State * vpstate MAKE_ATTR_UNUSED,char * word,Boolean addSpace,Buffer * buf,void * patternp)16562bc7c627SLionel Sambuc VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED,
16572bc7c627SLionel Sambuc Var_Parse_State *vpstate MAKE_ATTR_UNUSED,
16582e2caf59SThomas Veerman char *word, Boolean addSpace, Buffer *buf,
16592e2caf59SThomas Veerman void *patternp)
16602e2caf59SThomas Veerman {
16612e2caf59SThomas Veerman VarREPattern *pat;
16622e2caf59SThomas Veerman int xrv;
16632e2caf59SThomas Veerman char *wp;
16642e2caf59SThomas Veerman char *rp;
16652e2caf59SThomas Veerman int added;
16662e2caf59SThomas Veerman int flags = 0;
16672e2caf59SThomas Veerman
16682e2caf59SThomas Veerman #define MAYBE_ADD_SPACE() \
16692e2caf59SThomas Veerman if (addSpace && !added) \
16702e2caf59SThomas Veerman Buf_AddByte(buf, ' '); \
16712e2caf59SThomas Veerman added = 1
16722e2caf59SThomas Veerman
16732e2caf59SThomas Veerman added = 0;
16742e2caf59SThomas Veerman wp = word;
16752e2caf59SThomas Veerman pat = patternp;
16762e2caf59SThomas Veerman
16772e2caf59SThomas Veerman if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
16782e2caf59SThomas Veerman (VAR_SUB_ONE|VAR_SUB_MATCHED))
16792e2caf59SThomas Veerman xrv = REG_NOMATCH;
16802e2caf59SThomas Veerman else {
16812e2caf59SThomas Veerman tryagain:
16822e2caf59SThomas Veerman xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags);
16832e2caf59SThomas Veerman }
16842e2caf59SThomas Veerman
16852e2caf59SThomas Veerman switch (xrv) {
16862e2caf59SThomas Veerman case 0:
16872e2caf59SThomas Veerman pat->flags |= VAR_SUB_MATCHED;
16882e2caf59SThomas Veerman if (pat->matches[0].rm_so > 0) {
16892e2caf59SThomas Veerman MAYBE_ADD_SPACE();
16902e2caf59SThomas Veerman Buf_AddBytes(buf, pat->matches[0].rm_so, wp);
16912e2caf59SThomas Veerman }
16922e2caf59SThomas Veerman
16932e2caf59SThomas Veerman for (rp = pat->replace; *rp; rp++) {
16942e2caf59SThomas Veerman if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
16952e2caf59SThomas Veerman MAYBE_ADD_SPACE();
16962e2caf59SThomas Veerman Buf_AddByte(buf,rp[1]);
16972e2caf59SThomas Veerman rp++;
16982e2caf59SThomas Veerman }
16992e2caf59SThomas Veerman else if ((*rp == '&') ||
17002e2caf59SThomas Veerman ((*rp == '\\') && isdigit((unsigned char)rp[1]))) {
17012e2caf59SThomas Veerman int n;
17022e2caf59SThomas Veerman const char *subbuf;
17032e2caf59SThomas Veerman int sublen;
17042e2caf59SThomas Veerman char errstr[3];
17052e2caf59SThomas Veerman
17062e2caf59SThomas Veerman if (*rp == '&') {
17072e2caf59SThomas Veerman n = 0;
17082e2caf59SThomas Veerman errstr[0] = '&';
17092e2caf59SThomas Veerman errstr[1] = '\0';
17102e2caf59SThomas Veerman } else {
17112e2caf59SThomas Veerman n = rp[1] - '0';
17122e2caf59SThomas Veerman errstr[0] = '\\';
17132e2caf59SThomas Veerman errstr[1] = rp[1];
17142e2caf59SThomas Veerman errstr[2] = '\0';
17152e2caf59SThomas Veerman rp++;
17162e2caf59SThomas Veerman }
17172e2caf59SThomas Veerman
17182e2caf59SThomas Veerman if (n > pat->nsub) {
17192e2caf59SThomas Veerman Error("No subexpression %s", &errstr[0]);
17202e2caf59SThomas Veerman subbuf = "";
17212e2caf59SThomas Veerman sublen = 0;
17222e2caf59SThomas Veerman } else if ((pat->matches[n].rm_so == -1) &&
17232e2caf59SThomas Veerman (pat->matches[n].rm_eo == -1)) {
17242e2caf59SThomas Veerman Error("No match for subexpression %s", &errstr[0]);
17252e2caf59SThomas Veerman subbuf = "";
17262e2caf59SThomas Veerman sublen = 0;
17272e2caf59SThomas Veerman } else {
17282e2caf59SThomas Veerman subbuf = wp + pat->matches[n].rm_so;
17292e2caf59SThomas Veerman sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so;
17302e2caf59SThomas Veerman }
17312e2caf59SThomas Veerman
17322e2caf59SThomas Veerman if (sublen > 0) {
17332e2caf59SThomas Veerman MAYBE_ADD_SPACE();
17342e2caf59SThomas Veerman Buf_AddBytes(buf, sublen, subbuf);
17352e2caf59SThomas Veerman }
17362e2caf59SThomas Veerman } else {
17372e2caf59SThomas Veerman MAYBE_ADD_SPACE();
17382e2caf59SThomas Veerman Buf_AddByte(buf, *rp);
17392e2caf59SThomas Veerman }
17402e2caf59SThomas Veerman }
17412e2caf59SThomas Veerman wp += pat->matches[0].rm_eo;
17422e2caf59SThomas Veerman if (pat->flags & VAR_SUB_GLOBAL) {
17432e2caf59SThomas Veerman flags |= REG_NOTBOL;
17442e2caf59SThomas Veerman if (pat->matches[0].rm_so == 0 && pat->matches[0].rm_eo == 0) {
17452e2caf59SThomas Veerman MAYBE_ADD_SPACE();
17462e2caf59SThomas Veerman Buf_AddByte(buf, *wp);
17472e2caf59SThomas Veerman wp++;
17482e2caf59SThomas Veerman
17492e2caf59SThomas Veerman }
17502e2caf59SThomas Veerman if (*wp)
17512e2caf59SThomas Veerman goto tryagain;
17522e2caf59SThomas Veerman }
17532e2caf59SThomas Veerman if (*wp) {
17542e2caf59SThomas Veerman MAYBE_ADD_SPACE();
17552e2caf59SThomas Veerman Buf_AddBytes(buf, strlen(wp), wp);
17562e2caf59SThomas Veerman }
17572e2caf59SThomas Veerman break;
17582e2caf59SThomas Veerman default:
17592e2caf59SThomas Veerman VarREError(xrv, &pat->re, "Unexpected regex error");
17602e2caf59SThomas Veerman /* fall through */
17612e2caf59SThomas Veerman case REG_NOMATCH:
17622e2caf59SThomas Veerman if (*wp) {
17632e2caf59SThomas Veerman MAYBE_ADD_SPACE();
17642e2caf59SThomas Veerman Buf_AddBytes(buf,strlen(wp),wp);
17652e2caf59SThomas Veerman }
17662e2caf59SThomas Veerman break;
17672e2caf59SThomas Veerman }
17682e2caf59SThomas Veerman return(addSpace||added);
17692e2caf59SThomas Veerman }
17702e2caf59SThomas Veerman #endif
17712e2caf59SThomas Veerman
17722e2caf59SThomas Veerman
17732e2caf59SThomas Veerman
17742e2caf59SThomas Veerman /*-
17752e2caf59SThomas Veerman *-----------------------------------------------------------------------
17762e2caf59SThomas Veerman * VarLoopExpand --
17772e2caf59SThomas Veerman * Implements the :@<temp>@<string>@ modifier of ODE make.
17782e2caf59SThomas Veerman * We set the temp variable named in pattern.lhs to word and expand
17792e2caf59SThomas Veerman * pattern.rhs storing the result in the passed buffer.
17802e2caf59SThomas Veerman *
17812e2caf59SThomas Veerman * Input:
17822e2caf59SThomas Veerman * word Word to modify
17832e2caf59SThomas Veerman * addSpace True if space should be added before
17842e2caf59SThomas Veerman * other characters
17852e2caf59SThomas Veerman * buf Buffer for result
17862e2caf59SThomas Veerman * pattern Datafor substitution
17872e2caf59SThomas Veerman *
17882e2caf59SThomas Veerman * Results:
17892e2caf59SThomas Veerman * TRUE if a space is needed before more characters are added.
17902e2caf59SThomas Veerman *
17912e2caf59SThomas Veerman * Side Effects:
17922e2caf59SThomas Veerman * None.
17932e2caf59SThomas Veerman *
17942e2caf59SThomas Veerman *-----------------------------------------------------------------------
17952e2caf59SThomas Veerman */
17962e2caf59SThomas Veerman static Boolean
VarLoopExpand(GNode * ctx MAKE_ATTR_UNUSED,Var_Parse_State * vpstate MAKE_ATTR_UNUSED,char * word,Boolean addSpace,Buffer * buf,void * loopp)17972bc7c627SLionel Sambuc VarLoopExpand(GNode *ctx MAKE_ATTR_UNUSED,
17982bc7c627SLionel Sambuc Var_Parse_State *vpstate MAKE_ATTR_UNUSED,
17992e2caf59SThomas Veerman char *word, Boolean addSpace, Buffer *buf,
18002e2caf59SThomas Veerman void *loopp)
18012e2caf59SThomas Veerman {
18022e2caf59SThomas Veerman VarLoop_t *loop = (VarLoop_t *)loopp;
18032e2caf59SThomas Veerman char *s;
18042e2caf59SThomas Veerman int slen;
18052e2caf59SThomas Veerman
18062e2caf59SThomas Veerman if (word && *word) {
18072e2caf59SThomas Veerman Var_Set(loop->tvar, word, loop->ctxt, VAR_NO_EXPORT);
18082e2caf59SThomas Veerman s = Var_Subst(NULL, loop->str, loop->ctxt, loop->errnum);
18092e2caf59SThomas Veerman if (s != NULL && *s != '\0') {
18102e2caf59SThomas Veerman if (addSpace && *s != '\n')
18112e2caf59SThomas Veerman Buf_AddByte(buf, ' ');
18122e2caf59SThomas Veerman Buf_AddBytes(buf, (slen = strlen(s)), s);
18132e2caf59SThomas Veerman addSpace = (slen > 0 && s[slen - 1] != '\n');
18142e2caf59SThomas Veerman free(s);
18152e2caf59SThomas Veerman }
18162e2caf59SThomas Veerman }
18172e2caf59SThomas Veerman return addSpace;
18182e2caf59SThomas Veerman }
18192e2caf59SThomas Veerman
18202e2caf59SThomas Veerman
18212e2caf59SThomas Veerman /*-
18222e2caf59SThomas Veerman *-----------------------------------------------------------------------
18232e2caf59SThomas Veerman * VarSelectWords --
18242e2caf59SThomas Veerman * Implements the :[start..end] modifier.
18252e2caf59SThomas Veerman * This is a special case of VarModify since we want to be able
18262e2caf59SThomas Veerman * to scan the list backwards if start > end.
18272e2caf59SThomas Veerman *
18282e2caf59SThomas Veerman * Input:
18292e2caf59SThomas Veerman * str String whose words should be trimmed
18302e2caf59SThomas Veerman * seldata words to select
18312e2caf59SThomas Veerman *
18322e2caf59SThomas Veerman * Results:
18332e2caf59SThomas Veerman * A string of all the words selected.
18342e2caf59SThomas Veerman *
18352e2caf59SThomas Veerman * Side Effects:
18362e2caf59SThomas Veerman * None.
18372e2caf59SThomas Veerman *
18382e2caf59SThomas Veerman *-----------------------------------------------------------------------
18392e2caf59SThomas Veerman */
18402e2caf59SThomas Veerman static char *
VarSelectWords(GNode * ctx MAKE_ATTR_UNUSED,Var_Parse_State * vpstate,const char * str,VarSelectWords_t * seldata)18412bc7c627SLionel Sambuc VarSelectWords(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
18422e2caf59SThomas Veerman const char *str, VarSelectWords_t *seldata)
18432e2caf59SThomas Veerman {
18442e2caf59SThomas Veerman Buffer buf; /* Buffer for the new string */
18452e2caf59SThomas Veerman Boolean addSpace; /* TRUE if need to add a space to the
18462e2caf59SThomas Veerman * buffer before adding the trimmed
18472e2caf59SThomas Veerman * word */
18482e2caf59SThomas Veerman char **av; /* word list */
18492e2caf59SThomas Veerman char *as; /* word list memory */
18502e2caf59SThomas Veerman int ac, i;
18512e2caf59SThomas Veerman int start, end, step;
18522e2caf59SThomas Veerman
18532e2caf59SThomas Veerman Buf_Init(&buf, 0);
18542e2caf59SThomas Veerman addSpace = FALSE;
18552e2caf59SThomas Veerman
18562e2caf59SThomas Veerman if (vpstate->oneBigWord) {
18572e2caf59SThomas Veerman /* fake what brk_string() would do if there were only one word */
18582e2caf59SThomas Veerman ac = 1;
18592e2caf59SThomas Veerman av = bmake_malloc((ac + 1) * sizeof(char *));
18602e2caf59SThomas Veerman as = bmake_strdup(str);
18612e2caf59SThomas Veerman av[0] = as;
18622e2caf59SThomas Veerman av[1] = NULL;
18632e2caf59SThomas Veerman } else {
18642e2caf59SThomas Veerman av = brk_string(str, &ac, FALSE, &as);
18652e2caf59SThomas Veerman }
18662e2caf59SThomas Veerman
18672e2caf59SThomas Veerman /*
18682e2caf59SThomas Veerman * Now sanitize seldata.
18692e2caf59SThomas Veerman * If seldata->start or seldata->end are negative, convert them to
18702e2caf59SThomas Veerman * the positive equivalents (-1 gets converted to argc, -2 gets
18712e2caf59SThomas Veerman * converted to (argc-1), etc.).
18722e2caf59SThomas Veerman */
18732e2caf59SThomas Veerman if (seldata->start < 0)
18742e2caf59SThomas Veerman seldata->start = ac + seldata->start + 1;
18752e2caf59SThomas Veerman if (seldata->end < 0)
18762e2caf59SThomas Veerman seldata->end = ac + seldata->end + 1;
18772e2caf59SThomas Veerman
18782e2caf59SThomas Veerman /*
18792e2caf59SThomas Veerman * We avoid scanning more of the list than we need to.
18802e2caf59SThomas Veerman */
18812e2caf59SThomas Veerman if (seldata->start > seldata->end) {
18822e2caf59SThomas Veerman start = MIN(ac, seldata->start) - 1;
18832e2caf59SThomas Veerman end = MAX(0, seldata->end - 1);
18842e2caf59SThomas Veerman step = -1;
18852e2caf59SThomas Veerman } else {
18862e2caf59SThomas Veerman start = MAX(0, seldata->start - 1);
18872e2caf59SThomas Veerman end = MIN(ac, seldata->end);
18882e2caf59SThomas Veerman step = 1;
18892e2caf59SThomas Veerman }
18902e2caf59SThomas Veerman
18912e2caf59SThomas Veerman for (i = start;
18922e2caf59SThomas Veerman (step < 0 && i >= end) || (step > 0 && i < end);
18932e2caf59SThomas Veerman i += step) {
18942e2caf59SThomas Veerman if (av[i] && *av[i]) {
18952e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
18962e2caf59SThomas Veerman Buf_AddByte(&buf, vpstate->varSpace);
18972e2caf59SThomas Veerman }
18982e2caf59SThomas Veerman Buf_AddBytes(&buf, strlen(av[i]), av[i]);
18992e2caf59SThomas Veerman addSpace = TRUE;
19002e2caf59SThomas Veerman }
19012e2caf59SThomas Veerman }
19022e2caf59SThomas Veerman
19032e2caf59SThomas Veerman free(as);
19042e2caf59SThomas Veerman free(av);
19052e2caf59SThomas Veerman
19062e2caf59SThomas Veerman return Buf_Destroy(&buf, FALSE);
19072e2caf59SThomas Veerman }
19082e2caf59SThomas Veerman
19092e2caf59SThomas Veerman
19102e2caf59SThomas Veerman /*-
19112e2caf59SThomas Veerman * VarRealpath --
19122e2caf59SThomas Veerman * Replace each word with the result of realpath()
19132e2caf59SThomas Veerman * if successful.
19142e2caf59SThomas Veerman */
19152e2caf59SThomas Veerman static Boolean
VarRealpath(GNode * ctx MAKE_ATTR_UNUSED,Var_Parse_State * vpstate,char * word,Boolean addSpace,Buffer * buf,void * patternp MAKE_ATTR_UNUSED)19162bc7c627SLionel Sambuc VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate,
19172e2caf59SThomas Veerman char *word, Boolean addSpace, Buffer *buf,
19182bc7c627SLionel Sambuc void *patternp MAKE_ATTR_UNUSED)
19192e2caf59SThomas Veerman {
19202e2caf59SThomas Veerman struct stat st;
19212e2caf59SThomas Veerman char rbuf[MAXPATHLEN];
19222e2caf59SThomas Veerman char *rp;
19232e2caf59SThomas Veerman
19242e2caf59SThomas Veerman if (addSpace && vpstate->varSpace) {
19252e2caf59SThomas Veerman Buf_AddByte(buf, vpstate->varSpace);
19262e2caf59SThomas Veerman }
19272e2caf59SThomas Veerman addSpace = TRUE;
19282e2caf59SThomas Veerman rp = realpath(word, rbuf);
19292e2caf59SThomas Veerman if (rp && *rp == '/' && stat(rp, &st) == 0)
19302e2caf59SThomas Veerman word = rp;
19312e2caf59SThomas Veerman
19322e2caf59SThomas Veerman Buf_AddBytes(buf, strlen(word), word);
19332e2caf59SThomas Veerman return(addSpace);
19342e2caf59SThomas Veerman }
19352e2caf59SThomas Veerman
19362e2caf59SThomas Veerman /*-
19372e2caf59SThomas Veerman *-----------------------------------------------------------------------
19382e2caf59SThomas Veerman * VarModify --
19392e2caf59SThomas Veerman * Modify each of the words of the passed string using the given
19402e2caf59SThomas Veerman * function. Used to implement all modifiers.
19412e2caf59SThomas Veerman *
19422e2caf59SThomas Veerman * Input:
19432e2caf59SThomas Veerman * str String whose words should be trimmed
19442e2caf59SThomas Veerman * modProc Function to use to modify them
19452e2caf59SThomas Veerman * datum Datum to pass it
19462e2caf59SThomas Veerman *
19472e2caf59SThomas Veerman * Results:
19482e2caf59SThomas Veerman * A string of all the words modified appropriately.
19492e2caf59SThomas Veerman *
19502e2caf59SThomas Veerman * Side Effects:
19512e2caf59SThomas Veerman * None.
19522e2caf59SThomas Veerman *
19532e2caf59SThomas Veerman *-----------------------------------------------------------------------
19542e2caf59SThomas Veerman */
19552e2caf59SThomas Veerman static char *
VarModify(GNode * ctx,Var_Parse_State * vpstate,const char * str,Boolean (* modProc)(GNode *,Var_Parse_State *,char *,Boolean,Buffer *,void *),void * datum)19562e2caf59SThomas Veerman VarModify(GNode *ctx, Var_Parse_State *vpstate,
19572e2caf59SThomas Veerman const char *str,
19582e2caf59SThomas Veerman Boolean (*modProc)(GNode *, Var_Parse_State *, char *,
19592e2caf59SThomas Veerman Boolean, Buffer *, void *),
19602e2caf59SThomas Veerman void *datum)
19612e2caf59SThomas Veerman {
19622e2caf59SThomas Veerman Buffer buf; /* Buffer for the new string */
19632e2caf59SThomas Veerman Boolean addSpace; /* TRUE if need to add a space to the
19642e2caf59SThomas Veerman * buffer before adding the trimmed
19652e2caf59SThomas Veerman * word */
19662e2caf59SThomas Veerman char **av; /* word list */
19672e2caf59SThomas Veerman char *as; /* word list memory */
19682e2caf59SThomas Veerman int ac, i;
19692e2caf59SThomas Veerman
19702e2caf59SThomas Veerman Buf_Init(&buf, 0);
19712e2caf59SThomas Veerman addSpace = FALSE;
19722e2caf59SThomas Veerman
19732e2caf59SThomas Veerman if (vpstate->oneBigWord) {
19742e2caf59SThomas Veerman /* fake what brk_string() would do if there were only one word */
19752e2caf59SThomas Veerman ac = 1;
19762e2caf59SThomas Veerman av = bmake_malloc((ac + 1) * sizeof(char *));
19772e2caf59SThomas Veerman as = bmake_strdup(str);
19782e2caf59SThomas Veerman av[0] = as;
19792e2caf59SThomas Veerman av[1] = NULL;
19802e2caf59SThomas Veerman } else {
19812e2caf59SThomas Veerman av = brk_string(str, &ac, FALSE, &as);
19822e2caf59SThomas Veerman }
19832e2caf59SThomas Veerman
19842e2caf59SThomas Veerman for (i = 0; i < ac; i++) {
19852e2caf59SThomas Veerman addSpace = (*modProc)(ctx, vpstate, av[i], addSpace, &buf, datum);
19862e2caf59SThomas Veerman }
19872e2caf59SThomas Veerman
19882e2caf59SThomas Veerman free(as);
19892e2caf59SThomas Veerman free(av);
19902e2caf59SThomas Veerman
19912e2caf59SThomas Veerman return Buf_Destroy(&buf, FALSE);
19922e2caf59SThomas Veerman }
19932e2caf59SThomas Veerman
19942e2caf59SThomas Veerman
19952e2caf59SThomas Veerman static int
VarWordCompare(const void * a,const void * b)19962e2caf59SThomas Veerman VarWordCompare(const void *a, const void *b)
19972e2caf59SThomas Veerman {
19982e2caf59SThomas Veerman int r = strcmp(*(const char * const *)a, *(const char * const *)b);
19992e2caf59SThomas Veerman return r;
20002e2caf59SThomas Veerman }
20012e2caf59SThomas Veerman
20022e2caf59SThomas Veerman /*-
20032e2caf59SThomas Veerman *-----------------------------------------------------------------------
20042e2caf59SThomas Veerman * VarOrder --
20052e2caf59SThomas Veerman * Order the words in the string.
20062e2caf59SThomas Veerman *
20072e2caf59SThomas Veerman * Input:
20082e2caf59SThomas Veerman * str String whose words should be sorted.
20092e2caf59SThomas Veerman * otype How to order: s - sort, x - random.
20102e2caf59SThomas Veerman *
20112e2caf59SThomas Veerman * Results:
20122e2caf59SThomas Veerman * A string containing the words ordered.
20132e2caf59SThomas Veerman *
20142e2caf59SThomas Veerman * Side Effects:
20152e2caf59SThomas Veerman * None.
20162e2caf59SThomas Veerman *
20172e2caf59SThomas Veerman *-----------------------------------------------------------------------
20182e2caf59SThomas Veerman */
20192e2caf59SThomas Veerman static char *
VarOrder(const char * str,const char otype)20202e2caf59SThomas Veerman VarOrder(const char *str, const char otype)
20212e2caf59SThomas Veerman {
20222e2caf59SThomas Veerman Buffer buf; /* Buffer for the new string */
20232e2caf59SThomas Veerman char **av; /* word list [first word does not count] */
20242e2caf59SThomas Veerman char *as; /* word list memory */
20252e2caf59SThomas Veerman int ac, i;
20262e2caf59SThomas Veerman
20272e2caf59SThomas Veerman Buf_Init(&buf, 0);
20282e2caf59SThomas Veerman
20292e2caf59SThomas Veerman av = brk_string(str, &ac, FALSE, &as);
20302e2caf59SThomas Veerman
20312e2caf59SThomas Veerman if (ac > 0)
20322e2caf59SThomas Veerman switch (otype) {
20332e2caf59SThomas Veerman case 's': /* sort alphabetically */
20342e2caf59SThomas Veerman qsort(av, ac, sizeof(char *), VarWordCompare);
20352e2caf59SThomas Veerman break;
20362e2caf59SThomas Veerman case 'x': /* randomize */
20372e2caf59SThomas Veerman {
20382e2caf59SThomas Veerman int rndidx;
20392e2caf59SThomas Veerman char *t;
20402e2caf59SThomas Veerman
20412e2caf59SThomas Veerman /*
20422e2caf59SThomas Veerman * We will use [ac..2] range for mod factors. This will produce
20432e2caf59SThomas Veerman * random numbers in [(ac-1)..0] interval, and minimal
20442e2caf59SThomas Veerman * reasonable value for mod factor is 2 (the mod 1 will produce
20452e2caf59SThomas Veerman * 0 with probability 1).
20462e2caf59SThomas Veerman */
20472e2caf59SThomas Veerman for (i = ac-1; i > 0; i--) {
20482e2caf59SThomas Veerman rndidx = random() % (i + 1);
20492e2caf59SThomas Veerman if (i != rndidx) {
20502e2caf59SThomas Veerman t = av[i];
20512e2caf59SThomas Veerman av[i] = av[rndidx];
20522e2caf59SThomas Veerman av[rndidx] = t;
20532e2caf59SThomas Veerman }
20542e2caf59SThomas Veerman }
20552e2caf59SThomas Veerman }
20562e2caf59SThomas Veerman } /* end of switch */
20572e2caf59SThomas Veerman
20582e2caf59SThomas Veerman for (i = 0; i < ac; i++) {
20592e2caf59SThomas Veerman Buf_AddBytes(&buf, strlen(av[i]), av[i]);
20602e2caf59SThomas Veerman if (i != ac - 1)
20612e2caf59SThomas Veerman Buf_AddByte(&buf, ' ');
20622e2caf59SThomas Veerman }
20632e2caf59SThomas Veerman
20642e2caf59SThomas Veerman free(as);
20652e2caf59SThomas Veerman free(av);
20662e2caf59SThomas Veerman
20672e2caf59SThomas Veerman return Buf_Destroy(&buf, FALSE);
20682e2caf59SThomas Veerman }
20692e2caf59SThomas Veerman
20702e2caf59SThomas Veerman
20712e2caf59SThomas Veerman /*-
20722e2caf59SThomas Veerman *-----------------------------------------------------------------------
20732e2caf59SThomas Veerman * VarUniq --
20742e2caf59SThomas Veerman * Remove adjacent duplicate words.
20752e2caf59SThomas Veerman *
20762e2caf59SThomas Veerman * Input:
20772e2caf59SThomas Veerman * str String whose words should be sorted
20782e2caf59SThomas Veerman *
20792e2caf59SThomas Veerman * Results:
20802e2caf59SThomas Veerman * A string containing the resulting words.
20812e2caf59SThomas Veerman *
20822e2caf59SThomas Veerman * Side Effects:
20832e2caf59SThomas Veerman * None.
20842e2caf59SThomas Veerman *
20852e2caf59SThomas Veerman *-----------------------------------------------------------------------
20862e2caf59SThomas Veerman */
20872e2caf59SThomas Veerman static char *
VarUniq(const char * str)20882e2caf59SThomas Veerman VarUniq(const char *str)
20892e2caf59SThomas Veerman {
20902e2caf59SThomas Veerman Buffer buf; /* Buffer for new string */
20912e2caf59SThomas Veerman char **av; /* List of words to affect */
20922e2caf59SThomas Veerman char *as; /* Word list memory */
20932e2caf59SThomas Veerman int ac, i, j;
20942e2caf59SThomas Veerman
20952e2caf59SThomas Veerman Buf_Init(&buf, 0);
20962e2caf59SThomas Veerman av = brk_string(str, &ac, FALSE, &as);
20972e2caf59SThomas Veerman
20982e2caf59SThomas Veerman if (ac > 1) {
20992e2caf59SThomas Veerman for (j = 0, i = 1; i < ac; i++)
21002e2caf59SThomas Veerman if (strcmp(av[i], av[j]) != 0 && (++j != i))
21012e2caf59SThomas Veerman av[j] = av[i];
21022e2caf59SThomas Veerman ac = j + 1;
21032e2caf59SThomas Veerman }
21042e2caf59SThomas Veerman
21052e2caf59SThomas Veerman for (i = 0; i < ac; i++) {
21062e2caf59SThomas Veerman Buf_AddBytes(&buf, strlen(av[i]), av[i]);
21072e2caf59SThomas Veerman if (i != ac - 1)
21082e2caf59SThomas Veerman Buf_AddByte(&buf, ' ');
21092e2caf59SThomas Veerman }
21102e2caf59SThomas Veerman
21112e2caf59SThomas Veerman free(as);
21122e2caf59SThomas Veerman free(av);
21132e2caf59SThomas Veerman
21142e2caf59SThomas Veerman return Buf_Destroy(&buf, FALSE);
21152e2caf59SThomas Veerman }
21162e2caf59SThomas Veerman
21172e2caf59SThomas Veerman
21182e2caf59SThomas Veerman /*-
21192e2caf59SThomas Veerman *-----------------------------------------------------------------------
21202e2caf59SThomas Veerman * VarGetPattern --
21212e2caf59SThomas Veerman * Pass through the tstr looking for 1) escaped delimiters,
21222e2caf59SThomas Veerman * '$'s and backslashes (place the escaped character in
21232e2caf59SThomas Veerman * uninterpreted) and 2) unescaped $'s that aren't before
21242e2caf59SThomas Veerman * the delimiter (expand the variable substitution unless flags
21252e2caf59SThomas Veerman * has VAR_NOSUBST set).
21262e2caf59SThomas Veerman * Return the expanded string or NULL if the delimiter was missing
21272e2caf59SThomas Veerman * If pattern is specified, handle escaped ampersands, and replace
21282e2caf59SThomas Veerman * unescaped ampersands with the lhs of the pattern.
21292e2caf59SThomas Veerman *
21302e2caf59SThomas Veerman * Results:
21312e2caf59SThomas Veerman * A string of all the words modified appropriately.
21322e2caf59SThomas Veerman * If length is specified, return the string length of the buffer
21332e2caf59SThomas Veerman * If flags is specified and the last character of the pattern is a
21342e2caf59SThomas Veerman * $ set the VAR_MATCH_END bit of flags.
21352e2caf59SThomas Veerman *
21362e2caf59SThomas Veerman * Side Effects:
21372e2caf59SThomas Veerman * None.
21382e2caf59SThomas Veerman *-----------------------------------------------------------------------
21392e2caf59SThomas Veerman */
21402e2caf59SThomas Veerman static char *
VarGetPattern(GNode * ctxt,Var_Parse_State * vpstate MAKE_ATTR_UNUSED,int errnum,const char ** tstr,int delim,int * flags,int * length,VarPattern * pattern)21412bc7c627SLionel Sambuc VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate MAKE_ATTR_UNUSED,
21422e2caf59SThomas Veerman int errnum, const char **tstr, int delim, int *flags,
21432e2caf59SThomas Veerman int *length, VarPattern *pattern)
21442e2caf59SThomas Veerman {
21452e2caf59SThomas Veerman const char *cp;
21462e2caf59SThomas Veerman char *rstr;
21472e2caf59SThomas Veerman Buffer buf;
21482e2caf59SThomas Veerman int junk;
21492e2caf59SThomas Veerman
21502e2caf59SThomas Veerman Buf_Init(&buf, 0);
21512e2caf59SThomas Veerman if (length == NULL)
21522e2caf59SThomas Veerman length = &junk;
21532e2caf59SThomas Veerman
21542e2caf59SThomas Veerman #define IS_A_MATCH(cp, delim) \
21552e2caf59SThomas Veerman ((cp[0] == '\\') && ((cp[1] == delim) || \
21562e2caf59SThomas Veerman (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&'))))
21572e2caf59SThomas Veerman
21582e2caf59SThomas Veerman /*
21592e2caf59SThomas Veerman * Skim through until the matching delimiter is found;
21602e2caf59SThomas Veerman * pick up variable substitutions on the way. Also allow
21612e2caf59SThomas Veerman * backslashes to quote the delimiter, $, and \, but don't
21622e2caf59SThomas Veerman * touch other backslashes.
21632e2caf59SThomas Veerman */
21642e2caf59SThomas Veerman for (cp = *tstr; *cp && (*cp != delim); cp++) {
21652e2caf59SThomas Veerman if (IS_A_MATCH(cp, delim)) {
21662e2caf59SThomas Veerman Buf_AddByte(&buf, cp[1]);
21672e2caf59SThomas Veerman cp++;
21682e2caf59SThomas Veerman } else if (*cp == '$') {
21692e2caf59SThomas Veerman if (cp[1] == delim) {
21702e2caf59SThomas Veerman if (flags == NULL)
21712e2caf59SThomas Veerman Buf_AddByte(&buf, *cp);
21722e2caf59SThomas Veerman else
21732e2caf59SThomas Veerman /*
21742e2caf59SThomas Veerman * Unescaped $ at end of pattern => anchor
21752e2caf59SThomas Veerman * pattern at end.
21762e2caf59SThomas Veerman */
21772e2caf59SThomas Veerman *flags |= VAR_MATCH_END;
21782e2caf59SThomas Veerman } else {
21792e2caf59SThomas Veerman if (flags == NULL || (*flags & VAR_NOSUBST) == 0) {
21802e2caf59SThomas Veerman char *cp2;
21812e2caf59SThomas Veerman int len;
21822e2caf59SThomas Veerman void *freeIt;
21832e2caf59SThomas Veerman
21842e2caf59SThomas Veerman /*
21852e2caf59SThomas Veerman * If unescaped dollar sign not before the
21862e2caf59SThomas Veerman * delimiter, assume it's a variable
21872e2caf59SThomas Veerman * substitution and recurse.
21882e2caf59SThomas Veerman */
21892e2caf59SThomas Veerman cp2 = Var_Parse(cp, ctxt, errnum, &len, &freeIt);
21902e2caf59SThomas Veerman Buf_AddBytes(&buf, strlen(cp2), cp2);
21912e2caf59SThomas Veerman free(freeIt);
21922e2caf59SThomas Veerman cp += len - 1;
21932e2caf59SThomas Veerman } else {
21942e2caf59SThomas Veerman const char *cp2 = &cp[1];
21952e2caf59SThomas Veerman
21962e2caf59SThomas Veerman if (*cp2 == PROPEN || *cp2 == BROPEN) {
21972e2caf59SThomas Veerman /*
21982e2caf59SThomas Veerman * Find the end of this variable reference
21992e2caf59SThomas Veerman * and suck it in without further ado.
22002e2caf59SThomas Veerman * It will be interperated later.
22012e2caf59SThomas Veerman */
22022e2caf59SThomas Veerman int have = *cp2;
22032e2caf59SThomas Veerman int want = (*cp2 == PROPEN) ? PRCLOSE : BRCLOSE;
22042e2caf59SThomas Veerman int depth = 1;
22052e2caf59SThomas Veerman
22062e2caf59SThomas Veerman for (++cp2; *cp2 != '\0' && depth > 0; ++cp2) {
22072e2caf59SThomas Veerman if (cp2[-1] != '\\') {
22082e2caf59SThomas Veerman if (*cp2 == have)
22092e2caf59SThomas Veerman ++depth;
22102e2caf59SThomas Veerman if (*cp2 == want)
22112e2caf59SThomas Veerman --depth;
22122e2caf59SThomas Veerman }
22132e2caf59SThomas Veerman }
22142e2caf59SThomas Veerman Buf_AddBytes(&buf, cp2 - cp, cp);
22152e2caf59SThomas Veerman cp = --cp2;
22162e2caf59SThomas Veerman } else
22172e2caf59SThomas Veerman Buf_AddByte(&buf, *cp);
22182e2caf59SThomas Veerman }
22192e2caf59SThomas Veerman }
22202e2caf59SThomas Veerman }
22212e2caf59SThomas Veerman else if (pattern && *cp == '&')
22222e2caf59SThomas Veerman Buf_AddBytes(&buf, pattern->leftLen, pattern->lhs);
22232e2caf59SThomas Veerman else
22242e2caf59SThomas Veerman Buf_AddByte(&buf, *cp);
22252e2caf59SThomas Veerman }
22262e2caf59SThomas Veerman
22272e2caf59SThomas Veerman if (*cp != delim) {
22282e2caf59SThomas Veerman *tstr = cp;
22292e2caf59SThomas Veerman *length = 0;
22302e2caf59SThomas Veerman return NULL;
22312e2caf59SThomas Veerman }
22322e2caf59SThomas Veerman
22332e2caf59SThomas Veerman *tstr = ++cp;
22342e2caf59SThomas Veerman *length = Buf_Size(&buf);
22352e2caf59SThomas Veerman rstr = Buf_Destroy(&buf, FALSE);
22362e2caf59SThomas Veerman if (DEBUG(VAR))
22372e2caf59SThomas Veerman fprintf(debug_file, "Modifier pattern: \"%s\"\n", rstr);
22382e2caf59SThomas Veerman return rstr;
22392e2caf59SThomas Veerman }
22402e2caf59SThomas Veerman
22412e2caf59SThomas Veerman /*-
22422e2caf59SThomas Veerman *-----------------------------------------------------------------------
22432e2caf59SThomas Veerman * VarQuote --
2244*0a6a1f1dSLionel Sambuc * Quote shell meta-characters and space characters in the string
22452e2caf59SThomas Veerman *
22462e2caf59SThomas Veerman * Results:
22472e2caf59SThomas Veerman * The quoted string
22482e2caf59SThomas Veerman *
22492e2caf59SThomas Veerman * Side Effects:
22502e2caf59SThomas Veerman * None.
22512e2caf59SThomas Veerman *
22522e2caf59SThomas Veerman *-----------------------------------------------------------------------
22532e2caf59SThomas Veerman */
22542e2caf59SThomas Veerman static char *
VarQuote(char * str)22552e2caf59SThomas Veerman VarQuote(char *str)
22562e2caf59SThomas Veerman {
22572e2caf59SThomas Veerman
22582e2caf59SThomas Veerman Buffer buf;
22592e2caf59SThomas Veerman const char *newline;
2260*0a6a1f1dSLionel Sambuc size_t nlen;
22612e2caf59SThomas Veerman
22622e2caf59SThomas Veerman if ((newline = Shell_GetNewline()) == NULL)
22632e2caf59SThomas Veerman newline = "\\\n";
22642e2caf59SThomas Veerman nlen = strlen(newline);
22652e2caf59SThomas Veerman
22662e2caf59SThomas Veerman Buf_Init(&buf, 0);
2267*0a6a1f1dSLionel Sambuc
2268*0a6a1f1dSLionel Sambuc for (; *str != '\0'; str++) {
2269*0a6a1f1dSLionel Sambuc if (*str == '\n') {
22702e2caf59SThomas Veerman Buf_AddBytes(&buf, nlen, newline);
2271*0a6a1f1dSLionel Sambuc continue;
2272*0a6a1f1dSLionel Sambuc }
2273*0a6a1f1dSLionel Sambuc if (isspace((unsigned char)*str) || ismeta((unsigned char)*str))
22742e2caf59SThomas Veerman Buf_AddByte(&buf, '\\');
22752e2caf59SThomas Veerman Buf_AddByte(&buf, *str);
22762e2caf59SThomas Veerman }
2277*0a6a1f1dSLionel Sambuc
22782e2caf59SThomas Veerman str = Buf_Destroy(&buf, FALSE);
22792e2caf59SThomas Veerman if (DEBUG(VAR))
22802e2caf59SThomas Veerman fprintf(debug_file, "QuoteMeta: [%s]\n", str);
22812e2caf59SThomas Veerman return str;
22822e2caf59SThomas Veerman }
22832e2caf59SThomas Veerman
22842e2caf59SThomas Veerman /*-
22852e2caf59SThomas Veerman *-----------------------------------------------------------------------
22862e2caf59SThomas Veerman * VarHash --
22872e2caf59SThomas Veerman * Hash the string using the MurmurHash3 algorithm.
22882e2caf59SThomas Veerman * Output is computed using 32bit Little Endian arithmetic.
22892e2caf59SThomas Veerman *
22902e2caf59SThomas Veerman * Input:
22912e2caf59SThomas Veerman * str String to modify
22922e2caf59SThomas Veerman *
22932e2caf59SThomas Veerman * Results:
22942e2caf59SThomas Veerman * Hash value of str, encoded as 8 hex digits.
22952e2caf59SThomas Veerman *
22962e2caf59SThomas Veerman * Side Effects:
22972e2caf59SThomas Veerman * None.
22982e2caf59SThomas Veerman *
22992e2caf59SThomas Veerman *-----------------------------------------------------------------------
23002e2caf59SThomas Veerman */
23012e2caf59SThomas Veerman static char *
VarHash(char * str)23022e2caf59SThomas Veerman VarHash(char *str)
23032e2caf59SThomas Veerman {
23042e2caf59SThomas Veerman static const char hexdigits[16] = "0123456789abcdef";
23052e2caf59SThomas Veerman Buffer buf;
23062e2caf59SThomas Veerman size_t len, len2;
23072e2caf59SThomas Veerman unsigned char *ustr = (unsigned char *)str;
23082e2caf59SThomas Veerman uint32_t h, k, c1, c2;
23092e2caf59SThomas Veerman
23102e2caf59SThomas Veerman h = 0x971e137bU;
23112e2caf59SThomas Veerman c1 = 0x95543787U;
23122e2caf59SThomas Veerman c2 = 0x2ad7eb25U;
23132e2caf59SThomas Veerman len2 = strlen(str);
23142e2caf59SThomas Veerman
23152e2caf59SThomas Veerman for (len = len2; len; ) {
23162e2caf59SThomas Veerman k = 0;
23172e2caf59SThomas Veerman switch (len) {
23182e2caf59SThomas Veerman default:
23192e2caf59SThomas Veerman k = (ustr[3] << 24) | (ustr[2] << 16) | (ustr[1] << 8) | ustr[0];
23202e2caf59SThomas Veerman len -= 4;
23212e2caf59SThomas Veerman ustr += 4;
23222e2caf59SThomas Veerman break;
23232e2caf59SThomas Veerman case 3:
23242e2caf59SThomas Veerman k |= (ustr[2] << 16);
23252e2caf59SThomas Veerman case 2:
23262e2caf59SThomas Veerman k |= (ustr[1] << 8);
23272e2caf59SThomas Veerman case 1:
23282e2caf59SThomas Veerman k |= ustr[0];
23292e2caf59SThomas Veerman len = 0;
23302e2caf59SThomas Veerman }
23312e2caf59SThomas Veerman c1 = c1 * 5 + 0x7b7d159cU;
23322e2caf59SThomas Veerman c2 = c2 * 5 + 0x6bce6396U;
23332e2caf59SThomas Veerman k *= c1;
23342e2caf59SThomas Veerman k = (k << 11) ^ (k >> 21);
23352e2caf59SThomas Veerman k *= c2;
23362e2caf59SThomas Veerman h = (h << 13) ^ (h >> 19);
23372e2caf59SThomas Veerman h = h * 5 + 0x52dce729U;
23382e2caf59SThomas Veerman h ^= k;
233984d9c625SLionel Sambuc }
23402e2caf59SThomas Veerman h ^= len2;
23412e2caf59SThomas Veerman h *= 0x85ebca6b;
23422e2caf59SThomas Veerman h ^= h >> 13;
23432e2caf59SThomas Veerman h *= 0xc2b2ae35;
23442e2caf59SThomas Veerman h ^= h >> 16;
23452e2caf59SThomas Veerman
23462e2caf59SThomas Veerman Buf_Init(&buf, 0);
23472e2caf59SThomas Veerman for (len = 0; len < 8; ++len) {
23482e2caf59SThomas Veerman Buf_AddByte(&buf, hexdigits[h & 15]);
23492e2caf59SThomas Veerman h >>= 4;
23502e2caf59SThomas Veerman }
23512e2caf59SThomas Veerman
23522e2caf59SThomas Veerman return Buf_Destroy(&buf, FALSE);
23532e2caf59SThomas Veerman }
23542e2caf59SThomas Veerman
23552e2caf59SThomas Veerman static char *
VarStrftime(const char * fmt,int zulu)23562e2caf59SThomas Veerman VarStrftime(const char *fmt, int zulu)
23572e2caf59SThomas Veerman {
23582e2caf59SThomas Veerman char buf[BUFSIZ];
23592e2caf59SThomas Veerman time_t utc;
23602e2caf59SThomas Veerman
23612e2caf59SThomas Veerman time(&utc);
23622e2caf59SThomas Veerman if (!*fmt)
23632e2caf59SThomas Veerman fmt = "%c";
23642e2caf59SThomas Veerman strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc));
23652e2caf59SThomas Veerman
23662e2caf59SThomas Veerman buf[sizeof(buf) - 1] = '\0';
23672e2caf59SThomas Veerman return bmake_strdup(buf);
23682e2caf59SThomas Veerman }
23692e2caf59SThomas Veerman
23702e2caf59SThomas Veerman /*
23712e2caf59SThomas Veerman * Now we need to apply any modifiers the user wants applied.
23722e2caf59SThomas Veerman * These are:
23732e2caf59SThomas Veerman * :M<pattern> words which match the given <pattern>.
23742e2caf59SThomas Veerman * <pattern> is of the standard file
23752e2caf59SThomas Veerman * wildcarding form.
23762e2caf59SThomas Veerman * :N<pattern> words which do not match the given <pattern>.
23772e2caf59SThomas Veerman * :S<d><pat1><d><pat2><d>[1gW]
23782e2caf59SThomas Veerman * Substitute <pat2> for <pat1> in the value
23792e2caf59SThomas Veerman * :C<d><pat1><d><pat2><d>[1gW]
23802e2caf59SThomas Veerman * Substitute <pat2> for regex <pat1> in the value
23812e2caf59SThomas Veerman * :H Substitute the head of each word
23822e2caf59SThomas Veerman * :T Substitute the tail of each word
23832e2caf59SThomas Veerman * :E Substitute the extension (minus '.') of
23842e2caf59SThomas Veerman * each word
23852e2caf59SThomas Veerman * :R Substitute the root of each word
23862e2caf59SThomas Veerman * (pathname minus the suffix).
23872e2caf59SThomas Veerman * :O ("Order") Alphabeticaly sort words in variable.
23882e2caf59SThomas Veerman * :Ox ("intermiX") Randomize words in variable.
23892e2caf59SThomas Veerman * :u ("uniq") Remove adjacent duplicate words.
23902e2caf59SThomas Veerman * :tu Converts the variable contents to uppercase.
23912e2caf59SThomas Veerman * :tl Converts the variable contents to lowercase.
23922e2caf59SThomas Veerman * :ts[c] Sets varSpace - the char used to
23932e2caf59SThomas Veerman * separate words to 'c'. If 'c' is
23942e2caf59SThomas Veerman * omitted then no separation is used.
23952e2caf59SThomas Veerman * :tW Treat the variable contents as a single
23962e2caf59SThomas Veerman * word, even if it contains spaces.
23972e2caf59SThomas Veerman * (Mnemonic: one big 'W'ord.)
23982e2caf59SThomas Veerman * :tw Treat the variable contents as multiple
23992e2caf59SThomas Veerman * space-separated words.
24002e2caf59SThomas Veerman * (Mnemonic: many small 'w'ords.)
24012e2caf59SThomas Veerman * :[index] Select a single word from the value.
24022e2caf59SThomas Veerman * :[start..end] Select multiple words from the value.
24032e2caf59SThomas Veerman * :[*] or :[0] Select the entire value, as a single
24042e2caf59SThomas Veerman * word. Equivalent to :tW.
24052e2caf59SThomas Veerman * :[@] Select the entire value, as multiple
24062e2caf59SThomas Veerman * words. Undoes the effect of :[*].
24072e2caf59SThomas Veerman * Equivalent to :tw.
24082e2caf59SThomas Veerman * :[#] Returns the number of words in the value.
24092e2caf59SThomas Veerman *
24102e2caf59SThomas Veerman * :?<true-value>:<false-value>
24112e2caf59SThomas Veerman * If the variable evaluates to true, return
24122e2caf59SThomas Veerman * true value, else return the second value.
24132e2caf59SThomas Veerman * :lhs=rhs Like :S, but the rhs goes to the end of
24142e2caf59SThomas Veerman * the invocation.
24152e2caf59SThomas Veerman * :sh Treat the current value as a command
24162e2caf59SThomas Veerman * to be run, new value is its output.
24172e2caf59SThomas Veerman * The following added so we can handle ODE makefiles.
24182e2caf59SThomas Veerman * :@<tmpvar>@<newval>@
24192e2caf59SThomas Veerman * Assign a temporary local variable <tmpvar>
24202e2caf59SThomas Veerman * to the current value of each word in turn
24212e2caf59SThomas Veerman * and replace each word with the result of
24222e2caf59SThomas Veerman * evaluating <newval>
24232e2caf59SThomas Veerman * :D<newval> Use <newval> as value if variable defined
24242e2caf59SThomas Veerman * :U<newval> Use <newval> as value if variable undefined
24252e2caf59SThomas Veerman * :L Use the name of the variable as the value.
24262e2caf59SThomas Veerman * :P Use the path of the node that has the same
24272e2caf59SThomas Veerman * name as the variable as the value. This
24282e2caf59SThomas Veerman * basically includes an implied :L so that
24292e2caf59SThomas Veerman * the common method of refering to the path
24302e2caf59SThomas Veerman * of your dependent 'x' in a rule is to use
24312e2caf59SThomas Veerman * the form '${x:P}'.
24322e2caf59SThomas Veerman * :!<cmd>! Run cmd much the same as :sh run's the
24332e2caf59SThomas Veerman * current value of the variable.
24342e2caf59SThomas Veerman * The ::= modifiers, actually assign a value to the variable.
24352e2caf59SThomas Veerman * Their main purpose is in supporting modifiers of .for loop
24362e2caf59SThomas Veerman * iterators and other obscure uses. They always expand to
24372e2caf59SThomas Veerman * nothing. In a target rule that would otherwise expand to an
24382e2caf59SThomas Veerman * empty line they can be preceded with @: to keep make happy.
24392e2caf59SThomas Veerman * Eg.
24402e2caf59SThomas Veerman *
24412e2caf59SThomas Veerman * foo: .USE
24422e2caf59SThomas Veerman * .for i in ${.TARGET} ${.TARGET:R}.gz
24432e2caf59SThomas Veerman * @: ${t::=$i}
24442e2caf59SThomas Veerman * @echo blah ${t:T}
24452e2caf59SThomas Veerman * .endfor
24462e2caf59SThomas Veerman *
24472e2caf59SThomas Veerman * ::=<str> Assigns <str> as the new value of variable.
24482e2caf59SThomas Veerman * ::?=<str> Assigns <str> as value of variable if
24492e2caf59SThomas Veerman * it was not already set.
24502e2caf59SThomas Veerman * ::+=<str> Appends <str> to variable.
24512e2caf59SThomas Veerman * ::!=<cmd> Assigns output of <cmd> as the new value of
24522e2caf59SThomas Veerman * variable.
24532e2caf59SThomas Veerman */
24542e2caf59SThomas Veerman
24552e2caf59SThomas Veerman /* we now have some modifiers with long names */
24562e2caf59SThomas Veerman #define STRMOD_MATCH(s, want, n) \
24572e2caf59SThomas Veerman (strncmp(s, want, n) == 0 && (s[n] == endc || s[n] == ':'))
24582e2caf59SThomas Veerman
24592e2caf59SThomas Veerman static char *
ApplyModifiers(char * nstr,const char * tstr,int startc,int endc,Var * v,GNode * ctxt,Boolean errnum,int * lengthPtr,void ** freePtr)24602e2caf59SThomas Veerman ApplyModifiers(char *nstr, const char *tstr,
24612e2caf59SThomas Veerman int startc, int endc,
24622e2caf59SThomas Veerman Var *v, GNode *ctxt, Boolean errnum,
24632e2caf59SThomas Veerman int *lengthPtr, void **freePtr)
24642e2caf59SThomas Veerman {
24652e2caf59SThomas Veerman const char *start;
24662e2caf59SThomas Veerman const char *cp; /* Secondary pointer into str (place marker
24672e2caf59SThomas Veerman * for tstr) */
24682e2caf59SThomas Veerman char *newStr; /* New value to return */
24692e2caf59SThomas Veerman char termc; /* Character which terminated scan */
24702e2caf59SThomas Veerman int cnt; /* Used to count brace pairs when variable in
24712e2caf59SThomas Veerman * in parens or braces */
24722e2caf59SThomas Veerman char delim;
24732e2caf59SThomas Veerman int modifier; /* that we are processing */
24742e2caf59SThomas Veerman Var_Parse_State parsestate; /* Flags passed to helper functions */
24752e2caf59SThomas Veerman
24762e2caf59SThomas Veerman delim = '\0';
24772e2caf59SThomas Veerman parsestate.oneBigWord = FALSE;
24782e2caf59SThomas Veerman parsestate.varSpace = ' '; /* word separator */
24792e2caf59SThomas Veerman
24802e2caf59SThomas Veerman start = cp = tstr;
24812e2caf59SThomas Veerman
24822e2caf59SThomas Veerman while (*tstr && *tstr != endc) {
24832e2caf59SThomas Veerman
24842e2caf59SThomas Veerman if (*tstr == '$') {
24852e2caf59SThomas Veerman /*
24862e2caf59SThomas Veerman * We may have some complex modifiers in a variable.
24872e2caf59SThomas Veerman */
24882e2caf59SThomas Veerman void *freeIt;
24892e2caf59SThomas Veerman char *rval;
24902e2caf59SThomas Veerman int rlen;
24912e2caf59SThomas Veerman int c;
24922e2caf59SThomas Veerman
24932e2caf59SThomas Veerman rval = Var_Parse(tstr, ctxt, errnum, &rlen, &freeIt);
24942e2caf59SThomas Veerman
24952e2caf59SThomas Veerman /*
24962e2caf59SThomas Veerman * If we have not parsed up to endc or ':',
24972e2caf59SThomas Veerman * we are not interested.
24982e2caf59SThomas Veerman */
24992e2caf59SThomas Veerman if (rval != NULL && *rval &&
25002e2caf59SThomas Veerman (c = tstr[rlen]) != '\0' &&
25012e2caf59SThomas Veerman c != ':' &&
25022e2caf59SThomas Veerman c != endc) {
25032e2caf59SThomas Veerman free(freeIt);
25042e2caf59SThomas Veerman goto apply_mods;
25052e2caf59SThomas Veerman }
25062e2caf59SThomas Veerman
25072e2caf59SThomas Veerman if (DEBUG(VAR)) {
25082e2caf59SThomas Veerman fprintf(debug_file, "Got '%s' from '%.*s'%.*s\n",
25092e2caf59SThomas Veerman rval, rlen, tstr, rlen, tstr + rlen);
25102e2caf59SThomas Veerman }
25112e2caf59SThomas Veerman
25122e2caf59SThomas Veerman tstr += rlen;
25132e2caf59SThomas Veerman
25142e2caf59SThomas Veerman if (rval != NULL && *rval) {
25152e2caf59SThomas Veerman int used;
25162e2caf59SThomas Veerman
25172e2caf59SThomas Veerman nstr = ApplyModifiers(nstr, rval,
25182e2caf59SThomas Veerman 0, 0,
25192e2caf59SThomas Veerman v, ctxt, errnum, &used, freePtr);
25202e2caf59SThomas Veerman if (nstr == var_Error
25212e2caf59SThomas Veerman || (nstr == varNoError && errnum == 0)
25222e2caf59SThomas Veerman || strlen(rval) != (size_t) used) {
25232e2caf59SThomas Veerman free(freeIt);
25242e2caf59SThomas Veerman goto out; /* error already reported */
25252e2caf59SThomas Veerman }
25262e2caf59SThomas Veerman }
25272e2caf59SThomas Veerman free(freeIt);
25282e2caf59SThomas Veerman if (*tstr == ':')
25292e2caf59SThomas Veerman tstr++;
25302e2caf59SThomas Veerman else if (!*tstr && endc) {
25312e2caf59SThomas Veerman Error("Unclosed variable specification after complex modifier (expecting '%c') for %s", endc, v->name);
25322e2caf59SThomas Veerman goto out;
25332e2caf59SThomas Veerman }
25342e2caf59SThomas Veerman continue;
25352e2caf59SThomas Veerman }
25362e2caf59SThomas Veerman apply_mods:
25372e2caf59SThomas Veerman if (DEBUG(VAR)) {
253884d9c625SLionel Sambuc fprintf(debug_file, "Applying[%s] :%c to \"%s\"\n", v->name,
253984d9c625SLionel Sambuc *tstr, nstr);
25402e2caf59SThomas Veerman }
25412e2caf59SThomas Veerman newStr = var_Error;
25422e2caf59SThomas Veerman switch ((modifier = *tstr)) {
25432e2caf59SThomas Veerman case ':':
25442e2caf59SThomas Veerman {
25452e2caf59SThomas Veerman if (tstr[1] == '=' ||
25462e2caf59SThomas Veerman (tstr[2] == '=' &&
25472e2caf59SThomas Veerman (tstr[1] == '!' || tstr[1] == '+' || tstr[1] == '?'))) {
25482e2caf59SThomas Veerman /*
25492e2caf59SThomas Veerman * "::=", "::!=", "::+=", or "::?="
25502e2caf59SThomas Veerman */
25512e2caf59SThomas Veerman GNode *v_ctxt; /* context where v belongs */
25522e2caf59SThomas Veerman const char *emsg;
25532e2caf59SThomas Veerman char *sv_name;
25542e2caf59SThomas Veerman VarPattern pattern;
25552e2caf59SThomas Veerman int how;
25562e2caf59SThomas Veerman
25572e2caf59SThomas Veerman if (v->name[0] == 0)
25582e2caf59SThomas Veerman goto bad_modifier;
25592e2caf59SThomas Veerman
25602e2caf59SThomas Veerman v_ctxt = ctxt;
25612e2caf59SThomas Veerman sv_name = NULL;
25622e2caf59SThomas Veerman ++tstr;
25632e2caf59SThomas Veerman if (v->flags & VAR_JUNK) {
25642e2caf59SThomas Veerman /*
25652e2caf59SThomas Veerman * We need to bmake_strdup() it incase
25662e2caf59SThomas Veerman * VarGetPattern() recurses.
25672e2caf59SThomas Veerman */
25682e2caf59SThomas Veerman sv_name = v->name;
25692e2caf59SThomas Veerman v->name = bmake_strdup(v->name);
25702e2caf59SThomas Veerman } else if (ctxt != VAR_GLOBAL) {
25712e2caf59SThomas Veerman Var *gv = VarFind(v->name, ctxt, 0);
25722e2caf59SThomas Veerman if (gv == NULL)
25732e2caf59SThomas Veerman v_ctxt = VAR_GLOBAL;
25742e2caf59SThomas Veerman else
25752e2caf59SThomas Veerman VarFreeEnv(gv, TRUE);
25762e2caf59SThomas Veerman }
25772e2caf59SThomas Veerman
25782e2caf59SThomas Veerman switch ((how = *tstr)) {
25792e2caf59SThomas Veerman case '+':
25802e2caf59SThomas Veerman case '?':
25812e2caf59SThomas Veerman case '!':
25822e2caf59SThomas Veerman cp = &tstr[2];
25832e2caf59SThomas Veerman break;
25842e2caf59SThomas Veerman default:
25852e2caf59SThomas Veerman cp = ++tstr;
25862e2caf59SThomas Veerman break;
25872e2caf59SThomas Veerman }
25882e2caf59SThomas Veerman delim = startc == PROPEN ? PRCLOSE : BRCLOSE;
25892e2caf59SThomas Veerman pattern.flags = 0;
25902e2caf59SThomas Veerman
25912e2caf59SThomas Veerman pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum,
25922e2caf59SThomas Veerman &cp, delim, NULL,
25932e2caf59SThomas Veerman &pattern.rightLen,
25942e2caf59SThomas Veerman NULL);
25952e2caf59SThomas Veerman if (v->flags & VAR_JUNK) {
25962e2caf59SThomas Veerman /* restore original name */
25972e2caf59SThomas Veerman free(v->name);
25982e2caf59SThomas Veerman v->name = sv_name;
25992e2caf59SThomas Veerman }
26002e2caf59SThomas Veerman if (pattern.rhs == NULL)
26012e2caf59SThomas Veerman goto cleanup;
26022e2caf59SThomas Veerman
26032e2caf59SThomas Veerman termc = *--cp;
26042e2caf59SThomas Veerman delim = '\0';
26052e2caf59SThomas Veerman
26062e2caf59SThomas Veerman switch (how) {
26072e2caf59SThomas Veerman case '+':
26082e2caf59SThomas Veerman Var_Append(v->name, pattern.rhs, v_ctxt);
26092e2caf59SThomas Veerman break;
26102e2caf59SThomas Veerman case '!':
26112e2caf59SThomas Veerman newStr = Cmd_Exec(pattern.rhs, &emsg);
26122e2caf59SThomas Veerman if (emsg)
26132e2caf59SThomas Veerman Error(emsg, nstr);
26142e2caf59SThomas Veerman else
26152e2caf59SThomas Veerman Var_Set(v->name, newStr, v_ctxt, 0);
26162e2caf59SThomas Veerman free(newStr);
26172e2caf59SThomas Veerman break;
26182e2caf59SThomas Veerman case '?':
26192e2caf59SThomas Veerman if ((v->flags & VAR_JUNK) == 0)
26202e2caf59SThomas Veerman break;
26212e2caf59SThomas Veerman /* FALLTHROUGH */
26222e2caf59SThomas Veerman default:
26232e2caf59SThomas Veerman Var_Set(v->name, pattern.rhs, v_ctxt, 0);
26242e2caf59SThomas Veerman break;
26252e2caf59SThomas Veerman }
26262e2caf59SThomas Veerman free(UNCONST(pattern.rhs));
2627*0a6a1f1dSLionel Sambuc newStr = varNoError;
26282e2caf59SThomas Veerman break;
26292e2caf59SThomas Veerman }
26302e2caf59SThomas Veerman goto default_case; /* "::<unrecognised>" */
26312e2caf59SThomas Veerman }
26322e2caf59SThomas Veerman case '@':
26332e2caf59SThomas Veerman {
26342e2caf59SThomas Veerman VarLoop_t loop;
26352e2caf59SThomas Veerman int flags = VAR_NOSUBST;
26362e2caf59SThomas Veerman
26372e2caf59SThomas Veerman cp = ++tstr;
26382e2caf59SThomas Veerman delim = '@';
26392e2caf59SThomas Veerman if ((loop.tvar = VarGetPattern(ctxt, &parsestate, errnum,
26402e2caf59SThomas Veerman &cp, delim,
26412e2caf59SThomas Veerman &flags, &loop.tvarLen,
26422e2caf59SThomas Veerman NULL)) == NULL)
26432e2caf59SThomas Veerman goto cleanup;
26442e2caf59SThomas Veerman
26452e2caf59SThomas Veerman if ((loop.str = VarGetPattern(ctxt, &parsestate, errnum,
26462e2caf59SThomas Veerman &cp, delim,
26472e2caf59SThomas Veerman &flags, &loop.strLen,
26482e2caf59SThomas Veerman NULL)) == NULL)
26492e2caf59SThomas Veerman goto cleanup;
26502e2caf59SThomas Veerman
26512e2caf59SThomas Veerman termc = *cp;
26522e2caf59SThomas Veerman delim = '\0';
26532e2caf59SThomas Veerman
26542e2caf59SThomas Veerman loop.errnum = errnum;
26552e2caf59SThomas Veerman loop.ctxt = ctxt;
26562e2caf59SThomas Veerman newStr = VarModify(ctxt, &parsestate, nstr, VarLoopExpand,
26572e2caf59SThomas Veerman &loop);
26582e2caf59SThomas Veerman free(loop.tvar);
26592e2caf59SThomas Veerman free(loop.str);
26602e2caf59SThomas Veerman break;
26612e2caf59SThomas Veerman }
26622e2caf59SThomas Veerman case 'D':
26632e2caf59SThomas Veerman case 'U':
26642e2caf59SThomas Veerman {
26652e2caf59SThomas Veerman Buffer buf; /* Buffer for patterns */
26662e2caf59SThomas Veerman int wantit; /* want data in buffer */
26672e2caf59SThomas Veerman
26682e2caf59SThomas Veerman /*
26692e2caf59SThomas Veerman * Pass through tstr looking for 1) escaped delimiters,
26702e2caf59SThomas Veerman * '$'s and backslashes (place the escaped character in
26712e2caf59SThomas Veerman * uninterpreted) and 2) unescaped $'s that aren't before
26722e2caf59SThomas Veerman * the delimiter (expand the variable substitution).
26732e2caf59SThomas Veerman * The result is left in the Buffer buf.
26742e2caf59SThomas Veerman */
26752e2caf59SThomas Veerman Buf_Init(&buf, 0);
26762e2caf59SThomas Veerman for (cp = tstr + 1;
26772e2caf59SThomas Veerman *cp != endc && *cp != ':' && *cp != '\0';
26782e2caf59SThomas Veerman cp++) {
26792e2caf59SThomas Veerman if ((*cp == '\\') &&
26802e2caf59SThomas Veerman ((cp[1] == ':') ||
26812e2caf59SThomas Veerman (cp[1] == '$') ||
26822e2caf59SThomas Veerman (cp[1] == endc) ||
26832e2caf59SThomas Veerman (cp[1] == '\\')))
26842e2caf59SThomas Veerman {
26852e2caf59SThomas Veerman Buf_AddByte(&buf, cp[1]);
26862e2caf59SThomas Veerman cp++;
26872e2caf59SThomas Veerman } else if (*cp == '$') {
26882e2caf59SThomas Veerman /*
26892e2caf59SThomas Veerman * If unescaped dollar sign, assume it's a
26902e2caf59SThomas Veerman * variable substitution and recurse.
26912e2caf59SThomas Veerman */
26922e2caf59SThomas Veerman char *cp2;
26932e2caf59SThomas Veerman int len;
26942e2caf59SThomas Veerman void *freeIt;
26952e2caf59SThomas Veerman
26962e2caf59SThomas Veerman cp2 = Var_Parse(cp, ctxt, errnum, &len, &freeIt);
26972e2caf59SThomas Veerman Buf_AddBytes(&buf, strlen(cp2), cp2);
26982e2caf59SThomas Veerman free(freeIt);
26992e2caf59SThomas Veerman cp += len - 1;
27002e2caf59SThomas Veerman } else {
27012e2caf59SThomas Veerman Buf_AddByte(&buf, *cp);
27022e2caf59SThomas Veerman }
27032e2caf59SThomas Veerman }
27042e2caf59SThomas Veerman
27052e2caf59SThomas Veerman termc = *cp;
27062e2caf59SThomas Veerman
27072e2caf59SThomas Veerman if (*tstr == 'U')
27082e2caf59SThomas Veerman wantit = ((v->flags & VAR_JUNK) != 0);
27092e2caf59SThomas Veerman else
27102e2caf59SThomas Veerman wantit = ((v->flags & VAR_JUNK) == 0);
27112e2caf59SThomas Veerman if ((v->flags & VAR_JUNK) != 0)
27122e2caf59SThomas Veerman v->flags |= VAR_KEEP;
27132e2caf59SThomas Veerman if (wantit) {
27142e2caf59SThomas Veerman newStr = Buf_Destroy(&buf, FALSE);
27152e2caf59SThomas Veerman } else {
27162e2caf59SThomas Veerman newStr = nstr;
27172e2caf59SThomas Veerman Buf_Destroy(&buf, TRUE);
27182e2caf59SThomas Veerman }
27192e2caf59SThomas Veerman break;
27202e2caf59SThomas Veerman }
27212e2caf59SThomas Veerman case 'L':
27222e2caf59SThomas Veerman {
27232e2caf59SThomas Veerman if ((v->flags & VAR_JUNK) != 0)
27242e2caf59SThomas Veerman v->flags |= VAR_KEEP;
27252e2caf59SThomas Veerman newStr = bmake_strdup(v->name);
27262e2caf59SThomas Veerman cp = ++tstr;
27272e2caf59SThomas Veerman termc = *tstr;
27282e2caf59SThomas Veerman break;
27292e2caf59SThomas Veerman }
27302e2caf59SThomas Veerman case 'P':
27312e2caf59SThomas Veerman {
27322e2caf59SThomas Veerman GNode *gn;
27332e2caf59SThomas Veerman
27342e2caf59SThomas Veerman if ((v->flags & VAR_JUNK) != 0)
27352e2caf59SThomas Veerman v->flags |= VAR_KEEP;
27362e2caf59SThomas Veerman gn = Targ_FindNode(v->name, TARG_NOCREATE);
27372e2caf59SThomas Veerman if (gn == NULL || gn->type & OP_NOPATH) {
27382e2caf59SThomas Veerman newStr = NULL;
27392e2caf59SThomas Veerman } else if (gn->path) {
27402e2caf59SThomas Veerman newStr = bmake_strdup(gn->path);
27412e2caf59SThomas Veerman } else {
27422e2caf59SThomas Veerman newStr = Dir_FindFile(v->name, Suff_FindPath(gn));
27432e2caf59SThomas Veerman }
27442e2caf59SThomas Veerman if (!newStr) {
27452e2caf59SThomas Veerman newStr = bmake_strdup(v->name);
27462e2caf59SThomas Veerman }
27472e2caf59SThomas Veerman cp = ++tstr;
27482e2caf59SThomas Veerman termc = *tstr;
27492e2caf59SThomas Veerman break;
27502e2caf59SThomas Veerman }
27512e2caf59SThomas Veerman case '!':
27522e2caf59SThomas Veerman {
27532e2caf59SThomas Veerman const char *emsg;
27542e2caf59SThomas Veerman VarPattern pattern;
27552e2caf59SThomas Veerman pattern.flags = 0;
27562e2caf59SThomas Veerman
27572e2caf59SThomas Veerman delim = '!';
27582e2caf59SThomas Veerman
27592e2caf59SThomas Veerman cp = ++tstr;
27602e2caf59SThomas Veerman if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum,
27612e2caf59SThomas Veerman &cp, delim,
27622e2caf59SThomas Veerman NULL, &pattern.rightLen,
27632e2caf59SThomas Veerman NULL)) == NULL)
27642e2caf59SThomas Veerman goto cleanup;
27652e2caf59SThomas Veerman newStr = Cmd_Exec(pattern.rhs, &emsg);
27662e2caf59SThomas Veerman free(UNCONST(pattern.rhs));
27672e2caf59SThomas Veerman if (emsg)
27682e2caf59SThomas Veerman Error(emsg, nstr);
27692e2caf59SThomas Veerman termc = *cp;
27702e2caf59SThomas Veerman delim = '\0';
27712e2caf59SThomas Veerman if (v->flags & VAR_JUNK) {
27722e2caf59SThomas Veerman v->flags |= VAR_KEEP;
27732e2caf59SThomas Veerman }
27742e2caf59SThomas Veerman break;
27752e2caf59SThomas Veerman }
27762e2caf59SThomas Veerman case '[':
27772e2caf59SThomas Veerman {
27782e2caf59SThomas Veerman /*
27792e2caf59SThomas Veerman * Look for the closing ']', recursively
27802e2caf59SThomas Veerman * expanding any embedded variables.
27812e2caf59SThomas Veerman *
27822e2caf59SThomas Veerman * estr is a pointer to the expanded result,
27832e2caf59SThomas Veerman * which we must free().
27842e2caf59SThomas Veerman */
27852e2caf59SThomas Veerman char *estr;
27862e2caf59SThomas Veerman
27872e2caf59SThomas Veerman cp = tstr+1; /* point to char after '[' */
27882e2caf59SThomas Veerman delim = ']'; /* look for closing ']' */
27892e2caf59SThomas Veerman estr = VarGetPattern(ctxt, &parsestate,
27902e2caf59SThomas Veerman errnum, &cp, delim,
27912e2caf59SThomas Veerman NULL, NULL, NULL);
27922e2caf59SThomas Veerman if (estr == NULL)
27932e2caf59SThomas Veerman goto cleanup; /* report missing ']' */
27942e2caf59SThomas Veerman /* now cp points just after the closing ']' */
27952e2caf59SThomas Veerman delim = '\0';
27962e2caf59SThomas Veerman if (cp[0] != ':' && cp[0] != endc) {
27972e2caf59SThomas Veerman /* Found junk after ']' */
27982e2caf59SThomas Veerman free(estr);
27992e2caf59SThomas Veerman goto bad_modifier;
28002e2caf59SThomas Veerman }
28012e2caf59SThomas Veerman if (estr[0] == '\0') {
28022e2caf59SThomas Veerman /* Found empty square brackets in ":[]". */
28032e2caf59SThomas Veerman free(estr);
28042e2caf59SThomas Veerman goto bad_modifier;
28052e2caf59SThomas Veerman } else if (estr[0] == '#' && estr[1] == '\0') {
28062e2caf59SThomas Veerman /* Found ":[#]" */
28072e2caf59SThomas Veerman
28082e2caf59SThomas Veerman /*
28092e2caf59SThomas Veerman * We will need enough space for the decimal
28102e2caf59SThomas Veerman * representation of an int. We calculate the
28112e2caf59SThomas Veerman * space needed for the octal representation,
28122e2caf59SThomas Veerman * and add enough slop to cope with a '-' sign
28132e2caf59SThomas Veerman * (which should never be needed) and a '\0'
28142e2caf59SThomas Veerman * string terminator.
28152e2caf59SThomas Veerman */
28162e2caf59SThomas Veerman int newStrSize =
28172e2caf59SThomas Veerman (sizeof(int) * CHAR_BIT + 2) / 3 + 2;
28182e2caf59SThomas Veerman
28192e2caf59SThomas Veerman newStr = bmake_malloc(newStrSize);
28202e2caf59SThomas Veerman if (parsestate.oneBigWord) {
28212e2caf59SThomas Veerman strncpy(newStr, "1", newStrSize);
28222e2caf59SThomas Veerman } else {
28232e2caf59SThomas Veerman /* XXX: brk_string() is a rather expensive
28242e2caf59SThomas Veerman * way of counting words. */
28252e2caf59SThomas Veerman char **av;
28262e2caf59SThomas Veerman char *as;
28272e2caf59SThomas Veerman int ac;
28282e2caf59SThomas Veerman
28292e2caf59SThomas Veerman av = brk_string(nstr, &ac, FALSE, &as);
28302e2caf59SThomas Veerman snprintf(newStr, newStrSize, "%d", ac);
28312e2caf59SThomas Veerman free(as);
28322e2caf59SThomas Veerman free(av);
28332e2caf59SThomas Veerman }
28342e2caf59SThomas Veerman termc = *cp;
28352e2caf59SThomas Veerman free(estr);
28362e2caf59SThomas Veerman break;
28372e2caf59SThomas Veerman } else if (estr[0] == '*' && estr[1] == '\0') {
28382e2caf59SThomas Veerman /* Found ":[*]" */
28392e2caf59SThomas Veerman parsestate.oneBigWord = TRUE;
28402e2caf59SThomas Veerman newStr = nstr;
28412e2caf59SThomas Veerman termc = *cp;
28422e2caf59SThomas Veerman free(estr);
28432e2caf59SThomas Veerman break;
28442e2caf59SThomas Veerman } else if (estr[0] == '@' && estr[1] == '\0') {
28452e2caf59SThomas Veerman /* Found ":[@]" */
28462e2caf59SThomas Veerman parsestate.oneBigWord = FALSE;
28472e2caf59SThomas Veerman newStr = nstr;
28482e2caf59SThomas Veerman termc = *cp;
28492e2caf59SThomas Veerman free(estr);
28502e2caf59SThomas Veerman break;
28512e2caf59SThomas Veerman } else {
28522e2caf59SThomas Veerman /*
28532e2caf59SThomas Veerman * We expect estr to contain a single
28542e2caf59SThomas Veerman * integer for :[N], or two integers
28552e2caf59SThomas Veerman * separated by ".." for :[start..end].
28562e2caf59SThomas Veerman */
28572e2caf59SThomas Veerman char *ep;
28582e2caf59SThomas Veerman
28592e2caf59SThomas Veerman VarSelectWords_t seldata = { 0, 0 };
28602e2caf59SThomas Veerman
28612e2caf59SThomas Veerman seldata.start = strtol(estr, &ep, 0);
28622e2caf59SThomas Veerman if (ep == estr) {
28632e2caf59SThomas Veerman /* Found junk instead of a number */
28642e2caf59SThomas Veerman free(estr);
28652e2caf59SThomas Veerman goto bad_modifier;
28662e2caf59SThomas Veerman } else if (ep[0] == '\0') {
28672e2caf59SThomas Veerman /* Found only one integer in :[N] */
28682e2caf59SThomas Veerman seldata.end = seldata.start;
28692e2caf59SThomas Veerman } else if (ep[0] == '.' && ep[1] == '.' &&
28702e2caf59SThomas Veerman ep[2] != '\0') {
28712e2caf59SThomas Veerman /* Expecting another integer after ".." */
28722e2caf59SThomas Veerman ep += 2;
28732e2caf59SThomas Veerman seldata.end = strtol(ep, &ep, 0);
28742e2caf59SThomas Veerman if (ep[0] != '\0') {
28752e2caf59SThomas Veerman /* Found junk after ".." */
28762e2caf59SThomas Veerman free(estr);
28772e2caf59SThomas Veerman goto bad_modifier;
28782e2caf59SThomas Veerman }
28792e2caf59SThomas Veerman } else {
28802e2caf59SThomas Veerman /* Found junk instead of ".." */
28812e2caf59SThomas Veerman free(estr);
28822e2caf59SThomas Veerman goto bad_modifier;
28832e2caf59SThomas Veerman }
28842e2caf59SThomas Veerman /*
28852e2caf59SThomas Veerman * Now seldata is properly filled in,
28862e2caf59SThomas Veerman * but we still have to check for 0 as
28872e2caf59SThomas Veerman * a special case.
28882e2caf59SThomas Veerman */
28892e2caf59SThomas Veerman if (seldata.start == 0 && seldata.end == 0) {
28902e2caf59SThomas Veerman /* ":[0]" or perhaps ":[0..0]" */
28912e2caf59SThomas Veerman parsestate.oneBigWord = TRUE;
28922e2caf59SThomas Veerman newStr = nstr;
28932e2caf59SThomas Veerman termc = *cp;
28942e2caf59SThomas Veerman free(estr);
28952e2caf59SThomas Veerman break;
28962e2caf59SThomas Veerman } else if (seldata.start == 0 ||
28972e2caf59SThomas Veerman seldata.end == 0) {
28982e2caf59SThomas Veerman /* ":[0..N]" or ":[N..0]" */
28992e2caf59SThomas Veerman free(estr);
29002e2caf59SThomas Veerman goto bad_modifier;
29012e2caf59SThomas Veerman }
29022e2caf59SThomas Veerman /*
29032e2caf59SThomas Veerman * Normal case: select the words
29042e2caf59SThomas Veerman * described by seldata.
29052e2caf59SThomas Veerman */
29062e2caf59SThomas Veerman newStr = VarSelectWords(ctxt, &parsestate,
29072e2caf59SThomas Veerman nstr, &seldata);
29082e2caf59SThomas Veerman
29092e2caf59SThomas Veerman termc = *cp;
29102e2caf59SThomas Veerman free(estr);
29112e2caf59SThomas Veerman break;
29122e2caf59SThomas Veerman }
29132e2caf59SThomas Veerman
29142e2caf59SThomas Veerman }
29152e2caf59SThomas Veerman case 'g':
29162e2caf59SThomas Veerman cp = tstr + 1; /* make sure it is set */
29172e2caf59SThomas Veerman if (STRMOD_MATCH(tstr, "gmtime", 6)) {
29182e2caf59SThomas Veerman newStr = VarStrftime(nstr, 1);
29192e2caf59SThomas Veerman cp = tstr + 6;
29202e2caf59SThomas Veerman termc = *cp;
29212e2caf59SThomas Veerman } else {
29222e2caf59SThomas Veerman goto default_case;
29232e2caf59SThomas Veerman }
29242e2caf59SThomas Veerman break;
29252e2caf59SThomas Veerman case 'h':
29262e2caf59SThomas Veerman cp = tstr + 1; /* make sure it is set */
29272e2caf59SThomas Veerman if (STRMOD_MATCH(tstr, "hash", 4)) {
29282e2caf59SThomas Veerman newStr = VarHash(nstr);
29292e2caf59SThomas Veerman cp = tstr + 4;
29302e2caf59SThomas Veerman termc = *cp;
29312e2caf59SThomas Veerman } else {
29322e2caf59SThomas Veerman goto default_case;
29332e2caf59SThomas Veerman }
29342e2caf59SThomas Veerman break;
29352e2caf59SThomas Veerman case 'l':
29362e2caf59SThomas Veerman cp = tstr + 1; /* make sure it is set */
29372e2caf59SThomas Veerman if (STRMOD_MATCH(tstr, "localtime", 9)) {
29382e2caf59SThomas Veerman newStr = VarStrftime(nstr, 0);
29392e2caf59SThomas Veerman cp = tstr + 9;
29402e2caf59SThomas Veerman termc = *cp;
29412e2caf59SThomas Veerman } else {
29422e2caf59SThomas Veerman goto default_case;
29432e2caf59SThomas Veerman }
29442e2caf59SThomas Veerman break;
29452e2caf59SThomas Veerman case 't':
29462e2caf59SThomas Veerman {
29472e2caf59SThomas Veerman cp = tstr + 1; /* make sure it is set */
29482e2caf59SThomas Veerman if (tstr[1] != endc && tstr[1] != ':') {
29492e2caf59SThomas Veerman if (tstr[1] == 's') {
29502e2caf59SThomas Veerman /*
29512e2caf59SThomas Veerman * Use the char (if any) at tstr[2]
29522e2caf59SThomas Veerman * as the word separator.
29532e2caf59SThomas Veerman */
29542e2caf59SThomas Veerman VarPattern pattern;
29552e2caf59SThomas Veerman
29562e2caf59SThomas Veerman if (tstr[2] != endc &&
29572e2caf59SThomas Veerman (tstr[3] == endc || tstr[3] == ':')) {
29582e2caf59SThomas Veerman /* ":ts<unrecognised><endc>" or
29592e2caf59SThomas Veerman * ":ts<unrecognised>:" */
29602e2caf59SThomas Veerman parsestate.varSpace = tstr[2];
29612e2caf59SThomas Veerman cp = tstr + 3;
29622e2caf59SThomas Veerman } else if (tstr[2] == endc || tstr[2] == ':') {
29632e2caf59SThomas Veerman /* ":ts<endc>" or ":ts:" */
29642e2caf59SThomas Veerman parsestate.varSpace = 0; /* no separator */
29652e2caf59SThomas Veerman cp = tstr + 2;
29662e2caf59SThomas Veerman } else if (tstr[2] == '\\') {
29672e2caf59SThomas Veerman switch (tstr[3]) {
29682e2caf59SThomas Veerman case 'n':
29692e2caf59SThomas Veerman parsestate.varSpace = '\n';
29702e2caf59SThomas Veerman cp = tstr + 4;
29712e2caf59SThomas Veerman break;
29722e2caf59SThomas Veerman case 't':
29732e2caf59SThomas Veerman parsestate.varSpace = '\t';
29742e2caf59SThomas Veerman cp = tstr + 4;
29752e2caf59SThomas Veerman break;
29762e2caf59SThomas Veerman default:
29772e2caf59SThomas Veerman if (isdigit((unsigned char)tstr[3])) {
29782e2caf59SThomas Veerman char *ep;
29792e2caf59SThomas Veerman
29802e2caf59SThomas Veerman parsestate.varSpace =
29812e2caf59SThomas Veerman strtoul(&tstr[3], &ep, 0);
29822e2caf59SThomas Veerman if (*ep != ':' && *ep != endc)
29832e2caf59SThomas Veerman goto bad_modifier;
29842e2caf59SThomas Veerman cp = ep;
29852e2caf59SThomas Veerman } else {
29862e2caf59SThomas Veerman /*
29872e2caf59SThomas Veerman * ":ts<backslash><unrecognised>".
29882e2caf59SThomas Veerman */
29892e2caf59SThomas Veerman goto bad_modifier;
29902e2caf59SThomas Veerman }
29912e2caf59SThomas Veerman break;
29922e2caf59SThomas Veerman }
29932e2caf59SThomas Veerman } else {
29942e2caf59SThomas Veerman /*
29952e2caf59SThomas Veerman * Found ":ts<unrecognised><unrecognised>".
29962e2caf59SThomas Veerman */
29972e2caf59SThomas Veerman goto bad_modifier;
29982e2caf59SThomas Veerman }
29992e2caf59SThomas Veerman
30002e2caf59SThomas Veerman termc = *cp;
30012e2caf59SThomas Veerman
30022e2caf59SThomas Veerman /*
30032e2caf59SThomas Veerman * We cannot be certain that VarModify
30042e2caf59SThomas Veerman * will be used - even if there is a
30052e2caf59SThomas Veerman * subsequent modifier, so do a no-op
30062e2caf59SThomas Veerman * VarSubstitute now to for str to be
30072e2caf59SThomas Veerman * re-expanded without the spaces.
30082e2caf59SThomas Veerman */
30092e2caf59SThomas Veerman pattern.flags = VAR_SUB_ONE;
30102e2caf59SThomas Veerman pattern.lhs = pattern.rhs = "\032";
30112e2caf59SThomas Veerman pattern.leftLen = pattern.rightLen = 1;
30122e2caf59SThomas Veerman
30132e2caf59SThomas Veerman newStr = VarModify(ctxt, &parsestate, nstr,
30142e2caf59SThomas Veerman VarSubstitute,
30152e2caf59SThomas Veerman &pattern);
30162e2caf59SThomas Veerman } else if (tstr[2] == endc || tstr[2] == ':') {
30172e2caf59SThomas Veerman /*
30182e2caf59SThomas Veerman * Check for two-character options:
30192e2caf59SThomas Veerman * ":tu", ":tl"
30202e2caf59SThomas Veerman */
30212e2caf59SThomas Veerman if (tstr[1] == 'A') { /* absolute path */
30222e2caf59SThomas Veerman newStr = VarModify(ctxt, &parsestate, nstr,
30232e2caf59SThomas Veerman VarRealpath, NULL);
30242e2caf59SThomas Veerman cp = tstr + 2;
30252e2caf59SThomas Veerman termc = *cp;
302684d9c625SLionel Sambuc } else if (tstr[1] == 'u') {
302784d9c625SLionel Sambuc char *dp = bmake_strdup(nstr);
302884d9c625SLionel Sambuc for (newStr = dp; *dp; dp++)
302984d9c625SLionel Sambuc *dp = toupper((unsigned char)*dp);
303084d9c625SLionel Sambuc cp = tstr + 2;
303184d9c625SLionel Sambuc termc = *cp;
303284d9c625SLionel Sambuc } else if (tstr[1] == 'l') {
303384d9c625SLionel Sambuc char *dp = bmake_strdup(nstr);
303484d9c625SLionel Sambuc for (newStr = dp; *dp; dp++)
303584d9c625SLionel Sambuc *dp = tolower((unsigned char)*dp);
30362e2caf59SThomas Veerman cp = tstr + 2;
30372e2caf59SThomas Veerman termc = *cp;
30382e2caf59SThomas Veerman } else if (tstr[1] == 'W' || tstr[1] == 'w') {
30392e2caf59SThomas Veerman parsestate.oneBigWord = (tstr[1] == 'W');
30402e2caf59SThomas Veerman newStr = nstr;
30412e2caf59SThomas Veerman cp = tstr + 2;
30422e2caf59SThomas Veerman termc = *cp;
30432e2caf59SThomas Veerman } else {
30442e2caf59SThomas Veerman /* Found ":t<unrecognised>:" or
30452e2caf59SThomas Veerman * ":t<unrecognised><endc>". */
30462e2caf59SThomas Veerman goto bad_modifier;
30472e2caf59SThomas Veerman }
30482e2caf59SThomas Veerman } else {
30492e2caf59SThomas Veerman /*
30502e2caf59SThomas Veerman * Found ":t<unrecognised><unrecognised>".
30512e2caf59SThomas Veerman */
30522e2caf59SThomas Veerman goto bad_modifier;
30532e2caf59SThomas Veerman }
30542e2caf59SThomas Veerman } else {
30552e2caf59SThomas Veerman /*
30562e2caf59SThomas Veerman * Found ":t<endc>" or ":t:".
30572e2caf59SThomas Veerman */
30582e2caf59SThomas Veerman goto bad_modifier;
30592e2caf59SThomas Veerman }
30602e2caf59SThomas Veerman break;
30612e2caf59SThomas Veerman }
30622e2caf59SThomas Veerman case 'N':
30632e2caf59SThomas Veerman case 'M':
30642e2caf59SThomas Veerman {
30652e2caf59SThomas Veerman char *pattern;
30662e2caf59SThomas Veerman const char *endpat; /* points just after end of pattern */
30672e2caf59SThomas Veerman char *cp2;
30682e2caf59SThomas Veerman Boolean copy; /* pattern should be, or has been, copied */
30692e2caf59SThomas Veerman Boolean needSubst;
30702e2caf59SThomas Veerman int nest;
30712e2caf59SThomas Veerman
30722e2caf59SThomas Veerman copy = FALSE;
30732e2caf59SThomas Veerman needSubst = FALSE;
30742e2caf59SThomas Veerman nest = 1;
30752e2caf59SThomas Veerman /*
30762e2caf59SThomas Veerman * In the loop below, ignore ':' unless we are at
30772e2caf59SThomas Veerman * (or back to) the original brace level.
30782e2caf59SThomas Veerman * XXX This will likely not work right if $() and ${}
30792e2caf59SThomas Veerman * are intermixed.
30802e2caf59SThomas Veerman */
30812e2caf59SThomas Veerman for (cp = tstr + 1;
30822e2caf59SThomas Veerman *cp != '\0' && !(*cp == ':' && nest == 1);
30832e2caf59SThomas Veerman cp++)
30842e2caf59SThomas Veerman {
30852e2caf59SThomas Veerman if (*cp == '\\' &&
30862e2caf59SThomas Veerman (cp[1] == ':' ||
30872e2caf59SThomas Veerman cp[1] == endc || cp[1] == startc)) {
30882e2caf59SThomas Veerman if (!needSubst) {
30892e2caf59SThomas Veerman copy = TRUE;
30902e2caf59SThomas Veerman }
30912e2caf59SThomas Veerman cp++;
30922e2caf59SThomas Veerman continue;
30932e2caf59SThomas Veerman }
30942e2caf59SThomas Veerman if (*cp == '$') {
30952e2caf59SThomas Veerman needSubst = TRUE;
30962e2caf59SThomas Veerman }
30972e2caf59SThomas Veerman if (*cp == '(' || *cp == '{')
30982e2caf59SThomas Veerman ++nest;
30992e2caf59SThomas Veerman if (*cp == ')' || *cp == '}') {
31002e2caf59SThomas Veerman --nest;
31012e2caf59SThomas Veerman if (nest == 0)
31022e2caf59SThomas Veerman break;
31032e2caf59SThomas Veerman }
31042e2caf59SThomas Veerman }
31052e2caf59SThomas Veerman termc = *cp;
31062e2caf59SThomas Veerman endpat = cp;
31072e2caf59SThomas Veerman if (copy) {
31082e2caf59SThomas Veerman /*
31092e2caf59SThomas Veerman * Need to compress the \:'s out of the pattern, so
31102e2caf59SThomas Veerman * allocate enough room to hold the uncompressed
31112e2caf59SThomas Veerman * pattern (note that cp started at tstr+1, so
31122e2caf59SThomas Veerman * cp - tstr takes the null byte into account) and
31132e2caf59SThomas Veerman * compress the pattern into the space.
31142e2caf59SThomas Veerman */
31152e2caf59SThomas Veerman pattern = bmake_malloc(cp - tstr);
31162e2caf59SThomas Veerman for (cp2 = pattern, cp = tstr + 1;
31172e2caf59SThomas Veerman cp < endpat;
31182e2caf59SThomas Veerman cp++, cp2++)
31192e2caf59SThomas Veerman {
31202e2caf59SThomas Veerman if ((*cp == '\\') && (cp+1 < endpat) &&
31212e2caf59SThomas Veerman (cp[1] == ':' || cp[1] == endc)) {
31222e2caf59SThomas Veerman cp++;
31232e2caf59SThomas Veerman }
31242e2caf59SThomas Veerman *cp2 = *cp;
31252e2caf59SThomas Veerman }
31262e2caf59SThomas Veerman *cp2 = '\0';
31272e2caf59SThomas Veerman endpat = cp2;
31282e2caf59SThomas Veerman } else {
31292e2caf59SThomas Veerman /*
31302e2caf59SThomas Veerman * Either Var_Subst or VarModify will need a
31312e2caf59SThomas Veerman * nul-terminated string soon, so construct one now.
31322e2caf59SThomas Veerman */
31332e2caf59SThomas Veerman pattern = bmake_strndup(tstr+1, endpat - (tstr + 1));
31342e2caf59SThomas Veerman }
31352e2caf59SThomas Veerman if (needSubst) {
31362e2caf59SThomas Veerman /*
31372e2caf59SThomas Veerman * pattern contains embedded '$', so use Var_Subst to
31382e2caf59SThomas Veerman * expand it.
31392e2caf59SThomas Veerman */
31402e2caf59SThomas Veerman cp2 = pattern;
31412e2caf59SThomas Veerman pattern = Var_Subst(NULL, cp2, ctxt, errnum);
31422e2caf59SThomas Veerman free(cp2);
31432e2caf59SThomas Veerman }
31442e2caf59SThomas Veerman if (DEBUG(VAR))
314584d9c625SLionel Sambuc fprintf(debug_file, "Pattern[%s] for [%s] is [%s]\n",
314684d9c625SLionel Sambuc v->name, nstr, pattern);
31472e2caf59SThomas Veerman if (*tstr == 'M') {
31482e2caf59SThomas Veerman newStr = VarModify(ctxt, &parsestate, nstr, VarMatch,
31492e2caf59SThomas Veerman pattern);
31502e2caf59SThomas Veerman } else {
31512e2caf59SThomas Veerman newStr = VarModify(ctxt, &parsestate, nstr, VarNoMatch,
31522e2caf59SThomas Veerman pattern);
31532e2caf59SThomas Veerman }
31542e2caf59SThomas Veerman free(pattern);
31552e2caf59SThomas Veerman break;
31562e2caf59SThomas Veerman }
31572e2caf59SThomas Veerman case 'S':
31582e2caf59SThomas Veerman {
31592e2caf59SThomas Veerman VarPattern pattern;
31602e2caf59SThomas Veerman Var_Parse_State tmpparsestate;
31612e2caf59SThomas Veerman
31622e2caf59SThomas Veerman pattern.flags = 0;
31632e2caf59SThomas Veerman tmpparsestate = parsestate;
31642e2caf59SThomas Veerman delim = tstr[1];
31652e2caf59SThomas Veerman tstr += 2;
31662e2caf59SThomas Veerman
31672e2caf59SThomas Veerman /*
31682e2caf59SThomas Veerman * If pattern begins with '^', it is anchored to the
31692e2caf59SThomas Veerman * start of the word -- skip over it and flag pattern.
31702e2caf59SThomas Veerman */
31712e2caf59SThomas Veerman if (*tstr == '^') {
31722e2caf59SThomas Veerman pattern.flags |= VAR_MATCH_START;
31732e2caf59SThomas Veerman tstr += 1;
31742e2caf59SThomas Veerman }
31752e2caf59SThomas Veerman
31762e2caf59SThomas Veerman cp = tstr;
31772e2caf59SThomas Veerman if ((pattern.lhs = VarGetPattern(ctxt, &parsestate, errnum,
31782e2caf59SThomas Veerman &cp, delim,
31792e2caf59SThomas Veerman &pattern.flags,
31802e2caf59SThomas Veerman &pattern.leftLen,
31812e2caf59SThomas Veerman NULL)) == NULL)
31822e2caf59SThomas Veerman goto cleanup;
31832e2caf59SThomas Veerman
31842e2caf59SThomas Veerman if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum,
31852e2caf59SThomas Veerman &cp, delim, NULL,
31862e2caf59SThomas Veerman &pattern.rightLen,
31872e2caf59SThomas Veerman &pattern)) == NULL)
31882e2caf59SThomas Veerman goto cleanup;
31892e2caf59SThomas Veerman
31902e2caf59SThomas Veerman /*
31912e2caf59SThomas Veerman * Check for global substitution. If 'g' after the final
31922e2caf59SThomas Veerman * delimiter, substitution is global and is marked that
31932e2caf59SThomas Veerman * way.
31942e2caf59SThomas Veerman */
31952e2caf59SThomas Veerman for (;; cp++) {
31962e2caf59SThomas Veerman switch (*cp) {
31972e2caf59SThomas Veerman case 'g':
31982e2caf59SThomas Veerman pattern.flags |= VAR_SUB_GLOBAL;
31992e2caf59SThomas Veerman continue;
32002e2caf59SThomas Veerman case '1':
32012e2caf59SThomas Veerman pattern.flags |= VAR_SUB_ONE;
32022e2caf59SThomas Veerman continue;
32032e2caf59SThomas Veerman case 'W':
32042e2caf59SThomas Veerman tmpparsestate.oneBigWord = TRUE;
32052e2caf59SThomas Veerman continue;
32062e2caf59SThomas Veerman }
32072e2caf59SThomas Veerman break;
32082e2caf59SThomas Veerman }
32092e2caf59SThomas Veerman
32102e2caf59SThomas Veerman termc = *cp;
32112e2caf59SThomas Veerman newStr = VarModify(ctxt, &tmpparsestate, nstr,
32122e2caf59SThomas Veerman VarSubstitute,
32132e2caf59SThomas Veerman &pattern);
32142e2caf59SThomas Veerman
32152e2caf59SThomas Veerman /*
32162e2caf59SThomas Veerman * Free the two strings.
32172e2caf59SThomas Veerman */
32182e2caf59SThomas Veerman free(UNCONST(pattern.lhs));
32192e2caf59SThomas Veerman free(UNCONST(pattern.rhs));
32202e2caf59SThomas Veerman delim = '\0';
32212e2caf59SThomas Veerman break;
32222e2caf59SThomas Veerman }
32232e2caf59SThomas Veerman case '?':
32242e2caf59SThomas Veerman {
32252e2caf59SThomas Veerman VarPattern pattern;
32262e2caf59SThomas Veerman Boolean value;
32272e2caf59SThomas Veerman
32282e2caf59SThomas Veerman /* find ':', and then substitute accordingly */
32292e2caf59SThomas Veerman
32302e2caf59SThomas Veerman pattern.flags = 0;
32312e2caf59SThomas Veerman
32322e2caf59SThomas Veerman cp = ++tstr;
32332e2caf59SThomas Veerman delim = ':';
32342e2caf59SThomas Veerman if ((pattern.lhs = VarGetPattern(ctxt, &parsestate, errnum,
32352e2caf59SThomas Veerman &cp, delim, NULL,
32362e2caf59SThomas Veerman &pattern.leftLen,
32372e2caf59SThomas Veerman NULL)) == NULL)
32382e2caf59SThomas Veerman goto cleanup;
32392e2caf59SThomas Veerman
32402e2caf59SThomas Veerman /* BROPEN or PROPEN */
32412e2caf59SThomas Veerman delim = endc;
32422e2caf59SThomas Veerman if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum,
32432e2caf59SThomas Veerman &cp, delim, NULL,
32442e2caf59SThomas Veerman &pattern.rightLen,
32452e2caf59SThomas Veerman NULL)) == NULL)
32462e2caf59SThomas Veerman goto cleanup;
32472e2caf59SThomas Veerman
32482e2caf59SThomas Veerman termc = *--cp;
32492e2caf59SThomas Veerman delim = '\0';
3250*0a6a1f1dSLionel Sambuc if (Cond_EvalExpression(NULL, v->name, &value, 0, FALSE)
32512e2caf59SThomas Veerman == COND_INVALID) {
32522e2caf59SThomas Veerman Error("Bad conditional expression `%s' in %s?%s:%s",
32532e2caf59SThomas Veerman v->name, v->name, pattern.lhs, pattern.rhs);
32542e2caf59SThomas Veerman goto cleanup;
32552e2caf59SThomas Veerman }
32562e2caf59SThomas Veerman
32572e2caf59SThomas Veerman if (value) {
32582e2caf59SThomas Veerman newStr = UNCONST(pattern.lhs);
32592e2caf59SThomas Veerman free(UNCONST(pattern.rhs));
32602e2caf59SThomas Veerman } else {
32612e2caf59SThomas Veerman newStr = UNCONST(pattern.rhs);
32622e2caf59SThomas Veerman free(UNCONST(pattern.lhs));
32632e2caf59SThomas Veerman }
32642e2caf59SThomas Veerman if (v->flags & VAR_JUNK) {
32652e2caf59SThomas Veerman v->flags |= VAR_KEEP;
32662e2caf59SThomas Veerman }
32672e2caf59SThomas Veerman break;
32682e2caf59SThomas Veerman }
32692e2caf59SThomas Veerman #ifndef NO_REGEX
32702e2caf59SThomas Veerman case 'C':
32712e2caf59SThomas Veerman {
32722e2caf59SThomas Veerman VarREPattern pattern;
32732e2caf59SThomas Veerman char *re;
32742e2caf59SThomas Veerman int error;
32752e2caf59SThomas Veerman Var_Parse_State tmpparsestate;
32762e2caf59SThomas Veerman
32772e2caf59SThomas Veerman pattern.flags = 0;
32782e2caf59SThomas Veerman tmpparsestate = parsestate;
32792e2caf59SThomas Veerman delim = tstr[1];
32802e2caf59SThomas Veerman tstr += 2;
32812e2caf59SThomas Veerman
32822e2caf59SThomas Veerman cp = tstr;
32832e2caf59SThomas Veerman
32842e2caf59SThomas Veerman if ((re = VarGetPattern(ctxt, &parsestate, errnum, &cp, delim,
32852e2caf59SThomas Veerman NULL, NULL, NULL)) == NULL)
32862e2caf59SThomas Veerman goto cleanup;
32872e2caf59SThomas Veerman
32882e2caf59SThomas Veerman if ((pattern.replace = VarGetPattern(ctxt, &parsestate,
32892e2caf59SThomas Veerman errnum, &cp, delim, NULL,
32902e2caf59SThomas Veerman NULL, NULL)) == NULL){
32912e2caf59SThomas Veerman free(re);
32922e2caf59SThomas Veerman goto cleanup;
32932e2caf59SThomas Veerman }
32942e2caf59SThomas Veerman
32952e2caf59SThomas Veerman for (;; cp++) {
32962e2caf59SThomas Veerman switch (*cp) {
32972e2caf59SThomas Veerman case 'g':
32982e2caf59SThomas Veerman pattern.flags |= VAR_SUB_GLOBAL;
32992e2caf59SThomas Veerman continue;
33002e2caf59SThomas Veerman case '1':
33012e2caf59SThomas Veerman pattern.flags |= VAR_SUB_ONE;
33022e2caf59SThomas Veerman continue;
33032e2caf59SThomas Veerman case 'W':
33042e2caf59SThomas Veerman tmpparsestate.oneBigWord = TRUE;
33052e2caf59SThomas Veerman continue;
33062e2caf59SThomas Veerman }
33072e2caf59SThomas Veerman break;
33082e2caf59SThomas Veerman }
33092e2caf59SThomas Veerman
33102e2caf59SThomas Veerman termc = *cp;
33112e2caf59SThomas Veerman
33122e2caf59SThomas Veerman error = regcomp(&pattern.re, re, REG_EXTENDED);
33132e2caf59SThomas Veerman free(re);
33142e2caf59SThomas Veerman if (error) {
33152e2caf59SThomas Veerman *lengthPtr = cp - start + 1;
33162e2caf59SThomas Veerman VarREError(error, &pattern.re, "RE substitution error");
33172e2caf59SThomas Veerman free(pattern.replace);
33182e2caf59SThomas Veerman goto cleanup;
33192e2caf59SThomas Veerman }
33202e2caf59SThomas Veerman
33212e2caf59SThomas Veerman pattern.nsub = pattern.re.re_nsub + 1;
33222e2caf59SThomas Veerman if (pattern.nsub < 1)
33232e2caf59SThomas Veerman pattern.nsub = 1;
33242e2caf59SThomas Veerman if (pattern.nsub > 10)
33252e2caf59SThomas Veerman pattern.nsub = 10;
33262e2caf59SThomas Veerman pattern.matches = bmake_malloc(pattern.nsub *
33272e2caf59SThomas Veerman sizeof(regmatch_t));
33282e2caf59SThomas Veerman newStr = VarModify(ctxt, &tmpparsestate, nstr,
33292e2caf59SThomas Veerman VarRESubstitute,
33302e2caf59SThomas Veerman &pattern);
33312e2caf59SThomas Veerman regfree(&pattern.re);
33322e2caf59SThomas Veerman free(pattern.replace);
33332e2caf59SThomas Veerman free(pattern.matches);
33342e2caf59SThomas Veerman delim = '\0';
33352e2caf59SThomas Veerman break;
33362e2caf59SThomas Veerman }
33372e2caf59SThomas Veerman #endif
33382e2caf59SThomas Veerman case 'Q':
33392e2caf59SThomas Veerman if (tstr[1] == endc || tstr[1] == ':') {
33402e2caf59SThomas Veerman newStr = VarQuote(nstr);
33412e2caf59SThomas Veerman cp = tstr + 1;
33422e2caf59SThomas Veerman termc = *cp;
33432e2caf59SThomas Veerman break;
33442e2caf59SThomas Veerman }
33452e2caf59SThomas Veerman goto default_case;
33462e2caf59SThomas Veerman case 'T':
33472e2caf59SThomas Veerman if (tstr[1] == endc || tstr[1] == ':') {
33482e2caf59SThomas Veerman newStr = VarModify(ctxt, &parsestate, nstr, VarTail,
33492e2caf59SThomas Veerman NULL);
33502e2caf59SThomas Veerman cp = tstr + 1;
33512e2caf59SThomas Veerman termc = *cp;
33522e2caf59SThomas Veerman break;
33532e2caf59SThomas Veerman }
33542e2caf59SThomas Veerman goto default_case;
33552e2caf59SThomas Veerman case 'H':
33562e2caf59SThomas Veerman if (tstr[1] == endc || tstr[1] == ':') {
33572e2caf59SThomas Veerman newStr = VarModify(ctxt, &parsestate, nstr, VarHead,
33582e2caf59SThomas Veerman NULL);
33592e2caf59SThomas Veerman cp = tstr + 1;
33602e2caf59SThomas Veerman termc = *cp;
33612e2caf59SThomas Veerman break;
33622e2caf59SThomas Veerman }
33632e2caf59SThomas Veerman goto default_case;
33642e2caf59SThomas Veerman case 'E':
33652e2caf59SThomas Veerman if (tstr[1] == endc || tstr[1] == ':') {
33662e2caf59SThomas Veerman newStr = VarModify(ctxt, &parsestate, nstr, VarSuffix,
33672e2caf59SThomas Veerman NULL);
33682e2caf59SThomas Veerman cp = tstr + 1;
33692e2caf59SThomas Veerman termc = *cp;
33702e2caf59SThomas Veerman break;
33712e2caf59SThomas Veerman }
33722e2caf59SThomas Veerman goto default_case;
33732e2caf59SThomas Veerman case 'R':
33742e2caf59SThomas Veerman if (tstr[1] == endc || tstr[1] == ':') {
33752e2caf59SThomas Veerman newStr = VarModify(ctxt, &parsestate, nstr, VarRoot,
33762e2caf59SThomas Veerman NULL);
33772e2caf59SThomas Veerman cp = tstr + 1;
33782e2caf59SThomas Veerman termc = *cp;
33792e2caf59SThomas Veerman break;
33802e2caf59SThomas Veerman }
33812e2caf59SThomas Veerman goto default_case;
33822e2caf59SThomas Veerman case 'O':
33832e2caf59SThomas Veerman {
33842e2caf59SThomas Veerman char otype;
33852e2caf59SThomas Veerman
33862e2caf59SThomas Veerman cp = tstr + 1; /* skip to the rest in any case */
33872e2caf59SThomas Veerman if (tstr[1] == endc || tstr[1] == ':') {
33882e2caf59SThomas Veerman otype = 's';
33892e2caf59SThomas Veerman termc = *cp;
33902e2caf59SThomas Veerman } else if ( (tstr[1] == 'x') &&
33912e2caf59SThomas Veerman (tstr[2] == endc || tstr[2] == ':') ) {
33922e2caf59SThomas Veerman otype = tstr[1];
33932e2caf59SThomas Veerman cp = tstr + 2;
33942e2caf59SThomas Veerman termc = *cp;
33952e2caf59SThomas Veerman } else {
33962e2caf59SThomas Veerman goto bad_modifier;
33972e2caf59SThomas Veerman }
33982e2caf59SThomas Veerman newStr = VarOrder(nstr, otype);
33992e2caf59SThomas Veerman break;
34002e2caf59SThomas Veerman }
34012e2caf59SThomas Veerman case 'u':
34022e2caf59SThomas Veerman if (tstr[1] == endc || tstr[1] == ':') {
34032e2caf59SThomas Veerman newStr = VarUniq(nstr);
34042e2caf59SThomas Veerman cp = tstr + 1;
34052e2caf59SThomas Veerman termc = *cp;
34062e2caf59SThomas Veerman break;
34072e2caf59SThomas Veerman }
34082e2caf59SThomas Veerman goto default_case;
34092e2caf59SThomas Veerman #ifdef SUNSHCMD
34102e2caf59SThomas Veerman case 's':
34112e2caf59SThomas Veerman if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) {
34122e2caf59SThomas Veerman const char *emsg;
34132e2caf59SThomas Veerman newStr = Cmd_Exec(nstr, &emsg);
34142e2caf59SThomas Veerman if (emsg)
34152e2caf59SThomas Veerman Error(emsg, nstr);
34162e2caf59SThomas Veerman cp = tstr + 2;
34172e2caf59SThomas Veerman termc = *cp;
34182e2caf59SThomas Veerman break;
34192e2caf59SThomas Veerman }
34202e2caf59SThomas Veerman goto default_case;
34212e2caf59SThomas Veerman #endif
34222e2caf59SThomas Veerman default:
34232e2caf59SThomas Veerman default_case:
34242e2caf59SThomas Veerman {
34252e2caf59SThomas Veerman #ifdef SYSVVARSUB
34262e2caf59SThomas Veerman /*
34272e2caf59SThomas Veerman * This can either be a bogus modifier or a System-V
34282e2caf59SThomas Veerman * substitution command.
34292e2caf59SThomas Veerman */
34302e2caf59SThomas Veerman VarPattern pattern;
34312e2caf59SThomas Veerman Boolean eqFound;
34322e2caf59SThomas Veerman
34332e2caf59SThomas Veerman pattern.flags = 0;
34342e2caf59SThomas Veerman eqFound = FALSE;
34352e2caf59SThomas Veerman /*
34362e2caf59SThomas Veerman * First we make a pass through the string trying
34372e2caf59SThomas Veerman * to verify it is a SYSV-make-style translation:
34382e2caf59SThomas Veerman * it must be: <string1>=<string2>)
34392e2caf59SThomas Veerman */
34402e2caf59SThomas Veerman cp = tstr;
34412e2caf59SThomas Veerman cnt = 1;
34422e2caf59SThomas Veerman while (*cp != '\0' && cnt) {
34432e2caf59SThomas Veerman if (*cp == '=') {
34442e2caf59SThomas Veerman eqFound = TRUE;
34452e2caf59SThomas Veerman /* continue looking for endc */
34462e2caf59SThomas Veerman }
34472e2caf59SThomas Veerman else if (*cp == endc)
34482e2caf59SThomas Veerman cnt--;
34492e2caf59SThomas Veerman else if (*cp == startc)
34502e2caf59SThomas Veerman cnt++;
34512e2caf59SThomas Veerman if (cnt)
34522e2caf59SThomas Veerman cp++;
34532e2caf59SThomas Veerman }
34542e2caf59SThomas Veerman if (*cp == endc && eqFound) {
34552e2caf59SThomas Veerman
34562e2caf59SThomas Veerman /*
34572e2caf59SThomas Veerman * Now we break this sucker into the lhs and
34582e2caf59SThomas Veerman * rhs. We must null terminate them of course.
34592e2caf59SThomas Veerman */
34602e2caf59SThomas Veerman delim='=';
34612e2caf59SThomas Veerman cp = tstr;
34622e2caf59SThomas Veerman if ((pattern.lhs = VarGetPattern(ctxt, &parsestate,
34632e2caf59SThomas Veerman errnum, &cp, delim, &pattern.flags,
34642e2caf59SThomas Veerman &pattern.leftLen, NULL)) == NULL)
34652e2caf59SThomas Veerman goto cleanup;
34662e2caf59SThomas Veerman delim = endc;
34672e2caf59SThomas Veerman if ((pattern.rhs = VarGetPattern(ctxt, &parsestate,
34682e2caf59SThomas Veerman errnum, &cp, delim, NULL, &pattern.rightLen,
34692e2caf59SThomas Veerman &pattern)) == NULL)
34702e2caf59SThomas Veerman goto cleanup;
34712e2caf59SThomas Veerman
34722e2caf59SThomas Veerman /*
34732e2caf59SThomas Veerman * SYSV modifications happen through the whole
34742e2caf59SThomas Veerman * string. Note the pattern is anchored at the end.
34752e2caf59SThomas Veerman */
34762e2caf59SThomas Veerman termc = *--cp;
34772e2caf59SThomas Veerman delim = '\0';
34782e2caf59SThomas Veerman if (pattern.leftLen == 0 && *nstr == '\0') {
34792e2caf59SThomas Veerman newStr = nstr; /* special case */
34802e2caf59SThomas Veerman } else {
34812e2caf59SThomas Veerman newStr = VarModify(ctxt, &parsestate, nstr,
34822e2caf59SThomas Veerman VarSYSVMatch,
34832e2caf59SThomas Veerman &pattern);
34842e2caf59SThomas Veerman }
34852e2caf59SThomas Veerman free(UNCONST(pattern.lhs));
34862e2caf59SThomas Veerman free(UNCONST(pattern.rhs));
34872e2caf59SThomas Veerman } else
34882e2caf59SThomas Veerman #endif
34892e2caf59SThomas Veerman {
34902e2caf59SThomas Veerman Error("Unknown modifier '%c'", *tstr);
34912e2caf59SThomas Veerman for (cp = tstr+1;
34922e2caf59SThomas Veerman *cp != ':' && *cp != endc && *cp != '\0';
34932e2caf59SThomas Veerman cp++)
34942e2caf59SThomas Veerman continue;
34952e2caf59SThomas Veerman termc = *cp;
34962e2caf59SThomas Veerman newStr = var_Error;
34972e2caf59SThomas Veerman }
34982e2caf59SThomas Veerman }
34992e2caf59SThomas Veerman }
35002e2caf59SThomas Veerman if (DEBUG(VAR)) {
350184d9c625SLionel Sambuc fprintf(debug_file, "Result[%s] of :%c is \"%s\"\n",
350284d9c625SLionel Sambuc v->name, modifier, newStr);
35032e2caf59SThomas Veerman }
35042e2caf59SThomas Veerman
35052e2caf59SThomas Veerman if (newStr != nstr) {
35062e2caf59SThomas Veerman if (*freePtr) {
35072e2caf59SThomas Veerman free(nstr);
35082e2caf59SThomas Veerman *freePtr = NULL;
35092e2caf59SThomas Veerman }
35102e2caf59SThomas Veerman nstr = newStr;
35112e2caf59SThomas Veerman if (nstr != var_Error && nstr != varNoError) {
35122e2caf59SThomas Veerman *freePtr = nstr;
35132e2caf59SThomas Veerman }
35142e2caf59SThomas Veerman }
35152e2caf59SThomas Veerman if (termc == '\0' && endc != '\0') {
35162e2caf59SThomas Veerman Error("Unclosed variable specification (expecting '%c') for \"%s\" (value \"%s\") modifier %c", endc, v->name, nstr, modifier);
35172e2caf59SThomas Veerman } else if (termc == ':') {
35182e2caf59SThomas Veerman cp++;
35192e2caf59SThomas Veerman }
35202e2caf59SThomas Veerman tstr = cp;
35212e2caf59SThomas Veerman }
35222e2caf59SThomas Veerman out:
35232e2caf59SThomas Veerman *lengthPtr = tstr - start;
35242e2caf59SThomas Veerman return (nstr);
35252e2caf59SThomas Veerman
35262e2caf59SThomas Veerman bad_modifier:
35272e2caf59SThomas Veerman /* "{(" */
35282e2caf59SThomas Veerman Error("Bad modifier `:%.*s' for %s", (int)strcspn(tstr, ":)}"), tstr,
35292e2caf59SThomas Veerman v->name);
35302e2caf59SThomas Veerman
35312e2caf59SThomas Veerman cleanup:
35322e2caf59SThomas Veerman *lengthPtr = cp - start;
35332e2caf59SThomas Veerman if (delim != '\0')
35342e2caf59SThomas Veerman Error("Unclosed substitution for %s (%c missing)",
35352e2caf59SThomas Veerman v->name, delim);
35362e2caf59SThomas Veerman free(*freePtr);
35372e2caf59SThomas Veerman *freePtr = NULL;
35382e2caf59SThomas Veerman return (var_Error);
35392e2caf59SThomas Veerman }
35402e2caf59SThomas Veerman
35412e2caf59SThomas Veerman /*-
35422e2caf59SThomas Veerman *-----------------------------------------------------------------------
35432e2caf59SThomas Veerman * Var_Parse --
35442e2caf59SThomas Veerman * Given the start of a variable invocation, extract the variable
35452e2caf59SThomas Veerman * name and find its value, then modify it according to the
35462e2caf59SThomas Veerman * specification.
35472e2caf59SThomas Veerman *
35482e2caf59SThomas Veerman * Input:
35492e2caf59SThomas Veerman * str The string to parse
35502e2caf59SThomas Veerman * ctxt The context for the variable
35512e2caf59SThomas Veerman * errnum TRUE if undefined variables are an error
35522e2caf59SThomas Veerman * lengthPtr OUT: The length of the specification
35532e2caf59SThomas Veerman * freePtr OUT: Non-NULL if caller should free *freePtr
35542e2caf59SThomas Veerman *
35552e2caf59SThomas Veerman * Results:
35562e2caf59SThomas Veerman * The (possibly-modified) value of the variable or var_Error if the
35572e2caf59SThomas Veerman * specification is invalid. The length of the specification is
35582e2caf59SThomas Veerman * placed in *lengthPtr (for invalid specifications, this is just
35592e2caf59SThomas Veerman * 2...?).
35602e2caf59SThomas Veerman * If *freePtr is non-NULL then it's a pointer that the caller
35612e2caf59SThomas Veerman * should pass to free() to free memory used by the result.
35622e2caf59SThomas Veerman *
35632e2caf59SThomas Veerman * Side Effects:
35642e2caf59SThomas Veerman * None.
35652e2caf59SThomas Veerman *
35662e2caf59SThomas Veerman *-----------------------------------------------------------------------
35672e2caf59SThomas Veerman */
35682e2caf59SThomas Veerman /* coverity[+alloc : arg-*4] */
35692e2caf59SThomas Veerman char *
Var_Parse(const char * str,GNode * ctxt,Boolean errnum,int * lengthPtr,void ** freePtr)35702e2caf59SThomas Veerman Var_Parse(const char *str, GNode *ctxt, Boolean errnum, int *lengthPtr,
35712e2caf59SThomas Veerman void **freePtr)
35722e2caf59SThomas Veerman {
35732e2caf59SThomas Veerman const char *tstr; /* Pointer into str */
35742e2caf59SThomas Veerman Var *v; /* Variable in invocation */
35752e2caf59SThomas Veerman Boolean haveModifier;/* TRUE if have modifiers for the variable */
35762e2caf59SThomas Veerman char endc; /* Ending character when variable in parens
35772e2caf59SThomas Veerman * or braces */
35782e2caf59SThomas Veerman char startc; /* Starting character when variable in parens
35792e2caf59SThomas Veerman * or braces */
35802e2caf59SThomas Veerman int vlen; /* Length of variable name */
35812e2caf59SThomas Veerman const char *start; /* Points to original start of str */
35822e2caf59SThomas Veerman char *nstr; /* New string, used during expansion */
35832e2caf59SThomas Veerman Boolean dynamic; /* TRUE if the variable is local and we're
35842e2caf59SThomas Veerman * expanding it in a non-local context. This
35852e2caf59SThomas Veerman * is done to support dynamic sources. The
35862e2caf59SThomas Veerman * result is just the invocation, unaltered */
3587*0a6a1f1dSLionel Sambuc const char *extramodifiers; /* extra modifiers to apply first */
35882e2caf59SThomas Veerman char name[2];
35892e2caf59SThomas Veerman
35902e2caf59SThomas Veerman *freePtr = NULL;
3591*0a6a1f1dSLionel Sambuc extramodifiers = NULL;
35922e2caf59SThomas Veerman dynamic = FALSE;
35932e2caf59SThomas Veerman start = str;
35942e2caf59SThomas Veerman
35952e2caf59SThomas Veerman startc = str[1];
35962e2caf59SThomas Veerman if (startc != PROPEN && startc != BROPEN) {
35972e2caf59SThomas Veerman /*
35982e2caf59SThomas Veerman * If it's not bounded by braces of some sort, life is much simpler.
35992e2caf59SThomas Veerman * We just need to check for the first character and return the
36002e2caf59SThomas Veerman * value if it exists.
36012e2caf59SThomas Veerman */
36022e2caf59SThomas Veerman
36032e2caf59SThomas Veerman /* Error out some really stupid names */
36042e2caf59SThomas Veerman if (startc == '\0' || strchr(")}:$", startc)) {
36052e2caf59SThomas Veerman *lengthPtr = 1;
36062e2caf59SThomas Veerman return var_Error;
36072e2caf59SThomas Veerman }
36082e2caf59SThomas Veerman name[0] = startc;
36092e2caf59SThomas Veerman name[1] = '\0';
36102e2caf59SThomas Veerman
36112e2caf59SThomas Veerman v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
36122e2caf59SThomas Veerman if (v == NULL) {
36132e2caf59SThomas Veerman *lengthPtr = 2;
36142e2caf59SThomas Veerman
36152e2caf59SThomas Veerman if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) {
36162e2caf59SThomas Veerman /*
36172e2caf59SThomas Veerman * If substituting a local variable in a non-local context,
36182e2caf59SThomas Veerman * assume it's for dynamic source stuff. We have to handle
36192e2caf59SThomas Veerman * this specially and return the longhand for the variable
36202e2caf59SThomas Veerman * with the dollar sign escaped so it makes it back to the
36212e2caf59SThomas Veerman * caller. Only four of the local variables are treated
36222e2caf59SThomas Veerman * specially as they are the only four that will be set
36232e2caf59SThomas Veerman * when dynamic sources are expanded.
36242e2caf59SThomas Veerman */
36252e2caf59SThomas Veerman switch (str[1]) {
36262e2caf59SThomas Veerman case '@':
36272e2caf59SThomas Veerman return UNCONST("$(.TARGET)");
36282e2caf59SThomas Veerman case '%':
36292e2caf59SThomas Veerman return UNCONST("$(.ARCHIVE)");
36302e2caf59SThomas Veerman case '*':
36312e2caf59SThomas Veerman return UNCONST("$(.PREFIX)");
36322e2caf59SThomas Veerman case '!':
36332e2caf59SThomas Veerman return UNCONST("$(.MEMBER)");
36342e2caf59SThomas Veerman }
36352e2caf59SThomas Veerman }
36362e2caf59SThomas Veerman /*
36372e2caf59SThomas Veerman * Error
36382e2caf59SThomas Veerman */
36392e2caf59SThomas Veerman return (errnum ? var_Error : varNoError);
36402e2caf59SThomas Veerman } else {
36412e2caf59SThomas Veerman haveModifier = FALSE;
36422e2caf59SThomas Veerman tstr = &str[1];
36432e2caf59SThomas Veerman endc = str[1];
36442e2caf59SThomas Veerman }
36452e2caf59SThomas Veerman } else {
36462e2caf59SThomas Veerman Buffer buf; /* Holds the variable name */
3647*0a6a1f1dSLionel Sambuc int depth = 1;
36482e2caf59SThomas Veerman
36492e2caf59SThomas Veerman endc = startc == PROPEN ? PRCLOSE : BRCLOSE;
36502e2caf59SThomas Veerman Buf_Init(&buf, 0);
36512e2caf59SThomas Veerman
36522e2caf59SThomas Veerman /*
36532e2caf59SThomas Veerman * Skip to the end character or a colon, whichever comes first.
36542e2caf59SThomas Veerman */
3655*0a6a1f1dSLionel Sambuc for (tstr = str + 2; *tstr != '\0'; tstr++)
36562e2caf59SThomas Veerman {
36572e2caf59SThomas Veerman /*
3658*0a6a1f1dSLionel Sambuc * Track depth so we can spot parse errors.
3659*0a6a1f1dSLionel Sambuc */
3660*0a6a1f1dSLionel Sambuc if (*tstr == startc) {
3661*0a6a1f1dSLionel Sambuc depth++;
3662*0a6a1f1dSLionel Sambuc }
3663*0a6a1f1dSLionel Sambuc if (*tstr == endc) {
3664*0a6a1f1dSLionel Sambuc if (--depth == 0)
3665*0a6a1f1dSLionel Sambuc break;
3666*0a6a1f1dSLionel Sambuc }
3667*0a6a1f1dSLionel Sambuc if (depth == 1 && *tstr == ':') {
3668*0a6a1f1dSLionel Sambuc break;
3669*0a6a1f1dSLionel Sambuc }
3670*0a6a1f1dSLionel Sambuc /*
36712e2caf59SThomas Veerman * A variable inside a variable, expand
36722e2caf59SThomas Veerman */
36732e2caf59SThomas Veerman if (*tstr == '$') {
36742e2caf59SThomas Veerman int rlen;
36752e2caf59SThomas Veerman void *freeIt;
36762e2caf59SThomas Veerman char *rval = Var_Parse(tstr, ctxt, errnum, &rlen, &freeIt);
36772e2caf59SThomas Veerman if (rval != NULL) {
36782e2caf59SThomas Veerman Buf_AddBytes(&buf, strlen(rval), rval);
36792e2caf59SThomas Veerman }
36802e2caf59SThomas Veerman free(freeIt);
36812e2caf59SThomas Veerman tstr += rlen - 1;
36822e2caf59SThomas Veerman }
36832e2caf59SThomas Veerman else
36842e2caf59SThomas Veerman Buf_AddByte(&buf, *tstr);
36852e2caf59SThomas Veerman }
36862e2caf59SThomas Veerman if (*tstr == ':') {
36872e2caf59SThomas Veerman haveModifier = TRUE;
3688*0a6a1f1dSLionel Sambuc } else if (*tstr == endc) {
36892e2caf59SThomas Veerman haveModifier = FALSE;
36902e2caf59SThomas Veerman } else {
36912e2caf59SThomas Veerman /*
36922e2caf59SThomas Veerman * If we never did find the end character, return NULL
36932e2caf59SThomas Veerman * right now, setting the length to be the distance to
36942e2caf59SThomas Veerman * the end of the string, since that's what make does.
36952e2caf59SThomas Veerman */
36962e2caf59SThomas Veerman *lengthPtr = tstr - str;
36972e2caf59SThomas Veerman Buf_Destroy(&buf, TRUE);
36982e2caf59SThomas Veerman return (var_Error);
36992e2caf59SThomas Veerman }
37002e2caf59SThomas Veerman str = Buf_GetAll(&buf, &vlen);
37012e2caf59SThomas Veerman
37022e2caf59SThomas Veerman /*
37032e2caf59SThomas Veerman * At this point, str points into newly allocated memory from
37042e2caf59SThomas Veerman * buf, containing only the name of the variable.
37052e2caf59SThomas Veerman *
37062e2caf59SThomas Veerman * start and tstr point into the const string that was pointed
37072e2caf59SThomas Veerman * to by the original value of the str parameter. start points
37082e2caf59SThomas Veerman * to the '$' at the beginning of the string, while tstr points
37092e2caf59SThomas Veerman * to the char just after the end of the variable name -- this
37102e2caf59SThomas Veerman * will be '\0', ':', PRCLOSE, or BRCLOSE.
37112e2caf59SThomas Veerman */
37122e2caf59SThomas Veerman
37132e2caf59SThomas Veerman v = VarFind(str, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
37142e2caf59SThomas Veerman /*
37152e2caf59SThomas Veerman * Check also for bogus D and F forms of local variables since we're
37162e2caf59SThomas Veerman * in a local context and the name is the right length.
37172e2caf59SThomas Veerman */
37182e2caf59SThomas Veerman if ((v == NULL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) &&
37192e2caf59SThomas Veerman (vlen == 2) && (str[1] == 'F' || str[1] == 'D') &&
3720*0a6a1f1dSLionel Sambuc strchr("@%?*!<>", str[0]) != NULL) {
37212e2caf59SThomas Veerman /*
37222e2caf59SThomas Veerman * Well, it's local -- go look for it.
37232e2caf59SThomas Veerman */
37242e2caf59SThomas Veerman name[0] = *str;
37252e2caf59SThomas Veerman name[1] = '\0';
37262e2caf59SThomas Veerman v = VarFind(name, ctxt, 0);
37272e2caf59SThomas Veerman
37282e2caf59SThomas Veerman if (v != NULL) {
37292e2caf59SThomas Veerman if (str[1] == 'D') {
3730*0a6a1f1dSLionel Sambuc extramodifiers = "H:";
37312e2caf59SThomas Veerman }
3732*0a6a1f1dSLionel Sambuc else { /* F */
3733*0a6a1f1dSLionel Sambuc extramodifiers = "T:";
3734*0a6a1f1dSLionel Sambuc }
37352e2caf59SThomas Veerman }
37362e2caf59SThomas Veerman }
37372e2caf59SThomas Veerman
37382e2caf59SThomas Veerman if (v == NULL) {
37392e2caf59SThomas Veerman if (((vlen == 1) ||
37402e2caf59SThomas Veerman (((vlen == 2) && (str[1] == 'F' || str[1] == 'D')))) &&
37412e2caf59SThomas Veerman ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
37422e2caf59SThomas Veerman {
37432e2caf59SThomas Veerman /*
37442e2caf59SThomas Veerman * If substituting a local variable in a non-local context,
37452e2caf59SThomas Veerman * assume it's for dynamic source stuff. We have to handle
37462e2caf59SThomas Veerman * this specially and return the longhand for the variable
37472e2caf59SThomas Veerman * with the dollar sign escaped so it makes it back to the
37482e2caf59SThomas Veerman * caller. Only four of the local variables are treated
37492e2caf59SThomas Veerman * specially as they are the only four that will be set
37502e2caf59SThomas Veerman * when dynamic sources are expanded.
37512e2caf59SThomas Veerman */
37522e2caf59SThomas Veerman switch (*str) {
37532e2caf59SThomas Veerman case '@':
37542e2caf59SThomas Veerman case '%':
37552e2caf59SThomas Veerman case '*':
37562e2caf59SThomas Veerman case '!':
37572e2caf59SThomas Veerman dynamic = TRUE;
37582e2caf59SThomas Veerman break;
37592e2caf59SThomas Veerman }
37602e2caf59SThomas Veerman } else if ((vlen > 2) && (*str == '.') &&
37612e2caf59SThomas Veerman isupper((unsigned char) str[1]) &&
37622e2caf59SThomas Veerman ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
37632e2caf59SThomas Veerman {
37642e2caf59SThomas Veerman int len;
37652e2caf59SThomas Veerman
37662e2caf59SThomas Veerman len = vlen - 1;
37672e2caf59SThomas Veerman if ((strncmp(str, ".TARGET", len) == 0) ||
37682e2caf59SThomas Veerman (strncmp(str, ".ARCHIVE", len) == 0) ||
37692e2caf59SThomas Veerman (strncmp(str, ".PREFIX", len) == 0) ||
37702e2caf59SThomas Veerman (strncmp(str, ".MEMBER", len) == 0))
37712e2caf59SThomas Veerman {
37722e2caf59SThomas Veerman dynamic = TRUE;
37732e2caf59SThomas Veerman }
37742e2caf59SThomas Veerman }
37752e2caf59SThomas Veerman
37762e2caf59SThomas Veerman if (!haveModifier) {
37772e2caf59SThomas Veerman /*
37782e2caf59SThomas Veerman * No modifiers -- have specification length so we can return
37792e2caf59SThomas Veerman * now.
37802e2caf59SThomas Veerman */
37812e2caf59SThomas Veerman *lengthPtr = tstr - start + 1;
37822e2caf59SThomas Veerman if (dynamic) {
37832e2caf59SThomas Veerman char *pstr = bmake_strndup(start, *lengthPtr);
37842e2caf59SThomas Veerman *freePtr = pstr;
37852e2caf59SThomas Veerman Buf_Destroy(&buf, TRUE);
37862e2caf59SThomas Veerman return(pstr);
37872e2caf59SThomas Veerman } else {
37882e2caf59SThomas Veerman Buf_Destroy(&buf, TRUE);
37892e2caf59SThomas Veerman return (errnum ? var_Error : varNoError);
37902e2caf59SThomas Veerman }
37912e2caf59SThomas Veerman } else {
37922e2caf59SThomas Veerman /*
37932e2caf59SThomas Veerman * Still need to get to the end of the variable specification,
37942e2caf59SThomas Veerman * so kludge up a Var structure for the modifications
37952e2caf59SThomas Veerman */
37962e2caf59SThomas Veerman v = bmake_malloc(sizeof(Var));
37972e2caf59SThomas Veerman v->name = UNCONST(str);
37982e2caf59SThomas Veerman Buf_Init(&v->val, 1);
37992e2caf59SThomas Veerman v->flags = VAR_JUNK;
38002e2caf59SThomas Veerman Buf_Destroy(&buf, FALSE);
38012e2caf59SThomas Veerman }
38022e2caf59SThomas Veerman } else
38032e2caf59SThomas Veerman Buf_Destroy(&buf, TRUE);
38042e2caf59SThomas Veerman }
38052e2caf59SThomas Veerman
38062e2caf59SThomas Veerman if (v->flags & VAR_IN_USE) {
38072e2caf59SThomas Veerman Fatal("Variable %s is recursive.", v->name);
38082e2caf59SThomas Veerman /*NOTREACHED*/
38092e2caf59SThomas Veerman } else {
38102e2caf59SThomas Veerman v->flags |= VAR_IN_USE;
38112e2caf59SThomas Veerman }
38122e2caf59SThomas Veerman /*
38132e2caf59SThomas Veerman * Before doing any modification, we have to make sure the value
38142e2caf59SThomas Veerman * has been fully expanded. If it looks like recursion might be
38152e2caf59SThomas Veerman * necessary (there's a dollar sign somewhere in the variable's value)
38162e2caf59SThomas Veerman * we just call Var_Subst to do any other substitutions that are
38172e2caf59SThomas Veerman * necessary. Note that the value returned by Var_Subst will have
38182e2caf59SThomas Veerman * been dynamically-allocated, so it will need freeing when we
38192e2caf59SThomas Veerman * return.
38202e2caf59SThomas Veerman */
38212e2caf59SThomas Veerman nstr = Buf_GetAll(&v->val, NULL);
38222e2caf59SThomas Veerman if (strchr(nstr, '$') != NULL) {
38232e2caf59SThomas Veerman nstr = Var_Subst(NULL, nstr, ctxt, errnum);
38242e2caf59SThomas Veerman *freePtr = nstr;
38252e2caf59SThomas Veerman }
38262e2caf59SThomas Veerman
38272e2caf59SThomas Veerman v->flags &= ~VAR_IN_USE;
38282e2caf59SThomas Veerman
3829*0a6a1f1dSLionel Sambuc if ((nstr != NULL) && (haveModifier || extramodifiers != NULL)) {
3830*0a6a1f1dSLionel Sambuc void *extraFree;
38312e2caf59SThomas Veerman int used;
3832*0a6a1f1dSLionel Sambuc
3833*0a6a1f1dSLionel Sambuc extraFree = NULL;
3834*0a6a1f1dSLionel Sambuc if (extramodifiers != NULL) {
3835*0a6a1f1dSLionel Sambuc nstr = ApplyModifiers(nstr, extramodifiers, '(', ')',
3836*0a6a1f1dSLionel Sambuc v, ctxt, errnum, &used, &extraFree);
3837*0a6a1f1dSLionel Sambuc }
3838*0a6a1f1dSLionel Sambuc
3839*0a6a1f1dSLionel Sambuc if (haveModifier) {
3840*0a6a1f1dSLionel Sambuc /* Skip initial colon. */
38412e2caf59SThomas Veerman tstr++;
38422e2caf59SThomas Veerman
38432e2caf59SThomas Veerman nstr = ApplyModifiers(nstr, tstr, startc, endc,
38442e2caf59SThomas Veerman v, ctxt, errnum, &used, freePtr);
38452e2caf59SThomas Veerman tstr += used;
3846*0a6a1f1dSLionel Sambuc free(extraFree);
3847*0a6a1f1dSLionel Sambuc } else {
3848*0a6a1f1dSLionel Sambuc *freePtr = extraFree;
3849*0a6a1f1dSLionel Sambuc }
38502e2caf59SThomas Veerman }
38512e2caf59SThomas Veerman if (*tstr) {
38522e2caf59SThomas Veerman *lengthPtr = tstr - start + 1;
38532e2caf59SThomas Veerman } else {
38542e2caf59SThomas Veerman *lengthPtr = tstr - start;
38552e2caf59SThomas Veerman }
38562e2caf59SThomas Veerman
38572e2caf59SThomas Veerman if (v->flags & VAR_FROM_ENV) {
38582e2caf59SThomas Veerman Boolean destroy = FALSE;
38592e2caf59SThomas Veerman
38602e2caf59SThomas Veerman if (nstr != Buf_GetAll(&v->val, NULL)) {
38612e2caf59SThomas Veerman destroy = TRUE;
38622e2caf59SThomas Veerman } else {
38632e2caf59SThomas Veerman /*
38642e2caf59SThomas Veerman * Returning the value unmodified, so tell the caller to free
38652e2caf59SThomas Veerman * the thing.
38662e2caf59SThomas Veerman */
38672e2caf59SThomas Veerman *freePtr = nstr;
38682e2caf59SThomas Veerman }
38692e2caf59SThomas Veerman VarFreeEnv(v, destroy);
38702e2caf59SThomas Veerman } else if (v->flags & VAR_JUNK) {
38712e2caf59SThomas Veerman /*
38722e2caf59SThomas Veerman * Perform any free'ing needed and set *freePtr to NULL so the caller
38732e2caf59SThomas Veerman * doesn't try to free a static pointer.
38742e2caf59SThomas Veerman * If VAR_KEEP is also set then we want to keep str as is.
38752e2caf59SThomas Veerman */
38762e2caf59SThomas Veerman if (!(v->flags & VAR_KEEP)) {
38772e2caf59SThomas Veerman if (*freePtr) {
38782e2caf59SThomas Veerman free(nstr);
38792e2caf59SThomas Veerman *freePtr = NULL;
38802e2caf59SThomas Veerman }
38812e2caf59SThomas Veerman if (dynamic) {
38822e2caf59SThomas Veerman nstr = bmake_strndup(start, *lengthPtr);
38832e2caf59SThomas Veerman *freePtr = nstr;
38842e2caf59SThomas Veerman } else {
38852e2caf59SThomas Veerman nstr = errnum ? var_Error : varNoError;
38862e2caf59SThomas Veerman }
38872e2caf59SThomas Veerman }
38882e2caf59SThomas Veerman if (nstr != Buf_GetAll(&v->val, NULL))
38892e2caf59SThomas Veerman Buf_Destroy(&v->val, TRUE);
38902e2caf59SThomas Veerman free(v->name);
38912e2caf59SThomas Veerman free(v);
38922e2caf59SThomas Veerman }
38932e2caf59SThomas Veerman return (nstr);
38942e2caf59SThomas Veerman }
38952e2caf59SThomas Veerman
38962e2caf59SThomas Veerman /*-
38972e2caf59SThomas Veerman *-----------------------------------------------------------------------
38982e2caf59SThomas Veerman * Var_Subst --
38992e2caf59SThomas Veerman * Substitute for all variables in the given string in the given context
39002e2caf59SThomas Veerman * If undefErr is TRUE, Parse_Error will be called when an undefined
39012e2caf59SThomas Veerman * variable is encountered.
39022e2caf59SThomas Veerman *
39032e2caf59SThomas Veerman * Input:
39042e2caf59SThomas Veerman * var Named variable || NULL for all
39052e2caf59SThomas Veerman * str the string which to substitute
39062e2caf59SThomas Veerman * ctxt the context wherein to find variables
39072e2caf59SThomas Veerman * undefErr TRUE if undefineds are an error
39082e2caf59SThomas Veerman *
39092e2caf59SThomas Veerman * Results:
39102e2caf59SThomas Veerman * The resulting string.
39112e2caf59SThomas Veerman *
39122e2caf59SThomas Veerman * Side Effects:
39132e2caf59SThomas Veerman * None. The old string must be freed by the caller
39142e2caf59SThomas Veerman *-----------------------------------------------------------------------
39152e2caf59SThomas Veerman */
39162e2caf59SThomas Veerman char *
Var_Subst(const char * var,const char * str,GNode * ctxt,Boolean undefErr)39172e2caf59SThomas Veerman Var_Subst(const char *var, const char *str, GNode *ctxt, Boolean undefErr)
39182e2caf59SThomas Veerman {
39192e2caf59SThomas Veerman Buffer buf; /* Buffer for forming things */
39202e2caf59SThomas Veerman char *val; /* Value to substitute for a variable */
39212e2caf59SThomas Veerman int length; /* Length of the variable invocation */
39222e2caf59SThomas Veerman Boolean trailingBslash; /* variable ends in \ */
39232e2caf59SThomas Veerman void *freeIt = NULL; /* Set if it should be freed */
39242e2caf59SThomas Veerman static Boolean errorReported; /* Set true if an error has already
39252e2caf59SThomas Veerman * been reported to prevent a plethora
39262e2caf59SThomas Veerman * of messages when recursing */
39272e2caf59SThomas Veerman
39282e2caf59SThomas Veerman Buf_Init(&buf, 0);
39292e2caf59SThomas Veerman errorReported = FALSE;
39302e2caf59SThomas Veerman trailingBslash = FALSE;
39312e2caf59SThomas Veerman
39322e2caf59SThomas Veerman while (*str) {
39332e2caf59SThomas Veerman if (*str == '\n' && trailingBslash)
39342e2caf59SThomas Veerman Buf_AddByte(&buf, ' ');
39352e2caf59SThomas Veerman if (var == NULL && (*str == '$') && (str[1] == '$')) {
39362e2caf59SThomas Veerman /*
39372e2caf59SThomas Veerman * A dollar sign may be escaped either with another dollar sign.
39382e2caf59SThomas Veerman * In such a case, we skip over the escape character and store the
39392e2caf59SThomas Veerman * dollar sign into the buffer directly.
39402e2caf59SThomas Veerman */
39412e2caf59SThomas Veerman str++;
39422e2caf59SThomas Veerman Buf_AddByte(&buf, *str);
39432e2caf59SThomas Veerman str++;
39442e2caf59SThomas Veerman } else if (*str != '$') {
39452e2caf59SThomas Veerman /*
39462e2caf59SThomas Veerman * Skip as many characters as possible -- either to the end of
39472e2caf59SThomas Veerman * the string or to the next dollar sign (variable invocation).
39482e2caf59SThomas Veerman */
39492e2caf59SThomas Veerman const char *cp;
39502e2caf59SThomas Veerman
39512e2caf59SThomas Veerman for (cp = str++; *str != '$' && *str != '\0'; str++)
39522e2caf59SThomas Veerman continue;
39532e2caf59SThomas Veerman Buf_AddBytes(&buf, str - cp, cp);
39542e2caf59SThomas Veerman } else {
39552e2caf59SThomas Veerman if (var != NULL) {
39562e2caf59SThomas Veerman int expand;
39572e2caf59SThomas Veerman for (;;) {
39582e2caf59SThomas Veerman if (str[1] == '\0') {
39592e2caf59SThomas Veerman /* A trailing $ is kind of a special case */
39602e2caf59SThomas Veerman Buf_AddByte(&buf, str[0]);
39612e2caf59SThomas Veerman str++;
39622e2caf59SThomas Veerman expand = FALSE;
39632e2caf59SThomas Veerman } else if (str[1] != PROPEN && str[1] != BROPEN) {
39642e2caf59SThomas Veerman if (str[1] != *var || strlen(var) > 1) {
39652e2caf59SThomas Veerman Buf_AddBytes(&buf, 2, str);
39662e2caf59SThomas Veerman str += 2;
39672e2caf59SThomas Veerman expand = FALSE;
39682e2caf59SThomas Veerman }
39692e2caf59SThomas Veerman else
39702e2caf59SThomas Veerman expand = TRUE;
39712e2caf59SThomas Veerman break;
39722e2caf59SThomas Veerman }
39732e2caf59SThomas Veerman else {
39742e2caf59SThomas Veerman const char *p;
39752e2caf59SThomas Veerman
39762e2caf59SThomas Veerman /*
39772e2caf59SThomas Veerman * Scan up to the end of the variable name.
39782e2caf59SThomas Veerman */
39792e2caf59SThomas Veerman for (p = &str[2]; *p &&
39802e2caf59SThomas Veerman *p != ':' && *p != PRCLOSE && *p != BRCLOSE; p++)
39812e2caf59SThomas Veerman if (*p == '$')
39822e2caf59SThomas Veerman break;
39832e2caf59SThomas Veerman /*
39842e2caf59SThomas Veerman * A variable inside the variable. We cannot expand
39852e2caf59SThomas Veerman * the external variable yet, so we try again with
39862e2caf59SThomas Veerman * the nested one
39872e2caf59SThomas Veerman */
39882e2caf59SThomas Veerman if (*p == '$') {
39892e2caf59SThomas Veerman Buf_AddBytes(&buf, p - str, str);
39902e2caf59SThomas Veerman str = p;
39912e2caf59SThomas Veerman continue;
39922e2caf59SThomas Veerman }
39932e2caf59SThomas Veerman
39942e2caf59SThomas Veerman if (strncmp(var, str + 2, p - str - 2) != 0 ||
39952e2caf59SThomas Veerman var[p - str - 2] != '\0') {
39962e2caf59SThomas Veerman /*
39972e2caf59SThomas Veerman * Not the variable we want to expand, scan
39982e2caf59SThomas Veerman * until the next variable
39992e2caf59SThomas Veerman */
40002e2caf59SThomas Veerman for (;*p != '$' && *p != '\0'; p++)
40012e2caf59SThomas Veerman continue;
40022e2caf59SThomas Veerman Buf_AddBytes(&buf, p - str, str);
40032e2caf59SThomas Veerman str = p;
40042e2caf59SThomas Veerman expand = FALSE;
40052e2caf59SThomas Veerman }
40062e2caf59SThomas Veerman else
40072e2caf59SThomas Veerman expand = TRUE;
40082e2caf59SThomas Veerman break;
40092e2caf59SThomas Veerman }
40102e2caf59SThomas Veerman }
40112e2caf59SThomas Veerman if (!expand)
40122e2caf59SThomas Veerman continue;
40132e2caf59SThomas Veerman }
40142e2caf59SThomas Veerman
40152e2caf59SThomas Veerman val = Var_Parse(str, ctxt, undefErr, &length, &freeIt);
40162e2caf59SThomas Veerman
40172e2caf59SThomas Veerman /*
40182e2caf59SThomas Veerman * When we come down here, val should either point to the
40192e2caf59SThomas Veerman * value of this variable, suitably modified, or be NULL.
40202e2caf59SThomas Veerman * Length should be the total length of the potential
40212e2caf59SThomas Veerman * variable invocation (from $ to end character...)
40222e2caf59SThomas Veerman */
40232e2caf59SThomas Veerman if (val == var_Error || val == varNoError) {
40242e2caf59SThomas Veerman /*
40252e2caf59SThomas Veerman * If performing old-time variable substitution, skip over
40262e2caf59SThomas Veerman * the variable and continue with the substitution. Otherwise,
40272e2caf59SThomas Veerman * store the dollar sign and advance str so we continue with
40282e2caf59SThomas Veerman * the string...
40292e2caf59SThomas Veerman */
40302e2caf59SThomas Veerman if (oldVars) {
40312e2caf59SThomas Veerman str += length;
4032*0a6a1f1dSLionel Sambuc } else if (undefErr || val == var_Error) {
40332e2caf59SThomas Veerman /*
40342e2caf59SThomas Veerman * If variable is undefined, complain and skip the
40352e2caf59SThomas Veerman * variable. The complaint will stop us from doing anything
40362e2caf59SThomas Veerman * when the file is parsed.
40372e2caf59SThomas Veerman */
40382e2caf59SThomas Veerman if (!errorReported) {
40392e2caf59SThomas Veerman Parse_Error(PARSE_FATAL,
40402e2caf59SThomas Veerman "Undefined variable \"%.*s\"",length,str);
40412e2caf59SThomas Veerman }
40422e2caf59SThomas Veerman str += length;
40432e2caf59SThomas Veerman errorReported = TRUE;
40442e2caf59SThomas Veerman } else {
40452e2caf59SThomas Veerman Buf_AddByte(&buf, *str);
40462e2caf59SThomas Veerman str += 1;
40472e2caf59SThomas Veerman }
40482e2caf59SThomas Veerman } else {
40492e2caf59SThomas Veerman /*
40502e2caf59SThomas Veerman * We've now got a variable structure to store in. But first,
40512e2caf59SThomas Veerman * advance the string pointer.
40522e2caf59SThomas Veerman */
40532e2caf59SThomas Veerman str += length;
40542e2caf59SThomas Veerman
40552e2caf59SThomas Veerman /*
40562e2caf59SThomas Veerman * Copy all the characters from the variable value straight
40572e2caf59SThomas Veerman * into the new string.
40582e2caf59SThomas Veerman */
40592e2caf59SThomas Veerman length = strlen(val);
40602e2caf59SThomas Veerman Buf_AddBytes(&buf, length, val);
40612e2caf59SThomas Veerman trailingBslash = length > 0 && val[length - 1] == '\\';
40622e2caf59SThomas Veerman }
40632e2caf59SThomas Veerman free(freeIt);
40642e2caf59SThomas Veerman freeIt = NULL;
40652e2caf59SThomas Veerman }
40662e2caf59SThomas Veerman }
40672e2caf59SThomas Veerman
40682bc7c627SLionel Sambuc return Buf_DestroyCompact(&buf);
40692e2caf59SThomas Veerman }
40702e2caf59SThomas Veerman
40712e2caf59SThomas Veerman /*-
40722e2caf59SThomas Veerman *-----------------------------------------------------------------------
40732e2caf59SThomas Veerman * Var_GetTail --
40742e2caf59SThomas Veerman * Return the tail from each of a list of words. Used to set the
40752e2caf59SThomas Veerman * System V local variables.
40762e2caf59SThomas Veerman *
40772e2caf59SThomas Veerman * Input:
40782e2caf59SThomas Veerman * file Filename to modify
40792e2caf59SThomas Veerman *
40802e2caf59SThomas Veerman * Results:
40812e2caf59SThomas Veerman * The resulting string.
40822e2caf59SThomas Veerman *
40832e2caf59SThomas Veerman * Side Effects:
40842e2caf59SThomas Veerman * None.
40852e2caf59SThomas Veerman *
40862e2caf59SThomas Veerman *-----------------------------------------------------------------------
40872e2caf59SThomas Veerman */
40882e2caf59SThomas Veerman #if 0
40892e2caf59SThomas Veerman char *
40902e2caf59SThomas Veerman Var_GetTail(char *file)
40912e2caf59SThomas Veerman {
40922e2caf59SThomas Veerman return(VarModify(file, VarTail, NULL));
40932e2caf59SThomas Veerman }
40942e2caf59SThomas Veerman
40952e2caf59SThomas Veerman /*-
40962e2caf59SThomas Veerman *-----------------------------------------------------------------------
40972e2caf59SThomas Veerman * Var_GetHead --
40982e2caf59SThomas Veerman * Find the leading components of a (list of) filename(s).
40992e2caf59SThomas Veerman * XXX: VarHead does not replace foo by ., as (sun) System V make
41002e2caf59SThomas Veerman * does.
41012e2caf59SThomas Veerman *
41022e2caf59SThomas Veerman * Input:
41032e2caf59SThomas Veerman * file Filename to manipulate
41042e2caf59SThomas Veerman *
41052e2caf59SThomas Veerman * Results:
41062e2caf59SThomas Veerman * The leading components.
41072e2caf59SThomas Veerman *
41082e2caf59SThomas Veerman * Side Effects:
41092e2caf59SThomas Veerman * None.
41102e2caf59SThomas Veerman *
41112e2caf59SThomas Veerman *-----------------------------------------------------------------------
41122e2caf59SThomas Veerman */
41132e2caf59SThomas Veerman char *
41142e2caf59SThomas Veerman Var_GetHead(char *file)
41152e2caf59SThomas Veerman {
41162e2caf59SThomas Veerman return(VarModify(file, VarHead, NULL));
41172e2caf59SThomas Veerman }
41182e2caf59SThomas Veerman #endif
41192e2caf59SThomas Veerman
41202e2caf59SThomas Veerman /*-
41212e2caf59SThomas Veerman *-----------------------------------------------------------------------
41222e2caf59SThomas Veerman * Var_Init --
41232e2caf59SThomas Veerman * Initialize the module
41242e2caf59SThomas Veerman *
41252e2caf59SThomas Veerman * Results:
41262e2caf59SThomas Veerman * None
41272e2caf59SThomas Veerman *
41282e2caf59SThomas Veerman * Side Effects:
41292e2caf59SThomas Veerman * The VAR_CMD and VAR_GLOBAL contexts are created
41302e2caf59SThomas Veerman *-----------------------------------------------------------------------
41312e2caf59SThomas Veerman */
41322e2caf59SThomas Veerman void
Var_Init(void)41332e2caf59SThomas Veerman Var_Init(void)
41342e2caf59SThomas Veerman {
413584d9c625SLionel Sambuc VAR_INTERNAL = Targ_NewGN("Internal");
41362e2caf59SThomas Veerman VAR_GLOBAL = Targ_NewGN("Global");
41372e2caf59SThomas Veerman VAR_CMD = Targ_NewGN("Command");
41382e2caf59SThomas Veerman
41392e2caf59SThomas Veerman }
41402e2caf59SThomas Veerman
41412e2caf59SThomas Veerman
41422e2caf59SThomas Veerman void
Var_End(void)41432e2caf59SThomas Veerman Var_End(void)
41442e2caf59SThomas Veerman {
41452e2caf59SThomas Veerman }
41462e2caf59SThomas Veerman
41472e2caf59SThomas Veerman
41482e2caf59SThomas Veerman /****************** PRINT DEBUGGING INFO *****************/
41492e2caf59SThomas Veerman static void
VarPrintVar(void * vp)41502e2caf59SThomas Veerman VarPrintVar(void *vp)
41512e2caf59SThomas Veerman {
41522e2caf59SThomas Veerman Var *v = (Var *)vp;
41532e2caf59SThomas Veerman fprintf(debug_file, "%-16s = %s\n", v->name, Buf_GetAll(&v->val, NULL));
41542e2caf59SThomas Veerman }
41552e2caf59SThomas Veerman
41562e2caf59SThomas Veerman /*-
41572e2caf59SThomas Veerman *-----------------------------------------------------------------------
41582e2caf59SThomas Veerman * Var_Dump --
41592e2caf59SThomas Veerman * print all variables in a context
41602e2caf59SThomas Veerman *-----------------------------------------------------------------------
41612e2caf59SThomas Veerman */
41622e2caf59SThomas Veerman void
Var_Dump(GNode * ctxt)41632e2caf59SThomas Veerman Var_Dump(GNode *ctxt)
41642e2caf59SThomas Veerman {
41652e2caf59SThomas Veerman Hash_Search search;
41662e2caf59SThomas Veerman Hash_Entry *h;
41672e2caf59SThomas Veerman
41682e2caf59SThomas Veerman for (h = Hash_EnumFirst(&ctxt->context, &search);
41692e2caf59SThomas Veerman h != NULL;
41702e2caf59SThomas Veerman h = Hash_EnumNext(&search)) {
41712e2caf59SThomas Veerman VarPrintVar(Hash_GetValue(h));
41722e2caf59SThomas Veerman }
41732e2caf59SThomas Veerman }
4174