#!/bin/bash
#---------------------------------------------------------------------
## @Synopsis Delve is a tool for running individual spell files during the
## @Synopsis build and install phase. This can be used as a debugging tool or to
## @Synopsis "force" a spell to be installed. Use with caution and keep your hands
## @Synopsis off if you dont know what you are doing.
##
## @Copyright 2004 the Source Mage Team
## @Author Andrew Stitt
#---------------------------------------------------------------------

function help() {

  cat  <<  EOF
Delve allows the running of single spell files in the build/install phase

NOTE: this is a low level tool and should not be necessary in normal
situations. It is a bit of a big hammer not everything is a nail :)
You can do serious harm to your box if used improperly, also little
sanity checking is done because it is intended to circumvent some of
the normal restrictions of cast. Please use with care or not at all.

Usage:

delve spellname SPELL_FILE [... SPELL_FILE]
delve spellname success|failure
delve spellname all

Paramters

-f | --force                    Continue running files even if one fails
                                (use with caution!)

-c | --continue start end       Run all the files from start to end:

delve -c start BUILD spellname
will run everything for spellname from begining through building

delve -c INSTALL end spellname
will run everything for spellname from INSTALL to success or fail

Spell file names and other values:
start, PRE_BUILD, config_loc, BUILD, PRE_INSTALL, INSTALL, POST_BUILD,
POST_INSTALL, FINAL, TRIGGERS, end, success, failure, all.

"start" and "end" are place holders for the start/end of the process,
mostly only meaningful with the -c option.

"config_loc" is the phase of cast where it asks you about adding stuff to
./configure

"end" can be either success or failure depending on the return value of
the previous state (note, without -f delve will bail out immediatly).

"all" runs all the states for a given spell.

All other files should be self evident

Delve is conscious of build api versions and will refuse to run files
that do not exist in the spell's api. The -c option will file in the
appropriate files for the spell.

EOF

  exit  1

}

#---------------------------------------------------------------------------
## Loads OPTS and all the other log files to provide the appropriate
## environment for spell files
#---------------------------------------------------------------------------
function delve_log_helper() {

  if ! [[ "$__DELVE_IN_CAST" ]] ; then
    # must declare OPS before sourcing config
    local OPTS
    run_spell_config

    get_uncommitted_depends_file $SPELL spell_depends
    if test -e $spell_depends && test -e $ABANDONED_DEPENDS/$SPELL ; then
      cp $ABANDONED_DEPENDS/$SPELL $spell_depends
    fi
    test -e $spell_depends &&
    OPTS="$OPTS $(get_depends_options $spell_depends $SPELL)"
  fi

  save_bin_lib_paths

  # these have to go somewhere where future delves can find them
  C_LOG=${C_LOG:="/tmp/sorcery/delve/$SPELL.compile.log"}
  CASTFS_DBGLOG=${CASTFS_DBGLOG:="/tmp/sorcery/delve/$SPELL.iwdbglog"}
  C_FIFO=${C_FIFO:="/dev/stdout"}
  IW_LOG=${IW_LOG:="/tmp/sorcery/delve/$SPELL.iw"}
  VOYEUR_STDOUT=${VOYEUR_STDOUT:="/dev/stdout"}
  VOYEUR_STDERR=${VOYEUR_STDERR:="/dev/stderr"}
  S_PWD=${S_PWD:="/tmp/sorcery/delve/$SPELL.pwd"}
  touch $C_LOG

}

#---------------------------------------------------------------------------
## Determines if a step is valid for the current spell
## @param name of step
## @param all the remaining parameters are numbers for which the step is valid
## @stdout a complaint if the step is not valid
##
## if the BUILD_API number is not found in the parameter list complain
## and return 1
#---------------------------------------------------------------------------
function delve_is_valid_step() {
  local STEP=$1
  local found
  shift
  for api in $@; do
    if [ "$BUILD_API" == "$api" ] ; then
      found=yes
      break
    fi
  done
  if [[ ! $found ]] ; then
    message "Refusing to run $STEP for build api $BUILD_API"
    return 1
  fi
  return 0
}

function delve_pre_build() {
  debug "delve" "Running $FUNCNAME"
  delve_is_valid_step PRE_BUILD 1 2 || return 1
  run_pre_build
}

function delve_config_loc() {
  debug "delve" "Running $FUNCNAME"
  delve_is_valid_step config_loc 1 2 || return 1
  run_config_loc
}

function delve_build() {
  debug "delve" "Running $FUNCNAME"
  delve_is_valid_step BUILD 1 2 || return 1
  delve_log_helper
  (
    run_build
  ) 2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
     > >(tee -a $C_LOG > $VOYEUR_STDOUT)
}

