#!/bin/ksh

#############################################################
## The following functions are included in this file and
## are listed in alphabetical order:
##
##		build_apply_list
##		check_flags
##		check_user
##		cleanup
##		create_liblpp
##		create_package
##		debug_switch
##		initialize_log
##		initialize_variables
##		list_only
##		log_print
##		manage_cd
##		mount_apply_list
##		print_usage
##		validate_argument
##		verify_device
##		verify_file
##		verify_hypertext
##		
## The main script is the last routine of this file.
#############################################################


######################### build_apply_list #############################
##
## Name:  build_apply_list
##
## Function:  The build_apply_list function is responsible for creating
##            the master apply list that will be used to create
##            temporary file mounts for the new package.
##
## Parameters:  None
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

build_apply_list()
{

#############################################################
## Remove existing master list and create a new list
#############################################################

	> /tmp/master.al
	cat *.al >> /tmp/master.al

} # end of build_apply_list()


######################### check_flags ##################################
##
## Name:  check_flags
##
## Function:  The check_flags function is responsible for checking the
##            accuracy of the set command flags.
##
## Parameters:  None
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

check_flags()
{

#############################################################
## Check for zero arguments passed to the command
#############################################################

	if [ -z "$ARGS" ]; then
		MESG="$CMDNAME: No argument passed with command."
		log_print "$MESG"
		print_usage
		exit 1
	fi

#############################################################
## Check for list flags accuracy and call the list routine.
## The bffcreate command will provide a package listing and
## the sm_inst command will provide a package and fileset
## description listing.
#############################################################

	if [ $BFFCREATE_LIST_FLAG -eq $ON ]; then
		if [ $DEVICE_FLAG -eq $OFF ]; then
			MESG="$CMDNAME: option -d <device> must be specified."
			log_print "$MESG"
			print_usage
			exit 1
		else
			list_only
		fi
	elif [ $SM_INST_LIST_FLAG -eq $ON ]; then
		if [ $DEVICE_FLAG -eq $OFF ]; then
			MESG="$CMDNAME: option -d <device> must be specified."
			log_print "$MESG"
			print_usage
			exit 1
		else
			list_only
		fi
	fi

#############################################################
## Ensure that -d and -t flags are set for normal operation
#############################################################

	if [ $DEVICE_FLAG -eq $OFF -o $TARGET_DIR_FLAG -eq $OFF ]; then
		MESG="$CMDNAME: option -d and -t are required."
		log_print "$MESG"
		print_usage
		exit 1
	fi

} # end of check_flags()


######################### check_user ###################################
##
## Name:  check_user
##
## Function:  The check_user function is responsible for checking the
##            user ID of the person calling the command.  If the
##            user is not root, the command will fail.
##
## Parameters:  None
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

check_user()
{

	if [[ $(whoami) != root ]]; then
		MESG="$CMDNAME: You must be a root user to run this command."
		log_print "$MESG"
		exit 1
	fi

} # end of check_user()


######################### cleanup ######################################
##
## Name:  cleanup
##
## Function:  The cleanup function is responsible for cleaning after
##            the command.  This would include umounting the cd and
##            removing temporary mounts and files.
##
## Parameters:  None
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

cleanup()
{

	MESG="Cleaning up after $CMDNAME command ..."
	log_print "$MESG"

	for MOUNT_POINT in `mount | grep $TARGET_DIR | grep cdrfs | \
		awk '{print $2}' | sort -r`
	do
		umount -f $MOUNT_POINT 2>$MSG_BUF 1>/dev/null
		if [ "$?" -ne 0 ]; then
			log_print $MSG_BUF
			MESG="$CMDNAME: Error umounting $MOUNT_POINT."
			log_print "$MESG"
		fi
	done

	if [ $CDROM_FLAG -eq $ON ]; then
		umount -f /hyper_cd 2>$MSG_BUF 1>/dev/null
		if [ "$?" -ne 0 ]; then
			log_print $MSG_BUF
			MESG="$CMDNAME: Error unmounting $CDROM."
			log_print "$MESG"
		fi
		rm -rf /hyper_cd
	fi

	cd /
	rm -rf $TARGET_DIR/tmp
	rm -f $MSG_BUF /tmp/master.al

} # end of cleanup()


######################### create_liblpp ################################
##
## Name:  create_liblpp
##
## Function:  The create_liblpp function is responsible for creating
##            the liblpp for the new package.
##
## Parameters:  None
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

create_liblpp()
{

	rm *.cfginfo
	rm ../liblpp.a

	MESG="Creating a new liblpp.a archive ..."
	log_print "$MESG"

	ar vq ../liblpp.a *

	cd ..
	rm -rf tmp

} # end of create_liblpp()


