1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
cde/src/cmd/ksh93/tests/builtins.sh
Martijn Dekker b398f33c49 Another round of accumulated minor fixes and cleanups
Only notable changes listed below.

**/Mamfile:
- Do not bother redirecting standard error for 'cmp -s' to
  /dev/null. Normally, 'cmp -s' on Linux, macOS, *BSD, or Solaris
  do not not print any message. If it does, something unusual is
  going on and I would want to see the message.
- Since we now require a POSIX shell, we can use '!'.

src/cmd/ksh93/include/defs.h,
src/cmd/ksh93/sh/init.c:
- Remove SH_TYPE_PROFILE symbol, unused after the removal of the
  SHOPT_PFSH code. (re: eabd6453)

src/cmd/ksh93/sh/io.c:
- piperead(), slowread(): Replace redundant sffileno() calls by
  the variables already containing their results. (re: 50d342e4)

src/cmd/ksh93/bltins/mkservice.c,
rc/lib/libcmd/vmstate.c:
- If these aren't compiled, define a stub function to silence the
  ranlib(1) warning that the .o file does not contain symbols.
2022-03-11 21:20:32 +01:00

1609 lines
58 KiB
Bash
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

########################################################################
# #
# This software is part of the ast package #
# Copyright (c) 1982-2012 AT&T Intellectual Property #
# 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 <dgk@research.att.com> #
# #
########################################################################
. "${SHTESTS_COMMON:-${0%/*}/_common}"
bincat=$(whence -p cat)
# ======
# These are regression tests for the getconf builtin.
if builtin getconf 2> /dev/null; then
bingetconf=$(getconf GETCONF)
bad_result=$(getconf --version 2>&1)
# The -l option should convert all variable names to lowercase.
# https://github.com/att/ast/issues/1171
got=$(getconf -lq | LC_ALL=C sed -n -e 's/=.*//' -e '/[A-Z]/p')
[[ -n $got ]] && err_exit "'getconf -l' doesn't convert all variable names to lowercase" \
"(got $(printf %q "$got"))"
# The -q option should quote all string values.
# https://github.com/att/ast/issues/1173
exp="GETCONF=\"$bingetconf\""
got=$(getconf -q | grep 'GETCONF=')
[[ $exp == "$got" ]] || err_exit "'getconf -q' fails to quote string values" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
# The -n option should only return matching names.
# https://github.com/ksh93/ksh/issues/279
exp="GETCONF=$bingetconf"
got=$(getconf -n GETCONF)
[[ $exp == "$got" ]] || err_exit "'getconf -n' doesn't match names correctly" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
fi
# ======
# Test shell builtin commands
: ${foo=bar} || err_exit ": failed"
[[ $foo == bar ]] || err_exit ": side effects failed"
set -- - foobar
[[ $# == 2 && $1 == - && $2 == foobar ]] || err_exit "set -- - foobar failed"
set -- -x foobar
[[ $# == 2 && $1 == -x && $2 == foobar ]] || err_exit "set -- -x foobar failed"
getopts :x: foo || err_exit "getopts :x: returns false"
[[ $foo == x && $OPTARG == foobar ]] || err_exit "getopts :x: failed"
OPTIND=1
getopts :r:s var -r
if [[ $var != : || $OPTARG != r ]]
then err_exit "'getopts :r:s var -r' not working"
fi
OPTIND=1
getopts :d#u OPT -d 16177
if [[ $OPT != d || $OPTARG != 16177 ]]
then err_exit "'getopts :d#u OPT=d OPTARG=16177' failed -- OPT=$OPT OPTARG=$OPTARG"
fi
OPTIND=1
while getopts 'ab' option -a -b
do [[ $OPTIND == $((OPTIND)) ]] || err_exit "OPTIND optimization bug"
done
USAGE=$'[-][S:server?Operate on the specified \asubservice\a:]:[subservice:=pmserver]
{
[p:pmserver]
[r:repserver]
[11:notifyd]
}'
set pmser p rep r notifyd -11
while (( $# > 1 ))
do OPTIND=1
getopts "$USAGE" OPT -S $1
[[ $OPT == S && $OPTARG == $2 ]] || err_exit "OPT=$OPT OPTARG=$OPTARG -- expected OPT=S OPTARG=$2"
shift 2
done
false ${foo=bar} && err_exit "false failed"
read <<!
hello world
!
[[ $REPLY == 'hello world' ]] || err_exit "read builtin failed"
print x:y | IFS=: read a b
if [[ $a != x ]]
then err_exit "IFS=: read ... not working"
fi
read <<!
hello \
world
!
[[ $REPLY == 'hello world' ]] || err_exit "read continuation failed"
read -d x <<!
hello worldxfoobar
!
[[ $REPLY == 'hello world' ]] || err_exit "read builtin failed"
read <<\!
hello \
world \
!
[[ $REPLY == 'hello world' ]] || err_exit "read continuation 2 failed"
print "one\ntwo" | { read line
print $line | "$bincat" > /dev/null
read line
}
read <<\!
\
a\
\
\
b
!
if [[ $REPLY != ab ]]
then err_exit "read multiple continuation failed"
fi
if [[ $line != two ]]
then err_exit "read from pipeline failed"
fi
line=two
read line < /dev/null
if [[ $line != "" ]]
then err_exit "read from /dev/null failed"
fi
if [[ $(print -R -) != - ]]
then err_exit "print -R not working correctly"
fi
if [[ $(print -- -) != - ]]
then err_exit "print -- not working correctly"
fi
print -f "hello%nbar\n" size > /dev/null
if (( size != 5 ))
then err_exit "%n format of printf not working"
fi
print -n -u2 2>&1-
[[ -w /dev/fd/1 ]] || err_exit "2<&1- with built-ins has side effects"
x=$0
if [[ $(eval 'print $0') != $x ]]
then err_exit '$0 not correct for eval'
fi
$SHELL -c 'read x <<< hello' 2> /dev/null || err_exit 'syntax <<< not recognized'
($SHELL -c 'read x[1] <<< hello') 2> /dev/null || err_exit 'read x[1] not working'
unset x
readonly x
set -- $(readonly)
if [[ " $@ " != *" x "* ]]
then err_exit 'unset readonly variables are not displayed'
fi
if [[ $( for i in foo bar
do print $i
continue 10
done
) != $'foo\nbar' ]]
then err_exit 'continue breaks out of loop'
fi
(continue bad 2>/dev/null && err_exit 'continue bad should return an error')
(break bad 2>/dev/null && err_exit 'break bad should return an error')
(continue 0 2>/dev/null && err_exit 'continue 0 should return an error')
(break 0 2>/dev/null && err_exit 'break 0 should return an error')
breakfun() { break;}
continuefun() { continue;}
for fun in break continue
do if [[ $( for i in foo
do ${fun}fun
print $i
done
) != foo ]]
then err_exit "$fun call in ${fun}fun breaks out of for loop"
fi
done
if [[ $(print -f "%b" "\a\n\v\b\r\f\E\03\\oo") != $'\a\n\v\b\r\f\E\03\\oo' ]]
then err_exit 'print -f "%b" not working'
fi
if [[ $(print -f "%P" "[^x].*b\$") != '*[!x]*b' ]]
then err_exit 'print -f "%P" not working'
fi
if [[ $(print -f "%(pattern)q" "[^x].*b\$") != '*[!x]*b' ]]
then err_exit 'print -f "%(pattern)q" not working'
fi
if [[ $(abc: for i in foo bar;do print $i;break abc;done) != foo ]]
then err_exit 'break labels not working'
fi
if [[ $(command -v if) != if ]]
then err_exit 'command -v not working'
fi
read -r var <<\!
!
if [[ $var != "" ]]
then err_exit "read -r of blank line not working"
fi
mkdir -p $tmp/a/b/c 2>/dev/null || err_exit "mkdir -p failed"
$SHELL -c "cd $tmp/a/b; cd c" 2>/dev/null || err_exit "initial script relative cd fails"
trap 'print TERM' TERM
exp="trap -- 'print TERM' TERM"
got=$(trap)
[[ $got == $exp ]] || err_exit "\$(trap) failed -- expected \"$exp\", got \"$got\""
exp='print TERM'
got=$(trap -p TERM)
[[ $got == $exp ]] || err_exit "\$(trap -p TERM) failed -- expected \"$exp\", got \"$got\""
[[ $($SHELL -c 'trap "print ok" SIGTERM; kill -s SIGTERM $$' 2> /dev/null) == ok ]] || err_exit 'SIGTERM not recognized'
[[ $($SHELL -c 'trap "print ok" sigterm; kill -s sigterm $$' 2> /dev/null) == ok ]] || err_exit 'SIGTERM not recognized'
[[ $($SHELL -c '( trap "" TERM);kill $$;print bad' == bad) ]] 2> /dev/null && err_exit 'trap ignored in subshell causes it to be ignored by parent'
${SHELL} -c 'kill -1 -$$' 2> /dev/null
[[ $(kill -l $?) == HUP ]] || err_exit 'kill -1 -pid not working'
${SHELL} -c 'kill -1 -$$' 2> /dev/null
[[ $(kill -l $?) == HUP ]] || err_exit 'kill -n1 -pid not working'
${SHELL} -c 'kill -s HUP -$$' 2> /dev/null
[[ $(kill -l $?) == HUP ]] || err_exit 'kill -HUP -pid not working'
n=123
typeset -A base
base[o]=8#
base[x]=16#
base[X]=16#
for i in d i o u x X
do if (( $(( ${base[$i]}$(printf "%$i" $n) )) != n ))
then err_exit "printf %$i not working"
fi
done
if [[ $( trap 'print done' EXIT) != done ]]
then err_exit 'trap on EXIT not working'
fi
if [[ $( trap 'print done' EXIT; trap - EXIT) == done ]]
then err_exit 'trap on EXIT not being cleared'
fi
if [[ $(LC_MESSAGES=C type test) != 'test is a shell builtin' ]]
then err_exit 'whence -v test not a builtin'
fi
if [[ $(builtin -d test; type test) == *builtin* ]]
then err_exit 'whence -v test after builtin -d incorrect'
fi
typeset -Z3 percent=$(printf '%o\n' "'%'")
forrmat=\\${percent}s
if [[ $(printf "$forrmat") != %s ]]
then err_exit "printf $forrmat not working"
fi
if (( $(printf 'x\0y' | wc -c) != 3 ))
then err_exit 'printf \0 not working'
fi
if [[ $(printf "%bx%s\n" 'f\to\cbar') != $'f\to' ]]
then err_exit 'printf %bx%s\n not working'
fi
alpha=abcdefghijklmnop
if [[ $(printf "%10.*s\n" 5 $alpha) != ' abcde' ]]
then err_exit 'printf %10.%s\n not working'
fi
float x2=.0000625
if [[ $(printf "%10.5E\n" x2) != 6.25000E-05 ]]
then err_exit 'printf "%10.5E" not normalizing correctly'
fi
x2=.000000001
if [[ $(printf "%g\n" x2 2>/dev/null) != 1e-09 ]]
then err_exit 'printf "%g" not working correctly'
fi
(read -s foobar <<<testing_read_s) 2> /dev/null || err_exit "'read -s var' fails"
exp=$'^[[:digit:]]+\ttesting_read_s$'
got=$(fc -l -0)
[[ $got =~ $exp ]] || err_exit "'read -s' did not write to history file" \
"(expected match of regex $(printf %q "$exp"), got $(printf %q "$got"))"
if [[ $(printf +3 2>/dev/null) != +3 ]]
then err_exit 'printf is not processing formats beginning with + correctly'
fi
if printf "%d %d\n" 123bad 78 >/dev/null 2>/dev/null
then err_exit "printf not exiting non-zero with conversion errors"
fi
if [[ $(trap --version 2> /dev/null;print done) != done ]]
then err_exit 'trap builtin terminating after --version'
fi
if [[ $(set --version 2> /dev/null;print done) != done ]]
then err_exit 'set builtin terminating after --version'
fi
unset -f foobar
function foobar
{
print 'hello world'
}
OPTIND=1
if [[ $(getopts $'[+?X\ffoobar\fX]' v --man 2>&1) != *'Xhello world'X* ]]
then err_exit '\f...\f not working in getopts usage strings'
fi
expect=$'&lt;&gt;&quot;&amp; &#39;\tabc'
actual=$(printf '%H\n' $'<>"& \'\tabc')
[[ $expect == "$actual" ]] || err_exit 'printf %H not working' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
actual=$(printf '%(html)q\n' $'<>"& \'\tabc')
[[ $expect == "$actual" ]] || err_exit 'printf %(html)q not working' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
expect='foo://ab_c%3C%3E%22%26%20%27%09abc'
actual=$(printf 'foo://ab_c%#H\n' $'<>"& \'\tabc')
[[ $expect == "$actual" ]] || err_exit 'printf %#H not working' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
actual=$(printf 'foo://ab_c%(url)q\n' $'<>"& \'\tabc')
[[ $expect == "$actual" ]] || err_exit 'printf %(url)q not working' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
case ${LC_ALL:-${LC_CTYPE:-${LANG:-}}} in
( *[Uu][Tt][Ff]8* | *[Uu][Tt][Ff]-8* )
# HTML encoding UTF-8 characters
# (UTF-8 literal characters wrapped in 'eval' to avoid syntax error on ja_JP.SJIS)
eval 'expect='\''正常終了 正常終了'\'
eval 'actual=$(printf %H '\''正常終了 正常終了'\'')'
[[ $actual == "$expect" ]] || err_exit 'printf %H: Japanese UTF-8 characters' \
"(expected $expect; got $actual)"
expect='w?h?á?t??'
actual=$(printf %H $'w\x80h\x81\uE1\x82t\x83?')
[[ $actual == "$expect" ]] || err_exit 'printf %H: invalid UTF-8 characters' \
"(expected $expect; got $actual)"
# URL/URI encoding of UTF-8 characters
expect='wh.at%3F'
actual=$(printf %#H 'wh.at?')
[[ $actual == "$expect" ]] || err_exit 'printf %H: ASCII characters' \
"(expected $expect; got $actual)"
expect='%D8%B9%D9%86%D8%AF%D9%85%D8%A7%20%D9%8A%D8%B1%D9%8A%D8%AF%20%D8%A7%D9%84%D8%B9%D8%A7%D9%84%D9%85%20%D8%A3%D9%86%20%E2%80%AA%D9%8A%D8%AA%D9%83%D9%84%D9%91%D9%85%20%E2%80%AC%20%D8%8C%20%D9%81%D9%87%D9%88%20%D9%8A%D8%AA%D8%AD%D8%AF%D9%91%D8%AB%20%D8%A8%D9%84%D8%BA%D8%A9%20%D9%8A%D9%88%D9%86%D9%8A%D9%83%D9%88%D8%AF.'
actual=$(printf %#H 'عندما يريد العالم أن ‪يتكلّم ، فهو يتحدّث بلغة يونيكود.')
[[ $actual == "$expect" ]] || err_exit 'printf %H: Arabic UTF-8 characters' \
"(expected $expect; got $actual)"
expect='%E6%AD%A3%E5%B8%B8%E7%B5%82%E4%BA%86%20%E6%AD%A3%E5%B8%B8%E7%B5%82%E4%BA%86'
eval 'actual=$(printf %#H '\''正常終了 正常終了'\'')'
[[ $actual == "$expect" ]] || err_exit 'printf %H: Japanese UTF-8 characters' \
"(expected $expect; got $actual)"
expect='%C2%AB%20l%E2%80%99ab%C3%AEme%20de%20mon%C2%A0m%C3%A9tier%E2%80%A6%20%C2%BB'
actual=$(printf %#H '« labîme de mon métier… »')
[[ $actual == "$expect" ]] || err_exit 'printf %H: Latin UTF-8 characters' \
"(expected $expect; got $actual)"
expect='%3F%C2%86%3F%3F%3F'
actual=$(printf %#H $'\x86\u86\xF0\x96\x76\xA7\xB5')
[[ $actual == "$expect" ]] || err_exit 'printf %H: invalid UTF-8 characters' \
"(expected $expect; got $actual)"
;;
esac
if [[ $(printf '%R %R %R %R\n' 'a.b' '*.c' '^' '!(*.*)') != '^a\.b$ \.c$ ^\^$ ^(.*\..*)!$' ]]
then err_exit 'printf %T not working'
fi
if [[ $(printf '%(ere)q %(ere)q %(ere)q %(ere)q\n' 'a.b' '*.c' '^' '!(*.*)') != '^a\.b$ \.c$ ^\^$ ^(.*\..*)!$' ]]
then err_exit 'printf %(ere)q not working'
fi
if [[ $(printf '%..:c\n' abc) != a:b:c ]]
then err_exit "printf '%..:c' not working"
fi
if [[ $(printf '%..*c\n' : abc) != a:b:c ]]
then err_exit "printf '%..*c' not working"
fi
if [[ $(printf '%..:s\n' abc def ) != abc:def ]]
then err_exit "printf '%..:s' not working"
fi
if [[ $(printf '%..*s\n' : abc def) != abc:def ]]
then err_exit "printf '%..*s' not working"
fi
# ======
# we won't get hit by the one second boundary twice, right?
expect= actual=
{ expect=$(LC_ALL=C date) && actual=$(LC_ALL=C printf '%T\n' now) && [[ ${actual/ GMT / UTC } == "${expect/ GMT / UTC }" ]]; } ||
{ expect=$(LC_ALL=C date) && actual=$(LC_ALL=C printf '%T\n' now) && [[ ${actual/ GMT / UTC } == "${expect/ GMT / UTC }" ]]; } ||
err_exit 'printf "%T" now' "(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
behead()
{
read line
left=$(cat)
}
print $'line1\nline2' | behead
if [[ $left != line2 ]]
then err_exit "read reading ahead on a pipe"
fi
read -n1 y <<!
abc
!
exp=a
if [[ $y != $exp ]]
then err_exit "read -n1 failed -- expected '$exp', got '$y'"
fi
print -n $'{ read -r line;print $line;}\nhello' > $tmp/script
chmod 755 $tmp/script
if [[ $($SHELL < $tmp/script) != hello ]]
then err_exit 'read of incomplete line not working correctly'
fi
set -f
set -- *
if [[ $1 != '*' ]]
then err_exit 'set -f not working'
fi
unset pid1 pid2
false &
pid1=$!
pid2=$(
wait $pid1
(( $? == 127 )) || err_exit "job known to subshell"
print $!
)
wait $pid1
(( $? == 1 )) || err_exit "wait not saving exit value"
wait $pid2
(( $? == 127 )) || err_exit "subshell job known to parent"
env=
if builtin getconf 2> /dev/null; then
v=$(getconf LIBPATH)
for v in ${v//,/ }
do v=${v#*:}
v=${v%%:*}
eval [[ \$$v ]] && env="$env $v=\"\$$v\""
done
if [[ $(foo=bar; eval foo=\$foo $env exec -c \$SHELL -c \'print \$foo\') != bar ]]
then err_exit '"name=value exec -c ..." not working'
fi
fi
$SHELL -c 'OPTIND=-1000000; getopts a opt -a' 2> /dev/null
[[ $? == 1 ]] || err_exit 'getopts with negative OPTIND not working'
getopts 'n#num' opt -n 3
[[ $OPTARG == 3 ]] || err_exit 'getopts with numerical arguments failed'
if [[ $($SHELL -c $'printf \'%2$s %1$s\n\' world hello') != 'hello world' ]]
then err_exit 'printf %2$s %1$s not working'
fi
val=$(( 'C' ))
set -- \
"'C" $val 0 \
"'C'" $val 0 \
'"C' $val 0 \
'"C"' $val 0 \
"'CX" $val 1 \
"'CX'" $val 1 \
"'C'X" $val 1 \
'"CX' $val 1 \
'"CX"' $val 1 \
'"C"X' $val 1
while (( $# >= 3 ))
do arg=$1 val=$2 code=$3
shift 3
for fmt in '%d' '%g'
do out=$(printf "$fmt" "$arg" 2>/dev/null)
err=$(set +x; printf "$fmt" "$arg" 2>&1 >/dev/null)
printf "$fmt" "$arg" >/dev/null 2>&1
ret=$?
[[ $out == $val ]] || err_exit "printf $fmt $arg failed -- expected '$val', got '$out'"
if (( $code ))
then [[ $err ]] || err_exit "printf $fmt $arg failed, error message expected"
else [[ $err ]] && err_exit "$err: printf $fmt $arg failed, error message not expected -- got '$err'"
fi
(( $ret == $code )) || err_exit "printf $fmt $arg failed -- expected exit code $code, got $ret"
done
done
((n=0))
((n++)); ARGC[$n]=1 ARGV[$n]=""
((n++)); ARGC[$n]=2 ARGV[$n]="-a"
((n++)); ARGC[$n]=4 ARGV[$n]="-a -v 2"
((n++)); ARGC[$n]=4 ARGV[$n]="-a -v 2 x"
((n++)); ARGC[$n]=4 ARGV[$n]="-a -v 2 x y"
for ((i=1; i<=n; i++))
do set -- ${ARGV[$i]}
OPTIND=0
while getopts -a tst "av:" OPT
do :
done
if [[ $OPTIND != ${ARGC[$i]} ]]
then err_exit "\$OPTIND after getopts loop incorrect -- expected ${ARGC[$i]}, got $OPTIND"
fi
done
options=ab:c
optarg=foo
set -- -a -b $optarg -c bar
while getopts $options opt
do case $opt in
a|c) [[ $OPTARG ]] && err_exit "getopts $options \$OPTARG for flag $opt failed, expected \"\", got \"$OPTARG\"" ;;
b) [[ $OPTARG == $optarg ]] || err_exit "getopts $options \$OPTARG failed -- \"$optarg\" expected, got \"$OPTARG\"" ;;
*) err_exit "getopts $options failed -- got flag $opt" ;;
esac
done
[[ $($SHELL 2> /dev/null -c 'readonly foo; getopts a: foo -a blah; echo foo') == foo ]] || err_exit 'getopts with readonly variable causes script to abort'
unset a
{ read -N3 a; read -N1 b;} <<!
abcdefg
!
exp=abc
[[ $a == $exp ]] || err_exit "read -N3 here-document failed -- expected '$exp', got '$a'"
exp=d
[[ $b == $exp ]] || err_exit "read -N1 here-document failed -- expected '$exp', got '$b'"
read -n3 a <<!
abcdefg
!
exp=abc
[[ $a == $exp ]] || err_exit "read -n3 here-document failed -- expected '$exp', got '$a'"
#(print -n a;sleep 1; print -n bcde) | { read -N3 a; read -N1 b;}
#[[ $a == $exp ]] || err_exit "read -N3 from pipe failed -- expected '$exp', got '$a'"
#exp=d
#[[ $b == $exp ]] || err_exit "read -N1 from pipe failed -- expected '$exp', got '$b'"
#(print -n a;sleep 1; print -n bcde) | read -n3 a
#exp=a
#[[ $a == $exp ]] || err_exit "read -n3 from pipe failed -- expected '$exp', got '$a'"
#rm -f $tmp/fifo
#if mkfifo $tmp/fifo 2> /dev/null
#then (print -n a; sleep 1;print -n bcde) > $tmp/fifo &
# {
# read -u5 -n3 -t2 a || err_exit 'read -n3 from fifo timedout'
# read -u5 -n1 -t2 b || err_exit 'read -n1 from fifo timedout'
# } 5< $tmp/fifo
# exp=a
# [[ $a == $exp ]] || err_exit "read -n3 from fifo failed -- expected '$exp', got '$a'"
# rm -f $tmp/fifo
# mkfifo $tmp/fifo 2> /dev/null
# (print -n a; sleep 1;print -n bcde) > $tmp/fifo &
# {
# read -u5 -N3 -t2 a || err_exit 'read -N3 from fifo timed out'
# read -u5 -N1 -t2 b || err_exit 'read -N1 from fifo timedout'
# } 5< $tmp/fifo
# exp=abc
# [[ $a == $exp ]] || err_exit "read -N3 from fifo failed -- expected '$exp', got '$a'"
# exp=d
# [[ $b == $exp ]] || err_exit "read -N1 from fifo failed -- expected '$exp', got '$b'"
#fi
#rm -f $tmp/fifo
function longline
{
integer i
for((i=0; i < $1; i++))
do print argument$i
done
}
# test that 'command' can result from expansion and can be overridden by a function
expect=all\ ok
c=command
actual=$("$c" echo all ok 2>&1)
[[ $actual == "$expect" ]] || err_exit '"command" utility from "$c" expansion not executed' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
set -- command echo all ok
actual=$("$@" 2>&1)
[[ $actual == "$expect" ]] || err_exit '"command" utility from "$@" expansion not executed' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# must use 'eval' in following test as 'command' is integrated in parser and ksh likes to parse ahead
actual=$(function command { echo all ok; }; eval 'command echo all wrong')
[[ $actual == "$expect" ]] || err_exit '"command" failed to be overridden by function' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
actual=$(function command { echo all ok; }; "$c" echo all wrong)
[[ $actual == "$expect" ]] || err_exit '"command" from "$c" expansion failed to be overridden by function' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
set -- command echo all wrong
actual=$(function command { echo all ok; }; "$@")
[[ $actual == "$expect" ]] || err_exit '"command" from "$@" expansion failed to be overridden by function' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# test command -x option
integer sum=0 n=10000
if ! ${SHELL:-ksh} -c 'print $#' count $(longline $n) > /dev/null 2>&1
then for i in $(command command -x ${SHELL:-ksh} -c 'print $#;[[ $1 != argument0 ]]' count $(longline $n) 2> /dev/null)
do ((sum += $i))
done
(( sum == n )) || err_exit "command -x processed only $sum arguments"
command -p command -x ${SHELL:-ksh} -c 'print $#;[[ $1 == argument0 ]]' count $(longline $n) > /dev/null 2>&1
[[ $? != 1 ]] && err_exit 'incorrect exit status for command -x'
fi
# test command -x option with extra arguments
integer sum=0 n=10000
if ! ${SHELL:-ksh} -c 'print $#' count $(longline $n) > /dev/null 2>&1
then for i in $(command command -x ${SHELL:-ksh} -c 'print $#;[[ $1 != argument0 ]]' count $(longline $n) one two three) #2> /dev/null)
do ((sum += $i))
done
(( sum > n )) || err_exit "command -x processed only $sum arguments"
(( (sum-n)%3==0 )) || err_exit "command -x processed only $sum arguments"
(( sum == n+3)) && err_exit "command -x processed only $sum arguments"
command -p command -x ${SHELL:-ksh} -c 'print $#;[[ $1 == argument0 ]]' count $(longline $n) > /dev/null 2>&1
[[ $? != 1 ]] && err_exit 'incorrect exit status for command -x'
fi
# test for debug trap
[[ $(typeset -i i=0
trap 'print $i' DEBUG
while (( i <2))
do (( i++))
done) == $'0\n0\n1\n1\n2' ]] || err_exit "DEBUG trap not working"
if builtin getconf 2> /dev/null; then
getconf UNIVERSE - ucb
[[ $($SHELL -c 'echo -3') == -3 ]] || err_exit "echo -3 not working in ucb universe"
fi
typeset -F3 start_x=SECONDS total_t delay=0.02
typeset reps=50 leeway=5
sleep $(( 2 * leeway * reps * delay )) |
for (( i=0 ; i < reps ; i++ ))
do read -N1 -t $delay
done
(( total_t = SECONDS - start_x ))
if (( total_t > leeway * reps * delay ))
then err_exit "read -t in pipe taking $total_t secs - $(( reps * delay )) minimum - too long"
elif (( total_t < reps * delay ))
then err_exit "read -t in pipe taking $total_t secs - $(( reps * delay )) minimum - too fast"
fi
$SHELL -c 'sleep $(printf "%a" .95)' 2> /dev/null || err_exit "sleep doesn't except %a format constants"
$SHELL -c 'test \( ! -e \)' 2> /dev/null ; [[ $? == 1 ]] || err_exit 'test \( ! -e \) not working'
[[ $(ulimit) == "$(ulimit -fS)" ]] || err_exit 'ulimit is not the same as ulimit -fS'
tmpfile=$tmp/file.2
print $'\nprint -r -- "${.sh.file} ${LINENO} ${.sh.lineno}"' > $tmpfile
[[ $( . "$tmpfile") == "$tmpfile 2 1" ]] || err_exit 'dot command not working'
print -r -- "'xxx" > $tmpfile
[[ $($SHELL -c ". $tmpfile"$'\n print ok' 2> /dev/null) == ok ]] || err_exit 'syntax error in dot command affects next command'
float sec=$SECONDS del=4
exec 3>&2 2>/dev/null
$SHELL -c "( sleep 1; kill -ALRM \$\$ ) & sleep $del" 2> /dev/null
exitval=$?
(( sec = SECONDS - sec ))
exec 2>&3-
(( exitval )) && err_exit "sleep doesn't exit 0 with ALRM interrupt"
(( sec > (del - 1) )) || err_exit "ALRM signal causes sleep to terminate prematurely -- expected 3 sec, got $sec"
typeset -r z=3
y=5
for i in 123 z %x a.b.c
do ( unset $i) 2>/dev/null && err_exit "unset $i should fail"
done
a=()
for i in y y y[8] t[abc] y.d a.b a
do unset $i || print -u2 "err_exit unset $i should not fail"
done
[[ $($SHELL -c 'y=3; unset 123 y;print $?$y') == 1 ]] 2> /dev/null || err_exit 'y is not getting unset with unset 123 y'
[[ $($SHELL -c 'trap foo TERM; (trap;(trap) )') == 'trap -- foo TERM' ]] || err_exit 'traps not getting reset when subshell is last process'
n=$(printf "%b" 'a\0b\0c' | wc -c)
(( n == 5 )) || err_exit '\0 not working with %b format with printf'
t=$(ulimit -t)
[[ $($SHELL -c 'ulimit -v 15000 2>/dev/null; ulimit -t') == "$t" ]] || err_exit 'ulimit -v changes ulimit -t'
$SHELL 2> /dev/null -c 'cd ""' && err_exit 'cd "" not producing an error'
[[ $($SHELL 2> /dev/null -c 'cd "";print hi') != hi ]] && err_exit 'cd "" should not terminate script'
if builtin cat 2> /dev/null; then
out=$tmp/seq.out
for ((i=1; i<=11; i++)); do print "$i"; done >$out
cmp -s <(print -- "$($bincat<( $bincat $out ) )") <(print -- "$(cat <( cat $out ) )") || err_exit "builtin cat differs from $bincat"
fi
[[ $($SHELL -c '{ printf %R "["; print ok;}' 2> /dev/null) == ok ]] || err_exit $'\'printf %R "["\' causes shell to abort'
v=$( $SHELL -c $'
trap \'print "usr1"\' USR1
trap exit USR2
sleep 1 && {
kill -USR1 $$ && sleep 1
kill -0 $$ 2>/dev/null && kill -USR2 $$
} &
sleep 2 | read
echo done
' ) 2> /dev/null
[[ $v == $'usr1\ndone' ]] || err_exit 'read not terminating when receiving USR1 signal'
mkdir $tmp/tmpdir1
cd $tmp/tmpdir1
pwd=$PWD
cd ../tmpdir1
[[ $PWD == "$pwd" ]] || err_exit 'cd ../tmpdir1 causes directory to change'
cd "$pwd"
mv $tmp/tmpdir1 $tmp/tmpdir2
cd .. 2> /dev/null || err_exit 'cannot change directory to .. after current directory has been renamed'
[[ $PWD == "$tmp" ]] || err_exit 'after "cd $tmp/tmpdir1; cd .." directory is not $tmp'
cd "$tmp"
mkdir $tmp/tmpdir2/foo
pwd=$PWD
cd $tmp/tmpdir2/foo
mv $tmp/tmpdir2 $tmp/tmpdir1
cd ../.. 2> /dev/null || err_exit 'cannot change directory to ../.. after current directory has been renamed'
[[ $PWD == "$tmp" ]] || err_exit 'after "cd $tmp/tmpdir2; cd ../.." directory is not $tmp'
cd "$tmp"
rm -rf tmpdir1
cd /etc
cd ..
[[ $(pwd) == / ]] || err_exit 'cd /etc;cd ..;pwd is not /'
cd /etc
cd ../..
[[ $(pwd) == / ]] || err_exit 'cd /etc;cd ../..;pwd is not /'
cd /etc
cd .././..
[[ $(pwd) == / ]] || err_exit 'cd /etc;cd .././..;pwd is not /'
cd /usr/bin
cd ../..
[[ $(pwd) == / ]] || err_exit 'cd /usr/bin;cd ../..;pwd is not /'
cd /usr/bin
cd ..
[[ $(pwd) == /usr ]] || err_exit 'cd /usr/bin;cd ..;pwd is not /usr'
cd "$tmp"
if mkdir $tmp/t1
then (
cd $tmp/t1
> real_t1
(
cd ..
mv t1 t2
mkdir t1
)
[[ -f real_t1 ]]
) || err_exit 'real_t1 not found after parent directory renamed in subshell'
fi
cd "$tmp"
if kill -L > /dev/null 2>&1
then [[ $(kill -l HUP) == "$(kill -L HUP)" ]] || err_exit 'kill -l and kill -L are not the same when given a signal name'
[[ $(kill -l 9) == "$(kill -L 9)" ]] || err_exit 'kill -l and kill -L are not the same when given a signal number'
[[ $(kill -L) == *'9) KILL'* ]] || err_exit 'kill -L output does not contain 9) KILL'
fi
export ENV=/./dev/null
v=$($SHELL 2> /dev/null +o rc -ic $'getopts a:bc: opt --man\nprint $?')
[[ $v == 2* ]] || err_exit 'getopts --man does not exit 2 for interactive shells'
read baz <<< 'foo\\\\bar'
[[ $baz == 'foo\\bar' ]] || err_exit 'read of foo\\\\bar not getting foo\\bar'
# ======
# Check that I/O errors are detected <https://github.com/att/ast/issues/1093>
actual=$(
set +x
{
(
trap "" PIPE
for ((i = SECONDS + 1; SECONDS < i; )); do
print hi || {
print $? >&2
exit
}
done
) | true
} 2>&1
)
expect='1'
if [[ $actual != "$expect" ]]
then
err_exit "I/O error not detected: expected $(printf %q "$expect"), got $(printf %q "$actual")"
fi
# ======
# 'times' builtin
expect=$'0m00.0[0-9][0-9]s 0m00.0[0-9][0-9]s\n0m00.000s 0m00.000s'
actual=$("$SHELL" -c times)
[[ $actual == $expect ]] || err_exit "times output: expected $(printf %q "$expect"), got $(printf %q "$actual")"
expect=$'*: times: too many operands'
actual=$(set +x; eval 'times Extra Args' 2>&1)
[[ $actual == $expect ]] || err_exit "times with args: expected $(printf %q "$expect"), got $(printf %q "$actual")"
# ======
# 'whence' builtin
PATH=$tmp:$PATH $SHELL <<-\EOF || err_exit "'whence' gets wrong path on init"
wc=$(whence wc)
[[ -x $wc ]]
EOF
# ======
# 'builtin -d' should not delete special builtins
(builtin -d export 2> /dev/null
PATH=/dev/null
whence -q export) || err_exit "'builtin -d' deletes special builtins"
# ======
# 'read -r -d' should not ignore '-r'
printf '\\\000' | read -r -d ''
[[ $REPLY == $'\\' ]] || err_exit "read -r -d '' ignores -r"
# ======
# BUG_CMDSPASGN: Preceding a special builtin with 'command' should disable its special properties.
# Test that assignments preceding 'command' are local.
for arg in '' -v -V -p -x
do
for cmd in '' : true ls eval 'eval :' 'eval true' 'eval ls'
do
[[ $arg == -x ]] && ! command -xv "${cmd% *}" >/dev/null && continue
unset foo
eval "foo=BUG command $arg $cmd" >/dev/null 2>&1
got=$?
case $arg,$cmd in
-v, | -V, ) exp=2 ;;
*) exp=0 ;;
esac
[[ $got == "$exp" ]] || err_exit "exit status of 'command $arg $cmd' is $got, expected $exp"
[[ -v foo ]] && err_exit "preceding assignment survives 'command $arg $cmd'"
done
done
# Regression that occurred after fixing the bug above: the += operator didn't work correctly.
# https://www.mail-archive.com/ast-users@lists.research.att.com/msg00369.html
unset foo
integer foo=1
exp=4
got=$(foo+=3 command eval 'echo $foo')
[[ $exp == $got ]] || err_exit "Test 1: += assignment for environment variables doesn't work with 'command special_builtin'" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
foo+=3 command eval 'test $foo'
exp=1
(( foo == exp )) || err_exit "environment isn't restored after 'command special_builtin'" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
got=$(foo+=3 eval 'echo $foo')
exp=4
[[ $exp == $got ]] || err_exit "+= assignment for environment variables doesn't work with builtins" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
unset foo
exp=barbaz
got=$(foo=bar; foo+=baz command eval 'echo $foo')
[[ $exp == $got ]] || err_exit "Test 2: += assignment for environment variables doesn't work with 'command special_builtin'" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
unset y
exp='outside f, 1, 2, 3, outside f'
got=$(
f() {
if [ -n "${_called_f+_}" ]; then
for y; do
printf '%s, ' "$y"
done
else
_called_f= y= command eval '{ typeset +x y; } 2>/dev/null; f "$@"'
fi
}
y='outside f'
printf "$y, "
f 1 2 3
echo "$y"
)
[[ $got == "$exp" ]] || err_exit 'assignments to "command special_built-in" leaving side effects' \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
# Attempting to modify a readonly variable with the += operator should fail
exp=2
got=$(integer -r foo=2; foo+=3 command eval 'echo $foo' 2> /dev/null)
[[ $? == 0 ]] && err_exit "+= assignment modifies readonly variables" \
"(expected $exp, got $got)"
# ======
# 'whence -f' should ignore functions
foo_bar() { true; }
actual="$(whence -f foo_bar)"
whence -f foo_bar >/dev/null && err_exit "'whence -f' doesn't ignore functions (got $(printf %q "$actual"))"
# whence -vq/type -q must be tested as well
actual="$(type -f foo_bar 2>&1)"
type -f foo_bar >/dev/null 2>&1 && err_exit "'type -f' doesn't ignore functions (got $(printf %q "$actual"))"
type -qf foo_bar && err_exit "'type -qf' doesn't ignore functions"
# Test the exit status of 'whence -q'
mkdir "$tmp/fakepath"
ln -s "${ whence -p cat ;}" "$tmp/fakepath/"
ln -s "${ whence -p ls ;}" "$tmp/fakepath/"
save_PATH=$PATH
PATH=$tmp/fakepath
whence -q cat nonexist ls && err_exit "'whence -q' has the wrong exit status"
whence -q cat nonexist && err_exit "'whence -q' has the wrong exit status"
whence -q nonexist && err_exit "'whence -q' has the wrong exit status"
PATH=$save_PATH
# ======
# These are the regression tests for the whence builtin's '-t' flag
((.sh.version >= 20211227)) &&
for w in 'whence -t' 'type -t' 'whence -t -v'; do
exp=file
got=$($w $SHELL)
[[ $exp == "$got" ]] || err_exit "'$w' has the wrong output for external commands" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
got=$(builtin -d chmod; hash chmod; $w chmod)
[[ $exp == "$got" ]] || err_exit "'$w' has the wrong output for tracked aliases" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
exp=keyword
got=$($w time)
[[ $exp == "$got" ]] || err_exit "'$w' has the wrong output for keywords" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
exp=builtin
got=$($w sleep)
[[ $exp == "$got" ]] || err_exit "'$w' has the wrong output for regular builtins" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
got=$($w export)
[[ $exp == "$got" ]] || err_exit "'$w' has the wrong output for special builtins" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
builtin -d cat
if [[ $(set +x; PATH=/opt/ast/bin type cat 2>&1) != "cat is a shell builtin version of /opt/ast/bin/cat" ]]
then warning "/opt/ast/bin/cat isn't a builtin; skipping path-bound builtin tests"
else
got=$(PATH=/opt/ast/bin $w cat)
[[ $exp == "$got" ]] || err_exit "Test A: '$w' has the wrong output for path-bound builtins" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
got=$($w /opt/ast/bin/cat)
[[ $exp == "$got" ]] || err_exit "Test B: '$w' has the wrong output for path-bound builtins" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
got=$(PATH=/opt/ast/bin:$PATH; $w cat)
[[ $exp == "$got" ]] || err_exit "Test C: '$w' has the wrong output for path-bound builtins" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
got=$(builtin cat; $w cat)
[[ $exp == "$got" ]] || err_exit "Test D: '$w' has the wrong output for path-bound builtins" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
fi
exp=alias
got=$(alias foo=bar; $w foo)
[[ $exp == "$got" ]] || err_exit "'$w' has the wrong output for aliases" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
exp=function
got=$(foo() { true; }; $w foo)
[[ $exp == "$got" ]] || err_exit "'$w' has the wrong output for POSIX functions" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
got=$(function foo { true; }; $w foo)
[[ $exp == "$got" ]] || err_exit "'$w' has the wrong output for KornShell functions" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
got=$(autoload FooBar; $w FooBar)
[[ $exp == "$got" ]] || err_exit "'$w' has the wrong output for undefined autoloaded functions" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
echo 'FooBar() { true; }' > FooBar
got=$(FPATH=. autoload FooBar; $w FooBar)
[[ $exp == "$got" ]] || err_exit "'$w' has the wrong output for autoloaded POSIX functions" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
echo 'function FooBar { true; }' > FooBar
got=$(FPATH=. autoload FooBar; $w FooBar)
[[ $exp == "$got" ]] || err_exit "'$w' has the wrong output for autoloaded KornShell functions" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
# The final few tests are for '-t -a'
builtin -d cat
if [[ $(set +x; PATH=/opt/ast/bin type cat 2>&1) == "cat is a shell builtin version of /opt/ast/bin/cat" ]]
then exp="alias
function
builtin
$($w -pa cat)"
got=$(alias cat=false
autoload cat
PATH=/opt/ast/bin:$PATH $w -a cat)
[[ $exp == "$got" ]] || err_exit "'$w -a' output is incorrect (cat command)" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
fi
if [[ -n $($w -pa time) ]]
then exp="keyword
alias
$($w -pa time)"
else
exp=$'keyword\nalias'
fi
got=$(alias time=nottime
$w -a time)
[[ $exp == "$got" ]] || err_exit "'$w -a' output is incorrect (time command)" \
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
done
# ======
# 'cd ../.foo' should not exclude the '.' in '.foo'
# https://bugzilla.redhat.com/889748
expect=$tmp/.ssh
actual=$( HOME=$tmp
mkdir ~/.ssh 2>&1 &&
cd ~/.ssh 2>&1 &&
cd ../.ssh 2>&1 &&
print -r -- "$PWD" )
[[ $actual == "$expect" ]] || err_exit 'changing to a hidden directory using a path that contains the parent directory (..) fails' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
expect=$tmp/.java
actual=$( mkdir "$tmp/java" "$tmp/.java" 2>&1 &&
cd "$tmp/.java" 2>&1 &&
cd ../.java 2>&1 &&
pwd )
[[ $actual == "$expect" ]] || err_exit 'the dot (.) part of the directory name is being stripped' \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# check that we cannot cd into a regular file and get misbehaviour
: > "$tmp/regular_file"
expect=": cd: $tmp/regular_file: [Not a directory]"
actual=$(LC_ALL=C cd "$tmp/regular_file" 2>&1)
e=$?
[[ e -eq 1 && $actual == *"$expect" ]] || err_exit 'can cd into a regular file' \
"(expected status 1 and msg ending in $(printf %q "$expect"), got status $e and msg $(printf %q "$actual"))"
# https://bugzilla.redhat.com/1102627
if [[ $(id -u) == '0' ]]
then warning "running as root: skipping tests involving directory search (x) permission"
else
mkdir -m 600 "$tmp/no_x_dir"
expect=": cd: $tmp/no_x_dir: [Permission denied]"
actual=$(LC_ALL=C cd "$tmp/no_x_dir" 2>&1)
e=$?
[[ e -eq 1 && $actual == *"$expect" ]] || err_exit 'can cd into a directory without x permission bit (absolute path arg)' \
"(expected status 1 and msg ending in $(printf %q "$expect"), got status $e and msg $(printf %q "$actual"))"
expect=": cd: no_x_dir: [Permission denied]"
actual=$(cd "$tmp" 2>&1 && LC_ALL=C cd "no_x_dir" 2>&1)
e=$?
[[ e -eq 1 && $actual == *"$expect" ]] || err_exit 'can cd into a directory without x permission bit (relative path arg)' \
"(expected status 1 and msg ending in $(printf %q "$expect"), got status $e and msg $(printf %q "$actual"))"
rmdir "$tmp/no_x_dir" # on HP-UX, 'rm -rf $tmp' won't work unless we rmdir this or fix the perms
fi
# https://bugzilla.redhat.com/1133582
expect=$HOME
actual=$({ a=`cd; pwd`; } >&-; print -r -- "$a")
[[ $actual == "$expect" ]] || err_exit "'cd' broke old-form command substitution with outer stdout closed" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
actual=$({ a=$(cd; pwd); } >&-; print -r -- "$a")
[[ $actual == "$expect" ]] || err_exit "'cd' broke new-form command substitution with outer stdout closed" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# CDPATH was not ignored by 'cd ./dir': https://github.com/ksh93/ksh/issues/151
expect=': cd: ./dev: [No such file or directory]'
actual=$( (CDPATH=/ LC_ALL=C cd -P ./dev && pwd) 2>&1 )
let "(e=$?)==1" && [[ $actual == *"$expect" ]] || err_exit "CDPATH not ignored by cd ./dir" \
"(expected *$(printf %q "$expect") with status 1, got $(printf %q "$actual") with status $e)"
# ======
# 'readonly' should set the correct scope when creating variables in functions
unset foo
(
function test_func
{
readonly foo="bar"
[[ $foo = "bar" ]]
}
test_func
) || err_exit "readonly variable is not assigned a value inside functions"
# ======
# Test the output of nonstandard date formats with 'printf %T'
[[ $(printf '%(%l)T') == $(printf '%(%_I)T') ]] || err_exit 'date format %l is not the same as %_I'
[[ $(printf '%(%k)T') == $(printf '%(%_H)T') ]] || err_exit 'date format %k is not the same as %_H'
[[ $(printf '%(%f)T') == $(printf '%(%Y.%m.%d-%H:%M:%S)T') ]] || err_exit 'date format %f is not the same as %Y.%m.%d-%H:%M:%S'
[[ $(printf '%(%q)T') == $(printf '%(%Qz)T') ]] && err_exit 'date format %q is the same as %Qz'
[[ $(printf '%(%Z)T') == $(date '+%Z') ]] || err_exit "date format %Z is incorrect (expected $(date '+%Z'), got $(printf '%(%Z)T'))"
# Test manually specified blank and zero padding with 'printf %T'
(
IFS=$'\n\t' # Preserve spaces in output
for i in d e H I j J k l m M N S U V W y; do
for f in ' ' 0; do
if [[ $f == ' ' ]]; then
padding='blank'
specify='_'
else
padding='zero'
specify='0'
fi
actual="$(printf "%(%${specify}${i})T" 'January 1 6AM 2001')"
expect="${f}${actual:1}"
[[ $expect != $actual ]] && err_exit "Specifying $padding padding with format '%$i' doesn't work (expected '$expect', got '$actual')"
done
done
exit $Errors
)
Errors=$? # Ensure error count survives subshell
# ======
# Test various AST getopts usage/manual outputs
OPTIND=1
USAGE=$'
[-s8?
@(#)$Id: foo (ksh93) 2020-07-16 $
]
[+NAME?foo - bar]
[+DESC?Baz.]
[x:xylophone?Lorem.]
[y:ypsilon?Ipsum.]
[z:zeta?Sit.]
[ name=value ... ]
-y [ name ... ]
[+SEE ALSO?\bgetopts\b(1)]
'
function testusage {
getopts "$USAGE" dummy 2>&1
}
actual=$(testusage -\?)
expect='Usage: testusage [-xyz] [ name=value ... ]
Or: testusage -y [ name ... ]
Help: testusage [ --help | --man ] 2>&1'
[[ $actual == "$expect" ]] || err_exit "getopts: '-?' output" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
actual=$(testusage --\?x)
expect='Usage: testusage [ options ] [ name=value ... ]
Or: testusage -y [ name ... ]
Help: testusage [ --help | --man ] 2>&1
OPTIONS
-x, --xylophone Lorem.'
[[ $actual == "$expect" ]] || err_exit "getopts: '--?x' output" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
actual=$(testusage --help)
expect='Usage: testusage [ options ] [ name=value ... ]
Or: testusage -y [ name ... ]
Help: testusage [ --help | --man ] 2>&1
OPTIONS
-x, --xylophone Lorem.
-y, --ypsilon Ipsum.
-z, --zeta Sit.'
[[ $actual == "$expect" ]] || err_exit "getopts: '--help' output" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
actual=$(testusage --man)
expect='NAME
foo - bar
SYNOPSIS
foo [ options ] [ name=value ... ]
foo -y [ name ... ]
DESC
Baz.
OPTIONS
-x, --xylophone Lorem.
-y, --ypsilon Ipsum.
-z, --zeta Sit.
SEE ALSO
getopts(1)
IMPLEMENTATION
version foo (ksh93) 2020-07-16'
[[ $actual == "$expect" ]] || err_exit "getopts: '--man' output" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# ======
# 'sleep -s' should work in interactive shells when seconds > 30.
sleepsig="$tmp/sleepsig.sh"
cat >| "$sleepsig" << 'EOF'
sleep -s 31 &
sleep .1
kill -CONT $!
sleep .1
if kill -0 $!; then
kill -TERM $! # Don't leave a lingering background process
exit 1
else
exit 0
fi
EOF
"$SHELL" -i "$sleepsig" 2> /dev/null || err_exit "'sleep -s' doesn't work with intervals of more than 30 seconds"
# ======
# Builtins should handle unrecognized options correctly
function test_usage
{
while IFS= read -r bltin <&3
do case $bltin in
echo | test | true | false | \[ | : | expr | */expr | getconf | */getconf | uname | */uname | catclose | catgets | catopen | Dt* | _Dt* | X* | login | newgrp )
continue ;;
/*/*) expect="Usage: ${bltin##*/} "
actual=$({ PATH=${bltin%/*}; "${bltin##*/}" --this-option-does-not-exist; } 2>&1) ;;
*/*) err_exit "strange path name in 'builtin' output: $(printf %q "$bltin")"
continue ;;
autoload | compound | float | functions | integer | nameref)
bltin=typeset ;&
*) expect="Usage: $bltin "
actual=$({ "${bltin}" --this-option-does-not-exist; } 2>&1) ;;
esac
[[ $actual == *"$expect"* ]] || err_exit "$bltin should show usage info on unrecognized options" \
"(expected string containing $(printf %q "$expect"), got $(printf %q "$actual"))"
done 3< <(builtin)
}; test_usage
# ======
# The 'alarm' builtin could make 'read' crash due to IFS table corruption caused by unsafe asynchronous execution.
# https://bugzilla.redhat.com/1176670
if (builtin alarm) 2>/dev/null
then got=$( { "$SHELL" -c '
builtin alarm
alarm -r alarm_handler +.005
i=0
function alarm_handler.alarm
{
let "(++i) > 20" && exit
}
while :; do
echo cargo,odds and ends,jetsam,junk,wreckage,castoffs,sea-drift
done | while IFS="," read arg1 arg2 arg3 arg4 junk; do
:
done
'; } 2>&1)
((!(e = $?))) || err_exit 'crash with alarm and IFS' \
"(got status $e$( ((e>128)) && print -n /SIG && kill -l "$e"), $(printf %q "$got"))"
fi
# ======
# Verify that the POSIX 'test' builtin exits with status 2 when given an invalid binary operator.
for operator in '===' ']]'
do
actual=$(test foo "$operator" foo 2>&1)
actual_status=$?
actual=${actual#*: }
expect="test: $operator: unknown operator"
expect_status=2
[[ "$actual" = "$expect" ]] || err_exit "test $operator failed" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
[[ "$actual_status" = "$expect_status" ]] ||
err_exit "'test foo $operator foo' failed with the wrong exit status (expected $expect_status, got $actual_status)"
done
# ======
# Regression test for https://github.com/att/ast/issues/1402
#
# We throw away stderr because we only want the value of '$t', not the error text from running
# 'command' with an invalid flag.
exp='good'
got=$($SHELL -c 't=good; t=bad command -@; print $t' 2>/dev/null)
[[ $exp == $got ]] || err_exit "temp var assignment with 'command'" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# ======
# Regression test for https://github.com/att/ast/issues/949
if (builtin chmod) 2>/dev/null
then foo_script='#!/bin/sh
exit 0'
echo "$foo_script" > "$tmp/foo1.sh"
echo "$foo_script" > "$tmp/foo2.sh"
builtin chmod
chmod +x "$tmp/foo1.sh" "$tmp/foo2.sh"
$SHELL "$tmp/foo1.sh" || err_exit "builtin 'chmod +x' doesn't work on first script"
$SHELL "$tmp/foo2.sh" || err_exit "builtin 'chmod +x' doesn't work on second script"
fi
# ======
# In ksh93v- 2013-10-10 alpha cd doesn't fail on directories without execute permission.
# Additionally, ksh93v- added a regression test for attempting to use cd on a file.
if [[ $(id -u) == 0 ]]
then warning "running as root: skipping test if cd fails on directory without execute permission"
else mkdir "$tmp/noexecute"
chmod -x "$tmp/noexecute"
"$SHELL" -c "cd $tmp/noexecute" 2>/dev/null && err_exit "'cd' on directories without an execute bit doesn't fail"
fi
touch "$tmp/notadir"
$SHELL -c "cd $tmp/notadir" 2> /dev/null && err_exit "'cd' on a normal file doesn't fail"
# ======
# 'kill %' should fail with exit status 1
{ $SHELL -c 'kill %' ;} 2> /dev/null
got=$?
exp=1
[[ $got == $exp ]] || err_exit "'kill %' has the wrong exit status (expected '$exp'; got '$got')"
# ======
# 'cd -' should recognize the value of an overridden $OLDPWD variable
# https://github.com/ksh93/ksh/pull/249
# https://github.com/att/ast/issues/8
mkdir "$tmp/oldpwd" "$tmp/otherpwd"
exp=$tmp/oldpwd
OLDPWD=$exp
cd - > /dev/null
got=$PWD
[[ $got == "$exp" ]] || err_exit "cd - doesn't recognize overridden OLDPWD variable" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
cd "$tmp"
OLDPWD=$tmp/otherpwd
got=$(OLDPWD=$tmp/oldpwd cd -)
[[ $got == "$exp" ]] ||
err_exit "cd - doesn't recognize overridden OLDPWD variable if it is overridden in new scope" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
function fn
{
typeset OLDPWD=/tmp
cd -
}
exp='/tmp'
got=$(OLDPWD=/bin fn)
[[ $got == "$exp" ]] ||
err_exit "cd - doesn't recognize overridden OLDPWD variable if it is overridden in function scope" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
function fn
{
typeset PWD=bug
cd /tmp
echo "$PWD"
}
exp='/tmp'
got=$(fn)
[[ $got == "$exp" ]] ||
err_exit "PWD isn't set after cd if already set in function scope" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
# $PWD should be set correctly after cd
exp="$PWD
$PWD"
got=$(echo $PWD; PWD=/tmp cd /dev; echo $PWD)
[[ $got == "$exp" ]] ||
err_exit "PWD is incorrect after cd" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
# Test for $OLDPWD and/or $PWD leaking out of subshell
exp='/tmp /dev'
got=$(
PWD=/dev
OLDPWD=/tmp
(
cd /usr; cd /bin
cd - > /dev/null
)
echo $OLDPWD $PWD
)
[[ $got == "$exp" ]] ||
err_exit "OLDPWD and/or PWD leak out of subshell" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
# $OLDPWD and $PWD should survive after being set in a subshare
exp='/usr /bin'
got=$(
PWD=/dev
OLDPWD=/tmp
foo=${
cd /usr; cd /bin
}
echo $OLDPWD $PWD
)
[[ $got == "$exp" ]] ||
err_exit "OLDPWD and/or PWD fail to survive subshare" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
# ======
# Test for bugs related to 'uname -d'
# https://github.com/att/ast/pull/1187
if (builtin uname) 2>/dev/null
then builtin uname
exp=$(uname -o)
# Test for a possible crash (to avoid crashing the script, fork the subshell)
(
ulimit -t unlimited 2> /dev/null
uname -d > /dev/null
) || err_exit "'uname -d' crashes"
# 'uname -d' shouldn't change the output of 'uname -o'
got=$(ulimit -t unlimited 2> /dev/null; uname -d > /dev/null; uname -o)
[[ $exp == $got ]] || err_exit "'uname -d' changes the output of 'uname -o'" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
fi
# ======
# https://github.com/ksh93/ksh/issues/138
builtin -d cat
if [[ $'\n'${ builtin; }$'\n' == *$'\n/opt/ast/bin/cat\n'* ]]
then exp=' version cat (*) ????-??-??'
got=$(/opt/ast/bin/cat --version 2>&1)
[[ $got == $exp ]] || err_exit "path-bound builtin not executable by literal canonical path" \
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
got=$(PATH=/opt/ast/bin:$PATH; "${ whence -p cat; }" --version 2>&1)
[[ $got == $exp ]] || err_exit "path-bound builtin not executable by canonical path resulting from expansion" \
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
got=$(PATH=/opt/ast/bin:$PATH; "$SHELL" -o restricted -c 'cat --version' 2>&1)
[[ $got == $exp ]] || err_exit "restricted shells do not recognize path-bound builtins" \
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
got=$(set +x; PATH=/opt/ast/bin cat --version 2>&1)
[[ $got == $exp ]] || err_exit "path-bound builtin not found on PATH in preceding assignment" \
"(expected match of $(printf %q "$exp"), got $(printf %q "$got"))"
else warning 'skipping path-bound builtin tests: builtin /opt/ast/bin/cat not found'
fi
# ======
# part of https://github.com/ksh93/ksh/issues/153
mkdir "$tmp/deleted"
cd "$tmp/deleted"
tmp=$tmp "$SHELL" -c 'cd /; rmdir "$tmp/deleted"'
exp=$PWD
got=$("$SHELL" -c 'cd /; echo "$OLDPWD"' 2>&1)
[[ $got == "$exp" ]] || err_exit "OLDPWD not correct after cd'ing from a nonexistent PWD" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
cd "$tmp"
# ======
# BUG_CMDSPEXIT
exp='ok1ok2ok3ok4ok5ok6ok7ok8ok9ok10ok11ok12end'
got=$( readonly v=foo
exec 2>/dev/null
# All the "special builtins" below should fail, and not exit, so 'print end' is reached.
# Ref.: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/contents.html
# Left out are 'command exec /dev/null/nonexistent', where no shell follows the standard,
# as well as 'command exit' and 'command return', because, well, obviously.
command : </dev/null/no || print -n ok1
command . /dev/null/no || print -n ok2
command set +o bad@option || print -n ok3
command shift $(($# + 1)) || print -n ok4
(unalias times; PATH=/dev/null; eval 'command times foo bar >/dev/null || print -n ok5')
command trap foo bar baz quux || print -n ok6
command unset v || print -n ok7
command eval "(" || print -n ok8
command export v=baz || print -n ok9
command readonly v=bar || print -n ok10
command break && print -n ok11 # 'break' and 'continue' are POSIXly allowed to quietly...
command continue && print -n ok12 # ..."succeed" if they are used outside of a loop :-/
print end)
[[ $got == "$exp" ]] || err_exit "prefixing special builtin with 'command' does not stop it from exiting the shell on error" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
# ======
# https://github.com/att/ast/issues/872
hist_leak=$tmp/hist_leak.sh
print 'ulimit -n 15' > "$hist_leak"
for ((i=0; i!=11; i++)) do
print 'true foo\nhist -s foo=bar 2> /dev/null' >> "$hist_leak"
done
print 'print OK' >> "$hist_leak"
exp="OK"
got="$($SHELL -i "$hist_leak" 2>&1)"
[[ $exp == "$got" ]] || err_exit "file descriptor leak in hist builtin" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
# File descriptor leak after hist builtin substitution error
hist_error_leak=$tmp/hist_error_leak.sh
print 'ulimit -n 15' > "$hist_error_leak"
for ((i=0; i!=11; i++)) do
print 'hist -s no=yes 2> /dev/null' >> "$hist_error_leak"
done
print 'print OK' >> "$hist_error_leak"
exp="OK"
got="$($SHELL -i "$hist_error_leak" 2>&1)"
[[ $exp == "$got" ]] || err_exit "file descriptor leak after substitution error in hist builtin" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
# ======
# printf -v works as of 2021-11-18
((.sh.version >= 20211118)) && {
integer ver=.sh.version
exp=ok$'\f'0000$ver$'\n'
printf -v got 'ok\f%012d\n' $ver 2>/dev/null
[[ $got == "$exp" ]] || err_exit "printf -v not working" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
unset got
printf -v 'got[1][two][3]' 'ok\f%012d\n' $ver 2>/dev/null
[[ ${got[1]["two"][3]} == "$exp" ]] || err_exit "printf -v not working with array subscripts" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
unset got ver
}
# ======
# The rm builtin's -d option should remove files and empty directories without
# removing non-empty directories (unless the -r option is also passed).
# https://www.austingroupbugs.net/view.php?id=802
if builtin rm 2> /dev/null; then
echo foo > "$tmp/bar"
mkdir "$tmp/emptydir"
mkdir -p "$tmp/nonemptydir1/subfolder"
mkdir "$tmp/nonemptydir2"
echo dummyfile > "$tmp/nonemptydir2/shouldexist"
# Tests for lone -d option
got=$(rm -d "$tmp/emptydir" 2>&1)
[[ $? == 0 ]] || err_exit 'rm builtin fails to remove empty directory with -d option' \
"(got $(printf %q "$got"))"
[[ -d $tmp/emptydir ]] && err_exit 'rm builtin fails to remove empty directory with -d option'
got=$(rm -d $tmp/bar 2>&1)
[[ $? == 0 ]] || err_exit 'rm builtin fails to remove files with -d option' \
"(got $(printf %q "$got"))"
[[ -f $tmp/bar ]] && err_exit 'rm builtin fails to remove files with -d option'
rm -d "$tmp/nonemptydir1" 2> /dev/null
[[ ! -d $tmp/nonemptydir1/subfolder ]] && err_exit 'rm builtin has unwanted recursion with -d option on folder containing folder'
rm -d "$tmp/nonemptydir2" 2> /dev/null
[[ ! -f $tmp/nonemptydir2/shouldexist ]] && err_exit 'rm builtin has unwanted recursion with -d option on folder containing file'
# Recreate non-empty directories in case the above tests failed
mkdir -p "$tmp/nonemptydir1/subfolder"
mkdir -p "$tmp/nonemptydir2"
echo dummyfile > "$tmp/nonemptydir2/shouldexist"
# Tests combining -d with -r
got=$(rm -rd "$tmp/nonemptydir1" 2>&1)
[[ $? == 0 ]] || err_exit 'rm builtin fails to remove non-empty directory and subdirectory with -rd options' \
"(got $(printf %q "$got"))"
[[ -d $tmp/nonemptydir1/subfolder || -d $tmp/nonemptydir1 ]] && err_exit 'rm builtin fails to remove all folders with -rd options'
got=$(rm -rd "$tmp/nonemptydir2" 2>&1)
[[ $? == 0 ]] || err_exit 'rm builtin fails to remove non-empty directory and file with -rd options' \
"(got $(printf %q "$got"))"
[[ -f $tmp/nonemptydir2/shouldexist || -d $tmp/nonemptydir2 ]] && err_exit 'rm builtin fails to remove all folders and files with -rd options'
# Additional test: 'rm -f' without additional arguments should act
# as a no-op command. This bug was fixed in ksh93u+ 2012-02-14.
got=$(rm -f 2>&1)
if (($? != 0)) || [[ ! -z $got ]]
then err_exit 'rm -f without additional arguments does not work correctly' \
"(got $(printf %q "$got"))"
fi
fi
# ======
# These are regression tests for the cd command's -e and -P flags
mkdir -p "$tmp/failpwd"
cd "$tmp/failpwd"
"$SHELL" -c 'cd /; exec rmdir "$1"' x "$tmp/failpwd"
cd -P .
got=$?; exp=0
(( got == exp )) || err_exit "cd -P without -e exits with error status if \$PWD doesn't exist (expected $exp, got $got)"
if ((.sh.version >= 20211205))
then
cd -eP .
got=$?; exp=1
(( got == exp )) || err_exit "cd -eP doesn't fail if \$PWD doesn't exist (expected $exp, got $got)"
fi
cd "$tmp"
cd -P "$tmp/notadir" >/dev/null 2>&1
got=$?; exp=1
(( got == exp )) || err_exit "cd -P without -e fails with wrong exit status on nonexistent dir (expected $exp, got $got)"
if ((.sh.version >= 20211205))
then
cd -eP "$tmp/notadir" >/dev/null 2>&1
got=$?; exp=2
(( got == exp )) || err_exit "cd -eP fails with wrong exit status on nonexistent dir (expected $exp, got $got)"
fi
OLDPWD="$tmp/baddir"
cd -P - >/dev/null 2>&1
got=$?; exp=1
(( got == exp )) || err_exit "cd -P without -e fails with wrong exit status on \$OLDPWD (expected $exp, got $got)"
if ((.sh.version >= 20211205))
then
cd -eP - >/dev/null 2>&1
got=$?; exp=2
(( got == exp )) || err_exit "cd -eP fails with wrong exit status on \$OLDPWD (expected $exp, got $got)"
fi
cd "$tmp" || err_exit "couldn't change directory from nonexistent dir"
(set -o restricted; cd -P /) >/dev/null 2>&1
got=$?; exp=1
(( got == exp )) || err_exit "cd -P in restricted shell has wrong exit status (expected $exp, got $got)"
if ((.sh.version >= 20211205))
then
(set -o restricted; cd -eP /) >/dev/null 2>&1
got=$?; exp=2
(( got == exp )) || err_exit "cd -eP in restricted shell has wrong exit status (expected $exp, got $got)"
fi
(set -o restricted; cd -\?) >/dev/null 2>&1
got=$?; exp=1
(( got == exp )) || err_exit "cd -\\? shows usage info in restricted shell and has wrong exit status (expected $exp, got $got)"
(cd -P '') >/dev/null 2>&1
got=$?; exp=1
(( got == exp )) || err_exit "cd -P to empty string has wrong exit status (expected $exp, got $got)"
if ((.sh.version >= 20211205))
then
(cd -eP '') >/dev/null 2>&1
got=$?; exp=2
(( got == exp )) || err_exit "cd -eP to empty string has wrong exit status (expected $exp, got $got)"
fi
# ======
# The head and tail builtins should work on files without newlines
if builtin head 2> /dev/null; then
print -n nonewline > "$tmp/nonewline"
exp=nonewline
got=$(head -1 "$tmp/nonewline")
[[ $got == $exp ]] || err_exit "head builtin fails to correctly handle files without an ending newline" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
fi
if builtin tail 2> /dev/null; then
print -n 'newline\nnonewline' > "$tmp/nonewline"
exp=nonewline
got=$(tail -1 "$tmp/nonewline")
[[ $got == $exp ]] || err_exit "tail builtin fails to correctly handle files without an ending newline" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
fi
# ======
# ksh93v- accidentally broke the sleep builtin's support for
# using microseconds in the form of <num>U.
got=$(sleep 1U 2>&1) || err_exit "sleep builtin cannot handle microseconds in the form of <num>U (got $(printf %q "$got"))"
# ======
# Tests to avoid backporting an inherently broken 'read -p' compat hack from ksh 93v- or ksh2020
# Discussion: https://github.com/ksh93/ksh/issues/463
# ...It hangs in an infinite option parsing loop on encountering a stacked 'p' option
(echo ok |& read -pt1 && [[ $REPLY == ok ]]) &
test_pid=$!
(sleep 1; kill -s KILL "$test_pid" 2>/dev/null) & # another bg job to kill frozen test job
sleep_pid=$!
{ wait "$test_pid"; } 2>/dev/null # get job's exit status, suppressing signal messages
((!(e = $?))) || err_exit "'read -pt1' hangs (got status $e$( ((e>128)) && print -n /SIG && kill -l "$e"))"
kill "$sleep_pid" 2>/dev/null
# ...It failed to implement co-process compatibility for 'read -p .dot.varname' or 'read -p varname?prompt'
(echo ok |& read -p .sh.foo"?dummy prompt: " && [[ ${.sh.foo} == ok ]]) </dev/null \
|| err_exit "'read -p' co-process usage is not fully backward compatible with ksh 93u+"
# Backported ksh93v- 2014-06-25 test for eval bug when called
# from . script in a startup file.
print $'eval : foo\nprint ok' > "$tmp/evalbug"
print ". $tmp/evalbug" > "$tmp/envfile"
[[ $(ENV=$tmp/envfile "$SHELL" -i -c : 2> /dev/null) == ok ]] || err_exit 'eval inside dot script called from profile file not working'
# Backported ksh93v- 2013-03-18 test for 'read -A', where
# IFS sets the delimiter to a newline while -d specifies
# no delimiter (-d takes priority over IFS).
if ((SHOPT_BRACEPAT)); then
got=$(printf %s\\n {a..f} | IFS=$'\n' read -rd '' -A a; typeset -p a)
exp=$'typeset -a a=($\'a\\nb\\nc\\nd\\ne\\nf\\n\')'
[[ $got == "$exp" ]] || err_exit "IFS overrides the delimiter specified by the read command's -d option" \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
fi
# ======
exit $((Errors<125?Errors:125))