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