11.3Srillig# $NetBSD: directive-for-if.mk,v 1.3 2023/11/19 21:47:52 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.2Srillig# expect+3: if-less endif
491.2Srillig# expect+2: if-less endif
501.2Srillig# expect+1: if-less endif
511.1Srillig.  endif
521.1Srillig.endfor
531.1Srillig# In 2021, the above code does not generate an error message, even though the
541.1Srillig# code looks clearly malformed.  This is due to the '!', which is interpreted
551.1Srillig# as a dependency operator, similar to ':' and '::'.  The parser turns this
561.1Srillig# line into a dependency with the 3 targets '.', 'if', '"1"' and the 2 sources
571.1Srillig# '=' and '"0"'.  Since that line is not interpreted as an '.if' directive,
581.1Srillig# the error message 'if-less endif' makes sense.
591.1Srillig
601.1Srillig# In 2005, make complained:
611.1Srillig#
621.1Srillig#	.if line:	Malformed conditional (VAR1 != "VAR2")
631.1Srillig#	.endif line:	if-less endif
641.1Srillig#	.endif line:	Need an operator
651.1Srillig#
661.1Srillig# 2008.11.30.22.37.55 does not complain about the left-hand side ${var}.
671.1Srillig.for var in VAR1 VAR2 VAR3
681.1Srillig.  if ${var} != "VAR2"
691.1Srillig_!=	echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet.
701.1Srillig.  endif
711.1Srillig.endfor
721.1Srillig
731.1Srillig# Before for.c 1.39 from 2008-12-21, a common workaround was to surround the
741.3Srillig# expression from the .for loop with '"'.  Such a string literal
751.1Srillig# has been allowed since cond.c 1.23 from 2004-04-13.  Between that commit and
761.1Srillig# the one from 2008, the parser would still get confused if the value from the
771.1Srillig# .for loop contained '"', which was effectively a code injection.
781.1Srillig#
791.1Srillig# Surrounding ${var} with quotes disabled the check for typos though.  For
801.1Srillig# ordinary variables, referring to an undefined variable on the left-hand side
811.1Srillig# of the comparison resulted in a "Malformed conditional".  Since the .for
821.1Srillig# loop was usually close to the .if clause, this was not a problem in
831.1Srillig# practice.
841.1Srillig.for var in VAR1 VAR2 VAR3
851.1Srillig.  if "${var}" != "VAR2"
861.1Srillig.  endif
871.1Srillig.endfor
881.1Srillig
891.1Srilligall:
90