#!/bin/sh

# Author: Benoit PAPILLAULT <benoit.papillault@sourcemage.org>
# Creation : 2004-12-09

# Requirements: a working chroot system
# Output: an updated chroot system

# to define HOST and ARCH automatically
. config-auto

# default answer to optional dependencies can be stored
# in /var/state/sorcery/default_depends
#
# sorcery default add basesystem "" off
# sorcery default add basesystem SORCERY-SOUNDS off
# sorcery default add basesystem BOOTLOADER off
# => cast -r -c basesystem

# usage: spell_versions bash
# returns the grimoire version and the installed version
# you can use it as : 
# grimoire_version=`spell_versions bash | awk '{print $1}'
# installed_version=`spell_versions bash | awk '{print $2}'

spell_versions() {

  local SPELL="$1"
  chroot "${IMAGE}" gaze version "${SPELL}" |
  awk '{print $3" "$4" "$5}' | grep "^${SPELL}" | tail -n 1

}

# usage: grimoire_version bash
# returns the version in the grimoire

grimoire_version() {

  local SPELL="$1"
  spell_versions "${SPELL}" | awk '{print $2}'

}

# usage: installed_version bash
# returns the installed version or "-"

installed_version() {

  local SPELL="$1"
  grep "^${SPELL}:" "${SPELL_STATUS}" | cut -d: -f4

}

# usage: cat_file file
# output file + file.$ARCH is this latter exist

cat_file()
{

  for file in "$@";
  do
    if [ -f "${file}" ]; then
      cat "${file}"
    fi &&
    if [ -f "${file}.${ARCH}" ]; then
      cat "${file}.${ARCH}"
    fi
  done

}

# list direct dependencies of a spell
# returns: a spell list on stdout

get_depends() {

  local SPELL="$1"

  (grep "^${SPELL}:.*:on:.*" "${DEPENDS_STATUS}" || true) |
  cut -d: -f2 | cut -d'(' -f1

}

# list all dependencies (recursively) of a spell, 
# returns: a spell list on stdout

get_full_depends() {

  local SPELL="$1"
  local SPELL_LIST SPELL_LIST_NEW SPELL_ADDED SPELL_ADDED_TMP

  # initialization
  SPELL_LIST=( "${SPELL}" )
  SPELL_ADDED=( "${SPELL}" )

  # while we have added spell in the current iteration, continue
  while [ "${#SPELL_ADDED[@]}" -ne 0 ];
  do
    # we need to empty SPELL_ADDED list before starting the loop, but
    # we need it for the loop too, so we store it in the temporary
    # variable SPELL_ADDED_TMP

    SPELL_ADDED_TMP=( "${SPELL_ADDED[@]}" )
    SPELL_ADDED=()

    # loop through all spells added in the previous iteration
    for x in "${SPELL_ADDED_TMP[@]}";
    do

      SPELL_LIST_NEW=( `get_depends "${x}"` )

# for each spell in SPELL_LIST_NEW which is not already in SPELL_LIST,
# we add it to SPELL_LIST and SPELL_ADDED

      for y in "${SPELL_LIST_NEW[@]}";
      do

# we set SPELL_EXIST to "yes" if spell y is already in the list
# SPELL_LIST

        SPELL_EXIST="no"
        for z in "${SPELL_LIST[@]}";
        do
          if [ "${y}" = "${z}" ]; then
            SPELL_EXIST="yes"
            break;
          fi
        done

        if [ "${SPELL_EXIST}" != "yes" ]; then
          SPELL_LIST=( "${SPELL_LIST[@]}" "${y}" )
          SPELL_ADDED=( "${SPELL_ADDED[@]}" "${y}" )
        fi

      done
    done
  done

# output the result
  for x in "${SPELL_LIST[@]}";
  do
    echo "${x}"
  done

}

# for spell-binary-make
export PATH="${PWD}/src/iso_root:${PATH}" &&
export BASE="${IMAGE}/install" &&
export INSTALL_ROOT="${IMAGE}" &&

# mount needed directories
mount --bind /dev "${IMAGE}/dev" &&
mount --bind /dev/pts "${IMAGE}/dev/pts" &&
mount --bind /proc "${IMAGE}/proc" &&

# update grimoires, sorcery and installed spells in one step
yes "" | chroot "${IMAGE}" sorcery system-update &&

# to repair broken dependencies, you can do:
# cleanse --prune doit

# we loop throught spell list twice : 
# - 1st to make sure spells are installed
# - 2nd to ensure binary caches are up to date
# we need to loop twice because some binary caches might be up to date and
# would need spells that are not installed yet (like basesystem needs which)

echo "Compiling spells if needed..." &&

# cast needed spells
(cat_file src/iso_root/installerdata/install-list &&
 cat_file src/iso_root/installerdata/optional-list &&
 cat_file src/iso_root/spell-iso-root)|
grep -v "^#" | grep -v "^$" | sort -u |
while read spell;
do
  get_full_depends "${spell}"
