:
#
#	@(#) std_funcs 12.6 92/12/14 
#
# **************************************************************************
#
#	Copyright (c) 1992		The Santa Cruz Operation, Inc.
#
#	All rights reserved.  No part of this program or publication may be
#	reproduced, transmitted, transcribed, stored in a retrieval system,
#	or translated into any language or computer language, in any form or
#	by any means, electronic, mechanical, magnetic, optical, chemical,
#	biological, or otherwise, without the prior written permission of:
#
#		The Santa Cruz Operation , Inc.		(408) 425-7222
#		400 Encinal St., Santa Cruz, California 95060 USA
#
# **************************************************************************
#
# set up the environment
PATH=/bin:/usr/bin:/etc
LANG=english_us.ascii
export LANG PATH

OLDDIR=/inst.sav/upgrade/old # upgrade save dir for displaced old files
NEWDIR=/inst.sav/upgrade/new # upgrade save dir for displaced new files
TMPDIR=/tmp # general work area for scripts
SAVEDIR=/tmp/up.sav # temporary save area for upgrade
PREPMDIR=/tmp/perms/instdir # message dir for prep scripts
INITMDIR=/messages # message dir for init scripts

# Exit status for scripts
OK=0 # success
FAIL=1 # error or failure
STOP=10 # stop installation of the component

# fixperm return code
FIXPERM_FULL=0 # fully installed
FIXPERM_PART=5 # partially installed
FIXPERM_NOT=4 # not installed
FIXPERM_BAD=3 # bad package or perm file name

UF1=""
UF2=""
UF3=""
UF4=""
UF5=""
UF6=""
UF7=""
UF8=""
UF9=""
filelist=""

# getyn - prompt for yes or no answer
#
# Usage: getyn "question" [ "more question" ... ]
# returns $OK on 'y' or 'Y'
# returns $FAIL on 'n' or 'N'
# loops on any other response
#
getyn() {
	while	:
	do	term_echo "\n$* (y/n) \c"
		read yn rest
		case $yn in
		[yY])	return $OK 				;;
		[nN])	return $FAIL				;;
		*)	term_echo "Please answer y or n"	;;
		esac
	done
}

# prompt - prompt with mesg
#
# Usage: prompt [ "default value" ]
# requires: variable mesg to be set to some message
# returns $FAIL on receiving a 'q' or 'Q'
# returns $OK on any other response
# returns $OK and sets cmd to the default value on 'return'
# notes: always sets 'cmd' to the value returned by the user or the
#        default value
# notes: responses beginning with a '!' are evaluated by the shell
# notes: responses of '-x' and '+x' cause a 'set -x' and 'set +x'
#
prompt() {
	while	:
	do	term_echo "\n${mesg}or enter 'q' to quit: \c"
		read cmd
		case $cmd in
		+x|-x)	set $cmd					;;
		Q|q)	return $FAIL					;;
		!*)	eval `expr "$cmd" : "!\(.*\)"`			;;
		"")	# If there is an argument use it as the default
			# else loop until 'cmd' is set
			[ "$1" ] && { 
				cmd=$1
				return $OK
			}
			: continue
			;;
		*)	return $OK					;;
		esac
	done
}

# error - print an error message to a log and to standard error.
#
# Usage: error [ -l<logfile> ] "message"
# Arguments are an optional logfile name (or use a global var $LOGFILE) 
#       and a quoted error message
# Notes: error always returns $FAIL.
#
error() {
	# user can provide a logfile either in an argument
	# or as an environment variable: "LOGFILE"
	case $1 in
		-l*) LOGFILE=`expr "$1" : "-l\(.*\)"`
		     shift
		     ;;
	esac
	
	# put "ERROR" at the beginning of each line, for grep convenience
	estring=`echo "$*" | sed 's/^/ERROR: /'`
	[ $LOGFILE ] && {
		# if the directory's not there, appending the file is fatal
		[ -x /bin/dirname ] && LOGDIR=`dirname $LOGFILE`
		[ -z "$LOGDIR" -o -d "$LOGDIR" ] && {
			echo "\n$estring" >> $LOGFILE
		}
	}

	echo "\n\n$estring\n\n" >&2
	return $FAIL
}

