#!/bin/bash
#
#   srcpac - A tool to rebuild official Arch Linux packages from source
#
#   Copyright (C) 2004-2009 Jason Chu <jason@archlinux.org>
#   Copyright (C) 2009-2010 Andrea Scarpino <andrea@archlinux.org>
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
#

ver=0.7

declare -a args

[ -f /etc/abs.conf ] && source /etc/abs.conf
[ -f /etc/makepkg.conf ] && source /etc/makepkg.conf
[ -f ~/.makepkg.conf ] && source ~/.makepkg.conf

usage()
{
	echo "usage:  srcpac {-h --help}"
	echo "        srcpac {-V --version}"
	echo "        srcpac {-R --remove}  [options] <package(s)>"
	echo "        srcpac {-S --sync}    [options] [package(s)]"
	echo "        srcpac {-U --upgrade} [options] <file(s)>"
	echo
	echo "srcpac's options are based on pacman, so check the pacman man page"
	echo "srcpac adds some option to -S:"
	echo "-b, builds the targets from source"
	echo "-e, edit PKGBUILD with your favorite editor"
	echo "-o, applies config changes and displays the PKGBUILD in your pager app without building"
	
	exit 0
}

version()
{
	echo "                       srcpac v${ver}"
	echo "                       Copyright (C) 2004 Jason Chu <jason@archlinux.org>"
	echo "                       Copyright (C) 2009 Andrea Scarpino <andrea@archlinux.org>"
	echo
	echo "                       This program may be freely redistributed under"
	echo "                       the terms of the GNU General Public License"
	pacman -V

	exit 0
}

##
# Applies changes of source build packages to PKGUILDs,
# if -o option is set we only display the PKGBUILD in a pager
##
apply_config()
{
	conf=/etc/srcpac.d/${1}
	if [ -f ${conf} ]; then
		if [ "${3}" == 'noreplace' ]; then
			if [ "${PAGER}" != '' ]; then
				sed -f ${conf} ${2}/PKGBUILD | ${PAGER}
			else
				sed -f ${conf} ${2}/PKGBUILD | less
			fi
		else
			sed -i -f ${conf} ${2}/PKGBUILD
		fi
	fi
}

##
# Searches the ABS tree for a possible package candidates
##
get_candidates()
{
	candidates=$(find ${ABSROOT} -type d -name ${1})
	if [ "${candidates}" = "" ]; then
		echo "Error: Could not find \"${1}\" under ${ABSROOT}"
		exit 1
	fi
}

