diff --git a/usr.bin/make/Makefile b/usr.bin/make/Makefile index bf33e718fd4c..4a445437593e 100644 --- a/usr.bin/make/Makefile +++ b/usr.bin/make/Makefile @@ -5,8 +5,7 @@ PROG= make CFLAGS+=-I${.CURDIR} SRCS= arch.c buf.c compat.c cond.c dir.c for.c hash.c hash_tables.c \ - job.c lst.c main.c make.c parse.c str.c suff.c targ.c util.c \ - var.c var_modify.c + job.c lst.c main.c make.c parse.c str.c suff.c targ.c util.c var.c NO_WERROR= WARNS?= 3 diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c index 9848d5866e33..9784cebe3cc5 100644 --- a/usr.bin/make/var.c +++ b/usr.bin/make/var.c @@ -1,4 +1,5 @@ /*- + * 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 @@ -87,6 +88,8 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include "buf.h" #include "config.h" @@ -110,6 +113,41 @@ typedef struct VarParser { Boolean err; Boolean execute; } VarParser; + +typedef struct Var { + char *name; /* the variable's name */ + struct 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_JUNK 4 /* Variable is a junk variable that + * should be destroyed when done with + * it. Used by Var_Parse for undefined, + * modified variables */ + +#define VAR_TO_ENV 8 /* Place variable in environment */ +} Var; + +typedef struct { + struct Buffer *lhs; /* String to match */ + struct Buffer *rhs; /* Replacement string (w/ &'s removed) */ + + regex_t re; + int nsub; + regmatch_t *matches; + + int 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 */ +} VarPattern; + +typedef Boolean VarModifyProc(const char *, Boolean, struct Buffer *, void *); + static char *VarParse(VarParser *, Boolean *); /* @@ -194,6 +232,545 @@ VarDestroy(Var *v, Boolean f) free(v); } +/** + * 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(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. + */ +static 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. + */ +static 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. + */ +static 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. + */ +static 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. + */ +static 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. + */ +static 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. + */ +static 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); +} + +/** + * Print the error caused by a regcomp or regexec call. + * + * 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. + */ +static 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); +} + /* * Find a variable in a variable list. */ @@ -796,62 +1373,6 @@ VarGetPattern(VarParser *vp, int delim, int *flags, VarPattern *patt) return (NULL); } -/*- - *----------------------------------------------------------------------- - * Var_Quote -- - * Quote shell meta-characters in the string - * - * Results: - * The quoted string - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -char * -Var_Quote(const char *str) -{ - Buffer *buf; - /* This should cover most shells :-( */ - static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; - - buf = Buf_Init(MAKE_BSIZE); - for (; *str; str++) { - if (strchr(meta, *str) != NULL) - Buf_AddByte(buf, (Byte)'\\'); - Buf_AddByte(buf, (Byte)*str); - } - - return (Buf_Peel(buf)); -} - -/*- - *----------------------------------------------------------------------- - * 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); -} - /** * Make sure this variable is fully expanded. */ @@ -1167,6 +1688,30 @@ sysVvarsub(VarParser *vp, char startc, Var *v, const char value[]) return (newStr); } +/** + * Quote shell meta-characters in the string + * + * Results: + * The quoted string + */ +static char * +Var_Quote(const char *str) +{ + Buffer *buf; + /* This should cover most shells :-( */ + static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; + + buf = Buf_Init(MAKE_BSIZE); + for (; *str; str++) { + if (strchr(meta, *str) != NULL) + Buf_AddByte(buf, (Byte)'\\'); + Buf_AddByte(buf, (Byte)*str); + } + + return (Buf_Peel(buf)); +} + + /* * Now we need to apply any modifiers the user wants applied. * These are: diff --git a/usr.bin/make/var.h b/usr.bin/make/var.h index 986efe03e4a6..6de80d38e0ce 100644 --- a/usr.bin/make/var.h +++ b/usr.bin/make/var.h @@ -42,53 +42,9 @@ #ifndef var_h_9cccafce #define var_h_9cccafce -#include - -#include "config.h" - struct GNode; struct Buffer; -typedef struct Var { - char *name; /* the variable's name */ - struct 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_JUNK 4 /* Variable is a junk variable that - * should be destroyed when done with - * it. Used by Var_Parse for undefined, - * modified variables */ - -#define VAR_TO_ENV 8 /* Place variable in environment */ -} 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 */ - -typedef struct { - struct Buffer *lhs; /* String to match */ - struct Buffer *rhs; /* Replacement string (w/ &'s removed) */ - - regex_t re; - int nsub; - regmatch_t *matches; - - int flags; -} VarPattern; - -typedef Boolean VarModifyProc(const char *, Boolean, struct Buffer *, void *); - -/* - * var.c - */ -void VarREError(int, regex_t *, const char *); void Var_Append(const char *, const char *, struct GNode *); void Var_Delete(const char *, struct GNode *); void Var_Dump(void); @@ -96,27 +52,10 @@ Boolean Var_Exists(const char *, struct GNode *); void Var_Init(char **); size_t Var_Match(const char [], struct GNode *); char *Var_Parse(const char *, struct GNode *, Boolean, size_t *, Boolean *); -char *Var_Quote(const char *); void Var_Set(const char *, const char *, struct GNode *); void Var_SetEnv(const char *, struct GNode *); struct Buffer *Var_Subst(const char *, struct GNode *, Boolean); struct Buffer *Var_SubstOnly(const char *, const char *, struct GNode *, Boolean); char *Var_Value(const char *, struct GNode *, char **); -/* - * var_modify.c - */ -VarModifyProc VarHead; -VarModifyProc VarMatch; -VarModifyProc VarNoMatch; -VarModifyProc VarRESubstitute; -VarModifyProc VarRoot; -VarModifyProc VarSubstitute; -VarModifyProc VarSuffix; -VarModifyProc VarTail; - -#ifdef SYSVVARSUB -VarModifyProc VarSYSVMatch; -#endif - #endif /* var_h_9cccafce */ diff --git a/usr.bin/make/var_modify.c b/usr.bin/make/var_modify.c deleted file mode 100644 index 5e30892d04e2..000000000000 --- a/usr.bin/make/var_modify.c +++ /dev/null @@ -1,572 +0,0 @@ -/*- - * 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 -__FBSDID("$FreeBSD$"); - -#include -#include -#include - -#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); -}