#!/bin/bash
me=${0/*\//} # $(echo $0 | sed -e 's=.*/\([^/][^/]*\)$=\1=')
MYP4BIN=${0%\/$me} # $(echo $0 | sed -e "s=/$me$==") 
if [[ "$MYP4BIN" != /* ]]; then
	MYP4BIN="$PWD/$MYP4BIN"
fi

what=$(uname)
if [ "$what" == 'Darwin' ]; then
	MYPLAT='bin.darwin90x86_64'
	BINTYPE='mac'
  MD5BIN='md5'
else 
	kind=$(uname -p)
	if [ "$kind" == 'x86_64' ]; then
		MYPLAT='bin.linux26x86_64'
		BINTYPE='linux'
  	MD5BIN='md5sum -'
	else 
		if [ "$kind" == 'armv6l' ]; then
			MYPLAT='bin.linux26armhf'
			BINTYPE='pi'
  		MD5BIN='md5sum -'
		fi
	fi
fi
if [ -z $MYPLAT ]; then
	echo 'I have no idea what system you are playing'
	exit 1
fi

P4BIN=$MYP4BIN/p4.$BINTYPE.bin

P4DRAW=$MYP4BIN/p4d
P4DBIN=$P4DRAW.$BINTYPE.bin

P4WEBNAME=p4web.$BINTYPE.bin
P4WEBBIN=$MYP4BIN/$P4WEBNAME

if [ ! -f "$P4BIN" ] || [ ! -f "$P4DBIN" ] || [ "$1" == 'update' ]; then
	echo "Downloading Perforce binaries for $MYPLAT ..."
	sourceSiteP4="ftp://ftp.perforce.com/perforce/r15.1"
	sourceSiteWeb="ftp://ftp.perforce.com/perforce/r12.1"
	which wget > /dev/null
	if [ $? -eq 0 ]; then
		wget -q -O "$P4BIN" "$sourceSiteP4/$MYPLAT/p4"
		wget -q -O "$P4DBIN" "$sourceSiteP4/$MYPLAT/p4d"
		wget -q -O "$P4WEBBIN" "$sourceSiteWeb/$MYPLAT/p4web"
	else
		curl -q -o "$P4BIN" "$sourceSiteP4/$MYPLAT/p4"
		curl -q -o "$P4DBIN" "$sourceSiteP4/$MYPLAT/p4d"
		curl -q -o "$P4WEBBIN" "$sourceSiteWeb/$MYPLAT/p4web"
	fi
	chmod a+x "$P4BIN"
	chmod a+x "$P4DBIN"
	chmod a+x "$P4WEBBIN"
	echo "Done."
	if [ "$1" == 'update' ]; then
		echo 'Update finished. If you meant to update your client use sync instead'
		exit 0
	fi
fi

if [ ! -f "$P4DRAW" ]; then
	echo 'Missing link sighted'
	ln -s "$P4DBIN" "$MYP4BIN/p4d"
	echo "Linking $MYP4BIN/p4d.$BINTYPE.bin to p4d"
fi

# Check when requested only
if [ "$1" == 'check' ]; then
	THIS_REV=9
	NEWER_REV=$((THIS_REV+1))
	which wget > /dev/null
	if [ $? -eq 0 ]; then
		wget -S --spider \
			https://swarm.workshop.perforce.com/files/guest/alan_h_teague/myb4/b4?v=$NEWER_REV \
			> /dev/null 2> /dev/null
		if [ $? -eq 0 ]; then
			echo "A newer version of myb4 is available"
			echo "https://swarm.workshop.perforce.com/files/guest/alan_h_teague/myb4/b4"
		elif [ "$1" == "check" ]; then
			echo "No newer version is available"
			exit 0
		fi
	else
		cur=$(curl -o /dev/null --silent --head --write-out "%{http_code}\n" \
			https://swarm.workshop.perforce.com/files/guest/alan_h_teague/myb4/b4?v=$NEWER_REV)
		if [ "$cur" == "200" ]; then
			echo "A newer version of myb4 is available"
			echo "https://swarm.workshop.perforce.com/files/guest/alan_h_teague/myb4/b4"
		elif [ "$1" == "check" ]; then
			echo "No newer version is available"
			exit 0
		fi
	fi
	if [ "$1" == 'check' ]; then
		exit 0
	fi
fi

P4CONFIG=${P4CONFIG:-.p4config}
rootDir=$($P4BIN -ztag set | \
	egrep "^P4CONFIG=$P4CONFIG" | \
	cut -d "'" -f 2 | \
	sed "s/$P4CONFIG/.p4root/")

RAWP4PORT="rsh:/bin/sh -c \"umask 077 && exec $P4DBIN -i -J off -r '$rootDir'\""

makeGroup () # (group owner subgroups protects)
{
	exists=$($P4BIN groups | grep -e "$1")
	if [ -z "$exists" ]; then
		$P4BIN group -i > /dev/null <<FOOBAR
Group:  $1
MaxResults: unset
MaxScanRows:  unset
MaxLockTime:  unset
Timeout:  999999
PasswordTimeout:  unset
Subgroups: $3
Owners: $2
Users: 
FOOBAR

		if [ ! -z "$4" ]; then
			$P4BIN protect -o | sed "\$,\$ s=\$=  $4=" | $P4BIN protect -i > /dev/null
		fi
	fi
}

addToGroup () # (group userid)
{
	group=$1
	userid=$2
	$P4BIN group -o "$group" | sed "\$,\$ s/\$/  $userid/" | $P4BIN group -i > /dev/null
	$P4BIN user -f -o "$userid" | $P4BIN user -if  > /dev/null
}

delFromGroup () # (group userid)
{
	group=$1
	userid=$2
	$P4BIN group -o "$group" | egrep -v "^[	 ][	 ]*$userid\$" | $P4BIN group -i > /dev/null
	$P4BIN user -d -f "$userid" > /dev/null 2>&1
}

reformatRemote () # oldRemoteName
{
	remoteName=$1
	echo "Converting remote $remoteName to local configuration"
	address=$($P4BIN -ztag remote -o "$remoteName" | egrep '^[.][.][.] *Address')
	address=${address##... Address }

	# XXX Future - These two should be copied over to new remote spec
	# options=$($P4BIN -ztag remote -o "$remoteName" | egrep '^... Options' | \
	#					sed -e 's/... Options //')
	#
	# description=$($P4BIN -ztag remote -o "$remoteName" | egrep '^... Description' | \
	#						sed -e 's/... Description //')
	lls=$($P4BIN -ztag remote -o "$remoteName" | egrep '^... DepotMap' | \
						sed -e 's/... DepotMap[0-9]* //' -e 's=^\(//.*\) //.*=\1=')
	rrs=$($P4BIN -ztag remote -o "$remoteName" | egrep '^... DepotMap' | \
						sed -e 's/... DepotMap[0-9]* //' -e 's=^.* \(//.*\)=\1=')
	IFS=$'\n' lefts=($lls)
	IFS=$'\n' rights=($rrs)
	for(( x=0; x < ${#lefts[*]}; x++)); do
		curStream=$(echo ${lefts[$x]} | sed -e 's=//[^/]*/==' -e 's=/[.][.][.]==')
		mapping=$(echo ${rights[$x]} | sed -e 's=/[.][.][.]==')
		setupRemote
	done
}

setupRemote ()
{
	owner=${address%%@*} # $(echo "$address" | sed -e 's/@.*//')
	if [ ! -z "$owner" ]; then
		address=${address##*@} # $(echo "$address" | sed -e 's/^[^@]*@//')
	else
		owner=$P4USER
		# To catch @ with no userid specified
		address=${address#@} # $(echo "$address" | sed -e 's/^@//')
	fi
	if [ "$owner" == "$address" ]; then
		owner=$P4USER
	fi
	remote=$($P4BIN key "push-$curStream")
	if [ "$remote" != '0' ]; then
		if [ "push-$owner:$address" != "$remote" ]; then
			echo "Changing remote server details for $curStream - NOT IMPLEMENTED"
			exit 1
		fi
	fi
	existing=$($P4BIN remotes | grep "push-$owner:$address")
	if [ -z "$existing" ]; then
		echo 'Creating new mapping'
		$P4BIN -u "$owner" -p "$address" login -s > /dev/null
		if [ $? -ne 0 ]; then
			echo "Please run '$me -u $owner -p $address login'"
			exit 1
		fi
		$P4BIN remote -i > /dev/null <<FOOBAR
RemoteID: push-$owner:$address
Address: $address
Owner: $owner
Description: Push Mapping for $owner @ $address
DepotMap:
	//$streamDepot/$curStream/... $mapping/...
FOOBAR
		$P4BIN key "push-$curStream" "push-$owner:$address" > /dev/null
	else
		if [ "$remote" != '0' ]; then
			echo 'Replacing previous mapping'
			$P4BIN remote -o "push-$owner:$address" | \
				sed \
					-e "s=^[ 	][ 	]*//$streamDepot/$curStream/... .*\$=  //$streamDepot/$curStream/... $mapping/...=" \
					| \
				$P4BIN remote -i > /dev/null 
		else
			echo 'Appending to existing mapping'
			$P4BIN remote -o "push-$owner:$address" | \
				sed "\$,\$ s=\$=  //$streamDepot/$curStream/... $mapping/...=" | \
				$P4BIN remote -i > /dev/null 
			$P4BIN key "push-$curStream" "push-$owner:$address" > /dev/null
		fi
	fi
	$P4BIN -ztag remote -o "push-$owner:$address" | \
		egrep '^[.][.][.] *(Address|DepotMap|Owner)' | \
		sed -e 's/Address/RemoteServer:/' \
			-e 's/Owner/RemoteUserID:/' \
			-e 's/DepotMap[0-9][0-9]*/       /' \
			-e 's/[.][.][.]//'
}

pendingWork ()
{
	work=$($P4BIN -ztag opened "//$P4CLIENT/..." 2> /dev/null | wc -l)
	if [ "$work" -eq 0 ]; then
		work=$($P4BIN -ztag status "//$P4CLIENT/..." 2> /dev/null | wc -l)
	fi
	if [ "$work" -gt 0 ]; then
		echo 'There are pending changes, how do you want to proceed?'
		# One day add in (m) Move changes
		if [ "$submitPending" -eq 1 ]; then
			read -p '(s) Submit changes (r) Revert changes (q) Quit? ' \
				answer
		else
			read -p '(s) Shelve changes for later (r) Revert changes (q) Quit? ' \
				answer
		fi
		case $answer in
		s)
			$P4BIN reconcile -f "//$P4CLIENT/..."
			oops=$($P4BIN reconcile -n -f "//$P4CLIENT/..." 2>&1 | \
						grep -c 'no file(s) to reconcile')
			if [ "$oops" -eq 0 ]; then
				echo 'There is a problem with reconcile. Fix it before continuing'
				exit 1
			fi
			if [ "$submitPending" -eq 1 ]; then
				read -p 'Change description? ' \
					answer
				if [ -z "$answer" ]; then
					$P4BIN submit -d 'Checkpoint work'
				else
					$P4BIN submit -d "$answer"
				fi
			fi
			;;
		r)
			read -p 'New files will be deleted, edits discarded, etc. Are you sure? (y/n) ' \
			answer
			if [ "$answer" == 'y' ]; then
				$P4BIN reconcile -f "//$P4CLIENT/..." > /dev/null 2> /dev/null
				$P4BIN revert -w "//$streamDepot/$curStream/..."
			else
				echo 'Aborting...'
				exit 1
			fi
			;;
		q)
			exit 0
			;;
		esac
	fi
}

fetchCurrent () {
	if [ ! -z "$1" ] && [ "$1" != '-a' ] && [ "$1" != '-u' ]; then
		echo 'Unknown argument'
		exit 1
	fi
	map=$($P4BIN key "push-$curStream")
	if [ -z "$map" ]; then
		echo Please define a map before fetching
		exit 1
	fi
	submitPending=1
	pendingWork
	address=$($P4BIN -ztag remote -o "$map" | egrep '^[.][.][.] *Address')
	address=${address##... Address }
	if [ -z "$address" ]; then
		echo "Map $map has a malformed address"
		exit 1
	fi
	mapping=$($P4BIN -ztag remote -o "$map" | \
		egrep '^[.][.][.] *DepotMap' | \
		egrep "//$streamDepot/$curStream" | \
		awk '{print $4;}')
	if [ -z "$mapping" ]; then
		echo "Map $map has a malformed DepotMap for //$streamDepot/$curStream"
		exit 1
	fi
	owner=$($P4BIN -ztag remote -o "$map" | egrep '^[.][.][.] *Owner' | \
		awk '{print $3;}')
	if [ -z "$owner" ]; then
		owner=$P4USER
	fi
	$P4BIN -u "$owner" -p "$address" login -s > /dev/null
	if [ $? -ne 0 ]; then
		echo "Please run '$me -u $owner -p $address login'"
		exit 1
	fi
	if [ "$owner" != "$P4USER" ]; then
		addToGroup 'alteregos' "$owner"
	fi
	if [ "$1" == '-u' ] || [ "$2" == '-u' ]; then
		unsub='-u'
	else
		unsub=''
	fi
	if [ "$1" != '-a' ] && [ "$2" != '-a' ]; then
		echo "Trying to fetch $mapping from remote $address"
		echo "$P4BIN -u $owner fetch $unsub -v -r $map //$streamDepot/$curStream/..."
		$P4BIN -u "$owner" fetch $unsub -v -r "$map" "//$streamDepot/$curStream/..." # | grep 'Usage'
	else
		echo "Trying to fetch all streams associated with $address"
		echo "$P4BIN -u $owner fetch $unsub -v -r $map"
		$P4BIN -u "$owner" fetch $unsub -v -r "$map" # | grep 'Usage'
	fi
	#cnt=$($P4BIN -u "$owner" fetch -v -r "$map" "$mapping" 2> /dev/null | wc -l)
	$P4BIN sync //... > /dev/null
	if [ "$owner" != "$P4USER" ]; then
		delFromGroup 'alteregos' "$owner"
	fi
}

helpMe () {
	if [ -z "$1" ]; then
		$P4BIN "$@"
		exit $?
	fi
	if [ -z "$2" ]; then
		$P4BIN "$@"
		exit $?
	fi
	if [ "$2" == '-r' ]; then
		$P4BIN help "$3"
		exit $?
	fi
	case $2 in
		dvcs)
			cat <<FOOBAR 
    Using Perforce for Distributed Version Control
        $me init {-c stream} {rootDir}  Create local server

        $me clone -p ADDRESS -f PATH   {rootDir} Create local server based on remote
        $me clone -p ADDRESS -r REMOTE {rootDir} Create local server based on remote

        $me switch                      Which stream am I on?
        $me switch -l                   List all streams

        $me switch -c NEW_STREAM        Create new stream from this one
        $me switch -cm NEW_STREAM       Create new empty stream
        $me switch -mc NEW_STREAM       Create new empty stream

        $me switch STREAM               Switch to STREAM

        $me switch -d STREAM            Hide a stream
        $me switch -r STREAM            Recover a hidden stream
        $me switch -h                   List hidden streams

        $me map REMOTE_URL              Define remote for cur STREAM
            {userid@}{ssl:}host:port://depot_path
        $me push {-a}                   Push current stream to defined remote
        $me fetch {-a}                  Fetch from defined remote to current stream

        $me mergefrom STREAM            Run merge from named stream into current one
        $me checkpoint {description}    Reconcile and submit if needed

        $me server {port}               Start up a network accessible p4d
        $me server stop                 Shutdown the network acccessible p4d
        $me allow {{-a}USER PWD {p}}    Allow user/password to fetch {and push}
        $me disallow {USER}             Disallow user from fetching

        $me web                         Bring up p4web on the local server

        $me check                       Check for a newer version of this script
        $me update                      Download the lastest versions of the binaries
                                        but not the script.

        $me ignores                     Crudely list out matching .p4ignore entries

    Run '$me help' on any of the above commands for more details
FOOBAR
			;;

		init)
			cat <<FOOBAR
    $me init {-c streamDepot} {rootDir}

    Create a personal Perforce server configured for distributed use.
    The optional rootDir argument defines the directory to use for this
    new server installation. The directory will be created. By default,
    this server is non-Unicode and case-sensitive. The optional -c
    argument defines the branching structure to use other than //stream/*

    Note: This wrapper obscures the underlying command
FOOBAR
			;;

		clone)
			cat <<FOOBAR
    $me clone -p host:port -f FILE_SPEC {rootDir}
    $me clone -p host:port -r REMOTE_SPEC {rootDir}

    Create a personal Perforce server configured for distributed use.
    The optional rootDir argument defines the directory to use for this
    new server installation. The directory will be created. By default,
    this server is non-Unicode and case-sensitive.

    You can specify 'MYP4=userid $me clone ...' to specify the
    userid to use on the remote server for authentication.

    The first form calls the underlying p4 clone command and then
    normalizes the system to confirm to this script's conversions.

    The second form calls the underlying p4 clone command and then
    normalizes the system to conform to this script's conventions.

    Note: This wrapper obscures the underlying command
