#
# Work out where the user's pc/, patch/ and txt/ directories live.
#
# If the user specified PATCHSCRIPTS in environment then use that (it's
# probably a relative path)
#
# If there is a directory ./patch-scripts then use that
#
# Otherwise use "."
#

PATCHSCRIPTS_LIBDIR=$(dirname $0)

if [ x$PATCHSCRIPTS != x ]
then
	P=$PATCHSCRIPTS
elif [ -d ./quilt ]
then
	P=./quilt
elif [ -d ./patch-scripts ]
then
	P=./patch-scripts
elif [ -d ./patches ]
then
	P=.
fi

DB="$P/applied-patches"

__cat_series()
{
	grep -v '^#' $1
}

cat_series()
{
	__cat_series $P/series
}

top_patch()
{
	tail -n 1 $DB
}

die()
{
	echo error: $*
	exit 1
}

is_numeric()
{
	if echo $1 | egrep '^[0-9]*$' > /dev/null
	then
		return 0
	fi
	return 1
}

is_applied_last()
{
	top_patch >$DB.1
	if grep "^$1$" "$DB.1" > /dev/null 2>&1
	then
		rm $DB.1
		return 0
	else
		rm $DB.1
		return 1
	fi
}

is_applied()
{
	if grep "^$1$" "$DB" > /dev/null 2>&1
	then
		return 0
	else
		return 1
	fi
}

is_in_series()
{
	name=$(stripit "$1")
	if grep "^$name.patch$" $P/series > /dev/null 2>&1
	then
		return 0
	else
		return 1
	fi
}

can_apply()
{
	pn=$(stripit $1)
	if ! grep "^+++" "$P/patches/$pn.patch" > /dev/null
	then
		return 0
	elif patch -p1 --dry-run -i "$P/patches/$pn.patch" -f
	then
		return 0
	else
		return 1
	fi
}

can_remove()
{
	if ! grep "^+++" $P/patches/"$1".patch > /dev/null
	then
		return 0
	elif patch -R -p1 --dry-run -i $P/patches/"$1".patch -f
	then
		return 0
	else
		return 1
	fi
}

remove_from_db()
{
	tmpfile=$DB.1
	name="$1"
	sed -e "/^$name$/d" < "$DB" > $tmpfile
	mv $tmpfile "$DB"
}

remove_from_series()
{
	tmpfile=$(mktemp /tmp/p_XXXXXX)
	name=$(stripit "$1")
	sed -e "/^$name.patch$/d" < $P/series > $tmpfile
	cp -H $tmpfile $P/series
	rm -f $tmpfile
}

next_patch()
{
	top=$($PATCHSCRIPTS_LIBDIR/toppatch)
	last_in_series=$(stripit $(cat_series | tail -n 1))
	if [ "$last_in_series" == "$top" ]
	then
		return 0
	fi
	todo=$(cat_series | grep -C1 "^$top\.patch" | tail -n 1)
	if [ x$todo = x ]
	then
		todo=$(cat_series | head -n 1)
	fi
	echo $(stripit $todo)
}

file_in_patch()
{
	file=$1
	patch=$2

	if [ -e $P/pc/$patch.pc ]
	then
		if grep "^"$file"$" $P/pc/$patch.pc > /dev/null
		then
			return 0
		fi
	fi
	return 1
}

# copy_file_to_bup filename patchname
copy_file_to_bup()
{
	bup="$1"~"$2"

	if [ -e $bup ]
	then
		echo "Cannot install file $1 in patch $2: backup $bup exists"
		exit 1
	fi

	if [ -e $1 ]
	then
		cp $1 "$1"~"$2"
	else
		echo "file $1 appears to be newly added"
	fi
}

# copy_bup_to_file filename patchname
copy_bup_to_file()
{
	file=$1
	patch=$2
	bup="$file"~"$patch"

	if [ ! -e $bup ]
	then
		echo "Cannot restore file $file from $bup: $bup does not exist"
		exit 1
	fi

	mv $bup $file
}

mergebase_name()
{
	file=$1
	PATCH_NAME=$2
	echo $(dirname "$file")/.$(basename "$file")~mergebase~"$PATCH_NAME"
}

delete_mergebase_files()
{
	PATCH_NAME=$1
	FILES=$(< $P/pc/$1.pc)
	for file in $FILES
	do
		rm -f $(mergebase_name "$file" $1)
	done
}

install_file_in_patch()
{
	file=$1
	patch=$2

	copy_file_to_bup $file $patch
	echo $file >> $P/pc/$patch.pc
}

remove_file_from_patch()
{
	file=$1
	patch=$2
	tmpfile=$(mktemp /tmp/p_XXXXXX)

	sed -e "/^$file$/d" < $P/pc/$patch.pc > $tmpfile
	mv $tmpfile $P/pc/$patch.pc
	copy_bup_to_file $file $patch
}

