directive-for-if.mk revision 1.1
11.1Srillig# $NetBSD: directive-for-if.mk,v 1.1 2021/08/30 17:08:13 rillig Exp $
21.1Srillig#
31.1Srillig# Test for a .for directive that contains an .if directive.
41.1Srillig#
51.1Srillig# Before for.c 1.39 from 2008-12-21, when expanding the variables of a .for
61.1Srillig# loop, their values were placed verbatim in the expanded body.  Since then,
71.1Srillig# each variable value expands to an expression of the form ${:Uvalue}.
81.1Srillig#
91.1Srillig# Before that change, the following adventurous code was possible:
101.1Srillig#
111.1Srillig#	.for directive in if ifdef ifndef
121.1Srillig#	.  ${directive} "1" != "0"
131.1Srillig#	.  endif
141.1Srillig#	.endfor
151.1Srillig#
161.1Srillig# A more practical usage of the .for loop that often led to surprises was the
171.1Srillig# following:
181.1Srillig#
191.1Srillig#	.for var in VAR1 VAR2 VAR3
201.1Srillig#	.  if ${var} != "VAR2"
211.1Srillig#	.  endif
221.1Srillig#	.endfor
231.1Srillig#
241.1Srillig# The .for loop body expanded to this string:
251.1Srillig#
261.1Srillig#	.  if VAR1 != "VAR2"
271.1Srillig#	.  endif
281.1Srillig#
291.1Srillig# Since bare words were not allowed at the left-hand side of a condition,
301.1Srillig# make complained about a "Malformed conditional", which was surprising since
311.1Srillig# the code before expanding the .for loop body looked quite well.
321.1Srillig#
331.1Srillig# In cond.c 1.48 from 2008-11-29, just a month before the expansion of .for
341.1Srillig# loops changed from plain textual value to using expressions of the form
351.1Srillig# ${:Uvalue}, this surprising behavior was documented in the code, and a
361.1Srillig# workaround was implemented that allowed bare words when they are followed
371.1Srillig# by either '!' or '=', as part of the operators '!=' or '=='.
381.1Srillig#
391.1Srillig# Since cond.c 1.68 from 2015-05-05, bare words are allowed on the left-hand
401.1Srillig# side of a condition, but that applies only to expression of the form
411.1Srillig# ${${cond} :? then : else}, it does not apply to conditions in ordinary .if
421.1Srillig# directives.
431.1Srillig
441.1Srillig# The following snippet worked in 2005, when the variables from the .for loop
451.1Srillig# expanded to their bare textual value.
461.1Srillig.for directive in if ifdef ifndef
471.1Srillig.  ${directive} "1" != "0"
481.1Srillig.  endif
491.1Srillig.endfor
501.1Srillig# In 2021, the above code does not generate an error message, even though the
511.1Srillig# code looks clearly malformed.  This is due to the '!', which is interpreted
521.1Srillig# as a dependency operator, similar to ':' and '::'.  The parser turns this
531.1Srillig# line into a dependency with the 3 targets '.', 'if', '"1"' and the 2 sources
541.1Srillig# '=' and '"0"'.  Since that line is not interpreted as an '.if' directive,
551.1Srillig# the error message 'if-less endif' makes sense.
561.1Srillig
571.1Srillig# In 2005, make complained:
581.1Srillig#
591.1Srillig#	.if line:	Malformed conditional (VAR1 != "VAR2")
601.1Srillig#	.endif line:	if-less endif
611.1Srillig#	.endif line:	Need an operator
621.1Srillig#
631.1Srillig# 2008.11.30.22.37.55 does not complain about the left-hand side ${var}.
641.1Srillig.for var in VAR1 VAR2 VAR3
651.1Srillig.  if ${var} != "VAR2"
661.1Srillig_!=	echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet.
671.1Srillig.  endif
681.1Srillig.endfor
691.1Srillig
701.1Srillig# Before for.c 1.39 from 2008-12-21, a common workaround was to surround the
711.1Srillig# variable expression from the .for loop with '"'.  Such a string literal
721.1Srillig# has been allowed since cond.c 1.23 from 2004-04-13.  Between that commit and
731.1Srillig# the one from 2008, the parser would still get confused if the value from the
741.1Srillig# .for loop contained '"', which was effectively a code injection.
751.1Srillig#
761.1Srillig# Surrounding ${var} with quotes disabled the check for typos though.  For
771.1Srillig# ordinary variables, referring to an undefined variable on the left-hand side
781.1Srillig# of the comparison resulted in a "Malformed conditional".  Since the .for
791.1Srillig# loop was usually close to the .if clause, this was not a problem in
801.1Srillig# practice.
811.1Srillig.for var in VAR1 VAR2 VAR3
821.1Srillig.  if "${var}" != "VAR2"
831.1Srillig.  endif
841.1Srillig.endfor
851.1Srillig
861.1Srilligall:
87