]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Latest upstream jimsh0.c.
authorstephan <stephan@noemail.net>
Mon, 13 Apr 2026 10:56:14 +0000 (10:56 +0000)
committerstephan <stephan@noemail.net>
Mon, 13 Apr 2026 10:56:14 +0000 (10:56 +0000)
FossilOrigin-Name: 414ae4dd65182ce571a6abb1a1a2ba2c169b204156f4f8923c42f64805a3ef9f

autosetup/jimsh0.c
manifest
manifest.uuid

index 0f0a890888b43186d41b52d621b92777b79e2885..51b52968a8c5c0810d293b2ae44dd650594d0844 100644 (file)
 #else
 #define _GNU_SOURCE
 #endif
+#ifndef __ixemul__
 #define HAVE_FORK
+#else
+#define HAVE_VFORK
+#endif
 #define HAVE_WAITPID
 #define HAVE_ISATTY
 #define HAVE_MKSTEMP
@@ -227,7 +231,8 @@ extern "C" {
 
 
 
-#define JIM_ABI_VERSION 101
+#define JIM_ABI_VERSION 102
+
 
 #define JIM_OK 0
 #define JIM_ERR 1
@@ -237,6 +242,8 @@ extern "C" {
 #define JIM_SIGNAL 5
 #define JIM_EXIT 6
 
+#define JIM_USAGE -1
+
 #define JIM_EVAL 7
 
 #define JIM_MAX_CALLFRAME_DEPTH 1000
@@ -247,7 +254,6 @@ extern "C" {
 
 #define JIM_NONE 0
 #define JIM_ERRMSG 1
-#define JIM_ENUM_ABBREV 2
 #define JIM_UNSHARED 4
 #define JIM_MUSTEXIST 8
 #define JIM_NORESULT 16
@@ -258,6 +264,14 @@ extern "C" {
 #define JIM_SUBST_NOESC 4
 #define JIM_SUBST_FLAG 128
 
+#define JIM_TAINT_STD   1 /* The "normal" type of taint. Allows for multiple
+                           * types of taint in the future
+                           */
+#define JIM_TAINT_ANY   ~0
+
+
+#define JIM_ENUM_ABBREV 2
+
 
 #define JIM_CASESENS    0
 #define JIM_NOCASE      1
@@ -277,6 +291,7 @@ typedef struct Jim_Stack {
     int len;
     int maxlen;
     void **vector;
+    void (*freefunc) (void *ptr);
 } Jim_Stack;
 
 
@@ -363,6 +378,7 @@ typedef struct Jim_Obj {
     const struct Jim_ObjType *typePtr;
     int refCount;
     int length;
+    unsigned taint;
 
     union {
 
@@ -490,8 +506,6 @@ typedef struct Jim_CallFrame {
     Jim_Obj *procBodyObjPtr;
     struct Jim_CallFrame *next;
     Jim_Obj *nsObj;
-    Jim_Obj *unused_fileNameObj;
-    int unused_line;
     Jim_Stack *localCommands;
     struct Jim_Obj *tailcallObj;
     struct Jim_Cmd *tailcallCmd;
@@ -534,9 +548,15 @@ typedef struct Jim_Dict {
     unsigned int dummy;
 } Jim_Dict;
 
+#define JIM_CMD_ISPROC 1
+#define JIM_CMD_ISCHANNEL 2
+#define JIM_CMD_ISALIAS 4
+
+#define JIM_CMD_NOTAINT 0x100
+
 typedef struct Jim_Cmd {
     int inUse;
-    int isproc;
+    int flags;
     struct Jim_Cmd *prevCmd;
     Jim_Obj *cmdNameObj;
     union {
@@ -545,6 +565,10 @@ typedef struct Jim_Cmd {
             Jim_CmdProc *cmdProc;
             Jim_DelCmdProc *delProc;
             void *privData;
+            const char *usage;
+            const char *help;
+            short minargs;
+            short maxargs;
         } native;
         struct {
 
@@ -573,7 +597,6 @@ typedef struct Jim_PrngState {
 
 typedef struct Jim_Interp {
     Jim_Obj *result;
-    int unused_errorLine;
     Jim_Obj *currentFilenameObj;
     int break_level;
     int maxCallFrameDepth;
@@ -601,11 +624,9 @@ typedef struct Jim_Interp {
     int safeexpr;
     Jim_Obj *liveList;
     Jim_Obj *freeList;
-    Jim_Obj *unused_currentScriptObj;
     Jim_EvalFrame topEvalFrame;
     Jim_EvalFrame *evalFrame;
     int procLevel;
-    Jim_Obj * const *unused_argv;
     Jim_Obj *nullScriptObj;
     Jim_Obj *emptyObj;
     Jim_Obj *trueObj;
@@ -624,7 +645,7 @@ typedef struct Jim_Interp {
     Jim_Obj *defer;
     Jim_Obj *traceCmdObj;
     int unknown_called;
-    int errorFlag;
+    int hasErrorStackTrace;
     void *cmdPrivData; /* Used to pass the private data pointer to
                   a command. It is set to what the user specified
                   via Jim_CreateCommand(). */
@@ -636,6 +657,7 @@ typedef struct Jim_Interp {
     Jim_PrngState *prngState;
     struct Jim_HashTable packages;
     Jim_Stack *loadHandles;
+    unsigned taint;
 } Jim_Interp;
 
 #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
@@ -727,13 +749,10 @@ JIM_EXPORT void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
 
 
 
-JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
-JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
-JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
+JIM_EXPORT void Jim_StackInit(Jim_Stack *stack, void (*freefunc) (void *ptr));
+JIM_EXPORT void Jim_StackFree(Jim_Stack *stack);
 JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
-JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
-JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
-JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr));
+JIM_EXPORT void *Jim_StackPop(Jim_Stack *stack);
 
 
 JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
@@ -810,16 +829,33 @@ JIM_EXPORT const char *Jim_ReturnCode(int code);
 JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...);
 
 
-JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
+JIM_EXPORT Jim_Cmd *Jim_RegisterCommand(Jim_Interp *interp, Jim_Obj *cmdNameObj,
+    Jim_CmdProc *cmdProc,
+    Jim_DelCmdProc *delProc,
+    const char *usage,
+    const char *help,
+    short minargs,
+    short maxargs,
+    int flags,
+    void *privData);
+
 JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
         const char *cmdName, Jim_CmdProc *cmdProc, void *privData,
          Jim_DelCmdProc *delProc);
+#define Jim_RegisterSimpleCmd(interp, name, usage, minargs, maxargs, cmdproc) \
+        Jim_RegisterCommand(interp, Jim_NewStringObj(interp, name, -1), cmdproc, NULL, usage, NULL, minargs, maxargs, 0, NULL)
+
+#define Jim_RegisterCmd(interp, name, usage, minargs, maxargs, cmdproc, delproc, privdata, flags) \
+        Jim_RegisterCommand(interp, Jim_NewStringObj(interp, name, -1), cmdproc, delproc, usage, NULL, minargs, maxargs, flags, privdata)
+
+JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
 JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
         Jim_Obj *cmdNameObj);
 JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
         Jim_Obj *oldNameObj, Jim_Obj *newNameObj);
 JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
         Jim_Obj *objPtr, int flags);
+
 JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
         Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
 JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
@@ -998,6 +1034,22 @@ JIM_EXPORT int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
 JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
 JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
 
+
+JIM_EXPORT void Jim_SetTaintError(Jim_Interp *interp, int cmdargs, Jim_Obj *const *argv);
+JIM_EXPORT int Jim_CalcTaint(int argc, Jim_Obj *const *argv);
+
+#ifdef JIM_TAINT
+#define Jim_CheckTaint(i, t) ((i)->taint & (t))
+#define Jim_TaintObj(o,t) (o)->taint |= (t)
+#define Jim_UntaintObj(o) (o)->taint = 0
+#define Jim_GetObjTaint(o) (o)->taint
+#else
+#define Jim_CheckTaint(i, t) 0
+#define Jim_TaintObj(o,t)
+#define Jim_UntaintObj(o)
+#define Jim_GetObjTaint(o) 0
+#endif
+
 #ifdef __cplusplus
 }
 #endif
@@ -1015,6 +1067,10 @@ extern "C" {
 
 #define JIM_MODFLAG_HIDDEN   0x0001
 #define JIM_MODFLAG_FULLARGV 0x0002
+#define JIM_MODFLAG_NOTAINT  0x0004
+
+#define JIM_SUBCMD_BADARGS -1
+#define JIM_SUBCMD_TAINTED -2
 
 
 
@@ -1041,6 +1097,9 @@ int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_
 
 void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type *ct, Jim_Obj *subcmd);
 
+Jim_Cmd *Jim_RegisterSubCmd(Jim_Interp *interp, const char *cmdname,
+       const jim_subcmd_type *command_table, Jim_DelCmdProc *delProc);
+
 #ifdef __cplusplus
 }
 #endif
@@ -1074,6 +1133,7 @@ typedef struct regexp {
        int regmust;
        int regmlen;
        int *program;
+       char *exp;
 
 
        const char *regparse;
@@ -1094,10 +1154,14 @@ typedef struct regexp {
 typedef regexp regex_t;
 
 #define REG_EXTENDED 0
-#define REG_NEWLINE 1
 #define REG_ICASE 2
+#define REG_NEWLINE_ANCHOR 4
+#define REG_NEWLINE_STOP 8
+
+#define REG_NEWLINE (REG_NEWLINE_ANCHOR | REG_NEWLINE_STOP)
 
 #define REG_NOTBOL 16
+#define REG_EXPANDED 32
 
 enum {
        REG_NOERROR,
@@ -1408,8 +1472,8 @@ int Jim_initjimshInit(Jim_Interp *interp)
 "\n"
 "proc tcl::stdhint {string} {\n"
 "      set result \"\"\n"
+"      lassign $string cmd arg\n"
 "      if {[llength $string] >= 2} {\n"
-"              lassign $string cmd arg\n"
 "              if {$cmd in $::tcl::stdhint_commands || [info channel $cmd] ne \"\"} {\n"
 "                      catch {\n"
 "                              set help [$cmd -help $arg]\n"
@@ -1431,6 +1495,23 @@ int Jim_initjimshInit(Jim_Interp *interp)
 "                              }\n"
 "                      }\n"
 "              }\n"
+"      } else {\n"
+"              catch {\n"
+"                      if {[exists -alias $cmd] && [llength [info alias $cmd]] == 1} {\n"
+"\n"
+"\n"
+"\n"
+"                              set help [info usage [info alias $cmd]]\n"
+"                      } else {\n"
+"                              set help [info usage $cmd]\n"
+"                      }\n"
+"                      set hint [join [lrange $help 1 end]]\n"
+"                      set prefix \" \"\n"
+"                      if {[string match \"* \" $string]} {\n"
+"                              set prefix \"\"\n"
+"                      }\n"
+"                      set result [list $prefix$hint {*}$::tcl::stdhint_col]\n"
+"              }\n"
 "      }\n"
 "      return $result\n"
 "}\n"
@@ -1894,7 +1975,9 @@ int Jim_tclcompatInit(Jim_Interp *interp)
 "\n"
 "\n"
 "\n"
-"proc parray {arrayname {pattern *} {puts puts}} {\n"
+"\n"
+"\n"
+"proc parray {arrayname {pattern *} {puts puts} args} {\n"
 "      upvar $arrayname a\n"
 "\n"
 "      set max 0\n"
@@ -1906,7 +1989,7 @@ int Jim_tclcompatInit(Jim_Interp *interp)
 "      incr max [string length $arrayname]\n"
 "      incr max 2\n"
 "      foreach name [lsort [array names a $pattern]] {\n"
-"              $puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
+"              $puts {*}$args [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
 "      }\n"
 "}\n"
 "\n"
@@ -2073,6 +2156,7 @@ int Jim_tclcompatInit(Jim_Interp *interp)
 #define AIO_EOF 4
 #define AIO_WBUF_NONE 8
 #define AIO_NONBLOCK 16
+#define AIO_NOTAINT  32
 
 #define AIO_ONEREAD 32
 
@@ -2131,7 +2215,6 @@ typedef struct {
     int (*reader)(struct AioFile *af, char *buf, int len, int pending);
     int (*error)(const struct AioFile *af);
     const char *(*strerror)(struct AioFile *af);
-    int (*verify)(struct AioFile *af);
 } JimAioFopsType;
 
 typedef struct AioFile
@@ -2141,6 +2224,8 @@ typedef struct AioFile
     int flags;
     long timeout;
     int fd;
+    unsigned taintsource;
+    unsigned taintsink;
     int addr_family;
     void *ssl;
     const JimAioFopsType *fops;
@@ -2211,7 +2296,6 @@ static const JimAioFopsType stdio_fops = {
     stdio_reader,
     stdio_error,
     stdio_strerror,
-    NULL,
 };
 
 
@@ -2244,6 +2328,7 @@ static int aio_start_nonblocking(AioFile *af)
 }
 
 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
+static void JimAioSetTaint(AioFile *af, int taintsource, int taintsink);
 static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
     const char *hdlfmt, int family, int flags);
 
@@ -2498,7 +2583,7 @@ static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
         argv++;
     }
     if (argc) {
-        return -1;
+        return JIM_USAGE;
     }
 
 
@@ -2509,6 +2594,7 @@ static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
         return JIM_ERR;
     }
     objPtr = aio_read_consume(interp, af, neededLen);
+    Jim_TaintObj(objPtr, af->taintsource);
 
     aio_set_nonblocking(af, nb);
 
@@ -2535,7 +2621,7 @@ int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
     Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
 
 
-    if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
+    if (cmdPtr && !(cmdPtr->flags & JIM_CMD_ISPROC) && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
         return ((AioFile *) cmdPtr->u.native.privData)->fd;
     }
     Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
@@ -2554,6 +2640,30 @@ static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     return JIM_OK;
 }
 
+static int aio_cmd_gettaint(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+    AioFile *af = Jim_CmdPrivData(interp);
+
+    static const char * const options[] = { "-source", "-sink", NULL };
+    enum { OPT_SOURCE, OPT_SINK };
+    int option;
+
+    if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
+        return JIM_ERR;
+    }
+    switch (option) {
+        case OPT_SOURCE:
+            Jim_SetResultInt(interp, af->taintsource);
+            break;
+
+        case OPT_SINK:
+            Jim_SetResultInt(interp, af->taintsink);
+            break;
+    }
+
+    return JIM_OK;
+}
+
 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
     AioFile *af = Jim_CmdPrivData(interp);
@@ -2561,6 +2671,7 @@ static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     jim_wide maxlen = JIM_WIDE_MAX;
     int ok = 1;
     Jim_Obj *objv[4];
+    long taintsink;
 
     if (argc == 2) {
         if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
@@ -2568,6 +2679,19 @@ static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
         }
     }
 
+    objv[0] = argv[0];
+    objv[1] = Jim_NewStringObj(interp, "gettaint", -1);
+    objv[2] = Jim_NewStringObj(interp, "-sink", -1);
+    if (Jim_EvalObjVector(interp, 3, objv) != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &taintsink) != JIM_OK) {
+        Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", argv[0]);
+        return JIM_ERR;
+    }
+
+    if (af->taintsource & taintsink) {
+        Jim_SetResultString(interp, "copying tainted source", -1);
+        return JIM_ERR;
+    }
+
     objv[0] = argv[0];
     objv[1] = Jim_NewStringObj(interp, "flush", -1);
     if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
@@ -2619,6 +2743,15 @@ static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     return JIM_OK;
 }
 
+
+static const char *jim_strstr(const char *haystack, int haylen, const char *needle, int needlen)
+{
+    if (needlen == 1) {
+        return (const char *)memchr(haystack, needle[0], haylen);
+    }
+    return strstr(haystack, needle);
+}
+
 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
     AioFile *af = Jim_CmdPrivData(interp);
@@ -2626,8 +2759,44 @@ static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     int len;
     int nb;
     unsigned flags = AIO_ONEREAD;
-    char *nl = NULL;
+    const char *nl = NULL;
     int offset = 0;
