#!/bin/sh
#
# This script has been modified by kosmikus and is based on 
# python-updater by liquidx.
#
# It tries to update any package that provides a ghc library.
# This script can be run as many times as you like. It will log the 
# results in /var/log/ghc-updater.log
#
# NEW_GHC_VER     = new ghc version we are upgrading to
# PKGS_EXCEPTIONS = packages that should NOT be re-emerged for any reason
# PKGS_MANUAL     = packages that should be re-emerged even if they don't
#                   fit the criteria
#
# Runtime Variables:
# 
# PKGS_TO_REMERGE = list of packages we deem to need re-emerging
# PKGS_OK         = list of packages that should be merged without any problems
# PKGS_MISSING    = list of packages that are installed, but cannot be merged
#                   because they have been pruned from portage
# PKGS_MASKED     = list of packages that are installed, but masked.
#

shopt -s nullglob

# fix the PATH to include the dirs where portage installs ghc
PATH="/usr/bin:/opt/ghc/bin:${PATH}"

NEW_GHC_VER=$(ghc --version | sed 's:^.*version ::')
NEW_GHC_LIBDIR=$(ghc --print-libdir)

PKGS_EXCEPTIONS="dev-lang/ghc dev-lang/ghc-bin"
PKGS_MANUAL=""
LOGFILE="/var/log/ghc-updater.log"

# portage variables
PKG_DBDIR=/var/db/pkg

# moved the portageq checks into a function to make command
# line parsing immediate

setup_portdir() {
	PORTDIR=`portageq portdir`
	PORTDIR_OVERLAYS=`portageq portdir_overlay`
}

PRETEND=0
PKGS_TO_REMERGE=""
PKGS_COUNT_REMERGE=0
PORTAGE_PYTHON="/usr/bin/python"

usage() {
	echo "usage: ghc-updater [options]"
	echo " -h, -?, --help   help"
	echo " -p, --pretend    pretend (don't do anything)"
}

# 
#
# Command Line Parsing
#
#
while [ -n "$1" ]; do
	case "$1" in
		-h | -\? | --help)
			usage
			exit 0
			;;
		-p | --pretend)
			PRETEND=1
			;;
		*)
			usage
			echo "unrecognised option: $1"
			;;
	esac
	shift
done

# load the gentoo-style info macros, but hack to get around
# it thinking this is an rc script
EBUILD="1"

# /etc/init.d/functions.sh always points to the correct functions.sh no
# matter which version of baselayout
source /etc/init.d/functions.sh

# misc helper functions
eloginfo() {
	einfo $*
	DATESTRING=`date +"%Y/%m/%d %H:%M:%S"`
	(echo "${DATESTRING} - ${*}" >> ${LOGFILE}) 2>/dev/null
}

elogecho() {
	echo -n "   "
	echo $*
	DATESTRING=`date +"%Y/%m/%d %H:%M:%S"`
	(echo "${DATESTRING} - ${*}" >> ${LOGFILE}) 2>/dev/null
}

elogerr() {
	eerror $*
	DATESTRING=`date +"%Y/%m/%d %H:%M:%S"`
	(echo "${DATESTRING} ! ${*}" >> ${LOGFILE}) 2>/dev/null
}

elog() {
	DATESTRING=`date +"%Y/%m/%d %H:%M:%S"`
	(echo "${DATESTRING} - ${*}" >> ${LOGFILE}) 2>/dev/null
}

#
# Sanity check
#

setup_portdir

find_in_portdir() {
	local f
	for f in ${PORTDIR} ${PORTDIR_OVERLAYS}; do
		if [[ -f "${f}/${1}" ]]; then
			echo "${f}/${1}"
			return 0
		fi
	done
	return 1
}

if [ -z "${PORTDIR}" ]; then
	eerror "Unable to proceed. Can not find PORTDIR. Make sure the command:"
	eerror " "
	eerror "  portageq portdir"
	eerror " "
	eerror "returns a value. If it doesn't, make sure you have updated to"
	eerror "latest portage version."
	eerror " "
	eerror "Report bugs to http://bugs.gentoo.org/"
	exit 1
