summaryrefslogblamecommitdiffstats
path: root/lib/rpatch.in
blob: 73f8ac60c6f7eb064300c6fda7dfedb8801a81d2 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
        





                                                                      
 

                                               
    





                                                             
  


       
                                         























                                                             
                                                  










                                                                    
                                                  
                                            





                            

                                                   
                                                             



















                                                    
                
 



                                                            




                      

                                                                 


              













                                                                 
                                                                        



                                                
                                                                        

                                     
                                                                      




                                                    


                                                     






                                                                










                                                                        




                                      
            

                                                                
                    
                                                                   



                                              

                                    
                         
 
                              
 

                                                             



                                                                     


                                             

                                                                             



                                








                                          

 
                                 




















                                                                        


                             














                                
                                                 

                
                                                                      
    
                                                                  


              
                         
#!@BASH@

#  This script is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License version 2 as
#  published by the Free Software Foundation.
#
#  See the COPYING and AUTHORS files for more details.

# Read in library functions
if [ "$(type -t patch_file_name)" != function ]
then
	if ! [ -r @LIB@/patchfns ]
	then
		echo "Cannot read library @LIB@/patchfns" >&2
		exit 1
	fi
	. @LIB@/patchfns
fi

usage()
{
	echo "Usage: $0 [-fRq] patchname"
	exit 1
}

verify_removal()
{
	local patch=$1
	local bup file status=0
	local dir=$(basename $PWD) suffix=${patch//\//_}

	# Check if all changes of the patch are undone
	for file in $(files_in_patch $patch)
	do
		bup=$(backup_file_name $patch $file)
		if ! [ -s $file -o -s $bup ] || \
		   cmp $file $bup > /dev/null 2> /dev/null
		then
			continue
		fi

		if [ $status -eq 0 ]
		then
			echo "Patch does not remove changes:"
		fi

		@DIFF@ -Nu $DIFF_OPTS $bup $file \
		| sed -e 's:^--- [^ \t]*:--- '"$dir/$file.orig"':' \
		      -e 's:^+++ [^ \t]*:+++ '"$dir/$file"':'

		status=1
	done
	return $status
}

files_may_have_changed()
{
	local patch=$1 file
	local patch_file=$(patch_file_name $patch)
	local pc_file=$(pc_file_name $patch)

	if ! [ -e $pc_file ]
	then
		return 1
	fi
	
	local apply_ts=$(date -r $pc_file '+%s') ts

	if [ -e "$patch_file" -a $pc_file -ot "$patch_file" ]
	then
		return 0
	fi
	
	for file in $(files_in_patch $patch)
	do
		if ! [ -e $file ]
		then
			return 0  # file is missing
		fi
		
		ts=$(date -r $file '+%s')
		if [ $? -ne 0 -o $ts -gt $apply_ts ]
		then
			return 0  # file has changed
		fi
	done
	return 1
}

rollback_patch()
{
	local patch=$1 pc_file=$(pc_file_name $patch)
	@LIB@/backup-files $silent_unless_verbose \
			   -f $pc_file -z ~rpatch -r
	rm -f $(files_in_patch $patch | sed -e 's/$/\.rej/')
}

interrupt()
{
	local patch=$1
	rollback_patch $patch
	echo "Interrupted by user; patch $patch was not removed."
	exit 1
}

reverse_patch()
{
	local patch=$1
	local patch_file=$(patch_file_name $patch)

	if ! [ -s $patch_file ]
	then
		echo "Patch file $patch_file appears to be empty"
		return 0
	fi
	
	if [ "x${patch_file:(-3)}" = "x.gz" ]
	then
		gzip -cd $patch_file \
		| @PATCH@ $(patch_args $patch) --no-backup-if-mismatch \
			-R -E $silent
	elif [ "x${patch_file:(-4)}" = "x.bz2" ]
	then
		bzip2 -cd $patch_file \
		| @PATCH@ $(patch_args $patch) --no-backup-if-mismatch \
			-R -E $silent
	else
		@PATCH@ $(patch_args $patch) --no-backup-if-mismatch \
			-R -E $silent -i $patch_file
	fi
}

rpatch()
{
	local patch=$1 pc_file=$(pc_file_name $patch)

	if ! [ -e $pc_file ]
	then
		echo "Patch $patch appears to be empty, removed"
		remove_from_db $patch
		return 0
	fi

	trap "" SIGINT
	if [ -n "$opt_force" ] || \
	   ( [ -z "$opt_remove" ] && ! files_may_have_changed $patch )
	then
		# Optimize: Force remove if the patch has not been
		# modified since it was applied. (Forced remove is
		# faster!)

		@LIB@/backup-files $silent -f $pc_file -B .pc/$patch/ -r
		status=$?
		remove_from_db $patch
		rm -f $pc_file~refresh
		if [ $status != 0 ]
		then
			exit $status
		fi
	else
		if ! @LIB@/backup-files $silent_unless_verbose \
					-f $pc_file -z ~rpatch
		then
			echo "Failed to create temporary files" >&2
			return 1
		fi
		
		trap "interrupt $patch" SIGINT

		reverse_patch $patch
		status=$?

		trap "" SIGINT

		if [ $status -eq 0 ] && verify_removal $patch
		then 
			@LIB@/backup-files $silent_unless_verbose \
					-f $pc_file -z ~rpatch -x
			@LIB@/backup-files $silent_unless_verbose \
					-f $pc_file -B .pc/$patch/ -x

			remove_from_db $patch
		else
			rollback_patch $patch
			echo "Patch $patch does not remove (enforce with -f)"
			return 1
		fi
	fi
	trap - SIGINT
	
	local top=$(top_patch) where
	if [ -z "$top" ]
	then
		where="no patches applied"
	else
		where="now at $top"
	fi
	echo "Removed $patch, $where"
}

options=`getopt -o fRqvh -- "$@"`

if [ $? -ne 0 ]
then
	usage
fi

eval set -- "$options"

while true
do
	case "$1" in
	-f)
		opt_force=1
		shift ;;
	-R)
		opt_remove=1  # remove properly with patch -R; no tricks
		unset opt_force
		shift ;;
	-q)
		opt_quiet=1 
		shift ;;
	-v)
		opt_verbose=1
		shift ;;
	-h)
		usage -h ;;
	--)
		shift
		break ;;
	esac
done

if [ $# -ne 1 ]
then
	usage
fi

patch=$(stripit $1)
[ -n "$opt_quiet" ] && silent=-s
[ -z "$opt_verbose" ] && silent_unless_verbose=-s

top=$(top_patch)
if [ -n "$top" -a -e $(pc_file_name $top)~refresh -a -z "$opt_force" ]
then
	echo "The topmost patch $top needs to be refreshed first."
	exit 1
fi

rpatch "$patch" || exit 1