1#
2# An almost ksh-compatible `autoload'.  A function declared as `autoload' will
3# be read in from a file the same name as the function found by searching the
4# $FPATH (which works the same as $PATH), then that definition will be run.
5#
6# To do this without source support, we define a dummy function that, when
7# executed, will load the file (thereby re-defining the function), then 
8# execute that newly-redefined function with the original arguments.
9#
10# It's not identical to ksh because ksh apparently does lazy evaluation
11# and looks for the file to load from only when the function is referenced.
12# This one requires that the file exist when the function is declared as
13# `autoload'.
14#
15# usage: autoload [-pu] [func ...]
16#
17# options:
18#	-p	print in a format that can be reused as input
19#	-u	unset each function and remove it from the autoload list
20#
21# The first cut of this was by Bill Trost, trost@reed.edu
22#
23# Chet Ramey
24# chet@ins.CWRU.Edu
25
26unset _AUTOLOADS
27_aindex=0
28
29#
30# Declare a function ($1) to be autoloaded from a file ($2) when it is first
31# called.  This defines a `temporary' function that will `.' the file 
32# containg the real function definition, then execute that new definition with
33# the arguments given to this `fake' function.  The autoload function defined
34# by the file and the file itself *must* be named identically.
35#
36
37_aload()
38{
39	eval $1 '() {  . '$2' ; '$1' "$@" ; return $? ; }'
40	_autoload_addlist "$1"
41}
42
43_autoload_addlist()
44{
45	local i=0
46
47	while (( i < $_aindex )); do
48		case "${_AUTOLOADS[i]}" in
49		"$1")	return 1 ;;
50		esac
51		(( i += 1 ))
52	done
53	_AUTOLOADS[_aindex]="$1"
54	(( _aindex += 1 ))
55	return 0
56}
57
58_autoload_dump()
59{
60	local func
61
62	for func in ${_AUTOLOADS[@]}; do
63		[ -n "$1" ] && echo -n "autoload "
64		echo "$func"
65	done
66}
67
68# Remove $1 from the list of autoloaded functions
69_autoload_remove_one()
70{
71	local i=0 nnl=0
72	local -a nlist
73
74	while (( i < _aindex )); do
75		case "${_AUTOLOADS[i]}" in
76		"$1")	;;
77		*)	nlist[nnl]="${_AUTOLOADS[i]}" ; (( nnl += 1 ));;
78		esac
79		(( i += 1 ))
80	done
81	unset _AUTOLOADS _aindex
82	eval _AUTOLOADS=( ${nlist[@]} )
83	_aindex=$nnl
84}
85
86# Remove all function arguments from the list of autoloaded functions
87_autoload_remove()
88{
89	local func i es=0
90	
91	# first unset the autoloaded functions
92	for func; do
93		i=0
94		while (( i < _aindex )); do
95			case "${_AUTOLOADS[i]}" in
96			"$func")	unset -f $func ; break ;;
97			esac
98			(( i += 1 ))
99		done
100		if (( i == _aindex )); then
101			echo "autoload: $func: not an autoloaded function" >&2
102			es=1
103		fi
104	done
105
106	# then rebuild the list of autoloaded functions
107	for func ; do
108		_autoload_remove_one "$func"
109	done
110
111	return $es
112}
113
114#
115# Search $FPATH for a file the same name as the function given as $1, and
116# autoload the function from that file.  There is no default $FPATH.
117#
118
119autoload()
120{
121	local -a fp
122	local _autoload_unset nfp i
123
124	if (( $# == 0 )) ; then
125		_autoload_dump
126		return 0
127	fi
128
129	OPTIND=1
130	while getopts pu opt
131	do
132		case "$opt" in
133		p)	_autoload_dump printable; return 0;;
134		u)	_autoload_unset=y ;;
135		*)	echo "autoload: usage: autoload [-pu] [function ...]" >&2
136			return 1 ;;
137		esac
138	done
139
140	shift $(( $OPTIND - 1 ))
141
142	if [ -n "$_autoload_unset" ]; then
143		_autoload_remove "$@"
144		return $?
145	fi
146
147	#
148	# If there is no $FPATH, there is no work to be done
149	#
150
151	if [ -z "$FPATH" ] ; then
152		echo "autoload: FPATH not set or null" >&2
153		return 1
154	fi
155
156	#
157	# This treats FPATH exactly like PATH: a null field anywhere in the
158	# FPATH is treated the same as the current directory.
159	#
160	# This turns $FPATH into an array, substituting `.' for `'
161	#
162	eval fp=( $(
163			IFS=':'
164			set -- ${FPATH}
165			for p in "$@" ; do echo -n "${p:-.} "; done
166		   )
167		)
168
169	nfp=${#fp[@]}
170
171	for FUNC ; do
172		i=0;
173		while (( i < nfp )) ; do
174			if [ -f ${fp[i]}/$FUNC ] ; then
175				break			# found it! 
176			fi
177			(( i += 1 ))
178		done
179
180		if (( i == nfp )) ; then
181			echo "autoload: $FUNC: autoload function not found" >&2
182			es=1
183			continue
184		fi
185
186#		echo auto-loading $FUNC from ${fp[i]}/$FUNC
187		_aload $FUNC ${fp[i]}/$FUNC
188		es=0
189	done
190
191	return $es
192}
193