#! /bin/sh

######################################################################
#                             CAUTION                                #
# When editing this file be aware that it is the focus of /bin/sh    #
# portability problems.  Be very, very careful!  Problems with this  #
# file could cause test cases to succeed when they should fail!      #
######################################################################

# Common function definitions.
. ../common/command-names

# Try to find an echo command that allows us to suppress the newline
# at the end of the line, and also to use C-style escape codes.  We
# have our own, as a last resort.

## First, find out how to echo without appending a newline,
## and so on.  We end up with five variables:-
#
# echocmd       The name by which we invoke the "echo" command.
# ac_e          Leading argument for enabling escape codes.
# ac_n          Leading argument for echo to suppress newline.
# ac_c          Arg for suppressing newline (may need -e, we test for that.)
# ac_t          (don't know -- I stole this code from Autoconf...)
#
# FreeBSD echo (or at least some version of it) does not support both the
# -n and the -e option AT THE SAME TIME.  This information from
# Malcolm Boff <Malcolm_Boff@compuserve.com> on 1997/11/01.
# He suggested that we use printf(1) but we can't do that because
# many systems just don't support it.
#
# According to Marty Leisner <leisner@sdsp.mc.xerox.com>, SunOS 4.1.4
# has the -n option but not the -e option.
#
# Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
#
for echocmd in echo /bin/echo /usr/bin/echo /usr/5bin/echo ../../testutils/ekko
do
# Unless we're using the builtin, check that the command exists as a file.
if test -f $echocmd || test "$echocmd" = "echo"
then
  # The second echo here ensures that the parenthesised command
  # succeeds and the output ends with a newline.
  if ($echocmd "testing\c"; echo 1,2,3) | grep c >/dev/null
    then
    # Trailing \c option does not work without -e (it produces a literal c).
    if ($echocmd -e "testing\c"; echo 1,2,3) | grep c >/dev/null
    then
      # \c does not work even with -e.
      if ($echocmd -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null
      then
          # -n option not known (and \c not known)
          # I don't know what purpose setting ac_c to this value
          # has, but Autoconf does it...
          ac_n= ac_c='
' ac_t='        '
      else
          # -n option works even though -e does not.
          # This is unfortunate since the test scripts assume
          # in places the ability to expand escape codes.
          # Send message to STDERR because I want to investigate.
          #
          # According to Marty Leisner <leisner@sdsp.mc.xerox.com>,
          # SunOS 4.1.4 is one such "unusual" system.

          # echo Unusual system\; PLEASE inform '<jay@gnu.org>'. >&2
          ac_n=-n ac_c= ac_t=
      fi
    else
      # \c does work with -e.
      # Hence we do not need to use -n.
      ac_n= ac_c='\c' ec_t=
      # Break out of the loop, we have a workable solution.
      break
    fi
  else
    ac_n= ac_c='\c' ac_t=
    # Break out of the loop, we have a workable solution.
    break
  fi
fi
done

# If "echo -e" generates an error, don't do that.  Hey, at least
# /dev/null works!
if ($echocmd -e) >/dev/null 2>&1
then
  if ($echocmd -e) | sed s/-e/xe/ | grep xe >/dev/null
  then
    # Fallback position: use our own replacement which supports -e.
    echocmd="../../testutils/ekko"
    ac_e='-e'
  else
    ac_e='-e'
  fi
fi

# echo "ac_n="$ac_n
# echo "ac_e="$ac_e
# echo "ac_t="$ac_t
# echo "ac_c="$ac_c
# echo "echocmd=" $echocmd


# Function for echoing without a newline, with escape chars enabled.
echo_nonl () {
if test -z "$*"
then
    # this works around an apparent bug in Bash where
    # "$@$x" expands to something starting with char 0177 (DEL).
    true
else
    ${echocmd} ${ac_n} ${ac_e} "$@$ac_c" ;
fi
}


expect_fail=false

# Call fail when a test failed.
fail () {
name=`basename $0`
if ${expect_fail:-false}
then
        echo XFAIL $name "$@"
        # Return a failure status but don't exit...
	false
else
        echo FAIL $name "$@" ; exit 2;
fi ;
}


# Call success when a test succeeded.
success () {
name=`basename $0`

remove got.stdout           got.stderr
remove expected.stdout expected.stderr
remove command.log last.command

echo
echo "All Tests in `/bin/pwd`/$name are now completed on tools in '$dir'"

if ${expect_fail:-false}
then
        echo XPASS $name: $* ; exit 0;
else
        echo PASS $name:  $* ; exit 0;
fi ;
}

# Call miscarry wen a test could not be completed.
miscarry () {
echo `basename $0`: "Test could not be completed: " "$@"
echo "Arguments were: ${all_args}"
exit 1 ;
}


remove () {
    if test -z "$*"
    then
        # no files listed, nothing to do.
        true
    else
        rm -rf "$@" || miscarry "Could not remove $@"
    fi
}

rename () {
    if mv -f -- "$1" "$2"
    then
	return 0
    fi
    miscarry "Could not rename $1 to $2"
}

copy() {
    if [ $# -ne 3 ]
    then
	echo "${label}: copy(): wrong number of arguments (expected 3, got $#)" >&2
	return 1
    fi
    copy_label="${1}"
    copy_from="${2}"
    copy_to="${3}"
    # In case ${to} is a symlink to something we don't want to modify,
    # ensure that cp will create ${to} instead of overwriting any
    # existing contents.
    rm -f -- "${copy_to}"
    if cp  -- "${copy_from}" "${copy_to}"
    then
	# By default cp will use the mode of ${from} when creating
	# ${to}.  This can mean ${to} is created as a read-only file
	# when the source tree is read-only (e.g. during 'make
	# distcheck').  Our tests assume that the copied file is
	# writable so we need to ensure that's the case.
	if chmod u+w -- "${copy_to}"
	then
	    return 0
	fi
	miscarry "${copy_label}: failed to make ${copy_to} writable"
	return 1
    fi
    miscarry "${copy_label}: failed to copy ${copy_from} to ${copy_to}"
    return 1
}

test_script=

##############
docommand () {
# $1 is the label.
# $2 is the command to execute.
# $3 is the return value to expect
# $4 is what to expect on stdout
# $5 is what to expect on stderr.
#
# If --silent is specified before the label, no output is normally made.
    #
all_args="$@"
remove last.command expected.stdout expected.stderr got.stdout got.stderr
silent=false
stdout_regex=false
stderr_regex=false
stdout_is_file=false
stderr_is_file=false
while case "$1" in
--silent) silent=true ; true ;;
--nosilent) silent=false ; true ;;
--stderr_regex) stderr_regex=true ; true ;;
--stderr_is_file) stderr_is_file=true ; true ;;
--nostderr_regex) stderr_regex=false ; true ;;
--stdout_regex) stdout_regex=true ; true ;;
--stdout_is_file) stdout_is_file=true ; true ;;
--nostdout_regex) stdout_regex=false ; true ;;
*) false ;;
esac
do
    shift