FOOBAR
			;;

		switch)
			cat <<FOOBAR
    $me switch                      Which stream am I on?
    $me switch -l                   List all streams

    $me switch -c NEW_STREAM        Create new stream from this one
    $me switch -cm NEW_STREAM       Create new empty stream
    $me switch -mc NEW_STREAM       Create new empty stream

    $me switch STREAM               Switch to STREAM

    $me switch -d STREAM            Hide a stream
    $me switch -r STREAM            Recover a hidden stream
    $me switch -h                   List hidden streams

    Local branching is controlled using this command. The wrapper calls
    the underlying Perforce switch command for all of this functionality.

    With no arguments, it returns the name of the current stream.

    When called with the following arguments:
      -l returns the list of all streams
      -c creates a new stream of the specified name and populates it based
         on the current stream
      -cm create a new empty stream of the specified name
      -mc create a new empty stream of the specified name
      STREAM switches to the specified stream after shelving any pending
         work.

    The -d argument is used to hide a stream from being used or listed.
    No content is removed from the underlying storage and no remote
    mappings are changed. Use the -r option to restore a hidden stream.
    The -h argument is used to list out the hidden streams.
FOOBAR
			;;
		map)
			cat <<FOOBAR
    $me map
    $me map REMOTE_URL

    With no arguments, this returns the all mappings for the 
    remote server associated with the current stream. 

    The REMOTE_URL defines the mapping from the current stream and
    the specified location on a remote server. This mapping will be
    aggregated with any other mapping targeting the same remote
    server. The REMOTE_URL is in the following format:

            {userid@}{ssl:}host:port://depot_path

    If there is currently a map defined for this stream, then this 
    will replace that mapping with this new one.

    This is not a Perforce command. This is a wrapper around the
    'p4 remote' command.

    You may want to read '$me help remote' and '$me help remotes'
