#! /bin/sh

## Usage: xrsh clientmachine [ -l username ] [ Xcommand [ args ]]

## This is analogous to the standard "rsh" command, but for X programs.

## Runs Xcommand (defaults to "xterm") remotely on the client
## machine with the given name, displaying on X server determined by
## the current DISPLAY environment variable, with TERM set to "xterm".
## If DISPLAY is not set, or is incomplete, the necessary pieces are
## filled in from the default value of "<hostname>:0.0".  Optional "-l
## username" arguments are passed to rsh.  If the command is "xterm",
## then it adds "-n 'xterm @ <hostname>'" args.  Runs xhost on the
## server machine if server is different than client and XRSH_NOXHOST
## is unset.  If environment variable XDEBUG is unset, errors go to
## dev/null.  If XDEBUG is set to null, status messages are sent to
## stdout.  Otherwise, assumes XDEBUG is a filename to which status
## messages are appended.  Attempts not to leave any processes waiting
## (on either machine) for the client to exit: uses exec where
## possible, and tries to prevent rsh from waiting for input.  Uses
## /bin/csh on the remote host to get around user's shell.

## Things that commonly go wrong:
##   - DISPLAY variable is incorrect.  Run  setenv <displayhost>:0.0.
##   - host must be in user's .rhosts file on server (to allow rsh for xhost).
##   - "xhost" must be in your path on server (should set $path in .cshrc).
##   - host must be in user's .rhosts file on client (to allow rsh for Xcommand).
##   - Xcommand must be in your path on client (should set $path in .cshrc).
## where: host = machine from which xrsh is run, server = machine
## running X, client = machine that will run X program.

## Caveats: 1) This is a sh script, but uses csh on the remote
## machine.  2) Still leaves extra processes around sometimes.  THis
## is hard to get rid of on all machines.  2) If XDEBUG is a filename,
## the xrsh process must remain alive to receive error messages (maybe
## we could avoid this by sending messages to a temp file, and then
## cat'ing them back to the host machine when the X command exits).
## 3) Runs (possibly) unecessary xhost commands, but this is hard to
## avoid.

## Suggestions: 1) Might be nice if this could parse the .rhosts file
## to get a default username and full clientname.  2) Maybe 
## scrap the xhost stuff altogether, and assume the user has run a
## command to call xhost on each machine in the .rhosts file!  [the
## correct answer is to use xauth].

## Written 1/91 by Eero Simoncelli, <eero@media-lab.media.mit.edu>,
## with help from Martin Friedman <martin@media-lab.media.mit.edu>.
## Based on similar scripts by Jim Dempsey <jjd@bbn.com> and 
## Martin Friedman.

## Usage message
if [ $# = 0 ]; then
  echo "Usage: `basename $0` clientmachine [ -l username ] [ Xcommand [ args ]]"
  exit 1
fi

## Send messages and error output to /dev/null by default, thus
## allowing the script to terminate.  If XDEBUG is set to nothing,
## send output to stdout (script process will remain, awaiting output).
## Else assume XDEBUG is a filename and append errors to this file
## (again, script process will remain, awaiting output).
case "x${XDEBUG-NODEBUG}" in
  xNODEBUG)
	debugsh="> /dev/null 2>&1"
	debugcsh=">& /dev/null"
	;;
  x)
	debugsh=
	debugcsh=
	;;
  x*)
	debugsh=">> $XDEBUG 2>&1"
	debugcsh=">>&! $XDEBUG"
	;;
esac

## Get client machine name, and current (host) machine names
client="$1"; shift
host=${HOST-`/bin/hostname`}

## Get (optional) username
if [ "x$1" = "x-l" ] ; then
	user="$1 $2"; shift; shift
else
	user=
fi

## Set up command
if [ $# -ge 1 ]; then
	command="$1"; shift
else
	command="xterm"
fi

## Grab rest of args, since will lose them below when using the IFS
args=$@

## Hold onto original Internal Field Specifier
oldIFS=$IFS

## Set up shortened client and host names.  Fast parsing using the IFS.
IFS="${oldIFS}."
set - $client;  sclient=$1
set - $host;  shost=$1
IFS=$oldIFS

## If command is xterm, add title string arg to args
if [ "$command" = "xterm" ]; then
	args="-n 'xterm @ $sclient' $args"
fi

## Set up display, defaulting to ${host}:0.0
case "x$DISPLAY" in
  x) 	echo "DISPLAY variable not set.  Assuming $host:0.0"
	display="${host}:0.0"
	;;
  x:*) 	display="${host}${DISPLAY}"
	;;
  xunix:*)
	IFS="${oldIFS}:"; set - $DISPLAY; display="${host}:$2"; IFS=$oldIFS
	;;
  x*:)	display="${DISPLAY}0.0"
	;;
  x*)	display="$DISPLAY"
	;;
esac

## Determine if display machine (server) is same as host machine
case "x$DISPLAY" in
  x|x:*|xunix:*)
	serverishost=1
	;;
  x*)
	IFS="${oldIFS}:"; set - $DISPLAY; server=$1
	IFS="${oldIFS}."; set - $server; sserver=$1; IFS=$oldIFS
	if [ $sserver = $shost ] ; then
	    serverishost=1
	else
	    serverishost=0
	fi
        ;;
esac

## Redirect all subsequent input and output
eval "exec < /dev/null $debugsh"

## If debugging, then echo some status lines:
if [ "x${XDEBUG-NODEBUG}" != "xNODEBUG" ] ; then
	echo "EXECUTING $command $args"
	if [ "$user" = "" ] ; then
		echo "   ON CLIENT $client"
	else
		echo "   ON CLIENT $client, AS USER $user"
	fi 
	echo "   FROM HOST $host, ON DISPLAY $display"
fi

## If client is not the same as server, and XRSH_NOXHOST is unset, run
## xhost.
if [ "x${sclient}" != "x${sserver}" ] ; then
    if [ "x${XRSH_NOXHOST-doit}" = "xdoit" ] ; then
	if [ $serverishost = 1 ] ; then
	 	DISPLAY=$display; export DISPLAY; xhost +$client < /dev/null
	else
		rsh $server $user -n "exec /bin/csh -c \
		    \"setenv DISPLAY $display; xhost +$client\" < /dev/null"
	fi
    fi
fi

## Execute command on client, using exec to avoid spawning extra
## processes.  Use /bin/csh to get around the user's shell.  If no
## debugging messages will be passed back, let the rsh
## process exit immediately by sending the /bin/csh output to
## /dev/null.  
if [ "x${XDEBUG-NODEBUG}" = "xNODEBUG" ] ; then
  exec rsh $client $user \
    "exec /bin/csh -c \
       \"setenv DISPLAY $display; setenv TERM xterm; exec $command $args &\" \
     	< /dev/null >& /dev/null"
else
  exec rsh $client $user -n \
    "exec /bin/csh -c \
       \"setenv DISPLAY $display; setenv TERM xterm; exec $command $args \" \
    "
fi

exit 0
