GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/make/varmodifiers.c Lines: 309 459 67.3 %
Date: 2017-11-13 Branches: 189 384 49.2 %

Line Branch Exec Source
1
/*	$OpenBSD: varmodifiers.c,v 1.47 2017/07/10 07:10:29 bluhm 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
33706
	choose_mod['M'] = &match_mod;
214
16853
	choose_mod['N'] = &nomatch_mod;
215
16853
	choose_mod['S'] = &subst_mod;
216
16853
	choose_mod['C'] = &resubst_mod;
217
16853
	choose_mod['Q'] = &quote_mod;
218
16853
	choose_mod['T'] = &tail_mod;
219
16853
	choose_mod['H'] = &head_mod;
220
16853
	choose_mod['E'] = &suffix_mod;
221
16853
	choose_mod['R'] = &root_mod;
222
	if (FEATURES(FEATURE_UPPERLOWER)) {
223
16853
		choose_mod['U'] = &upper_mod;
224
16853
		choose_mod['L'] = &lower_mod;
225
	}
226
	if (FEATURES(FEATURE_SUNSHCMD))
227
16853
		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
16853
}
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
6
	slash = Str_rchri(word->s, word->e, '/');
262
6
	if (slash != NULL) {
263
6
		if (addSpace)
264
			Buf_AddSpace(buf);
265
6
		Buf_Addi(buf, word->s, slash);
266
6
	} 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
6
	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
10628
	if (addSpace)
289
		Buf_AddSpace(buf);
290
10628
	slash = Str_rchri(word->s, word->e, '/');
291
10628
	if (slash != NULL)
292
464
		Buf_Addi(buf, slash+1, word->e);
293
	else
294
10164
		Buf_Addi(buf, word->s, word->e);
295
10628
	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
10164
	dot = Str_rchri(word->s, word->e, '.');
310
10164
	if (dot != NULL) {
311
10164
		if (addSpace)
312
			Buf_AddSpace(buf);
313
10164
		Buf_Addi(buf, dot+1, word->e);
314
		addSpace = true;
315
10164
	}
316
10164
	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
870206
	if (addSpace)
332
1437419
		Buf_AddSpace(buf);
333
870206
	dot = Str_rchri(word->s, word->e, '.');
334
870206
	if (dot != NULL)
335
870206
		Buf_Addi(buf, word->s, dot);
336
	else
337
		Buf_Addi(buf, word->s, word->e);
338
870206
	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
4584287
	if (Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {
354
397381
		if (addSpace)
355
252856
			Buf_AddSpace(buf);
356
397381
		Buf_Addi(buf, word->s, word->e);
357
397381
		return true;
358
	} else
359
4186906
		return addSpace;
360
4584287
}
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
3166770
	if (!Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {
375
3091852
		if (addSpace)
376
6003475
			Buf_AddSpace(buf);
377
3091852
		Buf_Addi(buf, word->s, word->e);
378
3091852
		return true;
379
	} else
380
74918
		return addSpace;
381
3166770
}
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
478048
	size_t	len;
586
	const char	*ptr;
587
478048
	VarPattern	*pat = patp;
588
589
478048
	if (*word->s != '\0') {
590
478048
		if (addSpace)
591
927519
			Buf_AddSpace(buf);
592
478048
		if ((ptr = Str_SYSVMatch(word->s, pat->lhs, &len)) != NULL)
593
478048
			Str_SYSVSubst(buf, pat->rhs, ptr, len);
594
		else
595
			Buf_Addi(buf, word->s, word->e);
596
478048
		return true;
597
	} else
598
		return addSpace;
599
478048
}
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
37264
	BUFFER buf, buf2;
607
	int cnt = 0;
608
37264
	char startc = endc == ')' ? '(' : '{';
609
610
37264
	Buf_Init(&buf, 0);
611
111434
	for (cp = *p;; cp++) {
612
111434
		if (*cp == '=' && cnt == 0)
613
			break;
614
74170
		if (*cp == '\0') {
615
			Buf_Destroy(&buf);
616
			return NULL;
617
		}
618
74170
		if (*cp == startc)
619
			cnt++;
620
74170
		else if (*cp == endc) {
621
			cnt--;
622
			if (cnt < 0) {
623
				Buf_Destroy(&buf);
624
				return NULL;
625
			}
626
74170
		} else if (*cp == '$') {
627
			if (cp[1] == '$')
628
				cp++;
629
			else {
630
				size_t len;
631
				(void)Var_ParseBuffer(&buf, cp, ctxt, err,
632
				    &len);
633
				cp += len - 1;
634
				continue;
635
			}
636
		}
637
148340
		Buf_AddChar(&buf, *cp);
638
74170
	}
639
640
37264
	Buf_Init(&buf2, 0);
641
116079
	for (cp2 = cp+1;; cp2++) {
642


348237
		if (((*cp2 == ':' && cp2[1] != endc) || *cp2 == endc) &&
643
116079
		    cnt == 0)
644
			break;
645
78815
		if (*cp2 == '\0') {
646
			Buf_Destroy(&buf);
647
			Buf_Destroy(&buf2);
648
			return NULL;
649
		}
650
78815
		if (*cp2 == startc)
651
			cnt++;
652
78815
		else if (*cp2 == endc) {
653
			cnt--;
654
			if (cnt < 0) {
655
				Buf_Destroy(&buf);
656
				Buf_Destroy(&buf2);
657
				return NULL;
658
			}
659
78815
		} else if (*cp2 == '$') {
660
			if (cp2[1] == '$')
661
				cp2++;
662
			else {
663
				size_t len;
664
				(void)Var_ParseBuffer(&buf2, cp2, ctxt, err,
665
				    &len);
666
				cp2 += len - 1;
667
				continue;
668
			}
669
		}
670
157630
		Buf_AddChar(&buf2, *cp2);
671
78815
	}
672
673
37264
	pattern = emalloc(sizeof(VarPattern));
674
37264
	pattern->lbuffer = pattern->lhs = Buf_Retrieve(&buf);
675
37264
	pattern->leftLen = Buf_Size(&buf);
676
37264
	pattern->rhs = Buf_Retrieve(&buf2);
677
37264
	pattern->rightLen = Buf_Size(&buf2);
678
37264
	pattern->flags = 0;
679
37264
	*p = cp2;
680
37264
	return pattern;
681
37264
}
682
683
684
/*-
685
 *-----------------------------------------------------------------------
686
 * VarSubstitute --
687
 *	Perform a string-substitution on the given word, Adding the
688
 *	result to the given buffer.
689
 *-----------------------------------------------------------------------
690
 */
