V10/cmd/odist/pax/ship/shipout

:
# @(#)shipout (ulysses!{dgk,gsf}) 01/31/91
#
# shipout [ options ... ] [ name ... ] [ tool ... ]
#
# ship software to recipient using system|user info in $SHIPINFO
#
# options -- + turns the corresponding option off
#
#	-b		ship bases with deltas
#	-c		don't generate tool closure
#	-d		don't ship deltas
#	-i		list info on specified recipient(s) only
#	-k		mark recipient(s) as having received tool(s)
#	-l file		list of people to ship to
#	-m		don't send shipment manifest mail message
#	-n		show but don't execute
#	-o		set shipment ownership to shipper
#	-p name		next argument is a recipient to ship to
#	-s		don't send shipment support files
#	-t		don't execute but show total shipment
#	-u [[yy]mm]dd	ignore db info for date pattern
#	-v yymmdd	override current date stamp
#	-C files	add to default crate file list
#	-D secs		delay in seconds between sends
#	-F		force db override
#	-S files	add to default support file list
#
# name -- recipient address
#
#	machine!user	uucp address
#	host:directory	rcp address
#	*%compress	generates compressed cpio archive on stdout
#	*%list		generates shipment file list
#	*%pull		generates sh script with datakit pull's
#	*%push		generates sh script with datakit push's
#
# the message file is evaluated by the shell and the following variables
# are predefined by ship:
#
#	f			list of expanded file names
#	name			name of shipee
#

umask 02
PATH=:$PATH

SHIPSLOG=${SHIPSLOG:-shipslog}
SHIPINFO=${SHIPINFO:-$SHIPSLOG/info}
SHIPFILES="README shipin shipout"
SHIPMENT=*[0-9][0-9][0-9][0-9][0-9][0-9]
SHIPPER=${SHIPPER:-${USER:-${LOGNAME:-${HOME##*/}}}}
SHIPSPOOL=${SHIPSPOOL:-/usr/spool/uucppublic}
CRATEFILES="items message promo release report"
TMP=${TMPDIR:=/tmp}/ship$$
FROMSYS=$((uname -n || hostname || cat /etc/whoami) 2>/dev/null)

test -f shipinit && . ./shipinit

function errexit
{
	# print out an error message on unit 2 and exit
	print -u2 - "$command: $@"
	exit 1
}

function warning
{
	print -u2 - "$command: warning: $@"
}

function cleanup # exitcode
{
	db_done
	rm -f $TMP.?
	exit $1
}

db_data=
db_db=
db_key_base=
db_key_delta=
db_status=
db_style=

#
# send request to dbm server
# status returned in db_status
# data returned in db_data
#

function db_request # request
{
	print -p "$@"
	read -p db_status db_data
	case $db_debug in
	?*)	print -u2 DB: "$@": $db_status $db_data ;;
	esac
	case $db_status in
	I)	return 0 ;;
	E)	print -u2 $logfile: $db_data; return 1 ;;
	*)	return 1 ;;
	esac
}

#
# initialize db
#

function db_init # machine user
{
	case $db_debug in
	grep)	db_style=grep ;;
	esac
	case $db_style in
	"")	if	(shipdbm) </dev/null >/dev/null 2>&1
		then	db_style=dbm
			logfile=$SHIPSLOG/log
			shipdbm |&
			db_request om$logfile || db_request oc$logfile || errexit $logfile: cannot access log
			db_db=$db_data
		else	db_style=grep
		fi
		;;
	esac
	case $db_style in
	dbm)	first_time=0
		db_request s$db_db
		db_check - $1 $2 shipin "*" base || first_time=1
		;;
	grep)	typeset -L2 dir=$1
		case $logfile in
		?*)	mv $logfile $SHIPSLOG/.tmp.
			sort -r < $SHIPSLOG/.tmp. | sort -m -u +0 -1 > $logfile
			rm -f $SHIPSLOG/.tmp.
			;;
		esac
		logfile=$SHIPSLOG/$dir/$1/$2
		if	test  "" = "$force" -a -r "$logfile"
		then	first_time=0
		else	first_time=1
			case $noexec in
			"")	if	test ! -d "$SHIPSLOG/$dir"
				then	mkdir "$SHIPSLOG/$dir" || logfile=$SHIPSLOG/log
				fi
				if	test ! -d "$SHIPSLOG/$dir/$1"
				then	mkdir "$SHIPSLOG/$dir/$1" || logfile=$SHIPSLOG/log
				fi
				;;
			esac
		fi
		;;
	esac
}

#
# end db interaction
#

