#!/bin/sh 
#
# patcher.sh		-delivery system for patches on NT/HPUX/SOLARIS
#
# version: 1.0
# created: 19981028-cfriesen
# updated: 19990811-cfriesen
# 
#
# TODO list:
#	- shutdown nmserver and machd
#	- check if OPENSTEP apps are running
#
#


debug="${DEBUG_PATCHER- > /dev/null}"


#eval echo "executing: $0"
backup_suffix=OS
COMPRESS=Z			# gnutar compress flag: 'Z' or 'z' or ''
NO_ORIGINAL="File/Dir did not exist before patch."
ORIGINAL_FILE="Existed before patch."

# usage
usage="usage: patcher.sh -install <patch-tarfile> | -remove <patch-tarfile> [-next_root <dir>] [-backup_suffix <string>] -help"

if [ $# -eq 0 ]; then
	echo "$usage"
	exit 0
fi

eval echo  "Processing args." $debug
# process args
while [ $# -gt 0 ]; do
	case $1 in
		-help)
			echo ""
			echo "$usage"
			echo ""
			echo "Arguments \(must have -install or -remove\):"
			echo "	-install <tarfile>		# install patch from tarfile"
			echo "	-remove <tarfile>		# remove files in patch tarfile"
			echo "					#  restoring previous versions"
			echo "	-next_root <dir>		# specify if NEXT_ROOT env not set"
			echo "	-backup_suffix <string>		# used for backed-up files"
			echo "" 
	
			exit 0
			;;
		-install)
			eval echo "-install options entered" $debug
			shift
			if [ "$installPackage" ]; then
				echo "*** ERROR *** -install is multiply defined."
				exit 1
			fi
			
			if [ "$removePackage" ]; then
				echo "*** ERROR *** -install and -remove are exclusive."
				exit 1
			fi

			if [ $# -eq 0 ]; then
				echo "*** ERROR *** no package spefified after -install option."
				exit 1
			fi
			
			if [ -f "$1" ]; then
				installPackage=$1
			fi
			if [ -f "`pwd`/$1" ]; then
				installPackage=`pwd`/$1
			fi

			eval echo "installPackage: $installPackage" $debug
				
			shift
			;;
		-remove)
			eval echo "-remove options entered" $debug
			shift
			if [ "$removePackage" ]; then
                echo "*** ERROR *** -remove is multiply defined."
                exit 1
            fi
            
            		if [ "$installPackage" ]; then
                		echo "*** ERROR *** -install and -remove are exclusive."
                		exit 1
            		fi

            		if [ $# -eq 0 ]; then
                		echo "*** ERROR *** no package spefified after -remove option."
                 		exit 1
            		fi

			if [ -f "$1" ]; then
				removePackage=$1
			fi
			if [ -f "`pwd`/$1" ]; then
				removePackage=`pwd`/$1
			fi

			eval echo "-removePackage: $removePackage" $debug
			shift
			;;
		-next_root)
			shift
            if [ $# -eq 0 ]; then
                echo "*** ERROR *** no directory spefified after -next_root option."
                exit 1
            fi

			next_root=$1
			if [ `echo $next_root | grep  '^.:$'` ]; then
				$next_root=$next_root/
			fi
			if [ ! -d "$next_root" ]; then
				echo "*** ERROR *** NeXT root $next_root not found."
				exit 1
			fi
			if [ "$NEXT_ROOT" ]; then
				echo "*** WARNING *** NEXT_ROOT environment var ingored, using $next_root"
			fi
			shift
			;;
		-backup_suffix)
			shift
			if [ $# -eq 0 ]; then
                echo "*** ERROR *** no string spefified after -backup_suffix option."
                exit 1
            fi

			backup_suffix=$1
			shift
			;;
		*)
			echo "**** ERROR **** Invalid option: $1"
			echo $usage
			exit 1
        	;;
		esac
done

eval echo "Processing args done." $debug
if [ ! "$installPackage" -a ! "$removePackage" ]; then
	echo "*** ERROR *** Failed to specify -install or -remove option."
	exit 1
fi

eval echo "backup_suffix is $backup_suffix" $debug

# Locate NeXT root, might be defines in environment as $NEXT_ROOT
#
if [ ! "$next_root" ]; then
	if [ "$NEXT_ROOT" ]; then
		if [ ! -d "$NEXT_ROOT" ]; then
			echo "*** ERROR *** $NEXT_ROOT does not exist!"
			exit 1
		fi

		next_root=$NEXT_ROOT
	else
		if [ ! -d /NextLibrary ]; then
			echo "*** ERROR *** can not seem to find NeXT root, please specify using -next_root option."
			exit 1
		fi
		
		next_root=/
	fi
