#!/bin/bash
#---------------------------------------------------------------------
##
## @Synopsis Set of functions used by the internal sorcery scripts
##
## This should really be home to things related to the sorcery script
## itself, not a repository for other functions. (Andrew 5/29/04)
##
## @Copyright Original version Copyright 2001 by Kyle Sallee
## @Copyright Additions/Corrections Copyright 2002 by the Source Mage Team
##
#---------------------------------------------------------------------

#---------------------------------------------------------------------
## @param branch
##
## Updates the sorcery scripts to their latest version for specified
## branch.
##
#---------------------------------------------------------------------
function update_sorcery_scripts() {

  local BRANCH=$1

  debug "libsorcery" "update_sorcery_scripts() (sorcery-$BRANCH)"

  {
    #This section is to make sure that all sorcery dependencies are cast
    #Keep this outer if until basesystem is in the grimoires properly.
    #(2002/10/13)
    if [[ `codex_find_spell_by_name basesystem` ]] ; then
      if ! spell_ok basesystem  ; then
        cast basesystem
      fi &&
      if [[ "$UPDATEFIX" == on ]] ; then
        cleanse --fix basesystem
      fi
      if [[ $? != 0 ]] ; then
        query "Would you like to continue anyway?" y || return 1
      fi
    fi
  }

  pushd $TMP_DIR &>/dev/null
  # sorcery is not a spell
  # there is no spoon
  SOURCE=sorcery-$BRANCH.tar.bz2
  SOURCE_DIRECTORY=$BUILD_DIRECTORY/sorcery
  SOURCE_URL=$SORCERY_URL/$SOURCE

  message  "${MESSAGE_COLOR}Downloading source file"       \
           "${FILE_COLOR}${SOURCE}${DEFAULT_COLOR}"     \
           "for sorcery update."
  url_download "$SOURCE" "$SOURCE_URL" "" "sr_target" "sr_type" || {
    message "${PROBLEM_COLOR}Failed to download sorcery${DEFAULT_COLOR}"
    return 1
  }
  if [[ $sr_type != file ]] ; then
    message "Downloaded type is not a file, file a bug if you see this"
    return 1
  fi
  [[ $sr_target != $SOURCE ]] && mv $sr_target $SOURCE

  gpg_verify_sorcery $SOURCE $SORCERY_URL
  rc=$?

  gpg_user_query $rc $SORCERY_BRANCH sorcery || return 1
  popd &>/dev/null

  local FILENAME=$TMP_DIR/$SOURCE
  # copy the tarball to /var/spool/sorcery after we've downloaded and
  # verified it.
  cp $FILENAME $SOURCE_CACHE

  mk_source_dir  $SOURCE_DIRECTORY              &&
  cd $BUILD_DIRECTORY
  bzip2  -cdf   $FILENAME                       |
  tar --owner=root --group=root -xf /dev/stdin  &&
  cd      $SOURCE_DIRECTORY                     &&

  echo `pwd`                                    || return 1

  # last chance to lock
  message -n "${MESSAGE_COLOR}Waiting for${DEFAULT_COLOR} ${SPELL_COLOR}all" \
             "${DEFAULT_COLOR} ${MESSAGE_COLOR}spells to complete..." \
             "${DEFAULT_COLOR}"
  lock_resources "solo" "cast" # prevent other casts
  excllock_resources "cast" "sorcery" # wait for other casts
  message "done."
  lock_resources "libgrimoire" "install"      &&

  if test -x ./uninstall && test -f /etc/sorcery/install.log ; then
    message uninstalling...
    ./uninstall
  else
    # rough equivalent to prepare_install...
    rm /etc/sorcery/* 2>/dev/null
    rm /etc/sorcery/licenses/*   2>/dev/null
    rm /etc/sorcery/mirrors/* 2>/dev/null
    rm /var/lib/sorcery/* 2>/dev/null
    rm -r /var/lib/sorcery/modules/* 2>/dev/null
    rm -r /var/lib/sorcery/archspecs/* 2>/dev/null
    rm -r /var/lib/sorcery/build/* 2>/dev/null
  fi
  ./install                                     &&

  cd /                                          &&
  unlock_resources "libgrimoire" "install"      &&

  SORCERY_VERSION=`cat /etc/sorcery/version`

  unlock_resources "solo" "cast"
  unlock_resources "cast" "sorcery"

  if [ $? -eq 0 ] ; then
    activity_log "update" "sorcery-$BRANCH" "$SORCERY_VERSION" "success"
    rm_source_dir
  else
    activity_log "update" "sorcery-$BRANCH" "$SORCERY_VERSION" "failure"
    if [[  $CLEAN_SOURCE == on ]]; then
      rm_source_dir
    fi
  fi
  message "${MESSAGE_COLOR}Current${SPELL_COLOR}" \
          "sorcery-$BRANCH${DEFAULT_COLOR} ${MESSAGE_COLOR}version" \
          "${VERSION_COLOR}$SORCERY_VERSION${DEFAULT_COLOR}"
  sorcery_history
  rm $FILENAME
}

#---------------------------------------------------------------------
##
## Menu query to user asking to set the nice value which sorcery should
## use to run proccesses at.
##
#---------------------------------------------------------------------
function set_nice()  {

  PROMPT="Please enter the process priority sorcery should run at."

  if  NICE=`eval $DIALOG '  --ok-label  "Commit"  \
                            --inputbox            \
                            "$PROMPT"             \
                            0 0  "$NICE"'`
  then
     modify_local_config "NICE" "$NICE"
  fi

}

#---------------------------------------------------------------------
##
## Menu query to user asking to set the umask value which sorcery should
## use to run proccesses with.
##
#---------------------------------------------------------------------
function set_umask()  {

  PROMPT="Please enter the permissions mask sorcery should run with."

  if  UMASK=`eval $DIALOG '  --ok-label  "Commit"  \
                            --inputbox            \
                            "$PROMPT"             \
                            0 0  "$UMASK"'`
  then
     modify_local_config "UMASK" "$UMASK"
  fi

}


#---------------------------------------------------------------------
## @param filename
##
## Displays and/or mails the contents of the file given as the first
## argument.
##
#---------------------------------------------------------------------
function report() {

  if  !  [  -f  $1  ];  then  return;  fi

  if  [  "$VIEW_REPORTS"  ==  "on"  ];  then

    debug  "libsorcery" "Prompting to view $2 for $SPELL"

    VIEW_PROMPT="View $2 for ${SPELL_COLOR}${SPELL}-${VERSION_COLOR}${VERSION}${DEFAULT_COLOR}?"
    sound  REPORT
    if  query  "$VIEW_PROMPT" n;  then  $PAGER  $1;  fi
  fi

  if  [  "$MAIL_REPORTS"  ==  "on"  ];  then
    debug  "libsorcery" "Mailing report ($2) for $SPELL to $SORCERER"
    date  -u  |
    mail  -s  "Sorcery Report : $HOSTNAME : $2 for $SPELL-$VERSION"  \
              -a $1 $SORCERER 2>/dev/null
  fi

  true

}


#---------------------------------------------------------------------
## @param filename
##
## Given a filename, will return the actual filename if a similar
## filename with a different extension exists.
##
#---------------------------------------------------------------------
function guess_filename()  {

  FILENAME=$1

  debug  "libsorcery" "Running guess_filename() on $FILENAME"

  BASENAME=`echo  $FILENAME         |
            sed  "s/\.tar\.gz$//"   |
            sed  "s/\.tgz$//"       |
            sed  "s/\.tar\.bz2$//"`

  if    [  -f  $FILENAME          ];  then  echo  $FILENAME
  elif  [  "$FUZZ"  !=  "on"     ];  then  return  1
  elif  [  -f  $BASENAME.tar.gz   ];  then  echo  $BASENAME.tar.gz
  elif  [  -f  $BASENAME.tar.bz2  ];  then  echo  $BASENAME.tar.bz2
  elif  [  -f  $BASENAME.tgz      ];  then  echo  $BASENAME.tgz
  else  false
  fi

}

#-------------------------------------------------------------------------
## check if the file exists, or one with a similar compression exists
## @param file
#-------------------------------------------------------------------------
function file_exists()  {
  guess_filename  $1  >  /dev/null
}

#---------------------------------------------------------------------
##
## Saves the current libraries associated with the spell, from /lib
## and /usr/lib into $SOURCE_DIRECTORY/old.libraries/
## Also runs ldconfig with the saved libraries.
##
#---------------------------------------------------------------------
function save_libraries()  {

  if    [  -z  "$SOURCE_DIRECTORY"  ]
  then  return
  fi

  debug  "libsorcery" "Running save_libraries()"

  OLD_LIBS=$SOURCE_DIRECTORY/old.libraries
  mkdir  -p  $OLD_LIBS

  SAVED=$OLD_LIBS/$SPELL.saved.libraries
  lock_file $SAVED
  rm  -rf  $SAVED

  OLD_VERSION=`installed_version  $SPELL`
      OLD_LOG=$INSTALL_LOGS/$SPELL-$OLD_VERSION

  grep   "^/lib/\|^/usr/lib"  $OLD_LOG  |
  while  read        LINE;  do

    if   [  -f      $LINE   ]  &&
         file  -bL  $LINE   |
         grep  -q   "shared object"
    then
      if  [  -h  $LINE  ];  then
        DEST=$(  basename  $(  ls   -la  "$LINE"  |
                               cut  -d  '>'  -f2  |
                               cut  -c  2-
                            )
              )
        ln  -sf  $DEST  $OLD_LIBS/`basename  $LINE`
      else
        cp  $LINE  $OLD_LIBS
      fi
      echo       $OLD_LIBS/`basename  $LINE`  >>  $SAVED
    fi


  done

  ldconfig  $OLD_LIBS
  unlock_file $SAVED

  if  [  -z   "$LD_LIBRARY_PATH"  ]
  then  export  LD_LIBRARY_PATH="$OLD_LIBS"
  else  export  LD_LIBRARY_PATH="$OLD_LIBS:$LD_LIBRARY_PATH"
  fi

}

#---------------------------------------------------------------------
##
## Saves the current libraries associated with the spell, from /lib
## and /usr/lib into $SOURCE_DIRECTORY/old.libraries/
## Also runs ldconfig with the saved libraries.
##
#---------------------------------------------------------------------
function save_binaries()  {
  local dir
  if    [  -z  "$SOURCE_DIRECTORY"  ]
  then  return
  fi

  debug  "libsorcery" "Running save_binaries()"

  OLD_BINS=$SOURCE_DIRECTORY/old.binaries
  mkdir  -p  $OLD_BINS

  SAVED=$OLD_BINS/$SPELL.saved.binaries
  lock_file $SAVED
  rm  -rf  $SAVED

  OLD_VERSION=`installed_version  $SPELL`
      OLD_LOG=$INSTALL_LOGS/$SPELL-$OLD_VERSION

  grep   "^/bin/\|^/sbin/\|^/usr/bin/\|^/usr/sbin/"  $OLD_LOG  |
  while  read        LINE;  do

    if   [  -f      $LINE   ]  &&
         file  -bL  $LINE   |
         grep  -q   "executable"
    then
      if  [  -h  $LINE  ];  then
        DEST=$( readlink "$LINE" )
        mkdir -p $( dirname $OLD_BINS/$LINE )
        ln  -sf  $DEST  $OLD_BINS/$LINE
      else
        mkdir -p $( dirname $OLD_BINS/$LINE )
        cp  $LINE  $OLD_BINS/$LINE
      fi
      echo       $OLD_BINS/$LINE >> $SAVED
    fi


  done

  unlock_file $SAVED

  for dir in $( find $OLD_BINS )
  do
    if [[ -d $dir ]]
    then
      export PATH=$( envar_prepend_path "$dir" "$PATH" )
    fi
  done
}


#---------------------------------------------------------------------
##
## Runs default ldconfig to stop using the saved libraries
##
#---------------------------------------------------------------------
function release_saved_libraries()  {
  ldconfig
}


#---------------------------------------------------------------------
##
## Recovers from a CTRL-C while casting a spell
## FIXME: this should be a resurrect
##
#---------------------------------------------------------------------
function spell_recover()  {

  debug  "libsorcery" "Running spell_recover()"

  message  "${MESSAGE_COLOR}Aborting dispel of ${SPELL_COLOR}${SPELL}" \
           "${MESSAGE_COLOR} and recovering${DEFAULT_COLOR}"

  CURRENT_VERSION=$(installed_version  $SPELL)
  CACHE_COMP="$INSTALL_CACHE/$SPELL-$CURRENT_VERSION-$HOST.tar$EXTENSION"
  CONTINUE=false

  if    [  !      -f  $CACHE_COMP   ];  then
    false
  elif  [         -n  "$EXTENSION"  ];  then
    CONTINUE=true
  elif  $COMPRESSBIN  -tf  $CACHE_COMP
  then
    CONTINUE=true
  fi

  if  $CONTINUE
  then

    if  [  -n  "$EXTENSION"  ];  then
      $COMPRESSBIN  -cd  $CACHE_COMP  |  tar  -Pkx  2>/dev/null
    else
      tar  -Pkxf  $CACHE_COMP  2>/dev/null
    fi
    add_spell  $SPELL  installed  $CURRENT_VERSION
  else
    false
  fi

  exit  130

}


#---------------------------------------------------------------------
## @param spell name
## @Stdout section
## Returns the section a spell is in.
##
#---------------------------------------------------------------------
function find_section()  {

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

  codex_get_spell_section_name  $1

}


#---------------------------------------------------------------------
##=item directories
##
## @Stdin list of files/dirs/..
## @Stdout list of dirs
##
## Reads a list from standard input, and prints out each entry that is
## a directory (and not a symbolic link to a directory).
#---------------------------------------------------------------------
function directories() {

  while  read       ITEM;  do
    if  [     -d  "$ITEM"  ]   &&
        !  [  -h  "$ITEM"  ];  then
      echo        "$ITEM"
    fi
  done

}


#---------------------------------------------------------------------
## @Stdin list of files/dirs/..
## @Stdout list of files
## Reads a list from standard input, and prints out each entry that is
## a file (and not a symbolic link to a file).
##
#---------------------------------------------------------------------
function files()  {

  while  read       ITEM;   do

    if  [     -f  "$ITEM"  ]   &&
        !  [  -h  "$ITEM"  ];  then
      echo "$ITEM"
    fi
  done

}


#---------------------------------------------------------------------
## @Stdin list of files/dirs/..
## @Stdout list of symlinks
## Reads a list from standard input, and prints out each entry that is
## a symbolic linke
##
#---------------------------------------------------------------------
function symlinks()  {

  while  read    ITEM;   do
    if  [  -h  "$ITEM"  ];  then
      echo     "$ITEM"
    fi
  done

}


#---------------------------------------------------------------------
## @param filename
## @Stdin list of files/dirs/..
## @Stdout filtered list
## First argument is a file that contains (basic) grep regular expressions.
## They are joined with \|'s and given to grep -v.
## Which will filter standard input to remove entries that match.
## Used to filter out excluded or protected files from install logs.
##
#---------------------------------------------------------------------
function filter() {
  local thing RID_LIST NOT_RID_LIST
  local files each invert
  for each in $@; do
    test -f $each && files="$files $each"
  done

  if [[ $files ]] ; then
    RID_LIST=$(
      cat $files   |
      grep -v '^!' | while read thing; do
                       echo -n "${thing}\|"
                     done)
    NOT_RID_LIST=$(
      cat $files   |
      grep '^!'    | while read thing; do
                       echo -n "${thing/\!/}\|"
                     done)
    RID_LIST=${RID_LIST%\\|}
    NOT_RID_LIST=${NOT_RID_LIST%\\|}

    local TMP_SSF=$(make_safe_dir)
    local FILE=$TMP_SSF/ssf

    if [[ $RID_LIST ]]
    then
      if [[ $NOT_RID_LIST ]]
      then
        cat > $FILE
        {
          grep -v "$NOT_RID_LIST\|$RID_LIST" $FILE
          grep "$NOT_RID_LIST" $FILE
        }
        rm $FILE 2>/dev/null
      else
        grep -v "$RID_LIST"
      fi
    else
      cat
    fi
    rmdir $TMP_SSF 2>/dev/null
  else
    cat
  fi
}

#---------------------------------------------------------------------
## @param filename
## @Stdin list of files/dirs/..
## @Stdout filtered list
## First argument is a file that contains (basic) grep regular expressions.
## They are joined with \|'s and given to grep
## Which will filter standard input to remove and remove entries
## that do not match.
## Used to filter in config files from install logs.
##
#---------------------------------------------------------------------
function filter_in() {
  local thing RID_LIST NOT_RID_LIST
  local files each
  for each in $@; do
    test -f $each && files="$files $each"
  done

  if [[ $files ]] ; then
    RID_LIST=$(
      cat $files   |
      grep -v '^!' | while read thing; do
                       echo -n "${thing}\|"
                     done)
    NOT_RID_LIST=$(
      cat $files   |
      grep '^!'    | while read thing; do
                       echo -n "${thing/\!/}\|"
                     done)
    RID_LIST=${RID_LIST%\\|}
    NOT_RID_LIST=${NOT_RID_LIST%\\|}

    if [[ $RID_LIST ]]
    then
      if [[ $NOT_RID_LIST ]]
      then
        grep "$RID_LIST" |
        grep -v "$NOT_RID_LIST"
      else
        grep "$RID_LIST"
      fi
    else
      # grep '' outputs everything in this case we want nothing
      cat > /dev/null
    fi
  else
    cat
  fi
}

function filter_generic() {
  local args=$1
  local filter_name=$2
  local system_filter=$3
  local spell_from=$4
  local grimoire_filter section_filter spell_filter spell_dir
  if [[ $spell_from == "codex" ]]
  then
    debug libsorcery "filter_generic codex at : $SCRIPT_DIRECTORY "
    grimoire_filter=$GRIMOIRE/$filter_name
    section_filter=$SECTION_DIRECTORY/$filter_name
    spell_filter=$SCRIPT_DIRECTORY/$filter_name
  else
    if tablet_find_spell_dir $SPELL spell_dir &>/dev/null; then
      debug libsorcery "filter_generic tablet at : $spell_dir "
      tablet_get_spell_filter "$spell_dir" "$filter_name" spell_filter &>/dev/null
      tablet_get_section_filter "$spell_dir" "$filter_name" section_filter &>/dev/null
      tablet_get_grimoire_filter "$spell_dir" "$filter_name" grimoire_filter &>/dev/null
    fi
  fi
  set -- $args
  if [ "$1" == -v ] ; then
    shift
    filter_in "$@" $system_filter $grimoire_filter $section_filter $spell_filter
  else
    filter "$@" $system_filter $grimoire_filter $section_filter $spell_filter
  fi
}

function filter_volatiles() {
  filter_generic "$*" volatiles $VOLATILES
}

function filter_configs() {
  filter_generic "$*" configs $CONFIGS
}

function filter_excluded() {
  filter_generic "$*" excluded $EXCLUDED
}

function filter_protected() {
  filter_generic "$*" protected $PROTECTED
}

#---------------------------------------------------------------------
## @Stdin list of files
## @Stdout list of directories
##
## Given a list of files from standard input, returns the directory
## of each file.
##
## Note: Is a duplicate of get_dirnames. 2003-09-28 Martin
#---------------------------------------------------------------------
function dirnames()  {
  while  read  FILE;  do  dirname  "$FILE";  done;
}

#---------------------------------------------------------------------
## @param queue filename
## @param items to remove ...
##
## The first argument is the name of the file containing the queue.
## If a second argument is given, any items in the queue that match
## the second argument are removed from the queue.  Otherwise, the top
## line of the queue is removed, and returned.
##
#---------------------------------------------------------------------
function pop_queue() {  (

  local ITEM=`esc_str $2`
  local exit_code=0

  [[ -f $1 ]] || return 1

  lock_start_transaction "$1" tQUEUE_FILE
  if  [  -n  "$ITEM"  ];  then
    grep  -v  "^$ITEM\$"  $1 > $tQUEUE_FILE
  else
    FOUND=`sed  -n  1p  $1`

    if  [  -z  "$FOUND"  ];  then
      exit_code=1
    else
      FOUND=`esc_str $FOUND`
      grep  -v  "^$FOUND$"  $1 > $tQUEUE_FILE
      echo  $FOUND
    fi
  fi
  lock_commit_transaction $1

  return $exit_code

) }


#---------------------------------------------------------------------
## @param filename
##
## The first argument is the name of the file that installed correctly
## and will be removed from the install file (this makes a failure of
## the casting of the queue not have to recast completed spells).
##
#---------------------------------------------------------------------
function pop_install_queue() {

  pop_queue  $INSTALL_QUEUE  "$1"

}


#---------------------------------------------------------------------
## @param queue filename
## @param item to add
## The first argument is the name of the file containing the queue.
## The second argument is an item to add to the end of the queue.  If
## the item exists anywhere in the queue, the item is removed from the
## queue before being added at the end.
##
#---------------------------------------------------------------------
function push_queue()  {

  pop_queue  "$1"  "$2"
  local t_file
  lock_start_transaction "$1" t_file
  echo  "$2" >>  "$t_file";
  lock_commit_transaction "$1"

}


#---------------------------------------------------------------------
## @param spell
##
## Adds the given spell to the install queue and removes it from the
## remove queue.
##
#---------------------------------------------------------------------
function push_install_queue() {

  pop_queue   $REMOVE_QUEUE    "$1"
  pop_queue   $INSTALL_QUEUE   "$1"
  !  spell_installed           "$1"  &&
  push_queue  $INSTALL_QUEUE   "$1"

}


#---------------------------------------------------------------------
## @param spell
##
## Adds the given spell to the remove queue and removes it from the
## install queue.
##
#---------------------------------------------------------------------
function push_remove_queue() {

  pop_queue   $INSTALL_QUEUE  "$1"
  pop_queue   $REMOVE_QUEUE   "$1"
  spell_installed             "$1"  &&
  push_queue  $REMOVE_QUEUE   "$1"

}

#---------------------------------------------------------------------
##
## Sets some environment variables (such as C<CFLAGS>) based on the
## option passed.  Options may be on or more of:  i386, i486, i586,
## pentium, pentium-mmx, i686, pentiumpro, pentium2, pentium3,
## pentium4, k6, k6-2, k6-3, athlon, athlon-tbird, athlon-4,
## athlon-xp, athlon-mp, powerpc, speedy, tiny, risky, strip,
## combreloc
##
#---------------------------------------------------------------------
function optimize()  {
  debug "libsorcery" "In optimize()"

  unset  CFLAGS CXXFLAGS LDFLAGS CPPFLAGS
  unset  COMBRELOC FAST PRELINK RISKY SMALL SPEEDY STRIP TINY

  # if user specified --no-opts then use only the args from the command line
  # otherwise do the normal stuff
  if [[ $NO_OPTIMIZATION_FLAGS ]] ; then
    export CFLAGS="$OVERRIDE_CFLAGS"
    export CXXFLAGS="$OVERRIDE_CXXFLAGS"
    export LDFLAGS="$OVERRIDE_LDLAGS"
    export CPPFLAGS="$OVERRIDE_CPPFLAGS"
  else
    set_architecture
    debug "ARCHITECTURE='${ARCHITECTURE}'"
    debug "libsorcery" "TARGET='${TARGET}'"
    debug "libsorcery" "OPTIMIZATIONS='${OPTIMIZATIONS}'"
    BUILD=${HOST}

    debug "libsorcery" "BUILD is $BUILD, HOST is $HOST"

    CFLAGS="$CFLAGS -pipe"
    for  PARAM  in  $OPTIMIZATIONS;  do
      case  $PARAM  in
        combreloc)
          LDFLAGS="$LDFLAGS -z combreloc"
          COMBRELOC="on"
          ;;

        prelink)
          CFLAGS="$CFLAGS -DPIC -fPIC"
          PRELINK="on"
          ;;

        risky)
          CFLAGS="$CFLAGS -ffast-math -funroll-loops"
          RISKY="on"
          ;;

        speedy)
          CFLAGS="$CFLAGS $FAST"
          SPEEDY="on"
          ;;

        strip)
          LDFLAGS="$LDFLAGS -s"
          STRIP="on"
          ;;

        tiny)
          CFLAGS="$CFLAGS $SMALL"
          TINY="on"
          ;;

      esac
    done
    # use echo to crunch all the whitespace out for broken configure scripts
    export CXXFLAGS=$(echo $CFLAGS $CUSTOM_CXXFLAGS $OVERRIDE_CXXFLAGS $USER_SPELL_CXXFLAGS)
    export CFLAGS=$(echo $CFLAGS $CUSTOM_CFLAGS $OVERRIDE_CFLAGS $USER_SPELL_CFLAGS)
    export LDFLAGS=$(echo $LDFLAGS $CUSTOM_LDFLAGS $OVERRIDE_LDLAGS $USER_SPELL_LDFLAGS)
    export CPPFLAGS=$(echo $CPPFLAGS $CUSTOM_CPPFLAGS $OVERRIDE_CPPFLAGS $USER_SPELL_CPPFLAGS)
  fi

}


#---------------------------------------------------------------------
## @Stdout install queue
##
## List files in install queue, give user chance to modify it.
##
#---------------------------------------------------------------------
function list_install_queue() {
  if  [  -f  $INSTALL_QUEUE  ];  then
    lock_file $INSTALL_QUEUE

    message -n "The following spells will be updated:"
    message "${SPELL_COLOR}"
    cat  $INSTALL_QUEUE | column
    message "${DEFAULT_COLOR}"

    if    query  "Do you wish to edit ${FILE_COLOR} $INSTALL_QUEUE ${DEFAULT_COLOR}?" n
    then  edit_file  $INSTALL_QUEUE
    fi

    if  [  -n  "`cat  $INSTALL_QUEUE`"  ];  then
      SPELLS=`cat $INSTALL_QUEUE`
    fi

    unlock_file $INSTALL_QUEUE
  else
    message "${MESSAGE_COLOR}No spells listed in queue.${DEFAULT_COLOR}"
  fi
}

#---------------------------------------------------------------------
## Frontend routine to encapsulate whether or not a spell needs updating
##
#---------------------------------------------------------------------
function does_spell_need_update() {
  local line curr_updated curr_version status
  local spell=$1
  # this is in a subshell because the caller may not want to actually load
  # the spell yet
  (
    line=$(search_spell_status $SPELL_STATUS $spell)
    codex_set_current_spell_by_name  $spell
    explode "$line" ":" "info"
    does_spell_need_update_sub "$spell" "${info[3]}" "${info[1]}" /dev/null
  )
}

#---------------------------------------------------------------------
##
## common code between update_install_queue and does_spell_need_update
## This helps keep update_install_queue optimized.
#---------------------------------------------------------------------
function does_spell_need_update_sub() {
  local spell=$1
  local curr_version=$2
  local curr_updated=$3
  local tmp_queue=$4
  if  [[ $VERSION != $curr_version ]] ; then
    echo $spell >> $tmp_queue
    return 0
  else
    curr_patchlevel=0
    curr_sec_patch=0
    if tablet_find_spell_dir $spell page_dir ; then
      curr_updated=0
      tablet_get_updated $page_dir curr_updated
      tablet_get_patchlevel $page_dir curr_patchlevel
      tablet_get_security_patch $page_dir curr_sec_patch
    fi
    if (( "${PATCHLEVEL:-0}" >  "$curr_patchlevel" )) ||
       # the following is subject to change
       (( "${SECURITY_PATCH:-0}" >  "$curr_sec_patch" )) ||
       (( "${UPDATED:-0}" >  "${curr_updated:-0}" )) ; then
      echo $spell >> $tmp_queue
      return 0
    fi
  fi
  return 1
}


#---------------------------------------------------------------------
##
## Checks all installed spells for newer versions, and creates an
## install queue.
##
#---------------------------------------------------------------------
function update_install_queue()  {
  local line spell curr_version curr_updated page_dir info curr_patchlevel
  local curr_sec_patch count size
  local tmp_queue=$TMP_DIR/install_queue
  touch $tmp_queue

  message  "${CHECK_COLOR}Generating list of spells to update... ${DEFAULT_COLOR} "
  count=0
  size=$(all_spell_status | wc -l)
  for  line  in  `all_spell_status`;  do
    progress_bar $count $size 50
    #0=spell, 1=date, 2=status, 3=version
    explode "$line" ":" "info"
    spell=${info[0]}
    curr_updated=${info[1]}
    status=${info[2]}
    curr_version=${info[3]}

    ( # do this in a subshell in case a spell mis-behaves and exits we
      # still want queueing to work
      if  [[  "$status"  ==  "installed"  ]] &&
        codex_set_current_spell_by_name  $spell;  then
        does_spell_need_update_sub "$spell" "$curr_version" \
                                   "$curr_updated" "$tmp_queue"
      fi
    )
  let count++
  done
  clear_line
  lock_file $INSTALL_QUEUE
  rm  -f  $INSTALL_QUEUE
  sort $tmp_queue | uniq > $INSTALL_QUEUE
  unlock_file $INSTALL_QUEUE

}

function update_security_install_queue()  {
  local line spell page_dir info curr_sec_patch=0
  local tmp_queue=$TMP_DIR/install_queue
  touch $tmp_queue

  message  "${CHECK_COLOR}Generating list of spells to update for" \
           "security reasons... ${DEFAULT_COLOR} "
  for  line  in  `all_spell_status`;  do
    #0=spell, 1=date, 2=status, 3=version
    explode "$line" ":" "info"
    spell=${info[0]}
    status=${info[2]}

    ( # do this in a subshell in case a spell mis-behaves and exits we
      # still want queueing to work
      if  [[  "$status"  ==  "installed"  ]] &&
          codex_set_current_spell_by_name  $spell &&
          tablet_find_spell_dir $spell page_dir ; then
        tablet_get_security_patch $page_dir curr_sec_patch &&
        if (( "${SECURITY_PATCH:-0}" >  "$curr_sec_patch" )) ; then
          echo $spell >> $tmp_queue
        fi
      fi
    )
  done

  lock_file $INSTALL_QUEUE
  rm  -f  $INSTALL_QUEUE
  sort $tmp_queue | uniq > $INSTALL_QUEUE
  unlock_file $INSTALL_QUEUE

}

#---------------------------------------------------------------------
##
## Builds all spells in the install queue.
##
#---------------------------------------------------------------------
function build_install_queue()  {

  if  [  -f  $INSTALL_QUEUE  ];  then
    lock_file $INSTALL_QUEUE

    message  "The following spells will be updated :"
    cat  $INSTALL_QUEUE

    unset  SPELL
    if    query  "Do you wish to edit ${FILE_COLOR}$INSTALL_QUEUE${DEFAULT_COLOR}?" n
    then  edit_file  $INSTALL_QUEUE
    fi

    if  [  -n  "`cat  $INSTALL_QUEUE`"  ];  then
      cast      `cat  $INSTALL_QUEUE`
      rm              $INSTALL_QUEUE
    fi

    unlock_file $INSTALL_QUEUE
  else
    message  "No spells will be updated."
  fi
}


#---------------------------------------------------------------------
## @Stdout history
##
## Display the history of the install queue for review
##
#---------------------------------------------------------------------
function install_queue_history() {
  local spell
  for spell in `cat $INSTALL_QUEUE`; do
    lock_file "$SPELL_STATUS"
    local date="`grep "^${spell}:" $SPELL_STATUS | cut -d':' -f2`"
    unlock_file "$SPELL_STATUS"
    local datedash="`echo "$date" | sed 's,^\(....\)\(..\)\(..\)$,\1-\2-\3,'`"

    get_new_changes "Viewing history since last update ($datedash) for spell --  ${spell}  -- :" "$date" "$(codex_find_spell_by_name $spell)/HISTORY"

    if query "Would you like to ${RED}remove ${QUERY_COLOR}spell $SPELL_COLOR$spell$QUERY_COLOR from the install queue?" n; then
      local t_queue
      lock_start_transaction "$INSTALL_QUEUE" t_queue
      sedit "s/^$spell$//" "$t_queue"
      lock_commit_transaction "$INSTALL_QUEUE"
      echo -e "${QUERY_COLOR} The spell ${SPELL_COLOR} $spell ${QUERY_COLOR} removed from $INSTALL_QUEUE.${DEFAULT_COLOR}"
    fi
  done
}

#---------------------------------------------------------------------
## @param grimoire-path
## @Stdout history
## Display the history of the grimoire given for review
#---------------------------------------------------------------------
function grimoire_history() {

  local grimoire="$1"
  if [ -e $grimoire/ChangeLog ] ; then
    local grimoirename="${grimoire##*/}"
    local date="`cat "$STATE_DIRECTORY/$grimoirename.lastupdate" 2> /dev/null`"
    [ -z "$date" ] && date="20030818"
    local datedash=$(echo "$date" | sed 's,^\(....\)\(..\)\(..\)$,\1-\2-\3,')
    get_new_changes "Viewing history since last update ($datedash) for grimoire --  ${grimoirename}  -- :" "$date" "$grimoire/ChangeLog"
  else
    echo  "No ChangeLog in the $grimoire Grimoire, skipping viewing..."
    echo  ""
  fi
  date +%Y%m%d > "$STATE_DIRECTORY/$grimoirename.lastupdate"
}


