#! /bin/bash -e

# NOTE: should be invoked as either:
#    ./msys-build-cvs cvs-VER.tar.bz2
#    which will unpack the "pristine" source distribution, patch it, 
#    and begin the build,
# OR, as
#    ../msys-build-cvs
#    where the current working directory is the unpacked and patched
#    source distribution.

### hardcode PKG and patches tarball
export PKG=cvs
export VER=1.12.13
export BLD=2
export SYS=msys
_sysver=$(uname -r)
export SYSVER=${_sysver%%\(*}
FULLPKG=${PKG}-${VER}-${BLD}-${SYS} # not sysver
SRCDIR_=${PKG}-${VER}
RELDOCDIR=share/doc/${PKG}/${VER}
BINPKG=${PKG}-${VER}-${BLD}-${SYS}-${SYSVER}-bin.tar.lzma
LANGPKG=${PKG}-${VER}-${BLD}-${SYS}-${SYSVER}-lang.tar.lzma
LICPKG=${PKG}-${VER}-${BLD}-${SYS}-${SYSVER}-lic.tar.lzma
DOCPKG=${PKG}-${VER}-${BLD}-${SYS}-${SYSVER}-doc.tar.lzma
SRCPKG=${PKG}-${VER}-${BLD}-${SYS}-${SYSVER}-src.tar.lzma
BIN_CONTENTS='bin share/cvs'
LANG_CONTENTS='share/locale'
LIC_CONTENTS="${RELDOCDIR}/COPYING ${RELDOCDIR}/COPYING.LIB"
DOC_CONTENTS="--exclude ${RELDOCDIR}/COPYING \
	--exclude ${RELDOCDIR}/COPYING.LIB \
	share/doc share/info share/man"

# displays error message and exits
error() {
        case $? in
                0) local errorcode=1 ;;
                *) local errorcode=$? ;;
        esac

        echo -e "\e[1;31m*** ERROR:\e[0;0m ${1:-no error message provided}";
        exit ${errorcode};
}

# displays information message
inform() {
        echo -e "\e[1;32m*** Info:\e[0;0m ${1}";
}

# displays warning message only
warning() {
        echo -e "\e[1;33m*** Warning:\e[0;0m ${1}";
}

# query
query() {
	while true
	do
          echo -e "\e[1;35m*** Query:\e[0;0m ${1}";
	  if read -e answer
	  then
	    query_result=$answer
	    return 0
	  else
	    # user did a ^D
	    echo -e "Quitting.\n"
	    exit 1
	  fi
	done
}

# displays command to stdout before execution
verbose() {
        echo "${@}"
        "${@}"
        return $?
}
export -f verbose warning inform error

cmparg=
set_cmparg() {
        case "$1" in
        *tar.bz2       ) cmparg="j" ;;
        *tar.lzma      ) cmparg=" --lzma" ;;
        *tar.xz        ) cmparg=" --use-compress-program=xz" ;;
        *tar.gz | *tgz ) cmparg="z" ;;
        *              ) cmparg="" ;;
        esac
}

do_patch() {
  #local patchfile_name;
  #local patchfile_path;
  #local patchlevel;
  if [ -n "$specific_patchlevel" ]
  then
    starting_patchlevel=$specific_patchlevel
    stopping_patchlevel=$specific_patchlevel
  else
    starting_patchlevel=0
    stopping_patchlevel=3
  fi
  specific_patchlevel=

  for patchfile_path
  do
    patchfile_name="${patchfile_path##*/}"

    if [ ! -e ${patchfile_path} ]
    then
      warning "patch ${patchfile_name} not found";
      continue;
    fi

    patchlevel=$starting_patchlevel

    while test $patchlevel -le $stopping_patchlevel
    do
      if patch -N -s --dry-run -p${patchlevel} -i ${patchfile_path} &> /dev/null
      then
        echo "*** applying patch ${patchfile_name}:";
        patch -N -p${patchlevel} -i ${patchfile_path} || error "patch ${patchfile_name} failed"
        break;
      elif patch -N -s --binary --dry-run -p${patchlevel} -i ${patchfile_path} &> /dev/null
      then
        echo "*** applying patch ${patchfile_name}:";
        patch -N --binary -p${patchlevel} -i ${patchfile_path} || error "patch ${patchfile_name} failed"
        break;
      elif patch -R -s --dry-run -p${patchlevel} -i ${patchfile_path} &> /dev/null
      then
        warning "patch ${patchfile_name} already applied; skipping";
        break;
      else
        if (( patchlevel == $stopping_patchlevel ))
        then
          error "patch ${patchfile_name} will not apply";
        else
          patchlevel=`expr $patchlevel + 1`;
          continue;
        fi
      fi
    done
  done
}
export -f do_patch