+    long keepnl = 0;
+    const char *nlstr = "\n";
+    int nlstrlen = 1;
+
+    while (argc >= 2) {
+        enum {OPT_EOL, OPT_KEEP};
+        static const char * const options[] = {
+            "-eol",
+            "-keep",
+            NULL
+        };
+        int opt;
+
+
+        if (*Jim_String(argv[0]) != '-') {
+            return JIM_USAGE;
+        }
+
+        if (Jim_GetEnum(interp, argv[0], options, &opt, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
+            return JIM_ERR;
+        }
+
+        switch (opt) {
+            case OPT_EOL:
+                nlstr = Jim_GetString(argv[1], &nlstrlen);
+                break;
+
+            case OPT_KEEP:
+                if (Jim_GetLong(interp, argv[1], &keepnl) != JIM_OK) {
+                    return JIM_ERR;
+                }
+                break;
+        }
+        argc -= 2;
+        argv += 2;
+    }
 
     errno = 0;
 
@@ -2640,12 +2809,12 @@ static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     while (!aio_eof(af)) {
         if (af->readbuf) {
             const char *pt = Jim_GetString(af->readbuf, &len);
-            nl = memchr(pt + offset, '\n', len - offset);
+            nl = jim_strstr(pt + offset, len - offset, nlstr, nlstrlen);
             if (nl) {
 
-                objPtr = Jim_NewStringObj(interp, pt, nl - pt);
+                objPtr = Jim_NewStringObj(interp, pt, nl - pt + (keepnl ? nlstrlen : 0));
 
-                aio_consume(af->readbuf, nl - pt + 1);
+                aio_consume(af->readbuf, nl - pt + nlstrlen);
                 break;
             }
             offset = len;
@@ -2667,10 +2836,10 @@ static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     else if (!objPtr) {
         objPtr = Jim_NewStringObj(interp, NULL, 0);
     }
+    Jim_TaintObj(objPtr, af->taintsource);
 
     if (argc) {
         if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
-            Jim_FreeNewObj(interp, objPtr);
             return JIM_ERR;
         }
 
@@ -2697,9 +2866,14 @@ static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     int wnow = 0;
     int nl = 1;
 
+    if (Jim_CheckTaint(interp, af->taintsink)) {
+        Jim_SetResultString(interp, "puts: tainted data", -1);
+        return JIM_ERR;
+    }
+
     if (argc == 2) {
         if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
-            return -1;
+            return JIM_USAGE;
         }
         strObj = argv[1];
         nl = 0;
@@ -2832,7 +3006,7 @@ static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
         else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
             orig = SEEK_END;
         else {
-            return -1;
+            return JIM_USAGE;
         }
     }
     if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
@@ -2870,6 +3044,45 @@ static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     return JIM_OK;
 }
 
+#ifdef JIM_TAINT
+static int aio_cmd_taint(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+    AioFile *af = Jim_CmdPrivData(interp);
+    static const char * const types[] = {
+        "sink",
+        "source",
+        NULL
+    };
+    enum
+    {
+        TAINT_TYPE_SINK,
+        TAINT_TYPE_SOURCE,
+    };
+    int type;
+    long taint;
+
+    if (Jim_GetEnum(interp, argv[0], types, &type, NULL, JIM_ERRMSG) != JIM_OK)
+        return JIM_ERR;
+
+    if (argc == 1) {
+        Jim_SetResultInt(interp, type == TAINT_TYPE_SINK ? af->taintsink : af->taintsource);
+        return JIM_OK;
+    }
+    else if (Jim_GetLong(interp, argv[1], &taint) == JIM_OK) {
+        if (type == TAINT_TYPE_SINK) {
+            af->taintsink = taint;
+        }
+        else {
+            af->taintsource = taint;
+        }
+        return JIM_OK;
+    }
+    else {
+        return JIM_ERR;
+    }
+}
+#endif
+
 #ifdef O_NDELAY
 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
@@ -3086,13 +3299,22 @@ static const jim_subcmd_type aio_command_table[] = {
         aio_cmd_getfd,
         0,
         0,
+        JIM_MODFLAG_HIDDEN,
+
+    },
+    {   "gettaint",
+        "?-source|-sink?",
+        aio_cmd_gettaint,
+        1,
+        1,
+        JIM_MODFLAG_HIDDEN,
 
     },
     {   "gets",
-        "?var?",
+        "?-eol <str>? ?-keep 0|1? ?var?",
         aio_cmd_gets,
         0,
-        1,
+        -1,
 
     },
     {   "puts",
@@ -3152,6 +3374,15 @@ static const jim_subcmd_type aio_command_table[] = {
         0,
 
     },
+#ifdef JIM_TAINT
+    {   "taint",
+        "source|sink ?0|n?",
+        aio_cmd_taint,
+        1,
+        2,
+
+    },
+#endif
 #ifdef O_NDELAY
     {   "ndelay",
         "?0|1?",
@@ -3333,19 +3564,25 @@ static int JimAioOpenCommand(Jim_Interp *interp, int argc,
         Jim_Obj *const *argv)
 {
     int openflags;
+    AioFile *af;
     const char *filename;
     int fd = -1;
     int n = 0;
-    int flags = 0;
+
+    int flags = AIO_NOTAINT;
 
     if (argc > 2 && Jim_CompareStringImmediate(interp, argv[2], "-noclose")) {
         flags = AIO_KEEPOPEN;
         n++;
     }
-    if (argc < 2 || argc > 3 + n) {
-        Jim_WrongNumArgs(interp, 1, argv, "filename ?-noclose? ?mode?");
+
+    if (Jim_CheckTaint(interp, JIM_TAINT_ANY)) {
+        Jim_SetTaintError(interp, 1, argv);
         return JIM_ERR;
     }
+    if (argc > 3 + n) {
+        return JIM_USAGE;
+    }
 
     filename = Jim_String(argv[1]);
 
@@ -3376,16 +3613,29 @@ static int JimAioOpenCommand(Jim_Interp *interp, int argc,
     else {
         openflags = O_RDONLY;
     }
+
     fd = open(filename, openflags, 0666);
     if (fd < 0) {
         JimAioSetError(interp, argv[1]);
         return JIM_ERR;
     }
 
-    return JimMakeChannel(interp, fd, argv[1], "aio.handle%ld", 0, flags) ? JIM_OK : JIM_ERR;
+    af = JimMakeChannel(interp, fd, argv[1], "aio.handle%ld", 0, flags);
+    if (af) {
+
+        JimAioSetTaint(af, 0, 0);
+        return JIM_OK;
+    }
+    return JIM_ERR;
 }
 
 
+static void JimAioSetTaint(AioFile *af, int taintsource, int taintsink)
+{
+    af->taintsource = taintsource;
+    af->taintsink = taintsink;
+}
+
 static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
     const char *hdlfmt, int family, int flags)
 {
@@ -3434,7 +3684,15 @@ static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
     af->rbuf_len = AIO_DEFAULT_RBUF_LEN;
 
 
-    Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
+
+    if (!(flags & AIO_NOTAINT)) {
+        JimAioSetTaint(af, JIM_TAINT_STD, JIM_TAINT_STD);
+    }
+    Jim_RegisterCommand(interp, cmdname,
+        JimAioSubCmdProc, JimAioDelProc,
+        NULL,
+        NULL,
+        0, -1, JIM_CMD_ISCHANNEL, af);
 
     Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, cmdname));
 
@@ -3479,10 +3737,6 @@ static int JimCreatePipe(Jim_Interp *interp, Jim_Obj *filenameObj, int flags)
 
 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc != 1) {
-        Jim_WrongNumArgs(interp, 1, argv, "");
-        return JIM_ERR;
-    }
     return JimCreatePipe(interp, argv[0], 0);
 }
 #endif
@@ -3493,11 +3747,6 @@ static int JimAioOpenPtyCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
     int p[2];
     char path[MAXPATHLEN];
 
-    if (argc != 1) {
-        Jim_WrongNumArgs(interp, 1, argv, "");
-        return JIM_ERR;
-    }
-
     if (openpty(&p[0], &p[1], path, NULL, NULL) != 0) {
         JimAioSetError(interp, NULL);
         return JIM_ERR;
@@ -3511,27 +3760,33 @@ static int JimAioOpenPtyCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
 
 
 
-int Jim_aioInit(Jim_Interp *interp)
+
+static void JimMakeStdioChannel(Jim_Interp *interp, FILE *fh, const char *name, unsigned flags)
 {
-    if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
-        return JIM_ERR;
 
+    AioFile *af = JimMakeChannel(interp, fileno(fh), NULL, name, 0, AIO_KEEPOPEN | flags);
+    JimAioSetTaint(af, 0, 0);
+}
+
+int Jim_aioInit(Jim_Interp *interp)
+{
+    Jim_PackageProvideCheck(interp, "aio");
 #if defined(JIM_SSL)
-    Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
+    Jim_RegisterSimpleCmd(interp, "load_ssl_certs", "dir", 1, 1, JimAioLoadSSLCertsCommand);
 #endif
 
-    Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
+    Jim_RegisterCmd(interp, "open", "filename ?-noclose? ?mode?", 1, 3, JimAioOpenCommand, NULL, NULL, JIM_CMD_NOTAINT);
 #ifdef HAVE_SOCKETS
-    Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
+    Jim_RegisterCmd(interp, "socket", "?-async? ?-ipv6? socktype ?address?", 1, 4, JimAioSockCommand, NULL, NULL, JIM_CMD_NOTAINT);
 #endif
 #ifdef HAVE_PIPE
-    Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
+    Jim_RegisterSimpleCmd(interp, "pipe", "", 0, 0, JimAioPipeCommand);
 #endif
 
 
-    JimMakeChannel(interp, fileno(stdin), NULL, "stdin", 0, AIO_KEEPOPEN);
-    JimMakeChannel(interp, fileno(stdout), NULL, "stdout", 0, AIO_KEEPOPEN);
-    JimMakeChannel(interp, fileno(stderr), NULL, "stderr", 0, AIO_KEEPOPEN | AIO_WBUF_NONE);
+    JimMakeStdioChannel(interp, stdin, "stdin", 0);
+    JimMakeStdioChannel(interp, stdout, "stdout", 0);
+    JimMakeStdioChannel(interp, stderr, "stderr", AIO_WBUF_NONE);
 
     return JIM_OK;
 }
@@ -3556,8 +3811,7 @@ int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
         nocomplain = 1;
     }
     if (argc != 2 && !nocomplain) {
-        Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
-        return JIM_ERR;
+        return JIM_USAGE;
     }
 
     dirPath = Jim_String(argv[1 + nocomplain]);
@@ -3594,7 +3848,7 @@ int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 int Jim_readdirInit(Jim_Interp *interp)
 {
     Jim_PackageProvideCheck(interp, "readdir");
-    Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
+    Jim_RegisterSimpleCmd(interp, "readdir", "?-nocomplain? dirPath", 1, 2, Jim_ReaddirCmd);
     return JIM_OK;
 }
 
@@ -3667,6 +3921,7 @@ int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     int opt_indices = 0;
     int opt_all = 0;
     int opt_inline = 0;
+    int opt_lineanchor = 0;
     regex_t *regex;
     int match, i, j;
     int offset = 0;
@@ -3682,26 +3937,19 @@ int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     int eflags = 0;
     int option;
     enum {
-        OPT_INDICES,  OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
+        OPT_INDICES,  OPT_NOCASE, OPT_LINE, OPT_LINESTOP, OPT_LINEANCHOR, OPT_ALL, OPT_INLINE, OPT_START, OPT_EXPANDED, OPT_END
     };
     static const char * const options[] = {
-        "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
+        "-indices", "-nocase", "-line", "-linestop", "-lineanchor", "-all", "-inline", "-start", "-expanded", "--", NULL
     };
 
-    if (argc < 3) {
-      wrongNumArgs:
-        Jim_WrongNumArgs(interp, 1, argv,
-            "?-switch ...? exp string ?matchVar? ?subMatchVar ...?");
-        return JIM_ERR;
-    }
-
     for (i = 1; i < argc; i++) {
         const char *opt = Jim_String(argv[i]);
 
         if (*opt != '-') {
             break;
         }
-        if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
+        if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
             return JIM_ERR;
         }
         if (option == OPT_END) {
@@ -3719,8 +3967,20 @@ int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 
             case OPT_LINE:
                 regcomp_flags |= REG_NEWLINE;
+                opt_lineanchor = 1;
                 break;
 
+#ifdef REG_NEWLINE_STOP
+            case OPT_LINESTOP:
+                regcomp_flags |= REG_NEWLINE_STOP;
+                break;
+#endif
+#ifdef REG_NEWLINE_ANCHOR
+            case OPT_LINEANCHOR:
+                regcomp_flags |= REG_NEWLINE_ANCHOR;
+                opt_lineanchor = 1;
+                break;
+#endif
             case OPT_ALL:
                 opt_all = 1;
                 break;
@@ -3731,16 +3991,26 @@ int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 
             case OPT_START:
                 if (++i == argc) {
-                    goto wrongNumArgs;
+                    return JIM_USAGE;
                 }
                 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
                     return JIM_ERR;
                 }
                 break;
+
+#ifdef REG_EXPANDED
+            case OPT_EXPANDED:
+                regcomp_flags |= REG_EXPANDED;
+                break;
+#endif
+            default:
+
+                Jim_SetResultFormatted(interp, "not supported: %#s", argv[i]);
+                return JIM_ERR;
         }
     }
     if (argc - i < 2) {
-        goto wrongNumArgs;
+        return JIM_USAGE;
     }
 
     regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
@@ -3799,14 +4069,10 @@ int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 
     num_matches++;
 
-    if (opt_all && !opt_inline) {
-
-        goto try_next_match;
-    }
 
 
     j = 0;
-    for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
+    for (j = 0; j < num_vars; j++) {
         Jim_Obj *resultObj;
 
         if (opt_indices) {
@@ -3840,17 +4106,15 @@ int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
         }
         else {
 
-            result = Jim_SetVariable(interp, argv[i], resultObj);
+            result = Jim_SetVariable(interp, argv[i + 2 + j], resultObj);
 
             if (result != JIM_OK) {
-                Jim_FreeObj(interp, resultObj);
                 break;
             }
         }
     }
 
-  try_next_match:
-    if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
+    if (opt_all && (pattern[0] != '^' || opt_lineanchor) && *source_str) {
         if (pmatch[0].rm_eo) {
             offset += utf8_strlen(source_str, pmatch[0].rm_eo);
             source_str += pmatch[0].rm_eo;
@@ -3906,26 +4170,19 @@ int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     const char *pattern;
     int option;
     enum {
-        OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_COMMAND, OPT_END
+        OPT_NOCASE, OPT_LINE, OPT_LINESTOP, OPT_LINEANCHOR, OPT_ALL, OPT_START, OPT_COMMAND, OPT_EXPANDED, OPT_END
     };
     static const char * const options[] = {
-        "-nocase", "-line", "-all", "-start", "-command", "--", NULL
+        "-nocase", "-line", "-linestop", "-lineanchor", "-all", "-start", "-command", "-expanded", "--", NULL
     };
 
-    if (argc < 4) {
-      wrongNumArgs:
-        Jim_WrongNumArgs(interp, 1, argv,
-            "?-switch ...? exp string subSpec ?varName?");
-        return JIM_ERR;
-    }
-
     for (i = 1; i < argc; i++) {
         const char *opt = Jim_String(argv[i]);
 
         if (*opt != '-') {
             break;
         }
-        if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
+        if (Jim_GetEnum(interp, argv[i], options, &option, "option", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
             return JIM_ERR;
         }
         if (option == OPT_END) {
@@ -3941,13 +4198,23 @@ int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
                 regcomp_flags |= REG_NEWLINE;
                 break;
 
+#ifdef REG_NEWLINE_STOP
+            case OPT_LINESTOP:
+                regcomp_flags |= REG_NEWLINE_STOP;
+                break;
+#endif
+#ifdef REG_NEWLINE_ANCHOR
+            case OPT_LINEANCHOR:
+                regcomp_flags |= REG_NEWLINE_ANCHOR;
+                break;
+#endif
             case OPT_ALL:
                 opt_all = 1;
                 break;
 
             case OPT_START:
                 if (++i == argc) {
-                    goto wrongNumArgs;
+                    return JIM_USAGE;
                 }
                 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
                     return JIM_ERR;
@@ -3957,18 +4224,29 @@ int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
             case OPT_COMMAND:
                 opt_command = 1;
                 break;
+
+#ifdef REG_EXPANDED
+            case OPT_EXPANDED:
+                regcomp_flags |= REG_EXPANDED;
+                break;
+#endif
+
+            default:
+
+                Jim_SetResultFormatted(interp, "not supported: %#s", argv[i]);
+                return JIM_ERR;
         }
     }
     if (argc - i != 3 && argc - i != 4) {
-        goto wrongNumArgs;
+        return JIM_USAGE;
     }
 
 
     regcomp_obj = Jim_DuplicateObj(interp, argv[i]);
-       Jim_IncrRefCount(regcomp_obj);
+    Jim_IncrRefCount(regcomp_obj);
     regex = SetRegexpFromAny(interp, regcomp_obj, regcomp_flags);
     if (!regex) {
-               Jim_DecrRefCount(interp, regcomp_obj);
+        Jim_DecrRefCount(interp, regcomp_obj);
         return JIM_ERR;
     }
     pattern = Jim_String(argv[i]);
@@ -3978,7 +4256,7 @@ int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
         cmd_prefix = argv[i + 2];
         if (Jim_ListLength(interp, cmd_prefix) == 0) {
             Jim_SetResultString(interp, "command prefix must be a list of at least one element", -1);
-                       Jim_DecrRefCount(interp, regcomp_obj);
+            Jim_DecrRefCount(interp, regcomp_obj);
             return JIM_ERR;
         }
         Jim_IncrRefCount(cmd_prefix);
@@ -4011,7 +4289,8 @@ int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 
     n = source_len - offset;
     p = source_str + offset;
-    do {
+
+    while (n || pattern[0]) {
         int match = jim_regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
 
         if (match >= REG_BADPAT) {
@@ -4092,28 +4371,22 @@ int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
             break;
         }
 
+        regexec_flags = 0;
+        if (pmatch[0].rm_eo == pmatch[0].rm_so) {
 
-        if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
-            break;
-        }
-
-
-        if (pattern[0] == '\0' && n) {
-
-            Jim_AppendString(interp, resultObj, p, 1);
-            p++;
-            n--;
-        }
+            if (pattern[0] == '^') {
 
-        if (pmatch[0].rm_eo == pmatch[0].rm_so) {
+                regexec_flags = REG_NOTBOL;
+            }
+            else {
 
-            regexec_flags = REG_NOTBOL;
-        }
-        else {
-            regexec_flags = 0;
+                int charlen = utf8_charlen(p[0]);
+                Jim_AppendString(interp, resultObj, p, charlen);
+                p += charlen;
+                n -= charlen;
+            }
         }
-
-    } while (n);
+    }
 
     Jim_AppendString(interp, resultObj, p, -1);
 
@@ -4126,9 +4399,6 @@ cmd_error:
             if (result == JIM_OK) {
                 Jim_SetResultInt(interp, num_matches);
             }
-            else {
-                Jim_FreeObj(interp, resultObj);
-            }
         }
         else {
             Jim_SetResult(interp, resultObj);
@@ -4143,7 +4413,7 @@ cmd_error:
         Jim_DecrRefCount(interp, cmd_prefix);
     }
 
-       Jim_DecrRefCount(interp, regcomp_obj);
+    Jim_DecrRefCount(interp, regcomp_obj);
 
     return result;
 }
@@ -4151,8 +4421,8 @@ cmd_error:
 int Jim_regexpInit(Jim_Interp *interp)
 {
     Jim_PackageProvideCheck(interp, "regexp");
-    Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
-    Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
+    Jim_RegisterSimpleCmd(interp, "regexp", "?-option ...? exp string ?matchVar? ?subMatchVar ...?", 2, -1, Jim_RegexpCmd);
+    Jim_RegisterSimpleCmd(interp, "regsub", "?-option ...? exp string subSpec ?varName?", 3, -1, Jim_RegsubCmd);
     return JIM_OK;
 }
 
@@ -4620,7 +4890,7 @@ static int mkdir_all(char *path)
             continue;
         }
 
-        if (errno == EEXIST) {
+        if (errno == EEXIST || errno == ENOTDIR) {
             jim_stat_t sb;
 
             if (Jim_Stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
@@ -5055,6 +5325,7 @@ static const jim_subcmd_type file_command_table[] = {
         file_cmd_delete,
         1,
         -1,
+        JIM_MODFLAG_NOTAINT,
 
     },
     {   "mkdir",
@@ -5062,6 +5333,7 @@ static const jim_subcmd_type file_command_table[] = {
         file_cmd_mkdir,
         1,
         -1,
+        JIM_MODFLAG_NOTAINT,
 
     },
     {   "tempfile",
@@ -5069,6 +5341,7 @@ static const jim_subcmd_type file_command_table[] = {
         file_cmd_tempfile,
         0,
         1,
+        JIM_MODFLAG_NOTAINT,
 
     },
     {   "rename",
@@ -5076,6 +5349,7 @@ static const jim_subcmd_type file_command_table[] = {
         file_cmd_rename,
         2,
         3,
+        JIM_MODFLAG_NOTAINT,
 
     },
 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
@@ -5154,14 +5428,7 @@ static const jim_subcmd_type file_command_table[] = {
 
 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    const char *path;
-
-    if (argc != 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "dirname");
-        return JIM_ERR;
-    }
-
-    path = Jim_String(argv[1]);
+    const char *path = Jim_String(argv[1]);
 
     if (chdir(path) != 0) {
         Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
@@ -5190,9 +5457,9 @@ static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 int Jim_fileInit(Jim_Interp *interp)
 {
     Jim_PackageProvideCheck(interp, "file");
-    Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
-    Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
-    Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
+    Jim_RegisterSubCmd(interp, "file", file_command_table, NULL);
+    Jim_RegisterSimpleCmd(interp, "pwd", "", 0, 0, Jim_PwdCmd);
+    Jim_RegisterCmd(interp, "cd", "dirname", 1, 1, Jim_CdCmd, NULL, NULL, JIM_CMD_NOTAINT);
     return JIM_OK;
 }
 
@@ -5249,7 +5516,7 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 int Jim_execInit(Jim_Interp *interp)
 {
     Jim_PackageProvideCheck(interp, "exec");
-    Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
+    Jim_RegisterSimpleCmd(interp, "exec", "arg ?arg ...? ?&?", 1, -1, Jim_ExecCmd);
     return JIM_OK;
 }
 #else
@@ -5264,8 +5531,8 @@ struct WaitInfoTable;
 static char **JimOriginalEnviron(void);
 static char **JimSaveEnv(char **env);
 static void JimRestoreEnv(char **env);
-static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
-    phandle_t **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
+static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr,
+    int *outPipePtr, int *errFilePtr);
 static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr);
 static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj);
 static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
@@ -5447,7 +5714,7 @@ static struct WaitInfoTable *JimAllocWaitInfoTable(void)
     struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
     table->info = NULL;
     table->size = table->used = 0;
-    table->refcount = 1;
+    table->refcount = 0;
 
     return table;
 }
@@ -5485,7 +5752,7 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
         int i;
 
         argc--;
-        numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
+        numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL);
         if (numPids < 0) {
             return JIM_ERR;
         }
@@ -5501,7 +5768,7 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     }
 
     numPids =
-        JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
+        JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, &outputId, &errorId);
 
     if (numPids < 0) {
         return JIM_ERR;
@@ -5593,7 +5860,7 @@ static int JimGetChannelFd(Jim_Interp *interp, const char *name)
     if (Jim_EvalObjVector(interp, 2, objv) == JIM_OK) {
         jim_wide fd;
         if (Jim_GetWide(interp, Jim_GetResult(interp), &fd) == JIM_OK) {
-            return fd;
+            return dup(fd);
         }
     }
     return -1;
@@ -5647,8 +5914,7 @@ static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
         nohang = 1;
     }
     if (argc != nohang + 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?");
-        return JIM_ERR;
+        return JIM_USAGE;
     }
     if (Jim_GetLong(interp, argv[nohang + 1], &pid) != JIM_OK) {
         return JIM_ERR;
@@ -5663,6 +5929,10 @@ static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     else if (pid < 0) {
         pid = phandle;
     }
+    else if (phandle == 0) {
+
+        pid = 0;
+    }
 #endif
 
     errCodeObj = JimMakeErrorCode(interp, pid, status, NULL);
@@ -5677,42 +5947,212 @@ static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 
 static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc != 1) {
-        Jim_WrongNumArgs(interp, 1, argv, "");
-        return JIM_ERR;
-    }
-
     Jim_SetResultInt(interp, (jim_wide)getpid());
     return JIM_OK;
 }
 
