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:
parent
797e07ff5e
commit
96fc5e800a
5 changed files with 195 additions and 23 deletions
|
@ -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
|
||||
|
|
4
Functions/Exceptions/.distfiles
Normal file
4
Functions/Exceptions/.distfiles
Normal file
|
@ -0,0 +1,4 @@
|
|||
DISTFILES_SRC='
|
||||
.distfiles
|
||||
catch throw
|
||||
'
|
41
Functions/Exceptions/catch
Normal file
41
Functions/Exceptions/catch
Normal 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 "$@"
|
30
Functions/Exceptions/throw
Normal file
30
Functions/Exceptions/throw
Normal 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
|
52
Src/zsh.mdd
52
Src/zsh.mdd
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue