#! /bin/sh
# Copyright (c) 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
# Copyright 1999-2010 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

__author__="Karl Trygve Kalleberg"
__email__="<karltk@gentoo.org>"
__description__="Ebuild version bumping tool"

# MOTIVATION
# The ebump utility is a Gentoo-specific tool for bumping the revision of
# a given ebuild and auxiliary files in the Portage tree. It is only
# useful for Gentoo developers with CVS commit access.

VERSION="@VERSION@"

die() {
	echo $1 >&2
	exit -1
}

einfo() {
	if [ ${opt_verbosity:-0} -eq 1 ] ; then
		echo $*
	fi
}

print_version() {
	echo "${__description__}, v${VERSION}"
	echo "Copyright (c) 2004 ${__author__} ${__email__}"
	echo "Copyright 1999-2010 Gentoo Foundation"
	echo "Distributed under the terms of the GNU General Public License v2"
}

print_usage() {
	echo "Usage: ebump <options> foo<.ebuild>"
	echo "Ebuild version bumping tool, v${VERSION}"
	echo "  -V|--version           show version info"
	echo "  -v|--verbose           increase verbosity"
	echo "  -q|--quiet             turn off output"
	echo "  -a|--no-auxfiles       don't bump auxfiles (files/*)"
	echo "  -C|--no-vcs            do not add to VCS"
	echo "  -d|--delete-old        delete previous revision from VCS (DANGEROUS!)"
}

#
# Load options from /etc/gentoolkit/ebump.conf and ${HOME}/.gentoo/ebump.conf
# Home directory file takes precedence.
#
load_options() {
	# FIXME: Sourcing config files like this is really a bad idea; users may
	# easily override any function in this program inside his config files.
	if [ -f "/etc/gentoolkit/ebump.conf" ] ; then
		. /etc/gentoolkit/ebump.conf
	fi
	if [ -f "${HOME}/.gentoo/ebump.conf" ] ; then
		. ${HOME}/.gentoo/ebump.conf
	fi
}

#
# Find closes ebuild to ${1}, if any
#
find_ebuild() {
	local f=${1}

	if [ -f "${f}" ] ; then
		echo ${f}
	fi

	if [ -f "${f}.ebuild" ] ; then
		echo ${f}
	fi
}

#
# splitname (version|name|revision) package-name-version-revision
#
splitname() {
	case $1 in
		version)
			echo ${2} | sed -r "s/.*-([0-9].*)/\1/"
		;;
		name)
			name=$(echo ${2} | sed -r "s/(.*)-[0-9].*/\1/")
			if [ ${name} = ${2} ] ; then
				if [ $(echo ${2} | grep "^[0-9].*") ] ; then
					# The filename starts with a version number, thus it has no
					# name
					name=""
				else
					# The filename doesn't have a recognizeable version number;
					# everything is a name
					name=${2}
				fi
			fi
			echo ${name}
		;;
		revision)
			rev=$(echo ${2} | sed -r "s/.*-r([0-9][0-9]*)/\1/")
			if [ ${rev} = ${2} ] ; then
				rev=0
			fi
			echo ${rev}
		;;
		vernorev)
			ver=$(echo ${2} | sed -r "s/.*-([0-9].*)-r[0-9]+/\1/")
			if [ ${ver} = ${2} ] ; then
				ver=$(echo ${2} | sed -r "s/.*-([0-9].*)/\1/")
			fi
			echo ${ver}
		;;
		*)
			echo
		;;
	esac
}

