xref: /openbsd-src/usr.bin/make/varmodifiers.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenPackages$ */
2 /*	$OpenBSD: varmodifiers.c,v 1.25 2007/11/03 15:42:11 deraadt Exp $	*/
3 /*	$NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $	*/
4 
5 /*
6  * Copyright (c) 1999 Marc Espie.
7  *
8  * Extensive code changes for the OpenBSD project.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
23  * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*
32  * Copyright (c) 1988, 1989, 1990, 1993
33  *	The Regents of the University of California.  All rights reserved.
34  * Copyright (c) 1989 by Berkeley Softworks
35  * All rights reserved.
36  *
37  * This code is derived from software contributed to Berkeley by
38  * Adam de Boor.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  * 3. Neither the name of the University nor the names of its contributors
49  *    may be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  */
64 
65 /* VarModifiers_Apply is mostly a constituent function of Var_Parse, it
66  * is also called directly by Var_SubstVar.  */
67 
68 
69 #include <ctype.h>
70 #include <sys/types.h>
71 #ifndef MAKE_BOOTSTRAP
72 #include <regex.h>
73 #endif
74 #include <stddef.h>
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include "config.h"
79 #include "defines.h"
80 #include "buf.h"
81 #include "var.h"
82 #include "varmodifiers.h"
83 #include "varname.h"
84 #include "targ.h"
85 #include "error.h"
86 #include "str.h"
87 #include "cmd_exec.h"
88 #include "memory.h"
89 #include "gnode.h"
90 
91 
92 /* Var*Pattern flags */
93 #define VAR_SUB_GLOBAL	0x01	/* Apply substitution globally */
94 #define VAR_SUB_ONE	0x02	/* Apply substitution to one word */
95 #define VAR_SUB_MATCHED 0x04	/* There was a match */
96 #define VAR_MATCH_START 0x08	/* Match at start of word */
97 #define VAR_MATCH_END	0x10	/* Match at end of word */
98 
99 /* Modifiers flags */
100 #define VAR_EQUAL	0x20
101 #define VAR_MAY_EQUAL	0x40
102 #define VAR_ADD_EQUAL	0x80
103 #define VAR_BANG_EQUAL	0x100
104 
105 typedef struct {
106 	char	  *lbuffer; /* left string to free */
107 	char	  *lhs;     /* String to match */
108 	size_t	  leftLen;  /* Length of string */
109 	char	  *rhs;     /* Replacement string (w/ &'s removed) */
110 	size_t	  rightLen; /* Length of replacement */
111 	int 	  flags;
112 } VarPattern;
113 
114 struct LoopStuff {
115 	struct LoopVar	*var;
116 	char	*expand;
117 	bool	err;
118 };
119 
120 static bool VarHead(struct Name *, bool, Buffer, void *);
121 static bool VarTail(struct Name *, bool, Buffer, void *);
122 static bool VarSuffix(struct Name *, bool, Buffer, void *);
123 static bool VarRoot(struct Name *, bool, Buffer, void *);
124 static bool VarMatch(struct Name *, bool, Buffer, void *);
125 static bool VarSYSVMatch(struct Name *, bool, Buffer, void *);
126 static bool VarNoMatch(struct Name *, bool, Buffer, void *);
127 static bool VarUniq(struct Name *, bool, Buffer, void *);
128 static bool VarLoop(struct Name *, bool, Buffer, void *);
129 
130 
131 #ifndef MAKE_BOOTSTRAP
132 static void VarREError(int, regex_t *, const char *);
133 static bool VarRESubstitute(struct Name *, bool, Buffer, void *);
134 static char *do_regex(const char *, const struct Name *, void *);
135 
136 typedef struct {
137 	regex_t	  re;
138 	int 	  nsub;
139 	regmatch_t	 *matches;
140 	char	 *replace;
141 	int 	  flags;
142 } VarREPattern;
143 #endif
144 
145 static bool VarSubstitute(struct Name *, bool, Buffer, void *);
146 static char *VarGetPattern(SymTable *, int, const char **, int, int,
147     size_t *, VarPattern *);
148 static char *VarQuote(const char *, const struct Name *, void *);
149 static char *VarModify(char *, bool (*)(struct Name *, bool, Buffer, void *), void *);
150 
151 static void *check_empty(const char **, SymTable *, bool, int);
152 static char *do_upper(const char *, const struct Name *, void *);
153 static char *do_lower(const char *, const struct Name *, void *);
154 static void *check_shcmd(const char **, SymTable *, bool, int);
155 static char *do_shcmd(const char *, const struct Name *, void *);
156 static char *do_sort(const char *, const struct Name *, void *);
157 static char *finish_loop(const char *, const struct Name *, void *);
158 static int NameCompare(const void *, const void *);
159 static char *do_label(const char *, const struct Name *, void *);
160 static char *do_path(const char *, const struct Name *, void *);
161 static char *do_def(const char *, const struct Name *, void *);
162 static char *do_undef(const char *, const struct Name *, void *);
163 static char *do_assign(const char *, const struct Name *, void *);
164 static char *do_exec(const char *, const struct Name *, void *);
165 
166 static void *assign_get_value(const char **, SymTable *, bool, int);
167 static void *get_cmd(const char **, SymTable *, bool, int);
168 static void *get_value(const char **, SymTable *, bool, int);
169 static void *get_stringarg(const char **, SymTable *, bool, int);
170 static void free_stringarg(void *);
171 static void *get_patternarg(const char **, SymTable *, bool, int);
172 static void *get_spatternarg(const char **, SymTable *, bool, int);
173 static void *common_get_patternarg(const char **, SymTable *, bool, int, bool);
174 static void free_patternarg(void *);
175 static void free_looparg(void *);
176 static void *get_sysvpattern(const char **, SymTable *, bool, int);
177 static void *get_loop(const char **, SymTable *, bool, int);
178 static char *LoopGrab(const char **);
179 
180 static struct Name dummy;
181 static struct Name *dummy_arg = &dummy;
182 
183 static struct modifier {
184 	    bool atstart;
185 	    void * (*getarg)(const char **, SymTable *, bool, int);
186 	    char * (*apply)(const char *, const struct Name *, void *);
187 	    bool (*word_apply)(struct Name *, bool, Buffer, void *);
188 	    void   (*freearg)(void *);
189 } *choose_mod[256],
190 	match_mod = {false, get_stringarg, NULL, VarMatch, free_stringarg},
191 	nomatch_mod = {false, get_stringarg, NULL, VarNoMatch, free_stringarg},
192 	subst_mod = {false, get_spatternarg, NULL, VarSubstitute, free_patternarg},
193 #ifndef MAKE_BOOTSTRAP
194 	resubst_mod = {false, get_patternarg, do_regex, NULL, free_patternarg},
195 #endif
196 	quote_mod = {false, check_empty, VarQuote, NULL , NULL},
197 	tail_mod = {false, check_empty, NULL, VarTail, NULL},
198 	head_mod = {false, check_empty, NULL, VarHead, NULL},
199 	suffix_mod = {false, check_empty, NULL, VarSuffix, NULL},
200 	root_mod = {false, check_empty, NULL, VarRoot, NULL},
201 	upper_mod = {false, check_empty, do_upper, NULL, NULL},
202 	lower_mod = {false, check_empty, do_lower, NULL, NULL},
203 	shcmd_mod = {false, check_shcmd, do_shcmd, NULL, NULL},
204 	sysv_mod = {false, get_sysvpattern, NULL, VarSYSVMatch, free_patternarg},
205 	uniq_mod = {false, check_empty, NULL, VarUniq, NULL},
206 	sort_mod = {false, check_empty, do_sort, NULL, NULL},
207 	loop_mod = {false, get_loop, finish_loop, VarLoop, free_looparg},
208 	undef_mod = {true, get_value, do_undef, NULL, NULL},
209 	def_mod = {true, get_value, do_def, NULL, NULL},
210 	label_mod = {true, check_empty, do_label, NULL, NULL},
211 	path_mod = {true, check_empty, do_path, NULL, NULL},
212 	assign_mod = {true, assign_get_value, do_assign, NULL, free_patternarg},
213 	exec_mod = {true, get_cmd, do_exec, NULL, free_patternarg}
214 ;
215 
216 void
217 VarModifiers_Init()
218 {
219 	choose_mod['M'] = &match_mod;
220 	choose_mod['N'] = &nomatch_mod;
221 	choose_mod['S'] = &subst_mod;
222 #ifndef MAKE_BOOTSTRAP
223 	choose_mod['C'] = &resubst_mod;
224 #endif
225 	choose_mod['Q'] = &quote_mod;
226 	choose_mod['T'] = &tail_mod;
227 	choose_mod['H'] = &head_mod;
228 	choose_mod['E'] = &suffix_mod;
229 	choose_mod['R'] = &root_mod;
230 	if (FEATURES(FEATURE_UPPERLOWER)) {
231 		choose_mod['U'] = &upper_mod;
232 		choose_mod['L'] = &lower_mod;
233 	}
234 	if (FEATURES(FEATURE_SUNSHCMD))
235 		choose_mod['s'] = &shcmd_mod;
236 	if (FEATURES(FEATURE_UNIQ))
237 		choose_mod['u'] = &uniq_mod;
238 	if (FEATURES(FEATURE_SORT))
239 		choose_mod['O'] = &sort_mod;
240 	if (FEATURES(FEATURE_ODE)) {
241 		choose_mod['@'] = &loop_mod;
242 		choose_mod['D'] = &def_mod;
243 		choose_mod['U'] = &undef_mod;
244 		choose_mod['L'] = &label_mod;
245 		choose_mod['P'] = &path_mod;
246 	}
247 	if (FEATURES(FEATURE_ASSIGN))
248 		choose_mod[':'] = &assign_mod;
249 	if (FEATURES(FEATURE_EXECMOD))
250 		choose_mod['!'] = &exec_mod;
251 }
252 
253 /* All modifiers handle addSpace (need to add a space before placing the
254  * next word into the buffer) and propagate it when necessary.
255  */
256 
257 /*-
258  *-----------------------------------------------------------------------
259  * VarHead --
260  *	Remove the tail of the given word and add the result to the given
261  *	buffer.
262  *-----------------------------------------------------------------------
263  */
264 static bool
265 VarHead(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
266 {
267 	const char	*slash;
268 
269 	slash = Str_rchri(word->s, word->e, '/');
270 	if (slash != NULL) {
271 		if (addSpace)
272 			Buf_AddSpace(buf);
273 		Buf_Addi(buf, word->s, slash);
274 	} else {
275 		/* If no directory part, give . (q.v. the POSIX standard).  */
276 		if (addSpace)
277 			Buf_AddString(buf, " .");
278 		else
279 			Buf_AddChar(buf, '.');
280 	}
281 	return true;
282 }
283 
284 /*-
285  *-----------------------------------------------------------------------
286  * VarTail --
287  *	Remove the head of the given word add the result to the given
288  *	buffer.
289  *-----------------------------------------------------------------------
290  */
291 static bool
292 VarTail(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
293 {
294 	const char	*slash;
295 
296 	if (addSpace)
297 		Buf_AddSpace(buf);
298 	slash = Str_rchri(word->s, word->e, '/');
299 	if (slash != NULL)
300 		Buf_Addi(buf, slash+1, word->e);
301 	else
302 		Buf_Addi(buf, word->s, word->e);
303 	return true;
304 }
305 
306 /*-
307  *-----------------------------------------------------------------------
308  * VarSuffix --
309  *	Add the suffix of the given word to the given buffer.
310  *-----------------------------------------------------------------------
311  */
312 static bool
313 VarSuffix(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
314 {
315 	const char	*dot;
316 
317 	dot = Str_rchri(word->s, word->e, '.');
318 	if (dot != NULL) {
319 		if (addSpace)
320 			Buf_AddSpace(buf);
321 		Buf_Addi(buf, dot+1, word->e);
322 		addSpace = true;
323 	}
324 	return addSpace;
325 }
326 
327 /*-
328  *-----------------------------------------------------------------------
329  * VarRoot --
330  *	Remove the suffix of the given word and add the result to the
331  *	buffer.
332  *-----------------------------------------------------------------------
333  */
334 static bool
335 VarRoot(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
336 {
337 	const char	*dot;
338 
339 	if (addSpace)
340 		Buf_AddSpace(buf);
341 	dot = Str_rchri(word->s, word->e, '.');
342 	if (dot != NULL)
343 		Buf_Addi(buf, word->s, dot);
344 	else
345 		Buf_Addi(buf, word->s, word->e);
346 	return true;
347 }
348 
349 /*-
350  *-----------------------------------------------------------------------
351  * VarMatch --
352  *	Add the word to the buffer if it matches the given pattern.
353  *-----------------------------------------------------------------------
354  */
355 static bool
356 VarMatch(struct Name *word, bool addSpace, Buffer buf,
357     void *pattern) /* Pattern the word must match */
358 {
359 	const char *pat = (const char *)pattern;
360 
361 	if (Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {
362 		if (addSpace)
363 			Buf_AddSpace(buf);
364 		Buf_Addi(buf, word->s, word->e);
365 		return true;
366 	} else
367 		return addSpace;
368 }
369 
370 /*-
371  *-----------------------------------------------------------------------
372  * VarNoMatch --
373  *	Add the word to the buffer if it doesn't match the given pattern.
374  *-----------------------------------------------------------------------
375  */
376 static bool
377 VarNoMatch(struct Name *word, bool addSpace, Buffer buf,
378     void *pattern) /* Pattern the word must not match */
379 {
380 	const char *pat = (const char *)pattern;
381 
382 	if (!Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {
383 		if (addSpace)
384 			Buf_AddSpace(buf);
385 		Buf_Addi(buf, word->s, word->e);
386 		return true;
387 	} else
388 		return addSpace;
389 }
390 
391 static bool
392 VarUniq(struct Name *word, bool addSpace, Buffer buf, void *lastp)
393 {
394 	struct Name *last = (struct Name *)lastp;
395 
396 	/* does not match */
397 	if (last->s == NULL || last->e - last->s != word->e - word->s ||
398 	    strncmp(word->s, last->s, word->e - word->s) != 0) {
399 		if (addSpace)
400 			Buf_AddSpace(buf);
401 		Buf_Addi(buf, word->s, word->e);
402 		addSpace = true;
403 	}
404 	last->s = word->s;
405 	last->e = word->e;
406 	return addSpace;
407 }
408 
409 static bool
410 VarLoop(struct Name *word, bool addSpace, Buffer buf, void *vp)
411 {
412 	struct LoopStuff *v = (struct LoopStuff *)vp;
413 
414 	if (addSpace)
415 		Buf_AddSpace(buf);
416 	Var_SubstVar(buf, v->expand, v->var, word->s);
417 	return true;
418 }
419 
420 static char *
421 finish_loop(const char *s, const struct Name *n UNUSED , void *p)
422 {
423 	struct LoopStuff *l = (struct LoopStuff *)p;
424 
425 	return Var_Subst(s, NULL,  l->err);
426 }
427 
428 static int
429 NameCompare(const void *ap, const void *bp)
430 {
431 	struct Name *a, *b;
432 	size_t n, m;
433 	int c;
434 
435 	a = (struct Name *)ap;
436 	b = (struct Name *)bp;
437 	n = a->e - a->s;
438 	m = b->e - b->s;
439 	if (n < m) {
440 		c = strncmp(a->s, b->s, n);
441 		if (c != 0)
442 		    return c;
443 		else
444 		    return -1;
445     	} else if (m < n) {
446 		c = strncmp(a->s, b->s, m);
447 		if (c != 0)
448 		    return c;
449 		else
450 		    return 1;
451     	} else
452 	    return strncmp(a->s, b->s, n);
453 }
454 
455 static char *
456 do_sort(const char *s, const struct Name *dummy UNUSED, void *arg UNUSED)
457 {
458 	struct Name *t;
459 	unsigned long n, i, j;
460 	const char *start, *end;
461 
462 	n = 1024;	/* start at 1024 words */
463 	t = (struct Name *)emalloc(sizeof(struct Name) * n);
464 	start = s;
465 	end = start;
466 
467 	for (i = 0;; i++) {
468 		if (i == n) {
469 			n *= 2;
470 			t = (struct Name *)erealloc(t, sizeof(struct Name) * n);
471 		}
472 		start = iterate_words(&end);
473 		if (start == NULL)
474 			break;
475 		t[i].s = start;
476 		t[i].e = end;
477 	}
478 	if (i > 0) {
479 		BUFFER buf;
480 
481 		Buf_Init(&buf, end - s);
482 		qsort(t, i, sizeof(struct Name), NameCompare);
483 		Buf_Addi(&buf, t[0].s, t[0].e);
484 		for (j = 1; j < i; j++) {
485 			Buf_AddSpace(&buf);
486 			Buf_Addi(&buf, t[j].s, t[j].e);
487 		}
488 		free(t);
489 		return Buf_Retrieve(&buf);
490 	} else {
491 		free(t);
492 		return "";
493 	}
494 }
495 
496 static char *
497 do_label(const char *s UNUSED, const struct Name *n, void *arg UNUSED)
498 {
499 	return Str_dupi(n->s, n->e);
500 }
501 
502 static char *
503 do_path(const char *s UNUSED, const struct Name *n, void *arg UNUSED)
504 {
505 	GNode *gn;
506 
507 	gn = Targ_FindNodei(n->s, n->e, TARG_NOCREATE);
508 	if (gn == NULL)
509 		return Str_dupi(n->s, n->e);
510 	else
511 		return strdup(gn->path);
512 }
513 
514 static char *
515 do_def(const char *s, const struct Name *n UNUSED, void *arg)
516 {
517 	VarPattern *v = (VarPattern *)arg;
518 	if (s == NULL) {
519 		free_patternarg(v);
520 		return NULL;
521 	} else
522 		return v->lbuffer;
523 }
524 
525 static char *
526 do_undef(const char *s, const struct Name *n UNUSED, void *arg)
527 {
528 	VarPattern *v = (VarPattern *)arg;
529 	if (s != NULL) {
530 		free_patternarg(v);
531 		return NULL;
532 	} else
533 		return v->lbuffer;
534 }
535 
536 static char *
537 do_assign(const char *s, const struct Name *n, void *arg)
538 {
539 	VarPattern *v = (VarPattern *)arg;
540 	char *msg;
541 	char *result;
542 
543 	switch (v->flags) {
544 	case VAR_EQUAL:
545 		Var_Seti(n->s, n->e, v->lbuffer);
546 		break;
547 	case VAR_MAY_EQUAL:
548 		if (s == NULL)
549 			Var_Seti(n->s, n->e, v->lbuffer);
550 		break;
551 	case VAR_ADD_EQUAL:
552 		if (s == NULL)
553 			Var_Seti(n->s, n->e, v->lbuffer);
554 		else
555 			Var_Appendi(n->s, n->e, v->lbuffer);
556 		break;
557 	case VAR_BANG_EQUAL:
558 		result = Cmd_Exec(v->lbuffer, &msg);
559 		if (result != NULL) {
560 			Var_Seti(n->s, n->e, result);
561 			free(result);
562 		} else
563 			Error(msg, v->lbuffer);
564 		break;
565 
566 	}
567 	return NULL;
568 }
569 
570 static char *
571 do_exec(const char *s UNUSED, const struct Name *n UNUSED, void *arg)
572 {
573 	VarPattern *v = (VarPattern *)arg;
574 	char *msg;
575 	char *result;
576 
577 	result = Cmd_Exec(v->lbuffer, &msg);
578 	if (result == NULL)
579 		Error(msg, v->lbuffer);
580 	return result;
581 }
582 
583 /*-
584  *-----------------------------------------------------------------------
585  * VarSYSVMatch --
586  *	Add the word to the buffer if it matches the given pattern.
587  *	Used to implement the System V % modifiers.
588  *-----------------------------------------------------------------------
589  */
590 static bool
591 VarSYSVMatch(struct Name *word, bool addSpace, Buffer buf, void *patp)
592 {
593 	size_t	len;
594 	const char	*ptr;
595 	VarPattern	*pat = (VarPattern *)patp;
596 
597 	if (*word->s != '\0') {
598 		if (addSpace)
599 			Buf_AddSpace(buf);
600 		if ((ptr = Str_SYSVMatch(word->s, pat->lhs, &len)) != NULL)
601 			Str_SYSVSubst(buf, pat->rhs, ptr, len);
602 		else
603 			Buf_Addi(buf, word->s, word->e);
604 		return true;
605 	} else
606 		return addSpace;
607 }
608 
609 void *
610 get_sysvpattern(const char **p, SymTable *ctxt UNUSED, bool err UNUSED,
611     int endc)
612 {
613 	VarPattern		*pattern;
614 	const char		*cp, *cp2;
615 	int cnt = 0;
616 	char startc = endc == ')' ? '(' : '{';
617 
618 	for (cp = *p;; cp++) {
619 		if (*cp == '=' && cnt == 0)
620 			break;
621 		if (*cp == '\0')
622 			return NULL;
623 		if (*cp == startc)
624 			cnt++;
625 		else if (*cp == endc) {
626 			cnt--;
627 			if (cnt < 0)
628 				return NULL;
629 		}
630 	}
631 	for (cp2 = cp+1;; cp2++) {
632 		if ((*cp2 == ':' || *cp2 == endc) && cnt == 0)
633 			break;
634 		if (*cp2 == '\0')
635 			return NULL;
636 		if (*cp2 == startc)
637 			cnt++;
638 		else if (*cp2 == endc) {
639 			cnt--;
640 			if (cnt < 0)
641 				return NULL;
642 		}
643 	}
644 
645 	pattern = (VarPattern *)emalloc(sizeof(VarPattern));
646 	pattern->lbuffer = pattern->lhs = Str_dupi(*p, cp);
647 	pattern->leftLen = cp - *p;
648 	pattern->rhs = Str_dupi(cp+1, cp2);
649 	pattern->rightLen = cp2 - (cp+1);
650 	pattern->flags = 0;
651 	*p = cp2;
652 	return pattern;
653 }
654 
655 
656 /*-
657  *-----------------------------------------------------------------------
658  * VarSubstitute --
659  *	Perform a string-substitution on the given word, Adding the
660  *	result to the given buffer.
661  *-----------------------------------------------------------------------
662  */
663 static bool
664 VarSubstitute(struct Name *word, bool addSpace, Buffer buf,
665     void *patternp) /* Pattern for substitution */
666 {
667     size_t	wordLen;    /* Length of word */
668     const char	*cp;	    /* General pointer */
669     VarPattern	*pattern = (VarPattern *)patternp;
670 
671     wordLen = word->e - word->s;
672     if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) !=
673 	(VAR_SUB_ONE|VAR_SUB_MATCHED)) {
674 	/* Still substituting -- break it down into simple anchored cases
675 	 * and if none of them fits, perform the general substitution case.  */
676 	if ((pattern->flags & VAR_MATCH_START) &&
677 	    (strncmp(word->s, pattern->lhs, pattern->leftLen) == 0)) {
678 		/* Anchored at start and beginning of word matches pattern.  */
679 		if ((pattern->flags & VAR_MATCH_END) &&
680 		    (wordLen == pattern->leftLen)) {
681 			/* Also anchored at end and matches to the end (word
682 			 * is same length as pattern) add space and rhs only
683 			 * if rhs is non-null.	*/
684 			if (pattern->rightLen != 0) {
685 			    if (addSpace)
686 				Buf_AddSpace(buf);
687 			    addSpace = true;
688 			    Buf_AddChars(buf, pattern->rightLen,
689 					 pattern->rhs);
690 			}
691 			pattern->flags |= VAR_SUB_MATCHED;
692 		} else if (pattern->flags & VAR_MATCH_END) {
693 		    /* Doesn't match to end -- copy word wholesale.  */
694 		    goto nosub;
695 		} else {
696 		    /* Matches at start but need to copy in
697 		     * trailing characters.  */
698 		    if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
699 			if (addSpace)
700 			    Buf_AddSpace(buf);
701 			addSpace = true;
702 		    }
703 		    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
704 		    Buf_AddChars(buf, wordLen - pattern->leftLen,
705 				 word->s + pattern->leftLen);
706 		    pattern->flags |= VAR_SUB_MATCHED;
707 		}
708 	} else if (pattern->flags & VAR_MATCH_START) {
709 	    /* Had to match at start of word and didn't -- copy whole word.  */
710 	    goto nosub;
711 	} else if (pattern->flags & VAR_MATCH_END) {
712 	    /* Anchored at end, Find only place match could occur (leftLen
713 	     * characters from the end of the word) and see if it does. Note
714 	     * that because the $ will be left at the end of the lhs, we have
715 	     * to use strncmp.	*/
716 	    cp = word->s + (wordLen - pattern->leftLen);
717 	    if (cp >= word->s &&
718 		strncmp(cp, pattern->lhs, pattern->leftLen) == 0) {
719 		/* Match found. If we will place characters in the buffer,
720 		 * add a space before hand as indicated by addSpace, then
721 		 * stuff in the initial, unmatched part of the word followed
722 		 * by the right-hand-side.  */
723 		if (((cp - word->s) + pattern->rightLen) != 0) {
724 		    if (addSpace)
725 			Buf_AddSpace(buf);
726 		    addSpace = true;
727 		}
728 		Buf_Addi(buf, word->s, cp);
729 		Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
730 		pattern->flags |= VAR_SUB_MATCHED;
731 	    } else {
732 		/* Had to match at end and didn't. Copy entire word.  */
733 		goto nosub;
734 	    }
735 	} else {
736 	    /* Pattern is unanchored: search for the pattern in the word using
737 	     * strstr, copying unmatched portions and the
738 	     * right-hand-side for each match found, handling non-global
739 	     * substitutions correctly, etc. When the loop is done, any
740 	     * remaining part of the word (word and wordLen are adjusted
741 	     * accordingly through the loop) is copied straight into the
742 	     * buffer.
743 	     * addSpace is set to false as soon as a space is added to the
744 	     * buffer.	*/
745 	    bool done;
746 	    size_t origSize;
747 
748 	    done = false;
749 	    origSize = Buf_Size(buf);
750 	    while (!done) {
751 		cp = strstr(word->s, pattern->lhs);
752 		if (cp != NULL) {
753 		    if (addSpace && (cp - word->s) + pattern->rightLen != 0){
754 			Buf_AddSpace(buf);
755 			addSpace = false;
756 		    }
757 		    Buf_Addi(buf, word->s, cp);
758 		    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
759 		    wordLen -= (cp - word->s) + pattern->leftLen;
760 		    word->s = cp + pattern->leftLen;
761 		    if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0)
762 			done = true;
763 		    pattern->flags |= VAR_SUB_MATCHED;
764 		} else
765 		    done = true;
766 	    }
767 	    if (wordLen != 0) {
768 		if (addSpace)
769 		    Buf_AddSpace(buf);
770 		Buf_AddChars(buf, wordLen, word->s);
771 	    }
772 	    /* If added characters to the buffer, need to add a space
773 	     * before we add any more. If we didn't add any, just return
774 	     * the previous value of addSpace.	*/
775 	    return Buf_Size(buf) != origSize || addSpace;
776 	}
777 	return addSpace;
778     }
779  nosub:
780     if (addSpace)
781 	Buf_AddSpace(buf);
782     Buf_AddChars(buf, wordLen, word->s);
783     return true;
784 }
785 
786 #ifndef MAKE_BOOTSTRAP
787 /*-
788  *-----------------------------------------------------------------------
789  * VarREError --
790  *	Print the error caused by a regcomp or regexec call.
791  *-----------------------------------------------------------------------
792  */
793 static void
794 VarREError(int err, regex_t *pat, const char *str)
795 {
796 	char	*errbuf;
797 	int 	errlen;
798 
799 	errlen = regerror(err, pat, 0, 0);
800 	errbuf = emalloc(errlen);
801 	regerror(err, pat, errbuf, errlen);
802 	Error("%s: %s", str, errbuf);
803 	free(errbuf);
804 }
805 
806 /*-
807  *-----------------------------------------------------------------------
808  * VarRESubstitute --
809  *	Perform a regex substitution on the given word, placing the
810  *	result in the passed buffer.
811  *-----------------------------------------------------------------------
812  */
813 static bool
814 VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp)
815 {
816 	VarREPattern	*pat;
817 	int 		xrv;
818 	const char		*wp;
819 	char		*rp;
820 	int 		added;
821 
822 #define MAYBE_ADD_SPACE()		\
823 	if (addSpace && !added) 	\
824 		Buf_AddSpace(buf);	\
825 	added = 1
826 
827 	added = 0;
828 	wp = word->s;
829 	pat = patternp;
830 
831 	if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
832 	    (VAR_SUB_ONE|VAR_SUB_MATCHED))
833 		xrv = REG_NOMATCH;
834 	else {
835 	tryagain:
836 		xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0);
837 	}
838 
839 	switch (xrv) {
840 	case 0:
841 		pat->flags |= VAR_SUB_MATCHED;
842 		if (pat->matches[0].rm_so > 0) {
843 			MAYBE_ADD_SPACE();
844 			Buf_AddChars(buf, pat->matches[0].rm_so, wp);
845 		}
846 
847 		for (rp = pat->replace; *rp; rp++) {
848 			if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
849 				MAYBE_ADD_SPACE();
850 				Buf_AddChar(buf,rp[1]);
851 				rp++;
852 			}
853 			else if (*rp == '&' ||
854 			    (*rp == '\\' && isdigit(rp[1]))) {
855 				int n;
856 				const char *subbuf;
857 				int sublen;
858 				char errstr[3];
859 
860 				if (*rp == '&') {
861 					n = 0;
862 					errstr[0] = '&';
863 					errstr[1] = '\0';
864 				} else {
865 					n = rp[1] - '0';
866 					errstr[0] = '\\';
867 					errstr[1] = rp[1];
868 					errstr[2] = '\0';
869 					rp++;
870 				}
871 
872 				if (n > pat->nsub) {
873 					Error("No subexpression %s",
874 					    &errstr[0]);
875 					subbuf = "";
876 					sublen = 0;
877 				} else if (pat->matches[n].rm_so == -1 &&
878 				    pat->matches[n].rm_eo == -1) {
879 					Error("No match for subexpression %s",
880 					    &errstr[0]);
881 					subbuf = "";
882 					sublen = 0;
883 				} else {
884 					subbuf = wp + pat->matches[n].rm_so;
885 					sublen = pat->matches[n].rm_eo -
886 					    pat->matches[n].rm_so;
887 				}
888 
889 				if (sublen > 0) {
890 					MAYBE_ADD_SPACE();
891 					Buf_AddChars(buf, sublen, subbuf);
892 				}
893 			} else {
894 				MAYBE_ADD_SPACE();
895 				Buf_AddChar(buf, *rp);
896 			}
897 		}
898 		wp += pat->matches[0].rm_eo;
899 		if (pat->flags & VAR_SUB_GLOBAL)
900 			goto tryagain;
901 		if (*wp) {
902 			MAYBE_ADD_SPACE();
903 			Buf_AddString(buf, wp);
904 		}
905 		break;
906 	default:
907 		VarREError(xrv, &pat->re, "Unexpected regex error");
908 	       /* FALLTHROUGH */
909 	case REG_NOMATCH:
910 		if (*wp) {
911 			MAYBE_ADD_SPACE();
912 			Buf_AddString(buf, wp);
913 		}
914 		break;
915 	}
916 	return addSpace||added;
917 }
918 #endif
919 
920 /*-
921  *-----------------------------------------------------------------------
922  * VarModify --
923  *	Modify each of the words of the passed string using the given
924  *	function. Used to implement all modifiers.
925  *
926  * Results:
927  *	A string of all the words modified appropriately.
928  *-----------------------------------------------------------------------
929  */
930 static char *
931 VarModify(char *str, 		/* String whose words should be trimmed */
932 				/* Function to use to modify them */
933     bool (*modProc)(struct Name *, bool, Buffer, void *),
934     void *datum)		/* Datum to pass it */
935 {
936 	BUFFER	  buf;		/* Buffer for the new string */
937 	bool	  addSpace;	/* true if need to add a space to the
938 				     * buffer before adding the trimmed
939 				     * word */
940 	struct Name	  word;
941 
942 	Buf_Init(&buf, 0);
943 	addSpace = false;
944 
945 	word.e = str;
946 
947 	while ((word.s = iterate_words(&word.e)) != NULL) {
948 		char termc;
949 
950 		termc = *word.e;
951 		*((char *)(word.e)) = '\0';
952 		addSpace = (*modProc)(&word, addSpace, &buf, datum);
953 		*((char *)(word.e)) = termc;
954 	}
955 	return Buf_Retrieve(&buf);
956 }
957 
958 /*-
959  *-----------------------------------------------------------------------
960  * VarGetPattern --
961  *	Pass through the tstr looking for 1) escaped delimiters,
962  *	'$'s and backslashes (place the escaped character in
963  *	uninterpreted) and 2) unescaped $'s that aren't before
964  *	the delimiter (expand the variable substitution).
965  *	Return the expanded string or NULL if the delimiter was missing
966  *	If pattern is specified, handle escaped ampersands, and replace
967  *	unescaped ampersands with the lhs of the pattern.
968  *
969  * Results:
970  *	A string of all the words modified appropriately.
971  *	If length is specified, return the string length of the buffer
972  *-----------------------------------------------------------------------
973  */
974 static char *
975 VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1,
976     int delim2, size_t *length, VarPattern *pattern)
977 {
978 	const char	*cp;
979 	char	*result;
980 	BUFFER	buf;
981 	size_t	junk;
982 
983 	Buf_Init(&buf, 0);
984 	if (length == NULL)
985 		length = &junk;
986 
987 #define IS_A_MATCH(cp, delim1, delim2) \
988 	(cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \
989 	 cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&')))
990 
991 	/*
992 	 * Skim through until the matching delimiter is found;
993 	 * pick up variable substitutions on the way. Also allow
994 	 * backslashes to quote the delimiter, $, and \, but don't
995 	 * touch other backslashes.
996 	 */
997 	for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) {
998 		if (IS_A_MATCH(cp, delim1, delim2)) {
999 			Buf_AddChar(&buf, cp[1]);
1000 			cp++;
1001 		} else if (*cp == '$') {
1002 			/* Allowed at end of pattern */
1003 			if (cp[1] == delim1 || cp[1] == delim2)
1004 				Buf_AddChar(&buf, *cp);
1005 			else {
1006 				size_t len;
1007 
1008 				/* If unescaped dollar sign not before the
1009 				 * delimiter, assume it's a variable
1010 				 * substitution and recurse.  */
1011 				(void)Var_ParseBuffer(&buf, cp, ctxt, err,
1012 				    &len);
1013 				cp += len - 1;
1014 			}
1015 		} else if (pattern && *cp == '&')
1016 			Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
1017 		else
1018 			Buf_AddChar(&buf, *cp);
1019 	}
1020 
1021 	*length = Buf_Size(&buf);
1022 	result = Buf_Retrieve(&buf);
1023 
1024 	if (*cp != delim1 && *cp != delim2) {
1025 		*tstr = cp;
1026 		*length = 0;
1027 		free(result);
1028 		return NULL;
1029 	}
1030 	else {
1031 		*tstr = ++cp;
1032 		return result;
1033 	}
1034 }
1035 
1036 /*-
1037  *-----------------------------------------------------------------------
1038  * VarQuote --
1039  *	Quote shell meta-characters in the string
1040  *
1041  * Results:
1042  *	The quoted string
1043  *-----------------------------------------------------------------------
1044  */
1045 static char *
1046 VarQuote(const char *str, const struct Name *n UNUSED, void *dummy UNUSED)
1047 {
1048 
1049 	BUFFER	  buf;
1050 	/* This should cover most shells :-( */
1051 	static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1052 
1053 	Buf_Init(&buf, MAKE_BSIZE);
1054 	for (; *str; str++) {
1055 		if (strchr(meta, *str) != NULL)
1056 			Buf_AddChar(&buf, '\\');
1057 		Buf_AddChar(&buf, *str);
1058 	}
1059 	return Buf_Retrieve(&buf);
1060 }
1061 
1062 static void *
1063 check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1064 {
1065 	dummy_arg->s = NULL;
1066 	if ((*p)[1] == endc || (*p)[1] == ':') {
1067 		(*p)++;
1068 		return dummy_arg;
1069 	} else
1070 		return NULL;
1071 }
1072 
1073 static void *
1074 check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1075 {
1076 	if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) {
1077 		(*p)+=2;
1078 		return dummy_arg;
1079 	} else
1080 		return NULL;
1081 }
1082 
1083 
1084 static char *
1085 do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1086 {
1087 	char *err;
1088 	char *t;
1089 
1090 	t = Cmd_Exec(s, &err);
1091 	if (err)
1092 		Error(err, s);
1093 	return t;
1094 }
1095 
1096 static void *
1097 get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1098 {
1099 	const char *cp;
1100 	char *s;
1101 
1102 	for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) {
1103 		if (*cp == '\\') {
1104 			if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\')
1105 				cp++;
1106 		} else if (*cp == '\0')
1107 			return NULL;
1108 	}
1109 	s = escape_dupi(*p+1, cp, ":)}");
1110 	*p = cp;
1111 	return s;
1112 }
1113 
1114 static void
1115 free_stringarg(void *arg)
1116 {
1117 	free(arg);
1118 }
1119 
1120 static char *
1121 do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1122 {
1123 	size_t len, i;
1124 	char *t;
1125 
1126 	len = strlen(s);
1127 	t = emalloc(len+1);
1128 	for (i = 0; i < len; i++)
1129 		t[i] = toupper(s[i]);
1130 	t[len] = '\0';
1131 	return t;
1132 }
1133 
1134 static char *
1135 do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1136 {
1137 	size_t	len, i;
1138 	char	*t;
1139 
1140 	len = strlen(s);
1141 	t = emalloc(len+1);
1142 	for (i = 0; i < len; i++)
1143 		t[i] = tolower(s[i]);
1144 	t[len] = '\0';
1145 	return t;
1146 }
1147 
1148 static void *
1149 get_patternarg(const char **p, SymTable *ctxt, bool err, int endc)
1150 {
1151 	return common_get_patternarg(p, ctxt, err, endc, false);
1152 }
1153 
1154 /* Extract anchors */
1155 static void *
1156 get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc)
1157 {
1158 	VarPattern *pattern;
1159 
1160 	pattern = common_get_patternarg(p, ctxt, err, endc, true);
1161 	if (pattern != NULL && pattern->leftLen > 0) {
1162 		if (pattern->lhs[pattern->leftLen-1] == '$') {
1163 			    pattern->leftLen--;
1164 			    pattern->flags |= VAR_MATCH_END;
1165 		}
1166 		if (pattern->lhs[0] == '^') {
1167 			    pattern->lhs++;
1168 			    pattern->leftLen--;
1169 			    pattern->flags |= VAR_MATCH_START;
1170 		}
1171 	}
1172 	return pattern;
1173 }
1174 
1175 static void
1176 free_looparg(void *arg)
1177 {
1178 	struct LoopStuff *l = (struct LoopStuff *)arg;
1179 
1180 	Var_DeleteLoopVar(l->var);
1181 	free(l->expand);
1182 }
1183 
1184 static char *
1185 LoopGrab(const char **s)
1186 {
1187 	const char *p, *start;
1188 
1189 	start = *s;
1190 	for (p = start; *p != '@'; p++) {
1191 		if (*p == '\\')
1192 			p++;
1193 		if (*p == 0)
1194 			return NULL;
1195 	}
1196 	*s = p+1;
1197 	return escape_dupi(start, p, "@\\");
1198 }
1199 
1200 static void *
1201 get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc)
1202 {
1203 	static struct LoopStuff loop;
1204 	const char *s;
1205 	const char *var;
1206 
1207 	s = *p +1;
1208 
1209 	loop.var = NULL;
1210 	loop.expand = NULL;
1211 	loop.err = err;
1212 	var = LoopGrab(&s);
1213 	if (var != NULL) {
1214 		loop.expand = LoopGrab(&s);
1215 		if (*s == endc || *s == ':') {
1216 			*p = s;
1217 			loop.var = Var_NewLoopVar(var, NULL);
1218 			return &loop;
1219 		}
1220 	}
1221 	free_looparg(&loop);
1222 	return NULL;
1223 }
1224 
1225 static void *
1226 common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc,
1227     bool dosubst)
1228 {
1229 	VarPattern *pattern;
1230 	char delim;
1231 	const char *s;
1232 
1233 	pattern = (VarPattern *)emalloc(sizeof(VarPattern));
1234 	pattern->flags = 0;
1235 	s = *p;
1236 
1237 	delim = s[1];
1238 	if (delim == '\0')
1239 		return NULL;
1240 	s += 2;
1241 
1242 	pattern->rhs = NULL;
1243 	pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim,
1244 	    &pattern->leftLen, NULL);
1245 	pattern->lbuffer = pattern->lhs;
1246 	if (pattern->lhs != NULL) {
1247 		pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim,
1248 		    &pattern->rightLen, dosubst ? pattern: NULL);
1249 		if (pattern->rhs != NULL) {
1250 			/* Check for global substitution. If 'g' after the
1251 			 * final delimiter, substitution is global and is
1252 			 * marked that way.  */
1253 			for (;; s++) {
1254 				switch (*s) {
1255 				case 'g':
1256 					pattern->flags |= VAR_SUB_GLOBAL;
1257 					continue;
1258 				case '1':
1259 					pattern->flags |= VAR_SUB_ONE;
1260 					continue;
1261 				}
1262 				break;
1263 			}
1264 			if (*s == endc || *s == ':') {
1265 				*p = s;
1266 				return pattern;
1267 			}
1268 		}
1269 	}
1270 	free_patternarg(pattern);
1271 	return NULL;
1272 }
1273 
1274 static void *
1275 assign_get_value(const char **p, SymTable *ctxt, bool err, int endc)
1276 {
1277 	const char *s;
1278 	int flags;
1279 	VarPattern *arg;
1280 
1281 	s = *p + 1;
1282 	if (s[0] == '=')
1283 		flags = VAR_EQUAL;
1284 	else if (s[0] == '?' && s[1] == '=')
1285 		flags = VAR_MAY_EQUAL;
1286 	else if (s[0] == '+' && s[1] == '=')
1287 		flags = VAR_ADD_EQUAL;
1288 	else if (s[0] == '!' && s[1] == '=')
1289 		flags = VAR_BANG_EQUAL;
1290 	else
1291 		return NULL;
1292 
1293 	arg = get_value(&s, ctxt, err, endc);
1294 	if (arg != NULL) {
1295 		*p = s;
1296 		arg->flags = flags;
1297 	}
1298 	return arg;
1299 }
1300 
1301 static void *
1302 get_value(const char **p, SymTable *ctxt, bool err, int endc)
1303 {
1304 	VarPattern *pattern;
1305 	const char *s;
1306 
1307 	pattern = (VarPattern *)emalloc(sizeof(VarPattern));
1308 	s = *p + 1;
1309 	pattern->rhs = NULL;
1310 	pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc,
1311 	    &pattern->leftLen, NULL);
1312 	if (s[-1] == endc || s[-1] == ':') {
1313 		*p = s-1;
1314 		return pattern;
1315 	}
1316 	free_patternarg(pattern);
1317 	return NULL;
1318 }
1319 
1320 static void *
1321 get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED)
1322 {
1323 	VarPattern *pattern;
1324 	const char *s;
1325 
1326 	pattern = (VarPattern *)emalloc(sizeof(VarPattern));
1327 	s = *p + 1;
1328 	pattern->rhs = NULL;
1329 	pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!',
1330 	    &pattern->leftLen, NULL);
1331 	if (s[-1] == '!') {
1332 		*p = s-1;
1333 		return pattern;
1334 	}
1335 	free_patternarg(pattern);
1336 	return NULL;
1337 }
1338 
1339 static void
1340 free_patternarg(void *p)
1341 {
1342 	VarPattern *vp = (VarPattern *)p;
1343 
1344 	free(vp->lbuffer);
1345 	free(vp->rhs);
1346 	free(vp);
1347 }
1348 
1349 #ifndef MAKE_BOOTSTRAP
1350 static char *
1351 do_regex(const char *s, const struct Name *n UNUSED, void *arg)
1352 {
1353 	VarREPattern p2;
1354 	VarPattern *p = (VarPattern *)arg;
1355 	int error;
1356 	char *result;
1357 
1358 	error = regcomp(&p2.re, p->lhs, REG_EXTENDED);
1359 	if (error) {
1360 		VarREError(error, &p2.re, "RE substitution error");
1361 		return var_Error;
1362 	}
1363 	p2.nsub = p2.re.re_nsub + 1;
1364 	p2.replace = p->rhs;
1365 	p2.flags = p->flags;
1366 	if (p2.nsub < 1)
1367 		p2.nsub = 1;
1368 	if (p2.nsub > 10)
1369 		p2.nsub = 10;
1370 	p2.matches = emalloc(p2.nsub * sizeof(regmatch_t));
1371 	result = VarModify((char *)s, VarRESubstitute, &p2);
1372 	regfree(&p2.re);
1373 	free(p2.matches);
1374 	return result;
1375 }
1376 #endif
1377 
1378 char *
1379 VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt,
1380     bool err, bool *freePtr, const char **pscan, int paren)
1381 {
1382 	const char *tstr;
1383 	bool atstart;    /* Some ODE modifiers only make sense at start */
1384 	char endc = paren == '(' ? ')' : '}';
1385 	const char *start = *pscan;
1386 
1387 	tstr = start;
1388 	/*
1389 	 * Now we need to apply any modifiers the user wants applied.
1390 	 * These are:
1391 	 *		  :M<pattern>	words which match the given <pattern>.
1392 	 *				<pattern> is of the standard file
1393 	 *				wildcarding form.
1394 	 *		  :S<d><pat1><d><pat2><d>[g]
1395 	 *				Substitute <pat2> for <pat1> in the
1396 	 *				value
1397 	 *		  :C<d><pat1><d><pat2><d>[g]
1398 	 *				Substitute <pat2> for regex <pat1> in
1399 	 *				the value
1400 	 *		  :H		Substitute the head of each word
1401 	 *		  :T		Substitute the tail of each word
1402 	 *		  :E		Substitute the extension (minus '.') of
1403 	 *				each word
1404 	 *		  :R		Substitute the root of each word
1405 	 *				(pathname minus the suffix).
1406 	 *		  :lhs=rhs	Like :S, but the rhs goes to the end of
1407 	 *				the invocation.
1408 	 */
1409 
1410 	atstart = true;
1411 	while (*tstr != endc && *tstr != '\0') {
1412 		struct modifier *mod;
1413 		void *arg;
1414 		char *newStr;
1415 
1416 		tstr++;
1417 		if (DEBUG(VAR))
1418 			printf("Applying :%c to \"%s\"\n", *tstr, str);
1419 
1420 		mod = choose_mod[*tstr];
1421 		arg = NULL;
1422 
1423 		if (mod != NULL && (!mod->atstart || atstart))
1424 			arg = mod->getarg(&tstr, ctxt, err, endc);
1425 		if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) {
1426 			mod = &sysv_mod;
1427 			arg = mod->getarg(&tstr, ctxt, err, endc);
1428 		}
1429 		atstart = false;
1430 		if (arg != NULL) {
1431 			if (str != NULL || (mod->atstart && name != NULL)) {
1432 				if (mod->word_apply != NULL) {
1433 					newStr = VarModify(str,
1434 					    mod->word_apply, arg);
1435 					if (mod->apply != NULL) {
1436 						char *newStr2;
1437 
1438 						newStr2 = mod->apply(newStr,
1439 						    name, arg);
1440 						free(newStr);
1441 						newStr = newStr2;
1442 					}
1443 				} else
1444 					newStr = mod->apply(str, name, arg);
1445 				if (*freePtr)
1446 					free(str);
1447 				str = newStr;
1448 				if (str != var_Error)
1449 					*freePtr = true;
1450 				else
1451 					*freePtr = false;
1452 			}
1453 			if (mod->freearg != NULL)
1454 				mod->freearg(arg);
1455 		} else {
1456 			Error("Bad modifier: %s\n", tstr);
1457 			/* Try skipping to end of var... */
1458 			for (tstr++; *tstr != endc && *tstr != '\0';)
1459 				tstr++;
1460 			if (str != NULL && *freePtr)
1461 				free(str);
1462 			str = var_Error;
1463 			*freePtr = false;
1464 			break;
1465 		}
1466 		if (DEBUG(VAR))
1467 			printf("Result is \"%s\"\n", str);
1468 	}
1469 	if (*tstr == '\0')
1470 		Error("Unclosed variable specification");
1471 	else
1472 		tstr++;
1473 
1474 	*pscan = tstr;
1475 	return str;
1476 }
1477 
1478 char *
1479 Var_GetHead(char *s)
1480 {
1481 	return VarModify(s, VarHead, NULL);
1482 }
1483 
1484 char *
1485 Var_GetTail(char *s)
1486 {
1487 	return VarModify(s, VarTail, NULL);
1488 }
1489