gnezdo noticed that :S/old_string/new_string/ modifiers such as
:S/^sth/&/ and :S/sth$/&/ with an anchor in the old_string and an &
in the new_string don't work as documented (and expected) since they
replace & with old_string including the anchors.
This is because get_spatternarg() deals with skipping the anchors in
pattern->lhs only after having replaced any '&' in new_string with
pattern->lhs. This happens in VarGetPattern if the last argument is
non-NULL when pattern->rhs is determined, i.e., before
common_get_patternarg() is done.
I think the fix should be as simple as the diff below. I added a small
extension of a regress test for this. Unpatched make fails, patched
make passes.
Simple test case that illustrates the problem:
$ cat > makefile
A= foo bar barr
B= ${A:S/^b/s&/}
C= ${A:S/r$/&/}
D= ${A:S/^bar$/&&ian/}
all:
@echo "B= $B"
@echo "C= $C"
@echo "D= $D"
EOF
$ make
B= foo s^bar s^barr
C= foo bar$ barr$
D= foo ^bar$bar barr
I'm not entirely clear on why I get that result for D, but with the
patch below, I get the expected " foo barbarian barr"
Index: usr.bin/make/varmodifiers.c
===================================================================
RCS file: /var/cvs/src/usr.bin/make/varmodifiers.c,v
retrieving revision 1.47
diff -u -p -r1.47 varmodifiers.c
--- usr.bin/make/varmodifiers.c 10 Jul 2017 07:10:29 -0000 1.47
+++ usr.bin/make/varmodifiers.c 20 Aug 2020 16:03:54 -0000
@@ -1215,21 +1215,7 @@ get_patternarg(const char **p, SymTable
static void *
get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc)
{
- VarPattern *pattern;
-
- pattern = common_get_patternarg(p, ctxt, err, endc, true);
- if (pattern != NULL && pattern->leftLen > 0) {
- if (pattern->lhs[pattern->leftLen-1] == '$') {
- pattern->leftLen--;
- pattern->flags |= VAR_MATCH_END;
- }
- if (pattern->lhs[0] == '^') {
- pattern->lhs++;
- pattern->leftLen--;
- pattern->flags |= VAR_MATCH_START;
- }
- }
- return pattern;
+ return common_get_patternarg(p, ctxt, err, endc, true);
}
static void
@@ -1304,6 +1290,17 @@ common_get_patternarg(const char **p, Sy
&pattern->leftLen, NULL);
pattern->lbuffer = pattern->lhs;
if (pattern->lhs != NULL) {
+ if (dosubst && pattern->leftLen > 0) {
+ if (pattern->lhs[pattern->leftLen-1] == '$') {
+ pattern->leftLen--;
+ pattern->flags |= VAR_MATCH_END;
+ }
+ if (pattern->lhs[0] == '^') {
+ pattern->lhs++;
+ pattern->leftLen--;
+ pattern->flags |= VAR_MATCH_START;
+ }
+ }
pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim,
&pattern->rightLen, dosubst ? pattern: NULL);
if (pattern->rhs != NULL) {
Index: regress/usr.bin/make/mk21
===================================================================
RCS file: /var/cvs/src/regress/usr.bin/make/mk21,v
retrieving revision 1.2
diff -u -p -r1.2 mk21
--- regress/usr.bin/make/mk21 7 Jul 2017 16:31:37 -0000 1.2
+++ regress/usr.bin/make/mk21 20 Aug 2020 16:20:31 -0000
@@ -12,7 +12,13 @@ X?= ${TRUC:C@([^:/])/.+$@\1/@}
Y?= ${TRUC:S/^${X}//:S/^://}
Z?= ${TRUC:S/^${TRUC:C@([^:/])/.+$@\1/@}//:S/^://}
+A?= machin truc
+B?= ${A:S/^/mot: &/}
+C?= ${A:S/$/&: mot/}
+
all:
+ @echo "B= $B"
+ @echo "C= $C"
@echo "S= $S"
@echo "T= $T"
@echo "X= $X"
Index: regress/usr.bin/make/t21.out
===================================================================
RCS file: /var/cvs/src/regress/usr.bin/make/t21.out,v
retrieving revision 1.2
diff -u -p -r1.2 t21.out
--- regress/usr.bin/make/t21.out 7 Jul 2017 16:31:37 -0000 1.2
+++ regress/usr.bin/make/t21.out 20 Aug 2020 16:20:31 -0000
@@ -1,3 +1,5 @@
+B= mot: machin mot: truc
+C= machin: mot truc: mot
S= sourceforge/%SUBDIR%/
T= sourceforge/%SUBDIR%/
X= http://heanet.dl.sourceforge.net/