######################### create_package ###############################
##
## Name:  create_package
##
## Function:  The create_package function is responsible for creating
##            the new hypertext package.  This is accomplished by
##            executing a backup of the mounted cd files and including
##            the new liblpp.a.
##
## Parameters:  None
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

create_package()
{

	MESG="Creating $PKG_NAME package ..."
	log_print "$MESG"

	cd $TARGET_DIR/tmp
	find . -print | backup -ipq -f $TARGET_DIR/${PKG_NAME}.bff

	MESG="Refreshing the table of contents (.toc) in $TARGET_DIR ..."
	log_print "$MESG"
	inutoc $TARGET_DIR

} # end of create_package()


######################### debug_switch #################################
##
## Name:  debug_switch
##
## Function:  The debug_switch function is responsible for toggling the
##            debug functionality of the command.  If the debug
##            switch is on, then the shell debug capability will be
##            used.  The function was also written to allow for
##            debugging to be turned off.
##
## Parameters:  ON/OFF
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

debug_switch()
{

	if [ $1 -eq $ON ]; then
		MESG="Debug on ..."
		log_print "$MESG"

		set -x   # turn on command and function tracing
		for i in $(typeset +f)
		do
			typeset -ft $i
		done
	else
		MESG="Debug off ..."
		log_print "$MESG"

		set +x   # turn off command and function tracing
	fi

} # end of debug_switch()


######################### initialize_log ###############################
##
## Name:  initialize_log
##
## Function:  The initialize_log function is responsible for creating
##            the initial entry, for the command log.  The inital entry
##            includes the date/time of execution, as well as the exact
##            command executed.
##
## Parameters:  command line string (optional)
##  
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

initialize_log()
{

	echo "##################################################" > $LOGFILE
	echo $(date) >> $LOGFILE
	echo "cmd: ${CMDNAME} $*" >> $LOGFILE

} # end of initialize_log()


######################### initialize_variables #########################
##
## Name:  initialize_variables
##
## Function:  The initialize_variables function is responsible for
##            setting or initializing the command variables.
##
## Parameters:  None
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

initialize_variables()
{

##########################################################
## All variables that need initialization or defining.
##########################################################

	OFF=0
	ON=1

	DIRECTORY=1
	IMAGE=2

	ARGS=$*
	CMDNAME=`basename $0`
	DEVICE=
	IMAGE_NAME=
	LOGFILE=/var/adm/ras/$CMDNAME.log; >$LOGFILE
	MSG_BUF=/tmp/$CMDNAME.buf; >$MSG_BUF
	TARGET_DIR=

##########################################################
## Flags
##########################################################

	BFFCREATE_LIST_FLAG=$OFF
	CDROM_FLAG=$OFF
	DEVICE_FLAG=$OFF
	SM_INST_LIST_FLAG=$OFF
	TARGET_DIR_FLAG=$OFF

} # end of initialize_variables()


######################### list_only ####################################
##
## Name:  list_only
##
## Function:  The list_only function is responsible for determining the
##            type of list to display and calling the appropriate
##            routine.
##
## Parameters:  None
##
## Returns:  None
##
## Calling Routine:  check_flags
##
########################################################################

list_only()
{

##########################################################
## The bffcreate command is used to display a list
## of packages on the specified device
##########################################################

	if [ $BFFCREATE_LIST_FLAG -eq $ON ]; then
		/usr/sbin/bffcreate -l -d $DEVICE
		exit 0

##########################################################
## The sm_inst command is used to display a list
## of packages and associated fileset descriptions
##########################################################

	elif [ $SM_INST_LIST_FLAG -eq $ON ]; then
		/usr/lib/instl/sm_inst list_filesets -l -f _all_available -d $DEVICE
		exit 0
	fi


} # end of list_only()


######################### log_print ####################################
##
## Name:  log_print
##
## Function:  The log_print function is responsible for printing
##            information to the command log, as well as the
##            standard output.
##
## Parameters:  print string
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

log_print()
{

##########################################################
## This routine will be used for printing progress
## information to the standard output and the log file.
##########################################################

    echo "$*" | tee -a $LOGFILE

} # end of log_print()


######################### manage_cd ####################################
##
## Name:  manage_cd
##
## Function:  The manage_cd function is responsible for mounting the
##            cdrom device and redefining the device to the image
##            location under the /hyper_cd mount point.
##
## Parameters:  None
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