-static int
-JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr,
-    int *inPipePtr, int *outPipePtr, int *errFilePtr)
+#define JIM_ETT_IN    0x0001
+#define JIM_ETT_OUT   0x0002
+#define JIM_ETT_ERR   0x0004
+#define JIM_ETT_PIPE  0x0008
+
+#define JIM_ETT_NOARG   0x0010
+#define JIM_ETT_APPEND  0x0020
+#define JIM_ETT_STR     0x0040
+#define JIM_ETT_DUPERR  0x0080
+#define JIM_ETT_HANDLE  0x0100
+
+#define JIM_ETT_CMD     0xF000
+#define JIM_ETT_BAD     0xF001
+
+struct redir_type_t {
+    const char *prefix;
+    unsigned flags;
+};
+
+
+static const struct redir_type_t redir_types[] = {
+    { "<<@",    JIM_ETT_IN | JIM_ETT_HANDLE | JIM_ETT_STR },
+    { "<<",     JIM_ETT_IN | JIM_ETT_STR },
+    { "<@",     JIM_ETT_IN | JIM_ETT_HANDLE },
+    { "<",      JIM_ETT_IN },
+
+    { "2>>",    JIM_ETT_ERR | JIM_ETT_APPEND },
+    { "2>@",    JIM_ETT_ERR | JIM_ETT_HANDLE },
+    { "2>",     JIM_ETT_ERR },
+
+    { ">>&",    JIM_ETT_OUT | JIM_ETT_APPEND | JIM_ETT_DUPERR },
+    { ">>",     JIM_ETT_OUT | JIM_ETT_APPEND },
+    { ">&@",    JIM_ETT_OUT | JIM_ETT_HANDLE | JIM_ETT_DUPERR },
+    { ">@",     JIM_ETT_OUT | JIM_ETT_HANDLE },
+    { ">&",     JIM_ETT_OUT | JIM_ETT_DUPERR },
+    { ">",      JIM_ETT_OUT },
+
+    { "|&",     JIM_ETT_PIPE | JIM_ETT_DUPERR },
+    { "|",      JIM_ETT_PIPE },
+    { NULL }
+};
+
+static unsigned JimExecClassifyArg(const char *arg)
 {
-    phandle_t *pidPtr = NULL;         /* Points to alloc-ed array holding all
-                                 * the pids of child processes. */
-    int numPids = 0;            /* Actual number of processes that exist
-                                 * at *pidPtr right now. */
-    int cmdCount;               /* Count of number of distinct commands
-                                 * found in argc/argv. */
-    const char *input = NULL;   /* Describes input for pipeline, depending
-                                 * on "inputFile".  NULL means take input
-                                 * from stdin/pipe. */
-    int input_len = 0;
+    int i;
+    for (i = 0; redir_types[i].prefix; i++) {
+        int len = strlen(redir_types[i].prefix);
+        if (strncmp(arg, redir_types[i].prefix, len) == 0) {
+            if (strlen(arg) > len) {
+                if (redir_types[i].flags & JIM_ETT_NOARG) {
 
-#define FILE_NAME   0
-#define FILE_APPEND 1
-#define FILE_HANDLE 2
-#define FILE_TEXT   3
+                    return JIM_ETT_BAD;
+                }
+                return redir_types[i].flags;
+            }
 
-    int inputFile = FILE_NAME;  /* 1 means input is name of input file.
-                                 * 2 means input is filehandle name.
-                                 * 0 means input holds actual
-                                 * text to be input to command. */
+            return redir_types[i].flags | JIM_ETT_NOARG;
+        }
+    }
+    return JIM_ETT_CMD;
+}
 
-    int outputFile = FILE_NAME; /* 0 means output is the name of output file.
-                                 * 1 means output is the name of output file, and append.
+static int JimParsePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, Jim_Obj *cmdList, Jim_Obj *redirectList)
+{
+    int i;
+
+    int first = 1;
+    const char *arg = NULL;
+
+    for (i = 0; i < argc; i++) {
+        unsigned ett;
+        if (first) {
+            if (Jim_ListLength(interp, argv[i]) == 0) {
+                Jim_SetResultString(interp, "empty command list", -1);
+                return JIM_ERR;
+            }
+            Jim_ListAppendElement(interp, cmdList, argv[i]);
+            first = 0;
+            continue;
+        }
+
+        arg = Jim_String(argv[i]);
+        ett = JimExecClassifyArg(arg);
+        if (ett == JIM_ETT_BAD || ett == JIM_ETT_CMD) {
+            Jim_SetResultFormatted(interp, "invalid redirection %s", arg);
+            return JIM_ERR;
+        }
+        if (ett & JIM_ETT_PIPE) {
+            Jim_ListAppendElement(interp, cmdList, argv[i]);
+            first = 1;
+            continue;
+        }
+        Jim_ListAppendElement(interp, redirectList, argv[i]);
+        if ((ett & JIM_ETT_NOARG)) {
+
+            if (i >= argc - 1) {
+
+                Jim_SetResultFormatted(interp, "can't specify \"%#s\" as last word in command", argv[i]);
+                return -1;
+            }
+            i++;
+            Jim_ListAppendElement(interp, redirectList, argv[i]);
+        }
+    }
+
+    if (first) {
+        if (Jim_ListLength(interp, cmdList)) {
+            Jim_SetResultFormatted(interp, "cmdlist required after %s", arg);
+        }
+        else {
+            Jim_SetResultString(interp, "cmdlist is required", -1);
+        }
+        return JIM_ERR;
+    }
+
+    return JIM_OK;
+}
+
+static int JimParsePipelineLegacy(Jim_Interp *interp, int argc, Jim_Obj *const *argv, Jim_Obj *cmdList, Jim_Obj *redirectList)
+{
+    int i;
+
+    Jim_Obj *cmdObj = Jim_NewListObj(interp, NULL, 0);
+    Jim_ListAppendElement(interp, cmdList, cmdObj);
+    const char *arg = NULL;
+
+    for (i = 0; i < argc; i++) {
+        arg = Jim_String(argv[i]);
+        unsigned ett = JimExecClassifyArg(arg);
+        if (ett == JIM_ETT_BAD) {
+            Jim_SetResultFormatted(interp, "invalid: %s", arg);
+            return JIM_ERR;
+        }
+        if (ett == JIM_ETT_CMD) {
+
+            Jim_ListAppendElement(interp, cmdObj, argv[i]);
+            continue;
+        }
+        if (ett & JIM_ETT_PIPE) {
+            if (Jim_ListLength(interp, cmdObj) == 0) {
+                goto missing_cmd;
+            }
+
+            Jim_ListAppendElement(interp, cmdList, argv[i]);
+
+            cmdObj = Jim_NewListObj(interp, NULL, 0);
+            Jim_ListAppendElement(interp, cmdList, cmdObj);
+            continue;
+        }
+        Jim_ListAppendElement(interp, redirectList, argv[i]);
+        if ((ett & JIM_ETT_NOARG)) {
+
+            if (i >= argc - 1) {
+
+                Jim_SetResultFormatted(interp, "can't specify \"%#s\" as last word in command", argv[i]);
+                return -1;
+            }
+            i++;
+            Jim_ListAppendElement(interp, redirectList, argv[i]);
+        }
+    }
+
+    if (Jim_ListLength(interp, cmdObj) == 0) {
+missing_cmd:
+        if (arg && *arg == '|') {
+            Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
+        }
+        else {
+            Jim_SetResultString(interp, "didn't specify command to execute", -1);
+        }
+        return JIM_ERR;
+    }
+
+    return JIM_OK;
+}
+
+static int JimExecPipeline(Jim_Interp *interp, Jim_Obj *cmdList, Jim_Obj *redirectList,
+    phandle_t **pidArrayPtr, int *outPipePtr, int *errFilePtr)
+{
+    phandle_t *pidPtr = NULL;         /* Points to alloc-ed array holding all
+                                 * the pids of child processes. */
+    int numPids = 0;            /* Actual number of processes that exist
+                                 * at *pidPtr right now. */
+    int cmdCount;               /* Count of number of distinct commands
+                                 * found in argc/argv. */
+    const char *input = NULL;   /* Describes input for pipeline, depending
+                                 * on "inputFile".  NULL means take input
+                                 * from stdin/pipe. */
+    int input_len = 0;
+
+#define FILE_NAME   0
+#define FILE_APPEND 1
+#define FILE_HANDLE 2
+#define FILE_TEXT   3
+
+    int inputFile = FILE_NAME;  /* 1 means input is name of input file.
+                                 * 2 means input is filehandle name.
+                                 * 0 means input holds actual
+                                 * text to be input to command. */
+
+    int outputFile = FILE_NAME; /* 0 means output is the name of output file.
+                                 * 1 means output is the name of output file, and append.
                                  * 2 means output is filehandle name.
                                  * All this is ignored if output is NULL
                                  */
@@ -5730,9 +6170,6 @@ JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t
     int errorId = -1;
     int lastOutputId = -1;
     int pipeIds[2];
-    int firstArg, lastArg;      /* Indexes of first and last arguments in
-                                 * current command. */
-    int lastBar;
     int i;
     phandle_t phandle;
     char **save_environ;
@@ -5741,13 +6178,6 @@ JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t
 #endif
     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
 
-
-    char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
-    int arg_count = 0;
-
-    if (inPipePtr != NULL) {
-        *inPipePtr = -1;
-    }
     if (outPipePtr != NULL) {
         *outPipePtr = -1;
     }
@@ -5756,96 +6186,68 @@ JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t
     }
     pipeIds[0] = pipeIds[1] = -1;
 
-    cmdCount = 1;
-    lastBar = -1;
-    for (i = 0; i < argc; i++) {
-        const char *arg = Jim_String(argv[i]);
-
-        if (arg[0] == '<') {
-            inputFile = FILE_NAME;
-            input = arg + 1;
-            if (*input == '<') {
-                inputFile = FILE_TEXT;
-                input_len = Jim_Length(argv[i]) - 2;
-                input++;
-            }
-            else if (*input == '@') {
-                inputFile = FILE_HANDLE;
-                input++;
-            }
-
-            if (!*input && ++i < argc) {
-                input = Jim_GetString(argv[i], &input_len);
-            }
-        }
-        else if (arg[0] == '>') {
-            int dup_error = 0;
-
-            outputFile = FILE_NAME;
-
-            output = arg + 1;
-            if (*output == '>') {
-                outputFile = FILE_APPEND;
-                output++;
-            }
-            if (*output == '&') {
+    int redir_len = Jim_ListLength(interp, redirectList);
+    for (i = 0; i < redir_len; i++) {
+        int len;
+        int item_len;
+        Jim_Obj *redirObj = Jim_ListGetIndex(interp, redirectList, i);
+        const char *arg = Jim_GetString(redirObj, &len);
+        unsigned ett = JimExecClassifyArg(arg);
+        const char *item;
+        int type = FILE_NAME;
+        if ((ett & JIM_ETT_NOARG) == 0) {
 
-                output++;
-                dup_error = 1;
+            item = arg + 1;
+            if (ett & JIM_ETT_HANDLE) {
+                item++;
             }
-            if (*output == '@') {
-                outputFile = FILE_HANDLE;
-                output++;
-            }
-            if (!*output && ++i < argc) {
-                output = Jim_String(argv[i]);
-            }
-            if (dup_error) {
-                errorFile = outputFile;
-                error = output;
+            if (ett & JIM_ETT_APPEND) {
+                item++;
             }
-        }
-        else if (arg[0] == '2' && arg[1] == '>') {
-            error = arg + 2;
-            errorFile = FILE_NAME;
-
-            if (*error == '@') {
-                errorFile = FILE_HANDLE;
-                error++;
+            if (ett & JIM_ETT_DUPERR) {
+                item++;
             }
-            else if (*error == '>') {
-                errorFile = FILE_APPEND;
-                error++;
+            if (ett & JIM_ETT_ERR) {
+                item++;
             }
-            if (!*error && ++i < argc) {
-                error = Jim_String(argv[i]);
+            if (ett & JIM_ETT_STR) {
+                type = FILE_TEXT;
+                item++;
             }
+            item_len = len - (item - arg);
         }
         else {
-            if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
-                if (i == lastBar + 1 || i == argc - 1) {
-                    Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
-                    goto badargs;
-                }
-                lastBar = i;
-                cmdCount++;
-            }
 
-            arg_array[arg_count++] = (char *)arg;
-            continue;
+            i++;
+            item = Jim_GetString(Jim_ListGetIndex(interp, redirectList, i), &item_len);
         }
 
-        if (i >= argc) {
-            Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
-            goto badargs;
+        if (ett & JIM_ETT_HANDLE) {
+            type = FILE_HANDLE;
+        }
+        if (ett & JIM_ETT_APPEND) {
+            type = FILE_APPEND;
+        }
+        if (ett & JIM_ETT_STR) {
+            type = FILE_TEXT;
+        }
+        if (ett & JIM_ETT_IN) {
+            input = item;
+            input_len = item_len;
+            inputFile = type;
+        }
+        else if (ett & JIM_ETT_OUT) {
+            output = item;
+            outputFile = type;
+            if (ett & JIM_ETT_DUPERR) {
+                error = output;
+                errorFile = outputFile;
+            }
+        }
+        else if (ett & JIM_ETT_ERR) {
+            error = item;
+            errorFile = type;
         }
-    }
-
-    if (arg_count == 0) {
-        Jim_SetResultString(interp, "didn't specify command to execute", -1);
-badargs:
-        Jim_Free(arg_array);
-        return -1;
     }
 
 
@@ -5865,12 +6267,10 @@ badargs:
             Jim_Lseek(inputId, 0L, SEEK_SET);
         }
         else if (inputFile == FILE_HANDLE) {
-            int fd = JimGetChannelFd(interp, input);
-
-            if (fd < 0) {
+            inputId = JimGetChannelFd(interp, input);
+            if (inputId < 0) {
                 goto error;
             }
-            inputId = dup(fd);
         }
         else {
             inputId = Jim_OpenForRead(input);
@@ -5880,23 +6280,13 @@ badargs:
             }
         }
     }
-    else if (inPipePtr != NULL) {
-        if (pipe(pipeIds) != 0) {
-            Jim_SetResultErrno(interp, "couldn't create input pipe for command");
-            goto error;
-        }
-        inputId = pipeIds[0];
-        *inPipePtr = pipeIds[1];
-        pipeIds[0] = pipeIds[1] = -1;
-    }
 
     if (output != NULL) {
         if (outputFile == FILE_HANDLE) {
-            int fd = JimGetChannelFd(interp, output);
-            if (fd < 0) {
+            lastOutputId = JimGetChannelFd(interp, output);
+            if (lastOutputId < 0) {
                 goto error;
             }
-            lastOutputId = dup(fd);
         }
         else {
             lastOutputId = Jim_OpenForWrite(output, outputFile == FILE_APPEND);
@@ -5916,6 +6306,7 @@ badargs:
         pipeIds[0] = pipeIds[1] = -1;
     }
 
+
     if (error != NULL) {
         if (errorFile == FILE_HANDLE) {
             if (strcmp(error, "1") == 0) {
@@ -5929,11 +6320,10 @@ badargs:
                 }
             }
             if (errorId == -1) {
-                int fd = JimGetChannelFd(interp, error);
-                if (fd < 0) {
+                errorId = JimGetChannelFd(interp, error);
+                if (errorId < 0) {
                     goto error;
                 }
-                errorId = dup(fd);
             }
         }
         else {
@@ -5952,56 +6342,56 @@ badargs:
         *errFilePtr = dup(errorId);
     }
 
-
+    int cmd_list_size = Jim_ListLength(interp, cmdList);
+    cmdCount = (cmd_list_size + 1) / 2;
     pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
-    for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
-        int pipe_dup_err = 0;
-        int origErrorId = errorId;
 
-        for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
-            if (strcmp(arg_array[lastArg], "|") == 0) {
-                break;
-            }
-            if (strcmp(arg_array[lastArg], "|&") == 0) {
-                pipe_dup_err = 1;
-                break;
-            }
+    for (i = 0; i < cmd_list_size; ) {
+        char **arg_array;
+        int j;
+        int origErrorId = errorId;
+        Jim_Obj *cmdObj = Jim_ListGetIndex(interp, cmdList, i++);
+        int cmd_len = Jim_ListLength(interp, cmdObj);
+        Jim_Obj *sepObj = NULL;
+        if (i < cmd_list_size - 1) {
+            sepObj = Jim_ListGetIndex(interp, cmdList, i++);
         }
 
-        if (lastArg == firstArg) {
-            Jim_SetResultString(interp, "missing command to exec", -1);
-            goto error;
-        }
 
+        arg_array = Jim_Alloc((cmd_len + 1) * sizeof(*arg_array));
+        for (j = 0; j < cmd_len; j++) {
+            arg_array[j] = (char *)Jim_String(Jim_ListGetIndex(interp, cmdObj, j));
+        }
+        arg_array[j] = NULL;
 
-        arg_array[lastArg] = NULL;
-        if (lastArg == arg_count) {
+        if (sepObj == NULL) {
             outputId = lastOutputId;
             lastOutputId = -1;
         }
         else {
             if (pipe(pipeIds) != 0) {
                 Jim_SetResultErrno(interp, "couldn't create pipe");
+                Jim_Free(arg_array);
                 goto error;
             }
             outputId = pipeIds[1];
         }
 
 
-        if (pipe_dup_err) {
+        if (sepObj && Jim_CompareStringImmediate(interp, sepObj, "|&")) {
             errorId = outputId;
         }
 
 
 
 #ifdef __MINGW32__
-        phandle = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId);
+        phandle = JimStartWinProcess(interp, &arg_array[0], save_environ, inputId, outputId, errorId);
         if (phandle == JIM_BAD_PHANDLE) {
-            Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
+            Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[0]);
             goto error;
         }
 #else
-        i = strlen(arg_array[firstArg]);
+        int argv0_len = strlen(arg_array[0]);
 
 #ifdef HAVE_EXECVPE
         child_environ = Jim_GetEnviron();
@@ -6046,10 +6436,10 @@ badargs:
                 close(lastOutputId);
             }
 
-            execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ);
+            execvpe(arg_array[0], arg_array, child_environ);
 
             if (write(fileno(stderr), "couldn't exec \"", 15) &&
-                write(fileno(stderr), arg_array[firstArg], i) &&
+                write(fileno(stderr), arg_array[0], argv0_len) &&
                 write(fileno(stderr), "\"\n", 2)) {
 
             }
@@ -6065,6 +6455,7 @@ badargs:
 #endif
 
 
+        Jim_Free(arg_array);
 
         if (table->used == table->size) {
             table->size += WAIT_TABLE_GROW_BY;
@@ -6075,7 +6466,7 @@ badargs:
         table->info[table->used].flags = 0;
         table->used++;
 
-        pidPtr[numPids] = phandle;
+        pidPtr[numPids++] = phandle;
 
 
         errorId = origErrorId;
@@ -6103,7 +6494,6 @@ badargs:
     if (errorId != -1) {
         close(errorId);
     }
-    Jim_Free(arg_array);
 
     JimRestoreEnv(save_environ);
 
@@ -6111,10 +6501,6 @@ badargs:
 
 
   error:
-    if ((inPipePtr != NULL) && (*inPipePtr != -1)) {
-        close(*inPipePtr);
-        *inPipePtr = -1;
-    }
     if ((outPipePtr != NULL) && (*outPipePtr != -1)) {
         close(*outPipePtr);
         *outPipePtr = -1;
@@ -6141,6 +6527,35 @@ badargs:
     goto cleanup;
 }
 
+static int
+JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr,
+    int *outPipePtr, int *errFilePtr)
+{
+    int rc = -1;
+    int ret;
+
+    Jim_Obj *cmdList = Jim_NewListObj(interp, NULL, 0);
+    Jim_Obj *redirectList = Jim_NewListObj(interp, NULL, 0);
+    Jim_IncrRefCount(cmdList);
+    Jim_IncrRefCount(redirectList);
+
+    if (argc > 1 && Jim_CompareStringImmediate(interp, argv[0], "|")) {
+
+        ret = JimParsePipeline(interp, argc - 1, argv + 1, cmdList, redirectList);
+    }
+    else {
+
+        ret = JimParsePipelineLegacy(interp, argc, argv, cmdList, redirectList);
+    }
+    if (ret == JIM_OK) {
+
+        rc = JimExecPipeline(interp, cmdList, redirectList, pidArrayPtr, outPipePtr, errFilePtr);
+    }
+    Jim_DecrRefCount(interp, cmdList);
+    Jim_DecrRefCount(interp, redirectList);
+    return rc;
+}
+
 
 static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj)
 {
@@ -6170,10 +6585,11 @@ int Jim_execInit(Jim_Interp *interp)
     Jim_PackageProvideCheck(interp, "exec");
 
     waitinfo = JimAllocWaitInfoTable();
-    Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable);
+    Jim_RegisterCmd(interp, "exec", "arg ?arg ...? ?&?", 1, -1, Jim_ExecCmd, JimFreeWaitInfoTable, waitinfo, JIM_CMD_NOTAINT);
     waitinfo->refcount++;
-    Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable);
-    Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0);
+    Jim_RegisterCmd(interp, "wait", "?-nohang? ?pid?", 0, 2, Jim_WaitCommand, JimFreeWaitInfoTable, waitinfo, 0);
+    waitinfo->refcount++;
+    Jim_RegisterSimpleCmd(interp, "pid", "", 0, 0, Jim_PidCommand);
 
     return JIM_OK;
 }
@@ -6600,7 +7016,7 @@ static const jim_subcmd_type clock_command_table[] = {
 int Jim_clockInit(Jim_Interp *interp)
 {
     Jim_PackageProvideCheck(interp, "clock");
-    Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
+    Jim_RegisterSubCmd(interp, "clock", clock_command_table, NULL);
     return JIM_OK;
 }
 
@@ -6820,7 +7236,7 @@ static const jim_subcmd_type array_command_table[] = {
 int Jim_arrayInit(Jim_Interp *interp)
 {
     Jim_PackageProvideCheck(interp, "array");
-    Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
+    Jim_RegisterSubCmd(interp, "array", array_command_table, NULL);
     return JIM_OK;
 }
 int Jim_InitStaticExtensions(Jim_Interp *interp)
@@ -6911,7 +7327,8 @@ return JIM_OK;
 
 #define JIM_INTEGER_SPACE 24
 
-#if defined(DEBUG_SHOW_SCRIPT) || defined(DEBUG_SHOW_SCRIPT_TOKENS) || defined(JIM_DEBUG_COMMAND) || defined(DEBUG_SHOW_SUBST)
+#if defined(DEBUG_SHOW_SCRIPT) || defined(DEBUG_SHOW_SCRIPT_TOKENS) || defined(JIM_DEBUG_COMMAND) || defined(DEBUG_SHOW_EXPR_TOKENS) || defined(DEBUG_SHOW_EXPR)
+#define JIM_TT_NAME
 static const char *jim_tt_name(int type);
 #endif
 
@@ -6950,6 +7367,7 @@ static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
 static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv);
 static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr);
 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
+static int JimCallNative(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
 
 #define JIM_DICT_SUGAR 100
 
@@ -7168,7 +7586,7 @@ static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int id
     int i;
     int l1bytelen;
 
-    if (!l1 || !l2 || l1 > l2) {
+    if (!l1 || !l2 || l1 > l2 || idx > l2) {
         return -1;
     }
     if (idx < 0)
@@ -7409,13 +7827,11 @@ void *JimDefaultAllocator(void *ptr, size_t size)
         free(ptr);
         return NULL;
     }
+    else if (ptr) {
+        return realloc(ptr, size);
+    }
     else {
-        void *p = realloc(ptr, size);
-        if( p==0 ){
-            fprintf(stderr,"Out of memory\n");
-            exit(1);
-        }
-        return p;
+        return malloc(size);
     }
 }
 
@@ -7824,21 +8240,24 @@ static const Jim_HashTableType JimAssocDataHashTableType = {
     JimAssocDataHashTableValueDestructor
 };
 
-void Jim_InitStack(Jim_Stack *stack)
+void Jim_StackInit(Jim_Stack *stack, void (*freefunc) (void *ptr))
 {
     stack->len = 0;
     stack->maxlen = 0;
     stack->vector = NULL;
+    stack->freefunc = freefunc;
 }
 
-void Jim_FreeStack(Jim_Stack *stack)
+void Jim_StackFree(Jim_Stack *stack)
 {
-    Jim_Free(stack->vector);
-}
+    int i;
 
-int Jim_StackLen(Jim_Stack *stack)
-{
-    return stack->len;
+    if (stack->freefunc) {
+        for (i = 0; i < stack->len; i++) {
+            stack->freefunc(stack->vector[i]);
+        }
+    }
+    Jim_Free(stack->vector);
 }
 
 void Jim_StackPush(Jim_Stack *stack, void *element)
@@ -7861,21 +8280,6 @@ void *Jim_StackPop(Jim_Stack *stack)
     return stack->vector[stack->len];
 }
 
-void *Jim_StackPeek(Jim_Stack *stack)
-{
-    if (stack->len == 0)
-        return NULL;
-    return stack->vector[stack->len - 1];
-}
-
-void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
-{
-    int i;
-
-    for (i = 0; i < stack->len; i++)
-        freeFunc(stack->vector[i]);
-}
-
 
 
 #define JIM_TT_NONE    0
@@ -8793,6 +9197,7 @@ Jim_Obj *Jim_NewObj(Jim_Interp *interp)
     }
 
     objPtr->refCount = 0;
+    objPtr->taint = interp->taint;
 
 
     objPtr->prevObjPtr = NULL;
@@ -8853,6 +9258,8 @@ Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
     Jim_Obj *dupPtr;
 
     dupPtr = Jim_NewObj(interp);
+    dupPtr->taint = objPtr->taint;
+
     if (objPtr->bytes == NULL) {
 
         dupPtr->bytes = NULL;
@@ -9106,6 +9513,7 @@ void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
     int len;
     const char *str = Jim_GetString(appendObjPtr, &len);
     Jim_AppendString(interp, objPtr, str, len);
+    objPtr->taint |= appendObjPtr->taint;
 }
 
 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
@@ -9134,7 +9542,7 @@ int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
         const char *sA = Jim_GetString(aObjPtr, &Alen);
         const char *sB = Jim_GetString(bObjPtr, &Blen);
 
-        return Alen == Blen && memcmp(sA, sB, Alen) == 0;
+        return Alen == Blen && *sA == *sB && memcmp(sA, sB, Alen) == 0;
     }
 }
 
