1# $NetBSD: directive-for-escape.mk,v 1.20 2023/06/01 20:56:35 rillig Exp $ 2# 3# Test escaping of special characters in the iteration values of a .for loop. 4# These values get expanded later using the :U variable modifier, and this 5# escaping and unescaping must pass all characters and strings unmodified. 6 7.MAKEFLAGS: -df 8 9# Even though the .for loops take quotes into account when splitting the 10# string into words, the quotes don't need to be balanced, as of 2020-12-31. 11# This could be considered a bug. 12ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~ 13 14 15# XXX: As of 2020-12-31, the '#' is not preserved in the expanded body of 16# the loop. Not only would it need the escaping for the variable modifier 17# ':U' but also the escaping for the line-end comment. 18.for chars in ${ASCII} 19. info ${chars} 20.endfor 21# expect-2: !" 22 23# As of 2020-12-31, using 2 backslashes before be '#' would treat the '#' 24# as comment character. Using 3 backslashes doesn't help either since 25# then the situation is essentially the same as with 1 backslash. 26# This means that a '#' sign cannot be passed in the value of a .for loop 27# at all. 28ASCII.2020-12-31= !"\\\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~ 29.for chars in ${ASCII.2020-12-31} 30. info ${chars} 31.endfor 32# expect-2: !"\\ 33 34# Cover the code in ExprLen. 35# 36# XXX: It is unexpected that the variable V gets expanded in the loop body. 37# The double '$$' should intuitively prevent exactly this. Probably nobody 38# was adventurous enough to use literal dollar signs in the values of a .for 39# loop, allowing this edge case to go unnoticed for years. 40# 41# See for.c, function ExprLen. 42V= value 43VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier) 44.for i in ${VALUES} 45. info $i 46.endfor 47# expect-2: $ 48# expect-3: value 49# expect-4: value-with-modifier 50# expect-5: value 51# expect-6: value-with-modifier 52 53 54# Try to cover the code for nested '{}' in ExprLen, without success. 55# 56# The value of the variable VALUES is not meant to be a variable expression. 57# Instead, it is meant to represent literal text, the only escaping mechanism 58# being that each '$' is written as '$$'. 59VALUES= $${UNDEF:U\$$\$$ {{}} end} 60# 61# The .for loop splits ${VALUES} into 3 words, at the space characters, since 62# the '$$' is an ordinary character and the spaces are not escaped. 63# Word 1 is '${UNDEF:U\$\$' 64# Word 2 is '{{}}' 65# Word 3 is 'end}' 66# 67# Each of these words is now inserted in the body of the .for loop. 68.for i in ${VALUES} 69# $i 70.endfor 71# 72# When these words are injected into the body of the .for loop, each inside a 73# '${:U...}' expression, the result is: 74# 75# expect: For: loop body with i = ${UNDEF:U\$\$: 76# expect: # ${:U\${UNDEF\:U\\$\\$} 77# expect: For: loop body with i = {{}}: 78# expect: # ${:U{{\}\}} 79# expect: For: loop body with i = end}: 80# expect: # ${:Uend\}} 81# expect: For: end for 1 82# 83# The first of these expressions is the most interesting one, due to its many 84# special characters. This expression is properly balanced: 85# 86# Text Meaning Explanation 87# \$ $ escaped 88# { { ordinary text 89# UNDEF UNDEF ordinary text 90# \: : escaped 91# U U ordinary text 92# \\ \ escaped 93# $\ (expr) an expression, the variable name is '\' 94# \$ $ escaped 95# 96# To make the expression '$\' visible, define it to an actual word: 97${:U\\}= backslash 98.for i in ${VALUES} 99. info $i 100.endfor 101# 102# expect-3: ${UNDEF:U\backslash$ 103# expect-4: {{}} 104# expect-5: end} 105# 106# FIXME: There was no expression '$\' in the original text of the variable 107# 'VALUES', that's a surprise in the parser. 108 109 110# Second try to cover the code for nested '{}' in ExprLen. 111# 112# XXX: It is not the job of ExprLen to parse an expression, it is naive to 113# expect ExprLen to get all the details right in just a few lines of code. 114# Each variable modifier has its own inconsistent way of parsing nested 115# variable expressions, braces and parentheses. (Compare ':M', ':S', and 116# ':D' for details.) The only sensible thing to do is therefore to let 117# Var_Parse do all the parsing work. 118VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end 119.for i in ${VALUES} 120. info $i 121.endfor 122# expect-2: begin<fallback>end 123 124# A single trailing dollar doesn't happen in practice. 125# The dollar sign is correctly passed through to the body of the .for loop. 126# There, it is expanded by the .info directive, but even there a trailing 127# dollar sign is kept as-is. 128.for i in ${:U\$} 129. info ${i} 130.endfor 131# expect-2: $ 132 133# Before for.c 1.173 from 2023-05-08, the name of the iteration variable 134# could contain colons, which affected variable expressions having this exact 135# modifier. This possibility was neither intended nor documented. 136NUMBERS= one two three 137# expect+1: invalid character ':' in .for loop variable name 138.for NUMBERS:M*e in replaced 139. info ${NUMBERS} ${NUMBERS:M*e} 140.endfor 141 142# Before for.c 1.173 from 2023-05-08, the name of the iteration variable 143# could contain braces, which allowed to replace sequences of variable 144# expressions. This possibility was neither intended nor documented. 145BASENAME= one 146EXT= .c 147# expect+1: invalid character '}' in .for loop variable name 148.for BASENAME}${EXT in replaced 149. info ${BASENAME}${EXT} 150.endfor 151 152# Demonstrate the various ways to refer to the iteration variable. 153i= outer 154i2= two 155i,= comma 156.for i in inner 157. info . $$i: $i 158. info . $${i}: ${i} 159. info . $${i:M*}: ${i:M*} 160. info . $$(i): $(i) 161. info . $$(i:M*): $(i:M*) 162. info . $${i$${:U}}: ${i${:U}} 163. info . $${i\}}: ${i\}} # XXX: unclear why ForLoop_SubstVarLong needs this 164. info . $${i2}: ${i2} 165. info . $${i,}: ${i,} 166. info . adjacent: $i${i}${i:M*}$i 167.endfor 168# expect-11: . $i: inner 169# expect-11: . ${i}: inner 170# expect-11: . ${i:M*}: inner 171# expect-11: . $(i): inner 172# expect-11: . $(i:M*): inner 173# expect-11: . ${i${:U}}: outer 174# expect-11: . ${i\}}: inner} 175# expect-11: . ${i2}: two 176# expect-11: . ${i,}: comma 177# expect-11: . adjacent: innerinnerinnerinner 178 179# Before for.c 1.173 from 2023-05-08, the variable name could be a single '$' 180# since there was no check on valid variable names. ForLoop_SubstVarShort 181# skipped "stupid" variable names though, but ForLoop_SubstVarLong naively 182# parsed the body of the loop, substituting each '${$}' with an actual 183# '${:Udollar}'. 184# expect+1: invalid character '$' in .for loop variable name 185.for $ in dollar 186. info eight $$$$$$$$ and no cents. 187. info eight ${$}${$}${$}${$} and no cents. 188.endfor 189# Outside a .for loop, '${$}' is interpreted differently. The outer '$' starts 190# a variable expression. The inner '$' is followed by a '}' and is thus a 191# silent syntax error, the '$' is skipped. The variable name is thus '', and 192# since since there is never a variable named '', the whole expression '${$}' 193# evaluates to an empty string. 194closing-brace= } # guard against an 195${closing-brace}= <closing-brace> # alternative interpretation 196# expect+1: eight and no cents. 197.info eight ${$}${$}${$}${$} and no cents. 198 199# What happens if the values from the .for loop contain a literal newline? 200# Before for.c 1.144 from 2021-06-25, the newline was passed verbatim to the 201# body of the .for loop, where it was then interpreted as a literal newline, 202# leading to syntax errors such as "Unclosed variable expression" in the upper 203# line and "Invalid line type" in the lower line. 204# 205# The error message occurs in the line of the .for loop since that's the place 206# where the body of the .for loop is constructed, and at this point the 207# newline character gets replaced with a plain space. 208# expect+2: newline in .for value 209# expect+1: newline in .for value 210.for i in "${.newline}" 211. info short: $i 212. info long: ${i} 213.endfor 214# expect-3: short: " " 215# expect-3: long: " " 216 217# No error since the newline character is not actually used. 218.for i in "${.newline}" 219.endfor 220 221# Between for.c 1.161 from 2022-01-08 and before for.c 1.163 from 2022-01-09, 222# a newline character in a .for loop led to a crash since at the point where 223# the error message including the stack trace is printed, the body of the .for 224# loop is assembled, and at that point, ForLoop.nextItem had already been 225# advanced. 226.MAKEFLAGS: -dp 227# expect+1: newline in .for value 228.for i in "${.newline}" 229: $i 230.endfor 231.MAKEFLAGS: -d0 232 233.MAKEFLAGS: -df 234.for i in \# \\\# 235# $i 236.endfor 237 238.for i in $$ $$i $$(i) $${i} $$$$ $$$$$$$$ $${:U\$$\$$} 239# $i 240.endfor 241 242# The expression '${.TARGET}' must be preserved as it is one of the 7 built-in 243# target-local variables. See for.c 1.45 from 2009-01-14. 244.for i in ${.TARGET} $${.TARGET} $$${.TARGET} $$$${.TARGET} 245# $i 246.endfor 247# expect: # ${:U${.TARGET}} 248# XXX: Why does '$' result in the same text as '$$'? 249# expect: # ${:U${.TARGET}} 250# XXX: Why does the '$$' before the '${.TARGET}' lead to an escaped '}'? 251# expect: # ${:U$${.TARGET\}} 252# XXX: Why does '$' result in the same text as '$$'? 253# XXX: Why does the '$$' before the '${.TARGET}' lead to an escaped '}'? 254# expect: # ${:U$${.TARGET\}} 255 256.for i in ((( {{{ ))) }}} 257# $i 258.endfor 259.MAKEFLAGS: -d0 260 261all: 262