freebsd-dev/usr.bin/make/var_modify.c
2005-04-01 13:25:45 +00:00

573 lines
14 KiB
C

/*-
* Copyright (c) 2002 Juli Mallett.
* Copyright (c) 1988, 1989, 1990, 1993
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1989 by Berkeley Softworks
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Adam de Boor.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)var.c 8.3 (Berkeley) 3/19/94
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "buf.h"
#include "config.h"
#include "str.h"
#include "util.h"
#include "var.h"
/**
* VarHead
* Remove the tail of the given word and place the result in the given
* buffer.
*
* Results:
* TRUE if characters were added to the buffer (a space needs to be
* added to the buffer before the next word).
*
* Side Effects:
* The trimmed word is added to the buffer.
*/
Boolean
VarHead(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
{
char *slash;
slash = strrchr(word, '/');
if (slash != NULL) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
Buf_AppendRange(buf, word, slash);
} else {
/*
* If no directory part, give . (q.v. the POSIX standard)
*/
if (addSpace) {
Buf_Append(buf, " .");
} else {
Buf_AddByte(buf, (Byte)'.');
}
}
return (TRUE);
}
/**
* VarTail
* Remove the head of the given word and place the result in the given
* buffer.
*
* Results:
* TRUE if characters were added to the buffer (a space needs to be
* added to the buffer before the next word).
*
* Side Effects:
* The trimmed word is added to the buffer.
*/
Boolean
VarTail(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
{
const char *slash;
if (addSpace) {
Buf_AddByte (buf, (Byte)' ');
}
slash = strrchr(word, '/');
if (slash != NULL) {
slash++;
Buf_Append(buf, slash);
} else {
Buf_Append(buf, word);
}
return (TRUE);
}
/**
* VarSuffix
* Place the suffix of the given word in the given buffer.
*
* Results:
* TRUE if characters were added to the buffer (a space needs to be
* added to the buffer before the next word).
*
* Side Effects:
* The suffix from the word is placed in the buffer.
*/
Boolean
VarSuffix(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
{
const char *dot;
dot = strrchr(word, '.');
if (dot != NULL) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
dot++;
Buf_Append(buf, dot);
addSpace = TRUE;
}
return (addSpace);
}
/**
* VarRoot
* Remove the suffix of the given word and place the result in the
* buffer.
*
* Results:
* TRUE if characters were added to the buffer (a space needs to be
* added to the buffer before the next word).
*
* Side Effects:
* The trimmed word is added to the buffer.
*/
Boolean
VarRoot(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
{
char *dot;
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
dot = strrchr(word, '.');
if (dot != NULL) {
Buf_AppendRange(buf, word, dot);
} else {
Buf_Append(buf, word);
}
return (TRUE);
}
/**
* VarMatch
* Place the word in the buffer if it matches the given pattern.
* Callback function for VarModify to implement the :M modifier.
* A space will be added if requested. A pattern is supplied
* which the word must match.
*
* Results:
* TRUE if a space should be placed in the buffer before the next
* word.
*
* Side Effects:
* The word may be copied to the buffer.
*/
Boolean
VarMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern)
{
if (Str_Match(word, pattern)) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
Buf_Append(buf, word);
}
return (addSpace);
}
#ifdef SYSVVARSUB
/**
* VarSYSVMatch
* Place the word in the buffer if it matches the given pattern.
* Callback function for VarModify to implement the System V %
* modifiers. A space is added if requested.
*
* Results:
* TRUE if a space should be placed in the buffer before the next
* word.
*
* Side Effects:
* The word may be copied to the buffer.
*/
Boolean
VarSYSVMatch(const char *word, Boolean addSpace, Buffer *buf, void *patp)
{
int len;
const char *ptr;
VarPattern *pat = (VarPattern *)patp;
if (addSpace)
Buf_AddByte(buf, (Byte)' ');
addSpace = TRUE;
if ((ptr = Str_SYSVMatch(word, Buf_Data(pat->lhs), &len)) != NULL)
Str_SYSVSubst(buf, Buf_Data(pat->rhs), ptr, len);
else
Buf_Append(buf, word);
return (addSpace);
}
#endif
/**
* VarNoMatch
* Place the word in the buffer if it doesn't match the given pattern.
* Callback function for VarModify to implement the :N modifier. A
* space is added if requested.
*
* Results:
* TRUE if a space should be placed in the buffer before the next
* word.
*
* Side Effects:
* The word may be copied to the buffer.
*/
Boolean
VarNoMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern)
{
if (!Str_Match(word, pattern)) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
Buf_Append(buf, word);
}
return (addSpace);
}
/**
* VarSubstitute
* Perform a string-substitution on the given word, placing the
* result in the passed buffer. A space is added if requested.
*
* Results:
* TRUE if a space is needed before more characters are added.
*/
Boolean
VarSubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp)
{
size_t wordLen; /* Length of word */
const char *cp; /* General pointer */
VarPattern *pattern = patternp;
wordLen = strlen(word);
if (1) { /* substitute in each word of the variable */
/*
* Break substitution down into simple anchored cases
* and if none of them fits, perform the general substitution
* case.
*/
if ((pattern->flags & VAR_MATCH_START) &&
(strncmp(word, Buf_Data(pattern->lhs),
Buf_Size(pattern->lhs)) == 0)) {
/*
* Anchored at start and beginning of word matches
* pattern.
*/
if ((pattern->flags & VAR_MATCH_END) &&
(wordLen == Buf_Size(pattern->lhs))) {
/*
* Also anchored at end and matches to the end
* (word is same length as pattern) add space
* and rhs only if rhs is non-null.
*/
if (Buf_Size(pattern->rhs) != 0) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
Buf_AppendBuf(buf, pattern->rhs);
}
} else if (pattern->flags & VAR_MATCH_END) {
/*
* Doesn't match to end -- copy word wholesale
*/
goto nosub;
} else {
/*
* Matches at start but need to copy in
* trailing characters.
*/
if ((Buf_Size(pattern->rhs) + wordLen -
Buf_Size(pattern->lhs)) != 0) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
}
Buf_AppendBuf(buf, pattern->rhs);
Buf_AddBytes(buf, wordLen -
Buf_Size(pattern->lhs),
(word + Buf_Size(pattern->lhs)));
}
} else if (pattern->flags & VAR_MATCH_START) {
/*
* Had to match at start of word and didn't -- copy
* whole word.
*/
goto nosub;
} else if (pattern->flags & VAR_MATCH_END) {
/*
* Anchored at end, Find only place match could occur
* (leftLen characters from the end of the word) and
* see if it does. Note that because the $ will be
* left at the end of the lhs, we have to use strncmp.
*/
cp = word + (wordLen - Buf_Size(pattern->lhs));
if ((cp >= word) && (strncmp(cp, Buf_Data(pattern->lhs),
Buf_Size(pattern->lhs)) == 0)) {
/*
* Match found. If we will place characters in
* the buffer, add a space before hand as
* indicated by addSpace, then stuff in the
* initial, unmatched part of the word followed
* by the right-hand-side.
*/
if ((cp - word) + Buf_Size(pattern->rhs) != 0) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
}
Buf_AppendRange(buf, word, cp);
Buf_AppendBuf(buf, pattern->rhs);
} else {
/*
* Had to match at end and didn't. Copy entire
* word.
*/
goto nosub;
}
} else {
/*
* Pattern is unanchored: search for the pattern in the
* word using strstr(3), copying unmatched portions and
* the right-hand-side for each match found, handling
* non-global substitutions correctly, etc. When the
* loop is done, any remaining part of the word (word
* and wordLen are adjusted accordingly through the
* loop) is copied straight into the buffer.
* addSpace is set FALSE as soon as a space is added
* to the buffer.
*/
Boolean done;
size_t origSize;
done = FALSE;
origSize = Buf_Size(buf);
while (!done) {
cp = strstr(word, Buf_Data(pattern->lhs));
if (cp != NULL) {
if (addSpace && (((cp - word) +
Buf_Size(pattern->rhs)) != 0)) {
Buf_AddByte(buf, (Byte)' ');
addSpace = FALSE;
}
Buf_AppendRange(buf, word, cp);
Buf_AppendBuf(buf, pattern->rhs);
wordLen -= (cp - word) +
Buf_Size(pattern->lhs);
word = cp + Buf_Size(pattern->lhs);
if (wordLen == 0 || (pattern->flags &
VAR_SUB_GLOBAL) == 0) {
done = TRUE;
}
} else {
done = TRUE;
}
}
if (wordLen != 0) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
Buf_AddBytes(buf, wordLen, (const Byte *)word);
}
/*
* If added characters to the buffer, need to add a
* space before we add any more. If we didn't add any,
* just return the previous value of addSpace.
*/
return ((Buf_Size(buf) != origSize) || addSpace);
}
/*
* Common code for anchored substitutions:
* addSpace was set TRUE if characters were added to the buffer.
*/
return (addSpace);
}
nosub:
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
Buf_AddBytes(buf, wordLen, (const Byte *)word);
return (TRUE);
}
/**
* VarRESubstitute
* Perform a regex substitution on the given word, placing the
* result in the passed buffer. A space is added if requested.
*
* Results:
* TRUE if a space is needed before more characters are added.
*/
Boolean
VarRESubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp)
{
VarPattern *pat;
int xrv;
const char *wp;
char *rp;
int added;
int flags = 0;
#define MAYBE_ADD_SPACE() \
if (addSpace && !added) \
Buf_AddByte(buf, (Byte)' '); \
added = 1
added = 0;
wp = word;
pat = patternp;
if ((pat->flags & (VAR_SUB_ONE | VAR_SUB_MATCHED)) ==
(VAR_SUB_ONE | VAR_SUB_MATCHED)) {
xrv = REG_NOMATCH;
} else {
tryagain:
xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags);
}
switch (xrv) {
case 0:
pat->flags |= VAR_SUB_MATCHED;
if (pat->matches[0].rm_so > 0) {
MAYBE_ADD_SPACE();
Buf_AddBytes(buf, pat->matches[0].rm_so,
(const Byte *)wp);
}
for (rp = Buf_Data(pat->rhs); *rp; rp++) {
if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
MAYBE_ADD_SPACE();
Buf_AddByte(buf, (Byte)rp[1]);
rp++;
} else if ((*rp == '&') ||
((*rp == '\\') && isdigit((unsigned char)rp[1]))) {
int n;
const char *subbuf;
int sublen;
char errstr[3];
if (*rp == '&') {
n = 0;
errstr[0] = '&';
errstr[1] = '\0';
} else {
n = rp[1] - '0';
errstr[0] = '\\';
errstr[1] = rp[1];
errstr[2] = '\0';
rp++;
}
if (n > pat->nsub) {
Error("No subexpression %s",
&errstr[0]);
subbuf = "";
sublen = 0;
} else if ((pat->matches[n].rm_so == -1) &&
(pat->matches[n].rm_eo == -1)) {
Error("No match for subexpression %s",
&errstr[0]);
subbuf = "";
sublen = 0;
} else {
subbuf = wp + pat->matches[n].rm_so;
sublen = pat->matches[n].rm_eo -
pat->matches[n].rm_so;
}
if (sublen > 0) {
MAYBE_ADD_SPACE();
Buf_AddBytes(buf, sublen,
(const Byte *)subbuf);
}
} else {
MAYBE_ADD_SPACE();
Buf_AddByte(buf, (Byte)*rp);
}
}
wp += pat->matches[0].rm_eo;
if (pat->flags & VAR_SUB_GLOBAL) {
flags |= REG_NOTBOL;
if (pat->matches[0].rm_so == 0 &&
pat->matches[0].rm_eo == 0) {
MAYBE_ADD_SPACE();
Buf_AddByte(buf, (Byte)*wp);
wp++;
}
if (*wp)
goto tryagain;
}
if (*wp) {
MAYBE_ADD_SPACE();
Buf_Append(buf, wp);
}
break;
default:
VarREError(xrv, &pat->re, "Unexpected regex error");
/* fall through */
case REG_NOMATCH:
if (*wp) {
MAYBE_ADD_SPACE();
Buf_Append(buf, wp);
}
break;
}
return (addSpace || added);
}