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


#---------------------------------------------------------------------
## help
##
## print horribly unhelpful helpscreen
##
#---------------------------------------------------------------------
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
reindex-keyword [grimoire]       Recreates keyword listing
                                 (official grimoires should come with
                                 a keyword listing already)
reindex-version [grimoire]       Recreates the version listing

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


Grimoire tarballs are located at $CODEX_TARBALL_URL
Grimoire rsync modules are located at $CODEX_RSYNC_URL

EOF

  exit  1
}


#---------------------------------------------------------------------
## Common error messag when downloading a grimoire fails.
#---------------------------------------------------------------------
scribe_download_fail_error_msg() {
  error_message "${PROBLEM_COLOR}Error downloading grimoire..."
  error_message "${MESSAGE_COLOR}Grimoire tarballs are located at:" \
                "${FILE_COLOR}$CODEX_TARBALL_URL${DEFAULT_COLOR}"
  error_message "${MESSAGE_COLOR}Grimoire rsync modules are located at:" \
                "${FILE_COLOR}$CODEX_RSYNC_URL${DEFAULT_COLOR}"
}

#---------------------------------------------------------------------
## Unpackage a grimoire tarball and make sure it worked
#---------------------------------------------------------------------
unpackage_grimoire_tarball() {
  local tarball=$1
  local grimoire=$2
  local grim_name=$3
  #untar the new grimoire tarball
  if tar -xjf $tarball 1>/dev/null 2>&1; then
    # ensure the grimoire unpacked where it was expected to
    if ! [ -d $grimoire ]; then
      error_message "${PROBLEM_COLOR}ERROR: Grimoire tarball for" \
                    "${SPELL_COLOR}$grim_name ${PROBLEM_COLOR}" \
                    "not formatted correctly!${DEFAULT_COLOR}"
      return 1
    fi
    return 0
  else
    error_message "${PROBLEM_COLOR}ERROR: Grimoire tarball for" \
                  "${SPELL_COLOR}$grim_name ${PROBLEM_COLOR}" \
                  "did not unpack properly!${DEFAULT_COLOR}"
    return 1
  fi
}

#---------------------------------------------------------------------
## Given an rsync url, make sure it has a / on the end of it
## so downloading works.
#---------------------------------------------------------------------
sanitize_rsync_url() {
  # add a / to the end, if one isnt there, if the user types
  # rsync://sourcemage.org::codex/games instead of
  # rsync://sourcemage.org::codex/games/ strange things happen
  if list_find "rsync" $prefix ; then
    echo $from|sed 's!\([^/]\)$!\1/!'
  else
    echo $from
  fi
}

#---------------------------------------------------------------------
## Validate a grimoire tree using a manifest
#---------------------------------------------------------------------
scribe_validate_tree_common() {
  local scribe_target=$1
  local grim_name=$2
  local grimoire=$3
    if ! test -d "$scribe_target"; then
      error_message "${PROBLEM_COLOR}Downloaded a tree but directory doesn't exist!" \
                    "File a bug if you see this.${DEFAULT_COLOR}"
      return 1
    fi
    if [[ "$scribe_target" != "$grim_name" ]] ; then
      mv -f "$scribe_target" "$grim_name"
    fi

    verify_grimoire_tree "$grim_name" "$grimoire"
    local rc=$?
    if [[ $rc != 0 ]] && [[ $rc != 253 ]] ; then
      pushd $grim_dir >/dev/null
      grimoire_tree_user_query "$grim_name" || return 1
      popd >/dev/null
    fi
}


