f8764c29c01a2dac2c41251bc38444a1f1affdba
[dragonfly.git] / usr.bin / make / var_modify.c
1 /*
2  * Copyright (c) 2002 Juli Mallett.
3  * Copyright (c) 1988, 1989, 1990, 1993
4  *      The Regents of the University of California.  All rights reserved.
5  * Copyright (c) 1989 by Berkeley Softworks
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Adam de Boor.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  * @(#)var.c    8.3 (Berkeley) 3/19/94
40  * $FreeBSD: src/usr.bin/make/var.c,v 1.16.2.3 2002/02/27 14:18:57 cjc Exp $
41  * $DragonFly: src/usr.bin/make/Attic/var_modify.c,v 1.20 2005/01/31 09:49:14 okumoto Exp $
42  */
43
44 #include <ctype.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #include "buf.h"
49 #include "config.h"
50 #include "str.h"
51 #include "util.h"
52 #include "var.h"
53
54 /*-
55  *-----------------------------------------------------------------------
56  * VarHead --
57  *      Remove the tail of the given word and place the result in the given
58  *      buffer.
59  *
60  * Results:
61  *      TRUE if characters were added to the buffer (a space needs to be
62  *      added to the buffer before the next word).
63  *
64  * Side Effects:
65  *      The trimmed word is added to the buffer.
66  *
67  *-----------------------------------------------------------------------
68  */
69 Boolean
70 VarHead(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
71 {
72     char *slash;
73
74     slash = strrchr(word, '/');
75     if (slash != NULL) {
76         if (addSpace) {
77             Buf_AddByte(buf, (Byte)' ');
78         }
79         Buf_AppendRange(buf, word, slash);
80     } else {
81         /*
82          * If no directory part, give . (q.v. the POSIX standard)
83          */
84         if (addSpace) {
85             Buf_Append(buf, " .");
86         } else {
87             Buf_AddByte(buf, (Byte)'.');
88         }
89     }
90     return (TRUE);
91 }
92
93 /*-
94  *-----------------------------------------------------------------------
95  * VarTail --
96  *      Remove the head of the given word and place the result in the given
97  *      buffer.
98  *
99  * Results:
100  *      TRUE if characters were added to the buffer (a space needs to be
101  *      added to the buffer before the next word).
102  *
103  * Side Effects:
104  *      The trimmed word is added to the buffer.
105  *
106  *-----------------------------------------------------------------------
107  */
108 Boolean
109 VarTail(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
110 {
111     const char *slash;
112
113     if (addSpace) {
114         Buf_AddByte (buf, (Byte)' ');
115     }
116
117     slash = strrchr(word, '/');
118     if (slash != NULL) {
119         slash++;
120         Buf_Append(buf, slash);
121     } else {
122         Buf_Append(buf, word);
123     }
124     return (TRUE);
125 }
126
127 /*-
128  *-----------------------------------------------------------------------
129  * VarSuffix --
130  *      Place the suffix of the given word in the given buffer.
131  *
132  * Results:
133  *      TRUE if characters were added to the buffer (a space needs to be
134  *      added to the buffer before the next word).
135  *
136  * Side Effects:
137  *      The suffix from the word is placed in the buffer.
138  *
139  *-----------------------------------------------------------------------
140  */
141 Boolean
142 VarSuffix(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
143 {
144     const char *dot;
145
146     dot = strrchr(word, '.');
147     if (dot != NULL) {
148         if (addSpace) {
149             Buf_AddByte(buf, (Byte)' ');
150         }
151         dot++;
152         Buf_Append(buf, dot);
153         addSpace = TRUE;
154     }
155     return (addSpace);
156 }
157
158 /*-
159  *-----------------------------------------------------------------------
160  * VarRoot --
161  *      Remove the suffix of the given word and place the result in the
162  *      buffer.
163  *
164  * Results:
165  *      TRUE if characters were added to the buffer (a space needs to be
166  *      added to the buffer before the next word).
167  *
168  * Side Effects:
169  *      The trimmed word is added to the buffer.
170  *
171  *-----------------------------------------------------------------------
172  */
173 Boolean
174 VarRoot(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
175 {
176     char *dot;
177
178     if (addSpace) {
179         Buf_AddByte(buf, (Byte)' ');
180     }
181
182     dot = strrchr(word, '.');
183     if (dot != NULL) {
184         Buf_AppendRange(buf, word, dot);
185     } else {
186         Buf_Append(buf, word);
187     }
188     return (TRUE);
189 }
190
191 /*-
192  *-----------------------------------------------------------------------
193  * VarMatch --
194  *      Place the word in the buffer if it matches the given pattern.
195  *      Callback function for VarModify to implement the :M modifier.
196  *      A space will be added if requested.  A pattern is supplied
197  *      which the word must match.
198  *
199  * Results:
200  *      TRUE if a space should be placed in the buffer before the next
201  *      word.
202  *
203  * Side Effects:
204  *      The word may be copied to the buffer.
205  *
206  *-----------------------------------------------------------------------
207  */
208 Boolean
209 VarMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern)
210 {
211
212     if (Str_Match(word, pattern)) {
213         if (addSpace) {
214             Buf_AddByte(buf, (Byte)' ');
215         }
216         addSpace = TRUE;
217         Buf_Append(buf, word);
218     }
219     return (addSpace);
220 }
221
222 #ifdef SYSVVARSUB
223 /*-
224  *-----------------------------------------------------------------------
225  * VarSYSVMatch --
226  *      Place the word in the buffer if it matches the given pattern.
227  *      Callback function for VarModify to implement the System V %
228  *      modifiers.  A space is added if requested.
229  *
230  * Results:
231  *      TRUE if a space should be placed in the buffer before the next
232  *      word.
233  *
234  * Side Effects:
235  *      The word may be copied to the buffer.
236  *
237  *-----------------------------------------------------------------------
238  */
239 Boolean
240 VarSYSVMatch(const char *word, Boolean addSpace, Buffer *buf, void *patp)
241 {
242     int len;
243     const char *ptr;
244     VarPattern    *pat = (VarPattern *)patp;
245
246     if (addSpace)
247         Buf_AddByte(buf, (Byte)' ');
248
249     addSpace = TRUE;
250
251     if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL)
252         Str_SYSVSubst(buf, pat->rhs, ptr, len);
253     else
254         Buf_Append(buf, word);
255
256     return (addSpace);
257 }
258 #endif
259
260
261 /*-
262  *-----------------------------------------------------------------------
263  * VarNoMatch --
264  *      Place the word in the buffer if it doesn't match the given pattern.
265  *      Callback function for VarModify to implement the :N modifier.  A
266  *      space is added if requested.
267  *
268  * Results:
269  *      TRUE if a space should be placed in the buffer before the next
270  *      word.
271  *
272  * Side Effects:
273  *      The word may be copied to the buffer.
274  *
275  *-----------------------------------------------------------------------
276  */
277 Boolean
278 VarNoMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern)
279 {
280
281     if (!Str_Match(word, pattern)) {
282         if (addSpace) {
283             Buf_AddByte(buf, (Byte)' ');
284         }
285         addSpace = TRUE;
286         Buf_Append(buf, word);
287     }
288     return (addSpace);
289 }
290
291
292 /*-
293  *-----------------------------------------------------------------------
294  * VarSubstitute --
295  *      Perform a string-substitution on the given word, placing the
296  *      result in the passed buffer.  A space is added if requested.
297  *
298  * Results:
299  *      TRUE if a space is needed before more characters are added.
300  *
301  * Side Effects:
302  *      None.
303  *
304  *-----------------------------------------------------------------------
305  */
306 Boolean
307 VarSubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp)
308 {
309     size_t              wordLen;    /* Length of word */
310     const char          *cp;        /* General pointer */
311     VarPattern  *pattern = patternp;
312
313     wordLen = strlen(word);
314     if (1) { /* substitute in each word of the variable */
315         /*
316          * Break substitution down into simple anchored cases
317          * and if none of them fits, perform the general substitution case.
318          */
319         if ((pattern->flags & VAR_MATCH_START) &&
320             (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) {
321                 /*
322                  * Anchored at start and beginning of word matches pattern
323                  */
324                 if ((pattern->flags & VAR_MATCH_END) &&
325                     (wordLen == pattern->leftLen)) {
326                         /*
327                          * Also anchored at end and matches to the end (word
328                          * is same length as pattern) add space and rhs only
329                          * if rhs is non-null.
330                          */
331                         if (pattern->rightLen != 0) {
332                             if (addSpace) {
333                                 Buf_AddByte(buf, (Byte)' ');
334                             }
335                             addSpace = TRUE;
336                             Buf_AddBytes(buf, pattern->rightLen,
337                                          (Byte *)pattern->rhs);
338                         }
339                 } else if (pattern->flags & VAR_MATCH_END) {
340                     /*
341                      * Doesn't match to end -- copy word wholesale
342                      */
343                     goto nosub;
344                 } else {
345                     /*
346                      * Matches at start but need to copy in trailing characters
347                      */
348                     if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
349                         if (addSpace) {
350                             Buf_AddByte(buf, (Byte)' ');
351                         }
352                         addSpace = TRUE;
353                     }
354                     Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
355                     Buf_AddBytes(buf, wordLen - pattern->leftLen,
356                                  (const Byte *)(word + pattern->leftLen));
357                 }
358         } else if (pattern->flags & VAR_MATCH_START) {
359             /*
360              * Had to match at start of word and didn't -- copy whole word.
361              */
362             goto nosub;
363         } else if (pattern->flags & VAR_MATCH_END) {
364             /*
365              * Anchored at end, Find only place match could occur (leftLen
366              * characters from the end of the word) and see if it does. Note
367              * that because the $ will be left at the end of the lhs, we have
368              * to use strncmp.
369              */
370             cp = word + (wordLen - pattern->leftLen);
371             if ((cp >= word) &&
372                 (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) {
373                 /*
374                  * Match found. If we will place characters in the buffer,
375                  * add a space before hand as indicated by addSpace, then
376                  * stuff in the initial, unmatched part of the word followed
377                  * by the right-hand-side.
378                  */
379                 if (((cp - word) + pattern->rightLen) != 0) {
380                     if (addSpace) {
381                         Buf_AddByte(buf, (Byte)' ');
382                     }
383                     addSpace = TRUE;
384                 }
385                 Buf_AppendRange(buf, word, cp);
386                 Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
387             } else {
388                 /*
389                  * Had to match at end and didn't. Copy entire word.
390                  */
391                 goto nosub;
392             }
393         } else {
394             /*
395              * Pattern is unanchored: search for the pattern in the word using
396              * strstr(3), copying unmatched portions and the
397              * right-hand-side for each match found, handling non-global
398              * substitutions correctly, etc. When the loop is done, any
399              * remaining part of the word (word and wordLen are adjusted
400              * accordingly through the loop) is copied straight into the
401              * buffer.
402              * addSpace is set FALSE as soon as a space is added to the
403              * buffer.
404              */
405             Boolean done;
406             size_t origSize;
407
408             done = FALSE;
409             origSize = Buf_Size(buf);
410             while (!done) {
411                 cp = strstr(word, pattern->lhs);
412                 if (cp != NULL) {
413                     if (addSpace && (((cp - word) + pattern->rightLen) != 0)){
414                         Buf_AddByte(buf, (Byte)' ');
415                         addSpace = FALSE;
416                     }
417                     Buf_AppendRange(buf, word, cp);
418                     Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
419                     wordLen -= (cp - word) + pattern->leftLen;
420                     word = cp + pattern->leftLen;
421                     if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0){
422                         done = TRUE;
423                     }
424                 } else {
425                     done = TRUE;
426                 }
427             }
428             if (wordLen != 0) {
429                 if (addSpace) {
430                     Buf_AddByte(buf, (Byte)' ');
431                 }
432                 Buf_AddBytes(buf, wordLen, (const Byte *)word);
433             }
434             /*
435              * If added characters to the buffer, need to add a space
436              * before we add any more. If we didn't add any, just return
437              * the previous value of addSpace.
438              */
439             return ((Buf_Size(buf) != origSize) || addSpace);
440         }
441         /*
442          * Common code for anchored substitutions:
443          * addSpace was set TRUE if characters were added to the buffer.
444          */
445         return (addSpace);
446     }
447  nosub:
448     if (addSpace) {
449         Buf_AddByte(buf, (Byte)' ');
450     }
451     Buf_AddBytes(buf, wordLen, (const Byte *)word);
452     return (TRUE);
453 }
454
455 /*-
456  *-----------------------------------------------------------------------
457  * VarRESubstitute --
458  *      Perform a regex substitution on the given word, placing the
459  *      result in the passed buffer.  A space is added if requested.
460  *
461  * Results:
462  *      TRUE if a space is needed before more characters are added.
463  *
464  * Side Effects:
465  *      None.
466  *
467  *-----------------------------------------------------------------------
468  */
469 Boolean
470 VarRESubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp)
471 {
472     VarREPattern *pat;
473     int xrv;
474     const char *wp;
475     char *rp;
476     int added;
477     int flags = 0;
478
479 #define MAYBE_ADD_SPACE()                       \
480         if (addSpace && !added)                 \
481             Buf_AddByte(buf, (Byte)' ');        \
482         added = 1
483
484     added = 0;
485     wp = word;
486     pat = patternp;
487
488     if ((pat->flags & (VAR_SUB_ONE | VAR_SUB_MATCHED)) ==
489         (VAR_SUB_ONE | VAR_SUB_MATCHED))
490         xrv = REG_NOMATCH;
491     else {
492     tryagain:
493         xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags);
494     }
495
496     switch (xrv) {
497     case 0:
498         pat->flags |= VAR_SUB_MATCHED;
499         if (pat->matches[0].rm_so > 0) {
500             MAYBE_ADD_SPACE();
501             Buf_AddBytes(buf, pat->matches[0].rm_so, (const Byte *)wp);
502         }
503
504         for (rp = pat->replace; *rp; rp++) {
505             if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
506                 MAYBE_ADD_SPACE();
507                 Buf_AddByte(buf, (Byte)rp[1]);
508                 rp++;
509             }
510             else if ((*rp == '&') ||
511                 ((*rp == '\\') && isdigit((unsigned char)rp[1]))) {
512                 int n;
513                 const char *subbuf;
514                 int sublen;
515                 char errstr[3];
516
517                 if (*rp == '&') {
518                     n = 0;
519                     errstr[0] = '&';
520                     errstr[1] = '\0';
521                 } else {
522                     n = rp[1] - '0';
523                     errstr[0] = '\\';
524                     errstr[1] = rp[1];
525                     errstr[2] = '\0';
526                     rp++;
527                 }
528
529                 if (n > pat->nsub) {
530                     Error("No subexpression %s", &errstr[0]);
531                     subbuf = "";
532                     sublen = 0;
533                 } else if ((pat->matches[n].rm_so == -1) &&
534                            (pat->matches[n].rm_eo == -1)) {
535                     Error("No match for subexpression %s", &errstr[0]);
536                     subbuf = "";
537                     sublen = 0;
538                 } else {
539                     subbuf = wp + pat->matches[n].rm_so;
540                     sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so;
541                 }
542
543                 if (sublen > 0) {
544                     MAYBE_ADD_SPACE();
545                     Buf_AddBytes(buf, sublen, (const Byte *)subbuf);
546                 }
547             } else {
548                 MAYBE_ADD_SPACE();
549                 Buf_AddByte(buf, (Byte)*rp);
550             }
551         }
552         wp += pat->matches[0].rm_eo;
553         if (pat->flags & VAR_SUB_GLOBAL) {
554             flags |= REG_NOTBOL;
555             if (pat->matches[0].rm_so == 0 && pat->matches[0].rm_eo == 0) {
556                 MAYBE_ADD_SPACE();
557                 Buf_AddByte(buf, (Byte)*wp);
558                 wp++;
559
560             }
561             if (*wp)
562                 goto tryagain;
563         }
564         if (*wp) {
565             MAYBE_ADD_SPACE();
566             Buf_Append(buf, wp);
567         }
568         break;
569     default:
570         VarREError(xrv, &pat->re, "Unexpected regex error");
571        /* fall through */
572     case REG_NOMATCH:
573         if (*wp) {
574             MAYBE_ADD_SPACE();
575             Buf_Append(buf, wp);
576         }
577         break;
578     }
579     return (addSpace || added);
580 }
581