######################################################################## # # # This software is part of the ast package # # Copyright (c) 1982-2012 AT&T Intellectual Property # # Copyright (c) 2012 Roland Mainz # # Copyright (c) 2020-2022 Contributors to ksh 93u+m # # and is licensed under the # # Eclipse Public License, Version 1.0 # # by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.eclipse.org/org/documents/epl-v10.html # # (with md5 checksum b35adb5213ca9657e911e9befb180842) # # # # Information and Software Systems Research # # AT&T Research # # Florham Park NJ # # # # David Korn # # Roland Mainz # # # ######################################################################## # # This test module tests the .sh.match pattern matching facility # . "${SHTESTS_COMMON:-${0%/*}/_common}" # ===== # Start with basic character class matching tests backported from ksh2020. This # is primarily to verify that the underlying AST regex code is working as # expected before moving on to more complex tests. [[ 1 =~ [[:digit:]] ]] || err_exit 'pattern [[:digit:]] broken' [[ x =~ [[:digit:]] ]] && err_exit 'pattern [[:digit:]] broken' [[ 5 =~ [[:alpha:]] ]] && err_exit 'pattern [[:alpha:]] broken' [[ z =~ [[:alpha:]] ]] || err_exit 'pattern [[:alpha:]] broken' [[ 3 =~ [[:alnum:]] ]] || err_exit 'pattern [[:alnum:]] broken' [[ y =~ [[:alnum:]] ]] || err_exit 'pattern [[:alnum:]] broken' [[ / =~ [[:alnum:]] ]] && err_exit 'pattern [[:alnum:]] broken' [[ 3 =~ [[:lower:]] ]] && err_exit 'pattern [[:lower:]] broken' [[ y =~ [[:lower:]] ]] || err_exit 'pattern [[:lower:]] broken' [[ B =~ [[:lower:]] ]] && err_exit 'pattern [[:lower:]] broken' [[ 3 =~ [[:upper:]] ]] && err_exit 'pattern [[:upper:]] broken' [[ y =~ [[:upper:]] ]] && err_exit 'pattern [[:upper:]] broken' [[ B =~ [[:upper:]] ]] || err_exit 'pattern [[:upper:]] broken' [[ 7 =~ [[:word:]] ]] || err_exit 'pattern [[:word:]] broken' [[ x =~ [[:word:]] ]] || err_exit 'pattern [[:word:]] broken' [[ _ =~ [[:word:]] ]] || err_exit 'pattern [[:word:]] broken' [[ + =~ [[:word:]] ]] && err_exit 'pattern [[:word:]] broken' [[ . =~ [[:space:]] ]] && err_exit 'pattern [[:space:]] broken' [[ X =~ [[:space:]] ]] && err_exit 'pattern [[:space:]] broken' [[ ' ' =~ [[:space:]] ]] || err_exit 'pattern [[:space:]] broken' [[ $'\t' =~ [[:space:]] ]] || err_exit 'pattern [[:space:]] broken' [[ $'\v' =~ [[:space:]] ]] || err_exit 'pattern [[:space:]] broken' [[ $'\f' =~ [[:space:]] ]] || err_exit 'pattern [[:space:]] broken' [[ $'\n' =~ [[:space:]] ]] || err_exit 'pattern [[:space:]] broken' [[ . =~ [[:blank:]] ]] && err_exit 'pattern [[:blank:]] broken' [[ X =~ [[:blank:]] ]] && err_exit 'pattern [[:blank:]] broken' [[ ' ' =~ [[:blank:]] ]] || err_exit 'pattern [[:blank:]] broken' [[ $'\t' =~ [[:blank:]] ]] || err_exit 'pattern [[:blank:]] broken' [[ $'\v' =~ [[:blank:]] ]] && err_exit 'pattern [[:blank:]] broken' [[ ' ' =~ [[:space:]] ]] || err_exit 'pattern [[:space:]] broken' [[ $'\t' =~ [[:space:]] ]] || err_exit 'pattern [[:space:]] broken' [[ $'\v' =~ [[:space:]] ]] || err_exit 'pattern [[:space:]] broken' [[ $'\f' =~ [[:space:]] ]] || err_exit 'pattern [[:space:]] broken' [[ $'\n' =~ [[:space:]] ]] || err_exit 'pattern [[:space:]] broken' [[ . =~ [[:blank:]] ]] && err_exit 'pattern [[:blank:]] broken' [[ X =~ [[:blank:]] ]] && err_exit 'pattern [[:blank:]] broken' [[ ' ' =~ [[:blank:]] ]] || err_exit 'pattern [[:blank:]] broken' [[ $'\t' =~ [[:blank:]] ]] || err_exit 'pattern [[:blank:]] broken' [[ $'\v' =~ [[:blank:]] ]] && err_exit 'pattern [[:blank:]] broken' [[ $'\f' =~ [[:blank:]] ]] && err_exit 'pattern [[:blank:]] broken' [[ $'\n' =~ [[:blank:]] ]] && err_exit 'pattern [[:blank:]] broken' [[ Z =~ [[:print:]] ]] || err_exit 'pattern [[:print:]] broken' [[ ' ' =~ [[:print:]] ]] || err_exit 'pattern [[:print:]] broken' [[ $'\cg' =~ [[:print:]] ]] && err_exit 'pattern [[:print:]] broken' [[ Z =~ [[:cntrl:]] ]] && err_exit 'pattern [[:cntrl:]] broken' [[ ' ' =~ [[:cntrl:]] ]] && err_exit 'pattern [[:cntrl:]] broken' [[ $'\cg' =~ [[:cntrl:]] ]] || err_exit 'pattern [[:cntrl:]] broken' [[ \$ =~ [[:graph:]] ]] || err_exit 'pattern [[:graph:]] broken' [[ ' ' =~ [[:graph:]] ]] && err_exit 'pattern [[:graph:]] broken' [[ \$ =~ [[:punct:]] ]] || err_exit 'pattern [[:punct:]] broken' [[ / =~ [[:punct:]] ]] || err_exit 'pattern [[:punct:]] broken' [[ ' ' =~ [[:punct:]] ]] && err_exit 'pattern [[:punct:]] broken' [[ x =~ [[:punct:]] ]] && err_exit 'pattern [[:punct:]] broken' [[ ' ' =~ [[:xdigit:]] ]] && err_exit 'pattern [[:xdigit:]] broken' [[ x =~ [[:xdigit:]] ]] && err_exit 'pattern [[:xdigit:]] broken' [[ 0 =~ [[:xdigit:]] ]] || err_exit 'pattern [[:xdigit:]] broken' [[ 9 =~ [[:xdigit:]] ]] || err_exit 'pattern [[:xdigit:]] broken' [[ A =~ [[:xdigit:]] ]] || err_exit 'pattern [[:xdigit:]] broken' [[ a =~ [[:xdigit:]] ]] || err_exit 'pattern [[:xdigit:]] broken' [[ F =~ [[:xdigit:]] ]] || err_exit 'pattern [[:xdigit:]] broken' [[ f =~ [[:xdigit:]] ]] || err_exit 'pattern [[:xdigit:]] broken' [[ G =~ [[:xdigit:]] ]] && err_exit 'pattern [[:xdigit:]] broken' [[ g =~ [[:xdigit:]] ]] && err_exit 'pattern [[:xdigit:]] broken' [[ 3 =~ \w ]] || err_exit 'pattern \w broken' [[ y =~ \w ]] || err_exit 'pattern \w broken' [[ / =~ \w ]] && err_exit 'pattern \w broken' [[ 3 =~ \W ]] && err_exit 'pattern \w broken' [[ y =~ \W ]] && err_exit 'pattern \w broken' [[ / =~ \W ]] || err_exit 'pattern \w broken' [[ . =~ \s ]] && err_exit 'pattern \s broken' [[ X =~ \s ]] && err_exit 'pattern \s broken' [[ ' ' =~ \s ]] || err_exit 'pattern \s broken' [[ $'\t' =~ \s ]] || err_exit 'pattern \s broken' [[ $'\v' =~ \s ]] || err_exit 'pattern \s broken' [[ $'\f' =~ \s ]] || err_exit 'pattern \s broken' [[ $'\n' =~ \s ]] || err_exit 'pattern \s broken' [[ x =~ \d ]] && err_exit 'pattern \d broken' [[ 9 =~ \d ]] || err_exit 'pattern \d broken' [[ x =~ \D ]] || err_exit 'pattern \D broken' [[ 9 =~ \D ]] && err_exit 'pattern \D broken' [[ 7 =~ \b ]] || err_exit 'pattern \b broken' [[ x =~ \b ]] || err_exit 'pattern \b broken' [[ _ =~ \b ]] || err_exit 'pattern \b broken' [[ + =~ \b ]] || err_exit 'pattern \b broken' [[ 'x y ' =~ .\b.\b ]] || err_exit 'pattern \b broken' [[ ' xy ' =~ .\b.\b ]] && err_exit 'pattern \b broken' [[ 7 =~ \B ]] && err_exit 'pattern \B broken' [[ x =~ \B ]] && err_exit 'pattern \B broken' [[ _ =~ \B ]] && err_exit 'pattern \B broken' [[ + =~ \B ]] || err_exit 'pattern \B broken' # ====== # Tests backported from ksh93v- function test_xmlfragment1 { typeset -r testscript='test1_script.sh' cat >"${testscript}" <<-TEST1SCRIPT # memory safeguards to prevent out-of-control memory consumption { ulimit -M \$(( 1024 * 1024 )) ulimit -v \$(( 1024 * 1024 )) ulimit -d \$(( 1024 * 1024 )) } 2>/dev/null # input text xmltext="\$( < "\$1" )" print -f "%d characters to process...\\n" "\${#xmltext}" # # parse the XML data # typeset dummy function parse_xmltext { typeset xmltext="\$2" nameref ar="\$1" # fixme: # - We want to enforce standard conformance - does ~(Exp) or ~(Ex-p) do that ? dummy="\${xmltext//~(Ex-p)(?: ()+?| # xml comments (<[:_[:alnum:]-]+ (?: # attributes [[:space:]]+ (?: # four different types of name=value syntax (?:[:_[:alnum:]-]+=[^\\"\\'[:space:]]+?)| #x='foo=bar huz=123' (?:[:_[:alnum:]-]+=\\"[^\\"]*?\\")| #x='foo="ba=r o" huz=123' (?:[:_[:alnum:]-]+=\\'[^\\']*?\\')| #x="foox huz=123" (?:[:_[:alnum:]-]+) #x="foox huz=123" ) )* [[:space:]]* \\/? # start tags which are end tags, too (like ) >)+?| # xml start tags (<\\/[:_[:alnum:]-]+>)+?| # xml end tags ([^<]+) # xml text )/D}" # copy ".sh.match" to array "ar" integer i j for i in "\${!.sh.match[@]}" ; do for j in "\${!.sh.match[i][@]}" ; do [[ -v .sh.match[i][j] ]] && ar[i][j]="\${.sh.match[i][j]}" done done return 0 } function rebuild_xml_and_verify { nameref ar="\$1" typeset xtext="\$2" # xml text # # rebuild the original text from "ar" (copy of ".sh.match") # and compare it to the content of "xtext" # tmpfile=rebuild_xml_and_verify.\$\$ { # rebuild the original text, based on our matches nameref nodes_all=ar[0] # contains all matches nameref nodes_comments=ar[1] # contains only XML comment matches nameref nodes_start_tags=ar[2] # contains only XML start tag matches nameref nodes_end_tags=ar[3] # contains only XML end tag matches nameref nodes_text=ar[4] # contains only XML text matches integer i for (( i = 0 ; i < \${#nodes_all[@]} ; i++ )) ; do [[ -v nodes_comments[i] ]] && printf '%s' "\${nodes_comments[i]}" [[ -v nodes_start_tags[i] ]] && printf '%s' "\${nodes_start_tags[i]}" [[ -v nodes_end_tags[i] ]] && printf '%s' "\${nodes_end_tags[i]}" [[ -v nodes_text[i] ]] && printf '%s' "\${nodes_text[i]}" done printf '\\n' } >"\${tmpfile}" diff -u <( printf '%s\\n' "\${xtext}") "\${tmpfile}" if cmp <( printf '%s\\n' "\${xtext}") "\${tmpfile}" ; then printf "#input and output OK (%d characters).\\n" "\$(wc -m <"\${tmpfile}")" else printf "#difference between input and output found.\\n" fi rm -f "\${tmpfile}" return 0 } # main set -o nounset typeset -a xar parse_xmltext xar "\$xmltext" rebuild_xml_and_verify xar "\$xmltext" TEST1SCRIPT cat >'testfile1.xml' <<-EOF &dhtitle; &dhpackage; &dhrelease; &dhdate; XXXX YYYYYYYYYYYY Wrote this example manpage for the "SunOS Man Page Howto", available at or .
mailmail@YYYYYYYYYYYY.xxx
&dhfirstname; &dhsurname; Rewrote and extended the example manpage in DocBook XML for the Zebras distribution.
&dhemail;
1995 1996 1997 1998 1999 2000 2001 2002 2003 XXXX YYYYYYYYYYYY 2006 &dhusername; The Howto containing this example, was offered under the following conditions: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
&dhucpackage; &dhsection; &dhpackage; frobnicate the bar library &dhpackage; this this that file(s) &dhpackage; DESCRIPTION &dhpackage; frobnicates the bar library by tweaking internal symbol tables. By default it parses all baz segments and rearranges them in reverse order by time for the xyzzy1 linker to find them. The symdef entry is then compressed using the WBG (Whiz-Bang-Gizmo) algorithm. All files are processed in the order specified. OPTIONS Do not write busy to stdout while processing. Use the alternate system wide config-file instead of the /etc/foo.conf. This overrides any FOOCONF environment variable. In addition to the baz segments, also parse the blurfl3 headers. Recursive mode. Operates as fast as lightning at the expense of a megabyte of virtual memory. FILES /etc/foo.conf The system-wide configuration file. See foo.conf5 for further details. \${HOME}/.foo.conf The per-user configuration file. See foo.conf5 for further details. ENVIRONMENT FOOCONF The full pathname for an alternate system wide configuration file foo.conf5 (see also ). Overridden by the option. DIAGNOSTICS The following diagnostics may be issued on stderr: Bad magic number. The input file does not look like an archive file. Old style baz segments. &dhpackage; can only handle new style baz segments. COBOL object libraries are not supported in this version. The following return codes can be used in scripts: Errorcode Errortext Diagnostic 0 Program exited normally. No error. Program ran successfully. 1 Bad magic number. The input file does not look like an archive file. 2 Old style baz segments. &dhpackage; can only handle new style baz segments. COBOL object libraries are not supported in this version. BUGS The command name should have been chosen more carefully to reflect its purpose. The upstreams BTS can be found at . SEE ALSO bar 1 , foo 1 , foo.conf 5 , xyzzy 1 The programs are documented fully by The Rise and Fall of a Fooish Bar available via the Info system.
EOF # Note: Standalone '>' is valid XML text printf "%s" $'