@@ -9598,11 +10006,21 @@ int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *
     }
 }
 
+
 static int qsortCompareStringPointers(const void *a, const void *b)
 {
     char *const *sa = (char *const *)a;
     char *const *sb = (char *const *)b;
 
+
+    if (strcmp(*sa, "--") == 0) {
+        return 1;
+    }
+    if (strcmp(*sb, "--") == 0) {
+
+        return -1;
+    }
+
     return strcmp(*sa, *sb);
 }
 
@@ -10029,6 +10447,7 @@ static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
     ParseTokenList tokenlist;
     Jim_Obj *fileNameObj;
     int line;
+    int oldtaint;
 
 
     fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
@@ -10047,6 +10466,11 @@ static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
     ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
 
 
+
+
+    oldtaint = interp->taint;
+    interp->taint = objPtr->taint;
+
     script = Jim_Alloc(sizeof(*script));
     memset(script, 0, sizeof(*script));
     script->inUse = 1;
@@ -10064,6 +10488,8 @@ static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
     Jim_FreeIntRep(interp, objPtr);
     Jim_SetIntRepPtr(objPtr, script);
     objPtr->typePtr = &scriptObjType;
+
+    interp->taint = oldtaint;
 }
 
 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
@@ -10101,7 +10527,7 @@ static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
 {
     if (--cmdPtr->inUse == 0) {
-        if (cmdPtr->isproc) {
+        if (cmdPtr->flags & JIM_CMD_ISPROC) {
             Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
             Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
             Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
@@ -10244,7 +10670,7 @@ static int JimCommandsHT_KeyCompare(void *privdata, const void *key1, const void
     int len1, len2;
     const char *str1 = Jim_GetStringNoQualifier((Jim_Obj *)key1, &len1);
     const char *str2 = Jim_GetStringNoQualifier((Jim_Obj *)key2, &len2);
-    return len1 == len2 && memcmp(str1, str2, len1) == 0;
+    return len1 == len2 && *str1 == *str2 && memcmp(str1, str2, len1) == 0;
 }
 
 static void JimCommandsHT_ValDestructor(void *interp, void *val)
@@ -10321,30 +10747,44 @@ static void JimCreateCommand(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Cmd *c
     Jim_ReplaceHashEntry(&interp->commands, nameObjPtr, cmd);
 }
 
-int Jim_CreateCommandObj(Jim_Interp *interp, Jim_Obj *cmdNameObj,
-    Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
+
+Jim_Cmd *Jim_RegisterCommand(Jim_Interp *interp, Jim_Obj *cmdNameObj,
+    Jim_CmdProc *cmdProc,
+    Jim_DelCmdProc *delProc,
+    const char *usage,
+    const char *help,
+    short minargs,
+    short maxargs,
+    int flags,
+    void *privData)
 {
+    JimPanic(((flags & JIM_CMD_ISPROC), "Jim_RegisterCommand called with JIM_CMD_ISPROC flag"));
     Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
 
 
     memset(cmdPtr, 0, sizeof(*cmdPtr));
     cmdPtr->inUse = 1;
+    cmdPtr->flags = flags;
     cmdPtr->u.native.delProc = delProc;
     cmdPtr->u.native.cmdProc = cmdProc;
+    cmdPtr->u.native.usage = usage;
+    cmdPtr->u.native.help = help;
+    cmdPtr->u.native.minargs = minargs;
+    cmdPtr->u.native.maxargs = maxargs;
     cmdPtr->u.native.privData = privData;
 
     Jim_IncrRefCount(cmdNameObj);
     JimCreateCommand(interp, cmdNameObj, cmdPtr);
     Jim_DecrRefCount(interp, cmdNameObj);
 
-    return JIM_OK;
+    return cmdPtr;
 }
 
-
 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
     Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
 {
-    return Jim_CreateCommandObj(interp, Jim_NewStringObj(interp, cmdNameStr, -1), cmdProc, privData, delProc);
+    Jim_RegisterCmd(interp, cmdNameStr, NULL, 0, -1, cmdProc, delProc, privData, 0);
+    return JIM_OK;
 }
 
 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
@@ -10406,6 +10846,11 @@ static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Ob
                     }
                     else {
                         initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
+                        if (!initObjPtr) {
+                            Jim_SetResultFormatted(interp, "Could not resolve upvar \"%#s\"'s value", nameObjPtr);
+                            Jim_DecrRefCount(interp, nameObjPtr);
+                            return JIM_ERR;
+                        }
                     }
                     break;
 
@@ -10461,7 +10906,7 @@ static const char *Jim_memrchr(const char *p, int c, int len)
 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *nameObjPtr)
 {
 #ifdef jim_ext_namespace
-    if (cmdPtr->isproc) {
+    if (cmdPtr->flags & JIM_CMD_ISPROC) {
         int len;
         const char *cmdname = Jim_GetStringNoQualifier(nameObjPtr, &len);
 
@@ -10497,7 +10942,7 @@ static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr
     assert(cmdPtr);
     memset(cmdPtr, 0, sizeof(*cmdPtr));
     cmdPtr->inUse = 1;
-    cmdPtr->isproc = 1;
+    cmdPtr->flags = JIM_CMD_ISPROC;
     cmdPtr->u.proc.argListObjPtr = argListObjPtr;
     cmdPtr->u.proc.argListLen = argListLen;
     cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
@@ -10697,7 +11142,7 @@ Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
         Jim_IncrRefCount(interp->framePtr->nsObj);
         Jim_DecrRefCount(interp, qualifiedNameObj);
     }
-    while (cmd->u.proc.upcall) {
+    while ((cmd->flags & JIM_CMD_ISPROC) && cmd->u.proc.upcall) {
         cmd = cmd->prevCmd;
     }
     return cmd;
@@ -10779,7 +11224,7 @@ static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
 
 
 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
-static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
+static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
 
 static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv)
 {
@@ -10843,15 +11288,18 @@ static Jim_VarVal *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Ji
 
 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
 {
-    int err;
+    int ret = JIM_OK;
     Jim_VarVal *vv;
 
     switch (SetVariableFromAny(interp, nameObjPtr)) {
         case JIM_DICT_SUGAR:
-            return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
+            ret = JimDictSugarSet(interp, nameObjPtr, valObjPtr);
+            break;
 
         case JIM_ERR:
-            JimCreateVariable(interp, nameObjPtr, valObjPtr);
+            if (JimCreateVariable(interp, nameObjPtr, valObjPtr) == NULL) {
+                ret = JIM_ERR;
+            }
             break;
 
         case JIM_OK:
@@ -10866,13 +11314,16 @@ int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
 
                 savedCallFrame = interp->framePtr;
                 interp->framePtr = vv->linkFramePtr;
-                err = Jim_SetVariable(interp, vv->objPtr, valObjPtr);
+                ret = Jim_SetVariable(interp, vv->objPtr, valObjPtr);
                 interp->framePtr = savedCallFrame;
-                if (err != JIM_OK)
-                    return err;
             }
+            break;
     }
-    return JIM_OK;
+    if (ret != JIM_OK && valObjPtr->refCount == 0) {
+
+        Jim_FreeNewObj(interp, valObjPtr);
+    }
+    return ret;
 }
 
 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
@@ -11146,6 +11597,7 @@ static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
     JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
 
     varObjPtr = Jim_NewStringObj(interp, str, p - str);
+    varObjPtr->taint = objPtr->taint;
 
     p++;
     keyLen = (str + len) - p;
@@ -11155,6 +11607,7 @@ static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
 
 
     keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
+    keyObjPtr->taint = objPtr->taint;
 
     Jim_IncrRefCount(varObjPtr);
     Jim_IncrRefCount(keyObjPtr);
@@ -11357,7 +11810,7 @@ static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
             }
             Jim_DecrRefCount(interp, cmdNameObj);
         }
-        Jim_FreeStack(localCommands);
+        Jim_StackFree(localCommands);
         Jim_Free(localCommands);
     }
     return JIM_OK;
@@ -11692,7 +12145,7 @@ static Jim_Obj *JimProcForEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame)
     if (frame == interp->evalFrame || (frame->cmd && frame->cmd->cmdNameObj)) {
         Jim_EvalFrame *e;
         for (e = frame->parent; e; e = e->parent) {
-            if (e->cmd && e->cmd->isproc && e->cmd->cmdNameObj) {
+            if (e->cmd && (e->cmd->flags & JIM_CMD_ISPROC) && e->cmd->cmdNameObj) {
                 break;
             }
         }
@@ -11727,12 +12180,12 @@ static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
     Jim_IncrRefCount(stackTraceObj);
     Jim_DecrRefCount(interp, interp->stackTrace);
     interp->stackTrace = stackTraceObj;
-    interp->errorFlag = 1;
+    interp->hasErrorStackTrace = 1;
 }
 
 static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script)
 {
-    if (!interp->errorFlag) {
+    if (!interp->hasErrorStackTrace) {
         int i;
         Jim_Obj *stackTrace = Jim_NewListObj(interp, NULL, 0);
 
@@ -11941,6 +12394,7 @@ Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
     objPtr = Jim_NewObj(interp);
     objPtr->typePtr = &intObjType;
     objPtr->bytes = NULL;
+    objPtr->taint = 0;
     objPtr->internalRep.wideValue = wideValue;
     return objPtr;
 }
@@ -12080,6 +12534,7 @@ Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
     objPtr = Jim_NewObj(interp);
     objPtr->typePtr = &doubleObjType;
     objPtr->bytes = NULL;
+    objPtr->taint = 0;
     objPtr->internalRep.doubleValue = doubleValue;
     return objPtr;
 }
@@ -12461,6 +12916,7 @@ static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
                 continue;
             elementPtr = JimParserGetTokenObj(interp, &parser);
             Jim_SetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
+            elementPtr->taint = objPtr->taint;
             ListAppendElement(objPtr, elementPtr);
         }
     }
@@ -12475,6 +12931,7 @@ Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
     objPtr = Jim_NewObj(interp);
     objPtr->typePtr = &listObjType;
     objPtr->bytes = NULL;
+    objPtr->taint = 0;
     objPtr->internalRep.listValue.ele = NULL;
     objPtr->internalRep.listValue.len = 0;
     objPtr->internalRep.listValue.maxLen = 0;
@@ -12771,6 +13228,7 @@ static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *co
     memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
     for (i = 0; i < elemc; ++i) {
         point[i] = elemVec[i];
+        listPtr->taint |= point[i]->taint;
         Jim_IncrRefCount(point[i]);
     }
     listPtr->internalRep.listValue.len += elemc;
@@ -12903,6 +13361,7 @@ static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
         idx = listPtr->internalRep.listValue.len + idx;
     Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
     listPtr->internalRep.listValue.ele[idx] = newObjPtr;
+    listPtr->taint |= newObjPtr->taint;
     Jim_IncrRefCount(newObjPtr);
     return JIM_OK;
 }
@@ -12940,8 +13399,9 @@ int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
         goto err;
     Jim_InvalidateStringRep(objPtr);
     Jim_InvalidateStringRep(varObjPtr);
-    if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
-        goto err;
+    if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
+        return JIM_ERR;
+    }
     Jim_SetResult(interp, varObjPtr);
     return JIM_OK;
   err:
@@ -13339,6 +13799,7 @@ static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
             dict->table[dict->len++] = valueObjPtr;
 
         }
+        objPtr->taint |= keyObjPtr->taint | valueObjPtr->taint;
         return JIM_OK;
     }
 }
@@ -13450,7 +13911,6 @@ int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
         }
         varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
-            Jim_FreeNewObj(interp, varObjPtr);
             return JIM_ERR;
         }
     }
@@ -13471,6 +13931,9 @@ int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
                     goto err;
                 }
             }
+            if (newObjPtr) {
+                varObjPtr->taint |= newObjPtr->taint;
+            }
             break;
         }
 
@@ -13495,7 +13958,7 @@ int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
     Jim_InvalidateStringRep(objPtr);
     Jim_InvalidateStringRep(varObjPtr);
     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
-        goto err;
+        return JIM_ERR;
     }
 
     if (!(flags & JIM_NORESULT)) {
@@ -13591,6 +14054,9 @@ static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
     else if (idx < 0) {
         idx = -INT_MAX;
     }
+    else if (idx > INT_MAX) {
+        goto badindex;
+    }
 
 
     Jim_FreeIntRep(interp, objPtr);
@@ -13723,6 +14189,9 @@ enum
 
     JIM_EXPROP_STREQ,
     JIM_EXPROP_STRNE,
+    JIM_EXPROP_STRGLOB,
+    JIM_EXPROP_STRRE,
+
     JIM_EXPROP_STRIN,
     JIM_EXPROP_STRNI,
     JIM_EXPROP_STRLT,
@@ -13866,6 +14335,13 @@ static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
             case JIM_EXPROP_NOT:
                 wC = !bA;
                 break;
+            case JIM_EXPROP_UNARYPLUS:
+            case JIM_EXPROP_UNARYMINUS:
+                rc = JIM_ERR;
+                Jim_SetResultFormatted(interp,
+                    "can't use non-numeric string as operand of \"%s\"",
+                        node->type == JIM_EXPROP_UNARYPLUS ? "+" : "-");
+                break;
             default:
                 abort();
         }
@@ -14293,7 +14769,25 @@ static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valOb
     return 0;
 }
 
+static int JimRegexpMatch(Jim_Interp *interp, Jim_Obj *patternObj, Jim_Obj *objPtr)
+{
+    Jim_Obj *argv[3];
+    int argc = 0;
+    long eq;
+    int rc;
+
+    argv[argc++] = Jim_NewStringObj(interp, "regexp", -1);
+    argv[argc++] = patternObj;
+    argv[argc++] = objPtr;
+
+    rc = Jim_EvalObjVector(interp, argc, argv);
+
+    if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
+        eq = -rc;
+    }
 
+    return eq;
+}
 
 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
 {
@@ -14338,11 +14832,22 @@ static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
         case JIM_EXPROP_STRNI:
             wC = !JimSearchList(interp, B, A);
             break;
+        case JIM_EXPROP_STRGLOB:
+            wC = Jim_StringMatchObj(interp, B, A, 0);
+            break;
+        case JIM_EXPROP_STRRE:
+            wC = JimRegexpMatch(interp, B, A);
+            if (wC < 0) {
+                rc = JIM_ERR;
+                goto error;
+            }
+            break;
         default:
             abort();
     }
     Jim_SetResultInt(interp, wC);
 
+error:
     Jim_DecrRefCount(interp, A);
     Jim_DecrRefCount(interp, B);
 
