1# $NetBSD: varmod.mk,v 1.30 2025/06/29 11:27:21 rillig Exp $ 2# 3# Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback. 4# 5# See also: 6# varparse-errors.mk 7 8# As of 2024-06-05, the possible behaviors during parsing are: 9# 10# * `strict`: the parsing style used by most modifiers: 11# * either uses `ParseModifierPart` or parses the modifier literal 12# * other modifiers may follow, separated by a ':' 13# 14# * `greedy`: calls `ParseModifierPart` with `ch->endc`; this means 15# that no further modifiers are parsed in that expression. 16# 17# * `no-colon`: after parsing this modifier, the following modifier 18# does not need to be separated by a colon. 19# Omitting this colon is bad style. 20# 21# * `individual`: parsing this modifier does not follow the common 22# pattern of calling `ParseModifierPart`. 23# 24# The SysV column says whether a modifier falls back trying the `:from=to` 25# System V modifier. Remarks: 26# 27# In the assignment modifiers `::=` and its variants, the `=` is part of 28# the modifier name, so they never fall back to the `:from=to` modifier. 29# 30# All no-colon modifiers get a "no", as the modifier name would be 31# trimmed off before the `:from=to` modifier could see them, for 32# example, ${VAR:LAR=ALUE} and ${VAR:L:AR=ALUE} behave the same. 33# 34# | **Modifier** | **Behavior** | **Remarks** | **SysV** | 35# |--------------|--------------|--------------------|----------| 36# | ! | no-colon | | no | 37# | := | greedy | | no | 38# | :?= | greedy | | no | 39# | :+= | greedy | | no | 40# | :!= | greedy | | no | 41# | ?: | greedy | | no | 42# | @ | no-colon | | no | 43# | C | no-colon | | no | 44# | D | individual | custom parser | no | 45# | E | strict | | yes | 46# | H | strict | | yes | 47# | L | no-colon | | no | 48# | M | individual | custom parser | no | 49# | N | individual | custom parser | no | 50# | O | strict | only literal value | yes | 51# | P | no-colon | | no | 52# | Q | strict | | yes | 53# | R | strict | | yes | 54# | S | no-colon | | no | 55# | T | strict | | yes | 56# | U | individual | custom parser | no | 57# | [ | strict | | no | 58# | _ | individual | strcspn | no | 59# | gmtime | strict | | no | 60# | hash | strict | | yes | 61# | localtime | strict | | no | 62# | q | strict | | yes | 63# | range | strict | | no | 64# | sh | strict | | yes | 65# | t | strict | | yes | 66# | u | strict | | yes | 67# | from=to | greedy | SysV, fallback | --- | 68 69# These tests assume 70.MAKE.SAVE_DOLLARS = yes 71 72DOLLAR1= $$ 73DOLLAR2= ${:U\$} 74 75# To get a single '$' sign in the value of an expression, it has to 76# be written as '$$' in a literal variable value. 77# 78# See Var_Parse, where it calls Var_Subst. 79.if ${DOLLAR1} != "\$" 80. error 81.endif 82 83# Another way to get a single '$' sign is to use the :U modifier. In the 84# argument of that modifier, a '$' is escaped using the backslash instead. 85# 86# See Var_Parse, where it calls Var_Subst. 87.if ${DOLLAR2} != "\$" 88. error 89.endif 90 91# It is also possible to use the :U modifier directly in the expression. 92# 93# See Var_Parse, where it calls Var_Subst. 94.if ${:U\$} != "\$" 95. error 96.endif 97 98# XXX: As of 2020-09-13, it is not possible to use '$$' in a variable name 99# to mean a single '$'. This contradicts the manual page, which says that 100# '$' can be escaped as '$$'. 101.if ${$$:L} != "" 102. error 103.endif 104 105# In lint mode, make prints helpful error messages. 106# For compatibility, make does not print these error messages in normal mode. 107# Should it? 108.MAKEFLAGS: -dL 109# expect+2: To escape a dollar, use \$, not $$, at "$$:L} != """ 110# expect+1: Invalid variable name ":", at "$:L} != """ 111.if ${$$:L} != "" 112. error 113.endif 114 115# A '$' followed by nothing is an error as well. 116# expect+1: Dollar followed by nothing 117.if ${:Uword:@word@${word}$@} != "word" 118. error 119.endif 120 121# The modifier :P does not fall back to the SysV modifier. 122# Therefore the modifier :P=RE generates a parse error. 123VAR= STOP 124# expect+1: Missing delimiter ":" after modifier "P" 125.if ${VAR:P=RE} != "STORE" 126. error 127.else 128. error 129.endif 130 131# Test the word selection modifier ':[n]' with a very large number that is 132# larger than ULONG_MAX for any supported platform. 133# expect+1: Invalid modifier ":[99333000222000111000]" 134.if ${word:L:[99333000222000111000]} 135.endif 136# expect+1: Invalid modifier ":[2147483648]" 137.if ${word:L:[2147483648]} 138.endif 139 140# Test the range generation modifier ':range=n' with a very large number that 141# is larger than SIZE_MAX for any supported platform. 142# expect+1: Invalid number "99333000222000111000}" for modifier ":range" 143.if ${word:L:range=99333000222000111000} 144.endif 145 146# In an indirect modifier, the delimiter is '\0', which at the same time marks 147# the end of the string. The sequence '\\' '\0' is not an escaped delimiter, 148# as it would be wrong to skip past the end of the string. 149# expect+1: Invalid time value "\" 150.if ${:${:Ugmtime=\\}} 151. error 152.endif 153 154# Test a '$' at the end of a modifier part, for all modifiers in the order 155# listed in ApplyModifier. 156# 157# The only modifier parts where an unescaped '$' makes sense at the end are 158# the 'from' parts of the ':S' and ':C' modifiers. In all other modifier 159# parts, an unescaped '$' is an undocumented and discouraged edge case, as it 160# means the same as an escaped '$'. 161.if ${:U:!printf '%s\n' $!} != "\$" 162. error 163.endif 164# expect+1: Dollar followed by nothing 165.if ${VAR::=value$} != "" || ${VAR} != "value" 166. error 167.endif 168${:U }= <space> 169# expect+2: Dollar followed by nothing 170# expect+1: Dollar followed by nothing 171.if ${VAR::+=appended$} != "" || ${VAR} != "value<space>appended" 172. error 173.endif 174.if ${1:?then$:else$} != "then\$" 175. error 176.endif 177.if ${0:?then$:else$} != "else\$" 178. error 179.endif 180# expect+1: Dollar followed by nothing 181.if ${word:L:@w@$w$@} != "word" 182. error 183.endif 184# expect+1: Invalid modifier ":[$]" 185.if ${word:[$]} 186. error 187.else 188. error 189.endif 190VAR_DOLLAR= VAR$$ 191.if ${word:L:_=VAR$} != "word" || ${${VAR_DOLLAR}} != "word" 192. error 193.endif 194.if ${word:L:C,d$,m,} != "worm" 195. error 196.endif 197.if ${word:L:C,d,$,} != "wor\$" 198. error 199.endif 200# expect+2: Dollar followed by nothing 201# expect+1: Invalid variable name "}", at "$} != "set"" 202.if ${VAR:Dset$} != "set" 203. error 204.endif 205# expect+1: Invalid variable name "}", at "$} != "fallback"" 206.if ${:Ufallback$} != "fallback" 207. error 208.endif 209# expect+1: Invalid time value "1000$" 210.if ${%y:L:gmtime=1000$} 211. error 212.else 213. error 214.endif 215# expect+1: Invalid time value "1000$" 216.if ${%y:L:localtime=1000$} 217. error 218.else 219. error 220.endif 221# expect+1: Dollar followed by nothing 222.if ${word:L:Mw*$} != "word" 223. error 224.endif 225# expect+1: Dollar followed by nothing 226.if ${word:L:NX*$} != "word" 227. error 228.endif 229# expect+1: Invalid argument "fallback$" for modifier ":mtime" 230.if ${.:L:mtime=fallback$} 231. error 232.else 233. error 234.endif 235.if ${word:L:S,d$,m,} != "worm" 236. error 237.endif 238.if ${word:L:S,d,m$,} != "worm\$" 239. error 240.endif 241 242.undef VAR 243# expect+1: Missing delimiter ":" after modifier "L" 244.if ${VAR:LAR=ALUE} != "VALUE" 245. error 246.endif 247.if ${VAR:L:AR=ALUE} != "VALUE" 248. error 249.endif 250 251 252# When an expression has the usual form ${...} with braces, 253# in the part of a modifier, ":}\$" can be escaped using a backslash. 254# All other characters are passed through unmodified. 255# expect+1: Invalid time value " : } \ $ ) \) ( " 256.if ${%Y:L:localtime= \: \} \\ \$ ) \) ( :M*} != ": } \\ \$ ) \\) (" 257. error 258.endif 259# When an expression has the unusual form $(...) with parentheses, 260# in the part of a modifier, ":)\$" can be escaped using a backslash. 261# All other characters are passed through unmodified. 262# expect+1: Invalid time value " : \) \ $ " 263.if ${%Y:L:localtime= \: \) \\ \$ } \} { :M*} != ": ) \\ \$ } \\} {" 264. error 265.endif 266# Same when the modifier is the last modifier in an expression. 267# expect+1: Invalid time value " : } \ $ ) \) ( " 268.if ${%Y:L:localtime= \: \} \\ \$ ) \) ( } != " : } \\ \$ ) \\) ( " 269. error 270.endif 271# Same when the modifier is the last modifier in an expression. 272# expect+1: Invalid time value " : \) \ $ " 273.if ${%Y:L:localtime= \: \) \\ \$ } \} { } != " : ) \\ \$ } \\} { " 274. error 275.endif 276