if [ "x" != "x$1" ]; then
  if [ -d "$1" ]; then
    savedir="$PWD"
    cd "$1"
    srcdir="$PWD"
  elif [ -f "$1" ]; then
    case "$1" in
    *.tar.bz2 ) srcdir=`echo $1 | sed -e 's/\.tar\.bz2$//'`
                unpack=tbz
                savedir="$PWD" ;;
    *.tar.gz  ) srcdir=`echo $1 | sed -e 's/\.tar\.gz$//'`
                unpack=tgz
                savedir="$PWD" ;;
    *.zip     ) srcdir=`echo $1 | sed -e 's/\.zip$//'`
                unpack=zip
                savedir="$PWD" ;;
    *.tar.lzma ) srcdir=`echo $1 | sed -e 's/\.tar\.lzma$//'`
                unpack=tlz
                savedir="$PWD" ;;
    *.tar.xz )  srcdir=`echo $1 | sed -e 's/\.tar\.xz$//'`
                unpack=txz
                savedir="$PWD" ;;
    * ) error "Bad src directory specified: $1" ;;
    esac
  else
    error "Bad src directory specified: $1"
  fi
else
  savedir="$PWD"
  srcdir="$PWD"
fi

if [ "$MSYSTEM" != "MSYS" ]
then
  echo "You must be in an MSYS shell to use this script"
  exit 1
fi

if [ -n "$unpack" ] ; then
  case "$unpack" in
  tbz ) inform "unpacking $1" ; tar xjf $1 ;;
  tgz ) inform "unpacking $1" ; tar xzf $1 ;;
  zip ) inform "unpacking $1" ; unzip -q $1 ;;
  tlz ) inform "unpacking $1" ; tar --lzma -xf $1 ;;
  txz ) inform "unpacking $1" ; tar --use-compress-program=xz -xf $1 ;;
  * ) error "unknown pack format" ;;
  esac
  if [ -n "${SRCDIR_}" ]; then
    if [ ! -d "${SRCDIR_}" ]; then
      echo "src package $1 does not unpack into assumed srcdir $SRCDIR_"
      exit 1
    fi
    srcdir=`cd ${SRCDIR_} && pwd`
  fi
  if [ ! -d "$srcdir" ]; then
    echo "src package $1 does not unpack into assumed srcdir $srcdir"
    exit 1
  fi


  cd ${srcdir}
  tar -xvjf ${savedir}/debian-cvs-1.12.13-12-and-gnulib-20090425-patches.tar.bz2
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/10_rsc2log_fix
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/11_check_method_crash
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/12_rcs2log_POSIX_sort
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/14_ext_expansion
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/15_PATH_MAX_check
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/20_readdir_errno
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/21_getcwd_chroot
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/25_import-n-X
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/30_quieten_syslog_errors
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/31_ipv6
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/51_newlines_in_commit_template
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/55_keyword_alphanumerics
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/56_extra_tags
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/60_PAM_support
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/61_remove_-R_warning
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/62_cvsrc_whitespace
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/65_login_cvspass_message
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/66_64bit_crashfix
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/67_date_format_option
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/68_DSA_external_passwd_file
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/69_ext_allowroot
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/71_cvsbug_tmpfix
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/80_cvs-repouid-0.1
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/81_fix_-l
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/85_normalize_correct_roots
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/86_server_wrapper
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/89_history_val-tag_world_writeable
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/90zlib-read-compressed.diff
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/93_homedir
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/94_parseopts
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/95_flag_conflicted_copies
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/96_manpage_fixes
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/97_cvs.info.typo
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/98_fix_sparc_sigbus.diff
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/99_copyright

  # NOTE: See cygwin-1.2.13-10.cygport from the corresponding cygwin package
  # for an explanation of where these six patches originated and how they
  # were generated.  For msys, we just use them as-is.
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/99z_1_gnulib_modules
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/99z_2_gnulib_filelist
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/99z_3_gnulib_Makefile_gnulib
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/99z_4_gnulib_Makefile_am
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/99z_5_gnulib_update
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/99z_6_gnulib_code_b3fef2a4-20090425

  # NOTE: 99z_debian_po includes various debian i18l patches. However, these
  # affect only a template file, that is not installed on cygwin -- plus, our
  # gettext doesn't grok ".template" format files.  So, including this patch
  # cause a lot of (ignored) build errors. Plus, we install a bunch of locale
  # dirs, and then don't install anything into them, so cygport deletes them.
  # So, why bother: because *something* is required so that the OTHER debian
  # patches which presume gettext usage will apply cleanly.
  specific_patchlevel=2 && do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/99z_debian_po

  # NOTE: MSYS doesn't support IPv6, so...
  do_patch debian-cvs-1.12.13-12-and-gnulib-20090425-patches/99z_revert_31_ipv6

  rm -rf debian-cvs-1.12.13-12-and-gnulib-20090425-patches
  specific_patchlevel=2 && do_patch ${savedir}/9991-cvs-1.12.13-10-cygwin-src.patch
  specific_patchlevel=2 && do_patch ${savedir}/9992-cvs-1.12.13-10-cygwin-ext.patch
  do_patch ${savedir}/9993-cvs-1.12.13-2-msys.patch
  do_patch ${savedir}/9994-cvs-1.12.13-po-template.patch
  cd ${savedir}
  srcdir=`cd $srcdir; pwd`
