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 72 DOLLAR1= $$ 73 DOLLAR2= ${: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. 123 VAR= 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 190 VAR_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