691
static bool
692
VarSubstitute(struct Name *word, bool addSpace, Buffer buf,
693
    void *patternp) /* Pattern for substitution */
694
{
695
    size_t	wordLen;    /* Length of word */
696
    const char	*cp;	    /* General pointer */
697
6275821
    VarPattern	*pattern = patternp;
698
699
6275821
    wordLen = word->e - word->s;
700
6275821
    if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) !=
701
	(VAR_SUB_ONE|VAR_SUB_MATCHED)) {
702
	/* Still substituting -- break it down into simple anchored cases
703
	 * and if none of them fits, perform the general substitution case.  */
704

11610509
	if ((pattern->flags & VAR_MATCH_START) &&
705
5334688
	    (strncmp(word->s, pattern->lhs, pattern->leftLen) == 0)) {
706
		/* Anchored at start and beginning of word matches pattern.  */
707

2517746
		if ((pattern->flags & VAR_MATCH_END) &&
708
		    (wordLen == pattern->leftLen)) {
709
			/* Also anchored at end and matches to the end (word
710
			 * is same length as pattern) add space and rhs only
711
			 * if rhs is non-null.	*/
712
			if (pattern->rightLen != 0) {
713
			    if (addSpace)
714
				Buf_AddSpace(buf);
715
			    addSpace = true;
716
			    Buf_AddChars(buf, pattern->rightLen,
717
					 pattern->rhs);
718
			}
719
			pattern->flags |= VAR_SUB_MATCHED;
720
2517746
		} else if (pattern->flags & VAR_MATCH_END) {
721
		    /* Doesn't match to end -- copy word wholesale.  */
722
		    goto nosub;
723
		} else {
724
		    /* Matches at start but need to copy in
725
		     * trailing characters.  */
726
2517746
		    if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
727
2517746
			if (addSpace)
728
4695274
			    Buf_AddSpace(buf);
729
			addSpace = true;
730
2517746
		    }
731
2517746
		    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
732
5035492
		    Buf_AddChars(buf, wordLen - pattern->leftLen,
733
2517746
				 word->s + pattern->leftLen);
734
		    pattern->flags |= VAR_SUB_MATCHED;
735
		}
736
3758075
	} else if (pattern->flags & VAR_MATCH_START) {
737
	    /* Had to match at start of word and didn't -- copy whole word.  */
738
	    goto nosub;
739
941133
	} else if (pattern->flags & VAR_MATCH_END) {
740
	    /* Anchored at end, Find only place match could occur (leftLen
741
	     * characters from the end of the word) and see if it does. Note
742
	     * that because the $ will be left at the end of the lhs, we have
743
	     * to use strncmp.	*/
744
873003
	    cp = word->s + (wordLen - pattern->leftLen);
745

1746006
	    if (cp >= word->s &&
746
873003
		strncmp(cp, pattern->lhs, pattern->leftLen) == 0) {
747
		/* Match found. If we will place characters in the buffer,
748
		 * add a space before hand as indicated by addSpace, then
749
		 * stuff in the initial, unmatched part of the word followed
750
		 * by the right-hand-side.  */
751
873003
		if (((cp - word->s) + pattern->rightLen) != 0) {
752
873003
		    if (addSpace)
753
1438145
			Buf_AddSpace(buf);
754
		    addSpace = true;
755
873003
		}
756
873003
		Buf_Addi(buf, word->s, cp);
757
873003
		Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
758
		pattern->flags |= VAR_SUB_MATCHED;
759
	    } else {
760
		/* Had to match at end and didn't. Copy entire word.  */
761
		goto nosub;
762
	    }
763
	} else {
764
	    /* Pattern is unanchored: search for the pattern in the word using
765
	     * strstr, copying unmatched portions and the
766
	     * right-hand-side for each match found, handling non-global
767
	     * substitutions correctly, etc. When the loop is done, any
768
	     * remaining part of the word (word and wordLen are adjusted
769
	     * accordingly through the loop) is copied straight into the
770
	     * buffer.
771
	     * addSpace is set to false as soon as a space is added to the
772
	     * buffer.	*/
773
	    bool done;
774
	    size_t origSize;
775
776
	    done = false;
777
68130
	    origSize = Buf_Size(buf);
778
273036
	    while (!done) {
779
68388
		cp = strstr(word->s, pattern->lhs);
780
68388
		if (cp != NULL) {
781

11901
		    if (addSpace && (cp - word->s) + pattern->rightLen != 0){
782
470
			Buf_AddSpace(buf);
783
			addSpace = false;
784
235
		    }
785
11666
		    Buf_Addi(buf, word->s, cp);
786
11666
		    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
787
11666
		    wordLen -= (cp - word->s) + pattern->leftLen;
788
11666
		    word->s = cp + pattern->leftLen;
789

22695
		    if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0)
790
11408
			done = true;
791
11666
		    pattern->flags |= VAR_SUB_MATCHED;
792
11666
		} else
793
		    done = true;
794
	    }