fi


abovedir=`cd ${srcdir}/..; pwd`
PREFIX=/usr

opt_flags="-O3 -fno-unit-at-a-time -s -march=i386 -mtune=i686"
export CFLAGS=${CFLAGS:-"${opt_flags}"}
# note: defining towlower this way breaks mbscasecmp, which in turn breaks
# cvs's casefolding, for filenames encoded using multibyte. But, for msys,
# we really have no alternative; not even GNULIB has completely implemented
# a replacement towlower.
export CPPFLAGS="${CPPFLAGS} -D__CYGWIN__ -DWEOF='((wint_t)-1)' -DEILSEQ=138 -Dtowlower=tolower"
export CXXFLAGS=${CXXFLAGS:-"${opt_flags}"}
export F77FLAGS=${F77FLAGS:-"${opt_flags}"}
export GCJFLAGS=${GCJFLAGS:-"${opt_flags}"}
export LDFLAGS="${LDFLAGS} -Wl,--enable-auto-import"
export LIBS=-lminires

# NO SPACES!!
export PATH=`pwd`:/bin:/usr/local/bin:/mingw/bin:/c/WINDOWS/system32:/c/WINDOWS

mkdir -p ${abovedir}/_build
builddir=${abovedir}/_build
mkdir -p ${abovedir}/_inst
instdir=${abovedir}/_inst
mkdir -p ${abovedir}/_log
logdir=${abovedir}/_log
mkdir -p ${abovedir}/_test
testdir=${abovedir}/_test
cd ${builddir}

msys_conf_prep() {
  inform "Preparing ${PKG}"
  cd ${srcdir}
  autoreconf -fvi

  if [ -f po/templates.pot ]
  then
    mv po/templates.pot po/cvs.pot
  fi
  cd ${builddir}
}

