#!/bin/bash
#---------------------------------------------------------------------
## @Synopsis cast is the spell installation utility. It can be called by the user or by sorcery
## @Copyright Original version Copyright 2001 by Kyle Sallee
## @Copyright Some parts copyright 2002 Anders Bruun Olsen et al
## @Copyright Other additions/corrections Copyright 2002 by the Source Mage Team
## Script to cast spells.
#---------------------------------------------------------------------

#-----
## Help... hmm... I wonder what it does...
#-----
function help() {

  cat  <<  EOF

Cast installs single or multiple spells

Example:          cast  nano hdparm sudo
Usage:            cast  [parameters]  [spell]

Optional Parameters:

       --cflags   "flags"       Custom CFLAGS
       --cxxflags "flags"       Custom CXXFLAGS
       --cppflags "flags"       Custom CPPFLAGS
       --ldflags  "flags"       Custom LDFLAGS
       --no-opts                Turn off setting optimization flags, except
                                for those found in --cflags, --cxxflags,
                                --cppflags and --ldflags.

-V [on|off]                     Override \$VOYEUR setting

-d  |  --download               Force download of sources (overwrite existing
                                files)

-s                              Download all given spells before compiling
       --deps                   Configure spells and determine dependencies,
                                only cast dependencies, not spells themselves

-c  |  --compile                Recompile the spells (don't install from cache).
-r  |  --reconfigure            Select new dependencies for spells (implies -c)

-g  |  --grimoire  [...]        Use only the specified grimoires for this cast
                                NOTE: If there are any cross-grimoire
                                dependencies on unspecified grimoires they
                                will not work. The target spell will
                                not be found. To avoid this, specify all
                                relevant grimoires to the -g parameter
                                in the order you wish them to be searched.

-R  |  --recast-down            Recursively recast depended-upon spells, even
                                if they are already installed. You probably
                                also want to pass the -c flag to make sure they
                                are recompiled, not resurrected.

-B  |  --recast-up              Recursively recast dependent spells. You
                                probably also want to pass the -c flag to make
                                sure they are recompiled, not resurrected.

-O  |  --recast-optional [option] If a spell being built has spells which
                                   could optionally depend on it, but those
                                   dependencies are disabled, ask to recast
                                   the dependee. Optional parameter can be
                                   option can be one of: "always", "ask-yes",
                                   "ask-no", or "ignore"; it defaults to what
                                   is set via the sorcery menu. Implies -c.

-Z  |  --lazy-updates [option]  Perform updates on installed spells that
                                need updates. Optional parameter same as
                                above.

-b  |  --force-base-dep         Force all spells to depend on basesystem

       --from   directory       Specify an alternate for $SOURCE_CACHE

       --queue                  Cast all spells listed in $INSTALL_QUEUE

EOF

  exit  1

}

#---------------------------------------------------------------------
## @Aguments Arguments of cast
## @Globals DEPS_ONLY RECONFIGURE COMPILE
## @Globals FORCE_DOWNLOAD SOURCE_CACHE SILENT SEPARATE
## @Globals INSTALL_QUEUE
##
#---------------------------------------------------------------------
function process_parameters()  {

  local n
  while  [  -n  "$1"  ];  do

    if  echo  "" $1  |  grep  -q  "^ -";  then

      case  $1  in

            --cflags)  export OVERRIDE_CFLAGS="$2";    COMPILE="-c"; shift 2  ;;
          --cxxflags)  export OVERRIDE_CXXFLAGS="$2";  COMPILE="-c"; shift 2  ;;
          --cppflags)  export OVERRIDE_CPPFLAGS="$2";  COMPILE="-c"; shift 2  ;;
           --ldflags)  export OVERRIDE_LDFLAGS="$2";   COMPILE="-c"; shift 2  ;;
           --no-opts)  export NO_OPTIMIZATION_FLAGS=1; COMPILE="-c"; shift 1  ;;
                  -V)  export VOYEUR_OVERRIDE="$2";                  shift 2  ;;
              --deps)  export  DEPS_ONLY="$1";                       shift 1  ;;
      -g|--grimoire*)  override_grimoires "$2";                      shift 2  ;;
    -r|--reconfigure)  RECONFIGURE="$1"; COMPILE="-c";               shift 1  ;;
    -R|--recast-down)  RECAST_DOWN="$1";                             shift 1  ;;
      -B|--recast-up)  RECAST_UP="$1";                               shift 1  ;;
