#!/bin/bash
#
#  rejmerge (pkgutils)
# 
#  Copyright (c) 2000-2005 Per Liden
#  Copyright (c) 2006-2013 by CRUX team (http://crux.nu)
# 
#  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.
#
#  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.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
#  USA.
#

info_n() {
	echo -n "=======> $1"
}

info() {
	info_n "$1"
	echo
}

interrupted() {
	echo ""
	info "Aborted."
	exit 1
}

atexit() {
	if [ -e "$TMPFILE" ]; then
		rm -f "$TMPFILE"
	fi
}

rejmerge_diff() {
	diff -u "$1" "$2" > "$3"
}

rejmerge_merge() {
	diff --old-group-format="%<" \
	     --new-group-format="%>" \
	     --changed-group-format="<<<<< MERGE CONFLICT $1 >>>>>
%<<<<<< MERGE CONFLICT $2 >>>>>
%><<<<< END MERGE CONFLICT >>>>>
" \
	"$1" "$2" > "$3"

	REJMERGE_MERGE_INFO="$(grep -c '^<<<<< END MERGE CONFLICT >>>>>$' "$3") merge conflict(s)."
}

permissions_menu() {
	while true; do
		info "Access permissions $1"
		stat -c '%A  %U %G  %n' "$1"
		stat -c '%A  %U %G  %n' "$2"
		while true; do
			info_n "[K]eep [U]pgrade [D]iff [S]kip? "
			read -n1 CMD
			echo

			case "$CMD" in
			k|K)	chown --reference="$1" "$2"
				chmod --reference="$1" "$2"
				break 2
				;;
			u|U)	chown --reference="$2" "$1"
				chmod --reference="$2" "$1"
				break 2
				;;
			d|D)	break 1
				;;
			s|S)	break 2
				;;
			esac
		done
	done
}

merge_menu() {
	rejmerge_merge "$1" "$2" "$TMPFILE"

	while true; do
		info "Merged $1"
		cat "$TMPFILE" | more

		if [ "$REJMERGE_MERGE_INFO" ]; then
			info "$REJMERGE_MERGE_INFO"
			unset REJMERGE_MERGE_INFO
		fi

		while true; do
			info_n "[I]nstall [E]dit [V]iew [S]kip? "
			read -n1 CMD
			echo

			case "$CMD" in
			i|I)	chmod --reference="$1" "$TMPFILE"
				mv -f "$TMPFILE" "$1"
				rm -f "$2"
				break 2
				;;
			e|E)	$EDITOR "$TMPFILE"
				break 1
				;;
			v|V)	break 1
				;;
			s|S)	break 2
				;;
			esac
		done
	done

	: > "$TMPFILE"
}

diff_menu() {
	rejmerge_diff "$1" "$2" "$TMPFILE"

	while true; do
		info "$1"
		cat "$TMPFILE" | more
		while true; do
			info_n "[K]eep [U]pgrade [M]erge [D]iff [S]kip? "
			read -n1 CMD
			echo

			case "$CMD" in
			k|K)	rm -f "$2"
				break 2
				;;
			u|U)	mv -f "$2" "$1"
				break 2
				;;
			m|M)	merge_menu "$1" "$2"
				break 2
				;;
			d|D)	break 1
				;;
			s|S)	break 2
				;;
			esac
		done
	done

	: > "$TMPFILE"
}

file_menu() {
	while true; do
		info "$1"
		file "$1" "$2"
		while true; do
			info_n "[K]eep [U]pgrade [D]iff [S]kip? "
			read -n1 CMD
			echo

			case "$CMD" in
			k|K)	rm -f "$2"
				break 2
				;;
			u|U)	mv -f "$2" "$1"
				break 2
				;;
			d|D)	break 1
				;;
			s|S)	break 2
				;;
			esac
		done
	done
}

print_help() {
	echo "usage: $REJMERGE_COMMAND [options]"
	echo "options:"
	echo "  -r,   --root <path>         specify alternative root"
	echo "  -v,   --version             print version and exit "
	echo "  -h,   --help                print help and exit"
}

