#!/bin/bash
#---------------------------------------------------------------------
##
## @Synopsis Set of functions for dealing with a library of grimoires (spell books).
##
## A grimoire is a book containing one or more spells.  A codex
## is a collection of one or more grimoires.  There are functions
## for listing the available grimiores, listing spells in a grimoire,
## listing sections in a grimoire, etc.
##
## Note:  Each of the functions that returns a spell, section, or 
## grimoire returns the full path.  Functions that explicitly return 
## a spell I<name> or section I<name> do not return the full
## path.
##
## <br>grimoires<br>
##
## This section contains some notes on grimoires.
##
## <br>Grimoire Layout<br>
## The codex functions expect each grimoire to be a directory.
## Each directory entry in a grimoire directory is considered 
## to be a section.  All directory entries in a section are 
## considered to be a spell if they included an executable file
## named F<DETAILS>.
##
## <br>Multiple grimoires<br>
## 
## Multiple grimoires are specified by setting entries in the
## I<GRIMOIRE_DIR> array.  For example, to set two additional 
## grimoires, you would put something like the following in 
## your local SMGL grimoire file (F</etc/sorcery/local/grimoire>).
## <pre>
##     GRIMOIRE_DIR[1]=/path/to/alternate/grimoire
##     GRIMOIRE_DIR[2]=/path/to/other/alternate/grimoire
## </pre>
## Grimoires are processed/searched in increasing order starting 
## at index 0.  The SMGL configuration file provides the value for
## the default grimoire as I<GRIMOIRE_DIR[0]> or simply I<GRIMOIRE_DIR>.
##
## The following two lines show how to reorder the default 
## grimoire so that it's not searched first (in this example
## it will be searched second).
## <pre>
##     GRIMOIRE_DIR[1]=$GRIMOIRE
##     GRIMOIRE_DIR[0]=/path/to/grimoire/to/search/first
## </pre>
## There is no limitation on the number of grimoires that can be
## specified.
##
## It is also possible to add and remove grimoires using the 
## codex_add_grimoire and codex_remove_grimoire functions.
##
## @Copyright
##
## Copyright 2002 by the Source Mage Team
##
##
#---------------------------------------------------------------------

#####################GRIMOIRE FUNCTIONS###############################
#---------------------------------------------------------------------
## @param grimoire 
## @param lookup (optional)
## @return 0 if grimoire can be canonicalized
##
## Outputs a grimoire in canonical form (full path)
## if lookup equal to "lookup" look up the grimoire name
## else build the grimoire name from $CODEX_ROOT/$grimoire
## CODEX_ROOT is the default grimoire location
## NOTE: one can specify partial paths
#---------------------------------------------------------------------
function codex_canonicalize_grimoire_name() {
  local grimoire=$1
  if [ "${grimoire:0:1}" == "/" ] ; then
    # already a full path
    echo $1
    return 0
  fi

  if [ "$2" == "lookup" ] ; then
    codex_find_grimoire $1
  else
    echo "$CODEX_ROOT/$grimoire"
  fi
  # do nothing and preserve return code
}

function codex_is_canonicalized() {
  if [ ${1:0:1} != "/" ] ; then
    message "$1 is not canonicalized!!"
    message "If you see this please contact the sorcery team"
    return 1
  fi
}

