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