#
#
# Copyright 1997 Xi Graphics
#
# install.bsh - Use bash to install/uninstall something.
#		This script must be called from Install, Uninstall or
#               Patch
#
# Local variables are lowercase, Globals are all UPPERCASE.
#
#
# $XiGId: install.bsh,v 1.25 1998/07/08 18:03:01 jon Exp $
#
#############################################################################
#set -x                        # uncomment for debuging

#### Initialization
#############################################################################

InitializeInstall()
{	# InitializeInstall

	local prdtmp=""

	if [ -z "$AUTO_INSTALL" ]
	then # be quiet during autoinstalls
		echo "$(MessageCat 57 '### Initializing, please wait...')"
	fi

	export LICENSE_FILE="$INSTALL_BASE/install/LICENSE"
	export RELNOTES_FILE="$INSTALL_BASE/$SYSTYPE/RELEASE_NOTES"
	export COPYRIGHT_FILE="$INSTALL_BASE/install/COPYRIGHTS"

	# where installation info is kept
	export INSTALLED_BASE="$INSTALL_ROOT/var/xig"
	# Files to save for each package
	export INSTALLED_SAVEFILES="perms preremove postremove request pinfo"

	if [ -z "$LOGFILE" ]
	then    # set LOGFILE if not already set
		case "$INSTALL_MODE" in
			install) # normal install
				export LOGFILE="$INSTALL_ROOT/tmp/Install.log"  
				;;
			uninstall) # removing something
				export LOGFILE="$INSTALL_ROOT/tmp/Uninstall.log"
				;;
			patch) # adding a patch
				export LOGFILE="$INSTALL_ROOT/tmp/Patch.log"
				;;
			*)     # hmmm
				export LOGFILE="$INSTALL_ROOT/tmp/Confused.log"
				;;
		esac
	fi

	LOGFILE="$(CreateTF $LOGFILE)"

	# set depending on INSTALL_MODE
	export PRODUCT_INFO_FILE="$INSTALL_BASE/install/PRODINFO"

	if [ ! -r "$PRODUCT_INFO_FILE" ]
	then	# No beach to walk on...
		echo "$(MessageCat 58 'Cannot open Product information file: %s' \"$PRODUCT_INFO_FILE\")"
		exit 1
	else
		set -a		# auto export these
		. $PRODUCT_INFO_FILE
		set +a
	fi

	# if this is an uninstall, we'll re-read the PRODINFO file installed on
	#  the disk (INSTALLED_BASE/installed/prodname/PRODINFO) so that 
	#  we can pick up additions (ie: patches).  It's kind of a catch-22
	#  since we need to read the CD-ROM based one to find the installed
	#  version.  

	if [ "$INSTALL_MODE" = "uninstall" ]
	then
		prdtmp="$INSTALLED_BASE/installed/$PRODUCT_INST_NAME/PRODINFO"

		if [ -r "$prdtmp" ]
		then	# it's there, we'll use it.
			export PRODUCT_INFO_FILE="$prdtmp"

			set -a
			. $PRODUCT_INFO_FILE
			set +a
		fi
		# if it's not there, we'll leave the def's alone. 
	fi


	# response - records response of last user input selection 
	#  (via dialog for now)
	RESPONSE=""

	Log "PRODINFO is $PRODUCT_INFO_FILE"

	# Misc variables

	export DEFAULT_DWIDTH=77	# dflt width for dialog screens
	export DEFAULT_DHEIGHT=20	# dflt height

	export INSTALL_MSGSET="1"
	export DIALOG_MSGSET="2"	# msg set for dialog
	export START_MSGSET="3"		# msg set for start script
	export SUCCESS_MSGSET="4"	# msg set for success script
	export FAILURE_MSGSET="5"	# msg set for failure script

					# used to pass info from start to end
					#  scripts...
	export STARTDATA="$(CreateTF)"

	return 0
}


#### Some utility functions
#############################################################################

## Log a printf style message to LOGFILE
##
function Log
{	# Log "format" [ arg ... ]

	# Log won't use message catalogs....
	echo -e "$*" >>$LOGFILE 2>&1
}
export -f Log

## print error and exit
##
function Abort
{	# Abort

	
	echo -e "$(MessageCat 2 '### Installation aborted.')"
	Log "Aborted."

	# Mirimani!
	exit 1
}

## return SECONDS - works around bash bug
##
GetSeconds()
{	# GetSeconds

	echo "$SECONDS"
	return
}
export -f GetSeconds

## create a tmpfile in a hopefully safe way... (Thanks BugTraq!)
##
function CreateTF
{	# CreateTF [filename]
	local fl="$1"	# if set, then create this file, otherwise make one
			# up
	local umsk="$(umask)"	# save umask
	local tfile=""
	local done=0
	local RND=""
	# set umask to something more restrictive...

	umask 077

	while [ $done -eq 0 ]
	do
		# This bites, but $RANDOM seems to be totally 
		#  useless otherwise
		RND="$($INSTALLSH -c 'echo $RANDOM')"

		# start trying to build the puppy....
		# build a (possibly) temporary filename
		tfile="$INSTALL_ROOT/tmp/$$.$(GetSeconds)I.$RND"

		rm -f $tfile
		touch $tfile

		# see if we were quick enough
		if [ -L "$tfile" ]
		then	# it became a symlink... 
			# try again
			rm -f "$tfile"
				# Don't want to use Log here...
			echo -e "\007$tfile became a symlink! - trying again." 
		else	# we succeeded
			done=1
		fi
	done

	# If we're here, then we have a file, successfully created
	# reset umask to orig value

	umask $umsk

	# if a filename was supplied, mv it into place

	if [ ! -z "$fl" ]
	then
		mv -f "$tfile" $fl
		echo "$fl"
	else
		echo "$tfile"
	fi
	return
}
export -f CreateTF

## find a message catalog, given a directory and name, based on $LANG
##
function FindCatalog
{	# FindCatalog DIR CATFILE
	local catdir="$1"
	local catfile="$2"

	# first see if the dir exists
	if [ ! -d "$catdir" ]
	then # not there
		Log "FindCatalog: Cannot find $catdir"
		echo -e "NoFile"
		return 1
	fi

	# see if there's a locale/$LANG/$catfile
	if [ -r "$catdir/locale/$LANG/$catfile" ]
	then
		echo -e "$catdir/locale/$LANG/$catfile"
		return 0
	fi

	# if not, look for it in $catdir (which will be assumed a C locale)
	if [ -r "$catdir/$catfile" ] 
	then
		echo -e "$catdir/$catfile"
		return 0
	fi

	# If we're here, it's not there...
	Log "FindCatalog: Unable to locate $catfile in $catdir, or $catdir/locale/$LANG"
	echo -e "NoFile"
	return 1
}
export -f FindCatalog

## return a message using a catalog 
##
function MessageCat
{	# MessageCat msgnum dfltstr [args ...]

	local msgnum="$1"
	shift
	local dfltstr="$1"
	shift

	local TheMsg="$(eval $CATGETS -f $CATFILE -s $CATSET -m $msgnum -d \"$dfltstr\" -F $* )"

	echo -e -n "$TheMsg"

#	echo -e ">> $TheMsg" >>$LOGFILE	# for testing, or just for kicks
}
export -f MessageCat

## return a message using a catalog, but requires a set as the first arg.
##
function MessageCatSet
{	# MessageCatSet set msgnum dfltstr [args ...]

	local catset="$1"
	local msgnum="$2"
	local dfltstr="$3"
	shift
	shift
	shift

	local TheMsg="$(eval $CATGETS -f $CATFILE -s $catset -m $msgnum -d \"$dfltstr\" -F $* )"

	echo -e -n "$TheMsg"

#	echo -e ">> $TheMsg" >>$LOGFILE	# for testing, or just for kicks
}
export -f MessageCatSet