done  | sort -u |
while read spell;
do
  VERSION_I=`installed_version "${spell}"` &&

  # if the spell is already installed, we continue to the next spell
  if [ -n "${VERSION_I}" ]; then
    continue;
  fi

  VERSION_I=`grimoire_version "${spell}"` &&
  echo "${spell} not installed, trying grimoire version ${VERSION_I}"
  LOG="${IMAGE}/var/log/sorcery/install/${spell}-${VERSION_I}" &&
  C_SPELL=`echo "${spell}" | sed s/_/-/g` &&
  C_VERSION=`echo "${VERSION_I}" | sed s/_/-/g` &&
  C_HOST=`echo "${HOST}" | sed s/_/-/g` &&
  CACHE="${IMAGE}/install/${C_SPELL}_${C_VERSION}_${C_HOST}.tar.bz2" &&

  # check if we need to compile the spell
  COMPILE="no" &&
  # if there is no install log, the spell is probably not installed
  if [ ! -f "${LOG}" ]; then
      echo "${spell} has no install log -> compiling" &&
      COMPILE="yes"
  else
    if [ ! -f "${CACHE}" ]; then
      echo "${spell} has no binary cache -> compiling" &&
      COMPILE="yes"
    fi
  fi &&

  # compile the spell if needed
  if [ "${COMPILE}" = "yes" ]; then
    # handle conflicting spells
	case "${spell}" in
      kbd)
        yes "" | chroot "${IMAGE}" dispel console-tools console-data ;;
      console-tools | console-data)
        yes "" | chroot "${IMAGE}" dispel kbd ;;
	  metalog | syslog-ng | sysklogd)
		yes "" | chroot "${IMAGE}" dispel metalog syslog-ng sysklogd;;
    esac
    yes "" | chroot "${IMAGE}" cast -c "${spell}" &&

    # remove existing cache, even different versions
    rm -f "${BASE}/${spell}_"* &&
    spell-binary-make "${spell}"
  fi || exit 1
done &&

# here we need to fix broken dependencies, like
# util-linux depends on console-tools, but kbd is now installed
# udev depends on sysklogd, but syslog-ng is now installed
# the general solution is to recompile affected spells (util-linux, udev)

echo "Fixing broken dependencies..." &&
(cat_file src/iso_root/installerdata/install-list &&
 cat_file src/iso_root/installerdata/optional-list &&
 cat_file src/iso_root/spell-iso-root)|
grep -v "^#" | grep -v "^$" | sort -u |
while read spell;
do
  get_full_depends "${spell}"
done  | sort -u |
while read spell;
do
  get_depends "${spell}" | while read depends;
  do
    VERSION_I=`installed_version "${depends}"` &&
    if [ "${VERSION_I}" = "" ]; then
      echo "fixing ${spell}" &&
      yes "" | chroot "${IMAGE}" cast -c "${spell}"
      exit $?
    fi
  done
done &&

echo "Updating binary caches if needed..." &&

# update binary caches
(cat_file src/iso_root/installerdata/install-list &&
 cat_file src/iso_root/installerdata/optional-list &&
 cat_file src/iso_root/spell-iso-root)|
grep -v "^#" | grep -v "^$" | sort -u |
while read spell;
do
  get_full_depends "${spell}"
done  | sort -u |
while read spell;
do
  VERSION_I=`installed_version "${spell}"` &&
  if [ "${VERSION_I}" = "" ]; then
    VERSION_I=`grimoire_version "${spell}"` &&
    echo "${spell} not installed, trying grimoire version ${VERSION_I}"
  fi &&
  LOG="${IMAGE}/var/log/sorcery/install/${spell}-${VERSION_I}" &&
  C_SPELL=`echo "${spell}" | sed s/_/-/g` &&
  C_VERSION=`echo "${VERSION_I}" | sed s/_/-/g` &&
  C_HOST=`echo "${HOST}" | sed s/_/-/g` &&
  CACHE="${IMAGE}/install/${C_SPELL}_${C_VERSION}_${C_HOST}.tar.bz2" &&

  echo "${spell} ${VERSION_I}" &&

  # at this step, there must have an install log, check it
  if [ ! -f "${LOG}" ]; then
    echo "error: ${spell} has no install log" &&
    exit 1
  fi &&

  # check if we need to update the binary cache
  UPDATE="no" &&
  if [ ! -f "${CACHE}" ]; then
    echo "${spell}has no binary cache -> updating" &&
    UPDATE="yes"
  else
    if [ "${LOG}" -nt "${CACHE}" ]; then
      echo "${spell} has an install log newer than its binary cache -> updating" &&
      UPDATE="yes"
    fi
  fi &&

  # update the binary cache if needed
  if [ "${UPDATE}" = "yes" ]; then
    # remove existing cache, even different versions
    rm -f "${BASE}/${spell}_"* &&
    spell-binary-make "${spell}"
  fi || exit 1
done
RETURN=$?

umount "${IMAGE}/proc"
umount "${IMAGE}/dev/pts"
umount "${IMAGE}/dev"

exit ${RETURN}