fi

eval echo "NEXT root is $next_root" $debug

# find gnutar, $next_root exists at this point
#
HAVEGNUTAR=0
if [ !  "$GNUTAR" ]; then
	for x in /usr/bin/gnutar /tmp/gnutar $next_root/NextDeveloper/bin/gnutar /usr/local/bin/gnutar; do
		#eval echo "does $x exist?" $debug
		if [ -f "$x" ]; then
			GNUTAR=$x
			HAVEGNUTAR=1
			break 
		fi
	done

# need to chech that which is in the path....
# better find it above as this will break on NT....
	if [ $HAVEGNUTAR -eq 0 ]; then
		which gnutar | grep  "no gnutar in"
		if [ $? -eq 0 ]; then
			echo "*** ERROR *** gnutar not in path."
			exit 1
		else
			eval echo "using which to define GNUTAR" $debug
			GNUTAR=`which gnutar`
		fi
	fi
fi


trash=`echo "$GNUTAR" | grep -s gnutar`
if [ $? -eq 1 ]; then
	echo "*** ERROR *** gnutar not in path"
	exit 1
fi

`cp $GNUTAR /tmp/gnutar`
GNUTAR=/tmp/gnutar
eval echo "found gnutar at $GNUTAR" $debug

if [ "$installPackage" ]; then
eval echo echo "entered installPackage" $debug

	backupFileListing=./patcher.tmp
	if [ -f "$backupFileListing" ]; then
		rm $backupFileListing
		if [ $? -ne 0 ]; then
			echo "*** ERROR *** couldn't remove file $backupFileListing"
			exit 1
		fi
	fi

	for fileEntry in `$GNUTAR ${COMPRESS}ft $installPackage | awk '{printf "%s ", $0}'  ` ; do
eval echo -n "$fileEntry" $debug
		if [ `echo $fileEntry | grep  '/$'` ]; then
			fileEntryType="dir"
		else
			fileEntryType="file"
			# might be a link...
		fi
		eval echo " is a $fileEntryType" $debug

		oldfile=`echo ${next_root}/${fileEntry} | sed 's^/$^^'`
		newfile=${oldfile}.$backup_suffix

		if [ -f "$oldfile" -o -h "$oldfile" ]; then
			eval echo "*** EXISTING FILE ***  $fileEntry" $debug
	
			if [ -f "$newfile" ]; then
				eval echo "*** WARNING *** file: $newfile exists ie already patched" $debug
			else

				if [ -f $oldfile  ]; then
					chmod a+rw $oldfile
				fi
				if [ "$fileEntryType" = "file" ]; then
					mv $oldfile $newfile
				fi
			fi

			if [ $? -ne 0 ]; then
				echo "*** ERROR *** could not move $oldfile to ${oldfile}.$backup_suffix"
				exit 1
			fi
		elif [ ! -d $oldfile ]; then
			# directory is not part of original distribution
			# need to make the directory hierarchy so we can make our
			# "back out" file
			if [ $fileEntryType = "dir" ]; then
				mkdir -p $oldfile
				chmod a+rw $oldfile
			fi

			echo $NO_ORIGINAL > "$newfile" # "backout file"
		fi
	done

	echo "No errors, extracting patch"

	
	COMPRESS=-$COMPRESS

	eval echo "COMPRESS: $COMPRESS" $debug
	eval echo "extracting patch..." $debug

	`$GNUTAR --directory $next_root ${COMPRESS} -x -f $installPackage `

	if [ $? -ne 0 ]; then
		echo "*** ERROR *** errors while installing patch"
		# restore .$backup_suffix files...
		# by doing uninstall.... just do it or call myself with -remove????
		if [ $? -ne 0 ]; then
			echo "*** WARING *** uninstall failed as well..."
			exit 1
		fi
	else
		echo ""
		echo "Install of $installPackage successful"

		# grok for special files that will require a reboot...
		# and notify.
	fi

	exit 0
fi

if [ "$removePackage" ]; then
echo "scanning files for backups..."

	# scan to make sure every file has a backup file
	#
	for fileEntry in `$GNUTAR ${COMPRESS}ft $removePackage | awk '{printf "%s ", $0 }'`; do