function db_done #
{
	case $noexec in
	"")	case $db_style in
		dbm)	case $db_db in
			?*)	db_request c$db_db && db_request q ;;
			esac
			;;
		grep)	case $logfile in
			?*)	mv $logfile $SHIPSLOG/.tmp.
				sort -r < $SHIPSLOG/.tmp. | sort -m -u +0 -1 > $logfile
				rm -f $SHIPSLOG/.tmp.
				;;
			esac
			;;
		esac
		;;
	esac
}

#
# check if < machine user tool version type > was sent
#

function db_check # [ - ] machine user tool version type
{
	typeset k key ks sav x nocheck=
	case $first_time:$force in
	1:*|*:1)	return 1 ;;
	esac
	case $1 in
	-)	shift; nocheck=1 ;;
	esac
	typeset a=$1!$2 n=$3 v=$4 t=$5 m
	case $5 in
	"*")	ks="base delta" ;;
	base)	ks=base ;;
	*)	ks=delta ;;
	esac
	for k in $ks
	do	key=$a,$n,$k
		eval sav=\$db_key_$k
		case $key in
		$sav)	eval set -- \$db_val_$k
			;;
		*)	case $db_style in
			dbm)	if	db_request g$db_db$key
				then	set -- $db_data
				else	continue
				fi
				;;
			grep)	set -- `grep "^$key" $logfile`
				case $# in
				0)	continue ;;
				esac
				shift
				;;
			esac
			eval db_key_$k='$key' db_val_$k='$*'
			;;
		esac
		case $1/$2 in
		$v/$t)	case $undo:$nocheck in
			:*|*:1)	;;
			*)	continue ;;
			esac
			case $3 in
			$undo)	continue ;;
			*)	;;
			esac
			case $v in
			"*")	return 0 ;;
			esac
			;;
		*)	case "$v" in
			"*")	return 0 ;;
			esac
			continue
			;;
		esac
		if	test -d $n
		then	m=$(shipop time $n/$1/$2)
		else	m=$(shipop time $n)
		fi
		case $6 in
		""|$m)	return 0 ;;
		*)	return 1 ;;
		esac
	done
	return 1
}

#
# note that < machine user tool version type > was sent
#

function db_note # machine user tool version type name
{
	typeset k v m
	case $5 in
	base)	k=base ;;
	*)	k=delta ;;
	esac
	if	test -d $3
	then	m=$(shipop time $3/$4/$5)
	else	m=$(shipop time $3)
	fi
	v="$1!$2,$3,$k $4 $5 $date $SHIPPER $6 $m"
	case $db_style in
	dbm)	db_request p$db_db$v ;;
	grep)	print $v >> $logfile ;;
	esac
}

#
# output SHIPMENT stamp for file [current date]
#

function shipstamp # file
{
	typeset -Z2 day month
	typeset -R2 year
	integer mon Jan=1 Feb=2 Mar=3 Apr=4 May=5 Jun=6 \
		Jul=7 Aug=8 Sep=9 Oct=10 Nov=11 Dec=12
	case $# in
	0)	set -- $(date)
		shift 1
		;;
	*)	set -- $(ls -ld $1) 
		while	:
		do	case $# in
			[01])	break ;;
			esac
			case $1 in
			Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)
				case $2 in
				[1-9]|[0-3][0-9])
					case $3 in
					[0-9]*)	break ;;
					esac
					;;
				esac
				;;
					
			esac
			shift
		done
		;;
	esac
	mon=$1
	day=$2
	month=$mon
	case $3 in
	*:*:*)	year=$5
		;;
	*:*)	set -- $(date)
		year=$6
		if	(($mon > $2))
		then	let year=year-1
		fi
		;;
	*)	year=$3
		;;
	esac
	print $year$month$day
}

#
# prepare $tool for shipping -- $tool.000 always created
#

function package
{
	typeset tool=$1
	integer nblocks=${2-20}

	integer size i=0 skip=0
	integer chunksize=nblocks*8*1024
	typeset -RZ3 suffix
	typeset fsize
	print packaging $tool
	rm -f "$tool".???
	fsize=$(wc -c $tool)
	size=${fsize%$tool}
	if	((size <= chunksize))
	then	ln "$tool" "$tool".000
	else	while	(( size > 0))
		do	suffix=$i
			dd if="$tool" of="$tool.$suffix" bs=8k skip=$skip count=$nblocks 2> /dev/null
			let i=i+1 skip=skip+nblocks size=size-chunksize
		done
	fi
	set -- "$tool".???
	case $1 in
	*.\?\?\?)	errexit "$tool: cannot package" ;;
	esac
}