795
68130
	    if (wordLen != 0) {
796
67493
		if (addSpace)
797
109435
		    Buf_AddSpace(buf);
798
67493
		Buf_AddChars(buf, wordLen, word->s);
799
67493
	    }
800
	    /* If added characters to the buffer, need to add a space
801
	     * before we add any more. If we didn't add any, just return
802
	     * the previous value of addSpace.	*/
803
68130
	    return Buf_Size(buf) != origSize || addSpace;
804
	}
805
3390749
	return addSpace;
806
    }
807
 nosub:
808
2816942
    if (addSpace)
809
	Buf_AddSpace(buf);
810
2816942
    Buf_AddChars(buf, wordLen, word->s);
811
2816942
    return true;
812
6275821
}
813
814
/*-
815
 *-----------------------------------------------------------------------
816
 * VarREError --
817
 *	Print the error caused by a regcomp or regexec call.
818
 *-----------------------------------------------------------------------
819
 */
820
static void
821
VarREError(int err, regex_t *pat, const char *str)
822
{
823
	char	*errbuf;
824
	int 	errlen;
825
826
	errlen = regerror(err, pat, 0, 0);
827
	errbuf = emalloc(errlen);
828
	regerror(err, pat, errbuf, errlen);
829
	Error("%s: %s", str, errbuf);
830
	free(errbuf);
831
}
832
833
/*-
834
 *-----------------------------------------------------------------------
835
 * VarRESubstitute --
836
 *	Perform a regex substitution on the given word, placing the
837
 *	result in the passed buffer.
838
 *-----------------------------------------------------------------------
839
 */
840
static bool
841
VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp)
842
{
843
	VarREPattern	*pat;
844
	int 		xrv;
845
	const char		*wp;
846
	char		*rp;
847
	int 		added;
848
849
#define MAYBE_ADD_SPACE()		\
850
	if (addSpace && !added) 	\
851
		Buf_AddSpace(buf);	\
852
	added = 1
853
854
	added = 0;
855
10986379
	wp = word->s;
856
10986379
	pat = patternp;
857
858
10986379
	if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
859
	    (VAR_SUB_ONE|VAR_SUB_MATCHED))
860
		xrv = REG_NOMATCH;
861
	else {
862
	tryagain:
863
10986379
		xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0);
864
	}
