#!/bin/bash
# vim: set et sw=4 sts=4 tw=80:
# Copyright 2005-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

# 0.3: cleanups, disable old layout rebuild default, show packages to rebuild
# 0.2: adds support for libobjc change
# 0.1: initial version, allows layout migration


# Based on perl-cleaner
GNUSTEP_UPDATER_VERSION="0.3"

SUPPORTED_PMS="portage pkgcore paludis"
PMS_COMMAND=( "emerge" "pmerge" "cave resolve" )
PMS_OPTIONS=( "-vD1" "-Do" "-x1z" )
PMS_PRETEND=( "-p" "-p" "--no-execute" )
CUSTOM_PMS_COMMAND=""

PKGS_TO_REMERGE=""

PKGS_EXCEPTIONS="gnustep-base/gnustep-make gnustep-base/gnustep-updater gnustep-base/libobjc2 sys-devel/gcc"
PKGS_MANUAL="gnustep-libs/highlighterkit virtual/gnustep-back"

PKG_DBDIR=/var/db/pkg

. /etc/init.d/functions.sh || {
	echo "$0: Could not source /etc/init.d/functions.sh!"
	exit 1
}

veinfo() {
	if [[ VERBOSE -ge $1 ]] ; then
		shift
		einfo "$@"
	fi
}