function gencontrol # tool
{
	typeset tool=${1%/*}
	typeset format=${1#$tool/}
	typeset init
	if	test -f $1
	then	if	test $1 -nt $1.000
		then	package $1
		fi
		set -- $1.???
	else	# $SHIPFILES
		print - "cp $date.$SHIPPER/${tool%/*} \$INSTALLROOT/ship" >> $TMP.u
		return
	fi
	tooldir=${tool%%/*}
	{
		cat <<-!
			if	test ! -f "\$INSTALLROOT/ship/$tooldir/items"
			then
		!
		case $format in
		base)	init= ;;
		*)	init=$basetoo ;;
		esac
		case $init in
		"")	cat <<-!
				if	test ! -d "\$INSTALLROOT/ship/$tooldir"
				then	mkdir "\$INSTALLROOT/ship/$tooldir"
				fi
				rm -rf "\$INSTALLROOT/ship/$tool"
				mkdir "\$INSTALLROOT/ship/$tool"
			!
			;;
		esac
		cat <<-!
			if	cat $tool/$format.??? > \$INSTALLROOT/ship/$tool/$format
			then	rm -f $tool/$format.???
			fi
		!
		case $init in
		"")	for i in $CRATEFILES
			do	if	test -s $tool/$i
				then	print - "echo \"$(cat $tool/$i 2>/dev/null)\" > \$INSTALLROOT/ship/$tool/$i"
				elif	test -f $tool/$i
				then	print - "> \$INSTALLROOT/ship/$tool/$i"
				fi
			done
			if	test "" = "$shipper" -a -s $tool/owner
			then	owner=$(<$tool/owner)
			else	owner=$SHIPPER
			fi
			case ${TOSYS##*!} in
			${owner%%!*})	owner=${TOSYS%!*}!$owner ;;
			*)		owner=$TOSYS!$owner ;;
			esac
			print - "echo \"$owner\" > \$INSTALLROOT/ship/$tool/owner"
			;;
		esac
		cat <<-!
			fi
		!
	 } >> $TMP.u
}

function buildscript # machine user
{
	# create mscript to execute as a . script
	typeset tool format
	integer delta=0 first=1 no_shipin
	mscript=$TMP.s tfile=$TMP.f
	case $message in
	?*)	{
		print "cat > $tfile <<!EOF!"
		print "Subject: software shipment"
		if	test -f ship.body
		then	print
			cat ship.body
		else	f=${name#*,}
			l=${name%,*}
			case $f in
			$l)	;;
			*)	print "\

Dear ${name#*,} ${name%,*}:" ;;
			esac
			if	test -f ship.head
			then	cat ship.head
				print
			fi
			if	db_check $1 $2 shipin "*" base
			then	no_shipin=0
			else	no_shipin=1
			fi
			if	((first_time||no_shipin))
			then	print "\

As a first time recipient:

(1) Wait for a \\\`copy succeeded' message from uucp for the file:

	\$user/$FROMSYS/$date.$SHIPPER/manifest

(2) For safety do not run as root.  If you are not running as \$user then:

	RECIPIENT=\$user
	export RECIPIENT

(3) Create a shipment root directory under which all source and binaries
    will be generated.  This should not be a final installation directory.

	INSTALLROOT=<shipment-root-directory>
	test -d \\\$INSTALLROOT || mkdir \\\$INSTALLROOT
	cd \\\$INSTALLROOT
	test -d ship || mkdir ship

(4) Name the uucp shipment spool directory:

	SHIPSPOOL=$uuspool/\$user/$FROMSYS

    however if $uuspool/\$user/$FROMSYS was copied to \\\$SPOOLROOT then:

	SHIPSPOOL=\\\$SPOOLROOT

(5) If your system has att and bsd/ucb universes then in general the tools
    will have more functionality when built in the bsd/ucb universe.  You
    can set the C compiler name and/or flags by:

	CC='hackcc -systype bsd43' CCFLAGS='-g'  # just an example #
	export CC CCFLAGS

(6) Execute the following commands to unpack and install the shipment:

	export SHIPSPOOL
	cp \\\$SHIPSPOOL/$date.$SHIPPER/shipin ship/shipin
	chmod 0755 ship/shipin
	nohup ship/shipin &

(7) Use shipout within \\\$INSTALLROOT/ship to ship to other machines.
    \\\$INSTALLROOT/ship/README contains more detailed information.
"
			else	print "\

As a repeat recipient:

(1) Wait for a \\\`copy succeeded' message from uucp for the file:

	\$user/$FROMSYS/$date.$SHIPPER/manifest

(2) For safety do not run as root.  If you are not running as \$user then:

	RECIPIENT=\$user
	export RECIPIENT

(3) Name the directory under which previous shipments were installed:

	INSTALLROOT=<shipment-root-directory>

(4) If $uuspool/\$user/$FROMSYS was copied to \\\$SPOOLROOT then:

	SHIPSPOOL=\\\$SPOOLROOT
	export SHIPSPOOL

(5) If your system has att and bsd/ucb universes then in general the tools
    will have more functionality when built in the bsd/ucb universe.  You
    can set the C compiler name and/or flags by:

	CC='hackcc -systype bsd43' CCFLAGS='-g'  # just an example #
	export CC CCFLAGS

(6) Execute the following commands to unpack and install the shipment:

	cd \\\$INSTALLROOT
	nohup ship/shipin &

(7) Use shipout within \\\$INSTALLROOT/ship to ship to other machines.
    \\\$INSTALLROOT/ship/README contains more detailed information.
"
			fi
		fi
		typeset -L n='tool        ' d='release     '
		t='type'
		print "\
This distribution contains:

	$n$d$t"
		n=----
		d=-------
		t=----
		print "\
	$n$d$t"
		for tool in "${ship_list[@]}"
		do	format=${tool##*/}
			tool=${tool%/$format}
			test -r $tool && eval _notes_${tool%%/*}=0
			n=${tool%%/*}
			d=${tool##*/}
			case $format in
			base)	t="base" ;;
			$SHIPMENT)	t="$format delta" delta=1 ;;
			*)		errexit "$format: invalid format" ;;
			esac
			print "	$n$d$t"
		done
		print
		if	((delta))
		then	print "\
Unpacking delta shipments requires the new pax command.
"
		fi
		for tool in "${ship_list[@]}"
		do	if	test ! -r $tool
			then	continue
			fi
			format=${tool##*/}
			tool=${tool%/$format}
			if	test -s "$tool/message"
			then	item=${tool%%/*}
				eval x='$'_notes_$item
				case $x in
				0)	print "\
$item notes:
"
					eval _notes_$item=1
					;;
				1)	if	cmp -s "$tool/message" "$item/$format/message"
					then	continue
					fi
					;;
				esac
				cat "$tool/message"
				print
			fi
		done
		if	test -f ship.tail
		then	cat ship.tail
		elif	test -f $HOME/.signature
		then	cat $HOME/.signature
		fi
		print  '!EOF!'
		case $noexec in
		"")	print 'mail $mail < $tfile'
			;;
		*)	print 'print "mail $mail <<!EOF!"'
			print "cat $tfile"
			print  'echo !EOF!'
			;;
		esac
		} > $mscript
		;;
	esac
}

function tosys # machine
{
	# construct return mail address
	typeset IFS=!
	TOSYS=
	for i in $@
	do	TOSYS=$i!$TOSYS
	done
	case $TOSYS in
	$FROMSYS!) TOSYS= ;;
	esac
	TOSYS=$TOSYS$FROMSYS
}

function genshiplist # machine user mail
{
	typeset tool file x n v t need_pax= missing=
	integer i=0 j=0
	lclfiles= rmtfiles=
	db_init $1 $2
	db_check - $1 $2 pax "*" base || need_pax=1
	machine=$1
	user=$2
	mail=$3
	unset ship_list
	for file in $SHIPFILES
	do	if	test ! -r "$file"
		then	continue
		fi
		v=$(shipstamp $file)
		if	db_check $machine $user $file $v base
		then	continue
		fi
		ship_list[i]=$file/$v/base
		i=i+1
	done
	j=i
	for tool in "${tool_list[@]}"
	do	n=${tool%%/*}
		case $need_pax in
		"")	eval t=\$type_$n
			case $t in
			pax)	continue ;;
			esac
			;;
		esac
		v=${tool#*/}
		v=${v%/*}
		t=${tool##*/}
		case $tool in
		*/*/$SHIPMENT)
			if      db_check $machine $user $n $t "*"
			then    case $basetoo in
				?*)	if	db_check $machine $user $n $v base
					then	:
					elif	test -f $n/$v/base
					then	ship_list[i]=$n/$v/base
						i=i+1
					else	print -u2 "$n/$v/base: missing base archive"
						missing=1
					fi
					;;
				*)	case $noexec in
					"")	db_note $machine $user $n $t base $name ;;
					esac
					;;
				esac
			elif	test -f "$n/$v/base"
			then	if	db_check $machine $user $n $v base
				then	case $basetoo in
					"")	continue ;;
					esac
				else	ship_list[i]=$n/$v/base
					i=i+1
					case $basetoo in
					"")	continue ;;
					esac
				fi
			elif	test -f $n/$t/base
			then	ship_list[i]=$n/$t/base
				i=i+1
			else	print -u2 "$n/$t/base: missing base archive"
				missing=1
			fi
			;;
		esac
		if	db_check $machine $user $n $v $t
		then	continue
		fi
		ship_list[i]=$tool
		eval clean_$n=
		i=i+1
	done
	case $missing in
	?*)	exit 1 ;;
	esac
	if	((j>=i))
	then	case $total in
		"")	print -u2 "$mail is up to date" ;;
		*)	print -u2 "\n$mail: up to date" ;;
		esac
		return 0
	else	case $mark in
		?*)	case $user:$noexec in
			?*:)	for tool in "${ship_list[@]}"
				do	x=${tool#*/}
					x=${x%/*}
					db_note $machine $user ${tool%%/*} $x ${tool##*/} $name
				done
				;;
			esac
			return 0
			;;
		esac
		case $total in
		?*)	print -u2 "\n$mail:"
			PS3=''
			select i in ${ship_list[*]}
			do	:
			done </dev/null
			return 0
			;;
		esac
	fi
	return 1
}