eval echo echo $fileEntry $debug
		oldfile=`echo ${next_root}/${fileEntry} | sed 's^/$^^'`
        newfile=${oldfile}.$backup_suffix
		how_many_failed=0
		how_many_not_writable=0
		
		if [ ! -d "$oldfile" ]; then
			if [ ! -f "$newfile" ]; then
				how_many_failed=1
				echo "*** WARNING *** $oldfile does not have backup file $newfile"
			elif [ ! -w "$oldfile" ]; then
				how_many_not_writable=1
				echo "*** ERROR *** do not have permission to overwrite $oldfile"
			fi
		fi
		
		
	done
		

		if [ $how_many_failed -gt 0 -o $how_many_not_writable -gt 0 ]; then
			if [ $how_many_failed -gt 0 ]; then	
				echo "*** ERROR *** file(s) did not have backups..."
			fi
			if [ $how_many_not_writable -gt 0 ]; then
				echo "*** ERROR *** file(s) were not writable..."
			fi
			if [ ! "$FORCE_REMOVAL" ]; then
				echo "		... aborting patch removal."
				exit 1
			else
				echo "*** WARNING *** -force option specified, removing patch anyway."
			fi
		fi

	

problem_with_removal=0
	
	# did scan, time to remove patch and restore files
	#
	for fileEntry in `$GNUTAR ${COMPRESS}ft $removePackage | awk '{printf "%s ", $0 }' ` ; do
			# since there would not be any system necessary file like a kernel
			# or some library used by OS, since we are making sure nmserver,
			# machd, or OS/WOF apps we can just remove the files or move the
			# backups.
			#

		#
		eval echo -n "Processing: $fileEntry" $debug
		if [ `echo $fileEntry | grep  '/$'` ]; then
			fileEntryType="dir"
		else
			fileEntryType="file"
		fi
		eval echo " is a $fileEntryType" $debug

		patch_file=`echo ${next_root}/${fileEntry} | sed 's^/$^^'`
		original_file=${patch_file}.${backup_suffix}

		if [ $fileEntryType = "dir" ]; then
			if [ -f "$original_file" ]; then
				eval echo "FILE: $original_file" $debug
				grep -s  "$NO_ORIGINAL" "$original_file"
				if [ $? -ne 0 ]; then
					echo "*** DEBRIS WARNING *** expected file: $original_file does not contain string: '$NO_ORIGINAL'"
					problem_with_removal=$problem_with_removal+1
				else
					# try to remove it since the patch put it in...
					#
					eval echo "removing: $original_file" $debug
					\rm -f "$original_file"
					if [ $? -ne 0 ]; then
						echo "*** DEBRIS WARNING *** could not remove $file_to_remove"
						problem_with_removal=$problem_with_removal+1
					fi	

					# making a list of dirs to remove later
					#dirsToRemove="$dirsToRemove $patch_file"
					#eval echo "dirs to remove:  $dirsToRemove" $debug
				fi
			
			else
				# it's a dir and has no backup file, so skip it
				continue
			fi
		
			
		elif [ $fileEntryType = "file" ]; then

			if [ ! -f "$original_file" ]; then
				if [ ! -f "$patch_file" -a "$FORCE" ]; then
					break 2
				else
					eval echo "*** ERROR *** backup file does not exit, use -force to ignore" $debug
					exit 1
				fi
				exit 1
			fi
				
			eval echo "-moving: $original_file to: $patch_file"
			\mv -f "$original_file" "$patch_file"
			if [ $? -ne 0 ]; then
				echo "*** REMOVE WARNING *** could not move original file: $original_file to $patch_file"
				echo "*** REMOVE WARNING *** patch file $patch_file not removed!"

				problem_with_removal=$problem_with_removal+1
			fi
			grep -s "$NO_ORIGINAL" "$patch_file"
			if [ $? -eq 0 ]; then
				echo "$NO_ORIGINAL : $patch_file" $debug
				\rm -f $patch_file
				if [ $? -ne 0 ]; then
					echo "*** DEBRIS WARNING *** could not remove file: $patch_file"
				fi
			fi
		fi
	done

	problem_with_removal=`echo $problem_with_removal | bc`

	if [ "$problem_with_removal" -ne 0 ]; then
		echo ""
		echo "*** ERROR *** TROUBLE REMOVING PATCH! SEE ABOVE FOR *** REMOVE WARNING ***"
		exit 1
	else
		if [ ! "$FORCE_REMOVAL" ]; then
			echo " Removed patch successfully. Start nmserver and machd at your leisure."
			exit 0
		else
			echo "*** WARNING *** -force options specified, see ERROR messages for possible problems."
			exit 1
		fi
	fi

fi

				
	