process_ebuild() {
	local vcs=$1
	shift
	local ebuild_arg="${*}"
	shift $#

	# Files to add to VCS
	local addfiles=""
	# Files to remove from VCS
	local delfiles=""

	if [ -z "${ebuild_arg}" ] ; then
		print_usage
		exit
	fi

	for ebuild in $ebuild_arg; do
		#
		# Try to find a matching ebuild
		#
		local ebuild_name=$(find_ebuild ${ebuild})
		if [ -z "${ebuild_name}" ] ; then
			die "Could not find ${ebuild}"
		fi

		einfo "Processing ebuild ${ebuild_name}"

		#
		# Bump revision suffix (or add one)
		#
		local PF=$(basename ${ebuild_name} .ebuild)
		local PN=$(splitname name ${PF})
		local PV=$(splitname version ${PF})
		local rev=$(splitname revision ${PF})
		local PV_norev=$(splitname vernorev ${PF})
		local newPF=${PN}-${PV_norev}-r$((rev+1))

#		echo $PF / $PN / $PV / $rev / $PV_norev / $newPF

		einfo "Bumped ${PF}.ebuild to ${newPF}.ebuild"

		if [ "${vcs}" = "svn" ]; then
			svn cp ${PF}.ebuild ${newPF}.ebuild
		else
			cp ${PF}.ebuild ${newPF}.ebuild
		fi

		einfo "Reset keywords to ~arch"

		ekeyword '~all' "${newPF}.ebuild"

		addfiles="${addfiles} ${newPF}.ebuild"
		delfiles="${delfiles} ${PF}.ebuild"

		#
		# (Optional) Bump relevant files in files/
		#
		if [ "${opt_bump_auxfiles}" = "y" ] ; then
			# Gather list of auxiliary files in files/ that has a versioned
			# filename, where the version matches our current version.
			local bumplist=""
			for x in $(echo files/*) ; do
				if [ ! -z "$(echo $x | grep "${PV}$")" ] ; then
					bumplist="${bumplist} ${x}"
				fi
			done

			# Bump version of all matches
			for x in ${bumplist} ; do
				local bn=$(basename ${x})
				local dn=$(dirname ${x})
				local newbn

				PN=$(splitname name ${bn})
				PV=$(splitname version ${bn})
				rev=$(splitname revision ${bn})
				PV_norev=$(splitname vernorev ${bn})

#				echo $PN / ${PV_norev} / ${rev}

				# Special case for when we have no name part; filename
				# is just a version number
				if [ -z "${PN}" ] ; then
					newbn=${PV_norev}-r$((rev+1))
				else
					newbn=${PN}-${PV_norev}-r$((rev+1))
				fi

				if [ -d ${dn}/${bn} ] ; then
					if [ -e ${dn}/${newbn} ] ; then
						echo "Directory ${dn}/${newbn} exists, not copying" >&2
					else
						cp -a ${dn}/${bn} ${dn}/${newbn}
						# uhm, is that necessary?
#						find ${dn}/${newbn} -name CVS | xargs rm -rf
					fi
				else
					cp ${dn}/${bn} ${dn}/${newbn}
				fi

				addfiles="${addfiles} ${dn}/${newbn}"
				delfiles="${delfiles} ${dn}/${bn}"

				einfo "Bumped ${dn}/${bn} to ${dn}/${newbn}"
			done
		fi
	done

#	echo "addfiles ${addfiles}"
#	echo "delfiles ${delfiles}"

	#
	# (Optional) Add VCS entry for all new files
	#
	if [ "${opt_add_vcs}" = "y" ] ; then
#		for x in ${addfiles} ; do
#			if [ -d ${x} ] ; then
#				find ${x} -exec ${vcs} add {} ';'
#			else
#				${vcs} add ${x}
#			fi
#		done
		$vcs add $addfiles
		einfo "Added ${addfiles} to VCS"
	fi


	#
	# (Optional) Delete previous entry
	#
	# Could we use 'rm' instead of remove for all vcs?
	if [ "${opt_delete_old}" = "y" ] ; then
#		for x in ${delfiles} ; do
#			if [ "${vcs}" = "cvs" ]; then
#				${vcs} remove -f ${x}
#			elif [ "${vcs}" = "git" ]; then
#				${vcs} rm ${x}
#			else
#				${vcs} remove ${x}
#			fi
#		done
		if [ "${vcs}" = "cvs" ]; then
			$vcs remove -f $delfiles
		elif [ "${vcs}" = "git" ]; then
			$vcs rm $delfiles
		else
			$vcs remove $delfiles
		fi
		einfo "Removed ${delfiles} from VCS"
	fi
}

get_vcs() {
	if [ -d "CVS" ]; then
		echo "cvs"
		return 0
	elif [ -d ".svn" ]; then
		echo "svn"
		return 0
	else
		if command -v git >/dev/null; then
			if [ "$(git rev-parse --is-inside-work-tree)" = true ]; then
				echo "git"
				return 0
			fi
		fi

		echo
		return 1
	fi
}

#
# Global options
#
opt_verbosity=0
opt_add_vcs=y
opt_bump_auxfiles=y
opt_delete_old=n
opt_commitmessage=""

load_options

while [ ${#} -gt 0 ] ; do
	arg=${1}
	shift

	case ${arg} in
		-h|--help)
			print_usage
			exit 0
		;;
		-m|--message)
			opt_commitmessage="${1}"
			shift
			continue
		;;
		-a|--no-auxfiles)
			opt_bump_auxfiles=n
			continue
		;;
		-C|--no-vcs)
			opt_add_vcs=n
			continue
		;;
		-V|--version)
			print_version
			exit
		;;
		-v|--verbose)
			opt_verbosity=1
			continue
		;;
		-q|--quiet)
			opt_verbosity=0
			continue
		;;
		-d|--delete-old)
			opt_delete_old=y
			continue
		;;
		*)
			ebuild_arg="${ebuild_arg:+${ebuild_arg} }${arg}"
			continue
		;;
	esac
done

vcs=$(get_vcs)
if [ -z "${vcs}" ]; then
	echo "Warning: no cvs, git or svn repository found!" >&2
	echo "Changes can't be added to the VCS" >&2
	opt_add_vcs=n
	opt_delete_old=n
fi

process_ebuild "${vcs}" $ebuild_arg

# TODO:
# - put cli parser into separate functions
