summaryrefslogblamecommitdiffstats
path: root/scripts/patchfns.in
blob: f16ad4eb27e3eae48e9d880ddf45a1cdd0b1e029 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                                  





                                                                      
 


                       




                        


















                                 


                                                       
                                                       


                                                           

          
                                                              

 

          

                                

                          
 

         

                                




                                                
 





                         
                                                                                          



                                                         









                                                                    
                       
                                                                                   

                                                       





                                                
































                                     
                                             
                       











                                                                                  
                    
                                      
                    
                                              













                           
                                                                                                    






                                      
                                      





                                         
                                           
                                   

                        
                       









                                                                                 
            
                                                 

                                 
                                               

                  






                              
                                           
               









                                                                             





















                                    
            
 
                         
            


                                                                              




                        

           
                                   

 

            
                                    











                                    
                                              

 


















                                                 


                      



                            
                         



                                                 




                      


                            
                         





                                                  






                                                            
               


                                                 










                                                                  

                                                      
                             
                                                       
          



                                                  

 

                                                          
 



                                                    
            












                                                                
          
 








                                                                
            

                                          
          
 
               

























                                                                    
               




























                                                                         










                      
                                  
            
                                                                 



                                       






                         



                                                              





                              

                                           










                                        
                  
 

                                            
                  







                                            









                                                  
                        
          


                                          
            

                                                      
                                            



                                

                                                          





                                                                   


                                
                                            


                                              

                           


          


                           

                                                                    

 

           
                                             
                                  


                                                   

                        

                                     
                                  


                                          
                                       


                                  
                                                         


                                      
                                                                                          


                                                   

 
          
 






















                                                        
            









                                                 






                           
                                                   
            
                       
                                        
                                                
                                             


                                           

                                         

          










                                           











































                                                                



                      
# This file contains the common functions used in all quilt script
# It is meant to be sourced by bash scripts.

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

export TEXTDOMAIN=quilt
		

if [ -e $HOME/.quiltrc ]
then
	. $HOME/.quiltrc
fi

if [ -n "$PATCHSCRIPTS" ]
then
	P=$PATCHSCRIPTS/
else
	unset P
fi

if [ -e .pc/series ]
then
	SERIES=.pc/series
elif [ -e series ]
then
	SERIES=series
else
	SERIES=${P}patches/series
fi

DB=".pc/applied-patches"

# Quote a string for use in a basic regular expression.
quote_bre()
{
	echo "$1" | @SED@ -e 's:\([][^$/.*\\]\):\\\1:g'
}

# Quote a string for use in an extended regular expression.
quote_re()
{
	echo "$1" | @SED@ -e 's:\([][?{(|)}^$/.+*\\]\):\\\1:g'
}

basename()
{
        local path="${1//\/\//}"
        path="${path%%/}"
	echo "${path##*/}"
}

dirname()
{
        local path="${1//\/\//}"
        path="${path%%/}"
	local basename="${path##*/}"
	path="${path:0:${#path}-${#basename}}"
	[ x"$path" != x"/" ] && path="${path%/}"
	echo "${path:-.}"
}

patch_file_name()
{
	local patch=$1

	if [ -e $SERIES ]
	then
		@AWK@ '/^'"$(quote_re $patch)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \
			{ printf "'"$P"'patches/%s\n", $1
			  exit
			}
		' $SERIES
	fi
}

# The -pN option and possibly others that should be passed to patch.
patch_args()
{
	local patch=$1

	if [ -e $SERIES ]
	then
		@AWK@ '
		/^'"$(quote_re $patch)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \
			{ if (NF >= 2)
				for (i=2; i <= NF; i++)
					print $i
			  else
				print "-p1" ;
			  exit
			}
		' $SERIES
	fi
}

patch_strip_level()
{
	local patch=$1 i
	for i in $(patch_args $patch)
	do
		case $i in
		-p)
			echo $2
			return ;;
		-p*)
			echo ${i:2}
			return ;;
		esac
	done
	echo "1"
}

