#!/bin/ksh -hp
#
#	"@(#)add_to_install_server.sh 1.8 99/11/09"
#
# Copyright (c) 1992-1999 Sun Microsystems, Inc.  All Rights Reserved. Sun
# considers its source code as an unpublished, proprietary trade secret, and
# it is available only under strict license provisions.  This copyright
# notice is placed here only to protect Sun in the event the source is
# deemed a published work.  Dissassembly, decompilation, or other means of
# reducing the object code to human readable form is prohibited by the
# license agreement under which this code is provided to the user or company
# in possession of this copy.
#
# RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the Government
# is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the
# Rights in Technical Data and Computer Software clause at DFARS 52.227-7013
# and in similar clauses in the FAR and NASA FAR Supplement.

#
# This program copys additional packages within a Product tree to
# an existing net install server.
#

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CleanupExit()
{
	[ "$DEBUG" = "true" ] && set -x

	TurnOffSpinner
	rm -fr $TMPFILES
	echo ""; echo ""
	[ "$1" = "0" ] && echo "Processing completed successfully." || \
		echo "Processing did not complete successfully."
	exit $1
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Usage()
{
	echo "Usage: add_to_install_server [-s] [-p <product_image_path>] <install_server_path>"
	exit 1
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CreateSpinnerProgram() {

cat > $SPINNER << EOF
#!/bin/sh

if [ $# -ge 1 ]
then
 	kill \$1
	exit
fi

state=0

while [ 1 ]
do
	case \$state in
   		0)
   			echo "|\b\c"
   			state=1
   			;;
   		1)
   			echo "/\b\c"
   			state=2
   			;;
   		2)
   			echo "-\b\c"
   			state=3
   			;;
   		3)
   			echo "\\\" "\b\b\c"
   			state=0
   			;;
   	esac
	
   	sleep 1