## return a message using a catalog, but requires a filename and set as the first
##  args.  This will be used mostly for individual package install scripts
##  to read per-package msg catalogs...
##
function MessageFileCatSet
{	# MessageFileCatSet catfile set msgnum dfltstr [args ...]

	local catfile="$1"
	local catset="$2"
	local msgnum="$3"
	local dfltstr="$4"
	shift
	shift
	shift
	shift

	local TheMsg="$(eval $CATGETS -f $catfile -s $catset -m $msgnum -d \"$dfltstr\" -F $* )"

	echo -e -n "$TheMsg"

#	echo -e ">> $TheMsg" >>$LOGFILE	# for testing, or just for kicks
}
export -f MessageFileCatSet

## check for the existance of a command. 
##
function CheckCmd
{	# CheckCmd "cmd" "fullpath to cmd"

	local cmd="$1"
	local fullcmd="$2"

	if [ -z "$fullcmd" -o ! -x "$fullcmd" ]
	then
		echo -e "$(MessageCat 1 'ERROR: Cannot find %s in PATH: %s' $cmd $PATH )"
		return 1

	else
                Log "$cmd is $fullcmd"
        fi

	return 0
}

#### core functions
#############################################################################

## return disk left in $1, or /usr if $1 is empty
##
function DiskFree
{	# DiskFree [dir]

	local dcheck

	if [ -z "$1" ]
	then	# no arg specified, default to /usr
		dcheck="/usr"
	else
		dcheck="$1"
	fi

	if [ "$SYSTYPE" = "BSD_OS" ]
	then
		echo -e "$(${DF} ${dcheck} | ${AWK} '/^\// { printf("%d\n", $4); }')"
	else
		echo -e "$(${DF} -k ${dcheck} | ${AWK} '/^\// { printf("%d\n", $4); }')"
	fi
}
export -f DiskFree

## Display a message box (using dialog for now).
##  returns 0 if OK selected, non-zero if ESC pressed.
##
function MessageBox
{	# MessageBox "Title" "Text"

	local title="$1"
	local message="$2"
	local height=$DEFAULT_DHEIGHT
	local width=$DEFAULT_DWIDTH
	local argv
	local rv

	$DIALOG --title "$title" --msgcat $CATFILE $DIALOG_MSGSET --msgbox "$message" $height $width
	rv=$?

	return $rv
}
export -f MessageBox

## put up an info box, letting the user know something interesting is
##  going on
function InfoBox
{	# InfoBox "title" "text"

	local title="$1"
	local text="$2"
	local height=$DEFAULT_DHEIGHT
	local width=$DEFAULT_DWIDTH

	$DIALOG --title "$title" --msgcat $CATFILE $DIALOG_MSGSET --infobox "$text" $height $width
}
export -f InfoBox

## Put up a 'yes/no' box... returns 0 if YES selected, 1 otherwise
##
function YesNoBox
{
	# YesNoBox "title" "text"

	local title="$1"
	local text="$2"
	local height=$DEFAULT_DHEIGHT
	local width=$DEFAULT_DWIDTH
	local rv

	$DIALOG --title "$title" --msgcat $CATFILE $DIALOG_MSGSET --yesno "$text" $height $width

	rv=$?

	if [ $rv != 0 ]
	then		# no or cancelled
		return 1
	else
		return 0
	fi
}	
export -f YesNoBox

## Check disk space, issue a warning if insufficient space available.
##  returns 0 if ok, 1 otherwise
function CheckDSpace
{	# CheckDSpace "required" ["fs"]

	local req="$1"
	local filesys="$2"

	if [ -z "$filesys" ]
	then	# none spec'd, use /usr
		filesys="/usr"
	fi

	local dfree="$(DiskFree $filesys)"
	local title="$(MessageCat 3 'Insufficient Disk Space')"
	local msg="$(MessageCat 4 '\n\nNot enough space in %s to install selected packages.\nRequired: %dKB, Available: %dKB\n\nDo you want to try to install anyway?' $filesys $req $dfree )"

	if [ $req -ge $dfree ]
	then	# not enough space
		if YesNoBox "$title" "$msg"
		then		# Engage manual override Mr. Chekov.
			Log "dfree=$dfree req=$req" 
			return 0
		else		# chicken...bok bok bok
			return 1
		fi
	fi

	return 0
}
export -f CheckDSpace

## list releasenotes, copyright, prompt for agreement to license.
##
function DisplayIntroFiles
{
	local copytitle="$(MessageCat 66 'Welcome to %s Installation' \"$PRODUCT_NAME\")"
	local lictitle="$(MessageCat 67 'Xi Graphics Software License Agreement')"
	local yntitle="$(MessageCat 7 'Do you Agree?')"
	local yntext="$(MessageCat 8 '\n\nBy selecting <Yes>, You are agreeing to the terms of\nthis License Agreement.')"
	local relnotitle="$(MessageCat 9 'Release Notes')"

	# first the copyright
	if [ -f $COPYRIGHT_FILE ]
	then
		if ! $DIALOG --title "$copytitle" --msgcat $CATFILE $DIALOG_MSGSET --textbox "$COPYRIGHT_FILE" $DEFAULT_DHEIGHT $DEFAULT_DWIDTH
		then  # CANCEL
			return 1
		fi
	else
		Log "$COPYRIGHT_FILE not found"
	fi
	
	# now the license
	if [ -f $LICENSE_FILE ]
	then
		if ! $DIALOG --title "$lictitle" --msgcat $CATFILE $DIALOG_MSGSET --licensebox $LICENSE_FILE $DEFAULT_DHEIGHT $DEFAULT_DWIDTH
		then	# they refused
			return 1
		fi
	else
		Log "$LICENSE_FILE not found"
	fi

	if [ -f $RELNOTES_FILE ]
	then
		$DIALOG --title "$relnotitle" --msgcat $CATFILE $DIALOG_MSGSET --textbox "$RELNOTES_FILE" $DEFAULT_DHEIGHT $DEFAULT_DWIDTH

	else
		Log "$RELNOTES_FILE not found"
	fi

	return 0
}

## Display an Installation failed message.
##
function DisplayInstallFailed
{	# DisplayInstallFailed

	local title="$(MessageCat 10 'Install Failed')"
	local msg="$(MessageCat 11 '\nA failure occurred during installation of the selected packages.\nPlease see %s for clues as to the reason for failure.' $LOGFILE )"

	MessageBox "$title" "$msg"

}

## check to see if a package is installed.  Looks in 
## $INSTALLED_BASE/installed/$PRODUCT_INST_NAME/PKGNAME.
## return 0 if installed, 1 otherwise
##
function IsInstalled
{
	# IsInstalled PKG-NAME

	local pkg="$1"

	# first see if $INSTALLED_BASE and $INSTALLED_BASE/installed is 
	#  there.  If not then obviously there's nothing installed yet.

	local installed_dir="$INSTALLED_BASE/installed"

	if [ ! -d "$INSTALLED_BASE" -o ! -d "$installed_dir" ]
	then	# not installed
		return 1
	fi

	# dirs exist, see if pkgname is there....

	if [ -d "$installed_dir/$PRODUCT_INST_NAME/$pkg" ]
	then	# it's there
		return 0
	else
		return 1
	fi
}
export -f IsInstalled

## check to see if a product is installed. by looking for 
## $INSTALLED_BASE/installed/$PRODUCT_INST_NAME/PRODINFO
## return 0 if installed, 1 otherwise
##
function IsProdInstalled
{
	# IsProdInstalled ProdName
	local prod="$1"

	# first see if $INSTALLED_BASE and $INSTALLED_BASE/installed is 
	#  there.  If not then obviously there's nothing installed yet.

	local installed_dir="$INSTALLED_BASE/installed"

	if [ ! -d "$INSTALLED_BASE" -o ! -d "$installed_dir" ]
	then	# not installed
		return 1
	fi

	# dirs exist, see if product is there....

	if [ -d "$installed_dir/$prod" ]
	then	# it's there
		return 0
	else
		return 1
	fi
}
export -f IsProdInstalled