msys_conf () {
  # CYGWIN configuration notes:
  #   It is possible to use the following option:
  #      --with-cvs-admin-group=Administrators
  #   but we choose not to, for default cygwin installs
  #   Also, the following option
  #      --enable-case-sensitivity
  #   may be added for cygwin-1.7 builds, but not 1.5.
  #
  # MSYS configuration notes:
  #   explicitly set tmpdir to avoid autodetection hardcoding
  #     win32 path to $TMPDIR on buildhost
  #   explicitly specify editor (but vim must be installed)

  confargs="--prefix=${PREFIX} \
	--datarootdir=\${prefix}/share --docdir=\${prefix}/${RELDOCDIR} \
	--sysconfdir=/etc --localstatedir=/var \
	--sbindir=\${prefix}/sbin --libexecdir=\${prefix}/sbin \
	--enable-silent-rules --disable-rpath --enable-nls \
	--with-libiconv-prefix=/usr --with-libintl-prefix=/usr \
	--with-tmpdir=/tmp \
	--without-krb4 \
	--without-gssapi \
	--with-editor=vim"

  ### as of 20070923, msys fchdir is broken wrt relative paths
  ### verified 20090814
  export ac_cv_func_fchdir=no

  ${srcdir}/configure --srcdir=${srcdir} ${confargs} \
	CFLAGS="${CFLAGS}" \
	CPPFLAGS="${CPPFLAGS}" \
	CXXFLAGS="${CXXFLAGS}" \
	LDFLAGS="${LDFLAGS}" \
	LIBS="${LIBS}"

  for f in `find . -name "Makefile"`
  do
    cat "$f" | sed -e 's:/libintl\.a:/libintl.dll.a:g' \
      -e 's:/libiconv\.a:/libiconv.dll.a:g' \
      > "${f}.new" && \
    mv "${f}.new" "${f}"
  done
}

msys_build () {
  inform "Building ${PKG}"
  make V=0
}

msys_install () {
  inform "Installing ${PKG}"
  make install DESTDIR=${instdir}

  verbose rm -f ${instdir}${PREFIX}/share/info/dir

  verbose mkdir -p ${instdir}${PREFIX}/${RELDOCDIR}
  verbose cp -p ${srcdir}/ChangeLog* \
	${srcdir}/changelog.Debian \
	${srcdir}/AUTHORS \
	${srcdir}/BUGS \
	${srcdir}/COPYING \
	${srcdir}/COPYING.LIB \
	${srcdir}/FAQ \
	${srcdir}/HACKING \
	${srcdir}/MINOR-BUGS \
	${srcdir}/NEWS \
	${srcdir}/PROJECTS \
	${srcdir}/README \
	${srcdir}/README.Debian \
	${srcdir}/README.VMS \
	${srcdir}/TESTS \
	${srcdir}/TODO ${instdir}${PREFIX}/${RELDOCDIR}
  verbose cp -p ${abovedir}/cederqvist-1.12.13.html.bz2 ${instdir}${PREFIX}/${RELDOCDIR}
  verbose bzip2.exe -d ${instdir}${PREFIX}/${RELDOCDIR}/cederqvist-1.12.13.html.bz2
  verbose /usr/bin/install -m 0644 ${srcdir}/rcs2log.1 ${instdir}${PREFIX}/share/man/man1/

  verbose /usr/bin/install -d -m 755 ${instdir}${PREFIX}/share/doc/MSYS
  verbose /usr/bin/install -m 644 ${abovedir}/msys-cvs.RELEASE_NOTES \
          ${instdir}${PREFIX}/share/doc/MSYS/${PKG}-${VER}-${BLD}-${SYS}.RELEASE_NOTES.txt
}

msys_package () {
  inform "Packaging ${PKG}"
  cd ${instdir}${PREFIX}

  set_cmparg "${BINPKG}"
  tar cv${cmparg} --hard-dereference -f ${abovedir}/${BINPKG} ${BIN_CONTENTS}

  set_cmparg "${LANGPKG}"
  tar cv${cmparg} --hard-dereference -f ${abovedir}/${LANGPKG} ${LANG_CONTENTS}

  set_cmparg "${LICPKG}"
  tar cv${cmparg} --hard-dereference -f ${abovedir}/${LICPKG} ${LIC_CONTENTS}

  set_cmparg "${DOCPKG}"
  tar cv${cmparg} --hard-dereference -f ${abovedir}/${DOCPKG} ${DOC_CONTENTS}

  cd ${abovedir}
  set_cmparg "${SRCPKG}"
  tar cv${cmparg} -f ./${SRCPKG} \
    ${PKG}-${VER}.tar.bz2 \
    debian-cvs-1.12.13-12-and-gnulib-20090425-patches.tar.bz2 \
    cederqvist-1.12.13.html.bz2 \
    9991-cvs-1.12.13-10-cygwin-src.patch \
    9992-cvs-1.12.13-10-cygwin-ext.patch \
    9993-cvs-1.12.13-2-msys.patch \
    9994-cvs-1.12.13-po-template.patch \
    msys-cvs.RELEASE_NOTES \
    msys-build-cvs
}