function delve_pre_install() {
  debug "delve" "Running $FUNCNAME"
  delve_is_valid_step PRE_INSTALL 2 || return 1
  delve_log_helper
  (
    run_pre_install
  ) 2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
     > >(tee -a $C_LOG > $VOYEUR_STDOUT)
}

function delve_install() {
  debug "delve" "Running $FUNCNAME"

  delve_is_valid_step INSTALL 2 || return 1
  delve_log_helper
  if [[ "$STAGED_INSTALL" != "off" ]]
  then
    if [[ -z "$CHROOT_CHECK" ]]
    then
      prepare_stage_root &&
      invoke_stage_root &&
      export CHROOT_CHECK="true"
      CHROOT_CMD="chroot ${STAGE_DIRECTORY}/MOUNT"
    fi
    INSTALL_ROOT=""
  else
    invoke_installwatch
  fi
  (
    if [[ -z "$CHROOT_CMD" ]]
    then
      run_install || return 2
    else
      $CHROOT_CMD delve $SPELL INSTALL
    fi
  ) 2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
     > >(tee -a $C_LOG > $VOYEUR_STDOUT)
  local rc=$?
  if [[ $CHROOT_CMD ]]
  then
    devoke_stage_root
  fi &&
  return $rc
}

function delve_install_extras() {
  debug "delve" "Running $FUNCNAME"

  delve_is_valid_step INSTALL_EXTRAS 2 || return 1
  delve_log_helper
  if [[ "$STAGED_INSTALL" != "off" ]]
  then
    if [[ -z "$CHROOT_CHECK" ]]
    then
      invoke_stage_root &&
      export CHROOT_CHECK="true"
      CHROOT_CMD="chroot ${STAGE_DIRECTORY}/MOUNT"
    fi
    INSTALL_ROOT=""
  else
    invoke_installwatch
  fi
  (
    if [[ -z "$CHROOT_CMD" ]]
    then
      run_install_extras || return 2
    else
      $CHROOT_CMD delve $SPELL INSTALL_EXTRAS
    fi
  ) 2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
     > >(tee -a $C_LOG > $VOYEUR_STDOUT)
  local rc=$?
  if [[ $CHROOT_CMD ]]
  then
    devoke_stage_root
  fi &&
  return $rc
}

function delve_post_build() {
  debug "delve" "Running $FUNCNAME"

  delve_is_valid_step POST_BUILD 1 || return 1
  delve_log_helper
  [ -d $SOURCE_DIRECTORY ] || {
    message "Ack! there is no $SOURCE_DIRECTORY"
    return 1
  }
  cd $SOURCE_DIRECTORY
  (
    run_post_build
  ) 2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
     > >(tee -a $C_LOG > $VOYEUR_STDOUT)
}

function delve_post_install() {
  debug "delve" "Running $FUNCNAME"
  delve_is_valid_step POST_INSTALL 1 2 || return 1
  delve_log_helper
  (
    run_post_install
  ) 2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
     > >(tee -a $C_LOG > $VOYEUR_STDOUT)
}

function delve_transfer() {
  debug "delve" "Running $FUNCNAME"
  delve_is_valid_step TRANSFER 2 || return 1
  delve_log_helper
  (
    run_transfer
  ) 2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
     > >(tee -a $C_LOG > $VOYEUR_STDOUT)
}

function delve_final() {
  debug "delve" "Running $FUNCNAME"
  delve_is_valid_step FINAL 2 || return 1
  delve_log_helper
  devoke_installwatch &&
  STAGED_INSTALL=off
  (
    run_final
  ) 2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
     > >(tee -a $C_LOG > $VOYEUR_STDOUT)

}

function delve_triggers() {
  debug "delve" "Running $FUNCNAME"
  delve_is_valid_step TRIGGERS 1 2 || return 1
  run_triggers
}

function delve_spell_success() {
  debug "delve" "Running $FUNCNAME"
  SUCCESS_LIST=/dev/null
  cd /
  delve_log_helper
  run_spell_success
}

function delve_spell_failure() {
  debug "delve" "Running $FUNCNAME"
  FAILED_LIST=/dev/null
  delve_log_helper
  run_spell_failure 2
}