#---------------------------------------------------------------------
## @Stdout history
## Display the history of sorcery for review
##
#---------------------------------------------------------------------
function sorcery_history() {
  local date="`cat "$STATE_DIRECTORY/sorcery.lastupdate" 2> /dev/null`"
  [ -z "$date" ] && date="20030818"
  local datedash="`echo "$date" | sed 's,^\(....\)\(..\)\(..\)$,\1-\2-\3,'`"

  get_new_changes "Viewing history since last update ($datedash) for sorcery :"  "$date" /usr/share/doc/sorcery/ChangeLog

  date +%Y%m%d > "$STATE_DIRECTORY/sorcery.lastupdate"
}

#---------------------------------------------------------------------
##@param old date
##@param changelog
## Get the new changes from passed changelog argument
##
#---------------------------------------------------------------------
function get_new_changes() {
    local msg="$1"
    local date="$2"
    # This needs some explaination:
    # The first item in {}'s extracts the date, assuming the format yyyy-mm-dd
    # then checks if its less than the last-update date ($date). The second
    # part ensures that we're actually looking at a date and not a line
    # of changelog. If both those are true, set 'd' to 1, otherwise d is '0'.
    #
    # In other words, d is 0 until it encounters a starting changelog entry
    # from before the given $date. Then d is 1 for each starting entry. Its
    # always 0 for bulleted changelog lines and blank lines.
    #
    # The second portion begins "NR==1,d". This is special awk syntax
    # that says to run the stuff to the right in {}'s starting once the
    # first statement is true, up through when the second one is true.
    # For example, NR==1,NR==8 would run the right-hand block for lines 1
    # through 8.
    #
    # NR is the line number, so its true on the first line. d is true once the
    # the dates are from before the last update. The if(!d) is necessary
    # to skip the first line where the changelog date is less than $date.
    #
    local changelog="$( awk -v date="$date" '{ d = (strtonum(substr($0,1,4) substr($0,6,2) substr($0,9,2)) < date && strtonum(substr($0,1,4)) > 0); }; NR == 1, d { if (!d) print }' "$3" )"

    if [[ "$changelog" ]] ; then
      {
        echo "$msg"
        echo
        echo "$changelog"
      }  > $TMP_DIR/pagerdata
      timeout_pager "$TMP_DIR/pagerdata"
      rm $TMP_DIR/pagerdata
    else
      echo "$msg"
      echo
      echo "Nothing new."
    fi
}