update_packages(){
	local content exp 
	echo
	einfo "Locating packages for an update"

	if ${LIBOBJC} ; then
	        veinfo 1 "Locating ebuilds linked against libobjc"
		if ! type -P scanelf >/dev/null 2>&1; then
			ewarn "scanelf not found! Install app-misc/pax-utils."
			ewarn "--libobjc is disbled."
			LIBOBJC=false
		else
			SONAME="$(awk -F: '/^OBJC_LIB_FLAG/ {print $2}' /usr/share/GNUstep/Makefiles/config.make)"
			: ${SONAME:=libobjc.so.2}
			veinfo 4 SONAME="${SONAME}"
		fi
	fi
	
	# iterate thru all the installed package's contents
	while IFS= read -r -d $'\0' content; do
		# extract the category, package name and package version
		#CPV=$(sed "s:${PKG_DBDIR}/\(.*\)/CONTENTS:\1:" <<< ${content} )
		CPV=${content#${PKG_DBDIR}/}
		CPV=${CPV%/CONTENTS}
		CATPKG="${CPV%-[0-9]*}"
		veinfo 3 "Checking ${CPV}"

		# exclude packages that are an exception
		exception=0
		for exp in ${PKGS_EXCEPTIONS} ; do
			if [[ -z "${CATPKG##${exp}}" ]]; then
				veinfo 2 "Skipping ${CATPKG}, reason: exception"
				exception=1
				break
			fi
		done

		[[ ${exception} == 1 ]] && continue

		# Check if package is in PKGS_MANUAL
		for pkg in ${PKGS_MANUAL} ; do
			if [ -z "${CATPKG##${pkg}}" ] ; then
				exception=2
				break;
			fi
		done
		SLOT=$(< ${content/CONTENTS/SLOT})
		CATPKGVER="${CATPKG}:${SLOT}"

		if [[ ${exception} = 2 ]] ; then
			PKGS_TO_REMERGE="${PKGS_TO_REMERGE} ${CATPKGVER}"
			eindent
			einfo "Adding to list: ${CATPKGVER}"
			eindent
			veinfo 1 "check: manual"
			eoutdent && eoutdent
			continue
		fi

		if ${OLD_LAYOUT}; then
            while read -r type file ; do
		    	shopt -s extglob
	    		[[ ${type} == obj ]] || continue
    			[[ ${file} =~ ^/usr/GNUstep ]] || continue
			    file=${file% +(!([[:space:]])) +([[:digit:]])}
		    	shopt -u extglob
	    		PKGS_TO_REMERGE+=" ${CATPKGVER}"
    			exception=3
			    eindent
		    	einfo "Adding to list: ${CATPKGVER}"
	    		eindent
    			veinfo 1 "check: file ${file}"
		    	eoutdent && eoutdent
			    break
	    	done < "${content}"
        fi

		[[ ${exception} == 3 ]] && continue
	
		if ${LIBOBJC} ; then
			# Look in bin and libs
			broken_libs="$(scanelf -qBn < <(awk '/^(obj|sym) [^ ]*\/(s?bin|lib(32|64)?)\// && ! /^obj [^ ]*\/usr\/lib\/debug\//{ print $2 }' ${content} ) | grep -o 'libobjc\.so\.[0-9.]*' | sort -u )"
			if [[ -n "${broken_libs}" ]] ; then
				if [[ ${broken_libs} != ${SONAME} ]] ; then
					PKGS_TO_REMERGE+=" ${CATPKGVER}"
					eindent
					einfo "Adding to list: ${CATPKGVER}"
					eindent
					veinfo 1 "check: libobjc ${broken_libs}"
					eoutdent && eoutdent
				else
					eindent
					veinfo 3 "Not adding: ${CATPKGVER} because it should be uptodate."
					veinfo 3 "check: libobjc ${broken_libs}"
					eoutdent
				fi
			fi
	        fi
	done < <( find -L ${PKG_DBDIR}/${1} -path "*/-MERGING-*" -prune -o -name CONTENTS -print0 )
	# Pipe to command if we have one
	if [[ -n ${PIPE_COMMAND} ]] ; then
		echo "${PKGS_TO_REMERGE}" | ${PIPE_COMMAND}
		exit $?
	fi

	if [[ ${PMS_COMMAND[${PMS_INDEX}]} == emerge && -x /usr/bin/portageq ]] ; then
		# Filter out --getbinpkg, --getbinpkgonly, --usepkg and --usepkgonly options in EMERGE_DEFAULT_OPTS
		emerge_default_opts=""
		for option in $(portageq envvar EMERGE_DEFAULT_OPTS ) ; do
			if [[ "${option}" == -[[:alnum:]]* ]]; then
				[[ ${option//[gGkK]/} != - ]] && emerge_default_opts+=" ${option//[gGkK]/}"
			elif [[ "${option}" != "--getbinpkg" && "${option}" != "--getbinpkgonly" && "${option}" != "--usepkg" && "${option}" != "--usepkgonly" ]]; then
				emerge_default_opts+=" ${option}"
			fi
		done
		export EMERGE_DEFAULT_OPTS="${emerge_default_opts# }"
	fi

	# only pretending?
	${PRETEND} && PMS_OPTIONS[${PMS_INDEX}]="${PMS_OPTIONS[${PMS_INDEX}]} ${PMS_PRETEND[${PMS_INDEX}]}"

	# (Pretend to) remerge packages
	if [[ -n ${PKGS_TO_REMERGE} ]] ; then
		pmscmd="${CUSTOM_PMS_COMMAND}"
		[[ -z ${pmscmd} ]] && pmscmd="${PMS_COMMAND[${PMS_INDEX}]}"
		cmd="${pmscmd} ${PMS_OPTIONS[${PMS_INDEX}]} ${ADDITIONAL_OPTIONS} ${PKGS_TO_REMERGE}"
		einfo ${cmd}
		if ! ${cmd} ; then
			einfo "gnustep-updater is stopping here:"
			einfo "Fix the problem and start gnustep-updater again."
			exit 1
		fi
	else
		einfo "No package needs to be reinstalled."
	fi
}

usage() {
	cat << EOF_USAGE
${0##*/} -- Find & rebuild GNUstep packages installed in the old layout, or after a libobjc change

Usage: $0 [OPTION]

Options:
  -h, --help       Print usage
  -V, --version    Print version
  -p, --pretend    Pretend (don't do anything)
  -o, --old-layout Rebuild GNUstep packages installed in the old layout
  -l, --libobjc    Rebuild GNUstep packages after a libobjc change
  -v, --verbose    Increase verbosity (may be specified multiple times)
  -P PM, --package-manager PM
                 Use package manager PM, where PM can be one of:
$(for p in ${SUPPORTED_PMS} ; do
echo -ne $'\t\t  '\* ${p}
if [[ ${p} == portage ]] ; then
	echo ' (Default)'
else
	echo
fi
done )
  -- OPTIONS     Pass additional options to package manager
EOF_USAGE
exit 0
}

ADDITIONAL_OPTIONS=""
LIBOBJC=false
OLD_LAYOUT=false
PRETEND=false
VERBOSE=0

while [[ -n "$1" ]] ; do
	case "$1" in
		help|--help|-h)
			usage
			;;
		version|--version|-V)
			echo "${GNUSTEP_UPDATER_VERSION}"
			exit 0
			;;
        -o|--old-layout)
            OLD_LAYOUT=true
            ;;
		-l|--libobjc)
			LIBOBJC=true
			;;
		-p|--pretend|--dry-run)
			PRETEND=true
			;;
		-v|--verbose)
			VERBOSE=$(( ${VERBOSE} + 1 ))
			;;
		-P|--package-manager)
			shift
			PACKAGE_MANAGER="$1"
			case "${PACKAGE_MANAGER}" in
				portage|pkgcore|paludis)
					;;
				*)
					echo "unrecognised package manager selected. please select between ${SUPPORTED_PMS}"
					exit
					;;
			esac

			# PMS_INDEX is used to select the right commands and options for the selected package manager
			PMS_INDEX=0
			for PM in ${SUPPORTED_PMS} ; do
				[[ ${PM} == ${PACKAGE_MANAGER} ]] && break
				PMS_INDEX=$((${PMS_INDEX} + 1))
			done
			;;
		--package-manager-command)
			shift
			CUSTOM_PMS_COMMAND="$1"
			;;
		--)
			shift
			ADDITIONAL_OPTIONS="${ADDITIONAL_OPTIONS} $*"
			break
			;;
		*)
			usage
			echo "unrecognised option: $1"
			exit 0
			;;
	esac
	shift
done

(${LIBOBJC} || ${OLD_LAYOUT}) && update_packages || usage

exit 0
