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