@@ -14467,6 +14972,8 @@ static const struct Jim_ExprOperator Jim_ExprOperators[] = {
 
     OPRINIT("eq", 60, 2, JimExprOpStrBin),
     OPRINIT("ne", 60, 2, JimExprOpStrBin),
+    OPRINIT("=*", 60, 2, JimExprOpStrBin),
+    OPRINIT("=~", 60, 2, JimExprOpStrBin),
 
     OPRINIT("in", 55, 2, JimExprOpStrBin),
     OPRINIT("ni", 55, 2, JimExprOpStrBin),
@@ -15089,7 +15596,7 @@ static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenL
     builder.nodes = Jim_Alloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
     memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
     builder.next = builder.nodes;
-    Jim_InitStack(&builder.stack);
+    Jim_StackInit(&builder.stack, NULL);
 
     rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);
 
@@ -15103,7 +15610,7 @@ static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenL
     }
 
 
-    Jim_FreeStack(&builder.stack);
+    Jim_StackFree(&builder.stack);
 
     if (rc != JIM_OK) {
         ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes);
@@ -15138,6 +15645,9 @@ static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
 
     exprText = Jim_GetString(objPtr, &exprTextLen);
 
+    int oldtaint = interp->taint;
+    interp->taint = objPtr->taint;
+
 
     ScriptTokenListInit(&tokenlist);
 
@@ -15204,6 +15714,9 @@ static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
     Jim_FreeIntRep(interp, objPtr);
     Jim_SetIntRepPtr(objPtr, expr);
     objPtr->typePtr = &exprObjType;
+
+    interp->taint = oldtaint;
+
     return rc;
 }
 
@@ -15980,10 +16493,6 @@ static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
     jim_wide wideValue, increment = 1;
     Jim_Obj *intObjPtr;
 
-    if (argc != 2 && argc != 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
-        return JIM_ERR;
-    }
     if (argc == 3) {
         if (Jim_GetWideExpr(interp, argv[2], &increment) != JIM_OK)
             return JIM_ERR;
@@ -15999,7 +16508,6 @@ static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
     if (!intObjPtr || Jim_IsShared(intObjPtr)) {
         intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
-            Jim_FreeNewObj(interp, intObjPtr);
             return JIM_ERR;
         }
     }
@@ -16154,12 +16662,17 @@ tailcall:
         (retcode = JimTraceCallback(interp, "cmd", objc, objv)) == JIM_OK) {
 
         Jim_SetEmptyResult(interp);
-        if (cmdPtr->isproc) {
+        interp->taint = Jim_CalcTaint(objc, objv);
+        if (cmdPtr->flags & JIM_CMD_ISPROC) {
             retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
         }
+        else if ((cmdPtr->flags & JIM_CMD_NOTAINT) && Jim_CheckTaint(interp, JIM_TAINT_ANY)) {
+            Jim_SetTaintError(interp, 1, objv);
+            retcode = JIM_ERR;
+        }
         else {
-            interp->cmdPrivData = cmdPtr->u.native.privData;
-            retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
+            retcode = JimCallNative(interp, cmdPtr, objc, objv);
+
         }
         if (retcode == JIM_ERR) {
             JimSetErrorStack(interp, NULL);
@@ -16299,6 +16812,8 @@ static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * tok
     Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
     Jim_Obj *objPtr;
     char *s;
+    int taint = 0;
+    const char *error_action = NULL;
 
     if (tokens <= JIM_EVAL_SINTV_LEN)
         intv = sintv;
@@ -16316,14 +16831,16 @@ static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * tok
                     tokens = i;
                     continue;
                 }
-
+                error_action = "break";
 
             case JIM_CONTINUE:
                 if (flags & JIM_SUBST_FLAG) {
                     intv[i] = NULL;
                     continue;
                 }
-
+                if (!error_action) {
+                    error_action = "continue";
+                }
 
             default:
                 while (i--) {
@@ -16332,8 +16849,12 @@ static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * tok
                 if (intv != sintv) {
                     Jim_Free(intv);
                 }
+                if (error_action) {
+                    Jim_SetResultFormatted(interp, "invoked \"%s\" outside of a loop", error_action);
+                }
                 return NULL;
         }
+        taint |= intv[i]->taint;
         Jim_IncrRefCount(intv[i]);
         Jim_String(intv[i]);
         totlen += intv[i]->length;
@@ -16347,6 +16868,7 @@ static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * tok
     }
 
     objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
+    objPtr->taint = taint;
 
     if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
         && token[2].type == JIM_TT_VAR) {
@@ -16382,11 +16904,112 @@ static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * tok
     return objPtr;
 }
 
+#define JIM_LSUBST_LINE 0x0001
 
-static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
+static int JimListSubstObj(Jim_Interp *interp, struct Jim_Obj *objPtr, unsigned flags)
 {
+    int i;
+    ScriptObj *script;
+    ScriptToken *token;
+    Jim_Obj *resultListObj;
     int retcode = JIM_OK;
-    Jim_EvalFrame frame;
+
+    Jim_IncrRefCount(objPtr);
+    script = JimGetScript(interp, objPtr);
+    if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
+        JimSetErrorStack(interp, script);
+        Jim_DecrRefCount(interp, objPtr);
+        return JIM_ERR;
+    }
+
+    token = script->token;
+
+    script->inUse++;
+
+
+    resultListObj = Jim_NewListObj(interp, NULL, 0);
+
+
+    for (i = 0; i < script->len && retcode == JIM_OK; ) {
+        int argc;
+        int j;
+        Jim_Obj *lineListObj = resultListObj;
+
+
+        argc = token[i].objPtr->internalRep.scriptLineValue.argc;
+        script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
+
+
+        i++;
+
+        if (flags & JIM_LSUBST_LINE) {
+            lineListObj = Jim_NewListObj(interp, NULL, 0);
+        }
+
+
+        for (j = 0; j < argc; j++) {
+            long wordtokens = 1;
+            int expand = 0;
+            Jim_Obj *wordObjPtr = NULL;
+
+            if (token[i].type == JIM_TT_WORD) {
+                wordtokens = JimWideValue(token[i++].objPtr);
+                if (wordtokens < 0) {
+                    expand = 1;
+                    wordtokens = -wordtokens;
+                }
+            }
+
+
+            wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
+
+            if (!wordObjPtr) {
+                if (retcode == JIM_OK) {
+                    retcode = JIM_ERR;
+                }
+                break;
+            }
+
+            Jim_IncrRefCount(wordObjPtr);
+            i += wordtokens;
+
+            if (!expand) {
+                Jim_ListAppendElement(interp, lineListObj, wordObjPtr);
+            }
+            else {
+                int k;
+
+                for (k = 0; k < Jim_ListLength(interp, wordObjPtr); k++) {
+                    Jim_ListAppendElement(interp, lineListObj, Jim_ListGetIndex(interp, wordObjPtr, k));
+                }
+            }
+            Jim_DecrRefCount(interp, wordObjPtr);
+        }
+
+        if (flags & JIM_LSUBST_LINE) {
+            Jim_ListAppendElement(interp, resultListObj, lineListObj);
+        }
+    }
+
+    Jim_FreeIntRep(interp, objPtr);
+    objPtr->typePtr = &scriptObjType;
+    Jim_SetIntRepPtr(objPtr, script);
+    Jim_DecrRefCount(interp, objPtr);
+
+    if (retcode == JIM_OK) {
+        Jim_SetResult(interp, resultListObj);
+    }
+    else {
+        Jim_FreeNewObj(interp, resultListObj);
+    }
+
+    return retcode;
+}
+
+static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
+{
+    int retcode = JIM_OK;
+    Jim_EvalFrame frame;
 
     JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
 
@@ -16443,7 +17066,7 @@ int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
     }
     if (script->len == 3
         && token[1].objPtr->typePtr == &commandObjType
-        && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
+        && (token[1].objPtr->internalRep.cmdValue.cmdPtr->flags & JIM_CMD_ISPROC) == 0
         && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
         && token[2].objPtr->typePtr == &variableObjType) {
 
@@ -16464,7 +17087,7 @@ int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
     JimPushEvalFrame(interp, &frame, scriptObjPtr);
 
 
-    interp->errorFlag = 0;
+    interp->hasErrorStackTrace = 0;
     argv = sargv;
 
     for (i = 0; i < script->len && retcode == JIM_OK; ) {
@@ -16580,6 +17203,7 @@ int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
         if (retcode == JIM_OK && argc) {
 
             retcode = JimInvokeCommand(interp, argc, argv);
+            interp->taint = 0;
 
             if (Jim_CheckSignal(interp)) {
                 retcode = JIM_SIGNAL;
@@ -16641,43 +17265,58 @@ static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argVa
     return retcode;
 }
 
-static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
+static Jim_Obj *JimCmdUsage(Jim_Interp *interp, Jim_Obj *cmdNameObj, Jim_Cmd *cmd)
 {
+    Jim_Obj *usage = Jim_DuplicateObj(interp, cmdNameObj);
 
-    Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
-    int i;
-
-    for (i = 0; i < cmd->u.proc.argListLen; i++) {
-        Jim_AppendString(interp, argmsg, " ", 1);
+    if (cmd->flags & JIM_CMD_ISPROC) {
+        int i;
+        for (i = 0; i < cmd->u.proc.argListLen; i++) {
+            Jim_AppendString(interp, usage, " ", 1);
 
-        if (i == cmd->u.proc.argsPos) {
-            if (cmd->u.proc.arglist[i].defaultObjPtr) {
+            if (i == cmd->u.proc.argsPos) {
+                if (cmd->u.proc.arglist[i].defaultObjPtr) {
 
-                Jim_AppendString(interp, argmsg, "?", 1);
-                Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
-                Jim_AppendString(interp, argmsg, " ...?", -1);
-            }
-            else {
+                    Jim_AppendString(interp, usage, "?", 1);
+                    Jim_AppendObj(interp, usage, cmd->u.proc.arglist[i].defaultObjPtr);
+                    Jim_AppendString(interp, usage, " ...?", -1);
+                }
+                else {
 
-                Jim_AppendString(interp, argmsg, "?arg ...?", -1);
-            }
-        }
-        else {
-            if (cmd->u.proc.arglist[i].defaultObjPtr) {
-                Jim_AppendString(interp, argmsg, "?", 1);
-                Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
-                Jim_AppendString(interp, argmsg, "?", 1);
+                    Jim_AppendString(interp, usage, "?arg ...?", -1);
+                }
             }
             else {
-                const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
-                if (*arg == '&') {
-                    arg++;
+                if (cmd->u.proc.arglist[i].defaultObjPtr) {
+                    Jim_AppendString(interp, usage, "?", 1);
+                    Jim_AppendObj(interp, usage, cmd->u.proc.arglist[i].nameObjPtr);
+                    Jim_AppendString(interp, usage, "?", 1);
+                }
+                else {
+                    const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
+                    if (*arg == '&') {
+                        arg++;
+                    }
+                    Jim_AppendString(interp, usage, arg, -1);
                 }
-                Jim_AppendString(interp, argmsg, arg, -1);
             }
         }
     }
-    Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
+    else if (cmd->u.native.usage) {
+        if (*cmd->u.native.usage) {
+            Jim_AppendStrings(interp, usage, " ", cmd->u.native.usage, NULL);
+        }
+    }
+    else {
+        Jim_AppendString(interp, usage, " ...", -1);
+    }
+
+    return usage;
+}
+
+static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
+{
+    Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", JimCmdUsage(interp, procNameObj, cmd));
 }
 
 #ifdef jim_ext_namespace
@@ -16819,6 +17458,36 @@ badargset:
     return retcode;
 }
 
+static int JimCallNative(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
+{
+    int argsok = 1;
+    int ret;
+
+
+    if (argc - 1 < cmd->u.native.minargs) {
+        argsok = 0;
+    }
+    else if (cmd->u.native.maxargs >= 0 && argc - 1 > cmd->u.native.maxargs) {
+        argsok = 0;
+    }
+    else if (cmd->u.native.maxargs < -1 && (argc - 1) % -cmd->u.native.maxargs != 0) {
+
+        argsok = 0;
+    }
+    if (argsok) {
+        interp->cmdPrivData = cmd->u.native.privData;
+        ret = cmd->u.native.cmdProc(interp, argc, argv);
+        if (ret != JIM_USAGE) {
+            return ret;
+        }
+
+    }
+
+    //printf("Wrong args for %s, argc=%d, minargs=%d, maxargs=%d\n", Jim_String(argv[0]), argc, cmd->u.native.minargs, cmd->u.native.maxargs);
+    Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", JimCmdUsage(interp, argv[0], cmd));
+    return JIM_ERR;
+}
+
 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
 {
     int retval;
@@ -16866,51 +17535,38 @@ int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
 
 #include <sys/stat.h>
 
-static Jim_Obj *JimReadTextFile(Jim_Interp *interp, const char *filename)
+int Jim_EvalFile(Jim_Interp *interp, const char *filename)
 {
-    jim_stat_t sb;
-    int fd;
+    FILE *fp;
     char *buf;
+    Jim_Obj *scriptObjPtr;
+    Jim_Obj *filenameObj, *oldFilenameObj;
+    int retcode = JIM_ERR;
     int readlen;
+#define READ_BUF_SIZE 256
 
-    if (Jim_Stat(filename, &sb) == -1 || (fd = open(filename, O_RDONLY | O_TEXT, 0666)) < 0) {
+   if ((fp = fopen(filename, "rt")) == NULL) {
         Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
-        return NULL;
-    }
-    buf = Jim_Alloc(sb.st_size + 1);
-    readlen = read(fd, buf, sb.st_size);
-    close(fd);
-    if (readlen < 0) {
-        Jim_Free(buf);
-        Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
-        return NULL;
+        return JIM_ERR;
     }
-    else {
-        Jim_Obj *objPtr;
-        buf[readlen] = 0;
+    scriptObjPtr = Jim_NewStringObj(interp, NULL, 0);
 
-        objPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
-
-        return objPtr;
+    buf = Jim_Alloc(READ_BUF_SIZE);
+    while ((readlen = fread(buf, 1, READ_BUF_SIZE, fp)) > 0) {
+        Jim_AppendString(interp, scriptObjPtr, buf, readlen);
     }
-}
-
-
-int Jim_EvalFile(Jim_Interp *interp, const char *filename)
-{
-    Jim_Obj *filenameObj;
-    Jim_Obj *oldFilenameObj;
-    Jim_Obj *scriptObjPtr;
-    int retcode;
-
-    scriptObjPtr = JimReadTextFile(interp, filename);
-    if (!scriptObjPtr) {
-        return JIM_ERR;
+    Jim_Free(buf);
+    if (ferror(fp)) {
+        fclose(fp);
+        Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
+        Jim_FreeNewObj(interp, scriptObjPtr);
+        return retcode;
     }
+    fclose(fp);
+
 
     filenameObj = Jim_NewStringObj(interp, filename, -1);
     Jim_SetSourceInfo(interp, scriptObjPtr, filenameObj, 1);
-
     oldFilenameObj = JimPushInterpObj(interp->currentFilenameObj, filenameObj);
 
     retcode = Jim_EvalObj(interp, scriptObjPtr);
@@ -16980,6 +17636,7 @@ static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags
     struct JimParserCtx parser;
     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
     ParseTokenList tokenlist;
+    int oldtaint;
 
 
     ScriptTokenListInit(&tokenlist);
@@ -16995,6 +17652,9 @@ static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags
             parser.tline);
     }
 
+    oldtaint = interp->taint;
+    interp->taint = objPtr->taint;
+
 
     script->inUse = 1;
     script->substFlags = flags;
@@ -17021,6 +17681,7 @@ static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags
     Jim_FreeIntRep(interp, objPtr);
     Jim_SetIntRepPtr(objPtr, script);
     objPtr->typePtr = &scriptObjType;
+    interp->taint = oldtaint;
     return JIM_OK;
 }
 
@@ -17052,23 +17713,55 @@ int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPt
     return JIM_OK;
 }
 
-void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
+static Jim_Obj *JimJoinCmdArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
     Jim_Obj *objPtr;
     Jim_Obj *listObjPtr;
 
-    JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0"));
-
     listObjPtr = Jim_NewListObj(interp, argv, argc);
 
-    if (msg && *msg) {
-        Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
-    }
     Jim_IncrRefCount(listObjPtr);
     objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
     Jim_DecrRefCount(interp, listObjPtr);
+    Jim_IncrRefCount(objPtr);
+
+    return objPtr;
+}
+
+void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
+{
+    Jim_Obj *objPtr = JimJoinCmdArgs(interp, argc, argv);
+    if (*msg) {
+        Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s %s\"", objPtr, msg);
+    }
+    else {
+        Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
+    }
+    Jim_DecrRefCount(interp, objPtr);
+}
+
+int Jim_CalcTaint(int argc, Jim_Obj *const *argv)
+{
+    int taint = 0;
+#ifdef JIM_TAINT
+    int i;
+    for (i = 0; i < argc; i++) {
+        if (argv[i]) {
+            taint |= argv[i]->taint;
+        }
+    }
+#endif
+    return taint;
+}
 
-    Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
+void Jim_SetTaintError(Jim_Interp *interp, int cmdargs, Jim_Obj *const *argv)
+{
+#ifdef JIM_TAINT
+    Jim_Obj *objPtr = JimJoinCmdArgs(interp, cmdargs, argv);
+    Jim_SetResultFormatted(interp, "%#s: tainted data", objPtr);
+    Jim_DecrRefCount(interp, objPtr);
+    Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "TAINTED", -1));
+#endif
 }
 
 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
@@ -17102,38 +17795,51 @@ static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht,
 }
 
 
-#define JIM_CMDLIST_COMMANDS 0
-#define JIM_CMDLIST_PROCS 1
-#define JIM_CMDLIST_CHANNELS 2
+#define JIM_CMDLIST_COMMANDS 1
+#define JIM_CMDLIST_PROCS 2
+#define JIM_CMDLIST_ALIASES 4
+#define JIM_CMDLIST_CHANNELS 8
+
+#define JIM_CMDLIST_ALL 0x1000
 
 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
     Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
 {
     Jim_Cmd *cmdPtr = (Jim_Cmd *)value;
+    int match = 1;
+
+    if ((type & JIM_CMDLIST_PROCS) && !(cmdPtr->flags & JIM_CMD_ISPROC)) {
+
+        return;
+    }
+    if ((type & JIM_CMDLIST_CHANNELS) && !(cmdPtr->flags & JIM_CMD_ISCHANNEL)) {
 
-    if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
+        return;
+    }
+    if ((type & JIM_CMDLIST_ALIASES) && !(cmdPtr->flags & JIM_CMD_ISALIAS)) {
+
+        return;
+    }
+    if (!(type & JIM_CMDLIST_ALL) && strchr(Jim_String(keyObj), ' ')) {
 
         return;
     }
 
     Jim_IncrRefCount(keyObj);
 
-    if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, keyObj) >= 0) {
-        int match = 1;
-        if (patternObj) {
-            int plen, slen;
-            const char *pattern = Jim_GetStringNoQualifier(patternObj, &plen);
-            const char *str = Jim_GetStringNoQualifier(keyObj, &slen);
+    if (patternObj) {
+        int plen, slen;
+        const char *pattern = Jim_GetStringNoQualifier(patternObj, &plen);
+        const char *str = Jim_GetStringNoQualifier(keyObj, &slen);
 #ifdef JIM_NO_INTROSPECTION
 
-            match = (JimStringCompareUtf8(pattern, plen, str, slen, 0) == 0);
+        match = (JimStringCompareUtf8(pattern, plen, str, slen, 0) == 0);
 #else
-            match = JimGlobMatch(pattern, plen, str, slen, 0);
+        match = JimGlobMatch(pattern, plen, str, slen, 0);
 #endif
-        }
-        if (match) {
-            Jim_ListAppendElement(interp, listObjPtr, keyObj);
-        }
+    }
+    if (match) {
+        Jim_ListAppendElement(interp, listObjPtr, keyObj);
     }
     Jim_DecrRefCount(interp, keyObj);
 }
@@ -17144,10 +17850,9 @@ static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int
 }
 
 
-#define JIM_VARLIST_GLOBALS 0
-#define JIM_VARLIST_LOCALS 1
-#define JIM_VARLIST_VARS 2
-#define JIM_VARLIST_MASK 0x000f
+#define JIM_VARLIST_GLOBALS 1
+#define JIM_VARLIST_LOCALS 2
+#define JIM_VARLIST_VARS 4
 
 #define JIM_VARLIST_VALUES 0x1000
 
@@ -17156,7 +17861,7 @@ static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
 {
     Jim_VarVal *vv = (Jim_VarVal *)value;
 
-    if ((type & JIM_VARLIST_MASK) != JIM_VARLIST_LOCALS || vv->linkFramePtr == NULL) {
+    if (!(type & JIM_VARLIST_LOCALS) || vv->linkFramePtr == NULL) {
         if (patternObj == NULL || Jim_StringMatchObj(interp, patternObj, keyObj, 0)) {
             Jim_ListAppendElement(interp, listObjPtr, keyObj);
             if (type & JIM_VARLIST_VALUES) {
@@ -17169,11 +17874,11 @@ static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
 
 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
 {
-    if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
+    if ((mode & JIM_VARLIST_LOCALS) && interp->framePtr == interp->topFramePtr) {
         return interp->emptyObj;
     }
     else {
-        Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
+        Jim_CallFrame *framePtr = (mode & JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
         return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch,
             mode);
     }
@@ -17246,10 +17951,6 @@ static int JimInfoFrame(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objP
 
 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc != 2 && argc != 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
-        return JIM_ERR;
-    }
     if (argc == 3) {
         if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
             Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
@@ -17305,11 +18006,7 @@ static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, i
     double doubleValue, doubleRes = 0;
     int i = 2;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
-        return JIM_ERR;
-    }
-    else if (argc == 2) {
+    if (argc == 2) {
         if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
             if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
                 return JIM_ERR;
@@ -17402,10 +18099,6 @@ static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv
 
 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc != 2 && argc != 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
-        return JIM_ERR;
-    }
     if (argc == 2) {
         Jim_Obj *objPtr;
 
@@ -17465,11 +18158,6 @@ static int JimCheckLoopRetcode(Jim_Interp *interp, int retval)
 
 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc != 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "condition body");
-        return JIM_ERR;
-    }
-
 
     while (1) {
         int boolean = 0, retval;
@@ -17507,11 +18195,6 @@ static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv
     Jim_Obj *varNamePtr = NULL;
     Jim_Obj *stopVarNamePtr = NULL;
 
-    if (argc != 5) {
-        Jim_WrongNumArgs(interp, 1, argv, "start test next body");
-        return JIM_ERR;
-    }
-
 
     if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
         return retval;
@@ -17687,11 +18370,6 @@ static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
     jim_wide incr = 1;
     Jim_Obj *bodyObjPtr;
 
-    if (argc < 4 || argc > 6) {
-        Jim_WrongNumArgs(interp, 1, argv, "var ?first? limit ?incr? body");
-        return JIM_ERR;
-    }
-
     retval = Jim_GetWideExpr(interp, argv[2], &i);
     if (argc > 4 && retval == JIM_OK) {
         retval = Jim_GetWideExpr(interp, argv[3], &limit);
@@ -17742,9 +18420,6 @@ static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
             else {
                 objPtr = Jim_NewIntObj(interp, i);
                 retval = Jim_SetVariable(interp, argv[1], objPtr);
-                if (retval != JIM_OK) {
-                    Jim_FreeNewObj(interp, objPtr);
-                }
             }
         }
     }
@@ -17790,9 +18465,8 @@ static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *arg
     Jim_Obj *script;
     Jim_Obj *resultObj;
 
-    if (argc < 4 || argc % 2 != 0) {
-        Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
-        return JIM_ERR;
+    if (argc % 2 != 0) {
+        return JIM_USAGE;
     }
     script = argv[argc - 1];
     numargs = (argc - 1 - 1);
@@ -17846,10 +18520,10 @@ static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *arg
 
                     valObj = interp->emptyObj;
                 }
-
-                Jim_IncrRefCount(valObj);
+                // XXX
+                //Jim_IncrRefCount(valObj);
                 result = Jim_SetVariable(interp, varName, valObj);
-                Jim_DecrRefCount(interp, valObj);
+                //Jim_DecrRefCount(interp, valObj);
                 if (result != JIM_OK) {
                     goto err;
                 }
@@ -17905,11 +18579,6 @@ static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
     Jim_ListIter iter;
     Jim_Obj *resultObj;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
-        return JIM_ERR;
-    }
-
     JimListIterInit(&iter, argv[1]);
 
     for (i = 2; i < argc; i++) {
@@ -17935,48 +18604,48 @@ static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
     int boolean, retval, current = 1, falsebody = 0;
 
-    if (argc >= 3) {
-        while (1) {
+    while (1) {
 
-            if (current >= argc)
-                goto err;
-            if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
-                != JIM_OK)
-                return retval;
+        if (current >= argc) {
+            return JIM_USAGE;
+        }
+        if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
+            != JIM_OK)
+            return retval;
 
-            if (current >= argc)
-                goto err;
-            if (Jim_CompareStringImmediate(interp, argv[current], "then"))
-                current++;
+        if (current >= argc) {
+            return JIM_USAGE;
+        }
+        if (Jim_CompareStringImmediate(interp, argv[current], "then"))
+            current++;
 
-            if (current >= argc)
-                goto err;
-            if (boolean)
-                return Jim_EvalObj(interp, argv[current]);
+        if (current >= argc) {
+            return JIM_USAGE;
+        }
+        if (boolean)
+            return Jim_EvalObj(interp, argv[current]);
 
-            if (++current >= argc) {
-                Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
-                return JIM_OK;
-            }
-            falsebody = current++;
-            if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
+        if (++current >= argc) {
+            Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+            return JIM_OK;
+        }
+        falsebody = current++;
+        if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
 
-                if (current != argc - 1)
-                    goto err;
-                return Jim_EvalObj(interp, argv[current]);
+            if (current != argc - 1) {
+                return JIM_USAGE;
             }
-            else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
-                continue;
+            return Jim_EvalObj(interp, argv[current]);
+        }
+        else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
+            continue;
 
-            else if (falsebody != argc - 1)
-                goto err;
-            return Jim_EvalObj(interp, argv[falsebody]);
+        else if (falsebody != argc - 1) {
+            return JIM_USAGE;
         }
-        return JIM_OK;
+        return Jim_EvalObj(interp, argv[falsebody]);
     }
-  err:
-    Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
-    return JIM_ERR;
+    return JIM_OK;
 }
 
 
@@ -18016,12 +18685,6 @@ static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
     Jim_Obj *command = NULL, *scriptObj = NULL, *strObj;
     Jim_Obj **caseList;
 
-    if (argc < 3) {
-      wrongnumargs:
-        Jim_WrongNumArgs(interp, 1, argv, "?options? string "
-            "pattern body ... ?default body?   or   " "{pattern body ?pattern body ...?}");
-        return JIM_ERR;
-    }
     for (opt = 1; opt < argc; ++opt) {
         const char *option = Jim_String(argv[opt]);
 
@@ -18042,7 +18705,7 @@ static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
         else if (strncmp(option, "-command", 2) == 0) {
             matchOpt = SWITCH_CMD;
             if ((argc - opt) < 2)
-                goto wrongnumargs;
+                return JIM_USAGE;
             command = argv[++opt];
         }
         else {
@@ -18052,7 +18715,7 @@ static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
             return JIM_ERR;
         }
         if ((argc - opt) < 2)
-            goto wrongnumargs;
+            return JIM_USAGE;
     }
     strObj = argv[opt++];
     patCount = argc - opt;
@@ -18062,7 +18725,7 @@ static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
     else
         caseList = (Jim_Obj **)&argv[opt];
     if (patCount == 0 || patCount % 2 != 0)
-        goto wrongnumargs;
+        return JIM_USAGE;
     for (i = 0; scriptObj == NULL && i < patCount; i += 2) {
         Jim_Obj *patObj = caseList[i];
 
@@ -18129,10 +18792,6 @@ static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
     Jim_Obj *objPtr;
     int ret;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
-        return JIM_ERR;
-    }
     ret = Jim_ListIndices(interp, argv[1], argv + 2, argc - 2, &objPtr, JIM_NONE);
     if (ret < 0) {
         ret = JIM_OK;
@@ -18147,10 +18806,6 @@ static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
 
 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc != 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "list");
-        return JIM_ERR;
-    }
     Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
     return JIM_OK;
 }
@@ -18179,13 +18834,6 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
     int match_flags = 0;
     long stride = 1;
 
-    if (argc < 3) {
-      wrongargs:
-        Jim_WrongNumArgs(interp, 1, argv,
-            "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? ?-stride len? ?-index val? list value");
-        return JIM_ERR;
-    }
-
     for (i = 1; i < argc - 2; i++) {
         int option;
 
@@ -18216,7 +18864,7 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
                 break;
             case OPT_COMMAND:
                 if (i >= argc - 2) {
-                    goto wrongargs;
+                    return JIM_USAGE;
                 }
                 commandObj = argv[++i];
 
@@ -18226,13 +18874,13 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
                 break;
             case OPT_INDEX:
                 if (i >= argc - 2) {
-                    goto wrongargs;
+                    return JIM_USAGE;
                 }
                 indexObj = argv[++i];
                 break;
             case OPT_STRIDE:
                 if (i >= argc - 2) {
-                    goto wrongargs;
+                    return JIM_USAGE;
                 }
                 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
                     return JIM_ERR;
@@ -18247,7 +18895,7 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
 
     argc -= i;
     if (argc < 2) {
-        goto wrongargs;
+        return JIM_USAGE;
     }
     argv += i;
 
@@ -18386,28 +19034,19 @@ static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
     Jim_Obj *listObjPtr;
-    int new_obj = 0;
     int i;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
-        return JIM_ERR;
-    }
     listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
     if (!listObjPtr) {
 
         listObjPtr = Jim_NewListObj(interp, NULL, 0);
-        new_obj = 1;
     }
     else if (Jim_IsShared(listObjPtr)) {
         listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
-        new_obj = 1;
     }
     for (i = 2; i < argc; i++)
         Jim_ListAppendElement(interp, listObjPtr, argv[i]);
     if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
-        if (new_obj)
-            Jim_FreeNewObj(interp, listObjPtr);
         return JIM_ERR;
     }
     Jim_SetResult(interp, listObjPtr);
@@ -18420,10 +19059,6 @@ static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
     int idx, len;
     Jim_Obj *listPtr;
 
-    if (argc < 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
-        return JIM_ERR;
-    }
     listPtr = argv[1];
     if (Jim_IsShared(listPtr))
         listPtr = Jim_DuplicateObj(interp, listPtr);
@@ -18451,10 +19086,6 @@ static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const
     Jim_Obj *listObj;
     Jim_Obj *newListObj;
 
-    if (argc < 4) {
-        Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
-        return JIM_ERR;
-    }
     if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
         Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
         return JIM_ERR;
@@ -18488,11 +19119,7 @@ static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const
 
 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc < 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "listVar ?index ...? value");
-        return JIM_ERR;
-    }
-    else if (argc == 3) {
+    if (argc == 3) {
 
         if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
             return JIM_ERR;
@@ -18523,12 +19150,6 @@ static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const arg
 
     struct lsort_info info;
 
-    if (argc < 2) {
-wrongargs:
-        Jim_WrongNumArgs(interp, 1, argv, "?options? list");
-        return JIM_ERR;
-    }
-
     info.type = JIM_LSORT_ASCII;
     info.order = 1;
     info.indexc = 0;
@@ -18578,7 +19199,7 @@ wrongargs:
                 break;
             case OPT_STRIDE:
                 if (i >= argc - 2) {
-                    goto wrongargs;
+                    return JIM_USAGE;
                 }
                 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
                     return JIM_ERR;
@@ -18656,34 +19277,24 @@ static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
     Jim_Obj *stringObjPtr;
     int i;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
-        return JIM_ERR;
-    }
     if (argc == 2) {
         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
         if (!stringObjPtr)
             return JIM_ERR;
     }
     else {
-        int new_obj = 0;
         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
         if (!stringObjPtr) {
 
             stringObjPtr = Jim_NewEmptyStringObj(interp);
-            new_obj = 1;
         }
         else if (Jim_IsShared(stringObjPtr)) {
-            new_obj = 1;
             stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
         }
         for (i = 2; i < argc; i++) {
             Jim_AppendObj(interp, stringObjPtr, argv[i]);
         }
         if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
-            if (new_obj) {
-                Jim_FreeNewObj(interp, stringObjPtr);
-            }
             return JIM_ERR;
         }
     }