#---------------------------------------------------------------------
## @param grimoire name, or path to one
## @param variable name, is set to the index number
## @Stdout the grimoire's path
## @return 0 there is a grimoire
## @return 1 there is no grimoire
## NOTE: one can specify partial paths such as codex/stable
## <pre>
## example1:
## codex_find_grimoire test; echo "returned $?"
## /var/lib/sorcery/codex/test
## returned 0
## example2:
## codex_find_grimoire sorcery/codex/stable; echo "returned $?"
## /var/lib/sorcery/codex/stable
## returned 0
## </pre>
##
#---------------------------------------------------------------------
function codex_find_grimoire() {
  local cfg_lookup=$1
  local cfg_grimoire
  local use_refs=no
  if [ $# -eq 3 ] ; then
    use_refs=yes
    local grim_var=$2
    local position_var=$3
  fi

  # prepend a / so things like "able" dont match
  # /var/lib/sorcery/codex/stable
  [[ "${cfg_lookup:0:1}" == "/" ]] || cfg_lookup="/$cfg_lookup"

  local cfg_idx
  let cfg_idx=0
  for cfg_grimoire in $(codex_get_all_grimoires); do
    # this will be empty if "$foo" matches the glob "*$bar"
    match=$(eval echo ${cfg_grimoire%%*$cfg_lookup})
    # if empty, echo and succeed
    if [ -z "$match" ] ; then
      if [ "$use_refs" == yes ] ; then
        eval "$position_var=\$cfg_idx"
        eval "$grim_var=\$cfg_grimoire"
      else
        echo $cfg_grimoire
      fi
      return 0
    fi
    let cfg_idx++
  done
  return 1
}

#---------------------------------------------------------------------
## @param grimoire 
## @param [position] 
## @param [overwrite]
## 
## Adds the specified grimoire to the list of grimoires.  If no 
## position is given, the grimoire is added to the end of the list.
## Position is 0 based.  Adding a grimoire to position 0 places it as
## the first grimoire in the list, and moves all other grimoires down
## one spot, unless [<overwrite>] is set to "overwrite".
##
## This function does not currently delete duplicate entries.
##
#---------------------------------------------------------------------
function codex_add_grimoire() {
  local  NEW_GRIMOIRE=$1
  local  POSITION=$2
  local  OVERWRITE=$3

  codex_is_canonicalized $NEW_GRIMOIRE || return 1

  local  GRIMOIRES=`codex_get_all_grimoires`

  local  CURRENT_GRIMOIRE
  local  GRIMOIRE_COUNT=0

  if [ -z "$POSITION"  ]; then
    # print everything, then print the new grimoire
    for  CURRENT_GRIMOIRE  in  $GRIMOIRES;  do
      GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE
      let GRIMOIRE_COUNT++
    done
    # decrement to overwrite
    if [ "$OVERWRITE" == "overwrite"  ]; then
      let GRIMOIRE_COUNT--
    fi
    GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$NEW_GRIMOIRE
    PAST_END=false
    # increment so the loop at end gets all the grimoires
    let GRIMOIRE_COUNT++
  else
    local  PAST_END=true
    if [ "$OVERWRITE" == "overwrite"  ] ; then
      # print everything, and overwrite at the right position
      for  CURRENT_GRIMOIRE  in  $GRIMOIRES;  do
        GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE
        if [  $POSITION -eq $GRIMOIRE_COUNT  ] ; then
          GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$NEW_GRIMOIRE
          PAST_END=false
        fi
        let GRIMOIRE_COUNT++
      done
    else
      for  CURRENT_GRIMOIRE  in  $GRIMOIRES;  do
        if [  $POSITION -eq $GRIMOIRE_COUNT  ] ; then
          GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$NEW_GRIMOIRE
          PAST_END=false
          let GRIMOIRE_COUNT++
          GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE
        else
          GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE
        fi
        let GRIMOIRE_COUNT++
      done
    fi
    # if the range is beyond the total number of grimoires add it now
    if [ "$PAST_END" == "true" ] ; then
      GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$NEW_GRIMOIRE
      let GRIMOIRE_COUNT++
    fi
  fi


  local i
  touch  $GRIMOIRE_LIST
  tGRIMOIRE_LIST=`lock_start_transaction $GRIMOIRE_LIST`
  rm $tGRIMOIRE_LIST
  for ((i=0; i<$GRIMOIRE_COUNT;i++)); do 
    echo GRIMOIRE_DIR[$i]=${GRIMOIRE_DIR[$i]} >> $tGRIMOIRE_LIST
  done
  lock_commit_transaction $GRIMOIRE_LIST

}


#---------------------------------------------------------------------
## @param grimoire
## 
## Removes the specified grimoire from the list of grimoires.  
##
#---------------------------------------------------------------------
function codex_remove_grimoire() {
  local  GRIMOIRE_TO_DELETE="$1"

  codex_is_canonicalized $GRIMOIRE_TO_DELETE || return 1

  local  GRIMOIRES=`codex_get_all_grimoires`

  tGRIMOIRE_LIST=`lock_start_transaction $GRIMOIRE_LIST`
  touch  $GRIMOIRE_LIST
  cp     $GRIMOIRE_LIST  $GRIMOIRE_LIST_BACKUP
  rm  -f $tGRIMOIRE_LIST   
  touch $tGRIMOIRE_LIST   

  local  CURRENT_GRIMOIRE
  local  GRIMOIRE_COUNT=0
  for  CURRENT_GRIMOIRE  in  $GRIMOIRES ;  do
    if  [  "$CURRENT_GRIMOIRE"  !=  "$GRIMOIRE_TO_DELETE"  ];  then
      echo  GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE  >>  $tGRIMOIRE_LIST
    fi  
    let GRIMOIRE_COUNT++
  done

  lock_commit_transaction $GRIMOIRE_LIST
  unset GRIMOIRE_DIR
  .  $GRIMOIRE_LIST 
}

#---------------------------------------------------------------------
## Removes duplicate entries from the GRIMOIRE_LIST. This parses from
## 0 on up, and leaves only the first instance of a grimoire found.
## All others are removed.
## Then reloads the list
##
#---------------------------------------------------------------------
function codex_remove_duplicates() {

  local  GRIMOIRES=`codex_get_all_grimoires`
  local  CURRENT_GRIMOIRE=0
  local  GRIMOIRE_COUNT=0
  local  SEEN_GRIMOIRES=""
  local  ALREADY_SEEN=""

 
  touch  $GRIMOIRE_LIST
  tGRIMOIRE_LIST=`lock_start_transaction $GRIMOIRE_LIST`
  rm  -f $tGRIMOIRE_LIST   


  for  CURRENT_GRIMOIRE  in  $GRIMOIRES;  do
    ALREADY_SEEN=""
    echo "$SEEN_GRIMOIRES" | grep "$CURRENT_GRIMOIRE" && ALREADY_SEEN="yes"

    if [[  $ALREADY_SEEN  ]]; then
      SEEN_GRIMOIRES="$SEEN_GRIMOIRES $CURRENT_GRIMOIRE"
      echo "GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE" >> $tGRIMOIRE_LIST
      let GRIMOIRE_COUNT++
    fi

  done

  lock_commit_transaction $GRIMOIRE_LIST

  unset GRIMOIRE_DIR
  .  $GRIMOIRE_LIST 

}

#---------------------------------------------------------------------
## @param grimoire name | grimoire dir ....
## 
## Unsets the list of grimoires that existed before the call, then
## sets the lists of grimoires to be equal to the list of grimoires
## in the argument list. Grimoire names need not be canonicalized
##
#---------------------------------------------------------------------
function codex_set_grimoires() {

  local NEW_GRIMOIRE
  local GRIMOIRE_COUNT=0
  for NEW_GRIMOIRE in "$@";  do
    NEW_GRIMOIRE=$(codex_canonicalize_grimoire_name $NEW_GRIMOIRE lookup) &&
    test -d $NEW_GRIMOIRE ||
    { message "WARNING $NEW_GRIMOIRE does not exist!! skipping..."
      continue
    }
    NEW_GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$NEW_GRIMOIRE
    let GRIMOIRE_COUNT++
  done
  unset GRIMOIRE_DIR
  let GRIMOIRE_COUNT--
  for ((; $GRIMOIRE_COUNT>=0;GRIMOIRE_COUNT--)) ; do
    GRIMOIRE_DIR[$GRIMOIRE_COUNT]=${NEW_GRIMOIRE_DIR[$GRIMOIRE_COUNT]}
  done
}


#---------------------------------------------------------------------
##
## @Stdout all grimoires in the codex. 
##
#---------------------------------------------------------------------
function codex_get_all_grimoires() {
  debug "libcodex" "codex_get_all_grimoires()"
  echo "${GRIMOIRE_DIR[*]}" | tr '[:blank:]' '\n'
  return $?
}


#####################SECTION FUNCTIONS###############################


#---------------------------------------------------------------------
## @param section
## @return 0 if section is found
## @return 1 if section is not found
##
## @Stdout full path to the section
## Given a valid section name, this function lists the full path to 
## the section.  If an invalid section name is provided, nothing is 
## listed.
##
#---------------------------------------------------------------------
function codex_find_section_by_name() {
  local SECTION_NAME="$1"
  local GRIMOIRE=''
  
  for GRIMOIRE in `codex_get_all_grimoires`; do
    if [ -d "$GRIMOIRE/$SECTION_NAME" ] ; then
      debug "libcodex" "codex_find_section_by_name() - found section $GRIMOIRE/$SECTION_NAME"
      echo "$GRIMOIRE/$SECTION_NAME"
      return 0
    fi
  done
  return 1
}


#---------------------------------------------------------------------
##
## @Stdout all section names from all grimoires.
##
#---------------------------------------------------------------------
function codex_get_all_section_names() {
  codex_get_all_sections | get_basenames
}


#---------------------------------------------------------------------
## @param grimoire-pathes (optional)
##
## @Stdout all sections from all grimoires or only from the specified grimoires.
##
#---------------------------------------------------------------------
function codex_get_all_sections() {
  if [[ $# -gt 0 ]]; then
    codex_get_sections $@
  else
    codex_get_sections `codex_get_all_grimoires`
  fi
}

#---------------------------------------------------------------------
## @param grimoire
## 
## @Stdout Lists all section names in the specified grimoire.
## Relies on a wider-scope function <@function codex_get_sections>.
##
#---------------------------------------------------------------------
function codex_get_section_names() {
  codex_is_canonicalized $1 || return 1
  codex_get_sections "$1" | get_basenames
}

#---------------------------------------------------------------------
## @param canonicalized grimoire names
##
## @Stdout Lists all sections in the specified grimoire directories.
##
#---------------------------------------------------------------------
function codex_get_sections() { 
  debug "libcodex" "codex_get_sections() - $@"
  local GRIMOIRE

  while [ $# -gt 0 ] ; do
    # sanity check
    codex_is_canonicalized $1 || return 1

    # ensure there is a cache
    codex_check_cache $1
    GRIMOIRE="$1"

    # comes in the format
    # spellname /path/to/section
    cut -d' ' -f2 "$GRIMOIRE/$SPELL_INDEX_FILE" | sort | uniq
    shift
  done 
}


###########################SPELL FUNCTIONS############################

#---------------------------------------------------------------------
## @param full directory
##
## @return 0 if the specified directory is a spell directory.
## @return 1 otherwise
##
#---------------------------------------------------------------------
function codex_is_directory_a_spell() {
  [ -x "$1/DETAILS" ]
}


#---------------------------------------------------------------------
## @param spell 
## @param [spell ...]
##
## @return 0 if all the specified spells exist
## @return 1 othterwise
##
#---------------------------------------------------------------------
function codex_does_spell_exist() {
  local i
  local retValue=0
  for i in $* ; do
    if ! [[ `codex_find_spell_by_name $i` ]] ; then
      message "${SPELL_COLOR}$i${PROBLEM_COLOR} is not a spell!${DEFAULT_COLOR}"
	  retValue=1
    fi
  done
  return $retValue
}



#---------------------------------------------------------------------
## @param spell name
## @Stdout spell name
## Given a valid spell name, this function lists the full path to the 
## spell.  If an invalid spell name is provided, nothing is listed.
##
#---------------------------------------------------------------------
function codex_find_spell_by_name() {

  debug "libcodex" "codex_find_spell_by_name - $*"
  
  codex_cache_spell_lookup $1 `codex_get_all_grimoires`
  
}

#---------------------------------------------------------------------
## @param path/section
##
## @Stdout spells
## Lists full paths to spells in the specified section.
## Nothing is listed if the section doesn't include any spells.
##
#---------------------------------------------------------------------
function codex_get_spells_in_section() {
  debug "libcodex" "codex_get_spells_in_section - $*"
  codex_is_canonicalized $1 || return 1
  local section=`basename $1`
  local index="`dirname $1`/$SPELL_INDEX_FILE"
  if ! test -r $index || ! test -s $index; then
    message "${PROBLEM_COLOR}${section:-<null>} is not a section directory!${DEFAULT_COLOR}"
      return 1
  fi
  grep "/$section$" < $index | awk '{ printf("%s/%s\n",$2,$1); }'

}


#---------------------------------------------------------------------
## @param path/section
##
## @Stdout spells 
## Lists all spell names in the specified section.  Nothing is listed 
## if the section doesn't include any spells.
## 
#---------------------------------------------------------------------
function codex_get_spell_names() {
  codex_get_spells_in_section "$1" | get_basenames
}


#---------------------------------------------------------------------
## @param  grimoire-pathes (optional)
##
## @Stdout spells
## Lists all spells in all grimoires or only from the specified 
## grimoires. Nothing is listed if no spells exist in any of grimoires. 
##
##
## NOTE: This should be fixed so only the first of duplicate spells 
##  are listed.
##
#---------------------------------------------------------------------
function codex_get_all_spells() {
  local section
  for section in `codex_get_all_sections $@`; do
    codex_get_spells_in_section $section
  done
}

#---------------------------------------------------------------------
## @param spell name
##
## @Stdout spell name
## Lists the section of the given spell name.  Nothing is listed if 
## there are no spells with the given name.
##
#---------------------------------------------------------------------
function codex_get_spell_section() {
  codex_find_spell_by_name "$1" | get_dirnames
}


#---------------------------------------------------------------------
## @param spell name
##
## @Stdout section name
##
## Given a spell name, this function lists the section name.  If there
## are no spells with the given name, nothing is listed.
##
#---------------------------------------------------------------------
function codex_get_spell_section_name() {
  codex_find_spell_by_name "$1" | get_dirnames | get_basenames
}



#---------------------------------------------------------------------
## 
## @Globals GRIMOIRE SECTION SECTION_DIRECTORY SPELL SPELL_DIRECTORY SCRIPT_DIRECTORY SPELL_DESCRIPTION VERSION SHORT UPDATED SOURCE WEB_SITE ENTERRED MAINTAINER MD5 LICENSE
## Unets all these global variables. 
##
#---------------------------------------------------------------------
function codex_clear_current_spell()  {
  unset GRIMOIRE SECTION SECTION_DIRECTORY SPELL  \
  SPELL_DIRECTORY SCRIPT_DIRECTORY SPELL_DESCRIPTION \
  VERSION SHORT UPDATED SOURCE WEB_SITE ENTERRED MAINTAINER \
  MD5 LICENSE BUILD_API
}

#---------------------------------------------------------------------
## @param spell directory
## @Globals All vars set in a spell
## Sets the GRIMOIRE, SECTION, SECTION_DIRECTORY, SPELL_DIRECTORY, 
## SCRIPT_DIRECTORY, and SPELL_DESCRIPTION global variables for the 
## given spell directory.
##
## Assumes the directory passed in is a valid spell directory.
##
#---------------------------------------------------------------------
function codex_set_current_spell()  {

  debug "libcodex" "runing codex_set_current_spell"
  codex_clear_current_spell
  SPELL_DIRECTORY=$1

  # Directories
  SCRIPT_DIRECTORY=$SPELL_DIRECTORY
  # scribbled spells are self contained, SECTION_DIRECTORY and GRIMOIRE
  # point to slightly different places
  if test -e $SPELL_DIRECTORY/SCRIBBLED ; then
    # I dont know how to nest these
    local tmp=${SPELL_DIRECTORY%/*}
    SECTION=${tmp##*/}
    GRIMOIRE_NAME=$(basename ${tmp%/*})

    SECTION_DIRECTORY=${SPELL_DIRECTORY}/section
    GRIMOIRE=${SPELL_DIRECTORY}/grimoire
  else
    SECTION_DIRECTORY=${SPELL_DIRECTORY%/*}
    SECTION=${SECTION_DIRECTORY##*/}
    GRIMOIRE=${SECTION_DIRECTORY%/*}
    GRIMOIRE_NAME=$(basename $GRIMOIRE)
  fi

  # Names
  SPELL=${SPELL_DIRECTORY##*/}
  
  SPELL_CONFIG="$DEPENDS_CONFIG/$SPELL"
  if  [ -f  $SPELL_CONFIG  ]; then
    .  $SPELL_CONFIG > /dev/null  2> /dev/null
  fi

  debug "libcodex" "looking around for API_VERSION"
  [[ -x $GRIMOIRE/API_VERSION ]] && . $GRIMOIRE/API_VERSION
  [[ -x $SECTION_DIRECTORY/API_VERSION ]] && . $SECTION_DIRECTORY/API_VERSION

  debug "libcodex" "sourcing DETAILS"
  persistent_load
  .  $SPELL_DIRECTORY/DETAILS 1>/dev/null 2>&1
  persistent_clear

  # load compatibility functions needed for any stage of cast
  load_libcompat

  # set a default build api if there isn't one already
  # this isn't strictly necessary as other code should be able to handle the
  # lack of this variable, but I want to play it safe.
  [[ -z $BUILD_API ]] && BUILD_API=1
  true
}

function codex_set_current_spell_quick()  {
  SPELL_DIRECTORY=$1
  SCRIPT_DIRECTORY=$1
  if test -e $SPELL_DIRECTORY/SCRIBBLED ; then
    # I dont know how to nest these
    local tmp=${SPELL_DIRECTORY%/*}
    SECTION=${tmp##*/}
    GRIMOIRE_NAME=$(basename ${tmp%/*})

    SECTION_DIRECTORY=${SPELL_DIRECTORY}/section
    GRIMOIRE=${SPELL_DIRECTORY}/grimoire
  else
    SECTION_DIRECTORY=${SPELL_DIRECTORY%/*}
    SECTION=${SECTION_DIRECTORY##*/}
    GRIMOIRE=${SECTION_DIRECTORY%/*}
    GRIMOIRE_NAME=$(basename $GRIMOIRE)
  fi

  # Names
  SPELL=${SPELL_DIRECTORY##*/}
  SPELL=${1##*/}
  SPELL_CONFIG="$DEPENDS_CONFIG/$SPELL"
  test -f  "$SPELL_CONFIG" && .  "$SPELL_CONFIG" &> /dev/null
  persistent_load
  .  $1/DETAILS &> /dev/null
  persistent_clear
  true
}

#---------------------------------------------------------------------
## @param spell name
## Sets the GRIMOIRE, SECTION, SECTION_DIRECTORY, SPELL_DIRECTORY, 
## SCRIPT_DIRECTORY, and SPELL_DESCRIPTION global variables for the 
## given spell name.
## 
## @return 1 if the given name is not a spell.
##
#---------------------------------------------------------------------
function codex_set_current_spell_by_name()  {
  debug "libcodex" "codex_set_current_spell_by_name -- $1"
  local SPELL_NAME=`codex_find_spell_by_name "$1"`
  debug "libcodex" $SPELL_NAME

  [  -n  "$SPELL_NAME"  ]  &&  codex_set_current_spell  $SPELL_NAME
}


##############################CACHE FUNCTIONS#########################
#---------------------------------------------------------------------
## @param spell name
## @param grimoire-path 
## @param [grimoire-path ...]
## @return 0 Spell found
## @return 1 Spell not found
## Searches the indicies of the specified grimories for a spell.
## This will return only the first match found.
##
#---------------------------------------------------------------------
function codex_cache_spell_lookup()  {
  local SECTION
  local spell="$1"
  shift
  while [ $# -gt 0 ] ; do
   debug "libcodex" "looking up $spell in ${1}'s cache"
    codex_check_cache $1
    SECTION=`grep -m 1 "^$spell " $1/$SPELL_INDEX_FILE | cut -d' ' -f2`
    [[ $SECTION ]] && echo "$SECTION/$spell" && return
    shift
  done
  return 1
}


#---------------------------------------------------------------------
## @param grimoire-path
## @Stdout error if cache doesn't exist after creation
## Checks that the cache exists. if it doesn't exist, make it.
## If it still doesn't exist, the barf an error
##
#---------------------------------------------------------------------
function codex_check_cache()  {
  codex_is_canonicalized $1 || return 1
  if ! [ -f $1/$SPELL_INDEX_FILE ] || ! [ -f $1/$PROVIDE_INDEX_FILE ]; then
      codex_create_cache
  fi
  if ! [ -f $1/$SPELL_INDEX_FILE ] || ! [ -f $1/$PROVIDE_INDEX_FILE ]; then
    message "${PROBLEM_COLOR}Eeek, $2 is not a grimoire!${DEFAULT_COLOR}" >&2
    exit 1
  fi
}

#---------------------------------------------------------------------
## @param [grimoire-path]
## Creates the cache/index for the specified grimoire, or all if none
## is asked for.
##
#---------------------------------------------------------------------
function codex_create_cache()  {
	
	debug "libcodex" "codex_create_cache - $*"
	local list="$*"
	[[ $list ]] || list="${GRIMOIRE_DIR[*]}"

	for i in $list ; do
        codex_find_in_grimoire $i "DETAILS" | \
			sed 's@\(.*\)/\([^/]*\)/DETAILS@\2 \1@' |	\
			sort > $i/$SPELL_INDEX_FILE

        codex_list_provides $i > "$i/$PROVIDE_INDEX_FILE"
	done

}

###########################MISC FUNCTIONS#############################

#---------------------------------------------------------------------
## @param grimoire-path 
## @param file-name
##
## @Stdout file names
## Prints every file matching file-name in grimoire
##
#---------------------------------------------------------------------
function codex_find_in_grimoire () {
    find "$1" -follow -maxdepth 3 -mindepth 3 -perm +700 -name "$2"
}

#---------------------------------------------------------------------
## @param grimoire-path
## @Stdout provides spell
## Lists all providers in grimoire in the form of "provides spell"
## for instance: <br>
## shell /home/martin/p4/grimoire/shell-term-fm/sash
## 
#---------------------------------------------------------------------
function codex_list_provides () {
    grimoire=$1

    for file in $(codex_find_in_grimoire $grimoire "PROVIDES"); do
        spell=$(dirname $file)
        for provides in $(gawk '{if (/provides/) { print $2 } 
                                 else { print $1 }}' $file); do
            echo "$provides $spell"
        done
    done | sort
}

#---------------------------------------------------------------------
## @param servicename
## @return 0 if service exists
## @return 1 otherwise
## checks if servicename exists.
#---------------------------------------------------------------------
function codex_does_service_exist()
{
   local SERVICE="$@" 
    for GRIMOIRE in $(codex_get_all_grimoires); do
        grep -qE '^'$SERVICE' ' "$GRIMOIRE/$PROVIDE_INDEX_FILE" && return 0
    done
}   


#---------------------------------------------------------------------
## @param spell or section name
## @Globals CODEX_FOUND_SECTION CODEX_FOUND_SPELL
## @return 0 if the passed argument is a spell name or a section name.
## @return 1 otherwise
## CODEX_FOUND_SECTION is set if section was found
## CODEX_FOUND_SPELL   is set if a spell was found
##
#---------------------------------------------------------------------
function codex_find_spell_or_section_by_name()  {
    local SPELL_OR_SECTION="$1"

    CODEX_FOUND_SECTION=""
    CODEX_FOUND_SPELL=""

    CODEX_FOUND_SECTION=`codex_find_section_by_name $SPELL_OR_SECTION`
    [  -n  "$CODEX_FOUND_SECTION"  ]  &&  return

    CODEX_FOUND_SPELL=`codex_find_spell_by_name $SPELL_OR_SECTION`
    [  -n  "$CODEX_FOUND_SPELL"  ]    &&  return

    return 1
}

#---------------------------------------------------------------------
## @param spell directory
## @Stdout spell description 
## Echos the long description of the given spell.  Returns an
## empty string if the directory is not a valid spell.
##
#---------------------------------------------------------------------
function codex_get_spell_description()  {
  codex_is_directory_a_spell  "$1"  &&  .  "$1/DETAILS"
}

#---------------------------------------------------------------------
## @param spell directory
## @Stdout keywords
## Echos the keywords of the given spell.  Returns an
## empty string if the directory is not a valid spell.
##
#---------------------------------------------------------------------
function codex_get_spell_keywords()  {
  if codex_is_directory_a_spell  "$1"; then
    if [ -e "$1/KEYWORDS" ]; then 
      cat  "$1/KEYWORDS"
    fi
  fi
}

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