# message - print a message
#
# Usage: message [ -l<logfile> ] "message"
# Notes: message always returns $OK.
# Arguments are an optional logfile name (or use a global var $LOGFILE) 
#       and a quoted warning message
#
message() {
	# user can provide a logfile either in an argument
	# or as an environment variable: "LOGFILE"
	case $1 in
		-l*) LOGFILE=`expr "$1" : "-l\(.*\)"`
		     shift
		     ;;
	esac
	[ $LOGFILE ] && {
		# if the directory's not there, appending the file is fatal
		# no dirname there for initial install, try anyway
		[ -x /bin/dirname ] && LOGDIR=`dirname $LOGFILE`
		[ -z "$LOGDIR" -o -d "$LOGDIR" ] && {
			echo "\n$*" >> $LOGFILE
		}
	}
	
	echo "$*"
	return $OK
}


# warning - print a warning message
#
# Usage: warning [ -l<logfile> ] "message"
# Arguments are an optional logfile name (or use global var $LOGFILE) 
#       and a quoted warning message
# Notes: warning always returns $FAIL.
#
warning() {
	# user can provide a logfile either in an argument
	# or as an environment variable: "LOGFILE"
	case $1 in
		-l*) LOGFILE=`expr "$1" : "-l\(.*\)"`
		     shift
		     ;;
	esac
	wmessage=`echo "$*" | sed 's/^/WARNING: /'`
	[ $LOGFILE ] && {
		# if the directory's not there, appending the file is fatal
		# no dirname there for initial install, try anyway
		[ -x /bin/dirname ] && LOGDIR=`dirname $LOGFILE`
		[ -z "$LOGDIR" -o -d "$LOGDIR" ] && {
			echo "\n$wmessage" >> $LOGFILE
		}
	}
	echo "\n\n$wmessage\n\n" >&2
	return $FAIL
}

# log - log a message to a log file, not to the screen
#
# Usage: log [ -l<logfile> ] "message"
# Arguments are an optional logfile name (or use global var $LOGFILE) 
#       and a quoted message
# Notes: log always returns $OK.
#
log() {
	# user can provide a logfile either in an argument
	# or as an environment variable: "LOGFILE"
	case $1 in
		-l*) LOGFILE=`expr "$1" : "-l\(.*\)"`
		     shift
		     ;;
	esac
	[ $LOGFILE ] && {
		# if the directory's not there, appending the file is fatal
		# no dirname there for initial install, try anyway
		[ -x /bin/dirname ] && LOGDIR=`dirname $LOGFILE`
		[ -z "$LOGDIR" -o -d "$LOGDIR" ] && {
			echo "\n$*" >> $LOGFILE
		}
	}
	return $OK
}

# term_echo - print an message to the terminal
#
# Usage: term_echo "message"
# Argument is a quoted message
# Notes: term_echo always returns the used file descriptor
#
term_echo() {
        _te_terminal=1
        while [ ! -t $_te_terminal ]
        do
                _te_terminal=`expr $_te_terminal + 1`
        done
	echo "$*" >&${_te_terminal}
	return $_te_terminal
}

# exec_shell - abort the current program and start up a shell for the user
#
# Usage: exec_shell
#
exec_shell()
{
	echo "Shell escape.  System will halt when this shell exits."
	trap 1 2 3 15
	PATH=/bin:/etc:/usr/bin:/mnt/bin:/mnt/etc:/mnt/usr/bin
	PS1="<Installation> "
	HOME=/
        export HOME PS1 PATH
	sh -i
	sync 
        sync
        haltsys
}

#############################
# Upgrade specific routines #
#############################
# The following routines are provided to perform save/restore tasks
# when upgrade installation is being performed.
# The routines can only be used if a stanza file has been set up.
# The stanza file must have the following format:
# 	PKG:filepath:FORMAT:chk1:chk2:chk3
# Where,
#	PKG is the name of package the conf file belongs to
#	filepath is the full path of the conf file starting with ./
#	FORMAT is 'C' if format of file is changed, otherwise its 'U'
#	chk1, chk2, chk3 are checksums (sum -r) of conf file, (max of 6)


# ugetfields - set up shell varaibles with values of the fields in an entry
# Usage: ugetfields <entry>
# <entry> is of the form : PKG:filepath:FORMAT:chk1:chk2:chk3...:chk6
# Returns: UF1..UF9
#
ugetfields(){

	#reset field variables
	UF1=""
	UF2=""
	UF3=""
	UF4=""
	UF5=""
	UF6=""
	UF7=""
	UF8=""
	UF9=""
	eval `echo "$*" | awk '{
		nf=split($0,ff,":");
		for(j=1;j<=nf;j++)
			print "UF" j "=" ff[j];
	}'`
}