fi

#
#
# Find all packages that have installed something in
# directories of the form
#   /usr/lib/ghc-<version>
# or similar.
#
#   /usr/lib/ghc-bin-<version>
# is included because an old ghc-bin ebuild was buggy and
# installed to a wrong dir.
#
OLD_PACKAGES_DIR=""
# Exclude new library dir and lib symlinks:
for d in /{usr,opt/ghc}/lib{,64}/ghc{,-bin}-*; do
    [[ "${d}" == ${NEW_GHC_LIBDIR} ]] || [[ -L ${d%/*} ]] || OLD_PACKAGES_DIR="${OLD_PACKAGES_DIR}${d} "
done

eloginfo "Starting GHC Updater to $(which ghc), version ${NEW_GHC_VER} :"
eloginfo "Searching for packages with files in the directories:"
eloginfo "${OLD_PACKAGES_DIR}"

# iterate thru all the installed package's contents
for content in `find ${PKG_DBDIR} -name CONTENTS`; do
    # extract the category, package name and package version
    CATPKGVER=$(echo ${content} | sed "s:${PKG_DBDIR}/\(.*\)/CONTENTS:\1:")
    
    # exclude packages that are an exception, like portage and python itself.
    exception=0
    for exp in ${PKGS_EXCEPTIONS}; do
    	if [ -n "$(echo ${CATPKGVER} | grep ${exp})" ]; then
			exception=1
			break;
		fi
    done
	
    if [ ${exception} = 1 ]; then
       continue;
    fi

    for d in ${OLD_PACKAGES_DIR}; do
        if fgrep "${d}/" ${content} > /dev/null; then
            PKGS_TO_REMERGE="${PKGS_TO_REMERGE} ${CATPKGVER}"
            elogecho "${CATPKGVER} has files in ${d}"
        fi
    done
done    

# now we have to do each emerge seperately because if an installed version
# does not have the corresponding ebuild in portage, then it will bail.

eloginfo "Calculating Upgrade Package List .."

PKGS_OK=""
PKGS_MASKED=""
PKGS_BLOCKED=""
PKGS_MISSING=""

MASKED_STRING="been masked"
BLOCKED_STRING="is blocking"
MISSING_STRING='there are no ebuilds to satisfy'

for pkg in ${PKGS_TO_REMERGE}; do
   emerge_output="$(emerge -p '>='$pkg 2>&1)"
   if $(echo "${emerge_output}" | grep "${MASKED_STRING}" > /dev/null); then
      PKGS_MASKED="${PKGS_MASKED} $pkg"
	  elogecho ">=$pkg is masked"
   elif $(echo "${emerge_output}" | grep "${BLOCKED_STRING}" > /dev/null); then
      PKGS_BLOCKED="${PKGS_BLOCKED} $pkg"
	  elogecho ">=$pkg is blocked"
   elif $(echo "${emerge_output}" | grep "${MISSING_STRING}" > /dev/null); then
      PKGS_MISSING="${PKGS_MISSING} $pkg"
	  elogecho ">=$pkg is missing from portage"
   else
      PKGS_OK="${PKGS_OK} $pkg"
	  PKGS_COUNT_REMERGE=$((PKGS_COUNT_REMERGE + 1))
   fi
done      

#
# Use my super dumb package reordering algorithm that works most of the time
#

eloginfo "Re-ordering packages to merge .."

DEPSORT=$(find_in_portdir "dev-lang/ghc/files/depsort.py")
if [[ -z ${DEPSORT} ]]; then
	eerror "Fatal error: File dev-lang/ghc/files/depsort.py not in portage tree."
	exit 1
fi
PKGS_OK_SORTED="$(${PORTAGE_PYTHON} ${DEPSORT} ${PKGS_OK} | xargs)"

if [[ -n ${PRETEND} ]]; then
	eloginfo "These are the packages that would be merged, in order:"
else
	eloginfo "Preparing to merge these packages in this order:"
fi
for pkg in $PKGS_OK_SORTED; do
	elogecho ">=$pkg"
done

# we emerge each package seperately to ensure we know exactly which ones might
# cause an error, and then report it at the end

COUNT=1
PKGS_FAILED=""
if [ "${PRETEND}" != "1" ]; then
	for pkg in ${PKGS_OK_SORTED}; do
		eloginfo "Starting to merge ($COUNT/$PKGS_COUNT_REMERGE) $pkg .."
		if ! emerge --oneshot --nodeps '>='$pkg; then
			PKGS_FAILED="${PKGS_FAILED} $pkg"
			elogerr "Failed merging $pkg ($COUNT/$PKGS_COUNT_REMERGE)!"
		fi
		COUNT=$((COUNT+1))		
	done
fi

# final output stuff
OUTPUT_PKGS_MASKED=""
for pkg in ${PKGS_MASKED}; do OUTPUT_PKGS_MASKED="${OUTPUT_PKGS_MASKED} '>='$pkg"; done
OUTPUT_PKGS_BLOCKED=""
for pkg in ${PKGS_BLOCKED}; do OUTPUT_PKGS_BLOCKED="${OUTPUT_PKGS_BLOCKED} $pkg"; done
OUTPUT_PKGS_MISSING=""
for pkg in ${PKGS_MISSING}; do OUTPUT_PKGS_MISSING="${OUTPUT_PKGS_MISSING} $pkg"; done
OUTPUT_PKGS_FAILED=""
for pkg in ${PKGS_FAILED}; do OUTPUT_PKGS_FAILED="${OUTPUT_PKGS_FAILED} '>='$pkg"; done

if [ -n "${PKGS_FAILED}" -o -n "${PKGS_MISSING}" -o -n "${PKGS_MASKED}" ]; then
   echo
   ewarn "************************************************************"
   ewarn "* Packages that still need to be manually emerged :        *"
   ewarn "************************************************************"
   if [ -n "${OUTPUT_PKGS_MASKED}" ]; then
      echo
      ewarn " Masked Packages:"
	  ewarn " ----------------"
      ewarn " Unmask the following packages (at your own risk) and  "
      ewarn " emerge them using this command after removing the '-p'"
      ewarn " parameter."
      echo
      ewarn " emerge -p ${OUTPUT_PKGS_MASKED}"
      echo
   fi
   if [ -n "${OUTPUT_PKGS_BLOCKED}" ]; then
      echo
      ewarn " Blocked Packages:"
	  ewarn " -----------------"
      ewarn " These packages are currently blocked; they might not yet"
      ewarn " be compatible with the current ghc. You can run ghc-updater"
      ewarn " again at a later time."
      echo
      for x in ${OUTPUT_PKGS_BLOCKED}; do 
         echo "   ${x}"
      done
   fi
   if [ -n "${OUTPUT_PKGS_MISSING}" ]; then
      echo
      ewarn " Missing Packages:"
	  ewarn " -----------------"
      ewarn " These packages cannot be updated because they do not exist"
      ewarn " in portage anymore."
      echo
      for x in ${OUTPUT_PKGS_MISSING}; do 
         echo "   ${x}"
      done
   fi
   if [ -n "${OUTPUT_PKGS_FAILED}" ]; then
      echo
      ewarn " Failed Packages:"
	  ewarn " ----------------"
      ewarn " These packages have failed and need to be re-emerged again."
	  ewarn " Alternatively, try re-running this script again to see if it"
	  ewarn " can be fixed."
      echo
      ewarn " emerge -p ${OUTPUT_PKGS_FAILED}"
      echo
   fi
   
   elog "GHC update completed with errors."
   elog "Masked Packages:"
   for x in ${PKGS_MASKED}; do
   		elog $x
   done
   elog "Missing Packages:"
   for x in ${PKGS_MISSING}; do
   		elog $x
   done
   elog "Failed Packages:"
   for x in ${PKGS_FAILED}; do
   		elog $x
   done   
   elog "Update script completed."
else
   eloginfo "GHC update completed successfully."
fi