function sendcontrol # machine user
{
	# $transport the control file to <user> on given <machine>
	case $transport in
	uucp)	typeset dest=$1!$uupublic/$2/$FROMSYS/$date.$SHIPPER
		print "$rmtfiles
$date.$SHIPPER/unspool
$date.$SHIPPER/manifest" > $TMP.m
		print uucp -r -C $TMP.u $dest/unspool
		case $noexec in
		"")	uucp -r -C $TMP.u $dest/unspool ;;
		esac
		case $SHIPID in
		?*)	print $date.$SHIPPER/id >> $TMP.m
			id="$SHIPPER	SEAL	$(date)	$1!$2"
			print "$1!$SHIPID $id" > $TMP.i
			print uucp -r -C $TMP.i $dest/id
			case $noexec in
			"")	uucp -r -C $TMP.i $dest/id ;;
			esac
			case $noexec in
			"")	print "$id\t$(shipop seal $lclfiles $TMP.u $TMP.m $TMP.i)" >> $SHIPSLOG/seals
				;;
			esac
			;;
		esac
		print uucp -r -C -m -n"$2" $TMP.m $dest/manifest
		case $noexec in
		"")	uucp -r -C -m -n"$2" $TMP.m $dest/manifest ;;
		esac
		;;
	uuto)	print uuto -m $TMP.u $1!$2
		case $noexec in
		"")	uuto -m $TMP.u $1!$2 ;;
		esac
		;;
	esac
	case $noexec in
	?*)	case $SHIPID in
		?*)	print "ID:"
			cat $TMP.i
			;;
		esac
		print "MANIFEST:"
		cat $TMP.m
		print "UNSPOOL:"
		cat $TMP.u
		;;
	esac
}

