Annotation of src/usr.bin/make/varmodifiers.c, Revision 1.6
1.6 ! espie 1: /* $OpenBSD: varmodifiers.c,v 1.5 2000/09/14 13:35:38 espie Exp $ */
1.1 espie 2: /* $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $ */
3:
4: /*
5: * Copyright (c) 1999 Marc 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.
47: * 3. All advertising materials mentioning features or use of this software
48: * must display the following acknowledgement:
49: * This product includes software developed by the University of
50: * California, Berkeley and its contributors.
51: * 4. Neither the name of the University nor the names of its contributors
52: * may be used to endorse or promote products derived from this software
53: * without specific prior written permission.
54: *
55: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65: * SUCH DAMAGE.
66: */
67:
68: /* VarModifiers_Apply is mostly a constituent function of Var_Parse. */
69:
70: #include <ctype.h>
71: #ifndef MAKE_BOOTSTRAP
72: #include <sys/types.h>
73: #include <regex.h>
74: #endif
75: #include "make.h"
76: #include "buf.h"
77: #include "varmodifiers.h"
78:
79: /* Var*Pattern flags */
80: #define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */
81: #define VAR_SUB_ONE 0x02 /* Apply substitution to one word */
82: #define VAR_SUB_MATCHED 0x04 /* There was a match */
83: #define VAR_MATCH_START 0x08 /* Match at start of word */
84: #define VAR_MATCH_END 0x10 /* Match at end of word */
85:
86: typedef struct {
87: char *lhs; /* String to match */
88: size_t leftLen; /* Length of string */
89: char *rhs; /* Replacement string (w/ &'s removed) */
90: size_t rightLen; /* Length of replacement */
91: int flags;
92: } VarPattern;
93:
94: #ifndef MAKE_BOOTSTRAP
95: typedef struct {
96: regex_t re;
97: int nsub;
98: regmatch_t *matches;
99: char *replace;
100: int flags;
101: } VarREPattern;
102: #endif
103:
104: static Boolean VarHead __P((const char *, Boolean, Buffer, void *));
105: static Boolean VarTail __P((const char *, Boolean, Buffer, void *));
106: static Boolean VarSuffix __P((const char *, Boolean, Buffer, void *));
107: static Boolean VarRoot __P((const char *, Boolean, Buffer, void *));
108: static Boolean VarMatch __P((const char *, Boolean, Buffer, void *));
109: #ifdef SYSVVARSUB
110: static Boolean VarSYSVMatch __P((const char *, Boolean, Buffer, void *));
111: #endif
112: static Boolean VarNoMatch __P((const char *, Boolean, Buffer, void *));
113: #ifndef MAKE_BOOTSTRAP
114: static void VarREError __P((int, regex_t *, const char *));
115: static Boolean VarRESubstitute __P((const char *, Boolean, Buffer, void *));
116: #endif
117: static Boolean VarSubstitute __P((const char *, Boolean, Buffer, void *));
118: static char *VarGetPattern __P((SymTable *, int, char **, int, int *, size_t *,
119: VarPattern *));
120: static char *VarQuote __P((const char *));
1.5 espie 121: static char *VarModify __P((char *, Boolean (*)(const char *, Boolean, Buffer, void *), void *));
1.1 espie 122: static Boolean VarUppercase __P((const char *, Boolean, Buffer, void *));
123: static Boolean VarLowercase __P((const char *, Boolean, Buffer, void *));
124:
125: /*-
126: *-----------------------------------------------------------------------
127: * VarUppercase --
128: * Place the Upper cased word in the given buffer.
129: *
130: * Results:
131: * TRUE if characters were added to the buffer (a space needs to be
132: * added to the buffer before the next word).
133: *
134: * Side Effects:
135: * The word is added to the buffer.
136: *
137: *-----------------------------------------------------------------------
138: */
139: static Boolean
140: VarUppercase(word, addSpace, buf, dummy)
1.6 ! espie 141: const char *word; /* Word to Upper Case */
! 142: Boolean addSpace; /* True if need to add a space to the buffer
1.1 espie 143: * before sticking in the head */
1.6 ! espie 144: Buffer buf; /* Buffer in which to store it */
! 145: void *dummy UNUSED;
1.1 espie 146: {
147: size_t len = strlen(word);
148:
149: if (addSpace)
150: Buf_AddSpace(buf);
151: while (len--)
152: Buf_AddChar(buf, toupper(*word++));
153: return TRUE;
154: }
155:
156: /*-
157: *-----------------------------------------------------------------------
158: * VarLowercase --
159: * Place the Lower cased word in the given buffer.
160: *
161: * Results:
162: * TRUE if characters were added to the buffer (a space needs to be
163: * added to the buffer before the next word).
164: *
165: * Side Effects:
166: * The word is added to the buffer.
167: *
168: *-----------------------------------------------------------------------
169: */
170: static Boolean
171: VarLowercase(word, addSpace, buf, dummy)
1.6 ! espie 172: const char *word; /* Word to Lower Case */
! 173: Boolean addSpace; /* True if need to add a space to the buffer
1.1 espie 174: * before sticking in the head */
1.6 ! espie 175: Buffer buf; /* Buffer in which to store it */
! 176: void *dummy UNUSED;
1.1 espie 177: {
178: size_t len = strlen(word);
179:
180: if (addSpace)
181: Buf_AddSpace(buf);
182: while (len--)
183: Buf_AddChar(buf, tolower(*word++));
184: return TRUE;
185: }
186:
187: /*-
188: *-----------------------------------------------------------------------
189: * VarHead --
190: * Remove the tail of the given word and place the result in the given
191: * buffer.
192: *
193: * Results:
194: * TRUE if characters were added to the buffer (a space needs to be
195: * added to the buffer before the next word).
196: *
197: * Side Effects:
198: * The trimmed word is added to the buffer.
199: *
200: *-----------------------------------------------------------------------
201: */
202: static Boolean
203: VarHead(word, addSpace, buf, dummy)
1.6 ! espie 204: const char *word; /* Word to trim */
! 205: Boolean addSpace; /* True if need to add a space to the buffer
1.1 espie 206: * before sticking in the head */
1.6 ! espie 207: Buffer buf; /* Buffer in which to store it */
! 208: void *dummy UNUSED;
1.1 espie 209: {
1.6 ! espie 210: const char *slash;
1.1 espie 211:
212: slash = strrchr(word, '/');
213: if (slash != NULL) {
214: if (addSpace)
215: Buf_AddSpace(buf);
216: Buf_AddInterval(buf, word, slash);
217: return TRUE;
218: } else {
219: /* If no directory part, give . (q.v. the POSIX standard) */
220: if (addSpace)
221: Buf_AddString(buf, " .");
222: else
223: Buf_AddChar(buf, '.');
224: }
1.6 ! espie 225: return TRUE;
1.1 espie 226: }
227:
228: /*-
229: *-----------------------------------------------------------------------
230: * VarTail --
231: * Remove the head of the given word and place the result in the given
232: * buffer.
233: *
234: * Results:
235: * TRUE if characters were added to the buffer (a space needs to be
236: * added to the buffer before the next word).
237: *
238: * Side Effects:
239: * The trimmed word is added to the buffer.
240: *
241: *-----------------------------------------------------------------------
242: */
243: static Boolean
244: VarTail(word, addSpace, buf, dummy)
1.6 ! espie 245: const char *word; /* Word to trim */
! 246: Boolean addSpace; /* TRUE if need to stick a space in the
1.1 espie 247: * buffer before adding the tail */
1.6 ! espie 248: Buffer buf; /* Buffer in which to store it */
! 249: void *dummy UNUSED;
1.1 espie 250: {
1.6 ! espie 251: const char *slash;
1.1 espie 252:
253: if (addSpace)
254: Buf_AddSpace(buf);
255: slash = strrchr(word, '/');
256: if (slash != NULL)
257: Buf_AddString(buf, slash+1);
258: else
259: Buf_AddString(buf, word);
1.6 ! espie 260: return TRUE;
1.1 espie 261: }
262:
263: /*-
264: *-----------------------------------------------------------------------
265: * VarSuffix --
266: * Place the suffix of the given word in the given buffer.
267: *
268: * Results:
269: * TRUE if characters were added to the buffer (a space needs to be
270: * added to the buffer before the next word).
271: *
272: * Side Effects:
273: * The suffix from the word is placed in the buffer.
274: *
275: *-----------------------------------------------------------------------
276: */
277: static Boolean
278: VarSuffix(word, addSpace, buf, dummy)
1.6 ! espie 279: const char *word; /* Word to trim */
! 280: Boolean addSpace; /* TRUE if need to add a space before placing
1.1 espie 281: * the suffix in the buffer */
1.6 ! espie 282: Buffer buf; /* Buffer in which to store it */
! 283: void *dummy UNUSED;
1.1 espie 284: {
1.6 ! espie 285: const char *dot;
1.1 espie 286:
287: dot = strrchr(word, '.');
288: if (dot != NULL) {
289: if (addSpace)
290: Buf_AddSpace(buf);
291: Buf_AddString(buf, dot+1);
292: addSpace = TRUE;
293: }
1.6 ! espie 294: return addSpace;
1.1 espie 295: }
296:
297: /*-
298: *-----------------------------------------------------------------------
299: * VarRoot --
300: * Remove the suffix of the given word and place the result in the
301: * buffer.
302: *
303: * Results:
304: * TRUE if characters were added to the buffer (a space needs to be
305: * added to the buffer before the next word).
306: *
307: * Side Effects:
308: * The trimmed word is added to the buffer.
309: *
310: *-----------------------------------------------------------------------
311: */
312: static Boolean
313: VarRoot(word, addSpace, buf, dummy)
1.6 ! espie 314: const char *word; /* Word to trim */
! 315: Boolean addSpace; /* TRUE if need to add a space to the buffer
1.1 espie 316: * before placing the root in it */
1.6 ! espie 317: Buffer buf; /* Buffer in which to store it */
! 318: void *dummy UNUSED;
1.1 espie 319: {
1.6 ! espie 320: const char *dot;
1.1 espie 321:
322: if (addSpace)
323: Buf_AddSpace(buf);
324:
325: dot = strrchr(word, '.');
326: if (dot != NULL)
327: Buf_AddInterval(buf, word, dot);
328: else
329: Buf_AddString(buf, word);
1.6 ! espie 330: return TRUE;
1.1 espie 331: }
332:
333: /*-
334: *-----------------------------------------------------------------------
335: * VarMatch --
336: * Place the word in the buffer if it matches the given pattern.
337: * Callback function for VarModify to implement the :M modifier.
338: *
339: * Results:
340: * TRUE if a space should be placed in the buffer before the next
341: * word.
342: *
343: * Side Effects:
344: * The word may be copied to the buffer.
345: *
346: *-----------------------------------------------------------------------
347: */
348: static Boolean
349: VarMatch(word, addSpace, buf, pattern)
350: const char *word; /* Word to examine */
351: Boolean addSpace; /* TRUE if need to add a space to the
352: * buffer before adding the word, if it
353: * matches */
354: Buffer buf; /* Buffer in which to store it */
355: void *pattern; /* Pattern the word must match */
356: {
357: if (Str_Match(word, (char *) pattern)) {
358: if (addSpace)
359: Buf_AddSpace(buf);
360: addSpace = TRUE;
361: Buf_AddString(buf, word);
362: }
363: return addSpace;
364: }
365:
366: #ifdef SYSVVARSUB
367: /*-
368: *-----------------------------------------------------------------------
369: * VarSYSVMatch --
370: * Place the word in the buffer if it matches the given pattern.
371: * Callback function for VarModify to implement the System V %
372: * modifiers.
373: *
374: * Results:
375: * TRUE if a space should be placed in the buffer before the next
376: * word.
377: *
378: * Side Effects:
379: * The word may be copied to the buffer.
380: *
381: *-----------------------------------------------------------------------
382: */
383: static Boolean
384: VarSYSVMatch(word, addSpace, buf, patp)
385: const char *word; /* Word to examine */
386: Boolean addSpace; /* TRUE if need to add a space to the
387: * buffer before adding the word, if it
388: * matches */
389: Buffer buf; /* Buffer in which to store it */
390: void *patp; /* Pattern the word must match */
391: {
392: size_t len;
393: const char *ptr;
394: VarPattern *pat = (VarPattern *) patp;
395:
396: if (*word) {
397: if (addSpace)
398: Buf_AddSpace(buf);
399:
400: addSpace = TRUE;
401:
402: if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL)
403: Str_SYSVSubst(buf, pat->rhs, ptr, len);
404: else
405: Buf_AddString(buf, word);
406: }
407: return addSpace;
408: }
409: #endif
410:
411: /*-
412: *-----------------------------------------------------------------------
413: * VarNoMatch --
414: * Place the word in the buffer if it doesn't match the given pattern.
415: * Callback function for VarModify to implement the :N modifier.
416: *
417: * Results:
418: * TRUE if a space should be placed in the buffer before the next
419: * word.
420: *
421: * Side Effects:
422: * The word may be copied to the buffer.
423: *
424: *-----------------------------------------------------------------------
425: */
426: static Boolean
427: VarNoMatch(word, addSpace, buf, pattern)
428: const char *word; /* Word to examine */
429: Boolean addSpace; /* TRUE if need to add a space to the
430: * buffer before adding the word, if it
431: * matches */
432: Buffer buf; /* Buffer in which to store it */
433: void *pattern; /* Pattern the word must match */
434: {
435: if (!Str_Match(word, (char *) pattern)) {
436: if (addSpace)
437: Buf_AddSpace(buf);
438: addSpace = TRUE;
439: Buf_AddString(buf, word);
440: }
441: return(addSpace);
442: }
443:
444:
445: /*-
446: *-----------------------------------------------------------------------
447: * VarSubstitute --
448: * Perform a string-substitution on the given word, placing the
449: * result in the passed buffer.
450: *
451: * Results:
452: * TRUE if a space is needed before more characters are added.
453: *
454: * Side Effects:
455: * None.
456: *
457: *-----------------------------------------------------------------------
458: */
459: static Boolean
460: VarSubstitute(word, addSpace, buf, patternp)
461: const char *word; /* Word to modify */
462: Boolean addSpace; /* True if space should be added before
463: * other characters */
464: Buffer buf; /* Buffer for result */
465: void *patternp; /* Pattern for substitution */
466: {
467: size_t wordLen; /* Length of word */
468: const char *cp; /* General pointer */
469: VarPattern *pattern = (VarPattern *) patternp;
470:
471: wordLen = strlen(word);
472: if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) !=
473: (VAR_SUB_ONE|VAR_SUB_MATCHED)) {
474: /*
475: * Still substituting -- break it down into simple anchored cases
476: * and if none of them fits, perform the general substitution case.
477: */
478: if ((pattern->flags & VAR_MATCH_START) &&
479: (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) {
480: /*
481: * Anchored at start and beginning of word matches pattern
482: */
483: if ((pattern->flags & VAR_MATCH_END) &&
484: (wordLen == pattern->leftLen)) {
485: /*
486: * Also anchored at end and matches to the end (word
487: * is same length as pattern) add space and rhs only
488: * if rhs is non-null.
489: */
490: if (pattern->rightLen != 0) {
491: if (addSpace)
492: Buf_AddSpace(buf);
493: addSpace = TRUE;
494: Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
495: }
496: pattern->flags |= VAR_SUB_MATCHED;
497: } else if (pattern->flags & VAR_MATCH_END) {
498: /*
499: * Doesn't match to end -- copy word wholesale
500: */
501: goto nosub;
502: } else {
503: /*
504: * Matches at start but need to copy in trailing characters
505: */
506: if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
507: if (addSpace)
508: Buf_AddSpace(buf);
509: addSpace = TRUE;
510: }
511: Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
512: Buf_AddChars(buf, wordLen - pattern->leftLen,
513: word + pattern->leftLen);
514: pattern->flags |= VAR_SUB_MATCHED;
515: }
516: } else if (pattern->flags & VAR_MATCH_START) {
517: /*
518: * Had to match at start of word and didn't -- copy whole word.
519: */
520: goto nosub;
521: } else if (pattern->flags & VAR_MATCH_END) {
522: /*
523: * Anchored at end, Find only place match could occur (leftLen
524: * characters from the end of the word) and see if it does. Note
525: * that because the $ will be left at the end of the lhs, we have
526: * to use strncmp.
527: */
528: cp = word + (wordLen - pattern->leftLen);
529: if ((cp >= word) &&
530: (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) {
531: /*
532: * Match found. If we will place characters in the buffer,
533: * add a space before hand as indicated by addSpace, then
534: * stuff in the initial, unmatched part of the word followed
535: * by the right-hand-side.
536: */
537: if (((cp - word) + pattern->rightLen) != 0) {
538: if (addSpace)
539: Buf_AddSpace(buf);
540: addSpace = TRUE;
541: }
542: Buf_AddInterval(buf, word, cp);
543: Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
544: pattern->flags |= VAR_SUB_MATCHED;
545: } else {
546: /*
547: * Had to match at end and didn't. Copy entire word.
548: */
549: goto nosub;
550: }
551: } else {
552: /*
553: * Pattern is unanchored: search for the pattern in the word using
554: * String_FindSubstring, copying unmatched portions and the
555: * right-hand-side for each match found, handling non-global
556: * substitutions correctly, etc. When the loop is done, any
557: * remaining part of the word (word and wordLen are adjusted
558: * accordingly through the loop) is copied straight into the
559: * buffer.
560: * addSpace is set FALSE as soon as a space is added to the
561: * buffer.
562: */
563: register Boolean done;
564: size_t origSize;
565:
566: done = FALSE;
567: origSize = Buf_Size(buf);
568: while (!done) {
569: cp = strstr(word, pattern->lhs);
570: if (cp != (char *)NULL) {
571: if (addSpace && (((cp - word) + pattern->rightLen) != 0)){
572: Buf_AddSpace(buf);
573: addSpace = FALSE;
574: }
575: Buf_AddInterval(buf, word, cp);
576: Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
577: wordLen -= (cp - word) + pattern->leftLen;
578: word = cp + pattern->leftLen;
579: if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0){
580: done = TRUE;
581: }
582: pattern->flags |= VAR_SUB_MATCHED;
583: } else {
584: done = TRUE;
585: }
586: }
587: if (wordLen != 0) {
588: if (addSpace)
589: Buf_AddSpace(buf);
590: Buf_AddChars(buf, wordLen, word);
591: }
592: /*
593: * If added characters to the buffer, need to add a space
594: * before we add any more. If we didn't add any, just return
595: * the previous value of addSpace.
596: */
597: return (Buf_Size(buf) != origSize || addSpace);
598: }
599: return (addSpace);
600: }
601: nosub:
602: if (addSpace)
603: Buf_AddSpace(buf);
604: Buf_AddChars(buf, wordLen, word);
605: return(TRUE);
606: }
607:
608:
609: #ifndef MAKE_BOOTSTRAP
610: /*-
611: *-----------------------------------------------------------------------
612: * VarREError --
613: * Print the error caused by a regcomp or regexec call.
614: *
615: * Results:
616: * None.
617: *
618: * Side Effects:
619: * An error gets printed.
620: *
621: *-----------------------------------------------------------------------
622: */
623: static void
624: VarREError(err, pat, str)
625: int err;
626: regex_t *pat;
627: const char *str;
628: {
629: char *errbuf;
630: int errlen;
631:
632: errlen = regerror(err, pat, 0, 0);
633: errbuf = emalloc(errlen);
634: regerror(err, pat, errbuf, errlen);
635: Error("%s: %s", str, errbuf);
636: free(errbuf);
637: }
638:
639: /*-
640: *-----------------------------------------------------------------------
641: * VarRESubstitute --
642: * Perform a regex substitution on the given word, placing the
643: * result in the passed buffer.
644: *
645: * Results:
646: * TRUE if a space is needed before more characters are added.
647: *
648: * Side Effects:
649: * None.
650: *
651: *-----------------------------------------------------------------------
652: */
653: static Boolean
654: VarRESubstitute(word, addSpace, buf, patternp)
655: const char *word;
656: Boolean addSpace;
657: Buffer buf;
658: void *patternp;
659: {
660: VarREPattern *pat;
661: int xrv;
662: const char *wp;
663: char *rp;
664: int added;
665:
666: #define MAYBE_ADD_SPACE() \
667: if (addSpace && !added) \
668: Buf_AddSpace(buf); \
669: added = 1
670:
671: added = 0;
672: wp = word;
673: pat = patternp;
674:
675: if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
676: (VAR_SUB_ONE|VAR_SUB_MATCHED))
677: xrv = REG_NOMATCH;
678: else {
679: tryagain:
680: xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0);
681: }
682:
683: switch (xrv) {
684: case 0:
685: pat->flags |= VAR_SUB_MATCHED;
686: if (pat->matches[0].rm_so > 0) {
687: MAYBE_ADD_SPACE();
688: Buf_AddChars(buf, pat->matches[0].rm_so, wp);
689: }
690:
691: for (rp = pat->replace; *rp; rp++) {
692: if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
693: MAYBE_ADD_SPACE();
694: Buf_AddChar(buf, rp[1]);
695: rp++;
696: }
697: else if ((*rp == '&') || ((*rp == '\\') && isdigit(rp[1]))) {
698: int n;
699: const char *subbuf;
700: int sublen;
701: char errstr[3];
702:
703: if (*rp == '&') {
704: n = 0;
705: errstr[0] = '&';
706: errstr[1] = '\0';
707: } else {
708: n = rp[1] - '0';
709: errstr[0] = '\\';
710: errstr[1] = rp[1];
711: errstr[2] = '\0';
712: rp++;
713: }
714:
715: if (n > pat->nsub) {
716: Error("No subexpression %s", &errstr[0]);
717: subbuf = "";
718: sublen = 0;
719: } else if ((pat->matches[n].rm_so == -1) &&
720: (pat->matches[n].rm_eo == -1)) {
721: Error("No match for subexpression %s", &errstr[0]);
722: subbuf = "";
723: sublen = 0;
724: } else {
725: subbuf = wp + pat->matches[n].rm_so;
726: sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so;
727: }
728:
729: if (sublen > 0) {
730: MAYBE_ADD_SPACE();
731: Buf_AddChars(buf, sublen, subbuf);
732: }
733: } else {
734: MAYBE_ADD_SPACE();
735: Buf_AddChar(buf, *rp);
736: }
737: }
738: wp += pat->matches[0].rm_eo;
739: if (pat->flags & VAR_SUB_GLOBAL)
740: goto tryagain;
741: if (*wp) {
742: MAYBE_ADD_SPACE();
743: Buf_AddChars(buf, strlen(wp), wp);
744: }
745: break;
746: default:
747: VarREError(xrv, &pat->re, "Unexpected regex error");
748: /* fall through */
749: case REG_NOMATCH:
750: if (*wp) {
751: MAYBE_ADD_SPACE();
752: Buf_AddChars(buf, strlen(wp), wp);
753: }
754: break;
755: }
756: return(addSpace||added);
757: }
758: #endif
759:
760: /*-
761: *-----------------------------------------------------------------------
762: * VarModify --
763: * Modify each of the words of the passed string using the given
764: * function. Used to implement all modifiers.
765: *
766: * Results:
767: * A string of all the words modified appropriately.
768: *
769: *-----------------------------------------------------------------------
770: */
771: static char *
1.5 espie 772: VarModify(str, modProc, datum)
773: char *str; /* String whose words should be trimmed */
774: /* Function to use to modify them */
1.1 espie 775: Boolean (*modProc) __P((const char *, Boolean, Buffer, void *));
1.5 espie 776: void *datum; /* Datum to pass it */
1.1 espie 777: {
1.5 espie 778: BUFFER buf; /* Buffer for the new string */
779: Boolean addSpace; /* TRUE if need to add a space to the
780: * buffer before adding the trimmed
781: * word */
782: char *end;
783: char *word;
1.1 espie 784:
1.2 espie 785: if (str == NULL)
786: return NULL;
787:
1.1 espie 788: Buf_Init(&buf, 0);
789: addSpace = FALSE;
790:
1.5 espie 791: end = str;
1.1 espie 792:
1.5 espie 793: while ((word = iterate_words(&end)) != NULL) {
794: char termc;
1.1 espie 795:
1.5 espie 796: termc = *end;
797: *end = '\0';
798: addSpace = (*modProc)(word, addSpace, &buf, datum);
799: *end = termc;
800: }
1.1 espie 801: return Buf_Retrieve(&buf);
802: }
803:
804: /*-
805: *-----------------------------------------------------------------------
806: * VarGetPattern --
807: * Pass through the tstr looking for 1) escaped delimiters,
808: * '$'s and backslashes (place the escaped character in
809: * uninterpreted) and 2) unescaped $'s that aren't before
810: * the delimiter (expand the variable substitution).
811: * Return the expanded string or NULL if the delimiter was missing
812: * If pattern is specified, handle escaped ampersands, and replace
813: * unescaped ampersands with the lhs of the pattern.
814: *
815: * Results:
816: * A string of all the words modified appropriately.
817: * If length is specified, return the string length of the buffer
818: * If flags is specified and the last character of the pattern is a
819: * $ set the VAR_MATCH_END bit of flags.
820: *
821: * Side Effects:
822: * None.
823: *-----------------------------------------------------------------------
824: */
825: static char *
826: VarGetPattern(ctxt, err, tstr, delim, flags, length, pattern)
827: SymTable *ctxt;
828: int err;
829: char **tstr;
830: int delim;
831: int *flags;
832: size_t *length;
833: VarPattern *pattern;
834: {
835: char *cp;
836: BUFFER buf;
837: size_t junk;
838:
839: Buf_Init(&buf, 0);
840: if (length == NULL)
841: length = &junk;
842:
843: #define IS_A_MATCH(cp, delim) \
844: ((cp[0] == '\\') && ((cp[1] == delim) || \
845: (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&'))))
846:
847: /*
848: * Skim through until the matching delimiter is found;
849: * pick up variable substitutions on the way. Also allow
850: * backslashes to quote the delimiter, $, and \, but don't
851: * touch other backslashes.
852: */
853: for (cp = *tstr; *cp && (*cp != delim); cp++) {
854: if (IS_A_MATCH(cp, delim)) {
855: Buf_AddChar(&buf, cp[1]);
856: cp++;
857: } else if (*cp == '$') {
858: if (cp[1] == delim) {
859: if (flags == NULL)
860: Buf_AddChar(&buf, *cp);
861: else
862: /*
863: * Unescaped $ at end of pattern => anchor
864: * pattern at end.
865: */
866: *flags |= VAR_MATCH_END;
867: }
868: else {
869: char *cp2;
870: size_t len;
871: Boolean freeIt;
872:
873: /*
874: * If unescaped dollar sign not before the
875: * delimiter, assume it's a variable
876: * substitution and recurse.
877: */
878: cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt);
879: Buf_AddString(&buf, cp2);
880: if (freeIt)
881: free(cp2);
882: cp += len - 1;
883: }
884: }
885: else if (pattern && *cp == '&')
886: Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
887: else
888: Buf_AddChar(&buf, *cp);
889: }
890:
891: if (*cp != delim) {
892: *tstr = cp;
893: *length = 0;
894: return NULL;
895: }
896: else {
897: *tstr = ++cp;
898: *length = Buf_Size(&buf);
899: return Buf_Retrieve(&buf);
900: }
901: }
902:
903: char *
904: VarModifiers_Apply(str, ctxt, err, freePtr, start, endc, lengthPtr)
905: char *str;
906: SymTable *ctxt;
907: Boolean err;
908: Boolean *freePtr;
909: char *start;
910: char endc;
911: size_t *lengthPtr;
912: {
913: char *tstr;
914: char delim;
915: char *cp;
916:
917: tstr = start;
918:
919: /*
920: * Now we need to apply any modifiers the user wants applied.
921: * These are:
922: * :M<pattern> words which match the given <pattern>.
923: * <pattern> is of the standard file
924: * wildcarding form.
925: * :S<d><pat1><d><pat2><d>[g]
926: * Substitute <pat2> for <pat1> in the value
927: * :C<d><pat1><d><pat2><d>[g]
928: * Substitute <pat2> for regex <pat1> in the value
929: * :H Substitute the head of each word
930: * :T Substitute the tail of each word
931: * :E Substitute the extension (minus '.') of
932: * each word
933: * :R Substitute the root of each word
934: * (pathname minus the suffix).
935: * :lhs=rhs Like :S, but the rhs goes to the end of
936: * the invocation.
937: */
938: while (*tstr != endc) {
939: char *newStr; /* New value to return */
940: char termc; /* Character which terminated scan */
941:
942: if (DEBUG(VAR))
1.2 espie 943: printf("Applying :%c to \"%s\"\n", *tstr, str ? str : "");
1.1 espie 944: switch (*tstr) {
945: case 'N':
946: case 'M':
947: {
948: for (cp = tstr + 1;
949: *cp != '\0' && *cp != ':' && *cp != endc;
950: cp++) {
951: if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){
952: cp++;
953: }
954: }
955: termc = *cp;
956: *cp = '\0';
957: if (*tstr == 'M')
958: newStr = VarModify(str, VarMatch, tstr+1);
959: else
960: newStr = VarModify(str, VarNoMatch, tstr+1);
961: break;
962: }
963: case 'S':
964: {
965: VarPattern pattern;
966:
967: pattern.flags = 0;
968: delim = tstr[1];
969: tstr += 2;
970:
971: /* If pattern begins with '^', it is anchored to the
972: * start of the word -- skip over it and flag pattern. */
973: if (*tstr == '^') {
974: pattern.flags |= VAR_MATCH_START;
975: tstr++;
976: }
977:
978: cp = tstr;
979: if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim,
980: &pattern.flags, &pattern.leftLen, NULL)) == NULL)
981: goto cleanup;
982:
983: if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim,
984: NULL, &pattern.rightLen, &pattern)) == NULL)
985: goto cleanup;
986:
987: /* Check for global substitution. If 'g' after the final
988: * delimiter, substitution is global and is marked that
989: * way. */
990: for (;; cp++) {
991: switch (*cp) {
992: case 'g':
993: pattern.flags |= VAR_SUB_GLOBAL;
994: continue;
995: case '1':
996: pattern.flags |= VAR_SUB_ONE;
997: continue;
998: }
999: break;
1000: }
1001:
1002: termc = *cp;
1003: newStr = VarModify(str, VarSubstitute, &pattern);
1004:
1005: /* Free the two strings. */
1006: free(pattern.lhs);
1007: free(pattern.rhs);
1008: break;
1009: }
1010: #ifndef MAKE_BOOTSTRAP
1011: case 'C':
1012: {
1013: VarREPattern pattern;
1014: char *re;
1015: int error;
1016:
1017: pattern.flags = 0;
1018: delim = tstr[1];
1019: tstr += 2;
1020:
1021: cp = tstr;
1022:
1023: if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL,
1024: NULL, NULL)) == NULL)
1025: goto cleanup;
1026:
1027: if ((pattern.replace = VarGetPattern(ctxt, err, &cp,
1028: delim, NULL, NULL, NULL)) == NULL) {
1029: free(re);
1030: goto cleanup;
1031: }
1032:
1033: for (;; cp++) {
1034: switch (*cp) {
1035: case 'g':
1036: pattern.flags |= VAR_SUB_GLOBAL;
1037: continue;
1038: case '1':
1039: pattern.flags |= VAR_SUB_ONE;
1040: continue;
1041: }
1042: break;
1043: }
1044:
1045: termc = *cp;
1046:
1047: error = regcomp(&pattern.re, re, REG_EXTENDED);
1048: free(re);
1049: if (error) {
1050: *lengthPtr = cp - start + 1;
1051: VarREError(error, &pattern.re, "RE substitution error");
1052: free(pattern.replace);
1053: return var_Error;
1054: }
1055:
1056: pattern.nsub = pattern.re.re_nsub + 1;
1057: if (pattern.nsub < 1)
1058: pattern.nsub = 1;
1059: if (pattern.nsub > 10)
1060: pattern.nsub = 10;
1061: pattern.matches = emalloc(pattern.nsub *
1062: sizeof(regmatch_t));
1063: newStr = VarModify(str, VarRESubstitute, &pattern);
1064: regfree(&pattern.re);
1065: free(pattern.replace);
1066: free(pattern.matches);
1067: break;
1068: }
1069: #endif
1070: case 'Q':
1071: if (tstr[1] == endc || tstr[1] == ':') {
1072: newStr = VarQuote(str);
1073: cp = tstr + 1;
1074: termc = *cp;
1075: break;
1076: }
1.3 espie 1077: /* FALLTHROUGH */
1.1 espie 1078: case 'T':
1079: if (tstr[1] == endc || tstr[1] == ':') {
1080: newStr = VarModify(str, VarTail, NULL);
1081: cp = tstr + 1;
1082: termc = *cp;
1083: break;
1084: }
1.3 espie 1085: /* FALLTHROUGH */
1.1 espie 1086: case 'H':
1087: if (tstr[1] == endc || tstr[1] == ':') {
1088: newStr = VarModify(str, VarHead, NULL);
1089: cp = tstr + 1;
1090: termc = *cp;
1091: break;
1092: }
1.3 espie 1093: /* FALLTHROUGH */
1.1 espie 1094: case 'E':
1095: if (tstr[1] == endc || tstr[1] == ':') {
1096: newStr = VarModify(str, VarSuffix, NULL);
1097: cp = tstr + 1;
1098: termc = *cp;
1099: break;
1100: }
1.3 espie 1101: /* FALLTHROUGH */
1.1 espie 1102: case 'R':
1103: if (tstr[1] == endc || tstr[1] == ':') {
1104: newStr = VarModify(str, VarRoot, NULL);
1105: cp = tstr + 1;
1106: termc = *cp;
1107: break;
1108: }
1.3 espie 1109: /* FALLTHROUGH */
1.1 espie 1110: case 'U':
1111: if (tstr[1] == endc || tstr[1] == ':') {
1112: newStr = VarModify(str, VarUppercase, NULL);
1113: cp = tstr + 1;
1114: termc = *cp;
1115: break;
1116: }
1.3 espie 1117: /* FALLTHROUGH */
1.1 espie 1118: case 'L':
1119: if (tstr[1] == endc || tstr[1] == ':') {
1120: newStr = VarModify(str, VarLowercase, NULL);
1121: cp = tstr + 1;
1122: termc = *cp;
1123: break;
1124: }
1.3 espie 1125: /* FALLTHROUGH */
1.1 espie 1126: #ifdef SUNSHCMD
1127: case 's':
1128: if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) {
1.4 espie 1129: char *err = 0;
1.2 espie 1130: newStr = str ? Cmd_Exec(str, &err) : NULL;
1.1 espie 1131: if (err)
1132: Error(err, str);
1133: cp = tstr + 2;
1134: termc = *cp;
1135: break;
1136: }
1.3 espie 1137: /* FALLTHROUGH */
1.1 espie 1138: #endif
1139: default:
1140: {
1141: #ifdef SYSVVARSUB
1142: /* This can either be a bogus modifier or a System-V
1143: * substitution command. */
1144: VarPattern pattern;
1145: Boolean eqFound;
1146: int cnt; /* Used to count brace pairs when
1147: * variable in in parens or braces */
1148: char startc;
1149:
1150: if (endc == ')')
1151: startc = '(';
1152: else
1153: startc = '{';
1154:
1155: pattern.flags = 0;
1156: eqFound = FALSE;
1157: /* First we make a pass through the string trying
1158: * to verify it is a SYSV-make-style translation:
1159: * it must be: <string1>=<string2>) */
1160: cp = tstr;
1161: cnt = 1;
1162: while (*cp != '\0' && cnt) {
1163: if (*cp == '=') {
1164: eqFound = TRUE;
1165: /* continue looking for endc */
1166: }
1167: else if (*cp == endc)
1168: cnt--;
1169: else if (*cp == startc)
1170: cnt++;
1171: if (cnt)
1172: cp++;
1173: }
1174: if (*cp == endc && eqFound) {
1175:
1176: /* Now we break this sucker into the lhs and
1177: * rhs. We must null terminate them of course. */
1178: for (cp = tstr; *cp != '='; cp++)
1179: continue;
1180: pattern.lhs = tstr;
1181: pattern.leftLen = cp - tstr;
1182: *cp++ = '\0';
1183:
1184: pattern.rhs = cp;
1185: cnt = 1;
1186: while (cnt) {
1187: if (*cp == endc)
1188: cnt--;
1189: else if (*cp == startc)
1190: cnt++;
1191: if (cnt)
1192: cp++;
1193: }
1194: pattern.rightLen = cp - pattern.rhs;
1195: *cp = '\0';
1196:
1197: /* SYSV modifications happen through the whole
1198: * string. Note the pattern is anchored at the end. */
1199: newStr = VarModify(str, VarSYSVMatch, &pattern);
1200:
1201: /* Restore the nulled characters */
1202: pattern.lhs[pattern.leftLen] = '=';
1203: pattern.rhs[pattern.rightLen] = endc;
1204: termc = endc;
1205: } else
1206: #endif
1207: {
1208: Error ("Unknown modifier '%c'\n", *tstr);
1209: for (cp = tstr+1;
1210: *cp != ':' && *cp != endc && *cp != '\0';)
1211: cp++;
1212: termc = *cp;
1213: newStr = var_Error;
1214: }
1215: }
1216: }
1217: if (DEBUG(VAR))
1.2 espie 1218: printf("Result is \"%s\"\n", newStr != NULL ? newStr : "");
1.1 espie 1219:
1220: if (*freePtr)
1221: free(str);
1222: str = newStr;
1.2 espie 1223: if (str != var_Error && str != NULL)
1.1 espie 1224: *freePtr = TRUE;
1225: else
1226: *freePtr = FALSE;
1227: if (termc == '\0')
1228: Error("Unclosed variable specification");
1229: else if (termc == ':')
1230: *cp++ = termc;
1231: else
1232: *cp = termc;
1233: tstr = cp;
1234: }
1235: *lengthPtr += tstr - start+1;
1236: return str;
1237:
1238: cleanup:
1239: *lengthPtr += cp - start +1;
1240: if (*freePtr)
1241: free(str);
1242: Error("Unclosed substitution for (%c missing)", delim);
1243: return var_Error;
1244: }
1245:
1246: /*-
1247: *-----------------------------------------------------------------------
1248: * VarQuote --
1249: * Quote shell meta-characters in the string
1250: *
1251: * Results:
1252: * The quoted string
1253: *
1254: *-----------------------------------------------------------------------
1255: */
1256: static char *
1257: VarQuote(str)
1258: const char *str;
1259: {
1260:
1261: BUFFER buf;
1262: /* This should cover most shells :-( */
1263: static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1.2 espie 1264:
1265: if (str == NULL)
1266: return NULL;
1.1 espie 1267:
1268: Buf_Init(&buf, MAKE_BSIZE);
1269: for (; *str; str++) {
1270: if (strchr(meta, *str) != NULL)
1271: Buf_AddChar(&buf, '\\');
1272: Buf_AddChar(&buf, *str);
1273: }
1274: return Buf_Retrieve(&buf);
1275: }
1276: /*-
1277: *-----------------------------------------------------------------------
1278: * Var_GetHead --
1279: * Find the leading components of a (list of) filename(s).
1280: * XXX: VarHead does not replace foo by ., as (sun) System V make
1281: * does.
1282: *
1283: * Results:
1284: * The leading components.
1285: *
1286: * Side Effects:
1287: * None.
1288: *
1289: *-----------------------------------------------------------------------
1290: */
1291: char *
1292: Var_GetHead(file)
1293: char *file; /* Filename to manipulate */
1294: {
1295: return VarModify(file, VarHead, NULL);
1296: }
1297:
1298: /*-
1299: *-----------------------------------------------------------------------
1300: * Var_GetTail --
1301: * Return the tail from each of a list of words. Used to set the
1302: * System V local variables.
1303: *
1304: * Results:
1305: * The resulting string.
1306: *
1307: * Side Effects:
1308: * None.
1309: *
1310: *-----------------------------------------------------------------------
1311: */
1312: char *
1313: Var_GetTail(file)
1314: char *file; /* Filename to modify */
1315: {
1316: return VarModify(file, VarTail, NULL);
1317: }
1318: