summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpuppetmaster <puppetmaster>2006-01-12 13:16:32 +0000
committerpuppetmaster <puppetmaster>2006-01-12 13:16:32 +0000
commitacd86ac46a4e6949db7643420cd54861ec9f0c0a (patch)
treeadeb082655f4718d6ce1c615f3abaeb30f8f1d3d
parentAdd sorcery for Puppet Master (diff)
downloadsorcery-acd86ac46a4e6949db7643420cd54861ec9f0c0a.tar.gz
adding my sorcery modifications ;)
-rw-r--r--patch_for_svn_https.patch68
-rw-r--r--var/lib/sorcery/modules/build_api/api1288
-rw-r--r--var/lib/sorcery/modules/build_api/api2346
-rw-r--r--var/lib/sorcery/modules/build_api/common313
-rw-r--r--var/lib/sorcery/modules/dl_handlers/dl_cvs109
-rw-r--r--var/lib/sorcery/modules/dl_handlers/dl_dir92
-rw-r--r--var/lib/sorcery/modules/dl_handlers/dl_file69
-rw-r--r--var/lib/sorcery/modules/dl_handlers/dl_rsync180
-rw-r--r--var/lib/sorcery/modules/dl_handlers/dl_svn93
-rw-r--r--var/lib/sorcery/modules/dl_handlers/dl_tla97
-rw-r--r--var/lib/sorcery/modules/dl_handlers/dl_wget142
-rw-r--r--var/lib/sorcery/modules/libapi1043
-rw-r--r--var/lib/sorcery/modules/libcast703
-rw-r--r--var/lib/sorcery/modules/libcodex869
-rw-r--r--var/lib/sorcery/modules/libcrossinstall184
-rw-r--r--var/lib/sorcery/modules/libdepends1209
-rw-r--r--var/lib/sorcery/modules/libdepengine402
-rw-r--r--var/lib/sorcery/modules/libdispel414
-rw-r--r--var/lib/sorcery/modules/libdownload229
-rw-r--r--var/lib/sorcery/modules/libgcc2122
-rw-r--r--var/lib/sorcery/modules/libgpg447
-rw-r--r--var/lib/sorcery/modules/libgrimoire281
-rw-r--r--var/lib/sorcery/modules/libhash297
-rw-r--r--var/lib/sorcery/modules/libinitd287
-rw-r--r--var/lib/sorcery/modules/liblock499
-rw-r--r--var/lib/sorcery/modules/libmedia249
-rw-r--r--var/lib/sorcery/modules/libmisc1358
-rw-r--r--var/lib/sorcery/modules/libresurrect666
-rw-r--r--var/lib/sorcery/modules/libscreen209
-rw-r--r--var/lib/sorcery/modules/libsecurity323
-rw-r--r--var/lib/sorcery/modules/libsorcery1504
-rw-r--r--var/lib/sorcery/modules/libstate819
-rw-r--r--var/lib/sorcery/modules/libsummon421
-rw-r--r--var/lib/sorcery/modules/libtablet914
-rw-r--r--var/lib/sorcery/modules/libtrack396
-rw-r--r--var/lib/sorcery/modules/libtriggers210
-rw-r--r--var/lib/sorcery/modules/libunpack820
-rw-r--r--var/lib/sorcery/modules/liburl511
-rw-r--r--var/lib/sorcery/modules/url_handlers/url_cvs159
-rw-r--r--var/lib/sorcery/modules/url_handlers/url_default106
-rw-r--r--var/lib/sorcery/modules/url_handlers/url_dir73
-rw-r--r--var/lib/sorcery/modules/url_handlers/url_file52
-rw-r--r--var/lib/sorcery/modules/url_handlers/url_http193
-rw-r--r--var/lib/sorcery/modules/url_handlers/url_rsync100
-rw-r--r--var/lib/sorcery/modules/url_handlers/url_smgl_tla139
-rw-r--r--var/lib/sorcery/modules/url_handlers/url_svn154
46 files changed, 18159 insertions, 0 deletions
diff --git a/patch_for_svn_https.patch b/patch_for_svn_https.patch
new file mode 100644
index 0000000..5864290
--- /dev/null
+++ b/patch_for_svn_https.patch
@@ -0,0 +1,68 @@
+diff -Nru modules/dl_handlers/dl_svn /home/puppetmaster/code/sorcery/var/lib/sorcery/modules/dl_handlers/dl_svn
+--- modules/dl_handlers/dl_svn 2005-11-01 15:26:01.000000000 +0100
++++ /home/puppetmaster/code/sorcery/var/lib/sorcery/modules/dl_handlers/dl_svn 2006-01-11 12:04:43.000000000 +0100
+@@ -44,9 +44,23 @@
+ eval "$dl_target=\"$target\""
+ else
+ message "${MESSAGE_COLOR}Running svn checkout...${DEFAULT_COLOR}"
+- # hard-coded http :-(
+- svn checkout -r $SVN_TAG http://$SVN_ROOT $SVN_MODULE
+- rc=$?
++ # hard-coded http :-( ....
++ case $SVN_TYPE in
++ svn)
++ message "svn checkout -r $SVN_TAG http://$SVN_ROOT $SVN_MODULE"
++ svn checkout -r $SVN_TAG http://$SVN_ROOT $SVN_MODULE
++ rc=$?
++ ;;
++ svns)
++ message "svn checkout -r $SVN_TAG https://$SVN_ROOT $SVN_MODULE"
++ svn checkout -r $SVN_TAG https://$SVN_ROOT $SVN_MODULE
++ rc=$?
++ ;;
++ *)
++ message "unknow url type"
++ rc=2
++ ;;
++ esac
+ eval "$dl_target=\"$SVN_MODULE\""
+ fi
+ [[ $rc == 0 ]] && break
+diff -Nru modules/libdownload /home/puppetmaster/code/sorcery/var/lib/sorcery/modules/libdownload
+--- modules/libdownload 2005-11-01 15:26:10.000000000 +0100
++++ /home/puppetmaster/code/sorcery/var/lib/sorcery/modules/libdownload 2006-01-11 12:04:43.000000000 +0100
+@@ -75,6 +75,11 @@
+ local dl_target=$5
+ local dl_type=$6
+
++ if [ "$handler" == "svns" ]
++ then
++ local handler=svn
++ fi
++
+ if ! [[ "$handler" ]] ; then
+ message "Missing handler, this is probably a sorcery bug"
+ return 255
+diff -Nru modules/url_handlers/url_svn /home/puppetmaster/code/sorcery/var/lib/sorcery/modules/url_handlers/url_svn
+--- modules/url_handlers/url_svn 2005-11-01 15:26:13.000000000 +0100
++++ /home/puppetmaster/code/sorcery/var/lib/sorcery/modules/url_handlers/url_svn 2006-01-11 12:04:43.000000000 +0100
+@@ -68,11 +68,17 @@
+ ## @Global SVN_ROOT
+ ## @Global SVN_MODULE
+ ## @Global SVN_TAG
+-##
++## @Global SVN_TYPE
+ #---------------------------------------------------------------------
+ function url_svn_crack() {
+
+- URL=`url_strip_prefix "$1" svn`
++ SVN_TYPE=`echo $1 | cut -d ':' -f 1`
++ if [ "$SVN_TYPE" == "svns" ]
++ then
++ URL=`url_strip_prefix "$1" svns`
++ else
++ URL=`url_strip_prefix "$1" svn`
++ fi
+ SVN_ROOT=`echo $URL | sed "s#\(^[^/]*[^:]*\):.*#\1#"`
+ local SVN_MODULE_TAG=`echo $URL | sed "s#^[^/]*[^:]*\(.*\)#\1#"`
+ SVN_MODULE=`echo $SVN_MODULE_TAG | cut -d : -f2`
diff --git a/var/lib/sorcery/modules/build_api/api1 b/var/lib/sorcery/modules/build_api/api1
new file mode 100644
index 0000000..0605919
--- /dev/null
+++ b/var/lib/sorcery/modules/build_api/api1
@@ -0,0 +1,288 @@
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Functions for dealing with the actual compiling/installation of spells
+## and walking through casts 'pass 4' pipeline. If BUILD_API is '1'
+##
+##=head1 DESCRIPTION
+##
+## Contains functions for the build api version 1
+## which has the following steps:
+## PRE_BUILD -> BUILD -> POST_BUILD -> POST_INSTALL -> TRIGGERS
+##
+##=head1 COPYRIGHT
+##
+## Copyright (C) 2004 The Source Mage Team <http://www.sourcemage.org>
+##
+##=head1 FUNCTIONS
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## This runs through the phase 4 pipeline of building and installing
+## a spell for BUILD_API 1.
+#---------------------------------------------------------------------
+function run_build_spell() {
+ debug "build_api/api1" "run_build_spell"
+ C_LOG=$TMP_DIR/$SPELL.compile.log
+ C_FIFO=/dev/stdout
+
+ rm -f $C_LOG
+ touch $C_LOG
+ if [[ $SCREEN_NAME ]] ; then
+ screen_new_window "$SCREEN_NAME" $SCREEN_CAST_WIN "cast $SPELL" \
+ tail -f -s 0.1 $C_LOG
+ screen_switch_window "$SCREEN_NAME" $SCREEN_MAIN_WIN
+
+ VOYEUR_STDOUT=/dev/null
+ VOYEUR_STDERR=/dev/null
+ elif [ "$VOYEUR" == "on" -a -z "$SILENT" ] ; then
+ VOYEUR_STDOUT=/dev/stdout
+ VOYEUR_STDERR=/dev/stderr
+ else
+ VOYEUR_STDOUT=/dev/null
+ VOYEUR_STDERR=/dev/null
+ fi
+ # should OPTS get set here?
+
+ local PROTECT_SORCERY=yes
+ local rs
+
+ # This is an experiment, If we have actual interesting return codes
+ # from here, the caller can figure out what to do
+ # as opposed to trying to cram it all in here
+ (
+ run_pre_build || return 1
+ run_build || return 2
+ run_post_build &&
+ run_post_install || return 3
+ )
+ rs=$?
+
+ if [[ $SCREEN_NAME ]] && [ $rs -gt 0 ] ; then
+ screen_move_window "$SCREEN_NAME" $SCREEN_CAST_WIN $SCREEN_LAST_FAILED_CAST_WIN
+ screen_name_window "$SCREEN_NAME" $SCREEN_LAST_FAILED_CAST_WIN "Failed $SPELL"
+ screen_kill_window "$SCREEN_NAME" $SCREEN_CAST_WIN
+ screen_notify "$SCREEN_NAME" "Last failed cast at $SCREEN_LAST_FAILED_CAST_WIN"
+ elif [[ $SCREEN_NAME ]] ; then
+ screen_kill_window "$SCREEN_NAME" $SCREEN_CAST_WIN
+ fi
+
+ # Triggers don't run in the window
+ [ $rs -gt 0 ] && return $rs
+ run_triggers || return 4
+
+ return $rs
+}
+
+#---------------------------------------------------------------------
+## This phase of casting involves unpacking the source into the
+## source directories. If a PRE_BUILD file exists in SCRIPT_DIRECTORY
+## and is executable it is run in preference to the default_pre_build.
+#---------------------------------------------------------------------
+function run_pre_build() {
+
+ debug "build_api/api1" "run_pre_build()"
+ message "${MESSAGE_COLOR}Building" \
+ "${SPELL_COLOR}${SPELL}" \
+ "${DEFAULT_COLOR}"
+
+ rm_source_dir
+ mkdir -p $BUILD_DIRECTORY
+ cd $BUILD_DIRECTORY
+
+ persistent_load &&
+ if [ -x $SCRIPT_DIRECTORY/PRE_BUILD ]; then
+ debug "build_api/api1" "run_pre_build() - Prebuild script exists, now sourcing "
+ . $SCRIPT_DIRECTORY/PRE_BUILD
+ else
+ debug "build_api/api1" "run_pre_build() - Prebuild script not found, using default"
+ default_pre_build
+ fi &&
+ persistent_save
+}
+
+
+#---------------------------------------------------------------------
+## Starts up the compile logs, turns on the various environment
+## settings that need to be on, eventually gets around to running
+## BUILD or default_build, then does other things it shouldn't do.
+#---------------------------------------------------------------------
+function run_build() {
+
+ debug "build_api/api1" "Starting run_build()"
+
+ echo "Compile log for $SPELL $VERSION Built on `date -u`" > $C_LOG
+ echo "Using gcc version: `gcc -dumpversion`" >> $C_LOG
+
+ # slight bug here if SOURCE_DIRECTORY doesnt exist
+ [ -d "$SOURCE_DIRECTORY" ] &&
+ cd $SOURCE_DIRECTORY
+
+ invoke_build_dir
+ invoke_gcc2
+ optimize
+ invoke_installwatch
+
+ message -n "Installing in dir: "
+ pwd
+ message "$SPELL $VERSION"
+
+ run_config_loc
+ (
+ persistent_load &&
+ if [ -x $SCRIPT_DIRECTORY/BUILD ]; then
+ . $SCRIPT_DIRECTORY/BUILD
+ else
+ default_build
+ fi &&
+ persistent_save
+ ) 2> >(tee -a $C_LOG 1>&2 >> $VOYEUR_STDERR) \
+ > >(tee -a $C_LOG >> $VOYEUR_STDOUT) # see bug 7201
+
+ if [ "$?" != 0 ]; then
+ message "${PROBLEM_COLOR}" \
+ "! Problem Detected !" \
+ "${DEFAULT_COLOR}"
+ return 1
+ fi
+ echo "Compile log for $SPELL $VERSION Completed Build on `date -u`" >> $C_LOG
+
+}
+
+
+#---------------------------------------------------------------------
+## Checks for a POST_BUILD file in SCRIPT_DIRECTORY, and if it is
+## executable, runs it. This file is run after BUILD and before
+## POST_INSTALL. Its purpose is to bookend BUILD and shutoff installwatch.
+#---------------------------------------------------------------------
+function run_post_build() {
+
+ debug "build_api/api1" "Starting run_post_build()"
+ persistent_load &&
+ if [ -x $SCRIPT_DIRECTORY/POST_BUILD ]; then
+ . $SCRIPT_DIRECTORY/POST_BUILD
+ else
+ default_post_build
+ fi
+ persistent_save
+
+ # Lock made in prepare_install
+ unlock_resources "libgrimoire" "install"
+
+}
+
+
+#---------------------------------------------------------------------
+## Checks for a POST_INSTALL file in SCRIPT_DIRECTORY, and if it is
+## executable, runs it. This file is used for extra files that need
+## to be installed, but not tracked by installwatch.
+#---------------------------------------------------------------------
+function run_post_install() {
+
+ debug "build_api/api1" "Starting run_post_install()"
+ persistent_load &&
+ if [ -x $SCRIPT_DIRECTORY/POST_INSTALL ]
+ then
+ . $SCRIPT_DIRECTORY/POST_INSTALL
+ fi &&
+ persistent_save
+}
+
+
+#---------------------------------------------------------------------
+## @Type API
+## Creates the source directory and unpacks the source package into it.
+## Used if no PRE_BUILD script is found for a spell.
+#---------------------------------------------------------------------
+function real_default_pre_build() {
+
+ debug "build_api/api1" "Starting real_default_pre_build() - SOURCE=$SOURCE SOURCE_DIRECTORY=$SOURCE_DIRECTORY"
+ mk_source_dir $SOURCE_DIRECTORY &&
+ unpack_file
+
+}
+
+
+
+
+#---------------------------------------------------------------------
+## @Type API
+## Used if no BUILD script is found for a spell
+## Default build is:
+## <pre>
+## ./configure --build=$BUILD \
+## --prefix=/usr \
+## --sysconfdir=/etc \
+## --localstatedir=/var \
+## $OPTS &&
+## make &&
+## prepare_install &&
+## make install
+## </pre>
+##
+#---------------------------------------------------------------------
+function real_default_build() {
+
+ debug "build_api/api1" "Starting real_default_build()"
+ OPTS="$OPTS --build=${BUILD}"
+ #If this switches are used, they _may_ stop distcc and ccache from working
+ # for some spells (bug 3798)
+ # We could write wrappers for all of the possible binaries
+ [[ $CROSS_INSTALL == on ]] && OPTS="$OPTS --host=${HOST}"
+
+ ./configure --prefix=${INSTALL_ROOT}/usr \
+ --sysconfdir=${INSTALL_ROOT}/etc \
+ --localstatedir=${INSTALL_ROOT}/var \
+ --mandir=${INSTALL_ROOT}/usr/share/man \
+ --infodir=${INSTALL_ROOT}/usr/share/info \
+ $OPTS &&
+ make &&
+ prepare_install &&
+ make install
+
+}
+
+
+
+#---------------------------------------------------------------------
+## @Type API
+## Installs configuration files and documentation. Stops installwatch.
+## Used if no POST_BUILD script is found for a spell.
+##
+#---------------------------------------------------------------------
+function real_default_post_build() {
+
+ debug "build_api/api1" "Starting real_default_post_build()"
+
+ install_xinetd
+ install_initd
+ install_pam_confs
+ install_desktop_files
+ gather_docs
+ devoke_installwatch
+ init_post_install
+ ldconfig
+ # release_saved_libraries
+ cd /
+
+}
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/build_api/api2 b/var/lib/sorcery/modules/build_api/api2
new file mode 100644
index 0000000..e533f8b
--- /dev/null
+++ b/var/lib/sorcery/modules/build_api/api2
@@ -0,0 +1,346 @@
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Functions for dealing with the actual compiling/installation of spells
+## and walking through casts 'pass 4' pipeline.
+##
+##=head1 DESCRIPTION
+##
+## Contains functions for the build api version 2
+## which has the following steps:
+## PRE_BUILD -> BUILD -> PRE_INSTALL -> INSTALL -> POST_INSTALL ->
+## FINAL -> TRIGGERS
+##
+##=head1 COPYRIGHT
+##
+## Copyright (C) 2002 The Source Mage Team <http://www.sourcemage.org>
+##
+##=head1 FUNCTIONS
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## This runs through the phase 4 pipeline of building and installing
+## a spell for BUILD_API 2.
+#---------------------------------------------------------------------
+function run_build_spell() {
+ debug "build_api/api2" "run_build_spell"
+
+ C_LOG=$TMP_DIR/$SPELL.compile.log
+ C_FIFO=/dev/stdout # not needed for api2 anymore, but just in case
+
+ rm -f $C_LOG
+ touch $C_LOG
+
+ if [[ $SCREEN_NAME ]] ; then
+ screen_new_window "$SCREEN_NAME" $SCREEN_CAST_WIN "cast $SPELL" \
+ tail -f -s 0.1 $C_LOG
+ screen_switch_window "$SCREEN_NAME" $SCREEN_MAIN_WIN
+
+ VOYEUR_STDOUT=/dev/null
+ VOYEUR_STDERR=/dev/null
+ elif [ "$VOYEUR" == "on" -a -z "$SILENT" ] ; then
+ VOYEUR_STDOUT=/dev/stdout
+ VOYEUR_STDERR=/dev/stderr
+ else
+ VOYEUR_STDOUT=/dev/null
+ VOYEUR_STDERR=/dev/null
+ fi
+
+ local PROTECT_SORCERY=yes
+ local rs
+ (
+ run_pre_build || return 1
+ run_config_loc || return 2
+ (
+ run_build &&
+ run_pre_install &&
+ run_install || return 2
+ run_post_install &&
+ run_final || return 3
+ ) 2> >(tee -a $C_LOG 1>&2 >> $VOYEUR_STDERR) \
+ > >(tee -a $C_LOG >> $VOYEUR_STDOUT)
+ )
+ rs=$?
+ if [[ $SCREEN_NAME ]] && [ $rs -gt 0 ] ; then
+ screen_move_window "$SCREEN_NAME" $SCREEN_CAST_WIN $SCREEN_LAST_FAILED_CAST_WIN
+ screen_name_window "$SCREEN_NAME" $SCREEN_LAST_FAILED_CAST_WIN "Failed $SPELL"
+ screen_kill_window "$SCREEN_NAME" $SCREEN_CAST_WIN
+ screen_notify "$SCREEN_NAME" "Last failed cast at $SCREEN_LAST_FAILED_CAST_WIN"
+ elif [[ $SCREEN_NAME ]] ; then
+ screen_kill_window "$SCREEN_NAME" $SCREEN_CAST_WIN
+ fi
+
+ # triggers don't run in the window
+ [ $rs -gt 0 ] && return $rs
+
+ run_triggers || return 4
+
+ return 0
+}
+#---------------------------------------------------------------------
+## This phase of casting involves unpacking the source into the
+## source directories. If a PRE_BUILD file exists in SCRIPT_DIRECTORY
+## and is executable it is run in preference to the default_pre_build.
+#---------------------------------------------------------------------
+function run_pre_build() {
+
+ debug "build_api/api2" "run_pre_build()"
+ message "${MESSAGE_COLOR}Building" \
+ "${SPELL_COLOR}${SPELL}" \
+ "${DEFAULT_COLOR}"
+
+ rm_source_dir
+ mkdir -p $BUILD_DIRECTORY
+ cd $BUILD_DIRECTORY
+
+ verify_sources &&
+ persistent_load &&
+ if [ -x $SCRIPT_DIRECTORY/PRE_BUILD ]; then
+ debug "build_api/api2" "run_pre_build() - Prebuild script exists, now sourcing "
+ . $SCRIPT_DIRECTORY/PRE_BUILD
+ else
+ debug "build_api/api2" "run_pre_build() - Prebuild script not found, using default"
+ default_pre_build
+ fi &&
+ persistent_save
+}
+
+
+#---------------------------------------------------------------------
+## Starts up the compile logs, turns on the various environment
+## settings that need to be on, eventually gets around to running
+## BUILD or default_build.
+#---------------------------------------------------------------------
+function run_build() {
+
+ debug "build_api/api2" "Starting run_compile()"
+
+ echo "Compile log for $SPELL $VERSION Built on `date -u`" > $C_LOG
+ echo "Using gcc version: `gcc -dumpversion`" >> $C_LOG
+
+ [ -d "$SOURCE_DIRECTORY" ] &&
+ cd $SOURCE_DIRECTORY
+
+ invoke_build_dir
+ invoke_gcc2
+ optimize
+
+ message -n "Installing in dir: "
+ pwd
+ message "$SPELL $VERSION"
+
+ persistent_load &&
+ if [ -x $SCRIPT_DIRECTORY/BUILD ]; then
+ . $SCRIPT_DIRECTORY/BUILD
+ else
+ default_build
+ fi &&
+ persistent_save
+
+ if [ "$?" != 0 ]; then
+ message "${PROBLEM_COLOR}" \
+ "! Problem Detected !" \
+ "${DEFAULT_COLOR}"
+ return 1
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Turns all the various logs back on that were turned off from after
+## run_build finished, then runs PRE_INSTALL or default_pre_install
+#---------------------------------------------------------------------
+function run_pre_install() {
+ debug "build_api/api2" "Starting run_pre_install()"
+
+ persistent_load &&
+ if [ -x $SCRIPT_DIRECTORY/PRE_INSTALL ]; then
+ . $SCRIPT_DIRECTORY/PRE_INSTALL
+ else
+ default_pre_install
+ fi &&
+ persistent_save &&
+ echo "Compile log for $SPELL $VERSION Completed Build on `date -u`" >> $C_LOG
+}
+
+
+#---------------------------------------------------------------------
+## Turns all the various logs back on that were turned off from after
+## run_pre_install finished, then runs INSTALL or default_install
+## Along with other stuff that needs to be transplanted elsewhere.
+#---------------------------------------------------------------------
+function run_install() {
+ debug "build_api/api2" "Starting run_install()"
+
+ invoke_installwatch
+
+ persistent_load &&
+ if [ -x $SCRIPT_DIRECTORY/INSTALL ]; then
+ . $SCRIPT_DIRECTORY/INSTALL
+ else
+ default_install
+ fi &&
+ persistent_save &&
+
+ if [ "$?" != 0 ]; then
+ message "${PROBLEM_COLOR}" \
+ "! Problem Detected !" \
+ "${DEFAULT_COLOR}"
+ false
+ fi
+}
+
+#---------------------------------------------------------------------
+## Checks for a POST_BUILD file in SCRIPT_DIRECTORY, and if it is
+## executable, runs it. This file is run after INSTALL and before
+## FINAL. Its purpose is to bookend INSTALL and shutoff installwatch.
+#---------------------------------------------------------------------
+function run_post_install() {
+ debug "build_api/api2" "Starting run_post_install()"
+ persistent_load &&
+ if [ -x $SCRIPT_DIRECTORY/POST_INSTALL ]; then
+ . $SCRIPT_DIRECTORY/POST_INSTALL
+ else
+ default_post_install
+ fi &&
+ persistent_save &&
+ unlock_resources "libgrimoire" "install"
+}
+
+#---------------------------------------------------------------------
+## Checks for a FINAL file in SCRIPT_DIRECTORY, and if it is
+## executable, runs it. This file is used for extra files that need
+## to be installed, but not tracked by installwatch.
+#---------------------------------------------------------------------
+function run_final() {
+
+ debug "build_api/api2" "Starting run_final()"
+ persistent_load &&
+ if [ -x $SCRIPT_DIRECTORY/FINAL ]
+ then
+ . $SCRIPT_DIRECTORY/FINAL
+ fi &&
+ persistent_save
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## Creates the source directory and unpacks the source package into it.
+## Used if no PRE_BUILD script is found for a spell.
+##
+#---------------------------------------------------------------------
+function real_default_pre_build() {
+
+ debug "libgrimoire" "default_pre_build() - SOURCE=$SOURCE SOURCE_DIRECTORY=$SOURCE_DIRECTORY"
+ mk_source_dir $SOURCE_DIRECTORY &&
+ unpack_file
+
+}
+
+
+#---------------------------------------------------------------------
+## @Type API
+## Used if no BUILD script is found
+## Default build is:
+## <pre>
+## ./configure --build=$BUILD \
+## --prefix=/usr \
+## --sysconfdir=/etc \
+## --localstatedir=/var \
+## $OPTS &&
+## make
+## </pre>
+##
+#---------------------------------------------------------------------
+function real_default_build() {
+ debug "build_api/api2" "real_default_build"
+
+ OPTS="$OPTS --build=${BUILD}"
+ #If these switches are used, they _may_ stop distcc and ccache from working
+ # for some spells (bug 3798)
+ # We could write wrappers for all of the possible binaries
+ [[ $CROSS_INSTALL == on ]] && OPTS="$OPTS --host=${HOST}"
+
+ ./configure --prefix=${INSTALL_ROOT}/usr \
+ --sysconfdir=${INSTALL_ROOT}/etc \
+ --localstatedir=${INSTALL_ROOT}/var \
+ --mandir=${INSTALL_ROOT}/usr/share/man \
+ --infodir=${INSTALL_ROOT}/usr/share/info \
+ $OPTS &&
+ make
+
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## Used if no PRE_INSTALL script is found
+## Default pre_install is:
+## <pre>
+## prepare_install
+## </pre>
+##
+#---------------------------------------------------------------------
+function real_default_pre_install() {
+ debug "build_api/api2" "Starting real_default_pre_install"
+ devoke_installwatch
+ prepare_install
+ invoke_installwatch
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## Used if no INSTALL script is found
+## Default install is:
+## <pre>
+## make install
+## </pre>
+##
+#---------------------------------------------------------------------
+function real_default_install() {
+ debug "build_api/api2" "Starting real_default_install"
+ make install
+}
+
+
+#---------------------------------------------------------------------
+## @Type API
+## Installs configuration files and documentation. Stops installwatch.
+## Used if no POST_INSTALL script is found for a spell.
+##
+## This is identical to api1's post_build
+#---------------------------------------------------------------------
+function real_default_post_install() {
+
+ debug "build_api/api2" "Starting real_default_post_install"
+ install_xinetd
+ install_initd
+ install_pam_confs
+ install_desktop_files
+ gather_docs
+ devoke_installwatch
+ init_post_install
+ ldconfig
+ # release_saved_libraries
+ cd /
+
+}
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/build_api/common b/var/lib/sorcery/modules/build_api/common
new file mode 100644
index 0000000..338e58d
--- /dev/null
+++ b/var/lib/sorcery/modules/build_api/common
@@ -0,0 +1,313 @@
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Functions for dealing with the actual compiling/installation of spells
+## and walking through casts 'pass 4' pipeline.
+##
+##=head1 DESCRIPTION
+##
+##
+##=head1 COPYRIGHT
+##
+## Copyright (C) 2002 The Source Mage Team <http://www.sourcemage.org>
+##
+##=head1 FUNCTIONS
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## Prompts the user about possible security problems with the current
+## spell. Allows a safe way of failing a spell due to security problems.
+## @return 0 if there are no security problem or the user acknowledges them.
+## @return 1 if the user decides not to accept the security concerns
+#---------------------------------------------------------------------
+# checks for a security file
+# returns 0 if spell should be cast, 1 otherwise
+function run_security() {
+ debug "build_api/common" "Starting run_security() on $SPELL"
+ if [ -f $SCRIPT_DIRECTORY/SECURITY ]; then
+ echo -e "${SPELL_COLOR}${SPELL}:${DEFAULT_COLOR}"
+ tee -a $SECURITY_LOG < $SCRIPT_DIRECTORY/SECURITY
+ if [ `grep critical $SCRIPT_DIRECTORY/SECURITY` ]; then
+ if query "${RED}SECURITY CRITICAL:${QUERY_COLOR} Do you still want to cast ${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}?" "n"; then
+ return 0
+ fi
+ return 1
+ else
+ if query "SECURITY: Do you still want to cast ${SPELL_COLOR} $SPELL ${QUERY_COLOR}?" "y"; then
+ return 0
+ fi
+ return 1
+ fi
+ fi
+ return 0
+}
+
+
+#---------------------------------------------------------------------
+## pokes around for a configure or src/configure and if it exists
+## asks the user if they want to edit the custom options
+#---------------------------------------------------------------------
+function run_config_loc () {
+ debug "build_api/common" "Starting run_config_loc() on $SPELL"
+ pushd . > /dev/null # may have been somewhere else
+ [ -d "$SOURCE_DIRECTORY" ] &&
+ cd $SOURCE_DIRECTORY # we need to be in here for this function to work
+ if [[ $CONFIG_LOC == on ]]; then
+
+ if [ -x ./configure ] || [ -x ./src/configure ] ; then
+
+ if [ ! -d $SM_CONFIG_OPTION_CACHE ] ; then
+ mkdir --parents --mode=0755 $SM_CONFIG_OPTION_CACHE
+ fi
+
+ if [ -f $SM_CONFIG_OPTION_CACHE/$SPELL ] ; then
+ message "${MESSAGE_COLOR}These are your current -- config options for spell ${SPELL_COLOR}$SPELL"
+ message "${FILE_COLOR}($SM_CONFIG_OPTION_CACHE/$SPELL)"
+ cat $SM_CONFIG_OPTION_CACHE/$SPELL | column
+ fi
+
+ if query "Do you wish to add -- options to ./configure?" n ; then
+ F_TMP=$TMP_DIR/cast.$$.configure
+ rm -f $F_TMP
+
+ if [ -x ./configure ]; then
+ ./configure --help > $F_TMP
+ elif [ -x ./src/configure ]; then
+ ./src/configure --help > $F_TMP
+ fi
+
+ if [ -f $F_TMP ]; then
+ sedit 's/^/# /' $F_TMP
+ fi
+
+ cat $SM_CONFIG_OPTION_CACHE/$SPELL >> $F_TMP 2>/dev/null
+ edit_file $F_TMP
+ rm -f $SM_CONFIG_OPTION_CACHE/$SPELL
+ sedit '/^#.*$/d' $F_TMP
+ mv $F_TMP $SM_CONFIG_OPTION_CACHE/$SPELL
+ fi
+
+ # load custom OPTS
+ if [ -f $SM_CONFIG_OPTION_CACHE/$SPELL ]; then
+ OPTS="$OPTS $(< $SM_CONFIG_OPTION_CACHE/$SPELL)"
+ message "${MESSAGE_COLOR} OPTS= ${DEFAULT_COLOR}$OPTS"
+ fi
+
+ fi
+
+ fi
+
+ popd &>/dev/null
+
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## Prepares to install the spell. If the spell is installed already,
+## the libraries are saved with save_libraries() and the spell is dispelled.
+## Usually called from the BUILD or PRE_INSTALL script of a spell.
+## Locks "libgrimoire" "install" before proceeding.
+## If the spell is not installed (or held) the spell is not dispelled.
+##
+#---------------------------------------------------------------------
+function real_prepare_install() { (
+
+ debug "libgrimoire" "Running prepare_install() on $SPELL"
+
+ message "${MESSAGE_COLOR}Preparing to install" \
+ "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}"
+
+ devoke_installwatch
+ lock_resources "libgrimoire" "install"
+
+ if spell_installed $SPELL ||
+ spell_held $SPELL
+ then
+
+ trap "spell_recover" SIGINT
+ save_libraries
+ dispel --notriggers --nosustain $SPELL
+
+ fi
+) }
+
+#---------------------------------------------------------------------
+##
+## Gathers all documentation files from source archive and installs
+## them as part of the spell
+##
+#---------------------------------------------------------------------
+function real_gather_docs() {
+
+ if [ "$GATHER_DOCS" == "on" ]; then
+
+ debug "libgrimoire" "Running gather_docs() on $SPELL DOCS=$DOCS"
+
+ DOC_DIR=$DOCUMENT_DIRECTORY/$SPELL
+ mkdir -p $DOC_DIR
+
+ for i in ${DOCS[@]}
+ do
+ cp -r $SOURCE_DIRECTORY/${i} $DOC_DIR 2>/dev/null
+ done
+
+ # what is this here for and why isn't it part of the standard DOCS
+ # variable?
+ cp $SOURCE_DIRECTORY/*rc $DOC_DIR 2>/dev/null
+
+ # making installed docs readable for all users.
+ chmod -fR a+r $DOC_DIR
+
+ fi
+
+}
+
+
+#---------------------------------------------------------------------
+##
+## Copies pam configuration files to /etc/pam.d/
+##
+#---------------------------------------------------------------------
+function install_pam_confs() {
+
+ debug "libgrimoire" "Running install_pam_confs() on $SPELL"
+
+ if [ -d "$SCRIPT_DIRECTORY/pam.d" ]; then
+ cd "$SCRIPT_DIRECTORY/pam.d"
+
+ mkdir -p $INSTALL_ROOT/etc/pam.d
+ local FILE
+ for FILE in `ls`; do
+ if ! [ -f $INSTALL_ROOT/etc/pam.d/$FILE ]; then
+ cp $FILE $INSTALL_ROOT/etc/pam.d
+ fi
+ done
+
+ fi
+
+}
+
+#---------------------------------------------------------------------
+## Stuff that should be done if run_build_spell succeeds
+## This is intended to be overridable at some point
+#---------------------------------------------------------------------
+function run_spell_success() {
+ debug "build_api/common" "cast of $SPELL-$VERSION was successful"
+
+ local INST_LOG="$INSTALL_LOGS/$SPELL-$VERSION"
+ local MD5_LOG="$MD5SUM_LOGS/$SPELL-$VERSION"
+ local TMP_INST_LOG="$TMP_DIR/$SPELL-$VERSION.install"
+ local TMP_MD5_LOG="$TMP_DIR/$SPELL-$VERSION.md5"
+ local CACHE="$INSTALL_CACHE/$SPELL-$VERSION-$HOST.tar"
+ local CACHE_COMP=${CACHE}${EXTENSION}
+ local C_LOG_COMP="$COMPILE_LOGS/$SPELL-$VERSION$EXTENSION"
+
+ sound SUCCESS
+
+ # announce to everyone the spell succeeded
+ activity_log "cast" "$SPELL" "$VERSION" "success"
+ add_spell $SPELL installed $VERSION
+ echo $SPELL >> $SUCCESS_LIST
+
+ # update depends info
+ debug "build_api/common" "Merging depends info"
+ local t_DEPENDS_STATUS=$(lock_start_transaction $DEPENDS_STATUS)
+ # none of the old values are valid, only the new, uncommitted values are
+ remove_depends_status $t_DEPENDS_STATUS $SPELL
+ local spell_depends=$(hash_get uncommitted_hash $SPELL)
+ if [ -e $spell_depends ] ; then
+ cat $spell_depends >> $t_DEPENDS_STATUS
+ fi
+ lock_commit_transaction $DEPENDS_STATUS
+
+ # since the database is now accurate, no reason to keep old answers
+ # floating around
+ test -e "$ABANDONED_DEPENDS/$SPELL" &&
+ rm -f $ABANDONED_DEPENDS/$SPELL &>/dev/null
+
+ # compile log
+ create_compile_log
+ view_compile_log
+
+ local tablet_dir=$(tablet_get_path $SPELL)
+ if [ "$?" != 0 ] ; then
+ message "failed to make a tablet directory: $tablet_dir" \
+ "this may be a bug"
+ unset tablet_dir
+ else
+ message "${MESSAGE_COLOR}Creating tablet in directory" \
+ "${FILE_COLOR}${tablet_dir}${DEFAULT_COLOR}"
+ tablet_install_spell_files $tablet_dir
+
+ # this data is now doubly stale, remove it
+ rm -f $ABANDONED_PERSIST/$SPELL.p
+ fi
+
+ # install log
+ message "${MESSAGE_COLOR}Creating install log" \
+ "${FILE_COLOR}${INST_LOG}${DEFAULT_COLOR}"
+ # this will make a "root" format install log it will need to be
+ # reformatted to "log" format (see log_adjuster in libtrack for details)
+ # but basically replacing INSTALL_ROOT with TRACK_ROOT
+ create_install_log $IW_LOG $TMP_INST_LOG
+ if [[ $tablet_dir ]] ; then
+ find $tablet_dir >> $TMP_INST_LOG
+ fi
+ log_adjuster $TMP_INST_LOG $INST_LOG root log
+
+ # md5sum log
+ message "${MESSAGE_COLOR}Creating MD5 log" \
+ "${FILE_COLOR}${MD5_LOG}${DEFAULT_COLOR}"
+ create_md5list $TMP_INST_LOG $TMP_MD5_LOG
+ log_adjuster $TMP_MD5_LOG $MD5_LOG root log md5_log_filter
+
+ # do this before cache archive creation (bug 8249)
+ rm_source_dir
+
+ # cache archive
+
+ # Archives are stored with INSTALL_ROOT and STATE_ROOT stripped.
+ # this way they can be unpacked anywhere and sorcery will know
+ # where things will be.
+ create_cache_archive $TMP_INST_LOG $CACHE $CACHE_COMP
+
+ report_install
+ rm -f $IW_LOG $C_LOG $TMP_INST_LOG $TMP_MD5_LOG $spell_depends
+}
+
+#---------------------------------------------------------------------
+## Stuff that should be done if run_build_spell fails
+## This is intended to be overridable at some point
+#---------------------------------------------------------------------
+function run_spell_failure() {
+ debug "build_api/common" "cast of $SPELL-$VERSION failed"
+ local rc=$1
+
+ sound FAILURE
+
+ activity_log "cast" "$SPELL" "$VERSION" "failure"
+ echo $SPELL >> $FAILED_LIST
+
+ # if it failed after PRE_BUILD make a compile log
+ if [ "$rc" != 1 ] ; then
+ create_compile_log
+ view_compile_log
+ fi
+
+ # put the answers somewhere where they can still be used later
+ mkdir -p $ABANDONED_DEPENDS
+ local spell_depends=$(hash_get uncommitted_hash $SPELL)
+ [ -e $spell_depends ] && mv $spell_depends $ABANDONED_DEPENDS/$SPELL
+
+ # This may have been locked if there was a failure during the install
+ unlock_resources "libgrimoire" "install"
+
+ IW_LOG="$INSTALLWATCHFILE"
+ devoke_installwatch
+
+ rm -f $IW_LOG $C_LOG
+
+ [[ $CLEAN_SOURCE == on ]] && rm_source_dir
+ CAST_EXIT_STATUS=1
+}
diff --git a/var/lib/sorcery/modules/dl_handlers/dl_cvs b/var/lib/sorcery/modules/dl_handlers/dl_cvs
new file mode 100644
index 0000000..7f596d0
--- /dev/null
+++ b/var/lib/sorcery/modules/dl_handlers/dl_cvs
@@ -0,0 +1,109 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Download handler for downloading cvs urls.
+##
+##=head1 DESCRIPTION
+##
+## This file contains functions for I<downloading> files through cvs.
+##
+## In order for cvs urls to be downloaded, the I<cvs> spell must have been
+## cast. This script first determines if cvs has been installed before
+## attempting to download a cvs url.
+##
+##
+##=head1 COPYRIGHT
+##
+## Copyright 2002 by the Source Mage Team
+## Copyright 2005 by the Source Mage Team
+##
+##=head1 FUNCTIONS
+##
+##=over 4
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+##=item dl_get_cvs <url>
+##
+## Fetch the specified cvs url.
+##
+## This function always assumes that a tree is being downloaded (for now).
+## If there is a directory names $target, it is assumed that cvs should be
+## updating, not checking out. If the directory doesnt exist, then checkout
+## is assumed.
+##
+## Assume url is valid.
+##
+## No hints are defined currently.
+##
+#---------------------------------------------------------------------
+function dl_cvs_get() {
+ dl_command_check cvs || return 254
+
+ local target=$1
+ local url_list=$2
+ local hints=$3
+ local dl_target=$4
+ local dl_type=$5
+ local rc=1 url
+ local CVS_OPTIONS="-q -z3"
+
+ [[ $target ]] &&
+ dl_connect || return 255
+
+ for url in $url_list ; do
+ local URL CVS_ROOT CVS_MODULE CVS_TAG
+ url_cvs_crack $url
+
+ # if the target is a directory then its quite possible we're
+ # updating a tree rather than checking a new one out
+ if test -d $target; then
+ message "${MESSAGE_COLOR}Running cvs update...${DEFAULT_COLOR}"
+ if cd $target; then
+ cvs $CVS_OPTIONS update -d -r $CVS_TAG
+ rc=$?
+ cd ..
+ else
+ dl_disconnect
+ return 1
+ fi
+ else
+ message "${MESSAGE_COLOR}Running cvs checkout...${DEFAULT_COLOR}"
+ cvs $CVS_OPTIONS -d$CVS_ROOT checkout -r $CVS_TAG -d $target $CVS_MODULE
+ rc=$?
+ fi
+ [[ $rc == 0 ]] && break
+ done
+ dl_disconnect
+
+ eval "$dl_target=\"$target\""
+ eval "$dl_type=\"tree\""
+ return $rc
+}
+
+
+
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
+
diff --git a/var/lib/sorcery/modules/dl_handlers/dl_dir b/var/lib/sorcery/modules/dl_handlers/dl_dir
new file mode 100644
index 0000000..7a4bda9
--- /dev/null
+++ b/var/lib/sorcery/modules/dl_handlers/dl_dir
@@ -0,0 +1,92 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Url handler functions for grabbing directory urls.
+##
+##=head1 DESCRIPTION
+##
+## This type of url was added as a response to the following request:
+##
+## I'm an AbiWord developer, and I keep the latest source
+## tree in my abi directory. If I could make the abi spell
+## (not to be confused with the abispell) use my current
+## CVS checked-out tree, that would be nice.
+##
+## This file contains functions for I<downloading> (actually it just
+## copies, tars, and compresses) directories which can be accessed
+## through the local file system.
+##
+## Url's of this type are specified using the format
+##
+## dir://<directory>
+##
+## where <directory> is the full path to a directory that will be
+## tarred and compressed for use by sorcery in casting a spell.
+##
+##=head1 COPYRIGHT
+##
+## Copyright 2002 by the Source Mage Team
+##
+##=head1 FUNCTIONS
+##
+##=over 4
+##
+#---------------------------------------------------------------------
+
+
+#---------------------------------------------------------------------
+##=item dl_get_dir <url>
+##
+## Copies the specified dir url.
+##
+#---------------------------------------------------------------------
+function url_dir_get() {
+
+ local target=$1
+ local url_list=$2
+ local hints=$3
+ local dl_target=$4
+ local dl_type=$5
+ local url rc=1
+ local DIRNAME
+
+ [[ $target ]] || return 255
+
+ for url in $url_list; do
+ DIRNAME=`url_dir_crack $url`
+ if test -d "$DIRNAME" ; then
+ message "Copying $DIRNAME to $target."
+ cp -aR $DIRNAME $target &&
+ message "Finished."
+ fi
+ done
+
+ eval "$dl_target=\"$target\""
+ eval "$dl_type=\"tree\""
+ return $rc
+
+}
+
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
+
diff --git a/var/lib/sorcery/modules/dl_handlers/dl_file b/var/lib/sorcery/modules/dl_handlers/dl_file
new file mode 100644
index 0000000..28e0a1d
--- /dev/null
+++ b/var/lib/sorcery/modules/dl_handlers/dl_file
@@ -0,0 +1,69 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## @Synopsis Url handler functions for grabbing file urls.
+## This file contains functions for I<downloading> (actually it just
+## copies) files which can be accessed through the local file system.
+## Url's of this type are specified using the format
+##
+## file://<path>
+##
+## where <path> is the full path to the file.
+##
+## @Copyright Copyright 2002 by the Source Mage Team
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## @param url
+##
+## Copies the specified file url.
+##
+#---------------------------------------------------------------------
+function dl_file_get() {
+
+ local target=$1
+ local url_list=$2
+ local hints=$3
+ local dl_target=$4
+ local dl_type=$5
+ local url rc=1
+ local FILENAME
+
+ [[ $target ]] || return 255
+
+ for url in $url_list; do
+ FILENAME=`url_file_crack $url` &&
+ test -f "$FILENAME" &&
+ test -r "$FILENAME" &&
+ cp -a $FILENAME $target && break
+ done
+
+ eval "$dl_target=\"$target\""
+ eval "$dl_type=\"file\""
+ return $rc
+
+}
+
+
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
+
diff --git a/var/lib/sorcery/modules/dl_handlers/dl_rsync b/var/lib/sorcery/modules/dl_handlers/dl_rsync
new file mode 100644
index 0000000..3553121
--- /dev/null
+++ b/var/lib/sorcery/modules/dl_handlers/dl_rsync
@@ -0,0 +1,180 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Url handler functions for downloading rsync urls.
+##
+##=head1 DESCRIPTION
+##
+## This file contains functions for I<downloading> files through rsync.
+##
+## In order for rsync urls to be downloaded, the I<rsync> spell must have been
+## cast. This script first determines if rsync has been installed before
+## attempting to download a rsync url.
+##
+##=head1 RSYNC URL Format
+##
+##
+## rsync://SERVER::MODULE_NAME
+##
+## The above url will download the latest version of the specified
+## module.
+##
+##=head1 EXAMPLES
+##
+## Suppose we want to download the latest version of the sorcery
+## stable grimoire via rsync. We'd use the following url:
+##
+## rsync://codex.sourcemage.org::stable
+##
+##=head1 IMPLEMENTATION NOTE
+##
+## Downloading is supported but rsync url verification is not
+## currently supported.
+##
+##=head1 COPYRIGHT
+##
+## Copyright 2003 by the Source Mage Team
+##
+##=head1 FUNCTIONS
+##
+##=over 4
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+##=item dl_rsync_download <url>
+##
+## Fetch the specified rsync url.
+##
+## This handler supports both files and trees.
+##
+#---------------------------------------------------------------------
+function dl_rsync_get() {
+ dl_command_check rsync || return 254
+
+ local target=$1
+ local url_list=$2
+ local hints=$3
+ local dl_target=$4
+ local dl_type=$5
+ local url rc=1
+
+ [[ $target ]] &&
+ dl_connect || return 255
+
+ local dl_file=0
+ if list_find "$hints" file; then
+ dl_file=1
+ fi
+
+ for url in $url_list; do
+ url=`url_rsync_crack "$url"`
+ dl_rsync_run_rsync "$target" "$url" "$dl_target" "$dl_type"
+ rc=$?
+ [[ $rc == 0 ]] && break
+ done
+ dl_disconnect
+
+ return $rc
+}
+
+#---------------------------------------------------------------------
+#=item dl_rsync_run_rsync <url> <to>
+#
+# Private function. Calls rsync and beautifies output
+#
+#---------------------------------------------------------------------
+function dl_rsync_run_rsync() {
+ local target=$1
+ local url=$2
+ local dl_target=$3
+ local dl_type=$4
+
+ local TOTAL line retcode
+ local COUNT=0
+ local position="a"
+ local use_spinner
+
+ if [ "$dl_file" == 1 ]; then
+ TOTAL=0 # downloading a single file, not a source tree
+ else
+ let TOTAL=$(find $1 -type f 2>/dev/null | wc -l)
+ if [ "$TOTAL" -lt 2 ] ; then
+ let TOTAL=100
+ use_spinner=yes
+ else
+ let TOTAL+=10
+ fi
+ fi
+
+ message "${MESSAGE_COLOR}Running rsync...${DEFAULT_COLOR}"
+ if [ $TOTAL -lt 10 ] ; then
+ echo rsync -rz --delete --stats --progress "$url" "$target"
+ rsync -rz --delete --stats --progress "$url" "$target" #few files - big files
+ retcode=$?
+ else
+ echo rsync -rz --delete --stats -vv "$url" "$target"
+ {
+ rsync -rz --delete --stats -vv "$url" "$target"
+ echo $? > $TMP_DIR/rsync.rc
+ } | tee $TMP_DIR/rsyncout$$ |
+ while read line ; do
+ if [ "$position" == "c" ] ;then
+ #make the progress bar - as quick as possible
+ let COUNT=($COUNT+1)%$TOTAL
+ if [[ $use_spinner ]] ; then
+ progress_spinner
+ else
+ progress_bar $COUNT $TOTAL 50
+ fi
+ elif [ "$position" == "a" ] ;then
+ echo "$line" | grep -q 'receiving file list ...' && position="b"
+ echo "$line" #print welcome message until filelist almost starts
+ elif [ "$position" == "b" ] ;then
+ echo "$line" | grep -q 'done' && position="c"
+ echo "$line" #only now look for done = only filenames following
+ fi
+ done
+ retcode=$(<$TMP_DIR/rsync.rc)
+ [[ $retcode == 0 ]] &&
+ [[ $use_spinner ]] ||
+ progress_bar $TOTAL $TOTAL 50 # make the progress bar show 100, on success
+
+ echo
+ tail -n 25 $TMP_DIR/rsyncout$$ | head -n 12 #stats w/o useless stack stats
+ echo
+ rm -f $TMP_DIR/rsyncout$$
+ fi
+ if test -d $target; then
+ eval "$dl_type=\"tree\""
+ else
+ eval "$dl_type=\"file\""
+ fi
+ eval "$dl_target=\"$target\""
+ return $retcode
+}
+
+
+
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/dl_handlers/dl_svn b/var/lib/sorcery/modules/dl_handlers/dl_svn
new file mode 100644
index 0000000..6d50484
--- /dev/null
+++ b/var/lib/sorcery/modules/dl_handlers/dl_svn
@@ -0,0 +1,93 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+##=head1 COPYRIGHT
+##
+## Copyright 2004 by the Source Mage Team
+##
+##=head1 FUNCTIONS
+##
+##=over 4
+##
+#---------------------------------------------------------------------
+
+
+#---------------------------------------------------------------------
+##=item dl_get_svn <url>
+##
+## Fetch the specified svn url.
+##
+## This handler only supports tree downloads.
+##
+#---------------------------------------------------------------------
+function dl_svn_get () {
+ dl_command_check svn || return 254
+
+ local target=$1
+ local url_list=$2
+ local hints=$3
+ local dl_target=$4
+ local dl_type=$5
+ local url rc=0
+
+ [[ $target ]] &&
+ dl_connect || return 255
+
+ for url in $url_list; do
+ local URL SVN_ROOT SVN_MODULE SVN_TAG
+ url_svn_crack $url
+
+ if test -d $target; then
+ message "${MESSAGE_COLOR}Running svn update...${DEFAULT_COLOR}"
+ svn update -q --non-interactive -r $SVN_TAG $target
+ rc=$?
+ eval "$dl_target=\"$target\""
+ else
+ message "${MESSAGE_COLOR}Running svn checkout...${DEFAULT_COLOR}"
+ # hard-coded http :-( ....
+ case $SVN_TYPE in
+ svn)
+ message "svn checkout -r $SVN_TAG http://$SVN_ROOT $SVN_MODULE"
+ svn checkout -r $SVN_TAG http://$SVN_ROOT $SVN_MODULE
+ rc=$?
+ ;;
+ svns)
+ message "svn checkout -r $SVN_TAG https://$SVN_ROOT $SVN_MODULE"
+ svn checkout -r $SVN_TAG https://$SVN_ROOT $SVN_MODULE
+ rc=$?
+ ;;
+ *)
+ message "unknow url type"
+ rc=2
+ ;;
+ esac
+ eval "$dl_target=\"$SVN_MODULE\""
+ fi
+ [[ $rc == 0 ]] && break
+ done
+ dl_disconnect
+
+ eval "$dl_type=\"tree\""
+ return $rc
+}
+
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/dl_handlers/dl_tla b/var/lib/sorcery/modules/dl_handlers/dl_tla
new file mode 100644
index 0000000..2e7372b
--- /dev/null
+++ b/var/lib/sorcery/modules/dl_handlers/dl_tla
@@ -0,0 +1,97 @@
+#!/bin/bash
+## ----------------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Url handler functions for grabbing tla urls.
+##
+##
+##=head1 COPYRIGHT
+##
+## Copyright 2005 the SourceMage Team
+##
+## ----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+##=item dl_get_tla <url>
+##
+## Fetch the specified tla url.
+##
+## This handler only supports tree downloads.
+##
+# -----------------------------------------------------------------------------
+function dl_tla_get() {
+ dl_command_check tla || return 254
+
+ local target=$1
+ local url_list=$2
+ local hints=$3
+ local dl_target=$4
+ local dl_type=$5
+ local rc=1 url
+
+ [[ $target ]] &&
+ dl_connect || return 255
+
+ for url in $url_list; do
+ local URL TLA_ARCHIVE TLA_LOCATION TLA_REVISION
+
+ if ! tla archives | grep -q "\<$TLA_ARCHIVE\>"; then
+ tla register-archive $TLA_ARCHIVE $TLA_LOCATION || {
+ message "${PROBLEM_COLOR}Error registering archive${DEFAULT_COLOR}"
+ continue
+ }
+ else
+ debug "archive $TLA_ARCHIVE is already registered"
+ fi
+ if test -d $target; then
+ pushd $target &>/dev/null &&
+ message "${MESSAGE_COLOR}Running tla update...${DEFAULT_COLOR}"
+ tla update $TLA_ARCHIVE/$TLA_REVISION &&
+ tla register-archive -d $TLA_ARCHIVE $TLA_LOCATION &&
+ popd || {
+ message "${PROBLEM_COLOR}Error updating cached archive${DEFAULT_COLOR}"
+ tla register-archive -d $TLA_ARCHIVE $TLA_LOCATION &>/dev/null
+ continue
+ }
+ else
+ message "${MESSAGE_COLOR}Running tla get...${DEFAULT_COLOR}"
+ tla get -A $TLA_ARCHIVE $TLA_REVISION $target &&
+ tla register-archive -d $TLA_ARCHIVE $TLA_LOCATION || {
+ message "${PROBLEM_COLOR}Error checking out archive${DEFAULT_COLOR}"
+ continue
+ }
+ fi
+ rc=0
+ break
+ done
+
+ dl_disconnect
+
+ eval "$dl_target=\"$target\""
+ eval "$dl_type=\"tree\""
+ return $rc
+}
+
+
+
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/dl_handlers/dl_wget b/var/lib/sorcery/modules/dl_handlers/dl_wget
new file mode 100644
index 0000000..b69ca19
--- /dev/null
+++ b/var/lib/sorcery/modules/dl_handlers/dl_wget
@@ -0,0 +1,142 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Url handler functions for downloading http, https, and ftp urls
+##
+##=head1 DESCRIPTION
+##
+## This file contains functions for downloading and verifying
+## http, https, and ftp urls. This file uses the "wget" program.
+##
+##=head1 COPYRIGHT
+##
+## Copyright 2002 by the Source Mage Team
+##
+##=head1 FUNCTIONS
+##
+##=over 4
+##
+#---------------------------------------------------------------------
+
+function dl_wget_get() {
+ debug libdownload "$FUNCNAME -- $@"
+ dl_command_check wget || return 254
+
+ local target=$1
+ local url_list=$2
+ local hints=$3
+ local dl_target=$4
+ local dl_type=$5
+ local rc=1 url
+
+ [[ $target ]] &&
+ dl_connect || return 255
+
+ for url in $url_list; do
+ local WGET_OPTIONS
+ dl_wget_set_options $url
+ dl_wget_call_wget $target $url
+ rc=$?
+ [[ $rc == 0 ]] && break
+ done
+
+ dl_disconnect
+
+ eval "$dl_target=\"$target\""
+ eval "$dl_type=\"file\""
+ return $rc
+
+}
+
+#---------------------------------------------------------------------
+# dl_wget_call_wget <filename> <extension-less filename> <url>
+#
+# Private Function. Call wget to download the url.
+#
+#---------------------------------------------------------------------
+function dl_wget_call_wget() {
+ local FILE=$1
+ local URL=$2
+
+ debug 'dl_wget' "$funcname -- $@"
+
+ rm -f $FILE
+ wget $WGET_OPTIONS "$URL" 2>&1 &&
+ if ! test -f "$FILE" ; then
+ # stupid http site trying to be nice and re-direct us, this is a failure
+ # even though wget doesnt notice it...
+ rm -f $FILE*
+ return 1
+ fi
+}
+
+#---------------------------------------------------------------------
+# dl_wget_set_wget_options
+#
+# Private Function. Sets wget options
+#
+#---------------------------------------------------------------------
+function dl_wget_set_options() {
+ local URL=$1
+ if [ -n "$ACTIVE_FTP" ] ; then
+ unset PASSIVE
+ else
+ PASSIVE="--passive-ftp"
+ fi
+
+ # Check for ? in the url, this seems to indicate that there may be
+ # some cgi redirection involved which means continued downloading would
+ # not work (bug 8993).
+ # The sourceforge check pre-dates that but is lacking any documentation,
+ # I suspect it is a less general attempt to solve the same problem.
+ # (afk 2005-06-25)
+ if echo "$URL" | grep -q "\?" ||
+ echo "$URL" | grep -q "sourceforge" ; then
+ unset CONTINUE
+ else CONTINUE="-c"
+ fi
+
+ if [ -n "$DOWNLOAD_RATE" ] ; then
+ RATE="--limit-rate=${DOWNLOAD_RATE}"
+ fi
+
+ if [ -n "$URL_HTTP_FTP_TIMEOUT" ] ; then
+ URL_HTTP_TIMEOUT="-T $URL_HTTP_FTP_TIMEOUT"
+ else
+ unset URL_HTTP_TIMEOUT
+ fi
+
+ if [ -n "$URL_HTTP_FTP_RETRIES" ] ; then
+ URL_HTTP_RETRIES="-t $URL_HTTP_FTP_RETRIES"
+ else
+ URL_HTTP_RETRIES="-t 3"
+ fi
+
+ ONLY_NEWER=""
+ DEREF_SYM="--retr-symlinks"
+
+ WGET_OPTIONS="$URL_HTTP_TIMEOUT $URL_HTTP_RETRIES $NO_CACHE $RATE $PASSIVE $CONTINUE $ONLY_NEWER $DEREF_SYM"
+ debug 'dl_wget' "wget options: $WGET_OPTIONS"
+}
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libapi b/var/lib/sorcery/modules/libapi
new file mode 100644
index 0000000..dbb65b1
--- /dev/null
+++ b/var/lib/sorcery/modules/libapi
@@ -0,0 +1,1043 @@
+#----------------------------------------
+##
+## @Synopsis Defines the Spell-Sorcery Interface
+## @Copyright Copyright (C) 2004 The Source Mage Team <http://www.sourcemage.org>
+## This library contains all sorcery functions which a spell may use.
+## If the function isn't listed here, it may disapear without warning.
+## If you use a funtion which isn't listed here, you'd better have a good reason.
+## Better yet, raise a bug on the topic and the needed function will probably get
+## added.
+## @NOTE This library must not contain any logic. It is simply a set of wrapper functions.
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+# @NOTE: The following list and the function definition should be in
+# alphabetical order. If this is not the case, please file a bug
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+# functions defined in here: (in alphabetical order)
+# acquire_src (libsummon)
+# config_query (libmisc)
+# config_query_list (libmisc)
+# config_query_option (libmisc)
+# config_query_string (libmisc)
+# conflicts (libgrimoire)
+# default_build (build_api/...)
+# default_download (libsummon)
+# default_install (build_api/api2) (BUILD_API==2 only)
+# default_post_build (build_api/api1) (BUILD_API==1 only)
+# default_post_install (build_api/api2) (BUILD_API==2 only)
+# default_pre_build (build_api/common)
+# default_pre_install (build_api/api2) (BUILD_API==2 only)
+# depends (libdepends)
+# devoke_installwatch (libtrack)
+# download_src (libsummon)
+# gather_docs (build_api/common)
+# get_spell_provider (libstate)
+# get_source_nums (libmisc)
+# guess_compressor (libunpack)
+# handle_changed_config (libresurrect)
+# installed_version (libgrimoire)
+# install_config_file (libresurrect)
+# invoke_installwatch (libtrack)
+# list_add (libmisc)
+# list_find (libmisc)
+# list_remove (libmisc)
+# message (libmisc)
+# mk_source_dir (libgrimoire)
+# on_cast (libtriggers)
+# on_dispel (libtriggers)
+# on_pre_cast (libtriggers)
+# on_pre_dispel (libtriggers)
+# optional_depends (libdepends)
+# persistent_add (libmisc)
+# persistent_clear (libmisc)
+# persistent_load (libmisc)
+# persistent_remove (libmisc)
+# persistent_save (libmisc)
+# prepare_install (libgrimoire)
+# provider_ok (libstate)
+# query (libmisc)
+# query_string (libmisc)
+# requires (libdepends)
+# rm_source_dir (libgrimoire)
+# sedit (libmisc)
+# spell_exiled (libstate)
+# spell_held (libstate)
+# spell_installed (libstate)
+# spell_ok (libstate)
+# track_manual (libtrack)
+# uncompress (libunpack)
+# unpack (libunpack) (deprecated)
+# unpack_file (libunpack)
+# unpack_file_simple (libunpack)
+#
+# Read-only variable that might be of use to a spell:
+# SOURCE_CACHE, OPTS, BUILD HOST, INSTALL_ROOT
+#
+# The following can be modifies in BUILD, but nowhere else:
+# CFLAGS, CXXFLAGS, CPPFLAGS, CC and LDFLAGS
+#
+# The following are set when a spell is loaded, you may use them, but
+# not change them:
+# SCRIPT_DIRECTORY SECTION_DIRECTORY GRIMOIRE SPELL_DIRECTORY SECTION
+#
+# global sorcery variables you should never use in a spell:
+#
+# (do not modify or read any of these for any reason, if you feel you
+# must look at one of them, talk to your local sorcery lead about getting
+# some sort of accessor functionality)
+#
+# TOP_LEVEL DISPLAY PATH TMP_DIR SAFE_CAST
+# FAILED_LIST SUCCESS_LIST SPELL SPELLS spells MAKEFILE DEPS_ONLY
+# CAST_PASS download_log IW_LOG SOLO QUIET INSTALL_QUEUE
+# OVERRIDE_CFLAGS OVERRIDE_CXXFLAGS OVERRIDE_LDFLAGS NO_OPTIMIZATION_FLAGS
+# DOT_PROGRESS VOYEUR_OVERRIDE RECONFIGURE RECAST_DOWN COMPILE RECAST_UP
+# FORCE_DOWNLOAD SILENT FIX DEBUG SEPARATE BASE_URL
+# CAST_HASH BACK_CAST_HASH CANNOT_CAST_HASH uncommitted_hash NEW_DEPENDS
+# spell_depends DEPENDS_CONFIG UP_DEPENDS SPELL_CONFIG GRIMOIRE_DIR
+# BUILD_API VOYEUR_STDOUT VOYEUR_STDERR C_LOG C_FIFO INST_LOG MD5_LOG
+# INSTALLWATCHFILE CAST_EXIT_STATUS
+#
+#---------------------------------------------------------------------
+
+
+#---------------------------------------------------------------------
+## @Type API
+## @params Number portion of SOURCE[[:digit:]], eg '', "2", "3", etc.
+## @See <@function var.lib.sorcery.modules.libsummon.html,real_acquire_src> for more details.
+##
+## Get a particular source, check first in $SOURCE_CACHE and respect
+## FORCE_DOWNLOAD. Calls download_src if the file must be downloaded.
+##
+#---------------------------------------------------------------------
+function acquire_src() {
+ debug "libapi" "$FUNCNAME - $*"
+ real_acquire_src "$@"
+}
+
+#---------------------------------------------------------------------
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_config_query> for more details.
+## @param config file variable
+## @param question
+## @param default answer
+##
+## @return 0 in all cases
+##
+## Asks user for string, with default answer and timeout (like query)
+## Return variable is also marked as persistent
+## <pre>
+## Example:
+## config_query DETAILED "Detailed questions ?" n; then
+## if [ $DETAILED == y ]; then
+## ....
+## fi
+## echo The reply for last question was: $DETAILED
+## </pre>
+##
+#---------------------------------------------------------------------
+function config_query () {
+ debug "libapi" "config_query - $*"
+ real_config_query "$@"
+}
+
+#---------------------------------------------------------------------
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_config_query_list> for more details.
+## @param config file variable, return variable
+## @param question
+## @param elements, ...
+##
+## @return 0 in all cases
+##
+## Asks user for string, with numbered possibilities listed
+## Return variable is also marked as persistent
+## <pre>
+## Example:
+## config_query_list COLOR "What color ?" "red" "white" "blue"
+## echo Your color is $COLOR
+## </pre>
+##
+#---------------------------------------------------------------------
+function config_query_list () {
+ debug "libapi" "config_query_list - $*"
+ real_config_query_list "$@"
+}
+
+#---------------------------------------------------------------------
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_config_query_option> for more details.
+## @param config file variable
+## @param question
+## @param default answer [y|n]
+## @param option_yes - can't be empty string
+## @param option_no - can't be empty string
+##
+## @return 0 in all cases
+##
+## Asks user for string, with default answer and timeout (like query)
+## The string is added to the variable
+## If you want to use empty string, place there dummy string and remove
+## it later by list_remove function. Also for one config variable, all
+## option_yes and option_no have to be different.
+## Return variable is also marked as persistent
+
+## <pre>
+## Example 1:
+## config_query_option OPT "Use X ?" y "--with-x" "--without-x"
+## config_query_option OPT "Use jpeg ?" y "--with-jpeg" "--do-not-use-jpeg"
+## echo All selected options together: $OPT
+##
+## Example 2:
+## CONFIGURE:
+## config_query_option ASK "Use X11 ?" y --with-x11 EMPTY1
+## config_query_option ASK "Use Alsa ?" y --with-alsa EMPTY2
+##
+## BUILD:
+## local TEMP="$ASK"
+## list_remove TEMP EMPTY1 EMPTY2
+## ./configure $TEMP
+## ...
+## </pre>
+##
+#---------------------------------------------------------------------
+function config_query_option () {
+ debug "libapi" "config_query_option - $*"
+ real_config_query_option "$@"
+}
+
+#---------------------------------------------------------------------
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_config_query_string> for more details.
+## @param config file variable, return variable
+## @param question
+## @param default answer
+##
+## @return 0 in all cases
+##
+## Asks user for string, with default answer and timeout (like query)
+## Return variable is also marked as persistent
+## <pre>
+## Example:
+## config_query_string REAL_NAME "What is your real name ?" "nobody"
+## echo Hi $REAL_NAME
+## </pre>
+##
+#---------------------------------------------------------------------
+function config_query_string () {
+ debug "libapi" "config_query_string - $*"
+ real_config_query_string "$@"
+}
+
+
+#---------------------------------------------------------------------
+## @param spell
+## @param default answer to dispel query
+## @Type API
+## @See <@function var.lib.sorcery.modules.libdepends.html,real_conflicts> for more details.
+## If the default answer is anything other than 'y' then 'n' is assumed.
+## returns the given spellname if it is installed
+#---------------------------------------------------------------------
+function conflicts () {
+ debug "libapi" "conflicts - $*"
+ real_conflicts "$@"
+}
+
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libgrimoire.html,real_default_build> for more details.
+## Used if no BUILD script is found for a spell
+## Default build for BUILD_API==1 is:
+## <pre>
+## ./configure --build=$BUILD \
+## --prefix=/usr \
+## --sysconfdir=/etc \
+## --localstatedir=/var \
+## $OPTS &&
+## make &&
+## prepare_install &&
+## make install
+## </pre>
+## Default build for BUILD_API==2 is:
+## <pre>
+## ./configure --build=$BUILD \
+## --prefix=/usr \
+## --sysconfdir=/etc \
+## --localstatedir=/var \
+## $OPTS &&
+## make
+## </pre>
+##
+#---------------------------------------------------------------------
+function default_build () {
+ debug "libapi" "default_build - $*"
+ real_default_build "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libsummon.html,real_default_download> for more details.
+##
+## Default download code, downloads each SOURCE[[:digit:]]*
+##
+#---------------------------------------------------------------------
+function default_download() {
+ debug "libapi" "$FUNCNAME - $*"
+ real_default_download "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libgrimoire.html,real_default_install> for more details.
+## Only defined for BUILD_API==2
+## Used if no INSTALL script is found.
+## Default install is:
+## <pre>
+## make install
+## </pre>
+##
+#---------------------------------------------------------------------
+function default_install () {
+ debug "libapi" "default_install - $*"
+ real_default_install "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libgrimoire.html,real_default_post_build> for more details.
+## Only defined for BUILD_API==1
+## Installs configuration files and documentation. Stops installwatch.
+## Used if no POST_BUILD script is found for a spell.
+## equivalent to default_post_install in BUILD_API==2
+##
+#---------------------------------------------------------------------
+function default_post_build () {
+ debug "libapi" "default_post_build - $*"
+ real_default_post_build "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libgrimoire.html,real_default_post_install> for more details.
+## Only defined for BUILD_API==2
+## Installs configuration files and documentation. Stops installwatch.
+## Used if no POST_INSTALL script is found for a spell.
+## equivalent to default_post_build in BUILD_API==1
+##
+#---------------------------------------------------------------------
+function default_post_install () {
+ debug "libapi" "default_post_install - $*"
+ real_default_post_install "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libgrimoire.html,real_default_pre_build> for more details.
+## Creates the source directory and unpacks the source package into it.
+## Used if no PRE_BUILD script is found for a spell.
+#---------------------------------------------------------------------
+function default_pre_build () {
+ debug "libapi" "default_pre_build - $*"
+ real_default_pre_build "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libgrimoire.html,real_default_pre_install> for more details.
+## Only defined for BUILD_API==2
+## Used if no PRE_INSTALL script is found.
+## Default pre_install is:
+## <pre>
+## prepare_install
+## </pre>
+##
+#---------------------------------------------------------------------
+function default_pre_install () {
+ debug "libapi" "default_pre_install - $*"
+ real_default_pre_install "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell/service
+## @param enabled options (optional)
+## @param description (optional)
+## @param grimoires (optional)
+## @See <@function var.lib.sorcery.modules.libdepends.html,real_depends> for more details.
+## Denotes that a spell requires another spell to work.
+#---------------------------------------------------------------------
+function depends () {
+ debug "libapi" "depends - $*"
+ real_depends "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libtrack.html,real_devoke_installwatch> for more details.
+## Stops using installwatch
+#---------------------------------------------------------------------
+function devoke_installwatch () {
+ debug "libapi" "devoke_installwatch - $*"
+ real_devoke_installwatch "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @params Number portion of SOURCE[[:digit:]], eg '', "2", "3", etc.
+## @See <@function var.lib.sorcery.modules.libsummon.html,real_download_src> for more details.
+##
+## Download a file. If the source is a file tree (eg from cvs or svn)
+## then if there is an existing tarball, it will be unpacked, updated and
+## repackaged as a tar.bz2. If the source is a simple file it will be
+## downloaded as usual.
+##
+#---------------------------------------------------------------------
+function download_src() {
+ debug "libapi" "$FUNCNAME - $*"
+ real_download_src "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.build_api.common.html,real_gather_docs> for more details.
+## Gathers all documentation files from source archive and installs
+## them as part of the spell
+#---------------------------------------------------------------------
+function gather_docs() {
+ debug "libapi" "gather_docs - $*"
+ real_gather_docs "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @params String to prefix on the results.
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_get_source_nums> for more details.
+##
+## Output a list of source numbers associated with the current spell.
+## This is the number portion of SOURCE[[:digit:]], eg '', "2", "3", etc.
+## A prefix may be given and it will be prepended to each result value.
+##
+#---------------------------------------------------------------------
+function get_source_nums() {
+ debug "libapi" "$FUNCNAME - $*"
+ real_get_source_nums "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param Spell name
+## @param Provider name
+## @param If empty get the uncommited spell info, if anything else get
+## info from the committed ($DEPENDS_STATUS) database. If the uncommited
+## db doesnt exist (maybe we're not casting) use DEPENDS_STATUS instead.
+##
+## @See <@function var.lib.sorcery.modules.libstate.html,real_get_spell_provider,> for more details.
+##
+## @return 0 if a provider could be found 1 if not
+## @stdout the provider name(s)
+#---------------------------------------------------------------------
+function get_spell_provider() {
+ debug "libapi" "get_spell_provider - $*"
+ real_get_spell_provider "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param filename
+## @Stdout compressor
+## @See <@function var.lib.sorcery.modules.libunpack.html,real_guess_compressor> for more details.
+##
+## Guesses what program was used to compress a file
+## Return value is always success due to `file' workings
+##
+#---------------------------------------------------------------------
+function guess_compressor () {
+ debug "libapi" "guess_compressor - $*"
+ real_guess_compressor "$@"
+}
+
+#------------------------------------------------------------------------
+## @Type API
+## @param from File we might want to install
+## @param to File to replace
+## @param savetime Backup time if necessary
+##
+## @See <@function var.lib.sorcery.modules.libresurrect.html,real_handle_changed_config> for more details.
+##
+## Present the user with the following menu
+## (0) trash $to and install over it
+## (1) backup $to to $to.$savetime, install the new file in its place
+## (2) leave $to in its place, copy the new file to $to.$savetime
+## (3) do nothing
+## (4) see a diff between $to and the new file
+## choice 2 is currently the default, someday there will be a menu to
+## choose what the default will be
+#------------------------------------------------------------------------
+function handle_changed_config() {
+ debug "libapi" "handle_changed_config - $*"
+ real_handle_changed_config "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell
+## @See <@function var.lib.sorcery.modules.libgrimoire.html,real_install_version> for more details.
+## Returns the current version of the given spell
+##
+#---------------------------------------------------------------------
+function installed_version () {
+ debug "libapi" "installed_version - $*"
+ real_installed_version "$@"
+}
+
+#------------------------------------------------------------------------
+## @Type API
+## @param from File we might want to install
+## @param to File to replace
+## @param savetime Backup time if necessary (optional, defaults to
+## $(date +'%Y%m%d%H%M')
+## @See <@function var.lib.sorcery.modules.libresurrect.html,real_install_config_file> for more details.
+## Returns the current version of the given spell
+#------------------------------------------------------------------------
+function install_config_file() {
+ debug "libapi" "install_config_file - $*"
+ real_install_config_file "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libtrack.html,real_invoke_installwatch> for more details.
+## Starts using installwatch
+#---------------------------------------------------------------------
+function invoke_installwatch () {
+ debug "libapi" "invoke_installwatch - $*"
+ real_invoke_installwatch "$@"
+}
+
+#---------------------------------------------------------------------
+## @param return_var
+## @param elements, ...
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_list_remove> for more details.
+##
+## Removes from the list string(s). Strings are kept to be unique and
+## are separated by spaces
+## <pre>
+## Example:
+## MY_LIST="--disable-static --enable-dynamic"
+## list_remove MY_LIST "--enable-dynamic"
+## </pre>
+##
+#---------------------------------------------------------------------
+function list_remove () {
+ debug "libapi" "list_remove - $*"
+ real_list_remove "$@"
+}
+
+#---------------------------------------------------------------------
+## @param return_var
+## @param elements, ...
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_list_add> for more details.
+##
+## Puts in the list string(s). Strings are kept to be unique and are
+## separated by spaces
+## <pre>
+## Example:
+## list_add MY_LIST "--with-x"
+## list_add MY_LIST "--with-jpeg" $OTHER_OPTS
+## echo $MY_LIST
+## </pre>
+##
+#---------------------------------------------------------------------
+function list_add () {
+ debug "libapi" "list_add - $*"
+ real_list_add "$@"
+}
+
+#---------------------------------------------------------------------
+## @param string
+## @param elements, ...
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_list_find> for more details.
+##
+## return 0 at least one element is in list
+## return 1 none of supplied elements is not in list
+##
+## Finds if at least one of given elements is in the string. Warning,
+## this function takes real string, not variable name as other list_*
+## functions
+## <pre>
+## Example:
+## if list_find "$MY_LIST" "--with-x"; then
+## ... we have to compile X components in
+## fi
+## </pre>
+##
+#---------------------------------------------------------------------
+function list_find () {
+ debug "libapi" "list_find - $*"
+ real_list_find "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param message to echo
+## @Stdout message
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_message> for more details.
+## echo's the given arguments if SILENT is not set.
+##
+#---------------------------------------------------------------------
+function message () {
+ debug "libapi" "message - $*"
+ real_message "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param directory name
+## @param [size]
+## @See <@function var.lib.sorcery.modules.libgrimoire.html,real_mk_source_dir> for more details.
+## Creates a tmpfs filesystem. By default, the size is 1GB.
+## The caller may optionally supply a size argument.
+## <pre>
+## Example1: Create a 2GB mount at $SOURCE_DIRECTORY
+##
+## mk_source_dir $SOURCE_DIRECTORY 2g
+##
+## Example2: Create a mount at /tmp/newdir, defaults to 1GB size
+##
+## mk_source_dir /tmp/newdir
+## </pre>
+#---------------------------------------------------------------------
+function mk_source_dir () {
+ debug "libapi" "mk_source_dir - $*"
+ real_mk_source_dir "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libtriggers.html,real_on_cast> for more details.
+## @param spell that triggers
+## @param action
+##
+## Used by spells to make adding triggerse nice.
+##
+#---------------------------------------------------------------------
+function on_cast () {
+ debug "libapi" "on_cast - $*"
+ real_on_cast "$@"
+}
+
+
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell that triggers
+## @param action
+## @See <@function var.lib.sorcery.modules.libtriggers.html,real_on_dispel> for more details.
+##
+## Used by spells to make adding triggers nice.
+##
+#---------------------------------------------------------------------
+function on_dispel () {
+ debug "libapi" "on_dispel - $*"
+ real_on_dispel "$@"
+}
+
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libtriggers.html,real_on_pre_cast> for more details.
+## @param spell that triggers
+## @param action
+##
+## Used by spells to make adding triggerse nice.
+##
+#---------------------------------------------------------------------
+function on_pre_cast () {
+ debug "libapi" "on_pre_cast - $*"
+ real_on_pre_cast "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell that triggers
+## @param action
+## @See <@function var.lib.sorcery.modules.libtriggers.html,real_on_pre_dispel> for more details.
+##
+## Used by spells to make adding triggerse nice.
+##
+#---------------------------------------------------------------------
+function on_pre_dispel () {
+ debug "libapi" "on_pre_dispel - $*"
+ real_on_pre_dispel "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell/service
+## @param enabled options (optional)
+## @param disabled options (optional)
+## @param description (optional)
+## @param grimoires (optional)
+## @See <@function var.lib.sorcery.modules.libdepends.html,real_optional_depends> for more details.
+## Denotes that a spell can use another spell for additional functionality.
+#---------------------------------------------------------------------
+function optional_depends () {
+ debug "libapi" "optional_depends - $*"
+ real_optional_depends "$@"
+}
+
+#---------------------------------------------------------------------
+## @param variables, ...
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_persistent_add> for more details.
+##
+## Adds variable names to the list of persistent variables
+## <pre>
+## Example:
+## persistent_add MY_VARIABLE
+## </pre>
+##
+#---------------------------------------------------------------------
+function persistent_add () {
+ debug "libapi" "persistent_add - $*"
+ real_persistent_add "$@"
+}
+
+#---------------------------------------------------------------------
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_persistent_clear> for more details.
+## Unsets all persistent variables. Mainly usable as replacement of
+## persistent_save for functions which can be called by nonroot users
+## ( for example from 'gaze what' )
+## <pre>
+## Example:
+## persistent_clear
+## </pre>
+##
+#---------------------------------------------------------------------
+function persistent_clear () {
+ debug "persistent_clear - $*"
+ real_persistent_clear "$@"
+}
+
+#---------------------------------------------------------------------
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_persistent_load> for more details.
+## Loads persistent variables stored in file "$SPELL_CONFIG"
+## <pre>
+## Example:
+## persistent_load
+## </pre>
+##
+#---------------------------------------------------------------------
+function persistent_load () {
+ debug "libapi" "persistent_load - $*"
+ real_persistent_load "$@"
+}
+
+#---------------------------------------------------------------------
+## @param variables, ...
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_persistent_remove> for more details.
+##
+## Removes variable names from the list of persistent variables
+## <pre>
+## Example:
+## persistent_remove MY_VARIABLE
+## </pre>
+##
+#---------------------------------------------------------------------
+function persistent_remove () {
+ debug "libapi" "persistent_remove - $*"
+ real_persistent_remove "$@"
+}
+
+#---------------------------------------------------------------------
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_persistent_save> for more details.
+## Saves variables marked as persistent to file "$SPELL_CONFIG". The
+## File is completely overwritten. Also unsets all persistent
+## variables
+## <pre>
+## Example:
+## persistent_save
+## </pre>
+##
+#---------------------------------------------------------------------
+function persistent_save () {
+ debug "libapi" "persistent_save - $*"
+ real_persistent_save "$@"
+}
+
+#---------------------------------------------------------------------
+## @param spell
+## @param variable to read
+## @param upvar
+##
+## finds the persistent var for a spell and sets the upvar to the value of the
+## variable to read
+## returns:
+## 1 if the var doesn't exist in the file
+## 2 if the tablet dir doesn't exist
+## 3 if the persistent config file doesn't exist
+## 4 if the EXPORTS file doesn't exist
+##
+#---------------------------------------------------------------------
+function persistent_read () {
+ debug "libapi" "persistent_read - $*"
+ real_persistent_read "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libgrimoire.html,real_prepare_install> for more details.
+## Prepares to install the spell. Writes the boost lock file. If the
+## spell is installed already, the libraries are saved with
+## save_libraries() and the spell is dispelled. Usually called from
+## the BUILD sript of a spell.
+##
+#---------------------------------------------------------------------
+function prepare_install () {
+ debug "libapi" "prepare_install - $*"
+ real_prepare_install "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param Provider name
+## @See <@function var.lib.sorcery.modules.libstate.html,real_provider_ok> for more details.
+##
+## @return 0 if any provider of $1 is installed
+#---------------------------------------------------------------------
+function provider_ok() {
+ debug "libapi" "provider_ok - $*"
+ real_provider_ok "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param [directory to remove]
+## @Globals SOURCE_DIRECTORY
+## Removes the given directory or SOURCE_DIRECTORY if no argument is
+## given.
+## @See <@function var.lib.sorcery.modules.libgrimoire.html,real_rm_source_dir> for more details.
+##
+#---------------------------------------------------------------------
+function rm_source_dir() {
+ debug "libapi" "rm_source_dir - $*"
+ real_rm_source_dir "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_query> for more details.
+## @param question
+## @param default answer
+##
+## @return 0 on yes
+## @return 1 on no
+##
+## Asks the user a yes/no question. First argument is the question to
+## ask, second argument is the default answer. If a timeout occurs
+## before the question is answered, the given default answer is
+## applied. Returns true or false based on the answer given.
+##
+#---------------------------------------------------------------------
+function query () {
+ debug "libapi" "query - $*"
+ real_query "$@"
+}
+
+#---------------------------------------------------------------------
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_query_string> for more details.
+## @param return_var
+## @param question
+## @param default answer
+##
+## @return 0 user supplied answer
+## @return 1 default answer is used
+##
+## Asks user for string, with default answer and timeout (like query)
+## <pre>
+## Example:
+## query_string YOUR_HOST "What is your hostname ?" "localhost"
+## </pre>
+##
+#---------------------------------------------------------------------
+function query_string () {
+ debug "libapi" "query_string - $*"
+ real_query_string "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param sed command
+## @param file
+## @See <@function var.lib.sorcery.modules.libmisc.html,real_sedit> for more details.
+##
+## First argument is a sed command. Second argument is a file.
+## sedit performs the sed command on the file, modifiying the
+## original file. For example,
+## <br>sedit "s/foo/bar/g" /tmp/somefile <br>
+## will replace all occurances of foo with bar in /tmp/somefile.
+## This function is often used in spells to make changes to source
+## files before compiling. See the sed man page for more information.
+##
+#---------------------------------------------------------------------
+function sedit () {
+ debug "libapi" "sedit - $*"
+ real_sedit "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell name
+## @See <@function var.lib.sorcery.modules.libstate.html,real_spell_installed> for more details.
+##
+## @return 0 if the given spell's status is "installed"
+#---------------------------------------------------------------------
+function spell_installed () {
+ debug "libapi" "spell_installed - $*"
+ real_spell_installed "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell name
+## @See <@function var.lib.sorcery.modules.libstate.html,real_spell_installed> for more details.
+##
+## @return 0 if the given spell's status is "held"
+#---------------------------------------------------------------------
+function spell_held() {
+ debug "libapi" "spell_held - $*"
+ real_spell_held "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell name
+## @See <@function var.lib.sorcery.modules.libstate.html,real_spell_ok> for more details.
+##
+## @return 0 if the given spell's status is "installed" or "held"
+#---------------------------------------------------------------------
+function spell_ok() {
+ debug "libapi" "spell_ok - $*"
+ real_spell_ok "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell name
+## @See <@function var.lib.sorcery.modules.libstate.html,real_spell_exiled> for more details.
+##
+## @return 0 if the given spell's status is "exiled"
+#---------------------------------------------------------------------
+function spell_exiled() {
+ debug "libapi" "spell_ok - $*"
+ real_spell_exiled "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param file to uncompress
+## @See <@function var.lib.sorcery.modules.libunpack.html,real_uncompress> for more details.
+##
+## Given a file, uncompress runs the decompression program for that file,
+## Note: zip is a special case because it doesn't work with streams.
+##
+#---------------------------------------------------------------------
+function uncompress () {
+ debug "libapi" "uncompress - $*"
+ real_uncompress "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param file to unpack
+## @param md5sum
+## @See <@function var.lib.sorcery.modules.libunpack.html,real_unpack> for more details.
+##
+## Given a file, unpack runs the decompression program for that file,
+## as well as untar'ing the file if appropriate and if the MD5
+## matches.
+## Note: zip is a special case because it doesn't work with streams.
+##
+#---------------------------------------------------------------------
+function unpack () {
+ debug "libapi" "unpack - $*"
+ real_unpack "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param SOURCE suffix
+## @See <@function var.lib.sorcery.modules.libunpack.html,real_unpack_file> for more details.
+##
+## unpack_file takes the SOURCE suffix and figures out if it is supposed
+## to md5 or gpg/hash check it -- then it does its dirty work and runs
+## unpack, unpack_gpg, or unpack_hash depending upon the circumstances.
+##
+#---------------------------------------------------------------------
+function unpack_file () {
+ debug "libapi" "unpack_file - $*"
+ real_unpack_file "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @params Name of a file to unpack
+## @See <@function var.lib.sorcery.modules.libunpack.html,real_unpack_file_simple> for more details.
+##
+## Interface to unpack a file without any verification.
+##
+#---------------------------------------------------------------------
+function unpack_file_simple() {
+ debug "libapi" "$FUNCNAME - $*"
+ real_unpack_file_simple "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param Target of the trigger
+## @param Action to execute
+## @See <@function var.lib.sorcery.modules.libdepends.html,real_up_triggers> for more details.
+##
+## Only allowed from UP_TRIGGERS file.
+## Specify that the current spell should trigger the specifed action
+## on the target spell. This is a shortcut for the TRIGGERS file in the
+## target spell, this lets you specify lots of triggers easily.
+##
+#---------------------------------------------------------------------
+function up_trigger() {
+ debug "libapi" "$FUNCNAME - $*"
+ real_up_trigger "$@"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @params list of files to track manually
+## @See <@function var.lib.sorcery.modules.libtrack.html,real_track_manual> for more details.
+##
+## Given a list of files it will notify installwatch of them.
+## Useful for spells whose components are not dynamicly linked
+## to glibc. Installwatch must be running for this to work.
+#---------------------------------------------------------------------
+function track_manual() {
+ debug "libapi" "track_manual - $*"
+ real_track_manual "$@"
+}
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
+
diff --git a/var/lib/sorcery/modules/libcast b/var/lib/sorcery/modules/libcast
new file mode 100644
index 0000000..b5a61b1
--- /dev/null
+++ b/var/lib/sorcery/modules/libcast
@@ -0,0 +1,703 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+## @Synopsis Functions for dealing with the actual
+## compiling/installation of spells and walking through casts 'pass 4' pipeline.
+## @Copyright Copyright (C) 2002 The Source Mage Team
+## &lt;http://www.sourcemage.org&gt;
+## @Globals $SGL_LIBRARY_MODULES $GRIMOIRE $BUILD_API $USE_FUNCTIONS
+## A spell follows the following path during its installation:
+## PREPARE -&gt; DETAILS -&gt; PRE_BUILD -&gt; BUILD, or COMPILE/INSTALL -&gt;
+## POST_BUILD -&gt; POST_INSTALL -&gt; TRIGGERS
+## Each of these steps, along with some interim steps of dealing with
+## conflicts and security are handled here as well.
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## This is used to dynamically setup an api environment for a spell
+## into the build api that it is specified to use. (ie split BUILD or
+## no split BUILD).
+#---------------------------------------------------------------------
+function load_build_api() {
+
+ debug "libcast" "Starting load_build_api"
+
+ source $SGL_LIBRARY_MODULES/build_api/common
+ case $BUILD_API in
+ 1) source $SGL_LIBRARY_MODULES/build_api/api1 ;;
+ 2) source $SGL_LIBRARY_MODULES/build_api/api2 ;;
+ *) message "${PROBLEM_COLOR}Unknown build api version $BUILD_API, for" \
+ "$SPELL!!${DEFAULT_COLOR}"
+ message "Please update sorcery and see if the problem goes away."
+ message "If it doesnt this may be a bug, please contact" \
+ "the sorcery team. Thanks."
+ return 1 ;;
+ esac
+ load_functions_file
+}
+
+#---------------------------------------------------------------------
+## Load the grimoire and section level FUNCTIONS file
+#---------------------------------------------------------------------
+function load_functions_file() {
+ # the following allows spell writers to override certain functions
+ # in the build pipeline
+ USE_FUNCTIONS=${USE_FUNCTIONS:-on}
+ if [[ $USE_FUNCTIONS == "on" ]] ; then
+ [ -x $GRIMOIRE/FUNCTIONS ] && . $GRIMOIRE/FUNCTIONS
+ [ -x $SECTION_DIRECTORY/FUNCTIONS ] && . $SECTION_DIRECTORY/FUNCTIONS
+ elif [[ $USE_FUNCTIONS == "grimoire" ]] ; then
+ [ -x $GRIMOIRE/FUNCTIONS ] && . $GRIMOIRE/FUNCTIONS
+ elif [[ $USE_FUNCTIONS == "section" ]] ; then
+ [ -x $SECTION_DIRECTORY/FUNCTIONS ] && . $SECTION_DIRECTORY/FUNCTIONS
+ fi
+ return 0
+}
+
+
+#---------------------------------------------------------------------
+## Loads up this environment with the special configuration values
+## that the spell needs. Typically the mode of communication between
+## CONFIGURE/DEPENDS to PRE_BUILD, BUILD, COMPILE, INSTALL, etc.
+## @Globals $SPELL_CONFIG $DEPENDS_CONFIG
+#---------------------------------------------------------------------
+function prepare_spell_config() {
+
+ SPELL_CONFIG=$DEPENDS_CONFIG/$SPELL
+ if ! [ -x $SPELL_CONFIG ]; then
+ touch $SPELL_CONFIG
+ chmod a+x $SPELL_CONFIG
+ fi
+
+ . $SPELL_CONFIG
+
+}
+
+#---------------------------------------------------------------------
+## Prompts the user about spells that are in conflict with the current
+## spell and allows them to dispel them. If the user chooses not to
+## dispel the conflicting spell, the function returns 1
+## @param dis-allow conflict with anything in this list, normally used
+## to prevent self conflicts
+## @return nothing if the user dispels the conflicting spell
+## @return 1 if the user chooses not to dispel the conflicting spell
+#---------------------------------------------------------------------
+function run_conflicts() {
+
+ debug "build_api/common" "Starting run_conflicts() on $SPELL"
+ local ignore_conflict=$1
+ local each
+
+ if [ -x $SCRIPT_DIRECTORY/CONFLICTS ]; then
+ local CONFLICTS=$(
+ persistent_load
+ . $SCRIPT_DIRECTORY/CONFLICTS
+ persistent_save
+ )
+
+ local tmp to_dispel=""
+ for tmp in $CONFLICTS ; do
+ local spell_and_default
+ explode $tmp ':' spell_and_default
+ local skip=no
+ for each in $ignore_conflict ; do
+ [[ $each == "${spell_and_default[0]}" ]] && skip=yes
+ done
+ [[ "$skip" == "yes" ]] && continue
+
+ local text="${SPELL_COLOR}${SPELL}${MESSAGE_COLOR} conflicts with"
+ text="$text ${SPELL_COLOR}${spell_and_default[0]}${MESSAGE_COLOR}."
+ text="$text Dispel"
+ text="$text ${SPELL_COLOR}${spell_and_default[0]}${MESSAGE_COLOR}?"
+
+ if query "$text" ${spell_and_default[1]} ; then
+ to_dispel="$to_dispel ${spell_and_default[0]}"
+ echo ${spell_and_default[0]} >> $CONFLICT_LIST
+ else
+ echo $SPELL >> $FAILED_LIST
+ return 1
+ fi
+
+ dispel $to_dispel
+ done
+ fi
+
+}
+
+
+#---------------------------------------------------------------------
+## Asks the user about init and xinetd services
+##
+## expcetd $SCRIPT_DIRECTORY to be setable, any file one level deep in
+## $SCRIPT_DIRECTORY/xinetd.d or $SCRIPT_DIRECTORY/init.d is assumed to be
+## a service
+##
+## the following persistent variables may be set:
+## XINETD_ENABLED XINETD_DISABLED
+## INIT_ENABLED INIT_DISABLED
+## XINETD_INSTALLED XINETD_NOT_INSTALLED
+## INIT_INSTALLED INIT_NOT_INSTALLED
+##
+## They are all lists used by the list_add/list_find functions. The union
+## of FOO_ENABLED and FOO_DISABLED should be exactly equivalent to
+## FOO_INSTALLED. FOO_ENABLED and FOO_DISABLED have an empty intersection set.
+#---------------------------------------------------------------------
+function query_services() {
+ local XINETD_SCRIPTS
+ local INIT_SCRIPTS
+ local btmp xtmp itmp
+ local tmp retvar
+ local XINETD INIT BOTH found
+ local new_init_provides
+
+ test -d $SCRIPT_DIRECTORY/xinetd.d &&
+ XINETD_SCRIPTS=$(find $SCRIPT_DIRECTORY/xinetd.d -maxdepth 1 -type f \
+ -a -not -name '.*')
+
+ test -d $SCRIPT_DIRECTORY/init.d &&
+ INIT_SCRIPTS=$(find $SCRIPT_DIRECTORY/init.d -maxdepth 1 -type f \
+ -a -not -name '.*' -a -not -name '*.conf')
+
+ local new_init_inst new_init_ninst new_init_enab new_init_disab
+ local new_xinetd_inst new_xinetd_ninst new_xinetd_enab new_xinetd_disab
+
+ for xtmp in $XINETD_SCRIPTS; do
+ for itmp in $INIT_SCRIPTS; do
+ if [[ $(basename $xtmp) == $(basename $itmp) ]] ; then
+ list_add BOTH $(basename $itmp)
+ found=1
+ break
+ fi
+ done
+ done
+
+ for tmp in $INIT_SCRIPTS; do
+ tmp=$(basename $tmp)
+ list_find $tmp $BOTH || list_add INIT $tmp
+ # if the script is ESSENTIAL or RECOMMENDED default to yes unless
+ # the user previously said no
+ if grep -Fq 'ESSENTIAL=yes' $SCRIPT_DIRECTORY/init.d/$tmp ||
+ grep -Fq 'RECOMMENDED=yes' $SCRIPT_DIRECTORY/init.d/$tmp &&
+ ! list_find $tmp $INIT_NOT_INSTALLED; then
+ list_add INIT_INSTALLED $tmp
+ list_add INIT_ENABLED $tmp
+ list_remove INIT_DISABLED $tmp
+ fi
+ done
+
+ for tmp in $XINETD_SCRIPTS; do
+ tmp=$(basename $tmp)
+ list_find $tmp $BOTH || list_add XINETD $tmp
+ done
+
+ local d1 d2
+ for tmp in $BOTH ; do
+ message "${QUERY_COLOR}Would you like to install the init and/or" \
+ "xinetd script for $tmp?${DEFAULT_COLOR}"
+
+ if [[ $DEF_INIT_VS_XINETD == on ]]; then
+ d1=$DEF_INSTALL_INIT; d2=n
+ else
+ d1=n; d2=$DEF_INSTALL_XINETD
+ fi
+ dual_service_query $tmp choice "$XINETD_INSTALLED" "$XINETD_NOT_INSTALLED" \
+ "$INIT_INSTALLED" "$INIT_NOT_INSTALLED" \
+ "$d1" "$d2"
+ if [[ $choice == both ]] ; then
+ list_add new_init_inst $tmp
+ list_add new_xinetd_inst $tmp
+ message "${QUERY_COLOR}Would you like to enable $tmp through init" \
+ "and/or xinetd?${DEFAULT_COLOR}"
+
+ if [[ $DEF_INIT_VS_XINETD ]]; then
+ d1=$DEF_ENABLE_INIT; d2=n
+ else
+ d1=n;d2=$DEF_ENABLE_XINETD
+ fi
+ dual_service_query $tmp choice "$XINETD_ENABLED" "$XINETD_DISABLED" \
+ "$INIT_ENABLED" "$INIT_DISABLED" \
+ "$d1" "$d2"
+
+ case $choice in
+ both) list_add new_init_enab $tmp
+ list_add new_xinetd_enab $tmp
+ ;;
+ init) list_add new_init_enab $tmp
+ list_add new_xinetd_disab $tmp
+ ;;
+ xinetd) list_add new_init_disab $tmp
+ list_add new_xinetd_enab $tmp
+ ;;
+ neither) list_add new_init_disab $tmp
+ list_add new_xinetd_disab $tmp
+ ;;
+ esac
+ elif [[ $choice == init ]] ; then
+ list_add new_init_inst $tmp
+ list_add new_xinetd_ninst $tmp
+ if service_query $tmp "Enable init script $tmp?" "$INIT_ENABLED" \
+ "$INIT_DISABLED" "$DEF_ENABLE_INIT" \
+ new_init_enab new_init_disab; then
+ init_prepare_install "$tmp" "$INIT_PROVIDES" new_init_provides
+ fi
+ elif [[ $choice == xinetd ]] ; then
+ list_add new_init_ninst $tmp
+ list_add new_xinetd_inst $tmp
+ service_query $tmp "Enable xinetd script $tmp?" "$XINETD_ENABLED" \
+ "$XINETD_DISABLED" "$DEF_ENABLE_XINETD" \
+ new_xinetd_enab new_xinetd_disab
+ else
+ list_add new_init_ninst $tmp
+ list_add new_xinetd_ninst $tmp
+ fi
+ done
+
+ for tmp in $INIT; do
+ if grep -Fq 'ESSENTIAL=yes' $SCRIPT_DIRECTORY/init.d/$tmp ; then
+ message "Init script $tmp is \"ESSENTIAL\" for system startup," \
+ "\nit is recommended that you answer yes to the following" \
+ "\nqueries unless you know exactly what you're doing."
+ elif grep -Fq 'RECOMMENDED=yes' $SCRIPT_DIRECTORY/init.d/$tmp ; then
+ message "Init script $tmp is \"RECOMMENDED\" for system startup," \
+ "\nit is recommended that you answer yes to the following queries."
+ fi
+ if service_query $tmp "Install init script $tmp?" "$INIT_INSTALLED" \
+ "$INIT_NOT_INSTALLED" "$DEF_INSTALL_INIT" \
+ new_init_inst new_init_ninst; then
+ if service_query $tmp "Enable init script $tmp?" "$INIT_ENABLED" \
+ "$INIT_DISABLED" "$DEF_ENABLE_INIT" \
+ new_init_enab new_init_disab; then
+ init_prepare_install "$tmp" "$INIT_PROVIDES" new_init_provides
+ fi
+ fi
+ done
+ for tmp in $XINETD; do
+ if service_query $tmp "Install xinetd script $tmp?" "$XINETD_INSTALLED" \
+ "$XINETD_NOT_INSTALLED" "$DEF_INSTALL_XINETD" \
+ new_xinetd_inst new_xinetd_ninst; then
+ service_query $tmp "Enable xinetd script $tmp?" "$XINETD_ENABLED" \
+ "$XINETD_DISABLED" "$DEF_ENABLE_XINETD" \
+ new_xinetd_enab new_xinetd_disab
+ fi
+ done
+
+ # removing all of these is necessary incase some init/xinetd scripts were
+ # originally in the spell but were later removed, besides its harmless
+ persistent_remove INIT_INSTALLED
+ persistent_remove INIT_NOT_INSTALLED
+ persistent_remove INIT_ENABLED
+ persistent_remove INIT_DISABLED
+ persistent_remove XINETD_INSTALLED
+ persistent_remove XINETD_NOT_INSTALLED
+ persistent_remove XINETD_ENABLED
+ persistent_remove XINETD_DISABLED
+
+ persistent_remove INIT_PROVIDES
+
+ if [[ $INIT ]] || [[ $BOTH ]] ; then
+ service_store INIT_INSTALLED "$new_init_inst"
+ service_store INIT_NOT_INSTALLED "$new_init_ninst"
+ service_store INIT_ENABLED "$new_init_enab"
+ service_store INIT_DISABLED "$new_init_disab"
+ fi
+
+ if [[ $XINETD ]] || [[ $BOTH ]] ; then
+ service_store XINETD_INSTALLED "$new_xinetd_inst"
+ service_store XINETD_NOT_INSTALLED "$new_xinetd_ninst"
+ service_store XINETD_ENABLED "$new_xinetd_enab"
+ service_store XINETD_DISABLED "$new_xinetd_disab"
+ fi
+
+ if [[ $new_init_provides ]] ; then
+ service_store INIT_PROVIDES "$new_init_provides"
+ fi
+
+ if [[ $XINETD_INSTALLED ]] ; then
+ optional_depends xinetd "" "" "for $XINETD_INSTALLED"
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Determines what the best default answer should be then gives the
+## user a menu of neither, init, xinetd, or both to choose from.
+## This is for the case of a service being provided by either init or xinetd
+##
+## @param service name
+## @param return variable to put answer in
+## @param list to look in for a default of yes to xinetd
+## @param list to look in for a default of no to xinetd
+## @param list to look in for a default of yes to init
+## @param list to look in for a default of no to init
+## @param default for xinetd if not found in either of the xinetd lists
+## @param default for init if not found in either of the init lists
+##
+## @return nothing of importance, returns through a pointer
+## @stdout a menu sans question, the caller needs to present the question
+#---------------------------------------------------------------------
+function dual_service_query() {
+ local service=$1
+ local returnvar=$2
+ local XY=$3
+ local XN=$4
+ local IY=$5
+ local IN=$6
+ local xinetd_default=$7
+ local init_default=$8
+
+ [[ $xinetd_default == off ]] && xinetd_default=n
+ [[ $xinetd_default == on ]] && xinetd_default=y
+ [[ $init_default == off ]] && init_default=n
+ [[ $init_default == on ]] && init_default=y
+
+ if list_find $service $XY ; then
+ xinetd_default=y
+ elif list_find $service $XN ; then
+ xinetd_default=n
+ fi
+
+ if list_find $service $IY ; then
+ init_default=y
+ elif list_find $service $IN ; then
+ init_default=n
+ fi
+
+ if [[ $init_default == y ]] && [[ $xinetd_default == y ]] ; then
+ default=3
+ elif [[ $xinetd_default == y ]] ; then
+ default=2
+ elif [[ $init_default == y ]] ; then
+ default=1
+ else
+ default=0
+ fi
+ select_list $returnvar $default neither init xinetd both
+}
+
+
+#---------------------------------------------------------------------
+## Determines what the best default answer should be then asks the
+## user given question and adds the service to one of two lists
+## @param service name
+## @param question to ask
+## @param list to look in for a default of yes
+## @param list to look in for a default of no
+## @param default if not found in either of the above two lists
+## @param store in this list if the answer is yes
+## @param store in this list if the answer is no
+## @return true if yes false if no
+## @stdout a query supplied by the caller
+#---------------------------------------------------------------------
+function service_query() {
+ local service=$1
+ local question=$2
+ local L1=$3
+ local L2=$4
+ local real_default=$5
+ local yes_list=$6
+ local no_list=$7
+
+ local default
+ if list_find $service $L1 ; then
+ default=y
+ elif list_find $service $L2 ; then
+ default=n
+ else
+ [[ $real_default == off ]] && real_default=n
+ [[ $real_default == on ]] && real_default=y
+ default=$real_default
+ fi
+
+ if query "$question" $default ; then
+ list_add $yes_list $service
+ true
+ else
+ list_add $no_list $service
+ false
+ fi
+}
+
+#---------------------------------------------------------------------
+## Internal wrapper around the task of adding a persistent variable
+## then storing something in it, or unsetting whatever used to be in it
+#---------------------------------------------------------------------
+function service_store() {
+ local VARNAME=$1
+ local list=$2
+ persistent_add $VARNAME
+ if [[ $list ]] ; then
+ eval $VARNAME=\"$list\"
+ else
+ unset $VARNAME
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Installs the scripts selected from query_services
+#---------------------------------------------------------------------
+function install_xinetd() {
+ if [[ $XINETD_INSTALLED ]] ; then
+ local install_dir=$INSTALL_ROOT/etc/xinetd.d
+ mkdir -p $install_dir
+ pushd "$SCRIPT_DIRECTORY/xinetd.d" >/dev/null
+
+ # install and enable
+ for FILE in $XINETD_INSTALLED; do
+ if ! test -e $FILE ; then
+ message "${PROBLEM_COLOR}Xinetd script $FILE doesnt exist," \
+ "but was selected, this may be a sorcery bug${DEFAULT_COLOR}"
+ continue
+ fi
+ if list_find $FILE $XINETD_ENABLED; then
+ message "Installing and enabling xinetd script $FILE to $install_dir"
+ install_xinetd_sub $FILE no $install_dir
+ elif list_find $FILE $XINETD_DISABLED; then
+ message "Installing and disabling xinetd script $FILE to $install_dir"
+ install_xinetd_sub $FILE yes $install_dir
+ else
+ message "${PROBLEM_COLOR}Xinetd script $FILE is in installed list" \
+ "but wasnt enabled or disabled, this may be a sorcery bug" \
+ "${DEFAULT_COLOR}"
+ fi
+ done
+ popd >/dev/null
+ fi
+}
+
+#---------------------------------------------------------------------
+## Does the dirty work involved in installing an xinetd script
+## this includes setting the disabled field appropriately and
+## not trampling on pre-existing files by default
+#---------------------------------------------------------------------
+function install_xinetd_sub() {
+ local file=$1
+ local bfile=$(basename $1)
+ local state=$2
+ local install_dir=$3
+ local tmp_file=$TMP_DIR/$bfile
+ local inst_file=$install_dir/$bfile
+ cp $file $tmp_file
+
+ set_xinetd_state $tmp_file $state
+
+ if test -e $inst_file &&
+ ! grep -v disable $inst_file|diff - $file &>/dev/null ; then
+ message "${QUERY_COLOR}$inst_file differs from the default $file," \
+ "what would you like to do?${DEFAULT_COLOR}"
+ select_list choice 0 "ignore" "overwrite" "overwrite/backup"
+ case $choice in
+ "overwrite/backup")
+ local backup=$inst_file.$(date +'%Y%m%d%H%M')
+ mv $inst_file $backup
+ # disable the backup
+ set_xinetd_state $backup yes
+ cp $tmp_file $inst_file ;;
+ overwrite) cp $tmp_file $inst_file ;;
+ esac
+ else
+ cp $tmp_file $inst_file
+ fi
+}
+
+#---------------------------------------------------------------------
+## Handles the logic of enabling or disabling of an xinetd script
+#---------------------------------------------------------------------
+function set_xinetd_state() {
+ local file=$1
+ local state=$2
+ if grep -q "\s*disable" $file ; then
+ sedit "s/disable\s*=.*/disable = $state/" $file
+ else
+ sedit "s/}/disable = $state\n}/" $file
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Copies any init.d files from the SCRIPT_DIRECTORY to the /etc/init.d.
+## Hopefully saving the old one if it exists and only setting the
+## executable bit if the user specified that the init.d script should be
+## started at bootup.
+#---------------------------------------------------------------------
+function install_initd() {
+ if [[ $INIT_INSTALLED ]] ; then
+ local install_dir=$INSTALL_ROOT/etc/init.d
+ mkdir -p $install_dir
+ pushd "$SCRIPT_DIRECTORY/init.d" >/dev/null
+
+ # install and enable
+ for FILE in $INIT_INSTALLED; do
+ message "${QUERY_COLOR}installing $FILE${DEFAULT_COLOR}"
+ if ! test -e $FILE ; then
+ message "${PROBLEM_COLOR}Init script $FILE doesnt exist," \
+ "but was selected, this may be a sorcery bug${DEFAULT_COLOR}"
+ continue
+ fi
+ if list_find $FILE $INIT_ENABLED; then
+ init_install enabled $FILE
+ elif list_find $FILE $INIT_DISABLED; then
+ init_install disabled $FILE
+ else
+ message "${PROBLEM_COLOR}Init script $FILE is in installed list" \
+ "but wasnt enabled or disabled, this may be a sorcery bug" \
+ "${DEFAULT_COLOR}"
+ fi
+ done
+ popd >/dev/null
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Gets the cast lock for us and ensures that we've waited for all
+## solo/non-solo casts to complete
+#---------------------------------------------------------------------
+function acquire_cast_lock() {
+ # locking - blocks normal spells if a solo cast is running,
+ # solo casts if any cast is running
+ message "${MESSAGE_COLOR}Waiting for any${DEFAULT_COLOR} ${FILE_COLOR}" \
+ "Solo${DEFAULT_COLOR} ${MESSAGE_COLOR}casts to complete..." \
+ "${DEFAULT_COLOR}"
+ lock_resources "solo" "cast"
+ if test -f $SOLO && grep -q "^$SPELL$" $SOLO ||
+ [ -f $SCRIPT_DIRECTORY/SOLO ]
+ then #SOLO spell
+ message -n "${MESSAGE_COLOR}Waiting for${DEFAULT_COLOR} ${SPELL_COLOR}" \
+ "all other${DEFAULT_COLOR} ${MESSAGE_COLOR}spells to" \
+ " complete...${DEFAULT_COLOR}"
+ excllock_resources "cast" "$SPELL"
+ else
+ message -n "${MESSAGE_COLOR}Waiting for any other casts of" \
+ "${DEFAULT_COLOR} ${SPELL_COLOR}$SPELL${DEFAULT_COLOR}" \
+ " ${MESSAGE_COLOR}to complete...${DEFAULT_COLOR}"
+ lock_resources "cast" "$SPELL"
+ unlock_resources "solo" "cast"
+ fi
+ message " done."
+}
+
+#---------------------------------------------------------------------
+## Takes the compile log and stuffs it into
+## our compile log directory.
+#---------------------------------------------------------------------
+function create_compile_log() {
+
+ message "${MESSAGE_COLOR}Creating compile log" \
+ "${FILE_COLOR}$COMPILE_LOGS/$SPELL-$VERSION$EXTENSION" \
+ "${DEFAULT_COLOR}"
+ if [[ "$STORE_CONF_LOG" == on ]] && test -e $SOURCE_DIRECTORY/config.log; then
+ echo "---config.log---" >> $C_LOG
+ cat $SOURCE_DIRECTORY/config.log >> $C_LOG
+ fi
+ if [ -z "$EXTENSION" ]; then
+ cp $C_LOG $COMPILE_LOGS/$SPELL-$VERSION
+ else
+ $COMPRESSBIN -f < $C_LOG > $COMPILE_LOGS/$SPELL-$VERSION$EXTENSION
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Prompts the user to view the compile log
+## and deletes the temporary files too
+#---------------------------------------------------------------------
+function view_compile_log() {
+
+ debug "libcast" "In view_compile_log, C_LOG=$C_LOG"
+ report $C_LOG "Compile log"
+}
+
+#---------------------------------------------------------------------
+## Report that something got installed, and possibly display the report
+## Pawns the work off to <@function var.lib.sorcery.modules.libsorcery,report>
+## @Globals INST_LOG
+#---------------------------------------------------------------------
+function report_install() {
+
+ debug "libcast" "In report_install, INST_LOG=$INST_LOG"
+ report $INST_LOG "Install log"
+
+}
+
+#---------------------------------------------------------------------
+## LC means "line count" why not just type line_count?
+## @TODO This function is dumb. It should be fixed.
+## @param download log file name
+#---------------------------------------------------------------------
+function show_download_progress() {
+ [[ $SCREEN_NAME ]] && return
+ local download_log=$1
+ if [ -e "$download_log" ] &&
+ [ -z "$SILENT" ]; then
+
+ LC_OLD=${LC_OLD:-0}
+
+ LC=`cat $download_log 2>/dev/null | wc -l | tr -d ' '`
+
+ if [ "$LC" != "$LC_OLD" ]; then
+ let LC_OLD++
+ sed -n ${LC_OLD},${LC}p $download_log
+ LC_OLD=$LC
+ fi
+
+ fi
+
+}
+
+#---------------------------------------------------------------------
+## Shows download progress. Waits for the download to start, and shows
+## the progress until the download is done.
+## Pawn most of the display out to <@function show_download_progress>
+## @TODO fix the old style ad-hoc IPC
+## @param Spell to show the download progress for
+#---------------------------------------------------------------------
+function show_downloading() {
+
+ local SPELL=$1
+ local download_log=$(get_spell_dl_log $SPELL)
+ debug "cast" "Started show_downloading() on $SPELL from $download_log"
+
+ # poke around waiting for downloading to start
+ while ! ( [[ $download_log ]] &&
+ [ -e "$download_log" ] )
+ do
+ sleep 1
+ done
+
+ # isn't this out of band ipc great?
+ until [ -f "${download_log}.done" ] ; do
+ show_download_progress $download_log
+ sleep 1
+ done
+
+ show_download_progress $download_log
+ debug "libcast" "Out of show_downloading"
+}
+
+#---------------------------------------------------------------------
+## Gets the filename of the download log file
+## @param Spell
+## @Stdout file name
+#---------------------------------------------------------------------
+function get_spell_dl_log() {
+ echo "$TMP_DIR/download.$1"
+}
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libcodex b/var/lib/sorcery/modules/libcodex
new file mode 100644
index 0000000..9d2dc5c
--- /dev/null
+++ b/var/lib/sorcery/modules/libcodex
@@ -0,0 +1,869 @@
+#!/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() {
+ unset GRIMOIRE_DIR
+
+ local NEW_GRIMOIRE
+ local GRIMOIRE_COUNT=0
+ for NEW_GRIMOIRE in $@; do
+ NEW_GRIMOIRE=$(codex_canonicalize_grimoire_name $NEW_GRIMOIRE) ||
+ { message "WARNING $NEW_GRIMOIRE does not exist!! skipping..."
+ continue
+ }
+ GRIMOIRE_DIR[$GRIMOIRE_COUNT]=$NEW_GRIMOIRE
+ let 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
+
+ # 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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libcrossinstall b/var/lib/sorcery/modules/libcrossinstall
new file mode 100644
index 0000000..cff02cb
--- /dev/null
+++ b/var/lib/sorcery/modules/libcrossinstall
@@ -0,0 +1,184 @@
+#!/bin/bash
+#--------------------------------------------------------------------
+## Library for doing install_root stuff. It is home to menu's for
+## install_root and related variables. Future install_root specific
+## and cross install related code might live here.
+## @Copyright Copyright (C) 2004 The Source Mage Team
+## @Note The description may be wrong.
+#--------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+##
+## This menu function is called only if the CROSS_INSTALL is turned on,
+## it provides the user with a choice of the default INSTALL_ROOT,
+## TRACK_ROOT, STATE_ROOT, and someday CODEX_ROOT
+##
+#---------------------------------------------------------------------
+function install_root_menu() {
+
+ debug "libcrossinstall" "cross_install_menu is being built now..."
+ local C_HELP="Fill in your own preferred track root location."
+ local I_HELP="Fill in your own preferred install root location."
+ local S_HELP="Fill in your own preferred state root location."
+ local T_HELP="Fill in your own preferred track root location."
+
+ while
+
+ COMMAND=`eval $DIALOG ' --title "Install Root Menu" \
+ --item-help \
+ --ok-label "Select" \
+ --cancel-label "Exit" \
+ --menu \
+ "" \
+ 0 0 0 \
+ "C" "Set install cache location" "$C_HELP" \
+ "I" "Set install root location" "$I_HELP" \
+ "S" "Set state root location" "$S_HELP" \
+ "T" "Set track root location" "$T_HELP"'`
+
+ do
+
+ case $COMMAND in
+ C) set_install_cache_menu ;;
+ I) set_install_root_menu ;;
+ S) set_state_root_menu ;;
+ T) set_track_root_menu ;;
+ esac
+
+ done
+
+}
+
+#---------------------------------------------------------------------
+##
+## This menu function allows the user to fill in an INSTALL_CACHE location.
+##
+#---------------------------------------------------------------------
+function set_install_cache_menu() {
+
+ debug "libcrossinstall" "set_install_cache_menu - is starting..."
+ local PROMPT="Please enter the install cache location where cache tarballs will be stored"
+ if NEW_INSTALL_CACHE=`eval $DIALOG ' --ok-label "Commit" \
+ --inputbox \
+ "$PROMPT" \
+ 0 0 "$INSTALL_CACHE"'`
+ then
+ INSTALL_CACHE=${NEW_INSTALL_CACHE%/}
+ remove_config $LOCAL_CONFIG "INSTALL_CACHE"
+ modify_config $LOCAL_ROOTS_CONFIG "INSTALL_CACHE" "$INSTALL_CACHE"
+ fi
+}
+
+#---------------------------------------------------------------------
+##
+## This menu function allows the user to fill in an INSTALL_ROOT location.
+##
+#---------------------------------------------------------------------
+function set_install_root_menu() {
+
+ debug "libcrossinstall" "set_install_root_menu - is starting..."
+ local PROMPT="Please enter the install root location where you want file installed"
+ if NEW_INSTALL_ROOT=`eval $DIALOG ' --ok-label "Commit" \
+ --inputbox \
+ "$PROMPT" \
+ 0 0 "$INSTALL_ROOT"'`
+ then
+ INSTALL_ROOT=${NEW_INSTALL_ROOT%/}
+ remove_config $LOCAL_CONFIG "INSTALL_ROOT"
+ modify_config $LOCAL_ROOTS_CONFIG "INSTALL_ROOT" "$INSTALL_ROOT"
+ fi
+}
+
+#---------------------------------------------------------------------
+##
+## This menu function allows the user to fill in an STATE_ROOT location.
+##
+#---------------------------------------------------------------------
+function set_state_root_menu() {
+
+ debug "libcrossinstall" "set_state_root_menu - is starting..."
+ local PROMPT="Please enter location where you want state files to be"
+ if NEW_STATE_ROOT=`eval $DIALOG ' --ok-label "Commit" \
+ --inputbox \
+ "$PROMPT" \
+ 0 0 "$STATE_ROOT"'`
+ then
+ STATE_ROOT=${NEW_STATE_ROOT%/}
+ remove_config $LOCAL_CONFIG "STATE_ROOT"
+ modify_config $LOCAL_ROOTS_CONFIG "STATE_ROOT" "$STATE_ROOT"
+ fi
+}
+
+#---------------------------------------------------------------------
+##
+## This menu function allows the user to fill in an TRACK_ROOT location.
+##
+#---------------------------------------------------------------------
+function set_track_root_menu() {
+
+ debug "libcrossinstall" "set_track_root_menu - is starting..."
+ local PROMPT="Please enter the location where you want files tracked relative to"
+ if NEW_TRACK_ROOT=`eval $DIALOG ' --ok-label "Commit" \
+ --inputbox \
+ "$PROMPT" \
+ 0 0 "$TRACK_ROOT"'`
+ then
+ TRACK_ROOT=${NEW_TRACK_ROOT%/}
+ modify_config $LOCAL_ROOTS_CONFIG "TRACK_ROOT" "$TRACK_ROOT"
+ fi
+}
+
+#------------------------------
+## Will install sorcery in install_root directory, by running
+## the install script bundled with the tarball
+## This expects the user to have the sorcery tarball already downloaded
+## and in $SOURCE_CACHE.
+#------------------------------
+function replicate_sorcery() {
+ SOURCE=$SOURCE_CACHE/sorcery-$SORCERY_BRANCH.tar.bz2
+ SOURCE_DIRECTORY=$BUILD_DIRECTORY/sorcery
+
+ if ! test -f $SOURCE ; then
+ message "Cant find source in $SOURCE, please update or download sorcery"
+ return 1
+ fi
+
+ mk_source_dir $SOURCE_DIRECTORY &&
+ cd $INSTALL_ROOT/usr/src &&
+ bzip2 -cdf $SOURCE |
+ tar --owner=root --group=root -xf /dev/stdin &&
+ cd $SOURCE_DIRECTORY &&
+ echo `pwd` &&
+ ./install $INSTALL_ROOT
+ rc=$?
+ cd ..
+
+ if [ $rc -eq 0 ] ; then
+ rm_source_dir
+ else
+ if [[ $CLEAN_SOURCE == on ]]; then
+ rm_source_dir
+ fi
+ fi
+ return $rc
+}
+
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libdepends b/var/lib/sorcery/modules/libdepends
new file mode 100644
index 0000000..5ac59d3
--- /dev/null
+++ b/var/lib/sorcery/modules/libdepends
@@ -0,0 +1,1209 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+## @Synopsis Functions for dealing with dependencies as a non-cyclic directed graph. Since that's such a mouthful, it will simply be referred to as a tree, even though it's not.
+## @Copyright (C) 2002 The Source Mage Team <http://www.sourcemage.org>
+## A spell is represented as a node containing some pieces of data
+## seperated by colons.
+## spell:dependent spell:on/off:type:casting_flag:is_a_target_flag
+## @Contributers Chris Brien (christopher_brien@hotmail.com)
+## @Contributers Paul Mahon (pmahon@sourcemage.org)
+#---------------------------------------------------------------------
+
+#
+# conceptual function call tree...perhaps this will enlighten sorcery
+# students..
+#
+# compute_uninstalled_depends (the entry point)
+# -> for each spell (this list grows during processing)
+# -> run PREPARE
+# -> run CONFIGURE
+# -> run DEPENDS
+# -> depends <spell> (external)
+# -> work_depends_spell
+# -> private_common_depends -> libstate.add_depends
+# -> add to NEW_DEPENDS
+# -> libstate.add_depends
+# -> optional_depends <spell> (external)
+# -> work_optional_depends_spell
+# -> query
+# -> private_common_depends -> libstate.add_depends
+# -> add to NEW_DEPENDS
+# -> libstate.add_depends
+# -> depends <provider> (external)
+# -> work_depends_provider
+# -> select provider
+# -> private_common_depends -> libstate.add_depends
+# -> add to NEW_DEPENDS
+# -> libstate.add_depends
+# -> optional_depends <provider> (external)
+# -> work_optional_depends_provider
+# -> select provider
+# -> private_common_depends
+# -> add to NEW_DEPENDS
+# -> libstate.add_depends
+# -> private_add_depends
+# -> update hash tables and lists from NEW_DEPENDS
+#
+# in other words, for every spell run its external files
+# and then deal with their callbacks (depends and optional_depends)
+# each of those calls eventually arrives at a dependency rule
+# which is stored somewhere through libstate calls , and in an internal
+# variable (NEW_DEPENDS). After we finish all the files we bundle up
+# our new information and move to the next spell.
+#
+
+# Surprise env vars:
+# SPELL: this is actually locally defined somewhere up the call stack
+# but from most function's point of view it should be there...
+# COMPILE: set in cast. It means that the main spells should be recompiled
+# RECONFIGURE: set in cast. I means that the info in the state depends should
+# be disregarded and replaced.
+# PRETEND_NOT_INSTALLED: set here. It is a list of spells that are to be
+# recompiled, so they should not be treated as installed.
+# CAST_HASH: The name of the hast table to put spells and dependencies
+# that are to be cast (only used in this lib)
+# BACK_HASH: reverse of CAST_HASH will be used to handle failures
+# more gracefully someday...
+# CANNOT_CAST: The name of the hash table to put spells that cannot be cast
+# and the reason why. Usualy because they are exiled or don't exist
+# (only used in this lib)
+
+
+######################BEGIN CALLS TO OUTSIDE WORLD########################
+
+#---------------------------------------------------------------------
+## Run the spell's PREPARE script if it exists
+## @param Spell to prepare
+## @Globals SCRIPT_DIRECTORY
+#---------------------------------------------------------------------
+function run_prepare()
+{
+ local SPELL=$1
+
+ debug "cast" "run_prepare() - SPELL = $SPELL SCRIPT_DIRECTORY = $SCRIPT_DIRECTORY"
+
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}preparing environment...${DEFAULT_COLOR}"
+
+ # these are here so you can source section/grimoire level scripts in
+ # PREPARE, which by definition runs before the spell is loaded
+ # and they are defined as usual (see bug 8329)
+ local SPELL_DIRECTORY=$SCRIPT_DIRECTORY
+ local SECTION_DIRECTORY=${SPELL_DIRECTORY%/*}
+ local GRIMOIRE=${SECTION_DIRECTORY%/*}
+
+ local PROTECT_SORCERY=yes
+ if [ -x $SCRIPT_DIRECTORY/PREPARE ]; then
+ # we need &&'s to preserve the proper return code (persistant_save
+ # will probably succeed even if PREPARE fails)
+ persistent_load &&
+ . $SCRIPT_DIRECTORY/PREPARE &&
+ persistent_save
+ fi
+}
+
+#---------------------------------------------------------------------
+## This will be home to all "other" questions we are supposed to ask about
+## during this phase of things, right now its a placeholder
+## @param Spell
+#---------------------------------------------------------------------
+function run_other() {
+ local SPELL=$1
+ # ask the questions about xinetd/initd script installation
+ persistent_load
+ query_services
+ persistent_save
+# todo:
+#ask about conflicts
+#ask about other stuff
+
+}
+
+#---------------------------------------------------------------------
+## Run the spell's CONFIGURE script if it exists
+## @param Spell to configure
+#---------------------------------------------------------------------
+function run_configure()
+{
+
+ local SPELL=$1
+ debug "cast" "run_configure() - SCRIPT_DIRECTORY = $SCRIPT_DIRECTORY"
+
+
+ local PROTECT_SORCERY=yes
+ if [ -x $SCRIPT_DIRECTORY/CONFIGURE ]; then
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}running configuration...${DEFAULT_COLOR}"
+ # we need &&'s to preserve the proper return code (persistant_save
+ # will probably succeed even if PREPARE fails)
+ persistent_load &&
+ . $SCRIPT_DIRECTORY/CONFIGURE &&
+ persistent_save
+ fi
+}
+
+#---------------------------------------------------------------------
+## Run a spell's DEPENDS if it exists
+## @param Spell
+#---------------------------------------------------------------------
+function run_depends()
+{
+ local SPELL=$1
+ debug "cast" "run_depends() - SCRIPT_DIRECTORY = $SCRIPT_DIRECTORY"
+
+ local PROTECT_SORCERY=yes
+ if [ -x $SCRIPT_DIRECTORY/DEPENDS ]; then
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}checking dependencies...${DEFAULT_COLOR}"
+
+ # we need &&'s to preserve the proper return code (persistant_save
+ # will probably succeed even if PREPARE fails)
+ persistent_load &&
+ . $SCRIPT_DIRECTORY/DEPENDS &&
+ persistent_save
+ fi
+}
+
+#---------------------------------------------------------------------
+## Run a spell's UP_TRIGGERS if it exists
+## @param Spell
+#---------------------------------------------------------------------
+function run_up_triggers() {
+ local SPELL=$1
+ debug "cast" "run_up_triggers() - SCRIPT_DIRECTORY = $SCRIPT_DIRECTORY"
+
+ local PROTECT_SORCERY=yes
+ if [ -x $SCRIPT_DIRECTORY/UP_TRIGGERS ]; then
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}checking for reverse triggers...${DEFAULT_COLOR}"
+ # we need &&'s to preserve the proper return code (persistant_save
+ # will probably succeed even if PREPARE fails)
+ persistent_load &&
+ . $SCRIPT_DIRECTORY/UP_TRIGGERS &&
+ persistent_save
+ fi
+
+}
+######################END CALLS TO OUTSIDE WORLD########################
+
+
+
+#---------------------------------------------------------------------
+## Create a map of spells to their dependent spells.
+## Then for all installed or held spells, output a libhash command to
+## join the spell name and dependency info.
+## Then evaluate the output, thus filling a libhash with dependency info.
+## @param Name of hash table to put dependencies
+#---------------------------------------------------------------------
+function compute_installed_depends() {
+ #$1==hash table to fill
+ local hash=$1
+ touch $DEPENDS_STATUS $SPELL_STATUS &>/dev/null
+
+ # From here forward $1 and $2 are only used to refer to awk variables
+
+ # sub(/(.*)/, "", $2) removes a provider name
+ eval $(awk -F : 'BEGIN {
+ while (getline < ARGV[1] ) {
+ if( $3=="on") {
+ sub(/\(.*\)/, "", $2);
+ depmap[$1]=depmap[$1]" "$2" "
+ }
+ }
+ while (getline < ARGV[2] ) {
+ if( $3=="installed" || $3=="held") {
+ printf("hash_put $hash %s \"%s\";\n",$1,depmap[$1]);
+ }
+ }
+ }' $DEPENDS_STATUS $SPELL_STATUS )
+}
+
+#---------------------------------------------------------------------
+## Create a map of spells to their dependent spells.
+## Then for all installed or held spells, output a libhash command to
+## join the spell name and dependency info.
+## Then evaluate the output, thus filling a libhash with dependency info.
+## @param Name of hash table to fill with dependencies
+#---------------------------------------------------------------------
+function compute_reverse_installed_depends() {
+ #$1==hash table to fill
+ local hash=$1
+ touch $DEPENDS_STATUS $SPELL_STATUS &>/dev/null
+
+ # From here forward $1 and $2 are only used to refer to awk variables
+
+ # sub(/(.*)/, "", $2) removes a provider name
+ eval $(awk -F : 'BEGIN {
+ while (getline < ARGV[1] ) {
+ if( $3=="on") {
+ sub(/\(.*\)/, "", $2);
+ depmap[$2]=depmap[$2]" "$1" "
+ }
+ }
+ while (getline < ARGV[2] ) {
+ if( $3=="installed" || $3=="held") {
+ printf("hash_put $hash %s \"%s\";\n",$1,depmap[$1]);
+ }
+ }
+ }' $DEPENDS_STATUS $SPELL_STATUS )
+}
+
+#---------------------------------------------------------------------
+## calling this will accomplish several things:
+## <ol>
+## <li> most importantly it finds the closure of all spells that need to
+## be cast
+## <li> it builds a hash table mapping spells to their depends, possibly by
+## asking the user for input
+## <li> it updates DEPENDS_STATUS, arguably it shouldn't be doing this.
+## </ol>
+##
+## What happens: take all the spells we've been asked to resolve
+## for each one of them run its details file
+## the details file will call back to depends/optional_depends
+## at this point we determine/find/query for depends info
+## update the hash table, update DEPENDS_STATUS, and append to the
+## list of spells to resolve
+##
+## @param Hashtable name for dependencies
+## @param Hashtable name for spells with problem in resolution (or something)
+## @param Hashtable name for spells which cannot cast
+#---------------------------------------------------------------------
+function compute_uninstalled_depends()
+{
+
+ # $1=table to place spells in
+ # $2=table to put problem spells in, $* = root spells
+
+ debug "libdepends" "compute_depends of $*"
+ local CAST_HASH="$1"
+ local BACK_CAST_HASH="$2"
+ local CANNOT_CAST_HASH="$3"
+ shift 3
+ local spell spells
+ spells=( $@ )
+
+ PRETEND_NOT_INSTALLED=" $@ "
+
+ local _idx
+
+ # All specified spells are assumed to be not installed, or else -c and -r
+ # would have to be specified all the time.
+
+ for (( _idx=0 ; _idx<${#spells[*]} ; _idx++ )) ; do
+ if [[ ! `hash_get depends_looked_at ${spells[$_idx]}` ]]; then
+ if ! private_run_depends ${spells[$_idx]} ; then
+ # i dont know if this will work, but it will have to suffice
+ private_remove_dependees ${spells[$_idx]}
+ fi
+ else
+ debug "libdepends" "already looked at ${spells[$_idx]}, skipping"
+ fi
+ done
+ # we no longer need this, no sense in keeping it around
+ hash_unset depends_looked_at
+
+ # we need this so processes on the other side of make know whats
+ # going on
+ hash_export uncommitted_hash
+}
+
+#---------------------------------------------------------------------
+## A private function for running a spell's DEPENDS script.
+## No functions except libdepends functions should use this.
+## @param Spell
+#---------------------------------------------------------------------
+function private_run_depends()
+{
+ debug "libdepends" "$FUNCNAME - $*"
+ local SPELL=$1
+ local NEW_DEPENDS=""
+ local triggerees=""
+ local spell_depends
+ hash_put "depends_looked_at" "$SPELL" "start"
+
+ # move this up to compute_uninstalled_depends?
+ # this is a list of all spells basesystem depends on it is used to
+ # avoid loops with the "everything depends on basesystem" feature
+ local base_deps
+ base_deps=$(search_depends_status $DEPENDS_STATUS basesystem|cut -f2 -d:)
+
+
+ # We only need to run the stuff if we are going to be casting.
+ # It only needs to be added to the casting hash table if we are
+ # really casting it
+ if private_should_cast $SPELL ; then
+
+ # this cant go in private_should_cast because then the dependee wont
+ # have a chance at being fixed, ideally we should check in
+ # depends/optional_depends and fail there
+ if spell_exiled $1; then
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}is exiled and will not be cast.${DEFAULT_COLOR}"
+ return 1
+ fi
+ get_uncommitted_depends_file $SPELL spell_depends
+ if [ -n "$RECONFIGURE" ]; then
+ rm -f $DEPENDS_CONFIG/$SPELL
+ rm -f $DEPENDS_CONFIG/$SPELL.p
+ fi
+ prepare_spell_config
+ SCRIPT_DIRECTORY=`codex_find_spell_by_name $SPELL`
+ run_prepare $SPELL &&
+ run_details &&
+ run_configure $SPELL &&
+ run_other $SPELL &&
+ run_depends $SPELL &&
+ run_up_triggers $SPELL &&
+
+ # possibly recast things that depend on us if option is set (-B)
+ private_upward_depends $SPELL &&
+ private_add_triggerees &&
+ private_add_depends ||
+ { debug "libdepends" "$FUNCNAME: false inside if." ; return 1; }
+ # no point in keeping the file around if its empty...
+ test -s $spell_depends || rm $spell_depends
+ else
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}No work to do.${DEFAULT_COLOR}"
+ hash_put "depends_looked_at" "$SPELL" "ignore"
+ fi
+
+ # if there weren't any depends no sense in keeping the file around
+ return 0
+}
+
+#---------------------------------------------------------------------
+## Decides if a spell should be case. Check if the spell is installed
+## if it matters, etc...
+## Perhaps this function is overly splayed out in elif's but its
+## easier to add to later than what we had before...
+## @param Spell
+#---------------------------------------------------------------------
+function private_should_cast()
+{
+ local decision
+ # order is important here...
+ if ! codex_does_spell_exist $1; then
+ return 1
+ elif echo "$PRETEND_NOT_INSTALLED" | grep -q " $1 " ; then
+ # always look at stuff on the command line unless its exiled
+ return 0
+ # from here on the spell was not on the command line...
+ elif spell_held $1; then
+ # don't recast held even with -R
+ return 1
+ elif [[ "$RECAST_DOWN" ]] ; then
+ # user gave -R so recast...
+ return 0
+ elif echo "${UP_DEPENDS[*]}" | grep -q "\<$1\>" ; then
+ # if someone has determined this is an upward depend (-B)
+ return 0
+ elif echo "${TRIGGEREES[*]}" | grep -q "\<$1\>" ; then
+ # if its being triggered we need to look at it, despite its
+ # installed status
+ return 0
+ elif spell_installed $1 ; then
+ # spell is installed and no -R or -B, so dont cast
+ return 1
+ fi
+
+ # we must need to install this as we know nothing else about it
+ return 0
+}
+
+#---------------------------------------------------------------------
+## @param Spell name
+## Find all the spells that depend on the spell given as $1
+#---------------------------------------------------------------------
+function private_upward_depends() {
+ if [[ "$RECAST_UP" ]] ; then
+ local tmp
+ # Note, use the reverse depends tree for this when we get a chance
+ # and/or move the weird pattern into library functions...
+ # (afrayedknot 2005-10-02)
+ tmp=$(grep "^.*:$1\(([^:]*)\)\?" $DEPENDS_STATUS|cut -f1 -d:|tr "\n" " ")
+ local j each
+ let j=${#UP_DEPENDS[*]}
+ for each in $tmp; do
+ UP_DEPENDS[$j]=$each
+ let j++
+ done
+ spells=( ${spells[*]} ${tmp} )
+ fi
+}
+
+###################BEGIN CALLBACKS FROM OUTSIDE#######################
+
+#---------------------------------------------------------------------
+## @param spell or provider name
+## @param addition to OPTS
+## @param description
+## @param grimoires to look in
+## Delegates provider and spell cases to different worker functions.
+## and gets grimoires if necessary for cross grimoire depends
+#---------------------------------------------------------------------
+function real_depends()
+{
+ # see if theres another grimoire
+ if [[ "$4" ]] ; then
+ local grimoire here nothere current
+
+ for grimoire in $4; do
+ if [[ "$grimoire" == "current" ]] ; then
+ current=yes
+ elif codex_find_grimoire "$grimoire" > /dev/null; then
+ list_add here $grimoire
+ else
+ list_add nothere $grimoire
+ fi
+ done
+ if [[ "$here" ]] || [[ "$current" ]] ; then
+ if [[ "$nothere" ]] ; then
+ message "${SPELL_COLOR}$1${DEFAULT_COLOR}${CHECK_COLOR}" \
+ "exists in the following grimoires${DEFAULT_COLOR}" \
+ "${SPELL_COLOR}${4}${DEFAULT_COLOR}${CHECK_COLOR}"
+ message "You dont have ${SPELL_COLOR}$nothere${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}but you have ${SPELL_COLOR}$here${DEFAULT_COLOR}"
+ for grimoire in $nothere ; do
+ if query "Get $grimoire grimoire?" n; then
+ scribe add "$grimoire"
+ unset GRIMOIRE_DIR[*]
+ source $GRIMOIRE_LIST
+ if codex_find_grimoire "$grimoire" > /dev/null; then
+ list_add here $grimoire
+ else
+ message "${PROBLEM_COLOR}Failed to get grimoire${DEFAULT_COLOR}"
+ fi
+ fi
+ done
+ # else
+ # have all the grimoires, nothing to do, this is probably the case most
+ # of the time
+ fi
+ else
+ if [[ "$nothere" ]] ; then
+ message "${CHECK_COLOR}You dont have any of the grimoires" \
+ "${SPELL_COLOR}$4${DEFAULT_COLOR}${CHECK_COLOR}."
+ message "You must add at least one grimoire to satisfy the" \
+ "dependency.${DEFAULT_COLOR}"
+ for grimoire in $nothere ; do
+ if query "Get $grimoire grimoire?" n; then
+ scribe add "$grimoire"
+ unset GRIMOIRE_DIR[*]
+ source $GRIMOIRE_LIST
+ if codex_find_grimoire "$grimoire" > /dev/null; then
+ list_add here $grimoire
+ else
+ message "${PROBLEM_COLOR}Failed to get grimoire${DEFAULT_COLOR}"
+ fi
+ fi
+ done
+ else
+ # this is a bug, most likely with list_add in order for this
+ # to happen, there has to be some grimoires to look in, and the
+ # grimoires are neither installed nor uninstalled
+ message "This is a bug, probably with list_add, please contact the" \
+ "sorcery team, thanks."
+ return 1
+ fi
+ fi
+ if [[ ! $here ]] && [[ ! $current ]] ; then
+ message "${PROBLEM_COLOR}no grimoire for $1 was retrieved${DEFAULT_COLOR}"
+ return 1
+ fi
+ fi
+
+ if ! codex_does_spell_exist $1 &> /dev/null; then
+ work_depends_provider "$@"
+ else
+ work_depends_spell "$@"
+ fi
+
+}
+
+#---------------------------------------------------------------------
+## @param spell or provider name
+## @param addition to OPTS if enabled
+## @param addition to OPTS if disabled
+## @param description
+## @param grimoires to look in
+## Delegates provider and spell cases to different worker functions.
+#---------------------------------------------------------------------
+function real_optional_depends()
+{
+
+ # see if theres another grimoire
+ if [[ "$5" ]] ; then
+ local grimoire here nothere current
+ for grimoire in $5; do
+ if [[ "$grimoire" == "current" ]] ; then
+ current=yes
+ elif codex_find_grimoire "$grimoire" > /dev/null; then
+ list_add here $grimoire
+ else
+ list_add nothere $grimoire
+ fi
+ done
+ if [[ "$here" ]] || [[ "$current" ]] ; then
+ if [[ "$nothere" ]] ; then
+ message "${SPELL_COLOR}$1${DEFAULT_COLOR}${CHECK_COLOR}" \
+ "exists in the following grimoires${DEFAULT_COLOR}" \
+ "${SPELL_COLOR}${5}${DEFAULT_COLOR}${CHECK_COLOR}"
+ message "You dont have ${SPELL_COLOR}$nothere${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}but you have ${SPELL_COLOR}$here${DEFAULT_COLOR}"
+ for grimoire in $nothere ; do
+ if query "Get $grimoire grimoire?" n; then
+ scribe add "$grimoire"
+ unset GRIMOIRE_DIR[*]
+ source $GRIMOIRE_LIST
+ if codex_find_grimoire "$grimoire" > /dev/null; then
+ list_add here $grimoire
+ else
+ message "${PROBLEM_COLOR}Failed to get grimoire${DEFAULT_COLOR}"
+ fi
+ fi
+ done
+ # else
+ # have all the grimoires, nothing to do, this is probably the case most
+ # of the time
+ fi
+ else
+ if [[ "$nothere" ]] ; then
+ message "${CHECK_COLOR}You dont have any of the grimoires" \
+ "${SPELL_COLOR}${5}${DEFAULT_COLOR}${CHECK_COLOR}."
+ for grimoire in $nothere ; do
+ if query "Get $grimoire grimoire?" n; then
+ scribe add "$grimoire"
+ unset GRIMOIRE_DIR[*]
+ source $GRIMOIRE_LIST
+ if codex_find_grimoire "$grimoire" > /dev/null; then
+ list_add here $grimoire
+ else
+ message "${PROBLEM_COLOR}Failed to get grimoire${DEFAULT_COLOR}"
+ fi
+ fi
+ done
+ else
+ # this is a bug, most likely with list_add in order for this
+ # to happen, there has to be some grimoires to look in, and the
+ # grimoires are neither installed nor uninstalled
+ message "This is a bug, probably with list_add, please contact the" \
+ "sorcery team, thanks."
+ return 1
+ fi
+ fi
+ if [[ ! $here ]] && [[ ! $current ]] ; then
+ message "${PROBLEM_COLOR}no grimoire for $1 was retrieved${DEFAULT_COLOR}"
+ message "Assuming dependency is off because it could not be met"
+ private_common_depends "$1" "off" "optional" "$2" "$3"
+ fi
+ fi
+
+ if ! codex_does_spell_exist $1 &> /dev/null; then
+ work_optional_depends_provider "$@"
+ else
+ work_optional_depends_spell "$@"
+ fi
+
+}
+
+#---------------------------------------------------------------------
+## Asks the user what provider for a depends is desired if a choice
+## has not ben made before.
+## @param Service
+## @param Enabled options
+## @param Description
+#---------------------------------------------------------------------
+function work_depends_provider()
+{
+
+ debug "libdepends" "$FUNCNAME - $@"
+ local default tmp installed=no
+ local status=()
+
+ if [[ $3 ]] ; then
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}requires some${DEFAULT_COLOR}" \
+ "${SPELL_COLOR}${1}${DEFAULT_COLOR} ($3)."
+ else
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}requires some${DEFAULT_COLOR}" \
+ "${SPELL_COLOR}${1}${DEFAULT_COLOR}."
+ fi
+
+ local CANDIDATES=$( find_providers $1)
+ if [[ ! $CANDIDATES ]] ; then
+ message "${PROBLEM_COLOR}No providers of${DEFAULT_COLOR}" \
+ "${SPELL_COLOR}$1${DEFAULT_COLOR}" \
+ "${PROBLEM_COLOR} can be found!${DEFAULT_COLOR}"
+ return 1
+ fi
+
+ # if not reconfiguring check if theres already an answer in DEPENDS_STATUS
+ if [[ ! $RECONFIGURE ]]; then
+ # notice the clever ignorance of optional/required depends for the
+ # provider case, if the user chose none, we would fall out during spell_ok
+ # and we transparently switch between optional and required without
+ # anyone noticing
+ explode "$(search_depends_status $DEPENDS_STATUS "$SPELL" ".*($1)")" ":" "status"
+ tmp=${status[1]%(*} # Name of spell which provides $1
+ if spell_ok $tmp &&
+ query "Continue to use ${SPELL_COLOR}$tmp${DEFAULT_COLOR}?" y; then
+ private_common_depends "$tmp($1)" "on" "required" "$2" "$3"
+ return 0
+ fi
+ fi
+
+ # check if theres an abandoned answer, but only if its still a provider
+ if [[ ! $default ]] && [ -e $ABANDONED_DEPENDS/$SPELL ] ; then
+ tmp=$(search_depends_status $ABANDONED_DEPENDS/$SPELL "$SPELL" ".*($1)"|awk -F: '{print $2;exit}')
+ [[ $tmp ]] && echo $CANDIDATES|grep -q "\<$tmp\>" && default=$tmp
+ fi
+
+ # check if theres a default provider
+ if [[ ! $default ]]; then
+ explode "$(search_default_provider $DEFAULT_PROVIDERS "" "$1")" ":" "status"
+ tmp=${status[0]}
+ [[ $tmp ]] && echo $CANDIDATES|grep -q "\<$tmp\>" && default=$tmp
+ fi
+
+ # check if we've already answered this question
+ if [[ ! $default ]]; then
+ for tmp in $CANDIDATES; do
+ echo ${spells[@]} | grep -q "\<$tmp\>" && default=$tmp && break
+ done
+ fi
+
+ # check if theres a provider already installed
+ if [[ ! $default ]]; then
+ for tmp in $CANDIDATES; do
+ spell_ok $tmp && default=$tmp && break
+ done
+ fi
+
+ select_provider "provider" "$default" 0 $CANDIDATES
+
+ private_common_depends "$provider($1)" "on" "required" "$2" ""
+}
+
+#---------------------------------------------------------------------
+## One of the worker functions. Checks for exiled spell and passes
+## on to the common dependency function, <@function private_common_depends>
+## @param Spell
+## @param enabled options
+## @param disabled options (useless, but included for some reason)
+## @param grimoire the spell is in, if its different than the current one
+#---------------------------------------------------------------------
+function work_depends_spell()
+{
+ debug "libdepends" "$FUNCNAME - $@"
+
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}depends on" \
+ "${SPELL_COLOR}${1}${DEFAULT_COLOR}"
+
+ if spell_exiled $1 ; then
+ hash_put $CANNOT_CAST_HASH "$1" "Exiled"
+ message "${SPELL_COLOR}${1}${DEFAULT_COLOR} has been exiled!"
+ return 1
+ fi
+ private_common_depends "$1" "on" "required" "$2" ""
+
+}
+
+#---------------------------------------------------------------------
+## @param provider name
+## @param addition to OPTS if enabled
+## @param addition to OPTS if disabled
+## @param description
+## Handles optional dependency on a provider.
+#---------------------------------------------------------------------
+function work_optional_depends_provider()
+{
+
+ debug "libdepends" "$FUNCNAME - $@"
+ local default tmp installed=no
+ local status=()
+
+ if [[ $4 ]] ; then
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}optionally requires some${DEFAULT_COLOR}" \
+ "${SPELL_COLOR}${1}${DEFAULT_COLOR} ($4)."
+ else
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}optionally requires some${DEFAULT_COLOR}" \
+ "${SPELL_COLOR}${1}${DEFAULT_COLOR}."
+ fi
+
+ local CANDIDATES=$( find_providers $1)
+ # if not reconfiguring check if theres already an answer in DEPENDS_STATUS
+ if [[ ! $RECONFIGURE ]]; then
+ explode "$(search_depends_status $DEPENDS_STATUS "$SPELL" ".*($1)")" ":" "status"
+ local tmp=${status[1]%(*} # Name of spell which provides $1
+ if spell_ok $tmp &&
+ query "Continue to use ${SPELL_COLOR}$tmp${DEFAULT_COLOR}?" y; then
+ private_common_depends "$tmp($1)" "on" "required" "$2" "$3"
+ return 0
+ fi
+ fi
+
+ # check if theres an abandoned answer, but only if its still a provider
+ if [[ ! $default ]] && [ -e $ABANDONED_DEPENDS/$SPELL ] ; then
+ tmp=$(search_depends_status $ABANDONED_DEPENDS/$SPELL "$SPELL" ".*($1)"|awk -F: '{print $2;exit}')
+ [[ $tmp ]] && echo $CANDIDATES|grep -q "\<$tmp\>" && default=$tmp
+ fi
+
+ # check if theres a default provider
+ if [[ ! $default ]]; then
+ tmp=$(search_default_provider $DEFAULT_PROVIDERS "" "$1")
+
+ # make sure we found /something/ before trying to analyze it
+ # otherwise we'll fall into the else case and use 'none' as the
+ # provider, and short-circuit the other guesses
+ if [[ $tmp ]] ; then
+ explode "$tmp" ":" "status"
+ tmp=${status[0]}
+ if [[ ${status[2]} == on ]] ; then
+ # if the user said "on" use the default rather than none
+ # unless theres something wrong with the provider they chose
+ # in which case fall back to none
+ [[ $tmp ]] && echo $CANDIDATES|grep -q "\<$tmp\>" &&
+ default=$tmp || default=none
+ else
+ default=none
+ fi
+ fi
+ fi
+
+ # check if we've already answered this question
+ if [[ ! $default ]]; then
+ for tmp in $CANDIDATES; do
+ echo ${spells[@]} | grep -q "\<$tmp\>" && default=$tmp && break
+ done
+ fi
+
+ # check if theres a provider already installed
+ if [[ ! $default ]]; then
+ for tmp in $CANDIDATES; do
+ spell_ok $tmp && default=$tmp && break
+ done
+ fi
+
+ select_provider "provider" "$default" 1 $CANDIDATES
+
+ if [ $provider == "none" ] ; then
+ private_common_depends "($1)" "off" "optional" "$2" "$3"
+ else
+ private_common_depends "$provider($1)" "on" "optional" "$2" "$3"
+ fi
+
+}
+
+#---------------------------------------------------------------------
+## @param spell name
+## @param addition to OPTS if enabled
+## @param addition to OPTS if disabled
+## @param description
+## Handles optional dependency on a spell.
+#---------------------------------------------------------------------
+function work_optional_depends_spell()
+{
+
+ debug "libdepends" "$FUNCNAME - $@"
+ local default
+
+ # if $1 optionally depends on something exiled we always say no
+ if spell_exiled $1 ; then
+ message "${SPELL_COLOR}${1}${DEFAULT_COLOR} has been exiled! not using as a depends"
+ hash_put $CANNOT_CAST_HASH "$1" "Exiled"
+ private_common_depends "$1" "off" "optional" "$2" "$3"
+ return 0
+ fi
+
+
+ if [[ ! $RECONFIGURE ]] ; then
+ # See if there are preferences already in DEPENDS_STATUS, but only if
+ # not reconfiguring...
+ # example: icewm:imlib:off:optional:--with-imlib:--with-xpm
+ local status=()
+ explode "$(search_depends_status $DEPENDS_STATUS "$SPELL" "$1")" ":" "status"
+ if [[ ${status[2]} ]] ; then
+ # ah there are! use them
+ if [[ ${status[2]} == "on" ]] ; then
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}optionally depends on" \
+ "${SPELL_COLOR}${1}${DEFAULT_COLOR}"
+ else
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}has a disabled optional depends on" \
+ "${SPELL_COLOR}${1}${DEFAULT_COLOR}"
+ fi
+ private_common_depends "$1" "${status[2]}" "optional" "$2" "$3"
+ return 0
+ fi
+ fi
+
+ # check for abandoned answers
+ if [ -e $ABANDONED_DEPENDS/$SPELL ] ; then
+ debug "libdepends" "Checking in abandoned depends"
+ default=$(search_depends_status $ABANDONED_DEPENDS/$SPELL "$SPELL" "$1"|awk -F: '{print $3;exit}')
+ fi
+
+ # check the defaults file...
+ # first for explicit $SPELL -> $2
+ if [[ ! $default ]]; then
+ debug "libdepends" "Checking in default answers"
+ default=$(search_default_depends $DEFAULT_DEPENDS $SPELL $1|awk -F: '{print $3; exit}')
+ fi
+
+ # then for anything -> $2
+ if [[ ! $default ]]; then
+ debug "libdepends" "Checking in default answers"
+ default=$(search_default_depends $DEFAULT_DEPENDS "" $1|awk -F: '{print $3; exit}')
+ fi
+
+ # then for $1 -> anything
+ if [[ ! $default ]]; then
+ debug "libdepends" "Checking in default answers"
+ default=$(search_default_depends $DEFAULT_DEPENDS $SPELL "" |awk -F: '{print $3; exit}')
+ fi
+
+ # check the install queue
+ if [[ ! $default ]]; then
+ debug "libdepends" "Checking in queue"
+ #\< and \> match the empty string at the start and end of a word
+ echo ${spells[@]} | grep -q "\<$1\>" && default=on
+ fi
+
+ # check if installed/held
+ if [[ ! $default ]]; then
+ debug "libdepends" "Checking if already installed"
+ spell_ok $1 && default=on
+ fi
+
+ # otherwise default to no
+ [[ ! $default ]] && default=off
+
+ local install=off
+
+ local stuff
+ [[ $default == off ]] && stuff=n || stuff=y
+
+ message "${SPELL_COLOR}${1}${DEFAULT_COLOR}" \
+ "is an optional dependency for" \
+ "${SPELL_COLOR}$SPELL${DEFAULT_COLOR} ($4)"
+
+ if spell_ok $1 ; then
+ query "Do you want to use ${SPELL_COLOR}$1${DEFAULT_COLOR}?" "$stuff" &&
+ install="on"
+ else
+ query "Do you want to cast ${SPELL_COLOR}$1${DEFAULT_COLOR}?" "$stuff" &&
+ install="on"
+ fi
+
+ private_common_depends "$1" "$install" "optional" "$2" "$3"
+}
+
+
+#---------------------------------------------------------------------
+## @param name of return variable
+## @param default answer
+## @param 0 if required 1 if optional
+## @param list of possible providers
+## Present a list to the user complete with info about whats installed
+## and what isnt and allow a default value to be used
+#---------------------------------------------------------------------
+function select_provider()
+{
+ local returnvar=$1
+ local default=$2
+ local optional=$3
+ local i
+ shift 3
+
+ local each default_char=0 stuff=()
+ local char answer spell
+
+ # we can only read one character so use every one we can, I dont expect
+ # there to be more than 62 providers
+
+ # in bash 3.0 this expands to all the numbers and letters
+ # stuff=({0..9} {a..z} {A..Z})
+ # but we're still on bash 2 which cant do that, if someone
+ # knows a better way to do this please tell me
+ stuff=(0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
+
+ if [ $optional == 1 ] ; then
+ hash_put CHAR_TO_SPELL 0 "none"
+ let i=1
+ message "\t${DEFAULT_COLOR}(0)\t${SPELL_COLOR}[none]${DEFAULT_COLOR}"
+ else
+ let i=0
+ fi
+
+ for each in $@; do
+ char=${stuff[$i]}
+ hash_put CHAR_TO_SPELL $char $each
+
+ [[ $each == $default ]] && default_char=$char
+ if spell_ok $each ; then
+ message "\t${DEFAULT_COLOR}($char)\t${SPELL_COLOR}$each${DEFAULT_COLOR}\t (installed)"
+ else
+ message "\t${DEFAULT_COLOR}($char)\t${SPELL_COLOR}$each${DEFAULT_COLOR}"
+ fi
+ let i++
+ done
+
+ message -n "\n${QUERY_COLOR}Which one do you want? " \
+ "[$default_char]$DEFAULT_COLOR "
+ read -t $PROMPT_DELAY -n 1 answer
+ [[ $answer ]] || answer=$default_char
+ spell="$(hash_get CHAR_TO_SPELL $answer)"
+
+ while [[ ! $spell ]] ; do
+ message -n "\n${QUERY_COLOR}Which one do you want? " \
+ "[$default_char]$DEFAULT_COLOR "
+ read -t $PROMPT_DELAY -n 1 answer
+ [[ $answer ]] || answer=$default_char
+ spell=$(hash_get CHAR_TO_SPELL $answer)
+ done
+ echo
+ hash_unset CHAR_TO_SPELL
+ eval $returnvar=\"$spell\"
+}
+
+#---------------------------------------------------------------------
+## @param Target of the trigger
+## @param Action to execute (cast_self, check_self, etc).
+##
+## Create a trigger on ourself effecting the target spell,
+## shorthand for putting a TRIGGERS file in the target spell.
+#---------------------------------------------------------------------
+function real_up_trigger() {
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}triggers a" \
+ "${SPELL_COLOR}${2}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}on${DEFAULT_COLOR}" \
+ "${SPELL_COLOR}${1}${DEFAULT_COLOR}"
+ private_up_trigger $SPELL "$1" "$2"
+}
+
+#---------------------------------------------------------------------
+## @param Current spell
+## @param Target target of the trigger
+## @param Action to execute (cast_self, check_self, etc).
+##
+## Register a trigger, when $SPELL is cast, a $ACTION is executed
+## on $TARGET
+#---------------------------------------------------------------------
+function private_up_trigger() {
+ local SPELL=$1
+ local TARGET=$2
+ local ACTION=$3
+ # this maps triggerers to their trigerees
+ # perl -> cast_self:perl_module
+ hash_append trg_f_hash $SPELL "$TARGET:$ACTION" $'\n' &&
+ # this maps trigerees to triggerers
+ # cast_self:perl_module -> perl
+ hash_append trg_r_hash "$TARGET:$ACTION" " $SPELL "
+ echo $NEW_DEPENDS | grep -q "$TARGET" ||
+ NEW_DEPENDS=( ${NEW_DEPENDS[*]} $spell)
+ triggerees=( ${triggerees[*]} $TARGET )
+}
+
+#---------------------------------------------------------------------
+## all the depends callbacks eventually bottom out here
+## if a spell depends or doesnt depend on some other spell
+## @param Spell
+## @param on/off
+#---------------------------------------------------------------------
+function private_common_depends()
+{
+ debug "libdepends" "$FUNCNAME - $SPELL - $@"
+ add_depends $spell_depends "$SPELL" "$@"
+
+ if [[ $2 == on ]] ; then
+ # ${1%(*} = spell name (strips potential provider name)
+ local spell_name=${1%(*}
+ NEW_DEPENDS=( ${NEW_DEPENDS[*]} $spell_name )
+ fi
+
+ return 0
+}
+
+#---------------------------------------------------------------------
+## Default trigger checking function. Asks user if they want to
+## run the trigger.
+#---------------------------------------------------------------------
+function default_trigger_check() {
+ message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}triggers a" \
+ "${SPELL_COLOR}${ACTION}${DEFAULT_COLOR}" \
+ "${CHECK_COLOR}on${DEFAULT_COLOR}" \
+ "${SPELL_COLOR}${TARGET}${DEFAULT_COLOR}"
+ query "Run the trigger?" y
+}
+
+#---------------------------------------------------------------------
+## Inspects each trigger, asks the user if they want to run it.
+#---------------------------------------------------------------------
+function private_add_triggerees() {
+
+ # add the spells that we trigger to the $spells list
+ # having duplicate items is okay
+ local ACTION TARGET
+ local running_trigger
+
+ # run the trigger check file
+ # this is made into a sub-function just to reduce duplication...
+ function private_add_triggerees_sub1() {
+ running_trigger=0
+ persistent_load
+ if test -x $SCRIPT_DIRECTORY/TRIGGER_CHECK; then
+ source $SCRIPT_DIRECTORY/TRIGGER_CHECK
+ else
+ default_trigger_check
+ fi
+ running_trigger=$?
+ persistent_save
+ if [[ $running_trigger == 0 ]] ; then
+ private_up_trigger "$SPELL" "$TARGET" "$ACTION"
+ fi
+ }
+
+ # frontend to help with calling the trigger check file
+ function private_add_triggerees_sub2() {
+ local ACTION=$1
+ private_add_triggerees_sub1
+ }
+
+ for ACTION in cast_self check_self dispel_self run_script; do
+ for TARGET in $(get_triggerees $SPELL on_cast $ACTION); do
+ if [[ $ACTION == run_script ]] ; then
+ iterate private_add_triggerees_sub2 $'\n' \
+ "$(get_run_script_triggers $SPELL on_cast $TARGET)"
+ else
+ private_add_triggerees_sub1
+ fi
+ done
+ done
+ return 0
+}
+
+
+#---------------------------------------------------------------------
+## Adds the dependency to the hastable
+#---------------------------------------------------------------------
+function private_add_depends()
+{
+ debug "libdepends" "$FUNCNAME: SPELL=$SPELL, NEW_DEPENDS=${NEW_DEPENDS[*]}"
+ hash_put "$CAST_HASH" "$SPELL" "${NEW_DEPENDS[*]}"
+
+ for child in ${NEW_DEPENDS[*]}; do
+ hash_append "$BACK_CAST_HASH" "$child" "$SPELL"
+ done
+
+ # force implied basesystem dependency in the depends tree
+ if [[ $FORCE_BASESYSTEM_DEPENDS == on ]] &&
+ [[ $SPELL != basesystem ]] &&
+ ! echo $base_deps| grep -q $SPELL; then
+ hash_append "$CAST_HASH" "$SPELL" "basesystem"
+ hash_append "$BACK_CAST_HASH" "basesystem" "$SPELL"
+ fi
+
+ spells=( ${spells[*]} ${NEW_DEPENDS[*]} )
+
+ TRIGGEREES=( ${TRIGGEREES[*]} ${triggerees[*]} )
+ spells=( ${spells[*]} ${triggerees[*]} )
+
+ hash_put "depends_looked_at" "$SPELL" "done"
+
+}
+
+
+#########################BEGIN OTHER STUFF############################
+
+#---------------------------------------------------------------------
+## Removes a dependency from the list.
+#---------------------------------------------------------------------
+function private_remove_dependees()
+{
+ local SPELL=$1
+ local i
+ echo "Removing dependees of $1"
+
+ # spell is being removed from cast list, add to FAILED_LIST
+ echo "$SPELL" >> $FAILED_LIST
+ hash_put depends_looked_at $SPELL failed
+ hash_unset "$CAST_HASH" "$SPELL"
+ hash_put $CANNOT_CAST_HASH "$SPELL" "Failed"
+}
+
+#---------------------------------------------------------------------
+## Sets a spell's aux. config info.
+## @Globals SPELL
+#---------------------------------------------------------------------
+function run_spell_config()
+{
+
+ SPELL_CONFIG=$DEPENDS_CONFIG/$SPELL
+ debug "libdepends" "run_spell_config() - DEPENDS_CONFIG=$DEPENDS_CONFIG SPELL=$SPELL, SPELL_CONFIG=$SPELL_CONFIG DEPENDS_STATUS=$DEPENDS_STATUS"
+
+ if [ -x $SPELL_CONFIG ]; then
+ debug "libdepends" "run_spell_config() - found $SPELL_CONFIG"
+ . $SPELL_CONFIG
+ fi
+}
+
+#---------------------------------------------------------------------
+# i dont know if this should be here
+#---------------------------------------------------------------------
+function show_depends()
+{
+ debug "libdepends" "show_depends() - $@"
+ local DEP_SPELL=`esc_str $1`
+ local DEPTH=$2
+
+ if ! echo "$DONE" | grep -q "$DEP_SPELL"; then
+ DONE="$DONE $1"
+
+ function ld99() {
+
+ [[ $MAX_DEPTH ]] && [[ $DEPTH -ge $MAX_DEPTH ]] && return 1
+
+ SPELL=`echo $1 | cut -d : -f1`
+ STATUS=`echo $1 | cut -d : -f3`
+
+ if [ "$STATUS" == "on" ]; then
+ echo $1
+ show_depends $SPELL $(( DEPTH + 1 ))
+ fi
+
+ }
+
+ iterate "ld99" $'\n' "$(grep ":$DEP_SPELL\(([^:]*)\)\?:" $DEPENDS_STATUS)"
+
+ 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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libdepengine b/var/lib/sorcery/modules/libdepengine
new file mode 100644
index 0000000..845f92d
--- /dev/null
+++ b/var/lib/sorcery/modules/libdepengine
@@ -0,0 +1,402 @@
+#!/bin/bash
+#------------------------------------------------------------------------
+## @Synopsis Implements sorcery's dependency/trigger tree walking engine.
+##
+## @Copyright (C) 2005 The Source Mage Team <http://www.sourcemage.org>
+##
+## In the simple model, without triggers, we color graph nodes (spells)
+## white, and as we recursiely visit them, mark them grey. When all of
+## a spell's children have been visited, and successfully cast, we
+## cast the spell, and mark the node either black:0 or black:1 for success
+## or failure respectively. If a child fails we mark the current node
+## black:1 and return. In the case that we visit a child that is grey,
+## a dependency loop is detected, currently we ignore it and build the
+## tail spell anyway, in the future we could break optional depends and
+## cast some spell twice.
+##
+## The more complex model implemented below includes the above, but after
+## a spell builds (or fails), it colors itself brown and executes its triggers.
+## This is a little more complicated because there could be multiple
+## triggers on a single spell and cycles are frequent. If successful, the
+## spell marks each trigger as a 'pending trigger' . Then if that spell is
+## the last triggerer, the trigger is registered (possibly from another
+## spell), and the trigger is not grey (depends cycle, indicating it is
+## unsafe to cast the spell) the spell is cast in a special variant of the
+## above algorithm. The key difference is that if a grey node is encountered
+## (depends cycle), instead of breaking it, the spell gracefully backs
+## off and the trigger is left on the pending triggers list while other
+## triggers are executed. All the triggers for the spell are executed
+## similarly until there are no triggers left, or no progress is made. If
+## after all spells are cast there are still pending triggers they are
+## built at that point without the graceful cycle handling.
+##
+#------------------------------------------------------------------------
+
+
+#------------------------------------------------------------------------
+## This is the entry point for the dependency engine, it handles top level
+## tasks. It invokes the cast_engine on each requested spell and cleans
+## up any left-over pending triggers.
+#------------------------------------------------------------------------
+function depengine_entry_point() {
+ $STD_DEBUG
+ local spell_list=$1
+ local need_cast_list=$2
+ local spell pending_list spell_status rc
+ for spell in $(hash_get_table_fields dep_f_hash); do
+ dpgn_set_spell_color $spell white
+ done
+
+ for spell in $spell_list; do
+ spell_status=$(dpgn_get_spell_color $spell)
+ if [[ $spell_status != white ]] ; then
+ debug "libdepengine" "already did $spell"
+ else
+ depengine_cast_engine $spell
+ fi
+ done
+
+ pending_list=$(dpgn_get_all_pending_triggers)
+ while [[ $pending_list ]] ; do
+ for spell in $pending_list; do
+ if dpgn_is_pending_trigger_registered $spell; then
+ depengine_cast_engine $spell
+ rc=$?
+ [[ $rc == 1 ]] && dpgn_unregister_pending_trigger $spell
+ fi
+ done
+ pending_list=$(dpgn_get_all_pending_triggers)
+ done
+ return 0
+}
+
+#------------------------------------------------------------------------
+## Top level routine for executing a graph node
+## Builds the children, then itself, then executes triggers.
+##
+## @param Spell to cast
+## @param In trigger flag, default 0. If 1 and a depends loop exists
+## back-off and fail gracefully.
+#------------------------------------------------------------------------
+function depengine_cast_engine() {
+ $STD_DEBUG
+ local spell=$1
+ local in_trigger=${2:-0}
+ local org_color=$(dpgn_get_spell_color $spell)
+ local rc
+
+ dpgn_set_spell_color $spell grey
+
+ recurse_depends $spell $in_trigger
+ rc=$?
+ if [[ $in_trigger != 0 ]] && [[ $rc == 2 ]] ; then
+ dpgn_set_spell_color $spell $org_color
+ return 2
+ fi
+
+ spell_status=$(dpgn_get_spell_color $spell)
+ if [[ $spell_status == grey ]] && [[ $rc == 0 ]] ; then
+ dpgn_cast_spell_front $spell
+ rc=$?
+ fi
+
+ dpgn_set_spell_color $spell brown
+
+ if [[ $rc == 0 ]] ; then
+ # if some other spell is going to trigger us *and* it is marked
+ # grey, this is a cycle involving triggers (yuck!)
+ # when that happens dont execute $spell's triggers
+ # because it will be re-triggered
+ local grey_trigerer=0
+ for trigerer in $(hash_get trg_r_hash cast_self:$spell); do
+ # note that $spell's status is grey
+ [[ $trigerer == $spell ]] && continue
+ child_status=$(hash_get state_hash $trigerer)
+ if [[ $child_status == grey ]] ; then
+ #[[ $child_status == brown ]] && [[ $org_color == brown ]] ; then
+ debug "libdepengine" "$spell has a $child_status trigerer on $trigerer!"
+ grey_trigerer=1
+ break
+ fi
+ done
+ if [[ $grey_trigerer == 0 ]] ; then
+ execute_triggers $spell 0
+ fi
+ else
+ # execute triggers that were delayed because of us
+ # despite the fact that we failed
+ execute_triggers $spell 1
+ fi
+
+ dpgn_set_spell_color $spell black:$rc
+
+ return $rc
+}
+
+#------------------------------------------------------------------------
+## Iterative recursive step. Build each of the spell's dependencies.
+## @param Spell
+## @param In trigger flag (optional) if 1, then back off more readily in
+## the event of a dependency loop.
+## @global MINUS_K if set, then act like make -k and continue building
+## dependent spells even if another one fails.
+##
+#------------------------------------------------------------------------
+function recurse_depends() {
+ $STD_DEBUG
+ local spell=$1
+ local in_trigger=${2:-0}
+ local rc=0
+
+ for child in $(hash_get dep_f_hash $spell); do
+
+ # check if any of our dependencies failed while we weren't looking
+ # if one did, bail out, if MINUS_K is set then skip this and build
+ # everything we can
+ if ! [[ $MINUS_K ]] ; then
+ for _child in $(hash_get dep_f_hash $spell); do
+ if [[ $(dpgn_get_spell_color $_child) == black:1 ]] ; then
+ debug libdepengine "$spell already had a failed dep on $_child"
+ return 1
+ fi
+ done
+ fi
+
+ child_status=$(dpgn_get_spell_color $child)
+ case $child_status in
+ white)
+ tmp_rc=0
+ if list_find "$need_cast_list" $child; then
+ depengine_cast_engine $child $in_trigger
+ tmp_rc=$?
+ if [[ $tmp_rc != 0 ]] ; then
+ rc=$tmp_rc
+ [[ $MINUS_K ]] || break
+ fi
+ fi
+ ;;
+ grey)
+ if [[ $in_trigger != 0 ]] ; then
+ debug "libdepengine" "found grey depend from trigger $spell -> $child"
+ return 2
+ else
+ debug "libdepengine" "detected a dependency loop $spell -> $child"
+ fi
+ ;;
+ brown)
+ debug "libdepengine" "found brown depend $spell -> $child"
+ ;;
+ black:0)
+ debug "libdepengine" "$child already built properly, continuing"
+ ;;
+ black:1)
+ rc=1
+ [[ $MINUS_K ]] || break
+ ;;
+ esac
+ done
+ # build triggering spells, this helps keep the order
+ # its true however that most of the time, we'll also depend on the
+ # triggerer, this just catches those cases where we dont, but doesn't
+ # worry too much about a failure
+ for triggerer in $(hash_get trg_r_hash cast_self:$spell); do
+ trgrr_status=$(dpgn_get_spell_color $child)
+ if [[ "$trgrr_status" == "white" ]] ; then
+ if list_find "$need_cast_list" $child; then
+ depengine_cast_engine $child $in_trigger
+ fi
+ fi
+ done
+ return $rc
+}
+
+#------------------------------------------------------------------------
+## Attempt to run all this spell's triggers. Some triggers may not be
+## runable at this time, or it may be better to run them later.
+#------------------------------------------------------------------------
+function execute_triggers() {
+ $STD_DEBUG
+ local spell=$1
+ local failed=$2
+ local trigger rc
+ local trg_array trg_action trg_target stuff trg_color
+
+ function execute_triggers_sub() {
+ trigger=$1
+ explode "$trigger" : trg_array
+ trg_target=${trg_array[0]}
+ trg_action=${trg_array[1]}
+ # get the other spells that trigger this action
+ trg_color=$(dpgn_get_spell_color $trg_target)
+
+ # do trigger specific stuff, set needs_register to 1 if
+ # casting is needed
+ if [[ $failed == 0 ]]; then
+ local needs_register=""
+
+ if [[ $trg_action == cast_self ]] ; then
+ if ! [[ $CAST_PASS == three ]] ; then
+ message "${MESSAGE_COLOR}Queued cast_self on" \
+ "${SPELL_COLOR}$trg_target${DEFAULT_COLOR}."
+ fi
+ needs_register=1
+ elif [[ $trg_action == check_self ]] ; then
+ if [[ $trg_color != brown ]] ; then
+ if [[ $CAST_PASS == three ]] ; then
+ needs_register=1
+ else
+ message "${MESSAGE_COLOR}Performing check_self on" \
+ "${SPELL_COLOR}$trg_target${DEFAULT_COLOR}"
+ if cleanse --nofix_quick "$trg_target"; then
+ echo "${trg_target}" >> $CHECK_TRIGGERS_SUCCESS
+ else
+ echo "${trg_target}" >> $CHECK_TRIGGERS_FAILURE
+ needs_register=1
+ fi
+ fi
+ fi
+ elif [[ $CAST_PASS != three ]] ; then
+ # run any other trigger besides the two above
+ # but not if we're summoning
+ local action=cast
+ do_trigger "$spell:on_cast:$trg_target:$trg_action"
+ fi
+
+ # a brown trigger is a loop, drop it
+ if [[ $needs_register ]] && [[ $trg_color != brown ]] ; then
+ dpgn_register_pending_trigger $trg_target
+ fi
+ fi
+
+ # if there are no white triggerers, we are the last triggerer
+ local last=1
+ local triggerers=$(hash_get trg_r_hash $trg_target:$trg_action)
+ for triggerer in $triggerers; do
+ [[ $triggerer == $spell ]] && continue
+ if [[ $(dpgn_get_spell_color $triggerer) == white ]] ; then
+ last=0
+ break
+ fi
+ done
+
+ # decide if the trigger should be executed
+ if [[ $last == 1 ]] &&
+ dpgn_is_pending_trigger_registered $trg_target &&
+ [[ $trg_color != grey ]] ; then
+ list_add stuff $trg_target
+ fi
+ }
+ iterate execute_triggers_sub $'\n' "$(hash_get trg_f_hash $spell)"
+
+ local curr_list=$stuff
+ local prev_list=""
+ local item
+ # execute triggers, so may not be executable so delay them and try
+ # again. Give up when there are no triggers left, or no progress
+ # is made. Left-over triggers are cleaned up at the end.
+ while [[ $curr_list ]] && [[ "$curr_list" != "$prev_list" ]] ; do
+ prev_list="$curr_list"
+ for item in $prev_list; do
+ if dpgn_is_pending_trigger_registered $item; then
+ depengine_cast_engine $item 1
+ rc=$?
+ if [[ $rc == 2 ]] ; then
+ list_add curr_list $item
+ fi
+ fi
+ done
+ done
+ return 0
+}
+
+### helpers ####
+#------------------------------------------------------------------------
+## frontend to cast spells, unregisters pending triggers if any
+#------------------------------------------------------------------------
+function dpgn_cast_spell_front() {
+ if ! [[ $CAST_PASS ]] ; then
+ message "ERROR: Missing cast pass variable!!"
+ exit 1
+ fi
+
+ [[ $COMPILE ]] && args="-c "
+
+ if [[ $CAST_PASS == three ]] ; then
+ # pass four can finish first if there are triggers that aren't run
+ # or spells fail.
+ if test -f $TMP_DIR/pass_four.done; then
+ message "Pass four completed! bailing out!!!"
+ # we're in a seperate bash subshell, exiting here is okay
+ exit
+ fi
+ bash $SAFE_CAST $args $1 &>/dev/null
+ else
+ bash $SAFE_CAST $args $1
+ fi
+ local rc=$?
+
+ dpgn_unregister_pending_trigger $1
+ return $rc
+}
+
+#------------------------------------------------------------------------
+## set the spell color
+#------------------------------------------------------------------------
+function dpgn_set_spell_color() {
+ hash_put state_hash $1 $2
+}
+#------------------------------------------------------------------------
+## get the spell color
+#------------------------------------------------------------------------
+function dpgn_get_spell_color() {
+ hash_get state_hash $1
+}
+
+#------------------------------------------------------------------------
+## Mark this spell as a pending trigger
+#------------------------------------------------------------------------
+function dpgn_register_pending_trigger() {
+ hash_put pending_triggers $1 1
+}
+
+#------------------------------------------------------------------------
+## determine if a trigger is pending or not
+#------------------------------------------------------------------------
+function dpgn_is_pending_trigger_registered() {
+ [[ $(hash_get pending_triggers $1) == 1 ]]
+}
+
+#------------------------------------------------------------------------
+## mark spell as no longer needing a trigger
+#------------------------------------------------------------------------
+function dpgn_unregister_pending_trigger() {
+ hash_unset pending_triggers $1
+}
+
+#------------------------------------------------------------------------
+## return all the pending triggers
+#------------------------------------------------------------------------
+function dpgn_get_all_pending_triggers() {
+ hash_get_table_fields pending_triggers
+}
+
+#------------------------------------------------------------------------
+##=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
+##
+#------------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libdispel b/var/lib/sorcery/modules/libdispel
new file mode 100644
index 0000000..509bfe5
--- /dev/null
+++ b/var/lib/sorcery/modules/libdispel
@@ -0,0 +1,414 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## @Synopsis Functions for dispelling a spell
+##
+##
+## @Copyright Original version Copyright 2001 by Kyle Sallee
+## @Copyright Additions/Corrections Copyright 2002-4 by the SourceMage Team
+##
+## Functions for dispelling spells.
+#---------------------------------------------------------------------
+
+
+#---------------------------------------------------------------------
+## @Stdin list of files
+##
+## Given a list of files from standard input, deletes each file.
+## Performs a "rm -f" on each file given in standard input, so be
+## careful using this function!
+##
+#---------------------------------------------------------------------
+function reap_regular_files() {
+ debug "libsorcery" "reap_regular_files()"
+ local FILE
+ while read FILE; do
+ rm -f "$FILE"
+ done
+}
+
+
+#---------------------------------------------------------------------
+## @Stdin list of files
+##
+## Reads a list of files from standard input. If the file has been
+## modified (md5sum doesn't match the stored md5sum), then function
+## C<reap_modified_file> is called. Otherwise, the file is deleted.
+##
+#---------------------------------------------------------------------
+function reap_config_files() {
+ debug "libsorcery" "reap_config_files()"
+ local FILE
+ while read FILE; do
+ [[ $FILE ]] || continue
+ if grep -q "$(md5sum "$FILE" )" "$MD5S"; then
+ debug libdispel "$FILE is a config file that hasnt changed, removing..."
+ rm -f "$FILE"
+ else
+ reap_modified_file "$FILE"
+ fi
+ done
+}
+
+#---------------------------------------------------------------------
+## @param file
+##
+## If C<PRESERVE> is off, will move the file to filename.YYYYMMDD. If
+## C<PRESERVE> is on, the file will not be moved.
+##
+#---------------------------------------------------------------------
+function reap_modified_file() {
+ local SAVE
+ message "${FILE_COLOR}${1}${DEFAULT_COLOR}"
+ message "${MESSAGE_COLOR}was previously modified by SA?"
+ case $PRESERVE in
+ on) message "Therefore, it was not reaped." ;;
+ off) SAVE="$1.`date -u +%Y%m%d`"
+ mv $1 $SAVE
+ message "Therefore, it was moved to"
+ message "${FILE_COLOR}${SAVE}" ;;
+ esac
+ message "${DEFAULT_COLOR}"
+
+}
+
+#---------------------------------------------------------------------
+## Removes depends entries for what the spell depends on
+#---------------------------------------------------------------------
+function reap_depends() {
+ # save the old depends data as abandoned stuff so its seen later on
+ # recasts as the default
+ mkdir -p $ABANDONED_DEPENDS
+ search_depends_status $DEPENDS_STATUS $SPELL > $ABANDONED_DEPENDS/$SPELL
+
+ # This conditional is here because the iso team wants to be able to
+ # save dependencies after dispel (bug 8109), average users are expected
+ # to always run this code to remove old depends.
+ if [[ $NO_REAP_DEPENDS != "on" ]] ; then
+ remove_depends_status $DEPENDS_STATUS $SPELL
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## @param file
+## @param file
+##
+## First argument is a file containing a list of files to reap.
+## Second argument is a file containing md5 sums of those files, used
+## to detect if a config file has been modified. Config files are any
+## files in /etc or any of its sub-directories.
+##
+#---------------------------------------------------------------------
+function reaper() {
+# Example: reaper "$INSTALL_LOG" "$MD5_LOG"
+
+ debug "libsorcery" "Running reaper() on $1"
+
+ if ! [ "$REAP" == "on" ] ||
+ ! [ -f $1 ]; then return
+ fi
+
+ local MD5S=$2
+
+ local UNIQUE="$$.$RANDOM"
+ local REAPER_FILES="$TMP_DIR/reaper.$UNIQUE.files"
+ local REAPER_DIRS="$TMP_DIR/reaper.$UNIQUE.dirs"
+ local REAPER_SYMS="$TMP_DIR/reaper.$UNIQUE.syms"
+
+ rm -f $REAPER_FILES $REAPER_DIRS $REAPER_SYMS
+
+ # convert from TRACK_ROOT to / for protected filtering,
+ # then to INSTALL_ROOT.
+ # INSTALL_ROOT is relative to /, TRACK_ROOT is arbitrary.
+ seperate_state_files $1 /dev/stdout /dev/null |
+ log_adjuster /dev/stdin /dev/stdout log filterable |
+ filter_protected |
+ log_adjuster /dev/stdin /dev/stdout filterable root |
+ while read ITEM; do
+ if test -h "$ITEM"; then echo "$ITEM" >> $REAPER_SYMS
+ elif test -f "$ITEM"; then echo "$ITEM" >> $REAPER_FILES
+ elif test -d "$ITEM"; then echo "$ITEM" >> $REAPER_DIRS
+ fi
+ done
+
+ if test -f $REAPER_FILES ; then
+ sed "s:^$INSTALL_ROOT::" $REAPER_FILES |
+ grep -v /var/log/sorcery |
+ filter_configs -v |
+ sed "s:^:$INSTALL_ROOT:" |
+ reap_config_files
+
+ sed "s:^$INSTALL_ROOT::" $REAPER_FILES |
+ grep -v /var/log/sorcery |
+ filter_configs |
+ sed "s:^:$INSTALL_ROOT:" |
+ reap_regular_files
+ fi
+
+ [ -f $REAPER_SYMS ] && rm -f `cat $REAPER_SYMS` 2>/dev/null
+ [ -f $REAPER_DIRS ] && rmdir `sort -r $REAPER_DIRS` 2>/dev/null
+ [ -f $REAPER_FILES ] && rmdir `dirnames < $REAPER_FILES |uniq|sort -r` 2>/dev/null
+
+ rm -f $REAPER_FILES $REAPER_DIRS $REAPER_SYMS
+}
+
+#---------------------------------------------------------------------
+## @param file
+##
+## First argument is a file containing install log, removes state files
+##
+#---------------------------------------------------------------------
+function reap_state_files() {
+# Example: reaper "$INSTALL_LOG" "$MD5_LOG"
+
+ debug "libsorcery" "Running reaper() on $1"
+
+ local UNIQUE="$$.$RANDOM"
+ local REAPER_FILES="$TMP_DIR/reaper.$UNIQUE.files"
+ local REAPER_DIRS="$TMP_DIR/reaper.$UNIQUE.dirs"
+ local REAPER_SYMS="$TMP_DIR/reaper.$UNIQUE.syms"
+
+ rm -f $REAPER_FILES $REAPER_DIRS $REAPER_SYMS
+
+ # convert from TRACK_ROOT to / for protected filtering,
+ # then to INSTALL_ROOT.
+ # INSTALL_ROOT is relative to /, TRACK_ROOT is arbitrary.
+ seperate_state_files $1 /dev/null /dev/stdout |
+ log_adjuster /dev/stdin /dev/stdout log root |
+ grep -v ${LOG_DIRECTORY#$STATE_ROOT} |
+ while read ITEM; do
+ if test -h "$ITEM"; then echo "$ITEM" >> $REAPER_SYMS
+ elif test -f "$ITEM"; then echo "$ITEM" >> $REAPER_FILES
+ elif test -d "$ITEM"; then echo "$ITEM" >> $REAPER_DIRS
+ fi
+ done
+
+ if test -f $REAPER_FILES ; then
+ cat $REAPER_FILES | xargs rm
+ fi
+
+ [ -f $REAPER_SYMS ] && rm -f `cat $REAPER_SYMS` 2>/dev/null
+ [ -f $REAPER_DIRS ] && rmdir `sort -r $REAPER_DIRS` 2>/dev/null
+ [ -f $REAPER_FILES ] && rmdir `dirnames < $REAPER_FILES |uniq|sort -r` 2>/dev/null
+
+ rm -f $REAPER_FILES $REAPER_DIRS $REAPER_SYMS
+}
+
+#-----
+## Checks that a spell is indeed installed.
+## @return true Can be dispelled
+## @return false Cannot be dispelled
+#-----
+function dispel_not_possible() {
+
+ if ! spell_ok $SPELL ; then
+ message "${SPELL_COLOR}${SPELL}${PROBLEM_COLOR} is not installed." \
+ "${DEFAULT_COLOR}"
+ else
+ false
+ fi
+
+}
+
+#-----
+## Does the sustained checks for spells
+#-----
+function dispel_sustained() {
+
+ if [ "$SUSTAIN" == "on" ] &&
+ grep -q "^$SPELL$" $SUSTAINED
+ then
+ message "${SPELL_COLOR}${SPELL}${PROBLEM_COLOR} is sustained." \
+ "${DEFAULT_COLOR}"
+ else
+ false
+ fi
+
+}
+
+#-----
+## Find out where a spell is located
+## @Globals SPELL
+#-----
+function load_spell() {
+ local SPELL=$1
+ if tablet_set_spell $SPELL ; then
+ load_functions_file
+ else
+ SCRIPT_DIRECTORY="`codex_find_spell_by_name $SPELL`"
+
+ VERSION=`installed_version $SPELL`
+ INST_LOG=$INSTALL_LOGS/$SPELL-$VERSION
+ MD5_LOG=$MD5SUM_LOGS/$SPELL-$VERSION
+ if [[ $SCRIPT_DIRECTORY ]] ; then
+ codex_set_current_spell $SCRIPT_DIRECTORY
+ load_functions_file
+ else
+ message "Spell is missing from grimoires"
+ message "unable to run any PRE or POST_REMOVE scripts it once had."
+ if ! query "Continue anyway?" y; then
+ return 1
+ fi
+ fi
+ fi
+}
+
+#-----
+## Run the PRE_REMOVE script if it exists
+#-----
+function pre_remove() {
+ if [[ $SCRIPT_DIRECTORY ]] && [ -x $SCRIPT_DIRECTORY/PRE_REMOVE ]; then
+ unset LD_PRELOAD
+ . $SCRIPT_DIRECTORY/PRE_REMOVE
+ fi
+}
+
+#-----
+## Run the POST_REMOVE script if it exists.
+#-----
+function post_remove() {
+ # what is the point of this?
+ LD_PRELOAD_OLD="$LD_PRELOAD"
+ unset LD_PRELOAD
+
+ export LD_PRELOAD="$LD_PRELOAD_OLD"
+
+ if [[ $SCRIPT_DIRECTORY ]] && [ -x $SCRIPT_DIRECTORY/POST_REMOVE ]
+ then . $SCRIPT_DIRECTORY/POST_REMOVE
+ fi
+
+}
+
+
+
+
+#---------------------------------------------------------------------
+## Dispel a spell
+#---------------------------------------------------------------------
+
+function dispel_spell() {
+
+ local SPELL=$1
+ # fake dispel, only remove sorcery metadata
+ if [[ $DEBUG_DISPEL ]] ; then
+ echo "pretending to dispel $SPELL"
+ load_spell $SPELL &&
+ remove_spell $SPELL &&
+ reap_depends &&
+ message "${DISPEL_COLOR}Partly dispelled spell:" \
+ "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}"
+ return
+ fi
+
+ if dispel_sustained || dispel_not_possible ; then
+ DISPEL_EXIT_STATUS=1
+ return 1
+ fi
+
+ [[ $TRIGGER == off ]] || trigger "pre_dispel"
+
+ (
+ # this function messes with the environment a lot leave it in a subshell
+ load_spell $SPELL &&
+ pre_remove &&
+ reaper $INST_LOG $MD5_LOG &&
+ post_remove &&
+ reap_state_files $INST_LOG &&
+ remove_spell $SPELL &&
+ { [[ $TRIGGER == off ]] || trigger "dispel";} &&
+ remove_triggers $SPELL &&
+ message "${DISPEL_COLOR}Dispelled spell:" \
+ "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}" &&
+ activity_log "dispel" "$SPELL" "$VERSION" "success"
+ ) &&
+ reap_depends
+}
+
+#---------------------------------------------------------------------
+## @param default value
+## Set one of always, ask_yes, ask_no, and ignore to on based
+## on the value of default, leave the remaining as off.
+#---------------------------------------------------------------------
+function dispel_depends_value_to_items() {
+ always=off
+ ask_yes=off
+ ask_no=off
+ ignore=off
+ case $1 in
+ always) always=on ;;
+ ask-yes) ask_yes=on ;;
+ ask-no) ask_no=on ;;
+ ignore) ignore=on ;;
+ *) ignore=on ;;
+ esac
+}
+
+
+#---------------------------------------------------------------------
+## @param Title of the menu
+## @param Name of the parameter being set
+## @param Default value of the parameter being set
+##
+## Present a radiolist menu with the quad-options for one of various
+## dependency following options.
+#---------------------------------------------------------------------
+function dispel_depends_menu_template() {
+ local TITLE=$1
+ local DEFAULT_NAME=$2
+ local DEFAULT=$3
+ local always ask_yes ask_no ignore rc
+ dispel_depends_value_to_items $DEFAULT
+ RESULT=`eval $DIALOG ' --title "$TITLE" \
+ --radiolist \
+ "" \
+ 0 0 0 \
+ always "" $always \
+ ask-yes "" $ask_yes \
+ ask-no "" $ask_no \
+ ignore "" $ignore'`
+ rc=$?
+ if [[ $rc == 0 ]] ; then
+ # remove spurious ""
+ RESULT=`echo "${RESULT}" | sed -e 's/^"//' -e 's/"$//'`
+ modify_local_config $DEFAULT_NAME "$RESULT"
+ eval "$DEFAULT_NAME=\"$RESULT\""
+ fi
+}
+
+#---------------------------------------------------------------------
+## Present menus for each of the four dependency following options.
+#---------------------------------------------------------------------
+function dispel_depends_defaults_menu() {
+ dispel_depends_menu_template \
+ "Default action for orphaned spells" \
+ "ORPHAN_MENU_DEFAULT" "$ORPHAN_MENU_DEFAULT"
+ dispel_depends_menu_template \
+ "Default action for non-orphaned spells" \
+ "NONORPHAN_MENU_DEFAULT" "$NONORPHAN_MENU_DEFAULT"
+ dispel_depends_menu_template \
+ "Default action for recasting repairable parents spells" \
+ "RECAST_PARENT_MENU_DEFAULT" "$RECAST_PARENT_MENU_DEFAULT"
+ dispel_depends_menu_template \
+ "Default action for broken parent spells" \
+ "DISPEL_PARENT_MENU_DEFAULT" "$DISPEL_PARENT_MENU_DEFAULT"
+
+}
+
+#---------------------------------------------------------------------
+##
+## 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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libdownload b/var/lib/sorcery/modules/libdownload
new file mode 100644
index 0000000..bad3157
--- /dev/null
+++ b/var/lib/sorcery/modules/libdownload
@@ -0,0 +1,229 @@
+#!/bin/bash
+#-------------------------------------------------------------------------
+##
+## @Synopsis Frontend interface for dl_handlers
+##
+## This file contains functions for accessing download handlers.
+## Download handlers handle the physical acquiring of files as opposed to
+## the more abstract process of parsing and manipulating urls.
+## A url handler will specify a download handler for a given url.
+##
+## dl handlers may call back to url handlers to have a url parsed
+## it is up to the dl handler to interprit the results of the url
+## parsing as it may be different for each type of url.
+##
+## dl handlers are responsible for connecting to the internet if they
+## need to.
+##
+## WRITING NEW DL HANDLERS
+## Current dl handlers only need a single function
+## "dl_get_<handler>" This function takes the following arguments:
+## $target
+## $url_list List of urls to get the target from
+## $hints Hints, these help the function determine what
+## type of thing it is downloading or other tunables.
+## $dl_target Name of variable in which to store the name of the
+## result directory or file
+## $dl_type Name of the variable in which to store the type of
+## thing downloaded
+##
+##
+## The target parameter is advisory, it is a hint at what might be downloaded.
+##
+## The hints value is a list, values of interest to a particular
+## dl_handler should be prefixed "dl_<handler>", the handler may define
+## any hints which it see's necessary.
+## There are also well defined hints: "tree" "file" these indicate that the
+## type of thing being downloaded is probably a tree or file despite
+## what we may think.
+##
+## The basic form of a dl handler's get function is simply a loop over each
+## url until one successfully downloads, it should then set the target and
+## type reference variable successfully.
+##
+## @Copyright Copyright 2002 by the Source Mage Team
+## @Copyright Copyright 2005 by the Source Mage Team
+##
+#-------------------------------------------------------------------------
+
+if ! [[ $DL_HANDLER_FILES ]] ; then
+ DL_HANDLER_FILES=`ls $SGL_LIBRARY_MODULES/dl_handlers/dl_*[^~]`
+ for dl_handler_file in $DL_HANDLER_FILES; do
+ [ -x $dl_handler_file ] && . $dl_handler_file
+ done
+fi
+
+
+#-------------------------------------------------------------------------
+##
+## Download a specified group of urls, they are assumed to all be understood
+## by the specified handler.
+##
+## @Param type Name of handler
+## The remainder of the arguments are target, url_list, hints, dl_target
+## and dl_type. See above for information.
+##
+##
+#-------------------------------------------------------------------------
+
+function dl_get_bucket() {
+ $STD_DEBUG
+ local handler=$1
+ local target=$2
+ local url_list=$3
+ local hints=$4
+ local dl_target=$5
+ local dl_type=$6
+
+ if [ "$handler" == "svns" ]
+ then
+ local handler=svn
+ fi
+
+ if ! [[ "$handler" ]] ; then
+ message "Missing handler, this is probably a sorcery bug"
+ return 255
+ elif ! misc_is_function dl_${handler}_get; then
+ message "$handler is not a valid handler, this is probably a sorcery bug"
+ return 255
+ fi
+
+ dl_${handler}_get "$target" "$url_list" "$hints" "$dl_target" "$dl_type"
+ rc=$?
+ if [[ $rc == 0 ]] ; then
+ debug "libdownload" "dl_handler -- downloaded $url"
+ eval debug "\$$dl_target \$$dl_type"
+ return 0
+ fi
+ return 1
+}
+
+function dl_command_check() {
+ local cmd
+ smgl_which $1 cmd
+ if ! [[ $cmd ]] || ! test -x $cmd ; then
+ message "${PROBLEM_COLOR}$cmd is not installed, please cast it..." \
+ "${DEFAULT_COLOR}"
+ return 1
+ fi
+}
+
+
+#-------------------------------------------------------------------------
+## this nonsense is to get us connected to the outside world
+## it should eventually be a seperate, userdefinable script, with
+## this as the default of course
+#-------------------------------------------------------------------------
+function dl_connect() {
+
+ debug "libdownload" "connect()"
+
+ # We agree that while this lock is held, no other script will be
+ # connecting or disconnecting internet service out from under us
+ lock_resources internet signon
+ for interface in $INTERNET_INTERFACES; do
+ if ifconfig | grep -q "^$interface" ; then
+ debug "libdownload" "connected through $interface"
+ unlock_resources internet signon
+ return
+ fi
+ done
+
+
+ if $CONNECT_SCRIPT ; then
+ message "${PROBLEM_COLOR}Cannot find or initiate network connection"
+ message "${DEFAULT_COLOR}"
+ message "Please setup your network connection."
+ message "Try netconf if you use ethernet (with dsl), or see man pppd"
+ message "for dialup.\n"
+ message "If you still have trouble and have another way of getting"
+ message "online, please come by #sourcemage on irc.freenode.net and"
+ message "someone will help you."
+ return 1
+ fi
+
+ # Timeout in deciseconds to wait for Interface to come up.
+ local TIMEOUT=30
+
+ until ifconfig | grep -q eth ||
+ ifconfig | grep -q atml ||
+ ifconfig | grep -q ppp ||
+ [ $TIMEOUT == 0 ]
+ do
+ sleep 10
+ (( TIMEOUT-- ))
+ done
+ counter_up internet counter
+ unlock_resources internet signon
+}
+
+#-------------------------------------------------------------------------
+## this clever function disconnects us if we aren't downloading something
+## the heuristic is rather poor.
+#-------------------------------------------------------------------------
+function dl_disconnect() {
+ local i_counter=/tmp/sorcery/i_counter
+
+ # dont bother disconnecting if we didnt have to connect
+ if test $(counter_check internet counter) -lt 1 ; then
+ return
+ fi
+
+ counter_down internet counter
+ disconnect_script /tmp/sorcery/disconnect.$$
+ bash /tmp/sorcery/disconnect.$$ &
+}
+
+
+
+function disconnect_script() {
+cat > $1 << EOF
+
+. /etc/sorcery/config
+# return if a script is already running
+trylock_resources internet disconnect || exit
+let i=0
+#
+# the following loop polls the "internet counter" counter
+# if after some number of pollings the counter remains consistently 0
+# assume that no one is going to use the connect and we disconnect
+while :; do
+ if trylock_resources internet signon &&
+ test \$(counter_check internet counter) -lt 1 ; then
+ let i++
+ if test \$i -gt 10; then
+ DISCONNECT=yes
+ counter_reset internet counter
+ fi
+ else
+ let i=0
+ fi
+ if [[ \$DISCONNECT ]] ; then
+ \$DISCONNECT_SCRIPT
+ rm $0
+ unlock_resources internet signon
+ exit $?
+ else
+ unlock_resources internet signon
+ fi
+ sleep 10
+done
+EOF
+}
+
+#---------------------------------------------------------------------
+## 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
+#---------------------------------------------------------------------
+
diff --git a/var/lib/sorcery/modules/libgcc2 b/var/lib/sorcery/modules/libgcc2
new file mode 100644
index 0000000..d669eed
--- /dev/null
+++ b/var/lib/sorcery/modules/libgcc2
@@ -0,0 +1,122 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## @Synopsis Set of functions for dealing with the problem (hopefully a
+## @Synopsis temporary problem) of having to use both the gcc2 and gcc3
+## @Synopsis compilers.
+##
+## The migration to gcc3 will take place in the following steps:
+## <pre>
+## - The gcc spell will be updated to gcc 3
+## - gcc2 will be a new spell that installs gcc2 in /opt/gcc2.
+## - The gcc spell will have gcc2 as a dependency.
+## - Spells that will only compile with gcc3 will have a USEGCC2 file
+## in the spell directory.
+## - Sorcery will test for the existance of the USEGCC2 flag in a
+## spell's directory. If it's there, it will alter the environment
+## so that gcc2 is used for building the spell.
+## </pre>
+##
+## @Implementation These functions were added to allow the use of both the gcc2 and
+## @Implementation gcc3 compiler at the same time within sorcery. The code can be
+## @Implementation removed once only one compiler is supported.
+##
+##
+## @Copyright Copyright 2002 by the Source Mage Team
+##
+##
+#---------------------------------------------------------------------
+
+
+#---------------------------------------------------------------------
+## @param gcc version (2.95. or 3.3.)
+## @return 0 if gcc is being used to compile
+## @return 1 otherwise
+#---------------------------------------------------------------------
+function use_gcc() {
+
+ gcc -dumpversion | grep -q $(esc_str $1) > /dev/null
+
+}
+
+#---------------------------------------------------------------------
+## @return 0 if gcc 2.95 is being used to compile
+## @return 1 otherwise
+#---------------------------------------------------------------------
+function use_gcc2() {
+
+ use_gcc 2.95.
+
+}
+
+
+#---------------------------------------------------------------------
+## @param new path to add
+## @param old path
+## @Stdout new path
+## Echos the old path with new path prepended, separated with a ':'
+#---------------------------------------------------------------------
+function gcc2_prepend_path() {
+
+ if test -z $2
+ then echo $1
+ else echo $1:$2
+ fi
+
+}
+
+
+#---------------------------------------------------------------------
+## @param path
+##
+## Alters the environment so that gcc2 will be used for compiling and
+## building spells when the "gcc" command is used.
+##
+#---------------------------------------------------------------------
+function gcc2_add_paths() {
+
+ export PATH=$( gcc2_prepend_path $1/bin $PATH )
+ export LD_LIBRARY_PATH=$( gcc2_prepend_path $1/lib $LD_LIBRARY_PATH )
+ export LD_RUN_PATH=$( gcc2_prepend_path $1/lib $LD_RUN_PATH )
+ export INFOPATH=$( gcc2_prepend_path $1/info $INFOPATH )
+ export CPPFLAGS="-I $1/include $CPPFLAGS"
+
+}
+
+
+#---------------------------------------------------------------------
+##
+## Determines if gcc2 should be used for a spell. If so, it alters
+## the environment so that gcc2 is used for compilation.
+##
+#---------------------------------------------------------------------
+function invoke_gcc2() {
+
+ USEGCC2=$( ls $SCRIPT_DIRECTORY/USEGCC2 2>/dev/null )
+
+ if [ "$USEGCC2" ]
+ then gcc2_add_paths /opt/gcc2
+ fi
+
+ echo "Using gcc version: `gcc -dumpversion`"
+
+}
+
+
+#---------------------------------------------------------------------
+##
+## 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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libgpg b/var/lib/sorcery/modules/libgpg
new file mode 100644
index 0000000..1563188
--- /dev/null
+++ b/var/lib/sorcery/modules/libgpg
@@ -0,0 +1,447 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## @Synopsis Functions that verify gpg signatures
+##
+## @Copyright Copyright 2005 by the Source Mage Team
+##
+#---------------------------------------------------------------------
+
+
+
+#---------------------------------------------------------------------
+## Low level routine for verifying a file given a signature and keyring.
+## The keyring must contain the public key for the signature.
+## @param signature of the file
+## @param file to verify
+## @param public keyring
+##
+## @return 0 on success, non-zero on failure:
+## 1: verification failure
+## 3: no signature file
+## 4: no file to verify
+## 5: no keyring
+## 200: gpg isnt installed
+##
+## @stdout message when gpg is not installed
+##
+#---------------------------------------------------------------------
+function gpg_verify_signature() { # $1 sig $2 file $3 pubring $4 algo var
+
+ local signature=$1
+ local file=$2
+ local keyring=$3
+ local algo=""
+
+ test -f $signature || return 3
+ test -f $file || return 4
+ test -f $keyring || return 5
+
+ # always trust and supply our own keyring.
+ # We provide our own trust for the pubkey validity.
+
+ local GPGPROG
+ smgl_which gpg GPGPROG 2> /dev/null
+
+ if test -z "$GPGPROG" ; then
+ message "It appears you do not have gpg (gnupg) in your PATH."
+ message "${QUERY_COLOR}For full source verification, it is highly" \
+ "suggested that you cast gnupg\nas soon as possible. This" \
+ "should be done for you on a system update.${DEFAULT_COLOR}"
+ return 200
+ else
+ gpg --no-default-keyring \
+ --always-trust \
+ --keyring $keyring \
+ --batch \
+ --verbose \
+ --verify \
+ $signature \
+ $file 2>/dev/null || return 1
+ algo=$( gpg --no-default-keyring \
+ --always-trust \
+ --keyring $keyring \
+ --batch \
+ --verbose \
+ --verify \
+ $signature \
+ $file 2>&1 | grep 'digest algorithm' | awk '{ print $NF }' |
+ tr A-Z a-z)
+ fi
+ [ ! -z "$4" ] && eval "$4=\"$algo\""
+ return 0
+}
+
+#---------------------------------------------------------------------
+## Get the sorcery gpg key file associated with a branch
+## @param (optional) sorcery branch, if empty use $SORCERY_BRANCH
+## @return 0 on success, 1 on failure
+## @stdout full path to sorcery key (if successful)
+#---------------------------------------------------------------------
+function gpg_get_sorcery_key() {
+ local branch=${1:-$SORCERY_BRANCH}
+ local key=$GPG_KEY_DIR/sorcery-$branch.gpg
+ test -f $key || return 1
+ echo $key
+ return 0
+}
+
+#---------------------------------------------------------------------
+## Get the grimmoire gpg key file associated with a branch
+## @param grimoire branch (test, stable, games etc.)
+## @return 0 on success, 1 on failure
+## @stdout full path to grimoire key (if successful)
+#---------------------------------------------------------------------
+function gpg_get_grimoire_key() {
+ local branch=$1
+ local key=$GPG_KEY_DIR/grimoire-$branch.gpg
+ test -f $key || return 1
+ echo $key
+ return 0
+}
+
+#---------------------------------------------------------------------
+## Verify a grimoire tarball's gpg signature
+## @param file on local disk to verify
+## @param url from which to get the signature
+## @param (optional) grimoire branch, if derive it from the filename with
+## ${SOURCE%%*.}
+## @param (optional) signature file, if empty download $SOURCE.$GPG_SIG_EXT
+## from $2
+## @return 0 on success, non-zero on failure:
+## 1: verification failed
+## 2: verification is disabled
+## 254: no keyring found
+## 255: could not download signature
+## anything else see gpg_verify_signature
+##
+## @stdout possibly a failure message depending on what happens (nothing
+## is output on success)
+#---------------------------------------------------------------------
+function gpg_verify_grimoire() {
+ if [[ "$GPG_VERIFY_GRIMOIRE" != on ]] ; then
+ return 2
+ fi
+
+ local FILENAME=$1
+ local SOURCE_URL=$2
+
+
+ # optional args
+ local BRANCH=$3
+ local SIGNATURE=$4
+
+ local SOURCE=$(basename $FILENAME)
+
+ test -z $BRANCH && BRANCH=${SOURCE%%.*}
+
+ local gpg_pub_key=$(gpg_get_grimoire_key $BRANCH)
+ if test -z $gpg_pub_key && test -f $gpg_pub_key ; then
+ message "No keyring found! (maybe you need to cast sorcery-pubkeys?)"
+ return 254
+ fi
+ gpg_verify_common $FILENAME $SOURCE_URL $gpg_pub_key grimoire $SIGNATURE
+}
+
+
+
+function verify_grimoire_tree() {
+ local grimoire_name=$1
+ local grimoire_dir=$2
+
+ if [[ "$GPG_VERIFY_GRIMOIRE" != on ]] ; then
+ return 253
+ fi
+
+ local gpg_pub_key=$(gpg_get_grimoire_key $grimoire_name)
+ if test -z $gpg_pub_key && test -f $gpg_pub_key ; then
+ message "No keyring found! (maybe you need to cast sorcery-pubkeys?)"
+ return 254
+ fi
+
+ manifest=$grimoire_name.manifest.$GRIMOIRE_MANIFEST_ALGORITHM
+ manifest_url=${CODEX_MANIFEST_URL}/$manifest
+
+ pushd $TMP_DIR >/dev/null || return 1
+
+ local manifest_target manifest_type
+ url_download $manifest $manifest_url "" manifest_target manifest_type
+ #check the success of the download
+ if [ $? != 0 ] || [[ "$manifest_type" != file ]] ; then
+ message "Error downloading manifest..."
+ return 1
+ fi
+
+ gpg_verify_common $manifest $CODEX_MANIFEST_URL $gpg_pub_key "grimoire manifest"
+ if ! gpg_user_query $?; then
+ return 2
+ fi
+
+ popd >/dev/null
+
+ verify_grimoire_against_manifest "$grimoire_dir" "$TMP_DIR/$manifest" \
+ "$GRIMOIRE_MANIFEST_ALGORITHM"
+}
+
+#---------------------------------------------------------------------
+## Verify a sorcery tarball's gpg signature
+## @param file on local disk to verify
+## @param url from which to get the signature
+## @param (optional) signature file, if empty download $SOURCE.$GPG_SIG_EXT
+## from $2
+##
+## @return 0 on success, non-zero on failure:
+## 1: verification failed
+## 2: verification is disabled
+## 254: no keyring found
+## 255: could not download signature
+## anything else see gpg_verify_signature
+##
+## @stdout possibly a failure message depending on what happens (nothing
+## is output on success)
+#---------------------------------------------------------------------
+function gpg_verify_sorcery() {
+ if [[ "$GPG_VERIFY_SORCERY" != on ]] ; then
+ return 2
+ fi
+
+ local FILENAME=$1
+ local SOURCE_URL=$2
+
+ # optional args
+ local SIGNATURE=$3
+
+ local gpg_pub_key=$(gpg_get_sorcery_key)
+ if test -z $gpg_pub_key && test -f $gpg_pub_key ; then
+ message "No keyring found! (maybe you need to cast sorcery-pubkeys?)"
+ return 254
+ fi
+ gpg_verify_common "$FILENAME" "$SOURCE_URL" "$gpg_pub_key" "sorcery" "$SIGNATURE"
+}
+
+#---------------------------------------------------------------------
+## Common code for verifying sorcery/grimoire tarballs
+## @param file on local disk to verify
+## @param url from which to get the signature
+## @param keyring to verify with
+## @param grimoire or sorcery, whatever it is thats being verified (used in
+## an output message
+## @param (optional) signature file, if empty download $SOURCE.$GPG_SIG_EXT
+## from $2
+##
+## @return 0 on success, non-zero on failure:
+## 1: verification failed
+## 255: could not download signature
+## anything else see gpg_verify_signature
+##
+## @stdout possibly a failure message depending on what happens (nothing
+## is output on success)
+#---------------------------------------------------------------------
+function gpg_verify_common() {
+ # download the signature
+ local FILENAME=$1
+ local SOURCE_URL=$2
+ local KEYRING=$3
+ local REASON=$4
+ local SIGNATURE=$5
+ local SOURCE=$(basename $FILENAME)
+
+ local SIG_FILE=${SOURCE}.${GPG_SIG_EXT}
+ pushd $TMP_DIR &>/dev/null ||
+ { message "Failed to cd to $TMP_DIR!!"; return 2;}
+ if test -z "$SIGNATURE" ; then
+ local SIG_URL=$SOURCE_URL/$SIG_FILE
+ local gpg_target gpg_type
+ url_download "$SIG_FILE" "$SIG_URL" "file" gpg_target gpg_type &&
+ [[ $gpg_type == file ]] ||
+ {
+ message "Failed to get gpg signature! verification is impossible"
+ return 255
+ }
+ [[ "$gpg_target" != $SIG_FILE ]] && mv "$gpg_target" "$SIG_FILE"
+ else
+ cp $SIGNATURE $TMP_DIR/$SIG_FILE
+ fi
+
+ gpg_verify_signature $TMP_DIR/$SIG_FILE $FILENAME $gpg_pub_key
+ rc=$?
+ rm $TMP_DIR/$SIG_FILE
+ popd &>/dev/null
+
+ return $rc
+}
+
+#---------------------------------------------------------------------
+## Handles interpriting the output of gpg_verify_sorcery or
+## gpg_verify_grimoire.
+##
+## @param return code of gpg_verify_sorcery or gpg_verify_grimoire
+## @return 0 if the program should continue, 1 if not
+##
+## @stdout Some message thats supposed to inform the user of whats
+## going on, or possibly a query asking the user if they want
+## to continue even though gpg verification failed.
+#---------------------------------------------------------------------
+function gpg_user_query() {
+ local rc=$1
+ if [[ $rc == 0 ]] ; then
+ message "${MESSAGE_COLOR}gpg signature verified!${DEFAULT_COLOR}"
+ elif [[ $rc == 2 ]] ; then
+ message "${MESSAGE_COLOR}gpg verification is disabled${DEFAULT_COLOR}"
+ else
+ message "${PROBLEM_COLOR}Failure to verify gpg signature${DEFAULT_COLOR}"
+ case "$3" in
+ grimoire)
+ if list_find "$GPG_GRIMOIRE_LIST" $2 > /dev/null 2>&1 ; then
+ query "Continue anyway?" n || return 1
+ else
+ # if its not one of our grimoires may want the default to be y
+ query "Continue anyway?" y || return 1
+ fi
+ ;;
+ spell)
+ unpack_file_user_query $rc || return 1
+ ;;
+ *)
+ query "Continue anyway?" n || return 1
+ ;;
+ esac
+ fi
+ return 0
+}
+
+
+#---------------------------------------------------------------------
+## @param algorithm to use
+## @param file to get hashsum of
+## @out output is exactly the same format as md5sum/sha1sum, just with
+## a different hashsum. "hashsum<space><space>filename". The hashsum is
+## printed with all lowercase letters.
+##
+## This assumes that the caller has already verified that gpg is
+## installed and supports the specified hash function.
+##
+#---------------------------------------------------------------------
+function gpg_hashsum() {
+ local algorithm=$1
+ local file=$2
+ gpg --print-md "$algorithm" "$file"| tr -d '\n ' |cut -f2 -d:|
+ tr 'A-F' 'a-f'|tr -d '\n'
+ echo " $file"
+}
+
+#---------------------------------------------------------------------
+## @out All the hash algorithms supported by gpg, algorithms printed in
+## lower case.
+##
+## This assumes the caller has already verified that gpg is installed.
+#---------------------------------------------------------------------
+function gpg_get_hashes() {
+ gpg --version 2> /dev/null | grep '^Hash' | awk -F: '{ print $2 }' | tr , ' ' | tr 'A-Z' 'a-z'
+}
+
+#---------------------------------------------------------------------
+## Verify a tree against a manifest file
+## @param directory to verify
+## @param manifest file, the format is like what the md5sum tool would
+## produce
+## @param algorithm to use, this can be anything supported by gpg
+## @param regular expression of files to ignore
+#---------------------------------------------------------------------
+function verify_against_manifest() {
+ local dir=$1
+ local manifest=$2
+ local algorithm=$3
+ local ignore=$4
+ local base=$(basename $dir)
+ local real_list=$TMP_DIR/$base
+ local missing=$TMP_DIR/missing.$base
+ local rc=0
+
+ message "Validating tree at $dir with $manifest"
+ local manifest_format=$(echo $manifest|awk -F. '{print $NF}')
+
+ pushd $dir > /dev/null || return $?
+ find . -type f > $real_list
+ { cat $real_list $real_list ; awk '{print $NF}' $manifest ; } |
+ sort | uniq -c | grep -v '^ *3' | grep -v "$ignore" > $missing
+ NO_TREE=$(grep "^ *1" $missing|sed 's/^ *1 //')
+ if [[ $NO_TREE ]] ; then
+ message "${PROBLEM_COLOR}The following exist only in the manifest" \
+ "and are missing from the tree!${DEFAULT_COLOR}"
+ echo "$NO_TREE"|$PAGER
+ let rc+=1
+ fi
+ NO_MANIFEST=$(grep "^ *2" $missing|sed 's/^ *2 //')
+ if [[ $NO_MANIFEST ]] ; then
+ message "${PROBLEM_COLOR}The following exist only in the tree" \
+ "and are missing from the manifest!${DEFAULT_COLOR}"
+ echo "$NO_MANIFEST"|$PAGER
+ let rc+=1
+ fi
+ local hash r
+ while read hashsum file; do
+ local hash=$(gpg_hashsum $algorithm $file 2>/dev/null|cut -f1 -d' ')
+ r=$?
+ if [[ $hash != $hashsum ]] || [[ $r != 0 ]]; then
+ NOT_OK=( $NOT_OK "$file" )
+ fi
+ done < $manifest
+
+ if [[ $NOT_OK ]] ; then
+ message "${PROBLEM_COLOR}The following have bad checksums${DEFAULT_COLOR}"
+ echo "$NOT_OK"
+ let rc+=1
+ fi
+ popd $dir > /dev/null
+ return $rc
+}
+
+#---------------------------------------------------------------------
+## Verify a grimoire tree and ignore files sorcery adds post-download
+#---------------------------------------------------------------------
+function verify_grimoire_against_manifest() {
+ verify_against_manifest $1 $2 $3 \
+ '^ *2 \./\(GRIMOIRE\|codex\.index\|provides\.index\)$'
+}
+
+#---------------------------------------------------------------------
+## Ask the user what they want to do if verification of a grimoire tree
+## fails.
+#---------------------------------------------------------------------
+function grimoire_tree_user_query() {
+ local grimoire_name=$1
+ message "${PROBLEM_COLOR}Verification of the grimoire" \
+ "tree ${DEFAULT_COLOR}${SPELL_COLOR}$grimoire_name" \
+ "${DEFAULT_COLOR}${PROBLEM_COLOR}failed!"
+ message "What would you like to do?${DEFAULT_COLOR}"
+ local choice
+ select_list choice 0 "set aside" "remove" "ignore"
+ case "$choice" in
+ "set aside") local tgt=$grimoire_name.$(date +%Y%m%d%H%M).corrupt
+ message "moving grimoire to $tgt"
+ mv $grimoire_name $tgt
+ scribe_remove $grimoire_name &>/dev/null
+ ;;
+ remove) rm -rf $grimoire_name
+ scribe_remove $grimoire_name &>/dev/null;;
+ ignore) return 0 ;;
+ esac
+ return 1
+}
+
+#---------------------------------------------------------------------
+## 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
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libgrimoire b/var/lib/sorcery/modules/libgrimoire
new file mode 100644
index 0000000..03bad06
--- /dev/null
+++ b/var/lib/sorcery/modules/libgrimoire
@@ -0,0 +1,281 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## @Libgrimoire
+##
+## @Synopsis Set of functions containing the spell writing API.
+##
+##
+## These functions can be used in the PRE_BUILD, BUILD, POST_BUILD
+## and POST_INSTALL sections of spells.
+##
+## @Copyright
+## Original version Copyright 2001 by Kyle Sallee
+## Additions/Corrections Copyright 2002 by the Source Mage Team
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## @Type API
+## @param directory name
+## @param [size]
+## Creates a tmpfs filesystem. By default, the size is 1GB.
+## The caller may optionally supply a size argument.
+## <pre>
+## Example1: Create a 2GB mount at $SOURCE_DIRECTORY
+##
+## mk_source_dir $SOURCE_DIRECTORY 2g
+##
+## Example2: Create a mount at /tmp/newdir, defaults to 1GB size
+##
+## mk_source_dir /tmp/newdir
+## </pre>
+#---------------------------------------------------------------------
+function real_mk_source_dir() {
+
+ debug "libgrimoire" "Running mk_source_dir() on $SOURCE_DIRECTORY"
+
+ local NEW_DIR=$1
+ local NEW_DIR=${NEW_DIR:=$SOURCE_DIRECTORY}
+
+ local SIZE=$2
+ local SIZE=${SIZE:=1g}
+
+ if [ -n "$NEW_DIR" ]; then
+
+ rm_source_dir $NEW_DIR
+ mkdir $NEW_DIR &&
+ if [[ $TMPFS == on ]]; then
+ mount -o size=$SIZE,nr_inodes=1m -t tmpfs tmpfs $NEW_DIR
+ fi
+ fi
+
+}
+
+
+#---------------------------------------------------------------------
+## @param [directory to remove]
+## @Globals SOURCE_DIRECTORY
+## Removes the given directory or SOURCE_DIRECTORY if no argument is
+## given.
+##
+#---------------------------------------------------------------------
+function real_rm_source_dir() {
+
+ DEAD_DIR=$1
+ DEAD_DIR=${DEAD_DIR:-$SOURCE_DIRECTORY}
+
+ debug "libgrimoire" "Running rm_source_dir() on $DEAD_DIR"
+
+ if [ -n "$DEAD_DIR" ] && [ -d "$DEAD_DIR" ]; then
+
+ pushd $BUILD_DIRECTORY 2>&1 > /dev/null
+
+ umount -l $DEAD_DIR 2> /dev/null
+
+ # We don't really want to use "rm -rf", but if not using tmpfs,
+ # we have to. So we move the old dir to a known place before
+ # running rm. This prevents accidental damage that could be
+ # caused by
+ # a) $DEAD_DIR being an empty string
+ # b) $DEAD_DIR being set to "/" (or anything above /usr/src)
+ # c) $DEAD_DIR containing metacharacters (like newline)
+ mv "$DEAD_DIR" "$INSTALL_ROOT/usr/src/deaddir" &&
+ rm -rf "$INSTALL_ROOT/usr/src/deaddir" 2> /dev/null
+
+ popd 2>&1 > /dev/null
+ true
+
+ fi
+
+}
+
+
+#---------------------------------------------------------------------
+##
+## Override calls to the make program. Used to add custom arguments and
+## handle failures when using multiple jobs.
+##
+#---------------------------------------------------------------------
+function make() {
+
+ local JOBS=""
+
+ local make_njobs=${MAKE_NJOBS:-1}
+ local jobs_per_host=${JOBS_PER_HOST:-0}
+ local tmp num_hosts=0
+ for tmp in $DISTCC_HOSTS; do let num_hosts+=1; done
+ local JOBS
+
+ # if the parent didnt define this, define it here
+ if [[ ! "$REAL_MAKE" ]] ; then
+ local REAL_MAKE
+ find_make REAL_MAKE || return $?
+ fi
+
+
+ if [[ "$USE_DISTCC" == on ]] ; then
+ let JOBS=$(($make_njobs+$jobs_per_host*$num_hosts))
+ else
+ let JOBS=$make_njobs
+ fi
+
+ if [[ $JOBS == 0 ]]; then
+ # Zero jobs has the effect of unlimiting the number of make processes
+ JOBS=""
+ else
+ # Check there is a reasonable value.
+ [ $JOBS -lt 0 ] ||
+ echo $JOBS|grep -q '[0-9]+' &&
+ JOBS="1" # Use default of one when MAKE_NJOBS is nonsense.
+ fi
+
+ debug "libgrimoire" "make: running with $JOBS jobs"
+ $REAL_MAKE -j $JOBS -S "$@"
+
+ # Cache the result
+ local STATUS=$?
+
+ if [[ $STATUS -ne 0 ]] && [[ "$JOBS" != 1 ]] ; then
+ query "Running make with $JOBS jobs failed. Attempt to run with a single job?" y &&
+ # Try again
+ $REAL_MAKE -j1 -S "$@"
+ STATUS=$?
+ fi
+
+ # Return the exit status
+ return $STATUS
+
+}
+
+#---------------------------------------------------------------------
+## Install files in a spells "desktop" subdirectory for .desktop
+## @param Spell
+## @Stdout file name
+#---------------------------------------------------------------------
+function install_desktop_files() {
+ debug "libgrimoire" "Running install_desktop_files() on $SPELL"
+ local each file
+ local target_dir="/usr/share/applications"
+ local desktop_dir="$SCRIPT_DIRECTORY/desktop"
+ test -d $desktop_dir || return 0
+
+ mkdir -p $target_dir
+ for each in $(find $desktop_dir -maxdepth 1 -type f); do
+ file=$(basename $each)
+ if ! test -f "$target_dir/$file" ; then
+ debug "libgrimoire" "Installing $file in $target_dir as a desktop file"
+ cp $each $target_dir
+ fi
+ done
+}
+
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell
+## Returns the current version of the given spell
+##
+#---------------------------------------------------------------------
+function real_installed_version() {
+
+ local spell="$1"
+
+ grep "^$spell:" $SPELL_STATUS | cut -d : -f4 | head -n 1
+
+}
+
+
+#---------------------------------------------------------------------
+## @param spell
+## @param default answer to dispel query
+## @Type API
+## If the default answer is anything other than 'y' then 'n' is assumed.
+## returns the given spellname if it is installed
+##
+#---------------------------------------------------------------------
+function real_conflicts() {
+
+ debug "libgrimoire" "Running conflicts() on $1. Default query answer $2."
+
+ if spell_installed $1; then
+ [ "$2" = y ] && echo "$1:y" || echo "$1:n"
+ fi
+
+ true
+
+}
+
+
+#---------------------------------------------------------------------
+## @Stdout Warning messages.
+## Provides a neatly formatted rejection dialog for the
+## various rejected spells that might require user warnings.
+## See ask_continue_with_rejected for second part of this function.
+##
+#---------------------------------------------------------------------
+function warn_rejected()
+{
+ message "${MESSAGE_COLOR}This spell is considered${PROBLEM_COLOR}" \
+ "rejected${DEFAULT_COLOR}${MESSAGE_COLOR} because of the" \
+ "following reason:\n"
+ message "${PROBLEM_COLOR}$REJECT${DEFAULT_COLOR}${MESSAGE_COLOR}.\n"
+ message "Please view the software website for more information:\n"
+ message "${DEFAULT_COLOR}$WEB_SITE${MESSAGE_COLOR}\n"
+ message "You may continue casting the spell and it will still be tracked" \
+ "by Sorcery.\nHowever, the software installation process may have" \
+ "questions that need to be\nanswered and/or licensing agreements" \
+ "that must be agreed to.${DEFAULT_COLOR}\n"
+}
+
+
+#---------------------------------------------------------------------
+## @Stdout Question
+## @Stdin Answer ;-)
+## Part two of the warn_rejected funtion, ask if user wants to continue
+## anyway. (defaults to NO), unless running in UNATTEND_SAFE mode.
+##
+#---------------------------------------------------------------------
+function ask_continue_with_rejected()
+{
+ if spell_installed "$SPELL" && [ -e $SCRIPT_DIRECTORY/UNATTEND_SAFE ]; then
+ message "${SPELL_COLOR}$SPELL${DEFAULT_COLOR}${MESSAGE_COLOR} is" \
+ "installed, and has been determined" \
+ "to be ${FILE_COLOR}safe\nfor unattended" \
+ "update${DEFAULT_COLOR}${MESSAGE_COLOR}, so the prompt will" \
+ "be skipped.${DEFAULT_COLOR}\n"
+ return
+ fi
+ message "\n${MESSAGE_COLOR}Allowing the next question to timeout will" \
+ "choose not to install this spell.\nThis means that rejected" \
+ "spells ${PROBLEM_COLOR}will not be installed or updated" \
+ "automatically.\n"
+ message "${DEFAULT_COLOR}${MESSAGE_COLOR}If you want a rejected spell to" \
+ "be installed or updated you must\nconfirm your decision now or" \
+ "cast the spell later.${DEFAULT_COLOR}\n"
+ if ! query "CONTINUE casting?" n; then
+ return 1
+ fi
+ message "\n${MESSAGE_COLOR}OK, here we go... you are on your" \
+ "own!${DEFAULT_COLOR}"
+}
+
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libhash b/var/lib/sorcery/modules/libhash
new file mode 100644
index 0000000..83c0d78
--- /dev/null
+++ b/var/lib/sorcery/modules/libhash
@@ -0,0 +1,297 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## Set of functions for working with an associative array type data
+## structure. Values can be stored and retrieved using strings as
+## the index into the data structure instead of numbers.
+##
+## The hash data structure provided in this file allows you to store
+## values into fields of a table. The 'hash_put' function takes the
+## name of the table, a field name in the table, and the value to be
+## stored in the table. The 'hash_get' function retrieves a value from
+## the table given the table and field name.
+##
+## <pre>
+## To store a value into a field of a table, use hash_put:
+##
+## hash_put "myTable" "aField" "theValue"
+##
+## The value stored in the table can be retrieved with hash_get:
+##
+## hash_get "myTable" "aField"
+##
+## In this example, the hash_get function would echo "theValue".
+## </pre>
+## <br>
+## <p>IMPLEMENTATION NOTE</p>
+## <br>
+## Bash does not provide direct support for hash tables. These
+## functions are implemented by first building a variable using the
+## table name and field name, then using the eval function to store
+## (retrieve) value into (from) the variable.
+##
+## The idea for the hash data structure in bash was inspired by a
+## short example by Phil Howard which shows the use of hashes in bash.
+## Phil Howard's original example can be found here:
+##
+## http://www.codebits.com/bit.cfm?BitID=92
+##
+## @Copyright Copyright 2002 by the Source Mage Team
+##
+##
+#---------------------------------------------------------------------
+
+
+#---------------------------------------------------------------------
+## @param table name
+## @param field name
+## @param returning variable
+## @Type Private
+## Given a table and field name, bulds the name of
+## the variable into which a value will be stored. Also changes '+',
+## '-', and '.' in the table name into text since bash doesn't like
+## variable names with those characters.
+##
+#---------------------------------------------------------------------
+function hash_build_variable_name() {
+ local ___TABLE="$1"
+ local ___FIELD="$2"
+
+# debug "libhash" "hash_build_variable_name() - TABLE=$___TABLE:FIELD=$___FIELD"
+
+ ___TABLE=${___TABLE//\+/_P_}
+ ___TABLE=${___TABLE//\-/_M_}
+ ___TABLE=${___TABLE//\./_D_}
+ ___TABLE=${___TABLE//\:/_CLN_}
+ ___TABLE=${___TABLE// /_SPC_}
+ ___TABLE=${___TABLE//[/_OSB_}
+ ___TABLE=${___TABLE//]/_CSB_}
+
+ if [[ $___FIELD ]] ; then
+ ___FIELD=${___FIELD//\+/_P_}
+ ___FIELD=${___FIELD//\-/_M_}
+ ___FIELD=${___FIELD//\./_D_}
+ ___FIELD=${___FIELD//\:/_CLN_}
+ ___FIELD=${___FIELD// /_SPC_}
+ ___FIELD=${___FIELD//]/_CSB_}
+ ___FIELD=${___FIELD//]/_OSB_}
+ fi
+
+ # If this format is changed, modify hash_get_table_fields to suite
+ if [[ $___FIELD ]] ; then ___FIELD="HASH_${___TABLE}_${___FIELD}_"
+ else ___FIELD="HASH_${___TABLE}_" ; fi
+
+ eval $3=\"$___FIELD\"
+}
+
+
+#---------------------------------------------------------------------
+## @param field name
+## @param table name
+## @param returning variable
+## @Type Private
+## most likely reverses hash_build_field_name
+##
+#---------------------------------------------------------------------
+function hash_unbuild_field_name() {
+ local ___TABLE="$2"
+ local ___FIELD=${1#$___TABLE}
+ ___FIELD=${___FIELD%_*}
+# `echo "$1" | sed -n "s/^$2\(.*\)_$/\1/p"`
+
+# debug "libhash" "hash_unbuild_field_name() - TABLE=$___TABLE:FIELD=$___FIELD"
+
+ ___FIELD=${___FIELD//_P_/\+}
+ ___FIELD=${___FIELD//_M_/\-}
+ ___FIELD=${___FIELD//_D_/\.}
+ ___FIELD=${___FIELD//_CLN_/\:}
+ ___FIELD=${___FIELD//_SPC_/ }
+ ___FIELD=${___FIELD//_CSB_/]}
+ ___FIELD=${___FIELD//_OSB_/[}
+
+ eval $3=\"$___FIELD\"
+}
+
+
+#---------------------------------------------------------------------
+## @param table name
+## @param field name
+## @param value
+##
+## Saves the value in the specified table/field.
+##
+#---------------------------------------------------------------------
+function hash_put() {
+ local VARIABLE_NAME
+ hash_build_variable_name "$1" "$2" VARIABLE_NAME
+ eval "${VARIABLE_NAME}=\"${3}\""
+ debug "libhash" "hash_put() - VARIABLE_NAME=$VARIABLE_NAME, data=$3"
+}
+
+
+#---------------------------------------------------------------------
+## @param table name
+## @param field name
+##
+## @Stdout Value stored in table/field
+## Echos the value stored in the table/field. If no value was
+## previously stored in the table/field, this function echos an empty
+## string.
+##
+#---------------------------------------------------------------------
+function hash_get() {
+ local VARIABLE_NAME
+ hash_build_variable_name "$1" "$2" VARIABLE_NAME
+ echo "${!VARIABLE_NAME}"
+}
+
+#---------------------------------------------------------------------
+## @param table name
+## @param field name
+## @param value
+##
+## Appends the value to the specified table/field.
+##
+#---------------------------------------------------------------------
+function hash_append() {
+ local VARIABLE_NAME
+ local old_value
+ local sep=${4:-" "}
+ hash_build_variable_name "$1" "$2" VARIABLE_NAME
+ old_value=${!VARIABLE_NAME}
+ if [[ -n $old_value ]] ; then
+ eval "${VARIABLE_NAME}=\"$old_value${sep}${3}\""
+ else
+ eval "${VARIABLE_NAME}=\"$3\""
+ fi
+}
+
+#---------------------------------------------------------------------
+## @param table name
+##
+## 'export' all the values in the table. This is useful for getting
+## hash table data from cast's pass_one/two into pass_three/pass_four
+## which are run through make. Essentially exporting lets us pass
+## the variables through make.
+##
+#---------------------------------------------------------------------
+function hash_export() {
+ local VARIABLE_NAME
+ hash_build_variable_name $1 "" VARIABLE_NAME
+ # make sure the hash has something in it before trying to export it
+ [[ $(eval echo '${!'$VARIABLE_NAME'*}') ]] &&
+ eval 'export ${!'$VARIABLE_NAME'*}'
+}
+
+#---------------------------------------------------------------------
+## @param table name
+## @param field name
+##
+## Unsets field. Deletes value.
+##
+#---------------------------------------------------------------------
+function hash_unset() {
+ local VARIABLE_NAME
+ hash_build_variable_name "$1" "$2" VARIABLE_NAME
+ eval unset ${VARIABLE_NAME}
+}
+
+
+#---------------------------------------------------------------------
+## @param table name
+##
+## Unsets all fields in a table.
+##
+#---------------------------------------------------------------------
+function hash_reset() {
+ local TABLE_NAME
+ hash_build_variable_name "$1" '' TABLE_NAME
+ local VARIABLES=`eval 'echo ${!'${TABLE_NAME}'*}'`
+ unset $VARIABLES
+
+}
+
+#---------------------------------------------------------------------
+## @param table name
+## @param opt delimiter
+## @Stdout table data
+## Outputs the entire table data, with fields separated by the
+## optional delimiter. If no delimiter is give, \n will be used.
+##
+#---------------------------------------------------------------------
+function hash_get_table() {
+
+ local TABLE_NAME
+ hash_build_variable_name "$1" '' TABLE_NAME
+ local VARIABLES i
+ local separator="$2"
+ separator=${separator:-$'\n'}
+ VARIABLES=`eval 'echo ${!'${TABLE_NAME}'*}'`
+
+ for i in $VARIABLES; do
+ echo -n "${!i}${separator}"
+ done
+
+}
+
+
+#---------------------------------------------------------------------
+## @param table name
+## @param opt delimiter
+## @Stdout Fields in table
+## Outputs all of the fields in the table , with fields separated
+## by the optional delimiter. If no delimiter is give, \n wil be
+## used.
+##
+#---------------------------------------------------------------------
+function hash_get_table_fields() {
+
+ local TABLE_NAME
+ hash_build_variable_name "$1" '' TABLE_NAME
+ local VARIABLES i
+ local separator="$2"
+ separator=${separator:-$'\n'}
+ VARIABLES=`eval 'echo ${!'${TABLE_NAME}'*}'`
+
+ local FIELD
+ for i in $VARIABLES ; do
+ hash_unbuild_field_name "$i" "$TABLE_NAME" FIELD
+ echo -n "${FIELD}${separator}"
+ done
+
+}
+
+#---------------------------------------------------------------------
+## @param table name
+## @Stdout Print the table in some reasonably readable form
+## As the name would imply, this is mainly for development use
+## and is not intended for regular use.
+##
+#---------------------------------------------------------------------
+function hash_debug_dump() {
+ local TABLE_NAME
+ local FIELD
+ for FIELD in $(hash_get_table_fields $1); do
+ echo "${FIELD} : $(hash_get $1 ${FIELD})"
+ done
+}
+
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libinitd b/var/lib/sorcery/modules/libinitd
new file mode 100644
index 0000000..6671991
--- /dev/null
+++ b/var/lib/sorcery/modules/libinitd
@@ -0,0 +1,287 @@
+#---------------------------------------------------------------------
+##
+## @Synopsis Functions for automatic init-script installation.
+##
+## Installation steps performed by this script:
+## <pre>
+## pre install:
+## <li>Determine if the script is a facility provider, if it is then
+## ask the user whether it should be the default provider.
+##
+## install:
+## <ul>
+## <li>Determine which runlevel the script should be placed in.
+## <li>Make a backup if there already is a different version of the script in
+## the target location.
+## <li>Install the script with permissions 754.
+## <li>Determine if the script sources any config file from /etc/sysconfig.
+## <li>For each config file:
+## <ul>
+## <li>Install the config file if there is no existing copy installed.
+## </li>Otherwise merge new options into existing copy.
+## </ul></ul>
+##
+## post install:
+##
+## <li> When the spell has been installed successfully then change the
+## /etc/sysconfig/facilities-file if the user wanted the script to be a
+## default provider.
+## </pre>
+## @Copyright Copyright (C) 2003 The Source Mage Team <http://www.sourcemage.org>
+##
+##
+## @Contributors Robert Helgesson <rycee@home.se>
+##
+#---------------------------------------------------------------------
+
+
+#---------------------------------------------------------------------
+## @param new file
+## @param old file
+## Makes a backup of "old file" if it differs from "new file"
+##
+#---------------------------------------------------------------------
+function _init_make_backup()
+{
+
+ # Don't bother if the old file doesn't exist
+ [ -f $2 ] || return
+
+ # Do the files differ? Return if they don't.
+ cmp -s $2 $1 && return
+
+ local savetime=$( date +'%Y%m%d%H%M' )
+ bck_file=$2.$savetime.backup
+
+ message "Making a backup of $2 due to differing content..."
+ mv $2 $bck_file
+ chmod 644 $bck_file
+
+}
+
+
+#---------------------------------------------------------------------
+## @param init script
+## @Stdout filelist
+## Extracts the files in /etc/sysconfig which are sourced by the script and
+## prints them to stdout separated by newlines.
+##
+#---------------------------------------------------------------------
+function _init_get_conf_files()
+{
+
+ sed -n -f - $1 <<EOF
+s!^\.\s\s*/etc/sysconfig/\([^[:space:]]*\).*!\1!p
+s!^source\s\s*/etc/sysconfig/\([^[:space:]]*\).*!\1!p
+EOF
+
+}
+
+
+#---------------------------------------------------------------------
+## @param new config file
+## @param old config file
+## Inserts new options from the new file into the old, existing, file
+##
+#---------------------------------------------------------------------
+function _init_merge_config_file()
+{
+
+ # Get newly added options
+ local new_opts=$( sed -n -e 's/[[:space:]]*\([[:alnum:]_]*\)=.*/\1/p' $1 $2 |
+ sort | uniq -u )
+
+ # for now the file is always modified...
+ if [ -z "$new_opts" ] ; then
+ message " No new options to merge into $2..."
+ track_manual $2 # but we still need to track it.
+ return 0
+ fi
+
+ local savetime=$( date +'%Y%m%d%H%M' )
+ cp $2 $2.$savetime.backup
+
+ for opt in $new_opts ; do
+ message " Merging option $opt into $2..."
+ sed -n -f - $1 <<EOF >> $2
+# If we encounter an empty line then clear hold space since another
+# variable is coming up...
+/^$/ { x ; d ; h ; }
+
+# The variable we're looking for has come, add it to the hold space and
+# then print everything, quit when done...
+/^[[:space:]]*$opt=/ { H ; x ; p; q; }
+
+# Default action, append line to hold space
+H
+EOF
+ done
+
+}
+
+
+#---------------------------------------------------------------------
+## @param configuration file...
+##
+## Installs or merges a list of init-script configuration-files.
+##
+#---------------------------------------------------------------------
+function _init_install_conf_files()
+{
+
+ mkdir -p $INSTALL_ROOT/etc/sysconfig
+ for file in "$@" ; do
+ local target_file=${INSTALL_ROOT}/etc/sysconfig/$file
+ local file=$SCRIPT_DIRECTORY/init.d/$file.conf
+
+ if [ ! -f "$file" ] ; then
+ [ ! -f "$target_file" ] &&
+ message "${PROBLEM_COLOR}Unable to find file $file...${DEFAULT_COLOR}"
+ continue
+ fi
+
+ if [ -f "$target_file" ] ; then
+ check_if_modified "$target_file" && mark_file_modified "$target_file"
+ _init_merge_config_file $file $target_file
+ else
+ install -m 644 $file $target_file
+ touch $target_file
+ fi
+ done
+
+}
+
+
+#---------------------------------------------------------------------
+## @param init script
+## @param previous cast instance's answers, if any
+## @param init script
+## Prepares installation of an init-script.
+## Creates the variable $INITPROVIDES which contains facility provided by the
+## script (if any)
+##
+#---------------------------------------------------------------------
+function init_prepare_install()
+{
+
+ local service="$1"
+ local old_provides="$2"
+ local result_list="$3"
+ local script_path="$SCRIPT_DIRECTORY/init.d/$service"
+ local script_provides=$( grep '^[:space:]*PROVIDES' $script_path )
+ script_provides=$( eval "( ${script_provides:=:} ; echo \$PROVIDES ; )" )
+
+ [ -z "$script_provides" ] && return
+
+ local default=n
+ if test -f $INSTALL_ROOT/etc/sysconfig/facilities &&
+ grep -q "$script_provides=$service" \
+ $INSTALL_ROOT/etc/sysconfig/facilities ||
+ list_find "$service:$script_provides" $old_provides; then
+ default=y
+ fi
+ local question="Set $service to be the default provider of $script_provides?"
+ if query "$question" $default; then
+ list_add "$result_list" "$service:$script_provides"
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## @param init script
+## * Makes a backup of an existing script.
+## * Installs the new script into the correct runlevel.
+## * Installs any config file used by the script.
+##
+#---------------------------------------------------------------------
+function init_install()
+{
+
+ local enable_script="$1"
+ local script_name="$2"
+ local script_path="$SCRIPT_DIRECTORY/init.d/$script_name"
+ local script_runlevel=$( grep '^[:space:]*RUNLEVEL' $script_path )
+ script_runlevel=$( eval "( $script_runlevel ; echo \$RUNLEVEL )" )
+
+ [ "$script_runlevel" = "s" ] && script_runlevel="S"
+
+ if [ -z "$script_runlevel" ] ; then
+ message "Init script $script_name doesn't contain good RUNLEVEL variable..."
+ return
+ fi
+
+ message "Installing $script_name to runlevel $script_runlevel..."
+
+ (
+ . ${INSTALL_ROOT}/etc/sysconfig/init
+
+ # /etc/sysconfig/init might not exist yet so we define this in case
+ RUNLEVELS_DIR="${RUNLEVELS_DIR:-$INSTALL_ROOT/etc/init.d/runlevels}"
+
+ local runlevel_dir="$RUNLEVELS_DIR/%$script_runlevel"
+
+ if [ ! -d "$runlevel_dir" ] ; then
+ message -n "${PROBLEM_COLOR}"
+ message -n "Unable to install $script_name to runlevel $script_runlevel. "
+ message "Runlevel do not exist or is not implemented as a directory."
+ message -n ${DEFAULT_COLOR}
+ exit 1
+ else
+ _init_make_backup $script_path $runlevel_dir/$script_name
+ if [ "$enable_script" == "enabled" ] ; then
+ install -m 754 $script_path $runlevel_dir
+ message " script $script_name installed and ${MESSAGE_COLOR}enabled${DEFAULT_COLOR}. Disable with 'telinit disable $script_name'."
+ message "${MESSAGE_COLOR}If you want to [re]start the service now${DEFAULT_COLOR}, issue 'telinit run $script_name [re]start'."
+ else
+ install -m 644 $script_path $runlevel_dir
+ message " script $script_name installed and ${MESSAGE_COLOR}disabled${DEFAULT_COLOR}. Enable with 'telinit enable $script_name'."
+ fi
+ _init_install_conf_files $( _init_get_conf_files $script_path )
+ fi
+ )
+
+}
+
+
+#---------------------------------------------------------------------
+##
+## Does cleanup if necessary. Must be run _after_ installwatch has stopped.
+##
+#---------------------------------------------------------------------
+function init_post_install()
+{
+ (
+ IFS="$WHITESPACE_IFS"
+ for sp in $INIT_PROVIDES ; do
+ mkdir -p $INSTALL_ROOT/etc/sysconfig
+ mark_file_modified "$INSTALL_ROOT/etc/sysconfig/facilities"
+ IFS=":"
+ set $sp # $1 - script name, $2 - facility
+ if ! sedit "s/^\([:space:]*$2\)=.*/\1=$1/" \
+ $INSTALL_ROOT/etc/sysconfig/facilities ; then
+ message -n "${PROBLEM_COLOR}Unable to modify facilities file to make "
+ message "$1 provider of $2.${DEFAULT_COLOR}"
+ else
+ message "Made $1 provider of $2"
+ fi
+ done
+ )
+}
+
+#---------------------------------------------------------------------
+##
+## 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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/liblock b/var/lib/sorcery/modules/liblock
new file mode 100644
index 0000000..010da47
--- /dev/null
+++ b/var/lib/sorcery/modules/liblock
@@ -0,0 +1,499 @@
+#!/bin/bash
+
+#----------------------
+## @Author : Benoit PAPILLAULT <benoit.papillault@free.fr>
+## @Creation : 29/10/2003
+## This source code is based on the fact that mkdir will create a
+## directory atomically. If two processes try to create a dir, one and only one
+## will succeed immediately
+## when interrupting the current process, we need to unlock all current
+## locks. Therefor, we need a list of locks for the current
+## process. And we need to update this list at the same time we got the
+## lock.
+## liblock is used to lock and unlock resources. It should be deadlock
+## free, and clean up locks after processes that had locks die without
+## unlocking the files. It uses the directory $LOCK_DIR defined in
+## /etc/sorcery/config
+#-----------------------
+
+# global options that can be changed to suit your needs.
+#Perhaps this should be moved to the config? (Duff, 04/07)
+
+[ -z "$LOCK_DIR" ] && LOCK_DIR=/tmp/lockdir
+[ -z "$MAX_SLEEP" ] && MAX_SLEEP=2
+
+#---------------------------------------------------------------------
+## lock_resources
+## @param Type of resource
+## @param Name of resource
+##
+## Type of resource | Name of resource | Usage
+## cast | spell name | when casting a spell
+## file | file name | to lock a file
+## summon | spell name | when downloading a spell
+## network | network | ?
+## solo | resource name | when using a resource exclusively
+## libgrimoire | install | when a spell is in the install stage
+## locking solo will not cause these routines to behave any different.
+## catching it has to be done externally (see cast for an example)
+##
+## this function locks the specified resource. this is a blocking
+## function that can take an indeterminate period of time to acquire
+## the lock. If no other processes have locked the resource, this
+## function will return immediately
+#---------------------------------------------------------------------
+lock_resources()
+{
+# if [ $# -ne 2 ]; then
+# echo "usage: lock_resources type_of_resource name_of_resource"
+# exit -1
+# fi
+
+ while ! trylock_resources "$@";
+ do
+ local SLEEP=$(( ${RANDOM} % ${MAX_SLEEP} ))
+ debug "lock_resources" "process $$ is sleeping ${SLEEP} seconds"
+ sleep "${SLEEP}"
+ done
+}
+
+#---------------------------------------------------------------------
+## trylock_resources
+## @param Type of resource
+## @param Name of resource
+##
+## this function tries to lock the specified resource. On success, it
+## immediately returns 0 (true). On failure (the lock cannot be
+## acquired), it returns 1 (false).
+#---------------------------------------------------------------------
+trylock_resources()
+{
+# if [ $# -ne 2 ]; then
+# echo "usage: trylock_resources type_of_resource name_of_resource"
+# exit -1
+# fi
+
+ local lockfile="$1.$2"
+ # we replace / with ^
+ lockfile="${LOCK_DIR}/${lockfile//\//^}"
+
+ debug "trylock_resources" "lockfile=${lockfile}"
+
+ mkdir -p "${LOCK_DIR}"
+
+ if ! mkdir "${lockfile}" 2>/dev/null; then
+ # we try to remove stale locks here and try again
+ global_clean_resources
+ if ! mkdir "${lockfile}" 2>/dev/null; then
+ return 1
+ fi
+ fi
+ ln -sf /proc/$$ "${lockfile}/$$"
+ return 0
+}
+
+#---------------------------------------------------------------------
+## excllock_resources
+## @param Type of resource
+## @param Name of resource
+##
+## this function works like lock_resources except that it waits for
+## all resources of the given type to unlock before locking.
+## This will not stop any other lock from being created, that has to
+## be done in the code
+#---------------------------------------------------------------------
+excllock_resources()
+{
+# if [ $# -ne 2 ]; then
+# echo "usage: excllock_resources type_of_resource name_of_resource"
+# exit -1
+# fi
+
+ while find $LOCK_DIR -mindepth 1 -maxdepth 1 -name "$1.*" | grep -q '';
+ do
+ #^^^ look if a lock exists of the given type (ugly code)
+ global_clean_resources
+ local SLEEP=$(( ${RANDOM} % ${MAX_SLEEP} ))
+ sleep "$SLEEP"
+ done
+ #theoretically, someone could create a lock now... tiny race
+ trylock_resources "$@"
+}
+
+#---------------------------------------------------------------------
+## unlock_resources
+## @param Type of resource
+## @param Name of resource
+##
+## this function unlocks the specified resource and returns immediately
+## if the specified resource is :
+## - unlocked : nothing is done
+## - locked by this process : it's unlocked
+## - locked by another process : it's kept locked.
+## In all cases and for compatibility reasons with the old liblock, we
+## return true
+#---------------------------------------------------------------------
+unlock_resources()
+{
+# if [ $# -ne 2 ]; then
+# echo "usage: unlock_resources type_of_resource name_of_resource"
+# exit -1
+# fi
+
+ local lockfile="$1.$2"
+ lockfile="${LOCK_DIR}/${lockfile//\//^}"
+
+ rm "${lockfile}/$$" 2>/dev/null &&
+ rmdir "${lockfile}" 2>/dev/null
+
+ return 0
+}
+
+#---------------------------------------------------------------------
+## clean_resources
+##
+## this function removes all locks acquired by the current process.
+## you can call this function at the end of your script to check for
+## missing call to unlock_resources
+#---------------------------------------------------------------------
+clean_resources()
+{
+# we remove locks owned by this process. there is no guarantee since
+# locks that have been acquired, but where no pidfile have been
+# created will not be released.
+
+ if [ -d "${LOCK_DIR}" ]; then
+ find "${LOCK_DIR}" -maxdepth 2 -mindepth 2 -name $$ | \
+ while read file; do
+ debug "clean_resources" "removing forgotten lock ${file}"
+ rm -f "${file}" 2>/dev/null &&
+ rmdir `dirname "${file}"` 2>/dev/null
+ done
+ fi
+}
+
+#---------------------------------------------------------------------
+## global_clean_resources
+##
+## this function is used internaly to remove all stale locks.
+#---------------------------------------------------------------------
+global_clean_resources()
+{
+ if [ -d "${LOCK_DIR}" ]; then
+ # we remove old locks (>1 minute) that are not owned by any process.
+ find "${LOCK_DIR}" -maxdepth 1 -mindepth 1 -mmin +1 -type d -empty -exec rmdir {} \;
+
+ # we remove locks that are owned by dead processes
+ find "${LOCK_DIR}" -maxdepth 2 -mindepth 2 | \
+
+ while read file; do
+ # check if the process still exist (we use procfs mounted on /proc)
+ if [ ! -d "${file}" ]; then
+ debug "global_clean_resources" "removing stale lock ${file}"
+ rm -f "${file}" 2>/dev/null &&
+ rmdir `dirname "${file}"` 2>/dev/null
+ fi
+ done
+ fi
+}
+
+#---------------------------------------------------------------------
+## @param Name of file to lock
+## @param <file> ... (optional)
+##
+## Locks files for access to only this PID. Will cause attempts by
+## other processes that want to lock the file(s) to block until this
+## PID unlocks the file, or this PID dies.
+## It cannot prevent bad programs from modifying the file w/o
+## permission.
+## Note: Files with funky chars may break things. No colons or stuff.
+## @Blocking
+##
+#---------------------------------------------------------------------
+function lock_file()
+{
+ debug "liblock" "$FUNCNAME - $*"
+ local FILE
+ lock_resources $(
+ for FILE in "$@" ; do
+ echo file $FILE
+ done
+ )
+}
+
+
+#---------------------------------------------------------------------
+## @param Name of file to unlock
+## @param <file> ... (optional)
+##
+## Unlocks a file so that another process can lock and modify it.
+##
+#---------------------------------------------------------------------
+function unlock_file()
+{
+ debug "liblock" "$FUNCNAME - $*"
+ local FILE
+ unlock_resources $(
+ for FILE in "$@" ; do
+ echo file $FILE
+ done
+ )
+}
+
+#---------------------------------------------------------------------
+## @param File to start transaction on
+## @param <file> ... (optional)
+##
+## A transaction locks a file and ensures that changes made to the
+## file are all made at once. No changes are made to the file until
+## the transaction is commited. Furthermore, a transaction can be
+## killed before it is commited, ending the transaction and not
+## making any changes to the files. All files are locked at once.
+## If not all can be locked, they will be unlocked, and another try
+## will be made.
+##
+## @return The name of a temporary file that should be written to
+## instead of the real file.
+##
+#---------------------------------------------------------------------
+function lock_start_transaction()
+{
+ debug "liblock" "$FUNCNAME - $*"
+ local i TRANSNAME NUMTRANS RET=""
+
+ if lock_file "$@" ; then
+
+ lock_file $LOCK_TRANSACTIONS
+ [ -s $LOCK_TRANSACTIONS ] || echo "0:::" > $LOCK_TRANSACTIONS
+
+ NUMTRANS=`tail -n 1 $LOCK_TRANSACTIONS | cut -d : -f 1`
+ NUMTRANS=${NUMTRANS:-1}
+ RET=()
+
+ for i in $* ; do
+ let NUMTRANS++
+ # cp file to temp file, if doesn't exist, create empty file.
+ if [ -e "$i" ] ; then
+ cp "$i" "$LOCK_TRANSACTIONS.$NUMTRANS"
+ else
+ echo -n "" > $LOCK_TRANSACTIONS.$NUMTRANS
+ fi
+ echo "$NUMTRANS:$i:$$" >$LOCK_TRANSACTIONS
+ RET[$NUMTRANS]="${LOCK_TRANSACTIONS}.${NUMTRANS}"
+ done
+
+ unlock_file $LOCK_TRANSACTIONS
+ echo ${RET[*]}
+ return 0
+
+ else
+ return 1
+ fi
+}
+
+#---------------------------------------------------------------------
+## @param File to commit transaction of
+## @param <file> ... (optional)
+##
+## Commits the changes made to the files as atomicaly as possible
+## Before this func is called, the files remain unchanged.
+##
+#---------------------------------------------------------------------
+function lock_commit_transaction()
+{
+ debug "liblock" "$FUNCNAME - $*"
+ local TMP_FILE unlockList=""
+
+ lock_file $LOCK_TRANSACTIONS
+ for i in $* ; do
+ TMP_FILE=${LOCK_TRANSACTIONS}.$(grep ".*:$i:$$" $LOCK_TRANSACTIONS | cut -d : -f 1)
+ [ -e $TMP_FILE ] && cp $TMP_FILE $i
+ if [ $? -ne 0 ] ; then
+ message "${PROBLEM_COLOR}Transaction commit failed on $i."
+ message "${DEFAULT_COLOR}The modified file is stored in $TMP_FILE."
+ message "This normaly happens when the disk runs out of space."
+ if ! query "Do you wish to continue?" "n" ; then
+ exit 1
+ fi
+ else
+ rm $TMP_FILE
+ fi
+ unlock_file $i
+ cp $LOCK_TRANSACTIONS $LOCK_TRANSACTIONS.new
+ if [ $? -ne 0 ] ; then
+ message "${PROBLEM_COLOR}Transaction commit failed on $LOCK_TRANSACTIONS."
+ message "${DEFAULT_COLOR}"
+ message "This normaly happens when the disk runs out of space."
+ if ! query "Do you wish to continue?" "n" ; then
+ exit 1
+ fi
+ fi
+ grep -v ".*:$i:$$" $LOCK_TRANSACTIONS.new >$LOCK_TRANSACTIONS &&
+ { rm $LOCK_TRANSACTIONS.new ||
+ message "${PROBLEM_COLOR}Very odd case. Quitting."; exit 1; }
+ done
+ unlock_file $LOCK_TRANSACTIONS
+
+}
+
+#---------------------------------------------------------------------
+## @param File to stop the transaction of
+## @param file ... (optional)
+##
+## Stops a transaction. Causes the changes to the file(s) to be
+## ignored/undone.
+##
+#---------------------------------------------------------------------
+function lock_kill_transaction()
+{
+ debug "liblock" "$FUNCNAME - $*"
+ local TMP_FILE
+
+ lock_file $LOCK_TRANSACTIONS
+ for i in $* ; do
+ TMP_FILE=$(grep `esc_str ".*:$i:$$"` $LOCK_TRANSACTIONS | cut -d : -f 1)
+ unlock_file $i
+ cp $LOCK_TRANSACTIONS $LOCK_TRANSACTIONS.1
+ grep -v `esc_str ".*:$i:$$"` $LOCK_TRANSACTIONS.1 >$LOCK_TRANSACTIONS
+ done
+ unlock_file $LOCK_TRANSACTIONS
+}
+
+#---------------------------------------------------------------------
+## Increment a counter
+##
+## @param Type of resource
+## @param Name of resource
+##
+## Processes should call counter_down when done with the resource
+## however counter_down and counter_check will clean up after misbehaving
+## processes.
+#---------------------------------------------------------------------
+function counter_up() {
+ local counterfile="$1.$2"
+ # we replace / with ^
+ counterfile="${LOCK_DIR}/${counterfile//\//^}"
+
+ touch $counterfile
+ lock_file $counterfile
+ echo $$ > $counterfile
+ unlock_file $counterfile
+}
+#---------------------------------------------------------------------
+## Decrement a counter
+##
+## @param Type of resource
+## @param Name of resource
+#---------------------------------------------------------------------
+function counter_down() {
+ local counterfile="$1.$2"
+ local foo each
+ # we replace / with ^
+ counterfile="${LOCK_DIR}/${counterfile//\//^}"
+
+ touch $counterfile
+ local tc=$(lock_start_transaction $counterfile)
+ for each in $(counter_clean $tc); do
+ [[ $foo ]] && echo $each && continue
+ if [[ $each == $$ ]] ; then foo=found ; else echo $each; fi
+ done > $tc
+ lock_commit_transaction $counterfile
+}
+#---------------------------------------------------------------------
+##
+## Destroy a counter
+##
+## @param Type of resource
+## @param Name of resource
+##
+#---------------------------------------------------------------------
+function counter_reset() {
+ local counterfile="$1.$2"
+ local foo each
+ # we replace / with ^
+ counterfile="${LOCK_DIR}/${counterfile//\//^}"
+ lock_file $counterfile
+ rm "${LOCK_DIR}/${counterfile//\//^}"
+ unlock_file $counterfile
+}
+
+#---------------------------------------------------------------------
+##
+## Check the value of a counter
+##
+## @param Type of resource
+## @param Name of resource
+##
+## Also cleans stale items
+##
+#---------------------------------------------------------------------
+function counter_check() {
+ local counterfile="$1.$2"
+ # we replace / with ^
+ counterfile="${LOCK_DIR}/${counterfile//\//^}"
+ if test -f $counterfile ; then
+ local tc=$(lock_start_transaction $counterfile)
+ # sort saves a temp file since it has to read all the input
+ counter_clean $tc |sort > $tc
+ lock_commit_transaction $counterfile
+ cat $counterfile|wc -l
+ else
+ echo 0
+ fi
+}
+
+#---------------------------------------------------------------------
+##
+## @Internal
+## @param counter file
+##
+## Print out the file with non-existent pids removed
+## Caller must hold a lock on the file
+##
+#---------------------------------------------------------------------
+function counter_clean() {
+ for each in $(cat $1) ; do
+ ps -A|grep -q $each && echo $each
+ done
+}
+
+#---------------------------------------------------------------------
+##=item SYNCHRONIZE
+##
+## Prevents more than one process from entering a section of code
+## at a time
+## NOTE: This assumes that two functions of the same name will
+## not havethe SYNCHRONIZE command on the same line in different
+## files.
+##
+## Example:
+## #!/bin/bash
+## echo "Script started with PID=$$
+## eval "$SYNCHRONIZE" && {
+## echo "PID $$ is in the synched section of code."
+## sleep 10
+## } && eval "$UNSYNCHRONIZE"
+## echo "PID $$ done."
+##
+## Note: SYNCHRONIZE and UNSYNCHRONIZE must be in the same local
+## scope. Nested SYNCHs are not allowed in the same local scopes.
+## Local scope usualy being a function.
+## (Blocking)
+##
+#---------------------------------------------------------------------
+SYNCHRONIZE='
+ __llSYNCH_LINE=$LINENO
+ debug "liblock" "+++ in synch code"
+ lock_resources "lockfunction" "${FUNCNAME}/${__llSYNCH_LINE}"'
+
+#---------------------------------------------------------------------
+##=item UNSYNCHRONIZE
+##
+## Terminates a section of SYNCHRONIZED code
+##
+#---------------------------------------------------------------------
+UNSYNCHRONIZE='
+ debug "liblock" "+++ in unsynch code"
+ unlock_resources "lockfunction" "${FUNCNAME}/${__llSYNCH_LINE}"
+ unset __llSYNCH_LINE'
+
diff --git a/var/lib/sorcery/modules/libmedia b/var/lib/sorcery/modules/libmedia
new file mode 100644
index 0000000..f0cb839
--- /dev/null
+++ b/var/lib/sorcery/modules/libmedia
@@ -0,0 +1,249 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Functions for dealing with screen and sound settings
+##
+##=head1 DESCRIPTION
+##
+## Provides color schemes
+##
+##=head1 COPYRIGHT
+##
+## Copyright (C) 2004 The Source Mage Team <http://www.sourcemage.org>
+##
+##=head1 FUNCTIONS
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## sets the colors based upon COLOR_SCHEME and defaults to a BRIGHT
+## color scheme
+#---------------------------------------------------------------------
+function media_init() {
+
+ # color ASCII codes
+ #
+ DEFAULT="\e[0m"
+ BOLD="\e[1m"
+ BLACK="\e[30m"
+ RED="\e[31m"
+ GREEN="\e[32m"
+ YELLOW="\e[33m"
+ BLUE="\e[34m"
+ VIOLET="\e[35m"
+ CYAN="\e[36m"
+ WHITE="\e[37m"
+
+ # color schemes
+ #
+ case X$(echo $COLOR_SCHEME | tr "[A-Z]" "[a-z]") in
+ Xdark)
+ SPELL_COLOR="${BLACK}${BOLD}"
+ VERSION_COLOR="${BLACK}${BOLD}"
+ QUERY_COLOR="${BLUE}${BOLD}"
+ DISPEL_COLOR="${BLUE}${BOLD}"
+ CHECK_COLOR="${CYAN}"
+ RESURRECT_COLOR="${GREEN}${BOLD}"
+ FILE_COLOR="${GREEN}${BOLD}"
+ SYMLINK_COLOR="${CYAN}${BOLD}"
+ PROBLEM_COLOR="${RED}${BOLD}"
+ MESSAGE_COLOR="${CYAN}"
+ DEFAULT_COLOR="${DEFAULT}"
+ ;;
+
+ Xblueish)
+ SPELL_COLOR="${BLACK}${BOLD}"
+ VERSION_COLOR="${BLACK}${BOLD}"
+ QUERY_COLOR="${BLUE}${BOLD}"
+ DISPEL_COLOR="${BLUE}${BOLD}"
+ CHECK_COLOR="${CYAN}"
+ RESURRECT_COLOR="${VIOLET}"
+ FILE_COLOR="${VIOLET}${BOLD}"
+ SYMLINK_COLOR="${CYAN}${BOLD}"
+ PROBLEM_COLOR="${RED}${BOLD}"
+ MESSAGE_COLOR="${CYAN}"
+ DEFAULT_COLOR="${DEFAULT}"
+ ;;
+
+ X*)
+ SPELL_COLOR="${WHITE}${BOLD}\e[40m"
+ VERSION_COLOR="${WHITE}${BOLD}\e[40m"
+ QUERY_COLOR="${YELLOW}${BOLD}"
+ DISPEL_COLOR="${YELLOW}${BOLD}"
+ CHECK_COLOR="${CYAN}"
+ RESURRECT_COLOR="${GREEN}${BOLD}"
+ FILE_COLOR="${GREEN}${BOLD}"
+ SYMLINK_COLOR="${CYAN}${BOLD}"
+ PROBLEM_COLOR="${RED}${BOLD}"
+ MESSAGE_COLOR="${CYAN}"
+ DEFAULT_COLOR="${DEFAULT}"
+ ;;
+ esac
+}
+
+
+#---------------------------------------------------------------------
+##
+## Setup sound menu from existing installed sorcery sound spells.
+## Currently no check done for installed sound schemes, just message
+## to user.
+##
+## Passes name of chosen scheme to the set_sound_scheme funtion.
+##
+#---------------------------------------------------------------------
+function sound_schemes_menu() {
+
+ local SOUND_HELP="Here you can choose your prefered sound scheme"
+ FER_SOUND="Ferris sound scheme"
+ MIS_SOUND="Misc sound scheme"
+ SIM_SOUND="Simpsons sound scheme"
+ STA_SOUND="Startrek sound scheme"
+ OFF_SOUND="Sound off"
+
+ if SOUND_CHECKS=`eval $DIALOG ' --title "Sound Schemes" \
+ --cancel-label "Exit" \
+ --ok-label "Select" \
+ --item-help \
+ --menu \
+ "$SOUND_HELP" \
+ 0 0 0 \
+ "OFF" "$OFF_SOUND" "$SOUND_HELP" \
+ "FER" "$FER_SOUND" "$SOUND_HELP" \
+ "MIS" "$MIS_SOUND" "$SOUND_HELP" \
+ "SIM" "$SIM_SOUND" "$SOUND_HELP" \
+ "STA" "$STA_SOUND" "$SOUND_HELP"'`
+ then
+
+ for CHECK in $SOUND_CHECKS; do
+ case $CHECK in
+ OFF) remove_config $LOCAL_CONFIG "SOUND"
+ modify_config $LOCAL_MEDIA_CONFIG "SOUND" "off" &&
+ SOUND=off
+ eval $DIALOG '--msgbox "Sorcery sounds have been turned off." 0 0'
+ ;;
+ FER) set_sound_theme ferris ;;
+ MIS) set_sound_theme misc ;;
+ SIM) set_sound_theme simpsons ;;
+ STA) set_sound_theme startrek ;;
+ esac
+ done
+
+ fi
+
+}
+
+
+#---------------------------------------------------------------------
+## @param sound scheme name
+##
+## Here the passed scheme is set as default sound scheme.
+## If the sound spell is not installed it will be cast for the user.
+##
+#---------------------------------------------------------------------
+function set_sound_theme() {
+
+ remove_config $LOCAL_CONFIG "SOUND"
+ remove_config $LOCAL_CONFIG "SOUND_THEME"
+
+ modify_config $LOCAL_MEDIA_CONFIG "SOUND" "on" &&
+ modify_config $LOCAL_MEDIA_CONFIG "SOUND_THEME" "$1" &&
+ SOUND=on &&
+ SOUND_THEME=$1
+
+
+ if ! spell_ok sorcery-sound-$SOUND_THEME
+ then
+ eval $DIALOG '--msgbox "Sorcery has determined that the $SOUND_THEME \
+ theme must be installed to work. It will now \
+ be cast for you!" 0 0'
+ cast sorcery-sound-$SOUND_THEME
+ else
+ eval $DIALOG '--msgbox "The $SOUND_THEME theme is installed and ready \
+ for you to enjoy your new sounds!" 0 0'
+ fi
+
+}
+
+
+color_schemes_menu() {
+
+ if
+
+ local L_HELP="Light color scheme for use on dark backgrounds"
+ local D_HELP="Dark color scheme for use on bright backgrounds"
+ local B_HELP="blueish color scheme"
+
+ COMMAND=`eval $DIALOG ' --title "Current scheme: $COLOR_SCHEME" \
+ --item-help \
+ --ok-label "Select" \
+ --cancel-label "Exit" \
+ --menu \
+ "" \
+ 0 0 0 \
+ "L" "Light" "$L_HELP" \
+ "D" "Dark" "$D_HELP" \
+ "B" "Blueish" "$B_HELP" '`
+
+ then
+ case $COMMAND in
+
+ B) COLOR_SCHEME=blueish ;;
+ D) COLOR_SCHEME=dark ;;
+ L) COLOR_SCHEME=bright ;;
+
+ esac
+ remove_config $LOCAL_CONFIG COLOR_SCHEME
+
+ modify_config $LOCAL_MEDIA_CONFIG COLOR_SCHEME $COLOR_SCHEME &&
+ media_init
+ fi
+}
+
+#---------------------------------------------------------------------
+## @param on/off
+##
+## First argument is "off" or "on" to turn console colors off or on
+##
+#---------------------------------------------------------------------
+function color() {
+
+ case $1 in
+ off) unset SPELL_COLOR
+ unset VERSION_COLOR
+ unset QUERY_COLOR
+ unset DISPEL_COLOR
+ unset CHECK_COLOR
+ unset RESURRECT_COLOR
+ unset FILE_COLOR
+ unset SYMLINK_COLOR
+ unset PROBLEM_COLOR
+ unset MESSAGE_COLOR
+ unset DEFAULT_COLOR
+ COLOR=off
+ ;;
+ on) COLOR=on
+ ;;
+ esac
+
+}
+
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libmisc b/var/lib/sorcery/modules/libmisc
new file mode 100644
index 0000000..27249ff
--- /dev/null
+++ b/var/lib/sorcery/modules/libmisc
@@ -0,0 +1,1358 @@
+#!/bin/bash
+
+#---------------------------------------------------------------------
+## @param string to explode
+## @param delimiter
+## @param name of array to put it in
+##
+## @Description
+## Turns a string into an array. Each element of the array
+## is separated in the string by the delimiter.
+##
+## Note: The array you want the fields put into must be
+## declared before you call this function.
+## <pre>
+## EXAMPLE
+##
+## my_array=()
+## explode "a_string_to_explode" "_" "my_array"
+## echo my_array[*]
+##
+## Produces "a" "string" "to" "explode".
+## </pre>
+#---------------------------------------------------------------------
+function explode()
+{ # $1==string to explode, $2==delimiter, $3==name of array to put it in
+
+ [[ "$3" ]] || return 1
+ local l=$1
+ local i=0
+ while [[ $l ]]; do
+ local result=${l%%$2*}
+ l=${l#"$result"}
+ l=${l#$2}
+ eval "$3[$i]=\"$result\""
+ ((i++))
+ done
+ # this adds an empty array element at the end if the line ended in $2
+ local lc=${1//\*$2/true}
+ if [ "$lc" = "true" ]; then
+ eval "$3[$i]=\"\""
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## @Type API
+## @param sed command
+## @param file
+##
+## First argument is a sed command. Second argument is a file.
+## sedit performs the sed command on the file, modifiying the
+## original file. For example,
+## <br>sedit "s/foo/bar/g" /tmp/somefile <br>
+## will replace all occurances of foo with bar in /tmp/somefile.
+## This function is often used in spells to make changes to source
+## files before compiling. See the sed man page for more information.
+##
+#---------------------------------------------------------------------
+function real_sedit() {
+ sed -i "$1" "$2"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param string to check
+##
+## Argument is a string to check if the string contains all digits or
+## not
+#--------------------------------------------------------------------
+function isdigit() {
+ echo $1 | grep '[[:digit:]]+' >> /dev/null 2>&1 &&
+ return 0
+}
+
+#--------------------------------------------------------------------
+## @Type API
+## @param string to check
+##
+## Argumtemt is a string to check if it contains all chars a-zA-Z
+#--------------------------------------------------------------------
+function isalpha() {
+ echo $1 | grep '[[:alpha:]]+' >> /dev/null 2>&1 &&
+ return 0
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param question
+## @param default answer
+##
+## @return 0 on yes
+## @return 1 on no
+##
+## Asks the user a yes/no question. First argument is the question to
+## ask, second argument is the default answer. If a timeout occurs
+## before the question is answered, the given default answer is
+## applied. Pressing spacebar or enter applies the default answer
+## immediatelly without waiting for timeout.
+## Returns true or false based on the answer given.
+##
+#---------------------------------------------------------------------
+function real_query() { (
+
+ debug "libmisc" "Running query() with the following arguments: '$1' and '$2'"
+
+ while true ; do
+
+ RESPONSE=""
+
+ if [ -z "$SILENT" ]; then
+
+ echo -e -n "${QUERY_COLOR}$1 [$2] ${DEFAULT_COLOR}"
+
+ read -t $PROMPT_DELAY -n 1 RESPONSE
+ echo
+ fi
+
+ RESPONSE=${RESPONSE:=$2}
+ case $RESPONSE in
+ n|N) return 1 ;;
+ y|Y) return 0 ;;
+ esac
+
+ done
+
+) }
+
+#---------------------------------------------------------------------
+## @param return_var
+## @param question
+## @param default answer
+##
+## @return 0 user supplied answer
+## @return 1 default answer is used
+##
+## Asks user for string, with default answer and timeout (like query)
+##
+#---------------------------------------------------------------------
+function real_query_string () {
+
+ debug "libmisc" "Running question() with the following arguments: '$1' and '$2'"
+
+ local RESPONSE=""
+ local RETURN=0
+
+ local DEFAULT=""
+ [ -n "$3" ] && DEFAULT=" [$3] "
+
+ if [ -z "$SILENT" ]; then
+ echo -e -n "${QUERY_COLOR}$2${DEFAULT}${DEFAULT_COLOR}"
+ read -t $PROMPT_DELAY RESPONSE
+ fi
+
+ [ -z "$RESPONSE" ] && RETURN=1 && RESPONSE="$3"
+
+ eval $1=\"${RESPONSE}\"
+ return $RETURN
+}
+
+
+
+#---------------------------------------------------------------------
+## @Type API
+## @param message to echo
+## @Stdout message
+## echo's the given arguments if SILENT is not set.
+#---------------------------------------------------------------------
+function real_message() {
+ if [ ! -n "$SILENT" ]; then
+ if [ "$1" == "-n" ]; then
+ shift 1
+ echo -n -e "$*"
+ else
+ echo -e "$*"
+ fi
+ fi
+
+}
+
+#---------------------------------------------------------------------
+## @param type
+## @param message
+##
+## Enters a debug message if the type of debug message != 'no'
+##
+## The 'type' is usually the name of the file the debug statement
+## comes from. i.e. DEBUG_liblock, DEBUG_libsorcery, DEBUG_cast etc.
+##
+#---------------------------------------------------------------------
+function debug() {
+
+ [[ $DEBUG ]] || return 0
+
+ local debugVar="DEBUG_${1}"
+ local i
+ if [[ ${!debugVar} != "no" ]] ; then
+ echo -n "$1($$): " >>$DEBUG
+ shift
+ for i in "$@" ; do
+ echo -n " \"$i\"" >>$DEBUG
+ done
+ echo >>$DEBUG
+ fi
+
+ true
+}
+
+#---------------------------------------------------------------------
+## @Stdout progress spinner
+## Displays progress spinner like the one in fsck.
+##
+#---------------------------------------------------------------------
+function progress_spinner() {
+ let PROGRESS_SPINNER=$PROGRESS_SPINNER+1
+ if (( PROGRESS_SPINNER > ${#PROGRESS_SPINNER_CHARS}-1 )); then
+ PROGRESS_SPINNER=0;
+ fi
+ echo -en "\b${PROGRESS_SPINNER_CHARS:$PROGRESS_SPINNER:1}"
+}
+
+
+#---------------------------------------------------------------------
+## @param opt "dot"
+## @param count
+## @param total
+## @param opt length
+## <pre>
+## Displays progress bar in the form of [----> ] 100%
+## Or just a dot in the dot format
+##</pre>
+#---------------------------------------------------------------------
+function progress_bar() {
+debug "libmisc" "progress_bar - $*"
+ local percent i len num_dash
+
+ if [[ $1 == -dot ]] ; then
+ len=0
+ shift 1
+ else
+ len=${3:-${COLUMNS:-70}}
+ fi
+
+ # Can't make a bar because there's no total, or the length isn't
+ # long enough, or if there is no length
+ if [ $# -lt 2 ] || [ $len -lt 8 ] ; then
+ message -n "."
+ return 0
+ fi
+
+ if [ $1 -lt 1 ] || [ $2 -lt 1 ] ; then return 1 ; fi
+
+ percent=$((100*$1/$2))
+ percent=`printf "%.0f" $percent`
+
+ if [[ $LAST_PERCENT == $percent ]] ; then
+ progress_spinner
+ return 0
+ fi
+
+ LAST_PERCENT=$percent
+
+
+ dash_len=$(($len-8))
+ num_dash=$(( $dash_len*$1/$2 ))
+
+ #Format: [---> ] 100%
+
+ #opening of the bar
+ BAR_STRING="["
+
+ #The '=' signs
+ for (( i=0 ; i<$num_dash ; i++ )) ; do
+ BAR_STRING="${BAR_STRING}="
+ done
+
+ #Now a pretty indicator
+ [ $num_dash -lt $((dash_len-1)) ] && BAR_STRING="${BAR_STRING}>"
+
+ #Fill the rest of the bar up with blanks
+ for (( i++ ; i<$dash_len-1 ; i++ )) ; do
+ BAR_STRING="${BAR_STRING} "
+ done
+
+ #Put on the closing for the bar and the percent
+ BAR_STRING="${BAR_STRING}] ${percent}%"
+
+ #Clear the rest of the space
+ for (( i+=3+${#percent} ; i<$len ; i++ )) ; do
+ BAR_STRING="${BAR_STRING} "
+ done
+
+ clear_line
+ message -n "$BAR_STRING"
+ progress_spinner
+
+ return 0
+
+
+}
+#---------------------------------------------------------------------
+## @Stdout Clear line
+## Clears the current line and puts the cursor at the begining of it.
+##
+#---------------------------------------------------------------------
+function clear_line() {
+
+ echo -en '\r\033[2K'
+ return 0
+
+}
+
+
+#---------------------------------------------------------------------
+##
+## @Globals SOUND
+## Plays a given soundfile if sounds are on and the file exists in
+## the chosen theme.
+##
+#---------------------------------------------------------------------
+function sound() {
+
+ case $SOUND in
+ on) SOUND_FILE=$SOUND_DIRECTORY/$SOUND_THEME/$1
+ if [ -e $SOUND_FILE ]; then
+ ( cd / ; play $SOUND_FILE 2>/dev/null & )
+ debug "libmisc" "Playing $SOUND_FILE"
+ else
+ debug "libmisc" "Error playing $SOUND_FILE: no such file"
+ fi
+ ;;
+ esac
+
+}
+
+
+#---------------------------------------------------------------------
+## @param filename
+##
+## Runs the editor given by EDITOR on the file given in the first
+## argument. If EDITOR is not set, "nano -w" is used.
+##
+#---------------------------------------------------------------------
+function edit_file() {
+
+ ${EDITOR:-nano -w} $1
+
+}
+
+
+#---------------------------------------------------------------------
+## @param string
+## @Stdout escaped string
+##
+## Adds escape sequences to strings for special characters.
+## Does NOT escape the string ".*".
+## Used for putting escape sequences in regexp strings that should
+## be treated literaly.
+##
+#---------------------------------------------------------------------
+function esc_str()
+{
+
+ local OUT
+ if [[ $* == .* ]] ; then
+ OUT="$1"
+ else
+ OUT=`echo "$@" | sed -e 's:\.:\\\.:'g \
+ -e 's:\*:\\\*:'g`
+ fi
+ echo "$OUT"
+
+}
+
+
+#---------------------------------------------------------------------
+## Properly quotes and backquotes parameters so they can be passed
+## through su at the start of many sorcery commands
+##
+## Expected usage is:
+##
+## PARAMS=$(consolidate_params "$@")
+## su -c "$0 $PARAMS" root
+#---------------------------------------------------------------------
+function consolidate_params() {
+ local param
+ for param in "$@"; do
+ echo -n $param | sed 's/ /\\ /g'
+ echo -n " "
+ done
+}
+
+#---------------------------------------------------------------------
+## @Stdin directories.
+## @Stdout directories' basenames
+## Takes newline separated list of directories
+## and outputs their base names.
+##
+#---------------------------------------------------------------------
+function get_basenames() {
+ local DIRECTORY=''
+ while read DIRECTORY; do
+ basename "$DIRECTORY"
+ done
+}
+
+
+#---------------------------------------------------------------------
+## @Stdin directories.
+## @Stdout directories' basenames
+## Takes newline separated list of pathnames
+## and outputs their directory names.
+##
+#---------------------------------------------------------------------
+function get_dirnames() {
+ local PATH_NAME=''
+ while read PATH_NAME; do
+ dirname "$PATH_NAME"
+ done
+}
+
+#---------------------------------------------------------------------
+## @param The function to call on each itteration and it's arguments
+## @param The separator string. If there is no $3, only the first char counts
+## @param The string to itterate over. (optional)
+## @Stdin is used when $3 isn't given.
+##
+## $3 is optional, when it isn't used,
+## stdin is used and only the first letter in $2 is used. Note, using stdin
+## can have odd side effects when your function uses read and stdin itself.
+##
+## Special vars:
+## BREAK: Use this to break out of the loop prematurely, also causes a return of 1.
+##
+## If the function returns the value of the last return
+## Notes:
+## In stdin mode, if the string does not terminate with the delimiter, the last
+## token will be ignored.
+#----------------------------------------------------------------------
+function iterate()
+{ # $1=callback+args, $2=separator, $3=(opt)string
+# debug "libmisc" "iterate - $@"
+
+ [ $# -lt 2 ] && return 2
+
+ local oldIFS="$IFS"
+ local token
+ local func="$1"
+ local returnValue=0
+ if [[ $# -gt 2 ]] ; then
+ IFS="$2"
+ shift 2
+ for token in $* ; do
+ IFS="$oldIFS"
+
+ eval "$func \"$token\""
+ [[ $BREAK ]] && break
+
+ done
+ IFS="$oldIFS"
+ else
+ while read -r -d "$2" token ; do
+ eval "$func \"$token\""
+ [[ $BREAK ]] && break
+ done
+# debug "leftover token: $token"
+ fi
+
+ returnValue=$?
+ [[ $BREAK ]] && debug "libmisc" "iterate - I was BREAKed."
+ unset BREAK
+ return $returnValue
+}
+
+#---------------------------------------------------------------------
+## @param return_var (must not be i, foo, temp, returnvar, or default)
+## @param default choice
+## @param elements, ..
+##
+## gives the user some nice select list and puts the selected
+## item in return_var
+##
+#---------------------------------------------------------------------
+function select_list()
+{
+ local i
+ local foo temp number
+ local returnvar=$1
+ local default=$2
+ shift 2
+ let i=0
+ for foo in "$@"; do
+ message "\t$DEFAULT_COLOR($i) $SPELL_COLOR$foo$DEFAULT_COLOR"
+ temp[$i]="$foo"
+ let i++
+ done
+
+ message -n "\n${QUERY_COLOR}Which one do you want? [$default]$DEFAULT_COLOR "
+ read -t $PROMPT_DELAY -n 1 number
+ if [[ ! $number ]] ; then number=$default; fi
+ while [[ $number != [0-9]* ]] || (( $number >= $i )) ; do
+ message -n "\n${QUERY_COLOR}Which one do you want? [$default]$DEFAULT_COLOR "
+ read -n 1 number
+ if [[ ! $number ]] ; then number=$default; fi
+ done
+
+ echo
+
+ eval $returnvar=\"${temp[$number]}\"
+}
+
+#---------------------------------------------------------------------
+## @param title
+##
+## sets the terminal title if TERM=xterm|rxvt or the window title if
+## TERM=screen
+##
+#---------------------------------------------------------------------
+function set_term_title()
+{
+ if [ "$SET_TERM_TITLE" != "on" ]; then return; fi
+ case $TERM in
+ xterm*|rxvt) echo -ne "\e]0;$@\007"
+ ;;
+ screen) echo -ne "\ek$@\e\\"
+ ;;
+ *) ;;
+ esac
+}
+
+
+#---------------------------------------------------------------------
+## @param return_var
+## @param elements, ...
+##
+## Removes from the list string(s). Strings are kept to be unique and
+## are separated by spaces
+##
+#---------------------------------------------------------------------
+function real_list_remove () {
+ local var=$1
+ shift
+
+ local i
+
+ for i in $@; do
+ eval "$var=\`echo \" \$$var \" | sed \
+ -e \"s/[[:space:]]\\+/ /g\" \
+ -e \"s/ $i / /g\" \
+ -e \"s/^ *//\" \
+ -e \"s/ *$//\"\`"
+ done
+}
+
+
+#---------------------------------------------------------------------
+## @param return_var
+## @param elements, ...
+##
+## Puts in the list string(s). Strings are kept to be unique and are
+## separated by spaces
+##
+#---------------------------------------------------------------------
+function real_list_add () {
+ local var=$1
+ shift
+ local i
+ for i in $@; do
+ local found=0
+ function list_add_sub() {
+ if [ "$i" == "$1" ]; then
+ found=1
+ fi
+ }
+ iterate list_add_sub " " "$(eval "echo \$$var")"
+ if [ $found -eq 0 ]; then
+ eval "$var=\`echo \"\$$var $i\"\`"
+ eval "$var=\"\${$var/# /}\""
+ fi
+ done
+}
+
+
+#---------------------------------------------------------------------
+## @param string
+## @param elements, ...
+##
+## return 0 at least one element is in list
+## return 1 none of supplied elements is not in list
+##
+## Finds if at least one of given elements is in the string. Warning,
+## this function takes real string, not variable name as other list_*
+## functions
+##
+#---------------------------------------------------------------------
+function real_list_find () {
+ local i
+ local input="$1"
+ shift
+
+ for i in $@; do
+ eval "echo \" $input \" | grep -q -e \"[[:space:]]$i[[:space:]]\" && return 0"
+ done
+ return 1
+}
+
+#---------------------------------------------------------------------
+## @param spell
+## @param variable to read
+## @param variable name
+##
+## finds the persistent var for spell and sets variable name to the value
+## of the variable to read
+##
+#---------------------------------------------------------------------
+function real_persistent_read() {
+ local exfile pfile tb_dir
+ local varin=$2
+ local varout=$3
+ tablet_find_spell_dir $1 tb_dir || return 2
+ tablet_get_spell_file $tb_dir EXPORTS exfile || return 4
+ tablet_get_persistent_config $tb_dir pfile || return 3
+ if grep -q "^${varin}$" $exfile
+ then
+ eval "$3="'$( . "$pfile" && echo "${!varin}" )'
+ else
+ return 1
+ fi &&
+ return 0
+}
+
+#---------------------------------------------------------------------
+## @param variables, ...
+##
+## Adds variable names to the list of persistent variables
+##
+#---------------------------------------------------------------------
+function real_persistent_add () {
+ if [[ $PROTECT_SORCERY ]] ; then
+ for each in "$@" ; do
+ if is_sorcery_var $each; then
+ # complain, but not if we're persistent loading, bug 9416
+ if ! [[ $PERSISTENT_LOADING ]] ; then
+ complain_sorcery_var $each
+ fi
+ else
+ list_add PERSISTENT_VARIABLES $each
+ fi
+ done
+ else
+ list_add PERSISTENT_VARIABLES "$@"
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## @param variables, ...
+##
+## Removes variable names from the list of persistent variables
+##
+#---------------------------------------------------------------------
+function real_persistent_remove () {
+ list_remove PERSISTENT_VARIABLES "$@"
+}
+
+
+#---------------------------------------------------------------------
+## Loads persistent variables stored in file "$SPELL_CONFIG"
+##
+#---------------------------------------------------------------------
+function real_persistent_load () {
+ local PERSISTENT_LOADING=yes
+ local PERSISTENT_FILE=${1:-$SPELL_CONFIG.p}
+ if [ -f "$PERSISTENT_FILE" ]; then
+ . "$PERSISTENT_FILE"
+ local line
+ persistent_add `while read line; do echo ${line%%=*}; done < "$PERSISTENT_FILE"`
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Saves variables marked as persistent to file "$SPELL_CONFIG". The
+## File is completely overwritten. Also unsets all persistent
+## variables
+##
+#---------------------------------------------------------------------
+function real_persistent_save () {
+ local VAR
+ local TMP
+ > "$SPELL_CONFIG.p"
+ for VAR in $PERSISTENT_VARIABLES; do
+ config_get_option $VAR TMP
+ echo "$VAR=\"$TMP\"" >> "$SPELL_CONFIG.p"
+ unset "$VAR"
+ done
+ unset PERSISTENT_VARIABLES
+}
+
+
+#---------------------------------------------------------------------
+## Unsets all persistent variables. Mainly usable as replacement of
+## persistent_save for functions which can be called by nonroot users
+## ( for example from 'gaze what' )
+##
+#---------------------------------------------------------------------
+function real_persistent_clear () {
+ local VAR
+ for VAR in $PERSISTENT_VARIABLES; do
+ unset "$VAR"
+ done
+ unset PERSISTENT_VARIABLES
+}
+
+
+
+#---------------------------------------------------------------------
+## @param config file variable
+## @param return variable (optional)
+##
+## @return 1 option is not present in the config
+## @return 0 option is present in the config (even if the option is
+## empty string "") and it's set.
+##
+## Retrieves setting from $SPELL_CONFIG file and optionally sets user
+## supplied variable. Function is here to make possible changes to
+## config system easy, since all other functions are using this
+## function and are not working with variables directly
+##
+#---------------------------------------------------------------------
+#function config_get_option {
+# local VAR=$2
+# local REPLY=`grep -E "^$1=" $SPELL_CONFIG | head -n 1 | sed "s/^[^=]*=//"`
+# [ -n "$VAR" ] && eval $VAR=\"$REPLY\"
+# [ -z "$REPLY" ] && return 1
+# return 0
+#}
+function config_get_option () {
+ if [[ $PROTECT_SORCERY ]] ; then
+ if is_sorcery_var $1; then
+ complain_sorcery_var $1
+ return 1
+ fi
+ fi
+
+ # afrayedknot hates variable leakage
+ if real_list_find $1 $PERSISTENT_VARIABLES; then
+ # variable is known and valid
+ [[ -n $2 ]] && eval "$2=\"\$$1\""
+ return 0
+ else
+ # variable is not known and is invalid
+ eval "unset \"$1\""
+ return 1
+ fi
+}
+
+#---------------------------------------------------------------------
+## @param Name of variable
+## @param return variable
+##
+## @return 0 if the variable could be found, 1 if no old persistent file
+## exists.
+##
+## Get value of a persistent variable from the previous cast when
+## there is a -r. The persistent data moves to a seperate directory
+## when cast -r is run, this makes the new persistent config clean
+## but allows us to provide the user defaults from the last cast.
+#---------------------------------------------------------------------
+function config_get_last_option() {
+ if [[ $PROTECT_SORCERY ]] ; then
+ if is_sorcery_var $1; then
+ complain_sorcery_var $1
+ return 1
+ fi
+ fi
+
+ local pfile
+ if test -f $ABANDONED_PERSIST/$SPELL.p; then
+ pfile=$ABANDONED_PERSIST/$SPELL.p
+ # leave this out for now until theres a way to disable it
+ # having it enabled will make it difficult to get back to the true defaults
+ #else
+ # local tb_dir
+ # tablet_find_spell_dir $SPELL tb_dir &&
+ # tablet_get_persistent_config $tb_dir pfile || return 1
+ fi
+
+ local foo
+ if [[ $pfile ]] ; then
+ foo=$(
+ persistent_load $pfile
+ if real_list_find $1 $PERSISTENT_VARIABLES; then
+ eval "echo \"\$$1\""
+ fi
+ )
+ else
+ return 1
+ fi
+
+ eval "$2=\"\$foo\""
+ return 0
+}
+
+
+#---------------------------------------------------------------------
+## @param config file variable
+## @param value
+##
+## Stores string to given variable and makes the variable persistent
+## Function is here to make possible changes to config system easy,
+## since all other functions are using this function and are not
+## working with variables directly
+##
+#---------------------------------------------------------------------
+#function config_set_option {
+# echo "$1=$2" >> $SPELL_CONFIG
+#}
+function config_set_option () {
+ persistent_add $1
+ eval "$1=\"$2\""
+}
+
+
+#---------------------------------------------------------------------
+## @param config file variable
+## @param question
+## @param default answer
+##
+## @return 0 in all cases
+##
+## Asks user for string, with default answer and timeout (like query)
+## Return variable is also marked as persistent
+##
+#---------------------------------------------------------------------
+function real_config_query () {
+ local ANSWER
+ if config_get_option "$1" ANSWER; then
+ # option allready ANSWERed in config
+ echo -e "[[ ${QUERY_COLOR}$2${DEFAULT} -> ${QUERY_COLOR}$ANSWER${DEFAULT} ]]"
+
+ # already have a reasonable value...
+ [ "$ANSWER" == "y" ] || [ "$ANSWER" == "n" ] && return 0
+ fi
+
+ local default
+ config_get_last_option "$1" default
+ [[ $default ]] || default="$3"
+
+ if query "$2" "$default"; then
+ config_set_option "$1" y
+ else
+ config_set_option "$1" n
+ fi
+ return 0
+}
+
+#---------------------------------------------------------------------
+## @param config file variable
+## @param question
+## @param default answer [y|n]
+## @param option_yes - can't be empty string
+## @param option_no - can't be empty string
+##
+## @return 0 in all cases
+##
+## Asks user for string, with default answer and timeout (like query)
+## The string is added to the variable
+## If you want to use empty string, place there dummy string and remove
+## it later by list_remove function. Also for one config variable, all
+## option_yes and option_no have to be different.
+##
+## Return variable is also marked as persistent
+#---------------------------------------------------------------------
+function real_config_query_option () {
+ local ANSWER key
+
+ # If the option exists
+ # If the option contains option_yes or option_no
+ if config_get_option "$1" ANSWER && list_find "$ANSWER" $4 $5; then
+ # Then option allready ANSWERed in config
+
+ # Find out if the option was 'y' or 'n'
+ list_find "$ANSWER" $4 && key=y
+ list_find "$ANSWER" $5 && key=n
+
+ if [[ "$key" ]]; then
+ echo -e "[[ ${QUERY_COLOR}$2${DEFAULT} -> ${QUERY_COLOR}$key${DEFAULT} ]]"
+ return 0
+ fi
+ fi
+
+ local last_answer default=$3
+ config_get_last_option "$1" last_answer
+ # Find out if the option was 'y' or 'n'
+ list_find "$last_answer" $4 && default=y
+ list_find "$last_answer" $5 && default=n
+
+ if query "$2" "$default"; then
+ list_add ANSWER $4
+ else
+ list_add ANSWER $5
+ fi
+
+ config_set_option "$1" "$ANSWER"
+ return 0
+}
+
+
+#---------------------------------------------------------------------
+## @param config file variable, return variable
+## @param question
+## @param default answer
+##
+## @return 0 in all cases
+##
+## Asks user for string, with default answer and timeout (like query)
+## Return variable is also marked as persistent
+##
+#---------------------------------------------------------------------
+function real_config_query_string () {
+ local ANSWER
+
+ if config_get_option "$1" ANSWER; then
+ # option allready answered in config
+ echo -e "[[ ${QUERY_COLOR}$2${DEFAULT} -> '${QUERY_COLOR}$ANSWER${DEFAULT}' ]]"
+ else
+ local default
+ config_get_last_option "$1" default
+ [[ $default ]] || default="$3"
+ query_string ANSWER "$2" "$default"
+ config_set_option "$1" "$ANSWER"
+ fi
+ return 0
+}
+
+
+#---------------------------------------------------------------------
+## @param config file variable, return variable
+## @param question
+## @param elements, ...
+##
+## @return 0 in all cases
+##
+## Asks user for string, with numbered possibilities listed
+## Return variable is also marked as persistent
+##
+#---------------------------------------------------------------------
+function real_config_query_list () {
+ local ANSWER
+ local VARIABLE="$1"
+ local QUESTION="$2"
+ shift
+ shift
+
+ if config_get_option "$VARIABLE" ANSWER; then
+ # option allready ANSWERed in config
+ (
+ for foo in "$@"; do
+ [ "$foo" == "$ANSWER" ] && exit 0
+ done
+ echo "!!!! WARNING !!!!"
+ echo "!!!! stored option '$ANSWER' in config is not in list of provided options !!!!"
+ echo "!!!! WARNING !!!!"
+ )
+ echo -e "[[ ${QUERY_COLOR}${QUESTION}${DEFAULT} -> '${QUERY_COLOR}$ANSWER${DEFAULT}' ]]"
+ else
+ # if there was an answer before, find it
+ local default default_num=0 foo
+ config_get_last_option "$VARIABLE" default
+ [[ $default ]] || default="$1"
+ let i=0
+ for foo in "$@"; do
+ if [[ "$foo" == "$default" ]] ; then
+ default_num=$i
+ break
+ fi
+ let i++
+ done
+
+ # we have to ask user
+ message "$QUESTION"
+ select_list ANSWER ${default_num} "$@"
+ config_set_option "$VARIABLE" "$ANSWER"
+ fi
+
+ eval $VARIABLE=\"$ANSWER\"
+ return 0
+}
+
+#---------------------------------------------------------------------
+## Output a list of source numbers associated with the current spell.
+## This is the number portion of SOURCE[[:digit:]], eg '', "2", "3", etc.
+## A prefix may be given and it will be prepended to each result value.
+#---------------------------------------------------------------------
+function real_get_source_nums() {
+ local foo
+ compgen -v SOURCE |
+ grep '^SOURCE[[:digit:]]*$'|sort|uniq|
+ while read foo; do echo "${1}${foo/SOURCE/}"; done
+}
+
+function get_spell_files() {
+ for src in $(real_get_source_nums SOURCE); do
+ echo ${!src}
+ done
+}
+
+
+#---------------------------------------------------------------------
+## misc_is_function <function name>
+## @param function name
+## @return 0 if argument is a valid function
+## @return 1 otherwise
+## Returns true if input argument is the name of an
+## existing function, false otherwise.
+##
+#---------------------------------------------------------------------
+function misc_is_function() {
+ local FUNCTION_NAME=$1
+ [ "`type -t $FUNCTION_NAME`" = "function" ]
+}
+
+
+#---------------------------------------------------------------------
+## Remove files listed and any directories which become empty after
+## subsequent file removal.
+##
+## @param Filename to read as input, a pipe may not be used.
+## @param Base directory to remove directories up to (usually $INSTALL_ROOT)
+#---------------------------------------------------------------------
+function remove_files_and_dirs() {
+ cat $1 | while read file; do
+ test -d $file || rm -f $file
+ done
+
+ # remove possibly empty directories, rmdir WILL have error output
+ # because some directories wont be empty for one of many reasons,
+ # this is OKAY
+ cat $1|dirnames|while read dir; do
+ until [[ $dir == ${2:-/} ]] ; do
+ rmdir $dir &>/dev/null || break
+ dir=$(dirname $dir)
+ done
+ done
+}
+
+#---------------------------------------------------------------------
+## Safely creates $TMP_DIR and exports the variable so we can use it
+## even in subprocesses called through make.
+## @param name of the script needing the tmp dir
+#---------------------------------------------------------------------
+function mk_tmp_dirs() {
+ debug "$FUNCNAME" "Making tmp dirs for $$"
+ local SCRIPT_NAME=$1 STATS tmp
+ SCRIPT_NAME=${SCRIPT_NAME:-misc}
+
+ local BASE_DIR=${2:-/tmp/sorcery}
+ local SCRIPT_DIR=$BASE_DIR/$SCRIPT_NAME
+ local FULL_DIR=$SCRIPT_DIR/$$
+
+
+
+ tmp=$(ls -ld $BASE_DIR 2>/dev/null|awk '{printf "%s:%s:%s\n" ,$1,$3,$4; }')
+ if [[ "$tmp" != "drwxr-xr-x:root:root" ]] ; then
+ test -d $BASE_DIR &&
+ message "$BASE_DIR has unknown permissions and ownership, replacing it" 2>/dev/null
+ rm -rf $BASE_DIR
+ install -d -o root -g root -m 755 "$BASE_DIR" &&
+ chmod a-s "$BASE_DIR" # work around installer bug where /tmp is suid/sgid
+ fi || mk_tmp_dir_error $BASE_DIR
+
+ # double check
+ tmp=$(ls -ld $BASE_DIR 2>/dev/null|awk '{printf "%s:%s:%s\n" ,$1,$3,$4; }')
+ if [[ "$tmp" != "drwxr-xr-x:root:root" ]] ; then
+ mk_tmp_dir_error $BASE_DIR
+ fi
+
+
+ tmp=$(ls -ld $SCRIPT_DIR 2>/dev/null|awk '{printf "%s:%s:%s\n" ,$1,$3,$4; }')
+ if [[ "$tmp" != "drwxr-xr-x:root:root" ]] ; then
+ rm -rf $SCRIPT_DIR
+ install -d -o root -g root -m 755 "$SCRIPT_DIR"
+ fi || mk_tmp_dir_error $SCRIPT_DIR
+
+ # double check
+ tmp=$(ls -ld $SCRIPT_DIR|awk '{printf "%s:%s:%s\n" ,$1,$3,$4; }')
+ if [[ "$tmp" != "drwxr-xr-x:root:root" ]] ; then
+ mk_tmp_dir_error $SCRIPT_DIR
+ fi
+
+
+ if [ -e $FULL_DIR ] ; then
+ echo "Looks like you had an old $SCRIPT_NAME on PID $$. Cleaning it out..." 2>/dev/null
+ rm -rf $FULL_DIR
+ fi
+
+ install -d -o root -g root -m 755 "$FULL_DIR" || mk_tmp_dir_error $FULL_DIR
+ tmp=$(ls -ld $SCRIPT_DIR|awk '{printf "%s:%s:%s\n" ,$1,$3,$4; }')
+ if [[ "$tmp" != "drwxr-xr-x:root:root" ]] ; then
+ mk_tmp_dir_error $FULL_DIR
+ fi
+
+ # in order for TMP_DIR to make it through make and into pass_three/four
+ # we must export
+ export TMP_DIR=$FULL_DIR
+}
+
+# emergency exit function if we fail to make a TMP_DIR
+function mk_tmp_dir_error() {
+ message "Failed to make temporary dir, this" \
+ "might be due to a security attack,\nplease" \
+ "check the permissions of $1. Bailing out..." 2>/dev/null
+ exit 1
+}
+
+#---------------------------------------------------------------------
+##
+## 'which' is not in basesystem, heres our own simple version of it
+## it should be just as fast as the real one.
+##
+## @param executable to look for
+## @param variable to return path in (pass by reference)
+##
+## Marches through $PATH looking for the executable specified
+##
+#---------------------------------------------------------------------
+function smgl_which() {
+ local target=$1 location
+ local BREAK
+ function smgl_which_sub() {
+ if test -x "$1/$target" ; then
+ echo "$1/$target"
+ BREAK=yes
+ fi
+ }
+ if [ -z "$1" -o -z "$2" ]; then
+ echo "smgl_which: Not enough arguments"
+ return 1
+ fi
+ location=$(iterate smgl_which_sub : "$PATH")
+ if [[ "$location" ]] ; then
+ eval "$2=$location"
+ else
+ echo "which: no $target in ($PATH)"
+ return 1
+ fi
+}
+
+#---------------------------------------------------------------------
+##
+## Finds the make command and complains if its missing (shouldnt ever
+## happen)
+##
+## @param variable to return path in (pass by reference)
+##
+#---------------------------------------------------------------------
+function find_make() {
+ local ___REAL_MAKE
+ smgl_which make ___REAL_MAKE
+ rc=$?
+ if [[ $rc != 0 ]] ; then
+ message "Cannot find make command!!"
+ message "Bailing out"
+ return $rc
+ fi
+ eval "$1=$___REAL_MAKE"
+}
+
+
+#---------------------------------------------------------------------
+## @param tmp_dir, should begin with /tmp/sorcery
+#---------------------------------------------------------------------
+function cleanup_tmp_dir() {
+ local TMP_DIR=$1
+ if [ ${TMP_DIR:0:13} == "/tmp/sorcery/" ]; then
+ rm -rf $TMP_DIR
+ else
+ message "Cowardly refusing to remove $TMP_DIR, this may be a bug," \
+ "please alert the sorcery team."
+ fi
+}
+
+#---------------------------------------------------------------------
+## @param Variable to check if its a known sorcery variable
+## @return Success if its a sorcery variable, false if not
+##
+## This function will probably need to be updated as sorcery variables
+## come in and out of flux, the variables listed are roughly defined as
+## things defined at the point in which a spell file is run, and when it
+## is not run in a seperate subshell, note that the build phase of cast
+## is seperate from the frontend, so a spell could technically modify
+## something like "SPELLS" and not have a major problem. However the
+## philosophy is to not discriminate about what sorcery variables
+## are technically okay to use in what files and ones that arent,
+## instead we'll treat all usages of sorcery variables equally. The rules
+## are of course subject to change without warning and its just easier
+## to be consistent, also theres a good chance that if a variable is safe
+## in some circumstances and not others, and one uses it where its safe,
+## someone will forget and start using it in places that arent safe...
+#---------------------------------------------------------------------
+function is_sorcery_var() {
+ local vars="TOP_LEVEL DISPLAY PATH TMP_DIR SAFE_CAST \
+ FAILED_LIST SUCCESS_LIST SPELL SPELLS spells MAKEFILE DEPS_ONLY \
+ CAST_PASS download_log IW_LOG OPTS SOLO QUIET INSTALL_QUEUE \
+ OVERRIDE_CFLAGS OVERRIDE_CXXFLAGS OVERRIDE_LDFLAGS NO_OPTIMIZATION_FLAGS \
+ DOT_PROGRESS VOYEUR_OVERRIDE RECONFIGURE RECAST_DOWN COMPILE RECAST_UP \
+ FORCE_DOWNLOAD SOURCE_CACHE SILENT FIX DEBUG SEPARATE BASE_URL \
+ CAST_HASH BACK_CAST_HASH CANNOT_CAST_HASH uncommitted_hash NEW_DEPENDS \
+ spell_depends DEPENDS_CONFIG UP_DEPENDS SPELL_CONFIG GRIMOIRE_DIR \
+ SCRIPT_DIRECTORY SECTION_DIRECTORY GRIMOIRE SPELL_DIRECTORY SECTION \
+ BUILD_API VOYEUR_STDOUT VOYEUR_STDERR C_LOG C_FIFO INSTALL_ROOT HOST \
+ BUILD INST_LOG MD5_LOG INSTALLWATCHFILE CAST_EXIT_STATUS"
+
+ # the \\< \\> matches word boundaries
+ echo $vars|grep -q "\\<$1\\>"
+}
+
+
+#---------------------------------------------------------------------
+## @param Variable name used in complaint
+## @stdout Complain vehemently that a variable name is used by sorcery
+## and the user should file a bug because a spell is using the variable
+#---------------------------------------------------------------------
+function complain_sorcery_var() {
+ message "${PROBLEM_COLOR}WARNING: ATTEMPTING TO USE" \
+ "${DEFAULT_COLOR}${SPELL_COLOR}\"$1\"" \
+ "${DEFAULT_COLOR}${PROBLEM_COLOR}AS A SPELL VARIABLE."
+ message "THIS VARIABLE IS A USED BY SORCERY, THIS IS PROBABLY" \
+ "A SPELL BUG\nAND SORCERY MAY BEHAVE IN UNDEFINED WAYS IF" \
+ "YOU CONTINUE."
+ message "\n\nPLEASE REPORT A BUG IMMEDIATLY!!${DEFAULT_COLOR}\n\n"
+}
+
+#-------------------------------------------------------------------
+## @param (optional) Architecture to use
+## Sets the SPECFILE glocal variable and the SMGL_COMPAT_ARCHS global array
+## SPECFILE contains the compiler and other arch specifications
+## SMGL_COMPAT_ARCHS is an array that holds architectures which are compatible
+## with the desired architecture. The desired architecture is determined as
+## follows:
+## 1) If function is gien an argument, the argument is used, or
+## 2) If cross-install is on, the TARGET architecture is used, or
+## 3) The local ARCHITECTURE is used
+##
+## The least specific arch is in SMGL_COMPAT_ARCHS[0],
+## SMGL_COMPAT_ARCHS[1] is more specific, et cetera. For example:
+## desired architecture="athlon-xp" might result in:
+## SPECFILE=/usr/share/archspecs/ia32/amd/athlon-xp
+## SMGL_COMPAT_ARCHS=("ia32" "amd" "athlon-xp")
+##
+## ARCHITECTURE is also modified to be an array, the reverse of
+## SMGL_COMPAT_ARCHS. The result is an array from most specific arch to least
+## specific. $ARCHITECTURE does not change meaning since $A == ${A[0]}.
+#-------------------------------------------------------------------
+function set_architecture() {
+
+ $STD_DEBUG
+ local specdir
+ local i j
+ unset SPECFILE
+
+ # If given an argument, treat as the architecture to use
+ local arch=${1}
+
+ # If no arch is specified, see if this is a cross-install, if so, set arch to
+ # the target arch
+ if [[ ! $arch ]] &&
+ [[ $CROSS_INSTALL == on ]] &&
+ [[ $TARGET ]]
+ then
+ debug "libmisc" "set_architecture: using cross-install's target arch"
+ arch=${arch:-$TARGET}
+ fi
+
+ # If no arch given and this isn't a cross-install, then default to the ARCHITECTURE var
+ if ! [[ $arch ]] ; then
+ arch=$ARCHITECTURE
+ fi
+
+ # Find the specfile to use
+ local find_compat=0
+ find --version|grep -q 'version 4\.1\.' && find_compat=1
+ for specdir in ${ARCH_SPECS[@]} ; do
+ if [[ $find_compat == 1 ]] ; then
+ # older find still exists run in slower compatibility mode
+ SPECFILE=$(find ${specdir} -perm -400 -type f -name "$arch" -print 2>/dev/null)
+ else
+ # -L so symlinks work, -type f so it won't match dirs, -quit so it
+ # stops searching when it finds the answer
+ SPECFILE=$(find -L ${specdir} -perm -400 -type f -name "$arch" -print -quit 2>/dev/null)
+ fi
+ [ $SPECFILE ] && break
+ done
+ if [[ ! $SPECFILE ]] ; then
+ message "${PROBLEM_COLOR}Cannot find arch spec for $arch!"
+ return 1
+ fi
+ debug "libmisc" "set_architecture: SPECFILE=$SPECFILE"
+
+ # turn the path into an array, but remove $specdir from the start first
+ unset SMGL_COMPAT_ARCHS
+ explode "${SPECFILE#$specdir/}" '/' SMGL_COMPAT_ARCHS
+
+ unset ARCHITECTURE
+ # Reverse the array so that the most specific arch is first
+ j=0
+ for(( i=${#SMGL_COMPAT_ARCHS[@]}-1; i>=0; i--)) ; do
+ ARCHITECTURE[j++]=${SMGL_COMPAT_ARCHS[i]}
+ done
+
+ source "$SPECFILE"
+}
+
+#---------------------------------------------------------------------
+## @param Function body
+## @param Function name
+## @param (optional) Function name
+## @param ...
+## Creates functions with identical bodies. It is useful if you need
+## to override a bunch of functions which have already been defined.
+#---------------------------------------------------------------------
+function define_functions() {
+ local funcName
+ local funcContent="$1"
+ shift
+ for funcName in $* ; do
+ debug "libmisc" "define_functions - redefining $funcName"
+ eval "function $funcName () { \
+ $funcContent \
+ }"
+ done
+}
+
+# Standard debug line:
+# file "function@line" "all" "args"
+STD_DEBUG='eval local _stddbg_file=${BASH_SOURCE[0]} ;
+ _stddbg_file=${_stddbg_file##*/};
+ debug "${_stddbg_file}" "${FUNCNAME[0]}@$LINENO" "$@"'
+
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libresurrect b/var/lib/sorcery/modules/libresurrect
new file mode 100644
index 0000000..47546f0
--- /dev/null
+++ b/var/lib/sorcery/modules/libresurrect
@@ -0,0 +1,666 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+## @Synopsis Functions for dealing with resurrecting a spell
+##
+## @Copyright Copyright (C) 2005 The Source Mage Team
+## &lt;http://www.sourcemage.org&gt;
+##
+## @Globals TBD
+##
+#---------------------------------------------------------------------
+
+
+#---------------------------------------------------------------------
+## @param spellname
+## determines if a particular version of a spell can be cast
+## test -f $INSTALL_CACHE/$SPELL-$VERSION-$HOST.tar.bz2
+#---------------------------------------------------------------------
+function can_resurrect() {
+ debug "libresurrect" "can_resurrect - $*"
+ local SPELL=$1
+ local VERSION=$2
+
+ # FIXME accept files of other extensions
+ local file=$INSTALL_CACHE/$SPELL-$VERSION-$HOST.tar$EXTENSION &&
+ test -f $file &&
+ echo $file &&
+ debug "libresurrect" "I am able to resurrect." &&
+ return 0
+}
+
+#------------------------------------------------------------------------
+# @param spell
+# @param version
+#
+# High level overview:
+#
+# ensure theres a cache tarball
+#
+# unpack the tarball somewhere
+#
+# if the spell is installed backup everything
+#
+# split the files into three categories: state data, config files, regular files
+# this can be roughly viewed as (its slightly more complicated)
+# state data is data owned by sorcery (/var/state)
+# config files are defined in the /var/lib/sorcery/configs file
+# things not in the above categories are regular files
+#
+# copy regular files onto the system
+# copy config files carefully (dont trample on user changes)
+# if the file does not exist, just copy the new one
+# elif the file exists and is identical to the new one, do nothing
+# else the file exists and differs from the new one then
+# if it is owned by the old spell with a valid md5
+# replace the file
+# elif it is owned by the old spell with an invalid md5 (admin changed it)
+# ask if the user wants to change it
+# else the file might be owned by another spell, or might be an alien
+# ask if the user wants to change it
+#
+# if the spell was installed
+# remove regular files unique to the old spell
+# remove unchanged config files
+# ask about changed config files
+#
+# update state data
+# call it a day
+#
+# FIXME: some of the informative message calls might work better as debug messages
+#------------------------------------------------------------------------
+function resurrect() { (
+ debug "libresurrect" "resurrect - $*"
+ local SPELL=$1
+ local VERSION=$2
+
+ spell_held $SPELL && {
+ message "Refusing to resurrect a held spell, please unhold it first"
+ return 1
+ }
+
+ if [[ ! $VERSION ]] ; then
+ debug libresurrect "No spell version passed in"
+ return 1
+ fi
+
+ # 1) if no cache file exists fail
+
+ local CACHE_COMP=$(can_resurrect $SPELL $VERSION) || {
+ message "No cache file could be found"
+ return 1
+ }
+
+ if test -z $CACHE_COMP || ! test -f $CACHE_COMP ; then
+ message "bug in can_resurrect, failing!"
+ return 1
+ fi
+
+ # 2) become king of the hill
+ acquire_cast_lock
+
+ # 3) setup resurrect sandbox
+
+ local RESURRECT_DIR=$BUILD_DIRECTORY/resurrect-$SPELL-$VERSION
+
+ mkdir -p $BUILD_DIRECTORY &&
+ mk_source_dir $RESURRECT_DIR || {
+ debug "libresurrect" "Failed to make $RESURRECT_DIR"
+ resurrect_fail
+ return 1
+ }
+
+ pushd $RESURRECT_DIR &>/dev/null || {
+ message "Failed to change directories to $RESURRECT_DIR"
+ resurrect_fail
+ return 1
+ }
+
+ # 4) unpack tarball to resurrect dir or fail
+ # note: tarballs are cached relative to $INSTALL_ROOT and $STATE_ROOT
+ # eg, with no special prefix, however install logs are relative to
+ # track_root and state root (isnt this fun?)
+
+ # FIXME get a generic routine to unpack this stuff
+ if [ -n "$EXTENSION" ]; then
+ $COMPRESSBIN -cd $CACHE_COMP | tar -x 1>/dev/null 2>&1
+ else
+ tar -xf $CACHE_COMP 1>/dev/null 2>&1
+ fi || {
+ message "Failed to unpack $CACHE_COMP"
+ resurrect_fail
+ return 1
+ }
+
+ # 5) if the spell is installed, take its install/md5 logs, and normalize them
+ # every way possible so I dont have to think about it later
+ local installed
+ if spell_ok $SPELL ; then
+ installed=yes
+ # note if installed is not yes, none of this should be assumed to exist
+
+ # welcome to the first ring of pathname adjustment hell
+ local OLD_SPELL_VERSION=$(installed_version $SPELL)
+ local OLD_INSTALL_LOG=${INSTALL_LOGS}/$SPELL-$OLD_SPELL_VERSION
+ local OLD_MD5_LOG=${MD5SUM_LOGS}/$SPELL-$OLD_SPELL_VERSION
+
+ local OLD_INSTALL_LOG_F=$TMP_DIR/old.install.$SPELL.filterable
+ local OLD_INSTALL_LOG_R=$TMP_DIR/old.install.$SPELL.root
+
+ local OLD_DATA_F=$TMP_DIR/old.data.$SPELL.filterable
+ local OLD_DATA_R=$TMP_DIR/old.data.$SPELL.root
+ local OLD_DATA_F_C=$TMP_DIR/old.data.$SPELL.filterable.config
+ local OLD_DATA_R_C=$TMP_DIR/old.data.$SPELL.root.config
+ local OLD_DATA_F_NC=$TMP_DIR/old.data.$SPELL.filterable.nonconfig
+ local OLD_DATA_R_NC=$TMP_DIR/old.data.$SPELL.root.nonconfig
+
+ local OLD_MDATA_F=$TMP_DIR/old.mdata.$SPELL.filterable
+ local OLD_MDATA_R=$TMP_DIR/old.mdata.$SPELL.root
+
+ local OLD_MD5_LOG_F=$TMP_DIR/old.md5.$SPELL.filterable
+ local OLD_MD5_LOG_R=$TMP_DIR/old.md5.$SPELL.root
+
+ if test -e $OLD_INSTALL_LOG ; then
+ log_adjuster $OLD_INSTALL_LOG $OLD_INSTALL_LOG_F log filterable
+ log_adjuster $OLD_INSTALL_LOG $OLD_INSTALL_LOG_R log root
+ # dont include the logs in meta-data
+ seperate_state_files $OLD_INSTALL_LOG_F $OLD_DATA_F /dev/stdout |
+ grep -v "$LOG_DIRECTORY" > $OLD_MDATA_F
+ log_adjuster $OLD_DATA_F $OLD_DATA_R filterable root
+ cat $OLD_DATA_F| filter_configs -v > $OLD_DATA_F_C
+ log_adjuster $OLD_DATA_F_C $OLD_DATA_R_C filterable root
+ cat $OLD_DATA_F| filter_configs > $OLD_DATA_F_NC
+ log_adjuster $OLD_DATA_F_NC $OLD_DATA_R_NC filterable root
+ log_adjuster $OLD_MDATA_F $OLD_MDATA_R filterable root
+ fi
+
+ if test -e $OLD_MD5_LOG ; then
+ log_adjuster $OLD_MD5_LOG $OLD_MD5_LOG_F log filterable md5_log_filter
+ log_adjuster $OLD_MD5_LOG $OLD_MD5_LOG_R log root md5_log_filter
+ fi
+ fi
+
+ # 5) if theres a tablet in the cache, load it, otherwise load the
+ # "regular" spell info
+ # load spell or tablet
+
+ # notice the magic dot
+ local TABLET_PATH=.${TABLET_PATH#$STATE_ROOT}
+ local OLD_TABLET_DIR
+ local SPELL_CONFIG_FILTER
+ local SECTION_CONFIG_FILTER
+ local GRIMOIRE_CONFIG_FILTER
+ tablet_find_resurrect_dir $SPELL OLD_TABLET_DIR
+ local TMP_VERSION=$VERSION
+ if [[ $OLD_TABLET_DIR ]] && test -d $OLD_TABLET_DIR ; then
+ debug "libresurrect" "loading tablet at $OLD_TABLET_DIR"
+ tablet_set_spell $SPELL $OLD_TABLET_DIR || {
+ message "something is wrong with $OLD_TABLET_DIR"
+ returrect_fail
+ return 1
+ }
+ tablet_get_spell_filter "$OLD_TABLET_DIR" configs SPELL_CONFIG_FILTER
+ tablet_get_grimoire_filter "$OLD_TABLET_DIR" configs SECTION_CONFIG_FILTER
+ tablet_get_section_filter "$OLD_TABLET_DIR" configs GRIMOIRE_CONFIG_FILTER
+ else
+ unset $OLD_TABLET_DIR
+ debug "libresurrect" "no tablet"
+ codex_set_current_spell_by_name $SPELL
+ SPELL_CONFIG_FILTER=$SCRIPT_DIRECTORY/configs
+ SECTION_CONFIG_FILTER=$SECTION_DIRECTORY/configs
+ GRIMOIRE_CONFIG_FILTER=$GRIMOIRE/configs
+ # loading the spell may give us a different version which we dont want
+ fi
+ local CONFIG_FILTERS="$GRIMOIRE_CONFIG_FILTER $SECTION_CONFIG_FILTER $SPELL_CONFIG_FILTER"
+ VERSION=${VERSION:-TMP_VERSION}
+
+ # 6) find the cache's install log and divide it up every way imaginable
+ if ! [[ $OLD_TABLET_DIR ]] ; then
+ # in this case we know nothing about how the install log was built, so
+ # we have to make one up
+ local NEW_INSTALL_LOG=$TMP_DIR/new.install.log
+ find . | sed 's/^.//'|
+ log_adjuster /dev/stdin $NEW_INSTALL_LOG filterable log
+ else
+ local NEW_INSTALL_LOG=${INSTALL_LOGS#$STATE_ROOT/}/$SPELL-$VERSION
+ test -f $NEW_INSTALL_LOG || {
+ message "No install log found in unpacked cache, expected $INSTALL_LOG"
+ resurrect_fail
+ return 1
+ }
+ fi
+
+ # welcome back, did you get cold outside?
+ local NEW_INSTALL_LOG_F=$TMP_DIR/new.install.$SPELL.filterable
+ local NEW_INSTALL_LOG_R=$TMP_DIR/new.install.$SPELL.root
+
+ local NEW_DATA_F=$TMP_DIR/new.data.$SPELL.filterable
+ local NEW_DATA_R=$TMP_DIR/new.data.$SPELL.root
+ local NEW_DATA_F_C=$TMP_DIR/new.data.$SPELL.config.filterable
+ local NEW_DATA_R_C=$TMP_DIR/new.data.$SPELL.config.root
+ local NEW_DATA_F_NC=$TMP_DIR/new.data.$SPELL.nonconfig.filterable
+ local NEW_DATA_R_NC=$TMP_DIR/new.data.$SPELL.nonconfig.root
+
+ local NEW_MDATA_F=$TMP_DIR/new.mdata.$SPELL.filterable
+ local NEW_MDATA_R=$TMP_DIR/new.mdata.$SPELL.root
+
+ log_adjuster $NEW_INSTALL_LOG $NEW_INSTALL_LOG_F log filterable
+ log_adjuster $NEW_INSTALL_LOG $NEW_INSTALL_LOG_R log root
+ seperate_state_files $NEW_INSTALL_LOG_F $NEW_DATA_F $NEW_MDATA_F
+ log_adjuster $NEW_DATA_F $NEW_DATA_R filterable root
+ # cannot use filter_configs here because the tablet is in a
+ # non-standard location and may not exist at all...
+ cat $NEW_DATA_F| filter_in $CONFIGS $CONFIG_FILTERS > $NEW_DATA_F_C
+ log_adjuster $NEW_DATA_F_C $NEW_DATA_R_C filterable root
+ cat $NEW_DATA_F| filter $CONFIGS $CONFIG_FILTERS > $NEW_DATA_F_NC
+ log_adjuster $NEW_DATA_F_NC $NEW_DATA_R_NC filterable root
+ log_adjuster $NEW_MDATA_F $NEW_MDATA_R filterable root
+
+ # now that we have the install logs from the cache sorted out we can
+ # restore these which may have been overridden by tablet
+ tablet_unload_roots
+
+ # 7) take install lock
+ lock_resources "libgrimoire" "install"
+
+ # 8) backup the installed version of the spell
+ if [[ $installed == yes ]] && test -e $OLD_INSTALL_LOG ; then
+ # TODO evaluate the usefulness of this (see note in resurrect_fail
+ #local SAVE_DIR=$RESURRECT_DIR/save.dir.$$
+ #backup_spell $SPELL $SAVE_DIR $OLD_INSTALL_LOG_R || {
+ #message "Failed to backup old installation"
+ #resurrect_fail
+ #return 1
+ #}
+ # its okay to remove this stuff because its presumably been backed up
+ remove_files_and_dirs $OLD_MDATA_R $STATE_ROOT
+ fi
+
+ # 9) remove conflicts, but not self-conflicts, they make no sense
+ run_conflicts $SPELL || {
+ resurrect_fail
+ return 1
+ }
+
+ #####
+ # Start of critical region
+ #####
+
+ resurrect_sub || return $?
+
+ #####
+ # End of critical region
+ #####
+
+ resurrect_success
+ unlock_resources "libgrimoire" "install"
+ unlock_resources "cast" "$SPELL"
+ unlock_resources "solo" "cast"
+
+ # do a cleanse --fix if requested (FIXME make this turn off-able)
+ #cleanse --fix $SILENT $SPELL
+
+ # n) run triggers
+ #run_triggers
+
+ return 0
+) }
+
+
+function resurrect_sub() {
+ # 10) if theres a PRE_RESURRECT run it or fail/rollback
+ if test -x $SCRIPT_DIRECTORY/PRE_RESURRECT ; then
+ . $SCRIPT_DIRECTORY/PRE_RESURRECT || {
+ debug "libresurrect" "PRE_RESURRECT failed"
+ resurrect_fail 1
+ return 1
+ }
+ fi
+
+
+ # 11) for each non-config file, all old files are already backed up
+ # tar is nice enough to preserve permissions, atime, etc. for us, using
+ # mkdir and cp it isnt so easy, this also has the advantage of not
+ # totally screwing us if we stomp on some critical libraries.
+ # ld-linux.so.2 anyone?
+ local res_fail=0
+ cat $NEW_DATA_F_NC | while read line; do
+ # filterable data also has the clever trait that to make it relative
+ # in this context we prepend a .
+
+ # tar doesnt like directories, but symlinks to directories pass
+ # test -d so handle them specially
+ test -h ".$line" && echo .$line ||
+ test -d ".$line" || echo .$line
+ done | sort | tar -cT - | tar -xvf - -C ${INSTALL_ROOT:-/} || {
+ debug "libresurrect" "Failed to install regular files"
+ resurrect_fail 1
+ return 1
+ }
+ ldconfig # can never be too careful
+
+ # 12) carefully install config files (save old ones too), or fail/rollback
+ # this is interactive and so we must use iterator
+ local savetime=$(date +'%Y%m%d%H%M')
+
+ local res_fail=0 BREAK
+ function resurrect_install_conf_sub() {
+ debug libresurrect "resurrect_install_conf_sub -- $@"
+ test -d ".$1" ||
+ internal_install_config_file ".$1" "${INSTALL_ROOT}${1}" $savetime \
+ "$OLD_MD5_LOG" || {
+ res_fail=1
+ BREAK=1
+ }
+ }
+ iterate resurrect_install_conf_sub $'\n' "$(<$NEW_DATA_F_C)"
+
+ [[ $res_fail == 0 ]] || {
+ debug "libresurrect" "Failed to install config files"
+ resurrect_fail 1
+ return 1
+ }
+ ldconfig # paranoia paranoia
+
+ # 13) remove un-replaced files
+ if [[ $installed == yes ]] && test -e $OLD_INSTALL_LOG ; then
+ # show what is in arg 1 but isnt in arg 2
+ # this handles non-configs which can be removed
+ awk -F : 'BEGIN {
+ while (getline < ARGV[2] ) { fc[$1]=1; }
+ while (getline < ARGV[1] ) { if( fc[$1] != 1 ) { print $1; } }
+ }' $OLD_DATA_F_NC $NEW_DATA_F_NC |
+ log_adjuster /dev/stdin $TMP_DIR/leftover.$SPELL.non-config filterable root
+
+ # clear out old files/symlinks
+ remove_files_and_dirs $TMP_DIR/leftover.$SPELL.non-config "$INSTALL_ROOT"
+
+ # this handles configs which must be backed up if they've changed
+ awk -F : 'BEGIN {
+ while (getline < ARGV[2] ) { fc[$1]=1; }
+ while (getline < ARGV[1] ) { if( fc[$1] != 1 ) { print $1; } }
+ }' $OLD_DATA_F_C $NEW_DATA_F_C |
+ log_adjuster /dev/stdin $TMP_DIR/leftover.$SPELL.config filterable root
+ touch $TMP_DIR/leftover.$SPELL.config
+ while read file; do
+ if check_if_modified "$OLD_MD5_LOG"; then
+ reap_modified_file $file
+ fi
+ done < $TMP_DIR/leftover.$SPELL.config
+ fi
+
+ # 14) if theres a POST_RESURRECT run it
+ if test -x $SCRIPT_DIRECTORY/POST_RESURRECT ; then
+ . $SCRIPT_DIRECTORY/POST_RESURRECT || {
+ message "POST_RESURRECT failed"
+ resurrect_fail 1
+ return 1
+ }
+ fi
+ ldconfig # can never be too careful
+}
+
+
+#------------------------------------------------------------------------
+## @param from File we might want to install
+## @param to File to replace
+## @param savetime Backup time if necessary (optional, defaults to
+## $(date +'%Y%m%d%H%M')
+#------------------------------------------------------------------------
+function real_install_config_file() {
+ if [[ ! $1 ]] || [[ ! $2 ]] ; then
+ message "${PROBLEM_COLOR}Missing values for arguments \"$1\" or \"$2\"" \
+ "$DEFAULT_COLOR"
+ return 1
+ fi
+
+ local old_md5_log
+ if [[ $OLD_SPELL_VERSION ]] ; then
+ old_md5_log=$MD5SUM_LOGS/$SPELL-$OLD_SPELL_VERSION
+ # log must be in filterable form
+ log_adjuster "$old_md5_log" "$TMP_DIR/$SPELL.md5" filterable root
+ else
+ old_md5_log=/dev/null
+ fi
+ local savetime=${3:-$(date +'%Y%m%d%H%M')}
+ internal_install_config_file "$1" "$2" $savetime $TMP_DIR/$SPELL.md5
+ rc=$?
+ [[ $OLD_SPELL_VERSION ]] && rm $TMP_DIR/$SPELL.md5
+
+ return $rc
+}
+
+#------------------------------------------------------------------------
+## @param from File we might want to install
+## @param to File to replace
+## @param savetime Backup time if necessary
+## @param md5_log MD5 log for previous version of the spell,
+# must be in root form
+#------------------------------------------------------------------------
+function internal_install_config_file() {
+ local from=$1
+ local to=$2
+ local savetime=$3
+ local md5_log=$4
+
+ debug libresurrect "$FUNCNAME -- $@"
+ # if its a symlink, just copy, someday maybe we should figure
+ # out where it points to and so something, but thats too annoying
+ if test -h "$from" ; then
+ mkdir -p "$(dirname "$to")" &&
+ cp -pv --no-dereference "$from" "$to"
+ return $?
+ fi
+
+ # if the file does not exist,
+ if ! test -e "$to"; then
+ # copy the new one in (plus leading directories)
+ mkdir -p "$(dirname "$to")" &&
+ cp -pv "$from" "$to"
+
+ # to get here the file must exist
+ elif test -d "$to"; then
+ message "Trying to install a file ($from) to what was a directory ($to), I dont know what to do!"
+ return 1
+
+ # elif the file exists and is identical to the new one
+ elif cmp "$to" "$from" &>/dev/null; then
+ echo "$from"
+ # preserve modification status
+ if check_if_modified "$to" "$md5_log" ; then
+ mark_file_modified "$to"
+ fi
+ # else the file exists and differs from the new one then
+ else
+ # if it is owned by the old spell with a valid md5
+ # note that the md5_log is a subset of the install log, so if it
+ # matches in the md5 log, it is owned by the spell
+ if check_if_modified "$to" "$md5_log" ; then
+ # it is owned by the old spell with an invalid md5 OR
+ # the file might be owned by another spell OR
+ # might be an alien, all lead to the same direction
+ # note, it might be nice to split the above cases out for more informative
+ # user messages
+ real_handle_changed_config "$from" "$to" $savetime
+ else
+ # replace the file
+ mkdir -p "$(dirname "$to")" &&
+ cp -pv "$from" "$to"
+ fi
+ fi
+}
+
+#------------------------------------------------------------------------
+## @param from File we might want to install
+## @param to File to replace
+## @param savetime Backup time if necessary
+##
+## Present the user with the following menu
+## (0) trash $to and install over it
+## (1) backup $to to $to.$savetime, install the new file in its place
+## (2) leave $to in its place, copy the new file to $to.$savetime
+## (3) do nothing
+## (4) see a diff between $to and the new file
+## choice 2 is currently the default, someday there will be a menu to
+## choose what the default will be
+#------------------------------------------------------------------------
+function real_handle_changed_config() {
+ local from=$1
+ local to=$2
+ local savetime=$3
+ local number
+ local default=2 # TODO: this should come from a variable someday
+
+ local continue=yes
+ message "${MESSAGE_COLOR}Installing to $to, please choose an option:"
+ while [[ "$continue" == "yes" ]] ; do
+ message "${QUERY_COLOR}"
+ message "(0) trash $to and install over it"
+ message "(1) backup $to to $to.$savetime, install the new file in its place"
+ message "(2) leave $to in its place, copy the new file to $to.$savetime"
+ message "(3) do nothing"
+ message "(4) see a diff between $to and the new file"
+ # TODO: someday add an option to use an external merge tool
+
+ number=""
+ while [[ $number != [0-9]* ]] || (( " $number >= 5 " )); do
+ message -n "\n${QUERY_COLOR}Which one do you want? [$default]" \
+ "$DEFAULT_COLOR"
+ read -n 1 -t $PROMPT_DELAY number
+ if [[ ! -n $number ]]; then
+ number=$default
+ fi
+ done
+ echo
+ case $number in
+ 0) cp -pv "$from" "$to"
+ break ;;
+ 1) cp -pv "$to" "$to.$savetime"
+ cp -pv "$from" "$to"
+ break ;;
+ 2) cp -pv "$from" "$to.$savetime"
+ mark_file_modified "$to"
+ # this is deliberatly not track_manual, if this is run from
+ # FINAL the user might not have wanted the files tracked
+ # so we wont make an assumption on what the user intends
+ touch "$to"
+ break ;;
+ 3) mark_file_modified "$to"
+ touch "$to"
+ break ;;
+ 4) diff "$to" "$from" | $PAGER;;
+ esac
+ message "\n\nPlease select another option"
+ done
+
+}
+
+function resurrect_success() {
+
+ local INSTALL_LOG=$INSTALL_LOGS/$SPELL-$VERSION
+ local MD5_LOG=$MD5SUM_LOGS/$SPELL-$VERSION
+ local TMP_INSTALL_LOG_STATE=$TMP_DIR/$SPELL.tmp.ilog.state
+ local TMP_INSTALL_LOG_DATA=$TMP_DIR/$SPELL.tmp.ilog.data
+ local TMP_INSTALL_LOG=$TMP_DIR/$SPELL.tmp.ilog.all
+
+ # 14) copy compile log
+ # this file is not regenerated unlike its friends install and md5sum
+ local OLD_COMPILE_LOG=$(find .${COMPILE_LOGS#$INSTALL_ROOT}/ -maxdepth 1 -type f|head -n 1)
+ local COMPILE_LOG="$COMPILE_LOGS/$(basename "$OLD_COMPILE_LOG")"
+ # also relative to STATE_ROOT
+ cp "$OLD_COMPILE_LOG" "$COMPILE_LOG"
+
+ # 15) commit state data:
+ if [[ $OLD_TABLET_DIR ]] ; then
+ # depends info if it exists
+ local t_DEPENDS_STATUS=$(lock_start_transaction "$DEPENDS_STATUS")
+ # none of the old values are valid, only the new, uncommitted values are
+ remove_depends_status $t_DEPENDS_STATUS $SPELL
+ local spell_depends=$OLD_TABLET_DIR/depends
+ if [ -e $spell_depends ] ; then
+ cat $spell_depends >> $t_DEPENDS_STATUS
+ fi
+ lock_commit_transaction $DEPENDS_STATUS
+
+ # if theres a tablet dir, store it with a new timestamp
+ if new_tablet=$(tablet_get_path $SPELL) ; then
+ cp -Rp $OLD_TABLET_DIR/* $new_tablet
+ ln $INSTALL_LOG -sf $new_tablet/logs/install
+ ln $MD5_LOG -sf $new_tablet/logs/md5sum
+ ln $COMPILE_LOG -sf $new_tablet/logs/compile
+ find $new_tablet > $TMP_INSTALL_LOG_STATE # relative to STATE_ROOT
+ else
+ debug "libresurrect" "WARNING: unable to make tablet for $SPELL, oh well"
+ fi
+ fi
+
+
+ echo $COMPILE_LOG >> $TMP_INSTALL_LOG_STATE
+ echo $INSTALL_LOG >> $TMP_INSTALL_LOG_STATE
+ echo $MD5_LOG >> $TMP_INSTALL_LOG_STATE
+
+ # Use the cache's listing of files in filterable form
+ # dont use root form because it may be different
+ log_adjuster $NEW_DATA_F $TMP_INSTALL_LOG_DATA filterable root
+ cat $TMP_INSTALL_LOG_STATE $TMP_INSTALL_LOG_DATA > $TMP_INSTALL_LOG
+ log_adjuster $TMP_INSTALL_LOG $INSTALL_LOG root log
+
+ create_md5list $TMP_INSTALL_LOG /dev/stdout |
+ log_adjuster /dev/stdin $MD5_LOG root log md5_log_filter
+
+ # add packages line
+ add_spell "$SPELL" "installed" "$VERSION"
+
+ pop_install_queue "$SPELL"
+ echo $SPELL >> $SUCCESS_LIST
+
+ popd &>/dev/null
+
+ rm_source_dir $RESURRECT_DIR
+
+ clear_line
+ activity_log "resurrect" "$SPELL" "$VERSION" "success"
+ message "${RESURRECT_COLOR}Resurrected spell: ${SPELL_COLOR}${SPELL}" \
+ "${DEFAULT_COLOR} version ${VERSION_COLOR}${VERSION}" \
+ "${DEFAULT_COLOR}"
+}
+
+
+function resurrect_fail() {
+ message "${PROBLEM_COLOR}Resurrect failed for spell:" \
+ "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR} version" \
+ "${VERSION_COLOR}${VERSION}${DEFAULT_COLOR}"
+
+ #if [[ $1 == 1 ]] ; then
+ #TODO evaluate the usefulness of a backup/rollback scheme
+ # this would be the location in which to do rollback, however
+ # I think it adds needless complexity and can be dangerous
+ # also, it shouldnt be needed in a properly functioning resurrect
+ #fi
+
+ unlock_resources "libgrimoire" "install"
+ unlock_resources "cast" "$SPELL"
+ unlock_resources "solo" "cast"
+
+ [[ $CLEAN_SOURCE == on ]] && rm_source_dir $RESURRECT_DIR 2>/dev/null
+
+ echo $SPELL >> $FAILED_LIST
+}
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libscreen b/var/lib/sorcery/modules/libscreen
new file mode 100644
index 0000000..401a6cc
--- /dev/null
+++ b/var/lib/sorcery/modules/libscreen
@@ -0,0 +1,209 @@
+#!/bin/bash
+
+#-----
+## Output a short message about using libscreen
+#-----
+function screen_quick_intro() {
+ message "${MESSAGE_COLOR}Using screen mode. Watch the bottom for notices."
+ message "${MESSAGE_COLOR}Press ^A-n and ^A-p to cycle through windows."
+ message "${MESSAGE_COLOR}Screen help is available with ^A-? (control-a ?)${DEFAULT}"
+}
+
+#-----
+## @param Screen session name
+## @param Command
+## @param (optional) Command parameter
+## @param ...
+## THIS FUNCTION NEVER RETURNS.
+## It execs a screen session with the specified name, with the specified
+## arguments.
+#-----
+function screen_start() {
+ $STD_DEBUG
+ local name="$1"
+ local screen
+ shift
+ if ! smgl_which screen screen > /dev/null ; then
+ message "Cannot find screen."
+ message "Consider fixing the PATH, or disabling screen mode and casting screen."
+ exit 1
+ fi
+ exec $screen -S "$name" $SCREEN_KEY -c $SCREENRC "$@"
+}
+
+#-----
+## @param Screen session name
+## This function is not used, and probably won't be. It is a way to run
+## a command in a screen.
+## The .!!| args to exec were determined by trial and error. ::: didn't work.
+#-----
+function screen_command() {
+ $STD_DEBUG
+ local name="$1"
+ shift
+ screen -x "$name" -X exec '.!!|' echo "$@"
+ sleep $SCREEN_ANTI_RACE_SLEEP
+}
+
+#-----
+## @param Screen session name
+## @param Window type
+## @param Name of new window
+## @param Command
+## @param (optional) Command args
+## @param ...
+## Window type may be one of:
+## <li>SCREEN_CAST_WIN
+## <li>SCREEN_SUMMON_WIN
+## <li>SCREEN_MAIN_WIN
+## <li>SCREEN_LAST_FAILED_CAST_WIN
+## <li>SCREEN_DEBUG_WIN
+## Only one of each type is allowed to exist at once
+## They are actualy window numbers in disguise
+#-----
+function screen_new_window() {
+ $STD_DEBUG
+ local screen_name="$1"
+ local win_num="$2"
+ local win_name="$3"
+ shift 3
+ screen -x "$screen_name" -X screen "$win_num" "$@"
+ sleep $SCREEN_ANTI_RACE_SLEEP
+ screen_name_window "$screen_name" "$win_num" "$win_name"
+ screen_notify "$screen_name" "$win_name started"
+}
+
+#-----
+## @param Screen session name
+## Attaches the named screen session to the tty.
+## Doesn't work properly since screen doesn't like being run in the bg.
+## Default escape code is ^A.
+#-----
+function screen_attach() {
+ $STD_DEBUG
+ screen -r "$1" $SCREEN_KEY &
+}
+
+#-----
+## @param Screen session name
+## Detaches the named screen session from the tty.
+#-----
+function screen_detach() {
+ $STD_DEBUG
+ screen -x "$1" -X detach
+}
+
+#-----
+## @param Screen session name
+## @param Window number
+## Kills the window in the session. Also notifies the user that the window was
+## closed
+#-----
+function screen_kill_window() {
+ $STD_DEBUG
+ screen -x "$1" -p "$2" -X kill
+ sleep $SCREEN_ANTI_RACE_SLEEP
+ screen_notify "$1" "$2 ended"
+# screen_switch_window "$1" 0
+}
+
+#-----
+## @param Screen session name
+## @param Window number
+## @param New name
+## Give an existing window a new name
+#-----
+function screen_name_window() {
+ $STD_DEBUG
+ screen -x "$1" -p "$2" -X title "$3"
+ sleep $SCREEN_ANTI_RACE_SLEEP
+}
+
+#-----
+## @param Screen session name
+## Give aname to the current window.
+## There are race conditions aplenty if you're not careful
+#-----
+function screen_name_curr_window() {
+ $STD_DEBUG
+ screen -x "$1" -X title "$2"
+ sleep $SCREEN_ANTI_RACE_SLEEP
+}
+
+#-----
+## @param Screen session name
+## @param Message
+## Notify the user of something happening using the status bar
+#-----
+function screen_notify() {
+ $STD_DEBUG
+ screen -x "$1" -X echo "$@"
+ sleep $SCREEN_ANTI_RACE_SLEEP
+}
+
+#-----
+## @param Screen session name
+## @param Window to monitor
+## Sets a window to be monitored. The user will be informed of any activity
+## in the window.
+#-----
+function screen_monitor_window() {
+ $STD_DEBUG
+ screen -x "$1" -p "$2" -X monitor on
+ sleep $SCREEN_ANTI_RACE_SLEEP
+}
+
+#-----
+## @param Screen session name
+## @param Window to monitor
+## Stops a window from being monitored
+#-----
+function screen_unmonitor_window() {
+ $STD_DEBUG
+ screen -x "$1" -p "$2" -X monitor off
+ sleep $SCREEN_ANTI_RACE_SLEEP
+}
+
+#-----
+## @param Screen session name
+## @param Window to switch to
+## Changes the window the user sees
+#-----
+function screen_switch_window() {
+ $STD_DEBUG
+ screen -x "$1" -X select "$2"
+ sleep $SCREEN_ANTI_RACE_SLEEP
+}
+
+#-----
+## @param Screen session name
+## Ends the screen session. Should end all processes running in the screen.
+#-----
+function screen_quit() {
+ $STD_DEBUG
+ screen -x "$1" -X quit
+}
+
+#-----
+## @param Screen session name
+## @param Window to move to
+## @param Window number to move to
+## Moves a window to a new number
+## If something is already at that number, it swaps them
+#-----
+function screen_move_window() {
+ $STD_DEBUG
+ screen -x "$1" -p "$2" -X number "$3"
+ sleep $SCREEN_ANTI_RACE_SLEEP
+}
+
+#-----
+## @param Screen session name
+## @param Delay in seconds
+## Sets the time screen shows messages for
+#-----
+function screen_set_msgwait() {
+ $STD_DEBUG
+ screen -x "$1" -X msgwait $2
+ sleep $SCREEN_ANTI_RACE_SLEEP
+}
diff --git a/var/lib/sorcery/modules/libsecurity b/var/lib/sorcery/modules/libsecurity
new file mode 100644
index 0000000..b355e59
--- /dev/null
+++ b/var/lib/sorcery/modules/libsecurity
@@ -0,0 +1,323 @@
+#!/bin/bash
+
+#---------------------------------------------------------------------
+##
+## @Synopsis Set of functions for dealing with security related tasks.
+##
+## @Copyright
+##
+## Copyright 2002 by the Source Mage Team
+##
+##
+#---------------------------------------------------------------------
+
+
+
+#---------------------------------------------------------------------
+## @param spelldirectory
+## @param sourcenumber
+##
+## spelldirectory is the spell's directory.
+## sourcenumber is '' or '2', '3', '4', etc.
+## Checks the md5 of a single source file sourcenumber in spelldirectory.
+##
+#---------------------------------------------------------------------
+function gaze_checkmd5 () {
+ i=$1/DETAILS
+ SOURCEnum=$2
+ SOURCEvar=SOURCE$SOURCEnum
+
+ SOURCE=${!SOURCEvar}
+ if [ -n "$SOURCEnum" ]
+ then
+ MD5num="$(($SOURCEnum-1))"
+ else
+ MD5num="0"
+ fi
+ tMD5=${MD5[$MD5num]}
+
+ GRIMOIRE=`echo $i | sed -e 's/\/[^\/]*\/[^\/]*\/DETAILS//' -e 's/\/.*\///'`
+ SECTION=`echo $i | sed -e 's/\/[^\/]*\/DETAILS//' -e 's/\/.*\///'`
+ if test "$SECTION" == "$SPELL" || test "$SECTION" == "DETAILS"
+ then
+ echo -en "${SPELL_COLOR}$SPELL${DEFAULT} "
+ else
+ echo -en "$GRIMOIRE: $SECTION: ${SPELL_COLOR}$SPELL${DEFAULT} "
+ fi
+
+ REALSOURCE=
+ if test "$SOURCE" != ''
+ then
+ if [ -f $SOURCE_CACHE/$SOURCE ]
+ then
+ APPEND=
+ else
+ OLDESTSOURCE="$SOURCE"
+ OLDSOURCE="`echo $SOURCE | sed -e 's/\./\\\./g'`"
+ SOURCE="`echo $SOURCE | sed -e 's/\.[bt].*/\./'`"
+ SOURCE="`ls $SOURCE_CACHE/$SOURCE* 2> /dev/null | cut -d'/' -f 5 | head -n 1`"
+ if [ "$SOURCE" ]
+ then
+ if [ -z "`echo ${SOURCE#$OLDSOURCE.} | egrep '^[0-9]{14}$'`" ]
+ then
+ APPEND="FUZZ s/$OLDSOURCE/$SOURCE/ "
+ else
+ APPEND=
+ REALSOURCE="$OLDESTSOURCE"
+ fi
+ else
+ APPEND=
+ SOURCE="$OLDESTSOURCE"
+ fi
+ fi
+ if test -z "$PRECACHEDF"
+ then
+ if test `guess_compressor $SOURCE_CACHE/$SOURCE` != 'gzip' &&
+ test `guess_compressor $SOURCE_CACHE/$SOURCE` != 'bzip2' &&
+ test `guess_compressor $SOURCE_CACHE/$SOURCE` != 'compress'"'"'d' &&
+ test `guess_compressor $SOURCE_CACHE/$SOURCE` != 'Zip' &&
+ test `guess_compressor $SOURCE_CACHE/$SOURCE` != 'RPM' &&
+ test `guess_compressor $SOURCE_CACHE/$SOURCE` != 'tar'
+ then
+ fMD5=`cat $SOURCE_CACHE/$SOURCE 2> /dev/null | md5sum | cut -d' ' -f1 | head -n 1`
+ else
+ fMD5=$(
+ uncompress $SOURCE_CACHE/$SOURCE `
+ guess_compressor $SOURCE_CACHE/$SOURCE` |
+ md5sum | cut -d' ' -f1
+ )
+ fi
+ else
+ fMD5="`grep " $SOURCE\$" $PRECACHEDF | grep -Ev '\.[0-9]{14}$' | grep -v '\.asc$' | cut -d' ' -f1 | head -n 1`"
+ fi
+ if [ -n "$REALSOURCE" ]
+ then
+ SOURCE="$REALSOURCE"
+ fi
+ if [ -z "$(find $SOURCE_CACHE/$SOURCE -maxdepth 1 -mtime +3 2> /dev/null)" ]
+ then
+ AGE="${GREEN}NEW${DEFAULT}"
+ else
+ AGE="${YELLOW}${BOLD}OLD${DEFAULT}"
+ fi
+ SRCDATA="SOURCE$SOURCEnum L:'${LICENSE[$MD5num]}' $SOURCE${DEFAULT} $AGE"
+ if test "$fMD5" == "$tMD5"
+ then
+ echo -en "${GREEN}VERIFIED $SRCDATA${DEFAULT}"
+ else
+ if test "$fMD5" == "$EMPTYMD5" || test "$fMD5" == ""
+ then
+ if test "$tMD5" == "IGNORE"
+ then
+ echo -en "SKIPIGN $SRCDATA"
+ else
+ echo -en "SKIPPED $SRCDATA"
+ fi
+ else
+ if test "$tMD5" == ""
+ then
+ echo -en "${YELLOW}${BOLD}UNCHECKED $SRCDATA INSERT MD5[$MD5num]=$fMD5 or MD5[$MD5num]=IGNORE"
+ else
+ if test "$tMD5" == "IGNORE"
+ then
+ echo -en "${YELLOW}IGNORED $SRCDATA"
+ else
+ if test -z "$PRECACHEDB"
+ then
+ bMD5="`md5sum $SOURCE_CACHE/$SOURCE | cut -d' ' -f1` | head -n 1"
+ else
+ bMD5="`grep " $SOURCE\$" $PRECACHEDB | grep -Ev '\.[0-9]{14}$' | grep -v '\.asc$'`"
+ fi
+ if test "$tMD5" == "$bMD5"
+ then
+ echo -en "${RED}MALFORMED $SRCDATA EDIT s/$tMD5/$fMD5/"
+ elif test "$tMD5" == "$EMPTYMD5"
+ then
+ echo -en "${RED}EMPTY $SRCDATA EDIT s/$tMD5/$fMD5/"
+ elif test "$tMD5" == "gpg"
+ then
+ echo -en "${RED}${BOLD}GPGCHECK $SRCDATA"
+ elif test -n "`echo $tMD5 | grep 'gpg$'`"
+ then
+ echo -en "${RED}${BOLD}GPG-SIGN $SRCDATA"
+ elif test -n "$PRECACHEDB" && test -n "`grep $tMD5 $PRECACHEDF`"
+ then
+ MATCHED="`grep "$tMD5" "$PRECACHEDF" | cut -c60- | head -n 1`"
+ if test -n "`echo "$MATCHED" | fgrep "$SOURCE"`"
+ then
+ echo -en "${RED}${BOLD}MODIFIED $SRCDATA EDIT s/$tMD5/$fMD5/${DEFAULT} MATCHES $MATCHED"
+ else
+ echo -en "${RED}DIFFERENT $SRCDATA EDIT s/$tMD5/$fMD5/${DEFAULT} MATCHES $MATCHED"
+ fi
+ else
+ echo -en "${RED}${BOLD}INCORRECT $SRCDATA EDIT s/$tMD5/$fMD5/${DEFAULT}"
+ fi
+ fi
+ fi
+ fi
+ fi
+ echo -n " $APPEND"
+ if test "$tMD5" != "IGNORE" ; then
+ gaze_checkmd5syntax "$1" "$2"
+ fi
+ else
+ echo -n "NONSOURCE "
+ fi
+ echo
+
+}
+
+
+#---------------------------------------------------------------------
+## @param spelldirectory
+## @param sourcenumber
+##
+## spelldirectory is the spell's directory
+## sourcenumber is '' or '2', '3', '4', etc.
+## Checks the syntax related to md5 checking of a single source file
+## sourcenumber in spelldirectory.
+##
+#---------------------------------------------------------------------
+function gaze_checkmd5syntax () {
+ rp=$1
+ sn=$2
+ echo -en "${YELLOW}${BOLD}"
+ if test -f $rp/PRE_BUILD ; then
+ #echo $rp $sn
+ if test "`grep unpack $rp/* | grep SOURCE$sn[^0-9] | grep MD5`" = "" ; then
+ if test "$sn" != "" ; then
+ echo -n "ADD unpack md5 arg to EXISTING PRE_BUILD"
+ else
+ if test "`grep default_pre_build $rp/*`" = "" ; then
+ echo -n "ADD unpack md5 arg to EXISTING PRE_BUILD"
+ fi
+ fi
+ fi
+ else
+ if test -f $rp/POST_INSTALL ; then
+ if test "`grep unpack $rp/* | grep SOURCE$sn[^0-9] | grep MD5`" = "" ; then
+ if test "$sn" != "" ; then
+ echo -n "ADD unpack md5 arg to EXISTING POST_INSTALL or CREATED PRE_BUILD"
+ fi
+ fi
+ elif test -f $rp/BUILD ; then
+ if test "`grep unpack $rp/* | grep SOURCE$sn[^0-9] | grep MD5`" = "" ; then
+ if test "$sn" != "" ; then
+ echo -n "INIT unpack md5 arg or default_pre_build to NEW PRE_BUILD"
+ fi
+ fi
+ else
+ if test "$sn" != "" ; then
+ echo -n "APPEND unpack md5 arg to CREATED PRE_BUILD"
+ else
+ if test "$SOURCE2" != "" ; then
+ echo -n "INIT unpack md5 arg or default_pre_build to NEW PRE_BUILD"
+ fi
+ fi
+ fi
+ fi
+ echo -en "${DEFAULT}"
+
+}
+
+#---------------------------------------------------------------------
+## @param spelldirectory
+##
+## spelldirectory is the spell's directory.
+## Checks the md5s of all source files in spelldirectory.
+##
+#---------------------------------------------------------------------
+function gaze_checkmd5s() {
+ spellroot=$1
+ SCRIPT_DIRECTORY=$spellroot
+ spellpath=$spellroot/DETAILS
+ spellname=`echo $spellroot | sed -e 's!/.*/!!'`
+ unset FORCE_DOWNLOAD
+ unset SOURCE
+ unset MD5 2> /dev/null
+ unset LICENSE 2> /dev/null
+ SPELL_CONFIG=/etc/sorcery/local/depends/$spellname
+ #echo $SPELL_CONFIG
+ source $spellpath > /dev/null 2> /dev/null
+
+ gaze_checkmd5 $spellroot ''
+
+ unset SOURCE
+ unset MD5[0] 2> /dev/null
+ unset LICENSE[0] 2> /dev/null
+ j=2
+ jj=SOURCE$j
+ while [ -n "${!jj}" ]; do
+
+ SOURCEvar=SOURCE$j
+ SOURCE=${!SOURCEvar}
+ if test "$SOURCE" != " " ; then
+
+ gaze_checkmd5 $spellroot $j
+
+ fi
+ unset $jj
+ unset MD5[$(($j-1))] 2> /dev/null
+ unset LICENSE[$(($j-1))] 2> /dev/null
+ j=$(($j+1))
+ jj=SOURCE$j
+ done
+ unset VERSION
+
+}
+
+
+#---------------------------------------------------------------------
+## @param [<item> ...]
+##
+## item is a spell or section name.
+## Checks the md5s of various spells, sections, or if called with no
+## arguments, the entire grimoire.
+##
+#---------------------------------------------------------------------
+function gaze_md5check() {
+
+ message "${MESSAGE_COLOR}Going to check the md5 sums for the requested item, section or"
+ message "grimoire by testing your current collection of sources...${DEFAULT_COLOR}"
+
+ source /etc/sorcery/config
+
+ EMPTYMD5="`echo -n | md5sum | cut -d' ' -f1`"
+
+ #if CACHED is defined, then use some caches for speedy checking.
+ if ! test -z "$CACHED" ; then
+ PRECACHEDF=/var/spool/sorcery/reports/md5unpack
+ PRECACHEDB=/var/spool/sorcery/reports/md5sum
+ fi
+
+ unset SECTIONS SPELLS UNKNOWN
+
+ [ -z "$1" ] && SECTIONS=`codex_get_all_sections`
+
+ for spell_or_section in $@; do
+
+ if codex_find_spell_or_section_by_name $spell_or_section; then
+ [ -n "$CODEX_FOUND_SECTION" ] && SECTIONS="$SECTIONS $CODEX_FOUND_SECTION"
+ [ -n "$CODEX_FOUND_SPELL" ] && SPELLS="$SPELLS $CODEX_FOUND_SPELL"
+ else
+ UNKNOWN="$spell_or_section $UNKNOWN"
+ fi
+
+ done
+
+ for i in $SECTIONS ; do
+ SPELLS="$SPELLS `codex_get_spells_in_section $i`"
+ done
+
+ for i in $SPELLS ; do
+ gaze_checkmd5s $i
+ done
+
+ if [ -n "$UNKNOWN" ] ; then
+ for i in $UNKNOWN ; do
+ echo "unknown: $i"
+ done
+ fi
+
+}
+
diff --git a/var/lib/sorcery/modules/libsorcery b/var/lib/sorcery/modules/libsorcery
new file mode 100644
index 0000000..857b867
--- /dev/null
+++ b/var/lib/sorcery/modules/libsorcery
@@ -0,0 +1,1504 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## @Synopsis Set of functions used by the internal sorcery scripts
+##
+## This should really be home to things related to the sorcery script
+## itself, not a repository for other functions. (Andrew 5/29/04)
+##
+## @Copyright Original version Copyright 2001 by Kyle Sallee
+## @Copyright Additions/Corrections Copyright 2002 by the Source Mage Team
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## @param branch
+##
+## Updates the sorcery scripts to their latest version for specified
+## branch.
+##
+#---------------------------------------------------------------------
+function update_sorcery_scripts() {
+
+ local BRANCH=$1
+
+ debug "libsorcery" "update_sorcery_scripts() (sorcery-$BRANCH)"
+
+ {
+ #This section is to make sure that all sorcery dependencies are cast
+ #Keep this outer if until basesystem is in the grimoires properly.
+ #(2002/10/13)
+ if [[ `codex_find_spell_by_name basesystem` ]] ; then
+ if ! spell_ok basesystem ; then
+ cast basesystem
+ fi
+ if [ "$UPDATEFIX" == on ] ; then
+ cleanse --fix basesystem
+ fi
+ fi
+ }
+
+ pushd $TMP_DIR &>/dev/null
+ # sorcery is not a spell
+ # there is no spoon
+ SOURCE=sorcery-$BRANCH.tar.bz2
+ SOURCE_DIRECTORY=$BUILD_DIRECTORY/sorcery
+ SOURCE_URL=$SORCERY_URL/$SOURCE
+
+ message "${MESSAGE_COLOR}Downloading source file" \
+ "${FILE_COLOR}${SOURCE}${DEFAULT_COLOR}" \
+ "for sorcery update."
+ url_download "$SOURCE" "$SOURCE_URL" "" "sr_target" "sr_type" || {
+ message "${PROBLEM_COLOR}Failed to download sorcery${DEFAULT_COLOR}"
+ return 1
+ }
+ if [[ $sr_type != file ]] ; then
+ message "Downloaded type is not a file, file a bug if you see this"
+ return 1
+ fi
+ [[ $sr_target != $SOURCE ]] && mv $sr_target $SOURCE
+
+ gpg_verify_sorcery $SOURCE $SORCERY_URL
+ rc=$?
+
+ gpg_user_query $rc $SORCERY_BRANCH sorcery || return 1
+ popd &>/dev/null
+
+ local FILENAME=$TMP_DIR/$SOURCE
+ # copy the tarball to /var/spool/sorcery after we've downloaded and
+ # verified it.
+ cp $FILENAME $SOURCE_CACHE
+
+ mk_source_dir $SOURCE_DIRECTORY &&
+ cd $BUILD_DIRECTORY
+ bzip2 -cdf $FILENAME |
+ tar --owner=root --group=root -xf /dev/stdin &&
+ cd $SOURCE_DIRECTORY &&
+
+ echo `pwd` || return 1
+
+ # last chance to lock
+ message -n "${MESSAGE_COLOR}Waiting for${DEFAULT_COLOR} ${SPELL_COLOR}all" \
+ "${DEFAULT_COLOR} ${MESSAGE_COLOR}spells to complete..." \
+ "${DEFAULT_COLOR}"
+ lock_resources "solo" "cast" # prevent other casts
+ excllock_resources "cast" "sorcery" # wait for other casts
+ message "done."
+ lock_resources "libgrimoire" "install" &&
+
+ if test -x ./uninstall && test -f /etc/sorcery/install.log ; then
+ message uninstalling...
+ ./uninstall
+ else
+ # rough equivalent to prepare_install...
+ rm /etc/sorcery/* 2>/dev/null
+ rm /etc/sorcery/licenses/* 2>/dev/null
+ rm /etc/sorcery/mirrors/* 2>/dev/null
+ rm /var/lib/sorcery/* 2>/dev/null
+ rm -r /var/lib/sorcery/modules/* 2>/dev/null
+ rm -r /var/lib/sorcery/archspecs/* 2>/dev/null
+ rm -r /var/lib/sorcery/build/* 2>/dev/null
+ fi
+ ./install &&
+
+ cd / &&
+ unlock_resources "libgrimoire" "install" &&
+
+ SORCERY_VERSION=`cat /etc/sorcery/version`
+
+ unlock_resources "solo" "cast"
+ unlock_resources "cast" "sorcery"
+
+ if [ $? -eq 0 ] ; then
+ activity_log "update" "sorcery-$BRANCH" "$SORCERY_VERSION" "success"
+ rm_source_dir
+ else
+ activity_log "update" "sorcery-$BRANCH" "$SORCERY_VERSION" "failure"
+ if [[ $CLEAN_SOURCE == on ]]; then
+ rm_source_dir
+ fi
+ fi
+ message "${MESSAGE_COLOR}Current${SPELL_COLOR}" \
+ "sorcery-$BRANCH${DEFAULT_COLOR} ${MESSAGE_COLOR}version" \
+ "${VERSION_COLOR}$SORCERY_VERSION${DEFAULT_COLOR}"
+ sorcery_history
+ rm $FILENAME
+}
+
+#---------------------------------------------------------------------
+##
+## Menu query to user asking to set the nice value which sorcery should
+## use to run proccesses at.
+##
+#---------------------------------------------------------------------
+function set_nice() {
+
+ PROMPT="Please enter the process priority sorcery should run at."
+
+ if NICE=`eval $DIALOG ' --ok-label "Commit" \
+ --inputbox \
+ "$PROMPT" \
+ 0 0 "$NICE"'`
+ then
+ modify_local_config "NICE" "$NICE"
+ fi
+
+}
+
+#---------------------------------------------------------------------
+##
+## Menu query to user asking to set the umask value which sorcery should
+## use to run proccesses with.
+##
+#---------------------------------------------------------------------
+function set_umask() {
+
+ PROMPT="Please enter the permissions mask sorcery should run with."
+
+ if UMASK=`eval $DIALOG ' --ok-label "Commit" \
+ --inputbox \
+ "$PROMPT" \
+ 0 0 "$UMASK"'`
+ then
+ modify_local_config "UMASK" "$UMASK"
+ fi
+
+}
+
+
+#---------------------------------------------------------------------
+## @param filename
+##
+## Displays and/or mails the contents of the file given as the first
+## argument.
+##
+#---------------------------------------------------------------------
+function report() {
+
+ if ! [ -f $1 ]; then return; fi
+
+ if [ "$VIEW_REPORTS" == "on" ]; then
+
+ debug "libsorcery" "Prompting to view $2 for $SPELL"
+
+ VIEW_PROMPT="View $2 for ${SPELL_COLOR}${SPELL}-${VERSION_COLOR}${VERSION}${DEFAULT_COLOR}?"
+ sound REPORT
+ if query "$VIEW_PROMPT" n; then $PAGER $1; fi
+ fi
+
+ if [ "$MAIL_REPORTS" == "on" ]; then
+ debug "libsorcery" "Mailing report ($2) for $SPELL to $SORCERER"
+ date -u |
+ mail -s "Sorcery Report : $HOSTNAME : $2 for $SPELL-$VERSION" \
+ -a $1 $SORCERER 2>/dev/null
+ fi
+
+ true
+
+}
+
+
+#---------------------------------------------------------------------
+## @param filename
+##
+## Given a filename, will return the actual filename if a similar
+## filename with a different extension exists.
+##
+#---------------------------------------------------------------------
+function guess_filename() {
+
+ FILENAME=$1
+
+ debug "libsorcery" "Running guess_filename() on $FILENAME"
+
+ BASENAME=`echo $FILENAME |
+ sed "s/\.tar\.gz$//" |
+ sed "s/\.tgz$//" |
+ sed "s/\.tar\.bz2$//"`
+
+ if [ -f $FILENAME ]; then echo $FILENAME
+ elif [ "$FUZZ" == "off" ]; then return 1
+ elif [ -f $BASENAME.tar.gz ]; then echo $BASENAME.tar.gz
+ elif [ -f $BASENAME.tar.bz2 ]; then echo $BASENAME.tar.bz2
+ elif [ -f $BASENAME.tgz ]; then echo $BASENAME.tgz
+ else false
+ fi
+
+}
+
+#-------------------------------------------------------------------------
+## check if the file exists, or one with a similar compression exists
+## @param file
+#-------------------------------------------------------------------------
+function file_exists() {
+ guess_filename $1 > /dev/null
+}
+
+#---------------------------------------------------------------------
+##
+## Saves the current libraries associated with the spell, from /lib
+## and /usr/lib into $SOURCE_DIRECTORY/old.libraries/
+## Also runs ldconfig with the saved libraries.
+##
+#---------------------------------------------------------------------
+function save_libraries() {
+
+ if [ -z "$SOURCE_DIRECTORY" ]
+ then return
+ fi
+
+ debug "libsorcery" "Running save_libraries()"
+
+ OLD_LIBS=$SOURCE_DIRECTORY/old.libraries
+ mkdir -p $OLD_LIBS
+
+ SAVED=$OLD_LIBS/$SPELL.saved.libraries
+ lock_file $SAVED
+ rm -rf $SAVED
+
+ OLD_VERSION=`installed_version $SPELL`
+ OLD_LOG=$INSTALL_LOGS/$SPELL-$OLD_VERSION
+
+ grep "^/lib/\|^/usr/lib" $OLD_LOG |
+ while read LINE; do
+
+ if [ -f $LINE ] &&
+ file -bL $LINE |
+ grep -q "shared object"
+ then
+ if [ -h $LINE ]; then
+ DEST=$( basename $( ls -la "$LINE" |
+ cut -d '>' -f2 |
+ cut -c 2-
+ )
+ )
+ ln -sf $DEST $OLD_LIBS/`basename $LINE`
+ else
+ cp $LINE $OLD_LIBS
+ fi
+ echo $OLD_LIBS/`basename $LINE` >> $SAVED
+ fi
+
+
+ done
+
+ ldconfig $OLD_LIBS
+ unlock_file $SAVED
+
+ if [ -z "$LD_LIBRARY_PATH" ]
+ then export LD_LIBRARY_PATH="$OLD_LIBS"
+ else export LD_LIBRARY_PATH="$OLD_LIBS:$LD_LIBRARY_PATH"
+ fi
+
+ # NOTE: remove this in 1.13
+ if [ "$SPELL" == "glibc" ] ||
+ [ "$SPELL" == "glibc-custom" ] ; then
+ if [ -e $OLD_LIBS/ld-$OLD_VERSION.so ] ; then
+ [[ `uname -m` == ppc* ]] && ln -sf $OLD_LIBS/ld-$OLD_VERSION.so ${INSTALL_ROOT}/lib/ld.so.1 ||
+ ln -sf $OLD_LIBS/ld-$OLD_VERSION.so ${INSTALL_ROOT}/lib/ld-linux.so.2
+ fi
+ fi
+
+}
+
+
+#---------------------------------------------------------------------
+##
+## Runs default ldconfig to stop using the saved libraries
+##
+#---------------------------------------------------------------------
+function release_saved_libraries() {
+ ldconfig
+}
+
+
+#---------------------------------------------------------------------
+##
+## Recovers from a CTRL-C while casting a spell
+## FIXME: this should be a resurrect
+##
+#---------------------------------------------------------------------
+function spell_recover() {
+
+ debug "libsorcery" "Running spell_recover()"
+
+ message "${MESSAGE_COLOR}Aborting dispel of ${SPELL_COLOR}${SPELL}" \
+ "${MESSAGE_COLOR} and recovering${DEFAULT_COLOR}"
+
+ CURRENT_VERSION=$(installed_version $SPELL)
+ CACHE_COMP="$INSTALL_CACHE/$SPELL-$CURRENT_VERSION-$HOST.tar$EXTENSION"
+ CONTINUE=false
+
+ if [ ! -f $CACHE_COMP ]; then
+ false
+ elif [ -n "$EXTENSION" ]; then
+ CONTINUE=true
+ elif $COMPRESSBIN -tf $CACHE_COMP
+ then
+ CONTINUE=true
+ fi
+
+ if $CONTINUE
+ then
+
+ if [ -n "$EXTENSION" ]; then
+ $COMPRESSBIN -cd $CACHE_COMP | tar -Pkx 2>/dev/null
+ else
+ tar -Pkxf $CACHE_COMP 2>/dev/null
+ fi
+ add_spell $SPELL installed $CURRENT_VERSION
+ else
+ false
+ fi
+
+ exit 130
+
+}
+
+
+#---------------------------------------------------------------------
+## @param spell name
+## @Stdout section
+## Returns the section a spell is in.
+##
+#---------------------------------------------------------------------
+function find_section() {
+
+ debug "libsorcery" "Running find_section() on $1"
+
+ codex_get_spell_section_name $1
+
+}
+
+
+#---------------------------------------------------------------------
+##=item directories
+##
+## @Stdin list of files/dirs/..
+## @Stdout list of dirs
+##
+## Reads a list from standard input, and prints out each entry that is
+## a directory (and not a symbolic link to a directory).
+#---------------------------------------------------------------------
+function directories() {
+
+ while read ITEM; do
+ if [ -d "$ITEM" ] &&
+ ! [ -h "$ITEM" ]; then
+ echo "$ITEM"
+ fi
+ done
+
+}
+
+
+#---------------------------------------------------------------------
+## @Stdin list of files/dirs/..
+## @Stdout list of files
+## Reads a list from standard input, and prints out each entry that is
+## a file (and not a symbolic link to a file).
+##
+#---------------------------------------------------------------------
+function files() {
+
+ while read ITEM; do
+
+ if [ -f "$ITEM" ] &&
+ ! [ -h "$ITEM" ]; then
+ echo "$ITEM"
+ fi
+ done
+
+}
+
+
+#---------------------------------------------------------------------
+## @Stdin list of files/dirs/..
+## @Stdout list of symlinks
+## Reads a list from standard input, and prints out each entry that is
+## a symbolic linke
+##
+#---------------------------------------------------------------------
+function symlinks() {
+
+ while read ITEM; do
+ if [ -h "$ITEM" ]; then
+ echo "$ITEM"
+ fi
+ done
+
+}
+
+
+#---------------------------------------------------------------------
+## @param filename
+## @Stdin list of files/dirs/..
+## @Stdout filtered list
+## First argument is a file that contains (basic) grep regular expressions.
+## They are joined with \|'s and given to grep -v.
+## Which will filter standard input to remove entries that match.
+## Used to filter out excluded or protected files from install logs.
+##
+#---------------------------------------------------------------------
+function filter() {
+ local thing RID_LIST
+ local files each invert
+ for each in $@; do
+ test -f $each && files="$files $each"
+ done
+
+ if [[ $files ]] ; then
+ RID_LIST=$(
+ cat $files | while read thing; do
+ echo -n "$thing\|"
+ done
+ echo -n /dev/null)
+ grep -v "$RID_LIST"
+ else
+ cat
+ fi
+}
+
+#---------------------------------------------------------------------
+## @param filename
+## @Stdin list of files/dirs/..
+## @Stdout filtered list
+## First argument is a file that contains (basic) grep regular expressions.
+## They are joined with \|'s and given to grep
+## Which will filter standard input to remove and remove entries
+## that do not match.
+## Used to filter in config files from install logs.
+##
+#---------------------------------------------------------------------
+function filter_in() {
+ local thing RID_LIST
+ local files each
+ for each in $@; do
+ test -f $each && files="$files $each"
+ done
+
+ if [[ $files ]] ; then
+ RID_LIST=$(
+ cat $files | while read thing; do
+ echo -n "$thing\|"
+ done
+ echo -n /dev/null)
+ grep "$RID_LIST"
+ else
+ cat
+ fi
+}
+
+function filter_generic() {
+ local args=$1
+ local filter_name=$2
+ local system_filter=$3
+ local grimoire_filter section_filter spell_filter spell_dir
+ tablet_find_spell_dir $SPELL spell_dir &>/dev/null &&
+ tablet_get_spell_filter $spell_dir $filter_name spell_filter &>/dev/null
+ tablet_get_section_filter $spell_dir $filter_name section_filter &>/dev/null
+ tablet_get_grimoire_filter $spell_dir $filter_name grimoire_filter &>/dev/null
+ set -- $args
+ if [ "$1" == -v ] ; then
+ shift
+ filter_in "$@" $system_filter $grimoire_filter $section_filter $spell_filter
+ else
+ filter "$@" $system_filter $grimoire_filter $section_filter $spell_filter
+ fi
+}
+
+function filter_volatiles() {
+ filter_generic "$*" volatiles $VOLATILES
+}
+
+function filter_configs() {
+ filter_generic "$*" configs $CONFIGS
+}
+
+function filter_excluded() {
+ filter_generic "$*" excluded $EXCLUDED
+}
+
+function filter_protected() {
+ filter_generic "$*" protected $PROTECTED
+}
+
+#---------------------------------------------------------------------
+## @Stdin list of files
+## @Stdout list of directories
+##
+## Given a list of files from standard input, returns the directory
+## of each file.
+##
+## Note: Is a duplicate of get_dirnames. 2003-09-28 Martin
+#---------------------------------------------------------------------
+function dirnames() {
+ while read FILE; do dirname "$FILE"; done;
+}
+
+#---------------------------------------------------------------------
+## @param queue filename
+## @param items to remove ...
+##
+## The first argument is the name of the file containing the queue.
+## If a second argument is given, any items in the queue that match
+## the second argument are removed from the queue. Otherwise, the top
+## line of the queue is removed, and returned.
+##
+#---------------------------------------------------------------------
+function pop_queue() { (
+
+ local ITEM=`esc_str $2`
+ local exit_code=0
+
+ tQUEUE_FILE=$( lock_start_transaction $1 )
+ if ! [ -f "$1" ]; then
+ exit_code=1
+ elif [ -n "$ITEM" ]; then
+ grep -v "^$ITEM\$" $1 > $tQUEUE_FILE
+ else
+ FOUND=`sed -n 1p $1`
+
+ if [ -z "$FOUND" ]; then
+ exit_code=1
+ else
+ FOUND=`esc_str $FOUND`
+ grep -v "^$FOUND$" $1 > tQUEUE_FILE
+ echo $FOUND
+ fi
+ fi
+ lock_commit_transaction $1
+
+ return $exit_code
+
+) }
+
+
+#---------------------------------------------------------------------
+## @param filename
+##
+## The first argument is the name of the file that installed correctly
+## and will be removed from the install file (this makes a failure of
+## the casting of the queue not have to recast completed spells).
+##
+#---------------------------------------------------------------------
+function pop_install_queue() {
+
+ pop_queue $INSTALL_QUEUE "$1"
+
+}
+
+
+#---------------------------------------------------------------------
+## @param queue filename
+## @param item to add
+## The first argument is the name of the file containing the queue.
+## The second argument is an item to add to the end of the queue. If
+## the item exists anywhere in the queue, the item is removed from the
+## queue before being added at the end.
+##
+#---------------------------------------------------------------------
+function push_queue() {
+
+ pop_queue "$1" "$2"; echo "$2" >> $1;
+
+}
+
+
+#---------------------------------------------------------------------
+## @param spell
+##
+## Adds the given spell to the install queue and removes it from the
+## remove queue.
+##
+#---------------------------------------------------------------------
+function push_install_queue() {
+
+ pop_queue $REMOVE_QUEUE "$1"
+ pop_queue $INSTALL_QUEUE "$1"
+ ! spell_installed "$1" &&
+ push_queue $INSTALL_QUEUE "$1"
+
+}
+
+
+#---------------------------------------------------------------------
+## @param spell
+##
+## Adds the given spell to the remove queue and removes it from the
+## install queue.
+##
+#---------------------------------------------------------------------
+function push_remove_queue() {
+
+ pop_queue $INSTALL_QUEUE "$1"
+ pop_queue $REMOVE_QUEUE "$1"
+ spell_installed "$1" &&
+ push_queue $REMOVE_QUEUE "$1"
+
+}
+
+#---------------------------------------------------------------------
+##
+## Sets some environment variables (such as C<CFLAGS>) based on the
+## option passed. Options may be on or more of: i386, i486, i586,
+## pentium, pentium-mmx, i686, pentiumpro, pentium2, pentium3,
+## pentium4, k6, k6-2, k6-3, athlon, athlon-tbird, athlon-4,
+## athlon-xp, athlon-mp, powerpc, speedy, tiny, risky, strip,
+## combreloc
+##
+#---------------------------------------------------------------------
+function optimize() {
+ debug "libsorcery" "In optimize()"
+
+ unset CFLAGS CXXFLAGS LDFLAGS LC_ALL
+ unset COMBRELOC FAST PRELINK RISKY SMALL SPEEDY STRIP TINY
+
+ # if user specified --no-opts then use only the args from the command line
+ # otherwise do the normal stuff
+ if [[ $NO_OPTIMIZATION_FLAGS ]] ; then
+ export CFLAGS="$OVERRIDE_CFLAGS"
+ export CXXFLAGS="$OVERRIDE_CXXFLAGS"
+ export LDFLAGS="$OVERRIDE_LDLAGS"
+ else
+ set_architecture
+ debug "ARCHITECTURE='${ARCHITECTURE}'"
+ debug "libsorcery" "TARGET='${TARGET}'"
+ debug "libsorcery" "OPTIMIZATIONS='${OPTIMIZATIONS}'"
+ BUILD=${HOST}
+
+ debug "libsorcery" "BUILD is $BUILD, HOST is $HOST"
+
+ CFLAGS="$CFLAGS -pipe"
+ for PARAM in $OPTIMIZATIONS; do
+ case $PARAM in
+ combreloc)
+ LDFLAGS="$LDFLAGS -z combreloc"
+ COMBRELOC="on"
+ ;;
+
+ prelink)
+ CFLAGS="$CFLAGS -DPIC -fPIC"
+ PRELINK="on"
+ ;;
+
+ risky)
+ CFLAGS="$CFLAGS -ffast-math -funroll-loops"
+ RISKY="on"
+ ;;
+
+ speedy)
+ CFLAGS="$CFLAGS $FAST"
+ SPEEDY="on"
+ ;;
+
+ strip)
+ LDFLAGS="$LDFLAGS -s"
+ STRIP="on"
+ ;;
+
+ tiny)
+ CFLAGS="$CFLAGS $SMALL"
+ TINY="on"
+ ;;
+
+ esac
+ done
+ # use echo to crunch all the whitespace out for broken configure scripts
+ export CFLAGS=$(echo -O $CFLAGS $CUSTOM_CFLAGS $OVERRIDE_CFLAGS)
+ export CXXFLAGS=$(echo $CFLAGS $CUSTOM_CXXFLAGS $OVERRIDE_CXXFLAGS)
+ export LDFLAGS=$(echo $LDFLAGS $CUSTOM_LDFLAGS $OVERRIDE_LDLAGS)
+ fi
+ export LC_ALL="C"
+
+}
+
+
+#---------------------------------------------------------------------
+## @Stdout install queue
+##
+## List files in install queue, give user chance to modify it.
+##
+#---------------------------------------------------------------------
+function list_install_queue() {
+ if [ -f $INSTALL_QUEUE ]; then
+ lock_file $INSTALL_QUEUE
+
+ message -n "The following spells will be updated:"
+ message "${SPELL_COLOR}"
+ cat $INSTALL_QUEUE | column
+ message "${DEFAULT_COLOR}"
+
+ if query "Do you wish to edit ${FILE_COLOR} $INSTALL_QUEUE ${DEFAULT_COLOR}?" n
+ then edit_file $INSTALL_QUEUE
+ fi
+
+ if [ -n "`cat $INSTALL_QUEUE`" ]; then
+ SPELLS=`cat $INSTALL_QUEUE`
+ fi
+
+ unlock_file $INSTALL_QUEUE
+ else
+ message "${MESSAGE_COLOR}No spells listed in queue.${DEFAULT_COLOR}"
+ fi
+}
+
+
+#---------------------------------------------------------------------
+##
+## Checks all installed spells for newer versions, and creates an
+## install queue.
+##
+#---------------------------------------------------------------------
+function update_install_queue() {
+ local line spell curr_version curr_updated page_dir info curr_patchlevel
+ local curr_sec_patch
+ local tmp_queue=$TMP_DIR/install_queue
+ touch $tmp_queue
+
+ message "${CHECK_COLOR}Generating list of spells to update... ${DEFAULT_COLOR} "
+ for line in `all_spell_status`; do
+ #0=spell, 1=date, 2=status, 3=version
+ explode "$line" ":" "info"
+ spell=${info[0]}
+ curr_updated=${info[1]}
+ status=${info[2]}
+ curr_version=${info[3]}
+
+ ( # do this in a subshell in case a spell mis-behaves and exits we
+ # still want queueing to work
+ if [[ "$status" == "installed" ]] &&
+ codex_set_current_spell_by_name $spell; then
+ if [[ $VERSION != $curr_version ]] ; then
+ echo $spell >> $tmp_queue
+ else
+ curr_patchlevel=0
+ curr_sec_patch=0
+ if tablet_find_spell_dir $spell page_dir ; then
+ curr_updated=0
+ tablet_get_updated $page_dir curr_updated
+ tablet_get_patchlevel $page_dir curr_patchlevel
+ tablet_get_security_patch $page_dir curr_sec_patch
+ fi
+ if (( "${PATCHLEVEL:-0}" > "$curr_patchlevel" )) ||
+ # the following is subject to change
+ (( "${SECURITY_PATCH:-0}" > "$curr_sec_patch" )) ||
+ (( "${UPDATED:-0}" > "${curr_updated:-0}" )) ; then
+ echo $spell >> $tmp_queue
+ fi
+ fi
+ fi
+ )
+ done
+
+ lock_file $INSTALL_QUEUE
+ rm -f $INSTALL_QUEUE
+ sort $tmp_queue | uniq > $INSTALL_QUEUE
+ unlock_file $INSTALL_QUEUE
+
+}
+
+function update_security_install_queue() {
+ local line spell page_dir info curr_sec_patch=0
+ local tmp_queue=$TMP_DIR/install_queue
+ touch $tmp_queue
+
+ message "${CHECK_COLOR}Generating list of spells to update for" \
+ "security reasons... ${DEFAULT_COLOR} "
+ for line in `all_spell_status`; do
+ #0=spell, 1=date, 2=status, 3=version
+ explode "$line" ":" "info"
+ spell=${info[0]}
+ status=${info[2]}
+
+ ( # do this in a subshell in case a spell mis-behaves and exits we
+ # still want queueing to work
+ if [[ "$status" == "installed" ]] &&
+ codex_set_current_spell_by_name $spell &&
+ tablet_find_spell_dir $spell page_dir ; then
+ tablet_get_security_patch $page_dir curr_sec_patch &&
+ if (( "${SECURITY_PATCH:-0}" > "$curr_sec_patch" )) ; then
+ echo $spell >> $tmp_queue
+ fi
+ fi
+ )
+ done
+
+ lock_file $INSTALL_QUEUE
+ rm -f $INSTALL_QUEUE
+ sort $tmp_queue | uniq > $INSTALL_QUEUE
+ unlock_file $INSTALL_QUEUE
+
+}
+
+#---------------------------------------------------------------------
+##
+## Builds all spells in the install queue.
+##
+#---------------------------------------------------------------------
+function build_install_queue() {
+
+ if [ -f $INSTALL_QUEUE ]; then
+ lock_file $INSTALL_QUEUE
+
+ message "The following spells will be updated :"
+ cat $INSTALL_QUEUE
+
+ unset SPELL
+ if query "Do you wish to edit ${FILE_COLOR}$INSTALL_QUEUE${DEFAULT_COLOR}?" n
+ then edit_file $INSTALL_QUEUE
+ fi
+
+ if [ -n "`cat $INSTALL_QUEUE`" ]; then
+ cast `cat $INSTALL_QUEUE`
+ rm $INSTALL_QUEUE
+ fi
+
+ unlock_file $INSTALL_QUEUE
+ else
+ message "No spells will be updated."
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## @Stdout history
+##
+## Display the history of the install queue for review
+##
+#---------------------------------------------------------------------
+function install_queue_history() {
+ local PAGER2
+ if [ "$PAGER" == "less" ] ; then PAGER2="more"; else PAGER2="$PAGER"; fi
+ local spell=
+ for spell in `cat $INSTALL_QUEUE`; do
+ local date="`grep "^${spell}:" $SPELL_STATUS | cut -d':' -f2`"
+ local datedash="`echo "$date" | cut -c1-4`-`echo "$date" | cut -c5-6`-`echo "$date" | cut -c7-8`"
+ (
+ echo -e "Viewing history since last update ($datedash) for spell -- ${spell} -- :" &&
+ echo &&
+ gaze history $spell
+ ) |
+ awk -v date="$date" '{ d = (strtonum(substr($0,1,4) substr($0,6,2) substr($0,9,2)) < date && strtonum(substr($0,1,4)) > 0) }; NR == 1, d { if (!d) print }' |
+ $PAGER2
+ query "Would you like to ${RED} remove ${QUERY_COLOR} spell ${SPELL_COLOR} $spell ${QUERY_COLOR} from the install queue?" n &&
+ sedit "s/^$spell$//" $INSTALL_QUEUE &&
+ echo -e "${QUERY_COLOR} The spell ${SPELL_COLOR} $spell ${QUERY_COLOR} removed from $INSTALL_QUEUE.${DEFAULT_COLOR}"
+ done
+}
+
+#---------------------------------------------------------------------
+## @param grimoire-path
+## @Stdout history
+## Display the history of the grimoire given for review
+#---------------------------------------------------------------------
+function grimoire_history() {
+
+ if [ -e $grimoire/ChangeLog ] ; then
+ local PAGER2
+ if [ "$PAGER" == "less" ] ; then
+ PAGER2="more"
+ else
+ PAGER2="$PAGER"
+ fi
+ local grimoire="$1"
+ local grimoirename="${grimoire##*/}"
+ local date="`cat "$STATE_DIRECTORY/$grimoirename.lastupdate" 2> /dev/null`"
+ [ -z "$date" ] && date="20030818"
+ local datedash="`echo "$date" | cut -c1-4`-`echo "$date" | cut -c5-6`-`echo "$date" | cut -c7-8`"
+ (
+ echo -e "Viewing history since last update ($datedash) for grimoire -- ${grimoirename} -- :" &&
+ echo &&
+ cat "$grimoire/ChangeLog" 2> /dev/null
+ ) |
+ awk -v date="$date" '{ d = (strtonum(substr($0,1,4) substr($0,6,2) substr($0,9,2)) < date && strtonum(substr($0,1,4)) > 0) }; NR == 1, d { if (!d) print }' |
+ $PAGER2
+ else
+ echo "No ChangeLog in the $grimoire Grimoire, skipping viewing..."
+ echo ""
+ fi
+ echo "`date +%Y%m%d`" > "$STATE_DIRECTORY/$grimoirename.lastupdate"
+}
+
+
+#---------------------------------------------------------------------
+## @Stdout history
+## Display the history of sorcery for review
+##
+#---------------------------------------------------------------------
+function sorcery_history() {
+ local PAGER2
+ if [ "$PAGER" == "less" ] ; then PAGER2="more"; else PAGER2="$PAGER"; fi
+ local date="`cat "$STATE_DIRECTORY/sorcery.lastupdate" 2> /dev/null`"
+ [ -z "$date" ] && date="20030818"
+ local datedash="`echo "$date" | cut -c1-4`-`echo "$date" | cut -c5-6`-`echo "$date" | cut -c7-8`"
+ (
+ echo -e "Viewing history since last update ($datedash) for sorcery :" &&
+ echo &&
+ cat "/usr/share/doc/sorcery/ChangeLog"
+ ) |
+ awk -v date="$date" '{ d = (strtonum(substr($0,1,4) substr($0,6,2) substr($0,9,2)) < date && strtonum(substr($0,1,4)) > 0) }; NR == 1, d { if (!d) print }' |
+ $PAGER2
+ echo "`date +%Y%m%d`" > "$STATE_DIRECTORY/sorcery.lastupdate"
+}
+
+
+#---------------------------------------------------------------------
+##
+## Attempt to fix any spells that may be broken.
+##
+#---------------------------------------------------------------------
+function fix_installed_spells() {
+
+ local ANSWER=
+
+ [ "$AUTOFIX" == "on" ] && ANSWER=y || ANSWER=n
+ if query "Attempt to fix spells that may have become broken ?" $ANSWER; then
+ cleanse --fix;
+ fi
+
+ if [ "$AUTOPRUNE" == "on" ]; then prune; fi
+}
+
+#---------------------------------------------------------------------
+## @Stdout log files list
+## Returns a list of the log files for each spell.
+##
+#---------------------------------------------------------------------
+function log_list() {
+
+ for LINE in `all_spell_status`; do
+ SPELL=`echo $LINE | cut -d : -f1`
+ VER=`echo $LINE | cut -d : -f4`
+ echo "$SPELL-$VER"
+ echo "$SPELL-$VER$EXTENSION"
+ done
+
+}
+
+
+#---------------------------------------------------------------------
+##
+## Removes stale logs.
+##
+#---------------------------------------------------------------------
+function clean_logs() {
+
+ message "${CHECK_COLOR}Cleaning log files... ${DEFAULT_COLOR} "
+
+ debug "libsorcery" "Running clean_logs()"
+
+ LOGS=`log_list`
+
+ for FILE in `ls $INSTALL_LOGS`; do
+ if ! echo -e "$LOGS" | grep -q "^$FILE$"; then
+ message "Removing stale log : $INSTALL_LOGS/$FILE "
+ rm $INSTALL_LOGS/$FILE
+ fi
+ done
+
+ for FILE in `ls $COMPILE_LOGS`; do
+ if ! echo -e "$LOGS" | grep -q "^$FILE$"; then
+ message "Removing stale log : $COMPILE_LOGS/$FILE "
+ rm $COMPILE_LOGS/$FILE
+ fi
+ done
+
+ for FILE in `ls $MD5SUM_LOGS`; do
+ if ! echo -e "$LOGS" | grep -q "^$FILE$"; then
+ message "Removing stale log : $MD5SUM_LOGS/$FILE "
+ rm $MD5SUM_LOGS/$FILE
+ fi
+ done
+}
+
+
+#---------------------------------------------------------------------
+##
+## Updates the activity log.
+##
+#---------------------------------------------------------------------
+function activity_log() { (
+
+ lock_file $ACTIVITY_LOG
+
+ DATE=`date -u +%Y%m%d:%H%M\(%z\)`
+ COMMAND=$1
+ SPELL=$2
+ VERSION=$3
+ OUTCOME=$4
+ INFO=$5
+
+ echo -e "$DATE\t$COMMAND\t$SPELL\t$VERSION\t$OUTCOME\t$INFO" >> $ACTIVITY_LOG
+ unlock_file $ACTIVITY_LOG
+) }
+
+
+#---------------------------------------------------------------------
+##
+## Executes the spell's DETAILS file.
+##
+#---------------------------------------------------------------------
+function run_details() {
+ debug "libsorcery" "starting run_details for $SPELL"
+ if ! codex_set_current_spell_by_name $SPELL; then
+ local GRIMOIRES=`codex_get_all_grimoires`
+ message "${PROBLEM_COLOR}Unable to find spell" \
+ "${SPELL_COLOR}${SPELL}${PROBLEM_COLOR}" \
+ "in grimoire(s): $GRIMOIRES ${DEFAULT_COLOR}"
+ false
+ fi
+}
+
+#---------------------------------------------------------------------
+##
+## unsets variables set by DETAILS. <br>
+## vars: SPELL VERSION SOURCE_DIRECTORY WEB_SITE UPDATED ENTERED SHORT SOURCE*
+##
+## Needs to be merged with the libcodex function since they are both
+## needed to fully unset a DETAILS (afk 4/21/04)
+##
+#---------------------------------------------------------------------
+function unset_details() {
+
+ local VARS="SPELL VERSION SOURCE_DIRECTORY WEB_SITE UPDATED ENTERED SHORT"
+ local TEMP=""
+
+ # Take care or SOURCEx and SOURCEx_URL
+ for i in ${!SOURCE*} ; do
+ TEMP=${TEMP}${i}\\n
+ done
+ VARS="$VARS `echo -e $TEMP | egrep "^SOURCE[[:digit:]]*(_URL)?$"`"
+ unset TEMP
+
+ for i in $VARS ; do
+ unset $i
+ done
+
+}
+
+
+
+
+#---------------------------------------------------------------------
+##
+## Runs C<track> and C<archive> on the current spell.
+##
+## THIS FUNCTION IS DEPRECIATED AND SHOULD NOT BE CALLED FOR ANY REASON
+##
+#---------------------------------------------------------------------
+function boost() {
+
+ debug "libsorcery" "Running boost() on $SPELL THIS IS DEPRECIATED"
+ echo "Running boost() on $SPELL THIS IS DEPRECIATED, please contact the spell's maintainer and ask them to remove this call"
+ true
+}
+
+
+#---------------------------------------------------------------------
+## @Stdout spelllist
+## Returns a list of all installed spells that use linux-pam
+##
+## NOTE: this will become useless when triggers are introduced.
+##
+#---------------------------------------------------------------------
+function find_pam_aware() { (
+
+ cat $SPELL_STATUS |
+ while read LINE; do
+
+ SPELL="`echo $LINE | cut -d : -f1`"
+ STATUS="`echo $LINE | cut -d : -f3`"
+
+ if [ "$STATUS" == "installed" ] ||
+ [ "$STATUS" == "held" ]; then
+
+ SPELL_DIRECTORY=`codex_find_spell_by_name $SPELL`
+
+ if [ -d "$SPELL_DIRECTORY/pam.d" ] &&
+ [ "$SPELL" != "linux-pam" ]
+ then echo $SPELL
+ fi
+
+ fi
+ done
+
+) }
+
+
+#---------------------------------------------------------------------
+##
+## Given a list of source files, returns true if each file exists.
+##
+#---------------------------------------------------------------------
+function verify_source() { (
+
+
+ for SOURCE_FILE in $@; do
+ if ! guess_filename $SOURCE_CACHE/$SOURCE_FILE >/dev/null
+ then
+ message "${PROBLEM_COLOR}Missing ${FILE_COLOR}${1}${DEFAULT_COLOR}"
+ message "${PROBLEM_COLOR}Cast aborting.${DEFAULT_COLOR}"
+ activity_log "cast" "$SPELL" "$VERSION" "failed" \
+ "because it was missing source: $1"
+ return 1
+ fi
+ done
+
+ return 0
+
+) }
+
+
+#---------------------------------------------------------------------
+## @param spell
+## @Stdout filelist
+## Returns a list of each source file used by a spell. Most have only
+## one, but xfree86 for example splits the sources into three separate
+## source files.
+##
+#---------------------------------------------------------------------
+function sources() { (
+
+ local i srcVar
+
+ if [ -z "$SOURCE" ]; then
+ codex_set_current_spell_by_name $1
+ fi
+ get_spell_files
+
+) }
+
+#---------------------------------------------------------------------
+## @param spell
+## @Stdout filelist
+## Returns a list of each source file used by a spell. Most have only
+## one, but xfree86 for example splits the sources into three separate
+## source files.
+##
+#---------------------------------------------------------------------
+function source_urls() { (
+
+ local i srcVar
+
+ if [ -z "$SOURCE" ]; then
+ codex_set_current_spell_by_name $1
+ fi
+ get_spell_files_and_urls|while read -a line; do
+ unset line[0]
+ echo "${line[*]}"
+ done
+
+
+) }
+
+
+#---------------------------------------------------------------------
+## @Stdout filelist
+## Returns a list of files that should not be pruned.
+##
+#---------------------------------------------------------------------
+function generate_keep_list() {
+ local source_keep=$1
+ local cache_keep=$2
+ local type=$3
+ if [[ $type == installed_only ]] ; then
+ {
+ get_all_spells_with_status installed
+ get_all_spells_with_status held
+ } | sort | uniq | while read spell ; do
+ codex_find_spell_by_name $spell
+ done
+ else
+ codex_get_all_spells
+ fi |
+ (
+ echo sorcery-$SORCERY_BRANCH.tar.bz2
+ while read SPELL_DIRECTORY ; do
+ codex_set_current_spell_quick $SPELL_DIRECTORY &>/dev/null
+ sources 2>/dev/null
+ ls /var/cache/sorcery/$SPELL-$VERSION-* 1>&2 2>/dev/null
+ unset VERSION SPELL ${!SOURCE*}
+ done
+ ) 2> $cache_keep > $source_keep
+
+ #Add the README in the cache to list of files to keep
+ echo README >> $cache_keep
+
+}
+
+
+#---------------------------------------------------------------------
+##
+## Removes unnecessary files from the source cache and install cache.
+##
+#---------------------------------------------------------------------
+function prune() {
+
+ debug "libsorcery" "Running prune()"
+ local NO_KEEP
+ local NUM_NO_KEEP=0
+
+ message "${MESSAGE_COLOR}Generating list of files to keep...${DEFAULT_COLOR}"
+ local SOURCE_KEEP="$TMP_DIR/prune-keep-source"
+ local CACHE_KEEP="$TMP_DIR/prune-keep-cache"
+ rm -rf $CACHE_KEEP
+ rm -rf $SOURCE_KEEP
+ generate_keep_list $SOURCE_KEEP $CACHE_KEEP $1
+
+ message "${MESSAGE_COLOR}Cleaning up source cache" \
+ "(${DEFAULT_COLOR}$SOURCE_CACHE${MESSAGE_COLOR})...${DEFAULT_COLOR}"
+ local SOURCE_RM="$TMP_DIR/prune.rm.source"
+ find $SOURCE_CACHE -follow -mindepth 1 -maxdepth 1 -type f|
+ awk -F/ 'BEGIN {
+ while ( getline < ARGV[1] ) {
+ keep[$NF] = 1;
+ }
+ while ( getline < ARGV[2] ) {
+ if ( keep[$NF] != 1 ) {
+ print $0;
+ }
+ }
+ }' $SOURCE_KEEP - |sort|uniq > $SOURCE_RM
+ if test -s $SOURCE_RM ; then
+ NUM_NO_KEEP=$(cat $SOURCE_RM|wc -l)
+
+ message "There are $NUM_NO_KEEP files that can be removed"
+ if query "Edit the list?" "n" ; then
+ edit_file $SOURCE_RM
+ fi
+ if query "Remove the files?" "y" ; then
+ cat $SOURCE_RM|xargs rm
+ fi
+ fi
+
+ message "${MESSAGE_COLOR}Cleaning up install cache" \
+ "(${DEFAULT_COLOR}$INSTALL_CACHE${MESSAGE_COLOR})...${DEFAULT_COLOR}"
+ local CACHE_RM="$TMP_DIR/prune.rm.cache"
+ find $INSTALL_CACHE -mindepth 1 -maxdepth 1 -type f|
+ awk -F/ 'BEGIN {
+ while ( getline < ARGV[1] ) {
+ keep[$NF] = 1;
+ }
+ while ( getline < ARGV[2] ) {
+ if ( keep[$NF] != 1 ) {
+ print $0;
+ }
+ }
+ }' $CACHE_KEEP - |sort|uniq > $CACHE_RM
+ if test -s $CACHE_RM ; then
+ NUM_NO_KEEP=$( cat $CACHE_RM | wc -l )
+ message "There are $NUM_NO_KEEP files that can be removed"
+ if query "Edit the list?" "n" ; then
+ edit_file $CACHE_RM
+ fi
+ if query "Remove the files?" "y" ; then
+ cat $CACHE_RM|xargs rm
+ fi
+ fi
+
+ rm -f $SOURCE_KEEP $CACHE_KEEP $SOURCE_RM $CACHE_RM
+
+}
+
+
+#---------------------------------------------------------------------
+##
+## Sets DISTCC=[on|off] depending on the value of DISTCC_HOSTS.
+## Adds /var/lib/sorcery/build to the C<PATH> if necessary.
+##
+#---------------------------------------------------------------------
+function invoke_build_dir() {
+
+ # This merits some explaination:
+ #
+ # In the /var/lib/sorcery/build dir are frontends for the compilers
+ # they run CCACHE and DISTCC. By putting them in the front of the path
+ # they get run first, then hand the work off to the real compilers.
+ # The script also loads /etc/sorcery/compile_config, this made it
+ # convenient for building stuff outside of sorcery and taking advantage
+ # of ccache/distcc, just do "export PATH="/var/lib/sorcery/build:$PATH""
+ # and you're set, you can also edit the DISTCC hosts on the fly.
+ #
+ # Then someone had the bright idea "if we set DISTCC_HOSTS to the empty
+ # string, distcc wont run!" due to a misconception in how this function
+ # worked, that spell hack would only work /some/ of the time: if you
+ # used ccache in addition to distcc, distcc wouldnt get disabled. *Sigh*
+ #
+ # SO...now we're stuck with a bunch of spells in the field disabling
+ # distcc in an broken way, obviously they all cant change overnight. To
+ # make matters worse inside make() we have to fiddle with the number of
+ # make jobs to use in case distcc is disabled (badly).
+ #
+ # so the answer is:
+ # 1) make a new variable "USE_DISTCC" and set it to "off" if distcc
+ # is being disabled by the spell
+ # 2) check if USE_DISTCC == off in run_compiler, but still check the
+ # other signs just in case its being run manually
+ # 3) add a check for DISABLE_DISTCC so spells can start using that someday
+ # (phase this in after a few months, then phase out the old check below,
+ # but not in run_compiler)
+ # 4) remove the old stupid logic for MAKE_NJOBS (num_hosts + 1),
+ # instead split MAKE_NJOBS up, add a JOBS_PER_HOST multiplier, and add
+ # MAKE_NJOBS to that. This results in the number of jobs being $MAKE_NJOBS
+ # (default 1) when distcc is disabled (bug 6432), and allows an automatic
+ # increase in the number of jobs per host if its desired.
+ #
+
+ USE_DISTCC=off
+ if [[ $DISABLE_DISTCC != yes ]] ; then
+ if test -n "$DISTCC_HOSTS" && spell_ok distcc ; then
+ USE_DISTCC=on
+ fi
+ fi
+
+ if ! spell_ok ccache ; then
+ CCACHE=off
+ fi
+
+ if [[ "$CCACHE" == on ]] || [[ "$USE_DISTCC" == on ]] ; then
+ export PATH="$RUN_COMPILER_DIR:$PATH"
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## @param category/service
+## @Stdout spelllist
+##
+## First argument is a category of spell. Returns a list of spells
+## that match that category. For example,
+## C<find_providers email-client> returns evolution,althea,mutt,etc.
+##
+## This will list spells exactly once, exiled spells are not listed.
+## This also takes into account grimoire ordering. A spell must provide
+## the category in the first grimoire the spell is seen in (because that
+## is the only one that will be cast) otherwise it is not listed.
+##
+## Note to self: move to libcodex
+##
+#---------------------------------------------------------------------
+function find_providers() {
+ local feature="$1"
+ local provider GRIMOIRE spell_path
+ local providers=$(
+ for GRIMOIRE in $(codex_get_all_grimoires); do
+ gawk '/^'"$feature"'[[:blank:]]/ { print $2 }' \
+ "$GRIMOIRE/$PROVIDE_INDEX_FILE"
+ done | get_basenames|sort|uniq)
+
+ # filter out providers we dont want, so far this is:
+ # a) do not provide something in the first grimoire they are found in
+ # b) are exiled.
+ # ex: foo provides FOOBAR in the "stable" grimoire but not the "test"
+ # grimoire and test is searched before stable in this case cast will
+ # use the spell from test which does NOT provide FOOBAR, therefore
+ # we should not list foo as a provider of FOOBAR
+ for provider in $providers; do
+ if ! spell_exiled $provider; then
+ spell_path=$(codex_find_spell_by_name $provider)
+ GRIMOIRE=$(dirname $( dirname $spell_path))
+ # ensure the first place we look for the spell is a provider
+ grep -q "^$feature $spell_path$" $GRIMOIRE/$PROVIDE_INDEX_FILE &&
+ echo $provider
+ fi
+ done
+
+}
+
+
+#---------------------------------------------------------------------
+## @Stdout filelist
+## Returns false if one or more source files for the current spell
+## are missing.
+##
+#---------------------------------------------------------------------
+function verify_sources() {
+ verify_source "`sources $SPELL`";
+}
+
+
+
+#---------------------------------------------------------------------
+##
+## Will set the prefered compression type based on user filled option
+## in dialog menu (either gzip/bzip).
+##
+#---------------------------------------------------------------------
+function set_compression_type() {
+
+ local B_HELP="bzip2 compression (slow, small files)"
+ local G_HELP="gzip compression (fast, larger files)"
+ local T_HELP="no compression/tar (fastest, largest files)"
+
+ while
+
+ COMMAND=`eval $DIALOG ' --title "Currently using : $COMPRESSBIN " \
+ --item-help \
+ --ok-label "Select" \
+ --cancel-label "Exit" \
+ --menu \
+ "" \
+ 0 0 0 \
+ "B" "bzip2 compression" "$B_HELP" \
+ "G" "gzip compression" "$G_HELP" \
+ "T" "no compression/tar" "$T_HELP"'`
+
+ do
+
+ case $COMMAND in
+
+ B) modify_local_config "COMPRESSBIN" "bzip2" &&
+ COMPRESSBIN="bzip2" &&
+ modify_local_config "EXTENSION" ".bz2" ;;
+ G) modify_local_config "COMPRESSBIN" "gzip" &&
+ COMPRESSBIN="gzip" &&
+ modify_local_config "EXTENSION" ".gz" ;;
+ T) modify_local_config "COMPRESSBIN" "tar" &&
+ COMPRESSBIN="tar" &&
+ modify_local_config "EXTENSION" "" ;;
+
+ esac
+
+ done
+
+}
+
+#---------------------------------------------------------------------
+## @param filename
+## @return 0 if filename seems to be compressed
+## @return 1 otherwise
+## Returns true if the name of the file indicates that it should be
+## a compressed file. In other words, this function returns true if
+## the filename ends in .gz, .tgz, .bz2, .zip, .rpm, or .Z.
+##
+#---------------------------------------------------------------------
+function filename_indicates_compression() {
+ local FILENAME=`echo "file$1" | sed 's/^.*\.gz$//' \
+ | sed 's/^.*\.tgz$//' \
+ | sed 's/^.*\.bz2$//' \
+ | sed 's/^.*\.zip$//' \
+ | sed 's/^.*\.Z$//' \
+ | sed 's/^.*\.rpm$//' \
+ | sed 's/^.*\.bz2$//'`
+ [ -z "$FILENAME" ]
+}
+
+
+#---------------------------------------------------------------------
+##
+## 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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libstate b/var/lib/sorcery/modules/libstate
new file mode 100644
index 0000000..2f40b1e
--- /dev/null
+++ b/var/lib/sorcery/modules/libstate
@@ -0,0 +1,819 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+## Handles storage of state information, incluing depends and package
+## files. Also handles looking information up about what is installed
+## and what depends on what.
+##
+## @Copyright Copyright (C) 2002 The Source Mage Team <http://www.sourcemage.org>
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+##
+## Adds a dependency to the a depends database file. Returns 1 is the
+## 3rd or 4th fields are not valid.
+##
+## @param Depends status file
+## @param Name of the "parent" spell
+## @param Name of the spell the parent spell depends on
+## @param "on" or "off" depending on whether the user installed the dependency
+## @param Type of dependency (required or optional)
+## @param option to pass to C&lt;configure&gt; if the dependency is installed
+## @param option to pass to C&lt;configure&gt; if the dependency is not installed
+##
+## @Example add_depends kdelibs alsa-driver on optional --with-alsa --without-alsa
+##
+#---------------------------------------------------------------------
+function add_depends()
+{ # $1=depends file $2=spell, $3=depends, $4=on/off, $5=optional/required, $6=on arg, $7=off arg
+ debug "libstate" "add_depends() - $*"
+
+ #already here for some reason
+ search_depends_status_exact "$@" >/dev/null && return 0
+
+ #probably not needed with commit on succeed
+ #remove stale depends info
+ search_depends_status "$@" >/dev/null && remove_depends_status "$@"
+
+ local depends_status=$1
+ shift
+
+ #ensure the info is valid (perhaps add check that spells exist?)
+ if ( [[ $3 != on ]] && [[ $3 != off ]] ) ||
+ ( [[ $4 != required ]] && [[ $4 != optional ]] )
+ then
+ return 1
+ fi
+
+ echo "$1:$2:$3:$4:$5:$6" >> $depends_status
+
+ return 0
+
+}
+
+#---------------------------------------------------------------------
+## Sets up the uncommitted depends file. If the name isn't
+## found in the hash table create it. If the file already exists
+## move it to the abandoned depends directory and start with a new one.
+## The uncommitted_hash hash table should be hash_export'ed to
+## make it through the call to make.
+##
+## @param spell
+## @param variable name to put the filename name in (pass by reference)
+##
+#---------------------------------------------------------------------
+function get_uncommitted_depends_file() {
+ local SPELL=$1
+ local temp_spell_depends=$(hash_get "uncommitted_hash" $SPELL)
+ if ! [[ $temp_spell_depends ]] ; then
+ temp_spell_depends="$UNCOMMITTED_DEPENDS/$SPELL"
+ hash_put "uncommitted_hash" "$SPELL" "$temp_spell_depends"
+ fi
+ eval "$2=\"$temp_spell_depends\""
+ if [ -e $temp_spell_depends ]; then
+ mkdir -p "$ABANDONED_DEPENDS"
+ mv "$temp_spell_depends" "$ABANDONED_DEPENDS"
+ fi
+ mkdir -p "$UNCOMMITTED_DEPENDS"
+ touch "$temp_spell_depends"
+}
+
+#---------------------------------------------------------------------
+##
+## In case you want to search in all the fields of the $1
+##
+## Arguments can be regexp
+##
+## Prints out the matching line(s)
+## @Stdout the matching line(s)
+## @param depends file
+## @param spell
+## @param depends
+## @param on/off
+## @param optional/required
+## @param on arg
+## @param off arg
+##
+#---------------------------------------------------------------------
+function search_depends_status_exact()
+{ # $1=depends file $2=spell, $3=depends, $4=on/off, $5=optional/required, $6=on arg, $7=off arg
+
+ local depends_status=$1
+ shift
+
+ local a1 a2 a3 a4 a5 a6
+ a1=`esc_str "$1"` ; a2=`esc_str "$2"` ; a3=`esc_str "$3"` ; a4=`esc_str "$4"`; a5=`esc_str "$5"`; a6=`esc_str "$6"`
+ debug "libstate" "search_depends_status_exact: [$a1:$a2:$a3:$a4:$a5:$a6]"
+ grep "^$a1:$a2:$a3:$a4:$a5:$a6$" $depends_status
+
+}
+
+
+#---------------------------------------------------------------------
+## @param depends file
+## @param spell
+## @param depends (optional)
+## @Stdout mathing lines
+## In case you want to search by spell or dependency in $1
+##
+## Arguments can be regexp
+##
+## Prints out the matching line(s)
+##
+#---------------------------------------------------------------------
+function search_depends_status()
+{ # $1=depends file $2=spell, $3=(opt)depends
+
+ local depends_status=$1
+ shift
+
+ [ $# -eq 1 ] && grep "^`esc_str "$1"`:" $depends_status
+ [ $# -gt 1 ] && grep "^`esc_str "$1"`:`esc_str "$2"`:" $depends_status
+
+}
+
+
+
+#---------------------------------------------------------------------
+##
+## @param depends file
+## @param spell
+## @param dependency spell
+## @Stdout list of options
+##
+## Returns a list of options for ./configure from $1
+## If the second argument is given, returns options for the
+## matching pair of spell:dependency
+## Primarily aimed at generating $OPTS contents
+##
+## Prints out one line of output
+#---------------------------------------------------------------------
+function get_depends_options()
+{ # $1=depends_status $2=spell [$3]=dependency spell
+
+ local depends_status=$1
+ shift
+
+ [ -z "$1" ] && {
+ message "${PROBLEM_COLOR}${1:-<null>} is not a spell name${DEFAULT_COLOR}"
+ return
+ }
+
+ local START="$(esc_str $1):"
+ [ -n "$2" ] && START="${START}$(esc_str $2):"
+ debug "libstate" "get_depends_options() - START=$START"
+ awk -F ':' "/^$START/ { if (\$3 == \"on\") OPTS = OPTS \" \" \$5; else OPTS = OPTS \" \" \$6; } END { print OPTS; }" $depends_status
+}
+
+
+#---------------------------------------------------------------------
+##
+## Arguments can be regexp, and all but the spell are optional
+##
+## @param depends file
+## @param spell
+## @param depends
+## @param on/off
+## @param optional/required
+## @param on arg
+## @param off arg
+##
+#---------------------------------------------------------------------
+function remove_depends_status()
+{ # $1=depends file $2=spell, $3=(OPT)depends, $4=(OPT)on/off, $5=(OPT)optional/required, $6=(OPT)on arg, $7=(OPT)off arg
+
+ local depends_status=$1
+ shift
+
+ local a1 a2 a3 a4 a5 a6
+ # why do we esc_str the args? spells shouldnt have regexps in their name
+ # and "on" "off" "optional" and "required" are the only valid
+ # values for a3 and a4, also, with the exception of dispel depends
+ # this isnt ever called with more than a1 as a parameter...
+ #a1=`esc_str $1` ; a2=`esc_str $2` ; a3=`esc_str $3` ; a4=`esc_str $4`;
+ a1=$1 ; a2=$2 ; a3=$3 ; a4=$4;
+ a5=`esc_str $5`; a6=`esc_str $6`
+
+ a2=${a2:-.*}
+ a3=${a3:-.*}
+ a4=${a4:-.*}
+ a5=${a5:-.*}
+ a6=${a6:-.*}
+
+ [[ "$a5" == "[none]" ]] && a5="\\[none\\]"
+
+ sedit "/^$a1:$a2:$a3:$a4:$a5:$a6$/D" $depends_status
+
+}
+
+#---------------------------------------------------------------------
+# Default depends and default provider api
+# default depends examples:
+#
+# default xfree86 provider to ImageMagick is xfree86
+# ImageMagick:X11-LIBS:xfree86
+#
+# default answer to optional_depends graphviz for ImageMagick is "no"
+# ImageMagick:graphviz:off
+#
+#
+# default provider examples:
+# use xorg as X11-LIBS even if its optional
+# xorg:X11-LIBS:on
+#
+# use xfree86 as X11-LIBS unless the [none] option exists (and use that instead)
+# xfree86:X11-LIBS:off
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+##
+## add default depends entry, if $3 is a spell $4 must be on/off
+## if $3 is a PROVIDER $4 must be a spell
+##
+## @param depends file
+## @param spell
+## @param depends/PROVIDER
+## @param on/off
+##
+#---------------------------------------------------------------------
+function add_default_depends() {
+ debug "libstate" "add_default_depends() - $*"
+
+ #already here
+ search_default_depends "$@" >/dev/null && return 0
+
+ local file=$1
+ shift
+ # ensure the info is valid... (this is redundant if calling
+ # from sorcery, but that may not always be the case...
+ if [[ $3 != on ]] && [[ $3 != off ]] ; then
+ return 1
+ fi
+
+ { [[ ! $1 ]] && [[ ! $2 ]] ; } && return 1
+
+ if [[ $1 ]] && ! codex_does_spell_exist $1 &>/dev/null; then
+ return 1
+ fi
+
+ if [[ $2 ]] && ! codex_does_spell_exist $2; then
+ return 1
+ fi
+
+ tfile=`lock_start_transaction $file`
+ remove_default_depends $tfile $1 $2
+ echo "$1:$2:$3" >> $tfile
+ lock_commit_transaction $file
+
+ return 0
+
+}
+
+#---------------------------------------------------------------------
+##
+## Arguments can be regexp, $2, $3 and $4 are optional
+## Caller must lock the file
+##
+## @param depends file
+## @param spell
+## @param depends/PROVIDER
+## @param on/off/spell
+##
+#---------------------------------------------------------------------
+function remove_default_depends() {
+
+ local file=$1
+ shift
+ test -e $file || return
+
+ local a1 a2 a3
+ a1=`esc_str $1` ; a2=`esc_str $2` ; a3=`esc_str $3`
+
+ a3=${a3:-.*}
+
+ sedit "/^$a1:$a2:$a3$/D" $file
+}
+
+#---------------------------------------------------------------------
+##
+## Arguments can be regexp, $2 $3 and $4 are optional
+##
+## @param depends file
+## @param spell
+## @param depends/PROVIDER
+## @param on/off/spell
+##
+#---------------------------------------------------------------------
+function search_default_depends() {
+
+ local default_depends=$1
+ shift
+ [[ -s $default_depends ]] || return
+
+ local a1 a2 a3
+ a1=`esc_str "$1"` ; a2=`esc_str "$2"` ; a3=`esc_str "$3"`
+ a3=${a3:-.*}
+
+ debug "libstate" "search_default_depends: [$a1:$a2:$a3]"
+ grep "^$a1:$a2:$a3$" $default_depends
+}
+
+
+#---------------------------------------------------------------------
+##
+## Arguments can be regexp, $3 and $4 are optional
+##
+## @param depends file
+## @param spell
+## @param PROVIDER
+## @param on/off
+##
+#---------------------------------------------------------------------
+function add_default_provider() {
+ debug "libstate" "add_default_depends() - $*"
+
+ #already here
+ search_default_provider "$@" >/dev/null && return 0
+
+ local file=$1
+ shift
+
+ #ensure the info is valid...
+ { [[ ! $1 ]] && [[ ! $2 ]] ; } && return 1
+ { [[ $3 != on ]] && [[ $3 != off ]] ; } && return 1
+ if [[ $1 ]] && ! codex_does_spell_exist $1 &>/dev/null ; then
+ return 1
+ fi
+ if [[ $2 ]] && ! codex_does_service_exist $2 &>/dev/null ; then
+ return 1
+ fi
+
+ tfile=`lock_start_transaction $file`
+ remove_default_provider $tfile "" $2
+ echo "$1:$2:$3" >> $tfile
+ lock_commit_transaction $file
+
+ return 0
+}
+
+# these are the same, as their countparts but are here for completeness
+# and incase they do someday diverge
+function remove_default_provider() {
+ remove_default_depends "$@"
+}
+
+function search_default_provider() {
+ search_default_depends "$@"
+}
+
+#---------------------------------------------------------------------
+##
+## Adds an entry to the SPELL_STATUS file
+##
+## Arguments may either be SPELL, ACTION, VERSIONS, or just
+## ACTION if there exists SPELL and VERSIONS variables already
+## set.
+## @param spell OR action
+## @param (if spell) action
+## @param (if spell) version
+##
+#---------------------------------------------------------------------
+function add_spell_status()
+{ #$1=spell, $2=action, $3=version, OR $1=action.
+
+ local spell action version date
+ if [ $# -eq 1 ] ; then
+ spell="$SPELL"
+ action="$1"
+ version="$VERSION"
+ else
+ spell=$1
+ action=$2
+ version=$3
+ fi
+ [[ $spell ]] && [[ $action ]] && [[ $version ]] || return 1
+ date=`date +%Y%m%d`
+ tSPELL_STATUS=`lock_start_transaction $SPELL_STATUS`
+ echo "$spell:$date:$action:$version" >> $tSPELL_STATUS
+ lock_commit_transaction $SPELL_STATUS
+
+}
+
+#---------------------------------------------------------------------
+## @param spell name
+##
+## Given a spell, status (installed, held, etc), and version, add_spell
+## adds the spell to the /var/state/sorcery/packages file
+##
+#---------------------------------------------------------------------
+function add_spell() {
+ remove_spell $1
+ add_spell_status $1 $2 $3
+}
+
+
+#---------------------------------------------------------------------
+##
+## @param spell ( [A-Za-z0-9_-] )
+## @param version ( may not contain space,
+## '.' and '*' have special meaning)
+## @Stdout last matching line ( only one even if there are more )
+##
+## Searches the SPELL_STATUS file for a spell and optionaly
+## a version.
+##
+## Prints out the matching spell's status
+## Returns 1 only for critical problems ( missing SPELL_STAUS file )
+## 0 for all other cases ( even when spell is not found )
+##
+#---------------------------------------------------------------------
+function query_spell_status()
+{ #$1=spell, $2=(OPT)version
+
+ local version
+ version=`esc_str ${2:-".*"}`
+
+ tac $SPELL_STATUS | grep -m 1 "^$1:.*:.*:$version$" | \
+ cut -d ":" -f 3
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell name
+##
+## @return 0 if the given spell's status is "installed"
+#---------------------------------------------------------------------
+function real_spell_installed() {
+ [ "$( query_spell_status $1 )" == "installed" ];
+}
+
+
+#---------------------------------------------------------------------
+## @param spell name
+##
+## @return 0 if the given spell's status is "held"
+##
+#---------------------------------------------------------------------
+function real_spell_held() {
+ [ "$( query_spell_status $1 )" == "held" ];
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell name
+##
+## @return 0 if the given spell's status is "installed" or "held"
+#---------------------------------------------------------------------
+function real_spell_ok() {
+ local res="$( query_spell_status $1 )"
+ [ "$res" == "installed" ] || [ "$res" == "held" ]
+}
+
+
+#---------------------------------------------------------------------
+## @Type API
+## @param Provider name
+##
+## @return 0 if any provider of $1 is installed
+#---------------------------------------------------------------------
+function real_provider_ok() {
+ for spell in $(search_depends_status_exact $DEPENDS_STATUS '.*' ".*($1)" \
+ 'on' '.*' '.*' |cut -f2 -d:|cut -f1 -d\(|sort|uniq); do
+ spell_ok $spell && return 0
+ done
+ return 1
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param Spell name
+## @param Provider name
+## @param If empty get the uncommited spell info, if anything else get
+## info from the committed ($DEPENDS_STATUS) database. If the uncommited
+## db doesnt exist (maybe we're not casting) use DEPENDS_STATUS
+##
+## @return 0 if a provider could be found 1 if not
+## @stdout the provider name(s)
+#---------------------------------------------------------------------
+function real_get_spell_provider() {
+ local dep_status
+ if [[ $3 ]] ; then
+ dep_status=$DEPENDS_STATUS
+ else
+ dep_status=$(hash_get uncommitted_hash $1)
+ [[ $dep_status ]] || dep_status=$DEPENDS_STATUS
+ fi
+ search_depends_status_exact $dep_status "$1" ".*($2)" \
+ 'on' '.*' '.*' '.*' |cut -f2 -d:|cut -f1 -d\(|sort|uniq
+}
+
+
+#---------------------------------------------------------------------
+## @param spell name
+##
+## @return 0 if the given spell's status is "exiled"
+##
+#---------------------------------------------------------------------
+function real_spell_exiled() {
+ [ "$( query_spell_status $1 )" == "exiled" ];
+}
+
+
+#---------------------------------------------------------------------
+## @param spell to remove status of
+## Removes all specified offending entries in SPELL_STATUS
+##
+#---------------------------------------------------------------------
+function remove_spell_status()
+{ #$1=spell to remove status of
+
+ tSPELL_STATUS=`lock_start_transaction $SPELL_STATUS`
+ grep -v "^$1:" $SPELL_STATUS > $tSPELL_STATUS
+ lock_commit_transaction $SPELL_STATUS
+
+}
+
+#---------------------------------------------------------------------
+## @param spell name
+##
+## Removes the given spell from the /var/state/sorcery/packages file.
+## if C<EXILE> is set, the spell is changed to "exiled" in the file.
+##
+#---------------------------------------------------------------------
+function remove_spell() {
+ debug "libsorcery" "Running remove_spell() on $1"
+ remove_spell_status $1
+ if [ -n "$EXILE" ]; then
+ add_spell_status $1 "exiled" "0.0"
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## @Stdout All spell stati
+## Just here to round out the SPELL_STATUS functions so that
+## SPELL_STATUS doesn't have to mentioned in libsorcery
+##
+#---------------------------------------------------------------------
+function all_spell_status()
+{
+
+ cat $SPELL_STATUS
+
+}
+
+
+#---------------------------------------------------------------------
+## @param spells
+## sets &lt;spells&gt;'s status to held
+##
+#---------------------------------------------------------------------
+function set_held () {
+ local t_status=$(lock_start_transaction $SPELL_STATUS)
+ cp $SPELL_STATUS $t_status
+ for to_hold in $@; do
+ sedit 's/\(^'$(esc_str $to_hold)':[^:]*:\)installed/\1held/' $t_status
+ done
+ lock_commit_transaction $SPELL_STATUS
+}
+
+
+#---------------------------------------------------------------------
+## @param spells
+## sets &lt;spells&gt;'s status to exiled
+##
+#---------------------------------------------------------------------
+function set_exiled () {
+ local TO_DISPELL=""
+ local TO_EXILE
+ local MY_STATUS
+
+ for TO_EXILE in $@; do
+ MY_STATUS=$(query_spell_status "$TO_EXILE")
+ case "$MY_STATUS" in
+ "" )
+ add_spell_status $TO_EXILE exiled 0.0
+ message "Spell '$TO_EXILE' exiled."
+ ;;
+ "exiled" )
+ message "${MESSAGE_COLOR}Spell ${DEFAULT_COLOR}'$TO_EXILE'" \
+ "${MESSAGE_COLOR}is allready exiled.${DEFAULT_COLOR}"
+ ;;
+ "installed" )
+ local ask="'$TO_EXILE' is installed."
+ ask="${ask} Do you want to dispel and exile it ?"
+ query "$ask" &&
+ TO_DISPELL="$TO_DISPELL $TO_EXILE"
+ ;;
+ * )
+ message "${PROBLEM_COLOR}Can't exile spell" \
+ "${DEFAULT_COLOR}'$TO_EXILE'${PROBLEM_COLOR}" \
+ "which has status '$MY_STATUS'${DEFAULT_COLOR}"
+ ;;
+ esac
+ done
+ if [ -n "$TO_DISPELL" ]; then
+ dispel -e $TO_DISPELL
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## @param spells
+## resets <spells>'s 'exiled' status
+##
+#---------------------------------------------------------------------
+function set_unexiled () {
+ local MY_STATUS
+ local TO_UNEXILE
+ for TO_UNEXILE in $@; do
+ MY_STATUS=$(query_spell_status "$TO_UNEXILE")
+ if [ x"$MY_STATUS" == x"exiled" ]; then
+ remove_spell $TO_UNEXILE
+ else
+ message "${MESSAGE_COLOR}Can't unexile spell${DEFAULT_COLOR}" \
+ "'$TO_UNEXILE'${MESSAGE_COLOR}, it's not exiled${DEFAULT_COLOR}"
+ fi
+ done
+}
+
+
+#---------------------------------------------------------------------
+## @param spells
+## sets <spells>'s status to installed
+##
+#---------------------------------------------------------------------
+function set_unheld () {
+ local t_status=$(lock_start_transaction $SPELL_STATUS)
+ cp $SPELL_STATUS $t_status
+ for to_hold in $@; do
+ sedit 's/\(^'$(esc_str $to_hold)':[^:]*:\)held/\1installed/' $t_status
+ done
+ lock_commit_transaction $SPELL_STATUS
+}
+
+
+#---------------------------------------------------------------------
+## @param status
+## @Stdout spells
+## returns all spells that are in that status
+##
+#---------------------------------------------------------------------
+function get_all_spells_with_status () {
+ lock_file $SPELL_STATUS
+ gawk -F: 'BEGIN { status="'$1'" }
+ ($3 == status) { print $1 }' $SPELL_STATUS
+ unlock_file $SPELL_STATUS
+}
+
+
+#---------------------------------------------------------------------
+## @param variable
+## @param value
+## @param command (optional)
+##
+## Modifies (or adds) an entry in the local/config.
+## If "command" is the third argument, a space will separate the
+## variable and value rather than the equals sign.
+##
+#---------------------------------------------------------------------
+function modify_local_config() {
+ debug "libstate" "modify_local_config() - $*"
+ modify_config $LOCAL_CONFIG "$@"
+}
+
+
+#---------------------------------------------------------------------
+## @param Filename
+## @param variable
+## @param value
+## @param command (optional)
+##
+## Modifies (or adds) an entry in the local/config.
+## If "command" is the third argument, a space will separate the
+## variable and value rather than the equals sign.
+##
+#---------------------------------------------------------------------
+function modify_config() {
+
+ debug "libstate" "modify_config() - $*"
+ local FILE=$1
+ shift;
+
+ if ! [[ $1 ]]; then
+ debug "libstate" "modify_config() - Warning: No name given for config option."
+ return 1
+ fi
+
+ local TEMP separator EQUALS_COL VARIABLE
+
+ # what to use as separator?
+ if [[ $3 == command ]]
+ then separator=" "
+ else separator="=" ; fi
+
+ if ! test -f $FILE; then
+ if ! test -d $(dirname $FILE); then
+ if test -e $(dirname $FILE); then
+ message "Trying to modify $FILE, but $(dirname $FILE) is not a directory"
+ return 1
+ fi
+ mkdir -p $(dirname $FILE)
+ fi &&
+ touch $FILE || {
+ message "Failed to create $FILE"
+ return 1
+ }
+ fi
+
+ # remove previous reference
+ tFILE=`lock_start_transaction $FILE`
+ grep -v "^[[:blank:]]*${1}${separator}" $FILE > $tFILE
+
+ # justifying...
+ EQUALS_COL=$((20-${#1}))
+ [ $EQUALS_COL -lt 0 ] && EQUALS_COL=0
+ TEMP=""
+ for (( ; EQUALS_COL>0 ; EQUALS_COL-- )) ; do
+ TEMP="$TEMP "
+ done
+
+ debug "libstate" "modify_config() - entering new value $VARIABLE $seperator $@ into $tFILE"
+ # put new value and justification in
+ echo "${TEMP}${1}${separator}\"$2\"" >> $tFILE
+
+ lock_commit_transaction $FILE
+
+}
+
+#---------------------------------------------------------------------
+## @param Filename
+## @param variable
+## @param command (optional)
+##
+## Modifies (or adds) an entry in the local/config.
+## If "command" is the third argument, a space will separate the
+## variable and value rather than the equals sign.
+##
+#---------------------------------------------------------------------
+function remove_config() {
+
+ debug "libstate" "remove_config() - $*"
+ local FILE=$1
+ shift;
+
+ if ! [[ $1 ]]; then
+ debug "libstate" "remove_config() - Warning: No name given for config option."
+ return 1
+ fi
+
+ local TEMP separator EQUALS_COL VARIABLE
+
+ # what to use as separator?
+ if [[ $2 == command ]]
+ then separator=" "
+ else separator="=" ; fi
+
+ # remove previous reference
+ tFILE=`lock_start_transaction $FILE`
+ grep -v "^[[:blank:]]*${1}${separator}" $FILE > $tFILE
+ lock_commit_transaction $FILE
+
+}
+
+#---------------------------------------------------------------------
+## @param grimoire-name
+##
+## returns true if the grimoire is set local, false otherwise.
+#---------------------------------------------------------------------
+function codex_is_local () {(
+ local g_name=$1
+
+ local grimoire=$(codex_find_grimoire $g_name)
+ if ! [[ $grimoire ]]; then
+ return 1
+ fi
+
+ . "$grimoire/GRIMOIRE"
+
+ [[ $CODEX_IS_LOCAL == "yes" ]]
+)}
+
+
+#---------------------------------------------------------------------
+##
+## 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
+##
+#---------------------------------------------------------------------
+# vim: filetype=sh:tabstop=4:shiftwidth=4:expandtab
diff --git a/var/lib/sorcery/modules/libsummon b/var/lib/sorcery/modules/libsummon
new file mode 100644
index 0000000..e6d5125
--- /dev/null
+++ b/var/lib/sorcery/modules/libsummon
@@ -0,0 +1,421 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+## @Synopsis Internal api for summoning/printing spell file information. Mainly for use by cast and summon, who both need to "summon".
+##
+## This just wraps up calls to run_details, get_spell_files_and_urls
+## and download_files. Along with doing the logging and other stuff.
+## This is mainly to reduce duplication of code between cast and summon
+## and to make it so cast doesn't have to actually run the summon script.
+##
+## @Copyright Copyright (C) 2004 The Source Mage Team <http://www.sourcemage.org>
+## @Copyright Copyright (C) 2005 The Source Mage Team <http://www.sourcemage.org>
+##
+## @Contributors Andrew Stitt <astitt@sourcemage.org)
+## @Contributors Paul Mahon <pmahon@sourcemage.org)
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## @param Spell name to summon
+## @return 0 if all the source urls were downloaded/found
+## 1 if any of them failed.
+## As the name would imply, summon a spell given its name.
+## The spell is sourced in a subshell and thus no variable leakage
+## will occur to the caller (although the function could be effected
+## by leakage from other places). This function will take care of
+## locking out other processes from downloading the same spell.
+## Also takes care of making entries in the activity log
+##
+#---------------------------------------------------------------------
+function summon_spell() {
+ $STD_DEBUG
+ local SPELL=$1
+ local rc=1
+ if [ -n "${SPELL}" ] && lock_resources "summon" "${SPELL}"; then
+ #local dl_dir=/tmp/sorcery/summon/${SPELL:-none}
+ local dl_dir=$TMP_DIR/${SPELL:-none}
+ debug "libsummon" "dl_dir is $dl_dir"
+ mkdir -p $dl_dir && pushd $dl_dir &>/dev/null &&
+
+ # run in a subshell rather than try to unset_details
+ # and instead jump through hoops to get the return code correctly
+ ( local rc=1
+ run_details &&
+ persistent_load &&
+ if test -x $SCRIPT_DIRECTORY/DOWNLOAD ; then
+ . $SCRIPT_DIRECTORY/DOWNLOAD
+ else
+ default_download
+ fi &&
+ persistent_save
+ # save the rc, then unlock
+ rc=$?
+ unlock_resources "summon" "${SPELL}"
+
+ if [[ $rc == 0 ]]; then
+ activity_log "summon" "$SPELL" "$VERSION" "success"
+ else
+ activity_log "summon" "$SPELL" "$VERSION" "failure"
+ fi
+
+ # this actually only returns from the subshell, not the function
+ return $rc
+ ) &&
+ popd &>/dev/null
+ rm -rf $dl_dir
+ rc=$?
+ fi
+ return $rc
+}
+
+#-------------------------------------------------------------------------
+## Helper routine to dump out the files and urls for a spell given that
+## the caller has run run_details.
+##
+## @Stdout multiple lines, one per SOURCE/SOURCEx as "SOURCE SOURCE_URL[0] SOURCE_URL[1]"
+#-------------------------------------------------------------------------
+function get_spell_files_and_urls() {
+ $STD_DEBUG
+ local url src
+ for src in $(real_get_source_nums SOURCE); do
+ url="$src"'_URL[*]'
+ src="${!src}"
+ [[ ${src} ]] && echo ${src} ${!url}
+ done
+}
+
+
+#-------------------------------------------------------------------------
+## @param none
+##
+## Call acquire_src for each SOURCEx_URL
+##
+#-------------------------------------------------------------------------
+function real_default_download() {
+ $STD_DEBUG
+ local SOURCE_NUMBER
+ for SOURCE_NUMBER in $(real_get_source_nums X); do
+ SOURCE_NUMBER=${SOURCE_NUMBER/X/}
+ acquire_src $SOURCE_NUMBER
+ done
+}
+
+
+#-------------------------------------------------------------------------
+## @param source number or '' for the first $SOURCE
+##
+## Acquire the source. Check locally unless options insist on not
+## doing so.
+##
+## Inspects $FORCE_DOWNLOAD and ${FORCE_DOWNLOAD[$srcnum]}
+## if either are set then downloading is forced, a local copy of the
+## file is ignored.
+#-------------------------------------------------------------------------
+function real_acquire_src() {
+ $STD_DEBUG
+ local DLNUM="$1"
+ local SVAR="SOURCE${DLNUM}"
+ local target=${!SVAR}
+
+
+ REASON="for spell ${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}"
+
+ # maybe we already have the file
+ # but we can only use it if not FORCE_DOWNLOADing
+ if file_exists "$SOURCE_CACHE/$target " &&
+ ! [[ ${FORCE_DOWNLOAD} ]] &&
+ ! [[ ${FORCE_DOWNLOAD[${DLNUM:-1}]} ]] ; then
+ message "${MESSAGE_COLOR}Found source file" \
+ "${FILE_COLOR}${target}${DEFAULT_COLOR}" \
+ "$REASON in ${FILE_COLOR}${SOURCE_CACHE}${DEFAULT_COLOR}"
+ return 0
+ fi
+ # else the source must be downloaded/updated
+ download_src "$DLNUM"
+}
+
+#-------------------------------------------------------------------------
+## @param source number or '' for the first $SOURCE
+##
+## Expands $SOURCEx, $SOURCEx_URL[*] and $SOURCEx_HINTS
+## then calls download_src_args with them.
+##
+#-------------------------------------------------------------------------
+function real_download_src() {
+ $STD_DEBUG
+ local DLNUM="$1"
+ local SVAR="SOURCE${DLNUM}"
+ local SURLVAR=${SVAR}'_URL[*]'
+ local SHINTVAR=${SVAR}'_HINTS'
+
+ local target=${!SVAR}
+ local urls=${!SURLVAR}
+ local hints=${!SHINTVAR}
+
+ [[ "$target" ]] || {
+ message "Empty value in $SVAR!!"
+ return 1
+ }
+ [[ "$urls" ]] || {
+ message "No urls in $SURLVAR!!"
+ return 1
+ }
+ download_src_args "$target" "$urls" "$hints"
+}
+
+#-------------------------------------------------------------------------
+## @param file
+## @param url list
+## @param url options
+##
+## Download the resource, handles file and tree discrepencies, if a tree is
+## downloaded it is repackaged as the specified file for later use.
+##
+## Only tar.bz2 files are acceptable caches for repackaged trees
+##
+#-------------------------------------------------------------------------
+function download_src_args() {
+ $STD_DEBUG
+ local target="$1"
+ local urls="$2"
+ local hints="$3"
+
+ message "${MESSAGE_COLOR}Downloading source file" \
+ "${FILE_COLOR}${target}${DEFAULT_COLOR}" \
+ "${REASON}"
+
+ # get the update tree if we think there is one
+ local new_target=$target
+ unpack_for_update "$target" "$urls" "$hints" new_target guess_type
+
+ # this does the actual downloading:
+ local summon_type summon_target
+ if ! download_src_sub "$new_target" "$urls" "$hints" "$guess_type" \
+ summon_target summon_type; then
+ if [[ $guess_type == tree ]] && test -f $SOURCE_CACHE/$target; then
+ if [[ $STRICT_SCM_UPDATE == off ]] ; then
+ message "${PROBLEM_COLOR}Update of" \
+ "${FILE_COLOR}${target}${DEFAULT_COLOR}" \
+ "${PROBLEM_COLOR}failed, falling back to old" \
+ "version${DEFAULT_COLOR}"
+ # cleanup unpacked tree
+ rm -rf $new_target
+ return 0
+ fi
+ fi
+ message "${PROBLEM_COLOR}Download of" \
+ "${FILE_COLOR}${target}${DEFAULT_COLOR}" \
+ "${PROBLEM_COLOR}failed${DEFAULT_COLOR}"
+ return 1
+ fi
+
+ # look at what we got
+ if ! [[ $summon_target ]] ; then
+ message "${PROBLEM_COLOR}Empty value for downloaded target, file a bug" \
+ "if you see this.${DEFAULT_COLOR}"
+ return 255
+ fi
+
+ # repackage if necessary and move to $SOURCE_CACHE
+ if [[ "$summon_type" == tree ]] ; then
+
+ # incorrect guess, set new_target appropriatly
+ if [[ "$guess_type" == file ]] ; then
+ new_target="${target/.tar.bz2/}"
+ fi
+
+ if ! test -d $summon_target; then
+ message "${PROBLEM_COLOR}Value for downloaded target ($summon_target)" \
+ "is not a directory, but a tree was downloaded, file a bug" \
+ "if you see this.${DEFAULT_COLOR}"
+ return 255
+ fi
+ if [[ $summon_target != $new_target ]] ; then
+ mv -f $summon_target $new_target
+ fi &&
+ message "Repackaging $target"
+ tar --remove-files -cjf $target $new_target &&
+ rm -rf $new_target
+ elif [[ "$summon_type" == file ]] ; then
+ # there is no penalty for guessing tree when the result is a file
+
+ if [[ $summon_target != $target ]] ; then
+ mv -f $summon_target $target
+ fi
+ else
+ message "${PROBLEM_COLOR}Unknown download type: \"$summon_type\""\
+ "at \"$summon_target\". Please file a bug if you see this." \
+ "${DEFAULT_COLOR}"
+ return 1
+ fi
+ rm -rf $SOURCE_CACHE/$target &&
+ mv $target $SOURCE_CACHE
+}
+
+#-------------------------------------------------------------------------
+##
+## @param file
+## @param url list
+## @param url options
+## @param summon target return value, pass by reference
+## @param summon type return value, pass by reference
+##
+## Download the resource, first try a leapforward url, then the given
+## urls and finally the fallbacks.
+#-------------------------------------------------------------------------
+function download_src_sub() {
+
+ $STD_DEBUG
+
+ local target="$1"
+ local url_list="$2"
+ local hints="$3"
+ local guess_type=$4
+ local _summon_target=$5
+ local _summon_type=$6
+
+ {
+ [[ $guess_type == file ]] &&
+ download_from_leapforward "$target" "$url_list" "$hints" \
+ "$_summon_target" "$_summon_type"
+ } ||
+
+ url_download_expand_sort "$target" "$url_list" "$hints" \
+ "$_summon_target" "$_summon_type" ||
+
+ { # dont use fallback if the type is not a file (bug 9847)
+ [[ $guess_type == file ]] &&
+ download_from_fallback "$target" "$url_list" "$hints" \
+ "$_summon_target" "$_summon_type"
+ }
+
+}
+
+#-------------------------------------------------------------------------
+##
+## Download the specified resource from the leapforward url if one is
+## given.
+##
+## @param file
+## @param url list
+## @param url options
+## @param summon target return value, pass by reference
+## @param summon type return value, pass by reference
+#-------------------------------------------------------------------------
+function download_from_leapforward() {
+ $STD_DEBUG
+ if ! [ -n "$LEAPFORWARD_URL" ]; then
+ return 1
+ fi
+
+ local target="$1"
+ local url_list="$2"
+ local hints="$3"
+ local _summon_target=$4
+ local _summon_type=$5
+
+ message "${MESSAGE_COLOR}Attempting to get file from" \
+ "leap-forward mirror ${DEFAULT_COLOR}${LEAPFORWARD_URL}"
+ # leap forwards dont need to be expanded or sorted
+ url_download "$target" "$LEAPFORWARD_URL/$target" "$hints" \
+ "$_summon_target" "$_summon_type"
+}
+
+#-------------------------------------------------------------------------
+##
+## Attempt to get the resource from the fallback urls, try each one in
+## a random order.
+##
+## @param file
+## @param url list
+## @param url options
+## @param summon target return value, pass by reference
+## @param summon type return value, pass by reference
+##
+#-------------------------------------------------------------------------
+function download_from_fallback() {
+ $STD_DEBUG
+ local target="$1"
+ local url_list="$2"
+ local hints="$3"
+ local _summon_target=$4
+ local _summon_type=$5
+
+ message "${MESSAGE_COLOR}Attempting to get file from fall-back mirrors" \
+ "${DEFAULT_COLOR}"
+
+ # slow and painful buildup of all fallback mirrors in quasi random order
+ local i idx
+ local FALL_BACKS
+ local offset=$[${RANDOM} % $FURLNUM]
+ for (( i=0; $i < $FURLNUM; i++ )); do
+ idx=$[($i + $offset) % $FURLNUM]
+ FALL_BACKS="$FALL_BACKS ${FALLBACK_URL_MIRROR[$idx]}/$target"
+ done
+ [ -n "$FALL_BACKS" ] &&
+ # dont order fall backs, above we use a random ordering
+ url_download "$target" "$FALL_BACKS" "$hints" "$_summon_target" \
+ "$_summon_type"
+}
+
+#-------------------------------------------------------------------------
+##
+## Make a guess as to whether or not the resource being downloaded
+## will be a file or a tree if it is a tree and the cached source exists
+## then unpack it in the current directory so it may be updated
+##
+## @param target
+## @param url_list
+## @param hints
+## @param new target return value pass by reference
+## @param guessed type return value pass by reference
+##
+##
+#-------------------------------------------------------------------------
+function unpack_for_update() {
+ $STD_DEBUG
+ local target="$1"
+ local url_list="$2"
+ local hints="$3"
+ local new_target_ref=$4
+ local guess_type_ref=$5
+ local _new_target
+ local _guess_type
+
+ # hard-coded list of url prefixes that generally download trees
+ local tree_prefixes="cvs dir rsync smgl_tla svn"
+ local prefix=$(url_get_prefix $url_list)
+ if ! list_find "$hints" file &&
+ list_find "$hints" tree || list_find "$tree_prefixes" "$prefix" ; then
+ _new_target="${target/.tar.bz2/}"
+ eval "$guess_type_ref=\"tree\""
+ eval "$new_target_ref=\"\$_new_target\""
+
+ # if $target exists in $SOURCE_CACHE and we think the download might be
+ # a tree, unpack it so the tree can be updated
+ if test -f $SOURCE_CACHE/$target && ! list_find "$hints" no_update; then
+ unpack_file_simple $target || return 1
+ if ! test -d "$_new_target" ; then
+ debug "libsummon" "unpacked $target but to somewhere strange"
+ fi
+ fi
+ else
+ eval "$new_target_ref=\"\$target\""
+ eval "$guess_type_ref=\"file\""
+ fi
+ return 0
+}
+#---------------------------------------------------------------------
+## 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
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libtablet b/var/lib/sorcery/modules/libtablet
new file mode 100644
index 0000000..36b8908
--- /dev/null
+++ b/var/lib/sorcery/modules/libtablet
@@ -0,0 +1,914 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+## @Synopsis Functions for dealing with tablet
+## @Copyright (C) 2004 The Source Mage Team <http://www.sourcemage.org>
+##
+## tablet layout version 0:
+## layout 0 is anything unversioned with all its problems, if a
+## tablet like this is seen it should be updated to version 1,
+## the defects that exist are enumerated below, future tablet
+## versions will not require this as they will be more fully
+## documented and formal accessor functions will deal with interfacing
+## with them
+##
+## tablet layout version 1:
+##
+## $TABLET_PATH/$SPELL/<timestamp>/
+## build_api
+## depends
+## grimoire/<all files from the grimoire dir>
+## logs/[install,md5sum,compile] (links to real files)
+## roots (all important FOO_ROOT values)
+## section/<all files from the section dir>
+## section_name
+## sources (the sources and urls used)
+## spell/<all spell files>
+## spell_config
+## spell_config.p
+## status (installed or held)
+## tb_version (tablet version)
+## updated (value of $UPDATED)
+## patchlevel (value of $PATCHLEVEL) (optional,
+## defaults to 0)
+## security_patch (value of $SECURITY_PATCH)
+## (optional, defaults to 0, name still undecided upon)
+## updated (value of $UPDATED)
+## version (value of $VERSION)
+## cache symlink to cache archive
+##
+## known defects pre tablet version 1 (and the functions that fix them) :
+## no version file : tablet_0_repair_version
+## no updated file : tablet_0_repair_updated
+## spell/<spellname>/<spell files>, should be spell/<spell files>
+## tablet_0_repair_spell DONE
+## no tb_version : tablet_0_repair (bumps to version 1)
+##
+#---------------------------------------------------------------------
+##
+## Terminology:
+## "the tablet" the directory $TABLET_PATH and everything in it
+## "tablet chapter" A spell's directory within the tablet eg:
+## $TABLET_PATH/$SPELL/...
+## "tablet page" A specific instance of a spell in a chapter eg:
+## $TABLET_PATH/$SPELL/<timestamp>/...
+## this is sometimes called a tablet dir, but I'm trying to phase that out
+##
+## Accessors Routines (thus far):
+## tablet_get_spell_file
+## tablet_get_section_file
+## tablet_get_grimoire_file
+## tablet_get_build_api
+## tablet_get_version
+## tablet_get_updated
+## tablet_get_patchlevel
+## tablet_get_security_patch
+## tablet_get_depends
+## tablet_get_status
+## tablet_get_sources
+## tablet_get_spell_filter
+## tablet_get_section_filter
+## tablet_get_grimoire_filter
+## tablet_load_roots
+## tablet_unload_roots
+## tablet_get_tb_version
+## tablet_get_spell_name_from_path
+##
+## Cleanse routines:
+## tablet_cleanse_tablet : Fix the whole tablet
+## tablet_cleanse_chapter : Fix a chapter
+## tablet_coalesce_files : hardlink identical files to save space
+## tablet_fix_duplicates
+## determine if a tablet points back at itself through the install log
+##
+##
+##
+##
+##
+#---------------------------------------------------------------------
+
+
+################################### Accessors ########################
+
+function tablet_get_version() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/version && value=$(<$tb_dir/version) || return 2 ;;
+ *) return 3 ;;
+ esac
+ eval "$2=\"$value\""
+ return 0
+}
+
+function tablet_get_updated() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/updated && value=$(<$tb_dir/updated) || return 2 ;;
+ *) return 3 ;;
+ esac
+ eval "$2=\"$value\""
+ return 0
+}
+
+function tablet_get_patchlevel() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/patchlevel && value=$(<$tb_dir/patchlevel) || value=0 ;;
+ *) return 3 ;;
+ esac
+ # default value is 0 for patchlevel
+ eval "$2=\"$value\""
+ return 0
+}
+
+function tablet_get_security_patch() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/security_patch && value=$(<$tb_dir/security_patch) || value=0;;
+ *) return 3 ;;
+ esac
+ # default value is 0 for security_patch
+ eval "$2=\"$value\""
+ return 0
+}
+
+function tablet_get_build_api() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/build_api && value=$(<$tb_dir/build_api) || return 2 ;;
+ *) return 3 ;;
+ esac
+ eval "$2=\"$value\""
+ return 0
+}
+
+function tablet_get_depends() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/depends && value=$tb_dir/depends|| return 2 ;;
+ *) return 3 ;;
+ esac
+ eval "$2=\"$value\""
+ return 0
+}
+
+function tablet_get_status() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/status && value=$(<$tb_dir/status) || return 2 ;;
+ *) return 3 ;;
+ esac
+ eval "$2=\"$value\""
+ return 0
+}
+
+function tablet_get_sources() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/sources && value=$(<$tb_dir/sources) || return 2 ;;
+ *) return 3 ;;
+ esac
+ eval "$2=\"$value\""
+ return 0
+}
+
+function tablet_get_spell_file() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/spell/$2 && value=$tb_dir/spell/$2 || return 2 ;;
+ *) return 3 ;;
+ esac
+ eval "$3=\"$value\""
+ return 0
+}
+
+function tablet_get_persistent_config() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/spell_config.p && value=$tb_dir/spell_config.p || return 2 ;;
+ *) return 3 ;;
+ esac
+ eval "$2=\"$value\""
+ return 0
+}
+
+function tablet_get_section_file () {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/section/$2 && value=$tb_dir/section/$2 || return 2 ;;
+ *) return 3 ;;
+ esac
+ eval "$3=\"$value\""
+ return 0
+}
+
+function tablet_get_grimoire_file() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/grimoire/$2 && value=$tb_dir/grimoire/$2 || return 2 ;;
+ *) return 3 ;;
+ esac
+ eval "$3=\"$value\""
+ return 0
+}
+
+function tablet_get_log_file() {
+ local tb_dir=$1 tb_version value
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/logs/$2 && value=$tb_dir/logs/$2 || return 2 ;;
+ *) return 3 ;;
+ esac
+ eval "$3=\"$value\""
+ return 0
+}
+
+function tablet_get_spell_filter() {
+ tablet_get_spell_file $1 $2 $3
+}
+function tablet_get_section_filter() {
+ tablet_get_section_file $1 $2 $3
+}
+function tablet_get_grimoire_filter() {
+ tablet_get_grimoire_file $1 $2 $3
+}
+
+function tablet_load_roots() {
+ local tb_dir=$1 tb_version
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 1
+ case $tb_version in
+ 1) test -f $tb_dir/roots && source $tb_dir/roots || return 2 ;;
+ *) return 3 ;;
+ esac
+ source $STATE_CONFIG
+}
+
+function tablet_unload_roots() {
+ source $ROOTS_CONFIG
+ source $STATE_CONFIG
+}
+
+function tablet_get_tb_version() {
+ local tb_dir=$1
+ if ! [[ $tb_dir ]] ; then
+ message "nothing passed in!"
+ return 1
+ fi
+ if ! test -f $tb_dir/tb_version ||
+ [[ "$(cat $tb_dir/tb_version)" == 0 ]] ; then
+ tablet_0_repair $tb_dir || return 1
+ fi
+ local ____tb_version=$(<$tb_dir/tb_version)
+ if test $____tb_version -gt $TABLET_MAX_VERSION; then
+ message "${PROBLEM_COLOR}This sorcery is too old for tablet version" \
+ "$tb_version! Please update to the newer version of sorcery" \
+ "or recast this spell${DEFAULT_COLOR}"
+ return 255
+ fi
+ eval "$2=\"$____tb_version\""
+ return 0
+}
+
+#---------------------------------------------------------------------
+## determine the spell associated with a tablet path
+## @param tablet path
+## @stdout spell name
+##
+## tablet paths are $TABLET_PATH/<spell>/<timestamp>
+## dirname of that is $TABLET_PATH/<spell>
+## basename of that is <spell>
+#---------------------------------------------------------------------
+function tablet_get_spell_name_from_path() {
+ [[ $1 ]] && basename $(dirname $1)
+}
+
+######################################################################
+
+#---------------------------------------------------------------------
+## setup a unique tablet directory
+## @param spell name
+## @stdout path to unique tablet directory
+## @return 0 if the directory was made, 1 if not
+#---------------------------------------------------------------------
+function tablet_get_path() {
+ local SPELL=$1
+ local tb_dir=$TABLET_PATH/$SPELL/$(date +%Y%m%d%H%M%S)
+ local sleep_time
+ local made
+
+ mkdir -p $TABLET_PATH/$SPELL &&
+ for (( i=0 ; $i < 20 ; i++ )) ; do
+ if mkdir $tb_dir > /dev/null; then
+ made=1
+ break
+ fi
+ let sleep_time=$RANDOM%3
+ sleep $sleep_time
+ tb_dir=$TABLET_PATH/$SPELL/$(date +%Y%m%d%H%M%S)
+ done
+ if [[ $made ]]; then
+ echo $tb_dir
+ return 0
+ fi
+ return 1
+}
+
+#----------------------------------------------------------------------
+# @param spell name
+# @param upvar
+# @param timestamp (optional)
+#----------------------------------------------------------------------
+function tablet_find_spell_dir() {
+ local SPELL=$1
+ if ! test -d $TABLET_PATH; then
+ mkdir -p $TABLET_PATH
+ return 1
+ fi
+ local __spell_dir
+ local base_dir=$TABLET_PATH/$SPELL
+ if ! [[ $base_dir ]] || ! test -d $base_dir ; then
+ return 1
+ fi
+
+ if [[ $3 ]] ; then
+ local timestamp=$3
+ if test -d $base_dir/$timestamp; then
+ __spell_dir=$base_dir/$timestamp
+ eval "$2=\"$__spell_dir\""
+ return 0
+ fi
+ return 1 # requested a dir that was not there
+ fi
+ # print the newest one
+ local BREAK
+ function tablet_find_spell_dir_sub() {
+ tablet_is_spell_version_installed $SPELL $1 quiet &&
+ tablet_does_tablet_point_to_itself $1 quiet &&
+ __spell_dir=$1 &&
+ BREAK=yes
+ }
+ iterate tablet_find_spell_dir_sub $'\n' \
+ "$(find $base_dir -mindepth 1 -maxdepth 1 -type d)"
+ if ! [[ $__spell_dir ]] ; then
+ # base dir exists but is empty ( ideally this wont happen, but play it safe)
+ return 1
+ fi
+ eval "$2=\"$__spell_dir\""
+ return 0
+}
+
+#----------------------------------------------------------------------
+# @param spell name
+# @param upvar
+# quicker dirtier version of above without special checks since the
+# tablet in a cache tarball has nothing to do with the installed system
+#----------------------------------------------------------------------
+function tablet_find_resurrect_dir() {
+ local SPELL=$1
+ if ! test -d $TABLET_PATH; then
+ return 1
+ fi
+ local __spell_dir
+ local base_dir=$TABLET_PATH/$SPELL
+ if ! [[ $base_dir ]] || ! test -d $base_dir ; then
+ return 1
+ fi
+
+ local __spell_dir=$(find $base_dir -mindepth 1 -maxdepth 1 -type d|head -n 1)
+ if ! [[ $__spell_dir ]] ; then
+ # base dir exists but is empty ( ideally this wont happen, but play it safe)
+ return 1
+ fi
+ eval "$2=\"$__spell_dir\""
+ return 0
+}
+
+#----------------------------------------------------------------------
+## @param tablet dir
+## @globals everything that comes with a spell...
+#----------------------------------------------------------------------
+function tablet_install_spell_files() {
+ pushd $1 &>/dev/null || {
+ message "Failed to enter $1"
+ return 1
+ }
+
+ # this is tablet version 1
+ echo 1 > tb_version
+
+ # directories
+ mkdir spell section grimoire
+
+ cp -R $SCRIPT_DIRECTORY/* spell
+
+ local section_files=$(find $SECTION_DIRECTORY -maxdepth 1 -type f)
+ if [[ $section_files ]] ; then cp $section_files section; fi
+
+ local grimoire_files=$(find $GRIMOIRE -maxdepth 1 -type f)
+ if [[ $grimoire_files ]] ; then cp $grimoire_files grimoire; fi
+
+ # magic values we want to be able to easily look up
+ echo $VERSION > version
+ echo $UPDATED > updated
+ echo ${PATCHLEVEL:-0} > patchlevel
+ echo ${SECURITY_PATCH:-0} > security_patch
+ echo $BUILD_API > build_api
+ echo $SECTION > section_name
+ echo installed > status # this could be held or something else
+
+ # SPELL_CONFIG and persistent variables
+ test -e $SPELL_CONFIG && cp $SPELL_CONFIG spell_config
+ test -e $SPELL_CONFIG.p && cp ${SPELL_CONFIG}.p spell_config.p
+
+ # depends info
+ test -e $spell_depends && cp $spell_depends depends
+
+ # logs
+ mkdir logs
+ ln -s $INST_LOG logs/install
+ ln -s $MD5_LOG logs/md5sum
+ ln -s $C_LOG_COMP logs/compile
+
+ # cache archive
+ if [ "$ARCHIVE" == "on" ]; then
+ ln -s $CACHE_COMP cache
+ fi
+
+ # roots
+ echo INSTALL_ROOT="$INSTALL_ROOT" > roots
+ echo TRACK_ROOT="$TRACK_ROOT" >> roots
+ echo STATE_ROOT="$STATE_ROOT" >> roots
+
+ # all the sources and their urls
+ get_spell_files_and_urls > sources
+
+ popd &>/dev/null
+}
+
+
+#---------------------------------------------------------------------
+## this is to set a spell based on whats in the installed grimoire
+## possibly for re-casting, not sure what else...
+##
+## Note that this function does not use the tablet_get accessors for
+## efficiency.
+#---------------------------------------------------------------------
+function tablet_set_spell() {
+ codex_clear_current_spell
+ SPELL=$1
+ if [[ $2 ]] && test -d $2; then
+ SPELL_DIRECTORY=$2
+ else
+ tablet_find_spell_dir $SPELL SPELL_DIRECTORY || return 1
+ fi
+
+ VERSION=$(<$SPELL_DIRECTORY/version)
+
+ # Directories
+ SCRIPT_DIRECTORY=$SPELL_DIRECTORY/spell
+ SECTION_DIRECTORY=$SPELL_DIRECTORY/section
+ GRIMOIRE=$SPELL_DIRECTORY/grimoire
+
+ # Names
+ SECTION=$(<$SPELL_DIRECTORY/section)
+
+ SPELL_CONFIG="$SPELL_DIRECTORY/spell_config"
+ if [ -f $SPELL_CONFIG ]; then
+ . $SPELL_CONFIG > /dev/null 2> /dev/null
+ fi
+
+ persistent_load
+ . $SPELL_DIRECTORY/DETAILS 1>/dev/null 2>&1
+ persistent_clear
+
+ BUILD_API=$(<$SPELL_DIRECTORY/build_api)
+ # if BUILD_API isnt set something is wrong, but just be safe
+ [[ -z $BUILD_API ]] && BUILD_API=1
+
+ INST_LOG=$SPELL_DIRECTORY/logs/install
+ MD5_LOG=$SPELL_DIRECTORY/logs/md5sum
+ C_LOG_COMP=$SPELL_DIRECTORY/logs/compile
+
+
+ local given_tablet_path=$TABLET_PATH
+
+ . $SPELL_DIRECTORY/roots
+ . $STATE_CONFIG
+
+ TABLET_PATH=$given_tablet_path
+
+ return 0
+}
+
+
+############################ REPAIR files #############################
+function tablet_import_repair_files() {
+ local tablet_path=$1
+ local dir rc chapter page
+
+ for chapter in $tablet_path/* ; do
+ test -d $chapter || continue
+ for page in $chapter/*; do
+ test -d $page || continue
+ tablet_import_repair_files_page $page
+ done
+ done
+}
+
+function tablet_import_repair_files_page() { (
+ local page=$1
+ local spell=$(tablet_get_spell_name_from_path $page)
+ codex_set_current_spell_by_name $spell || return 1
+ local spell_version spell_updated codex_md5 repair_file tablet_file tablet_md5
+ local name key
+ tablet_get_version $page spell_version
+ tablet_get_updated $page spell_updated
+ tablet_get_patchlevel $page spell_patchlevel
+ for repair_file in $(find $SPELL_DIRECTORY -maxdepth 1 -mindepth 1 -type f -name 'REPAIR\^*'); do
+ name=$(basename $repair_file|cut -f3- -d^)
+ key=$(basename $repair_file|cut -f2 -d^)
+ [[ $name ]] || continue
+ [[ $key ]] || continue
+ tablet_check_repair_file spell
+ done
+
+ for repair_file in $(find $SECTION_DIRECTORY -maxdepth 1 -mindepth 1 -type f -name 'REPAIR\^*'); do
+ name=$(basename $repair_file|cut -f3- -d^)
+ key=$(basename $repair_file|cut -f2 -d^)
+ [[ $name ]] || continue
+ [[ $key ]] || continue
+ tablet_check_repair_file section
+ done
+
+ for repair_file in $(find $GRIMOIRE -maxdepth 1 -mindepth 1 -type f -name 'REPAIR\^*'); do
+ name=$(basename $repair_file|cut -f3- -d^)
+ key=$(basename $repair_file|cut -f2 -d^)
+ [[ $name ]] || continue
+ [[ $key ]] || continue
+ tablet_check_repair_file grimoire
+ done
+) }
+
+function tablet_check_repair_file() {
+ local type=$1
+ local replace=0
+
+ case $type in
+ spell) tablet_get_spell_file $page $name tablet_file;;
+ section) tablet_get_section_file $page $name tablet_file;;
+ grimoire) tablet_get_grimoire_file $page $name tablet_file;;
+ *) return 1
+ esac
+
+ if ! [[ $tablet_file ]] ; then
+ if [[ $key == none ]] ; then
+ # WARNING: tb_version specific path derivation here:
+ tablet_file=$page/$type/$name
+ replace=1
+ fi
+ else
+ codex_md5=$(md5sum $repair_file|cut -f1 -d' ')
+ tablet_md5=$(md5sum $tablet_file|cut -f1 -d' ')
+ if [[ $spell_version == $key ]] ||
+ [[ $spell_updated == $key ]] ||
+ [[ $spell_patchlevel == $key ]] ||
+ [[ $tablet_md5 == $key ]] ; then
+ replace=2
+ fi
+ fi
+ if [[ $replace == 1 ]] ||
+ { [[ $replace == 2 ]] && [[ $codex_md5 != $tablet_md5 ]] ; } ; then
+ message "${MESSAGE_COLOR}Tablet Repair: replacing${DEFAULT_COLOR}"
+ message "$tablet_file\nwith repair file \n$repair_file"
+ cp $repair_file $tablet_file
+ else
+ debug libtablet "Tablet Repair: not replacing $tablet_file with $repair_file ($repair, $codex_md5, $tablet_md5)"
+ fi
+}
+
+############################ cleanse functions ########################
+
+#---------------------------------------------------------------------
+##
+#---------------------------------------------------------------------
+function tablet_cleanse_tablet() {
+ local tablet_path=$1
+ local backup_dir=$2
+ local file_backup_dir=$backup_dir/tablet_files
+ local dir rc
+
+ mkdir -p $file_backup_dir
+ for dir in $tablet_path/* ; do
+ if test -d $dir ; then
+ tablet_cleanse_chapter $dir $backup_dir
+ else
+ message "$dir is not a directory! backing it up in $backup_dir"
+ mv $dir $file_backup_dir
+ fi
+ done
+}
+
+#---------------------------------------------------------------------
+##
+#---------------------------------------------------------------------
+function tablet_cleanse_chapter() {
+ local sdir=$1
+ local backup_dir=$2
+ local spell=$(basename $sdir)
+ local page rc
+
+ mkdir -p $backup_dir/duplicates
+ mkdir -p $backup_dir/chapter_files
+ tablet_fix_duplicates $spell $sdir $backup_dir/duplicates
+ test -d $sdir || return 0
+
+ for page in $sdir/*; do
+ if ! test -d $page; then
+ message "$dir is not a directory! backing it up in $backup_dir"
+ mv $page $backup_dir/chapter_files/$page.$spell
+ else
+ tablet_import_repair_files_page $page
+ fi
+ done
+}
+
+
+#---------------------------------------------------------------------
+## @param spell
+## @param path to a tablet chapter
+## @param backup dir
+#---------------------------------------------------------------------
+function tablet_fix_duplicates() {
+ local spell=$1
+ local tbc_dir=$2
+ local backup_dir=$3
+ local tablets rc dir i
+
+ let i=0
+ for dir in $tbc_dir/*; do
+ if test -d $dir; then
+ tablets[$i]=$dir
+ let i+=1
+ fi
+ done
+
+ if test ${#tablets[*]} -eq 0 ||
+ test -z "${tablets[0]}"; then
+ message "Empty tablet chapter: $tbc_dir, removing it"
+ rmdir $tbc_dir
+ return
+ fi
+
+ for ((i=0;$i<${#tablets[*]}; i++ )) ; do
+ message "Inspecting ${tablets[$i]}"
+ tablet_is_spell_version_installed $spell ${tablets[$i]} &&
+ tablet_does_tablet_point_to_itself ${tablets[$i]} ||
+ tablet_backup_page ${tablets[$i]} $backup_dir
+ done
+
+ if ! [[ $(ls $tbc_dir) ]] ; then
+ message "Empty tablet chapter: $tbc_dir, removing it"
+ rmdir $tbc_dir
+ fi
+}
+
+function tablet_backup_page() {
+ local tb_dir=$1
+ local backup_dir=$2
+ local spell_name=$(tablet_get_spell_name_from_path $tb_dir)
+ mkdir -p $backup_dir/$spell_name
+ mv $tb_dir $backup_dir/$spell_name
+}
+
+#---------------------------------------------------------------------
+## check if the tablet represents a spell version thats installed
+#---------------------------------------------------------------------
+function tablet_is_spell_version_installed() {
+ local spell=$1
+ local tb_dir=$2
+ local quiet=$3
+ local tb_version
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 253
+ local version_in_tablet
+ tablet_get_version $tb_dir version_in_tablet
+ [[ $? != 0 ]] && {
+ [[ $quiet ]] || message "Unable to get version for $tb_dir"
+ return 254
+ }
+ local version_installed=$(installed_version $spell)
+ if [[ "$version_in_tablet" != "$version_installed" ]] ; then
+ if ! [[ $quiet ]] ; then
+ message "Tablet at $tb_dir represents\na version that is not installed" \
+ "on the system!"
+ message "tablet version: \"$version_in_tablet\""
+ message "installed version: \"$version_installed\""
+ fi
+ return 1
+ fi
+ return 0
+}
+
+#---------------------------------------------------------------------
+## check if a tablets install log includes itself
+#---------------------------------------------------------------------
+function tablet_does_tablet_point_to_itself() {
+ local rc
+ local tb_dir=$1
+ local quiet=$2
+ local tb_version
+ tablet_get_tb_version $tb_dir tb_version
+ [[ $? != 0 ]] && return 253
+
+ local tb_inst_log
+ tablet_get_log_file $tb_dir install tb_inst_log
+ [[ $? != 0 ]] && {
+ message "Unable to get install log for $tb_dir!"
+ return 254
+ }
+ tablet_load_roots $tb_dir
+ log_adjuster $tb_inst_log /dev/stdout log root 2>/dev/null| grep -q "^$tb_dir"
+ rc=$?
+ tablet_unload_roots $tb_dir
+ if [[ $rc != 0 ]] ; then
+ if ! [[ $quiet ]] ; then
+ message "Tablet at $tb_dir points to an install log that does not point" \
+ "back at $tb_dir!"
+ fi
+ fi
+ return $rc
+}
+
+#----------------------------------------------------------------------
+## @param none
+## @stdout none
+## @globals $TABLET_PATH
+## Intended to be used by cleanse, this routine finds identical files in
+## $TABLET_PATH and hardlinks identical files together in order to save
+## space. This is done on the assumption that 1) tablet files are treated
+## as read only, and 2) anyone using the tablet for write operations (they
+## shouldnt be) will break the hardlinks
+#----------------------------------------------------------------------
+function tablet_coalesce_files() {
+ local dir=${1:-$TABLET_PATH}
+ local car cdr i each
+ let i=0
+
+ local link_log=${2:-$TMP_DIR}/link_log
+
+ local total_files=$(find $dir -type f 2>/dev/null|wc -l)
+ message "Computing md5sum of $total_files tablet files"
+ function tablet_coalesce_files_sub() {
+ hash_append tablet_coalesce $(md5sum $1 2>/dev/null)
+ progress_bar $i $total_files
+ let i+=1
+ }
+ iterate tablet_coalesce_files_sub $'\n' "$(find $dir -type f)"
+ message ""
+
+ # hash_get_table dumps out the table values one per line
+ # they are read into the stuff array and then if theres more than one
+ # thing we force hardlink them together
+ message "Coalescing files.."
+ hash_get_table tablet_coalesce| while read -a cdr; do
+ car=${cdr[0]}
+ for (( i=1 ; i<${#cdr[*]} ; i++ )) ; do
+ echo "Linking $car to ${cdr[$i]}" > $link_log
+ ln -f $car ${cdr[$i]}
+ done
+ done
+ hash_unset tablet_coalesce
+}
+
+###################### Version 0 repair functions ####################
+
+#---------------------------------------------------------------------
+## @param tablet dir
+## Fix the various known defects with an un-versioned tablet and stuff
+## a version of 1 in it.
+#---------------------------------------------------------------------
+function tablet_0_repair() {
+ [[ $1 ]] || return 1
+ # once all these are done, we should have a version 1 tablet
+ tablet_0_repair_spell $1 &&
+ tablet_0_repair_version $1 &&
+ tablet_0_repair_updated $1 &&
+ echo 1 > $1/tb_version || {
+ message "Error repairing tablet at $1"
+ return 1
+ }
+ return 0
+}
+
+#---------------------------------------------------------------------
+## @param tablet dir
+##
+## tablet may have either
+## $tb_dir/spell/DETAILS or
+## $tb_dir/spell/<spellname>/DETAILS
+## the first is correct, the second is not, correct the problem if it exists
+#---------------------------------------------------------------------
+function tablet_0_repair_spell() {
+ local tb_dir=$1
+ [[ $tb_dir ]] || return 1
+ local spell_name=$(tablet_get_spell_name_from_path $tb_dir)
+ if test -d $tb_dir/spell/; then
+ if test -f $tb_dir/spell/DETAILS; then
+ return 0
+ elif test -d $tb_dir/spell/$spell_name; then
+ message "Repairing defected tablet spell dir in $tb_dir"
+ mv $tb_dir/spell/$spell_name/* $tb_dir/spell/ &&
+ rmdir $tb_dir/spell/$spell_name &&
+ return 0
+ else
+ message "Corrupt spell dir in tablet, this shouldnt happen."
+ message "It'd be good to recast the spell now."
+ return 1
+ fi
+ else
+ message "Missing spell dir in tablet, this shouldnt happen."
+ message "It'd be good to recast the spell now."
+ return 1
+ fi
+}
+
+#---------------------------------------------------------------------
+## @param tablet dir
+##
+## Repair the "updated" file as it wasnt created for all pre-versioned
+## tablets. Source the spell, echo $VERSION into a file named version
+#---------------------------------------------------------------------
+function tablet_0_repair_version() {
+ local tb_dir=$1
+ [[ $tb_dir ]] || return 1
+ test -f $tb_dir/version ||
+ (
+ message "Repairing missing spell version file in $tb_dir"
+ # note this assumes tablet_0_repair_spell has run
+ source $tb_dir/spell/DETAILS &>/dev/null
+ echo $VERSION > $tb_dir/version
+ )
+}
+
+#---------------------------------------------------------------------
+## @param tablet dir
+## Repair the "updated" file as it wasnt created for all pre-versioned
+## tablets. Source the spell, echo $UPDATED into a file named updated
+#---------------------------------------------------------------------
+function tablet_0_repair_updated() {
+ local tb_dir=$1
+ [[ $tb_dir ]] || return 1
+ test -f $tb_dir/updated ||
+ (
+ message "Repairing missing spell updated file in $tb_dir"
+ # note this assumes tablet_0_repair_spell has run
+ source $tb_dir/spell/DETAILS &>/dev/null
+ echo $UPDATED > $tb_dir/updated
+ )
+}
+
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libtrack b/var/lib/sorcery/modules/libtrack
new file mode 100644
index 0000000..f794a22
--- /dev/null
+++ b/var/lib/sorcery/modules/libtrack
@@ -0,0 +1,396 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+## @Synopsis Functions for dealing with tracking of files, and other installwatch related things.
+## @Copyright Copyright (C) 2004 The Source Mage Team <http://www.sourcemage.org>
+## Functions for dealing with tracking of files, and other installwatch related things.
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## @Stdin list of files
+## @Stdout list of files
+## Reads a list of files from standard in, and returns a list of the
+## files that exist.
+##
+#---------------------------------------------------------------------
+function exists() {
+ while read ITEM; do [ -e "$ITEM" ] && echo "$ITEM"; done;
+}
+
+
+#---------------------------------------------------------------------
+##
+## Given a list of files it will notify installwatch of them.
+## Useful for spells whose components are not dynamically linked
+## to glibc. Uses simple hack of touching files while
+## installwatch is running.
+##
+#---------------------------------------------------------------------
+function real_track_manual() {
+ if [[ -z "$INSTALLWATCHFILE" ]]; then
+ echo "Can't tell installwatch to manually track... installwatch isn't running."
+ return 1
+ fi
+ local i
+ for i in $* ; do
+ [ -e $i ] && touch $i
+ done
+ return 0
+}
+
+
+#---------------------------------------------------------------------
+##
+## Sets up installwatch.
+##
+#---------------------------------------------------------------------
+function real_invoke_installwatch() {
+ if [ -e /usr/lib/installwatch.so ]; then
+ export INSTALLWATCHFILE=$IW_LOG
+ export LD_PRELOAD=/usr/lib/installwatch.so
+ fi
+}
+
+
+#---------------------------------------------------------------------
+##
+## Stops using installwatch
+##
+#---------------------------------------------------------------------
+function real_devoke_installwatch() {
+ unset LD_PRELOAD
+ unset INSTALLWATCHFILE
+}
+
+
+#---------------------------------------------------------------------
+##
+## Parses the installwatch log for files installed by a spell.
+##
+#---------------------------------------------------------------------
+function parse_iw() {
+ local INPUT=$1
+
+ # it is EXTREMELY IMPORTANT that this variable contains an actual
+ # tab character and not some number of spaces. Otherwise BAD THINGS
+ # will happen.
+ local TAB=" "
+ OMIT_IN="${TAB}rename\|${TAB}symlink\|${TAB}unlink"
+
+ grep -v "$OMIT_IN" $INPUT | cut -f3 | grep "^/"
+ cat $INPUT | cut -f4 | grep "^/"
+}
+
+
+#---------------------------------------------------------------------
+##
+## Creates the install log containing all files installed by the spell.
+##
+#---------------------------------------------------------------------
+function create_install_log() {
+ debug "libtrack" "$FUNCNAME on $SPELL"
+ local INPUT=$1
+ local OUTPUT=$2
+
+ rm -f $OUTPUT
+ parse_iw $INPUT |
+ sort |
+ uniq |
+ install_log_filter $INSTALL_ROOT "" |
+ grep -v -x "" |
+ filter_excluded |
+ install_log_filter "" $INSTALL_ROOT |
+ exists > $OUTPUT
+
+ echo "$C_LOG_COMP" >> $OUTPUT
+ echo "$MD5_LOG" >> $OUTPUT
+ echo "$INST_LOG" >> $OUTPUT
+
+}
+
+#---------------------------------------------------------------------
+## Makes a list of files with the md5sum
+#---------------------------------------------------------------------
+function create_md5list() {
+ local INPUT=$1
+ local OUTPUT=$2
+ debug "libtrack" "$FUNCNAME on $SPELL"
+
+ [[ $__MODIFIED_FILES ]] || export __MODIFIED_FILES="$TMP_DIR/modified_files"
+ touch $__MODIFIED_FILES
+ filter "$__MODIFIED_FILES" < $INPUT | while read LINE ; do
+ debug "libtrack" "Checking file $LINE"
+ if [ -f "$LINE" ] ; then
+ debug "libtrack" "Running md5 on $LINE"
+ md5sum "$LINE"
+ fi
+ done 2>/dev/null > $OUTPUT
+}
+
+#---------------------------------------------------------------------
+## Notes that a file was previously modified so that its md5 is
+## deliberatly munged
+#---------------------------------------------------------------------
+function mark_file_modified() {
+ [[ "$1" ]] || return 1
+ [[ $__MODIFIED_FILES ]] || export __MODIFIED_FILES="$TMP_DIR/modified_files"
+ echo "^$1\$" >> $__MODIFIED_FILES
+}
+
+
+#---------------------------------------------------------------------
+## @param file to check
+## @param md5 file (optional)
+#---------------------------------------------------------------------
+function check_if_modified() {
+ local to=$1
+ local md5_log=$2
+ if ! [[ $2 ]] ; then
+ md5_log="$TMP_DIR/$SPELL.md5"
+ if [[ $OLD_SPELL_VERSION ]] ; then
+ old_md5_log="$MD5SUM_LOGS/$SPELL-$OLD_SPELL_VERSION"
+ # log must be in filterable form
+ log_adjuster "$old_md5_log" "$md5_log" filterable root
+ else
+ old_md5_log=/dev/null
+ fi
+ fi
+ local my_md5=$(md5sum "$to")
+ if test -f "$md5_log" && grep -q "$my_md5" "$md5_log"; then
+ false
+ else
+ true
+ fi
+}
+
+#---------------------------------------------------------------------
+##
+## Creates a bzip/gzip'ed tar file containing an archived backup of
+## file specified on standard input into $CACHE_COMP
+## @stdin files, one per line, to put into the archive
+## @param directory input files are relative to install root for regular
+## files state root for state files
+##
+#---------------------------------------------------------------------
+function create_cache_archive() {
+
+ debug "libtrack" "$FUNCNAME on $SPELL"
+ if [ "$ARCHIVE" == "off" ]; then
+ debug "libtrack" "$FUNCNAME - ARCHIVE=$ARCHIVE, aborting archival."
+ return
+ fi
+ debug "libtrack" "$FUNCNAME - ARCHIVE=$ARCHIVE, archiving."
+ local input=$1
+ local CACHE=$2
+ local CACHE_COMP=$3
+
+ message "${MESSAGE_COLOR}Creating cache file" \
+ "${FILE_COLOR}${CACHE_COMP}${DEFAULT_COLOR}"
+
+ local TMP_DATA=$TMP_DIR/foo.data
+ local TMP_MDATA=$TMP_DIR/foo.mdata
+ seperate_state_files $input $TMP_DATA $TMP_MDATA
+
+ pushd $STATE_ROOT/ &>/dev/null
+ install_log_filter $STATE_ROOT "." < $TMP_MDATA |
+ tar --no-recursion -cPf "$CACHE" -T -
+ popd &>/dev/null
+
+ pushd $INSTALL_ROOT/ &>/dev/null
+ install_log_filter $INSTALL_ROOT "." < $TMP_DATA |
+ tar --no-recursion -rPf "$CACHE" -T -
+ rm $TMP_DATA $TMP_MDATA
+ popd &>/dev/null
+ if [ "$COMPRESSBIN" != "tar" ]; then
+ $COMPRESSBIN -c $CACHE > $CACHE_COMP
+ rm $CACHE
+ fi
+}
+
+#---------------------------------------------------------------------
+# this is to filter the install log from one form to another
+# for install_root/track_root conversions
+#---------------------------------------------------------------------
+function install_log_filter() {
+ sed "s:^$1:$2:"
+}
+function md5_log_filter() {
+ sed "s: $1: $2:"
+}
+
+#---------------------------------------------------------------------
+# @param input file (can be /dev/stdin)
+# @param output file (can be /dev/stdout)
+# @param input format (root/log/filterable)
+# @param output format (root/log/filterable)
+# @param filter callback (optional install_log_filter, could be
+# md5_log_filter)
+#
+# This filters an install log from a given format into another format
+#
+# root: relative to / all paths are relative to / file existence tests
+# should work, INSTALL_ROOT and STATE_ROOT are prepended to data and
+# state files respectively
+#
+# log: relative to track_root etc, format used in the logs (see note on
+# special behavior below)
+#
+# filterable: track_root/install_root/state_root stripped out files can
+# have filters applied to them
+#
+# "Special" handling applies depending on whether STATE_ROOT is inside
+# or outside INSTALL_ROOT.
+# For converting into log format:
+# If STATE_ROOT is within INSTALL_ROOT
+# eg: STATE_ROOT=/opt/stuff INSTALL_ROOT=/opt/stuff
+# or STATE_ROOT=/opt/stuff/state INSTALL_ROOT=/opt/stuff
+# the portion of INSTALL_ROOT within STATE_ROOT is replaced with TRACK_ROOT
+# if STATE_ROOT is outside of INSTALL_ROOT (eg /opt/stuff and /opt/state)
+# then STATE_ROOT is left as is
+#
+# Converting from log to root format is the inverse, and of course going
+# to filterable format just requires removing whatever the expected prefix is.
+#
+#
+#
+#---------------------------------------------------------------------
+function log_adjuster() {
+ local input=$1
+ local output=$2
+ local informat=$3
+ local outformat=$4
+ local callback=${5:-install_log_filter}
+
+ local data_in data_out metadata_in metadata_out cat_metadata
+
+
+ if [[ "$informat" == root ]] ; then
+ data_in=$INSTALL_ROOT
+ if [[ "$outformat" == log ]] ; then
+ # root to log
+ data_out=$TRACK_ROOT
+
+ # if the STATE_ROOT is within the install root, then the state files are
+ # adjusted relative to track_root, otherwise they are left as is
+ if ! [[ ${STATE_ROOT##$INSTALL_ROOT*} ]] ; then
+ metadata_in=$STATE_ROOT
+ metadata_out=$TRACK_ROOT${STATE_ROOT##$INSTALL_ROOT}
+ else
+ cat_metadata=yes
+ fi
+ elif [[ $outformat == filterable ]] ; then
+ # root to filterable
+ data_out=""
+ metadata_in=$STATE_ROOT
+ metadata_out=""
+ fi
+ elif [[ "$informat" == log ]] ; then
+ data_in=$TRACK_ROOT
+ if [[ "$outformat" == root ]] ; then
+ # log to root
+ data_out=$INSTALL_ROOT
+ if ! [[ ${STATE_ROOT##$INSTALL_ROOT*} ]] ; then
+ # we actually could do this another way by stripping off
+ # $TRACK_ROOT${STATE_ROOT##$INSTALL_ROOT}, and replacing
+ # it with $STATE_ROOT, but i think below is simpler and equivalent
+ metadata_in=$TRACK_ROOT
+ metadata_out=$INSTALL_ROOT
+ else
+ cat_metadata=yes
+ fi
+ elif [[ "$outformat" == filterable ]] ; then
+ # log to filterable
+ data_out=""
+ metadata_out=""
+ if ! [[ ${STATE_ROOT##$INSTALL_ROOT*} ]] ; then
+ metadata_in=$TRACK_ROOT${STATE_ROOT##$INSTALL_ROOT}
+ else
+ metadata_in=$STATE_ROOT
+ fi
+ fi
+ elif [[ "$informat" == filterable ]] ; then
+ data_in=""
+ metadata_in=""
+ if [[ "$outformat" == root ]] ; then
+ # filterable to root
+ data_out=$INSTALL_ROOT
+ metadata_out=$STATE_ROOT
+ elif [[ "$outformat" == log ]] ; then
+ # filterable to log
+ data_out=$TRACK_ROOT
+ if ! [[ ${STATE_ROOT##$INSTALL_ROOT*} ]] ; then
+ metadata_out=$TRACK_ROOT${STATE_ROOT##$INSTALL_ROOT}
+ else
+ metadata_out=$STATE_ROOT
+ fi
+ fi
+ fi
+
+ local TMP_SSF=$TMP_DIR/$RANDOM
+ while ! mkdir $TMP_SSF; do
+ TMP_SSF=$TMP_DIR/$RANDOM
+ sleep 1
+ done
+ local TMP_DATA=$TMP_SSF/foo.data
+ local TMP_MDATA=$TMP_SSF/foo.mdata
+
+ seperate_state_files $input $TMP_DATA $TMP_MDATA
+ {
+ if [[ $cat_metadata ]] ; then
+ cat $TMP_MDATA
+ else
+ eval "$callback \"$metadata_in\" \"$metadata_out\" < $TMP_MDATA"
+ fi
+ eval "$callback \"$data_in\" \"$data_out\" < $TMP_DATA"
+ } > $output
+ rm $TMP_DATA $TMP_MDATA
+ rmdir $TMP_SSF
+}
+
+#---------------------------------------------------------------------
+## Split a log file into data that should be TRACK_ROOT'd versus
+## STATE_ROOT'd.
+##
+## @param filename or - (or /dev/stdin) for a pipe. This routine will
+## read the input only once in-order to work with pipes.
+## @param filename for non-state files, possibly /dev/stdout or /dev/stderr
+## @param filename for state files, possibly /dev/stdout or /dev/stderr
+## don't use the same stream for both types.
+##
+#---------------------------------------------------------------------
+function seperate_state_files() {
+ local REAL_LOG_DIR=${LOG_DIRECTORY#$STATE_ROOT}
+ local REAL_STATE_DIR=${STATE_DIRECTORY#$STATE_ROOT}
+
+ # the input file is almost certainly a pipe, and things get weird
+ # since we have to grep twice, so just dump the data into a unique
+ # file, the loop below shouldn't go forever unless there's ~30,000 pipes
+
+ local FILE=$TMP_DIR/ssf.$RANDOM
+ while test -e $FILE; do
+ FILE=$TMP_DIR/ssf.$RANDOM
+ done
+ cat $1 > $FILE
+ grep -v "$REAL_LOG_DIR\|$REAL_STATE_DIR" $FILE | grep -xv '' > $2
+ grep "$REAL_LOG_DIR\|$REAL_STATE_DIR" $FILE | grep -xv '' > $3
+ rm $FILE
+ return 0
+}
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/libtriggers b/var/lib/sorcery/modules/libtriggers
new file mode 100644
index 0000000..1029e23
--- /dev/null
+++ b/var/lib/sorcery/modules/libtriggers
@@ -0,0 +1,210 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## @Synopsis Set of functions used by the internal sorcery scripts
+##
+## Functions used to manage triggers. Used both by spells
+## and by the sorcery scripts.
+##
+## @Copyright Original version Copyright 2002 by the Source Mage Team
+##
+#---------------------------------------------------------------------
+
+
+#---------------------------------------------------------------------
+## Checks for a TRIGGERS file in SCRIPT_DIRECTORY, and if it is
+## executable, runs it.
+#---------------------------------------------------------------------
+function run_triggers() {
+ debug "libtriggers" "Starting run_triggers() on $SPELL"
+
+ if [ -x $SCRIPT_DIRECTORY/TRIGGERS ]; then
+ persistent_load
+ . $SCRIPT_DIRECTORY/TRIGGERS
+ persistent_save
+ fi
+
+}
+
+#---------------------------------------------------------------------
+## @param spell
+##
+## Remove's all of a spell's triggers from the list of registered
+## triggers.
+##
+#---------------------------------------------------------------------
+function remove_triggers ()
+{
+ debug "libtriggers" "remove_triggers - $*"
+
+ [ -f $TRIGGER_LIST ] || return 0
+ tTRIGGER_LIST=`lock_start_transaction $TRIGGER_LIST`
+ [ -f $tTRIGGER_LIST ] || return 0
+ grep -v "^$SPELL:" $TRIGGER_LIST > $tTRIGGER_LIST
+ lock_commit_transaction $TRIGGER_LIST
+ return $?
+}
+
+#---------------------------------------------------------------------
+## @param event
+## @param [spell]
+## @Globals SPELL if \$2 is omitted
+##
+## @Stdout Query and warnings
+## @Stdin y/n
+## Triggers an event and performs necessary actions. Argument 2 is
+## optional. If omitted, the value of SPELL will be used.
+##
+#---------------------------------------------------------------------
+function trigger ()
+{
+ debug "libtriggers" "trigger - $*"
+
+ local spell TRIGGER action
+ action=$1
+ spell=`esc_str ${SPELL:-$4}`
+
+ [ -f $TRIGGER_LIST ] || return 0
+ iterate do_trigger $'\n' "`grep "^[^:]*:$spell:on_$action" $TRIGGER_LIST`"
+}
+
+#---------------------------------------------------------------------
+## Get the triggerees of a given spell event and action
+#---------------------------------------------------------------------
+function get_triggerees() {
+ local spell=$1
+ local event=$2
+ local each
+ # get all the cast/check_self triggers
+ [ -f $TRIGGER_LIST ] || return 0
+ for each in $3 ; do
+ grep "[^:]*:$spell:$event:$each" $TRIGGER_LIST|cut -f1 -d:
+ done|sort|uniq|grep -v '^$'
+}
+
+#---------------------------------------------------------------------
+## Get the run_script triggers from $spell on $target when theres a
+## $action
+#---------------------------------------------------------------------
+function get_run_script_triggers() {
+ local spell=$1
+ local event=$2
+ local target=$3
+ grep "$target:$spell:$event:run_script" $TRIGGER_LIST|cut -f4 -d:
+}
+
+#---------------------------------------------------------------------
+## @param event
+## @param causing-spell
+## @param action
+## @param subject-spell
+## Registers a trigger in the list of triggers. Also verifies that
+## the trigger and action exist.
+##
+#---------------------------------------------------------------------
+function set_trigger ()
+{ #1==trigger to set, $2==spell that triggers it, $3=action, $4==(optional)spell this is for
+
+ debug "libtriggers" "set_trigger - $*"
+
+ local str spell
+ [[ $4 ]] || [[ $SPELL ]] || return 1
+ case $3 in
+ cast_self|dispel_self|check_self|run_script*)
+ ;;
+ *)
+ message "${PROBLEM_COLOR}$3 is not a valid trigger action.${DEFAULT_COLOR}"
+ return 1
+ ;;
+ esac
+ case $1 in
+ on_cast|on_pre_cast|on_dispel|on_pre_dispel)
+ ;;
+ *)
+ message "${PROBLEM_COLOR}$1 is not a valid trigger.${DEFAULT_COLOR}"
+ return 1
+ ;;
+ esac
+ #perhaps a check to make sure that $2 exists?
+
+ spell=${SPELL:-$4}
+ str="$spell:$2:$1:$3"
+
+ lock_file $TRIGGER_LIST
+ if ! test -f $TRIGGER_LIST || ! grep -q "$str" $TRIGGER_LIST ; then
+ echo "$str" >> $TRIGGER_LIST
+ fi
+ unlock_file $TRIGGER_LIST
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell that triggers
+## @param action
+##
+## Used by spells to make adding triggerse nice.
+##
+#---------------------------------------------------------------------
+function real_on_cast () {
+ set_trigger "on_cast" "$1" "$2"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell that triggers
+## @param action
+##
+## Used by spells to make adding triggers nice.
+##
+#---------------------------------------------------------------------
+function real_on_dispel () {
+ set_trigger "on_dispel" "$1" "$2"
+}
+
+#---------------------------------------------------------------------
+## @Type API
+## @param spell that triggers
+## @param action
+##
+## Used by spells to make adding triggerse nice.
+##
+#---------------------------------------------------------------------
+function real_on_pre_dispel () {
+ set_trigger "on_pre_dispel" "$1" "$2"
+}
+
+
+function do_trigger () {
+ TRIGGER=()
+ explode "$1" ":" TRIGGER
+ local DC="$DEFAULT_COLOR"
+ message "${SPELL_COLOR}${TRIGGER[1]}${DC}${QUERY_COLOR}" \
+ "being $action has triggered a" \
+ "\"${DC}${FILE_COLOR}${TRIGGER[3]}${DC}${QUERY_COLOR}\"" \
+ "on spell ${DC}${SPELL_COLOR}${TRIGGER[0]}${DC}."
+ query "Proceed? " "y"
+ [[ $? != 0 ]] && return
+
+ (
+ unset CAST_PASS D_LOG
+ function run_script() { eval $@ ; }
+ #Ok, now do the required action:
+ case ${TRIGGER[3]} in
+ cast_self)
+ cast -c "${TRIGGER[0]}"
+ ;;
+ dispel_self)
+ dispel "${TRIGGER[0]}"
+ ;;
+ check_self)
+ cleanse --nofix_quick "${TRIGGER[0]}" ||
+ cast -c "${TRIGGER[0]}"
+ ;;
+ run_script*)
+ eval "${TRIGGER[3]}"
+ ;;
+ *) message "${PROBLEM_COLOR}${TRIGGER[3]} is not" \
+ "a known kind of trigger.${DEFAULT_COLOR}"
+ esac
+ )
+}
diff --git a/var/lib/sorcery/modules/libunpack b/var/lib/sorcery/modules/libunpack
new file mode 100644
index 0000000..8742e3f
--- /dev/null
+++ b/var/lib/sorcery/modules/libunpack
@@ -0,0 +1,820 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## @Libgrimoire
+##
+## @Synopsis Set of functions containing the spell writing API.
+##
+##
+## These functions can be used in the PRE_BUILD, BUILD, POST_BUILD
+## and POST_INSTALL sections of spells.
+##
+## @Copyright
+## Original version Copyright 2001 by Kyle Sallee
+## Additions/Corrections Copyright 2002 by the Source Mage Team
+## New World libunpack Additions/Corrections by Seth Woolley (2005)
+##
+#---------------------------------------------------------------------
+
+#===================== libunpack common ==============================
+
+#---------------------------------------------------------------------
+## @Type API
+## @param SOURCE suffix
+##
+## unpack_file takes the SOURCE suffix and figures out if it is supposed
+## to hash or gpg check it -- then it does its dirty work and runs unpack_hash
+## or unpack_gpg depending upon the circumstances. That's the only argument it
+## takes and needs: '' '2' '3', etc. It is run in default_pre_build for the
+## null argument only. Custom unpacking still requires a custom PRE_BUILD.
+##
+## valid formats: vendor-provided gpg, guru-provided gpg, any
+## hash-algorithm provided by gpg (currently md5, sha1, sha256, sha384,
+## sha512, ripemd160
+##
+## SOURCE=blah
+## SOURCE2=blah.asc
+## SOURCE_URL=http://blah.com/$SOURCE
+## SOURCE2_URL=http://blah.com/$SOURCE2
+## SOURCE_GPG=blah.gpg:$SOURCE2:UPSTREAM_KEY
+## SOURCE2_IGNORE=signature # for auditing purposes
+##
+## SOURCE=blah
+## SOURCE_URL=http://blah.com/$SOURCE
+## SOURCE_GPG=swoolley.gpg:$SOURCE.asc:WORKS_FOR_ME
+##
+## SOURCE=blah
+## SOURCE_URL=http://blah.com/$SOURCE
+## MD5[0]=d41d8cd98f00b204e9800998ecf8427e
+##
+## SOURCE=blah
+## SOURCE_URL=http://blah.com/$SOURCE
+## SOURCE_HASH=md5:d41d8cd98f00b204e9800998ecf8427e:WORKS_FOR_ME
+##
+## In GPG mode:
+## Validates the verification level (the third parameter) and the
+## hash algorithm against user defined lists.
+## It finds the public key and signature using locate_spell_file,
+## Then it validates it at the beginning.
+## see unpack_gpg()
+##
+## In HASH mode:
+## Validates the verification level (the third parameter) and the
+## hash algorithm against user defined lists.
+## It uses gpg to calculate the hash value except for md5 and sha1, which
+## coreutils provides.
+## see unpack_hash()
+##
+## In IGNORE mode:
+## It checks for the following text:
+## volatile (for cvs/svn/any-other-scm)
+## unversioned (the source file changes frequently, but not a direct scm)
+## signature (for gnupg signatures)
+## as reasons for ignoring the source code validation. Signatures
+## are silently ignored. Everything else respects MD5SUM_DL.
+## see unpack_ignore
+##
+## Otherwise, it falls back to MISSING mode, see unpack_missing
+## (or for now)
+## Otherwise, it falls back to old uncompressed md5sum check with MD5[n].
+## see real_unpack()
+##
+## The default verification level is "WORKS_FOR_ME"
+##
+## Verification levels are, these indicate how much effort was put into
+## validating the integrity of the source from the upstream vendor.
+## WORKS_FOR_ME No verification was done.
+## UPSTREAM_HASH Checked the upstream hash file
+## UPSTREAM_KEY Checked upstream (gpg) key, signature matched, but the
+## key was not validated
+## ESTABLISHED_UPSTREAM_KEY Upstream key was not validated against
+## multiple independent sources, but has been
+## in use for several years
+## VERIFIED_UPSTREAM_KEY Upstream key id was verified against multiple
+## independent sources.
+## ID_CHECK_UPSTREAM_KEY Key was verified in person with a photo id check.
+##
+## Also if you want to include more than one signature, hash, etc, just put
+## a 2, 3, 4, etc on the end of the variable like so:
+## SOURCE2_HASH2=...
+##
+## For cascading, currently it will still ask abort questions: a no abort
+## will make it fail over all cascades; a yes abort will have it skip to
+## the next cascdes. Missing binaries or other failures like that (error 200
+## below) will silently fail over to the next check. The cascade order is:
+## GPG, HASH, IGNORE, MISSING
+##
+## The cascade setup allows you to place a higher bit checksum earlier
+## in the cascade and even if the binary doesn't work it will just print
+## out an abort query which can be said no to and it will continue to
+## fail over to the lower bit checksum that should be available in
+## coreutils (like sha1/md5). That's if you're not using gpg, which is
+## preferred. If multiple hashes are included of different ciphers, the
+## user can abort on either that go bad, so it can be considered a
+## security increase to have more than one, but only if the harder cipher
+## is first in the cascade order, as the first successful hash match will
+## go ahead and prompt an untarball. I may change it later, but for now I
+## think first successful match skipping the rest is least intrusive, and
+## I'd need to add an interface element to let the user choose to run all
+## checks on a single source.
+##
+#---------------------------------------------------------------------
+function real_unpack_file() {
+ debug "libgrimoire" "real_unpack_file - $*"
+
+ local GPGNUM="$1"
+ local SVAR="SOURCE${GPGNUM}"
+
+ local crypto_func
+ for crypto_func in GPG HASH IGNORE; do
+ debug "libgrimoire" "checking $crypto_func verification"
+ local AVAR="SOURCE${GPGNUM}_${crypto_func}"
+ local AVARN="$AVAR"
+ local iter=0
+ local rc=""
+ # NOTE ## date: 2005-08-25
+ # This while loop runs through the SOURCEn_{GPG|HASH|IGNORE}m not the
+ # SOURCEn_*[m] set of vars this is kinda confusing we might rip it out
+ while [ -n "${!AVARN}" ]; do
+ local lcase_crypto_func="$(echo $crypto_func | tr 'A-Z' 'a-z')"
+ unpack_$lcase_crypto_func "${!SVAR}" "${!AVARN}"
+ rc="$?"
+ case "$rc" in
+ 200) debug "libgrimoire" "falling back from $AVARN"; rc="" ;;
+ 1) return "$rc" ;;
+ 0) uncompress_unpack "${!SVAR}"; return "$?" ;;
+ esac
+ (( iter++ ))
+ AVARN="$AVAR[$iter]"
+ done
+ [ -n "$rc" ] && return "$rc"
+ done
+
+ if false; then # <------ here's the switch to disable oldworld -------
+ debug "libgrimoire" "falling back to missing verification"
+ unpack_missing "${!SVAR}"
+ rc="$?"
+ case "$rc" in
+ 0) uncompress_unpack "${!SVAR}"; return "$?" ;;
+ *) return "$rc" ;;
+ esac
+ else
+ debug "libgrimoire" "falling back to regular MD5[]"
+ local MD5NUM="$([ -z "$GPGNUM" ] && echo 0 || echo "$(($GPGNUM - 1))")"
+ real_unpack "${!SVAR}" "${MD5[$MD5NUM]}"
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## @param filename
+## @param compressor
+## @Stdout uncompressed
+##
+## Just uncompresses the file, but does not expand it. i.e. bunzip
+## it, but don't untar it. It dumps the expanded file to stdout.
+## Note: zip is a special case because it doesn't work with streams.
+##
+#---------------------------------------------------------------------
+function uncompress_core() {
+ debug "libgrimoire" "uncompress_core - $*"
+
+ case "$2" in
+ bzip2) bzip2 -cdf "$1" ;;
+ gzip) gzip -cdf "$1" ;;
+ compress*) gzip -cdf "$1" ;;
+ Zip) cat "$1" ;;
+ RPM) rpmunpack < "$1" | gzip -cd ;;
+ tar) cat "$1" ;;
+ *) cat "$1" ;;
+ esac
+
+}
+
+
+#---------------------------------------------------------------------
+## @param filename
+## @param compressor
+## @Stdout uncompressed
+##
+## unpack_core takes the uncompressed stream and turns it into the
+## fully unarchived form.
+## Note: zip is a special case because it doesn't work with streams.
+##
+#---------------------------------------------------------------------
+function unpack_core() {
+ debug "libgrimoire" "unpack_core - $*"
+
+ case "$2" in
+ bzip2|gzip|compress*|tar)
+ tar --owner=root --group=root -xf /dev/stdin \
+ 2> /dev/null || cat > /dev/null ;;
+ Zip) cat /dev/stdin >/dev/null #get rid of unused output
+ unzip -q "$1" ;;
+ RPM) cpio -idm < /dev/stdin ;;
+ *) cat > /dev/null ;;
+ esac
+
+}
+
+
+#---------------------------------------------------------------------
+## @Type API
+## @param filename
+## @Stdout compressor
+##
+## Guesses what program was used to compress a file
+## Return value is always success due to `file' workings
+##
+#---------------------------------------------------------------------
+function real_guess_compressor() {
+ # NOTE: if the file doesn't exist, `file' still completes successfully
+ # the COMPRESSOR value in this case will be "can't"
+
+ local OUTPUT="$($FILEPROG -b "$1")"
+ local COMPRESSOR="$(echo "$OUTPUT" | cut -d ' ' -f1)"
+ [ "$COMPRESSOR" = "GNU" -o "$COMPRESSOR" = "POSIX" ] &&
+ COMPRESSOR="$(echo "$OUTPUT" | cut -d ' ' -f2)"
+ debug "libgrimoire" "guess_compressor() - guessed $1 compressor <$COMPRESSOR>"
+ echo "$COMPRESSOR"
+}
+
+
+#---------------------------------------------------------------------
+## @Type API
+##
+## alias function (was uncompress_md5)
+##
+## Used to be uncompress_md5(), now it is uncompress_core()
+##
+#---------------------------------------------------------------------
+function real_uncompress() { uncompress_core "$@"; }
+
+
+#---------------------------------------------------------------------
+## @param required spell
+##
+## Returns 200 if the user says not to Abort in the face, otherwise
+##
+#---------------------------------------------------------------------
+function unpack_spell_required() {
+ debug "libgrimoire" "Running unpack_spell_required -- $1"
+
+ if ! spell_ok "$1" ; then
+ query "This spell has an option to check its integrity via spell "\
+"${SPELL_COLOR}${1}${QUERY_COLOR} for $2, you might consider casting it. "\
+"Abort?" n &&
+ return 1 ||
+ return 200
+ else
+ return 0
+ fi
+
+}
+
+
+#===================== libunpack newworld ============================
+
+#--------------------------------------------------------------------
+## @param the verification level
+##
+## returns 0 if the specified verification level is in the user's
+## list of allowed verification levels, or if they allow unknown
+## verification levels, 1 otherwise
+##
+#--------------------------------------------------------------------
+function is_allowed_verf_level() {
+ local rc=0
+ local VRFLEVEL=$1
+ message "${MESSAGE_COLOR}Checking spell level ${VRFLEVEL}${DEFAULT_COLOR}"
+ if list_find "${VRF_ALLOWED_LEVELS}" "${VRFLEVEL}:on"
+ then
+ message "${MESSAGE_COLOR}Spell level is an allowed level${DEFAULT_COLOR}"
+ elif list_find "${VRF_ALLOWED_LEVELS}" "${VRFLEVEL}:off"
+ then
+ message "${PROBLEM_COLOR}Spell level is not an allowed level${DEFAULT_COLOR}"
+ rc=1
+ else
+ if [[ "${VRF_ALLOW_NEW_LEVELS}" == "on" ]]
+ then
+ message "${MESSAGE_COLOR}Spell level is a new allowed level${DEFAULT_COLOR}"
+ else
+ message "${PROBLEM_COLOR}Spell level is not an allowed level${DEFAULT_COLOR}"
+ rc=1
+ fi
+ fi
+ return $rc
+}
+
+#--------------------------------------------------------------------
+## @param hash used
+## @param spells verification level
+##
+## first checks if the hash is in the user specified list in an on state then
+## checks if the hash is there in an off state, if it can't find either then
+## it checks the state of VRF_ALLOW_NEW_HASHES to see if we should succeed or
+## not
+## Returns 0 if the hash is allowed or (VRF_ALLOW_NEW_HASHES is on and the hash
+## is not present in the hash list)
+##
+#--------------------------------------------------------------------
+function is_allowed_hash() {
+ local rc=0
+ local hash=$1
+ local HASHLEVEL=$2
+ message "${MESSAGE_COLOR}Algorithm used: ${hash}${DEFAULT_COLOR}" &&
+ if list_find "$VRF_ALLOWED_HASHES" "${hash}:on"
+ then
+ message "${MESSAGE_COLOR}Algorithm checks out${DEFAULT_COLOR}"
+ if is_allowed_verf_level $HASHLEVEL ; then rc=0 ; else rc=1 ; fi
+ elif list_find "$VRF_ALLOWED_HASHES" "${hash}:off"
+ then
+ message "${PROBLEM_COLOR}Algorithm is not in user selected list${DEFAULT_COLOR}"
+ rc=1
+ elif [[ "$VRF_ALLOW_NEW_HASHES" == "on" ]]
+ then
+ message "${MESSAGE_COLOR}Allowing new hash${hash}${DEFAULT_COLOR}"
+ if is_allowed_verf_level $HASHLEVEL ; then rc=0 ; else rc=1; fi
+ else
+ message "${PROBLEM_COLOR}Disallowing new hash ${hash}${DEFAULT_COLOR}"
+ rc=1
+ fi
+ return $rc
+}
+
+#---------------------------------------------------------------------
+## @param file to unpack
+## @param gpg public key file (.gpg) ":" gpg signature file (.asc)
+##
+## Given a file, unpack checks the gpg signature for that file, and, if
+## appropriate, runs the decompression program for that file, as well as
+## untar'ing the file. Note: zip is a special case because it doesn't
+## work with streams.
+##
+#---------------------------------------------------------------------
+function unpack_gpg() {
+ debug "libgrimoire" "Running unpack_gpg -- $*"
+
+ local FILENAME="$( guess_filename "$SOURCE_CACHE/$1" )"
+ local PFNAME="$( echo "$2" | cut -d: -f1 )"
+ local SFNAME="$( echo "$2" | cut -d: -f2 )"
+ local GPGLEVEL="$( echo "$2" | cut -d: -f3 )"
+ if [[ -z $GPGLEVEL ]]
+ then
+ GPGLEVEL=$DEFAULT_SPELL_VRF_LEVEL
+ elif ! list_find "${VERIFY_SPELL_LEVELS}" "${GPGLEVEL}"
+ then
+ message "${PROBLEM_COLOR}This is probably a spell bug ${GPGLEVEL} is not in ${VERIFY_SPELL_LEVELS}${DEFAULT_COLOR}"
+ return 1
+ fi
+ local GPGALGO_USED=""
+ local message_file=""
+
+ message "${MESSAGE_COLOR}GPG checking source file $1...${DEFAULT_COLOR}"
+
+ unpack_spell_required gnupg || return "$?"
+
+ gpg_verify_signature "$( locate_spell_file "$SFNAME" )" \
+ "$FILENAME" \
+ "$( locate_spell_file "$PFNAME" securely)" GPGALGO_USED
+ rc="$?"
+ case "$rc" in
+ 0)
+ if is_allowed_hash $GPGALGO_USED $GPGLEVEL ; then rc=0 ; else rc=1 ; fi
+ ;;
+ 3) message_file="Signature" ;;
+ 4) message_file="Source" ;;
+ 5) message_file="Keyring" ;;
+ esac
+ if [[ $message_file ]]
+ then
+ message "${PROBLEM_COLOR}AHHH!!! ${message_file} file not found${DEFAULT_COLOR}"
+ fi
+ if [ "$rc" -eq 200 ]; then
+ return 200
+ fi
+
+ gpg_user_query $rc $SPELL spell || return 1
+ return 0
+
+}
+
+
+#---------------------------------------------------------------------
+## @param file to unpack
+## @param algorithm ":" hashsum
+##
+## Given a file, unpack checks the hash for that file, and, if
+## appropriate, runs the decompression program for that file, as well as
+## untar'ing the file. Note: zip is a special case because it doesn't
+## work with streams.
+##
+#---------------------------------------------------------------------
+function unpack_hash() {
+ debug "libgrimoire" "Running unpack_hash() on $1"
+
+ local FILENAME="$( guess_filename "$SOURCE_CACHE/$1" )"
+ local ALGORITHM="$( echo "$2" | cut -d: -f1 )"
+ local HASHSUM="$( echo "$2" | cut -d: -f2 )"
+ local HLEVEL="$( echo "$2" | cut -d: -f3 )"
+ local rc=0
+ if [[ -z "$HLEVEL" ]]
+ then
+ HLEVEL=$DEFAULT_SPELL_VRF_LEVEL
+ fi
+
+ message "${MESSAGE_COLOR}hash checking source file $1...${DEFAULT_COLOR}"
+ local HASH
+ if [ "$MD5SUM_DL" != "off" ]; then
+
+ if [[ "$ALGORITHM" == md5 ]] || [[ "$ALGORITHM" == sha1 ]] ; then
+ unpack_spell_required coreutils "$ALGORITHM" || return "$?"
+ HASH="$(${ALGORITHM}sum "$FILENAME" | cut -d' ' -f1)"
+ else
+ unpack_spell_required gnupg "$ALGORITHM" || return "$?"
+ if list_find "$(gpg_get_hashes)" $ALGORITHM; then
+ HASH="$(gpg_hashsum "${ALGORITHM}" "$FILENAME" | cut -d' ' -f1)"
+ else
+ message "${PROBLEM_COLOR}Algorithm $ALGORITHM is not"\
+ "known!${DEFAULT_COLOR}"
+ return 200
+ fi
+ fi
+ local rc=$?
+
+ if [[ "$HASH" != "$HASHSUM" ]] || [[ $rc != 0 ]]
+ then
+ message "${PROBLEM_COLOR}$ALGORITHM check failed" \
+ "$HASH != $HASHSUM !${DEFAULT_COLOR}" &&
+ hash_user_query 1 "$SPELL" spell || return 1
+ else
+ if is_allowed_hash ${ALGORITHM} ${HLEVEL} ; then rc=0 ; else rc=1 ; fi
+ hash_user_query $rc "$SPELL" spell || return 1
+ fi
+ else
+ message "${PROBLEM_COLOR}Continuing!${DEFAULT_COLOR}"
+ fi
+ return 0
+}
+
+#--------------------------------------------------------------------
+## @param return code from unpack_hash
+## @param spell name
+##
+## Does some basic output to tell the user what failed and how then calls
+## unpack_file_user_query
+## Returns 0 if hash succeeded otherwise returns 1 if unpack_file_user_query
+## fails
+##
+#--------------------------------------------------------------------
+function hash_user_query() {
+ local rc=$1
+ local spell=$2
+ case "$rc" in
+ 0)
+ message "${MESSAGE_COLOR}hash verification succeeded${DEFAULT_COLOR}"
+ ;;
+ *)
+ message "${PROBLEM_COLOR}hash verification failure${DEFAULT_COLOR}"
+ unpack_file_user_query $rc || return 1
+ ;;
+ esac
+ return 0
+}
+
+#--------------------------------------------------------------------
+## @param return code from the unpack_gpg or unpack_hash
+##
+## checks MD5SUM_DL to abort or not
+## Returns what query returns if it's called
+##
+#--------------------------------------------------------------------
+function unpack_file_user_query() {
+ local rc=$1
+ case "$rc" in
+ 0)
+ ;;
+ *)
+ case "$MD5SUM_DL" in
+ ask_ignore) query "Abort?" "n" && return 1 ;;
+ ask_risky|ask_abort) query "Abort?" "y" && return 1 ;;
+ on|abort_all|*) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
+ esac
+ ;;
+ esac
+ return 0
+}
+
+#---------------------------------------------------------------------
+## @param file to unpack
+## @param reason to ignore it, one of: volatile unversioned signature
+##
+## Given a file, unpack checks the ignore rules for that file, and, if
+## appropriate, runs the decompression program for that file, as well as
+## untar'ing the file. Note: zip is a special case because it doesn't
+## work with streams.
+##
+#---------------------------------------------------------------------
+function unpack_ignore() {
+ debug "libgrimoire" "Running unpack_ignore() on $1"
+
+ REASON="$2"
+
+ message "${MESSAGE_COLOR}Not checking ${2} source file $1...${DEFAULT_COLOR}"
+
+ if [ "$MD5SUM_DL" != "off" ]; then
+
+ [ "$REASON" == "signature" ] ||
+ case "$MD5SUM_DL" in
+ask_risky|ask_ignore) query "Abort?" "n" && return 1 || return 0 ;;
+ abort_all) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
+ ask_abort|on|*) query "Abort?" "y" && return 1 || return 0 ;;
+ esac
+
+ else
+ message "${RED}Continuing!${DEFAULT_COLOR}"
+ return 0
+ fi
+
+}
+
+
+#---------------------------------------------------------------------
+## @param file to unpack
+## @param reason to ignore it, one of: volatile unversioned signature
+##
+## Given a file, unpack checks the ignore rules for that file, and, if
+## appropriate, runs the decompression program for that file, as well as
+## untar'ing the file. Note: zip is a special case because it doesn't
+## work with streams.
+##
+#---------------------------------------------------------------------
+function unpack_missing() {
+ debug "libgrimoire" "Running unpack_missing() on $1"
+
+ message "${PROBLEM_COLOR}Missing check for source file $1!${DEFAULT_COLOR}"
+
+ if [ "$MD5SUM_DL" != "off" ]; then
+
+ case "$MD5SUM_DL" in
+ ask_ignore) query "Abort?" "n" && return 1 || return 0 ;;
+ ask_risky|ask_abort) query "Abort?" "y" && return 1 || return 0 ;;
+ on|abort_all|*) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
+ esac
+
+ else
+ message "${RED}Continuing!${DEFAULT_COLOR}"
+ return 0
+ fi
+
+}
+
+
+#---------------------------------------------------------------------
+## @param file to unpack
+##
+## Given a file, runs the decompression program for that file, as well as
+## untar'ing the file.
+##
+#---------------------------------------------------------------------
+function uncompress_unpack() {
+ debug "libgrimoire" "Running uncompress_unpack() on $1"
+
+ FILENAME="$( guess_filename "$SOURCE_CACHE/$1" )" &&
+ COMPRESSOR="$( guess_compressor "$FILENAME" )"
+
+ message "${MESSAGE_COLOR}Unpacking source file ${SPELL_COLOR}${1}" \
+ "${DEFAULT_COLOR}${MESSAGE_COLOR}for spell${SPELL_COLOR}" \
+ "${SPELL}${DEFAULT_COLOR}${MESSAGE_COLOR}.${DEFAULT_COLOR}"
+
+ if [[ ! $FILENAME ]] || ! test -f "$FILENAME" ; then
+ message "${PROBLEM_COLOR}Source file not found.${DEFAULT_COLOR}"
+ return 1
+ fi
+
+ uncompress_core "$FILENAME" "$COMPRESSOR" |
+ unpack_core "$FILENAME" "$COMPRESSOR"
+}
+
+#---------------------------------------------------------------------
+## @param file to unpack
+##
+## Interface to unpack a file without any verification.
+##
+#---------------------------------------------------------------------
+function real_unpack_file_simple() { uncompress_unpack "$@"; }
+
+
+#---------------------------------------------------------------------
+## @param absolute or relative file path
+## @param empty or 'securely', which would skip SOURCE_CACHE
+## @Stdout the real path of the file (sometimes relative to CWD)
+##
+## Given a file, locate_spell_file finds out where it really is within
+## the spell hierarchy down to the grimoire root, and then tries cwd and
+## then the source cache.
+##
+#---------------------------------------------------------------------
+function locate_spell_file() {
+ debug "libgrimoire" "Running locate_spell_file() $2 on $1"
+
+ # checks in any case
+ [ -f "$SPELL_DIRECTORY/$1" ] && echo "$SPELL_DIRECTORY/$1" && return 0
+ [ -f "$SECTION_DIRECTORY/$1" ] && echo "$SECTION_DIRECTORY/$1" && return 0
+ [ -f "$GRIMOIRE/$1" ] && echo "$GRIMOIRE/$1" && return 0
+ [ -f "$1" ] && echo "$1" && return 0
+
+ [ "$2" != "securely" ] && # checks in "secure" mode
+ [ -f "$SOURCE_CACHE/$1" ] && echo "$SOURCE_CACHE/$1" && return 0
+
+ message "${MESSAGE_COLOR}" \
+ "Problem: $1: file not found in spell hierarchy." \
+ "${DEFAULT_COLOR}" > /dev/stderr
+ echo "$1"
+ return 1
+
+}
+
+
+#===================== libunpack oldworld ============================
+
+#---------------------------------------------------------------------
+## @Type API
+## @param file to unpack
+## @param md5sum
+##
+## Given a file, unpack runs the decompression program for that file,
+## as well as untar'ing the file if appropriate and if the MD5
+## matches.
+## Note: zip is a special case because it doesn't work with streams.
+##
+#---------------------------------------------------------------------
+function real_unpack() {
+ debug "libgrimoire" "Running unpack -- $*"
+
+ message "${MESSAGE_COLOR}Unpacking source file ${SPELL_COLOR}${1}" \
+ "${DEFAULT_COLOR}${MESSAGE_COLOR}for spell${SPELL_COLOR}" \
+ "${SPELL}${DEFAULT_COLOR}${MESSAGE_COLOR}.${DEFAULT_COLOR}"
+
+ FILENAME="$(guess_filename "$SOURCE_CACHE/$1")" &&
+ COMPRESSOR="$(guess_compressor "$FILENAME")"
+
+ if [[ ! $FILENAME ]] || ! test -f "$FILENAME" ; then
+ message "${PROBLEM_COLOR}Source file not found.${DEFAULT_COLOR}"
+ return 1
+ fi
+
+ uncompress_md5 "$FILENAME" "$COMPRESSOR" "$2" |
+ unpack_core "$FILENAME" "$COMPRESSOR" &&
+ {
+
+ # This section takes care of what happens if the md5sum doesn't match.
+ # $TMP_DIR/libgrimoire.uncompress.$$ is set in uncompress. It's the only
+ # way to get the return value since it's in a pipe.
+ if ! [[ $2 ]] ; then
+
+ rm "$TMP_DIR/libgrimoire.uncompress.$$"
+
+ message "${SPELL_COLOR}${SPELL}: ${QUERY_COLOR}doesn't have an" \
+ "MD5 sum for the uncompressed $1."
+
+ case "$MD5SUM_DL" in
+ off) message "${RED}Continuing!${DEFAULT_COLOR}"; return 0 ;;
+ ask_ignore) query "Abort?" "n" && return 1 || return 0 ;;
+ ask_risky|ask_abort) query "Abort?" "y" && return 1 || return 0 ;;
+ on|abort_all|*) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
+ esac
+
+ elif [[ $2 == "IGNORE" ]] ; then
+
+ rm "$TMP_DIR/libgrimoire.uncompress.$$"
+
+ message "${SPELL_COLOR}${SPELL}: ${QUERY_COLOR}MD5 sum was" \
+ "purposefully left out for the uncompressed $1."
+ message "${QUERY_COLOR}Would you like to abort so you can validate" \
+ "the source yourself via some alternate method?"
+
+ case "$MD5SUM_DL" in
+ off) message "${RED}Continuing!${DEFAULT_COLOR}"; return 0 ;;
+ask_risky|ask_ignore) query "Abort?" "n" && return 1 || return 0 ;;
+ abort_all) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
+ ask_abort|on|*) query "Abort?" "y" && return 1 || return 0 ;;
+ esac
+
+ elif [[ "$(cat $TMP_DIR/libgrimoire.uncompress.$$)" != 0 ]] ; then
+
+ rm "$TMP_DIR/libgrimoire.uncompress.$$"
+
+ message "${SPELL_COLOR}${SPELL}: ${QUERY_COLOR}MD5 sum is different" \
+ "for uncompressed $1."
+
+ case "$MD5SUM_DL" in
+ off) message "${RED}Continuing!${DEFAULT_COLOR}"; return 0 ;;
+ ask_ignore) query "Abort?" "n" && return 1 || return 0 ;;
+ ask_risky|ask_abort) query "Abort?" "y" && return 1 || return 0 ;;
+ on|abort_all|*) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
+ esac
+
+ fi
+
+ rm "$TMP_DIR/libgrimoire.uncompress.$$"
+
+ }
+
+ #By this point, the archive is unarchived, and we know the MD5 check was good.
+ return 0
+
+}
+
+
+#---------------------------------------------------------------------
+## @param filename
+## @param compressor
+## @param md5
+## @Stdout uncompressed
+##
+## Uncompress_md5 dumps the expanded file via tee to md5_tar_check where it
+## is gobbled up by the bitbucket. It also dumps the main stream out to
+## stdout.
+##
+#---------------------------------------------------------------------
+function uncompress_md5() {
+ debug "libgrimoire" "uncompress_md5 - $*"
+
+ # This is here so Duff's super debugging info doesn't screw the next step up
+ set +x
+
+ # Outer subshell is necessary to redirect stderr to stdout
+ (
+ uncompress_core "$1" "$2" |
+ tee /dev/stderr |
+ md5_tar_check "$3" 2>&1 1>/dev/null #we must avoid this printing
+ ) 2>&1
+
+ # This temp file is here because this function MUST NOT send
+ # anything to stdout or stderr, and upack needs a way to get the success or
+ # failure of this function.
+
+ local a="$?"
+ [[ $SUPER_DEBUG ]] && set -x #turn this back on as soon as possible
+ echo "$a" > "$TMP_DIR/libgrimoire.uncompress.$$"
+ return "$a"
+
+}
+
+
+#---------------------------------------------------------------------
+## @param md5
+##
+## Checks that the stdin matches the argument.
+## Note that DEBUG output may dissapear if it's /dev/stderr due to
+## uncompress' 2>/dev/null.
+##
+#---------------------------------------------------------------------
+function md5_tar_check() {
+ debug "libgrimoire" "md5_tar_check() - Checking MD5 sum"
+
+ local md5
+
+ #Do the md5
+ md5="$(md5sum /dev/stdin | awk '{print $1}')"
+ debug "libgrimoire" "md5_tar_check() - MD5 of tarball is $md5."
+ debug "libgrimoire" "md5_tar_check() - argument received is $1."
+
+ #See if they match
+ if [[ $1 == $md5 ]] ; then
+ debug "libgrimoire" "md5_tar_check() - MD5 Sum Success ( $1 == $md5 )"
+ return 0
+ fi
+
+ #See of we need to md5sum it at all
+ if [[ ${MD5SUM_DL:-on} == off ]] || ! [[ $1 ]] ; then
+ debug "libgrimoire" "md5_tar_check() - Skipping check"
+ return 0
+ fi
+
+ #If we get here, the md5's don't match, but should.
+ debug "libgrimoire" "md5_tar_check() - bad md5"
+ return 1
+
+}
+
+
+#---------------------------------------------------------------------
+## @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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/liburl b/var/lib/sorcery/modules/liburl
new file mode 100644
index 0000000..d4df1ea
--- /dev/null
+++ b/var/lib/sorcery/modules/liburl
@@ -0,0 +1,511 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## @Synopsis Functions that download and verify urls.
+##
+##
+## This file contains functions for downloading and verifying urls.
+## It does this by extracting information from a url with url handlers
+## specific to that url type. The url handler also determines a download
+## handler to actually download the url.
+##
+## FIXME
+## For example, the request to download
+## the following url is made through the generic F<url_download>
+## function:
+##
+## http://machinename.com/path/to/file.tar.bz2
+##
+## The F<url_download> function parses the url prefix (in this
+## case, http) and passes the url to the http download handler
+## (F<url_http_download>). A similar approach is used for url
+## verification.
+##
+## This file provides an infrastructure that makes it relatively easy
+## to add new url handlers. In order to add new handlers, all that
+## has to be done is add a new file to the sorcerer library directory
+## with the new url handler functions defined in the file. This new
+## file will automatically be discovered and used by the sorcerer
+## scripts.
+##
+## The following section describes how to add new url handlers in
+## a little more detail.
+##
+## <p>WRITING NEW URL HANDLERS</p>
+##
+## This section describes the steps needed to write new url handlers.
+##
+## <p>Decide on the Url Format</p>
+##
+## Urls must be of the form <prefix>://<address>. The prefix should
+## be something unique. Only the prefix is used by this script,
+## the address is not parsed or used, simply passed to the appropriate
+## url handler.
+##
+## <p>Create a File to Hold the New Url Handling Functions</p>
+##
+## In the SGL library directory (i.e., the directory pointed to by the
+## SGL_LIBRARY variable), create a new file called url_<prefix>. For
+## example, if your new url prefix is I<xyz>, you should create a new
+## file called F<url_xyz>. The file should be executable.
+##
+## <p>Implement Url Handlers</p>
+##
+## The next step is to write the actual functions that
+## will handle url requests and put them in the new file you just
+## created. The functions that must be implemented are:
+##
+## url_<URL_PREFIX>_bucketize <url>
+## url_<URL_PREFIX>_crack <url>
+## url_<URL_PREFIX>_expand <url>
+## url_<URL_PREFIX>_hostname <url>
+## url_<URL_PREFIX>_is_valid <url>
+## url_<URL_PREFIX>_netselect <url>
+## url_<URL_PREFIX>_verify <url>
+##
+## The easiest way to figure out what to do is to look at
+## one of the existing files (e.g., url_http handles http requests).
+##
+## <p>Handling Multiple Url Types in a Single File</p>
+##
+## It's perfectly valid for a file to handle mutlple types of urls.
+## The F<url_http> file actually handles ftp, http, and https urls.
+## Take a look at the file to see how it's done.
+##
+##
+## @Copyright Copyright 2002 by the Source Mage Team
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+# Load library files (url_*) that contain url handlers
+#
+# (2002/09/29) added if so it onlt loads the stuff once
+#---------------------------------------------------------------------
+if ! [[ $URL_HANDLER_FILES ]] ; then
+ URL_HANDLER_FILES=`ls $SGL_LIBRARY_MODULES/url_handlers/url_*[^~]`
+ for url_handler_file in $URL_HANDLER_FILES; do
+ [ -x $url_handler_file ] &&
+ URL_PREFIX=`echo $url_handler_file | sed "s/.*\/url_//"` &&
+ URL_TYPES[${#URL_TYPES[@]}]=$URL_PREFIX &&
+ . $url_handler_file
+ done
+fi
+
+#---------------------------------------------------------------------
+## @param see url_download
+##
+## This is simply a wrapper around url_download which provides the
+## additional functionality of expanding and ranking urls.
+## Processes like summon should use this, more defined processes
+## like scribe and sorcery update will probably directly call url_download.
+##
+#---------------------------------------------------------------------
+function url_download_expand_sort() {
+ $STD_DEBUG
+ local target=$1
+ local url_list=$2
+ local hints=$3
+ local udl_target=$4
+ local udl_type=$5
+
+ local url expanded_urls sorted_urls
+
+ debug "liburl" "$FUNCNAME -- $@"
+ # you can't expect me to download something without urls...
+ [ -z "$url_list" ] && return 255
+
+ # expand urls
+ url_expand_urls expanded_urls "$url_list"
+ if ! [[ "$expanded_urls" ]] ; then
+ message "No expanded urls! If you get this it is a sorcery bug"
+ return 1
+ fi
+ debug "liburl" "expanded urls $expanded_urls"
+ # sort urls
+ url_sort_urls sorted_urls "$expanded_urls"
+ if ! [[ "$sorted_urls" ]] ; then
+ message "No sorted urls! If you get this it is a sorcery bug"
+ return 1
+ fi
+ # shorten the list to a manageable amount
+ sorted_urls=$(echo "$sorted_urls"|head -n 10)
+ debug "liburl" "sorted urls $sorted_urls"
+ url_download "$1" "$sorted_urls" "$3" "$4" "$5"
+}
+
+#---------------------------------------------------------------------
+## @param target Expected target file or tree, this is only a
+## suggestion, and the download handler may ignore it.
+## @param url_list List of urls to get the target from
+## @param hints Hints, these help the function determine what
+## type of thing it is downloading or other tunables.
+## @param udl_target Name of variable in which to store the name of the
+## result directory or file
+## @param udl_type Name of the variable in which to store the type of
+## thing downloaded
+##
+## @return 0 if file could be downloaded
+## @return 1 otherwise
+## Downloads the specified url. Returns true if file could be
+## downloaded, false otherwise.
+##
+##
+#---------------------------------------------------------------------
+function url_download() {
+ $STD_DEBUG
+ local target=$1
+ local url_list=$2
+ local hints=$3
+ local udl_target=$4
+ local udl_type=$5
+
+ local url
+
+ debug "liburl" "$FUNCNAME -- $@"
+
+ # you can't expect me to download something without urls...
+ [ -z "$url_list" ] && return 255
+
+ # sanity check urls
+ local valid_urls
+ url_get_valid_urls valid_urls "$url_list"
+ if ! [[ "$valid_urls" ]] ; then
+ message "No valid urls!"
+ return 1
+ fi
+
+ hash_reset dl_buckets
+ local bucket
+ # bucketize
+ for url in $valid_urls; do
+ bucket=$(url_bucketize "$url")
+ if [[ $bucket ]] ; then
+ hash_append dl_buckets "$bucket" "$url"
+ else
+ message "Failed to find a download handler for $url, this is likely to be a sorcery bug"
+ fi
+ done
+
+ # sort buckets, someday a better algorithm may be used, however
+ # this should be sufficient for now.
+ buckets=$(hash_get_table_fields dl_buckets|sort)
+
+ # iterate through buckets
+ for bucket in $buckets; do
+ dl_get_bucket "$bucket" "$target" "$(hash_get dl_buckets $bucket)" \
+ "$hints" "$udl_target" "$udl_type"
+ rc=$?
+ if [[ $rc == 0 ]] ; then
+ debug "liburl" "url_download -- downloaded $url $udl_r_target $udl_r_type"
+ return 0
+ fi
+ done
+ return 1
+}
+
+
+#---------------------------------------------------------------------
+## @param urllist
+#---------------------------------------------------------------------
+function url_get_valid_urls() {
+ $STD_DEBUG
+ local upvar="$1"
+ local url_list="$2"
+ local tmp_list=$(
+ for url in $url_list; do
+ if url_is_valid "$url"; then
+ echo "$url"
+ fi
+ done
+ )
+ eval "$upvar=\"\$tmp_list\""
+}
+
+
+#---------------------------------------------------------------------
+## @param urllist
+##
+## Frontend to expand urls into a nice list.
+#---------------------------------------------------------------------
+function url_expand_urls() {
+ $STD_DEBUG
+ local upvar="$1"
+ local url_list="$2"
+ local tmp_list
+ tmp_list="$(for url in $url_list; do
+ # this should expand to something or return itself
+ url_expand "$url"
+ done|sort|uniq)"
+ # this ensures that the urls originally specified are given preference
+ for url in $url_list; do
+ tmp_list=$(echo "$tmp_list"|grep -v $url)
+ done
+ tmp_list="$(echo $url_list|tr ' ' '\n')"$'\n'"$tmp_list"
+ eval "$upvar=\"\$tmp_list\""
+}
+
+#---------------------------------------------------------------------
+## @param urllist
+#---------------------------------------------------------------------
+function url_sort_urls() {
+ $STD_DEBUG
+ local upvar="$1"
+ local url_list="$2"
+ if [[ $NET_SELECT == on ]] && spell_installed "netselect" ; then
+ lock_resources "network" "network"
+ message -n "${CHECK_COLOR}Looking for the fastest mirror site...${DEFAULT_COLOR} "
+ dl_connect
+ url_list="$(url_rank $url_list 2> /dev/null)"
+ dl_disconnect
+ unlock_resources "network" "network"
+ message " done."
+ fi
+ eval "$upvar=\"\$url_list\""
+}
+
+#---------------------------------------------------------------------
+## @param urllist
+## @Stdout new list
+## Ranks the urls in order of speed from fastest to slowest using netselect
+## Makes use of url_<url_type>_hostname functions.
+##
+## If multiple urls from the same hostname are passed in, their ordering
+## is preserved, although that group of urls may move as a whole in
+## the list.
+#---------------------------------------------------------------------
+function url_rank() {
+
+ local urlList sortedList urlCounter
+ local finalList url_speed
+ local tmp_url tmp_prefix tmp_hostname tmp_list
+
+ debug "liburl" "unsorted list: $*"
+
+ urlList="$*"
+
+ # Even if theres one url it might have multiple A records and we'll
+ # want netselect to find the fastest one
+
+ # The take home message is that a url can be /any/ random string of text,
+ # and a url is officially defined by its handler.
+
+ # So we are having the handler give us the netselected output
+ # from there we are sticking things into an array and letting
+ # bash handle the sorting for us...
+ function url_rank_divide_by_prefix() {
+ local tmp_url=$1 tmp_prefix
+ tmp_prefix=$(url_get_prefix $tmp_url) &&
+ hash_append url_div_hash $tmp_prefix $tmp_url
+ }
+ iterate url_rank_divide_by_prefix " $IFS" $urlList
+
+ for tmp_prefix in $(hash_get_table_fields url_div_hash) ; do
+ tmp_list=$(hash_get url_div_hash $tmp_prefix)
+ debug liburl "urls are $tmp_list"
+ # the awk command turns netselect output like this:
+ # 69 url1
+ # 234 url2
+ # into the following bash code, which is then executed
+ # url_speed[69]="${url_speed[69]} url1"
+ # url_speed[234]="${url_speed[234]} url2"
+ eval $(url_netselect $tmp_list|
+ awk '{printf "url_speed[%s]=\"${url_speed[%s]} %s\";",$1,$1,$2}')
+ done
+
+ # since we put things in url_speed indexed by speed, the * should
+ # expand back in sorted order
+
+ sortedList=$(echo ${url_speed[*]})
+ debug "liburl" "Ordered list pre-sanity check is $sortedList"
+
+ # if all sites are ICMP Unreachable, return the unsorted list instead of null.
+ if [[ -z "$sortedList" ]] ; then
+ echo $urlList
+ return
+ fi
+
+ # try really hard not to lose urls by appending missing ones to the end
+ # of the list, please tell me if theres a faster way to do this
+ function url_rank_iterator2() {
+ echo $sortedList|grep -q "$1" || sortedList="$sortedList $1"
+ }
+ iterate url_rank_iterator2 " $IFS" $urlList
+
+ # just in case something failed along the way just return what we had
+ if [[ -z "$sortedList" ]] ; then
+ debug "liburl" "Ordering failed somewhere, giving back original input"
+ echo $urlList
+ else
+ debug "liburl" "Ordered URLs: $sortedList"
+ echo $sortedList
+ fi
+}
+
+#---------------------------------------------------------------------
+## @Type Private
+## @param url
+## @Stdout url prefix
+## @return 0 valid url
+## @return 1 otherwise
+## Takes a url and echos the url prefix. Returns
+## true if a valid url could be found, returns false otherwise.
+##
+## This is the only place parsing of a url should take place outside of
+## a url_handler. Doing so elsewhere is bugworthy.
+##
+#---------------------------------------------------------------------
+function url_get_prefix() {
+ $STD_DEBUG
+ local URL=$1
+ local URL_PREFIX=${URL/:\/\/*}
+ [ -n "$URL_PREFIX" ] &&
+ [ "$URL_PREFIX" != "$URL" ] &&
+ echo $URL_PREFIX
+}
+
+
+#---------------------------------------------------------------------
+## @Type Private
+## @param url
+## @param url prefix
+## @stdout url sans prefix
+#---------------------------------------------------------------------
+function url_strip_prefix() {
+ $STD_DEBUG
+ echo $1 | sed "s!^$2://!!"
+}
+
+#---------------------------------------------------------------------
+## url handler api functions. Use these to access a url specific
+## functionality/data.
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## @param url
+## @stdout dl handler
+##
+## Get the download handler for this url
+#---------------------------------------------------------------------
+function url_bucketize() {
+ $STD_DEBUG
+ url_generic_apifunc bucketize $1
+}
+
+#---------------------------------------------------------------------
+## @param url
+##
+## Parse the url somehow. The results are url specific right now,
+## this is usually only called by dl handlers who know what url types
+## they can handle, and thus, understand the return value.
+#---------------------------------------------------------------------
+function url_crack() {
+ $STD_DEBUG
+ url_generic_apifunc crack $1
+}
+
+#---------------------------------------------------------------------
+## @param url(s)
+## @stdout urls
+##
+## Attempt to get more similar urls to the given one based on the
+## sorcery mirrors files. Most url types simply expand to the input.
+#---------------------------------------------------------------------
+function url_expand() {
+ $STD_DEBUG
+ url_generic_apifunc expand "$@"
+}
+
+#---------------------------------------------------------------------
+## @param url
+##
+## Verify the url, this usually means going out to the internet and
+## somehow determining if the link is good.
+#---------------------------------------------------------------------
+function url_verify() {
+ $STD_DEBUG
+ url_generic_apifunc verify $1
+}
+
+#---------------------------------------------------------------------
+## @param url
+## @Stdout url hostname
+## @return 0 valid url
+## @return 1 otherwise
+## Takes a url and echos the url hostname. Returns
+## true if a hostname could be found, returns false otherwise.
+##
+#---------------------------------------------------------------------
+function url_hostname() {
+ $STD_DEBUG
+ url_generic_apifunc hostname $1
+}
+
+#---------------------------------------------------------------------
+## @param url
+## @Stdout url netslect output
+## @return 0 valid url
+## @return 1 otherwise
+## Prints the netselect output from the url handlers attempt at
+## running netselect.
+##
+#---------------------------------------------------------------------
+function url_netselect() {
+ $STD_DEBUG
+ url_generic_apifunc netselect "$@"
+}
+
+#---------------------------------------------------------------------
+## @param url
+## @return 0 valid url
+## @return 1 otherwise
+## Returns true if the given url is a valid url understood by the url
+## library, returns false otherwise.
+##
+#---------------------------------------------------------------------
+function url_is_valid() {
+ $STD_DEBUG
+ url_generic_apifunc is_valid $1
+}
+
+
+#---------------------------------------------------------------------
+## @param function name
+## @param url(s)
+##
+## This implements the common code for simple url handler inheritence.
+## The above url api functions call this which then calls the handler
+## specific function, if it exists, or the default version. This
+## allows url handlers to only override functions as necessary.
+##
+## If multiple urls are given, the prefix is assumed of all of them
+## is assumed to be the same.
+##
+#---------------------------------------------------------------------
+function url_generic_apifunc() {
+ local tmp_func=$1
+ shift
+ local tmp_url=$1 tmp_prefix
+ tmp_prefix=$(url_get_prefix $1)
+ if misc_is_function url_${tmp_prefix}_${tmp_func}; then
+ url_${tmp_prefix}_${tmp_func} "$@"
+ else
+ url_default_${tmp_func} "$@"
+ fi
+}
+
+#---------------------------------------------------------------------
+## 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
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/url_handlers/url_cvs b/var/lib/sorcery/modules/url_handlers/url_cvs
new file mode 100644
index 0000000..68ac9f3
--- /dev/null
+++ b/var/lib/sorcery/modules/url_handlers/url_cvs
@@ -0,0 +1,159 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Url handler functions for grabbing cvs urls.
+##
+##=head1 DESCRIPTION
+##
+## This file contains functions for parsing cvs urls.
+##
+##=head1 CVS URL Format
+##
+## There is no standard (that I know of) for cvs urls so we use a
+## source mage specific format:
+##
+## cvs://CVSROOT:MODULE_NAME
+##
+## The above url will download the latest version of the specified
+## module (i.e., the HEAD revision). To specify a specific revision,
+## the following format can be used:
+##
+## cvs://CVSROOT:MODULE_NAME:REVISION_TAG
+##
+## The CVSROOT portion of the url may include information such as type of cvs
+## server, port number for the server, user name, password, cvs repository
+## directory, etc. The CVSROOT syntax is defined by cvs and is as follows:
+##
+## :method:[[user][:password]@]hostname[:[port]]/path/to/repository
+##
+## For more details, see the CVS manual at
+## http://www.cvshome.org/docs/manual/cvs.html
+##
+##=head1 EXAMPLES
+##
+## Suppose we want to download the latest version of the sorcery
+## scripts from cvs. We'd use the following url:
+##
+## cvs://:pserver:anonymous@mplayerhq.hu:/cvsroot/mplayer:main
+##
+## If we want the 1.0pre7 release instead (i.e., those files tagged with
+## 1_0pre7, we would use the following url:
+##
+## cvs://:pserver:anonymous@mplayerhq.hu:/cvsroot/mplayer:main:1_0pre7
+##
+## Some cvs repositories require passwords. One such repository is the
+## cvs repository for the ROOT package (an object-oriented data analysis
+## framework, see L<http://root.cern.ch/root/>). The CVSROOT, without the
+## password, would look like this:
+##
+## :pserver:cvs@root.cern.ch:/user/cvs
+##
+## The password for their cvs repository is I<cvs>. Adding the password would
+## make the CVSROOT look like this:
+##
+## :pserver:cvs:cvs@root.cern.ch:/user/cvs
+##
+## Thus, the full cvs url, including the password, would be:
+##
+## cvs://:pserver:cvs:cvs@root.cern.ch:/user/cvs:root
+##
+##=head1 COPYRIGHT
+##
+## Copyright 2002 by the Source Mage Team
+##
+##=head1 FUNCTIONS
+##
+##=over 4
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+##=item url_file_download <url>
+##
+## Parse the specified cvs url.
+##
+## @global URL
+## @global CVS_ROOT
+## @global CVS_MODULE
+## @global CVS_TAG
+##
+#---------------------------------------------------------------------
+function url_cvs_crack() {
+
+ URL=`url_strip_prefix "$1" cvs`
+ CVS_ROOT=`echo $URL | sed "s#\(^[^/]*[^:]*\):.*#\1#"`
+ local CVS_MODULE_TAG=`echo $URL | sed "s#^[^/]*[^:]*\(.*\)#\1#"`
+ CVS_MODULE=`echo $CVS_MODULE_TAG | cut -d : -f2`
+ local CVS_TAGNAME=`echo $CVS_MODULE_TAG | cut -d : -f3`
+ CVS_TAG=${CVS_TAGNAME:=HEAD}
+
+}
+
+#---------------------------------------------------------------------
+##=item url_cvs_is_valid <url>
+##
+## Ensure that all the fields that should be parsed out from a url
+## do indeed exist
+#---------------------------------------------------------------------
+function url_cvs_is_valid() {
+ local URL CVS_ROOT CVS_MODULE CVS_TAG item
+ url_cvs_crack $1
+ for item in URL CVS_ROOT CVS_MODULE CVS_TAG; do
+ if ! [[ ${!item} ]] ; then
+ return 1
+ fi
+ done
+}
+
+#---------------------------------------------------------------------
+##=item url_cvs_hostname <url>
+##
+## Get the hostname of the url
+##
+#---------------------------------------------------------------------
+function url_cvs_hostname() {
+ echo $1|sed 's/^[^@]*@\([^:]*\):.*$/\1/'
+}
+
+#---------------------------------------------------------------------
+##=item url_cvs_netselect <url>
+##
+## Gets a netselect type output for the url
+##
+#---------------------------------------------------------------------
+function url_cvs_netselect() {
+ local tmp_hostname url_speed each
+
+ for each in $@ ; do
+ tmp_hostname=$(url_cvs_hostname $each)
+ # since we had to pull the url appart to give netselect
+ # something it can understand we'll just pretend like
+ # multiple A records wont exist for this host...
+ url_speed=$(netselect -s 1 $tmp_hostname 2>/dev/null|awk '{print $1}')
+ [[ -n $url_speed ]] && echo "$url_speed $each"
+ done
+}
+
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
+
diff --git a/var/lib/sorcery/modules/url_handlers/url_default b/var/lib/sorcery/modules/url_handlers/url_default
new file mode 100644
index 0000000..ff97283
--- /dev/null
+++ b/var/lib/sorcery/modules/url_handlers/url_default
@@ -0,0 +1,106 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## @Synopsis
+##
+## Default implementations of url_handler api.
+##
+## @Copyright Copyright 2005 by the Source Mage Team
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+##=item url_default_bucketize
+## @param url
+##
+## Outputs file as that is the dl handler for file:// urls
+##
+#---------------------------------------------------------------------
+function url_default_bucketize() {
+ url_get_prefix $1
+}
+
+#---------------------------------------------------------------------
+##=item url_default_crack
+##
+## Outputs the url with the prefix removed
+##
+#---------------------------------------------------------------------
+function url_default_crack() {
+ local prefix=$(url_get_prefix $1)
+ [[ $prefix ]] || return 1
+ url_strip_prefix "$1" $prefix
+}
+
+#---------------------------------------------------------------------
+##=item url_default_expand
+#---------------------------------------------------------------------
+function url_default_expand() {
+ echo "$@"
+}
+
+#---------------------------------------------------------------------
+##=item url_default_is_valid
+## @param url
+##
+## True if url_crack returns true
+##
+#---------------------------------------------------------------------
+function url_default_is_valid() {
+ url_crack $1 >/dev/null
+}
+
+#---------------------------------------------------------------------
+##=url_default_verify
+##
+## Always returns true
+##
+#---------------------------------------------------------------------
+function url_default_verify() {
+ true
+}
+
+#---------------------------------------------------------------------
+##=item url_default_hostname
+##
+## Outputs localhost
+##
+#---------------------------------------------------------------------
+function url_default_hostname() {
+ echo localhost
+}
+
+#---------------------------------------------------------------------
+##=item url_default_netselect
+##
+## Fake netselect return 0 for each url
+##
+#---------------------------------------------------------------------
+function url_default_netselect() {
+ local each
+ for each in $@ ; do
+ echo "0 $each"
+ done
+}
+
+
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/url_handlers/url_dir b/var/lib/sorcery/modules/url_handlers/url_dir
new file mode 100644
index 0000000..0e8a25e
--- /dev/null
+++ b/var/lib/sorcery/modules/url_handlers/url_dir
@@ -0,0 +1,73 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Url handler functions for grabbing directory urls.
+##
+##=head1 DESCRIPTION
+##
+## This type of url was added as a response to the following request:
+##
+## I'm an AbiWord developer, and I keep the latest source
+## tree in my abi directory. If I could make the abi spell
+## (not to be confused with the abispell) use my current
+## CVS checked-out tree, that would be nice.
+##
+## This file contains functions for I<downloading> (actually it just
+## copies, tars, and compresses) directories which can be accessed
+## through the local file system.
+##
+## Url's of this type are specified using the format
+##
+## dir://<directory>
+##
+## where <directory> is the full path to a directory that will be
+## tarred and compressed for use by sorcery in casting a spell.
+##
+##=head1 COPYRIGHT
+##
+## Copyright 2002 by the Source Mage Team
+##
+##=head1 FUNCTIONS
+##
+##=over 4
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+##=item url_dir_verify <url>
+##
+## Verifies the specified directory exists. Returns true if the
+## directory exists OR if the dir is an empty string.
+##
+#---------------------------------------------------------------------
+function url_dir_verify() {
+
+ DIRNAME=`url_dir_crack $1`
+ if [ -n "$DIRNAME" ]; then
+ [ -d "$DIRNAME" ]
+ 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
+##
+#---------------------------------------------------------------------
+
diff --git a/var/lib/sorcery/modules/url_handlers/url_file b/var/lib/sorcery/modules/url_handlers/url_file
new file mode 100644
index 0000000..b8aabef
--- /dev/null
+++ b/var/lib/sorcery/modules/url_handlers/url_file
@@ -0,0 +1,52 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+## @Synopsis Url handler functions for grabbing file urls.
+## This file contains functions for I<downloading> (actually it just
+## copies) files which can be accessed through the local file system.
+## Url's of this type are specified using the format
+##
+## file://<path>
+##
+## where <path> is the full path to the file.
+##
+## @Copyright Copyright 2002 by the Source Mage Team
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+## @param url
+##
+## Verifies the specified file exists. Returns true if the file exists
+## OR if the file is an empty string.
+##
+#---------------------------------------------------------------------
+function url_file_verify() {
+ FILENAME=`url_file_strip_prefix $1`
+ if [[ "$FILENAME" ]] ; then
+ [ -f "$FILENAME" ] &&
+ [ -r "$FILENAME" ]
+ 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
+##
+#---------------------------------------------------------------------
+
diff --git a/var/lib/sorcery/modules/url_handlers/url_http b/var/lib/sorcery/modules/url_handlers/url_http
new file mode 100644
index 0000000..ad99515
--- /dev/null
+++ b/var/lib/sorcery/modules/url_handlers/url_http
@@ -0,0 +1,193 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Url handler functions for downloading http, https, and ftp urls
+##
+##=head1 DESCRIPTION
+##
+## This file contains functions for downloading and verifying
+## http, https, and ftp urls. This file uses the "wget" program.
+##
+##=head1 COPYRIGHT
+##
+## Copyright 2002 by the Source Mage Team
+##
+##=head1 FUNCTIONS
+##
+##=over 4
+##
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+##=item url_http_bucketize <url>
+##
+## Verifies the specified http url. Returns true if the url exists
+## OR if the url is an empty string.
+##
+#---------------------------------------------------------------------
+function url_http_bucketize() {
+ echo wget
+}
+function url_ftp_bucketize() {
+ echo wget
+}
+function url_https_bucketize() {
+ echo wget
+}
+
+#---------------------------------------------------------------------
+##=item url_http_verify <url>
+##
+## Verifies the specified http url. Returns true if the url exists
+## OR if the url is an empty string.
+##
+#---------------------------------------------------------------------
+function url_http_verify() {
+ local URL=$1
+
+ if [ -n "$URL" ]; then
+ if OUTPUT=`wget --passive-ftp -t 1 -T 30 --spider "$URL" 2>&1`; then
+ true
+ else
+ echo $OUTPUT
+ false
+ fi
+ fi
+}
+
+
+#---------------------------------------------------------------------
+##=item url_https_verify <url>
+##
+## Verifies the specified https url. Returns true if the url exists
+## OR if the url is an empty string.
+##
+#---------------------------------------------------------------------
+function url_https_verify() {
+ url_http_verify $1
+}
+
+
+#---------------------------------------------------------------------
+##=item url_ftp_verify <url>
+##
+## Verifies the specified ftp url. Echos results of wget if file
+## is not found.
+##
+#
+# Implementation note: wget --spider still downloads ftp files in
+# full rather than just checking that the file is there. To get
+# around this problem, we download the directory and see if the file
+# is in the directory listing.
+#
+#---------------------------------------------------------------------
+function url_ftp_verify() {
+ local URL=$1
+
+ if [ -n "$URL" ]; then
+ local FILENAME=`basename $URL`
+ local DIRECTORY=`dirname $URL`
+ local OUTPUT=`wget --passive-ftp -t 1 -T 30 -O - --spider -nr "$DIRECTORY/" 2>&1`
+
+ if echo $OUTPUT | grep -q "$FILENAME"; then
+ rm -f .listing
+ else
+ echo $OUTPUT | sed 's/LIST.*//g'
+ [ -f .listing ] && cat .listing
+ rm -f .listing
+ false
+ fi
+ fi
+}
+
+#---------------------------------------------------------------------
+##=item url_<prefix>_hostname <url>
+##
+## Gets the hostname out of the url
+#---------------------------------------------------------------------
+function url_http_hostname() {
+ echo $1|sed 's:^.*//\([^/]*\).*$:\1:'
+}
+function url_https_hostname() {
+ url_http_hostname $1
+}
+function url_ftp_hostname() {
+ url_http_hostname $1
+}
+
+#---------------------------------------------------------------------
+##=item url_<prefix>_netselect <url>
+##
+## Run netselect on the url, netselect understands these urls and
+## we can take advantage of its multi-A record handling
+##
+#---------------------------------------------------------------------
+function url_http_netselect() {
+ netselect -t 3 -s 1000 "$@" 2>/dev/null
+}
+function url_https_netselect() {
+ netselect -t 3 -s 1000 "$@" 2>/dev/null
+}
+function url_ftp_netselect() {
+ netselect -t 3 -s 1000 "$@" 2>/dev/null
+}
+
+#-------------------------------------------------------------------------
+## expand a url to ALL of the mirrors we think may be related
+## Take the url, find its hostname, if a file in $MIRRORS matches
+## generate new urls from all the mirrors in the file, the original url
+## is kept at the top of the list and not duplicated
+##
+## @param a list of urls
+## @stdout the expanded form of those urls
+#-------------------------------------------------------------------------
+function url_http_expand() {
+ local A=$'\a'
+ local URL rep tgt my_hostname each
+ # put the requested url first
+ echo "$@"
+ for URL in $@ ; do
+ my_hostname=$(url_hostname ${URL})
+ # a mirror is listed in the mirror listings as either
+ # ftp://foo.somemirror.org/stuff
+ # OR
+ # ftp://foo.somemirror.org
+ # note neither ends in a '/' but one has a '/' at the end of the
+ # hostname, the other has an end of line. The \(/\|\$\) stuff matches
+ # either
+ for each in $(grep -l "://$my_hostname\(/\|\$\)" $MIRRORS/*); do
+ rep=$(grep "://$my_hostname\(/\|\$\)" $each|awk '{print $NF; exit 0; }')
+ for tgt in $(awk '{print $NF}' $each|grep -v '^Custom$'); do
+ echo ${URL}|sed "s$A${rep}$A${tgt}$A"
+ done
+ done |grep -v $my_hostname
+ done
+}
+
+function url_ftp_expand() {
+ url_http_expand "$@"
+}
+# note that there is no https_expand
+
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
diff --git a/var/lib/sorcery/modules/url_handlers/url_rsync b/var/lib/sorcery/modules/url_handlers/url_rsync
new file mode 100644
index 0000000..4a62fb7
--- /dev/null
+++ b/var/lib/sorcery/modules/url_handlers/url_rsync
@@ -0,0 +1,100 @@
+#!/bin/bash
+#---------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Url handler functions for grabbing rsync urls.
+##
+##=head1 DESCRIPTION
+##
+## This file contains functions for I<downloading> files through rsync.
+##
+## In order for rsync urls to be downloaded, the I<rsync> spell must have been
+## cast. This script first determines if rsync has been installed before
+## attempting to download a rsync url.
+##
+##=head1 RSYNC URL Format
+##
+##
+## rsync://SERVER::MODULE_NAME
+##
+## The above url will download the latest version of the specified
+## module.
+##
+##=head1 EXAMPLES
+##
+## Suppose we want to download the latest version of the sorcery
+## stable grimoire via rsync. We'd use the following url:
+##
+## rsync://codex.sourcemage.org::stable
+##
+##=head1 IMPLEMENTATION NOTE
+##
+## Downloading is supported but rsync url verification is not
+## currently supported.
+##
+##=head1 COPYRIGHT
+##
+## Copyright 2003 by the Source Mage Team
+##
+##=head1 FUNCTIONS
+##
+##=over 4
+##
+#---------------------------------------------------------------------
+
+
+#---------------------------------------------------------------------
+##=item url_rsync_crack <url>
+##
+## No cracking is needed on these urls currently.
+## Simply outputs the input.
+##
+#---------------------------------------------------------------------
+function url_rsync_crack() {
+ url_strip_prefix "$1" rsync
+}
+
+
+#---------------------------------------------------------------------
+##=item url_rsync_hostname <url>
+##
+## Gets the hostname for this rsync type url
+##
+#---------------------------------------------------------------------
+function url_rsync_hostname() {
+ echo $1|sed 's|^.*//\(.*\)::.*$|\1|'
+}
+
+#---------------------------------------------------------------------
+##=item url_rsync_netselect <url>
+##
+## run netselect on the url, this will also expand and rank when there
+## are multiple A records
+##
+#---------------------------------------------------------------------
+function url_rsync_netselect() {
+ netselect -s 1000 $@ 2>/dev/null
+}
+
+#---------------------------------------------------------------------
+##=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
+##
+#---------------------------------------------------------------------
+
diff --git a/var/lib/sorcery/modules/url_handlers/url_smgl_tla b/var/lib/sorcery/modules/url_handlers/url_smgl_tla
new file mode 100644
index 0000000..31272bc
--- /dev/null
+++ b/var/lib/sorcery/modules/url_handlers/url_smgl_tla
@@ -0,0 +1,139 @@
+#!/bin/bash
+## ----------------------------------------------------------------------------
+##
+##=head1 SYNOPSIS
+##
+## Url handler functions for parsing smgl's tla urls.
+##
+##=head1 DESCRIPTION
+##
+## This file contains functions for parsing tla urls.
+##
+##=head1 TLA URL Format
+##
+## There is no standard (that I know of) for tla urls so we use a
+## source mage specific format:
+##
+## smgl_tla://location%archive%revision
+##
+## Location is an embedded url, this will be passed directly to tla.
+## Archive is the name of an archive, the format of this is defined by tla.
+## Revision is the category--branch--version[--patch] construct defined by
+## tla.
+##
+## For more details, see the tla manual at
+## http://www.gnu.org/software/gnu-arch/
+##
+##=head1 EXAMPLES
+##
+## Suppose we want to download the latest version of the emacs-wiki
+## scripts from tla. We'd use the following url:
+##
+## smgl_tla://http://sacha.free.net.ph/notebook/arch%sacha@free.net.ph--main%emacs-wiki--stable--1.0
+##
+## If we want the 1.0.13 release instead (i.e., patch level 13)
+## we would use the following url:
+##
+## smgl_tla://http://sacha.free.net.ph/notebook/arch%sacha@free.net.ph--main%emacs-wiki--stable--1.0--patch-13
+##
+##=head1 COPYRIGHT
+##
+## Copyright 2005 the SourceMage Team
+##
+## ----------------------------------------------------------------------------
+
+
+# -----------------------------------------------------------------------------
+##=item url_smgl_tla_bucketize<url>
+##
+## Outputs "tla".
+# -----------------------------------------------------------------------------
+function url_smgl_tla_bucketize() {
+ echo tla
+}
+
+# -----------------------------------------------------------------------------
+##=item url_smgl_tla_crack <url>
+##
+## Parse the url
+##
+## @global URL
+## @global TLA_LOCATION
+## @global TLA_ARCHIVE
+## @global TLA_REVISION
+##
+# -----------------------------------------------------------------------------
+function url_smgl_tla_crack() {
+
+ URL=`url_strip_prefix "$1" smgl_tla`
+ TLA_LOCATION=`echo $URL|cut -d"%" -f1`
+ TLA_ARCHIVE=`echo $URL|cut -d"%" -f2`
+ TLA_REVISION=`echo $URL|cut -d"%" -f3`
+}
+
+
+# -----------------------------------------------------------------------------
+##=item url_smgl_tla_is_valid<url>
+##
+## Verifies the specified tla url exists. Returns true for now...
+##
+# ----------------------------------------------------------------------------
+function url_smgl_tla_is_valid() {
+ local URL TLA_LOCATION TLA_ARCHIVE TLA_REVISION item
+ for item in URL TLA_LOCATION TLA_ARCHIVE TLA_REVISION; do
+ [[ ${!item} ]] || return 1
+ done
+}
+
+
+#---------------------------------------------------------------------
+##=item url_smgl_tla_hostname <url>
+##
+## Get the hostname of the url
+##
+#---------------------------------------------------------------------
+function url_smgl_tla_hostname() {
+ local P1=$(echo $1|cut -d"%" -f1)
+ echo $P1|sed 's#^.*://\([^/:]*\)[/:].*$#\1#'
+}
+
+#---------------------------------------------------------------------
+##=item url_svn_netselect <url>
+##
+## Gets a netselect type output for the url
+##
+#---------------------------------------------------------------------
+function url_smgl_tla_netselect() {
+ local tmp_hostname url_speed each
+
+ for each in $@ ; do
+ tmp_hostname=$(url_smgl_tla_hostname $each)
+ # since we had to pull the url appart to give netselect
+ # something it can understand we'll just pretend like
+ # multiple A records wont exist for this host...
+ url_speed=$(netselect -s 1 $tmp_hostname 2>/dev/null|awk '{print $1}')
+ [[ -n $url_speed ]] && echo "$url_spe