
# HPUX_ID: @(#)29.1   86/07/04
#          @(#)partition.sh	29.1      86/07/02
 # partition - intelligently package up sharfiles 
#
# Usage:
#  partition [-dV] [-P num] [-O dest] [-N name] [shar-opts] [prefix] - < flist
# or:
#  partition [-dV] [-P num] [-O dest] [-N name] [shar-opts] source-dir ...
# 
# See manual entry partition(1) for complete description of usage.
#
# Author: John Newman, System Software Operation, HP
#         hpfcla!jmn			July 1, 1986
#	(this is an example of the power of scripts, as well as limitations)
#----------------------------------------------------------------------
# Setup
#----------------------------------------------------------------------
tmp1=/tmp/part.A$$
tmp2=/tmp/part.B$$
tmp3=/tmp/part.C$$
DEBUG=0

cleanup="/bin/rm -f $tmp1 $tmp2 $tmp3; exit"
trap "$cleanup" 0 15 
trap "echo 'Aborted...some cleanup may be necessary' 1>&2; $cleanup" 1 2 
#----------------------------------------------------------------------
# Shell functions
#----------------------------------------------------------------------
usage() {
	echo "Usage:"
	echo "  partition [-dV] [-P num] [-O dest] [-N name] \c"
	echo "[sharopts] [prefix] - <file-list"
	echo "or:"
	echo "  partition [-dV] [-P num] [-O dest] [-N name] \c"
	echo "[sharopts] source-dir ..."
	exit
}

abort_err() {
	# Goes to stderr
	echo "error: $1 - aborting" 1>&2
	exit
}

ignore_err() {
	if [ $verbose -gt 0 ]
	then
		echo "$1 - ignoring" 1>&2
	fi
}

warn_big_file() {
	if [ $verbose -gt 0 ]
	then
		echo "\tWarning: shar of $1 ($2)"
		echo "\t         may exceed partition size - try split(1)"
	fi
}

goto_prefix() {
	if [ -n "$prefixdir" ]
	then
		cd $prefixdir
	fi
}

#----------------------------------------------------------------------
# Examine all arguments
#----------------------------------------------------------------------
destdir=""
prefixdir=""
create_dirs=1
verbose=0
shar_opts=""
name_args=""
name_ct=0
read_stdin=0
part_size=32768
sharf_name="sharf"
part_count=0

set -- `getopt "hHO:bcCdD:mN:oP:rstuVv-" $*`

while [ -n "$1" ]
do case $1 in
	-[hH])
		usage;;
	-)
		read_stdin=1
		shift;;
	-N)
		# user wants sharfiles named a particular way
		shift
		if [ -z "$1" ]
		then
			usage
		else
			sharf_name=$1
		fi
		shift;;
	-O)
		# user wants sharfiles created in specific place
		shift;
		if [ -z "$1" ]
		then
			usage
		elif [ ! -d $1 ]
		then
			abort_err "$1: not a directory"
		elif [ ! -w $1 ]
		then
			abort_err "$1: not a writable directory"
		fi
		destdir=$1
		shift;;
	-P)
		# user wants to set his own max-sharfile-size (in Kbytes)
		shift;
		if [ -z "$1" ]
		then
			usage
		else
			part_size=`expr match $1 "\([0-9]*\)"`
			if [ "$part_size" != "$1" ]
			then
				usage
			else
				part_size=`expr $part_size \* 1024`
			fi
		fi
		shift;;
	-V)
		verbose=1
		shar_opts="$shar_opts -v"
		shift;;
	-d)
		# Prevents directories from being created by shar;
		# this means shar must never see dirnames
		# shar will ALWAYS get -d, tho.
		create_dirs=0
		shift;;

	-D)	shift
		shar_opts="$shar_opts -D $1"
		shift;;

	-[bcCmorsuv])
		shar_opts="$shar_opts $1"
		shift;;

	--)	shift;;
	-*)
		usage;;
	*)
		# dir-name as an argument
		name_ct=`expr $name_ct + 1`
		name_args="$name_args $1"
		shift;;
	esac
done

# Check usage forms (1 or 2)
if [ $read_stdin -gt 0 ]
then
	# See Usage, first method
	if [ $name_ct -gt 1 ]
	then
		abort_err "only one prefix directory allowed with '-' option"
	elif [ $name_ct -eq 1 ]
	then
		prefix_dir=$name_args
		if [ ! -d $prefix_dir ]
		then
			abort_err "$prefix_dir: not a directory"
		fi
	fi
elif [ $name_ct -lt 1 ]
then
	usage
fi

# See if we must create a destdir, or use one user has specified
#  also, if user-specified, find out if existing packages are there
#  output from this run must be sequentially numbered.
if [ -z "$destdir" ]
then
	destdir=/tmp/part.`date +'%H.%M.%S'`
	mkdir $destdir
	echo "Sharfiles will be created as $destdir/$sharf_name.*" 1>&2
	part_count=0
else
	# check if we've been run in destdir before
	# and set initial part_count accordingly by finding highest
	# sequence number already existing
	sharf_list=`find $destdir -name "$sharf_name.*" -type f -print`
	oldest=0
	for f in $sharf_list
	do
		sequence=`expr match $f ".*$sharf_name\.\([0-9]*\)"`
		if [ $sequence -gt $oldest ]
		then
			oldest=$sequence
		fi
	done
	if [ $oldest -gt 0 ]
	then
		part_count=$oldest
	fi
	t=`expr $part_count + 1`
	echo "Sharfiles will be created beginning with \c" 1>&2
	echo "$destdir/$sharf_name.$t" 1>&2