parse_options() {
	while [ "$1" ]; do
		case $1 in
			-r|--root)
				if [ ! "$2" ]; then
					echo "$REJMERGE_COMMAND: option $1 requires an argument"
					exit 1
				fi
				REJMERGE_ROOT="$2"
				REJMERGE_CONF="$2$REJMERGE_CONF"
				REJECTED_DIR="$2$REJECTED_DIR"
				shift ;;
			-v|--version)
				echo "$REJMERGE_COMMAND (pkgutils) $REJMERGE_VERSION"
				exit 0 ;;
			-h|--help)
				print_help
				exit 0 ;;
			*)
				echo "$REJMERGE_COMMAND: invalid option $1"
				exit 1 ;;
		esac
		shift
	done

	if [ ! -d "$REJECTED_DIR" ]; then
		echo "$REJMERGE_COMMAND: $REJECTED_DIR not found"
		exit 1
	fi
}

files_regular() {
	local STAT_FILE1=$(stat -c '%F' "$1")
	local STAT_FILE2=$(stat -c '%F' "$2")

	if [ "$STAT_FILE1" != "regular file" ]; then
		return 1
	fi

	if [ "$STAT_FILE2" != "regular file" ]; then
		return 1
	fi

	return 0
}

main() {
	parse_options "$@"

	if [ "$UID" != "0" ]; then
		echo "$REJMERGE_COMMAND: only root can merge rejected files"
		exit 1
	fi

	# Read configuration
	if [ -f "$REJMERGE_CONF" ]; then
		. "$REJMERGE_CONF"
	fi
	
	REJECTED_FILES_FOUND="no"

	# Check files
	for REJECTED_FILE in $(find $REJECTED_DIR ! -type d); do
		INSTALLED_FILE="$REJMERGE_ROOT${REJECTED_FILE##$REJECTED_DIR}"

		# Remove rejected file if there is no installed version
		if [ ! -e "$INSTALLED_FILE" ]; then
			rm -f "$REJECTED_FILE"
			continue
		fi

		# Check permissions
		local STAT_FILE1=$(stat -c '%A %U %G' "$INSTALLED_FILE")
		local STAT_FILE2=$(stat -c '%A %U %G' "$REJECTED_FILE")

		if [ "$STAT_FILE1" != "$STAT_FILE2" ]; then
			REJECTED_FILES_FOUND="yes"
			permissions_menu "$INSTALLED_FILE" "$REJECTED_FILE"
		fi

		# Check file types
		if files_regular "$INSTALLED_FILE" "$REJECTED_FILE"; then
			# Both files are regular
			if cmp -s "$INSTALLED_FILE" "$REJECTED_FILE"; then
				rm -f "$REJECTED_FILE"
			else
				REJECTED_FILES_FOUND="yes"
				diff_menu "$INSTALLED_FILE" "$REJECTED_FILE"
			fi
		else
			# At least one file is non-regular
			REJECTED_FILES_FOUND="yes"
			file_menu "$INSTALLED_FILE" "$REJECTED_FILE"
		fi
	done

	# Remove empty directories
	for DIR in $(find $REJECTED_DIR -depth -type d); do
		if [ "$DIR" != "$REJECTED_DIR" ]; then
			rmdir "$DIR" &> /dev/null
		fi
	done

	if [ "$REJECTED_FILES_FOUND" = "no" ]; then
		echo "Nothing to merge"
	fi

	exit 0
}

trap "interrupted" SIGHUP SIGINT SIGQUIT SIGTERM
trap "atexit" EXIT

export LC_ALL=POSIX

readonly REJMERGE_VERSION="5.35.5"
readonly REJMERGE_COMMAND="${0##*/}"
REJMERGE_ROOT=""
REJMERGE_CONF="/etc/rejmerge.conf"
REJECTED_DIR="/var/lib/pkg/rejected"
EDITOR=${EDITOR:-vi}
TMPFILE=$(mktemp) || exit 1

main "$@"

# End of file
