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