#!/bin/bash
# Copyright 2002 Gentoo Technologies, Inc.
# Distributed under the terms of the GNU Public License v2
# Author Brandon Low <lostlogic@gentoo.org>
#
# Previous version (from which I've borrowed a few bits) by:
# Jochem Kossen <j.kossen@home.nl>
# Leo Lipelis <aeoo@gentoo.org>
# Karl Trygve Kalleberg <karltk@gentoo.org>
#
# $Header: /home/cvsroot/gentoo-src/portage/bin/etc-update,v 1.13 2003/08/04 15:21:15 aether Exp $

export PORTAGE_CALLER="etc-update"

function get_config() {
	item=$1

	# First strip off comment lines, then grab the configuration
	# item. If there's more than one of the same configuration item,
	# then allow the last setting to take precedence.
	cut -d'#' -f1-1 /etc/etc-update.conf | \
		sed -ne "s/^\ *$item\ *=\ *\"\(.*\)\"/\1/p" |sed -e '$p;d'
}

function scan() {

	echo "Scanning Configuration files..."
	rm -rf ${TMP}/files > /dev/null 2>&1
	mkdir ${TMP}/files || die "Failed mkdir command!" 1
	count=0
	input=0
	
	for path in ${CONFIG_PROTECT}; do if [ -d ${path} ]; then
		ofile=""
		for file in `find ${path} -iname "._cfg????_*" |
			   sed -e "s:\(^.*/\)\(._cfg*_\)\(/.*$\):\1\2\3\%\2\%\3:" |
			   sort -t'%' -k3 -k2 | cut -f1 -d'%'`; do
			rpath=`echo "${file}" | sed -e "s:/[^/]*$::"`
			rfile=`echo "${file}" | sed -e "s:^.*/::"`
			for mpath in ${CONFIG_PROTECT_MASK}; do
				if [[ "${rpath}" == "${mpath}"* ]]; then
					mv ${rpath}/${rfile} ${rpath}/${rfile:10}
					break
				fi
			done
			[ ! -f ${file} ] && continue


			if [[ "${ofile:10}" != "${rfile:10}" ]] || 
			   [[ ${opath} != ${rpath} ]]; then
				MATCHES=0
				if [[ "${EU_AUTOMERGE}" == "yes" ]]; then
					if [ ! -e "${rpath}/${rfile}" ] || [ ! -e "${rpath}/${rfile:10}" ]; then
						MATCHES=0
					else
						diff -Bbua ${rpath}/${rfile} ${rpath}/${rfile:10} | egrep '^[+-]' | egrep -v '^[+-][\t ]*#|^--- |^\+\+\+ ' | egrep -qv '^[-+][\t ]*$'
						MATCHES=$?
					fi
				elif [[ -z `diff -Nua ${rpath}/${rfile} ${rpath}/${rfile:10}|
							  grep "^[+-][^+-]"|grep -v '# .Header:.*'` ]]; then
					MATCHES=1
				fi
				if [[ "${MATCHES}" == "1" ]]; then
					echo "Automerging trivial changes in: ${rfile:10}"
					mv ${rpath}/${rfile} ${rpath}/${rfile:10}
					continue
				else
					count=${count}+1
					echo "${rpath}/${rfile:10}" > ${TMP}/files/${count}
					echo "${rpath}/${rfile}" >> ${TMP}/files/${count}
					ofile="${rfile}"
					opath="${rpath}"
					continue
				fi
			fi

			if [[ -z `diff -Nua ${rpath}/${rfile} ${rpath}/${ofile}|
					  grep "^[+-][^+-]"|grep -v '# .Header:.*'` ]]; then
				mv ${rpath}/${rfile} ${rpath}/${ofile}
				continue
			else
				echo "${rpath}/${rfile}" >> ${TMP}/files/${count}
				ofile="${rfile}"
				opath="${rpath}"
			fi
		done
	fi; done

}

function sel_file() {
	local -i isfirst=0
	until [ -f ${TMP}/files/${input} ] || [ ${input} == -1 ] || [ ${input} == -3 ]; do
		for file in `ls ${TMP}/files|sort -n`; do
			if (( ${isfirst} == 0 )); then
				isfirst=${file}
			fi
			echo -n "${file}${PAR} "
			if (( ${mode} == 0 )); then
				for word in `cat ${TMP}/files/${file}`; do
					echo ${word}
				done
			else
				head -n1 ${TMP}/files/${file}
			fi
		done > ${TMP}/menuitems

		if [ "${OVERWRITE_ALL}" == "yes" ]; then
			input=0
		else
			if (( ${mode} == 0 )); then
				echo "The following is the list of files which need updating, each 
configuration file is followed by a list of possible replacement files."
			else
				local my_title="Please select a file to update"
			fi

			if (( ${mode} == 0 )); then
				cat ${TMP}/menuitems
				echo    "Please select a file to edit by entering the corresponding number."
				echo    "              (-1 to exit) (-3 to auto merge all remaining files)"
				echo -n "                           (-5 to auto-merge AND not use 'mv -i'): "
				read input
			else
				dialog --title "${title}" --menu "${my_title}" \
					0 0 0 `echo "-1 Exit";cat ${TMP}/menuitems` \
					2> ${TMP}/input
				input=`cat ${TMP}/input`
			fi
			if (( ${input} == -5 )); then
				input=-3
				export mv_opts=""
			fi
			if (( ${input} == -3 )); then
				input=0
				export OVERWRITE_ALL="yes"
			fi
		fi # -3 automerge
		if (( ${input} == 0 )); then
			input=${isfirst}
		fi
	done
}

function do_file() {
	echo
	local -i my_input
	local -i fcount=0
	until (( `cat ${TMP}/files/${input}|wc -l` < 2 )); do
		my_input=0
		if (( `cat ${TMP}/files/${input}|wc -l` == 2 )); then
			my_input=1
		fi
		until (( ${my_input} > 0 )) && (( ${my_input} < `cat ${TMP}/files/${input}|wc -l` )); do
			fcount=0

			if [ "${OVERWRITE_ALL}" == "yes" ]; then
				my_input=0
			else
				for line in `cat ${TMP}/files/${input}`; do
					if (( ${fcount} > 0 )); then
						echo -n "${fcount}${PAR} "
						echo "${line}"
					else
						if (( ${mode} == 0 )); then
							echo "Below are the new config files for ${line}:"
						else
							local my_title="Please select a file to process for ${line}"
						fi
					fi
					fcount=${fcount}+1
				done > ${TMP}/menuitems

				if (( ${mode} == 0 )); then
					cat ${TMP}/menuitems
					echo -n "Please select a file to process (-1 to exit this file): "
					read my_input
				else
					dialog --title "${title}" --menu "${my_title}" \
						0 0 0 `cat ${TMP}/menuitems;echo "${fcount} Exit"` \
						2> ${TMP}/input
					my_input=`cat ${TMP}/input`
				fi
			fi # OVERWRITE_ALL

			if (( ${my_input} == 0 )); then
				my_input=1
			elif (( ${my_input} == -1 )); then
				return
			elif (( ${my_input} == ${fcount} )); then
				break
			fi
		done
		if (( ${my_input} == ${fcount} )); then
			break
		fi

		fcount=${my_input}+1

		file=`cat ${TMP}/files/${input} | sed -e "${fcount}p;d"`
		ofile=`head -n1 ${TMP}/files/${input}`

		do_cfg "${file}" "${ofile}"

		cat ${TMP}/files/${input}|sed -e "${fcount}!p;d" > ${TMP}/files/sed
		mv ${TMP}/files/sed ${TMP}/files/${input}

		if (( ${my_input} == -1 )); then
			break
		fi
	done
	echo
	rm ${TMP}/files/${input}
	count=${count}-1
}

function do_cfg() {
	
	local file="${1}"
	local ofile="${2}"
	local -i my_input=0
	
	until (( ${my_input} == -1 )) || [ ! -f ${file} ]; do
		if [ "${OVERWRITE_ALL}" == "yes" ]; then
			my_input=1
		else
		  showdiffcmd=$(echo "${diff_command}" |
				sed -e "s:%file1:${ofile}:" -e "s:%file2:${file}:")
				
			if [ "${using_editor}" == 0 ]; then
				(
					echo "Showing differences between ${ofile} and ${file}"
					${showdiffcmd}
				) | ${pager}
			else
				echo "Beginning of differences between ${ofile} and ${file}"
				${showdiffcmd}
				echo "End of differences between ${ofile} and ${file}"
			fi
			if [ -L "${file}" ]; then
				echo
				echo "-------------------------------------------------------------"
				echo "NOTE: File is a symlink to another file. REPLACE recommended."
				echo "      The original file may simply have moved. Please review."
				echo "-------------------------------------------------------------"
				echo
			fi
			echo -n "1) Replace original with update
2) Delete update, keeping original as is
3) Interactively merge original with update
4) Show differences again
Please select from the menu above (-1 to ignore this update): "
			read my_input
		fi
		
		case ${my_input} in
			1) echo "Replacing ${ofile} with ${file}"
			   mv ${mv_opts} ${file} ${ofile}
			   continue
			   ;;
			2) echo "Deleting ${file}"
			   rm ${rm_opts} ${file}
			   continue
			   ;;
			3) do_merge "${file}" "${ofile}"
			   my_input=${?}
#			   [ ${my_input} == 255 ] && my_input=-1
			   continue
			   ;;
			4) continue
			   ;;
			*) continue
			   ;;
		esac
	done
}

function do_merge() {
	
	local file="${1}"
	local ofile="${2}"
	local mfile="${2}.merged"
	local -i my_input=0
	echo "${file} ${ofile} ${mfile}"

	if [ -e ${mfile} ] ; then
		echo "A previous version of the merged file exists, cleaning..."
		rm ${rm_opts} ${mfile}
	fi

	until (( ${my_input} == -1 )); do
		echo "Merging ${file} and ${ofile}"
		`echo "${merge_command}" |
		 sed -e "s:%merged:${mfile}:g" \
		 	 -e "s:%orig:${ofile}:g" \
			 -e "s:%new:${file}:g"`
		until (( ${my_input} == -1 )); do	
			echo -n "1) Replace ${ofile} with merged file
2) Show differences between merged file and original
3) Remerge original with update
4) Return to the previous menu
Please select from the menu above (-1 to exit, losing this merge): "
			read my_input
			case ${my_input} in
				1) echo "Replacing ${ofile} with ${mfile}"
				   chmod --reference=${ofile} ${mfile}
				   mv ${mv_opts} ${mfile} ${ofile}
				   rm ${rm_opts} ${file}
				   return 255
				   ;;
				2) ( echo "Showing differences between ${ofile} and ${mfile}"
					 `echo "${diff_command}" | \
					  sed -e "s:%file1:${ofile}:" \
					  	  -e "s:%file2:${mfile}:"` ) | ${pager}
				   continue
				   ;;
				3) break
				   ;;
				4) rm ${rm_opts} ${mfile}
				   return 0
				   ;;
				*) continue
				   ;;
			esac
		done
	done
	rm ${rm_opts} ${mfile}
	return 255
}

function die() {
	trap "" term
	trap "" kill
	echo "Exiting: ${1}"
	rm -rf ${TMP}
	exit ${2}
}

#
# Run the script
#
scriptname=`basename $0`

trap die term

TMP=/tmp/$$
rm -rf ${TMP} 2> /dev/null
mkdir ${TMP} || die "failed mkdir command!" 1

# I need the CONFIG_PROTECT value
CONFIG_PROTECT=$(/usr/lib/portage/bin/portageq config_protect)
CONFIG_PROTECT_MASK=$(/usr/lib/portage/bin/portageq config_protect_mask)

# load etc-config's configuration
EU_AUTOMERGE=`get_config eu_automerge`
rm_opts=`get_config rm_opts`
mv_opts=`get_config mv_opts`
cp_opts=`get_config cp_opts`
pager=`get_config pager`
diff_command=`get_config diff_command`
using_editor=`get_config using_editor`
merge_command=`get_config merge_command`
declare -i mode=`get_config mode`
[ -z ${mode} ] && mode=0
[ -z "${pager}" ] && pager="cat"

#echo "rm_opts: $rm_opts, mv_opts: $mv_opts, cp_opts: $cp_opts"
#echo "pager: $pager, diff_command: $diff_command, merge_command: $merge_command" 

if (( ${mode} == 0 )); then
	PAR=")"
else
	PAR=""
	rm_opts=""
	mv_opts=""
	cp_opts=""
	pager=""P
fi

declare -i count=0
declare -i input=0
declare title="Gentoolkit's etc-update tool!"

scan

until (( ${input} == -1 )); do
	if (( ${count} == 0 )); then
		die "Nothing left to do; exiting. :)" 0
	fi
	sel_file
	if (( ${input} != -1 )); then
		do_file
	fi
done

die "User termination!" 0
