1141104Sharti/*-
2146045Sharti * Copyright (c) 2002 Juli Mallett.
31590Srgrimes * Copyright (c) 1988, 1989, 1990, 1993
41590Srgrimes *	The Regents of the University of California.  All rights reserved.
51590Srgrimes * Copyright (c) 1989 by Berkeley Softworks
61590Srgrimes * All rights reserved.
71590Srgrimes *
81590Srgrimes * This code is derived from software contributed to Berkeley by
91590Srgrimes * Adam de Boor.
101590Srgrimes *
111590Srgrimes * Redistribution and use in source and binary forms, with or without
121590Srgrimes * modification, are permitted provided that the following conditions
131590Srgrimes * are met:
141590Srgrimes * 1. Redistributions of source code must retain the above copyright
151590Srgrimes *    notice, this list of conditions and the following disclaimer.
161590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
171590Srgrimes *    notice, this list of conditions and the following disclaimer in the
181590Srgrimes *    documentation and/or other materials provided with the distribution.
191590Srgrimes * 3. All advertising materials mentioning features or use of this software
201590Srgrimes *    must display the following acknowledgement:
211590Srgrimes *	This product includes software developed by the University of
221590Srgrimes *	California, Berkeley and its contributors.
231590Srgrimes * 4. Neither the name of the University nor the names of its contributors
241590Srgrimes *    may be used to endorse or promote products derived from this software
251590Srgrimes *    without specific prior written permission.
261590Srgrimes *
271590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
281590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
291590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
301590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
311590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
321590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
331590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
341590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
351590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
361590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
371590Srgrimes * SUCH DAMAGE.
3862833Swsanchez *
3962833Swsanchez * @(#)var.c	8.3 (Berkeley) 3/19/94
401590Srgrimes */
411590Srgrimes
4262833Swsanchez#include <sys/cdefs.h>
4394587Sobrien__FBSDID("$FreeBSD$");
441590Srgrimes
45146046Sharti/**
461590Srgrimes * var.c --
471590Srgrimes *	Variable-handling functions
481590Srgrimes *
491590Srgrimes * Interface:
50143813Sharti *	Var_Set		Set the value of a variable in the given
51143813Sharti *			context. The variable is created if it doesn't
52143813Sharti *			yet exist. The value and variable name need not
53143813Sharti *			be preserved.
541590Srgrimes *
55143813Sharti *	Var_Append	Append more characters to an existing variable
56143813Sharti *			in the given context. The variable needn't
57143813Sharti *			exist already -- it will be created if it doesn't.
58143813Sharti *			A space is placed between the old value and the
59143813Sharti *			new one.
601590Srgrimes *
61143813Sharti *	Var_Exists	See if a variable exists.
621590Srgrimes *
63143813Sharti *	Var_Value	Return the value of a variable in a context or
64143813Sharti *			NULL if the variable is undefined.
651590Srgrimes *
66143813Sharti *	Var_Subst	Substitute named variable, or all variables if
67143813Sharti *			NULL in a string using
68143813Sharti *			the given context as the top-most one. If the
69143813Sharti *			third argument is non-zero, Parse_Error is
70143813Sharti *			called if any variables are undefined.
711590Srgrimes *
72143813Sharti *	Var_Parse	Parse a variable expansion from a string and
73143813Sharti *			return the result and the number of characters
74143813Sharti *			consumed.
751590Srgrimes *
76143813Sharti *	Var_Delete	Delete a variable in a context.
771590Srgrimes *
78143813Sharti *	Var_Init	Initialize this module.
791590Srgrimes *
801590Srgrimes * Debugging:
81143813Sharti *	Var_Dump	Print out all variables defined in the given
82143813Sharti *			context.
831590Srgrimes *
841590Srgrimes * XXX: There's a lot of duplication in these functions.
851590Srgrimes */
861590Srgrimes
87141104Sharti#include <ctype.h>
88141104Sharti#include <stdlib.h>
89141104Sharti#include <string.h>
90146045Sharti#include <sys/types.h>
91146045Sharti#include <regex.h>
921590Srgrimes
93141104Sharti#include "buf.h"
94141104Sharti#include "config.h"
95141104Sharti#include "globals.h"
96141104Sharti#include "GNode.h"
97146054Sharti#include "job.h"
98146141Sharti#include "lst.h"
99141104Sharti#include "parse.h"
100141104Sharti#include "str.h"
101141104Sharti#include "targ.h"
102141104Sharti#include "util.h"
103141104Sharti#include "var.h"
104141104Sharti
105143811Sharti/**
106143811Sharti *
107143811Sharti */
108143811Shartitypedef struct VarParser {
109143811Sharti	const char	*const input;	/* pointer to input string */
110143904Sharti	const char	*ptr;		/* current parser pos in input str */
111143811Sharti	GNode		*ctxt;
112143811Sharti	Boolean		err;
113146038Sharti	Boolean		execute;
114143811Sharti} VarParser;
115146045Sharti
116146045Shartitypedef struct Var {
117146045Sharti	char		*name;	/* the variable's name */
118146045Sharti	struct Buffer	*val;	/* its value */
119146045Sharti	int		flags;	/* miscellaneous status flags */
120146045Sharti
121146045Sharti#define	VAR_IN_USE	1	/* Variable's value currently being used.
122146045Sharti				 * Used to avoid recursion */
123146045Sharti
124146045Sharti#define	VAR_JUNK	4	/* Variable is a junk variable that
125146045Sharti				 * should be destroyed when done with
126146045Sharti				 * it. Used by Var_Parse for undefined,
127146045Sharti				 * modified variables */
128146045Sharti
129146045Sharti#define	VAR_TO_ENV	8	/* Place variable in environment */
130146045Sharti} Var;
131146045Sharti
132146045Shartitypedef struct {
133146045Sharti	struct Buffer	*lhs;	/* String to match */
134146045Sharti	struct Buffer	*rhs;	/* Replacement string (w/ &'s removed) */
135146045Sharti
136146045Sharti	regex_t			re;
137146045Sharti	int			nsub;
138146045Sharti	regmatch_t		*matches;
139146045Sharti
140146045Sharti	int	flags;
141146045Sharti#define	VAR_SUB_GLOBAL	0x01	/* Apply substitution globally */
142146045Sharti#define	VAR_SUB_ONE	0x02	/* Apply substitution to one word */
143146045Sharti#define	VAR_SUB_MATCHED	0x04	/* There was a match */
144146045Sharti#define	VAR_MATCH_START	0x08	/* Match at start of word */
145146045Sharti#define	VAR_MATCH_END	0x10	/* Match at end of word */
146146045Sharti} VarPattern;
147146045Sharti
148146045Shartitypedef Boolean VarModifyProc(const char *, Boolean, struct Buffer *, void *);
149146045Sharti
150143914Shartistatic char *VarParse(VarParser *, Boolean *);
151143811Sharti
1521590Srgrimes/*
1531590Srgrimes * This is a harmless return value for Var_Parse that can be used by Var_Subst
1541590Srgrimes * to determine if there was an error in parsing -- easier than returning
1551590Srgrimes * a flag, as things outside this module don't give a hoot.
1561590Srgrimes */
157143813Shartichar	var_Error[] = "";
1581590Srgrimes
1591590Srgrimes/*
1601590Srgrimes * Similar to var_Error, but returned when the 'err' flag for Var_Parse is
1611590Srgrimes * set false. Why not just use a constant? Well, gcc likes to condense
1621590Srgrimes * identical string instances...
1631590Srgrimes */
1641590Srgrimesstatic char	varNoError[] = "";
1651590Srgrimes
1661590Srgrimes/*
1671590Srgrimes * Internally, variables are contained in four different contexts.
1681590Srgrimes *	1) the environment. They may not be changed. If an environment
169143813Sharti *	   variable is appended-to, the result is placed in the global
170143813Sharti *	   context.
1711590Srgrimes *	2) the global context. Variables set in the Makefile are located in
172143813Sharti *	   the global context. It is the penultimate context searched when
173143813Sharti *	   substituting.
1741590Srgrimes *	3) the command-line context. All variables set on the command line
1751590Srgrimes *	   are placed in this context. They are UNALTERABLE once placed here.
1761590Srgrimes *	4) the local context. Each target has associated with it a context
1771590Srgrimes *	   list. On this list are located the structures describing such
1781590Srgrimes *	   local variables as $(@) and $(*)
1791590Srgrimes * The four contexts are searched in the reverse order from which they are
1801590Srgrimes * listed.
1811590Srgrimes */
182146134Shartistatic GNode	*VAR_ENV;	/* variables from the environment */
183146134ShartiGNode		*VAR_GLOBAL;	/* variables from the makefile */
184146134ShartiGNode		*VAR_CMD;	/* variables defined on the command-line */
1851590Srgrimes
186146134ShartiBoolean		oldVars;	/* variable substitution style */
187146134ShartiBoolean		checkEnvFirst;	/* -e flag */
188146134Sharti
189141583Sharti#define	OPEN_PAREN		'('
190141583Sharti#define	CLOSE_PAREN		')'
191141583Sharti#define	OPEN_BRACE		'{'
192141583Sharti#define	CLOSE_BRACE		'}'
193141583Sharti
194146046Sharti/**
195141564Sharti * Create a Var object.
196141564Sharti *
197141564Sharti * Params:
198141564Sharti *	name		Name of variable (copied).
199141564Sharti *	value		Value of variable (copied) or NULL.
200141564Sharti *	flags		Flags set on variable.
201141564Sharti *
202141564Sharti * Returns:
203141564Sharti *	New variable.
204141564Sharti */
205141564Shartistatic Var *
206141564ShartiVarCreate(const char name[], const char value[], int flags)
207141564Sharti{
208141564Sharti	Var *v;
209141564Sharti
210141564Sharti	v = emalloc(sizeof(Var));
211141564Sharti	v->name = estrdup(name);
212141564Sharti	v->val = Buf_Init(0);
213141564Sharti	v->flags = flags;
214141564Sharti
215141564Sharti	if (value != NULL) {
216141564Sharti		Buf_Append(v->val, value);
217141564Sharti	}
218141564Sharti	return (v);
219141564Sharti}
220141564Sharti
221146046Sharti/**
222141564Sharti * Destroy a Var object.
223141564Sharti *
224141564Sharti * Params:
225146039Sharti *	v	Object to destroy.
226146039Sharti *	f	True if internal buffer in Buffer object is to be removed.
227141564Sharti */
228141564Shartistatic void
229141564ShartiVarDestroy(Var *v, Boolean f)
230141564Sharti{
231141564Sharti
232141564Sharti	Buf_Destroy(v->val, f);
233141564Sharti	free(v->name);
234141564Sharti	free(v);
235141564Sharti}
236141564Sharti
237146045Sharti/**
238146046Sharti * Remove the tail of the given word and place the result in the given
239146046Sharti * buffer.
240146045Sharti *
241146045Sharti * Results:
242146045Sharti *	TRUE if characters were added to the buffer (a space needs to be
243146045Sharti *	added to the buffer before the next word).
244146045Sharti *
245146045Sharti * Side Effects:
246146045Sharti *	The trimmed word is added to the buffer.
247146045Sharti */
248146045Shartistatic Boolean
249146045ShartiVarHead(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
250146045Sharti{
251146045Sharti	char *slash;
252146045Sharti
253146045Sharti	slash = strrchr(word, '/');
254146045Sharti	if (slash != NULL) {
255146045Sharti		if (addSpace) {
256146045Sharti			Buf_AddByte(buf, (Byte)' ');
257146045Sharti		}
258146045Sharti		Buf_AppendRange(buf, word, slash);
259146045Sharti	} else {
260146045Sharti		/*
261146045Sharti		 * If no directory part, give . (q.v. the POSIX standard)
262146045Sharti		 */
263146045Sharti		if (addSpace) {
264146045Sharti			Buf_Append(buf, " .");
265146045Sharti		} else {
266146045Sharti			Buf_AddByte(buf, (Byte)'.');
267146045Sharti		}
268146045Sharti	}
269146045Sharti	return (TRUE);
270146045Sharti}
271146045Sharti
272146045Sharti/**
273146046Sharti * Remove the head of the given word and place the result in the given
274146046Sharti * buffer.
275146045Sharti *
276146045Sharti * Results:
277146045Sharti *	TRUE if characters were added to the buffer (a space needs to be
278146045Sharti *	added to the buffer before the next word).
279146045Sharti *
280146045Sharti * Side Effects:
281146045Sharti *	The trimmed word is added to the buffer.
282146045Sharti */
283146045Shartistatic Boolean
284146045ShartiVarTail(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
285146045Sharti{
286146045Sharti	const char *slash;
287146045Sharti
288146045Sharti	if (addSpace) {
289146045Sharti		Buf_AddByte (buf, (Byte)' ');
290146045Sharti	}
291146045Sharti
292146045Sharti	slash = strrchr(word, '/');
293146045Sharti	if (slash != NULL) {
294146045Sharti		slash++;
295146045Sharti		Buf_Append(buf, slash);
296146045Sharti	} else {
297146045Sharti		Buf_Append(buf, word);
298146045Sharti	}
299146045Sharti	return (TRUE);
300146045Sharti}
301146045Sharti
302146045Sharti/**
303146046Sharti * Place the suffix of the given word in the given buffer.
304146045Sharti *
305146045Sharti * Results:
306146045Sharti *	TRUE if characters were added to the buffer (a space needs to be
307146045Sharti *	added to the buffer before the next word).
308146045Sharti *
309146045Sharti * Side Effects:
310146045Sharti *	The suffix from the word is placed in the buffer.
311146045Sharti */
312146045Shartistatic Boolean
313146045ShartiVarSuffix(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
314146045Sharti{
315146045Sharti	const char *dot;
316146045Sharti
317146045Sharti	dot = strrchr(word, '.');
318146045Sharti	if (dot != NULL) {
319146045Sharti		if (addSpace) {
320146045Sharti			Buf_AddByte(buf, (Byte)' ');
321146045Sharti		}
322146045Sharti		dot++;
323146045Sharti		Buf_Append(buf, dot);
324146045Sharti		addSpace = TRUE;
325146045Sharti	}
326146045Sharti	return (addSpace);
327146045Sharti}
328146045Sharti
329146045Sharti/**
330146046Sharti * Remove the suffix of the given word and place the result in the
331146046Sharti * buffer.
332146045Sharti *
333146045Sharti * Results:
334146045Sharti *	TRUE if characters were added to the buffer (a space needs to be
335146045Sharti *	added to the buffer before the next word).
336146045Sharti *
337146045Sharti * Side Effects:
338146045Sharti *	The trimmed word is added to the buffer.
339146045Sharti */
340146045Shartistatic Boolean
341146045ShartiVarRoot(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
342146045Sharti{
343146045Sharti	char *dot;
344146045Sharti
345146045Sharti	if (addSpace) {
346146045Sharti		Buf_AddByte(buf, (Byte)' ');
347146045Sharti	}
348146045Sharti
349146045Sharti	dot = strrchr(word, '.');
350146045Sharti	if (dot != NULL) {
351146045Sharti		Buf_AppendRange(buf, word, dot);
352146045Sharti	} else {
353146045Sharti		Buf_Append(buf, word);
354146045Sharti	}
355146045Sharti	return (TRUE);
356146045Sharti}
357146045Sharti
358146045Sharti/**
359146046Sharti * Place the word in the buffer if it matches the given pattern.
360146046Sharti * Callback function for VarModify to implement the :M modifier.
361146046Sharti * A space will be added if requested.  A pattern is supplied
362146046Sharti * which the word must match.
363146045Sharti *
364146045Sharti * Results:
365146045Sharti *	TRUE if a space should be placed in the buffer before the next
366146045Sharti *	word.
367146045Sharti *
368146045Sharti * Side Effects:
369146045Sharti *	The word may be copied to the buffer.
370146045Sharti */
371146045Shartistatic Boolean
372146045ShartiVarMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern)
373146045Sharti{
374146045Sharti
375146045Sharti	if (Str_Match(word, pattern)) {
376146045Sharti		if (addSpace) {
377146045Sharti			Buf_AddByte(buf, (Byte)' ');
378146045Sharti		}
379146045Sharti		addSpace = TRUE;
380146045Sharti		Buf_Append(buf, word);
381146045Sharti	}
382146045Sharti	return (addSpace);
383146045Sharti}
384146045Sharti
385146045Sharti#ifdef SYSVVARSUB
386146045Sharti/**
387146046Sharti * Place the word in the buffer if it matches the given pattern.
388146046Sharti * Callback function for VarModify to implement the System V %
389146046Sharti * modifiers.  A space is added if requested.
390146045Sharti *
391146045Sharti * Results:
392146045Sharti *	TRUE if a space should be placed in the buffer before the next
393146045Sharti *	word.
394146045Sharti *
395146045Sharti * Side Effects:
396146045Sharti *	The word may be copied to the buffer.
397146045Sharti */
398146045Shartistatic Boolean
399146045ShartiVarSYSVMatch(const char *word, Boolean addSpace, Buffer *buf, void *patp)
400146045Sharti{
401146045Sharti	int		len;
402146045Sharti	const char	*ptr;
403146045Sharti	VarPattern	*pat = (VarPattern *)patp;
404146045Sharti
405146045Sharti	if (addSpace)
406146045Sharti		Buf_AddByte(buf, (Byte)' ');
407146045Sharti
408146045Sharti	addSpace = TRUE;
409146045Sharti
410146045Sharti	if ((ptr = Str_SYSVMatch(word, Buf_Data(pat->lhs), &len)) != NULL)
411146045Sharti		Str_SYSVSubst(buf, Buf_Data(pat->rhs), ptr, len);
412146045Sharti	else
413146045Sharti		Buf_Append(buf, word);
414146045Sharti
415146045Sharti	return (addSpace);
416146045Sharti}
417146045Sharti#endif
418146045Sharti
419146045Sharti/**
420146046Sharti * Place the word in the buffer if it doesn't match the given pattern.
421146046Sharti * Callback function for VarModify to implement the :N modifier.  A
422146046Sharti * space is added if requested.
423146045Sharti *
424146045Sharti * Results:
425146045Sharti *	TRUE if a space should be placed in the buffer before the next
426146045Sharti *	word.
427146045Sharti *
428146045Sharti * Side Effects:
429146045Sharti *	The word may be copied to the buffer.
430146045Sharti */
431146045Shartistatic Boolean
432146045ShartiVarNoMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern)
433146045Sharti{
434146045Sharti
435146045Sharti	if (!Str_Match(word, pattern)) {
436146045Sharti		if (addSpace) {
437146045Sharti			Buf_AddByte(buf, (Byte)' ');
438146045Sharti		}
439146045Sharti		addSpace = TRUE;
440146045Sharti		Buf_Append(buf, word);
441146045Sharti	}
442146045Sharti	return (addSpace);
443146045Sharti}
444146045Sharti
445146045Sharti/**
446146046Sharti * Perform a string-substitution on the given word, placing the
447146046Sharti * result in the passed buffer.  A space is added if requested.
448146045Sharti *
449146045Sharti * Results:
450146045Sharti *	TRUE if a space is needed before more characters are added.
451146045Sharti */
452146045Shartistatic Boolean
453146045ShartiVarSubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp)
454146045Sharti{
455146045Sharti	size_t		wordLen;	/* Length of word */
456146045Sharti	const char	*cp;		/* General pointer */
457146045Sharti	VarPattern	*pattern = patternp;
458146045Sharti
459146045Sharti	wordLen = strlen(word);
460146045Sharti	if (1) { /* substitute in each word of the variable */
461146045Sharti		/*
462146045Sharti		 * Break substitution down into simple anchored cases
463146045Sharti		 * and if none of them fits, perform the general substitution
464146045Sharti		 * case.
465146045Sharti		 */
466146045Sharti		if ((pattern->flags & VAR_MATCH_START) &&
467146045Sharti		   (strncmp(word, Buf_Data(pattern->lhs),
468146045Sharti		    Buf_Size(pattern->lhs)) == 0)) {
469146045Sharti			/*
470146045Sharti			 * Anchored at start and beginning of word matches
471146045Sharti			 * pattern.
472146045Sharti			 */
473146045Sharti			if ((pattern->flags & VAR_MATCH_END) &&
474146045Sharti			    (wordLen == Buf_Size(pattern->lhs))) {
475146045Sharti				/*
476146045Sharti				 * Also anchored at end and matches to the end
477146045Sharti				 * (word is same length as pattern) add space
478146045Sharti				 * and rhs only if rhs is non-null.
479146045Sharti				 */
480146045Sharti				if (Buf_Size(pattern->rhs) != 0) {
481146045Sharti					if (addSpace) {
482146045Sharti						Buf_AddByte(buf, (Byte)' ');
483146045Sharti					}
484146045Sharti					addSpace = TRUE;
485146045Sharti					Buf_AppendBuf(buf, pattern->rhs);
486146045Sharti				}
487146045Sharti
488146045Sharti			} else if (pattern->flags & VAR_MATCH_END) {
489146045Sharti				/*
490146045Sharti				 * Doesn't match to end -- copy word wholesale
491146045Sharti				 */
492146045Sharti				goto nosub;
493146045Sharti
494146045Sharti			} else {
495146045Sharti				/*
496146045Sharti				 * Matches at start but need to copy in
497146045Sharti				 * trailing characters.
498146045Sharti				 */
499146045Sharti				if ((Buf_Size(pattern->rhs) + wordLen -
500146045Sharti				    Buf_Size(pattern->lhs)) != 0) {
501146045Sharti					if (addSpace) {
502146045Sharti						Buf_AddByte(buf, (Byte)' ');
503146045Sharti					}
504146045Sharti					addSpace = TRUE;
505146045Sharti				}
506146045Sharti				Buf_AppendBuf(buf, pattern->rhs);
507146045Sharti				Buf_AddBytes(buf, wordLen -
508146045Sharti				    Buf_Size(pattern->lhs),
509146045Sharti				    (word + Buf_Size(pattern->lhs)));
510146045Sharti			}
511146045Sharti
512146045Sharti		} else if (pattern->flags & VAR_MATCH_START) {
513146045Sharti			/*
514146045Sharti			 * Had to match at start of word and didn't -- copy
515146045Sharti			 * whole word.
516146045Sharti			 */
517146045Sharti			goto nosub;
518146045Sharti
519146045Sharti		} else if (pattern->flags & VAR_MATCH_END) {
520146045Sharti			/*
521146045Sharti			 * Anchored at end, Find only place match could occur
522146045Sharti			 * (leftLen characters from the end of the word) and
523146045Sharti			 * see if it does. Note that because the $ will be
524146045Sharti			 * left at the end of the lhs, we have to use strncmp.
525146045Sharti			 */
526146045Sharti			cp = word + (wordLen - Buf_Size(pattern->lhs));
527146045Sharti			if ((cp >= word) && (strncmp(cp, Buf_Data(pattern->lhs),
528146045Sharti			    Buf_Size(pattern->lhs)) == 0)) {
529146045Sharti				/*
530146045Sharti				 * Match found. If we will place characters in
531146045Sharti				 * the buffer, add a space before hand as
532146045Sharti				 * indicated by addSpace, then stuff in the
533146045Sharti				 * initial, unmatched part of the word followed
534146045Sharti				 * by the right-hand-side.
535146045Sharti				 */
536146045Sharti				if ((cp - word) + Buf_Size(pattern->rhs) != 0) {
537146045Sharti					if (addSpace) {
538146045Sharti						Buf_AddByte(buf, (Byte)' ');
539146045Sharti					}
540146045Sharti					addSpace = TRUE;
541146045Sharti				}
542146045Sharti				Buf_AppendRange(buf, word, cp);
543146045Sharti				Buf_AppendBuf(buf, pattern->rhs);
544146045Sharti
545146045Sharti			} else {
546146045Sharti				/*
547146045Sharti				 * Had to match at end and didn't. Copy entire
548146045Sharti				 * word.
549146045Sharti				 */
550146045Sharti				goto nosub;
551146045Sharti			}
552146045Sharti		} else {
553146045Sharti			/*
554146045Sharti			 * Pattern is unanchored: search for the pattern in the
555146045Sharti			 * word using strstr(3), copying unmatched portions and
556146045Sharti			 * the right-hand-side for each match found, handling
557146045Sharti			 * non-global substitutions correctly, etc. When the
558146045Sharti			 * loop is done, any remaining part of the word (word
559146045Sharti			 * and wordLen are adjusted accordingly through the
560146045Sharti			 * loop) is copied straight into the buffer.
561146045Sharti			 * addSpace is set FALSE as soon as a space is added
562146045Sharti			 * to the buffer.
563146045Sharti			 */
564146045Sharti			Boolean done;
565146045Sharti			size_t origSize;
566146045Sharti
567146045Sharti			done = FALSE;
568146045Sharti			origSize = Buf_Size(buf);
569146045Sharti			while (!done) {
570146045Sharti				cp = strstr(word, Buf_Data(pattern->lhs));
571146045Sharti				if (cp != NULL) {
572146045Sharti					if (addSpace && (((cp - word) +
573146045Sharti					    Buf_Size(pattern->rhs)) != 0)) {
574146045Sharti						Buf_AddByte(buf, (Byte)' ');
575146045Sharti						addSpace = FALSE;
576146045Sharti					}
577146045Sharti					Buf_AppendRange(buf, word, cp);
578146045Sharti					Buf_AppendBuf(buf, pattern->rhs);
579146045Sharti					wordLen -= (cp - word) +
580146045Sharti					    Buf_Size(pattern->lhs);
581146045Sharti					word = cp + Buf_Size(pattern->lhs);
582146045Sharti					if (wordLen == 0 || (pattern->flags &
583146045Sharti					    VAR_SUB_GLOBAL) == 0) {
584146045Sharti						done = TRUE;
585146045Sharti					}
586146045Sharti				} else {
587146045Sharti					done = TRUE;
588146045Sharti				}
589146045Sharti			}
590146045Sharti			if (wordLen != 0) {
591146045Sharti				if (addSpace) {
592146045Sharti					Buf_AddByte(buf, (Byte)' ');
593146045Sharti				}
594146045Sharti				Buf_AddBytes(buf, wordLen, (const Byte *)word);
595146045Sharti			}
596146045Sharti
597146045Sharti			/*
598146045Sharti			 * If added characters to the buffer, need to add a
599146045Sharti			 * space before we add any more. If we didn't add any,
600146045Sharti			 * just return the previous value of addSpace.
601146045Sharti			 */
602146045Sharti			return ((Buf_Size(buf) != origSize) || addSpace);
603146045Sharti		}
604146045Sharti		/*
605146045Sharti		 * Common code for anchored substitutions:
606146045Sharti		 * addSpace was set TRUE if characters were added to the buffer.
607146045Sharti		 */
608146045Sharti		return (addSpace);
609146045Sharti	}
610146045Sharti  nosub:
611146045Sharti	if (addSpace) {
612146045Sharti		Buf_AddByte(buf, (Byte)' ');
613146045Sharti	}
614146045Sharti	Buf_AddBytes(buf, wordLen, (const Byte *)word);
615146045Sharti	return (TRUE);
616146045Sharti}
617146045Sharti
618146045Sharti/**
619146045Sharti * Print the error caused by a regcomp or regexec call.
620146045Sharti *
621146045Sharti * Side Effects:
622146045Sharti *	An error gets printed.
623146045Sharti */
624146045Shartistatic void
625146045ShartiVarREError(int err, regex_t *pat, const char *str)
626146045Sharti{
627146045Sharti	char   *errbuf;
628146045Sharti	int     errlen;
629146045Sharti
630146045Sharti	errlen = regerror(err, pat, 0, 0);
631146045Sharti	errbuf = emalloc(errlen);
632146045Sharti	regerror(err, pat, errbuf, errlen);
633146045Sharti	Error("%s: %s", str, errbuf);
634146045Sharti	free(errbuf);
635146045Sharti}
636146045Sharti
637146045Sharti
638146045Sharti/**
639146046Sharti * Perform a regex substitution on the given word, placing the
640146046Sharti * result in the passed buffer.  A space is added if requested.
641146045Sharti *
642146045Sharti * Results:
643146045Sharti *	TRUE if a space is needed before more characters are added.
644146045Sharti */
645146045Shartistatic Boolean
646146045ShartiVarRESubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp)
647146045Sharti{
648146045Sharti	VarPattern	*pat;
649146045Sharti	int		xrv;
650146045Sharti	const char	*wp;
651146045Sharti	char		*rp;
652146045Sharti	int		added;
653146045Sharti	int		flags = 0;
654146045Sharti
655146045Sharti#define	MAYBE_ADD_SPACE()			\
656146045Sharti	if (addSpace && !added)			\
657146045Sharti		Buf_AddByte(buf, (Byte)' ');	\
658146045Sharti	added = 1
659146045Sharti
660146045Sharti	added = 0;
661146045Sharti	wp = word;
662146045Sharti	pat = patternp;
663146045Sharti
664146045Sharti	if ((pat->flags & (VAR_SUB_ONE | VAR_SUB_MATCHED)) ==
665146045Sharti	    (VAR_SUB_ONE | VAR_SUB_MATCHED)) {
666146045Sharti		xrv = REG_NOMATCH;
667146045Sharti	} else {
668146045Sharti  tryagain:
669146045Sharti		xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags);
670146045Sharti	}
671146045Sharti
672146045Sharti	switch (xrv) {
673146045Sharti	  case 0:
674146045Sharti		pat->flags |= VAR_SUB_MATCHED;
675146045Sharti		if (pat->matches[0].rm_so > 0) {
676146045Sharti			MAYBE_ADD_SPACE();
677146045Sharti			Buf_AddBytes(buf, pat->matches[0].rm_so,
678146045Sharti			    (const Byte *)wp);
679146045Sharti		}
680146045Sharti
681146045Sharti		for (rp = Buf_Data(pat->rhs); *rp; rp++) {
682146045Sharti			if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
683146045Sharti				MAYBE_ADD_SPACE();
684146045Sharti				Buf_AddByte(buf, (Byte)rp[1]);
685146045Sharti				rp++;
686146045Sharti
687146045Sharti			} else if ((*rp == '&') ||
688146045Sharti			    ((*rp == '\\') && isdigit((unsigned char)rp[1]))) {
689146045Sharti				int	n;
690146045Sharti				const char *subbuf;
691146045Sharti				int	sublen;
692146045Sharti				char	errstr[3];
693146045Sharti
694146045Sharti				if (*rp == '&') {
695146045Sharti					n = 0;
696146045Sharti					errstr[0] = '&';
697146045Sharti					errstr[1] = '\0';
698146045Sharti				} else {
699146045Sharti					n = rp[1] - '0';
700146045Sharti					errstr[0] = '\\';
701146045Sharti					errstr[1] = rp[1];
702146045Sharti					errstr[2] = '\0';
703146045Sharti					rp++;
704146045Sharti				}
705146045Sharti
706146045Sharti				if (n > pat->nsub) {
707146045Sharti					Error("No subexpression %s",
708146045Sharti					    &errstr[0]);
709146045Sharti					subbuf = "";
710146045Sharti					sublen = 0;
711146045Sharti
712146045Sharti				} else if ((pat->matches[n].rm_so == -1) &&
713146045Sharti				    (pat->matches[n].rm_eo == -1)) {
714146045Sharti					Error("No match for subexpression %s",
715146045Sharti					    &errstr[0]);
716146045Sharti					subbuf = "";
717146045Sharti					sublen = 0;
718146045Sharti
719146045Sharti				} else {
720146045Sharti					subbuf = wp + pat->matches[n].rm_so;
721146045Sharti					sublen = pat->matches[n].rm_eo -
722146045Sharti					    pat->matches[n].rm_so;
723146045Sharti				}
724146045Sharti
725146045Sharti				if (sublen > 0) {
726146045Sharti					MAYBE_ADD_SPACE();
727146045Sharti					Buf_AddBytes(buf, sublen,
728146045Sharti					    (const Byte *)subbuf);
729146045Sharti				}
730146045Sharti			} else {
731146045Sharti				MAYBE_ADD_SPACE();
732146045Sharti				Buf_AddByte(buf, (Byte)*rp);
733146045Sharti			}
734146045Sharti		}
735146045Sharti		wp += pat->matches[0].rm_eo;
736146045Sharti		if (pat->flags & VAR_SUB_GLOBAL) {
737146045Sharti			flags |= REG_NOTBOL;
738146045Sharti			if (pat->matches[0].rm_so == 0 &&
739146045Sharti			    pat->matches[0].rm_eo == 0) {
740146045Sharti				MAYBE_ADD_SPACE();
741146045Sharti				Buf_AddByte(buf, (Byte)*wp);
742146045Sharti				wp++;
743146045Sharti			}
744146045Sharti			if (*wp)
745146045Sharti				goto tryagain;
746146045Sharti		}
747146045Sharti		if (*wp) {
748146045Sharti			MAYBE_ADD_SPACE();
749146045Sharti			Buf_Append(buf, wp);
750146045Sharti		}
751146045Sharti		break;
752146045Sharti
753146045Sharti	  default:
754146045Sharti		VarREError(xrv, &pat->re, "Unexpected regex error");
755146045Sharti		/* fall through */
756146045Sharti
757146045Sharti	  case REG_NOMATCH:
758146045Sharti		if (*wp) {
759146045Sharti			MAYBE_ADD_SPACE();
760146045Sharti			Buf_Append(buf, wp);
761146045Sharti		}
762146045Sharti		break;
763146045Sharti	}
764146045Sharti	return (addSpace || added);
765146045Sharti}
766146045Sharti
767146046Sharti/**
768143975Sharti * Find a variable in a variable list.
7691590Srgrimes */
770143975Shartistatic Var *
771143975ShartiVarLookup(Lst *vlist, const char *name)
7721590Srgrimes{
773143975Sharti	LstNode	*ln;
774138232Sharti
775143975Sharti	LST_FOREACH(ln, vlist)
776143975Sharti		if (strcmp(((const Var *)Lst_Datum(ln))->name, name) == 0)
777143975Sharti			return (Lst_Datum(ln));
778143975Sharti	return (NULL);
7791590Srgrimes}
7801590Srgrimes
781146046Sharti/**
782146046Sharti * Expand a variable name's embedded variables in the given context.
78398439Sjmallett *
78498439Sjmallett * Results:
78598441Sjmallett *	The contents of name, possibly expanded.
78698439Sjmallett */
787141268Shartistatic char *
788141268ShartiVarPossiblyExpand(const char *name, GNode *ctxt)
78998439Sjmallett{
790142457Sharti	Buffer	*buf;
791138232Sharti
792141268Sharti	if (strchr(name, '$') != NULL) {
793146027Sharti		buf = Var_Subst(name, ctxt, 0);
794143959Sharti		return (Buf_Peel(buf));
795142457Sharti	} else {
796143919Sharti		return estrdup(name);
797142457Sharti	}
79898439Sjmallett}
79998439Sjmallett
800145007Sharti/**
801145007Sharti * If the variable name begins with a '.', it could very well be
802145007Sharti * one of the local ones.  We check the name against all the local
803145007Sharti * variables and substitute the short version in for 'name' if it
804145007Sharti * matches one of them.
8051590Srgrimes */
806146046Shartistatic const char *
807145007ShartiVarLocal(const char name[])
8081590Srgrimes{
809143653Sharti	if (name[0] == '.') {
8101590Srgrimes		switch (name[1]) {
8111590Srgrimes		case 'A':
8121590Srgrimes			if (!strcmp(name, ".ALLSRC"))
813145007Sharti				return (ALLSRC);
8141590Srgrimes			if (!strcmp(name, ".ARCHIVE"))
815145007Sharti				return (ARCHIVE);
8161590Srgrimes			break;
8171590Srgrimes		case 'I':
8181590Srgrimes			if (!strcmp(name, ".IMPSRC"))
819145007Sharti				return (IMPSRC);
8201590Srgrimes			break;
8211590Srgrimes		case 'M':
8221590Srgrimes			if (!strcmp(name, ".MEMBER"))
823145007Sharti				return (MEMBER);
8241590Srgrimes			break;
8251590Srgrimes		case 'O':
8261590Srgrimes			if (!strcmp(name, ".OODATE"))
827145007Sharti				return (OODATE);
8281590Srgrimes			break;
8291590Srgrimes		case 'P':
8301590Srgrimes			if (!strcmp(name, ".PREFIX"))
831145007Sharti				return (PREFIX);
8321590Srgrimes			break;
8331590Srgrimes		case 'T':
8341590Srgrimes			if (!strcmp(name, ".TARGET"))
835145007Sharti				return (TARGET);
8361590Srgrimes			break;
837141461Sharti		default:
838141461Sharti			break;
8391590Srgrimes		}
840141461Sharti	}
841145007Sharti	return (name);
842145007Sharti}
84349332Shoek
844145007Sharti/**
845228992Suqs * Find the given variable in the given context and the environment.
846145007Sharti *
847145007Sharti * Results:
848145007Sharti *	A pointer to the structure describing the desired variable or
849145007Sharti *	NULL if the variable does not exist.
850145007Sharti */
851145007Shartistatic Var *
852145007ShartiVarFindEnv(const char name[], GNode *ctxt)
853145007Sharti{
854145007Sharti	Var	*var;
855145007Sharti
856145007Sharti	name = VarLocal(name);
857145007Sharti
858145007Sharti	if ((var = VarLookup(&ctxt->context, name)) != NULL)
859145007Sharti		return (var);
860145007Sharti
861145971Sharti	if ((var = VarLookup(&VAR_ENV->context, name)) != NULL)
862145971Sharti		return (var);
863145007Sharti
864145007Sharti	return (NULL);
865145007Sharti}
866145007Sharti
867145007Sharti/**
868145007Sharti * Look for the variable in the given context.
869145007Sharti */
870145007Shartistatic Var *
871145007ShartiVarFindOnly(const char name[], GNode *ctxt)
872145007Sharti{
873145007Sharti	Var	*var;
874145007Sharti
875145007Sharti	name = VarLocal(name);
876145007Sharti
877145007Sharti	if ((var = VarLookup(&ctxt->context, name)) != NULL)
878145007Sharti		return (var);
879145007Sharti
880145007Sharti	return (NULL);
881145007Sharti}
882145007Sharti
883145007Sharti/**
884145007Sharti * Look for the variable in all contexts.
885145007Sharti */
886145007Shartistatic Var *
887145007ShartiVarFindAny(const char name[], GNode *ctxt)
888145007Sharti{
889145007Sharti	Boolean	localCheckEnvFirst;
890145007Sharti	LstNode	*ln;
891145007Sharti	Var	*var;
892145007Sharti
893145007Sharti	name = VarLocal(name);
894145007Sharti
895141572Sharti	/*
896141572Sharti	 * Note whether this is one of the specific variables we were told
897141572Sharti	 * through the -E flag to use environment-variable-override for.
898141572Sharti	 */
899143975Sharti	localCheckEnvFirst = FALSE;
900143975Sharti	LST_FOREACH(ln, &envFirstVars) {
901143975Sharti		if (strcmp(Lst_Datum(ln), name) == 0) {
902143975Sharti			localCheckEnvFirst = TRUE;
903143975Sharti			break;
904143975Sharti		}
905141572Sharti	}
90649332Shoek
907141572Sharti	/*
908141572Sharti	 * First look for the variable in the given context. If it's not there,
909141572Sharti	 * look for it in VAR_CMD, VAR_GLOBAL and the environment,
910141572Sharti	 * in that order, depending on the FIND_* flags in 'flags'
911141572Sharti	 */
912145007Sharti	if ((var = VarLookup(&ctxt->context, name)) != NULL)
913143975Sharti		return (var);
9141590Srgrimes
915141572Sharti	/* not there - try command line context */
916145007Sharti	if (ctxt != VAR_CMD) {
917145007Sharti		if ((var = VarLookup(&VAR_CMD->context, name)) != NULL)
918143975Sharti			return (var);
919141572Sharti	}
9201590Srgrimes
921141572Sharti	/* not there - try global context, but only if not -e/-E */
922145007Sharti	if (ctxt != VAR_GLOBAL && (!checkEnvFirst && !localCheckEnvFirst)) {
923145007Sharti		if ((var = VarLookup(&VAR_GLOBAL->context, name)) != NULL)
924143975Sharti			return (var);
925141572Sharti	}
926141572Sharti
927145971Sharti	if ((var = VarLookup(&VAR_ENV->context, name)) != NULL)
928145971Sharti		return (var);
929141572Sharti
930141572Sharti	/* deferred check for the environment (in case of -e/-E) */
931145007Sharti	if ((ctxt != VAR_GLOBAL) && (checkEnvFirst || localCheckEnvFirst)) {
932145007Sharti		if ((var = VarLookup(&VAR_GLOBAL->context, name)) != NULL)
933143975Sharti			return (var);
934141572Sharti	}
935145007Sharti
936138264Sharti	return (NULL);
9371590Srgrimes}
9381590Srgrimes
939145971Sharti/**
940145971Sharti * Add a new variable of name name and value val to the given context.
9411590Srgrimes *
9421590Srgrimes * Side Effects:
9431590Srgrimes *	The new variable is placed at the front of the given context
9441590Srgrimes *	The name and val arguments are duplicated so they may
9451590Srgrimes *	safely be freed.
9461590Srgrimes */
947186713Sobrienstatic Var *
948141252ShartiVarAdd(const char *name, const char *val, GNode *ctxt)
9491590Srgrimes{
950186713Sobrien	Var *v;
9511590Srgrimes
952186713Sobrien	Lst_AtFront(&ctxt->context, v = VarCreate(name, val, 0));
953143813Sharti	DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, name, val));
954186713Sobrien	return (v);
9551590Srgrimes}
9561590Srgrimes
957146046Sharti/**
958146046Sharti * Remove a variable from a context.
9591590Srgrimes *
9601590Srgrimes * Side Effects:
9611590Srgrimes *	The Var structure is removed and freed.
9621590Srgrimes */
9631590Srgrimesvoid
964141268ShartiVar_Delete(const char *name, GNode *ctxt)
9651590Srgrimes{
966143813Sharti	LstNode *ln;
9671590Srgrimes
968143813Sharti	DEBUGF(VAR, ("%s:delete %s\n", ctxt->name, name));
969143975Sharti	LST_FOREACH(ln, &ctxt->context) {
970143975Sharti		if (strcmp(((const Var *)Lst_Datum(ln))->name, name) == 0) {
971143975Sharti			VarDestroy(Lst_Datum(ln), TRUE);
972143975Sharti			Lst_Remove(&ctxt->context, ln);
973143975Sharti			break;
974143975Sharti		}
975143813Sharti	}
9761590Srgrimes}
9771590Srgrimes
978146046Sharti/**
979146046Sharti * Set the variable name to the value val in the given context.
9801590Srgrimes *
9811590Srgrimes * Side Effects:
9821590Srgrimes *	If the variable doesn't yet exist, a new record is created for it.
9831590Srgrimes *	Else the old value is freed and the new one stuck in its place
9841590Srgrimes *
9851590Srgrimes * Notes:
9861590Srgrimes *	The variable is searched for only in its context before being
9871590Srgrimes *	created in that context. I.e. if the context is VAR_GLOBAL,
9881590Srgrimes *	only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only
9891590Srgrimes *	VAR_CMD->context is searched. This is done to avoid the literally
9901590Srgrimes *	thousands of unnecessary strcmp's that used to be done to
9911590Srgrimes *	set, say, $(@) or $(<).
9921590Srgrimes */
9931590Srgrimesvoid
994141268ShartiVar_Set(const char *name, const char *val, GNode *ctxt)
9951590Srgrimes{
996143813Sharti	Var    *v;
997143813Sharti	char   *n;
9981590Srgrimes
999143813Sharti	/*
1000143813Sharti	 * We only look for a variable in the given context since anything
1001143813Sharti	 * set here will override anything in a lower context, so there's not
1002143813Sharti	 * much point in searching them all just to save a bit of memory...
1003143813Sharti	 */
1004143813Sharti	n = VarPossiblyExpand(name, ctxt);
1005145007Sharti	v = VarFindOnly(n, ctxt);
1006143813Sharti	if (v == NULL) {
1007186713Sobrien		v = VarAdd(n, val, ctxt);
1008143813Sharti	} else {
1009143813Sharti		Buf_Clear(v->val);
1010143813Sharti		Buf_Append(v->val, val);
1011160450Sobrien		DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, n, val));
1012143813Sharti	}
1013145971Sharti
1014186713Sobrien	if (ctxt == VAR_CMD || (v->flags & VAR_TO_ENV)) {
1015186713Sobrien		/*
1016186713Sobrien		 * Any variables given on the command line
1017186713Sobrien		 * are automatically exported to the
1018186713Sobrien		 * environment (as per POSIX standard)
1019186713Sobrien		 */
1020186713Sobrien		setenv(n, val, 1);
1021186713Sobrien	}
1022186713Sobrien
1023143813Sharti	free(n);
10241590Srgrimes}
10251590Srgrimes
1026146046Sharti/**
1027146145Sharti * Set the a global name variable to the value.
1028146145Sharti */
1029146145Shartivoid
1030146145ShartiVar_SetGlobal(const char name[], const char value[])
1031146145Sharti{
1032146145Sharti
1033146145Sharti	Var_Set(name, value, VAR_GLOBAL);
1034146145Sharti}
1035146145Sharti
1036146145Sharti
1037146145Sharti/**
1038146046Sharti * Set the VAR_TO_ENV flag on a variable
1039145971Sharti */
1040145971Shartivoid
1041145971ShartiVar_SetEnv(const char *name, GNode *ctxt)
1042145971Sharti{
1043145971Sharti	Var    *v;
1044145971Sharti
1045145971Sharti	v = VarFindOnly(name, VAR_CMD);
1046145971Sharti	if (v != NULL) {
1047145971Sharti		/*
1048145971Sharti		 * Do not allow .EXPORT: to be set on variables
1049145971Sharti		 * from the comand line or MAKEFLAGS.
1050145971Sharti		 */
1051145971Sharti		Error(
1052145971Sharti		    "Warning: Did not set .EXPORTVAR: on %s because it "
1053145971Sharti		    "is from the comand line or MAKEFLAGS", name);
1054145971Sharti		return;
1055145971Sharti	}
1056145971Sharti
1057145971Sharti	v = VarFindAny(name, ctxt);
1058145971Sharti	if (v == NULL) {
1059145971Sharti		Lst_AtFront(&VAR_ENV->context,
1060145971Sharti		    VarCreate(name, NULL, VAR_TO_ENV));
1061145971Sharti		setenv(name, "", 1);
1062145971Sharti		Error("Warning: .EXPORTVAR: set on undefined variable %s", name);
1063145971Sharti	} else {
1064145971Sharti		if ((v->flags & VAR_TO_ENV) == 0) {
1065145971Sharti			v->flags |= VAR_TO_ENV;
1066145971Sharti			setenv(v->name, Buf_Data(v->val), 1);
1067145971Sharti		}
1068145971Sharti	}
1069145971Sharti}
1070145971Sharti
1071146046Sharti/**
1072146046Sharti * The variable of the given name has the given value appended to it in
1073146046Sharti * the given context.
10741590Srgrimes *
10751590Srgrimes * Side Effects:
10761590Srgrimes *	If the variable doesn't exist, it is created. Else the strings
10771590Srgrimes *	are concatenated (with a space in between).
10781590Srgrimes *
10791590Srgrimes * Notes:
10801590Srgrimes *	Only if the variable is being sought in the global context is the
10811590Srgrimes *	environment searched.
10821590Srgrimes *	XXX: Knows its calling circumstances in that if called with ctxt
10831590Srgrimes *	an actual target, it will only search that context since only
10841590Srgrimes *	a local variable could be being appended to. This is actually
10851590Srgrimes *	a big win and must be tolerated.
10861590Srgrimes */
10871590Srgrimesvoid
1088141268ShartiVar_Append(const char *name, const char *val, GNode *ctxt)
10891590Srgrimes{
1090143813Sharti	Var	*v;
1091143813Sharti	char	*n;
10921590Srgrimes
1093143813Sharti	n = VarPossiblyExpand(name, ctxt);
1094145007Sharti	if (ctxt == VAR_GLOBAL) {
1095145007Sharti		v = VarFindEnv(n, ctxt);
1096145007Sharti	} else {
1097145007Sharti		v = VarFindOnly(n, ctxt);
1098145007Sharti	}
1099143813Sharti	if (v == NULL) {
1100143813Sharti		VarAdd(n, val, ctxt);
1101143813Sharti	} else {
1102143813Sharti		Buf_AddByte(v->val, (Byte)' ');
1103143813Sharti		Buf_Append(v->val, val);
1104143959Sharti		DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, n, Buf_Data(v->val)));
11051590Srgrimes	}
1106143813Sharti	free(n);
11071590Srgrimes}
11081590Srgrimes
1109146046Sharti/**
1110146046Sharti * See if the given variable exists.
11111590Srgrimes *
11121590Srgrimes * Results:
11131590Srgrimes *	TRUE if it does, FALSE if it doesn't
11141590Srgrimes */
11151590SrgrimesBoolean
1116141268ShartiVar_Exists(const char *name, GNode *ctxt)
11171590Srgrimes{
1118143813Sharti	Var	*v;
1119143813Sharti	char	*n;
11201590Srgrimes
1121143813Sharti	n = VarPossiblyExpand(name, ctxt);
1122145007Sharti	v = VarFindAny(n, ctxt);
1123143813Sharti	if (v == NULL) {
1124143968Sharti		free(n);
1125143813Sharti		return (FALSE);
1126143968Sharti	} else {
1127143968Sharti		free(n);
1128143968Sharti		return (TRUE);
1129143813Sharti	}
11301590Srgrimes}
11311590Srgrimes
1132146046Sharti/**
1133146046Sharti * Return the value of the named variable in the given context
11341590Srgrimes *
11351590Srgrimes * Results:
1136146580Sharti *	The value if the variable exists, NULL if it doesn't.
11371590Srgrimes */
1138146581Sharticonst char *
1139146580ShartiVar_Value(const char name[], GNode *ctxt)
11401590Srgrimes{
1141143813Sharti	Var	*v;
1142143813Sharti	char	*n;
11431590Srgrimes
1144143813Sharti	n = VarPossiblyExpand(name, ctxt);
1145145007Sharti	v = VarFindAny(n, ctxt);
1146146580Sharti	free(n);
1147143968Sharti	if (v == NULL) {
1148146580Sharti		return (NULL);
1149143813Sharti	} else {
1150146580Sharti		return (Buf_Data(v->val));
11515814Sjkh	}
11521590Srgrimes}
11531590Srgrimes
1154146046Sharti/**
1155146046Sharti * Modify each of the words of the passed string using the given
1156146046Sharti * function. Used to implement all modifiers.
11571590Srgrimes *
11581590Srgrimes * Results:
11591590Srgrimes *	A string of all the words modified appropriately.
11601590Srgrimes *
11611590Srgrimes * Side Effects:
1162143255Sharti *	Uses brk_string() so it invalidates any previous call to
1163143255Sharti *	brk_string().
11641590Srgrimes */
11651590Srgrimesstatic char *
1166143255ShartiVarModify(const char *str, VarModifyProc *modProc, void *datum)
11671590Srgrimes{
1168146345Sharti	ArgArray	aa;
1169146345Sharti	Buffer		*buf;		/* Buffer for the new string */
1170146345Sharti	int		i;
1171146345Sharti	Boolean		addSpace;	/*
1172146345Sharti					 * TRUE if need to add a space to
1173146345Sharti					 * the buffer before adding the
1174146345Sharti					 * trimmed word
1175146345Sharti					 */
11765814Sjkh
1177146345Sharti	brk_string(&aa, str, FALSE);
1178143255Sharti
1179146345Sharti	addSpace = FALSE;
1180143254Sharti	buf = Buf_Init(0);
1181146345Sharti	for (i = 1; i < aa.argc; i++)
1182146345Sharti		addSpace = (*modProc)(aa.argv[i], addSpace, buf, datum);
1183143255Sharti
1184146345Sharti	ArgArray_Done(&aa);
1185143959Sharti	return (Buf_Peel(buf));
11861590Srgrimes}
11871590Srgrimes
1188146046Sharti/**
1189146046Sharti * Sort the words in the string.
1190120184Smarcel *
1191120184Smarcel * Input:
1192120184Smarcel *	str		String whose words should be sorted
1193120184Smarcel *	cmp		A comparison function to control the ordering
1194120184Smarcel *
1195120184Smarcel * Results:
1196120184Smarcel *	A string containing the words sorted
1197120184Smarcel */
1198120184Smarcelstatic char *
1199143255ShartiVarSortWords(const char *str, int (*cmp)(const void *, const void *))
1200120184Smarcel{
1201146345Sharti	ArgArray	aa;
1202146345Sharti	Buffer		*buf;
1203146345Sharti	int		i;
1204120184Smarcel
1205146345Sharti	brk_string(&aa, str, FALSE);
1206146345Sharti	qsort(aa.argv + 1, aa.argc - 1, sizeof(char *), cmp);
1207143255Sharti
1208143255Sharti	buf = Buf_Init(0);
1209146345Sharti	for (i = 1; i < aa.argc; i++) {
1210146345Sharti		Buf_Append(buf, aa.argv[i]);
1211146345Sharti		Buf_AddByte(buf, (Byte)((i < aa.argc - 1) ? ' ' : '\0'));
1212120184Smarcel	}
1213143255Sharti
1214146345Sharti	ArgArray_Done(&aa);
1215143959Sharti	return (Buf_Peel(buf));
1216120184Smarcel}
1217120184Smarcel
1218120184Smarcelstatic int
1219120184SmarcelSortIncreasing(const void *l, const void *r)
1220120184Smarcel{
1221138232Sharti
1222120184Smarcel	return (strcmp(*(const char* const*)l, *(const char* const*)r));
1223120184Smarcel}
1224120184Smarcel
1225146046Sharti/**
1226157588Sfjoe * Remove adjacent duplicate words.
1227157588Sfjoe *
1228157588Sfjoe * Results:
1229157588Sfjoe *	A string containing the resulting words.
1230157588Sfjoe */
1231157588Sfjoestatic char *
1232157588SfjoeVarUniq(const char *str)
1233157588Sfjoe{
1234157588Sfjoe	ArgArray	aa;
1235157588Sfjoe	Buffer		*buf;		    /* Buffer for new string */
1236157588Sfjoe	int		i, j;
1237157588Sfjoe
1238157588Sfjoe	buf = Buf_Init(0);
1239157588Sfjoe	brk_string(&aa, str, FALSE);
1240157588Sfjoe
1241157588Sfjoe	if (aa.argc > 2) {
1242157588Sfjoe		for (j = 1, i = 2; i < aa.argc; i++) {
1243157588Sfjoe			if (strcmp(aa.argv[i], aa.argv[j]) != 0 && (++j != i))
1244157588Sfjoe				aa.argv[j] = aa.argv[i];
1245157588Sfjoe		}
1246157588Sfjoe		aa.argc = j + 1;
1247157588Sfjoe	}
1248157588Sfjoe
1249157588Sfjoe	for (i = 1; i < aa.argc; i++) {
1250157588Sfjoe		Buf_AddBytes(buf, strlen(aa.argv[i]), (Byte *)aa.argv[i]);
1251157588Sfjoe		if (i != aa.argc - 1)
1252157588Sfjoe			Buf_AddByte(buf, ' ');
1253157588Sfjoe	}
1254157588Sfjoe	Buf_AddByte(buf, '\0');
1255157588Sfjoe
1256157588Sfjoe	ArgArray_Done(&aa);
1257157588Sfjoe	return (Buf_Peel(buf));
1258157588Sfjoe}
1259157588Sfjoe
1260157588Sfjoe/**
1261146046Sharti * Pass through the tstr looking for 1) escaped delimiters,
1262146046Sharti * '$'s and backslashes (place the escaped character in
1263146046Sharti * uninterpreted) and 2) unescaped $'s that aren't before
1264146046Sharti * the delimiter (expand the variable substitution).
1265146046Sharti * Return the expanded string or NULL if the delimiter was missing
1266146046Sharti * If pattern is specified, handle escaped ampersands, and replace
1267146046Sharti * unescaped ampersands with the lhs of the pattern.
126866853Swill *
126966853Swill * Results:
127066853Swill *	A string of all the words modified appropriately.
127166853Swill *	If length is specified, return the string length of the buffer
127266853Swill *	If flags is specified and the last character of the pattern is a
127366853Swill *	$ set the VAR_MATCH_END bit of flags.
127466853Swill */
1275143966Shartistatic Buffer *
1276143966ShartiVarGetPattern(VarParser *vp, int delim, int *flags, VarPattern *patt)
127766853Swill{
1278143955Sharti	Buffer		*buf;
1279138232Sharti
1280143955Sharti	buf = Buf_Init(0);
128166853Swill
1282143813Sharti	/*
1283143813Sharti	 * Skim through until the matching delimiter is found; pick up
1284143813Sharti	 * variable substitutions on the way. Also allow backslashes to quote
1285143813Sharti	 * the delimiter, $, and \, but don't touch other backslashes.
1286143813Sharti	 */
1287143955Sharti	while (*vp->ptr != '\0') {
1288143955Sharti		if (*vp->ptr == delim) {
1289143966Sharti			return (buf);
1290143955Sharti
1291143955Sharti		} else if ((vp->ptr[0] == '\\') &&
1292143955Sharti		    ((vp->ptr[1] == delim) ||
1293143955Sharti		     (vp->ptr[1] == '\\') ||
1294143955Sharti		     (vp->ptr[1] == '$') ||
1295143955Sharti		     (vp->ptr[1] == '&' && patt != NULL))) {
1296143955Sharti			vp->ptr++;		/* consume backslash */
1297143955Sharti			Buf_AddByte(buf, (Byte)vp->ptr[0]);
1298143955Sharti			vp->ptr++;
1299143955Sharti
1300143955Sharti		} else if (vp->ptr[0] == '$') {
1301143955Sharti			if (vp->ptr[1] == delim) {
1302143955Sharti				if (flags == NULL) {
1303143955Sharti					Buf_AddByte(buf, (Byte)vp->ptr[0]);
1304143955Sharti					vp->ptr++;
1305143955Sharti				} else {
1306143813Sharti					/*
1307143955Sharti					 * Unescaped $ at end of patt =>
1308143955Sharti					 * anchor patt at end.
1309143813Sharti					 */
1310143813Sharti					*flags |= VAR_MATCH_END;
1311143955Sharti					vp->ptr++;
1312143955Sharti				}
1313143813Sharti			} else {
1314143970Sharti				VarParser	subvp = {
1315143970Sharti					vp->ptr,
1316143970Sharti					vp->ptr,
1317143970Sharti					vp->ctxt,
1318146038Sharti					vp->err,
1319146038Sharti					vp->execute
1320143970Sharti				};
1321143969Sharti				char   *rval;
1322143969Sharti				Boolean rfree;
132366853Swill
1324143955Sharti				/*
1325143955Sharti				 * If unescaped dollar sign not
1326143955Sharti				 * before the delimiter, assume it's
1327143955Sharti				 * a variable substitution and
1328143955Sharti				 * recurse.
1329143955Sharti				 */
1330143970Sharti				rval = VarParse(&subvp, &rfree);
1331143969Sharti				Buf_Append(buf, rval);
1332143969Sharti				if (rfree)
1333143969Sharti					free(rval);
1334143970Sharti				vp->ptr = subvp.ptr;
133566853Swill			}
1336143955Sharti		} else if (vp->ptr[0] == '&' && patt != NULL) {
1337143966Sharti			Buf_AppendBuf(buf, patt->lhs);
1338143955Sharti			vp->ptr++;
1339143955Sharti		} else {
1340143955Sharti			Buf_AddByte(buf, (Byte)vp->ptr[0]);
1341143955Sharti			vp->ptr++;
1342143955Sharti		}
134366853Swill	}
134466853Swill
1345143955Sharti	Buf_Destroy(buf, TRUE);
1346143955Sharti	return (NULL);
134766853Swill}
134866853Swill
1349143811Sharti/**
1350142557Sharti * Make sure this variable is fully expanded.
13511590Srgrimes */
1352142557Shartistatic char *
1353143811ShartiVarExpand(Var *v, VarParser *vp)
13541590Srgrimes{
1355142557Sharti	char	*value;
1356142557Sharti	char	*result;
1357142557Sharti
1358142557Sharti	if (v->flags & VAR_IN_USE) {
1359142557Sharti		Fatal("Variable %s is recursive.", v->name);
1360142557Sharti		/* NOTREACHED */
1361142557Sharti	}
1362142557Sharti
1363142557Sharti	v->flags |= VAR_IN_USE;
1364142557Sharti
1365142557Sharti	/*
1366142557Sharti	 * Before doing any modification, we have to make sure the
1367142557Sharti	 * value has been fully expanded. If it looks like recursion
1368142557Sharti	 * might be necessary (there's a dollar sign somewhere in the
1369142557Sharti	 * variable's value) we just call Var_Subst to do any other
1370142557Sharti	 * substitutions that are necessary. Note that the value
1371142557Sharti	 * returned by Var_Subst will have been
1372142557Sharti	 * dynamically-allocated, so it will need freeing when we
1373142557Sharti	 * return.
1374142557Sharti	 */
1375143959Sharti	value = Buf_Data(v->val);
1376142557Sharti	if (strchr(value, '$') == NULL) {
1377142557Sharti		result = strdup(value);
1378142557Sharti	} else {
1379142557Sharti		Buffer	*buf;
1380142557Sharti
1381146027Sharti		buf = Var_Subst(value, vp->ctxt, vp->err);
1382143959Sharti		result = Buf_Peel(buf);
1383142557Sharti	}
1384142557Sharti
1385142557Sharti	v->flags &= ~VAR_IN_USE;
1386142557Sharti
1387142557Sharti	return (result);
1388142557Sharti}
1389142557Sharti
1390143240Sharti/**
1391143812Sharti * Select only those words in value that match the modifier.
1392143240Sharti */
1393143240Shartistatic char *
1394143914Shartimodifier_M(VarParser *vp, const char value[], char endc)
1395143240Sharti{
1396143914Sharti	char	*patt;
1397143914Sharti	char	*ptr;
1398143914Sharti	char	*newValue;
1399143914Sharti	char	modifier;
1400143240Sharti
1401143914Sharti	modifier = vp->ptr[0];
1402143914Sharti	vp->ptr++;	/* consume 'M' or 'N' */
1403143914Sharti
1404143240Sharti	/*
1405143240Sharti	 * Compress the \:'s out of the pattern, so allocate enough
1406143240Sharti	 * room to hold the uncompressed pattern and compress the
1407143656Sharti	 * pattern into that space.
1408143240Sharti	 */
1409143914Sharti	patt = estrdup(vp->ptr);
1410143240Sharti	ptr = patt;
1411143914Sharti	while (vp->ptr[0] != '\0') {
1412143967Sharti		if (vp->ptr[0] == endc || vp->ptr[0] == ':') {
1413143656Sharti			break;
1414143656Sharti		}
1415143967Sharti		if (vp->ptr[0] == '\\' &&
1416143967Sharti		    (vp->ptr[1] == endc || vp->ptr[1] == ':')) {
1417143919Sharti			vp->ptr++;	/* consume backslash */
1418143240Sharti		}
1419143914Sharti		*ptr = vp->ptr[0];
1420143240Sharti		ptr++;
1421143914Sharti		vp->ptr++;
1422143240Sharti	}
1423143240Sharti	*ptr = '\0';
1424241280Savg	DEBUGF(VAR, ("Pattern :%s\n", patt));
1425143240Sharti
1426143914Sharti	if (modifier == 'M') {
1427143240Sharti		newValue = VarModify(value, VarMatch, patt);
1428143240Sharti	} else {
1429143240Sharti		newValue = VarModify(value, VarNoMatch, patt);
1430143240Sharti	}
1431143240Sharti	free(patt);
1432143240Sharti
1433143240Sharti	return (newValue);
1434143240Sharti}
1435143240Sharti
1436143812Sharti/**
1437143812Sharti * Substitute the replacement string for the pattern.  The substitution
1438143812Sharti * is applied to each word in value.
1439143812Sharti */
1440143252Shartistatic char *
1441143914Shartimodifier_S(VarParser *vp, const char value[], Var *v)
1442143252Sharti{
1443143955Sharti	VarPattern	patt;
1444143252Sharti	char		delim;
1445143252Sharti	char		*newValue;
1446143240Sharti
1447143955Sharti	patt.flags = 0;
1448143252Sharti
1449143920Sharti	vp->ptr++;		/* consume 'S' */
1450143252Sharti
1451143920Sharti	delim = *vp->ptr;	/* used to find end of pattern */
1452143920Sharti	vp->ptr++;		/* consume 1st delim */
1453143920Sharti
1454143252Sharti	/*
1455143252Sharti	 * If pattern begins with '^', it is anchored to the start of the
1456143252Sharti	 * word -- skip over it and flag pattern.
1457143252Sharti	 */
1458143920Sharti	if (*vp->ptr == '^') {
1459143955Sharti		patt.flags |= VAR_MATCH_START;
1460143920Sharti		vp->ptr++;
1461143252Sharti	}
1462143252Sharti
1463143966Sharti	patt.lhs = VarGetPattern(vp, delim, &patt.flags, NULL);
1464143955Sharti	if (patt.lhs == NULL) {
1465143955Sharti		/*
1466143955Sharti		 * LHS didn't end with the delim, complain and exit.
1467143955Sharti		 */
1468143955Sharti		Fatal("Unclosed substitution for %s (%c missing)",
1469143955Sharti		    v->name, delim);
1470143955Sharti	}
1471143252Sharti
1472143955Sharti	vp->ptr++;	/* consume 2nd delim */
1473143252Sharti
1474143966Sharti	patt.rhs = VarGetPattern(vp, delim, NULL, &patt);
1475143955Sharti	if (patt.rhs == NULL) {
1476143955Sharti		/*
1477143955Sharti		 * RHS didn't end with the delim, complain and exit.
1478143955Sharti		 */
1479143955Sharti		Fatal("Unclosed substitution for %s (%c missing)",
1480143955Sharti		    v->name, delim);
1481143252Sharti	}
1482143252Sharti
1483143920Sharti	vp->ptr++;	/* consume last delim */
1484143252Sharti
1485143252Sharti	/*
1486143252Sharti	 * Check for global substitution. If 'g' after the final delimiter,
1487143252Sharti	 * substitution is global and is marked that way.
1488143252Sharti	 */
1489143920Sharti	if (vp->ptr[0] == 'g') {
1490143955Sharti		patt.flags |= VAR_SUB_GLOBAL;
1491143920Sharti		vp->ptr++;
1492143252Sharti	}
1493143252Sharti
1494143252Sharti	/*
1495143252Sharti	 * Global substitution of the empty string causes an infinite number
1496143252Sharti	 * of matches, unless anchored by '^' (start of string) or '$' (end
1497143252Sharti	 * of string). Catch the infinite substitution here. Note that flags
1498143252Sharti	 * can only contain the 3 bits we're interested in so we don't have
1499143252Sharti	 * to mask unrelated bits. We can test for equality.
1500143252Sharti	 */
1501143966Sharti	if (Buf_Size(patt.lhs) == 0 && patt.flags == VAR_SUB_GLOBAL)
1502143252Sharti		Fatal("Global substitution of the empty string");
1503143252Sharti
1504143955Sharti	newValue = VarModify(value, VarSubstitute, &patt);
1505143252Sharti
1506143252Sharti	/*
1507143252Sharti	 * Free the two strings.
1508143252Sharti	 */
1509143955Sharti	free(patt.lhs);
1510143955Sharti	free(patt.rhs);
1511143252Sharti
1512143252Sharti	return (newValue);
1513143252Sharti}
1514143252Sharti
1515143812Shartistatic char *
1516143914Shartimodifier_C(VarParser *vp, char value[], Var *v)
1517143812Sharti{
1518143964Sharti	VarPattern	patt;
1519143913Sharti	char		delim;
1520143812Sharti	int		error;
1521143812Sharti	char		*newValue;
1522143812Sharti
1523143812Sharti	patt.flags = 0;
1524143812Sharti
1525143920Sharti	vp->ptr++;		/* consume 'C' */
1526143812Sharti
1527143920Sharti	delim = *vp->ptr;	/* delimiter between sections */
1528143812Sharti
1529143920Sharti	vp->ptr++;		/* consume 1st delim */
1530143920Sharti
1531143966Sharti	patt.lhs = VarGetPattern(vp, delim, NULL, NULL);
1532143965Sharti	if (patt.lhs == NULL) {
1533143812Sharti		Fatal("Unclosed substitution for %s (%c missing)",
1534143813Sharti		     v->name, delim);
1535143812Sharti	}
1536143812Sharti
1537143955Sharti	vp->ptr++;		/* consume 2st delim */
1538143955Sharti
1539143966Sharti	patt.rhs = VarGetPattern(vp, delim, NULL, NULL);
1540143964Sharti	if (patt.rhs == NULL) {
1541143812Sharti		Fatal("Unclosed substitution for %s (%c missing)",
1542143813Sharti		     v->name, delim);
1543143812Sharti	}
1544143812Sharti
1545143955Sharti	vp->ptr++;		/* consume last delim */
1546143955Sharti
1547143920Sharti	switch (*vp->ptr) {
1548143920Sharti	case 'g':
1549143920Sharti		patt.flags |= VAR_SUB_GLOBAL;
1550143920Sharti		vp->ptr++;		/* consume 'g' */
1551143812Sharti		break;
1552143920Sharti	case '1':
1553143920Sharti		patt.flags |= VAR_SUB_ONE;
1554143920Sharti		vp->ptr++;		/* consume '1' */
1555143920Sharti		break;
1556143920Sharti	default:
1557143920Sharti		break;
1558143812Sharti	}
1559143812Sharti
1560143966Sharti	error = regcomp(&patt.re, Buf_Data(patt.lhs), REG_EXTENDED);
1561143812Sharti	if (error) {
1562143812Sharti		VarREError(error, &patt.re, "RE substitution error");
1563143964Sharti		free(patt.rhs);
1564143965Sharti		free(patt.lhs);
1565143812Sharti		return (var_Error);
1566143812Sharti	}
1567143812Sharti
1568143812Sharti	patt.nsub = patt.re.re_nsub + 1;
1569143812Sharti	if (patt.nsub < 1)
1570143812Sharti		patt.nsub = 1;
1571143812Sharti	if (patt.nsub > 10)
1572143812Sharti		patt.nsub = 10;
1573143812Sharti	patt.matches = emalloc(patt.nsub * sizeof(regmatch_t));
1574143812Sharti
1575143812Sharti	newValue = VarModify(value, VarRESubstitute, &patt);
1576143812Sharti
1577143812Sharti	regfree(&patt.re);
1578143812Sharti	free(patt.matches);
1579143964Sharti	free(patt.rhs);
1580143965Sharti	free(patt.lhs);
1581143812Sharti
1582143812Sharti	return (newValue);
1583143812Sharti}
1584143812Sharti
1585143918Shartistatic char *
1586143918ShartisysVvarsub(VarParser *vp, char startc, Var *v, const char value[])
1587143918Sharti{
1588143918Sharti#ifdef SYSVVARSUB
1589143918Sharti	/*
1590143918Sharti	 * This can either be a bogus modifier or a System-V substitution
1591143918Sharti	 * command.
1592143918Sharti	 */
1593143918Sharti	char		endc;
1594143918Sharti	VarPattern	patt;
1595143918Sharti	Boolean		eqFound;
1596143918Sharti	int		cnt;
1597143918Sharti	char		*newStr;
1598143918Sharti	const char	*cp;
1599143918Sharti
1600143918Sharti	endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE;
1601143918Sharti
1602143918Sharti	patt.flags = 0;
1603143918Sharti
1604143918Sharti	/*
1605143918Sharti	 * First we make a pass through the string trying to verify it is a
1606143918Sharti	 * SYSV-make-style translation: it must be: <string1>=<string2>)
1607143918Sharti	 */
1608143955Sharti	eqFound = FALSE;
1609143918Sharti	cp = vp->ptr;
1610143918Sharti	cnt = 1;
1611143918Sharti	while (*cp != '\0' && cnt) {
1612143918Sharti		if (*cp == '=') {
1613143918Sharti			eqFound = TRUE;
1614143918Sharti			/* continue looking for endc */
1615143918Sharti		} else if (*cp == endc)
1616143918Sharti			cnt--;
1617143918Sharti		else if (*cp == startc)
1618143918Sharti			cnt++;
1619143918Sharti		if (cnt)
1620143918Sharti			cp++;
1621143918Sharti	}
1622143918Sharti
1623143918Sharti	if (*cp == endc && eqFound) {
1624143918Sharti		/*
1625143918Sharti		 * Now we break this sucker into the lhs and rhs.
1626143918Sharti		 */
1627143966Sharti		patt.lhs = VarGetPattern(vp, '=', &patt.flags, NULL);
1628143918Sharti		if (patt.lhs == NULL) {
1629143918Sharti			Fatal("Unclosed substitution for %s (%c missing)",
1630143918Sharti			      v->name, '=');
1631143918Sharti		}
1632143955Sharti		vp->ptr++;	/* consume '=' */
1633143955Sharti
1634143966Sharti		patt.rhs = VarGetPattern(vp, endc, NULL, &patt);
1635143918Sharti		if (patt.rhs == NULL) {
1636143918Sharti			Fatal("Unclosed substitution for %s (%c missing)",
1637143918Sharti			      v->name, endc);
1638143918Sharti		}
1639143918Sharti
1640143918Sharti		/*
1641143918Sharti		 * SYSV modifications happen through the whole string. Note
1642143918Sharti		 * the pattern is anchored at the end.
1643143918Sharti		 */
1644143918Sharti		newStr = VarModify(value, VarSYSVMatch, &patt);
1645143918Sharti
1646143918Sharti		free(patt.lhs);
1647143918Sharti		free(patt.rhs);
1648143918Sharti	} else
1649143918Sharti#endif
1650143918Sharti	{
1651143918Sharti		Error("Unknown modifier '%c'\n", *vp->ptr);
1652143918Sharti		vp->ptr++;
1653143918Sharti		while (*vp->ptr != '\0') {
1654143918Sharti			if (*vp->ptr == endc && *vp->ptr == ':') {
1655143918Sharti				break;
1656143918Sharti			}
1657143918Sharti			vp->ptr++;
1658143918Sharti		}
1659143918Sharti		newStr = var_Error;
1660143918Sharti	}
1661143918Sharti
1662143918Sharti	return (newStr);
1663143918Sharti}
1664143918Sharti
1665146045Sharti/**
1666146045Sharti * Quote shell meta-characters in the string
1667146045Sharti *
1668146045Sharti * Results:
1669146045Sharti *	The quoted string
1670146045Sharti */
1671146045Shartistatic char *
1672146045ShartiVar_Quote(const char *str)
1673146045Sharti{
1674146045Sharti	Buffer *buf;
1675146045Sharti	/* This should cover most shells :-( */
1676146045Sharti	static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1677146045Sharti
1678146045Sharti	buf = Buf_Init(MAKE_BSIZE);
1679146045Sharti	for (; *str; str++) {
1680146045Sharti		if (strchr(meta, *str) != NULL)
1681146045Sharti			Buf_AddByte(buf, (Byte)'\\');
1682146045Sharti		Buf_AddByte(buf, (Byte)*str);
1683146045Sharti	}
1684146045Sharti
1685146045Sharti	return (Buf_Peel(buf));
1686146045Sharti}
1687146045Sharti
1688146045Sharti
1689142557Sharti/*
1690142910Sharti * Now we need to apply any modifiers the user wants applied.
1691142910Sharti * These are:
1692142910Sharti *	:M<pattern>
1693142910Sharti *		words which match the given <pattern>.
1694142910Sharti *		<pattern> is of the standard file
1695142910Sharti *		wildcarding form.
1696157589Sfjoe *	:N<pattern>
1697157589Sfjoe *		words which do not match the given <pattern>
1698157589Sfjoe *		<pattern> is of the standard file
1699157589Sfjoe *		wildcarding form.
1700142910Sharti *	:S<d><pat1><d><pat2><d>[g]
1701142910Sharti *		Substitute <pat2> for <pat1> in the value
1702142910Sharti *	:C<d><pat1><d><pat2><d>[g]
1703142910Sharti *		Substitute <pat2> for regex <pat1> in the value
1704142910Sharti *	:H	Substitute the head of each word
1705142910Sharti *	:T	Substitute the tail of each word
1706142910Sharti *	:E	Substitute the extension (minus '.') of
1707142910Sharti *		each word
1708142910Sharti *	:R	Substitute the root of each word
1709142910Sharti *		(pathname minus the suffix).
1710142910Sharti *	:lhs=rhs
1711142910Sharti *		Like :S, but the rhs goes to the end of
1712142910Sharti *		the invocation.
1713142910Sharti *	:U	Converts variable to upper-case.
1714142910Sharti *	:L	Converts variable to lower-case.
1715157589Sfjoe *	:O	("Order") Alphabeticaly sort words in variable.
1716157588Sfjoe *	:u	("uniq") Remove adjacent duplicate words.
1717142910Sharti */
1718142910Shartistatic char *
1719143917ShartiParseModifier(VarParser *vp, char startc, Var *v, Boolean *freeResult)
1720142910Sharti{
1721143914Sharti	char	*value;
1722143914Sharti	char	endc;
1723142910Sharti
1724143811Sharti	value = VarExpand(v, vp);
1725143917Sharti	*freeResult = TRUE;
1726142910Sharti
1727143914Sharti	endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE;
1728143914Sharti
1729143918Sharti	vp->ptr++;	/* consume first colon */
1730143918Sharti
1731143918Sharti	while (*vp->ptr != '\0') {
1732143813Sharti		char	*newStr;	/* New value to return */
1733142910Sharti
1734143918Sharti		if (*vp->ptr == endc) {
1735143918Sharti			return (value);
1736143918Sharti		}
1737143918Sharti
1738143914Sharti		DEBUGF(VAR, ("Applying :%c to \"%s\"\n", *vp->ptr, value));
1739143914Sharti		switch (*vp->ptr) {
1740142910Sharti		case 'N':
1741142910Sharti		case 'M':
1742143914Sharti			newStr = modifier_M(vp, value, endc);
1743143104Sharti			break;
1744142910Sharti		case 'S':
1745143914Sharti			newStr = modifier_S(vp, value, v);
1746143252Sharti			break;
1747142910Sharti		case 'C':
1748143914Sharti			newStr = modifier_C(vp, value, v);
1749142910Sharti			break;
1750236338Sobrien		case 't':
1751236338Sobrien			/* :tl :tu for OSF ODE & NetBSD make compatibility */
1752236338Sobrien			switch (vp->ptr[1]) {
1753236338Sobrien			case 'l':
1754236338Sobrien				vp->ptr++;
1755236338Sobrien				goto mod_lower;
1756236338Sobrien				break;
1757236338Sobrien			case 'u':
1758236338Sobrien				vp->ptr++;
1759236338Sobrien				goto mod_upper;
1760236338Sobrien				break;
1761236338Sobrien			}
1762236338Sobrien			/* FALLTHROUGH */
1763143814Sharti		default:
1764143918Sharti			if (vp->ptr[1] != endc && vp->ptr[1] != ':') {
1765142910Sharti#ifdef SUNSHCMD
1766143914Sharti				if ((vp->ptr[0] == 's') &&
1767143914Sharti				    (vp->ptr[1] == 'h') &&
1768143914Sharti				    (vp->ptr[2] == endc || vp->ptr[2] == ':')) {
1769239071Sdim					const char	*error = NULL;
1770142910Sharti
1771146038Sharti					if (vp->execute) {
1772146038Sharti						newStr = Buf_Peel(
1773146038Sharti						    Cmd_Exec(value, &error));
1774146038Sharti					} else {
1775146038Sharti						newStr = estrdup("");
1776146038Sharti					}
1777142910Sharti
1778143814Sharti					if (error)
1779143814Sharti						Error(error, value);
1780143918Sharti					vp->ptr += 2;
1781143814Sharti				} else
1782142910Sharti#endif
1783143814Sharti				{
1784143918Sharti					newStr = sysVvarsub(vp, startc, v, value);
1785143918Sharti				}
1786143918Sharti				break;
1787143918Sharti			}
1788142910Sharti
1789143918Sharti			switch (vp->ptr[0]) {
1790143918Sharti			case 'L':
1791236338Sobrien			mod_lower:
1792143918Sharti				{
1793143918Sharti				const char	*cp;
1794143918Sharti				Buffer		*buf;
1795143918Sharti				buf = Buf_Init(MAKE_BSIZE);
1796143918Sharti				for (cp = value; *cp; cp++)
1797143918Sharti					Buf_AddByte(buf, (Byte)tolower(*cp));
1798142910Sharti
1799143959Sharti				newStr = Buf_Peel(buf);
1800142910Sharti
1801143918Sharti				vp->ptr++;
1802143918Sharti				break;
1803143918Sharti				}
1804143918Sharti			case 'O':
1805143918Sharti				newStr = VarSortWords(value, SortIncreasing);
1806143918Sharti				vp->ptr++;
1807143918Sharti				break;
1808143918Sharti			case 'Q':
1809143918Sharti				newStr = Var_Quote(value);
1810143918Sharti				vp->ptr++;
1811143918Sharti				break;
1812143918Sharti			case 'T':
1813143918Sharti				newStr = VarModify(value, VarTail, NULL);
1814143918Sharti				vp->ptr++;
1815143918Sharti				break;
1816143918Sharti			case 'U':
1817236338Sobrien			mod_upper:
1818143918Sharti				{
1819143918Sharti				const char	*cp;
1820143918Sharti				Buffer		*buf;
1821143918Sharti				buf = Buf_Init(MAKE_BSIZE);
1822143918Sharti				for (cp = value; *cp; cp++)
1823143918Sharti					Buf_AddByte(buf, (Byte)toupper(*cp));
1824142910Sharti
1825143959Sharti				newStr = Buf_Peel(buf);
1826143813Sharti
1827143918Sharti				vp->ptr++;
1828143918Sharti				break;
1829143814Sharti				}
1830143918Sharti			case 'H':
1831143918Sharti				newStr = VarModify(value, VarHead, NULL);
1832143918Sharti				vp->ptr++;
1833143918Sharti				break;
1834143918Sharti			case 'E':
1835143918Sharti				newStr = VarModify(value, VarSuffix, NULL);
1836143918Sharti				vp->ptr++;
1837143918Sharti				break;
1838143918Sharti			case 'R':
1839143918Sharti				newStr = VarModify(value, VarRoot, NULL);
1840143918Sharti				vp->ptr++;
1841143918Sharti				break;
1842157588Sfjoe			case 'u':
1843157588Sfjoe				newStr = VarUniq(value);
1844157588Sfjoe				vp->ptr++;
1845157588Sfjoe				break;
1846143918Sharti			default:
1847143918Sharti				newStr = sysVvarsub(vp, startc, v, value);
1848143918Sharti				break;
1849143813Sharti			}
1850143814Sharti			break;
1851142910Sharti		}
1852143104Sharti
1853143813Sharti		DEBUGF(VAR, ("Result is \"%s\"\n", newStr));
1854143917Sharti		if (*freeResult) {
1855143813Sharti			free(value);
1856143813Sharti		}
1857143918Sharti
1858143813Sharti		value = newStr;
1859143918Sharti		*freeResult = (value == var_Error) ? FALSE : TRUE;
1860143918Sharti
1861143918Sharti		if (vp->ptr[0] == ':') {
1862143918Sharti			vp->ptr++;	/* consume colon */
1863143813Sharti		}
1864142910Sharti	}
1865142910Sharti
1866143916Sharti	return (value);
1867142910Sharti}
1868142910Sharti
1869142557Shartistatic char *
1870143917ShartiParseRestModifier(VarParser *vp, char startc, Buffer *buf, Boolean *freeResult)
1871142557Sharti{
1872143097Sharti	const char	*vname;
1873143566Sharti	size_t		vlen;
1874143566Sharti	Var		*v;
1875143915Sharti	char		*value;
1876143097Sharti
1877143566Sharti	vname = Buf_GetAll(buf, &vlen);
1878143566Sharti
1879145007Sharti	v = VarFindAny(vname, vp->ctxt);
1880143097Sharti	if (v != NULL) {
1881143917Sharti		value = ParseModifier(vp, startc, v, freeResult);
1882143916Sharti		return (value);
1883143566Sharti	}
1884142014Sharti
1885143811Sharti	if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) {
1886144340Sharti		size_t  consumed;
1887144340Sharti		/*
1888144340Sharti		 * Still need to get to the end of the variable
1889144340Sharti		 * specification, so kludge up a Var structure for the
1890144340Sharti		 * modifications
1891144340Sharti		 */
1892144340Sharti		v = VarCreate(vname, NULL, VAR_JUNK);
1893144340Sharti		value = ParseModifier(vp, startc, v, freeResult);
1894144340Sharti		if (*freeResult) {
1895144340Sharti			free(value);
1896144340Sharti		}
1897144340Sharti		VarDestroy(v, TRUE);
1898143916Sharti
1899144340Sharti		consumed = vp->ptr - vp->input + 1;
1900143915Sharti		/*
1901143916Sharti		 * If substituting a local variable in a non-local context,
1902143916Sharti		 * assume it's for dynamic source stuff. We have to handle
1903143916Sharti		 * this specially and return the longhand for the variable
1904143916Sharti		 * with the dollar sign escaped so it makes it back to the
1905143916Sharti		 * caller. Only four of the local variables are treated
1906143916Sharti		 * specially as they are the only four that will be set when
1907143916Sharti		 * dynamic sources are expanded.
1908143915Sharti		 */
1909143967Sharti		if (vlen == 1 ||
1910143967Sharti		    (vlen == 2 && (vname[1] == 'F' || vname[1] == 'D'))) {
1911143566Sharti			if (strchr("!%*@", vname[0]) != NULL) {
1912143915Sharti				value = emalloc(consumed + 1);
1913143915Sharti				strncpy(value, vp->input, consumed);
1914143915Sharti				value[consumed] = '\0';
1915143915Sharti
1916143917Sharti				*freeResult = TRUE;
1917143915Sharti				return (value);
1918143097Sharti			}
19191590Srgrimes		}
1920143967Sharti		if (vlen > 2 &&
1921143967Sharti		    vname[0] == '.' &&
1922143566Sharti		    isupper((unsigned char)vname[1])) {
1923143566Sharti			if ((strncmp(vname, ".TARGET", vlen - 1) == 0) ||
1924143566Sharti			    (strncmp(vname, ".ARCHIVE", vlen - 1) == 0) ||
1925143566Sharti			    (strncmp(vname, ".PREFIX", vlen - 1) == 0) ||
1926143566Sharti			    (strncmp(vname, ".MEMBER", vlen - 1) == 0)) {
1927143915Sharti				value = emalloc(consumed + 1);
1928143915Sharti				strncpy(value, vp->input, consumed);
1929143915Sharti				value[consumed] = '\0';
1930143915Sharti
1931143917Sharti				*freeResult = TRUE;
1932143915Sharti				return (value);
1933143566Sharti			}
1934143566Sharti		}
1935144340Sharti
1936144340Sharti		*freeResult = FALSE;
1937144340Sharti		return (vp->err ? var_Error : varNoError);
1938143566Sharti	} else {
1939143566Sharti		/*
1940143097Sharti		 * Check for D and F forms of local variables since we're in
1941143097Sharti		 * a local context and the name is the right length.
19421590Srgrimes		 */
1943143967Sharti		if (vlen == 2 &&
1944143097Sharti		    (vname[1] == 'F' || vname[1] == 'D') &&
1945143097Sharti		    (strchr("!%*<>@", vname[0]) != NULL)) {
1946143097Sharti			char	name[2];
19478874Srgrimes
1948143097Sharti			name[0] = vname[0];
1949143097Sharti			name[1] = '\0';
1950143097Sharti
1951145007Sharti			v = VarFindOnly(name, vp->ctxt);
1952143097Sharti			if (v != NULL) {
1953143917Sharti				value = ParseModifier(vp, startc, v, freeResult);
1954143916Sharti				return (value);
1955143097Sharti			}
19561590Srgrimes		}
1957143566Sharti
1958144340Sharti		/*
1959144340Sharti		 * Still need to get to the end of the variable
1960144340Sharti		 * specification, so kludge up a Var structure for the
1961144340Sharti		 * modifications
1962144340Sharti		 */
1963144340Sharti		v = VarCreate(vname, NULL, VAR_JUNK);
1964144340Sharti		value = ParseModifier(vp, startc, v, freeResult);
1965144340Sharti		if (*freeResult) {
1966144340Sharti			free(value);
1967144340Sharti		}
1968144340Sharti		VarDestroy(v, TRUE);
1969144340Sharti
1970144340Sharti		*freeResult = FALSE;
1971144340Sharti		return (vp->err ? var_Error : varNoError);
1972143919Sharti	}
1973143566Sharti}
1974143566Sharti
1975143566Shartistatic char *
1976143917ShartiParseRestEnd(VarParser *vp, Buffer *buf, Boolean *freeResult)
1977143566Sharti{
1978143566Sharti	const char	*vname;
1979143566Sharti	size_t		vlen;
1980143566Sharti	Var		*v;
1981143653Sharti	char		*value;
1982143566Sharti
1983143566Sharti	vname = Buf_GetAll(buf, &vlen);
1984143566Sharti
1985145007Sharti	v = VarFindAny(vname, vp->ctxt);
1986143566Sharti	if (v != NULL) {
1987143811Sharti		value = VarExpand(v, vp);
1988143917Sharti		*freeResult = TRUE;
1989143653Sharti		return (value);
1990143566Sharti	}
1991143566Sharti
1992143811Sharti	if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) {
1993143967Sharti		size_t	consumed = vp->ptr - vp->input + 1;
1994143916Sharti
1995143566Sharti		/*
1996143916Sharti		 * If substituting a local variable in a non-local context,
1997143916Sharti		 * assume it's for dynamic source stuff. We have to handle
1998143916Sharti		 * this specially and return the longhand for the variable
1999143916Sharti		 * with the dollar sign escaped so it makes it back to the
2000143916Sharti		 * caller. Only four of the local variables are treated
2001143916Sharti		 * specially as they are the only four that will be set when
2002143566Sharti		 * dynamic sources are expanded.
2003143566Sharti		 */
2004143967Sharti		if (vlen == 1 ||
2005143967Sharti		    (vlen == 2 && (vname[1] == 'F' || vname[1] == 'D'))) {
2006143097Sharti			if (strchr("!%*@", vname[0]) != NULL) {
2007143910Sharti				value = emalloc(consumed + 1);
2008143910Sharti				strncpy(value, vp->input, consumed);
2009143910Sharti				value[consumed] = '\0';
2010143566Sharti
2011143917Sharti				*freeResult = TRUE;
2012143653Sharti				return (value);
2013143097Sharti			}
2014143097Sharti		}
2015143967Sharti		if (vlen > 2 &&
2016143967Sharti		    vname[0] == '.' &&
2017143097Sharti		    isupper((unsigned char)vname[1])) {
2018143097Sharti			if ((strncmp(vname, ".TARGET", vlen - 1) == 0) ||
2019143097Sharti			    (strncmp(vname, ".ARCHIVE", vlen - 1) == 0) ||
2020143097Sharti			    (strncmp(vname, ".PREFIX", vlen - 1) == 0) ||
2021143097Sharti			    (strncmp(vname, ".MEMBER", vlen - 1) == 0)) {
2022143910Sharti				value = emalloc(consumed + 1);
2023143910Sharti				strncpy(value, vp->input, consumed);
2024143910Sharti				value[consumed] = '\0';
2025143566Sharti
2026143917Sharti				*freeResult = TRUE;
2027143653Sharti				return (value);
2028143097Sharti			}
2029143097Sharti		}
2030143097Sharti	} else {
2031141686Sharti		/*
2032143566Sharti		 * Check for D and F forms of local variables since we're in
2033143566Sharti		 * a local context and the name is the right length.
20341590Srgrimes		 */
2035143967Sharti		if (vlen == 2 &&
2036143566Sharti		    (vname[1] == 'F' || vname[1] == 'D') &&
2037143566Sharti		    (strchr("!%*<>@", vname[0]) != NULL)) {
2038143566Sharti			char	name[2];
2039142382Sharti
2040143566Sharti			name[0] = vname[0];
2041143566Sharti			name[1] = '\0';
2042142382Sharti
2043145007Sharti			v = VarFindOnly(name, vp->ctxt);
2044143566Sharti			if (v != NULL) {
2045143566Sharti				char	*val;
2046143566Sharti				/*
2047143916Sharti				 * No need for nested expansion or anything,
2048143916Sharti				 * as we're the only one who sets these
2049143916Sharti				 * things and we sure don't put nested
2050143916Sharti				 * invocations in them...
2051143566Sharti				 */
2052143959Sharti				val = Buf_Data(v->val);
2053142382Sharti
2054143566Sharti				if (vname[1] == 'D') {
2055143566Sharti					val = VarModify(val, VarHead, NULL);
2056143566Sharti				} else {
2057143566Sharti					val = VarModify(val, VarTail, NULL);
2058143566Sharti				}
2059143566Sharti
2060143917Sharti				*freeResult = TRUE;
2061143566Sharti				return (val);
2062143566Sharti			}
2063143566Sharti		}
2064143919Sharti	}
2065143566Sharti
2066143919Sharti	*freeResult = FALSE;
2067143919Sharti	return (vp->err ? var_Error : varNoError);
2068143566Sharti}
2069143566Sharti
2070143566Sharti/**
2071143566Sharti * Parse a multi letter variable name, and return it's value.
2072143566Sharti */
2073143566Shartistatic char *
2074143917ShartiVarParseLong(VarParser *vp, Boolean *freeResult)
2075143566Sharti{
2076143566Sharti	Buffer		*buf;
2077143649Sharti	char		startc;
2078143649Sharti	char		endc;
2079143917Sharti	char		*value;
2080143566Sharti
2081143566Sharti	buf = Buf_Init(MAKE_BSIZE);
2082143566Sharti
2083143910Sharti	startc = vp->ptr[0];
2084143919Sharti	vp->ptr++;		/* consume opening paren or brace */
2085143914Sharti
2086143566Sharti	endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE;
2087143566Sharti
2088143919Sharti	/*
2089143919Sharti	 * Process characters until we reach an end character or a colon,
2090143919Sharti	 * replacing embedded variables as we go.
2091143919Sharti	 */
2092143910Sharti	while (*vp->ptr != '\0') {
2093143910Sharti		if (*vp->ptr == endc) {
2094143917Sharti			value = ParseRestEnd(vp, buf, freeResult);
2095143910Sharti			vp->ptr++;	/* consume closing paren or brace */
2096143599Sharti			Buf_Destroy(buf, TRUE);
2097143917Sharti			return (value);
2098143566Sharti
2099143910Sharti		} else if (*vp->ptr == ':') {
2100143917Sharti			value = ParseRestModifier(vp, startc, buf, freeResult);
2101143914Sharti			vp->ptr++;	/* consume closing paren or brace */
2102143649Sharti			Buf_Destroy(buf, TRUE);
2103143917Sharti			return (value);
2104143649Sharti
2105143910Sharti		} else if (*vp->ptr == '$') {
2106143970Sharti			VarParser	subvp = {
2107143970Sharti				vp->ptr,
2108143970Sharti				vp->ptr,
2109143970Sharti				vp->ctxt,
2110146038Sharti				vp->err,
2111146038Sharti				vp->execute
2112143970Sharti			};
2113143970Sharti			char	*rval;
2114143566Sharti			Boolean	rfree;
2115143566Sharti
2116143970Sharti			rval = VarParse(&subvp, &rfree);
2117143566Sharti			if (rval == var_Error) {
2118143566Sharti				Fatal("Error expanding embedded variable.");
2119143566Sharti			}
2120143566Sharti			Buf_Append(buf, rval);
2121143566Sharti			if (rfree)
2122143566Sharti				free(rval);
2123143970Sharti			vp->ptr = subvp.ptr;
21241590Srgrimes		} else {
2125143910Sharti			Buf_AddByte(buf, (Byte)*vp->ptr);
2126143910Sharti			vp->ptr++;
21271590Srgrimes		}
2128141686Sharti	}
2129143566Sharti
2130143916Sharti	/* If we did not find the end character, return var_Error */
2131143566Sharti	Buf_Destroy(buf, TRUE);
2132143917Sharti	*freeResult = FALSE;
2133143910Sharti	return (var_Error);
2134142557Sharti}
21358874Srgrimes
2136142881Sharti/**
2137143566Sharti * Parse a single letter variable name, and return it's value.
2138142881Sharti */
2139142557Shartistatic char *
2140143910ShartiVarParseShort(VarParser *vp, Boolean *freeResult)
2141142557Sharti{
2142143653Sharti	char	vname[2];
2143142881Sharti	Var	*v;
2144143653Sharti	char	*value;
21458874Srgrimes
2146143910Sharti	vname[0] = vp->ptr[0];
2147143653Sharti	vname[1] = '\0';
2148142557Sharti
2149143910Sharti	vp->ptr++;	/* consume single letter */
2150143036Sharti
2151145007Sharti	v = VarFindAny(vname, vp->ctxt);
2152142881Sharti	if (v != NULL) {
2153143811Sharti		value = VarExpand(v, vp);
2154143653Sharti		*freeResult = TRUE;
2155143653Sharti		return (value);
21561590Srgrimes	}
2157142881Sharti
2158142881Sharti	/*
2159142881Sharti	 * If substituting a local variable in a non-local context, assume
2160142881Sharti	 * it's for dynamic source stuff. We have to handle this specially
2161142881Sharti	 * and return the longhand for the variable with the dollar sign
2162142881Sharti	 * escaped so it makes it back to the caller. Only four of the local
2163142881Sharti	 * variables are treated specially as they are the only four that
2164142881Sharti	 * will be set when dynamic sources are expanded.
2165142881Sharti	 */
2166143811Sharti	if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) {
2167142881Sharti
2168142881Sharti		/* XXX: It looks like $% and $! are reversed here */
2169143653Sharti		switch (vname[0]) {
2170142881Sharti		case '@':
2171143653Sharti			*freeResult = TRUE;
2172143036Sharti			return (estrdup("$(.TARGET)"));
2173142881Sharti		case '%':
2174143653Sharti			*freeResult = TRUE;
2175143036Sharti			return (estrdup("$(.ARCHIVE)"));
2176142881Sharti		case '*':
2177143653Sharti			*freeResult = TRUE;
2178143036Sharti			return (estrdup("$(.PREFIX)"));
2179142881Sharti		case '!':
2180143653Sharti			*freeResult = TRUE;
2181143036Sharti			return (estrdup("$(.MEMBER)"));
2182142881Sharti		default:
2183143653Sharti			*freeResult = FALSE;
2184143811Sharti			return (vp->err ? var_Error : varNoError);
2185142881Sharti		}
2186142881Sharti	}
2187142881Sharti
2188143918Sharti	/* Variable name was not found. */
2189143653Sharti	*freeResult = FALSE;
2190143811Sharti	return (vp->err ? var_Error : varNoError);
2191142557Sharti}
2192142557Sharti
2193143811Shartistatic char *
2194143914ShartiVarParse(VarParser *vp, Boolean *freeResult)
2195143811Sharti{
2196143811Sharti
2197143919Sharti	vp->ptr++;	/* consume '$' or last letter of conditional */
2198143811Sharti
2199143904Sharti	if (vp->ptr[0] == '\0') {
2200143811Sharti		/* Error, there is only a dollar sign in the input string. */
2201143811Sharti		*freeResult = FALSE;
2202143914Sharti		return (vp->err ? var_Error : varNoError);
2203143811Sharti
2204143904Sharti	} else if (vp->ptr[0] == OPEN_PAREN || vp->ptr[0] == OPEN_BRACE) {
2205143811Sharti		/* multi letter variable name */
2206143914Sharti		return (VarParseLong(vp, freeResult));
2207143811Sharti
2208143811Sharti	} else {
2209143811Sharti		/* single letter variable name */
2210143914Sharti		return (VarParseShort(vp, freeResult));
2211143811Sharti	}
2212143811Sharti}
2213143811Sharti
2214146046Sharti/**
2215146046Sharti * Given the start of a variable invocation, extract the variable
2216146046Sharti * name and find its value, then modify it according to the
2217146046Sharti * specification.
2218142557Sharti *
2219142557Sharti * Results:
2220143276Sharti *	The value of the variable or var_Error if the specification
2221143566Sharti *	is invalid.  The number of characters in the specification
2222143566Sharti *	is placed in the variable pointed to by consumed.  (for
2223143566Sharti *	invalid specifications, this is just 2 to skip the '$' and
2224143566Sharti *	the following letter, or 1 if '$' was the last character
2225143653Sharti *	in the string).  A Boolean in *freeResult telling whether the
2226143566Sharti *	returned string should be freed by the caller.
2227142557Sharti */
2228142557Shartichar *
2229143276ShartiVar_Parse(const char input[], GNode *ctxt, Boolean err,
2230143653Sharti	size_t *consumed, Boolean *freeResult)
2231142557Sharti{
2232143811Sharti	VarParser	vp = {
2233143811Sharti		input,
2234143904Sharti		input,
2235143811Sharti		ctxt,
2236146038Sharti		err,
2237146038Sharti		TRUE
2238143811Sharti	};
2239143910Sharti	char		*value;
2240143036Sharti
2241143914Sharti	value = VarParse(&vp, freeResult);
2242143914Sharti	*consumed += vp.ptr - vp.input;
2243143910Sharti	return (value);
22441590Srgrimes}
22451590Srgrimes
2246146038Sharti/*
2247146046Sharti * Given the start of a variable invocation, determine the length
2248146046Sharti * of the specification.
2249146038Sharti *
2250146038Sharti * Results:
2251146038Sharti *	The number of characters in the specification.  For invalid
2252146038Sharti *	specifications, this is just 2 to skip the '$' and the
2253146038Sharti *	following letter, or 1 if '$' was the last character in the
2254146038Sharti *	string.
2255146038Sharti */
2256146038Shartisize_t
2257146038ShartiVar_Match(const char input[], GNode *ctxt)
2258146038Sharti{
2259146038Sharti	VarParser	vp = {
2260146038Sharti		input,
2261146038Sharti		input,
2262146038Sharti		ctxt,
2263146038Sharti		FALSE,
2264146038Sharti		FALSE
2265146038Sharti	};
2266146038Sharti	char		*value;
2267146038Sharti	Boolean		freeResult;
2268146038Sharti
2269146038Sharti	value = VarParse(&vp, &freeResult);
2270146038Sharti	if (freeResult) {
2271146038Sharti		free(value);
2272146038Sharti	}
2273146038Sharti	return (vp.ptr - vp.input);
2274146038Sharti}
2275146038Sharti
2276146046Shartistatic int
2277146046Shartimatch_var(const char str[], const char var[])
2278146046Sharti{
2279146046Sharti	const char	*start = str;
2280146046Sharti	size_t		len;
2281146046Sharti
2282146046Sharti	str++;			/* consume '$' */
2283146046Sharti
2284146046Sharti	if (str[0] == OPEN_PAREN || str[0] == OPEN_BRACE) {
2285146046Sharti		str++;		/* consume opening paren or brace */
2286146046Sharti
2287146046Sharti		while (str[0] != '\0') {
2288146046Sharti			if (str[0] == '$') {
2289146046Sharti				/*
2290146046Sharti				 * A variable inside the variable. We cannot
2291146046Sharti				 * expand the external variable yet.
2292146046Sharti				 */
2293146046Sharti				return (str - start);
2294146046Sharti			} else if (str[0] == ':' ||
2295146046Sharti				   str[0] == CLOSE_PAREN ||
2296146046Sharti				   str[0] == CLOSE_BRACE) {
2297146046Sharti				len = str - (start + 2);
2298146046Sharti
2299152969Sfjoe				if (strncmp(var, start + 2, len) == 0 && var[len] == '\0') {
2300146046Sharti					return (0);	/* match */
2301146046Sharti				} else {
2302146046Sharti					/*
2303146046Sharti					 * Not the variable we want to
2304146046Sharti					 * expand.
2305146046Sharti					 */
2306146046Sharti					return (str - start);
2307146046Sharti				}
2308146046Sharti			} else {
2309146046Sharti				++str;
2310146046Sharti			}
2311146046Sharti		}
2312146046Sharti		return (str - start);
2313146046Sharti	} else {
2314146046Sharti		/* Single letter variable name */
2315146046Sharti		if (var[1] == '\0' && var[0] == str[0]) {
2316146046Sharti			return (0);	/* match */
2317146046Sharti		} else {
2318146046Sharti			str++;	/* consume variable name */
2319146046Sharti			return (str - start);
2320146046Sharti		}
2321146046Sharti	}
2322146046Sharti}
2323146046Sharti
2324146046Sharti/**
2325146046Sharti * Substitute for all variables in the given string in the given
2326146046Sharti * context If err is TRUE, Parse_Error will be called when an
2327146046Sharti * undefined variable is encountered.
23281590Srgrimes *
23291590Srgrimes * Results:
23301590Srgrimes *	The resulting string.
23311590Srgrimes *
23321590Srgrimes * Side Effects:
23331590Srgrimes *	None. The old string must be freed by the caller
23341590Srgrimes */
2335142457ShartiBuffer *
2336186831SobrienVar_Subst(const char *str, GNode *ctxt, Boolean err)
23371590Srgrimes{
2338146027Sharti	Boolean	errorReported;
2339143813Sharti	Buffer *buf;		/* Buffer for forming things */
23401590Srgrimes
2341143813Sharti	/*
2342143813Sharti	 * Set TRUE if an error has already been reported to prevent a
2343143813Sharti	 * plethora of messages when recursing. XXXHB this comment sounds
2344143813Sharti	 * wrong.
2345143813Sharti	 */
2346143813Sharti	errorReported = FALSE;
2347141648Sharti
2348143813Sharti	buf = Buf_Init(0);
2349146027Sharti	while (str[0] != '\0') {
2350146027Sharti		if ((str[0] == '$') && (str[1] == '$')) {
2351143813Sharti			/*
2352146027Sharti			 * A dollar sign may be escaped with another dollar
2353146027Sharti			 * sign.  In such a case, we skip over the escape
2354146027Sharti			 * character and store the dollar sign into the
2355146027Sharti			 * buffer directly.
2356143813Sharti			 */
2357146027Sharti			str++;
2358143813Sharti			Buf_AddByte(buf, (Byte)str[0]);
2359146027Sharti			str++;
2360141659Sharti
2361143813Sharti		} else if (str[0] == '$') {
2362146027Sharti			/* Variable invocation. */
2363146027Sharti			VarParser subvp = {
2364143970Sharti				str,
2365143970Sharti				str,
2366143970Sharti				ctxt,
2367146038Sharti				err,
2368146038Sharti				TRUE
2369143970Sharti			};
2370143970Sharti			char	*rval;
2371143970Sharti			Boolean	rfree;
2372141648Sharti
2373143970Sharti			rval = VarParse(&subvp, &rfree);
2374143970Sharti
23751590Srgrimes			/*
2376143813Sharti			 * When we come down here, val should either point to
2377143813Sharti			 * the value of this variable, suitably modified, or
2378143813Sharti			 * be NULL. Length should be the total length of the
2379143813Sharti			 * potential variable invocation (from $ to end
2380143813Sharti			 * character...)
23811590Srgrimes			 */
2382143969Sharti			if (rval == var_Error || rval == varNoError) {
2383143813Sharti				/*
2384143813Sharti				 * If performing old-time variable
2385143813Sharti				 * substitution, skip over the variable and
2386143813Sharti				 * continue with the substitution. Otherwise,
2387143813Sharti				 * store the dollar sign and advance str so
2388143813Sharti				 * we continue with the string...
2389143813Sharti				 */
2390143813Sharti				if (oldVars) {
2391143970Sharti					str = subvp.ptr;
2392143969Sharti				} else if (err) {
2393143813Sharti					/*
2394143813Sharti					 * If variable is undefined, complain
2395143813Sharti					 * and skip the variable. The
2396143813Sharti					 * complaint will stop us from doing
2397143813Sharti					 * anything when the file is parsed.
2398143813Sharti					 */
2399143813Sharti					if (!errorReported) {
2400143813Sharti						Parse_Error(PARSE_FATAL,
2401143970Sharti							    "Undefined variable \"%.*s\"", subvp.ptr - subvp.input, str);
2402143813Sharti					}
2403146027Sharti					errorReported = TRUE;
2404143970Sharti					str = subvp.ptr;
2405143813Sharti				} else {
2406146027Sharti					Buf_AddByte(buf, (Byte)str[0]);
2407146027Sharti					str++;
2408143813Sharti				}
2409141686Sharti			} else {
2410143813Sharti				/*
2411143813Sharti				 * Copy all the characters from the variable
2412143813Sharti				 * value straight into the new string.
2413143813Sharti				 */
2414143969Sharti				Buf_Append(buf, rval);
2415143969Sharti				if (rfree) {
2416143969Sharti					free(rval);
2417143813Sharti				}
2418146027Sharti				str = subvp.ptr;
2419141686Sharti			}
2420143813Sharti		} else {
2421146027Sharti			Buf_AddByte(buf, (Byte)str[0]);
2422146027Sharti			str++;
24231590Srgrimes		}
24241590Srgrimes	}
24258874Srgrimes
2426143813Sharti	return (buf);
24271590Srgrimes}
24281590Srgrimes
2429146046Sharti/**
2430146046Sharti * Substitute for all variables except if it is the same as 'var',
2431146046Sharti * in the given string in the given context.  If err is TRUE,
2432146046Sharti * Parse_Error will be called when an undefined variable is
2433146046Sharti * encountered.
2434146046Sharti *
2435146046Sharti * Results:
2436146046Sharti *	The resulting string.
2437146046Sharti *
2438146046Sharti * Side Effects:
2439146046Sharti *	None. The old string must be freed by the caller
2440146046Sharti */
2441146027ShartiBuffer *
2442146048ShartiVar_SubstOnly(const char *var, const char *str, Boolean err)
24431590Srgrimes{
2444146048Sharti	GNode *ctxt = VAR_GLOBAL;
2445146027Sharti	Boolean	errorReported;
2446146027Sharti	Buffer	*buf;		/* Buffer for forming things */
2447138232Sharti
2448146027Sharti	/*
2449146027Sharti	 * Set TRUE if an error has already been reported to prevent a
2450146027Sharti	 * plethora of messages when recursing. XXXHB this comment sounds
2451146027Sharti	 * wrong.
2452146027Sharti	 */
2453146027Sharti	errorReported = FALSE;
2454146027Sharti
2455146027Sharti	buf = Buf_Init(0);
2456146027Sharti	while (str[0] != '\0') {
2457146027Sharti		if (str[0] == '$') {
2458146027Sharti			int	skip;
2459146027Sharti
2460146027Sharti			skip = match_var(str, var);
2461146027Sharti			if (skip > 0) {
2462146027Sharti				Buf_AddBytes(buf, skip, str);
2463146027Sharti				str += skip;
2464146027Sharti			} else {
2465146027Sharti				/* Variable invocation. */
2466146027Sharti				VarParser	subvp = {
2467146027Sharti					str,
2468146027Sharti					str,
2469146027Sharti					ctxt,
2470146038Sharti					err,
2471146038Sharti					TRUE
2472146027Sharti				};
2473146027Sharti				char	*rval;
2474146027Sharti				Boolean	rfree;
2475146027Sharti
2476146027Sharti				rval = VarParse(&subvp, &rfree);
2477146027Sharti
2478146027Sharti				/*
2479146027Sharti				 * When we get down here, rval should either
2480146027Sharti				 * point to the value of this variable, or be
2481146027Sharti				 * NULL.
2482146027Sharti				 */
2483146027Sharti				if (rval == var_Error || rval == varNoError) {
2484146027Sharti					/*
2485146027Sharti					 * If performing old-time variable
2486146027Sharti					 * substitution, skip over the
2487146027Sharti					 * variable and continue with the
2488146027Sharti					 * substitution. Otherwise, store the
2489146027Sharti					 * dollar sign and advance str so we
2490146027Sharti					 * continue with the string...
2491146027Sharti					 */
2492146027Sharti					if (oldVars) {
2493146027Sharti						str = subvp.ptr;
2494146027Sharti					} else if (err) {
2495146027Sharti						/*
2496146027Sharti						 * If variable is undefined,
2497146027Sharti						 * complain and skip the
2498146027Sharti						 * variable. The complaint
2499146027Sharti						 * will stop us from doing
2500146027Sharti						 * anything when the file is
2501146027Sharti						 * parsed.
2502146027Sharti						 */
2503146027Sharti						if (!errorReported) {
2504146027Sharti							Parse_Error(PARSE_FATAL,
2505146027Sharti								    "Undefined variable \"%.*s\"", subvp.ptr - subvp.input, str);
2506146027Sharti						}
2507146027Sharti						errorReported = TRUE;
2508146027Sharti						str = subvp.ptr;
2509146027Sharti					} else {
2510146027Sharti						Buf_AddByte(buf, (Byte)str[0]);
2511146027Sharti						str++;
2512146027Sharti					}
2513146027Sharti				} else {
2514146027Sharti					/*
2515146027Sharti					 * Copy all the characters from the
2516146027Sharti					 * variable value straight into the
2517146027Sharti					 * new string.
2518146027Sharti					 */
2519146027Sharti					Buf_Append(buf, rval);
2520146027Sharti					if (rfree) {
2521146027Sharti						free(rval);
2522146027Sharti					}
2523146027Sharti					str = subvp.ptr;
2524146027Sharti				}
2525146027Sharti			}
2526146027Sharti		} else {
2527146027Sharti			Buf_AddByte(buf, (Byte)str[0]);
2528146027Sharti			str++;
2529146027Sharti		}
2530146027Sharti	}
2531146027Sharti
2532146027Sharti	return (buf);
25331590Srgrimes}
25341590Srgrimes
2535146046Sharti/**
2536146046Sharti * Initialize the module
25371590Srgrimes *
25381590Srgrimes * Side Effects:
25398874Srgrimes *	The VAR_CMD and VAR_GLOBAL contexts are created
25401590Srgrimes */
25411590Srgrimesvoid
2542145971ShartiVar_Init(char **env)
25431590Srgrimes{
2544145971Sharti	char	**ptr;
2545138232Sharti
2546145971Sharti	VAR_CMD = Targ_NewGN("Command");
2547145971Sharti	VAR_ENV = Targ_NewGN("Environment");
2548143813Sharti	VAR_GLOBAL = Targ_NewGN("Global");
2549145971Sharti
2550145971Sharti	/*
2551145971Sharti	 * Copy user environment variables into ENV context.
2552145971Sharti	 */
2553145971Sharti	for (ptr = env; *ptr != NULL; ++ptr) {
2554145971Sharti		char		*tmp = estrdup(*ptr);
2555145971Sharti		const char	*name = tmp;
2556145971Sharti		char		*sep = strchr(name, '=');
2557145971Sharti		const char	*value = sep + 1;
2558145971Sharti
2559145971Sharti		if (sep != NULL) {
2560145971Sharti			*sep = '\0';
2561145971Sharti			VarAdd(name, value, VAR_ENV);
2562145971Sharti		}
2563145971Sharti		free(tmp);
2564145971Sharti	}
25651590Srgrimes}
25661590Srgrimes
2567146046Sharti/**
2568146046Sharti * Print all variables in global and command line contexts.
25691590Srgrimes */
25701590Srgrimesvoid
2571146039ShartiVar_Dump(void)
25721590Srgrimes{
2573143377Sharti	const LstNode	*ln;
2574143377Sharti	const Var	*v;
2575138232Sharti
2576146039Sharti	printf("#*** Global Variables:\n");
2577146039Sharti	LST_FOREACH(ln, &VAR_GLOBAL->context) {
2578143377Sharti		v = Lst_Datum(ln);
2579143959Sharti		printf("%-16s = %s\n", v->name, Buf_Data(v->val));
2580143377Sharti	}
2581146039Sharti
2582146039Sharti	printf("#*** Command-line Variables:\n");
2583146039Sharti	LST_FOREACH(ln, &VAR_CMD->context) {
2584146039Sharti		v = Lst_Datum(ln);
2585146039Sharti		printf("%-16s = %s\n", v->name, Buf_Data(v->val));
2586146039Sharti	}
25871590Srgrimes}
2588146039Sharti
2589146141Sharti/**
2590146141Sharti * Print the values of any variables requested by
2591146141Sharti * the user.
2592146141Sharti */
2593146141Shartivoid
2594146141ShartiVar_Print(Lst *vlist, Boolean expandVars)
2595146141Sharti{
2596146141Sharti	LstNode		*n;
2597236346Sobrien	char		*name;
2598146141Sharti
2599146141Sharti	LST_FOREACH(n, vlist) {
2600146141Sharti		name = Lst_Datum(n);
2601146141Sharti		if (expandVars) {
2602146581Sharti			char *value;
2603146581Sharti			char *v;
2604146581Sharti
2605236346Sobrien			if (*name == '$') {
2606236346Sobrien				v = name;
2607236346Sobrien			} else {
2608236346Sobrien				v = emalloc(strlen(name) + 1 + 3);
2609236346Sobrien				sprintf(v, "${%s}", name);
2610236346Sobrien			}
2611146580Sharti			value = Buf_Peel(Var_Subst(v, VAR_GLOBAL, FALSE));
2612146141Sharti			printf("%s\n", value);
2613146141Sharti
2614236346Sobrien			if (v != name)
2615236346Sobrien				free(v);
2616146141Sharti			free(value);
2617146141Sharti		} else {
2618146581Sharti			const char *value = Var_Value(name, VAR_GLOBAL);
2619146141Sharti			printf("%s\n", value != NULL ? value : "");
2620146141Sharti		}
2621146141Sharti	}
2622146141Sharti}
2623146141Sharti
2624