#!/bin/sh

# Copyright (c) 2000
#      Tanaka Akira. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright notice, this
#    list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

###

# CVSconnect enables you to do multiple CVS operations on single network
# connection.  CVSconnect sets up special environment using
# $CVSRSH/$CVSROOT for the command line CVS client.  In the environment,
# the command line CVS client reuses a network connection.

# CVSconnect is especially designed to use with CVSsuck.  And CVSconnect
# (or similar) will be integrated to a future version of CVSsuck.
# However, it is planned that CVSsuck directly connects to a cvs server
# without cvs command.

# Usage: cvsconnect cvsroot [command-to-run...]
#
# If command-to-run is not provided, cvsconnect runs shell with some
# diagnostic messages.  Then you can run cvs.

# Example:
# cvsconnect $HOME/.cvsroot

# Limitation:
# * The global option -z for compression cannot be used.
# If you use SSH via ext method, you can use compression by SSH instead.
#
# * CVS client must be run at a time.  Running two or more clients at a
# same time will make a trouble.  (will be fixed in future release.)
#
# * Some static variables in a CVS server may confuse you.
# It's a CVS bug.  Report it to CVS developper :-).
#
# * command line CVS client must request `Root' first.
# Don't mind.  There is no implementation which violates this
# assumption, however I know.  At least, CVS-1.10 and CVS-1.11 should
# work.

# Notes:
# * When CVS command is failed, CVSconnect automatically re-run the
# command with reconnected connection when CVS command is failed.
# Especially the failure is caused by a long running CVS-1.10 server.
# Since CVS-1.10 doesn't close some file descriptors, a long running CVS
# server will exceed a limit of file descriptors. 
#
# * /bin/sh should be POSIX sh because this script uses
# ${parameter#pattern}, etc.  If your /bin/sh is ancient Bourne shell,
# use ksh or bash.

case "$0" in
/*) prefix="`dirname $0`/";;
*/*) prefix="`pwd`/`dirname $0`/";;
*) prefix=;;
esac

cvs_client=${prefix}cvs-client
cvsconnect_client=${prefix}cvsconnect-client

cvsroot="${1?no cvsroot}"
shift

CVSCONNECT_BASE=${TMPDIR:-/tmp}/cvsconnect-$$
CVSCONNECT_CONNECTION=default	
export CVSCONNECT_BASE CVSCONNECT_CONNECTION
mkdir $CVSCONNECT_BASE $CVSCONNECT_BASE/bin || exit 1

cat <<End > $CVSCONNECT_BASE/_connect
#!/bin/sh
PATH="$PATH"
CVS_RSH='${CVS_RSH:-rsh}'
export PATH CVS_RSH
exec $cvs_client -r $cvsroot
End
chmod 755 $CVSCONNECT_BASE/_connect

cat <<End > $CVSCONNECT_BASE/_disconnect
#!/bin/sh
PATH="$PATH"
export PATH
for connection in "\$@"
do
  for f in $CVSCONNECT_BASE/\$connection.*.pid
  do
    pid=\`cat \$f 2>/dev/null\` && rm \$f 2>/dev/null && kill \$pid
  done
done
rm $CVSCONNECT_BASE/\$connection.*
End
chmod 755 $CVSCONNECT_BASE/_disconnect

CVSCONNECT_CVS="`which cvs`"
export CVSCONNECT_CVS
cat <<End > $CVSCONNECT_BASE/bin/cvs
#!/bin/sh
PATH="$PATH"
export PATH
if $CVSCONNECT_CVS "\$@" >$CVSCONNECT_BASE/\$\$.stdout 2>$CVSCONNECT_BASE/\$\$.stderr
then
  cat $CVSCONNECT_BASE/\$\$.stdout
  cat $CVSCONNECT_BASE/\$\$.stderr >&2
  rm $CVSCONNECT_BASE/\$\$.stdout $CVSCONNECT_BASE/\$\$.stderr
  exit 0
else
  #rm $CVSCONNECT_BASE/\$\$.stdout $CVSCONNECT_BASE/\$\$.stderr
  $CVSCONNECT_BASE/_disconnect \${CVSCONNECT_CONNECTION:-default}
  $CVSCONNECT_CVS "\$@"
  exit \$?
fi
End
chmod 755 $CVSCONNECT_BASE/bin/cvs

root="`${cvs_client} -p $cvsroot`"
CVS_RSH="${cvsconnect_client}"
CVSROOT=":ext:dummy@dummy.invalid:$root"
export CVS_RSH CVSROOT

echo "CVSCONNECT_BASE=$CVSCONNECT_BASE"
echo "CVSCONNECT_CONNECTION=$CVSCONNECT_CONNECTION"

echo "CVS_RSH=$CVS_RSH"
echo "CVSROOT=$CVSROOT"

PATH=$CVSCONNECT_BASE/bin:$PATH "${@-${SHELL:-/bin/sh}}"
status=$?

for f in $CVSCONNECT_BASE/*.pid
do
  pid=`cat $f 2>/dev/null` && rm $f 2>/dev/null && kill $pid
done

rm -rf $CVSCONNECT_BASE

exit $status
