[comp.sources.misc] v02i093: Autoloading shell scripts as functions

john@basser.cs.su.oz.AU (John Mackin) (04/13/88)

comp.sources.misc: Volume 2, Issue 93
Submitted-By: John Mackin <john@basser.cs.su.oz.AU>
Archive-Name: autoload

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by john on Wed Apr 13 07:08:49 EST 1988
# Contents:  README autoload which
 
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'
@		Autoloading Shell Scripts as Functions

@	Concept and Implementation by (in alphabetical order):

@		John Mackin <john@basser.cs.su.oz.AU>
@		Boyd Roberts <boyd@basser.cs.su.oz.AU>


If you don't have a shell that supports functions, don't bother with
this.  If you do, well, we developed this under the Rob Pike (Eighth
Edition) shell, but we tested it on System V (seems to work as long
as you don't try to export any functions) and under ksh (seems like
it might work, we aren't sure though).  Caveat emptor, it's mainly the
idea that matters, we're sure you can make it work on your implementation
if you want to.

Suppose you have a shell function that you don't often use, that must
be a function and not a script (maybe it sets environment variables).
Or just say you have some big functions that you would like to have
around when you want them, but that don't clutter your environment
when you're not using them (for those that can export functions, that
is).  Then this code is for you.  Originally inspired by the Franz
Lisp `autoload' mechanism, this allows you to write your functions
as scripts, and have them autoloaded as functions into the calling
shell the first time they are executed.

For example, if $HOME/bin was in your path, and $HOME/bin/foo contained:

@	#!/bin/sh

@	echo This is foo.

Then if you included our autoload functions in your .profile, along
with a call to autoload:

@	autoload foo

foo would originally get a function definition that would cause its
autoloading if it were ever used, and after it was first used it would
be defined as:

@	foo()
@	{
@		echo This is foo.
@	}

We've included a copy of `which' after Kernighan & Pike (`The UNIX
Programming Environment') so that this can be 100% self-contained.  Feel
free to use a shell builtin instead if you have one that works (we
do, almost...).

One `gotcha' is that if you autoload a script that uses here documents
(that is, `<<') you may be surprised by the result.  It works on our
shell, though; well, sort of.  I have no idea what other shells will
do with it.

Another `gotcha' is that you shouldn't blindly autoload any script;
rather, it requires a little bit of pre-planning.  In particular,
setting environment variables cuts both ways.  What's great for
a cutie that changes $TERM based on what kind of terminal your
window is emulating is no fun at all if the script (and hence
the function) changes PATH, or worse, IFS.

Have fun!

John Mackin, Basser Department of Computer Science,
@	     University of Sydney, Sydney, Australia

john@basser.oz.AU (john%basser.oz.AU@UUNET.UU.NET)
{uunet,mcvax,ukc,nttlab}!munnari!basser.oz!john
@//E*O*F README//
chmod u=rw,g=r,o=r README
echo x - autoload
sed 's/^@//' > "autoload" <<'@//E*O*F autoload//'
autoload()
{
@	for i
@	do
@		eval "$i()
@		{
@			loadit $i &&
@			$i "'"$@"'"
@		}
@		export $i
@		"
@	done
}

loadit()
{
@	if cmd=`which $1`
@	then
@		eval "$1()
@		{
@			`sed 's/\([^A-Za-z0-9_]*\)exec[ 	]/\1/
@			      s/\([^A-Za-z0-9_]*\)exit[ 	]/\1return /
@			      s/\([^A-Za-z0-9_]*\)exit$/\1return/
@			      s/\([^A-Za-z0-9_]*\)trap[ 	]/\1: /' $cmd`
@		}
@		"
@	else
@		echo $1: not found >&2
@		return 1
@	fi
}

export autoload loadit
@//E*O*F autoload//
chmod u=rw,g=r,o=r autoload
echo x - which
sed 's/^@//' > "which" <<'@//E*O*F which//'
# which - which command will execute?

oldpath=$PATH
PATH=/bin:/usr/bin

case $# in

@    1)	;;
@    *)	echo usage: `basename $0` command-name >&2; exit 2 ;;

esac

for i in `echo $oldpath | sed 's/^:/.:/
@			       s/::/:.:/g
@			       s/:$/:./
@			       s/:/ /g'`
do
@    if test -f $i/$1
@    then
@	echo $i/$1
@	exit 0
@    fi
done

exit 1
@//E*O*F which//
chmod u=rwx,g=rx,o=rx which
exit 0