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