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