Annotation of src/usr.bin/make/varmodifiers.c, Revision 1.49
1.49 ! espie 1: /* $OpenBSD: varmodifiers.c,v 1.48 2020/08/30 12:16:04 tb Exp $ */
1.1 espie 2: /* $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $ */
3:
4: /*
1.27 espie 5: * Copyright (c) 1999-2010 Marc Espie.
1.1 espie 6: *
7: * Extensive code changes for the OpenBSD project.
8: *
9: * Redistribution and use in source and binary forms, with or without
10: * modification, are permitted provided that the following conditions
11: * are met:
12: * 1. Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: *
18: * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
19: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
22: * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29: */
30: /*
31: * Copyright (c) 1988, 1989, 1990, 1993
32: * The Regents of the University of California. All rights reserved.
33: * Copyright (c) 1989 by Berkeley Softworks
34: * All rights reserved.
35: *
36: * This code is derived from software contributed to Berkeley by
37: * Adam de Boor.
38: *
39: * Redistribution and use in source and binary forms, with or without
40: * modification, are permitted provided that the following conditions
41: * are met:
42: * 1. Redistributions of source code must retain the above copyright
43: * notice, this list of conditions and the following disclaimer.
44: * 2. Redistributions in binary form must reproduce the above copyright
45: * notice, this list of conditions and the following disclaimer in the
46: * documentation and/or other materials provided with the distribution.
1.11 millert 47: * 3. Neither the name of the University nor the names of its contributors
1.1 espie 48: * may be used to endorse or promote products derived from this software
49: * without specific prior written permission.
50: *
51: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61: * SUCH DAMAGE.
62: */
63:
1.7 espie 64: /* VarModifiers_Apply is mostly a constituent function of Var_Parse, it
65: * is also called directly by Var_SubstVar. */
66:
67:
1.49 ! espie 68: #include <assert.h>
1.9 espie 69: #include <ctype.h>
70: #include <sys/types.h>
71: #include <regex.h>
72: #include <stddef.h>
73: #include <stdio.h>
74: #include <stdlib.h>
75: #include <string.h>
76: #include "defines.h"
1.1 espie 77: #include "buf.h"
1.9 espie 78: #include "var.h"
1.1 espie 79: #include "varmodifiers.h"
1.9 espie 80: #include "varname.h"
81: #include "targ.h"
82: #include "error.h"
83: #include "str.h"
84: #include "cmd_exec.h"
85: #include "memory.h"
86: #include "gnode.h"
87:
1.1 espie 88:
89: /* Var*Pattern flags */
90: #define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */
91: #define VAR_SUB_ONE 0x02 /* Apply substitution to one word */
1.7 espie 92: #define VAR_SUB_MATCHED 0x04 /* There was a match */
93: #define VAR_MATCH_START 0x08 /* Match at start of word */
1.1 espie 94: #define VAR_MATCH_END 0x10 /* Match at end of word */
95:
1.7 espie 96: /* Modifiers flags */
97: #define VAR_EQUAL 0x20
98: #define VAR_MAY_EQUAL 0x40
99: #define VAR_ADD_EQUAL 0x80
100: #define VAR_BANG_EQUAL 0x100
101:
1.1 espie 102: typedef struct {
1.49 ! espie 103: char *lbuffer; /* Left string to free */
1.22 espie 104: char *lhs; /* String to match */
105: size_t leftLen; /* Length of string */
106: char *rhs; /* Replacement string (w/ &'s removed) */
107: size_t rightLen; /* Length of replacement */
108: int flags;
1.1 espie 109: } VarPattern;
110:
1.9 espie 111: static bool VarHead(struct Name *, bool, Buffer, void *);
112: static bool VarTail(struct Name *, bool, Buffer, void *);
113: static bool VarSuffix(struct Name *, bool, Buffer, void *);
114: static bool VarRoot(struct Name *, bool, Buffer, void *);
115: static bool VarMatch(struct Name *, bool, Buffer, void *);
116: static bool VarSYSVMatch(struct Name *, bool, Buffer, void *);
117: static bool VarNoMatch(struct Name *, bool, Buffer, void *);
1.7 espie 118:
119:
120: static void VarREError(int, regex_t *, const char *);
1.9 espie 121: static bool VarRESubstitute(struct Name *, bool, Buffer, void *);
1.7 espie 122: static char *do_regex(const char *, const struct Name *, void *);
123:
1.1 espie 124: typedef struct {
1.22 espie 125: regex_t re;
126: int nsub;
127: regmatch_t *matches;
128: char *replace;
129: int flags;
1.1 espie 130: } VarREPattern;
131:
1.9 espie 132: static bool VarSubstitute(struct Name *, bool, Buffer, void *);
1.7 espie 133: static char *VarGetPattern(SymTable *, int, const char **, int, int,
134: size_t *, VarPattern *);
135: static char *VarQuote(const char *, const struct Name *, void *);
1.9 espie 136: static char *VarModify(char *, bool (*)(struct Name *, bool, Buffer, void *), void *);
1.7 espie 137:
1.9 espie 138: static void *check_empty(const char **, SymTable *, bool, int);
1.26 espie 139: static void *check_quote(const char **, SymTable *, bool, int);
1.7 espie 140: static char *do_upper(const char *, const struct Name *, void *);
141: static char *do_lower(const char *, const struct Name *, void *);
1.9 espie 142: static void *check_shcmd(const char **, SymTable *, bool, int);
1.7 espie 143: static char *do_shcmd(const char *, const struct Name *, void *);
1.9 espie 144: static void *get_stringarg(const char **, SymTable *, bool, int);
1.7 espie 145: static void free_stringarg(void *);
1.9 espie 146: static void *get_patternarg(const char **, SymTable *, bool, int);
147: static void *get_spatternarg(const char **, SymTable *, bool, int);
1.10 espie 148: static void *common_get_patternarg(const char **, SymTable *, bool, int, bool);
1.7 espie 149: static void free_patternarg(void *);
1.9 espie 150: static void *get_sysvpattern(const char **, SymTable *, bool, int);
1.7 espie 151:
152: static struct Name dummy;
153: static struct Name *dummy_arg = &dummy;
154:
155: static struct modifier {
1.22 espie 156: void * (*getarg)(const char **, SymTable *, bool, int);
157: char * (*apply)(const char *, const struct Name *, void *);
158: bool (*word_apply)(struct Name *, bool, Buffer, void *);
159: void (*freearg)(void *);
1.7 espie 160: } *choose_mod[256],
1.49 ! espie 161: match_mod = {get_stringarg, NULL, VarMatch, free_stringarg},
! 162: nomatch_mod = {get_stringarg, NULL, VarNoMatch, free_stringarg},
! 163: subst_mod = {get_spatternarg, NULL, VarSubstitute, free_patternarg},
! 164: resubst_mod = {get_patternarg, do_regex, NULL, free_patternarg},
! 165: quote_mod = {check_quote, VarQuote, NULL , free},
! 166: tail_mod = {check_empty, NULL, VarTail, NULL},
! 167: head_mod = {check_empty, NULL, VarHead, NULL},
! 168: suffix_mod = {check_empty, NULL, VarSuffix, NULL},
! 169: root_mod = {check_empty, NULL, VarRoot, NULL},
! 170: upper_mod = {check_empty, do_upper, NULL, NULL},
! 171: lower_mod = {check_empty, do_lower, NULL, NULL},
! 172: shcmd_mod = {check_shcmd, do_shcmd, NULL, NULL},
! 173: sysv_mod = {get_sysvpattern, NULL, VarSYSVMatch, free_patternarg}
1.7 espie 174: ;
1.1 espie 175:
1.7 espie 176: void
177: VarModifiers_Init()
1.1 espie 178: {
1.22 espie 179: choose_mod['M'] = &match_mod;
180: choose_mod['N'] = &nomatch_mod;
181: choose_mod['S'] = &subst_mod;
182: choose_mod['C'] = &resubst_mod;
183: choose_mod['Q'] = "e_mod;
184: choose_mod['T'] = &tail_mod;
185: choose_mod['H'] = &head_mod;
186: choose_mod['E'] = &suffix_mod;
187: choose_mod['R'] = &root_mod;
1.49 ! espie 188: choose_mod['U'] = &upper_mod;
! 189: choose_mod['L'] = &lower_mod;
! 190: choose_mod['s'] = &shcmd_mod;
1.1 espie 191: }
192:
1.7 espie 193: /* All modifiers handle addSpace (need to add a space before placing the
194: * next word into the buffer) and propagate it when necessary.
1.1 espie 195: */
196:
197: /*-
198: *-----------------------------------------------------------------------
199: * VarHead --
1.7 espie 200: * Remove the tail of the given word and add the result to the given
1.1 espie 201: * buffer.
202: *-----------------------------------------------------------------------
203: */
1.9 espie 204: static bool
1.13 espie 205: VarHead(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
1.1 espie 206: {
1.22 espie 207: const char *slash;
1.1 espie 208:
1.22 espie 209: slash = Str_rchri(word->s, word->e, '/');
210: if (slash != NULL) {
211: if (addSpace)
212: Buf_AddSpace(buf);
213: Buf_Addi(buf, word->s, slash);
214: } else {
215: /* If no directory part, give . (q.v. the POSIX standard). */
216: if (addSpace)
217: Buf_AddString(buf, " .");
218: else
219: Buf_AddChar(buf, '.');
220: }
221: return true;
1.1 espie 222: }
223:
224: /*-
225: *-----------------------------------------------------------------------
226: * VarTail --
1.7 espie 227: * Remove the head of the given word add the result to the given
1.1 espie 228: * buffer.
229: *-----------------------------------------------------------------------
230: */
1.9 espie 231: static bool
1.13 espie 232: VarTail(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
1.1 espie 233: {
1.22 espie 234: const char *slash;
1.1 espie 235:
1.22 espie 236: if (addSpace)
237: Buf_AddSpace(buf);
238: slash = Str_rchri(word->s, word->e, '/');
239: if (slash != NULL)
240: Buf_Addi(buf, slash+1, word->e);
241: else
242: Buf_Addi(buf, word->s, word->e);
243: return true;
1.1 espie 244: }
245:
246: /*-
247: *-----------------------------------------------------------------------
248: * VarSuffix --
1.7 espie 249: * Add the suffix of the given word to the given buffer.
1.1 espie 250: *-----------------------------------------------------------------------
251: */
1.9 espie 252: static bool
1.13 espie 253: VarSuffix(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
1.1 espie 254: {
1.22 espie 255: const char *dot;
1.1 espie 256:
1.22 espie 257: dot = Str_rchri(word->s, word->e, '.');
258: if (dot != NULL) {
259: if (addSpace)
260: Buf_AddSpace(buf);
261: Buf_Addi(buf, dot+1, word->e);
262: addSpace = true;
263: }
264: return addSpace;
1.1 espie 265: }
266:
267: /*-
268: *-----------------------------------------------------------------------
269: * VarRoot --
1.7 espie 270: * Remove the suffix of the given word and add the result to the
1.1 espie 271: * buffer.
272: *-----------------------------------------------------------------------
273: */
1.9 espie 274: static bool
1.13 espie 275: VarRoot(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
1.1 espie 276: {
1.22 espie 277: const char *dot;
1.1 espie 278:
1.22 espie 279: if (addSpace)
280: Buf_AddSpace(buf);
281: dot = Str_rchri(word->s, word->e, '.');
282: if (dot != NULL)
283: Buf_Addi(buf, word->s, dot);
284: else
285: Buf_Addi(buf, word->s, word->e);
286: return true;
1.1 espie 287: }
288:
289: /*-
290: *-----------------------------------------------------------------------
291: * VarMatch --
1.7 espie 292: * Add the word to the buffer if it matches the given pattern.
1.1 espie 293: *-----------------------------------------------------------------------
294: */
1.9 espie 295: static bool
1.49 ! espie 296: VarMatch(struct Name *word, bool addSpace, Buffer buf, void *pattern)
1.7 espie 297: {
1.41 espie 298: const char *pat = pattern;
1.9 espie 299:
1.22 espie 300: if (Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {
301: if (addSpace)
302: Buf_AddSpace(buf);
303: Buf_Addi(buf, word->s, word->e);
304: return true;
305: } else
306: return addSpace;
1.7 espie 307: }
308:
309: /*-
310: *-----------------------------------------------------------------------
311: * VarNoMatch --
312: * Add the word to the buffer if it doesn't match the given pattern.
313: *-----------------------------------------------------------------------
314: */
1.9 espie 315: static bool
1.49 ! espie 316: VarNoMatch(struct Name *word, bool addSpace, Buffer buf, void *pattern)
1.7 espie 317: {
1.41 espie 318: const char *pat = pattern;
1.9 espie 319:
1.22 espie 320: if (!Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {
321: if (addSpace)
322: Buf_AddSpace(buf);
323: Buf_Addi(buf, word->s, word->e);
324: return true;
325: } else
326: return addSpace;
1.7 espie 327: }
328:
1.1 espie 329: /*-
330: *-----------------------------------------------------------------------
331: * VarSYSVMatch --
1.7 espie 332: * Add the word to the buffer if it matches the given pattern.
333: * Used to implement the System V % modifiers.
1.1 espie 334: *-----------------------------------------------------------------------
335: */
1.9 espie 336: static bool
1.22 espie 337: VarSYSVMatch(struct Name *word, bool addSpace, Buffer buf, void *patp)
1.7 espie 338: {
1.22 espie 339: size_t len;
340: const char *ptr;
1.40 espie 341: VarPattern *pat = patp;
1.1 espie 342:
1.22 espie 343: if (*word->s != '\0') {
344: if (addSpace)
345: Buf_AddSpace(buf);
346: if ((ptr = Str_SYSVMatch(word->s, pat->lhs, &len)) != NULL)
347: Str_SYSVSubst(buf, pat->rhs, ptr, len);
348: else
349: Buf_Addi(buf, word->s, word->e);
350: return true;
351: } else
352: return addSpace;
1.1 espie 353: }
354:
1.7 espie 355: void *
1.26 espie 356: get_sysvpattern(const char **p, SymTable *ctxt UNUSED, bool err, int endc)
1.1 espie 357: {
1.22 espie 358: VarPattern *pattern;
359: const char *cp, *cp2;
1.46 espie 360: BUFFER buf, buf2;
1.22 espie 361: int cnt = 0;
362: char startc = endc == ')' ? '(' : '{';
1.46 espie 363:
364: Buf_Init(&buf, 0);
1.22 espie 365: for (cp = *p;; cp++) {
366: if (*cp == '=' && cnt == 0)
367: break;
1.46 espie 368: if (*cp == '\0') {
369: Buf_Destroy(&buf);
1.22 espie 370: return NULL;
1.46 espie 371: }
1.22 espie 372: if (*cp == startc)
373: cnt++;
374: else if (*cp == endc) {
375: cnt--;
1.46 espie 376: if (cnt < 0) {
377: Buf_Destroy(&buf);
1.22 espie 378: return NULL;
1.46 espie 379: }
380: } else if (*cp == '$') {
381: if (cp[1] == '$')
382: cp++;
383: else {
384: size_t len;
385: (void)Var_ParseBuffer(&buf, cp, ctxt, err,
386: &len);
387: cp += len - 1;
388: continue;
389: }
1.22 espie 390: }
1.46 espie 391: Buf_AddChar(&buf, *cp);
1.7 espie 392: }
1.46 espie 393:
394: Buf_Init(&buf2, 0);
1.22 espie 395: for (cp2 = cp+1;; cp2++) {
1.30 espie 396: if (((*cp2 == ':' && cp2[1] != endc) || *cp2 == endc) &&
397: cnt == 0)
1.22 espie 398: break;
1.26 espie 399: if (*cp2 == '\0') {
400: Buf_Destroy(&buf);
1.46 espie 401: Buf_Destroy(&buf2);
1.22 espie 402: return NULL;
1.26 espie 403: }
1.22 espie 404: if (*cp2 == startc)
405: cnt++;
406: else if (*cp2 == endc) {
407: cnt--;
1.26 espie 408: if (cnt < 0) {
409: Buf_Destroy(&buf);
1.46 espie 410: Buf_Destroy(&buf2);
1.22 espie 411: return NULL;
1.26 espie 412: }
413: } else if (*cp2 == '$') {
414: if (cp2[1] == '$')
415: cp2++;
416: else {
417: size_t len;
1.46 espie 418: (void)Var_ParseBuffer(&buf2, cp2, ctxt, err,
1.26 espie 419: &len);
420: cp2 += len - 1;
421: continue;
422: }
1.22 espie 423: }
1.46 espie 424: Buf_AddChar(&buf2, *cp2);
1.7 espie 425: }
1.23 espie 426:
1.42 deraadt 427: pattern = emalloc(sizeof(VarPattern));
1.46 espie 428: pattern->lbuffer = pattern->lhs = Buf_Retrieve(&buf);
429: pattern->leftLen = Buf_Size(&buf);
430: pattern->rhs = Buf_Retrieve(&buf2);
431: pattern->rightLen = Buf_Size(&buf2);
1.22 espie 432: pattern->flags = 0;
433: *p = cp2;
434: return pattern;
1.1 espie 435: }
436:
437:
438: /*-
439: *-----------------------------------------------------------------------
440: * VarSubstitute --
1.7 espie 441: * Perform a string-substitution on the given word, Adding the
442: * result to the given buffer.
1.1 espie 443: *-----------------------------------------------------------------------
444: */
1.9 espie 445: static bool
1.23 espie 446: VarSubstitute(struct Name *word, bool addSpace, Buffer buf,
1.13 espie 447: void *patternp) /* Pattern for substitution */
1.7 espie 448: {
449: size_t wordLen; /* Length of word */
450: const char *cp; /* General pointer */
1.40 espie 451: VarPattern *pattern = patternp;
1.1 espie 452:
1.7 espie 453: wordLen = word->e - word->s;
1.1 espie 454: if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) !=
455: (VAR_SUB_ONE|VAR_SUB_MATCHED)) {
1.7 espie 456: /* Still substituting -- break it down into simple anchored cases
457: * and if none of them fits, perform the general substitution case. */
1.1 espie 458: if ((pattern->flags & VAR_MATCH_START) &&
1.7 espie 459: (strncmp(word->s, pattern->lhs, pattern->leftLen) == 0)) {
460: /* Anchored at start and beginning of word matches pattern. */
1.1 espie 461: if ((pattern->flags & VAR_MATCH_END) &&
462: (wordLen == pattern->leftLen)) {
1.7 espie 463: /* Also anchored at end and matches to the end (word
1.1 espie 464: * is same length as pattern) add space and rhs only
1.7 espie 465: * if rhs is non-null. */
1.1 espie 466: if (pattern->rightLen != 0) {
467: if (addSpace)
468: Buf_AddSpace(buf);
1.9 espie 469: addSpace = true;
1.7 espie 470: Buf_AddChars(buf, pattern->rightLen,
471: pattern->rhs);
1.1 espie 472: }
473: pattern->flags |= VAR_SUB_MATCHED;
474: } else if (pattern->flags & VAR_MATCH_END) {
1.7 espie 475: /* Doesn't match to end -- copy word wholesale. */
1.1 espie 476: goto nosub;
477: } else {
1.7 espie 478: /* Matches at start but need to copy in
479: * trailing characters. */
1.1 espie 480: if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
481: if (addSpace)
482: Buf_AddSpace(buf);
1.9 espie 483: addSpace = true;
1.1 espie 484: }
485: Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
486: Buf_AddChars(buf, wordLen - pattern->leftLen,
1.7 espie 487: word->s + pattern->leftLen);
1.1 espie 488: pattern->flags |= VAR_SUB_MATCHED;
489: }
490: } else if (pattern->flags & VAR_MATCH_START) {
1.7 espie 491: /* Had to match at start of word and didn't -- copy whole word. */
1.1 espie 492: goto nosub;
493: } else if (pattern->flags & VAR_MATCH_END) {
1.7 espie 494: /* Anchored at end, Find only place match could occur (leftLen
1.1 espie 495: * characters from the end of the word) and see if it does. Note
496: * that because the $ will be left at the end of the lhs, we have
1.7 espie 497: * to use strncmp. */
498: cp = word->s + (wordLen - pattern->leftLen);
499: if (cp >= word->s &&
500: strncmp(cp, pattern->lhs, pattern->leftLen) == 0) {
501: /* Match found. If we will place characters in the buffer,
1.1 espie 502: * add a space before hand as indicated by addSpace, then
503: * stuff in the initial, unmatched part of the word followed
1.7 espie 504: * by the right-hand-side. */
505: if (((cp - word->s) + pattern->rightLen) != 0) {
1.1 espie 506: if (addSpace)
507: Buf_AddSpace(buf);
1.9 espie 508: addSpace = true;
1.1 espie 509: }
1.9 espie 510: Buf_Addi(buf, word->s, cp);
1.1 espie 511: Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
512: pattern->flags |= VAR_SUB_MATCHED;
513: } else {
1.7 espie 514: /* Had to match at end and didn't. Copy entire word. */
1.1 espie 515: goto nosub;
516: }
517: } else {
1.7 espie 518: /* Pattern is unanchored: search for the pattern in the word using
519: * strstr, copying unmatched portions and the
1.1 espie 520: * right-hand-side for each match found, handling non-global
521: * substitutions correctly, etc. When the loop is done, any
522: * remaining part of the word (word and wordLen are adjusted
523: * accordingly through the loop) is copied straight into the
524: * buffer.
1.9 espie 525: * addSpace is set to false as soon as a space is added to the
1.7 espie 526: * buffer. */
1.9 espie 527: bool done;
1.1 espie 528: size_t origSize;
529:
1.9 espie 530: done = false;
1.1 espie 531: origSize = Buf_Size(buf);
532: while (!done) {
1.7 espie 533: cp = strstr(word->s, pattern->lhs);
534: if (cp != NULL) {
535: if (addSpace && (cp - word->s) + pattern->rightLen != 0){
1.1 espie 536: Buf_AddSpace(buf);
1.9 espie 537: addSpace = false;
1.1 espie 538: }
1.9 espie 539: Buf_Addi(buf, word->s, cp);
1.1 espie 540: Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
1.7 espie 541: wordLen -= (cp - word->s) + pattern->leftLen;
542: word->s = cp + pattern->leftLen;
543: if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0)
1.9 espie 544: done = true;
1.1 espie 545: pattern->flags |= VAR_SUB_MATCHED;
1.7 espie 546: } else
1.9 espie 547: done = true;
1.1 espie 548: }
549: if (wordLen != 0) {
550: if (addSpace)
551: Buf_AddSpace(buf);
1.7 espie 552: Buf_AddChars(buf, wordLen, word->s);
1.1 espie 553: }
1.7 espie 554: /* If added characters to the buffer, need to add a space
1.1 espie 555: * before we add any more. If we didn't add any, just return
1.7 espie 556: * the previous value of addSpace. */
557: return Buf_Size(buf) != origSize || addSpace;
1.1 espie 558: }
1.7 espie 559: return addSpace;
1.1 espie 560: }
561: nosub:
562: if (addSpace)
563: Buf_AddSpace(buf);
1.7 espie 564: Buf_AddChars(buf, wordLen, word->s);
1.9 espie 565: return true;
1.1 espie 566: }
567:
568: /*-
569: *-----------------------------------------------------------------------
570: * VarREError --
571: * Print the error caused by a regcomp or regexec call.
572: *-----------------------------------------------------------------------
573: */
574: static void
1.13 espie 575: VarREError(int err, regex_t *pat, const char *str)
1.1 espie 576: {
1.22 espie 577: char *errbuf;
578: int errlen;
1.1 espie 579:
1.22 espie 580: errlen = regerror(err, pat, 0, 0);
581: errbuf = emalloc(errlen);
582: regerror(err, pat, errbuf, errlen);
583: Error("%s: %s", str, errbuf);
584: free(errbuf);
1.1 espie 585: }
586:
587: /*-
588: *-----------------------------------------------------------------------
589: * VarRESubstitute --
590: * Perform a regex substitution on the given word, placing the
591: * result in the passed buffer.
592: *-----------------------------------------------------------------------
593: */
1.9 espie 594: static bool
1.13 espie 595: VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp)
1.7 espie 596: {
1.22 espie 597: VarREPattern *pat;
598: int xrv;
599: const char *wp;
600: char *rp;
601: int added;
1.1 espie 602:
603: #define MAYBE_ADD_SPACE() \
1.7 espie 604: if (addSpace && !added) \
1.22 espie 605: Buf_AddSpace(buf); \
1.1 espie 606: added = 1
607:
1.22 espie 608: added = 0;
609: wp = word->s;
610: pat = patternp;
611:
612: if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
613: (VAR_SUB_ONE|VAR_SUB_MATCHED))
614: xrv = REG_NOMATCH;
615: else {
616: tryagain:
617: xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0);
1.1 espie 618: }
619:
1.22 espie 620: switch (xrv) {
621: case 0:
622: pat->flags |= VAR_SUB_MATCHED;
623: if (pat->matches[0].rm_so > 0) {
624: MAYBE_ADD_SPACE();
625: Buf_AddChars(buf, pat->matches[0].rm_so, wp);
1.1 espie 626: }
627:
1.22 espie 628: for (rp = pat->replace; *rp; rp++) {
629: if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
630: MAYBE_ADD_SPACE();
631: Buf_AddChar(buf,rp[1]);
632: rp++;
633: }
1.23 espie 634: else if (*rp == '&' ||
1.35 espie 635: (*rp == '\\' && ISDIGIT(rp[1]))) {
1.22 espie 636: int n;
637: const char *subbuf;
638: int sublen;
639: char errstr[3];
640:
641: if (*rp == '&') {
642: n = 0;
643: errstr[0] = '&';
644: errstr[1] = '\0';
645: } else {
646: n = rp[1] - '0';
647: errstr[0] = '\\';
648: errstr[1] = rp[1];
649: errstr[2] = '\0';
650: rp++;
651: }
652:
1.45 espie 653: if (n >= pat->nsub) {
1.23 espie 654: Error("No subexpression %s",
1.22 espie 655: &errstr[0]);
656: subbuf = "";
657: sublen = 0;
658: } else if (pat->matches[n].rm_so == -1 &&
659: pat->matches[n].rm_eo == -1) {
1.23 espie 660: Error("No match for subexpression %s",
1.22 espie 661: &errstr[0]);
662: subbuf = "";
663: sublen = 0;
664: } else {
665: subbuf = wp + pat->matches[n].rm_so;
1.23 espie 666: sublen = pat->matches[n].rm_eo -
1.22 espie 667: pat->matches[n].rm_so;
668: }
669:
670: if (sublen > 0) {
671: MAYBE_ADD_SPACE();
672: Buf_AddChars(buf, sublen, subbuf);
673: }
674: } else {
675: MAYBE_ADD_SPACE();
676: Buf_AddChar(buf, *rp);
677: }
678: }
679: wp += pat->matches[0].rm_eo;
1.32 espie 680: if (pat->flags & VAR_SUB_GLOBAL) {
681: /* like most modern tools, empty string matches
682: * should advance one char at a time...
683: */
684: if (pat->matches[0].rm_eo == 0) {
685: if (*wp) {
686: MAYBE_ADD_SPACE();
687: Buf_AddChar(buf, *wp++);
688: } else
689: break;
690: }
1.22 espie 691: goto tryagain;
1.32 espie 692: }
1.22 espie 693: if (*wp) {
694: MAYBE_ADD_SPACE();
695: Buf_AddString(buf, wp);
1.1 espie 696: }
1.22 espie 697: break;
698: default:
699: VarREError(xrv, &pat->re, "Unexpected regex error");
1.25 deraadt 700: /* FALLTHROUGH */
1.22 espie 701: case REG_NOMATCH:
702: if (*wp) {
703: MAYBE_ADD_SPACE();
704: Buf_AddString(buf, wp);
1.1 espie 705: }
1.22 espie 706: break;
1.1 espie 707: }
1.22 espie 708: return addSpace||added;
1.1 espie 709: }
710:
711: /*-
712: *-----------------------------------------------------------------------
713: * VarModify --
714: * Modify each of the words of the passed string using the given
1.49 ! espie 715: * function. Used to implement most modifiers.
1.1 espie 716: *
717: * Results:
718: * A string of all the words modified appropriately.
719: *-----------------------------------------------------------------------
720: */
721: static char *
1.13 espie 722: VarModify(char *str, /* String whose words should be trimmed */
1.7 espie 723: /* Function to use to modify them */
1.23 espie 724: bool (*modProc)(struct Name *, bool, Buffer, void *),
1.13 espie 725: void *datum) /* Datum to pass it */
1.1 espie 726: {
1.22 espie 727: BUFFER buf; /* Buffer for the new string */
728: bool addSpace; /* true if need to add a space to the
729: * buffer before adding the trimmed
730: * word */
731: struct Name word;
732:
733: Buf_Init(&buf, 0);
734: addSpace = false;
735:
736: word.e = str;
737:
738: while ((word.s = iterate_words(&word.e)) != NULL) {
739: char termc;
740:
741: termc = *word.e;
742: *((char *)(word.e)) = '\0';
743: addSpace = (*modProc)(&word, addSpace, &buf, datum);
744: *((char *)(word.e)) = termc;
745: }
746: return Buf_Retrieve(&buf);
1.1 espie 747: }
748:
749: /*-
750: *-----------------------------------------------------------------------
751: * VarGetPattern --
752: * Pass through the tstr looking for 1) escaped delimiters,
753: * '$'s and backslashes (place the escaped character in
754: * uninterpreted) and 2) unescaped $'s that aren't before
755: * the delimiter (expand the variable substitution).
756: * Return the expanded string or NULL if the delimiter was missing
757: * If pattern is specified, handle escaped ampersands, and replace
758: * unescaped ampersands with the lhs of the pattern.
759: *
760: * Results:
761: * A string of all the words modified appropriately.
762: * If length is specified, return the string length of the buffer
763: *-----------------------------------------------------------------------
764: */
765: static char *
1.23 espie 766: VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1,
1.13 espie 767: int delim2, size_t *length, VarPattern *pattern)
1.1 espie 768: {
1.22 espie 769: const char *cp;
770: char *result;
771: BUFFER buf;
772: size_t junk;
773:
774: Buf_Init(&buf, 0);
775: if (length == NULL)
776: length = &junk;
1.1 espie 777:
1.7 espie 778: #define IS_A_MATCH(cp, delim1, delim2) \
1.22 espie 779: (cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \
780: cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&')))
1.1 espie 781:
1.22 espie 782: /*
783: * Skim through until the matching delimiter is found;
784: * pick up variable substitutions on the way. Also allow
785: * backslashes to quote the delimiter, $, and \, but don't
786: * touch other backslashes.
787: */
788: for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) {
789: if (IS_A_MATCH(cp, delim1, delim2)) {
790: Buf_AddChar(&buf, cp[1]);
791: cp++;
792: } else if (*cp == '$') {
793: /* Allowed at end of pattern */
794: if (cp[1] == delim1 || cp[1] == delim2)
795: Buf_AddChar(&buf, *cp);
796: else {
797: size_t len;
798:
799: /* If unescaped dollar sign not before the
800: * delimiter, assume it's a variable
801: * substitution and recurse. */
1.23 espie 802: (void)Var_ParseBuffer(&buf, cp, ctxt, err,
1.22 espie 803: &len);
804: cp += len - 1;
805: }
806: } else if (pattern && *cp == '&')
807: Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
808: else
809: Buf_AddChar(&buf, *cp);
810: }
1.1 espie 811:
1.22 espie 812: *length = Buf_Size(&buf);
813: result = Buf_Retrieve(&buf);
1.7 espie 814:
1.22 espie 815: if (*cp != delim1 && *cp != delim2) {
816: *tstr = cp;
817: *length = 0;
818: free(result);
819: return NULL;
820: }
821: else {
822: *tstr = ++cp;
823: return result;
824: }
1.7 espie 825: }
826:
827: /*-
828: *-----------------------------------------------------------------------
829: * VarQuote --
830: * Quote shell meta-characters in the string
831: *
832: * Results:
833: * The quoted string
834: *-----------------------------------------------------------------------
835: */
836: static char *
1.26 espie 837: VarQuote(const char *str, const struct Name *n UNUSED, void *islistp)
1.7 espie 838: {
1.40 espie 839: int *p = islistp;
840: int islist = *p;
1.7 espie 841:
1.22 espie 842: BUFFER buf;
843: /* This should cover most shells :-( */
844: static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1.26 espie 845: char *rep = meta;
846: if (islist)
847: rep += 3;
1.22 espie 848:
849: Buf_Init(&buf, MAKE_BSIZE);
850: for (; *str; str++) {
1.26 espie 851: if (strchr(rep, *str) != NULL)
1.22 espie 852: Buf_AddChar(&buf, '\\');
853: Buf_AddChar(&buf, *str);
854: }
855: return Buf_Retrieve(&buf);
1.7 espie 856: }
857:
858: static void *
1.13 espie 859: check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1.7 espie 860: {
1.22 espie 861: dummy_arg->s = NULL;
862: if ((*p)[1] == endc || (*p)[1] == ':') {
863: (*p)++;
864: return dummy_arg;
1.26 espie 865: } else
866: return NULL;
867: }
868:
869: static void *
870: check_quote(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
871: {
872: int *qargs = emalloc(sizeof(int));
873: *qargs = 0;
874: if ((*p)[1] == 'L') {
875: *qargs = 1;
876: (*p)++;
877: }
878: if ((*p)[1] == endc || (*p)[1] == ':') {
879: (*p)++;
880: return qargs;
1.39 espie 881: } else {
882: free(qargs);
1.22 espie 883: return NULL;
1.39 espie 884: }
1.7 espie 885: }
886:
887: static void *
1.13 espie 888: check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1.7 espie 889: {
1.22 espie 890: if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) {
891: (*p)+=2;
892: return dummy_arg;
893: } else
894: return NULL;
1.7 espie 895: }
896:
897:
898: static char *
1.13 espie 899: do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1.7 espie 900: {
1.22 espie 901: char *err;
902: char *t;
1.7 espie 903:
1.22 espie 904: t = Cmd_Exec(s, &err);
905: if (err)
906: Error(err, s);
907: return t;
1.7 espie 908: }
909:
910: static void *
1.13 espie 911: get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1.7 espie 912: {
1.22 espie 913: const char *cp;
914: char *s;
1.7 espie 915:
1.22 espie 916: for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) {
917: if (*cp == '\\') {
918: if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\')
919: cp++;
920: } else if (*cp == '\0')
921: return NULL;
922: }
923: s = escape_dupi(*p+1, cp, ":)}");
924: *p = cp;
925: return s;
1.7 espie 926: }
927:
928: static void
1.13 espie 929: free_stringarg(void *arg)
1.7 espie 930: {
1.22 espie 931: free(arg);
1.7 espie 932: }
933:
934: static char *
1.13 espie 935: do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1.7 espie 936: {
1.22 espie 937: size_t len, i;
938: char *t;
1.7 espie 939:
1.22 espie 940: len = strlen(s);
941: t = emalloc(len+1);
942: for (i = 0; i < len; i++)
1.35 espie 943: t[i] = TOUPPER(s[i]);
1.22 espie 944: t[len] = '\0';
945: return t;
1.7 espie 946: }
947:
948: static char *
1.13 espie 949: do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1.7 espie 950: {
1.22 espie 951: size_t len, i;
952: char *t;
1.7 espie 953:
1.22 espie 954: len = strlen(s);
955: t = emalloc(len+1);
956: for (i = 0; i < len; i++)
1.35 espie 957: t[i] = TOLOWER(s[i]);
1.22 espie 958: t[len] = '\0';
959: return t;
1.1 espie 960: }
961:
1.10 espie 962: static void *
1.13 espie 963: get_patternarg(const char **p, SymTable *ctxt, bool err, int endc)
1.10 espie 964: {
1.22 espie 965: return common_get_patternarg(p, ctxt, err, endc, false);
1.10 espie 966: }
967:
1.7 espie 968: /* Extract anchors */
969: static void *
1.13 espie 970: get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc)
1.7 espie 971: {
1.48 tb 972: return common_get_patternarg(p, ctxt, err, endc, true);
1.7 espie 973: }
974:
975: static void *
1.23 espie 976: common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc,
1.13 espie 977: bool dosubst)
1.7 espie 978: {
1.22 espie 979: VarPattern *pattern;
980: char delim;
981: const char *s;
982:
1.42 deraadt 983: pattern = emalloc(sizeof(VarPattern));
1.22 espie 984: pattern->flags = 0;
985: s = *p;
1.1 espie 986:
1.22 espie 987: delim = s[1];
988: if (delim == '\0')
989: return NULL;
990: s += 2;
1.1 espie 991:
1.22 espie 992: pattern->rhs = NULL;
1.23 espie 993: pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim,
1.22 espie 994: &pattern->leftLen, NULL);
995: pattern->lbuffer = pattern->lhs;
996: if (pattern->lhs != NULL) {
1.48 tb 997: if (dosubst && pattern->leftLen > 0) {
998: if (pattern->lhs[pattern->leftLen-1] == '$') {
999: pattern->leftLen--;
1000: pattern->flags |= VAR_MATCH_END;
1001: }
1002: if (pattern->lhs[0] == '^') {
1003: pattern->lhs++;
1004: pattern->leftLen--;
1005: pattern->flags |= VAR_MATCH_START;
1006: }
1007: }
1.22 espie 1008: pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim,
1009: &pattern->rightLen, dosubst ? pattern: NULL);
1010: if (pattern->rhs != NULL) {
1011: /* Check for global substitution. If 'g' after the
1012: * final delimiter, substitution is global and is
1013: * marked that way. */
1014: for (;; s++) {
1015: switch (*s) {
1016: case 'g':
1017: pattern->flags |= VAR_SUB_GLOBAL;
1018: continue;
1019: case '1':
1020: pattern->flags |= VAR_SUB_ONE;
1021: continue;
1022: }
1023: break;
1024: }
1025: if (*s == endc || *s == ':') {
1026: *p = s;
1027: return pattern;
1028: }
1.1 espie 1029: }
1.7 espie 1030: }
1.22 espie 1031: free_patternarg(pattern);
1032: return NULL;
1.7 espie 1033: }
1034:
1035: static void
1.13 espie 1036: free_patternarg(void *p)
1.7 espie 1037: {
1.40 espie 1038: VarPattern *vp = p;
1.8 espie 1039:
1.22 espie 1040: free(vp->lbuffer);
1041: free(vp->rhs);
1042: free(vp);
1.1 espie 1043: }
1044:
1045: static char *
1.13 espie 1046: do_regex(const char *s, const struct Name *n UNUSED, void *arg)
1.7 espie 1047: {
1.22 espie 1048: VarREPattern p2;
1.40 espie 1049: VarPattern *p = arg;
1.22 espie 1050: int error;
1051: char *result;
1052:
1053: error = regcomp(&p2.re, p->lhs, REG_EXTENDED);
1054: if (error) {
1055: VarREError(error, &p2.re, "RE substitution error");
1056: return var_Error;
1057: }
1058: p2.nsub = p2.re.re_nsub + 1;
1059: p2.replace = p->rhs;
1060: p2.flags = p->flags;
1061: if (p2.nsub < 1)
1062: p2.nsub = 1;
1063: if (p2.nsub > 10)
1064: p2.nsub = 10;
1.38 espie 1065: p2.matches = ereallocarray(NULL, p2.nsub, sizeof(regmatch_t));
1.22 espie 1066: result = VarModify((char *)s, VarRESubstitute, &p2);
1067: regfree(&p2.re);
1068: free(p2.matches);
1069: return result;
1.7 espie 1070: }
1071:
1072: char *
1.23 espie 1073: VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt,
1.18 espie 1074: bool err, bool *freePtr, const char **pscan, int paren)
1.1 espie 1075: {
1.22 espie 1076: const char *tstr;
1077: char endc = paren == '(' ? ')' : '}';
1078: const char *start = *pscan;
1079:
1080: tstr = start;
1081: /*
1082: * Now we need to apply any modifiers the user wants applied.
1083: * These are:
1084: * :M<pattern> words which match the given <pattern>.
1085: * <pattern> is of the standard file
1086: * wildcarding form.
1087: * :S<d><pat1><d><pat2><d>[g]
1088: * Substitute <pat2> for <pat1> in the
1089: * value
1090: * :C<d><pat1><d><pat2><d>[g]
1091: * Substitute <pat2> for regex <pat1> in
1092: * the value
1093: * :H Substitute the head of each word
1094: * :T Substitute the tail of each word
1095: * :E Substitute the extension (minus '.') of
1096: * each word
1097: * :R Substitute the root of each word
1098: * (pathname minus the suffix).
1099: * :lhs=rhs Like :S, but the rhs goes to the end of
1100: * the invocation.
1101: */
1.24 espie 1102:
1.22 espie 1103: while (*tstr != endc && *tstr != '\0') {
1104: struct modifier *mod;
1105: void *arg;
1106: char *newStr;
1107:
1108: tstr++;
1.44 tb 1109: if (DEBUG(VAR)) {
1110: if (str != NULL)
1111: printf("Applying :%c to \"%s\"\n", *tstr, str);
1112: else
1113: printf("Applying :%c\n", *tstr);
1114: }
1.22 espie 1115:
1.34 deraadt 1116: mod = choose_mod[(unsigned char)*tstr];
1.22 espie 1117: arg = NULL;
1118:
1.49 ! espie 1119: if (mod != NULL)
1.22 espie 1120: arg = mod->getarg(&tstr, ctxt, err, endc);
1.49 ! espie 1121: if (arg == NULL) {
1.22 espie 1122: mod = &sysv_mod;
1123: arg = mod->getarg(&tstr, ctxt, err, endc);
1124: }
1125: if (arg != NULL) {
1.49 ! espie 1126: if (str != NULL) {
1.22 espie 1127: if (mod->word_apply != NULL) {
1.23 espie 1128: newStr = VarModify(str,
1.22 espie 1129: mod->word_apply, arg);
1.49 ! espie 1130: assert(mod->apply == NULL);
1.22 espie 1131: } else
1132: newStr = mod->apply(str, name, arg);
1133: if (*freePtr)
1134: free(str);
1135: str = newStr;
1136: if (str != var_Error)
1137: *freePtr = true;
1138: else
1139: *freePtr = false;
1140: }
1141: if (mod->freearg != NULL)
1142: mod->freearg(arg);
1143: } else {
1.31 espie 1144: Error("Bad modifier: %s", tstr);
1.22 espie 1145: /* Try skipping to end of var... */
1.47 bluhm 1146: while (*tstr != endc && *tstr != '\0')
1.22 espie 1147: tstr++;
1148: if (str != NULL && *freePtr)
1149: free(str);
1150: str = var_Error;
1151: *freePtr = false;
1152: break;
1153: }
1.44 tb 1154: if (DEBUG(VAR) && str != NULL)
1.22 espie 1155: printf("Result is \"%s\"\n", str);
1.7 espie 1156: }
1.22 espie 1157: if (*tstr == '\0')
1.33 espie 1158: Parse_Error(PARSE_FATAL, "Unclosed variable specification");
1.22 espie 1159: else
1.7 espie 1160: tstr++;
1161:
1.22 espie 1162: *pscan = tstr;
1163: return str;
1.1 espie 1164: }
1.7 espie 1165:
1.1 espie 1166: char *
1.13 espie 1167: Var_GetHead(char *s)
1.1 espie 1168: {
1.22 espie 1169: return VarModify(s, VarHead, NULL);
1.1 espie 1170: }
1171:
1172: char *
1.13 espie 1173: Var_GetTail(char *s)
1.1 espie 1174: {
1.22 espie 1175: return VarModify(s, VarTail, NULL);
1.1 espie 1176: }