done
exit 0
EOF

}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TurnOnSpinner()
{
	[ "$DEBUG" = "true" ] && set -x

	if [ -x $SPINNER ]; then
		$SPINNER &
		DIAL_PID=$!
	fi
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TurnOffSpinner()
{
	[ "$DEBUG" = "true" ] && set -x

	[ "$DIAL_PID" != "0" ] && kill $DIAL_PID && DIAL_PID=0
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PrintErr() {
	echo "$@" >&2
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AbsolutePath() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -r  path=$1
	
	if ! valpath -a $path; then
		echo "$(pwd)/$path"
	elif valpath -a $path; then
		echo "$path"
	fi
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ParseArgs() {

	while getopts "ngsp:" opt; do
		case $opt in
			p)	SOURCE="$OPTARG" ;;
			s)	PRODS="true" ;;
			g)	DEBUG="true";;
			n)	NOSPACECHK="true";;
			\?)	Usage;;
			:)	Usage;;
		esac
	done
	
	shift OPTIND-1
	if [ $# -ne 0 ]; then
		TARGET="$*"
	else
		Usage
	fi
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DetermineProductAreas() {

	[ "$DEBUG" = "true" ] && set -x

	# Gather this info from the .cdtoc file
	typeset -r cdtoc="$SOURCE/.cdtoc"

	prodDirs=$(GetProdDirs $cdtoc)
	
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GetProdNames() {

	typeset -r cdtoc=$1
	awk -F= '/PRODNAME/ {print $2}' $cdtoc
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GetProdDirs() {

	typeset -r cdtoc=$1
	############### Use 2nd line of code once RE typo gets fixed
	grep DIR $cdtoc | awk -F= '{print $2}'
	#awk -F= '/PRODDIR/ {print $2}' $cdtoc
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DetermineSpaceNeeded() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -i prodSize=0
	typeset -i totProdSize=0
	typeset -r EASource="$(dirname $ORIGDIR)/EA"

	[ -n "$NOSPACECHK" ] && \
		echo "Skipping disk space check" && echo "" && return

	echo "Checking required disk space..."; echo ""

	# Check EA space requirement.
	typeset -i EASpaceNeeded=$(du -sk $EASource | awk '{print $1}')

	# This function uses the size of a pkg already determined with
	# pkgmk (fast) and it uses 'du' (slowww) to figure the size of
	# pkgs not made with pkgmk. 

	for product in $FINALPRODDIRS; do
		[ ! -d "$SOURCE/$product" ] && \
			PrintErr "Can't find $SOURCE/$product" && \
			CleanupExit 1

		cd $SOURCE/$product

		# Fast way
		awk '$1 ~ /\:/ {  \
			if (NF == 3) { print $3 } 
			else { print $4 }}' */pkgmap > $TMPFILES/sizes.$$

		prodSize=0

		cat $TMPFILES/sizes.$$ | \
		while read size; do
			prodSize=prodSize+size
		done

		# Slow way
		typeset nonABIpkgs=$(GetNonABIpkgs)
		[ -n "$nonABIpkgs" ] && \
			typeset sznonABIpkgs=$(GetDUSize "$nonABIpkgs")

		if [ -n "$nonABIpkgs" ]; then
			listOfProdSizes="$listOfProdSizes $prodSize $sznonABIpkgs"
		else
			listOfProdSizes="$listOfProdSizes $prodSize"
		fi
	done

	for prodSize in $listOfProdSizes; do
		totProdSize=totProdSize+prodSize
	done

	typeset -i KspaceAvailable=$(df -b $TARGET | \
		sed -e '1d' | awk '{print $2}')

	typeset -i totKProdSize=totProdSize*512/1024+EASpaceNeeded

	if (( totKProdSize > KspaceAvailable )); then
		PrintErr "Not enough space available in $TARGET to copy packages"
		PrintErr "$KspaceAvailable Kbytes available" \
			"$totKProdSize Kbytes needed."
		CleanupExit 1
	fi
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GetNonABIpkgs() {
	[ "$DEBUG" = "true" ] && set -x

	grep "^:" */pkgmap | awk -F/ '{print $1}' > $TMPFILES/abipkgs
	\ls > $TMPFILES/allpkgs
	fgrep -v -f $TMPFILES/abipkgs $TMPFILES/allpkgs
	rm $TMPFILES/allpkgs $TMPFILES/abipkgs
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GetDUSize() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -r nonABIpkg=$1
	typeset -i size=0
	typeset -i totalSize=0

	for pkg in $nonABIpkg; do
		size=$(du -s $pkg | awk '{print $1}')
		totalSize=totalSize+size
	done

	echo $totalSize
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CheckSource() {

	[ "$DEBUG" = "true" ] && set -x

	if [ -n "$SOURCE" ]; then
		cd $SOURCE >/dev/null 2>&1
		SOURCE=$(pwd)
	else
		SOURCE=$(dirname $progName)
		cd $SOURCE/../.. >/dev/null 2>&1
		SOURCE=$(pwd)
	fi
		
	# if we find a .cdtoc file then we can assume this is a valid CD
	# to process.
	[ ! -f ".cdtoc" ] && \
		PrintErr "Cannot find the .cdtoc file on $SOURCE" && \
			 CleanupExit 1

	# ADD OTHER CHECKS ONCE THE CD LAYOUT IS COMPLETE

}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HandleUsrSelection() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -i totalProds=0

	DisplayChoiceHeader

	DisplayProducts "no"

	typeset result=$(ProcessResult) || return 1

	typeset -r cdtoc="$SOURCE/.cdtoc"
	typeset -r prodDirs=$(GetProdDirs "$cdtoc")

	for items in $prodDirs; do
		totalProds=totalProds+1
	done

	CheckEntry "$prodDirs" "$totalProds" "$result" || return 1
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CheckEntry() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -r prodDirs=$1
	typeset totalProds=$2
	typeset result=$3

	typeset -i ctr=0
	typeset -i num=0
	typeset -i atLeastOneSelected=0

	for selProd in $prodDirs; do
		ctr=ctr+1
		for num in $result; do
			(( num > totalProds)) && \
				PrintErr "$num is not a valid selection!" && return 1
			(( num == ctr )) && \
				echo $selProd && \
				atLeastOneSelected=1 && \
				break
		done
	done

	(( atLeastOneSelected != 1 )) && PrintErr "$result is not valid" \
		&& return 1

	return 0
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ProcessResult() {

	[ "$DEBUG" = "true" ] && set -x

	#typeset result=$1

	while read result; do
		echo "" >&2
		[ -z "$result" ] && CommonMsg && return 1
		[ -n $(echo $result | grep " |,|\n") >/dev/null 2>&1 ] \ &&
			PrintErr "The numbers must be seperated by a space or comma." \
			CommonMsg && return 1
		[ "$result" = "all" ] && result=$(SelectAllProducts) && break
		alphas=""
		alphas=$(echo $result | awk '/[a-z]|[A-Z]/ {print}')
		[ -n "$alphas" ] && \
			PrintErr "Only numbers are valid" && \
			CommonMsg && return 1
		# OK looks like a valid entry. See if commas are provided
		result=$(echo $result | sed 's/,/ /g')
		break
	done
	echo "$result"
	return 0
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SelectAllProducts() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -i ctr=0
	typeset result

	typeset -r cdtoc="$SOURCE/.cdtoc"
	typeset -r prodDirs=$(GetProdDirs "$cdtoc")

	for i in $prodDirs; do
		ctr=ctr+1
		result="$result $ctr"
	done

	echo "$result"
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CommonMsg() {

	echo "Please enter number(s) or the keyword all" >&2
	echo ""
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DisplayProducts() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -r suppressCtr=$1
	typeset -r cdtoc="$SOURCE/.cdtoc"

	[ -f $TMPFILES/prdNames.$$ ] && rm -f $TMPFILES/prdNames.$$

	GetProdNames "$cdtoc" > $TMPFILES/prdNames.$$
	[ "$suppressCtr" = "yes" ] && cat $TMPFILES/prdNames.$$ >&2 \
	|| cat -n $TMPFILES/prdNames.$$ >&2
	echo "" >&2
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DisplayChoiceHeader() {

	[ "$DEBUG" = "true" ] && set -x

	echo "" >&2
	echo "Enter the number of the product(s) you want to add" >&2
	echo "to $TARGET or 'all' for all products." >&2
	echo "" >&2
	echo "The following are valid products:" >&2
	echo "" >&2
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CheckProducts() {

	[ "$DEBUG" = "true" ] && set -x

	if [ -n "$PRODS" ]; then
		while (( 1 )); do
			FINALPRODDIRS=$(HandleUsrSelection) && break
		done
	else
		typeset -r cdtoc="$SOURCE/.cdtoc"

		FINALPRODDIRS=$(GetProdDirs "$cdtoc")
		FINALPRODNAMES=$(GetProdNames "$cdtoc")
		
		echo "The following Products will be copied to $TARGET:"
		echo ""
		echo "$FINALPRODNAMES"
		echo ""
		echo "If only a subset of products is needed enter Control-C"
		echo "and invoke $progName with the -s option."
		echo ""
	fi
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CheckTarget() {

	[ "$DEBUG" = "true" ] && set -x

	cd $TARGET > /dev/null 2>&1
	TARGET=$(pwd)

	if [ ! -f "$TARGET/.cdtoc" ]; then
		PrintErr "An existing install server cannot be found at $TARGET."
		PrintErr "This tool can only add packages to an install server" \
			"that already exists."
		CleanupExit 1
	else
		# construct target from .cdtoc
		typeset -r tmpTARGET=$(GetProdDirs "$TARGET/.cdtoc")
		TARGET="$TARGET/$tmpTARGET"
	fi
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CopyEADirectory() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -r EASource="$(dirname $ORIGDIR)/EA"
	typeset -r EATarget="$(dirname $TARGET)/EA"

	[ ! -d $EASource ] && return

	echo ""; echo "Copying the Early Access products..."

	[ -d $EATarget ] && rm -fr $EATarget
	mkdir -m 755 $EATarget
	cd $EASource
	find . -print -depth | cpio -pdmu $EATarget
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CopyProducts() {

	[ "$DEBUG" = "true" ] && set -x

	for prod in $FINALPRODDIRS; do
		#typeset pName=$(dirname $prod);pName=$(dirname $pName)
		#pName=$(basename $pName) 
		#echo "Copying $pName packages..."
		[ ! -d $SOURCE/$prod ] && \
			PrintErr "Can't find $SOURCE/$prod" && \
			CleanupExit 1
		[ ! -d $TARGET ] && \
			PrintErr "Can't find $TARGET" && \
			CleanupExit 1
		cd $SOURCE/$prod
		pkgDirs=$(\ls) 
		for pkg in $pkgDirs; do
			if CheckSrcAndDstPkgs "$pkg" ; then

				# cp is used instead of 'find ... | cpio'
				# since this will give us more control
				# over previously failed attempts

				cp -pr $pkg $TARGET
				[ $? != 0 ] && \
					PrintErr "cp could not complete the" \
						"copy of $pkg" && \
					CleanupExit 1
				[ -d "$TARGET/.virtual_packages/$pkg" ] && \
					rm -fr $TARGET/.virtual_packages/$pkg
			else
				PrintErr "Skipping $pkg it has already been copied."
			fi
		done
	done
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GetPkgabbrevVersionArch() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -r pkgDir=$1

	awk -F= ' /^VERSION=|^ARCH=|^PKG=/ { print $2} ' $pkgDir/pkginfo
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CheckSrcAndDstPkgs() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -r srcPkg=$1
	typeset -r dstPkg=$TARGET/$srcPkg

	if [ -d "$dstPkg" ]; then

		# The pkg already seems to exist check the qualifiers.

		srcqualifiers=$(GetPkgabbrevVersionArch $srcPkg)
		dstqualifiers=$(GetPkgabbrevVersionArch $dstPkg)
		[ "$srcqualifiers" = "$dstqualifiers" ] && return 1
	fi
	return 0
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MergePkgHistory() {

	[ "$DEBUG" = "true" ] && set -x

	# First see what pkgs exist in the target install servers .pkghistory
	# file. If there are no pkgs with the same PKG, ARCH and VERSION
	# then we can append the source .pkghistory file to the targets.
	
	typeset -r cdtoc="$SOURCE/.cdtoc"
	typeset -r dstPkgHistory="$TARGET/.pkghistory"

	for prodDir in $FINALPRODDIRS; do
		typeset srcPkgHistory="$SOURCE/$prodDir/.pkghistory"
	
		# First check for  the existence of the source .pkghistory
		[ ! -f "$srcPkgHistory" ] && continue

		# Check for the PKG variables first.
		if CheckForMatchingPKGs "$srcPkgHistory" "$dstPkgHistory"; then
			AppendHistoryFiles "$srcPkgHistory" "$dstPkgHistory"
		else
			MergEm "$srcPkgHistory" "$dstPkgHistory"
		fi
	done
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CheckForMatchingPKGs() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -r srcPkgHistory=$1
	typeset -r dstPkgHistory=$2

	awk -F= '$1 ~ /PKG/ {print $2}' $srcPkgHistory > $TMPFILES/srcpkgs.$$
	awk -F= '$1 ~ /PKG/ {print $2}' $dstPkgHistory > $TMPFILES/dstpkgs.$$
	typeset -r result=$(fgrep -f $TMPFILES/srcpkgs.$$ $TMPFILES/dstpkgs.$$)
	[ -n "$result" ] && return 0 || return 1
}	

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AppendHistoryFiles() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -r srcPkgHistory=$1
	typeset dstPkgHistory=$2

	cat $srcPkgHistory >> $dstPkgHistory
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MergePkgHistories() {

	[ "$DEBUG" = "true" ] && set -x

	typeset -r srcPkgHistory=$1
	typeset -r dstPkgHistory=$2

	#FindMatchingPKGEntries
	#CheckPKGsARCH
	#CheckPKGsVERSION
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Initialize() {

	echo ""
	DIAL_PID=0

	# Set PATH
	PATH=/usr/bin:/usr/sadm/bin:/usr/etc:/usr/sbin:${PATH}

	ORIGDIR=$(pwd)
	cmdArgs=$*
	progName=$0

	TMPFILES=/tmp/add_to_is.$$
	mkdir $TMPFILES

	SPINNER=$TMPFILES/spinner
	CreateSpinnerProgram
	chmod 500 $SPINNER
}

#~~~~~~~~~~~~~~~~~~~~~ Main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

trap "CleanupExit 1" 1 2 3 15

Initialize $@

ParseArgs $cmdArgs

CheckSource

CheckTarget

CheckProducts

TurnOnSpinner

DetermineSpaceNeeded

CopyProducts

CopyEADirectory

MergePkgHistory

CleanupExit 0
