1# require.bash 2# Author: Noah Friedman <friedman@prep.ai.mit.edu> 3# Created: 1992-07-08 4# Last modified: 1993-09-29 5# Public domain 6 7# Commentary: 8 9# These functions provide an interface based on the lisp implementation for 10# loading libraries when they are needed and eliminating redundant loading. 11# The basic idea is that each "package" (or set of routines, even if it is 12# only one function) registers itself with a symbol that marks a "feature" 13# as being "provided". If later you "require" a given feature, you save 14# yourself the trouble of explicitly loading it again. 15# 16# At the bottom of each package, put a "provide foobar", so when another 17# package has a "require foobar", it gets loaded and registered as a 18# "feature" that won't need to get loaded again. (See warning below for 19# reasons why provide should be put at the end.) 20# 21# The list of provided features are kept in the `FEATURES' variable, which 22# is not exported. Care should be taken not to munge this in the shell. 23# The search path comes from a colon-separated `FPATH' variable. It has no 24# default value and must be set by the user. 25# 26# Require uses `fpath_search', which works by scanning all of FPATH for a 27# file named the same as the required symbol but with a `.bash' appended to 28# the name. If that is found, it is loaded. If it is not, FPATH is 29# searched again for a file name the same as the feature (i.e. without any 30# extension). Fpath_search may be useful for doing library filename 31# lookups in other functions (such as a `load' or `autoload' function). 32# 33# Warning: Because require ultimately uses the builtin `source' command to 34# read in files, it has no way of undoing the commands contained in the 35# file if there is an error or if no provide statement appeared (this 36# differs from the lisp implementation of require, which normally undoes 37# most of the forms that were loaded if the require fails). Therefore, to 38# minize the number of problems caused by requiring a faulty package (such 39# as syntax errors in the source file) it is better to put the provide at 40# the end of the file, rather than at the beginning. 41 42# Code: 43 44# Exporting this variable would cause considerable lossage, since none of 45# the functions are exported (or at least, they're not guaranteed to be) 46export -n FEATURES 47 48#:docstring : 49# Null function. Provided only so that one can put page breaks in source 50# files without any ill effects. 51#:end docstring: 52# 53# (\\014 == C-l) 54eval "function $(echo -e \\014) () { : }" 55 56 57#:docstring featurep: 58# Usage: featurep argument 59# 60# Returns 0 (true) if argument is a provided feature. Returns 1 (false) 61# otherwise. 62#:end docstring: 63 64###;;;autoload 65function featurep () 66{ 67 local feature="$1" 68 69 case " ${FEATURES} " in 70 *" ${feature} "* ) return 0 ;; 71 esac 72 73 return 1 74} 75 76 77#:docstring provide: 78# Usage: provide symbol ... 79# 80# Register a list of symbols as provided features 81#:end docstring: 82 83###;;;autoload 84function provide () 85{ 86 local feature 87 88 for feature in "$@" ; do 89 if ! featurep "${feature}" ; then 90 FEATURES="${FEATURES} ${feature}" 91 fi 92 done 93 94 return 0 95} 96 97 98#:docstring require: 99# Usage: require feature {file} 100# 101# Load FEATURE if it is not already provided. Note that require does not 102# call `provide' to register features. The loaded file must do that 103# itself. If the package does not explicitly do a `provide' after being 104# loaded, require will complain about the feature not being provided on 105# stderr. 106# 107# Optional argument FILE means to try to load FEATURE from FILE. If no 108# file argument is given, require searches through FPATH (see fpath_search) 109# for the appropriate file. 110# 111# If the variable REQUIRE_FAILURE_FATAL is set, require will cause the 112# current shell invocation to exit, rather than merely return. This may be 113# useful for a shell script that vitally depends on a package. 114# 115#:end docstring: 116 117###;;;autoload 118function require () 119{ 120 local feature="$1" 121 local path="$2" 122 local file 123 124 if ! featurep "${feature}" ; then 125 file=$(fpath_search "${feature}" "${path}") && source "${file}" 126 127 if ! featurep "${feature}" ; then 128 echo "require: ${feature}: feature was not provided." 1>&2 129 if [ "${REQUIRE_FAILURE_FATAL+set}" = "set" ]; then 130 exit 1 131 fi 132 return 1 133 fi 134 fi 135 136 return 0 137} 138 139#:docstring fpath_search: 140# Usage: fpath_search filename {path ...} 141# 142# Search $FPATH for `filename' or, if `path' (a list) is specified, search 143# those directories instead of $FPATH. First the path is searched for an 144# occurrence of `filename.bash, then a second search is made for just 145# `filename'. 146#:end docstring: 147 148###;;;autoload 149function fpath_search () 150{ 151 local name="$1" 152 local path="$2" 153 local suffix=".bash" 154 local file 155 156 if [ -z "${path}" ]; then path="${FPATH}"; fi 157 158 for file in "${name}${suffix}" "${name}" ; do 159 set -- $(IFS=':' 160 set -- ${path} 161 for p in "$@" ; do 162 echo -n "${p:-.} " 163 done) 164 165 while [ $# -ne 0 ]; do 166 test -f "${1}/${file}" && { file="${1}/${file}"; break 2 } 167 shift 168 done 169 done 170 171 if [ $# -eq 0 ]; then 172 echo "fpath_search: ${name}: file not found in fpath" 1>&2 173 return 1 174 fi 175 176 echo "${file}" 177 return 0 178} 179 180provide require 181 182# require.bash ends here 183