function doship #
{
	# ship the files
	typeset i j tool file
	integer d
	case "$info" in
	?*)	fixedname=${name%% *}
		print "$fixedname $address $phone $mail $company $project $transport"
		return
	esac
	user=${mail##*!} machine=${mail%!*}
	user=${user%%@*} machine=${machine#*@}
	target=$machine!~/$user/$FROMSYS
	uuspool=$SHIPSPOOL
	case $transport in
	uucp)	uupublic='~'
		;;
	uuto)	uuspool=$uuspoool/receive
		uupublic=$uuspool
		print -u2 $transport: transport not supported for $user
		return
		;;
	*)	print -u2 $transport: unknown transport for $user
		return
		;;
	esac
	if	genshiplist $machine $user $mail
	then	return
	fi
	tosys $machine
	> $TMP.u
	for i in "${ship_list[@]}"
	do	gencontrol "$i"
	done
	buildscript $machine $user
	dtime=0
	for tool in "${ship_list[@]}"
	do	for i in $tool.???
		do	case $i in
			*.\?\?\?)
				d=50
				if	test -f $tool
				then	i=$tool j=$tool.000
				else	# here for $SHIPFILES
					i=${tool%%/*}; j=$date.$SHIPPER/$i
				fi;;
			*.000)	j=$i d=50;;
			*)	j=$i d=100;;
			esac
			dfile=$target/$j
			if	test -f "$i"
			then	lclfiles="$lclfiles $i"
				case $rmtfiles in
				"")	rmtfiles=$j
					;;
				*)	rmtfiles="$rmtfiles
$j"
					;;
				esac
				print	uucp -r $nocopy $PWD/"$i" "$dfile"
				case $noexec in
				"")	uucp -r $nocopy $PWD/"$i" "$dfile"
					dtime=dtime+d
					;;
				esac
			fi
		done
		i=${tool#*/}
		i=${i%/*}
		case $noexec in
		"")	db_note $machine $user ${tool%%/*} $i ${tool##*/} $name ;;
		esac
	done
	if	test -r $mscript
	then	. $mscript
	fi
	sendcontrol $machine $user
}

