#!/bin/bash
#---------------------------------------------------------------------
##
##=head1 SYNOPSIS
##
## cast is the spell installation utility
## It can be called by the user or by sorcery
##
##=head1 DESCRIPTION
##
## ...
##
##=head1 COPYRIGHT
##
## Original version Copyright 2001 by Kyle Sallee
## Some parts copyright 2002 Anders Bruun Olsen et al
## Other additions/corrections Copyright 2002 by the Source Mage Team
##
##=head1 FUNCTIONS
##
##=over 4
##
#---------------------------------------------------------------------


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

-f  |  --fix			Discover and fix broken spells
-n  |  --nofix			Discover, but do not fix broken spells

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

-s				Download 1st, cast 2nd
    |  --deps			Configure spells and determine dependencies,
				but do not cast.

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

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

}



function set_failed_cast() {     # this is never called... erics (2004-01-28)

  debug "cast" "Function : set_failed_cast"

  if [[ $FAILED_CASTS ]] ; then
    FAILED_CASTS="$FAILED_CASTS $SPELL"
  else
    FAILED_CASTS="$SPELL"
  fi

  debug "set_failed_cast" "Value for FAILED CASTS : '$FAILED_CASTS'"

}

# this is called from resurrect, but the global variable it sets
# isn't used anywhere astitt (2004-03-12)
function set_successful_cast() {
  
  debug "cast" "Function : set_successful_cast"

  if [[ $SUCCESSFUL_CASTS ]] ; then
    SUCCESSFUL_CASTS="$SUCCESSFUL_CASTS $SPELL"
  else
    SUCCESSFUL_CASTS="$SPELL"
  fi

  debug "set_successful_cast" "Value for SUCCESSFUL CASTS : '$SUCCESSFUL_CASTS'"

}



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

}


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


}


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

) }


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}"

}


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

) }


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

) }


function status_okay()  {

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

}

#---------------------------------------------------------------------
## @Parameters 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()  { (

  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
  rm  -f  /tmp/LD_LIBRARY_PATH_FILE

) }

#---------------------------------------------------------------------
## @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  ;;
              --deps)  export  DEPS_ONLY="$1";           shift 1  ;;
    -r|--reconfigure)  RECONFIGURE="$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  ;;
        -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."
}


#---------------------------------------------------------------------
## 
#---------------------------------------------------------------------
function cast_spell()  { (

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

  run_details || return 1
  load_build_api

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

  # this is already done by run_details, and should go away (afk 4/10/04)
  run_spell_config                            &&

  # depends should not be an issue here, the whole point of using make
  # was to avoid having to care at this point
  # this line may disappear (afk 4/10/04)
  satisfy_depends             && #needed or optional deps dont get passed??
  show_downloading            && 
  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

  CACHE_COMP="$INSTALL_CACHE/$SPELL-$VERSION-$BUILD.tar$EXTENSION"
  C_LOG_COMP="$COMPILE_LOGS/$SPELL-$VERSION$EXTENSION"
  TMP_LOG="/tmp/$SPELL-$VERSION"
  INST_LOG="$INSTALL_LOGS/$SPELL-$VERSION"
  MD5_LOG="$MD5SUM_LOGS/$SPELL-$VERSION"
  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"
  
  # 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
    create_compile_log
    run_spell_success
    CAST_EXIT_STATUS=0
  else
    if [ $rc != 1 ] ; then # dont munge the log if pre_build failed
      create_compile_log
      view_compile_log
    fi
    run_spell_failure
    CAST_EXIT_STATUS=$rc
  fi


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

  return $CAST_EXIT_STATUS

) }

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

#---------------------------------------------------------------------
## @Globals SPELL VERSION
## @return 0 if SPELL is installed and it's version equals VERSION
## @return 1 otherwise
## VERSION should be set by run_details before.
## Checks if the most recent version of SPELL is installed.
#---------------------------------------------------------------------
function current_sources()  {

  spell_installed  $SPELL  &&
  [  "$VERSION"  ==  "`installed_version  $SPELL`"  ]

}

#---------------------------------------------------------------------
## @Globals D_LOG COMPILE FORCE_COMPILE SPELL
## Downloads the sources by calling summon if it has to.
## It downloads if COMPILE || FORCE_COMPILE || ! current_sources
## || ! (spell_installed $SPELL || spell_held $SPELL)
## It logs the download-process in D_LOG
## Always touches ${D_LOG}.done.$SPELL at the end to signal that
## it is done.
#---------------------------------------------------------------------
function run_summon()  {

  touch $D_LOG
  if 	[[ $COMPILE ]] 			|| 
  		[[ $FORCE_COMPILE ]] 	|| 
		! current_sources 		|| 
		! (spell_installed $SPELL || spell_held $SPELL) 
  then
    lock_file   $D_LOG
    
    ( . summon  $SPELL  >>  $D_LOG  2>&1 )
    
    unlock_file $D_LOG
  fi
  
  #This is to notify show_downloading that it is done this source
  touch "${D_LOG}.done.$SPELL"

}

#---------------------------------------------------------------------
## @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" "bad_cast" $SPELLS

  message "${MESSAGE_COLOR}Collating dependencies...${DEFAULT_COLOR}"
  depends_to_Makefile to_cast already_cast > $MAKEFILE
  SPELLS=$(hash_get_table_fields "to_cast")

  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"

}

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@$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:]' ' ')
	echo "all : $all_spells"
	echo
	echo ".PHONY : all $all_spells"

#	export SPELLS="$all_spells"
	
}


#---------------------------------------------------------------------
## @Globals SPELLS
## @Arguments spellname
## calls run_details and run_summon for every element in SPELLS
## doesn't actually build anything, just summons it.
#---------------------------------------------------------------------
function build_sources()  {

  for  SPELL  in  $SPELLS;  do
    ( run_details                       &&
#      satisfy_depends  && (duff, probably not needed 10/07)
      run_summon
      debug "cast" "Out of run_summon."
    )
  done

}

#---------------------------------------------------------------------
## @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>
  if  [[  $SEPARATE   ]] ; then
    ( CAST_PASS="three" ; /usr/bin/make -j1 -k -f $MAKEFILE all )
  else
    ( CAST_PASS="three" ; /usr/bin/make -j1 -k -f $MAKEFILE all ) &
  fi
  
  debug "cast" "Starting stage four make."
  
  ( CAST_PASS="four" ; /usr/bin/make  -j1 -k -f $MAKEFILE all )
  rm $MAKEFILE

}

#---------------------------------------------------------------------
## @param spellname
## @Globals SPELL
## Sets SPELL to the spellname and calls build_sources with spellname
## as argument.
## (Is being called by make)
#---------------------------------------------------------------------
function pass_three()  {
  debug "cast" "pass_three - $*"
  SPELL=$1
  build_sources $*
}

#---------------------------------------------------------------------
## @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 D_LOG 
## 
## Starts the passes and sets CAST_PASS accordingly.
## D_LOG is being set and the file is being cleared if it does already
## exists 
#---------------------------------------------------------------------
function pass_zero()  {

  debug "cast" "Starting pass_zero()"
  PASS_ZERO="y"  #<--- What is this here for? (Duff, 03/02/13)
  export  D_LOG="/tmp/sorcery/cast/$$/downloading.$RANDOM"
  rm -f $D_LOG 2>/dev/null

  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 "$@"

  rm  -f   $D_LOG*
  

}

#---------------------------------------------------------------------
## @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/sorcery/cast/$$
## basically.
#---------------------------------------------------------------------
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/sorcery/cast/$$

}
#---------------------------------------------------------------------
## Safely creates /tmp/sorcery/cast/$$
#---------------------------------------------------------------------
function mk_tmp_dirs() {
	debug "$FUNCNAME" "Making tmp dirs for $$"
	if [ -d /tmp/sorcery/cast/$$ ] ; then
		echo "Looks like you had an old cast on PID $$. I'm cleaning it out."
		rm -rf /tmp/sorcery/cast/$$
	fi
	mkdir -p /tmp/sorcery/cast/$$
}

#---------------------------------------------------------------------
## @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/sorcery/cast/$$/Makefile"
	  export ENV_CACHE=${ENV_CACHE:-/tmp/sorcery/cast/$$/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

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
  mk_tmp_dirs

  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 the feel the need to delete
    export RM_AT_END=${RM_AT_END:-"/tmp/sorcery/cast/$$/tmplist"}
	  touch $RM_AT_END
	  chown root:root $RM_AT_END
	  echo -n '' > $RM_AT_END
  fi
  
  export SAFE_CAST=/tmp/sorcery/cast/$$/casting.safe
  export SUCCESS_LIST="/tmp/sorcery/cast/$$/success_list"
  export FAILED_LIST="/tmp/sorcery/cast/$$/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 $SAFE_CAST "$@"
  
else

  if  [[  $NICE != "0"  ]] ; then
    renice $NICE -p $$  >/dev/null
  fi
  trap int_trap INT
  main  $*
  if [ $SHLVL -eq 2 ] ; then
	unlock_file $SAFE_CAST
	cleanup
  else
  	rm -rf /tmp/sorcery/cast/$$
  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
##
#---------------------------------------------------------------------