## this function marks a package as installed.  
##  it will copy INSTALLED_SAVEFILES for the pkg into 
##  $INSTALLED_BASE/installed/$PRODUCT_INST_NAME.
##
function MarkInstalled
{
	# MarkInstalled PKG-NAME	

	local pkg="$1"

	local installed_dir="$INSTALLED_BASE/installed"
	local pkginstdir="$installed_dir/$PRODUCT_INST_NAME/$pkg"
	local prodinfo="$installed_dir/$PRODUCT_INST_NAME/PRODINFO"
	local tmpfile="$(CreateTF)"

	if [ ! -d "$INSTALLED_BASE" ]
	then	# make it
		mkdir -p $INSTALLED_BASE
		$CHMOD 755 $INSTALLED_BASE 
	fi

	if [ ! -d "$installed_dir" ]
	then	# make it
		mkdir -p $installed_dir
		$CHMOD 755 $installed_dir
	fi

	# check to see if the product info directory and file has been installed...

	if [ ! -d "$installed_dir/$PRODUCT_INST_NAME" ]
	then
		mkdir -p $installed_dir/$PRODUCT_INST_NAME
		$CHMOD 755 $installed_dir/$PRODUCT_INST_NAME
		# we shouldn't even be here if this is a patch - which is good.
		$CP $INSTALL_BASE/install/PRODINFO $installed_dir/$PRODUCT_INST_NAME/PRODINFO
	fi

	if [ ! -d "$pkginstdir" ]
	then	# make it
		mkdir -p $pkginstdir
		$CHMOD 755 $pkginstdir
	fi

	# now copy all the files mentioned in INSTALLED_SAVEFILES into 
	#  $pkginstdir (if they exist)

	for i in $INSTALLED_SAVEFILES
	do
		if [ -r "$SYSBASE/packages/$pkg/$i" ]
		then
			$CP $SYSBASE/packages/$pkg/$i $pkginstdir/$i
			$CHMOD 644 $pkginstdir/$i
		fi
	done

	### for a patch, we'll need to edit the base product's PRODINFO
	###  file and add this pkg to the list of packages so that Uninstall
	###  will work correctly...

	if [ "$INSTALL_MODE" = "patch" ]
	then	# a patch... 
		local tmppkgs=""

		. $prodinfo

		# first make sure it's not already there...
		echo "$PRODUCT_PKGS" |$FGREP -w -q "$pkg"
		rv=$?

		if [ $rv -ne 0 ]
		then	# it's not there...add it
			tmppkgs="$PRODUCT_PKGS $pkg"

			$GREP -v "^PRODUCT_PKGS=" $prodinfo >$tmpfile
			echo "PRODUCT_PKGS=\"$tmppkgs\"" >>$tmpfile
			$CP -f $tmpfile $prodinfo
			$RM -f $tmpfile

			# update PRODUCT_PKGS..
			PRODUCT_PKGS="$tmppkgs"
		fi
	fi
}
export -f MarkInstalled

