Split var.c into var.c and var_modify.c and move all the modification funcs

to var_modify.c, for readability.  constify some low hanging fruit (string
manipulation functions) and the upper layers appropriately.  No longer use
the private strstr(3) implementation, while changing string code.

Tested by:      lots of successful make buildworld.
This commit is contained in:
Juli Mallett 2002-10-28 23:33:57 +00:00
parent 89331f114e
commit 85779f0ead
7 changed files with 731 additions and 669 deletions

View file

@ -5,7 +5,7 @@
PROG= make
CFLAGS+=-I${.CURDIR}
SRCS= arch.c buf.c compat.c cond.c dir.c for.c hash.c job.c main.c \
make.c parse.c str.c suff.c targ.c util.c var.c
make.c parse.c str.c suff.c targ.c util.c var.c var_modify.c
SRCS+= lstAppend.c lstAtEnd.c lstAtFront.c lstClose.c lstConcat.c \
lstDatum.c lstDeQueue.c lstDestroy.c lstDupl.c lstEnQueue.c \
lstFind.c lstFindFrom.c lstFirst.c lstForEach.c lstForEachFrom.c \

View file

@ -1877,7 +1877,7 @@ JobOutput(Job *job, char *cp, char *endp, int msg)
char *ecp;
if (commandShell->noPrint) {
ecp = Str_FindSubstring(cp, commandShell->noPrint);
ecp = strstr(cp, commandShell->noPrint);
while (ecp != NULL) {
if (cp != ecp) {
*ecp = '\0';
@ -1905,7 +1905,7 @@ JobOutput(Job *job, char *cp, char *endp, int msg)
while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
cp++;
}
ecp = Str_FindSubstring(cp, commandShell->noPrint);
ecp = strstr(cp, commandShell->noPrint);
} else {
return cp;
}

View file

@ -94,10 +94,9 @@ void str_init(void);
void str_end(void);
char *str_concat(char *, char *, int);
char **brk_string(char *, int *, Boolean);
char *Str_FindSubstring(char *, char *);
int Str_Match(char *, char *);
char *Str_SYSVMatch(char *, char *, int *len);
void Str_SYSVSubst(Buffer, char *, char *, int);
int Str_Match(const char *, const char *);
const char *Str_SYSVMatch(const char *, const char *, int *);
void Str_SYSVSubst(Buffer, const char *, const char *, int);
/* suff.c */
void Suff_ClearSuffixes(void);

View file

@ -252,44 +252,6 @@ done: argv[argc] = (char *)NULL;
return(argv);
}
/*
* Str_FindSubstring -- See if a string contains a particular substring.
*
* Results: If string contains substring, the return value is the location of
* the first matching instance of substring in string. If string doesn't
* contain substring, the return value is NULL. Matching is done on an exact
* character-for-character basis with no wildcards or special characters.
*
* Side effects: None.
*
* XXX should be strstr(3).
*/
char *
Str_FindSubstring(char *string, char *substring)
{
char *a, *b;
/*
* First scan quickly through the two strings looking for a single-
* character match. When it's found, then compare the rest of the
* substring.
*/
for (b = substring; *string != 0; string += 1) {
if (*string != *b)
continue;
a = string;
for (;;) {
if (*b == 0)
return(string);
if (*a++ != *b++)
break;
}
b = substring;
}
return((char *) NULL);
}
/*
* Str_Match --
*
@ -302,7 +264,7 @@ Str_FindSubstring(char *string, char *substring)
* Side effects: None.
*/
int
Str_Match(char *string, char *pattern)
Str_Match(const char *string, const char *pattern)
{
char c2;
@ -404,12 +366,13 @@ thisCharOK: ++pattern;
*
*-----------------------------------------------------------------------
*/
char *
Str_SYSVMatch(char *word, char *pattern, int *len)
const char *
Str_SYSVMatch(const char *word, const char *pattern, int *len)
{
char *p = pattern;
char *w = word;
char *m;
const char *m, *p, *w;
p = pattern;
w = word;
if (*w == '\0') {
/* Zero-length word cannot be matched against */
@ -468,9 +431,9 @@ Str_SYSVMatch(char *word, char *pattern, int *len)
*-----------------------------------------------------------------------
*/
void
Str_SYSVSubst(Buffer buf, char *pat, char *src, int len)
Str_SYSVSubst(Buffer buf, const char *pat, const char *src, int len)
{
char *m;
const char *m;
if ((m = strchr(pat, '%')) != NULL) {
/* Copy the prefix */

View file

@ -89,6 +89,7 @@ __FBSDID("$FreeBSD$");
#include <stdlib.h>
#include "make.h"
#include "buf.h"
#include "var.h"
/*
* This is a harmless return value for Var_Parse that can be used by Var_Subst
@ -129,64 +130,16 @@ static Lst allVars; /* List of all variables */
#define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */
#define FIND_ENV 0x4 /* look in the environment also */
typedef struct Var {
char *name; /* the variable's name */
Buffer val; /* its value */
int flags; /* miscellaneous status flags */
#define VAR_IN_USE 1 /* Variable's value currently being used.
* Used to avoid recursion */
#define VAR_FROM_ENV 2 /* Variable comes from the environment */
#define VAR_JUNK 4 /* Variable is a junk variable that
* should be destroyed when done with
* it. Used by Var_Parse for undefined,
* modified variables */
} Var;
/* Var*Pattern flags */
#define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */
#define VAR_SUB_ONE 0x02 /* Apply substitution to one word */
#define VAR_SUB_MATCHED 0x04 /* There was a match */
#define VAR_MATCH_START 0x08 /* Match at start of word */
#define VAR_MATCH_END 0x10 /* Match at end of word */
#define VAR_NOSUBST 0x20 /* don't expand vars in VarGetPattern */
typedef struct {
char *lhs; /* String to match */
int leftLen; /* Length of string */
char *rhs; /* Replacement string (w/ &'s removed) */
int rightLen; /* Length of replacement */
int flags;
} VarPattern;
typedef struct {
regex_t re;
int nsub;
regmatch_t *matches;
char *replace;
int flags;
} VarREPattern;
static int VarCmp(void *, void *);
static void VarPossiblyExpand(char **, GNode *);
static Var *VarFind(char *, GNode *, int);
static void VarAdd(char *, char *, GNode *);
static void VarDelete(void *);
static Boolean VarHead(char *, Boolean, Buffer, void *);
static Boolean VarTail(char *, Boolean, Buffer, void *);
static Boolean VarSuffix(char *, Boolean, Buffer, void *);
static Boolean VarRoot(char *, Boolean, Buffer, void *);
static Boolean VarMatch(char *, Boolean, Buffer, void *);
#ifdef SYSVVARSUB
static Boolean VarSYSVMatch(char *, Boolean, Buffer, void *);
#endif
static Boolean VarNoMatch(char *, Boolean, Buffer, void *);
static void VarREError(int, regex_t *, const char *);
static Boolean VarRESubstitute(char *, Boolean, Buffer, void *);
static Boolean VarSubstitute(char *, Boolean, Buffer, void *);
static char *VarGetPattern(GNode *, int, char **, int, int *, int *,
VarPattern *);
static char *VarQuote(char *);
static char *VarModify(char *, Boolean (*)(char *, Boolean, Buffer, void *),
static char *VarQuote(const char *);
static char *VarModify(char *,
Boolean (*)(const char *, Boolean, Buffer, void *),
void *);
static int VarPrintVar(void *, void *);
@ -614,567 +567,6 @@ Var_Value (char *name, GNode *ctxt, char **frp)
}
}
/*-
*-----------------------------------------------------------------------
* 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.
*
*-----------------------------------------------------------------------
*/
static Boolean
VarHead (char *word, Boolean addSpace, Buffer buf, void *dummy __unused)
{
char *slash;
slash = strrchr (word, '/');
if (slash != (char *)NULL) {
if (addSpace) {
Buf_AddByte (buf, (Byte)' ');
}
*slash = '\0';
Buf_AddBytes (buf, strlen (word), (Byte *)word);
*slash = '/';
return (TRUE);
} else {
/*
* If no directory part, give . (q.v. the POSIX standard)
*/
if (addSpace) {
Buf_AddBytes(buf, 2, (Byte *)" .");
} 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.
*
*-----------------------------------------------------------------------
*/
static Boolean
VarTail (char *word, Boolean addSpace, Buffer buf, void *dummy __unused)
{
char *slash;
if (addSpace) {
Buf_AddByte (buf, (Byte)' ');
}
slash = strrchr (word, '/');
if (slash != (char *)NULL) {
*slash++ = '\0';
Buf_AddBytes (buf, strlen(slash), (Byte *)slash);
slash[-1] = '/';
} else {
Buf_AddBytes (buf, strlen(word), (Byte *)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.
*
*-----------------------------------------------------------------------
*/
static Boolean
VarSuffix (char *word, Boolean addSpace, Buffer buf, void *dummy __unused)
{
char *dot;
dot = strrchr (word, '.');
if (dot != (char *)NULL) {
if (addSpace) {
Buf_AddByte (buf, (Byte)' ');
}
*dot++ = '\0';
Buf_AddBytes (buf, strlen (dot), (Byte *)dot);
dot[-1] = '.';
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.
*
*-----------------------------------------------------------------------
*/
static Boolean
VarRoot (char *word, Boolean addSpace, Buffer buf, void *dummy __unused)
{
char *dot;
if (addSpace) {
Buf_AddByte (buf, (Byte)' ');
}
dot = strrchr (word, '.');
if (dot != (char *)NULL) {
*dot = '\0';
Buf_AddBytes (buf, strlen (word), (Byte *)word);
*dot = '.';
} else {
Buf_AddBytes (buf, strlen(word), (Byte *)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.
*
*-----------------------------------------------------------------------
*/
static Boolean
VarMatch (char *word, Boolean addSpace, Buffer buf, void *pattern)
{
if (Str_Match(word, (char *) pattern)) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
Buf_AddBytes(buf, strlen(word), (Byte *)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.
*
*-----------------------------------------------------------------------
*/
static Boolean
VarSYSVMatch (char *word, Boolean addSpace, Buffer buf, void *patp)
{
int len;
char *ptr;
VarPattern *pat = (VarPattern *) patp;
if (addSpace)
Buf_AddByte(buf, (Byte)' ');
addSpace = TRUE;
if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL)
Str_SYSVSubst(buf, pat->rhs, ptr, len);
else
Buf_AddBytes(buf, strlen(word), (Byte *) 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.
*
*-----------------------------------------------------------------------
*/
static Boolean
VarNoMatch (char *word, Boolean addSpace, Buffer buf, void *pattern)
{
if (!Str_Match(word, (char *) pattern)) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
Buf_AddBytes(buf, strlen(word), (Byte *)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.
*
* Side Effects:
* None.
*
*-----------------------------------------------------------------------
*/
static Boolean
VarSubstitute (char *word, Boolean addSpace, Buffer buf, void *patternp)
{
int wordLen; /* Length of word */
char *cp; /* General pointer */
VarPattern *pattern = (VarPattern *) 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, pattern->lhs, pattern->leftLen) == 0)) {
/*
* Anchored at start and beginning of word matches pattern
*/
if ((pattern->flags & VAR_MATCH_END) &&
(wordLen == pattern->leftLen)) {
/*
* 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 (pattern->rightLen != 0) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
Buf_AddBytes(buf, pattern->rightLen,
(Byte *)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 ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
}
Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
Buf_AddBytes(buf, wordLen - pattern->leftLen,
(Byte *)(word + pattern->leftLen));
}
} 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 - pattern->leftLen);
if ((cp >= word) &&
(strncmp(cp, pattern->lhs, pattern->leftLen) == 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) + pattern->rightLen) != 0) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
}
Buf_AddBytes(buf, cp - word, (Byte *)word);
Buf_AddBytes(buf, pattern->rightLen, (Byte *)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
* String_FindSubstring, 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;
int origSize;
done = FALSE;
origSize = Buf_Size(buf);
while (!done) {
cp = Str_FindSubstring(word, pattern->lhs);
if (cp != (char *)NULL) {
if (addSpace && (((cp - word) + pattern->rightLen) != 0)){
Buf_AddByte(buf, (Byte)' ');
addSpace = FALSE;
}
Buf_AddBytes(buf, cp-word, (Byte *)word);
Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
wordLen -= (cp - word) + pattern->leftLen;
word = cp + pattern->leftLen;
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, (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, (Byte *)word);
return(TRUE);
}
/*-
*-----------------------------------------------------------------------
* VarREError --
* Print the error caused by a regcomp or regexec call.
*
* Results:
* None.
*
* Side Effects:
* An error gets printed.
*
*-----------------------------------------------------------------------
*/
static void
VarREError(int err, regex_t *pat, const char *str)
{
char *errbuf;
int errlen;
errlen = regerror(err, pat, 0, 0);
errbuf = emalloc(errlen);
regerror(err, pat, errbuf, errlen);
Error("%s: %s", str, errbuf);
free(errbuf);
}
/*-
*-----------------------------------------------------------------------
* 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.
*
* Side Effects:
* None.
*
*-----------------------------------------------------------------------
*/
static Boolean
VarRESubstitute(char *word, Boolean addSpace, Buffer buf, void *patternp)
{
VarREPattern *pat;
int xrv;
char *wp;
char *rp;
int added;
int flags = 0;
#define MAYBE_ADD_SPACE() \
if (addSpace && !added) \
Buf_AddByte(buf, ' '); \
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, wp);
}
for (rp = pat->replace; *rp; rp++) {
if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
MAYBE_ADD_SPACE();
Buf_AddByte(buf,rp[1]);
rp++;
}
else if ((*rp == '&') ||
((*rp == '\\') && isdigit((unsigned char)rp[1]))) {
int n;
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, subbuf);
}
} else {
MAYBE_ADD_SPACE();
Buf_AddByte(buf, *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, *wp);
wp++;
}
if (*wp)
goto tryagain;
}
if (*wp) {
MAYBE_ADD_SPACE();
Buf_AddBytes(buf, strlen(wp), wp);
}
break;
default:
VarREError(xrv, &pat->re, "Unexpected regex error");
/* fall through */
case REG_NOMATCH:
if (*wp) {
MAYBE_ADD_SPACE();
Buf_AddBytes(buf,strlen(wp),wp);
}
break;
}
return(addSpace||added);
}
/*-
*-----------------------------------------------------------------------
* VarModify --
@ -1190,7 +582,7 @@ VarRESubstitute(char *word, Boolean addSpace, Buffer buf, void *patternp)
*-----------------------------------------------------------------------
*/
static char *
VarModify (char *str, Boolean (*modProc)(char *, Boolean, Buffer, void *),
VarModify (char *str, Boolean (*modProc)(const char *, Boolean, Buffer, void *),
void *datum)
{
Buffer buf; /* Buffer for the new string */
@ -1351,12 +743,13 @@ VarGetPattern(GNode *ctxt, int err, char **tstr, int delim, int *flags,
*-----------------------------------------------------------------------
*/
static char *
VarQuote(char *str)
VarQuote(const char *str)
{
Buffer buf;
/* This should cover most shells :-( */
static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
char *ret;
buf = Buf_Init (MAKE_BSIZE);
for (; *str; str++) {
@ -1365,11 +758,38 @@ VarQuote(char *str)
Buf_AddByte(buf, (Byte)*str);
}
Buf_AddByte(buf, (Byte) '\0');
str = (char *)Buf_GetAll (buf, (int *)NULL);
ret = Buf_GetAll (buf, NULL);
Buf_Destroy (buf, FALSE);
return str;
return ret;
}
/*-
*-----------------------------------------------------------------------
* VarREError --
* Print the error caused by a regcomp or regexec call.
*
* Results:
* None.
*
* Side Effects:
* An error gets printed.
*
*-----------------------------------------------------------------------
*/
void
VarREError(int err, regex_t *pat, const char *str)
{
char *errbuf;
int errlen;
errlen = regerror(err, pat, 0, 0);
errbuf = emalloc(errlen);
regerror(err, pat, errbuf, errlen);
Error("%s: %s", str, errbuf);
free(errbuf);
}
/*-
*-----------------------------------------------------------------------
* Var_Parse --

97
usr.bin/make/var.h Normal file
View file

@ -0,0 +1,97 @@
/*-
* 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.
*
* $FreeBSD$
*/
typedef struct Var {
char *name; /* the variable's name */
Buffer val; /* its value */
int flags; /* miscellaneous status flags */
#define VAR_IN_USE 1 /* Variable's value currently being used.
* Used to avoid recursion */
#define VAR_FROM_ENV 2 /* Variable comes from the environment */
#define VAR_JUNK 4 /* Variable is a junk variable that
* should be destroyed when done with
* it. Used by Var_Parse for undefined,
* modified variables */
} Var;
/* Var*Pattern flags */
#define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */
#define VAR_SUB_ONE 0x02 /* Apply substitution to one word */
#define VAR_SUB_MATCHED 0x04 /* There was a match */
#define VAR_MATCH_START 0x08 /* Match at start of word */
#define VAR_MATCH_END 0x10 /* Match at end of word */
#define VAR_NOSUBST 0x20 /* don't expand vars in VarGetPattern */
typedef struct {
char *lhs; /* String to match */
int leftLen; /* Length of string */
char *rhs; /* Replacement string (w/ &'s removed) */
int rightLen; /* Length of replacement */
int flags;
} VarPattern;
typedef struct {
regex_t re;
int nsub;
regmatch_t *matches;
char *replace;
int flags;
} VarREPattern;
/*
* var.c
*/
void VarREError(int, regex_t *, const char *);
/*
* var_modify.c
*/
Boolean VarHead(const char *, Boolean, Buffer, void *);
Boolean VarTail(const char *, Boolean, Buffer, void *);
Boolean VarSuffix(const char *, Boolean, Buffer, void *);
Boolean VarRoot(const char *, Boolean, Buffer, void *);
Boolean VarMatch(const char *, Boolean, Buffer, void *);
#ifdef SYSVVARSUB
Boolean VarSYSVMatch(const char *, Boolean, Buffer, void *);
#endif
Boolean VarNoMatch(const char *, Boolean, Buffer, void *);
Boolean VarRESubstitute(const char *, Boolean, Buffer, void *);
Boolean VarSubstitute(const char *, Boolean, Buffer, void *);

583
usr.bin/make/var_modify.c Normal file
View file

@ -0,0 +1,583 @@
/*
* 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 <sys/types.h>
#include <regex.h>
#include <stdlib.h>
#include "make.h"
#include "buf.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;
char *buffer;
buffer = estrdup(word);
slash = strrchr (buffer, '/');
if (slash != NULL) {
if (addSpace) {
Buf_AddByte (buf, (Byte)' ');
}
*slash = '\0';
Buf_AddBytes (buf, strlen (word), (Byte *)word);
free(buffer);
return (TRUE);
} else {
/*
* If no directory part, give . (q.v. the POSIX standard)
*/
if (addSpace) {
Buf_AddBytes(buf, 2, (Byte *)" .");
} else {
Buf_AddByte(buf, (Byte)'.');
}
}
free(buffer);
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) {
Buf_AddBytes (buf, strlen(slash), (Byte *)slash);
} else {
Buf_AddBytes (buf, strlen(word), (Byte *)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++ != (char *)NULL) {
if (addSpace) {
Buf_AddByte (buf, (Byte)' ');
}
Buf_AddBytes (buf, strlen (dot), (Byte *)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 *buffer;
char *dot;
if (addSpace) {
Buf_AddByte (buf, (Byte)' ');
}
buffer = estrdup(word);
dot = strrchr (buffer, '.');
if (dot != NULL) {
*dot = '\0';
}
Buf_AddBytes (buf, strlen(buffer), (Byte *)buffer);
free(buffer);
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_AddBytes(buf, strlen(word), 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, pat->lhs, &len)) != NULL)
Str_SYSVSubst(buf, pat->rhs, ptr, len);
else
Buf_AddBytes(buf, strlen(word), (Byte *) 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_AddBytes(buf, strlen(word), (Byte *)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.
*
* Side Effects:
* None.
*
*-----------------------------------------------------------------------
*/
Boolean
VarSubstitute (const char *word, Boolean addSpace, Buffer buf, void *patternp)
{
int wordLen; /* Length of word */
const char *cp; /* General pointer */
VarPattern *pattern = (VarPattern *) 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, pattern->lhs, pattern->leftLen) == 0)) {
/*
* Anchored at start and beginning of word matches pattern
*/
if ((pattern->flags & VAR_MATCH_END) &&
(wordLen == pattern->leftLen)) {
/*
* 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 (pattern->rightLen != 0) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
Buf_AddBytes(buf, pattern->rightLen,
(Byte *)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 ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
}
Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
Buf_AddBytes(buf, wordLen - pattern->leftLen,
(Byte *)(word + pattern->leftLen));
}
} 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 - pattern->leftLen);
if ((cp >= word) &&
(strncmp(cp, pattern->lhs, pattern->leftLen) == 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) + pattern->rightLen) != 0) {
if (addSpace) {
Buf_AddByte(buf, (Byte)' ');
}
addSpace = TRUE;
}
Buf_AddBytes(buf, cp - word, (Byte *)word);
Buf_AddBytes(buf, pattern->rightLen, (Byte *)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;
int origSize;
done = FALSE;
origSize = Buf_Size(buf);
while (!done) {
cp = strstr(word, pattern->lhs);
if (cp != (char *)NULL) {
if (addSpace && (((cp - word) + pattern->rightLen) != 0)){
Buf_AddByte(buf, (Byte)' ');
addSpace = FALSE;
}
Buf_AddBytes(buf, cp-word, (Byte *)word);
Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
wordLen -= (cp - word) + pattern->leftLen;
word = cp + pattern->leftLen;
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, (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, (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.
*
* Side Effects:
* None.
*
*-----------------------------------------------------------------------
*/
Boolean
VarRESubstitute(const char *word, Boolean addSpace, Buffer buf, void *patternp)
{
VarREPattern *pat;
int xrv;
const char *wp;
char *rp;
int added;
int flags = 0;
#define MAYBE_ADD_SPACE() \
if (addSpace && !added) \
Buf_AddByte(buf, ' '); \
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, wp);
}
for (rp = pat->replace; *rp; rp++) {
if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
MAYBE_ADD_SPACE();
Buf_AddByte(buf,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, subbuf);
}
} else {
MAYBE_ADD_SPACE();
Buf_AddByte(buf, *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, *wp);
wp++;
}
if (*wp)
goto tryagain;
}
if (*wp) {
MAYBE_ADD_SPACE();
Buf_AddBytes(buf, strlen(wp), wp);
}
break;
default:
VarREError(xrv, &pat->re, "Unexpected regex error");
/* fall through */
case REG_NOMATCH:
if (*wp) {
MAYBE_ADD_SPACE();
Buf_AddBytes(buf,strlen(wp),wp);
}
break;
}
return(addSpace||added);
}