>

a text
More [TEXT].

' >'testfile2.xml' compound -r -a tests=( ( file='testfile1.xml' expected_output=$'9764 characters to process...\n#input and output OK (9765 characters).' ) ( file='testfile2.xml' expected_output=$'201 characters to process...\n#input and output OK (202 characters).' ) ) compound out=( typeset stdout stderr ; integer res ) integer i typeset expected_output typeset testname for (( i=0 ; i < ${#tests[@]} ; i++ )) ; do nameref tst=tests[i] testname="${0}/${i}/${tst.file}" expected_output="${tst.expected_output}" out.stderr="${ { out.stdout="${ ${SHELL} -o nounset "${testscript}" "${tst.file}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }" [[ "${out.stdout}" == "${expected_output}" ]] || err_exit "${testname}: Expected stdout==${ printf '%q\n' "${expected_output}" ;}, got ${ printf '%q\n' "${out.stdout}" ; }" [[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got ${ printf '%q\n' "${out.stderr}" ; }" (( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}" done rm "${testscript}" rm 'testfile1.xml' rm 'testfile2.xml' return 0 } # test whether the [[ -v .sh.match[x][y] ]] operator works, try1 function test_testop_v1 { compound out=( typeset stdout stderr ; integer res ) integer i typeset testname typeset expected_output compound -r -a tests=( ( cmd='s="aaa bbb 333 ccc 555" ; s="${s//~(E)([[:alpha:]]+)|([[:digit:]]+)/NOP}" ; [[ -v .sh.match[2][3] ]] || print "OK"' expected_output='OK' ) ( cmd='s="aaa bbb 333 ccc 555" ; s="${s//~(E)([[:alpha:]]+)|([[:digit:]]+)/NOP}" ; integer i=2 j=3 ; [[ -v .sh.match[$i][$j] ]] || print "OK"' expected_output='OK' ) ( cmd='s="aaa bbb 333 ccc 555" ; s="${s//~(E)([[:alpha:]]+)|([[:digit:]]+)/NOP}" ; integer i=2 j=3 ; [[ -v .sh.match[i][j] ]] || print "OK"' expected_output='OK' ) ) for (( i=0 ; i < ${#tests[@]} ; i++ )) ; do nameref tst=tests[i] testname="${0}/${i}/${tst.cmd}" expected_output="${tst.expected_output}" out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -c "${tst.cmd}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }" [[ "${out.stdout}" == "${expected_output}" ]] || err_exit "${testname}: Expected stdout==${ printf '%q\n' "${expected_output}" ;}, got ${ printf '%q\n' "${out.stdout}" ; }" [[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got ${ printf '%q\n' "${out.stderr}" ; }" (( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}" done return 0 } # test whether the [[ -v .sh.match[x][y] ]] operator works, try2 function test_testop_v2 { compound out=( typeset stdout stderr ; integer res ) integer i integer j integer j typeset testname typeset cmd compound -r -a tests=( ( cmd='s="aaa bbb 333 ccc 555" ; s="${s//~(E)([[:alpha:]]+)|([[:digit:]]+)/NOP}"' integer y=6 expected_output_1d=$'[0]\n[1]\n[2]' expected_output_2d=$'[0][0]\n[0][1]\n[0][2]\n[0][3]\n[0][4]\n[1][0]\n[1][1]\n[1][3]\n[2][2]\n[2][4]' ) # FIXME: Add more hideous horror tests here ) for (( i=0 ; i < ${#tests[@]} ; i++ )) ; do nameref tst=tests[i] # # test first dimension, by plain number # cmd="${tst.cmd}" for (( j=0 ; j < tst.y ; j++ )) ; do cmd+="; $( printf "[[ -v .sh.match[%d] ]] && print '[%d]'\n" j j )" done cmd+='; true' testname="${0}/${i}/plain_number_index_1d/${cmd}" out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -c "${cmd}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }" [[ "${out.stdout}" == "${tst.expected_output_1d}" ]] || err_exit "${testname}: Expected stdout==${ printf '%q\n' "${tst.expected_output_1d}" ;}, got ${ printf '%q\n' "${out.stdout}" ; }" [[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got ${ printf '%q\n' "${out.stderr}" ; }" (( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}" # # test second dimension, by plain number # cmd="${tst.cmd}" for (( j=0 ; j < tst.y ; j++ )) ; do for (( k=0 ; k < tst.y ; k++ )) ; do cmd+="; $( printf "[[ -v .sh.match[%d][%d] ]] && print '[%d][%d]'\n" j k j k )" done done cmd+='; true' testname="${0}/${i}/plain_number_index_2d/${cmd}" out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -c "${cmd}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }" [[ "${out.stdout}" == "${tst.expected_output_2d}" ]] || err_exit "${testname}: Expected stdout==${ printf '%q\n' "${tst.expected_output_2d}" ;}, got ${ printf '%q\n' "${out.stdout}" ; }" [[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got ${ printf '%q\n' "${out.stderr}" ; }" (( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}" # # test first dimension, by variable index # cmd="${tst.cmd} ; integer i" for (( j=0 ; j < tst.y ; j++ )) ; do cmd+="; $( printf "(( i=%d )) ; [[ -v .sh.match[i] ]] && print '[%d]'\n" j j )" done cmd+='; true' testname="${0}/${i}/variable_index_1d/${cmd}" out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -c "${cmd}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }" [[ "${out.stdout}" == "${tst.expected_output_1d}" ]] || err_exit "${testname}: Expected stdout==${ printf '%q\n' "${tst.expected_output_1d}" ;}, got ${ printf '%q\n' "${out.stdout}" ; }" [[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got ${ printf '%q\n' "${out.stderr}" ; }" (( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}" # # test second dimension, by variable index # cmd="${tst.cmd} ; integer i j" for (( j=0 ; j < tst.y ; j++ )) ; do for (( k=0 ; k < tst.y ; k++ )) ; do cmd+="; $( printf "(( i=%d , j=%d )) ; [[ -v .sh.match[i][j] ]] && print '[%d][%d]'\n" j k j k )" done done cmd+='; true' testname="${0}/${i}/variable_index_2d/${cmd}" out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -c "${cmd}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }" [[ "${out.stdout}" == "${tst.expected_output_2d}" ]] || err_exit "${testname}: Expected stdout==${ printf '%q\n' "${tst.expected_output_2d}" ;}, got ${ printf '%q\n' "${out.stdout}" ; }" [[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got ${ printf '%q\n' "${out.stderr}" ; }" (( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}" done return 0 } # test whether ${#.sh.match[0][@]} returns the right number of elements function test_num_elements1 { compound out=( typeset stdout stderr ; integer res ) integer i typeset testname typeset expected_output compound -r -a tests=( ( cmd='s="a1a2a3" ; d="${s//~(E)([[:alpha:]])|([[:digit:]])/dummy}" ; printf "num=%d\n" "${#.sh.match[0][@]}"' expected_output='num=6' ) ( cmd='s="ababab" ; d="${s//~(E)([[:alpha:]])|([[:digit:]])/dummy}" ; printf "num=%d\n" "${#.sh.match[0][@]}"' expected_output='num=6' ) ( cmd='s="123456" ; d="${s//~(E)([[:alpha:]])|([[:digit:]])/dummy}" ; printf "num=%d\n" "${#.sh.match[0][@]}"' expected_output='num=6' ) ) for (( i=0 ; i < ${#tests[@]} ; i++ )) ; do nameref tst=tests[i] testname="${0}/${i}/${tst.cmd}" expected_output="${tst.expected_output}" out.stderr="${ { out.stdout="${ ${SHELL} -o nounset -c "${tst.cmd}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }" [[ "${out.stdout}" == "${expected_output}" ]] || err_exit "${testname}: Expected stdout==${ printf '%q\n' "${expected_output}" ; }, got ${ printf '%q\n' "${out.stdout}" ; }" [[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got ${ printf '%q\n' "${out.stderr}" ; }" (( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}" done return 0 } # dgk's test which checks whether typeset -m (rename variable) works for .sh.match function test_shmatch_varmove_dgk1 { typeset out # we use an array of $'...\n' here to get correct line numbers typeset -r -a script=( $'set -o nounset\n' $'x=1234\n' $'compound co\n' $': "${x//~(X)([012])|([345])/ }"\n' $'x="$(print -v .sh.match)"\n' $'typeset -m co.array=.sh.match\n' $'y="$(print -v co.array)"\n' $'[[ "$y" == "$x" ]] && print "MATCH"\n' # fixme: this currently outputs as ${co.array[2][(null)]}, which isn't correct # # added later by gisburn # $'printf "%s" "${co.array[2][1]}"' ) out="$(${SHELL} -c "${script[*]}" 2>&1 ; print -- "$?")" [[ "${out}" == $'MATCH\n0' ]] || err_exit "${0}: typeset -m of .sh.match to variable not working, expected 'MATCH', got ${ printf '%q\n' "${out}" ; }" return 0 } function test_nomatch_dgk1 { cat >'testscript1.sh' <<'EOF' integer j k compound c compound -a c.attrs attrdata=$' x=\'1\' y=\'2\' z="3" end="world"' dummy="${attrdata//~(Ex-p)(?: [[:space:]]+ ( # four different types of name=value syntax (?:([:_[:alnum:]-]+)=([^\"\'[:space:]]+?))| #x='foo=bar huz=123' (?:([:_[:alnum:]-]+)=\"([^\"]*?)\")| #x='foo="ba=r o" huz=123' (?:([:_[:alnum:]-]+)=\'([^\']*?)\')| #x="foox huz=123" (?:([:_[:alnum:]-]+)) #x="foox huz=123" ) )/D}" for (( j=0 ; j < ${#.sh.match[0][@]} ; j++ )) do if [[ -v .sh.match[2][j] && -v .sh.match[3][j] ]] then c.attrs+=( name="${.sh.match[2][j]}" value="${.sh.match[3][j]}" ) fi if [[ -v .sh.match[4][j] && -v .sh.match[5][j] ]] then c.attrs+=( name="${.sh.match[4][j]}" value="${.sh.match[5][j]}" ) fi if [[ -v .sh.match[6][j] && -v .sh.match[7][j] ]] ; then c.attrs+=( name="${.sh.match[6][j]}" value="${.sh.match[7][j]}" ) fi done print -v c EOF expect='( typeset -a attrs=( [0]=( name=x value=1 ) [1]=( name=y value=2 ) [2]=( name=z value=3 ) [3]=( name=end value=world ) ) )' compound out=( typeset stdout stderr ; integer res ) typeset testname # plain testname="${0}/plain" out.stderr="${ { out.stdout="${ ${SHELL} -o nounset 'testscript1.sh' ; (( out.res=$? )) ; }" ; } 2>&1 ; }" [[ "${out.stdout}" == "${expect}" ]] || err_exit "${testname}: Expected stdout==${ printf '%q\n' "${expect}" ; }, got ${ printf '%q\n' "${out.stdout}" ; }" [[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got ${ printf '%q\n' "${out.stderr}" ; }" (( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}" # compiled testname="${0}/compiled" out.stderr="${ { out.stdout="${ ${SHCOMP} -n 'testscript1.sh' 'testscript1.shbin' ; ${SHELL} -o nounset 'testscript1.shbin' ; (( out.res=$? )) ; }" ; } 2>&1 ; }" [[ "${out.stdout}" == "${expect}" ]] || err_exit "${testname}: Expected stdout==${ printf '%q\n' "${expect}" ; }, got ${ printf '%q\n' "${out.stdout}" ; }" [[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got ${ printf '%q\n' "${out.stderr}" ; }" (( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}" rm 'testscript1.sh' 'testscript1.shbin' return 0 } function test_sh_match_varmove2 { cat >'testscript1.sh' <&1 ; }" [[ "${out.stdout}" == "${tst.output}" ]] || err_exit "${testname}: Expected stdout==${ printf '%q\n' "${tst.output}" ; }, got ${ printf '%q\n' "${out.stdout}" ; }" [[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got ${ printf '%q\n' "${out.stderr}" ; }" (( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}" (( numtests++ )) # compiled testname="${0}/${i}/${mode}/compiled" out.stderr="${ { out.stdout="${ ${SHELL} 'testscript1.shbin' ${mode} "${tst.attrstr}" ; (( out.res=$? )) ; }" ; } 2>&1 ; }" [[ "${out.stdout}" == "${tst.output}" ]] || err_exit "${testname}: Expected stdout==${ printf '%q\n' "${tst.output}" ; }, got ${ printf '%q\n' "${out.stdout}" ; }" [[ "${out.stderr}" == '' ]] || err_exit "${testname}: Expected empty stderr, got ${ printf '%q\n' "${out.stderr}" ; }" (( out.res == 0 )) || err_exit "${testname}: Unexpected exit code ${out.res}" (( numtests++ )) done done rm 'testscript1.sh' 'testscript1.shbin' # safeguard against malfunctions in the test chain (( numtests == 40 )) || err_exit "${0}: Internal test script error, expected numtests == 40, got ${numtests}" return 0 } # run tests test_xmlfragment1 test_testop_v1 test_testop_v2 test_num_elements1 test_shmatch_varmove_dgk1 test_sh_match_varmove2 test_nomatch_dgk1 # ====== set +u x=1234 compound co : "${x//~(X)([012])|([345])/ }" x=$(print -v .sh.match) typeset -m co.array=.sh.match y=$(print -v co.array) [[ $y == "$x" ]] || 'typeset -m of .sh.match to variable not working' # ====== # https://github.com/ksh93/ksh/issues/308 exp='typeset -a .sh.match=((1 2 3 4) (1 2) ([2]=3 [3]=4) ) typeset -a .sh.match[1]=(1 2) typeset -a .sh.match[2]=([2]=3 [3]=4) 3 2 2 2 3' got=$("$SHELL" -c ' x=1234 true ${x//~(X)([012])|([345])/ } typeset -p .sh.match .sh.match[1] .sh.match[2] echo ${#.sh.match[@]} ${#.sh.match[1][@]} ${#.sh.match[2][@]} echo ${!.sh.match[2][@]}; ') [[ $exp == "$got" ]] || err_exit "listing .sh.match indexed array results doesn't work correctly" \ "(expected $(printf %q "$exp"), got $(printf %q "$got"))" # https://marc.info/?l=ast-developers&m=134604855504311&w=2 nummatches=$tmp/nummatches.sh cat > "$nummatches" << 'EOF' attrdata=$' aname=avalue ' dummy="${attrdata//~(Ex-p)(?: [[:space:]]+ ( # four different types of name=value syntax (?:([:_[:alnum:]-]+)=([^\"\'[:space:]]+?))| (?:([:_[:alnum:]-]+)=\"([^\"]*?)\")| (?:([:_[:alnum:]-]+)=\'([^\']*?)\')| (?:([:_[:alnum:]-]+)) ) )/D}" print -v .sh.match print "Nummatches=${#.sh.match[0][@]}" EOF exp=$'( ( \' aname=a\' ) ( aname\\=a ) ( aname ) ( a ) ) Nummatches=1' got=$("$SHELL" "$nummatches") [[ $exp == "$got" ]] || err_exit "Nummatches should be one" \ "(expected $(printf %q "$exp"), got $(printf %q "$got"))" # https://marc.info/?l=ast-developers&m=134490505607093 if ((SHOPT_NAMESPACE)); then type_nameref=$tmp/ksh93v_typeset_T_nameref_fails001.sh cat > "$type_nameref" << 'EOF' namespace xmlfragmentparser { typeset -T parser_t=( typeset -a data # "raw" data from .sh.match compound -a context # parsed tag data function build_context { typeset dummy typeset attrdata # data after "" "" ) xd.build_context print "$xd" return 0 } # main set -o nounset main EOF exp="( typeset -a data=( $'' $'' ) typeset -C -a context=( [0]=( typeset -a attrs=( [0]=( name=x value=1 ) [1]=( name=y value=2 ) ) tagname=foo ) [1]=( typeset -a attrs=( [0]=( name=a value=1 ) [1]=( name=b value=2 ) ) tagname=bar ) ) )" got=$("$SHELL" "$type_nameref") [[ $exp == "$got" ]] || err_exit "Compound variable \$context is not printed with 'print -v'." \ $'Diff follows:\n'"$(diff -u <(print -r -- "$exp") <(print -r -- "$got") | sed $'s/^/\t| /')" fi # ====== exit $((Errors<125?Errors:125))