diff options
-rwxr-xr-x | configure.ac | 1 | ||||
-rw-r--r-- | jim-exec.c | 49 | ||||
-rw-r--r-- | jim-signal.c | 10 | ||||
-rw-r--r-- | jim-signal.h | 1 | ||||
-rw-r--r-- | jim.c | 22 | ||||
-rw-r--r-- | jim_tcl.txt | 56 |
6 files changed, 124 insertions, 15 deletions
diff --git a/configure.ac b/configure.ac index f53455f..84f726c 100755 --- a/configure.ac +++ b/configure.ac @@ -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`) @@ -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) diff --git a/jim-signal.c b/jim-signal.c index 09a0f7a..e841d61 100644 --- a/jim-signal.c +++ b/jim-signal.c @@ -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. diff --git a/jim-signal.h b/jim-signal.h index c5cdf9f..92e080d 100644 --- a/jim-signal.h +++ b/jim-signal.h @@ -19,5 +19,6 @@ *---------------------------------------------------------------------- */ const char *Jim_SignalId(int sig); +const char *Jim_SignalName(int sig); #endif @@ -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; diff --git a/jim_tcl.txt b/jim_tcl.txt index 62e8990..6b173ea 100644 --- a/jim_tcl.txt +++ b/jim_tcl.txt @@ -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 ------- |