mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-01-01 05:16:05 +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(Utilities)
|
||||||
menu(Prompt Themes)
|
menu(Prompt Themes)
|
||||||
menu(ZLE Functions)
|
menu(ZLE Functions)
|
||||||
|
menu(Exception Handling)
|
||||||
menu(MIME Functions)
|
menu(MIME Functions)
|
||||||
menu(Other Functions)
|
menu(Other Functions)
|
||||||
endmenu()
|
endmenu()
|
||||||
|
@ -345,7 +346,7 @@ normally call a theme's setup function directly.
|
||||||
)
|
)
|
||||||
enditem()
|
enditem()
|
||||||
|
|
||||||
texinode(ZLE Functions)(MIME Functions)(Prompt Themes)(User Contributions)
|
texinode(ZLE Functions)(Exception Handling)(Prompt Themes)(User Contributions)
|
||||||
sect(ZLE Functions)
|
sect(ZLE Functions)
|
||||||
|
|
||||||
subsect(Widgets)
|
subsect(Widgets)
|
||||||
|
@ -1042,7 +1043,93 @@ whether the tt(widget) style is used.
|
||||||
)
|
)
|
||||||
enditem()
|
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)
|
sect(MIME Functions)
|
||||||
|
|
||||||
Three functions are available to provide handling of files recognised by
|
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
|
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
|
nozshdep=1
|
||||||
alwayslink=1
|
alwayslink=1
|
||||||
|
@ -8,7 +12,7 @@ alwayslink=1
|
||||||
objects="builtin.o compat.o cond.o exec.o glob.o hashtable.o \
|
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 \
|
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 \
|
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 \
|
headers="../config.h system.h zsh.h sigcount.h signals.h \
|
||||||
prototypes.h hashtable.h ztype.h"
|
prototypes.h hashtable.h ztype.h"
|
||||||
|
@ -27,7 +31,7 @@ sigcount.h: signames.c
|
||||||
|
|
||||||
init.o: bltinmods.list zshpaths.h zshxmods.h
|
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
|
version.h: $(sdir_top)/Config/version.mk
|
||||||
echo '#define ZSH_VERSION "'$(VERSION)'"' > $@
|
echo '#define ZSH_VERSION "'$(VERSION)'"' > $@
|
||||||
|
@ -41,10 +45,13 @@ zshpaths.h: Makemod $(CONFIG_INCS)
|
||||||
echo '#define FPATH_DIR "'$(fndir)'"' >> zshpaths.h.tmp; \
|
echo '#define FPATH_DIR "'$(fndir)'"' >> zshpaths.h.tmp; \
|
||||||
if test x$(FUNCTIONS_SUBDIRS) != x -a \
|
if test x$(FUNCTIONS_SUBDIRS) != x -a \
|
||||||
x$(FUNCTIONS_SUBDIRS) != xno; then \
|
x$(FUNCTIONS_SUBDIRS) != xno; then \
|
||||||
fpath_tmp="`for f in $$FUNCTIONS_INSTALL; do \
|
fpath_tmp="`grep ' functions=.' \
|
||||||
echo $$f | sed s%/.*%%; \
|
$(dir_top)/config.modules | sed -e '/^#/d' -e '/ link=no/d' \
|
||||||
done | sort | uniq`"; \
|
-e 's/^.* functions=//'`"; \
|
||||||
fpath_tmp="`echo $$fpath_tmp | sed 's/ /\", \"/g'`"; \
|
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\" }" \
|
echo "#define FPATH_SUBDIRS { \"$$fpath_tmp\" }" \
|
||||||
>>zshpaths.h.tmp; \
|
>>zshpaths.h.tmp; \
|
||||||
fi; \
|
fi; \
|
||||||
|
@ -57,24 +64,27 @@ zshpaths.h: Makemod $(CONFIG_INCS)
|
||||||
echo "Updated \`zshpaths.h'." ; \
|
echo "Updated \`zshpaths.h'." ; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
bltinmods.list: modules.stamp modules-bltin xmods.conf mkbltnmlst.sh
|
bltinmods.list: modules.stamp mkbltnmlst.sh $(dir_top)/config.modules
|
||||||
srcdir='$(sdir)' MODBINS='modules-bltin' \
|
srcdir='$(sdir)' CFMOD='$(dir_top)/config.modules' \
|
||||||
XMODCF='$(sdir)/xmods.conf' $(SHELL) $(sdir)/mkbltnmlst.sh $@
|
$(SHELL) $(sdir)/mkbltnmlst.sh $@
|
||||||
|
|
||||||
zshxmods.h: modules-bltin xmods.conf
|
zshxmods.h: $(dir_top)/config.modules
|
||||||
@echo "Creating \`$@'."
|
@echo "Creating \`$@'."
|
||||||
@( \
|
@( \
|
||||||
binmods=`sed 's/^/ /;s/$$/ /' modules-bltin`; \
|
for q_mod in `grep ' load=yes' $(dir_top)/config.modules | \
|
||||||
for mod in `sed 's/^.* //' $(sdir_src)/xmods.conf`; do \
|
grep ' link=static' | sed -e '/^#/d' -e 's/ .*//' \
|
||||||
q_mod=`echo $$mod | sed 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'`; \
|
-e 's/^name=//' -e 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'`; do \
|
||||||
case $$binmods in \
|
test x$q_mod = xzshQsmain && continue; \
|
||||||
*" $$mod "*) \
|
echo "#define LINKED_XMOD_$$q_mod 1"; \
|
||||||
echo "#define LINKED_XMOD_$$q_mod 1" ;; \
|
done; \
|
||||||
*) echo "#ifdef DYNAMIC"; \
|
for q_mod in `grep ' load=yes' $(dir_top)/config.modules | \
|
||||||
echo "# define UNLINKED_XMOD_$$q_mod 1"; \
|
grep ' link=dynamic' | sed -e '/^#/d' -e 's/ .*//' \
|
||||||
echo "#endif" ;; \
|
-e 's/^name=//' -e 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'`; do \
|
||||||
esac; \
|
test x$q_mod = x && continue; \
|
||||||
done \
|
echo "#ifdef DYNAMIC"; \
|
||||||
|
echo "# define UNLINKED_XMOD_$$q_mod 1"; \
|
||||||
|
echo "#endif"; \
|
||||||
|
done; \
|
||||||
) > $@
|
) > $@
|
||||||
|
|
||||||
clean-here: clean.zsh
|
clean-here: clean.zsh
|
||||||
|
|
Loading…
Reference in a new issue