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