From aa675175227258b0c762e4fa7bfb2aa863711abd Mon Sep 17 00:00:00 2001 From: harti Date: Tue, 10 May 2005 11:53:20 +0000 Subject: Merge var_modify.c into var.c and move types and function declarations that are now used only in var.c from var.h to var.c Patches: 7.193,7.194 Submitted by: Max Okumoto --- usr.bin/make/var.c | 657 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 601 insertions(+), 56 deletions(-) (limited to 'usr.bin/make/var.c') diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c index 9848d58..9784ceb 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: -- cgit v1.1