#---------------------------------------------------------------------
## scribe_add
##
## add grimoires to codex, unless they already exist
## usage:
## scribe_add grimoire [ from location] [grimoire [ from location]] ...
##
#---------------------------------------------------------------------
scribe_add() {
  $STD_DEBUG
  #For each item listed add it
  if ! [ -d $CODEX_ROOT ] ; then
    message "${MESSAGE_COLOR}Main codex directory not present." \
            "Creating ${FILE_COLOR}$CODEX_ROOT${MESSAGE_COLOR}.${DEFAULT_COLOR}"
    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
      error_message "${PROBLEM_COLOR}There already exists a grimoire with" \
              "the name ${SPELL_COLOR}$grimoire${PROBLEM_COLOR}!" \
              "Refusing to add.${DEFAULT_COLOR}"
      continue
    fi

    # avoid deleting $grimoire if it is a dir #13742
    if [[ -d $grimoire ]]; then
      error_message "${PROBLEM_COLOR}The grimoire ${FILE_COLOR}$grimoire" \
                    "${PROBLEM_COLOR} is an existing directory!" \
                    "Refusing to add!${DEFAULT_COLOR}"
      continue
    fi

    local grim_name="$grimoire"
    # derive a full grimoire name
    grimoire=$(codex_canonicalize_grimoire_name $grimoire)
    local grim_dir
    smgl_dirname "$grimoire" grim_dir

    if [[ -z $from ]]; then
      message "${MESSAGE_COLOR}Adding grimoire ${SPELL_COLOR}$grim_name" \
              "${MESSAGE_COLOR}to ${FILE_COLOR}$grim_dir${DEFAULT_COLOR}"
    else
      message "${MESSAGE_COLOR}Adding grimoire ${SPELL_COLOR}$grim_name" \
              "${MESSAGE_COLOR}to ${FILE_COLOR}$grim_dir ${MESSAGE_COLOR}from" \
              "${FILE_COLOR}$from${DEFAULT_COLOR}"
    fi
    scribe_add_update_worker "$grimoire" "$from" add
  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
##
#---------------------------------------------------------------------
scribe_add_update_worker() {
  $STD_DEBUG
  local grimoire=$1
  local from=$2
  local add_or_update=$3

  if [ -z $grimoire ] ; then
    error_message "${PROBLEM_COLOR}Empty grimoire name! Please" \
                  "contact the sorcery team if you see this.${DEFAULT_COLOR}"
    return 1
  fi

  if [[ $add_or_update == update ]] ; then
    if ! test -d $grimoire ; then
      error_message "${FILE_COLOR}$grimoire ${PROBLEM_COLOR}is not a directory!" \
                    "Refusing to update!${DEFAULT_COLOR}"
      return 1
    fi
  fi

  local grim_dir grim_name grim_target
  smgl_dirname "$grimoire" grim_dir
  smgl_basename "$grimoire" grim_name
  pushd $grim_dir &> /dev/null

  # get the url, if one was specified sanity check it
  if  [ -n "$from" ];  then
    if ! url_is_valid "$from"; then
      #if not valid, use default
      error_message  "${PROBLEM_COLOR}Error: ${FILE_COLOR}$from" \
                     "${PROBLEM_COLOR}is not a recognized url." \
                     "Using the default.${DEFAULT_COLOR}"
      from=""
    else
      smgl_basename "$from" grim_target
    fi
  fi

  local prefix
  if  [ -z "$from" ];  then
    #from is empty - use the default
    prefix=$(url_get_prefix $CODEX_URL)
    if [[ $? != 0 ]]; then
      error_message "${PROBLEM_COLOR}Failed to get the \$CODEX_URL prefix!" \
      "${DEFAULT_COLOR}"
      popd &>/dev/null
      return 1
    fi

    local tree_prefixes="rsync svn svn_http svn_https svn_ssh cvs smgl_tla"
    tree_prefixes="$tree_prefixes dir git git_http hg_http bzr"
    if list_find "$tree_prefixes" "$prefix"; then
      grim_target=$grim_name
    else
      grim_target="$grim_name.tar.bz2"
    fi
    from="$CODEX_URL/$grim_target"
  fi

  local prefix=$(url_get_prefix $from)
  from=$(sanitize_rsync_url $prefix $from)


  # do some cleanup before downloading
  if [[ $add_or_update == add ]] ; then
    if test -d $grimoire ; then
      message "${MESSAGE_COLOR}Found an old grimoire directory," \
              "removing it...${DEFAULT_COLOR}"
      rm -rf $grimoire
    fi
  fi
  test -f $grim_target && rm -f $grim_target

  # download it
  local scribe_target scribe_type
  case "$add_or_update:$from" in
    # Special case to avoid re-downloading git repos
    (update:git*)
      url_download "$grim_name" "$from" "" scribe_target scribe_type
      ;;
    (*)
      url_download "$grim_target" "$from" "" scribe_target scribe_type
      ;;
  esac

  #check the success of the download
  if [ $? != 0 ]; then
    scribe_download_fail_error_msg
    popd &>/dev/null
    return 1
  fi

  # do result specific actions
  local rc
  if [[ "$scribe_type" == file ]] ; then

    gpg_verify_grimoire $PWD/$scribe_target $(smgl_dirname $from)
    rc=$?
    gpg_user_query $rc $(smgl_basename $from) grimoire || return 1

    [[ $add_or_update == update ]] && mv $grim_name temp_$grim_name.old

    if unpackage_grimoire_tarball "$scribe_target" "$grimoire" "$grim_name"
    then
      rm $scribe_target
      if [[ $add_or_update == update ]] && [ -d temp_$grim_name.old ]; then
        rm -rf temp_$grim_name.old
      fi
    else
      # restore from backup
      [[ $add_or_update == update ]] && mv -f temp_$grim_name.old $grim_name
      popd &>/dev/null; return 1
    fi
  elif [[ "$scribe_type" == tree ]]; then
    if ! scribe_validate_tree_common "$scribe_target" "$grim_name" "$grimoire"
    then
      popd &>/dev/null; return 1
    fi
  else
    error_message "${PROBLEM_COLOR}Unknown download type" \
                  "${FILE_COLOR}$scribe_type ${PROBLEM_COLOR}," \
                  "file a bug if you see this.${DEFAULT_COLOR}"
    return 1
  fi
  popd &>/dev/null

  #success! create a GRIMOIRE file that stores where it was downloaded from
  echo "FROM_URL=$from" > $grimoire/GRIMOIRE &&
  chmod +x $grimoire/GRIMOIRE &&

  if [[ $add_or_update == add ]] ; then
    codex_add_grimoire $grimoire 0
  fi &&
  scribe_reindex $grimoire &&
  scribe_reindex_version $grimoire &&
  if [[ $add_or_update == add ]] ; then
    message "${MESSAGE_COLOR}Grimoire ${FILE_COLOR}\"$grimoire\"" \
            "${MESSAGE_COLOR}successfully added to your codex.${DEFAULT_COLOR}"
  else
    message "${SPELL_COLOR}$grim_name ${MESSAGE_COLOR}updated!${DEFAULT_COLOR}"
    echo
    grimoire_history $grimoire
  fi

  return 0
}
#---------------------------------------------------------------------
## scribe_fix
##
## frontend to metadata fixing
##
## @param grimoire names, if none all grimoires
##
#---------------------------------------------------------------------
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
      error_message "${PROBLEM_COLOR}Grimoire ${SPELL_COLOR}$grim" \
                    "${PROBLEM_COLOR}not found!${DEFAULT_COLOR}"
      return 1
    fi

    if ! [ -e $grimoire/GRIMOIRE ]; then
      message "${MESSAGE_COLOR}No Metadata found for Grimoire" \
              "${SPELL_COLOR}$grim${DEFAULT_COLOR}"
      if scribe_fix_metadata $grimoire; then
        message "${FILE_COLOR}$grimoire ${MESSAGE_COLOR}fixed.${DEFAULT_COLOR}"
      else
        error_message "${FILE_COLOR}$grimoire" \
                      "${PROBLEM_COLOR}not fixed!${DEFAULT_COLOR}"
      fi
    elif query "Metadata for $grimoire found. Fix anyway?" n; then
      if scribe_fix_metadata $grimoire 1; then
        message "${FILE_COLOR}$grimoire ${MESSAGE_COLOR}fixed.${DEFAULT_COLOR}"
      else
        error_message "${FILE_COLOR}$grimoire ${PROBLEM_COLOR}not fixed!${DEFAULT_COLOR}"
      fi
    else
      message "${FILE_COLOR}$grimoire ${MESSAGE_COLOR}ignored${DEFAULT_COLOR}"
    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
##
#---------------------------------------------------------------------
scribe_fix_metadata() {
  local URL grimoire_dir grimoire_name

  if ! grimoire_dir=$(codex_find_grimoire $1) ; then
    error_message "${PROBLEM_COLOR}Grimoire ${FILE_COLOR}$1 ${PROBLEM_COLOR}" \
                  "not found, this may be a sorcery bug...${DEFAULT_COLOR}"
    return 1
  fi
  smgl_basename "$1" grimoire_name

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

#---------------------------------------------------------------------
## scribe_index
##
## Display installed grimoires
##
#---------------------------------------------------------------------
scribe_index() {
  local idx grimoire grimoire_name

  echo ""
  echo "Codex Listing"
  echo "-------------"
  echo ""

  #for each grimoire
  let idx=0
  for grimoire in $(codex_get_all_grimoires); do
    smgl_basename "$grimoire" grimoire_name
    echo -n " [$idx] : $grimoire_name : $grimoire"
    if [[ -f $grimoire/VERSION ]]; then
      echo " : $(head -c 16 $grimoire/VERSION | head -n 1)"
    else
      echo
    fi
    let idx+=1
  done
  echo ""
}

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

#---------------------------------------------------------------------
## scribe_localize_sub
##
## Adjust grimoires localization state
##
## @param Names of grimoires to (un)localize
## @param yes/no state of grimoire localization
##
#---------------------------------------------------------------------
scribe_localize_sub() {
    path=$(codex_find_grimoire $grimoire)
    if [ -z "$path" ]; then
      error_message "${PROBLEM_COLOR}No such grimoire:" \
                    "${FILE_COLOR}$grimoire${DEFAULT_COLOR}"
      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
##
#---------------------------------------------------------------------
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
      error_message "${PROBLEM_COLOR}Grimoire ${FILE_COLOR}$grim" \
                    "${PROBLEM_COLOR}not found!${DEFAULT_COLOR}"
      return 1
    fi
    paths="$paths $grimoire"
  done
  debug "scribe" "reindex: paths = \"$paths\""

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

#---------------------------------------------------------------------
## scribe_reindex_keyword
##
## Recreate the keyword index, this is slow in comparison to
## normal reindexing.
##
## @param grimoire names, if none all grimoires
##
#---------------------------------------------------------------------
scribe_reindex_keyword() {
  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
      error_message "${PROBLEM_COLOR}Grimoire ${FILE_COLOR}$grim" \
                    "${PROBLEM_COLOR}not found!${DEFAULT_COLOR}"
      return 1
    fi
    message -n "${MESSAGE_COLOR}Reindexing keywords for" \
               "${FILE_COLOR}$grim ${MESSAGE_COLOR}... ${DEFAULT_COLOR}"
    codex_create_keyword_cache "$grimoire"
    message "${MESSAGE_COLOR}done.${DEFAULT_COLOR}"
  done
}

#---------------------------------------------------------------------
## scribe_reindex_version
##
## Recreate the version index
##
## @param grimoire names, if none all grimoires
##
## TODO: merge scribe_reindex_* functions?
#---------------------------------------------------------------------
scribe_reindex_version() {
  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
      error_message "${PROBLEM_COLOR}Grimoire ${FILE_COLOR}$grim" \
                    "${PROBLEM_COLOR}not found!${DEFAULT_COLOR}"
      return 1
    fi
    message -n "${MESSAGE_COLOR}Reindexing versions for" \
               "${FILE_COLOR}$grim${MESSAGE_COLOR} ... ${DEFAULT_COLOR}"
    codex_create_version_cache "$grimoire"
    message "${MESSAGE_COLOR}done.${DEFAULT_COLOR}"
  done

}
#---------------------------------------------------------------------
## scribe_remove
##
## remove a grimoire from the codex
##
## @param grimoires to remove
##
#---------------------------------------------------------------------
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
      error_message "${PROBLEM_COLOR}Grimoire ${FILE_COLOR}$grim" \
                    "${PROBLEM_COLOR}not found!${DEFAULT_COLOR}"
      return 1
    fi

    message "${MESSAGE_COLOR}Deleting grimoire ${FILE_COLOR}$grimoire" \
            "${MESSAGE_COLOR}directory${DEFAULT_COLOR}"
    rm -rf $grimoire &&
    message "${MESSAGE_COLOR}Removing grimoire ${FILE_COLOR}$grimoire" \
            "${MESSAGE_COLOR}from codex${DEFAULT_COLOR}" &&
    codex_remove_grimoire $grimoire ||
    { error_message "${PROBLEM_COLOR}An error occured trying to remove" \
                    "${FILE_COLOR}$grimoire${PROBLEM_COLOR}," \
                    "I wonder why?${DEFAULT_COLOR}"
      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
##
#---------------------------------------------------------------------
scribe_set(){
  local grim1=$1 grimoire1
  local grim2=$2 grimoire2
  local idx1 idx2
  if [ $grim1 == $grim2 ] ; then
    error_message "${SPELL_COLOR}$grim1 ${PROBLEM_COLOR}IS" \
                  "${SPELL_COLOR}$grim2${PROBLEM_COLOR}," \
                  "can't set a grimoire to itself!${DEFAULT_COLOR}"
    return 1
  fi
  if ! codex_find_grimoire $grim1 grimoire1 idx1 ; then
    error_message "${PROBLEM_COLOR}Grimoire ${SPELL_COLOR}$grim1" \
                  "${PROBLEM_COLOR}not found!${DEFAULT_COLOR}"

    return 1
  fi
  if ! codex_find_grimoire $grim2 grimoire2 idx2 ; then
    error_message "${PROBLEM_COLOR}Grimoire ${SPELL_COLOR}$grim2" \
                  "${PROBLEM_COLOR}not found!${DEFAULT_COLOR}"
    return 1
  fi
  if [ $idx1 -eq $idx2 ] ; then
    error_message "${PROBLEM_COLOR}This shouldn't happen, but if it" \
                  "does contact the sorcery team.${DEFAULT_COLOR}"
    return 1
  elif [ $idx1 -lt $idx2 ] ; then
    let idx2-- # grim1 is before grim2, by removing grim1, the position
               # decrease by 1
  fi

  message "${MESSAGE_COLOR}Setting ${SPELL_COLOR}$grim1" \
          "${MESSAGE_COLOR}before ${SPELL_COLOR}$grim2${DEFAULT_COLOR}"
  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
##
#---------------------------------------------------------------------
scribe_swap() {
  local grim1=$1 grimoire1
  local grim2=$2 grimoire2
  local idx1 idx2
  if ! codex_find_grimoire $grim1 grimoire1 idx1 ; then
    error_message "${PROBLEM_COLOR}Grimoire ${SPELL_COLOR}$grim1" \
                  "${PROBLEM_COLOR}not found!${DEFAULT_COLOR}"
    return 1
  fi
  if ! codex_find_grimoire $grim2 grimoire2 idx2 ; then
    error_message "${PROBLEM_COLOR}Grimoire ${SPELL_COLOR}$grim2" \
                  "${PROBLEM_COLOR}not found!${DEFAULT_COLOR}"
    return 1
  fi
  message -n "${MESSAGE_COLOR}Swapping "
  message "${SPELL_COLOR}$grim1 ${MESSAGE_COLOR}with" \
          "${SPELL_COLOR}$grim2 ${DEFAULT_COLOR}"
  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
##
#---------------------------------------------------------------------
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
        error_message "${PROBLEM_COLOR}Grimoire ${SPELL_COLOR}$grim" \
                      "${PROBLEM_COLOR}not found!${DEFAULT_COLOR}"
        if [[ "$2" == "from" ]] ; then shift 3 ; else  shift 1 ; fi
        let rc++
        continue
      fi

      if ! [ -e $grimoire/GRIMOIRE ] && ! scribe_fix_metadata $grim; then
        error_message "${SPELL_COLOR}$grim ${PROBLEM_COLOR}has invalid metadata." \
                      "Not Updating!${DEFAULT_COLOR}"
        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 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
        shift 3
      else
        from=$FROM_URL
        shift 1
      fi

      message "${MESSAGE_COLOR}Updating grimoire ${SPELL_COLOR}$grim${MESSAGE_COLOR}" \
              "from ${FILE_COLOR}$from${DEFAULT_COLOR}"
      scribe_add_update_worker "$grimoire" "$from" update ||
      let rc++
    done
  else
    #for each grimoire
    for grimoire in $(codex_get_all_grimoires); do
      smgl_basename "$grimoire" grim
      if ! [ -e $grimoire/GRIMOIRE ] && ! scribe_fix_metadata $grimoire; then
        #no metadata found. dont auto-update
        error_message "${SPELL_COLOR}$grim ${PROBLEM_COLOR}has invalid metadata." \
                      "Not Updating!${DEFAULT_COLOR}"
        continue
      fi

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

      #include metadata
      . $grimoire/GRIMOIRE
      message "${MESSAGE_COLOR}Updating grimoire ${SPELL_COLOR}$grim " \
              "${MESSAGE_COLOR}from ${FILE_COLOR}$FROM_URL${DEFAULT_COLOR}"
      scribe_add_update_worker "$grimoire" "$FROM_URL" update ||
      let rc++
    done
  fi
  tablet_import_repair_files $TABLET_PATH
  return $rc
}

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

#---------------------------------------------------------------------
## find_function
##
## take the command line and figure out what the user wants to do
##
## @param command line args
##
#---------------------------------------------------------------------
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";;
               reindex-keyword) FUNCTION="reindex-keyword";;
               reindex-version) FUNCTION="reindex-version";;
      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.
#---------------------------------------------------------------------
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 $@ ;;
reindex-keyword) scribe_reindex_keyword $@ ;;
reindex-version) scribe_reindex_version $@ ;;
        remove) scribe_remove $@ ;;
           set) scribe_set $@ ;;
          swap) scribe_swap $@ ;;
    unlocalize) scribe_unlocalize $@ ;;
        update) scribe_update $@ ;;
             *) help ;;
  esac
}

. /etc/sorcery/config

# validate the parameters before potentially su-ing
find_function $*

#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
  init_hooks
  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 "$@")
  exec su -c "scribe $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
##
#---------------------------------------------------------------------
