#!/bin/bash
#---------------------------------------------------------------------
##
## @Synopsis Functions for dispelling a spell
##
##
## @Copyright Original version Copyright 2001 by Kyle Sallee
## @Copyright Additions/Corrections Copyright 2002-4 by the SourceMage Team
##
## Functions for dispelling spells.
#---------------------------------------------------------------------


#---------------------------------------------------------------------
## @Stdin list of files
##
## Given a list of files from standard input, deletes each file.
## Performs a "rm -f" on each file given in standard input, so be
## careful using this function!
##
#---------------------------------------------------------------------
function reap_regular_files()  {
  debug "libsorcery" "reap_regular_files()"
  local FILE
  while read FILE; do
    rm  -f   "$FILE"
  done
}


#---------------------------------------------------------------------
## @Stdin list of files
##
## Reads a list of files from standard input.  If the file has been
## modified (md5sum doesn't match the stored md5sum), then function
## C<reap_modified_file> is called.  Otherwise, the file is deleted.
##
#---------------------------------------------------------------------
function reap_config_files()  {
  debug "libsorcery" "reap_config_files()"
  local FILE
  while  read  FILE;  do
    [[ $FILE ]] || continue
    if grep -q "$(md5sum "$FILE" )" "$MD5S"; then
      debug libdispel "$FILE is a config file that hasnt changed, removing..."
      rm -f "$FILE"
    else
      reap_modified_file "$FILE"
    fi
  done
}

#---------------------------------------------------------------------
## @param file
##
## If C<PRESERVE> is off, will move the file to filename.YYYYMMDD.  If
## C<PRESERVE> is on, the file will not be moved.
##
#---------------------------------------------------------------------
function reap_modified_file()  {
  local SAVE
  message  "${FILE_COLOR}${1}${DEFAULT_COLOR}"
  message  "${MESSAGE_COLOR}was previously modified by SA?"
  case  $PRESERVE  in
     on)  message  "Therefore, it was not reaped."  ;;
    off)  SAVE="$1.`date  -u  +%Y%m%d`"
          mv  $1  $SAVE
          message  "Therefore, it was moved to"
          message  "${FILE_COLOR}${SAVE}"  ;;
  esac
  message  "${DEFAULT_COLOR}"

}

#---------------------------------------------------------------------
## Removes depends entries for what the spell depends on
#---------------------------------------------------------------------
function reap_depends()  {
  # save the old depends data as abandoned stuff so its seen later on
  # recasts as the default
  mkdir -p $ABANDONED_DEPENDS
  search_depends_status $DEPENDS_STATUS $SPELL > $ABANDONED_DEPENDS/$SPELL

  # This conditional is here because the iso team wants to be able to
  # save dependencies after dispel (bug 8109), average users are expected
  # to always run this code to remove old depends.
  if [[ $NO_REAP_DEPENDS != "on" ]] ; then
    remove_depends_status $DEPENDS_STATUS $SPELL
  fi
}


#---------------------------------------------------------------------
## @param file
## @param file
##
## First argument is a file containing a list of files to reap.
## Second argument is a file containing md5 sums of those files, used
## to detect if a config file has been modified.  Config files are any
## files in /etc or any of its sub-directories.
##
#---------------------------------------------------------------------
function reaper()  {
#  Example:  reaper "$INSTALL_LOG"  "$MD5_LOG"

  debug  "libsorcery" "Running reaper() on $1"

  if  !  [  "$REAP"  ==  "on"  ]   ||
      !  [  -f  $1             ];  then  return
  fi

  local MD5S=$2

  local UNIQUE="$$.$RANDOM"
  local REAPER_FILES="$TMP_DIR/reaper.$UNIQUE.files"
  local REAPER_DIRS="$TMP_DIR/reaper.$UNIQUE.dirs"
  local REAPER_SYMS="$TMP_DIR/reaper.$UNIQUE.syms"
  local REAPER_SPECIAL="$TMP_DIR/reaper.$UNIQUE.special"

  rm  -f  $REAPER_FILES  $REAPER_DIRS  $REAPER_SYMS

  # convert from TRACK_ROOT to / for protected filtering,
  # then to INSTALL_ROOT.
  # INSTALL_ROOT is relative to /, TRACK_ROOT is arbitrary.
  seperate_state_files $1 /dev/stdout /dev/null       |
  log_adjuster /dev/stdin /dev/stdout log filterable  |
  filter_protected                                    |
  log_adjuster /dev/stdin /dev/stdout filterable root |
  while read ITEM; do
    if   test -h  "$ITEM"; then echo "$ITEM" >> $REAPER_SYMS
    elif test -f  "$ITEM"; then echo "$ITEM" >> $REAPER_FILES
    elif test -d  "$ITEM"; then echo "$ITEM" >> $REAPER_DIRS
    # if it isn't a symlink, regular file or directory, assume it is a
    # special file (character, block, or fifo)
    elif test -e  "$ITEM"; then echo "$ITEM" >> $REAPER_SPECIAL
    fi
  done

  if test -f $REAPER_FILES ; then
    sed "s:^$INSTALL_ROOT::" $REAPER_FILES |
    grep -v /var/log/sorcery               |
    filter_configs -v                      |
    sed "s:^:$INSTALL_ROOT:"               |
    reap_config_files

    sed "s:^$INSTALL_ROOT::" $REAPER_FILES |
    grep -v /var/log/sorcery               |
    filter_configs                         |
    sed "s:^:$INSTALL_ROOT:"               |
    reap_regular_files
  fi

  [ -f $REAPER_SYMS ] && rm -f `cat $REAPER_SYMS` 2>/dev/null
  [ -f $REAPER_SPECIAL ] && rm -f `cat $REAPER_SPECIAL` 2>/dev/null
  [ -f $REAPER_DIRS ] && rmdir `sort -r $REAPER_DIRS` 2>/dev/null
  [ -f $REAPER_FILES ] && rmdir `dirnames < $REAPER_FILES |uniq|sort  -r` 2>/dev/null

  rm  -f  $REAPER_FILES  $REAPER_DIRS  $REAPER_SYMS $REAPER_SPECIAL
}