865
866
10986379
	switch (xrv) {
867
	case 0:
868
10985869
		pat->flags |= VAR_SUB_MATCHED;
869
10985869
		if (pat->matches[0].rm_so > 0) {
870

2966909
			MAYBE_ADD_SPACE();
871
2848389
			Buf_AddChars(buf, pat->matches[0].rm_so, wp);
872
2848389
		}
873
874
23604174
		for (rp = pat->replace; *rp; rp++) {
875

816218
			if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
876
				MAYBE_ADD_SPACE();
877
				Buf_AddChar(buf,rp[1]);
878
				rp++;
879
			}
880

816218
			else if (*rp == '&' ||
881
816218
			    (*rp == '\\' && ISDIGIT(rp[1]))) {
882
				int n;
883
				const char *subbuf;
884
				int sublen;
885
				char errstr[3];
886
887
				if (*rp == '&') {
888
					n = 0;
889
					errstr[0] = '&';
890
					errstr[1] = '\0';
891
				} else {
892
					n = rp[1] - '0';
893
					errstr[0] = '\\';
894
					errstr[1] = rp[1];
895
					errstr[2] = '\0';
896
					rp++;
897
				}
898
899
				if (n >= pat->nsub) {
900
					Error("No subexpression %s",
901
					    &errstr[0]);
902
					subbuf = "";
903
					sublen = 0;
904
				} else if (pat->matches[n].rm_so == -1 &&
905
				    pat->matches[n].rm_eo == -1) {
906
					Error("No match for subexpression %s",
907
					    &errstr[0]);
908
					subbuf = "";
909
					sublen = 0;
910
				} else {
911
					subbuf = wp + pat->matches[n].rm_so;
912
					sublen = pat->matches[n].rm_eo -
913
					    pat->matches[n].rm_so;
914
				}
915
916
				if (sublen > 0) {
917
					MAYBE_ADD_SPACE();
918
					Buf_AddChars(buf, sublen, subbuf);
919
				}
920
			} else {
921

816218
				MAYBE_ADD_SPACE();
922
1633538
				Buf_AddChar(buf, *rp);
923
			}
924
		}
925
10985869
		wp += pat->matches[0].rm_eo;
926
10985869
		if (pat->flags & VAR_SUB_GLOBAL) {
927
			/* like most modern tools, empty string matches
928
			 * should advance one char at a time...
929
			 */
930
			if (pat->matches[0].rm_eo == 0)  {
931
				if (*wp) {
932
					MAYBE_ADD_SPACE();
933
					Buf_AddChar(buf, *wp++);
934
				} else
935
					break;
936
			}
937
			goto tryagain;
938
		}
939
10985869
		if (*wp) {
940

8137500
			MAYBE_ADD_SPACE();
941
8137500
			Buf_AddString(buf, wp);
942
8137500
		}
943
		break;
944
	default:
945
		VarREError(xrv, &pat->re, "Unexpected regex error");
946
	       /* FALLTHROUGH */
947
	case REG_NOMATCH:
948
510
		if (*wp) {
949

1490
			MAYBE_ADD_SPACE();
950
510
			Buf_AddString(buf, wp);
951
510
		}
952
		break;
953
	}
954
10986379
	return addSpace||added;
955
}
956
957
/*-
958
 *-----------------------------------------------------------------------
959
 * VarModify --
960
 *	Modify each of the words of the passed string using the given
961
 *	function. Used to implement all modifiers.
962
 *
963
 * Results:
964
 *	A string of all the words modified appropriately.
965
 *-----------------------------------------------------------------------
966
 */
967
static char *
968
VarModify(char *str, 		/* String whose words should be trimmed */
969
				/* Function to use to modify them */
970
    bool (*modProc)(struct Name *, bool, Buffer, void *),
971
    void *datum)		/* Datum to pass it */
972
{
973
36570662
	BUFFER	  buf;		/* Buffer for the new string */
974
	bool	  addSpace;	/* true if need to add a space to the
975
				     * buffer before adding the trimmed
976
				     * word */
977
18285331
	struct Name	  word;
978
979
18285331
	Buf_Init(&buf, 0);
980
	addSpace = false;
981
982
18285331
	word.e = str;
983
984
89335280
	while ((word.s = iterate_words(&word.e)) != NULL) {
985
		char termc;
986
987
26382309
		termc = *word.e;
988
26382309
		*((char *)(word.e)) = '\0';
989
26382309
		addSpace = (*modProc)(&word, addSpace, &buf, datum);
990
26382309
		*((char *)(word.e)) = termc;
991
	}
992
36570662
	return Buf_Retrieve(&buf);
993
18285331
}
994
995
/*-
996
 *-----------------------------------------------------------------------
997
 * VarGetPattern --
998
 *	Pass through the tstr looking for 1) escaped delimiters,
999
 *	'$'s and backslashes (place the escaped character in
1000
 *	uninterpreted) and 2) unescaped $'s that aren't before
1001
 *	the delimiter (expand the variable substitution).
1002
 *	Return the expanded string or NULL if the delimiter was missing
1003
 *	If pattern is specified, handle escaped ampersands, and replace
1004
 *	unescaped ampersands with the lhs of the pattern.
1005
 *
1006
 * Results:
1007
 *	A string of all the words modified appropriately.
1008
 *	If length is specified, return the string length of the buffer
1009
 *-----------------------------------------------------------------------
1010
 */
