Annotation of src/usr.bin/make/varmodifiers.c, Revision 1.3
1.3 ! espie 1: /* $OpenBSD: varmodifiers.c,v 1.2 2000/07/17 23:26:51 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 *));
121: static char *VarModify __P((const char *, Boolean (*)(const char *, Boolean, Buffer, void *), void *));
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)
141: const char *word; /* Word to Upper Case */
142: Boolean addSpace; /* True if need to add a space to the buffer
143: * before sticking in the head */
144: Buffer buf; /* Buffer in which to store it */
145: void *dummy;
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)
172: const char *word; /* Word to Lower Case */
173: Boolean addSpace; /* True if need to add a space to the buffer
174: * before sticking in the head */
175: Buffer buf; /* Buffer in which to store it */
176: void *dummy;
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)
204: const char *word; /* Word to trim */
205: Boolean addSpace; /* True if need to add a space to the buffer
206: * before sticking in the head */
207: Buffer buf; /* Buffer in which to store it */
208: void *dummy;
209: {
210: const char *slash;
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: }
225: return(dummy ? TRUE : TRUE);
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)
245: const char *word; /* Word to trim */
246: Boolean addSpace; /* TRUE if need to stick a space in the
247: * buffer before adding the tail */
248: Buffer buf; /* Buffer in which to store it */
249: void *dummy;
250: {
251: const char *slash;
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);
260: return (dummy ? TRUE : TRUE);
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)
279: const char *word; /* Word to trim */
280: Boolean addSpace; /* TRUE if need to add a space before placing
281: * the suffix in the buffer */
282: Buffer buf; /* Buffer in which to store it */
283: void *dummy;
284: {
285: const char *dot;
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: }
294: return (dummy ? addSpace : addSpace);
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)
314: const char *word; /* Word to trim */
315: Boolean addSpace; /* TRUE if need to add a space to the buffer
316: * before placing the root in it */
317: Buffer buf; /* Buffer in which to store it */
318: void *dummy;
319: {
320: const char *dot;
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);
330: return (dummy ? TRUE : TRUE);
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 *
772: VarModify (str, modProc, datum)
773: const char *str; /* String whose words should be trimmed */
774: /* Function to use to modify them */
775: Boolean (*modProc) __P((const char *, Boolean, Buffer, void *));
776: void *datum; /* Datum to pass it */
777: {
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 **av; /* word list */
783: char *as; /* word list memory */
784: int ac, i;
785:
1.2 espie 786: if (str == NULL)
787: return NULL;
788:
1.1 espie 789: Buf_Init(&buf, 0);
790: addSpace = FALSE;
791:
792: av = brk_string(str, &ac, FALSE, &as);
793:
794: for (i = 0; i < ac; i++)
795: addSpace = (*modProc)(av[i], addSpace, &buf, datum);
796:
797: free(as);
798: free(av);
799: return Buf_Retrieve(&buf);
800: }
801:
802: /*-
803: *-----------------------------------------------------------------------
804: * VarGetPattern --
805: * Pass through the tstr looking for 1) escaped delimiters,
806: * '$'s and backslashes (place the escaped character in
807: * uninterpreted) and 2) unescaped $'s that aren't before
808: * the delimiter (expand the variable substitution).
809: * Return the expanded string or NULL if the delimiter was missing
810: * If pattern is specified, handle escaped ampersands, and replace
811: * unescaped ampersands with the lhs of the pattern.
812: *
813: * Results:
814: * A string of all the words modified appropriately.
815: * If length is specified, return the string length of the buffer
816: * If flags is specified and the last character of the pattern is a
817: * $ set the VAR_MATCH_END bit of flags.
818: *
819: * Side Effects:
820: * None.
821: *-----------------------------------------------------------------------
822: */
823: static char *
824: VarGetPattern(ctxt, err, tstr, delim, flags, length, pattern)
825: SymTable *ctxt;
826: int err;
827: char **tstr;
828: int delim;
829: int *flags;
830: size_t *length;
831: VarPattern *pattern;
832: {
833: char *cp;
834: BUFFER buf;
835: size_t junk;
836:
837: Buf_Init(&buf, 0);
838: if (length == NULL)
839: length = &junk;
840:
841: #define IS_A_MATCH(cp, delim) \
842: ((cp[0] == '\\') && ((cp[1] == delim) || \
843: (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&'))))
844:
845: /*
846: * Skim through until the matching delimiter is found;
847: * pick up variable substitutions on the way. Also allow
848: * backslashes to quote the delimiter, $, and \, but don't
849: * touch other backslashes.
850: */
851: for (cp = *tstr; *cp && (*cp != delim); cp++) {
852: if (IS_A_MATCH(cp, delim)) {
853: Buf_AddChar(&buf, cp[1]);
854: cp++;
855: } else if (*cp == '$') {
856: if (cp[1] == delim) {
857: if (flags == NULL)
858: Buf_AddChar(&buf, *cp);
859: else
860: /*
861: * Unescaped $ at end of pattern => anchor
862: * pattern at end.
863: */
864: *flags |= VAR_MATCH_END;
865: }
866: else {
867: char *cp2;
868: size_t len;
869: Boolean freeIt;
870:
871: /*
872: * If unescaped dollar sign not before the
873: * delimiter, assume it's a variable
874: * substitution and recurse.
875: */
876: cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt);
877: Buf_AddString(&buf, cp2);
878: if (freeIt)
879: free(cp2);
880: cp += len - 1;
881: }
882: }
883: else if (pattern && *cp == '&')
884: Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
885: else
886: Buf_AddChar(&buf, *cp);
887: }
888:
889: if (*cp != delim) {
890: *tstr = cp;
891: *length = 0;
892: return NULL;
893: }
894: else {
895: *tstr = ++cp;
896: *length = Buf_Size(&buf);
897: return Buf_Retrieve(&buf);
898: }
899: }
900:
901: char *
902: VarModifiers_Apply(str, ctxt, err, freePtr, start, endc, lengthPtr)
903: char *str;
904: SymTable *ctxt;
905: Boolean err;
906: Boolean *freePtr;
907: char *start;
908: char endc;
909: size_t *lengthPtr;
910: {
911: char *tstr;
912: char delim;
913: char *cp;
914:
915: tstr = start;
916:
917: /*
918: * Now we need to apply any modifiers the user wants applied.
919: * These are:
920: * :M<pattern> words which match the given <pattern>.
921: * <pattern> is of the standard file
922: * wildcarding form.
923: * :S<d><pat1><d><pat2><d>[g]
924: * Substitute <pat2> for <pat1> in the value
925: * :C<d><pat1><d><pat2><d>[g]
926: * Substitute <pat2> for regex <pat1> in the value
927: * :H Substitute the head of each word
928: * :T Substitute the tail of each word
929: * :E Substitute the extension (minus '.') of
930: * each word
931: * :R Substitute the root of each word
932: * (pathname minus the suffix).
933: * :lhs=rhs Like :S, but the rhs goes to the end of
934: * the invocation.
935: */
936: while (*tstr != endc) {
937: char *newStr; /* New value to return */
938: char termc; /* Character which terminated scan */
939:
940: if (DEBUG(VAR))
1.2 espie 941: printf("Applying :%c to \"%s\"\n", *tstr, str ? str : "");
1.1 espie 942: switch (*tstr) {
943: case 'N':
944: case 'M':
945: {
946: for (cp = tstr + 1;
947: *cp != '\0' && *cp != ':' && *cp != endc;
948: cp++) {
949: if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){
950: cp++;
951: }
952: }
953: termc = *cp;
954: *cp = '\0';
955: if (*tstr == 'M')
956: newStr = VarModify(str, VarMatch, tstr+1);
957: else
958: newStr = VarModify(str, VarNoMatch, tstr+1);
959: break;
960: }
961: case 'S':
962: {
963: VarPattern pattern;
964:
965: pattern.flags = 0;
966: delim = tstr[1];
967: tstr += 2;
968:
969: /* If pattern begins with '^', it is anchored to the
970: * start of the word -- skip over it and flag pattern. */
971: if (*tstr == '^') {
972: pattern.flags |= VAR_MATCH_START;
973: tstr++;
974: }
975:
976: cp = tstr;
977: if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim,
978: &pattern.flags, &pattern.leftLen, NULL)) == NULL)
979: goto cleanup;
980:
981: if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim,
982: NULL, &pattern.rightLen, &pattern)) == NULL)
983: goto cleanup;
984:
985: /* Check for global substitution. If 'g' after the final
986: * delimiter, substitution is global and is marked that
987: * way. */
988: for (;; cp++) {
989: switch (*cp) {
990: case 'g':
991: pattern.flags |= VAR_SUB_GLOBAL;
992: continue;
993: case '1':
994: pattern.flags |= VAR_SUB_ONE;
995: continue;
996: }
997: break;
998: }
999:
1000: termc = *cp;
1001: newStr = VarModify(str, VarSubstitute, &pattern);
1002:
1003: /* Free the two strings. */
1004: free(pattern.lhs);
1005: free(pattern.rhs);
1006: break;
1007: }
1008: #ifndef MAKE_BOOTSTRAP
1009: case 'C':
1010: {
1011: VarREPattern pattern;
1012: char *re;
1013: int error;
1014:
1015: pattern.flags = 0;
1016: delim = tstr[1];
1017: tstr += 2;
1018:
1019: cp = tstr;
1020:
1021: if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL,
1022: NULL, NULL)) == NULL)
1023: goto cleanup;
1024:
1025: if ((pattern.replace = VarGetPattern(ctxt, err, &cp,
1026: delim, NULL, NULL, NULL)) == NULL) {
1027: free(re);
1028: goto cleanup;
1029: }
1030:
1031: for (;; cp++) {
1032: switch (*cp) {
1033: case 'g':
1034: pattern.flags |= VAR_SUB_GLOBAL;
1035: continue;
1036: case '1':
1037: pattern.flags |= VAR_SUB_ONE;
1038: continue;
1039: }
1040: break;
1041: }
1042:
1043: termc = *cp;
1044:
1045: error = regcomp(&pattern.re, re, REG_EXTENDED);
1046: free(re);
1047: if (error) {
1048: *lengthPtr = cp - start + 1;
1049: VarREError(error, &pattern.re, "RE substitution error");
1050: free(pattern.replace);
1051: return var_Error;
1052: }
1053:
1054: pattern.nsub = pattern.re.re_nsub + 1;
1055: if (pattern.nsub < 1)
1056: pattern.nsub = 1;
1057: if (pattern.nsub > 10)
1058: pattern.nsub = 10;
1059: pattern.matches = emalloc(pattern.nsub *
1060: sizeof(regmatch_t));
1061: newStr = VarModify(str, VarRESubstitute, &pattern);
1062: regfree(&pattern.re);
1063: free(pattern.replace);
1064: free(pattern.matches);
1065: break;
1066: }
1067: #endif
1068: case 'Q':
1069: if (tstr[1] == endc || tstr[1] == ':') {
1070: newStr = VarQuote(str);
1071: cp = tstr + 1;
1072: termc = *cp;
1073: break;
1074: }
1.3 ! espie 1075: /* FALLTHROUGH */
1.1 espie 1076: case 'T':
1077: if (tstr[1] == endc || tstr[1] == ':') {
1078: newStr = VarModify(str, VarTail, NULL);
1079: cp = tstr + 1;
1080: termc = *cp;
1081: break;
1082: }
1.3 ! espie 1083: /* FALLTHROUGH */
1.1 espie 1084: case 'H':
1085: if (tstr[1] == endc || tstr[1] == ':') {
1086: newStr = VarModify(str, VarHead, NULL);
1087: cp = tstr + 1;
1088: termc = *cp;
1089: break;
1090: }
1.3 ! espie 1091: /* FALLTHROUGH */
1.1 espie 1092: case 'E':
1093: if (tstr[1] == endc || tstr[1] == ':') {
1094: newStr = VarModify(str, VarSuffix, NULL);
1095: cp = tstr + 1;
1096: termc = *cp;
1097: break;
1098: }
1.3 ! espie 1099: /* FALLTHROUGH */
1.1 espie 1100: case 'R':
1101: if (tstr[1] == endc || tstr[1] == ':') {
1102: newStr = VarModify(str, VarRoot, NULL);
1103: cp = tstr + 1;
1104: termc = *cp;
1105: break;
1106: }
1.3 ! espie 1107: /* FALLTHROUGH */
1.1 espie 1108: case 'U':
1109: if (tstr[1] == endc || tstr[1] == ':') {
1110: newStr = VarModify(str, VarUppercase, NULL);
1111: cp = tstr + 1;
1112: termc = *cp;
1113: break;
1114: }
1.3 ! espie 1115: /* FALLTHROUGH */
1.1 espie 1116: case 'L':
1117: if (tstr[1] == endc || tstr[1] == ':') {
1118: newStr = VarModify(str, VarLowercase, NULL);
1119: cp = tstr + 1;
1120: termc = *cp;
1121: break;
1122: }
1.3 ! espie 1123: /* FALLTHROUGH */
1.1 espie 1124: #ifdef SUNSHCMD
1125: case 's':
1126: if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) {
1127: char *err;
1.2 espie 1128: newStr = str ? Cmd_Exec(str, &err) : NULL;
1.1 espie 1129: if (err)
1130: Error(err, str);
1131: cp = tstr + 2;
1132: termc = *cp;
1133: break;
1134: }
1.3 ! espie 1135: /* FALLTHROUGH */
1.1 espie 1136: #endif
1137: default:
1138: {
1139: #ifdef SYSVVARSUB
1140: /* This can either be a bogus modifier or a System-V
1141: * substitution command. */
1142: VarPattern pattern;
1143: Boolean eqFound;
1144: int cnt; /* Used to count brace pairs when
1145: * variable in in parens or braces */
1146: char startc;
1147:
1148: if (endc == ')')
1149: startc = '(';
1150: else
1151: startc = '{';
1152:
1153: pattern.flags = 0;
1154: eqFound = FALSE;
1155: /* First we make a pass through the string trying
1156: * to verify it is a SYSV-make-style translation:
1157: * it must be: <string1>=<string2>) */
1158: cp = tstr;
1159: cnt = 1;
1160: while (*cp != '\0' && cnt) {
1161: if (*cp == '=') {
1162: eqFound = TRUE;
1163: /* continue looking for endc */
1164: }
1165: else if (*cp == endc)
1166: cnt--;
1167: else if (*cp == startc)
1168: cnt++;
1169: if (cnt)
1170: cp++;
1171: }
1172: if (*cp == endc && eqFound) {
1173:
1174: /* Now we break this sucker into the lhs and
1175: * rhs. We must null terminate them of course. */
1176: for (cp = tstr; *cp != '='; cp++)
1177: continue;
1178: pattern.lhs = tstr;
1179: pattern.leftLen = cp - tstr;
1180: *cp++ = '\0';
1181:
1182: pattern.rhs = cp;
1183: cnt = 1;
1184: while (cnt) {
1185: if (*cp == endc)
1186: cnt--;
1187: else if (*cp == startc)
1188: cnt++;
1189: if (cnt)
1190: cp++;
1191: }
1192: pattern.rightLen = cp - pattern.rhs;
1193: *cp = '\0';
1194:
1195: /* SYSV modifications happen through the whole
1196: * string. Note the pattern is anchored at the end. */
1197: newStr = VarModify(str, VarSYSVMatch, &pattern);
1198:
1199: /* Restore the nulled characters */
1200: pattern.lhs[pattern.leftLen] = '=';
1201: pattern.rhs[pattern.rightLen] = endc;
1202: termc = endc;
1203: } else
1204: #endif
1205: {
1206: Error ("Unknown modifier '%c'\n", *tstr);
1207: for (cp = tstr+1;
1208: *cp != ':' && *cp != endc && *cp != '\0';)
1209: cp++;
1210: termc = *cp;
1211: newStr = var_Error;
1212: }
1213: }
1214: }
1215: if (DEBUG(VAR))
1.2 espie 1216: printf("Result is \"%s\"\n", newStr != NULL ? newStr : "");
1.1 espie 1217:
1218: if (*freePtr)
1219: free(str);
1220: str = newStr;
1.2 espie 1221: if (str != var_Error && str != NULL)
1.1 espie 1222: *freePtr = TRUE;
1223: else
1224: *freePtr = FALSE;
1225: if (termc == '\0')
1226: Error("Unclosed variable specification");
1227: else if (termc == ':')
1228: *cp++ = termc;
1229: else
1230: *cp = termc;
1231: tstr = cp;
1232: }
1233: *lengthPtr += tstr - start+1;
1234: return str;
1235:
1236: cleanup:
1237: *lengthPtr += cp - start +1;
1238: if (*freePtr)
1239: free(str);
1240: Error("Unclosed substitution for (%c missing)", delim);
1241: return var_Error;
1242: }
1243:
1244: /*-
1245: *-----------------------------------------------------------------------
1246: * VarQuote --
1247: * Quote shell meta-characters in the string
1248: *
1249: * Results:
1250: * The quoted string
1251: *
1252: *-----------------------------------------------------------------------
1253: */
1254: static char *
1255: VarQuote(str)
1256: const char *str;
1257: {
1258:
1259: BUFFER buf;
1260: /* This should cover most shells :-( */
1261: static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1.2 espie 1262:
1263: if (str == NULL)
1264: return NULL;
1.1 espie 1265:
1266: Buf_Init(&buf, MAKE_BSIZE);
1267: for (; *str; str++) {
1268: if (strchr(meta, *str) != NULL)
1269: Buf_AddChar(&buf, '\\');
1270: Buf_AddChar(&buf, *str);
1271: }
1272: return Buf_Retrieve(&buf);
1273: }
1274: /*-
1275: *-----------------------------------------------------------------------
1276: * Var_GetHead --
1277: * Find the leading components of a (list of) filename(s).
1278: * XXX: VarHead does not replace foo by ., as (sun) System V make
1279: * does.
1280: *
1281: * Results:
1282: * The leading components.
1283: *
1284: * Side Effects:
1285: * None.
1286: *
1287: *-----------------------------------------------------------------------
1288: */
1289: char *
1290: Var_GetHead(file)
1291: char *file; /* Filename to manipulate */
1292: {
1293: return VarModify(file, VarHead, NULL);
1294: }
1295:
1296: /*-
1297: *-----------------------------------------------------------------------
1298: * Var_GetTail --
1299: * Return the tail from each of a list of words. Used to set the
1300: * System V local variables.
1301: *
1302: * Results:
1303: * The resulting string.
1304: *
1305: * Side Effects:
1306: * None.
1307: *
1308: *-----------------------------------------------------------------------
1309: */
1310: char *
1311: Var_GetTail(file)
1312: char *file; /* Filename to modify */
1313: {
1314: return VarModify(file, VarTail, NULL);
1315: }
1316: