#!/bin/sh -e

# xvfb-run - run the specified command in a virtual X server

# This script starts an instance of Xvfb, the "fake" X server, runs a
# command with that server available, and kills the X server when
# done.  The return value of the command becomes the return value of
# this script.

PROGNAME=xvfb-run
SERVERNUM=99
XVFB_RUN_TMPDIR=
AUTHFILE=
ERRORFILE=
STARTWAIT=3
XVFBARGS='-extension GLX -screen 0 640x480x8'
LISTENTCP='-nolisten tcp'
XAUTHPROTO=.

fatal()
{
	echo "$PROGNAME: $*" >&2
	exit 1
}

# display a usage message
usage () {
    [ "$1" = 0 ] || exec >&2
    cat << EOF
$PROGNAME - run COMMAND (usually an X client) in a virtual X server environment

Usage: $PROGNAME [OPTIONS] COMMAND

Valid options are:
-a       --auto-servernum       try to get a free server number
-e FILE  --error-file=FILE      file used to store xauth errors and Xvfb output
                                  (by default is no redirection)
-f FILE  --auth-file=FILE       file used to store auth cookie
                                  (default is to create temporary file)
-n NUM   --server-num=NUM       server number to use
                                  (default: $SERVERNUM)
-l       --listen-tcp           enable TCP port listening in the X server
-p PROTO --xauth-protocol=PROTO X authority protocol name to use
                                  (defaults to xauth default)
-s ARGS  --server-args=ARGS     arguments (other than server number and
                                  -nolisten tcp) to pass to the Xvfb server
                                  (default: $XVFBARGS)
-w DELAY --wait=DELAY           delay in seconds to wait for Xvfb to start
                                  (default: $STARTWAIT)
-h       --help                 display this text and exit
EOF
    [ -n "$1" ] && exit "$1" || exit
}

# find free server number by looking at .X*-lock files in /tmp
find_free_servernum()
{
	local last_display
	last_display=`find /tmp/ -maxdepth 1 -type f -name '.X*-lock' |
		sed -ne 's,^/tmp/\.X\([[:digit:]]\+\)-lock$,\1,p' |
		sort -n |
		tail -1`
	[ -z "$last_display" ] && echo 0 || echo "$((1+last_display))"
}

XVFBPID=
kill_xvfb()
{
	if [ -n "$XVFBPID" ]; then
		kill "$XVFBPID"
		XVFBPID=
	fi
}

# parse command line
ARGS=$(getopt --options +ae:f:hn:lp:s:w: \
       --long auto-servernum,error-file:,auth-file:,help,server-num:,listen-tcp,xauth-protocol:,server-args:,wait: \
       --name "$PROGNAME" -- "$@") || usage
eval set -- "$ARGS"

while :; do
    case "$1" in
	-a|--auto-servernum) SERVERNUM=$(find_free_servernum) ;;
	-e|--error-file) shift; ERRORFILE="$1" ;;
	-f|--auth-file) shift; AUTHFILE="$1" ;;
	-n|--server-num) shift; SERVERNUM="$1" ;;
	-l|--listen-tcp) LISTENTCP="" ;;
	-p|--xauth-protocol) shift; XAUTHPROTO="$1" ;;
	-s|--server-args) shift; XVFBARGS="$1" ;;
	-w|--wait) shift; STARTWAIT="$1" ;;
	-h|--help) usage 0 ;;
	--) shift; break ;;
	*) fatal "unrecognized option: $1" ;;
    esac
    shift
done

[ -n "$*" ] || fatal "need a command to run"

cleanup()
{
	trap - EXIT
	set +e
	if [ -n "$XVFB_RUN_TMPDIR" ]; then
		rm -rf -- "$XVFB_RUN_TMPDIR"
	elif [ -s "$AUTHFILE" ]; then
		if [ -z "$ERRORFILE" ]; then
			XAUTHORITY="$AUTHFILE" xauth remove ":$SERVERNUM"
		else
			XAUTHORITY="$AUTHFILE" xauth remove ":$SERVERNUM" >>"$ERRORFILE" 2>&1
		fi
	fi
	kill_xvfb
	exit "$@"
}

exit_handler()
{
	cleanup $?
}

signal_handler()
{
	cleanup 1
}

trap exit_handler EXIT
trap signal_handler HUP INT QUIT TERM

# check whether fakeroot is requires
if [ -d "/tmp/.X11-unix" -o "$(id -u)" = 0 ]; then
	FAKEROOT=
else
	FAKEROOT='fakeroot --'
fi

# if the user did not specify an X authorization file to use,
# set up a temporary directory to house one
if [ -z "$AUTHFILE" ]; then
	XVFB_RUN_TMPDIR="$(mktemp -d -t "$PROGNAME.XXXXXX")"
	AUTHFILE="$XVFB_RUN_TMPDIR/Xauthority"
	>"$AUTHFILE"
fi

# start Xvfb
MCOOKIE="$(mcookie)"

if [ -z "$ERRORFILE" ]; then
	XAUTHORITY="$AUTHFILE" xauth source - <<__EOF__
add :$SERVERNUM $XAUTHPROTO $MCOOKIE
__EOF__
	XAUTHORITY="$AUTHFILE" $FAKEROOT Xvfb :"$SERVERNUM" $XVFBARGS $LISTENTCP &
else
	XAUTHORITY="$AUTHFILE" xauth source - <<__EOF__ >>"$ERRORFILE" 2>&1
add :$SERVERNUM $XAUTHPROTO $MCOOKIE
__EOF__
	XAUTHORITY="$AUTHFILE" $FAKEROOT Xvfb :"$SERVERNUM" $XVFBARGS $LISTENTCP >"$ERRORFILE" 2>&1 &
fi
XVFBPID=$!
sleep $STARTWAIT

# start the command and save its exit status
set +e
DISPLAY=:"$SERVERNUM" XAUTHORITY="$AUTHFILE" "$@"
RETVAL=$?

kill_xvfb

# return the executed command's exit status
exit $RETVAL

# vim:set ai et sts=4 sw=4 tw=0:
