aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2021-08-01 15:15:15 +1000
committerSteve Bennett <steveb@workware.net.au>2022-04-14 07:51:29 +1000
commit5b967d60f4f5277e0f843d15fc54c0cb906a1bc2 (patch)
tree3c6e9e7b8b49cd48b01334b552e91e40dbac4a6d
parentb66b2a731be6bbc680f3f2c69d972050686d6720 (diff)
downloadjimtcl-5b967d60f4f5277e0f843d15fc54c0cb906a1bc2.zip
jimtcl-5b967d60f4f5277e0f843d15fc54c0cb906a1bc2.tar.gz
jimtcl-5b967d60f4f5277e0f843d15fc54c0cb906a1bc2.tar.bz2
try: add support for trap
In addition to "on codes ..." it is now possible to trap on errorcode with "trap sublist ..." e.g. try { ... } trap CHILDSTATUS {msg opts} { ... } Fixes #204 Signed-off-by: Steve Bennett <steveb@workware.net.au>
-rw-r--r--jim.c98
-rw-r--r--jim_tcl.txt37
-rw-r--r--tests/try.test44
3 files changed, 132 insertions, 47 deletions
diff --git a/jim.c b/jim.c
index d818bb8..33166af 100644
--- a/jim.c
+++ b/jim.c
@@ -14518,7 +14518,7 @@ static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *c
{
static const char * const wrongargs_catchtry[2] = {
"?-?no?code ... --? script ?resultVarName? ?optionVarName?",
- "?-?no?code ... --? script ?on codes vars script? ... ?finally script?"
+ "?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?"
};
int exitCode = 0;
int i;
@@ -14527,7 +14527,8 @@ static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *c
Jim_Obj *finallyScriptObj = NULL;
Jim_Obj *msgVarObj = NULL;
Jim_Obj *optsVarObj = NULL;
- Jim_Obj *onScriptObj = NULL;
+ Jim_Obj *handlerScriptObj = NULL;
+ Jim_Obj *errorCodeObj;
int idx;
/* Which return codes are ignored (passed through)? By default, only exit, eval and signal */
@@ -14606,9 +14607,11 @@ wrongargs:
}
interp->signal_level -= sig;
- /* For try, we need to find both a matching return code and finally (if they exist)
+ errorCodeObj = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
+
+ /* For try, we need to find both a matching return or trap code and finally (if they exist)
* Set: finallyScriptObj
- * onScriptObj
+ * handlerScriptObj
* msgVarObj
* optsVarObj
* Any of these can be NULL;
@@ -14616,31 +14619,60 @@ wrongargs:
idx++;
if (istry) {
while (idx < argc) {
- if (Jim_CompareStringImmediate(interp, argv[idx], "on")) {
- int ret;
- if (idx + 4 > argc) {
- goto wrongargs;
- }
- ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode);
- if (ret > JIM_OK) {
- goto wrongargs;
- }
- if (ret == JIM_OK) {
- msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0);
- optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1);
- onScriptObj = argv[idx + 3];
- }
- idx += 4;
- }
- else if (Jim_CompareStringImmediate(interp, argv[idx], "finally")) {
- if (idx + 2 != argc) {
- goto wrongargs;
- }
- finallyScriptObj = argv[idx + 1];
- idx += 2;
+ int option;
+ int ret;
+ static const char * const try_options[] = { "on", "trap", "finally", NULL };
+ enum { TRY_ON, TRY_TRAP, TRY_FINALLY, };
+
+ if (Jim_GetEnum(interp, argv[idx], try_options, &option, "handler", JIM_ERRMSG) != JIM_OK) {
+ return JIM_ERR;
}
- else {
- goto wrongargs;
+ switch (option) {
+ case TRY_ON:
+ case TRY_TRAP:
+ if (idx + 4 > argc) {
+ goto wrongargs;
+ }
+ if (option == TRY_ON) {
+ ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode);
+ if (ret > JIM_OK) {
+ goto wrongargs;
+ }
+ }
+ else if (errorCodeObj) {
+ int len = Jim_ListLength(interp, argv[idx + 1]);
+ int i;
+
+ ret = JIM_OK;
+ /* Try to match the sublist against errorcode */
+ for (i = 0; i < len; i++) {
+ Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i);
+ Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i);
+ if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) {
+ ret = -1;
+ break;
+ }
+ }
+ }
+ else {
+ /* No errorCode, so no match for trap */
+ ret = -1;
+ }
+ /* Save the details of the first match */
+ if (ret == JIM_OK && handlerScriptObj == NULL) {
+ msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0);
+ optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1);
+ handlerScriptObj = argv[idx + 3];
+ }
+ idx += 4;
+ break;
+ case TRY_FINALLY:
+ if (idx + 2 != argc) {
+ goto wrongargs;
+ }
+ finallyScriptObj = argv[idx + 1];
+ idx += 2;
+ break;
}
}
}
@@ -14690,24 +14722,22 @@ wrongargs:
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
if (exitCode == JIM_ERR) {
- Jim_Obj *errorCode;
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
-1));
Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
- errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
- if (errorCode) {
+ if (errorCodeObj) {
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
- Jim_ListAppendElement(interp, optListObj, errorCode);
+ Jim_ListAppendElement(interp, optListObj, errorCodeObj);
}
}
if (Jim_SetVariable(interp, optsVarObj, optListObj) != JIM_OK) {
ok = 0;
}
}
- if (ok && onScriptObj) {
+ if (ok && handlerScriptObj) {
/* Execute the on script. Any return code replaces the original. */
- exitCode = Jim_EvalObj(interp, onScriptObj);
+ exitCode = Jim_EvalObj(interp, handlerScriptObj);
}
if (finallyScriptObj) {
diff --git a/jim_tcl.txt b/jim_tcl.txt
index 9aad71e..f84101f 100644
--- a/jim_tcl.txt
+++ b/jim_tcl.txt
@@ -66,6 +66,7 @@ Changes between 0.80 and 0.81
9. Add support for `lsort -stride`
10. `open` now supports POSIX-style access arguments
11. TIP 526, `expr` now only allows a single argument (unless --compat is enabled)
+12. `try` now supports trap to match on errorcode
Changes between 0.79 and 0.80
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -4570,29 +4571,35 @@ Time is measured in elapsed time, not CPU time.
try
~~~
-+*try* '?catchopts? tryscript' ?*on* 'returncodes {?resultvar? ?optsvar?} handlerscript \...'? ?*finally* 'finalscript'?+
++*try* '?catchopts? tryscript' ?*on|trap* 'match {?resultvar? ?optsvar?} handlerscript \...'? ?*finally* 'finalscript'?+
The `try` command is provided as a convenience for exception handling.
This interpeter first evaluates +'tryscript'+ under the effect of the catch
options +'catchopts'+ (e.g. +-signal -noexit --+, see `catch`).
-It then evaluates the script for the first matching 'on' handler
-(there many be zero or more) based on the return code from the `try`
-section. For example a normal +JIM_ERR+ error will be matched by
-an 'on error' handler.
+It then evaluates the script for the first matching 'on' or 'trap'
+handler (there many be zero or more) based on the return code and
+'errorcode' from the `try` section. For example a normal +JIM_ERR+
+error will be matched by an 'on error' handler.
Finally, any +'finalscript'+ is evaluated.
-The result of this command is the result of +'tryscript'+, except in the
-case where an exception occurs in a matching 'on' handler script or the 'finally' script,
-in which case the result is this new exception.
+The result of this command is the result of +'tryscript'+, except
+in the case where an exception occurs in a matching 'on' handler
+script or the 'finally' script, in which case the result is this
+new exception.
-The specified +'returncodes'+ is a list of return codes either as names ('ok', 'error', 'break', etc.)
-or as integers.
+For the +'on'+ handler, 'match' is a list of return codes either
+as names ('ok', 'error', 'break', etc.) or as integers.
-If +'resultvar'+ and +'optsvar'+ are specified, they are set as for `catch` before evaluating
-the matching handler.
+For the +'trap'+ handler, 'match' is a list to match against
+'errorcode'. For example, if the errorcode was
++'\{CHILDKILLED 10627 SIGTERM\}'+, then a 'match' value of +\{CHILDKILLED\}+
+would match.
+
+If +'resultvar'+ and +'optsvar'+ are specified, they are set as for
+`catch` before evaluating the matching handler.
For example:
@@ -4602,6 +4609,9 @@ For example:
process $f
} on {continue break} {} {
error "Unexpected break/continue"
+ } trap CHILDKILLED {msg opts} {
+ puts "A child process died"
+ return {*}$opts $msg
} on error {msg opts} {
puts "Dealing with error"
return {*}$opts $msg
@@ -4613,7 +4623,8 @@ For example:
----
If break, continue or error are raised, they are dealt with by the matching
-handler.
+handler. If an error occurred with an errorcode of +CHILDKILLED+, the trap
+handler will be evaluated as it is specified before the 'on error' handler.
In any case, the file will be closed via the 'finally' clause.
diff --git a/tests/try.test b/tests/try.test
index a2bb38a..0d76865 100644
--- a/tests/try.test
+++ b/tests/try.test
@@ -97,6 +97,50 @@ test try-1.9 "return -code from within try" {
list [catch a msg] $msg
} {3 text}
+test try-2.1 "try ... trap" -body {
+ proc a {} {
+ return -code error -errorcode {CUSTOM RESULT} "custom errorcode"
+ }
+ try {
+ a
+ } trap CUSTOM {msg opts} {
+ list $msg $opts(-code) $opts(-errorcode)
+ }
+} -result {{custom errorcode} 1 {CUSTOM RESULT}}
+
+test try-2.2 "trap single match" {
+ try {
+ apply {{} {return -code error -errorcode {FOO BAR} failed}}
+ } trap FOO {msg opts} {
+ list trapped
+ }
+} trapped
+
+test try-2.3 "trap two matches" {
+ try {
+ apply {{} {return -code error -errorcode {FOO BAR} failed}}
+ } trap {FOO BAR} {msg opts} {
+ list trapped
+ }
+} trapped
+
+test try-2.4 "trap no match" -body {
+ try {
+ apply {{} {return -code error -errorcode {FOO BAR} failed}}
+ } trap BAZ {msg opts} {
+ list trapped
+ }
+} -returnCodes error -result failed
+
+test try-2.5 "trap match first but not second" -body {
+ try {
+ apply {{} {return -code error -errorcode {FOO BAR} failed}}
+ } trap {FOO BAZ} {msg opts} {
+ list trapped
+ }
+} -returnCodes error -result failed
+
+
proc c {} {
try {
error here