#!/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:

-q  |  --quiet			Do not be verbose, show less info
-t  |  --dot                    Display progress as dots, not percent
-V [on|off]			Override \$VOYEUR setting

-f  |  --fix			Discover and fix broken spells
-n  |  --nofix			Discover, but do not fix broken spells
				(Both deprecated, use cleanse instead)

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

-s				Download all given spells before compiling
       --deps			Configure spells and determine dependencies,
				but do not cast.

-c  |  --compile		Ignore $INSTALL_CACHE and compiles
-r  |  --reconfigure		Select new dependencies for spells

-R  |  --recast-down		Recast depended-upon spells, even if they are
                                already installed...
-B  |  --recast-up		Recast dependent spells, even if they are
                                already installed...
				
       --from	directory	Specify an alternate for $SOURCE_CACHE
       --url	URL		Specify an alternate download URL

       --pam			Re-casts all installed spells that
				can use Linux-PAM

       --queue                  Casts all spells listed in $INSTALL_QUEUE

EOF

  exit  1

}

#-----
## Finds all dirs that have shard objects in
## @Args directories to check for shared objects
## @Stdout dirs with shared objects in
#-----
function lib_dirs()  {

  for  DIRECTORY  in  $*;  do
    for FILE in $DIRECTORY/*.so* ; do
      if file -b "$FILE" | grep  -q  "shared object" ; then
        echo -n "$DIRECTORY:"
	break
      fi
    done
  done

}

#-----
## Set the LD path to include all dirs with so's.
## @Args Directories to potentialy add
#-----
function export_ld()  {

  if    [  -z  "$LD_LIBRARY_PATH"  ] ; 
  then  export   LD_LIBRARY_PATH="$( lib_dirs  \"$@\"  )"
  else  export   LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$(  lib_dirs  \"$@\"  )"
  fi
  
  export LD_LIBRARY_PATH=`cat /etc/ld.so.conf | tr '[:blank:][:cntrl:]' ':'`$LD_LIBRARY_PATH

}

#-----
## Do the ldd check for cast --fix
## @Deprecated Use cleanse
#-----
function ldd_check()  {  (

  if  [  "$LDD_CHECK"  ==  "off"    ] ;  then
    return
  fi

     SPELL=$1
     VERSION=`installed_version  "$SPELL"`
       I_LOG=$INSTALL_LOGS/$SPELL-$VERSION
  LDD_STATUS=0

  if  [  -e  $I_LOG  ];  then

    LOG=`cat   $I_LOG                               |
         grep      "/bin/\|/games/\|/lib/\|/sbin/"  |
         grep  -v  "/doc/\|/fonts/\|/include/\|/locale/\|/man/\|/modules/\|/var/"`
    
    let size=`grep -c '' <<<"$LOG"`

    let count=0
    echo "$LOG" | while read FILE ; do

      if  [  -z  "$QUIET"  ];  then
        progress_bar $DOT_PROGRESS $count $size 50
      fi
      let count++

      if     [  -f  "$FILE"  ]     &&
          !  [  -h  "$FILE"  ]     &&
          file  -b  "$FILE"        |
          grep  -q  "ELF"          &&
          ldd       "$FILE"  2>&1  |
          grep  -q  "not found"
      then
        (
	   clear_line
           message  "${FILE_COLOR}${FILE}"       \
                    "${DEFAULT_COLOR}of"         \
                    "${SPELL_COLOR}${SPELL}"     \
                    "${PROBLEM_COLOR}is broken."  \
                    "${DEFAULT_COLOR}"
           ldd      "$FILE" 2>&1 | grep  "not found"
        )
        LDD_STATUS=1
      fi
    done
	clear_line
  fi
  return  $LDD_STATUS

) }

#-----
## Do the find check for cast --fix
## @Deprecated Use cleanse
#-----
function find_check()  { (

  if  [  "$FIND_CHECK"  ==  "off"             ]   ||
      [  "$1"           ==  "xfree86"         ]   ||
      [  "$1"           ==  "xfree86-devel"         ]   ||
      [  "$1"           ==  "xfree86-custom"  ];  then
    return
  fi

        SPELL=$1
      VERSION=`installed_version  $SPELL`
        I_LOG=$INSTALL_LOGS/$SPELL-$VERSION
  FIND_STATUS=0

  if  [  -e  $I_LOG  ];  then

    LOG=`cat   $I_LOG                                          |
         grep      "/bin/\|/games/\|/include/\|/lib/\|/sbin/"  |
         grep  -v  "/doc/\|/etc/\|/fonts/\|/man/\|/var/"`

    let size=`grep -c '' <<<"$LOG"`

    let count=0
    echo "$LOG" | while read ITEM;  do

      if  [  -z  "$QUIET"  ];  then
        progress_bar $DOT_PROGRESS $count $size 50
      fi
      let count++
      
      
		if  [ -z "$ITEM" ] || [ -e  "$ITEM"  ];  then
        true
      else
        (
	   clear_line
           message  "${FILE_COLOR}${ITEM}"       \
                    "${DEFAULT_COLOR}of"           \
                    "${SPELL_COLOR}${SPELL}"       \
                    "${PROBLEM_COLOR}is missing."  \
                    "${DEFAULT_COLOR}"
        )
        FIND_STATUS=1
      fi
    done
	clear_line
  else
    (
       message  "${SPELL_COLOR}${SPELL} "                     \
                "${PROBLEM_COLOR}is missing an install log."  \
                "${DEFAULT_COLOR}"
    )
    FIND_STATUS=1
  fi
  return  $FIND_STATUS

) }


function show_owner()  {

  OWNER=`grep  "$TARGET"  $INSTALL_LOGS/*  |
         cut  -d :  -f1                  |
         sed  -n  1p`

  if  [  -n  "$OWNER"  ];  then  echo  "`basename  $OWNER`"
                           else  echo  "nobody"
  fi

}


function remove_line()  {

  F_TMP=/tmp/`basename  $I_LOG`.$$.$RANDOM

  cp         "$I_LOG"  "$F_TMP"
  grep  -v   "$ITEM"   "$F_TMP"  >  "$I_LOG"
  rm    -rf          "$F_TMP"

  message  "Symbolic link: ${SYMLINK_COLOR}${ITEM}${DEFAULT_COLOR}"  \
           "is owned by ${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}"

  message  "Target of symbolic link is ${FILE_COLOR}${TARGET}${DEFAULT_COLOR}"

  message  "${FILE_COLOR}${TARGET}${DEFAULT_COLOR} is owned by `show_owner`"

  message  "Removed: ${SYMLINK_COLOR}${ITEM}${DEFAULT_COLOR} from ${I_LOG}"

}

#-----
## Do the symlink check for cast --fix
## @Deprecated Use cleanse
#-----
function sym_check()  { (

  if  [  "$SYM_CHECK"  ==  "off"  ];  then
    return
  fi

      SPELL=$1
      VERSION=`installed_version  $SPELL`
        I_LOG=$INSTALL_LOGS/$SPELL-$VERSION

  if  [  -e  $I_LOG  ];  then

    LOG=`cat   $I_LOG`

    let size=`grep -c '' <<<"$LOG"`

    let count=0
    echo "$LOG" | while read ITEM;  do

      if  [  -z  "$QUIET"  ];  then
        progress_bar $DOT_PROGRESS $count $size 50 
      fi
      let count++
      
      if  [  -h  "$ITEM"  ]  &&
          [  -f  "$ITEM"  ]
      then

      TARGET=$(  basename  "$(  ls   -la  "$ITEM"  |
                               cut  -d  '>'  -f2  |
                               cut  -c  2-
                            )"
              )

        if  !  grep  -q  "$TARGET"  "$I_LOG"
        then  (  remove_line  )
        fi

      fi
    done
	clear_line
  fi

) }

#-----
## Do the md5sum check for cast --fix
## @Deprecated Use cleanse
#-----

function md5sum_check()  { (

  if  [  "$MD5SUM_CHECK"  ==  "off"  ];  then
    return
  fi

  SPELL=$1
  VERSION=`installed_version  $SPELL`
  MD5_LOG="$MD5SUM_LOGS/$SPELL-$VERSION"

  MD5SUM_STATUS=0

  if  [  -e  $MD5_LOG  ];  then

    OUTPUT=`cat  $MD5_LOG                                         |
            grep      "/bin/\|/games/\|/include/\|/lib/\|/sbin/"  |
            grep  -v  "/doc/\|/etc/\|/fonts/\|/man/\|/var/"       |
            md5sum  --check  2>/dev/null                          |    
            grep   -v       ": OK"                                |
            cut    -d :  -f1`

    if  [  -n  "$OUTPUT"  ];  then
    
      let size=`grep -c '' <<<"$LOG"`

      let count=0
      echo $OUTPUT | while read FILE;  do

        if  [  -z  "$QUIET"  ];  then
          progress_bar $DOT_PROGRESS $count $size 50 
        fi
        let count++
      
        if     [  -f  "$FILE"  ]  &&
            !  [  -h  "$FILE"  ]  &&
            file  -b  "$FILE"     |
            grep  -q  "ELF"
        then
          MD5SUM=`md5sum  "$FILE"`
          if  !  grep  -q  "$MD5SUM"  $MD5SUM_LOGS/*;  then
            (
	       clear_line
               message  "${FILE_COLOR}${FILE}"               \
                        "${DEFAULT_COLOR}of"                 \
                        "${SPELL_COLOR}${SPELL}"             \
                        "${PROBLEM_COLOR}has wrong md5sum."  \
                        "${DEFAULT_COLOR}"
            )
            MD5SUM_STATUS=1
          fi
        fi
      done
	  clear_line
    fi
  else
    (
       message  "${SPELL_COLOR}${SPELL} "                   \
                "${PROBLEM_COLOR}is missing a md5sum log."  \
                "${DEFAULT_COLOR}"
    )
    MD5SUM_STATUS=1
  fi
  return  $MD5SUM_STATUS

) }

#-----
## This function should be moved elsewhere. It's generaly handy.
## It should also be added to the API.
#-----
function status_okay()  {

  spell_installed  "$1"  ||
  spell_held       "$1"

}

#---------------------------------------------------------------------
## @param SPELL
## @Globals QUIET DOT_PROGRESS
## Runs all different sorts of checks.
## sym_check find_check ldd_check md5sum_check.
#---------------------------------------------------------------------
function run_checks()  {

  if  [ -z  "$QUIET"  ]; then
    progress_bar $DOT_PROGRESS 1 1 50 
  fi

     sym_check  "$1"  &&
    find_check  "$1"  &&
     ldd_check  "$1"  &&
  md5sum_check  "$1"

}


function run_fix()  { (
  message "${PROBLEM_COLOR}cast --fix is depreciated and will be" \
          "removed in the next sorcery releae.\nPlease use cleanse --fix" \
          "instead.${DEFAULT_COLOR}"

  SPELLS=$*

  if  [  -z  "$QUIET"  ];  then
    if  [  -z  "$PASS_ZERO"  ]  &&  [  -z  "$CAST_PASS"  ];  then
      message  "${CHECK_COLOR}Initiating integrity check, please wait..."  \
               "${DEFAULT_COLOR}"
    fi
  fi

  if  [  -z  "$SPELLS"  ];  then


    # Why are these special cases? (duff, 09/29)
    # Because we want to always check them first?
    if    spell_installed  xfree86
    then  SPELLS="glibc xfree86"
    elif  spell_installed xfree86-devel
    then  SPELLS="glibc xfree86-devel"
    else  SPELLS="glibc"
    fi

    #was $SPELL_STATUS_BACKUP (2002/09/29)
    SPELLS="$SPELLS $(  awk -F : '{print $1; }' $SPELL_STATUS |
                        grep  -v  'glibc|xfree86|xfree86-devel|sorcery|j2sdk'   )"
  fi

  if  [  -z  "$QUIET"  ];  then
    progress_bar $DOT_PROGRESS 1 5 50 
  fi

  PASSED=":"

  if    [  "$LDD_CHECK"  ==  "on"    ]
  then  LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH"
        if  [  -z  "$QUIET"  ];  then
         progress_bar $DOT_PROGRESS 2 5 50 
          wait_flag_up
          wait_flag  $$  &
        fi
        export_ld  $(  find  /usr/lib  -type  d  ! -empty  )
        if  [  -z  "$QUIET"  ];  then
          wait_flag_down
        fi
  fi

  until  [  "$FIXED"     ==  "yes"  ]   ||
         [  "$HOPELESS"  ==  "yes"  ];  do

       FIXED="yes"
    HOPELESS="no"

    if  [  -z  "$QUIET"  ];  then
      progress_bar $DOT_PROGRESS 3 5 50 
    fi

    for  SPELL  in  $SPELLS;  do

      if  [  -z  "$QUIET"  ];  then
        progress_bar $DOT_PROGRESS 4 5 50 
      fi

      if  status_okay  $SPELL  &&
          !  echo  $PASSED     |
             grep  -q  ":`esc_str $SPELL`:";  then

        if  [  -z  "$QUIET"  ];  then
          progress_bar $DOT_PROGRESS 5 5 50 
		  clear_line 
          message    "${CHECK_COLOR}Checking integrity of"  \
                     "${SPELL_COLOR}${SPELL}"               \
                     "${DEFAULT_COLOR}"
        else
          message  "${CHECK_COLOR}Checking integrity of"  \
                   "${SPELL_COLOR}${SPELL}"               \
                   "${DEFAULT_COLOR}"
        fi

        if    run_checks     $SPELL  ||
              [  -n  "$NOFIX"  ]
        then
          PASSED="$PASSED$SPELL:"
        else

          FIXED="no"
          (  run_details
             satisfy_depends
             unset  FIX
             satisfy_depends
          )

          if  [  "$LDD_CHECK"  ==  "on"    ] ;  then
            export  LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD"
            export_ld  $(  find  /usr/lib  -type  d  ! -empty  ) #This will have an issue with spaces
          fi

          if    run_checks     $SPELL  ||
                [  -n  "$NOFIX"  ]
          then

            PASSED="$PASSED$SPELL:"
            if  [  "$LDD_CHECK"  ==  "on"    ] ;  then
              export  LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD"
              export_ld  $(  find  /usr/lib  -type  d  ! -empty  )
            fi

          else
            unset  FIX
            if    !  cast  --compile  $SPELL;  then  HOPELESS="yes"
            elif  !  run_checks       $SPELL;  then  HOPELESS="yes"
            fi
            export  FIX="--fix"
          fi
        fi
      fi
    done
  done

) }

#---------------------------------------------------------------------
## @Aguments Arguments of cast
## @Globals QUIET DOT_PROGRESS DEPS_ONLY RECONFIGURE COMPILE 
## @Globals FORCE_DOWNLOAD SOURCE_CACHE SILENT FIX NOFIX SEPARATE
## @Globals BASE_URL INSTALL_QUEUE 
##
## Sets these globals except INSTALL_QUEUE according to cast's 
## arguments.
## Requires some user interaction if --queue is given and starts
## another cast with INSTALL_QUEUE's content as parameter.
#---------------------------------------------------------------------
function process_parameters()  {

  while  [  -n  "$1"  ];  do

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

      case  $1  in

          -q|--quiet)  QUIET="yes"                ;      shift 1  ;;
	    -t|--dot)  export DOT_PROGRESS='-dot' ;      shift 1  ;;
	          -V)  export VOYEUR_OVERRIDE="$2";	 shift 2  ;;
              --deps)  export  DEPS_ONLY="$1";           shift 1  ;;
    -r|--reconfigure)  RECONFIGURE="$1";                 shift 1  ;;
    -R|--recast-down)  RECAST_DOWN="$1";                 shift 1  ;;
      -B|--recast-up)  RECAST_UP="$1";                   shift 1  ;;
        -c|--compile)  COMPILE="$1";                     shift 1  ;;
       -d|--download)  export  FORCE_DOWNLOAD="$1";      shift 1  ;;
              --from)  export  SOURCE_CACHE=$2;          shift 2  ;;
            --silent)  SILENT="$1";                      shift 1  ;;
            -f|--fix)  export  FIX="$1";                 shift 1  ;;
          -n|--nofix)  export  FIX="--fix"
                       export  NOFIX="$1";               shift 1  ;;
                  -s)  SEPARATE="$1";                    shift 1  ;;
               --url)  export  BASE_URL="$2";            shift 2  ;;
               --pam)  cast  -c  `find_pam_aware` ;      shift 1  ;;
             --queue)  
                       if query "Would you like review the queue history for each spell?" n ; then
                         sorcery review-queue
                       fi

                       if [ -z $QUIET ]; then
                         list_install_queue
                       fi
                         
                       if  [  -s $INSTALL_QUEUE  ]; then
                         message -n "${MESSAGE_COLOR}Casting install queue..."
                         message    "${DEFAULT_COLOR}"

                         # remove possible empty lines from queue.
                         grep -Ev '^$' $INSTALL_QUEUE >"$INSTALL_QUEUE.new"
                         rm -f "$INSTALL_QUEUE"
                         mv "$INSTALL_QUEUE.new" "$INSTALL_QUEUE"

                         # cast the queue.
                         cast -c `cat $INSTALL_QUEUE 2>/dev/null`
												 
                         # report spells still in queue.
                         if  [  -s  $INSTALL_QUEUE  ] ; then
                           message "${MESSAGE_COLOR}The install queue is not empty, "
                           message "it still contains the following spells: "
                           message "---------------------------------${PROBLEM_COLOR}"
                           message "`cat $INSTALL_QUEUE 2>/dev/null`"
                           message "${DEFAULT_COLOR}"
                         fi
                       fi;                               
                       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()  {

  while  [  -n  "$1"  ];  do

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

      case  $1  in

                  --deps)  shift 1  ;;
	        -d|--dot)  shift 1  ;;
		      -V)  shift 2  ;;
        -r|--reconfigure)  shift 1  ;;
                  --from)  shift 2  ;;
                --silent)  shift 1  ;;
                -f|--fix)  shift 1  ;;
              -n|--nofix)  shift 1  ;;
            -c|--compile)  shift 1  ;;
                      -s)  shift 1  ;;
                   --url)  shift 2  ;;
                   --pam)  shift 1  ;;
		 --queue)  shift 1  ;;
                       *)  shift 1  ;;

      esac

    else

      echo  $1
      shift
 
   fi

  done

}

#---------------------------------------------------------------------
## Gets the cast lock for us and ensures that we've waited for all
## solo/non-solo casts to complete
#---------------------------------------------------------------------
function acquire_cast_lock() {
  # locking - blocks normal spells if a solo cast is running,
  # solo casts if any cast is running
  message "Waiting for any ${FILE_COLOR}Solo${DEFAULT_COLOR}" \
          "casts to complete..."
  lock_resources "solo" "cast"
  if grep -q "^$SPELL$" $SOLO || [ -f $SCRIPT_DIRECTORY/SOLO ]
  then #SOLO spell
    message -n "Waiting for ${SPELL_COLOR}all other${DEFAULT_COLOR}" \
               "spells to complete..."
    excllock_resources "cast" "$SPELL"
  else
    message -n "Waiting for any other casts of" \
               "${SPELL_COLOR}$SPELL${DEFAULT_COLOR} to complete..."
    lock_resources "cast" "$SPELL"
    unlock_resources "solo" "cast"
  fi
  message " done."
}


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

# these both prompt, and prompting should NOT occur at this point
# someday they will get split to a query and action pair (like use_xinetd
# and install_xinetd) (afk 4/10/04)
  run_security  &&
  run_conflicts || return 1

  show_downloading $SPELL
  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

  IW_LOG="/tmp/$SPELL.iw"

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

  # must declare OPS before sourcing config
  local OPTS
  run_spell_config                            

  # this should probably be moved...
  spell_depends=$(hash_get uncommitted_hash $SPELL)
  test -e $spell_depends && 
  OPTS="$OPTS $(get_depends_options $spell_depends $SPELL)"
  
  # this will run through the whole build process
  run_build_spell
  rc=$?

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

  pop_install_queue "$SPELL"    

  # 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 MAKEFILE DEPS_ONLY 
##
## Basically only gathers all dependencies and creates the Makefile
## (According to pass_zero it ought to take SPELL as parameter, but
## it doesn't)
#---------------------------------------------------------------------
function pass_one()  {
  #  This pass does configuration and dependency identification.
  debug "cast" "Starting pass_one()"

  local i j tempList SPELL_RULES=""
  local numRules=0

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

  # special case of no spell making it through dependency resolution
  SPELLS=$(hash_get_table_fields "to_cast")
  if [ -z "$SPELLS" ] ; 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 "---------------------------"
    message "${SPELL_COLOR}${cannot_cast}" | tr '[:blank:]' '\n' | column 
    message "${DEFAULT_COLOR}"
    exit 1
  fi

  message "${MESSAGE_COLOR}Collating dependencies...${DEFAULT_COLOR}"
  depends_to_Makefile to_cast already_cast > $MAKEFILE

  if  [  -n  "$DEPS_ONLY"  ] ;  then  
   	for i in $SPELLS ; do
	  #BUG: $i=="foo" and "foobar" is also to be built it will remove foobar too"
	  sed -e 's/cast .* $i.*/true/' $MAKEFILE > $MAKEFILE.2
	  mv $MAKEFILE.2 $MAKEFILE
   	done  
  fi

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

}