function dosend # recipient open
{
	# lookup <recipient> in database and transport software to them
	# if <open> is given, the database is re-opened
	addr=$1
	case $addr in
	*:*)	transport=rcp
		mail=$1
		host=${addr%:*}
		machine=$host
		dir=${addr#*:}
		user=${dir%%/*}
		case $user in
		'~'*)	user=${user#~} ;;
		*)	user=$SHIPPER ;;
		esac
		;;
	*!*)	name=${1##*!} transport=uucp
		;;
	*@*)	name=${1%%@*} transport=uucp
		;;
	*%compress|*%list|*%pull|*%push)
		mail=$addr
		transport=${addr##*'%'}
		machine=%$transport
		user=${addr%$machine}
		dir=.
		;;
	*)	transport=db
		;;
	esac
	case $transport in
	uucp)	address= phone= mail=$1 project=
		doship
		return 1
		;;
	db)	typeset -l given match
		case $shipinfo_test in
		"")	if	test ! -f "$SHIPINFO"
			then	errexit "$SHIPINFO: cannot find information file"
			fi
			shipinfo_test=1
			;;
		esac
		case $2 in
		?*)	exec 3< $SHIPINFO ;;
		esac
		given=$1
		while IFS=: read -ru3 name address phone mail company project unused transport comments
		do	match=$name
			case $match in
			\#*)		;;
			$given*)	doship
					return 1
					;;
			esac
		done
		;;
	*)	typeset d i p s
		if	genshiplist $machine $user $mail
		then	return 1
		fi
		tosys $machine
		{
			case $transport in
			compress|list|push)
				;;
			*)	print - "umask 02"
				;;
			esac
			for i in "${ship_list[@]}"
			do	if	test -f $i
				then	d=$i/file
					p=${d%%/*}
					s=${d#${p}/}
					t=$p
					while 	test "$s" != file
					do	if	test "" != "$p"
						then	case $transport in
							compress|list)
								print - "$dir/$p"
								;;
							push)	;;
							*)	print - "if	test ! -d $dir/$p"
								print - "then	mkdir $dir/$p"
								print - "fi"
								;;
							esac
						fi
						p=${p}/${s%%/*}
						s=${d#${p}/}
					done
					i=${i%/*}
					for f in $CRATEFILES
					do	if	test -s $i/$f
						then	case $transport in
							compress|list|push)
								print - "$dir/$i/$f"
								;;
							*)	print - "echo \"$(cat $i/$f 2>/dev/null)\" > $dir/$i/$f"
								;;
							esac
						elif	test -f $i/$f
						then	case $transport in
							compress|list|push)
								print - "$dir/$i/$f"
								;;

							*)	print - "> $dir/$i/$f"
								;;
							esac
						fi
					done
					case $transport in
					compress|list|push)
						if	test -s $i/owner
						then	print - "$dir/$i/owner"
						fi
						;;
					*)	if	test "" = "$shipper" -a -s $i/owner
						then	owner=$(<$i/owner)
						else	owner=$SHIPPER
						fi
						case ${TOSYS##*!} in
						${owner%%!*})	owner=${TOSYS%!*}!$owner ;;
						*)		owner=$TOSYS!$owner ;;
						esac
						print - "echo \"$owner\" > $dir/$i/owner"
						;;
					esac
				fi
			done
			case $transport in
			list|push)
				;;
			*)	print - "rm -f $dir/ship.$date"
				;;
			esac
		} | case $transport:$noexec in
		compress:)
			cat
			;;
		compress:*)
			print - "(pax -wv | compress${mail:+" > $mail"}) <<!"
			cat
			;;
		list:*)	cat
			;;
		pull:*)	print - ': pull shipment from remote dk host'
			print - 'case $# in'
			print - '1|2)	;;'
			print - '*)	echo "Usage: $0 dk-address [remote-INSTALLROOT]" >&2; exit 1 ;;'
			print - 'esac'
			print - 'DK_ADDRESS=$1'
			print - 'DK_INSTALLROOT=${2-.}'
			cat
			;;
		push:*)	print - ': push shipment to remote dk host'
			print - 'case $# in'
			print - '1|2)	;;'
			print - '*)	echo "Usage: $0 dk-address [remote-INSTALLROOT]" >&2; exit 1 ;;'
			print - 'esac'
			print - 'push $1 - ${2-ship} <<"!"'
			cat
			;;
		*:)	cat > $TMP.r
			rcp $TMP.r $host:$dir/ship.$date || exit
			rm -f $TMP.r
			case $rsh in
			"")	for i in rcmd remsh
				do	if	($i $host date) >/dev/null 2>&1
					then	rsh=$i
						break
					fi
				done
				case $rsh in
				"") rsh=rsh ;;
				esac
				;;
			esac
			$rsh $host sh $dir/ship.$date || exit
			;;
		*)	print - "cat '"
			cat
			print - "' > ship.$date"
			print - "rcp ship.$date $host:$dir"
			print - "rm ship.$date"
			print - "rsh $host sh $dir/ship.$date"
			;;
		esac
		for tool in "${ship_list[@]}"
		do	if	test ! -f $tool
			then	i=${tool%%/*}
			else	i=$tool
			fi
			dfile=$mail/$i
			if	test -f "$i"
			then	case $transport in
				compress)
					print -u2 "oops: $i"
					;;
				list|push)
					print - "$i"
					;;
				pull)	case $i in
					*/*)	t=${i%/*} ;;
					*)	t=. ;;
					esac
					print - echo pull '$DK_ADDRESS' '$DK_INSTALLROOT'/ship/$i $t
					print - pull '$DK_ADDRESS' '$DK_INSTALLROOT'/ship/$i $t
					;;
				rcp)	print	rcp "$i" "$dfile"
					case $noexec in
					"")	rcp "$i" "$dfile" || exit ;;
					esac
					;;
				esac
			fi
			i=${tool#*/}
			i=${i%/*}
			case $user:$noexec in
			?*:)	db_note $machine $user ${tool%%/*} $i ${tool##*/} $name ;;
			esac
		done
		case $transport in
		push)	print - '!' ;;
		esac
		return 1
		;;
	esac
	print -u2 "$command: address for $1 not found"
	return 1
}