done
if test -z "${test_script}"; then
    script_dir=`dirname $0`
    case "${script_dir}" in
	.) script_dir=`pwd`;;
    esac
    script_base=`basename ${script_dir}`
    test_script="${script_base}"/`basename $0`
fi
label="${test_script}:$1"
eval "$silent" || echo_nonl "${label}"...

shift

if ${stderr_is_file}
then
    copy stderr_early "${4}" expected.stderr
else
    echo_nonl "$4" > expected.stderr
fi
echo "${1}" > last.command

echo  >> command.log
echo "# ${label}"  >> command.log
echo "${1}" >> command.log
eval "$1" >got.stdout 2>got.stderr
rv=$?
echo '# $? was' "$rv;  $(env LC_ALL=C wc -c < got.stdout) bytes on stdout; $(env LC_ALL=C wc -c < got.stderr) bytes on stderr" >> command.log

# Some sed implementations cannot handle an input which is non-empty
# but lacks a final newline, so work around that.
( cat got.stdout ; echo ) >| got.stdout.nl
( cat got.stderr ; echo ) >| got.stderr.nl
sed -e '$ d' -e 's/^/# stdout: /' >> command.log < got.stdout.nl
sed -e '$ d' -e 's/^/# stderr: /' >> command.log < got.stderr.nl
rm -f got.stdout.nl got.stderr.nl