-O|--recast-optional) get_option "$2" "$DEFAULT_RECAST_OPTIONALS" \
                                 RECAST_OPTIONALS n; COMPILE="-c";
                                                                     shift $n ;;
   -Z|--lazy-updates) get_option "$2" "$DEFAULT_LAZY_DEPENDS_UPDATE" \
                                 LAZY_DEPENDS_UPDATES n
                                                                     shift $n ;;
 -b|--force-base-dep)  FORCE_BASESYSTEM_DEPENDS=on;                  shift 1  ;;
        -c|--compile)  COMPILE="$1";                                 shift 1  ;;
       -d|--download)  export  FORCE_DOWNLOAD="$1"; COMPILE="-c";    shift 1  ;;
              --from)  export  SOURCE_CACHE=$2;                      shift 2  ;;
            --silent)  SILENT="$1";                                  shift 1  ;;
                  -s)  export  SEPARATE="$1";                        shift 1  ;;
             --queue)  CAST_QUEUE=yes ; COMPILE=$1;                  shift 1  ;;
                   *)  help                                   ;;
      esac

    else

      shift

   fi

  done

}

#---------------------------------------------------------------------
## @Arguments cast's arguments
## Goes through arguments and prints spells or parameters that are no
## switches rather.
#---------------------------------------------------------------------
function strip_parameters()  {
  local x n

  while  [  -n  "$1"  ];  do

    if  echo  "" $1  |  grep  -q  "^ -";  then

      case  $1  in

                  --deps)  shift 1  ;;
                --cflags)  shift 2  ;;
              --cxxflags)  shift 2  ;;
              --cppflags)  shift 2  ;;
               --ldflags)  shift 2  ;;
               --no-opts)  shift 1  ;;
                      -V)  shift 2  ;;
          -g|--grimoire*)  shift 2  ;;
        -R|--recast-down)  shift 1  ;;
          -B|--recast-up)  shift 1  ;;
    -O|--recast-optional)  get_option "$2" "" x n ; shift $n  ;;
       -Z|--lazy-updates)  get_option "$2" "" x n ; shift $n  ;;
     -b|--force-base-dep)  shift 1  ;;
        -r|--reconfigure)  shift 1  ;;
                  --from)  shift 2  ;;
                --silent)  shift 1  ;;
            -c|--compile)  shift 1  ;;
           -d|--download)  shift 1  ;;
                      -s)  shift 1  ;;
                 --queue)  shift 1  ;;
                       *)  shift 1  ;;

      esac

    else

      echo  $1
      shift

   fi

  done

}

#---------------------------------------------------------------------
## Look for optional parameter and use default otherwise
## specify via upvar how much to shift parameters
#---------------------------------------------------------------------
function get_option() {
  local param=$1
  local default=$2
  local answer=$3
  local up_shift=$4
  local _answer=$2
  local _up_shift=1
  case "$param" in
    always|ask-yes|ask-no|ignore) _answer=$param; _up_shift=2 ;;
    *) _answer=$default ;;
  esac
  upvar "$answer" "$_answer"
  upvar "$up_shift" "$_up_shift"
}

#---------------------------------------------------------------------
## Cast a single spell. When this function is called, all dependencies
## have been taken care of and we are allowed to cast this.
## @Globals SPELL
#---------------------------------------------------------------------
function cast_spell()  { (

  debug "cast" "Casting spell [$SPELL]"
  set_term_title "Casting spell [$SPELL]"

  run_details
  load_build_api || return 1

  dispel_conflicts $SPELL || return 1

  # show_downloading tail -f's (essentially) the download log as it
  # comes in, or if downloading is complete, cats the download log, in
  # either case it blocks until downloading is complete by the presence of
  # a ${download_log}.done file. If SEPARATE is set, then all downloading
  # is done first, so there is no need to wait for it to complete.
  if [[ ! $SEPARATE ]] ; then
    show_downloading $SPELL
  fi

  verify_sources ||   return 1

  # wait for solo casts, to finish and ensure that others can't
  acquire_cast_lock

  # all this needs to reorganized and stuff...later

  export IW_LOG="$TMP_DIR/$SPELL.iw"

  #some minor discussion occured about having this, i'll leave it out for now
  activity_log  "cast"  "$SPELL"  "$VERSION"  "start"

  # must declare OPTS before sourcing config
  local OPTS
  export OPTS
  run_spell_config

  libtrack_init

  local spell_depends spell_sub_depends
  get_uncommitted_depends_file $SPELL spell_depends
  test -e $spell_depends &&
  OPTS="$OPTS $(get_depends_options $spell_depends $SPELL)"

  get_uncommitted_sub_depends_file $SPELL spell_sub_depends
  test -e $spell_sub_depends &&
  local PROCESSED_SUB_DEPENDS=$(cut -f3 -d: $spell_sub_depends|tr '\n' ' ')

  # HACK for bug 2910 and 10546
  local saved_lc_all=$LC_ALL
  export LC_ALL=C

  export DISTCC_DIR="$SOURCE_DIRECTORY/.distcc"

  # this will run through the whole build process
  run_build_spell
  rc=$?

  # HACK for bug 2910 and 10546
  if [[ -n ${saved_lc_all} ]] ; then
    export LC_ALL=$saved_lc_all
  else
    unset LC_ALL
  fi

  # This is the home for anything and everything we do
  # when a phase4 succeeds or fails, no more spreading things out
  # into multiple functions.

  cd /

  # hooks back out to the build_api to do whatever needs to be done
  if [ $rc == 0 ] ; then
    run_spell_success
  else
    run_spell_failure $rc
  fi


  unlock_resources "cast" "$SPELL"
  #It's OK to try to release a lock you don't have
  unlock_resources "solo" "cast"

  return $rc

) }

