#! /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 . # # 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