@@ -18699,11 +19310,6 @@ static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
 {
     int rc;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
-        return JIM_ERR;
-    }
-
     if (argc == 2) {
         rc = Jim_EvalObj(interp, argv[1]);
     }
@@ -18717,84 +19323,62 @@ static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
 
 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc >= 2) {
-        int retcode;
-        Jim_CallFrame *savedCallFrame, *targetCallFrame;
-        const char *str;
-
-
-        savedCallFrame = interp->framePtr;
-
-
-        str = Jim_String(argv[1]);
-        if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
-            targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
-            argc--;
-            argv++;
-        }
-        else {
-            targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
-        }
-        if (targetCallFrame == NULL) {
-            return JIM_ERR;
-        }
-        if (argc < 2) {
-            Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
-            return JIM_ERR;
-        }
-
-        interp->framePtr = targetCallFrame;
-        if (argc == 2) {
-            retcode = Jim_EvalObj(interp, argv[1]);
-        }
-        else {
-            retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
-        }
-        interp->framePtr = savedCallFrame;
-        return retcode;
+    int retcode;
+    Jim_CallFrame *savedCallFrame, *targetCallFrame;
+    const char *str;
+
+
+    savedCallFrame = interp->framePtr;
+
+
+    str = Jim_String(argv[1]);
+    if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
+        targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
+        argc--;
+        argv++;
     }
     else {
-        Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
+        targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
+    }
+    if (targetCallFrame == NULL) {
         return JIM_ERR;
     }
-}
-
-
-static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
-{
-    int retcode;
+    if (argc < 2) {
+        return JIM_USAGE;
+    }
 
+    interp->framePtr = targetCallFrame;
     if (argc == 2) {
-        retcode = Jim_EvalExpression(interp, argv[1]);
+        retcode = Jim_EvalObj(interp, argv[1]);
     }
-#ifndef JIM_COMPAT
     else {
-        Jim_WrongNumArgs(interp, 1, argv, "expression");
-        retcode = JIM_ERR;
+        retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
     }
-#else
-    else if (argc > 2) {
+    interp->framePtr = savedCallFrame;
+    return retcode;
+}
+
+
+static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+#ifdef JIM_COMPAT
+    if (argc > 2) {
+        int retcode;
         Jim_Obj *objPtr;
 
         objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
         Jim_IncrRefCount(objPtr);
         retcode = Jim_EvalExpression(interp, objPtr);
         Jim_DecrRefCount(interp, objPtr);
-    }
-    else {
-        Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
-        return JIM_ERR;
+
+        return retcode;
     }
 #endif
-    return retcode;
+    return Jim_EvalExpression(interp, argv[1]);
 }
 
 static int JimBreakContinueHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int retcode)
 {
-    if (argc != 1 && argc != 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "?level?");
-        return JIM_ERR;
-    }
     if (argc == 2) {
         long level;
         int ret = Jim_GetLong(interp, argv[1], &level);
@@ -18882,8 +19466,7 @@ static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
     }
 
     if (i != argc - 1 && i != argc) {
-        Jim_WrongNumArgs(interp, 1, argv,
-            "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
+        return JIM_USAGE;
     }
 
 
@@ -18957,18 +19540,12 @@ static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
 
 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    Jim_Obj *prefixListObj;
-
-    if (argc < 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
-        return JIM_ERR;
-    }
-
-    prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
+    Jim_Obj *prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
     Jim_IncrRefCount(prefixListObj);
     Jim_SetResult(interp, argv[1]);
 
-    return Jim_CreateCommandObj(interp, argv[1], JimAliasCmd, prefixListObj, JimAliasCmdDelete);
+    Jim_RegisterCommand(interp, argv[1], JimAliasCmd, JimAliasCmdDelete, NULL, NULL, 0, -1, JIM_CMD_ISALIAS, prefixListObj);
+    return JIM_OK;
 }
 
 
@@ -18976,11 +19553,6 @@ static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
 {
     Jim_Cmd *cmd;
 
-    if (argc != 4 && argc != 5) {
-        Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
-        return JIM_ERR;
-    }
-
     if (argc == 4) {
         cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
     }
@@ -19007,11 +19579,6 @@ static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
 
 static int Jim_XtraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc != 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "callback");
-        return JIM_ERR;
-    }
-
     if (interp->traceCmdObj) {
         Jim_DecrRefCount(interp, interp->traceCmdObj);
         interp->traceCmdObj = NULL;
@@ -19030,11 +19597,6 @@ static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
 {
     int retcode;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
-        return JIM_ERR;
-    }
-
 
     interp->local++;
     retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
@@ -19050,7 +19612,7 @@ static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
         }
         if (interp->framePtr->localCommands == NULL) {
             interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
-            Jim_InitStack(interp->framePtr->localCommands);
+            Jim_StackInit(interp->framePtr->localCommands, NULL);
         }
         Jim_IncrRefCount(cmdNameObj);
         Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
@@ -19062,86 +19624,126 @@ static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
 
 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
+    int retcode;
+
+    Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
+    if (cmdPtr == NULL || !(cmdPtr->flags & JIM_CMD_ISPROC) || !cmdPtr->prevCmd) {
+        Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
         return JIM_ERR;
     }
-    else {
-        int retcode;
-
-        Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
-        if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
-            Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
-            return JIM_ERR;
-        }
 
-        cmdPtr->u.proc.upcall++;
-        JimIncrCmdRefCount(cmdPtr);
+    cmdPtr->u.proc.upcall++;
+    JimIncrCmdRefCount(cmdPtr);
 
 
-        retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
+    retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
 
 
-        cmdPtr->u.proc.upcall--;
-        JimDecrCmdRefCount(interp, cmdPtr);
+    cmdPtr->u.proc.upcall--;
+    JimDecrCmdRefCount(interp, cmdPtr);
 
-        return retcode;
-    }
+    return retcode;
 }
 
 
 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
+    int ret;
+    Jim_Cmd *cmd;
+    Jim_Obj *argListObjPtr;
+    Jim_Obj *bodyObjPtr;
+    Jim_Obj *nsObj = NULL;
+    Jim_Obj **nargv;
+
+    int len = Jim_ListLength(interp, argv[1]);
+    if (len != 2 && len != 3) {
+        Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
         return JIM_ERR;
     }
-    else {
-        int ret;
-        Jim_Cmd *cmd;
-        Jim_Obj *argListObjPtr;
-        Jim_Obj *bodyObjPtr;
-        Jim_Obj *nsObj = NULL;
-        Jim_Obj **nargv;
-
-        int len = Jim_ListLength(interp, argv[1]);
-        if (len != 2 && len != 3) {
-            Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
-            return JIM_ERR;
-        }
 
-        if (len == 3) {
+    if (len == 3) {
 #ifdef jim_ext_namespace
 
-            nsObj = Jim_ListGetIndex(interp, argv[1], 2);
+        nsObj = Jim_ListGetIndex(interp, argv[1], 2);
 #else
-            Jim_SetResultString(interp, "namespaces not enabled", -1);
-            return JIM_ERR;
+        Jim_SetResultString(interp, "namespaces not enabled", -1);
+        return JIM_ERR;
 #endif
-        }
-        argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
-        bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
+    }
+    argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
+    bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
 
-        cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
+    cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
 
-        if (cmd) {
+    if (cmd) {
 
-            nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
-            nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
-            Jim_IncrRefCount(nargv[0]);
-            memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
-            ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
-            Jim_DecrRefCount(interp, nargv[0]);
-            Jim_Free(nargv);
+        nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
+        nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
+        Jim_IncrRefCount(nargv[0]);
+        memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
+        ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
+        Jim_DecrRefCount(interp, nargv[0]);
+        Jim_Free(nargv);
 
-            JimDecrCmdRefCount(interp, cmd);
-            return ret;
+        JimDecrCmdRefCount(interp, cmd);
+        return ret;
+    }
+    return JIM_ERR;
+}
+
+#ifdef JIM_TAINT
+static int JimTaintVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int taint)
+{
+    Jim_Obj *valueObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_ERRMSG | JIM_UNSHARED);
+
+    if (valueObjPtr == NULL) {
+        return JIM_ERR;
+    }
+
+    if (Jim_IsShared(valueObjPtr)) {
+        valueObjPtr = Jim_DuplicateObj(interp, valueObjPtr);
+        Jim_SetVariable(interp, nameObjPtr, valueObjPtr);
+    }
+
+    if (taint && nameObjPtr->typePtr == &dictSubstObjType) {
+
+        Jim_Obj *objPtr = Jim_GetVariable(interp, nameObjPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE);
+        if (objPtr) {
+            SetStringFromAny(interp, valueObjPtr);
+            valueObjPtr->taint = taint;
+            objPtr->taint |= taint;
         }
+    }
+
+
+    SetStringFromAny(interp, valueObjPtr);
+    valueObjPtr->taint = taint;
+
+    //printf("taint of %s is %d\n", valueObjPtr->bytes, valueObjPtr->taint);
+    return JIM_OK;
+}
+
+
+static int Jim_TaintCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+    if (argc != 2) {
+        Jim_WrongNumArgs(interp, 1, argv, "varname");
         return JIM_ERR;
     }
