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