1# $NetBSD: varmod.mk,v 1.15 2024/06/06 20:41:50 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 parse error in the modifier falls back
25# trying the `:from=to` System V modifier.
26#
27# | **Operator** | **Behavior** | **Remarks**        | **SysV** |
28# |--------------|--------------|--------------------|----------|
29# | `!`          | no-colon     |                    | no       |
30# | `:=`         | greedy       |                    | yes      |
31# | `?:`         | greedy       |                    | no       |
32# | `@`          | no-colon     |                    | no       |
33# | `C`          | no-colon     |                    | no       |
34# | `D`          | individual   | custom parser      | N/A      |
35# | `E`          | strict       |                    | yes      |
36# | `H`          | strict       |                    | yes      |
37# | `L`          | no-colon     |                    | N/A      |
38# | `M`          | individual   | custom parser      | N/A      |
39# | `N`          | individual   | custom parser      | N/A      |
40# | `O`          | strict       | only literal value | no       |
41# | `P`          | no-colon     |                    | N/A      |
42# | `Q`          | strict       |                    | yes      |
43# | `R`          | strict       |                    | yes      |
44# | `S`          | no-colon     |                    | N/A      |
45# | `T`          | strict       |                    | N/A      |
46# | `U`          | individual   | custom parser      | N/A      |
47# | `[`          | strict       |                    | no       |
48# | `_`          | individual   | strcspn            | yes      |
49# | `gmtime`     | strict       |                    | yes      |
50# | `hash`       | strict       |                    | N/A      |
51# | `localtime`  | strict       |                    | yes      |
52# | `q`          | strict       |                    | yes      |
53# | `range`      | strict       |                    | N/A      |
54# | `sh`         | strict       |                    | N/A      |
55# | `t`          | strict       |                    | no       |
56# | `u`          | strict       |                    | yes      |
57# | `from=to`    | greedy       | SysV, fallback     | N/A      |
58
59# These tests assume
60.MAKE.SAVE_DOLLARS = yes
61
62DOLLAR1=	$$
63DOLLAR2=	${:U\$}
64
65# To get a single '$' sign in the value of an expression, it has to
66# be written as '$$' in a literal variable value.
67#
68# See Var_Parse, where it calls Var_Subst.
69.if ${DOLLAR1} != "\$"
70.  error
71.endif
72
73# Another way to get a single '$' sign is to use the :U modifier.  In the
74# argument of that modifier, a '$' is escaped using the backslash instead.
75#
76# See Var_Parse, where it calls Var_Subst.
77.if ${DOLLAR2} != "\$"
78.  error
79.endif
80
81# It is also possible to use the :U modifier directly in the expression.
82#
83# See Var_Parse, where it calls Var_Subst.
84.if ${:U\$} != "\$"
85.  error
86.endif
87
88# XXX: As of 2020-09-13, it is not possible to use '$$' in a variable name
89# to mean a single '$'.  This contradicts the manual page, which says that
90# '$' can be escaped as '$$'.
91.if ${$$:L} != ""
92.  error
93.endif
94
95# In lint mode, make prints helpful error messages.
96# For compatibility, make does not print these error messages in normal mode.
97# Should it?
98.MAKEFLAGS: -dL
99# expect+2: To escape a dollar, use \$, not $$, at "$$:L} != """
100# expect+1: Invalid variable name ':', at "$:L} != """
101.if ${$$:L} != ""
102.  error
103.endif
104
105# A '$' followed by nothing is an error as well.
106# expect+1: while evaluating "${:Uword:@word@${word}$@} != "word"": Dollar followed by nothing
107.if ${:Uword:@word@${word}$@} != "word"
108.  error
109.endif
110
111# The variable modifier :P does not fall back to the SysV modifier.
112# Therefore the modifier :P=RE generates a parse error.
113# XXX: The .error should not be reached since the expression is
114# malformed, and this error should be propagated up to Cond_EvalLine.
115VAR=	STOP
116# expect+1: while evaluating variable "VAR": Missing delimiter ':' after modifier "P"
117.if ${VAR:P=RE} != "STORE"
118# expect+1: Missing argument for ".error"
119.  error
120.endif
121
122# Test the word selection modifier ':[n]' with a very large number that is
123# larger than ULONG_MAX for any supported platform.
124# expect+1: Malformed conditional (${word:L:[99333000222000111000]})
125.if ${word:L:[99333000222000111000]}
126.endif
127# expect+1: Malformed conditional (${word:L:[2147483648]})
128.if ${word:L:[2147483648]}
129.endif
130
131# Test the range generation modifier ':range=n' with a very large number that
132# is larger than SIZE_MAX for any supported platform.
133# expect+2: Malformed conditional (${word:L:range=99333000222000111000})
134# expect+1: while evaluating variable "word": Invalid number "99333000222000111000}" for ':range' modifier
135.if ${word:L:range=99333000222000111000}
136.endif
137
138# In an indirect modifier, the delimiter is '\0', which at the same time marks
139# the end of the string.  The sequence '\\' '\0' is not an escaped delimiter,
140# as it would be wrong to skip past the end of the string.
141# expect+2: while evaluating "${:${:Ugmtime=\\}}": Invalid time value "\"
142# expect+1: Malformed conditional (${:${:Ugmtime=\\}})
143.if ${:${:Ugmtime=\\}}
144.  error
145.endif
146
147# Test a '$' at the end of a modifier part, for all modifiers in the order
148# listed in ApplyModifier.
149#
150# The only modifier parts where an unescaped '$' makes sense at the end are
151# the 'from' parts of the ':S' and ':C' modifiers.  In all other modifier
152# parts, an unescaped '$' is an undocumented and discouraged edge case, as it
153# means the same as an escaped '$'.
154.if ${:U:!printf '%s\n' $!} != "\$"
155.  error
156.endif
157# expect+1: while evaluating variable "VAR": Dollar followed by nothing
158.if ${VAR::=value$} != "" || ${VAR} != "value"
159.  error
160.endif
161${:U }=		<space>
162# expect+2: while evaluating variable "VAR": Dollar followed by nothing
163# expect+1: while evaluating variable "VAR": Dollar followed by nothing
164.if ${VAR::+=appended$} != "" || ${VAR} != "value<space>appended"
165.  error
166.endif
167.if ${1:?then$:else$} != "then\$"
168.  error
169.endif
170.if ${0:?then$:else$} != "else\$"
171.  error
172.endif
173# expect+1: while evaluating variable "word": Dollar followed by nothing
174.if ${word:L:@w@$w$@} != "word"
175.  error
176.endif
177# expect: make: Bad modifier ":[$]" for variable "word"
178# expect+1: Malformed conditional (${word:[$]})
179.if ${word:[$]}
180.  error
181.else
182.  error
183.endif
184VAR_DOLLAR=	VAR$$
185.if ${word:L:_=VAR$} != "word" || ${${VAR_DOLLAR}} != "word"
186.  error
187.endif
188.if ${word:L:C,d$,m,} != "worm"
189.  error
190.endif
191.if ${word:L:C,d,$,} != "wor\$"
192.  error
193.endif
194# expect+2: while evaluating variable "VAR": Invalid variable name '}', at "$} != "set""
195# expect+1: while evaluating variable "VAR": Dollar followed by nothing
196.if ${VAR:Dset$} != "set"
197.  error
198.endif
199# expect+1: while evaluating "${:Ufallback$} != "fallback"": Invalid variable name '}', at "$} != "fallback""
200.if ${:Ufallback$} != "fallback"
201.  error
202.endif
203# expect+2: Malformed conditional (${%y:L:gmtime=1000$})
204# expect+1: while evaluating variable "%y": Invalid time value "1000$"
205.if ${%y:L:gmtime=1000$}
206.  error
207.else
208.  error
209.endif
210# expect+2: Malformed conditional (${%y:L:localtime=1000$})
211# expect+1: while evaluating variable "%y": Invalid time value "1000$"
212.if ${%y:L:localtime=1000$}
213.  error
214.else
215.  error
216.endif
217# expect+1: while evaluating variable "word": Dollar followed by nothing
218.if ${word:L:Mw*$} != "word"
219.  error
220.endif
221# expect+1: while evaluating variable "word": Dollar followed by nothing
222.if ${word:L:NX*$} != "word"
223.  error
224.endif
225# expect+2: while evaluating variable ".": Invalid argument 'fallback$' for modifier ':mtime'
226# expect+1: Malformed conditional (${.:L:mtime=fallback$})
227.if ${.:L:mtime=fallback$}
228.  error
229.else
230.  error
231.endif
232.if ${word:L:S,d$,m,} != "worm"
233.  error
234.endif
235.if ${word:L:S,d,m$,} != "worm\$"
236.  error
237.endif
238