xref: /openbsd-src/usr.bin/make/varmodifiers.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: varmodifiers.c,v 1.43 2015/11/15 06:19:22 daniel 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;
607 	int cnt = 0;
608 	char startc = endc == ')' ? '(' : '{';
609 	for (cp = *p;; cp++) {
610 		if (*cp == '=' && cnt == 0)
611 			break;
612 		if (*cp == '\0')
613 			return NULL;
614 		if (*cp == startc)
615 			cnt++;
616 		else if (*cp == endc) {
617 			cnt--;
618 			if (cnt < 0)
619 				return NULL;
620 		}
621 	}
622 	Buf_Init(&buf, 0);
623 	for (cp2 = cp+1;; cp2++) {
624 		if (((*cp2 == ':' && cp2[1] != endc) || *cp2 == endc) &&
625 		    cnt == 0)
626 			break;
627 		if (*cp2 == '\0') {
628 			Buf_Destroy(&buf);
629 			return NULL;
630 		}
631 		if (*cp2 == startc)
632 			cnt++;
633 		else if (*cp2 == endc) {
634 			cnt--;
635 			if (cnt < 0) {
636 				Buf_Destroy(&buf);
637 				return NULL;
638 			}
639 		} else if (*cp2 == '$') {
640 			if (cp2[1] == '$')
641 				cp2++;
642 			else {
643 				size_t len;
644 				(void)Var_ParseBuffer(&buf, cp2, ctxt, err,
645 				    &len);
646 				cp2 += len - 1;
647 				continue;
648 			}
649 		}
650 		Buf_AddChar(&buf, *cp2);
651 	}
652 
653 	pattern = emalloc(sizeof(VarPattern));
654 	pattern->lbuffer = pattern->lhs = Str_dupi(*p, cp);
655 	pattern->leftLen = cp - *p;
656 	pattern->rhs = Buf_Retrieve(&buf);
657 	pattern->rightLen = Buf_Size(&buf);
658 	pattern->flags = 0;
659 	*p = cp2;
660 	return pattern;
661 }
662 
663 
664 /*-
665  *-----------------------------------------------------------------------
666  * VarSubstitute --
667  *	Perform a string-substitution on the given word, Adding the
668  *	result to the given buffer.
669  *-----------------------------------------------------------------------
670  */
671 static bool
672 VarSubstitute(struct Name *word, bool addSpace, Buffer buf,
673     void *patternp) /* Pattern for substitution */
674 {
675     size_t	wordLen;    /* Length of word */
676     const char	*cp;	    /* General pointer */
677     VarPattern	*pattern = patternp;
678 
679     wordLen = word->e - word->s;
680     if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) !=
681 	(VAR_SUB_ONE|VAR_SUB_MATCHED)) {
682 	/* Still substituting -- break it down into simple anchored cases
683 	 * and if none of them fits, perform the general substitution case.  */
684 	if ((pattern->flags & VAR_MATCH_START) &&
685 	    (strncmp(word->s, pattern->lhs, pattern->leftLen) == 0)) {
686 		/* Anchored at start and beginning of word matches pattern.  */
687 		if ((pattern->flags & VAR_MATCH_END) &&
688 		    (wordLen == pattern->leftLen)) {
689 			/* Also anchored at end and matches to the end (word
690 			 * is same length as pattern) add space and rhs only
691 			 * if rhs is non-null.	*/
692 			if (pattern->rightLen != 0) {
693 			    if (addSpace)
694 				Buf_AddSpace(buf);
695 			    addSpace = true;
696 			    Buf_AddChars(buf, pattern->rightLen,
697 					 pattern->rhs);
698 			}
699 			pattern->flags |= VAR_SUB_MATCHED;
700 		} else if (pattern->flags & VAR_MATCH_END) {
701 		    /* Doesn't match to end -- copy word wholesale.  */
702 		    goto nosub;
703 		} else {
704 		    /* Matches at start but need to copy in
705 		     * trailing characters.  */
706 		    if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
707 			if (addSpace)
708 			    Buf_AddSpace(buf);
709 			addSpace = true;
710 		    }
711 		    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
712 		    Buf_AddChars(buf, wordLen - pattern->leftLen,
713 				 word->s + pattern->leftLen);
714 		    pattern->flags |= VAR_SUB_MATCHED;
715 		}
716 	} else if (pattern->flags & VAR_MATCH_START) {
717 	    /* Had to match at start of word and didn't -- copy whole word.  */
718 	    goto nosub;
719 	} else if (pattern->flags & VAR_MATCH_END) {
720 	    /* Anchored at end, Find only place match could occur (leftLen
721 	     * characters from the end of the word) and see if it does. Note
722 	     * that because the $ will be left at the end of the lhs, we have
723 	     * to use strncmp.	*/
724 	    cp = word->s + (wordLen - pattern->leftLen);
725 	    if (cp >= word->s &&
726 		strncmp(cp, pattern->lhs, pattern->leftLen) == 0) {
727 		/* Match found. If we will place characters in the buffer,
728 		 * add a space before hand as indicated by addSpace, then
729 		 * stuff in the initial, unmatched part of the word followed
730 		 * by the right-hand-side.  */
731 		if (((cp - word->s) + pattern->rightLen) != 0) {
732 		    if (addSpace)
733 			Buf_AddSpace(buf);
734 		    addSpace = true;
735 		}
736 		Buf_Addi(buf, word->s, cp);
737 		Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
738 		pattern->flags |= VAR_SUB_MATCHED;
739 	    } else {
740 		/* Had to match at end and didn't. Copy entire word.  */
741 		goto nosub;
742 	    }
743 	} else {
744 	    /* Pattern is unanchored: search for the pattern in the word using
745 	     * strstr, copying unmatched portions and the
746 	     * right-hand-side for each match found, handling non-global
747 	     * substitutions correctly, etc. When the loop is done, any
748 	     * remaining part of the word (word and wordLen are adjusted
749 	     * accordingly through the loop) is copied straight into the
750 	     * buffer.
751 	     * addSpace is set to false as soon as a space is added to the
752 	     * buffer.	*/
753 	    bool done;
754 	    size_t origSize;
755 
756 	    done = false;
757 	    origSize = Buf_Size(buf);
758 	    while (!done) {
759 		cp = strstr(word->s, pattern->lhs);
760 		if (cp != NULL) {
761 		    if (addSpace && (cp - word->s) + pattern->rightLen != 0){
762 			Buf_AddSpace(buf);
763 			addSpace = false;
764 		    }
765 		    Buf_Addi(buf, word->s, cp);
766 		    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
767 		    wordLen -= (cp - word->s) + pattern->leftLen;
768 		    word->s = cp + pattern->leftLen;
769 		    if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0)
770 			done = true;
771 		    pattern->flags |= VAR_SUB_MATCHED;
772 		} else
773 		    done = true;
774 	    }
775 	    if (wordLen != 0) {
776 		if (addSpace)
777 		    Buf_AddSpace(buf);
778 		Buf_AddChars(buf, wordLen, word->s);
779 	    }
780 	    /* If added characters to the buffer, need to add a space
781 	     * before we add any more. If we didn't add any, just return
782 	     * the previous value of addSpace.	*/
783 	    return Buf_Size(buf) != origSize || addSpace;
784 	}
785 	return addSpace;
786     }
787  nosub:
788     if (addSpace)
789 	Buf_AddSpace(buf);
790     Buf_AddChars(buf, wordLen, word->s);
791     return true;
792 }
793 
794 /*-
795  *-----------------------------------------------------------------------
796  * VarREError --
797  *	Print the error caused by a regcomp or regexec call.
798  *-----------------------------------------------------------------------
799  */
800 static void
801 VarREError(int err, regex_t *pat, const char *str)
802 {
803 	char	*errbuf;
804 	int 	errlen;
805 
806 	errlen = regerror(err, pat, 0, 0);
807 	errbuf = emalloc(errlen);
808 	regerror(err, pat, errbuf, errlen);
809 	Error("%s: %s", str, errbuf);
810 	free(errbuf);
811 }
812 
813 /*-
814  *-----------------------------------------------------------------------
815  * VarRESubstitute --
816  *	Perform a regex substitution on the given word, placing the
817  *	result in the passed buffer.
818  *-----------------------------------------------------------------------
819  */
820 static bool
821 VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp)
822 {
823 	VarREPattern	*pat;
824 	int 		xrv;
825 	const char		*wp;
826 	char		*rp;
827 	int 		added;
828 
829 #define MAYBE_ADD_SPACE()		\
830 	if (addSpace && !added) 	\
831 		Buf_AddSpace(buf);	\
832 	added = 1
833 
834 	added = 0;
835 	wp = word->s;
836 	pat = patternp;
837 
838 	if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
839 	    (VAR_SUB_ONE|VAR_SUB_MATCHED))
840 		xrv = REG_NOMATCH;
841 	else {
842 	tryagain:
843 		xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0);
844 	}
845 
846 	switch (xrv) {
847 	case 0:
848 		pat->flags |= VAR_SUB_MATCHED;
849 		if (pat->matches[0].rm_so > 0) {
850 			MAYBE_ADD_SPACE();
851 			Buf_AddChars(buf, pat->matches[0].rm_so, wp);
852 		}
853 
854 		for (rp = pat->replace; *rp; rp++) {
855 			if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
856 				MAYBE_ADD_SPACE();
857 				Buf_AddChar(buf,rp[1]);
858 				rp++;
859 			}
860 			else if (*rp == '&' ||
861 			    (*rp == '\\' && ISDIGIT(rp[1]))) {
862 				int n;
863 				const char *subbuf;
864 				int sublen;
865 				char errstr[3];
866 
867 				if (*rp == '&') {
868 					n = 0;
869 					errstr[0] = '&';
870 					errstr[1] = '\0';
871 				} else {
872 					n = rp[1] - '0';
873 					errstr[0] = '\\';
874 					errstr[1] = rp[1];
875 					errstr[2] = '\0';
876 					rp++;
877 				}
878 
879 				if (n > pat->nsub) {
880 					Error("No subexpression %s",
881 					    &errstr[0]);
882 					subbuf = "";
883 					sublen = 0;
884 				} else if (pat->matches[n].rm_so == -1 &&
885 				    pat->matches[n].rm_eo == -1) {
886 					Error("No match for subexpression %s",
887 					    &errstr[0]);
888 					subbuf = "";
889 					sublen = 0;
890 				} else {
891 					subbuf = wp + pat->matches[n].rm_so;
892 					sublen = pat->matches[n].rm_eo -
893 					    pat->matches[n].rm_so;
894 				}
895 
896 				if (sublen > 0) {
897 					MAYBE_ADD_SPACE();
898 					Buf_AddChars(buf, sublen, subbuf);
899 				}
900 			} else {
901 				MAYBE_ADD_SPACE();
902 				Buf_AddChar(buf, *rp);
903 			}
904 		}
905 		wp += pat->matches[0].rm_eo;
906 		if (pat->flags & VAR_SUB_GLOBAL) {
907 			/* like most modern tools, empty string matches
908 			 * should advance one char at a time...
909 			 */
910 			if (pat->matches[0].rm_eo == 0)  {
911 				if (*wp) {
912 					MAYBE_ADD_SPACE();
913 					Buf_AddChar(buf, *wp++);
914 				} else
915 					break;
916 			}
917 			goto tryagain;
918 		}
919 		if (*wp) {
920 			MAYBE_ADD_SPACE();
921 			Buf_AddString(buf, wp);
922 		}
923 		break;
924 	default:
925 		VarREError(xrv, &pat->re, "Unexpected regex error");
926 	       /* FALLTHROUGH */
927 	case REG_NOMATCH:
928 		if (*wp) {
929 			MAYBE_ADD_SPACE();
930 			Buf_AddString(buf, wp);
931 		}
932 		break;
933 	}
934 	return addSpace||added;
935 }
936 
937 /*-
938  *-----------------------------------------------------------------------
939  * VarModify --
940  *	Modify each of the words of the passed string using the given
941  *	function. Used to implement all modifiers.
942  *
943  * Results:
944  *	A string of all the words modified appropriately.
945  *-----------------------------------------------------------------------
946  */
947 static char *
948 VarModify(char *str, 		/* String whose words should be trimmed */
949 				/* Function to use to modify them */
950     bool (*modProc)(struct Name *, bool, Buffer, void *),
951     void *datum)		/* Datum to pass it */
952 {
953 	BUFFER	  buf;		/* Buffer for the new string */
954 	bool	  addSpace;	/* true if need to add a space to the
955 				     * buffer before adding the trimmed
956 				     * word */
957 	struct Name	  word;
958 
959 	Buf_Init(&buf, 0);
960 	addSpace = false;
961 
962 	word.e = str;
963 
964 	while ((word.s = iterate_words(&word.e)) != NULL) {
965 		char termc;
966 
967 		termc = *word.e;
968 		*((char *)(word.e)) = '\0';
969 		addSpace = (*modProc)(&word, addSpace, &buf, datum);
970 		*((char *)(word.e)) = termc;
971 	}
972 	return Buf_Retrieve(&buf);
973 }
974 
975 /*-
976  *-----------------------------------------------------------------------
977  * VarGetPattern --
978  *	Pass through the tstr looking for 1) escaped delimiters,
979  *	'$'s and backslashes (place the escaped character in
980  *	uninterpreted) and 2) unescaped $'s that aren't before
981  *	the delimiter (expand the variable substitution).
982  *	Return the expanded string or NULL if the delimiter was missing
983  *	If pattern is specified, handle escaped ampersands, and replace
984  *	unescaped ampersands with the lhs of the pattern.
985  *
986  * Results:
987  *	A string of all the words modified appropriately.
988  *	If length is specified, return the string length of the buffer
989  *-----------------------------------------------------------------------
990  */
991 static char *
992 VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1,
993     int delim2, size_t *length, VarPattern *pattern)
994 {
995 	const char	*cp;
996 	char	*result;
997 	BUFFER	buf;
998 	size_t	junk;
999 
1000 	Buf_Init(&buf, 0);
1001 	if (length == NULL)
1002 		length = &junk;
1003 
1004 #define IS_A_MATCH(cp, delim1, delim2) \
1005 	(cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \
1006 	 cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&')))
1007 
1008 	/*
1009 	 * Skim through until the matching delimiter is found;
1010 	 * pick up variable substitutions on the way. Also allow
1011 	 * backslashes to quote the delimiter, $, and \, but don't
1012 	 * touch other backslashes.
1013 	 */
1014 	for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) {
1015 		if (IS_A_MATCH(cp, delim1, delim2)) {
1016 			Buf_AddChar(&buf, cp[1]);
1017 			cp++;
1018 		} else if (*cp == '$') {
1019 			/* Allowed at end of pattern */
1020 			if (cp[1] == delim1 || cp[1] == delim2)
1021 				Buf_AddChar(&buf, *cp);
1022 			else {
1023 				size_t len;
1024 
1025 				/* If unescaped dollar sign not before the
1026 				 * delimiter, assume it's a variable
1027 				 * substitution and recurse.  */
1028 				(void)Var_ParseBuffer(&buf, cp, ctxt, err,
1029 				    &len);
1030 				cp += len - 1;
1031 			}
1032 		} else if (pattern && *cp == '&')
1033 			Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
1034 		else
1035 			Buf_AddChar(&buf, *cp);
1036 	}
1037 
1038 	*length = Buf_Size(&buf);
1039 	result = Buf_Retrieve(&buf);
1040 
1041 	if (*cp != delim1 && *cp != delim2) {
1042 		*tstr = cp;
1043 		*length = 0;
1044 		free(result);
1045 		return NULL;
1046 	}
1047 	else {
1048 		*tstr = ++cp;
1049 		return result;
1050 	}
1051 }
1052 
1053 /*-
1054  *-----------------------------------------------------------------------
1055  * VarQuote --
1056  *	Quote shell meta-characters in the string
1057  *
1058  * Results:
1059  *	The quoted string
1060  *-----------------------------------------------------------------------
1061  */
1062 static char *
1063 VarQuote(const char *str, const struct Name *n UNUSED, void *islistp)
1064 {
1065 	int *p = islistp;
1066 	int islist = *p;
1067 
1068 	BUFFER	  buf;
1069 	/* This should cover most shells :-( */
1070 	static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1071 	char *rep = meta;
1072 	if (islist)
1073 		rep += 3;
1074 
1075 	Buf_Init(&buf, MAKE_BSIZE);
1076 	for (; *str; str++) {
1077 		if (strchr(rep, *str) != NULL)
1078 			Buf_AddChar(&buf, '\\');
1079 		Buf_AddChar(&buf, *str);
1080 	}
1081 	return Buf_Retrieve(&buf);
1082 }
1083 
1084 static void *
1085 check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1086 {
1087 	dummy_arg->s = NULL;
1088 	if ((*p)[1] == endc || (*p)[1] == ':') {
1089 		(*p)++;
1090 		return dummy_arg;
1091 	} else
1092 		return NULL;
1093 }
1094 
1095 static void *
1096 check_quote(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1097 {
1098 	int *qargs = emalloc(sizeof(int));
1099 	*qargs = 0;
1100 	if ((*p)[1] == 'L') {
1101 		*qargs = 1;
1102 		(*p)++;
1103 	}
1104 	if ((*p)[1] == endc || (*p)[1] == ':') {
1105 		(*p)++;
1106 		return qargs;
1107 	} else  {
1108 		free(qargs);
1109 		return NULL;
1110 	}
1111 }
1112 
1113 static void *
1114 check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1115 {
1116 	if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) {
1117 		(*p)+=2;
1118 		return dummy_arg;
1119 	} else
1120 		return NULL;
1121 }
1122 
1123 
1124 static char *
1125 do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1126 {
1127 	char *err;
1128 	char *t;
1129 
1130 	t = Cmd_Exec(s, &err);
1131 	if (err)
1132 		Error(err, s);
1133 	return t;
1134 }
1135 
1136 static void *
1137 get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1138 {
1139 	const char *cp;
1140 	char *s;
1141 
1142 	for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) {
1143 		if (*cp == '\\') {
1144 			if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\')
1145 				cp++;
1146 		} else if (*cp == '\0')
1147 			return NULL;
1148 	}
1149 	s = escape_dupi(*p+1, cp, ":)}");
1150 	*p = cp;
1151 	return s;
1152 }
1153 
1154 static void
1155 free_stringarg(void *arg)
1156 {
1157 	free(arg);
1158 }
1159 
1160 static char *
1161 do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1162 {
1163 	size_t len, i;
1164 	char *t;
1165 
1166 	len = strlen(s);
1167 	t = emalloc(len+1);
1168 	for (i = 0; i < len; i++)
1169 		t[i] = TOUPPER(s[i]);
1170 	t[len] = '\0';
1171 	return t;
1172 }
1173 
1174 static char *
1175 do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1176 {
1177 	size_t	len, i;
1178 	char	*t;
1179 
1180 	len = strlen(s);
1181 	t = emalloc(len+1);
1182 	for (i = 0; i < len; i++)
1183 		t[i] = TOLOWER(s[i]);
1184 	t[len] = '\0';
1185 	return t;
1186 }
1187 
1188 static void *
1189 get_patternarg(const char **p, SymTable *ctxt, bool err, int endc)
1190 {
1191 	return common_get_patternarg(p, ctxt, err, endc, false);
1192 }
1193 
1194 /* Extract anchors */
1195 static void *
1196 get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc)
1197 {
1198 	VarPattern *pattern;
1199 
1200 	pattern = common_get_patternarg(p, ctxt, err, endc, true);
1201 	if (pattern != NULL && pattern->leftLen > 0) {
1202 		if (pattern->lhs[pattern->leftLen-1] == '$') {
1203 			    pattern->leftLen--;
1204 			    pattern->flags |= VAR_MATCH_END;
1205 		}
1206 		if (pattern->lhs[0] == '^') {
1207 			    pattern->lhs++;
1208 			    pattern->leftLen--;
1209 			    pattern->flags |= VAR_MATCH_START;
1210 		}
1211 	}
1212 	return pattern;
1213 }
1214 
1215 static void
1216 free_looparg(void *arg)
1217 {
1218 	struct LoopStuff *l = arg;
1219 
1220 	Var_DeleteLoopVar(l->var);
1221 	free(l->expand);
1222 }
1223 
1224 static char *
1225 LoopGrab(const char **s)
1226 {
1227 	const char *p, *start;
1228 
1229 	start = *s;
1230 	for (p = start; *p != '@'; p++) {
1231 		if (*p == '\\')
1232 			p++;
1233 		if (*p == 0)
1234 			return NULL;
1235 	}
1236 	*s = p+1;
1237 	return escape_dupi(start, p, "@\\");
1238 }
1239 
1240 static void *
1241 get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc)
1242 {
1243 	static struct LoopStuff loop;
1244 	const char *s;
1245 	const char *var;
1246 
1247 	s = *p +1;
1248 
1249 	loop.var = NULL;
1250 	loop.expand = NULL;
1251 	loop.err = err;
1252 	var = LoopGrab(&s);
1253 	if (var != NULL) {
1254 		loop.expand = LoopGrab(&s);
1255 		if (*s == endc || *s == ':') {
1256 			*p = s;
1257 			loop.var = Var_NewLoopVar(var, NULL);
1258 			return &loop;
1259 		}
1260 	}
1261 	free_looparg(&loop);
1262 	return NULL;
1263 }
1264 
1265 static void *
1266 common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc,
1267     bool dosubst)
1268 {
1269 	VarPattern *pattern;
1270 	char delim;
1271 	const char *s;
1272 
1273 	pattern = emalloc(sizeof(VarPattern));
1274 	pattern->flags = 0;
1275 	s = *p;
1276 
1277 	delim = s[1];
1278 	if (delim == '\0')
1279 		return NULL;
1280 	s += 2;
1281 
1282 	pattern->rhs = NULL;
1283 	pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim,
1284 	    &pattern->leftLen, NULL);
1285 	pattern->lbuffer = pattern->lhs;
1286 	if (pattern->lhs != NULL) {
1287 		pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim,
1288 		    &pattern->rightLen, dosubst ? pattern: NULL);
1289 		if (pattern->rhs != NULL) {
1290 			/* Check for global substitution. If 'g' after the
1291 			 * final delimiter, substitution is global and is
1292 			 * marked that way.  */
1293 			for (;; s++) {
1294 				switch (*s) {
1295 				case 'g':
1296 					pattern->flags |= VAR_SUB_GLOBAL;
1297 					continue;
1298 				case '1':
1299 					pattern->flags |= VAR_SUB_ONE;
1300 					continue;
1301 				}
1302 				break;
1303 			}
1304 			if (*s == endc || *s == ':') {
1305 				*p = s;
1306 				return pattern;
1307 			}
1308 		}
1309 	}
1310 	free_patternarg(pattern);
1311 	return NULL;
1312 }
1313 
1314 static void *
1315 assign_get_value(const char **p, SymTable *ctxt, bool err, int endc)
1316 {
1317 	const char *s;
1318 	int flags;
1319 	VarPattern *arg;
1320 
1321 	s = *p + 1;
1322 	if (s[0] == '=')
1323 		flags = VAR_EQUAL;
1324 	else if (s[0] == '?' && s[1] == '=')
1325 		flags = VAR_MAY_EQUAL;
1326 	else if (s[0] == '+' && s[1] == '=')
1327 		flags = VAR_ADD_EQUAL;
1328 	else if (s[0] == '!' && s[1] == '=')
1329 		flags = VAR_BANG_EQUAL;
1330 	else
1331 		return NULL;
1332 
1333 	arg = get_value(&s, ctxt, err, endc);
1334 	if (arg != NULL) {
1335 		*p = s;
1336 		arg->flags = flags;
1337 	}
1338 	return arg;
1339 }
1340 
1341 static void *
1342 get_value(const char **p, SymTable *ctxt, bool err, int endc)
1343 {
1344 	VarPattern *pattern;
1345 	const char *s;
1346 
1347 	pattern = emalloc(sizeof(VarPattern));
1348 	s = *p + 1;
1349 	pattern->rhs = NULL;
1350 	pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc,
1351 	    &pattern->leftLen, NULL);
1352 	if (s[-1] == endc || s[-1] == ':') {
1353 		*p = s-1;
1354 		return pattern;
1355 	}
1356 	free_patternarg(pattern);
1357 	return NULL;
1358 }
1359 
1360 static void *
1361 get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED)
1362 {
1363 	VarPattern *pattern;
1364 	const char *s;
1365 
1366 	pattern = emalloc(sizeof(VarPattern));
1367 	s = *p + 1;
1368 	pattern->rhs = NULL;
1369 	pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!',
1370 	    &pattern->leftLen, NULL);
1371 	if (s[-1] == '!') {
1372 		*p = s-1;
1373 		return pattern;
1374 	}
1375 	free_patternarg(pattern);
1376 	return NULL;
1377 }
1378 
1379 static void
1380 free_patternarg(void *p)
1381 {
1382 	VarPattern *vp = p;
1383 
1384 	free(vp->lbuffer);
1385 	free(vp->rhs);
1386 	free(vp);
1387 }
1388 
1389 static char *
1390 do_regex(const char *s, const struct Name *n UNUSED, void *arg)
1391 {
1392 	VarREPattern p2;
1393 	VarPattern *p = arg;
1394 	int error;
1395 	char *result;
1396 
1397 	error = regcomp(&p2.re, p->lhs, REG_EXTENDED);
1398 	if (error) {
1399 		VarREError(error, &p2.re, "RE substitution error");
1400 		return var_Error;
1401 	}
1402 	p2.nsub = p2.re.re_nsub + 1;
1403 	p2.replace = p->rhs;
1404 	p2.flags = p->flags;
1405 	if (p2.nsub < 1)
1406 		p2.nsub = 1;
1407 	if (p2.nsub > 10)
1408 		p2.nsub = 10;
1409 	p2.matches = ereallocarray(NULL, p2.nsub, sizeof(regmatch_t));
1410 	result = VarModify((char *)s, VarRESubstitute, &p2);
1411 	regfree(&p2.re);
1412 	free(p2.matches);
1413 	return result;
1414 }
1415 
1416 char *
1417 VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt,
1418     bool err, bool *freePtr, const char **pscan, int paren)
1419 {
1420 	const char *tstr;
1421 	bool atstart;    /* Some ODE modifiers only make sense at start */
1422 	char endc = paren == '(' ? ')' : '}';
1423 	const char *start = *pscan;
1424 
1425 	tstr = start;
1426 	/*
1427 	 * Now we need to apply any modifiers the user wants applied.
1428 	 * These are:
1429 	 *		  :M<pattern>	words which match the given <pattern>.
1430 	 *				<pattern> is of the standard file
1431 	 *				wildcarding form.
1432 	 *		  :S<d><pat1><d><pat2><d>[g]
1433 	 *				Substitute <pat2> for <pat1> in the
1434 	 *				value
1435 	 *		  :C<d><pat1><d><pat2><d>[g]
1436 	 *				Substitute <pat2> for regex <pat1> in
1437 	 *				the value
1438 	 *		  :H		Substitute the head of each word
1439 	 *		  :T		Substitute the tail of each word
1440 	 *		  :E		Substitute the extension (minus '.') of
1441 	 *				each word
1442 	 *		  :R		Substitute the root of each word
1443 	 *				(pathname minus the suffix).
1444 	 *		  :lhs=rhs	Like :S, but the rhs goes to the end of
1445 	 *				the invocation.
1446 	 */
1447 
1448 	atstart = true;
1449 	while (*tstr != endc && *tstr != '\0') {
1450 		struct modifier *mod;
1451 		void *arg;
1452 		char *newStr;
1453 
1454 		tstr++;
1455 		if (DEBUG(VAR))
1456 			printf("Applying :%c to \"%s\"\n", *tstr, str);
1457 
1458 		mod = choose_mod[(unsigned char)*tstr];
1459 		arg = NULL;
1460 
1461 		if (mod != NULL && (!mod->atstart || atstart))
1462 			arg = mod->getarg(&tstr, ctxt, err, endc);
1463 		if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) {
1464 			mod = &sysv_mod;
1465 			arg = mod->getarg(&tstr, ctxt, err, endc);
1466 		}
1467 		atstart = false;
1468 		if (arg != NULL) {
1469 			if (str != NULL || (mod->atstart && name != NULL)) {
1470 				if (mod->word_apply != NULL) {
1471 					newStr = VarModify(str,
1472 					    mod->word_apply, arg);
1473 					if (mod->apply != NULL) {
1474 						char *newStr2;
1475 
1476 						newStr2 = mod->apply(newStr,
1477 						    name, arg);
1478 						free(newStr);
1479 						newStr = newStr2;
1480 					}
1481 				} else
1482 					newStr = mod->apply(str, name, arg);
1483 				if (*freePtr)
1484 					free(str);
1485 				str = newStr;
1486 				if (str != var_Error)
1487 					*freePtr = true;
1488 				else
1489 					*freePtr = false;
1490 			}
1491 			if (mod->freearg != NULL)
1492 				mod->freearg(arg);
1493 		} else {
1494 			Error("Bad modifier: %s", tstr);
1495 			/* Try skipping to end of var... */
1496 			for (tstr++; *tstr != endc && *tstr != '\0';)
1497 				tstr++;
1498 			if (str != NULL && *freePtr)
1499 				free(str);
1500 			str = var_Error;
1501 			*freePtr = false;
1502 			break;
1503 		}
1504 		if (DEBUG(VAR))
1505 			printf("Result is \"%s\"\n", str);
1506 	}
1507 	if (*tstr == '\0')
1508 		Parse_Error(PARSE_FATAL, "Unclosed variable specification");
1509 	else
1510 		tstr++;
1511 
1512 	*pscan = tstr;
1513 	return str;
1514 }
1515 
1516 char *
1517 Var_GetHead(char *s)
1518 {
1519 	return VarModify(s, VarHead, NULL);
1520 }
1521 
1522 char *
1523 Var_GetTail(char *s)
1524 {
1525 	return VarModify(s, VarTail, NULL);
1526 }
1527