FOOBAR
			;;
		push)
			cat <<FOOBAR
    $me push {-a}                   Push current stream to defined remote
    
    This command looks up the associated remote for this stream and
    invokes the underlying 'p4 push' command with the appropriate
    arguments. If there is pending work, then you will be asked if
    you want to submit or revert the changes. 

    The optional -a argument is used to push all streams associated
    with the remote linked to the current stream.

    Note: This wrapper obscures the underlying command
FOOBAR
			;;
		fetch)
			cat <<FOOBAR
    $me fetch {-a}                  Fetch from defined remote to current stream

    This command looks up the associated remote for this stream and
    invokes the underlying 'p4 fetch' command with the appropriate
    arguments. If there is pending work, then you will be asked if
    you want to submit or revert the changes. 

    The optional -a argument is used to fetch all streams associated
    with the remote linked to the current stream.

    Note: This wrapper obscures the underlying command
FOOBAR
			;;
		mergefrom)
			cat <<FOOBAR
    $me mergefrom STREAM            Run integ from named stream into current one

    This command runs 'p4 merge --from STREAM'. It then runs 'p4 resolve -as'. 
    This does not submit the merged and resolved files, you must do that manually.

    This is not a Perforce command. You may want to look at the help for
    p4 merge.
FOOBAR
			;;
		checkpoint)
			cat <<FOOBAR
    $me checkpoint {description}    Reconcile and submit if needed

    This command runs 'p4 reconcile' and then 'p4 submit -d {description}'

    This is not a Perforce command.
