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