if [ -z "$CVS_RUN_TEST_ARGS" ]
then

msys_conf_prep
msys_conf
msys_build
msys_install
msys_package
cd "$savedir"

fi

######################################################################
### some function definitions to help with running the tests       ###
######################################################################

PF=${PKG}
B=${builddir}
S=${srcdir}
T=${testdir}
L=${logdir}

############################################################################
# all_tests
############################################################################
all_tests() {
# Note: this is cut-n-paste from src/sanity.sh and should be 
# updated with each new upstream version.
	# Basic/miscellaneous functionality
	tests="version basica basicb basicc basic1 deep basic2 ls"
	tests="$tests parseroot parseroot2 parseroot3 files spacefiles"
	tests="${tests} commit-readonly commit-add-missing"
	tests="${tests} status"
	# Branching, tagging, removing, adding, multiple directories
	tests="${tests} rdiff rdiff-short"
	tests="${tests} rdiff2 diff diffnl death death2"
	tests="${tests} rm-update-message rmadd rmadd2 rmadd3 resurrection"
	tests="${tests} dirs dirs2 branches branches2 branches3"
	tests="${tests} branches4 tagc tagf tag-space"
	tests="${tests} rcslib multibranch import importb importc importX"
	tests="$tests importX2 import-CVS import-quirks"
	tests="${tests} update-p import-after-initial branch-after-import"
	tests="${tests} join join2 join3 join4 join5 join6 join7"
	tests="${tests} join-readonly-conflict join-admin join-admin-2"
	tests="${tests} join-rm"
	tests="${tests} new newb conflicts conflicts2 conflicts3"
	tests="${tests} clean"
	tests="${tests} keywordexpand"
	# Checking out various places (modules, checkout -d, &c)
	tests="${tests} modules modules2 modules3 modules4 modules5 modules6"
	tests="${tests} modules7 mkmodules co-d"
	tests="${tests} cvsadm emptydir abspath abspath2 toplevel toplevel2"
	tests="${tests} rstar-toplevel trailingslashes checkout_repository"
	# Log messages, error messages.
	tests="${tests} mflag editor env errmsg1 errmsg2 adderrmsg opterrmsg"
	tests="${tests} errmsg3"
	tests="${tests} close-stdout"
	tests="$tests debug-log-nonfatal"
	# Watches, binary files, history browsing, &c.
	tests="${tests} devcom devcom2 devcom3 watch4 watch5 watch6-0 watch6"
	tests="${tests} edit-check"
	tests="${tests} unedit-without-baserev"
	tests="${tests} ignore ignore-on-branch binfiles binfiles2 binfiles3"
	tests="${tests} mcopy binwrap binwrap2"
	tests="${tests} binwrap3 mwrap info taginfo posttag"
	tests="$tests config config2 config3 config4"
	tests="${tests} serverpatch log log2 logopt ann ann-id"
	# Repository Storage (RCS file format, CVS lock files, creating
	# a repository without "cvs init", &c).
	tests="${tests} crerepos rcs rcs2 rcs3 rcs4 rcs5"
	tests="$tests lockfiles backuprecover"
	tests="${tests} sshstdio"
	# More history browsing, &c.
	tests="${tests} history"
	tests="${tests} big modes modes2 modes3 stamps"
	# PreservePermissions stuff: permissions, symlinks et al.
	# tests="${tests} perms symlinks symlinks2 hardlinks"
	# More tag and branch tests, keywords.
	tests="${tests} sticky keyword keywordlog keywordname keyword2"
	tests="${tests} head tagdate multibranch2 tag8k"
	# "cvs admin", reserved checkouts.
	tests="${tests} admin reserved"
	# Nuts and bolts of diffing/merging (diff library, &c)
	tests="${tests} diffmerge1 diffmerge2"
	# Release of multiple directories
	tests="${tests} release"
	tests="${tests} recase"
	# Multiple root directories and low-level protocol tests.
	tests="${tests} multiroot multiroot2 multiroot3 multiroot4"
	tests="${tests} rmroot reposmv pserver server server2 client"
	tests="${tests} dottedroot fork commit-d template"
	tests="${tests} writeproxy writeproxy-noredirect writeproxy-ssh"
	tests="${tests} writeproxy-ssh-noredirect"
	echo "$tests"
}