1011
static char *
1012
VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1,
1013
    int delim2, size_t *length, VarPattern *pattern)
1014
{
1015
	const char	*cp;
1016
	char	*result;
1017
56406792
	BUFFER	buf;
1018
28203396
	size_t	junk;
1019
1020
28203396
	Buf_Init(&buf, 0);
1021
28203396
	if (length == NULL)
1022
		length = &junk;
1023
1024
#define IS_A_MATCH(cp, delim1, delim2) \
1025
	(cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \
1026
	 cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&')))
1027
1028
	/*
1029
	 * Skim through until the matching delimiter is found;
1030
	 * pick up variable substitutions on the way. Also allow
1031
	 * backslashes to quote the delimiter, $, and \, but don't
1032
	 * touch other backslashes.
1033
	 */
1034

386971220
	for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) {
1035



90008775
		if (IS_A_MATCH(cp, delim1, delim2)) {
1036
43084
			Buf_AddChar(&buf, cp[1]);
1037
21542
			cp++;
1038
75590258
		} else if (*cp == '$') {
1039
			/* Allowed at end of pattern */
1040

205146
			if (cp[1] == delim1 || cp[1] == delim2)
1041
367208
				Buf_AddChar(&buf, *cp);
1042
			else {
1043
10771
				size_t len;
1044
1045
				/* If unescaped dollar sign not before the
1046
				 * delimiter, assume it's a variable
1047
				 * substitution and recurse.  */
1048
10771
				(void)Var_ParseBuffer(&buf, cp, ctxt, err,
1049
				    &len);
1050
10771
				cp += len - 1;
1051
10771
			}
1052

75993336
		} else if (pattern && *cp == '&')
1053
			Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
1054
		else
1055
150748682
			Buf_AddChar(&buf, *cp);
1056
	}
1057
1058
28203396
	*length = Buf_Size(&buf);
1059
28203396
	result = Buf_Retrieve(&buf);
1060
1061

28203396
	if (*cp != delim1 && *cp != delim2) {
1062
		*tstr = cp;
1063
		*length = 0;
1064
		free(result);
1065
		return NULL;
1066
	}
1067
	else {
1068
28203396
		*tstr = ++cp;
1069
28203396
		return result;
1070
	}
1071
28203396
}
1072
1073
/*-
1074
 *-----------------------------------------------------------------------
1075
 * VarQuote --
1076
 *	Quote shell meta-characters in the string
1077
 *
1078
 * Results:
1079
 *	The quoted string
1080
 *-----------------------------------------------------------------------
1081
 */
1082
static char *
1083
VarQuote(const char *str, const struct Name *n UNUSED, void *islistp)
1084
{
1085
	int *p = islistp;
1086
	int islist = *p;
1087
1088
	BUFFER	  buf;
1089
	/* This should cover most shells :-( */
1090
	static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1091
	char *rep = meta;
1092
	if (islist)
1093
		rep += 3;
1094
1095
	Buf_Init(&buf, MAKE_BSIZE);
1096
	for (; *str; str++) {
1097
		if (strchr(rep, *str) != NULL)
1098
			Buf_AddChar(&buf, '\\');
1099
		Buf_AddChar(&buf, *str);
1100
	}
1101
	return Buf_Retrieve(&buf);
1102
}
1103
1104
static void *
1105
check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1106
{
1107
385490
	dummy_arg->s = NULL;
1108

341591
	if ((*p)[1] == endc || (*p)[1] == ':') {
1109
192745
		(*p)++;
1110
192745
		return dummy_arg;
1111
	} else
1112
		return NULL;
1113
192745
}
1114
1115
static void *
1116
check_quote(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1117
{
1118
	int *qargs = emalloc(sizeof(int));
1119
	*qargs = 0;
1120
	if ((*p)[1] == 'L') {
1121
		*qargs = 1;
1122
		(*p)++;
1123
	}
1124
	if ((*p)[1] == endc || (*p)[1] == ':') {
1125
		(*p)++;
1126
		return qargs;
1127
	} else  {
1128
		free(qargs);
1129
		return NULL;
1130
	}
1131
}
1132
1133
static void *
1134
check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1135
{
1136
	if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) {
1137
		(*p)+=2;
1138
		return dummy_arg;
1139
	} else
1140
		return NULL;
1141
}
1142
1143
1144
static char *
1145
do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1146
{
1147
	char *err;
1148
	char *t;
1149
1150
	t = Cmd_Exec(s, &err);
1151
	if (err)
1152
		Error(err, s);
1153
	return t;
1154
}
1155
1156
static void *
1157
get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1158
{
1159
	const char *cp;
1160
	char *s;
1161
1162

148011467
	for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) {
1163
44002086
		if (*cp == '\\') {
1164
			if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\')
1165
				cp++;
1166
44002086
		} else if (*cp == '\0')
1167
			return NULL;
1168
	}
