/var/lib/sorcery/modules/libcodex

     1	#!/bin/bash
     2	#---------------------------------------------------------------------
     3	##
     4	## @Synopsis Set of functions for dealing with a library of grimoires (spell books).
     5	##
     6	## A grimoire is a book containing one or more spells.  A codex
     7	## is a collection of one or more grimoires.  There are functions
     8	## for listing the available grimiores, listing spells in a grimoire,
     9	## listing sections in a grimoire, etc.
    10	##
    11	## Note:  Each of the functions that returns a spell, section, or
    12	## grimoire returns the full path.  Functions that explicitly return
    13	## a spell I<name> or section I<name> do not return the full
    14	## path.
    15	##
    16	## <br>grimoires<br>
    17	##
    18	## This section contains some notes on grimoires.
    19	##
    20	## <br>Grimoire Layout<br>
    21	## The codex functions expect each grimoire to be a directory.
    22	## Each directory entry in a grimoire directory is considered
    23	## to be a section.  All directory entries in a section are
    24	## considered to be a spell if they included an executable file
    25	## named F<DETAILS>.
    26	##
    27	## <br>Multiple grimoires<br>
    28	##
    29	## Multiple grimoires are specified by setting entries in the
    30	## I<GRIMOIRE_DIR> array.  For example, to set two additional
    31	## grimoires, you would put something like the following in
    32	## your local SMGL grimoire file (F</etc/sorcery/local/grimoire>).
    33	## <pre>
    34	##     GRIMOIRE_DIR[1]=/path/to/alternate/grimoire
    35	##     GRIMOIRE_DIR[2]=/path/to/other/alternate/grimoire
    36	## </pre>
    37	## Grimoires are processed/searched in increasing order starting
    38	## at index 0.  The SMGL configuration file provides the value for
    39	## the default grimoire as I<GRIMOIRE_DIR[0]> or simply I<GRIMOIRE_DIR>.
    40	##
    41	## The following two lines show how to reorder the default
    42	## grimoire so that it's not searched first (in this example
    43	## it will be searched second).
    44	## <pre>
    45	##     GRIMOIRE_DIR[1]=$GRIMOIRE
    46	##     GRIMOIRE_DIR[0]=/path/to/grimoire/to/search/first
    47	## </pre>
    48	## There is no limitation on the number of grimoires that can be
    49	## specified.
    50	##
    51	## It is also possible to add and remove grimoires using the
    52	## codex_add_grimoire and codex_remove_grimoire functions.
    53	##
    54	## @Copyright
    55	##
    56	## Copyright 2002 by the Source Mage Team
    57	##
    58	##
    59	#---------------------------------------------------------------------
    60	
    61	#####################GRIMOIRE FUNCTIONS###############################
    62	#---------------------------------------------------------------------
    63	## @param grimoire
    64	## @param lookup (optional)
    65	## @param variable to set (optional)
    66	## @return 0 if grimoire can be canonicalized
    67	##
    68	## Outputs a grimoire in canonical form (full path)
    69	## if lookup equal to "lookup" look up the grimoire name
    70	## else build the grimoire name from $CODEX_ROOT/$grimoire
    71	## CODEX_ROOT is the default grimoire location
    72	## NOTE: one can specify partial paths
    73	#---------------------------------------------------------------------
    74	function codex_canonicalize_grimoire_name() {
    75	  local grimoire=$1
    76	  if [ "${grimoire:0:1}" == "/" ] ; then
    77	    # already a full path
    78	    if [[ -z $3 ]]; then
    79	      echo $grimoire
    80	    else
    81	      eval "$3=\"\$grimoire\""
    82	    fi
    83	    return 0
    84	  fi
    85	
    86	  if [ "$2" == "lookup" ] ; then
    87	    if [[ -z $3 ]]; then
    88	      codex_find_grimoire $grimoire
    89	    else
    90	      local grim idx
    91	      codex_find_grimoire $grimoire grim idx
    92	      eval "$3=\"\$grim\""
    93	    fi
    94	  else
    95	    if [[ -z $3 ]]; then
    96	      echo "$CODEX_ROOT/$grimoire"
    97	    else
    98	      eval "$3=\"\$CODEX_ROOT/\$grimoire\""
    99	    fi
   100	  fi
   101	  # do nothing and preserve return code
   102	}
   103	
   104	function codex_is_canonicalized() {
   105	  if [ ${1:0:1} != "/" ] ; then
   106	    message "$1 is not canonicalized!!"
   107	    message "If you see this please contact the sorcery team"
   108	    return 1
   109	  fi
   110	}
   111	
   112	#---------------------------------------------------------------------
   113	## @param grimoire name, or path to one
   114	## @param variable name, is set to the matching grimoire (optional)
   115	## @param variable name, is set to the matching grimoire's index (optional)
   116	## @Stdout the grimoire's path
   117	## @return 0 there is a grimoire
   118	## @return 1 there is no grimoire
   119	## NOTE: one can specify partial paths such as codex/stable
   120	## <pre>
   121	## example1:
   122	## codex_find_grimoire test; echo "returned $?"
   123	## /var/lib/sorcery/codex/test
   124	## returned 0
   125	## example2:
   126	## codex_find_grimoire sorcery/codex/stable; echo "returned $?"
   127	## /var/lib/sorcery/codex/stable
   128	## returned 0
   129	## </pre>
   130	##
   131	#---------------------------------------------------------------------
   132	function codex_find_grimoire() {
   133	  local cfg_lookup=$1
   134	  local cfg_grimoire
   135	  local use_refs=no
   136	  if [ $# -eq 3 ] ; then
   137	    use_refs=yes
   138	    local grim_var=$2
   139	    local position_var=$3
   140	  fi
   141	
   142	  # prepend a / so things like "able" dont match
   143	  # /var/lib/sorcery/codex/stable
   144	  [[ "${cfg_lookup:0:1}" == "/" ]] || cfg_lookup="/$cfg_lookup"
   145	
   146	  local cfg_idx
   147	  let cfg_idx=0
   148	  for cfg_grimoire in $(codex_get_all_grimoires) $CODEX_ROOT/*; do
   149	    [[ -d $cfg_grimoire ]] || continue
   150	    # this will be empty if "$foo" matches the glob "*$bar"
   151	    match=$(eval echo ${cfg_grimoire%%*$cfg_lookup})
   152	    # if empty, echo and succeed
   153	    if [ -z "$match" ] ; then
   154	      if [ "$use_refs" == yes ] ; then
   155	        eval "$position_var=\$cfg_idx"
   156	        eval "$grim_var=\$cfg_grimoire"
   157	      else
   158	        echo $cfg_grimoire
   159	      fi
   160	      return 0
   161	    fi
   162	    let cfg_idx++
   163	  done
   164	  return 1
   165	}
   166	
   167	#---------------------------------------------------------------------
   168	## @param grimoire
   169	## @param [position]
   170	## @param [overwrite]
   171	##
   172	## Adds the specified grimoire to the list of grimoires.  If no
   173	## position is given, the grimoire is added to the end of the list.
   174	## Position is 0 based.  Adding a grimoire to position 0 places it as
   175	## the first grimoire in the list, and moves all other grimoires down
   176	## one spot, unless [<overwrite>] is set to "overwrite".
   177	##
   178	## This function does not currently delete duplicate entries.
   179	##
   180	#---------------------------------------------------------------------
   181	function codex_add_grimoire() {
   182	  local  NEW_GRIMOIRE=$1
   183	  local  POSITION=$2
   184	  local  OVERWRITE=$3
   185	
   186	  codex_is_canonicalized $NEW_GRIMOIRE || return 1
   187	
   188	  local  GRIMOIRES=`codex_get_all_grimoires`
   189	
   190	  local  CURRENT_GRIMOIRE
   191	  local  GRIMOIRE_COUNT=0
   192	
   193	  if [ -z "$POSITION"  ]; then
   194	    # print everything, then print the new grimoire
   195	    for  CURRENT_GRIMOIRE  in  $GRIMOIRES;  do
   196	      GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE
   197	      let GRIMOIRE_COUNT++
   198	    done
   199	    # decrement to overwrite
   200	    if [ "$OVERWRITE" == "overwrite"  ]; then
   201	      let GRIMOIRE_COUNT--
   202	    fi
   203	    GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$NEW_GRIMOIRE
   204	    PAST_END=false
   205	    # increment so the loop at end gets all the grimoires
   206	    let GRIMOIRE_COUNT++
   207	  else
   208	    local  PAST_END=true
   209	    if [ "$OVERWRITE" == "overwrite"  ] ; then
   210	      # print everything, and overwrite at the right position
   211	      for  CURRENT_GRIMOIRE  in  $GRIMOIRES;  do
   212	        GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE
   213	        if [  $POSITION -eq $GRIMOIRE_COUNT  ] ; then
   214	          GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$NEW_GRIMOIRE
   215	          PAST_END=false
   216	        fi
   217	        let GRIMOIRE_COUNT++
   218	      done
   219	    else
   220	      for  CURRENT_GRIMOIRE  in  $GRIMOIRES;  do
   221	        if [  $POSITION -eq $GRIMOIRE_COUNT  ] ; then
   222	          GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$NEW_GRIMOIRE
   223	          PAST_END=false
   224	          let GRIMOIRE_COUNT++
   225	          GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE
   226	        else
   227	          GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE
   228	        fi
   229	        let GRIMOIRE_COUNT++
   230	      done
   231	    fi
   232	    # if the range is beyond the total number of grimoires add it now
   233	    if [ "$PAST_END" == "true" ] ; then
   234	      GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$NEW_GRIMOIRE
   235	      let GRIMOIRE_COUNT++
   236	    fi
   237	  fi
   238	
   239	
   240	  local i
   241	  touch  $GRIMOIRE_LIST
   242	  lock_start_transaction "$GRIMOIRE_LIST" tGRIMOIRE_LIST
   243	  rm $tGRIMOIRE_LIST
   244	  for ((i=0; i<$GRIMOIRE_COUNT;i++)); do
   245	    echo GRIMOIRE_DIR[$i]=${GRIMOIRE_DIR[$i]} >> $tGRIMOIRE_LIST
   246	  done
   247	  lock_commit_transaction $GRIMOIRE_LIST
   248	
   249	}
   250	
   251	
   252	#---------------------------------------------------------------------
   253	## @param grimoire
   254	##
   255	## Removes the specified grimoire from the list of grimoires.
   256	##
   257	#---------------------------------------------------------------------
   258	function codex_remove_grimoire() {
   259	  local  GRIMOIRE_TO_DELETE="$1"
   260	
   261	  codex_is_canonicalized $GRIMOIRE_TO_DELETE || return 1
   262	
   263	  local  GRIMOIRES=`codex_get_all_grimoires`
   264	
   265	  lock_start_transaction "$GRIMOIRE_LIST" tGRIMOIRE_LIST
   266	  touch  $GRIMOIRE_LIST
   267	  cp     $GRIMOIRE_LIST  $GRIMOIRE_LIST_BACKUP
   268	  rm  -f $tGRIMOIRE_LIST
   269	  touch $tGRIMOIRE_LIST
   270	
   271	  local  CURRENT_GRIMOIRE
   272	  local  GRIMOIRE_COUNT=0
   273	  for  CURRENT_GRIMOIRE  in  $GRIMOIRES ;  do
   274	    if  [  "$CURRENT_GRIMOIRE"  !=  "$GRIMOIRE_TO_DELETE"  ];  then
   275	      echo  GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE  >>  $tGRIMOIRE_LIST
   276	    fi
   277	    let GRIMOIRE_COUNT++
   278	  done
   279	
   280	  lock_commit_transaction $GRIMOIRE_LIST
   281	  unset GRIMOIRE_DIR
   282	  .  $GRIMOIRE_LIST
   283	}
   284	
   285	#---------------------------------------------------------------------
   286	## Removes duplicate entries from the GRIMOIRE_LIST. This parses from
   287	## 0 on up, and leaves only the first instance of a grimoire found.
   288	## All others are removed.
   289	## Then reloads the list
   290	##
   291	#---------------------------------------------------------------------
   292	function codex_remove_duplicates() {
   293	
   294	  local  GRIMOIRES=`codex_get_all_grimoires`
   295	  local  CURRENT_GRIMOIRE=0
   296	  local  GRIMOIRE_COUNT=0
   297	  local  SEEN_GRIMOIRES=""
   298	  local  ALREADY_SEEN=""
   299	
   300	
   301	  touch  $GRIMOIRE_LIST
   302	  lock_start_transaction "$GRIMOIRE_LIST" tGRIMOIRE_LIST
   303	  rm  -f $tGRIMOIRE_LIST
   304	
   305	
   306	  for  CURRENT_GRIMOIRE  in  $GRIMOIRES;  do
   307	    ALREADY_SEEN=""
   308	    real_list_find "$SEEN_GRIMOIRES" "$CURRENT_GRIMOIRE" && ALREADY_SEEN="yes"
   309	
   310	    if [[  $ALREADY_SEEN  ]]; then
   311	      SEEN_GRIMOIRES="$SEEN_GRIMOIRES $CURRENT_GRIMOIRE"
   312	      echo "GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$CURRENT_GRIMOIRE" >> $tGRIMOIRE_LIST
   313	      let GRIMOIRE_COUNT++
   314	    fi
   315	
   316	  done
   317	
   318	  lock_commit_transaction $GRIMOIRE_LIST
   319	
   320	  unset GRIMOIRE_DIR
   321	  .  $GRIMOIRE_LIST
   322	
   323	}
   324	
   325	#---------------------------------------------------------------------
   326	## @param grimoire name | grimoire dir ....
   327	##
   328	## Unsets the list of grimoires that existed before the call, then
   329	## sets the lists of grimoires to be equal to the list of grimoires
   330	## in the argument list. Grimoire names need not be canonicalized
   331	##
   332	#---------------------------------------------------------------------
   333	function codex_set_grimoires() {
   334	
   335	  local NEW_GRIMOIRE
   336	  local GRIMOIRE_COUNT=0
   337	  for NEW_GRIMOIRE in "$@";  do
   338	    codex_canonicalize_grimoire_name $NEW_GRIMOIRE lookup NEW_GRIMOIRE &&
   339	    test -d $NEW_GRIMOIRE ||
   340	    { message "WARNING $NEW_GRIMOIRE does not exist!! skipping..."
   341	      continue
   342	    }
   343	    NEW_GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$NEW_GRIMOIRE
   344	    let GRIMOIRE_COUNT++
   345	  done
   346	  unset GRIMOIRE_DIR
   347	  let GRIMOIRE_COUNT--
   348	  for ((; $GRIMOIRE_COUNT>=0;GRIMOIRE_COUNT--)) ; do
   349	    GRIMOIRE_DIR[$GRIMOIRE_COUNT]=${NEW_GRIMOIRE_DIR[$GRIMOIRE_COUNT]}
   350	  done
   351	}
   352	
   353	
   354	#---------------------------------------------------------------------
   355	##
   356	## @Stdout all grimoires in the codex.
   357	##
   358	#---------------------------------------------------------------------
   359	function codex_get_all_grimoires() {
   360	  debug "libcodex" "codex_get_all_grimoires()"
   361	  echo "${GRIMOIRE_DIR[*]}" | tr '[:blank:]' '\n'
   362	  return $?
   363	}
   364	
   365	#---------------------------------------------------------------------
   366	## @param grimoire-name
   367	##
   368	## returns true if the grimoire is set local, false otherwise.
   369	#---------------------------------------------------------------------
   370	function codex_is_local() {(
   371	  local g_name=$1 idx
   372	
   373	  codex_find_grimoire $g_name grimoire idx
   374	  if ! [[ $grimoire ]]; then
   375	    return 1
   376	  fi
   377	
   378	  . "$grimoire/GRIMOIRE"
   379	
   380	  [[ $CODEX_IS_LOCAL == "yes" ]]
   381	)}
   382	
   383	#####################SECTION FUNCTIONS###############################
   384	
   385	
   386	#---------------------------------------------------------------------
   387	## @param section
   388	## @param variable to set (optional)
   389	## @return 0 if section is found
   390	## @return 1 if section is not found
   391	##
   392	## @Stdout full path to the section
   393	## Given a valid section name, this function lists the full path to
   394	## the section.  If an invalid section name is provided, nothing is
   395	## listed.
   396	##
   397	#---------------------------------------------------------------------
   398	function codex_find_section_by_name() {
   399	  local SECTION_NAME="$1"
   400	  local GRIMOIRE=''
   401	
   402	  for GRIMOIRE in `codex_get_all_grimoires`; do
   403	    if [ -d "$GRIMOIRE/$SECTION_NAME" ] ; then
   404	      debug "libcodex" "codex_find_section_by_name() - found section $GRIMOIRE/$SECTION_NAME"
   405	      if [[ -z $2 ]]; then
   406	        echo "$GRIMOIRE/$SECTION_NAME"
   407	      else
   408	        eval "$2=\"\$GRIMOIRE/\$SECTION_NAME\""
   409	      fi
   410	      return 0
   411	    fi
   412	  done
   413	  return 1
   414	}
   415	
   416	
   417	#---------------------------------------------------------------------
   418	##
   419	## @Stdout all section names from all grimoires.
   420	##
   421	#---------------------------------------------------------------------
   422	function codex_get_all_section_names() {
   423	  codex_get_all_sections | get_basenames
   424	}
   425	
   426	
   427	#---------------------------------------------------------------------
   428	## @param grimoire-pathes (optional)
   429	##
   430	## @Stdout all sections from all grimoires or only from the specified grimoires.
   431	##
   432	#---------------------------------------------------------------------
   433	function codex_get_all_sections() {
   434	  if [[ $# -gt 0 ]]; then
   435	    codex_get_sections $@
   436	  else
   437	    codex_get_sections `codex_get_all_grimoires`
   438	  fi
   439	}
   440	
   441	#---------------------------------------------------------------------
   442	## @param grimoire
   443	##
   444	## @Stdout Lists all section names in the specified grimoire.
   445	## Relies on a wider-scope function <@function codex_get_sections>.
   446	##
   447	#---------------------------------------------------------------------
   448	function codex_get_section_names() {
   449	  codex_is_canonicalized $1 || return 1
   450	  codex_get_sections "$1" | get_basenames
   451	}
   452	
   453	#---------------------------------------------------------------------
   454	## @param canonicalized grimoire names
   455	##
   456	## @Stdout Lists all sections in the specified grimoire directories.
   457	##
   458	#---------------------------------------------------------------------
   459	function codex_get_sections() {
   460	  debug "libcodex" "codex_get_sections() - $@"
   461	  local GRIMOIRE
   462	
   463	  while [ $# -gt 0 ] ; do
   464	    # sanity check
   465	    codex_is_canonicalized $1 || return 1
   466	
   467	    # ensure there is a cache
   468	    codex_check_cache $1
   469	    GRIMOIRE="$1"
   470	
   471	    # comes in the format
   472	    # spellname /path/to/section
   473	    cut -d' ' -f2 "$GRIMOIRE/$SPELL_INDEX_FILE" | sort -u
   474	    shift
   475	  done
   476	}
   477	
   478	
   479	###########################SPELL FUNCTIONS############################
   480	
   481	#---------------------------------------------------------------------
   482	## @param full directory
   483	##
   484	## @return 0 if the specified directory is a spell directory.
   485	## @return 1 otherwise
   486	##
   487	#---------------------------------------------------------------------
   488	function codex_is_directory_a_spell() {
   489	  [ -x "$1/DETAILS" ]
   490	}
   491	
   492	
   493	#---------------------------------------------------------------------
   494	## @param spell
   495	## @param [spell ...]
   496	##
   497	## @return 0 if all the specified spells exist
   498	## @return 1 othterwise
   499	##
   500	#---------------------------------------------------------------------
   501	function codex_does_spell_exist() {
   502	  local i
   503	  local retValue=0
   504	  [[ "$#" -lt 1 ]] && return 1
   505	  for i in "$@" ; do
   506	    if [[ "$i" == "" ]] ; then
   507	      message "${PROBLEM_COLOR}Empty string is not a spell!${DEFAULT_COLOR}"
   508	      retValue=1
   509	    elif ! [[ $(codex_find_spell_by_name "$i") ]] ; then
   510	      message "${SPELL_COLOR}$i${DEFAULT_COLOR}${PROBLEM_COLOR} is not a spell!${DEFAULT_COLOR}"
   511	      retValue=1
   512	    fi
   513	  done
   514	  return $retValue
   515	}
   516	
   517	
   518	
   519	#---------------------------------------------------------------------
   520	## @param spell name
   521	## @Stdout spell name
   522	## Given a valid spell name, this function lists the full path to the
   523	## spell.  If an invalid spell name is provided, nothing is listed.
   524	##
   525	#---------------------------------------------------------------------
   526	function codex_find_spell_by_name() {
   527	
   528	  debug "libcodex" "codex_find_spell_by_name - $*"
   529	
   530	  codex_cache_spell_lookup $1 `codex_get_all_grimoires`
   531	
   532	}
   533	
   534	#---------------------------------------------------------------------
   535	## @param path/section
   536	##
   537	## @Stdout spells
   538	## Lists full paths to spells in the specified section.
   539	## Nothing is listed if the section doesn't include any spells.
   540	##
   541	#---------------------------------------------------------------------
   542	function codex_get_spells_in_section() {
   543	  debug "libcodex" "codex_get_spells_in_section - $*"
   544	  codex_is_canonicalized $1 || return 1
   545	  local section="$(smgl_basename $1)"
   546	  local index="$(smgl_dirname $1)/$SPELL_INDEX_FILE"
   547	  if ! test -r $index || ! test -s $index; then
   548	    message "${PROBLEM_COLOR}${section:-<null>} is not a section directory!${DEFAULT_COLOR}"
   549	      return 1
   550	  fi
   551	  grep "/$section$" < $index | awk '{ printf("%s/%s\n",$2,$1); }'
   552	
   553	}
   554	
   555	
   556	#---------------------------------------------------------------------
   557	## @param path/section
   558	##
   559	## @Stdout spells
   560	## Lists all spell names in the specified section.  Nothing is listed
   561	## if the section doesn't include any spells.
   562	##
   563	#---------------------------------------------------------------------
   564	function codex_get_spell_names() {
   565	  codex_get_spells_in_section "$1" | get_basenames
   566	}
   567	
   568	
   569	#---------------------------------------------------------------------
   570	## @param  grimoire-pathes (optional)
   571	##
   572	## @Stdout spells
   573	## Lists all spells in all grimoires or only from the specified
   574	## grimoires. Nothing is listed if no spells exist in any of grimoires.
   575	##
   576	##
   577	## @NOTE This should be fixed so only the first of duplicate spells
   578	## @NOTE are listed.
   579	##
   580	#---------------------------------------------------------------------
   581	function codex_get_all_spells() {
   582	  local section
   583	  for section in `codex_get_all_sections $@`; do
   584	    codex_get_spells_in_section $section
   585	  done
   586	}
   587	
   588	#---------------------------------------------------------------------
   589	## @param spell name
   590	##
   591	## @Stdout spell name
   592	## Lists the section of the given spell name.  Nothing is listed if
   593	## there are no spells with the given name.
   594	##
   595	#---------------------------------------------------------------------
   596	function codex_get_spell_section() {
   597	  codex_find_spell_by_name "$1" | get_dirnames
   598	}
   599	
   600	
   601	#---------------------------------------------------------------------
   602	## @param spell name
   603	##
   604	## @Stdout section name
   605	##
   606	## Given a spell name, this function lists the section name.  If there
   607	## are no spells with the given name, nothing is listed.
   608	##
   609	#---------------------------------------------------------------------
   610	function codex_get_spell_section_name() {
   611	  codex_find_spell_by_name "$1" | get_dirnames | get_basenames
   612	}
   613	
   614	
   615	
   616	#---------------------------------------------------------------------
   617	##
   618	## @Globals GRIMOIRE SECTION SECTION_DIRECTORY SPELL SPELL_DIRECTORY SCRIPT_DIRECTORY SPELL_DESCRIPTION VERSION SHORT UPDATED SOURCE WEB_SITE ENTERED MAINTAINER MD5 LICENSE
   619	## Unets all these global variables.
   620	##
   621	#---------------------------------------------------------------------
   622	function codex_clear_current_spell()  {
   623	  unset GRIMOIRE SECTION SECTION_DIRECTORY SPELL  \
   624	  SPELL_DIRECTORY SCRIPT_DIRECTORY SPELL_DESCRIPTION \
   625	  VERSION SHORT UPDATED SOURCE WEB_SITE ENTERED MAINTAINER \
   626	  MD5 LICENSE BUILD_API SECURITY_PATCH PATCHLEVEL
   627	}
   628	
   629	#---------------------------------------------------------------------
   630	## @param spell directory
   631	## @Globals All vars set in a spell
   632	## Sets the GRIMOIRE, SECTION, SECTION_DIRECTORY, SPELL_DIRECTORY,
   633	## SCRIPT_DIRECTORY global variables for the
   634	## given spell directory.
   635	##
   636	## Assumes the directory passed in is a valid spell directory.
   637	##
   638	#---------------------------------------------------------------------
   639	function codex_set_current_spell()  {
   640	
   641	  debug "libcodex" "runing codex_set_current_spell"
   642	
   643	  codex_clear_current_spell
   644	
   645	  codex_get_spell_paths $1
   646	
   647	  if  [ -f  $SPELL_CONFIG  ]; then
   648	    .  $SPELL_CONFIG > /dev/null  2> /dev/null
   649	  fi
   650	
   651	  debug "libcodex" "looking around for API_VERSION"
   652	  [[ -x $GRIMOIRE/API_VERSION ]] && . $GRIMOIRE/API_VERSION
   653	  [[ -x $SECTION_DIRECTORY/API_VERSION ]] && . $SECTION_DIRECTORY/API_VERSION
   654	
   655	  # load compatibility functions needed for any stage of cast
   656	  load_libcompat
   657	
   658	  debug "libcodex" "sourcing DETAILS"
   659	  persistent_load
   660	  .  $SPELL_DIRECTORY/DETAILS 1>/dev/null 2>&1
   661	  persistent_clear
   662	
   663	  STAGE_DIRECTORY=$BUILD_DIRECTORY/stage-$SPELL-$VERSION
   664	
   665	  # set a default build api if there isn't one already
   666	  # this isn't strictly necessary as other code should be able to handle the
   667	  # lack of this variable, but I want to play it safe.
   668	  [[ -z $BUILD_API ]] && BUILD_API=2
   669	  true
   670	}
   671	
   672	#---------------------------------------------------------------------
   673	## Load just the very basics for a spell, skip build api, libcompat
   674	## and other debug stuff. Intended for use in tight loops.
   675	#---------------------------------------------------------------------
   676	function codex_set_current_spell_quick()  {
   677	  codex_get_spell_paths $1
   678	
   679	  test -f  "$SPELL_CONFIG" && .  "$SPELL_CONFIG" &> /dev/null
   680	  persistent_load
   681	  .  $1/DETAILS &> /dev/null
   682	  persistent_clear
   683	  true
   684	}
   685	
   686	
   687	#---------------------------------------------------------------------
   688	## Setup the various spell paths associated with a spell without
   689	## loading it.
   690	#---------------------------------------------------------------------
   691	function codex_get_spell_paths() {
   692	  SPELL_DIRECTORY=$1
   693	  SCRIPT_DIRECTORY=$1
   694	  if test -e $SPELL_DIRECTORY/SCRIBBLED ; then
   695	    local tmp
   696	    smgl_dirname "$SPELL_DIRECTORY" tmp
   697	    smgl_basename "$tmp" SECTION
   698	    smgl_dirname "$tmp" tmp
   699	    smgl_basename "$tmp" GRIMOIRE_NAME
   700	
   701	    SECTION_DIRECTORY=${SPELL_DIRECTORY}/section
   702	    GRIMOIRE=${SPELL_DIRECTORY}/grimoire
   703	  else
   704	    smgl_dirname "$SPELL_DIRECTORY" SECTION_DIRECTORY
   705	    smgl_basename "$SECTION_DIRECTORY" SECTION
   706	    smgl_dirname "$SECTION_DIRECTORY" GRIMOIRE
   707	    smgl_basename "$GRIMOIRE" GRIMOIRE_NAME
   708	  fi
   709	  smgl_basename "$SPELL_DIRECTORY" SPELL
   710	  SPELL_CONFIG="$DEPENDS_CONFIG/$SPELL"
   711	  return 0
   712	}
   713	
   714	#---------------------------------------------------------------------
   715	## @param spell name
   716	## Sets the GRIMOIRE, SECTION, SECTION_DIRECTORY, SPELL_DIRECTORY,
   717	## SCRIPT_DIRECTORY global variables for the given spell name.
   718	##
   719	## @return 1 if the given name is not a spell.
   720	##
   721	#---------------------------------------------------------------------
   722	function codex_set_current_spell_by_name()  {
   723	  debug "libcodex" "codex_set_current_spell_by_name -- $1"
   724	  local SPELL_NAME=`codex_find_spell_by_name "$1"`
   725	  debug "libcodex" $SPELL_NAME
   726	
   727	  [  -n  "$SPELL_NAME"  ]  &&  codex_set_current_spell  $SPELL_NAME
   728	}
   729	
   730	#---------------------------------------------------------------------
   731	## @param spell name
   732	## Returns the features a spell is a provider off.
   733	## Example, firefox: GECKO, GRAPHICAL-WEB-BROWSER, WEB-BROWSER,
   734	##                   NS-PLUGIN-COMPATIBLE
   735	##
   736	#---------------------------------------------------------------------
   737	function codex_find_spell_provides() {
   738	  local spell="$1"
   739	  local feature grimoire
   740	  local features=$(
   741	    for grimoire in $(codex_get_all_grimoires);  do
   742	      gawk '/\/'$spell'$/ { print $1 }' \
   743	        "$grimoire/$PROVIDE_INDEX_FILE"
   744	    done | sort -u)
   745	
   746	  # filter out providers we dont want, so far this is:
   747	  # a) spells that do not provide something in the first grimoire they are
   748	  # found in (see note in find_providers)
   749	  local spell_path=$(codex_find_spell_by_name $spell)
   750	  smgl_dirname $(smgl_dirname $spell_path) grimoire
   751	  for feature in $features; do
   752	    # ensure the first place we look for the spell is a provider
   753	    grep -q "^$feature $spell_path$" $grimoire/$PROVIDE_INDEX_FILE &&
   754	      echo $feature
   755	  done
   756	}
   757	
   758	#---------------------------------------------------------------------
   759	## @param category/service
   760	## @Stdout spelllist
   761	##
   762	## First argument is a category of spell.  Returns a list of spells
   763	## that match that category.  For example,
   764	## C<find_providers email-client> returns evolution,althea,mutt,etc.
   765	##
   766	## This will list spells exactly once, exiled spells are not listed.
   767	## This also takes into account grimoire ordering. A spell must provide
   768	## the category in the first grimoire the spell is seen in (because that
   769	## is the only one that will be cast) otherwise it is not listed.
   770	#---------------------------------------------------------------------
   771	function find_providers()  {
   772	  local feature="$1"
   773	  local provider GRIMOIRE spell_path
   774	  local providers=$(
   775	    for GRIMOIRE in $(codex_get_all_grimoires);  do
   776	      gawk '/^'"$feature"'[[:blank:]]/ { print $2 }' \
   777	        "$GRIMOIRE/$PROVIDE_INDEX_FILE"
   778	    done | get_basenames|sort -u)
   779	
   780	  # filter out providers we dont want, so far this is:
   781	  # a) do not provide something in the first grimoire they are found in
   782	  # b) are exiled.
   783	  # ex: foo provides FOOBAR in the "stable" grimoire but not the "test"
   784	  # grimoire and test is searched before stable in this case cast will
   785	  # use the spell from test which does NOT provide FOOBAR, therefore
   786	  # we should not list foo as a provider of FOOBAR
   787	  for provider in $providers; do
   788	    if ! spell_exiled $provider; then
   789	      spell_path=$(codex_find_spell_by_name $provider)
   790	      smgl_dirname "$(smgl_dirname $spell_path)" GRIMOIRE
   791	      # ensure the first place we look for the spell is a provider
   792	      grep -q "^$feature $spell_path$" $GRIMOIRE/$PROVIDE_INDEX_FILE &&
   793	        echo $provider
   794	    fi
   795	  done
   796	}
   797	
   798	
   799	##############################CACHE FUNCTIONS#########################
   800	#---------------------------------------------------------------------
   801	## @param spell name
   802	## @param grimoire-path
   803	## @param [grimoire-path ...]
   804	## @return 0 Spell found
   805	## @return 1 Spell not found
   806	## Searches the indicies of the specified grimories for a spell.
   807	## This will return only the first match found.
   808	##
   809	#---------------------------------------------------------------------
   810	function codex_cache_spell_lookup()  {
   811	  local SECTION
   812	  local spell="$1"
   813	  shift
   814	  while [ $# -gt 0 ] ; do
   815	   debug "libcodex" "looking up $spell in ${1}'s cache"
   816	    codex_check_cache $1
   817	    SECTION=`grep -m 1 "^$spell " $1/$SPELL_INDEX_FILE | cut -d' ' -f2`
   818	    [[ $SECTION ]] && echo "$SECTION/$spell" && return
   819	    shift
   820	  done
   821	  return 1
   822	}
   823	
   824	
   825	#---------------------------------------------------------------------
   826	## @param grimoire-path
   827	## @Stdout error if cache doesn't exist after creation
   828	## Checks that the cache exists. if it doesn't exist, make it.
   829	## If it still doesn't exist, the barf an error
   830	##
   831	#---------------------------------------------------------------------
   832	function codex_check_cache()  {
   833	  codex_is_canonicalized $1 || return 1
   834	  [[ -x $1 ]] || return 1
   835	
   836	  if ! [ -f $1/$SPELL_INDEX_FILE ] || ! [ -f $1/$PROVIDE_INDEX_FILE ]; then
   837	      codex_create_cache
   838	  fi
   839	  if ! [ -f $1/$SPELL_INDEX_FILE ] || ! [ -f $1/$PROVIDE_INDEX_FILE ]; then
   840	    error_message "${PROBLEM_COLOR}Eeek, $1 is not a grimoire!${DEFAULT_COLOR}"
   841	    exit 1
   842	  fi
   843	}
   844	
   845	#---------------------------------------------------------------------
   846	## @param [grimoire-path]
   847	## Creates the cache/index for the specified grimoire, or all if none
   848	## is asked for.
   849	##
   850	#---------------------------------------------------------------------
   851	function codex_create_cache()  {
   852	
   853	  debug "libcodex" "codex_create_cache - $*"
   854	  local list="$*"
   855	  [[ $list ]] || list="${GRIMOIRE_DIR[*]}"
   856	
   857	  for i in $list ; do
   858	    codex_find_in_grimoire $i "DETAILS" | \
   859	    sed 's@\(.*\)/\([^/]*\)/DETAILS@\2 \1@' | \
   860	    sort > $i/$SPELL_INDEX_FILE
   861	
   862	    codex_list_provides $i > "$i/$PROVIDE_INDEX_FILE"
   863	  done
   864	
   865	}
   866	
   867	
   868	#---------------------------------------------------------------------
   869	## Create in-memory spell lookup table for named grimoires.
   870	#---------------------------------------------------------------------
   871	function codex_create_in_memory_cache() {
   872	  local list
   873	  if [[ $1 == -i ]] ; then
   874	    list=$SPELL_STATUS
   875	    shift
   876	  fi
   877	  local hash=$1
   878	  shift
   879	  eval $(
   880	    for each in "$@"; do
   881	      cat $each/$SPELL_INDEX_FILE
   882	    done | awk -v i=$list -v hash=$hash '{
   883	      if ( ! ($1 in map)) {
   884	        map[$1]=$2;
   885	      }
   886	    } END {
   887	      if (i) {
   888	        FS=":";
   889	        while (getline < i) {
   890	          if( $3=="installed" || $3=="held") {
   891	            printf("hash_put %s %s \"%s/%s\";\n",hash,$1,map[$1],$1);
   892	          }
   893	        }
   894	      } else {
   895	        for (spell in map) {
   896	          printf("hash_put %s %s \"%s/%s\";\n",hash,spell,map[spell],spell);
   897	        }
   898	      }
   899	    }'
   900	  )
   901	
   902	}
   903	
   904	#---------------------------------------------------------------------
   905	## Creae in-memory spell lookup table for all known grimoires.
   906	#---------------------------------------------------------------------
   907	function codex_create_in_memory_cache_all() {
   908	  codex_create_in_memory_cache "$@" $(codex_get_all_grimoires)
   909	}
   910	
   911	###########################MISC FUNCTIONS#############################
   912	
   913	#---------------------------------------------------------------------
   914	## @param grimoire-path
   915	## @param file-name
   916	##
   917	## @Stdout file names
   918	## Prints every file matching file-name in grimoire
   919	##
   920	#---------------------------------------------------------------------
   921	function codex_find_in_grimoire() {
   922	    find "$1" -follow -maxdepth 3 -mindepth 3 -perm /700 -name "$2"
   923	}
   924	
   925	#---------------------------------------------------------------------
   926	## @param grimoire-path
   927	## @Stdout provides spell
   928	## Lists all providers in grimoire in the form of "provides spell"
   929	## for instance: <br>
   930	## shell /home/martin/p4/grimoire/shell-term-fm/sash
   931	##
   932	#---------------------------------------------------------------------
   933	function codex_list_provides() {
   934	    grimoire=$1
   935	
   936	    for file in $(codex_find_in_grimoire $grimoire "PROVIDES"); do
   937	        smgl_dirname "$file" spell
   938	        for provides in $(gawk '{if (/provides/) { print $2 }
   939	                                 else { print $1 }}' $file); do
   940	            echo "$provides $spell"
   941	        done
   942	    done | sort
   943	}
   944	
   945	#---------------------------------------------------------------------
   946	## @param grimoire-path
   947	## Creates keyword index
   948	##
   949	#---------------------------------------------------------------------
   950	function codex_create_keyword_cache() { (
   951	    grimoire=$1
   952	
   953	    for file in $(codex_find_in_grimoire "$grimoire" "DETAILS"); do
   954	        unset SPELL KEYWORDS
   955	        codex_set_current_spell "${file%DETAILS}"
   956	        echo "$SPELL $KEYWORDS"
   957	    done | sort > $grimoire/$KEYWORD_INDEX_FILE
   958	) }
   959	
   960	#---------------------------------------------------------------------
   961	## @param grimoire-path
   962	## Creates the version index, which additionally includes the other
   963	## queuing factors - patchlevels and UPDATED.
   964	##
   965	## Multiversioned spells will a dummy entry. Persistent
   966	## variables are ignored.
   967	#---------------------------------------------------------------------
   968	function codex_create_version_cache() { (
   969	  grimoire="$1"
   970	
   971	  for file in $(codex_find_in_grimoire "$grimoire" DETAILS); do
   972	    codex_get_spell_paths ${file%/DETAILS}
   973	    source "$file" &> /dev/null
   974	    if [[ $(grep -c "^[[:space:]]*VERSION=" "$file") == 1 ]]; then
   975	      echo "$SPELL ${VERSION:-0} ${PATCHLEVEL:-0} ${SECURITY_PATCH:-0} ${UPDATED:-0}"
   976	    else
   977	      # multiversioned or unusually formatted spell - add a dummy entry
   978	      echo "$SPELL multiversioned"
   979	    fi
   980	    unset SPELL VERSION PATCHLEVEL SECURITY_PATCH UPDATED
   981	  done | sort > $grimoire/$VERSION_INDEX_FILE
   982	) }
   983	
   984	#---------------------------------------------------------------------
   985	## @param servicename
   986	## @return 0 if service exists
   987	## @return 1 otherwise
   988	## checks if servicename exists.
   989	#---------------------------------------------------------------------
   990	function codex_does_service_exist()
   991	{
   992	   local SERVICE="$@"
   993	    for GRIMOIRE in $(codex_get_all_grimoires); do
   994	        grep -qE '^'$SERVICE' ' "$GRIMOIRE/$PROVIDE_INDEX_FILE" && return 0
   995	    done
   996	}
   997	
   998	
   999	#---------------------------------------------------------------------
  1000	## @param spell or section name
  1001	## @Globals CODEX_FOUND_SECTION CODEX_FOUND_SPELL
  1002	## @return 0 if the passed argument is a spell name or a section name.
  1003	## @return 1 otherwise
  1004	## CODEX_FOUND_SECTION is set if section was found
  1005	## CODEX_FOUND_SPELL   is set if a spell was found
  1006	##
  1007	#---------------------------------------------------------------------
  1008	function codex_find_spell_or_section_by_name()  {
  1009	    local SPELL_OR_SECTION="$1"
  1010	
  1011	    CODEX_FOUND_SECTION=""
  1012	    CODEX_FOUND_SPELL=""
  1013	
  1014	    codex_find_section_by_name $SPELL_OR_SECTION CODEX_FOUND_SECTION
  1015	    [  -n  "$CODEX_FOUND_SECTION"  ]  &&  return
  1016	
  1017	    CODEX_FOUND_SPELL=`codex_find_spell_by_name $SPELL_OR_SECTION`
  1018	    [  -n  "$CODEX_FOUND_SPELL"  ]    &&  return
  1019	
  1020	    return 1
  1021	}
  1022	
  1023	#---------------------------------------------------------------------
  1024	## @param spell directory
  1025	## @Stdout spell description
  1026	## Echos the long description of the given spell.  Returns an
  1027	## empty string if the directory is not a valid spell.
  1028	##
  1029	#---------------------------------------------------------------------
  1030	function codex_get_spell_description()  {
  1031	  local width=$(stty size | cut -d' ' -f2)
  1032	  codex_is_directory_a_spell  "$1"  &&  .  "$1/DETAILS" | fmt -w $width -s
  1033	}
  1034	
  1035	#---------------------------------------------------------------------
  1036	##
  1037	## @License
  1038	## This software is free software; you can redistribute it and/or modify
  1039	## it under the terms of the GNU General Public License as published by
  1040	## the Free Software Foundation; either version 2 of the License, or
  1041	## (at your option) any later version.
  1042	##
  1043	## This software is distributed in the hope that it will be useful,
  1044	## but WITHOUT ANY WARRANTY; without even the implied warranty of
  1045	## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  1046	## GNU General Public License for more details.
  1047	##
  1048	## You should have received a copy of the GNU General Public License
  1049	## along with this software; if not, write to the Free Software
  1050	## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  1051	##
  1052	#---------------------------------------------------------------------