#---------------------------------------------------------------------
## Pager with timeouts, cannot be used from a pipe.
## Invokes a subshell in case there are other background jobs running
#---------------------------------------------------------------------
function timeout_pager() { (
  $PAGER "$@" &
  sleep 1
  for ((i=0;i<PAGER_TIMEOUT*10;i++)) ; do
    jobs &>/dev/null # this makes bash clean up so the next line works
    [[ "$(jobs)" ]] || return 0
    sleep .1
  done
  jobs -p|xargs kill
  jobs &>/dev/null # hide the [Done [2] blah blah] message
  stty sane
  if query "\nPager timeout, continue viewing $@" n; then
    $PAGER "$@"
  fi
) 2>/dev/null # hides less complaining that it was killed
}

#---------------------------------------------------------------------
##
## Attempt to fix any spells that may be broken.
##
#---------------------------------------------------------------------
function fix_installed_spells()  {

  local ANSWER=

  [  "$AUTOFIX"  ==  "on"  ]  &&  ANSWER=y  ||  ANSWER=n
  if  query  "Attempt to fix spells that may have become broken ?" $ANSWER;  then
    cleanse --fix;
  fi

  if  [  "$AUTOPRUNE"  ==  "on"  ];  then  prune;  fi
}

#---------------------------------------------------------------------
## @Stdout log files list
## Returns a list of the log files for each spell.
##
#---------------------------------------------------------------------
function log_list()  {
  local spell version
  while read spell version; do
    # the install and md5sum logs are not compressed
    echo "$INSTALL_LOGS/$spell-$version"
    echo "$MD5SUM_LOGS/$spell-$version"
    # the compile and castfs log usually are compressed
    # FIXME $EXTENSION may be the wrong one if the user changed it
    echo "$COMPILE_LOGS/$spell-$version$EXTENSION"
    echo "$COMPILE_LOGS/$spell-$version.castfs.dbglog$EXTENSION"
  done < <(all_spell_status | cut -d: -f1,4 --output-delimiter=" ")
}