change_db_strip_level()
{
	local level=$1 patch=$2

	if [ x$level != x-p1 ]
	then
		level="$level"
	else
		level=""
	fi

	if [ -e $SERIES ]
	then
		local tmpfile=$(gen_tempfile)
		@AWK@ '
		/^'"$(quote_re $patch)"'(\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \
			{ for(i=2; i<=NF; i++)
				if ($i ~ /^-p/) {
					$i="'"$level"'"
					break
				}
			  if (i > NF)
				 $i="'"$level"'"
			}
			{ print }
		' $SERIES > $tmpfile
		if cmp $SERIES $tmpfile >/dev/null 2>/dev/null
		then
			rm -f $tmpfile
		else
			mv -f $tmpfile $SERIES
		fi
	else
		return 1
	fi
}

patch_in_series()
{
	local patch=$1

	if ! [ -e $SERIES ]
	then
		return 1
	else
		grep -q -E '^'"$(quote_re $patch)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)' $SERIES
	fi
}

# Insert new patch after topmost patch
insert_in_series()
{
	local patch=$1 patch_args=$2
	local top=$(top_patch) tmpfile

	if [ -n "$patch_args" ]
	then
		patch_args=" $patch_args"
	fi

	tmpfile=$(gen_tempfile) || return 1
	mkdir -p $(dirname $SERIES)
	if [ -n "$top" ]
	then
		@AWK@ '
				{ print }
		/^'"$(quote_re $top)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \
				{ print "'"$patch$patch_args"'" }
		' $SERIES > $tmpfile
		status=$?
		if [ $status -ne 0 ]
		then
			rm -f $tmpfile
			return 1
		fi
	else
		echo $patch$patch_args > $tmpfile
		if [ -e $SERIES ]
		then
			cat $SERIES >> $tmpfile
		fi
	fi
	mv -f $tmpfile $SERIES
}

remove_from_series()
{
	local patch=$1

	tmpfile=$(gen_tempfile) || return 1
	@AWK@ '
	! /^'"$(quote_re $patch)"'(|\.patch|\.diff?)(|\.gz|\.bz2)([ \t]|$)/ \
				{ print }
	' $SERIES > $tmpfile
	if [ $? -eq 0 ]
	then
		mv -f $tmpfile $SERIES
	else
		rm -f $tmpfile
	   	return 1
	fi
}

pc_file_name()
{
	while [ $# -gt 0 ]
	do
		echo ".pc/$1/.pc"
		shift
	done

}

backup_file_name()
{
	local patch=$1
	while [ $# -gt 1 ]
	do
		echo ".pc/$patch/$2"
		shift
	done
}

cat_series()
{
	if [ -e $SERIES ]
	then
		@SED@ -e '/^#/d' -e 's/^[ '$'\t'']*//' -e 's/[ '$'\t''].*//' \
		      -e 's/\.gz$//' -e 's/\.bz2$//' \
		      -e 's/\.patch$//' -e 's/\.diff\?$//' $SERIES
	else
		return 1
	fi
}

top_patch()
{
	[ -e $DB ] && tail -n 1 $DB
}

is_numeric()
{
	echo $1 | grep -q '^[0-9]*$'
}

is_applied_last()
{
	local patch=$1
	[ "$(top_patch)" == $patch ]
}

is_applied()
{
	local patch=$1
	[ -e $DB ] || return 1
	grep -q -E "^$(quote_re $patch)\$" $DB
}

applied_patches()
{
	[ -e $DB ] || return 1
	cat $DB
}

applied_before()
{
	local patch=$1

	if [ -n "$patch" ]
	then
		@AWK@ '
		$0 == "'"$patch"'"	{ exit }
					{ print }
		' $DB
	fi
}
		
patches_before()
{
	local patch=$1

	if [ -n "$patch" ]
	then
		cat_series \
		| @AWK@ '
		$0 == "'"$patch"'"	{ exit }
					{ print }
		'
	fi
}

patches_after()
{
	local patch=$1
	if [ -n "$patch" ]
	then
		cat_series \
		| @AWK@ '
		seen			{ print }
		$0 == "'"$patch"'"	{ seen=1 }
		'
	else
		cat_series
	fi
}

# List all patches that have been applied on top of patch $1
patches_on_top_of()
{
	local patch=$1
	[ -e $DB ] || return
	@AWK@ '
	$0 == "'"$patch"'"	{ seen=1 ; next }
	seen			{ print }
	' $DB
}

# Print the name of the patch that modified the file $2 next after
# patch $1, or print nothing if patch $1 is on top.
next_patch_for_file()
{
	local patch=$1 file=$2
	local patches_on_top=$(patches_on_top_of $patch)

	if [ -n "$patches_on_top" ]
	then
		grep -l -E "^$(quote_re $file)\$" \
		     $(pc_file_name $patches_on_top) \
		| head -n 1 \
		| @SED@ -e 's:^\.pc/::' -e 's:/\.pc$::'
	fi

	#modified_files $file -- $patches_on_top \
	#| cut -d $'\t' -f2 \
	#| cut -d ' ' -f1
}

# Create a list of files and the patches that modify them.
refresh_patches_per_file()
{
	local pc_files=$(pc_file_name $(cat_series))
	local ex_pc_files pc_file

	if [ -e .pc/patches-per-file ]
	then
		local needs_refresh
		for pc_file in pc_files
		do
			if [ .pc/patches-per-file -ot $pc_file ]
			then
				needs_refresh=1
				break
			fi
		done
		if [ -z "$needs_refresh" ]
		then
			return 0
		fi
	fi

	for pc_file in $pc_files
	do
		if [ -e $pc_file ]
		then
			ex_pc_files[${#ex_pc_files[@]}]=$pc_file
		fi
	done

	if [ ${#ex_pc_files[@]} -eq 0 ]
	then
		rm -f .pc/patches-per-file
		return 0
	fi

	@AWK@ '
	ARGIND!=saved	{ sub(/^.pc\//, "", FILENAME)
			  sub(/\/\.pc/, "", FILENAME)
			  saved=ARGIND
			}
			{ if (files[$0])
				files[$0]=files[$0] " " FILENAME
			  else
				files[$0]=FILENAME
			}
	END		{ for (file in files)
				printf "%s\t%s\n", file, files[file]
			}
	' "${ex_pc_files[@]}" > .pc/patches-per-file
}

# For a lists of patches and a list of files, compute which patches
# modify which files. Invoked as
# 	modified_files file ... [-- patch ...]
#
modified_files()
{
	if ! refresh_patches_per_file
	then
		return 1
	fi

	@AWK@ '
	BEGIN		{ no_files=1
			  no_patches=1
			  for (i=1; i<ARGC; i++) {
				if (ARGV[i]=="--")
					break
				files[ARGV[i]]=1
				no_files=0
			  }
			  for (i++; i<ARGC; i++) {
				 patches[ARGV[i]]=1
				 no_patches=0
			  }
			  split("", ARGV) # read from standard input
			}
	no_files || files[$1] {
			  if (no_patches) {
				  print
				  next
			  }
			  for (i=2; i<=NF; i++)
				if ($i in patches) {
					printf "%s\t%s", $1, $i
					for (i++; i<=NF; i++)
						if ($i in patches)
							 printf " %s", $i
					printf "\n"
				}
			}
	' "$@" < .pc/patches-per-file
}

add_to_db()
{
	echo $1 >> $DB
}

remove_from_db()
{
	local patch=$1
	local tmpfile
	if tmpfile=$(gen_tempfile)
	then
		grep -v -E "^$(quote_re $patch)\$" $DB > $tmpfile
		mv -f $tmpfile $DB
		rm -f $tmpfile
		[ -s $DB ] || rm -f $DB
	fi
}

stripit()
{
	if [ -n "$1" ]
	then
		echo $1 |
		@SED@ -e 's/^\(\.\/\)*//' \
		      -e 's/^'"$(quote_bre $P)"'patches\///' \
		      -e 's/\.gz$//' -e 's/\.bz2$//' \
		      -e 's/\.patch$//' -e 's/\.diff\?$//'
	fi
}

file_in_patch()
{
	local file=$1 patch=$2
	files_in_patch $patch \
	| grep -q -E "^$(quote_re $file)\$"
}

files_in_patch()
{
	local pc_file=$(pc_file_name $1)
	if [ -e "$pc_file" ]
	then
		cat $pc_file
	fi
}

touched_by_patch()
{
	local strip=$1 patch=$2
	cat_file $(patch_file_name $patch) \
	|  @AWK@ '
	/^\+\+\+[ \t]/ {
		sub(/^\+\+\+[ \t]/, "")
		sub(/[ \t].*/, "")
		sub(/^\/dev\/null/, "")
		for (i=0; i<'$strip'; i++)
			sub(/^[^\/]*\//, "")
		print
	}'
}

refresh_file_list()
{
	local patch=$1
	local pc_file=$(pc_file_name $patch)
	local patch_file=$(patch_file_name $patch)
	
	if ! [ -e "$patch_file" ]
	then
		return 0
	fi
	if [ ! -e $pc_file -o \
	     $pc_file -ot $patch_file -o \
	     $pc_file -ot $SERIES ]
	then
		local tmpfile status
		if ! mkdir -p $(dirname $pc_file) || \
		   ! tmpfile=$(gen_tempfile)
		then
			return 1
		fi

		# Do not reorder files in the file list...

		if [ -e $pc_file ]
		then
			cat $pc_file >> $tmpfile
		fi
		if ! touched_by_patch $(patch_strip_level $patch) \
			$patch >> $tmpfile
		then
			return 1
		fi
		@AWK@ '	{ if (seen[$0]) next
			  seen[$0]=1
			  print
			}' $tmpfile > $pc_file
		rm $tmpfile
		return 0
	fi
}

fix_diff_header()
{
	local from=$1 to=$2
	@SED@ -e 's/^--- [^ '$'\t'']*/--- '"$(quote_bre $from)"'/' \
	      -e 's/^+++ [^ '$'\t'']*/+++ '"$(quote_bre $to)"'/'
}

diff_file()
{
	local file=$1 old_file=$2 new_file=$3
	local old_hdr new_hdr line

	if [ ! -e "$old_file" -a ! -e "$new_file" ]
	then
		return 0
	fi
	if [ $opt_strip_level -eq 0 ]
	then
		old_hdr=$file.orig
		new_hdr=$file
	else
		local dir=$(basename $PWD)
		old_hdr=$dir.orig/$file
		new_hdr=$dir/$file
	fi

	@DIFF@ -Nu $QUILT_DIFF_OPTS $old_file $new_file |
	if read line
	then
		echo "Index: $new_hdr"
		echo "==================================================================="
		(echo "$line" ; cat) \
		| fix_diff_header $old_hdr $new_hdr
	fi
}

cat_file()
{
	local filename
	
	for filename in "$@"
	do
		if [ -e "$filename" ]
		then
			case "$filename" in
			*.gz|*.tgz)
				gzip -cd "$filename" ;;
			*.bz2)
				bzip2 -cd "$filename" ;;
			*)
				cat "$filename" ;;
			esac
		fi
	done
}

cat_to_file()
{
	local filename="$1"

	if [ -z "$filename" ]
	then
		cat
	else
		case "$filename" in
		*.gz|*.tgz)
			gzip -c > "$filename" ;;
		*.bz2)
			bzip2 -c > "$filename" ;;
		*)
			cat > "$filename" ;;
		esac
	fi
}

patch_description()
{
	local patch_file=$1

	if [ -e "$patch_file" -o -z "$patch_file" ]
	then
		@AWK@ '
		$1 == "---"	{ exit }
               /^Index:[ \t]|^diff[ \t]|^==*$/ \
                               { eat = eat $0
				  next }
		eat		{ print eat
				  eat="" }
				{ print }
		' $patch_file
	fi
}

in_array()
{
	local a=$1
	while [ $# -gt 1 ]
	do
		shift
		[ "$a" = "$1" ] && return 0
	done
	return 1
}

gen_tempfile()
{
	# This is a substitute for the mktemp executable.
	internal_mktemp()
	{
		local try n
		if [ x"$1" = x"-d" ]
		then
			for ((n=0 ; $n<100 ; n++))
			do
				try=${2%XXXXXX}$RANDOM
				mkdir -m 700 $try 2>/dev/null \
				&& break
			done
		else
			local user_mask=$(umask)
			umask 077
			set -o noclobber
			for ((n=0 ; $n<100 ; n++))
			do
				try=${1%XXXXXX}$RANDOM
				echo -n "" 2> /dev/null > $try \
				&& break
			done
			set +o noclobber
			umask $user_mask
		fi
		if [ $n -lt 100 ]
		then
			echo $try
		else
			return 1
		fi
	}

	local dir
	if [ x"$1" = x"-d" ]
	then
		dir=-d
		shift
	fi
	@MKTEMP@ $dir ${1:-${TMPDIR:-/tmp}/quilt}.XXXXXX
}
### Local Variables:
### mode: shell-script
### End:
# vim:filetype=sh