Home | History | Annotate | Line # | Download | only in autosetup
      1 /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */
      2 #define JIM_COMPAT
      3 #define JIM_ANSIC
      4 #define JIM_REGEXP
      5 #define HAVE_NO_AUTOCONF
      6 #define JIM_TINY
      7 #define _JIMAUTOCONF_H
      8 #define TCL_LIBRARY "."
      9 #define jim_ext_bootstrap
     10 #define jim_ext_aio
     11 #define jim_ext_readdir
     12 #define jim_ext_regexp
     13 #define jim_ext_file
     14 #define jim_ext_glob
     15 #define jim_ext_exec
     16 #define jim_ext_clock
     17 #define jim_ext_array
     18 #define jim_ext_stdlib
     19 #define jim_ext_tclcompat
     20 #if defined(_MSC_VER)
     21 #define TCL_PLATFORM_OS "windows"
     22 #define TCL_PLATFORM_PLATFORM "windows"
     23 #define TCL_PLATFORM_PATH_SEPARATOR ";"
     24 #define HAVE_MKDIR_ONE_ARG
     25 #define HAVE_SYSTEM
     26 #elif defined(__MINGW32__)
     27 #define TCL_PLATFORM_OS "mingw"
     28 #define TCL_PLATFORM_PLATFORM "windows"
     29 #define TCL_PLATFORM_PATH_SEPARATOR ";"
     30 #define HAVE_MKDIR_ONE_ARG
     31 #define HAVE_SYSTEM
     32 #define HAVE_SYS_TIME_H
     33 #define HAVE_DIRENT_H
     34 #define HAVE_UNISTD_H
     35 #define HAVE_UMASK
     36 #include <sys/stat.h>
     37 #ifndef S_IRWXG
     38 #define S_IRWXG 0
     39 #endif
     40 #ifndef S_IRWXO
     41 #define S_IRWXO 0
     42 #endif
     43 #else
     44 #define TCL_PLATFORM_OS "unknown"
     45 #define TCL_PLATFORM_PLATFORM "unix"
     46 #define TCL_PLATFORM_PATH_SEPARATOR ":"
     47 #ifdef _MINIX
     48 #define vfork fork
     49 #define _POSIX_SOURCE
     50 #else
     51 #define _GNU_SOURCE
     52 #endif
     53 #define HAVE_FORK
     54 #define HAVE_WAITPID
     55 #define HAVE_ISATTY
     56 #define HAVE_MKSTEMP
     57 #define HAVE_LINK
     58 #define HAVE_SYS_TIME_H
     59 #define HAVE_DIRENT_H
     60 #define HAVE_UNISTD_H
     61 #define HAVE_UMASK
     62 #define HAVE_PIPE
     63 #define _FILE_OFFSET_BITS 64
     64 #endif
     65 #define JIM_VERSION 84
     66 #ifndef JIM_WIN32COMPAT_H
     67 #define JIM_WIN32COMPAT_H
     68 
     69 
     70 
     71 #ifdef __cplusplus
     72 extern "C" {
     73 #endif
     74 
     75 
     76 #if defined(_WIN32) || defined(WIN32)
     77 
     78 #define HAVE_DLOPEN
     79 void *dlopen(const char *path, int mode);
     80 int dlclose(void *handle);
     81 void *dlsym(void *handle, const char *symbol);
     82 char *dlerror(void);
     83 
     84 
     85 #if defined(__MINGW32__)
     86     #define JIM_SPRINTF_DOUBLE_NEEDS_FIX
     87 #endif
     88 
     89 #ifdef _MSC_VER
     90 
     91 
     92 #if _MSC_VER >= 1000
     93 	#pragma warning(disable:4146)
     94 #endif
     95 
     96 #include <limits.h>
     97 #define jim_wide _int64
     98 #ifndef HAVE_LONG_LONG
     99 #define HAVE_LONG_LONG
    100 #endif
    101 #ifndef LLONG_MAX
    102 	#define LLONG_MAX    9223372036854775807I64
    103 #endif
    104 #ifndef LLONG_MIN
    105 	#define LLONG_MIN    (-LLONG_MAX - 1I64)
    106 #endif
    107 #define JIM_WIDE_MIN LLONG_MIN
    108 #define JIM_WIDE_MAX LLONG_MAX
    109 #define JIM_WIDE_MODIFIER "I64d"
    110 #define strcasecmp _stricmp
    111 #define strtoull _strtoui64
    112 
    113 #include <io.h>
    114 
    115 #include <winsock.h>
    116 int gettimeofday(struct timeval *tv, void *unused);
    117 
    118 #define HAVE_OPENDIR
    119 struct dirent {
    120 	char *d_name;
    121 };
    122 
    123 typedef struct DIR {
    124 	long                handle;
    125 	struct _finddata_t  info;
    126 	struct dirent       result;
    127 	char                *name;
    128 } DIR;
    129 
    130 DIR *opendir(const char *name);
    131 int closedir(DIR *dir);
    132 struct dirent *readdir(DIR *dir);
    133 
    134 #endif
    135 
    136 #endif
    137 
    138 #ifdef __cplusplus
    139 }
    140 #endif
    141 
    142 #endif
    143 #ifndef UTF8_UTIL_H
    144 #define UTF8_UTIL_H
    145 
    146 #ifdef __cplusplus
    147 extern "C" {
    148 #endif
    149 
    150 
    151 
    152 #define MAX_UTF8_LEN 4
    153 
    154 int utf8_fromunicode(char *p, unsigned uc);
    155 
    156 #ifndef JIM_UTF8
    157 #include <ctype.h>
    158 
    159 
    160 #define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
    161 #define utf8_strwidth(S, B) utf8_strlen((S), (B))
    162 #define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
    163 #define utf8_getchars(CP, C) (*(CP) = (C), 1)
    164 #define utf8_upper(C) toupper(C)
    165 #define utf8_title(C) toupper(C)
    166 #define utf8_lower(C) tolower(C)
    167 #define utf8_index(C, I) (I)
    168 #define utf8_charlen(C) 1
    169 #define utf8_prev_len(S, L) 1
    170 #define utf8_width(C) 1
    171 
    172 #else
    173 
    174 #endif
    175 
    176 #ifdef __cplusplus
    177 }
    178 #endif
    179 
    180 #endif
    181 
    182 #ifndef __JIM__H
    183 #define __JIM__H
    184 
    185 #ifdef __cplusplus
    186 extern "C" {
    187 #endif
    188 
    189 #include <time.h>
    190 #include <limits.h>
    191 #include <stdlib.h>
    192 #include <stdarg.h>
    193 
    194 
    195 #ifndef HAVE_NO_AUTOCONF
    196 #endif
    197 
    198 
    199 
    200 #ifndef jim_wide
    201 #  ifdef HAVE_LONG_LONG
    202 #    define jim_wide long long
    203 #    ifndef LLONG_MAX
    204 #      define LLONG_MAX    9223372036854775807LL
    205 #    endif
    206 #    ifndef LLONG_MIN
    207 #      define LLONG_MIN    (-LLONG_MAX - 1LL)
    208 #    endif
    209 #    define JIM_WIDE_MIN LLONG_MIN
    210 #    define JIM_WIDE_MAX LLONG_MAX
    211 #  else
    212 #    define jim_wide long
    213 #    define JIM_WIDE_MIN LONG_MIN
    214 #    define JIM_WIDE_MAX LONG_MAX
    215 #  endif
    216 
    217 
    218 #  ifdef HAVE_LONG_LONG
    219 #    define JIM_WIDE_MODIFIER "lld"
    220 #  else
    221 #    define JIM_WIDE_MODIFIER "ld"
    222 #    define strtoull strtoul
    223 #  endif
    224 #endif
    225 
    226 #define UCHAR(c) ((unsigned char)(c))
    227 
    228 
    229 
    230 #define JIM_ABI_VERSION 101
    231 
    232 #define JIM_OK 0
    233 #define JIM_ERR 1
    234 #define JIM_RETURN 2
    235 #define JIM_BREAK 3
    236 #define JIM_CONTINUE 4
    237 #define JIM_SIGNAL 5
    238 #define JIM_EXIT 6
    239 
    240 #define JIM_EVAL 7
    241 
    242 #define JIM_MAX_CALLFRAME_DEPTH 1000
    243 #define JIM_MAX_EVAL_DEPTH 2000
    244 
    245 
    246 #define JIM_PRIV_FLAG_SHIFT 20
    247 
    248 #define JIM_NONE 0
    249 #define JIM_ERRMSG 1
    250 #define JIM_ENUM_ABBREV 2
    251 #define JIM_UNSHARED 4
    252 #define JIM_MUSTEXIST 8
    253 #define JIM_NORESULT 16
    254 
    255 
    256 #define JIM_SUBST_NOVAR 1
    257 #define JIM_SUBST_NOCMD 2
    258 #define JIM_SUBST_NOESC 4
    259 #define JIM_SUBST_FLAG 128
    260 
    261 
    262 #define JIM_CASESENS    0
    263 #define JIM_NOCASE      1
    264 #define JIM_OPT_END     2
    265 
    266 
    267 #define JIM_PATH_LEN 1024
    268 
    269 
    270 #define JIM_NOTUSED(V) ((void) V)
    271 
    272 #define JIM_LIBPATH "auto_path"
    273 #define JIM_INTERACTIVE "tcl_interactive"
    274 
    275 
    276 typedef struct Jim_Stack {
    277     int len;
    278     int maxlen;
    279     void **vector;
    280 } Jim_Stack;
    281 
    282 
    283 typedef struct Jim_HashEntry {
    284     void *key;
    285     union {
    286         void *val;
    287         int intval;
    288     } u;
    289     struct Jim_HashEntry *next;
    290 } Jim_HashEntry;
    291 
    292 typedef struct Jim_HashTableType {
    293     unsigned int (*hashFunction)(const void *key);
    294     void *(*keyDup)(void *privdata, const void *key);
    295     void *(*valDup)(void *privdata, const void *obj);
    296     int (*keyCompare)(void *privdata, const void *key1, const void *key2);
    297     void (*keyDestructor)(void *privdata, void *key);
    298     void (*valDestructor)(void *privdata, void *obj);
    299 } Jim_HashTableType;
    300 
    301 typedef struct Jim_HashTable {
    302     Jim_HashEntry **table;
    303     const Jim_HashTableType *type;
    304     void *privdata;
    305     unsigned int size;
    306     unsigned int sizemask;
    307     unsigned int used;
    308     unsigned int collisions;
    309     unsigned int uniq;
    310 } Jim_HashTable;
    311 
    312 typedef struct Jim_HashTableIterator {
    313     Jim_HashTable *ht;
    314     Jim_HashEntry *entry, *nextEntry;
    315     int index;
    316 } Jim_HashTableIterator;
    317 
    318 
    319 #define JIM_HT_INITIAL_SIZE     16
    320 
    321 
    322 #define Jim_FreeEntryVal(ht, entry) \
    323     if ((ht)->type->valDestructor) \
    324         (ht)->type->valDestructor((ht)->privdata, (entry)->u.val)
    325 
    326 #define Jim_SetHashVal(ht, entry, _val_) do { \
    327     if ((ht)->type->valDup) \
    328         (entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \
    329     else \
    330         (entry)->u.val = (_val_); \
    331 } while(0)
    332 
    333 #define Jim_SetHashIntVal(ht, entry, _val_) (entry)->u.intval = (_val_)
    334 
    335 #define Jim_FreeEntryKey(ht, entry) \
    336     if ((ht)->type->keyDestructor) \
    337         (ht)->type->keyDestructor((ht)->privdata, (entry)->key)
    338 
    339 #define Jim_SetHashKey(ht, entry, _key_) do { \
    340     if ((ht)->type->keyDup) \
    341         (entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \
    342     else \
    343         (entry)->key = (void *)(_key_); \
    344 } while(0)
    345 
    346 #define Jim_CompareHashKeys(ht, key1, key2) \
    347     (((ht)->type->keyCompare) ? \
    348         (ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \
    349         (key1) == (key2))
    350 
    351 #define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq)
    352 
    353 #define Jim_GetHashEntryKey(he) ((he)->key)
    354 #define Jim_GetHashEntryVal(he) ((he)->u.val)
    355 #define Jim_GetHashEntryIntVal(he) ((he)->u.intval)
    356 #define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
    357 #define Jim_GetHashTableSize(ht) ((ht)->size)
    358 #define Jim_GetHashTableUsed(ht) ((ht)->used)
    359 
    360 
    361 typedef struct Jim_Obj {
    362     char *bytes;
    363     const struct Jim_ObjType *typePtr;
    364     int refCount;
    365     int length;
    366 
    367     union {
    368 
    369         jim_wide wideValue;
    370 
    371         int intValue;
    372 
    373         double doubleValue;
    374 
    375         void *ptr;
    376 
    377         struct {
    378             void *ptr1;
    379             void *ptr2;
    380         } twoPtrValue;
    381 
    382         struct {
    383             void *ptr;
    384             int int1;
    385             int int2;
    386         } ptrIntValue;
    387 
    388         struct {
    389             struct Jim_VarVal *vv;
    390             unsigned long callFrameId;
    391             int global;
    392         } varValue;
    393 
    394         struct {
    395             struct Jim_Obj *nsObj;
    396             struct Jim_Cmd *cmdPtr;
    397             unsigned long procEpoch;
    398         } cmdValue;
    399 
    400         struct {
    401             struct Jim_Obj **ele;
    402             int len;
    403             int maxLen;
    404         } listValue;
    405 
    406         struct Jim_Dict *dictValue;
    407 
    408         struct {
    409             int maxLength;
    410             int charLength;
    411         } strValue;
    412 
    413         struct {
    414             unsigned long id;
    415             struct Jim_Reference *refPtr;
    416         } refValue;
    417 
    418         struct {
    419             struct Jim_Obj *fileNameObj;
    420             int lineNumber;
    421         } sourceValue;
    422 
    423         struct {
    424             struct Jim_Obj *varNameObjPtr;
    425             struct Jim_Obj *indexObjPtr;
    426         } dictSubstValue;
    427         struct {
    428             int line;
    429             int argc;
    430         } scriptLineValue;
    431     } internalRep;
    432     struct Jim_Obj *prevObjPtr;
    433     struct Jim_Obj *nextObjPtr;
    434 } Jim_Obj;
    435 
    436 
    437 #define Jim_IncrRefCount(objPtr) \
    438     ++(objPtr)->refCount
    439 #define Jim_DecrRefCount(interp, objPtr) \
    440     if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr)
    441 #define Jim_IsShared(objPtr) \
    442     ((objPtr)->refCount > 1)
    443 
    444 #define Jim_FreeNewObj Jim_FreeObj
    445 
    446 
    447 #define Jim_FreeIntRep(i,o) \
    448     if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \
    449         (o)->typePtr->freeIntRepProc(i, o)
    450 
    451 
    452 #define Jim_GetIntRepPtr(o) (o)->internalRep.ptr
    453 
    454 
    455 #define Jim_SetIntRepPtr(o, p) \
    456     (o)->internalRep.ptr = (p)
    457 
    458 
    459 struct Jim_Interp;
    460 
    461 typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp,
    462         struct Jim_Obj *objPtr);
    463 typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp,
    464         struct Jim_Obj *srcPtr, Jim_Obj *dupPtr);
    465 typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr);
    466 
    467 typedef struct Jim_ObjType {
    468     const char *name;
    469     Jim_FreeInternalRepProc *freeIntRepProc;
    470     Jim_DupInternalRepProc *dupIntRepProc;
    471     Jim_UpdateStringProc *updateStringProc;
    472     int flags;
    473 } Jim_ObjType;
    474 
    475 
    476 #define JIM_TYPE_NONE 0
    477 #define JIM_TYPE_REFERENCES 1
    478 
    479 
    480 
    481 typedef struct Jim_CallFrame {
    482     unsigned long id;
    483     int level;
    484     struct Jim_HashTable vars;
    485     struct Jim_HashTable *staticVars;
    486     struct Jim_CallFrame *parent;
    487     Jim_Obj *const *argv;
    488     int argc;
    489     Jim_Obj *procArgsObjPtr;
    490     Jim_Obj *procBodyObjPtr;
    491     struct Jim_CallFrame *next;
    492     Jim_Obj *nsObj;
    493     Jim_Obj *unused_fileNameObj;
    494     int unused_line;
    495     Jim_Stack *localCommands;
    496     struct Jim_Obj *tailcallObj;
    497     struct Jim_Cmd *tailcallCmd;
    498 } Jim_CallFrame;
    499 
    500 
    501 typedef struct Jim_EvalFrame {
    502     Jim_CallFrame *framePtr;
    503     int level;
    504     int procLevel;
    505     struct Jim_Cmd *cmd;
    506     struct Jim_EvalFrame *parent;
    507     Jim_Obj *const *argv;
    508     int argc;
    509     Jim_Obj *scriptObj;
    510 } Jim_EvalFrame;
    511 
    512 typedef struct Jim_VarVal {
    513     Jim_Obj *objPtr;
    514     struct Jim_CallFrame *linkFramePtr;
    515     int refCount;
    516 } Jim_VarVal;
    517 
    518 
    519 typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
    520     Jim_Obj *const *argv);
    521 typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);
    522 
    523 typedef struct Jim_Dict {
    524     struct JimDictHashEntry {
    525         int offset;
    526         unsigned hash;
    527     } *ht;
    528     unsigned int size;
    529     unsigned int sizemask;
    530     unsigned int uniq;
    531     Jim_Obj **table;
    532     int len;
    533     int maxLen;
    534     unsigned int dummy;
    535 } Jim_Dict;
    536 
    537 typedef struct Jim_Cmd {
    538     int inUse;
    539     int isproc;
    540     struct Jim_Cmd *prevCmd;
    541     Jim_Obj *cmdNameObj;
    542     union {
    543         struct {
    544 
    545             Jim_CmdProc *cmdProc;
    546             Jim_DelCmdProc *delProc;
    547             void *privData;
    548         } native;
    549         struct {
    550 
    551             Jim_Obj *argListObjPtr;
    552             Jim_Obj *bodyObjPtr;
    553             Jim_HashTable *staticVars;
    554             int argListLen;
    555             int reqArity;
    556             int optArity;
    557             int argsPos;
    558             int upcall;
    559             struct Jim_ProcArg {
    560                 Jim_Obj *nameObjPtr;
    561                 Jim_Obj *defaultObjPtr;
    562             } *arglist;
    563             Jim_Obj *nsObj;
    564         } proc;
    565     } u;
    566 } Jim_Cmd;
    567 
    568 
    569 typedef struct Jim_PrngState {
    570     unsigned char sbox[256];
    571     unsigned int i, j;
    572 } Jim_PrngState;
    573 
    574 typedef struct Jim_Interp {
    575     Jim_Obj *result;
    576     int unused_errorLine;
    577     Jim_Obj *currentFilenameObj;
    578     int break_level;
    579     int maxCallFrameDepth;
    580     int maxEvalDepth;
    581     int evalDepth;
    582     int returnCode;
    583     int returnLevel;
    584     int exitCode;
    585     long id;
    586     int signal_level;
    587     jim_wide sigmask;
    588     int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask);
    589     Jim_CallFrame *framePtr;
    590     Jim_CallFrame *topFramePtr;
    591     struct Jim_HashTable commands;
    592     unsigned long procEpoch; /* Incremented every time the result
    593                 of procedures names lookup caching
    594                 may no longer be valid. */
    595     unsigned long callFrameEpoch; /* Incremented every time a new
    596                 callframe is created. This id is used for the
    597                 'ID' field contained in the Jim_CallFrame
    598                 structure. */
    599     int local;
    600     int quitting;
    601     int safeexpr;
    602     Jim_Obj *liveList;
    603     Jim_Obj *freeList;
    604     Jim_Obj *unused_currentScriptObj;
    605     Jim_EvalFrame topEvalFrame;
    606     Jim_EvalFrame *evalFrame;
    607     int procLevel;
    608     Jim_Obj * const *unused_argv;
    609     Jim_Obj *nullScriptObj;
    610     Jim_Obj *emptyObj;
    611     Jim_Obj *trueObj;
    612     Jim_Obj *falseObj;
    613     unsigned long referenceNextId;
    614     struct Jim_HashTable references;
    615     unsigned long lastCollectId; /* reference max Id of the last GC
    616                 execution. It's set to ~0 while the collection
    617                 is running as sentinel to avoid to recursive
    618                 calls via the [collect] command inside
    619                 finalizers. */
    620     jim_wide lastCollectTime;
    621     Jim_Obj *stackTrace;
    622     Jim_Obj *errorProc;
    623     Jim_Obj *unknown;
    624     Jim_Obj *defer;
    625     Jim_Obj *traceCmdObj;
    626     int unknown_called;
    627     int errorFlag;
    628     void *cmdPrivData; /* Used to pass the private data pointer to
    629                   a command. It is set to what the user specified
    630                   via Jim_CreateCommand(). */
    631 
    632     Jim_Cmd *oldCmdCache;
    633     int oldCmdCacheSize;
    634     struct Jim_CallFrame *freeFramesList;
    635     struct Jim_HashTable assocData;
    636     Jim_PrngState *prngState;
    637     struct Jim_HashTable packages;
    638     Jim_Stack *loadHandles;
    639 } Jim_Interp;
    640 
    641 #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
    642 #define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))
    643 
    644 #define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
    645 #define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj)
    646 #define Jim_GetResult(i) ((i)->result)
    647 #define Jim_CmdPrivData(i) ((i)->cmdPrivData)
    648 
    649 #define Jim_SetResult(i,o) do {     \
    650     Jim_Obj *_resultObjPtr_ = (o);    \
    651     Jim_IncrRefCount(_resultObjPtr_); \
    652     Jim_DecrRefCount(i,(i)->result);  \
    653     (i)->result = _resultObjPtr_;     \
    654 } while(0)
    655 
    656 
    657 #define Jim_GetId(i) (++(i)->id)
    658 
    659 
    660 #define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference
    661                                   string representation must be fixed length. */
    662 typedef struct Jim_Reference {
    663     Jim_Obj *objPtr;
    664     Jim_Obj *finalizerCmdNamePtr;
    665     char tag[JIM_REFERENCE_TAGLEN+1];
    666 } Jim_Reference;
    667 
    668 
    669 #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
    670 #define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
    671 
    672 #define JIM_EXPORT extern
    673 
    674 
    675 
    676 JIM_EXPORT void *(*Jim_Allocator)(void *ptr, size_t size);
    677 
    678 #define Jim_Free(P) Jim_Allocator((P), 0)
    679 #define Jim_Realloc(P, S) Jim_Allocator((P), (S))
    680 #define Jim_Alloc(S) Jim_Allocator(NULL, (S))
    681 JIM_EXPORT char * Jim_StrDup (const char *s);
    682 JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
    683 
    684 
    685 JIM_EXPORT char **Jim_GetEnviron(void);
    686 JIM_EXPORT void Jim_SetEnviron(char **env);
    687 JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file);
    688 #ifndef CLOCK_REALTIME
    689 #  define CLOCK_REALTIME 0
    690 #endif
    691 #ifndef CLOCK_MONOTONIC
    692 #  define CLOCK_MONOTONIC 1
    693 #endif
    694 #ifndef CLOCK_MONOTONIC_RAW
    695 #  define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
    696 #endif
    697 JIM_EXPORT jim_wide Jim_GetTimeUsec(unsigned type);
    698 
    699 
    700 JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
    701 
    702 
    703 JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);
    704 
    705 #define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S))
    706 
    707 JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script);
    708 JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename);
    709 JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename);
    710 JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr);
    711 JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc,
    712         Jim_Obj *const *objv);
    713 JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj);
    714 JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
    715         int objc, Jim_Obj *const *objv);
    716 #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
    717 JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
    718 JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
    719         Jim_Obj **resObjPtrPtr, int flags);
    720 
    721 
    722 JIM_EXPORT Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
    723         int *lineptr);
    724 
    725 JIM_EXPORT void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
    726         Jim_Obj *fileNameObj, int lineNumber);
    727 
    728 
    729 
    730 JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
    731 JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
    732 JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
    733 JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
    734 JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
    735 JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
    736 JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr));
    737 
    738 
    739 JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
    740         const Jim_HashTableType *type, void *privdata);
    741 JIM_EXPORT void Jim_ExpandHashTable (Jim_HashTable *ht,
    742         unsigned int size);
    743 JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key,
    744         void *val);
    745 JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
    746         const void *key, void *val);
    747 JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
    748         const void *key);
    749 JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
    750 JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
    751         const void *key);
    752 JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator
    753         (Jim_HashTable *ht);
    754 JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
    755         (Jim_HashTableIterator *iter);
    756 
    757 
    758 JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
    759 JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr);
    760 JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr);
    761 JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
    762         Jim_Obj *objPtr);
    763 JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
    764         int *lenPtr);
    765 JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
    766 JIM_EXPORT int Jim_Length(Jim_Obj *objPtr);
    767 
    768 
    769 JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp,
    770         const char *s, int len);
    771 JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp,
    772         const char *s, int charlen);
    773 JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp,
    774         char *s, int len);
    775 JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr,
    776         const char *str, int len);
    777 JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr,
    778         Jim_Obj *appendObjPtr);
    779 JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp,
    780         Jim_Obj *objPtr, ...);
    781 JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr);
    782 JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr,
    783         Jim_Obj *objPtr, int nocase);
    784 JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp,
    785         Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr,
    786         Jim_Obj *lastObjPtr);
    787 JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp,
    788         Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
    789 JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
    790         Jim_Obj *fmtObjPtr, int flags);
    791 JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
    792         Jim_Obj *objPtr, const char *str);
    793 JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
    794         Jim_Obj *secondObjPtr, int nocase);
    795 JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr);
    796 
    797 
    798 JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp,
    799         Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr);
    800 JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp,
    801         Jim_Obj *objPtr);
    802 JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr);
    803 JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr);
    804 
    805 
    806 JIM_EXPORT Jim_Interp * Jim_CreateInterp (void);
    807 JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i);
    808 JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp);
    809 JIM_EXPORT const char *Jim_ReturnCode(int code);
    810 JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...);
    811 
    812 
    813 JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
    814 JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
    815         const char *cmdName, Jim_CmdProc *cmdProc, void *privData,
    816          Jim_DelCmdProc *delProc);
    817 JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
    818         Jim_Obj *cmdNameObj);
    819 JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
    820         Jim_Obj *oldNameObj, Jim_Obj *newNameObj);
    821 JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
    822         Jim_Obj *objPtr, int flags);
    823 JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
    824         Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
    825 JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
    826         const char *name, Jim_Obj *objPtr);
    827 JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp,
    828         const char *name, Jim_Obj *objPtr);
    829 JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp,
    830         const char *name, const char *val);
    831 JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp,
    832         Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr,
    833         Jim_CallFrame *targetCallFrame);
    834 JIM_EXPORT Jim_Obj * Jim_MakeGlobalNamespaceName(Jim_Interp *interp,
    835         Jim_Obj *nameObjPtr);
    836 JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp,
    837         Jim_Obj *nameObjPtr, int flags);
    838 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp,
    839         Jim_Obj *nameObjPtr, int flags);
    840 JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp,
    841         const char *name, int flags);
    842 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp,
    843         const char *name, int flags);
    844 JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp,
    845         Jim_Obj *nameObjPtr, int flags);
    846 
    847 
    848 JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp,
    849         Jim_Obj *levelObjPtr);
    850 
    851 
    852 JIM_EXPORT int Jim_Collect (Jim_Interp *interp);
    853 JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp);
    854 
    855 
    856 JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr,
    857         int *indexPtr);
    858 
    859 
    860 JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp,
    861         Jim_Obj *const *elements, int len);
    862 JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp,
    863         Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec);
    864 JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp,
    865         Jim_Obj *listPtr, Jim_Obj *objPtr);
    866 JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp,
    867         Jim_Obj *listPtr, Jim_Obj *appendListPtr);
    868 JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr);
    869 JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt,
    870         int listindex, Jim_Obj **objPtrPtr, int seterr);
    871 JIM_EXPORT Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx);
    872 JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp,
    873         Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc,
    874         Jim_Obj *newObjPtr);
    875 JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc,
    876         Jim_Obj *const *objv);
    877 JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp,
    878         Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen);
    879 
    880 
    881 JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp,
    882         Jim_Obj *const *elements, int len);
    883 JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr,
    884         Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
    885 JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
    886         Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
    887         Jim_Obj **objPtrPtr, int flags);
    888 JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
    889         Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
    890         Jim_Obj *newObjPtr, int flags);
    891 JIM_EXPORT Jim_Obj **Jim_DictPairs(Jim_Interp *interp,
    892         Jim_Obj *dictPtr, int *len);
    893 JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
    894         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
    895 
    896 #define JIM_DICTMATCH_KEYS 0x0001
    897 #define JIM_DICTMATCH_VALUES 0x002
    898 
    899 JIM_EXPORT int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types);
    900 JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
    901 JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr);
    902 JIM_EXPORT Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv);
    903 
    904 
    905 JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
    906         int *intPtr);
    907 
    908 
    909 JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
    910         Jim_Obj *exprObjPtr);
    911 JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
    912         Jim_Obj *exprObjPtr, int *boolPtr);
    913 
    914 
    915 JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
    916         int *booleanPtr);
    917 
    918 
    919 JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
    920         jim_wide *widePtr);
    921 JIM_EXPORT int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr,
    922         jim_wide *widePtr);
    923 JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
    924         long *longPtr);
    925 #define Jim_NewWideObj  Jim_NewIntObj
    926 JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
    927         jim_wide wideValue);
    928 
    929 
    930 JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
    931         double *doublePtr);
    932 JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
    933         double doubleValue);
    934 JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue);
    935 
    936 
    937 JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc,
    938         Jim_Obj *const *argv, const char *msg);
    939 JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
    940         const char * const *tablePtr, int *indexPtr, const char *name, int flags);
    941 JIM_EXPORT int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr,
    942         const char *const *tablePtr);
    943 JIM_EXPORT int Jim_ScriptIsComplete(Jim_Interp *interp,
    944         Jim_Obj *scriptObj, char *stateCharPtr);
    945 
    946 JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);
    947 
    948 
    949 typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
    950 JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
    951 JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
    952         Jim_InterpDeleteProc *delProc, void *data);
    953 JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
    954 JIM_EXPORT int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version);
    955 
    956 
    957 
    958 
    959 JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
    960         const char *name, const char *ver, int flags);
    961 JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
    962         const char *name, int flags);
    963 #define Jim_PackageProvideCheck(INTERP, NAME) \
    964         if (Jim_CheckAbiVersion(INTERP, JIM_ABI_VERSION) == JIM_ERR || Jim_PackageProvide(INTERP, NAME, "1.0", JIM_ERRMSG)) \
    965                 return JIM_ERR
    966 
    967 
    968 JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
    969 
    970 
    971 JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
    972 JIM_EXPORT void Jim_HistoryLoad(const char *filename);
    973 JIM_EXPORT void Jim_HistorySave(const char *filename);
    974 JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt);
    975 JIM_EXPORT void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj);
    976 JIM_EXPORT void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj);
    977 JIM_EXPORT void Jim_HistoryAdd(const char *line);
    978 JIM_EXPORT void Jim_HistoryShow(void);
    979 JIM_EXPORT void Jim_HistorySetMaxLen(int length);
    980 JIM_EXPORT int Jim_HistoryGetMaxLen(void);
    981 
    982 
    983 JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
    984 JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
    985 JIM_EXPORT int Jim_IsBigEndian(void);
    986 
    987 #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
    988 JIM_EXPORT void Jim_SignalSetIgnored(jim_wide mask);
    989 
    990 
    991 JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
    992 JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
    993 
    994 
    995 JIM_EXPORT int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
    996 
    997 
    998 JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
    999 JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
   1000 
   1001 #ifdef __cplusplus
   1002 }
   1003 #endif
   1004 
   1005 #endif
   1006 
   1007 #ifndef JIM_SUBCMD_H
   1008 #define JIM_SUBCMD_H
   1009 
   1010 
   1011 #ifdef __cplusplus
   1012 extern "C" {
   1013 #endif
   1014 
   1015 
   1016 #define JIM_MODFLAG_HIDDEN   0x0001
   1017 #define JIM_MODFLAG_FULLARGV 0x0002
   1018 
   1019 
   1020 
   1021 typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
   1022 
   1023 typedef struct {
   1024 	const char *cmd;
   1025 	const char *args;
   1026 	jim_subcmd_function *function;
   1027 	short minargs;
   1028 	short maxargs;
   1029 	unsigned short flags;
   1030 } jim_subcmd_type;
   1031 
   1032 #define JIM_DEF_SUBCMD(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs }
   1033 #define JIM_DEF_SUBCMD_HIDDEN(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs, JIM_MODFLAG_HIDDEN }
   1034 
   1035 const jim_subcmd_type *
   1036 Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
   1037 
   1038 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
   1039 
   1040 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
   1041 
   1042 void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type *ct, Jim_Obj *subcmd);
   1043 
   1044 #ifdef __cplusplus
   1045 }
   1046 #endif
   1047 
   1048 #endif
   1049 #ifndef JIMREGEXP_H
   1050 #define JIMREGEXP_H
   1051 
   1052 
   1053 #ifdef __cplusplus
   1054 extern "C" {
   1055 #endif
   1056 
   1057 #include <stdlib.h>
   1058 
   1059 typedef struct {
   1060 	int rm_so;
   1061 	int rm_eo;
   1062 } regmatch_t;
   1063 
   1064 
   1065 typedef struct regexp {
   1066 
   1067 	int re_nsub;
   1068 
   1069 
   1070 	int cflags;
   1071 	int err;
   1072 	int regstart;
   1073 	int reganch;
   1074 	int regmust;
   1075 	int regmlen;
   1076 	int *program;
   1077 
   1078 
   1079 	const char *regparse;
   1080 	int p;
   1081 	int proglen;
   1082 
   1083 
   1084 	int eflags;
   1085 	const char *start;
   1086 	const char *reginput;
   1087 	const char *regbol;
   1088 
   1089 
   1090 	regmatch_t *pmatch;
   1091 	int nmatch;
   1092 } regexp;
   1093 
   1094 typedef regexp regex_t;
   1095 
   1096 #define REG_EXTENDED 0
   1097 #define REG_NEWLINE 1
   1098 #define REG_ICASE 2
   1099 
   1100 #define REG_NOTBOL 16
   1101 
   1102 enum {
   1103 	REG_NOERROR,
   1104 	REG_NOMATCH,
   1105 	REG_BADPAT,
   1106 	REG_ERR_NULL_ARGUMENT,
   1107 	REG_ERR_UNKNOWN,
   1108 	REG_ERR_TOO_BIG,
   1109 	REG_ERR_NOMEM,
   1110 	REG_ERR_TOO_MANY_PAREN,
   1111 	REG_ERR_UNMATCHED_PAREN,
   1112 	REG_ERR_UNMATCHED_BRACES,
   1113 	REG_ERR_BAD_COUNT,
   1114 	REG_ERR_JUNK_ON_END,
   1115 	REG_ERR_OPERAND_COULD_BE_EMPTY,
   1116 	REG_ERR_NESTED_COUNT,
   1117 	REG_ERR_INTERNAL,
   1118 	REG_ERR_COUNT_FOLLOWS_NOTHING,
   1119 	REG_ERR_INVALID_ESCAPE,
   1120 	REG_ERR_CORRUPTED,
   1121 	REG_ERR_NULL_CHAR,
   1122 	REG_ERR_UNMATCHED_BRACKET,
   1123 	REG_ERR_NUM
   1124 };
   1125 
   1126 int jim_regcomp(regex_t *preg, const char *regex, int cflags);
   1127 int jim_regexec(regex_t  *preg,  const  char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
   1128 size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf,  size_t errbuf_size);
   1129 void jim_regfree(regex_t *preg);
   1130 
   1131 #ifdef __cplusplus
   1132 }
   1133 #endif
   1134 
   1135 #endif
   1136 #ifndef JIM_SIGNAL_H
   1137 #define JIM_SIGNAL_H
   1138 
   1139 #ifdef __cplusplus
   1140 extern "C" {
   1141 #endif
   1142 
   1143 const char *Jim_SignalId(int sig);
   1144 
   1145 #ifdef __cplusplus
   1146 }
   1147 #endif
   1148 
   1149 #endif
   1150 #ifndef JIMIOCOMPAT_H
   1151 #define JIMIOCOMPAT_H
   1152 
   1153 
   1154 #include <stdio.h>
   1155 #include <errno.h>
   1156 #include <sys/stat.h>
   1157 
   1158 
   1159 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg);
   1160 
   1161 int Jim_OpenForWrite(const char *filename, int append);
   1162 
   1163 int Jim_OpenForRead(const char *filename);
   1164 
   1165 #if defined(__MINGW32__) || defined(_WIN32)
   1166     #ifndef STRICT
   1167     #define STRICT
   1168     #endif
   1169     #define WIN32_LEAN_AND_MEAN
   1170     #include <windows.h>
   1171     #include <fcntl.h>
   1172     #include <io.h>
   1173     #include <process.h>
   1174 
   1175     typedef HANDLE phandle_t;
   1176     #define JIM_BAD_PHANDLE INVALID_HANDLE_VALUE
   1177 
   1178 
   1179     #define WIFEXITED(STATUS) (((STATUS) & 0xff00) == 0)
   1180     #define WEXITSTATUS(STATUS) ((STATUS) & 0x00ff)
   1181     #define WIFSIGNALED(STATUS) (((STATUS) & 0xff00) != 0)
   1182     #define WTERMSIG(STATUS) (((STATUS) >> 8) & 0xff)
   1183     #define WNOHANG 1
   1184 
   1185     int Jim_Errno(void);
   1186 
   1187     long waitpid(phandle_t phandle, int *status, int nohang);
   1188 
   1189     phandle_t JimWaitPid(long processid, int *status, int nohang);
   1190 
   1191     long JimProcessPid(phandle_t phandle);
   1192 
   1193     #define HAVE_PIPE
   1194     #define pipe(P) _pipe((P), 0, O_NOINHERIT)
   1195 
   1196     typedef struct __stat64 jim_stat_t;
   1197     #define Jim_Stat _stat64
   1198     #define Jim_FileStat _fstat64
   1199     #define Jim_Lseek _lseeki64
   1200     #define O_TEXT _O_TEXT
   1201     #define O_BINARY _O_BINARY
   1202     #define Jim_SetMode _setmode
   1203     #ifndef STDIN_FILENO
   1204     #define STDIN_FILENO 0
   1205     #endif
   1206 
   1207 #else
   1208     #if defined(HAVE_STAT64)
   1209         typedef struct stat64 jim_stat_t;
   1210         #define Jim_Stat stat64
   1211         #if defined(HAVE_FSTAT64)
   1212             #define Jim_FileStat fstat64
   1213         #endif
   1214         #if defined(HAVE_LSTAT64)
   1215             #define Jim_LinkStat lstat64
   1216         #endif
   1217     #else
   1218         typedef struct stat jim_stat_t;
   1219         #define Jim_Stat stat
   1220         #if defined(HAVE_FSTAT)
   1221             #define Jim_FileStat fstat
   1222         #endif
   1223         #if defined(HAVE_LSTAT)
   1224             #define Jim_LinkStat lstat
   1225         #endif
   1226     #endif
   1227     #if defined(HAVE_LSEEK64)
   1228         #define Jim_Lseek lseek64
   1229     #else
   1230         #define Jim_Lseek lseek
   1231     #endif
   1232 
   1233     #if defined(HAVE_UNISTD_H)
   1234         #include <unistd.h>
   1235         #include <fcntl.h>
   1236         #include <sys/wait.h>
   1237 
   1238         typedef int phandle_t;
   1239         #define Jim_Errno() errno
   1240         #define JIM_BAD_PHANDLE -1
   1241         #define JimProcessPid(PIDTYPE) (PIDTYPE)
   1242         #define JimWaitPid waitpid
   1243 
   1244         #ifndef HAVE_EXECVPE
   1245             #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
   1246         #endif
   1247     #endif
   1248 
   1249     #ifndef O_TEXT
   1250         #define O_TEXT 0
   1251     #endif
   1252 
   1253 #endif
   1254 
   1255 # ifndef MAXPATHLEN
   1256 # ifdef PATH_MAX
   1257 # define MAXPATHLEN PATH_MAX
   1258 # else
   1259 # define MAXPATHLEN JIM_PATH_LEN
   1260 # endif
   1261 # endif
   1262 
   1263 
   1264 int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb);
   1265 
   1266 #endif
   1267 int Jim_bootstrapInit(Jim_Interp *interp)
   1268 {
   1269 	if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
   1270 		return JIM_ERR;
   1271 
   1272 	return Jim_EvalSource(interp, "bootstrap.tcl", 1,
   1273 "\n"
   1274 "proc package {cmd args} {\n"
   1275 "	if {$cmd eq \"require\"} {\n"
   1276 "		foreach path $::auto_path {\n"
   1277 "			lassign $args pkg\n"
   1278 "			set pkgpath $path/$pkg.tcl\n"
   1279 "			if {$path eq \".\"} {\n"
   1280 "				set pkgpath $pkg.tcl\n"
   1281 "			}\n"
   1282 "			if {[file exists $pkgpath]} {\n"
   1283 "				tailcall uplevel #0 [list source $pkgpath]\n"
   1284 "			}\n"
   1285 "		}\n"
   1286 "	}\n"
   1287 "}\n"
   1288 "set tcl_platform(bootstrap) 1\n"
   1289 );
   1290 }
   1291 int Jim_initjimshInit(Jim_Interp *interp)
   1292 {
   1293 	if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
   1294 		return JIM_ERR;
   1295 
   1296 	return Jim_EvalSource(interp, "initjimsh.tcl", 1,
   1297 "\n"
   1298 "\n"
   1299 "\n"
   1300 "proc _jimsh_init {} {\n"
   1301 "	rename _jimsh_init {}\n"
   1302 "	global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
   1303 "\n"
   1304 "\n"
   1305 "	if {[exists jim::argv0]} {\n"
   1306 "		if {[string match \"*/*\" $jim::argv0]} {\n"
   1307 "			set jim::exe [file join [pwd] $jim::argv0]\n"
   1308 "		} else {\n"
   1309 "			set jim::argv0 [file tail $jim::argv0]\n"
   1310 "			set path [split [env PATH \"\"] $tcl_platform(pathSeparator)]\n"
   1311 "			if {$tcl_platform(platform) eq \"windows\"} {\n"
   1312 "\n"
   1313 "				set path [lmap p [list \"\" {*}$path] { string map {\\\\ /} $p }]\n"
   1314 "			}\n"
   1315 "			foreach p $path {\n"
   1316 "				set exec [file join [pwd] $p $jim::argv0]\n"
   1317 "				if {[file executable $exec]} {\n"
   1318 "					set jim::exe $exec\n"
   1319 "					break\n"
   1320 "				}\n"
   1321 "			}\n"
   1322 "		}\n"
   1323 "	}\n"
   1324 "\n"
   1325 "\n"
   1326 "	lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
   1327 "	if {[exists jim::exe]} {\n"
   1328 "		lappend p [file dirname $jim::exe]\n"
   1329 "	}\n"
   1330 "	lappend p {*}$auto_path\n"
   1331 "	set auto_path $p\n"
   1332 "\n"
   1333 "	if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
   1334 "		foreach src {.jimrc jimrc.tcl} {\n"
   1335 "			if {[file exists [env HOME]/$src]} {\n"
   1336 "				uplevel #0 source [env HOME]/$src\n"
   1337 "				break\n"
   1338 "			}\n"
   1339 "		}\n"
   1340 "	}\n"
   1341 "	return \"\"\n"
   1342 "}\n"
   1343 "\n"
   1344 "if {$tcl_platform(platform) eq \"windows\"} {\n"
   1345 "	set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
   1346 "}\n"
   1347 "\n"
   1348 "\n"
   1349 "set tcl::autocomplete_commands {array clock debug dict file history info namespace package signal socket string tcl::prefix zlib}\n"
   1350 "\n"
   1351 "\n"
   1352 "\n"
   1353 "proc tcl::autocomplete {prefix} {\n"
   1354 "	if {[set space [string first \" \" $prefix]] != -1} {\n"
   1355 "		set cmd [string range $prefix 0 $space-1]\n"
   1356 "		if {$cmd in $::tcl::autocomplete_commands || [info channel $cmd] ne \"\"} {\n"
   1357 "			set arg [string range $prefix $space+1 end]\n"
   1358 "\n"
   1359 "			return [lmap p [$cmd -commands] {\n"
   1360 "				if {![string match \"${arg}*\" $p]} continue\n"
   1361 "				function \"$cmd $p\"\n"
   1362 "			}]\n"
   1363 "		}\n"
   1364 "	}\n"
   1365 "\n"
   1366 "	if {[string match \"source *\" $prefix]} {\n"
   1367 "		set path [string range $prefix 7 end]\n"
   1368 "		return [lmap p [glob -nocomplain \"${path}*\"] {\n"
   1369 "			function \"source $p\"\n"
   1370 "		}]\n"
   1371 "	}\n"
   1372 "\n"
   1373 "	return [lmap p [lsort [info commands $prefix*]] {\n"
   1374 "		if {[string match \"* *\" $p]} {\n"
   1375 "			continue\n"
   1376 "		}\n"
   1377 "		function $p\n"
   1378 "	}]\n"
   1379 "}\n"
   1380 "\n"
   1381 "\n"
   1382 "set tcl::stdhint_commands {array clock debug dict file history info namespace package signal string zlib}\n"
   1383 "\n"
   1384 "set tcl::stdhint_cols {\n"
   1385 "	none {0}\n"
   1386 "	black {30}\n"
   1387 "	red {31}\n"
   1388 "	green {32}\n"
   1389 "	yellow {33}\n"
   1390 "	blue {34}\n"
   1391 "	purple {35}\n"
   1392 "	cyan {36}\n"
   1393 "	normal {37}\n"
   1394 "	grey {30 1}\n"
   1395 "	gray {30 1}\n"
   1396 "	lred {31 1}\n"
   1397 "	lgreen {32 1}\n"
   1398 "	lyellow {33 1}\n"
   1399 "	lblue {34 1}\n"
   1400 "	lpurple {35 1}\n"
   1401 "	lcyan {36 1}\n"
   1402 "	white {37 1}\n"
   1403 "}\n"
   1404 "\n"
   1405 "\n"
   1406 "set tcl::stdhint_col $tcl::stdhint_cols(lcyan)\n"
   1407 "\n"
   1408 "\n"
   1409 "proc tcl::stdhint {string} {\n"
   1410 "	set result \"\"\n"
   1411 "	if {[llength $string] >= 2} {\n"
   1412 "		lassign $string cmd arg\n"
   1413 "		if {$cmd in $::tcl::stdhint_commands || [info channel $cmd] ne \"\"} {\n"
   1414 "			catch {\n"
   1415 "				set help [$cmd -help $arg]\n"
   1416 "				if {[string match \"Usage: $cmd *\" $help]} {\n"
   1417 "					set n [llength $string]\n"
   1418 "					set subcmd [lindex $help $n]\n"
   1419 "					incr n\n"
   1420 "					set hint [join [lrange $help $n end]]\n"
   1421 "					set prefix \"\"\n"
   1422 "					if {![string match \"* \" $string]} {\n"
   1423 "						if {$n == 3 && $subcmd ne $arg} {\n"
   1424 "\n"
   1425 "							set prefix \"[string range $subcmd [string length $arg] end] \"\n"
   1426 "						} else {\n"
   1427 "							set prefix \" \"\n"
   1428 "						}\n"
   1429 "					}\n"
   1430 "					set result [list $prefix$hint {*}$::tcl::stdhint_col]\n"
   1431 "				}\n"
   1432 "			}\n"
   1433 "		}\n"
   1434 "	}\n"
   1435 "	return $result\n"
   1436 "}\n"
   1437 "\n"
   1438 "_jimsh_init\n"
   1439 );
   1440 }
   1441 int Jim_globInit(Jim_Interp *interp)
   1442 {
   1443 	if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
   1444 		return JIM_ERR;
   1445 
   1446 	return Jim_EvalSource(interp, "glob.tcl", 1,
   1447 "\n"
   1448 "\n"
   1449 "\n"
   1450 "\n"
   1451 "\n"
   1452 "\n"
   1453 "\n"
   1454 "package require readdir\n"
   1455 "\n"
   1456 "\n"
   1457 "proc glob.globdir {dir pattern} {\n"
   1458 "	if {[file exists $dir/$pattern]} {\n"
   1459 "\n"
   1460 "		return [list $pattern]\n"
   1461 "	}\n"
   1462 "\n"
   1463 "	set result {}\n"
   1464 "	set files [readdir $dir]\n"
   1465 "	lappend files . ..\n"
   1466 "\n"
   1467 "	foreach name $files {\n"
   1468 "		if {[string match $pattern $name]} {\n"
   1469 "\n"
   1470 "			if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n"
   1471 "				continue\n"
   1472 "			}\n"
   1473 "			lappend result $name\n"
   1474 "		}\n"
   1475 "	}\n"
   1476 "\n"
   1477 "	return $result\n"
   1478 "}\n"
   1479 "\n"
   1480 "\n"
   1481 "\n"
   1482 "\n"
   1483 "proc glob.explode {pattern} {\n"
   1484 "	set oldexp {}\n"
   1485 "	set newexp {\"\"}\n"
   1486 "\n"
   1487 "	while 1 {\n"
   1488 "		set oldexp $newexp\n"
   1489 "		set newexp {}\n"
   1490 "		set ob [string first \\{ $pattern]\n"
   1491 "		set cb [string first \\} $pattern]\n"
   1492 "\n"
   1493 "		if {$ob < $cb && $ob != -1} {\n"
   1494 "			set mid [string range $pattern 0 $ob-1]\n"
   1495 "			set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n"
   1496 "			if {$pattern eq \"\"} {\n"
   1497 "				error \"unmatched open brace in glob pattern\"\n"
   1498 "			}\n"
   1499 "			set pattern [string range $pattern 1 end]\n"
   1500 "\n"
   1501 "			foreach subs $subexp {\n"
   1502 "				foreach sub [split $subs ,] {\n"
   1503 "					foreach old $oldexp {\n"
   1504 "						lappend newexp $old$mid$sub\n"
   1505 "					}\n"
   1506 "				}\n"
   1507 "			}\n"
   1508 "		} elseif {$cb != -1} {\n"
   1509 "			set suf  [string range $pattern 0 $cb-1]\n"
   1510 "			set rest [string range $pattern $cb end]\n"
   1511 "			break\n"
   1512 "		} else {\n"
   1513 "			set suf  $pattern\n"
   1514 "			set rest \"\"\n"
   1515 "			break\n"
   1516 "		}\n"
   1517 "	}\n"
   1518 "\n"
   1519 "	foreach old $oldexp {\n"
   1520 "		lappend newexp $old$suf\n"
   1521 "	}\n"
   1522 "	list $rest {*}$newexp\n"
   1523 "}\n"
   1524 "\n"
   1525 "\n"
   1526 "\n"
   1527 "proc glob.glob {base pattern} {\n"
   1528 "	set dir [file dirname $pattern]\n"
   1529 "	if {$pattern eq $dir || $pattern eq \"\"} {\n"
   1530 "		return [list [file join $base $dir] $pattern]\n"
   1531 "	} elseif {$pattern eq [file tail $pattern]} {\n"
   1532 "		set dir \"\"\n"
   1533 "	}\n"
   1534 "\n"
   1535 "\n"
   1536 "	set dirlist [glob.glob $base $dir]\n"
   1537 "	set pattern [file tail $pattern]\n"
   1538 "\n"
   1539 "\n"
   1540 "	set result {}\n"
   1541 "	foreach {realdir dir} $dirlist {\n"
   1542 "		if {![file isdir $realdir]} {\n"
   1543 "			continue\n"
   1544 "		}\n"
   1545 "		if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n"
   1546 "			append dir /\n"
   1547 "		}\n"
   1548 "		foreach name [glob.globdir $realdir $pattern] {\n"
   1549 "			lappend result [file join $realdir $name] $dir$name\n"
   1550 "		}\n"
   1551 "	}\n"
   1552 "	return $result\n"
   1553 "}\n"
   1554 "\n"
   1555 "\n"
   1556 "\n"
   1557 "\n"
   1558 "\n"
   1559 "\n"
   1560 "\n"
   1561 "\n"
   1562 "\n"
   1563 "\n"
   1564 "\n"
   1565 "\n"
   1566 "proc glob {args} {\n"
   1567 "	set nocomplain 0\n"
   1568 "	set base \"\"\n"
   1569 "	set tails 0\n"
   1570 "\n"
   1571 "	set n 0\n"
   1572 "	foreach arg $args {\n"
   1573 "		if {[info exists param]} {\n"
   1574 "			set $param $arg\n"
   1575 "			unset param\n"
   1576 "			incr n\n"
   1577 "			continue\n"
   1578 "		}\n"
   1579 "		switch -glob -- $arg {\n"
   1580 "			-d* {\n"
   1581 "				set switch $arg\n"
   1582 "				set param base\n"
   1583 "			}\n"
   1584 "			-n* {\n"
   1585 "				set nocomplain 1\n"
   1586 "			}\n"
   1587 "			-ta* {\n"
   1588 "				set tails 1\n"
   1589 "			}\n"
   1590 "			-- {\n"
   1591 "				incr n\n"
   1592 "				break\n"
   1593 "			}\n"
   1594 "			-* {\n"
   1595 "				return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
   1596 "			}\n"
   1597 "			*  {\n"
   1598 "				break\n"
   1599 "			}\n"
   1600 "		}\n"
   1601 "		incr n\n"
   1602 "	}\n"
   1603 "	if {[info exists param]} {\n"
   1604 "		return -code error \"missing argument to \\\"$switch\\\"\"\n"
   1605 "	}\n"
   1606 "	if {[llength $args] <= $n} {\n"
   1607 "		return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n"
   1608 "	}\n"
   1609 "\n"
   1610 "	set args [lrange $args $n end]\n"
   1611 "\n"
   1612 "	set result {}\n"
   1613 "	foreach pattern $args {\n"
   1614 "		set escpattern [string map {\n"
   1615 "			\\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
   1616 "		} $pattern]\n"
   1617 "		set patexps [lassign [glob.explode $escpattern] rest]\n"
   1618 "		if {$rest ne \"\"} {\n"
   1619 "			return -code error \"unmatched close brace in glob pattern\"\n"
   1620 "		}\n"
   1621 "		foreach patexp $patexps {\n"
   1622 "			set patexp [string map {\n"
   1623 "				\\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
   1624 "			} $patexp]\n"
   1625 "			foreach {realname name} [glob.glob $base $patexp] {\n"
   1626 "				incr n\n"
   1627 "				if {$tails} {\n"
   1628 "					lappend result $name\n"
   1629 "				} else {\n"
   1630 "					lappend result [file join $base $name]\n"
   1631 "				}\n"
   1632 "			}\n"
   1633 "		}\n"
   1634 "	}\n"
   1635 "\n"
   1636 "	if {!$nocomplain && [llength $result] == 0} {\n"
   1637 "		set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
   1638 "		return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
   1639 "	}\n"
   1640 "\n"
   1641 "	return $result\n"
   1642 "}\n"
   1643 );
   1644 }
   1645 int Jim_stdlibInit(Jim_Interp *interp)
   1646 {
   1647 	if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
   1648 		return JIM_ERR;
   1649 
   1650 	return Jim_EvalSource(interp, "stdlib.tcl", 1,
   1651 "\n"
   1652 "\n"
   1653 "if {![exists -command ref]} {\n"
   1654 "\n"
   1655 "	proc ref {args} {{count 0}} {\n"
   1656 "		format %08x [incr count]\n"
   1657 "	}\n"
   1658 "}\n"
   1659 "\n"
   1660 "\n"
   1661 "proc lambda {arglist args} {\n"
   1662 "	tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
   1663 "}\n"
   1664 "\n"
   1665 "proc lambda.finalizer {name val} {\n"
   1666 "	rename $name {}\n"
   1667 "}\n"
   1668 "\n"
   1669 "\n"
   1670 "proc curry {args} {\n"
   1671 "	alias [ref {} function lambda.finalizer] {*}$args\n"
   1672 "}\n"
   1673 "\n"
   1674 "\n"
   1675 "\n"
   1676 "\n"
   1677 "\n"
   1678 "\n"
   1679 "\n"
   1680 "\n"
   1681 "\n"
   1682 "proc function {value} {\n"
   1683 "	return $value\n"
   1684 "}\n"
   1685 "\n"
   1686 "\n"
   1687 "proc stackdump {stacktrace} {\n"
   1688 "	set lines {}\n"
   1689 "	lappend lines \"Traceback (most recent call last):\"\n"
   1690 "	foreach {cmd l f p} [lreverse $stacktrace] {\n"
   1691 "		set line {}\n"
   1692 "		if {$f ne \"\"} {\n"
   1693 "			append line \"  File \\\"$f\\\", line $l\"\n"
   1694 "		}\n"
   1695 "		if {$p ne \"\"} {\n"
   1696 "			append line \", in $p\"\n"
   1697 "		}\n"
   1698 "		if {$line ne \"\"} {\n"
   1699 "			lappend lines $line\n"
   1700 "			if {$cmd ne \"\"} {\n"
   1701 "				set nl [string first \\n $cmd 1]\n"
   1702 "				if {$nl >= 0} {\n"
   1703 "					set cmd [string range $cmd 0 $nl-1]...\n"
   1704 "				}\n"
   1705 "				lappend lines \"    $cmd\"\n"
   1706 "			}\n"
   1707 "		}\n"
   1708 "	}\n"
   1709 "	if {[llength $lines] > 1} {\n"
   1710 "		return [join $lines \\n]\n"
   1711 "	}\n"
   1712 "}\n"
   1713 "\n"
   1714 "\n"
   1715 "\n"
   1716 "proc defer {script} {\n"
   1717 "	upvar jim::defer v\n"
   1718 "	lappend v $script\n"
   1719 "}\n"
   1720 "\n"
   1721 "\n"
   1722 "\n"
   1723 "proc errorInfo {msg {stacktrace \"\"}} {\n"
   1724 "	if {$stacktrace eq \"\"} {\n"
   1725 "\n"
   1726 "		set stacktrace [info stacktrace]\n"
   1727 "	}\n"
   1728 "	lassign $stacktrace p f l cmd\n"
   1729 "	if {$f ne \"\"} {\n"
   1730 "		set result \"$f:$l: Error: \"\n"
   1731 "	}\n"
   1732 "	append result \"$msg\\n\"\n"
   1733 "	append result [stackdump $stacktrace]\n"
   1734 "\n"
   1735 "\n"
   1736 "	string trim $result\n"
   1737 "}\n"
   1738 "\n"
   1739 "\n"
   1740 "\n"
   1741 "proc {info nameofexecutable} {} {\n"
   1742 "	if {[exists ::jim::exe]} {\n"
   1743 "		return $::jim::exe\n"
   1744 "	}\n"
   1745 "}\n"
   1746 "\n"
   1747 "\n"
   1748 "proc {dict update} {&varName args script} {\n"
   1749 "	set keys {}\n"
   1750 "	foreach {n v} $args {\n"
   1751 "		upvar $v var_$v\n"
   1752 "		if {[dict exists $varName $n]} {\n"
   1753 "			set var_$v [dict get $varName $n]\n"
   1754 "		}\n"
   1755 "	}\n"
   1756 "	catch {uplevel 1 $script} msg opts\n"
   1757 "	if {[info exists varName]} {\n"
   1758 "		foreach {n v} $args {\n"
   1759 "			if {[info exists var_$v]} {\n"
   1760 "				dict set varName $n [set var_$v]\n"
   1761 "			} else {\n"
   1762 "				dict unset varName $n\n"
   1763 "			}\n"
   1764 "		}\n"
   1765 "	}\n"
   1766 "	return {*}$opts $msg\n"
   1767 "}\n"
   1768 "\n"
   1769 "proc {dict replace} {dictionary {args {key value}}} {\n"
   1770 "	if {[llength ${key value}] % 2} {\n"
   1771 "		tailcall {dict replace}\n"
   1772 "	}\n"
   1773 "	tailcall dict merge $dictionary ${key value}\n"
   1774 "}\n"
   1775 "\n"
   1776 "\n"
   1777 "proc {dict lappend} {varName key {args value}} {\n"
   1778 "	upvar $varName dict\n"
   1779 "	if {[exists dict] && [dict exists $dict $key]} {\n"
   1780 "		set list [dict get $dict $key]\n"
   1781 "	}\n"
   1782 "	lappend list {*}$value\n"
   1783 "	dict set dict $key $list\n"
   1784 "}\n"
   1785 "\n"
   1786 "\n"
   1787 "proc {dict append} {varName key {args value}} {\n"
   1788 "	upvar $varName dict\n"
   1789 "	if {[exists dict] && [dict exists $dict $key]} {\n"
   1790 "		set str [dict get $dict $key]\n"
   1791 "	}\n"
   1792 "	append str {*}$value\n"
   1793 "	dict set dict $key $str\n"
   1794 "}\n"
   1795 "\n"
   1796 "\n"
   1797 "proc {dict incr} {varName key {increment 1}} {\n"
   1798 "	upvar $varName dict\n"
   1799 "	if {[exists dict] && [dict exists $dict $key]} {\n"
   1800 "		set value [dict get $dict $key]\n"
   1801 "	}\n"
   1802 "	incr value $increment\n"
   1803 "	dict set dict $key $value\n"
   1804 "}\n"
   1805 "\n"
   1806 "\n"
   1807 "proc {dict remove} {dictionary {args key}} {\n"
   1808 "	foreach k $key {\n"
   1809 "		dict unset dictionary $k\n"
   1810 "	}\n"
   1811 "	return $dictionary\n"
   1812 "}\n"
   1813 "\n"
   1814 "\n"
   1815 "proc {dict for} {vars dictionary script} {\n"
   1816 "	if {[llength $vars] != 2} {\n"
   1817 "		return -code error \"must have exactly two variable names\"\n"
   1818 "	}\n"
   1819 "	dict size $dictionary\n"
   1820 "	tailcall foreach $vars $dictionary $script\n"
   1821 "}\n"
   1822 );
   1823 }
   1824 int Jim_tclcompatInit(Jim_Interp *interp)
   1825 {
   1826 	if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG))
   1827 		return JIM_ERR;
   1828 
   1829 	return Jim_EvalSource(interp, "tclcompat.tcl", 1,
   1830 "\n"
   1831 "\n"
   1832 "\n"
   1833 "\n"
   1834 "\n"
   1835 "\n"
   1836 "\n"
   1837 "\n"
   1838 "set env [env]\n"
   1839 "\n"
   1840 "\n"
   1841 "if {[exists -command stdout]} {\n"
   1842 "\n"
   1843 "	foreach p {gets flush close eof seek tell} {\n"
   1844 "		proc $p {chan args} {p} {\n"
   1845 "			tailcall $chan $p {*}$args\n"
   1846 "		}\n"
   1847 "	}\n"
   1848 "	unset p\n"
   1849 "\n"
   1850 "\n"
   1851 "\n"
   1852 "	proc puts {{-nonewline {}} {chan stdout} msg} {\n"
   1853 "		if {${-nonewline} ni {-nonewline {}}} {\n"
   1854 "			tailcall ${-nonewline} puts $msg\n"
   1855 "		}\n"
   1856 "		tailcall $chan puts {*}${-nonewline} $msg\n"
   1857 "	}\n"
   1858 "\n"
   1859 "\n"
   1860 "\n"
   1861 "\n"
   1862 "\n"
   1863 "	proc read {{-nonewline {}} chan} {\n"
   1864 "		if {${-nonewline} ni {-nonewline {}}} {\n"
   1865 "			tailcall ${-nonewline} read {*}${chan}\n"
   1866 "		}\n"
   1867 "		tailcall $chan read {*}${-nonewline}\n"
   1868 "	}\n"
   1869 "\n"
   1870 "	proc fconfigure {f args} {\n"
   1871 "		foreach {n v} $args {\n"
   1872 "			switch -glob -- $n {\n"
   1873 "				-bl* {\n"
   1874 "					$f ndelay $(!$v)\n"
   1875 "				}\n"
   1876 "				-bu* {\n"
   1877 "					$f buffering $v\n"
   1878 "				}\n"
   1879 "				-tr* {\n"
   1880 "					$f translation $v\n"
   1881 "				}\n"
   1882 "				default {\n"
   1883 "					return -code error \"fconfigure: unknown option $n\"\n"
   1884 "				}\n"
   1885 "			}\n"
   1886 "		}\n"
   1887 "	}\n"
   1888 "}\n"
   1889 "\n"
   1890 "\n"
   1891 "proc fileevent {args} {\n"
   1892 "	tailcall {*}$args\n"
   1893 "}\n"
   1894 "\n"
   1895 "\n"
   1896 "\n"
   1897 "proc parray {arrayname {pattern *} {puts puts}} {\n"
   1898 "	upvar $arrayname a\n"
   1899 "\n"
   1900 "	set max 0\n"
   1901 "	foreach name [array names a $pattern]] {\n"
   1902 "		if {[string length $name] > $max} {\n"
   1903 "			set max [string length $name]\n"
   1904 "		}\n"
   1905 "	}\n"
   1906 "	incr max [string length $arrayname]\n"
   1907 "	incr max 2\n"
   1908 "	foreach name [lsort [array names a $pattern]] {\n"
   1909 "		$puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
   1910 "	}\n"
   1911 "}\n"
   1912 "\n"
   1913 "\n"
   1914 "proc {file copy} {{force {}} source target} {\n"
   1915 "	try {\n"
   1916 "		if {$force ni {{} -force}} {\n"
   1917 "			error \"bad option \\\"$force\\\": should be -force\"\n"
   1918 "		}\n"
   1919 "\n"
   1920 "		set in [open $source rb]\n"
   1921 "\n"
   1922 "		if {[file exists $target]} {\n"
   1923 "			if {$force eq \"\"} {\n"
   1924 "				error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
   1925 "			}\n"
   1926 "\n"
   1927 "			if {$source eq $target} {\n"
   1928 "				return\n"
   1929 "			}\n"
   1930 "\n"
   1931 "\n"
   1932 "			file stat $source ss\n"
   1933 "			file stat $target ts\n"
   1934 "			if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
   1935 "				return\n"
   1936 "			}\n"
   1937 "		}\n"
   1938 "		set out [open $target wb]\n"
   1939 "		$in copyto $out\n"
   1940 "		$out close\n"
   1941 "	} on error {msg opts} {\n"
   1942 "		incr opts(-level)\n"
   1943 "		return {*}$opts $msg\n"
   1944 "	} finally {\n"
   1945 "		catch {$in close}\n"
   1946 "	}\n"
   1947 "}\n"
   1948 "\n"
   1949 "\n"
   1950 "\n"
   1951 "proc popen {cmd {mode r}} {\n"
   1952 "	lassign [pipe] r w\n"
   1953 "	try {\n"
   1954 "		if {[string match \"w*\" $mode]} {\n"
   1955 "			lappend cmd <@$r &\n"
   1956 "			set pids [exec {*}$cmd]\n"
   1957 "			$r close\n"
   1958 "			set f $w\n"
   1959 "		} else {\n"
   1960 "			lappend cmd >@$w &\n"
   1961 "			set pids [exec {*}$cmd]\n"
   1962 "			$w close\n"
   1963 "			set f $r\n"
   1964 "		}\n"
   1965 "		lambda {cmd args} {f pids} {\n"
   1966 "			if {$cmd eq \"pid\"} {\n"
   1967 "				return $pids\n"
   1968 "			}\n"
   1969 "			if {$cmd eq \"close\"} {\n"
   1970 "				$f close\n"
   1971 "\n"
   1972 "				set retopts {}\n"
   1973 "				foreach p $pids {\n"
   1974 "					lassign [wait $p] status - rc\n"
   1975 "					if {$status eq \"CHILDSTATUS\"} {\n"
   1976 "						if {$rc == 0} {\n"
   1977 "							continue\n"
   1978 "						}\n"
   1979 "						set msg \"child process exited abnormally\"\n"
   1980 "					} else {\n"
   1981 "						set msg \"child killed: received signal\"\n"
   1982 "					}\n"
   1983 "					set retopts [list -code error -errorcode [list $status $p $rc] $msg]\n"
   1984 "				}\n"
   1985 "				return {*}$retopts\n"
   1986 "			}\n"
   1987 "			tailcall $f $cmd {*}$args\n"
   1988 "		}\n"
   1989 "	} on error {error opts} {\n"
   1990 "		$r close\n"
   1991 "		$w close\n"
   1992 "		error $error\n"
   1993 "	}\n"
   1994 "}\n"
   1995 "\n"
   1996 "\n"
   1997 "local proc pid {{channelId {}}} {\n"
   1998 "	if {$channelId eq \"\"} {\n"
   1999 "		tailcall upcall pid\n"
   2000 "	}\n"
   2001 "	if {[catch {$channelId tell}]} {\n"
   2002 "		return -code error \"can not find channel named \\\"$channelId\\\"\"\n"
   2003 "	}\n"
   2004 "	if {[catch {$channelId pid} pids]} {\n"
   2005 "		return \"\"\n"
   2006 "	}\n"
   2007 "	return $pids\n"
   2008 "}\n"
   2009 "\n"
   2010 "\n"
   2011 "\n"
   2012 "proc throw {code {msg \"\"}} {\n"
   2013 "	return -code $code $msg\n"
   2014 "}\n"
   2015 "\n"
   2016 "\n"
   2017 "proc {file delete force} {path} {\n"
   2018 "	foreach e [readdir $path] {\n"
   2019 "		file delete -force $path/$e\n"
   2020 "	}\n"
   2021 "	file delete $path\n"
   2022 "}\n"
   2023 );
   2024 }
   2025 
   2026 
   2027 #include <stdio.h>
   2028 #include <string.h>
   2029 #include <errno.h>
   2030 #include <fcntl.h>
   2031 #include <assert.h>
   2032 #ifdef HAVE_UNISTD_H
   2033 #include <unistd.h>
   2034 #include <sys/stat.h>
   2035 #endif
   2036 #ifdef HAVE_UTIL_H
   2037 #include <util.h>
   2038 #endif
   2039 #ifdef HAVE_PTY_H
   2040 #include <pty.h>
   2041 #endif
   2042 
   2043 
   2044 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
   2045 #include <sys/socket.h>
   2046 #include <netinet/in.h>
   2047 #include <netinet/tcp.h>
   2048 #include <arpa/inet.h>
   2049 #include <netdb.h>
   2050 #ifdef HAVE_SYS_UN_H
   2051 #include <sys/un.h>
   2052 #endif
   2053 #define HAVE_SOCKETS
   2054 #elif defined (__MINGW32__)
   2055 
   2056 #endif
   2057 
   2058 #if defined(JIM_SSL)
   2059 #include <openssl/ssl.h>
   2060 #include <openssl/err.h>
   2061 #endif
   2062 
   2063 #ifdef HAVE_TERMIOS_H
   2064 #endif
   2065 
   2066 
   2067 #define AIO_CMD_LEN 32
   2068 #define AIO_DEFAULT_RBUF_LEN 256
   2069 #define AIO_DEFAULT_WBUF_LIMIT (64 * 1024)
   2070 
   2071 #define AIO_KEEPOPEN 1
   2072 #define AIO_NODELETE 2
   2073 #define AIO_EOF 4
   2074 #define AIO_WBUF_NONE 8
   2075 #define AIO_NONBLOCK 16
   2076 
   2077 #define AIO_ONEREAD 32
   2078 
   2079 enum wbuftype {
   2080     WBUF_OPT_NONE,
   2081     WBUF_OPT_LINE,
   2082     WBUF_OPT_FULL,
   2083 };
   2084 
   2085 #if defined(JIM_IPV6)
   2086 #define IPV6 1
   2087 #else
   2088 #define IPV6 0
   2089 #ifndef PF_INET6
   2090 #define PF_INET6 0
   2091 #endif
   2092 #endif
   2093 #if defined(HAVE_SYS_UN_H) && defined(PF_UNIX)
   2094 #define UNIX_SOCKETS 1
   2095 #else
   2096 #define UNIX_SOCKETS 0
   2097 #endif
   2098 
   2099 
   2100 
   2101 
   2102 static int JimReadableTimeout(int fd, long ms)
   2103 {
   2104 #ifdef HAVE_SELECT
   2105     int retval;
   2106     struct timeval tv;
   2107     fd_set rfds;
   2108 
   2109     FD_ZERO(&rfds);
   2110 
   2111     FD_SET(fd, &rfds);
   2112     tv.tv_sec = ms / 1000;
   2113     tv.tv_usec = (ms % 1000) * 1000;
   2114 
   2115     retval = select(fd + 1, &rfds, NULL, NULL, ms == 0 ? NULL : &tv);
   2116 
   2117     if (retval > 0) {
   2118         return JIM_OK;
   2119     }
   2120     return JIM_ERR;
   2121 #else
   2122     return JIM_OK;
   2123 #endif
   2124 }
   2125 
   2126 
   2127 struct AioFile;
   2128 
   2129 typedef struct {
   2130     int (*writer)(struct AioFile *af, const char *buf, int len);
   2131     int (*reader)(struct AioFile *af, char *buf, int len, int pending);
   2132     int (*error)(const struct AioFile *af);
   2133     const char *(*strerror)(struct AioFile *af);
   2134     int (*verify)(struct AioFile *af);
   2135 } JimAioFopsType;
   2136 
   2137 typedef struct AioFile
   2138 {
   2139     Jim_Obj *filename;
   2140     int wbuft;
   2141     int flags;
   2142     long timeout;
   2143     int fd;
   2144     int addr_family;
   2145     void *ssl;
   2146     const JimAioFopsType *fops;
   2147     Jim_Obj *readbuf;
   2148     Jim_Obj *writebuf;
   2149     char *rbuf;
   2150     size_t rbuf_len;
   2151     size_t wbuf_limit;
   2152 } AioFile;
   2153 
   2154 static void aio_consume(Jim_Obj *objPtr, int n);
   2155 
   2156 static int stdio_writer(struct AioFile *af, const char *buf, int len)
   2157 {
   2158     int ret = write(af->fd, buf, len);
   2159     if (ret < 0 && errno == EPIPE) {
   2160         aio_consume(af->writebuf, Jim_Length(af->writebuf));
   2161     }
   2162     return ret;
   2163 }
   2164 
   2165 static int stdio_reader(struct AioFile *af, char *buf, int len, int nb)
   2166 {
   2167     if (nb || af->timeout == 0 || JimReadableTimeout(af->fd, af->timeout) == JIM_OK) {
   2168 
   2169         int ret;
   2170 
   2171         errno = 0;
   2172         ret = read(af->fd, buf, len);
   2173         if (ret <= 0 && errno != EAGAIN && errno != EINTR) {
   2174             af->flags |= AIO_EOF;
   2175         }
   2176         return ret;
   2177     }
   2178     errno = ETIMEDOUT;
   2179     return -1;
   2180 }
   2181 
   2182 static int stdio_error(const AioFile *af)
   2183 {
   2184     if (af->flags & AIO_EOF) {
   2185         return JIM_OK;
   2186     }
   2187 
   2188     switch (errno) {
   2189         case EAGAIN:
   2190         case EINTR:
   2191         case ETIMEDOUT:
   2192 #ifdef ECONNRESET
   2193         case ECONNRESET:
   2194 #endif
   2195 #ifdef ECONNABORTED
   2196         case ECONNABORTED:
   2197 #endif
   2198             return JIM_OK;
   2199         default:
   2200             return JIM_ERR;
   2201     }
   2202 }
   2203 
   2204 static const char *stdio_strerror(struct AioFile *af)
   2205 {
   2206     return strerror(errno);
   2207 }
   2208 
   2209 static const JimAioFopsType stdio_fops = {
   2210     stdio_writer,
   2211     stdio_reader,
   2212     stdio_error,
   2213     stdio_strerror,
   2214     NULL,
   2215 };
   2216 
   2217 
   2218 static void aio_set_nonblocking(AioFile *af, int nb)
   2219 {
   2220 #ifdef O_NDELAY
   2221     int old = !!(af->flags & AIO_NONBLOCK);
   2222     if (old != nb) {
   2223         int fmode = fcntl(af->fd, F_GETFL);
   2224         if (nb) {
   2225             fmode |= O_NDELAY;
   2226             af->flags |= AIO_NONBLOCK;
   2227         }
   2228         else {
   2229             fmode &= ~O_NDELAY;
   2230             af->flags &= ~AIO_NONBLOCK;
   2231         }
   2232         (void)fcntl(af->fd, F_SETFL, fmode);
   2233     }
   2234 #endif
   2235 }
   2236 
   2237 static int aio_start_nonblocking(AioFile *af)
   2238 {
   2239     int old = !!(af->flags & AIO_NONBLOCK);
   2240     if (af->timeout) {
   2241         aio_set_nonblocking(af, 1);
   2242     }
   2243     return old;
   2244 }
   2245 
   2246 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
   2247 static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
   2248     const char *hdlfmt, int family, int flags);
   2249 
   2250 
   2251 static const char *JimAioErrorString(AioFile *af)
   2252 {
   2253     if (af && af->fops)
   2254         return af->fops->strerror(af);
   2255 
   2256     return strerror(errno);
   2257 }
   2258 
   2259 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
   2260 {
   2261     AioFile *af = Jim_CmdPrivData(interp);
   2262 
   2263     if (name) {
   2264         Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
   2265     }
   2266     else {
   2267         Jim_SetResultString(interp, JimAioErrorString(af), -1);
   2268     }
   2269 }
   2270 
   2271 static int aio_eof(AioFile *af)
   2272 {
   2273     return af->flags & AIO_EOF;
   2274 }
   2275 
   2276 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
   2277 {
   2278     int ret = 0;
   2279     if (!aio_eof(af)) {
   2280         ret = af->fops->error(af);
   2281         if (ret) {
   2282             JimAioSetError(interp, af->filename);
   2283         }
   2284     }
   2285     return ret;
   2286 }
   2287 
   2288 static void aio_consume(Jim_Obj *objPtr, int n)
   2289 {
   2290     assert(objPtr->bytes);
   2291     assert(n <= objPtr->length);
   2292 
   2293 
   2294     memmove(objPtr->bytes, objPtr->bytes + n, objPtr->length - n + 1);
   2295     objPtr->length -= n;
   2296 }
   2297 
   2298 
   2299 static int aio_flush(Jim_Interp *interp, AioFile *af);
   2300 
   2301 #ifdef jim_ext_eventloop
   2302 static int aio_autoflush(Jim_Interp *interp, void *clientData, int mask)
   2303 {
   2304     AioFile *af = clientData;
   2305 
   2306     aio_flush(interp, af);
   2307     if (Jim_Length(af->writebuf) == 0) {
   2308 
   2309         return -1;
   2310     }
   2311     return 0;
   2312 }
   2313 #endif
   2314 
   2315 
   2316 static int aio_flush(Jim_Interp *interp, AioFile *af)
   2317 {
   2318     int len;
   2319     const char *pt = Jim_GetString(af->writebuf, &len);
   2320     if (len) {
   2321         int ret = af->fops->writer(af, pt, len);
   2322         if (ret > 0) {
   2323 
   2324             aio_consume(af->writebuf, ret);
   2325         }
   2326         if (ret < 0) {
   2327             return JimCheckStreamError(interp, af);
   2328         }
   2329         if (Jim_Length(af->writebuf)) {
   2330 #ifdef jim_ext_eventloop
   2331             void *handler = Jim_FindFileHandler(interp, af->fd, JIM_EVENT_WRITABLE);
   2332             if (handler == NULL) {
   2333                 Jim_CreateFileHandler(interp, af->fd, JIM_EVENT_WRITABLE, aio_autoflush, af, NULL);
   2334                 return JIM_OK;
   2335             }
   2336             else if (handler == af) {
   2337 
   2338                 return JIM_OK;
   2339             }
   2340 #endif
   2341 
   2342             Jim_SetResultString(interp, "send buffer is full", -1);
   2343             return JIM_ERR;
   2344         }
   2345     }
   2346     return JIM_OK;
   2347 }
   2348 
   2349 static int aio_read_len(Jim_Interp *interp, AioFile *af, unsigned flags, int neededLen)
   2350 {
   2351     if (!af->readbuf) {
   2352         af->readbuf = Jim_NewStringObj(interp, NULL, 0);
   2353     }
   2354 
   2355     if (neededLen >= 0) {
   2356         neededLen -= Jim_Length(af->readbuf);
   2357         if (neededLen <= 0) {
   2358             return JIM_OK;
   2359         }
   2360     }
   2361 
   2362     while (neededLen && !aio_eof(af)) {
   2363         int retval;
   2364         int readlen;
   2365 
   2366         if (neededLen == -1) {
   2367             readlen = af->rbuf_len;
   2368         }
   2369         else {
   2370             readlen = (neededLen > af->rbuf_len ? af->rbuf_len : neededLen);
   2371         }
   2372 
   2373         if (!af->rbuf) {
   2374             af->rbuf = Jim_Alloc(af->rbuf_len);
   2375         }
   2376         retval = af->fops->reader(af, af->rbuf, readlen, flags & AIO_NONBLOCK);
   2377         if (retval > 0) {
   2378             if (retval) {
   2379                 Jim_AppendString(interp, af->readbuf, af->rbuf, retval);
   2380             }
   2381             if (neededLen != -1) {
   2382                 neededLen -= retval;
   2383             }
   2384             if (flags & AIO_ONEREAD) {
   2385                 return JIM_OK;
   2386             }
   2387             continue;
   2388         }
   2389         if ((flags & AIO_ONEREAD) || JimCheckStreamError(interp, af)) {
   2390             return JIM_ERR;
   2391         }
   2392         break;
   2393     }
   2394 
   2395     return JIM_OK;
   2396 }
   2397 
   2398 static Jim_Obj *aio_read_consume(Jim_Interp *interp, AioFile *af, int neededLen)
   2399 {
   2400     Jim_Obj *objPtr = NULL;
   2401 
   2402     if (neededLen < 0 || af->readbuf == NULL || Jim_Length(af->readbuf) <= neededLen) {
   2403         objPtr = af->readbuf;
   2404         af->readbuf = NULL;
   2405     }
   2406     else if (af->readbuf) {
   2407 
   2408         int len;
   2409         const char *pt = Jim_GetString(af->readbuf, &len);
   2410 
   2411         objPtr  = Jim_NewStringObj(interp, pt, neededLen);
   2412         aio_consume(af->readbuf, neededLen);
   2413     }
   2414 
   2415     return objPtr;
   2416 }
   2417 
   2418 static void JimAioDelProc(Jim_Interp *interp, void *privData)
   2419 {
   2420     AioFile *af = privData;
   2421 
   2422     JIM_NOTUSED(interp);
   2423 
   2424 
   2425     aio_flush(interp, af);
   2426     Jim_DecrRefCount(interp, af->writebuf);
   2427 
   2428 #if UNIX_SOCKETS
   2429     if (af->addr_family == PF_UNIX && (af->flags & AIO_NODELETE) == 0) {
   2430 
   2431         Jim_Obj *filenameObj = aio_sockname(interp, af->fd);
   2432         if (filenameObj) {
   2433             if (Jim_Length(filenameObj)) {
   2434                 remove(Jim_String(filenameObj));
   2435             }
   2436             Jim_FreeNewObj(interp, filenameObj);
   2437         }
   2438     }
   2439 #endif
   2440 
   2441     Jim_DecrRefCount(interp, af->filename);
   2442 
   2443 #ifdef jim_ext_eventloop
   2444 
   2445     Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
   2446 #endif
   2447 
   2448 #if defined(JIM_SSL)
   2449     if (af->ssl != NULL) {
   2450         SSL_free(af->ssl);
   2451     }
   2452 #endif
   2453     if (!(af->flags & AIO_KEEPOPEN)) {
   2454         close(af->fd);
   2455     }
   2456     if (af->readbuf) {
   2457         Jim_FreeNewObj(interp, af->readbuf);
   2458     }
   2459 
   2460     Jim_Free(af->rbuf);
   2461     Jim_Free(af);
   2462 }
   2463 
   2464 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2465 {
   2466     AioFile *af = Jim_CmdPrivData(interp);
   2467     int nonewline = 0;
   2468     jim_wide neededLen = -1;
   2469     static const char * const options[] = { "-pending", "-nonewline", NULL };
   2470     enum { OPT_PENDING, OPT_NONEWLINE };
   2471     int option;
   2472     int nb;
   2473     Jim_Obj *objPtr;
   2474 
   2475     if (argc) {
   2476         if (*Jim_String(argv[0]) == '-') {
   2477             if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
   2478                 return JIM_ERR;
   2479             }
   2480             switch (option) {
   2481                 case OPT_PENDING:
   2482 
   2483                     break;
   2484                 case OPT_NONEWLINE:
   2485                     nonewline++;
   2486                     break;
   2487             }
   2488         }
   2489         else {
   2490             if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
   2491                 return JIM_ERR;
   2492             if (neededLen < 0) {
   2493                 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
   2494                 return JIM_ERR;
   2495             }
   2496         }
   2497         argc--;
   2498         argv++;
   2499     }
   2500     if (argc) {
   2501         return -1;
   2502     }
   2503 
   2504 
   2505     nb = aio_start_nonblocking(af);
   2506 
   2507     if (aio_read_len(interp, af, nb ? AIO_NONBLOCK : 0, neededLen) != JIM_OK) {
   2508         aio_set_nonblocking(af, nb);
   2509         return JIM_ERR;
   2510     }
   2511     objPtr = aio_read_consume(interp, af, neededLen);
   2512 
   2513     aio_set_nonblocking(af, nb);
   2514 
   2515     if (objPtr) {
   2516         if (nonewline) {
   2517             int len;
   2518             const char *s = Jim_GetString(objPtr, &len);
   2519 
   2520             if (len > 0 && s[len - 1] == '\n') {
   2521                 objPtr->length--;
   2522                 objPtr->bytes[objPtr->length] = '\0';
   2523             }
   2524         }
   2525         Jim_SetResult(interp, objPtr);
   2526     }
   2527     else {
   2528         Jim_SetEmptyResult(interp);
   2529     }
   2530     return JIM_OK;
   2531 }
   2532 
   2533 int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
   2534 {
   2535     Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
   2536 
   2537 
   2538     if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
   2539         return ((AioFile *) cmdPtr->u.native.privData)->fd;
   2540     }
   2541     Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
   2542     return -1;
   2543 }
   2544 
   2545 static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2546 {
   2547     AioFile *af = Jim_CmdPrivData(interp);
   2548 
   2549 
   2550     aio_flush(interp, af);
   2551 
   2552     Jim_SetResultInt(interp, af->fd);
   2553 
   2554     return JIM_OK;
   2555 }
   2556 
   2557 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2558 {
   2559     AioFile *af = Jim_CmdPrivData(interp);
   2560     jim_wide count = 0;
   2561     jim_wide maxlen = JIM_WIDE_MAX;
   2562     int ok = 1;
   2563     Jim_Obj *objv[4];
   2564 
   2565     if (argc == 2) {
   2566         if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
   2567             return JIM_ERR;
   2568         }
   2569     }
   2570 
   2571     objv[0] = argv[0];
   2572     objv[1] = Jim_NewStringObj(interp, "flush", -1);
   2573     if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
   2574         Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", argv[0]);
   2575         return JIM_ERR;
   2576     }
   2577 
   2578 
   2579     objv[0] = argv[0];
   2580     objv[1] = Jim_NewStringObj(interp, "puts", -1);
   2581     objv[2] = Jim_NewStringObj(interp, "-nonewline", -1);
   2582     Jim_IncrRefCount(objv[1]);
   2583     Jim_IncrRefCount(objv[2]);
   2584 
   2585     while (count < maxlen) {
   2586         jim_wide len = maxlen - count;
   2587         if (len > af->rbuf_len) {
   2588             len = af->rbuf_len;
   2589         }
   2590         if (aio_read_len(interp, af, 0, len) != JIM_OK) {
   2591             ok = 0;
   2592             break;
   2593         }
   2594         objv[3] = aio_read_consume(interp, af, len);
   2595         count += Jim_Length(objv[3]);
   2596         if (Jim_EvalObjVector(interp, 4, objv) != JIM_OK) {
   2597             ok = 0;
   2598             break;
   2599         }
   2600         if (aio_eof(af)) {
   2601             break;
   2602         }
   2603         if (count >= 16384 && af->rbuf_len < 65536) {
   2604 
   2605             af->rbuf_len = 65536;
   2606             af->rbuf = Jim_Realloc(af->rbuf, af->rbuf_len);
   2607         }
   2608     }
   2609 
   2610     Jim_DecrRefCount(interp, objv[1]);
   2611     Jim_DecrRefCount(interp, objv[2]);
   2612 
   2613     if (!ok) {
   2614         return JIM_ERR;
   2615     }
   2616 
   2617     Jim_SetResultInt(interp, count);
   2618 
   2619     return JIM_OK;
   2620 }
   2621 
   2622 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2623 {
   2624     AioFile *af = Jim_CmdPrivData(interp);
   2625     Jim_Obj *objPtr = NULL;
   2626     int len;
   2627     int nb;
   2628     unsigned flags = AIO_ONEREAD;
   2629     char *nl = NULL;
   2630     int offset = 0;
   2631 
   2632     errno = 0;
   2633 
   2634 
   2635     nb = aio_start_nonblocking(af);
   2636     if (nb) {
   2637         flags |= AIO_NONBLOCK;
   2638     }
   2639 
   2640     while (!aio_eof(af)) {
   2641         if (af->readbuf) {
   2642             const char *pt = Jim_GetString(af->readbuf, &len);
   2643             nl = memchr(pt + offset, '\n', len - offset);
   2644             if (nl) {
   2645 
   2646                 objPtr = Jim_NewStringObj(interp, pt, nl - pt);
   2647 
   2648                 aio_consume(af->readbuf, nl - pt + 1);
   2649                 break;
   2650             }
   2651             offset = len;
   2652         }
   2653 
   2654 
   2655         if (aio_read_len(interp, af, flags, -1) != JIM_OK) {
   2656             break;
   2657         }
   2658     }
   2659 
   2660     aio_set_nonblocking(af, nb);
   2661 
   2662     if (!nl && aio_eof(af) && af->readbuf) {
   2663 
   2664         objPtr = af->readbuf;
   2665         af->readbuf = NULL;
   2666     }
   2667     else if (!objPtr) {
   2668         objPtr = Jim_NewStringObj(interp, NULL, 0);
   2669     }
   2670 
   2671     if (argc) {
   2672         if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
   2673             Jim_FreeNewObj(interp, objPtr);
   2674             return JIM_ERR;
   2675         }
   2676 
   2677         len = Jim_Length(objPtr);
   2678 
   2679         if (!nl && len == 0) {
   2680 
   2681             len = -1;
   2682         }
   2683         Jim_SetResultInt(interp, len);
   2684     }
   2685     else {
   2686         Jim_SetResult(interp, objPtr);
   2687     }
   2688     return JIM_OK;
   2689 }
   2690 
   2691 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2692 {
   2693     AioFile *af = Jim_CmdPrivData(interp);
   2694     int wlen;
   2695     const char *wdata;
   2696     Jim_Obj *strObj;
   2697     int wnow = 0;
   2698     int nl = 1;
   2699 
   2700     if (argc == 2) {
   2701         if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
   2702             return -1;
   2703         }
   2704         strObj = argv[1];
   2705         nl = 0;
   2706     }
   2707     else {
   2708         strObj = argv[0];
   2709     }
   2710 
   2711 #ifdef JIM_MAINTAINER
   2712     if (Jim_IsShared(af->writebuf)) {
   2713         Jim_DecrRefCount(interp, af->writebuf);
   2714         af->writebuf = Jim_DuplicateObj(interp, af->writebuf);
   2715         Jim_IncrRefCount(af->writebuf);
   2716     }
   2717 #endif
   2718     Jim_AppendObj(interp, af->writebuf, strObj);
   2719     if (nl) {
   2720         Jim_AppendString(interp, af->writebuf, "\n", 1);
   2721     }
   2722 
   2723 
   2724     wdata = Jim_GetString(af->writebuf, &wlen);
   2725     switch (af->wbuft) {
   2726         case WBUF_OPT_NONE:
   2727 
   2728             wnow = 1;
   2729             break;
   2730 
   2731         case WBUF_OPT_LINE:
   2732 
   2733             if (nl || memchr(wdata, '\n', wlen) != NULL) {
   2734                 wnow = 1;
   2735             }
   2736             break;
   2737 
   2738         case WBUF_OPT_FULL:
   2739             if (wlen >= af->wbuf_limit) {
   2740                 wnow = 1;
   2741             }
   2742             break;
   2743     }
   2744 
   2745     if (wnow) {
   2746         return aio_flush(interp, af);
   2747     }
   2748     return JIM_OK;
   2749 }
   2750 
   2751 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2752 {
   2753 #ifdef HAVE_ISATTY
   2754     AioFile *af = Jim_CmdPrivData(interp);
   2755     Jim_SetResultInt(interp, isatty(af->fd));
   2756 #else
   2757     Jim_SetResultInt(interp, 0);
   2758 #endif
   2759 
   2760     return JIM_OK;
   2761 }
   2762 
   2763 
   2764 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2765 {
   2766     AioFile *af = Jim_CmdPrivData(interp);
   2767     return aio_flush(interp, af);
   2768 }
   2769 
   2770 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2771 {
   2772     AioFile *af = Jim_CmdPrivData(interp);
   2773 
   2774     Jim_SetResultInt(interp, !!aio_eof(af));
   2775     return JIM_OK;
   2776 }
   2777 
   2778 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2779 {
   2780     AioFile *af = Jim_CmdPrivData(interp);
   2781     if (argc == 3) {
   2782         int option = -1;
   2783 #if defined(HAVE_SOCKETS)
   2784         static const char * const options[] = { "r", "w", "-nodelete", NULL };
   2785         enum { OPT_R, OPT_W, OPT_NODELETE };
   2786 
   2787         if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
   2788             return JIM_ERR;
   2789         }
   2790 #endif
   2791         switch (option) {
   2792 #if defined(HAVE_SHUTDOWN)
   2793             case OPT_R:
   2794             case OPT_W:
   2795                 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
   2796                     return JIM_OK;
   2797                 }
   2798                 JimAioSetError(interp, NULL);
   2799                 return JIM_ERR;
   2800 #endif
   2801 #if UNIX_SOCKETS
   2802             case OPT_NODELETE:
   2803                 if (af->addr_family == PF_UNIX) {
   2804                     af->flags |= AIO_NODELETE;
   2805                     break;
   2806                 }
   2807 
   2808 #endif
   2809             default:
   2810                 Jim_SetResultString(interp, "not supported", -1);
   2811                 return JIM_ERR;
   2812         }
   2813     }
   2814 
   2815 
   2816     af->flags &= ~AIO_KEEPOPEN;
   2817 
   2818     return Jim_DeleteCommand(interp, argv[0]);
   2819 }
   2820 
   2821 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2822 {
   2823     AioFile *af = Jim_CmdPrivData(interp);
   2824     int orig = SEEK_SET;
   2825     jim_wide offset;
   2826 
   2827     if (argc == 2) {
   2828         if (Jim_CompareStringImmediate(interp, argv[1], "start"))
   2829             orig = SEEK_SET;
   2830         else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
   2831             orig = SEEK_CUR;
   2832         else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
   2833             orig = SEEK_END;
   2834         else {
   2835             return -1;
   2836         }
   2837     }
   2838     if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
   2839         return JIM_ERR;
   2840     }
   2841     if (orig != SEEK_CUR || offset != 0) {
   2842 
   2843         aio_flush(interp, af);
   2844     }
   2845     if (Jim_Lseek(af->fd, offset, orig) == -1) {
   2846         JimAioSetError(interp, af->filename);
   2847         return JIM_ERR;
   2848     }
   2849     if (af->readbuf) {
   2850         Jim_FreeNewObj(interp, af->readbuf);
   2851         af->readbuf = NULL;
   2852     }
   2853     af->flags &= ~AIO_EOF;
   2854     return JIM_OK;
   2855 }
   2856 
   2857 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2858 {
   2859     AioFile *af = Jim_CmdPrivData(interp);
   2860 
   2861     Jim_SetResultInt(interp, Jim_Lseek(af->fd, 0, SEEK_CUR));
   2862     return JIM_OK;
   2863 }
   2864 
   2865 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2866 {
   2867     AioFile *af = Jim_CmdPrivData(interp);
   2868 
   2869     Jim_SetResult(interp, af->filename);
   2870     return JIM_OK;
   2871 }
   2872 
   2873 #ifdef O_NDELAY
   2874 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2875 {
   2876     AioFile *af = Jim_CmdPrivData(interp);
   2877 
   2878     if (argc) {
   2879         long nb;
   2880 
   2881         if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
   2882             return JIM_ERR;
   2883         }
   2884         aio_set_nonblocking(af, nb);
   2885     }
   2886     Jim_SetResultInt(interp, (af->flags & AIO_NONBLOCK) ? 1 : 0);
   2887     return JIM_OK;
   2888 }
   2889 #endif
   2890 
   2891 
   2892 #ifdef HAVE_FSYNC
   2893 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2894 {
   2895     AioFile *af = Jim_CmdPrivData(interp);
   2896 
   2897     if (aio_flush(interp, af) != JIM_OK) {
   2898         return JIM_ERR;
   2899     }
   2900     fsync(af->fd);
   2901     return JIM_OK;
   2902 }
   2903 #endif
   2904 
   2905 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2906 {
   2907     AioFile *af = Jim_CmdPrivData(interp);
   2908     Jim_Obj *resultObj;
   2909 
   2910     static const char * const options[] = {
   2911         "none",
   2912         "line",
   2913         "full",
   2914         NULL
   2915     };
   2916 
   2917     if (argc) {
   2918         if (Jim_GetEnum(interp, argv[0], options, &af->wbuft, NULL, JIM_ERRMSG) != JIM_OK) {
   2919             return JIM_ERR;
   2920         }
   2921 
   2922         if (af->wbuft == WBUF_OPT_FULL && argc == 2) {
   2923             long l;
   2924             if (Jim_GetLong(interp, argv[1], &l) != JIM_OK || l <= 0) {
   2925                 return JIM_ERR;
   2926             }
   2927             af->wbuf_limit = l;
   2928         }
   2929 
   2930         if (af->wbuft == WBUF_OPT_NONE) {
   2931             if (aio_flush(interp, af) != JIM_OK) {
   2932                 return JIM_ERR;
   2933             }
   2934         }
   2935 
   2936     }
   2937 
   2938     resultObj = Jim_NewListObj(interp, NULL, 0);
   2939     Jim_ListAppendElement(interp, resultObj, Jim_NewStringObj(interp, options[af->wbuft], -1));
   2940     if (af->wbuft == WBUF_OPT_FULL) {
   2941         Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, af->wbuf_limit));
   2942     }
   2943     Jim_SetResult(interp, resultObj);
   2944 
   2945     return JIM_OK;
   2946 }
   2947 
   2948 static int aio_cmd_translation(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2949 {
   2950     enum {OPT_BINARY, OPT_TEXT};
   2951     static const char * const options[] = {
   2952         "binary",
   2953         "text",
   2954         NULL
   2955     };
   2956     int opt;
   2957 
   2958     if (Jim_GetEnum(interp, argv[0], options, &opt, NULL, JIM_ERRMSG) != JIM_OK) {
   2959             return JIM_ERR;
   2960     }
   2961 #if defined(Jim_SetMode)
   2962     else {
   2963         AioFile *af = Jim_CmdPrivData(interp);
   2964         Jim_SetMode(af->fd, opt == OPT_BINARY ? O_BINARY : O_TEXT);
   2965     }
   2966 #endif
   2967     return JIM_OK;
   2968 }
   2969 
   2970 static int aio_cmd_readsize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2971 {
   2972     AioFile *af = Jim_CmdPrivData(interp);
   2973 
   2974     if (argc) {
   2975         long l;
   2976         if (Jim_GetLong(interp, argv[0], &l) != JIM_OK || l <= 0) {
   2977             return JIM_ERR;
   2978         }
   2979         af->rbuf_len = l;
   2980         if (af->rbuf) {
   2981             af->rbuf = Jim_Realloc(af->rbuf, af->rbuf_len);
   2982         }
   2983     }
   2984     Jim_SetResultInt(interp, af->rbuf_len);
   2985 
   2986     return JIM_OK;
   2987 }
   2988 
   2989 #ifdef jim_ext_eventloop
   2990 static int aio_cmd_timeout(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   2991 {
   2992 #ifdef HAVE_SELECT
   2993     AioFile *af = Jim_CmdPrivData(interp);
   2994     if (argc == 1) {
   2995         if (Jim_GetLong(interp, argv[0], &af->timeout) != JIM_OK) {
   2996             return JIM_ERR;
   2997         }
   2998     }
   2999     Jim_SetResultInt(interp, af->timeout);
   3000     return JIM_OK;
   3001 #else
   3002     Jim_SetResultString(interp, "timeout not supported", -1);
   3003     return JIM_ERR;
   3004 #endif
   3005 }
   3006 
   3007 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask,
   3008     int argc, Jim_Obj * const *argv)
   3009 {
   3010     if (argc == 0) {
   3011 
   3012         Jim_Obj *objPtr = Jim_FindFileHandler(interp, af->fd, mask);
   3013         if (objPtr) {
   3014             Jim_SetResult(interp, objPtr);
   3015         }
   3016         return JIM_OK;
   3017     }
   3018 
   3019 
   3020     Jim_DeleteFileHandler(interp, af->fd, mask);
   3021 
   3022 
   3023     if (Jim_Length(argv[0])) {
   3024         Jim_CreateScriptFileHandler(interp, af->fd, mask, argv[0]);
   3025     }
   3026 
   3027     return JIM_OK;
   3028 }
   3029 
   3030 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   3031 {
   3032     AioFile *af = Jim_CmdPrivData(interp);
   3033 
   3034     return aio_eventinfo(interp, af, JIM_EVENT_READABLE, argc, argv);
   3035 }
   3036 
   3037 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   3038 {
   3039     AioFile *af = Jim_CmdPrivData(interp);
   3040 
   3041     return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, argc, argv);
   3042 }
   3043 
   3044 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   3045 {
   3046     AioFile *af = Jim_CmdPrivData(interp);
   3047 
   3048     return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, argc, argv);
   3049 }
   3050 #endif
   3051 
   3052 #if defined(jim_ext_file) && defined(Jim_FileStat)
   3053 static int aio_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   3054 {
   3055     jim_stat_t sb;
   3056     AioFile *af = Jim_CmdPrivData(interp);
   3057 
   3058     if (Jim_FileStat(af->fd, &sb) == -1) {
   3059         JimAioSetError(interp, NULL);
   3060         return JIM_ERR;
   3061     }
   3062     return Jim_FileStoreStatData(interp, argc == 0 ? NULL : argv[0], &sb);
   3063 }
   3064 #endif
   3065 
   3066 
   3067 
   3068 
   3069 static const jim_subcmd_type aio_command_table[] = {
   3070     {   "read",
   3071         "?-nonewline|len?",
   3072         aio_cmd_read,
   3073         0,
   3074         2,
   3075 
   3076     },
   3077     {   "copyto",
   3078         "handle ?size?",
   3079         aio_cmd_copy,
   3080         1,
   3081         2,
   3082 
   3083     },
   3084     {   "getfd",
   3085         NULL,
   3086         aio_cmd_getfd,
   3087         0,
   3088         0,
   3089 
   3090     },
   3091     {   "gets",
   3092         "?var?",
   3093         aio_cmd_gets,
   3094         0,
   3095         1,
   3096 
   3097     },
   3098     {   "puts",
   3099         "?-nonewline? str",
   3100         aio_cmd_puts,
   3101         1,
   3102         2,
   3103 
   3104     },
   3105     {   "isatty",
   3106         NULL,
   3107         aio_cmd_isatty,
   3108         0,
   3109         0,
   3110 
   3111     },
   3112     {   "flush",
   3113         NULL,
   3114         aio_cmd_flush,
   3115         0,
   3116         0,
   3117 
   3118     },
   3119     {   "eof",
   3120         NULL,
   3121         aio_cmd_eof,
   3122         0,
   3123         0,
   3124 
   3125     },
   3126     {   "close",
   3127         "?r(ead)|w(rite)?",
   3128         aio_cmd_close,
   3129         0,
   3130         1,
   3131         JIM_MODFLAG_FULLARGV,
   3132 
   3133     },
   3134     {   "seek",
   3135         "offset ?start|current|end",
   3136         aio_cmd_seek,
   3137         1,
   3138         2,
   3139 
   3140     },
   3141     {   "tell",
   3142         NULL,
   3143         aio_cmd_tell,
   3144         0,
   3145         0,
   3146 
   3147     },
   3148     {   "filename",
   3149         NULL,
   3150         aio_cmd_filename,
   3151         0,
   3152         0,
   3153 
   3154     },
   3155 #ifdef O_NDELAY
   3156     {   "ndelay",
   3157         "?0|1?",
   3158         aio_cmd_ndelay,
   3159         0,
   3160         1,
   3161 
   3162     },
   3163 #endif
   3164 #ifdef HAVE_FSYNC
   3165     {   "sync",
   3166         NULL,
   3167         aio_cmd_sync,
   3168         0,
   3169         0,
   3170 
   3171     },
   3172 #endif
   3173     {   "buffering",
   3174         "?none|line|full? ?size?",
   3175         aio_cmd_buffering,
   3176         0,
   3177         2,
   3178 
   3179     },
   3180     {   "translation",
   3181         "binary|text",
   3182         aio_cmd_translation,
   3183         1,
   3184         1,
   3185 
   3186     },
   3187     {   "readsize",
   3188         "?size?",
   3189         aio_cmd_readsize,
   3190         0,
   3191         1,
   3192 
   3193     },
   3194 #if defined(jim_ext_file) && defined(Jim_FileStat)
   3195     {   "stat",
   3196         "?var?",
   3197         aio_cmd_stat,
   3198         0,
   3199         1,
   3200 
   3201     },
   3202 #endif
   3203 #ifdef jim_ext_eventloop
   3204     {   "readable",
   3205         "?readable-script?",
   3206         aio_cmd_readable,
   3207         0,
   3208         1,
   3209 
   3210     },
   3211     {   "writable",
   3212         "?writable-script?",
   3213         aio_cmd_writable,
   3214         0,
   3215         1,
   3216 
   3217     },
   3218     {   "onexception",
   3219         "?exception-script?",
   3220         aio_cmd_onexception,
   3221         0,
   3222         1,
   3223 
   3224     },
   3225     {   "timeout",
   3226         "?ms?",
   3227         aio_cmd_timeout,
   3228         0,
   3229         1,
   3230 
   3231     },
   3232 #endif
   3233     { NULL }
   3234 };
   3235 
   3236 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   3237 {
   3238     return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
   3239 }
   3240 
   3241 static int parse_posix_open_mode(Jim_Interp *interp, Jim_Obj *modeObj)
   3242 {
   3243     int i;
   3244     int flags = 0;
   3245     #ifndef O_NOCTTY
   3246 
   3247         #define O_NOCTTY 0
   3248     #endif
   3249     static const char * const modetypes[] = {
   3250         "RDONLY", "WRONLY", "RDWR", "APPEND", "BINARY", "CREAT", "EXCL", "NOCTTY", "TRUNC", NULL
   3251     };
   3252     static const int modeflags[] = {
   3253         O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, 0, O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC,
   3254     };
   3255 
   3256     for (i = 0; i < Jim_ListLength(interp, modeObj); i++) {
   3257         int opt;
   3258         Jim_Obj *objPtr = Jim_ListGetIndex(interp, modeObj, i);
   3259         if (Jim_GetEnum(interp, objPtr, modetypes, &opt, "access mode", JIM_ERRMSG) != JIM_OK) {
   3260             return -1;
   3261         }
   3262         flags |= modeflags[opt];
   3263     }
   3264     return flags;
   3265 }
   3266 
   3267 static int parse_open_mode(Jim_Interp *interp, Jim_Obj *filenameObj, Jim_Obj *modeObj)
   3268 {
   3269 
   3270     int flags;
   3271     const char *mode = Jim_String(modeObj);
   3272     if (*mode == 'R' || *mode == 'W') {
   3273         return parse_posix_open_mode(interp, modeObj);
   3274     }
   3275     if (*mode == 'r') {
   3276         flags = O_RDONLY;
   3277     }
   3278     else if (*mode == 'w') {
   3279         flags = O_WRONLY | O_CREAT | O_TRUNC;
   3280     }
   3281     else if (*mode == 'a') {
   3282         flags = O_WRONLY | O_CREAT | O_APPEND;
   3283     }
   3284     else {
   3285         Jim_SetResultFormatted(interp, "%s: invalid open mode '%s'", Jim_String(filenameObj), mode);
   3286         return -1;
   3287     }
   3288     mode++;
   3289 
   3290     if (*mode == 'b') {
   3291 #ifdef O_BINARY
   3292         flags |= O_BINARY;
   3293 #endif
   3294         mode++;
   3295     }
   3296 
   3297     if (*mode == 't') {
   3298 #ifdef O_TEXT
   3299         flags |= O_TEXT;
   3300 #endif
   3301         mode++;
   3302     }
   3303 
   3304     if (*mode == '+') {
   3305         mode++;
   3306 
   3307         flags &= ~(O_RDONLY | O_WRONLY);
   3308         flags |= O_RDWR;
   3309     }
   3310 
   3311     if (*mode == 'x') {
   3312         mode++;
   3313 #ifdef O_EXCL
   3314         flags |= O_EXCL;
   3315 #endif
   3316     }
   3317 
   3318     if (*mode == 'F') {
   3319         mode++;
   3320 #ifdef O_LARGEFILE
   3321         flags |= O_LARGEFILE;
   3322 #endif
   3323     }
   3324 
   3325     if (*mode == 'e') {
   3326 
   3327         mode++;
   3328     }
   3329     return flags;
   3330 }
   3331 
   3332 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
   3333         Jim_Obj *const *argv)
   3334 {
   3335     int openflags;
   3336     const char *filename;
   3337     int fd = -1;
   3338     int n = 0;
   3339     int flags = 0;
   3340 
   3341     if (argc > 2 && Jim_CompareStringImmediate(interp, argv[2], "-noclose")) {
   3342         flags = AIO_KEEPOPEN;
   3343         n++;
   3344     }
   3345     if (argc < 2 || argc > 3 + n) {
   3346         Jim_WrongNumArgs(interp, 1, argv, "filename ?-noclose? ?mode?");
   3347         return JIM_ERR;
   3348     }
   3349 
   3350     filename = Jim_String(argv[1]);
   3351 
   3352 #ifdef jim_ext_tclcompat
   3353     {
   3354 
   3355 
   3356         if (*filename == '|') {
   3357             Jim_Obj *evalObj[3];
   3358             int i = 0;
   3359 
   3360             evalObj[i++] = Jim_NewStringObj(interp, "::popen", -1);
   3361             evalObj[i++] = Jim_NewStringObj(interp, filename + 1, -1);
   3362             if (argc == 3 + n) {
   3363                 evalObj[i++] = argv[2 + n];
   3364             }
   3365 
   3366             return Jim_EvalObjVector(interp, i, evalObj);
   3367         }
   3368     }
   3369 #endif
   3370     if (argc == 3 + n) {
   3371         openflags = parse_open_mode(interp, argv[1], argv[2 + n]);
   3372         if (openflags == -1) {
   3373             return JIM_ERR;
   3374         }
   3375     }
   3376     else {
   3377         openflags = O_RDONLY;
   3378     }
   3379     fd = open(filename, openflags, 0666);
   3380     if (fd < 0) {
   3381         JimAioSetError(interp, argv[1]);
   3382         return JIM_ERR;
   3383     }
   3384 
   3385     return JimMakeChannel(interp, fd, argv[1], "aio.handle%ld", 0, flags) ? JIM_OK : JIM_ERR;
   3386 }
   3387 
   3388 
   3389 static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
   3390     const char *hdlfmt, int family, int flags)
   3391 {
   3392     AioFile *af;
   3393     char buf[AIO_CMD_LEN];
   3394     Jim_Obj *cmdname;
   3395 
   3396     snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
   3397     cmdname = Jim_NewStringObj(interp, buf, -1);
   3398     if (!filename) {
   3399         filename = cmdname;
   3400     }
   3401     Jim_IncrRefCount(filename);
   3402 
   3403 
   3404     af = Jim_Alloc(sizeof(*af));
   3405     memset(af, 0, sizeof(*af));
   3406     af->filename = filename;
   3407     af->fd = fd;
   3408     af->addr_family = family;
   3409     af->fops = &stdio_fops;
   3410     af->ssl = NULL;
   3411     if (flags & AIO_WBUF_NONE) {
   3412         af->wbuft = WBUF_OPT_NONE;
   3413     }
   3414     else {
   3415 #ifdef HAVE_ISATTY
   3416         af->wbuft = isatty(af->fd) ? WBUF_OPT_LINE : WBUF_OPT_FULL;
   3417 #else
   3418         af->wbuft = WBUF_OPT_FULL;
   3419 #endif
   3420     }
   3421 
   3422 #ifdef FD_CLOEXEC
   3423     if ((flags & AIO_KEEPOPEN) == 0) {
   3424         (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
   3425     }
   3426 #endif
   3427     aio_set_nonblocking(af, !!(flags & AIO_NONBLOCK));
   3428 
   3429     af->flags |= flags;
   3430 
   3431     af->writebuf = Jim_NewStringObj(interp, NULL, 0);
   3432     Jim_IncrRefCount(af->writebuf);
   3433     af->wbuf_limit = AIO_DEFAULT_WBUF_LIMIT;
   3434     af->rbuf_len = AIO_DEFAULT_RBUF_LEN;
   3435 
   3436 
   3437     Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
   3438 
   3439     Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, cmdname));
   3440 
   3441     return af;
   3442 }
   3443 
   3444 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS) || defined(HAVE_OPENPTY)
   3445 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
   3446     const char *hdlfmt, int family, int flags)
   3447 {
   3448     if (JimMakeChannel(interp, p[0], filename, hdlfmt, family, flags)) {
   3449         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
   3450         Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
   3451         if (JimMakeChannel(interp, p[1], filename, hdlfmt, family, flags)) {
   3452             Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
   3453             Jim_SetResult(interp, objPtr);
   3454             return JIM_OK;
   3455         }
   3456     }
   3457 
   3458 
   3459     close(p[0]);
   3460     close(p[1]);
   3461     JimAioSetError(interp, NULL);
   3462     return JIM_ERR;
   3463 }
   3464 #endif
   3465 
   3466 #ifdef HAVE_PIPE
   3467 static int JimCreatePipe(Jim_Interp *interp, Jim_Obj *filenameObj, int flags)
   3468 {
   3469     int p[2];
   3470 
   3471     if (pipe(p) != 0) {
   3472         JimAioSetError(interp, NULL);
   3473         return JIM_ERR;
   3474     }
   3475 
   3476     return JimMakeChannelPair(interp, p, filenameObj, "aio.pipe%ld", 0, flags);
   3477 }
   3478 
   3479 
   3480 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   3481 {
   3482     if (argc != 1) {
   3483         Jim_WrongNumArgs(interp, 1, argv, "");
   3484         return JIM_ERR;
   3485     }
   3486     return JimCreatePipe(interp, argv[0], 0);
   3487 }
   3488 #endif
   3489 
   3490 #ifdef HAVE_OPENPTY
   3491 static int JimAioOpenPtyCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   3492 {
   3493     int p[2];
   3494     char path[MAXPATHLEN];
   3495 
   3496     if (argc != 1) {
   3497         Jim_WrongNumArgs(interp, 1, argv, "");
   3498         return JIM_ERR;
   3499     }
   3500 
   3501     if (openpty(&p[0], &p[1], path, NULL, NULL) != 0) {
   3502         JimAioSetError(interp, NULL);
   3503         return JIM_ERR;
   3504     }
   3505 
   3506 
   3507     return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0);
   3508     return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0);
   3509 }
   3510 #endif
   3511 
   3512 
   3513 
   3514 int Jim_aioInit(Jim_Interp *interp)
   3515 {
   3516     if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
   3517         return JIM_ERR;
   3518 
   3519 #if defined(JIM_SSL)
   3520     Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
   3521 #endif
   3522 
   3523     Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
   3524 #ifdef HAVE_SOCKETS
   3525     Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
   3526 #endif
   3527 #ifdef HAVE_PIPE
   3528     Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
   3529 #endif
   3530 
   3531 
   3532     JimMakeChannel(interp, fileno(stdin), NULL, "stdin", 0, AIO_KEEPOPEN);
   3533     JimMakeChannel(interp, fileno(stdout), NULL, "stdout", 0, AIO_KEEPOPEN);
   3534     JimMakeChannel(interp, fileno(stderr), NULL, "stderr", 0, AIO_KEEPOPEN | AIO_WBUF_NONE);
   3535 
   3536     return JIM_OK;
   3537 }
   3538 
   3539 #include <errno.h>
   3540 #include <stdio.h>
   3541 #include <string.h>
   3542 
   3543 
   3544 #ifdef HAVE_DIRENT_H
   3545 #include <dirent.h>
   3546 #endif
   3547 
   3548 int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   3549 {
   3550     const char *dirPath;
   3551     DIR *dirPtr;
   3552     struct dirent *entryPtr;
   3553     int nocomplain = 0;
   3554 
   3555     if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) {
   3556         nocomplain = 1;
   3557     }
   3558     if (argc != 2 && !nocomplain) {
   3559         Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
   3560         return JIM_ERR;
   3561     }
   3562 
   3563     dirPath = Jim_String(argv[1 + nocomplain]);
   3564 
   3565     dirPtr = opendir(dirPath);
   3566     if (dirPtr == NULL) {
   3567         if (nocomplain) {
   3568             return JIM_OK;
   3569         }
   3570         Jim_SetResultString(interp, strerror(errno), -1);
   3571         return JIM_ERR;
   3572     }
   3573     else {
   3574         Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
   3575 
   3576         while ((entryPtr = readdir(dirPtr)) != NULL) {
   3577             if (entryPtr->d_name[0] == '.') {
   3578                 if (entryPtr->d_name[1] == '\0') {
   3579                     continue;
   3580                 }
   3581                 if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0'))
   3582                     continue;
   3583             }
   3584             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1));
   3585         }
   3586         closedir(dirPtr);
   3587 
   3588         Jim_SetResult(interp, listObj);
   3589 
   3590         return JIM_OK;
   3591     }
   3592 }
   3593 
   3594 int Jim_readdirInit(Jim_Interp *interp)
   3595 {
   3596     Jim_PackageProvideCheck(interp, "readdir");
   3597     Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
   3598     return JIM_OK;
   3599 }
   3600 
   3601 #include <stdlib.h>
   3602 #include <string.h>
   3603 
   3604 #if defined(JIM_REGEXP)
   3605 #else
   3606     #include <regex.h>
   3607     #define jim_regcomp regcomp
   3608     #define jim_regexec regexec
   3609     #define jim_regerror regerror
   3610     #define jim_regfree regfree
   3611 #endif
   3612 
   3613 static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
   3614 {
   3615     jim_regfree(objPtr->internalRep.ptrIntValue.ptr);
   3616     Jim_Free(objPtr->internalRep.ptrIntValue.ptr);
   3617 }
   3618 
   3619 static const Jim_ObjType regexpObjType = {
   3620     "regexp",
   3621     FreeRegexpInternalRep,
   3622     NULL,
   3623     NULL,
   3624     JIM_TYPE_NONE
   3625 };
   3626 
   3627 static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
   3628 {
   3629     regex_t *compre;
   3630     const char *pattern;
   3631     int ret;
   3632 
   3633 
   3634     if (objPtr->typePtr == &regexpObjType &&
   3635         objPtr->internalRep.ptrIntValue.ptr && objPtr->internalRep.ptrIntValue.int1 == flags) {
   3636 
   3637         return objPtr->internalRep.ptrIntValue.ptr;
   3638     }
   3639 
   3640 
   3641 
   3642 
   3643     pattern = Jim_String(objPtr);
   3644     compre = Jim_Alloc(sizeof(regex_t));
   3645 
   3646     if ((ret = jim_regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
   3647         char buf[100];
   3648 
   3649         jim_regerror(ret, compre, buf, sizeof(buf));
   3650         Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
   3651         jim_regfree(compre);
   3652         Jim_Free(compre);
   3653         return NULL;
   3654     }
   3655 
   3656     Jim_FreeIntRep(interp, objPtr);
   3657 
   3658     objPtr->typePtr = &regexpObjType;
   3659     objPtr->internalRep.ptrIntValue.int1 = flags;
   3660     objPtr->internalRep.ptrIntValue.ptr = compre;
   3661 
   3662     return compre;
   3663 }
   3664 
   3665 int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   3666 {
   3667     int opt_indices = 0;
   3668     int opt_all = 0;
   3669     int opt_inline = 0;
   3670     regex_t *regex;
   3671     int match, i, j;
   3672     int offset = 0;
   3673     regmatch_t *pmatch = NULL;
   3674     int source_len;
   3675     int result = JIM_OK;
   3676     const char *pattern;
   3677     const char *source_str;
   3678     int num_matches = 0;
   3679     int num_vars;
   3680     Jim_Obj *resultListObj = NULL;
   3681     int regcomp_flags = 0;
   3682     int eflags = 0;
   3683     int option;
   3684     enum {
   3685         OPT_INDICES,  OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
   3686     };
   3687     static const char * const options[] = {
   3688         "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
   3689     };
   3690 
   3691     if (argc < 3) {
   3692       wrongNumArgs:
   3693         Jim_WrongNumArgs(interp, 1, argv,
   3694             "?-switch ...? exp string ?matchVar? ?subMatchVar ...?");
   3695         return JIM_ERR;
   3696     }
   3697 
   3698     for (i = 1; i < argc; i++) {
   3699         const char *opt = Jim_String(argv[i]);
   3700 
   3701         if (*opt != '-') {
   3702             break;
   3703         }
   3704         if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
   3705             return JIM_ERR;
   3706         }
   3707         if (option == OPT_END) {
   3708             i++;
   3709             break;
   3710         }
   3711         switch (option) {
   3712             case OPT_INDICES:
   3713                 opt_indices = 1;
   3714                 break;
   3715 
   3716             case OPT_NOCASE:
   3717                 regcomp_flags |= REG_ICASE;
   3718                 break;
   3719 
   3720             case OPT_LINE:
   3721                 regcomp_flags |= REG_NEWLINE;
   3722                 break;
   3723 
   3724             case OPT_ALL:
   3725                 opt_all = 1;
   3726                 break;
   3727 
   3728             case OPT_INLINE:
   3729                 opt_inline = 1;
   3730                 break;
   3731 
   3732             case OPT_START:
   3733                 if (++i == argc) {
   3734                     goto wrongNumArgs;
   3735                 }
   3736                 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
   3737                     return JIM_ERR;
   3738                 }
   3739                 break;
   3740         }
   3741     }
   3742     if (argc - i < 2) {
   3743         goto wrongNumArgs;
   3744     }
   3745 
   3746     regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
   3747     if (!regex) {
   3748         return JIM_ERR;
   3749     }
   3750 
   3751     pattern = Jim_String(argv[i]);
   3752     source_str = Jim_GetString(argv[i + 1], &source_len);
   3753 
   3754     num_vars = argc - i - 2;
   3755 
   3756     if (opt_inline) {
   3757         if (num_vars) {
   3758             Jim_SetResultString(interp, "regexp match variables not allowed when using -inline",
   3759                 -1);
   3760             result = JIM_ERR;
   3761             goto done;
   3762         }
   3763         num_vars = regex->re_nsub + 1;
   3764     }
   3765 
   3766     pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch));
   3767 
   3768     if (offset) {
   3769         if (offset < 0) {
   3770             offset += source_len + 1;
   3771         }
   3772         if (offset > source_len) {
   3773             source_str += source_len;
   3774         }
   3775         else if (offset > 0) {
   3776             source_str += utf8_index(source_str, offset);
   3777         }
   3778         eflags |= REG_NOTBOL;
   3779     }
   3780 
   3781     if (opt_inline) {
   3782         resultListObj = Jim_NewListObj(interp, NULL, 0);
   3783     }
   3784 
   3785   next_match:
   3786     match = jim_regexec(regex, source_str, num_vars + 1, pmatch, eflags);
   3787     if (match >= REG_BADPAT) {
   3788         char buf[100];
   3789 
   3790         jim_regerror(match, regex, buf, sizeof(buf));
   3791         Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
   3792         result = JIM_ERR;
   3793         goto done;
   3794     }
   3795 
   3796     if (match == REG_NOMATCH) {
   3797         goto done;
   3798     }
   3799 
   3800     num_matches++;
   3801 
   3802     if (opt_all && !opt_inline) {
   3803 
   3804         goto try_next_match;
   3805     }
   3806 
   3807 
   3808     j = 0;
   3809     for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
   3810         Jim_Obj *resultObj;
   3811 
   3812         if (opt_indices) {
   3813             resultObj = Jim_NewListObj(interp, NULL, 0);
   3814         }
   3815         else {
   3816             resultObj = Jim_NewStringObj(interp, "", 0);
   3817         }
   3818 
   3819         if (pmatch[j].rm_so == -1) {
   3820             if (opt_indices) {
   3821                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
   3822                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
   3823             }
   3824         }
   3825         else {
   3826             if (opt_indices) {
   3827 
   3828                 int so = utf8_strlen(source_str, pmatch[j].rm_so);
   3829                 int eo = utf8_strlen(source_str, pmatch[j].rm_eo);
   3830                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + so));
   3831                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + eo - 1));
   3832             }
   3833             else {
   3834                 Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so);
   3835             }
   3836         }
   3837 
   3838         if (opt_inline) {
   3839             Jim_ListAppendElement(interp, resultListObj, resultObj);
   3840         }
   3841         else {
   3842 
   3843             result = Jim_SetVariable(interp, argv[i], resultObj);
   3844 
   3845             if (result != JIM_OK) {
   3846                 Jim_FreeObj(interp, resultObj);
   3847                 break;
   3848             }
   3849         }
   3850     }
   3851 
   3852   try_next_match:
   3853     if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
   3854         if (pmatch[0].rm_eo) {
   3855             offset += utf8_strlen(source_str, pmatch[0].rm_eo);
   3856             source_str += pmatch[0].rm_eo;
   3857         }
   3858         else {
   3859             source_str++;
   3860             offset++;
   3861         }
   3862         if (*source_str) {
   3863             eflags = REG_NOTBOL;
   3864             goto next_match;
   3865         }
   3866     }
   3867 
   3868   done:
   3869     if (result == JIM_OK) {
   3870         if (opt_inline) {
   3871             Jim_SetResult(interp, resultListObj);
   3872         }
   3873         else {
   3874             Jim_SetResultInt(interp, num_matches);
   3875         }
   3876     }
   3877 
   3878     Jim_Free(pmatch);
   3879     return result;
   3880 }
   3881 
   3882 #define MAX_SUB_MATCHES 50
   3883 
   3884 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   3885 {
   3886     int regcomp_flags = 0;
   3887     int regexec_flags = 0;
   3888     int opt_all = 0;
   3889     int opt_command = 0;
   3890     int offset = 0;
   3891     regex_t *regex;
   3892     const char *p;
   3893     int result = JIM_OK;
   3894     regmatch_t pmatch[MAX_SUB_MATCHES + 1];
   3895     int num_matches = 0;
   3896 
   3897     int i, j, n;
   3898     Jim_Obj *varname;
   3899     Jim_Obj *resultObj;
   3900     Jim_Obj *cmd_prefix = NULL;
   3901     Jim_Obj *regcomp_obj = NULL;
   3902     const char *source_str;
   3903     int source_len;
   3904     const char *replace_str = NULL;
   3905     int replace_len;
   3906     const char *pattern;
   3907     int option;
   3908     enum {
   3909         OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_COMMAND, OPT_END
   3910     };
   3911     static const char * const options[] = {
   3912         "-nocase", "-line", "-all", "-start", "-command", "--", NULL
   3913     };
   3914 
   3915     if (argc < 4) {
   3916       wrongNumArgs:
   3917         Jim_WrongNumArgs(interp, 1, argv,
   3918             "?-switch ...? exp string subSpec ?varName?");
   3919         return JIM_ERR;
   3920     }
   3921 
   3922     for (i = 1; i < argc; i++) {
   3923         const char *opt = Jim_String(argv[i]);
   3924 
   3925         if (*opt != '-') {
   3926             break;
   3927         }
   3928         if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
   3929             return JIM_ERR;
   3930         }
   3931         if (option == OPT_END) {
   3932             i++;
   3933             break;
   3934         }
   3935         switch (option) {
   3936             case OPT_NOCASE:
   3937                 regcomp_flags |= REG_ICASE;
   3938                 break;
   3939 
   3940             case OPT_LINE:
   3941                 regcomp_flags |= REG_NEWLINE;
   3942                 break;
   3943 
   3944             case OPT_ALL:
   3945                 opt_all = 1;
   3946                 break;
   3947 
   3948             case OPT_START:
   3949                 if (++i == argc) {
   3950                     goto wrongNumArgs;
   3951                 }
   3952                 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
   3953                     return JIM_ERR;
   3954                 }
   3955                 break;
   3956 
   3957             case OPT_COMMAND:
   3958                 opt_command = 1;
   3959                 break;
   3960         }
   3961     }
   3962     if (argc - i != 3 && argc - i != 4) {
   3963         goto wrongNumArgs;
   3964     }
   3965 
   3966 
   3967     regcomp_obj = Jim_DuplicateObj(interp, argv[i]);
   3968 	Jim_IncrRefCount(regcomp_obj);
   3969     regex = SetRegexpFromAny(interp, regcomp_obj, regcomp_flags);
   3970     if (!regex) {
   3971 		Jim_DecrRefCount(interp, regcomp_obj);
   3972         return JIM_ERR;
   3973     }
   3974     pattern = Jim_String(argv[i]);
   3975 
   3976     source_str = Jim_GetString(argv[i + 1], &source_len);
   3977     if (opt_command) {
   3978         cmd_prefix = argv[i + 2];
   3979         if (Jim_ListLength(interp, cmd_prefix) == 0) {
   3980             Jim_SetResultString(interp, "command prefix must be a list of at least one element", -1);
   3981 			Jim_DecrRefCount(interp, regcomp_obj);
   3982             return JIM_ERR;
   3983         }
   3984         Jim_IncrRefCount(cmd_prefix);
   3985     }
   3986     else {
   3987         replace_str = Jim_GetString(argv[i + 2], &replace_len);
   3988     }
   3989     varname = argv[i + 3];
   3990 
   3991 
   3992     resultObj = Jim_NewStringObj(interp, "", 0);
   3993 
   3994     if (offset) {
   3995         if (offset < 0) {
   3996             offset += source_len + 1;
   3997         }
   3998         if (offset > source_len) {
   3999             offset = source_len;
   4000         }
   4001         else if (offset < 0) {
   4002             offset = 0;
   4003         }
   4004     }
   4005 
   4006     offset = utf8_index(source_str, offset);
   4007 
   4008 
   4009     Jim_AppendString(interp, resultObj, source_str, offset);
   4010 
   4011 
   4012     n = source_len - offset;
   4013     p = source_str + offset;
   4014     do {
   4015         int match = jim_regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
   4016 
   4017         if (match >= REG_BADPAT) {
   4018             char buf[100];
   4019 
   4020             jim_regerror(match, regex, buf, sizeof(buf));
   4021             Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
   4022             return JIM_ERR;
   4023         }
   4024         if (match == REG_NOMATCH) {
   4025             break;
   4026         }
   4027 
   4028         num_matches++;
   4029 
   4030         Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
   4031 
   4032         if (opt_command) {
   4033 
   4034             Jim_Obj *cmdListObj = Jim_DuplicateObj(interp, cmd_prefix);
   4035             for (j = 0; j < MAX_SUB_MATCHES; j++) {
   4036                 if (pmatch[j].rm_so == -1) {
   4037                     break;
   4038                 }
   4039                 else {
   4040                     Jim_Obj *srcObj = Jim_NewStringObj(interp, p + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so);
   4041                     Jim_ListAppendElement(interp, cmdListObj, srcObj);
   4042                 }
   4043             }
   4044             Jim_IncrRefCount(cmdListObj);
   4045 
   4046             result = Jim_EvalObj(interp, cmdListObj);
   4047             Jim_DecrRefCount(interp, cmdListObj);
   4048             if (result != JIM_OK) {
   4049                 goto cmd_error;
   4050             }
   4051             Jim_AppendString(interp, resultObj, Jim_String(Jim_GetResult(interp)), -1);
   4052         }
   4053         else {
   4054 
   4055             for (j = 0; j < replace_len; j++) {
   4056                 int idx;
   4057                 int c = replace_str[j];
   4058 
   4059                 if (c == '&') {
   4060                     idx = 0;
   4061                 }
   4062                 else if (c == '\\' && j < replace_len) {
   4063                     c = replace_str[++j];
   4064                     if ((c >= '0') && (c <= '9')) {
   4065                         idx = c - '0';
   4066                     }
   4067                     else if ((c == '\\') || (c == '&')) {
   4068                         Jim_AppendString(interp, resultObj, replace_str + j, 1);
   4069                         continue;
   4070                     }
   4071                     else {
   4072                         Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2);
   4073                         continue;
   4074                     }
   4075                 }
   4076                 else {
   4077                     Jim_AppendString(interp, resultObj, replace_str + j, 1);
   4078                     continue;
   4079                 }
   4080                 if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
   4081                     Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
   4082                         pmatch[idx].rm_eo - pmatch[idx].rm_so);
   4083                 }
   4084             }
   4085         }
   4086 
   4087         p += pmatch[0].rm_eo;
   4088         n -= pmatch[0].rm_eo;
   4089 
   4090 
   4091         if (!opt_all || n == 0) {
   4092             break;
   4093         }
   4094 
   4095 
   4096         if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
   4097             break;
   4098         }
   4099 
   4100 
   4101         if (pattern[0] == '\0' && n) {
   4102 
   4103             Jim_AppendString(interp, resultObj, p, 1);
   4104             p++;
   4105             n--;
   4106         }
   4107 
   4108         if (pmatch[0].rm_eo == pmatch[0].rm_so) {
   4109 
   4110             regexec_flags = REG_NOTBOL;
   4111         }
   4112         else {
   4113             regexec_flags = 0;
   4114         }
   4115 
   4116     } while (n);
   4117 
   4118     Jim_AppendString(interp, resultObj, p, -1);
   4119 
   4120 cmd_error:
   4121     if (result == JIM_OK) {
   4122 
   4123         if (argc - i == 4) {
   4124             result = Jim_SetVariable(interp, varname, resultObj);
   4125 
   4126             if (result == JIM_OK) {
   4127                 Jim_SetResultInt(interp, num_matches);
   4128             }
   4129             else {
   4130                 Jim_FreeObj(interp, resultObj);
   4131             }
   4132         }
   4133         else {
   4134             Jim_SetResult(interp, resultObj);
   4135             result = JIM_OK;
   4136         }
   4137     }
   4138     else {
   4139         Jim_FreeObj(interp, resultObj);
   4140     }
   4141 
   4142     if (opt_command) {
   4143         Jim_DecrRefCount(interp, cmd_prefix);
   4144     }
   4145 
   4146 	Jim_DecrRefCount(interp, regcomp_obj);
   4147 
   4148     return result;
   4149 }
   4150 
   4151 int Jim_regexpInit(Jim_Interp *interp)
   4152 {
   4153     Jim_PackageProvideCheck(interp, "regexp");
   4154     Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
   4155     Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
   4156     return JIM_OK;
   4157 }
   4158 
   4159 #include <limits.h>
   4160 #include <stdlib.h>
   4161 #include <string.h>
   4162 #include <stdio.h>
   4163 #include <errno.h>
   4164 
   4165 
   4166 #ifdef HAVE_UTIMES
   4167 #include <sys/time.h>
   4168 #endif
   4169 #ifdef HAVE_UNISTD_H
   4170 #include <unistd.h>
   4171 #elif defined(_MSC_VER)
   4172 #include <direct.h>
   4173 #define F_OK 0
   4174 #define W_OK 2
   4175 #define R_OK 4
   4176 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
   4177 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
   4178 #endif
   4179 
   4180 #if defined(__MINGW32__) || defined(__MSYS__) || defined(_MSC_VER)
   4181 #define ISWINDOWS 1
   4182 
   4183 #undef HAVE_SYMLINK
   4184 #else
   4185 #define ISWINDOWS 0
   4186 #endif
   4187 
   4188 
   4189 #if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
   4190     #define STAT_MTIME_US(STAT) ((STAT).st_mtimespec.tv_sec * 1000000ll + (STAT).st_mtimespec.tv_nsec / 1000)
   4191 #elif defined(HAVE_STRUCT_STAT_ST_MTIM)
   4192     #define STAT_MTIME_US(STAT) ((STAT).st_mtim.tv_sec * 1000000ll + (STAT).st_mtim.tv_nsec / 1000)
   4193 #endif
   4194 
   4195 
   4196 static void JimFixPath(char *path)
   4197 {
   4198     if (ISWINDOWS) {
   4199 
   4200         char *p = path;
   4201         while ((p = strchr(p, '\\')) != NULL) {
   4202             *p++ = '/';
   4203         }
   4204     }
   4205 }
   4206 
   4207 
   4208 static const char *JimGetFileType(int mode)
   4209 {
   4210     if (S_ISREG(mode)) {
   4211         return "file";
   4212     }
   4213     else if (S_ISDIR(mode)) {
   4214         return "directory";
   4215     }
   4216 #ifdef S_ISCHR
   4217     else if (S_ISCHR(mode)) {
   4218         return "characterSpecial";
   4219     }
   4220 #endif
   4221 #ifdef S_ISBLK
   4222     else if (S_ISBLK(mode)) {
   4223         return "blockSpecial";
   4224     }
   4225 #endif
   4226 #ifdef S_ISFIFO
   4227     else if (S_ISFIFO(mode)) {
   4228         return "fifo";
   4229     }
   4230 #endif
   4231 #ifdef S_ISLNK
   4232     else if (S_ISLNK(mode)) {
   4233         return "link";
   4234     }
   4235 #endif
   4236 #ifdef S_ISSOCK
   4237     else if (S_ISSOCK(mode)) {
   4238         return "socket";
   4239     }
   4240 #endif
   4241     return "unknown";
   4242 }
   4243 
   4244 static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
   4245 {
   4246     Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
   4247     Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
   4248 }
   4249 
   4250 int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb)
   4251 {
   4252 
   4253     Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
   4254 
   4255     AppendStatElement(interp, listObj, "dev", sb->st_dev);
   4256     AppendStatElement(interp, listObj, "ino", sb->st_ino);
   4257     AppendStatElement(interp, listObj, "mode", sb->st_mode);
   4258     AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
   4259     AppendStatElement(interp, listObj, "uid", sb->st_uid);
   4260     AppendStatElement(interp, listObj, "gid", sb->st_gid);
   4261     AppendStatElement(interp, listObj, "size", sb->st_size);
   4262     AppendStatElement(interp, listObj, "atime", sb->st_atime);
   4263     AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
   4264     AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
   4265 #ifdef STAT_MTIME_US
   4266     AppendStatElement(interp, listObj, "mtimeus", STAT_MTIME_US(*sb));
   4267 #endif
   4268     Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
   4269     Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));
   4270 
   4271 
   4272     if (varName) {
   4273         Jim_Obj *objPtr;
   4274         objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
   4275 
   4276         if (objPtr) {
   4277             Jim_Obj *objv[2];
   4278 
   4279             objv[0] = objPtr;
   4280             objv[1] = listObj;
   4281 
   4282             objPtr = Jim_DictMerge(interp, 2, objv);
   4283             if (objPtr == NULL) {
   4284 
   4285                 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
   4286                 Jim_FreeNewObj(interp, listObj);
   4287                 return JIM_ERR;
   4288             }
   4289 
   4290             Jim_InvalidateStringRep(objPtr);
   4291 
   4292             Jim_FreeNewObj(interp, listObj);
   4293             listObj = objPtr;
   4294         }
   4295         Jim_SetVariable(interp, varName, listObj);
   4296     }
   4297 
   4298 
   4299     Jim_SetResult(interp, listObj);
   4300 
   4301     return JIM_OK;
   4302 }
   4303 
   4304 static int JimPathLenNoTrailingSlashes(const char *path, int len)
   4305 {
   4306     int i;
   4307     for (i = len; i > 1 && path[i - 1] == '/'; i--) {
   4308 
   4309         if (ISWINDOWS && path[i - 2] == ':') {
   4310 
   4311             break;
   4312         }
   4313     }
   4314     return i;
   4315 }
   4316 
   4317 static Jim_Obj *JimStripTrailingSlashes(Jim_Interp *interp, Jim_Obj *objPtr)
   4318 {
   4319     int len = Jim_Length(objPtr);
   4320     const char *path = Jim_String(objPtr);
   4321     int i = JimPathLenNoTrailingSlashes(path, len);
   4322     if (i != len) {
   4323         objPtr = Jim_NewStringObj(interp, path, i);
   4324     }
   4325     Jim_IncrRefCount(objPtr);
   4326     return objPtr;
   4327 }
   4328 
   4329 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4330 {
   4331     Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
   4332     const char *path = Jim_String(objPtr);
   4333     const char *p = strrchr(path, '/');
   4334 
   4335     if (!p) {
   4336         Jim_SetResultString(interp, ".", -1);
   4337     }
   4338     else if (p[1] == 0) {
   4339 
   4340         Jim_SetResult(interp, objPtr);
   4341     }
   4342     else if (p == path) {
   4343         Jim_SetResultString(interp, "/", -1);
   4344     }
   4345     else if (ISWINDOWS && p[-1] == ':') {
   4346 
   4347         Jim_SetResultString(interp, path, p - path + 1);
   4348     }
   4349     else {
   4350 
   4351         int len = JimPathLenNoTrailingSlashes(path, p - path);
   4352         Jim_SetResultString(interp, path, len);
   4353     }
   4354     Jim_DecrRefCount(interp, objPtr);
   4355     return JIM_OK;
   4356 }
   4357 
   4358 static int file_cmd_split(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4359 {
   4360     Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
   4361     const char *path = Jim_String(argv[0]);
   4362 
   4363     if (*path == '/') {
   4364         Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "/", 1));
   4365     }
   4366 
   4367     while (1) {
   4368 
   4369         while (*path == '/') {
   4370             path++;
   4371         }
   4372         if (*path) {
   4373             const char *pt = strchr(path, '/');
   4374             if (pt) {
   4375                 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, pt - path));
   4376                 path = pt;
   4377                 continue;
   4378             }
   4379             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, -1));
   4380         }
   4381         break;
   4382     }
   4383     Jim_SetResult(interp, listObj);
   4384     return JIM_OK;
   4385 }
   4386 
   4387 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4388 {
   4389     const char *path = Jim_String(argv[0]);
   4390     const char *lastSlash = strrchr(path, '/');
   4391     const char *p = strrchr(path, '.');
   4392 
   4393     if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
   4394         Jim_SetResult(interp, argv[0]);
   4395     }
   4396     else {
   4397         Jim_SetResultString(interp, path, p - path);
   4398     }
   4399     return JIM_OK;
   4400 }
   4401 
   4402 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4403 {
   4404     Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
   4405     const char *path = Jim_String(objPtr);
   4406     const char *lastSlash = strrchr(path, '/');
   4407     const char *p = strrchr(path, '.');
   4408 
   4409     if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
   4410         p = "";
   4411     }
   4412     Jim_SetResultString(interp, p, -1);
   4413     Jim_DecrRefCount(interp, objPtr);
   4414     return JIM_OK;
   4415 }
   4416 
   4417 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4418 {
   4419     Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
   4420     const char *path = Jim_String(objPtr);
   4421     const char *lastSlash = strrchr(path, '/');
   4422 
   4423     if (lastSlash) {
   4424         Jim_SetResultString(interp, lastSlash + 1, -1);
   4425     }
   4426     else {
   4427         Jim_SetResult(interp, objPtr);
   4428     }
   4429     Jim_DecrRefCount(interp, objPtr);
   4430     return JIM_OK;
   4431 }
   4432 
   4433 #ifndef HAVE_RESTRICT
   4434 #define restrict
   4435 #endif
   4436 
   4437 static char *JimRealPath(const char *restrict path, char *restrict resolved_path, size_t len)
   4438 {
   4439 #if defined(HAVE__FULLPATH)
   4440     return _fullpath(resolved_path, path, len);
   4441 #elif defined(HAVE_REALPATH)
   4442     return realpath(path, resolved_path);
   4443 #else
   4444     return NULL;
   4445 #endif
   4446 }
   4447 
   4448 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4449 {
   4450     const char *path = Jim_String(argv[0]);
   4451     char *newname = Jim_Alloc(MAXPATHLEN);
   4452 
   4453     if (JimRealPath(path, newname, MAXPATHLEN)) {
   4454         JimFixPath(newname);
   4455         Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
   4456         return JIM_OK;
   4457     }
   4458     Jim_Free(newname);
   4459     Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
   4460     return JIM_ERR;
   4461 }
   4462 
   4463 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4464 {
   4465     int i;
   4466     char *newname = Jim_Alloc(MAXPATHLEN + 1);
   4467     char *last = newname;
   4468 
   4469     *newname = 0;
   4470 
   4471 
   4472     for (i = 0; i < argc; i++) {
   4473         int len;
   4474         const char *part = Jim_GetString(argv[i], &len);
   4475 
   4476         if (*part == '/') {
   4477 
   4478             last = newname;
   4479         }
   4480         else if (ISWINDOWS && strchr(part, ':')) {
   4481 
   4482             last = newname;
   4483         }
   4484         else if (part[0] == '.') {
   4485             if (part[1] == '/') {
   4486                 part += 2;
   4487                 len -= 2;
   4488             }
   4489             else if (part[1] == 0 && last != newname) {
   4490 
   4491                 continue;
   4492             }
   4493         }
   4494 
   4495 
   4496         if (last != newname && last[-1] != '/') {
   4497             *last++ = '/';
   4498         }
   4499 
   4500         if (len) {
   4501             if (last + len - newname >= MAXPATHLEN) {
   4502                 Jim_Free(newname);
   4503                 Jim_SetResultString(interp, "Path too long", -1);
   4504                 return JIM_ERR;
   4505             }
   4506             memcpy(last, part, len);
   4507             last += len;
   4508         }
   4509 
   4510 
   4511         if (last > newname + 1 && last[-1] == '/') {
   4512 
   4513             if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
   4514                 *--last = 0;
   4515             }
   4516         }
   4517     }
   4518 
   4519     *last = 0;
   4520 
   4521 
   4522 
   4523     Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
   4524 
   4525     return JIM_OK;
   4526 }
   4527 
   4528 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
   4529 {
   4530     Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1);
   4531 
   4532     return JIM_OK;
   4533 }
   4534 
   4535 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4536 {
   4537     return file_access(interp, argv[0], R_OK);
   4538 }
   4539 
   4540 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4541 {
   4542     return file_access(interp, argv[0], W_OK);
   4543 }
   4544 
   4545 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4546 {
   4547 #ifdef X_OK
   4548     return file_access(interp, argv[0], X_OK);
   4549 #else
   4550 
   4551     Jim_SetResultBool(interp, 1);
   4552     return JIM_OK;
   4553 #endif
   4554 }
   4555 
   4556 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4557 {
   4558     return file_access(interp, argv[0], F_OK);
   4559 }
   4560 
   4561 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4562 {
   4563     int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
   4564 
   4565     if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
   4566         argc--;
   4567         argv++;
   4568     }
   4569 
   4570     while (argc--) {
   4571         const char *path = Jim_String(argv[0]);
   4572 
   4573         if (unlink(path) == -1 && errno != ENOENT) {
   4574             if (rmdir(path) == -1) {
   4575 
   4576                 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
   4577                     Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
   4578                         strerror(errno));
   4579                     return JIM_ERR;
   4580                 }
   4581             }
   4582         }
   4583         argv++;
   4584     }
   4585     return JIM_OK;
   4586 }
   4587 
   4588 #ifdef HAVE_MKDIR_ONE_ARG
   4589 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
   4590 #else
   4591 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
   4592 #endif
   4593 
   4594 static int mkdir_all(char *path)
   4595 {
   4596     int ok = 1;
   4597 
   4598 
   4599     goto first;
   4600 
   4601     while (ok--) {
   4602 
   4603         {
   4604             char *slash = strrchr(path, '/');
   4605 
   4606             if (slash && slash != path) {
   4607                 *slash = 0;
   4608                 if (mkdir_all(path) != 0) {
   4609                     return -1;
   4610                 }
   4611                 *slash = '/';
   4612             }
   4613         }
   4614       first:
   4615         if (MKDIR_DEFAULT(path) == 0) {
   4616             return 0;
   4617         }
   4618         if (errno == ENOENT) {
   4619 
   4620             continue;
   4621         }
   4622 
   4623         if (errno == EEXIST) {
   4624             jim_stat_t sb;
   4625 
   4626             if (Jim_Stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
   4627                 return 0;
   4628             }
   4629 
   4630             errno = EEXIST;
   4631         }
   4632 
   4633         break;
   4634     }
   4635     return -1;
   4636 }
   4637 
   4638 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4639 {
   4640     while (argc--) {
   4641         char *path = Jim_StrDup(Jim_String(argv[0]));
   4642         int rc = mkdir_all(path);
   4643 
   4644         Jim_Free(path);
   4645         if (rc != 0) {
   4646             Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
   4647                 strerror(errno));
   4648             return JIM_ERR;
   4649         }
   4650         argv++;
   4651     }
   4652     return JIM_OK;
   4653 }
   4654 
   4655 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4656 {
   4657     int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL, 0);
   4658 
   4659     if (fd < 0) {
   4660         return JIM_ERR;
   4661     }
   4662     close(fd);
   4663 
   4664     return JIM_OK;
   4665 }
   4666 
   4667 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4668 {
   4669     const char *source;
   4670     const char *dest;
   4671     int force = 0;
   4672 
   4673     if (argc == 3) {
   4674         if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
   4675             return -1;
   4676         }
   4677         force++;
   4678         argv++;
   4679         argc--;
   4680     }
   4681 
   4682     source = Jim_String(argv[0]);
   4683     dest = Jim_String(argv[1]);
   4684 
   4685     if (!force && access(dest, F_OK) == 0) {
   4686         Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
   4687             argv[1]);
   4688         return JIM_ERR;
   4689     }
   4690 #if ISWINDOWS
   4691     if (access(dest, F_OK) == 0) {
   4692 
   4693         remove(dest);
   4694     }
   4695 #endif
   4696     if (rename(source, dest) != 0) {
   4697         Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
   4698             strerror(errno));
   4699         return JIM_ERR;
   4700     }
   4701 
   4702     return JIM_OK;
   4703 }
   4704 
   4705 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
   4706 static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4707 {
   4708     int ret;
   4709     const char *source;
   4710     const char *dest;
   4711     static const char * const options[] = { "-hard", "-symbolic", NULL };
   4712     enum { OPT_HARD, OPT_SYMBOLIC, };
   4713     int option = OPT_HARD;
   4714 
   4715     if (argc == 3) {
   4716         if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
   4717             return JIM_ERR;
   4718         }
   4719         argv++;
   4720         argc--;
   4721     }
   4722 
   4723     dest = Jim_String(argv[0]);
   4724     source = Jim_String(argv[1]);
   4725 
   4726     if (option == OPT_HARD) {
   4727         ret = link(source, dest);
   4728     }
   4729     else {
   4730         ret = symlink(source, dest);
   4731     }
   4732 
   4733     if (ret != 0) {
   4734         Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
   4735             strerror(errno));
   4736         return JIM_ERR;
   4737     }
   4738 
   4739     return JIM_OK;
   4740 }
   4741 #endif
   4742 
   4743 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb)
   4744 {
   4745     const char *path = Jim_String(filename);
   4746 
   4747     if (Jim_Stat(path, sb) == -1) {
   4748         Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
   4749         return JIM_ERR;
   4750     }
   4751     return JIM_OK;
   4752 }
   4753 
   4754 #ifdef Jim_LinkStat
   4755 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb)
   4756 {
   4757     const char *path = Jim_String(filename);
   4758 
   4759     if (Jim_LinkStat(path, sb) == -1) {
   4760         Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
   4761         return JIM_ERR;
   4762     }
   4763     return JIM_OK;
   4764 }
   4765 #else
   4766 #define file_lstat file_stat
   4767 #endif
   4768 
   4769 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4770 {
   4771     jim_stat_t sb;
   4772 
   4773     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
   4774         return JIM_ERR;
   4775     }
   4776     Jim_SetResultInt(interp, sb.st_atime);
   4777     return JIM_OK;
   4778 }
   4779 
   4780 static int JimSetFileTimes(Jim_Interp *interp, const char *filename, jim_wide us)
   4781 {
   4782 #ifdef HAVE_UTIMES
   4783     struct timeval times[2];
   4784 
   4785     times[1].tv_sec = times[0].tv_sec = us / 1000000;
   4786     times[1].tv_usec = times[0].tv_usec = us % 1000000;
   4787 
   4788     if (utimes(filename, times) != 0) {
   4789         Jim_SetResultFormatted(interp, "can't set time on \"%s\": %s", filename, strerror(errno));
   4790         return JIM_ERR;
   4791     }
   4792     return JIM_OK;
   4793 #else
   4794     Jim_SetResultString(interp, "Not implemented", -1);
   4795     return JIM_ERR;
   4796 #endif
   4797 }
   4798 
   4799 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4800 {
   4801     jim_stat_t sb;
   4802 
   4803     if (argc == 2) {
   4804         jim_wide secs;
   4805         if (Jim_GetWide(interp, argv[1], &secs) != JIM_OK) {
   4806             return JIM_ERR;
   4807         }
   4808         return JimSetFileTimes(interp, Jim_String(argv[0]), secs * 1000000);
   4809     }
   4810     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
   4811         return JIM_ERR;
   4812     }
   4813     Jim_SetResultInt(interp, sb.st_mtime);
   4814     return JIM_OK;
   4815 }
   4816 
   4817 #ifdef STAT_MTIME_US
   4818 static int file_cmd_mtimeus(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4819 {
   4820     jim_stat_t sb;
   4821 
   4822     if (argc == 2) {
   4823         jim_wide us;
   4824         if (Jim_GetWide(interp, argv[1], &us) != JIM_OK) {
   4825             return JIM_ERR;
   4826         }
   4827         return JimSetFileTimes(interp, Jim_String(argv[0]), us);
   4828     }
   4829     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
   4830         return JIM_ERR;
   4831     }
   4832     Jim_SetResultInt(interp, STAT_MTIME_US(sb));
   4833     return JIM_OK;
   4834 }
   4835 #endif
   4836 
   4837 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4838 {
   4839     return Jim_EvalPrefix(interp, "file copy", argc, argv);
   4840 }
   4841 
   4842 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4843 {
   4844     jim_stat_t sb;
   4845 
   4846     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
   4847         return JIM_ERR;
   4848     }
   4849     Jim_SetResultInt(interp, sb.st_size);
   4850     return JIM_OK;
   4851 }
   4852 
   4853 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4854 {
   4855     jim_stat_t sb;
   4856     int ret = 0;
   4857 
   4858     if (file_stat(interp, argv[0], &sb) == JIM_OK) {
   4859         ret = S_ISDIR(sb.st_mode);
   4860     }
   4861     Jim_SetResultInt(interp, ret);
   4862     return JIM_OK;
   4863 }
   4864 
   4865 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4866 {
   4867     jim_stat_t sb;
   4868     int ret = 0;
   4869 
   4870     if (file_stat(interp, argv[0], &sb) == JIM_OK) {
   4871         ret = S_ISREG(sb.st_mode);
   4872     }
   4873     Jim_SetResultInt(interp, ret);
   4874     return JIM_OK;
   4875 }
   4876 
   4877 #ifdef HAVE_GETEUID
   4878 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4879 {
   4880     jim_stat_t sb;
   4881     int ret = 0;
   4882 
   4883     if (file_stat(interp, argv[0], &sb) == JIM_OK) {
   4884         ret = (geteuid() == sb.st_uid);
   4885     }
   4886     Jim_SetResultInt(interp, ret);
   4887     return JIM_OK;
   4888 }
   4889 #endif
   4890 
   4891 #if defined(HAVE_READLINK)
   4892 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4893 {
   4894     const char *path = Jim_String(argv[0]);
   4895     char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
   4896 
   4897     int linkLength = readlink(path, linkValue, MAXPATHLEN);
   4898 
   4899     if (linkLength == -1) {
   4900         Jim_Free(linkValue);
   4901         Jim_SetResultFormatted(interp, "could not read link \"%#s\": %s", argv[0], strerror(errno));
   4902         return JIM_ERR;
   4903     }
   4904     linkValue[linkLength] = 0;
   4905     Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
   4906     return JIM_OK;
   4907 }
   4908 #endif
   4909 
   4910 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4911 {
   4912     jim_stat_t sb;
   4913 
   4914     if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
   4915         return JIM_ERR;
   4916     }
   4917     Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
   4918     return JIM_OK;
   4919 }
   4920 
   4921 #ifdef Jim_LinkStat
   4922 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4923 {
   4924     jim_stat_t sb;
   4925 
   4926     if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
   4927         return JIM_ERR;
   4928     }
   4929     return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
   4930 }
   4931 #else
   4932 #define file_cmd_lstat file_cmd_stat
   4933 #endif
   4934 
   4935 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   4936 {
   4937     jim_stat_t sb;
   4938 
   4939     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
   4940         return JIM_ERR;
   4941     }
   4942     return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
   4943 }
   4944 
   4945 static const jim_subcmd_type file_command_table[] = {
   4946     {   "atime",
   4947         "name",
   4948         file_cmd_atime,
   4949         1,
   4950         1,
   4951 
   4952     },
   4953     {   "mtime",
   4954         "name ?time?",
   4955         file_cmd_mtime,
   4956         1,
   4957         2,
   4958 
   4959     },
   4960 #ifdef STAT_MTIME_US
   4961     {   "mtimeus",
   4962         "name ?time?",
   4963         file_cmd_mtimeus,
   4964         1,
   4965         2,
   4966 
   4967     },
   4968 #endif
   4969     {   "copy",
   4970         "?-force? source dest",
   4971         file_cmd_copy,
   4972         2,
   4973         3,
   4974 
   4975     },
   4976     {   "dirname",
   4977         "name",
   4978         file_cmd_dirname,
   4979         1,
   4980         1,
   4981 
   4982     },
   4983     {   "rootname",
   4984         "name",
   4985         file_cmd_rootname,
   4986         1,
   4987         1,
   4988 
   4989     },
   4990     {   "extension",
   4991         "name",
   4992         file_cmd_extension,
   4993         1,
   4994         1,
   4995 
   4996     },
   4997     {   "tail",
   4998         "name",
   4999         file_cmd_tail,
   5000         1,
   5001         1,
   5002 
   5003     },
   5004     {   "split",
   5005         "name",
   5006         file_cmd_split,
   5007         1,
   5008         1,
   5009 
   5010     },
   5011     {   "normalize",
   5012         "name",
   5013         file_cmd_normalize,
   5014         1,
   5015         1,
   5016 
   5017     },
   5018     {   "join",
   5019         "name ?name ...?",
   5020         file_cmd_join,
   5021         1,
   5022         -1,
   5023 
   5024     },
   5025     {   "readable",
   5026         "name",
   5027         file_cmd_readable,
   5028         1,
   5029         1,
   5030 
   5031     },
   5032     {   "writable",
   5033         "name",
   5034         file_cmd_writable,
   5035         1,
   5036         1,
   5037 
   5038     },
   5039     {   "executable",
   5040         "name",
   5041         file_cmd_executable,
   5042         1,
   5043         1,
   5044 
   5045     },
   5046     {   "exists",
   5047         "name",
   5048         file_cmd_exists,
   5049         1,
   5050         1,
   5051 
   5052     },
   5053     {   "delete",
   5054         "?-force|--? name ...",
   5055         file_cmd_delete,
   5056         1,
   5057         -1,
   5058 
   5059     },
   5060     {   "mkdir",
   5061         "dir ...",
   5062         file_cmd_mkdir,
   5063         1,
   5064         -1,
   5065 
   5066     },
   5067     {   "tempfile",
   5068         "?template?",
   5069         file_cmd_tempfile,
   5070         0,
   5071         1,
   5072 
   5073     },
   5074     {   "rename",
   5075         "?-force? source dest",
   5076         file_cmd_rename,
   5077         2,
   5078         3,
   5079 
   5080     },
   5081 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
   5082     {   "link",
   5083         "?-symbolic|-hard? newname target",
   5084         file_cmd_link,
   5085         2,
   5086         3,
   5087 
   5088     },
   5089 #endif
   5090 #if defined(HAVE_READLINK)
   5091     {   "readlink",
   5092         "name",
   5093         file_cmd_readlink,
   5094         1,
   5095         1,
   5096 
   5097     },
   5098 #endif
   5099     {   "size",
   5100         "name",
   5101         file_cmd_size,
   5102         1,
   5103         1,
   5104 
   5105     },
   5106     {   "stat",
   5107         "name ?var?",
   5108         file_cmd_stat,
   5109         1,
   5110         2,
   5111 
   5112     },
   5113     {   "lstat",
   5114         "name ?var?",
   5115         file_cmd_lstat,
   5116         1,
   5117         2,
   5118 
   5119     },
   5120     {   "type",
   5121         "name",
   5122         file_cmd_type,
   5123         1,
   5124         1,
   5125 
   5126     },
   5127 #ifdef HAVE_GETEUID
   5128     {   "owned",
   5129         "name",
   5130         file_cmd_owned,
   5131         1,
   5132         1,
   5133 
   5134     },
   5135 #endif
   5136     {   "isdirectory",
   5137         "name",
   5138         file_cmd_isdirectory,
   5139         1,
   5140         1,
   5141 
   5142     },
   5143     {   "isfile",
   5144         "name",
   5145         file_cmd_isfile,
   5146         1,
   5147         1,
   5148 
   5149     },
   5150     {
   5151         NULL
   5152     }
   5153 };
   5154 
   5155 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   5156 {
   5157     const char *path;
   5158 
   5159     if (argc != 2) {
   5160         Jim_WrongNumArgs(interp, 1, argv, "dirname");
   5161         return JIM_ERR;
   5162     }
   5163 
   5164     path = Jim_String(argv[1]);
   5165 
   5166     if (chdir(path) != 0) {
   5167         Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
   5168             strerror(errno));
   5169         return JIM_ERR;
   5170     }
   5171     return JIM_OK;
   5172 }
   5173 
   5174 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   5175 {
   5176     char *cwd = Jim_Alloc(MAXPATHLEN);
   5177 
   5178     if (getcwd(cwd, MAXPATHLEN) == NULL) {
   5179         Jim_SetResultString(interp, "Failed to get pwd", -1);
   5180         Jim_Free(cwd);
   5181         return JIM_ERR;
   5182     }
   5183     JimFixPath(cwd);
   5184     Jim_SetResultString(interp, cwd, -1);
   5185 
   5186     Jim_Free(cwd);
   5187     return JIM_OK;
   5188 }
   5189 
   5190 int Jim_fileInit(Jim_Interp *interp)
   5191 {
   5192     Jim_PackageProvideCheck(interp, "file");
   5193     Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
   5194     Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
   5195     Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
   5196     return JIM_OK;
   5197 }
   5198 
   5199 #include <string.h>
   5200 #include <ctype.h>
   5201 
   5202 
   5203 #if (!(defined(HAVE_VFORK) || defined(HAVE_FORK)) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
   5204 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   5205 {
   5206     Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
   5207     int i, j;
   5208     int rc;
   5209 
   5210 
   5211     for (i = 1; i < argc; i++) {
   5212         int len;
   5213         const char *arg = Jim_GetString(argv[i], &len);
   5214 
   5215         if (i > 1) {
   5216             Jim_AppendString(interp, cmdlineObj, " ", 1);
   5217         }
   5218         if (strpbrk(arg, "\\\" ") == NULL) {
   5219 
   5220             Jim_AppendString(interp, cmdlineObj, arg, len);
   5221             continue;
   5222         }
   5223 
   5224         Jim_AppendString(interp, cmdlineObj, "\"", 1);
   5225         for (j = 0; j < len; j++) {
   5226             if (arg[j] == '\\' || arg[j] == '"') {
   5227                 Jim_AppendString(interp, cmdlineObj, "\\", 1);
   5228             }
   5229             Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
   5230         }
   5231         Jim_AppendString(interp, cmdlineObj, "\"", 1);
   5232     }
   5233     rc = system(Jim_String(cmdlineObj));
   5234 
   5235     Jim_FreeNewObj(interp, cmdlineObj);
   5236 
   5237     if (rc) {
   5238         Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
   5239         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
   5240         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
   5241         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
   5242         Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
   5243         return JIM_ERR;
   5244     }
   5245 
   5246     return JIM_OK;
   5247 }
   5248 
   5249 int Jim_execInit(Jim_Interp *interp)
   5250 {
   5251     Jim_PackageProvideCheck(interp, "exec");
   5252     Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
   5253     return JIM_OK;
   5254 }
   5255 #else
   5256 
   5257 
   5258 #include <errno.h>
   5259 #include <signal.h>
   5260 #include <sys/stat.h>
   5261 
   5262 struct WaitInfoTable;
   5263 
   5264 static char **JimOriginalEnviron(void);
   5265 static char **JimSaveEnv(char **env);
   5266 static void JimRestoreEnv(char **env);
   5267 static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
   5268     phandle_t **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
   5269 static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr);
   5270 static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj);
   5271 static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
   5272 
   5273 #if defined(__MINGW32__)
   5274 static phandle_t JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId);
   5275 #endif
   5276 
   5277 static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
   5278 {
   5279     int len;
   5280     const char *s = Jim_GetString(objPtr, &len);
   5281 
   5282     if (len > 0 && s[len - 1] == '\n') {
   5283         objPtr->length--;
   5284         objPtr->bytes[objPtr->length] = '\0';
   5285     }
   5286 }
   5287 
   5288 static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj)
   5289 {
   5290     char buf[256];
   5291     int ret = 0;
   5292 
   5293     while (1) {
   5294         int retval = read(fd, buf, sizeof(buf));
   5295         if (retval > 0) {
   5296             ret = 1;
   5297             Jim_AppendString(interp, strObj, buf, retval);
   5298         }
   5299         if (retval <= 0) {
   5300             break;
   5301         }
   5302     }
   5303     close(fd);
   5304     return ret;
   5305 }
   5306 
   5307 static char **JimBuildEnv(Jim_Interp *interp)
   5308 {
   5309     int i;
   5310     int size;
   5311     int num;
   5312     int n;
   5313     char **envptr;
   5314     char *envdata;
   5315 
   5316     Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
   5317 
   5318     if (!objPtr) {
   5319         return JimOriginalEnviron();
   5320     }
   5321 
   5322 
   5323 
   5324     num = Jim_ListLength(interp, objPtr);
   5325     if (num % 2) {
   5326 
   5327         num--;
   5328     }
   5329     size = Jim_Length(objPtr) + 2;
   5330 
   5331     envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
   5332     envdata = (char *)&envptr[num / 2 + 1];
   5333 
   5334     n = 0;
   5335     for (i = 0; i < num; i += 2) {
   5336         const char *s1, *s2;
   5337         Jim_Obj *elemObj;
   5338 
   5339         Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
   5340         s1 = Jim_String(elemObj);
   5341         Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
   5342         s2 = Jim_String(elemObj);
   5343 
   5344         envptr[n] = envdata;
   5345         envdata += sprintf(envdata, "%s=%s", s1, s2);
   5346         envdata++;
   5347         n++;
   5348     }
   5349     envptr[n] = NULL;
   5350     *envdata = 0;
   5351 
   5352     return envptr;
   5353 }
   5354 
   5355 static void JimFreeEnv(char **env, char **original_environ)
   5356 {
   5357     if (env != original_environ) {
   5358         Jim_Free(env);
   5359     }
   5360 }
   5361 
   5362 static Jim_Obj *JimMakeErrorCode(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj)
   5363 {
   5364     Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
   5365 
   5366     if (pid <= 0) {
   5367         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
   5368         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
   5369         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, -1));
   5370     }
   5371     else if (WIFEXITED(waitStatus)) {
   5372         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
   5373         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
   5374         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
   5375     }
   5376     else {
   5377         const char *type;
   5378         const char *action;
   5379         const char *signame;
   5380 
   5381         if (WIFSIGNALED(waitStatus)) {
   5382             type = "CHILDKILLED";
   5383             action = "killed";
   5384             signame = Jim_SignalId(WTERMSIG(waitStatus));
   5385         }
   5386         else {
   5387             type = "CHILDSUSP";
   5388             action = "suspended";
   5389             signame = "none";
   5390         }
   5391 
   5392         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
   5393 
   5394         if (errStrObj) {
   5395             Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
   5396         }
   5397 
   5398         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
   5399         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, signame, -1));
   5400     }
   5401     return errorCode;
   5402 }
   5403 
   5404 static int JimCheckWaitStatus(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj)
   5405 {
   5406     if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
   5407         return JIM_OK;
   5408     }
   5409     Jim_SetGlobalVariableStr(interp, "errorCode", JimMakeErrorCode(interp, pid, waitStatus, errStrObj));
   5410 
   5411     return JIM_ERR;
   5412 }
   5413 
   5414 
   5415 struct WaitInfo
   5416 {
   5417     phandle_t phandle;
   5418     int status;
   5419     int flags;
   5420 };
   5421 
   5422 
   5423 struct WaitInfoTable {
   5424     struct WaitInfo *info;
   5425     int size;
   5426     int used;
   5427     int refcount;
   5428 };
   5429 
   5430 
   5431 #define WI_DETACHED 2
   5432 
   5433 #define WAIT_TABLE_GROW_BY 4
   5434 
   5435 static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
   5436 {
   5437     struct WaitInfoTable *table = privData;
   5438 
   5439     if (--table->refcount == 0) {
   5440         Jim_Free(table->info);
   5441         Jim_Free(table);
   5442     }
   5443 }
   5444 
   5445 static struct WaitInfoTable *JimAllocWaitInfoTable(void)
   5446 {
   5447     struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
   5448     table->info = NULL;
   5449     table->size = table->used = 0;
   5450     table->refcount = 1;
   5451 
   5452     return table;
   5453 }
   5454 
   5455 static int JimWaitRemove(struct WaitInfoTable *table, phandle_t phandle)
   5456 {
   5457     int i;
   5458 
   5459 
   5460     for (i = 0; i < table->used; i++) {
   5461         if (phandle == table->info[i].phandle) {
   5462             if (i != table->used - 1) {
   5463                 table->info[i] = table->info[table->used - 1];
   5464             }
   5465             table->used--;
   5466             return 0;
   5467         }
   5468     }
   5469     return -1;
   5470 }
   5471 
   5472 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   5473 {
   5474     int outputId;
   5475     int errorId;
   5476     phandle_t *pidPtr;
   5477     int numPids, result;
   5478     int child_siginfo = 1;
   5479     Jim_Obj *childErrObj;
   5480     Jim_Obj *errStrObj;
   5481     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
   5482 
   5483     if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
   5484         Jim_Obj *listObj;
   5485         int i;
   5486 
   5487         argc--;
   5488         numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
   5489         if (numPids < 0) {
   5490             return JIM_ERR;
   5491         }
   5492 
   5493         listObj = Jim_NewListObj(interp, NULL, 0);
   5494         for (i = 0; i < numPids; i++) {
   5495             Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, JimProcessPid(pidPtr[i])));
   5496         }
   5497         Jim_SetResult(interp, listObj);
   5498         JimDetachPids(table, numPids, pidPtr);
   5499         Jim_Free(pidPtr);
   5500         return JIM_OK;
   5501     }
   5502 
   5503     numPids =
   5504         JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
   5505 
   5506     if (numPids < 0) {
   5507         return JIM_ERR;
   5508     }
   5509 
   5510     result = JIM_OK;
   5511 
   5512     errStrObj = Jim_NewStringObj(interp, "", 0);
   5513 
   5514 
   5515     if (outputId != -1) {
   5516         if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) {
   5517             result = JIM_ERR;
   5518             Jim_SetResultErrno(interp, "error reading from output pipe");
   5519         }
   5520     }
   5521 
   5522 
   5523     childErrObj = Jim_NewStringObj(interp, "", 0);
   5524     Jim_IncrRefCount(childErrObj);
   5525 
   5526     if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
   5527         result = JIM_ERR;
   5528     }
   5529 
   5530     if (errorId != -1) {
   5531         int ret;
   5532         Jim_Lseek(errorId, 0, SEEK_SET);
   5533         ret = JimAppendStreamToString(interp, errorId, errStrObj);
   5534         if (ret < 0) {
   5535             Jim_SetResultErrno(interp, "error reading from error pipe");
   5536             result = JIM_ERR;
   5537         }
   5538         else if (ret > 0) {
   5539 
   5540             child_siginfo = 0;
   5541         }
   5542     }
   5543 
   5544     if (child_siginfo) {
   5545 
   5546         Jim_AppendObj(interp, errStrObj, childErrObj);
   5547     }
   5548     Jim_DecrRefCount(interp, childErrObj);
   5549 
   5550 
   5551     Jim_RemoveTrailingNewline(errStrObj);
   5552 
   5553 
   5554     Jim_SetResult(interp, errStrObj);
   5555 
   5556     return result;
   5557 }
   5558 
   5559 static long JimWaitForProcess(struct WaitInfoTable *table, phandle_t phandle, int *statusPtr)
   5560 {
   5561     if (JimWaitRemove(table, phandle) == 0) {
   5562 
   5563          return waitpid(phandle, statusPtr, 0);
   5564     }
   5565 
   5566 
   5567     return -1;
   5568 }
   5569 
   5570 static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr)
   5571 {
   5572     int j;
   5573 
   5574     for (j = 0; j < numPids; j++) {
   5575 
   5576         int i;
   5577         for (i = 0; i < table->used; i++) {
   5578             if (pidPtr[j] == table->info[i].phandle) {
   5579                 table->info[i].flags |= WI_DETACHED;
   5580                 break;
   5581             }
   5582         }
   5583     }
   5584 }
   5585 
   5586 static int JimGetChannelFd(Jim_Interp *interp, const char *name)
   5587 {
   5588     Jim_Obj *objv[2];
   5589 
   5590     objv[0] = Jim_NewStringObj(interp, name, -1);
   5591     objv[1] = Jim_NewStringObj(interp, "getfd", -1);
   5592 
   5593     if (Jim_EvalObjVector(interp, 2, objv) == JIM_OK) {
   5594         jim_wide fd;
   5595         if (Jim_GetWide(interp, Jim_GetResult(interp), &fd) == JIM_OK) {
   5596             return fd;
   5597         }
   5598     }
   5599     return -1;
   5600 }
   5601 
   5602 static void JimReapDetachedPids(struct WaitInfoTable *table)
   5603 {
   5604     struct WaitInfo *waitPtr;
   5605     int count;
   5606     int dest;
   5607 
   5608     if (!table) {
   5609         return;
   5610     }
   5611 
   5612     waitPtr = table->info;
   5613     dest = 0;
   5614     for (count = table->used; count > 0; waitPtr++, count--) {
   5615         if (waitPtr->flags & WI_DETACHED) {
   5616             int status;
   5617             long pid = waitpid(waitPtr->phandle, &status, WNOHANG);
   5618             if (pid > 0) {
   5619 
   5620                 table->used--;
   5621                 continue;
   5622             }
   5623         }
   5624         if (waitPtr != &table->info[dest]) {
   5625             table->info[dest] = *waitPtr;
   5626         }
   5627         dest++;
   5628     }
   5629 }
   5630 
   5631 static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   5632 {
   5633     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
   5634     int nohang = 0;
   5635     long pid;
   5636     phandle_t phandle;
   5637     int status;
   5638     Jim_Obj *errCodeObj;
   5639 
   5640 
   5641     if (argc == 1) {
   5642         JimReapDetachedPids(table);
   5643         return JIM_OK;
   5644     }
   5645 
   5646     if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) {
   5647         nohang = 1;
   5648     }
   5649     if (argc != nohang + 2) {
   5650         Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?");
   5651         return JIM_ERR;
   5652     }
   5653     if (Jim_GetLong(interp, argv[nohang + 1], &pid) != JIM_OK) {
   5654         return JIM_ERR;
   5655     }
   5656 
   5657 
   5658     phandle = JimWaitPid(pid, &status, nohang ? WNOHANG : 0);
   5659     if (phandle == JIM_BAD_PHANDLE) {
   5660         pid = -1;
   5661     }
   5662 #ifndef __MINGW32__
   5663     else if (pid < 0) {
   5664         pid = phandle;
   5665     }
   5666 #endif
   5667 
   5668     errCodeObj = JimMakeErrorCode(interp, pid, status, NULL);
   5669 
   5670     if (phandle != JIM_BAD_PHANDLE && (WIFEXITED(status) || WIFSIGNALED(status))) {
   5671 
   5672         JimWaitRemove(table, phandle);
   5673     }
   5674     Jim_SetResult(interp, errCodeObj);
   5675     return JIM_OK;
   5676 }
   5677 
   5678 static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   5679 {
   5680     if (argc != 1) {
   5681         Jim_WrongNumArgs(interp, 1, argv, "");
   5682         return JIM_ERR;
   5683     }
   5684 
   5685     Jim_SetResultInt(interp, (jim_wide)getpid());
   5686     return JIM_OK;
   5687 }
   5688 
   5689 static int
   5690 JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr,
   5691     int *inPipePtr, int *outPipePtr, int *errFilePtr)
   5692 {
   5693     phandle_t *pidPtr = NULL;         /* Points to alloc-ed array holding all
   5694                                  * the pids of child processes. */
   5695     int numPids = 0;            /* Actual number of processes that exist
   5696                                  * at *pidPtr right now. */
   5697     int cmdCount;               /* Count of number of distinct commands
   5698                                  * found in argc/argv. */
   5699     const char *input = NULL;   /* Describes input for pipeline, depending
   5700                                  * on "inputFile".  NULL means take input
   5701                                  * from stdin/pipe. */
   5702     int input_len = 0;
   5703 
   5704 #define FILE_NAME   0
   5705 #define FILE_APPEND 1
   5706 #define FILE_HANDLE 2
   5707 #define FILE_TEXT   3
   5708 
   5709     int inputFile = FILE_NAME;  /* 1 means input is name of input file.
   5710                                  * 2 means input is filehandle name.
   5711                                  * 0 means input holds actual
   5712                                  * text to be input to command. */
   5713 
   5714     int outputFile = FILE_NAME; /* 0 means output is the name of output file.
   5715                                  * 1 means output is the name of output file, and append.
   5716                                  * 2 means output is filehandle name.
   5717                                  * All this is ignored if output is NULL
   5718                                  */
   5719     int errorFile = FILE_NAME;  /* 0 means error is the name of error file.
   5720                                  * 1 means error is the name of error file, and append.
   5721                                  * 2 means error is filehandle name.
   5722                                  * All this is ignored if error is NULL
   5723                                  */
   5724     const char *output = NULL;  /* Holds name of output file to pipe to,
   5725                                  * or NULL if output goes to stdout/pipe. */
   5726     const char *error = NULL;   /* Holds name of stderr file to pipe to,
   5727                                  * or NULL if stderr goes to stderr/pipe. */
   5728     int inputId = -1;
   5729     int outputId = -1;
   5730     int errorId = -1;
   5731     int lastOutputId = -1;
   5732     int pipeIds[2];
   5733     int firstArg, lastArg;      /* Indexes of first and last arguments in
   5734                                  * current command. */
   5735     int lastBar;
   5736     int i;
   5737     phandle_t phandle;
   5738     char **save_environ;
   5739 #if defined(HAVE_EXECVPE) && !defined(__MINGW32__)
   5740     char **child_environ;
   5741 #endif
   5742     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
   5743 
   5744 
   5745     char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
   5746     int arg_count = 0;
   5747 
   5748     if (inPipePtr != NULL) {
   5749         *inPipePtr = -1;
   5750     }
   5751     if (outPipePtr != NULL) {
   5752         *outPipePtr = -1;
   5753     }
   5754     if (errFilePtr != NULL) {
   5755         *errFilePtr = -1;
   5756     }
   5757     pipeIds[0] = pipeIds[1] = -1;
   5758 
   5759     cmdCount = 1;
   5760     lastBar = -1;
   5761     for (i = 0; i < argc; i++) {
   5762         const char *arg = Jim_String(argv[i]);
   5763 
   5764         if (arg[0] == '<') {
   5765             inputFile = FILE_NAME;
   5766             input = arg + 1;
   5767             if (*input == '<') {
   5768                 inputFile = FILE_TEXT;
   5769                 input_len = Jim_Length(argv[i]) - 2;
   5770                 input++;
   5771             }
   5772             else if (*input == '@') {
   5773                 inputFile = FILE_HANDLE;
   5774                 input++;
   5775             }
   5776 
   5777             if (!*input && ++i < argc) {
   5778                 input = Jim_GetString(argv[i], &input_len);
   5779             }
   5780         }
   5781         else if (arg[0] == '>') {
   5782             int dup_error = 0;
   5783 
   5784             outputFile = FILE_NAME;
   5785 
   5786             output = arg + 1;
   5787             if (*output == '>') {
   5788                 outputFile = FILE_APPEND;
   5789                 output++;
   5790             }
   5791             if (*output == '&') {
   5792 
   5793                 output++;
   5794                 dup_error = 1;
   5795             }
   5796             if (*output == '@') {
   5797                 outputFile = FILE_HANDLE;
   5798                 output++;
   5799             }
   5800             if (!*output && ++i < argc) {
   5801                 output = Jim_String(argv[i]);
   5802             }
   5803             if (dup_error) {
   5804                 errorFile = outputFile;
   5805                 error = output;
   5806             }
   5807         }
   5808         else if (arg[0] == '2' && arg[1] == '>') {
   5809             error = arg + 2;
   5810             errorFile = FILE_NAME;
   5811 
   5812             if (*error == '@') {
   5813                 errorFile = FILE_HANDLE;
   5814                 error++;
   5815             }
   5816             else if (*error == '>') {
   5817                 errorFile = FILE_APPEND;
   5818                 error++;
   5819             }
   5820             if (!*error && ++i < argc) {
   5821                 error = Jim_String(argv[i]);
   5822             }
   5823         }
   5824         else {
   5825             if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
   5826                 if (i == lastBar + 1 || i == argc - 1) {
   5827                     Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
   5828                     goto badargs;
   5829                 }
   5830                 lastBar = i;
   5831                 cmdCount++;
   5832             }
   5833 
   5834             arg_array[arg_count++] = (char *)arg;
   5835             continue;
   5836         }
   5837 
   5838         if (i >= argc) {
   5839             Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
   5840             goto badargs;
   5841         }
   5842     }
   5843 
   5844     if (arg_count == 0) {
   5845         Jim_SetResultString(interp, "didn't specify command to execute", -1);
   5846 badargs:
   5847         Jim_Free(arg_array);
   5848         return -1;
   5849     }
   5850 
   5851 
   5852     save_environ = JimSaveEnv(JimBuildEnv(interp));
   5853 
   5854     if (input != NULL) {
   5855         if (inputFile == FILE_TEXT) {
   5856             inputId = Jim_MakeTempFile(interp, NULL, 1);
   5857             if (inputId == -1) {
   5858                 goto error;
   5859             }
   5860             if (write(inputId, input, input_len) != input_len) {
   5861                 Jim_SetResultErrno(interp, "couldn't write temp file");
   5862                 close(inputId);
   5863                 goto error;
   5864             }
   5865             Jim_Lseek(inputId, 0L, SEEK_SET);
   5866         }
   5867         else if (inputFile == FILE_HANDLE) {
   5868             int fd = JimGetChannelFd(interp, input);
   5869 
   5870             if (fd < 0) {
   5871                 goto error;
   5872             }
   5873             inputId = dup(fd);
   5874         }
   5875         else {
   5876             inputId = Jim_OpenForRead(input);
   5877             if (inputId == -1) {
   5878                 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, strerror(Jim_Errno()));
   5879                 goto error;
   5880             }
   5881         }
   5882     }
   5883     else if (inPipePtr != NULL) {
   5884         if (pipe(pipeIds) != 0) {
   5885             Jim_SetResultErrno(interp, "couldn't create input pipe for command");
   5886             goto error;
   5887         }
   5888         inputId = pipeIds[0];
   5889         *inPipePtr = pipeIds[1];
   5890         pipeIds[0] = pipeIds[1] = -1;
   5891     }
   5892 
   5893     if (output != NULL) {
   5894         if (outputFile == FILE_HANDLE) {
   5895             int fd = JimGetChannelFd(interp, output);
   5896             if (fd < 0) {
   5897                 goto error;
   5898             }
   5899             lastOutputId = dup(fd);
   5900         }
   5901         else {
   5902             lastOutputId = Jim_OpenForWrite(output, outputFile == FILE_APPEND);
   5903             if (lastOutputId == -1) {
   5904                 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, strerror(Jim_Errno()));
   5905                 goto error;
   5906             }
   5907         }
   5908     }
   5909     else if (outPipePtr != NULL) {
   5910         if (pipe(pipeIds) != 0) {
   5911             Jim_SetResultErrno(interp, "couldn't create output pipe");
   5912             goto error;
   5913         }
   5914         lastOutputId = pipeIds[1];
   5915         *outPipePtr = pipeIds[0];
   5916         pipeIds[0] = pipeIds[1] = -1;
   5917     }
   5918 
   5919     if (error != NULL) {
   5920         if (errorFile == FILE_HANDLE) {
   5921             if (strcmp(error, "1") == 0) {
   5922 
   5923                 if (lastOutputId != -1) {
   5924                     errorId = dup(lastOutputId);
   5925                 }
   5926                 else {
   5927 
   5928                     error = "stdout";
   5929                 }
   5930             }
   5931             if (errorId == -1) {
   5932                 int fd = JimGetChannelFd(interp, error);
   5933                 if (fd < 0) {
   5934                     goto error;
   5935                 }
   5936                 errorId = dup(fd);
   5937             }
   5938         }
   5939         else {
   5940             errorId = Jim_OpenForWrite(error, errorFile == FILE_APPEND);
   5941             if (errorId == -1) {
   5942                 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, strerror(Jim_Errno()));
   5943                 goto error;
   5944             }
   5945         }
   5946     }
   5947     else if (errFilePtr != NULL) {
   5948         errorId = Jim_MakeTempFile(interp, NULL, 1);
   5949         if (errorId == -1) {
   5950             goto error;
   5951         }
   5952         *errFilePtr = dup(errorId);
   5953     }
   5954 
   5955 
   5956     pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
   5957     for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
   5958         int pipe_dup_err = 0;
   5959         int origErrorId = errorId;
   5960 
   5961         for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
   5962             if (strcmp(arg_array[lastArg], "|") == 0) {
   5963                 break;
   5964             }
   5965             if (strcmp(arg_array[lastArg], "|&") == 0) {
   5966                 pipe_dup_err = 1;
   5967                 break;
   5968             }
   5969         }
   5970 
   5971         if (lastArg == firstArg) {
   5972             Jim_SetResultString(interp, "missing command to exec", -1);
   5973             goto error;
   5974         }
   5975 
   5976 
   5977         arg_array[lastArg] = NULL;
   5978         if (lastArg == arg_count) {
   5979             outputId = lastOutputId;
   5980             lastOutputId = -1;
   5981         }
   5982         else {
   5983             if (pipe(pipeIds) != 0) {
   5984                 Jim_SetResultErrno(interp, "couldn't create pipe");
   5985                 goto error;
   5986             }
   5987             outputId = pipeIds[1];
   5988         }
   5989 
   5990 
   5991         if (pipe_dup_err) {
   5992             errorId = outputId;
   5993         }
   5994 
   5995 
   5996 
   5997 #ifdef __MINGW32__
   5998         phandle = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId);
   5999         if (phandle == JIM_BAD_PHANDLE) {
   6000             Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
   6001             goto error;
   6002         }
   6003 #else
   6004         i = strlen(arg_array[firstArg]);
   6005 
   6006 #ifdef HAVE_EXECVPE
   6007         child_environ = Jim_GetEnviron();
   6008 #endif
   6009 #ifdef HAVE_VFORK
   6010         phandle = vfork();
   6011 #else
   6012         phandle = fork();
   6013 #endif
   6014         if (phandle < 0) {
   6015             Jim_SetResultErrno(interp, "couldn't fork child process");
   6016             goto error;
   6017         }
   6018         if (phandle == 0) {
   6019 
   6020 
   6021             if (inputId != -1 && inputId != fileno(stdin)) {
   6022                 dup2(inputId, fileno(stdin));
   6023                 close(inputId);
   6024             }
   6025             if (outputId != -1 && outputId != fileno(stdout)) {
   6026                 dup2(outputId, fileno(stdout));
   6027                 if (outputId != errorId) {
   6028                     close(outputId);
   6029                 }
   6030             }
   6031             if (errorId != -1 && errorId != fileno(stderr)) {
   6032                 dup2(errorId, fileno(stderr));
   6033                 close(errorId);
   6034             }
   6035 
   6036             if (outPipePtr && *outPipePtr != -1) {
   6037                 close(*outPipePtr);
   6038             }
   6039             if (errFilePtr && *errFilePtr != -1) {
   6040                 close(*errFilePtr);
   6041             }
   6042             if (pipeIds[0] != -1) {
   6043                 close(pipeIds[0]);
   6044             }
   6045             if (lastOutputId != -1) {
   6046                 close(lastOutputId);
   6047             }
   6048 
   6049             execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ);
   6050 
   6051             if (write(fileno(stderr), "couldn't exec \"", 15) &&
   6052                 write(fileno(stderr), arg_array[firstArg], i) &&
   6053                 write(fileno(stderr), "\"\n", 2)) {
   6054 
   6055             }
   6056 #ifdef JIM_MAINTAINER
   6057             {
   6058 
   6059                 static char *const false_argv[2] = {"false", NULL};
   6060                 execvp(false_argv[0],false_argv);
   6061             }
   6062 #endif
   6063             _exit(127);
   6064         }
   6065 #endif
   6066 
   6067 
   6068 
   6069         if (table->used == table->size) {
   6070             table->size += WAIT_TABLE_GROW_BY;
   6071             table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
   6072         }
   6073 
   6074         table->info[table->used].phandle = phandle;
   6075         table->info[table->used].flags = 0;
   6076         table->used++;
   6077 
   6078         pidPtr[numPids] = phandle;
   6079 
   6080 
   6081         errorId = origErrorId;
   6082 
   6083 
   6084         if (inputId != -1) {
   6085             close(inputId);
   6086         }
   6087         if (outputId != -1) {
   6088             close(outputId);
   6089         }
   6090         inputId = pipeIds[0];
   6091         pipeIds[0] = pipeIds[1] = -1;
   6092     }
   6093     *pidArrayPtr = pidPtr;
   6094 
   6095 
   6096   cleanup:
   6097     if (inputId != -1) {
   6098         close(inputId);
   6099     }
   6100     if (lastOutputId != -1) {
   6101         close(lastOutputId);
   6102     }
   6103     if (errorId != -1) {
   6104         close(errorId);
   6105     }
   6106     Jim_Free(arg_array);
   6107 
   6108     JimRestoreEnv(save_environ);
   6109 
   6110     return numPids;
   6111 
   6112 
   6113   error:
   6114     if ((inPipePtr != NULL) && (*inPipePtr != -1)) {
   6115         close(*inPipePtr);
   6116         *inPipePtr = -1;
   6117     }
   6118     if ((outPipePtr != NULL) && (*outPipePtr != -1)) {
   6119         close(*outPipePtr);
   6120         *outPipePtr = -1;
   6121     }
   6122     if ((errFilePtr != NULL) && (*errFilePtr != -1)) {
   6123         close(*errFilePtr);
   6124         *errFilePtr = -1;
   6125     }
   6126     if (pipeIds[0] != -1) {
   6127         close(pipeIds[0]);
   6128     }
   6129     if (pipeIds[1] != -1) {
   6130         close(pipeIds[1]);
   6131     }
   6132     if (pidPtr != NULL) {
   6133         for (i = 0; i < numPids; i++) {
   6134             if (pidPtr[i] != JIM_BAD_PHANDLE) {
   6135                 JimDetachPids(table, 1, &pidPtr[i]);
   6136             }
   6137         }
   6138         Jim_Free(pidPtr);
   6139     }
   6140     numPids = -1;
   6141     goto cleanup;
   6142 }
   6143 
   6144 
   6145 static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj)
   6146 {
   6147     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
   6148     int result = JIM_OK;
   6149     int i;
   6150 
   6151 
   6152     for (i = 0; i < numPids; i++) {
   6153         int waitStatus = 0;
   6154         long pid = JimWaitForProcess(table, pidPtr[i], &waitStatus);
   6155         if (pid > 0) {
   6156             if (JimCheckWaitStatus(interp, pid, waitStatus, errStrObj) != JIM_OK) {
   6157                 result = JIM_ERR;
   6158             }
   6159         }
   6160     }
   6161     Jim_Free(pidPtr);
   6162 
   6163     return result;
   6164 }
   6165 
   6166 int Jim_execInit(Jim_Interp *interp)
   6167 {
   6168     struct WaitInfoTable *waitinfo;
   6169 
   6170     Jim_PackageProvideCheck(interp, "exec");
   6171 
   6172     waitinfo = JimAllocWaitInfoTable();
   6173     Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable);
   6174     waitinfo->refcount++;
   6175     Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable);
   6176     Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0);
   6177 
   6178     return JIM_OK;
   6179 }
   6180 
   6181 #if defined(__MINGW32__)
   6182 
   6183 
   6184 static int
   6185 JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
   6186 {
   6187     int i;
   6188     static char extensions[][5] = {".exe", "", ".bat"};
   6189 
   6190     for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
   6191         snprintf(fullPath, MAX_PATH, "%s%s", originalName, extensions[i]);
   6192 
   6193         if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
   6194             continue;
   6195         }
   6196         if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
   6197             continue;
   6198         }
   6199         return 0;
   6200     }
   6201 
   6202     return -1;
   6203 }
   6204 
   6205 static char **JimSaveEnv(char **env)
   6206 {
   6207     return env;
   6208 }
   6209 
   6210 static void JimRestoreEnv(char **env)
   6211 {
   6212     JimFreeEnv(env, Jim_GetEnviron());
   6213 }
   6214 
   6215 static char **JimOriginalEnviron(void)
   6216 {
   6217     return NULL;
   6218 }
   6219 
   6220 static Jim_Obj *
   6221 JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
   6222 {
   6223     char *start, *special;
   6224     int quote, i;
   6225 
   6226     Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
   6227 
   6228     for (i = 0; argv[i]; i++) {
   6229         if (i > 0) {
   6230             Jim_AppendString(interp, strObj, " ", 1);
   6231         }
   6232 
   6233         if (argv[i][0] == '\0') {
   6234             quote = 1;
   6235         }
   6236         else {
   6237             quote = 0;
   6238             for (start = argv[i]; *start != '\0'; start++) {
   6239                 if (isspace(UCHAR(*start))) {
   6240                     quote = 1;
   6241                     break;
   6242                 }
   6243             }
   6244         }
   6245         if (quote) {
   6246             Jim_AppendString(interp, strObj, "\"" , 1);
   6247         }
   6248 
   6249         start = argv[i];
   6250         for (special = argv[i]; ; ) {
   6251             if ((*special == '\\') && (special[1] == '\\' ||
   6252                     special[1] == '"' || (quote && special[1] == '\0'))) {
   6253                 Jim_AppendString(interp, strObj, start, special - start);
   6254                 start = special;
   6255                 while (1) {
   6256                     special++;
   6257                     if (*special == '"' || (quote && *special == '\0')) {
   6258 
   6259                         Jim_AppendString(interp, strObj, start, special - start);
   6260                         break;
   6261                     }
   6262                     if (*special != '\\') {
   6263                         break;
   6264                     }
   6265                 }
   6266                 Jim_AppendString(interp, strObj, start, special - start);
   6267                 start = special;
   6268             }
   6269             if (*special == '"') {
   6270         if (special == start) {
   6271             Jim_AppendString(interp, strObj, "\"", 1);
   6272         }
   6273         else {
   6274             Jim_AppendString(interp, strObj, start, special - start);
   6275         }
   6276                 Jim_AppendString(interp, strObj, "\\\"", 2);
   6277                 start = special + 1;
   6278             }
   6279             if (*special == '\0') {
   6280                 break;
   6281             }
   6282             special++;
   6283         }
   6284         Jim_AppendString(interp, strObj, start, special - start);
   6285         if (quote) {
   6286             Jim_AppendString(interp, strObj, "\"", 1);
   6287         }
   6288     }
   6289     return strObj;
   6290 }
   6291 
   6292 static phandle_t
   6293 JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId)
   6294 {
   6295     STARTUPINFO startInfo;
   6296     PROCESS_INFORMATION procInfo;
   6297     HANDLE hProcess;
   6298     char execPath[MAX_PATH];
   6299     phandle_t phandle = INVALID_HANDLE_VALUE;
   6300     Jim_Obj *cmdLineObj;
   6301     char *winenv;
   6302 
   6303     if (JimWinFindExecutable(argv[0], execPath) < 0) {
   6304         return phandle;
   6305     }
   6306     argv[0] = execPath;
   6307 
   6308     hProcess = GetCurrentProcess();
   6309     cmdLineObj = JimWinBuildCommandLine(interp, argv);
   6310 
   6311 
   6312     ZeroMemory(&startInfo, sizeof(startInfo));
   6313     startInfo.cb = sizeof(startInfo);
   6314     startInfo.dwFlags   = STARTF_USESTDHANDLES;
   6315     startInfo.hStdInput = INVALID_HANDLE_VALUE;
   6316     startInfo.hStdOutput= INVALID_HANDLE_VALUE;
   6317     startInfo.hStdError = INVALID_HANDLE_VALUE;
   6318 
   6319     if (inputId == -1) {
   6320         inputId = _fileno(stdin);
   6321     }
   6322     DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(inputId), hProcess, &startInfo.hStdInput,
   6323             0, TRUE, DUPLICATE_SAME_ACCESS);
   6324     if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
   6325         goto end;
   6326     }
   6327 
   6328     if (outputId == -1) {
   6329         outputId = _fileno(stdout);
   6330     }
   6331     DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(outputId), hProcess, &startInfo.hStdOutput,
   6332             0, TRUE, DUPLICATE_SAME_ACCESS);
   6333     if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
   6334         goto end;
   6335     }
   6336 
   6337 
   6338     if (errorId == -1) {
   6339         errorId = _fileno(stderr);
   6340     }
   6341     DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(errorId), hProcess, &startInfo.hStdError,
   6342             0, TRUE, DUPLICATE_SAME_ACCESS);
   6343     if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
   6344         goto end;
   6345     }
   6346 
   6347     if (env == NULL) {
   6348 
   6349         winenv = NULL;
   6350     }
   6351     else if (env[0] == NULL) {
   6352         winenv = (char *)"\0";
   6353     }
   6354     else {
   6355         winenv = env[0];
   6356     }
   6357 
   6358     if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
   6359             0, winenv, NULL, &startInfo, &procInfo)) {
   6360         goto end;
   6361     }
   6362 
   6363 
   6364     WaitForInputIdle(procInfo.hProcess, 5000);
   6365     CloseHandle(procInfo.hThread);
   6366 
   6367     phandle = procInfo.hProcess;
   6368 
   6369     end:
   6370     Jim_FreeNewObj(interp, cmdLineObj);
   6371     if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
   6372         CloseHandle(startInfo.hStdInput);
   6373     }
   6374     if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
   6375         CloseHandle(startInfo.hStdOutput);
   6376     }
   6377     if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
   6378         CloseHandle(startInfo.hStdError);
   6379     }
   6380     return phandle;
   6381 }
   6382 
   6383 #else
   6384 
   6385 static char **JimOriginalEnviron(void)
   6386 {
   6387     return Jim_GetEnviron();
   6388 }
   6389 
   6390 static char **JimSaveEnv(char **env)
   6391 {
   6392     char **saveenv = Jim_GetEnviron();
   6393     Jim_SetEnviron(env);
   6394     return saveenv;
   6395 }
   6396 
   6397 static void JimRestoreEnv(char **env)
   6398 {
   6399     JimFreeEnv(Jim_GetEnviron(), env);
   6400     Jim_SetEnviron(env);
   6401 }
   6402 #endif
   6403 #endif
   6404 
   6405 
   6406 #include <stdlib.h>
   6407 #include <string.h>
   6408 #include <stdio.h>
   6409 #include <time.h>
   6410 
   6411 
   6412 #ifdef HAVE_SYS_TIME_H
   6413 #include <sys/time.h>
   6414 #endif
   6415 
   6416 struct clock_options {
   6417     int gmt;
   6418     const char *format;
   6419 };
   6420 
   6421 static int parse_clock_options(Jim_Interp *interp, int argc, Jim_Obj *const *argv, struct clock_options *opts)
   6422 {
   6423     static const char * const options[] = { "-gmt", "-format", NULL };
   6424     enum { OPT_GMT, OPT_FORMAT, };
   6425     int i;
   6426 
   6427     for (i = 0; i < argc; i += 2) {
   6428         int option;
   6429         if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
   6430             return JIM_ERR;
   6431         }
   6432         switch (option) {
   6433             case OPT_GMT:
   6434                 if (Jim_GetBoolean(interp, argv[i + 1], &opts->gmt) != JIM_OK) {
   6435                     return JIM_ERR;
   6436                 }
   6437                 break;
   6438             case OPT_FORMAT:
   6439                 opts->format = Jim_String(argv[i + 1]);
   6440                 break;
   6441         }
   6442     }
   6443     return JIM_OK;
   6444 }
   6445 
   6446 static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6447 {
   6448 
   6449     char buf[100];
   6450     time_t t;
   6451     jim_wide seconds;
   6452     struct clock_options options = { 0, "%a %b %d %H:%M:%S %Z %Y" };
   6453     struct tm *tm;
   6454 
   6455     if (Jim_GetWide(interp, argv[0], &seconds) != JIM_OK) {
   6456         return JIM_ERR;
   6457     }
   6458     if (argc % 2 == 0) {
   6459         return -1;
   6460     }
   6461     if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
   6462         return JIM_ERR;
   6463     }
   6464 
   6465     t = seconds;
   6466     tm = options.gmt ? gmtime(&t) : localtime(&t);
   6467 
   6468     if (tm == NULL || strftime(buf, sizeof(buf), options.format, tm) == 0) {
   6469         Jim_SetResultString(interp, "format string too long or invalid time", -1);
   6470         return JIM_ERR;
   6471     }
   6472 
   6473     Jim_SetResultString(interp, buf, -1);
   6474 
   6475     return JIM_OK;
   6476 }
   6477 
   6478 #ifdef HAVE_STRPTIME
   6479 static time_t jim_timegm(const struct tm *tm)
   6480 {
   6481     int m = tm->tm_mon + 1;
   6482     int y = 1900 + tm->tm_year - (m <= 2);
   6483     int era = (y >= 0 ? y : y - 399) / 400;
   6484     unsigned yoe = (unsigned)(y - era * 400);
   6485     unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + tm->tm_mday - 1;
   6486     unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
   6487     long days = (era * 146097 + (int)doe - 719468);
   6488     int secs = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
   6489 
   6490     return days * 24 * 60 * 60 + secs;
   6491 }
   6492 
   6493 static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6494 {
   6495     char *pt;
   6496     struct tm tm;
   6497     time_t now = time(NULL);
   6498 
   6499     struct clock_options options = { 0, NULL };
   6500 
   6501     if (argc % 2 == 0) {
   6502         return -1;
   6503     }
   6504 
   6505     if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
   6506         return JIM_ERR;
   6507     }
   6508     if (options.format == NULL) {
   6509         return -1;
   6510     }
   6511 
   6512     localtime_r(&now, &tm);
   6513 
   6514     pt = strptime(Jim_String(argv[0]), options.format, &tm);
   6515     if (pt == 0 || *pt != 0) {
   6516         Jim_SetResultString(interp, "Failed to parse time according to format", -1);
   6517         return JIM_ERR;
   6518     }
   6519 
   6520 
   6521     tm.tm_isdst = options.gmt ? 0 : -1;
   6522     Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm));
   6523 
   6524     return JIM_OK;
   6525 }
   6526 #endif
   6527 
   6528 static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6529 {
   6530     Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000000);
   6531     return JIM_OK;
   6532 }
   6533 
   6534 static int clock_cmd_clicks(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6535 {
   6536     Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW));
   6537     return JIM_OK;
   6538 }
   6539 
   6540 static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6541 {
   6542     Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME));
   6543     return JIM_OK;
   6544 }
   6545 
   6546 static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6547 {
   6548     Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000);
   6549     return JIM_OK;
   6550 }
   6551 
   6552 static const jim_subcmd_type clock_command_table[] = {
   6553     {   "clicks",
   6554         NULL,
   6555         clock_cmd_clicks,
   6556         0,
   6557         0,
   6558 
   6559     },
   6560     {   "format",
   6561         "seconds ?-format string? ?-gmt boolean?",
   6562         clock_cmd_format,
   6563         1,
   6564         5,
   6565 
   6566     },
   6567     {   "microseconds",
   6568         NULL,
   6569         clock_cmd_micros,
   6570         0,
   6571         0,
   6572 
   6573     },
   6574     {   "milliseconds",
   6575         NULL,
   6576         clock_cmd_millis,
   6577         0,
   6578         0,
   6579 
   6580     },
   6581 #ifdef HAVE_STRPTIME
   6582     {   "scan",
   6583         "str -format format ?-gmt boolean?",
   6584         clock_cmd_scan,
   6585         3,
   6586         5,
   6587 
   6588     },
   6589 #endif
   6590     {   "seconds",
   6591         NULL,
   6592         clock_cmd_seconds,
   6593         0,
   6594         0,
   6595 
   6596     },
   6597     { NULL }
   6598 };
   6599 
   6600 int Jim_clockInit(Jim_Interp *interp)
   6601 {
   6602     Jim_PackageProvideCheck(interp, "clock");
   6603     Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
   6604     return JIM_OK;
   6605 }
   6606 
   6607 #include <limits.h>
   6608 #include <stdlib.h>
   6609 #include <string.h>
   6610 #include <stdio.h>
   6611 #include <errno.h>
   6612 
   6613 
   6614 static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6615 {
   6616 
   6617     Jim_Obj *dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
   6618     Jim_SetResultInt(interp, dictObj && Jim_DictSize(interp, dictObj) != -1);
   6619     return JIM_OK;
   6620 }
   6621 
   6622 static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6623 {
   6624     Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
   6625     Jim_Obj *patternObj;
   6626 
   6627     if (!objPtr) {
   6628         return JIM_OK;
   6629     }
   6630 
   6631     patternObj = (argc == 1) ? NULL : argv[1];
   6632 
   6633 
   6634     if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) {
   6635         if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) {
   6636 
   6637             Jim_SetResult(interp, objPtr);
   6638             return JIM_OK;
   6639         }
   6640     }
   6641 
   6642     return Jim_DictMatchTypes(interp, objPtr, patternObj, JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS | JIM_DICTMATCH_VALUES);
   6643 }
   6644 
   6645 static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6646 {
   6647     Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
   6648 
   6649     if (!objPtr) {
   6650         return JIM_OK;
   6651     }
   6652 
   6653     return Jim_DictMatchTypes(interp, objPtr, argc == 1 ? NULL : argv[1], JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS);
   6654 }
   6655 
   6656 static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6657 {
   6658     int i;
   6659     int len;
   6660     Jim_Obj *resultObj;
   6661     Jim_Obj *objPtr;
   6662     Jim_Obj **dictValuesObj;
   6663 
   6664     if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
   6665 
   6666         Jim_UnsetVariable(interp, argv[0], JIM_NONE);
   6667         return JIM_OK;
   6668     }
   6669 
   6670     objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
   6671 
   6672     if (objPtr == NULL) {
   6673 
   6674         return JIM_OK;
   6675     }
   6676 
   6677     dictValuesObj = Jim_DictPairs(interp, objPtr, &len);
   6678     if (dictValuesObj == NULL) {
   6679 
   6680         Jim_SetResultString(interp, "", -1);
   6681         return JIM_OK;
   6682     }
   6683 
   6684 
   6685     resultObj = Jim_NewDictObj(interp, NULL, 0);
   6686 
   6687     for (i = 0; i < len; i += 2) {
   6688         if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
   6689             Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
   6690         }
   6691     }
   6692 
   6693     Jim_SetVariable(interp, argv[0], resultObj);
   6694     return JIM_OK;
   6695 }
   6696 
   6697 static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6698 {
   6699     Jim_Obj *objPtr;
   6700     int len = 0;
   6701 
   6702 
   6703     objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
   6704     if (objPtr) {
   6705         len = Jim_DictSize(interp, objPtr);
   6706         if (len < 0) {
   6707 
   6708             Jim_SetResultInt(interp, 0);
   6709             return JIM_OK;
   6710         }
   6711     }
   6712 
   6713     Jim_SetResultInt(interp, len);
   6714 
   6715     return JIM_OK;
   6716 }
   6717 
   6718 static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6719 {
   6720     Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
   6721     if (objPtr) {
   6722         return Jim_DictInfo(interp, objPtr);
   6723     }
   6724     Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL);
   6725     return JIM_ERR;
   6726 }
   6727 
   6728 static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   6729 {
   6730     int i;
   6731     int len;
   6732     Jim_Obj *listObj = argv[1];
   6733     Jim_Obj *dictObj;
   6734 
   6735     len = Jim_ListLength(interp, listObj);
   6736     if (len % 2) {
   6737         Jim_SetResultString(interp, "list must have an even number of elements", -1);
   6738         return JIM_ERR;
   6739     }
   6740 
   6741     dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
   6742     if (!dictObj) {
   6743 
   6744         return Jim_SetVariable(interp, argv[0], listObj);
   6745     }
   6746     else if (Jim_DictSize(interp, dictObj) < 0) {
   6747         return JIM_ERR;
   6748     }
   6749 
   6750     if (Jim_IsShared(dictObj)) {
   6751         dictObj = Jim_DuplicateObj(interp, dictObj);
   6752     }
   6753 
   6754     for (i = 0; i < len; i += 2) {
   6755         Jim_Obj *nameObj;
   6756         Jim_Obj *valueObj;
   6757 
   6758         Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE);
   6759         Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE);
   6760 
   6761         Jim_DictAddElement(interp, dictObj, nameObj, valueObj);
   6762     }
   6763     return Jim_SetVariable(interp, argv[0], dictObj);
   6764 }
   6765 
   6766 static const jim_subcmd_type array_command_table[] = {
   6767         {       "exists",
   6768                 "arrayName",
   6769                 array_cmd_exists,
   6770                 1,
   6771                 1,
   6772 
   6773         },
   6774         {       "get",
   6775                 "arrayName ?pattern?",
   6776                 array_cmd_get,
   6777                 1,
   6778                 2,
   6779 
   6780         },
   6781         {       "names",
   6782                 "arrayName ?pattern?",
   6783                 array_cmd_names,
   6784                 1,
   6785                 2,
   6786 
   6787         },
   6788         {       "set",
   6789                 "arrayName list",
   6790                 array_cmd_set,
   6791                 2,
   6792                 2,
   6793 
   6794         },
   6795         {       "size",
   6796                 "arrayName",
   6797                 array_cmd_size,
   6798                 1,
   6799                 1,
   6800 
   6801         },
   6802         {       "stat",
   6803                 "arrayName",
   6804                 array_cmd_stat,
   6805                 1,
   6806                 1,
   6807 
   6808         },
   6809         {       "unset",
   6810                 "arrayName ?pattern?",
   6811                 array_cmd_unset,
   6812                 1,
   6813                 2,
   6814 
   6815         },
   6816         {       NULL
   6817         }
   6818 };
   6819 
   6820 int Jim_arrayInit(Jim_Interp *interp)
   6821 {
   6822     Jim_PackageProvideCheck(interp, "array");
   6823     Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
   6824     return JIM_OK;
   6825 }
   6826 int Jim_InitStaticExtensions(Jim_Interp *interp)
   6827 {
   6828 extern int Jim_bootstrapInit(Jim_Interp *);
   6829 extern int Jim_aioInit(Jim_Interp *);
   6830 extern int Jim_readdirInit(Jim_Interp *);
   6831 extern int Jim_regexpInit(Jim_Interp *);
   6832 extern int Jim_fileInit(Jim_Interp *);
   6833 extern int Jim_globInit(Jim_Interp *);
   6834 extern int Jim_execInit(Jim_Interp *);
   6835 extern int Jim_clockInit(Jim_Interp *);
   6836 extern int Jim_arrayInit(Jim_Interp *);
   6837 extern int Jim_stdlibInit(Jim_Interp *);
   6838 extern int Jim_tclcompatInit(Jim_Interp *);
   6839 Jim_bootstrapInit(interp);
   6840 Jim_aioInit(interp);
   6841 Jim_readdirInit(interp);
   6842 Jim_regexpInit(interp);
   6843 Jim_fileInit(interp);
   6844 Jim_globInit(interp);
   6845 Jim_execInit(interp);
   6846 Jim_clockInit(interp);
   6847 Jim_arrayInit(interp);
   6848 Jim_stdlibInit(interp);
   6849 Jim_tclcompatInit(interp);
   6850 return JIM_OK;
   6851 }
   6852 #ifndef JIM_TINY
   6853 #define JIM_OPTIMIZATION
   6854 #endif
   6855 
   6856 #include <stdio.h>
   6857 #include <stdlib.h>
   6858 
   6859 #include <string.h>
   6860 #include <stdarg.h>
   6861 #include <ctype.h>
   6862 #include <limits.h>
   6863 #include <assert.h>
   6864 #include <errno.h>
   6865 #include <time.h>
   6866 #include <setjmp.h>
   6867 
   6868 
   6869 #ifdef HAVE_SYS_TIME_H
   6870 #include <sys/time.h>
   6871 #endif
   6872 #ifdef HAVE_EXECINFO_H
   6873 #include <execinfo.h>
   6874 #endif
   6875 #ifdef HAVE_CRT_EXTERNS_H
   6876 #include <crt_externs.h>
   6877 #endif
   6878 
   6879 
   6880 #include <math.h>
   6881 
   6882 
   6883 
   6884 
   6885 
   6886 #ifndef TCL_LIBRARY
   6887 #define TCL_LIBRARY "."
   6888 #endif
   6889 #ifndef TCL_PLATFORM_OS
   6890 #define TCL_PLATFORM_OS "unknown"
   6891 #endif
   6892 #ifndef TCL_PLATFORM_PLATFORM
   6893 #define TCL_PLATFORM_PLATFORM "unknown"
   6894 #endif
   6895 #ifndef TCL_PLATFORM_PATH_SEPARATOR
   6896 #define TCL_PLATFORM_PATH_SEPARATOR ":"
   6897 #endif
   6898 
   6899 
   6900 
   6901 
   6902 
   6903 
   6904 
   6905 #ifdef JIM_MAINTAINER
   6906 #define JIM_DEBUG_COMMAND
   6907 #define JIM_DEBUG_PANIC
   6908 #endif
   6909 
   6910 
   6911 
   6912 #define JIM_INTEGER_SPACE 24
   6913 
   6914 #if defined(DEBUG_SHOW_SCRIPT) || defined(DEBUG_SHOW_SCRIPT_TOKENS) || defined(JIM_DEBUG_COMMAND) || defined(DEBUG_SHOW_SUBST)
   6915 static const char *jim_tt_name(int type);
   6916 #endif
   6917 
   6918 #ifdef JIM_DEBUG_PANIC
   6919 static void JimPanicDump(int fail_condition, const char *fmt, ...);
   6920 #define JimPanic(X) JimPanicDump X
   6921 #else
   6922 #define JimPanic(X)
   6923 #endif
   6924 
   6925 #ifdef JIM_OPTIMIZATION
   6926 static int JimIsWide(Jim_Obj *objPtr);
   6927 #define JIM_IF_OPTIM(X) X
   6928 #else
   6929 #define JIM_IF_OPTIM(X)
   6930 #endif
   6931 
   6932 
   6933 static char JimEmptyStringRep[] = "";
   6934 
   6935 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
   6936 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
   6937     int flags);
   6938 static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *const *indexv, int indexc,
   6939     Jim_Obj **resultObj, int flags);
   6940 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
   6941 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
   6942 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
   6943 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
   6944     const char *prefix, const char *const *tablePtr, const char *name);
   6945 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
   6946 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
   6947 static int JimSign(jim_wide w);
   6948 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
   6949 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
   6950 static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv);
   6951 static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr);
   6952 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
   6953 
   6954 #define JIM_DICT_SUGAR 100
   6955 
   6956 
   6957 
   6958 
   6959 #define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
   6960 
   6961 #define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none")
   6962 
   6963 static int utf8_tounicode_case(const char *s, int *uc, int upper)
   6964 {
   6965     int l = utf8_tounicode(s, uc);
   6966     if (upper) {
   6967         *uc = utf8_upper(*uc);
   6968     }
   6969     return l;
   6970 }
   6971 
   6972 static Jim_Obj *JimPushInterpObjImpl(Jim_Obj **iop, Jim_Obj *no)
   6973 {
   6974     Jim_Obj *io = *iop;
   6975     Jim_IncrRefCount(no);
   6976     *iop = no;
   6977     return io;
   6978 }
   6979 
   6980 #define JimPushInterpObj(IO, NO) JimPushInterpObjImpl(&(IO), NO)
   6981 #define JimPopInterpObj(I, IO, SO) do { Jim_DecrRefCount(I, IO); IO = SO; } while (0)
   6982 
   6983 
   6984 #define JIM_CHARSET_SCAN 2
   6985 #define JIM_CHARSET_GLOB 0
   6986 
   6987 static const char *JimCharsetMatch(const char *pattern, int plen, int c, int flags)
   6988 {
   6989     int not = 0;
   6990     int pchar;
   6991     int match = 0;
   6992     int nocase = 0;
   6993     int n;
   6994 
   6995     if (flags & JIM_NOCASE) {
   6996         nocase++;
   6997         c = utf8_upper(c);
   6998     }
   6999 
   7000     if (flags & JIM_CHARSET_SCAN) {
   7001         if (*pattern == '^') {
   7002             not++;
   7003             pattern++;
   7004             plen--;
   7005         }
   7006 
   7007 
   7008         if (*pattern == ']') {
   7009             goto first;
   7010         }
   7011     }
   7012 
   7013     while (plen && *pattern != ']') {
   7014 
   7015         if (pattern[0] == '\\') {
   7016 first:
   7017             n = utf8_tounicode_case(pattern, &pchar, nocase);
   7018             pattern += n;
   7019             plen -= n;
   7020         }
   7021         else {
   7022 
   7023             int start;
   7024             int end;
   7025 
   7026             n = utf8_tounicode_case(pattern, &start, nocase);
   7027             pattern += n;
   7028             plen -= n;
   7029             if (pattern[0] == '-' && plen > 1) {
   7030 
   7031                 n = 1 + utf8_tounicode_case(pattern + 1, &end, nocase);
   7032                 pattern += n;
   7033                 plen -= n;
   7034 
   7035 
   7036                 if ((c >= start && c <= end) || (c >= end && c <= start)) {
   7037                     match = 1;
   7038                 }
   7039                 continue;
   7040             }
   7041             pchar = start;
   7042         }
   7043 
   7044         if (pchar == c) {
   7045             match = 1;
   7046         }
   7047     }
   7048     if (not) {
   7049         match = !match;
   7050     }
   7051 
   7052     return match ? pattern : NULL;
   7053 }
   7054 
   7055 
   7056 
   7057 static int JimGlobMatch(const char *pattern, int plen, const char *string, int slen, int nocase)
   7058 {
   7059     int c;
   7060     int pchar;
   7061     int n;
   7062     const char *p;
   7063     while (plen) {
   7064         switch (pattern[0]) {
   7065             case '*':
   7066                 while (pattern[1] == '*' && plen) {
   7067                     pattern++;
   7068                     plen--;
   7069                 }
   7070                 pattern++;
   7071                 plen--;
   7072                 if (!plen) {
   7073                     return 1;
   7074                 }
   7075                 while (slen) {
   7076 
   7077                     if (JimGlobMatch(pattern, plen, string, slen, nocase))
   7078                         return 1;
   7079                     n = utf8_tounicode(string, &c);
   7080                     string += n;
   7081                     slen -= n;
   7082                 }
   7083                 return 0;
   7084 
   7085             case '?':
   7086                 n = utf8_tounicode(string, &c);
   7087                 string += n;
   7088                 slen -= n;
   7089                 break;
   7090 
   7091             case '[': {
   7092                     n = utf8_tounicode(string, &c);
   7093                     string += n;
   7094                     slen -= n;
   7095                     p = JimCharsetMatch(pattern + 1, plen - 1, c, nocase ? JIM_NOCASE : 0);
   7096                     if (!p) {
   7097                         return 0;
   7098                     }
   7099                     plen -= p - pattern;
   7100                     pattern = p;
   7101 
   7102                     if (!plen) {
   7103 
   7104                         continue;
   7105                     }
   7106                     break;
   7107                 }
   7108             case '\\':
   7109                 if (pattern[1]) {
   7110                     pattern++;
   7111                     plen--;
   7112                 }
   7113 
   7114             default:
   7115                 n = utf8_tounicode_case(string, &c, nocase);
   7116                 string += n;
   7117                 slen -= n;
   7118                 utf8_tounicode_case(pattern, &pchar, nocase);
   7119                 if (pchar != c) {
   7120                     return 0;
   7121                 }
   7122                 break;
   7123         }
   7124         n = utf8_tounicode_case(pattern, &pchar, nocase);
   7125         pattern += n;
   7126         plen -= n;
   7127         if (!slen) {
   7128             while (*pattern == '*' && plen) {
   7129                 pattern++;
   7130                 plen--;
   7131             }
   7132             break;
   7133         }
   7134     }
   7135     if (!plen && !slen) {
   7136         return 1;
   7137     }
   7138     return 0;
   7139 }
   7140 
   7141 static int JimStringCompareUtf8(const char *s1, int l1, const char *s2, int l2, int nocase)
   7142 {
   7143     int minlen = l1;
   7144     if (l2 < l1) {
   7145         minlen = l2;
   7146     }
   7147     while (minlen) {
   7148         int c1, c2;
   7149         s1 += utf8_tounicode_case(s1, &c1, nocase);
   7150         s2 += utf8_tounicode_case(s2, &c2, nocase);
   7151         if (c1 != c2) {
   7152             return JimSign(c1 - c2);
   7153         }
   7154         minlen--;
   7155     }
   7156 
   7157     if (l1 < l2) {
   7158         return -1;
   7159     }
   7160     if (l1 > l2) {
   7161         return 1;
   7162     }
   7163     return 0;
   7164 }
   7165 
   7166 static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
   7167 {
   7168     int i;
   7169     int l1bytelen;
   7170 
   7171     if (!l1 || !l2 || l1 > l2) {
   7172         return -1;
   7173     }
   7174     if (idx < 0)
   7175         idx = 0;
   7176     s2 += utf8_index(s2, idx);
   7177 
   7178     l1bytelen = utf8_index(s1, l1);
   7179 
   7180     for (i = idx; i <= l2 - l1; i++) {
   7181         int c;
   7182         if (memcmp(s2, s1, l1bytelen) == 0) {
   7183             return i;
   7184         }
   7185         s2 += utf8_tounicode(s2, &c);
   7186     }
   7187     return -1;
   7188 }
   7189 
   7190 static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
   7191 {
   7192     const char *p;
   7193 
   7194     if (!l1 || !l2 || l1 > l2)
   7195         return -1;
   7196 
   7197 
   7198     for (p = s2 + l2 - 1; p != s2 - 1; p--) {
   7199         if (*p == *s1 && memcmp(s1, p, l1) == 0) {
   7200             return p - s2;
   7201         }
   7202     }
   7203     return -1;
   7204 }
   7205 
   7206 #ifdef JIM_UTF8
   7207 static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2)
   7208 {
   7209     int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2));
   7210     if (n > 0) {
   7211         n = utf8_strlen(s2, n);
   7212     }
   7213     return n;
   7214 }
   7215 #endif
   7216 
   7217 static int JimCheckConversion(const char *str, const char *endptr)
   7218 {
   7219     if (str[0] == '\0' || str == endptr) {
   7220         return JIM_ERR;
   7221     }
   7222 
   7223     if (endptr[0] != '\0') {
   7224         while (*endptr) {
   7225             if (!isspace(UCHAR(*endptr))) {
   7226                 return JIM_ERR;
   7227             }
   7228             endptr++;
   7229         }
   7230     }
   7231     return JIM_OK;
   7232 }
   7233 
   7234 static int JimNumberBase(const char *str, int *base, int *sign)
   7235 {
   7236     int i = 0;
   7237 
   7238     *base = 0;
   7239 
   7240     while (isspace(UCHAR(str[i]))) {
   7241         i++;
   7242     }
   7243 
   7244     if (str[i] == '-') {
   7245         *sign = -1;
   7246         i++;
   7247     }
   7248     else {
   7249         if (str[i] == '+') {
   7250             i++;
   7251         }
   7252         *sign = 1;
   7253     }
   7254 
   7255     if (str[i] != '0') {
   7256 
   7257         return 0;
   7258     }
   7259 
   7260 
   7261     switch (str[i + 1]) {
   7262         case 'x': case 'X': *base = 16; break;
   7263         case 'o': case 'O': *base = 8; break;
   7264         case 'b': case 'B': *base = 2; break;
   7265         case 'd': case 'D': *base = 10; break;
   7266         default: return 0;
   7267     }
   7268     i += 2;
   7269 
   7270     if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
   7271 
   7272         return i;
   7273     }
   7274 
   7275     *base = 0;
   7276     return 0;
   7277 }
   7278 
   7279 static long jim_strtol(const char *str, char **endptr)
   7280 {
   7281     int sign;
   7282     int base;
   7283     int i = JimNumberBase(str, &base, &sign);
   7284 
   7285     if (base != 0) {
   7286         long value = strtol(str + i, endptr, base);
   7287         if (endptr == NULL || *endptr != str + i) {
   7288             return value * sign;
   7289         }
   7290     }
   7291 
   7292 
   7293     return strtol(str, endptr, 10);
   7294 }
   7295 
   7296 
   7297 static jim_wide jim_strtoull(const char *str, char **endptr)
   7298 {
   7299 #ifdef HAVE_LONG_LONG
   7300     int sign;
   7301     int base;
   7302     int i = JimNumberBase(str, &base, &sign);
   7303 
   7304     if (base != 0) {
   7305         jim_wide value = strtoull(str + i, endptr, base);
   7306         if (endptr == NULL || *endptr != str + i) {
   7307             return value * sign;
   7308         }
   7309     }
   7310 
   7311 
   7312     return strtoull(str, endptr, 10);
   7313 #else
   7314     return (unsigned long)jim_strtol(str, endptr);
   7315 #endif
   7316 }
   7317 
   7318 int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
   7319 {
   7320     char *endptr;
   7321 
   7322     if (base) {
   7323         *widePtr = strtoull(str, &endptr, base);
   7324     }
   7325     else {
   7326         *widePtr = jim_strtoull(str, &endptr);
   7327     }
   7328 
   7329     return JimCheckConversion(str, endptr);
   7330 }
   7331 
   7332 int Jim_StringToDouble(const char *str, double *doublePtr)
   7333 {
   7334     char *endptr;
   7335 
   7336 
   7337     errno = 0;
   7338 
   7339     *doublePtr = strtod(str, &endptr);
   7340 
   7341     return JimCheckConversion(str, endptr);
   7342 }
   7343 
   7344 static jim_wide JimPowWide(jim_wide b, jim_wide e)
   7345 {
   7346     jim_wide res = 1;
   7347 
   7348 
   7349     if (b == 1) {
   7350 
   7351         return 1;
   7352     }
   7353     if (e < 0) {
   7354         if (b != -1) {
   7355             return 0;
   7356         }
   7357         e = -e;
   7358     }
   7359     while (e)
   7360     {
   7361         if (e & 1) {
   7362             res *= b;
   7363         }
   7364         e >>= 1;
   7365         b *= b;
   7366     }
   7367     return res;
   7368 }
   7369 
   7370 #ifdef JIM_DEBUG_PANIC
   7371 static void JimPanicDump(int condition, const char *fmt, ...)
   7372 {
   7373     va_list ap;
   7374 
   7375     if (!condition) {
   7376         return;
   7377     }
   7378 
   7379     va_start(ap, fmt);
   7380 
   7381     fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
   7382     vfprintf(stderr, fmt, ap);
   7383     fprintf(stderr, "\n\n");
   7384     va_end(ap);
   7385 
   7386 #if defined(HAVE_BACKTRACE)
   7387     {
   7388         void *array[40];
   7389         int size, i;
   7390         char **strings;
   7391 
   7392         size = backtrace(array, 40);
   7393         strings = backtrace_symbols(array, size);
   7394         for (i = 0; i < size; i++)
   7395             fprintf(stderr, "[backtrace] %s\n", strings[i]);
   7396         fprintf(stderr, "[backtrace] Include the above lines and the output\n");
   7397         fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n");
   7398     }
   7399 #endif
   7400 
   7401     exit(1);
   7402 }
   7403 #endif
   7404 
   7405 
   7406 void *JimDefaultAllocator(void *ptr, size_t size)
   7407 {
   7408     if (size == 0) {
   7409         free(ptr);
   7410         return NULL;
   7411     }
   7412     else {
   7413         void *p = realloc(ptr, size);
   7414         if( p==0 ){
   7415             fprintf(stderr,"Out of memory\n");
   7416             exit(1);
   7417         }
   7418         return p;
   7419     }
   7420 }
   7421 
   7422 void *(*Jim_Allocator)(void *ptr, size_t size) = JimDefaultAllocator;
   7423 
   7424 char *Jim_StrDup(const char *s)
   7425 {
   7426     return Jim_StrDupLen(s, strlen(s));
   7427 }
   7428 
   7429 char *Jim_StrDupLen(const char *s, int l)
   7430 {
   7431     char *copy = Jim_Alloc(l + 1);
   7432 
   7433     memcpy(copy, s, l);
   7434     copy[l] = 0;
   7435     return copy;
   7436 }
   7437 
   7438 
   7439 jim_wide Jim_GetTimeUsec(unsigned type)
   7440 {
   7441     long long now;
   7442     struct timeval tv;
   7443 
   7444 #if defined(HAVE_CLOCK_GETTIME)
   7445     struct timespec ts;
   7446 
   7447     if (clock_gettime(type, &ts) == 0) {
   7448         now = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
   7449     }
   7450     else
   7451 #endif
   7452     {
   7453         gettimeofday(&tv, NULL);
   7454 
   7455         now = tv.tv_sec * 1000000LL + tv.tv_usec;
   7456     }
   7457 
   7458     return now;
   7459 }
   7460 
   7461 
   7462 
   7463 
   7464 
   7465 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
   7466 static unsigned int JimHashTableNextPower(unsigned int size);
   7467 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
   7468 
   7469 
   7470 
   7471 
   7472 unsigned int Jim_IntHashFunction(unsigned int key)
   7473 {
   7474     key += ~(key << 15);
   7475     key ^= (key >> 10);
   7476     key += (key << 3);
   7477     key ^= (key >> 6);
   7478     key += ~(key << 11);
   7479     key ^= (key >> 16);
   7480     return key;
   7481 }
   7482 
   7483 
   7484 unsigned int Jim_GenHashFunction(const unsigned char *string, int length)
   7485 {
   7486     unsigned result = 0;
   7487     string += length;
   7488     while (length--) {
   7489         result += (result << 3) + (unsigned char)(*--string);
   7490     }
   7491     return result;
   7492 }
   7493 
   7494 
   7495 
   7496 static void JimResetHashTable(Jim_HashTable *ht)
   7497 {
   7498     ht->table = NULL;
   7499     ht->size = 0;
   7500     ht->sizemask = 0;
   7501     ht->used = 0;
   7502     ht->collisions = 0;
   7503 #ifdef JIM_RANDOMISE_HASH
   7504     ht->uniq = (rand() ^ time(NULL) ^ clock());
   7505 #else
   7506     ht->uniq = 0;
   7507 #endif
   7508 }
   7509 
   7510 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
   7511 {
   7512     iter->ht = ht;
   7513     iter->index = -1;
   7514     iter->entry = NULL;
   7515     iter->nextEntry = NULL;
   7516 }
   7517 
   7518 
   7519 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
   7520 {
   7521     JimResetHashTable(ht);
   7522     ht->type = type;
   7523     ht->privdata = privDataPtr;
   7524     return JIM_OK;
   7525 }
   7526 
   7527 
   7528 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
   7529 {
   7530     Jim_HashTable n;
   7531     unsigned int realsize = JimHashTableNextPower(size), i;
   7532 
   7533      if (size <= ht->used)
   7534         return;
   7535 
   7536     Jim_InitHashTable(&n, ht->type, ht->privdata);
   7537     n.size = realsize;
   7538     n.sizemask = realsize - 1;
   7539     n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
   7540 
   7541     n.uniq = ht->uniq;
   7542 
   7543 
   7544     memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
   7545 
   7546     n.used = ht->used;
   7547     for (i = 0; ht->used > 0; i++) {
   7548         Jim_HashEntry *he, *nextHe;
   7549 
   7550         if (ht->table[i] == NULL)
   7551             continue;
   7552 
   7553 
   7554         he = ht->table[i];
   7555         while (he) {
   7556             unsigned int h;
   7557 
   7558             nextHe = he->next;
   7559 
   7560             h = Jim_HashKey(ht, he->key) & n.sizemask;
   7561             he->next = n.table[h];
   7562             n.table[h] = he;
   7563             ht->used--;
   7564 
   7565             he = nextHe;
   7566         }
   7567     }
   7568     assert(ht->used == 0);
   7569     Jim_Free(ht->table);
   7570 
   7571 
   7572     *ht = n;
   7573 }
   7574 
   7575 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
   7576 {
   7577     Jim_HashEntry *entry = JimInsertHashEntry(ht, key, 0);;
   7578     if (entry == NULL)
   7579         return JIM_ERR;
   7580 
   7581 
   7582     Jim_SetHashKey(ht, entry, key);
   7583     Jim_SetHashVal(ht, entry, val);
   7584     return JIM_OK;
   7585 }
   7586 
   7587 
   7588 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
   7589 {
   7590     int existed;
   7591     Jim_HashEntry *entry;
   7592 
   7593     entry = JimInsertHashEntry(ht, key, 1);
   7594     if (entry->key) {
   7595         if (ht->type->valDestructor && ht->type->valDup) {
   7596             void *newval = ht->type->valDup(ht->privdata, val);
   7597             ht->type->valDestructor(ht->privdata, entry->u.val);
   7598             entry->u.val = newval;
   7599         }
   7600         else {
   7601             Jim_FreeEntryVal(ht, entry);
   7602             Jim_SetHashVal(ht, entry, val);
   7603         }
   7604         existed = 1;
   7605     }
   7606     else {
   7607 
   7608         Jim_SetHashKey(ht, entry, key);
   7609         Jim_SetHashVal(ht, entry, val);
   7610         existed = 0;
   7611     }
   7612 
   7613     return existed;
   7614 }
   7615 
   7616 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
   7617 {
   7618     if (ht->used) {
   7619         unsigned int h = Jim_HashKey(ht, key) & ht->sizemask;
   7620         Jim_HashEntry *prevHe = NULL;
   7621         Jim_HashEntry *he = ht->table[h];
   7622 
   7623         while (he) {
   7624             if (Jim_CompareHashKeys(ht, key, he->key)) {
   7625 
   7626                 if (prevHe)
   7627                     prevHe->next = he->next;
   7628                 else
   7629                     ht->table[h] = he->next;
   7630                 ht->used--;
   7631                 Jim_FreeEntryKey(ht, he);
   7632                 Jim_FreeEntryVal(ht, he);
   7633                 Jim_Free(he);
   7634                 return JIM_OK;
   7635             }
   7636             prevHe = he;
   7637             he = he->next;
   7638         }
   7639     }
   7640 
   7641     return JIM_ERR;
   7642 }
   7643 
   7644 void Jim_ClearHashTable(Jim_HashTable *ht)
   7645 {
   7646     unsigned int i;
   7647 
   7648 
   7649     for (i = 0; ht->used > 0; i++) {
   7650         Jim_HashEntry *he, *nextHe;
   7651 
   7652         he = ht->table[i];
   7653         while (he) {
   7654             nextHe = he->next;
   7655             Jim_FreeEntryKey(ht, he);
   7656             Jim_FreeEntryVal(ht, he);
   7657             Jim_Free(he);
   7658             ht->used--;
   7659             he = nextHe;
   7660         }
   7661         ht->table[i] = NULL;
   7662     }
   7663 }
   7664 
   7665 int Jim_FreeHashTable(Jim_HashTable *ht)
   7666 {
   7667     Jim_ClearHashTable(ht);
   7668 
   7669     Jim_Free(ht->table);
   7670 
   7671     JimResetHashTable(ht);
   7672     return JIM_OK;
   7673 }
   7674 
   7675 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
   7676 {
   7677     Jim_HashEntry *he;
   7678     unsigned int h;
   7679 
   7680     if (ht->used == 0)
   7681         return NULL;
   7682     h = Jim_HashKey(ht, key) & ht->sizemask;
   7683     he = ht->table[h];
   7684     while (he) {
   7685         if (Jim_CompareHashKeys(ht, key, he->key))
   7686             return he;
   7687         he = he->next;
   7688     }
   7689     return NULL;
   7690 }
   7691 
   7692 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
   7693 {
   7694     Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
   7695     JimInitHashTableIterator(ht, iter);
   7696     return iter;
   7697 }
   7698 
   7699 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
   7700 {
   7701     while (1) {
   7702         if (iter->entry == NULL) {
   7703             iter->index++;
   7704             if (iter->index >= (signed)iter->ht->size)
   7705                 break;
   7706             iter->entry = iter->ht->table[iter->index];
   7707         }
   7708         else {
   7709             iter->entry = iter->nextEntry;
   7710         }
   7711         if (iter->entry) {
   7712             iter->nextEntry = iter->entry->next;
   7713             return iter->entry;
   7714         }
   7715     }
   7716     return NULL;
   7717 }
   7718 
   7719 
   7720 
   7721 
   7722 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
   7723 {
   7724     if (ht->size == 0)
   7725         Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
   7726     if (ht->size == ht->used)
   7727         Jim_ExpandHashTable(ht, ht->size * 2);
   7728 }
   7729 
   7730 
   7731 static unsigned int JimHashTableNextPower(unsigned int size)
   7732 {
   7733     unsigned int i = JIM_HT_INITIAL_SIZE;
   7734 
   7735     if (size >= 2147483648U)
   7736         return 2147483648U;
   7737     while (1) {
   7738         if (i >= size)
   7739             return i;
   7740         i *= 2;
   7741     }
   7742 }
   7743 
   7744 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
   7745 {
   7746     unsigned int h;
   7747     Jim_HashEntry *he;
   7748 
   7749 
   7750     JimExpandHashTableIfNeeded(ht);
   7751 
   7752 
   7753     h = Jim_HashKey(ht, key) & ht->sizemask;
   7754 
   7755     he = ht->table[h];
   7756     while (he) {
   7757         if (Jim_CompareHashKeys(ht, key, he->key))
   7758             return replace ? he : NULL;
   7759         he = he->next;
   7760     }
   7761 
   7762 
   7763     he = Jim_Alloc(sizeof(*he));
   7764     he->next = ht->table[h];
   7765     ht->table[h] = he;
   7766     ht->used++;
   7767     he->key = NULL;
   7768 
   7769     return he;
   7770 }
   7771 
   7772 
   7773 
   7774 static unsigned int JimStringCopyHTHashFunction(const void *key)
   7775 {
   7776     return Jim_GenHashFunction(key, strlen(key));
   7777 }
   7778 
   7779 static void *JimStringCopyHTDup(void *privdata, const void *key)
   7780 {
   7781     return Jim_StrDup(key);
   7782 }
   7783 
   7784 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
   7785 {
   7786     return strcmp(key1, key2) == 0;
   7787 }
   7788 
   7789 static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
   7790 {
   7791     Jim_Free(key);
   7792 }
   7793 
   7794 static const Jim_HashTableType JimPackageHashTableType = {
   7795     JimStringCopyHTHashFunction,
   7796     JimStringCopyHTDup,
   7797     NULL,
   7798     JimStringCopyHTKeyCompare,
   7799     JimStringCopyHTKeyDestructor,
   7800     NULL
   7801 };
   7802 
   7803 typedef struct AssocDataValue
   7804 {
   7805     Jim_InterpDeleteProc *delProc;
   7806     void *data;
   7807 } AssocDataValue;
   7808 
   7809 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
   7810 {
   7811     AssocDataValue *assocPtr = (AssocDataValue *) data;
   7812 
   7813     if (assocPtr->delProc != NULL)
   7814         assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
   7815     Jim_Free(data);
   7816 }
   7817 
   7818 static const Jim_HashTableType JimAssocDataHashTableType = {
   7819     JimStringCopyHTHashFunction,
   7820     JimStringCopyHTDup,
   7821     NULL,
   7822     JimStringCopyHTKeyCompare,
   7823     JimStringCopyHTKeyDestructor,
   7824     JimAssocDataHashTableValueDestructor
   7825 };
   7826 
   7827 void Jim_InitStack(Jim_Stack *stack)
   7828 {
   7829     stack->len = 0;
   7830     stack->maxlen = 0;
   7831     stack->vector = NULL;
   7832 }
   7833 
   7834 void Jim_FreeStack(Jim_Stack *stack)
   7835 {
   7836     Jim_Free(stack->vector);
   7837 }
   7838 
   7839 int Jim_StackLen(Jim_Stack *stack)
   7840 {
   7841     return stack->len;
   7842 }
   7843 
   7844 void Jim_StackPush(Jim_Stack *stack, void *element)
   7845 {
   7846     int neededLen = stack->len + 1;
   7847 
   7848     if (neededLen > stack->maxlen) {
   7849         stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
   7850         stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
   7851     }
   7852     stack->vector[stack->len] = element;
   7853     stack->len++;
   7854 }
   7855 
   7856 void *Jim_StackPop(Jim_Stack *stack)
   7857 {
   7858     if (stack->len == 0)
   7859         return NULL;
   7860     stack->len--;
   7861     return stack->vector[stack->len];
   7862 }
   7863 
   7864 void *Jim_StackPeek(Jim_Stack *stack)
   7865 {
   7866     if (stack->len == 0)
   7867         return NULL;
   7868     return stack->vector[stack->len - 1];
   7869 }
   7870 
   7871 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
   7872 {
   7873     int i;
   7874 
   7875     for (i = 0; i < stack->len; i++)
   7876         freeFunc(stack->vector[i]);
   7877 }
   7878 
   7879 
   7880 
   7881 #define JIM_TT_NONE    0
   7882 #define JIM_TT_STR     1
   7883 #define JIM_TT_ESC     2
   7884 #define JIM_TT_VAR     3
   7885 #define JIM_TT_DICTSUGAR   4
   7886 #define JIM_TT_CMD     5
   7887 
   7888 #define JIM_TT_SEP     6
   7889 #define JIM_TT_EOL     7
   7890 #define JIM_TT_EOF     8
   7891 
   7892 #define JIM_TT_LINE    9
   7893 #define JIM_TT_WORD   10
   7894 
   7895 
   7896 #define JIM_TT_SUBEXPR_START  11
   7897 #define JIM_TT_SUBEXPR_END    12
   7898 #define JIM_TT_SUBEXPR_COMMA  13
   7899 #define JIM_TT_EXPR_INT       14
   7900 #define JIM_TT_EXPR_DOUBLE    15
   7901 #define JIM_TT_EXPR_BOOLEAN   16
   7902 
   7903 #define JIM_TT_EXPRSUGAR      17
   7904 
   7905 
   7906 #define JIM_TT_EXPR_OP        20
   7907 
   7908 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
   7909 
   7910 #define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA)
   7911 
   7912 #define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP)
   7913 
   7914 struct JimParseMissing {
   7915     int ch;
   7916     int line;
   7917 };
   7918 
   7919 struct JimParserCtx
   7920 {
   7921     const char *p;
   7922     int len;
   7923     int linenr;
   7924     const char *tstart;
   7925     const char *tend;
   7926     int tline;
   7927     int tt;
   7928     int eof;
   7929     int inquote;
   7930     int comment;
   7931     struct JimParseMissing missing;
   7932     const char *errmsg;
   7933 };
   7934 
   7935 static int JimParseScript(struct JimParserCtx *pc);
   7936 static int JimParseSep(struct JimParserCtx *pc);
   7937 static int JimParseEol(struct JimParserCtx *pc);
   7938 static int JimParseCmd(struct JimParserCtx *pc);
   7939 static int JimParseQuote(struct JimParserCtx *pc);
   7940 static int JimParseVar(struct JimParserCtx *pc);
   7941 static int JimParseBrace(struct JimParserCtx *pc);
   7942 static int JimParseStr(struct JimParserCtx *pc);
   7943 static int JimParseComment(struct JimParserCtx *pc);
   7944 static void JimParseSubCmd(struct JimParserCtx *pc);
   7945 static int JimParseSubQuote(struct JimParserCtx *pc);
   7946 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
   7947 
   7948 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
   7949 {
   7950     pc->p = prg;
   7951     pc->len = len;
   7952     pc->tstart = NULL;
   7953     pc->tend = NULL;
   7954     pc->tline = 0;
   7955     pc->tt = JIM_TT_NONE;
   7956     pc->eof = 0;
   7957     pc->inquote = 0;
   7958     pc->linenr = linenr;
   7959     pc->comment = 1;
   7960     pc->missing.ch = ' ';
   7961     pc->missing.line = linenr;
   7962 }
   7963 
   7964 static int JimParseScript(struct JimParserCtx *pc)
   7965 {
   7966     while (1) {
   7967         if (!pc->len) {
   7968             pc->tstart = pc->p;
   7969             pc->tend = pc->p - 1;
   7970             pc->tline = pc->linenr;
   7971             pc->tt = JIM_TT_EOL;
   7972             if (pc->inquote) {
   7973                 pc->missing.ch = '"';
   7974             }
   7975             pc->eof = 1;
   7976             return JIM_OK;
   7977         }
   7978         switch (*(pc->p)) {
   7979             case '\\':
   7980                 if (*(pc->p + 1) == '\n' && !pc->inquote) {
   7981                     return JimParseSep(pc);
   7982                 }
   7983                 pc->comment = 0;
   7984                 return JimParseStr(pc);
   7985             case ' ':
   7986             case '\t':
   7987             case '\r':
   7988             case '\f':
   7989                 if (!pc->inquote)
   7990                     return JimParseSep(pc);
   7991                 pc->comment = 0;
   7992                 return JimParseStr(pc);
   7993             case '\n':
   7994             case ';':
   7995                 pc->comment = 1;
   7996                 if (!pc->inquote)
   7997                     return JimParseEol(pc);
   7998                 return JimParseStr(pc);
   7999             case '[':
   8000                 pc->comment = 0;
   8001                 return JimParseCmd(pc);
   8002             case '$':
   8003                 pc->comment = 0;
   8004                 if (JimParseVar(pc) == JIM_ERR) {
   8005 
   8006                     pc->tstart = pc->tend = pc->p++;
   8007                     pc->len--;
   8008                     pc->tt = JIM_TT_ESC;
   8009                 }
   8010                 return JIM_OK;
   8011             case '#':
   8012                 if (pc->comment) {
   8013                     JimParseComment(pc);
   8014                     continue;
   8015                 }
   8016                 return JimParseStr(pc);
   8017             default:
   8018                 pc->comment = 0;
   8019                 return JimParseStr(pc);
   8020         }
   8021         return JIM_OK;
   8022     }
   8023 }
   8024 
   8025 static int JimParseSep(struct JimParserCtx *pc)
   8026 {
   8027     pc->tstart = pc->p;
   8028     pc->tline = pc->linenr;
   8029     while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
   8030         if (*pc->p == '\n') {
   8031             break;
   8032         }
   8033         if (*pc->p == '\\') {
   8034             pc->p++;
   8035             pc->len--;
   8036             pc->linenr++;
   8037         }
   8038         pc->p++;
   8039         pc->len--;
   8040     }
   8041     pc->tend = pc->p - 1;
   8042     pc->tt = JIM_TT_SEP;
   8043     return JIM_OK;
   8044 }
   8045 
   8046 static int JimParseEol(struct JimParserCtx *pc)
   8047 {
   8048     pc->tstart = pc->p;
   8049     pc->tline = pc->linenr;
   8050     while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
   8051         if (*pc->p == '\n')
   8052             pc->linenr++;
   8053         pc->p++;
   8054         pc->len--;
   8055     }
   8056     pc->tend = pc->p - 1;
   8057     pc->tt = JIM_TT_EOL;
   8058     return JIM_OK;
   8059 }
   8060 
   8061 
   8062 static void JimParseSubBrace(struct JimParserCtx *pc)
   8063 {
   8064     int level = 1;
   8065 
   8066 
   8067     pc->p++;
   8068     pc->len--;
   8069     while (pc->len) {
   8070         switch (*pc->p) {
   8071             case '\\':
   8072                 if (pc->len > 1) {
   8073                     if (*++pc->p == '\n') {
   8074                         pc->linenr++;
   8075                     }
   8076                     pc->len--;
   8077                 }
   8078                 break;
   8079 
   8080             case '{':
   8081                 level++;
   8082                 break;
   8083 
   8084             case '}':
   8085                 if (--level == 0) {
   8086                     pc->tend = pc->p - 1;
   8087                     pc->p++;
   8088                     pc->len--;
   8089                     return;
   8090                 }
   8091                 break;
   8092 
   8093             case '\n':
   8094                 pc->linenr++;
   8095                 break;
   8096         }
   8097         pc->p++;
   8098         pc->len--;
   8099     }
   8100     pc->missing.ch = '{';
   8101     pc->missing.line = pc->tline;
   8102     pc->tend = pc->p - 1;
   8103 }
   8104 
   8105 static int JimParseSubQuote(struct JimParserCtx *pc)
   8106 {
   8107     int tt = JIM_TT_STR;
   8108     int line = pc->tline;
   8109 
   8110 
   8111     pc->p++;
   8112     pc->len--;
   8113     while (pc->len) {
   8114         switch (*pc->p) {
   8115             case '\\':
   8116                 if (pc->len > 1) {
   8117                     if (*++pc->p == '\n') {
   8118                         pc->linenr++;
   8119                     }
   8120                     pc->len--;
   8121                     tt = JIM_TT_ESC;
   8122                 }
   8123                 break;
   8124 
   8125             case '"':
   8126                 pc->tend = pc->p - 1;
   8127                 pc->p++;
   8128                 pc->len--;
   8129                 return tt;
   8130 
   8131             case '[':
   8132                 JimParseSubCmd(pc);
   8133                 tt = JIM_TT_ESC;
   8134                 continue;
   8135 
   8136             case '\n':
   8137                 pc->linenr++;
   8138                 break;
   8139 
   8140             case '$':
   8141                 tt = JIM_TT_ESC;
   8142                 break;
   8143         }
   8144         pc->p++;
   8145         pc->len--;
   8146     }
   8147     pc->missing.ch = '"';
   8148     pc->missing.line = line;
   8149     pc->tend = pc->p - 1;
   8150     return tt;
   8151 }
   8152 
   8153 static void JimParseSubCmd(struct JimParserCtx *pc)
   8154 {
   8155     int level = 1;
   8156     int startofword = 1;
   8157     int line = pc->tline;
   8158 
   8159 
   8160     pc->p++;
   8161     pc->len--;
   8162     while (pc->len) {
   8163         switch (*pc->p) {
   8164             case '\\':
   8165                 if (pc->len > 1) {
   8166                     if (*++pc->p == '\n') {
   8167                         pc->linenr++;
   8168                     }
   8169                     pc->len--;
   8170                 }
   8171                 break;
   8172 
   8173             case '[':
   8174                 level++;
   8175                 break;
   8176 
   8177             case ']':
   8178                 if (--level == 0) {
   8179                     pc->tend = pc->p - 1;
   8180                     pc->p++;
   8181                     pc->len--;
   8182                     return;
   8183                 }
   8184                 break;
   8185 
   8186             case '"':
   8187                 if (startofword) {
   8188                     JimParseSubQuote(pc);
   8189                     if (pc->missing.ch == '"') {
   8190                         return;
   8191                     }
   8192                     continue;
   8193                 }
   8194                 break;
   8195 
   8196             case '{':
   8197                 JimParseSubBrace(pc);
   8198                 startofword = 0;
   8199                 continue;
   8200 
   8201             case '\n':
   8202                 pc->linenr++;
   8203                 break;
   8204         }
   8205         startofword = isspace(UCHAR(*pc->p));
   8206         pc->p++;
   8207         pc->len--;
   8208     }
   8209     pc->missing.ch = '[';
   8210     pc->missing.line = line;
   8211     pc->tend = pc->p - 1;
   8212 }
   8213 
   8214 static int JimParseBrace(struct JimParserCtx *pc)
   8215 {
   8216     pc->tstart = pc->p + 1;
   8217     pc->tline = pc->linenr;
   8218     pc->tt = JIM_TT_STR;
   8219     JimParseSubBrace(pc);
   8220     return JIM_OK;
   8221 }
   8222 
   8223 static int JimParseCmd(struct JimParserCtx *pc)
   8224 {
   8225     pc->tstart = pc->p + 1;
   8226     pc->tline = pc->linenr;
   8227     pc->tt = JIM_TT_CMD;
   8228     JimParseSubCmd(pc);
   8229     return JIM_OK;
   8230 }
   8231 
   8232 static int JimParseQuote(struct JimParserCtx *pc)
   8233 {
   8234     pc->tstart = pc->p + 1;
   8235     pc->tline = pc->linenr;
   8236     pc->tt = JimParseSubQuote(pc);
   8237     return JIM_OK;
   8238 }
   8239 
   8240 static int JimParseVar(struct JimParserCtx *pc)
   8241 {
   8242 
   8243     pc->p++;
   8244     pc->len--;
   8245 
   8246 #ifdef EXPRSUGAR_BRACKET
   8247     if (*pc->p == '[') {
   8248 
   8249         JimParseCmd(pc);
   8250         pc->tt = JIM_TT_EXPRSUGAR;
   8251         return JIM_OK;
   8252     }
   8253 #endif
   8254 
   8255     pc->tstart = pc->p;
   8256     pc->tt = JIM_TT_VAR;
   8257     pc->tline = pc->linenr;
   8258 
   8259     if (*pc->p == '{') {
   8260         pc->tstart = ++pc->p;
   8261         pc->len--;
   8262 
   8263         while (pc->len && *pc->p != '}') {
   8264             if (*pc->p == '\n') {
   8265                 pc->linenr++;
   8266             }
   8267             pc->p++;
   8268             pc->len--;
   8269         }
   8270         pc->tend = pc->p - 1;
   8271         if (pc->len) {
   8272             pc->p++;
   8273             pc->len--;
   8274         }
   8275     }
   8276     else {
   8277         while (1) {
   8278 
   8279             if (pc->p[0] == ':' && pc->p[1] == ':') {
   8280                 while (*pc->p == ':') {
   8281                     pc->p++;
   8282                     pc->len--;
   8283                 }
   8284                 continue;
   8285             }
   8286             if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
   8287                 pc->p++;
   8288                 pc->len--;
   8289                 continue;
   8290             }
   8291             break;
   8292         }
   8293 
   8294         if (*pc->p == '(') {
   8295             int count = 1;
   8296             const char *paren = NULL;
   8297 
   8298             pc->tt = JIM_TT_DICTSUGAR;
   8299 
   8300             while (count && pc->len) {
   8301                 pc->p++;
   8302                 pc->len--;
   8303                 if (*pc->p == '\\' && pc->len >= 1) {
   8304                     pc->p++;
   8305                     pc->len--;
   8306                 }
   8307                 else if (*pc->p == '(') {
   8308                     count++;
   8309                 }
   8310                 else if (*pc->p == ')') {
   8311                     paren = pc->p;
   8312                     count--;
   8313                 }
   8314             }
   8315             if (count == 0) {
   8316                 pc->p++;
   8317                 pc->len--;
   8318             }
   8319             else if (paren) {
   8320 
   8321                 paren++;
   8322                 pc->len += (pc->p - paren);
   8323                 pc->p = paren;
   8324             }
   8325 #ifndef EXPRSUGAR_BRACKET
   8326             if (*pc->tstart == '(') {
   8327                 pc->tt = JIM_TT_EXPRSUGAR;
   8328             }
   8329 #endif
   8330         }
   8331         pc->tend = pc->p - 1;
   8332     }
   8333     if (pc->tstart == pc->p) {
   8334         pc->p--;
   8335         pc->len++;
   8336         return JIM_ERR;
   8337     }
   8338     return JIM_OK;
   8339 }
   8340 
   8341 static int JimParseStr(struct JimParserCtx *pc)
   8342 {
   8343     if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
   8344         pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
   8345 
   8346         if (*pc->p == '{') {
   8347             return JimParseBrace(pc);
   8348         }
   8349         if (*pc->p == '"') {
   8350             pc->inquote = 1;
   8351             pc->p++;
   8352             pc->len--;
   8353 
   8354             pc->missing.line = pc->tline;
   8355         }
   8356     }
   8357     pc->tstart = pc->p;
   8358     pc->tline = pc->linenr;
   8359     while (1) {
   8360         if (pc->len == 0) {
   8361             if (pc->inquote) {
   8362                 pc->missing.ch = '"';
   8363             }
   8364             pc->tend = pc->p - 1;
   8365             pc->tt = JIM_TT_ESC;
   8366             return JIM_OK;
   8367         }
   8368         switch (*pc->p) {
   8369             case '\\':
   8370                 if (!pc->inquote && *(pc->p + 1) == '\n') {
   8371                     pc->tend = pc->p - 1;
   8372                     pc->tt = JIM_TT_ESC;
   8373                     return JIM_OK;
   8374                 }
   8375                 if (pc->len >= 2) {
   8376                     if (*(pc->p + 1) == '\n') {
   8377                         pc->linenr++;
   8378                     }
   8379                     pc->p++;
   8380                     pc->len--;
   8381                 }
   8382                 else if (pc->len == 1) {
   8383 
   8384                     pc->missing.ch = '\\';
   8385                 }
   8386                 break;
   8387             case '(':
   8388 
   8389                 if (pc->len > 1 && pc->p[1] != '$') {
   8390                     break;
   8391                 }
   8392 
   8393             case ')':
   8394 
   8395                 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
   8396                     if (pc->p == pc->tstart) {
   8397 
   8398                         pc->p++;
   8399                         pc->len--;
   8400                     }
   8401                     pc->tend = pc->p - 1;
   8402                     pc->tt = JIM_TT_ESC;
   8403                     return JIM_OK;
   8404                 }
   8405                 break;
   8406 
   8407             case '$':
   8408             case '[':
   8409                 pc->tend = pc->p - 1;
   8410                 pc->tt = JIM_TT_ESC;
   8411                 return JIM_OK;
   8412             case ' ':
   8413             case '\t':
   8414             case '\n':
   8415             case '\r':
   8416             case '\f':
   8417             case ';':
   8418                 if (!pc->inquote) {
   8419                     pc->tend = pc->p - 1;
   8420                     pc->tt = JIM_TT_ESC;
   8421                     return JIM_OK;
   8422                 }
   8423                 else if (*pc->p == '\n') {
   8424                     pc->linenr++;
   8425                 }
   8426                 break;
   8427             case '"':
   8428                 if (pc->inquote) {
   8429                     pc->tend = pc->p - 1;
   8430                     pc->tt = JIM_TT_ESC;
   8431                     pc->p++;
   8432                     pc->len--;
   8433                     pc->inquote = 0;
   8434                     return JIM_OK;
   8435                 }
   8436                 break;
   8437         }
   8438         pc->p++;
   8439         pc->len--;
   8440     }
   8441     return JIM_OK;
   8442 }
   8443 
   8444 static int JimParseComment(struct JimParserCtx *pc)
   8445 {
   8446     while (*pc->p) {
   8447         if (*pc->p == '\\') {
   8448             pc->p++;
   8449             pc->len--;
   8450             if (pc->len == 0) {
   8451                 pc->missing.ch = '\\';
   8452                 return JIM_OK;
   8453             }
   8454             if (*pc->p == '\n') {
   8455                 pc->linenr++;
   8456             }
   8457         }
   8458         else if (*pc->p == '\n') {
   8459             pc->p++;
   8460             pc->len--;
   8461             pc->linenr++;
   8462             break;
   8463         }
   8464         pc->p++;
   8465         pc->len--;
   8466     }
   8467     return JIM_OK;
   8468 }
   8469 
   8470 
   8471 static int xdigitval(int c)
   8472 {
   8473     if (c >= '0' && c <= '9')
   8474         return c - '0';
   8475     if (c >= 'a' && c <= 'f')
   8476         return c - 'a' + 10;
   8477     if (c >= 'A' && c <= 'F')
   8478         return c - 'A' + 10;
   8479     return -1;
   8480 }
   8481 
   8482 static int odigitval(int c)
   8483 {
   8484     if (c >= '0' && c <= '7')
   8485         return c - '0';
   8486     return -1;
   8487 }
   8488 
   8489 static int JimEscape(char *dest, const char *s, int slen)
   8490 {
   8491     char *p = dest;
   8492     int i, len;
   8493 
   8494     for (i = 0; i < slen; i++) {
   8495         switch (s[i]) {
   8496             case '\\':
   8497                 switch (s[i + 1]) {
   8498                     case 'a':
   8499                         *p++ = 0x7;
   8500                         i++;
   8501                         break;
   8502                     case 'b':
   8503                         *p++ = 0x8;
   8504                         i++;
   8505                         break;
   8506                     case 'f':
   8507                         *p++ = 0xc;
   8508                         i++;
   8509                         break;
   8510                     case 'n':
   8511                         *p++ = 0xa;
   8512                         i++;
   8513                         break;
   8514                     case 'r':
   8515                         *p++ = 0xd;
   8516                         i++;
   8517                         break;
   8518                     case 't':
   8519                         *p++ = 0x9;
   8520                         i++;
   8521                         break;
   8522                     case 'u':
   8523                     case 'U':
   8524                     case 'x':
   8525                         {
   8526                             unsigned val = 0;
   8527                             int k;
   8528                             int maxchars = 2;
   8529 
   8530                             i++;
   8531 
   8532                             if (s[i] == 'U') {
   8533                                 maxchars = 8;
   8534                             }
   8535                             else if (s[i] == 'u') {
   8536                                 if (s[i + 1] == '{') {
   8537                                     maxchars = 6;
   8538                                     i++;
   8539                                 }
   8540                                 else {
   8541                                     maxchars = 4;
   8542                                 }
   8543                             }
   8544 
   8545                             for (k = 0; k < maxchars; k++) {
   8546                                 int c = xdigitval(s[i + k + 1]);
   8547                                 if (c == -1) {
   8548                                     break;
   8549                                 }
   8550                                 val = (val << 4) | c;
   8551                             }
   8552 
   8553                             if (s[i] == '{') {
   8554                                 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
   8555 
   8556                                     i--;
   8557                                     k = 0;
   8558                                 }
   8559                                 else {
   8560 
   8561                                     k++;
   8562                                 }
   8563                             }
   8564                             if (k) {
   8565 
   8566                                 if (s[i] == 'x') {
   8567                                     *p++ = val;
   8568                                 }
   8569                                 else {
   8570                                     p += utf8_fromunicode(p, val);
   8571                                 }
   8572                                 i += k;
   8573                                 break;
   8574                             }
   8575 
   8576                             *p++ = s[i];
   8577                         }
   8578                         break;
   8579                     case 'v':
   8580                         *p++ = 0xb;
   8581                         i++;
   8582                         break;
   8583                     case '\0':
   8584                         *p++ = '\\';
   8585                         i++;
   8586                         break;
   8587                     case '\n':
   8588 
   8589                         *p++ = ' ';
   8590                         do {
   8591                             i++;
   8592                         } while (s[i + 1] == ' ' || s[i + 1] == '\t');
   8593                         break;
   8594                     case '0':
   8595                     case '1':
   8596                     case '2':
   8597                     case '3':
   8598                     case '4':
   8599                     case '5':
   8600                     case '6':
   8601                     case '7':
   8602 
   8603                         {
   8604                             int val = 0;
   8605                             int c = odigitval(s[i + 1]);
   8606 
   8607                             val = c;
   8608                             c = odigitval(s[i + 2]);
   8609                             if (c == -1) {
   8610                                 *p++ = val;
   8611                                 i++;
   8612                                 break;
   8613                             }
   8614                             val = (val * 8) + c;
   8615                             c = odigitval(s[i + 3]);
   8616                             if (c == -1) {
   8617                                 *p++ = val;
   8618                                 i += 2;
   8619                                 break;
   8620                             }
   8621                             val = (val * 8) + c;
   8622                             *p++ = val;
   8623                             i += 3;
   8624                         }
   8625                         break;
   8626                     default:
   8627                         *p++ = s[i + 1];
   8628                         i++;
   8629                         break;
   8630                 }
   8631                 break;
   8632             default:
   8633                 *p++ = s[i];
   8634                 break;
   8635         }
   8636     }
   8637     len = p - dest;
   8638     *p = '\0';
   8639     return len;
   8640 }
   8641 
   8642 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
   8643 {
   8644     const char *start, *end;
   8645     char *token;
   8646     int len;
   8647 
   8648     start = pc->tstart;
   8649     end = pc->tend;
   8650     len = (end - start) + 1;
   8651     if (len < 0) {
   8652         len = 0;
   8653     }
   8654     token = Jim_Alloc(len + 1);
   8655     if (pc->tt != JIM_TT_ESC) {
   8656 
   8657         memcpy(token, start, len);
   8658         token[len] = '\0';
   8659     }
   8660     else {
   8661 
   8662         len = JimEscape(token, start, len);
   8663     }
   8664 
   8665     return Jim_NewStringObjNoAlloc(interp, token, len);
   8666 }
   8667 
   8668 static int JimParseListSep(struct JimParserCtx *pc);
   8669 static int JimParseListStr(struct JimParserCtx *pc);
   8670 static int JimParseListQuote(struct JimParserCtx *pc);
   8671 
   8672 static int JimParseList(struct JimParserCtx *pc)
   8673 {
   8674     if (isspace(UCHAR(*pc->p))) {
   8675         return JimParseListSep(pc);
   8676     }
   8677     switch (*pc->p) {
   8678         case '"':
   8679             return JimParseListQuote(pc);
   8680 
   8681         case '{':
   8682             return JimParseBrace(pc);
   8683 
   8684         default:
   8685             if (pc->len) {
   8686                 return JimParseListStr(pc);
   8687             }
   8688             break;
   8689     }
   8690 
   8691     pc->tstart = pc->tend = pc->p;
   8692     pc->tline = pc->linenr;
   8693     pc->tt = JIM_TT_EOL;
   8694     pc->eof = 1;
   8695     return JIM_OK;
   8696 }
   8697 
   8698 static int JimParseListSep(struct JimParserCtx *pc)
   8699 {
   8700     pc->tstart = pc->p;
   8701     pc->tline = pc->linenr;
   8702     while (isspace(UCHAR(*pc->p))) {
   8703         if (*pc->p == '\n') {
   8704             pc->linenr++;
   8705         }
   8706         pc->p++;
   8707         pc->len--;
   8708     }
   8709     pc->tend = pc->p - 1;
   8710     pc->tt = JIM_TT_SEP;
   8711     return JIM_OK;
   8712 }
   8713 
   8714 static int JimParseListQuote(struct JimParserCtx *pc)
   8715 {
   8716     pc->p++;
   8717     pc->len--;
   8718 
   8719     pc->tstart = pc->p;
   8720     pc->tline = pc->linenr;
   8721     pc->tt = JIM_TT_STR;
   8722 
   8723     while (pc->len) {
   8724         switch (*pc->p) {
   8725             case '\\':
   8726                 pc->tt = JIM_TT_ESC;
   8727                 if (--pc->len == 0) {
   8728 
   8729                     pc->tend = pc->p;
   8730                     return JIM_OK;
   8731                 }
   8732                 pc->p++;
   8733                 break;
   8734             case '\n':
   8735                 pc->linenr++;
   8736                 break;
   8737             case '"':
   8738                 pc->tend = pc->p - 1;
   8739                 pc->p++;
   8740                 pc->len--;
   8741                 return JIM_OK;
   8742         }
   8743         pc->p++;
   8744         pc->len--;
   8745     }
   8746 
   8747     pc->tend = pc->p - 1;
   8748     return JIM_OK;
   8749 }
   8750 
   8751 static int JimParseListStr(struct JimParserCtx *pc)
   8752 {
   8753     pc->tstart = pc->p;
   8754     pc->tline = pc->linenr;
   8755     pc->tt = JIM_TT_STR;
   8756 
   8757     while (pc->len) {
   8758         if (isspace(UCHAR(*pc->p))) {
   8759             pc->tend = pc->p - 1;
   8760             return JIM_OK;
   8761         }
   8762         if (*pc->p == '\\') {
   8763             if (--pc->len == 0) {
   8764 
   8765                 pc->tend = pc->p;
   8766                 return JIM_OK;
   8767             }
   8768             pc->tt = JIM_TT_ESC;
   8769             pc->p++;
   8770         }
   8771         pc->p++;
   8772         pc->len--;
   8773     }
   8774     pc->tend = pc->p - 1;
   8775     return JIM_OK;
   8776 }
   8777 
   8778 
   8779 
   8780 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
   8781 {
   8782     Jim_Obj *objPtr;
   8783 
   8784 
   8785     if (interp->freeList != NULL) {
   8786 
   8787         objPtr = interp->freeList;
   8788         interp->freeList = objPtr->nextObjPtr;
   8789     }
   8790     else {
   8791 
   8792         objPtr = Jim_Alloc(sizeof(*objPtr));
   8793     }
   8794 
   8795     objPtr->refCount = 0;
   8796 
   8797 
   8798     objPtr->prevObjPtr = NULL;
   8799     objPtr->nextObjPtr = interp->liveList;
   8800     if (interp->liveList)
   8801         interp->liveList->prevObjPtr = objPtr;
   8802     interp->liveList = objPtr;
   8803 
   8804     return objPtr;
   8805 }
   8806 
   8807 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
   8808 {
   8809 
   8810     JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
   8811         objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
   8812 
   8813 
   8814     Jim_FreeIntRep(interp, objPtr);
   8815 
   8816     if (objPtr->bytes != NULL) {
   8817         if (objPtr->bytes != JimEmptyStringRep)
   8818             Jim_Free(objPtr->bytes);
   8819     }
   8820 
   8821     if (objPtr->prevObjPtr)
   8822         objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
   8823     if (objPtr->nextObjPtr)
   8824         objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
   8825     if (interp->liveList == objPtr)
   8826         interp->liveList = objPtr->nextObjPtr;
   8827 #ifdef JIM_DISABLE_OBJECT_POOL
   8828     Jim_Free(objPtr);
   8829 #else
   8830 
   8831     objPtr->prevObjPtr = NULL;
   8832     objPtr->nextObjPtr = interp->freeList;
   8833     if (interp->freeList)
   8834         interp->freeList->prevObjPtr = objPtr;
   8835     interp->freeList = objPtr;
   8836     objPtr->refCount = -1;
   8837 #endif
   8838 }
   8839 
   8840 
   8841 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
   8842 {
   8843     if (objPtr->bytes != NULL) {
   8844         if (objPtr->bytes != JimEmptyStringRep)
   8845             Jim_Free(objPtr->bytes);
   8846     }
   8847     objPtr->bytes = NULL;
   8848 }
   8849 
   8850 
   8851 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
   8852 {
   8853     Jim_Obj *dupPtr;
   8854 
   8855     dupPtr = Jim_NewObj(interp);
   8856     if (objPtr->bytes == NULL) {
   8857 
   8858         dupPtr->bytes = NULL;
   8859     }
   8860     else if (objPtr->length == 0) {
   8861         dupPtr->bytes = JimEmptyStringRep;
   8862         dupPtr->length = 0;
   8863         dupPtr->typePtr = NULL;
   8864         return dupPtr;
   8865     }
   8866     else {
   8867         dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
   8868         dupPtr->length = objPtr->length;
   8869 
   8870         memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
   8871     }
   8872 
   8873 
   8874     dupPtr->typePtr = objPtr->typePtr;
   8875     if (objPtr->typePtr != NULL) {
   8876         if (objPtr->typePtr->dupIntRepProc == NULL) {
   8877             dupPtr->internalRep = objPtr->internalRep;
   8878         }
   8879         else {
   8880 
   8881             objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
   8882         }
   8883     }
   8884     return dupPtr;
   8885 }
   8886 
   8887 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
   8888 {
   8889     if (objPtr->bytes == NULL) {
   8890 
   8891         JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
   8892         objPtr->typePtr->updateStringProc(objPtr);
   8893     }
   8894     if (lenPtr)
   8895         *lenPtr = objPtr->length;
   8896     return objPtr->bytes;
   8897 }
   8898 
   8899 
   8900 int Jim_Length(Jim_Obj *objPtr)
   8901 {
   8902     if (objPtr->bytes == NULL) {
   8903 
   8904         Jim_GetString(objPtr, NULL);
   8905     }
   8906     return objPtr->length;
   8907 }
   8908 
   8909 
   8910 const char *Jim_String(Jim_Obj *objPtr)
   8911 {
   8912     if (objPtr->bytes == NULL) {
   8913 
   8914         Jim_GetString(objPtr, NULL);
   8915     }
   8916     return objPtr->bytes;
   8917 }
   8918 
   8919 static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
   8920 {
   8921     objPtr->bytes = Jim_StrDup(str);
   8922     objPtr->length = strlen(str);
   8923 }
   8924 
   8925 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
   8926 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
   8927 
   8928 static const Jim_ObjType dictSubstObjType = {
   8929     "dict-substitution",
   8930     FreeDictSubstInternalRep,
   8931     DupDictSubstInternalRep,
   8932     NULL,
   8933     JIM_TYPE_NONE,
   8934 };
   8935 
   8936 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
   8937 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
   8938 
   8939 static const Jim_ObjType interpolatedObjType = {
   8940     "interpolated",
   8941     FreeInterpolatedInternalRep,
   8942     DupInterpolatedInternalRep,
   8943     NULL,
   8944     JIM_TYPE_NONE,
   8945 };
   8946 
   8947 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
   8948 {
   8949     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
   8950 }
   8951 
   8952 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
   8953 {
   8954 
   8955     dupPtr->internalRep = srcPtr->internalRep;
   8956 
   8957     Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
   8958 }
   8959 
   8960 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
   8961 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
   8962 
   8963 static const Jim_ObjType stringObjType = {
   8964     "string",
   8965     NULL,
   8966     DupStringInternalRep,
   8967     NULL,
   8968     JIM_TYPE_REFERENCES,
   8969 };
   8970 
   8971 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
   8972 {
   8973     JIM_NOTUSED(interp);
   8974 
   8975     dupPtr->internalRep.strValue.maxLength = srcPtr->length;
   8976     dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
   8977 }
   8978 
   8979 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
   8980 {
   8981     if (objPtr->typePtr != &stringObjType) {
   8982 
   8983         if (objPtr->bytes == NULL) {
   8984 
   8985             JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
   8986             objPtr->typePtr->updateStringProc(objPtr);
   8987         }
   8988 
   8989         Jim_FreeIntRep(interp, objPtr);
   8990 
   8991         objPtr->typePtr = &stringObjType;
   8992         objPtr->internalRep.strValue.maxLength = objPtr->length;
   8993 
   8994         objPtr->internalRep.strValue.charLength = -1;
   8995     }
   8996     return JIM_OK;
   8997 }
   8998 
   8999 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
   9000 {
   9001 #ifdef JIM_UTF8
   9002     SetStringFromAny(interp, objPtr);
   9003 
   9004     if (objPtr->internalRep.strValue.charLength < 0) {
   9005         objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
   9006     }
   9007     return objPtr->internalRep.strValue.charLength;
   9008 #else
   9009     return Jim_Length(objPtr);
   9010 #endif
   9011 }
   9012 
   9013 
   9014 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
   9015 {
   9016     Jim_Obj *objPtr = Jim_NewObj(interp);
   9017 
   9018 
   9019     if (len == -1)
   9020         len = strlen(s);
   9021 
   9022     if (len == 0) {
   9023         objPtr->bytes = JimEmptyStringRep;
   9024     }
   9025     else {
   9026         objPtr->bytes = Jim_StrDupLen(s, len);
   9027     }
   9028     objPtr->length = len;
   9029 
   9030 
   9031     objPtr->typePtr = NULL;
   9032     return objPtr;
   9033 }
   9034 
   9035 
   9036 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
   9037 {
   9038 #ifdef JIM_UTF8
   9039 
   9040     int bytelen = utf8_index(s, charlen);
   9041 
   9042     Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
   9043 
   9044 
   9045     objPtr->typePtr = &stringObjType;
   9046     objPtr->internalRep.strValue.maxLength = bytelen;
   9047     objPtr->internalRep.strValue.charLength = charlen;
   9048 
   9049     return objPtr;
   9050 #else
   9051     return Jim_NewStringObj(interp, s, charlen);
   9052 #endif
   9053 }
   9054 
   9055 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
   9056 {
   9057     Jim_Obj *objPtr = Jim_NewObj(interp);
   9058 
   9059     objPtr->bytes = s;
   9060     objPtr->length = (len == -1) ? strlen(s) : len;
   9061     objPtr->typePtr = NULL;
   9062     return objPtr;
   9063 }
   9064 
   9065 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
   9066 {
   9067     int needlen;
   9068 
   9069     if (len == -1)
   9070         len = strlen(str);
   9071     needlen = objPtr->length + len;
   9072     if (objPtr->internalRep.strValue.maxLength < needlen ||
   9073         objPtr->internalRep.strValue.maxLength == 0) {
   9074         needlen *= 2;
   9075 
   9076         if (needlen < 7) {
   9077             needlen = 7;
   9078         }
   9079         if (objPtr->bytes == JimEmptyStringRep) {
   9080             objPtr->bytes = Jim_Alloc(needlen + 1);
   9081         }
   9082         else {
   9083             objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
   9084         }
   9085         objPtr->internalRep.strValue.maxLength = needlen;
   9086     }
   9087     memcpy(objPtr->bytes + objPtr->length, str, len);
   9088     objPtr->bytes[objPtr->length + len] = '\0';
   9089 
   9090     if (objPtr->internalRep.strValue.charLength >= 0) {
   9091 
   9092         objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
   9093     }
   9094     objPtr->length += len;
   9095 }
   9096 
   9097 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
   9098 {
   9099     JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
   9100     SetStringFromAny(interp, objPtr);
   9101     StringAppendString(objPtr, str, len);
   9102 }
   9103 
   9104 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
   9105 {
   9106     int len;
   9107     const char *str = Jim_GetString(appendObjPtr, &len);
   9108     Jim_AppendString(interp, objPtr, str, len);
   9109 }
   9110 
   9111 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
   9112 {
   9113     va_list ap;
   9114 
   9115     SetStringFromAny(interp, objPtr);
   9116     va_start(ap, objPtr);
   9117     while (1) {
   9118         const char *s = va_arg(ap, const char *);
   9119 
   9120         if (s == NULL)
   9121             break;
   9122         Jim_AppendString(interp, objPtr, s, -1);
   9123     }
   9124     va_end(ap);
   9125 }
   9126 
   9127 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
   9128 {
   9129     if (aObjPtr == bObjPtr) {
   9130         return 1;
   9131     }
   9132     else {
   9133         int Alen, Blen;
   9134         const char *sA = Jim_GetString(aObjPtr, &Alen);
   9135         const char *sB = Jim_GetString(bObjPtr, &Blen);
   9136 
   9137         return Alen == Blen && memcmp(sA, sB, Alen) == 0;
   9138     }
   9139 }
   9140 
   9141 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
   9142 {
   9143     int plen, slen;
   9144     const char *pattern = Jim_GetString(patternObjPtr, &plen);
   9145     const char *string = Jim_GetString(objPtr, &slen);
   9146     return JimGlobMatch(pattern, plen, string, slen, nocase);
   9147 }
   9148 
   9149 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
   9150 {
   9151     const char *s1 = Jim_String(firstObjPtr);
   9152     int l1 = Jim_Utf8Length(interp, firstObjPtr);
   9153     const char *s2 = Jim_String(secondObjPtr);
   9154     int l2 = Jim_Utf8Length(interp, secondObjPtr);
   9155     return JimStringCompareUtf8(s1, l1, s2, l2, nocase);
   9156 }
   9157 
   9158 static int JimRelToAbsIndex(int len, int idx)
   9159 {
   9160     if (idx < 0 && idx > -INT_MAX)
   9161         return len + idx;
   9162     return idx;
   9163 }
   9164 
   9165 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
   9166 {
   9167     int rangeLen;
   9168 
   9169     if (*firstPtr > *lastPtr) {
   9170         rangeLen = 0;
   9171     }
   9172     else {
   9173         rangeLen = *lastPtr - *firstPtr + 1;
   9174         if (rangeLen) {
   9175             if (*firstPtr < 0) {
   9176                 rangeLen += *firstPtr;
   9177                 *firstPtr = 0;
   9178             }
   9179             if (*lastPtr >= len) {
   9180                 rangeLen -= (*lastPtr - (len - 1));
   9181                 *lastPtr = len - 1;
   9182             }
   9183         }
   9184     }
   9185     if (rangeLen < 0)
   9186         rangeLen = 0;
   9187 
   9188     *rangeLenPtr = rangeLen;
   9189 }
   9190 
   9191 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
   9192     int len, int *first, int *last, int *range)
   9193 {
   9194     if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
   9195         return JIM_ERR;
   9196     }
   9197     if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
   9198         return JIM_ERR;
   9199     }
   9200     *first = JimRelToAbsIndex(len, *first);
   9201     *last = JimRelToAbsIndex(len, *last);
   9202     JimRelToAbsRange(len, first, last, range);
   9203     return JIM_OK;
   9204 }
   9205 
   9206 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
   9207     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
   9208 {
   9209     int first, last;
   9210     const char *str;
   9211     int rangeLen;
   9212     int bytelen;
   9213 
   9214     str = Jim_GetString(strObjPtr, &bytelen);
   9215 
   9216     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
   9217         return NULL;
   9218     }
   9219 
   9220     if (first == 0 && rangeLen == bytelen) {
   9221         return strObjPtr;
   9222     }
   9223     return Jim_NewStringObj(interp, str + first, rangeLen);
   9224 }
   9225 
   9226 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
   9227     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
   9228 {
   9229 #ifdef JIM_UTF8
   9230     int first, last;
   9231     const char *str;
   9232     int len, rangeLen;
   9233     int bytelen;
   9234 
   9235     str = Jim_GetString(strObjPtr, &bytelen);
   9236     len = Jim_Utf8Length(interp, strObjPtr);
   9237 
   9238     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
   9239         return NULL;
   9240     }
   9241 
   9242     if (first == 0 && rangeLen == len) {
   9243         return strObjPtr;
   9244     }
   9245     if (len == bytelen) {
   9246 
   9247         return Jim_NewStringObj(interp, str + first, rangeLen);
   9248     }
   9249     return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
   9250 #else
   9251     return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
   9252 #endif
   9253 }
   9254 
   9255 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
   9256     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
   9257 {
   9258     int first, last;
   9259     const char *str;
   9260     int len, rangeLen;
   9261     Jim_Obj *objPtr;
   9262 
   9263     len = Jim_Utf8Length(interp, strObjPtr);
   9264 
   9265     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
   9266         return NULL;
   9267     }
   9268 
   9269     if (last < first) {
   9270         return strObjPtr;
   9271     }
   9272 
   9273     str = Jim_String(strObjPtr);
   9274 
   9275 
   9276     objPtr = Jim_NewStringObjUtf8(interp, str, first);
   9277 
   9278 
   9279     if (newStrObj) {
   9280         Jim_AppendObj(interp, objPtr, newStrObj);
   9281     }
   9282 
   9283 
   9284     Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
   9285 
   9286     return objPtr;
   9287 }
   9288 
   9289 static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
   9290 {
   9291     while (*str) {
   9292         int c;
   9293         str += utf8_tounicode(str, &c);
   9294         dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c));
   9295     }
   9296     *dest = 0;
   9297 }
   9298 
   9299 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
   9300 {
   9301     char *buf;
   9302     int len;
   9303     const char *str;
   9304 
   9305     str = Jim_GetString(strObjPtr, &len);
   9306 
   9307 #ifdef JIM_UTF8
   9308     len *= 2;
   9309 #endif
   9310     buf = Jim_Alloc(len + 1);
   9311     JimStrCopyUpperLower(buf, str, 0);
   9312     return Jim_NewStringObjNoAlloc(interp, buf, -1);
   9313 }
   9314 
   9315 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
   9316 {
   9317     char *buf;
   9318     const char *str;
   9319     int len;
   9320 
   9321     str = Jim_GetString(strObjPtr, &len);
   9322 
   9323 #ifdef JIM_UTF8
   9324     len *= 2;
   9325 #endif
   9326     buf = Jim_Alloc(len + 1);
   9327     JimStrCopyUpperLower(buf, str, 1);
   9328     return Jim_NewStringObjNoAlloc(interp, buf, -1);
   9329 }
   9330 
   9331 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
   9332 {
   9333     char *buf, *p;
   9334     int len;
   9335     int c;
   9336     const char *str;
   9337 
   9338     str = Jim_GetString(strObjPtr, &len);
   9339 
   9340 #ifdef JIM_UTF8
   9341     len *= 2;
   9342 #endif
   9343     buf = p = Jim_Alloc(len + 1);
   9344 
   9345     str += utf8_tounicode(str, &c);
   9346     p += utf8_getchars(p, utf8_title(c));
   9347 
   9348     JimStrCopyUpperLower(p, str, 0);
   9349 
   9350     return Jim_NewStringObjNoAlloc(interp, buf, -1);
   9351 }
   9352 
   9353 static const char *utf8_memchr(const char *str, int len, int c)
   9354 {
   9355 #ifdef JIM_UTF8
   9356     while (len) {
   9357         int sc;
   9358         int n = utf8_tounicode(str, &sc);
   9359         if (sc == c) {
   9360             return str;
   9361         }
   9362         str += n;
   9363         len -= n;
   9364     }
   9365     return NULL;
   9366 #else
   9367     return memchr(str, c, len);
   9368 #endif
   9369 }
   9370 
   9371 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
   9372 {
   9373     while (len) {
   9374         int c;
   9375         int n = utf8_tounicode(str, &c);
   9376 
   9377         if (utf8_memchr(trimchars, trimlen, c) == NULL) {
   9378 
   9379             break;
   9380         }
   9381         str += n;
   9382         len -= n;
   9383     }
   9384     return str;
   9385 }
   9386 
   9387 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
   9388 {
   9389     str += len;
   9390 
   9391     while (len) {
   9392         int c;
   9393         int n = utf8_prev_len(str, len);
   9394 
   9395         len -= n;
   9396         str -= n;
   9397 
   9398         n = utf8_tounicode(str, &c);
   9399 
   9400         if (utf8_memchr(trimchars, trimlen, c) == NULL) {
   9401             return str + n;
   9402         }
   9403     }
   9404 
   9405     return NULL;
   9406 }
   9407 
   9408 static const char default_trim_chars[] = " \t\n\r";
   9409 
   9410 static int default_trim_chars_len = sizeof(default_trim_chars);
   9411 
   9412 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
   9413 {
   9414     int len;
   9415     const char *str = Jim_GetString(strObjPtr, &len);
   9416     const char *trimchars = default_trim_chars;
   9417     int trimcharslen = default_trim_chars_len;
   9418     const char *newstr;
   9419 
   9420     if (trimcharsObjPtr) {
   9421         trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
   9422     }
   9423 
   9424     newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
   9425     if (newstr == str) {
   9426         return strObjPtr;
   9427     }
   9428 
   9429     return Jim_NewStringObj(interp, newstr, len - (newstr - str));
   9430 }
   9431 
   9432 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
   9433 {
   9434     int len;
   9435     const char *trimchars = default_trim_chars;
   9436     int trimcharslen = default_trim_chars_len;
   9437     const char *nontrim;
   9438 
   9439     if (trimcharsObjPtr) {
   9440         trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
   9441     }
   9442 
   9443     SetStringFromAny(interp, strObjPtr);
   9444 
   9445     len = Jim_Length(strObjPtr);
   9446     nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
   9447 
   9448     if (nontrim == NULL) {
   9449 
   9450         return Jim_NewEmptyStringObj(interp);
   9451     }
   9452     if (nontrim == strObjPtr->bytes + len) {
   9453 
   9454         return strObjPtr;
   9455     }
   9456 
   9457     if (Jim_IsShared(strObjPtr)) {
   9458         strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
   9459     }
   9460     else {
   9461 
   9462         strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
   9463         strObjPtr->length = (nontrim - strObjPtr->bytes);
   9464     }
   9465 
   9466     return strObjPtr;
   9467 }
   9468 
   9469 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
   9470 {
   9471 
   9472     Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
   9473 
   9474 
   9475     strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
   9476 
   9477 
   9478     if (objPtr != strObjPtr && objPtr->refCount == 0) {
   9479 
   9480         Jim_FreeNewObj(interp, objPtr);
   9481     }
   9482 
   9483     return strObjPtr;
   9484 }
   9485 
   9486 
   9487 #ifdef HAVE_ISASCII
   9488 #define jim_isascii isascii
   9489 #else
   9490 static int jim_isascii(int c)
   9491 {
   9492     return !(c & ~0x7f);
   9493 }
   9494 #endif
   9495 
   9496 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
   9497 {
   9498     static const char * const strclassnames[] = {
   9499         "integer", "alpha", "alnum", "ascii", "digit",
   9500         "double", "lower", "upper", "space", "xdigit",
   9501         "control", "print", "graph", "punct", "boolean",
   9502         NULL
   9503     };
   9504     enum {
   9505         STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
   9506         STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
   9507         STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN,
   9508     };
   9509     int strclass;
   9510     int len;
   9511     int i;
   9512     const char *str;
   9513     int (*isclassfunc)(int c) = NULL;
   9514 
   9515     if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
   9516         return JIM_ERR;
   9517     }
   9518 
   9519     str = Jim_GetString(strObjPtr, &len);
   9520     if (len == 0) {
   9521         Jim_SetResultBool(interp, !strict);
   9522         return JIM_OK;
   9523     }
   9524 
   9525     switch (strclass) {
   9526         case STR_IS_INTEGER:
   9527             {
   9528                 jim_wide w;
   9529                 Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
   9530                 return JIM_OK;
   9531             }
   9532 
   9533         case STR_IS_DOUBLE:
   9534             {
   9535                 double d;
   9536                 Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
   9537                 return JIM_OK;
   9538             }
   9539 
   9540         case STR_IS_BOOLEAN:
   9541             {
   9542                 int b;
   9543                 Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK);
   9544                 return JIM_OK;
   9545             }
   9546 
   9547         case STR_IS_ALPHA: isclassfunc = isalpha; break;
   9548         case STR_IS_ALNUM: isclassfunc = isalnum; break;
   9549         case STR_IS_ASCII: isclassfunc = jim_isascii; break;
   9550         case STR_IS_DIGIT: isclassfunc = isdigit; break;
   9551         case STR_IS_LOWER: isclassfunc = islower; break;
   9552         case STR_IS_UPPER: isclassfunc = isupper; break;
   9553         case STR_IS_SPACE: isclassfunc = isspace; break;
   9554         case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
   9555         case STR_IS_CONTROL: isclassfunc = iscntrl; break;
   9556         case STR_IS_PRINT: isclassfunc = isprint; break;
   9557         case STR_IS_GRAPH: isclassfunc = isgraph; break;
   9558         case STR_IS_PUNCT: isclassfunc = ispunct; break;
   9559         default:
   9560             return JIM_ERR;
   9561     }
   9562 
   9563     for (i = 0; i < len; i++) {
   9564         if (!isclassfunc(UCHAR(str[i]))) {
   9565             Jim_SetResultBool(interp, 0);
   9566             return JIM_OK;
   9567         }
   9568     }
   9569     Jim_SetResultBool(interp, 1);
   9570     return JIM_OK;
   9571 }
   9572 
   9573 
   9574 
   9575 static const Jim_ObjType comparedStringObjType = {
   9576     "compared-string",
   9577     NULL,
   9578     NULL,
   9579     NULL,
   9580     JIM_TYPE_REFERENCES,
   9581 };
   9582 
   9583 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
   9584 {
   9585     if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
   9586         return 1;
   9587     }
   9588     else {
   9589         if (strcmp(str, Jim_String(objPtr)) != 0)
   9590             return 0;
   9591 
   9592         if (objPtr->typePtr != &comparedStringObjType) {
   9593             Jim_FreeIntRep(interp, objPtr);
   9594             objPtr->typePtr = &comparedStringObjType;
   9595         }
   9596         objPtr->internalRep.ptr = (char *)str;
   9597         return 1;
   9598     }
   9599 }
   9600 
   9601 static int qsortCompareStringPointers(const void *a, const void *b)
   9602 {
   9603     char *const *sa = (char *const *)a;
   9604     char *const *sb = (char *const *)b;
   9605 
   9606     return strcmp(*sa, *sb);
   9607 }
   9608 
   9609 
   9610 
   9611 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
   9612 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
   9613 
   9614 static const Jim_ObjType sourceObjType = {
   9615     "source",
   9616     FreeSourceInternalRep,
   9617     DupSourceInternalRep,
   9618     NULL,
   9619     JIM_TYPE_REFERENCES,
   9620 };
   9621 
   9622 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
   9623 {
   9624     Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
   9625 }
   9626 
   9627 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
   9628 {
   9629     dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
   9630     Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
   9631 }
   9632 
   9633 static const Jim_ObjType scriptLineObjType = {
   9634     "scriptline",
   9635     NULL,
   9636     NULL,
   9637     NULL,
   9638     JIM_NONE,
   9639 };
   9640 
   9641 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
   9642 {
   9643     Jim_Obj *objPtr;
   9644 
   9645 #ifdef DEBUG_SHOW_SCRIPT
   9646     char buf[100];
   9647     snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
   9648     objPtr = Jim_NewStringObj(interp, buf, -1);
   9649 #else
   9650     objPtr = Jim_NewEmptyStringObj(interp);
   9651 #endif
   9652     objPtr->typePtr = &scriptLineObjType;
   9653     objPtr->internalRep.scriptLineValue.argc = argc;
   9654     objPtr->internalRep.scriptLineValue.line = line;
   9655 
   9656     return objPtr;
   9657 }
   9658 
   9659 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
   9660 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
   9661 
   9662 static const Jim_ObjType scriptObjType = {
   9663     "script",
   9664     FreeScriptInternalRep,
   9665     DupScriptInternalRep,
   9666     NULL,
   9667     JIM_TYPE_NONE,
   9668 };
   9669 
   9670 typedef struct ScriptToken
   9671 {
   9672     Jim_Obj *objPtr;
   9673     int type;
   9674 } ScriptToken;
   9675 
   9676 typedef struct ScriptObj
   9677 {
   9678     ScriptToken *token;
   9679     Jim_Obj *fileNameObj;
   9680     int len;
   9681     int substFlags;
   9682     int inUse;                  /* Used to share a ScriptObj. Currently
   9683                                    only used by Jim_EvalObj() as protection against
   9684                                    shimmering of the currently evaluated object. */
   9685     int firstline;
   9686     int linenr;
   9687     int missing;
   9688 } ScriptObj;
   9689 
   9690 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
   9691 static int JimParseCheckMissing(Jim_Interp *interp, int ch);
   9692 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
   9693 static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script);
   9694 
   9695 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
   9696 {
   9697     int i;
   9698     struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
   9699 
   9700     if (--script->inUse != 0)
   9701         return;
   9702     for (i = 0; i < script->len; i++) {
   9703         Jim_DecrRefCount(interp, script->token[i].objPtr);
   9704     }
   9705     Jim_Free(script->token);
   9706     Jim_DecrRefCount(interp, script->fileNameObj);
   9707     Jim_Free(script);
   9708 }
   9709 
   9710 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
   9711 {
   9712     JIM_NOTUSED(interp);
   9713     JIM_NOTUSED(srcPtr);
   9714 
   9715     dupPtr->typePtr = NULL;
   9716 }
   9717 
   9718 typedef struct
   9719 {
   9720     const char *token;
   9721     int len;
   9722     int type;
   9723     int line;
   9724 } ParseToken;
   9725 
   9726 typedef struct
   9727 {
   9728 
   9729     ParseToken *list;
   9730     int size;
   9731     int count;
   9732     ParseToken static_list[20];
   9733 } ParseTokenList;
   9734 
   9735 static void ScriptTokenListInit(ParseTokenList *tokenlist)
   9736 {
   9737     tokenlist->list = tokenlist->static_list;
   9738     tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
   9739     tokenlist->count = 0;
   9740 }
   9741 
   9742 static void ScriptTokenListFree(ParseTokenList *tokenlist)
   9743 {
   9744     if (tokenlist->list != tokenlist->static_list) {
   9745         Jim_Free(tokenlist->list);
   9746     }
   9747 }
   9748 
   9749 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
   9750     int line)
   9751 {
   9752     ParseToken *t;
   9753 
   9754     if (tokenlist->count == tokenlist->size) {
   9755 
   9756         tokenlist->size *= 2;
   9757         if (tokenlist->list != tokenlist->static_list) {
   9758             tokenlist->list =
   9759                 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
   9760         }
   9761         else {
   9762 
   9763             tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
   9764             memcpy(tokenlist->list, tokenlist->static_list,
   9765                 tokenlist->count * sizeof(*tokenlist->list));
   9766         }
   9767     }
   9768     t = &tokenlist->list[tokenlist->count++];
   9769     t->token = token;
   9770     t->len = len;
   9771     t->type = type;
   9772     t->line = line;
   9773 }
   9774 
   9775 static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t)
   9776 {
   9777     int expand = 1;
   9778     int count = 0;
   9779 
   9780 
   9781     if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
   9782         if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
   9783 
   9784             expand = -1;
   9785             t++;
   9786         }
   9787         else {
   9788             if (script->missing == ' ') {
   9789 
   9790                 script->missing = '}';
   9791                 script->linenr = t[1].line;
   9792             }
   9793         }
   9794     }
   9795 
   9796 
   9797     while (!TOKEN_IS_SEP(t->type)) {
   9798         t++;
   9799         count++;
   9800     }
   9801 
   9802     return count * expand;
   9803 }
   9804 
   9805 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
   9806 {
   9807     Jim_Obj *objPtr;
   9808 
   9809     if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
   9810 
   9811         int len = t->len;
   9812         char *str = Jim_Alloc(len + 1);
   9813         len = JimEscape(str, t->token, len);
   9814         objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
   9815     }
   9816     else {
   9817         objPtr = Jim_NewStringObj(interp, t->token, t->len);
   9818     }
   9819     return objPtr;
   9820 }
   9821 
   9822 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
   9823     ParseTokenList *tokenlist)
   9824 {
   9825     int i;
   9826     struct ScriptToken *token;
   9827 
   9828     int lineargs = 0;
   9829 
   9830     ScriptToken *linefirst;
   9831     int count;
   9832     int linenr;
   9833 
   9834 #ifdef DEBUG_SHOW_SCRIPT_TOKENS
   9835     printf("==== Tokens ====\n");
   9836     for (i = 0; i < tokenlist->count; i++) {
   9837         printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
   9838             tokenlist->list[i].len, tokenlist->list[i].token);
   9839     }
   9840 #endif
   9841 
   9842 
   9843     count = tokenlist->count;
   9844     for (i = 0; i < tokenlist->count; i++) {
   9845         if (tokenlist->list[i].type == JIM_TT_EOL) {
   9846             count++;
   9847         }
   9848     }
   9849     linenr = script->firstline = tokenlist->list[0].line;
   9850 
   9851     token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
   9852 
   9853 
   9854     linefirst = token++;
   9855 
   9856     for (i = 0; i < tokenlist->count; ) {
   9857 
   9858         int wordtokens;
   9859 
   9860 
   9861         while (tokenlist->list[i].type == JIM_TT_SEP) {
   9862             i++;
   9863         }
   9864 
   9865         wordtokens = JimCountWordTokens(script, tokenlist->list + i);
   9866 
   9867         if (wordtokens == 0) {
   9868 
   9869             if (lineargs) {
   9870                 linefirst->type = JIM_TT_LINE;
   9871                 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
   9872                 Jim_IncrRefCount(linefirst->objPtr);
   9873 
   9874 
   9875                 lineargs = 0;
   9876                 linefirst = token++;
   9877             }
   9878             i++;
   9879             continue;
   9880         }
   9881         else if (wordtokens != 1) {
   9882 
   9883             token->type = JIM_TT_WORD;
   9884             token->objPtr = Jim_NewIntObj(interp, wordtokens);
   9885             Jim_IncrRefCount(token->objPtr);
   9886             token++;
   9887             if (wordtokens < 0) {
   9888 
   9889                 i++;
   9890                 wordtokens = -wordtokens - 1;
   9891                 lineargs--;
   9892             }
   9893         }
   9894 
   9895         if (lineargs == 0) {
   9896 
   9897             linenr = tokenlist->list[i].line;
   9898         }
   9899         lineargs++;
   9900 
   9901 
   9902         while (wordtokens--) {
   9903             const ParseToken *t = &tokenlist->list[i++];
   9904 
   9905             token->type = t->type;
   9906             token->objPtr = JimMakeScriptObj(interp, t);
   9907             Jim_IncrRefCount(token->objPtr);
   9908 
   9909             Jim_SetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
   9910             token++;
   9911         }
   9912     }
   9913 
   9914     if (lineargs == 0) {
   9915         token--;
   9916     }
   9917 
   9918     script->len = token - script->token;
   9919 
   9920     JimPanic((script->len >= count, "allocated script array is too short"));
   9921 
   9922 #ifdef DEBUG_SHOW_SCRIPT
   9923     printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj));
   9924     for (i = 0; i < script->len; i++) {
   9925         const ScriptToken *t = &script->token[i];
   9926         printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
   9927     }
   9928 #endif
   9929 
   9930 }
   9931 
   9932 int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
   9933 {
   9934     ScriptObj *script = JimGetScript(interp, scriptObj);
   9935     if (stateCharPtr) {
   9936         *stateCharPtr = script->missing;
   9937     }
   9938     return script->missing == ' ' || script->missing == '}';
   9939 }
   9940 
   9941 static int JimParseCheckMissing(Jim_Interp *interp, int ch)
   9942 {
   9943     const char *msg;
   9944 
   9945     switch (ch) {
   9946         case '\\':
   9947         case ' ':
   9948             return JIM_OK;
   9949 
   9950         case '[':
   9951             msg = "unmatched \"[\"";
   9952             break;
   9953         case '{':
   9954             msg = "missing close-brace";
   9955             break;
   9956         case '}':
   9957             msg = "extra characters after close-brace";
   9958             break;
   9959         case '"':
   9960         default:
   9961             msg = "missing quote";
   9962             break;
   9963     }
   9964 
   9965     Jim_SetResultString(interp, msg, -1);
   9966     return JIM_ERR;
   9967 }
   9968 
   9969 Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, int *lineptr)
   9970 {
   9971     int line;
   9972     Jim_Obj *fileNameObj;
   9973 
   9974     if (objPtr->typePtr == &sourceObjType) {
   9975         fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
   9976         line = objPtr->internalRep.sourceValue.lineNumber;
   9977     }
   9978     else if (objPtr->typePtr == &scriptObjType) {
   9979         ScriptObj *script = JimGetScript(interp, objPtr);
   9980         fileNameObj = script->fileNameObj;
   9981         line = script->firstline;
   9982     }
   9983     else {
   9984         fileNameObj = interp->emptyObj;
   9985         line = 1;
   9986     }
   9987     *lineptr = line;
   9988     return fileNameObj;
   9989 }
   9990 
   9991 void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
   9992     Jim_Obj *fileNameObj, int lineNumber)
   9993 {
   9994     JimPanic((Jim_IsShared(objPtr), "Jim_SetSourceInfo called with shared object"));
   9995     Jim_FreeIntRep(interp, objPtr);
   9996     Jim_IncrRefCount(fileNameObj);
   9997     objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
   9998     objPtr->internalRep.sourceValue.lineNumber = lineNumber;
   9999     objPtr->typePtr = &sourceObjType;
   10000 }
   10001 
   10002 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
   10003     ParseTokenList *tokenlist)
   10004 {
   10005     int i;
   10006     struct ScriptToken *token;
   10007 
   10008     token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
   10009 
   10010     for (i = 0; i < tokenlist->count; i++) {
   10011         const ParseToken *t = &tokenlist->list[i];
   10012 
   10013 
   10014         token->type = t->type;
   10015         token->objPtr = JimMakeScriptObj(interp, t);
   10016         Jim_IncrRefCount(token->objPtr);
   10017         token++;
   10018     }
   10019 
   10020     script->len = i;
   10021 }
   10022 
   10023 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
   10024 {
   10025     int scriptTextLen;
   10026     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
   10027     struct JimParserCtx parser;
   10028     struct ScriptObj *script;
   10029     ParseTokenList tokenlist;
   10030     Jim_Obj *fileNameObj;
   10031     int line;
   10032 
   10033 
   10034     fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
   10035 
   10036 
   10037     ScriptTokenListInit(&tokenlist);
   10038 
   10039     JimParserInit(&parser, scriptText, scriptTextLen, line);
   10040     while (!parser.eof) {
   10041         JimParseScript(&parser);
   10042         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
   10043             parser.tline);
   10044     }
   10045 
   10046 
   10047     ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
   10048 
   10049 
   10050     script = Jim_Alloc(sizeof(*script));
   10051     memset(script, 0, sizeof(*script));
   10052     script->inUse = 1;
   10053     script->fileNameObj = fileNameObj;
   10054     Jim_IncrRefCount(script->fileNameObj);
   10055     script->missing = parser.missing.ch;
   10056     script->linenr = parser.missing.line;
   10057 
   10058     ScriptObjAddTokens(interp, script, &tokenlist);
   10059 
   10060 
   10061     ScriptTokenListFree(&tokenlist);
   10062 
   10063 
   10064     Jim_FreeIntRep(interp, objPtr);
   10065     Jim_SetIntRepPtr(objPtr, script);
   10066     objPtr->typePtr = &scriptObjType;
   10067 }
   10068 
   10069 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
   10070 {
   10071     if (objPtr == interp->emptyObj) {
   10072 
   10073         objPtr = interp->nullScriptObj;
   10074     }
   10075 
   10076     if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
   10077         JimSetScriptFromAny(interp, objPtr);
   10078     }
   10079 
   10080     return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
   10081 }
   10082 
   10083 void Jim_InterpIncrProcEpoch(Jim_Interp *interp)
   10084 {
   10085     interp->procEpoch++;
   10086 
   10087 
   10088     while (interp->oldCmdCache) {
   10089         Jim_Cmd *next = interp->oldCmdCache->prevCmd;
   10090         Jim_Free(interp->oldCmdCache);
   10091         interp->oldCmdCache = next;
   10092     }
   10093     interp->oldCmdCacheSize = 0;
   10094 }
   10095 
   10096 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
   10097 {
   10098     cmdPtr->inUse++;
   10099 }
   10100 
   10101 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
   10102 {
   10103     if (--cmdPtr->inUse == 0) {
   10104         if (cmdPtr->isproc) {
   10105             Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
   10106             Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
   10107             Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
   10108             if (cmdPtr->u.proc.staticVars) {
   10109                 Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
   10110                 Jim_Free(cmdPtr->u.proc.staticVars);
   10111             }
   10112         }
   10113         else {
   10114 
   10115             if (cmdPtr->u.native.delProc) {
   10116                 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
   10117             }
   10118         }
   10119         if (cmdPtr->prevCmd) {
   10120 
   10121             JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
   10122         }
   10123 
   10124         cmdPtr->prevCmd = interp->oldCmdCache;
   10125         interp->oldCmdCache = cmdPtr;
   10126         if (!interp->quitting && ++interp->oldCmdCacheSize >= 1000) {
   10127             Jim_InterpIncrProcEpoch(interp);
   10128         }
   10129     }
   10130 }
   10131 
   10132 static void JimIncrVarRef(Jim_VarVal *vv)
   10133 {
   10134     vv->refCount++;
   10135 }
   10136 
   10137 static void JimDecrVarRef(Jim_Interp *interp, Jim_VarVal *vv)
   10138 {
   10139     assert(vv->refCount > 0);
   10140     if (--vv->refCount == 0) {
   10141         if (vv->objPtr) {
   10142             Jim_DecrRefCount(interp, vv->objPtr);
   10143         }
   10144         Jim_Free(vv);
   10145     }
   10146 }
   10147 
   10148 static void JimVariablesHTValDestructor(void *interp, void *val)
   10149 {
   10150     JimDecrVarRef(interp, val);
   10151 }
   10152 
   10153 static unsigned int JimObjectHTHashFunction(const void *key)
   10154 {
   10155     Jim_Obj *keyObj = (Jim_Obj *)key;
   10156     int length;
   10157     const char *string;
   10158 
   10159 #ifdef JIM_OPTIMIZATION
   10160     if (JimIsWide(keyObj) && keyObj->bytes == NULL) {
   10161 
   10162         jim_wide objValue = JimWideValue(keyObj);
   10163         if (objValue > INT_MIN && objValue < INT_MAX) {
   10164             unsigned result = 0;
   10165             unsigned value = (unsigned)objValue;
   10166 
   10167             if (objValue < 0) {
   10168                 value = (unsigned)-objValue;
   10169             }
   10170 
   10171 
   10172             do {
   10173                 result += (result << 3) + (value % 10 + '0');
   10174                 value /= 10;
   10175             } while (value);
   10176 
   10177             if (objValue < 0) {
   10178                 result += (result << 3) + '-';
   10179             }
   10180             return result;
   10181         }
   10182     }
   10183 #endif
   10184     string = Jim_GetString(keyObj, &length);
   10185     return Jim_GenHashFunction((const unsigned char *)string, length);
   10186 }
   10187 
   10188 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
   10189 {
   10190     return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
   10191 }
   10192 
   10193 static void *JimObjectHTKeyValDup(void *privdata, const void *val)
   10194 {
   10195     Jim_IncrRefCount((Jim_Obj *)val);
   10196     return (void *)val;
   10197 }
   10198 
   10199 static void JimObjectHTKeyValDestructor(void *interp, void *val)
   10200 {
   10201     Jim_DecrRefCount(interp, (Jim_Obj *)val);
   10202 }
   10203 
   10204 
   10205 static void *JimVariablesHTValDup(void *privdata, const void *val)
   10206 {
   10207     JimIncrVarRef((Jim_VarVal *)val);
   10208     return (void *)val;
   10209 }
   10210 
   10211 static const Jim_HashTableType JimVariablesHashTableType = {
   10212     JimObjectHTHashFunction,
   10213     JimObjectHTKeyValDup,
   10214     JimVariablesHTValDup,
   10215     JimObjectHTKeyCompare,
   10216     JimObjectHTKeyValDestructor,
   10217     JimVariablesHTValDestructor
   10218 };
   10219 
   10220 
   10221 static const char *Jim_GetStringNoQualifier(Jim_Obj *objPtr, int *length)
   10222 {
   10223     int len;
   10224     const char *str = Jim_GetString(objPtr, &len);
   10225     if (len >= 2 && str[0] == ':' && str[1] == ':') {
   10226         while (len && *str == ':') {
   10227             len--;
   10228             str++;
   10229         }
   10230     }
   10231     *length = len;
   10232     return str;
   10233 }
   10234 
   10235 static unsigned int JimCommandsHT_HashFunction(const void *key)
   10236 {
   10237     int len;
   10238     const char *str = Jim_GetStringNoQualifier((Jim_Obj *)key, &len);
   10239     return Jim_GenHashFunction((const unsigned char *)str, len);
   10240 }
   10241 
   10242 static int JimCommandsHT_KeyCompare(void *privdata, const void *key1, const void *key2)
   10243 {
   10244     int len1, len2;
   10245     const char *str1 = Jim_GetStringNoQualifier((Jim_Obj *)key1, &len1);
   10246     const char *str2 = Jim_GetStringNoQualifier((Jim_Obj *)key2, &len2);
   10247     return len1 == len2 && memcmp(str1, str2, len1) == 0;
   10248 }
   10249 
   10250 static void JimCommandsHT_ValDestructor(void *interp, void *val)
   10251 {
   10252     JimDecrCmdRefCount(interp, val);
   10253 }
   10254 
   10255 static const Jim_HashTableType JimCommandsHashTableType = {
   10256     JimCommandsHT_HashFunction,
   10257     JimObjectHTKeyValDup,
   10258     NULL,
   10259     JimCommandsHT_KeyCompare,
   10260     JimObjectHTKeyValDestructor,
   10261     JimCommandsHT_ValDestructor
   10262 };
   10263 
   10264 
   10265 
   10266 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
   10267 {
   10268 #ifdef jim_ext_namespace
   10269     Jim_Obj *resultObj;
   10270 
   10271     const char *name = Jim_String(nameObjPtr);
   10272     if (name[0] == ':' && name[1] == ':') {
   10273         return nameObjPtr;
   10274     }
   10275     Jim_IncrRefCount(nameObjPtr);
   10276     resultObj = Jim_NewStringObj(interp, "::", -1);
   10277     Jim_AppendObj(interp, resultObj, nameObjPtr);
   10278     Jim_DecrRefCount(interp, nameObjPtr);
   10279 
   10280     return resultObj;
   10281 #else
   10282     return nameObjPtr;
   10283 #endif
   10284 }
   10285 
   10286 static Jim_Obj *JimQualifyName(Jim_Interp *interp, Jim_Obj *objPtr)
   10287 {
   10288 #ifdef jim_ext_namespace
   10289     if (Jim_Length(interp->framePtr->nsObj)) {
   10290         int len;
   10291         const char *name = Jim_GetString(objPtr, &len);
   10292         if (len < 2 || name[0] != ':' || name[1] != ':') {
   10293 
   10294             objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
   10295             Jim_AppendStrings(interp, objPtr, "::", name, NULL);
   10296         }
   10297     }
   10298 #endif
   10299     Jim_IncrRefCount(objPtr);
   10300     return objPtr;
   10301 }
   10302 
   10303 static void JimCreateCommand(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Cmd *cmd)
   10304 {
   10305     JimPanic((nameObjPtr->refCount == 0, "JimCreateCommand called with zero ref count name"));
   10306 
   10307     if (interp->local) {
   10308         Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, nameObjPtr);
   10309         if (he) {
   10310 
   10311             cmd->prevCmd = Jim_GetHashEntryVal(he);
   10312             Jim_SetHashVal(&interp->commands, he, cmd);
   10313 
   10314             Jim_InterpIncrProcEpoch(interp);
   10315             return;
   10316         }
   10317     }
   10318 
   10319 
   10320 
   10321     Jim_ReplaceHashEntry(&interp->commands, nameObjPtr, cmd);
   10322 }
   10323 
   10324 int Jim_CreateCommandObj(Jim_Interp *interp, Jim_Obj *cmdNameObj,
   10325     Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
   10326 {
   10327     Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
   10328 
   10329 
   10330     memset(cmdPtr, 0, sizeof(*cmdPtr));
   10331     cmdPtr->inUse = 1;
   10332     cmdPtr->u.native.delProc = delProc;
   10333     cmdPtr->u.native.cmdProc = cmdProc;
   10334     cmdPtr->u.native.privData = privData;
   10335 
   10336     Jim_IncrRefCount(cmdNameObj);
   10337     JimCreateCommand(interp, cmdNameObj, cmdPtr);
   10338     Jim_DecrRefCount(interp, cmdNameObj);
   10339 
   10340     return JIM_OK;
   10341 }
   10342 
   10343 
   10344 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
   10345     Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
   10346 {
   10347     return Jim_CreateCommandObj(interp, Jim_NewStringObj(interp, cmdNameStr, -1), cmdProc, privData, delProc);
   10348 }
   10349 
   10350 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
   10351 {
   10352     int len, i;
   10353 
   10354     len = Jim_ListLength(interp, staticsListObjPtr);
   10355     if (len == 0) {
   10356         return JIM_OK;
   10357     }
   10358 
   10359     cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
   10360     Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
   10361     for (i = 0; i < len; i++) {
   10362         Jim_Obj *initObjPtr = NULL;
   10363         Jim_Obj *nameObjPtr;
   10364         Jim_VarVal *vv = NULL;
   10365         Jim_Obj *objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
   10366         int subLen = Jim_ListLength(interp, objPtr);
   10367         int byref = 0;
   10368 
   10369 
   10370         if (subLen != 1 && subLen != 2) {
   10371             Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
   10372                 objPtr);
   10373             return JIM_ERR;
   10374         }
   10375 
   10376         nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
   10377 
   10378 
   10379         if (subLen == 1) {
   10380             int len;
   10381             const char *pt = Jim_GetString(nameObjPtr, &len);
   10382             if (*pt == '&') {
   10383 
   10384                 nameObjPtr = Jim_NewStringObj(interp, pt + 1, len - 1);
   10385                 byref = 1;
   10386             }
   10387         }
   10388         Jim_IncrRefCount(nameObjPtr);
   10389 
   10390         if (subLen == 1) {
   10391             switch (SetVariableFromAny(interp, nameObjPtr)) {
   10392                 case JIM_DICT_SUGAR:
   10393 
   10394                     if (byref) {
   10395                         Jim_SetResultFormatted(interp, "Can't link to array element \"%#s\"", nameObjPtr);
   10396                     }
   10397                     else {
   10398                         Jim_SetResultFormatted(interp, "Can't initialise array element \"%#s\"", nameObjPtr);
   10399                     }
   10400                     Jim_DecrRefCount(interp, nameObjPtr);
   10401                     return JIM_ERR;
   10402 
   10403                 case JIM_OK:
   10404                     if (byref) {
   10405                         vv = nameObjPtr->internalRep.varValue.vv;
   10406                     }
   10407                     else {
   10408                         initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
   10409                     }
   10410                     break;
   10411 
   10412                 case JIM_ERR:
   10413 
   10414                     Jim_SetResultFormatted(interp,
   10415                         "variable for initialization of static \"%#s\" not found in the local context",
   10416                         nameObjPtr);
   10417                     Jim_DecrRefCount(interp, nameObjPtr);
   10418                     return JIM_ERR;
   10419             }
   10420         }
   10421         else {
   10422             initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
   10423         }
   10424 
   10425         if (vv == NULL) {
   10426             vv = Jim_Alloc(sizeof(*vv));
   10427             vv->objPtr = initObjPtr;
   10428             Jim_IncrRefCount(vv->objPtr);
   10429             vv->linkFramePtr = NULL;
   10430             vv->refCount = 0;
   10431         }
   10432 
   10433         if (JimSetNewVariable(cmdPtr->u.proc.staticVars, nameObjPtr, vv) != JIM_OK) {
   10434             Jim_SetResultFormatted(interp,
   10435                 "static variable name \"%#s\" duplicated in statics list", nameObjPtr);
   10436             JimIncrVarRef(vv);
   10437             JimDecrVarRef(interp, vv);
   10438             Jim_DecrRefCount(interp, nameObjPtr);
   10439             return JIM_ERR;
   10440         }
   10441 
   10442         Jim_DecrRefCount(interp, nameObjPtr);
   10443     }
   10444     return JIM_OK;
   10445 }
   10446 
   10447 
   10448 #ifdef jim_ext_namespace
   10449 static const char *Jim_memrchr(const char *p, int c, int len)
   10450 {
   10451     int i;
   10452     for (i = len; i > 0; i--) {
   10453         if (p[i] == c) {
   10454             return p + i;
   10455         }
   10456     }
   10457     return NULL;
   10458 }
   10459 #endif
   10460 
   10461 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *nameObjPtr)
   10462 {
   10463 #ifdef jim_ext_namespace
   10464     if (cmdPtr->isproc) {
   10465         int len;
   10466         const char *cmdname = Jim_GetStringNoQualifier(nameObjPtr, &len);
   10467 
   10468         const char *pt = Jim_memrchr(cmdname, ':', len);
   10469         if (pt && pt != cmdname && pt[-1] == ':') {
   10470             pt++;
   10471             Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
   10472             cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 2);
   10473             Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
   10474 
   10475             Jim_Obj *tempObj = Jim_NewStringObj(interp, pt, len - (pt - cmdname));
   10476             if (Jim_FindHashEntry(&interp->commands, tempObj)) {
   10477 
   10478                 Jim_InterpIncrProcEpoch(interp);
   10479             }
   10480             Jim_FreeNewObj(interp, tempObj);
   10481         }
   10482     }
   10483 #endif
   10484 }
   10485 
   10486 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
   10487     Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
   10488 {
   10489     Jim_Cmd *cmdPtr;
   10490     int argListLen;
   10491     int i;
   10492 
   10493     argListLen = Jim_ListLength(interp, argListObjPtr);
   10494 
   10495 
   10496     cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
   10497     assert(cmdPtr);
   10498     memset(cmdPtr, 0, sizeof(*cmdPtr));
   10499     cmdPtr->inUse = 1;
   10500     cmdPtr->isproc = 1;
   10501     cmdPtr->u.proc.argListObjPtr = argListObjPtr;
   10502     cmdPtr->u.proc.argListLen = argListLen;
   10503     cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
   10504     cmdPtr->u.proc.argsPos = -1;
   10505     cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
   10506     cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
   10507     Jim_IncrRefCount(argListObjPtr);
   10508     Jim_IncrRefCount(bodyObjPtr);
   10509     Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
   10510 
   10511 
   10512     if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
   10513         goto err;
   10514     }
   10515 
   10516 
   10517 
   10518     for (i = 0; i < argListLen; i++) {
   10519         Jim_Obj *argPtr;
   10520         Jim_Obj *nameObjPtr;
   10521         Jim_Obj *defaultObjPtr;
   10522         int len;
   10523 
   10524 
   10525         argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
   10526         len = Jim_ListLength(interp, argPtr);
   10527         if (len == 0) {
   10528             Jim_SetResultString(interp, "argument with no name", -1);
   10529 err:
   10530             JimDecrCmdRefCount(interp, cmdPtr);
   10531             return NULL;
   10532         }
   10533         if (len > 2) {
   10534             Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
   10535             goto err;
   10536         }
   10537 
   10538         if (len == 2) {
   10539 
   10540             nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
   10541             defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
   10542         }
   10543         else {
   10544 
   10545             nameObjPtr = argPtr;
   10546             defaultObjPtr = NULL;
   10547         }
   10548 
   10549 
   10550         if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
   10551             if (cmdPtr->u.proc.argsPos >= 0) {
   10552                 Jim_SetResultString(interp, "'args' specified more than once", -1);
   10553                 goto err;
   10554             }
   10555             cmdPtr->u.proc.argsPos = i;
   10556         }
   10557         else {
   10558             if (len == 2) {
   10559                 cmdPtr->u.proc.optArity++;
   10560             }
   10561             else {
   10562                 cmdPtr->u.proc.reqArity++;
   10563             }
   10564         }
   10565 
   10566         cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
   10567         cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
   10568     }
   10569 
   10570     return cmdPtr;
   10571 }
   10572 
   10573 int Jim_DeleteCommand(Jim_Interp *interp, Jim_Obj *nameObj)
   10574 {
   10575     int ret = JIM_OK;
   10576 
   10577     nameObj = JimQualifyName(interp, nameObj);
   10578 
   10579     if (Jim_DeleteHashEntry(&interp->commands, nameObj) == JIM_ERR) {
   10580         Jim_SetResultFormatted(interp, "can't delete \"%#s\": command doesn't exist", nameObj);
   10581         ret = JIM_ERR;
   10582     }
   10583     Jim_DecrRefCount(interp, nameObj);
   10584 
   10585     return ret;
   10586 }
   10587 
   10588 int Jim_RenameCommand(Jim_Interp *interp, Jim_Obj *oldNameObj, Jim_Obj *newNameObj)
   10589 {
   10590     int ret = JIM_ERR;
   10591     Jim_HashEntry *he;
   10592     Jim_Cmd *cmdPtr;
   10593 
   10594     if (Jim_Length(newNameObj) == 0) {
   10595         return Jim_DeleteCommand(interp, oldNameObj);
   10596     }
   10597 
   10598 
   10599 
   10600     oldNameObj = JimQualifyName(interp, oldNameObj);
   10601     newNameObj = JimQualifyName(interp, newNameObj);
   10602 
   10603 
   10604     he = Jim_FindHashEntry(&interp->commands, oldNameObj);
   10605     if (he == NULL) {
   10606         Jim_SetResultFormatted(interp, "can't rename \"%#s\": command doesn't exist", oldNameObj);
   10607     }
   10608     else if (Jim_FindHashEntry(&interp->commands, newNameObj)) {
   10609         Jim_SetResultFormatted(interp, "can't rename to \"%#s\": command already exists", newNameObj);
   10610     }
   10611     else {
   10612         cmdPtr = Jim_GetHashEntryVal(he);
   10613         if (cmdPtr->prevCmd) {
   10614             Jim_SetResultFormatted(interp, "can't rename local command \"%#s\"", oldNameObj);
   10615         }
   10616         else {
   10617 
   10618             JimIncrCmdRefCount(cmdPtr);
   10619             JimUpdateProcNamespace(interp, cmdPtr, newNameObj);
   10620             Jim_AddHashEntry(&interp->commands, newNameObj, cmdPtr);
   10621 
   10622 
   10623             Jim_DeleteHashEntry(&interp->commands, oldNameObj);
   10624 
   10625 
   10626             Jim_InterpIncrProcEpoch(interp);
   10627 
   10628             ret = JIM_OK;
   10629         }
   10630     }
   10631 
   10632     Jim_DecrRefCount(interp, oldNameObj);
   10633     Jim_DecrRefCount(interp, newNameObj);
   10634 
   10635     return ret;
   10636 }
   10637 
   10638 
   10639 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
   10640 {
   10641     Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
   10642 }
   10643 
   10644 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
   10645 {
   10646     dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
   10647     dupPtr->typePtr = srcPtr->typePtr;
   10648     Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
   10649 }
   10650 
   10651 static const Jim_ObjType commandObjType = {
   10652     "command",
   10653     FreeCommandInternalRep,
   10654     DupCommandInternalRep,
   10655     NULL,
   10656     JIM_TYPE_REFERENCES,
   10657 };
   10658 
   10659 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
   10660 {
   10661     Jim_Cmd *cmd;
   10662 
   10663     if (objPtr->typePtr == &commandObjType
   10664         && objPtr->internalRep.cmdValue.procEpoch == interp->procEpoch
   10665 #ifdef jim_ext_namespace
   10666         && Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
   10667 #endif
   10668         && objPtr->internalRep.cmdValue.cmdPtr->inUse) {
   10669 
   10670         cmd = objPtr->internalRep.cmdValue.cmdPtr;
   10671     }
   10672     else {
   10673         Jim_Obj *qualifiedNameObj = JimQualifyName(interp, objPtr);
   10674         Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, qualifiedNameObj);
   10675 #ifdef jim_ext_namespace
   10676         if (he == NULL && Jim_Length(interp->framePtr->nsObj)) {
   10677             he = Jim_FindHashEntry(&interp->commands, objPtr);
   10678         }
   10679 #endif
   10680         if (he == NULL) {
   10681             if (flags & JIM_ERRMSG) {
   10682                 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
   10683             }
   10684             Jim_DecrRefCount(interp, qualifiedNameObj);
   10685             return NULL;
   10686         }
   10687         cmd = Jim_GetHashEntryVal(he);
   10688 
   10689         cmd->cmdNameObj = Jim_GetHashEntryKey(he);
   10690 
   10691 
   10692         Jim_FreeIntRep(interp, objPtr);
   10693         objPtr->typePtr = &commandObjType;
   10694         objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
   10695         objPtr->internalRep.cmdValue.cmdPtr = cmd;
   10696         objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
   10697         Jim_IncrRefCount(interp->framePtr->nsObj);
   10698         Jim_DecrRefCount(interp, qualifiedNameObj);
   10699     }
   10700     while (cmd->u.proc.upcall) {
   10701         cmd = cmd->prevCmd;
   10702     }
   10703     return cmd;
   10704 }
   10705 
   10706 
   10707 
   10708 static const Jim_ObjType variableObjType = {
   10709     "variable",
   10710     NULL,
   10711     NULL,
   10712     NULL,
   10713     JIM_TYPE_REFERENCES,
   10714 };
   10715 
   10716 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
   10717 {
   10718     const char *varName;
   10719     Jim_CallFrame *framePtr;
   10720     int global;
   10721     int len;
   10722     Jim_VarVal *vv;
   10723 
   10724 
   10725     if (objPtr->typePtr == &variableObjType) {
   10726         framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
   10727         if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
   10728 
   10729             return JIM_OK;
   10730         }
   10731 
   10732     }
   10733     else if (objPtr->typePtr == &dictSubstObjType) {
   10734         return JIM_DICT_SUGAR;
   10735     }
   10736 
   10737     varName = Jim_GetString(objPtr, &len);
   10738 
   10739 
   10740     if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
   10741         return JIM_DICT_SUGAR;
   10742     }
   10743 
   10744     if (varName[0] == ':' && varName[1] == ':') {
   10745         while (*varName == ':') {
   10746             varName++;
   10747             len--;
   10748         }
   10749         global = 1;
   10750         framePtr = interp->topFramePtr;
   10751 
   10752         Jim_Obj *tempObj = Jim_NewStringObj(interp, varName, len);
   10753         vv = JimFindVariable(&framePtr->vars, tempObj);
   10754         Jim_FreeNewObj(interp, tempObj);
   10755     }
   10756     else {
   10757         global = 0;
   10758         framePtr = interp->framePtr;
   10759 
   10760         vv = JimFindVariable(&framePtr->vars, objPtr);
   10761         if (vv == NULL && framePtr->staticVars) {
   10762 
   10763             vv = JimFindVariable(framePtr->staticVars, objPtr);
   10764         }
   10765     }
   10766 
   10767     if (vv == NULL) {
   10768         return JIM_ERR;
   10769     }
   10770 
   10771 
   10772     Jim_FreeIntRep(interp, objPtr);
   10773     objPtr->typePtr = &variableObjType;
   10774     objPtr->internalRep.varValue.callFrameId = framePtr->id;
   10775     objPtr->internalRep.varValue.vv = vv;
   10776     objPtr->internalRep.varValue.global = global;
   10777     return JIM_OK;
   10778 }
   10779 
   10780 
   10781 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
   10782 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
   10783 
   10784 static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv)
   10785 {
   10786     return Jim_AddHashEntry(ht, nameObjPtr, vv);
   10787 }
   10788 
   10789 static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
   10790 {
   10791     Jim_HashEntry *he = Jim_FindHashEntry(ht, nameObjPtr);
   10792     if (he) {
   10793         return (Jim_VarVal *)Jim_GetHashEntryVal(he);
   10794     }
   10795     return NULL;
   10796 }
   10797 
   10798 static int JimUnsetVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
   10799 {
   10800     return Jim_DeleteHashEntry(ht, nameObjPtr);
   10801 }
   10802 
   10803 static Jim_VarVal *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
   10804 {
   10805     const char *name;
   10806     Jim_CallFrame *framePtr;
   10807     int global;
   10808     int len;
   10809 
   10810 
   10811     Jim_VarVal *vv = Jim_Alloc(sizeof(*vv));
   10812 
   10813     vv->objPtr = valObjPtr;
   10814     Jim_IncrRefCount(valObjPtr);
   10815     vv->linkFramePtr = NULL;
   10816     vv->refCount = 0;
   10817 
   10818     name = Jim_GetString(nameObjPtr, &len);
   10819     if (name[0] == ':' && name[1] == ':') {
   10820         while (*name == ':') {
   10821             name++;
   10822             len--;
   10823         }
   10824         framePtr = interp->topFramePtr;
   10825         global = 1;
   10826         JimSetNewVariable(&framePtr->vars, Jim_NewStringObj(interp, name, len), vv);
   10827     }
   10828     else {
   10829         framePtr = interp->framePtr;
   10830         global = 0;
   10831         JimSetNewVariable(&framePtr->vars, nameObjPtr, vv);
   10832     }
   10833 
   10834 
   10835     Jim_FreeIntRep(interp, nameObjPtr);
   10836     nameObjPtr->typePtr = &variableObjType;
   10837     nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
   10838     nameObjPtr->internalRep.varValue.vv = vv;
   10839     nameObjPtr->internalRep.varValue.global = global;
   10840 
   10841     return vv;
   10842 }
   10843 
   10844 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
   10845 {
   10846     int err;
   10847     Jim_VarVal *vv;
   10848 
   10849     switch (SetVariableFromAny(interp, nameObjPtr)) {
   10850         case JIM_DICT_SUGAR:
   10851             return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
   10852 
   10853         case JIM_ERR:
   10854             JimCreateVariable(interp, nameObjPtr, valObjPtr);
   10855             break;
   10856 
   10857         case JIM_OK:
   10858             vv = nameObjPtr->internalRep.varValue.vv;
   10859             if (vv->linkFramePtr == NULL) {
   10860                 Jim_IncrRefCount(valObjPtr);
   10861                 Jim_DecrRefCount(interp, vv->objPtr);
   10862                 vv->objPtr = valObjPtr;
   10863             }
   10864             else {
   10865                 Jim_CallFrame *savedCallFrame;
   10866 
   10867                 savedCallFrame = interp->framePtr;
   10868                 interp->framePtr = vv->linkFramePtr;
   10869                 err = Jim_SetVariable(interp, vv->objPtr, valObjPtr);
   10870                 interp->framePtr = savedCallFrame;
   10871                 if (err != JIM_OK)
   10872                     return err;
   10873             }
   10874     }
   10875     return JIM_OK;
   10876 }
   10877 
   10878 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
   10879 {
   10880     Jim_Obj *nameObjPtr;
   10881     int result;
   10882 
   10883     nameObjPtr = Jim_NewStringObj(interp, name, -1);
   10884     Jim_IncrRefCount(nameObjPtr);
   10885     result = Jim_SetVariable(interp, nameObjPtr, objPtr);
   10886     Jim_DecrRefCount(interp, nameObjPtr);
   10887     return result;
   10888 }
   10889 
   10890 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
   10891 {
   10892     Jim_CallFrame *savedFramePtr;
   10893     int result;
   10894 
   10895     savedFramePtr = interp->framePtr;
   10896     interp->framePtr = interp->topFramePtr;
   10897     result = Jim_SetVariableStr(interp, name, objPtr);
   10898     interp->framePtr = savedFramePtr;
   10899     return result;
   10900 }
   10901 
   10902 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
   10903 {
   10904     Jim_Obj *valObjPtr;
   10905     int result;
   10906 
   10907     valObjPtr = Jim_NewStringObj(interp, val, -1);
   10908     Jim_IncrRefCount(valObjPtr);
   10909     result = Jim_SetVariableStr(interp, name, valObjPtr);
   10910     Jim_DecrRefCount(interp, valObjPtr);
   10911     return result;
   10912 }
   10913 
   10914 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
   10915     Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
   10916 {
   10917     const char *varName;
   10918     const char *targetName;
   10919     Jim_CallFrame *framePtr;
   10920     Jim_VarVal *vv;
   10921     int len;
   10922     int varnamelen;
   10923 
   10924 
   10925     switch (SetVariableFromAny(interp, nameObjPtr)) {
   10926         case JIM_DICT_SUGAR:
   10927 
   10928             Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
   10929             return JIM_ERR;
   10930 
   10931         case JIM_OK:
   10932             vv = nameObjPtr->internalRep.varValue.vv;
   10933 
   10934             if (vv->linkFramePtr == NULL) {
   10935                 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
   10936                 return JIM_ERR;
   10937             }
   10938 
   10939 
   10940             vv->linkFramePtr = NULL;
   10941             break;
   10942     }
   10943 
   10944 
   10945 
   10946     varName = Jim_GetString(nameObjPtr, &varnamelen);
   10947 
   10948     if (varName[0] == ':' && varName[1] == ':') {
   10949         while (*varName == ':') {
   10950             varName++;
   10951             varnamelen--;
   10952         }
   10953 
   10954         framePtr = interp->topFramePtr;
   10955     }
   10956     else {
   10957         framePtr = interp->framePtr;
   10958     }
   10959 
   10960     targetName = Jim_GetString(targetNameObjPtr, &len);
   10961     if (targetName[0] == ':' && targetName[1] == ':') {
   10962         while (*targetName == ':') {
   10963             targetName++;
   10964             len--;
   10965         }
   10966         targetNameObjPtr = Jim_NewStringObj(interp, targetName, len);
   10967         targetCallFrame = interp->topFramePtr;
   10968     }
   10969     Jim_IncrRefCount(targetNameObjPtr);
   10970 
   10971     if (framePtr->level < targetCallFrame->level) {
   10972         Jim_SetResultFormatted(interp,
   10973             "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
   10974             nameObjPtr);
   10975         Jim_DecrRefCount(interp, targetNameObjPtr);
   10976         return JIM_ERR;
   10977     }
   10978 
   10979 
   10980     if (framePtr == targetCallFrame) {
   10981         Jim_Obj *objPtr = targetNameObjPtr;
   10982 
   10983 
   10984         while (1) {
   10985             if (Jim_Length(objPtr) == varnamelen && memcmp(Jim_String(objPtr), varName, varnamelen) == 0) {
   10986                 Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
   10987                 Jim_DecrRefCount(interp, targetNameObjPtr);
   10988                 return JIM_ERR;
   10989             }
   10990             if (SetVariableFromAny(interp, objPtr) != JIM_OK)
   10991                 break;
   10992             vv = objPtr->internalRep.varValue.vv;
   10993             if (vv->linkFramePtr != targetCallFrame)
   10994                 break;
   10995             objPtr = vv->objPtr;
   10996         }
   10997     }
   10998 
   10999 
   11000     Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
   11001 
   11002     nameObjPtr->internalRep.varValue.vv->linkFramePtr = targetCallFrame;
   11003     Jim_DecrRefCount(interp, targetNameObjPtr);
   11004     return JIM_OK;
   11005 }
   11006 
   11007 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
   11008 {
   11009     if (interp->safeexpr) {
   11010         return nameObjPtr;
   11011     }
   11012     switch (SetVariableFromAny(interp, nameObjPtr)) {
   11013         case JIM_OK:{
   11014                 Jim_VarVal *vv = nameObjPtr->internalRep.varValue.vv;
   11015 
   11016                 if (vv->linkFramePtr == NULL) {
   11017                     return vv->objPtr;
   11018                 }
   11019                 else {
   11020                     Jim_Obj *objPtr;
   11021 
   11022 
   11023                     Jim_CallFrame *savedCallFrame = interp->framePtr;
   11024 
   11025                     interp->framePtr = vv->linkFramePtr;
   11026                     objPtr = Jim_GetVariable(interp, vv->objPtr, flags);
   11027                     interp->framePtr = savedCallFrame;
   11028                     if (objPtr) {
   11029                         return objPtr;
   11030                     }
   11031 
   11032                 }
   11033             }
   11034             break;
   11035 
   11036         case JIM_DICT_SUGAR:
   11037 
   11038             return JimDictSugarGet(interp, nameObjPtr, flags);
   11039     }
   11040     if (flags & JIM_ERRMSG) {
   11041         Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
   11042     }
   11043     return NULL;
   11044 }
   11045 
   11046 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
   11047 {
   11048     Jim_CallFrame *savedFramePtr;
   11049     Jim_Obj *objPtr;
   11050 
   11051     savedFramePtr = interp->framePtr;
   11052     interp->framePtr = interp->topFramePtr;
   11053     objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
   11054     interp->framePtr = savedFramePtr;
   11055 
   11056     return objPtr;
   11057 }
   11058 
   11059 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
   11060 {
   11061     Jim_Obj *nameObjPtr, *varObjPtr;
   11062 
   11063     nameObjPtr = Jim_NewStringObj(interp, name, -1);
   11064     Jim_IncrRefCount(nameObjPtr);
   11065     varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
   11066     Jim_DecrRefCount(interp, nameObjPtr);
   11067     return varObjPtr;
   11068 }
   11069 
   11070 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
   11071 {
   11072     Jim_CallFrame *savedFramePtr;
   11073     Jim_Obj *objPtr;
   11074 
   11075     savedFramePtr = interp->framePtr;
   11076     interp->framePtr = interp->topFramePtr;
   11077     objPtr = Jim_GetVariableStr(interp, name, flags);
   11078     interp->framePtr = savedFramePtr;
   11079 
   11080     return objPtr;
   11081 }
   11082 
   11083 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
   11084 {
   11085     Jim_VarVal *vv;
   11086     int retval;
   11087     Jim_CallFrame *framePtr;
   11088 
   11089     retval = SetVariableFromAny(interp, nameObjPtr);
   11090     if (retval == JIM_DICT_SUGAR) {
   11091 
   11092         return JimDictSugarSet(interp, nameObjPtr, NULL);
   11093     }
   11094     else if (retval == JIM_OK) {
   11095         vv = nameObjPtr->internalRep.varValue.vv;
   11096 
   11097 
   11098         if (vv->linkFramePtr) {
   11099             framePtr = interp->framePtr;
   11100             interp->framePtr = vv->linkFramePtr;
   11101             retval = Jim_UnsetVariable(interp, vv->objPtr, JIM_NONE);
   11102             interp->framePtr = framePtr;
   11103         }
   11104         else {
   11105             if (nameObjPtr->internalRep.varValue.global) {
   11106                 int len;
   11107                 const char *name = Jim_GetString(nameObjPtr, &len);
   11108                 while (*name == ':') {
   11109                     name++;
   11110                     len--;
   11111                 }
   11112                 framePtr = interp->topFramePtr;
   11113                 Jim_Obj *tempObj = Jim_NewStringObj(interp, name, len);
   11114                 retval = JimUnsetVariable(&framePtr->vars, tempObj);
   11115                 Jim_FreeNewObj(interp, tempObj);
   11116             }
   11117             else {
   11118                 framePtr = interp->framePtr;
   11119                 retval = JimUnsetVariable(&framePtr->vars, nameObjPtr);
   11120             }
   11121 
   11122             if (retval == JIM_OK) {
   11123 
   11124                 framePtr->id = interp->callFrameEpoch++;
   11125             }
   11126         }
   11127     }
   11128     if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
   11129         Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
   11130     }
   11131     return retval;
   11132 }
   11133 
   11134 
   11135 
   11136 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
   11137     Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
   11138 {
   11139     const char *str, *p;
   11140     int len, keyLen;
   11141     Jim_Obj *varObjPtr, *keyObjPtr;
   11142 
   11143     str = Jim_GetString(objPtr, &len);
   11144 
   11145     p = strchr(str, '(');
   11146     JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
   11147 
   11148     varObjPtr = Jim_NewStringObj(interp, str, p - str);
   11149 
   11150     p++;
   11151     keyLen = (str + len) - p;
   11152     if (str[len - 1] == ')') {
   11153         keyLen--;
   11154     }
   11155 
   11156 
   11157     keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
   11158 
   11159     Jim_IncrRefCount(varObjPtr);
   11160     Jim_IncrRefCount(keyObjPtr);
   11161     *varPtrPtr = varObjPtr;
   11162     *keyPtrPtr = keyObjPtr;
   11163 }
   11164 
   11165 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
   11166 {
   11167     int err;
   11168 
   11169     SetDictSubstFromAny(interp, objPtr);
   11170 
   11171     err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
   11172         &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);
   11173 
   11174     if (err == JIM_OK) {
   11175 
   11176         Jim_SetEmptyResult(interp);
   11177     }
   11178     else {
   11179         if (!valObjPtr) {
   11180 
   11181             if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
   11182                 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
   11183                     objPtr);
   11184                 return err;
   11185             }
   11186         }
   11187 
   11188         Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
   11189             (valObjPtr ? "set" : "unset"), objPtr);
   11190     }
   11191     return err;
   11192 }
   11193 
   11194 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
   11195     Jim_Obj *keyObjPtr, int flags)
   11196 {
   11197     Jim_Obj *dictObjPtr;
   11198     Jim_Obj *resObjPtr = NULL;
   11199     int ret;
   11200 
   11201     dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
   11202     if (!dictObjPtr) {
   11203         return NULL;
   11204     }
   11205 
   11206     ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
   11207     if (ret != JIM_OK) {
   11208         Jim_SetResultFormatted(interp,
   11209             "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
   11210             ret < 0 ? "variable isn't" : "no such element in");
   11211     }
   11212     else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
   11213 
   11214         Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
   11215     }
   11216 
   11217     return resObjPtr;
   11218 }
   11219 
   11220 
   11221 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
   11222 {
   11223     SetDictSubstFromAny(interp, objPtr);
   11224 
   11225     return JimDictExpandArrayVariable(interp,
   11226         objPtr->internalRep.dictSubstValue.varNameObjPtr,
   11227         objPtr->internalRep.dictSubstValue.indexObjPtr, flags);
   11228 }
   11229 
   11230 
   11231 
   11232 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
   11233 {
   11234     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
   11235     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
   11236 }
   11237 
   11238 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
   11239 {
   11240 
   11241     dupPtr->internalRep = srcPtr->internalRep;
   11242 
   11243     Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr);
   11244     Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
   11245 }
   11246 
   11247 
   11248 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
   11249 {
   11250     if (objPtr->typePtr != &dictSubstObjType) {
   11251         Jim_Obj *varObjPtr, *keyObjPtr;
   11252 
   11253         if (objPtr->typePtr == &interpolatedObjType) {
   11254 
   11255 
   11256             varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
   11257             keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
   11258 
   11259             Jim_IncrRefCount(varObjPtr);
   11260             Jim_IncrRefCount(keyObjPtr);
   11261         }
   11262         else {
   11263             JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
   11264         }
   11265 
   11266         Jim_FreeIntRep(interp, objPtr);
   11267         objPtr->typePtr = &dictSubstObjType;
   11268         objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
   11269         objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
   11270     }
   11271 }
   11272 
   11273 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
   11274 {
   11275     Jim_Obj *resObjPtr = NULL;
   11276     Jim_Obj *substKeyObjPtr = NULL;
   11277 
   11278     if (interp->safeexpr) {
   11279         return objPtr;
   11280     }
   11281 
   11282     SetDictSubstFromAny(interp, objPtr);
   11283 
   11284     if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
   11285             &substKeyObjPtr, JIM_NONE)
   11286         != JIM_OK) {
   11287         return NULL;
   11288     }
   11289     Jim_IncrRefCount(substKeyObjPtr);
   11290     resObjPtr =
   11291         JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
   11292         substKeyObjPtr, 0);
   11293     Jim_DecrRefCount(interp, substKeyObjPtr);
   11294 
   11295     return resObjPtr;
   11296 }
   11297 
   11298 
   11299 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
   11300 {
   11301     Jim_CallFrame *cf;
   11302 
   11303     if (interp->freeFramesList) {
   11304         cf = interp->freeFramesList;
   11305         interp->freeFramesList = cf->next;
   11306 
   11307         cf->argv = NULL;
   11308         cf->argc = 0;
   11309         cf->procArgsObjPtr = NULL;
   11310         cf->procBodyObjPtr = NULL;
   11311         cf->next = NULL;
   11312         cf->staticVars = NULL;
   11313         cf->localCommands = NULL;
   11314         cf->tailcallObj = NULL;
   11315         cf->tailcallCmd = NULL;
   11316     }
   11317     else {
   11318         cf = Jim_Alloc(sizeof(*cf));
   11319         memset(cf, 0, sizeof(*cf));
   11320 
   11321         Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
   11322     }
   11323 
   11324     cf->id = interp->callFrameEpoch++;
   11325     cf->parent = parent;
   11326     cf->level = parent ? parent->level + 1 : 0;
   11327     cf->nsObj = nsObj;
   11328     Jim_IncrRefCount(nsObj);
   11329 
   11330     return cf;
   11331 }
   11332 
   11333 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
   11334 {
   11335 
   11336     if (localCommands) {
   11337         Jim_Obj *cmdNameObj;
   11338 
   11339         while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
   11340             Jim_HashTable *ht = &interp->commands;
   11341             Jim_HashEntry *he = Jim_FindHashEntry(ht, cmdNameObj);
   11342             if (he) {
   11343                 Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
   11344                 if (cmd->prevCmd) {
   11345                     Jim_Cmd *prevCmd = cmd->prevCmd;
   11346                     cmd->prevCmd = NULL;
   11347 
   11348 
   11349                     JimDecrCmdRefCount(interp, cmd);
   11350 
   11351 
   11352                     Jim_SetHashVal(ht, he, prevCmd);
   11353                 }
   11354                 else {
   11355                     Jim_DeleteHashEntry(ht, cmdNameObj);
   11356                 }
   11357             }
   11358             Jim_DecrRefCount(interp, cmdNameObj);
   11359         }
   11360         Jim_FreeStack(localCommands);
   11361         Jim_Free(localCommands);
   11362     }
   11363     return JIM_OK;
   11364 }
   11365 
   11366 static int JimInvokeDefer(Jim_Interp *interp, int retcode)
   11367 {
   11368     Jim_Obj *objPtr;
   11369 
   11370 
   11371     if (JimFindVariable(&interp->framePtr->vars, interp->defer) == NULL) {
   11372         return retcode;
   11373     }
   11374     objPtr = Jim_GetVariable(interp, interp->defer, JIM_NONE);
   11375 
   11376     if (objPtr) {
   11377         int ret = JIM_OK;
   11378         int i;
   11379         int listLen = Jim_ListLength(interp, objPtr);
   11380         Jim_Obj *resultObjPtr;
   11381 
   11382         Jim_IncrRefCount(objPtr);
   11383 
   11384         resultObjPtr = Jim_GetResult(interp);
   11385         Jim_IncrRefCount(resultObjPtr);
   11386         Jim_SetEmptyResult(interp);
   11387 
   11388 
   11389         for (i = listLen; i > 0; i--) {
   11390 
   11391             Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1);
   11392             ret = Jim_EvalObj(interp, scriptObjPtr);
   11393             if (ret != JIM_OK) {
   11394                 break;
   11395             }
   11396         }
   11397 
   11398         if (ret == JIM_OK || retcode == JIM_ERR) {
   11399 
   11400             Jim_SetResult(interp, resultObjPtr);
   11401         }
   11402         else {
   11403             retcode = ret;
   11404         }
   11405 
   11406         Jim_DecrRefCount(interp, resultObjPtr);
   11407         Jim_DecrRefCount(interp, objPtr);
   11408     }
   11409     return retcode;
   11410 }
   11411 
   11412 #define JIM_FCF_FULL 0
   11413 #define JIM_FCF_REUSE 1
   11414 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
   11415  {
   11416     JimDeleteLocalProcs(interp, cf->localCommands);
   11417 
   11418     if (cf->procArgsObjPtr)
   11419         Jim_DecrRefCount(interp, cf->procArgsObjPtr);
   11420     if (cf->procBodyObjPtr)
   11421         Jim_DecrRefCount(interp, cf->procBodyObjPtr);
   11422     Jim_DecrRefCount(interp, cf->nsObj);
   11423     if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
   11424         Jim_FreeHashTable(&cf->vars);
   11425     else {
   11426         Jim_ClearHashTable(&cf->vars);
   11427     }
   11428     cf->next = interp->freeFramesList;
   11429     interp->freeFramesList = cf;
   11430 }
   11431 
   11432 
   11433 
   11434 int Jim_IsBigEndian(void)
   11435 {
   11436     union {
   11437         unsigned short s;
   11438         unsigned char c[2];
   11439     } uval = {0x0102};
   11440 
   11441     return uval.c[0] == 1;
   11442 }
   11443 
   11444 
   11445 Jim_Interp *Jim_CreateInterp(void)
   11446 {
   11447     Jim_Interp *i = Jim_Alloc(sizeof(*i));
   11448 
   11449     memset(i, 0, sizeof(*i));
   11450 
   11451     i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
   11452     i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
   11453     i->lastCollectTime = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
   11454 
   11455     Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
   11456 #ifdef JIM_REFERENCES
   11457     Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
   11458 #endif
   11459     Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
   11460     Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
   11461     i->emptyObj = Jim_NewEmptyStringObj(i);
   11462     i->trueObj = Jim_NewIntObj(i, 1);
   11463     i->falseObj = Jim_NewIntObj(i, 0);
   11464     i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
   11465     i->result = i->emptyObj;
   11466     i->stackTrace = Jim_NewListObj(i, NULL, 0);
   11467     i->unknown = Jim_NewStringObj(i, "unknown", -1);
   11468     i->defer = Jim_NewStringObj(i, "jim::defer", -1);
   11469     i->errorProc = i->emptyObj;
   11470     i->nullScriptObj = Jim_NewEmptyStringObj(i);
   11471     i->evalFrame = &i->topEvalFrame;
   11472     i->currentFilenameObj = Jim_NewEmptyStringObj(i);
   11473     Jim_IncrRefCount(i->emptyObj);
   11474     Jim_IncrRefCount(i->result);
   11475     Jim_IncrRefCount(i->stackTrace);
   11476     Jim_IncrRefCount(i->unknown);
   11477     Jim_IncrRefCount(i->defer);
   11478     Jim_IncrRefCount(i->nullScriptObj);
   11479     Jim_IncrRefCount(i->errorProc);
   11480     Jim_IncrRefCount(i->trueObj);
   11481     Jim_IncrRefCount(i->falseObj);
   11482     Jim_IncrRefCount(i->currentFilenameObj);
   11483 
   11484 
   11485     Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
   11486     Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
   11487 
   11488     Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
   11489     Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
   11490     Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
   11491     Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
   11492     Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
   11493     Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
   11494     Jim_SetVariableStrWithStr(i, "tcl_platform(bootstrap)", "0");
   11495     Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
   11496     Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
   11497     Jim_SetVariableStr(i, "tcl_platform(stackFormat)", Jim_NewIntObj(i, 4));
   11498 
   11499     return i;
   11500 }
   11501 
   11502 void Jim_FreeInterp(Jim_Interp *i)
   11503 {
   11504     Jim_CallFrame *cf, *cfx;
   11505 
   11506     Jim_Obj *objPtr, *nextObjPtr;
   11507 
   11508     i->quitting = 1;
   11509 
   11510 
   11511     for (cf = i->framePtr; cf; cf = cfx) {
   11512 
   11513         JimInvokeDefer(i, JIM_OK);
   11514         cfx = cf->parent;
   11515         JimFreeCallFrame(i, cf, JIM_FCF_FULL);
   11516     }
   11517 
   11518 
   11519     Jim_FreeHashTable(&i->commands);
   11520 
   11521     Jim_DecrRefCount(i, i->emptyObj);
   11522     Jim_DecrRefCount(i, i->trueObj);
   11523     Jim_DecrRefCount(i, i->falseObj);
   11524     Jim_DecrRefCount(i, i->result);
   11525     Jim_DecrRefCount(i, i->stackTrace);
   11526     Jim_DecrRefCount(i, i->errorProc);
   11527     Jim_DecrRefCount(i, i->unknown);
   11528     Jim_DecrRefCount(i, i->defer);
   11529     Jim_DecrRefCount(i, i->nullScriptObj);
   11530     Jim_DecrRefCount(i, i->currentFilenameObj);
   11531 
   11532 
   11533     Jim_InterpIncrProcEpoch(i);
   11534 
   11535 #ifdef JIM_REFERENCES
   11536     Jim_FreeHashTable(&i->references);
   11537 #endif
   11538     Jim_FreeHashTable(&i->packages);
   11539     Jim_Free(i->prngState);
   11540     Jim_FreeHashTable(&i->assocData);
   11541     if (i->traceCmdObj) {
   11542         Jim_DecrRefCount(i, i->traceCmdObj);
   11543     }
   11544 
   11545 #ifdef JIM_MAINTAINER
   11546     if (i->liveList != NULL) {
   11547         objPtr = i->liveList;
   11548 
   11549         printf("\n-------------------------------------\n");
   11550         printf("Objects still in the free list:\n");
   11551         while (objPtr) {
   11552             const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
   11553             Jim_String(objPtr);
   11554 
   11555             if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
   11556                 printf("%p (%d) %-10s: '%.20s...'\n",
   11557                     (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
   11558             }
   11559             else {
   11560                 printf("%p (%d) %-10s: '%s'\n",
   11561                     (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
   11562             }
   11563             if (objPtr->typePtr == &sourceObjType) {
   11564                 printf("FILE %s LINE %d\n",
   11565                     Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
   11566                     objPtr->internalRep.sourceValue.lineNumber);
   11567             }
   11568             objPtr = objPtr->nextObjPtr;
   11569         }
   11570         printf("-------------------------------------\n\n");
   11571         JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
   11572     }
   11573 #endif
   11574 
   11575 
   11576     objPtr = i->freeList;
   11577     while (objPtr) {
   11578         nextObjPtr = objPtr->nextObjPtr;
   11579         Jim_Free(objPtr);
   11580         objPtr = nextObjPtr;
   11581     }
   11582 
   11583 
   11584     for (cf = i->freeFramesList; cf; cf = cfx) {
   11585         cfx = cf->next;
   11586         if (cf->vars.table)
   11587             Jim_FreeHashTable(&cf->vars);
   11588         Jim_Free(cf);
   11589     }
   11590 
   11591 
   11592     Jim_Free(i);
   11593 }
   11594 
   11595 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
   11596 {
   11597     long level;
   11598     const char *str;
   11599     Jim_CallFrame *framePtr;
   11600 
   11601     if (levelObjPtr) {
   11602         str = Jim_String(levelObjPtr);
   11603         if (str[0] == '#') {
   11604             char *endptr;
   11605 
   11606             level = jim_strtol(str + 1, &endptr);
   11607             if (str[1] == '\0' || endptr[0] != '\0') {
   11608                 level = -1;
   11609             }
   11610         }
   11611         else {
   11612             if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
   11613                 level = -1;
   11614             }
   11615             else {
   11616 
   11617                 level = interp->framePtr->level - level;
   11618             }
   11619         }
   11620     }
   11621     else {
   11622         str = "1";
   11623         level = interp->framePtr->level - 1;
   11624     }
   11625 
   11626     if (level == 0) {
   11627         return interp->topFramePtr;
   11628     }
   11629     if (level > 0) {
   11630 
   11631         for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
   11632             if (framePtr->level == level) {
   11633                 return framePtr;
   11634             }
   11635         }
   11636     }
   11637 
   11638     Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
   11639     return NULL;
   11640 }
   11641 
   11642 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, long level)
   11643 {
   11644     Jim_CallFrame *framePtr;
   11645 
   11646     if (level == 0) {
   11647         return interp->framePtr;
   11648     }
   11649 
   11650     if (level < 0) {
   11651 
   11652         level = interp->framePtr->level + level;
   11653     }
   11654 
   11655     if (level > 0) {
   11656 
   11657         for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
   11658             if (framePtr->level == level) {
   11659                 return framePtr;
   11660             }
   11661         }
   11662     }
   11663     return NULL;
   11664 }
   11665 
   11666 static Jim_EvalFrame *JimGetEvalFrameByProcLevel(Jim_Interp *interp, int proclevel)
   11667 {
   11668     Jim_EvalFrame *evalFrame;
   11669 
   11670     if (proclevel == 0) {
   11671         return interp->evalFrame;
   11672     }
   11673 
   11674     if (proclevel < 0) {
   11675 
   11676         proclevel = interp->procLevel + proclevel;
   11677     }
   11678 
   11679     if (proclevel >= 0) {
   11680 
   11681         for (evalFrame = interp->evalFrame; evalFrame; evalFrame = evalFrame->parent) {
   11682             if (evalFrame->procLevel == proclevel) {
   11683                 return evalFrame;
   11684             }
   11685         }
   11686     }
   11687     return NULL;
   11688 }
   11689 
   11690 static Jim_Obj *JimProcForEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame)
   11691 {
   11692     if (frame == interp->evalFrame || (frame->cmd && frame->cmd->cmdNameObj)) {
   11693         Jim_EvalFrame *e;
   11694         for (e = frame->parent; e; e = e->parent) {
   11695             if (e->cmd && e->cmd->isproc && e->cmd->cmdNameObj) {
   11696                 break;
   11697             }
   11698         }
   11699         if (e && e->cmd && e->cmd->cmdNameObj) {
   11700             return e->cmd->cmdNameObj;
   11701         }
   11702     }
   11703     return NULL;
   11704 }
   11705 
   11706 static void JimAddStackFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *listObj)
   11707 {
   11708     Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
   11709     Jim_Obj *fileNameObj = interp->emptyObj;
   11710     int linenr = 1;
   11711 
   11712     if (frame->scriptObj) {
   11713         ScriptObj *script = JimGetScript(interp, frame->scriptObj);
   11714         fileNameObj = script->fileNameObj;
   11715         linenr = script->linenr;
   11716     }
   11717 
   11718     Jim_ListAppendElement(interp, listObj, procNameObj ? procNameObj : interp->emptyObj);
   11719     Jim_ListAppendElement(interp, listObj, fileNameObj);
   11720     Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, linenr));
   11721     Jim_ListAppendElement(interp, listObj, Jim_NewListObj(interp, frame->argv, frame->argc));
   11722 }
   11723 
   11724 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
   11725 {
   11726 
   11727     Jim_IncrRefCount(stackTraceObj);
   11728     Jim_DecrRefCount(interp, interp->stackTrace);
   11729     interp->stackTrace = stackTraceObj;
   11730     interp->errorFlag = 1;
   11731 }
   11732 
   11733 static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script)
   11734 {
   11735     if (!interp->errorFlag) {
   11736         int i;
   11737         Jim_Obj *stackTrace = Jim_NewListObj(interp, NULL, 0);
   11738 
   11739         if (interp->procLevel == 0 && script) {
   11740             Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
   11741             Jim_ListAppendElement(interp, stackTrace, script->fileNameObj);
   11742             Jim_ListAppendElement(interp, stackTrace, Jim_NewIntObj(interp, script->linenr));
   11743             Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
   11744         }
   11745         else {
   11746             for (i = 0; i <= interp->procLevel; i++) {
   11747                 Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
   11748                 if (frame) {
   11749                     JimAddStackFrame(interp, frame, stackTrace);
   11750                 }
   11751             }
   11752         }
   11753         JimSetStackTrace(interp, stackTrace);
   11754     }
   11755 }
   11756 
   11757 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
   11758     void *data)
   11759 {
   11760     AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
   11761 
   11762     assocEntryPtr->delProc = delProc;
   11763     assocEntryPtr->data = data;
   11764     return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
   11765 }
   11766 
   11767 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
   11768 {
   11769     Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
   11770 
   11771     if (entryPtr != NULL) {
   11772         AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr);
   11773         return assocEntryPtr->data;
   11774     }
   11775     return NULL;
   11776 }
   11777 
   11778 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
   11779 {
   11780     return Jim_DeleteHashEntry(&interp->assocData, key);
   11781 }
   11782 
   11783 int Jim_GetExitCode(Jim_Interp *interp)
   11784 {
   11785     return interp->exitCode;
   11786 }
   11787 
   11788 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
   11789 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
   11790 
   11791 static const Jim_ObjType intObjType = {
   11792     "int",
   11793     NULL,
   11794     NULL,
   11795     UpdateStringOfInt,
   11796     JIM_TYPE_NONE,
   11797 };
   11798 
   11799 static const Jim_ObjType coercedDoubleObjType = {
   11800     "coerced-double",
   11801     NULL,
   11802     NULL,
   11803     UpdateStringOfInt,
   11804     JIM_TYPE_NONE,
   11805 };
   11806 
   11807 
   11808 static void UpdateStringOfInt(struct Jim_Obj *objPtr)
   11809 {
   11810     char buf[JIM_INTEGER_SPACE + 1];
   11811     jim_wide wideValue = JimWideValue(objPtr);
   11812     int pos = 0;
   11813 
   11814     if (wideValue == 0) {
   11815         buf[pos++] = '0';
   11816     }
   11817     else {
   11818         char tmp[JIM_INTEGER_SPACE];
   11819         int num = 0;
   11820         int i;
   11821 
   11822         if (wideValue < 0) {
   11823             buf[pos++] = '-';
   11824             i = wideValue % 10;
   11825             tmp[num++] = (i > 0) ? (10 - i) : -i;
   11826             wideValue /= -10;
   11827         }
   11828 
   11829         while (wideValue) {
   11830             tmp[num++] = wideValue % 10;
   11831             wideValue /= 10;
   11832         }
   11833 
   11834         for (i = 0; i < num; i++) {
   11835             buf[pos++] = '0' + tmp[num - i - 1];
   11836         }
   11837     }
   11838     buf[pos] = 0;
   11839 
   11840     JimSetStringBytes(objPtr, buf);
   11841 }
   11842 
   11843 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
   11844 {
   11845     jim_wide wideValue;
   11846     const char *str;
   11847 
   11848     if (objPtr->typePtr == &coercedDoubleObjType) {
   11849 
   11850         objPtr->typePtr = &intObjType;
   11851         return JIM_OK;
   11852     }
   11853 
   11854 
   11855     str = Jim_String(objPtr);
   11856 
   11857     if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
   11858         if (flags & JIM_ERRMSG) {
   11859             Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
   11860         }
   11861         return JIM_ERR;
   11862     }
   11863     if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
   11864         Jim_SetResultString(interp, "Integer value too big to be represented", -1);
   11865         return JIM_ERR;
   11866     }
   11867 
   11868     Jim_FreeIntRep(interp, objPtr);
   11869     objPtr->typePtr = &intObjType;
   11870     objPtr->internalRep.wideValue = wideValue;
   11871     return JIM_OK;
   11872 }
   11873 
   11874 #ifdef JIM_OPTIMIZATION
   11875 static int JimIsWide(Jim_Obj *objPtr)
   11876 {
   11877     return objPtr->typePtr == &intObjType;
   11878 }
   11879 #endif
   11880 
   11881 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
   11882 {
   11883     if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
   11884         return JIM_ERR;
   11885     *widePtr = JimWideValue(objPtr);
   11886     return JIM_OK;
   11887 }
   11888 
   11889 int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
   11890 {
   11891     int ret = JIM_OK;
   11892 
   11893     if (objPtr->typePtr == &sourceObjType || objPtr->typePtr == NULL) {
   11894         SetIntFromAny(interp, objPtr, 0);
   11895     }
   11896     if (objPtr->typePtr == &intObjType) {
   11897         *widePtr = JimWideValue(objPtr);
   11898     }
   11899     else {
   11900         JimPanic((interp->safeexpr, "interp->safeexpr is set"));
   11901         interp->safeexpr++;
   11902         ret = Jim_EvalExpression(interp, objPtr);
   11903         interp->safeexpr--;
   11904 
   11905         if (ret == JIM_OK) {
   11906             ret = Jim_GetWide(interp, Jim_GetResult(interp), widePtr);
   11907         }
   11908         if (ret != JIM_OK) {
   11909             Jim_SetResultFormatted(interp, "expected integer expression but got \"%#s\"", objPtr);
   11910         }
   11911     }
   11912     return ret;
   11913 }
   11914 
   11915 
   11916 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
   11917 {
   11918     if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
   11919         return JIM_ERR;
   11920     *widePtr = JimWideValue(objPtr);
   11921     return JIM_OK;
   11922 }
   11923 
   11924 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
   11925 {
   11926     jim_wide wideValue;
   11927     int retval;
   11928 
   11929     retval = Jim_GetWide(interp, objPtr, &wideValue);
   11930     if (retval == JIM_OK) {
   11931         *longPtr = (long)wideValue;
   11932         return JIM_OK;
   11933     }
   11934     return JIM_ERR;
   11935 }
   11936 
   11937 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
   11938 {
   11939     Jim_Obj *objPtr;
   11940 
   11941     objPtr = Jim_NewObj(interp);
   11942     objPtr->typePtr = &intObjType;
   11943     objPtr->bytes = NULL;
   11944     objPtr->internalRep.wideValue = wideValue;
   11945     return objPtr;
   11946 }
   11947 
   11948 #define JIM_DOUBLE_SPACE 30
   11949 
   11950 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
   11951 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
   11952 
   11953 static const Jim_ObjType doubleObjType = {
   11954     "double",
   11955     NULL,
   11956     NULL,
   11957     UpdateStringOfDouble,
   11958     JIM_TYPE_NONE,
   11959 };
   11960 
   11961 #if !HAVE_DECL_ISNAN
   11962 #undef isnan
   11963 #define isnan(X) ((X) != (X))
   11964 #endif
   11965 #if !HAVE_DECL_ISINF
   11966 #undef isinf
   11967 #define isinf(X) (1.0 / (X) == 0.0)
   11968 #endif
   11969 
   11970 static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
   11971 {
   11972     double value = objPtr->internalRep.doubleValue;
   11973 
   11974     if (isnan(value)) {
   11975         JimSetStringBytes(objPtr, "NaN");
   11976         return;
   11977     }
   11978     if (isinf(value)) {
   11979         if (value < 0) {
   11980             JimSetStringBytes(objPtr, "-Inf");
   11981         }
   11982         else {
   11983             JimSetStringBytes(objPtr, "Inf");
   11984         }
   11985         return;
   11986     }
   11987     {
   11988         char buf[JIM_DOUBLE_SPACE + 1];
   11989         int i;
   11990         int len = sprintf(buf, "%.12g", value);
   11991 
   11992 
   11993         for (i = 0; i < len; i++) {
   11994             if (buf[i] == '.' || buf[i] == 'e') {
   11995 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
   11996                 char *e = strchr(buf, 'e');
   11997                 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
   11998 
   11999                     e += 2;
   12000                     memmove(e, e + 1, len - (e - buf));
   12001                 }
   12002 #endif
   12003                 break;
   12004             }
   12005         }
   12006         if (buf[i] == '\0') {
   12007             buf[i++] = '.';
   12008             buf[i++] = '0';
   12009             buf[i] = '\0';
   12010         }
   12011         JimSetStringBytes(objPtr, buf);
   12012     }
   12013 }
   12014 
   12015 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
   12016 {
   12017     double doubleValue;
   12018     jim_wide wideValue;
   12019     const char *str;
   12020 
   12021 #ifdef HAVE_LONG_LONG
   12022 
   12023 #define MIN_INT_IN_DOUBLE -(1LL << 53)
   12024 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)
   12025 
   12026     if (objPtr->typePtr == &intObjType
   12027         && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
   12028         && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {
   12029 
   12030 
   12031         objPtr->typePtr = &coercedDoubleObjType;
   12032         return JIM_OK;
   12033     }
   12034 #endif
   12035     str = Jim_String(objPtr);
   12036 
   12037     if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
   12038 
   12039         Jim_FreeIntRep(interp, objPtr);
   12040         objPtr->typePtr = &coercedDoubleObjType;
   12041         objPtr->internalRep.wideValue = wideValue;
   12042         return JIM_OK;
   12043     }
   12044     else {
   12045 
   12046         if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
   12047             Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
   12048             return JIM_ERR;
   12049         }
   12050 
   12051         Jim_FreeIntRep(interp, objPtr);
   12052     }
   12053     objPtr->typePtr = &doubleObjType;
   12054     objPtr->internalRep.doubleValue = doubleValue;
   12055     return JIM_OK;
   12056 }
   12057 
   12058 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
   12059 {
   12060     if (objPtr->typePtr == &coercedDoubleObjType) {
   12061         *doublePtr = JimWideValue(objPtr);
   12062         return JIM_OK;
   12063     }
   12064     if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
   12065         return JIM_ERR;
   12066 
   12067     if (objPtr->typePtr == &coercedDoubleObjType) {
   12068         *doublePtr = JimWideValue(objPtr);
   12069     }
   12070     else {
   12071         *doublePtr = objPtr->internalRep.doubleValue;
   12072     }
   12073     return JIM_OK;
   12074 }
   12075 
   12076 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
   12077 {
   12078     Jim_Obj *objPtr;
   12079 
   12080     objPtr = Jim_NewObj(interp);
   12081     objPtr->typePtr = &doubleObjType;
   12082     objPtr->bytes = NULL;
   12083     objPtr->internalRep.doubleValue = doubleValue;
   12084     return objPtr;
   12085 }
   12086 
   12087 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
   12088 
   12089 int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
   12090 {
   12091     if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
   12092         return JIM_ERR;
   12093     *booleanPtr = (int) JimWideValue(objPtr);
   12094     return JIM_OK;
   12095 }
   12096 
   12097 static const char * const jim_true_false_strings[8] = {
   12098     "1", "true", "yes", "on",
   12099     "0", "false", "no", "off"
   12100 };
   12101 
   12102 static const int jim_true_false_lens[8] = {
   12103     1, 4, 3, 2,
   12104     1, 5, 2, 3,
   12105 };
   12106 
   12107 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
   12108 {
   12109     int index = Jim_FindByName(Jim_String(objPtr), jim_true_false_strings,
   12110         sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings));
   12111     if (index < 0) {
   12112         if (flags & JIM_ERRMSG) {
   12113             Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr);
   12114         }
   12115         return JIM_ERR;
   12116     }
   12117 
   12118 
   12119     Jim_FreeIntRep(interp, objPtr);
   12120     objPtr->typePtr = &intObjType;
   12121 
   12122     objPtr->internalRep.wideValue = index < 4 ? 1 : 0;
   12123     return JIM_OK;
   12124 }
   12125 
   12126 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
   12127 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
   12128 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
   12129 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
   12130 static void UpdateStringOfList(struct Jim_Obj *objPtr);
   12131 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
   12132 
   12133 static const Jim_ObjType listObjType = {
   12134     "list",
   12135     FreeListInternalRep,
   12136     DupListInternalRep,
   12137     UpdateStringOfList,
   12138     JIM_TYPE_NONE,
   12139 };
   12140 
   12141 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
   12142 {
   12143     int i;
   12144 
   12145     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
   12146         Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
   12147     }
   12148     Jim_Free(objPtr->internalRep.listValue.ele);
   12149 }
   12150 
   12151 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
   12152 {
   12153     int i;
   12154 
   12155     JIM_NOTUSED(interp);
   12156 
   12157     dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
   12158     dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
   12159     dupPtr->internalRep.listValue.ele =
   12160         Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
   12161     memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
   12162         sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
   12163     for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
   12164         Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
   12165     }
   12166     dupPtr->typePtr = &listObjType;
   12167 }
   12168 
   12169 #define JIM_ELESTR_SIMPLE 0
   12170 #define JIM_ELESTR_BRACE 1
   12171 #define JIM_ELESTR_QUOTE 2
   12172 static unsigned char ListElementQuotingType(const char *s, int len)
   12173 {
   12174     int i, level, blevel, trySimple = 1;
   12175 
   12176 
   12177     if (len == 0)
   12178         return JIM_ELESTR_BRACE;
   12179     if (s[0] == '"' || s[0] == '{') {
   12180         trySimple = 0;
   12181         goto testbrace;
   12182     }
   12183     for (i = 0; i < len; i++) {
   12184         switch (s[i]) {
   12185             case ' ':
   12186             case '$':
   12187             case '"':
   12188             case '[':
   12189             case ']':
   12190             case ';':
   12191             case '\\':
   12192             case '\r':
   12193             case '\n':
   12194             case '\t':
   12195             case '\f':
   12196             case '\v':
   12197                 trySimple = 0;
   12198 
   12199             case '{':
   12200             case '}':
   12201                 goto testbrace;
   12202         }
   12203     }
   12204     return JIM_ELESTR_SIMPLE;
   12205 
   12206   testbrace:
   12207 
   12208     if (s[len - 1] == '\\')
   12209         return JIM_ELESTR_QUOTE;
   12210     level = 0;
   12211     blevel = 0;
   12212     for (i = 0; i < len; i++) {
   12213         switch (s[i]) {
   12214             case '{':
   12215                 level++;
   12216                 break;
   12217             case '}':
   12218                 level--;
   12219                 if (level < 0)
   12220                     return JIM_ELESTR_QUOTE;
   12221                 break;
   12222             case '[':
   12223                 blevel++;
   12224                 break;
   12225             case ']':
   12226                 blevel--;
   12227                 break;
   12228             case '\\':
   12229                 if (s[i + 1] == '\n')
   12230                     return JIM_ELESTR_QUOTE;
   12231                 else if (s[i + 1] != '\0')
   12232                     i++;
   12233                 break;
   12234         }
   12235     }
   12236     if (blevel < 0) {
   12237         return JIM_ELESTR_QUOTE;
   12238     }
   12239 
   12240     if (level == 0) {
   12241         if (!trySimple)
   12242             return JIM_ELESTR_BRACE;
   12243         for (i = 0; i < len; i++) {
   12244             switch (s[i]) {
   12245                 case ' ':
   12246                 case '$':
   12247                 case '"':
   12248                 case '[':
   12249                 case ']':
   12250                 case ';':
   12251                 case '\\':
   12252                 case '\r':
   12253                 case '\n':
   12254                 case '\t':
   12255                 case '\f':
   12256                 case '\v':
   12257                     return JIM_ELESTR_BRACE;
   12258                     break;
   12259             }
   12260         }
   12261         return JIM_ELESTR_SIMPLE;
   12262     }
   12263     return JIM_ELESTR_QUOTE;
   12264 }
   12265 
   12266 static int BackslashQuoteString(const char *s, int len, char *q)
   12267 {
   12268     char *p = q;
   12269 
   12270     while (len--) {
   12271         switch (*s) {
   12272             case ' ':
   12273             case '$':
   12274             case '"':
   12275             case '[':
   12276             case ']':
   12277             case '{':
   12278             case '}':
   12279             case ';':
   12280             case '\\':
   12281                 *p++ = '\\';
   12282                 *p++ = *s++;
   12283                 break;
   12284             case '\n':
   12285                 *p++ = '\\';
   12286                 *p++ = 'n';
   12287                 s++;
   12288                 break;
   12289             case '\r':
   12290                 *p++ = '\\';
   12291                 *p++ = 'r';
   12292                 s++;
   12293                 break;
   12294             case '\t':
   12295                 *p++ = '\\';
   12296                 *p++ = 't';
   12297                 s++;
   12298                 break;
   12299             case '\f':
   12300                 *p++ = '\\';
   12301                 *p++ = 'f';
   12302                 s++;
   12303                 break;
   12304             case '\v':
   12305                 *p++ = '\\';
   12306                 *p++ = 'v';
   12307                 s++;
   12308                 break;
   12309             default:
   12310                 *p++ = *s++;
   12311                 break;
   12312         }
   12313     }
   12314     *p = '\0';
   12315 
   12316     return p - q;
   12317 }
   12318 
   12319 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
   12320 {
   12321     #define STATIC_QUOTING_LEN 32
   12322     int i, bufLen, realLength;
   12323     const char *strRep;
   12324     char *p;
   12325     unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
   12326 
   12327 
   12328     if (objc > STATIC_QUOTING_LEN) {
   12329         quotingType = Jim_Alloc(objc);
   12330     }
   12331     else {
   12332         quotingType = staticQuoting;
   12333     }
   12334     bufLen = 0;
   12335     for (i = 0; i < objc; i++) {
   12336         int len;
   12337 
   12338         strRep = Jim_GetString(objv[i], &len);
   12339         quotingType[i] = ListElementQuotingType(strRep, len);
   12340         switch (quotingType[i]) {
   12341             case JIM_ELESTR_SIMPLE:
   12342                 if (i != 0 || strRep[0] != '#') {
   12343                     bufLen += len;
   12344                     break;
   12345                 }
   12346 
   12347                 quotingType[i] = JIM_ELESTR_BRACE;
   12348 
   12349             case JIM_ELESTR_BRACE:
   12350                 bufLen += len + 2;
   12351                 break;
   12352             case JIM_ELESTR_QUOTE:
   12353                 bufLen += len * 2;
   12354                 break;
   12355         }
   12356         bufLen++;
   12357     }
   12358     bufLen++;
   12359 
   12360 
   12361     p = objPtr->bytes = Jim_Alloc(bufLen + 1);
   12362     realLength = 0;
   12363     for (i = 0; i < objc; i++) {
   12364         int len, qlen;
   12365 
   12366         strRep = Jim_GetString(objv[i], &len);
   12367 
   12368         switch (quotingType[i]) {
   12369             case JIM_ELESTR_SIMPLE:
   12370                 memcpy(p, strRep, len);
   12371                 p += len;
   12372                 realLength += len;
   12373                 break;
   12374             case JIM_ELESTR_BRACE:
   12375                 *p++ = '{';
   12376                 memcpy(p, strRep, len);
   12377                 p += len;
   12378                 *p++ = '}';
   12379                 realLength += len + 2;
   12380                 break;
   12381             case JIM_ELESTR_QUOTE:
   12382                 if (i == 0 && strRep[0] == '#') {
   12383                     *p++ = '\\';
   12384                     realLength++;
   12385                 }
   12386                 qlen = BackslashQuoteString(strRep, len, p);
   12387                 p += qlen;
   12388                 realLength += qlen;
   12389                 break;
   12390         }
   12391 
   12392         if (i + 1 != objc) {
   12393             *p++ = ' ';
   12394             realLength++;
   12395         }
   12396     }
   12397     *p = '\0';
   12398     objPtr->length = realLength;
   12399 
   12400     if (quotingType != staticQuoting) {
   12401         Jim_Free(quotingType);
   12402     }
   12403 }
   12404 
   12405 static void UpdateStringOfList(struct Jim_Obj *objPtr)
   12406 {
   12407     JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
   12408 }
   12409 
   12410 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
   12411 {
   12412     struct JimParserCtx parser;
   12413     const char *str;
   12414     int strLen;
   12415     Jim_Obj *fileNameObj;
   12416     int linenr;
   12417 
   12418     if (objPtr->typePtr == &listObjType) {
   12419         return JIM_OK;
   12420     }
   12421 
   12422 
   12423     if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
   12424         Jim_Dict *dict = objPtr->internalRep.dictValue;
   12425 
   12426 
   12427         objPtr->typePtr = &listObjType;
   12428         objPtr->internalRep.listValue.len = dict->len;
   12429         objPtr->internalRep.listValue.maxLen = dict->maxLen;
   12430         objPtr->internalRep.listValue.ele = dict->table;
   12431 
   12432 
   12433         Jim_Free(dict->ht);
   12434 
   12435 
   12436         Jim_Free(dict);
   12437         return JIM_OK;
   12438     }
   12439 
   12440 
   12441     fileNameObj = Jim_GetSourceInfo(interp, objPtr, &linenr);
   12442     Jim_IncrRefCount(fileNameObj);
   12443 
   12444 
   12445     str = Jim_GetString(objPtr, &strLen);
   12446 
   12447     Jim_FreeIntRep(interp, objPtr);
   12448     objPtr->typePtr = &listObjType;
   12449     objPtr->internalRep.listValue.len = 0;
   12450     objPtr->internalRep.listValue.maxLen = 0;
   12451     objPtr->internalRep.listValue.ele = NULL;
   12452 
   12453 
   12454     if (strLen) {
   12455         JimParserInit(&parser, str, strLen, linenr);
   12456         while (!parser.eof) {
   12457             Jim_Obj *elementPtr;
   12458 
   12459             JimParseList(&parser);
   12460             if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
   12461                 continue;
   12462             elementPtr = JimParserGetTokenObj(interp, &parser);
   12463             Jim_SetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
   12464             ListAppendElement(objPtr, elementPtr);
   12465         }
   12466     }
   12467     Jim_DecrRefCount(interp, fileNameObj);
   12468     return JIM_OK;
   12469 }
   12470 
   12471 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
   12472 {
   12473     Jim_Obj *objPtr;
   12474 
   12475     objPtr = Jim_NewObj(interp);
   12476     objPtr->typePtr = &listObjType;
   12477     objPtr->bytes = NULL;
   12478     objPtr->internalRep.listValue.ele = NULL;
   12479     objPtr->internalRep.listValue.len = 0;
   12480     objPtr->internalRep.listValue.maxLen = 0;
   12481 
   12482     if (len) {
   12483         ListInsertElements(objPtr, 0, len, elements);
   12484     }
   12485 
   12486     return objPtr;
   12487 }
   12488 
   12489 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
   12490     Jim_Obj ***listVec)
   12491 {
   12492     *listLen = Jim_ListLength(interp, listObj);
   12493     *listVec = listObj->internalRep.listValue.ele;
   12494 }
   12495 
   12496 
   12497 static int JimSign(jim_wide w)
   12498 {
   12499     if (w == 0) {
   12500         return 0;
   12501     }
   12502     else if (w < 0) {
   12503         return -1;
   12504     }
   12505     return 1;
   12506 }
   12507 
   12508 
   12509 struct lsort_info {
   12510     jmp_buf jmpbuf;
   12511     Jim_Obj *command;
   12512     Jim_Interp *interp;
   12513     enum {
   12514         JIM_LSORT_ASCII,
   12515         JIM_LSORT_NOCASE,
   12516         JIM_LSORT_INTEGER,
   12517         JIM_LSORT_REAL,
   12518         JIM_LSORT_COMMAND,
   12519         JIM_LSORT_DICT
   12520     } type;
   12521     int order;
   12522     Jim_Obj **indexv;
   12523     int indexc;
   12524     int unique;
   12525     int (*subfn)(Jim_Obj **, Jim_Obj **);
   12526 };
   12527 
   12528 static struct lsort_info *sort_info;
   12529 
   12530 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
   12531 {
   12532     Jim_Obj *lObj, *rObj;
   12533 
   12534     if (Jim_ListIndices(sort_info->interp, *lhsObj, sort_info->indexv, sort_info->indexc, &lObj, JIM_ERRMSG) != JIM_OK ||
   12535         Jim_ListIndices(sort_info->interp, *rhsObj, sort_info->indexv, sort_info->indexc, &rObj, JIM_ERRMSG) != JIM_OK) {
   12536         longjmp(sort_info->jmpbuf, JIM_ERR);
   12537     }
   12538     return sort_info->subfn(&lObj, &rObj);
   12539 }
   12540 
   12541 
   12542 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
   12543 {
   12544     return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
   12545 }
   12546 
   12547 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
   12548 {
   12549     return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
   12550 }
   12551 
   12552 static int ListSortDict(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
   12553 {
   12554 
   12555     const char *left = Jim_String(*lhsObj);
   12556     const char *right = Jim_String(*rhsObj);
   12557 
   12558     while (1) {
   12559         if (isdigit(UCHAR(*left)) && isdigit(UCHAR(*right))) {
   12560 
   12561             jim_wide lint, rint;
   12562             char *lend, *rend;
   12563             lint = jim_strtoull(left, &lend);
   12564             rint = jim_strtoull(right, &rend);
   12565             if (lint != rint) {
   12566                 return JimSign(lint - rint) * sort_info->order;
   12567             }
   12568             if (lend -left != rend - right) {
   12569                 return JimSign((lend - left) - (rend - right)) * sort_info->order;
   12570             }
   12571             left = lend;
   12572             right = rend;
   12573         }
   12574         else {
   12575             int cl, cr;
   12576             left += utf8_tounicode_case(left, &cl, 1);
   12577             right += utf8_tounicode_case(right, &cr, 1);
   12578             if (cl != cr) {
   12579                 return JimSign(cl - cr) * sort_info->order;
   12580             }
   12581             if (cl == 0) {
   12582 
   12583                 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
   12584             }
   12585         }
   12586     }
   12587 }
   12588 
   12589 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
   12590 {
   12591     jim_wide lhs = 0, rhs = 0;
   12592 
   12593     if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
   12594         Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
   12595         longjmp(sort_info->jmpbuf, JIM_ERR);
   12596     }
   12597 
   12598     return JimSign(lhs - rhs) * sort_info->order;
   12599 }
   12600 
   12601 static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
   12602 {
   12603     double lhs = 0, rhs = 0;
   12604 
   12605     if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
   12606         Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
   12607         longjmp(sort_info->jmpbuf, JIM_ERR);
   12608     }
   12609     if (lhs == rhs) {
   12610         return 0;
   12611     }
   12612     if (lhs > rhs) {
   12613         return sort_info->order;
   12614     }
   12615     return -sort_info->order;
   12616 }
   12617 
   12618 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
   12619 {
   12620     Jim_Obj *compare_script;
   12621     int rc;
   12622 
   12623     jim_wide ret = 0;
   12624 
   12625 
   12626     compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
   12627     Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
   12628     Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);
   12629 
   12630     rc = Jim_EvalObj(sort_info->interp, compare_script);
   12631 
   12632     if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
   12633         longjmp(sort_info->jmpbuf, rc);
   12634     }
   12635 
   12636     return JimSign(ret) * sort_info->order;
   12637 }
   12638 
   12639 static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
   12640 {
   12641     int src;
   12642     int dst = 0;
   12643     Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
   12644 
   12645     for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
   12646         if (comp(&ele[dst], &ele[src]) == 0) {
   12647 
   12648             Jim_DecrRefCount(sort_info->interp, ele[dst]);
   12649         }
   12650         else {
   12651 
   12652             dst++;
   12653         }
   12654         ele[dst] = ele[src];
   12655     }
   12656 
   12657 
   12658     dst++;
   12659     if (dst < listObjPtr->internalRep.listValue.len) {
   12660         ele[dst] = ele[src];
   12661     }
   12662 
   12663 
   12664     listObjPtr->internalRep.listValue.len = dst;
   12665 }
   12666 
   12667 
   12668 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
   12669 {
   12670     struct lsort_info *prev_info;
   12671 
   12672     typedef int (qsort_comparator) (const void *, const void *);
   12673     int (*fn) (Jim_Obj **, Jim_Obj **);
   12674     Jim_Obj **vector;
   12675     int len;
   12676     int rc;
   12677 
   12678     JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
   12679     SetListFromAny(interp, listObjPtr);
   12680 
   12681 
   12682     prev_info = sort_info;
   12683     sort_info = info;
   12684 
   12685     vector = listObjPtr->internalRep.listValue.ele;
   12686     len = listObjPtr->internalRep.listValue.len;
   12687     switch (info->type) {
   12688         case JIM_LSORT_ASCII:
   12689             fn = ListSortString;
   12690             break;
   12691         case JIM_LSORT_NOCASE:
   12692             fn = ListSortStringNoCase;
   12693             break;
   12694         case JIM_LSORT_INTEGER:
   12695             fn = ListSortInteger;
   12696             break;
   12697         case JIM_LSORT_REAL:
   12698             fn = ListSortReal;
   12699             break;
   12700         case JIM_LSORT_COMMAND:
   12701             fn = ListSortCommand;
   12702             break;
   12703         case JIM_LSORT_DICT:
   12704             fn = ListSortDict;
   12705             break;
   12706         default:
   12707             fn = NULL;
   12708             JimPanic((1, "ListSort called with invalid sort type"));
   12709             return -1;
   12710     }
   12711 
   12712     if (info->indexc) {
   12713 
   12714         info->subfn = fn;
   12715         fn = ListSortIndexHelper;
   12716     }
   12717 
   12718     if ((rc = setjmp(info->jmpbuf)) == 0) {
   12719         qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
   12720 
   12721         if (info->unique && len > 1) {
   12722             ListRemoveDuplicates(listObjPtr, fn);
   12723         }
   12724 
   12725         Jim_InvalidateStringRep(listObjPtr);
   12726     }
   12727     sort_info = prev_info;
   12728 
   12729     return rc;
   12730 }
   12731 
   12732 
   12733 static void ListEnsureLength(Jim_Obj *listPtr, int idx)
   12734 {
   12735     assert(idx >= 0);
   12736     if (idx >= listPtr->internalRep.listValue.maxLen) {
   12737         if (idx < 4) {
   12738 
   12739             idx = 4;
   12740         }
   12741         listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
   12742             sizeof(Jim_Obj *) * idx);
   12743 
   12744         listPtr->internalRep.listValue.maxLen = idx;
   12745     }
   12746 }
   12747 
   12748 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
   12749 {
   12750     int currentLen = listPtr->internalRep.listValue.len;
   12751     int requiredLen = currentLen + elemc;
   12752     int i;
   12753     Jim_Obj **point;
   12754 
   12755     if (elemc == 0) {
   12756 
   12757         return;
   12758     }
   12759 
   12760     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
   12761         if (currentLen) {
   12762 
   12763             requiredLen *= 2;
   12764         }
   12765         ListEnsureLength(listPtr, requiredLen);
   12766     }
   12767     if (idx < 0) {
   12768         idx = currentLen;
   12769     }
   12770     point = listPtr->internalRep.listValue.ele + idx;
   12771     memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
   12772     for (i = 0; i < elemc; ++i) {
   12773         point[i] = elemVec[i];
   12774         Jim_IncrRefCount(point[i]);
   12775     }
   12776     listPtr->internalRep.listValue.len += elemc;
   12777 }
   12778 
   12779 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
   12780 {
   12781     ListInsertElements(listPtr, -1, 1, &objPtr);
   12782 }
   12783 
   12784 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
   12785 {
   12786     ListInsertElements(listPtr, -1,
   12787         appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele);
   12788 }
   12789 
   12790 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
   12791 {
   12792     JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object"));
   12793     SetListFromAny(interp, listPtr);
   12794     Jim_InvalidateStringRep(listPtr);
   12795     ListAppendElement(listPtr, objPtr);
   12796 }
   12797 
   12798 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
   12799 {
   12800     JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object"));
   12801     SetListFromAny(interp, listPtr);
   12802     SetListFromAny(interp, appendListPtr);
   12803     Jim_InvalidateStringRep(listPtr);
   12804     ListAppendList(listPtr, appendListPtr);
   12805 }
   12806 
   12807 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
   12808 {
   12809     SetListFromAny(interp, objPtr);
   12810     return objPtr->internalRep.listValue.len;
   12811 }
   12812 
   12813 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
   12814     int objc, Jim_Obj *const *objVec)
   12815 {
   12816     JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
   12817     SetListFromAny(interp, listPtr);
   12818     if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
   12819         idx = listPtr->internalRep.listValue.len;
   12820     else if (idx < 0)
   12821         idx = 0;
   12822     Jim_InvalidateStringRep(listPtr);
   12823     ListInsertElements(listPtr, idx, objc, objVec);
   12824 }
   12825 
   12826 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
   12827 {
   12828     SetListFromAny(interp, listPtr);
   12829     if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
   12830         (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
   12831         return NULL;
   12832     }
   12833     if (idx < 0)
   12834         idx = listPtr->internalRep.listValue.len + idx;
   12835     return listPtr->internalRep.listValue.ele[idx];
   12836 }
   12837 
   12838 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
   12839 {
   12840     *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
   12841     if (*objPtrPtr == NULL) {
   12842         if (flags & JIM_ERRMSG) {
   12843             Jim_SetResultString(interp, "list index out of range", -1);
   12844         }
   12845         return JIM_ERR;
   12846     }
   12847     return JIM_OK;
   12848 }
   12849 
   12850 static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr,
   12851     Jim_Obj *const *indexv, int indexc, Jim_Obj **resultObj, int flags)
   12852 {
   12853     int i;
   12854     int static_idxes[5];
   12855     int *idxes = static_idxes;
   12856     int ret = JIM_OK;
   12857 
   12858     if (indexc > sizeof(static_idxes) / sizeof(*static_idxes)) {
   12859         idxes = Jim_Alloc(indexc * sizeof(*idxes));
   12860     }
   12861 
   12862     for (i = 0; i < indexc; i++) {
   12863         ret = Jim_GetIndex(interp, indexv[i], &idxes[i]);
   12864         if (ret != JIM_OK) {
   12865             goto err;
   12866         }
   12867     }
   12868 
   12869     for (i = 0; i < indexc; i++) {
   12870         Jim_Obj *objPtr = Jim_ListGetIndex(interp, listPtr, idxes[i]);
   12871         if (!objPtr) {
   12872             if (flags & JIM_ERRMSG) {
   12873                 if (idxes[i] < 0 || idxes[i] > Jim_ListLength(interp, listPtr)) {
   12874                     Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
   12875                 }
   12876                 else {
   12877                     Jim_SetResultFormatted(interp, "element %#s missing from sublist \"%#s\"", indexv[i], listPtr);
   12878                 }
   12879             }
   12880             return -1;
   12881         }
   12882         listPtr = objPtr;
   12883     }
   12884     *resultObj = listPtr;
   12885 err:
   12886     if (idxes != static_idxes)
   12887         Jim_Free(idxes);
   12888     return ret;
   12889 }
   12890 
   12891 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
   12892     Jim_Obj *newObjPtr, int flags)
   12893 {
   12894     SetListFromAny(interp, listPtr);
   12895     if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
   12896         (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
   12897         if (flags & JIM_ERRMSG) {
   12898             Jim_SetResultString(interp, "list index out of range", -1);
   12899         }
   12900         return JIM_ERR;
   12901     }
   12902     if (idx < 0)
   12903         idx = listPtr->internalRep.listValue.len + idx;
   12904     Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
   12905     listPtr->internalRep.listValue.ele[idx] = newObjPtr;
   12906     Jim_IncrRefCount(newObjPtr);
   12907     return JIM_OK;
   12908 }
   12909 
   12910 int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
   12911     Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
   12912 {
   12913     Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
   12914     int shared, i, idx;
   12915 
   12916     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
   12917     if (objPtr == NULL)
   12918         return JIM_ERR;
   12919     if ((shared = Jim_IsShared(objPtr)))
   12920         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
   12921     for (i = 0; i < indexc - 1; i++) {
   12922         listObjPtr = objPtr;
   12923         if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
   12924             goto err;
   12925 
   12926         objPtr = Jim_ListGetIndex(interp, listObjPtr, idx);
   12927         if (objPtr == NULL) {
   12928             Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
   12929             goto err;
   12930         }
   12931         if (Jim_IsShared(objPtr)) {
   12932             objPtr = Jim_DuplicateObj(interp, objPtr);
   12933             ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
   12934         }
   12935         Jim_InvalidateStringRep(listObjPtr);
   12936     }
   12937     if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
   12938         goto err;
   12939     if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
   12940         goto err;
   12941     Jim_InvalidateStringRep(objPtr);
   12942     Jim_InvalidateStringRep(varObjPtr);
   12943     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
   12944         goto err;
   12945     Jim_SetResult(interp, varObjPtr);
   12946     return JIM_OK;
   12947   err:
   12948     if (shared) {
   12949         Jim_FreeNewObj(interp, varObjPtr);
   12950     }
   12951     return JIM_ERR;
   12952 }
   12953 
   12954 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
   12955 {
   12956     int i;
   12957     int listLen = Jim_ListLength(interp, listObjPtr);
   12958     Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
   12959 
   12960     for (i = 0; i < listLen; ) {
   12961         Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i));
   12962         if (++i != listLen) {
   12963             Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
   12964         }
   12965     }
   12966     return resObjPtr;
   12967 }
   12968 
   12969 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
   12970 {
   12971     int i;
   12972 
   12973     for (i = 0; i < objc; i++) {
   12974         if (!Jim_IsList(objv[i]))
   12975             break;
   12976     }
   12977     if (i == objc) {
   12978         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
   12979 
   12980         for (i = 0; i < objc; i++)
   12981             ListAppendList(objPtr, objv[i]);
   12982         return objPtr;
   12983     }
   12984     else {
   12985 
   12986         int len = 0, objLen;
   12987         char *bytes, *p;
   12988 
   12989 
   12990         for (i = 0; i < objc; i++) {
   12991             len += Jim_Length(objv[i]);
   12992         }
   12993         if (objc)
   12994             len += objc - 1;
   12995 
   12996         p = bytes = Jim_Alloc(len + 1);
   12997         for (i = 0; i < objc; i++) {
   12998             const char *s = Jim_GetString(objv[i], &objLen);
   12999 
   13000 
   13001             while (objLen && isspace(UCHAR(*s))) {
   13002                 s++;
   13003                 objLen--;
   13004                 len--;
   13005             }
   13006 
   13007             while (objLen && isspace(UCHAR(s[objLen - 1]))) {
   13008 
   13009                 if (objLen > 1 && s[objLen - 2] == '\\') {
   13010                     break;
   13011                 }
   13012                 objLen--;
   13013                 len--;
   13014             }
   13015             memcpy(p, s, objLen);
   13016             p += objLen;
   13017             if (i + 1 != objc) {
   13018                 if (objLen)
   13019                     *p++ = ' ';
   13020                 else {
   13021                     len--;
   13022                 }
   13023             }
   13024         }
   13025         *p = '\0';
   13026         return Jim_NewStringObjNoAlloc(interp, bytes, len);
   13027     }
   13028 }
   13029 
   13030 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr,
   13031     Jim_Obj *lastObjPtr)
   13032 {
   13033     int first, last;
   13034     int len, rangeLen;
   13035 
   13036     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
   13037         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
   13038         return NULL;
   13039     len = Jim_ListLength(interp, listObjPtr);
   13040     first = JimRelToAbsIndex(len, first);
   13041     last = JimRelToAbsIndex(len, last);
   13042     JimRelToAbsRange(len, &first, &last, &rangeLen);
   13043     if (first == 0 && last == len) {
   13044         return listObjPtr;
   13045     }
   13046     return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
   13047 }
   13048 
   13049 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
   13050 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
   13051 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
   13052 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
   13053 
   13054 
   13055 static const Jim_ObjType dictObjType = {
   13056     "dict",
   13057     FreeDictInternalRep,
   13058     DupDictInternalRep,
   13059     UpdateStringOfDict,
   13060     JIM_TYPE_NONE,
   13061 };
   13062 
   13063 static void JimFreeDict(Jim_Interp *interp, Jim_Dict *dict)
   13064 {
   13065     int i;
   13066     for (i = 0; i < dict->len; i++) {
   13067         Jim_DecrRefCount(interp, dict->table[i]);
   13068     }
   13069     Jim_Free(dict->table);
   13070     Jim_Free(dict->ht);
   13071     Jim_Free(dict);
   13072 }
   13073 
   13074 enum {
   13075     DICT_HASH_FIND = -1,
   13076     DICT_HASH_REMOVE = -2,
   13077     DICT_HASH_ADD = -3,
   13078 };
   13079 
   13080 static int JimDictHashFind(Jim_Dict *dict, Jim_Obj *keyObjPtr, int op_tvoffset)
   13081 {
   13082     unsigned h = (JimObjectHTHashFunction(keyObjPtr) + dict->uniq);
   13083     unsigned idx = h & dict->sizemask;
   13084     int tvoffset = 0;
   13085     unsigned peturb = h;
   13086     unsigned first_removed = ~0;
   13087 
   13088     if (dict->len) {
   13089         while ((tvoffset = dict->ht[idx].offset)) {
   13090             if (tvoffset == -1) {
   13091                 if (first_removed == ~0) {
   13092                     first_removed = idx;
   13093                 }
   13094             }
   13095             else if (dict->ht[idx].hash == h) {
   13096                 if (Jim_StringEqObj(keyObjPtr, dict->table[tvoffset - 1])) {
   13097                     break;
   13098                 }
   13099             }
   13100 
   13101             peturb >>= 5;
   13102             idx = (5 * idx + 1 + peturb) & dict->sizemask;
   13103         }
   13104     }
   13105 
   13106     switch (op_tvoffset) {
   13107         case DICT_HASH_FIND:
   13108 
   13109             break;
   13110         case DICT_HASH_REMOVE:
   13111             if (tvoffset) {
   13112 
   13113                 dict->ht[idx].offset = -1;
   13114                 dict->dummy++;
   13115             }
   13116 
   13117             break;
   13118         case DICT_HASH_ADD:
   13119             if (tvoffset == 0) {
   13120 
   13121                 if (first_removed != ~0) {
   13122                     idx = first_removed;
   13123                     dict->dummy--;
   13124                 }
   13125                 dict->ht[idx].offset = dict->len + 1;
   13126                 dict->ht[idx].hash = h;
   13127             }
   13128 
   13129             break;
   13130         default:
   13131             assert(tvoffset);
   13132 
   13133             dict->ht[idx].offset = op_tvoffset;
   13134             break;
   13135     }
   13136 
   13137     return tvoffset;
   13138 }
   13139 
   13140 static void JimDictExpandHashTable(Jim_Dict *dict, unsigned int size)
   13141 {
   13142     int i;
   13143     struct JimDictHashEntry *prevht = dict->ht;
   13144     int prevsize = dict->size;
   13145 
   13146     dict->size = JimHashTableNextPower(size);
   13147     dict->sizemask = dict->size - 1;
   13148 
   13149 
   13150     dict->ht = Jim_Alloc(dict->size * sizeof(*dict->ht));
   13151     memset(dict->ht, 0, dict->size * sizeof(*dict->ht));
   13152 
   13153 
   13154     for (i = 0; i < prevsize; i++) {
   13155         if (prevht[i].offset > 0) {
   13156 
   13157             unsigned h = prevht[i].hash;
   13158             unsigned idx = h & dict->sizemask;
   13159             unsigned peturb = h;
   13160 
   13161             while (dict->ht[idx].offset) {
   13162                 peturb >>= 5;
   13163                 idx = (5 * idx + 1 + peturb) & dict->sizemask;
   13164             }
   13165             dict->ht[idx].offset = prevht[i].offset;
   13166             dict->ht[idx].hash = h;
   13167         }
   13168     }
   13169     Jim_Free(prevht);
   13170 }
   13171 
   13172 static int JimDictAdd(Jim_Dict *dict, Jim_Obj *keyObjPtr)
   13173 {
   13174     if (dict->size <= dict->len + dict->dummy) {
   13175         JimDictExpandHashTable(dict, dict->size ? dict->size * 2 : 8);
   13176     }
   13177     return JimDictHashFind(dict, keyObjPtr, DICT_HASH_ADD);
   13178 }
   13179 
   13180 static Jim_Dict *JimDictNew(Jim_Interp *interp, int table_size, int ht_size)
   13181 {
   13182     Jim_Dict *dict = Jim_Alloc(sizeof(*dict));
   13183     memset(dict, 0, sizeof(*dict));
   13184 
   13185     if (ht_size) {
   13186         JimDictExpandHashTable(dict, ht_size);
   13187     }
   13188     if (table_size) {
   13189         dict->table = Jim_Alloc(table_size * sizeof(*dict->table));
   13190         dict->maxLen = table_size;
   13191     }
   13192 #ifdef JIM_RANDOMISE_HASH
   13193     dict->uniq = (rand() ^ time(NULL) ^ clock());
   13194 #endif
   13195     return dict;
   13196 }
   13197 
   13198 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
   13199 {
   13200     JimFreeDict(interp, objPtr->internalRep.dictValue);
   13201 }
   13202 
   13203 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
   13204 {
   13205     Jim_Dict *oldDict = srcPtr->internalRep.dictValue;
   13206     int i;
   13207 
   13208 
   13209     Jim_Dict *newDict = JimDictNew(interp, oldDict->maxLen, oldDict->size);
   13210 
   13211 
   13212     for (i = 0; i < oldDict->len; i++) {
   13213         newDict->table[i] = oldDict->table[i];
   13214         Jim_IncrRefCount(newDict->table[i]);
   13215     }
   13216     newDict->len = oldDict->len;
   13217 
   13218 
   13219     newDict->uniq = oldDict->uniq;
   13220 
   13221 
   13222     memcpy(newDict->ht, oldDict->ht, sizeof(*oldDict->ht) * oldDict->size);
   13223 
   13224     dupPtr->internalRep.dictValue = newDict;
   13225     dupPtr->typePtr = &dictObjType;
   13226 }
   13227 
   13228 static void UpdateStringOfDict(struct Jim_Obj *objPtr)
   13229 {
   13230     JimMakeListStringRep(objPtr, objPtr->internalRep.dictValue->table, objPtr->internalRep.dictValue->len);
   13231 }
   13232 
   13233 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
   13234 {
   13235     int listlen;
   13236 
   13237     if (objPtr->typePtr == &dictObjType) {
   13238         return JIM_OK;
   13239     }
   13240 
   13241     if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
   13242         Jim_String(objPtr);
   13243     }
   13244 
   13245     listlen = Jim_ListLength(interp, objPtr);
   13246     if (listlen % 2) {
   13247         Jim_SetResultString(interp, "missing value to go with key", -1);
   13248         return JIM_ERR;
   13249     }
   13250     else {
   13251 
   13252         Jim_Dict *dict = JimDictNew(interp, 0, listlen);
   13253         int i;
   13254 
   13255 
   13256         dict->table = objPtr->internalRep.listValue.ele;
   13257         dict->maxLen = objPtr->internalRep.listValue.maxLen;
   13258 
   13259 
   13260         for (i = 0; i < listlen; i += 2) {
   13261             int tvoffset = JimDictAdd(dict, dict->table[i]);
   13262             if (tvoffset) {
   13263 
   13264 
   13265                 Jim_DecrRefCount(interp, dict->table[tvoffset]);
   13266 
   13267                 dict->table[tvoffset] = dict->table[i + 1];
   13268 
   13269                 Jim_DecrRefCount(interp, dict->table[i]);
   13270             }
   13271             else {
   13272                 if (dict->len != i) {
   13273                     dict->table[dict->len++] = dict->table[i];
   13274                     dict->table[dict->len++] = dict->table[i + 1];
   13275                 }
   13276                 else {
   13277                     dict->len += 2;
   13278                 }
   13279             }
   13280         }
   13281 
   13282         objPtr->typePtr = &dictObjType;
   13283         objPtr->internalRep.dictValue = dict;
   13284 
   13285         return JIM_OK;
   13286     }
   13287 }
   13288 
   13289 
   13290 
   13291 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
   13292     Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
   13293 {
   13294     Jim_Dict *dict = objPtr->internalRep.dictValue;
   13295     if (valueObjPtr == NULL) {
   13296 
   13297         int tvoffset = JimDictHashFind(dict, keyObjPtr, DICT_HASH_REMOVE);
   13298         if (tvoffset) {
   13299 
   13300             Jim_DecrRefCount(interp, dict->table[tvoffset - 1]);
   13301             Jim_DecrRefCount(interp, dict->table[tvoffset]);
   13302             dict->len -= 2;
   13303             if (tvoffset != dict->len + 1) {
   13304 
   13305                 dict->table[tvoffset - 1] = dict->table[dict->len];
   13306                 dict->table[tvoffset] = dict->table[dict->len + 1];
   13307 
   13308 
   13309                 JimDictHashFind(dict, dict->table[tvoffset - 1], tvoffset);
   13310             }
   13311             return JIM_OK;
   13312         }
   13313         return JIM_ERR;
   13314     }
   13315     else {
   13316 
   13317         int tvoffset = JimDictAdd(dict, keyObjPtr);
   13318         if (tvoffset) {
   13319 
   13320             Jim_IncrRefCount(valueObjPtr);
   13321             Jim_DecrRefCount(interp, dict->table[tvoffset]);
   13322             dict->table[tvoffset] = valueObjPtr;
   13323         }
   13324         else {
   13325             if (dict->maxLen == dict->len) {
   13326 
   13327                 if (dict->maxLen < 4) {
   13328                     dict->maxLen = 4;
   13329                 }
   13330                 else {
   13331                     dict->maxLen *= 2;
   13332                 }
   13333                 dict->table = Jim_Realloc(dict->table, dict->maxLen * sizeof(*dict->table));
   13334             }
   13335             Jim_IncrRefCount(keyObjPtr);
   13336             Jim_IncrRefCount(valueObjPtr);
   13337 
   13338             dict->table[dict->len++] = keyObjPtr;
   13339             dict->table[dict->len++] = valueObjPtr;
   13340 
   13341         }
   13342         return JIM_OK;
   13343     }
   13344 }
   13345 
   13346 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
   13347     Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
   13348 {
   13349     JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
   13350     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
   13351         return JIM_ERR;
   13352     }
   13353     Jim_InvalidateStringRep(objPtr);
   13354     return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
   13355 }
   13356 
   13357 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
   13358 {
   13359     Jim_Obj *objPtr;
   13360     int i;
   13361 
   13362     JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
   13363 
   13364     objPtr = Jim_NewObj(interp);
   13365     objPtr->typePtr = &dictObjType;
   13366     objPtr->bytes = NULL;
   13367 
   13368     objPtr->internalRep.dictValue = JimDictNew(interp, len, len);
   13369     for (i = 0; i < len; i += 2)
   13370         DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
   13371     return objPtr;
   13372 }
   13373 
   13374 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
   13375     Jim_Obj **objPtrPtr, int flags)
   13376 {
   13377     int tvoffset;
   13378     Jim_Dict *dict;
   13379 
   13380     if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
   13381         return -1;
   13382     }
   13383     dict = dictPtr->internalRep.dictValue;
   13384     tvoffset = JimDictHashFind(dict, keyPtr, DICT_HASH_FIND);
   13385     if (tvoffset == 0) {
   13386         if (flags & JIM_ERRMSG) {
   13387             Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
   13388         }
   13389         return JIM_ERR;
   13390     }
   13391     *objPtrPtr = dict->table[tvoffset];
   13392     return JIM_OK;
   13393 }
   13394 
   13395 Jim_Obj **Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, int *len)
   13396 {
   13397 
   13398     if (Jim_IsList(dictPtr)) {
   13399         Jim_Obj **table;
   13400         JimListGetElements(interp, dictPtr, len, &table);
   13401         if (*len % 2 == 0) {
   13402             return table;
   13403         }
   13404 
   13405     }
   13406     if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
   13407 
   13408         *len = 1;
   13409         return NULL;
   13410     }
   13411     *len = dictPtr->internalRep.dictValue->len;
   13412     return dictPtr->internalRep.dictValue->table;
   13413 }
   13414 
   13415 
   13416 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
   13417     Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
   13418 {
   13419     int i;
   13420 
   13421     if (keyc == 0) {
   13422         *objPtrPtr = dictPtr;
   13423         return JIM_OK;
   13424     }
   13425 
   13426     for (i = 0; i < keyc; i++) {
   13427         Jim_Obj *objPtr;
   13428 
   13429         int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
   13430         if (rc != JIM_OK) {
   13431             return rc;
   13432         }
   13433         dictPtr = objPtr;
   13434     }
   13435     *objPtrPtr = dictPtr;
   13436     return JIM_OK;
   13437 }
   13438 
   13439 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
   13440     Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags)
   13441 {
   13442     Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
   13443     int shared, i;
   13444 
   13445     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
   13446     if (objPtr == NULL) {
   13447         if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
   13448 
   13449             return JIM_ERR;
   13450         }
   13451         varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
   13452         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
   13453             Jim_FreeNewObj(interp, varObjPtr);
   13454             return JIM_ERR;
   13455         }
   13456     }
   13457     if ((shared = Jim_IsShared(objPtr)))
   13458         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
   13459     for (i = 0; i < keyc; i++) {
   13460         dictObjPtr = objPtr;
   13461 
   13462 
   13463         if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
   13464             goto err;
   13465         }
   13466 
   13467         if (i == keyc - 1) {
   13468 
   13469             if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
   13470                 if (newObjPtr || (flags & JIM_MUSTEXIST)) {
   13471                     goto err;
   13472                 }
   13473             }
   13474             break;
   13475         }
   13476 
   13477 
   13478         Jim_InvalidateStringRep(dictObjPtr);
   13479         if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
   13480                 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
   13481             if (Jim_IsShared(objPtr)) {
   13482                 objPtr = Jim_DuplicateObj(interp, objPtr);
   13483                 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
   13484             }
   13485         }
   13486         else {
   13487             if (newObjPtr == NULL) {
   13488                 goto err;
   13489             }
   13490             objPtr = Jim_NewDictObj(interp, NULL, 0);
   13491             DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
   13492         }
   13493     }
   13494 
   13495     Jim_InvalidateStringRep(objPtr);
   13496     Jim_InvalidateStringRep(varObjPtr);
   13497     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
   13498         goto err;
   13499     }
   13500 
   13501     if (!(flags & JIM_NORESULT)) {
   13502         Jim_SetResult(interp, varObjPtr);
   13503     }
   13504     return JIM_OK;
   13505   err:
   13506     if (shared) {
   13507         Jim_FreeNewObj(interp, varObjPtr);
   13508     }
   13509     return JIM_ERR;
   13510 }
   13511 
   13512 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
   13513 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
   13514 
   13515 static const Jim_ObjType indexObjType = {
   13516     "index",
   13517     NULL,
   13518     NULL,
   13519     UpdateStringOfIndex,
   13520     JIM_TYPE_NONE,
   13521 };
   13522 
   13523 static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
   13524 {
   13525     if (objPtr->internalRep.intValue == -1) {
   13526         JimSetStringBytes(objPtr, "end");
   13527     }
   13528     else {
   13529         char buf[JIM_INTEGER_SPACE + 1];
   13530         if (objPtr->internalRep.intValue >= 0 || objPtr->internalRep.intValue == -INT_MAX) {
   13531             sprintf(buf, "%d", objPtr->internalRep.intValue);
   13532         }
   13533         else {
   13534 
   13535             sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
   13536         }
   13537         JimSetStringBytes(objPtr, buf);
   13538     }
   13539 }
   13540 
   13541 static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
   13542 {
   13543     jim_wide idx;
   13544     int end = 0;
   13545     const char *str;
   13546     Jim_Obj *exprObj = objPtr;
   13547 
   13548     JimPanic((objPtr->refCount == 0, "SetIndexFromAny() called with zero refcount object"));
   13549 
   13550 
   13551     str = Jim_String(objPtr);
   13552 
   13553 
   13554     if (strncmp(str, "end", 3) == 0) {
   13555         end = 1;
   13556         str += 3;
   13557         idx = 0;
   13558         switch (*str) {
   13559             case '\0':
   13560                 exprObj = NULL;
   13561                 break;
   13562 
   13563             case '-':
   13564             case '+':
   13565                 exprObj = Jim_NewStringObj(interp, str, -1);
   13566                 break;
   13567 
   13568             default:
   13569                 goto badindex;
   13570         }
   13571     }
   13572     if (exprObj) {
   13573         int ret;
   13574         Jim_IncrRefCount(exprObj);
   13575         ret = Jim_GetWideExpr(interp, exprObj, &idx);
   13576         Jim_DecrRefCount(interp, exprObj);
   13577         if (ret != JIM_OK) {
   13578             goto badindex;
   13579         }
   13580     }
   13581 
   13582     if (end) {
   13583         if (idx > 0) {
   13584             idx = INT_MAX;
   13585         }
   13586         else {
   13587 
   13588             idx--;
   13589         }
   13590     }
   13591     else if (idx < 0) {
   13592         idx = -INT_MAX;
   13593     }
   13594 
   13595 
   13596     Jim_FreeIntRep(interp, objPtr);
   13597     objPtr->typePtr = &indexObjType;
   13598     objPtr->internalRep.intValue = idx;
   13599     return JIM_OK;
   13600 
   13601   badindex:
   13602     Jim_SetResultFormatted(interp,
   13603         "bad index \"%#s\": must be intexpr or end?[+-]intexpr?", objPtr);
   13604     return JIM_ERR;
   13605 }
   13606 
   13607 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
   13608 {
   13609 
   13610     if (objPtr->typePtr == &intObjType) {
   13611         jim_wide val = JimWideValue(objPtr);
   13612 
   13613         if (val < 0)
   13614             *indexPtr = -INT_MAX;
   13615         else if (val > INT_MAX)
   13616             *indexPtr = INT_MAX;
   13617         else
   13618             *indexPtr = (int)val;
   13619         return JIM_OK;
   13620     }
   13621     if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
   13622         return JIM_ERR;
   13623     *indexPtr = objPtr->internalRep.intValue;
   13624     return JIM_OK;
   13625 }
   13626 
   13627 
   13628 
   13629 static const char * const jimReturnCodes[] = {
   13630     "ok",
   13631     "error",
   13632     "return",
   13633     "break",
   13634     "continue",
   13635     "signal",
   13636     "exit",
   13637     "eval",
   13638     NULL
   13639 };
   13640 
   13641 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1)
   13642 
   13643 static const Jim_ObjType returnCodeObjType = {
   13644     "return-code",
   13645     NULL,
   13646     NULL,
   13647     NULL,
   13648     JIM_TYPE_NONE,
   13649 };
   13650 
   13651 const char *Jim_ReturnCode(int code)
   13652 {
   13653     if (code < 0 || code >= (int)jimReturnCodesSize) {
   13654         return "?";
   13655     }
   13656     else {
   13657         return jimReturnCodes[code];
   13658     }
   13659 }
   13660 
   13661 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
   13662 {
   13663     int returnCode;
   13664     jim_wide wideValue;
   13665 
   13666 
   13667     if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
   13668         returnCode = (int)wideValue;
   13669     else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
   13670         Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
   13671         return JIM_ERR;
   13672     }
   13673 
   13674     Jim_FreeIntRep(interp, objPtr);
   13675     objPtr->typePtr = &returnCodeObjType;
   13676     objPtr->internalRep.intValue = returnCode;
   13677     return JIM_OK;
   13678 }
   13679 
   13680 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
   13681 {
   13682     if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
   13683         return JIM_ERR;
   13684     *intPtr = objPtr->internalRep.intValue;
   13685     return JIM_OK;
   13686 }
   13687 
   13688 static int JimParseExprOperator(struct JimParserCtx *pc);
   13689 static int JimParseExprNumber(struct JimParserCtx *pc);
   13690 static int JimParseExprIrrational(struct JimParserCtx *pc);
   13691 static int JimParseExprBoolean(struct JimParserCtx *pc);
   13692 
   13693 
   13694 enum
   13695 {
   13696 
   13697 
   13698 
   13699     JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
   13700     JIM_EXPROP_DIV,
   13701     JIM_EXPROP_MOD,
   13702     JIM_EXPROP_SUB,
   13703     JIM_EXPROP_ADD,
   13704     JIM_EXPROP_LSHIFT,
   13705     JIM_EXPROP_RSHIFT,
   13706     JIM_EXPROP_ROTL,
   13707     JIM_EXPROP_ROTR,
   13708     JIM_EXPROP_LT,
   13709     JIM_EXPROP_GT,
   13710     JIM_EXPROP_LTE,
   13711     JIM_EXPROP_GTE,
   13712     JIM_EXPROP_NUMEQ,
   13713     JIM_EXPROP_NUMNE,
   13714     JIM_EXPROP_BITAND,
   13715     JIM_EXPROP_BITXOR,
   13716     JIM_EXPROP_BITOR,
   13717     JIM_EXPROP_LOGICAND,
   13718     JIM_EXPROP_LOGICOR,
   13719     JIM_EXPROP_TERNARY,
   13720     JIM_EXPROP_COLON,
   13721     JIM_EXPROP_POW,
   13722 
   13723 
   13724     JIM_EXPROP_STREQ,
   13725     JIM_EXPROP_STRNE,
   13726     JIM_EXPROP_STRIN,
   13727     JIM_EXPROP_STRNI,
   13728     JIM_EXPROP_STRLT,
   13729     JIM_EXPROP_STRGT,
   13730     JIM_EXPROP_STRLE,
   13731     JIM_EXPROP_STRGE,
   13732 
   13733 
   13734     JIM_EXPROP_NOT,
   13735     JIM_EXPROP_BITNOT,
   13736     JIM_EXPROP_UNARYMINUS,
   13737     JIM_EXPROP_UNARYPLUS,
   13738 
   13739 
   13740     JIM_EXPROP_FUNC_INT,
   13741     JIM_EXPROP_FUNC_WIDE,
   13742     JIM_EXPROP_FUNC_ABS,
   13743     JIM_EXPROP_FUNC_DOUBLE,
   13744     JIM_EXPROP_FUNC_ROUND,
   13745     JIM_EXPROP_FUNC_RAND,
   13746     JIM_EXPROP_FUNC_SRAND,
   13747 
   13748 
   13749     JIM_EXPROP_FUNC_SIN,
   13750     JIM_EXPROP_FUNC_COS,
   13751     JIM_EXPROP_FUNC_TAN,
   13752     JIM_EXPROP_FUNC_ASIN,
   13753     JIM_EXPROP_FUNC_ACOS,
   13754     JIM_EXPROP_FUNC_ATAN,
   13755     JIM_EXPROP_FUNC_ATAN2,
   13756     JIM_EXPROP_FUNC_SINH,
   13757     JIM_EXPROP_FUNC_COSH,
   13758     JIM_EXPROP_FUNC_TANH,
   13759     JIM_EXPROP_FUNC_CEIL,
   13760     JIM_EXPROP_FUNC_FLOOR,
   13761     JIM_EXPROP_FUNC_EXP,
   13762     JIM_EXPROP_FUNC_LOG,
   13763     JIM_EXPROP_FUNC_LOG10,
   13764     JIM_EXPROP_FUNC_SQRT,
   13765     JIM_EXPROP_FUNC_POW,
   13766     JIM_EXPROP_FUNC_HYPOT,
   13767     JIM_EXPROP_FUNC_FMOD,
   13768 };
   13769 
   13770 struct JimExprNode {
   13771     int type;
   13772     struct Jim_Obj *objPtr;
   13773 
   13774     struct JimExprNode *left;
   13775     struct JimExprNode *right;
   13776     struct JimExprNode *ternary;
   13777 };
   13778 
   13779 
   13780 typedef struct Jim_ExprOperator
   13781 {
   13782     const char *name;
   13783     int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode);
   13784     unsigned char precedence;
   13785     unsigned char arity;
   13786     unsigned char attr;
   13787     unsigned char namelen;
   13788 } Jim_ExprOperator;
   13789 
   13790 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr);
   13791 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
   13792 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);
   13793 
   13794 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
   13795 {
   13796     int intresult = 1;
   13797     int rc, bA = 0;
   13798     double dA, dC = 0;
   13799     jim_wide wA, wC = 0;
   13800     Jim_Obj *A;
   13801 
   13802     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
   13803         return rc;
   13804     }
   13805 
   13806     if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
   13807         switch (node->type) {
   13808             case JIM_EXPROP_FUNC_INT:
   13809             case JIM_EXPROP_FUNC_WIDE:
   13810             case JIM_EXPROP_FUNC_ROUND:
   13811             case JIM_EXPROP_UNARYPLUS:
   13812                 wC = wA;
   13813                 break;
   13814             case JIM_EXPROP_FUNC_DOUBLE:
   13815                 dC = wA;
   13816                 intresult = 0;
   13817                 break;
   13818             case JIM_EXPROP_FUNC_ABS:
   13819                 wC = wA >= 0 ? wA : -wA;
   13820                 break;
   13821             case JIM_EXPROP_UNARYMINUS:
   13822                 wC = -wA;
   13823                 break;
   13824             case JIM_EXPROP_NOT:
   13825                 wC = !wA;
   13826                 break;
   13827             default:
   13828                 abort();
   13829         }
   13830     }
   13831     else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
   13832         switch (node->type) {
   13833             case JIM_EXPROP_FUNC_INT:
   13834             case JIM_EXPROP_FUNC_WIDE:
   13835                 wC = dA;
   13836                 break;
   13837             case JIM_EXPROP_FUNC_ROUND:
   13838                 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
   13839                 break;
   13840             case JIM_EXPROP_FUNC_DOUBLE:
   13841             case JIM_EXPROP_UNARYPLUS:
   13842                 dC = dA;
   13843                 intresult = 0;
   13844                 break;
   13845             case JIM_EXPROP_FUNC_ABS:
   13846 #ifdef JIM_MATH_FUNCTIONS
   13847                 dC = fabs(dA);
   13848 #else
   13849                 dC = dA >= 0 ? dA : -dA;
   13850 #endif
   13851                 intresult = 0;
   13852                 break;
   13853             case JIM_EXPROP_UNARYMINUS:
   13854                 dC = -dA;
   13855                 intresult = 0;
   13856                 break;
   13857             case JIM_EXPROP_NOT:
   13858                 wC = !dA;
   13859                 break;
   13860             default:
   13861                 abort();
   13862         }
   13863     }
   13864     else if ((rc = Jim_GetBoolean(interp, A, &bA)) == JIM_OK) {
   13865         switch (node->type) {
   13866             case JIM_EXPROP_NOT:
   13867                 wC = !bA;
   13868                 break;
   13869             default:
   13870                 abort();
   13871         }
   13872     }
   13873 
   13874     if (rc == JIM_OK) {
   13875         if (intresult) {
   13876             Jim_SetResultInt(interp, wC);
   13877         }
   13878         else {
   13879             Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
   13880         }
   13881     }
   13882 
   13883     Jim_DecrRefCount(interp, A);
   13884 
   13885     return rc;
   13886 }
   13887 
   13888 static double JimRandDouble(Jim_Interp *interp)
   13889 {
   13890     unsigned long x;
   13891     JimRandomBytes(interp, &x, sizeof(x));
   13892 
   13893     return (double)x / (double)~0UL;
   13894 }
   13895 
   13896 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node)
   13897 {
   13898     jim_wide wA;
   13899     Jim_Obj *A;
   13900     int rc;
   13901 
   13902     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
   13903         return rc;
   13904     }
   13905 
   13906     rc = Jim_GetWide(interp, A, &wA);
   13907     if (rc == JIM_OK) {
   13908         switch (node->type) {
   13909             case JIM_EXPROP_BITNOT:
   13910                 Jim_SetResultInt(interp, ~wA);
   13911                 break;
   13912             case JIM_EXPROP_FUNC_SRAND:
   13913                 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
   13914                 Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
   13915                 break;
   13916             default:
   13917                 abort();
   13918         }
   13919     }
   13920 
   13921     Jim_DecrRefCount(interp, A);
   13922 
   13923     return rc;
   13924 }
   13925 
   13926 static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node)
   13927 {
   13928     JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
   13929 
   13930     Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
   13931 
   13932     return JIM_OK;
   13933 }
   13934 
   13935 #ifdef JIM_MATH_FUNCTIONS
   13936 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node)
   13937 {
   13938     int rc;
   13939     double dA, dC;
   13940     Jim_Obj *A;
   13941 
   13942     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
   13943         return rc;
   13944     }
   13945 
   13946     rc = Jim_GetDouble(interp, A, &dA);
   13947     if (rc == JIM_OK) {
   13948         switch (node->type) {
   13949             case JIM_EXPROP_FUNC_SIN:
   13950                 dC = sin(dA);
   13951                 break;
   13952             case JIM_EXPROP_FUNC_COS:
   13953                 dC = cos(dA);
   13954                 break;
   13955             case JIM_EXPROP_FUNC_TAN:
   13956                 dC = tan(dA);
   13957                 break;
   13958             case JIM_EXPROP_FUNC_ASIN:
   13959                 dC = asin(dA);
   13960                 break;
   13961             case JIM_EXPROP_FUNC_ACOS:
   13962                 dC = acos(dA);
   13963                 break;
   13964             case JIM_EXPROP_FUNC_ATAN:
   13965                 dC = atan(dA);
   13966                 break;
   13967             case JIM_EXPROP_FUNC_SINH:
   13968                 dC = sinh(dA);
   13969                 break;
   13970             case JIM_EXPROP_FUNC_COSH:
   13971                 dC = cosh(dA);
   13972                 break;
   13973             case JIM_EXPROP_FUNC_TANH:
   13974                 dC = tanh(dA);
   13975                 break;
   13976             case JIM_EXPROP_FUNC_CEIL:
   13977                 dC = ceil(dA);
   13978                 break;
   13979             case JIM_EXPROP_FUNC_FLOOR:
   13980                 dC = floor(dA);
   13981                 break;
   13982             case JIM_EXPROP_FUNC_EXP:
   13983                 dC = exp(dA);
   13984                 break;
   13985             case JIM_EXPROP_FUNC_LOG:
   13986                 dC = log(dA);
   13987                 break;
   13988             case JIM_EXPROP_FUNC_LOG10:
   13989                 dC = log10(dA);
   13990                 break;
   13991             case JIM_EXPROP_FUNC_SQRT:
   13992                 dC = sqrt(dA);
   13993                 break;
   13994             default:
   13995                 abort();
   13996         }
   13997         Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
   13998     }
   13999 
   14000     Jim_DecrRefCount(interp, A);
   14001 
   14002     return rc;
   14003 }
   14004 #endif
   14005 
   14006 
   14007 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node)
   14008 {
   14009     jim_wide wA, wB;
   14010     int rc;
   14011     Jim_Obj *A, *B;
   14012 
   14013     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
   14014         return rc;
   14015     }
   14016     if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
   14017         Jim_DecrRefCount(interp, A);
   14018         return rc;
   14019     }
   14020 
   14021     rc = JIM_ERR;
   14022 
   14023     if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
   14024         jim_wide wC;
   14025 
   14026         rc = JIM_OK;
   14027 
   14028         switch (node->type) {
   14029             case JIM_EXPROP_LSHIFT:
   14030                 wC = wA << wB;
   14031                 break;
   14032             case JIM_EXPROP_RSHIFT:
   14033                 wC = wA >> wB;
   14034                 break;
   14035             case JIM_EXPROP_BITAND:
   14036                 wC = wA & wB;
   14037                 break;
   14038             case JIM_EXPROP_BITXOR:
   14039                 wC = wA ^ wB;
   14040                 break;
   14041             case JIM_EXPROP_BITOR:
   14042                 wC = wA | wB;
   14043                 break;
   14044             case JIM_EXPROP_MOD:
   14045                 if (wB == 0) {
   14046                     wC = 0;
   14047                     Jim_SetResultString(interp, "Division by zero", -1);
   14048                     rc = JIM_ERR;
   14049                 }
   14050                 else {
   14051                     int negative = 0;
   14052 
   14053                     if (wB < 0) {
   14054                         wB = -wB;
   14055                         wA = -wA;
   14056                         negative = 1;
   14057                     }
   14058                     wC = wA % wB;
   14059                     if (wC < 0) {
   14060                         wC += wB;
   14061                     }
   14062                     if (negative) {
   14063                         wC = -wC;
   14064                     }
   14065                 }
   14066                 break;
   14067             case JIM_EXPROP_ROTL:
   14068             case JIM_EXPROP_ROTR:{
   14069 
   14070                     unsigned long uA = (unsigned long)wA;
   14071                     unsigned long uB = (unsigned long)wB;
   14072                     const unsigned int S = sizeof(unsigned long) * 8;
   14073 
   14074 
   14075                     uB %= S;
   14076 
   14077                     if (node->type == JIM_EXPROP_ROTR) {
   14078                         uB = S - uB;
   14079                     }
   14080                     wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
   14081                     break;
   14082                 }
   14083             default:
   14084                 abort();
   14085         }
   14086         Jim_SetResultInt(interp, wC);
   14087     }
   14088 
   14089     Jim_DecrRefCount(interp, A);
   14090     Jim_DecrRefCount(interp, B);
   14091 
   14092     return rc;
   14093 }
   14094 
   14095 
   14096 
   14097 static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node)
   14098 {
   14099     int rc = JIM_OK;
   14100     double dA, dB, dC = 0;
   14101     jim_wide wA, wB, wC = 0;
   14102     Jim_Obj *A, *B;
   14103 
   14104     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
   14105         return rc;
   14106     }
   14107     if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
   14108         Jim_DecrRefCount(interp, A);
   14109         return rc;
   14110     }
   14111 
   14112     if ((A->typePtr != &doubleObjType || A->bytes) &&
   14113         (B->typePtr != &doubleObjType || B->bytes) &&
   14114         JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
   14115 
   14116 
   14117 
   14118         switch (node->type) {
   14119             case JIM_EXPROP_POW:
   14120             case JIM_EXPROP_FUNC_POW:
   14121                 if (wA == 0 && wB < 0) {
   14122                     Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
   14123                     rc = JIM_ERR;
   14124                     goto done;
   14125                 }
   14126                 wC = JimPowWide(wA, wB);
   14127                 goto intresult;
   14128             case JIM_EXPROP_ADD:
   14129                 wC = wA + wB;
   14130                 goto intresult;
   14131             case JIM_EXPROP_SUB:
   14132                 wC = wA - wB;
   14133                 goto intresult;
   14134             case JIM_EXPROP_MUL:
   14135                 wC = wA * wB;
   14136                 goto intresult;
   14137             case JIM_EXPROP_DIV:
   14138                 if (wB == 0) {
   14139                     Jim_SetResultString(interp, "Division by zero", -1);
   14140                     rc = JIM_ERR;
   14141                     goto done;
   14142                 }
   14143                 else {
   14144                     if (wB < 0) {
   14145                         wB = -wB;
   14146                         wA = -wA;
   14147                     }
   14148                     wC = wA / wB;
   14149                     if (wA % wB < 0) {
   14150                         wC--;
   14151                     }
   14152                     goto intresult;
   14153                 }
   14154             case JIM_EXPROP_LT:
   14155                 wC = wA < wB;
   14156                 goto intresult;
   14157             case JIM_EXPROP_GT:
   14158                 wC = wA > wB;
   14159                 goto intresult;
   14160             case JIM_EXPROP_LTE:
   14161                 wC = wA <= wB;
   14162                 goto intresult;
   14163             case JIM_EXPROP_GTE:
   14164                 wC = wA >= wB;
   14165                 goto intresult;
   14166             case JIM_EXPROP_NUMEQ:
   14167                 wC = wA == wB;
   14168                 goto intresult;
   14169             case JIM_EXPROP_NUMNE:
   14170                 wC = wA != wB;
   14171                 goto intresult;
   14172         }
   14173     }
   14174     if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
   14175         switch (node->type) {
   14176 #ifndef JIM_MATH_FUNCTIONS
   14177             case JIM_EXPROP_POW:
   14178             case JIM_EXPROP_FUNC_POW:
   14179             case JIM_EXPROP_FUNC_ATAN2:
   14180             case JIM_EXPROP_FUNC_HYPOT:
   14181             case JIM_EXPROP_FUNC_FMOD:
   14182                 Jim_SetResultString(interp, "unsupported", -1);
   14183                 rc = JIM_ERR;
   14184                 goto done;
   14185 #else
   14186             case JIM_EXPROP_POW:
   14187             case JIM_EXPROP_FUNC_POW:
   14188                 dC = pow(dA, dB);
   14189                 goto doubleresult;
   14190             case JIM_EXPROP_FUNC_ATAN2:
   14191                 dC = atan2(dA, dB);
   14192                 goto doubleresult;
   14193             case JIM_EXPROP_FUNC_HYPOT:
   14194                 dC = hypot(dA, dB);
   14195                 goto doubleresult;
   14196             case JIM_EXPROP_FUNC_FMOD:
   14197                 dC = fmod(dA, dB);
   14198                 goto doubleresult;
   14199 #endif
   14200             case JIM_EXPROP_ADD:
   14201                 dC = dA + dB;
   14202                 goto doubleresult;
   14203             case JIM_EXPROP_SUB:
   14204                 dC = dA - dB;
   14205                 goto doubleresult;
   14206             case JIM_EXPROP_MUL:
   14207                 dC = dA * dB;
   14208                 goto doubleresult;
   14209             case JIM_EXPROP_DIV:
   14210                 if (dB == 0) {
   14211 #ifdef INFINITY
   14212                     dC = dA < 0 ? -INFINITY : INFINITY;
   14213 #else
   14214                     dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
   14215 #endif
   14216                 }
   14217                 else {
   14218                     dC = dA / dB;
   14219                 }
   14220                 goto doubleresult;
   14221             case JIM_EXPROP_LT:
   14222                 wC = dA < dB;
   14223                 goto intresult;
   14224             case JIM_EXPROP_GT:
   14225                 wC = dA > dB;
   14226                 goto intresult;
   14227             case JIM_EXPROP_LTE:
   14228                 wC = dA <= dB;
   14229                 goto intresult;
   14230             case JIM_EXPROP_GTE:
   14231                 wC = dA >= dB;
   14232                 goto intresult;
   14233             case JIM_EXPROP_NUMEQ:
   14234                 wC = dA == dB;
   14235                 goto intresult;
   14236             case JIM_EXPROP_NUMNE:
   14237                 wC = dA != dB;
   14238                 goto intresult;
   14239         }
   14240     }
   14241     else {
   14242 
   14243 
   14244 
   14245         int i = Jim_StringCompareObj(interp, A, B, 0);
   14246 
   14247         switch (node->type) {
   14248             case JIM_EXPROP_LT:
   14249                 wC = i < 0;
   14250                 goto intresult;
   14251             case JIM_EXPROP_GT:
   14252                 wC = i > 0;
   14253                 goto intresult;
   14254             case JIM_EXPROP_LTE:
   14255                 wC = i <= 0;
   14256                 goto intresult;
   14257             case JIM_EXPROP_GTE:
   14258                 wC = i >= 0;
   14259                 goto intresult;
   14260             case JIM_EXPROP_NUMEQ:
   14261                 wC = i == 0;
   14262                 goto intresult;
   14263             case JIM_EXPROP_NUMNE:
   14264                 wC = i != 0;
   14265                 goto intresult;
   14266         }
   14267     }
   14268 
   14269     rc = JIM_ERR;
   14270 done:
   14271     Jim_DecrRefCount(interp, A);
   14272     Jim_DecrRefCount(interp, B);
   14273     return rc;
   14274 intresult:
   14275     Jim_SetResultInt(interp, wC);
   14276     goto done;
   14277 doubleresult:
   14278     Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
   14279     goto done;
   14280 }
   14281 
   14282 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
   14283 {
   14284     int listlen;
   14285     int i;
   14286 
   14287     listlen = Jim_ListLength(interp, listObjPtr);
   14288     for (i = 0; i < listlen; i++) {
   14289         if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
   14290             return 1;
   14291         }
   14292     }
   14293     return 0;
   14294 }
   14295 
   14296 
   14297 
   14298 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
   14299 {
   14300     Jim_Obj *A, *B;
   14301     jim_wide wC;
   14302     int comp, rc;
   14303 
   14304     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
   14305         return rc;
   14306     }
   14307     if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
   14308         Jim_DecrRefCount(interp, A);
   14309         return rc;
   14310     }
   14311 
   14312     switch (node->type) {
   14313         case JIM_EXPROP_STREQ:
   14314         case JIM_EXPROP_STRNE:
   14315             wC = Jim_StringEqObj(A, B);
   14316             if (node->type == JIM_EXPROP_STRNE) {
   14317                 wC = !wC;
   14318             }
   14319             break;
   14320         case JIM_EXPROP_STRLT:
   14321         case JIM_EXPROP_STRGT:
   14322         case JIM_EXPROP_STRLE:
   14323         case JIM_EXPROP_STRGE:
   14324             comp = Jim_StringCompareObj(interp, A, B, 0);
   14325             if (node->type == JIM_EXPROP_STRLT) {
   14326                 wC = comp == -1;
   14327             } else if (node->type == JIM_EXPROP_STRGT) {
   14328                 wC = comp == 1;
   14329             } else if (node->type == JIM_EXPROP_STRLE) {
   14330                 wC = comp == -1 || comp == 0;
   14331             } else {
   14332                 wC = comp == 0 || comp == 1;
   14333             }
   14334             break;
   14335         case JIM_EXPROP_STRIN:
   14336             wC = JimSearchList(interp, B, A);
   14337             break;
   14338         case JIM_EXPROP_STRNI:
   14339             wC = !JimSearchList(interp, B, A);
   14340             break;
   14341         default:
   14342             abort();
   14343     }
   14344     Jim_SetResultInt(interp, wC);
   14345 
   14346     Jim_DecrRefCount(interp, A);
   14347     Jim_DecrRefCount(interp, B);
   14348 
   14349     return rc;
   14350 }
   14351 
   14352 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
   14353 {
   14354     long l;
   14355     double d;
   14356     int b;
   14357     int ret = -1;
   14358 
   14359 
   14360     Jim_IncrRefCount(obj);
   14361 
   14362     if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
   14363         ret = (l != 0);
   14364     }
   14365     else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
   14366         ret = (d != 0);
   14367     }
   14368     else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
   14369         ret = (b != 0);
   14370     }
   14371 
   14372     Jim_DecrRefCount(interp, obj);
   14373     return ret;
   14374 }
   14375 
   14376 static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node)
   14377 {
   14378 
   14379     int result = JimExprGetTermBoolean(interp, node->left);
   14380 
   14381     if (result == 1) {
   14382 
   14383         result = JimExprGetTermBoolean(interp, node->right);
   14384     }
   14385     if (result == -1) {
   14386         return JIM_ERR;
   14387     }
   14388     Jim_SetResultInt(interp, result);
   14389     return JIM_OK;
   14390 }
   14391 
   14392 static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node)
   14393 {
   14394 
   14395     int result = JimExprGetTermBoolean(interp, node->left);
   14396 
   14397     if (result == 0) {
   14398 
   14399         result = JimExprGetTermBoolean(interp, node->right);
   14400     }
   14401     if (result == -1) {
   14402         return JIM_ERR;
   14403     }
   14404     Jim_SetResultInt(interp, result);
   14405     return JIM_OK;
   14406 }
   14407 
   14408 static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node)
   14409 {
   14410 
   14411     int result = JimExprGetTermBoolean(interp, node->left);
   14412 
   14413     if (result == 1) {
   14414 
   14415         return JimExprEvalTermNode(interp, node->right);
   14416     }
   14417     else if (result == 0) {
   14418 
   14419         return JimExprEvalTermNode(interp, node->ternary);
   14420     }
   14421 
   14422     return JIM_ERR;
   14423 }
   14424 
   14425 enum
   14426 {
   14427     OP_FUNC = 0x0001,
   14428     OP_RIGHT_ASSOC = 0x0002,
   14429 };
   14430 
   14431 #define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
   14432 #define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0)
   14433 
   14434 static const struct Jim_ExprOperator Jim_ExprOperators[] = {
   14435     OPRINIT("*", 110, 2, JimExprOpBin),
   14436     OPRINIT("/", 110, 2, JimExprOpBin),
   14437     OPRINIT("%", 110, 2, JimExprOpIntBin),
   14438 
   14439     OPRINIT("-", 100, 2, JimExprOpBin),
   14440     OPRINIT("+", 100, 2, JimExprOpBin),
   14441 
   14442     OPRINIT("<<", 90, 2, JimExprOpIntBin),
   14443     OPRINIT(">>", 90, 2, JimExprOpIntBin),
   14444 
   14445     OPRINIT("<<<", 90, 2, JimExprOpIntBin),
   14446     OPRINIT(">>>", 90, 2, JimExprOpIntBin),
   14447 
   14448     OPRINIT("<", 80, 2, JimExprOpBin),
   14449     OPRINIT(">", 80, 2, JimExprOpBin),
   14450     OPRINIT("<=", 80, 2, JimExprOpBin),
   14451     OPRINIT(">=", 80, 2, JimExprOpBin),
   14452 
   14453     OPRINIT("==", 70, 2, JimExprOpBin),
   14454     OPRINIT("!=", 70, 2, JimExprOpBin),
   14455 
   14456     OPRINIT("&", 50, 2, JimExprOpIntBin),
   14457     OPRINIT("^", 49, 2, JimExprOpIntBin),
   14458     OPRINIT("|", 48, 2, JimExprOpIntBin),
   14459 
   14460     OPRINIT("&&", 10, 2, JimExprOpAnd),
   14461     OPRINIT("||", 9, 2, JimExprOpOr),
   14462     OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC),
   14463     OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC),
   14464 
   14465 
   14466     OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),
   14467 
   14468     OPRINIT("eq", 60, 2, JimExprOpStrBin),
   14469     OPRINIT("ne", 60, 2, JimExprOpStrBin),
   14470 
   14471     OPRINIT("in", 55, 2, JimExprOpStrBin),
   14472     OPRINIT("ni", 55, 2, JimExprOpStrBin),
   14473 
   14474     OPRINIT("lt", 75, 2, JimExprOpStrBin),
   14475     OPRINIT("gt", 75, 2, JimExprOpStrBin),
   14476     OPRINIT("le", 75, 2, JimExprOpStrBin),
   14477     OPRINIT("ge", 75, 2, JimExprOpStrBin),
   14478 
   14479     OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
   14480     OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
   14481     OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
   14482     OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
   14483 
   14484 
   14485 
   14486     OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC),
   14487     OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC),
   14488     OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC),
   14489     OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC),
   14490     OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC),
   14491     OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC),
   14492     OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC),
   14493 
   14494 #ifdef JIM_MATH_FUNCTIONS
   14495     OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14496     OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14497     OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14498     OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14499     OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14500     OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14501     OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC),
   14502     OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14503     OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14504     OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14505     OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14506     OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14507     OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14508     OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14509     OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14510     OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
   14511     OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC),
   14512     OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC),
   14513     OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC),
   14514 #endif
   14515 };
   14516 #undef OPRINIT
   14517 #undef OPRINIT_ATTR
   14518 
   14519 #define JIM_EXPR_OPERATORS_NUM \
   14520     (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
   14521 
   14522 static int JimParseExpression(struct JimParserCtx *pc)
   14523 {
   14524     pc->errmsg = NULL;
   14525 
   14526     while (1) {
   14527 
   14528         while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
   14529             if (*pc->p == '\n') {
   14530                 pc->linenr++;
   14531             }
   14532             pc->p++;
   14533             pc->len--;
   14534         }
   14535 
   14536         if (*pc->p == '#') {
   14537             JimParseComment(pc);
   14538 
   14539             continue;
   14540         }
   14541         break;
   14542     }
   14543 
   14544 
   14545     pc->tline = pc->linenr;
   14546     pc->tstart = pc->p;
   14547 
   14548     if (pc->len == 0) {
   14549         pc->tend = pc->p;
   14550         pc->tt = JIM_TT_EOL;
   14551         pc->eof = 1;
   14552         return JIM_OK;
   14553     }
   14554     switch (*(pc->p)) {
   14555         case '(':
   14556                 pc->tt = JIM_TT_SUBEXPR_START;
   14557                 goto singlechar;
   14558         case ')':
   14559                 pc->tt = JIM_TT_SUBEXPR_END;
   14560                 goto singlechar;
   14561         case ',':
   14562             pc->tt = JIM_TT_SUBEXPR_COMMA;
   14563 singlechar:
   14564             pc->tend = pc->p;
   14565             pc->p++;
   14566             pc->len--;
   14567             break;
   14568         case '[':
   14569             return JimParseCmd(pc);
   14570         case '$':
   14571             if (JimParseVar(pc) == JIM_ERR)
   14572                 return JimParseExprOperator(pc);
   14573             else {
   14574 
   14575                 if (pc->tt == JIM_TT_EXPRSUGAR) {
   14576                     pc->errmsg = "nesting expr in expr is not allowed";
   14577                     return JIM_ERR;
   14578                 }
   14579                 return JIM_OK;
   14580             }
   14581             break;
   14582         case '0':
   14583         case '1':
   14584         case '2':
   14585         case '3':
   14586         case '4':
   14587         case '5':
   14588         case '6':
   14589         case '7':
   14590         case '8':
   14591         case '9':
   14592         case '.':
   14593             return JimParseExprNumber(pc);
   14594         case '"':
   14595             return JimParseQuote(pc);
   14596         case '{':
   14597             return JimParseBrace(pc);
   14598 
   14599         case 'N':
   14600         case 'I':
   14601         case 'n':
   14602         case 'i':
   14603             if (JimParseExprIrrational(pc) == JIM_ERR)
   14604                 if (JimParseExprBoolean(pc) == JIM_ERR)
   14605                     return JimParseExprOperator(pc);
   14606             break;
   14607         case 't':
   14608         case 'f':
   14609         case 'o':
   14610         case 'y':
   14611             if (JimParseExprBoolean(pc) == JIM_ERR)
   14612                 return JimParseExprOperator(pc);
   14613             break;
   14614         default:
   14615             return JimParseExprOperator(pc);
   14616             break;
   14617     }
   14618     return JIM_OK;
   14619 }
   14620 
   14621 static int JimParseExprNumber(struct JimParserCtx *pc)
   14622 {
   14623     char *end;
   14624 
   14625 
   14626     pc->tt = JIM_TT_EXPR_INT;
   14627 
   14628     jim_strtoull(pc->p, (char **)&pc->p);
   14629 
   14630     if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
   14631         if (strtod(pc->tstart, &end)) { }
   14632         if (end == pc->tstart)
   14633             return JIM_ERR;
   14634         if (end > pc->p) {
   14635 
   14636             pc->tt = JIM_TT_EXPR_DOUBLE;
   14637             pc->p = end;
   14638         }
   14639     }
   14640     pc->tend = pc->p - 1;
   14641     pc->len -= (pc->p - pc->tstart);
   14642     return JIM_OK;
   14643 }
   14644 
   14645 static int JimParseExprIrrational(struct JimParserCtx *pc)
   14646 {
   14647     const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
   14648     int i;
   14649 
   14650     for (i = 0; irrationals[i]; i++) {
   14651         const char *irr = irrationals[i];
   14652 
   14653         if (strncmp(irr, pc->p, 3) == 0) {
   14654             pc->p += 3;
   14655             pc->len -= 3;
   14656             pc->tend = pc->p - 1;
   14657             pc->tt = JIM_TT_EXPR_DOUBLE;
   14658             return JIM_OK;
   14659         }
   14660     }
   14661     return JIM_ERR;
   14662 }
   14663 
   14664 static int JimParseExprBoolean(struct JimParserCtx *pc)
   14665 {
   14666     int i;
   14667     for (i = 0; i < sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings); i++) {
   14668         if (strncmp(pc->p, jim_true_false_strings[i], jim_true_false_lens[i]) == 0) {
   14669             pc->p += jim_true_false_lens[i];
   14670             pc->len -= jim_true_false_lens[i];
   14671             pc->tend = pc->p - 1;
   14672             pc->tt = JIM_TT_EXPR_BOOLEAN;
   14673             return JIM_OK;
   14674         }
   14675     }
   14676     return JIM_ERR;
   14677 }
   14678 
   14679 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
   14680 {
   14681     static Jim_ExprOperator dummy_op;
   14682     if (opcode < JIM_TT_EXPR_OP) {
   14683         return &dummy_op;
   14684     }
   14685     return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
   14686 }
   14687 
   14688 static int JimParseExprOperator(struct JimParserCtx *pc)
   14689 {
   14690     int i;
   14691     const struct Jim_ExprOperator *bestOp = NULL;
   14692     int bestLen = 0;
   14693 
   14694 
   14695     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
   14696         const struct Jim_ExprOperator *op = &Jim_ExprOperators[i];
   14697 
   14698         if (op->name[0] != pc->p[0]) {
   14699             continue;
   14700         }
   14701 
   14702         if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) {
   14703             bestOp = op;
   14704             bestLen = op->namelen;
   14705         }
   14706     }
   14707     if (bestOp == NULL) {
   14708         return JIM_ERR;
   14709     }
   14710 
   14711 
   14712     if (bestOp->attr & OP_FUNC) {
   14713         const char *p = pc->p + bestLen;
   14714         int len = pc->len - bestLen;
   14715 
   14716         while (len && isspace(UCHAR(*p))) {
   14717             len--;
   14718             p++;
   14719         }
   14720         if (*p != '(') {
   14721             pc->errmsg = "function requires parentheses";
   14722             return JIM_ERR;
   14723         }
   14724     }
   14725     pc->tend = pc->p + bestLen - 1;
   14726     pc->p += bestLen;
   14727     pc->len -= bestLen;
   14728 
   14729     pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
   14730     return JIM_OK;
   14731 }
   14732 
   14733 
   14734 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
   14735 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
   14736 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
   14737 
   14738 static const Jim_ObjType exprObjType = {
   14739     "expression",
   14740     FreeExprInternalRep,
   14741     DupExprInternalRep,
   14742     NULL,
   14743     JIM_TYPE_NONE,
   14744 };
   14745 
   14746 
   14747 struct ExprTree
   14748 {
   14749     struct JimExprNode *expr;
   14750     struct JimExprNode *nodes;
   14751     int len;
   14752     int inUse;
   14753 };
   14754 
   14755 static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num)
   14756 {
   14757     int i;
   14758     for (i = 0; i < num; i++) {
   14759         if (nodes[i].objPtr) {
   14760             Jim_DecrRefCount(interp, nodes[i].objPtr);
   14761         }
   14762     }
   14763     Jim_Free(nodes);
   14764 }
   14765 
   14766 static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr)
   14767 {
   14768     ExprTreeFreeNodes(interp, expr->nodes, expr->len);
   14769     Jim_Free(expr);
   14770 }
   14771 
   14772 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
   14773 {
   14774     struct ExprTree *expr = (void *)objPtr->internalRep.ptr;
   14775 
   14776     if (expr) {
   14777         if (--expr->inUse != 0) {
   14778             return;
   14779         }
   14780 
   14781         ExprTreeFree(interp, expr);
   14782     }
   14783 }
   14784 
   14785 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
   14786 {
   14787     JIM_NOTUSED(interp);
   14788     JIM_NOTUSED(srcPtr);
   14789 
   14790 
   14791     dupPtr->typePtr = NULL;
   14792 }
   14793 
   14794 struct ExprBuilder {
   14795     int parencount;
   14796     int level;
   14797     ParseToken *token;
   14798     ParseToken *first_token;
   14799     Jim_Stack stack;
   14800     Jim_Obj *exprObjPtr;
   14801     Jim_Obj *fileNameObj;
   14802     struct JimExprNode *nodes;
   14803     struct JimExprNode *next;
   14804 };
   14805 
   14806 #ifdef DEBUG_SHOW_EXPR
   14807 static void JimShowExprNode(struct JimExprNode *node, int level)
   14808 {
   14809     int i;
   14810     for (i = 0; i < level; i++) {
   14811         printf("  ");
   14812     }
   14813     if (TOKEN_IS_EXPR_OP(node->type)) {
   14814         printf("%s\n", jim_tt_name(node->type));
   14815         if (node->left) {
   14816             JimShowExprNode(node->left, level + 1);
   14817         }
   14818         if (node->right) {
   14819             JimShowExprNode(node->right, level + 1);
   14820         }
   14821         if (node->ternary) {
   14822             JimShowExprNode(node->ternary, level + 1);
   14823         }
   14824     }
   14825     else {
   14826         printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr));
   14827     }
   14828 }
   14829 #endif
   14830 
   14831 #define EXPR_UNTIL_CLOSE 0x0001
   14832 #define EXPR_FUNC_ARGS   0x0002
   14833 #define EXPR_TERNARY     0x0004
   14834 
   14835 static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms) {
   14836     int rc;
   14837     struct JimExprNode *node;
   14838 
   14839     int exp_stacklen = builder->stack.len + exp_numterms;
   14840 
   14841     if (builder->level++ > 200) {
   14842         Jim_SetResultString(interp, "Expression too complex", -1);
   14843         return JIM_ERR;
   14844     }
   14845 
   14846     while (builder->token->type != JIM_TT_EOL) {
   14847         ParseToken *t = builder->token++;
   14848         int prevtt;
   14849 
   14850         if (t == builder->first_token) {
   14851             prevtt = JIM_TT_NONE;
   14852         }
   14853         else {
   14854             prevtt = t[-1].type;
   14855         }
   14856 
   14857         if (t->type == JIM_TT_SUBEXPR_START) {
   14858             if (builder->stack.len == exp_stacklen) {
   14859                 Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr);
   14860                 return JIM_ERR;
   14861             }
   14862             builder->parencount++;
   14863             rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1);
   14864             if (rc != JIM_OK) {
   14865                 return rc;
   14866             }
   14867 
   14868         }
   14869         else if (t->type == JIM_TT_SUBEXPR_END) {
   14870             if (!(flags & EXPR_UNTIL_CLOSE)) {
   14871                 if (builder->stack.len == exp_stacklen && builder->level > 1) {
   14872                     builder->token--;
   14873                     builder->level--;
   14874                     return JIM_OK;
   14875                 }
   14876                 Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr);
   14877                 return JIM_ERR;
   14878             }
   14879             builder->parencount--;
   14880             if (builder->stack.len == exp_stacklen) {
   14881 
   14882                 break;
   14883             }
   14884         }
   14885         else if (t->type == JIM_TT_SUBEXPR_COMMA) {
   14886             if (!(flags & EXPR_FUNC_ARGS)) {
   14887                 if (builder->stack.len == exp_stacklen) {
   14888 
   14889                     builder->token--;
   14890                     builder->level--;
   14891                     return JIM_OK;
   14892                 }
   14893                 Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr);
   14894                 return JIM_ERR;
   14895             }
   14896             else {
   14897 
   14898                 if (builder->stack.len > exp_stacklen) {
   14899                     Jim_SetResultFormatted(interp, "too many arguments to math function");
   14900                     return JIM_ERR;
   14901                 }
   14902             }
   14903 
   14904         }
   14905         else if (t->type == JIM_EXPROP_COLON) {
   14906             if (!(flags & EXPR_TERNARY)) {
   14907                 if (builder->level != 1) {
   14908 
   14909                     builder->token--;
   14910                     builder->level--;
   14911                     return JIM_OK;
   14912                 }
   14913                 Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr);
   14914                 return JIM_ERR;
   14915             }
   14916             if (builder->stack.len == exp_stacklen) {
   14917 
   14918                 builder->token--;
   14919                 builder->level--;
   14920                 return JIM_OK;
   14921             }
   14922 
   14923         }
   14924         else if (TOKEN_IS_EXPR_OP(t->type)) {
   14925             const struct Jim_ExprOperator *op;
   14926 
   14927 
   14928             if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) {
   14929                 if (t->type == JIM_EXPROP_SUB) {
   14930                     t->type = JIM_EXPROP_UNARYMINUS;
   14931                 }
   14932                 else if (t->type == JIM_EXPROP_ADD) {
   14933                     t->type = JIM_EXPROP_UNARYPLUS;
   14934                 }
   14935             }
   14936 
   14937             op = JimExprOperatorInfoByOpcode(t->type);
   14938 
   14939             if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) {
   14940 
   14941                 builder->token--;
   14942                 break;
   14943             }
   14944 
   14945             if (op->attr & OP_FUNC) {
   14946                 if (builder->token->type != JIM_TT_SUBEXPR_START) {
   14947                     Jim_SetResultString(interp, "missing arguments for math function", -1);
   14948                     return JIM_ERR;
   14949                 }
   14950                 builder->token++;
   14951                 if (op->arity == 0) {
   14952                     if (builder->token->type != JIM_TT_SUBEXPR_END) {
   14953                         Jim_SetResultString(interp, "too many arguments for math function", -1);
   14954                         return JIM_ERR;
   14955                     }
   14956                     builder->token++;
   14957                     goto noargs;
   14958                 }
   14959                 builder->parencount++;
   14960 
   14961 
   14962                 rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity);
   14963             }
   14964             else if (t->type == JIM_EXPROP_TERNARY) {
   14965 
   14966                 rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2);
   14967             }
   14968             else {
   14969                 rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1);
   14970             }
   14971 
   14972             if (rc != JIM_OK) {
   14973                 return rc;
   14974             }
   14975 
   14976 noargs:
   14977             node = builder->next++;
   14978             node->type = t->type;
   14979 
   14980             if (op->arity >= 3) {
   14981                 node->ternary = Jim_StackPop(&builder->stack);
   14982                 if (node->ternary == NULL) {
   14983                     goto missingoperand;
   14984                 }
   14985             }
   14986             if (op->arity >= 2) {
   14987                 node->right = Jim_StackPop(&builder->stack);
   14988                 if (node->right == NULL) {
   14989                     goto missingoperand;
   14990                 }
   14991             }
   14992             if (op->arity >= 1) {
   14993                 node->left = Jim_StackPop(&builder->stack);
   14994                 if (node->left == NULL) {
   14995 missingoperand:
   14996                     Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr);
   14997                     builder->next--;
   14998                     return JIM_ERR;
   14999 
   15000                 }
   15001             }
   15002 
   15003 
   15004             Jim_StackPush(&builder->stack, node);
   15005         }
   15006         else {
   15007             Jim_Obj *objPtr = NULL;
   15008 
   15009 
   15010 
   15011 
   15012             if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
   15013                 Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr);
   15014                 return JIM_ERR;
   15015             }
   15016 
   15017 
   15018             if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
   15019                 char *endptr;
   15020                 if (t->type == JIM_TT_EXPR_INT) {
   15021                     objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
   15022                 }
   15023                 else {
   15024                     objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
   15025                 }
   15026                 if (endptr != t->token + t->len) {
   15027 
   15028                     Jim_FreeNewObj(interp, objPtr);
   15029                     objPtr = NULL;
   15030                 }
   15031             }
   15032 
   15033             if (!objPtr) {
   15034 
   15035                 objPtr = Jim_NewStringObj(interp, t->token, t->len);
   15036                 if (t->type == JIM_TT_CMD) {
   15037 
   15038                     Jim_SetSourceInfo(interp, objPtr, builder->fileNameObj, t->line);
   15039                 }
   15040             }
   15041 
   15042 
   15043             node = builder->next++;
   15044             node->objPtr = objPtr;
   15045             Jim_IncrRefCount(node->objPtr);
   15046             node->type = t->type;
   15047             Jim_StackPush(&builder->stack, node);
   15048         }
   15049     }
   15050 
   15051     if (builder->stack.len == exp_stacklen) {
   15052         builder->level--;
   15053         return JIM_OK;
   15054     }
   15055 
   15056     if ((flags & EXPR_FUNC_ARGS)) {
   15057         Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many");
   15058     }
   15059     else {
   15060         if (builder->stack.len < exp_stacklen) {
   15061             if (builder->level == 0) {
   15062                 Jim_SetResultFormatted(interp, "empty expression");
   15063             }
   15064             else {
   15065                 Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr);
   15066             }
   15067         }
   15068         else {
   15069             Jim_SetResultFormatted(interp, "extra terms after expression");
   15070         }
   15071     }
   15072 
   15073     return JIM_ERR;
   15074 }
   15075 
   15076 static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
   15077 {
   15078     struct ExprTree *expr;
   15079     struct ExprBuilder builder;
   15080     int rc;
   15081     struct JimExprNode *top = NULL;
   15082 
   15083     builder.parencount = 0;
   15084     builder.level = 0;
   15085     builder.token = builder.first_token = tokenlist->list;
   15086     builder.exprObjPtr = exprObjPtr;
   15087     builder.fileNameObj = fileNameObj;
   15088 
   15089     builder.nodes = Jim_Alloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
   15090     memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
   15091     builder.next = builder.nodes;
   15092     Jim_InitStack(&builder.stack);
   15093 
   15094     rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);
   15095 
   15096     if (rc == JIM_OK) {
   15097         top = Jim_StackPop(&builder.stack);
   15098 
   15099         if (builder.parencount) {
   15100             Jim_SetResultString(interp, "missing close parenthesis", -1);
   15101             rc = JIM_ERR;
   15102         }
   15103     }
   15104 
   15105 
   15106     Jim_FreeStack(&builder.stack);
   15107 
   15108     if (rc != JIM_OK) {
   15109         ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes);
   15110         return NULL;
   15111     }
   15112 
   15113     expr = Jim_Alloc(sizeof(*expr));
   15114     expr->inUse = 1;
   15115     expr->expr = top;
   15116     expr->nodes = builder.nodes;
   15117     expr->len = builder.next - builder.nodes;
   15118 
   15119     assert(expr->len <= tokenlist->count - 1);
   15120 
   15121     return expr;
   15122 }
   15123 
   15124 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
   15125 {
   15126     int exprTextLen;
   15127     const char *exprText;
   15128     struct JimParserCtx parser;
   15129     struct ExprTree *expr;
   15130     ParseTokenList tokenlist;
   15131     int line;
   15132     Jim_Obj *fileNameObj;
   15133     int rc = JIM_ERR;
   15134 
   15135 
   15136     fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
   15137     Jim_IncrRefCount(fileNameObj);
   15138 
   15139     exprText = Jim_GetString(objPtr, &exprTextLen);
   15140 
   15141 
   15142     ScriptTokenListInit(&tokenlist);
   15143 
   15144     JimParserInit(&parser, exprText, exprTextLen, line);
   15145     while (!parser.eof) {
   15146         if (JimParseExpression(&parser) != JIM_OK) {
   15147             ScriptTokenListFree(&tokenlist);
   15148             Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
   15149             if (parser.errmsg) {
   15150                 Jim_AppendStrings(interp, Jim_GetResult(interp), ": ", parser.errmsg, NULL);
   15151             }
   15152             expr = NULL;
   15153             goto err;
   15154         }
   15155 
   15156         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
   15157             parser.tline);
   15158     }
   15159 
   15160 #ifdef DEBUG_SHOW_EXPR_TOKENS
   15161     {
   15162         int i;
   15163         printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
   15164         for (i = 0; i < tokenlist.count; i++) {
   15165             printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
   15166                 tokenlist.list[i].len, tokenlist.list[i].token);
   15167         }
   15168     }
   15169 #endif
   15170 
   15171     if (tokenlist.count <= 1) {
   15172         Jim_SetResultString(interp, "empty expression", -1);
   15173         rc = JIM_ERR;
   15174     }
   15175     else {
   15176         rc = JimParseCheckMissing(interp, parser.missing.ch);
   15177     }
   15178     if (rc != JIM_OK) {
   15179         ScriptTokenListFree(&tokenlist);
   15180         Jim_DecrRefCount(interp, fileNameObj);
   15181         return rc;
   15182     }
   15183 
   15184 
   15185     expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj);
   15186 
   15187 
   15188     ScriptTokenListFree(&tokenlist);
   15189 
   15190     if (!expr) {
   15191         goto err;
   15192     }
   15193 
   15194 #ifdef DEBUG_SHOW_EXPR
   15195     printf("==== Expr ====\n");
   15196     JimShowExprNode(expr->expr, 0);
   15197 #endif
   15198 
   15199     rc = JIM_OK;
   15200 
   15201   err:
   15202 
   15203     Jim_DecrRefCount(interp, fileNameObj);
   15204     Jim_FreeIntRep(interp, objPtr);
   15205     Jim_SetIntRepPtr(objPtr, expr);
   15206     objPtr->typePtr = &exprObjType;
   15207     return rc;
   15208 }
   15209 
   15210 static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
   15211 {
   15212     if (objPtr->typePtr != &exprObjType) {
   15213         if (SetExprFromAny(interp, objPtr) != JIM_OK) {
   15214             return NULL;
   15215         }
   15216     }
   15217     return (struct ExprTree *) Jim_GetIntRepPtr(objPtr);
   15218 }
   15219 
   15220 #ifdef JIM_OPTIMIZATION
   15221 static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node)
   15222 {
   15223     if (node->type == JIM_TT_EXPR_INT)
   15224         return node->objPtr;
   15225     else if (node->type == JIM_TT_VAR)
   15226         return Jim_GetVariable(interp, node->objPtr, JIM_NONE);
   15227     else if (node->type == JIM_TT_DICTSUGAR)
   15228         return JimExpandDictSugar(interp, node->objPtr);
   15229     else
   15230         return NULL;
   15231 }
   15232 #endif
   15233 
   15234 
   15235 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node)
   15236 {
   15237     if (TOKEN_IS_EXPR_OP(node->type)) {
   15238         const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type);
   15239         return op->funcop(interp, node);
   15240     }
   15241     else {
   15242         Jim_Obj *objPtr;
   15243 
   15244 
   15245         switch (node->type) {
   15246             case JIM_TT_EXPR_INT:
   15247             case JIM_TT_EXPR_DOUBLE:
   15248             case JIM_TT_EXPR_BOOLEAN:
   15249             case JIM_TT_STR:
   15250                 Jim_SetResult(interp, node->objPtr);
   15251                 return JIM_OK;
   15252 
   15253             case JIM_TT_VAR:
   15254                 objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG);
   15255                 if (objPtr) {
   15256                     Jim_SetResult(interp, objPtr);
   15257                     return JIM_OK;
   15258                 }
   15259                 return JIM_ERR;
   15260 
   15261             case JIM_TT_DICTSUGAR:
   15262                 objPtr = JimExpandDictSugar(interp, node->objPtr);
   15263                 if (objPtr) {
   15264                     Jim_SetResult(interp, objPtr);
   15265                     return JIM_OK;
   15266                 }
   15267                 return JIM_ERR;
   15268 
   15269             case JIM_TT_ESC:
   15270                 if (interp->safeexpr) {
   15271                     return JIM_ERR;
   15272                 }
   15273                 if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
   15274                     Jim_SetResult(interp, objPtr);
   15275                     return JIM_OK;
   15276                 }
   15277                 return JIM_ERR;
   15278 
   15279             case JIM_TT_CMD:
   15280                 if (interp->safeexpr) {
   15281                     return JIM_ERR;
   15282                 }
   15283                 return Jim_EvalObj(interp, node->objPtr);
   15284 
   15285             default:
   15286 
   15287                 return JIM_ERR;
   15288         }
   15289     }
   15290 }
   15291 
   15292 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr)
   15293 {
   15294     int rc = JimExprEvalTermNode(interp, node);
   15295     if (rc == JIM_OK) {
   15296         *objPtrPtr = Jim_GetResult(interp);
   15297         Jim_IncrRefCount(*objPtrPtr);
   15298     }
   15299     return rc;
   15300 }
   15301 
   15302 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node)
   15303 {
   15304     if (JimExprEvalTermNode(interp, node) == JIM_OK) {
   15305         return ExprBool(interp, Jim_GetResult(interp));
   15306     }
   15307     return -1;
   15308 }
   15309 
   15310 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
   15311 {
   15312     struct ExprTree *expr;
   15313     int retcode = JIM_OK;
   15314 
   15315     Jim_IncrRefCount(exprObjPtr);
   15316     expr = JimGetExpression(interp, exprObjPtr);
   15317     if (!expr) {
   15318         retcode = JIM_ERR;
   15319         goto done;
   15320     }
   15321 
   15322 #ifdef JIM_OPTIMIZATION
   15323     if (!interp->safeexpr) {
   15324         Jim_Obj *objPtr;
   15325 
   15326 
   15327         switch (expr->len) {
   15328             case 1:
   15329                 objPtr = JimExprIntValOrVar(interp, expr->expr);
   15330                 if (objPtr) {
   15331                     Jim_SetResult(interp, objPtr);
   15332                     goto done;
   15333                 }
   15334                 break;
   15335 
   15336             case 2:
   15337                 if (expr->expr->type == JIM_EXPROP_NOT) {
   15338                     objPtr = JimExprIntValOrVar(interp, expr->expr->left);
   15339 
   15340                     if (objPtr && JimIsWide(objPtr)) {
   15341                         Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj);
   15342                         goto done;
   15343                     }
   15344                 }
   15345                 break;
   15346 
   15347             case 3:
   15348                 objPtr = JimExprIntValOrVar(interp, expr->expr->left);
   15349                 if (objPtr && JimIsWide(objPtr)) {
   15350                     Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right);
   15351                     if (objPtr2 && JimIsWide(objPtr2)) {
   15352                         jim_wide wideValueA = JimWideValue(objPtr);
   15353                         jim_wide wideValueB = JimWideValue(objPtr2);
   15354                         int cmpRes;
   15355                         switch (expr->expr->type) {
   15356                             case JIM_EXPROP_LT:
   15357                                 cmpRes = wideValueA < wideValueB;
   15358                                 break;
   15359                             case JIM_EXPROP_LTE:
   15360                                 cmpRes = wideValueA <= wideValueB;
   15361                                 break;
   15362                             case JIM_EXPROP_GT:
   15363                                 cmpRes = wideValueA > wideValueB;
   15364                                 break;
   15365                             case JIM_EXPROP_GTE:
   15366                                 cmpRes = wideValueA >= wideValueB;
   15367                                 break;
   15368                             case JIM_EXPROP_NUMEQ:
   15369                                 cmpRes = wideValueA == wideValueB;
   15370                                 break;
   15371                             case JIM_EXPROP_NUMNE:
   15372                                 cmpRes = wideValueA != wideValueB;
   15373                                 break;
   15374                             default:
   15375                                 goto noopt;
   15376                         }
   15377                         Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);
   15378                         goto done;
   15379                     }
   15380                 }
   15381                 break;
   15382         }
   15383     }
   15384 noopt:
   15385 #endif
   15386 
   15387     expr->inUse++;
   15388 
   15389 
   15390     retcode = JimExprEvalTermNode(interp, expr->expr);
   15391 
   15392 
   15393     Jim_FreeIntRep(interp, exprObjPtr);
   15394     exprObjPtr->typePtr = &exprObjType;
   15395     Jim_SetIntRepPtr(exprObjPtr, expr);
   15396 
   15397 done:
   15398     Jim_DecrRefCount(interp, exprObjPtr);
   15399 
   15400     return retcode;
   15401 }
   15402 
   15403 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
   15404 {
   15405     int retcode = Jim_EvalExpression(interp, exprObjPtr);
   15406 
   15407     if (retcode == JIM_OK) {
   15408         switch (ExprBool(interp, Jim_GetResult(interp))) {
   15409             case 0:
   15410                 *boolPtr = 0;
   15411                 break;
   15412 
   15413             case 1:
   15414                 *boolPtr = 1;
   15415                 break;
   15416 
   15417             case -1:
   15418                 retcode = JIM_ERR;
   15419                 break;
   15420         }
   15421     }
   15422     return retcode;
   15423 }
   15424 
   15425 
   15426 
   15427 
   15428 typedef struct ScanFmtPartDescr
   15429 {
   15430     const char *arg;
   15431     const char *prefix;
   15432     size_t width;
   15433     int pos;
   15434     char type;
   15435     char modifier;
   15436 } ScanFmtPartDescr;
   15437 
   15438 
   15439 typedef struct ScanFmtStringObj
   15440 {
   15441     jim_wide size;
   15442     char *stringRep;
   15443     size_t count;
   15444     size_t convCount;
   15445     size_t maxPos;
   15446     const char *error;
   15447     char *scratch;
   15448     ScanFmtPartDescr descr[1];
   15449 } ScanFmtStringObj;
   15450 
   15451 
   15452 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
   15453 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
   15454 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
   15455 
   15456 static const Jim_ObjType scanFmtStringObjType = {
   15457     "scanformatstring",
   15458     FreeScanFmtInternalRep,
   15459     DupScanFmtInternalRep,
   15460     UpdateStringOfScanFmt,
   15461     JIM_TYPE_NONE,
   15462 };
   15463 
   15464 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
   15465 {
   15466     JIM_NOTUSED(interp);
   15467     Jim_Free((char *)objPtr->internalRep.ptr);
   15468     objPtr->internalRep.ptr = 0;
   15469 }
   15470 
   15471 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
   15472 {
   15473     size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
   15474     ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
   15475 
   15476     JIM_NOTUSED(interp);
   15477     memcpy(newVec, srcPtr->internalRep.ptr, size);
   15478     dupPtr->internalRep.ptr = newVec;
   15479     dupPtr->typePtr = &scanFmtStringObjType;
   15480 }
   15481 
   15482 static void UpdateStringOfScanFmt(Jim_Obj *objPtr)
   15483 {
   15484     JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep);
   15485 }
   15486 
   15487 
   15488 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
   15489 {
   15490     ScanFmtStringObj *fmtObj;
   15491     char *buffer;
   15492     int maxCount, i, approxSize, lastPos = -1;
   15493     const char *fmt = Jim_String(objPtr);
   15494     int maxFmtLen = Jim_Length(objPtr);
   15495     const char *fmtEnd = fmt + maxFmtLen;
   15496     int curr;
   15497 
   15498     Jim_FreeIntRep(interp, objPtr);
   15499 
   15500     for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
   15501         if (fmt[i] == '%')
   15502             ++maxCount;
   15503 
   15504     approxSize = sizeof(ScanFmtStringObj)
   15505         +(maxCount + 1) * sizeof(ScanFmtPartDescr)
   15506         +maxFmtLen * sizeof(char) + 3 + 1
   15507         + maxFmtLen * sizeof(char) + 1
   15508         + maxFmtLen * sizeof(char)
   15509         +(maxCount + 1) * sizeof(char)
   15510         +1;
   15511     fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
   15512     memset(fmtObj, 0, approxSize);
   15513     fmtObj->size = approxSize;
   15514     fmtObj->maxPos = 0;
   15515     fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
   15516     fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
   15517     memcpy(fmtObj->stringRep, fmt, maxFmtLen);
   15518     buffer = fmtObj->stringRep + maxFmtLen + 1;
   15519     objPtr->internalRep.ptr = fmtObj;
   15520     objPtr->typePtr = &scanFmtStringObjType;
   15521     for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
   15522         int width = 0, skip;
   15523         ScanFmtPartDescr *descr = &fmtObj->descr[curr];
   15524 
   15525         fmtObj->count++;
   15526         descr->width = 0;
   15527 
   15528         if (*fmt != '%' || fmt[1] == '%') {
   15529             descr->type = 0;
   15530             descr->prefix = &buffer[i];
   15531             for (; fmt < fmtEnd; ++fmt) {
   15532                 if (*fmt == '%') {
   15533                     if (fmt[1] != '%')
   15534                         break;
   15535                     ++fmt;
   15536                 }
   15537                 buffer[i++] = *fmt;
   15538             }
   15539             buffer[i++] = 0;
   15540         }
   15541 
   15542         ++fmt;
   15543 
   15544         if (fmt >= fmtEnd)
   15545             goto done;
   15546         descr->pos = 0;
   15547         if (*fmt == '*') {
   15548             descr->pos = -1;
   15549             ++fmt;
   15550         }
   15551         else
   15552             fmtObj->convCount++;
   15553 
   15554         if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
   15555             fmt += skip;
   15556 
   15557             if (descr->pos != -1 && *fmt == '$') {
   15558                 int prev;
   15559 
   15560                 ++fmt;
   15561                 descr->pos = width;
   15562                 width = 0;
   15563 
   15564                 if ((lastPos == 0 && descr->pos > 0)
   15565                     || (lastPos > 0 && descr->pos == 0)) {
   15566                     fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
   15567                     return JIM_ERR;
   15568                 }
   15569 
   15570                 for (prev = 0; prev < curr; ++prev) {
   15571                     if (fmtObj->descr[prev].pos == -1)
   15572                         continue;
   15573                     if (fmtObj->descr[prev].pos == descr->pos) {
   15574                         fmtObj->error =
   15575                             "variable is assigned by multiple \"%n$\" conversion specifiers";
   15576                         return JIM_ERR;
   15577                     }
   15578                 }
   15579                 if (descr->pos < 0) {
   15580                     fmtObj->error =
   15581                         "\"%n$\" conversion specifier is negative";
   15582                     return JIM_ERR;
   15583                 }
   15584 
   15585                 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
   15586                     descr->width = width;
   15587                     fmt += skip;
   15588                 }
   15589                 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
   15590                     fmtObj->maxPos = descr->pos;
   15591             }
   15592             else {
   15593 
   15594                 descr->width = width;
   15595             }
   15596         }
   15597 
   15598         if (lastPos == -1)
   15599             lastPos = descr->pos;
   15600 
   15601         if (*fmt == '[') {
   15602             int swapped = 1, beg = i, end, j;
   15603 
   15604             descr->type = '[';
   15605             descr->arg = &buffer[i];
   15606             ++fmt;
   15607             if (*fmt == '^')
   15608                 buffer[i++] = *fmt++;
   15609             if (*fmt == ']')
   15610                 buffer[i++] = *fmt++;
   15611             while (*fmt && *fmt != ']')
   15612                 buffer[i++] = *fmt++;
   15613             if (*fmt != ']') {
   15614                 fmtObj->error = "unmatched [ in format string";
   15615                 return JIM_ERR;
   15616             }
   15617             end = i;
   15618             buffer[i++] = 0;
   15619 
   15620             while (swapped) {
   15621                 swapped = 0;
   15622                 for (j = beg + 1; j < end - 1; ++j) {
   15623                     if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
   15624                         char tmp = buffer[j - 1];
   15625 
   15626                         buffer[j - 1] = buffer[j + 1];
   15627                         buffer[j + 1] = tmp;
   15628                         swapped = 1;
   15629                     }
   15630                 }
   15631             }
   15632         }
   15633         else {
   15634 
   15635             if (fmt < fmtEnd && strchr("hlL", *fmt))
   15636                 descr->modifier = tolower((int)*fmt++);
   15637 
   15638             if (fmt >= fmtEnd) {
   15639                 fmtObj->error = "missing scan conversion character";
   15640                 return JIM_ERR;
   15641             }
   15642 
   15643             descr->type = *fmt;
   15644             if (strchr("efgcsndoxui", *fmt) == 0) {
   15645                 fmtObj->error = "bad scan conversion character";
   15646                 return JIM_ERR;
   15647             }
   15648             else if (*fmt == 'c' && descr->width != 0) {
   15649                 fmtObj->error = "field width may not be specified in %c " "conversion";
   15650                 return JIM_ERR;
   15651             }
   15652             else if (*fmt == 'u' && descr->modifier == 'l') {
   15653                 fmtObj->error = "unsigned wide not supported";
   15654                 return JIM_ERR;
   15655             }
   15656         }
   15657         curr++;
   15658     }
   15659   done:
   15660     return JIM_OK;
   15661 }
   15662 
   15663 
   15664 
   15665 #define FormatGetCnvCount(_fo_) \
   15666     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
   15667 #define FormatGetMaxPos(_fo_) \
   15668     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
   15669 #define FormatGetError(_fo_) \
   15670     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
   15671 
   15672 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
   15673 {
   15674     char *buffer = Jim_StrDup(str);
   15675     char *p = buffer;
   15676 
   15677     while (*str) {
   15678         int c;
   15679         int n;
   15680 
   15681         if (!sdescr && isspace(UCHAR(*str)))
   15682             break;
   15683 
   15684         n = utf8_tounicode(str, &c);
   15685         if (sdescr && !JimCharsetMatch(sdescr, strlen(sdescr), c, JIM_CHARSET_SCAN))
   15686             break;
   15687         while (n--)
   15688             *p++ = *str++;
   15689     }
   15690     *p = 0;
   15691     return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
   15692 }
   15693 
   15694 
   15695 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int str_bytelen,
   15696     ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
   15697 {
   15698     const char *tok;
   15699     const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
   15700     size_t scanned = 0;
   15701     size_t anchor = pos;
   15702     int i;
   15703     Jim_Obj *tmpObj = NULL;
   15704 
   15705 
   15706     *valObjPtr = 0;
   15707     if (descr->prefix) {
   15708         for (i = 0; pos < str_bytelen && descr->prefix[i]; ++i) {
   15709 
   15710             if (isspace(UCHAR(descr->prefix[i])))
   15711                 while (pos < str_bytelen && isspace(UCHAR(str[pos])))
   15712                     ++pos;
   15713             else if (descr->prefix[i] != str[pos])
   15714                 break;
   15715             else
   15716                 ++pos;
   15717         }
   15718         if (pos >= str_bytelen) {
   15719             return -1;
   15720         }
   15721         else if (descr->prefix[i] != 0)
   15722             return 0;
   15723     }
   15724 
   15725     if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
   15726         while (isspace(UCHAR(str[pos])))
   15727             ++pos;
   15728 
   15729 
   15730     scanned = pos - anchor;
   15731 
   15732 
   15733     if (descr->type == 'n') {
   15734 
   15735         *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
   15736     }
   15737     else if (pos >= str_bytelen) {
   15738 
   15739         return -1;
   15740     }
   15741     else if (descr->type == 'c') {
   15742         int c;
   15743         scanned += utf8_tounicode(&str[pos], &c);
   15744         *valObjPtr = Jim_NewIntObj(interp, c);
   15745         return scanned;
   15746     }
   15747     else {
   15748 
   15749         if (descr->width > 0) {
   15750             size_t sLen = utf8_strlen(&str[pos], str_bytelen - pos);
   15751             size_t tLen = descr->width > sLen ? sLen : descr->width;
   15752 
   15753             tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
   15754             tok = tmpObj->bytes;
   15755         }
   15756         else {
   15757 
   15758             tok = &str[pos];
   15759         }
   15760         switch (descr->type) {
   15761             case 'd':
   15762             case 'o':
   15763             case 'x':
   15764             case 'u':
   15765             case 'i':{
   15766                     char *endp;
   15767                     jim_wide w;
   15768 
   15769                     int base = descr->type == 'o' ? 8
   15770                         : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
   15771 
   15772 
   15773                     if (base == 0) {
   15774                         w = jim_strtoull(tok, &endp);
   15775                     }
   15776                     else {
   15777                         w = strtoull(tok, &endp, base);
   15778                     }
   15779 
   15780                     if (endp != tok) {
   15781 
   15782                         *valObjPtr = Jim_NewIntObj(interp, w);
   15783 
   15784 
   15785                         scanned += endp - tok;
   15786                     }
   15787                     else {
   15788                         scanned = *tok ? 0 : -1;
   15789                     }
   15790                     break;
   15791                 }
   15792             case 's':
   15793             case '[':{
   15794                     *valObjPtr = JimScanAString(interp, descr->arg, tok);
   15795                     scanned += Jim_Length(*valObjPtr);
   15796                     break;
   15797                 }
   15798             case 'e':
   15799             case 'f':
   15800             case 'g':{
   15801                     char *endp;
   15802                     double value = strtod(tok, &endp);
   15803 
   15804                     if (endp != tok) {
   15805 
   15806                         *valObjPtr = Jim_NewDoubleObj(interp, value);
   15807 
   15808                         scanned += endp - tok;
   15809                     }
   15810                     else {
   15811                         scanned = *tok ? 0 : -1;
   15812                     }
   15813                     break;
   15814                 }
   15815         }
   15816         if (tmpObj) {
   15817             Jim_FreeNewObj(interp, tmpObj);
   15818         }
   15819     }
   15820     return scanned;
   15821 }
   15822 
   15823 
   15824 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
   15825 {
   15826     size_t i, pos;
   15827     int scanned = 1;
   15828     const char *str = Jim_String(strObjPtr);
   15829     int str_bytelen = Jim_Length(strObjPtr);
   15830     Jim_Obj *resultList = 0;
   15831     Jim_Obj **resultVec = 0;
   15832     int resultc;
   15833     Jim_Obj *emptyStr = 0;
   15834     ScanFmtStringObj *fmtObj;
   15835 
   15836 
   15837     JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
   15838 
   15839     fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
   15840 
   15841     if (fmtObj->error != 0) {
   15842         if (flags & JIM_ERRMSG)
   15843             Jim_SetResultString(interp, fmtObj->error, -1);
   15844         return 0;
   15845     }
   15846 
   15847     emptyStr = Jim_NewEmptyStringObj(interp);
   15848     Jim_IncrRefCount(emptyStr);
   15849 
   15850     resultList = Jim_NewListObj(interp, NULL, 0);
   15851     if (fmtObj->maxPos > 0) {
   15852         for (i = 0; i < fmtObj->maxPos; ++i)
   15853             Jim_ListAppendElement(interp, resultList, emptyStr);
   15854         JimListGetElements(interp, resultList, &resultc, &resultVec);
   15855     }
   15856 
   15857     for (i = 0, pos = 0; i < fmtObj->count; ++i) {
   15858         ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
   15859         Jim_Obj *value = 0;
   15860 
   15861 
   15862         if (descr->type == 0)
   15863             continue;
   15864 
   15865         if (scanned > 0)
   15866             scanned = ScanOneEntry(interp, str, pos, str_bytelen, fmtObj, i, &value);
   15867 
   15868         if (scanned == -1 && i == 0)
   15869             goto eof;
   15870 
   15871         pos += scanned;
   15872 
   15873 
   15874         if (value == 0)
   15875             value = Jim_NewEmptyStringObj(interp);
   15876 
   15877         if (descr->pos == -1) {
   15878             Jim_FreeNewObj(interp, value);
   15879         }
   15880         else if (descr->pos == 0)
   15881 
   15882             Jim_ListAppendElement(interp, resultList, value);
   15883         else if (resultVec[descr->pos - 1] == emptyStr) {
   15884 
   15885             Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
   15886             Jim_IncrRefCount(value);
   15887             resultVec[descr->pos - 1] = value;
   15888         }
   15889         else {
   15890 
   15891             Jim_FreeNewObj(interp, value);
   15892             goto err;
   15893         }
   15894     }
   15895     Jim_DecrRefCount(interp, emptyStr);
   15896     return resultList;
   15897   eof:
   15898     Jim_DecrRefCount(interp, emptyStr);
   15899     Jim_FreeNewObj(interp, resultList);
   15900     return (Jim_Obj *)EOF;
   15901   err:
   15902     Jim_DecrRefCount(interp, emptyStr);
   15903     Jim_FreeNewObj(interp, resultList);
   15904     return 0;
   15905 }
   15906 
   15907 
   15908 static void JimPrngInit(Jim_Interp *interp)
   15909 {
   15910 #define PRNG_SEED_SIZE 256
   15911     int i;
   15912     unsigned int *seed;
   15913     time_t t = time(NULL);
   15914 
   15915     interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
   15916 
   15917     seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
   15918     for (i = 0; i < PRNG_SEED_SIZE; i++) {
   15919         seed[i] = (rand() ^ t ^ clock());
   15920     }
   15921     JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
   15922     Jim_Free(seed);
   15923 }
   15924 
   15925 
   15926 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
   15927 {
   15928     Jim_PrngState *prng;
   15929     unsigned char *destByte = (unsigned char *)dest;
   15930     unsigned int si, sj, x;
   15931 
   15932 
   15933     if (interp->prngState == NULL)
   15934         JimPrngInit(interp);
   15935     prng = interp->prngState;
   15936 
   15937     for (x = 0; x < len; x++) {
   15938         prng->i = (prng->i + 1) & 0xff;
   15939         si = prng->sbox[prng->i];
   15940         prng->j = (prng->j + si) & 0xff;
   15941         sj = prng->sbox[prng->j];
   15942         prng->sbox[prng->i] = sj;
   15943         prng->sbox[prng->j] = si;
   15944         *destByte++ = prng->sbox[(si + sj) & 0xff];
   15945     }
   15946 }
   15947 
   15948 
   15949 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
   15950 {
   15951     int i;
   15952     Jim_PrngState *prng;
   15953 
   15954 
   15955     if (interp->prngState == NULL)
   15956         JimPrngInit(interp);
   15957     prng = interp->prngState;
   15958 
   15959 
   15960     for (i = 0; i < 256; i++)
   15961         prng->sbox[i] = i;
   15962 
   15963     for (i = 0; i < seedLen; i++) {
   15964         unsigned char t;
   15965 
   15966         t = prng->sbox[i & 0xFF];
   15967         prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
   15968         prng->sbox[seed[i]] = t;
   15969     }
   15970     prng->i = prng->j = 0;
   15971 
   15972     for (i = 0; i < 256; i += seedLen) {
   15973         JimRandomBytes(interp, seed, seedLen);
   15974     }
   15975 }
   15976 
   15977 
   15978 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   15979 {
   15980     jim_wide wideValue, increment = 1;
   15981     Jim_Obj *intObjPtr;
   15982 
   15983     if (argc != 2 && argc != 3) {
   15984         Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
   15985         return JIM_ERR;
   15986     }
   15987     if (argc == 3) {
   15988         if (Jim_GetWideExpr(interp, argv[2], &increment) != JIM_OK)
   15989             return JIM_ERR;
   15990     }
   15991     intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
   15992     if (!intObjPtr) {
   15993 
   15994         wideValue = 0;
   15995     }
   15996     else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
   15997         return JIM_ERR;
   15998     }
   15999     if (!intObjPtr || Jim_IsShared(intObjPtr)) {
   16000         intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
   16001         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
   16002             Jim_FreeNewObj(interp, intObjPtr);
   16003             return JIM_ERR;
   16004         }
   16005     }
   16006     else {
   16007 
   16008         Jim_InvalidateStringRep(intObjPtr);
   16009         JimWideValue(intObjPtr) = wideValue + increment;
   16010 
   16011         if (argv[1]->typePtr != &variableObjType) {
   16012 
   16013             Jim_SetVariable(interp, argv[1], intObjPtr);
   16014         }
   16015     }
   16016     Jim_SetResult(interp, intObjPtr);
   16017     return JIM_OK;
   16018 }
   16019 
   16020 
   16021 #define JIM_EVAL_SARGV_LEN 8
   16022 #define JIM_EVAL_SINTV_LEN 8
   16023 
   16024 static int JimTraceCallback(Jim_Interp *interp, const char *type, int argc, Jim_Obj *const *argv)
   16025 {
   16026     JimPanic((interp->traceCmdObj == NULL, "xtrace invoked with no object"));
   16027 
   16028     int ret;
   16029     Jim_Obj *nargv[7];
   16030     Jim_Obj *traceCmdObj = interp->traceCmdObj;
   16031     Jim_Obj *resultObj = Jim_GetResult(interp);
   16032     ScriptObj *script = NULL;
   16033 
   16034 
   16035 
   16036     if (interp->evalFrame->scriptObj) {
   16037         script = JimGetScript(interp, interp->evalFrame->scriptObj);
   16038     }
   16039 
   16040     nargv[0] = traceCmdObj;
   16041     nargv[1] = Jim_NewStringObj(interp, type, -1);
   16042     nargv[2] = script ? script->fileNameObj : interp->emptyObj;
   16043     nargv[3] = Jim_NewIntObj(interp, script ? script->linenr : 1);
   16044     nargv[4] = resultObj;
   16045     nargv[5] = argv[0];
   16046     nargv[6] = Jim_NewListObj(interp, argv + 1, argc - 1);
   16047 
   16048 
   16049     interp->traceCmdObj = NULL;
   16050 
   16051     Jim_IncrRefCount(resultObj);
   16052     ret = Jim_EvalObjVector(interp, 7, nargv);
   16053     Jim_DecrRefCount(interp, resultObj);
   16054 
   16055     if (ret == JIM_OK || ret == JIM_RETURN) {
   16056 
   16057         interp->traceCmdObj = traceCmdObj;
   16058         Jim_SetEmptyResult(interp);
   16059         ret = JIM_OK;
   16060     }
   16061     else {
   16062 
   16063         Jim_DecrRefCount(interp, traceCmdObj);
   16064     }
   16065     return ret;
   16066 }
   16067 
   16068 
   16069 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   16070 {
   16071     int retcode;
   16072 
   16073     if (interp->unknown_called > 50) {
   16074         return JIM_ERR;
   16075     }
   16076 
   16077 
   16078 
   16079     if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
   16080         return JIM_ERR;
   16081 
   16082     interp->unknown_called++;
   16083 
   16084     retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
   16085     interp->unknown_called--;
   16086 
   16087     return retcode;
   16088 }
   16089 
   16090 static void JimPushEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *scriptObj)
   16091 {
   16092     memset(frame, 0, sizeof(*frame));
   16093     frame->parent = interp->evalFrame;
   16094     frame->level = frame->parent->level + 1;
   16095     frame->procLevel = interp->procLevel;
   16096     frame->framePtr = interp->framePtr;
   16097     if (scriptObj) {
   16098         frame->scriptObj = scriptObj;
   16099     }
   16100     else {
   16101         frame->scriptObj = frame->parent->scriptObj;
   16102     }
   16103     interp->evalFrame = frame;
   16104 #if 0
   16105     if (frame->scriptObj) {
   16106         printf("script: %.*s\n", 20, Jim_String(frame->scriptObj));
   16107     }
   16108 #endif
   16109 }
   16110 
   16111 static void JimPopEvalFrame(Jim_Interp *interp)
   16112 {
   16113     interp->evalFrame = interp->evalFrame->parent;
   16114 }
   16115 
   16116 
   16117 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
   16118 {
   16119     int retcode;
   16120     Jim_Cmd *cmdPtr;
   16121     void *prevPrivData;
   16122     Jim_Obj *tailcallObj = NULL;
   16123 
   16124 #if 0
   16125     printf("invoke");
   16126     int j;
   16127     for (j = 0; j < objc; j++) {
   16128         printf(" '%s'", Jim_String(objv[j]));
   16129     }
   16130     printf("\n");
   16131 #endif
   16132 
   16133     cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
   16134     if (cmdPtr == NULL) {
   16135         return JimUnknown(interp, objc, objv);
   16136     }
   16137     JimIncrCmdRefCount(cmdPtr);
   16138 
   16139     if (interp->evalDepth == interp->maxEvalDepth) {
   16140         Jim_SetResultString(interp, "Infinite eval recursion", -1);
   16141         retcode = JIM_ERR;
   16142         goto out;
   16143     }
   16144     interp->evalDepth++;
   16145     prevPrivData = interp->cmdPrivData;
   16146 
   16147 tailcall:
   16148 
   16149     interp->evalFrame->argc = objc;
   16150     interp->evalFrame->argv = objv;
   16151     interp->evalFrame->cmd = cmdPtr;
   16152 
   16153     if (!interp->traceCmdObj ||
   16154         (retcode = JimTraceCallback(interp, "cmd", objc, objv)) == JIM_OK) {
   16155 
   16156         Jim_SetEmptyResult(interp);
   16157         if (cmdPtr->isproc) {
   16158             retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
   16159         }
   16160         else {
   16161             interp->cmdPrivData = cmdPtr->u.native.privData;
   16162             retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
   16163         }
   16164         if (retcode == JIM_ERR) {
   16165             JimSetErrorStack(interp, NULL);
   16166         }
   16167     }
   16168 
   16169     if (tailcallObj) {
   16170 
   16171         Jim_DecrRefCount(interp, tailcallObj);
   16172         tailcallObj = NULL;
   16173     }
   16174 
   16175 
   16176     interp->evalFrame->argc = 0;
   16177     interp->evalFrame->argv = NULL;
   16178 
   16179 
   16180     if (retcode == JIM_EVAL && interp->framePtr->tailcallObj) {
   16181         JimDecrCmdRefCount(interp, cmdPtr);
   16182 
   16183 
   16184         cmdPtr = interp->framePtr->tailcallCmd;
   16185         interp->framePtr->tailcallCmd = NULL;
   16186         tailcallObj = interp->framePtr->tailcallObj;
   16187         interp->framePtr->tailcallObj = NULL;
   16188         objc = tailcallObj->internalRep.listValue.len;
   16189         objv = tailcallObj->internalRep.listValue.ele;
   16190         goto tailcall;
   16191     }
   16192 
   16193     interp->cmdPrivData = prevPrivData;
   16194     interp->evalDepth--;
   16195 
   16196 out:
   16197     JimDecrCmdRefCount(interp, cmdPtr);
   16198 
   16199     if (retcode == JIM_ERR) {
   16200         JimSetErrorStack(interp, NULL);
   16201     }
   16202 
   16203     if (interp->framePtr->tailcallObj) {
   16204         JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
   16205         Jim_DecrRefCount(interp, interp->framePtr->tailcallObj);
   16206         interp->framePtr->tailcallCmd = NULL;
   16207         interp->framePtr->tailcallObj = NULL;
   16208     }
   16209 
   16210     return retcode;
   16211 }
   16212 
   16213 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
   16214 {
   16215     int i, retcode;
   16216     Jim_EvalFrame frame;
   16217 
   16218 
   16219     for (i = 0; i < objc; i++)
   16220         Jim_IncrRefCount(objv[i]);
   16221 
   16222 
   16223     JimPushEvalFrame(interp, &frame, NULL);
   16224 
   16225     retcode = JimInvokeCommand(interp, objc, objv);
   16226 
   16227     JimPopEvalFrame(interp);
   16228 
   16229 
   16230     for (i = 0; i < objc; i++)
   16231         Jim_DecrRefCount(interp, objv[i]);
   16232 
   16233     return retcode;
   16234 }
   16235 
   16236 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
   16237 {
   16238     int ret;
   16239     Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
   16240 
   16241     nargv[0] = prefix;
   16242     memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
   16243     ret = Jim_EvalObjVector(interp, objc + 1, nargv);
   16244     Jim_Free(nargv);
   16245     return ret;
   16246 }
   16247 
   16248 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
   16249 {
   16250     Jim_Obj *objPtr;
   16251     int ret = JIM_ERR;
   16252 
   16253     switch (token->type) {
   16254         case JIM_TT_STR:
   16255         case JIM_TT_ESC:
   16256             objPtr = token->objPtr;
   16257             break;
   16258         case JIM_TT_VAR:
   16259             objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
   16260             break;
   16261         case JIM_TT_DICTSUGAR:
   16262             objPtr = JimExpandDictSugar(interp, token->objPtr);
   16263             break;
   16264         case JIM_TT_EXPRSUGAR:
   16265             ret = Jim_EvalExpression(interp, token->objPtr);
   16266             if (ret == JIM_OK) {
   16267                 objPtr = Jim_GetResult(interp);
   16268             }
   16269             else {
   16270                 objPtr = NULL;
   16271             }
   16272             break;
   16273         case JIM_TT_CMD:
   16274             ret = Jim_EvalObj(interp, token->objPtr);
   16275             if (ret == JIM_OK || ret == JIM_RETURN) {
   16276                 objPtr = interp->result;
   16277             } else {
   16278 
   16279                 objPtr = NULL;
   16280             }
   16281             break;
   16282         default:
   16283             JimPanic((1,
   16284                 "default token type (%d) reached " "in Jim_SubstObj().", token->type));
   16285             objPtr = NULL;
   16286             break;
   16287     }
   16288     if (objPtr) {
   16289         *objPtrPtr = objPtr;
   16290         return JIM_OK;
   16291     }
   16292     return ret;
   16293 }
   16294 
   16295 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
   16296 {
   16297     int totlen = 0, i;
   16298     Jim_Obj **intv;
   16299     Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
   16300     Jim_Obj *objPtr;
   16301     char *s;
   16302 
   16303     if (tokens <= JIM_EVAL_SINTV_LEN)
   16304         intv = sintv;
   16305     else
   16306         intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
   16307 
   16308     for (i = 0; i < tokens; i++) {
   16309         switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
   16310             case JIM_OK:
   16311             case JIM_RETURN:
   16312                 break;
   16313             case JIM_BREAK:
   16314                 if (flags & JIM_SUBST_FLAG) {
   16315 
   16316                     tokens = i;
   16317                     continue;
   16318                 }
   16319 
   16320 
   16321             case JIM_CONTINUE:
   16322                 if (flags & JIM_SUBST_FLAG) {
   16323                     intv[i] = NULL;
   16324                     continue;
   16325                 }
   16326 
   16327 
   16328             default:
   16329                 while (i--) {
   16330                     Jim_DecrRefCount(interp, intv[i]);
   16331                 }
   16332                 if (intv != sintv) {
   16333                     Jim_Free(intv);
   16334                 }
   16335                 return NULL;
   16336         }
   16337         Jim_IncrRefCount(intv[i]);
   16338         Jim_String(intv[i]);
   16339         totlen += intv[i]->length;
   16340     }
   16341 
   16342 
   16343     if (tokens == 1 && intv[0] && intv == sintv) {
   16344 
   16345         intv[0]->refCount--;
   16346         return intv[0];
   16347     }
   16348 
   16349     objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
   16350 
   16351     if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
   16352         && token[2].type == JIM_TT_VAR) {
   16353 
   16354         objPtr->typePtr = &interpolatedObjType;
   16355         objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
   16356         objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
   16357         Jim_IncrRefCount(intv[2]);
   16358     }
   16359     else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
   16360 
   16361         int line;
   16362         Jim_Obj *fileNameObj = Jim_GetSourceInfo(interp, intv[0], &line);
   16363         Jim_SetSourceInfo(interp, objPtr, fileNameObj, line);
   16364     }
   16365 
   16366 
   16367     s = objPtr->bytes = Jim_Alloc(totlen + 1);
   16368     objPtr->length = totlen;
   16369     for (i = 0; i < tokens; i++) {
   16370         if (intv[i]) {
   16371             memcpy(s, intv[i]->bytes, intv[i]->length);
   16372             s += intv[i]->length;
   16373             Jim_DecrRefCount(interp, intv[i]);
   16374         }
   16375     }
   16376     objPtr->bytes[totlen] = '\0';
   16377 
   16378     if (intv != sintv) {
   16379         Jim_Free(intv);
   16380     }
   16381 
   16382     return objPtr;
   16383 }
   16384 
   16385 
   16386 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
   16387 {
   16388     int retcode = JIM_OK;
   16389     Jim_EvalFrame frame;
   16390 
   16391     JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
   16392 
   16393     JimPushEvalFrame(interp, &frame, NULL);
   16394 
   16395     if (listPtr->internalRep.listValue.len) {
   16396         Jim_IncrRefCount(listPtr);
   16397         retcode = JimInvokeCommand(interp,
   16398             listPtr->internalRep.listValue.len,
   16399             listPtr->internalRep.listValue.ele);
   16400         Jim_DecrRefCount(interp, listPtr);
   16401     }
   16402 
   16403     JimPopEvalFrame(interp);
   16404 
   16405     return retcode;
   16406 }
   16407 
   16408 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
   16409 {
   16410     SetListFromAny(interp, listPtr);
   16411     return JimEvalObjList(interp, listPtr);
   16412 }
   16413 
   16414 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
   16415 {
   16416     int i;
   16417     ScriptObj *script;
   16418     ScriptToken *token;
   16419     int retcode = JIM_OK;
   16420     Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
   16421     Jim_EvalFrame frame;
   16422 
   16423     if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
   16424         return JimEvalObjList(interp, scriptObjPtr);
   16425     }
   16426 
   16427     Jim_IncrRefCount(scriptObjPtr);
   16428     script = JimGetScript(interp, scriptObjPtr);
   16429     if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
   16430         JimSetErrorStack(interp, script);
   16431         Jim_DecrRefCount(interp, scriptObjPtr);
   16432         return JIM_ERR;
   16433     }
   16434 
   16435     Jim_SetEmptyResult(interp);
   16436 
   16437     token = script->token;
   16438 
   16439 #ifdef JIM_OPTIMIZATION
   16440     if (script->len == 0) {
   16441         Jim_DecrRefCount(interp, scriptObjPtr);
   16442         return JIM_OK;
   16443     }
   16444     if (script->len == 3
   16445         && token[1].objPtr->typePtr == &commandObjType
   16446         && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
   16447         && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
   16448         && token[2].objPtr->typePtr == &variableObjType) {
   16449 
   16450         Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
   16451 
   16452         if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
   16453             JimWideValue(objPtr)++;
   16454             Jim_InvalidateStringRep(objPtr);
   16455             Jim_DecrRefCount(interp, scriptObjPtr);
   16456             Jim_SetResult(interp, objPtr);
   16457             return JIM_OK;
   16458         }
   16459     }
   16460 #endif
   16461 
   16462     script->inUse++;
   16463 
   16464     JimPushEvalFrame(interp, &frame, scriptObjPtr);
   16465 
   16466 
   16467     interp->errorFlag = 0;
   16468     argv = sargv;
   16469 
   16470     for (i = 0; i < script->len && retcode == JIM_OK; ) {
   16471         int argc;
   16472         int j;
   16473 
   16474 
   16475         argc = token[i].objPtr->internalRep.scriptLineValue.argc;
   16476         script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
   16477 
   16478 
   16479         if (argc > JIM_EVAL_SARGV_LEN)
   16480             argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
   16481 
   16482 
   16483         i++;
   16484 
   16485         for (j = 0; j < argc; j++) {
   16486             long wordtokens = 1;
   16487             int expand = 0;
   16488             Jim_Obj *wordObjPtr = NULL;
   16489 
   16490             if (token[i].type == JIM_TT_WORD) {
   16491                 wordtokens = JimWideValue(token[i++].objPtr);
   16492                 if (wordtokens < 0) {
   16493                     expand = 1;
   16494                     wordtokens = -wordtokens;
   16495                 }
   16496             }
   16497 
   16498             if (wordtokens == 1) {
   16499 
   16500                 switch (token[i].type) {
   16501                     case JIM_TT_ESC:
   16502                     case JIM_TT_STR:
   16503                         wordObjPtr = token[i].objPtr;
   16504                         break;
   16505                     case JIM_TT_VAR:
   16506                         wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
   16507                         break;
   16508                     case JIM_TT_EXPRSUGAR:
   16509                         retcode = Jim_EvalExpression(interp, token[i].objPtr);
   16510                         if (retcode == JIM_OK) {
   16511                             wordObjPtr = Jim_GetResult(interp);
   16512                         }
   16513                         else {
   16514                             wordObjPtr = NULL;
   16515                         }
   16516                         break;
   16517                     case JIM_TT_DICTSUGAR:
   16518                         wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
   16519                         break;
   16520                     case JIM_TT_CMD:
   16521                         retcode = Jim_EvalObj(interp, token[i].objPtr);
   16522                         if (retcode == JIM_OK) {
   16523                             wordObjPtr = Jim_GetResult(interp);
   16524                         }
   16525                         break;
   16526                     default:
   16527                         JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
   16528                 }
   16529             }
   16530             else {
   16531                 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
   16532             }
   16533 
   16534             if (!wordObjPtr) {
   16535                 if (retcode == JIM_OK) {
   16536                     retcode = JIM_ERR;
   16537                 }
   16538                 break;
   16539             }
   16540 
   16541             Jim_IncrRefCount(wordObjPtr);
   16542             i += wordtokens;
   16543 
   16544             if (!expand) {
   16545                 argv[j] = wordObjPtr;
   16546             }
   16547             else {
   16548 
   16549                 int len = Jim_ListLength(interp, wordObjPtr);
   16550                 int newargc = argc + len - 1;
   16551                 int k;
   16552 
   16553                 if (len > 1) {
   16554                     if (argv == sargv) {
   16555                         if (newargc > JIM_EVAL_SARGV_LEN) {
   16556                             argv = Jim_Alloc(sizeof(*argv) * newargc);
   16557                             memcpy(argv, sargv, sizeof(*argv) * j);
   16558                         }
   16559                     }
   16560                     else {
   16561 
   16562                         argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
   16563                     }
   16564                 }
   16565 
   16566 
   16567                 for (k = 0; k < len; k++) {
   16568                     argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
   16569                     Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
   16570                 }
   16571 
   16572                 Jim_DecrRefCount(interp, wordObjPtr);
   16573 
   16574 
   16575                 j--;
   16576                 argc += len - 1;
   16577             }
   16578         }
   16579 
   16580         if (retcode == JIM_OK && argc) {
   16581 
   16582             retcode = JimInvokeCommand(interp, argc, argv);
   16583 
   16584             if (Jim_CheckSignal(interp)) {
   16585                 retcode = JIM_SIGNAL;
   16586             }
   16587         }
   16588 
   16589 
   16590         while (j-- > 0) {
   16591             Jim_DecrRefCount(interp, argv[j]);
   16592         }
   16593 
   16594         if (argv != sargv) {
   16595             Jim_Free(argv);
   16596             argv = sargv;
   16597         }
   16598     }
   16599 
   16600 
   16601     if (retcode == JIM_ERR) {
   16602         JimSetErrorStack(interp, NULL);
   16603     }
   16604 
   16605     JimPopEvalFrame(interp);
   16606 
   16607     Jim_FreeIntRep(interp, scriptObjPtr);
   16608     scriptObjPtr->typePtr = &scriptObjType;
   16609     Jim_SetIntRepPtr(scriptObjPtr, script);
   16610     Jim_DecrRefCount(interp, scriptObjPtr);
   16611 
   16612     return retcode;
   16613 }
   16614 
   16615 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
   16616 {
   16617     int retcode;
   16618 
   16619     const char *varname = Jim_String(argNameObj);
   16620     if (*varname == '&') {
   16621 
   16622         Jim_Obj *objPtr;
   16623         Jim_CallFrame *savedCallFrame = interp->framePtr;
   16624 
   16625         interp->framePtr = interp->framePtr->parent;
   16626         objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
   16627         interp->framePtr = savedCallFrame;
   16628         if (!objPtr) {
   16629             return JIM_ERR;
   16630         }
   16631 
   16632 
   16633         objPtr = Jim_NewStringObj(interp, varname + 1, -1);
   16634         Jim_IncrRefCount(objPtr);
   16635         retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
   16636         Jim_DecrRefCount(interp, objPtr);
   16637     }
   16638     else {
   16639         retcode = Jim_SetVariable(interp, argNameObj, argValObj);
   16640     }
   16641     return retcode;
   16642 }
   16643 
   16644 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
   16645 {
   16646 
   16647     Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
   16648     int i;
   16649 
   16650     for (i = 0; i < cmd->u.proc.argListLen; i++) {
   16651         Jim_AppendString(interp, argmsg, " ", 1);
   16652 
   16653         if (i == cmd->u.proc.argsPos) {
   16654             if (cmd->u.proc.arglist[i].defaultObjPtr) {
   16655 
   16656                 Jim_AppendString(interp, argmsg, "?", 1);
   16657                 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
   16658                 Jim_AppendString(interp, argmsg, " ...?", -1);
   16659             }
   16660             else {
   16661 
   16662                 Jim_AppendString(interp, argmsg, "?arg ...?", -1);
   16663             }
   16664         }
   16665         else {
   16666             if (cmd->u.proc.arglist[i].defaultObjPtr) {
   16667                 Jim_AppendString(interp, argmsg, "?", 1);
   16668                 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
   16669                 Jim_AppendString(interp, argmsg, "?", 1);
   16670             }
   16671             else {
   16672                 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
   16673                 if (*arg == '&') {
   16674                     arg++;
   16675                 }
   16676                 Jim_AppendString(interp, argmsg, arg, -1);
   16677             }
   16678         }
   16679     }
   16680     Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
   16681 }
   16682 
   16683 #ifdef jim_ext_namespace
   16684 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
   16685 {
   16686     Jim_CallFrame *callFramePtr;
   16687     int retcode;
   16688 
   16689 
   16690     callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
   16691     callFramePtr->argv = interp->evalFrame->argv;
   16692     callFramePtr->argc = interp->evalFrame->argc;
   16693     callFramePtr->procArgsObjPtr = NULL;
   16694     callFramePtr->procBodyObjPtr = scriptObj;
   16695     callFramePtr->staticVars = NULL;
   16696     Jim_IncrRefCount(scriptObj);
   16697     interp->framePtr = callFramePtr;
   16698 
   16699 
   16700     if (interp->framePtr->level == interp->maxCallFrameDepth) {
   16701         Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
   16702         retcode = JIM_ERR;
   16703     }
   16704     else {
   16705 
   16706         retcode = Jim_EvalObj(interp, scriptObj);
   16707     }
   16708 
   16709 
   16710     interp->framePtr = interp->framePtr->parent;
   16711     JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
   16712 
   16713     return retcode;
   16714 }
   16715 #endif
   16716 
   16717 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
   16718 {
   16719     Jim_CallFrame *callFramePtr;
   16720     int i, d, retcode, optargs;
   16721 
   16722 
   16723     if (argc - 1 < cmd->u.proc.reqArity ||
   16724         (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
   16725         JimSetProcWrongArgs(interp, argv[0], cmd);
   16726         return JIM_ERR;
   16727     }
   16728 
   16729     if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
   16730 
   16731         return JIM_OK;
   16732     }
   16733 
   16734 
   16735     if (interp->framePtr->level == interp->maxCallFrameDepth) {
   16736         Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
   16737         return JIM_ERR;
   16738     }
   16739 
   16740 
   16741     callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
   16742     callFramePtr->argv = argv;
   16743     callFramePtr->argc = argc;
   16744     callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
   16745     callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
   16746     callFramePtr->staticVars = cmd->u.proc.staticVars;
   16747 
   16748     interp->procLevel++;
   16749 
   16750     Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
   16751     Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
   16752     interp->framePtr = callFramePtr;
   16753 
   16754 
   16755     optargs = (argc - 1 - cmd->u.proc.reqArity);
   16756 
   16757 
   16758     i = 1;
   16759     for (d = 0; d < cmd->u.proc.argListLen; d++) {
   16760         Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
   16761         if (d == cmd->u.proc.argsPos) {
   16762 
   16763             Jim_Obj *listObjPtr;
   16764             int argsLen = 0;
   16765             if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
   16766                 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
   16767             }
   16768             listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
   16769 
   16770 
   16771             if (cmd->u.proc.arglist[d].defaultObjPtr) {
   16772                 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
   16773             }
   16774             retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
   16775             if (retcode != JIM_OK) {
   16776                 goto badargset;
   16777             }
   16778 
   16779             i += argsLen;
   16780             continue;
   16781         }
   16782 
   16783 
   16784         if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
   16785             retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
   16786         }
   16787         else {
   16788 
   16789             retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
   16790         }
   16791         if (retcode != JIM_OK) {
   16792             goto badargset;
   16793         }
   16794     }
   16795 
   16796     if (interp->traceCmdObj == NULL ||
   16797         (retcode = JimTraceCallback(interp, "proc", argc, argv)) == JIM_OK) {
   16798 
   16799         retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
   16800     }
   16801 
   16802 badargset:
   16803 
   16804 
   16805     retcode = JimInvokeDefer(interp, retcode);
   16806     interp->framePtr = interp->framePtr->parent;
   16807     JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
   16808 
   16809 
   16810     if (retcode == JIM_RETURN) {
   16811         if (--interp->returnLevel <= 0) {
   16812             retcode = interp->returnCode;
   16813             interp->returnCode = JIM_OK;
   16814             interp->returnLevel = 0;
   16815         }
   16816     }
   16817     interp->procLevel--;
   16818 
   16819     return retcode;
   16820 }
   16821 
   16822 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
   16823 {
   16824     int retval;
   16825     Jim_Obj *scriptObjPtr;
   16826 
   16827     scriptObjPtr = Jim_NewStringObj(interp, script, -1);
   16828     Jim_IncrRefCount(scriptObjPtr);
   16829     if (filename) {
   16830         Jim_SetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
   16831     }
   16832     retval = Jim_EvalObj(interp, scriptObjPtr);
   16833     Jim_DecrRefCount(interp, scriptObjPtr);
   16834     return retval;
   16835 }
   16836 
   16837 int Jim_Eval(Jim_Interp *interp, const char *script)
   16838 {
   16839     return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
   16840 }
   16841 
   16842 
   16843 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
   16844 {
   16845     int retval;
   16846     Jim_CallFrame *savedFramePtr = interp->framePtr;
   16847 
   16848     interp->framePtr = interp->topFramePtr;
   16849     retval = Jim_Eval(interp, script);
   16850     interp->framePtr = savedFramePtr;
   16851 
   16852     return retval;
   16853 }
   16854 
   16855 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
   16856 {
   16857     int retval;
   16858     Jim_CallFrame *savedFramePtr = interp->framePtr;
   16859 
   16860     interp->framePtr = interp->topFramePtr;
   16861     retval = Jim_EvalFile(interp, filename);
   16862     interp->framePtr = savedFramePtr;
   16863 
   16864     return retval;
   16865 }
   16866 
   16867 #include <sys/stat.h>
   16868 
   16869 static Jim_Obj *JimReadTextFile(Jim_Interp *interp, const char *filename)
   16870 {
   16871     jim_stat_t sb;
   16872     int fd;
   16873     char *buf;
   16874     int readlen;
   16875 
   16876     if (Jim_Stat(filename, &sb) == -1 || (fd = open(filename, O_RDONLY | O_TEXT, 0666)) < 0) {
   16877         Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
   16878         return NULL;
   16879     }
   16880     buf = Jim_Alloc(sb.st_size + 1);
   16881     readlen = read(fd, buf, sb.st_size);
   16882     close(fd);
   16883     if (readlen < 0) {
   16884         Jim_Free(buf);
   16885         Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
   16886         return NULL;
   16887     }
   16888     else {
   16889         Jim_Obj *objPtr;
   16890         buf[readlen] = 0;
   16891 
   16892         objPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
   16893 
   16894         return objPtr;
   16895     }
   16896 }
   16897 
   16898 
   16899 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
   16900 {
   16901     Jim_Obj *filenameObj;
   16902     Jim_Obj *oldFilenameObj;
   16903     Jim_Obj *scriptObjPtr;
   16904     int retcode;
   16905 
   16906     scriptObjPtr = JimReadTextFile(interp, filename);
   16907     if (!scriptObjPtr) {
   16908         return JIM_ERR;
   16909     }
   16910 
   16911     filenameObj = Jim_NewStringObj(interp, filename, -1);
   16912     Jim_SetSourceInfo(interp, scriptObjPtr, filenameObj, 1);
   16913 
   16914     oldFilenameObj = JimPushInterpObj(interp->currentFilenameObj, filenameObj);
   16915 
   16916     retcode = Jim_EvalObj(interp, scriptObjPtr);
   16917 
   16918     JimPopInterpObj(interp, interp->currentFilenameObj, oldFilenameObj);
   16919 
   16920 
   16921     if (retcode == JIM_RETURN) {
   16922         if (--interp->returnLevel <= 0) {
   16923             retcode = interp->returnCode;
   16924             interp->returnCode = JIM_OK;
   16925             interp->returnLevel = 0;
   16926         }
   16927     }
   16928 
   16929     return retcode;
   16930 }
   16931 
   16932 static void JimParseSubst(struct JimParserCtx *pc, int flags)
   16933 {
   16934     pc->tstart = pc->p;
   16935     pc->tline = pc->linenr;
   16936 
   16937     if (pc->len == 0) {
   16938         pc->tend = pc->p;
   16939         pc->tt = JIM_TT_EOL;
   16940         pc->eof = 1;
   16941         return;
   16942     }
   16943     if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
   16944         JimParseCmd(pc);
   16945         return;
   16946     }
   16947     if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
   16948         if (JimParseVar(pc) == JIM_OK) {
   16949             return;
   16950         }
   16951 
   16952         pc->tstart = pc->p;
   16953 
   16954         pc->p++;
   16955         pc->len--;
   16956     }
   16957     while (pc->len) {
   16958         if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
   16959             break;
   16960         }
   16961         if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
   16962             break;
   16963         }
   16964         if (*pc->p == '\\' && pc->len > 1) {
   16965             pc->p++;
   16966             pc->len--;
   16967         }
   16968         pc->p++;
   16969         pc->len--;
   16970     }
   16971     pc->tend = pc->p - 1;
   16972     pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
   16973 }
   16974 
   16975 
   16976 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
   16977 {
   16978     int scriptTextLen;
   16979     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
   16980     struct JimParserCtx parser;
   16981     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
   16982     ParseTokenList tokenlist;
   16983 
   16984 
   16985     ScriptTokenListInit(&tokenlist);
   16986 
   16987     JimParserInit(&parser, scriptText, scriptTextLen, 1);
   16988     while (1) {
   16989         JimParseSubst(&parser, flags);
   16990         if (parser.eof) {
   16991 
   16992             break;
   16993         }
   16994         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
   16995             parser.tline);
   16996     }
   16997 
   16998 
   16999     script->inUse = 1;
   17000     script->substFlags = flags;
   17001     script->fileNameObj = interp->emptyObj;
   17002     Jim_IncrRefCount(script->fileNameObj);
   17003     SubstObjAddTokens(interp, script, &tokenlist);
   17004 
   17005 
   17006     ScriptTokenListFree(&tokenlist);
   17007 
   17008 #ifdef DEBUG_SHOW_SUBST
   17009     {
   17010         int i;
   17011 
   17012         printf("==== Subst ====\n");
   17013         for (i = 0; i < script->len; i++) {
   17014             printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
   17015                 Jim_String(script->token[i].objPtr));
   17016         }
   17017     }
   17018 #endif
   17019 
   17020 
   17021     Jim_FreeIntRep(interp, objPtr);
   17022     Jim_SetIntRepPtr(objPtr, script);
   17023     objPtr->typePtr = &scriptObjType;
   17024     return JIM_OK;
   17025 }
   17026 
   17027 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
   17028 {
   17029     if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
   17030         SetSubstFromAny(interp, objPtr, flags);
   17031     return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
   17032 }
   17033 
   17034 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
   17035 {
   17036     ScriptObj *script;
   17037 
   17038     JimPanic((substObjPtr->refCount == 0, "Jim_SubstObj() called with zero refcount object"));
   17039 
   17040     script = Jim_GetSubst(interp, substObjPtr, flags);
   17041 
   17042     Jim_IncrRefCount(substObjPtr);
   17043     script->inUse++;
   17044 
   17045     *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
   17046 
   17047     script->inUse--;
   17048     Jim_DecrRefCount(interp, substObjPtr);
   17049     if (*resObjPtrPtr == NULL) {
   17050         return JIM_ERR;
   17051     }
   17052     return JIM_OK;
   17053 }
   17054 
   17055 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
   17056 {
   17057     Jim_Obj *objPtr;
   17058     Jim_Obj *listObjPtr;
   17059 
   17060     JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0"));
   17061 
   17062     listObjPtr = Jim_NewListObj(interp, argv, argc);
   17063 
   17064     if (msg && *msg) {
   17065         Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
   17066     }
   17067     Jim_IncrRefCount(listObjPtr);
   17068     objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
   17069     Jim_DecrRefCount(interp, listObjPtr);
   17070 
   17071     Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
   17072 }
   17073 
   17074 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
   17075     Jim_Obj *keyObjPtr, void *value, Jim_Obj *patternObjPtr, int type);
   17076 
   17077 #define JimTrivialMatch(pattern)    (strpbrk((pattern), "*[?\\") == NULL)
   17078 
   17079 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
   17080     JimHashtableIteratorCallbackType *callback, int type)
   17081 {
   17082     Jim_HashEntry *he;
   17083     Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
   17084 
   17085 
   17086     if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
   17087         he = Jim_FindHashEntry(ht, patternObjPtr);
   17088         if (he) {
   17089             callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
   17090                 patternObjPtr, type);
   17091         }
   17092     }
   17093     else {
   17094         Jim_HashTableIterator htiter;
   17095         JimInitHashTableIterator(ht, &htiter);
   17096         while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
   17097             callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
   17098                 patternObjPtr, type);
   17099         }
   17100     }
   17101     return listObjPtr;
   17102 }
   17103 
   17104 
   17105 #define JIM_CMDLIST_COMMANDS 0
   17106 #define JIM_CMDLIST_PROCS 1
   17107 #define JIM_CMDLIST_CHANNELS 2
   17108 
   17109 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
   17110     Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
   17111 {
   17112     Jim_Cmd *cmdPtr = (Jim_Cmd *)value;
   17113 
   17114     if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
   17115 
   17116         return;
   17117     }
   17118 
   17119     Jim_IncrRefCount(keyObj);
   17120 
   17121     if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, keyObj) >= 0) {
   17122         int match = 1;
   17123         if (patternObj) {
   17124             int plen, slen;
   17125             const char *pattern = Jim_GetStringNoQualifier(patternObj, &plen);
   17126             const char *str = Jim_GetStringNoQualifier(keyObj, &slen);
   17127 #ifdef JIM_NO_INTROSPECTION
   17128 
   17129             match = (JimStringCompareUtf8(pattern, plen, str, slen, 0) == 0);
   17130 #else
   17131             match = JimGlobMatch(pattern, plen, str, slen, 0);
   17132 #endif
   17133         }
   17134         if (match) {
   17135             Jim_ListAppendElement(interp, listObjPtr, keyObj);
   17136         }
   17137     }
   17138     Jim_DecrRefCount(interp, keyObj);
   17139 }
   17140 
   17141 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
   17142 {
   17143     return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
   17144 }
   17145 
   17146 
   17147 #define JIM_VARLIST_GLOBALS 0
   17148 #define JIM_VARLIST_LOCALS 1
   17149 #define JIM_VARLIST_VARS 2
   17150 #define JIM_VARLIST_MASK 0x000f
   17151 
   17152 #define JIM_VARLIST_VALUES 0x1000
   17153 
   17154 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
   17155     Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
   17156 {
   17157     Jim_VarVal *vv = (Jim_VarVal *)value;
   17158 
   17159     if ((type & JIM_VARLIST_MASK) != JIM_VARLIST_LOCALS || vv->linkFramePtr == NULL) {
   17160         if (patternObj == NULL || Jim_StringMatchObj(interp, patternObj, keyObj, 0)) {
   17161             Jim_ListAppendElement(interp, listObjPtr, keyObj);
   17162             if (type & JIM_VARLIST_VALUES) {
   17163                 Jim_ListAppendElement(interp, listObjPtr, vv->objPtr);
   17164             }
   17165         }
   17166     }
   17167 }
   17168 
   17169 
   17170 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
   17171 {
   17172     if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
   17173         return interp->emptyObj;
   17174     }
   17175     else {
   17176         Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
   17177         return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch,
   17178             mode);
   17179     }
   17180 }
   17181 
   17182 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
   17183 {
   17184     long level;
   17185 
   17186     if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
   17187         Jim_CallFrame *targetCallFrame = JimGetCallFrameByInteger(interp, level);
   17188         if (targetCallFrame && targetCallFrame != interp->topFramePtr) {
   17189 #ifdef JIM_NO_INTROSPECTION
   17190 
   17191             *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, 1);
   17192 #else
   17193             *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
   17194 #endif
   17195             return JIM_OK;
   17196         }
   17197     }
   17198     Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
   17199     return JIM_ERR;
   17200 }
   17201 
   17202 static int JimInfoFrame(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
   17203 {
   17204     long level;
   17205 
   17206     if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
   17207         Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, level);
   17208         if (frame) {
   17209             Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
   17210 
   17211             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
   17212             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "source", -1));
   17213             if (frame->scriptObj) {
   17214                 ScriptObj *script = JimGetScript(interp, frame->scriptObj);
   17215                 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "line", -1));
   17216                 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, script->linenr));
   17217                 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "file", -1));
   17218                 Jim_ListAppendElement(interp, listObj, script->fileNameObj);
   17219             }
   17220 #ifndef JIM_NO_INTROSPECTION
   17221             {
   17222                 Jim_Obj *cmdObj = Jim_NewListObj(interp, frame->argv, frame->argc);
   17223 
   17224                 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "cmd", -1));
   17225                 Jim_ListAppendElement(interp, listObj, cmdObj);
   17226             }
   17227 #endif
   17228             {
   17229                 Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
   17230                 if (procNameObj) {
   17231                     Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "proc", -1));
   17232                     Jim_ListAppendElement(interp, listObj, procNameObj);
   17233                  }
   17234             }
   17235             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "level", -1));
   17236             Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, interp->framePtr->level - frame->framePtr->level));
   17237 
   17238             *objPtrPtr = listObj;
   17239             return JIM_OK;
   17240         }
   17241     }
   17242     Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
   17243     return JIM_ERR;
   17244 }
   17245 
   17246 
   17247 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17248 {
   17249     if (argc != 2 && argc != 3) {
   17250         Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
   17251         return JIM_ERR;
   17252     }
   17253     if (argc == 3) {
   17254         if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
   17255             Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
   17256             return JIM_ERR;
   17257         }
   17258         else {
   17259             fputs(Jim_String(argv[2]), stdout);
   17260         }
   17261     }
   17262     else {
   17263         puts(Jim_String(argv[1]));
   17264     }
   17265     return JIM_OK;
   17266 }
   17267 
   17268 
   17269 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
   17270 {
   17271     jim_wide wideValue, res;
   17272     double doubleValue, doubleRes;
   17273     int i;
   17274 
   17275     res = (op == JIM_EXPROP_ADD) ? 0 : 1;
   17276 
   17277     for (i = 1; i < argc; i++) {
   17278         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
   17279             goto trydouble;
   17280         if (op == JIM_EXPROP_ADD)
   17281             res += wideValue;
   17282         else
   17283             res *= wideValue;
   17284     }
   17285     Jim_SetResultInt(interp, res);
   17286     return JIM_OK;
   17287   trydouble:
   17288     doubleRes = (double)res;
   17289     for (; i < argc; i++) {
   17290         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
   17291             return JIM_ERR;
   17292         if (op == JIM_EXPROP_ADD)
   17293             doubleRes += doubleValue;
   17294         else
   17295             doubleRes *= doubleValue;
   17296     }
   17297     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
   17298     return JIM_OK;
   17299 }
   17300 
   17301 
   17302 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
   17303 {
   17304     jim_wide wideValue, res = 0;
   17305     double doubleValue, doubleRes = 0;
   17306     int i = 2;
   17307 
   17308     if (argc < 2) {
   17309         Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
   17310         return JIM_ERR;
   17311     }
   17312     else if (argc == 2) {
   17313         if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
   17314             if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
   17315                 return JIM_ERR;
   17316             }
   17317             else {
   17318                 if (op == JIM_EXPROP_SUB)
   17319                     doubleRes = -doubleValue;
   17320                 else
   17321                     doubleRes = 1.0 / doubleValue;
   17322                 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
   17323                 return JIM_OK;
   17324             }
   17325         }
   17326         if (op == JIM_EXPROP_SUB) {
   17327             res = -wideValue;
   17328             Jim_SetResultInt(interp, res);
   17329         }
   17330         else {
   17331             doubleRes = 1.0 / wideValue;
   17332             Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
   17333         }
   17334         return JIM_OK;
   17335     }
   17336     else {
   17337         if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
   17338             if (Jim_GetDouble(interp, argv[1], &doubleRes)
   17339                 != JIM_OK) {
   17340                 return JIM_ERR;
   17341             }
   17342             else {
   17343                 goto trydouble;
   17344             }
   17345         }
   17346     }
   17347     for (i = 2; i < argc; i++) {
   17348         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
   17349             doubleRes = (double)res;
   17350             goto trydouble;
   17351         }
   17352         if (op == JIM_EXPROP_SUB)
   17353             res -= wideValue;
   17354         else {
   17355             if (wideValue == 0) {
   17356                 Jim_SetResultString(interp, "Division by zero", -1);
   17357                 return JIM_ERR;
   17358             }
   17359             res /= wideValue;
   17360         }
   17361     }
   17362     Jim_SetResultInt(interp, res);
   17363     return JIM_OK;
   17364   trydouble:
   17365     for (; i < argc; i++) {
   17366         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
   17367             return JIM_ERR;
   17368         if (op == JIM_EXPROP_SUB)
   17369             doubleRes -= doubleValue;
   17370         else
   17371             doubleRes /= doubleValue;
   17372     }
   17373     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
   17374     return JIM_OK;
   17375 }
   17376 
   17377 
   17378 
   17379 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17380 {
   17381     return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
   17382 }
   17383 
   17384 
   17385 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17386 {
   17387     return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
   17388 }
   17389 
   17390 
   17391 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17392 {
   17393     return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
   17394 }
   17395 
   17396 
   17397 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17398 {
   17399     return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
   17400 }
   17401 
   17402 
   17403 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17404 {
   17405     if (argc != 2 && argc != 3) {
   17406         Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
   17407         return JIM_ERR;
   17408     }
   17409     if (argc == 2) {
   17410         Jim_Obj *objPtr;
   17411 
   17412         objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
   17413         if (!objPtr)
   17414             return JIM_ERR;
   17415         Jim_SetResult(interp, objPtr);
   17416         return JIM_OK;
   17417     }
   17418 
   17419     if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
   17420         return JIM_ERR;
   17421     Jim_SetResult(interp, argv[2]);
   17422     return JIM_OK;
   17423 }
   17424 
   17425 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17426 {
   17427     int i = 1;
   17428     int complain = 1;
   17429 
   17430     while (i < argc) {
   17431         if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
   17432             i++;
   17433             break;
   17434         }
   17435         if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
   17436             complain = 0;
   17437             i++;
   17438             continue;
   17439         }
   17440         break;
   17441     }
   17442 
   17443     while (i < argc) {
   17444         if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
   17445             && complain) {
   17446             return JIM_ERR;
   17447         }
   17448         i++;
   17449     }
   17450 
   17451     Jim_SetEmptyResult(interp);
   17452     return JIM_OK;
   17453 }
   17454 
   17455 static int JimCheckLoopRetcode(Jim_Interp *interp, int retval)
   17456 {
   17457     if (retval == JIM_BREAK || retval == JIM_CONTINUE) {
   17458         if (--interp->break_level > 0) {
   17459             return 1;
   17460         }
   17461     }
   17462     return 0;
   17463 }
   17464 
   17465 
   17466 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17467 {
   17468     if (argc != 3) {
   17469         Jim_WrongNumArgs(interp, 1, argv, "condition body");
   17470         return JIM_ERR;
   17471     }
   17472 
   17473 
   17474     while (1) {
   17475         int boolean = 0, retval;
   17476 
   17477         if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
   17478             return retval;
   17479         if (!boolean)
   17480             break;
   17481 
   17482         if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
   17483             if (JimCheckLoopRetcode(interp, retval)) {
   17484                 return retval;
   17485             }
   17486             switch (retval) {
   17487                 case JIM_BREAK:
   17488                     goto out;
   17489                 case JIM_CONTINUE:
   17490                     continue;
   17491                 default:
   17492                     return retval;
   17493             }
   17494         }
   17495     }
   17496   out:
   17497     Jim_SetEmptyResult(interp);
   17498     return JIM_OK;
   17499 }
   17500 
   17501 
   17502 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17503 {
   17504     int retval;
   17505     int boolean = 1;
   17506     int immediate = 0;
   17507     Jim_Obj *varNamePtr = NULL;
   17508     Jim_Obj *stopVarNamePtr = NULL;
   17509 
   17510     if (argc != 5) {
   17511         Jim_WrongNumArgs(interp, 1, argv, "start test next body");
   17512         return JIM_ERR;
   17513     }
   17514 
   17515 
   17516     if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
   17517         return retval;
   17518     }
   17519 
   17520     retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
   17521 
   17522 
   17523 #ifdef JIM_OPTIMIZATION
   17524     if (retval == JIM_OK && boolean) {
   17525         ScriptObj *incrScript;
   17526         struct ExprTree *expr;
   17527         jim_wide stop, currentVal;
   17528         Jim_Obj *objPtr;
   17529         int cmpOffset;
   17530 
   17531 
   17532         expr = JimGetExpression(interp, argv[2]);
   17533         incrScript = JimGetScript(interp, argv[3]);
   17534 
   17535 
   17536         if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
   17537             goto evalstart;
   17538         }
   17539 
   17540         if (incrScript->token[1].type != JIM_TT_ESC) {
   17541             goto evalstart;
   17542         }
   17543 
   17544         if (expr->expr->type == JIM_EXPROP_LT) {
   17545             cmpOffset = 0;
   17546         }
   17547         else if (expr->expr->type == JIM_EXPROP_LTE) {
   17548             cmpOffset = 1;
   17549         }
   17550         else {
   17551             goto evalstart;
   17552         }
   17553 
   17554         if (expr->expr->left->type != JIM_TT_VAR) {
   17555             goto evalstart;
   17556         }
   17557 
   17558         if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) {
   17559             goto evalstart;
   17560         }
   17561 
   17562 
   17563         if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
   17564             goto evalstart;
   17565         }
   17566 
   17567 
   17568         if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
   17569             goto evalstart;
   17570         }
   17571 
   17572 
   17573         if (expr->expr->right->type == JIM_TT_EXPR_INT) {
   17574             if (Jim_GetWideExpr(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) {
   17575                 goto evalstart;
   17576             }
   17577         }
   17578         else {
   17579             stopVarNamePtr = expr->expr->right->objPtr;
   17580             Jim_IncrRefCount(stopVarNamePtr);
   17581 
   17582             stop = 0;
   17583         }
   17584 
   17585 
   17586         varNamePtr = expr->expr->left->objPtr;
   17587         Jim_IncrRefCount(varNamePtr);
   17588 
   17589         objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
   17590         if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
   17591             goto testcond;
   17592         }
   17593 
   17594 
   17595         while (retval == JIM_OK) {
   17596 
   17597 
   17598 
   17599 
   17600             if (stopVarNamePtr) {
   17601                 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
   17602                 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
   17603                     goto testcond;
   17604                 }
   17605             }
   17606 
   17607             if (currentVal >= stop + cmpOffset) {
   17608                 break;
   17609             }
   17610 
   17611 
   17612             retval = Jim_EvalObj(interp, argv[4]);
   17613             if (JimCheckLoopRetcode(interp, retval)) {
   17614                 immediate++;
   17615                 goto out;
   17616             }
   17617             if (retval == JIM_OK || retval == JIM_CONTINUE) {
   17618                 retval = JIM_OK;
   17619 
   17620                 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
   17621 
   17622 
   17623                 if (objPtr == NULL) {
   17624                     retval = JIM_ERR;
   17625                     goto out;
   17626                 }
   17627                 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
   17628                     currentVal = ++JimWideValue(objPtr);
   17629                     Jim_InvalidateStringRep(objPtr);
   17630                 }
   17631                 else {
   17632                     if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
   17633                         Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
   17634                                 ++currentVal)) != JIM_OK) {
   17635                         goto evalnext;
   17636                     }
   17637                 }
   17638             }
   17639         }
   17640         goto out;
   17641     }
   17642   evalstart:
   17643 #endif
   17644 
   17645     while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
   17646 
   17647         retval = Jim_EvalObj(interp, argv[4]);
   17648         if (JimCheckLoopRetcode(interp, retval)) {
   17649             immediate++;
   17650             break;
   17651         }
   17652         if (retval == JIM_OK || retval == JIM_CONTINUE) {
   17653 
   17654 JIM_IF_OPTIM(evalnext:)
   17655             retval = Jim_EvalObj(interp, argv[3]);
   17656             if (retval == JIM_OK || retval == JIM_CONTINUE) {
   17657 
   17658 JIM_IF_OPTIM(testcond:)
   17659                 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
   17660             }
   17661         }
   17662     }
   17663 JIM_IF_OPTIM(out:)
   17664     if (stopVarNamePtr) {
   17665         Jim_DecrRefCount(interp, stopVarNamePtr);
   17666     }
   17667     if (varNamePtr) {
   17668         Jim_DecrRefCount(interp, varNamePtr);
   17669     }
   17670 
   17671     if (!immediate) {
   17672         if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
   17673             Jim_SetEmptyResult(interp);
   17674             return JIM_OK;
   17675         }
   17676     }
   17677 
   17678     return retval;
   17679 }
   17680 
   17681 
   17682 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17683 {
   17684     int retval;
   17685     jim_wide i;
   17686     jim_wide limit = 0;
   17687     jim_wide incr = 1;
   17688     Jim_Obj *bodyObjPtr;
   17689 
   17690     if (argc < 4 || argc > 6) {
   17691         Jim_WrongNumArgs(interp, 1, argv, "var ?first? limit ?incr? body");
   17692         return JIM_ERR;
   17693     }
   17694 
   17695     retval = Jim_GetWideExpr(interp, argv[2], &i);
   17696     if (argc > 4 && retval == JIM_OK) {
   17697         retval = Jim_GetWideExpr(interp, argv[3], &limit);
   17698     }
   17699     if (argc > 5 && retval == JIM_OK) {
   17700         Jim_GetWideExpr(interp, argv[4], &incr);
   17701     }
   17702     if (retval != JIM_OK) {
   17703         return retval;
   17704     }
   17705     if (argc == 4) {
   17706         limit = i;
   17707         i = 0;
   17708     }
   17709     bodyObjPtr = argv[argc - 1];
   17710 
   17711     retval = Jim_SetVariable(interp, argv[1], Jim_NewIntObj(interp, i));
   17712 
   17713     while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
   17714         retval = Jim_EvalObj(interp, bodyObjPtr);
   17715         if (JimCheckLoopRetcode(interp, retval)) {
   17716             return retval;
   17717         }
   17718         if (retval == JIM_OK || retval == JIM_CONTINUE) {
   17719             Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
   17720 
   17721             retval = JIM_OK;
   17722 
   17723 
   17724             i += incr;
   17725 
   17726             if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
   17727                 if (argv[1]->typePtr != &variableObjType) {
   17728                     if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
   17729                         return JIM_ERR;
   17730                     }
   17731                 }
   17732                 JimWideValue(objPtr) = i;
   17733                 Jim_InvalidateStringRep(objPtr);
   17734 
   17735                 if (argv[1]->typePtr != &variableObjType) {
   17736                     if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
   17737                         retval = JIM_ERR;
   17738                         break;
   17739                     }
   17740                 }
   17741             }
   17742             else {
   17743                 objPtr = Jim_NewIntObj(interp, i);
   17744                 retval = Jim_SetVariable(interp, argv[1], objPtr);
   17745                 if (retval != JIM_OK) {
   17746                     Jim_FreeNewObj(interp, objPtr);
   17747                 }
   17748             }
   17749         }
   17750     }
   17751 
   17752     if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
   17753         Jim_SetEmptyResult(interp);
   17754         return JIM_OK;
   17755     }
   17756     return retval;
   17757 }
   17758 
   17759 typedef struct {
   17760     Jim_Obj *objPtr;
   17761     int idx;
   17762 } Jim_ListIter;
   17763 
   17764 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
   17765 {
   17766     iter->objPtr = objPtr;
   17767     iter->idx = 0;
   17768 }
   17769 
   17770 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
   17771 {
   17772     if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
   17773         return NULL;
   17774     }
   17775     return iter->objPtr->internalRep.listValue.ele[iter->idx++];
   17776 }
   17777 
   17778 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
   17779 {
   17780     return iter->idx >= Jim_ListLength(interp, iter->objPtr);
   17781 }
   17782 
   17783 
   17784 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
   17785 {
   17786     int result = JIM_OK;
   17787     int i, numargs;
   17788     Jim_ListIter twoiters[2];
   17789     Jim_ListIter *iters;
   17790     Jim_Obj *script;
   17791     Jim_Obj *resultObj;
   17792 
   17793     if (argc < 4 || argc % 2 != 0) {
   17794         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
   17795         return JIM_ERR;
   17796     }
   17797     script = argv[argc - 1];
   17798     numargs = (argc - 1 - 1);
   17799 
   17800     if (numargs == 2) {
   17801         iters = twoiters;
   17802     }
   17803     else {
   17804         iters = Jim_Alloc(numargs * sizeof(*iters));
   17805     }
   17806     for (i = 0; i < numargs; i++) {
   17807         JimListIterInit(&iters[i], argv[i + 1]);
   17808         if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
   17809             result = JIM_ERR;
   17810         }
   17811     }
   17812     if (result != JIM_OK) {
   17813         Jim_SetResultString(interp, "foreach varlist is empty", -1);
   17814         goto empty_varlist;
   17815     }
   17816 
   17817     if (doMap) {
   17818         resultObj = Jim_NewListObj(interp, NULL, 0);
   17819     }
   17820     else {
   17821         resultObj = interp->emptyObj;
   17822     }
   17823     Jim_IncrRefCount(resultObj);
   17824 
   17825     while (1) {
   17826 
   17827         for (i = 0; i < numargs; i += 2) {
   17828             if (!JimListIterDone(interp, &iters[i + 1])) {
   17829                 break;
   17830             }
   17831         }
   17832         if (i == numargs) {
   17833 
   17834             break;
   17835         }
   17836 
   17837 
   17838         for (i = 0; i < numargs; i += 2) {
   17839             Jim_Obj *varName;
   17840 
   17841 
   17842             JimListIterInit(&iters[i], argv[i + 1]);
   17843             while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
   17844                 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
   17845                 if (!valObj) {
   17846 
   17847                     valObj = interp->emptyObj;
   17848                 }
   17849 
   17850                 Jim_IncrRefCount(valObj);
   17851                 result = Jim_SetVariable(interp, varName, valObj);
   17852                 Jim_DecrRefCount(interp, valObj);
   17853                 if (result != JIM_OK) {
   17854                     goto err;
   17855                 }
   17856             }
   17857         }
   17858         result = Jim_EvalObj(interp, script);
   17859         if (JimCheckLoopRetcode(interp, result)) {
   17860             goto err;
   17861         }
   17862         switch (result) {
   17863             case JIM_OK:
   17864                 if (doMap) {
   17865                     Jim_ListAppendElement(interp, resultObj, interp->result);
   17866                 }
   17867                 break;
   17868             case JIM_CONTINUE:
   17869                 break;
   17870             case JIM_BREAK:
   17871                 goto out;
   17872             default:
   17873                 goto err;
   17874         }
   17875     }
   17876   out:
   17877     result = JIM_OK;
   17878     Jim_SetResult(interp, resultObj);
   17879   err:
   17880     Jim_DecrRefCount(interp, resultObj);
   17881   empty_varlist:
   17882     if (numargs > 2) {
   17883         Jim_Free(iters);
   17884     }
   17885     return result;
   17886 }
   17887 
   17888 
   17889 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17890 {
   17891     return JimForeachMapHelper(interp, argc, argv, 0);
   17892 }
   17893 
   17894 
   17895 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17896 {
   17897     return JimForeachMapHelper(interp, argc, argv, 1);
   17898 }
   17899 
   17900 
   17901 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17902 {
   17903     int result = JIM_ERR;
   17904     int i;
   17905     Jim_ListIter iter;
   17906     Jim_Obj *resultObj;
   17907 
   17908     if (argc < 2) {
   17909         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
   17910         return JIM_ERR;
   17911     }
   17912 
   17913     JimListIterInit(&iter, argv[1]);
   17914 
   17915     for (i = 2; i < argc; i++) {
   17916         Jim_Obj *valObj = JimListIterNext(interp, &iter);
   17917         result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
   17918         if (result != JIM_OK) {
   17919             return result;
   17920         }
   17921     }
   17922 
   17923     resultObj = Jim_NewListObj(interp, NULL, 0);
   17924     while (!JimListIterDone(interp, &iter)) {
   17925         Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
   17926     }
   17927 
   17928     Jim_SetResult(interp, resultObj);
   17929 
   17930     return JIM_OK;
   17931 }
   17932 
   17933 
   17934 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   17935 {
   17936     int boolean, retval, current = 1, falsebody = 0;
   17937 
   17938     if (argc >= 3) {
   17939         while (1) {
   17940 
   17941             if (current >= argc)
   17942                 goto err;
   17943             if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
   17944                 != JIM_OK)
   17945                 return retval;
   17946 
   17947             if (current >= argc)
   17948                 goto err;
   17949             if (Jim_CompareStringImmediate(interp, argv[current], "then"))
   17950                 current++;
   17951 
   17952             if (current >= argc)
   17953                 goto err;
   17954             if (boolean)
   17955                 return Jim_EvalObj(interp, argv[current]);
   17956 
   17957             if (++current >= argc) {
   17958                 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
   17959                 return JIM_OK;
   17960             }
   17961             falsebody = current++;
   17962             if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
   17963 
   17964                 if (current != argc - 1)
   17965                     goto err;
   17966                 return Jim_EvalObj(interp, argv[current]);
   17967             }
   17968             else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
   17969                 continue;
   17970 
   17971             else if (falsebody != argc - 1)
   17972                 goto err;
   17973             return Jim_EvalObj(interp, argv[falsebody]);
   17974         }
   17975         return JIM_OK;
   17976     }
   17977   err:
   17978     Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
   17979     return JIM_ERR;
   17980 }
   17981 
   17982 
   17983 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
   17984     Jim_Obj *stringObj, int flags)
   17985 {
   17986     Jim_Obj *parms[5];
   17987     int argc = 0;
   17988     long eq;
   17989     int rc;
   17990 
   17991     parms[argc++] = commandObj;
   17992     if (flags & JIM_NOCASE) {
   17993         parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
   17994     }
   17995     if (flags & JIM_OPT_END) {
   17996         parms[argc++] = Jim_NewStringObj(interp, "--", -1);
   17997     }
   17998     parms[argc++] = patternObj;
   17999     parms[argc++] = stringObj;
   18000 
   18001     rc = Jim_EvalObjVector(interp, argc, parms);
   18002 
   18003     if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
   18004         eq = -rc;
   18005     }
   18006 
   18007     return eq;
   18008 }
   18009 
   18010 
   18011 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18012 {
   18013     enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
   18014     int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
   18015     int match_flags = 0;
   18016     Jim_Obj *command = NULL, *scriptObj = NULL, *strObj;
   18017     Jim_Obj **caseList;
   18018 
   18019     if (argc < 3) {
   18020       wrongnumargs:
   18021         Jim_WrongNumArgs(interp, 1, argv, "?options? string "
   18022             "pattern body ... ?default body?   or   " "{pattern body ?pattern body ...?}");
   18023         return JIM_ERR;
   18024     }
   18025     for (opt = 1; opt < argc; ++opt) {
   18026         const char *option = Jim_String(argv[opt]);
   18027 
   18028         if (*option != '-')
   18029             break;
   18030         else if (strncmp(option, "--", 2) == 0) {
   18031             ++opt;
   18032             break;
   18033         }
   18034         else if (strncmp(option, "-exact", 2) == 0)
   18035             matchOpt = SWITCH_EXACT;
   18036         else if (strncmp(option, "-glob", 2) == 0)
   18037             matchOpt = SWITCH_GLOB;
   18038         else if (strncmp(option, "-regexp", 2) == 0) {
   18039             matchOpt = SWITCH_RE;
   18040             match_flags |= JIM_OPT_END;
   18041         }
   18042         else if (strncmp(option, "-command", 2) == 0) {
   18043             matchOpt = SWITCH_CMD;
   18044             if ((argc - opt) < 2)
   18045                 goto wrongnumargs;
   18046             command = argv[++opt];
   18047         }
   18048         else {
   18049             Jim_SetResultFormatted(interp,
   18050                 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
   18051                 argv[opt]);
   18052             return JIM_ERR;
   18053         }
   18054         if ((argc - opt) < 2)
   18055             goto wrongnumargs;
   18056     }
   18057     strObj = argv[opt++];
   18058     patCount = argc - opt;
   18059     if (patCount == 1) {
   18060         JimListGetElements(interp, argv[opt], &patCount, &caseList);
   18061     }
   18062     else
   18063         caseList = (Jim_Obj **)&argv[opt];
   18064     if (patCount == 0 || patCount % 2 != 0)
   18065         goto wrongnumargs;
   18066     for (i = 0; scriptObj == NULL && i < patCount; i += 2) {
   18067         Jim_Obj *patObj = caseList[i];
   18068 
   18069         if (!Jim_CompareStringImmediate(interp, patObj, "default")
   18070             || i < (patCount - 2)) {
   18071             switch (matchOpt) {
   18072                 case SWITCH_EXACT:
   18073                     if (Jim_StringEqObj(strObj, patObj))
   18074                         scriptObj = caseList[i + 1];
   18075                     break;
   18076                 case SWITCH_GLOB:
   18077                     if (Jim_StringMatchObj(interp, patObj, strObj, 0))
   18078                         scriptObj = caseList[i + 1];
   18079                     break;
   18080                 case SWITCH_RE:
   18081                     command = Jim_NewStringObj(interp, "regexp", -1);
   18082 
   18083                 case SWITCH_CMD:{
   18084                         int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, match_flags);
   18085 
   18086                         if (argc - opt == 1) {
   18087                             JimListGetElements(interp, argv[opt], &patCount, &caseList);
   18088                         }
   18089 
   18090                         if (rc < 0) {
   18091                             return -rc;
   18092                         }
   18093                         if (rc)
   18094                             scriptObj = caseList[i + 1];
   18095                         break;
   18096                     }
   18097             }
   18098         }
   18099         else {
   18100             scriptObj = caseList[i + 1];
   18101         }
   18102     }
   18103     for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2)
   18104         scriptObj = caseList[i + 1];
   18105     if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) {
   18106         Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
   18107         return JIM_ERR;
   18108     }
   18109     Jim_SetEmptyResult(interp);
   18110     if (scriptObj) {
   18111         return Jim_EvalObj(interp, scriptObj);
   18112     }
   18113     return JIM_OK;
   18114 }
   18115 
   18116 
   18117 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18118 {
   18119     Jim_Obj *listObjPtr;
   18120 
   18121     listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1);
   18122     Jim_SetResult(interp, listObjPtr);
   18123     return JIM_OK;
   18124 }
   18125 
   18126 
   18127 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18128 {
   18129     Jim_Obj *objPtr;
   18130     int ret;
   18131 
   18132     if (argc < 2) {
   18133         Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
   18134         return JIM_ERR;
   18135     }
   18136     ret = Jim_ListIndices(interp, argv[1], argv + 2, argc - 2, &objPtr, JIM_NONE);
   18137     if (ret < 0) {
   18138         ret = JIM_OK;
   18139         Jim_SetEmptyResult(interp);
   18140     }
   18141     else if (ret == JIM_OK) {
   18142         Jim_SetResult(interp, objPtr);
   18143     }
   18144     return ret;
   18145 }
   18146 
   18147 
   18148 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18149 {
   18150     if (argc != 2) {
   18151         Jim_WrongNumArgs(interp, 1, argv, "list");
   18152         return JIM_ERR;
   18153     }
   18154     Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
   18155     return JIM_OK;
   18156 }
   18157 
   18158 
   18159 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18160 {
   18161     static const char * const options[] = {
   18162         "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
   18163         "-stride", "-index", NULL
   18164     };
   18165     enum
   18166     { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
   18167             OPT_COMMAND, OPT_STRIDE, OPT_INDEX };
   18168     int i;
   18169     int opt_bool = 0;
   18170     int opt_not = 0;
   18171     int opt_all = 0;
   18172     int opt_inline = 0;
   18173     int opt_match = OPT_EXACT;
   18174     int listlen;
   18175     int rc = JIM_OK;
   18176     Jim_Obj *listObjPtr = NULL;
   18177     Jim_Obj *commandObj = NULL;
   18178     Jim_Obj *indexObj = NULL;
   18179     int match_flags = 0;
   18180     long stride = 1;
   18181 
   18182     if (argc < 3) {
   18183       wrongargs:
   18184         Jim_WrongNumArgs(interp, 1, argv,
   18185             "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? ?-stride len? ?-index val? list value");
   18186         return JIM_ERR;
   18187     }
   18188 
   18189     for (i = 1; i < argc - 2; i++) {
   18190         int option;
   18191 
   18192         if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
   18193             return JIM_ERR;
   18194         }
   18195         switch (option) {
   18196             case OPT_BOOL:
   18197                 opt_bool = 1;
   18198                 opt_inline = 0;
   18199                 break;
   18200             case OPT_NOT:
   18201                 opt_not = 1;
   18202                 break;
   18203             case OPT_NOCASE:
   18204                 match_flags |= JIM_NOCASE;
   18205                 break;
   18206             case OPT_INLINE:
   18207                 opt_inline = 1;
   18208                 opt_bool = 0;
   18209                 break;
   18210             case OPT_ALL:
   18211                 opt_all = 1;
   18212                 break;
   18213             case OPT_REGEXP:
   18214                 opt_match = option;
   18215                 match_flags |= JIM_OPT_END;
   18216                 break;
   18217             case OPT_COMMAND:
   18218                 if (i >= argc - 2) {
   18219                     goto wrongargs;
   18220                 }
   18221                 commandObj = argv[++i];
   18222 
   18223             case OPT_EXACT:
   18224             case OPT_GLOB:
   18225                 opt_match = option;
   18226                 break;
   18227             case OPT_INDEX:
   18228                 if (i >= argc - 2) {
   18229                     goto wrongargs;
   18230                 }
   18231                 indexObj = argv[++i];
   18232                 break;
   18233             case OPT_STRIDE:
   18234                 if (i >= argc - 2) {
   18235                     goto wrongargs;
   18236                 }
   18237                 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
   18238                     return JIM_ERR;
   18239                 }
   18240                 if (stride < 1) {
   18241                     Jim_SetResultString(interp, "stride length must be at least 1", -1);
   18242                     return JIM_ERR;
   18243                 }
   18244                 break;
   18245         }
   18246     }
   18247 
   18248     argc -= i;
   18249     if (argc < 2) {
   18250         goto wrongargs;
   18251     }
   18252     argv += i;
   18253 
   18254     listlen = Jim_ListLength(interp, argv[0]);
   18255     if (listlen % stride) {
   18256         Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
   18257         return JIM_ERR;
   18258     }
   18259 
   18260     if (opt_all) {
   18261         listObjPtr = Jim_NewListObj(interp, NULL, 0);
   18262     }
   18263     if (opt_match == OPT_REGEXP) {
   18264         commandObj = Jim_NewStringObj(interp, "regexp", -1);
   18265     }
   18266     if (commandObj) {
   18267         Jim_IncrRefCount(commandObj);
   18268     }
   18269 
   18270     for (i = 0; i < listlen; i += stride) {
   18271         int eq = 0;
   18272         Jim_Obj *searchListObj;
   18273         Jim_Obj *objPtr;
   18274         int offset;
   18275 
   18276         if (indexObj) {
   18277             int indexlen = Jim_ListLength(interp, indexObj);
   18278             if (stride == 1) {
   18279                 searchListObj = Jim_ListGetIndex(interp, argv[0], i);
   18280             }
   18281             else {
   18282                 searchListObj = Jim_NewListObj(interp, argv[0]->internalRep.listValue.ele + i, stride);
   18283             }
   18284             Jim_IncrRefCount(searchListObj);
   18285             rc = Jim_ListIndices(interp, searchListObj, indexObj->internalRep.listValue.ele, indexlen, &objPtr, JIM_ERRMSG);
   18286             if (rc != JIM_OK) {
   18287                 Jim_DecrRefCount(interp, searchListObj);
   18288                 rc = JIM_ERR;
   18289                 goto done;
   18290             }
   18291 
   18292             offset = 0;
   18293         }
   18294         else {
   18295 
   18296             searchListObj = argv[0];
   18297             offset = i;
   18298             objPtr = Jim_ListGetIndex(interp, searchListObj, i);
   18299             Jim_IncrRefCount(searchListObj);
   18300         }
   18301 
   18302         switch (opt_match) {
   18303             case OPT_EXACT:
   18304                 eq = Jim_StringCompareObj(interp, argv[1], objPtr, match_flags) == 0;
   18305                 break;
   18306 
   18307             case OPT_GLOB:
   18308                 eq = Jim_StringMatchObj(interp, argv[1], objPtr, match_flags);
   18309                 break;
   18310 
   18311             case OPT_REGEXP:
   18312             case OPT_COMMAND:
   18313                 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, match_flags);
   18314                 if (eq < 0) {
   18315                     Jim_DecrRefCount(interp, searchListObj);
   18316                     rc = JIM_ERR;
   18317                     goto done;
   18318                 }
   18319                 break;
   18320         }
   18321 
   18322 
   18323         if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
   18324             Jim_Obj *resultObj;
   18325 
   18326             if (opt_bool) {
   18327                 resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
   18328             }
   18329             else if (!opt_inline) {
   18330                 resultObj = Jim_NewIntObj(interp, i);
   18331             }
   18332             else if (stride == 1) {
   18333                 resultObj = objPtr;
   18334             }
   18335             else if (opt_all) {
   18336 
   18337                 ListInsertElements(listObjPtr, -1, stride,
   18338                     searchListObj->internalRep.listValue.ele + offset);
   18339 
   18340                 resultObj = NULL;
   18341             }
   18342             else {
   18343                 resultObj = Jim_NewListObj(interp, searchListObj->internalRep.listValue.ele + offset, stride);
   18344             }
   18345 
   18346             if (opt_all) {
   18347 
   18348                 if (stride == 1) {
   18349                     Jim_ListAppendElement(interp, listObjPtr, resultObj);
   18350                 }
   18351             }
   18352             else {
   18353                 Jim_SetResult(interp, resultObj);
   18354                 Jim_DecrRefCount(interp, searchListObj);
   18355                 goto done;
   18356             }
   18357         }
   18358         Jim_DecrRefCount(interp, searchListObj);
   18359     }
   18360 
   18361     if (opt_all) {
   18362         Jim_SetResult(interp, listObjPtr);
   18363         listObjPtr = NULL;
   18364     }
   18365     else {
   18366 
   18367         if (opt_bool) {
   18368             Jim_SetResultBool(interp, opt_not);
   18369         }
   18370         else if (!opt_inline) {
   18371             Jim_SetResultInt(interp, -1);
   18372         }
   18373     }
   18374 
   18375   done:
   18376     if (listObjPtr) {
   18377         Jim_FreeNewObj(interp, listObjPtr);
   18378     }
   18379     if (commandObj) {
   18380         Jim_DecrRefCount(interp, commandObj);
   18381     }
   18382     return rc;
   18383 }
   18384 
   18385 
   18386 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18387 {
   18388     Jim_Obj *listObjPtr;
   18389     int new_obj = 0;
   18390     int i;
   18391 
   18392     if (argc < 2) {
   18393         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
   18394         return JIM_ERR;
   18395     }
   18396     listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
   18397     if (!listObjPtr) {
   18398 
   18399         listObjPtr = Jim_NewListObj(interp, NULL, 0);
   18400         new_obj = 1;
   18401     }
   18402     else if (Jim_IsShared(listObjPtr)) {
   18403         listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
   18404         new_obj = 1;
   18405     }
   18406     for (i = 2; i < argc; i++)
   18407         Jim_ListAppendElement(interp, listObjPtr, argv[i]);
   18408     if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
   18409         if (new_obj)
   18410             Jim_FreeNewObj(interp, listObjPtr);
   18411         return JIM_ERR;
   18412     }
   18413     Jim_SetResult(interp, listObjPtr);
   18414     return JIM_OK;
   18415 }
   18416 
   18417 
   18418 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18419 {
   18420     int idx, len;
   18421     Jim_Obj *listPtr;
   18422 
   18423     if (argc < 3) {
   18424         Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
   18425         return JIM_ERR;
   18426     }
   18427     listPtr = argv[1];
   18428     if (Jim_IsShared(listPtr))
   18429         listPtr = Jim_DuplicateObj(interp, listPtr);
   18430     if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
   18431         goto err;
   18432     len = Jim_ListLength(interp, listPtr);
   18433     if (idx >= len)
   18434         idx = len;
   18435     else if (idx < 0)
   18436         idx = len + idx + 1;
   18437     Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
   18438     Jim_SetResult(interp, listPtr);
   18439     return JIM_OK;
   18440   err:
   18441     if (listPtr != argv[1]) {
   18442         Jim_FreeNewObj(interp, listPtr);
   18443     }
   18444     return JIM_ERR;
   18445 }
   18446 
   18447 
   18448 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18449 {
   18450     int first, last, len, rangeLen;
   18451     Jim_Obj *listObj;
   18452     Jim_Obj *newListObj;
   18453 
   18454     if (argc < 4) {
   18455         Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
   18456         return JIM_ERR;
   18457     }
   18458     if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
   18459         Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
   18460         return JIM_ERR;
   18461     }
   18462 
   18463     listObj = argv[1];
   18464     len = Jim_ListLength(interp, listObj);
   18465 
   18466     first = JimRelToAbsIndex(len, first);
   18467     last = JimRelToAbsIndex(len, last);
   18468     JimRelToAbsRange(len, &first, &last, &rangeLen);
   18469 
   18470 
   18471     if (first > len) {
   18472         first = len;
   18473     }
   18474 
   18475 
   18476     newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
   18477 
   18478 
   18479     ListInsertElements(newListObj, -1, argc - 4, argv + 4);
   18480 
   18481 
   18482     ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
   18483 
   18484     Jim_SetResult(interp, newListObj);
   18485     return JIM_OK;
   18486 }
   18487 
   18488 
   18489 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18490 {
   18491     if (argc < 3) {
   18492         Jim_WrongNumArgs(interp, 1, argv, "listVar ?index ...? value");
   18493         return JIM_ERR;
   18494     }
   18495     else if (argc == 3) {
   18496 
   18497         if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
   18498             return JIM_ERR;
   18499         Jim_SetResult(interp, argv[2]);
   18500         return JIM_OK;
   18501     }
   18502     return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
   18503 }
   18504 
   18505 
   18506 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
   18507 {
   18508     static const char * const options[] = {
   18509         "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique",
   18510         "-stride", "-dictionary", NULL
   18511     };
   18512     enum {
   18513         OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE,
   18514         OPT_STRIDE, OPT_DICT
   18515     };
   18516     Jim_Obj *resObj;
   18517     int i;
   18518     int retCode;
   18519     int shared;
   18520     long stride = 1;
   18521     Jim_Obj **elements;
   18522     int listlen;
   18523 
   18524     struct lsort_info info;
   18525 
   18526     if (argc < 2) {
   18527 wrongargs:
   18528         Jim_WrongNumArgs(interp, 1, argv, "?options? list");
   18529         return JIM_ERR;
   18530     }
   18531 
   18532     info.type = JIM_LSORT_ASCII;
   18533     info.order = 1;
   18534     info.indexc = 0;
   18535     info.unique = 0;
   18536     info.command = NULL;
   18537     info.interp = interp;
   18538 
   18539     for (i = 1; i < (argc - 1); i++) {
   18540         int option;
   18541 
   18542         if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
   18543             != JIM_OK)
   18544             return JIM_ERR;
   18545         switch (option) {
   18546             case OPT_ASCII:
   18547                 info.type = JIM_LSORT_ASCII;
   18548                 break;
   18549             case OPT_DICT:
   18550                 info.type = JIM_LSORT_DICT;
   18551                 break;
   18552             case OPT_NOCASE:
   18553                 info.type = JIM_LSORT_NOCASE;
   18554                 break;
   18555             case OPT_INTEGER:
   18556                 info.type = JIM_LSORT_INTEGER;
   18557                 break;
   18558             case OPT_REAL:
   18559                 info.type = JIM_LSORT_REAL;
   18560                 break;
   18561             case OPT_INCREASING:
   18562                 info.order = 1;
   18563                 break;
   18564             case OPT_DECREASING:
   18565                 info.order = -1;
   18566                 break;
   18567             case OPT_UNIQUE:
   18568                 info.unique = 1;
   18569                 break;
   18570             case OPT_COMMAND:
   18571                 if (i >= (argc - 2)) {
   18572                     Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
   18573                     return JIM_ERR;
   18574                 }
   18575                 info.type = JIM_LSORT_COMMAND;
   18576                 info.command = argv[i + 1];
   18577                 i++;
   18578                 break;
   18579             case OPT_STRIDE:
   18580                 if (i >= argc - 2) {
   18581                     goto wrongargs;
   18582                 }
   18583                 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
   18584                     return JIM_ERR;
   18585                 }
   18586                 if (stride < 2) {
   18587                     Jim_SetResultString(interp, "stride length must be at least 2", -1);
   18588                     return JIM_ERR;
   18589                 }
   18590                 break;
   18591             case OPT_INDEX:
   18592                 if (i >= (argc - 2)) {
   18593 badindex:
   18594                     Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
   18595                     return JIM_ERR;
   18596                 }
   18597                 JimListGetElements(interp, argv[i + 1], &info.indexc, &info.indexv);
   18598                 if (info.indexc == 0) {
   18599                     goto badindex;
   18600                 }
   18601                 i++;
   18602                 break;
   18603         }
   18604     }
   18605     resObj = argv[argc - 1];
   18606     JimListGetElements(interp, resObj, &listlen, &elements);
   18607     if (listlen <= 1) {
   18608 
   18609         Jim_SetResult(interp, resObj);
   18610         return JIM_OK;
   18611     }
   18612 
   18613     if (stride > 1) {
   18614         Jim_Obj *tmpListObj;
   18615         int i;
   18616 
   18617         if (listlen % stride) {
   18618             Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
   18619             return JIM_ERR;
   18620         }
   18621 
   18622         tmpListObj = Jim_NewListObj(interp, NULL, 0);
   18623         Jim_IncrRefCount(tmpListObj);
   18624         for (i = 0; i < listlen; i += stride) {
   18625             Jim_ListAppendElement(interp, tmpListObj, Jim_NewListObj(interp, elements + i, stride));
   18626         }
   18627         retCode = ListSortElements(interp, tmpListObj, &info);
   18628         if (retCode == JIM_OK) {
   18629             resObj = Jim_NewListObj(interp, NULL, 0);
   18630 
   18631             for (i = 0; i < listlen; i += stride) {
   18632                 Jim_ListAppendList(interp, resObj, Jim_ListGetIndex(interp, tmpListObj, i / stride));
   18633             }
   18634             Jim_SetResult(interp, resObj);
   18635         }
   18636         Jim_DecrRefCount(interp, tmpListObj);
   18637     }
   18638     else {
   18639         if ((shared = Jim_IsShared(resObj))) {
   18640             resObj = Jim_DuplicateObj(interp, resObj);
   18641         }
   18642         retCode = ListSortElements(interp, resObj, &info);
   18643         if (retCode == JIM_OK) {
   18644             Jim_SetResult(interp, resObj);
   18645         }
   18646         else if (shared) {
   18647             Jim_FreeNewObj(interp, resObj);
   18648         }
   18649     }
   18650     return retCode;
   18651 }
   18652 
   18653 
   18654 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18655 {
   18656     Jim_Obj *stringObjPtr;
   18657     int i;
   18658 
   18659     if (argc < 2) {
   18660         Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
   18661         return JIM_ERR;
   18662     }
   18663     if (argc == 2) {
   18664         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
   18665         if (!stringObjPtr)
   18666             return JIM_ERR;
   18667     }
   18668     else {
   18669         int new_obj = 0;
   18670         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
   18671         if (!stringObjPtr) {
   18672 
   18673             stringObjPtr = Jim_NewEmptyStringObj(interp);
   18674             new_obj = 1;
   18675         }
   18676         else if (Jim_IsShared(stringObjPtr)) {
   18677             new_obj = 1;
   18678             stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
   18679         }
   18680         for (i = 2; i < argc; i++) {
   18681             Jim_AppendObj(interp, stringObjPtr, argv[i]);
   18682         }
   18683         if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
   18684             if (new_obj) {
   18685                 Jim_FreeNewObj(interp, stringObjPtr);
   18686             }
   18687             return JIM_ERR;
   18688         }
   18689     }
   18690     Jim_SetResult(interp, stringObjPtr);
   18691     return JIM_OK;
   18692 }
   18693 
   18694 
   18695 
   18696 
   18697 
   18698 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18699 {
   18700     int rc;
   18701 
   18702     if (argc < 2) {
   18703         Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
   18704         return JIM_ERR;
   18705     }
   18706 
   18707     if (argc == 2) {
   18708         rc = Jim_EvalObj(interp, argv[1]);
   18709     }
   18710     else {
   18711         rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
   18712     }
   18713 
   18714     return rc;
   18715 }
   18716 
   18717 
   18718 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18719 {
   18720     if (argc >= 2) {
   18721         int retcode;
   18722         Jim_CallFrame *savedCallFrame, *targetCallFrame;
   18723         const char *str;
   18724 
   18725 
   18726         savedCallFrame = interp->framePtr;
   18727 
   18728 
   18729         str = Jim_String(argv[1]);
   18730         if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
   18731             targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
   18732             argc--;
   18733             argv++;
   18734         }
   18735         else {
   18736             targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
   18737         }
   18738         if (targetCallFrame == NULL) {
   18739             return JIM_ERR;
   18740         }
   18741         if (argc < 2) {
   18742             Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
   18743             return JIM_ERR;
   18744         }
   18745 
   18746         interp->framePtr = targetCallFrame;
   18747         if (argc == 2) {
   18748             retcode = Jim_EvalObj(interp, argv[1]);
   18749         }
   18750         else {
   18751             retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
   18752         }
   18753         interp->framePtr = savedCallFrame;
   18754         return retcode;
   18755     }
   18756     else {
   18757         Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
   18758         return JIM_ERR;
   18759     }
   18760 }
   18761 
   18762 
   18763 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18764 {
   18765     int retcode;
   18766 
   18767     if (argc == 2) {
   18768         retcode = Jim_EvalExpression(interp, argv[1]);
   18769     }
   18770 #ifndef JIM_COMPAT
   18771     else {
   18772         Jim_WrongNumArgs(interp, 1, argv, "expression");
   18773         retcode = JIM_ERR;
   18774     }
   18775 #else
   18776     else if (argc > 2) {
   18777         Jim_Obj *objPtr;
   18778 
   18779         objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
   18780         Jim_IncrRefCount(objPtr);
   18781         retcode = Jim_EvalExpression(interp, objPtr);
   18782         Jim_DecrRefCount(interp, objPtr);
   18783     }
   18784     else {
   18785         Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
   18786         return JIM_ERR;
   18787     }
   18788 #endif
   18789     return retcode;
   18790 }
   18791 
   18792 static int JimBreakContinueHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int retcode)
   18793 {
   18794     if (argc != 1 && argc != 2) {
   18795         Jim_WrongNumArgs(interp, 1, argv, "?level?");
   18796         return JIM_ERR;
   18797     }
   18798     if (argc == 2) {
   18799         long level;
   18800         int ret = Jim_GetLong(interp, argv[1], &level);
   18801         if (ret != JIM_OK) {
   18802             return ret;
   18803         }
   18804         interp->break_level = level;
   18805     }
   18806     return retcode;
   18807 }
   18808 
   18809 
   18810 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18811 {
   18812     return JimBreakContinueHelper(interp, argc, argv, JIM_BREAK);
   18813 }
   18814 
   18815 
   18816 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18817 {
   18818     return JimBreakContinueHelper(interp, argc, argv, JIM_CONTINUE);
   18819 }
   18820 
   18821 
   18822 static int Jim_StacktraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18823 {
   18824     Jim_Obj *listObj;
   18825     int i;
   18826     jim_wide skip = 0;
   18827     jim_wide last = 0;
   18828 
   18829     if (argc > 1) {
   18830         if (Jim_GetWideExpr(interp, argv[1], &skip) != JIM_OK) {
   18831             return JIM_ERR;
   18832         }
   18833     }
   18834     if (argc > 2) {
   18835         if (Jim_GetWideExpr(interp, argv[2], &last) != JIM_OK) {
   18836             return JIM_ERR;
   18837         }
   18838     }
   18839 
   18840     listObj = Jim_NewListObj(interp, NULL, 0);
   18841     for (i = skip; i <= interp->procLevel; i++) {
   18842         Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
   18843         if (frame->procLevel < last) {
   18844             break;
   18845         }
   18846         JimAddStackFrame(interp, frame, listObj);
   18847     }
   18848     Jim_SetResult(interp, listObj);
   18849     return JIM_OK;
   18850 }
   18851 
   18852 
   18853 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18854 {
   18855     int i;
   18856     Jim_Obj *stackTraceObj = NULL;
   18857     Jim_Obj *errorCodeObj = NULL;
   18858     int returnCode = JIM_OK;
   18859     long level = 1;
   18860 
   18861     for (i = 1; i < argc - 1; i += 2) {
   18862         if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
   18863             if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
   18864                 return JIM_ERR;
   18865             }
   18866         }
   18867         else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
   18868             stackTraceObj = argv[i + 1];
   18869         }
   18870         else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
   18871             errorCodeObj = argv[i + 1];
   18872         }
   18873         else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
   18874             if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
   18875                 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
   18876                 return JIM_ERR;
   18877             }
   18878         }
   18879         else {
   18880             break;
   18881         }
   18882     }
   18883 
   18884     if (i != argc - 1 && i != argc) {
   18885         Jim_WrongNumArgs(interp, 1, argv,
   18886             "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
   18887     }
   18888 
   18889 
   18890     if (stackTraceObj && returnCode == JIM_ERR) {
   18891         JimSetStackTrace(interp, stackTraceObj);
   18892     }
   18893 
   18894     if (errorCodeObj && returnCode == JIM_ERR) {
   18895         Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
   18896     }
   18897     interp->returnCode = returnCode;
   18898     interp->returnLevel = level;
   18899 
   18900     if (i == argc - 1) {
   18901         Jim_SetResult(interp, argv[i]);
   18902     }
   18903     return level == 0 ? returnCode : JIM_RETURN;
   18904 }
   18905 
   18906 
   18907 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18908 {
   18909     if (interp->framePtr->level == 0) {
   18910         Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
   18911         return JIM_ERR;
   18912     }
   18913     else if (argc >= 2) {
   18914 
   18915         Jim_CallFrame *cf = interp->framePtr->parent;
   18916 
   18917         Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
   18918         if (cmdPtr == NULL) {
   18919             return JIM_ERR;
   18920         }
   18921 
   18922         JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));
   18923 
   18924 
   18925         JimIncrCmdRefCount(cmdPtr);
   18926         cf->tailcallCmd = cmdPtr;
   18927 
   18928 
   18929         JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));
   18930 
   18931         cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
   18932         Jim_IncrRefCount(cf->tailcallObj);
   18933 
   18934 
   18935         return JIM_EVAL;
   18936     }
   18937     return JIM_OK;
   18938 }
   18939 
   18940 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18941 {
   18942     Jim_Obj *cmdList;
   18943     Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
   18944 
   18945 
   18946     cmdList = Jim_DuplicateObj(interp, prefixListObj);
   18947     Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
   18948 
   18949     return JimEvalObjList(interp, cmdList);
   18950 }
   18951 
   18952 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
   18953 {
   18954     Jim_Obj *prefixListObj = privData;
   18955     Jim_DecrRefCount(interp, prefixListObj);
   18956 }
   18957 
   18958 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18959 {
   18960     Jim_Obj *prefixListObj;
   18961 
   18962     if (argc < 3) {
   18963         Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
   18964         return JIM_ERR;
   18965     }
   18966 
   18967     prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
   18968     Jim_IncrRefCount(prefixListObj);
   18969     Jim_SetResult(interp, argv[1]);
   18970 
   18971     return Jim_CreateCommandObj(interp, argv[1], JimAliasCmd, prefixListObj, JimAliasCmdDelete);
   18972 }
   18973 
   18974 
   18975 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   18976 {
   18977     Jim_Cmd *cmd;
   18978 
   18979     if (argc != 4 && argc != 5) {
   18980         Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
   18981         return JIM_ERR;
   18982     }
   18983 
   18984     if (argc == 4) {
   18985         cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
   18986     }
   18987     else {
   18988         cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
   18989     }
   18990 
   18991     if (cmd) {
   18992 
   18993         Jim_Obj *nameObjPtr = JimQualifyName(interp, argv[1]);
   18994         JimCreateCommand(interp, nameObjPtr, cmd);
   18995 
   18996 
   18997         JimUpdateProcNamespace(interp, cmd, nameObjPtr);
   18998         Jim_DecrRefCount(interp, nameObjPtr);
   18999 
   19000 
   19001         Jim_SetResult(interp, argv[1]);
   19002         return JIM_OK;
   19003     }
   19004     return JIM_ERR;
   19005 }
   19006 
   19007 
   19008 static int Jim_XtraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19009 {
   19010     if (argc != 2) {
   19011         Jim_WrongNumArgs(interp, 1, argv, "callback");
   19012         return JIM_ERR;
   19013     }
   19014 
   19015     if (interp->traceCmdObj) {
   19016         Jim_DecrRefCount(interp, interp->traceCmdObj);
   19017         interp->traceCmdObj = NULL;
   19018     }
   19019 
   19020     if (Jim_Length(argv[1])) {
   19021 
   19022         interp->traceCmdObj = argv[1];
   19023         Jim_IncrRefCount(interp->traceCmdObj);
   19024     }
   19025     return JIM_OK;
   19026 }
   19027 
   19028 
   19029 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19030 {
   19031     int retcode;
   19032 
   19033     if (argc < 2) {
   19034         Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
   19035         return JIM_ERR;
   19036     }
   19037 
   19038 
   19039     interp->local++;
   19040     retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
   19041     interp->local--;
   19042 
   19043 
   19044 
   19045     if (retcode == 0) {
   19046         Jim_Obj *cmdNameObj = Jim_GetResult(interp);
   19047 
   19048         if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
   19049             return JIM_ERR;
   19050         }
   19051         if (interp->framePtr->localCommands == NULL) {
   19052             interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
   19053             Jim_InitStack(interp->framePtr->localCommands);
   19054         }
   19055         Jim_IncrRefCount(cmdNameObj);
   19056         Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
   19057     }
   19058 
   19059     return retcode;
   19060 }
   19061 
   19062 
   19063 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19064 {
   19065     if (argc < 2) {
   19066         Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
   19067         return JIM_ERR;
   19068     }
   19069     else {
   19070         int retcode;
   19071 
   19072         Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
   19073         if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
   19074             Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
   19075             return JIM_ERR;
   19076         }
   19077 
   19078         cmdPtr->u.proc.upcall++;
   19079         JimIncrCmdRefCount(cmdPtr);
   19080 
   19081 
   19082         retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
   19083 
   19084 
   19085         cmdPtr->u.proc.upcall--;
   19086         JimDecrCmdRefCount(interp, cmdPtr);
   19087 
   19088         return retcode;
   19089     }
   19090 }
   19091 
   19092 
   19093 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19094 {
   19095     if (argc < 2) {
   19096         Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
   19097         return JIM_ERR;
   19098     }
   19099     else {
   19100         int ret;
   19101         Jim_Cmd *cmd;
   19102         Jim_Obj *argListObjPtr;
   19103         Jim_Obj *bodyObjPtr;
   19104         Jim_Obj *nsObj = NULL;
   19105         Jim_Obj **nargv;
   19106 
   19107         int len = Jim_ListLength(interp, argv[1]);
   19108         if (len != 2 && len != 3) {
   19109             Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
   19110             return JIM_ERR;
   19111         }
   19112 
   19113         if (len == 3) {
   19114 #ifdef jim_ext_namespace
   19115 
   19116             nsObj = Jim_ListGetIndex(interp, argv[1], 2);
   19117 #else
   19118             Jim_SetResultString(interp, "namespaces not enabled", -1);
   19119             return JIM_ERR;
   19120 #endif
   19121         }
   19122         argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
   19123         bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
   19124 
   19125         cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
   19126 
   19127         if (cmd) {
   19128 
   19129             nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
   19130             nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
   19131             Jim_IncrRefCount(nargv[0]);
   19132             memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
   19133             ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
   19134             Jim_DecrRefCount(interp, nargv[0]);
   19135             Jim_Free(nargv);
   19136 
   19137             JimDecrCmdRefCount(interp, cmd);
   19138             return ret;
   19139         }
   19140         return JIM_ERR;
   19141     }
   19142 }
   19143 
   19144 
   19145 
   19146 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19147 {
   19148     Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
   19149     return JIM_OK;
   19150 }
   19151 
   19152 
   19153 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19154 {
   19155     int i;
   19156     Jim_CallFrame *targetCallFrame;
   19157 
   19158 
   19159     if (argc > 3 && (argc % 2 == 0)) {
   19160         targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
   19161         argc--;
   19162         argv++;
   19163     }
   19164     else {
   19165         targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
   19166     }
   19167     if (targetCallFrame == NULL) {
   19168         return JIM_ERR;
   19169     }
   19170 
   19171 
   19172     if (argc < 3) {
   19173         Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
   19174         return JIM_ERR;
   19175     }
   19176 
   19177 
   19178     for (i = 1; i < argc; i += 2) {
   19179         if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
   19180             return JIM_ERR;
   19181     }
   19182     return JIM_OK;
   19183 }
   19184 
   19185 
   19186 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19187 {
   19188     int i;
   19189 
   19190     if (argc < 2) {
   19191         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
   19192         return JIM_ERR;
   19193     }
   19194 
   19195     if (interp->framePtr->level == 0)
   19196         return JIM_OK;
   19197     for (i = 1; i < argc; i++) {
   19198 
   19199         const char *name = Jim_String(argv[i]);
   19200         if (name[0] != ':' || name[1] != ':') {
   19201             if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
   19202                 return JIM_ERR;
   19203         }
   19204     }
   19205     return JIM_OK;
   19206 }
   19207 
   19208 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
   19209     Jim_Obj *objPtr, int nocase)
   19210 {
   19211     int numMaps;
   19212     const char *str, *noMatchStart = NULL;
   19213     int strLen, i;
   19214     Jim_Obj *resultObjPtr;
   19215 
   19216     numMaps = Jim_ListLength(interp, mapListObjPtr);
   19217     if (numMaps % 2) {
   19218         Jim_SetResultString(interp, "list must contain an even number of elements", -1);
   19219         return NULL;
   19220     }
   19221 
   19222     str = Jim_String(objPtr);
   19223     strLen = Jim_Utf8Length(interp, objPtr);
   19224 
   19225 
   19226     resultObjPtr = Jim_NewStringObj(interp, "", 0);
   19227     while (strLen) {
   19228         for (i = 0; i < numMaps; i += 2) {
   19229             Jim_Obj *eachObjPtr;
   19230             const char *k;
   19231             int kl;
   19232 
   19233             eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
   19234             k = Jim_String(eachObjPtr);
   19235             kl = Jim_Utf8Length(interp, eachObjPtr);
   19236 
   19237             if (strLen >= kl && kl) {
   19238                 int rc;
   19239                 rc = JimStringCompareUtf8(str, kl, k, kl, nocase);
   19240                 if (rc == 0) {
   19241                     if (noMatchStart) {
   19242                         Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
   19243                         noMatchStart = NULL;
   19244                     }
   19245                     Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
   19246                     str += utf8_index(str, kl);
   19247                     strLen -= kl;
   19248                     break;
   19249                 }
   19250             }
   19251         }
   19252         if (i == numMaps) {
   19253             int c;
   19254             if (noMatchStart == NULL)
   19255                 noMatchStart = str;
   19256             str += utf8_tounicode(str, &c);
   19257             strLen--;
   19258         }
   19259     }
   19260     if (noMatchStart) {
   19261         Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
   19262     }
   19263     return resultObjPtr;
   19264 }
   19265 
   19266 
   19267 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19268 {
   19269     int len;
   19270     int opt_case = 1;
   19271     int option;
   19272     static const char * const nocase_options[] = {
   19273         "-nocase", NULL
   19274     };
   19275     static const char * const nocase_length_options[] = {
   19276         "-nocase", "-length", NULL
   19277     };
   19278 
   19279     enum {
   19280         OPT_BYTELENGTH,
   19281         OPT_BYTERANGE,
   19282         OPT_CAT,
   19283         OPT_COMPARE,
   19284         OPT_EQUAL,
   19285         OPT_FIRST,
   19286         OPT_INDEX,
   19287         OPT_IS,
   19288         OPT_LAST,
   19289         OPT_LENGTH,
   19290         OPT_MAP,
   19291         OPT_MATCH,
   19292         OPT_RANGE,
   19293         OPT_REPEAT,
   19294         OPT_REPLACE,
   19295         OPT_REVERSE,
   19296         OPT_TOLOWER,
   19297         OPT_TOTITLE,
   19298         OPT_TOUPPER,
   19299         OPT_TRIM,
   19300         OPT_TRIMLEFT,
   19301         OPT_TRIMRIGHT,
   19302         OPT_COUNT
   19303     };
   19304     static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
   19305         JIM_DEF_SUBCMD("bytelength", "string", 1, 1),
   19306         JIM_DEF_SUBCMD("byterange", "string first last", 3, 3),
   19307         JIM_DEF_SUBCMD("cat", "?...?", 0, -1),
   19308         JIM_DEF_SUBCMD("compare", "?-nocase? ?-length int? string1 string2", 2, 5),
   19309         JIM_DEF_SUBCMD("equal", "?-nocase? ?-length int? string1 string2", 2, 5),
   19310         JIM_DEF_SUBCMD("first", "subString string ?index?", 2, 3),
   19311         JIM_DEF_SUBCMD("index", "string index", 2, 2),
   19312         JIM_DEF_SUBCMD("is", "class ?-strict? str", 2, 3),
   19313         JIM_DEF_SUBCMD("last", "subString string ?index?", 2, 3),
   19314         JIM_DEF_SUBCMD("length","string", 1, 1),
   19315         JIM_DEF_SUBCMD("map", "?-nocase? mapList string", 2, 3),
   19316         JIM_DEF_SUBCMD("match", "?-nocase? pattern string", 2, 3),
   19317         JIM_DEF_SUBCMD("range", "string first last", 3, 3),
   19318         JIM_DEF_SUBCMD("repeat", "string count", 2, 2),
   19319         JIM_DEF_SUBCMD("replace", "string first last ?string?", 3, 4),
   19320         JIM_DEF_SUBCMD("reverse", "string", 1, 1),
   19321         JIM_DEF_SUBCMD("tolower", "string", 1, 1),
   19322         JIM_DEF_SUBCMD("totitle", "string", 1, 1),
   19323         JIM_DEF_SUBCMD("toupper", "string", 1, 1),
   19324         JIM_DEF_SUBCMD("trim", "string ?trimchars?", 1, 2),
   19325         JIM_DEF_SUBCMD("trimleft", "string ?trimchars?", 1, 2),
   19326         JIM_DEF_SUBCMD("trimright", "string ?trimchars?", 1, 2),
   19327         { NULL }
   19328     };
   19329     const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
   19330     if (!ct) {
   19331         return JIM_ERR;
   19332     }
   19333     if (ct->function) {
   19334 
   19335         return ct->function(interp, argc, argv);
   19336     }
   19337 
   19338     option = ct - cmds;
   19339 
   19340     switch (option) {
   19341         case OPT_LENGTH:
   19342             Jim_SetResultInt(interp, Jim_Utf8Length(interp, argv[2]));
   19343             return JIM_OK;
   19344 
   19345         case OPT_BYTELENGTH:
   19346             Jim_SetResultInt(interp, Jim_Length(argv[2]));
   19347             return JIM_OK;
   19348 
   19349         case OPT_CAT:{
   19350                 Jim_Obj *objPtr;
   19351                 if (argc == 3) {
   19352 
   19353                     objPtr = argv[2];
   19354                 }
   19355                 else {
   19356                     int i;
   19357 
   19358                     objPtr = Jim_NewStringObj(interp, "", 0);
   19359 
   19360                     for (i = 2; i < argc; i++) {
   19361                         Jim_AppendObj(interp, objPtr, argv[i]);
   19362                     }
   19363                 }
   19364                 Jim_SetResult(interp, objPtr);
   19365                 return JIM_OK;
   19366             }
   19367 
   19368         case OPT_COMPARE:
   19369         case OPT_EQUAL:
   19370             {
   19371 
   19372                 long opt_length = -1;
   19373                 int n = argc - 4;
   19374                 int i = 2;
   19375                 while (n > 0) {
   19376                     int subopt;
   19377                     if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
   19378                             JIM_ENUM_ABBREV) != JIM_OK) {
   19379 badcompareargs:
   19380                         Jim_SubCmdArgError(interp, ct, argv[0]);
   19381                         return JIM_ERR;
   19382                     }
   19383                     if (subopt == 0) {
   19384 
   19385                         opt_case = 0;
   19386                         n--;
   19387                     }
   19388                     else {
   19389 
   19390                         if (n < 2) {
   19391                             goto badcompareargs;
   19392                         }
   19393                         if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
   19394                             return JIM_ERR;
   19395                         }
   19396                         n -= 2;
   19397                     }
   19398                 }
   19399                 if (n) {
   19400                     goto badcompareargs;
   19401                 }
   19402                 argv += argc - 2;
   19403                 if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
   19404 
   19405                     Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
   19406                 }
   19407                 else {
   19408                     const char *s1 = Jim_String(argv[0]);
   19409                     int l1 = Jim_Utf8Length(interp, argv[0]);
   19410                     const char *s2 = Jim_String(argv[1]);
   19411                     int l2 = Jim_Utf8Length(interp, argv[1]);
   19412                     if (opt_length >= 0) {
   19413                         if (l1 > opt_length) {
   19414                             l1 = opt_length;
   19415                         }
   19416                         if (l2 > opt_length) {
   19417                             l2 = opt_length;
   19418                         }
   19419                     }
   19420                     n = JimStringCompareUtf8(s1, l1, s2, l2, !opt_case);
   19421                     Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
   19422                 }
   19423                 return JIM_OK;
   19424             }
   19425 
   19426         case OPT_MATCH:
   19427             if (argc != 4 &&
   19428                 (argc != 5 ||
   19429                     Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
   19430                         JIM_ENUM_ABBREV) != JIM_OK)) {
   19431                 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
   19432                 return JIM_ERR;
   19433             }
   19434             if (opt_case == 0) {
   19435                 argv++;
   19436             }
   19437             Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
   19438             return JIM_OK;
   19439 
   19440         case OPT_MAP:{
   19441                 Jim_Obj *objPtr;
   19442 
   19443                 if (argc != 4 &&
   19444                     (argc != 5 ||
   19445                         Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
   19446                             JIM_ENUM_ABBREV) != JIM_OK)) {
   19447                     Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
   19448                     return JIM_ERR;
   19449                 }
   19450 
   19451                 if (opt_case == 0) {
   19452                     argv++;
   19453                 }
   19454                 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
   19455                 if (objPtr == NULL) {
   19456                     return JIM_ERR;
   19457                 }
   19458                 Jim_SetResult(interp, objPtr);
   19459                 return JIM_OK;
   19460             }
   19461 
   19462         case OPT_RANGE:{
   19463                 Jim_Obj *objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
   19464                 if (objPtr == NULL) {
   19465                     return JIM_ERR;
   19466                 }
   19467                 Jim_SetResult(interp, objPtr);
   19468                 return JIM_OK;
   19469             }
   19470 
   19471         case OPT_BYTERANGE:{
   19472                 Jim_Obj *objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
   19473                 if (objPtr == NULL) {
   19474                     return JIM_ERR;
   19475                 }
   19476                 Jim_SetResult(interp, objPtr);
   19477                 return JIM_OK;
   19478             }
   19479 
   19480         case OPT_REPLACE:{
   19481                 Jim_Obj *objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
   19482                 if (objPtr == NULL) {
   19483                     return JIM_ERR;
   19484                 }
   19485                 Jim_SetResult(interp, objPtr);
   19486                 return JIM_OK;
   19487             }
   19488 
   19489 
   19490         case OPT_REPEAT:{
   19491                 Jim_Obj *objPtr;
   19492                 jim_wide count;
   19493 
   19494                 if (Jim_GetWideExpr(interp, argv[3], &count) != JIM_OK) {
   19495                     return JIM_ERR;
   19496                 }
   19497                 objPtr = Jim_NewStringObj(interp, "", 0);
   19498                 if (count > 0) {
   19499                     while (count--) {
   19500                         Jim_AppendObj(interp, objPtr, argv[2]);
   19501                     }
   19502                 }
   19503                 Jim_SetResult(interp, objPtr);
   19504                 return JIM_OK;
   19505             }
   19506 
   19507         case OPT_REVERSE:{
   19508                 char *buf, *p;
   19509                 const char *str;
   19510                 int i;
   19511 
   19512                 str = Jim_GetString(argv[2], &len);
   19513                 buf = Jim_Alloc(len + 1);
   19514                 assert(buf);
   19515                 p = buf + len;
   19516                 *p = 0;
   19517                 for (i = 0; i < len; ) {
   19518                     int c;
   19519                     int l = utf8_tounicode(str, &c);
   19520                     memcpy(p - l, str, l);
   19521                     p -= l;
   19522                     i += l;
   19523                     str += l;
   19524                 }
   19525                 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
   19526                 return JIM_OK;
   19527             }
   19528 
   19529         case OPT_INDEX:{
   19530                 int idx;
   19531                 const char *str;
   19532 
   19533                 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
   19534                     return JIM_ERR;
   19535                 }
   19536                 str = Jim_String(argv[2]);
   19537                 len = Jim_Utf8Length(interp, argv[2]);
   19538                 idx = JimRelToAbsIndex(len, idx);
   19539                 if (idx < 0 || idx >= len || str == NULL) {
   19540                     Jim_SetResultString(interp, "", 0);
   19541                 }
   19542                 else if (len == Jim_Length(argv[2])) {
   19543 
   19544                     Jim_SetResultString(interp, str + idx, 1);
   19545                 }
   19546                 else {
   19547                     int c;
   19548                     int i = utf8_index(str, idx);
   19549                     Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
   19550                 }
   19551                 return JIM_OK;
   19552             }
   19553 
   19554         case OPT_FIRST:
   19555         case OPT_LAST:{
   19556                 int idx = 0, l1, l2;
   19557                 const char *s1, *s2;
   19558 
   19559                 s1 = Jim_String(argv[2]);
   19560                 s2 = Jim_String(argv[3]);
   19561                 l1 = Jim_Utf8Length(interp, argv[2]);
   19562                 l2 = Jim_Utf8Length(interp, argv[3]);
   19563                 if (argc == 5) {
   19564                     if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
   19565                         return JIM_ERR;
   19566                     }
   19567                     idx = JimRelToAbsIndex(l2, idx);
   19568                     if (idx < 0) {
   19569                         idx = 0;
   19570                     }
   19571                 }
   19572                 else if (option == OPT_LAST) {
   19573                     idx = l2;
   19574                 }
   19575                 if (option == OPT_FIRST) {
   19576                     Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
   19577                 }
   19578                 else {
   19579 #ifdef JIM_UTF8
   19580                     Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
   19581 #else
   19582                     Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
   19583 #endif
   19584                 }
   19585                 return JIM_OK;
   19586             }
   19587 
   19588         case OPT_TRIM:
   19589                 Jim_SetResult(interp, JimStringTrim(interp, argv[2], argc == 4 ? argv[3] : NULL));
   19590                 return JIM_OK;
   19591         case OPT_TRIMLEFT:
   19592                 Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], argc == 4 ? argv[3] : NULL));
   19593                 return JIM_OK;
   19594         case OPT_TRIMRIGHT:{
   19595                 Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], argc == 4 ? argv[3] : NULL));
   19596                 return JIM_OK;
   19597         }
   19598 
   19599         case OPT_TOLOWER:
   19600                 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
   19601                 return JIM_OK;
   19602         case OPT_TOUPPER:
   19603                 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
   19604                 return JIM_OK;
   19605         case OPT_TOTITLE:
   19606                 Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
   19607                 return JIM_OK;
   19608 
   19609         case OPT_IS:
   19610             if (argc == 5 && !Jim_CompareStringImmediate(interp, argv[3], "-strict")) {
   19611                 Jim_SubCmdArgError(interp, ct, argv[0]);
   19612                 return JIM_ERR;
   19613             }
   19614             return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
   19615     }
   19616     return JIM_OK;
   19617 }
   19618 
   19619 
   19620 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19621 {
   19622     long i, count = 1;
   19623     jim_wide start, elapsed;
   19624 
   19625     if (argc < 2) {
   19626         Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
   19627         return JIM_ERR;
   19628     }
   19629     if (argc == 3) {
   19630         if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
   19631             return JIM_ERR;
   19632     }
   19633     if (count < 0)
   19634         return JIM_OK;
   19635     i = count;
   19636     start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
   19637     while (i-- > 0) {
   19638         int retval;
   19639 
   19640         retval = Jim_EvalObj(interp, argv[1]);
   19641         if (retval != JIM_OK) {
   19642             return retval;
   19643         }
   19644     }
   19645     elapsed = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
   19646     if (elapsed < count * 10) {
   19647         Jim_SetResult(interp, Jim_NewDoubleObj(interp, elapsed * 1.0 / count));
   19648     }
   19649     else {
   19650         Jim_SetResultInt(interp, count == 0 ? 0 : elapsed / count);
   19651     }
   19652     Jim_AppendString(interp, Jim_GetResult(interp)," microseconds per iteration", -1);
   19653     return JIM_OK;
   19654 }
   19655 
   19656 
   19657 static int Jim_TimeRateCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19658 {
   19659     long us = 0;
   19660     jim_wide start, delta, overhead;
   19661     Jim_Obj *objPtr;
   19662     double us_per_iter;
   19663     int count;
   19664     int n;
   19665 
   19666     if (argc < 2) {
   19667         Jim_WrongNumArgs(interp, 1, argv, "script ?milliseconds?");
   19668         return JIM_ERR;
   19669     }
   19670     if (argc == 3) {
   19671         if (Jim_GetLong(interp, argv[2], &us) != JIM_OK)
   19672             return JIM_ERR;
   19673         us *= 1000;
   19674     }
   19675     if (us < 1) {
   19676 
   19677         us = 1000 * 1000;
   19678     }
   19679 
   19680 
   19681     start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
   19682     count = 0;
   19683     do {
   19684         int retval = Jim_EvalObj(interp, argv[1]);
   19685         delta = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
   19686         if (retval != JIM_OK) {
   19687             return retval;
   19688         }
   19689         count++;
   19690     } while (delta < us);
   19691 
   19692 
   19693     start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
   19694     n = 0;
   19695     do {
   19696         int retval = Jim_EvalObj(interp, interp->nullScriptObj);
   19697         overhead = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
   19698         if (retval != JIM_OK) {
   19699             return retval;
   19700         }
   19701         n++;
   19702     } while (n < count);
   19703 
   19704     delta -= overhead;
   19705 
   19706     us_per_iter = (double)delta / count;
   19707     objPtr = Jim_NewListObj(interp, NULL, 0);
   19708 
   19709     Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "us_per_iter", -1));
   19710     Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, us_per_iter));
   19711     Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "iters_per_sec", -1));
   19712     Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, 1e6 / us_per_iter));
   19713     Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "count", -1));
   19714     Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, count));
   19715     Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "elapsed_us", -1));
   19716     Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, delta));
   19717     Jim_SetResult(interp, objPtr);
   19718     return JIM_OK;
   19719 }
   19720 
   19721 
   19722 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19723 {
   19724     long exitCode = 0;
   19725 
   19726     if (argc > 2) {
   19727         Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
   19728         return JIM_ERR;
   19729     }
   19730     if (argc == 2) {
   19731         if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
   19732             return JIM_ERR;
   19733         Jim_SetResult(interp, argv[1]);
   19734     }
   19735     interp->exitCode = exitCode;
   19736     return JIM_EXIT;
   19737 }
   19738 
   19739 static int JimMatchReturnCodes(Jim_Interp *interp, Jim_Obj *retcodeListObj, int rc)
   19740 {
   19741     int len = Jim_ListLength(interp, retcodeListObj);
   19742     int i;
   19743     for (i = 0; i < len; i++) {
   19744         int returncode;
   19745         if (Jim_GetReturnCode(interp, Jim_ListGetIndex(interp, retcodeListObj, i), &returncode) != JIM_OK) {
   19746             return JIM_ERR;
   19747         }
   19748         if (rc == returncode) {
   19749             return JIM_OK;
   19750         }
   19751     }
   19752     return -1;
   19753 }
   19754 
   19755 
   19756 static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *const *argv)
   19757 {
   19758     static const char * const wrongargs_catchtry[2] = {
   19759         "?-?no?code ... --? script ?resultVarName? ?optionVarName?",
   19760         "?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?"
   19761     };
   19762     int exitCode = 0;
   19763     int i;
   19764     int sig = 0;
   19765     int ok;
   19766     Jim_Obj *finallyScriptObj = NULL;
   19767     Jim_Obj *msgVarObj = NULL;
   19768     Jim_Obj *optsVarObj = NULL;
   19769     Jim_Obj *handlerScriptObj = NULL;
   19770     Jim_Obj *errorCodeObj;
   19771     int idx;
   19772 
   19773 
   19774     jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
   19775     static const int max_ignore_code = sizeof(ignore_mask) * 8;
   19776 
   19777     JimPanic((istry != 0 && istry != 1, "wrong args to JimCatchTryHelper"));
   19778 
   19779     Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
   19780 
   19781     for (i = 1; i < argc - 1; i++) {
   19782         const char *arg = Jim_String(argv[i]);
   19783         jim_wide option;
   19784         int ignore;
   19785 
   19786 
   19787         if (strcmp(arg, "--") == 0) {
   19788             i++;
   19789             break;
   19790         }
   19791         if (*arg != '-') {
   19792             break;
   19793         }
   19794 
   19795         if (strncmp(arg, "-no", 3) == 0) {
   19796             arg += 3;
   19797             ignore = 1;
   19798         }
   19799         else {
   19800             arg++;
   19801             ignore = 0;
   19802         }
   19803 
   19804         if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
   19805             option = -1;
   19806         }
   19807         if (option < 0) {
   19808             option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
   19809         }
   19810         if (option < 0) {
   19811             goto wrongargs;
   19812         }
   19813 
   19814         if (ignore) {
   19815             ignore_mask |= ((jim_wide)1 << option);
   19816         }
   19817         else {
   19818             ignore_mask &= (~((jim_wide)1 << option));
   19819         }
   19820     }
   19821 
   19822     idx = i;
   19823 
   19824     if (argc - idx < 1) {
   19825 wrongargs:
   19826         Jim_WrongNumArgs(interp, 1, argv, wrongargs_catchtry[istry]);
   19827         return JIM_ERR;
   19828     }
   19829 
   19830     if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
   19831         sig++;
   19832     }
   19833 
   19834     interp->signal_level += sig;
   19835     if (Jim_CheckSignal(interp)) {
   19836 
   19837         exitCode = JIM_SIGNAL;
   19838     }
   19839     else {
   19840         exitCode = Jim_EvalObj(interp, argv[idx]);
   19841 
   19842         interp->errorFlag = 0;
   19843     }
   19844     interp->signal_level -= sig;
   19845 
   19846     errorCodeObj = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
   19847 
   19848     idx++;
   19849     if (istry) {
   19850         while (idx < argc) {
   19851             int option;
   19852             int ret;
   19853             static const char * const try_options[] = { "on", "trap", "finally", NULL };
   19854             enum { TRY_ON, TRY_TRAP, TRY_FINALLY, };
   19855 
   19856             if (Jim_GetEnum(interp, argv[idx], try_options, &option, "handler", JIM_ERRMSG) != JIM_OK) {
   19857                 return JIM_ERR;
   19858             }
   19859             switch (option) {
   19860                 case TRY_ON:
   19861                 case TRY_TRAP:
   19862                     if (idx + 4 > argc) {
   19863                         goto wrongargs;
   19864                     }
   19865                     if (option == TRY_ON) {
   19866                         ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode);
   19867                         if (ret > JIM_OK) {
   19868                             goto wrongargs;
   19869                         }
   19870                     }
   19871                     else if (errorCodeObj) {
   19872                         int len = Jim_ListLength(interp, argv[idx + 1]);
   19873                         int i;
   19874 
   19875                         ret = JIM_OK;
   19876 
   19877                         for (i = 0; i < len; i++) {
   19878                             Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i);
   19879                             Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i);
   19880                             if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) {
   19881                                 ret = -1;
   19882                                 break;
   19883                             }
   19884                         }
   19885                     }
   19886                     else {
   19887 
   19888                         ret = -1;
   19889                     }
   19890 
   19891                     if (ret == JIM_OK && handlerScriptObj == NULL) {
   19892                         msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0);
   19893                         optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1);
   19894                         handlerScriptObj = argv[idx + 3];
   19895                     }
   19896                     idx += 4;
   19897                     break;
   19898                 case TRY_FINALLY:
   19899                     if (idx + 2 != argc) {
   19900                         goto wrongargs;
   19901                     }
   19902                     finallyScriptObj = argv[idx + 1];
   19903                     idx += 2;
   19904                     break;
   19905             }
   19906         }
   19907     }
   19908     else {
   19909         if (argc - idx >= 1) {
   19910             msgVarObj = argv[idx];
   19911             idx++;
   19912             if (argc - idx >= 1) {
   19913                 optsVarObj = argv[idx];
   19914                 idx++;
   19915             }
   19916         }
   19917     }
   19918 
   19919 
   19920     if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
   19921 
   19922         if (finallyScriptObj) {
   19923             Jim_EvalObj(interp, finallyScriptObj);
   19924         }
   19925         return exitCode;
   19926     }
   19927 
   19928     if (sig && exitCode == JIM_SIGNAL) {
   19929 
   19930         if (interp->signal_set_result) {
   19931             interp->signal_set_result(interp, interp->sigmask);
   19932         }
   19933         else if (!istry) {
   19934             Jim_SetResultInt(interp, interp->sigmask);
   19935         }
   19936         interp->sigmask = 0;
   19937     }
   19938 
   19939     ok = 1;
   19940     if (msgVarObj && Jim_Length(msgVarObj)) {
   19941         if (Jim_SetVariable(interp, msgVarObj, Jim_GetResult(interp)) != JIM_OK) {
   19942             ok = 0;
   19943         }
   19944     }
   19945     if (ok && optsVarObj && Jim_Length(optsVarObj)) {
   19946         Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
   19947 
   19948         Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
   19949         Jim_ListAppendElement(interp, optListObj,
   19950             Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
   19951         Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
   19952         Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
   19953         if (exitCode == JIM_ERR) {
   19954             Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
   19955                 -1));
   19956             Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
   19957 
   19958             if (errorCodeObj) {
   19959                 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
   19960                 Jim_ListAppendElement(interp, optListObj, errorCodeObj);
   19961             }
   19962         }
   19963         if (Jim_SetVariable(interp, optsVarObj, optListObj) != JIM_OK) {
   19964             ok = 0;
   19965         }
   19966     }
   19967     if (ok && handlerScriptObj) {
   19968 
   19969         exitCode = Jim_EvalObj(interp, handlerScriptObj);
   19970     }
   19971 
   19972     if (finallyScriptObj) {
   19973 
   19974         Jim_Obj *prevResultObj = Jim_GetResult(interp);
   19975         Jim_IncrRefCount(prevResultObj);
   19976         int ret = Jim_EvalObj(interp, finallyScriptObj);
   19977         if (ret == JIM_OK) {
   19978             Jim_SetResult(interp, prevResultObj);
   19979         }
   19980         else {
   19981             exitCode = ret;
   19982         }
   19983         Jim_DecrRefCount(interp, prevResultObj);
   19984     }
   19985     if (!istry) {
   19986         Jim_SetResultInt(interp, exitCode);
   19987         exitCode = JIM_OK;
   19988     }
   19989     return exitCode;
   19990 }
   19991 
   19992 
   19993 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   19994 {
   19995     return JimCatchTryHelper(interp, 0, argc, argv);
   19996 }
   19997 
   19998 
   19999 static int Jim_TryCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20000 {
   20001     return JimCatchTryHelper(interp, 1, argc, argv);
   20002 }
   20003 
   20004 
   20005 
   20006 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20007 {
   20008     if (argc != 3) {
   20009         Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
   20010         return JIM_ERR;
   20011     }
   20012 
   20013     return Jim_RenameCommand(interp, argv[1], argv[2]);
   20014 }
   20015 
   20016 #define JIM_DICTMATCH_KEYS 0x0001
   20017 #define JIM_DICTMATCH_VALUES 0x002
   20018 
   20019 int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
   20020 {
   20021     Jim_Obj *listObjPtr;
   20022     Jim_Dict *dict;
   20023     int i;
   20024 
   20025     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
   20026         return JIM_ERR;
   20027     }
   20028     dict = objPtr->internalRep.dictValue;
   20029 
   20030     listObjPtr = Jim_NewListObj(interp, NULL, 0);
   20031 
   20032     for (i = 0; i < dict->len; i += 2 ) {
   20033         Jim_Obj *keyObj = dict->table[i];
   20034         Jim_Obj *valObj = dict->table[i + 1];
   20035         if (patternObj) {
   20036             Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? keyObj : valObj;
   20037             if (!Jim_StringMatchObj(interp, patternObj, matchObj, 0)) {
   20038 
   20039                 continue;
   20040             }
   20041         }
   20042         if (return_types & JIM_DICTMATCH_KEYS) {
   20043             Jim_ListAppendElement(interp, listObjPtr, keyObj);
   20044         }
   20045         if (return_types & JIM_DICTMATCH_VALUES) {
   20046             Jim_ListAppendElement(interp, listObjPtr, valObj);
   20047         }
   20048     }
   20049 
   20050     Jim_SetResult(interp, listObjPtr);
   20051     return JIM_OK;
   20052 }
   20053 
   20054 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
   20055 {
   20056     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
   20057         return -1;
   20058     }
   20059     return objPtr->internalRep.dictValue->len / 2;
   20060 }
   20061 
   20062 Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
   20063 {
   20064     Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
   20065     int i;
   20066 
   20067     JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));
   20068 
   20069 
   20070 
   20071     for (i = 0; i < objc; i++) {
   20072         Jim_Obj **table;
   20073         int tablelen;
   20074         int j;
   20075 
   20076         table = Jim_DictPairs(interp, objv[i], &tablelen);
   20077         if (tablelen && !table) {
   20078             Jim_FreeNewObj(interp, objPtr);
   20079             return NULL;
   20080         }
   20081         for (j = 0; j < tablelen; j += 2) {
   20082             DictAddElement(interp, objPtr, table[j], table[j + 1]);
   20083         }
   20084     }
   20085     return objPtr;
   20086 }
   20087 
   20088 int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
   20089 {
   20090     char buffer[100];
   20091     Jim_Obj *output;
   20092     Jim_Dict *dict;
   20093 
   20094     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
   20095         return JIM_ERR;
   20096     }
   20097 
   20098     dict = objPtr->internalRep.dictValue;
   20099 
   20100 
   20101     snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets", dict->len, dict->size);
   20102     output = Jim_NewStringObj(interp, buffer, -1);
   20103     Jim_SetResult(interp, output);
   20104     return JIM_OK;
   20105 }
   20106 
   20107 static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
   20108 {
   20109     Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
   20110 
   20111     Jim_AppendString(interp, prefixObj, " ", 1);
   20112     Jim_AppendString(interp, prefixObj, subcmd, -1);
   20113 
   20114     return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
   20115 }
   20116 
   20117 static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj)
   20118 {
   20119     int i;
   20120     Jim_Obj *objPtr;
   20121     Jim_Obj *dictObj;
   20122     Jim_Obj **dictValues;
   20123     int len;
   20124     int ret = JIM_OK;
   20125 
   20126 
   20127     dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
   20128     if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
   20129         return JIM_ERR;
   20130     }
   20131 
   20132     dictValues = Jim_DictPairs(interp, objPtr, &len);
   20133     if (len && dictValues == NULL) {
   20134         return JIM_ERR;
   20135     }
   20136     for (i = 0; i < len; i += 2) {
   20137         if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
   20138             return JIM_ERR;
   20139         }
   20140     }
   20141 
   20142 
   20143     if (Jim_Length(scriptObj)) {
   20144         ret = Jim_EvalObj(interp, scriptObj);
   20145 
   20146 
   20147         if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {
   20148 
   20149             Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
   20150             for (i = 0; i < keyc; i++) {
   20151                 newkeyv[i] = keyv[i];
   20152             }
   20153 
   20154             for (i = 0; i < len; i += 2) {
   20155 
   20156                 if (Jim_StringCompareObj(interp, dictVarName, dictValues[i], 0) != 0) {
   20157 
   20158                     objPtr = Jim_GetVariable(interp, dictValues[i], 0);
   20159                     newkeyv[keyc] = dictValues[i];
   20160                     Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, JIM_NORESULT);
   20161                 }
   20162             }
   20163             Jim_Free(newkeyv);
   20164         }
   20165     }
   20166 
   20167     return ret;
   20168 }
   20169 
   20170 
   20171 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20172 {
   20173     Jim_Obj *objPtr;
   20174     int types = JIM_DICTMATCH_KEYS;
   20175 
   20176     enum {
   20177         OPT_CREATE,
   20178         OPT_GET,
   20179         OPT_GETDEF,
   20180         OPT_GETWITHDEFAULT,
   20181         OPT_SET,
   20182         OPT_UNSET,
   20183         OPT_EXISTS,
   20184         OPT_KEYS,
   20185         OPT_SIZE,
   20186         OPT_INFO,
   20187         OPT_MERGE,
   20188         OPT_WITH,
   20189         OPT_APPEND,
   20190         OPT_LAPPEND,
   20191         OPT_INCR,
   20192         OPT_REMOVE,
   20193         OPT_VALUES,
   20194         OPT_FOR,
   20195         OPT_REPLACE,
   20196         OPT_UPDATE,
   20197         OPT_COUNT
   20198     };
   20199     static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
   20200         JIM_DEF_SUBCMD("create", "?key value ...?", 0, -2),
   20201         JIM_DEF_SUBCMD("get", "dictionary ?key ...?", 1, -1),
   20202         JIM_DEF_SUBCMD_HIDDEN("getdef", "dictionary ?key ...? key default", 3, -1),
   20203         JIM_DEF_SUBCMD("getwithdefault", "dictionary ?key ...? key default", 3, -1),
   20204         JIM_DEF_SUBCMD("set", "varName key ?key ...? value", 3, -1),
   20205         JIM_DEF_SUBCMD("unset", "varName key ?key ...?", 2, -1),
   20206         JIM_DEF_SUBCMD("exists", "dictionary key ?key ...?", 2, -1),
   20207         JIM_DEF_SUBCMD("keys", "dictionary ?pattern?", 1, 2),
   20208         JIM_DEF_SUBCMD("size", "dictionary", 1, 1),
   20209         JIM_DEF_SUBCMD("info", "dictionary", 1, 1),
   20210         JIM_DEF_SUBCMD("merge", "?...?", 0, -1),
   20211         JIM_DEF_SUBCMD("with", "dictVar ?key ...? script", 2, -1),
   20212         JIM_DEF_SUBCMD("append", "varName key ?value ...?", 2, -1),
   20213         JIM_DEF_SUBCMD("lappend",  "varName key ?value ...?", 2, -1),
   20214         JIM_DEF_SUBCMD("incr", "varName key ?increment?", 2, 3),
   20215         JIM_DEF_SUBCMD("remove", "dictionary ?key ...?", 1, -1),
   20216         JIM_DEF_SUBCMD("values", "dictionary ?pattern?", 1, 2),
   20217         JIM_DEF_SUBCMD("for", "vars dictionary script", 3, 3),
   20218         JIM_DEF_SUBCMD("replace", "dictionary ?key value ...?", 1, -1),
   20219         JIM_DEF_SUBCMD("update", "varName ?arg ...? script", 2, -1),
   20220         { NULL }
   20221     };
   20222     const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
   20223     if (!ct) {
   20224         return JIM_ERR;
   20225     }
   20226     if (ct->function) {
   20227 
   20228         return ct->function(interp, argc, argv);
   20229     }
   20230 
   20231 
   20232     switch (ct - cmds) {
   20233         case OPT_GET:
   20234             if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
   20235                     JIM_ERRMSG) != JIM_OK) {
   20236                 return JIM_ERR;
   20237             }
   20238             Jim_SetResult(interp, objPtr);
   20239             return JIM_OK;
   20240 
   20241         case OPT_GETDEF:
   20242         case OPT_GETWITHDEFAULT:{
   20243             int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 4, &objPtr, JIM_ERRMSG);
   20244             if (rc == -1) {
   20245 
   20246                 return JIM_ERR;
   20247             }
   20248             if (rc == JIM_ERR) {
   20249                 Jim_SetResult(interp, argv[argc - 1]);
   20250             }
   20251             else {
   20252                 Jim_SetResult(interp, objPtr);
   20253             }
   20254             return JIM_OK;
   20255         }
   20256 
   20257         case OPT_SET:
   20258             return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
   20259 
   20260         case OPT_EXISTS:{
   20261                 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_NONE);
   20262                 if (rc < 0) {
   20263                     return JIM_ERR;
   20264                 }
   20265                 Jim_SetResultBool(interp,  rc == JIM_OK);
   20266                 return JIM_OK;
   20267             }
   20268 
   20269         case OPT_UNSET:
   20270             if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE) != JIM_OK) {
   20271                 return JIM_ERR;
   20272             }
   20273             return JIM_OK;
   20274 
   20275         case OPT_VALUES:
   20276             types = JIM_DICTMATCH_VALUES;
   20277 
   20278         case OPT_KEYS:
   20279             return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);
   20280 
   20281         case OPT_SIZE:
   20282             if (Jim_DictSize(interp, argv[2]) < 0) {
   20283                 return JIM_ERR;
   20284             }
   20285             Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
   20286             return JIM_OK;
   20287 
   20288         case OPT_MERGE:
   20289             if (argc == 2) {
   20290                 return JIM_OK;
   20291             }
   20292             objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
   20293             if (objPtr == NULL) {
   20294                 return JIM_ERR;
   20295             }
   20296             Jim_SetResult(interp, objPtr);
   20297             return JIM_OK;
   20298 
   20299         case OPT_CREATE:
   20300             objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
   20301             Jim_SetResult(interp, objPtr);
   20302             return JIM_OK;
   20303 
   20304         case OPT_INFO:
   20305             return Jim_DictInfo(interp, argv[2]);
   20306 
   20307         case OPT_WITH:
   20308             return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
   20309 
   20310         case OPT_UPDATE:
   20311             if (argc < 6 || argc % 2) {
   20312 
   20313                 argc = 2;
   20314             }
   20315 
   20316         default:
   20317             return Jim_EvalEnsemble(interp, "dict", Jim_String(argv[1]), argc - 2, argv + 2);
   20318     }
   20319 }
   20320 
   20321 
   20322 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20323 {
   20324     static const char * const options[] = {
   20325         "-nobackslashes", "-nocommands", "-novariables", NULL
   20326     };
   20327     enum
   20328     { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
   20329     int i;
   20330     int flags = JIM_SUBST_FLAG;
   20331     Jim_Obj *objPtr;
   20332 
   20333     if (argc < 2) {
   20334         Jim_WrongNumArgs(interp, 1, argv, "?options? string");
   20335         return JIM_ERR;
   20336     }
   20337     for (i = 1; i < (argc - 1); i++) {
   20338         int option;
   20339 
   20340         if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
   20341                 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
   20342             return JIM_ERR;
   20343         }
   20344         switch (option) {
   20345             case OPT_NOBACKSLASHES:
   20346                 flags |= JIM_SUBST_NOESC;
   20347                 break;
   20348             case OPT_NOCOMMANDS:
   20349                 flags |= JIM_SUBST_NOCMD;
   20350                 break;
   20351             case OPT_NOVARIABLES:
   20352                 flags |= JIM_SUBST_NOVAR;
   20353                 break;
   20354         }
   20355     }
   20356     if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
   20357         return JIM_ERR;
   20358     }
   20359     Jim_SetResult(interp, objPtr);
   20360     return JIM_OK;
   20361 }
   20362 
   20363 #ifdef jim_ext_namespace
   20364 static int JimIsGlobalNamespace(Jim_Obj *objPtr)
   20365 {
   20366     int len;
   20367     const char *str = Jim_GetString(objPtr, &len);
   20368     return len >= 2 && str[0] == ':' && str[1] == ':';
   20369 }
   20370 #endif
   20371 
   20372 
   20373 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20374 {
   20375     Jim_Obj *objPtr;
   20376     int mode = 0;
   20377 
   20378 
   20379     enum {
   20380         INFO_ALIAS,
   20381         INFO_ARGS,
   20382         INFO_BODY,
   20383         INFO_CHANNELS,
   20384         INFO_COMMANDS,
   20385         INFO_COMPLETE,
   20386         INFO_EXISTS,
   20387         INFO_FRAME,
   20388         INFO_GLOBALS,
   20389         INFO_HOSTNAME,
   20390         INFO_LEVEL,
   20391         INFO_LOCALS,
   20392         INFO_NAMEOFEXECUTABLE,
   20393         INFO_PATCHLEVEL,
   20394         INFO_PROCS,
   20395         INFO_REFERENCES,
   20396         INFO_RETURNCODES,
   20397         INFO_SCRIPT,
   20398         INFO_SOURCE,
   20399         INFO_STACKTRACE,
   20400         INFO_STATICS,
   20401         INFO_VARS,
   20402         INFO_VERSION,
   20403         INFO_COUNT
   20404     };
   20405     static const jim_subcmd_type cmds[INFO_COUNT + 1] = {
   20406         JIM_DEF_SUBCMD("alias", "command", 1, 1),
   20407         JIM_DEF_SUBCMD("args", "procname", 1, 1),
   20408         JIM_DEF_SUBCMD("body", "procname", 1, 1),
   20409         JIM_DEF_SUBCMD("channels", "?pattern?", 0, 1),
   20410         JIM_DEF_SUBCMD("commands", "?pattern?", 0, 1),
   20411         JIM_DEF_SUBCMD("complete", "script ?missing?", 1, 2),
   20412         JIM_DEF_SUBCMD("exists", "varName", 1, 1),
   20413         JIM_DEF_SUBCMD("frame", "?levelNum?", 0, 1),
   20414         JIM_DEF_SUBCMD("globals", "?pattern?", 0, 1),
   20415         JIM_DEF_SUBCMD("hostname", NULL, 0, 0),
   20416         JIM_DEF_SUBCMD("level", "?levelNum?", 0, 1),
   20417         JIM_DEF_SUBCMD("locals", "?pattern?", 0, 1),
   20418         JIM_DEF_SUBCMD("nameofexecutable", NULL, 0, 0),
   20419         JIM_DEF_SUBCMD("patchlevel", NULL, 0, 0),
   20420         JIM_DEF_SUBCMD("procs", "?pattern?", 0, 1),
   20421         JIM_DEF_SUBCMD("references", NULL, 0, 0),
   20422         JIM_DEF_SUBCMD("returncodes", "?code?", 0, 1),
   20423         JIM_DEF_SUBCMD("script", "?filename?", 0, 1),
   20424         JIM_DEF_SUBCMD("source", "source ?filename line?", 1, 3),
   20425         JIM_DEF_SUBCMD("stacktrace", NULL, 0, 0),
   20426         JIM_DEF_SUBCMD("statics", "procname", 1, 1),
   20427         JIM_DEF_SUBCMD("vars", "?pattern?", 0, 1),
   20428         JIM_DEF_SUBCMD("version", NULL, 0, 0),
   20429         { NULL }
   20430     };
   20431     const jim_subcmd_type *ct;
   20432 #ifdef jim_ext_namespace
   20433     int nons = 0;
   20434 
   20435     if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
   20436 
   20437         argc--;
   20438         argv++;
   20439         nons = 1;
   20440     }
   20441 #endif
   20442     ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
   20443     if (!ct) {
   20444         return JIM_ERR;
   20445     }
   20446     if (ct->function) {
   20447 
   20448         return ct->function(interp, argc, argv);
   20449     }
   20450 
   20451     int option = ct - cmds;
   20452 
   20453     switch (option) {
   20454         case INFO_EXISTS:
   20455             Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
   20456             return JIM_OK;
   20457 
   20458         case INFO_ALIAS:{
   20459             Jim_Cmd *cmdPtr;
   20460 
   20461             if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
   20462                 return JIM_ERR;
   20463             }
   20464             if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
   20465                 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
   20466                 return JIM_ERR;
   20467             }
   20468             Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
   20469             return JIM_OK;
   20470         }
   20471 
   20472         case INFO_CHANNELS:
   20473             mode++;
   20474 #ifndef jim_ext_aio
   20475             Jim_SetResultString(interp, "aio not enabled", -1);
   20476             return JIM_ERR;
   20477 #endif
   20478 
   20479         case INFO_PROCS:
   20480             mode++;
   20481 
   20482         case INFO_COMMANDS:
   20483 
   20484 #ifdef jim_ext_namespace
   20485             if (!nons) {
   20486                 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
   20487                     return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
   20488                 }
   20489             }
   20490 #endif
   20491             Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
   20492             return JIM_OK;
   20493 
   20494         case INFO_VARS:
   20495             mode++;
   20496 
   20497         case INFO_LOCALS:
   20498             mode++;
   20499 
   20500         case INFO_GLOBALS:
   20501 
   20502 #ifdef jim_ext_namespace
   20503             if (!nons) {
   20504                 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
   20505                     return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
   20506                 }
   20507             }
   20508 #endif
   20509             Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
   20510             return JIM_OK;
   20511 
   20512         case INFO_SCRIPT:
   20513             if (argc == 3) {
   20514                 Jim_IncrRefCount(argv[2]);
   20515                 Jim_DecrRefCount(interp, interp->currentFilenameObj);
   20516                 interp->currentFilenameObj = argv[2];
   20517             }
   20518             Jim_SetResult(interp, interp->currentFilenameObj);
   20519             return JIM_OK;
   20520 
   20521         case INFO_SOURCE:{
   20522                 Jim_Obj *resObjPtr;
   20523                 Jim_Obj *fileNameObj;
   20524 
   20525                 if (argc == 4) {
   20526                     Jim_SubCmdArgError(interp, ct, argv[0]);
   20527                     return JIM_ERR;
   20528                 }
   20529                 if (argc == 5) {
   20530                     jim_wide line;
   20531                     if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
   20532                         return JIM_ERR;
   20533                     }
   20534                     resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
   20535                     Jim_SetSourceInfo(interp, resObjPtr, argv[3], line);
   20536                 }
   20537                 else {
   20538                     int line;
   20539                     fileNameObj = Jim_GetSourceInfo(interp, argv[2], &line);
   20540                     resObjPtr = Jim_NewListObj(interp, NULL, 0);
   20541                     Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
   20542                     Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
   20543                 }
   20544                 Jim_SetResult(interp, resObjPtr);
   20545                 return JIM_OK;
   20546             }
   20547 
   20548         case INFO_STACKTRACE:
   20549             Jim_SetResult(interp, interp->stackTrace);
   20550             return JIM_OK;
   20551 
   20552         case INFO_LEVEL:
   20553             if (argc == 2) {
   20554                 Jim_SetResultInt(interp, interp->framePtr->level);
   20555             }
   20556             else {
   20557                 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK) {
   20558                     return JIM_ERR;
   20559                 }
   20560                 Jim_SetResult(interp, objPtr);
   20561             }
   20562             return JIM_OK;
   20563 
   20564         case INFO_FRAME:
   20565             if (argc == 2) {
   20566                 Jim_SetResultInt(interp, interp->procLevel + 1);
   20567             }
   20568             else {
   20569                 if (JimInfoFrame(interp, argv[2], &objPtr) != JIM_OK) {
   20570                     return JIM_ERR;
   20571                 }
   20572                 Jim_SetResult(interp, objPtr);
   20573             }
   20574             return JIM_OK;
   20575 
   20576         case INFO_BODY:
   20577         case INFO_STATICS:
   20578         case INFO_ARGS:{
   20579                 Jim_Cmd *cmdPtr;
   20580 
   20581                 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
   20582                     return JIM_ERR;
   20583                 }
   20584                 if (!cmdPtr->isproc) {
   20585                     Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
   20586                     return JIM_ERR;
   20587                 }
   20588                 switch (option) {
   20589 #ifdef JIM_NO_INTROSPECTION
   20590                     default:
   20591                         Jim_SetResultString(interp, "unsupported", -1);
   20592                         return JIM_ERR;
   20593 #else
   20594                     case INFO_BODY:
   20595                         Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
   20596                         break;
   20597                     case INFO_ARGS:
   20598                         Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
   20599                         break;
   20600 #endif
   20601                     case INFO_STATICS:
   20602                         if (cmdPtr->u.proc.staticVars) {
   20603                             Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
   20604                                 NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES));
   20605                         }
   20606                         break;
   20607                 }
   20608                 return JIM_OK;
   20609             }
   20610 
   20611         case INFO_VERSION:
   20612         case INFO_PATCHLEVEL:{
   20613                 char buf[(JIM_INTEGER_SPACE * 2) + 1];
   20614 
   20615                 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
   20616                 Jim_SetResultString(interp, buf, -1);
   20617                 return JIM_OK;
   20618             }
   20619 
   20620         case INFO_COMPLETE: {
   20621                 char missing;
   20622 
   20623                 Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing));
   20624                 if (missing != ' ' && argc == 4) {
   20625                     Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
   20626                 }
   20627                 return JIM_OK;
   20628             }
   20629 
   20630         case INFO_HOSTNAME:
   20631 
   20632             return Jim_Eval(interp, "os.gethostname");
   20633 
   20634         case INFO_NAMEOFEXECUTABLE:
   20635 
   20636             return Jim_Eval(interp, "{info nameofexecutable}");
   20637 
   20638         case INFO_RETURNCODES:
   20639             if (argc == 2) {
   20640                 int i;
   20641                 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
   20642 
   20643                 for (i = 0; jimReturnCodes[i]; i++) {
   20644                     Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
   20645                     Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
   20646                             jimReturnCodes[i], -1));
   20647                 }
   20648 
   20649                 Jim_SetResult(interp, listObjPtr);
   20650             }
   20651             else if (argc == 3) {
   20652                 long code;
   20653                 const char *name;
   20654 
   20655                 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
   20656                     return JIM_ERR;
   20657                 }
   20658                 name = Jim_ReturnCode(code);
   20659                 if (*name == '?') {
   20660                     Jim_SetResultInt(interp, code);
   20661                 }
   20662                 else {
   20663                     Jim_SetResultString(interp, name, -1);
   20664                 }
   20665             }
   20666             return JIM_OK;
   20667         case INFO_REFERENCES:
   20668 #ifdef JIM_REFERENCES
   20669             return JimInfoReferences(interp, argc, argv);
   20670 #else
   20671             Jim_SetResultString(interp, "not supported", -1);
   20672             return JIM_ERR;
   20673 #endif
   20674         default:
   20675             abort();
   20676     }
   20677 }
   20678 
   20679 
   20680 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20681 {
   20682     Jim_Obj *objPtr;
   20683     int result = 0;
   20684 
   20685     static const char * const options[] = {
   20686         "-command", "-proc", "-alias", "-var", NULL
   20687     };
   20688     enum
   20689     {
   20690         OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
   20691     };
   20692     int option;
   20693 
   20694     if (argc == 2) {
   20695         option = OPT_VAR;
   20696         objPtr = argv[1];
   20697     }
   20698     else if (argc == 3) {
   20699         if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
   20700             return JIM_ERR;
   20701         }
   20702         objPtr = argv[2];
   20703     }
   20704     else {
   20705         Jim_WrongNumArgs(interp, 1, argv, "?option? name");
   20706         return JIM_ERR;
   20707     }
   20708 
   20709     if (option == OPT_VAR) {
   20710         result = Jim_GetVariable(interp, objPtr, 0) != NULL;
   20711     }
   20712     else {
   20713 
   20714         Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
   20715 
   20716         if (cmd) {
   20717             switch (option) {
   20718             case OPT_COMMAND:
   20719                 result = 1;
   20720                 break;
   20721 
   20722             case OPT_ALIAS:
   20723                 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
   20724                 break;
   20725 
   20726             case OPT_PROC:
   20727                 result = cmd->isproc;
   20728                 break;
   20729             }
   20730         }
   20731     }
   20732     Jim_SetResultBool(interp, result);
   20733     return JIM_OK;
   20734 }
   20735 
   20736 
   20737 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20738 {
   20739     const char *str, *splitChars, *noMatchStart;
   20740     int splitLen, strLen;
   20741     Jim_Obj *resObjPtr;
   20742     int c;
   20743     int len;
   20744 
   20745     if (argc != 2 && argc != 3) {
   20746         Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
   20747         return JIM_ERR;
   20748     }
   20749 
   20750     str = Jim_GetString(argv[1], &len);
   20751     if (len == 0) {
   20752         return JIM_OK;
   20753     }
   20754     strLen = Jim_Utf8Length(interp, argv[1]);
   20755 
   20756 
   20757     if (argc == 2) {
   20758         splitChars = " \n\t\r";
   20759         splitLen = 4;
   20760     }
   20761     else {
   20762         splitChars = Jim_String(argv[2]);
   20763         splitLen = Jim_Utf8Length(interp, argv[2]);
   20764     }
   20765 
   20766     noMatchStart = str;
   20767     resObjPtr = Jim_NewListObj(interp, NULL, 0);
   20768 
   20769 
   20770     if (splitLen) {
   20771         Jim_Obj *objPtr;
   20772         while (strLen--) {
   20773             const char *sc = splitChars;
   20774             int scLen = splitLen;
   20775             int sl = utf8_tounicode(str, &c);
   20776             while (scLen--) {
   20777                 int pc;
   20778                 sc += utf8_tounicode(sc, &pc);
   20779                 if (c == pc) {
   20780                     objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
   20781                     Jim_ListAppendElement(interp, resObjPtr, objPtr);
   20782                     noMatchStart = str + sl;
   20783                     break;
   20784                 }
   20785             }
   20786             str += sl;
   20787         }
   20788         objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
   20789         Jim_ListAppendElement(interp, resObjPtr, objPtr);
   20790     }
   20791     else {
   20792         Jim_Obj **commonObj = NULL;
   20793 #define NUM_COMMON (128 - 9)
   20794         while (strLen--) {
   20795             int n = utf8_tounicode(str, &c);
   20796 #ifdef JIM_OPTIMIZATION
   20797             if (c >= 9 && c < 128) {
   20798 
   20799                 c -= 9;
   20800                 if (!commonObj) {
   20801                     commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
   20802                     memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
   20803                 }
   20804                 if (!commonObj[c]) {
   20805                     commonObj[c] = Jim_NewStringObj(interp, str, 1);
   20806                 }
   20807                 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
   20808                 str++;
   20809                 continue;
   20810             }
   20811 #endif
   20812             Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
   20813             str += n;
   20814         }
   20815         Jim_Free(commonObj);
   20816     }
   20817 
   20818     Jim_SetResult(interp, resObjPtr);
   20819     return JIM_OK;
   20820 }
   20821 
   20822 
   20823 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20824 {
   20825     const char *joinStr;
   20826     int joinStrLen;
   20827 
   20828     if (argc != 2 && argc != 3) {
   20829         Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
   20830         return JIM_ERR;
   20831     }
   20832 
   20833     if (argc == 2) {
   20834         joinStr = " ";
   20835         joinStrLen = 1;
   20836     }
   20837     else {
   20838         joinStr = Jim_GetString(argv[2], &joinStrLen);
   20839     }
   20840     Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
   20841     return JIM_OK;
   20842 }
   20843 
   20844 
   20845 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20846 {
   20847     Jim_Obj *objPtr;
   20848 
   20849     if (argc < 2) {
   20850         Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
   20851         return JIM_ERR;
   20852     }
   20853     objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
   20854     if (objPtr == NULL)
   20855         return JIM_ERR;
   20856     Jim_SetResult(interp, objPtr);
   20857     return JIM_OK;
   20858 }
   20859 
   20860 
   20861 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20862 {
   20863     Jim_Obj *listPtr, **outVec;
   20864     int outc, i;
   20865 
   20866     if (argc < 3) {
   20867         Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
   20868         return JIM_ERR;
   20869     }
   20870     if (argv[2]->typePtr != &scanFmtStringObjType)
   20871         SetScanFmtFromAny(interp, argv[2]);
   20872     if (FormatGetError(argv[2]) != 0) {
   20873         Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
   20874         return JIM_ERR;
   20875     }
   20876     if (argc > 3) {
   20877         int maxPos = FormatGetMaxPos(argv[2]);
   20878         int count = FormatGetCnvCount(argv[2]);
   20879 
   20880         if (maxPos > argc - 3) {
   20881             Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
   20882             return JIM_ERR;
   20883         }
   20884         else if (count > argc - 3) {
   20885             Jim_SetResultString(interp, "different numbers of variable names and "
   20886                 "field specifiers", -1);
   20887             return JIM_ERR;
   20888         }
   20889         else if (count < argc - 3) {
   20890             Jim_SetResultString(interp, "variable is not assigned by any "
   20891                 "conversion specifiers", -1);
   20892             return JIM_ERR;
   20893         }
   20894     }
   20895     listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
   20896     if (listPtr == 0)
   20897         return JIM_ERR;
   20898     if (argc > 3) {
   20899         int rc = JIM_OK;
   20900         int count = 0;
   20901 
   20902         if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
   20903             int len = Jim_ListLength(interp, listPtr);
   20904 
   20905             if (len != 0) {
   20906                 JimListGetElements(interp, listPtr, &outc, &outVec);
   20907                 for (i = 0; i < outc; ++i) {
   20908                     if (Jim_Length(outVec[i]) > 0) {
   20909                         ++count;
   20910                         if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
   20911                             rc = JIM_ERR;
   20912                         }
   20913                     }
   20914                 }
   20915             }
   20916             Jim_FreeNewObj(interp, listPtr);
   20917         }
   20918         else {
   20919             count = -1;
   20920         }
   20921         if (rc == JIM_OK) {
   20922             Jim_SetResultInt(interp, count);
   20923         }
   20924         return rc;
   20925     }
   20926     else {
   20927         if (listPtr == (Jim_Obj *)EOF) {
   20928             Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
   20929             return JIM_OK;
   20930         }
   20931         Jim_SetResult(interp, listPtr);
   20932     }
   20933     return JIM_OK;
   20934 }
   20935 
   20936 
   20937 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20938 {
   20939     if (argc != 2 && argc != 3) {
   20940         Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
   20941         return JIM_ERR;
   20942     }
   20943     Jim_SetResult(interp, argv[1]);
   20944     if (argc == 3) {
   20945         JimSetStackTrace(interp, argv[2]);
   20946         return JIM_ERR;
   20947     }
   20948     return JIM_ERR;
   20949 }
   20950 
   20951 
   20952 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20953 {
   20954     Jim_Obj *objPtr;
   20955 
   20956     if (argc != 4) {
   20957         Jim_WrongNumArgs(interp, 1, argv, "list first last");
   20958         return JIM_ERR;
   20959     }
   20960     if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
   20961         return JIM_ERR;
   20962     Jim_SetResult(interp, objPtr);
   20963     return JIM_OK;
   20964 }
   20965 
   20966 
   20967 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   20968 {
   20969     Jim_Obj *objPtr;
   20970     jim_wide count;
   20971 
   20972     if (argc < 2 || Jim_GetWideExpr(interp, argv[1], &count) != JIM_OK || count < 0) {
   20973         Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
   20974         return JIM_ERR;
   20975     }
   20976     if (count == 0 || argc == 2) {
   20977         Jim_SetEmptyResult(interp);
   20978         return JIM_OK;
   20979     }
   20980 
   20981     argc -= 2;
   20982     argv += 2;
   20983 
   20984     objPtr = Jim_NewListObj(interp, NULL, 0);
   20985     ListEnsureLength(objPtr, argc * count);
   20986     while (count--) {
   20987         ListInsertElements(objPtr, -1, argc, argv);
   20988     }
   20989 
   20990     Jim_SetResult(interp, objPtr);
   20991     return JIM_OK;
   20992 }
   20993 
   20994 char **Jim_GetEnviron(void)
   20995 {
   20996 #if defined(HAVE__NSGETENVIRON)
   20997     return *_NSGetEnviron();
   20998 #elif defined(_environ)
   20999     return _environ;
   21000 #else
   21001     #if !defined(NO_ENVIRON_EXTERN)
   21002     extern char **environ;
   21003     #endif
   21004     return environ;
   21005 #endif
   21006 }
   21007 
   21008 void Jim_SetEnviron(char **env)
   21009 {
   21010 #if defined(HAVE__NSGETENVIRON)
   21011     *_NSGetEnviron() = env;
   21012 #elif defined(_environ)
   21013     _environ = env;
   21014 #else
   21015     #if !defined(NO_ENVIRON_EXTERN)
   21016     extern char **environ;
   21017     #endif
   21018 
   21019     environ = env;
   21020 #endif
   21021 }
   21022 
   21023 
   21024 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   21025 {
   21026     const char *key;
   21027     const char *val;
   21028 
   21029     if (argc == 1) {
   21030         char **e = Jim_GetEnviron();
   21031 
   21032         int i;
   21033         Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
   21034 
   21035         for (i = 0; e[i]; i++) {
   21036             const char *equals = strchr(e[i], '=');
   21037 
   21038             if (equals) {
   21039                 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
   21040                         equals - e[i]));
   21041                 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
   21042             }
   21043         }
   21044 
   21045         Jim_SetResult(interp, listObjPtr);
   21046         return JIM_OK;
   21047     }
   21048 
   21049     if (argc > 3) {
   21050         Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
   21051         return JIM_ERR;
   21052     }
   21053     key = Jim_String(argv[1]);
   21054     val = getenv(key);
   21055     if (val == NULL) {
   21056         if (argc < 3) {
   21057             Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
   21058             return JIM_ERR;
   21059         }
   21060         val = Jim_String(argv[2]);
   21061     }
   21062     Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
   21063     return JIM_OK;
   21064 }
   21065 
   21066 
   21067 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   21068 {
   21069     int retval;
   21070 
   21071     if (argc != 2) {
   21072         Jim_WrongNumArgs(interp, 1, argv, "fileName");
   21073         return JIM_ERR;
   21074     }
   21075     retval = Jim_EvalFile(interp, Jim_String(argv[1]));
   21076     if (retval == JIM_RETURN)
   21077         return JIM_OK;
   21078     return retval;
   21079 }
   21080 
   21081 
   21082 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   21083 {
   21084     Jim_Obj *revObjPtr, **ele;
   21085     int len;
   21086 
   21087     if (argc != 2) {
   21088         Jim_WrongNumArgs(interp, 1, argv, "list");
   21089         return JIM_ERR;
   21090     }
   21091     JimListGetElements(interp, argv[1], &len, &ele);
   21092     revObjPtr = Jim_NewListObj(interp, NULL, 0);
   21093     ListEnsureLength(revObjPtr, len);
   21094     len--;
   21095     while (len >= 0)
   21096         ListAppendElement(revObjPtr, ele[len--]);
   21097     Jim_SetResult(interp, revObjPtr);
   21098     return JIM_OK;
   21099 }
   21100 
   21101 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
   21102 {
   21103     jim_wide len;
   21104 
   21105     if (step == 0)
   21106         return -1;
   21107     if (start == end)
   21108         return 0;
   21109     else if (step > 0 && start > end)
   21110         return -1;
   21111     else if (step < 0 && end > start)
   21112         return -1;
   21113     len = end - start;
   21114     if (len < 0)
   21115         len = -len;
   21116     if (step < 0)
   21117         step = -step;
   21118     len = 1 + ((len - 1) / step);
   21119     if (len > INT_MAX)
   21120         len = INT_MAX;
   21121     return (int)((len < 0) ? -1 : len);
   21122 }
   21123 
   21124 
   21125 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   21126 {
   21127     jim_wide start = 0, end, step = 1;
   21128     int len, i;
   21129     Jim_Obj *objPtr;
   21130 
   21131     if (argc < 2 || argc > 4) {
   21132         Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
   21133         return JIM_ERR;
   21134     }
   21135     if (argc == 2) {
   21136         if (Jim_GetWideExpr(interp, argv[1], &end) != JIM_OK)
   21137             return JIM_ERR;
   21138     }
   21139     else {
   21140         if (Jim_GetWideExpr(interp, argv[1], &start) != JIM_OK ||
   21141             Jim_GetWideExpr(interp, argv[2], &end) != JIM_OK)
   21142             return JIM_ERR;
   21143         if (argc == 4 && Jim_GetWideExpr(interp, argv[3], &step) != JIM_OK)
   21144             return JIM_ERR;
   21145     }
   21146     if ((len = JimRangeLen(start, end, step)) == -1) {
   21147         Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
   21148         return JIM_ERR;
   21149     }
   21150     objPtr = Jim_NewListObj(interp, NULL, 0);
   21151     ListEnsureLength(objPtr, len);
   21152     for (i = 0; i < len; i++)
   21153         ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
   21154     Jim_SetResult(interp, objPtr);
   21155     return JIM_OK;
   21156 }
   21157 
   21158 
   21159 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   21160 {
   21161     jim_wide min = 0, max = 0, len, maxMul;
   21162 
   21163     if (argc < 1 || argc > 3) {
   21164         Jim_WrongNumArgs(interp, 1, argv, "?min? max");
   21165         return JIM_ERR;
   21166     }
   21167     if (argc == 1) {
   21168         max = JIM_WIDE_MAX;
   21169     } else if (argc == 2) {
   21170         if (Jim_GetWideExpr(interp, argv[1], &max) != JIM_OK)
   21171             return JIM_ERR;
   21172     } else if (argc == 3) {
   21173         if (Jim_GetWideExpr(interp, argv[1], &min) != JIM_OK ||
   21174             Jim_GetWideExpr(interp, argv[2], &max) != JIM_OK)
   21175             return JIM_ERR;
   21176     }
   21177     len = max-min;
   21178     if (len < 0) {
   21179         Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
   21180         return JIM_ERR;
   21181     }
   21182     maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
   21183     while (1) {
   21184         jim_wide r;
   21185 
   21186         JimRandomBytes(interp, &r, sizeof(jim_wide));
   21187         if (r < 0 || r >= maxMul) continue;
   21188         r = (len == 0) ? 0 : r%len;
   21189         Jim_SetResultInt(interp, min+r);
   21190         return JIM_OK;
   21191     }
   21192 }
   21193 
   21194 static const struct {
   21195     const char *name;
   21196     Jim_CmdProc *cmdProc;
   21197 } Jim_CoreCommandsTable[] = {
   21198     {"alias", Jim_AliasCoreCommand},
   21199     {"set", Jim_SetCoreCommand},
   21200     {"unset", Jim_UnsetCoreCommand},
   21201     {"puts", Jim_PutsCoreCommand},
   21202     {"+", Jim_AddCoreCommand},
   21203     {"*", Jim_MulCoreCommand},
   21204     {"-", Jim_SubCoreCommand},
   21205     {"/", Jim_DivCoreCommand},
   21206     {"incr", Jim_IncrCoreCommand},
   21207     {"while", Jim_WhileCoreCommand},
   21208     {"loop", Jim_LoopCoreCommand},
   21209     {"for", Jim_ForCoreCommand},
   21210     {"foreach", Jim_ForeachCoreCommand},
   21211     {"lmap", Jim_LmapCoreCommand},
   21212     {"lassign", Jim_LassignCoreCommand},
   21213     {"if", Jim_IfCoreCommand},
   21214     {"switch", Jim_SwitchCoreCommand},
   21215     {"list", Jim_ListCoreCommand},
   21216     {"lindex", Jim_LindexCoreCommand},
   21217     {"lset", Jim_LsetCoreCommand},
   21218     {"lsearch", Jim_LsearchCoreCommand},
   21219     {"llength", Jim_LlengthCoreCommand},
   21220     {"lappend", Jim_LappendCoreCommand},
   21221     {"linsert", Jim_LinsertCoreCommand},
   21222     {"lreplace", Jim_LreplaceCoreCommand},
   21223     {"lsort", Jim_LsortCoreCommand},
   21224     {"append", Jim_AppendCoreCommand},
   21225     {"eval", Jim_EvalCoreCommand},
   21226     {"uplevel", Jim_UplevelCoreCommand},
   21227     {"expr", Jim_ExprCoreCommand},
   21228     {"break", Jim_BreakCoreCommand},
   21229     {"continue", Jim_ContinueCoreCommand},
   21230     {"proc", Jim_ProcCoreCommand},
   21231     {"xtrace", Jim_XtraceCoreCommand},
   21232     {"concat", Jim_ConcatCoreCommand},
   21233     {"return", Jim_ReturnCoreCommand},
   21234     {"upvar", Jim_UpvarCoreCommand},
   21235     {"global", Jim_GlobalCoreCommand},
   21236     {"string", Jim_StringCoreCommand},
   21237     {"time", Jim_TimeCoreCommand},
   21238     {"timerate", Jim_TimeRateCoreCommand},
   21239     {"exit", Jim_ExitCoreCommand},
   21240     {"catch", Jim_CatchCoreCommand},
   21241     {"try", Jim_TryCoreCommand},
   21242 #ifdef JIM_REFERENCES
   21243     {"ref", Jim_RefCoreCommand},
   21244     {"getref", Jim_GetrefCoreCommand},
   21245     {"setref", Jim_SetrefCoreCommand},
   21246     {"finalize", Jim_FinalizeCoreCommand},
   21247     {"collect", Jim_CollectCoreCommand},
   21248 #endif
   21249     {"rename", Jim_RenameCoreCommand},
   21250     {"dict", Jim_DictCoreCommand},
   21251     {"subst", Jim_SubstCoreCommand},
   21252     {"info", Jim_InfoCoreCommand},
   21253     {"exists", Jim_ExistsCoreCommand},
   21254     {"split", Jim_SplitCoreCommand},
   21255     {"join", Jim_JoinCoreCommand},
   21256     {"format", Jim_FormatCoreCommand},
   21257     {"scan", Jim_ScanCoreCommand},
   21258     {"error", Jim_ErrorCoreCommand},
   21259     {"lrange", Jim_LrangeCoreCommand},
   21260     {"lrepeat", Jim_LrepeatCoreCommand},
   21261     {"env", Jim_EnvCoreCommand},
   21262     {"source", Jim_SourceCoreCommand},
   21263     {"lreverse", Jim_LreverseCoreCommand},
   21264     {"range", Jim_RangeCoreCommand},
   21265     {"rand", Jim_RandCoreCommand},
   21266     {"tailcall", Jim_TailcallCoreCommand},
   21267     {"local", Jim_LocalCoreCommand},
   21268     {"upcall", Jim_UpcallCoreCommand},
   21269     {"apply", Jim_ApplyCoreCommand},
   21270     {"stacktrace", Jim_StacktraceCoreCommand},
   21271     {NULL, NULL},
   21272 };
   21273 
   21274 void Jim_RegisterCoreCommands(Jim_Interp *interp)
   21275 {
   21276     int i = 0;
   21277 
   21278     while (Jim_CoreCommandsTable[i].name != NULL) {
   21279         Jim_CreateCommand(interp,
   21280             Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
   21281         i++;
   21282     }
   21283 }
   21284 
   21285 void Jim_MakeErrorMessage(Jim_Interp *interp)
   21286 {
   21287     Jim_Obj *argv[2];
   21288 
   21289     argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
   21290     argv[1] = interp->result;
   21291 
   21292     Jim_EvalObjVector(interp, 2, argv);
   21293 }
   21294 
   21295 static char **JimSortStringTable(const char *const *tablePtr)
   21296 {
   21297     int count;
   21298     char **tablePtrSorted;
   21299 
   21300 
   21301     for (count = 0; tablePtr[count]; count++) {
   21302     }
   21303 
   21304 
   21305     tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1));
   21306     memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
   21307     qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
   21308     tablePtrSorted[count] = NULL;
   21309 
   21310     return tablePtrSorted;
   21311 }
   21312 
   21313 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
   21314     const char *prefix, const char *const *tablePtr, const char *name)
   21315 {
   21316     char **tablePtrSorted;
   21317     int i;
   21318 
   21319     if (name == NULL) {
   21320         name = "option";
   21321     }
   21322 
   21323     Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
   21324     tablePtrSorted = JimSortStringTable(tablePtr);
   21325     for (i = 0; tablePtrSorted[i]; i++) {
   21326         if (tablePtrSorted[i + 1] == NULL && i > 0) {
   21327             Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
   21328         }
   21329         Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
   21330         if (tablePtrSorted[i + 1]) {
   21331             Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
   21332         }
   21333     }
   21334     Jim_Free(tablePtrSorted);
   21335 }
   21336 
   21337 
   21338 int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr)
   21339 {
   21340     if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) {
   21341         int i;
   21342         char **tablePtrSorted = JimSortStringTable(tablePtr);
   21343         Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
   21344         for (i = 0; tablePtrSorted[i]; i++) {
   21345             Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1));
   21346         }
   21347         Jim_Free(tablePtrSorted);
   21348         return JIM_OK;
   21349     }
   21350     return JIM_ERR;
   21351 }
   21352 
   21353 static const Jim_ObjType getEnumObjType = {
   21354     "get-enum",
   21355     NULL,
   21356     NULL,
   21357     NULL,
   21358     JIM_TYPE_REFERENCES
   21359 };
   21360 
   21361 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
   21362     const char *const *tablePtr, int *indexPtr, const char *name, int flags)
   21363 {
   21364     const char *bad = "bad ";
   21365     const char *const *entryPtr = NULL;
   21366     int i;
   21367     int match = -1;
   21368     int arglen;
   21369     const char *arg;
   21370 
   21371     if (objPtr->typePtr == &getEnumObjType) {
   21372         if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) {
   21373             *indexPtr = objPtr->internalRep.ptrIntValue.int2;
   21374             return JIM_OK;
   21375         }
   21376     }
   21377 
   21378     arg = Jim_GetString(objPtr, &arglen);
   21379 
   21380     *indexPtr = -1;
   21381 
   21382     for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
   21383         if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
   21384 
   21385             match = i;
   21386             goto found;
   21387         }
   21388         if (flags & JIM_ENUM_ABBREV) {
   21389             if (strncmp(arg, *entryPtr, arglen) == 0) {
   21390                 if (*arg == '-' && arglen == 1) {
   21391                     break;
   21392                 }
   21393                 if (match >= 0) {
   21394                     bad = "ambiguous ";
   21395                     goto ambiguous;
   21396                 }
   21397                 match = i;
   21398             }
   21399         }
   21400     }
   21401 
   21402 
   21403     if (match >= 0) {
   21404   found:
   21405 
   21406         Jim_FreeIntRep(interp, objPtr);
   21407         objPtr->typePtr = &getEnumObjType;
   21408         objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr;
   21409         objPtr->internalRep.ptrIntValue.int1 = flags;
   21410         objPtr->internalRep.ptrIntValue.int2 = match;
   21411 
   21412         *indexPtr = match;
   21413         return JIM_OK;
   21414     }
   21415 
   21416   ambiguous:
   21417     if (flags & JIM_ERRMSG) {
   21418         JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
   21419     }
   21420     return JIM_ERR;
   21421 }
   21422 
   21423 int Jim_FindByName(const char *name, const char * const array[], size_t len)
   21424 {
   21425     int i;
   21426 
   21427     for (i = 0; i < (int)len; i++) {
   21428         if (array[i] && strcmp(array[i], name) == 0) {
   21429             return i;
   21430         }
   21431     }
   21432     return -1;
   21433 }
   21434 
   21435 int Jim_IsDict(Jim_Obj *objPtr)
   21436 {
   21437     return objPtr->typePtr == &dictObjType;
   21438 }
   21439 
   21440 int Jim_IsList(Jim_Obj *objPtr)
   21441 {
   21442     return objPtr->typePtr == &listObjType;
   21443 }
   21444 
   21445 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
   21446 {
   21447 
   21448     int len = strlen(format);
   21449     int extra = 0;
   21450     int n = 0;
   21451     const char *params[5];
   21452     int nobjparam = 0;
   21453     Jim_Obj *objparam[5];
   21454     char *buf;
   21455     va_list args;
   21456     int i;
   21457 
   21458     va_start(args, format);
   21459 
   21460     for (i = 0; i < len && n < 5; i++) {
   21461         int l;
   21462 
   21463         if (strncmp(format + i, "%s", 2) == 0) {
   21464             params[n] = va_arg(args, char *);
   21465 
   21466             l = strlen(params[n]);
   21467         }
   21468         else if (strncmp(format + i, "%#s", 3) == 0) {
   21469             Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
   21470 
   21471             params[n] = Jim_GetString(objPtr, &l);
   21472             objparam[nobjparam++] = objPtr;
   21473             Jim_IncrRefCount(objPtr);
   21474         }
   21475         else {
   21476             if (format[i] == '%') {
   21477                 i++;
   21478             }
   21479             continue;
   21480         }
   21481         n++;
   21482         extra += l;
   21483     }
   21484 
   21485     len += extra;
   21486     buf = Jim_Alloc(len + 1);
   21487     len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
   21488 
   21489     va_end(args);
   21490 
   21491     Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
   21492 
   21493     for (i = 0; i < nobjparam; i++) {
   21494         Jim_DecrRefCount(interp, objparam[i]);
   21495     }
   21496 }
   21497 
   21498 int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version)
   21499 {
   21500     if (abi_version != JIM_ABI_VERSION) {
   21501         Jim_SetResultString(interp, "ABI version mismatch", -1);
   21502         return JIM_ERR;
   21503     }
   21504     return JIM_OK;
   21505 }
   21506 
   21507 
   21508 #ifndef jim_ext_package
   21509 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
   21510 {
   21511     return JIM_OK;
   21512 }
   21513 #endif
   21514 #ifndef jim_ext_aio
   21515 int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
   21516 {
   21517     return -1;
   21518 }
   21519 #endif
   21520 
   21521 
   21522 #include <stdio.h>
   21523 #include <string.h>
   21524 
   21525 
   21526 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   21527 {
   21528 
   21529     return JIM_OK;
   21530 }
   21531 
   21532 static const jim_subcmd_type dummy_subcmd = {
   21533     "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
   21534 };
   21535 
   21536 static Jim_Obj *subcmd_cmd_list(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
   21537 {
   21538 
   21539     Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
   21540     Jim_Obj *sortCmd[2];
   21541 
   21542     for (; ct->cmd; ct++) {
   21543         if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
   21544             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, ct->cmd, -1));
   21545         }
   21546     }
   21547 
   21548 
   21549     sortCmd[0] = Jim_NewStringObj(interp, "lsort", -1);
   21550     sortCmd[1] = listObj;
   21551 
   21552     if (Jim_EvalObjVector(interp, 2, sortCmd) == JIM_OK) {
   21553         return Jim_ListJoin(interp, Jim_GetResult(interp), sep, strlen(sep));
   21554     }
   21555 
   21556     return Jim_GetResult(interp);
   21557 }
   21558 
   21559 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
   21560     Jim_Obj *cmd, Jim_Obj *subcmd)
   21561 {
   21562     Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be %#s", cmd, type,
   21563         subcmd, subcmd_cmd_list(interp, command_table, ", "));
   21564 }
   21565 
   21566 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
   21567     Jim_Obj *const *argv)
   21568 {
   21569     Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: %#s",
   21570         argv[0], subcmd_cmd_list(interp, command_table, ", "));
   21571 }
   21572 
   21573 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
   21574 {
   21575     if (cmd) {
   21576         Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
   21577     }
   21578     Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
   21579     if (ct->args && *ct->args) {
   21580         Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
   21581     }
   21582 }
   21583 
   21584 void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *subcmd)
   21585 {
   21586     Jim_SetResultString(interp, "wrong # args: should be \"", -1);
   21587     add_cmd_usage(interp, ct, subcmd);
   21588     Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
   21589 }
   21590 
   21591 static const Jim_ObjType subcmdLookupObjType = {
   21592     "subcmd-lookup",
   21593     NULL,
   21594     NULL,
   21595     NULL,
   21596     JIM_TYPE_REFERENCES
   21597 };
   21598 
   21599 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
   21600     int argc, Jim_Obj *const *argv)
   21601 {
   21602     const jim_subcmd_type *ct;
   21603     const jim_subcmd_type *partial = 0;
   21604     int cmdlen;
   21605     Jim_Obj *cmd;
   21606     const char *cmdstr;
   21607     int help = 0;
   21608     int argsok = 1;
   21609 
   21610     if (argc < 2) {
   21611         Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n"
   21612             "Use \"%#s -help ?command?\" for help", argv[0], argv[0]);
   21613         return 0;
   21614     }
   21615 
   21616     cmd = argv[1];
   21617 
   21618 
   21619     if (cmd->typePtr == &subcmdLookupObjType) {
   21620         if (cmd->internalRep.ptrIntValue.ptr == command_table) {
   21621             ct = command_table + cmd->internalRep.ptrIntValue.int1;
   21622             goto found;
   21623         }
   21624     }
   21625 
   21626 
   21627     if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
   21628         if (argc == 2) {
   21629 
   21630             show_cmd_usage(interp, command_table, argc, argv);
   21631             return &dummy_subcmd;
   21632         }
   21633         help = 1;
   21634 
   21635 
   21636         cmd = argv[2];
   21637     }
   21638 
   21639 
   21640     if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
   21641         Jim_SetResult(interp, subcmd_cmd_list(interp, command_table, " "));
   21642         return &dummy_subcmd;
   21643     }
   21644 
   21645     cmdstr = Jim_GetString(cmd, &cmdlen);
   21646 
   21647     for (ct = command_table; ct->cmd; ct++) {
   21648         if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
   21649 
   21650             break;
   21651         }
   21652         if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
   21653             if (partial) {
   21654 
   21655                 if (help) {
   21656 
   21657                     show_cmd_usage(interp, command_table, argc, argv);
   21658                     return &dummy_subcmd;
   21659                 }
   21660                 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
   21661                 return 0;
   21662             }
   21663             partial = ct;
   21664         }
   21665         continue;
   21666     }
   21667 
   21668 
   21669     if (partial && !ct->cmd) {
   21670         ct = partial;
   21671     }
   21672 
   21673     if (!ct->cmd) {
   21674 
   21675         if (help) {
   21676 
   21677             show_cmd_usage(interp, command_table, argc, argv);
   21678             return &dummy_subcmd;
   21679         }
   21680         bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
   21681         return 0;
   21682     }
   21683 
   21684     if (help) {
   21685         Jim_SetResultString(interp, "Usage: ", -1);
   21686 
   21687         add_cmd_usage(interp, ct, argv[0]);
   21688         return &dummy_subcmd;
   21689     }
   21690 
   21691 
   21692     Jim_FreeIntRep(interp, cmd);
   21693     cmd->typePtr = &subcmdLookupObjType;
   21694     cmd->internalRep.ptrIntValue.ptr = (void *)command_table;
   21695     cmd->internalRep.ptrIntValue.int1 = ct - command_table;
   21696 
   21697 found:
   21698 
   21699 
   21700     if (argc - 2 < ct->minargs) {
   21701         argsok = 0;
   21702     }
   21703     else if (ct->maxargs >= 0 && argc - 2 > ct->maxargs) {
   21704         argsok = 0;
   21705     }
   21706     else if (ct->maxargs < -1 && (argc - 2) % -ct->maxargs != 0) {
   21707 
   21708         argsok = 0;
   21709     }
   21710     if (!argsok) {
   21711         Jim_SetResultString(interp, "wrong # args: should be \"", -1);
   21712 
   21713         add_cmd_usage(interp, ct, argv[0]);
   21714         Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
   21715 
   21716         return 0;
   21717     }
   21718 
   21719 
   21720     return ct;
   21721 }
   21722 
   21723 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
   21724 {
   21725     int ret = JIM_ERR;
   21726 
   21727     if (ct) {
   21728         if (ct->flags & JIM_MODFLAG_FULLARGV) {
   21729             ret = ct->function(interp, argc, argv);
   21730         }
   21731         else {
   21732             ret = ct->function(interp, argc - 2, argv + 2);
   21733         }
   21734         if (ret < 0) {
   21735             Jim_SubCmdArgError(interp, ct, argv[0]);
   21736             ret = JIM_ERR;
   21737         }
   21738     }
   21739     return ret;
   21740 }
   21741 
   21742 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
   21743 {
   21744     const jim_subcmd_type *ct =
   21745         Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
   21746 
   21747     return Jim_CallSubCmd(interp, ct, argc, argv);
   21748 }
   21749 
   21750 #include <ctype.h>
   21751 #include <stdlib.h>
   21752 #include <string.h>
   21753 #include <stdio.h>
   21754 #include <assert.h>
   21755 
   21756 
   21757 int utf8_fromunicode(char *p, unsigned uc)
   21758 {
   21759     if (uc <= 0x7f) {
   21760         *p = uc;
   21761         return 1;
   21762     }
   21763     else if (uc <= 0x7ff) {
   21764         *p++ = 0xc0 | ((uc & 0x7c0) >> 6);
   21765         *p = 0x80 | (uc & 0x3f);
   21766         return 2;
   21767     }
   21768     else if (uc <= 0xffff) {
   21769         *p++ = 0xe0 | ((uc & 0xf000) >> 12);
   21770         *p++ = 0x80 | ((uc & 0xfc0) >> 6);
   21771         *p = 0x80 | (uc & 0x3f);
   21772         return 3;
   21773     }
   21774 
   21775     else {
   21776         *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
   21777         *p++ = 0x80 | ((uc & 0x3f000) >> 12);
   21778         *p++ = 0x80 | ((uc & 0xfc0) >> 6);
   21779         *p = 0x80 | (uc & 0x3f);
   21780         return 4;
   21781     }
   21782 }
   21783 
   21784 #include <ctype.h>
   21785 #include <string.h>
   21786 #include <stdio.h>
   21787 
   21788 
   21789 #define JIM_INTEGER_SPACE 24
   21790 #define MAX_FLOAT_WIDTH 320
   21791 
   21792 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
   21793 {
   21794     const char *span, *format, *formatEnd, *msg;
   21795     int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
   21796     static const char * const mixedXPG =
   21797             "cannot mix \"%\" and \"%n$\" conversion specifiers";
   21798     static const char * const badIndex[2] = {
   21799         "not enough arguments for all format specifiers",
   21800         "\"%n$\" argument index out of range"
   21801     };
   21802     int formatLen;
   21803     Jim_Obj *resultPtr;
   21804 
   21805     char *num_buffer = NULL;
   21806     int num_buffer_size = 0;
   21807 
   21808     span = format = Jim_GetString(fmtObjPtr, &formatLen);
   21809     formatEnd = format + formatLen;
   21810     resultPtr = Jim_NewEmptyStringObj(interp);
   21811 
   21812     while (format != formatEnd) {
   21813         char *end;
   21814         int gotMinus, sawFlag;
   21815         int gotPrecision, useShort;
   21816         long width, precision;
   21817         int newXpg;
   21818         int ch;
   21819         int step;
   21820         int doubleType;
   21821         char pad = ' ';
   21822         char spec[2*JIM_INTEGER_SPACE + 12];
   21823         char *p;
   21824 
   21825         int formatted_chars;
   21826         int formatted_bytes;
   21827         const char *formatted_buf;
   21828 
   21829         step = utf8_tounicode(format, &ch);
   21830         format += step;
   21831         if (ch != '%') {
   21832             numBytes += step;
   21833             continue;
   21834         }
   21835         if (numBytes) {
   21836             Jim_AppendString(interp, resultPtr, span, numBytes);
   21837             numBytes = 0;
   21838         }
   21839 
   21840 
   21841         step = utf8_tounicode(format, &ch);
   21842         if (ch == '%') {
   21843             span = format;
   21844             numBytes = step;
   21845             format += step;
   21846             continue;
   21847         }
   21848 
   21849 
   21850         newXpg = 0;
   21851         if (isdigit(ch)) {
   21852             int position = strtoul(format, &end, 10);
   21853             if (*end == '$') {
   21854                 newXpg = 1;
   21855                 objIndex = position - 1;
   21856                 format = end + 1;
   21857                 step = utf8_tounicode(format, &ch);
   21858             }
   21859         }
   21860         if (newXpg) {
   21861             if (gotSequential) {
   21862                 msg = mixedXPG;
   21863                 goto errorMsg;
   21864             }
   21865             gotXpg = 1;
   21866         } else {
   21867             if (gotXpg) {
   21868                 msg = mixedXPG;
   21869                 goto errorMsg;
   21870             }
   21871             gotSequential = 1;
   21872         }
   21873         if ((objIndex < 0) || (objIndex >= objc)) {
   21874             msg = badIndex[gotXpg];
   21875             goto errorMsg;
   21876         }
   21877 
   21878         p = spec;
   21879         *p++ = '%';
   21880 
   21881         gotMinus = 0;
   21882         sawFlag = 1;
   21883         do {
   21884             switch (ch) {
   21885             case '-':
   21886                 gotMinus = 1;
   21887                 break;
   21888             case '0':
   21889                 pad = ch;
   21890                 break;
   21891             case ' ':
   21892             case '+':
   21893             case '#':
   21894                 break;
   21895             default:
   21896                 sawFlag = 0;
   21897                 continue;
   21898             }
   21899             *p++ = ch;
   21900             format += step;
   21901             step = utf8_tounicode(format, &ch);
   21902 
   21903         } while (sawFlag && (p - spec <= 5));
   21904 
   21905 
   21906         width = 0;
   21907         if (isdigit(ch)) {
   21908             width = strtoul(format, &end, 10);
   21909             format = end;
   21910             step = utf8_tounicode(format, &ch);
   21911         } else if (ch == '*') {
   21912             if (objIndex >= objc - 1) {
   21913                 msg = badIndex[gotXpg];
   21914                 goto errorMsg;
   21915             }
   21916             if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) {
   21917                 goto error;
   21918             }
   21919             if (width < 0) {
   21920                 width = -width;
   21921                 if (!gotMinus) {
   21922                     *p++ = '-';
   21923                     gotMinus = 1;
   21924                 }
   21925             }
   21926             objIndex++;
   21927             format += step;
   21928             step = utf8_tounicode(format, &ch);
   21929         }
   21930 
   21931 
   21932         gotPrecision = precision = 0;
   21933         if (ch == '.') {
   21934             gotPrecision = 1;
   21935             format += step;
   21936             step = utf8_tounicode(format, &ch);
   21937         }
   21938         if (isdigit(ch)) {
   21939             precision = strtoul(format, &end, 10);
   21940             format = end;
   21941             step = utf8_tounicode(format, &ch);
   21942         } else if (ch == '*') {
   21943             if (objIndex >= objc - 1) {
   21944                 msg = badIndex[gotXpg];
   21945                 goto errorMsg;
   21946             }
   21947             if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) {
   21948                 goto error;
   21949             }
   21950 
   21951 
   21952             if (precision < 0) {
   21953                 precision = 0;
   21954             }
   21955             objIndex++;
   21956             format += step;
   21957             step = utf8_tounicode(format, &ch);
   21958         }
   21959 
   21960 
   21961         useShort = 0;
   21962         if (ch == 'h') {
   21963             useShort = 1;
   21964             format += step;
   21965             step = utf8_tounicode(format, &ch);
   21966         } else if (ch == 'l') {
   21967 
   21968             format += step;
   21969             step = utf8_tounicode(format, &ch);
   21970             if (ch == 'l') {
   21971                 format += step;
   21972                 step = utf8_tounicode(format, &ch);
   21973             }
   21974         }
   21975 
   21976         format += step;
   21977         span = format;
   21978 
   21979 
   21980         if (ch == 'i') {
   21981             ch = 'd';
   21982         }
   21983 
   21984         doubleType = 0;
   21985 
   21986         switch (ch) {
   21987         case '\0':
   21988             msg = "format string ended in middle of field specifier";
   21989             goto errorMsg;
   21990         case 's': {
   21991             formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
   21992             formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
   21993             if (gotPrecision && (precision < formatted_chars)) {
   21994 
   21995                 formatted_chars = precision;
   21996                 formatted_bytes = utf8_index(formatted_buf, precision);
   21997             }
   21998             break;
   21999         }
   22000         case 'c': {
   22001             jim_wide code;
   22002 
   22003             if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
   22004                 goto error;
   22005             }
   22006 
   22007             formatted_bytes = utf8_getchars(spec, code);
   22008             formatted_buf = spec;
   22009             formatted_chars = 1;
   22010             break;
   22011         }
   22012         case 'b': {
   22013                 unsigned jim_wide w;
   22014                 int length;
   22015                 int i;
   22016                 int j;
   22017 
   22018                 if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
   22019                     goto error;
   22020                 }
   22021                 length = sizeof(w) * 8;
   22022 
   22023 
   22024 
   22025                 if (num_buffer_size < length + 1) {
   22026                     num_buffer_size = length + 1;
   22027                     num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
   22028                 }
   22029 
   22030                 j = 0;
   22031                 for (i = length; i > 0; ) {
   22032                         i--;
   22033                         if (w & ((unsigned jim_wide)1 << i)) {
   22034                                 num_buffer[j++] = '1';
   22035                         }
   22036                         else if (j || i == 0) {
   22037                                 num_buffer[j++] = '0';
   22038                         }
   22039                 }
   22040                 num_buffer[j] = 0;
   22041                 formatted_chars = formatted_bytes = j;
   22042                 formatted_buf = num_buffer;
   22043                 break;
   22044         }
   22045 
   22046         case 'e':
   22047         case 'E':
   22048         case 'f':
   22049         case 'g':
   22050         case 'G':
   22051             doubleType = 1;
   22052 
   22053         case 'd':
   22054         case 'u':
   22055         case 'o':
   22056         case 'x':
   22057         case 'X': {
   22058             jim_wide w;
   22059             double d;
   22060             int length;
   22061 
   22062 
   22063             if (width) {
   22064                 p += sprintf(p, "%ld", width);
   22065             }
   22066             if (gotPrecision) {
   22067                 p += sprintf(p, ".%ld", precision);
   22068             }
   22069 
   22070 
   22071             if (doubleType) {
   22072                 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
   22073                     goto error;
   22074                 }
   22075                 length = MAX_FLOAT_WIDTH;
   22076             }
   22077             else {
   22078                 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) {
   22079                     goto error;
   22080                 }
   22081                 length = JIM_INTEGER_SPACE;
   22082                 if (useShort) {
   22083                     if (ch == 'd') {
   22084                         w = (short)w;
   22085                     }
   22086                     else {
   22087                         w = (unsigned short)w;
   22088                     }
   22089                 }
   22090                 *p++ = 'l';
   22091 #ifdef HAVE_LONG_LONG
   22092                 if (sizeof(long long) == sizeof(jim_wide)) {
   22093                     *p++ = 'l';
   22094                 }
   22095 #endif
   22096             }
   22097 
   22098             *p++ = (char) ch;
   22099             *p = '\0';
   22100 
   22101 
   22102             if (width > 10000 || length > 10000 || precision > 10000) {
   22103                 Jim_SetResultString(interp, "format too long", -1);
   22104                 goto error;
   22105             }
   22106 
   22107 
   22108 
   22109             if (width > length) {
   22110                 length = width;
   22111             }
   22112             if (gotPrecision) {
   22113                 length += precision;
   22114             }
   22115 
   22116 
   22117             if (num_buffer_size < length + 1) {
   22118                 num_buffer_size = length + 1;
   22119                 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
   22120             }
   22121 
   22122             if (doubleType) {
   22123                 snprintf(num_buffer, length + 1, spec, d);
   22124             }
   22125             else {
   22126                 formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
   22127             }
   22128             formatted_chars = formatted_bytes = strlen(num_buffer);
   22129             formatted_buf = num_buffer;
   22130             break;
   22131         }
   22132 
   22133         default: {
   22134 
   22135             spec[0] = ch;
   22136             spec[1] = '\0';
   22137             Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
   22138             goto error;
   22139         }
   22140         }
   22141 
   22142         if (!gotMinus) {
   22143             while (formatted_chars < width) {
   22144                 Jim_AppendString(interp, resultPtr, &pad, 1);
   22145                 formatted_chars++;
   22146             }
   22147         }
   22148 
   22149         Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes);
   22150 
   22151         while (formatted_chars < width) {
   22152             Jim_AppendString(interp, resultPtr, &pad, 1);
   22153             formatted_chars++;
   22154         }
   22155 
   22156         objIndex += gotSequential;
   22157     }
   22158     if (numBytes) {
   22159         Jim_AppendString(interp, resultPtr, span, numBytes);
   22160     }
   22161 
   22162     Jim_Free(num_buffer);
   22163     return resultPtr;
   22164 
   22165   errorMsg:
   22166     Jim_SetResultString(interp, msg, -1);
   22167   error:
   22168     Jim_FreeNewObj(interp, resultPtr);
   22169     Jim_Free(num_buffer);
   22170     return NULL;
   22171 }
   22172 
   22173 
   22174 #if defined(JIM_REGEXP)
   22175 #include <stdio.h>
   22176 #include <ctype.h>
   22177 #include <stdlib.h>
   22178 #include <string.h>
   22179 
   22180 
   22181 
   22182 #define REG_MAX_PAREN 100
   22183 
   22184 
   22185 
   22186 #define	END	0
   22187 #define	BOL	1
   22188 #define	EOL	2
   22189 #define	ANY	3
   22190 #define	ANYOF	4
   22191 #define	ANYBUT	5
   22192 #define	BRANCH	6
   22193 #define	BACK	7
   22194 #define	EXACTLY	8
   22195 #define	NOTHING	9
   22196 #define	REP	10
   22197 #define	REPMIN	11
   22198 #define	REPX	12
   22199 #define	REPXMIN	13
   22200 #define	BOLX	14
   22201 #define	EOLX	15
   22202 #define	WORDA	16
   22203 #define	WORDZ	17
   22204 
   22205 #define	OPENNC 	1000
   22206 #define	OPEN   	1001
   22207 
   22208 
   22209 
   22210 
   22211 #define	CLOSENC	2000
   22212 #define	CLOSE	2001
   22213 #define	CLOSE_END	(CLOSE+REG_MAX_PAREN)
   22214 
   22215 #define	REG_MAGIC	0xFADED00D
   22216 
   22217 
   22218 #define	OP(preg, p)	(preg->program[p])
   22219 #define	NEXT(preg, p)	(preg->program[p + 1])
   22220 #define	OPERAND(p)	((p) + 2)
   22221 
   22222 
   22223 
   22224 
   22225 #define	FAIL(R,M)	{ (R)->err = (M); return (M); }
   22226 #define	ISMULT(c)	((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
   22227 #define	META		"^$.[()|?{+*"
   22228 
   22229 #define	HASWIDTH	1
   22230 #define	SIMPLE		2
   22231 #define	SPSTART		4
   22232 #define	WORST		0
   22233 
   22234 #define MAX_REP_COUNT 1000000
   22235 
   22236 static int reg(regex_t *preg, int paren, int *flagp );
   22237 static int regpiece(regex_t *preg, int *flagp );
   22238 static int regbranch(regex_t *preg, int *flagp );
   22239 static int regatom(regex_t *preg, int *flagp );
   22240 static int regnode(regex_t *preg, int op );
   22241 static int regnext(regex_t *preg, int p );
   22242 static void regc(regex_t *preg, int b );
   22243 static int reginsert(regex_t *preg, int op, int size, int opnd );
   22244 static void regtail(regex_t *preg, int p, int val);
   22245 static void regoptail(regex_t *preg, int p, int val );
   22246 static int regopsize(regex_t *preg, int p );
   22247 
   22248 static int reg_range_find(const int *string, int c);
   22249 static const char *str_find(const char *string, int c, int nocase);
   22250 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase);
   22251 
   22252 
   22253 #ifdef DEBUG
   22254 static int regnarrate = 0;
   22255 static void regdump(regex_t *preg);
   22256 static const char *regprop( int op );
   22257 #endif
   22258 
   22259 
   22260 static int str_int_len(const int *seq)
   22261 {
   22262 	int n = 0;
   22263 	while (*seq++) {
   22264 		n++;
   22265 	}
   22266 	return n;
   22267 }
   22268 
   22269 int jim_regcomp(regex_t *preg, const char *exp, int cflags)
   22270 {
   22271 	int scan;
   22272 	int longest;
   22273 	unsigned len;
   22274 	int flags;
   22275 
   22276 #ifdef DEBUG
   22277 	fprintf(stderr, "Compiling: '%s'\n", exp);
   22278 #endif
   22279 	memset(preg, 0, sizeof(*preg));
   22280 
   22281 	if (exp == NULL)
   22282 		FAIL(preg, REG_ERR_NULL_ARGUMENT);
   22283 
   22284 
   22285 	preg->cflags = cflags;
   22286 	preg->regparse = exp;
   22287 
   22288 
   22289 	preg->proglen = (strlen(exp) + 1) * 5;
   22290 	preg->program = malloc(preg->proglen * sizeof(int));
   22291 	if (preg->program == NULL)
   22292 		FAIL(preg, REG_ERR_NOMEM);
   22293 
   22294 	regc(preg, REG_MAGIC);
   22295 	if (reg(preg, 0, &flags) == 0) {
   22296 		return preg->err;
   22297 	}
   22298 
   22299 
   22300 	if (preg->re_nsub >= REG_MAX_PAREN)
   22301 		FAIL(preg,REG_ERR_TOO_BIG);
   22302 
   22303 
   22304 	preg->regstart = 0;
   22305 	preg->reganch = 0;
   22306 	preg->regmust = 0;
   22307 	preg->regmlen = 0;
   22308 	scan = 1;
   22309 	if (OP(preg, regnext(preg, scan)) == END) {
   22310 		scan = OPERAND(scan);
   22311 
   22312 
   22313 		if (OP(preg, scan) == EXACTLY) {
   22314 			preg->regstart = preg->program[OPERAND(scan)];
   22315 		}
   22316 		else if (OP(preg, scan) == BOL)
   22317 			preg->reganch++;
   22318 
   22319 		if (flags&SPSTART) {
   22320 			longest = 0;
   22321 			len = 0;
   22322 			for (; scan != 0; scan = regnext(preg, scan)) {
   22323 				if (OP(preg, scan) == EXACTLY) {
   22324 					int plen = str_int_len(preg->program + OPERAND(scan));
   22325 					if (plen >= len) {
   22326 						longest = OPERAND(scan);
   22327 						len = plen;
   22328 					}
   22329 				}
   22330 			}
   22331 			preg->regmust = longest;
   22332 			preg->regmlen = len;
   22333 		}
   22334 	}
   22335 
   22336 #ifdef DEBUG
   22337 	regdump(preg);
   22338 #endif
   22339 
   22340 	return 0;
   22341 }
   22342 
   22343 static int reg(regex_t *preg, int paren, int *flagp )
   22344 {
   22345 	int ret;
   22346 	int br;
   22347 	int ender;
   22348 	int parno = 0;
   22349 	int flags;
   22350 
   22351 	*flagp = HASWIDTH;
   22352 
   22353 
   22354 	if (paren) {
   22355 		if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
   22356 
   22357 			preg->regparse += 2;
   22358 			parno = -1;
   22359 		}
   22360 		else {
   22361 			parno = ++preg->re_nsub;
   22362 		}
   22363 		ret = regnode(preg, OPEN+parno);
   22364 	} else
   22365 		ret = 0;
   22366 
   22367 
   22368 	br = regbranch(preg, &flags);
   22369 	if (br == 0)
   22370 		return 0;
   22371 	if (ret != 0)
   22372 		regtail(preg, ret, br);
   22373 	else
   22374 		ret = br;
   22375 	if (!(flags&HASWIDTH))
   22376 		*flagp &= ~HASWIDTH;
   22377 	*flagp |= flags&SPSTART;
   22378 	while (*preg->regparse == '|') {
   22379 		preg->regparse++;
   22380 		br = regbranch(preg, &flags);
   22381 		if (br == 0)
   22382 			return 0;
   22383 		regtail(preg, ret, br);
   22384 		if (!(flags&HASWIDTH))
   22385 			*flagp &= ~HASWIDTH;
   22386 		*flagp |= flags&SPSTART;
   22387 	}
   22388 
   22389 
   22390 	ender = regnode(preg, (paren) ? CLOSE+parno : END);
   22391 	regtail(preg, ret, ender);
   22392 
   22393 
   22394 	for (br = ret; br != 0; br = regnext(preg, br))
   22395 		regoptail(preg, br, ender);
   22396 
   22397 
   22398 	if (paren && *preg->regparse++ != ')') {
   22399 		preg->err = REG_ERR_UNMATCHED_PAREN;
   22400 		return 0;
   22401 	} else if (!paren && *preg->regparse != '\0') {
   22402 		if (*preg->regparse == ')') {
   22403 			preg->err = REG_ERR_UNMATCHED_PAREN;
   22404 			return 0;
   22405 		} else {
   22406 			preg->err = REG_ERR_JUNK_ON_END;
   22407 			return 0;
   22408 		}
   22409 	}
   22410 
   22411 	return(ret);
   22412 }
   22413 
   22414 static int regbranch(regex_t *preg, int *flagp )
   22415 {
   22416 	int ret;
   22417 	int chain;
   22418 	int latest;
   22419 	int flags;
   22420 
   22421 	*flagp = WORST;
   22422 
   22423 	ret = regnode(preg, BRANCH);
   22424 	chain = 0;
   22425 	while (*preg->regparse != '\0' && *preg->regparse != ')' &&
   22426 	       *preg->regparse != '|') {
   22427 		latest = regpiece(preg, &flags);
   22428 		if (latest == 0)
   22429 			return 0;
   22430 		*flagp |= flags&HASWIDTH;
   22431 		if (chain == 0) {
   22432 			*flagp |= flags&SPSTART;
   22433 		}
   22434 		else {
   22435 			regtail(preg, chain, latest);
   22436 		}
   22437 		chain = latest;
   22438 	}
   22439 	if (chain == 0)
   22440 		(void) regnode(preg, NOTHING);
   22441 
   22442 	return(ret);
   22443 }
   22444 
   22445 static int regpiece(regex_t *preg, int *flagp)
   22446 {
   22447 	int ret;
   22448 	char op;
   22449 	int next;
   22450 	int flags;
   22451 	int min;
   22452 	int max;
   22453 
   22454 	ret = regatom(preg, &flags);
   22455 	if (ret == 0)
   22456 		return 0;
   22457 
   22458 	op = *preg->regparse;
   22459 	if (!ISMULT(op)) {
   22460 		*flagp = flags;
   22461 		return(ret);
   22462 	}
   22463 
   22464 	if (!(flags&HASWIDTH) && op != '?') {
   22465 		preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
   22466 		return 0;
   22467 	}
   22468 
   22469 
   22470 	if (op == '{') {
   22471 		char *end;
   22472 
   22473 		min = strtoul(preg->regparse + 1, &end, 10);
   22474 		if (end == preg->regparse + 1) {
   22475 			preg->err = REG_ERR_BAD_COUNT;
   22476 			return 0;
   22477 		}
   22478 		if (*end == '}') {
   22479 			max = min;
   22480 		}
   22481 		else if (*end == '\0') {
   22482 			preg->err = REG_ERR_UNMATCHED_BRACES;
   22483 			return 0;
   22484 		}
   22485 		else {
   22486 			preg->regparse = end;
   22487 			max = strtoul(preg->regparse + 1, &end, 10);
   22488 			if (*end != '}') {
   22489 				preg->err = REG_ERR_UNMATCHED_BRACES;
   22490 				return 0;
   22491 			}
   22492 		}
   22493 		if (end == preg->regparse + 1) {
   22494 			max = MAX_REP_COUNT;
   22495 		}
   22496 		else if (max < min || max >= 100) {
   22497 			preg->err = REG_ERR_BAD_COUNT;
   22498 			return 0;
   22499 		}
   22500 		if (min >= 100) {
   22501 			preg->err = REG_ERR_BAD_COUNT;
   22502 			return 0;
   22503 		}
   22504 
   22505 		preg->regparse = strchr(preg->regparse, '}');
   22506 	}
   22507 	else {
   22508 		min = (op == '+');
   22509 		max = (op == '?' ? 1 : MAX_REP_COUNT);
   22510 	}
   22511 
   22512 	if (preg->regparse[1] == '?') {
   22513 		preg->regparse++;
   22514 		next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret);
   22515 	}
   22516 	else {
   22517 		next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret);
   22518 	}
   22519 	preg->program[ret + 2] = max;
   22520 	preg->program[ret + 3] = min;
   22521 	preg->program[ret + 4] = 0;
   22522 
   22523 	*flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART);
   22524 
   22525 	if (!(flags & SIMPLE)) {
   22526 		int back = regnode(preg, BACK);
   22527 		regtail(preg, back, ret);
   22528 		regtail(preg, next, back);
   22529 	}
   22530 
   22531 	preg->regparse++;
   22532 	if (ISMULT(*preg->regparse)) {
   22533 		preg->err = REG_ERR_NESTED_COUNT;
   22534 		return 0;
   22535 	}
   22536 
   22537 	return ret;
   22538 }
   22539 
   22540 static void reg_addrange(regex_t *preg, int lower, int upper)
   22541 {
   22542 	if (lower > upper) {
   22543 		reg_addrange(preg, upper, lower);
   22544 	}
   22545 
   22546 	regc(preg, upper - lower + 1);
   22547 	regc(preg, lower);
   22548 }
   22549 
   22550 static void reg_addrange_str(regex_t *preg, const char *str)
   22551 {
   22552 	while (*str) {
   22553 		reg_addrange(preg, *str, *str);
   22554 		str++;
   22555 	}
   22556 }
   22557 
   22558 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper)
   22559 {
   22560 	int l = utf8_tounicode(s, uc);
   22561 	if (upper) {
   22562 		*uc = utf8_upper(*uc);
   22563 	}
   22564 	return l;
   22565 }
   22566 
   22567 static int hexdigitval(int c)
   22568 {
   22569 	if (c >= '0' && c <= '9')
   22570 		return c - '0';
   22571 	if (c >= 'a' && c <= 'f')
   22572 		return c - 'a' + 10;
   22573 	if (c >= 'A' && c <= 'F')
   22574 		return c - 'A' + 10;
   22575 	return -1;
   22576 }
   22577 
   22578 static int parse_hex(const char *s, int n, int *uc)
   22579 {
   22580 	int val = 0;
   22581 	int k;
   22582 
   22583 	for (k = 0; k < n; k++) {
   22584 		int c = hexdigitval(*s++);
   22585 		if (c == -1) {
   22586 			break;
   22587 		}
   22588 		val = (val << 4) | c;
   22589 	}
   22590 	if (k) {
   22591 		*uc = val;
   22592 	}
   22593 	return k;
   22594 }
   22595 
   22596 static int reg_decode_escape(const char *s, int *ch)
   22597 {
   22598 	int n;
   22599 	const char *s0 = s;
   22600 
   22601 	*ch = *s++;
   22602 
   22603 	switch (*ch) {
   22604 		case 'b': *ch = '\b'; break;
   22605 		case 'e': *ch = 27; break;
   22606 		case 'f': *ch = '\f'; break;
   22607 		case 'n': *ch = '\n'; break;
   22608 		case 'r': *ch = '\r'; break;
   22609 		case 't': *ch = '\t'; break;
   22610 		case 'v': *ch = '\v'; break;
   22611 		case 'u':
   22612 			if (*s == '{') {
   22613 
   22614 				n = parse_hex(s + 1, 6, ch);
   22615 				if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
   22616 					s += n + 2;
   22617 				}
   22618 				else {
   22619 
   22620 					*ch = 'u';
   22621 				}
   22622 			}
   22623 			else if ((n = parse_hex(s, 4, ch)) > 0) {
   22624 				s += n;
   22625 			}
   22626 			break;
   22627 		case 'U':
   22628 			if ((n = parse_hex(s, 8, ch)) > 0) {
   22629 				s += n;
   22630 			}
   22631 			break;
   22632 		case 'x':
   22633 			if ((n = parse_hex(s, 2, ch)) > 0) {
   22634 				s += n;
   22635 			}
   22636 			break;
   22637 		case '\0':
   22638 			s--;
   22639 			*ch = '\\';
   22640 			break;
   22641 	}
   22642 	return s - s0;
   22643 }
   22644 
   22645 static int regatom(regex_t *preg, int *flagp)
   22646 {
   22647 	int ret;
   22648 	int flags;
   22649 	int nocase = (preg->cflags & REG_ICASE);
   22650 
   22651 	int ch;
   22652 	int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);
   22653 
   22654 	*flagp = WORST;
   22655 
   22656 	preg->regparse += n;
   22657 	switch (ch) {
   22658 
   22659 	case '^':
   22660 		ret = regnode(preg, BOL);
   22661 		break;
   22662 	case '$':
   22663 		ret = regnode(preg, EOL);
   22664 		break;
   22665 	case '.':
   22666 		ret = regnode(preg, ANY);
   22667 		*flagp |= HASWIDTH|SIMPLE;
   22668 		break;
   22669 	case '[': {
   22670 			const char *pattern = preg->regparse;
   22671 
   22672 			if (*pattern == '^') {
   22673 				ret = regnode(preg, ANYBUT);
   22674 				pattern++;
   22675 			} else
   22676 				ret = regnode(preg, ANYOF);
   22677 
   22678 
   22679 			if (*pattern == ']' || *pattern == '-') {
   22680 				reg_addrange(preg, *pattern, *pattern);
   22681 				pattern++;
   22682 			}
   22683 
   22684 			while (*pattern != ']') {
   22685 
   22686 				int start;
   22687 				int end;
   22688 
   22689 				enum {
   22690 					CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
   22691 					CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
   22692 					CC_NUM
   22693 				};
   22694 				int cc;
   22695 
   22696 				if (!*pattern) {
   22697 					preg->err = REG_ERR_UNMATCHED_BRACKET;
   22698 					return 0;
   22699 				}
   22700 
   22701 				pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
   22702 				if (start == '\\') {
   22703 
   22704 					switch (*pattern) {
   22705 						case 's':
   22706 							pattern++;
   22707 							cc = CC_SPACE;
   22708 							goto cc_switch;
   22709 						case 'd':
   22710 							pattern++;
   22711 							cc = CC_DIGIT;
   22712 							goto cc_switch;
   22713 						case 'w':
   22714 							pattern++;
   22715 							reg_addrange(preg, '_', '_');
   22716 							cc = CC_ALNUM;
   22717 							goto cc_switch;
   22718 					}
   22719 					pattern += reg_decode_escape(pattern, &start);
   22720 					if (start == 0) {
   22721 						preg->err = REG_ERR_NULL_CHAR;
   22722 						return 0;
   22723 					}
   22724 					if (start == '\\' && *pattern == 0) {
   22725 						preg->err = REG_ERR_INVALID_ESCAPE;
   22726 						return 0;
   22727 					}
   22728 				}
   22729 				if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
   22730 
   22731 					pattern += utf8_tounicode(pattern, &end);
   22732 					pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
   22733 					if (end == '\\') {
   22734 						pattern += reg_decode_escape(pattern, &end);
   22735 						if (end == 0) {
   22736 							preg->err = REG_ERR_NULL_CHAR;
   22737 							return 0;
   22738 						}
   22739 						if (end == '\\' && *pattern == 0) {
   22740 							preg->err = REG_ERR_INVALID_ESCAPE;
   22741 							return 0;
   22742 						}
   22743 					}
   22744 
   22745 					reg_addrange(preg, start, end);
   22746 					continue;
   22747 				}
   22748 				if (start == '[' && pattern[0] == ':') {
   22749 					static const char *character_class[] = {
   22750 						":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
   22751 						":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
   22752 					};
   22753 
   22754 					for (cc = 0; cc < CC_NUM; cc++) {
   22755 						n = strlen(character_class[cc]);
   22756 						if (strncmp(pattern, character_class[cc], n) == 0) {
   22757 							if (pattern[n] != ']') {
   22758 								preg->err = REG_ERR_UNMATCHED_BRACKET;
   22759 								return 0;
   22760 							}
   22761 
   22762 							pattern += n + 1;
   22763 							break;
   22764 						}
   22765 					}
   22766 					if (cc != CC_NUM) {
   22767 cc_switch:
   22768 						switch (cc) {
   22769 							case CC_ALNUM:
   22770 								reg_addrange(preg, '0', '9');
   22771 
   22772 							case CC_ALPHA:
   22773 								if ((preg->cflags & REG_ICASE) == 0) {
   22774 									reg_addrange(preg, 'a', 'z');
   22775 								}
   22776 								reg_addrange(preg, 'A', 'Z');
   22777 								break;
   22778 							case CC_SPACE:
   22779 								reg_addrange_str(preg, " \t\r\n\f\v");
   22780 								break;
   22781 							case CC_BLANK:
   22782 								reg_addrange_str(preg, " \t");
   22783 								break;
   22784 							case CC_UPPER:
   22785 								reg_addrange(preg, 'A', 'Z');
   22786 								break;
   22787 							case CC_LOWER:
   22788 								reg_addrange(preg, 'a', 'z');
   22789 								break;
   22790 							case CC_XDIGIT:
   22791 								reg_addrange(preg, 'a', 'f');
   22792 								reg_addrange(preg, 'A', 'F');
   22793 
   22794 							case CC_DIGIT:
   22795 								reg_addrange(preg, '0', '9');
   22796 								break;
   22797 							case CC_CNTRL:
   22798 								reg_addrange(preg, 0, 31);
   22799 								reg_addrange(preg, 127, 127);
   22800 								break;
   22801 							case CC_PRINT:
   22802 								reg_addrange(preg, ' ', '~');
   22803 								break;
   22804 							case CC_GRAPH:
   22805 								reg_addrange(preg, '!', '~');
   22806 								break;
   22807 							case CC_PUNCT:
   22808 								reg_addrange(preg, '!', '/');
   22809 								reg_addrange(preg, ':', '@');
   22810 								reg_addrange(preg, '[', '`');
   22811 								reg_addrange(preg, '{', '~');
   22812 								break;
   22813 						}
   22814 						continue;
   22815 					}
   22816 				}
   22817 
   22818 				reg_addrange(preg, start, start);
   22819 			}
   22820 			regc(preg, '\0');
   22821 
   22822 			if (*pattern) {
   22823 				pattern++;
   22824 			}
   22825 			preg->regparse = pattern;
   22826 
   22827 			*flagp |= HASWIDTH|SIMPLE;
   22828 		}
   22829 		break;
   22830 	case '(':
   22831 		ret = reg(preg, 1, &flags);
   22832 		if (ret == 0)
   22833 			return 0;
   22834 		*flagp |= flags&(HASWIDTH|SPSTART);
   22835 		break;
   22836 	case '\0':
   22837 	case '|':
   22838 	case ')':
   22839 		preg->err = REG_ERR_INTERNAL;
   22840 		return 0;
   22841 	case '?':
   22842 	case '+':
   22843 	case '*':
   22844 	case '{':
   22845 		preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
   22846 		return 0;
   22847 	case '\\':
   22848 		ch = *preg->regparse++;
   22849 		switch (ch) {
   22850 		case '\0':
   22851 			preg->err = REG_ERR_INVALID_ESCAPE;
   22852 			return 0;
   22853 		case 'A':
   22854 			ret = regnode(preg, BOLX);
   22855 			break;
   22856 		case 'Z':
   22857 			ret = regnode(preg, EOLX);
   22858 			break;
   22859 		case '<':
   22860 		case 'm':
   22861 			ret = regnode(preg, WORDA);
   22862 			break;
   22863 		case '>':
   22864 		case 'M':
   22865 			ret = regnode(preg, WORDZ);
   22866 			break;
   22867 		case 'd':
   22868 		case 'D':
   22869 			ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT);
   22870 			reg_addrange(preg, '0', '9');
   22871 			regc(preg, '\0');
   22872 			*flagp |= HASWIDTH|SIMPLE;
   22873 			break;
   22874 		case 'w':
   22875 		case 'W':
   22876 			ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT);
   22877 			if ((preg->cflags & REG_ICASE) == 0) {
   22878 				reg_addrange(preg, 'a', 'z');
   22879 			}
   22880 			reg_addrange(preg, 'A', 'Z');
   22881 			reg_addrange(preg, '0', '9');
   22882 			reg_addrange(preg, '_', '_');
   22883 			regc(preg, '\0');
   22884 			*flagp |= HASWIDTH|SIMPLE;
   22885 			break;
   22886 		case 's':
   22887 		case 'S':
   22888 			ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT);
   22889 			reg_addrange_str(preg," \t\r\n\f\v");
   22890 			regc(preg, '\0');
   22891 			*flagp |= HASWIDTH|SIMPLE;
   22892 			break;
   22893 
   22894 		default:
   22895 
   22896 
   22897 			preg->regparse--;
   22898 			goto de_fault;
   22899 		}
   22900 		break;
   22901 	de_fault:
   22902 	default: {
   22903 			int added = 0;
   22904 
   22905 
   22906 			preg->regparse -= n;
   22907 
   22908 			ret = regnode(preg, EXACTLY);
   22909 
   22910 
   22911 
   22912 			while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
   22913 				n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
   22914 				if (ch == '\\' && preg->regparse[n]) {
   22915 					if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) {
   22916 
   22917 						break;
   22918 					}
   22919 					n += reg_decode_escape(preg->regparse + n, &ch);
   22920 					if (ch == 0) {
   22921 						preg->err = REG_ERR_NULL_CHAR;
   22922 						return 0;
   22923 					}
   22924 				}
   22925 
   22926 
   22927 				if (ISMULT(preg->regparse[n])) {
   22928 
   22929 					if (added) {
   22930 
   22931 						break;
   22932 					}
   22933 
   22934 					regc(preg, ch);
   22935 					added++;
   22936 					preg->regparse += n;
   22937 					break;
   22938 				}
   22939 
   22940 
   22941 				regc(preg, ch);
   22942 				added++;
   22943 				preg->regparse += n;
   22944 			}
   22945 			regc(preg, '\0');
   22946 
   22947 			*flagp |= HASWIDTH;
   22948 			if (added == 1)
   22949 				*flagp |= SIMPLE;
   22950 			break;
   22951 		}
   22952 		break;
   22953 	}
   22954 
   22955 	return(ret);
   22956 }
   22957 
   22958 static void reg_grow(regex_t *preg, int n)
   22959 {
   22960 	if (preg->p + n >= preg->proglen) {
   22961 		preg->proglen = (preg->p + n) * 2;
   22962 		preg->program = realloc(preg->program, preg->proglen * sizeof(int));
   22963 	}
   22964 }
   22965 
   22966 
   22967 static int regnode(regex_t *preg, int op)
   22968 {
   22969 	reg_grow(preg, 2);
   22970 
   22971 
   22972 	preg->program[preg->p++] = op;
   22973 	preg->program[preg->p++] = 0;
   22974 
   22975 
   22976 	return preg->p - 2;
   22977 }
   22978 
   22979 static void regc(regex_t *preg, int b )
   22980 {
   22981 	reg_grow(preg, 1);
   22982 	preg->program[preg->p++] = b;
   22983 }
   22984 
   22985 static int reginsert(regex_t *preg, int op, int size, int opnd )
   22986 {
   22987 	reg_grow(preg, size);
   22988 
   22989 
   22990 	memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
   22991 
   22992 	memset(preg->program + opnd, 0, sizeof(int) * size);
   22993 
   22994 	preg->program[opnd] = op;
   22995 
   22996 	preg->p += size;
   22997 
   22998 	return opnd + size;
   22999 }
   23000 
   23001 static void regtail(regex_t *preg, int p, int val)
   23002 {
   23003 	int scan;
   23004 	int temp;
   23005 	int offset;
   23006 
   23007 
   23008 	scan = p;
   23009 	for (;;) {
   23010 		temp = regnext(preg, scan);
   23011 		if (temp == 0)
   23012 			break;
   23013 		scan = temp;
   23014 	}
   23015 
   23016 	if (OP(preg, scan) == BACK)
   23017 		offset = scan - val;
   23018 	else
   23019 		offset = val - scan;
   23020 
   23021 	preg->program[scan + 1] = offset;
   23022 }
   23023 
   23024 
   23025 static void regoptail(regex_t *preg, int p, int val )
   23026 {
   23027 
   23028 	if (p != 0 && OP(preg, p) == BRANCH) {
   23029 		regtail(preg, OPERAND(p), val);
   23030 	}
   23031 }
   23032 
   23033 
   23034 static int regtry(regex_t *preg, const char *string );
   23035 static int regmatch(regex_t *preg, int prog);
   23036 static int regrepeat(regex_t *preg, int p, int max);
   23037 
   23038 int jim_regexec(regex_t  *preg,  const  char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
   23039 {
   23040 	const char *s;
   23041 	int scan;
   23042 
   23043 
   23044 	if (preg == NULL || preg->program == NULL || string == NULL) {
   23045 		return REG_ERR_NULL_ARGUMENT;
   23046 	}
   23047 
   23048 
   23049 	if (*preg->program != REG_MAGIC) {
   23050 		return REG_ERR_CORRUPTED;
   23051 	}
   23052 
   23053 #ifdef DEBUG
   23054 	fprintf(stderr, "regexec: %s\n", string);
   23055 	regdump(preg);
   23056 #endif
   23057 
   23058 	preg->eflags = eflags;
   23059 	preg->pmatch = pmatch;
   23060 	preg->nmatch = nmatch;
   23061 	preg->start = string;
   23062 
   23063 
   23064 	for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) {
   23065 		int op = OP(preg, scan);
   23066 		if (op == END)
   23067 			break;
   23068 		if (op == REPX || op == REPXMIN)
   23069 			preg->program[scan + 4] = 0;
   23070 	}
   23071 
   23072 
   23073 	if (preg->regmust != 0) {
   23074 		s = string;
   23075 		while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
   23076 			if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
   23077 				break;
   23078 			}
   23079 			s++;
   23080 		}
   23081 		if (s == NULL)
   23082 			return REG_NOMATCH;
   23083 	}
   23084 
   23085 
   23086 	preg->regbol = string;
   23087 
   23088 
   23089 	if (preg->reganch) {
   23090 		if (eflags & REG_NOTBOL) {
   23091 
   23092 			goto nextline;
   23093 		}
   23094 		while (1) {
   23095 			if (regtry(preg, string)) {
   23096 				return REG_NOERROR;
   23097 			}
   23098 			if (*string) {
   23099 nextline:
   23100 				if (preg->cflags & REG_NEWLINE) {
   23101 
   23102 					string = strchr(string, '\n');
   23103 					if (string) {
   23104 						preg->regbol = ++string;
   23105 						continue;
   23106 					}
   23107 				}
   23108 			}
   23109 			return REG_NOMATCH;
   23110 		}
   23111 	}
   23112 
   23113 
   23114 	s = string;
   23115 	if (preg->regstart != '\0') {
   23116 
   23117 		while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
   23118 			if (regtry(preg, s))
   23119 				return REG_NOERROR;
   23120 			s++;
   23121 		}
   23122 	}
   23123 	else
   23124 
   23125 		while (1) {
   23126 			if (regtry(preg, s))
   23127 				return REG_NOERROR;
   23128 			if (*s == '\0') {
   23129 				break;
   23130 			}
   23131 			else {
   23132 				int c;
   23133 				s += utf8_tounicode(s, &c);
   23134 			}
   23135 		}
   23136 
   23137 
   23138 	return REG_NOMATCH;
   23139 }
   23140 
   23141 
   23142 static int regtry( regex_t *preg, const char *string )
   23143 {
   23144 	int i;
   23145 
   23146 	preg->reginput = string;
   23147 
   23148 	for (i = 0; i < preg->nmatch; i++) {
   23149 		preg->pmatch[i].rm_so = -1;
   23150 		preg->pmatch[i].rm_eo = -1;
   23151 	}
   23152 	if (regmatch(preg, 1)) {
   23153 		preg->pmatch[0].rm_so = string - preg->start;
   23154 		preg->pmatch[0].rm_eo = preg->reginput - preg->start;
   23155 		return(1);
   23156 	} else
   23157 		return(0);
   23158 }
   23159 
   23160 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase)
   23161 {
   23162 	const char *s = string;
   23163 	while (proglen && *s) {
   23164 		int ch;
   23165 		int n = reg_utf8_tounicode_case(s, &ch, nocase);
   23166 		if (ch != *prog) {
   23167 			return -1;
   23168 		}
   23169 		prog++;
   23170 		s += n;
   23171 		proglen--;
   23172 	}
   23173 	if (proglen == 0) {
   23174 		return s - string;
   23175 	}
   23176 	return -1;
   23177 }
   23178 
   23179 static int reg_range_find(const int *range, int c)
   23180 {
   23181 	while (*range) {
   23182 
   23183 		if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
   23184 			return 1;
   23185 		}
   23186 		range += 2;
   23187 	}
   23188 	return 0;
   23189 }
   23190 
   23191 static const char *str_find(const char *string, int c, int nocase)
   23192 {
   23193 	if (nocase) {
   23194 
   23195 		c = utf8_upper(c);
   23196 	}
   23197 	while (*string) {
   23198 		int ch;
   23199 		int n = reg_utf8_tounicode_case(string, &ch, nocase);
   23200 		if (c == ch) {
   23201 			return string;
   23202 		}
   23203 		string += n;
   23204 	}
   23205 	return NULL;
   23206 }
   23207 
   23208 static int reg_iseol(regex_t *preg, int ch)
   23209 {
   23210 	if (preg->cflags & REG_NEWLINE) {
   23211 		return ch == '\0' || ch == '\n';
   23212 	}
   23213 	else {
   23214 		return ch == '\0';
   23215 	}
   23216 }
   23217 
   23218 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin)
   23219 {
   23220 	int nextch = '\0';
   23221 	const char *save;
   23222 	int no;
   23223 	int c;
   23224 
   23225 	int max = preg->program[scan + 2];
   23226 	int min = preg->program[scan + 3];
   23227 	int next = regnext(preg, scan);
   23228 
   23229 	if (OP(preg, next) == EXACTLY) {
   23230 		nextch = preg->program[OPERAND(next)];
   23231 	}
   23232 	save = preg->reginput;
   23233 	no = regrepeat(preg, scan + 5, max);
   23234 	if (no < min) {
   23235 		return 0;
   23236 	}
   23237 	if (matchmin) {
   23238 
   23239 		max = no;
   23240 		no = min;
   23241 	}
   23242 
   23243 	while (1) {
   23244 		if (matchmin) {
   23245 			if (no > max) {
   23246 				break;
   23247 			}
   23248 		}
   23249 		else {
   23250 			if (no < min) {
   23251 				break;
   23252 			}
   23253 		}
   23254 		preg->reginput = save + utf8_index(save, no);
   23255 		reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
   23256 
   23257 		if (reg_iseol(preg, nextch) || c == nextch) {
   23258 			if (regmatch(preg, next)) {
   23259 				return(1);
   23260 			}
   23261 		}
   23262 		if (matchmin) {
   23263 
   23264 			no++;
   23265 		}
   23266 		else {
   23267 
   23268 			no--;
   23269 		}
   23270 	}
   23271 	return(0);
   23272 }
   23273 
   23274 static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
   23275 {
   23276 	int *scanpt = preg->program + scan;
   23277 
   23278 	int max = scanpt[2];
   23279 	int min = scanpt[3];
   23280 
   23281 
   23282 	if (scanpt[4] < min) {
   23283 
   23284 		scanpt[4]++;
   23285 		if (regmatch(preg, scan + 5)) {
   23286 			return 1;
   23287 		}
   23288 		scanpt[4]--;
   23289 		return 0;
   23290 	}
   23291 	if (scanpt[4] > max) {
   23292 		return 0;
   23293 	}
   23294 
   23295 	if (matchmin) {
   23296 
   23297 		if (regmatch(preg, regnext(preg, scan))) {
   23298 			return 1;
   23299 		}
   23300 
   23301 		scanpt[4]++;
   23302 		if (regmatch(preg, scan + 5)) {
   23303 			return 1;
   23304 		}
   23305 		scanpt[4]--;
   23306 		return 0;
   23307 	}
   23308 
   23309 	if (scanpt[4] < max) {
   23310 		scanpt[4]++;
   23311 		if (regmatch(preg, scan + 5)) {
   23312 			return 1;
   23313 		}
   23314 		scanpt[4]--;
   23315 	}
   23316 
   23317 	return regmatch(preg, regnext(preg, scan));
   23318 }
   23319 
   23320 
   23321 static int regmatch(regex_t *preg, int prog)
   23322 {
   23323 	int scan;
   23324 	int next;
   23325 	const char *save;
   23326 
   23327 	scan = prog;
   23328 
   23329 #ifdef DEBUG
   23330 	if (scan != 0 && regnarrate)
   23331 		fprintf(stderr, "%s(\n", regprop(scan));
   23332 #endif
   23333 	while (scan != 0) {
   23334 		int n;
   23335 		int c;
   23336 #ifdef DEBUG
   23337 		if (regnarrate) {
   23338 			fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
   23339 		}
   23340 #endif
   23341 		next = regnext(preg, scan);
   23342 		n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
   23343 
   23344 		switch (OP(preg, scan)) {
   23345 		case BOLX:
   23346 			if ((preg->eflags & REG_NOTBOL)) {
   23347 				return(0);
   23348 			}
   23349 
   23350 		case BOL:
   23351 			if (preg->reginput != preg->regbol) {
   23352 				return(0);
   23353 			}
   23354 			break;
   23355 		case EOLX:
   23356 			if (c != 0) {
   23357 
   23358 				return 0;
   23359 			}
   23360 			break;
   23361 		case EOL:
   23362 			if (!reg_iseol(preg, c)) {
   23363 				return(0);
   23364 			}
   23365 			break;
   23366 		case WORDA:
   23367 
   23368 			if ((!isalnum(UCHAR(c))) && c != '_')
   23369 				return(0);
   23370 
   23371 			if (preg->reginput > preg->regbol &&
   23372 				(isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
   23373 				return(0);
   23374 			break;
   23375 		case WORDZ:
   23376 
   23377 			if (preg->reginput > preg->regbol) {
   23378 
   23379 				if (reg_iseol(preg, c) || !(isalnum(UCHAR(c)) || c == '_')) {
   23380 					c = preg->reginput[-1];
   23381 
   23382 					if (isalnum(UCHAR(c)) || c == '_') {
   23383 						break;
   23384 					}
   23385 				}
   23386 			}
   23387 
   23388 			return(0);
   23389 
   23390 		case ANY:
   23391 			if (reg_iseol(preg, c))
   23392 				return 0;
   23393 			preg->reginput += n;
   23394 			break;
   23395 		case EXACTLY: {
   23396 				int opnd;
   23397 				int len;
   23398 				int slen;
   23399 
   23400 				opnd = OPERAND(scan);
   23401 				len = str_int_len(preg->program + opnd);
   23402 
   23403 				slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE);
   23404 				if (slen < 0) {
   23405 					return(0);
   23406 				}
   23407 				preg->reginput += slen;
   23408 			}
   23409 			break;
   23410 		case ANYOF:
   23411 			if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) {
   23412 				return(0);
   23413 			}
   23414 			preg->reginput += n;
   23415 			break;
   23416 		case ANYBUT:
   23417 			if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) {
   23418 				return(0);
   23419 			}
   23420 			preg->reginput += n;
   23421 			break;
   23422 		case NOTHING:
   23423 			break;
   23424 		case BACK:
   23425 			break;
   23426 		case BRANCH:
   23427 			if (OP(preg, next) != BRANCH)
   23428 				next = OPERAND(scan);
   23429 			else {
   23430 				do {
   23431 					save = preg->reginput;
   23432 					if (regmatch(preg, OPERAND(scan))) {
   23433 						return(1);
   23434 					}
   23435 					preg->reginput = save;
   23436 					scan = regnext(preg, scan);
   23437 				} while (scan != 0 && OP(preg, scan) == BRANCH);
   23438 				return(0);
   23439 
   23440 			}
   23441 			break;
   23442 		case REP:
   23443 		case REPMIN:
   23444 			return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);
   23445 
   23446 		case REPX:
   23447 		case REPXMIN:
   23448 			return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);
   23449 
   23450 		case END:
   23451 			return 1;
   23452 
   23453 		case OPENNC:
   23454 		case CLOSENC:
   23455 			return regmatch(preg, next);
   23456 
   23457 		default:
   23458 			if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
   23459 				save = preg->reginput;
   23460 				if (regmatch(preg, next)) {
   23461 					if (OP(preg, scan) < CLOSE) {
   23462 						int no = OP(preg, scan) - OPEN;
   23463 						if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
   23464 							preg->pmatch[no].rm_so = save - preg->start;
   23465 						}
   23466 					}
   23467 					else {
   23468 						int no = OP(preg, scan) - CLOSE;
   23469 						if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
   23470 							preg->pmatch[no].rm_eo = save - preg->start;
   23471 						}
   23472 					}
   23473 					return(1);
   23474 				}
   23475 
   23476 				preg->reginput = save;
   23477 				return(0);
   23478 			}
   23479 			return REG_ERR_INTERNAL;
   23480 		}
   23481 
   23482 		scan = next;
   23483 	}
   23484 
   23485 	return REG_ERR_INTERNAL;
   23486 }
   23487 
   23488 static int regrepeat(regex_t *preg, int p, int max)
   23489 {
   23490 	int count = 0;
   23491 	const char *scan;
   23492 	int opnd;
   23493 	int ch;
   23494 	int n;
   23495 
   23496 	scan = preg->reginput;
   23497 	opnd = OPERAND(p);
   23498 	switch (OP(preg, p)) {
   23499 	case ANY:
   23500 		while (!reg_iseol(preg, *scan) && count < max) {
   23501 			count++;
   23502 			scan += utf8_charlen(*scan);
   23503 		}
   23504 		break;
   23505 	case EXACTLY:
   23506 		while (count < max) {
   23507 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
   23508 			if (preg->program[opnd] != ch) {
   23509 				break;
   23510 			}
   23511 			count++;
   23512 			scan += n;
   23513 		}
   23514 		break;
   23515 	case ANYOF:
   23516 		while (count < max) {
   23517 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
   23518 			if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) {
   23519 				break;
   23520 			}
   23521 			count++;
   23522 			scan += n;
   23523 		}
   23524 		break;
   23525 	case ANYBUT:
   23526 		while (count < max) {
   23527 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
   23528 			if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
   23529 				break;
   23530 			}
   23531 			count++;
   23532 			scan += n;
   23533 		}
   23534 		break;
   23535 	default:
   23536 		preg->err = REG_ERR_INTERNAL;
   23537 		count = 0;
   23538 		break;
   23539 	}
   23540 	preg->reginput = scan;
   23541 
   23542 	return(count);
   23543 }
   23544 
   23545 static int regnext(regex_t *preg, int p )
   23546 {
   23547 	int offset;
   23548 
   23549 	offset = NEXT(preg, p);
   23550 
   23551 	if (offset == 0)
   23552 		return 0;
   23553 
   23554 	if (OP(preg, p) == BACK)
   23555 		return(p-offset);
   23556 	else
   23557 		return(p+offset);
   23558 }
   23559 
   23560 static int regopsize(regex_t *preg, int p )
   23561 {
   23562 
   23563 	switch (OP(preg, p)) {
   23564 		case REP:
   23565 		case REPMIN:
   23566 		case REPX:
   23567 		case REPXMIN:
   23568 			return 5;
   23569 
   23570 		case ANYOF:
   23571 		case ANYBUT:
   23572 		case EXACTLY: {
   23573 			int s = p + 2;
   23574 			while (preg->program[s++]) {
   23575 			}
   23576 			return s - p;
   23577 		}
   23578 	}
   23579 	return 2;
   23580 }
   23581 
   23582 
   23583 size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf,  size_t errbuf_size)
   23584 {
   23585 	static const char *error_strings[] = {
   23586 		"success",
   23587 		"no match",
   23588 		"bad pattern",
   23589 		"null argument",
   23590 		"unknown error",
   23591 		"too big",
   23592 		"out of memory",
   23593 		"too many ()",
   23594 		"parentheses () not balanced",
   23595 		"braces {} not balanced",
   23596 		"invalid repetition count(s)",
   23597 		"extra characters",
   23598 		"*+ of empty atom",
   23599 		"nested count",
   23600 		"internal error",
   23601 		"count follows nothing",
   23602 		"invalid escape \\ sequence",
   23603 		"corrupted program",
   23604 		"contains null char",
   23605 		"brackets [] not balanced",
   23606 	};
   23607 	const char *err;
   23608 
   23609 	if (errcode < 0 || errcode >= REG_ERR_NUM) {
   23610 		err = "Bad error code";
   23611 	}
   23612 	else {
   23613 		err = error_strings[errcode];
   23614 	}
   23615 
   23616 	return snprintf(errbuf, errbuf_size, "%s", err);
   23617 }
   23618 
   23619 void jim_regfree(regex_t *preg)
   23620 {
   23621 	free(preg->program);
   23622 }
   23623 
   23624 #endif
   23625 #include <string.h>
   23626 
   23627 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
   23628 {
   23629     Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno()));
   23630 }
   23631 
   23632 #if defined(_WIN32) || defined(WIN32)
   23633 #include <sys/stat.h>
   23634 
   23635 int Jim_Errno(void)
   23636 {
   23637     switch (GetLastError()) {
   23638     case ERROR_FILE_NOT_FOUND: return ENOENT;
   23639     case ERROR_PATH_NOT_FOUND: return ENOENT;
   23640     case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
   23641     case ERROR_ACCESS_DENIED: return EACCES;
   23642     case ERROR_INVALID_HANDLE: return EBADF;
   23643     case ERROR_BAD_ENVIRONMENT: return E2BIG;
   23644     case ERROR_BAD_FORMAT: return ENOEXEC;
   23645     case ERROR_INVALID_ACCESS: return EACCES;
   23646     case ERROR_INVALID_DRIVE: return ENOENT;
   23647     case ERROR_CURRENT_DIRECTORY: return EACCES;
   23648     case ERROR_NOT_SAME_DEVICE: return EXDEV;
   23649     case ERROR_NO_MORE_FILES: return ENOENT;
   23650     case ERROR_WRITE_PROTECT: return EROFS;
   23651     case ERROR_BAD_UNIT: return ENXIO;
   23652     case ERROR_NOT_READY: return EBUSY;
   23653     case ERROR_BAD_COMMAND: return EIO;
   23654     case ERROR_CRC: return EIO;
   23655     case ERROR_BAD_LENGTH: return EIO;
   23656     case ERROR_SEEK: return EIO;
   23657     case ERROR_WRITE_FAULT: return EIO;
   23658     case ERROR_READ_FAULT: return EIO;
   23659     case ERROR_GEN_FAILURE: return EIO;
   23660     case ERROR_SHARING_VIOLATION: return EACCES;
   23661     case ERROR_LOCK_VIOLATION: return EACCES;
   23662     case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
   23663     case ERROR_HANDLE_DISK_FULL: return ENOSPC;
   23664     case ERROR_NOT_SUPPORTED: return ENODEV;
   23665     case ERROR_REM_NOT_LIST: return EBUSY;
   23666     case ERROR_DUP_NAME: return EEXIST;
   23667     case ERROR_BAD_NETPATH: return ENOENT;
   23668     case ERROR_NETWORK_BUSY: return EBUSY;
   23669     case ERROR_DEV_NOT_EXIST: return ENODEV;
   23670     case ERROR_TOO_MANY_CMDS: return EAGAIN;
   23671     case ERROR_ADAP_HDW_ERR: return EIO;
   23672     case ERROR_BAD_NET_RESP: return EIO;
   23673     case ERROR_UNEXP_NET_ERR: return EIO;
   23674     case ERROR_NETNAME_DELETED: return ENOENT;
   23675     case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
   23676     case ERROR_BAD_DEV_TYPE: return ENODEV;
   23677     case ERROR_BAD_NET_NAME: return ENOENT;
   23678     case ERROR_TOO_MANY_NAMES: return ENFILE;
   23679     case ERROR_TOO_MANY_SESS: return EIO;
   23680     case ERROR_SHARING_PAUSED: return EAGAIN;
   23681     case ERROR_REDIR_PAUSED: return EAGAIN;
   23682     case ERROR_FILE_EXISTS: return EEXIST;
   23683     case ERROR_CANNOT_MAKE: return ENOSPC;
   23684     case ERROR_OUT_OF_STRUCTURES: return ENFILE;
   23685     case ERROR_ALREADY_ASSIGNED: return EEXIST;
   23686     case ERROR_INVALID_PASSWORD: return EPERM;
   23687     case ERROR_NET_WRITE_FAULT: return EIO;
   23688     case ERROR_NO_PROC_SLOTS: return EAGAIN;
   23689     case ERROR_DISK_CHANGE: return EXDEV;
   23690     case ERROR_BROKEN_PIPE: return EPIPE;
   23691     case ERROR_OPEN_FAILED: return ENOENT;
   23692     case ERROR_DISK_FULL: return ENOSPC;
   23693     case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
   23694     case ERROR_INVALID_TARGET_HANDLE: return EBADF;
   23695     case ERROR_INVALID_NAME: return ENOENT;
   23696     case ERROR_PROC_NOT_FOUND: return ESRCH;
   23697     case ERROR_WAIT_NO_CHILDREN: return ECHILD;
   23698     case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
   23699     case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
   23700     case ERROR_SEEK_ON_DEVICE: return ESPIPE;
   23701     case ERROR_BUSY_DRIVE: return EAGAIN;
   23702     case ERROR_DIR_NOT_EMPTY: return EEXIST;
   23703     case ERROR_NOT_LOCKED: return EACCES;
   23704     case ERROR_BAD_PATHNAME: return ENOENT;
   23705     case ERROR_LOCK_FAILED: return EACCES;
   23706     case ERROR_ALREADY_EXISTS: return EEXIST;
   23707     case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
   23708     case ERROR_BAD_PIPE: return EPIPE;
   23709     case ERROR_PIPE_BUSY: return EAGAIN;
   23710     case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
   23711     case ERROR_DIRECTORY: return ENOTDIR;
   23712     }
   23713     return EINVAL;
   23714 }
   23715 
   23716 long JimProcessPid(phandle_t pid)
   23717 {
   23718     if (pid == INVALID_HANDLE_VALUE) {
   23719         return -1;
   23720     }
   23721     return GetProcessId(pid);
   23722 }
   23723 
   23724 phandle_t JimWaitPid(long pid, int *status, int nohang)
   23725 {
   23726     if (pid > 0) {
   23727         HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid);
   23728         if (h) {
   23729             long pid = waitpid(h, status, nohang);
   23730             CloseHandle(h);
   23731             if (pid > 0) {
   23732                 return h;
   23733             }
   23734         }
   23735     }
   23736     return JIM_BAD_PHANDLE;
   23737 }
   23738 
   23739 long waitpid(phandle_t phandle, int *status, int nohang)
   23740 {
   23741     long pid;
   23742     DWORD ret = WaitForSingleObject(phandle, nohang ? 0 : INFINITE);
   23743     if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
   23744 
   23745         return -1;
   23746     }
   23747     GetExitCodeProcess(phandle, &ret);
   23748     *status = ret;
   23749 
   23750     pid = GetProcessId(phandle);
   23751     CloseHandle(phandle);
   23752     return pid;
   23753 }
   23754 
   23755 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
   23756 {
   23757     char name[MAX_PATH];
   23758     HANDLE handle;
   23759 
   23760     if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) {
   23761         return -1;
   23762     }
   23763 
   23764     handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
   23765             CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0),
   23766             NULL);
   23767 
   23768     if (handle == INVALID_HANDLE_VALUE) {
   23769         goto error;
   23770     }
   23771 
   23772     Jim_SetResultString(interp, name, -1);
   23773     return _open_osfhandle((intptr_t)handle, _O_RDWR | _O_TEXT);
   23774 
   23775   error:
   23776     Jim_SetResultErrno(interp, name);
   23777     DeleteFile(name);
   23778     return -1;
   23779 }
   23780 
   23781 int Jim_OpenForWrite(const char *filename, int append)
   23782 {
   23783     if (strcmp(filename, "/dev/null") == 0) {
   23784         filename = "nul:";
   23785     }
   23786     int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE);
   23787     if (fd >= 0 && append) {
   23788 
   23789         _lseek(fd, 0L, SEEK_END);
   23790     }
   23791     return fd;
   23792 }
   23793 
   23794 int Jim_OpenForRead(const char *filename)
   23795 {
   23796     if (strcmp(filename, "/dev/null") == 0) {
   23797         filename = "nul:";
   23798     }
   23799     return _open(filename, _O_RDONLY | _O_TEXT, 0);
   23800 }
   23801 
   23802 #elif defined(HAVE_UNISTD_H)
   23803 
   23804 
   23805 
   23806 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
   23807 {
   23808     int fd;
   23809     mode_t mask;
   23810     Jim_Obj *filenameObj;
   23811 
   23812     if (filename_template == NULL) {
   23813         const char *tmpdir = getenv("TMPDIR");
   23814         if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
   23815             tmpdir = "/tmp/";
   23816         }
   23817         filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
   23818         if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
   23819             Jim_AppendString(interp, filenameObj, "/", 1);
   23820         }
   23821         Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
   23822     }
   23823     else {
   23824         filenameObj = Jim_NewStringObj(interp, filename_template, -1);
   23825     }
   23826 
   23827 
   23828 #ifdef HAVE_UMASK
   23829     mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
   23830 #endif
   23831 #ifdef HAVE_MKSTEMP
   23832     fd = mkstemp(filenameObj->bytes);
   23833 #else
   23834     if (mktemp(filenameObj->bytes) == NULL) {
   23835         fd = -1;
   23836     }
   23837     else {
   23838         fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC);
   23839     }
   23840 #endif
   23841 #ifdef HAVE_UMASK
   23842     umask(mask);
   23843 #endif
   23844     if (fd < 0) {
   23845         Jim_SetResultErrno(interp, Jim_String(filenameObj));
   23846         Jim_FreeNewObj(interp, filenameObj);
   23847         return -1;
   23848     }
   23849     if (unlink_file) {
   23850         remove(Jim_String(filenameObj));
   23851     }
   23852 
   23853     Jim_SetResult(interp, filenameObj);
   23854     return fd;
   23855 }
   23856 
   23857 int Jim_OpenForWrite(const char *filename, int append)
   23858 {
   23859     return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
   23860 }
   23861 
   23862 int Jim_OpenForRead(const char *filename)
   23863 {
   23864     return open(filename, O_RDONLY, 0);
   23865 }
   23866 
   23867 #endif
   23868 
   23869 #if defined(_WIN32) || defined(WIN32)
   23870 #ifndef STRICT
   23871 #define STRICT
   23872 #endif
   23873 #define WIN32_LEAN_AND_MEAN
   23874 #include <windows.h>
   23875 
   23876 #if defined(HAVE_DLOPEN_COMPAT)
   23877 void *dlopen(const char *path, int mode)
   23878 {
   23879     JIM_NOTUSED(mode);
   23880 
   23881     return (void *)LoadLibraryA(path);
   23882 }
   23883 
   23884 int dlclose(void *handle)
   23885 {
   23886     FreeLibrary((HANDLE)handle);
   23887     return 0;
   23888 }
   23889 
   23890 void *dlsym(void *handle, const char *symbol)
   23891 {
   23892     return GetProcAddress((HMODULE)handle, symbol);
   23893 }
   23894 
   23895 char *dlerror(void)
   23896 {
   23897     static char msg[121];
   23898     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
   23899                    LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
   23900     return msg;
   23901 }
   23902 #endif
   23903 
   23904 #ifdef _MSC_VER
   23905 
   23906 #include <sys/timeb.h>
   23907 
   23908 
   23909 int gettimeofday(struct timeval *tv, void *unused)
   23910 {
   23911     struct _timeb tb;
   23912 
   23913     _ftime(&tb);
   23914     tv->tv_sec = tb.time;
   23915     tv->tv_usec = tb.millitm * 1000;
   23916 
   23917     return 0;
   23918 }
   23919 
   23920 
   23921 DIR *opendir(const char *name)
   23922 {
   23923     DIR *dir = 0;
   23924 
   23925     if (name && name[0]) {
   23926         size_t base_length = strlen(name);
   23927         const char *all =
   23928             strchr("/\\", name[base_length - 1]) ? "*" : "/*";
   23929 
   23930         if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
   23931             (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
   23932             strcat(strcpy(dir->name, name), all);
   23933 
   23934             if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
   23935                 dir->result.d_name = 0;
   23936             else {
   23937                 Jim_Free(dir->name);
   23938                 Jim_Free(dir);
   23939                 dir = 0;
   23940             }
   23941         }
   23942         else {
   23943             Jim_Free(dir);
   23944             dir = 0;
   23945             errno = ENOMEM;
   23946         }
   23947     }
   23948     else {
   23949         errno = EINVAL;
   23950     }
   23951     return dir;
   23952 }
   23953 
   23954 int closedir(DIR * dir)
   23955 {
   23956     int result = -1;
   23957 
   23958     if (dir) {
   23959         if (dir->handle != -1)
   23960             result = _findclose(dir->handle);
   23961         Jim_Free(dir->name);
   23962         Jim_Free(dir);
   23963     }
   23964     if (result == -1)
   23965         errno = EBADF;
   23966     return result;
   23967 }
   23968 
   23969 struct dirent *readdir(DIR * dir)
   23970 {
   23971     struct dirent *result = 0;
   23972 
   23973     if (dir && dir->handle != -1) {
   23974         if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
   23975             result = &dir->result;
   23976             result->d_name = dir->info.name;
   23977         }
   23978     }
   23979     else {
   23980         errno = EBADF;
   23981     }
   23982     return result;
   23983 }
   23984 #endif
   23985 #endif
   23986 #include <stdio.h>
   23987 #include <signal.h>
   23988 
   23989 
   23990 
   23991 
   23992 
   23993 
   23994 #ifndef SIGPIPE
   23995 #define SIGPIPE 13
   23996 #endif
   23997 #ifndef SIGINT
   23998 #define SIGINT 2
   23999 #endif
   24000 
   24001 const char *Jim_SignalId(int sig)
   24002 {
   24003 	static char buf[10];
   24004 	switch (sig) {
   24005 		case SIGINT: return "SIGINT";
   24006 		case SIGPIPE: return "SIGPIPE";
   24007 
   24008 	}
   24009 	snprintf(buf, sizeof(buf), "%d", sig);
   24010 	return buf;
   24011 }
   24012 #ifndef JIM_BOOTSTRAP_LIB_ONLY
   24013 #include <errno.h>
   24014 #include <string.h>
   24015 #include <stdio.h>
   24016 
   24017 
   24018 #ifdef USE_LINENOISE
   24019 #ifdef HAVE_UNISTD_H
   24020     #include <unistd.h>
   24021 #endif
   24022 #ifdef HAVE_SYS_STAT_H
   24023     #include <sys/stat.h>
   24024 #endif
   24025 #include "linenoise.h"
   24026 #else
   24027 #define MAX_LINE_LEN 512
   24028 #endif
   24029 
   24030 #ifdef USE_LINENOISE
   24031 struct JimCompletionInfo {
   24032     Jim_Interp *interp;
   24033     Jim_Obj *completion_command;
   24034     Jim_Obj *hints_command;
   24035 
   24036 };
   24037 
   24038 static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp);
   24039 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata);
   24040 static const char completion_callback_assoc_key[] = "interactive-completion";
   24041 static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata);
   24042 static void JimFreeHintsCallback(void *hint, void *userdata);
   24043 #endif
   24044 
   24045 char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
   24046 {
   24047 #ifdef USE_LINENOISE
   24048     struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
   24049     char *result;
   24050     Jim_Obj *objPtr;
   24051     long mlmode = 0;
   24052     if (compinfo->completion_command) {
   24053         linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
   24054     }
   24055     if (compinfo->hints_command) {
   24056         linenoiseSetHintsCallback(JimHintsCallback, compinfo);
   24057         linenoiseSetFreeHintsCallback(JimFreeHintsCallback);
   24058     }
   24059     objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE);
   24060     if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) {
   24061         linenoiseSetMultiLine(mlmode);
   24062     }
   24063 
   24064     result = linenoise(prompt);
   24065 
   24066     linenoiseSetCompletionCallback(NULL, NULL);
   24067     linenoiseSetHintsCallback(NULL, NULL);
   24068     linenoiseSetFreeHintsCallback(NULL);
   24069     return result;
   24070 #else
   24071     int len;
   24072     char *line = Jim_Alloc(MAX_LINE_LEN);
   24073 
   24074     fputs(prompt, stdout);
   24075     fflush(stdout);
   24076 
   24077     if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
   24078         Jim_Free(line);
   24079         return NULL;
   24080     }
   24081     len = strlen(line);
   24082     if (len && line[len - 1] == '\n') {
   24083         line[len - 1] = '\0';
   24084     }
   24085     return line;
   24086 #endif
   24087 }
   24088 
   24089 void Jim_HistoryLoad(const char *filename)
   24090 {
   24091 #ifdef USE_LINENOISE
   24092     linenoiseHistoryLoad(filename);
   24093 #endif
   24094 }
   24095 
   24096 void Jim_HistoryAdd(const char *line)
   24097 {
   24098 #ifdef USE_LINENOISE
   24099     linenoiseHistoryAdd(line);
   24100 #endif
   24101 }
   24102 
   24103 void Jim_HistorySave(const char *filename)
   24104 {
   24105 #ifdef USE_LINENOISE
   24106 #ifdef HAVE_UMASK
   24107     mode_t mask;
   24108 
   24109     mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
   24110 #endif
   24111     linenoiseHistorySave(filename);
   24112 #ifdef HAVE_UMASK
   24113     umask(mask);
   24114 #endif
   24115 #endif
   24116 }
   24117 
   24118 void Jim_HistoryShow(void)
   24119 {
   24120 #ifdef USE_LINENOISE
   24121 
   24122     int i;
   24123     int len;
   24124     char **history = linenoiseHistory(&len);
   24125     for (i = 0; i < len; i++) {
   24126         printf("%4d %s\n", i + 1, history[i]);
   24127     }
   24128 #endif
   24129 }
   24130 
   24131 void Jim_HistorySetMaxLen(int length)
   24132 {
   24133 #ifdef USE_LINENOISE
   24134     linenoiseHistorySetMaxLen(length);
   24135 #endif
   24136 }
   24137 
   24138 int Jim_HistoryGetMaxLen(void)
   24139 {
   24140 #ifdef USE_LINENOISE
   24141     return linenoiseHistoryGetMaxLen();
   24142 #endif
   24143     return 0;
   24144 }
   24145 
   24146 #ifdef USE_LINENOISE
   24147 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
   24148 {
   24149     struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
   24150     Jim_Obj *objv[2];
   24151     int ret;
   24152 
   24153     objv[0] = info->completion_command;
   24154     objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
   24155 
   24156     ret = Jim_EvalObjVector(info->interp, 2, objv);
   24157 
   24158 
   24159     if (ret == JIM_OK) {
   24160         int i;
   24161         Jim_Obj *listObj = Jim_GetResult(info->interp);
   24162         int len = Jim_ListLength(info->interp, listObj);
   24163         for (i = 0; i < len; i++) {
   24164             linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
   24165         }
   24166     }
   24167 }
   24168 
   24169 static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata)
   24170 {
   24171     struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
   24172     Jim_Obj *objv[2];
   24173     int ret;
   24174     char *result = NULL;
   24175 
   24176     objv[0] = info->hints_command;
   24177     objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
   24178 
   24179     ret = Jim_EvalObjVector(info->interp, 2, objv);
   24180 
   24181 
   24182     if (ret == JIM_OK) {
   24183         Jim_Obj *listObj = Jim_GetResult(info->interp);
   24184         Jim_IncrRefCount(listObj);
   24185 
   24186         int len = Jim_ListLength(info->interp, listObj);
   24187         if (len >= 1) {
   24188             long x;
   24189             result = Jim_StrDup(Jim_String(Jim_ListGetIndex(info->interp, listObj, 0)));
   24190             if (len >= 2 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 1), &x) == JIM_OK) {
   24191                 *color = x;
   24192             }
   24193             if (len >= 3 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 2), &x) == JIM_OK) {
   24194                 *bold = x;
   24195             }
   24196         }
   24197         Jim_DecrRefCount(info->interp, listObj);
   24198     }
   24199     return result;
   24200 }
   24201 
   24202 static void JimFreeHintsCallback(void *hint, void *userdata)
   24203 {
   24204     Jim_Free(hint);
   24205 }
   24206 
   24207 static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
   24208 {
   24209     struct JimCompletionInfo *compinfo = data;
   24210 
   24211     if (compinfo->completion_command) {
   24212         Jim_DecrRefCount(interp, compinfo->completion_command);
   24213     }
   24214     if (compinfo->hints_command) {
   24215         Jim_DecrRefCount(interp, compinfo->hints_command);
   24216     }
   24217 
   24218     Jim_Free(compinfo);
   24219 }
   24220 
   24221 static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp)
   24222 {
   24223     struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key);
   24224     if (compinfo == NULL) {
   24225         compinfo = Jim_Alloc(sizeof(*compinfo));
   24226         compinfo->interp = interp;
   24227         compinfo->completion_command = NULL;
   24228         compinfo->hints_command = NULL;
   24229         Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
   24230     }
   24231     return compinfo;
   24232 }
   24233 #endif
   24234 
   24235 void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj)
   24236 {
   24237 #ifdef USE_LINENOISE
   24238     struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
   24239 
   24240     if (completionCommandObj) {
   24241 
   24242         Jim_IncrRefCount(completionCommandObj);
   24243     }
   24244     if (compinfo->completion_command) {
   24245         Jim_DecrRefCount(interp, compinfo->completion_command);
   24246     }
   24247     compinfo->completion_command = completionCommandObj;
   24248 #endif
   24249 }
   24250 
   24251 void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj)
   24252 {
   24253 #ifdef USE_LINENOISE
   24254     struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
   24255 
   24256     if (hintsCommandObj) {
   24257 
   24258         Jim_IncrRefCount(hintsCommandObj);
   24259     }
   24260     if (compinfo->hints_command) {
   24261         Jim_DecrRefCount(interp, compinfo->hints_command);
   24262     }
   24263     compinfo->hints_command = hintsCommandObj;
   24264 #endif
   24265 }
   24266 
   24267 int Jim_InteractivePrompt(Jim_Interp *interp)
   24268 {
   24269     int retcode = JIM_OK;
   24270     char *history_file = NULL;
   24271 #ifdef USE_LINENOISE
   24272     const char *home;
   24273 
   24274     home = getenv("HOME");
   24275     if (home && isatty(STDIN_FILENO)) {
   24276         int history_len = strlen(home) + sizeof("/.jim_history");
   24277         history_file = Jim_Alloc(history_len);
   24278         snprintf(history_file, history_len, "%s/.jim_history", home);
   24279         Jim_HistoryLoad(history_file);
   24280     }
   24281 
   24282     Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
   24283     Jim_HistorySetHints(interp, Jim_NewStringObj(interp, "tcl::stdhint", -1));
   24284 #endif
   24285 
   24286     printf("Welcome to Jim version %d.%d\n",
   24287         JIM_VERSION / 100, JIM_VERSION % 100);
   24288     Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
   24289 
   24290     while (1) {
   24291         Jim_Obj *scriptObjPtr;
   24292         const char *result;
   24293         int reslen;
   24294         char prompt[20];
   24295 
   24296         if (retcode != JIM_OK) {
   24297             const char *retcodestr = Jim_ReturnCode(retcode);
   24298 
   24299             if (*retcodestr == '?') {
   24300                 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
   24301             }
   24302             else {
   24303                 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
   24304             }
   24305         }
   24306         else {
   24307             strcpy(prompt, ". ");
   24308         }
   24309 
   24310         scriptObjPtr = Jim_NewStringObj(interp, "", 0);
   24311         Jim_IncrRefCount(scriptObjPtr);
   24312         while (1) {
   24313             char state;
   24314             char *line;
   24315 
   24316             line = Jim_HistoryGetline(interp, prompt);
   24317             if (line == NULL) {
   24318                 if (errno == EINTR) {
   24319                     continue;
   24320                 }
   24321                 Jim_DecrRefCount(interp, scriptObjPtr);
   24322                 retcode = JIM_OK;
   24323                 goto out;
   24324             }
   24325             if (Jim_Length(scriptObjPtr) != 0) {
   24326 
   24327                 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
   24328             }
   24329             Jim_AppendString(interp, scriptObjPtr, line, -1);
   24330             Jim_Free(line);
   24331             if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
   24332                 break;
   24333 
   24334             snprintf(prompt, sizeof(prompt), "%c> ", state);
   24335         }
   24336 #ifdef USE_LINENOISE
   24337         if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
   24338 
   24339             Jim_HistoryShow();
   24340             Jim_DecrRefCount(interp, scriptObjPtr);
   24341             continue;
   24342         }
   24343 
   24344         Jim_HistoryAdd(Jim_String(scriptObjPtr));
   24345         if (history_file) {
   24346             Jim_HistorySave(history_file);
   24347         }
   24348 #endif
   24349         retcode = Jim_EvalObj(interp, scriptObjPtr);
   24350         Jim_DecrRefCount(interp, scriptObjPtr);
   24351 
   24352         if (retcode == JIM_EXIT) {
   24353             break;
   24354         }
   24355         if (retcode == JIM_ERR) {
   24356             Jim_MakeErrorMessage(interp);
   24357         }
   24358         result = Jim_GetString(Jim_GetResult(interp), &reslen);
   24359         if (reslen) {
   24360             if (fwrite(result, reslen, 1, stdout) == 0) {
   24361 
   24362             }
   24363             putchar('\n');
   24364         }
   24365     }
   24366   out:
   24367     Jim_Free(history_file);
   24368 
   24369     return retcode;
   24370 }
   24371 
   24372 #include <stdio.h>
   24373 #include <stdlib.h>
   24374 #include <string.h>
   24375 
   24376 
   24377 
   24378 extern int Jim_initjimshInit(Jim_Interp *interp);
   24379 
   24380 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
   24381 {
   24382     int n;
   24383     Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
   24384 
   24385 
   24386     for (n = 0; n < argc; n++) {
   24387         Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
   24388 
   24389         Jim_ListAppendElement(interp, listObj, obj);
   24390     }
   24391 
   24392     Jim_SetVariableStr(interp, "argv", listObj);
   24393     Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
   24394 }
   24395 
   24396 static void JimPrintErrorMessage(Jim_Interp *interp)
   24397 {
   24398     Jim_MakeErrorMessage(interp);
   24399     fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
   24400 }
   24401 
   24402 void usage(const char* executable_name)
   24403 {
   24404     printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
   24405     printf("Usage: %s\n", executable_name);
   24406     printf("or   : %s [options] [filename]\n", executable_name);
   24407     printf("\n");
   24408     printf("Without options: Interactive mode\n");
   24409     printf("\n");
   24410     printf("Options:\n");
   24411     printf("      --version  : prints the version string\n");
   24412     printf("      --help     : prints this text\n");
   24413     printf("      -e CMD     : executes command CMD\n");
   24414     printf("                   NOTE: all subsequent options will be passed as arguments to the command\n");
   24415     printf("    [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n");
   24416     printf("                   NOTE: all subsequent options will be passed to the script\n\n");
   24417 }
   24418 
   24419 int main(int argc, char *const argv[])
   24420 {
   24421     int retcode;
   24422     Jim_Interp *interp;
   24423     char *const orig_argv0 = argv[0];
   24424 
   24425 
   24426     if (argc > 1 && strcmp(argv[1], "--version") == 0) {
   24427         printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
   24428         return 0;
   24429     }
   24430     else if (argc > 1 && strcmp(argv[1], "--help") == 0) {
   24431         usage(argv[0]);
   24432         return 0;
   24433     }
   24434 
   24435 
   24436     interp = Jim_CreateInterp();
   24437     Jim_RegisterCoreCommands(interp);
   24438 
   24439 
   24440     if (Jim_InitStaticExtensions(interp) != JIM_OK) {
   24441         JimPrintErrorMessage(interp);
   24442     }
   24443 
   24444     Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
   24445     Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
   24446 #ifdef USE_LINENOISE
   24447     Jim_SetVariableStrWithStr(interp, "jim::lineedit", "1");
   24448 #else
   24449     Jim_SetVariableStrWithStr(interp, "jim::lineedit", "0");
   24450 #endif
   24451     retcode = Jim_initjimshInit(interp);
   24452 
   24453     if (argc == 1) {
   24454 
   24455         if (retcode == JIM_ERR) {
   24456             JimPrintErrorMessage(interp);
   24457         }
   24458         if (retcode != JIM_EXIT) {
   24459             JimSetArgv(interp, 0, NULL);
   24460             if (!isatty(STDIN_FILENO)) {
   24461 
   24462                 goto eval_stdin;
   24463             }
   24464             retcode = Jim_InteractivePrompt(interp);
   24465         }
   24466     }
   24467     else {
   24468 
   24469         if (argc > 2 && strcmp(argv[1], "-e") == 0) {
   24470 
   24471             JimSetArgv(interp, argc - 3, argv + 3);
   24472             retcode = Jim_Eval(interp, argv[2]);
   24473             if (retcode != JIM_ERR) {
   24474                 int len;
   24475                 const char *msg = Jim_GetString(Jim_GetResult(interp), &len);
   24476                 if (fwrite(msg, len, 1, stdout) == 0) {
   24477 
   24478                 }
   24479                 putchar('\n');
   24480             }
   24481         }
   24482         else {
   24483             Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
   24484             JimSetArgv(interp, argc - 2, argv + 2);
   24485             if (strcmp(argv[1], "-") == 0) {
   24486 eval_stdin:
   24487                 retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]");
   24488             } else {
   24489                 retcode = Jim_EvalFile(interp, argv[1]);
   24490             }
   24491         }
   24492         if (retcode == JIM_ERR) {
   24493             JimPrintErrorMessage(interp);
   24494         }
   24495     }
   24496     if (retcode == JIM_EXIT) {
   24497         retcode = Jim_GetExitCode(interp);
   24498     }
   24499     else if (retcode == JIM_ERR) {
   24500         retcode = 1;
   24501     }
   24502     else {
   24503         retcode = 0;
   24504     }
   24505     Jim_FreeInterp(interp);
   24506     return retcode;
   24507 }
   24508 #endif
   24509