xref: /trueos/usr.bin/make/var.c (revision b972b67ed72b5687a023c92602aaef64163b2f59)
1 /*-
2  * Copyright (c) 2002 Juli Mallett.
3  * Copyright (c) 1988, 1989, 1990, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  * Copyright (c) 1989 by Berkeley Softworks
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Adam de Boor.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  * @(#)var.c	8.3 (Berkeley) 3/19/94
40  */
41 
42 #include <sys/cdefs.h>
43 __FBSDID("$FreeBSD$");
44 
45 /**
46  * var.c --
47  *	Variable-handling functions
48  *
49  * Interface:
50  *	Var_Set		Set the value of a variable in the given
51  *			context. The variable is created if it doesn't
52  *			yet exist. The value and variable name need not
53  *			be preserved.
54  *
55  *	Var_Append	Append more characters to an existing variable
56  *			in the given context. The variable needn't
57  *			exist already -- it will be created if it doesn't.
58  *			A space is placed between the old value and the
59  *			new one.
60  *
61  *	Var_Exists	See if a variable exists.
62  *
63  *	Var_Value	Return the value of a variable in a context or
64  *			NULL if the variable is undefined.
65  *
66  *	Var_Subst	Substitute named variable, or all variables if
67  *			NULL in a string using
68  *			the given context as the top-most one. If the
69  *			third argument is non-zero, Parse_Error is
70  *			called if any variables are undefined.
71  *
72  *	Var_Parse	Parse a variable expansion from a string and
73  *			return the result and the number of characters
74  *			consumed.
75  *
76  *	Var_Delete	Delete a variable in a context.
77  *
78  *	Var_Init	Initialize this module.
79  *
80  * Debugging:
81  *	Var_Dump	Print out all variables defined in the given
82  *			context.
83  *
84  * XXX: There's a lot of duplication in these functions.
85  */
86 
87 #include <ctype.h>
88 #include <stdlib.h>
89 #include <string.h>
90 #include <sys/types.h>
91 #include <regex.h>
92 
93 #include "buf.h"
94 #include "config.h"
95 #include "globals.h"
96 #include "GNode.h"
97 #include "job.h"
98 #include "lst.h"
99 #include "parse.h"
100 #include "str.h"
101 #include "targ.h"
102 #include "util.h"
103 #include "var.h"
104 
105 /**
106  *
107  */
108 typedef struct VarParser {
109 	const char	*const input;	/* pointer to input string */
110 	const char	*ptr;		/* current parser pos in input str */
111 	GNode		*ctxt;
112 	Boolean		err;
113 	Boolean		execute;
114 } VarParser;
115 
116 typedef struct Var {
117 	char		*name;	/* the variable's name */
118 	struct Buffer	*val;	/* its value */
119 	int		flags;	/* miscellaneous status flags */
120 
121 #define	VAR_IN_USE	1	/* Variable's value currently being used.
122 				 * Used to avoid recursion */
123 
124 #define	VAR_JUNK	4	/* Variable is a junk variable that
125 				 * should be destroyed when done with
126 				 * it. Used by Var_Parse for undefined,
127 				 * modified variables */
128 
129 #define	VAR_TO_ENV	8	/* Place variable in environment */
130 } Var;
131 
132 typedef struct {
133 	struct Buffer	*lhs;	/* String to match */
134 	struct Buffer	*rhs;	/* Replacement string (w/ &'s removed) */
135 
136 	regex_t			re;
137 	int			nsub;
138 	regmatch_t		*matches;
139 
140 	int	flags;
141 #define	VAR_SUB_GLOBAL	0x01	/* Apply substitution globally */
142 #define	VAR_SUB_ONE	0x02	/* Apply substitution to one word */
143 #define	VAR_SUB_MATCHED	0x04	/* There was a match */
144 #define	VAR_MATCH_START	0x08	/* Match at start of word */
145 #define	VAR_MATCH_END	0x10	/* Match at end of word */
146 } VarPattern;
147 
148 typedef Boolean VarModifyProc(const char *, Boolean, struct Buffer *, void *);
149 
150 static char *VarParse(VarParser *, Boolean *);
151 
152 /*
153  * This is a harmless return value for Var_Parse that can be used by Var_Subst
154  * to determine if there was an error in parsing -- easier than returning
155  * a flag, as things outside this module don't give a hoot.
156  */
157 char	var_Error[] = "";
158 
159 /*
160  * Similar to var_Error, but returned when the 'err' flag for Var_Parse is
161  * set false. Why not just use a constant? Well, gcc likes to condense
162  * identical string instances...
163  */
164 static char	varNoError[] = "";
165 
166 /*
167  * Internally, variables are contained in four different contexts.
168  *	1) the environment. They may not be changed. If an environment
169  *	   variable is appended-to, the result is placed in the global
170  *	   context.
171  *	2) the global context. Variables set in the Makefile are located in
172  *	   the global context. It is the penultimate context searched when
173  *	   substituting.
174  *	3) the command-line context. All variables set on the command line
175  *	   are placed in this context. They are UNALTERABLE once placed here.
176  *	4) the local context. Each target has associated with it a context
177  *	   list. On this list are located the structures describing such
178  *	   local variables as $(@) and $(*)
179  * The four contexts are searched in the reverse order from which they are
180  * listed.
181  */
182 static GNode	*VAR_ENV;	/* variables from the environment */
183 GNode		*VAR_GLOBAL;	/* variables from the makefile */
184 GNode		*VAR_CMD;	/* variables defined on the command-line */
185 
186 Boolean		oldVars;	/* variable substitution style */
187 Boolean		checkEnvFirst;	/* -e flag */
188 
189 #define	OPEN_PAREN		'('
190 #define	CLOSE_PAREN		')'
191 #define	OPEN_BRACE		'{'
192 #define	CLOSE_BRACE		'}'
193 
194 /**
195  * Create a Var object.
196  *
197  * Params:
198  *	name		Name of variable (copied).
199  *	value		Value of variable (copied) or NULL.
200  *	flags		Flags set on variable.
201  *
202  * Returns:
203  *	New variable.
204  */
205 static Var *
VarCreate(const char name[],const char value[],int flags)206 VarCreate(const char name[], const char value[], int flags)
207 {
208 	Var *v;
209 
210 	v = emalloc(sizeof(Var));
211 	v->name = estrdup(name);
212 	v->val = Buf_Init(0);
213 	v->flags = flags;
214 
215 	if (value != NULL) {
216 		Buf_Append(v->val, value);
217 	}
218 	return (v);
219 }
220 
221 /**
222  * Destroy a Var object.
223  *
224  * Params:
225  *	v	Object to destroy.
226  *	f	True if internal buffer in Buffer object is to be removed.
227  */
228 static void
VarDestroy(Var * v,Boolean f)229 VarDestroy(Var *v, Boolean f)
230 {
231 
232 	Buf_Destroy(v->val, f);
233 	free(v->name);
234 	free(v);
235 }
236 
237 /**
238  * Remove the tail of the given word and place the result in the given
239  * buffer.
240  *
241  * Results:
242  *	TRUE if characters were added to the buffer (a space needs to be
243  *	added to the buffer before the next word).
244  *
245  * Side Effects:
246  *	The trimmed word is added to the buffer.
247  */
248 static Boolean
VarHead(const char * word,Boolean addSpace,Buffer * buf,void * dummy __unused)249 VarHead(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
250 {
251 	char *slash;
252 
253 	slash = strrchr(word, '/');
254 	if (slash != NULL) {
255 		if (addSpace) {
256 			Buf_AddByte(buf, (Byte)' ');
257 		}
258 		Buf_AppendRange(buf, word, slash);
259 	} else {
260 		/*
261 		 * If no directory part, give . (q.v. the POSIX standard)
262 		 */
263 		if (addSpace) {
264 			Buf_Append(buf, " .");
265 		} else {
266 			Buf_AddByte(buf, (Byte)'.');
267 		}
268 	}
269 	return (TRUE);
270 }
271 
272 /**
273  * Remove the head of the given word and place the result in the given
274  * buffer.
275  *
276  * Results:
277  *	TRUE if characters were added to the buffer (a space needs to be
278  *	added to the buffer before the next word).
279  *
280  * Side Effects:
281  *	The trimmed word is added to the buffer.
282  */
283 static Boolean
VarTail(const char * word,Boolean addSpace,Buffer * buf,void * dummy __unused)284 VarTail(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
285 {
286 	const char *slash;
287 
288 	if (addSpace) {
289 		Buf_AddByte (buf, (Byte)' ');
290 	}
291 
292 	slash = strrchr(word, '/');
293 	if (slash != NULL) {
294 		slash++;
295 		Buf_Append(buf, slash);
296 	} else {
297 		Buf_Append(buf, word);
298 	}
299 	return (TRUE);
300 }
301 
302 /**
303  * Place the suffix of the given word in the given buffer.
304  *
305  * Results:
306  *	TRUE if characters were added to the buffer (a space needs to be
307  *	added to the buffer before the next word).
308  *
309  * Side Effects:
310  *	The suffix from the word is placed in the buffer.
311  */
312 static Boolean
VarSuffix(const char * word,Boolean addSpace,Buffer * buf,void * dummy __unused)313 VarSuffix(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
314 {
315 	const char *dot;
316 
317 	dot = strrchr(word, '.');
318 	if (dot != NULL) {
319 		if (addSpace) {
320 			Buf_AddByte(buf, (Byte)' ');
321 		}
322 		dot++;
323 		Buf_Append(buf, dot);
324 		addSpace = TRUE;
325 	}
326 	return (addSpace);
327 }
328 
329 /**
330  * Remove the suffix of the given word and place the result in the
331  * buffer.
332  *
333  * Results:
334  *	TRUE if characters were added to the buffer (a space needs to be
335  *	added to the buffer before the next word).
336  *
337  * Side Effects:
338  *	The trimmed word is added to the buffer.
339  */
340 static Boolean
VarRoot(const char * word,Boolean addSpace,Buffer * buf,void * dummy __unused)341 VarRoot(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
342 {
343 	char *dot;
344 
345 	if (addSpace) {
346 		Buf_AddByte(buf, (Byte)' ');
347 	}
348 
349 	dot = strrchr(word, '.');
350 	if (dot != NULL) {
351 		Buf_AppendRange(buf, word, dot);
352 	} else {
353 		Buf_Append(buf, word);
354 	}
355 	return (TRUE);
356 }
357 
358 /**
359  * Place the word in the buffer if it matches the given pattern.
360  * Callback function for VarModify to implement the :M modifier.
361  * A space will be added if requested.  A pattern is supplied
362  * which the word must match.
363  *
364  * Results:
365  *	TRUE if a space should be placed in the buffer before the next
366  *	word.
367  *
368  * Side Effects:
369  *	The word may be copied to the buffer.
370  */
371 static Boolean
VarMatch(const char * word,Boolean addSpace,Buffer * buf,void * pattern)372 VarMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern)
373 {
374 
375 	if (Str_Match(word, pattern)) {
376 		if (addSpace) {
377 			Buf_AddByte(buf, (Byte)' ');
378 		}
379 		addSpace = TRUE;
380 		Buf_Append(buf, word);
381 	}
382 	return (addSpace);
383 }
384 
385 #ifdef SYSVVARSUB
386 /**
387  * Place the word in the buffer if it matches the given pattern.
388  * Callback function for VarModify to implement the System V %
389  * modifiers.  A space is added if requested.
390  *
391  * Results:
392  *	TRUE if a space should be placed in the buffer before the next
393  *	word.
394  *
395  * Side Effects:
396  *	The word may be copied to the buffer.
397  */
398 static Boolean
VarSYSVMatch(const char * word,Boolean addSpace,Buffer * buf,void * patp)399 VarSYSVMatch(const char *word, Boolean addSpace, Buffer *buf, void *patp)
400 {
401 	int		len;
402 	const char	*ptr;
403 	VarPattern	*pat = (VarPattern *)patp;
404 
405 	if (addSpace)
406 		Buf_AddByte(buf, (Byte)' ');
407 
408 	addSpace = TRUE;
409 
410 	if ((ptr = Str_SYSVMatch(word, Buf_Data(pat->lhs), &len)) != NULL)
411 		Str_SYSVSubst(buf, Buf_Data(pat->rhs), ptr, len);
412 	else
413 		Buf_Append(buf, word);
414 
415 	return (addSpace);
416 }
417 #endif
418 
419 /**
420  * Place the word in the buffer if it doesn't match the given pattern.
421  * Callback function for VarModify to implement the :N modifier.  A
422  * space is added if requested.
423  *
424  * Results:
425  *	TRUE if a space should be placed in the buffer before the next
426  *	word.
427  *
428  * Side Effects:
429  *	The word may be copied to the buffer.
430  */
431 static Boolean
VarNoMatch(const char * word,Boolean addSpace,Buffer * buf,void * pattern)432 VarNoMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern)
433 {
434 
435 	if (!Str_Match(word, pattern)) {
436 		if (addSpace) {
437 			Buf_AddByte(buf, (Byte)' ');
438 		}
439 		addSpace = TRUE;
440 		Buf_Append(buf, word);
441 	}
442 	return (addSpace);
443 }
444 
445 /**
446  * Perform a string-substitution on the given word, placing the
447  * result in the passed buffer.  A space is added if requested.
448  *
449  * Results:
450  *	TRUE if a space is needed before more characters are added.
451  */
452 static Boolean
VarSubstitute(const char * word,Boolean addSpace,Buffer * buf,void * patternp)453 VarSubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp)
454 {
455 	size_t		wordLen;	/* Length of word */
456 	const char	*cp;		/* General pointer */
457 	VarPattern	*pattern = patternp;
458 
459 	wordLen = strlen(word);
460 	if (1) { /* substitute in each word of the variable */
461 		/*
462 		 * Break substitution down into simple anchored cases
463 		 * and if none of them fits, perform the general substitution
464 		 * case.
465 		 */
466 		if ((pattern->flags & VAR_MATCH_START) &&
467 		   (strncmp(word, Buf_Data(pattern->lhs),
468 		    Buf_Size(pattern->lhs)) == 0)) {
469 			/*
470 			 * Anchored at start and beginning of word matches
471 			 * pattern.
472 			 */
473 			if ((pattern->flags & VAR_MATCH_END) &&
474 			    (wordLen == Buf_Size(pattern->lhs))) {
475 				/*
476 				 * Also anchored at end and matches to the end
477 				 * (word is same length as pattern) add space
478 				 * and rhs only if rhs is non-null.
479 				 */
480 				if (Buf_Size(pattern->rhs) != 0) {
481 					if (addSpace) {
482 						Buf_AddByte(buf, (Byte)' ');
483 					}
484 					addSpace = TRUE;
485 					Buf_AppendBuf(buf, pattern->rhs);
486 				}
487 
488 			} else if (pattern->flags & VAR_MATCH_END) {
489 				/*
490 				 * Doesn't match to end -- copy word wholesale
491 				 */
492 				goto nosub;
493 
494 			} else {
495 				/*
496 				 * Matches at start but need to copy in
497 				 * trailing characters.
498 				 */
499 				if ((Buf_Size(pattern->rhs) + wordLen -
500 				    Buf_Size(pattern->lhs)) != 0) {
501 					if (addSpace) {
502 						Buf_AddByte(buf, (Byte)' ');
503 					}
504 					addSpace = TRUE;
505 				}
506 				Buf_AppendBuf(buf, pattern->rhs);
507 				Buf_AddBytes(buf, wordLen -
508 				    Buf_Size(pattern->lhs),
509 				    (word + Buf_Size(pattern->lhs)));
510 			}
511 
512 		} else if (pattern->flags & VAR_MATCH_START) {
513 			/*
514 			 * Had to match at start of word and didn't -- copy
515 			 * whole word.
516 			 */
517 			goto nosub;
518 
519 		} else if (pattern->flags & VAR_MATCH_END) {
520 			/*
521 			 * Anchored at end, Find only place match could occur
522 			 * (leftLen characters from the end of the word) and
523 			 * see if it does. Note that because the $ will be
524 			 * left at the end of the lhs, we have to use strncmp.
525 			 */
526 			cp = word + (wordLen - Buf_Size(pattern->lhs));
527 			if ((cp >= word) && (strncmp(cp, Buf_Data(pattern->lhs),
528 			    Buf_Size(pattern->lhs)) == 0)) {
529 				/*
530 				 * Match found. If we will place characters in
531 				 * the buffer, add a space before hand as
532 				 * indicated by addSpace, then stuff in the
533 				 * initial, unmatched part of the word followed
534 				 * by the right-hand-side.
535 				 */
536 				if ((cp - word) + Buf_Size(pattern->rhs) != 0) {
537 					if (addSpace) {
538 						Buf_AddByte(buf, (Byte)' ');
539 					}
540 					addSpace = TRUE;
541 				}
542 				Buf_AppendRange(buf, word, cp);
543 				Buf_AppendBuf(buf, pattern->rhs);
544 
545 			} else {
546 				/*
547 				 * Had to match at end and didn't. Copy entire
548 				 * word.
549 				 */
550 				goto nosub;
551 			}
552 		} else {
553 			/*
554 			 * Pattern is unanchored: search for the pattern in the
555 			 * word using strstr(3), copying unmatched portions and
556 			 * the right-hand-side for each match found, handling
557 			 * non-global substitutions correctly, etc. When the
558 			 * loop is done, any remaining part of the word (word
559 			 * and wordLen are adjusted accordingly through the
560 			 * loop) is copied straight into the buffer.
561 			 * addSpace is set FALSE as soon as a space is added
562 			 * to the buffer.
563 			 */
564 			Boolean done;
565 			size_t origSize;
566 
567 			done = FALSE;
568 			origSize = Buf_Size(buf);
569 			while (!done) {
570 				cp = strstr(word, Buf_Data(pattern->lhs));
571 				if (cp != NULL) {
572 					if (addSpace && (((cp - word) +
573 					    Buf_Size(pattern->rhs)) != 0)) {
574 						Buf_AddByte(buf, (Byte)' ');
575 						addSpace = FALSE;
576 					}
577 					Buf_AppendRange(buf, word, cp);
578 					Buf_AppendBuf(buf, pattern->rhs);
579 					wordLen -= (cp - word) +
580 					    Buf_Size(pattern->lhs);
581 					word = cp + Buf_Size(pattern->lhs);
582 					if (wordLen == 0 || (pattern->flags &
583 					    VAR_SUB_GLOBAL) == 0) {
584 						done = TRUE;
585 					}
586 				} else {
587 					done = TRUE;
588 				}
589 			}
590 			if (wordLen != 0) {
591 				if (addSpace) {
592 					Buf_AddByte(buf, (Byte)' ');
593 				}
594 				Buf_AddBytes(buf, wordLen, (const Byte *)word);
595 			}
596 
597 			/*
598 			 * If added characters to the buffer, need to add a
599 			 * space before we add any more. If we didn't add any,
600 			 * just return the previous value of addSpace.
601 			 */
602 			return ((Buf_Size(buf) != origSize) || addSpace);
603 		}
604 		/*
605 		 * Common code for anchored substitutions:
606 		 * addSpace was set TRUE if characters were added to the buffer.
607 		 */
608 		return (addSpace);
609 	}
610   nosub:
611 	if (addSpace) {
612 		Buf_AddByte(buf, (Byte)' ');
613 	}
614 	Buf_AddBytes(buf, wordLen, (const Byte *)word);
615 	return (TRUE);
616 }
617 
618 /**
619  * Print the error caused by a regcomp or regexec call.
620  *
621  * Side Effects:
622  *	An error gets printed.
623  */
624 static void
VarREError(int err,regex_t * pat,const char * str)625 VarREError(int err, regex_t *pat, const char *str)
626 {
627 	char   *errbuf;
628 	int     errlen;
629 
630 	errlen = regerror(err, pat, 0, 0);
631 	errbuf = emalloc(errlen);
632 	regerror(err, pat, errbuf, errlen);
633 	Error("%s: %s", str, errbuf);
634 	free(errbuf);
635 }
636 
637 
638 /**
639  * Perform a regex substitution on the given word, placing the
640  * result in the passed buffer.  A space is added if requested.
641  *
642  * Results:
643  *	TRUE if a space is needed before more characters are added.
644  */
645 static Boolean
VarRESubstitute(const char * word,Boolean addSpace,Buffer * buf,void * patternp)646 VarRESubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp)
647 {
648 	VarPattern	*pat;
649 	int		xrv;
650 	const char	*wp;
651 	char		*rp;
652 	int		added;
653 	int		flags = 0;
654 
655 #define	MAYBE_ADD_SPACE()			\
656 	if (addSpace && !added)			\
657 		Buf_AddByte(buf, (Byte)' ');	\
658 	added = 1
659 
660 	added = 0;
661 	wp = word;
662 	pat = patternp;
663 
664 	if ((pat->flags & (VAR_SUB_ONE | VAR_SUB_MATCHED)) ==
665 	    (VAR_SUB_ONE | VAR_SUB_MATCHED)) {
666 		xrv = REG_NOMATCH;
667 	} else {
668   tryagain:
669 		xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags);
670 	}
671 
672 	switch (xrv) {
673 	  case 0:
674 		pat->flags |= VAR_SUB_MATCHED;
675 		if (pat->matches[0].rm_so > 0) {
676 			MAYBE_ADD_SPACE();
677 			Buf_AddBytes(buf, pat->matches[0].rm_so,
678 			    (const Byte *)wp);
679 		}
680 
681 		for (rp = Buf_Data(pat->rhs); *rp; rp++) {
682 			if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
683 				MAYBE_ADD_SPACE();
684 				Buf_AddByte(buf, (Byte)rp[1]);
685 				rp++;
686 
687 			} else if ((*rp == '&') ||
688 			    ((*rp == '\\') && isdigit((unsigned char)rp[1]))) {
689 				int	n;
690 				const char *subbuf;
691 				int	sublen;
692 				char	errstr[3];
693 
694 				if (*rp == '&') {
695 					n = 0;
696 					errstr[0] = '&';
697 					errstr[1] = '\0';
698 				} else {
699 					n = rp[1] - '0';
700 					errstr[0] = '\\';
701 					errstr[1] = rp[1];
702 					errstr[2] = '\0';
703 					rp++;
704 				}
705 
706 				if (n > pat->nsub) {
707 					Error("No subexpression %s",
708 					    &errstr[0]);
709 					subbuf = "";
710 					sublen = 0;
711 
712 				} else if ((pat->matches[n].rm_so == -1) &&
713 				    (pat->matches[n].rm_eo == -1)) {
714 					Error("No match for subexpression %s",
715 					    &errstr[0]);
716 					subbuf = "";
717 					sublen = 0;
718 
719 				} else {
720 					subbuf = wp + pat->matches[n].rm_so;
721 					sublen = pat->matches[n].rm_eo -
722 					    pat->matches[n].rm_so;
723 				}
724 
725 				if (sublen > 0) {
726 					MAYBE_ADD_SPACE();
727 					Buf_AddBytes(buf, sublen,
728 					    (const Byte *)subbuf);
729 				}
730 			} else {
731 				MAYBE_ADD_SPACE();
732 				Buf_AddByte(buf, (Byte)*rp);
733 			}
734 		}
735 		wp += pat->matches[0].rm_eo;
736 		if (pat->flags & VAR_SUB_GLOBAL) {
737 			flags |= REG_NOTBOL;
738 			if (pat->matches[0].rm_so == 0 &&
739 			    pat->matches[0].rm_eo == 0) {
740 				MAYBE_ADD_SPACE();
741 				Buf_AddByte(buf, (Byte)*wp);
742 				wp++;
743 			}
744 			if (*wp)
745 				goto tryagain;
746 		}
747 		if (*wp) {
748 			MAYBE_ADD_SPACE();
749 			Buf_Append(buf, wp);
750 		}
751 		break;
752 
753 	  default:
754 		VarREError(xrv, &pat->re, "Unexpected regex error");
755 		/* fall through */
756 
757 	  case REG_NOMATCH:
758 		if (*wp) {
759 			MAYBE_ADD_SPACE();
760 			Buf_Append(buf, wp);
761 		}
762 		break;
763 	}
764 	return (addSpace || added);
765 }
766 
767 /**
768  * Find a variable in a variable list.
769  */
770 static Var *
VarLookup(Lst * vlist,const char * name)771 VarLookup(Lst *vlist, const char *name)
772 {
773 	LstNode	*ln;
774 
775 	LST_FOREACH(ln, vlist)
776 		if (strcmp(((const Var *)Lst_Datum(ln))->name, name) == 0)
777 			return (Lst_Datum(ln));
778 	return (NULL);
779 }
780 
781 /**
782  * Expand a variable name's embedded variables in the given context.
783  *
784  * Results:
785  *	The contents of name, possibly expanded.
786  */
787 static char *
VarPossiblyExpand(const char * name,GNode * ctxt)788 VarPossiblyExpand(const char *name, GNode *ctxt)
789 {
790 	Buffer	*buf;
791 
792 	if (strchr(name, '$') != NULL) {
793 		buf = Var_Subst(name, ctxt, 0);
794 		return (Buf_Peel(buf));
795 	} else {
796 		return estrdup(name);
797 	}
798 }
799 
800 /**
801  * If the variable name begins with a '.', it could very well be
802  * one of the local ones.  We check the name against all the local
803  * variables and substitute the short version in for 'name' if it
804  * matches one of them.
805  */
806 static const char *
VarLocal(const char name[])807 VarLocal(const char name[])
808 {
809 	if (name[0] == '.') {
810 		switch (name[1]) {
811 		case 'A':
812 			if (!strcmp(name, ".ALLSRC"))
813 				return (ALLSRC);
814 			if (!strcmp(name, ".ARCHIVE"))
815 				return (ARCHIVE);
816 			break;
817 		case 'I':
818 			if (!strcmp(name, ".IMPSRC"))
819 				return (IMPSRC);
820 			break;
821 		case 'M':
822 			if (!strcmp(name, ".MEMBER"))
823 				return (MEMBER);
824 			break;
825 		case 'O':
826 			if (!strcmp(name, ".OODATE"))
827 				return (OODATE);
828 			break;
829 		case 'P':
830 			if (!strcmp(name, ".PREFIX"))
831 				return (PREFIX);
832 			break;
833 		case 'T':
834 			if (!strcmp(name, ".TARGET"))
835 				return (TARGET);
836 			break;
837 		default:
838 			break;
839 		}
840 	}
841 	return (name);
842 }
843 
844 /**
845  * Find the given variable in the given context and the environment.
846  *
847  * Results:
848  *	A pointer to the structure describing the desired variable or
849  *	NULL if the variable does not exist.
850  */
851 static Var *
VarFindEnv(const char name[],GNode * ctxt)852 VarFindEnv(const char name[], GNode *ctxt)
853 {
854 	Var	*var;
855 
856 	name = VarLocal(name);
857 
858 	if ((var = VarLookup(&ctxt->context, name)) != NULL)
859 		return (var);
860 
861 	if ((var = VarLookup(&VAR_ENV->context, name)) != NULL)
862 		return (var);
863 
864 	return (NULL);
865 }
866 
867 /**
868  * Look for the variable in the given context.
869  */
870 static Var *
VarFindOnly(const char name[],GNode * ctxt)871 VarFindOnly(const char name[], GNode *ctxt)
872 {
873 	Var	*var;
874 
875 	name = VarLocal(name);
876 
877 	if ((var = VarLookup(&ctxt->context, name)) != NULL)
878 		return (var);
879 
880 	return (NULL);
881 }
882 
883 /**
884  * Look for the variable in all contexts.
885  */
886 static Var *
VarFindAny(const char name[],GNode * ctxt)887 VarFindAny(const char name[], GNode *ctxt)
888 {
889 	Boolean	localCheckEnvFirst;
890 	LstNode	*ln;
891 	Var	*var;
892 
893 	name = VarLocal(name);
894 
895 	/*
896 	 * Note whether this is one of the specific variables we were told
897 	 * through the -E flag to use environment-variable-override for.
898 	 */
899 	localCheckEnvFirst = FALSE;
900 	LST_FOREACH(ln, &envFirstVars) {
901 		if (strcmp(Lst_Datum(ln), name) == 0) {
902 			localCheckEnvFirst = TRUE;
903 			break;
904 		}
905 	}
906 
907 	/*
908 	 * First look for the variable in the given context. If it's not there,
909 	 * look for it in VAR_CMD, VAR_GLOBAL and the environment,
910 	 * in that order, depending on the FIND_* flags in 'flags'
911 	 */
912 	if ((var = VarLookup(&ctxt->context, name)) != NULL)
913 		return (var);
914 
915 	/* not there - try command line context */
916 	if (ctxt != VAR_CMD) {
917 		if ((var = VarLookup(&VAR_CMD->context, name)) != NULL)
918 			return (var);
919 	}
920 
921 	/* not there - try global context, but only if not -e/-E */
922 	if (ctxt != VAR_GLOBAL && (!checkEnvFirst && !localCheckEnvFirst)) {
923 		if ((var = VarLookup(&VAR_GLOBAL->context, name)) != NULL)
924 			return (var);
925 	}
926 
927 	if ((var = VarLookup(&VAR_ENV->context, name)) != NULL)
928 		return (var);
929 
930 	/* deferred check for the environment (in case of -e/-E) */
931 	if ((ctxt != VAR_GLOBAL) && (checkEnvFirst || localCheckEnvFirst)) {
932 		if ((var = VarLookup(&VAR_GLOBAL->context, name)) != NULL)
933 			return (var);
934 	}
935 
936 	return (NULL);
937 }
938 
939 /**
940  * Add a new variable of name name and value val to the given context.
941  *
942  * Side Effects:
943  *	The new variable is placed at the front of the given context
944  *	The name and val arguments are duplicated so they may
945  *	safely be freed.
946  */
947 static Var *
VarAdd(const char * name,const char * val,GNode * ctxt)948 VarAdd(const char *name, const char *val, GNode *ctxt)
949 {
950 	Var *v;
951 
952 	Lst_AtFront(&ctxt->context, v = VarCreate(name, val, 0));
953 	DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, name, val));
954 	return (v);
955 }
956 
957 /**
958  * Remove a variable from a context.
959  *
960  * Side Effects:
961  *	The Var structure is removed and freed.
962  */
963 void
Var_Delete(const char * name,GNode * ctxt)964 Var_Delete(const char *name, GNode *ctxt)
965 {
966 	LstNode *ln;
967 
968 	DEBUGF(VAR, ("%s:delete %s\n", ctxt->name, name));
969 	LST_FOREACH(ln, &ctxt->context) {
970 		if (strcmp(((const Var *)Lst_Datum(ln))->name, name) == 0) {
971 			VarDestroy(Lst_Datum(ln), TRUE);
972 			Lst_Remove(&ctxt->context, ln);
973 			break;
974 		}
975 	}
976 }
977 
978 /**
979  * Set the variable name to the value val in the given context.
980  *
981  * Side Effects:
982  *	If the variable doesn't yet exist, a new record is created for it.
983  *	Else the old value is freed and the new one stuck in its place
984  *
985  * Notes:
986  *	The variable is searched for only in its context before being
987  *	created in that context. I.e. if the context is VAR_GLOBAL,
988  *	only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only
989  *	VAR_CMD->context is searched. This is done to avoid the literally
990  *	thousands of unnecessary strcmp's that used to be done to
991  *	set, say, $(@) or $(<).
992  */
993 void
Var_Set(const char * name,const char * val,GNode * ctxt)994 Var_Set(const char *name, const char *val, GNode *ctxt)
995 {
996 	Var    *v;
997 	char   *n;
998 
999 	/*
1000 	 * We only look for a variable in the given context since anything
1001 	 * set here will override anything in a lower context, so there's not
1002 	 * much point in searching them all just to save a bit of memory...
1003 	 */
1004 	n = VarPossiblyExpand(name, ctxt);
1005 	v = VarFindOnly(n, ctxt);
1006 	if (v == NULL) {
1007 		v = VarAdd(n, val, ctxt);
1008 	} else {
1009 		Buf_Clear(v->val);
1010 		Buf_Append(v->val, val);
1011 		DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, n, val));
1012 	}
1013 
1014 	if (ctxt == VAR_CMD || (v->flags & VAR_TO_ENV)) {
1015 		/*
1016 		 * Any variables given on the command line
1017 		 * are automatically exported to the
1018 		 * environment (as per POSIX standard)
1019 		 */
1020 		setenv(n, val, 1);
1021 	}
1022 
1023 	free(n);
1024 }
1025 
1026 /**
1027  * Set the a global name variable to the value.
1028  */
1029 void
Var_SetGlobal(const char name[],const char value[])1030 Var_SetGlobal(const char name[], const char value[])
1031 {
1032 
1033 	Var_Set(name, value, VAR_GLOBAL);
1034 }
1035 
1036 
1037 /**
1038  * Set the VAR_TO_ENV flag on a variable
1039  */
1040 void
Var_SetEnv(const char * name,GNode * ctxt)1041 Var_SetEnv(const char *name, GNode *ctxt)
1042 {
1043 	Var    *v;
1044 
1045 	v = VarFindOnly(name, VAR_CMD);
1046 	if (v != NULL) {
1047 		/*
1048 		 * Do not allow .EXPORT: to be set on variables
1049 		 * from the comand line or MAKEFLAGS.
1050 		 */
1051 		Error(
1052 		    "Warning: Did not set .EXPORTVAR: on %s because it "
1053 		    "is from the comand line or MAKEFLAGS", name);
1054 		return;
1055 	}
1056 
1057 	v = VarFindAny(name, ctxt);
1058 	if (v == NULL) {
1059 		Lst_AtFront(&VAR_ENV->context,
1060 		    VarCreate(name, NULL, VAR_TO_ENV));
1061 		setenv(name, "", 1);
1062 		Error("Warning: .EXPORTVAR: set on undefined variable %s", name);
1063 	} else {
1064 		if ((v->flags & VAR_TO_ENV) == 0) {
1065 			v->flags |= VAR_TO_ENV;
1066 			setenv(v->name, Buf_Data(v->val), 1);
1067 		}
1068 	}
1069 }
1070 
1071 /**
1072  * The variable of the given name has the given value appended to it in
1073  * the given context.
1074  *
1075  * Side Effects:
1076  *	If the variable doesn't exist, it is created. Else the strings
1077  *	are concatenated (with a space in between).
1078  *
1079  * Notes:
1080  *	Only if the variable is being sought in the global context is the
1081  *	environment searched.
1082  *	XXX: Knows its calling circumstances in that if called with ctxt
1083  *	an actual target, it will only search that context since only
1084  *	a local variable could be being appended to. This is actually
1085  *	a big win and must be tolerated.
1086  */
1087 void
Var_Append(const char * name,const char * val,GNode * ctxt)1088 Var_Append(const char *name, const char *val, GNode *ctxt)
1089 {
1090 	Var	*v;
1091 	char	*n;
1092 
1093 	n = VarPossiblyExpand(name, ctxt);
1094 	if (ctxt == VAR_GLOBAL) {
1095 		v = VarFindEnv(n, ctxt);
1096 	} else {
1097 		v = VarFindOnly(n, ctxt);
1098 	}
1099 	if (v == NULL) {
1100 		VarAdd(n, val, ctxt);
1101 	} else {
1102 		Buf_AddByte(v->val, (Byte)' ');
1103 		Buf_Append(v->val, val);
1104 		DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, n, Buf_Data(v->val)));
1105 	}
1106 	free(n);
1107 }
1108 
1109 /**
1110  * See if the given variable exists.
1111  *
1112  * Results:
1113  *	TRUE if it does, FALSE if it doesn't
1114  */
1115 Boolean
Var_Exists(const char * name,GNode * ctxt)1116 Var_Exists(const char *name, GNode *ctxt)
1117 {
1118 	Var	*v;
1119 	char	*n;
1120 
1121 	n = VarPossiblyExpand(name, ctxt);
1122 	v = VarFindAny(n, ctxt);
1123 	if (v == NULL) {
1124 		free(n);
1125 		return (FALSE);
1126 	} else {
1127 		free(n);
1128 		return (TRUE);
1129 	}
1130 }
1131 
1132 /**
1133  * Return the value of the named variable in the given context
1134  *
1135  * Results:
1136  *	The value if the variable exists, NULL if it doesn't.
1137  */
1138 const char *
Var_Value(const char name[],GNode * ctxt)1139 Var_Value(const char name[], GNode *ctxt)
1140 {
1141 	Var	*v;
1142 	char	*n;
1143 
1144 	n = VarPossiblyExpand(name, ctxt);
1145 	v = VarFindAny(n, ctxt);
1146 	free(n);
1147 	if (v == NULL) {
1148 		return (NULL);
1149 	} else {
1150 		return (Buf_Data(v->val));
1151 	}
1152 }
1153 
1154 /**
1155  * Modify each of the words of the passed string using the given
1156  * function. Used to implement all modifiers.
1157  *
1158  * Results:
1159  *	A string of all the words modified appropriately.
1160  *
1161  * Side Effects:
1162  *	Uses brk_string() so it invalidates any previous call to
1163  *	brk_string().
1164  */
1165 static char *
VarModify(const char * str,VarModifyProc * modProc,void * datum)1166 VarModify(const char *str, VarModifyProc *modProc, void *datum)
1167 {
1168 	ArgArray	aa;
1169 	Buffer		*buf;		/* Buffer for the new string */
1170 	int		i;
1171 	Boolean		addSpace;	/*
1172 					 * TRUE if need to add a space to
1173 					 * the buffer before adding the
1174 					 * trimmed word
1175 					 */
1176 
1177 	brk_string(&aa, str, FALSE);
1178 
1179 	addSpace = FALSE;
1180 	buf = Buf_Init(0);
1181 	for (i = 1; i < aa.argc; i++)
1182 		addSpace = (*modProc)(aa.argv[i], addSpace, buf, datum);
1183 
1184 	ArgArray_Done(&aa);
1185 	return (Buf_Peel(buf));
1186 }
1187 
1188 /**
1189  * Sort the words in the string.
1190  *
1191  * Input:
1192  *	str		String whose words should be sorted
1193  *	cmp		A comparison function to control the ordering
1194  *
1195  * Results:
1196  *	A string containing the words sorted
1197  */
1198 static char *
VarSortWords(const char * str,int (* cmp)(const void *,const void *))1199 VarSortWords(const char *str, int (*cmp)(const void *, const void *))
1200 {
1201 	ArgArray	aa;
1202 	Buffer		*buf;
1203 	int		i;
1204 
1205 	brk_string(&aa, str, FALSE);
1206 	qsort(aa.argv + 1, aa.argc - 1, sizeof(char *), cmp);
1207 
1208 	buf = Buf_Init(0);
1209 	for (i = 1; i < aa.argc; i++) {
1210 		Buf_Append(buf, aa.argv[i]);
1211 		Buf_AddByte(buf, (Byte)((i < aa.argc - 1) ? ' ' : '\0'));
1212 	}
1213 
1214 	ArgArray_Done(&aa);
1215 	return (Buf_Peel(buf));
1216 }
1217 
1218 static int
SortIncreasing(const void * l,const void * r)1219 SortIncreasing(const void *l, const void *r)
1220 {
1221 
1222 	return (strcmp(*(const char* const*)l, *(const char* const*)r));
1223 }
1224 
1225 /**
1226  * Remove adjacent duplicate words.
1227  *
1228  * Results:
1229  *	A string containing the resulting words.
1230  */
1231 static char *
VarUniq(const char * str)1232 VarUniq(const char *str)
1233 {
1234 	ArgArray	aa;
1235 	Buffer		*buf;		    /* Buffer for new string */
1236 	int		i, j;
1237 
1238 	buf = Buf_Init(0);
1239 	brk_string(&aa, str, FALSE);
1240 
1241 	if (aa.argc > 2) {
1242 		for (j = 1, i = 2; i < aa.argc; i++) {
1243 			if (strcmp(aa.argv[i], aa.argv[j]) != 0 && (++j != i))
1244 				aa.argv[j] = aa.argv[i];
1245 		}
1246 		aa.argc = j + 1;
1247 	}
1248 
1249 	for (i = 1; i < aa.argc; i++) {
1250 		Buf_AddBytes(buf, strlen(aa.argv[i]), (Byte *)aa.argv[i]);
1251 		if (i != aa.argc - 1)
1252 			Buf_AddByte(buf, ' ');
1253 	}
1254 	Buf_AddByte(buf, '\0');
1255 
1256 	ArgArray_Done(&aa);
1257 	return (Buf_Peel(buf));
1258 }
1259 
1260 /**
1261  * Pass through the tstr looking for 1) escaped delimiters,
1262  * '$'s and backslashes (place the escaped character in
1263  * uninterpreted) and 2) unescaped $'s that aren't before
1264  * the delimiter (expand the variable substitution).
1265  * Return the expanded string or NULL if the delimiter was missing
1266  * If pattern is specified, handle escaped ampersands, and replace
1267  * unescaped ampersands with the lhs of the pattern.
1268  *
1269  * Results:
1270  *	A string of all the words modified appropriately.
1271  *	If length is specified, return the string length of the buffer
1272  *	If flags is specified and the last character of the pattern is a
1273  *	$ set the VAR_MATCH_END bit of flags.
1274  */
1275 static Buffer *
VarGetPattern(VarParser * vp,int delim,int * flags,VarPattern * patt)1276 VarGetPattern(VarParser *vp, int delim, int *flags, VarPattern *patt)
1277 {
1278 	Buffer		*buf;
1279 
1280 	buf = Buf_Init(0);
1281 
1282 	/*
1283 	 * Skim through until the matching delimiter is found; pick up
1284 	 * variable substitutions on the way. Also allow backslashes to quote
1285 	 * the delimiter, $, and \, but don't touch other backslashes.
1286 	 */
1287 	while (*vp->ptr != '\0') {
1288 		if (*vp->ptr == delim) {
1289 			return (buf);
1290 
1291 		} else if ((vp->ptr[0] == '\\') &&
1292 		    ((vp->ptr[1] == delim) ||
1293 		     (vp->ptr[1] == '\\') ||
1294 		     (vp->ptr[1] == '$') ||
1295 		     (vp->ptr[1] == '&' && patt != NULL))) {
1296 			vp->ptr++;		/* consume backslash */
1297 			Buf_AddByte(buf, (Byte)vp->ptr[0]);
1298 			vp->ptr++;
1299 
1300 		} else if (vp->ptr[0] == '$') {
1301 			if (vp->ptr[1] == delim) {
1302 				if (flags == NULL) {
1303 					Buf_AddByte(buf, (Byte)vp->ptr[0]);
1304 					vp->ptr++;
1305 				} else {
1306 					/*
1307 					 * Unescaped $ at end of patt =>
1308 					 * anchor patt at end.
1309 					 */
1310 					*flags |= VAR_MATCH_END;
1311 					vp->ptr++;
1312 				}
1313 			} else {
1314 				VarParser	subvp = {
1315 					vp->ptr,
1316 					vp->ptr,
1317 					vp->ctxt,
1318 					vp->err,
1319 					vp->execute
1320 				};
1321 				char   *rval;
1322 				Boolean rfree;
1323 
1324 				/*
1325 				 * If unescaped dollar sign not
1326 				 * before the delimiter, assume it's
1327 				 * a variable substitution and
1328 				 * recurse.
1329 				 */
1330 				rval = VarParse(&subvp, &rfree);
1331 				Buf_Append(buf, rval);
1332 				if (rfree)
1333 					free(rval);
1334 				vp->ptr = subvp.ptr;
1335 			}
1336 		} else if (vp->ptr[0] == '&' && patt != NULL) {
1337 			Buf_AppendBuf(buf, patt->lhs);
1338 			vp->ptr++;
1339 		} else {
1340 			Buf_AddByte(buf, (Byte)vp->ptr[0]);
1341 			vp->ptr++;
1342 		}
1343 	}
1344 
1345 	Buf_Destroy(buf, TRUE);
1346 	return (NULL);
1347 }
1348 
1349 /**
1350  * Make sure this variable is fully expanded.
1351  */
1352 static char *
VarExpand(Var * v,VarParser * vp)1353 VarExpand(Var *v, VarParser *vp)
1354 {
1355 	char	*value;
1356 	char	*result;
1357 
1358 	if (v->flags & VAR_IN_USE) {
1359 		Fatal("Variable %s is recursive.", v->name);
1360 		/* NOTREACHED */
1361 	}
1362 
1363 	v->flags |= VAR_IN_USE;
1364 
1365 	/*
1366 	 * Before doing any modification, we have to make sure the
1367 	 * value has been fully expanded. If it looks like recursion
1368 	 * might be necessary (there's a dollar sign somewhere in the
1369 	 * variable's value) we just call Var_Subst to do any other
1370 	 * substitutions that are necessary. Note that the value
1371 	 * returned by Var_Subst will have been
1372 	 * dynamically-allocated, so it will need freeing when we
1373 	 * return.
1374 	 */
1375 	value = Buf_Data(v->val);
1376 	if (strchr(value, '$') == NULL) {
1377 		result = strdup(value);
1378 	} else {
1379 		Buffer	*buf;
1380 
1381 		buf = Var_Subst(value, vp->ctxt, vp->err);
1382 		result = Buf_Peel(buf);
1383 	}
1384 
1385 	v->flags &= ~VAR_IN_USE;
1386 
1387 	return (result);
1388 }
1389 
1390 /**
1391  * Select only those words in value that match the modifier.
1392  */
1393 static char *
modifier_M(VarParser * vp,const char value[],char endc)1394 modifier_M(VarParser *vp, const char value[], char endc)
1395 {
1396 	char	*patt;
1397 	char	*ptr;
1398 	char	*newValue;
1399 	char	modifier;
1400 
1401 	modifier = vp->ptr[0];
1402 	vp->ptr++;	/* consume 'M' or 'N' */
1403 
1404 	/*
1405 	 * Compress the \:'s out of the pattern, so allocate enough
1406 	 * room to hold the uncompressed pattern and compress the
1407 	 * pattern into that space.
1408 	 */
1409 	patt = estrdup(vp->ptr);
1410 	ptr = patt;
1411 	while (vp->ptr[0] != '\0') {
1412 		if (vp->ptr[0] == endc || vp->ptr[0] == ':') {
1413 			break;
1414 		}
1415 		if (vp->ptr[0] == '\\' &&
1416 		    (vp->ptr[1] == endc || vp->ptr[1] == ':')) {
1417 			vp->ptr++;	/* consume backslash */
1418 		}
1419 		*ptr = vp->ptr[0];
1420 		ptr++;
1421 		vp->ptr++;
1422 	}
1423 	*ptr = '\0';
1424 	DEBUGF(VAR, ("Pattern :%s\n", patt));
1425 
1426 	if (modifier == 'M') {
1427 		newValue = VarModify(value, VarMatch, patt);
1428 	} else {
1429 		newValue = VarModify(value, VarNoMatch, patt);
1430 	}
1431 	free(patt);
1432 
1433 	return (newValue);
1434 }
1435 
1436 /**
1437  * Substitute the replacement string for the pattern.  The substitution
1438  * is applied to each word in value.
1439  */
1440 static char *
modifier_S(VarParser * vp,const char value[],Var * v)1441 modifier_S(VarParser *vp, const char value[], Var *v)
1442 {
1443 	VarPattern	patt;
1444 	char		delim;
1445 	char		*newValue;
1446 
1447 	patt.flags = 0;
1448 
1449 	vp->ptr++;		/* consume 'S' */
1450 
1451 	delim = *vp->ptr;	/* used to find end of pattern */
1452 	vp->ptr++;		/* consume 1st delim */
1453 
1454 	/*
1455 	 * If pattern begins with '^', it is anchored to the start of the
1456 	 * word -- skip over it and flag pattern.
1457 	 */
1458 	if (*vp->ptr == '^') {
1459 		patt.flags |= VAR_MATCH_START;
1460 		vp->ptr++;
1461 	}
1462 
1463 	patt.lhs = VarGetPattern(vp, delim, &patt.flags, NULL);
1464 	if (patt.lhs == NULL) {
1465 		/*
1466 		 * LHS didn't end with the delim, complain and exit.
1467 		 */
1468 		Fatal("Unclosed substitution for %s (%c missing)",
1469 		    v->name, delim);
1470 	}
1471 
1472 	vp->ptr++;	/* consume 2nd delim */
1473 
1474 	patt.rhs = VarGetPattern(vp, delim, NULL, &patt);
1475 	if (patt.rhs == NULL) {
1476 		/*
1477 		 * RHS didn't end with the delim, complain and exit.
1478 		 */
1479 		Fatal("Unclosed substitution for %s (%c missing)",
1480 		    v->name, delim);
1481 	}
1482 
1483 	vp->ptr++;	/* consume last delim */
1484 
1485 	/*
1486 	 * Check for global substitution. If 'g' after the final delimiter,
1487 	 * substitution is global and is marked that way.
1488 	 */
1489 	if (vp->ptr[0] == 'g') {
1490 		patt.flags |= VAR_SUB_GLOBAL;
1491 		vp->ptr++;
1492 	}
1493 
1494 	/*
1495 	 * Global substitution of the empty string causes an infinite number
1496 	 * of matches, unless anchored by '^' (start of string) or '$' (end
1497 	 * of string). Catch the infinite substitution here. Note that flags
1498 	 * can only contain the 3 bits we're interested in so we don't have
1499 	 * to mask unrelated bits. We can test for equality.
1500 	 */
1501 	if (Buf_Size(patt.lhs) == 0 && patt.flags == VAR_SUB_GLOBAL)
1502 		Fatal("Global substitution of the empty string");
1503 
1504 	newValue = VarModify(value, VarSubstitute, &patt);
1505 
1506 	/*
1507 	 * Free the two strings.
1508 	 */
1509 	free(patt.lhs);
1510 	free(patt.rhs);
1511 
1512 	return (newValue);
1513 }
1514 
1515 static char *
modifier_C(VarParser * vp,char value[],Var * v)1516 modifier_C(VarParser *vp, char value[], Var *v)
1517 {
1518 	VarPattern	patt;
1519 	char		delim;
1520 	int		error;
1521 	char		*newValue;
1522 
1523 	patt.flags = 0;
1524 
1525 	vp->ptr++;		/* consume 'C' */
1526 
1527 	delim = *vp->ptr;	/* delimiter between sections */
1528 
1529 	vp->ptr++;		/* consume 1st delim */
1530 
1531 	patt.lhs = VarGetPattern(vp, delim, NULL, NULL);
1532 	if (patt.lhs == NULL) {
1533 		Fatal("Unclosed substitution for %s (%c missing)",
1534 		     v->name, delim);
1535 	}
1536 
1537 	vp->ptr++;		/* consume 2st delim */
1538 
1539 	patt.rhs = VarGetPattern(vp, delim, NULL, NULL);
1540 	if (patt.rhs == NULL) {
1541 		Fatal("Unclosed substitution for %s (%c missing)",
1542 		     v->name, delim);
1543 	}
1544 
1545 	vp->ptr++;		/* consume last delim */
1546 
1547 	switch (*vp->ptr) {
1548 	case 'g':
1549 		patt.flags |= VAR_SUB_GLOBAL;
1550 		vp->ptr++;		/* consume 'g' */
1551 		break;
1552 	case '1':
1553 		patt.flags |= VAR_SUB_ONE;
1554 		vp->ptr++;		/* consume '1' */
1555 		break;
1556 	default:
1557 		break;
1558 	}
1559 
1560 	error = regcomp(&patt.re, Buf_Data(patt.lhs), REG_EXTENDED);
1561 	if (error) {
1562 		VarREError(error, &patt.re, "RE substitution error");
1563 		free(patt.rhs);
1564 		free(patt.lhs);
1565 		return (var_Error);
1566 	}
1567 
1568 	patt.nsub = patt.re.re_nsub + 1;
1569 	if (patt.nsub < 1)
1570 		patt.nsub = 1;
1571 	if (patt.nsub > 10)
1572 		patt.nsub = 10;
1573 	patt.matches = emalloc(patt.nsub * sizeof(regmatch_t));
1574 
1575 	newValue = VarModify(value, VarRESubstitute, &patt);
1576 
1577 	regfree(&patt.re);
1578 	free(patt.matches);
1579 	free(patt.rhs);
1580 	free(patt.lhs);
1581 
1582 	return (newValue);
1583 }
1584 
1585 static char *
sysVvarsub(VarParser * vp,char startc,Var * v,const char value[])1586 sysVvarsub(VarParser *vp, char startc, Var *v, const char value[])
1587 {
1588 #ifdef SYSVVARSUB
1589 	/*
1590 	 * This can either be a bogus modifier or a System-V substitution
1591 	 * command.
1592 	 */
1593 	char		endc;
1594 	VarPattern	patt;
1595 	Boolean		eqFound;
1596 	int		cnt;
1597 	char		*newStr;
1598 	const char	*cp;
1599 
1600 	endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE;
1601 
1602 	patt.flags = 0;
1603 
1604 	/*
1605 	 * First we make a pass through the string trying to verify it is a
1606 	 * SYSV-make-style translation: it must be: <string1>=<string2>)
1607 	 */
1608 	eqFound = FALSE;
1609 	cp = vp->ptr;
1610 	cnt = 1;
1611 	while (*cp != '\0' && cnt) {
1612 		if (*cp == '=') {
1613 			eqFound = TRUE;
1614 			/* continue looking for endc */
1615 		} else if (*cp == endc)
1616 			cnt--;
1617 		else if (*cp == startc)
1618 			cnt++;
1619 		if (cnt)
1620 			cp++;
1621 	}
1622 
1623 	if (*cp == endc && eqFound) {
1624 		/*
1625 		 * Now we break this sucker into the lhs and rhs.
1626 		 */
1627 		patt.lhs = VarGetPattern(vp, '=', &patt.flags, NULL);
1628 		if (patt.lhs == NULL) {
1629 			Fatal("Unclosed substitution for %s (%c missing)",
1630 			      v->name, '=');
1631 		}
1632 		vp->ptr++;	/* consume '=' */
1633 
1634 		patt.rhs = VarGetPattern(vp, endc, NULL, &patt);
1635 		if (patt.rhs == NULL) {
1636 			Fatal("Unclosed substitution for %s (%c missing)",
1637 			      v->name, endc);
1638 		}
1639 
1640 		/*
1641 		 * SYSV modifications happen through the whole string. Note
1642 		 * the pattern is anchored at the end.
1643 		 */
1644 		newStr = VarModify(value, VarSYSVMatch, &patt);
1645 
1646 		free(patt.lhs);
1647 		free(patt.rhs);
1648 	} else
1649 #endif
1650 	{
1651 		Error("Unknown modifier '%c'\n", *vp->ptr);
1652 		vp->ptr++;
1653 		while (*vp->ptr != '\0') {
1654 			if (*vp->ptr == endc && *vp->ptr == ':') {
1655 				break;
1656 			}
1657 			vp->ptr++;
1658 		}
1659 		newStr = var_Error;
1660 	}
1661 
1662 	return (newStr);
1663 }
1664 
1665 /**
1666  * Quote shell meta-characters in the string
1667  *
1668  * Results:
1669  *	The quoted string
1670  */
1671 static char *
Var_Quote(const char * str)1672 Var_Quote(const char *str)
1673 {
1674 	Buffer *buf;
1675 	/* This should cover most shells :-( */
1676 	static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1677 
1678 	buf = Buf_Init(MAKE_BSIZE);
1679 	for (; *str; str++) {
1680 		if (strchr(meta, *str) != NULL)
1681 			Buf_AddByte(buf, (Byte)'\\');
1682 		Buf_AddByte(buf, (Byte)*str);
1683 	}
1684 
1685 	return (Buf_Peel(buf));
1686 }
1687 
1688 
1689 /*
1690  * Now we need to apply any modifiers the user wants applied.
1691  * These are:
1692  *	:M<pattern>
1693  *		words which match the given <pattern>.
1694  *		<pattern> is of the standard file
1695  *		wildcarding form.
1696  *	:N<pattern>
1697  *		words which do not match the given <pattern>
1698  *		<pattern> is of the standard file
1699  *		wildcarding form.
1700  *	:S<d><pat1><d><pat2><d>[g]
1701  *		Substitute <pat2> for <pat1> in the value
1702  *	:C<d><pat1><d><pat2><d>[g]
1703  *		Substitute <pat2> for regex <pat1> in the value
1704  *	:H	Substitute the head of each word
1705  *	:T	Substitute the tail of each word
1706  *	:E	Substitute the extension (minus '.') of
1707  *		each word
1708  *	:R	Substitute the root of each word
1709  *		(pathname minus the suffix).
1710  *	:lhs=rhs
1711  *		Like :S, but the rhs goes to the end of
1712  *		the invocation.
1713  *	:U	Converts variable to upper-case.
1714  *	:L	Converts variable to lower-case.
1715  *	:O	("Order") Alphabeticaly sort words in variable.
1716  *	:u	("uniq") Remove adjacent duplicate words.
1717  */
1718 static char *
ParseModifier(VarParser * vp,char startc,Var * v,Boolean * freeResult)1719 ParseModifier(VarParser *vp, char startc, Var *v, Boolean *freeResult)
1720 {
1721 	char	*value;
1722 	char	endc;
1723 
1724 	value = VarExpand(v, vp);
1725 	*freeResult = TRUE;
1726 
1727 	endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE;
1728 
1729 	vp->ptr++;	/* consume first colon */
1730 
1731 	while (*vp->ptr != '\0') {
1732 		char	*newStr;	/* New value to return */
1733 
1734 		if (*vp->ptr == endc) {
1735 			return (value);
1736 		}
1737 
1738 		DEBUGF(VAR, ("Applying :%c to \"%s\"\n", *vp->ptr, value));
1739 		switch (*vp->ptr) {
1740 		case 'N':
1741 		case 'M':
1742 			newStr = modifier_M(vp, value, endc);
1743 			break;
1744 		case 'S':
1745 			newStr = modifier_S(vp, value, v);
1746 			break;
1747 		case 'C':
1748 			newStr = modifier_C(vp, value, v);
1749 			break;
1750 		case 't':
1751 			/* :tl :tu for OSF ODE & NetBSD make compatibility */
1752 			switch (vp->ptr[1]) {
1753 			case 'l':
1754 				vp->ptr++;
1755 				goto mod_lower;
1756 				break;
1757 			case 'u':
1758 				vp->ptr++;
1759 				goto mod_upper;
1760 				break;
1761 			}
1762 			/* FALLTHROUGH */
1763 		default:
1764 			if (vp->ptr[1] != endc && vp->ptr[1] != ':') {
1765 #ifdef SUNSHCMD
1766 				if ((vp->ptr[0] == 's') &&
1767 				    (vp->ptr[1] == 'h') &&
1768 				    (vp->ptr[2] == endc || vp->ptr[2] == ':')) {
1769 					const char	*error = NULL;
1770 
1771 					if (vp->execute) {
1772 						newStr = Buf_Peel(
1773 						    Cmd_Exec(value, &error));
1774 					} else {
1775 						newStr = estrdup("");
1776 					}
1777 
1778 					if (error)
1779 						Error(error, value);
1780 					vp->ptr += 2;
1781 				} else
1782 #endif
1783 				{
1784 					newStr = sysVvarsub(vp, startc, v, value);
1785 				}
1786 				break;
1787 			}
1788 
1789 			switch (vp->ptr[0]) {
1790 			case 'L':
1791 			mod_lower:
1792 				{
1793 				const char	*cp;
1794 				Buffer		*buf;
1795 				buf = Buf_Init(MAKE_BSIZE);
1796 				for (cp = value; *cp; cp++)
1797 					Buf_AddByte(buf, (Byte)tolower(*cp));
1798 
1799 				newStr = Buf_Peel(buf);
1800 
1801 				vp->ptr++;
1802 				break;
1803 				}
1804 			case 'O':
1805 				newStr = VarSortWords(value, SortIncreasing);
1806 				vp->ptr++;
1807 				break;
1808 			case 'Q':
1809 				newStr = Var_Quote(value);
1810 				vp->ptr++;
1811 				break;
1812 			case 'T':
1813 				newStr = VarModify(value, VarTail, NULL);
1814 				vp->ptr++;
1815 				break;
1816 			case 'U':
1817 			mod_upper:
1818 				{
1819 				const char	*cp;
1820 				Buffer		*buf;
1821 				buf = Buf_Init(MAKE_BSIZE);
1822 				for (cp = value; *cp; cp++)
1823 					Buf_AddByte(buf, (Byte)toupper(*cp));
1824 
1825 				newStr = Buf_Peel(buf);
1826 
1827 				vp->ptr++;
1828 				break;
1829 				}
1830 			case 'H':
1831 				newStr = VarModify(value, VarHead, NULL);
1832 				vp->ptr++;
1833 				break;
1834 			case 'E':
1835 				newStr = VarModify(value, VarSuffix, NULL);
1836 				vp->ptr++;
1837 				break;
1838 			case 'R':
1839 				newStr = VarModify(value, VarRoot, NULL);
1840 				vp->ptr++;
1841 				break;
1842 			case 'u':
1843 				newStr = VarUniq(value);
1844 				vp->ptr++;
1845 				break;
1846 			default:
1847 				newStr = sysVvarsub(vp, startc, v, value);
1848 				break;
1849 			}
1850 			break;
1851 		}
1852 
1853 		DEBUGF(VAR, ("Result is \"%s\"\n", newStr));
1854 		if (*freeResult) {
1855 			free(value);
1856 		}
1857 
1858 		value = newStr;
1859 		*freeResult = (value == var_Error) ? FALSE : TRUE;
1860 
1861 		if (vp->ptr[0] == ':') {
1862 			vp->ptr++;	/* consume colon */
1863 		}
1864 	}
1865 
1866 	return (value);
1867 }
1868 
1869 static char *
ParseRestModifier(VarParser * vp,char startc,Buffer * buf,Boolean * freeResult)1870 ParseRestModifier(VarParser *vp, char startc, Buffer *buf, Boolean *freeResult)
1871 {
1872 	const char	*vname;
1873 	size_t		vlen;
1874 	Var		*v;
1875 	char		*value;
1876 
1877 	vname = Buf_GetAll(buf, &vlen);
1878 
1879 	v = VarFindAny(vname, vp->ctxt);
1880 	if (v != NULL) {
1881 		value = ParseModifier(vp, startc, v, freeResult);
1882 		return (value);
1883 	}
1884 
1885 	if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) {
1886 		size_t  consumed;
1887 		/*
1888 		 * Still need to get to the end of the variable
1889 		 * specification, so kludge up a Var structure for the
1890 		 * modifications
1891 		 */
1892 		v = VarCreate(vname, NULL, VAR_JUNK);
1893 		value = ParseModifier(vp, startc, v, freeResult);
1894 		if (*freeResult) {
1895 			free(value);
1896 		}
1897 		VarDestroy(v, TRUE);
1898 
1899 		consumed = vp->ptr - vp->input + 1;
1900 		/*
1901 		 * If substituting a local variable in a non-local context,
1902 		 * assume it's for dynamic source stuff. We have to handle
1903 		 * this specially and return the longhand for the variable
1904 		 * with the dollar sign escaped so it makes it back to the
1905 		 * caller. Only four of the local variables are treated
1906 		 * specially as they are the only four that will be set when
1907 		 * dynamic sources are expanded.
1908 		 */
1909 		if (vlen == 1 ||
1910 		    (vlen == 2 && (vname[1] == 'F' || vname[1] == 'D'))) {
1911 			if (strchr("!%*@", vname[0]) != NULL) {
1912 				value = emalloc(consumed + 1);
1913 				strncpy(value, vp->input, consumed);
1914 				value[consumed] = '\0';
1915 
1916 				*freeResult = TRUE;
1917 				return (value);
1918 			}
1919 		}
1920 		if (vlen > 2 &&
1921 		    vname[0] == '.' &&
1922 		    isupper((unsigned char)vname[1])) {
1923 			if ((strncmp(vname, ".TARGET", vlen - 1) == 0) ||
1924 			    (strncmp(vname, ".ARCHIVE", vlen - 1) == 0) ||
1925 			    (strncmp(vname, ".PREFIX", vlen - 1) == 0) ||
1926 			    (strncmp(vname, ".MEMBER", vlen - 1) == 0)) {
1927 				value = emalloc(consumed + 1);
1928 				strncpy(value, vp->input, consumed);
1929 				value[consumed] = '\0';
1930 
1931 				*freeResult = TRUE;
1932 				return (value);
1933 			}
1934 		}
1935 
1936 		*freeResult = FALSE;
1937 		return (vp->err ? var_Error : varNoError);
1938 	} else {
1939 		/*
1940 		 * Check for D and F forms of local variables since we're in
1941 		 * a local context and the name is the right length.
1942 		 */
1943 		if (vlen == 2 &&
1944 		    (vname[1] == 'F' || vname[1] == 'D') &&
1945 		    (strchr("!%*<>@", vname[0]) != NULL)) {
1946 			char	name[2];
1947 
1948 			name[0] = vname[0];
1949 			name[1] = '\0';
1950 
1951 			v = VarFindOnly(name, vp->ctxt);
1952 			if (v != NULL) {
1953 				value = ParseModifier(vp, startc, v, freeResult);
1954 				return (value);
1955 			}
1956 		}
1957 
1958 		/*
1959 		 * Still need to get to the end of the variable
1960 		 * specification, so kludge up a Var structure for the
1961 		 * modifications
1962 		 */
1963 		v = VarCreate(vname, NULL, VAR_JUNK);
1964 		value = ParseModifier(vp, startc, v, freeResult);
1965 		if (*freeResult) {
1966 			free(value);
1967 		}
1968 		VarDestroy(v, TRUE);
1969 
1970 		*freeResult = FALSE;
1971 		return (vp->err ? var_Error : varNoError);
1972 	}
1973 }
1974 
1975 static char *
ParseRestEnd(VarParser * vp,Buffer * buf,Boolean * freeResult)1976 ParseRestEnd(VarParser *vp, Buffer *buf, Boolean *freeResult)
1977 {
1978 	const char	*vname;
1979 	size_t		vlen;
1980 	Var		*v;
1981 	char		*value;
1982 
1983 	vname = Buf_GetAll(buf, &vlen);
1984 
1985 	v = VarFindAny(vname, vp->ctxt);
1986 	if (v != NULL) {
1987 		value = VarExpand(v, vp);
1988 		*freeResult = TRUE;
1989 		return (value);
1990 	}
1991 
1992 	if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) {
1993 		size_t	consumed = vp->ptr - vp->input + 1;
1994 
1995 		/*
1996 		 * If substituting a local variable in a non-local context,
1997 		 * assume it's for dynamic source stuff. We have to handle
1998 		 * this specially and return the longhand for the variable
1999 		 * with the dollar sign escaped so it makes it back to the
2000 		 * caller. Only four of the local variables are treated
2001 		 * specially as they are the only four that will be set when
2002 		 * dynamic sources are expanded.
2003 		 */
2004 		if (vlen == 1 ||
2005 		    (vlen == 2 && (vname[1] == 'F' || vname[1] == 'D'))) {
2006 			if (strchr("!%*@", vname[0]) != NULL) {
2007 				value = emalloc(consumed + 1);
2008 				strncpy(value, vp->input, consumed);
2009 				value[consumed] = '\0';
2010 
2011 				*freeResult = TRUE;
2012 				return (value);
2013 			}
2014 		}
2015 		if (vlen > 2 &&
2016 		    vname[0] == '.' &&
2017 		    isupper((unsigned char)vname[1])) {
2018 			if ((strncmp(vname, ".TARGET", vlen - 1) == 0) ||
2019 			    (strncmp(vname, ".ARCHIVE", vlen - 1) == 0) ||
2020 			    (strncmp(vname, ".PREFIX", vlen - 1) == 0) ||
2021 			    (strncmp(vname, ".MEMBER", vlen - 1) == 0)) {
2022 				value = emalloc(consumed + 1);
2023 				strncpy(value, vp->input, consumed);
2024 				value[consumed] = '\0';
2025 
2026 				*freeResult = TRUE;
2027 				return (value);
2028 			}
2029 		}
2030 	} else {
2031 		/*
2032 		 * Check for D and F forms of local variables since we're in
2033 		 * a local context and the name is the right length.
2034 		 */
2035 		if (vlen == 2 &&
2036 		    (vname[1] == 'F' || vname[1] == 'D') &&
2037 		    (strchr("!%*<>@", vname[0]) != NULL)) {
2038 			char	name[2];
2039 
2040 			name[0] = vname[0];
2041 			name[1] = '\0';
2042 
2043 			v = VarFindOnly(name, vp->ctxt);
2044 			if (v != NULL) {
2045 				char	*val;
2046 				/*
2047 				 * No need for nested expansion or anything,
2048 				 * as we're the only one who sets these
2049 				 * things and we sure don't put nested
2050 				 * invocations in them...
2051 				 */
2052 				val = Buf_Data(v->val);
2053 
2054 				if (vname[1] == 'D') {
2055 					val = VarModify(val, VarHead, NULL);
2056 				} else {
2057 					val = VarModify(val, VarTail, NULL);
2058 				}
2059 
2060 				*freeResult = TRUE;
2061 				return (val);
2062 			}
2063 		}
2064 	}
2065 
2066 	*freeResult = FALSE;
2067 	return (vp->err ? var_Error : varNoError);
2068 }
2069 
2070 /**
2071  * Parse a multi letter variable name, and return it's value.
2072  */
2073 static char *
VarParseLong(VarParser * vp,Boolean * freeResult)2074 VarParseLong(VarParser *vp, Boolean *freeResult)
2075 {
2076 	Buffer		*buf;
2077 	char		startc;
2078 	char		endc;
2079 	char		*value;
2080 
2081 	buf = Buf_Init(MAKE_BSIZE);
2082 
2083 	startc = vp->ptr[0];
2084 	vp->ptr++;		/* consume opening paren or brace */
2085 
2086 	endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE;
2087 
2088 	/*
2089 	 * Process characters until we reach an end character or a colon,
2090 	 * replacing embedded variables as we go.
2091 	 */
2092 	while (*vp->ptr != '\0') {
2093 		if (*vp->ptr == endc) {
2094 			value = ParseRestEnd(vp, buf, freeResult);
2095 			vp->ptr++;	/* consume closing paren or brace */
2096 			Buf_Destroy(buf, TRUE);
2097 			return (value);
2098 
2099 		} else if (*vp->ptr == ':') {
2100 			value = ParseRestModifier(vp, startc, buf, freeResult);
2101 			vp->ptr++;	/* consume closing paren or brace */
2102 			Buf_Destroy(buf, TRUE);
2103 			return (value);
2104 
2105 		} else if (*vp->ptr == '$') {
2106 			VarParser	subvp = {
2107 				vp->ptr,
2108 				vp->ptr,
2109 				vp->ctxt,
2110 				vp->err,
2111 				vp->execute
2112 			};
2113 			char	*rval;
2114 			Boolean	rfree;
2115 
2116 			rval = VarParse(&subvp, &rfree);
2117 			if (rval == var_Error) {
2118 				Fatal("Error expanding embedded variable.");
2119 			}
2120 			Buf_Append(buf, rval);
2121 			if (rfree)
2122 				free(rval);
2123 			vp->ptr = subvp.ptr;
2124 		} else {
2125 			Buf_AddByte(buf, (Byte)*vp->ptr);
2126 			vp->ptr++;
2127 		}
2128 	}
2129 
2130 	/* If we did not find the end character, return var_Error */
2131 	Buf_Destroy(buf, TRUE);
2132 	*freeResult = FALSE;
2133 	return (var_Error);
2134 }
2135 
2136 /**
2137  * Parse a single letter variable name, and return it's value.
2138  */
2139 static char *
VarParseShort(VarParser * vp,Boolean * freeResult)2140 VarParseShort(VarParser *vp, Boolean *freeResult)
2141 {
2142 	char	vname[2];
2143 	Var	*v;
2144 	char	*value;
2145 
2146 	vname[0] = vp->ptr[0];
2147 	vname[1] = '\0';
2148 
2149 	vp->ptr++;	/* consume single letter */
2150 
2151 	v = VarFindAny(vname, vp->ctxt);
2152 	if (v != NULL) {
2153 		value = VarExpand(v, vp);
2154 		*freeResult = TRUE;
2155 		return (value);
2156 	}
2157 
2158 	/*
2159 	 * If substituting a local variable in a non-local context, assume
2160 	 * it's for dynamic source stuff. We have to handle this specially
2161 	 * and return the longhand for the variable with the dollar sign
2162 	 * escaped so it makes it back to the caller. Only four of the local
2163 	 * variables are treated specially as they are the only four that
2164 	 * will be set when dynamic sources are expanded.
2165 	 */
2166 	if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) {
2167 
2168 		/* XXX: It looks like $% and $! are reversed here */
2169 		switch (vname[0]) {
2170 		case '@':
2171 			*freeResult = TRUE;
2172 			return (estrdup("$(.TARGET)"));
2173 		case '%':
2174 			*freeResult = TRUE;
2175 			return (estrdup("$(.ARCHIVE)"));
2176 		case '*':
2177 			*freeResult = TRUE;
2178 			return (estrdup("$(.PREFIX)"));
2179 		case '!':
2180 			*freeResult = TRUE;
2181 			return (estrdup("$(.MEMBER)"));
2182 		default:
2183 			*freeResult = FALSE;
2184 			return (vp->err ? var_Error : varNoError);
2185 		}
2186 	}
2187 
2188 	/* Variable name was not found. */
2189 	*freeResult = FALSE;
2190 	return (vp->err ? var_Error : varNoError);
2191 }
2192 
2193 static char *
VarParse(VarParser * vp,Boolean * freeResult)2194 VarParse(VarParser *vp, Boolean *freeResult)
2195 {
2196 
2197 	vp->ptr++;	/* consume '$' or last letter of conditional */
2198 
2199 	if (vp->ptr[0] == '\0') {
2200 		/* Error, there is only a dollar sign in the input string. */
2201 		*freeResult = FALSE;
2202 		return (vp->err ? var_Error : varNoError);
2203 
2204 	} else if (vp->ptr[0] == OPEN_PAREN || vp->ptr[0] == OPEN_BRACE) {
2205 		/* multi letter variable name */
2206 		return (VarParseLong(vp, freeResult));
2207 
2208 	} else {
2209 		/* single letter variable name */
2210 		return (VarParseShort(vp, freeResult));
2211 	}
2212 }
2213 
2214 /**
2215  * Given the start of a variable invocation, extract the variable
2216  * name and find its value, then modify it according to the
2217  * specification.
2218  *
2219  * Results:
2220  *	The value of the variable or var_Error if the specification
2221  *	is invalid.  The number of characters in the specification
2222  *	is placed in the variable pointed to by consumed.  (for
2223  *	invalid specifications, this is just 2 to skip the '$' and
2224  *	the following letter, or 1 if '$' was the last character
2225  *	in the string).  A Boolean in *freeResult telling whether the
2226  *	returned string should be freed by the caller.
2227  */
2228 char *
Var_Parse(const char input[],GNode * ctxt,Boolean err,size_t * consumed,Boolean * freeResult)2229 Var_Parse(const char input[], GNode *ctxt, Boolean err,
2230 	size_t *consumed, Boolean *freeResult)
2231 {
2232 	VarParser	vp = {
2233 		input,
2234 		input,
2235 		ctxt,
2236 		err,
2237 		TRUE
2238 	};
2239 	char		*value;
2240 
2241 	value = VarParse(&vp, freeResult);
2242 	*consumed += vp.ptr - vp.input;
2243 	return (value);
2244 }
2245 
2246 /*
2247  * Given the start of a variable invocation, determine the length
2248  * of the specification.
2249  *
2250  * Results:
2251  *	The number of characters in the specification.  For invalid
2252  *	specifications, this is just 2 to skip the '$' and the
2253  *	following letter, or 1 if '$' was the last character in the
2254  *	string.
2255  */
2256 size_t
Var_Match(const char input[],GNode * ctxt)2257 Var_Match(const char input[], GNode *ctxt)
2258 {
2259 	VarParser	vp = {
2260 		input,
2261 		input,
2262 		ctxt,
2263 		FALSE,
2264 		FALSE
2265 	};
2266 	char		*value;
2267 	Boolean		freeResult;
2268 
2269 	value = VarParse(&vp, &freeResult);
2270 	if (freeResult) {
2271 		free(value);
2272 	}
2273 	return (vp.ptr - vp.input);
2274 }
2275 
2276 static int
match_var(const char str[],const char var[])2277 match_var(const char str[], const char var[])
2278 {
2279 	const char	*start = str;
2280 	size_t		len;
2281 
2282 	str++;			/* consume '$' */
2283 
2284 	if (str[0] == OPEN_PAREN || str[0] == OPEN_BRACE) {
2285 		str++;		/* consume opening paren or brace */
2286 
2287 		while (str[0] != '\0') {
2288 			if (str[0] == '$') {
2289 				/*
2290 				 * A variable inside the variable. We cannot
2291 				 * expand the external variable yet.
2292 				 */
2293 				return (str - start);
2294 			} else if (str[0] == ':' ||
2295 				   str[0] == CLOSE_PAREN ||
2296 				   str[0] == CLOSE_BRACE) {
2297 				len = str - (start + 2);
2298 
2299 				if (strncmp(var, start + 2, len) == 0 && var[len] == '\0') {
2300 					return (0);	/* match */
2301 				} else {
2302 					/*
2303 					 * Not the variable we want to
2304 					 * expand.
2305 					 */
2306 					return (str - start);
2307 				}
2308 			} else {
2309 				++str;
2310 			}
2311 		}
2312 		return (str - start);
2313 	} else {
2314 		/* Single letter variable name */
2315 		if (var[1] == '\0' && var[0] == str[0]) {
2316 			return (0);	/* match */
2317 		} else {
2318 			str++;	/* consume variable name */
2319 			return (str - start);
2320 		}
2321 	}
2322 }
2323 
2324 /**
2325  * Substitute for all variables in the given string in the given
2326  * context If err is TRUE, Parse_Error will be called when an
2327  * undefined variable is encountered.
2328  *
2329  * Results:
2330  *	The resulting string.
2331  *
2332  * Side Effects:
2333  *	None. The old string must be freed by the caller
2334  */
2335 Buffer *
Var_Subst(const char * str,GNode * ctxt,Boolean err)2336 Var_Subst(const char *str, GNode *ctxt, Boolean err)
2337 {
2338 	Boolean	errorReported;
2339 	Buffer *buf;		/* Buffer for forming things */
2340 
2341 	/*
2342 	 * Set TRUE if an error has already been reported to prevent a
2343 	 * plethora of messages when recursing. XXXHB this comment sounds
2344 	 * wrong.
2345 	 */
2346 	errorReported = FALSE;
2347 
2348 	buf = Buf_Init(0);
2349 	while (str[0] != '\0') {
2350 		if ((str[0] == '$') && (str[1] == '$')) {
2351 			/*
2352 			 * A dollar sign may be escaped with another dollar
2353 			 * sign.  In such a case, we skip over the escape
2354 			 * character and store the dollar sign into the
2355 			 * buffer directly.
2356 			 */
2357 			str++;
2358 			Buf_AddByte(buf, (Byte)str[0]);
2359 			str++;
2360 
2361 		} else if (str[0] == '$') {
2362 			/* Variable invocation. */
2363 			VarParser subvp = {
2364 				str,
2365 				str,
2366 				ctxt,
2367 				err,
2368 				TRUE
2369 			};
2370 			char	*rval;
2371 			Boolean	rfree;
2372 
2373 			rval = VarParse(&subvp, &rfree);
2374 
2375 			/*
2376 			 * When we come down here, val should either point to
2377 			 * the value of this variable, suitably modified, or
2378 			 * be NULL. Length should be the total length of the
2379 			 * potential variable invocation (from $ to end
2380 			 * character...)
2381 			 */
2382 			if (rval == var_Error || rval == varNoError) {
2383 				/*
2384 				 * If performing old-time variable
2385 				 * substitution, skip over the variable and
2386 				 * continue with the substitution. Otherwise,
2387 				 * store the dollar sign and advance str so
2388 				 * we continue with the string...
2389 				 */
2390 				if (oldVars) {
2391 					str = subvp.ptr;
2392 				} else if (err) {
2393 					/*
2394 					 * If variable is undefined, complain
2395 					 * and skip the variable. The
2396 					 * complaint will stop us from doing
2397 					 * anything when the file is parsed.
2398 					 */
2399 					if (!errorReported) {
2400 						Parse_Error(PARSE_FATAL,
2401 							    "Undefined variable \"%.*s\"", subvp.ptr - subvp.input, str);
2402 					}
2403 					errorReported = TRUE;
2404 					str = subvp.ptr;
2405 				} else {
2406 					Buf_AddByte(buf, (Byte)str[0]);
2407 					str++;
2408 				}
2409 			} else {
2410 				/*
2411 				 * Copy all the characters from the variable
2412 				 * value straight into the new string.
2413 				 */
2414 				Buf_Append(buf, rval);
2415 				if (rfree) {
2416 					free(rval);
2417 				}
2418 				str = subvp.ptr;
2419 			}
2420 		} else {
2421 			Buf_AddByte(buf, (Byte)str[0]);
2422 			str++;
2423 		}
2424 	}
2425 
2426 	return (buf);
2427 }
2428 
2429 /**
2430  * Substitute for all variables except if it is the same as 'var',
2431  * in the given string in the given context.  If err is TRUE,
2432  * Parse_Error will be called when an undefined variable is
2433  * encountered.
2434  *
2435  * Results:
2436  *	The resulting string.
2437  *
2438  * Side Effects:
2439  *	None. The old string must be freed by the caller
2440  */
2441 Buffer *
Var_SubstOnly(const char * var,const char * str,Boolean err)2442 Var_SubstOnly(const char *var, const char *str, Boolean err)
2443 {
2444 	GNode *ctxt = VAR_GLOBAL;
2445 	Boolean	errorReported;
2446 	Buffer	*buf;		/* Buffer for forming things */
2447 
2448 	/*
2449 	 * Set TRUE if an error has already been reported to prevent a
2450 	 * plethora of messages when recursing. XXXHB this comment sounds
2451 	 * wrong.
2452 	 */
2453 	errorReported = FALSE;
2454 
2455 	buf = Buf_Init(0);
2456 	while (str[0] != '\0') {
2457 		if (str[0] == '$') {
2458 			int	skip;
2459 
2460 			skip = match_var(str, var);
2461 			if (skip > 0) {
2462 				Buf_AddBytes(buf, skip, str);
2463 				str += skip;
2464 			} else {
2465 				/* Variable invocation. */
2466 				VarParser	subvp = {
2467 					str,
2468 					str,
2469 					ctxt,
2470 					err,
2471 					TRUE
2472 				};
2473 				char	*rval;
2474 				Boolean	rfree;
2475 
2476 				rval = VarParse(&subvp, &rfree);
2477 
2478 				/*
2479 				 * When we get down here, rval should either
2480 				 * point to the value of this variable, or be
2481 				 * NULL.
2482 				 */
2483 				if (rval == var_Error || rval == varNoError) {
2484 					/*
2485 					 * If performing old-time variable
2486 					 * substitution, skip over the
2487 					 * variable and continue with the
2488 					 * substitution. Otherwise, store the
2489 					 * dollar sign and advance str so we
2490 					 * continue with the string...
2491 					 */
2492 					if (oldVars) {
2493 						str = subvp.ptr;
2494 					} else if (err) {
2495 						/*
2496 						 * If variable is undefined,
2497 						 * complain and skip the
2498 						 * variable. The complaint
2499 						 * will stop us from doing
2500 						 * anything when the file is
2501 						 * parsed.
2502 						 */
2503 						if (!errorReported) {
2504 							Parse_Error(PARSE_FATAL,
2505 								    "Undefined variable \"%.*s\"", subvp.ptr - subvp.input, str);
2506 						}
2507 						errorReported = TRUE;
2508 						str = subvp.ptr;
2509 					} else {
2510 						Buf_AddByte(buf, (Byte)str[0]);
2511 						str++;
2512 					}
2513 				} else {
2514 					/*
2515 					 * Copy all the characters from the
2516 					 * variable value straight into the
2517 					 * new string.
2518 					 */
2519 					Buf_Append(buf, rval);
2520 					if (rfree) {
2521 						free(rval);
2522 					}
2523 					str = subvp.ptr;
2524 				}
2525 			}
2526 		} else {
2527 			Buf_AddByte(buf, (Byte)str[0]);
2528 			str++;
2529 		}
2530 	}
2531 
2532 	return (buf);
2533 }
2534 
2535 /**
2536  * Initialize the module
2537  *
2538  * Side Effects:
2539  *	The VAR_CMD and VAR_GLOBAL contexts are created
2540  */
2541 void
Var_Init(char ** env)2542 Var_Init(char **env)
2543 {
2544 	char	**ptr;
2545 
2546 	VAR_CMD = Targ_NewGN("Command");
2547 	VAR_ENV = Targ_NewGN("Environment");
2548 	VAR_GLOBAL = Targ_NewGN("Global");
2549 
2550 	/*
2551 	 * Copy user environment variables into ENV context.
2552 	 */
2553 	for (ptr = env; *ptr != NULL; ++ptr) {
2554 		char		*tmp = estrdup(*ptr);
2555 		const char	*name = tmp;
2556 		char		*sep = strchr(name, '=');
2557 		const char	*value = sep + 1;
2558 
2559 		if (sep != NULL) {
2560 			*sep = '\0';
2561 			VarAdd(name, value, VAR_ENV);
2562 		}
2563 		free(tmp);
2564 	}
2565 }
2566 
2567 /**
2568  * Print all variables in global and command line contexts.
2569  */
2570 void
Var_Dump(void)2571 Var_Dump(void)
2572 {
2573 	const LstNode	*ln;
2574 	const Var	*v;
2575 
2576 	printf("#*** Global Variables:\n");
2577 	LST_FOREACH(ln, &VAR_GLOBAL->context) {
2578 		v = Lst_Datum(ln);
2579 		printf("%-16s = %s\n", v->name, Buf_Data(v->val));
2580 	}
2581 
2582 	printf("#*** Command-line Variables:\n");
2583 	LST_FOREACH(ln, &VAR_CMD->context) {
2584 		v = Lst_Datum(ln);
2585 		printf("%-16s = %s\n", v->name, Buf_Data(v->val));
2586 	}
2587 }
2588 
2589 /**
2590  * Print the values of any variables requested by
2591  * the user.
2592  */
2593 void
Var_Print(Lst * vlist,Boolean expandVars)2594 Var_Print(Lst *vlist, Boolean expandVars)
2595 {
2596 	LstNode		*n;
2597 	char		*name;
2598 
2599 	LST_FOREACH(n, vlist) {
2600 		name = Lst_Datum(n);
2601 		if (expandVars) {
2602 			char *value;
2603 			char *v;
2604 
2605 			if (*name == '$') {
2606 				v = name;
2607 			} else {
2608 				v = emalloc(strlen(name) + 1 + 3);
2609 				sprintf(v, "${%s}", name);
2610 			}
2611 			value = Buf_Peel(Var_Subst(v, VAR_GLOBAL, FALSE));
2612 			printf("%s\n", value);
2613 
2614 			if (v != name)
2615 				free(v);
2616 			free(value);
2617 		} else {
2618 			const char *value = Var_Value(name, VAR_GLOBAL);
2619 			printf("%s\n", value != NULL ? value : "");
2620 		}
2621 	}
2622 }
2623 
2624