############################################################################
# local_fail_list
############################################################################
local_fail_list() {
        tests="         rcslib"    # symlinks
        tests="${tests} abspath"   # also fails on cygwin
        tests="${tests} toplevel"  # permissions
        tests="${tests} lockfiles" # permissions
        tests="${tests} modes"     # permissions
        tests="${tests} modes3"    # permissions
        echo "$tests"
}

############################################################################
# remote_fail_list
############################################################################
remote_fail_list() {
	# not verified. this is just a copy of the
	# similar list from cvs-1.12.13-1 for cygwin-1.5
        tests="         rcslib"
        tests="${tests} parseroot"
        tests="${tests} status"
        tests="${tests} conflicts"
        tests="${tests} edit-check"
        tests="${tests} binfiles"
        tests="${tests} config"
        tests="${tests} stamps"
        tests="${tests} writeproxy-noredirect"
        tests="${tests} writeproxy-ssh-noredirect"
	echo "$tests"
}

############################################################################
# cvs_check_ssh_setup
############################################################################
GLOBAL_cvs_check_ssh_setup_good=no
cvs_check_ssh_setup() {
	if [ "${GLOBAL_cvs_check_ssh_setup_good}" == "yes" ]
	then
		/bin/true
		return
	fi      

	if ! (ps -eaf | grep ssh >/dev/null)
	then
		inform "sshd not running"
		/bin/false
		return
	fi
	if [ -z $SSH_AUTH_SOCK -o -z $SSH_AGENT_PID ]
	then
		inform "ssh-agent not running"
		/bin/false
		return
	fi
	inform "Hopefully, there won't be a passphrase query below"
	if ! /usr/bin/ssh $USER@$HOSTNAME exit
	then
		inform "Failed to ssh to $USER@$HOSTNAME."
		/bin/false
		return
	fi

	inform "If you just typed a passphrase, this testsuite is"
	inform "gonna get real old, real fast."
	GLOBAL_cvs_check_ssh_setup_good=yes
	sleep 2
	/bin/true
}

############################################################################
# check_cvsrc
############################################################################
GLOBAL_cvs_check_cvsrc_warned=
cvs_check_cvsrc() {
	if test -f ~/.cvsrc && test -z "$GLOBAL_cvs_check_cvsrc_warned"
	then
		warning "Detected ~/.cvsrc file. Test resuits are unreliable."
		GLOBAL_cvs_check_cvsrc_warned=yes
	fi
}