#---------------------------------------------------------------------
## @param file
##
## First argument is a file containing install log, removes state files
##
#---------------------------------------------------------------------
function reap_state_files()  {
#  Example:  reaper "$INSTALL_LOG"  "$MD5_LOG"

  debug  "libsorcery" "Running reaper() on $1"

  local UNIQUE="$$.$RANDOM"
  local REAPER_FILES="$TMP_DIR/reaper.$UNIQUE.files"
  local REAPER_DIRS="$TMP_DIR/reaper.$UNIQUE.dirs"
  local REAPER_SYMS="$TMP_DIR/reaper.$UNIQUE.syms"

  rm  -f  $REAPER_FILES  $REAPER_DIRS  $REAPER_SYMS

  # convert from TRACK_ROOT to / for protected filtering,
  # then to INSTALL_ROOT.
  # INSTALL_ROOT is relative to /, TRACK_ROOT is arbitrary.
  seperate_state_files $1 /dev/null /dev/stdout       |
  log_adjuster /dev/stdin /dev/stdout log root        |
  grep -v ${LOG_DIRECTORY#$STATE_ROOT}                |
  while read ITEM; do
    if   test -h  "$ITEM"; then echo "$ITEM" >> $REAPER_SYMS
    elif test -f  "$ITEM"; then echo "$ITEM" >> $REAPER_FILES
    elif test -d  "$ITEM"; then echo "$ITEM" >> $REAPER_DIRS
    fi
  done

  if test -f $REAPER_FILES ; then
    cat $REAPER_FILES | xargs rm
  fi

  [ -f $REAPER_SYMS ] && rm -f `cat $REAPER_SYMS` 2>/dev/null
  [ -f $REAPER_DIRS ] && rmdir `sort -r $REAPER_DIRS` 2>/dev/null
  [ -f $REAPER_FILES ] && rmdir `dirnames < $REAPER_FILES |uniq|sort  -r` 2>/dev/null

  rm  -f  $REAPER_FILES  $REAPER_DIRS  $REAPER_SYMS
}

#-----
## Checks that a spell is indeed installed.
## @return true Can be dispelled
## @return false Cannot be dispelled
#-----
function dispel_not_possible()  {

  if  ! spell_ok $SPELL ; then
    message  "${SPELL_COLOR}${SPELL}${PROBLEM_COLOR} is not installed."  \
             "${DEFAULT_COLOR}"
  else
    false
  fi

}

#-----
## Does the sustained checks for spells
#-----
function dispel_sustained()  {

  if  [  "$SUSTAIN"  ==  "on"  ]  &&
      grep  -q  "^$SPELL$"  $SUSTAINED
  then
    message "${SPELL_COLOR}${SPELL}${PROBLEM_COLOR} is sustained." \
            "${DEFAULT_COLOR}"
  else
    false
  fi

}

#-----
## Find out where a spell is located
## @Globals SPELL
#-----
function load_spell()  {
  local SPELL=$1
  if tablet_set_spell $SPELL ; then
    load_functions_file
  else
    SCRIPT_DIRECTORY="`codex_find_spell_by_name $SPELL`"

    VERSION=`installed_version  $SPELL`
    INST_LOG=$INSTALL_LOGS/$SPELL-$VERSION
    MD5_LOG=$MD5SUM_LOGS/$SPELL-$VERSION
    if [[ $SCRIPT_DIRECTORY ]] ; then
      codex_set_current_spell $SCRIPT_DIRECTORY
      load_functions_file
    else
      message "Spell is missing from grimoires"
      message "unable to run any PRE or POST_REMOVE scripts it once had."
      if ! query "Continue anyway?" y; then
        return 1
      fi
    fi
  fi
}

#-----
## Run the PRE_REMOVE script if it exists
#-----
function pre_remove() {
  if [[ $SCRIPT_DIRECTORY ]] && [ -x $SCRIPT_DIRECTORY/PRE_REMOVE ]; then
    unset  LD_PRELOAD
    . $SCRIPT_DIRECTORY/PRE_REMOVE
  fi
}

#-----
## Run the POST_REMOVE script if it exists.
#-----
function post_remove() {
  # what is the point of this?
  LD_PRELOAD_OLD="$LD_PRELOAD"
  unset LD_PRELOAD

  export  LD_PRELOAD="$LD_PRELOAD_OLD"

  if  [[ $SCRIPT_DIRECTORY ]] && [  -x  $SCRIPT_DIRECTORY/POST_REMOVE  ]
  then     . $SCRIPT_DIRECTORY/POST_REMOVE
  fi

}




#---------------------------------------------------------------------
## Dispel a spell
#---------------------------------------------------------------------

function dispel_spell() {

    local SPELL=$1
    # fake dispel, only remove sorcery metadata
    if [[ $DEBUG_DISPEL ]] ; then
        echo "pretending to dispel $SPELL"
        load_spell $SPELL           &&
        remove_spell  $SPELL &&
        reap_depends &&
        message  "${DISPEL_COLOR}Partly dispelled spell:"  \
                 "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}"
        return
    fi

    if  dispel_sustained  || dispel_not_possible ; then
      DISPEL_EXIT_STATUS=1
      return 1
    fi

    [[ $TRIGGER == off ]] || trigger "pre_dispel"

    (
      # this function messes with the environment a lot leave it in a subshell
      load_spell $SPELL           &&
      pre_remove                  &&
      reaper  $INST_LOG  $MD5_LOG &&
      post_remove                 &&
      reap_state_files $INST_LOG  &&
      remove_spell  $SPELL        &&
      { [[ $TRIGGER == off ]] || trigger "dispel";} &&
      remove_triggers $SPELL      &&
      message  "${DISPEL_COLOR}Dispelled spell:"  \
               "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" &&
      activity_log  "dispel"  "$SPELL"  "$VERSION"  "success"
    ) &&
    reap_depends
}

#---------------------------------------------------------------------
## @param	default value
## Set one of always, ask_yes, ask_no, and ignore to on based
## on the value of default, leave the remaining as off.
#---------------------------------------------------------------------
function dispel_depends_value_to_items() {
  always=off
  ask_yes=off
  ask_no=off
  ignore=off
  case $1 in
     always) always=on ;;
    ask-yes) ask_yes=on ;;
     ask-no) ask_no=on ;;
     ignore) ignore=on ;;
          *) ignore=on ;;
  esac
}