##Removed *_solo functions, Duff 2002/11/01

#---------------------------------------------------------------------
## @Globals SPELLS
## @Stdout User information ("Collating dependencies")
## @Globals SPELL DEPS_ONLY
##
## Do dependency resolution. Takes the spells in the $SPELLS variable
## and resolves them. A superset list is created called $SPELLS_TO_CAST
#---------------------------------------------------------------------
function pass_one()  {
  #  This pass does configuration and dependency identification.
  debug "cast" "Starting pass_one()"

  echo -n "Computing previously installed dependencies..."
  compute_installed_depends "dep_f_hash"
  echo
  compute_uninstalled_depends "to_cast" "back_hash" "bad_spells" $SPELLS

  SPELLS_TO_CAST=$(hash_get_table_fields "to_cast"|tr '\n' ' ')

  SPELLS="$SPELLS ${BONUS_SPELLS[*]}"

  # special case of no spell making it through dependency resolution
  # or if DEPS_ONLY is set, there arent any dependent spells to cast
  if [ -z "$SPELLS_TO_CAST" ] ; then
    local cannot_cast=$(hash_get_table_fields "bad_spells")
    message "${PROBLEM_COLOR}No spells to cast!${DEFAULT_COLOR}"
    message "${MESSAGE_COLOR}Cannot cast these spells:${DEFAULT_COLOR}"
    message "---------------------------$PROBLEM_COLOR"

    local failure_reason_log=$CAST_BACKUPDIR/failure_reason_log
    local spell
    for spell in $cannot_cast; do
      grep -s "^$spell " $failure_reason_log ||
      message "$spell"
    done | sort -u | column
    rm -f $failure_reason_log

    message "${DEFAULT_COLOR}"
    exit 1
  fi

  # if only doing the deps, find the dependencies and make them the
  # SPELLS list, then recompute the full list of spells that needs to be cast
  if  [[ "$DEPS_ONLY" ]] ;  then
    local dep_spell
    SPELLS=$(
      for spell in $SPELLS; do
        local dep_spells
        hash_get_ref to_cast $spell dep_spells
        for dep_spell in $dep_spells; do
          spell_ok $dep_spell || echo $dep_spell
        done
      done|sort -u)
    spells=( $SPELLS )
    hash_reset deps_only
    hash_reset looked_at
    for ((i=0;$i<${#spells[@]}; i++)); do
      if [[ "${spells[$i]}" ]] &&
         ! [[ $(hash_get looked_at "${spells[$i]}") ]] ; then
        hash_put looked_at "${spells[$i]}" done
        if ! spell_ok ${spells[$i]}; then
          hash_put deps_only ${spells[$i]} done
          hash_get_ref to_cast "${spells[$i]}" new_spells
        fi
        spells=( ${spells[@]} $new_spells )
        unset new_spells
      fi
    done
    SPELLS_TO_CAST=$(hash_get_table_fields "deps_only"|tr '\n' ' ')
    hash_reset deps_only
    hash_reset looked_at
    if [ -z "$SPELLS_TO_CAST" ] ; then
      local parents=$(hash_get_table_fields "to_cast")
      message "${PROBLEM_COLOR}No spells to cast!${DEFAULT_COLOR}"
      message "${MESSAGE_COLOR}None of the spells have dependencies" \
              "to cast:${DEFAULT_COLOR}"
      message "---------------------------"
      message "${PROBLEM_COLOR}${parents}" | tr '[:blank:]' '\n' | sort | column
      message "${DEFAULT_COLOR}"
      exit 1
    fi
  fi

  message "${MESSAGE_COLOR}Collating dependencies...${DEFAULT_COLOR}"

  # have new depends overwrite existing ones
  for i in $SPELLS_TO_CAST; do
    local dependencies
    hash_get_ref to_cast $i dependencies
    hash_put dep_f_hash $i "$dependencies"
  done

  debug "cast" "pass_one, done with SPELLS=$SPELLS"

}


#---------------------------------------------------------------------
## @Arguments Spells to be cast
## @Stdout the "Spells are to be cast" message.
## @Stdin User interface, y or n
## @Globals SEPARATE
## Asks whether you want to cast the listed spells or not.
## Returns if not.
## Then it starts pass three and four by calling make.
## Depending on SEPARATE it starts pass three in the background
## or not.
#---------------------------------------------------------------------
function pass_two()  {
  #  This pass downloads required sources.
  #  And starts the make process
  debug "cast" "Starting pass_two()"

  unset_details

  message "${MESSAGE_COLOR}Spells are to be cast:${DEFAULT_COLOR}"
  message "---------------------------"
  message "${SPELL_COLOR}${SPELLS_TO_CAST}" | tr '[:blank:]' '\n' | column
  message "${DEFAULT_COLOR}"

  if ! query "Do you want to cast these spells?" "y" ; then
    message "Ok, quitting cast. Figure out what you want."
    # conflicts are already registered, but they weren't removed, so don't show them
    :> $CONFLICT_LIST
    return 1
  fi

  echo

  rm -f $TMP_DIR/pass_three.done # don't return until this file exists
  MINUS_K=yes
  if  [[  $SEPARATE   ]] ; then
    ( CAST_PASS="three"
      depengine_entry_point "$SPELLS" "$SPELLS_TO_CAST"
      if [[ $SCREEN_NAME ]] ; then
        screen_notify "$SCREEN_NAME" "Done downloading"
        screen_kill_window "$SCREEN_NAME" $SCREEN_SUMMON_WIN
      fi
      touch $TMP_DIR/pass_three.done
    )
  else
    ( CAST_PASS="three"
      trap "exit 1" INT TERM # this keep a rather nasty message
                                    # from appearing if we get killed
      depengine_entry_point "$SPELLS" "$SPELLS_TO_CAST"
      if [[ $SCREEN_NAME ]] ; then
        screen_notify "$SCREEN_NAME" "Done downloading"
        screen_kill_window "$SCREEN_NAME" $SCREEN_SUMMON_WIN
      fi
      touch $TMP_DIR/pass_three.done &>/dev/null
    ) &
  fi

  debug "cast" "Starting stage four make."

  (
    CAST_PASS="four"
    depengine_entry_point "$SPELLS" "$SPELLS_TO_CAST"
    touch $TMP_DIR/pass_four.done
  )


  # pass three might not finish, give it a short amount of time to complete
  # in case its near the end of a download or something, if after a minute
  # it doesn't finish just kill everything off.
  # in 1.14 this may be improved, see bug 8763
  local i
  let i=0
  if ! test -e $TMP_DIR/pass_three.done; then
    message "Download pass has not completed yet, pausing momentarily"
  fi
  while ! test -e $TMP_DIR/pass_three.done; do
    sleep 5
    let i+=5
    if [[ $i -gt 60 ]] ; then
      message "Download pass did not finish, killing it off."
      # this is rather violent, but job control in bash lame and theres
      # no other way to kill everything off effectively :-(
      ps x|grep $TMP_DIR|awk '{print $1}'|grep -v $$|xargs kill &>/dev/null
      echo -n .; sleep 1
      ps x|grep $TMP_DIR|awk '{print $1}'|grep -v $$|xargs kill &>/dev/null
      echo -n .; sleep 1
      ps x|grep $TMP_DIR|awk '{print $1}'|grep -v $$|xargs kill -9 &>/dev/null
      echo -n .;  sleep 1
      echo
      break
    fi
  done

}

#---------------------------------------------------------------------
## @param spellname
## @Globals SPELL
## Sets SPELL to the spellname and calls summon_spell with spellname
## as argument. This is equivalent to calling summon, but without the
## overhead.
## (Is being called by make)
## If in screen mode it also execs a tail to the summon window
#---------------------------------------------------------------------
function pass_three()  {
  debug "cast" "pass_three - $*"
  unset CAST_PASS
  trap "exit 1" INT TERM
  local SPELL=$1

  # download_log is removed with it's .done in libcast
  local download_log=$(get_spell_dl_log $SPELL)

  if [[ $SCREEN_NAME ]] && [ ! -p $TMP_DIR/download.fifo ] ; then
    # Create the summon window
    mkfifo $TMP_DIR/download.fifo
    # Neither tail not cat work properly on FIFOs so a while loop will
    # have to be used
    screen_new_window "$SCREEN_NAME" $SCREEN_SUMMON_WIN "Summon $SPELL" \
      /bin/bash -c  'while : ; do
          read LINE && echo $LINE || sleep 0.1 ;
        done < '$TMP_DIR/download.fifo
    sleep 0.1
  fi
  if [[ $SCREEN_NAME ]] ; then
    # A summon window is already up, just change the name
    screen_name_window "$SCREEN_NAME" $SCREEN_SUMMON_WIN "Summon $SPELL"
    # symlink the d/l log to the fifo
    ln -s $TMP_DIR/download.fifo $download_log
  else
    # normal non-screen cast, create a normal file
    touch $download_log
  fi

  # check if this is being resurrected and if so, don't summon
  # this will go away when resurrect support and dependency resolution merge
  if ! [[ $COMPILE ]] ; then
    VERSION=$(codex_set_current_spell_by_name $SPELL &> /dev/null;echo $VERSION)
    if [[ $VERSION ]] && can_resurrect $SPELL $VERSION &> /dev/null ; then
      # The 2>/dev/null is necessary because the dir may not exist
      echo "pass_three thought that $SPELL was being resurrected" \
        > $download_log 2>/dev/null
      # touch the 'log' file just in case the spell disagreed so cast doesnt
      # hang forever
      touch ${download_log}.done &>/dev/null
      return
    fi
  fi

  if  [[  $SEPARATE   ]] ; then
    summon_spell "$SPELL" &> "$download_log"
  else
    lock_file   $download_log
    summon_spell "$SPELL" &> "$download_log"
    unlock_file   $download_log
  fi

  # This is to notify show_downloading/cast_spell that it is done with
  # this source
  touch "${download_log}.done"
}

#---------------------------------------------------------------------
## @param spellname
## @Globals SPELL COMPILE
## Sets SPELL to spellname.
## Calls trigger "pre_cast"
## If COMPILE is not set, the spell doesn't need an update and it can
## be resurrected (can_resurrect SPELL) it calls resurrect with SPELL.
## Otherwise it calls cast_spell.
## If resurrect or cast_spell returned 0 it calls trigger "cast"
## (Is being called by make)
#---------------------------------------------------------------------
function pass_four()  {

  debug "cast" "pass_four - $*"
  local do_resurrect=no
  local VERSION
  unset CAST_PASS
  SPELL=$1

  trigger "pre_cast"

  if ! [[ $COMPILE ]] ; then
    # hacky way to get the latest version
    VERSION=$(codex_set_current_spell_by_name $SPELL &> /dev/null;echo $VERSION)
    if [[ ! $VERSION ]] ; then
      message "Can't find spell version for some reason, is $SPELL a spell?"
    else
      can_resurrect $SPELL $VERSION &> /dev/null &&
      ! does_spell_need_update $SPELL &> /dev/null &&
      do_resurrect=yes
    fi
  fi

  # this is a hacky way to tell what version we're updating from
  # if any, we can use it to find md5 logs for installing config type
  # files, it mostly is here for libresurrect.real_install_config_file
  local OLD_SPELL_VERSION=$(installed_version $SPELL)

  local rc
  if [[ "$do_resurrect" == "yes" ]] ; then
    resurrect_spell $SPELL $VERSION
    rc=$?
    # this is here for safety in case resurrect bails out somewhere
    # we didnt expect it to
    unlock_resources "libgrimoire" "install"
    unlock_resources "cast" "$SPELL"
    unlock_resources "solo" "cast"
  else
    cast_spell $*
    rc=$?
  fi

  return $rc
}


#---------------------------------------------------------------------
## @Globals SUCCESS_LIST FAILED_LIST CHECK_TRIGGERS_SUCCESS
## CHECK_TRIGGERS_FAILURE
## Does report generation.
#---------------------------------------------------------------------
function pass_five()  {

  debug "cast" "Function : pass_five"
  local rc=0
  local notice_log=$TMP_DIR/notice_log

  if [[ -s $notice_log ]]; then
    message "$DEFAULT_COLOR"
    message "${MESSAGE_COLOR}Spell notices are repeated below:"
    message "-----------------------------------------------$DEFAULT_COLOR"
    cp $notice_log $CAST_BACKUPDIR
    if (( $(wc -l < $notice_log) > 40 )); then
      cat - $notice_log > $notice_log.2 <<< "Spell notices are repeated below:"
      mv $notice_log.2 $notice_log
      timeout_pager $notice_log
    else
      cat $notice_log
    fi
    message "These notices are backed up to $CAST_BACKUPDIR/notice_log ."
    message "$DEFAULT_COLOR"
  fi

  # must exist or we get problems with checks here.
  touch $SUCCESS_LIST
  touch $FAILED_LIST
  touch $CHECK_TRIGGERS_SUCCESS
  touch $CHECK_TRIGGERS_FAILURE
  touch $CONFLICT_LIST

  debug "pass_five" "SUCCESS LIST is : `cat $SUCCESS_LIST 2>/dev/null`"
  debug "pass_five" "FAILED LIST is : `cat $FAILED_LIST 2>/dev/null`"
  debug "pass_five" "CONFLICT LIST is : `cat $CONFLICT_LIST 2>/dev/null`"
  debug "pass_five" "CHECK_TRIGGERS_SUCCESS is : `cat $CHECK_TRIGGERS_SUCCESS 2>/dev/null`"
  debug "pass_five" "bad_spells is : `hash_get_table_fields bad_spells`"
  debug "pass_five" "to_cast is : `hash_get_table_fields to_cast`"

  if [ -s $SUCCESS_LIST ] ; then

    message "${MESSAGE_COLOR}Finished processing install requests."
    message ""
    message "Spells installed successfully:"
    message "------------------------------${SPELL_COLOR}"

    for item in `cat $SUCCESS_LIST 2>/dev/null`; do
      message "$item"
    done | sort | column

    set_term_title "Casting successful."
    message "${DEFAULT_COLOR}"

  fi

  if  [  -s  $CHECK_TRIGGERS_SUCCESS ] ; then
    message "${DEFAULT_COLOR}"
    message "${MESSAGE_COLOR}Spells that had a check_self trigger succeed:"
    message "---------------------------------------------${SPELL_COLOR}"
    for item in `cat $CHECK_TRIGGERS_SUCCESS 2>/dev/null`; do
      message "$item"
    done | sort | column
    message "${DEFAULT_COLOR}"
  fi

  if  [  -s  $CHECK_TRIGGERS_FAILURE ] ; then
    message "${DEFAULT_COLOR}"
    message "${MESSAGE_COLOR}Spells that had a check_self trigger fail:"
    message "------------------------------------------${SPELL_COLOR}"
    for item in `cat $CHECK_TRIGGERS_FAILURE 2>/dev/null`; do
      message "$item"
    done | sort | column
    message "${DEFAULT_COLOR}"
  fi

  local NOT_CAST
  NOT_CAST=$( { hash_get_table_fields to_cast
    hash_get_table_fields bad_spells
    cat $SUCCESS_LIST $FAILED_LIST $CHECK_TRIGGERS_SUCCESS
    } | sort | uniq -u ) # all not caught otherwise
  if [[ "$NOT_CAST" ]] ;then
    message "${DEFAULT_COLOR}" #being paranoid
    message "${MESSAGE_COLOR}Spells that have been dropped:"
    message "------------------------------${QUERY_COLOR}"
    message "$NOT_CAST" | sort | column
    message "${DEFAULT_COLOR}"
  fi

  if  [  -s  $CONFLICT_LIST   ] ; then
    message "${DEFAULT_COLOR}"
    message "${MESSAGE_COLOR}Spells that have been removed due to conflicts:"
    message "-----------------------------------------------${QUERY_COLOR}"
    for item in $(cut -d" " -f2- $CONFLICT_LIST 2>/dev/null); do
      message "$item"
    done | sort | column
    message "${DEFAULT_COLOR}"
  fi

  #
  # To check for failed spells, we only need to see what is remaining in the
  # install queue.
  #
  if  [  -s  $FAILED_LIST   ] ; then
    local failure_reason_log=$CAST_BACKUPDIR/failure_reason_log
    message "${DEFAULT_COLOR}"
    message "${MESSAGE_COLOR}Spells that encountered problems:"
    message "---------------------------------${PROBLEM_COLOR}"

    while read item; do
      grep -s "^$item " $failure_reason_log ||
      message "$item"
    done < $FAILED_LIST | sort -u | column
    rm -f $failure_reason_log

    set_term_title "Casting failed."
    message "${DEFAULT_COLOR}"
    rc=1

  fi

  if [[ "$CAST_QUEUE" == "yes" ]] ; then
    if  [  -s  $INSTALL_QUEUE  ] ; then
      message "${MESSAGE_COLOR}The install queue is not empty, "
      message "it still contains the following spells: "
      message "---------------------------------------${PROBLEM_COLOR}"
      for item in `cat $INSTALL_QUEUE 2>/dev/null`; do
        message "$item"
      done | sort | column
      message "${DEFAULT_COLOR}"
      rc=1
    fi
  fi

  # check if confmeld should be run
  local spell deferred_list
  while read spell; do
    # ignore the date - previously deferred files should be merged too
    if [[ -d $CONFIG_STAGE_DIRECTORY/$spell ]]; then
      deferred_list="$spell $deferred_list"
    fi
  done < $SUCCESS_LIST
  if [[ $deferred_list ]]; then
    message "${MESSAGE_COLOR}Cast deferred installing some configuration files."
    message "The following spells have new pending updates: "
    message "----------------------------------------------$SPELL_COLOR"
    message "$deferred_list" | sort | column
    message "${MESSAGE_COLOR}Please run confmeld to merge them into your system."
    message "$DEFAULT_COLOR"
  fi

  debug "cast" "End of pass_five"
  return $rc
}

#---------------------------------------------------------------------
## @Arguments Spells to cast
## @Globals CAST_PASS
##
## Starts the passes and sets CAST_PASS accordingly.
#---------------------------------------------------------------------
function pass_zero()  {

  debug "cast" "Starting pass_zero()"
  debug "pass_zero" "Starting passes 1,2,3,4 with : '$*'"
  debug "pass_zero" "   and with spells : '$SPELLS'"
  export  CAST_PASS="one";    pass_one $*  &&
  export  CAST_PASS="two";    pass_two $SPELLS
  #pass_three and _four are run via pass_two
  debug "pass_zero" "Starting pass 5 with: '$@'"
  export  CAST_PASS="five";  pass_five "$@"


}

#---------------------------------------------------------------------
## @STDOUT User information ("Cleaning up as well as I can...")
## That function is being called when the process receives
## SIGINT. It then calls cleanup.
#---------------------------------------------------------------------
function int_trap()
{
  message "${PROBLEM_COLOR}SIGINT${DEFAULT_COLOR}"
  message "Cleaning up as well as I can..."
  cast_cleanup
  if [[ $SCREEN_NAME ]] ; then
    message -n "Press a key to exit screen."
    read -n 1
    screen_quit "$SCREEN_NAME"
  fi
  exit 1
}

#---------------------------------------------------------------------
## Used for cleaning up. Deleting some files and $TMP_DIR.
#---------------------------------------------------------------------
function cast_cleanup() {
  $STD_DEBUG
  cleanup_tmp_dir $TMP_DIR
  rmdir $CAST_BACKUPDIR 2>/dev/null
  clean_resources
}

#---------------------------------------------------------------------
## @Arguments arguments of cast
## @Globals CAST_PASS
## @return 0 always
## Starts prameter processing and casts the given spells.
## The list of given spells is being searched for invalid spells
## which are then being reported. Then the passes are being started
## according to CAST_PASS
#---------------------------------------------------------------------
function main()  {
  debug "cast" "main() - $*"
  local T_SPELLS
  process_parameters        "$@"

  if [[ "$CAST_QUEUE" == yes ]] ; then

    touch "$INSTALL_QUEUE"
    local tfile
    lock_start_transaction "$INSTALL_QUEUE" tfile
    grep -Ev '^$' $INSTALL_QUEUE > $tfile
    lock_commit_transaction "$INSTALL_QUEUE"
    if  [  -s $INSTALL_QUEUE  ]; then
      message -n "${MESSAGE_COLOR}Casting install queue..."
      message    "${DEFAULT_COLOR}"

      # remove possible empty lines from queue.
      if query "Would you like to review the queue history for each spell?" n ; then
        sorcery review-queue
      fi

      SPELLS=$(<$INSTALL_QUEUE)
    else
      message "${MESSAGE_COLOR}Install queue is empty${DEFAULT_COLOR}"
      exit
    fi
  else
    SPELLS=`strip_parameters  "$@"`
  fi

  if [[ $OVERRIDE_GRIMOIRES ]] ; then
    codex_set_grimoires $OVERRIDE_GRIMOIRES
  fi

  for spell in $SPELLS ; do
    codex_does_spell_exist $spell
    if [ $? -eq 0 ] ; then
      T_SPELLS="$T_SPELLS $spell"
    fi
  done
  SPELLS=$T_SPELLS

  if ! [[ $SPELLS ]]; then
    return 1
  fi

  case  $CAST_PASS  in
     one)  pass_one    $SPELLS  ;; #Never matches
     two)  pass_two    $SPELLS  ;; #Never matches
   three)  pass_three  $SPELLS  ;; #d/l sources, Never matches
    four)  pass_four   $SPELLS  ;; #real casting, Never matches
    five)  pass_five   $SPELLS  ;; #cast report
       *)  pass_zero   $SPELLS  ;; #start everything
  esac
  local rc=$?

  return $rc
}