get_pkgname()
{
	local tmp

	tmp=${1##*/}
	tmp=${tmp%-$CARCH*}
	tmp=${tmp%-any*}
	echo ${tmp%-*-*}
}

refresh()
{
	abs
	pacman -Sy
	if [ ${#args[@]} -eq 0 -a $SYSUPGRADE -eq 0 ]; then
		exit 0
	fi
}

build_packages()
{
	action="build"
	if [ "$1" = "install" ]; then
		action="install"
	fi

	shift

	while [ $# -ne 0 ]; do
		pkg=$1
		get_candidates $pkg
		success=0

		for pkgdir in ${candidates}; do
			if [ -f ${pkgdir}/PKGBUILD ]; then
				builddir=/var/srcpac${pkgdir/${ABSROOT}//}

				# seperate out our makepkg flags
				MAKEPKGOPTS="-c -s -r -f"
				
				# create the build dir and apply configuration
				[[ -d ${builddir} ]] && rm -rf ${builddir}
				mkdir -p ${builddir}
				cp ${pkgdir}/* ${builddir}
				apply_config ${pkg} ${builddir}

				cd ${builddir}

				if [ ${EDIT} -eq 1 ]; then
					[ -z "$EDITOR" ] && EDITOR=nano
					$EDITOR PKGBUILD
				fi

				if [ "${SUDO_USER}" = "" ]; then
					chown -R nobody ${builddir}
					sudo -u nobody makepkg ${MAKEPKGOPTS}
				else
					chown -R ${SUDO_USER} ${builddir}
					su ${SUDO_USER} -m -c "makepkg ${MAKEPKGOPTS}"
				fi
				
				ret=$?
				if [ $ret -ne 0 ]; then
					break
				fi

				cp ${builddir}/*${PKGEXT} /var/cache/pacman/pkg/

				if [ "${action}" = "install" ]; then
					pacargs="-U"
					[ $NODEPS -eq 1 ] && pacargs="${pacargs}d"
					[ $FORCE -eq 1 ] && pacargs="${pacargs}f"
					[ $ROOT -eq 1 ] && pacargs="${pacargs} -r $NEWROOT"
					pacman $pacargs ${builddir}/*${PKGEXT}
				fi

			fi
		done
		unset MAKEPKGOPTS

		if [ $ret -ne 0 ]; then
			echo "Error: Failed to build \"${pkg}\""
			exit 1
		fi

		if [ "${action}" = "install" ]; then
			if [ ! -d /var/lib/srcpac ]; then
				mkdir /var/lib/srcpac
			fi
			
			touch /var/lib/srcpac/${pkg}
		fi
		shift
	done
}

function check_args()
{
	# Options
	MAJOR=""
	FORCE=0
	ROOT=0
	NEWROOT=""
	NODEPS=0
	BUILD=0
	REFRESH=0
	SYSUPGRADE=0
	DOWNLOAD=0
	ONLYCONF=0
	IGNORE=0
	IGNOREPKG=""
	NOCONFIRM=0
	EDIT=0

	ARGLIST=$@
	ARGSANS=""

	while [ "$#" -ne "0" ]; do
		case $1 in
			--help) usage ;;
			--version) version ;;
			--remove)
				MAJOR="remove"
				ARGSANS="$ARGSANS $1"
				;;
			--upgrade)
				MAJOR="upgrade"
				ARGSANS="$ARGSANS $1"
				;;
			--sync)
				MAJOR="sync"
				ARGSANS="$ARGSANS $1"
				;;
			--force)
				FORCE=1
				ARGSANS="$ARGSANS $1"
				;;
			--root)
				ROOT=1
				NEWROOT="$2"
				ARGSANS="$ARGSANS $1 $2"
				shift
				;;
			--nodeps)
				NODEPS=1
				ARGSANS="$ARGSANS $1"
				;;
			--build)
				BUILD=1
				ARGSANS="$ARGSANS $1"
				;;
			--refresh)
				REFRESH=1
				ARGSANS="$ARGSANS $1"
				;;
			--sysupgrade)
				SYSUPGRADE=1
				ARGSANS="$ARGSANS $1"
				;;
			--downloadonly)
				DOWNLOAD=1
				ARGSANS="$ARGSANS $1"
				;;
			--onlyconf)
				ONLYCONF=1
				ARGSANS="$ARGSANS $1"
				;;
			--ignore)
				IGNORE=1
				IGNOREPKG="$IGNOREPKG $2"
				ARGSANS="$ARGSANS $1 $2"
				;;
			--noconfirm)
				NOCONFIRM=1
				ARGSANS="$ARGSANS $1"
				;;
			--edit)
				EDIT=1
				ARGSANS="$ARGSANS $1"
				;;
			--*)
				ARGSANS="$ARGSANS $1"
				;;
			-*)
				ARGSANS="$ARGSANS $1"
				if [ $(echo $1 | grep r) ]; then
					OPTIONAL=$2
				fi
				OPTIND=1
				while getopts ":VRUFSdfbyur:eow" opt $1 $OPTIONAL; do
					case $opt in
						V) version ;;
						R) MAJOR="remove" ;;
						U) MAJOR="upgrade" ;;
						S) MAJOR="sync" ;;
						f) FORCE=1 ;;
						r) ROOT=1
						   NEWROOT="${OPTARG}"
						   ;;
						d) NODEPS=1 ;;
						b) BUILD=1 ;;
						y) REFRESH=1 ;;
						u) SYSUPGRADE=1 ;;
						w) DOWNLOAD=1 ;;
						e) EDIT=1 ;;
						o) ONLYCONF=1 ;;
					esac
				done
				;;
			*)
				args[${#args[@]}]=$1
				;;
		esac
		shift
	done
}

function main()
{
	check_args $@

	if [ "${MAJOR}" = "" ]; then
		usage
	fi
	
	if [ ${UID} -ne 0 -a "${SUDO_USER}" = "" ]; then
		echo "Error: You need to use sudo or to be root"
		exit 1
	fi

	if [ "${ABSROOT}" = "" ]; then
		echo "Error: The ABSROOT environment variable is not defined."
		exit 1
	fi

	if [ "${MAJOR}" = "remove" -o "${MAJOR}" = "upgrade" ]; then
		pacman $ARGLIST
		
		if [ -d /var/lib/srcpac -a "${MAJOR}" = "remove" ]; then
			for pkg in ${args[@]}; do
				if [ -f /var/lib/srcpac/${pkg} ]; then
					echo -n "removing source reference ${pkg}... "
					rm /var/lib/srcpac/${pkg}
					echo "done"
				fi
			done
		fi
	fi

	if [ "${MAJOR}" = "sync" ]; then
		if [ $BUILD -eq 0 -a $ONLYCONF -ne 1 ]; then
			if [ $SYSUPGRADE -eq 1 -a -d /var/lib/srcpac ]; then
				# TODO: Needs support for "x is up to date.  Upgrade anyway?"
				if [ $REFRESH -eq 1 ]; then
					refresh
				fi

				ignorestr=""
				if [ $IGNORE -eq 1 ]; then
					ignorestr="--ignore $IGNOREPKG"
				fi
				output=$(pacman -Spu --noconfirm ${ignorestr})
				ret=$?

				if [ $ret -ne 0 ]; then
					echo $output
					exit $ret
				fi

				declare -a packages
				for line in $output; do
					# ensure the string is a URL
					if [ $( echo "${line}" | grep -F '://' ) ]; then
						packages[${#packages[@]}]=$(get_pkgname ${line})
					fi
				done

				# if packages array is empty, there is nothing to upgrade
				if [ ${#packages[@]} -eq 0 ]; then
					exit 0
				fi

				declare -a sourcepac
				declare -a regpac

				for pkg in ${packages[@]}; do
					if [ -f /etc/srcpac.d/${pkg} ]; then
						sourcepac[${#sourcepac[@]}]=$pkg
					else
						regpac[${#regpac[@]}]=$pkg
					fi
				done

				pacargs="-S"
				[ $NODEPS -eq 1 ] && pacargs="${pacargs}d"
				[ $FORCE -eq 1 ] && pacargs="${pacargs}f"
				[ $ROOT -eq 1 ] && pacargs="${pacargs} -r $NEWROOT"

				pacman $pacargs ${regpac[*]} $ignorestr

				# no packages to install from source, quit then
				if [ ${#sourcepac[@]} -eq 0 ]; then
					exit 0
				fi

				echo
				echo "Source Targets: ${sourcepac[*]}"
				echo
				if [ ${NOCONFIRM} -eq 0 ]; then
					echo -n "Proceed? [Y/n] "
					read
					if [ "${REPLY}" != "y" -a "${REPLY}" != "Y" -a "${REPLY}" != "" ]; then
						exit 0
					fi
				fi

				build_packages "install" ${sourcepac[@]}

			else
				if [ $REFRESH -eq 1 ]; then
					refresh
				fi
				pacman $ARGLIST
			fi
		else
			if [ $REFRESH -eq 1 ]; then
				refresh
			fi

			if [ $DOWNLOAD -eq 1 ]; then
				build_packages "build" ${args[@]}
				exit 0
			fi

			if [ $SYSUPGRADE -eq 1 ]; then
				# TODO: Needs support for "x is up to date.  Upgrade anyway?"
				output=$(pacman -Spu --noconfirm)
			else
				# TODO: Needs support for "x is up to date.  Upgrade anyway?"
				output=$(pacman -Sp ${args[*]} --noconfirm)
			fi

			if [ $? -ne 0 ]; then
				echo $output
				exit $?
			fi

			declare -a packages
			for line in $output; do
				# ensure the string is a URL
				if [ $( echo "${line}" | grep -F '://' ) ]; then
					packages[${#packages[@]}]=$(get_pkgname ${line})
				fi
			done

			if [ ${#packages[@]} -eq 0 ]; then
				exit 0
			fi

			if [ $ONLYCONF -eq 1 ]; then
				for pkg in ${packages[@]}; do
					get_candidates $pkg
					success=0
					for pkgdir in ${candidates}; do
						if [ -f ${pkgdir}/PKGBUILD ]; then
							# Look for config options and apply them
							apply_config $pkg $pkgdir noreplace
							success=1
							break
						fi
					done
					if [ ${success} -eq 0 ]; then
						echo "Error: Failed to find \"${pkg}\""
						exit 1
					fi
				done
				exit 0
			fi

			echo
			echo "Source Targets: ${packages[*]}"
			echo
			if [ ${NOCONFIRM} -eq 0 ]; then
				echo -n "Proceed? [Y/n]"
				read
				if [ "${REPLY}" != "y" -a "${REPLY}" != "Y" -a "${REPLY}" != "" ]; then
					exit 0
				fi
			fi
			
			build_packages "install" ${packages[@]}
		fi
	fi
}

main $@

# vim:ts=4:sw=4:noet:enc=utf-8:
