#
# 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.bitnet
#
# 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 
# containg 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
}