1169
4026734
	s = escape_dupi(*p+1, cp, ":)}");
1170
4026734
	*p = cp;
1171
4026734
	return s;
1172
4026734
}
1173
1174
static void
1175
free_stringarg(void *arg)
1176
{
1177
8053468
	free(arg);
1178
4026734
}
1179
1180
static char *
1181
do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1182
{
1183
	size_t len, i;
1184
	char *t;
1185
1186
	len = strlen(s);
1187
	t = emalloc(len+1);
1188
	for (i = 0; i < len; i++)
1189
		t[i] = TOUPPER(s[i]);
1190
	t[len] = '\0';
1191
	return t;
1192
}
1193
1194
static char *
1195
do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1196
{
1197
	size_t	len, i;
1198
	char	*t;
1199
1200
33278
	len = strlen(s);
1201
16639
	t = emalloc(len+1);
1202
101912
	for (i = 0; i < len; i++)
1203
34317
		t[i] = TOLOWER(s[i]);
1204
16639
	t[len] = '\0';
1205
16639
	return t;
1206
}
1207
1208
static void *
1209
get_patternarg(const char **p, SymTable *ctxt, bool err, int endc)
1210
{
1211
10946192
	return common_get_patternarg(p, ctxt, err, endc, false);
1212
}
1213
1214
/* Extract anchors */
1215
static void *
1216
get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc)
1217
{
1218
	VarPattern *pattern;
1219
1220
3155506
	pattern = common_get_patternarg(p, ctxt, err, endc, true);
1221

6311012
	if (pattern != NULL && pattern->leftLen > 0) {
1222
3155506
		if (pattern->lhs[pattern->leftLen-1] == '$') {
1223
154162
			    pattern->leftLen--;
1224
154162
			    pattern->flags |= VAR_MATCH_END;
1225
154162
		}
1226
3155506
		if (pattern->lhs[0] == '^') {
1227
2988059
			    pattern->lhs++;
1228
2988059
			    pattern->leftLen--;
1229
2988059
			    pattern->flags |= VAR_MATCH_START;
1230
2988059
		}
1231
	}
1232
3155506
	return pattern;
1233
}
1234
1235
static void
1236
free_looparg(void *arg)
1237
{
1238
	struct LoopStuff *l = arg;
1239
1240
	Var_DeleteLoopVar(l->var);
1241
	free(l->expand);
1242
}
1243
1244
static char *
1245
LoopGrab(const char **s)
1246
{
1247
	const char *p, *start;
1248
1249
	start = *s;
1250
	for (p = start; *p != '@'; p++) {
1251
		if (*p == '\\')
1252
			p++;
1253
		if (*p == 0)
1254
			return NULL;
1255
	}
1256
	*s = p+1;
1257
	return escape_dupi(start, p, "@\\");
1258
}
1259
1260
static void *
1261
get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc)
1262
{
1263
	static struct LoopStuff loop;
1264
	const char *s;
1265
	const char *var;
1266
1267
	s = *p +1;
1268
1269
	loop.var = NULL;
1270
	loop.expand = NULL;
1271
	loop.err = err;
1272
	var = LoopGrab(&s);
1273
	if (var != NULL) {
1274
		loop.expand = LoopGrab(&s);
1275
		if (*s == endc || *s == ':') {
1276
			*p = s;
1277
			loop.var = Var_NewLoopVar(var, NULL);
1278
			return &loop;
1279
		}
1280
	}
1281
	free_looparg(&loop);
1282
	return NULL;
1283
}
1284
1285
static void *
1286
common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc,
1287
    bool dosubst)
1288
{
1289
	VarPattern *pattern;
1290
	char delim;
1291
14101698
	const char *s;
1292
1293
14101698
	pattern = emalloc(sizeof(VarPattern));
1294
14101698
	pattern->flags = 0;
1295
14101698
	s = *p;
1296
1297
14101698
	delim = s[1];
1298
14101698
	if (delim == '\0')
1299
		return NULL;
1300
14101698
	s += 2;
1301
1302
14101698
	pattern->rhs = NULL;
1303
28203396
	pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim,
1304
14101698
	    &pattern->leftLen, NULL);
