########################################################################
#                                                                      #
#               This software is part of the ast package               #
#          Copyright (c) 1982-2011 AT&T Intellectual Property          #
#          Copyright (c) 2020-2022 Contributors to ksh 93u+m           #
#                      and is licensed under the                       #
#                 Eclipse Public License, Version 2.0                  #
#                                                                      #
#                A copy of the License is available at                 #
#      https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html      #
#         (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d)         #
#                                                                      #
#                  David Korn <dgk@research.att.com>                   #
#                  Martijn Dekker <martijn@inlv.org>                   #
#          atheik <14833674+atheik@users.noreply.github.com>           #
#            Johnothan King <johnothanking@protonmail.com>             #
#                                                                      #
########################################################################

. "${SHTESTS_COMMON:-${0%/*}/_common}"

set -o noglob
if	[[ 'hi there' != "hi there" ]]
then	err_exit "single quotes not the same as double quotes"
fi
x='hi there'
if	[[ $x != 'hi there' ]]
then	err_exit "$x not the same as 'hi there'"
fi
if	[[ $x != "hi there" ]]
then	err_exit "$x not the same as \"hi there \""
fi
if	[[ \a\b\c\*\|\"\ \\ != 'abc*|" \' ]]
then	err_exit " \\ differs from '' "
fi
if	[[ "ab\'\"\$(" != 'ab\'\''"$(' ]]
then	err_exit " \"\" differs from '' "
fi
if	[[ $(print -r - 'abc*|" \') !=  'abc*|" \' ]]
then	err_exit "\$(print -r - '') differs from ''"
fi
if	[[ $(print -r - "abc*|\" \\") !=  'abc*|" \' ]]
then	err_exit "\$(print -r - '') differs from ''"
fi
if	[[ "$(print -r - 'abc*|" \')" !=  'abc*|" \' ]]
then	err_exit "\"\$(print -r - '')\" differs from ''"
fi
if	[[ "$(print -r - "abc*|\" \\")" !=  'abc*|" \' ]]
then	err_exit "\"\$(print -r - "")\" differs from ''"
fi
if	[[ $(print -r - "$(print -r - 'abc*|" \')") !=  'abc*|" \' ]]
then	err_exit "nested \$(print -r - '') differs from ''"
fi
if	[[ "$(print -r - $(print -r - 'abc*|" \'))" !=  'abc*|" \' ]]
then	err_exit "\"nested \$(print -r - '')\" differs from ''"
fi
if	[[ $(print -r - "$(print -r - 'abc*|" \')") !=  'abc*|" \' ]]
then	err_exit "nested \"\$(print -r - '')\" differs from ''"
fi
unset x
if	[[ ${x-$(print -r - "abc*|\" \\")} !=  'abc*|" \' ]]
then	err_exit "\${x-\$(print -r - '')} differs from ''"
fi
if	[[ ${x-$(print -r - "a}c*|\" \\")} !=  'a}c*|" \' ]]
then	err_exit "\${x-\$(print -r - '}')} differs from ''"
fi
x=$((echo foo)|(cat))
if	[[ $x != foo  ]]
then	err_exit "((cmd)|(cmd)) failed"
fi
x=$(print -r -- "\"$HOME\"")
if	[[ $x != '"'$HOME'"' ]]
then	err_exit "nested double quotes failed"
fi
unset z
: ${z="a{b}c"}
if	[[ $z != 'a{b}c' ]]
then	err_exit '${z="a{b}c"} not correct'
fi
unset z
: "${z="a{b}c"}"
if	[[ $z != 'a{b}c' ]]
then	err_exit '"${z="a{b}c"}" not correct'
fi
if	[[ $(print -r -- "a\*b") !=  'a\*b' ]]
then	err_exit '$(print -r -- "a\*b") differs from a\*b'
fi
unset x
if	[[ $(print -r -- "a\*b$x") !=  'a\*b' ]]
then	err_exit '$(print -r -- "a\*b$x") differs from a\*b'
fi
x=hello
set -- ${x+foo bar bam}
if	(( $# !=3 ))
then	err_exit '${x+foo bar bam} does not yield three arguments'
fi
set -- ${x+foo "bar bam"}
if	(( $# !=2 ))
then	err_exit '${x+foo "bar bam"} does not yield two arguments'
fi
set -- ${x+foo 'bar bam'}
if	(( $# !=2 ))
then	err_exit '${x+foo '\''bar bam'\''} does not yield two arguments'
fi
set -- ${x+foo $x bam}
if	(( $# !=3 ))
then	err_exit '${x+foo $x bam} does not yield three arguments'
fi
set -- ${x+foo "$x" bam}
if	(( $# !=3 ))
then	err_exit '${x+foo "$x" bam} does not yield three arguments'
fi
set -- ${x+"foo $x bam"}
if	(( $# !=1 ))
then	err_exit '${x+"foo $x bam"} does not yield one argument'
fi
set -- "${x+foo $x bam}"
if	(( $# !=1 ))
then	err_exit '"${x+foo $x bam}" does not yield one argument'
fi
set -- ${x+foo "$x "bam}
if	(( $# !=2 ))
then	err_exit '${x+foo "$x "bam} does not yield two arguments'
fi
x="ab$'cd"
if	[[ $x != 'ab$'"'cd" ]]
then	err_exit '$'"' inside double quotes not working"
fi
x=`print 'ab$'`
if	[[ $x != 'ab$' ]]
then	err_exit '$'"' inside `` quotes not working"
fi
unset a
x=$(print -r -- "'\
\
")
if	[[ $x != "'" ]]
then	err_exit 'line continuation in double strings not working'
fi
x=$(print -r -- "'\
$a\
")
if	[[ $x != "'" ]]
then	err_exit 'line continuation in expanded double strings not working'
fi
x='\*'
if	[[ $(print -r -- $x) != '\*' ]]
then	err_exit 'x="\\*";$x != \*'
fi
if	[[ $(print -r -- "\}" ) != '\}' ]]
then	err_exit '(print -r -- "\}"' not working
fi
if	[[ $(print -r -- "\{" ) != '\{' ]]
then	err_exit 'print -r -- "\{"' not working
fi
# The following caused a syntax error on earlier versions
foo=foo x=-
if	[[  `eval print \\${foo$x}` != foo* ]]
then	err_exit '`eval print \\${foo$x}`' not working
fi
if	[[  "`eval print \\${foo$x}`" != foo* ]]
then	err_exit '"`eval print \\${foo$x}`"' not working
fi
if	( [[ $() != '' ]] )
then	err_exit '$() not working'
fi
x=a:b:c
set -- $( IFS=:; print $x)
if	(( $# != 3))
then	err_exit 'IFS not working correctly with command substitution'
fi
$SHELL -n 2> /dev/null << \! || err_exit '$(...) bug with ( in comment'
y=$(
	# ( this line is a bug fix
	print hi
)
!
x=
for j in  glob noglob
do	for i in 'a\*b' 'a\ b' 'a\bc' 'a\*b' 'a\"b'
	do	eval [[ '$('print -r -- \'$i\'\$x')' != "'$i'" ]]  && err_exit "quoting of $i\$x with $j enabled failed"
		eval [[ '$('print -r -- \'$i\'\${x%*}')' != "'$i'" ]]  && err_exit "quoting of $i\${x%*} with $j enabled failed"
		if	[[ $j == noglob ]]
		then	eval [[ '$('print -r -- \'$i\'\${x:-*}')' != "'$i''*'" ]]  && err_exit "quoting of $i\${x:-*} with $j enabled failed"
		fi
	done
	set -f
done
foo=foo
[[ "$" == '$' ]] || err_exit '"$" != $'
[[ "${foo}$" == 'foo$' ]] || err_exit 'foo=foo;"${foo}$" != foo$'
[[ "${foo}${foo}$" == 'foofoo$' ]] || err_exit 'foo=foo;"${foo}${foo}$" != foofoo$'
foo='$ '
[[ "$foo" == ~(Elr)(\\\$|#)\  ]] || err_exit $'\'$ \' not matching RE \\\\\\$|#\''
[[ "$foo" == ~(Elr)('\$'|#)\  ]] || err_exit $'\'$ \' not matching RE \'\\$\'|#\''
foo='# '
[[ "$foo" == ~(Elr)(\\\$|#)\  ]] || err_exit $'\'# \' not matching RE \\'\$|#\''
[[ "$foo" == ~(Elr)('\$'|#)\  ]] || err_exit $'\'# \' not matching RE \'\\$\'|#\''
[[ '\$' == '\$'* ]] ||   err_exit $'\'\\$\' not matching \'\\$\'*'
[[ a+a == ~(E)a\+a ]] || err_exit '~(E)a\+a not matching a+a'
[[ a+a =~ a\+a ]] || err_exit 'RE a\+a not matching a+a'

exp='ac'
got=$'a\0b'c
[[ $got == "$exp" ]] || err_exit "\$'a\\0b'c expansion failed -- expected '$exp', got '$got'"

# ======
# generating shell-quoted strings using printf %q (same algorithm used for xtrace and output of 'set', 'trap', ...)

[[ $(printf '%q\n') == '' ]] || err_exit 'printf "%q" with missing arguments yields non-empty result'

# the following fails on 2012-08-01 in UTF-8 locales
expect="'shell-quoted string'"
actual=$(
	print -nr $'\303\274' | read -n1 foo  # interrupt processing of 2-byte UTF-8 char after reading 1 byte
	printf '%q\n' "shell-quoted string"
)
LC_CTYPE=POSIX true	    # on buggy ksh, a locale re-init via temp assignment restores correct shellquoting
[[ $actual == "$expect" ]] || err_exit 'shell-quoting corrupted after interrupted processing of UTF-8 char' \
				"(expected $expect; got $actual)"

# shell-quoting UTF-8 characters: check for unnecessary encoding
case ${LC_ALL:-${LC_CTYPE:-${LANG:-}}} in
( *[Uu][Tt][Ff]8* | *[Uu][Tt][Ff]-8* )
	# must wrap literal UTF-8 characters in 'eval' to avoid syntax error in ja_JP.SJIS
	eval 'expect=$'\''$\'\''عندما يريد العالم أن \\u[202a]يتكلّم \\u[202c] ، فهو يتحدّث بلغة يونيكود.\'\'''\'
	eval 'actual=$(printf %q '\''عندما يريد العالم أن ‪يتكلّم ‬ ، فهو يتحدّث بلغة يونيكود.'\'')'
	[[ $actual == "$expect" ]] || err_exit 'shell-quoting: Arabic UTF-8 characters' \
				"(expected $expect; got $actual)"
	eval 'expect="'\''正常終了 正常終了'\''"'
	eval 'actual=$(printf %q '\''正常終了 正常終了'\'')'
	[[ $actual == "$expect" ]] || err_exit 'shell-quoting: Japanese UTF-8 characters' \
				"(expected $expect; got $actual)"
	eval 'expect="'\''aeu aéu'\''"'
	eval 'actual=$(printf %q '\''aeu aéu'\'')'
	[[ $actual == "$expect" ]] || err_exit 'shell-quoting: Latin UTF-8 characters' \
				"(expected $expect; got $actual)"
	expect=$'$\'\\x86\\u[86]\\xf0\\x96v\\xa7\\xb5\''
	actual=$(printf %q $'\x86\u86\xF0\x96\x76\xA7\xB5')
	[[ $actual == "$expect" ]] || err_exit 'shell-quoting: invalid UTF-8 characters not encoded with \xNN' \
				"(expected $expect; got $actual)"
	;;
esac

# check that hex bytes are protected with square braces if needed
expect=$'$\'1\\x[11]1\''
actual=$(printf %q $'1\x[11]1')
[[ $actual == "$expect" ]] || err_exit 'shell-quoting: hex bytes not protected from subsequent hex-like chars' \
				"(expected $expect; got $actual)"

# ======
# https://github.com/ksh93/ksh/issues/290
var=dummy
exp='{}'
got=$(set +x; eval 'echo ${var:+'\''{}'\''}' 2>&1)
[[ $got == "$exp" ]] || err_exit "Single quotes misparsed in expansion operator string (1)" \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
unset var
exp='}'
got=$(set +x; eval 'echo ${var:-'\''}'\''}' 2>&1)
[[ $got == "$exp" ]] || err_exit "Single quotes misparsed in expansion operator string (2)" \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
exp='x'
got=$(var=x; set +x; eval 'echo ${var:-'\''{}'\''}' 2>&1)
[[ $got == "$exp" ]] || err_exit "Single quotes misparsed in expansion operator string (3)" \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
exp=''
got=$(var=; set +x; eval 'echo ${var:+'\''{}'\''}' 2>&1)
[[ $got == "$exp" ]] || err_exit "Single quotes misparsed in expansion operator string (4)" \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
exp='{}'
got=$(unset var; set +x; eval 'echo ${var-'\''{}'\''}' 2>&1)
[[ $got == "$exp" ]] || err_exit "Single quotes misparsed in expansion operator string (5)" \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
exp=''
got=$(unset var; set +x; eval 'echo ${var+'\''{}'\''}' 2>&1)
[[ $got == "$exp" ]] || err_exit "Single quotes misparsed in expansion operator string (6)" \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
# https://github.com/ksh93/ksh/commit/5ed4c71
unset var
exp='text between expansions'
got="${var:+'}text between expansions${var:+'}"
[[ $got == "$exp" ]] || err_exit "Single quotes misparsed in expansion operator string (7)" \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
var=foo
exp=\''text between expansions'\'
got="${var:+'}text between expansions${var:+'}"
[[ $got == "$exp" ]] || err_exit "Single quotes misparsed in expansion operator string (8)" \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"

# ======
# https://github.com/ksh93/ksh/issues/352
got=$(eval 'foo="`: "^Exec(\[[^]=]*])?="`"' 2>&1) || err_exit "Backtick command substitutions can't nest double quotes" \
	"(got $(printf %q "$got"))"

# ======
# https://github.com/ksh93/ksh/issues/367
exp='one twothree'
got=$(
echo one two\
three
)
[[ $got == "$exp" ]] || err_exit "Line continuation broken within standard command substitution" \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
got=${
echo one two\
three
}
[[ $got == "$exp" ]] || err_exit "Line continuation broken within shared-state command substitution" \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
# backticks did not have this bug but let's test them anyway
got=`
echo one two\
three
`
[[ $got == "$exp" ]] || err_exit "Line continuation broken within backtick command substitution" \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"

# ======
# BUG_IFSGLOBS: https://github.com/ksh93/ksh/issues/489
# "$*" does pattern matching if the first char of $IFS is a wildcard

IFS=*			# output field separator for "$*"
set -- F ''		# "$*" is now "F*"
exp=BUGFREE
got=${exp%"$*"}		# the quoted "*" in "F*" should not act as a wildcard
[[ $got == "$exp" ]] || err_exit 'BUG_IGSGLOBS reproducer 1' \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
got=${exp%$*}		# the unquoted * in F* should act as a wildcard
exp=BUG
[[ $got == "$exp" ]] || err_exit 'BUG_IGSGLOBS reproducer 1b' \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
case BUGFREE in
BUG"$*")	err_exit 'BUG_IFSGLOBS reproducer 2' ;;
BUGFREE)	;;
*)		err_exit 'BUG_IFSGLOBS reproducer 2 fails badly' ;;
esac
case BUGFAIL in
BUG$*)		;;
BUGFAIL)	err_exit 'BUG_IFSGLOBS reproducer 2b' ;;
*)		err_exit 'BUG_IFSGLOBS reproducer 2b fails badly' ;;
esac

IFS=?
exp=abcd
set a c
got=${exp#"$*"}
[[ $got == "$exp" ]] || err_exit 'BUG_IFSGLOBS reproducer 3' \
	"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
case abc in
"$*")	err_exit 'BUG_IFSGLOBS reproducer 4' ;;
esac
[[ abc = "$*" ]] && err_exit 'BUG_IFSGLOBS reproducer 5'

# https://unix.stackexchange.com/questions/411001/using-case-and-arrays-together-in-bash/411006#411006
IFS='|'
arr=(opt1 opt2 opt3)
case opt2 in
@("${arr[*]}"))
	err_exit 'BUG_IFSGLOBS reproducer 6' ;;
esac
[[ opt2 == @("${arr[*]}") ]] && err_exit 'BUG_IFSGLOBS reproducer 7'
unset arr

IFS=$' \t\n'

# ======
exit $((Errors<125?Errors:125))