+    return JimTaintVariable(interp, argv[1], 1);
 }
 
 
+static int Jim_UntaintCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+    if (argc != 2) {
+        Jim_WrongNumArgs(interp, 1, argv, "varname");
+        return JIM_ERR;
+    }
+    return JimTaintVariable(interp, argv[1], 0);
+}
+#endif
+
 
 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
@@ -19170,8 +19772,7 @@ static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
 
 
     if (argc < 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
-        return JIM_ERR;
+        return JIM_USAGE;
     }
 
 
@@ -19187,10 +19788,6 @@ static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
 {
     int i;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
-        return JIM_ERR;
-    }
 
     if (interp->framePtr->level == 0)
         return JIM_OK;
@@ -19304,7 +19901,7 @@ static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
     static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
         JIM_DEF_SUBCMD("bytelength", "string", 1, 1),
         JIM_DEF_SUBCMD("byterange", "string first last", 3, 3),
-        JIM_DEF_SUBCMD("cat", "?...?", 0, -1),
+        JIM_DEF_SUBCMD("cat", "?string ...?", 0, -1),
         JIM_DEF_SUBCMD("compare", "?-nocase? ?-length int? string1 string2", 2, 5),
         JIM_DEF_SUBCMD("equal", "?-nocase? ?-length int? string1 string2", 2, 5),
         JIM_DEF_SUBCMD("first", "subString string ?index?", 2, 3),
@@ -19622,10 +20219,6 @@ static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
     long i, count = 1;
     jim_wide start, elapsed;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
-        return JIM_ERR;
-    }
     if (argc == 3) {
         if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
             return JIM_ERR;
@@ -19654,7 +20247,7 @@ static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
 }
 
 
-static int Jim_TimeRateCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+static int Jim_TimerateCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
     long us = 0;
     jim_wide start, delta, overhead;
@@ -19663,10 +20256,6 @@ static int Jim_TimeRateCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const
     int count;
     int n;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "script ?milliseconds?");
-        return JIM_ERR;
-    }
     if (argc == 3) {
         if (Jim_GetLong(interp, argv[2], &us) != JIM_OK)
             return JIM_ERR;
@@ -19723,10 +20312,6 @@ static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
 {
     long exitCode = 0;
 
-    if (argc > 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
-        return JIM_ERR;
-    }
     if (argc == 2) {
         if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
             return JIM_ERR;
@@ -19755,10 +20340,6 @@ static int JimMatchReturnCodes(Jim_Interp *interp, Jim_Obj *retcodeListObj, int
 
 static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *const *argv)
 {
-    static const char * const wrongargs_catchtry[2] = {
-        "?-?no?code ... --? script ?resultVarName? ?optionVarName?",
-        "?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?"
-    };
     int exitCode = 0;
     int i;
     int sig = 0;
@@ -19808,7 +20389,7 @@ static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *c
             option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
         }
         if (option < 0) {
-            goto wrongargs;
+            return JIM_USAGE;
         }
 
         if (ignore) {
@@ -19822,9 +20403,7 @@ static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *c
     idx = i;
 
     if (argc - idx < 1) {
-wrongargs:
-        Jim_WrongNumArgs(interp, 1, argv, wrongargs_catchtry[istry]);
-        return JIM_ERR;
+        return JIM_USAGE;
     }
 
     if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
@@ -19839,7 +20418,7 @@ wrongargs:
     else {
         exitCode = Jim_EvalObj(interp, argv[idx]);
 
-        interp->errorFlag = 0;
+        interp->hasErrorStackTrace = 0;
     }
     interp->signal_level -= sig;
 
@@ -19860,26 +20439,32 @@ wrongargs:
                 case TRY_ON:
                 case TRY_TRAP:
                     if (idx + 4 > argc) {
-                        goto wrongargs;
+                        return JIM_USAGE;
                     }
                     if (option == TRY_ON) {
                         ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode);
                         if (ret > JIM_OK) {
-                            goto wrongargs;
+                            return JIM_USAGE;
                         }
                     }
                     else if (errorCodeObj) {
                         int len = Jim_ListLength(interp, argv[idx + 1]);
-                        int i;
 
-                        ret = JIM_OK;
+                        if (len > Jim_ListLength(interp, errorCodeObj)) {
 
-                        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;
+                            ret = -1;
+                        }
+                        else {
+                            int i;
+                            ret = JIM_OK;
+
+                            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;
+                                }
                             }
                         }
                     }
@@ -19897,7 +20482,7 @@ wrongargs:
                     break;
                 case TRY_FINALLY:
                     if (idx + 2 != argc) {
-                        goto wrongargs;
+                        return JIM_USAGE;
                     }
                     finallyScriptObj = argv[idx + 1];
                     idx += 2;
@@ -20005,11 +20590,6 @@ static int Jim_TryCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv
 
 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc != 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
-        return JIM_ERR;
-    }
-
     return Jim_RenameCommand(interp, argv[1], argv[2]);
 }
 
@@ -20255,7 +20835,7 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
         }
 
         case OPT_SET:
-            return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
+            return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG | JIM_UNSHARED);
 
         case OPT_EXISTS:{
                 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_NONE);
@@ -20267,7 +20847,7 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
             }
 
         case OPT_UNSET:
-            if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE) != JIM_OK) {
+            if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_UNSHARED) != JIM_OK) {
                 return JIM_ERR;
             }
             return JIM_OK;
@@ -20330,10 +20910,6 @@ static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
     int flags = JIM_SUBST_FLAG;
     Jim_Obj *objPtr;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "?options? string");
-        return JIM_ERR;
-    }
     for (i = 1; i < (argc - 1); i++) {
         int option;
 
@@ -20360,6 +20936,18 @@ static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
     return JIM_OK;
 }
 
+
+static int Jim_LsubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+    if (argc == 3) {
+        if (Jim_CompareStringImmediate(interp, argv[1], "-line")) {
+            return JimListSubstObj(interp, argv[2], JIM_LSUBST_LINE);
+        }
+        return JIM_USAGE;
+    }
+    return JimListSubstObj(interp, argv[1], 0);
+}
+
 #ifdef jim_ext_namespace
 static int JimIsGlobalNamespace(Jim_Obj *objPtr)
 {
@@ -20373,11 +20961,12 @@ static int JimIsGlobalNamespace(Jim_Obj *objPtr)
 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
     Jim_Obj *objPtr;
-    int mode = 0;
+    int mode = 1;
 
 
     enum {
         INFO_ALIAS,
+        INFO_ALIASES,
         INFO_ARGS,
         INFO_BODY,
         INFO_CHANNELS,
@@ -20386,6 +20975,7 @@ static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
         INFO_EXISTS,
         INFO_FRAME,
         INFO_GLOBALS,
+        INFO_HELP,
         INFO_HOSTNAME,
         INFO_LEVEL,
         INFO_LOCALS,
@@ -20398,32 +20988,38 @@ static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
         INFO_SOURCE,
         INFO_STACKTRACE,
         INFO_STATICS,
+        INFO_TAINTED,
+        INFO_USAGE,
         INFO_VARS,
         INFO_VERSION,
         INFO_COUNT
     };
     static const jim_subcmd_type cmds[INFO_COUNT + 1] = {
         JIM_DEF_SUBCMD("alias", "command", 1, 1),
+        JIM_DEF_SUBCMD("aliases", "?-all? ?pattern?", 0, 2),
         JIM_DEF_SUBCMD("args", "procname", 1, 1),
         JIM_DEF_SUBCMD("body", "procname", 1, 1),
-        JIM_DEF_SUBCMD("channels", "?pattern?", 0, 1),
-        JIM_DEF_SUBCMD("commands", "?pattern?", 0, 1),
+        JIM_DEF_SUBCMD("channels", "?-all? ?pattern?", 0, 2),
+        JIM_DEF_SUBCMD("commands", "?-all? ?pattern?", 0, 2),
         JIM_DEF_SUBCMD("complete", "script ?missing?", 1, 2),
         JIM_DEF_SUBCMD("exists", "varName", 1, 1),
         JIM_DEF_SUBCMD("frame", "?levelNum?", 0, 1),
         JIM_DEF_SUBCMD("globals", "?pattern?", 0, 1),
+        JIM_DEF_SUBCMD("help", "command", 1, 1),
         JIM_DEF_SUBCMD("hostname", NULL, 0, 0),
         JIM_DEF_SUBCMD("level", "?levelNum?", 0, 1),
         JIM_DEF_SUBCMD("locals", "?pattern?", 0, 1),
         JIM_DEF_SUBCMD("nameofexecutable", NULL, 0, 0),
         JIM_DEF_SUBCMD("patchlevel", NULL, 0, 0),
-        JIM_DEF_SUBCMD("procs", "?pattern?", 0, 1),
+        JIM_DEF_SUBCMD("procs", "?-all? ?pattern?", 0, 2),
         JIM_DEF_SUBCMD("references", NULL, 0, 0),
         JIM_DEF_SUBCMD("returncodes", "?code?", 0, 1),
         JIM_DEF_SUBCMD("script", "?filename?", 0, 1),
         JIM_DEF_SUBCMD("source", "source ?filename line?", 1, 3),
         JIM_DEF_SUBCMD("stacktrace", NULL, 0, 0),
         JIM_DEF_SUBCMD("statics", "procname", 1, 1),
+        JIM_DEF_SUBCMD("tainted", "value", 1, 1),
+        JIM_DEF_SUBCMD("usage", "command", 1, 1),
         JIM_DEF_SUBCMD("vars", "?pattern?", 0, 1),
         JIM_DEF_SUBCMD("version", NULL, 0, 0),
         { NULL }
@@ -20461,7 +21057,7 @@ static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
             if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
                 return JIM_ERR;
             }
-            if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
+            if ((cmdPtr->flags & JIM_CMD_ISALIAS) == 0) {
                 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
                 return JIM_ERR;
             }
@@ -20469,33 +21065,54 @@ static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
             return JIM_OK;
         }
 
+        case INFO_TAINTED:
+            if (argc != 3) {
+                Jim_WrongNumArgs(interp, 2, argv, "value");
+                return JIM_ERR;
+            }
+            Jim_SetResultBool(interp, argv[2]->taint != 0);
+            return JIM_OK;
         case INFO_CHANNELS:
-            mode++;
+            mode <<= 1;
 #ifndef jim_ext_aio
             Jim_SetResultString(interp, "aio not enabled", -1);
             return JIM_ERR;
 #endif
 
+        case INFO_ALIASES:
+            mode <<= 1;
+
         case INFO_PROCS:
-            mode++;
+            mode <<= 1;
 
-        case INFO_COMMANDS:
+        case INFO_COMMANDS:{
+            int n = 0;
 
+            if (argc > 2 && Jim_CompareStringImmediate(interp, argv[2], "-all")) {
+                mode |= JIM_CMDLIST_ALL;
+                n++;
+            }
+            if (argc < 2 + n || argc > 3 + n) {
+                Jim_SetResultFormatted(interp, "wrong # args: should be \"info %#s %s\"", argv[1], cmds[option].args);
+                return JIM_ERR;
+            }
 #ifdef jim_ext_namespace
             if (!nons) {
+
                 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
                     return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
                 }
             }
 #endif
-            Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
+            Jim_SetResult(interp, JimCommandsList(interp, (argc == 3 + n) ? argv[2 + n] : NULL, mode));
             return JIM_OK;
+        }
 
         case INFO_VARS:
-            mode++;
+            mode <<= 1;
 
         case INFO_LOCALS:
-            mode++;
+            mode <<= 1;
 
         case INFO_GLOBALS:
 
@@ -20573,6 +21190,27 @@ static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
             }
             return JIM_OK;
 
+        case INFO_USAGE:
+        case INFO_HELP:{
+            Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG);
+            if (!cmdPtr) {
+                return JIM_ERR;
+            }
+            if (option == INFO_USAGE) {
+                Jim_SetResult(interp, JimCmdUsage(interp, argv[2], cmdPtr));
+                return JIM_OK;
+            }
+            else if ((cmdPtr->flags & JIM_CMD_ISPROC) == 0) {
+                if (cmdPtr->u.native.help) {
+                    Jim_SetResultString(interp, cmdPtr->u.native.help, -1);
+                    return JIM_OK;
+                }
+            }
+
+            Jim_SetResultFormatted(interp, "No help available for command \"%#s\"", argv[2]);
+            return JIM_OK;
+        }
+
         case INFO_BODY:
         case INFO_STATICS:
         case INFO_ARGS:{
@@ -20581,7 +21219,7 @@ static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
                 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
                     return JIM_ERR;
                 }
-                if (!cmdPtr->isproc) {
+                if ((cmdPtr->flags & JIM_CMD_ISPROC) == 0) {
                     Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
                     return JIM_ERR;
                 }
@@ -20608,12 +21246,17 @@ static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
                 return JIM_OK;
             }
 
-        case INFO_VERSION:
-        case INFO_PATCHLEVEL:{
-                char buf[(JIM_INTEGER_SPACE * 2) + 1];
+        case INFO_PATCHLEVEL:
+
+#ifdef JIM_GITVERSION
+            Jim_SetResultString(interp, JIM_GITVERSION, -1);
+            return JIM_OK;
+#endif
 
-                sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
-                Jim_SetResultString(interp, buf, -1);
+        case INFO_VERSION:{
+                char versionbuf[64];
+                snprintf(versionbuf, sizeof(versionbuf), "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
+                Jim_SetResultString(interp, versionbuf, -1);
                 return JIM_OK;
             }
 
@@ -20683,11 +21326,11 @@ static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
     int result = 0;
 
     static const char * const options[] = {
-        "-command", "-proc", "-alias", "-var", NULL
+        "-command", "-proc", "-alias", "-channel", "-var", NULL
     };
     enum
     {
-        OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
+        OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_CHANNEL, OPT_VAR
     };
     int option;
 
@@ -20702,8 +21345,7 @@ static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
         objPtr = argv[2];
     }
     else {
-        Jim_WrongNumArgs(interp, 1, argv, "?option? name");
-        return JIM_ERR;
+        return JIM_USAGE;
     }
 
     if (option == OPT_VAR) {
@@ -20720,11 +21362,15 @@ static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
                 break;
 
             case OPT_ALIAS:
-                result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
+                result = (cmd->flags & JIM_CMD_ISALIAS) != 0;
                 break;
 
             case OPT_PROC:
-                result = cmd->isproc;
+                result = (cmd->flags & JIM_CMD_ISPROC) != 0;
+                break;
+
+            case OPT_CHANNEL:
+                result = (cmd->flags & JIM_CMD_ISCHANNEL) != 0;
                 break;
             }
         }
@@ -20742,11 +21388,6 @@ static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
     int c;
     int len;
 