1305
14101698
	pattern->lbuffer = pattern->lhs;
1306
14101698
	if (pattern->lhs != NULL) {
1307
14101698
		pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim,
1308
14101698
		    &pattern->rightLen, dosubst ? pattern: NULL);
1309
14101698
		if (pattern->rhs != NULL) {
1310
			/* Check for global substitution. If 'g' after the
1311
			 * final delimiter, substitution is global and is
1312
			 * marked that way.  */
1313
14104053
			for (;; s++) {
1314
14104053
				switch (*s) {
1315
				case 'g':
1316
					pattern->flags |= VAR_SUB_GLOBAL;
1317
2355
					continue;
1318
				case '1':
1319
					pattern->flags |= VAR_SUB_ONE;
1320
					continue;
1321
				}
1322
				break;
1323
			}
1324

16781978
			if (*s == endc || *s == ':') {
1325
14101698
				*p = s;
1326
14101698
				return pattern;
1327
			}
1328
		}
1329
	}
1330
	free_patternarg(pattern);
1331
	return NULL;
1332
14101698
}
1333
1334
static void *
1335
assign_get_value(const char **p, SymTable *ctxt, bool err, int endc)
1336
{
1337
	const char *s;
1338
	int flags;
1339
	VarPattern *arg;
1340
1341
	s = *p + 1;
1342
	if (s[0] == '=')
1343
		flags = VAR_EQUAL;
1344
	else if (s[0] == '?' && s[1] == '=')
1345
		flags = VAR_MAY_EQUAL;
1346
	else if (s[0] == '+' && s[1] == '=')
1347
		flags = VAR_ADD_EQUAL;
1348
	else if (s[0] == '!' && s[1] == '=')
1349
		flags = VAR_BANG_EQUAL;
1350
	else
1351
		return NULL;
1352
1353
	arg = get_value(&s, ctxt, err, endc);
1354
	if (arg != NULL) {
1355
		*p = s;
1356
		arg->flags = flags;
1357
	}
1358
	return arg;
1359
}
1360
1361
static void *
1362
get_value(const char **p, SymTable *ctxt, bool err, int endc)
1363
{
1364
	VarPattern *pattern;
1365
	const char *s;
1366
1367
	pattern = emalloc(sizeof(VarPattern));
1368
	s = *p + 1;
1369
	pattern->rhs = NULL;
1370
	pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc,
1371
	    &pattern->leftLen, NULL);
1372
	if (s[-1] == endc || s[-1] == ':') {
1373
		*p = s-1;
1374
		return pattern;
1375
	}
1376
	free_patternarg(pattern);
1377
	return NULL;
1378
}
1379
1380
static void *
1381
get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED)
1382
{
1383
	VarPattern *pattern;
1384
	const char *s;
1385
1386
	pattern = emalloc(sizeof(VarPattern));
1387
	s = *p + 1;
1388
	pattern->rhs = NULL;
1389
	pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!',
1390
	    &pattern->leftLen, NULL);
1391
	if (s[-1] == '!') {
1392
		*p = s-1;
1393
		return pattern;
1394
	}
1395
	free_patternarg(pattern);
1396
	return NULL;
1397
}
1398
1399
static void
1400
free_patternarg(void *p)
1401
{
1402
28277924
	VarPattern *vp = p;
1403
1404
14138962
	free(vp->lbuffer);
1405
14138962
	free(vp->rhs);
1406
14138962
	free(vp);
1407
14138962
}
1408
1409
static char *
1410
do_regex(const char *s, const struct Name *n UNUSED, void *arg)
1411
{
1412
21853258
	VarREPattern p2;
1413
10926629
	VarPattern *p = arg;
1414
	int error;
1415
	char *result;
1416
1417
10926629
	error = regcomp(&p2.re, p->lhs, REG_EXTENDED);
1418
10926629
	if (error) {
1419
		VarREError(error, &p2.re, "RE substitution error");
1420
		return var_Error;
1421
	}
1422
10926629
	p2.nsub = p2.re.re_nsub + 1;
1423
10926629
	p2.replace = p->rhs;
1424
10926629
	p2.flags = p->flags;
1425
10926629
	if (p2.nsub < 1)
1426
		p2.nsub = 1;
1427
10926629
	if (p2.nsub > 10)
1428
		p2.nsub = 10;
1429
10926629
	p2.matches = ereallocarray(NULL, p2.nsub, sizeof(regmatch_t));
1430
10926629
	result = VarModify((char *)s, VarRESubstitute, &p2);
1431
10926629
	regfree(&p2.re);
1432
10926629
	free(p2.matches);
1433
10926629
	return result;
1434
10926629
}
1435
1436
char *
1437
VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt,
1438
    bool err, bool *freePtr, const char **pscan, int paren)