FOOBAR
			;;
		web)
			cat <<FOOBAR
    $me web                          Bring up p4web

    This command will either startup or shutdown a p4web browse instance
    running against the local server. This p4web is not network accessible.

    This is not a Perforce command.
FOOBAR
			;;
		update)
			cat <<FOOBAR
    $me update                       Update the underlying binaries

    This command will retrieve the lastest binaries for p4 p4d and possibly
    p4web. This hides the real 'p4 update' command, use 'p4 sync' instead.

    This is not a Perforce command.
FOOBAR
			;;
		ignores)
			cat <<FOOBAR
    $me ignores                      Crudely lists ignored items 

    This command will run reconcile -n to try to identify any items that
    are being ignored do to some .p4ignore entry.

    This is not yet a Perforce command. 
FOOBAR
			;;
		check)
			cat <<FOOBAR
    $me check                        Check for updates to this script

    This command will check to see if there is a newer version of this 
    script.

    This is not a Perforce command.
FOOBAR
			;;
		server)
			cat <<FOOBAR
    $me server {port}               Start up a network accessible p4d
    $me server stop                 Shutdown the network acccessible p4d

    This command invokes p4d on the specified port or 1666 for use either
    by remote user or by some local client like p4eclipse or p4v. By
    default, the local server is restricted to only the individual user.
    Use the '$me allow' command to enable others to fetch from this 
    networked server.

    The 'stop' argument will shutdown this network accessible server.

    This is not yet a Perforce command.
FOOBAR
			;;
		allow)
			cat <<FOOBAR
    $me allow {-a} {USER PASSWORD {p}}   Allow user/password to fetch {and push}

    This command creates the named user and sets their password accordingly.
    The optional 'p' argument enables the named user to 'push' to this
    server and is not recommended except for testing purposes. 'push' 
		implies '-a' so all streams can be fetched and pushed to/from.

    The -a argument indicates that all streams should be accessible to the
    specified user. Without the -a, only the USER can only fetch from the
    current stream.

    With no arguments, this command lists the users which are currently
    allowed to fetch from this server when a networked server is running.

    This is not yet a Perforce command.
