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'] = "e_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