#---------------------------------------------------------------------
##
## Removes stale logs.
##
#---------------------------------------------------------------------
function clean_logs()  {
  debug  "libsorcery" "Running clean_logs()"
  local file

  message "${CHECK_COLOR}Cleaning log files...$DEFAULT_COLOR"

  {
    # don't try to delete logs of installed spells, as they may not exist
    # tee duplicates the output, so uniq will filter them out
    log_list | sed p
    find $INSTALL_LOGS $MD5SUM_LOGS $COMPILE_LOGS -type f
  } | sort | uniq -u |
  while read file; do
    message "Removing stale log: $file"
    rm "$file"
  done
}

#---------------------------------------------------------------------
##
## Updates the activity log.
##
#---------------------------------------------------------------------
function activity_log()  { (

  lock_file $ACTIVITY_LOG

     DATE=`date  -u  +%Y%m%d:%H%M\(%z\)`
  COMMAND=$1
    SPELL=$2
  VERSION=$3
  OUTCOME=$4
     INFO=$5

  echo  -e "$DATE\t$COMMAND\t$SPELL\t$VERSION\t$OUTCOME\t$INFO"  >> $ACTIVITY_LOG
  unlock_file $ACTIVITY_LOG
) }


#---------------------------------------------------------------------
##
## Executes the spell's DETAILS file.
##
#---------------------------------------------------------------------
function run_details() {
  debug "libsorcery" "starting run_details for $SPELL"
  if  !  codex_set_current_spell_by_name  $SPELL;  then
    local  GRIMOIRES=`codex_get_all_grimoires`
    message  "${PROBLEM_COLOR}Unable to find spell"        \
             "${SPELL_COLOR}${SPELL}${PROBLEM_COLOR}"      \
             "in grimoire(s): $GRIMOIRES ${DEFAULT_COLOR}"
    false
  fi
}

