Home | History | Annotate | Line # | Download | only in unit-tests
      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