function closure # tools
{
	tools=
	for tool
	do	if	test ! -d $tool
		then	case $original in
			*" ${tool%/*} "*|*" $tool "*)	warning "${tool%/*}: invalid tool" ;;
			esac
		else	case $tool in
			*/?*)	release=${tool#*/}
				tool=${tool#*/}
				;;
			*)	eval items=\"\$items_$item\"
				case $items in
				"")	release= ;;
				*)	eval release=\$release_$tool ;;
				esac
				;;
			esac
			eval release_${tool%/*}=$release
			tools="$tools $tool"
		fi
	done
	set -- $tools
	#
	# now generate the closure of the top level tools and releases
	#
	tools=
	for tool
	do	old=
		new=$tool
		while :
		do	case $new in
			$old)	break
			esac
			dup=
			for item in $new
			do	# we assume items_* not in environment
				eval items=\"\$items_$item\"
				case $items in
				"")	eval release=\$release_$item
					case $release in
					"")	set -- $item/$SHIPMENT
						shift $#-1
						case $1 in
						"$item/$SHIPMENT") continue ;;
						esac
						release=${1#*/}
						eval release_$item=$release
					esac
					if	test ! -f $item/$release/items
					then	continue
					fi
					case $closure in
					"")	items=$item ;;
					*)	items=$(<$item/$release/items)" $item" ;;
					esac
					eval items_$item=\"$items\"
					;;
				esac
				dup="$dup $items"
			done
			old=$new
			new=
			for item in $dup
			do	eval seen_$item=
			done
			for item in $dup
			do	eval seen=\$seen_$item
				case $seen in
				"")	eval seen_$item=1
					new="$new $item"
				esac
			done
		done
		tools="$tools $new"
	done
}

function undup # tools
{
	tools=
	for item
	do	eval seen_$item=
	done
	for item
	do	eval seen=\$seen_$item
		case $seen in
		"")	eval seen_$item=1
			tools="$tools $item"
		esac
	done
}

command=$0
integer dtime=0 i=0 nrecipient=0 first_time=1 delay=0
info= mark= list= logfile= message=1 nocopy= noexec= format=$SHIPMENT
shipinfo_test= force= shipper= closure=1 undo= date= basetoo=
while	:
do	case	$1 in
	-b)	basetoo=1 ;;
	+b)	basetoo= ;;
	-c)	closure= ;;
	+c)	closure=1 ;;
	-d)	format=base ;;
	+d)	format=$SHIPMENT ;;
	-i)	info=1 delay=0 ;;
	-k)	mark=1 delay=0 ;;
	-l)	shift; list=$1 ;;
	+l)	list= ;;
	-m)	message= ;;
	+m)	message=1 ;;
	-n)	noexec=1 delay=0 ;;
	+n)	noexec= ;;
	-o)	shipper=1 ;;
	+o)	shipper= ;;
	-p)	shift; recipient_list[nrecipient]=$1; nrecipient=nrecipient+1 ;;
	+p)	recipient= ;;
	-s)	SHIPFILES= ;;
	-t)	total=1 noexec=1 delay=0 ;;
	-u)	shift; undo=$1 ;;
	+u)	undo= ;;
	-v)	shift; date=$1 ;;
	-C)	shift; CRATEFILES="$CRATEFILES $1" ;;
	-D)	shift;	delay=$1 ;;
	+D)	delay=0 ;;
	-F)	force=1 ;;
	+F)	force= ;;
	-S)	shift; SHIPFILES="$SHIPFILES $1" ;;
	*[,:!%@]*)recipient_list[nrecipient]=$1;nrecipient=nrecipient+1 ;;
	--)	shift; break ;;
	-*|+*)	print -u2 "Usage: $command [-bcdikmnostfF] [-l recipient-list] [-p recipient] [-u [[yy]mm]dd] [-v yymmdd] [-C crate-file] [-D delay] [-S support-file] [recipient ...] [tool ...]"; exit 2 ;;
	*)	break ;;
	esac
	shift
