1*759b177aSSimon J. Gerraty# $NetBSD: varmod-sysv.mk,v 1.24 2025/03/30 00:35:52 rillig Exp $ 22c3632d1SSimon J. Gerraty# 3b0c40a00SSimon J. Gerraty# Tests for the variable modifier ':from=to', which replaces the suffix 42c3632d1SSimon J. Gerraty# "from" with "to". It can also use '%' as a wildcard. 52c3632d1SSimon J. Gerraty# 62c3632d1SSimon J. Gerraty# This modifier is applied when the other modifiers don't match exactly. 7956e45f6SSimon J. Gerraty# 8956e45f6SSimon J. Gerraty# See ApplyModifier_SysV. 92c3632d1SSimon J. Gerraty 10b0c40a00SSimon J. Gerraty# A typical use case for the modifier ':from=to' is conversion of filename 11956e45f6SSimon J. Gerraty# extensions. 12956e45f6SSimon J. Gerraty.if ${src.c:L:.c=.o} != "src.o" 13956e45f6SSimon J. Gerraty. error 14956e45f6SSimon J. Gerraty.endif 152c3632d1SSimon J. Gerraty 16956e45f6SSimon J. Gerraty# The modifier applies to each word on its own. 17956e45f6SSimon J. Gerraty.if ${one.c two.c three.c:L:.c=.o} != "one.o two.o three.o" 18956e45f6SSimon J. Gerraty. error 19956e45f6SSimon J. Gerraty.endif 20956e45f6SSimon J. Gerraty 21956e45f6SSimon J. Gerraty# Words that don't match the pattern are passed unmodified. 22956e45f6SSimon J. Gerraty.if ${src.c src.h:L:.c=.o} != "src.o src.h" 23956e45f6SSimon J. Gerraty. error 24956e45f6SSimon J. Gerraty.endif 25956e45f6SSimon J. Gerraty 26b0c40a00SSimon J. Gerraty# The modifier ':from=to' is therefore often combined with the modifier ':M'. 27956e45f6SSimon J. Gerraty.if ${src.c src.h:L:M*.c:.c=.o} != "src.o" 28956e45f6SSimon J. Gerraty. error 29956e45f6SSimon J. Gerraty.endif 30956e45f6SSimon J. Gerraty 31b0c40a00SSimon J. Gerraty# Another use case for the modifier ':from=to' is to append a suffix to each 32956e45f6SSimon J. Gerraty# word. In this case, the "from" string is empty, therefore it always 33b0c40a00SSimon J. Gerraty# matches. The same effect can be achieved with the modifier ':S,$,teen,'. 34956e45f6SSimon J. Gerraty.if ${four six seven nine:L:=teen} != "fourteen sixteen seventeen nineteen" 35956e45f6SSimon J. Gerraty. error 36956e45f6SSimon J. Gerraty.endif 37956e45f6SSimon J. Gerraty 38b0c40a00SSimon J. Gerraty# The modifier ':from=to' can also be used to surround each word by strings. 39956e45f6SSimon J. Gerraty# It might be tempting to use this for enclosing a string in quotes for the 40b0c40a00SSimon J. Gerraty# shell, but that's the job of the modifier ':Q'. 41956e45f6SSimon J. Gerraty.if ${one two three:L:%=(%)} != "(one) (two) (three)" 42956e45f6SSimon J. Gerraty. error 43956e45f6SSimon J. Gerraty.endif 44956e45f6SSimon J. Gerraty 45b0c40a00SSimon J. Gerraty# When the modifier ':from=to' is parsed, it lasts until the closing brace 46b0c40a00SSimon J. Gerraty# or parenthesis. The ':Q' in the below expression may look like a modifier 47b0c40a00SSimon J. Gerraty# but it isn't. It is part of the replacement string. 48956e45f6SSimon J. Gerraty.if ${a b c d e:L:%a=x:Q} != "x:Q b c d e" 49956e45f6SSimon J. Gerraty. error 50956e45f6SSimon J. Gerraty.endif 51956e45f6SSimon J. Gerraty 52d5e0a182SSimon J. Gerraty# In the modifier ':from=to', both parts can contain expressions. 53956e45f6SSimon J. Gerraty.if ${one two:L:${:Uone}=${:U1}} != "1 two" 54956e45f6SSimon J. Gerraty. error 55956e45f6SSimon J. Gerraty.endif 56956e45f6SSimon J. Gerraty 57b0c40a00SSimon J. Gerraty# In the modifier ':from=to', the "from" part is expanded exactly once. 58956e45f6SSimon J. Gerraty.if ${:U\$ \$\$ \$\$\$\$:${:U\$\$\$\$}=4} != "\$ \$\$ 4" 59956e45f6SSimon J. Gerraty. error 60956e45f6SSimon J. Gerraty.endif 61956e45f6SSimon J. Gerraty 62b0c40a00SSimon J. Gerraty# In the modifier ':from=to', the "to" part is expanded exactly twice. 63956e45f6SSimon J. Gerraty# XXX: The right-hand side should be expanded only once. 64956e45f6SSimon J. Gerraty# XXX: It's hard to get the escaping correct here, and to read that. 65956e45f6SSimon J. Gerraty# XXX: It's not intuitive why the closing brace must be escaped but not 66956e45f6SSimon J. Gerraty# the opening brace. 67956e45f6SSimon J. Gerraty.if ${:U1 2 4:4=${:Uonce\${\:Utwice\}}} != "1 2 oncetwice" 68956e45f6SSimon J. Gerraty. error 69956e45f6SSimon J. Gerraty.endif 70956e45f6SSimon J. Gerraty 71956e45f6SSimon J. Gerraty# The replacement string can contain spaces, thereby changing the number 72d5e0a182SSimon J. Gerraty# of words in the expression. 73956e45f6SSimon J. Gerraty.if ${In:L:%=% ${:Uthe Sun}} != "In the Sun" 74956e45f6SSimon J. Gerraty. error 75956e45f6SSimon J. Gerraty.endif 76956e45f6SSimon J. Gerraty 77956e45f6SSimon J. Gerraty# If the variable value is empty, it is debatable whether it consists of a 78b0c40a00SSimon J. Gerraty# single empty word, or no word at all. The modifier ':from=to' treats it as 79956e45f6SSimon J. Gerraty# no word at all. 8006b9b3e0SSimon J. Gerraty# 8106b9b3e0SSimon J. Gerraty# See SysVMatch, which doesn't handle w_len == p_len specially. 82956e45f6SSimon J. Gerraty.if ${:L:=suffix} != "" 83956e45f6SSimon J. Gerraty. error 84956e45f6SSimon J. Gerraty.endif 85956e45f6SSimon J. Gerraty 86956e45f6SSimon J. Gerraty# If the variable value is empty, it is debatable whether it consists of a 8706b9b3e0SSimon J. Gerraty# single empty word (before 2020-05-06), or no word at all (since 2020-05-06). 8806b9b3e0SSimon J. Gerraty# 8906b9b3e0SSimon J. Gerraty# See SysVMatch, percent != NULL && w[0] == '\0'. 90956e45f6SSimon J. Gerraty.if ${:L:%=suffix} != "" 91956e45f6SSimon J. Gerraty. error 92956e45f6SSimon J. Gerraty.endif 932c3632d1SSimon J. Gerraty 942c3632d1SSimon J. Gerraty# Before 2020-07-19, an ampersand could be used in the replacement part 95956e45f6SSimon J. Gerraty# of a SysV substitution modifier, and it was replaced with the whole match, 96b0c40a00SSimon J. Gerraty# just like in the modifier ':S'. 97956e45f6SSimon J. Gerraty# 98956e45f6SSimon J. Gerraty# This was probably a copy-and-paste mistake since the code for the SysV 99b0c40a00SSimon J. Gerraty# modifier looked a lot like the code for the modifiers ':S' and ':C'. 100956e45f6SSimon J. Gerraty# The ampersand is not mentioned in the manual page. 101956e45f6SSimon J. Gerraty.if ${a.bcd.e:L:a.%=%} != "bcd.e" 102956e45f6SSimon J. Gerraty. error 103956e45f6SSimon J. Gerraty.endif 104956e45f6SSimon J. Gerraty# Before 2020-07-19, the result of the expression was "a.bcd.e". 105956e45f6SSimon J. Gerraty.if ${a.bcd.e:L:a.%=&} != "&" 106956e45f6SSimon J. Gerraty. error 107956e45f6SSimon J. Gerraty.endif 1082c3632d1SSimon J. Gerraty 1092c3632d1SSimon J. Gerraty# Before 2020-07-20, when a SysV modifier was parsed, a single dollar 110956e45f6SSimon J. Gerraty# before the '=' was parsed (but not interpreted) as an anchor. 111956e45f6SSimon J. Gerraty# Parsing something without then evaluating it accordingly doesn't make 112b0c40a00SSimon J. Gerraty# sense, so this has been fixed. 113956e45f6SSimon J. Gerraty.if ${value:L:e$=x} != "value" 114956e45f6SSimon J. Gerraty. error 115956e45f6SSimon J. Gerraty.endif 116b0c40a00SSimon J. Gerraty# Before 2020-07-20, the modifier ':e$=x' was parsed as having a left-hand 117b0c40a00SSimon J. Gerraty# side 'e' and a right-hand side 'x'. The dollar was parsed (but not 118956e45f6SSimon J. Gerraty# interpreted) as 'anchor at the end'. Therefore the modifier was equivalent 119b0c40a00SSimon J. Gerraty# to ':e=x', which doesn't match the string "value$". Therefore the whole 120956e45f6SSimon J. Gerraty# expression evaluated to "value$". 121956e45f6SSimon J. Gerraty.if ${${:Uvalue\$}:L:e$=x} != "valux" 122956e45f6SSimon J. Gerraty. error 123956e45f6SSimon J. Gerraty.endif 124956e45f6SSimon J. Gerraty.if ${value:L:e=x} != "valux" 125956e45f6SSimon J. Gerraty. error 126956e45f6SSimon J. Gerraty.endif 1272c3632d1SSimon J. Gerraty 1282c3632d1SSimon J. Gerraty# Words that don't match are copied unmodified. 129956e45f6SSimon J. Gerraty.if ${:Ufile.c file.h:%.c=%.cpp} != "file.cpp file.h" 130956e45f6SSimon J. Gerraty. error 1312c3632d1SSimon J. Gerraty.endif 132956e45f6SSimon J. Gerraty 133956e45f6SSimon J. Gerraty# The % placeholder can be anywhere in the string, it doesn't have to be at 134956e45f6SSimon J. Gerraty# the beginning of the pattern. 135956e45f6SSimon J. Gerraty.if ${:Ufile.c other.c:file.%=renamed.%} != "renamed.c other.c" 136956e45f6SSimon J. Gerraty. error 137956e45f6SSimon J. Gerraty.endif 138956e45f6SSimon J. Gerraty 139956e45f6SSimon J. Gerraty# It's also possible to modify each word by replacing the prefix and adding 140956e45f6SSimon J. Gerraty# a suffix. 141956e45f6SSimon J. Gerraty.if ${one two:L:o%=a%w} != "anew two" 142956e45f6SSimon J. Gerraty. error 143956e45f6SSimon J. Gerraty.endif 144956e45f6SSimon J. Gerraty 145956e45f6SSimon J. Gerraty# Each word gets the suffix "X" appended. 146956e45f6SSimon J. Gerraty.if ${one two:L:=X} != "oneX twoX" 147956e45f6SSimon J. Gerraty. error 148956e45f6SSimon J. Gerraty.endif 149956e45f6SSimon J. Gerraty 150956e45f6SSimon J. Gerraty# The suffix "o" is replaced with "X". 151956e45f6SSimon J. Gerraty.if ${one two:L:o=X} != "one twX" 152956e45f6SSimon J. Gerraty. error 153956e45f6SSimon J. Gerraty.endif 154956e45f6SSimon J. Gerraty 155956e45f6SSimon J. Gerraty# The suffix "o" is replaced with nothing. 156956e45f6SSimon J. Gerraty.if ${one two:L:o=} != "one tw" 157956e45f6SSimon J. Gerraty. error 158956e45f6SSimon J. Gerraty.endif 159956e45f6SSimon J. Gerraty 160956e45f6SSimon J. Gerraty# The suffix "o" is replaced with a literal percent. The percent is only 161956e45f6SSimon J. Gerraty# a wildcard when it appears on the left-hand side. 162956e45f6SSimon J. Gerraty.if ${one two:L:o=%} != "one tw%" 163956e45f6SSimon J. Gerraty. error 164956e45f6SSimon J. Gerraty.endif 165956e45f6SSimon J. Gerraty 166956e45f6SSimon J. Gerraty# Each word with the suffix "o" is replaced with "X". The percent is a 167956e45f6SSimon J. Gerraty# wildcard even though the right-hand side does not contain another percent. 168956e45f6SSimon J. Gerraty.if ${one two:L:%o=X} != "one X" 169956e45f6SSimon J. Gerraty. error 170956e45f6SSimon J. Gerraty.endif 171956e45f6SSimon J. Gerraty 172956e45f6SSimon J. Gerraty# Each word with the prefix "o" is replaced with "X". The percent is a 173956e45f6SSimon J. Gerraty# wildcard even though the right-hand side does not contain another percent. 174956e45f6SSimon J. Gerraty.if ${one two:L:o%=X} != "X two" 175956e45f6SSimon J. Gerraty. error 176956e45f6SSimon J. Gerraty.endif 177956e45f6SSimon J. Gerraty 178956e45f6SSimon J. Gerraty# For each word with the prefix "o" and the suffix "e", the whole word is 179956e45f6SSimon J. Gerraty# replaced with "X". 180956e45f6SSimon J. Gerraty.if ${one two oe oxen:L:o%e=X} != "X two X oxen" 181956e45f6SSimon J. Gerraty. error 182956e45f6SSimon J. Gerraty.endif 183956e45f6SSimon J. Gerraty 184956e45f6SSimon J. Gerraty# Only the first '%' is the wildcard. 185956e45f6SSimon J. Gerraty.if ${one two o%e other%e:L:o%%e=X} != "one two X X" 186956e45f6SSimon J. Gerraty. error 187956e45f6SSimon J. Gerraty.endif 188956e45f6SSimon J. Gerraty 189956e45f6SSimon J. Gerraty# In the replacement, only the first '%' is the placeholder, all others 190956e45f6SSimon J. Gerraty# are literal percent characters. 191956e45f6SSimon J. Gerraty.if ${one two:L:%=%%} != "one% two%" 192956e45f6SSimon J. Gerraty. error 193956e45f6SSimon J. Gerraty.endif 194956e45f6SSimon J. Gerraty 195956e45f6SSimon J. Gerraty# In the word "one", only a prefix of the pattern suffix "nes" matches, 196956e45f6SSimon J. Gerraty# the whole word is too short. Therefore it doesn't match. 197956e45f6SSimon J. Gerraty.if ${one two:L:%nes=%xxx} != "one two" 198956e45f6SSimon J. Gerraty. error 199956e45f6SSimon J. Gerraty.endif 200956e45f6SSimon J. Gerraty 201b0c40a00SSimon J. Gerraty# The modifier ':from=to' can be used to replace both the prefix and a suffix 202956e45f6SSimon J. Gerraty# of a word with other strings. This is not possible with a single :S 203956e45f6SSimon J. Gerraty# modifier, and using a :C modifier for the same task looks more complicated 204956e45f6SSimon J. Gerraty# in many cases. 205956e45f6SSimon J. Gerraty.if ${prefix-middle-suffix:L:prefix-%-suffix=p-%-s} != "p-middle-s" 206956e45f6SSimon J. Gerraty. error 207956e45f6SSimon J. Gerraty.endif 208956e45f6SSimon J. Gerraty 209d5e0a182SSimon J. Gerraty# This is not a SysV modifier since the nested expression expands 210956e45f6SSimon J. Gerraty# to an empty string. The '=' in it should be irrelevant during parsing. 21122619282SSimon J. Gerraty# XXX: As of 2024-06-30, this expression generates an "Unfinished modifier" 21206b9b3e0SSimon J. Gerraty# error, while the correct error message would be "Unknown modifier" since 21306b9b3e0SSimon J. Gerraty# there is no modifier named "fromto". 214*759b177aSSimon J. Gerraty# expect+1: Unfinished modifier after "from${:D=}to}", expecting "=" 21522619282SSimon J. Gerraty.if ${word216:L:from${:D=}to} 216956e45f6SSimon J. Gerraty. error 217956e45f6SSimon J. Gerraty.endif 218956e45f6SSimon J. Gerraty 219956e45f6SSimon J. Gerraty# XXX: This specially constructed case demonstrates that the SysV modifier 220956e45f6SSimon J. Gerraty# lasts longer than expected. The whole expression initially has the value 221956e45f6SSimon J. Gerraty# "fromto}...". The next modifier is a SysV modifier. ApplyModifier_SysV 222956e45f6SSimon J. Gerraty# parses the modifier as "from${:D=}to", ending at the '}'. Next, the two 223956e45f6SSimon J. Gerraty# parts of the modifier are parsed using ParseModifierPart, which scans 224d5e0a182SSimon J. Gerraty# differently, properly handling nested expressions. The two parts 225956e45f6SSimon J. Gerraty# are now "fromto}..." and "replaced". 226956e45f6SSimon J. Gerraty.if "${:Ufromto\}...:from${:D=}to}...=replaced}" != "replaced" 227956e45f6SSimon J. Gerraty. error 228956e45f6SSimon J. Gerraty.endif 229956e45f6SSimon J. Gerraty 230956e45f6SSimon J. Gerraty# As of 2020-10-06, the right-hand side of the SysV modifier is expanded 231956e45f6SSimon J. Gerraty# twice. The first expansion happens in ApplyModifier_SysV, where the 232956e45f6SSimon J. Gerraty# modifier is split into its two parts. The second expansion happens 233956e45f6SSimon J. Gerraty# when each word is replaced in ModifyWord_SYSVSubst. 234956e45f6SSimon J. Gerraty# XXX: This is unexpected. Add more test case to demonstrate the effects 235956e45f6SSimon J. Gerraty# of removing one of the expansions. 236956e45f6SSimon J. GerratyVALUE= value 237956e45f6SSimon J. GerratyINDIRECT= 1:${VALUE} 2:$${VALUE} 4:$$$${VALUE} 238956e45f6SSimon J. Gerraty.if ${x:L:x=${INDIRECT}} != "1:value 2:value 4:\${VALUE}" 239956e45f6SSimon J. Gerraty. error 240956e45f6SSimon J. Gerraty.endif 241956e45f6SSimon J. Gerraty 242b0c40a00SSimon J. Gerraty# Test all relevant combinations of prefix, '%' and suffix in both the pattern 243b0c40a00SSimon J. Gerraty# and the replacement. 244b0c40a00SSimon J. Gerraty!=1>&2 printf '%-24s %-24s %-24s\n' 'word' 'modifier' 'result' 245b0c40a00SSimon J. Gerraty.for from in '' ffix % pre% %ffix pre%ffix 246b0c40a00SSimon J. Gerraty. for to in '' NS % %NS NPre% NPre%NS 247b0c40a00SSimon J. Gerraty. for word in '' suffix prefix pre-middle-suffix 248b0c40a00SSimon J. Gerraty. for mod in ${from:N''}=${to:N''} 249b0c40a00SSimon J. Gerraty!=1>&2 printf '%-24s %-24s "%s"\n' ''${word:Q} ''${mod:Q} ''${word:N'':${mod}:Q} 250b0c40a00SSimon J. Gerraty. endfor 251b0c40a00SSimon J. Gerraty. endfor 252b0c40a00SSimon J. Gerraty. endfor 253b0c40a00SSimon J. Gerraty.endfor 254b0c40a00SSimon J. Gerraty 2558d5c8e21SSimon J. Gerraty 2568d5c8e21SSimon J. Gerraty# The error case of an unfinished ':from=to' modifier after the '=' requires 2578d5c8e21SSimon J. Gerraty# an expression that is missing the closing '}'. 258*759b177aSSimon J. Gerraty# expect+1: Unfinished modifier after "$(})", expecting "}" 2598d5c8e21SSimon J. Gerraty.if ${error:L:from=$(}) 2608d5c8e21SSimon J. Gerraty.endif 2618d5c8e21SSimon J. Gerraty 2628d5c8e21SSimon J. Gerraty 263*759b177aSSimon J. Gerraty# The various ":t..." modifiers fall back to the ":from=to" modifier. 264*759b177aSSimon J. Gerraty.if ${:Utarget:target=source} != "source" 265*759b177aSSimon J. Gerraty. error 266*759b177aSSimon J. Gerraty.endif 267*759b177aSSimon J. Gerraty.if ${:Ufile.ts:ts=js} != "file.js" 268*759b177aSSimon J. Gerraty. error 269*759b177aSSimon J. Gerraty.endif 270*759b177aSSimon J. Gerraty.if ${:Ufile.tsx:tsx=jsx} != "file.jsx" 271*759b177aSSimon J. Gerraty. error 272*759b177aSSimon J. Gerraty.endif 273*759b177aSSimon J. Gerraty.if ${:Ufile.ts\\part:ts\part=replaced} != "file.replaced" 274*759b177aSSimon J. Gerraty. error 275*759b177aSSimon J. Gerraty.endif 276*759b177aSSimon J. Gerraty.if ${:Ufile.ts\\123xyz:ts\123xyz=gone} != "file.gone" 277*759b177aSSimon J. Gerraty. error 278*759b177aSSimon J. Gerraty.endif 279*759b177aSSimon J. Gerraty# Since the ":ts=" modifier is a valid form of the ":ts" modifier, don't fall 280*759b177aSSimon J. Gerraty# back to the ":from=to" modifier. 281*759b177aSSimon J. Gerraty.if ${:U1 2 3 file.ts:ts=} != "1=2=3=file.ts" 282*759b177aSSimon J. Gerraty. error 283*759b177aSSimon J. Gerraty.endif 284*759b177aSSimon J. Gerraty 285*759b177aSSimon J. Gerraty 286956e45f6SSimon J. Gerratyall: 287