#!/bin/bash
# Copyright 2007-2011 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2 or later
# $Id: emacs-updater 1642 2011-01-07 19:21:07Z ulm $

# Authors:
# Christian Faulhammer <fauli@gentoo.org>
# Ulrich Mueller <ulm@gentoo.org>

VERSION=1.8
EMACS=/usr/bin/emacs
SITELISP=/usr/share/emacs/site-lisp

# Default actions
ACTIONS="sitedir rebuild"

# Default package manager
PM_COMMAND=pm_auto
PM_EXTRAOPTS=( )

# Other default variable settings
BATCH=
EXACT=
MAJOR=
NOCOLOUR=
ORPHANS=
PRETEND=

usage() {
    sed -e 's/^X//' <<-EOF
	Usage: ${0##*/} [OPTION]...
	Rebuild Emacs packages that were compiled by another Emacs version,
	or that are installed in the wrong location.
	X
	X  -a, --action ACTION[,ACTION]...
	X                         specify actions, comma-separated list of:
	X                         'sitedir': find site-init files not in the
	X                           new-style location
	X                         'rebuild': rebuild packages with Elisp files
	X                           byte-compiled by a different Emacs version
	X                         'all': rebuild all packages that have
	X                           byte-compiled Elisp files
	X                         (default: sitedir,rebuild)
	X  -b, --batch            batch mode, don't ask any questions
	X  -e, --exact            match exact versions when remerging packages
	X  -m, --major            use only the major version when comparing
	X                         Emacs version numbers
	X  -n, --nocolour, --nocolor
	X                         disable colour in output
	X  -o, --orphans          list orphan files
	X  -p, --pretend          don't actually emerge packages
	X  -P, --package-manager PM
	X                         select a package manager. PM is one out of
	X                         'portage', 'pkgcore', or 'paludis'
	X                         (default: automatically determined)
	X      --package-manager-command CMD
	X                         call CMD instead of the default package
	X                         manager command. If CMD includes options
	X                         the whole command string must be quoted
	X  -h, --help             display this help and exit
	X      --version          output version information and exit
	X
	X  -- OPTIONS             Pass additional OPTIONS to package manager
	EOF
    exit ${1}
}

version() {
    cat <<-EOF
	Emacs updater version ${VERSION}
	Copyright 2007-2011 Gentoo Foundation.
	Distributed under the terms of the GNU GPL version 2 or later.

	Gentoo Emacs project: <http://www.gentoo.org/proj/en/lisp/emacs/>
	EOF
    exit
}

# Wrapper for package manager commands
have_portage() { type -P emerge >/dev/null; }
pm_portage() { emerge --oneshot "$@"; }

have_pkgcore() { type -P pmerge >/dev/null; }
pm_pkgcore() { pmerge --oneshot "$@"; }

have_paludis() { type -P cave >/dev/null; }
pm_paludis() { cave resolve --execute --preserve-world "$@"; }

pm_auto() {
    local pm
    # Try environment variable first
    case ${PACKAGE_MANAGER} in
	portage|pkgcore|paludis) pm=${PACKAGE_MANAGER} ;;
    esac
    for pm in ${pm} portage pkgcore paludis; do
	if have_${pm}; then
	    PM_COMMAND=pm_${pm}
	    ${PM_COMMAND} "$@"
	    return
	fi
    done
    failure "No package manager found"
    return 1
}

# Read in all command-line options and force English output
OPTIONS=$(LC_ALL=C getopt -o a:behmnopP: \
    --long action:,batch,exact,help,major,nocolour,nocolor,orphans,pretend \
    --long package-manager:,package-manager-command:,version \
    -n 'emacs-updater' -- "$@")
[ $? -eq 0 ] || usage 1

eval set -- "${OPTIONS}"

while true
do
    case "${1}" in
	-h|--help)      usage 0 ;;
	--version)      version ;;
	-b|--batch)     BATCH="true"; shift ;;
	-e|--exact)     EXACT="true"; shift ;;
	-m|--major)     MAJOR="true"; shift ;;
	-o|--orphans)   ORPHANS="true"; shift ;;
	-p|--pretend)   PRETEND="true"; shift ;;
	-n|--nocolour|--nocolor) NOCOLOUR="true"; shift ;;
	-a|--action)
	    ACTIONS=
	    for action in ${2/,/ }; do
		case "${action}" in
		    sitedir|rebuild|all)
			ACTIONS="${ACTIONS}${ACTIONS:+ }${action}"
			;;
		    *)
			echo "Invalid action '$action' given!"
			usage 20
			;;
		esac
	    done
	    shift 2
	    ;;
	-P|--package-manager)
	    case "${2}" in
		auto|automatic) PM_COMMAND=pm_auto ;;
		portage|pkgcore|paludis)
		    if ! have_${2}; then
			echo "Package manager '${2}' not found!"
			exit 22
		    fi
		    PM_COMMAND=pm_${2}
		    ;;
		*)
		    echo "Package manager '${2}' not known!"
		    usage 21
		    ;;
	    esac
	    shift 2
	    ;;
	--package-manager-command) PM_COMMAND=${2}; shift 2 ;;
	--) shift; PM_EXTRAOPTS=( "$@" ); break ;;
	*)
	    # this should not happen; getopt should return bad status
	    echo "Invalid option '${1}' given!"
	    usage 22
	    ;;
    esac
done

# Only set colours if output is not redirected or the --nocolour
# option is not set
if [ -t 1 ] && [ -z "${NOCOLOUR}" ] ; then
    BOLD=$(tput bold)
    NORMAL=$(tput sgr0)
    RED=$(tput setaf 1)${BOLD}
    GREEN=$(tput setaf 2)${BOLD}
    YELLOW=$(tput setaf 3)${BOLD}
    BLUE=$(tput setaf 4)${BOLD}
    MAGENTA=$(tput setaf 5)${BOLD}
    CYAN=$(tput setaf 6)${BOLD}
else
    BOLD=; NORMAL=; RED=; GREEN=; YELLOW=; BLUE=; MAGENTA=; CYAN=
fi

# Some type of output can be prettified and capsulated in functions
message() {
    local OUTPUT="$@"
    echo "${GREEN}*${NORMAL}${BOLD} ${OUTPUT}${NORMAL}"
}

warning() {
    local OUTPUT="$@"
    echo "${YELLOW}*${NORMAL}${BOLD} ${OUTPUT}${NORMAL}"
}

failure() {
    local OUTPUT="$@"
    echo "${RED}*${NORMAL}${BOLD} ${OUTPUT}${NORMAL}"
}

emacs_version() {
    "${EMACS}" -batch -q --no-site-file --eval "(princ emacs-version)" \
	|| { failure "Cannot run Emacs"; exit 5; }
}

# Get Emacs version from byte-compiled file
bytecomp_version() {
    sed -n '/^[^;]/q;s/\.$//;s/.*[Ee]macs version \([0-9].*\)/\1/p' "${1}"
}

action_sitedir() {
    local sf

    message "Searching for site-init files in obsolete location ..."

    # Set nullglob option since there may be no matching files
    local old_shopts=$(shopt -p nullglob)
    shopt -s nullglob

    for sf in "${ROOT}${SITELISP}"/[0-9][0-9]*-gentoo.el
    do
	echo "Found ${sf##*/}"
	echo "${sf}" >> "${TMPFILE}"
    done

    eval "${old_shopts}"
    echo
}

action_rebuild() {
    local active version elc

    message "Searching for byte-compiled elisp files ..."
    active=$(emacs_version)
    [ -n "${active}" ] || { failure "Cannot determine Emacs version"; exit 6; }
    message "Currently selected GNU Emacs version: ${active}"

    for elc in $(find "${ROOT}${SITELISP}" -name "*.elc")
    do
	version=$(bytecomp_version "${elc}")
	version=${version:-unknown}

	if [ -z "${MAJOR}" -a "${version}" != "${active}" ] \
	    || [ "${version%%.*}" != "${active%%.*}" ] \
	    || [ "${1}" = all ]
	then
	    echo "Found ${elc##*/} (compiled by Emacs ${version})"
	    echo "${elc}" >> "${TMPFILE}"
	else
	    echo "Skipping ${elc##*/} (compiled by Emacs ${version})"
	fi
    done
    echo
}

action_all() {
    action_rebuild all
}

cleanup() {
    rm -f "${TMPFILE}" "${PKGFILE}"
}

trap cleanup EXIT

TMPFILE="$(mktemp ${TMPDIR:-/tmp}/emacs-updater.files.XXXXXX)"
PKGFILE="$(mktemp ${TMPDIR:-/tmp}/emacs-updater.pkgs.XXXXXX)"

for action in ${ACTIONS}; do
    action_${action}
done

if [ ! -s "${TMPFILE}" ]; then
    warning "No files to update, quitting."
    exit 3
fi

NO_OF_FILES=$(wc -l <"${TMPFILE}")
[ "${NO_OF_FILES}" -eq 1 ] && s= || s=s
message "Assigning ${NO_OF_FILES} file${s} to packages ..."

if [ "${ORPHANS}" ]; then
    qfile -oCR -f "${TMPFILE}" | sort -u > "${PKGFILE}"
elif [ "${EXACT}" ]; then
    qfile -eqCR -f "${TMPFILE}" | sort -u | sed 's/^/=/' > "${PKGFILE}"
else
    qfile -qCR -f "${TMPFILE}" | sort -u > "${PKGFILE}"
    # Get package and slot number
    # qfile -eqCR -f "${TMPFILE}" | sort -u | xargs qlist -eqCILS
fi

NO_OF_PACKAGES=$(wc -l <"${PKGFILE}")
[ "${NO_OF_PACKAGES}" -eq 1 ] && s= || s=s
if [ "${ORPHANS}" ]; then
    message "${NO_OF_PACKAGES} orphan file${s} found:"
else
    message "${NO_OF_PACKAGES} package${s} to remerge:"
fi

cat "${PKGFILE}"

if [ "${PRETEND}" -o "${ORPHANS}" ]; then
    exit 4
fi

echo
if [ "${NO_OF_PACKAGES}" -eq 0 ]; then
    warning "No packages to update, quitting."
    exit 3
fi

if [ "${BATCH}" ]; then
    message "Remerging packages ..."
else
    echo -n "${BOLD}Remerge packages?${NORMAL} [${GREEN}Yes${NORMAL}/${RED}No${NORMAL}] "
    read choice
    echo
    case "${choice}" in
	y*|Y*|"") ;;
	*) warning "Quitting."; exit 10 ;;
    esac
fi

${PM_COMMAND} "${PM_EXTRAOPTS[@]}" $(<"${PKGFILE}")

warning "If a package is being rebuilt over and over again,"
warning "please report it on http://bugs.gentoo.org/"