############################################################################
# cvs_do_one_test
############################################################################
cvs_do_one_test() {
	local tests
	local location
	local expected
	local aTest
	local mylogbase
	local remotehost
	local remoteargs
	local test_status
	local cap_status

	cvs_check_cvsrc

	if [ -z "${1}" ]
	then
		error "cvs_do_one_test: no test specified"
	else
		aTest="${1}"
	fi

	shift
	if (( $# > 2 ))
	then
		error "cvs_do_one_test: too many arguments: $aTest $@"
	fi


	if (( $# >= 1 ))
	then
		case "$1" in
		[Ff][Aa][Ii][Ll] )       expected="fail"   ;;
		[Pp][Aa][Ss][Ss] )       expected="pass"   ;;
		[Ll][Oo][Cc][Aa][Ll] )     location="local"  ;;
		[Rr][Ee][Mm][Oo][Tt][Ee] ) location="remote" ;;
		* ) error "cvs_do_one_test: unknown argument 2: $2" ;;
		esac
	fi

	if (( $# >= 2 ))
	then
		case "$2" in
		[Ff][Aa][Ii][Ll] )       expected="fail"   ;;
		[Pp][Aa][Ss][Ss] )       expected="pass"   ;;
		[Ll][Oo][Cc][Aa][Ll] )     location="local"  ;;
		[Rr][Ee][Mm][Oo][Tt][Ee] ) location="remote" ;;
		* ) error "cvs_do_one_test: unknown argument 2: $2" ;;
		esac
	fi
	if [ -z "${location}" ]
	then
		location="local"
	fi
	if [ -z "${expected}" ]
	then
		expected="pass"
	fi
	mylogbase="${logdir}/${PF}-check.${location}.exp-${expected}.${aTest}"

	cd ${B}/src 
	tests=$(all_tests)

	if [ -z "${aTest}" ]
	then
		error "cvs_do_one_test: must specify a test"
	fi
	if ! echo ${tests} | grep " ${aTest} " >/dev/null 2>&1
	then
		if ! echo ${tests} | grep "^${aTest} " >/dev/null 2>&1
		then
			if ! echo ${tests} | grep " ${aTest}\$" >/dev/null 2>&1
			then
				error "cvs_do_one_test: no such test: ${aTest}"
			fi
		fi
	fi

	export TESTDIR=${T}/cvs-sanity
	remoteargs=""
	if [ "${location}" == "remote" ]
	then
		export CVS_RSH=ssh
		remotehost=$(hostname)
		remoteargs="-h ${remotehost} -r"
		if ! cvs_check_ssh_setup
		then
			/bin/false
			return
		fi
	fi

	if [ "${expected}" == "pass" ]
	then
		test_status=pass
		/bin/bash ${S}/src/sanity.sh ${remoteargs} ${B}/src/cvs ${aTest} || test_status="fail"

	elif [ "${expected}" == "fail" ]
	then
		test_status="pass"
		/bin/bash ${S}/src/sanity.sh ${remoteargs} ${B}/src/cvs ${aTest} || test_status="fail"

	else
		error "cvs_do_one_test: internal error (expected value of $expected is neither pass nor fail"
	fi

	cap_status="${expected}_"
	if [ "${test_status}" != "${expected}" ]
	then
		cap_status=$(echo ${test_status} | tr [[:lower:]] [[:upper:]])
		warning "UNEXPECTED ${cap_status}: ${location} ${aTest} should have ${expected}ed"
	fi

	for fn in ${mylogbase}.PASS ${mylogbase}.FAIL ${mylogbase}.pass_ ${mylogbase}.fail_
	do 
		if [ -f ${fn} ]
		then
			rm -f ${fn}
		fi
	done

	cp ${B}/src/check.log ${mylogbase}.${cap_status}
}

############################################################################
# cvs_do_one_test_usage
############################################################################
cvs_do_one_test_usage() {
	cat <<-_EOF
		do_one_test() enables running a single member of the cvs test suite.
		Usage: CVS_RUN_TEST_ARGS="NAME [LOCATION] [EXPECT]" ../msys-build-cvs
		where: NAME     = the name of the test ('basica', 'modules7', etc)
		       LOCATION = 'local' or 'remote' (defaults to 'local')
		       EXPECT   = 'pass' or 'fail'    (defaults to 'pass')
		This odd invocation is to enable maximum code sharing with cygport
		implementation.
	_EOF
}

############################################################################
# cvs_check_local_known_pass
############################################################################
cvs_check_local_known_pass() {
	local tests=$(all_tests)
	local fails=$(local_fail_list)
	local t
	local -i c
	local -i total_count

	for t in ${fails}
	do
		tests=$(echo "$tests" |\
			 sed -e "s/ ${t} / /" -e "s/ ${t}\$/ /" -e "s/^${t} / /")
	done

	c=0; for t in ${tests} ; do c+=1 ; done
	total_count=$c
	
	c=1
	for t in ${tests}
	do
		inform "Test $c of $total_count in set(local/pass): $t"
		cvs_do_one_test ${t} local pass
		c+=1
	done
}

############################################################################
# cvs_check_local_known_fail
############################################################################
cvs_check_local_known_fail() {
	local tests=$(local_fail_list)
	local t
	local -i c
	local -i total_count

	c=0; for t in ${tests} ; do c+=1 ; done
	total_count=$c
	
	c=1
	for t in ${tests}
	do
		inform "Test $c of $total_count in set(local/fail): $t"
		cvs_do_one_test ${t} local fail
		c+=1
	done
}

