summaryrefslogtreecommitdiffstats
path: root/examples/functions/autoload
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 06:17:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 06:17:24 +0000
commit9d8085074991d5c0a42d6fc96a2d1a3ee918aad1 (patch)
treec85bca1e6c11eb872edfc64c524d20f2b7e3307b /examples/functions/autoload
parentInitial commit. (diff)
downloadbash-9d8085074991d5c0a42d6fc96a2d1a3ee918aad1.tar.xz
bash-9d8085074991d5c0a42d6fc96a2d1a3ee918aad1.zip
Adding upstream version 5.1.upstream/5.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--examples/functions/autoload111
-rw-r--r--examples/functions/autoload.v2192
-rw-r--r--examples/functions/autoload.v3125
-rw-r--r--examples/functions/autoload.v4556
-rw-r--r--examples/functions/autoload.v4.t184
5 files changed, 1168 insertions, 0 deletions
diff --git a/examples/functions/autoload b/examples/functions/autoload
new file mode 100644
index 0000000..cb3a673
--- /dev/null
+++ b/examples/functions/autoload
@@ -0,0 +1,111 @@
+#
+# An almost ksh-compatible `autoload'. A function declared as `autoload' will
+# be read in from a file the same name as the function found by searching the
+# $FPATH (which works the same as $PATH), then that definition will be run.
+#
+# To do this without source support, we define a dummy function that, when
+# executed, will load the file (thereby re-defining the function), then
+# execute that newly-redefined function with the original arguments.
+#
+# It's not identical to ksh because ksh apparently does lazy evaluation
+# and looks for the file to load from only when the function is referenced.
+# This one requires that the file exist when the function is declared as
+# `autoload'.
+#
+# usage: autoload func [func...]
+#
+# The first cut of this was by Bill Trost, trost@reed.bitnet
+#
+# Chet Ramey
+# chet@ins.CWRU.Edu
+
+#
+# Declare a function ($1) to be autoloaded from a file ($2) when it is first
+# called. This defines a `temporary' function that will `.' the file
+# containing the real function definition, then execute that new definition with
+# the arguments given to this `fake' function. The autoload function defined
+# by the file and the file itself *must* be named identically.
+#
+
+aload()
+{
+ eval $1 '() { . '$2' ; '$1' "$@" ; return $? ; }'
+}
+
+#
+# Search $FPATH for a file the same name as the function given as $1, and
+# autoload the function from that file. There is no default $FPATH.
+#
+
+autoload()
+{
+ #
+ # Save the list of functions; we're going to blow away the arguments
+ # in a second. If any of the names contain white space, TFB.
+ #
+
+ local args="$*"
+
+ #
+ # This should, I think, list the functions marked as autoload and not
+ # yet defined, but we don't have enough information to do that here.
+ #
+ if [ $# -eq 0 ] ; then
+ echo "usage: autoload function [function...]" >&2
+ return 1
+ fi
+
+ #
+ # If there is no $FPATH, there is no work to be done
+ #
+
+ if [ -z "$FPATH" ] ; then
+ echo autoload: FPATH not set or null >&2
+ return 1
+ fi
+
+ #
+ # This treats FPATH exactly like PATH: a null field anywhere in the
+ # FPATH is treated the same as the current directory.
+ #
+ # The path splitting command is taken from Kernighan and Pike
+ #
+
+# fp=$(echo $FPATH | sed 's/^:/.:/
+# s/::/:.:/g
+# s/:$/:./
+# s/:/ /g')
+
+ # replaced with builtin mechanisms 2001 Oct 10
+
+ fp=${FPATH/#:/.:}
+ fp=${fp//::/:.:}
+ fp=${fp/%:/:.}
+ fp=${fp//:/ }
+
+ for FUNC in $args ; do
+ #
+ # We're blowing away the arguments to autoload here...
+ # We have to; there are no arrays (well, there are, but
+ # this doesn't use them yet).
+ #
+ set -- $fp
+
+ while [ $# -ne 0 ] ; do
+ if [ -f $1/$FUNC ] ; then
+ break # found it!
+ fi
+ shift
+ done
+
+ if [ $# -eq 0 ] ; then
+ echo "$FUNC: autoload function not found" >&2
+ continue
+ fi
+
+# echo auto-loading $FUNC from $1/$FUNC
+ aload $FUNC $1/$FUNC
+ done
+
+ return 0
+}
diff --git a/examples/functions/autoload.v2 b/examples/functions/autoload.v2
new file mode 100644
index 0000000..e29c695
--- /dev/null
+++ b/examples/functions/autoload.v2
@@ -0,0 +1,192 @@
+#
+# An almost ksh-compatible `autoload'. A function declared as `autoload' will
+# be read in from a file the same name as the function found by searching the
+# $FPATH (which works the same as $PATH), then that definition will be run.
+#
+# To do this without source support, we define a dummy function that, when
+# executed, will load the file (thereby re-defining the function), then
+# execute that newly-redefined function with the original arguments.
+#
+# It's not identical to ksh because ksh apparently does lazy evaluation
+# and looks for the file to load from only when the function is referenced.
+# This one requires that the file exist when the function is declared as
+# `autoload'.
+#
+# usage: autoload [-pu] [func ...]
+#
+# options:
+# -p print in a format that can be reused as input
+# -u unset each function and remove it from the autoload list
+#
+# The first cut of this was by Bill Trost, trost@reed.edu
+#
+# Chet Ramey
+# chet@ins.CWRU.Edu
+
+unset _AUTOLOADS
+_aindex=0
+
+#
+# Declare a function ($1) to be autoloaded from a file ($2) when it is first
+# called. This defines a `temporary' function that will `.' the file
+# containing the real function definition, then execute that new definition with
+# the arguments given to this `fake' function. The autoload function defined
+# by the file and the file itself *must* be named identically.
+#
+
+_aload()
+{
+ eval $1 '() { . '$2' ; '$1' "$@" ; return $? ; }'
+ _autoload_addlist "$1"
+}
+
+_autoload_addlist()
+{
+ local i=0
+
+ while (( i < $_aindex )); do
+ case "${_AUTOLOADS[i]}" in
+ "$1") return 1 ;;
+ esac
+ (( i += 1 ))
+ done
+ _AUTOLOADS[_aindex]="$1"
+ (( _aindex += 1 ))
+ return 0
+}
+
+_autoload_dump()
+{
+ local func
+
+ for func in ${_AUTOLOADS[@]}; do
+ [ -n "$1" ] && echo -n "autoload "
+ echo "$func"
+ done
+}
+
+# Remove $1 from the list of autoloaded functions
+_autoload_remove_one()
+{
+ local i=0 nnl=0
+ local -a nlist
+
+ while (( i < _aindex )); do
+ case "${_AUTOLOADS[i]}" in
+ "$1") ;;
+ *) nlist[nnl]="${_AUTOLOADS[i]}" ; (( nnl += 1 ));;
+ esac
+ (( i += 1 ))
+ done
+ unset _AUTOLOADS _aindex
+ eval _AUTOLOADS=( ${nlist[@]} )
+ _aindex=$nnl
+}
+
+# Remove all function arguments from the list of autoloaded functions
+_autoload_remove()
+{
+ local func i es=0
+
+ # first unset the autoloaded functions
+ for func; do
+ i=0
+ while (( i < _aindex )); do
+ case "${_AUTOLOADS[i]}" in
+ "$func") unset -f $func ; break ;;
+ esac
+ (( i += 1 ))
+ done
+ if (( i == _aindex )); then
+ echo "autoload: $func: not an autoloaded function" >&2
+ es=1
+ fi
+ done
+
+ # then rebuild the list of autoloaded functions
+ for func ; do
+ _autoload_remove_one "$func"
+ done
+
+ return $es
+}
+
+#
+# Search $FPATH for a file the same name as the function given as $1, and
+# autoload the function from that file. There is no default $FPATH.
+#
+
+autoload()
+{
+ local -a fp
+ local _autoload_unset nfp i
+
+ if (( $# == 0 )) ; then
+ _autoload_dump
+ return 0
+ fi
+
+ OPTIND=1
+ while getopts pu opt
+ do
+ case "$opt" in
+ p) _autoload_dump printable; return 0;;
+ u) _autoload_unset=y ;;
+ *) echo "autoload: usage: autoload [-pu] [function ...]" >&2
+ return 1 ;;
+ esac
+ done
+
+ shift $(( $OPTIND - 1 ))
+
+ if [ -n "$_autoload_unset" ]; then
+ _autoload_remove "$@"
+ return $?
+ fi
+
+ #
+ # If there is no $FPATH, there is no work to be done
+ #
+
+ if [ -z "$FPATH" ] ; then
+ echo "autoload: FPATH not set or null" >&2
+ return 1
+ fi
+
+ #
+ # This treats FPATH exactly like PATH: a null field anywhere in the
+ # FPATH is treated the same as the current directory.
+ #
+ # This turns $FPATH into an array, substituting `.' for `'
+ #
+ eval fp=( $(
+ IFS=':'
+ set -- ${FPATH}
+ for p in "$@" ; do echo -n "${p:-.} "; done
+ )
+ )
+
+ nfp=${#fp[@]}
+
+ for FUNC ; do
+ i=0;
+ while (( i < nfp )) ; do
+ if [ -f ${fp[i]}/$FUNC ] ; then
+ break # found it!
+ fi
+ (( i += 1 ))
+ done
+
+ if (( i == nfp )) ; then
+ echo "autoload: $FUNC: autoload function not found" >&2
+ es=1
+ continue
+ fi
+
+# echo auto-loading $FUNC from ${fp[i]}/$FUNC
+ _aload $FUNC ${fp[i]}/$FUNC
+ es=0
+ done
+
+ return $es
+}
diff --git a/examples/functions/autoload.v3 b/examples/functions/autoload.v3
new file mode 100644
index 0000000..b1e5dfe
--- /dev/null
+++ b/examples/functions/autoload.v3
@@ -0,0 +1,125 @@
+#From: Mark Kennedy <mark.t.kennedy@gmail.com> (<mtk@ny.ubs.com>)
+#Message-ID: <35E2B899.63A02DF5@ny.ubs.com>
+#Date: Tue, 25 Aug 1998 09:14:01 -0400
+#To: chet@nike.ins.cwru.edu
+#Subject: a newer version of the ksh-style 'autoload'
+
+#enclosed you'll find 'autoload.v3', a version of the autoloader
+#that emulates the ksh semantics of delaying the resolution (and loading) of the function
+#until its first use. i took the liberty of simplifying the code a bit although it still uses the
+#same functional breakdown. i recently went through the exercise of converting
+#my ksh-based environment to bash (a very, very pleasant experience)
+#and this popped out.
+
+# the psuedo-ksh autoloader.
+
+# The first cut of this was by Bill Trost, trost@reed.bitnet.
+# The second cut came from Chet Ramey, chet@ins.CWRU.Edu
+# The third cut came from Mark Kennedy, mtk@ny.ubs.com. 1998/08/25
+
+unset _AUTOLOADS
+
+_aload()
+{
+ local func
+ for func; do
+ eval $func '()
+ {
+ local f=$(_autoload_resolve '$func')
+ if [[ $f ]]; then
+ . $f
+ '$func' "$@"
+ return $?
+ else
+ return 1
+ fi
+ }'
+ _autoload_addlist $func
+ done
+}
+
+_autoload_addlist()
+{
+ local func
+
+ for func in ${_AUTOLOADS[@]}; do
+ [[ $func = "$1" ]] && return
+ done
+
+ _AUTOLOADS[${#_AUTOLOADS[@]}]=$1
+}
+
+_autoload_dump()
+{
+ local func
+
+ for func in ${_AUTOLOADS[@]}; do
+ [[ $1 ]] && echo -n "autoload "
+ echo $func
+ done
+}
+
+_autoload_remove_one()
+{
+ local func
+ local -a NEW_AUTOLOADS
+
+ for func in ${_AUTOLOADS[@]}; do
+ [[ $func != "$1" ]] && NEW_AUTOLOADS[${#NEW_AUTOLOADS[@]}]=$func
+ done
+
+ _AUTOLOADS=( ${NEW_AUTOLOADS[@]} )
+}
+
+_autoload_remove()
+{
+ local victim func
+
+ for victim; do
+ for func in ${_AUTOLOADS[@]}; do
+ [[ $victim = "$func" ]] && unset -f $func && continue 2
+ done
+ echo "autoload: $func: not an autoloaded function" >&2
+ done
+
+ for func; do
+ _autoload_remove_one $func
+ done
+}
+
+_autoload_resolve()
+{
+ if [[ ! "$FPATH" ]]; then
+ echo "autoload: FPATH not set or null" >&2
+ return
+ fi
+
+ local p
+
+ for p in $( (IFS=':'; set -- ${FPATH}; echo "$@") ); do
+ p=${p:-.}
+ if [ -f $p/$1 ]; then echo $p/$1; return; fi
+ done
+
+ echo "autoload: $1: function source file not found" >&2
+}
+
+autoload()
+{
+ if (( $# == 0 )) ; then _autoload_dump; return; fi
+
+ local opt OPTIND
+
+ while getopts pu opt
+ do
+ case $opt in
+ p) _autoload_dump printable; return;;
+ u) shift $((OPTIND-1)); _autoload_remove "$@"; return;;
+ *) echo "autoload: usage: autoload [-pu] [function ...]" >&2; return;;
+ esac
+ done
+
+ shift $(($OPTIND-1))
+
+ _aload "$@"
+}
diff --git a/examples/functions/autoload.v4 b/examples/functions/autoload.v4
new file mode 100644
index 0000000..7f60563
--- /dev/null
+++ b/examples/functions/autoload.v4
@@ -0,0 +1,556 @@
+## -*- sh -*-
+
+# The psuedo-ksh autoloader.
+
+# How to use:
+# o One function per file.
+# o File and function name match exactly.
+# o File is located in a directory that is in FPATH.
+# o This script (autoload) must be sourced in as early as possible. This
+# implies that any code in this script should NOT rely on any library of local
+# or self-defined functions having already been loaded.
+# o autoload must be called for each function before the function can be used. If
+# autoloads are in directories where there are nothing but autoloads, then
+# 'autoload /path/to/files/*' suffices (but see options -a and -f).
+# o The call must be made in the current environment, not a subshell.
+# o The command line suffices as "current environment". If you have autoload
+# calls in a script, that script must be dotted into the process.
+
+# The first cut of this was by Bill Trost, trost@reed.bitnet.
+# The second cut came from Chet Ramey, chet@ins.CWRU.Edu
+# The third cut came from Mark Kennedy, mtk@ny.ubs.com. 1998/08/25
+# The fourth cut came from Matthew Persico, matthew.persico@gmail.com 2017/August
+
+autoload_calc_shimsize ()
+{
+ echo $((AUTOLOAD_SHIM_OVERHEAD + 3 * ${#1}))
+}
+
+_autoload_split_fpath ()
+{
+ (IFS=':'; set -- ${FPATH}; echo "$@")
+}
+
+_aload()
+{
+ local opt OPTIND
+ local doexport=0
+ local doreload=0
+ local doverbose=0
+ local doevalshim=0
+ local loadthese
+ local optimize=0
+ local loaded=0
+ local exported=0
+ local optimized=0
+ local summary=0
+ local dofpath=0
+ while getopts xrvla:oyf opt; do
+ case $opt in
+ x) doexport=1;;
+ r) doreload=1;;
+ v) doverbose=1;;
+ l) doevalshim=1;;
+ a) loadthese=$(find $OPTARG -maxdepth 1 -type f -printf '%f ');;
+ o) optimize=1;;
+ y) summary=1;;
+ f) loadthese=$(find $(_autoload_split_fpath) -maxdepth 1 -type f -printf '%f ');;
+ *) echo "_aload: usage: _aload [-xrvlyf] [-a dir] [function ...]" >&2; return;;
+ esac
+ done
+
+ shift $(($OPTIND-1))
+
+ [ -z "$loadthese" ] && loadthese="$@"
+
+ local func
+ for func in $loadthese; do
+ local exists_fn
+ exists_fn=$(declare -F $func)
+ if [ -n "$exists_fn" ] && ((doreload==0)) && ((doevalshim==0))
+ then
+ if ((doverbose))
+ then
+ echo "autoload: function '$func' already exists"
+ fi
+ else
+ local andevaled=''
+ local andexported=''
+ local evalstat=0
+ local doshim=1
+ local funcfile
+ funcfile=$(_autoload_resolve $func)
+ if [[ $funcfile ]] ; then
+ ## The file was found for $func. Process it.
+
+ if ((optimize)); then
+ ## For the first function loaded, we will not know
+ ## AUTOLOAD_SHIM_OVERHEAD. We can only calculate it after
+ ## we have loaded one function.
+ if [[ $AUTOLOAD_SHIM_OVERHEAD ]]; then
+ local size=$(wc -c $funcfile| sed 's/ .*//')
+ local shimsize=$(autoload_calc_shimsize $func)
+ if (( size <= shimsize)); then
+ doshim=0
+ andevaled=', optimized'
+ ((optimized+=1))
+ fi
+ fi
+ fi
+
+ if ((doevalshim)); then
+ doshim=0
+ andevaled=', evaled'
+ fi
+
+ ## 'brand' as in branding a cow with a mark. We add a local
+ ## variable to each function we autoload so that we can tell
+ ## later on it is an autoloaded function without having to
+ ## maintain some bash array or hash that cannot be passed to
+ ## and used by subshells.
+ local brandtext
+ brandtext="eval \"\$(type $func | sed -e 1d -e 4ilocal\\ AUTOLOADED=\'$func\')\""
+ if ((doshim)); then
+ ## Don't bother trying to save space by shoving all the
+ ## eval text below onto one unreadable line; new lines will
+ ## be added at your semicolons and any indentation below
+ ## seems to be ignored anyway if you export the function;
+ ## look at its BASH_FUNCTION representation.
+ eval $func '()
+ {
+ local IS_SHIM="$func"
+ local file=$(_autoload_resolve '$func')
+ if [[ $file ]]
+ then
+ . $file
+ '$brandtext'
+ '$func' "$@"
+ return $?
+ else
+ return 1;
+ fi
+ }'
+ else
+ . $funcfile
+ eval "$brandtext"
+ fi
+ evalstat=$?
+ if((evalstat==0))
+ then
+ ((loaded+=1))
+ ((doexport)) && export -f $func && andexported=', exported' && ((exported+=1))
+ ((doverbose)) && echo "$func autoloaded${andexported}${andevaled}"
+ if [[ ! $AUTOLOAD_SHIM_OVERHEAD ]] && ((doshim)); then
+ ## ...we have just loaded the first function shim into
+ ## memory. Let's calc the AUTOLOAD_SHIM_OVERHEAD size
+ ## to use going forward. In theory, we could check
+ ## again here to see if we should optimize and source
+ ## in this function, now that we now the
+ ## AUTOLOAD_SHIM_OVERHEAD. In practice, it's not worth
+ ## duping that code or creating a function to do so for
+ ## one function.
+ AUTOLOAD_SHIM_OVERHEAD=$(type $func | grep -v -E "^$1 is a function" | sed "s/$func//g"| wc -c)
+ export AUTOLOAD_SHIM_OVERHEAD
+ fi
+ else
+ echo "$func failed to load" >&2
+ fi
+ fi
+ fi
+ done
+ ((summary)) && echo "autoload: loaded:$loaded exported:$exported optimized:$optimized overhead:$AUTOLOAD_SHIM_OVERHEAD bytes"
+}
+
+_autoload_dump()
+{
+ local opt OPTIND
+ local opt_p=''
+ local opt_s=''
+ while getopts ps opt
+ do
+ case $opt in
+ p ) opt_p=1;;
+ s ) opt_s=1;;
+ esac
+ done
+
+ shift $(($OPTIND-1))
+
+ local exported=''
+ local executed=''
+ local func
+ for func in $(declare | grep -E 'local\\{0,1} AUTOLOADED' | sed -e "s/.*AUTOLOADED=//" -e 's/\\//g' -e 's/[");]//g' -e "s/'//g")
+ do
+ if [ -n "$opt_p" ]; then echo -n "autoload "; fi
+ if [ -n "$opt_s" ]
+ then
+ exported=$(declare -F | grep -E "${func}$" | sed 's/declare -f\(x\{0,1\}\).*/\1/')
+ [ "$exported" = 'x' ] && exported=' exported' || exported=' not exported'
+ executed=$(type $func | grep 'local IS_SHIM')
+ [ -z "$executed" ] && executed=' executed' || executed=' not executed'
+ fi
+ echo "${func}${exported}${executed}"
+ done
+}
+
+_autoload_resolve()
+{
+ if [[ ! "$FPATH" ]]; then
+ echo "autoload: FPATH not set or null" >&2
+ return
+ fi
+
+ local p # for 'path'. The $() commands in the for loop split the FPATH
+ # string into its constituents so that each one may be processed.
+
+ for p in $( _autoload_split_fpath ); do
+ p=${p:-.}
+ if [ -f $p/$1 ]; then echo $p/$1; return; fi
+ done
+
+ echo "autoload: $1: function source file not found" >&2
+}
+
+_autoload_edit()
+{
+ [ -z "$EDITOR" ] && echo "Error: no EDITOR defined" && return 1
+ local toedit
+ local func
+ for func in "$@"
+ do
+ local file=$(_autoload_resolve $func)
+ if [[ $file ]]
+ then
+ toedit="$toedit $file"
+ else
+ echo "$funcname not found in FPATH funcfile. Skipping."
+ fi
+ done
+
+ [ -z "$toedit" ] && return 1
+
+ local timemarker=$(mktemp)
+
+ $EDITOR $toedit
+
+ local i
+ for i in $toedit
+ do
+ if [ $i -nt $timemarker ]
+ then
+ local f=$(basename $i)
+ echo Reloading $f
+ autoload -r $f
+ fi
+ done
+}
+
+_autoload_page()
+{
+ [ -z "$PAGER" ] && echo "Error: no PAGER defined" && return 1
+ local topage
+ local func
+ for func in "$@"
+ do
+ local file=$(_autoload_resolve $func)
+ if [[ $file ]]
+ then
+ topage="$topage $file"
+ else
+ echo "$funcname not found in FPATH funcfile. Skipping."
+ fi
+ done
+
+ [ -z "$topage" ] && return 1
+
+ $PAGER $topage
+}
+
+_autoload_remove()
+{
+ unset -f "$@"
+}
+
+_autoload_help()
+{
+ cat <<EOH
+NAME
+ autoload
+
+SYNOPSIS
+ autoload [-ps]
+ autoload [-xuremloyv] [function ...]
+ autoload -a directory [-oyv]
+ autoload -f [-oyv]
+ autoload [-h]
+
+ autoreload [function ...]
+
+DESCRIPTION
+
+ An implementation of the 'autoload' functionality built into other
+ shells, of which 'ksh' is the most prominent. It allows for a keeping
+ the process environment small by loading small 'shim' functions into
+ memory that will, on first call, load the full text of the given
+ function and run it. Subsequent calls to the function just run the
+ function.
+
+ 'autoreload' is a synonym for 'autoload -r'. See below.
+
+USAGE
+
+ o Each function to be autoloaded should be defined in a single file,
+ named exactly the same as the function.
+
+ o In order to avoid side effects, do NOT put code other than the
+ function definition in the file. Unless of course you want to do some
+ one-time initialization. But beware that if you reload the function
+ for any reason, you will rerun the initialization code. Make sure
+ your initialization is re-entrant. Or, better yet,
+
+ *** do NOT put code other than the function definition in the file ***
+
+ o These function definition files should be placed in a directory that
+ is in the FPATH environment variable. Subdirectories are NOT scanned.
+
+ o The autoload script should be sourced into the current process as
+ early as possible in process start up. See NOTES below for
+ suggestions.
+
+ o The calls to the autoload function must be made in the current
+ process. If your calls are in their own script, that script must be
+ sourced in. Command line invocations are also sufficient. (But see
+ '-l' below.)
+
+ o The first time the function is called, the shim function that was
+ created by the 'autoload' call is what is executed. This function
+ then goes and finds the appropriate file in FPATH, sources it in and
+ then calls the actual function with any arguments you just passed in
+ to the shim function. Subsequent calls just run the function.
+
+OPTIONS
+
+ -a Autoload (a)ll the functions found in the given directory.
+
+ -f Autoload all the functions found in all the directories on the
+ FPATH.
+
+ -p Print all the autoloaded functions.
+
+ -s Print all the autoloaded functions and add their export status.
+
+ -x Export the specified functions to the environment for use in
+ subshells.
+
+ -u Unset the function, so it can be reloaded.
+
+ -r Reload the shims of the specified functions, even if the functions
+ have been already been executed. This will allow you to modify the
+ functions' source and have the new version executed next time the
+ function is called.
+
+ It would be very easy to modify a function's script, run the
+ function and scratch your head for a long time trying to figure out
+ why your changes are not being executed. That's why we provide the
+ '-e' flag described below for modifications.
+
+ Reloads, of course, only apply in the context of the current session
+ and any future subshell you start from the current session. Existing
+ sessions will need to have the same 'autoload -r' command run in
+ them.
+
+ -e Find the scripts in which the specified functions are defined and
+ start up \$EDITOR on those scripts. Reload the ones that were
+ modified when you exit \$EDITOR. (Note: If you use 'autoload -e foo'
+ to edit function 'foo', and then in your editor you separately load
+ up function 'bar', 'autoload' has no way of knowing that you edited
+ 'bar' and will NOT reload 'bar' for you.)
+
+ Reloads, of course, only apply in the context of the current session
+ and any future subshell you start from the current session. Existing
+ sessions will need to have the same 'autoload -r' command run in
+ them.
+
+ -m Find the scripts in which the specified functions are defined and
+ run \$PAGER on them ('m' is for 'more', because 'p' (page) and 'l'
+ (load) are already used as options in 'autoload').
+
+ -l When autoloading a function, eval the shim immediately in order to
+ load the true function code. See "Using '-l'" in the NOTES below for
+ details.
+
+ -o Optimize. When autoloading, take the time to execute
+
+ 'theCharCount=\$(wc -c \$theFuncFile)'
+
+ for each function and
+
+ if \$theCharCount < \$AUTOLOAD_SHIM_OVERHEAD
+
+ don't shim it, just eval directly.
+
+ -y Summar(y). Print the number of loaded, exported and optimized
+ functions.
+
+ -v Turns up the chattiness.
+
+NOTES
+
+ o Calling 'autoload' on a function that already exists (either shimmed
+ or expanded) silently ignores the request to load the shim unless it
+ has been previously removed (-u) or you force the reload (-r).
+
+ o Changing and reloading a function that has been exported does not
+ require it be re-exported; the modifications will appear in
+ subsequent subshells.
+
+ o Using '-1'
+
+ If you are running under set -x and/or set -v, you may see that the
+ shim does not appear to "work"; instead of seeing the shim first and
+ the real code subsequently, you may see the shim evaluated multiple
+ times.
+
+ This may not be an error; review your code. What is most likely
+ happening is that you are calling the function in subshells via
+ backticks or $(), or in a script that is not being sourced into the
+ current environment. If you have not previously called the function
+ in question at your command line or in a script that was sourced into
+ the current environment, then the various subshells are going to
+ encounter the shim and replace with the real code before executing.
+
+ Remember, however, that environment modifications that occur in a
+ subshell are NOT propagated back to the calling shell or over to any
+ sibling shells. So, if you call an autoloaded function in a very
+ tight loop of very many subshells, you may want to make an 'autoload
+ -l' call before you start your loop. '-l' will instruct 'autoload' to
+ bypass the shim creation and just source in the function's file
+ directly. For a few calls, the overhead of repeatedly running the
+ shim is not expensive, but in a tight loop, it might be. Caveat
+ Programer.
+
+ o Although the number of functions in the environment does not change
+ by using 'autoload', the amount of memory they take up can be greatly
+ reduced, depending on the size of your functions. If you have a lot
+ of small functions, then it is possible that the shim text will be
+ larger than your actual functions, rendering the memory savings moot.
+
+ 'small' in this case can be determined by calling the function
+ 'autoload_calc_shimsize' with the name of the function to determine
+ its shim size.
+
+ o In order to support the -p and -s options, we need a way to determine
+ if a function 'func' has been autoloaded or if it was loaded
+ diredctly. In order to do that, we modify the function's code by
+ adding the text
+
+ local AUTOLOADED='func';
+
+ to the shim and to the actual function text, just after the opening
+ brace. Then supporting -p and -s is just a matter of grepping through
+ all the function text in memory. Even though grepping through the
+ environment may not be the most efficient way to support this, it is
+ the simplest to implement for -p and -s operations that are not
+ heavily used.
+
+ As a consquence of this (and other reasons), the AUTOLOAD* namespace
+ is reserved for autoloading. Make sure you check any functions that
+ you bring under autoload for use of variables or functions that start
+ with AUTOLOAD and change them.
+
+ o The easiest way to load shims for all functions on the FPATH is to run
+
+ autoload -f -x
+
+ in the profile that gets run for login shells.
+
+ When called in the profile of a login shell where no definitions
+ exist, -f will load all functions it can find on FPATH and -x will
+ export all of those functions to be available in subshells when this
+ is called in a login shell. Using this option will relieve you of the
+ need to call 'autoload' after Every Single Function Definition, nor
+ will you need to call it in subshells.
+
+ The only thing left to do is to load up the autoload function itself
+ and its helper functions. That needs to happen in your profile:
+
+ export FPATH=~/functions # or wherever you stash them
+ if [ -z $(declare -F autoload) ]
+ then
+ . ~/bin/autoload # or wherever you've put it
+ fi
+
+ The 'if' statement is used to make sure we don't reload autoload
+ needlessly. Sourcing in the autoload script loads the 'autoload'
+ function and all of its support functions. Additionally, we export
+ all of these functions so that they are available in subshells; you
+ do not have to re-source the autoload file in '.bashrc'.
+
+ o Even with all of these shenanigans, you will find cases where no
+ matter how hard you try, your autoloaded functions will be
+ unavailable to you, even if you run 'autoload -x -f'. The typical
+ condition for this is starting up not a subshell, but a brand new
+ DIFFERENT shell. And the typical example of this is git extensions.
+
+ At the time of this writing, git extensions work by taking a command
+ 'git foo' and looking for a file 'git-foo' on the path. 'git' then
+ executes 'git-foo' in a new shell - it executes your command in
+ /bin/sh. That's not a subshell of your process. It will not get your
+ exported shell functions. Ballgame over.
+
+ If you find that you want your functions to be available in such
+ circumstances, convert them back to plain old scripts, make sure they
+ are 'sh' compliant and take the read/parse hit every time they are
+ run.
+
+EOH
+}
+
+autoload()
+{
+ if (( $# == 0 )) ; then _autoload_dump; return; fi
+
+ local opt OPTIND OPTARG
+ local passthru
+ local dumpopt
+ while getopts psuema:yxrvlohf opt
+ do
+ case $opt in
+ p|s) dumpopt="$dumpopt -${opt}";;
+ u) shift $((OPTIND-1)); _autoload_remove "$@"; return;;
+ e) shift $((OPTIND-1)); _autoload_edit "$@"; return;;
+ m) shift $((OPTIND-1)); _autoload_page "$@"; return;;
+ x|r|v|l|y|f|o) passthru="$passthru -$opt";;
+ a) passthru="$passthru -$opt $OPTARG";;
+ h) _autoload_help; return;;
+ *) echo "autoload: usage: autoload [-puUx] [function ...]" >&2; return;;
+ esac
+ done
+
+ shift $(($OPTIND-1))
+ if [ -n "$dumpopt" ]
+ then
+ _autoload_dump $dumpopt
+ else
+ _aload $passthru "$@"
+ fi
+}
+
+autoreload ()
+{
+ autoload -r "$@"
+}
+
+## When we source in autoload, we export (but NOT autoload) the autoload
+## functions so that they are available in subshells and you don't have to
+## source in the autoload file in subshells.
+export -f _aload \
+ _autoload_dump \
+ _autoload_edit \
+ _autoload_help \
+ _autoload_page \
+ _autoload_resolve \
+ _autoload_split_fpath \
+ autoload \
+ autoload_calc_shimsize \
+ autoreload
diff --git a/examples/functions/autoload.v4.t b/examples/functions/autoload.v4.t
new file mode 100644
index 0000000..6d35d14
--- /dev/null
+++ b/examples/functions/autoload.v4.t
@@ -0,0 +1,184 @@
+#!/bin/bash
+
+workdir=$(mktemp -d)
+
+cp autoload $workdir
+
+cd $workdir
+pwd
+
+. ./autoload
+
+funclist='ALTEST_func1 ALTEST_funcexport ALTEST_funcu'
+for funcname in $funclist; do
+ cat <<EOFFUNC > $funcname
+$funcname ()
+{
+echo this is $funcname
+
+}
+EOFFUNC
+
+done
+
+export FPATH=$workdir
+
+autoload ALTEST_func1 ALTEST_funcu
+autoload -x ALTEST_funcexport
+
+ok=0
+failed=0
+
+for funcname in $funclist; do
+
+ testname="$funcname loaded"
+ got=$(type $funcname 2>&1)
+ if [[ $got =~ "$funcname: not found" ]]; then
+ echo "## Failed $testname"
+ ((failed+=1))
+ else
+ echo "ok - $testname"
+ ((ok+=1))
+
+ testname="$funcname is a shim"
+ if [[ ! $got =~ "IS_SHIM" ]]; then
+ echo "## Failed $testname"
+ ((failed+=1))
+ else
+ echo "ok - $testname"
+ ((ok+=1))
+
+ testname="$funcname shim executed"
+ $funcname > /dev/null
+ got=$(type $funcname 2>&1)
+ if [[ $got =~ "IS_SHIM" ]]; then
+ echo "## Failed $testname"
+ ((failed+=1))
+ else
+ echo "ok - $testname"
+ ((ok+=1))
+ fi
+ fi
+ fi
+done
+
+funcname=ALTEST_func1
+testname="$funcname shim reloaded"
+autoload -r $funcname
+got=$(type $funcname 2>&1)
+if [[ ! $got =~ "IS_SHIM" ]]; then
+ echo "## Failed $testname"
+ ((failed+=1))
+else
+ echo "ok - $testname"
+ ((ok+=1))
+fi
+
+funcname=ALTEST_funcu
+testname="$funcname shim unloaded"
+autoload -u $funcname
+got=$(type $funcname 2>&1)
+if [[ ! $got =~ "$funcname: not found" ]]; then
+ echo "## Failed $testname"
+ ((failed+=1))
+else
+ echo "ok - $testname"
+ ((ok+=1))
+fi
+
+testname="autoload -p"
+got=$(autoload -p | grep ALTEST)
+if [[ ! $got =~ "autoload ALTEST_func1" ]] || \
+ [[ ! $got =~ "autoload ALTEST_funcexport" ]] ; then
+echo "## Failed $testname"
+ ((failed+=1))
+else
+ echo "ok - $testname"
+ ((ok+=1))
+fi
+
+testname="autoload -s"
+echo "Executing $testname, could take a long time..."
+got=$(autoload -s | grep ALTEST)
+if [[ ! $got =~ "ALTEST_func1 not exported not executed" ]] || \
+ [[ ! $got =~ "ALTEST_funcexport exported executed" ]] ; then
+ echo "## Failed $testname"
+ echo "## got: $got"
+ ((failed+=1))
+else
+ echo "ok - $testname"
+ ((ok+=1))
+fi
+
+testname="autoload -r -a $FPATH"
+autoload -r -a $FPATH
+localfailed=0
+localok=0
+for funcname in $funclist; do
+ got=$(type $funcname 2>&1)
+ if [[ $got =~ "$funcname: not found" ]]; then
+ echo "## Failed $testname - $funcname"
+ ((localfailed+=1))
+ else
+ ((localok+=1))
+ if [[ ! $got =~ "IS_SHIM" ]]; then
+ ((localfailed+=1))
+ else
+ ((localok+=1))
+ fi
+ fi
+done
+if ((localfailed==0)); then
+ echo "ok - $testname"
+ ((ok+=1))
+else
+ ((failed+=1))
+fi
+
+testname="autoload -u $funclist"
+autoload -u $funclist
+localfailed=0
+localok=0
+for funcname in $funclist; do
+ got=$(type $funcname 2>&1)
+ if [[ ! $got =~ "$funcname: not found" ]]; then
+ echo "## Failed $testname - $funcname"
+ ((localfailed+=1))
+ else
+ ((localok+=1))
+ fi
+done
+if ((localfailed==0)); then
+ echo "ok - $testname"
+ ((ok+=1))
+else
+ ((failed+=1))
+fi
+
+testname="autoload -r -f"
+autoload -r -f
+localfailed=0
+localok=0
+for funcname in $funclist; do
+ got=$(type $funcname 2>&1)
+ if [[ $got =~ "$funcname: not found" ]]; then
+ echo "## Failed $testname - $funcname"
+ ((localfailed+=1))
+ else
+ ((localok+=1))
+ if [[ ! $got =~ "IS_SHIM" ]]; then
+ ((localfailed+=1))
+ else
+ ((localok+=1))
+ fi
+ fi
+done
+if ((localfailed==0)); then
+ echo "ok - $testname"
+ ((ok+=1))
+else
+ ((failed+=1))
+fi
+
+echo $ok passed, $failed failed
+exit $failed