#---------------------------------------------------------------------
## @param	Title of the menu
## @param	Name of the parameter being set
## @param	Default value of the parameter being set
##
## Present a radiolist menu with the quad-options for one of various
## dependency following options.
#---------------------------------------------------------------------
function dispel_depends_menu_template() {
  local TITLE=$1
  local DEFAULT_NAME=$2
  local DEFAULT=$3
  local always ask_yes ask_no ignore rc
  dispel_depends_value_to_items $DEFAULT
  RESULT=`eval $DIALOG ' --title "$TITLE"   \
           --radiolist                     \
           ""                              \
           0 0 0                           \
           always  "" $always              \
           ask-yes "" $ask_yes             \
           ask-no  "" $ask_no              \
           ignore  "" $ignore'`
  rc=$?
  if [[ $rc == 0 ]] ; then
    # remove spurious ""
    RESULT=`echo "${RESULT}" | sed -e 's/^"//' -e 's/"$//'`
    modify_local_config $DEFAULT_NAME "$RESULT"
    eval "$DEFAULT_NAME=\"$RESULT\""
  fi
}

#---------------------------------------------------------------------
## Present menus for each of the four dependency following options.
#---------------------------------------------------------------------
function dispel_depends_defaults_menu() {
  dispel_depends_menu_template \
               "Default action for orphaned spells"   \
               "ORPHAN_MENU_DEFAULT" "$ORPHAN_MENU_DEFAULT"
  dispel_depends_menu_template \
               "Default action for non-orphaned spells"   \
               "NONORPHAN_MENU_DEFAULT" "$NONORPHAN_MENU_DEFAULT"
  dispel_depends_menu_template \
               "Default action for recasting repairable parents spells"   \
               "RECAST_PARENT_MENU_DEFAULT" "$RECAST_PARENT_MENU_DEFAULT"
  dispel_depends_menu_template \
               "Default action for broken parent spells"   \
               "DISPEL_PARENT_MENU_DEFAULT" "$DISPEL_PARENT_MENU_DEFAULT"

}

#---------------------------------------------------------------------
##
## 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
##
#---------------------------------------------------------------------
