#! /bin/sh
# smart patch applier
# Copyright (C) 1999, 2001  Henry Spencer.
# 
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
# 
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# patcher [-v] [-c] targetdir target [ key patchfile ] ...
# In targetdir, patch target from patchfile unless it already contains
# key and it appears to have been patched with the same patch.  (If the
# patch has changed, undo the old one and then put the new one in.)  Save
# original as target.preipsec, and patched copy as target.wipsec, with
# patch md5sum stored as target.ipsecmd5.  If the patch doesn't work,
# put the original back and save the patch attempt as target.mangled.
# If there are no key+patchfile pairs, undo any old patch and leave it
# at that.
# -v means verbose
# -c means do "patching" by appending rather than by using patch(1)

PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin
export PATH
umask 022

verbose=
modifier=patch
for dummy
do
	case "$1" in
	-v)	verbose=yes		;;
	-c)	modifier=cat		;;
	--)	shift ; break		;;
	-*)	echo "$0: unknown option \`$1'" >&2 ; exit 2	;;
	*)	break			;;
	esac
	shift
done
if test $# -lt 2
then
	echo "Usage: $0 [-v] [-c] targetdir target [ key patchfile ] ..." >&2
	exit 2
fi

need() {
	if test ! -f $1
	then
		echo "$0: cannot find file \`$1'" >&2
		exit 1
	fi
}

note() {
	if test "$verbose"
	then
		echo "* $1"
	fi
}

dir="$1"
target="$2"
shift ; shift
it=$dir/$target
need $it



patches=
if test ! -s $it.ipsecmd5
then
	# no records of patching...
	while test $# -ge 2
	do
		key="$1"
		patchfile="$2"
		shift ; shift
		need $patchfile

		if egrep -q "$key" $it
		then
			# patched but no record of how
			note "$it no longer needs patch $patchfile"
		else
			patches="$patches $patchfile"
		fi
	done
elif test ! -f $it.preipsec -o ! -f $it.wipsec
then
	echo "$0: $it.preipsec or .wipsec is missing!" >&2
	exit 1
else
	# determine whether patches have changed
	tmp=/tmp/patcher.$$
	>$tmp
	while test $# -ge 2
	do
		key="$1"
		patchfile="$2"
		shift ; shift
		need $patchfile
		md5sum $patchfile | awk '{print $1}' >>$tmp

		if egrep -q "$key" $it.preipsec
		then
			note "$it no longer needs patch $patchfile"
		else
			patches="$patches $patchfile"
		fi
	done
	if cmp -s $tmp $it.ipsecmd5
	then
		note "$it already fully patched"
		rm -f $tmp
		exit 0
	fi
	rm -f $tmp

	# must undo old patch(es)
	note "$it old patches must be undone, undoing them..."
	if ! cmp -s $it $it.wipsec
	then
		note "$it has changed, cannot undo old patches!"
		echo "$0: cannot unpatch $it, it has changed since patching" >&2
		exit 1
	fi
	rm $it
	mv $it.preipsec $it
	rm $it.wipsec $it.ipsecmd5
fi

# if no necessary patches, we're done
if test " $patches" = " "
then
	note "$it no longer needs patching"
	exit 0
fi

# try to figure out patch options
if test " $modifier" = " patch"
then
	if patch --help >/dev/null 2>/dev/null
	then
		# looks like a modern version
		popts='-p1 -b'
	else
		# looks like an old one
		popts='-p1'
	fi
fi

# do it
>$it.ipsecmd5
for patchfile in $patches
do
	note "applying $patchfile to $it..."

	# make local copy - this defeats hard and soft links
        mv $it $it.preipsec || exit 0
        rm -f $it
        cp -p $it.preipsec $it

	case "$modifier" in
	patch)	( cd $dir ; patch $popts ) <$patchfile	;;
	cat)	cat $patchfile >>$it			;;
	esac
	status=$?
	if test $status -ne 0
	then
		note "$it patch failed, restoring original"
		echo "$0: patch on $it failed!" >&2
		echo "$0: restoring original $it," >&2
		echo "$0: leaving patch attempt in $it.mangled" >&2
		mv $it $it.mangled
		mv $it.preipsec $it
		rm -f $it.ipsecmd5
		exit 1
	fi
	rm -f $it.orig		# some patch versions leave debris
	md5sum $patchfile | awk '{print $1}' >>$it.ipsecmd5
done
cp -p $it $it.wipsec
