########################################################################
# #
# This software is part of the ast package #
# Copyright (c) 1994-2012 AT&T Intellectual Property #
# and is licensed under the #
# Eclipse Public License, Version 1.0 #
# by AT&T Intellectual Property #
# #
# A copy of the License is available at #
# (with md5 checksum b35adb5213ca9657e911e9befb180842) #
# #
# Information and Software Systems Research #
# AT&T Research #
# Florham Park NJ #
# #
# Glenn Fowler <gsf@research.att.com> #
# #
########################################################################
command=regress
[-?
@(#)$Id: regress (AT&T Research) 2012-02-02 $
]
[+NAME?regress - run regression tests]
[+DESCRIPTION?\bregress\b runs the tests in \aunit\a, or
\aunit\a\b.tst\b if \aunit\a does not exist. If \acommand\a is omitted
then it is assumed to be the base name of \aunit\a. All testing is done
in the temporary directory \aunit\a\b.tmp\b.]
[+?Default test output lists the \anumber\a and \adescription\a for
each active \bTEST\b group and the \anumber\a:\aline\a for each
individual \bEXEC\b test. Each test that fails results in a diagnostic
that contains the word \bFAILED\b; no other diagnostics contain this
word.]
[b:ignore-space?Ignore space differences when comparing expected
output.]
[i:pipe-input?Repeat each test with the standard input redirected through a
pipe.]
[k:keep?Enable \bcore\b dumps, exit after the first test that fails,
and do not remove the temporary directory \aunit\a\b.tmp\b.]
[l:local-fs?Force \aunit\a\b.tmp\b to be in a local filesystem.]
[o:pipe-output?Repeat each test with the standard output redirected through
a pipe.]
[p:pipe-io?Repeat each test with the standard input and standard output
redirected through pipes.]
[q:quiet?Output information on \bFAILED\b tests only.]
[r!:regular?Run each test with the standard input and standard output
redirected through regular files.]
[t:test?Run only tests matching \apattern\a. Tests are numbered and
consist of at least two digits (0 filled if necessary.) Tests matching
\b+(0)\b are always run.]:[pattern]
[x:trace?Enable debug tracing.]
[v:verbose?List differences between actual (<) and expected (>) output,
errors and exit codes. Also disable long output line truncation.]
unit [ command [ arg ... ] ]
[+INPUT FILES?The regression test file \aunit\a\b.tst\b is a \bksh\b(1)
script that is executed in an environment with the following functions
defined:]
{
[+BODY \b{ ... }?Defines the test body; used for complex tests.]
[+CD \b\adirectory\a?Create and change to working directory for
one test.]
[+CLEANUP \b\astatus\a?Called at exit time to remove the
temporary directory \aunit\a\b.tmp\b, list the tests totals via
\bTALLY\b, and exit with status \astatus\a.]
[+COMMAND \b\aarg\a ...?Runs the current command under test with
\aarg\a ... appended to the default args.]
[+CONTINUE?The background job must be running.]
[+COPY \b\afrom to\a?Copy file \afrom\a to \ato\a. \afrom\a may
be a regular file or \bINPUT\b, \bOUTPUT\b or \bERROR\b. Post
test comparisons are still done for \afrom\a.]
[+DIAGNOSTICS \b[ \b1\b | \b0\b | \apattern\a ]]?No argument or an
argument of \b1\b declares that diagnostics are to expected for
the remainder of the current \bTEST\b; \b0\b reverts to the default
state that diagnostics are not expected; otherwise the argument
is a \bksh\b(1) pattern that must match the non-empty contents
of the standard error.]
[+DO \b\astatement\a?Defines additional statements to be executed
for the current test. \astatement\a may be a { ... } group.]
[+EMPTY \bINPUT|OUTPUT|ERROR|SAME?The corresponding file is
expected to be empty.]
[+ERROR \b[ \b-e\b \afilter\a ]] [ \b-n\b ]] \afile\a | - \adata\a ...?The
standard error is expected to match either the contents
of \afile\a or the line \adata\a. \bERROR -n\b does not
append a newline to \adata\a. \afilter\a is a shell command
or pipeline that reads standard input and writes standard
output that is applied to ERROR before comparison with the
expected contents.]
[+EXEC \b[ \aarg\a ... ]]?Runs the command under test with
optional arguments. \bINPUT\b, \bOUTPUT\b, \bERROR\b, \bEXIT\b
and \bSAME\b calls following this \bEXEC\b up until the next
\bEXEC\b or the end of the script provide details for the
expected results. If no arguments are specified then the
arguments from the previious \bEXEC\b in the current \bTEST\b
group are used, or no arguments if this is the first \bEXEC\b
in the group.]
[+EXIT \b\astatus\a?The command exit status is expected to match
the pattern \astatus\a.]
[+EXITED?The background job must have exited.]
[+EXPORT \b[-]] \aname\a=\avalue\a ...?Export environment
variables for one test.]
[+FATAL \b\amessage\a ...?\amessage\a is printed on the standard
error and \bregress\b exits with status \b1\b.]
[+FIFO \bINPUT|OUTPUT|ERROR\b [ \b-n\b ]] \afile\a | - \adata\a ...?The
\bIO\B file is a fifo.]
[+IF \b\acommand\a [\anote\a]]?If the \bsh\b(1) \acommand\a exits
0 then tests until the next \bELIF\b, \bELSE\b or \bFI\b are
enabled. Otherwise those tests are skipped. \bIF\b ... \bFI\b
may be nested, but must not cross \bTEST\b boundaries. \anote\a
is listed on the standard error if the correspoding test block
is enabled; \bIF\b, \bELIF\b, \bELSE\b may nave a \anote\a
operand.]
[+IGNORE \b\afile\a ...?\afile\a is ignored for subsequent result
comparisons. \afile\a may be \bOUTPUT\b or \bERROR\b.]
[+IGNORESPACE?Ignore space differences when comparing expected
output.]
[+INCLUDE \b\afile\a ...?One or more \afile\a operands are read
via the \bksh\b(1) \b.\b(1) command. \bVIEW\b is used to locate
the files.]
[+INFO \b\adescription\a?\adescription\a is printed on the
standard error.]
[+INITIALIZE?Called by \bregress\b to initialize a each
\bTEST\b group.]
[+INPUT \b[ \b-e\b \afilter\a ]] [ \b-n\b ]] \afile\a | - \adata\a ...?The
standard input is set to either the contents of \afile\a
or the line \adata\a. \bINPUT -n\b does not append a newline
to \adata\a. \afilter\a is a shell command or pipeline that
reads standard input and writes standard output that is
applied to OUTPUT before comparison with the expected contents.]
[+INTRO?Called by \bregress\b to introduce all \bTEST\b
groups.]
[+IO \b[ \bFIFO\b | \bPIPE\b ]] \bINPUT|OUTPUT|ERROR\b [ \b-e\b \afilter\a ]] [ \b-n\b ]] \afile\a | - \adata\a ...?Internal
support for the \bINPUT\b, \bOUTPUT\b and \bERROR\b functions.]
[+JOB \b\aop\a [ ... ]]?Like \bEXEC\b except the command is run
as a background job for the duration of the group or until it
is killed via \bKILL\b.]
[+KEEP \b\apattern\a ...?The temporary directory is cleared for
each test. Files matching \apattern\a are retained between
tests.]
[+KILL \b[ \asignal\a ]]?Kill the background job with \asignal\a
[ \bSIGKILL\b ]].]
[+MOVE \b\afrom to\a?Rename file \afrom\a to \ato\a. \afrom\a may
be a regular file or \bINPUT\b, \bOUTPUT\b or \bERROR\b. Post
test comparisons are ignored for \afrom\a.]
[+NOTE \b\acomment\a?\acomment\a is added to the current test
trace output.]
[+OUTPUT \b[ \b-e\b \afilter\a ]] [ \b-n\b ]] \afile\a | - \adata\a ...?The
standard output is expected to match either the contents
of \afile\a or the line \adata\a. \bOUTPUT -n\b does not
append a newline to \adata\a. \afilter\a is a shell command
or pipeline that reads standard input and writes standard
output that is applied to ERROR before comparison with the
expected contents.]
[+PIPE \bINPUT|OUTPUT|ERROR\b [ \b-n\b ]] \afile\a | - \adata\a ...?The
\bIO\B file is a pipe.]
[+PROG \b\acommand\a [ \aarg\a ... ]]?\acommand\a is run with
optional arguments.]
[+REMOVE \b\afile\a ...?\afile\a ... are removed after the
current test is done.]
[+RUN?Called by \bregress\b to run the current test.]
[+SAME \b\anew old\a?\anew\a is expected to be the same as
\aold\a after the current test completes.]
[+SET \b[\bno\b]]\aname\a[=\avalue\a]]?Set the command line
option --\aname\a. The setting is in effect for all tests until
the next explicit \bSET\b.]
[+TALLY?Called by \bregress\b display the \bTEST\b results.]
[+TEST \b\anumber\a [ \adescription\a ... ]]?Define a new test
group labelled \anumber\a with optional \adescripion\a.]
[+TITLE \b[+]] \atext\a?Set the \bTEST\b output title to
\atext\a. If \b+\b is specified then \atext\a is appended to
the default title. The default title is the test file base
name, and, if different from the test file base name, the test
unit base name.]
[+TWD \b[ \adir\a ... ]]?Set the temporary test dir to \adir\a.
The default is \aunit\a\b.tmp\b, where \aunit\a is the test
input file sans directory and suffix. If \adir\a matches \b/*\b
then it is the directory name; if \adir\a is non-null then the
prefix \b${TMPDIR:-/tmp}\b is added; otherwise if \adir\a is
omitted then
\b${TMPDIR:-/tmp}/tst-\b\aunit\a-$$-$RANDOM.\b\aunit\a is
used.]
[+UMASK \b[ \amask\a ]]?Run subsequent tests with \bumask\b(1)
\amask\a. If \amask\a is omitted then the original \bumask\b is
used.]
[+UNIT \b\acommand\a [ \aarg\a ... ]]?Define the command and
optional default arguments to be tested. \bUNIT\b explicitly
overrides the default command name derived from the test script
file name. A \acommand\a operand with optional arguments
overrides the \bUNIT\b \acommand\a and arguments, with the
exception that if the \bUNIT\b \acommand\a is \b-\b or \b+\b
the \bUNIT\b arguments are appended to the operand or default
unit command and arguments.]
[+VIEW \b\avar\a [ \afile\a ]]?\avar\a is set to the full
pathname of \avar\a [ \afile\a ]] in the current \b$VPATH\b
view if defined.]
}
[+SEE ALSO?\bnmake\b(1), \bksh\b(1)]
'
;;
*) USAGE='ko:[[no]name[=value]]t:[test]v unit [path [arg ...]]'
;;
esac
{
exit 1
}
function EMPTY
{
typeset i
typeset -n ARRAY=$1
do unset ARRAY[$i]
done
}
{
typeset i j
cd "$TWD"
;;
*) for i in *
do case $i in
esac
done
case $j in
?*) RM $j ;;
esac
;;
esac
BODY=""
COPY=""
DIAGNOSTICS=""
DONE=""
ERROR=""
EXIT=0
IGNORE=""
INIT=""
INPUT=""
MOVE=""
OUTPUT=""
}
function INTRO
{
typeset base command
if [[ ! $TEST_quiet ]]
command=${COMMAND##*/}
command=${command%' '*}
set -- $TITLE
case $1 in
fi
if (( $# ))
then shift
fi
;;
esac
while (( $# ))
do if [[ $TITLE ]]
else TITLE="$1"
fi
shift
done
fi
}
{
typeset msg
INIT) ;;
1) ;;
esac
1) ;;
esac
if (( $# ))
fi
TESTS=0
ERRORS=0
;;
esac
}
{
TITLE=$@
}
function UNWIND
{
done
fi
if [[ $JOBPID ]]
then if [[ $JOBPID != 0 ]]
wait
fi
fi
wait
}
{
typeset note
then if [[ ! $TEST_keep ]]
then cd $SOURCE
if [[ $TEST_local ]]
then RM ${TEST_local}
fi
fi
then note=terminated
fi
fi
[[ $TEST_keep ]] || UNWIND
exit $1
}
{
typeset i r=1
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK_ORIG
#print -u2 AHA#$LINENO $0 GROUP=$GROUP ITEM=$ITEM FLUSHED=$FLUSHED JOBOP=$JOBOP
if [[ $TEST_local ]]
then TEST_local=${TMPDIR:-/tmp}/rt-$$/${TWD##*/}
TEST_local=${TEST_local%/*}
fi
cd "$TWD"
: > rmu
then TEST_rmu=-u
fi
if [[ $UNIT ]]
then set -- "${ARGV[@]}"
case $1 in
""|[-+]*)
;;
*) UNIT "${ARGV[@]}"
;;
esac
fi
;;
FINI) ;;
then return 0
fi
then return 1
fi
fi
TEST_file=""
*" $i "*)
if [[ -f $i.sav ]]
elif [[ -f $i ]]
fi
;;
esac
done
*" $i "*)
;;
;;
esac
done
done
#print -u2 AHA#$LINENO $0 GROUP=$GROUP ITEM=$ITEM JOBOP=$JOBOP JOBPID=$JOBPID JOBSTATUS=$JOBSTATUS
then if [[ ! $TEST_quiet ]]
fi
elif [[ $BODY ]]
if [[ ! $TEST_quiet ]]
fi
done
else SHOW=
then if [[ ! $TEST_quiet ]]
fi
RESULTS 'pipe input'
else if [[ ! $TEST_quiet ]]
fi
RESULTS 'pipe io'
fi
then if [[ ! $TEST_quiet ]]
fi
RESULTS 'pipe output'
else if [[ $TEST_regular ]]
then if [[ ! $TEST_quiet ]]
fi
fi
fi
if [[ $TEST_pipe_input ]]
then if [[ ! $TEST_quiet ]]
fi
STATUS=$?
RESULTS 'pipe input'
fi
if [[ $TEST_pipe_output ]]
then if [[ ! $TEST_quiet ]]
fi
STATUS=$?
RESULTS 'pipe output'
fi
if [[ $TEST_pipe_io ]]
then if [[ ! $TEST_quiet ]]
fi
STATUS=$?
RESULTS 'pipe io'
fi
fi
set -- $COPY
COPY=""
while :
do case $# in
0|1) break ;;
*) cp $1 $2 ;;
esac
shift 2
done
set -- $MOVE
MOVE=""
while (( $# > 1 ))
do mv $1 $2
shift 2
done
fi
done
COMPARE=""
r=0
;;
esac
if [[ $COMMAND_ORIG ]]
then COMMAND=$COMMAND_ORIG
fi
return $r
}
{
[[ $GROUP == $TEST_select ]] || return 1
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK
return 0
}
{
typeset cmd=$1
[-+]) shift
else #BUG# ARGV=("${ARGV[@]}" "$@")
set -- "${ARGV[@]}" "$@"
fi
return
;;
esac
then set -- "${ARGV[@]}"
case $1 in
*) cmd=$1 ;;
esac
fi
UNIT=
shift
if [[ ! $cmd ]]
elif [[ ! $cmd ]]
fi
case $# in
0) ;;
esac
}
{
case $1 in
/*) TWD=$1 ;;
esac
}
{
COUNT=0
LASTITEM=0
case $1 in
esac
NOTE=
fi
unset ARGS
unset EXPORT
EXPORTS=0
TEST_file=""
then INITIALIZE
fi
fi
}
{
then return
fi
then RUN
fi
case $# in
0) set -- "${ARGS[@]}" ;;
esac
}
{
JOBPID=0
EXEC "$@"
}
function CONTINUE
{
RUN || return
#print -u2 AHA#$LINENO JOBOP=$JOBOP ITEM=$ITEM NOTE=$NOTE
}
function EXITED
{
RUN || return
#print -u2 AHA#$LINENO JOBOP=$JOBOP ITEM=$ITEM NOTE=$NOTE
}
{
RUN || return
JOBOP=$2
}
function CD
{
fi
}
function EXPORT
{
typeset x n v
then for x
do n=${x%%=*}
v=${x#*=}
done
else RUN
then return
fi
for x
do n=${x%%=*}
v=${x#*=}
done
fi
}
function FLUSH
{
then return
fi
then RUN
fi
}
{
typeset command args
then return
fi
COMMAND=$1
shift
}
{
NOTE=$*
}
{
then return
fi
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK_ORIG
while :
do case $1 in
*) break ;;
esac
done
op=$1
shift
while :
do case $1 in
-x) x=1
shift
;;
-e) (( $# > 1 )) && shift
shift
;;
shift
;;
-f*|-n) f=$1
shift
;;
*) break
;;
esac
done
case $# in
0) ;;
*) case $1 in
-) ;;
*) file=$1
;;
;;
esac
;;
esac
shift
;;
esac
do case $i in
$file) ;;
*) v="$v $i" ;;
esac
done
IGNORE=$v
;;
esac
if [[ $file != /* ]]
fi
;;
esac
#unset SAME[$op]
if [[ $file == /* ]]
fi
if [[ $file == */* ]]
fi
if [[ $file != */ ]]
fi
then if [[ $JOBOP ]]
then case $#:$f in
0:) ;;
*) print $f -r -- "$@" ;;
esac >> $file
else case $#:$f in
0:) ;;
*) print $f -r -- "$@" ;;
esac > $file
fi
elif [[ $#:$f != 0: ]]
then case $#:$f in
*) print $f -r -- "$@" ;;
esac >> $file &
fi
if [[ $x ]]
fi
fi
}
{
IO $0 "$@"
}
{
then return
fi
}
{
typeset f
then return
fi
for f
do case $f in
f=$TWD/$f
;;
/*) ;;
*) f=$PWD/$f
;;
esac
done
}
{
typeset i file v
then return
fi
case $# in
2) case $1 in
esac
SAME[$1]=$2
file=$1
;;
3) SAME[$2]=$3
file=$2
eval i='$'$1
*" $2 "*)
;;
*) eval $1='"$'$1' $2"'
;;
esac
;;
esac
do case $i in
$file) ;;
*) v="$v $i" ;;
esac
done
IGNORE=$v
;;
esac
}
{
IO $0 "$@"
}
{
IO $0 "$@"
}
{
if [[ ! $TEST_rmu ]]
fi
rm $TEST_rmu $TEST_rmflags "$@"
}
{
typeset i
for i
done
}
{
typeset i
for i
do case $i in
i=$TWD/$i
;;
esac
*" $i "*)
;;
;;
esac
done
}
{
typeset i
for i
esac
done
}
{
case $#:$1 in
0:|1:1) DIAGNOSTICS=1
EXIT='*'
;;
1:|1:0) DIAGNOSTICS=""
EXIT=0
;;
*) DIAGNOSTICS=$1
EXIT='*'
;;
esac
}
function IGNORESPACE
{
: ${IGNORESPACE=-b}
}
{
EXIT=$1
}
{
if [[ ! $1 ]]
fi
shift
if [[ ! $TEST_quiet ]]
fi
}
{
typeset input
;;
;;
esac
if [[ $TEST_keep ]]
then (
PS4=''
set -x
fi
if [[ $UMASK != $UMASK_ORIG ]]
umask $UMASK
fi
then input="< ${FILE[INPUT]}"
fi
if [[ $TEST_trace ]]
then set +x
eval print -u2 "$PS4" "${ENVIRON[@]}" "${EXPORT[@]}" PATH='$PATH' '$'COMMAND '"$@"' '$input' '"2>$TWD/ERROR"' '"${JOBPID:+&}"'
fi
eval "${ENVIRON[@]}" "${EXPORT[@]}" PATH='$PATH' '$'COMMAND '"$@"' $input "2>$TWD/ERROR" "${JOBPID:+&}"
STATUS=$?
[[ $TEST_trace ]] && set -x
if [[ $JOBPID ]]
then JOBPID=$!
fi
[[ $UMASK != $UMASK_ORIG ]] && umask $UMASK_ORIG
return $STATUS
}
{
if [[ $1 ]]
then io="$1 "
fi
[[ $JOBOP || $JOBPID || $JOBSTATUS ]] && sleep 1
*" $i "*) continue ;;
esac
op=${i##*/}
fi
if [[ ! $j ]]
then if [[ $i == /* ]]
then k=$i
else k=$TWD/$i
fi
do [[ -f $k.$s ]] && break
done
j=$k.$s
fi
then failed=$failed${failed:+,}DIAGNOSTICS
if [[ $TEST_verbose && $DIAGNOSTICS != 1 ]]
cat $i >&2
fi
fi
continue
fi
diff $IGNORESPACE $i $j >$i.diff 2>&1
if [[ -s $i.diff ]]
if [[ $TEST_verbose ]]
fi
fi
done
if [[ $JOBOP ]]
then wait $JOBPID
JOBSTATUS=$?
fi
#print -u2 AHA#$LINENO JOBOP=$JOBOP JOBPID=$JOBPID JOBSTATUS=$JOBSTATUS
if [[ ! $JOBPID ]]
fi
;;
fi
;;
*) if [[ ! $JOBPID ]]
fi
fi
;;
esac
fi
fi
if [[ $failed ]]
if [[ ! $TEST_quiet ]]
fi
if [[ $TEST_keep ]]
exit
fi
elif [[ ! $TEST_quiet ]]
fi
}
{
typeset i r
if [[ $TEST ]]
then RUN
fi
for i
do if [[ $i == - ]]
then r=1
elif [[ $i == + ]]
then r=
else if [[ $i == no?* ]]
then i=${i#no}
v=
elif [[ $i == *=* ]]
then v=${i#*=}
if [[ $v == 0 ]]
then v=
fi
i=${i%%=*}
else v=1
fi
i=${i//-/_}
if [[ $r ]]
then READONLY[$i]=1
elif [[ ${READONLY[$i]} ]]
then continue
fi
eval TEST_$i=$v
fi
done
}
{
nameref var=$1
if [[ $var ]]
then return 0
fi
case $# in
1) file=$1 ;;
*) file=$2 ;;
esac
pwd=${TWD%/*}
if [[ -r $file ]]
then if [[ ! -d $file ]]
return 0
fi
do if [[ -r $i ]]
return 0
fi
break
done
fi
/*) ;;
esac
esac
;;
esac
return 0
fi
do if [[ -f $i ]]
return 0
fi
break
done
fi
done
var=
return 1
}
{
typeset f v x
for f
then x=$x$'\n'". $v"
fi
done
[[ $x ]] && trap "$x" 0
}
{
if (( $# ))
then UMASK=$1
else UMASK=$UMASK_ORIG
fi
}
{
IO $0 "$@"
}
{
IO $0 "$@"
}
{
[[ $GROUP == $TEST_select ]] || return
[[ $2 && ! $TEST_quiet ]] && print -u2 "NOTE $2"
fi
}
{
[[ $GROUP == $TEST_select ]] || return
fi
[[ $2 && ! $TEST_quiet ]] && print -u2 "NOTE $2"
fi
}
{
[[ $GROUP == $TEST_select ]] || return
fi
[[ $1 && ! $TEST_quiet ]] && print -u2 "NOTE $1"
fi
}
function FI
{
[[ $GROUP == $TEST_select ]] || return
fi
}
# main
typeset -Z LAST=00
unset FIGNORE
;;
;;
;;
l) SET - local
;;
;;
;;
;;
;;
t) if [[ $TEST_select ]]
then TEST_select="$TEST_select|${OPTARG//,/\|}"
else TEST_select="${OPTARG//,/\|}"
fi
;;
;;
;;
exit 2
;;
esac
done
shift $OPTIND-1
case $# in
esac
export COLUMNS=80
UNIT=$1
shift
fi
if [[ $VPATH ]]
if [[ $OFFSET ]]
fi
fi
if [[ $REGRESS == */* ]]
if [[ ${#VIEWS[@]} ]]
done
fi
if [[ $PREFIX ]]
fi
fi
UMASK_ORIG=$(umask)
then UNIT "${ARGV[@]}"
fi
trap 'code=$?; CLEANUP $code' EXIT
if [[ ! $TEST_select ]]
then TEST_select="[0123456789]*"
fi
TEST_select="@($TEST_select|+(0))"
if [[ $TEST_trace ]]
then export PS4=':$LINENO: '
typeset -ft $(typeset +f)
set -x
fi
if [[ $TEST_verbose ]]
then typeset SHOW
fi
then ulimit -c 0
fi
set --pipefail
# some last minute shenanigans
alias BODY='BODY=BODY; function BODY'
alias CONTINUE='LINE=$LINENO; CONTINUE'
alias DO='(( $ITEM != $FLUSHED )) && RUN DO; DO &&'
alias DONE='DONE=DONE; function DONE'
alias EXEC='LINE=$LINENO; EXEC'
alias EXITED='LINE=$LINENO; EXITED'
alias INIT='INIT=INIT; function INIT'
alias JOB='LINE=$LINENO; JOB'
alias KILL='LINE=$LINENO; KILL'
alias PROG='LINE=$LINENO; FLUSH; PROG'
alias TEST='TESTLINE=$LINENO; TEST'
alias IF='LINE=$LINENO; FLUSH; IF'
alias ELIF='LINE=$LINENO; FLUSH; ELIF'
alias ELSE='LINE=$LINENO; FLUSH; ELSE'
alias FI='LINE=$LINENO; FLUSH; FI'
# do the tests
. $REGRESS