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