############################################################################
# cvs_check_remote_known_pass
############################################################################
cvs_check_remote_known_pass() {
	local tests=$(all_tests)
	local fails=$(remote_fail_list)
	local t
	local -i c
	local -i total_count

	for t in ${fails}
	do
		tests=$(echo "$tests" |\
			 sed -e "s/ ${t} / /" -e "s/ ${t}\$/ /" -e "s/^${t} / /")
	done

	c=0; for t in ${tests} ; do c+=1 ; done
	total_count=$c
	
	c=1
	for t in ${tests}
	do
		inform "Test $c of $total_count in set(remote/pass): $t"
		cvs_do_one_test ${t} remote pass
		c+=1
	done
}

############################################################################
# cvs_check_remote_known_fail
############################################################################
cvs_check_remote_known_fail() {
	local tests=$(remote_fail_list)
	local t
	local -i c
	local -i total_count

	c=0; for t in ${tests} ; do c+=1 ; done
	total_count=$c
	
	c=1
	for t in ${tests}
	do
		inform "Test $c of $total_count in set(remote/fail): $t"
		cvs_do_one_test ${t} remote fail
		c+=1
	done
}

############################################################################
# invoke_func_with_args_default_usage
############################################################################
invoke_func_with_args_default_usage () {
	inform "$1: no usage statement available"
}

############################################################################
# invoke_func_with_args
############################################################################
invoke_func_with_args () {
	# This is an intermediary between do_one_test and cvs_do_one_test
	# Its sole job is to parse the arguments for --help/-h/-? and print
	# a usage message.
	if (( $# < 1 ))
	then
		error "Internal error: invoke_func_with_args requires at least one argument"
	fi

	local func_to_invoke=$1
	local usage_to_invoke=${1}_usage
	shift
	local -i l_argc=$#
	local -i l_argn=0
	local -a l_argv=("${@}")

	if test "$(type -t ${func_to_invoke})" != "function"
	then
		error "Internal error: '$func_to_invoke' is not a function"
	fi
	if test "$(type -t ${usage_to_invoke})" != "function"
	then
		usage_to_invoke=invoke_func_with_args_default_usage
	fi

	while (( l_argn < l_argc ))
	do
		case ${l_argv[${l_argn}]} in
		--help|-h|-? )
			eval "$usage_to_invoke $func_to_invoke"
			exit 0
			;;
		esac
		l_argn+=1;
	done
	eval "${func_to_invoke}" "${l_argv[@]}"
}

############################################################################
# do_one_test
############################################################################
do_one_test () {
	# This is an external-call interface to cvs_do_one_test
	# It accepts "arguments" in the environment variable
	# $CVS_RUN_TEST_ARGS which is a variable containing (space-
	# separated) arguments.  (Therefore, arguments themselves
	# may not contain spaces).

	# no quotes, let shell split into words
	invoke_func_with_args cvs_do_one_test $CVS_RUN_TEST_ARGS
}


############################################################################
# src_test
############################################################################
src_test() {
	local do_remote=yes

	if [ -n "$1" ]
	then
		if [ "$1" == "no-remote" ]
		then
			do_remote=no
		fi
	fi

	# let's check ssh first, while user is still
	# paying attention
	if test "${do_remote}" == "yes" && ! cvs_check_ssh_setup
	then
	        do_remote=no
	fi

	cvs_check_local_known_pass
	cvs_check_local_known_fail
	if [ "${do_remote}" == "yes" ]
	then
		cvs_check_remote_known_pass
		cvs_check_remote_known_fail
	else
		warning "All remote tests were skipped; ssh server inaccessible"
	fi
}

######################################################################
### end of function definitions -- back to "main" code             ###
######################################################################

cd ${builddir}

case "x$CVS_RUN_TEST_ARGS" in
"xdefault" ) src_test no-remote ;;
"x" )        warning "Skipping tests! Rerun from srcdir as"
             warning "     CVS_RUN_TEST_ARGS=default ../msys-build-cvs"
             warning "Also, for more information about the test harnees, try"
             warning "     CVS_RUN_TEST_ARGS=-h ../msys-build-cvs"
             ;;
* )          do_one_test "$CVS_RUN_TEST_ARGS"
esac

cd "$savedir"