FOOBAR
			;;
		disallow)
			cat <<FOOBAR
    $me disallow {USER}             Disallow user from fetching

    This command removes the named user from the system along with all
    access rules defined for them. This removes all access for the 
    specified USER.

    With no arguments, this command lists the users which are currently
    allowed to fetch from this server when a networked server is running.

    This is not yet a Perforce command.
FOOBAR
			;;
	
		*)
			$P4BIN "$@"
			exit $?
			;;
	esac
	echo "    Run '$me help -r TOPIC' to get the underlying Perforce help"
	exit 0
}

cloneUsage () {
	echo "Usage: $me clone -p HOST:PORT -r REMOTE_SPEC {targetDir}"
	echo "       $me clone -p HOST:PORT -f FILE_SPEC {targetDir}"
	echo "         Use MYP4=userid $me clone ...  to specify remote userid"
	exit 1
}

if [ "$1" == 'help' ] || ( [ "$2" == '-r' ] && [ "$3" == 'help' ] ) ; then
	helpMe "$@"
	# helpMe should never return
fi

if [ "$rootDir" == 'noconfig' ]; then
	baseDir="$PWD"
	if [ "$1" == 'switch' ]; then
		echo 
		exit 0
	fi
	if [ "$1" == 'init' ]; then
		if [ "$2" == '-h' ]; then
			helpMe help init
			# helpMe should never return
		fi
	fi
	streamDepot='stream'
	if [ "$1" != 'init' ]; then
		if [ "$1" != 'clone' ]; then
			if [ "$1" == 'ignores' ]; then
					$P4BIN -vmap=4 reconcile -n ... 2>&1 | grep REJECT
					exit $?
			fi
			$P4BIN "$@"
			exit $?
		else
			if [ -z "$2" ] || [ "$2" != '-p' ] || [ -z "$3" ] || [ -z "$4" ] || [ -z "$5" ] ; then
				cloneUsage # does not return
			fi
			if [ "$2" != '-p' ]; then
				address=$(echo "$2" | sed -e 's=://.*==' -e 's/^p4://')
				mapping=$(echo "$2" | sed -e 's=.*//=//=' -e 's=/[.][.][.]$==')
				if [[ ! "$mapping" =~ // ]]; then
					echo "Usage: $me map {userid@}{ssl:}host:port://depot_path"
					exit
				fi
				owner=${address%%@*} # $(echo "$address" | sed -e 's/@.*//')
				if [ ! -z "$owner" ]; then
					address=${address##*@} # $(echo "$address" | sed -e 's/^[^@]*@//')
				else
					owner=$P4USER
					# To catch @ with no userid specified
					address=${address#@} # $(echo "$address" | sed -e 's/^@//')
				fi
				if [ "$owner" == "$address" ]; then
					owner=$P4USER
				fi
				streamArg=''
				remoteSpec=''
				P4USER=$owner
			else
				if [ -z "$MYP4" ]; then
					P4USER=$USER
				else
					P4USER=$MYP4
				fi
				streamArg=''
				address=$3
				remoteSpec=''
				mapping=''
				if [ "$4" == '-r' ]; then
					remoteSpec=$5
					mapping=''
				else
					if [ "$4" == '-f' ]; then
						remoteSpec=''
						mapping=$5
					else
						cloneUsage # does not return
					fi
				fi
			fi
			$P4BIN -u "$P4USER" -p "$address" login -s > /dev/null
			if [ $? -ne 0 ]; then
				echo "Please run '$me -u $P4USER -p $address login"
				exit 1
			fi
			if [ ! -z "$6" ]; then
				baseDir="$PWD/$6"
				if [ -d "$baseDir" ]; then
					echo 'That directory already exists'
					exit 1
				fi
			else
				baseDir="$PWD"
			fi
		fi
	else
		if [ -z "$MYP4" ]; then
			P4USER=$USER
		else
			P4USER=$MYP4
		fi
		if [ "$2" == '-c' ]; then
			if [ -z "$3" ]; then
				echo 'Missing stream specification'
				exit 1
			fi
			streamDepot=$(echo "$3" | sed -e 's=^.*//==' -e 's=/.*==')
			streamArg="-c $3"
			baseDir="$4"
		else
			streamArg=''
			baseDir="$2"
		fi
		if [ ! -z "$baseDir" ]; then
			baseDir="$PWD/$baseDir"
			if [ -d "$baseDir" ]; then
				echo 'That directory already exists'
				exit 1
			fi
		else
			baseDir="$PWD"
		fi
		address=''
		mapping=''
	fi
	echo 'Initializing...'
	rootDir="$baseDir/.p4root"
  if [ -d "$rootDir" ]; then
		echo "Perforce storage already maps to $rootDir"
		exit 1
	fi
		
	if [ "$1" == 'init' ]; then
  	echo "$P4BIN -d $baseDir -u $P4USER init -C0 -n $streamArg"
  	$P4BIN -d "$baseDir" -u "$P4USER" init -C0 -n $streamArg # > /dev/null 2>&1
		cd "$baseDir"
		if [ $? -ne 0 ]; then
			echo "Failed to create instance in $baseDir"
			exit 1
		fi
	else
		if [ -z "$remoteSpec" ]; then
			echo "$P4BIN -d $baseDir -u $P4USER clone -p $address -f $mapping"
			$P4BIN -d "$baseDir" -u "$P4USER" clone -p "$address" -f "$mapping"
			if [ $? -ne 0 ]; then
				echo 'Aborting'
				exit 1
			fi
			# clone using a depot path creates a remote named origin
			remoteSpec='origin'
		else
			echo "$P4BIN -d $baseDir -u $P4USER clone -p $address -r $remoteSpec"
			$P4BIN -d "$baseDir" -u "$P4USER" clone -p "$address" -r "$remoteSpec"
			if [ $? -ne 0 ]; then
				echo 'Aborting'
				exit 1
			fi
		fi
		cd "$baseDir"
		reformatRemote origin
		$P4BIN remote -d origin
	fi
	sed -e 's/ -i -J off -r / -i -vdvcs=5 -J off -r /' .p4config > .tmpconfig
	mv -f .tmpconfig .p4config
	$P4BIN configure set "serviceUser=$P4USER" > /dev/null
	$P4BIN configure set monitor=0 > /dev/null
	rm -f .p4root/p4_log.txt .p4root/journal
	rmdir .p4root/backups

	# Setup for potential ssl network server
  mkdir .p4root/.p4ssldir
  chmod u+rwx .p4root/.p4ssldir
  chmod go-rwx .p4root/.p4ssldir
	P4SSLDIR="$baseDir/.p4root/.p4ssldir" $P4DBIN -Gc -r .p4root

	seed="$(openssl rand -base64 11) $(date)"
	userpwd=$(echo "$seed" | $MD5BIN | head -c11)
	echo "P4PASSWD=$userpwd" >> .p4config
	$P4BIN passwd -P "$userpwd" > /dev/null

	# Create fetch/push/alterego groups
	makeGroup alteregos "$P4USER" ''
	addToGroup alteregos "$P4USER"
	makeGroup fetch-group "$P4USER" alteregos
	makeGroup push-group "$P4USER" alteregos

	$P4BIN protect -i > /dev/null <<FOOBAR
Protections:
	read group fetch-group * //$streamDepot/...
	write group push-group * //$streamDepot/...
	super group alteregos * //...
FOOBAR

	exit 0
fi

if [ "$1" == 'ignores' ]; then
		$P4BIN -vmap=4 reconcile -n ... 2>&1 | grep REJECT
		exit $?
fi

if [ ! -d "$rootDir" ]; then
	$P4BIN "$@"
	exit $?
fi

curStream=$($P4BIN switch | sed -e 's/ [*]//')
P4USER=$($P4BIN set P4USER | sed -e 's/^P4USER=//' -e 's/ .config.$//')
P4CLIENT=$($P4BIN set P4CLIENT | sed -e 's/^P4CLIENT=//' -e 's/ .config.$//')
streamDepot=$($P4BIN client -o | egrep '^Stream' | sed -e 's=^Stream:.*//==' -e 's=/.*==')

case $1 in
	init)
		if [ "$2" == '-h' ]; then
			helpMe help init
		fi
		echo "Already within a Perforce storage structure: $rootDir"
		exit 1
		;;

	web)
		if [ ! -f "$P4WEBBIN" ]; then
			echo 'Missing optional Perforce server binary'
			echo "Get p4web from $MYPLAT and name it p4web.$BINTYPE.bin"
			echo "in directory $MYP4BIN. Remember to chmod a+x"
			exit 1
		fi
		cur=$(pgrep -f "$P4WEBNAME.*$rootDir")
		if [ -z "$cur" ]; then
			$P4WEBBIN -b -l -w 8080 -p "$RAWP4PORT" > /dev/null &
			if [ "$what" == 'Darwin' ]; then
				open http://localhost:8080
			fi
			echo "Run '$me web' again to shut it down"
		else
			echo 'Shutdown p4web'
			killall "$P4WEBNAME"
		fi
		exit 0
		;;

	map)
		# p4 map - return current mapping
		# p4 map {userid@}{ssl:}host:port://:depot_path - map current stream with remote location
		if [ ! -z "$2" ]; then
			address=$(echo "$2" | sed -e 's=://.*==' -e 's/^p4://')
			mapping=$(echo "$2" | sed -e 's=.*//=//=' -e 's=/[.][.][.]$==')
			if [[ ! "$mapping" =~ // ]]; then
				echo "Usage: $me map {userid@}{ssl:}host:port://depot_path"
				exit
			fi
			setupRemote
		else
			map=$($P4BIN key "push-$curStream")
      if [ "$map" != '0' ]; then
				$P4BIN -ztag remote -o "$map" | \
					egrep '^[.][.][.] *(Address|DepotMap|Owner)' | \
					sed -e 's/Address/RemoteServer:/' \
						-e 's/Owner/RemoteUserID:/' \
						-e 's/DepotMap[0-9][0-9]*/       /' \
						-e 's/[.][.][.]//'
			else
				echo 'No mapping defined for this stream'
			fi
		fi
		exit 0
		;;

	fetch)
		fetchCurrent "$2" "$3"
		;;

	push)
		map=$($P4BIN key "push-$curStream")
		if [ -z "$map" ]; then
			echo Please define a map before fetching
			exit 1
		fi
		submitPending=1
		pendingWork
		address=$($P4BIN -ztag remote -o "$map" | egrep '^[.][.][.] *Address')
		address=${address##... Address }
		if [ -z "$address" ]; then
			echo "Map $map has a malformed address"
			exit 1
		fi
		mapping=$($P4BIN -ztag remote -o "$map" | \
			egrep 'DepotMap' | 
			egrep "//$streamDepot/$curStream" | \
			awk '{print $3;}')
		if [ -z "$mapping" ]; then
			echo "Map $map has a malformed DepotMap for //$streamDepot/$curStream"
			exit 1
		fi
		owner=$($P4BIN -ztag remote -o "$map" | egrep '^[.][.][.] *Owner' | \
			awk '{print $3;}')
		if [ -z "$owner" ]; then
			owner=$P4USER
		fi
		$P4BIN -u "$owner" -p "$address" login -s > /dev/null
		if [ $? -ne 0 ]; then
			echo "Please run '$me -u $owner -p $address login'"
			exit 1
		fi
		if [ "$owner" != "$P4USER" ]; then
			addToGroup 'alteregos' "$owner"
		fi
		if [ "$2" != '-a' ]; then
			echo "$P4BIN -u $owner push -r $map -v //$streamDepot/$curStream/..."
			$P4BIN -u "$owner" push -r "$map" -v "//$streamDepot/$curStream/..."
		else
			echo "$P4BIN -u $owner push -r $map -v"
			$P4BIN -u "$owner" push -r "$map" -v 
		fi
		if [ "$owner" != "$P4USER" ]; then
			delFromGroup 'alteregos' "$owner"
		fi
		exit 0
		;;

	disallow)
		# user
		if [ -z "$2" ]; then
			cnt=$($P4BIN -ztag group -o "fetch-$curStream-group" | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };' | wc -l)
			if [ "$cnt" -gt 0 ]; then
				echo "The following users are allowed to fetch from $curStream:"
				$P4BIN -ztag group -o "fetch-$curStream-group" | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };'
				echo
			fi
			cnt=$($P4BIN -ztag group -o fetch-group | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };' | wc -l)
			if [ "$cnt" -gt 0 ]; then
				echo 'The following users are allowed to fetch from any stream:'
				$P4BIN -ztag group -o fetch-group | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };'
				echo
			fi
			cnt=$($P4BIN -ztag group -o push-group | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };' | wc -l)
			if [ "$cnt" -gt 0 ]; then
				echo 'The following users are allowed to push:'
				$P4BIN -ztag group -o push-group | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };'
				echo
			fi
			exit 0
		fi
		if [ "$2" != "$P4USER" ]; then
			for g in $($P4BIN groups); do
				delFromGroup "$g" "$2"
			done
			echo "User $2 can no longer fetch when networked"
		else
			echo 'You cannot remove yourself'
		fi
		numPushers=$($P4BIN -ztag group -o push-group | egrep -c -e ' Users[0-9]')
		if [ "$numPushers" -eq 0 ]; then
			$P4BIN configure set server.allowpush=1 > /dev/null
		fi
		exit 0
		;;

	allow)
		# user password - enable fetch by user
		if [ -z "$2" ]; then
			cnt=$($P4BIN -ztag group -o "fetch-$curStream-group" | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };' | wc -l)
			if [ "$cnt" -gt 0 ]; then
				echo "The following users are allowed to fetch from $curStream:"
				$P4BIN -ztag group -o "fetch-$curStream-group" | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };'
				echo
			fi
			cnt=$($P4BIN -ztag group -o fetch-group | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };' | wc -l)
			if [ "$cnt" -gt 0 ]; then
				echo 'The following users are allowed to fetch from any stream:'
				$P4BIN -ztag group -o fetch-group | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };'
				echo
			fi
			cnt=$($P4BIN -ztag group -o push-group | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };' | wc -l)
			if [ "$cnt" -gt 0 ]; then
				echo 'The following users are allowed to push:'
				$P4BIN -ztag group -o push-group | egrep '^[.][.][.] Users' | awk '{ print "\t", $3; };'
				echo
			fi
			exit 0
		fi
		if [ "$2" == '-a' ]; then
			allAccess=1
			useUser=$3
			usePassword=$4
			pushFlag=$5
		else
			allAccess=0
			useUser=$2
			usePassword=$3
			pushFlag=$4
		fi
		if [ "$useUser" == "$P4USER" ]; then
			echo 'You are already in the group'
			exit 0
		fi
		if [ -z "$usePassword" ]; then
			echo "You must specify a password for $useUser to use when fetching"
			seed="$(openssl rand -base64 11) $(date)"
			userpwd=$(echo "$seed" | $MD5BIN | head -c11)
			echo "Try: $me allow $useUser $userpwd"
			exit 1
		fi
		if [ "$allAccess" -eq 1 ]; then
			addToGroup 'fetch-group' "$useUser"
		  echo "User $useUser can fetch any stream using password \"$usePassword\""
		else
			makeGroup "fetch-$curStream-group" "$P4USER" alteregos "read group fetch-$curStream-group * //$streamDepot/$curStream/..."
			addToGroup "fetch-$curStream-group" "$useUser"
		  echo "User $useUser can fetch from $curStream using password \"$usePassword\""
		fi
		if [ ! -z "$pushFlag" ]; then
			addToGroup 'push-group' "$useUser"
			echo User can also push to this server
			$P4BIN configure set server.allowpush=3 > /dev/null
		fi
		$P4BIN passwd -P "$usePassword" "$useUser" > /dev/null
		exit 0
		;;

	server)
		# server stop
		if [ "$2" == 'stop' ]; then
			if [ ! -f "$rootDir/myPort" ]; then
				echo 'No server is running'
				exit 0
			fi
			usePort=$(cat "$rootDir/myPort")
			$P4BIN -p "$usePort" admin stop > /dev/null 2> /dev/null
			echo "Stopped server on $usePort"
			rm -f "$rootDir/myPort"
			exit 0
		fi

		# server {port}
		myip=$(ifconfig | grep inet | grep broadcast | awk '{ print $2; }')
		if [ -f "$rootDir/myPort" ]; then
			whichPort=$(cat "$rootDir/myPort")
			$P4BIN -p "$whichPort" help > /dev/null 2> /dev/null
			if [ $? -eq 0 ]; then
				if [ -z "$2" ]; then
					if [ "$whichPort" == ':1666' ]; then
						echo "Server is already running on $whichPort"
					else
						echo "Server is already running on $whichPort, not on :1666"
					fi
				else
					if [ "$whichPort" == "$2" ]; then
						echo "Server is already running on $whichPort"
					else
						echo "Server is already running on $whichPort, not on $2"
					fi
				fi
				if [ -z "$myip" ]; then
					echo "Fetchers should use \"$me map localhost$whichPort://$streamDepot/$curStream\""
				else
					echo "Fetchers should use \"$me map ${myip}$whichPort://$streamDepot/$curStream\""
				fi
				exit 0
			fi
		else
			if [ -z "$2" ]; then
				whichPort=':1666'
			else
				whichPort="$2"
			fi
			$P4BIN -p "$whichPort" help > /dev/null 2> /dev/null
			if [ $? -eq 0 ]; then
				echo "Some server is already running on $whichPort, aborting"
				exit 1
			fi
		fi
		
		echo "$whichPort" > "$rootDir/myPort"
		P4SSLDIR="$rootDir/.p4ssldir" $P4DBIN -r "$rootDir" -vdvcs=5 -J off -p "$whichPort" -d > /dev/null 2> /dev/null
		echo "Stop server by running: $me server stop"
		if [ -z "$myip" ]; then
			echo "Fetchers should use \"$me map localhost$whichPort://$streamDepot/$curStream\""
		else
			echo "Fetchers should use \"$me map ${myip}$whichPort://$streamDepot/$curStream\""
		fi
		exit 0
		;;
	checkpoint)
		$P4BIN reconcile -f "//$P4CLIENT/..."
		oops=$($P4BIN reconcile -n -f "//$P4CLIENT/..." 2>&1 | \
			grep -c 'no file(s) to reconcile')
		if [ "$oops" -eq 0 ]; then
			echo 'There is a problem with reconcile. Fix it before continuing'
			exit 1
		fi
		cnt=$($P4BIN -ztag opened "//$P4CLIENT/..." 2>&1 | wc -l)
		if [ "$cnt" -gt 0 ]; then
			if [ -z "$2" ]; then
				$P4BIN submit -d 'Auto-checkpoint'
			else
				$P4BIN submit -d "$2"
			fi
		fi
		exit 0
		;;
	switch)
		# switch reconciles and shelves by default
		if [ "$2" == '-h' ]; then
			 $P4BIN -ztag keys -e 'stream-*' | egrep '^[.].*key ' | sed -e 's/^....key stream-//' -e 's/$/ (hidden)/'
			exit 0
		fi
		if [ "$2" == '-d' ]; then
			if [ "$3" == "$curStream" ]; then
				echo You cannot hide the ground under your feet, switch to another stream first
				exit 1
			fi
			if [ -z "$3" ]; then
				echo Please specify which stream to hide
				exit 1
			fi
			echo "Hiding stream $3, use -r to recover it"
			stream=$($P4BIN stream -o "//$streamDepot/$3" | egrep -v '^Owner:')
			$P4BIN key "stream-$3" "$stream" > /dev/null
			$P4BIN stream -d "//$streamDepot/$3" > /dev/null
			exit $?
		fi
		if [ "$2" == '-r' ]; then
			if [ "$3" == "$curStream" ]; then
				echo Wherever you are, there you are. Recoved what was never hidden
				exit 1
			fi
			if [ -z "$3" ]; then
				echo Please specify which stream to recover
				exit 1
			fi
			exists=$($P4BIN switch -l | egrep "^$3\$")
			if [ ! -z "$exists" ]; then
				echo "$3 isn't hidden, no need to recover"
				exit 1
			fi
			exists=$($P4BIN key "stream-$3")
			if [ -z "$exists" ]; then
				echo "$3 never existed"
				exit 1
			fi
			cat <<FOOBAR | $P4BIN stream -i > /dev/null