fi

# >>>>> Calculate true maximum partition size <<<<<<
# The partition size has to include the overhead that shar(1)
# puts in: there is about 300 bytes constant overhead for each
# sharfile, plus about 660 bytes if there is at least one non-ascii file
# in the sharfile.  For each ascii file, there is minimal overhead
# (at most about 50 bytes plus 1 byte for each line in the file).
# For non-ascii files, the uuencoding increases the size by
# about 40%.  A rule of thumb, then, is to assume that no
# sharfile will be more than 1.5 x (file-sizes) + 1000 bytes in size.
# This yields: max_contents = (2/3)*part_size-667

t=`expr $part_size / 3`
u=`expr 2 \* $t`
max_file_size=`expr $u - 667`

if [ $verbose -gt 0 ]
then
	echo "Maximum partition size: $part_size bytes"
	if [ $read_stdin -gt 0 ]
	then
		echo "Prefix directory is $prefixdir"
	else
		echo "Source directories: $name_args"
	fi
	echo "Shar options used: $shar_opts"
	echo "---------------------------------------------"
fi

#//////// D E B U G - I N F O /////////////////////
if [ $DEBUG -gt 0 ]
then
	echo "destdir= $destdir"
	echo "prefixdir= $prefixdir"
	echo "create_dirs= $create_dirs"
	echo "verbose= $verbose"
	echo "shar_opts= $shar_opts"
	echo "name_args= $name_args"
	echo "name_ct= $name_ct"
	echo "read_stdin= $read_stdin"
fi
#//////////////////////////////////////////////////
#----------------------------------------------------------------------
# Check that each source file exists and is readable;
#	move names to a tmpfile; if it's a directory, generate
#	the path names of files in it (if -d option present).
#----------------------------------------------------------------------
# Need the function so we can recurse on directories (which we need to do
#	in order to get size information to partition with...
#	Output is ll's of the files
testfile() {
	if [ -d $1 ]
	then
		if [ \( $create_dirs -gt 0 \) -a \( $1 != "." \) ]
		then
			/bin/ll -d $1
		fi
	elif [ ! -f $1 ]
	then
		ignore_err "$1: not a regular file or directory"
	elif [ ! -r $1 ]
	then
		ignore_err "$1: not readable"
	else
		# "return value"
		/bin/ll $1 
	fi
}
# End of Function 

#----------------------------------------------------------------------
# Stage I
#----------------------------------------------------------------------
# Do the first stage in processing the input: generate
# ll's of all files

(
# This is so 2nd stage (awk) can have an argument passed in (1st input line)
echo $max_file_size

goto_prefix
if [ $read_stdin -gt 0 ]
then
	while read suffix
	do
		# does not recurse on directories; user must do this himself
		testfile $suffix
	done
else
	find $name_args -print > $tmp3
	for file in `cat $tmp3`
	do
		testfile $file
	done
fi
) |

#----------------------------------------------------------------------
#  Stage II
#----------------------------------------------------------------------
# run awk on ll's, calcuate partitions
(
awk '\
	BEGIN { cumsize = 0; toggle = 0; filecount=0; }\
	{\
		if(toggle==0) {\
			partsize=$1;\
			toggle=1;\
		} else if((cumsize+$5)>partsize) {\
			printf("@@");\
			if(filecount==1) printf("@");\
			printf("\n");\
			cumsize=$5;\
			filecount=1;\
			print $(NF);\
		} else {\
			cumsize=cumsize+$5;\
			filecount++;\
			print $(NF);\
		}\
	}\
	END {\
		printf("@@");\
		if(filecount==1) printf("@");\
		printf("\n");\
	}'
) |

#----------------------------------------------------------------------
#  Stage III
#----------------------------------------------------------------------
# gather up partition lists and create the sharfiles;
# part_count was initialized when arguments were checked
(
goto_prefix
while read file
do
	if [ \( $file = "@@" \) -o \( $file = "@@@" \) ]
	then
		# This is marker from awk that new partition begins
		if [ -s $tmp2 ]	
		then
			part_count=`expr $part_count + 1`
			dest=$destdir/$sharf_name.$part_count
			if [ $verbose -gt 0 ]
			then
				echo "$dest:"
			fi
			# Here's where shar really gets called
			# shar gets -d option always; if we don't
			# want any directories created, there won't
			# be any in $tmp2.  We do it this way so
			# shar will not recurse on directories; instead
			# we do.
			shar $shar_opts -d `cat $tmp2` > $dest
			if [ $file = "@@@" ]
			then
				# single-file partition; may exceed limit
				warn_big_file `cat $tmp2` $dest
			fi
			if [ $verbose -gt 0 ]
			then
				echo "\t\tPacked Size: \c"
				ll $dest | awk '{printf("%s bytes\n",$5);}'
			fi
			/bin/rm -f $tmp2
			touch $tmp2
		else
			# empty partition?
			echo "?? No files to partition" 1>&2
		fi
	else
		# gather it into a partition's list
		echo $file >> $tmp2
	fi
done
)

# Whew!
