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