xref: /minix3/usr.bin/make/var.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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