manage_cd()
{

	if [ $CDROM_FLAG -eq $ON ]; then
		mkdir -p /hyper_cd
		mount -v'cdrfs' -r /dev/${CDROM} /hyper_cd 2>$MSG_BUF
		if [ $? -ne 0 ]; then
			log_print $MSG_BUF
			MESG="$CMDNAME: Error mounting CDROM $CDROM.\n"
			MESG="$MESG  Device may already be mounted."
			log_print "$MESG"
			cleanup
			exit 1
		fi
	fi

	DEVICE=/hyper_cd/usr/sys/inst.images

} # end of manage_cd()


######################### mount_apply_list #############################
##
## Name:  mount_apply_list
##
## Function:  The mount_apply_list function is responsible for
##            creating directories and mounting the cd source files,
##            from the master apply list.
##
## Parameters:  None
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

mount_apply_list()
{

	MESG="Creating temporary mounts for backup ..."
	log_print "$MESG"

##########################################################
## Each file will be located directly under the original
## mount point.  For example: 
## /usr/share/man/info/en_US/a_doc_lib/aixbman/baseadmn/toc.htm
## will be located under the mount point (i.e. /hyper_cd)
## and relative to /hyper_cd/usr/sys/inst.images/../../..
##########################################################

	for EACH_FILE in `cat /tmp/master.al`
	do
		if [[ -d $DEVICE/../../../$EACH_FILE ]]; then
			mkdir -p $TARGET_DIR/tmp/$EACH_FILE
		elif [[ -f $DEVICE/../../../$EACH_FILE ]]; then

			if [[ ! -d $TARGET_DIR/tmp/${EACH_FILE%%/*([!/])*(/)} ]]; then
				mkdir -p $TARGET_DIR/tmp/${EACH_FILE%%/*([!/])*(/)}
			fi

			touch $TARGET_DIR/tmp/$EACH_FILE
			mount -v'cdrfs' -r $DEVICE/../../../$EACH_FILE \
				$TARGET_DIR/tmp/$EACH_FILE
		fi
	done

} # end of mount_apply_list()


######################### print_usage ##################################
##
## Name:  print_usage
##
## Function:  The print_usage function is responsible for printing a
##            usage message to the command log and the standard output.
##
## Parameters:  None.
##
## Returns:  0 - for successful completion
##
## Calling Routine:  main
##
########################################################################

print_usage()
{

	USAGE="Usage $CMDNAME: To create a backup file format from hypertext image:\n\
      $CMDNAME -d <device> -t <target directory> [-D] <image name>\n\
\n\
Usage $CMDNAME: To display a package list (-l):\n\
      $CMDNAME -l -d <device>\n\
\n\
Usage $CMDNAME: To display a fileset description list (-L):\n\
      $CMDNAME -L -d <device>\n"
	log_print "$USAGE"
																			    
} # end of print_usage()


######################### validate_argument ############################
##
## Name:  validate_argument
##
## Function:  The validate_argument function is responsible for ensuring 
##            that if an option requires a following argument, the
##            argument does not begin with a dash "-".  This is really
##            a check for a missing argument.  Since the getopts shell
##            function doesn't care what the following argument is, we
##            check for a dash, which is really the next command option.
##
## Parameters:  option, argument
##              option - the option flag passed to the command.
##              argument - The required argument for the specified option.
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

validate_argument()
{

	if [[ $2 = -* ]]; then
		MESG="$CMDNAME: Bad argument: $2 for option -$1."
		log_print "$MESG"
		print_usage
		exit 1
	fi

} # end of validate_argument()


######################### verify_device ################################
##
## Name:  verify_device 
##
## Function:  The verify_device function is responsible for checking
##            the existence of a device.
##
## Parameters:  device
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

verify_device()
{

#############################################################
## First check to make sure the device is not a cdrom.  This
## could be given with /dev/cd# or cd#.  Then, if the device
## is not a directory ( directory or mounted filesystem)
## then assume an error has occurred.
#############################################################

	if [ ! -b "$DEVICE" -a ! -b "/dev/$DEVICE" ]; then
		if [ ! -d "$DEVICE" ]; then
			MESG="$CMDNAME: Device $DEVICE does not exist or is not available."
			log_print "$MESG"
			exit 1
		fi
	else

#############################################################
## If the device is a cdrom, determine if it really exists
## and if it is in a working state.
##                 (like Texas or Oklahoma ;-)
#############################################################

		CDROM=`lsdev -Cccdrom | grep -w $(basename $DEVICE) | grep Available | \
			awk ' {print $1}'`
		DEVICE=/dev/$CDROM
		if [ -z "$CDROM" ]; then
			MESG="$CMDNAME: Device $DEVICE does not exist or is not available."
			log_print "$MESG"
			exit 1
		else
			if [ `2>/dev/null <$DEVICE; echo $?` -eq 1 ]; then
				MESG="$CMDNAME: Device $DEVICE is not in a working state."
				log_print "$MESG"
				exit 1
			fi
			CDROM_FLAG=$ON
		fi
	fi

} # end of verify_device()


######################### verify_file ##################################
##
## Name:  verify_file
##
## Function:  The verify_file function is responsible for checking for
##            the existence of an image file or directory.
##
## Parameters:  type, file
##              type - DIRECTORY - check target directory
##                   - IMAGE - check image file
##              file - the path/name of the directory of image file
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

verify_file()
{
  
	if [ $1 -eq $DIRECTORY ]; then
		if [ ! -d "$2" ]; then
			MESG="$CMDNAME: Directory $2 does not exist."
			log_print "$MESG"
			exit 1
		fi
	elif [ $1 -eq $IMAGE ]; then
		if [ -z "$2" ]; then
			MESG="$CMDNAME: No image file name provided."
			log_print "$MESG"
			print_usage
			cleanup
			exit 1
		elif [ ! -s "$DEVICE/$2" -a ! -s "$DEVICE/$2.hypertext" ]; then
			MESG="$CMDNAME: Image file $2 does not exist."
			log_print "$MESG"
			cleanup
			exit 1
		fi
	fi

	return 0

} # end of verify_file()


######################### verify_hypertext #############################
##
## Name:  verify_hypertext 
##
## Function:  The verify_hypertext function is responsible for checking
##            the liblpp.a archived .cfginfo files for the HYPERTEXT
##            keyword.  This indicates the package is a hypertext
##            image.
##
## Parameters:  None
##
## Returns:  None
##
## Calling Routine:  main
##
########################################################################

verify_hypertext()
{

#############################################################
## Remove the ".hypertext" extension from the package name
#############################################################

	PKG_NAME=${IMAGE_NAME%\.hypertext}
	IMAGE_NAME=${PKG_NAME}.hypertext

	mkdir -p $TARGET_DIR/tmp   # build a temp directory for restore
	cd $TARGET_DIR/tmp

#############################################################
## Restore the image liblpp.a file and check the .cfginfo
## file for the HYPERTEXT keyword
#############################################################

	restore -xqvf $DEVICE/$IMAGE_NAME

	mkdir -p ./usr/lpp/$PKG_NAME/tmp
	cd ./usr/lpp/$PKG_NAME/tmp

	MESG="Unarchiving liblpp.a ..."
	log_print "$MESG"

	ar xv ../liblpp.a

	for CFGFILE in `ls *.cfginfo`
	do
		if [ `grep -c HYPERTEXT $CFGFILE` -eq 0 ]; then
			MESG="$CMDNAME: $IMAGE_NAME is not a hypertext image file."
			log_print "$MESG"
			cleanup
			exit 1
		fi
	done

} # end of verify_hypertext()


######################### main #########################################


initialize_variables $*  # initialize and set command variables

check_user  # verify the user is root

initialize_log $*  # initailize the command log with date and command syntax

#############################################################
## Command Line Option Handling
## Validate and verify arguments, files, and devices
#############################################################

while getopts ":d:t:lLD" OPTION
do
	case $OPTION in
		d)  DEVICE=$OPTARG
			validate_argument $OPTION $DEVICE
			verify_device
			DEVICE_FLAG=$ON;;
		t)  TARGET_DIR=$OPTARG
			validate_argument $OPTION $TARGET_DIR
			verify_file $DIRECTORY $TARGET_DIR
			TARGET_DIR_FLAG=$ON;;
		l)  BFFCREATE_LIST_FLAG=$ON;;
		L)  SM_INST_LIST_FLAG=$ON;;
		D)  debug_switch $ON
			validate_argument $OPTION;;
	   \?)  shift $(($OPTIND -2))
			MESG="$CMDNAME: Unknown command line option $1."
			log_print "$MESG"
			print_usage
			exit 1;;
	esac
done
shift $(($OPTIND - 1))
IMAGE_NAME=$1

check_flags  # check command flags for accuracy

manage_cd  # f necessary, mount hypertext cdrom

verify_file $IMAGE $IMAGE_NAME  # check if file exists

verify_hypertext  # unarchive liblpp.a and check for HYPERTEXT in *.cfginfo file

build_apply_list  # create the master apply list

create_liblpp  # create the new liblpp.a for the package

mount_apply_list  # temporarily mount the master apply list

create_package  # create the new hypertext package

cleanup  # clean up the temporary files and mounts

exit 0

######################## end of main ##################################