#-----
## Take the depends hashtable and turn it into the Makefile used later
## @param Dependencies of spells to cast
## @param Dependencies of spells not to cast
#-----
function depends_to_Makefile()
{
	debug "cast" "depends_to_Makefile - $*"
	
        # This is needed because compute_installed_depends may have
        # stale dependency information compared with the information
        # from compute_uninstalled_depends. So we strip off any depends
        # info from the installed depends hash.
	for i in $( { hash_get_table_fields $1 ; hash_get_table_fields $2; } | sort | uniq -d ); do
		hash_unset "$2" "$i"
	done
	
	[[ $COMPILE ]] && args="-c "
	
	# Spells that are actualy going to be cast
	for i in $( hash_get_table_fields $1 ) ; do
		echo "$i : $(hash_get "$1" "$i")"
		echo -e "\t@bash $0 $args $i"
		echo
	done
	
	#Spells that are already cast
	#NOTE: For recursive recompile cast, make this loop like loop above
	for i in $( hash_get_table_fields $2 ) ; do
		echo "$i : $(hash_get "$2" "$i")"
		echo
	done
	local all_spells=$({ hash_get_table_fields $1 ; hash_get_table_fields $2; } | tr '[:cntrl:]' ' ')
        # make will run the all target, which would be the spells we care about
	echo "all : $all_spells"
        echo
        # if dependency rules fail us, match here after everything else
	# has had the chance, and do nothing, instead of exit early...
        echo %:
	echo -e "\t@echo -n"
	echo
        # this caused a rather nasty bug where a file in the current directory
	# had the same name as a spell, and make thought it had already "made"
	# that spell
	echo ".PHONY : all $all_spells"

#	export SPELLS="$all_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()"

  message "${MESSAGE_COLOR}Spells are to be cast:${DEFAULT_COLOR}"
  message "---------------------------"
  message "${SPELL_COLOR}${*}" | 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."
	return 1
  fi
  
  #make: -k keep going even if there's an error, -s silent, -f <file>
  rm -f $TMP_DIR/pass_three.done # dont return until this file exists
  if  [[  $SEPARATE   ]] ; then
    ( CAST_PASS="three" 
      /usr/bin/make -j1 -k -f $MAKEFILE all
      touch $TMP_DIR/pass_three.done
    )
  else
    ( CAST_PASS="three"
      /usr/bin/make -j1 -k -f $MAKEFILE all
      touch $TMP_DIR/pass_three.done
    ) &
  fi
  
  debug "cast" "Starting stage four make."
  
  ( CAST_PASS="four" ; /usr/bin/make  -j1 -k -f $MAKEFILE all )
  
  # in some very bad situations pass four can complete before three, and if
  # we return too soon more bad things can happen, so wait at least a little
  # bit of time before giving up.
  local i
  let i=0
  while ! test -e $TMP_DIR/pass_three.done; do 
    sleep 5
    let i+=5
    [ $i > 60 ] && break
  done
  rm $MAKEFILE

}

#---------------------------------------------------------------------
## @param spellname
## @Globals SPELL
## Sets SPELL to the spellname and calls summon_spells with spellname
## as argument. This is equivalent to calling summon, but without the
## overhead.
## (Is being called by make)
#---------------------------------------------------------------------
function pass_three()  {
  debug "cast" "pass_three - $*"
  local SPELL=$1
  local download_log=$(get_spell_dl_log $SPELL)
  touch $download_log
  lock_file   $download_log
  summon_spells "$SPELL" &>  $download_log
  unlock_file   $download_log
  
  # This is to notify show_downloading 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 and it can be resurrected 
## (can_resurect SPELL) it calls resurrect with SPELL as argument.
## 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 - $*"
  SPELL=$1
  trigger "pre_cast"
  if ! [[ $COMPILE ]] &&  can_resurect $SPELL ; then
    resurrect     $SPELL
  else
    cast_spell $*
  fi

  # We only want to trigger if the cast or resurect completed properly
  [ $? -eq 0 ] && trigger "cast"
}


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

  debug "cast" "Function : pass_five"

  # must exist or we get problems with checks here.
  touch $SUCCESS_LIST
  touch $FAILED_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`"

  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 | column
   
    set_term_title "Casting successfull."
    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
    
    message "${DEFAULT_COLOR}"
    message "${MESSAGE_COLOR}Spells that encountered problems:"
    message "---------------------------------${PROBLEM_COLOR}"
    
    # for item in $* ; do
    #  if ! ([ -e $SUCCESS_LIST ] && grep -q "^$item$" $SUCCESS_LIST) ; then
    #    message "$item"
	  #  fi

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

    set_term_title "Casting failed."
    message "${DEFAULT_COLOR}"
    return 1

  fi 
  
  debug "cast" "End of pass_five"

}

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

  debug "cast" "Starting pass_zero()"
  PASS_ZERO="y"  #<--- What is this here for? (Duff, 03/02/13)
                 # grep says that its used in run_fix (afk, June 04)
  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 hidden inside the makefile
  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..."
	cleanup
	exit 1
}

#---------------------------------------------------------------------
## @Globals RM_AT_END
## RM_AT_END is being set in main.
## Used for cleaning up. Deleting some files and $TMP_DIR.
#---------------------------------------------------------------------
function cleanup() {
  debug "cleanup" "Cleaning up the contents of `cat $RM_AT_END 2>/dev/null`"
  rm $(cat $RM_AT_END 2>/dev/null) $RM_AT_END 2>/dev/null
  rm -rf $TMP_DIR
}

#---------------------------------------------------------------------
## Safely creates $TMP_DIR and exports the variable so we can use it
## even in subprocesses called through make.
#---------------------------------------------------------------------
function mk_tmp_dirs() {
  debug "$FUNCNAME" "Making tmp dirs for $$"
  # in order for TMP_DIR to make it through make and into pass_three/four
  # we must export
  export TMP_DIR=/tmp/sorcery/cast/$$
  if [ -e $TMP_DIR ] ; then
    echo "Looks like you had an old cast on PID $$. I'm cleaning it out."
    rm -rf $TMP_DIR
  fi
  mkdir -p $TMP_DIR
}

#---------------------------------------------------------------------
## @Arguments arguments of cast
## @Globals FIX RM_AT_END ENV_CACHE CAST_PASS 
## @return 0 always
## Starts prameter processing and either runs run_fix or 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        $*
  SPELLS=`strip_parameters  $*`

  if  [[  $FIX  ]];  then

    run_fix  $SPELLS

  else

	  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
	
	  MAKEFILE="$TMP_DIR/Makefile"
	  export ENV_CACHE=${ENV_CACHE:-$TMP_DIR/envCache}
	  echo "$SUCCESS_LIST $FAILED_LIST $0 $ENV_CACHE" >> $RM_AT_END
	  export RM_AT_END="$RM_AT_END"
	  BASE_SPELLS=" $SPELLS "

    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

  fi

  echo
  true

}


. /etc/sorcery/config
[[ $VOYEUR_OVERRIDE ]] && VOYEUR="$VOYEUR_OVERRIDE"
export TOP_LEVEL=${TOP_LEVEL:-$SHLVL}

if    [  $#      -eq  0  ];  then  help  |  $PAGER

elif  [[  $1 == -h  ]]  ||  [[  $1 == --help  ]] ; then help
elif  [  "$UID"  -gt  0  ];  then

  echo  "Enter the root password, please."
  su  - -c  "DISPLAY=$DISPLAY PATH=$PATH  $0  $*"

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

  # Make a nice dir structure to put stuff in
  if ! mk_tmp_dirs ; then
    message "Failed to make temporary dir, this" \
             "might be due to a security attack,\nplease" \
             "check the permissions of /tmp/sorcery. Bailing out..."
    exit 1
  fi
  if ! [[ $RM_AT_END ]] ; then
    #This stuff is here to make sure a user can't make the file
    # and fill it up with stuff we feel the need to delete
    export RM_AT_END=${RM_AT_END:-"$TMP_DIR/tmplist"}
    touch $RM_AT_END
    chown root:root $RM_AT_END
    chmod 600 $RM_AT_END
    echo -n '' > $RM_AT_END
  fi
  
  export SAFE_CAST=$TMP_DIR/casting.safe
  export SUCCESS_LIST="$TMP_DIR/success_list"
  export FAILED_LIST="$TMP_DIR/failed_list"
  lock_file $SAFE_CAST
  
  cp  $0  $SAFE_CAST
  chmod +x $SAFE_CAST
  echo "$SAFE_CAST $FAILED_LIST $SUCCESS_LIST" >> $RM_AT_END
  exec bash $SAFE_CAST "$@"
  
else
  if  [[  $NICE != "0"  ]] ; then
    renice $NICE -p $$  >/dev/null
  fi
  trap int_trap INT
  main  $*
  if [ $SHLVL -eq $TOP_LEVEL ] ; then
    unlock_file $SAFE_CAST
    cleanup
  fi

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

