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