1*49caa483SSimon J. Gerraty# $NetBSD: varmod-edge.mk,v 1.7 2020/04/27 14:07:22 christos Exp $ 2*49caa483SSimon J. Gerraty# 3*49caa483SSimon J. Gerraty# Tests for edge cases in variable modifiers. 4*49caa483SSimon J. Gerraty# 5*49caa483SSimon J. Gerraty# These tests demonstrate the current implementation in small examples. 6*49caa483SSimon J. Gerraty# They may contain surprising behavior. 7*49caa483SSimon J. Gerraty# 8*49caa483SSimon J. Gerraty# Each test consists of: 9*49caa483SSimon J. Gerraty# - INP, the input to the test 10*49caa483SSimon J. Gerraty# - MOD, the expression for testing the modifier 11*49caa483SSimon J. Gerraty# - EXP, the expected output 12*49caa483SSimon J. Gerraty 13*49caa483SSimon J. GerratyTESTS+= M-paren 14*49caa483SSimon J. GerratyINP.M-paren= (parentheses) {braces} (opening closing) () 15*49caa483SSimon J. GerratyMOD.M-paren= ${INP.M-paren:M(*)} 16*49caa483SSimon J. GerratyEXP.M-paren= (parentheses) () 17*49caa483SSimon J. Gerraty 18*49caa483SSimon J. Gerraty# The first closing brace matches the opening parenthesis. 19*49caa483SSimon J. Gerraty# The second closing brace actually ends the variable expression. 20*49caa483SSimon J. Gerraty# 21*49caa483SSimon J. Gerraty# XXX: This is unexpected but rarely occurs in practice. 22*49caa483SSimon J. GerratyTESTS+= M-mixed 23*49caa483SSimon J. GerratyINP.M-mixed= (paren-brace} ( 24*49caa483SSimon J. GerratyMOD.M-mixed= ${INP.M-mixed:M(*}} 25*49caa483SSimon J. GerratyEXP.M-mixed= (paren-brace} 26*49caa483SSimon J. Gerraty 27*49caa483SSimon J. Gerraty# After the :M modifier has parsed the pattern, only the closing brace 28*49caa483SSimon J. Gerraty# and the colon are unescaped. The other characters are left as-is. 29*49caa483SSimon J. Gerraty# To actually see this effect, the backslashes in the :M modifier need 30*49caa483SSimon J. Gerraty# to be doubled since single backslashes would simply be unescaped by 31*49caa483SSimon J. Gerraty# Str_Match. 32*49caa483SSimon J. Gerraty# 33*49caa483SSimon J. Gerraty# XXX: This is unexpected. The opening brace should also be unescaped. 34*49caa483SSimon J. GerratyTESTS+= M-unescape 35*49caa483SSimon J. GerratyINP.M-unescape= ({}): \(\{\}\)\: \(\{}\): 36*49caa483SSimon J. GerratyMOD.M-unescape= ${INP.M-unescape:M\\(\\{\\}\\)\\:} 37*49caa483SSimon J. GerratyEXP.M-unescape= \(\{}\): 38*49caa483SSimon J. Gerraty 39*49caa483SSimon J. Gerraty# When the :M and :N modifiers are parsed, the pattern finishes as soon 40*49caa483SSimon J. Gerraty# as open_parens + open_braces == closing_parens + closing_braces. This 41*49caa483SSimon J. Gerraty# means that ( and } form a matching pair. 42*49caa483SSimon J. Gerraty# 43*49caa483SSimon J. Gerraty# Nested variable expressions are not parsed as such. Instead, only the 44*49caa483SSimon J. Gerraty# parentheses and braces are counted. This leads to a parse error since 45*49caa483SSimon J. Gerraty# the nested expression is not "${:U*)}" but only "${:U*)", which is 46*49caa483SSimon J. Gerraty# missing the closing brace. The expression is evaluated anyway. 47*49caa483SSimon J. Gerraty# The final brace in the output comes from the end of M.nest-mix. 48*49caa483SSimon J. Gerraty# 49*49caa483SSimon J. Gerraty# XXX: This is unexpected but rarely occurs in practice. 50*49caa483SSimon J. GerratyTESTS+= M-nest-mix 51*49caa483SSimon J. GerratyINP.M-nest-mix= (parentheses) 52*49caa483SSimon J. GerratyMOD.M-nest-mix= ${INP.M-nest-mix:M${:U*)}} 53*49caa483SSimon J. GerratyEXP.M-nest-mix= (parentheses)} 54*49caa483SSimon J. Gerraty# make: Unclosed variable specification (expecting '}') for "" (value "*)") modifier U 55*49caa483SSimon J. Gerraty 56*49caa483SSimon J. Gerraty# In contrast to parentheses and braces, the brackets are not counted 57*49caa483SSimon J. Gerraty# when the :M modifier is parsed since Makefile variables only take the 58*49caa483SSimon J. Gerraty# ${VAR} or $(VAR) forms, but not $[VAR]. 59*49caa483SSimon J. Gerraty# 60*49caa483SSimon J. Gerraty# The final ] in the pattern is needed to close the character class. 61*49caa483SSimon J. GerratyTESTS+= M-nest-brk 62*49caa483SSimon J. GerratyINP.M-nest-brk= [ [[ [[[ 63*49caa483SSimon J. GerratyMOD.M-nest-brk= ${INP.M-nest-brk:M${:U[[[[[]}} 64*49caa483SSimon J. GerratyEXP.M-nest-brk= [ 65*49caa483SSimon J. Gerraty 66*49caa483SSimon J. Gerraty# The pattern in the nested variable has an unclosed character class. 67*49caa483SSimon J. Gerraty# No error is reported though, and the pattern is closed implicitly. 68*49caa483SSimon J. Gerraty# 69*49caa483SSimon J. Gerraty# XXX: It is unexpected that no error is reported. 70*49caa483SSimon J. Gerraty# See str.c, function Str_Match. 71*49caa483SSimon J. Gerraty# 72*49caa483SSimon J. Gerraty# Before 2019-12-02, this test case triggered an out-of-bounds read 73*49caa483SSimon J. Gerraty# in Str_Match. 74*49caa483SSimon J. GerratyTESTS+= M-pat-err 75*49caa483SSimon J. GerratyINP.M-pat-err= [ [[ [[[ 76*49caa483SSimon J. GerratyMOD.M-pat-err= ${INP.M-pat-err:M${:U[[}} 77*49caa483SSimon J. GerratyEXP.M-pat-err= [ 78*49caa483SSimon J. Gerraty 79*49caa483SSimon J. Gerraty# The first backslash does not escape the second backslash. 80*49caa483SSimon J. Gerraty# Therefore, the second backslash escapes the parenthesis. 81*49caa483SSimon J. Gerraty# This means that the pattern ends there. 82*49caa483SSimon J. Gerraty# The final } in the output comes from the end of MOD.M-bsbs. 83*49caa483SSimon J. Gerraty# 84*49caa483SSimon J. Gerraty# If the first backslash were to escape the second backslash, the first 85*49caa483SSimon J. Gerraty# closing brace would match the opening parenthesis (see M-mixed), and 86*49caa483SSimon J. Gerraty# the second closing brace would be needed to close the variable. 87*49caa483SSimon J. Gerraty# After that, the remaining backslash would escape the parenthesis in 88*49caa483SSimon J. Gerraty# the pattern, therefore (} would match. 89*49caa483SSimon J. GerratyTESTS+= M-bsbs 90*49caa483SSimon J. GerratyINP.M-bsbs= (} \( \(} 91*49caa483SSimon J. GerratyMOD.M-bsbs= ${INP.M-bsbs:M\\(}} 92*49caa483SSimon J. GerratyEXP.M-bsbs= \(} 93*49caa483SSimon J. Gerraty#EXP.M-bsbs= (} # If the first backslash were to escape ... 94*49caa483SSimon J. Gerraty 95*49caa483SSimon J. Gerraty# The backslash in \( does not escape the parenthesis, therefore it 96*49caa483SSimon J. Gerraty# counts for the nesting level and matches with the first closing brace. 97*49caa483SSimon J. Gerraty# The second closing brace closes the variable, and the third is copied 98*49caa483SSimon J. Gerraty# literally. 99*49caa483SSimon J. Gerraty# 100*49caa483SSimon J. Gerraty# The second :M in the pattern is nested between ( and }, therefore it 101*49caa483SSimon J. Gerraty# does not start a new modifier. 102*49caa483SSimon J. GerratyTESTS+= M-bs1-par 103*49caa483SSimon J. GerratyINP.M-bs1-par= ( (:M (:M} \( \(:M \(:M} 104*49caa483SSimon J. GerratyMOD.M-bs1-par= ${INP.M-bs1-par:M\(:M*}}} 105*49caa483SSimon J. GerratyEXP.M-bs1-par= (:M}} 106*49caa483SSimon J. Gerraty 107*49caa483SSimon J. Gerraty# The double backslash is passed verbatim to the pattern matcher. 108*49caa483SSimon J. Gerraty# The Str_Match pattern is \\(:M*}, and there the backslash is unescaped. 109*49caa483SSimon J. Gerraty# Again, the ( takes place in the nesting level, and there is no way to 110*49caa483SSimon J. Gerraty# prevent this, no matter how many backslashes are used. 111*49caa483SSimon J. GerratyTESTS+= M-bs2-par 112*49caa483SSimon J. GerratyINP.M-bs2-par= ( (:M (:M} \( \(:M \(:M} 113*49caa483SSimon J. GerratyMOD.M-bs2-par= ${INP.M-bs2-par:M\\(:M*}}} 114*49caa483SSimon J. GerratyEXP.M-bs2-par= \(:M}} 115*49caa483SSimon J. Gerraty 116*49caa483SSimon J. Gerraty# Str_Match uses a recursive algorithm for matching the * patterns. 117*49caa483SSimon J. Gerraty# Make sure that it survives patterns with 128 asterisks. 118*49caa483SSimon J. Gerraty# That should be enough for all practical purposes. 119*49caa483SSimon J. Gerraty# To produce a stack overflow, just add more :Qs below. 120*49caa483SSimon J. GerratyTESTS+= M-128 121*49caa483SSimon J. GerratyINP.M-128= ${:U\\:Q:Q:Q:Q:Q:Q:Q:S,\\,x,g} 122*49caa483SSimon J. GerratyPAT.M-128= ${:U\\:Q:Q:Q:Q:Q:Q:Q:S,\\,*,g} 123*49caa483SSimon J. GerratyMOD.M-128= ${INP.M-128:M${PAT.M-128}} 124*49caa483SSimon J. GerratyEXP.M-128= ${INP.M-128} 125*49caa483SSimon J. Gerraty 126*49caa483SSimon J. Gerraty# This is the normal SysV substitution. Nothing surprising here. 127*49caa483SSimon J. GerratyTESTS+= eq-ext 128*49caa483SSimon J. GerratyINP.eq-ext= file.c file.cc 129*49caa483SSimon J. GerratyMOD.eq-ext= ${INP.eq-ext:%.c=%.o} 130*49caa483SSimon J. GerratyEXP.eq-ext= file.o file.cc 131*49caa483SSimon J. Gerraty 132*49caa483SSimon J. Gerraty# The SysV := modifier is greedy and consumes all the modifier text 133*49caa483SSimon J. Gerraty# up until the closing brace or parenthesis. The :Q may look like a 134*49caa483SSimon J. Gerraty# modifier, but it really isn't, that's why it appears in the output. 135*49caa483SSimon J. GerratyTESTS+= eq-q 136*49caa483SSimon J. GerratyINP.eq-q= file.c file.cc 137*49caa483SSimon J. GerratyMOD.eq-q= ${INP.eq-q:%.c=%.o:Q} 138*49caa483SSimon J. GerratyEXP.eq-q= file.o:Q file.cc 139*49caa483SSimon J. Gerraty 140*49caa483SSimon J. Gerraty# The = in the := modifier can be escaped. 141*49caa483SSimon J. GerratyTESTS+= eq-bs 142*49caa483SSimon J. GerratyINP.eq-bs= file.c file.c=%.o 143*49caa483SSimon J. GerratyMOD.eq-bs= ${INP.eq-bs:%.c\=%.o=%.ext} 144*49caa483SSimon J. GerratyEXP.eq-bs= file.c file.ext 145*49caa483SSimon J. Gerraty 146*49caa483SSimon J. Gerraty# Having only an escaped = results in a parse error. 147*49caa483SSimon J. Gerraty# The call to "pattern.lhs = VarGetPattern" fails. 148*49caa483SSimon J. GerratyTESTS+= eq-esc 149*49caa483SSimon J. GerratyINP.eq-esc= file.c file... 150*49caa483SSimon J. GerratyMOD.eq-esc= ${INP.eq-esc:a\=b} 151*49caa483SSimon J. GerratyEXP.eq-esc= # empty 152*49caa483SSimon J. Gerraty# make: Unclosed substitution for INP.eq-esc (= missing) 153*49caa483SSimon J. Gerraty 154*49caa483SSimon J. Gerratyall: 155*49caa483SSimon J. Gerraty.for test in ${TESTS} 156*49caa483SSimon J. Gerraty. if ${MOD.${test}} == ${EXP.${test}} 157*49caa483SSimon J. Gerraty @printf 'ok %s\n' ${test:Q}'' 158*49caa483SSimon J. Gerraty. else 159*49caa483SSimon J. Gerraty @printf 'error in %s: expected %s, got %s\n' \ 160*49caa483SSimon J. Gerraty ${test:Q}'' ${EXP.${test}:Q}'' ${MOD.${test}:Q}'' 161*49caa483SSimon J. Gerraty. endif 162*49caa483SSimon J. Gerraty.endfor 163