1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-12-29 16:25:35 +01:00

Merge in workers/20812 for exception handling using "always" block.

This commit is contained in:
Paul Ackersviller 2007-04-08 01:35:42 +00:00
parent 797e07ff5e
commit 96fc5e800a
5 changed files with 195 additions and 23 deletions

View file

@ -13,6 +13,7 @@ startmenu()
menu(Utilities)
menu(Prompt Themes)
menu(ZLE Functions)
menu(Exception Handling)
menu(MIME Functions)
menu(Other Functions)
endmenu()
@ -345,7 +346,7 @@ normally call a theme's setup function directly.
)
enditem()
texinode(ZLE Functions)(MIME Functions)(Prompt Themes)(User Contributions)
texinode(ZLE Functions)(Exception Handling)(Prompt Themes)(User Contributions)
sect(ZLE Functions)
subsect(Widgets)
@ -1042,7 +1043,93 @@ whether the tt(widget) style is used.
)
enditem()
texinode(MIME Functions)(Other Functions)(ZLE Functions)(User Contributions)
texinode(Exception Handling)(MIME Functions)(ZLE Functions)(User Contributions)
sect(Exception Handling)
Two functions are provided to enable zsh to provide exception handling in a
form that should be familiar from other languages.
startitem()
findex(throw)
item(tt(throw) var(exception))(
The function tt(throw) throws the named var(exception). The name is
an arbitrary string and is only used by the tt(throw) and tt(catch)
functions. An exception is for the most part treated the same as a
shell error, i.e. an unhandled exception will cause the shell to abort all
processing in a function or script and to return to the top level in an
interative shell.
)
item(tt(catch) var(exception-pattern))(
The function tt(catch) returns status zero if an exception was thrown and
the pattern var(exception-pattern) matches its name. Otherwise it
returns status 1. var(exception-pattern) is a standard
shell pattern, respecting the current setting of the tt(EXTENDED_GLOB)
option. An alias tt(catch) is also defined to prevent the argument to the
function from matching filenames, so patterns may be used unquoted. Note
that as exceptions are not fundamentally different from other shell errors
it is possible to catch shell errors by using an empty string as the
exception name. The shell variable tt(CAUGHT) is set by tt(catch) to the
name of the exception caught. It is possible to rethrow an exception by
calling the tt(throw) function again once an exception has been caught.
findex(catch)
)
enditem()
The functions are designed to be used together with the tt(always) construct
described in
ifzman(zmanref(zshmisc))\
ifnzman(noderef(Complex Commands)). This is important as only this
construct provides the required support for exceptions. A typical example
is as follows.
example({
# "try" block
# ... nested code here calls "throw MyExcept"
} always {
# "always" block
if catch MyExcept; then
print "Caught exception MyExcept"
elif catch ''; then
print "Caught a shell error. Propagating..."
throw ''
fi
# Other exceptions are not handled but may be caught further
# up the call stack.
})
If all exceptions should be caught, the following idiom might be
preferable.
example({
# ... nested code here throws an exception
} always {
if catch *; then
case $CAUGHT in
LPAR()MyExcept+RPAR()
print "Caught my own exception"
;;
LPAR()*RPAR()
print "Caught some other exception"
;;
esac
fi
})
In common with exception handling in other languages, the exception may be
thrown by code deeply nested inside the `try' block. However, note that it
must be thrown inside the current shell, not in a subshell forked for a
pipline, parenthesised current-shell construct, or some form of
substitution.
The system internally uses the shell variable tt(EXCEPTION) to record the
name of the exception between throwing and catching. One drawback of this
scheme is that if the exception is not handled the variable tt(EXCEPTION)
remains set and may be incorrectly recognised as the name of an exception
if a shell error subsequently occurs. Adding tt(unset EXCEPTION) at the
start of the outermost layer of any code that uses exception handling will
eliminate this problem.
texinode(MIME Functions)(Other Functions)(Exception Handling)(User Contributions)
sect(MIME Functions)
Three functions are available to provide handling of files recognised by

View file

@ -0,0 +1,4 @@
DISTFILES_SRC='
.distfiles
catch throw
'

View file

@ -0,0 +1,41 @@
# Catch an exception. Returns 0 if the exception in question was caught.
# The first argument gives the exception to catch, which may be a
# pattern.
# This must be within an always-block. A typical set of handlers looks
# like:
# {
# # try block; something here throws exceptions
# } always {
# if catch MyExcept; then
# # Handler code goes here.
# print Handling exception MyExcept
# elif catch *; then
# # This is the way to implement a catch-all.
# print Handling any other exception
# fi
# }
# As with other languages, exceptions do not need to be handled
# within an always block and may propagate to a handler further up the
# call chain.
#
# It is possible to throw an exception from within the handler by
# using "throw".
#
# The shell variable $CAUGHT is set to the last exception caught,
# which is useful if the argument to "catch" was a pattern.
#
# Use "function" keyword in case catch is already an alias.
function catch {
if [[ $TRY_BLOCK_ERROR -gt 0 && $EXCEPTION = ${~1} ]]; then
(( TRY_BLOCK_ERROR = 0 ))
typeset -g CAUGHT="$EXCEPTION"
unset EXCEPTION
return 0
fi
return 1
}
# Never use globbing with "catch".
alias catch="noglob catch"
catch "$@"

View file

@ -0,0 +1,30 @@
# Throw an exception.
# The first argument is a string giving the exception. Other arguments
# are ignored.
#
# This is designed to be called somewhere inside a "try-block", i.e.
# some code of the form:
# {
# # try-block
# } always {
# # always-block
# }
# although as normal with exceptions it might be hidden deep inside
# other code. Note, however, that it must be code running within the
# current shell; with shells, unlike other languages, it is quite easy
# to miss points at which the shell forks.
#
# If there is nothing to catch an exception, this behaves like any
# other shell error, aborting to the command prompt or abandoning a
# script.
# The following must not be local.
typeset -g EXCEPTION="$1"
readonly THROW
if (( TRY_BLOCK_ERROR == 0 )); then
# We are throwing an exception from the middle of an always-block.
# We can do this by restoring the error status from the try-block.
(( TRY_BLOCK_ERROR = 1 ))
fi
# Raise an error, but don't show an error message.
THROW= 2>/dev/null

View file

@ -1,4 +1,8 @@
name=zsh/main
link=static
load=yes
# load=static should replace use of alwayslink
functions='Functions/Exceptions/* Functions/Misc/* Functions/MIME/* Functions/Prompts/*'
nozshdep=1
alwayslink=1
@ -8,7 +12,7 @@ alwayslink=1
objects="builtin.o compat.o cond.o exec.o glob.o hashtable.o \
hist.o init.o input.o jobs.o lex.o linklist.o loop.o math.o \
mem.o module.o options.o params.o parse.o pattern.o prompt.o signals.o \
signames.o subst.o text.o utils.o watch.o"
signames.o string.o subst.o text.o utils.o watch.o"
headers="../config.h system.h zsh.h sigcount.h signals.h \
prototypes.h hashtable.h ztype.h"
@ -27,7 +31,7 @@ sigcount.h: signames.c
init.o: bltinmods.list zshpaths.h zshxmods.h
params.o: version.h
init.o params.o: version.h
version.h: $(sdir_top)/Config/version.mk
echo '#define ZSH_VERSION "'$(VERSION)'"' > $@
@ -41,10 +45,13 @@ zshpaths.h: Makemod $(CONFIG_INCS)
echo '#define FPATH_DIR "'$(fndir)'"' >> zshpaths.h.tmp; \
if test x$(FUNCTIONS_SUBDIRS) != x -a \
x$(FUNCTIONS_SUBDIRS) != xno; then \
fpath_tmp="`for f in $$FUNCTIONS_INSTALL; do \
echo $$f | sed s%/.*%%; \
done | sort | uniq`"; \
fpath_tmp="`echo $$fpath_tmp | sed 's/ /\", \"/g'`"; \
fpath_tmp="`grep ' functions=.' \
$(dir_top)/config.modules | sed -e '/^#/d' -e '/ link=no/d' \
-e 's/^.* functions=//'`"; \
fpath_tmp=`for f in $$fpath_tmp; do \
echo $$f | sed -e 's%^Functions/%%' -e 's%/[^/]*$$%%' -e 's%/\*%%'; \
done | sort | uniq`; \
fpath_tmp=`echo $$fpath_tmp | sed 's/ /\", \"/g'`; \
echo "#define FPATH_SUBDIRS { \"$$fpath_tmp\" }" \
>>zshpaths.h.tmp; \
fi; \
@ -57,24 +64,27 @@ zshpaths.h: Makemod $(CONFIG_INCS)
echo "Updated \`zshpaths.h'." ; \
fi
bltinmods.list: modules.stamp modules-bltin xmods.conf mkbltnmlst.sh
srcdir='$(sdir)' MODBINS='modules-bltin' \
XMODCF='$(sdir)/xmods.conf' $(SHELL) $(sdir)/mkbltnmlst.sh $@
bltinmods.list: modules.stamp mkbltnmlst.sh $(dir_top)/config.modules
srcdir='$(sdir)' CFMOD='$(dir_top)/config.modules' \
$(SHELL) $(sdir)/mkbltnmlst.sh $@
zshxmods.h: modules-bltin xmods.conf
zshxmods.h: $(dir_top)/config.modules
@echo "Creating \`$@'."
@( \
binmods=`sed 's/^/ /;s/$$/ /' modules-bltin`; \
for mod in `sed 's/^.* //' $(sdir_src)/xmods.conf`; do \
q_mod=`echo $$mod | sed 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'`; \
case $$binmods in \
*" $$mod "*) \
echo "#define LINKED_XMOD_$$q_mod 1" ;; \
*) echo "#ifdef DYNAMIC"; \
echo "# define UNLINKED_XMOD_$$q_mod 1"; \
echo "#endif" ;; \
esac; \
done \
for q_mod in `grep ' load=yes' $(dir_top)/config.modules | \
grep ' link=static' | sed -e '/^#/d' -e 's/ .*//' \
-e 's/^name=//' -e 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'`; do \
test x$q_mod = xzshQsmain && continue; \
echo "#define LINKED_XMOD_$$q_mod 1"; \
done; \
for q_mod in `grep ' load=yes' $(dir_top)/config.modules | \
grep ' link=dynamic' | sed -e '/^#/d' -e 's/ .*//' \
-e 's/^name=//' -e 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'`; do \
test x$q_mod = x && continue; \
echo "#ifdef DYNAMIC"; \
echo "# define UNLINKED_XMOD_$$q_mod 1"; \
echo "#endif"; \
done; \
) > $@
clean-here: clean.zsh