## this function marks a package as uninstalled, basically by removing
## it's information (perms, pinfo, etc).
## It will also check (after) to see if there are any installed pkgs
##  for this product left, if not, then it will remove the PRODUCT_INST_DIR
##  for this product as well... 
##
function MarkUninstalled
{
	# MarkUninstalled PKG-NAME	

	local pkg="$1"

	local installed_dir="$INSTALLED_BASE/installed"
	local prodinstdir="$installed_dir/$PRODUCT_INST_NAME"
	local pkginstdir="$prodinstdir/$pkg"
	local -i rmprod

	## prodinstdir and pkginstdir should exist or we wouldn't be here...

	## now delete any file in the pkginstdir.  

	# extra sanity check, it would be embarrassing, and possibly
	#  detrimental to my career to remove /* ;-)
	if [ ! -z "$pkginstdir" ]
	then
		$RM -f $pkginstdir/*
	fi
	
	# ok, now remove the pkginstdir

	$RMDIR $pkginstdir

	## now see if any packages are still installed for this product.
	## if not, the remove prodinstdir too

	rmprod=1	# delete product (default)

	for i in $PRODUCT_PKGS
	do
		if IsInstalled $i
		then	# there's at least one there.
			rmprod=0
		fi
	done

	## if no pkgs left for product, remove it

	if [ $rmprod -eq 1 ]
	then
		$RM -f $prodinstdir/PRODINFO
		$RMDIR $prodinstdir
	fi

	# Ahhh....AhhhAhhhAhhh...Bitter Dregs.
	return 0
}
export -f MarkUninstalled

## check for existance of package(s).  Has the side effect of printing the 
##  total size of the packages passed as parameters.  a return of 0 indicates
##  a failure to locate or obtain information on any one of the given pkg(s).
##
function CheckPkgs
{	# CheckPkgs pkgname [pkgname ...]

	local pinfofile
	local -i totspace=0

	unset PKG_SPACE		# clear it in case... comes from pinfo

	while [ ! -z "$1" ]
	do
		if [ ! -d $SYSBASE/packages/$1 ]
		then
			Log "ERROR: Package $1 not found"
			echo -e "0"
			return 1
		fi

		# get the package info file...
		pinfofile="$SYSBASE/packages/$1/pinfo"

		if [ ! -r "$pinfofile" ]
		then
			Log "ERROR: pkginfo file: $pinfofile not found"
			echo -e "0"
			return 1
		else
			. $pinfofile
		fi
		
		if [ -z "$PKG_SPACE" ]
		then
			Log "ERROR: PKG_SPACE not defined in $pinfofile"
			echo -e "0"
			return 1
		fi

		totspace=$((totspace + $PKG_SPACE))
		shift
	done

	echo -e $totspace
	return 0
}
export -f CheckPkgs

## display the installation options available as a menu.  print the selected 
##  item, return 0 - if OK pressed, 1 if CANCEL pressed or error
##
function DisplayMainMenu
{	# DisplayMainMenu

	local title="$(MessageCat 14 '%s Installation' \"$PRODUCT_NAME\")"
	local menutxt="$(MessageCat 15 '\nSelect an Item from the following list. \n\n\nYou have %dKB available in /usr.' $(DiskFree) )"
	local min="$(MessageCat 16 'Minimal')"
	local mintxt="$(MessageCat 17 'Minimal Installation, Runtime Only')"
	local full="$(MessageCat 18 'Full')"
	local fulltxt="$(MessageCat 19 'Full Installation, Everything')"
	local custom="$(MessageCat 20 'Custom')"
	local customtxt="$(MessageCat 21 'Custom Installation, Select Individual Packages')"
	local quit="$(MessageCat 22 'Quit')"
	local quittxt="$(MessageCat 23 'Exit Installation')"
	local express="$(MessageCat 59 'Express')"
	local expresstxt="$(MessageCat 60 'Express Installation, Select Individual Sets')"

	local setdir="$SYSBASE/sets"
	local rv
	local i
	local respfile="$(CreateTF)"

	local -i width=$DEFAULT_DWIDTH
	local -i height=$DEFAULT_DHEIGHT
	local -i menusize=4
	local argv

	# start building the DIALOG arg list
	argv="--title \"$title\" --msgcat $CATFILE $DIALOG_MSGSET --menu \"$menutxt\" $height $width $menusize"

	if [ -d "$setdir/MINIMAL" ]
	then	# add it

		# override text if a name file present
		if [ -r "$setdir/MINIMAL/name" ]
		then
			mintxt="$($CAT $setdir/MINIMAL/name)"
		fi
		argv="${argv} \"$min\" \"$mintxt\""
	fi

	if [ -d "$setdir/EXPRESS" ]
	then	# add it

		# override text if a name file present
		if [ -r "$setdir/EXPRESS/name" ]
		then
			expresstxt="$($CAT $setdir/EXPRESS/name)"
		fi

		argv="${argv} \"$express\" \"$expresstxt\""
	fi

	if [ -d "$setdir/CUSTOM" ]
	then	# add it

		# override text if a name file present
		if [ -r "$setdir/CUSTOM/name" ]
		then
			customtxt="$($CAT $setdir/CUSTOM/name)"
		fi

		argv="${argv} \"$custom\" \"$customtxt\""
	fi

	# There will always be a full. Esp. for one-package products, 
	#  patches etc...

	# override text if a name file present
	if [ -r "$setdir/FULL/name" ]
	then
		fulltxt="$($CAT $setdir/FULL/name)"
	fi

	argv="${argv} \"$full\" \"$fulltxt\""

	# now add a Quit option (for BSD/OS)

	if [ $SYSTYPE = "BSD_OS" ]
	then
		argv="${argv} \"$quit\" \"$quittxt\""
	fi

	# evalutate it. 
	eval "${DIALOG} ${argv}" 2>$respfile
	rv=$?

	RESPONSE="$($CAT $respfile)"
	
	$RM $respfile

	return $rv
}

## Install a package.  This function is responsible for running a package 
##  preinstall script (if it exists), unpacking the appropriate archive, 
##  then running the postinstall script (if it exists).  It returns 0 on
##  success, 1 if any one of these steps fail.  See LOGFILE for details 
##  on what went wrong
##
function InstallPkg
{	# InstallPkg pgkname

	local thepkg="$1"	# we assume it exists, or we wouldn't be this far

	local rv
	PKGLOCDIR="$SYSBASE/packages/$thepkg"
	export PKGLOCDIR	# for pkg scripts...
	local preinst="$SYSBASE/packages/$thepkg/preinstall"
	local postinst="$SYSBASE/packages/$thepkg/postinstall"
	local thearc="$SYSBASE/packages/$thepkg/archive"
	local perms="$SYSBASE/packages/$thepkg/perms"
	local pkgpinfo="$SYSBASE/packages/$thepkg/pinfo"	
	local title="$(MessageCat 24 'Installation Progress')"
#	local instmsg="$(MessageCat 25 '\n\n\n\n\n\n\n         Installing Package: %s...' $thepkg)"
	local instmsg=""	# filled in later 
	local isnullarc=0
	local isquiet=0

	unset PKG_QUIET

	# pkgrespfile is a file that is passed as an arguement to the 
	#  preinstall and postinstall files.  It is intended to be created
	#  by the preinstall script, and then to be sourced '.' in by the 
	#  postinstall script if it exists.  This provides a way for the pre/post
	#  install scripts to communicate with out interfering with this script.
	local pkgrespfile="$(CreateTF)"

	. $pkgpinfo	# get package data

	# see if this should be a 'quiet package' (ie: no screens, etc, except
	#  perhaps *request scripts

	if [ -z "$PKG_QUIET" ]
	then
		isquiet=0
	else
		if [ "$PKG_QUIET" = "yes" ]
		then
			Log "$0: isquiet=1"
			isquiet=1
		fi
	fi

	instmsg="$(MessageCat 68 '\n\n\n\n\n\n\n         Installing Package: %s\n                Description: %s\n               Size (in KB): %s' $thepkg \"$PKG_NAME\" \"$PKG_SPACE\" )"

	if [ $isquiet -ne 1 ]
	then
		InfoBox "$title"  "$instmsg"
	fi

	Log "Installing $thepkg"

	# see if the archive exists... it should...

	if [ ! -r "$thearc" ]
	then
		if [ -r "$SYSBASE/packages/$thepkg/nullarchive" ]
		then # this is a script only package
			Log "InstallPkg: nullarchive found"
			isnullarc=1
		else
			Log "InstallPkg: Cannot find archive: $thearc"
			return 1
		fi
	fi

	# see if there is a preinstall... if so execute it.  if a non-zero
	#  exit, terminate.

	if [ -r $preinst ]
	then		# go for it
		Log "### Executing PREINSTALL: $preinst"
		$INSTALLSH $preinst $pkgrespfile >>$LOGFILE 2>&1
		rv=$?
	
		if [ $rv != 0 ]
		then	# some kind of error
			Log "$preinst, retval=$rv" 
			Log "### PREINSTALL failed: $preinst"

			return 1
		else
			Log "### PREINSTALL completed."
		fi
	fi

	if [ $isnullarc -eq 0 ]
	then # there is an archive to unpack
		cd $INSTALL_ROOT

		## extract the dirs from the perms file, checking each directory, since
		##  the archive doesn't contain dir specific entries.

		Log "Checking directories..." 

		$GREP ' D ' $perms |
		while read thedir junk1 theperms theown thegrp junk2 junk3 junk4
		do
		    if [ ! -h $thedir ]
		    then # it's not a link
			if [ ! -d $thedir ]
			then # it's not a dir either (or it doesn't exist)
				mkdir $thedir >>$LOGFILE 2>&1
				$CHMOD $theperms $thedir >>$LOGFILE 2>&1
				$CHOWN $theown $thedir  >>$LOGFILE 2>&1
				$CHGRP $thegrp $thedir  >>$LOGFILE 2>&1

				Log "Created $thedir, $theperms $theown $thegrp"
			fi
		    fi
		done

		# now unpack it

		if [ "$SYSTYPE" = "BSD_OS" ]
		then
			$PAX -r -pe -v < $thearc >>$LOGFILE 2>&1
		else
			if [ $isquiet -ne 1 ]
			then	# 'noisy' installation
				$DIALOG --title "$title" --msgcat $CATFILE $DIALOG_MSGSET --prgguage "$instmsg" $DEFAULT_DHEIGHT $DEFAULT_DWIDTH "$CPIO -iduVm >>$LOGFILE 2>&1" $thearc
			else	# 'quiet' installation
				$CPIO -iduVm <$thearc >>$LOGFILE 2>&1
			fi
		fi
		rv=$?

	        if [ $rv != 0 ]
	        then    # some kind of error
			Log "### EXTRACT $thearc failed, retval=$rv" 
			$RM -f $pkgrespfile

			return 1
		fi
  
		cd - >/dev/null 2>/dev/null

	fi # ! a nullarchive

	# now for the postinstall
	if [ -r $postinst ]
	then		# go for it
		Log "### Executing POSTINSTALL: $postinst"
		$INSTALLSH $postinst $pkgrespfile >>$LOGFILE 2>&1
		rv=$?
	
		if [ $rv != 0 ]
		then	# some kind of error
			Log "$postinst, retval=$rv" 
			Log "### POSTINSTALL failed: $postinst"

			$RM -f $pkgrespfile
			return 1
		else
			Log "### POSTINSTALL completed."
		fi
	fi

	MarkInstalled "$thepkg"
	$RM -f $pkgrespfile

	# scram the pile
	return 0
}

## First, check all the packages, and execute any 'request' scripts found
##  within.  Once these have been run, then run InstallPkg on each package.
##  REQRESPFILE will be set and exported before InstallPkg is run so that
##  those post/preinstall scripts that are interested, can source it in via
##  '. $REQUEST_DATA'
function DoInstall
{	# DoInstall "PKG PKG ..."

	local reqrespfile="$(CreateTF)"
	local respfile="$(CreateTF)"
	local i
	local thepkgs="$1"
	local pkgbase="$SYSBASE/packages"
	local width=$DEFAULT_DWIDTH
	local height=$DEFAULT_DHEIGHT
	local cheight=10
	local argv=""
	local alreadyinstalled
	local notinstalled
	local arm
	local rv
	local overlaytitle="$(MessageCat 38 'Confirm Package Overlay')"
	local overlaytxt="$(MessageCat 39 'The following packages are already installed.\nPlease mark those packages you wish to re-install and select OK,\nelse select CANCEL.')"
	local noovrltitle=""		# filled in later
	local noovrltext=""		# filled in later

					# whether to show the overlay screen
					#  in case all the packages to be
					#  installed don't allow overlays.
	local showovlmenu="1"

	## First, remove duplicates...
	thepkgs="$(echo $thepkgs |$SORT |$UNIQ)"
	


	## Second, see if any of the packages are already installed...
	## if so, present a list of installed packages and ask for
	## confirmation...

	alreadyinstalled=""
	notinstalled=""

	for i in $thepkgs
	do
		if IsInstalled $i
		then
			alreadyinstalled="$alreadyinstalled $i"
		else
			notinstalled="$notinstalled $i"
		fi
	done

	thepkgs=""
	if [ ! -z "$alreadyinstalled" ]
	then		# some of these are installed already
		
		# build DIALOG argv
		argv="--title \"$overlaytitle\" --msgcat $CATFILE $DIALOG_MSGSET --checklist \"$overlaytxt\" $height $width $cheight"

		arm="on"		# automatically selected

		for i in $alreadyinstalled
		do
			if [ ! -r $pkgbase/$i/pinfo ]
			then
				Log "ERROR: package info file: $SYSBASE/packages/$i/pinfo not found"
				return 1
			else
				. $SYSBASE/packages/$i/pinfo
			fi

			# see if overlays for this package are allowed
			
			if [ "$PKG_OVERLAY" = "no" ]
			then	# not allowed for this package
				noovrltitle="$(MessageCat 64 'Cannot re-install %s' \"$i\")"
				noovrltext="$(MessageCat 65 '\nThis package cannot be installed over itself.\nIf you wish to re-install this package, you will need\nto remove it first with Uninstall.\n\nDo you wish to skip this package and continue Installation?\n\n')"

				if ! YesNoBox "$noovrltitle" "$noovrltext"
				then	# nope, they want to cancel
					return 2
				fi

			else
				# $PKG_NAME comes from the pinfo file
				argv="${argv} \"$i\" \"$PKG_NAME\" $arm"
				showovlmenu="0"
			fi
		done

		# evaluate it - if there's something to evaluate ;-)
		if [ "$showovlmenu" = "0" ]
		then
			eval "${DIALOG} ${argv}" 2>$respfile

			# here's the selected pkgs. 

			RESPONSE="$($CAT $respfile)"

			$RM $respfile

			if [ ! -z "$RESPONSE" ]
			then	# at least one selected
				# remove the "'s and set $thepkgs to the 
				#  (possibly) new list
				thepkgs="$(echo $RESPONSE |$TR -d \")"
			fi
		fi
	fi

	# add the not-yet-installed pkgs to the list
	thepkgs="$thepkgs $notinstalled"

	Log "thepkgs = $thepkgs"

	## now process per package request files...
	>$REQUEST_DATA		# truncate it

	for i in $thepkgs
	do
		if [ -r $pkgbase/$i/request ]
		then
			>$reqrespfile		# clear it

			Log "### executing request file: $pkgbase/$i/request"
			export PKGLOCDIR="$pkgbase/$i"
			$INSTALLSH $pkgbase/$i/request $reqrespfile
			if [ $? != 0 ]
			then	# a problem
				Log "### $pkgbase/$i/request failed."
				return 1
			fi
			
			$CAT $reqrespfile >> $REQUEST_DATA	# add it
		fi
	done

	## if we're here then all the request scripts (if any) have
	##  been run.  
	## Now loop through the packages, running InstallPkg for each one.

	for i in $thepkgs
	do
		InstallPkg $i
		if [ $? != 0 ]
		then	# error
			Log "Installation of $i failed"

			return 1
		fi
	done

	## now look for per package postrequest scripts, and exec 
	##  if there...
	for i in $thepkgs
	do
		if [ -r $pkgbase/$i/postrequest ]
		then
			Log "### executing postrequest file: $pkgbase/$i/postrequest"
			export PKGLOCDIR="$pkgbase/$i"
			$INSTALLSH $pkgbase/$i/postrequest $REQUEST_DATA
			if [ $? != 0 ]
			then	# a problem
				Log "### $pkgbase/$i/postrequest failed."
				return 1
			fi
		fi
	done

	return 0
}

## Install the 'Express' set.  In this case we present a list of all the 
##  sets available to the user and allow the user to select which ones to 
##  install.  returns 0 = success, 1 = error, 2 = cancelled
##
function DoExpress
{	# DoExpress

	#  We will present a list of available sets for the user
	#  to select.  Since we don't know off hand how much space this will 
	#  require, we have to defer computation until we know *which* actual
	#  packages will be installed.

	local respfile="$(CreateTF)"
	local rv
	local i
	local argv=""
	local width=$DEFAULT_DWIDTH
	local height=$DEFAULT_DHEIGHT
	local cheight=10
	local title="$(MessageCat 61 'Express Set selection')"
	local clisttext="$(MessageCat 62 '\nChoose one or more of the sets below.\nYou have %s available in /usr.\n' \"$(DiskFree)KB\")"
	local yntitle="$(MessageCat 29 'Confirm Install Choice')"
	local yntext="$(MessageCat 63 '\n\n\nProceed to install the Express set?\n\n(it may take some time to scan your installation media\nfor the list of available sets)')"
	local spacereq
	local pkgs
	local setname
	local expsetlist="$SYSBASE/sets/EXPRESS/setlist"
	local arm
	local pkgfile="$(CreateTF)"
	local sname
	local sarm

	if [ ! -r "$expsetlist" ]
	then	# Bummer dude.
		Log "$expsetlist not found."
		return 1
	fi

	# Confirm

	if ! YesNoBox "$yntitle" "$yntext"
	then	# nope
		return 2
	fi

	# build DIALOG argv
	argv="--title \"$title\" --checklist \"$clisttext\" $height $width $cheight"

	## now read in setlist.  It has entries like "X setname", where
	##  'X' = 'y' or 'n' indicating whether it should be armed by
	##  default.  Geez, sounds like I'm talking about photon torpedos..

	while read sarm sname	
	do 
		if [ -z "$sarm" -o -z "$sname" ]
		then
			continue	# skip blank lines
		fi

		if [ "$sarm" = "y" ]
		then
			arm="on"	# auto-activate this one
		else
			arm="off"
		fi

		# get packages, and compute size
		pkgs="$($CAT $SYSBASE/sets/$sname/list)"
		spacereq="$(CheckPkgs $pkgs)"
		setname="$($CAT $SYSBASE/sets/$sname/name)"
		if [ $spacereq = "0" ]
		then	# some error - die
			$RM -f $respfile
			return 1
		fi

		# now add to arg string
		argv="${argv} \"$sname\" \"$setname, ${spacereq}KB\" $arm"

	done <$expsetlist

	# evaluate it

	eval "${DIALOG} ${argv}" 2>$respfile
	rv=$?

	if [ $rv -ne 0 ]
	then		# cancel
		return 2
	fi

	# here's the selected sets.  get all the packages in each selected set, 
	#  sort, uniq, then get the diskspace.  Make sure there is enough disk.

	RESPONSE="$($CAT $respfile)"
	$RM -f $respfile

	if [ -z "$RESPONSE" ]
	then	# none selected
		return 2
	fi

	# remove the "'s...
	RESPONSE="$(echo $RESPONSE |$TR -d \")"

	pkgs=""

	# build sorted/uniq'd package list
	>$pkgfile

	for i in $RESPONSE
	do
		$CAT $SYSBASE/sets/$i/list >>$pkgfile
		echo >>$pkgfile
	done

	# sort/uniq them
	pkgs="$($SORT $pkgfile |$UNIQ)"

	$RM -f $pkgfile

	spacereq="$(CheckPkgs $pkgs)"

	if [ $spacereq = "0" ]
	then	# some error - die
		return 1
	fi

	if ! CheckDSpace "$spacereq"
	then
		return 1
	fi

	# Make It So
	DoInstall "$pkgs" 
	return $?
}
		

## Install selected packages - returns 0 = success, 1 = error, 2 = canceled
##
function DoCustom
{	# DoCustom

	# with Custom, present a list of all packages available for install.
	#  This assumes (erroneously?) that the user knows what she's doing.

	# first make sure we have enough diskspace

	local respfile="$(CreateTF)"
	local rv
	local i
	local argv=""
	local pkgs=""
	local width=$DEFAULT_DWIDTH
	local height=$DEFAULT_DHEIGHT
	local cheight=10
	local spacereq
	local name
	local arm

	local ctitle="$(MessageCat 26 'Custom Package selection')"
	local ctext="$(MessageCat 52 '\nChoose one or more of the packages below.\nYou have %dKB available in /usr.\n' $(DiskFree) )"
	local ynwarn="$(MessageCat 27 'Warning!')"
	local yntext="$(MessageCat 28 '\n\nCustom install is to be used by those people who understand the\npackage dependancies and relationships in some detail.\n\nAre you sure you want a Custom Installation?\n\n(it may take some time to scan your installation media\nfor the list of available packages)')"


	# Issue warning about Custom...

	if ! YesNoBox "$ynwarn" "$yntext" 
	then	# Cancel or NO selected
		return 2
	fi

	# build DIALOG argv
	argv="--title \"$ctitle\" --msgcat $CATFILE $DIALOG_MSGSET --checklist \"$ctext\" $height $width $cheight"

	arm="off"		# always, for now

	for i in $PRODUCT_PKGS
	do
		if [ ! -r $SYSBASE/packages/$i/pinfo ]
		then
			Log "ERROR: package info file: $SYSBASE/packages/$i/pinfo not found"
			return 1
		else
			. $SYSBASE/packages/$i/pinfo
		fi

		# from the pinfo file
		spacereq="$PKG_SPACE"
		name="$PKG_NAME"

		argv="${argv} \"$i\" \"$name, ${spacereq}KB\" $arm"
	done

	# evaluate it
	eval "${DIALOG} ${argv}" 2>$respfile
	rv=$?

	if [ $rv != 0 ]
	then		# cancel
		return 2
	fi

	# here's the selected pkgs. Make sure there is enough disk.

	RESPONSE="$($CAT $respfile)"

	$RM $respfile

	if [ -z "$RESPONSE" ]
	then	# none selected
		return 2
	fi

	# remove the "'s...
	pkgs="$(echo $RESPONSE |$TR -d \")"

	# check space
	spacereq="$(CheckPkgs $pkgs)"

	if [ $spacereq = "0" ]
	then	# some error - die
		return 1
	fi

	if ! CheckDSpace "$spacereq"
	then
		return 1
	fi

	# ok, lets burn some cycles
	DoInstall "$pkgs"
	return $?

}

## Install the minimal selection.
## returns 0 = success, 1 = fail, 2 = cancel
##
function DoMinimal
{	# DoMinimal

	# with minimum, install all of it's packages.  We die if any one of them 
	#  fails. Unfortunately we can't clean up after ourself if this happens.

	local minimalset="$SYSBASE/sets/MINIMAL"
	local pkgstoinstall="$($CAT $minimalset/list)"
	local dspacereq
	local yntitle="$(MessageCat 29 'Confirm Install Choice')"
	local yntext="$(MessageCat 30 '\n\n\nProceed to install the Minimal set?\n\n(it may take some time to scan your installation media\nfor the list of available packages)' )"

	if ! YesNoBox "$yntitle" "$yntext"
	then	# nope
		return 2
	fi

	# get disk space of required pkgs.
	dspacereq="$(CheckPkgs $pkgstoinstall)"

	# first make sure we have enough diskspace
	if ! CheckDSpace "$dspacereq" 
	then
		return 1
	fi

	# Ok... Let's Rock.
	DoInstall "$pkgstoinstall"

	return $?		

}

## Install everything
## returns 0 = success, 1 = fail, 2 = cancel
##
function DoFull
{	# DoFull

	# with Full, install all of it's packages.  We die if any one of them 
	#  fails. Unfortunately we can't clean up after ourself if this happens.

	local fullset="$SYSBASE/sets/FULL"
	local pkgstoinstall="$($CAT $fullset/list)"
	local dspacereq
        local yntitle="$(MessageCat 29 'Confirm Install Choice')"
	local yntext="$(MessageCat 31 '\n\n\nProceed to install the Full set?\n\n(it may take some time to scan your installation media\nfor the list of available packages)')"

	# Confirm

	if ! YesNoBox "$yntitle" "$yntext"
	then	# nope
		return 2
	fi

	# get disk space of required pkgs.
	
	dspacereq="$(CheckPkgs $pkgstoinstall)"

	# first make sure we have enough diskspace

	if ! CheckDSpace "$dspacereq"	
	then
		return 1
	fi

	# Engage.

	DoInstall "$pkgstoinstall"

	return $?		

}

## Remove a package.  This function is responsible for running a package 
##  preremove script (if it exists), deleting the files/dirs listed in
##  the package's perms file, then running the postremove script (if 
##  it exists).  It returns 0 on success, 1 if any one of these steps fail. 
##  See LOGFILE for details on what went wrong
##
function RemovePkg
{	# RemovePkg pgkname

	local thepkg="$1"	# we assume it exists or we wouldn't be this far
	local prodinstdir="$INSTALLED_BASE/installed/$PRODUCT_INST_NAME"
	local pkginstdir="$prodinstdir/$thepkg"
	local rv
	local preremove="$pkginstdir/preremove"
	local postremove="$pkginstdir/postremove"
	local perms="$pkginstdir/perms"
	local remdirs=""
	local dirlistfile="$(CreateTF)"
	local title="$(MessageCat 46 'Package Removal Progress')"
	local rmtext="$(MessageCat 47 '\n\n\n\n\n\n\n         Removing Package: %s...' $thepkg)"


	# pkgrespfile is a file that is passed as an arguement to the 
	#  preremove and postremove files.  It is intended to be created
	#  by the preremove script, and then to be sourced '.' in by the 
	#  postremove script if it exists.  This provides a way for the pre/post
	#  remove scripts to communicate with out interfering with this script.
	local pkgrespfile="$(CreateTF)"

	InfoBox "$title"  "$rmtext"

	Log "Removing $thepkg"

	# see if the perms file for the package exists... it better...

	if [ ! -r "$perms" ]
	then
		Log "Can't find perms file: $perms"
		return 1
	fi

	# see if there is a preremove... if so execute it.  if a non-zero
	#  exit, terminate.

	if [ -r $preremove ]
	then		# go for it
		Log "### Executing PREREMOVE: $preremove"
		export PKGLOCDIR="$pkginstdir"
		$INSTALLSH $preremove $pkgrespfile >>$LOGFILE 2>&1
		rv=$?
	
		if [ $rv != 0 ]
		then	# some kind of error
			Log "$preremove, retval=$rv" 
			Log "### PREREMOVE failed: $preremove"

			return 1
		else
			Log "### PREREMOVE completed."
		fi
	fi


	## read through the perms file, deleting all files mentioned.  We'll
	## save a list of all directories, so that they can be rmdir'd
	## once all the files have been killed.

	Log "Removing files..." 
	>$dirlistfile		# trunc the dirlist

	$CAT $perms | 
	while read thefile thetype junk
	do
		if [ "$thetype" = "D" ]
		then	# a dir, save it for later
			echo "$thefile" >>$dirlistfile
		else	# a file, delete it.  
			$RM -f $INSTALL_ROOT/$thefile
			Log "Deleted: $INSTALL_ROOT/$thefile"
		fi
	done

	## if we have dirs (and we should have at least one) delete them.
	## NOTE: rmdir is used so that non-empty dirs won't be deleted.
	## we'll reverse sort them so that rmdir gets them in the correct 
	##  order.

	remdirs="$($SORT -r +0 $dirlistfile)"

	## now loop through them, removing each one

	if [ ! -z "$remdirs" ]
	then
		for i in $remdirs
		do
			Log "Removing $INSTALL_ROOT/$i"
			$RMDIR $INSTALL_ROOT/$i
		done
	else
		# Hmmm.  "The lab theorizes an enveloping energy plasma,
		#  forcing an implosion."

		Log "No directories to remove. Curious."
	fi

	# cleanup
	$RM -f $dirlistfile

	# now for the postremove
	if [ -r $postremove ]
	then		# go for it
		Log "### Executing POSTREMOVE: $postremove"
		export PKGLOCDIR="$pkginstdir"
		$INSTALLSH $postremove $pkgrespfile >>$LOGFILE 2>&1
		rv=$?
	
		if [ $rv != 0 ]
		then	# some kind of error
			Log "$postremove, retval=$rv" 
			Log "### POSTREMOVE failed: $postremove"

			$RM -f $pkgrespfile
			return 1
		else
			Log "### POSTREMOVE completed."
		fi
	fi

	MarkUninstalled "$thepkg"
	$RM -f $pkgrespfile

	# collapse the quantum wave function
	return 0
}


## Uninstall packages for a product. returns 0 if packages were selected, 
##  and successfully removed, 1 if an error occured, 2 if canceled/nothing
##  removed.
##
function DoUninstall
{	# DoUninstall

	local argv=""
	local rmpkgs=""
	local installedpkgs=""

	local width=$DEFAULT_DWIDTH
	local height=$DEFAULT_DHEIGHT
	local cheight=10

	local arm
	local rv
	local -i count
	local name

	local respfile="$(CreateTF)"
	local prodinstdir="$INSTALLED_BASE/installed/$PRODUCT_INST_NAME"

	local ctitle="$(MessageCat 40 'Package Removal selection')"
	local ctext="$(MessageCat 41 '\nThe following packages have been installed for:\n %s.\nSelect the package(s) you would like to remove below.' \"$PRODUCT_NAME\")"
	local mtitle="$(MessageCat 42 'Not Installed')"
	local mtext="$(MessageCat 43 '\n\n\n%s is not installed on this machine.\n\n\n' \"$PRODUCT_NAME\")"
	local rctitle="$(MessageCat 44 'Confirm package removal')"
	local rctext=""	# filled in later

 	## see if the product is actually installed.
	if ! IsProdInstalled "$PRODUCT_INST_NAME"
	then	# not installed
		MessageBox "$mtitle" "$mtext"
		return 2
	fi

	## If something's installed, build a list of installed pkgs,
	##  and present 'an attractive user feedback element' ;-)
	## $PRODUCT_PKGS contains a list of valid packages for a product.
	## We'll loop through them, noting those that appear to be installed
	##  and building argv at the same time...

	## first init argv

	argv="--title \"$ctitle\" --msgcat $CATFILE $DIALOG_MSGSET --checklist \"$ctext\" $height $width $cheight"

	arm="off"		# always, for now

	for i in $PRODUCT_PKGS
	do
		if IsInstalled $i
		then	# it's installed

			installedpkgs="$installedpkgs $i"

			## get the product name and add to argv

			if [ ! -r "$prodinstdir/$i/pinfo" ]
			then
				Log "ERROR: package info file: $prodinstdir/$i/pinfo not found."
				return 1
			else
				. $prodinstdir/$i/pinfo
			fi

			# from the pinfo file
			name="$PKG_NAME"

			argv="${argv} \"$i\" \"$name\" $arm"
		fi
	done

	# display it
	eval "${DIALOG} ${argv}" 2>$respfile
	rv=$?

	if [ $rv != 0 ]
	then		# cancel
		return 2
	fi

	# here's the selected pkgs. 

	RESPONSE="$($CAT $respfile)"

	$RM $respfile

	if [ -z "$RESPONSE" ]
	then	# none selected
		return 2
	fi

	# remove the "'s...
	rmpkgs="$(echo $RESPONSE |$TR -d \")"

	# count the number to be removed, give user a last chance to cancel

	count=0
	for i in $rmpkgs
	do
		count=$(($count + 1))
	done

	rctext="$(MessageCat 45 '\n\nYou have selected %d package(s) for removal.\n\nProceed?' $count)"

	# Confirm
	if ! YesNoBox "$rctitle" "$rctext"
	then	# nope
		return 2
	fi

	## Ok... now we'll loop calling RemovePkg for each one selected

	for i in $rmpkgs
	do
		RemovePkg $i
		rv=$?

		if [ $rv -ne 0 ]
		then	# a problem
			Log "Removal of $i FAILED"
			return 1
		fi
	done

	return 0
}

	

####
#### MAIN - the 'guts' of this amazing script.
#############################################################################
#############################################################################

InitializeInstall

#### Find some useful executables
####  if any of these fail the script Abort's
#############################################################################

readonly AWK="$(type -path awk)"
readonly LS="$(type -path ls)"
readonly RM="$(type -path rm)"
readonly EXPR="$(type -path expr)"
if [ "$SYSTYPE" = "BSD_OS" ]
then
	readonly PAX="$(type -path pax)"
else
	readonly CPIO="$(type -path cpio)"
fi
readonly DF="$(type -path df)"
readonly MORE="$(type -path more)"

if [ "$INSTALL_MODE" = "patch" ]
then    # patching needs to get it's binaries of the cd-rom...
	if [ "$SYSTYPE" = "BSD_OS" ]
	then
		readonly DIALOG="$INSTALLSH $CD_SYSBASE/bin/dialogsh"
	else
		readonly DIALOG="$CD_SYSBASE/bin/dialog"
	fi

	BRANDY="$CD_SYSBASE/bin/brandy"
	PKGPERMS="$CD_SYSBASE/bin/pkgperms"
else
	if [ "$SYSTYPE" = "BSD_OS" ]
	then
		readonly DIALOG="$INSTALLSH $SYSBASE/bin/dialogsh"
	else
		readonly DIALOG="$SYSBASE/bin/dialog"
	fi

	BRANDY="$SYSBASE/bin/brandy"
	PKGPERMS="$SYSBASE/bin/pkgperms"
fi


readonly UNIQ="$(type -path uniq)"
readonly SORT="$(type -path sort)"
readonly TR="$(type -path tr)"
readonly MV="$(type -path mv)"
readonly CP="$(type -path cp)"
readonly CHOWN="$(type -path chown)"
readonly CHGRP="$(type -path chgrp)"
readonly CHMOD="$(type -path chmod)"
readonly FGREP="$(type -path fgrep)"
readonly GREP="$(type -path grep)"
readonly TOUCH="$(type -path touch)"
readonly CAT="$(type -path cat)"
readonly ED="$(type -path ed)"
readonly CUT="$(type -path cut)"
readonly RMDIR="$(type -path rmdir)"
readonly CHROOT="$(type -path chroot)"

if ! CheckCmd awk "$AWK"
then
	Abort
fi

if ! CheckCmd ls "$LS"
then
	Abort
fi

if ! CheckCmd rm "$RM"
then
	Abort
fi

if ! CheckCmd expr "$EXPR"
then
	Abort
fi

if [ "$SYSTYPE" = "BSD_OS" ]
then
	if ! CheckCmd pax "$PAX"
	then
		Abort
	fi
else

	if ! CheckCmd cpio "$CPIO"
	then
		Abort
	fi
fi

if ! CheckCmd df "$DF"
then
	Abort
fi

if ! CheckCmd more "$MORE"
then
	Abort
fi

# we won't do standard check for BSD_OS, we'll rely on the dialog test

if [ "$SYSTYPE" != "BSD_OS" ]
then
	if ! CheckCmd dialog "$DIALOG"
	then
		Abort
	fi
fi

if ! CheckCmd uniq "$UNIQ"
then
	Abort
fi

if ! CheckCmd sort "$SORT"
then
	Abort
fi

if ! CheckCmd mv "$MV"
then
	Abort
fi

if ! CheckCmd cp "$CP"
then
	Abort
fi

if ! CheckCmd chown "$CHOWN"
then
	Abort
fi

if ! CheckCmd chgrp "$CHGRP"
then
	Abort
fi

if ! CheckCmd chmod "$CHMOD"
then
	Abort
fi

if ! CheckCmd tr "$TR"
then
	Abort
fi

if ! CheckCmd fgrep "$FGREP"
then
	Abort
fi

if ! CheckCmd grep "$GREP"
then
	Abort
fi

if ! CheckCmd touch "$TOUCH"
then
	Abort
fi

if ! CheckCmd cat "$CAT"
then
	Abort
fi

if ! CheckCmd ed "$ED"
then
	Abort
fi

if ! CheckCmd cut "$CUT"
then
	Abort
fi

if ! CheckCmd rmdir "$RMDIR"
then
	Abort
fi

if [ "$PRODUCT_BRANDING" = "yes" ]
then    # only verify if this is a brandable product
	if ! CheckCmd brandy "$BRANDY"
	then
		Abort
	fi
fi

if ! CheckCmd pkgperms "$PKGPERMS"
then
	Abort
fi

if ! CheckCmd chroot "$CHROOT"
then
	Abort
fi

### export these
export AWK LS RM CPIO PAX DF MORE DIALOG UNIQ SORT TR MV CP CHOWN CHGRP CHMOD 
export FGREP GREP TOUCH CAT ED CUT RMDIR BRANDY PKGPERMS EXPR CHROOT

### First make sure we should be running, else walk...

if [ ! -z "$GOOD_EXEC" -a $GOOD_EXEC != 1 ]
then
	echo "$(MessageCat 32 'Please run Install/Uninstall instead of running this script directly.')"
	Abort 
fi

### Make sure some important variables are set...

if [ -z "$SYSTYPE" -o -z "$SYSBASE" -o -z "$INSTALL_BASE" -o -z "$INSTALL_PROG" -o -z "$PATH" -o -z "$INSTALLSH" -o -z "$INSTALL_ROOT" -o -z "$INSTALL_MODE" ]
then
	echo "$(MessageCat 33 '### ERROR: One of SYSTYPE, SYSBASE, INSTALL_BASE,\n           INSTALL_PROG, PATH, INSTALL_ROOT or INSTALLSH not\n           set properly.\n           Please run Install/Uninstall.' )"

	Abort
fi

Log "Off and running..."

# Let's see if TERMINFO is in /usr/share/terminfo (for RH5.1/FT users)
# 

if [ -d $INSTALL_ROOT/usr/share/terminfo ]
then
	export TERMINFO=$INSTALL_ROOT/usr/share/terminfo
fi

# let's not test dialog if we're doing an autoinstall.. or unpleasant screen
#  clearing when installing 'quiet' packages...

if [ -z "$AUTO_INSTALL" ]
then

	# lets see if TERM is correct.  Or at least correct enough not to gag...

	Log "Testing TERM via dialog --clear"

	$DIALOG --clear 

	if [ $? != 0 ]
	then	# somethings not right... bad TERM?
		echo "$(MessageCat 34 'ERROR: Could not test %s.  Is TERM set properly?' $DIALOG)"
		Abort
	fi
fi

### Some variables and initializations

# protect these - brought to you by Install and it's affiliates

readonly SYSTYPE
readonly SYSBASE
readonly INSTALL_BASE
readonly INSTALL_PROG
readonly INSTALL_ROOT
readonly INSTALL_MODE
readonly CD_BASE
readonly CD_SYSBASE
readonly AUTO_INSTALL

# Installation option configs

Log "Diskfree: $(DiskFree)KB"

### Let's re-route stderr to the LOGFILE here...
###  this will be explicitly overridden for dialog resp files...
exec 2>>$LOGFILE

# first check for a patch
if [ "$INSTALL_MODE" = "patch" ]
then	# a patch... make sure base product is there...

	if ! IsProdInstalled "$PRODUCT_INST_NAME"
	then	# not installed
		ptitle="$(MessageCat 53 '%s is not Installed' \"$PRODUCT_PATCHNAME\")"
		ptext="$(MessageCat 54 '\n\nYou must install %s before applying this patch.\n\n' \"$PRODUCT_PATCHNAME\")"

		MessageBox "$ptitle" "$ptext"
		DisplayInstallFailed
		Abort
	fi
fi

## if installing, print copyright and license, and present main menu.
if [ "$INSTALL_MODE" != "uninstall" ]
then	# installing/patching
	Log "Doing $INSTALL_MODE"

	# see if a start script exists, and if so run it.  If it fails
	#  we abort the [install|patch]

	startfile="$INSTALL_BASE/install/start"
	if [ -r "$startfile" ]
	then	# run it
		Log "### executing $startfile"

		CATSET=$START_MSGSET

		$INSTALLSH $startfile $STARTDATA
		rv=$?

		CATSET=$INSTALL_MSGSET

		if [ $rv != 0 ]
		then    # a problem
			Log "### $startfile failed."
			Abort
		fi
	else
		Log "### $startfile not found, not executing."
	fi

	if [ -z "$AUTO_INSTALL" ]
	then	# display these if not an auto_install
		if ! DisplayIntroFiles
		then	# refused license terms
			Abort
		fi
	fi

	REQUEST_DATA="$(CreateTF)"
	export REQUEST_DATA		# export so all the kiddies can see it


	# if AUTO_INSTALL is non-empty, then it should contain a list of
	#  packages to install directly without seeing the main menu/copyrights
	#  etc...

	if [ ! -z "$AUTO_INSTALL" ]
	then	# it's not empty...
		# call DoInstall directly for each of the packages
		DoInstall "$AUTO_INSTALL"
		rv=$?

		installedsomething=0
                case $rv in
	                0)      # success
				installedsomething=1
				;;
			1)	# failure
				DisplayInstallFailed
				;;
		esac

	else	# ! doing an AUTO_INSTALL

		# loop until exit, or finished install
		done=1
		installedsomething=0
		while [ $done -eq 1 ]
		do
			if DisplayMainMenu
			then		# OK pressed, something selected?
				case $RESPONSE in
					Quit)
						Log "### Exiting Installation"
						echo; echo
						exit 0
						;;
					Minimal)
						DoMinimal
						rv=$?
						case $rv in
							0)	# success
								installedsomething=1
								done=0
								;;
							1)	# failure
								DisplayInstallFailed
								continue
								;;
							2)	# cancel
								continue
								;;
						esac
						;;
					Express)
						DoExpress
						rv=$?
						case $rv in
							0)	# success
								installedsomething=1
								done=0
								;;
							1)	# failure
								DisplayInstallFailed
								continue
								;;
							2)	# cancel
								continue
								;;
						esac
						;;
					Custom)
						DoCustom
						rv=$?
						case $rv in
							0)	# success
								installedsomething=1
								done=0
								;;
							1)	# failure
								DisplayInstallFailed
								continue
								;;
							2)	# cancel
								continue
								;;
						esac
						;;
					Full)
						DoFull
						rv=$?
						case $rv in
							0)	# success
								installedsomething=1
								done=0
								;;
							1)	# failure
								DisplayInstallFailed
								continue
								;;
							2)	# cancel
								continue
								;;
						esac
						;;
					*)	# Red alert! Shields Up!
						echo "$(MessageCat 35 'ERROR: Internal error in case.')"
						Log "got invalid resp: RESPONSE = $RESPONSE"
						Abort
						;;
				esac
		
				Log "OK: rv = $? - Selection: $RESPONSE"
	
			else
				rv=$?
				if [ $rv != 1 ]
				then	# error 
					Log "DisplayMainMenu: $DIALOG exited with error ${rv}" 
				fi
	
				Abort
			fi
		done

	fi

	# if there's an 'success/failure' file, we'll call it depending on
	#  success or failure.

	if [ "$installedsomething" = "1" ]
	then
		endfile="$INSTALL_BASE/install/success"
		CATSET=$SUCCESS_MSGSET
	else
		endfile="$INSTALL_BASE/install/failure"
		CATSET=$FAILURE_MSGSET
	fi

	if [ -r "$endfile" ]
	then	# run it
		Log "### executing $endfile"

		$INSTALLSH $endfile $STARTDATA
		rv=$?
		CATSET=$INSTALL_MSGSET
		if [ $rv != 0 ]
		then    # a problem
			Log "### $endfile failed."
			Abort
		fi
	else
		Log "### $endfile not found, not executing."
		CATSET=$INSTALL_MSGSET
	fi

	if [ -z "$AUTO_INSTALL" ]
	then # not an autoinstall, don't need no steenking complete screen..
		MessageBox "$(MessageCat 36 'Installation of %s Complete' \"$PRODUCT_NAME\")" "$(MessageCat 37 '\nInstallation of the selected packages has completed successfully.\n\n')"
	fi

else # if uninstalling

	Log "Doing Uninstall..."

	DoUninstall
	rv=$?

	if [ $rv -ne 0 ]
	then	
		if [ $rv -ne 2 ]
		then	# not canceled - something 'undesirable' happened...
			MessageBox "$(MessageCat 48 'Package Removal Failed')" "$(MessageCat 49 '\n\nRemoval of selected packages failed.  Please see %s\nfor further information as to the reason for failure.\n\n' $LOGFILE)"
			exit 1
		fi
	else
		MessageBox "$(MessageCat 50 'Package Removal Succeeded')" "$(MessageCat 51 '\n\nRemoval of selected packages completed successfully.\n\n')"
	fi
fi

# remove any left over files...

$RM -f $INSTALL_ROOT/tmp/$$.*I.*

Log "The End"
echo;echo
exit 0


