On 2023-12-10, Lucas de Sena wrote:
> I could identify the following bugs in make(1), concerning the
> "%" character on a variable substitution:
>
> - If there's no "%" in the left or right hand side string of a
> variable substitution, make(1) prepends one into them. Thus
> ${SRCS:.c=.o} is equivalent to ${SRCS:%.c=%.o}. That's fine
> when both left- and right-hand side have no "%" in them; but
> works unexpectedly when one has a "%" and the other has not.
>
> - The "%" character cannot be escaped, neither by backslashing
> or by doubling it.
>
> This is what make(1) says about the '%' (the "pattern matching
> character") in this kind of expressions (called "AT&T System V
> UNIX variable substitutions):
>
> | :old_string=new_string
> | This is the AT&T System V UNIX style variable substitution.
> | It must be the last modifier specified. If old_string or
> | new_string do not contain the pattern matching character '%'
> | then it is assumed that they are anchored at the end of each
> | word, so only suffixes or entire words may be replaced.
> | Otherwise '%' is the substring of old_string to be replaced
> | in new_string. The right-hand side (new_string) may contain
> | variable values, which will be expanded. To put an actual
> | single dollar, just double it.
>
> For example, consider the following makefile:
>
> | $ cat makefile
> | VAR = foohellobar
> | FOO = foo%foo
> | all:
> | @echo "${VAR:foo%bar=% world}" # 0. % on both sides
> | @echo "${VAR:foo%bar= world}" # 1. % at left only
> | @echo "${VAR:hellobar=%world}" # 2. % at right only
> | @echo "${VAR:foo%bar=\% world}" # 3. right % escape
> | @echo "${FOO:\%foo=bar}" # 4. left % escape
>
> Running GNU gmake on it prints:
>
> | $ gmake
> | hello world
> | world
> | foo%world
> | % world
> | foobar
>
> 0. "%" in old_string matches "hello", and "%" in new_string is
> replaced with it.
>
> 1. "%" in old_string matches "hello", no replacement occurs in
> new_string (there is no "%" in it).
>
> 2. There is no "%" in old_string to match anything, the "%" in
> new_string is ignored.
>
> 3. "%" in old_string matches "hello", and "%" in new_string is
> escaped and thus not replaced (behaves as 2).
>
> 4. "%" in old_string is escaped, the suffix "%foo" is replaced
> with new_string.
>
> Now run OpenBSD make on the same makefile:
>
> | $ make
> | hello world
> | hello world
> | fooworld
> | \hello world
> | foo%foo
>
> 0. "%" in both sides. No bug here.
>
> 1. It works as if there were an implicit "%" inserted into the
> begining of new_string.
>
> 2. It works as if there were an implicit "%" inserted into the
> begining of old_string.
>
> 3. It works as if "%" were not escaped, and the backslash were
> a regular character before it.
>
> 4. It works as if "%" were not escaped, and the backslash were
> a regular character before it.
>
> -- Lucas de Sena
>
Yearly bump.