Permalink
Browse files

Set $::errorCode from exec

This allows the return code from a failed 'exec' command to be retrieved.

Also support -errorcode in catch and return.

Signed-off-by: Steve Bennett <steveb@workware.net.au>
  • Loading branch information...
1 parent 388ccb1 commit ec978d041463c9effdb17018f064df29592f8d40 @msteveb committed Sep 10, 2010
Showing with 124 additions and 15 deletions.
  1. +1 −0 configure.ac
  2. +38 −11 jim-exec.c
  3. +10 −0 jim-signal.c
  4. +1 −0 jim-signal.h
  5. +21 −1 jim.c
  6. +53 −3 jim_tcl.txt
View
@@ -79,6 +79,7 @@ AC_SUBST(JIM_LIBTYPE,$JIM_LIBTYPE)
AC_CHECK_FUNCS([ualarm sysinfo lstat fork vfork])
AC_CHECK_FUNCS([backtrace geteuid mkstemp realpath strptime])
+AC_CHECK_FUNCS([regcomp waitpid sigaction sys_signame sys_siglist])
AC_SUBST(EXTRA_CFLAGS,$EXTRA_CFLAGS)
AC_SUBST(SRCDIR,`dirname $0`)
View
@@ -93,22 +93,49 @@ static void JimTrimTrailingNewline(Jim_Interp *interp)
*/
static int JimCheckWaitStatus(Jim_Interp *interp, int pid, int waitStatus)
{
- /* REVISIT: Child exit status is lost here */
- if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
- return JIM_OK;
+ Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
+ int rc = JIM_ERR;
+
+ if (WIFEXITED(waitStatus)) {
+ if (WEXITSTATUS(waitStatus) == 0) {
+ Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
+ rc = JIM_OK;
+ }
+ else {
+ Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
+ }
}
- else if (WIFSIGNALED(waitStatus)) {
+ else {
+ const char *type;
+ const char *action;
+
+ if (WIFSIGNALED(waitStatus)) {
+ type = "CHILDKILLED";
+ action = "killed";
+ }
+ else {
+ type = "CHILDSUSP";
+ action = "suspended";
+ }
+
+ Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
+
#ifdef jim_ext_signal
- Jim_SetResultFormatted(interp, "child killed by signal %s",
- Jim_SignalId(WTERMSIG(waitStatus)));
+ Jim_SetResultFormatted(interp, "child %s by signal %s", action, Jim_SignalId(WTERMSIG(waitStatus)));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalId(WTERMSIG(waitStatus)), -1));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1));
#else
- Jim_SetResultFormatted(interp, "child killed by signal %d", WTERMSIG(waitStatus));
+ Jim_SetResultFormatted(interp, "child %s by signal %d", action, WTERMSIG(waitStatus));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus)));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus)));
#endif
}
- else if (WIFSTOPPED(waitStatus)) {
- Jim_SetResultString(interp, "child suspended", -1);
- }
- return JIM_ERR;
+ Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
+ return rc;
}
#if defined(HAVE_FORK) && !defined(HAVE_NO_FORK)
View
@@ -108,6 +108,16 @@ const char *Jim_SignalId(int sig)
return "unknown signal";
}
+const char *Jim_SignalName(int sig)
+{
+#ifdef HAVE_SYS_SIGLIST
+ if (sig >= 0 && sig < NSIG) {
+ return sys_siglist[sig];
+ }
+#endif
+ return Jim_SignalId(sig);
+}
+
/**
* Given the name of a signal, returns the signal value if found,
* or returns -1 (and sets an error) if not found.
View
@@ -19,5 +19,6 @@
*----------------------------------------------------------------------
*/
const char *Jim_SignalId(int sig);
+const char *Jim_SignalName(int sig);
#endif
View
22 jim.c
@@ -11922,6 +11922,7 @@ static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
{
int i;
Jim_Obj *stackTraceObj = NULL;
+ Jim_Obj *errorCodeObj = NULL;
int returnCode = JIM_OK;
long level = 1;
@@ -11934,6 +11935,9 @@ static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
stackTraceObj = argv[i + 1];
}
+ else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
+ errorCodeObj = argv[i + 1];
+ }
else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
@@ -11954,6 +11958,10 @@ static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
if (stackTraceObj && returnCode == JIM_ERR) {
JimSetStackTrace(interp, stackTraceObj);
}
+ /* If an error code list is supplied, set the global $errorCode */
+ if (errorCodeObj && returnCode == JIM_ERR) {
+ Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
+ }
interp->returnCode = returnCode;
interp->returnLevel = level;
@@ -12507,6 +12515,11 @@ static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
jim_wide mask =
(1 << JIM_OK | 1 << JIM_ERR | 1 << JIM_BREAK | 1 << JIM_CONTINUE | 1 << JIM_RETURN);
+ /* Reset the error code before catch.
+ * Note that this is not strictly correct.
+ */
+ Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
+
for (i = 1; i < argc - 1; i++) {
const char *arg = Jim_GetString(argv[i], NULL);
jim_wide option;
@@ -12601,9 +12614,16 @@ static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
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));
+ -1));
Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
+
+ errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
+ if (errorCode) {
+ Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
+ Jim_ListAppendElement(interp, optListObj, errorCode);
+ }
}
if (Jim_SetVariable(interp, argv[2], optListObj) != JIM_OK) {
return JIM_ERR;
View
@@ -77,6 +77,7 @@ Since v0.62:
14. IPv6 support is now included
15. Add 'string is'
16. Event handlers works better if an error occurs. eof handler has been removed.
+17. 'exec' now sets $::errorCode, and catch sets opts(-errorcode) for exit status
Since v0.61:
@@ -1542,8 +1543,10 @@ for the key +-code+ will be set to the return code. For +JIM_RETURN+
it will be set to the code given in 'return -code'. Additionally,
for the return code +JIM_ERR+, the value of the key +-errorinfo+
will contain the current stack trace (the same result as 'info
-stacktrace') and the value of the key +-level+ will be the current
-return level (see 'return -level'). This can be useful to rethrow an error:
+stacktrace'), the value of the key +-errorcode+ will contain the
+same value as the global variable $::errorCode, and the value of
+the key +-level+ will be the current return level (see 'return
+-level'). This can be useful to rethrow an error:
if {[catch {...} msg opts]} {
...maybe do something with the error...
@@ -1883,6 +1886,35 @@ an executable by the given name.
No 'glob' expansion or other shell-like substitutions
are performed on the arguments to commands.
+If the command fails, the global $::errorCode (and the -errorcode
+option in 'catch') will be set to a list, as follows:
+
++*CHILDKILLED* 'pid sigName msg'+::
+ This format is used when a child process has been killed
+ because of a signal. The pid element will be the process's
+ identifier (in decimal). The sigName element will be the
+ symbolic name of the signal that caused the process to
+ terminate; it will be one of the names from the include
+ file signal.h, such as SIGPIPE. The msg element will be a
+ short human-readable message describing the signal, such
+ as "write on pipe with no readers" for SIGPIPE.
+
++*CHILDSUSP* 'pid sigName msg'+::
+ This format is used when a child process has been suspended
+ because of a signal. The pid element will be the process's
+ identifier, in decimal. The sigName element will be the
+ symbolic name of the signal that caused the process to
+ suspend; this will be one of the names from the include
+ file signal.h, such as SIGTTIN. The msg element will be a
+ short human-readable message describing the signal, such
+ as "background tty read" for SIGTTIN.
+
++*CHILDSTATUS* 'pid code'+::
+ This format is used when a child process has exited with a
+ non-zero exit status. The pid element will be the process's
+ identifier (in decimal) and the code element will be the
+ exit code returned by the process (also in decimal).
+
exit
~~~~
+*exit* '?returnCode?'+
@@ -3110,7 +3142,7 @@ returns an empty string as result.
return
~~~~~~
-+*return* ?*-code* 'code'? ?*-errorinfo* 'stacktrace'? ?*-level* 'n'? ?'value'?+
++*return* ?*-code* 'code'? ?*-errorinfo* 'stacktrace'? ?*-errorcode* 'errorcode'? ?*-level* 'n'? ?'value'?+
Return immediately from the current procedure (or top-level command
or 'source' command), with *value* as the return value. If *value*
@@ -3126,8 +3158,13 @@ the new return code from *-code*. This is useful when rethrowing an error
from 'catch'. See the implementation of try/catch in tclcompat.tcl for
an example of how this is done.
+Note: The following options are only used when *-code* is JIM_ERR.
+
If *-errorinfo* is specified (as returned from 'info stacktrace')
it is used to initialize the stacktrace.
+
+If *-errorcode* is specified, it is used to set the global variable $::errorCode.
+
scan
~~~~
+*scan* 'string format varName1 ?varName2 ...?'+
@@ -4153,6 +4190,18 @@ by the Tcl library.
This variable contains a list of paths to search for packages.
It contains {. /lib/jim} by default.
++*errorCode*+::
+ This variable holds the value of the -errorcode return
+ option set by the most recent error that occurred in this
+ interpreter. This list value represents additional information
+ about the error in a form that is easy to process with
+ programs. The first element of the list identifies a general
+ class of errors, and determines the format of the rest of
+ the list. The following formats for -errorcode return options
+ are used by the Tcl core; individual applications may define
+ additional formats. Currently only 'exec' sets this variable.
+ Otherwise it will be *NONE*.
+
The following global variables are set by jimsh.
+*tcl_interactive*+::
@@ -4170,6 +4219,7 @@ The following global variables are set by jimsh.
+*jim_argv0*+::
The value of argv[0] when jimsh was invoked.
+
LICENCE
-------

0 comments on commit ec978d0

Please sign in to comment.