#---------------------------------------------------------------------
##
## unsets variables set by DETAILS. <br>
## vars: SPELL VERSION SOURCE_DIRECTORY WEB_SITE UPDATED ENTERED SHORT SOURCE*
##
## Needs to be merged with the libcodex function since they are both
## needed to fully unset a DETAILS (afk 4/21/04)
##
#---------------------------------------------------------------------
function unset_details()  {

  local TEMP=""
  local VARS="SPELL VERSION SOURCE_DIRECTORY WEB_SITE UPDATED ENTERED SHORT"
  VARS="$VARS SECURITY_PATCH PATCHLEVEL"

  # Take care or SOURCEx and SOURCEx_URL
  for i in ${!SOURCE*} ; do
    TEMP=${TEMP}${i}\\n
  done
  VARS="$VARS `echo -e $TEMP | egrep "^SOURCE[[:digit:]]*(_URL)?$"`"
  unset TEMP

  for i in $VARS ; do
    unset $i
  done

}




#---------------------------------------------------------------------
##
## Runs C<track> and C<archive> on the current spell.
##
## THIS FUNCTION IS DEPRECIATED AND SHOULD NOT BE CALLED FOR ANY REASON
##
#---------------------------------------------------------------------
function boost() {

  debug  "libsorcery" "Running boost() on $SPELL THIS IS DEPRECIATED"
  echo "Running boost() on $SPELL THIS IS DEPRECIATED, please contact the spell's maintainer and ask them to remove this call"
  true
}