#---------------------------------------------------------------------------
## @param begin state
## @param end state
## @global BUILD_API
## fill in the states from begin to end from the given BUILD_API
#---------------------------------------------------------------------------
function delve_make_continuation() {
  local begin=$1
  local end=$2
  local API1="start PRE_BUILD config_loc BUILD POST_BUILD POST_INSTALL TRIGGERS end"
  local API2="start PRE_BUILD config_loc BUILD PRE_INSTALL INSTALL INSTALL_EXTRAS POST_INSTALL TRANSFER FINAL TRIGGERS end"
  local THIS_API
  case $BUILD_API in
    1) THIS_API=$API1 ;;
    2) THIS_API=$API2 ;;
    *) message "Unknown api for spell $SPELL: \"$BUILD_API\"" ; return 1;;
  esac
  local phase=0
  local state
  local states
  for state in $THIS_API; do
    case $phase in
      0)
        if [ "$state" == "$begin" ]; then
          phase=1
        fi
        states="$state"
      ;;
      1)
        states="$states $state"
        if [ "$state" == "$end" ]; then
          phase=2
          break
        fi
      ;;
    esac
  done
  if [ $phase != 2 ] ; then
    message "Error making continuation"
    return 1
  fi
  echo $states
  return 0
}

#---------------------------------------------------------------------------
## fills in STATES, continue_start, continue_end, and FORCE as applicable
#---------------------------------------------------------------------------
function process_parameters() {
  for param in $@ ; do
    case $param in
        -f|--force) FORCE=on ;;
     -c|--continue) continue_start=$2
                    continue_end=$3
                    shift 2
                    ;;
             start) ;;
         PRE_BUILD) STATES="$STATES $param" ;;
        config_loc) STATES="$STATES $param" ;;
             BUILD) STATES="$STATES $param" ;;
       PRE_INSTALL) STATES="$STATES $param" ;;
           INSTALL) STATES="$STATES $param" ;;
        POST_BUILD) STATES="$STATES $param" ;;
    INSTALL_EXTRAS) STATES="$STATES $param" ;;
      POST_INSTALL) STATES="$STATES $param" ;;
          TRANSFER) STAGES="$STAGES $param" ;;
             FINAL) STATES="$STATES $param" ;;
          TRIGGERS) STATES="$STATES $param" ;;
           success) STATES="$STATES $param" ;;
           failure) STATES="$STATES $param" ;;
               end) STATES="$STATES $param" ;;
               all) ALL=on ;;
                 *) SPELL=$param ;;
    esac
  done
}

#---------------------------------------------------------------------------
## This is obviously the main function of delve, it loads the spell
## figures out what states to run and then loops through them.
#---------------------------------------------------------------------------
function main() {

  local STATES STATE
  local FORCE ALL SPELL
  local continue_start continue_end
  process_parameters $@

  codex_does_spell_exist $SPELL || return 1
  codex_set_current_spell_by_name $SPELL
  load_build_api
  if [[ $continue_start ]] && [[ $continue_end ]] ; then
    STATES=$(delve_make_continuation $continue_start $continue_end)
  elif [[ $ALL ]] ; then
    STATES=$(delve_make_continuation start end)
  fi

  if [[ -z $CHROOT_CHECK ]]
  then
    if [[ -z $STAGED_INSTALL ]]
    then
      if ! is_castfs_installed
      then
        STAGED_INSTALL=off
      else
        STAGED_INSTALL=on
      fi
    fi
  fi

  local last_rc=0
  for STATE in $STATES; do
    case $STATE in
             start) ;;
         PRE_BUILD) delve_pre_build ;;
        config_loc) delve_config_loc ;;
             BUILD) delve_build ;;
       PRE_INSTALL) delve_pre_install ;;
           INSTALL) delve_install ;;
        POST_BUILD) delve_post_build ;;
    INSTALL_EXTRAS) delve_install_extras ;;
      POST_INSTALL) delve_post_install ;;
          TRANSFER) delve_transfer ;;
             FINAL) delve_final ;;
          TRIGGERS) delve_triggers ;;
           success) delve_spell_success ;;
           failure) delve_spell_failure ;;
               end)if [[ $last_rc == 0 ]] ; then
                      delve_spell_success
                    else
                      delve_spell_failure
                    fi ;;
     esac
     last_rc=$?
     if [[ $last_rc != 0 ]] && [ "$FORCE" != 1 ] ; then
       message "$STATE failed!"
       return 1
     fi
  done
}

. /etc/sorcery/config
if    [  $#      -eq  0  ];  then  help  |  $PAGER
elif  [[  $1 == -h  ]]  ||  [[  $1 == --help  ]] ; then help
elif  [  "$UID"  -gt  0  ];  then
  echo  "Enter the root password, please."
  PARAMS=$(consolidate_params "$@")
  exec su -c "delve $PARAMS" root
else
  # Make a nice dir structure to put stuff in, this exits if it fails
  mk_tmp_dirs delve
  main  "$@"
  rc=$?
  cleanup_tmp_dir $TMP_DIR
  exit $rc
fi

debug "delve" "exiting..."

#---------------------------------------------------------------------
##=back
##
##=head1 LICENSE
##
## This software 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 2 of the License, or
## (at your option) any later version.
##
## This software 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 software; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
##
#---------------------------------------------------------------------