-    if (argc != 2 && argc != 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
-        return JIM_ERR;
-    }
-
     str = Jim_GetString(argv[1], &len);
     if (len == 0) {
         return JIM_OK;
@@ -20825,10 +21466,6 @@ static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
     const char *joinStr;
     int joinStrLen;
 
-    if (argc != 2 && argc != 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
-        return JIM_ERR;
-    }
 
     if (argc == 2) {
         joinStr = " ";
@@ -20846,10 +21483,6 @@ static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
 {
     Jim_Obj *objPtr;
 
-    if (argc < 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
-        return JIM_ERR;
-    }
     objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
     if (objPtr == NULL)
         return JIM_ERR;
@@ -20863,10 +21496,6 @@ static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
     Jim_Obj *listPtr, **outVec;
     int outc, i;
 
-    if (argc < 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
-        return JIM_ERR;
-    }
     if (argv[2]->typePtr != &scanFmtStringObjType)
         SetScanFmtFromAny(interp, argv[2]);
     if (FormatGetError(argv[2]) != 0) {
@@ -20936,10 +21565,6 @@ static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
 
 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    if (argc != 2 && argc != 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
-        return JIM_ERR;
-    }
     Jim_SetResult(interp, argv[1]);
     if (argc == 3) {
         JimSetStackTrace(interp, argv[2]);
@@ -20953,10 +21578,6 @@ static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a
 {
     Jim_Obj *objPtr;
 
-    if (argc != 4) {
-        Jim_WrongNumArgs(interp, 1, argv, "list first last");
-        return JIM_ERR;
-    }
     if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
         return JIM_ERR;
     Jim_SetResult(interp, objPtr);
@@ -20970,8 +21591,7 @@ static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
     jim_wide count;
 
     if (argc < 2 || Jim_GetWideExpr(interp, argv[1], &count) != JIM_OK || count < 0) {
-        Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
-        return JIM_ERR;
+        return JIM_USAGE;
     }
     if (count == 0 || argc == 2) {
         Jim_SetEmptyResult(interp);
@@ -21046,10 +21666,6 @@ static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv
         return JIM_OK;
     }
 
-    if (argc > 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
-        return JIM_ERR;
-    }
     key = Jim_String(argv[1]);
     val = getenv(key);
     if (val == NULL) {
@@ -21066,16 +21682,9 @@ static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv
 
 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
-    int retval;
+    int retval = Jim_EvalFile(interp, Jim_String(argv[1]));
 
-    if (argc != 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "fileName");
-        return JIM_ERR;
-    }
-    retval = Jim_EvalFile(interp, Jim_String(argv[1]));
-    if (retval == JIM_RETURN)
-        return JIM_OK;
-    return retval;
+    return retval == JIM_RETURN ? JIM_OK : retval;
 }
 
 
@@ -21084,10 +21693,6 @@ static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const
     Jim_Obj *revObjPtr, **ele;
     int len;
 
-    if (argc != 2) {
-        Jim_WrongNumArgs(interp, 1, argv, "list");
-        return JIM_ERR;
-    }
     JimListGetElements(interp, argv[1], &len, &ele);
     revObjPtr = Jim_NewListObj(interp, NULL, 0);
     ListEnsureLength(revObjPtr, len);
@@ -21128,10 +21733,6 @@ static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
     int len, i;
     Jim_Obj *objPtr;
 
-    if (argc < 2 || argc > 4) {
-        Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
-        return JIM_ERR;
-    }
     if (argc == 2) {
         if (Jim_GetWideExpr(interp, argv[1], &end) != JIM_OK)
             return JIM_ERR;
@@ -21160,10 +21761,6 @@ static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
 {
     jim_wide min = 0, max = 0, len, maxMul;
 
-    if (argc < 1 || argc > 3) {
-        Jim_WrongNumArgs(interp, 1, argv, "?min? max");
-        return JIM_ERR;
-    }
     if (argc == 1) {
         max = JIM_WIDE_MAX;
     } else if (argc == 2) {
@@ -21191,94 +21788,107 @@ static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
     }
 }
 
-static const struct {
+static const struct jim_core_cmd_def_t {
     const char *name;
     Jim_CmdProc *cmdProc;
+    short minargs;
+    short maxargs;
+    const char *usage;
+    int flags;
 } Jim_CoreCommandsTable[] = {
-    {"alias", Jim_AliasCoreCommand},
-    {"set", Jim_SetCoreCommand},
-    {"unset", Jim_UnsetCoreCommand},
-    {"puts", Jim_PutsCoreCommand},
-    {"+", Jim_AddCoreCommand},
-    {"*", Jim_MulCoreCommand},
-    {"-", Jim_SubCoreCommand},
-    {"/", Jim_DivCoreCommand},
-    {"incr", Jim_IncrCoreCommand},
-    {"while", Jim_WhileCoreCommand},
-    {"loop", Jim_LoopCoreCommand},
-    {"for", Jim_ForCoreCommand},
-    {"foreach", Jim_ForeachCoreCommand},
-    {"lmap", Jim_LmapCoreCommand},
-    {"lassign", Jim_LassignCoreCommand},
-    {"if", Jim_IfCoreCommand},
-    {"switch", Jim_SwitchCoreCommand},
-    {"list", Jim_ListCoreCommand},
-    {"lindex", Jim_LindexCoreCommand},
-    {"lset", Jim_LsetCoreCommand},
-    {"lsearch", Jim_LsearchCoreCommand},
-    {"llength", Jim_LlengthCoreCommand},
-    {"lappend", Jim_LappendCoreCommand},
-    {"linsert", Jim_LinsertCoreCommand},
-    {"lreplace", Jim_LreplaceCoreCommand},
-    {"lsort", Jim_LsortCoreCommand},
-    {"append", Jim_AppendCoreCommand},
-    {"eval", Jim_EvalCoreCommand},
-    {"uplevel", Jim_UplevelCoreCommand},
-    {"expr", Jim_ExprCoreCommand},
-    {"break", Jim_BreakCoreCommand},
-    {"continue", Jim_ContinueCoreCommand},
-    {"proc", Jim_ProcCoreCommand},
-    {"xtrace", Jim_XtraceCoreCommand},
-    {"concat", Jim_ConcatCoreCommand},
-    {"return", Jim_ReturnCoreCommand},
-    {"upvar", Jim_UpvarCoreCommand},
-    {"global", Jim_GlobalCoreCommand},
-    {"string", Jim_StringCoreCommand},
-    {"time", Jim_TimeCoreCommand},
-    {"timerate", Jim_TimeRateCoreCommand},
-    {"exit", Jim_ExitCoreCommand},
-    {"catch", Jim_CatchCoreCommand},
-    {"try", Jim_TryCoreCommand},
+    {"*", Jim_MulCoreCommand, 0, -1, "?number ...?"  },
+    {"+", Jim_AddCoreCommand, 0, -1, "?number ...?"  },
+    {"-", Jim_SubCoreCommand, 1, -1, "number ?number ...?" },
+    {"/", Jim_DivCoreCommand, 1, -1, "number ?number ...?" },
+    {"alias", Jim_AliasCoreCommand, 2, -1, "newname command ?args ...?" },
+    {"append", Jim_AppendCoreCommand, 1, -1, "varName ?value ...?" },
+    {"apply", Jim_ApplyCoreCommand, 1, -1, "lambdaExpr ?arg ...?" },
+    {"break", Jim_BreakCoreCommand, 0, 1, "?level?" },
+    {"catch", Jim_CatchCoreCommand, 1, -1, "?-?no?code ... --? script ?resultVarName? ?optionVarName?" },
+    {"concat", Jim_ConcatCoreCommand, 0, -1, "?arg ...?" },
+    {"continue", Jim_ContinueCoreCommand, 0, 1, "?level?" },
+    {"dict", Jim_DictCoreCommand, 1, -1, "subcommand ?arg ...?"},
+    {"env", Jim_EnvCoreCommand, 0, 2, "?varName? ?default?" },
+    {"error", Jim_ErrorCoreCommand, 1, 2, "message ?stacktrace?" },
+    {"eval", Jim_EvalCoreCommand, 1, -1, "arg ?arg ...?" },
+    {"exists", Jim_ExistsCoreCommand, 1, 2, "?-command|-proc|-alias|-channel|-var? name" },
+    {"exit", Jim_ExitCoreCommand, 0, 1, "?exitCode?" },
+#ifdef JIM_COMPAT
+    {"expr", Jim_ExprCoreCommand, 1, -1, "expression ?...?" },
+#else
+    {"expr", Jim_ExprCoreCommand, 1, 1, "expression" },
+#endif
+    {"for", Jim_ForCoreCommand, 4, 4, "start test next body" },
+    {"foreach", Jim_ForeachCoreCommand, 3, -1, "varList list ?varList list ...? script" },
+    {"format", Jim_FormatCoreCommand, 1, -1, "formatString ?arg arg ...?" },
+    {"global", Jim_GlobalCoreCommand, 1, -1, "varName ?varName ...?" },
+    {"if", Jim_IfCoreCommand, 2, -1, "condition ?then? trueBody ?elseif ...? ?else? ?falseBody?" },
+    {"incr", Jim_IncrCoreCommand, 1, 2, "varName ?increment?" },
+    {"info", Jim_InfoCoreCommand, 1, -1, "subcommand ?arg ...?"},
+    {"join", Jim_JoinCoreCommand, 1, 2, "list ?joinString?" },
+    {"lappend", Jim_LappendCoreCommand, 1, -1, "varName ?value value ...?" },
+    {"lassign", Jim_LassignCoreCommand, 2, -1, "varList list ?varName ...?" },
+    {"lindex", Jim_LindexCoreCommand, 1, -1, "list ?index ...?" },
+    {"linsert", Jim_LinsertCoreCommand, 2, -1, "list index ?element ...?" },
+    {"list", Jim_ListCoreCommand, 0, -1, "?arg ...?" },
+    {"llength", Jim_LlengthCoreCommand, 1, 1, "list" },
+    {"lmap", Jim_LmapCoreCommand, 3, -1, "varList list ?varList list ...? script" },
+    {"local", Jim_LocalCoreCommand, 1, -1, "cmd ?args ...?" },
+    {"loop", Jim_LoopCoreCommand, 3, 5, "var ?first? limit ?incr? body" },
+    {"lrange", Jim_LrangeCoreCommand, 3, 3, "list first last" },
+    {"lrepeat", Jim_LrepeatCoreCommand, 1, -1, "count ?value ...?" },
+    {"lreplace", Jim_LreplaceCoreCommand, 3, -1, "list first last ?element ...?" },
+    {"lreverse", Jim_LreverseCoreCommand, 1, 1, "list" },
+    {"lsearch", Jim_LsearchCoreCommand, 2, -1, "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? ?-stride len? ?-index val? list value" },
+    {"lset", Jim_LsetCoreCommand, 2, -1, "listVar ?index ...? value" },
+    {"lsort", Jim_LsortCoreCommand, 1, -1, "?options? list" },
+    {"lsubst", Jim_LsubstCoreCommand, 1, 2, "?-line? string" },
+    {"proc", Jim_ProcCoreCommand, 3, 4, "name arglist ?statics? body" },
+    {"puts", Jim_PutsCoreCommand, 1, 2, "?-nonewline? string" },
+    {"rand", Jim_RandCoreCommand, 0, 2, "?min? ?max?" },
+    {"range", Jim_RangeCoreCommand, 1, 3, "?start? end ?step?" },
+    {"rename", Jim_RenameCoreCommand, 2, 2, "oldName newName" },
+    {"return", Jim_ReturnCoreCommand, 0, -1, "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?" },
+    {"scan", Jim_ScanCoreCommand, 2, -1, "string format ?varName varName ...?" },
+    {"set", Jim_SetCoreCommand, 1, 2, "varName ?newValue?" },
+    {"source", Jim_SourceCoreCommand, 1, 1, "fileName", JIM_CMD_NOTAINT },
+    {"split", Jim_SplitCoreCommand, 1, 2, "string ?splitChars?" },
+    {"stacktrace", Jim_StacktraceCoreCommand, 0, 2, "?firstlevel? ?lastlevel?" },
+    {"string", Jim_StringCoreCommand, 1, -1, "subcommand ?arg ...?" },
+    {"subst", Jim_SubstCoreCommand, 1, 4, "?options? string" },
+    {"switch", Jim_SwitchCoreCommand, 2, -1, "?options? string pattern body ... ?default body? or pattern body ?pattern body ...?" },
+    {"tailcall", Jim_TailcallCoreCommand, 0, -1, "?cmd arg ...?" },
+    {"time", Jim_TimeCoreCommand, 1, 2, "script ?count?" },
+    {"timerate", Jim_TimerateCoreCommand, 1, 2, "script ?milliseconds?" },
+    {"try", Jim_TryCoreCommand, 1, -1, "?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?" },
+    {"unset", Jim_UnsetCoreCommand, 0, -1, "?-nocomplain? ?--? ?varName ...?"},
+    {"upcall", Jim_UpcallCoreCommand, 1, -1, "cmd ?args ...?" },
+    {"uplevel", Jim_UplevelCoreCommand, 1, -1, "?level? command ?arg ...?" },
+    {"upvar", Jim_UpvarCoreCommand, 2, -1, "?level? otherVar myVar ?otherVar myVar ...?"},
+    {"while", Jim_WhileCoreCommand, 2, 2, "condition body" },
+    {"xtrace", Jim_XtraceCoreCommand, 1, 1, "callback" },
 #ifdef JIM_REFERENCES
-    {"ref", Jim_RefCoreCommand},
-    {"getref", Jim_GetrefCoreCommand},
-    {"setref", Jim_SetrefCoreCommand},
-    {"finalize", Jim_FinalizeCoreCommand},
-    {"collect", Jim_CollectCoreCommand},
-#endif
-    {"rename", Jim_RenameCoreCommand},
-    {"dict", Jim_DictCoreCommand},
-    {"subst", Jim_SubstCoreCommand},
-    {"info", Jim_InfoCoreCommand},
-    {"exists", Jim_ExistsCoreCommand},
-    {"split", Jim_SplitCoreCommand},
-    {"join", Jim_JoinCoreCommand},
-    {"format", Jim_FormatCoreCommand},
-    {"scan", Jim_ScanCoreCommand},
-    {"error", Jim_ErrorCoreCommand},
-    {"lrange", Jim_LrangeCoreCommand},
-    {"lrepeat", Jim_LrepeatCoreCommand},
-    {"env", Jim_EnvCoreCommand},
-    {"source", Jim_SourceCoreCommand},
-    {"lreverse", Jim_LreverseCoreCommand},
-    {"range", Jim_RangeCoreCommand},
-    {"rand", Jim_RandCoreCommand},
-    {"tailcall", Jim_TailcallCoreCommand},
-    {"local", Jim_LocalCoreCommand},
-    {"upcall", Jim_UpcallCoreCommand},
-    {"apply", Jim_ApplyCoreCommand},
-    {"stacktrace", Jim_StacktraceCoreCommand},
+    {"collect", Jim_CollectCoreCommand, 0, 0, "" },
+    {"finalize", Jim_FinalizeCoreCommand, 1, 2, "reference ?finalizerProc?" },
+    {"getref", Jim_GetrefCoreCommand, 1, 1, "reference" },
+    {"ref", Jim_RefCoreCommand, 2, 3, "string tag ?finalizer?" },
+    {"setref", Jim_SetrefCoreCommand, 2, 2, "reference newValue" },
+#endif
+#ifdef JIM_TAINT
+    {"taint", Jim_TaintCoreCommand, 1, 1, "varname"},
+    {"untaint", Jim_UntaintCoreCommand, 1, 1, "varname"},
+#endif
     {NULL, NULL},
 };
 
 void Jim_RegisterCoreCommands(Jim_Interp *interp)
 {
-    int i = 0;
+    const struct jim_core_cmd_def_t *c;
 
-    while (Jim_CoreCommandsTable[i].name != NULL) {
-        Jim_CreateCommand(interp,
-            Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
-        i++;
+    for (c = Jim_CoreCommandsTable; c->name; c++) {
+
+        assert(c->usage);
+        Jim_RegisterCmd(interp, c->name, c->usage, c->minargs, c->maxargs, c->cmdProc, NULL, NULL, c->flags);
     }
 }
 
@@ -21725,14 +22335,22 @@ int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim
     int ret = JIM_ERR;
 
     if (ct) {
-        if (ct->flags & JIM_MODFLAG_FULLARGV) {
+        if ((ct->flags & JIM_MODFLAG_NOTAINT) && Jim_CheckTaint(interp, JIM_TAINT_ANY)) {
+            ret = JIM_SUBCMD_TAINTED;
+        }
+        else if (ct->flags & JIM_MODFLAG_FULLARGV) {
             ret = ct->function(interp, argc, argv);
         }
         else {
             ret = ct->function(interp, argc - 2, argv + 2);
         }
         if (ret < 0) {
-            Jim_SubCmdArgError(interp, ct, argv[0]);
+            if (ret == JIM_SUBCMD_TAINTED) {
+                Jim_SetTaintError(interp, 2, argv);
+            }
+            else {
+                Jim_SubCmdArgError(interp, ct, argv[0]);
+            }
             ret = JIM_ERR;
         }
     }
@@ -21747,6 +22365,18 @@ int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
     return Jim_CallSubCmd(interp, ct, argc, argv);
 }
 
+Jim_Cmd *Jim_RegisterSubCmd(Jim_Interp *interp, const char *cmdname,
+       const jim_subcmd_type *command_table, Jim_DelCmdProc *delProc)
+{
+    return Jim_RegisterCmd(interp, cmdname,
+        "subcommand ?arg ...?",
+        1, -1,
+        Jim_SubCmdProc,
+        delProc,
+        (void *)command_table,
+        0);
+}
+
 #include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
@@ -21936,7 +22566,7 @@ Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_
             step = utf8_tounicode(format, &ch);
         }
         if (isdigit(ch)) {
-            precision = strtoul(format, &end, 10);
+            precision = strtol(format, &end, 10);
             format = end;
             step = utf8_tounicode(format, &ch);
         } else if (ch == '*') {
@@ -22223,8 +22853,7 @@ Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_
 
 
 #define        FAIL(R,M)       { (R)->err = (M); return (M); }
-#define        ISMULT(c)       ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
-#define        META            "^$.[()|?{+*"
+#define        META            "^$.[()|"
 
 #define        HASWIDTH        1
 #define        SIMPLE          2
@@ -22257,6 +22886,22 @@ static const char *regprop( int op );
 #endif
 
 
+static int str_is_mult(const char *s)
+{
+       switch (*s) {
+               case '*':
+               case '+':
+               case '?':
+                       return 1;
+               case '{':
+                       if (isdigit(UCHAR(s[1]))) {
+                               return 1;
+                       }
+                       break;
+       }
+       return 0;
+}
+
 static int str_int_len(const int *seq)
 {
        int n = 0;
@@ -22266,6 +22911,41 @@ static int str_int_len(const int *seq)
        return n;
 }
 
+
+static char *reg_expanded_new_pattern(const char *exp)
+{
+
+       char *newexp = strdup(exp);
+       char *d = newexp;
+       const char *s = exp;
+       int escape = 0;
+
+       while (*s) {
+               if (escape) {
+                       escape = 0;
+                       continue;
+               }
+               else if (*s == '\\') {
+                       escape = 1;
+               }
+               else if (strchr(" \t\r\n\f\v", *s)) {
+                       s++;
+                       continue;
+               }
+               else if (*s == '#') {
+
+                       s++;
+                       while (*s && *s != '\n') {
+                               s++;
+                       }
+                       continue;
+               }
+               *d++ = *s++;
+       }
+       *d++ = '\0';
+       return newexp;
+}
+
 int jim_regcomp(regex_t *preg, const char *exp, int cflags)
 {
        int scan;
@@ -22281,6 +22961,11 @@ int jim_regcomp(regex_t *preg, const char *exp, int cflags)
        if (exp == NULL)
                FAIL(preg, REG_ERR_NULL_ARGUMENT);
 
+       if (cflags & REG_EXPANDED) {
+               preg->exp = reg_expanded_new_pattern(exp);
+               exp = preg->exp;
+       }
+
 
        preg->cflags = cflags;
        preg->regparse = exp;
@@ -22455,12 +23140,12 @@ static int regpiece(regex_t *preg, int *flagp)
        if (ret == 0)
                return 0;
 
-       op = *preg->regparse;
-       if (!ISMULT(op)) {
+       if (!str_is_mult(preg->regparse)) {
                *flagp = flags;
                return(ret);
        }
 
+       op = *preg->regparse;
        if (!(flags&HASWIDTH) && op != '?') {
                preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
                return 0;
@@ -22529,7 +23214,7 @@ static int regpiece(regex_t *preg, int *flagp)
        }
 
        preg->regparse++;
-       if (ISMULT(*preg->regparse)) {
+       if (str_is_mult(preg->regparse)) {
                preg->err = REG_ERR_NESTED_COUNT;
                return 0;
        }
@@ -22838,12 +23523,6 @@ cc_switch:
        case ')':
                preg->err = REG_ERR_INTERNAL;
                return 0;
-       case '?':
-       case '+':
-       case '*':
-       case '{':
-               preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
-               return 0;
        case '\\':
                ch = *preg->regparse++;
                switch (ch) {
@@ -22905,11 +23584,16 @@ cc_switch:
 
                        preg->regparse -= n;
 
+                       if (str_is_mult(preg->regparse)) {
+                               preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
+                               return 0;
+                       }
+
                        ret = regnode(preg, EXACTLY);
 
 
 
-                       while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
+                       while (*preg->regparse && strchr(META, *preg->regparse) == NULL && !str_is_mult(preg->regparse)) {
                                n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
                                if (ch == '\\' && preg->regparse[n]) {
                                        if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) {
@@ -22924,7 +23608,7 @@ cc_switch:
                                }
 
 
-                               if (ISMULT(preg->regparse[n])) {
+                               if (str_is_mult(&preg->regparse[n])) {
 
                                        if (added) {
 
@@ -23097,7 +23781,7 @@ int jim_regexec(regex_t  *preg,  const  char *string, size_t nmatch, regmatch_t
                        }
                        if (*string) {
 nextline:
-                               if (preg->cflags & REG_NEWLINE) {
+                               if (preg->cflags & REG_NEWLINE_ANCHOR) {
 
                                        string = strchr(string, '\n');
                                        if (string) {
@@ -23207,7 +23891,7 @@ static const char *str_find(const char *string, int c, int nocase)
 
 static int reg_iseol(regex_t *preg, int ch)
 {
-       if (preg->cflags & REG_NEWLINE) {
+       if (preg->cflags & REG_NEWLINE_STOP) {
                return ch == '\0' || ch == '\n';
        }
        else {
@@ -23618,6 +24302,7 @@ size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf,  size_t errb
 
 void jim_regfree(regex_t *preg)
 {
+       free(preg->exp);
        free(preg->program);
 }
 
index f4aa3979b0b8919802c92e59408d2b06b8d2d9f9..52a683001131c440656488cf79cae84872e0042e 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Doc\stypo\sfix\swhich\sbroke\sthe\sdocsrc\sbuild,\sreported\sby\sBrickViking.
-D 2026-04-12T17:47:57.565
+C Latest\supstream\sjimsh0.c.
+D 2026-04-13T10:56:14.049
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -44,7 +44,7 @@ F autosetup/cc-lib.tcl 493c5935b5dd3bf9bd4eca89b07c8b1b1a9356d61783035144e21795f
 F autosetup/cc-shared.tcl 163eda58c14cd662fd8a504bd2ad8a716ef4db7015dc1de0095d5de8dd601a4b
 F autosetup/cc.tcl c0fcc50ca91deff8741e449ddad05bcd08268bc31177e613a6343bbd1fd3e45f
 F autosetup/find_tclconfig.tcl e64886ffe3b982d4df42cd28ed91fe0b5940c2c5785e126c1821baf61bc86a7e
-F autosetup/jimsh0.c 916bbdf8023fbda9937afae57d81a853d8c2ea00f2320aa27becbc33574f963d
+F autosetup/jimsh0.c 740dc8cbfaedaff1f27b54b32e0015b22fa6c1a439492b9795968d61e56bab75
 F autosetup/pkg-config.tcl 4e635bf39022ff65e0d5434339dd41503ea48fc53822c9c5bde88b02d3d952ba
 F autosetup/proj.tcl ce301197f364f7ce2acabbbd84b43d19e917ec73653157ca134a06f32d322712
 F autosetup/sqlite-config.tcl 8fecce2838b7e7d990d161d08998034f3e3b0b2ddf4d7a99dbfafba9c902e302
@@ -2198,8 +2198,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
 F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
 F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P 7583385593354f90a9818d1820543841109f253dffcec5ca5ed7629336e29fac
-R f83f5494894ee6e4bc61b7cf7a505572
+P 3c212b5d85338f90a5f1a2d1484e54c0bf699eedef19a16f4c0de76a76dd9d07
+R e0962ba2c654c906138751c5846e6218
 U stephan
-Z 9bdb6964f3efeb6e9da57823378b906a
+Z 17befd6faf62861447677acabc5e0fc5
 # Remove this line to create a well-formed Fossil manifest.
index a2f9a043eafa639c624bb8516b052b1ca98a5f8d..52894c8a5d5c429c227a8ec58a516afa8f6a7f5e 100644 (file)
@@ -1 +1 @@
-3c212b5d85338f90a5f1a2d1484e54c0bf699eedef19a16f4c0de76a76dd9d07
+414ae4dd65182ce571a6abb1a1a2ba2c169b204156f4f8923c42f64805a3ef9f