#
# ugetfile - lists filenames in the specified stanza file to stdout
# Usage: ugetfile <stanza.file> [<pkg>]
# if pkg list is specified then only files in the packages are listed
#
ugetfile()
{
	tmpf=/tmp/xx.$$
	rm -f $tmpf
	stanza=$1
	shift
	pkg="$*"
	[ -f "$stanza" ] || return $FAIL
	[ "xx$pkg" = "xx" ] && {
		# list all filenames in stanza file
		tmpf=$stanza
	} || {
		# list filenames of specified pkgs
		for p in "$pkg"
		do
			grep "^$p:" $stanza >>$tmpf
		done
	
	}
	awk ' BEGIN { FS=":" }
		{ print $2 } ' $tmpf
	rm -f $tmpf
	return $OK
}

# usaveconf - Saves conf files to save dir
# Usage: usaveconf <stanza.file> [<pkg>]
# returns OK if save completes successfully
# otherwise returns FAIL 
#
usaveconf()
{

	stanza=$1
	shift
	pkg="$*"

	[ -f "$stanza" ] || return $FAIL
	[ "xx$pkg" = "xx" ] && {
		ugetfile "$stanza" |cpio -pdum /$SAVEDIR >/dev/null 2>&1
	} || {
		for p in $pkg
		do
			grep "^$p:" "$stanza" >/dev/null 2>&1 && {
				ugetfile "$stanza" "$p" | \
					cpio -pdum /$SAVEDIR >/dev/null 2>&1
			} || {
				# Error package not found
				continue	#ignore packages not found
			}
		done
	}
	return $OK
}

# urestoreconf - resotres files from save dir
# Usage: urestoreconf <stanza file> [<pkg>]
# Returns : list of files that are marked as "C" for changed format 
#
urestoreconf()
{

	stanza=$1
	shift
	pkg="$*"
	filelist="" # list of files that have changed format
	tmpf=/tmp/xx.$$
	rm -f $tmpf

	[ -f "$stanza" ] || return $FAIL
	[ "xx$pkg" = "xx" ] && {
		# handle all filenames in stanza file
		tmpf=$stanza
	} || {
		# handle only filenames of specified pkgs
		for p in "$pkg"
		do
			grep "^$p:" $stanza >>$tmpf
		done
	
	}

	while read entry
	do
		ugetfields $entry
		[ -f $SAVEDIR/$UF2 ] || continue #ignore non saved files 

		if [ "$UF3" = "U" ]
		then
			# file has unchanged format
			if [ "xx$UF4" != "xx" ]
			then
				# there is a checksum list
				# check all checksums
				matched=0
				ss=`sum -r $SAVEDIR/$UF2| awk ' {print $1}'`
				for s in $UF4 $UF5 $UF6 $UF7 $UF8 $UF9
				do
					[ "$ss" = "$s" ] && {
						# checksum matched
						# use new version of file
						# and delete old version
						matched=1
						rm -f $SAVEDIR/$UF2
						break
					}
				done
				[ $matched -eq 0 ] && {
					# checksums did not match
					# move new version to NEWDIR
					# and restore the saved file
					echo "/$UF2" |cpio -pdum $NEWDIR >/dev/null 2>&1
					cp $SAVEDIR/$UF2 /$UF2
				}
			else
				# no checksum list, assume file changed
				# move new version to NEWDIR
				# and restore the saved file	
				echo "/$UF2" |cpio -pdum $NEWDIR >/dev/null 2>&1
				cp $SAVEDIR/$UF2 /
			fi

		else
			# file has changed format
			if [ "xx$UF4" != "xx" ]
			then
				# there is a checksum list
				# check all checksums
				matched=0
				ss=`sum -r $SAVEDIR/$UF2|awk '{print $1}'`
				for s in $UF4 $UF5 $UF6 $UF7 $UF8 $UF9
				do
					[ "$ss" = "$s" ] && {
						# checksum matched
						# use new version of file
						# and delete old version
						matched=1
						rm -f $SAVEDIR/$UF2
						break
					}
				done
				[ $matched -eq 0 ] && {
					# checksums did not match
					filelist="$filelist $UF2"
				}
			else
				# no checksum list, assume file changed
				filelist="$filelist $UF2"
			fi

		fi
	done <$tmpf 
	return $OK
}
# Always executed before running main program

cd /
