1 1.17 rillig # $NetBSD: varmod-subst.mk,v 1.17 2025/03/29 19:08:53 rillig Exp $ 2 1.1 rillig # 3 1.2 rillig # Tests for the :S,from,to, variable modifier. 4 1.1 rillig 5 1.2 rillig all: mod-subst 6 1.2 rillig all: mod-subst-delimiter 7 1.2 rillig all: mod-subst-chain 8 1.2 rillig all: mod-subst-dollar 9 1.1 rillig 10 1.2 rillig WORDS= sequences of letters 11 1.7 rillig 12 1.13 rillig # The empty pattern never matches anything, except if it is anchored at the 13 1.13 rillig # beginning or the end of the word. 14 1.2 rillig .if ${WORDS:S,,,} != ${WORDS} 15 1.12 rillig . error 16 1.2 rillig .endif 17 1.7 rillig 18 1.12 rillig # The :S modifier flag '1' is applied exactly once. 19 1.2 rillig .if ${WORDS:S,e,*,1} != "s*quences of letters" 20 1.12 rillig . error 21 1.2 rillig .endif 22 1.7 rillig 23 1.12 rillig # The :S modifier flag '1' is applied to the first occurrence, no matter if 24 1.12 rillig # the occurrence is in the first word or not. 25 1.3 rillig .if ${WORDS:S,f,*,1} != "sequences o* letters" 26 1.12 rillig . error 27 1.3 rillig .endif 28 1.7 rillig 29 1.12 rillig # The :S modifier replaces every first match per word. 30 1.2 rillig .if ${WORDS:S,e,*,} != "s*quences of l*tters" 31 1.12 rillig . error 32 1.2 rillig .endif 33 1.7 rillig 34 1.12 rillig # The :S modifier flag 'g' replaces every occurrence. 35 1.2 rillig .if ${WORDS:S,e,*,g} != "s*qu*nc*s of l*tt*rs" 36 1.12 rillig . error 37 1.2 rillig .endif 38 1.7 rillig 39 1.12 rillig # The '^' in the search pattern anchors the pattern at the beginning of each 40 1.12 rillig # word, thereby matching a prefix. 41 1.2 rillig .if ${WORDS:S,^sequ,occurr,} != "occurrences of letters" 42 1.12 rillig . error 43 1.2 rillig .endif 44 1.7 rillig 45 1.12 rillig # The :S modifier with a '^' anchor replaces the whole word if that word is 46 1.12 rillig # exactly the pattern. 47 1.2 rillig .if ${WORDS:S,^of,with,} != "sequences with letters" 48 1.12 rillig . error 49 1.2 rillig .endif 50 1.7 rillig 51 1.12 rillig # The :S modifier does not match if the pattern is longer than the word. 52 1.2 rillig .if ${WORDS:S,^office,does not match,} != ${WORDS} 53 1.12 rillig . warning 54 1.2 rillig .endif 55 1.7 rillig 56 1.12 rillig # The '$' in the search pattern anchors the pattern at the end of each word, 57 1.12 rillig # thereby matching a suffix. 58 1.2 rillig .if ${WORDS:S,f$,r,} != "sequences or letters" 59 1.12 rillig . error 60 1.2 rillig .endif 61 1.7 rillig 62 1.12 rillig # The :S modifier with a '$' anchor replaces at most one occurrence per word. 63 1.2 rillig .if ${WORDS:S,s$,,} != "sequence of letter" 64 1.12 rillig . error 65 1.2 rillig .endif 66 1.7 rillig 67 1.12 rillig # The :S modifier with a '$' anchor replaces the whole word if that word is 68 1.12 rillig # exactly the pattern. 69 1.2 rillig .if ${WORDS:S,of$,,} != "sequences letters" 70 1.12 rillig . error 71 1.2 rillig .endif 72 1.7 rillig 73 1.12 rillig # The :S modifier with a '$' anchor and a pattern that is longer than a word 74 1.12 rillig # cannot match that word. 75 1.2 rillig .if ${WORDS:S,eof$,,} != ${WORDS} 76 1.12 rillig . warning 77 1.2 rillig .endif 78 1.7 rillig 79 1.12 rillig # The :S modifier with the '^' and '$' anchors matches an exact word. 80 1.2 rillig .if ${WORDS:S,^of$,,} != "sequences letters" 81 1.12 rillig . error 82 1.2 rillig .endif 83 1.7 rillig 84 1.12 rillig # The :S modifier with the '^' and '$' anchors does not match a word that 85 1.12 rillig # starts with the pattern but is longer than the pattern. 86 1.2 rillig .if ${WORDS:S,^o$,,} != ${WORDS} 87 1.12 rillig . error 88 1.2 rillig .endif 89 1.7 rillig 90 1.12 rillig # The :S modifier with the '^' and '$' anchors does not match a word that ends 91 1.12 rillig # with the pattern but is longer than the pattern. 92 1.2 rillig .if ${WORDS:S,^f$,,} != ${WORDS} 93 1.12 rillig . error 94 1.2 rillig .endif 95 1.7 rillig 96 1.12 rillig # The :S modifier with the '^' and '$' anchors does not match a word if the 97 1.12 rillig # pattern ends with the word but is longer than the word. 98 1.2 rillig .if ${WORDS:S,^eof$,,} != ${WORDS} 99 1.12 rillig . error 100 1.2 rillig .endif 101 1.7 rillig 102 1.12 rillig # The :S modifier with the '^' and '$' anchors does not match a word if the 103 1.12 rillig # pattern starts with the word but is longer than the word. 104 1.2 rillig .if ${WORDS:S,^office$,,} != ${WORDS} 105 1.12 rillig . error 106 1.2 rillig .endif 107 1.2 rillig 108 1.12 rillig # Except for the '^' and '$' anchors, the pattern does not contain any special 109 1.12 rillig # characters, so the '*' from the pattern would only match a literal '*' in a 110 1.12 rillig # word. 111 1.8 rillig .if ${WORDS:S,*,replacement,} != ${WORDS} 112 1.12 rillig . error 113 1.8 rillig .endif 114 1.8 rillig 115 1.12 rillig # Except for the '^' and '$' anchors, the pattern does not contain any special 116 1.12 rillig # characters, so the '.' from the pattern would only match a literal '.' in a 117 1.12 rillig # word. 118 1.8 rillig .if ${WORDS:S,.,replacement,} != ${WORDS} 119 1.12 rillig . error 120 1.8 rillig .endif 121 1.8 rillig 122 1.12 rillig # The '&' in the replacement is a placeholder for the text matched by the 123 1.12 rillig # pattern. 124 1.9 rillig .if ${:Uvalue:S,^val,&,} != "value" 125 1.9 rillig . error 126 1.9 rillig .endif 127 1.9 rillig .if ${:Uvalue:S,ue$,&,} != "value" 128 1.9 rillig . error 129 1.9 rillig .endif 130 1.9 rillig .if ${:Uvalue:S,^val,&-&-&,} != "val-val-value" 131 1.9 rillig . error 132 1.9 rillig .endif 133 1.9 rillig .if ${:Uvalue:S,ue$,&-&-&,} != "value-ue-ue" 134 1.9 rillig . error 135 1.9 rillig .endif 136 1.9 rillig 137 1.10 rillig 138 1.10 rillig # When a word is replaced with nothing, the remaining words are separated by a 139 1.10 rillig # single space, not two. 140 1.10 rillig .if ${1 2 3:L:S,2,,} != "1 3" 141 1.10 rillig . error 142 1.10 rillig .endif 143 1.10 rillig 144 1.10 rillig 145 1.14 rillig # In an empty expression, the ':S' modifier matches a single time, but only if 146 1.14 rillig # the search string is empty and anchored at either the beginning or the end 147 1.14 rillig # of the word. 148 1.13 rillig .if ${:U:S,,out-of-nothing,} != "" 149 1.13 rillig . error 150 1.13 rillig .endif 151 1.13 rillig .if ${:U:S,^,out-of-nothing,} != "out-of-nothing" 152 1.13 rillig . error 153 1.13 rillig .endif 154 1.13 rillig .if ${:U:S,$,out-of-nothing,} != "out-of-nothing" 155 1.13 rillig . error 156 1.13 rillig .endif 157 1.13 rillig .if ${:U:S,^$,out-of-nothing,} != "out-of-nothing" 158 1.13 rillig . error 159 1.13 rillig .endif 160 1.14 rillig .if ${:U:S,,out-of-nothing,g} != "" 161 1.14 rillig . error 162 1.14 rillig .endif 163 1.14 rillig .if ${:U:S,^,out-of-nothing,g} != "out-of-nothing" 164 1.14 rillig . error 165 1.14 rillig .endif 166 1.14 rillig .if ${:U:S,$,out-of-nothing,g} != "out-of-nothing" 167 1.14 rillig . error 168 1.14 rillig .endif 169 1.14 rillig .if ${:U:S,^$,out-of-nothing,g} != "out-of-nothing" 170 1.14 rillig . error 171 1.14 rillig .endif 172 1.14 rillig .if ${:U:S,,out-of-nothing,W} != "" 173 1.14 rillig . error 174 1.14 rillig .endif 175 1.14 rillig .if ${:U:S,^,out-of-nothing,W} != "out-of-nothing" 176 1.14 rillig . error 177 1.14 rillig .endif 178 1.14 rillig .if ${:U:S,$,out-of-nothing,W} != "out-of-nothing" 179 1.14 rillig . error 180 1.14 rillig .endif 181 1.14 rillig .if ${:U:S,^$,out-of-nothing,W} != "out-of-nothing" 182 1.14 rillig . error 183 1.14 rillig .endif 184 1.13 rillig 185 1.13 rillig 186 1.2 rillig mod-subst: 187 1.2 rillig @echo $@: 188 1.2 rillig @echo :${:Ua b b c:S,a b,,:Q}: 189 1.2 rillig @echo :${:Ua b b c:S,a b,,1:Q}: 190 1.2 rillig @echo :${:Ua b b c:S,a b,,W:Q}: 191 1.2 rillig @echo :${:Ua b b c:S,b,,g:Q}: 192 1.2 rillig @echo :${:U1 2 3 1 2 3:S,1 2,___,Wg:S,_,x,:Q}: 193 1.2 rillig @echo ${:U12345:S,,sep,g:Q} 194 1.2 rillig 195 1.2 rillig # The :S and :C modifiers accept an arbitrary character as the delimiter, 196 1.2 rillig # including characters that are otherwise used as escape characters or 197 1.2 rillig # interpreted in a special way. This can be used to confuse humans. 198 1.2 rillig mod-subst-delimiter: 199 1.2 rillig @echo $@: 200 1.2 rillig @echo ${:U1 2 3:S 2 two :Q} horizontal tabulator 201 1.2 rillig @echo ${:U1 2 3:S 2 two :Q} space 202 1.2 rillig @echo ${:U1 2 3:S!2!two!:Q} exclamation mark 203 1.5 rillig @echo ${:U1 2 3:S"2"two":Q} quotation mark 204 1.2 rillig # In shell command lines, the hash does not need to be escaped. 205 1.2 rillig # It needs to be escaped in variable assignment lines though. 206 1.5 rillig @echo ${:U1 2 3:S#2#two#:Q} number sign 207 1.5 rillig @echo ${:U1 2 3:S$2$two$:Q} dollar sign 208 1.5 rillig @echo ${:U1 2 3:S%2%two%:Q} percent sign 209 1.5 rillig @echo ${:U1 2 3:S&2&two&:Q} ampersand 210 1.2 rillig @echo ${:U1 2 3:S'2'two':Q} apostrophe 211 1.5 rillig @echo ${:U1 2 3:S(2(two(:Q} left parenthesis 212 1.5 rillig @echo ${:U1 2 3:S)2)two):Q} right parenthesis 213 1.5 rillig @echo ${:U1 2 3:S*2*two*:Q} asterisk 214 1.5 rillig @echo ${:U1 2 3:S+2+two+:Q} plus sign 215 1.5 rillig @echo ${:U1 2 3:S,2,two,:Q} comma 216 1.5 rillig @echo ${:U1 2 3:S-2-two-:Q} hyphen-minus 217 1.5 rillig @echo ${:U1 2 3:S.2.two.:Q} full stop 218 1.5 rillig @echo ${:U1 2 3:S/2/two/:Q} solidus 219 1.2 rillig @echo ${:U1 2 3:S121two1:Q} digit 220 1.2 rillig @echo ${:U1 2 3:S:2:two::Q} colon 221 1.5 rillig @echo ${:U1 2 3:S;2;two;:Q} semicolon 222 1.5 rillig @echo ${:U1 2 3:S<2<two<:Q} less-than sign 223 1.5 rillig @echo ${:U1 2 3:S=2=two=:Q} equals sign 224 1.5 rillig @echo ${:U1 2 3:S>2>two>:Q} greater-than sign 225 1.2 rillig @echo ${:U1 2 3:S?2?two?:Q} question mark 226 1.5 rillig @echo ${:U1 2 3:S@2@two@:Q} commercial at 227 1.5 rillig @echo ${:U1 2 3:SA2AtwoA:Q} capital letter 228 1.5 rillig @echo ${:U1 2 3:S[2[two[:Q} left square bracket 229 1.5 rillig @echo ${:U1 2 3:S\2\two\:Q} reverse solidus 230 1.5 rillig @echo ${:U1 2 3:S]2]two]:Q} right square bracket 231 1.5 rillig @echo ${:U1 2 3:S^2^two^:Q} circumflex accent 232 1.5 rillig @echo ${:U1 2 3:S_2_two_:Q} low line 233 1.5 rillig @echo ${:U1 2 3:S`2`two`:Q} grave accent 234 1.5 rillig @echo ${:U1 2 3:Sa2atwoa:Q} small letter 235 1.5 rillig @echo ${:U1 2 3:S{2{two{:Q} left curly bracket 236 1.2 rillig @echo ${:U1 2 3:S|2|two|:Q} vertical line 237 1.5 rillig @echo ${:U1 2 3:S}2}two}:Q} right curly bracket 238 1.2 rillig @echo ${:U1 2 3:S~2~two~:Q} tilde 239 1.2 rillig 240 1.2 rillig # The :S and :C modifiers can be chained without a separating ':'. 241 1.2 rillig # This is not documented in the manual page. 242 1.2 rillig # It works because ApplyModifier_Subst scans for the known modifiers g1W 243 1.2 rillig # and then just returns to ApplyModifiers. There, the colon is optionally 244 1.2 rillig # skipped (see the *st.next == ':' at the end of the loop). 245 1.2 rillig # 246 1.2 rillig # Most other modifiers cannot be chained since their parsers skip until 247 1.2 rillig # the next ':' or '}' or ')'. 248 1.2 rillig mod-subst-chain: 249 1.2 rillig @echo $@: 250 1.2 rillig @echo ${:Ua b c:S,a,A,S,b,B,}. 251 1.2 rillig # There is no 'i' modifier for the :S or :C modifiers. 252 1.2 rillig # The error message is "make: Unknown modifier 'i'", which is 253 1.2 rillig # kind of correct, although it is mixing the terms for variable 254 1.2 rillig # modifiers with the matching modifiers. 255 1.17 rillig # expect: make: Unknown modifier ":i" 256 1.2 rillig @echo ${:Uvalue:S,a,x,i}. 257 1.2 rillig 258 1.6 rillig # No matter how many dollar signs there are, they all get merged 259 1.2 rillig # into a single dollar by the :S modifier. 260 1.2 rillig # 261 1.2 rillig # As of 2020-08-09, this is because ParseModifierPart sees a '$' and 262 1.2 rillig # calls Var_Parse to expand the variable. In all other places, the "$$" 263 1.2 rillig # is handled outside of Var_Parse. Var_Parse therefore considers "$$" 264 1.2 rillig # one of the "really stupid names", skips the first dollar, and parsing 265 1.2 rillig # continues with the next character. This repeats for the other dollar 266 1.2 rillig # signs, except the one before the delimiter. That one is handled by 267 1.2 rillig # the code that optionally interprets the '$' as the end-anchor in the 268 1.2 rillig # first part of the :S modifier. That code doesn't call Var_Parse but 269 1.2 rillig # simply copies the dollar to the result. 270 1.2 rillig mod-subst-dollar: 271 1.2 rillig @echo $@:${:U1:S,^,$,:Q}: 272 1.2 rillig @echo $@:${:U2:S,^,$$,:Q}: 273 1.2 rillig @echo $@:${:U3:S,^,$$$,:Q}: 274 1.2 rillig @echo $@:${:U4:S,^,$$$$,:Q}: 275 1.2 rillig @echo $@:${:U5:S,^,$$$$$,:Q}: 276 1.2 rillig @echo $@:${:U6:S,^,$$$$$$,:Q}: 277 1.2 rillig @echo $@:${:U7:S,^,$$$$$$$,:Q}: 278 1.2 rillig @echo $@:${:U8:S,^,$$$$$$$$,:Q}: 279 1.2 rillig @echo $@:${:U40:S,^,$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$,:Q}: 280 1.2 rillig # This generates no dollar at all: 281 1.2 rillig @echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}: 282 1.6 rillig # Here is an alternative way to generate dollar signs. 283 1.2 rillig # It's unexpectedly complicated though. 284 1.2 rillig @echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}: 285 1.2 rillig # In modifiers, dollars are escaped using the backslash, not using another 286 1.2 rillig # dollar sign. Therefore, creating a dollar sign is pretty simple: 287 1.2 rillig @echo $@:${:Ugood3:S,^,\$\$\$,:Q} 288