1439
{
1440
15427588
	const char *tstr;
1441
	bool atstart;    /* Some ODE modifiers only make sense at start */
1442
15427588
	char endc = paren == '(' ? ')' : '}';
1443
15427588
	const char *start = *pscan;
1444
1445
15427588
	tstr = start;
1446
	/*
1447
	 * Now we need to apply any modifiers the user wants applied.
1448
	 * These are:
1449
	 *		  :M<pattern>	words which match the given <pattern>.
1450
	 *				<pattern> is of the standard file
1451
	 *				wildcarding form.
1452
	 *		  :S<d><pat1><d><pat2><d>[g]
1453
	 *				Substitute <pat2> for <pat1> in the
1454
	 *				value
1455
	 *		  :C<d><pat1><d><pat2><d>[g]
1456
	 *				Substitute <pat2> for regex <pat1> in
1457
	 *				the value
1458
	 *		  :H		Substitute the head of each word
1459
	 *		  :T		Substitute the tail of each word
1460
	 *		  :E		Substitute the extension (minus '.') of
1461
	 *				each word
1462
	 *		  :R		Substitute the root of each word
1463
	 *				(pathname minus the suffix).
1464
	 *		  :lhs=rhs	Like :S, but the rhs goes to the end of
1465
	 *				the invocation.
1466
	 */
1467
1468
	atstart = true;
1469

85930499
	while (*tstr != endc && *tstr != '\0') {
1470
		struct modifier *mod;
1471
		void *arg;
1472
		char *newStr;
1473
1474
18358441
		tstr++;
1475
18358441
		if (DEBUG(VAR)) {
1476
			if (str != NULL)
1477
				printf("Applying :%c to \"%s\"\n", *tstr, str);
1478
			else
1479
				printf("Applying :%c\n", *tstr);
1480
		}
1481
1482
18358441
		mod = choose_mod[(unsigned char)*tstr];
1483
		arg = NULL;
1484
1485

36679618
		if (mod != NULL && (!mod->atstart || atstart))
1486
18321177
			arg = mod->getarg(&tstr, ctxt, err, endc);
1487
18358441
		if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) {
1488
			mod = &sysv_mod;
1489
37264
			arg = mod->getarg(&tstr, ctxt, err, endc);
1490
37264
		}
1491
		atstart = false;
1492
18358441
		if (arg != NULL) {
1493

18414912
			if (str != NULL || (mod->atstart && name != NULL)) {
1494
18301970
				if (mod->word_apply != NULL) {
1495
7358702
					newStr = VarModify(str,
1496
					    mod->word_apply, arg);
1497
7358702
					if (mod->apply != NULL) {
1498
						char *newStr2;
1499
1500
						newStr2 = mod->apply(newStr,
1501
						    name, arg);
1502
						free(newStr);
1503
						newStr = newStr2;
1504
					}
1505
				} else
1506
10943268
					newStr = mod->apply(str, name, arg);
1507
18301970
				if (*freePtr)
1508
3070259
					free(str);
1509
				str = newStr;
1510
18301970
				if (str != var_Error)
1511
					*freePtr = true;
1512
				else
1513
					*freePtr = false;
1514
18301970
			}
1515
18358441
			if (mod->freearg != NULL)
1516
18165696
				mod->freearg(arg);
1517
		} else {
1518
			Error("Bad modifier: %s", tstr);
1519
			/* Try skipping to end of var... */
1520
			while (*tstr != endc && *tstr != '\0')
1521
				tstr++;
1522
			if (str != NULL && *freePtr)
1523
				free(str);
1524
			str = var_Error;
1525
			*freePtr = false;
1526
			break;
1527
		}
1528
18358441
		if (DEBUG(VAR) && str != NULL)
1529
			printf("Result is \"%s\"\n", str);
1530
18358441
	}
1531
15427588
	if (*tstr == '\0')
1532
		Parse_Error(PARSE_FATAL, "Unclosed variable specification");
1533
	else
1534
15427588
		tstr++;
1535
1536
15427588
	*pscan = tstr;
1537
15427588
	return str;
1538
15427588
}
1539
1540
char *
1541
Var_GetHead(char *s)
1542
{
1543
	return VarModify(s, VarHead, NULL);
1544
}
1545
1546
char *
1547
Var_GetTail(char *s)
1548
{
1549
	return VarModify(s, VarTail, NULL);
1550
}