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