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

usage()
{
	printf $"Usage: quilt annotate [-P patch] {file}\n"
	if [ x$1 = x-h ]
	then
		printf $"
Print an annotated listing of the specified file showing which
patches modify which lines. Only applied patches are included.

-P patch
	Stop checking for changes at the specified rather than the
	topmost patch.
"
		exit 0
	else
		exit 1
	fi
}

empty_file()
{
	local file=$1
	[ -s "$file" ] \
	&& sed -e 's:.*::' "$file"
}

annotation_for()
{
	local old_file=$1 new_file=$2 annotation=$3
	[ -s "$old_file" ] || old_file=/dev/null
	[ -s "$new_file" ] || new_file=/dev/null
	diff -e "$old_file" "$new_file" \
	| perl -e '
	while (<>) {
	    if (/^\d+(?:,\d+)?[ac]$/) {
		print;
		while (<>) {
		    last if /^\.$/;
		    print "'"$annotation"'\n";
		}
		print;
		next;
	    }
	    print;
	}
	'
}

merge_files()
{
	local a b saved_IFS="$IFS"
	local template=$1 file=$2

	[ -e "$file" ] || file=/dev/null

	exec 3< "$template"
	exec 4< "$file"
	IFS=
	while read -r a <&3
	do
		read -r b <&4
		echo "$a"$'\t'"$b"
	done
	IFS="$saved_IFS"
	exec 3<&-
	exec 4<&-
}

options=`getopt -o P:h -- "$@"`

if [ $? -ne 0 ]
then
	usage
fi

eval set -- "$options"

while true
do
	case "$1" in
	-P)
		opt_patch="$2"
		shift 2 ;;
	-h)
		usage -h ;;
	--)
		shift
		break ;;
	esac
done

if [ $# -ne 1 ]
then
	usage
fi

# Read in library functions
if ! [ -r $QUILT_DIR/scripts/patchfns ]
then
	echo "Cannot read library $QUILT_DIR/scripts/patchfns" >&2
	exit 1
fi
. $QUILT_DIR/scripts/patchfns

opt_file="$SUBDIR$1"

opt_patch=$(find_applied_patch "$opt_patch") || exit 1

for patch in $(applied_patches); do
	old_file="$(backup_file_name "$patch" "$opt_file")"
	if [ -f "$old_file" ]
	then
		patches[${#patches[@]}]="$patch"
		files[${#files[@]}]="$old_file"
	fi
	if [ "$opt_patch" = "$patch" ]
	then
		# We also need to know the next patch, if any
		next_patch="$(next_patch_for_file "$opt_patch" "$opt_file")"
		break
	fi
done

if [ -z "$next_patch" ]
then
	files[${#files[@]}]="$opt_file"
else
	files[${#files[@]}]="$(backup_file_name "$next_patch" "$opt_file")"
fi

if [ ${#patches[@]} = 0 ]
then
	sed -e 's:^:'$'\t'':' "${files[${#files[@]}-1]}"
	exit 0
fi

template=$(gen_tempfile)

add_exit_handler "rm -f $template"

# The annotated listing is generated as follows: A file of annotations
# is created based on a file that contains the same number of lines as
# the source file, but all lines are empty.
#
# Then, for each patch that modifies the source file, an ed-style diff
# (which has no context, and removes lines that are removed without
# caring for the line's contents) is generated. In that diff, all line
# additions are replaced with the identifier of the patch (1, 2, ...).
# These patches are then applied to the empty file.
#
# Finally, the annotations listing is merged with the source file line
# by line.

setup_pager

empty_file ${files[0]} > $template
for ((n = 0; n < ${#patches[@]}; n++))
do
	annotation_for "${files[n]}" "${files[n+1]}" $((n+1))
done \
| patch $template
merge_files $template "${files[${#files[@]}-1]}"

echo
for ((n = 0; n < ${#patches[@]}; n++))
do
	echo "$((n+1))"$'\t'"$(print_patch ${patches[n]})"
done