. /etc/sorcery/config
[[ $VOYEUR_OVERRIDE ]] && VOYEUR="$VOYEUR_OVERRIDE"
if    [  $#      -eq  0  ];  then  help  |  $PAGER

elif  [[  $1 == -h  ]]  ||  [[  $1 == --help  ]] ; then help
elif  [  "$UID"  -gt  0  ];  then
  # validate the parameters before su-ing, since we may still drop out
  process_parameters "$@"

  echo  "Enter the root password, please."
  PARAMS=$(consolidate_params "$@")
  exec su -c "cast $PARAMS" root

elif  [  ${0:0:5} !=  "/tmp/"  ];  then

  # Make a nice dir structure to put stuff in, this exits if it fails
  mk_tmp_dirs backup /tmp/cast
  export CAST_BACKUPDIR=$TMP_DIR
  mk_tmp_dirs cast
  export CAST_TMPDIR=$TMP_DIR

  export SAFE_CAST="$TMP_DIR/casting.safe"
  export SUCCESS_LIST="$TMP_DIR/success_list"
  export FAILED_LIST="$TMP_DIR/failed_list"
  export CONFLICT_LIST="$TMP_DIR/conflict_list"
  export CHECK_TRIGGERS_SUCCESS="$TMP_DIR/check_trigger_success_list"
  export CHECK_TRIGGERS_FAILURE="$TMP_DIR/check_trigger_failure_list"
  lock_file $SAFE_CAST

  # $0 can be bashdb, but we want to copy cast
  smgl_which cast PATH_TO_CAST
  cp "$PATH_TO_CAST" $SAFE_CAST

  chmod +x $SAFE_CAST
  exec bash $SAFE_CAST "$@"

else
  export TOP_LEVEL=${TOP_LEVEL:-$SHLVL}
  if  [[  $NICE != "0"  ]] ; then
    renice $NICE -p $$  >/dev/null
  fi

  # If we are in a screen, weird things happen if we make another screen.
  # So disable screen mode if we are in a screen aready which wasn't started
  # by smgl. We also have to make sure this is the top level cast so the SA
  # won't see the message over and over and over
  if  [[ $TERM == screen ]]       &&
      [[ $TOP_LEVEL == $SHLVL ]]  &&
      [[ $SCREEN == on ]]         &&
      [[ ! $SCREEN_NAME ]]
  then
    export SCREEN_OVERRIDE=no
    message "${MESSAGE_COLOR}Although screen mode is enabled, you already seem to be in a another"
    message "screen session. Screen-in-screen is disabled."
    message "Continuing with screen mode off.${DEFAULT_COLOR}"
  fi

  # Disable screen mode if the appropriate env var is set
  if  [[ $SCREEN_OVERRIDE ]]       &&
      [[ $SCREEN == on ]]
  then
    export SCREEN=$SCREEN_OVERRIDE
    debug "cast" "Turning $SCREEN_OVERRIDE screen mode"
  fi

  # If screen mode is on, but we aren't in screen, start a new session
  # Call to screen_start never exits
  if  [[ $SCREEN == on ]]         &&
      [[ $TERM != screen ]]
  then
    SCREEN_NAME="ScreenCast $*"
    export SCREEN_NAME="${SCREEN_NAME:0:64}$(echo $SCREEN_NAME | md5sum)"
    screen_start "$SCREEN_NAME" "$0" "$@"
    echo "WTF?? We should not get here. screen_start execs!"
    exit 1
  fi

  # If running in screen, screen mode is on, screen was started by us
  # and finally, it hasn't been initialized yet.
  if  [[ $TERM == screen ]]       &&
      [[ $SCREEN == on ]]         &&
      [[ $SCREEN_NAME ]]          &&
      [[ ! $SCREEN_INITIALIZED ]]
  then
    debug "cast" "Initializing screen windows"
    screen_name_window "$SCREEN_NAME" $SCREEN_MAIN_WIN "Main"

    # Start debugging window if debugging is on and not set to /dev*
    if  [[ $DEBUG ]]              &&
        [[ ${DEBUG#/dev} == $DEBUG ]]
    then
      touch $DEBUG
      screen_new_window "$SCREEN_NAME" $SCREEN_DEBUG_WIN "Debug" \
      tail -f -n 0 -s 0.1 $DEBUG
      #screen_unmonitor_window "$SCREEN_NAME" $SCREEN_DEBUG_WIN
    fi

    screen_switch_window "$SCREEN_NAME" $SCREEN_MAIN_WIN
    screen_monitor_window "$SCREEN_NAME" $SCREEN_MAIN_WIN
    screen_quick_intro
    export SCREEN_INITIALIZED=yes
  fi

  trap int_trap INT
  main  "$@"
  rc=$?
  if [ $SHLVL -eq $TOP_LEVEL ] ; then
    unlock_file $SAFE_CAST
    cast_cleanup
  fi
  if [[ $SCREEN_NAME ]] && [[ $TOP_LEVEL -eq $SHLVL ]] ;  then
    screen_notify "$SCREEN_NAME" "Done casting"
    message -n "Press enter to exit screen."
    read
    screen_quit "$SCREEN_NAME"
  fi
  exit $rc
fi

debug "cast" "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
##
#---------------------------------------------------------------------