done
case $date in
$SHIPMENT)	;;
*)		date=$(shipstamp) ;;
esac
case $info in
"")	if	test 0 = "$nrecipient" -a ! -f "$list"
	then	errexit "no recipient(s)"
	fi
	trap 'cleanup $?' EXIT INT TERM
	#
	# generate the tools list
	#
	original=" $* "
	case $# in
	0)	tools=
		for tool in *
		do	set -- $tool/$SHIPMENT
			case $1 in
			"$tool/$SHIPMENT")	;;
			*)		tools="$tools $tool" ;;
			esac
		done
		case $tools in
		"")	errexit "no tools" ;;
		esac
		set -- $tools
		;;
	esac
	closure $*
	case $tools in
	"")	errexit "no 'items' file(s)" ;;
	esac
	tools_usr=$tools
	tools_pax=
	case $closure:$format:$SHIPFILES in
	:*|*:|*:base:*)
		;;
	*)	set -- pax/$SHIPMENT
		case $1 in
		"$tool/$SHIPMENT")
			;;
		*)	closure pax
			tools_pax=$tools
			;;
		esac
		;;
	esac
	undup $tools_pax $tools_usr
	set -- $tools
	for tool in $tools_pax
	do	eval type_$tool=pax
	done
	for tool in $tools_usr
	do	eval type_$tool=usr
	done
	set -- $tools
	for tool
	do	eval tool=$tool/\$release_$tool
		item=
		for f in "$format" base
		do	set -- $tool/$f
			if	test -f "$1"
			then	item=${1##*/}
				break
			fi
		done
		case $item in
		"")	if	test ! -d ${tool%/*}
			then	case $original in
				*" ${tool%/*} "*|*" $tool "*)	warning "${tool%/*}: invalid tool" ;;
				esac
				continue
			fi
			case $format in
			base)	set -- $tool/$SHIPMENT
				if	test -f "$1"
				then	eval release_${tool%/*}=${1##*/}
					tool=${tool%/*}/${1##*/}
					item=base
					warning "${tool}: latest base not crated"
				else	warning "${tool}: base not crated"
					continue
				fi
				;;
			*)	continue
				;;
			esac
			;;
		esac
		case $format in
		"$SHIPMENT"|$item)
			tool=$tool/$item
			;;
		*)	tool=${tool%/*}/$item/base
			if	test ! -f $tool
			then	errexit "${tool%/*}: $item base not crated"
			fi
			;;
		esac
		tool_list[i]=$tool;i=i+1
	done
	;;
*)	typeset -lL16 fixedname
	typeset -L9 address project
	typeset -R12 phone
	typeset -L21 mail
	typeset -L11 company
	;;
esac
if	test ! -d $SHIPSLOG
then	mkdir $SHIPSLOG || errexit "$SHIPSLOG: cannot create log directory"
fi
if	test -f shipop.c -a -x shipop
then	SHIPID=$FROMSYS!$SHIPPER
	case $SHIPFILES in
	?*)	SHIPFILES="$SHIPFILES shipop.c" ;;
	esac
else	SHIPID=
fi
while	:
do	case $undo in
	""|??????*)	break ;;
	*)		undo="?$undo" ;;
	esac
done
nocopy=${nocopy+-c}
flag=1
case $list in
"")	set -s -- "${recipient_list[@]}"
	;;
?*)	if	test ! -r "$list"
	then	errexit "$list: cannot read"
	else	set -s -- "${recipient_list[@]}" $(sed -e '/^#/d' -e 's/[: 	].*//' $list)
	fi
	;;
esac
for recipient
do	if	((dtime))
	then	sleep $dtime
	fi
	if	dosend "$recipient" $flag
	then	flag=
		case $noexec$info in
		"")	print "$name" >> ${tool%/*}/list ;;
		esac
	else	flag=1
	fi
	if	((delay > dtime))
	then	dtime=delay
	fi
done
exit 0