need_file_there()
{
	if [ ! -e $1 ]
	then
		echo "File $1 does not exist"
		exit 1
	fi
}

desc()
{
	state=0
	while read x
	do
		if [ x"$x" = xDESC ]
		then
			state=1
		elif [ x"$x" = xEDESC ]
		then
			state=0
		elif [ $state = 1 ]
		then
			echo "  $x"
		fi
	done
}

body()
{
	file=$1

	did_stuff=0
	while read x
	do
		if [ x"$x" = xEDESC ]
		then
			cat
			did_stuff=1
		fi
	done < $file

	if [ $did_stuff = 0 ]
	then
		cat $file
	fi
}

check_patch()
{
	if grep "^+.*[ 	]$" $P/patches/$(stripit $1).patch
	then
		echo warning: $1 adds trailing whitespace
	fi
}

lastpatch()
{
	ls -tr1 $P/patches/*.patch | tail -n1
}

#
# author varname patchname [first_author_varname]
#
author()
{
	first_author=""
	patch_name=$(stripit $2)
	TXT=$P/txt/$patch_name.txt
	if [ ! -f $TXT ]
	then
		echo $TXT is missing
		exit 1
	fi
	author=""
	while read l
	do
		for x in $l
		do
			a=$(echo "$x" | sed -n -e 's/.*<\(.*@[^>]*\).*/\1/p')
			if [ x"$a" != x ]
			then
				if [ x"$author" == x ]
				then
					author=$a
					first_author=$a
				else
					author="$author $a"
				fi
			fi
		done
	done < $TXT
	author=$(echo $author | tr ' ' '\n' | grep -v "$first_author" |
		sort | uniq)
	author="$first_author $author"
	eval $1=$(echo $author | sed -e 's/ /,/g')
	if [ x"$3" != x ]
	then
		eval $3=$first_author
	fi
}

do_apply()
{
	for file in $(< $P/pc/$PATCH_NAME.pc)
	do
		copy_file_to_bup $file $PATCH_NAME
	done

	silent=-s
	if [ $opt_force != 0 ]
	then
		silent=
		flags=-l
	else
		flags=
	fi

	if ! grep "^+++" "$1" > /dev/null
	then
		true
	elif patch -p1 $silent $flags -i "$1" || [ $opt_force != 0 ]
	then
		true
	else
		echo SOMETHING WENT WRONG
		exit 1
	fi
}

do_merge()
{
	echo blip
	ret=0
	FILES=$(< $P/pc/$PATCH_NAME.pc)
	for file in $FILES
	do
		copy_file_to_bup $file $PATCH_NAME
	done

	for file in $FILES
	do
		merge_base=$(mergebase_name "$file" "$PATCH_NAME")
		if [ -e $merge_base ]
		then
			cp "$merge_base" "$file"
		fi
	done

	patch -p1 -s -i "$1" >/dev/null

	for file in $FILES
	do
		bup="$file"~"$PATCH_NAME"
		merge_base=$(mergebase_name "$file" "$PATCH_NAME")
		rej_file="$file".rej
		if [ -e "$merge_base" ]
		then
		    echo Wiggling changes into $file
		    if ! wiggle $bup $merge_base $file >"$file".tmp
		    then
			ret=1
		    fi
		    mv -f "$file".tmp $file
		elif [ -e $rej_file -a ! $rej_file -ot $bup ]
		then
		    echo Wiggling $rej_file into $file
		    if ! wiggle "$file" "$rej_file" >"$file".tmp
		    then
			ret=1
		    fi
		    mv -f "$file".tmp $file
		fi
	done

	return $ret
}

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

apatch()
{
	PATCH_NAME="$1"

	need_file_there $P/pc/$PATCH_NAME.pc

	if is_applied "$PATCH_NAME"
	then
		echo "$PATCH_NAME" is already applied
		exit 1
	fi

	if [ $opt_force != 0 ]
	then
		echo FORCING PATCH
	fi

	if [ $opt_force != 0 ] || can_apply $P/patches/"$PATCH_NAME".patch
	then
		do_apply $P/patches/"$PATCH_NAME".patch
		add_to_db "$PATCH_NAME"
		echo applied $PATCH_NAME, next is $(next_patch)
		echo
	elif [ $opt_merge != 0 ]
	then
		do_merge $P/patches/"$PATCH_NAME".patch
		ret=$?
		add_to_db "$PATCH_NAME"
		echo applied $PATCH_NAME, next is $(next_patch)
		echo
		if [ $ret != 0 ]
		then
			echo "Resolve conflicts before pushing more patches."
			delete_mergebase_files "$PATCH_NAME"
			exit 1
		fi
	else
		echo "$PATCH_NAME" does not apply
		exit 1
	fi

	delete_mergebase_files "$PATCH_NAME"
}