$exists
Owner: $P4USER
FOOBAR
			echo "Restored $3 and switched to it"
			$P4BIN switch "$3"
			$P4BIN key -d "stream-$3" > /dev/null
			exit $?
		fi
		if [ "$2" == '-c' ] || [ "$2" == '-mc' ] || [ "$2" == '-cm' ]; then
			exists=$($P4BIN key "$3-stream")
			if [ "$exists" != '0' ]; then
				echo "Seems that $3 is hidden, recover using $me switch -r $3"
				exit 1
			fi
		fi
		$P4BIN "$@"
		exit $?
		;;
	mergefrom)
		fromStream="$2"
		if [ -z "$fromStream" ]; then
			echo 'Please specify the stream from which to merge'
			exit 1
		fi
		if [ "$fromStream" == "$curStream" ]; then
			echo 'Mobius streams are not supported'
			exit 0
		fi
		stream_client=$($P4BIN switch -l | egrep "^$fromStream$")
		if [ -z "$stream_client" ]; then
			echo "Stream $2 does not exist"
			exit 1
		fi

		submitPending=1
		pendingWork

		# Simple merge, perhaps
		$P4BIN merge --from "$fromStream"
		opened=$($P4BIN -ztag opened "//$P4CLIENT/..." 2> /dev/null | wc -l)
		resolve=$($P4BIN -ztag resolve -n "//$P4CLIENT/..." 2> /dev/null | wc -l)
		if [ "$resolve" -gt 0 ]; then
			$P4BIN resolve -am
		fi
		if [ "$opened" -gt 0 ]; then
			echo 'Assuming no issues, you should now submit...'
		else
			echo 'No merge was necessary'
		fi
		exit 0
		;;

	*)
		$P4BIN "$@"
		exit $?
		;;
esac