Module Name: src Committed By: rillig Date: Mon Jul 20 14:50:41 UTC 2020
Modified Files: src/usr.bin/make: parse.c var.c src/usr.bin/make/unit-tests: modmisc.exp Log Message: make(1): make modifier handling simpler Implementing a modifier such as :S or :M should not be concerned with separating the words of the resulting string. Ideally this should be done in the same way by all modifiers. Before, the :R (filename root) modifier added a separator even if the resulting filename root was an empty string. The chances that this change in behavior breaks anything are epsilon. The :@ modifier, if it appeared after a :ts modifier, did not use the word separator from the :ts modifier (which all other modifiers do) but always added a space. This behavior has been preserved for now. It's an unnecessary inconsistency though. In contrast to Buffer, the newly added SepBuf uses size_t for memory sizes and also uses the conventional parameter order (mem, memsize) instead of the unusual (memsize, mem). To generate a diff of this commit: cvs rdiff -u -r1.237 -r1.238 src/usr.bin/make/parse.c cvs rdiff -u -r1.277 -r1.278 src/usr.bin/make/var.c cvs rdiff -u -r1.19 -r1.20 src/usr.bin/make/unit-tests/modmisc.exp Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/usr.bin/make/parse.c diff -u src/usr.bin/make/parse.c:1.237 src/usr.bin/make/parse.c:1.238 --- src/usr.bin/make/parse.c:1.237 Sun Jul 19 12:26:17 2020 +++ src/usr.bin/make/parse.c Mon Jul 20 14:50:41 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: parse.c,v 1.237 2020/07/19 12:26:17 rillig Exp $ */ +/* $NetBSD: parse.c,v 1.238 2020/07/20 14:50:41 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: parse.c,v 1.237 2020/07/19 12:26:17 rillig Exp $"; +static char rcsid[] = "$NetBSD: parse.c,v 1.238 2020/07/20 14:50:41 rillig Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: parse.c,v 1.237 2020/07/19 12:26:17 rillig Exp $"); +__RCSID("$NetBSD: parse.c,v 1.238 2020/07/20 14:50:41 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -1938,9 +1938,8 @@ Parse_DoVar(char *line, GNode *ctxt) break; } - while (isspace ((unsigned char)*cp)) { + while (isspace((unsigned char)*cp)) cp++; - } if (type == VAR_APPEND) { Var_Append(line, cp, ctxt); @@ -1980,7 +1979,7 @@ Parse_DoVar(char *line, GNode *ctxt) /* * There's a dollar sign in the command, so perform variable * expansion on the whole thing. The resulting string will need - * freeing when we're done, so set freeCmd to TRUE. + * freeing when we're done. */ cp = Var_Subst(NULL, cp, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES); freeCp = TRUE; Index: src/usr.bin/make/var.c diff -u src/usr.bin/make/var.c:1.277 src/usr.bin/make/var.c:1.278 --- src/usr.bin/make/var.c:1.277 Sun Jul 19 22:22:01 2020 +++ src/usr.bin/make/var.c Mon Jul 20 14:50:41 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: var.c,v 1.277 2020/07/19 22:22:01 rillig Exp $ */ +/* $NetBSD: var.c,v 1.278 2020/07/20 14:50:41 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: var.c,v 1.277 2020/07/19 22:22:01 rillig Exp $"; +static char rcsid[] = "$NetBSD: var.c,v 1.278 2020/07/20 14:50:41 rillig Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: var.c,v 1.277 2020/07/19 22:22:01 rillig Exp $"); +__RCSID("$NetBSD: var.c,v 1.278 2020/07/20 14:50:41 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -1106,103 +1106,110 @@ Var_Value(const char *name, GNode *ctxt, } +/* SepBuf is a string being built from "words", interleaved with separators. */ +typedef struct { + Buffer buf; + Boolean needSep; + char sep; +} SepBuf; + +static void +SepBuf_Init(SepBuf *buf, char sep) +{ + Buf_Init(&buf->buf, 32 /* bytes */); + buf->needSep = FALSE; + buf->sep = sep; +} + +static void +SepBuf_Sep(SepBuf *buf) +{ + buf->needSep = TRUE; +} + +static void +SepBuf_AddBytes(SepBuf *buf, const void *mem, size_t mem_size) +{ + if (mem_size == 0) + return; + if (buf->needSep && buf->sep != '\0') { + Buf_AddByte(&buf->buf, buf->sep); + buf->needSep = FALSE; + } + Buf_AddBytes(&buf->buf, mem_size, mem); +} + +static char * +SepBuf_Destroy(SepBuf *buf, Boolean free_buf) +{ + return Buf_Destroy(&buf->buf, free_buf); +} + + /* This callback for VarModify gets a single word from an expression and * typically adds a modification of this word to the buffer. It may also do - * nothing or add several words. - * - * If addSpaces is TRUE, it must add a space before adding anything else to - * the buffer. - * - * It returns the addSpace value for the next call of this callback. Typical - * return values are the current addSpaces or TRUE. */ -typedef Boolean (*VarModifyCallback)(GNode *ctxt, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, void *data); + * nothing or add several words. */ +typedef void (*VarModifyCallback)(GNode *ctxt, const char *word, SepBuf *buf, + void *data); /* Callback function for VarModify to implement the :H modifier. * Add the dirname of the given word to the buffer. */ -static Boolean -VarHead(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, +static void +VarHead(GNode *ctx MAKE_ATTR_UNUSED, const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { const char *slash = strrchr(word, '/'); - - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); if (slash != NULL) - Buf_AddBytes(buf, slash - word, word); + SepBuf_AddBytes(buf, word, slash - word); else - Buf_AddByte(buf, '.'); - - return TRUE; + SepBuf_AddBytes(buf, ".", 1); } /* Callback function for VarModify to implement the :T modifier. * Add the basename of the given word to the buffer. */ -static Boolean -VarTail(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, +static void +VarTail(GNode *ctx MAKE_ATTR_UNUSED, const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { const char *slash = strrchr(word, '/'); const char *base = slash != NULL ? slash + 1 : word; - - if (addSpace && vpstate->varSpace != '\0') - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(base), base); - return TRUE; + SepBuf_AddBytes(buf, base, strlen(base)); } /* Callback function for VarModify to implement the :E modifier. * Add the filename suffix of the given word to the buffer, if it exists. */ -static Boolean -VarSuffix(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, +static void +VarSuffix(GNode *ctx MAKE_ATTR_UNUSED, const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { const char *dot = strrchr(word, '.'); - if (dot == NULL) - return addSpace; - - if (addSpace && vpstate->varSpace != '\0') - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(dot + 1), dot + 1); - return TRUE; + if (dot != NULL) + SepBuf_AddBytes(buf, dot + 1, strlen(dot + 1)); } /* Callback function for VarModify to implement the :R modifier. * Add the filename basename of the given word to the buffer. */ -static Boolean -VarRoot(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, +static void +VarRoot(GNode *ctx MAKE_ATTR_UNUSED, const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { char *dot = strrchr(word, '.'); size_t len = dot != NULL ? (size_t)(dot - word) : strlen(word); - - if (addSpace && vpstate->varSpace != '\0') - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, len, word); - return TRUE; + SepBuf_AddBytes(buf, word, len); } /* Callback function for VarModify to implement the :M modifier. * Place the word in the buffer if it matches the given pattern. */ -static Boolean -VarMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, +static void +VarMatch(GNode *ctx MAKE_ATTR_UNUSED, const char *word, SepBuf *buf, void *data) { const char *pattern = data; if (DEBUG(VAR)) fprintf(debug_file, "VarMatch [%s] [%s]\n", word, pattern); - if (!Str_Match(word, pattern)) - return addSpace; - if (addSpace && vpstate->varSpace != '\0') - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(word), word); - return TRUE; + if (Str_Match(word, pattern)) + SepBuf_AddBytes(buf, word, strlen(word)); } #ifdef SYSVVARSUB @@ -1219,10 +1226,6 @@ VarMatch(GNode *ctx MAKE_ATTR_UNUSED, Va * Results: * Returns the beginning position of a match or null. The number * of characters matched is returned in len. - * - * Side Effects: - * None - * *----------------------------------------------------------------------- */ static const char * @@ -1283,28 +1286,27 @@ Str_SYSVMatch(const char *word, const ch * * Side Effects: * Places result on buf - * *----------------------------------------------------------------------- */ static void -Str_SYSVSubst(Buffer *buf, const char *pat, const char *src, size_t len, - Boolean lhsHasPercent) +Str_SYSVSubst(SepBuf *buf, const char *pat, const char *src, size_t len, + Boolean lhsHasPercent) { const char *m; if ((m = strchr(pat, '%')) != NULL && lhsHasPercent) { /* Copy the prefix */ - Buf_AddBytes(buf, m - pat, pat); + SepBuf_AddBytes(buf, pat, m - pat); /* skip the % */ pat = m + 1; } if (m != NULL || !lhsHasPercent) { /* Copy the pattern */ - Buf_AddBytes(buf, len, src); + SepBuf_AddBytes(buf, src, len); } /* append the rest */ - Buf_AddBytes(buf, strlen(pat), pat); + SepBuf_AddBytes(buf, pat, strlen(pat)); } @@ -1314,16 +1316,11 @@ typedef struct { } VarSYSVSubstArgs; /* Callback function for VarModify to implement the :%.from=%.to modifier. */ -static Boolean -VarSYSVSubst(GNode *ctx, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +static void +VarSYSVSubst(GNode *ctx, const char *word, SepBuf *buf, void *data) { const VarSYSVSubstArgs *args = data; - if (addSpace && vpstate->varSpace != '\0') - Buf_AddByte(buf, vpstate->varSpace); - size_t len; Boolean hasPercent; const char *ptr = Str_SYSVMatch(word, args->lhs, &len, &hasPercent); @@ -1332,34 +1329,26 @@ VarSYSVSubst(GNode *ctx, Var_Parse_State Str_SYSVSubst(buf, varexp, ptr, len, hasPercent); free(varexp); } else { - Buf_AddBytes(buf, strlen(word), word); + SepBuf_AddBytes(buf, word, strlen(word)); } - - return TRUE; } #endif /* Callback function for VarModify to implement the :N modifier. * Place the word in the buffer if it doesn't match the given pattern. */ -static Boolean -VarNoMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, +static void +VarNoMatch(GNode *ctx MAKE_ATTR_UNUSED, const char *word, SepBuf *buf, void *data) { const char *pattern = data; - if (Str_Match(word, pattern)) - return addSpace; - if (addSpace && vpstate->varSpace != '\0') - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(word), word); - return TRUE; + if (!Str_Match(word, pattern)) + SepBuf_AddBytes(buf, word, strlen(word)); } /* Callback function for VarModify to implement the :S,from,to, modifier. * Perform a string substitution on the given word. */ -static Boolean -VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, +static void +VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, const char *word, SepBuf *buf, void *data) { int wordLen = strlen(word); @@ -1381,16 +1370,9 @@ VarSubstitute(GNode *ctx MAKE_ATTR_UNUSE (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. + * is same length as pattern). */ - if (pattern->rightLen != 0) { - if (addSpace && vpstate->varSpace != '\0') { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - } + SepBuf_AddBytes(buf, pattern->rhs, pattern->rightLen); pattern->pflags |= VARP_SUB_MATCHED; } else if (pattern->pflags & VARP_MATCH_END) { /* @@ -1401,15 +1383,9 @@ VarSubstitute(GNode *ctx MAKE_ATTR_UNUSE /* * Matches at start but need to copy in trailing characters */ - if ((pattern->rightLen + wordLen - pattern->leftLen) != 0) { - if (addSpace && vpstate->varSpace != '\0') { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - } - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - Buf_AddBytes(buf, wordLen - pattern->leftLen, - (word + pattern->leftLen)); + SepBuf_AddBytes(buf, pattern->rhs, pattern->rightLen); + SepBuf_AddBytes(buf, word + pattern->leftLen, + wordLen - pattern->leftLen); pattern->pflags |= VARP_SUB_MATCHED; } } else if (pattern->pflags & VARP_MATCH_START) { @@ -1425,22 +1401,14 @@ VarSubstitute(GNode *ctx MAKE_ATTR_UNUSE * to use strncmp. */ cp = word + (wordLen - pattern->leftLen); - if ((cp >= word) && - (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) { + 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. + * Match found. Stuff in the initial, unmatched part of the + * word followed by the right-hand-side. */ - if (((cp - word) + pattern->rightLen) != 0) { - if (addSpace && vpstate->varSpace != '\0') { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - } - Buf_AddBytes(buf, cp - word, word); - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); + SepBuf_AddBytes(buf, word, cp - word); + SepBuf_AddBytes(buf, pattern->rhs, pattern->rightLen); pattern->pflags |= VARP_SUB_MATCHED; } else { /* @@ -1457,17 +1425,10 @@ VarSubstitute(GNode *ctx MAKE_ATTR_UNUSE * 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. */ - int origSize = Buf_Size(buf); while ((cp = Str_FindSubstring(word, pattern->lhs)) != NULL) { - if (addSpace && (((cp - word) + pattern->rightLen) != 0)) { - Buf_AddByte(buf, vpstate->varSpace); - addSpace = FALSE; - } - Buf_AddBytes(buf, cp - word, word); - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); + SepBuf_AddBytes(buf, word, cp - word); + SepBuf_AddBytes(buf, pattern->rhs, pattern->rightLen); wordLen -= (cp - word) + pattern->leftLen; word = cp + pattern->leftLen; if (wordLen == 0) @@ -1476,27 +1437,12 @@ VarSubstitute(GNode *ctx MAKE_ATTR_UNUSE break; pattern->pflags |= VARP_SUB_MATCHED; } - if (wordLen != 0) { - if (addSpace && vpstate->varSpace != '\0') { - Buf_AddByte(buf, vpstate->varSpace); - } - Buf_AddBytes(buf, wordLen, 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; + SepBuf_AddBytes(buf, word, wordLen); } - return addSpace; + return; } nosub: - if (addSpace && vpstate->varSpace != '\0') { - Buf_AddByte(buf, vpstate->varSpace); - } - Buf_AddBytes(buf, wordLen, word); - return TRUE; + SepBuf_AddBytes(buf, word, wordLen); } #ifndef NO_REGEX @@ -1525,24 +1471,16 @@ VarREError(int reerr, regex_t *pat, cons /* Callback function for VarModify to implement the :C/from/to/ modifier. * Perform a regex substitution on the given word. */ -static Boolean -VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED, - Var_Parse_State *vpstate MAKE_ATTR_UNUSED, - const char *word, Boolean addSpace, Buffer *buf, +static void +VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED, const char *word, SepBuf *buf, void *data) { VarREPattern *pat = data; int xrv; const char *wp = word; char *rp; - int added = 0; int flags = 0; -#define MAYBE_ADD_SPACE() \ - if (addSpace && !added) \ - Buf_AddByte(buf, ' '); \ - added = 1 - if ((pat->pflags & (VARP_SUB_ONE | VARP_SUB_MATCHED)) == (VARP_SUB_ONE | VARP_SUB_MATCHED)) xrv = REG_NOMATCH; @@ -1554,18 +1492,14 @@ VarRESubstitute(GNode *ctx MAKE_ATTR_UNU switch (xrv) { case 0: pat->pflags |= VARP_SUB_MATCHED; - if (pat->matches[0].rm_so > 0) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, pat->matches[0].rm_so, wp); - } + SepBuf_AddBytes(buf, wp, pat->matches[0].rm_so); for (rp = pat->replace; *rp; rp++) { - if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) { - MAYBE_ADD_SPACE(); - Buf_AddByte(buf, rp[1]); + if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { + SepBuf_AddBytes(buf, rp + 1, 1); rp++; - } else if ((*rp == '&') || - ((*rp == '\\') && isdigit((unsigned char)rp[1]))) { + } else if (*rp == '&' || + (*rp == '\\' && isdigit((unsigned char)rp[1]))) { int n; const char *subbuf; int sublen; @@ -1597,73 +1531,60 @@ VarRESubstitute(GNode *ctx MAKE_ATTR_UNU sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so; } - if (sublen > 0) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, sublen, subbuf); - } + SepBuf_AddBytes(buf, subbuf, sublen); } else { - MAYBE_ADD_SPACE(); - Buf_AddByte(buf, *rp); + SepBuf_AddBytes(buf, rp, 1); } } wp += pat->matches[0].rm_eo; if (pat->pflags & VARP_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); + SepBuf_AddBytes(buf, wp, 1); wp++; - } if (*wp) goto tryagain; } if (*wp) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, strlen(wp), wp); + SepBuf_AddBytes(buf, wp, strlen(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); - } + SepBuf_AddBytes(buf, wp, strlen(wp)); break; } - return addSpace || added; } #endif /* Callback for VarModify to implement the :@var@...@ modifier of ODE make. */ -static Boolean -VarLoopExpand(GNode *ctx, Var_Parse_State *vpstate MAKE_ATTR_UNUSED, - const char *word, Boolean addSpace, Buffer *buf, void *data) +static void +VarLoopExpand(GNode *ctx, const char *word, SepBuf *buf, void *data) { - VarLoop *loop = data; + if (word[0] == '\0') + return; - if (*word) { - Var_Set_with_flags(loop->tvar, word, ctx, VAR_NO_EXPORT); - char *s = Var_Subst(NULL, loop->str, ctx, loop->eflags); - if (DEBUG(VAR)) { - fprintf(debug_file, - "VarLoopExpand: in \"%s\", replace \"%s\" with \"%s\" " - "to \"%s\"\n", - word, loop->tvar, loop->str, s ? s : "(null)"); - } - if (s != NULL && *s != '\0') { - if (addSpace && *s != '\n') - Buf_AddByte(buf, ' '); - size_t slen = strlen(s); - Buf_AddBytes(buf, slen, s); - addSpace = (slen > 0 && s[slen - 1] != '\n'); - } - free(s); + const VarLoop *loop = data; + Var_Set_with_flags(loop->tvar, word, ctx, VAR_NO_EXPORT); + char *s = Var_Subst(NULL, loop->str, ctx, loop->eflags); + if (DEBUG(VAR)) { + fprintf(debug_file, + "VarLoopExpand: in \"%s\", replace \"%s\" with \"%s\" " + "to \"%s\"\n", + word, loop->tvar, loop->str, s ? s : "(null)"); } - return addSpace; + + if (s != NULL && s[0] != '\0') { + if (s[0] == '\n' || (buf->buf.count > 0 && + buf->buf.buffer[buf->buf.count - 1] == '\n')) + buf->needSep = FALSE; + SepBuf_AddBytes(buf, s, strlen(s)); + } + free(s); } @@ -1690,17 +1611,13 @@ static char * VarSelectWords(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, const char *str, VarSelectWords_t *seldata) { - Buffer buf; /* Buffer for the new string */ - Boolean addSpace; /* TRUE if need to add a space to the - * buffer before adding the trimmed - * word */ + SepBuf buf; char **av; /* word list */ char *as; /* word list memory */ int ac, i; int start, end, step; - Buf_Init(&buf, 0); - addSpace = FALSE; + SepBuf_Init(&buf, vpstate->varSpace); if (vpstate->oneBigWord) { /* fake what brk_string() would do if there were only one word */ @@ -1740,41 +1657,33 @@ VarSelectWords(GNode *ctx MAKE_ATTR_UNUS for (i = start; (step < 0 && i >= end) || (step > 0 && i < end); i += step) { - if (av[i] && *av[i]) { - if (addSpace && vpstate->varSpace != '\0') { - Buf_AddByte(&buf, vpstate->varSpace); - } - Buf_AddBytes(&buf, strlen(av[i]), av[i]); - addSpace = TRUE; + if (av[i][0] != '\0') { + SepBuf_AddBytes(&buf, av[i], strlen(av[i])); + SepBuf_Sep(&buf); } } free(as); free(av); - return Buf_Destroy(&buf, FALSE); + return SepBuf_Destroy(&buf, FALSE); } /* Callback function for VarModify to implement the :tA modifier. * Replace each word with the result of realpath() if successful. */ -static Boolean -VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, +static void +VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, const char *word, SepBuf *buf, void *patternp MAKE_ATTR_UNUSED) { struct stat st; char rbuf[MAXPATHLEN]; - char *rp; - if (addSpace && vpstate->varSpace != '\0') - Buf_AddByte(buf, vpstate->varSpace); - rp = cached_realpath(word, rbuf); + char *rp = cached_realpath(word, rbuf); if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) word = rp; - Buf_AddBytes(buf, strlen(word), word); - return TRUE; + SepBuf_AddBytes(buf, word, strlen(word)); } /*- @@ -1782,31 +1691,24 @@ VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, * Modify each of the words of the passed string using the given function. * * Input: - * str String whose words should be trimmed - * modProc Function to use to modify them + * str String whose words should be modified + * modProc Function that modifies a single word * data Custom data for the modProc * * Results: * A string of all the words modified appropriately. - * - * Side Effects: - * None. - * *----------------------------------------------------------------------- */ static char * VarModify(GNode *ctx, Var_Parse_State *vpstate, - const char *str, VarModifyCallback modProc, void *datum) + const char *str, VarModifyCallback modProc, void *data) { - Buffer buf; /* Buffer for the new string */ - Boolean addSpace; /* TRUE if need to add a space to the - * buffer before adding the trimmed word */ + SepBuf result; char **av; /* word list */ char *as; /* word list memory */ int ac, i; - Buf_Init(&buf, 0); - addSpace = FALSE; + SepBuf_Init(&result, vpstate->varSpace); if (vpstate->oneBigWord) { /* fake what brk_string() would do if there were only one word */ @@ -1824,13 +1726,19 @@ VarModify(GNode *ctx, Var_Parse_State *v str, ac); } - for (i = 0; i < ac; i++) - addSpace = modProc(ctx, vpstate, av[i], addSpace, &buf, datum); + for (i = 0; i < ac; i++) { + size_t orig_count = result.buf.count; + modProc(ctx, av[i], &result, data); + size_t count = result.buf.count; + if (count != orig_count) + SepBuf_Sep(&result); + } free(as); free(av); - return Buf_Destroy(&buf, FALSE); + vpstate->varSpace = result.sep; + return SepBuf_Destroy(&result, FALSE); } @@ -2045,7 +1953,7 @@ ParseModifierPart(GNode *ctxt, const cha * backslashes to quote the delimiter, $, and \, but don't * touch other backslashes. */ - for (cp = *tstr; *cp && (*cp != delim); cp++) { + for (cp = *tstr; *cp != '\0' && *cp != delim; cp++) { Boolean is_escaped = cp[0] == '\\' && (cp[1] == delim || cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&')); if (is_escaped) { @@ -2121,7 +2029,7 @@ ParseModifierPart(GNode *ctxt, const cha *length = Buf_Size(&buf); rstr = Buf_Destroy(&buf, FALSE); if (DEBUG(VAR)) - fprintf(debug_file, "Modifier pattern: \"%s\"\n", rstr); + fprintf(debug_file, "Modifier part: \"%s\"\n", rstr); return rstr; } @@ -2142,7 +2050,6 @@ ParseModifierPart(GNode *ctxt, const cha static char * VarQuote(char *str, Boolean quoteDollar) { - Buffer buf; const char *newline; size_t nlen; @@ -2317,8 +2224,11 @@ ApplyModifier_At(ApplyModifiersState *st st->delim = '\0'; loop.eflags = st->eflags & (VARE_UNDEFERR | VARE_WANTRES); + int prev_sep = st->parsestate.varSpace; + st->parsestate.varSpace = ' '; st->newStr = VarModify( st->ctxt, &st->parsestate, st->nstr, VarLoopExpand, &loop); + st->parsestate.varSpace = prev_sep; Var_Delete(loop.tvar, st->ctxt); free(loop.tvar); free(loop.str); @@ -2741,15 +2651,11 @@ ApplyModifier_Regex(ApplyModifiersState } #endif -static Boolean -VarModify_Copy(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *data MAKE_ATTR_UNUSED) -{ - if (addSpace && vpstate->varSpace != '\0') - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(word), word); - return TRUE; +static void +VarModify_Copy(GNode *ctx MAKE_ATTR_UNUSED, const char *word, SepBuf *buf, + void *data MAKE_ATTR_UNUSED) +{ + SepBuf_AddBytes(buf, word, strlen(word)); } /* :tA, :tu, :tl, etc. */ @@ -3481,7 +3387,7 @@ ApplyModifiers(char *nstr, const char *t case 'T': if (st.tstr[1] == st.endc || st.tstr[1] == ':') { st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarTail, - NULL); + NULL); st.cp = st.tstr + 1; st.termc = *st.cp; break; @@ -3490,7 +3396,7 @@ ApplyModifiers(char *nstr, const char *t case 'H': if (st.tstr[1] == st.endc || st.tstr[1] == ':') { st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarHead, - NULL); + NULL); st.cp = st.tstr + 1; st.termc = *st.cp; break; @@ -3499,7 +3405,7 @@ ApplyModifiers(char *nstr, const char *t case 'E': if (st.tstr[1] == st.endc || st.tstr[1] == ':') { st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarSuffix, - NULL); + NULL); st.cp = st.tstr + 1; st.termc = *st.cp; break; @@ -3508,7 +3414,7 @@ ApplyModifiers(char *nstr, const char *t case 'R': if (st.tstr[1] == st.endc || st.tstr[1] == ':') { st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarRoot, - NULL); + NULL); st.cp = st.tstr + 1; st.termc = *st.cp; break; Index: src/usr.bin/make/unit-tests/modmisc.exp diff -u src/usr.bin/make/unit-tests/modmisc.exp:1.19 src/usr.bin/make/unit-tests/modmisc.exp:1.20 --- src/usr.bin/make/unit-tests/modmisc.exp:1.19 Sun Jul 19 20:49:44 2020 +++ src/usr.bin/make/unit-tests/modmisc.exp Mon Jul 20 14:50:41 2020 @@ -10,7 +10,7 @@ The answer is 42 dirname of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'a/b . . a.b . . . . .' basename of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'c def a.b.c c a a.a .gitignore a a.a' suffix of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'c b/c a gitignore a' -root of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'a/b/c def a.b a a a a a' +root of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'a/b/c def a.b a a a a a' S: C: @: