#!/bin/bash
#---------------------------------------------------------------------
##
## @scribe
## 
## @Synopsis scribe handles adding/updating/reindexing/etc. of grimoires
##
## @Copyright
## Original version Copywright 2002 by Ryan Abrams
## Additions/Corrections Copyright 2002-2004 by the Source Mage Team
## Released under the GPL
#---------------------------------------------------------------------


#---------------------------------------------------------------------
## help
##
## print horribly unhelpful helpscreen
##
#---------------------------------------------------------------------
function help() {
  cat  <<  EOF

Scribe is a utility for controlling the grimoires in your codex,
and the spells in your grimoires.

Invoke scribe with the desired command followed by the target.
Note that most options can be called with the beginning of the command.

Command		Arguments	Description
add		grimoire [from source]	Adds a new grimoire
				 (optionally from a different URL)
remove		grimoire	Removes a grimoire
update		[grimoire]	Updates a grimoire or all grimoires

fix		grimoire	Attempts to fix metadata for grimoire
reindex		[grimoire]	Recreates the list of spells
index				Prints list of grimoires

set		grim1 grim2	puts grim1 before grim2
swap		grim1 grim2 	Swaps two grimoires

localize	grimoire	Makes a grimoire local
				 (so scribe won't try to update it)
unlocalize	grimoire	Makes a grimoire nonlocal

EOF

  exit  1
}

#---------------------------------------------------------------------
## scribe_add
##
## add grimoires to codex, unless they already exist
## usage:
## scribe_add grimoire [ from location] [grimoire [ from location]] ...
##
#---------------------------------------------------------------------
function scribe_add() {
  #For each item listed add it
  if ! [ -d $CODEX_ROOT ] ; then
    message "Main codex directory not present. Creating $CODEX_ROOT."
    mkdir -p $CODEX_ROOT
  fi

  local grimoire from
  while [ -n "$1" ] ; do
    grimoire="$1"
    if [ "$2" == "from" ] ; then
      from=$3
      shift 3
    else
      from=""
      shift 1
    fi

    if codex_find_grimoire $grimoire >/dev/null; then
      message "${PROBLEM_COLOR}There already exists a grimoire with" \
              "the name $grimoire! Refusing to add.${DEFAULT_COLOR}"
      continue
    fi
    # derive a full grimoire name
    grimoire=$(codex_canonicalize_grimoire_name $grimoire)
    scribe_add_worker $grimoire $from
  done
}
  
#---------------------------------------------------------------------
## scribe_add_worker
##
## downloads a grimoire, unpacks it and adds it to the codex listing
## if location is not given the default is used
##
## @param grimoire
## @param location to download from (optional), if empty use $CODEX_URL
##
#---------------------------------------------------------------------
function scribe_add_worker() {
  local grimoire=$1
  local from=$2

  if [ -z $grimoire ] ; then
    message "Empty grimoire name! Please contact the sorcery team"
    return 1
  fi

  local grim_dir=$(dirname $grimoire)
  local grim_name=$(basename $grimoire)

  #If the associated FROM is not empty, check it for validity
  if  [ -n "$from" ];  then
    if  ! url_is_valid "$from";  then
      #if not valid, use $CODEX_URL
      message  "Error: $from is not a recognized url. Using default."
      from="$CODEX_URL/$grim_name.tar.bz2"
    fi
  else
    #from is empty - use the default
    from="$CODEX_URL/$grim_name.tar.bz2"
  fi

  message "Adding grimoire $grim_name to $grim_dir from $from"

  #switch to and work in the codex
  pushd $grim_dir &> /dev/null

  if test -d $grimoire ; then
    message "Found an old grimoire directory, removing it..."
    rm -rf $grimoire
  fi

  local FILE
  url_set_options nocache
  url_download $from

  #check the success of the download
  if [ $? != 0 ]; then
    message "Error downloading grimoire..."
    popd &>/dev/null
    return 1
  fi

  local tarball=$(basename $from)

  gpg_verify_grimoire $PWD/$tarball $(dirname $from)
  rc=$?

  gpg_user_query $rc || return 1

  #if the grimoire is an archive, untar it
  if [ -e $tarball ] ; then
    #if successfuly downloaded, untar it and remove the zip
    bzcat $tarball | tar -x  1>/dev/null 2>&1 
    rm $tarball
  fi
  popd &>/dev/null

  local found
  #make sure that it untarred to the proper dir.
  if [ -d $grimoire ]; then
    found=yes
  elif  [ -d $grim_dir/grimoire ]; then
    mv -f $grim_dir/grimoire $grimoire &&
    found=yes
  else
    local tmp_name=$grim_dir/$(echo $tarball|sed 's/\.tar\.bz2//')
    if  [ -d $tmp_name ]; then
      mv -f $tmp_name $grimoire &&
      found=yes
    fi
  fi  

  local error
  if [ "$found" == "yes" ]; then
    #success! create a GRIMOIRE file that stores where it was downloaded from
    echo "FROM_URL=$from" > $grimoire/GRIMOIRE && 
    chmod +x $grimoire/GRIMOIRE && 

    codex_add_grimoire $grimoire 0 && 
    codex_create_cache $grimoire && 
    message "Grimoire \"$grimoire\" successfully added to your codex." ||
    error=yes
  fi
  if [ "$error" == "yes" ] ; then
    #dir not found. error.
    #FIXME- add a check for what dir it was and remove that
    message "ERROR: Grimoire tarball for $grimoire not formated correctly!"
    return 1
  fi 

  return 0
}

#---------------------------------------------------------------------
## scribe_fix
##
## frontend to metadata fixing
##
## @param grimoire names, if none all grimoires
##
#---------------------------------------------------------------------
function scribe_fix() {
  local grim grimoire grimoires
  if [[ $@ ]] ; then
    grimoires=$@
  else
    grimoires=`codex_get_all_grimoires`
  fi
  for grim in $grimoires; do

    if ! grimoire=$(codex_find_grimoire $grim) ; then
      message "Grimoire $grim not found"
      return 1
    fi

    if ! [ -e $grimoire/GRIMOIRE ]; then
      message "No Metadata found for Grimoire $grim"
      if scribe_fix_metadata $grimoire; then
        message "$grimoire fixed."
      else
        message "$grimoire not fixed"
      fi
    elif query "Metadata for $grimoire found. Fix anyway?" n; then
      if scribe_fix_metadata $grimoire 1; then
        message "$grimoire fixed."
      else
        message "$grimoire not fixed"
      fi
    else
      message "$grimoire ignored"
    fi
  done
}

#---------------------------------------------------------------------
## scribe_fix_metadata
##
## fixes the metadata on a grimoire based on user input
##
## @param grimoire name
## @param force, if one fix without prompting the user
##
#---------------------------------------------------------------------
function scribe_fix_metadata() {
  local URL grimoire_dir grimoire_name

  if ! grimoire_dir=$(codex_find_grimoire $1) ; then
    message "Grimoire $1 not found, this may be a sorcery bug..."
    return 1
  fi
  grimoire_name=$(basename $1)

  if query "Repair Grimoire:$1's Metadata?" n || [ "$2" == "1" ] ; then
    message "Enter url to pull grimoire from"
    read -p "[ $CODEX_URL/$grimoire_name.tar.bz2 ]: " URL
    URL=${URL:-$CODEX_URL/$grimoire_name.tar.bz2}
    message "Setting Metadata to: $URL"
    message "If this is incorrect, run \"scribe fix $1\" and correct"
    echo "FROM_URL=$URL" > $grimoire_dir/GRIMOIRE
    chmod +x $grimoire_dir/GRIMOIRE
    return 0
  fi
  return 1
}

#---------------------------------------------------------------------
## scribe_index
##
## Display installed grimoires
##
#---------------------------------------------------------------------
function scribe_index() {
  local idx grimoire
  
  echo ""
  echo "Codex Listing"
  echo "-------------"
  echo ""

  #for each grimoire
  let idx=0
  for grimoire in $(codex_get_all_grimoires); do
    echo " [$idx] : `basename "$grimoire"` : $grimoire"
    let idx+=1
  done
  echo ""
}

#---------------------------------------------------------------------
## scribe_localize
##
## Set grimoires "local" so scribe update ignores them
##
## @param Names of grimoires to localize
##
#---------------------------------------------------------------------
function scribe_localize() {
  local grimoire path
  for grimoire in $@; do
    scribe_localize_sub yes &&
    message "Made $path local"
  done
}

#---------------------------------------------------------------------
## scribe_localize_sub
##
## Adjust grimoires localization state
##
## @param Names of grimoires to (un)localize
## @param yes/no state of grimoire localization
##
#---------------------------------------------------------------------
function scribe_localize_sub() {
    path=$(codex_find_grimoire $grimoire)
    if [ -z "$path" ]; then
      message "No such grimoire: $grimoire"
      continue
    fi
    touch "$path/GRIMOIRE" &&
    modify_config "$path/GRIMOIRE" "CODEX_IS_LOCAL" "$1"
}

#---------------------------------------------------------------------
## scribe_reindex
##
## Update the spell index for grimoires
##
## @param grimoire names, if none all grimoires
##
#---------------------------------------------------------------------
function scribe_reindex() {
  local paths=""
  local grimoire grimoires grim
  if [[ "$@" ]] ; then
    grimoires=$@
  else
    grimoires=`codex_get_all_grimoires`
  fi

  for grim in $grimoires; do
    if ! grimoire=$(codex_find_grimoire $grim) ; then
      message "Grimoire $grim not found"
      return 1
    fi
    paths="$paths $grimoire"
  done
  debug "scribe" "reindex: paths = \"$paths\""

  message -n "Reindexing spell list... "
  codex_create_cache $paths
  message "done."
}

#---------------------------------------------------------------------
## scribe_remove
##
## remove a grimoire from the codex
##
## @param grimoires to remove
##
#---------------------------------------------------------------------
function scribe_remove() {
  #remove all listed grimoires
  local grimoire grim
  for grim in $@; do
    # if the grimoire name starts with / they may have given a
    # full path, the [[ ]] actually doesnt expand /* to $(ls /)
    if ! grimoire=$(codex_find_grimoire $grim) ; then
      message "Grimoire $grim not found"
      return 1
    fi

    message "Deleting grimoire $grimoire directory"
    rm -rf $grimoire &&
    message "Removing grimoire $grimoire from codex" &&
    codex_remove_grimoire $grimoire ||
    { message "An error occured trying to remove $grimoire, I wonder why"
      return 1
    }
  done
}

#---------------------------------------------------------------------
## scribe_set
##
## Set grimoire1 before grimoire2, do this by removing grimoire1,
## then finding the position of grimoire2 and then use codex_add_grimoire
## on grimore1 with overwrite off
##
## @param grimoire1
## @param grimoire2
##
#---------------------------------------------------------------------
function scribe_set(){
  local grim1=$1 grimoire1
  local grim2=$2 grimoire2
  local idx1 idx2
  if [ $grim1 == $grim2 ] ; then
    message "$grim1 IS $grim2, can't set a grimoire to itself"
    return 1
  fi
  if ! codex_find_grimoire $grim1 grimoire1 idx1 ; then
    message "Grimoire $grim1 not found"
    return 1
  fi
  if ! codex_find_grimoire $grim2 grimoire2 idx2 ; then
    message "Grimoire $grim2 not found"
    return 1
  fi
  if [ $idx1 -eq $idx2 ] ; then
    message "This shouldn't happen, but if it does contact the sorcery team"
    return 1
  elif [ $idx1 -lt $idx2 ] ; then
    let idx2-- # grim1 is before grim2, by removing grim1, the position
               # decrease by 1
  fi
  
  message "Setting $grim1 before $grim2"
  codex_remove_grimoire $grimoire1
  codex_add_grimoire $grimoire1 $idx2
}

#---------------------------------------------------------------------
## scribe_swap
##
## switch grimoire1 and grimoire2 in the grimoire ordering
## do this by finding their positions, using the overwrite feature of
## codex_add_grimoire
##
## @param grimoire1
## @param grimoire2
##
#---------------------------------------------------------------------
function scribe_swap() {
  local grim1=$1 grimoire1
  local grim2=$2 grimoire2
  local idx1 idx2
  if ! codex_find_grimoire $grim1 grimoire1 idx1 ; then
    message "Grimoire $grim1 not found"
    return 1
  fi
  if ! codex_find_grimoire $grim2 grimoire2 idx2 ; then
    message "Grimoire $grim1 not found"
    return 1
  fi
  message -n "swapping "
  message "$grim1 with $grim2"
  codex_add_grimoire $grimoire1 $idx2 overwrite
  codex_add_grimoire $grimoire2 $idx1 overwrite
}

#---------------------------------------------------------------------
## scribe_update
##
## updates all installed grimoires, or just those passed in as params
##
## @param grimoire names, if none all grimoires
##
#---------------------------------------------------------------------
function scribe_update() {
  local grimoires grimoire grim from rc
  rc=0
  if [[ $@ ]] ; then
    while [ -n "$1" ] ; do
      grim="$1"
      if ! grimoire=$(codex_find_grimoire $grim) ; then
        message "Grimoire $grim not found"
        if [[ "$2" == "from" ]] ; then shift 3 ; else  shift 1 ; fi
        let rc++
        continue
      fi

      if ! [ -e $grimoire/GRIMOIRE ] && ! scribe_fix_metadata $grim; then
        message "$grim has invalid metadata. Not Updating"
        if [[ "$2" == "from" ]] ; then shift 3 ; else  shift 1 ; fi
        let rc++
        continue
      fi

      if codex_is_local $grimoire; then
        message "${SPELL_COLOR}$grim${MESSAGE_COLOR} is being marked as local. Won't update.${DEFAULT_COLOR}"
        if [[ "$2" == "from" ]] ; then shift 3 ; else  shift 1 ; fi
        continue
      fi

      . $grimoire/GRIMOIRE
      # get a url if one exists
      if [ "$2" == "from" ] ; then
        from=$3
        if [ -z $from ] || ! url_is_valid "$from"; then
          local bad_from=$from
          from=$FROM_URL
          message "Error: \"$bad_from\" is not a valid URL. Using: $from"
        fi
        shift 3
      else
        from=$FROM_URL
        shift 1
      fi

      message "Updating grimoire $grim from $from"
      scribe_do_update $grimoire $from &&
      scribe_reindex $grimoire ||
      let rc++
    done
  else
    #for each grimoire
    for grimoire in $(codex_get_all_grimoires); do
      grim=$(basename $grimoire)
      if ! [ -e $grimoire/GRIMOIRE ] && ! scribe_fix_metadata $grimoire; then
        #no metadata found. dont auto-update
        message "$grim has invalid metadata. Not Updating"
        continue
      fi

      if codex_is_local $grimoire; then
        message "${SPELL_COLOR}$grim${MESSAGE_COLOR} is being marked as local. Won't update.${DEFAULT_COLOR}"
        continue
      fi

      #include metadata
      . $grimoire/GRIMOIRE
      from=$FROM_URL
      message "Updating grimoire $grim from $from"
      scribe_do_update $grimoire $from &&
      scribe_reindex $grimoire ||
      let rc++
    done
  fi
  tablet_import_repair_files $TABLET_PATH
  return $rc
}

#---------------------------------------------------------------------
## scribe_do_update
##
## actually update a grimoire
##
## @param full path to a grimoire
## @param url to get the grimoire from
##
#---------------------------------------------------------------------
function scribe_do_update() {
  #switch to codex for update
  local grimoire=$1
  local from=$2
  if ! codex_is_canonicalized $grimoire ; then
    message "$grimoire is not canonicalized, this may be a sorcery bug," \
            "please let us know if you see this. Bailing out."
    exit 1
  fi

  if ! test -d $grimoire ; then
    message "$grimoire is not a directory! refusing to update"
    return 1
  fi

  local grim_dir=$(dirname $grimoire)
  local grim_name=$(basename $grimoire)
  pushd $grim_dir &> /dev/null

  local FILE # this is set from url_download
  url_set_options nocache
  url_download $from

  #check the success of the download
  if [ $? != 0 ] ; then
    #grimoire was not downloaded. Error message.
    message "Error downloading grimoire..."
    return 1
  fi

  local rc
  gpg_verify_grimoire $PWD/$(basename $from) $(dirname $from)
  rc=$?

  gpg_user_query $rc $(basename $from) grimoire || return 1

  # for some reason assume all grimoire tarballs as .tar.bz2
  # and named after the name of the grimoire, this is kind of
  # a sneaky way to let rsyncing work also :-/ (afk 2004/10/31)

  #if the grimoire is an archive, untar it
  if [ -e $grim_name.tar.bz2 ]; then
    #backup old version until successful untar
    mv $grim_name temp_$grim_name.old

    #untar the new grimoire update and remove zip
    bzcat $grim_name.tar.bz2 | tar -x  1>/dev/null 2>&1 
    rm $grim_name.tar.bz2
  fi

  #check for a successful unzip
  if [ -d $grimoire ]; then
    #success! create metadata
    echo "FROM_URL=$2" > $grimoire/GRIMOIRE
    chmod +x $grimoire/GRIMOIRE

    #and remove backup version
    if [ -e temp_$grim_name.old ]; then
      rm -rf temp_$grim_name.old
    fi

    #Confirm Success!
    message "$grim_name Updated!"
    grimoire_history $grimoire
  else
    #uh oh! bad unzip.
    message "ERROR: Grimoire tarball for $grim_name not formated correctly! Reverting."
    #phew! we backed it up. restore backed up version
    mv -f temp_$grim_name.old $grim_name
  fi 

  #return to directory scribe was run from
  popd &>/dev/null
}

#---------------------------------------------------------------------
## scribe_localize
##
## Removes a grimoire's "local" state so scribe update will update them
##
## @param Names of grimoires to unlocalize
##
#---------------------------------------------------------------------
function scribe_unlocalize() {
  local grimoire path
  for grimoire in $@; do
    scribe_localize_sub $grimiore no &&
    message "Made $path unlocal"
  done
}

#---------------------------------------------------------------------
## find_function
##
## take the command line and figure out what the user wants to do
##
## @param command line args
##
#---------------------------------------------------------------------
function find_function()  {
  case  $1  in
                      a|ad|add) FUNCTION="add" ;;
                      f|fi|fix) FUNCTION="fix" ;;
           i|in|ind|inde|index) FUNCTION="index";;
          l|loc|local|localize) FUNCTION="localize";;
      rei|reind|reinde|reindex) FUNCTION="reindex";;
      rm|rem|remo|remov|remove) FUNCTION="remove" ;;
                        se|set) FUNCTION="set";;
                   sw|swa|swap) FUNCTION="swap";;
   un|unloc|unlocal|unlocalize) FUNCTION="unlocalize";;
    u|up|upd|upda|updat|update) FUNCTION="update" ;;
    *) help    ;;
  esac
}

#---------------------------------------------------------------------
## main
##
## start the ride.
#---------------------------------------------------------------------
function main() {
  #move to the tmp folder in case we create junk
  cd  /tmp

  #check out what it is we are doing
  local FUNCTION
  find_function $*
  shift 1

  #now that we have a function, do it.
  case $FUNCTION in
           add) scribe_add $@ ;;
           fix) scribe_fix $@ ;;
         index) scribe_index $@ ;;
      localize) scribe_localize $@ ;;
       reindex) scribe_reindex $@ ;;
        remove) scribe_remove $@ ;;
           set) scribe_set $@ ;;
          swap) scribe_swap $@ ;;
    unlocalize) scribe_unlocalize $@ ;;
        update) scribe_update $@ ;;
             *) help ;;
  esac
}

#---------------------------------------------------------------------
. /etc/sorcery/config

#check if root. If not, become root
if    [  "$UID"  ==  0  ] ; then
  if  [[  $NICE != "0"  ]] ; then
    renice $NICE -p $$  >/dev/null
  fi
  mk_tmp_dirs scribe
  main  $@
  rc=$?
  cleanup_tmp_dir $TMP_DIR
  exit $rc
elif  [[  $1 == -h  ]]  ||  [[  $1 == --help  ]] ; then
  help
elif [[ $1 == index ]] ; then
  scribe_index # this requires no access privaledges so special case it
else  
  echo  "Enter the root password, please."  1>&2
  PARAMS=$(consolidate_params "$@")
  su -c "$0 $PARAMS" root
fi

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