#!/bin/sh
#
# IRC - Internet Relay Chat, ircd-patch
# Copyright (C) 2002 Alex Badea <vampire@p16.pub.ro>
#
# 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 1, or (at your option)
# any later version.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# $Id: ircd-patch,v 1.5.2.1 2005-12-29 03:41:56 entrope Exp $
#
#
# Experimental centralized patch system for ircu
# Run with no arguments to get help.
#
# Return codes:
#   0 - success
#   1 - at least one live patch failed
#   2 - at least one simulation (dry run) failed 
#   3 - invalid arguments (i.e. no such patch)
#   4 - invalid operation (i.e. tried to apply when already applied)
#

DIFFS=patches/diffs
MARKS=patches/marks
PLIST_FILE=include/patchlist.h

retcode=0
force=0


PLIST=""
for fname in $DIFFS/*.diff ; do
	name=`basename $fname | sed -e 's/\.diff//'`
	PLIST="$PLIST $name"
done

update_patchlist() {
	list=""
	for name in $PLIST ; do
		test -f $MARKS/$name && list="$list.$name"
	done
	echo "/* This file was automatically generated by ircd-patch */" > $PLIST_FILE
	echo "#define PATCHLIST \"$list\"" >> $PLIST_FILE
	echo "Updated $PLIST_FILE"
}

test -d $DIFFS || (echo "*** Missing $DIFFS, creating it" ; mkdir -p $DIFFS)
test -d $MARKS || (echo "*** Missing $MARKS, creating it" ; mkdir -p $MARKS)

dry_run() {
	rejects=`patch -p0 -N -t --dry-run $2 < $1 | grep "hunk FAILED" | sed -e 's/.*to file /  /;s/\.rej$//'`
	test -z "$rejects"
}

patch_list() {
	echo "Available patches (* marks applied patches):"
	for name in $PLIST ; do
		test -f $MARKS/$name && echo -n " * " || echo -n "   "
		echo $name
	done
	echo "Done."
}

patch_test() {
	echo "Testing patches:"
	list="$*"
	test "z$list" = "z" && list=$PLIST
	for name in $list ; do
		fname=$DIFFS/$name.diff
		echo -ne "  $name\t"
		if test ! -f $MARKS/$name ; then
			if dry_run "$fname" ; then
				echo -n " OK"
			else
				echo -n " PATCH FAILED"
				retcode=2
			fi
		else
			echo -n " APPLIED"
			if dry_run "$fname" -R ; then
				echo -n " OK"
			else
				echo -n " REVERSE FAILED"
				retcode=2
			fi
		fi
		echo
	done
	echo "Done."
}

patch_add() {
	name=$1
	fname="$DIFFS/$name.diff"
	if test ! -f $fname ; then
		echo "Patch $name ($fname) does not exist"
		retcode=3
		return
	fi

	if test $force -lt 2 -a -f $MARKS/$name ; then
		echo "Patch $name seems already applied"
		retcode=4
		return
	fi

	if test $force -lt 1 ; then
		echo -n "Testing $fname... "
		if ! dry_run $fname ; then
			echo "Failed (use -f to force)."
			echo "The following files failed patching:"
			echo "$rejects"
			retcode=2
			return
		fi
		echo "seems ok."
	fi

	echo "Applying $fname..."
	if patch -p0 -N -t < $fname ; then
		touch $MARKS/$name
		echo "Done."
	else
		echo "Failed."
		retcode=1
	fi
}

patch_del() {
	name=$1
	fname="$DIFFS/$name.diff"
	if test ! -f $fname ; then
		echo "Patch $name ($fname) does not exist"
		retcode=3
		return
	fi

	if test $force -lt 2 -a ! -f $MARKS/$name ; then
		echo "Patch $name doesn't seem to be applied"
		retcode=4
		return
	fi

	if test $force -lt 1 ; then
		echo -n "Testing $fname... "
		if ! dry_run $fname -R ; then
			echo "Failed (use -f to force)."
			echo "The following files failed patching:"
			echo "$rejects"
			retcode=2
			return
		fi
		echo "seems ok."
	fi
	
	echo "Reversing $fname..."
	if patch -p0 -R -t < $fname ; then
		rm -f $MARKS/$name
		echo "Done."
	else
		echo "Failed."
		retcode=1
	fi
}

do_help() {
	echo "Usage: $0 [-f [-f]] [args]"
	echo "Arguments may be:"
	echo "  help                Prints this help"
	echo "  list                List available patches"
	echo "  test [patch list]   Tests whether patches can be (un)applied correctly"
	echo "  add  <patch list>   Applies a patch"
	echo "  del  <patch list>   Reverses a patch"
	echo "  update              Updates $PLIST_FILE with the currently applied patches"
	echo "The -f option forces patching even if a dry run fails (effective on 'add'"
	echo "and 'del' commands only). Using it twice will also skip checking whether"
	echo "a patch is already applied."
}

while test "$1" = "-f" ; do
	force=`expr $force + 1`
	shift
done

case "$1" in
	add)
		shift
		for name in $* ; do
			patch_add $name
		done
		update_patchlist
		;;
	del)
		shift
		for name in $* ; do
			patch_del $name
		done
		update_patchlist
		;;
	test)
		shift
		patch_test $*
		;;
	list)
		patch_list
		;;
	update)
		update_patchlist
		;;
	*)
		do_help
		;;
esac

exit $retcode