#---------------------------------------------------------------------
##
## Given a list of source files, returns true if each file exists.
##
#---------------------------------------------------------------------
function verify_source()  { (


  for  SOURCE_FILE  in  $@;  do
    if  !  guess_filename  $SOURCE_CACHE/$SOURCE_FILE  >/dev/null
    then
      message  "${PROBLEM_COLOR}Missing ${FILE_COLOR}${1}${DEFAULT_COLOR}"
      message  "${PROBLEM_COLOR}Cast aborting.${DEFAULT_COLOR}"
      activity_log  "cast"  "$SPELL"  "$VERSION"  "failure"  \
                    "because it was missing source:  $1"
      return 1
    fi
  done

  return 0

) }


#---------------------------------------------------------------------
## @param spell
## @Stdout filelist
## Returns a list of each source file used by a spell.  Most have only
## one, but xfree86 for example splits the sources into three separate
## source files.
##
#---------------------------------------------------------------------
function sources()  { (

  local i srcVar

  if  [  -z  "$SOURCE"  ];  then
    codex_set_current_spell_by_name  $1
  fi
  get_spell_files

) }

#---------------------------------------------------------------------
## @param spell
## @Stdout filelist
## Returns a list of each source file used by a spell.  Most have only
## one, but xfree86 for example splits the sources into three separate
## source files.
##
#---------------------------------------------------------------------
function source_urls()  { (

  local i srcVar

  if  [  -z  "$SOURCE"  ];  then
    codex_set_current_spell_by_name  $1
  fi
  get_spell_files_and_urls|while read -a line; do
    unset line[0]
    echo "${line[*]}"
  done


) }


#---------------------------------------------------------------------
## @Stdout filelist
## Returns a list of files that should not be pruned.
##
#---------------------------------------------------------------------
function generate_keep_list()  {
  local source_keep=$1
  local cache_keep=$2
  local type=$3
  if [[ $type == installed_only ]] ; then
    {
      get_all_spells_with_status installed
      get_all_spells_with_status held
    } | sort | uniq | while read spell ; do
      codex_find_spell_by_name $spell
    done
  else
    codex_get_all_spells
  fi |
  (
  echo sorcery-$SORCERY_BRANCH.tar.bz2
  while read SPELL_DIRECTORY ; do
    codex_set_current_spell_quick  $SPELL_DIRECTORY &>/dev/null
    sources 2>/dev/null
    ls $INSTALL_CACHE/$SPELL-$VERSION-* 1>&2 2>/dev/null
    unset VERSION SPELL ${!SOURCE*}
  done
  ) 2> $cache_keep > $source_keep

  #Add the README in the cache to list of files to keep
  echo  README >> $cache_keep

}


#---------------------------------------------------------------------
##
## Removes unnecessary files from the source cache and install cache.
##
#---------------------------------------------------------------------
function prune()  {

  debug  "libsorcery" "Running prune()"
  local NO_KEEP
  local NUM_NO_KEEP=0

  message "${MESSAGE_COLOR}Generating list of files to keep...${DEFAULT_COLOR}"
  local SOURCE_KEEP="$TMP_DIR/prune-keep-source"
  local CACHE_KEEP="$TMP_DIR/prune-keep-cache"
  rm -rf $CACHE_KEEP
  rm -rf $SOURCE_KEEP
  generate_keep_list  $SOURCE_KEEP $CACHE_KEEP $1

  message "${MESSAGE_COLOR}Cleaning up source cache" \
          "(${DEFAULT_COLOR}$SOURCE_CACHE${MESSAGE_COLOR})...${DEFAULT_COLOR}"
  local SOURCE_RM="$TMP_DIR/prune.rm.source"
  find $SOURCE_CACHE -follow -mindepth 1 -maxdepth 1 -type f|
  awk -F/ 'BEGIN {
             while ( getline < ARGV[1] ) {
               keep[$NF] = 1;
             }
             while ( getline < ARGV[2] ) {
               if ( keep[$NF] != 1 ) {
                 print $0;
               }
             }
           }' $SOURCE_KEEP - |sort|uniq > $SOURCE_RM
  if test -s $SOURCE_RM ; then
    NUM_NO_KEEP=$(cat $SOURCE_RM|wc -l)

    message "There are $NUM_NO_KEEP files that can be removed"
    if query "Edit the list?" "n" ; then
      edit_file $SOURCE_RM
    fi
    if query "Remove the files?" "y" ; then
      cat $SOURCE_RM|xargs rm
    fi
  fi

  message "${MESSAGE_COLOR}Cleaning up install cache" \
          "(${DEFAULT_COLOR}$INSTALL_CACHE${MESSAGE_COLOR})...${DEFAULT_COLOR}"
  local CACHE_RM="$TMP_DIR/prune.rm.cache"
  find $INSTALL_CACHE -mindepth 1 -maxdepth 1 -type f|
  awk -F/ 'BEGIN {
             while ( getline < ARGV[1] ) {
               keep[$NF] = 1;
             }
             while ( getline < ARGV[2] ) {
               if ( keep[$NF] != 1 ) {
                 print $0;
               }
             }
           }' $CACHE_KEEP - |sort|uniq > $CACHE_RM
  if test -s $CACHE_RM ; then
    NUM_NO_KEEP=$( cat $CACHE_RM | wc -l )
    message "There are $NUM_NO_KEEP files that can be removed"
    if query "Edit the list?" "n" ; then
      edit_file $CACHE_RM
    fi
    if query "Remove the files?" "y" ; then
      cat $CACHE_RM|xargs rm
    fi
  fi

  rm  -f  $SOURCE_KEEP $CACHE_KEEP $SOURCE_RM $CACHE_RM

}