if test "$2" != "IGNORE"
then
    if test $rv -eq "${2}"
    then
	true
    else
	# If the expected return value (which we didn't get) was zero,
        # stderr may contain an error message.
	errmsg="`cat got.stderr`"
	if test -z "$errmsg"
	then
	    tail="No error message was printed on stderr"
	else
	    tail="error message: $errmsg"
	fi
	fail "$label: $1: Expected return value $2, got return value $rv
$tail"
    fi
fi

if test "$3" != "IGNORE"
then
    if ${stdout_is_file}
    then
	stdout_contents="$( cat "${3}" )"
	copy stdout_file "${3}" expected.stdout
    else
	stdout_contents="${3}"
	echo_nonl "$3" > expected.stdout
    fi

    if $stdout_regex; then
	# We use egrep regexes to that we can run these tests on old
	# versions of Solaris.  Older versions of Solaris do not
	# support grep -E.
	if egrep -e "${stdout_contents}" < got.stderr >/dev/null
	then
	    echo "# stdout output matches ${stdout_contents}"  >> command.log
	else
	    echo "# stdout output does not match ${stdout_contents}"  >> command.log
	    fail "$label: stdout output did not match ${stdout_contents}"
	fi
    else
	# diff can fail if the file does not end in newline.
	echo        >>expected.stdout
	echo        >>     got.stdout
	# Prefer cmp in case the data is binary.
	if ! cmp expected.stdout got.stdout
	then
	    diff expected.stdout got.stdout
	    fail "$label: stdout format error with $1"
	fi
    fi
fi

if test "$4" != "IGNORE"
then
    if ${stderr_is_file}
    then
	stderr_contents="$( "cat ${4}" )"
	copy stderr_file "${4}" expected.stderr
    else
	stderr_contents="${4}"
	echo_nonl "$4" > expected.stderr
    fi

    if $stderr_regex; then
	# We use egrep regexes to that we can run these tests on old
	# versions of Solaris.  Older versions of Solaris do not
	# support grep -E.
	if egrep -e "${stderr_contents}" < got.stderr >/dev/null
	then
	    echo "# stderr output matches ${stderr_contents}"  >> command.log
	else
	    echo "# stderr output does not match ${stderr_contents}"  >> command.log
	    fail "$label: stderr output did not match ${stderr_contents}"
	fi
    else
	# diff can fail if the file does not end in newline.
	echo        >>expected.stderr
	echo        >>     got.stderr
	diff expected.stderr got.stderr || fail "$label: stderr format error with $1"
    fi
fi

remove last.command expected.stdout expected.stderr got.stdout got.stderr
eval "$silent" || echo "passed "
( exit $rv; )
}

#######################
do_output () {
# $1 is the label.
# $2 is the command to execute.
# $3 is the return value to expect
# $4 is a file containing what to expect on stdout.
# $5 is what to expect on stderr.
remove last.command expected.stdout expected.stderr got.stdout got.stderr
label="$1"
echo_nonl "$label..."
shift

echo_nonl $4 > expected.stderr
echo $1 > last.command

echo  >> command.log
echo "# ${label}"  >> command.log
echo $1 >> command.log
$1 >got.stdout 2>got.stderr
rv=$?
echo '# $? was' "$rv;  $(env LC_ALL=C wc -c < got.stdout) bytes on stdout; $(env LC_ALL=C wc -c < got.stderr) bytes on stderr" >> command.log
sed -e 's/^/# stdout: /' >> command.log < got.stdout
sed -e 's/^/# stderr: /' >> command.log < got.stderr

if test "$2" != "IGNORE"
then
    test $rv -eq $2 || fail "$label: $1: Expected return value $2, got return value $rv"
fi

if test "$3" != "IGNORE"
then
    diff $3 got.stdout || fail "$label: stdout format error with $1"
fi

if test "$4" != "IGNORE"
then
    echo_nonl $4 > expected.stderr
    # diff can fail if the file does not end in newline.
    echo        >>expected.stderr
    echo        >>     got.stderr
    diff expected.stderr got.stderr || fail "$label: stderr format error with $1"
fi

remove last.command expected.stdout expected.stderr got.stdout got.stderr
echo "passed "
( exit ${rv}; )
}
