summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/contrib.yo91
-rw-r--r--Functions/Exceptions/catch41
-rw-r--r--Functions/Exceptions/throw29
-rw-r--r--Src/zsh.mdd2
5 files changed, 166 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index d5ba2ac62..55e0cb27b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2005-02-15 Peter Stephenson <pws@csr.com>
+
+ * 20812: Doc/Zsh/contrib.yo, Functions/Exceptions/catch,
+ Functions/Exceptions/throw, Src/zsh.mdd: add functions for
+ exception handling using "always" block.
+
2005-02-14 Peter Stephenson <pws@csr.com>
* 20811: configure.ac: Copy the trick for finding the
diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 9f74af154..2e3e9b4a1 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -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)
@@ -1033,7 +1034,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
diff --git a/Functions/Exceptions/catch b/Functions/Exceptions/catch
new file mode 100644
index 000000000..5f3876228
--- /dev/null
+++ b/Functions/Exceptions/catch
@@ -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 ))
+ CAUGHT="$EXCEPTION"
+ unset EXCEPTION
+ return 0
+ fi
+
+ return 1
+}
+# Never use globbing with "catch".
+alias catch="noglob catch"
+
+catch "$@"
diff --git a/Functions/Exceptions/throw b/Functions/Exceptions/throw
new file mode 100644
index 000000000..a5478fb9c
--- /dev/null
+++ b/Functions/Exceptions/throw
@@ -0,0 +1,29 @@
+# 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.
+EXCEPTION="$1"
+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
diff --git a/Src/zsh.mdd b/Src/zsh.mdd
index fddd0c30f..e4dae2a2d 100644
--- a/Src/zsh.mdd
+++ b/Src/zsh.mdd
@@ -2,7 +2,7 @@ name=zsh/main
link=static
load=yes
# load=static should replace use of alwayslink
-functions='Functions/Misc/* Functions/MIME/* Functions/Prompts/*'
+functions='Functions/Exceptions/* Functions/Misc/* Functions/MIME/* Functions/Prompts/*'
nozshdep=1
alwayslink=1