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/