#---------------------------------------------------------------------
##
## Sets DISTCC=[on|off] depending on the value of DISTCC_HOSTS.
## Adds /var/lib/sorcery/build to the C<PATH> if necessary.
##
#---------------------------------------------------------------------
function invoke_build_dir() {

  # This merits some explaination:
  #
  # In the /var/lib/sorcery/build dir are frontends for the compilers
  # they run CCACHE and DISTCC. By putting them in the front of the path
  # they get run first, then hand the work off to the real compilers.
  # The script also loads /etc/sorcery/compile_config, this made it
  # convenient for building stuff outside of sorcery and taking advantage
  # of ccache/distcc, just do "export  PATH="/var/lib/sorcery/build:$PATH""
  # and you're set, you can also edit the DISTCC hosts on the fly.
  #
  # Then someone had the bright idea "if we set DISTCC_HOSTS to the empty
  # string, distcc wont run!" due to a misconception in how this function
  # worked, that spell hack would only work /some/ of the time: if you
  # used ccache in addition to distcc, distcc wouldnt get disabled. *Sigh*
  #
  # SO...now we're stuck with a bunch of spells in the field disabling
  # distcc in an broken way, obviously they all cant change overnight. To
  # make matters worse inside make() we have to fiddle with the number of
  # make jobs to use in case distcc is disabled (badly).
  #
  # so the answer is:
  # 1) make a new variable "USE_DISTCC" and set it to "off" if distcc
  # is being disabled by the spell
  # 2) check if USE_DISTCC == off in run_compiler, but still check the
  # other signs just in case its being run manually
  # 3) add a check for DISABLE_DISTCC so spells can start using that someday
  #   (phase this in after a few months, then phase out the old check below,
  #   but not in run_compiler)
  # 4) remove the old stupid logic for MAKE_NJOBS (num_hosts + 1),
  # instead split MAKE_NJOBS up, add a JOBS_PER_HOST multiplier, and add
  # MAKE_NJOBS to that. This results in the number of jobs being $MAKE_NJOBS
  # (default 1) when distcc is disabled (bug 6432), and allows an automatic
  # increase in the number of jobs per host if its desired.
  #

  USE_DISTCC=off
  if [[ $DISABLE_DISTCC != yes ]] ; then
    if test -n "$DISTCC_HOSTS" && spell_ok distcc ; then
      USE_DISTCC=on
    fi
  fi

  if ! spell_ok ccache ; then
    CCACHE=off
  fi

  if [[ "$CCACHE" == on ]] || [[ "$USE_DISTCC" == on ]] ; then
    export  PATH="$RUN_COMPILER_DIR:$PATH"
  fi
}


#---------------------------------------------------------------------
## @param category/service
## @Stdout spelllist
##
## First argument is a category of spell.  Returns a list of spells
## that match that category.  For example,
## C<find_providers email-client> returns evolution,althea,mutt,etc.
##
## This will list spells exactly once, exiled spells are not listed.
## This also takes into account grimoire ordering. A spell must provide
## the category in the first grimoire the spell is seen in (because that
## is the only one that will be cast) otherwise it is not listed.
##
## Note to self: move to libcodex
##
#---------------------------------------------------------------------
function find_providers()  {
  local feature="$1"
  local provider GRIMOIRE spell_path
  local providers=$(
    for GRIMOIRE in $(codex_get_all_grimoires);  do
      gawk '/^'"$feature"'[[:blank:]]/ { print $2 }' \
        "$GRIMOIRE/$PROVIDE_INDEX_FILE"
    done | get_basenames|sort|uniq)

  # filter out providers we dont want, so far this is:
  # a) do not provide something in the first grimoire they are found in
  # b) are exiled.
  # ex: foo provides FOOBAR in the "stable" grimoire but not the "test"
  # grimoire and test is searched before stable in this case cast will
  # use the spell from test which does NOT provide FOOBAR, therefore
  # we should not list foo as a provider of FOOBAR
  for provider in $providers; do
    if ! spell_exiled $provider; then
      spell_path=$(codex_find_spell_by_name $provider)
      GRIMOIRE=$(dirname $( dirname $spell_path))
      # ensure the first place we look for the spell is a provider
      grep -q "^$feature $spell_path$" $GRIMOIRE/$PROVIDE_INDEX_FILE &&
        echo $provider
    fi
  done

}


#---------------------------------------------------------------------
## @Stdout filelist
## Returns false if one or more source files for the current spell
## are missing.
##
#---------------------------------------------------------------------
function verify_sources()  {
  verify_source  "`sources  $SPELL`";
}



#---------------------------------------------------------------------
##
## Will set the prefered compression type based on user filled option
## in dialog menu (either gzip/bzip).
##
#---------------------------------------------------------------------
function set_compression_type() {

  local B_HELP="bzip2 compression (slow, small files)"
  local G_HELP="gzip compression (fast, larger files)"
  local T_HELP="no compression/tar (fastest, largest files)"

  while

    COMMAND=`eval $DIALOG '  --title "Currently using : $COMPRESSBIN "  \
                             --item-help                                \
                             --ok-label      "Select"                   \
                             --cancel-label  "Exit"                     \
                             --menu                                     \
                             ""                                         \
                             0 0 0                                      \
                             "B"  "bzip2 compression"        "$B_HELP"  \
                             "G"  "gzip compression"         "$G_HELP"  \
                             "T"  "no compression/tar"       "$T_HELP"'`

  do

    case  $COMMAND in

      B)  modify_local_config "COMPRESSBIN" "bzip2"  &&
          COMPRESSBIN="bzip2"                        &&
          modify_local_config "EXTENSION" ".bz2"     ;;
      G)  modify_local_config "COMPRESSBIN" "gzip"   &&
          COMPRESSBIN="gzip"                         &&
          modify_local_config "EXTENSION" ".gz"      ;;
      T)  modify_local_config "COMPRESSBIN" "tar"    &&
          COMPRESSBIN="tar"                          &&
          modify_local_config "EXTENSION" ""         ;;

    esac

  done

}

#---------------------------------------------------------------------
## @param filename
## @return 0 if filename seems to be compressed
## @return 1 otherwise
## Returns true if the name of the file indicates that it should be
## a compressed file.  In other words, this function returns true if
## the filename ends in .gz, .tgz, .bz2, .zip, .rpm, or .Z.
##
#---------------------------------------------------------------------
function filename_indicates_compression()  {
  local FILENAME=`echo "file$1" | sed 's/^.*\.gz$//'  \
                                | sed 's/^.*\.tgz$//' \
                                | sed 's/^.*\.bz2$//' \
                                | sed 's/^.*\.zip$//' \
                                | sed 's/^.*\.Z$//'   \
                                | sed 's/^.*\.rpm$//' \
                                | sed 's/^.*\.bz2$//'`
  [  -z  "$FILENAME"  ]
}


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