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