xref: /freebsd/contrib/sqlite3/autosetup/jimsh0.c (revision 17f0f75308f287efea825457364e2a4de2e107d4)
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
Jim_bootstrapInit(Jim_Interp * interp)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 }
Jim_initjimshInit(Jim_Interp * interp)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 }
Jim_globInit(Jim_Interp * interp)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 }
Jim_stdlibInit(Jim_Interp * interp)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 }
Jim_tclcompatInit(Jim_Interp * interp)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 
JimReadableTimeout(int fd,long ms)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 
stdio_writer(struct AioFile * af,const char * buf,int len)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 
stdio_reader(struct AioFile * af,char * buf,int len,int nb)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 
stdio_error(const AioFile * af)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 
stdio_strerror(struct AioFile * af)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 
aio_set_nonblocking(AioFile * af,int nb)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 
aio_start_nonblocking(AioFile * af)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 
JimAioErrorString(AioFile * af)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 
JimAioSetError(Jim_Interp * interp,Jim_Obj * name)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 
aio_eof(AioFile * af)2271 static int aio_eof(AioFile *af)
2272 {
2273     return af->flags & AIO_EOF;
2274 }
2275 
JimCheckStreamError(Jim_Interp * interp,AioFile * af)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 
aio_consume(Jim_Obj * objPtr,int n)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
aio_autoflush(Jim_Interp * interp,void * clientData,int mask)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 
aio_flush(Jim_Interp * interp,AioFile * af)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 
aio_read_len(Jim_Interp * interp,AioFile * af,unsigned flags,int neededLen)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 
aio_read_consume(Jim_Interp * interp,AioFile * af,int neededLen)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 
JimAioDelProc(Jim_Interp * interp,void * privData)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 
aio_cmd_read(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_AioFilehandle(Jim_Interp * interp,Jim_Obj * command)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 
aio_cmd_getfd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_copy(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_gets(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_puts(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_isatty(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_flush(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_eof(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_close(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_seek(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_tell(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_filename(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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
aio_cmd_ndelay(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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
aio_cmd_sync(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_buffering(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_translation(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_readsize(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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
aio_cmd_timeout(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_eventinfo(Jim_Interp * interp,AioFile * af,unsigned mask,int argc,Jim_Obj * const * argv)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 
aio_cmd_readable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_writable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
aio_cmd_onexception(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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)
aio_cmd_stat(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
JimAioSubCmdProc(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
parse_posix_open_mode(Jim_Interp * interp,Jim_Obj * modeObj)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 
parse_open_mode(Jim_Interp * interp,Jim_Obj * filenameObj,Jim_Obj * modeObj)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 
JimAioOpenCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
JimMakeChannel(Jim_Interp * interp,int fd,Jim_Obj * filename,const char * hdlfmt,int family,int flags)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)
JimMakeChannelPair(Jim_Interp * interp,int p[2],Jim_Obj * filename,const char * hdlfmt,int family,int flags)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
JimCreatePipe(Jim_Interp * interp,Jim_Obj * filenameObj,int flags)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 
JimAioPipeCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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
JimAioOpenPtyCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_aioInit(Jim_Interp * interp)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 
Jim_ReaddirCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_readdirInit(Jim_Interp * interp)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 
FreeRegexpInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)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 
SetRegexpFromAny(Jim_Interp * interp,Jim_Obj * objPtr,unsigned flags)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 
Jim_RegexpCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_RegsubCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_regexpInit(Jim_Interp * interp)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 
JimFixPath(char * path)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 
JimGetFileType(int mode)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 
AppendStatElement(Jim_Interp * interp,Jim_Obj * listObj,const char * key,jim_wide value)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 
Jim_FileStoreStatData(Jim_Interp * interp,Jim_Obj * varName,const jim_stat_t * sb)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 
JimPathLenNoTrailingSlashes(const char * path,int len)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 
JimStripTrailingSlashes(Jim_Interp * interp,Jim_Obj * objPtr)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 
file_cmd_dirname(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_split(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_rootname(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_extension(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_tail(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
JimRealPath(const char * restrict path,char * restrict resolved_path,size_t len)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 
file_cmd_normalize(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_join(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_access(Jim_Interp * interp,Jim_Obj * filename,int mode)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 
file_cmd_readable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_writable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_executable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_exists(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_delete(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
mkdir_all(char * path)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 
file_cmd_mkdir(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_tempfile(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_rename(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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)
file_cmd_link(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_stat(Jim_Interp * interp,Jim_Obj * filename,jim_stat_t * sb)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
file_lstat(Jim_Interp * interp,Jim_Obj * filename,jim_stat_t * sb)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 
file_cmd_atime(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
JimSetFileTimes(Jim_Interp * interp,const char * filename,jim_wide us)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 
file_cmd_mtime(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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
file_cmd_mtimeus(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_copy(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_size(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_isdirectory(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_isfile(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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
file_cmd_owned(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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)
file_cmd_readlink(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_type(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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
file_cmd_lstat(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
file_cmd_stat(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_CdCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_PwdCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_fileInit(Jim_Interp * interp)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__)
Jim_ExecCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_execInit(Jim_Interp * interp)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 
Jim_RemoveTrailingNewline(Jim_Obj * objPtr)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 
JimAppendStreamToString(Jim_Interp * interp,int fd,Jim_Obj * strObj)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 
JimBuildEnv(Jim_Interp * interp)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 
JimFreeEnv(char ** env,char ** original_environ)5355 static void JimFreeEnv(char **env, char **original_environ)
5356 {
5357     if (env != original_environ) {
5358         Jim_Free(env);
5359     }
5360 }
5361 
JimMakeErrorCode(Jim_Interp * interp,long pid,int waitStatus,Jim_Obj * errStrObj)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 
JimCheckWaitStatus(Jim_Interp * interp,long pid,int waitStatus,Jim_Obj * errStrObj)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 
JimFreeWaitInfoTable(struct Jim_Interp * interp,void * privData)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 
JimAllocWaitInfoTable(void)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 
JimWaitRemove(struct WaitInfoTable * table,phandle_t phandle)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 
Jim_ExecCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
JimWaitForProcess(struct WaitInfoTable * table,phandle_t phandle,int * statusPtr)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 
JimDetachPids(struct WaitInfoTable * table,int numPids,const phandle_t * pidPtr)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 
JimGetChannelFd(Jim_Interp * interp,const char * name)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 
JimReapDetachedPids(struct WaitInfoTable * table)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 
Jim_WaitCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_PidCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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
JimCreatePipeline(Jim_Interp * interp,int argc,Jim_Obj * const * argv,phandle_t ** pidArrayPtr,int * inPipePtr,int * outPipePtr,int * errFilePtr)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 
JimCleanupChildren(Jim_Interp * interp,int numPids,phandle_t * pidPtr,Jim_Obj * errStrObj)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 
Jim_execInit(Jim_Interp * interp)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
JimWinFindExecutable(const char * originalName,char fullPath[MAX_PATH])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 
JimSaveEnv(char ** env)6205 static char **JimSaveEnv(char **env)
6206 {
6207     return env;
6208 }
6209 
JimRestoreEnv(char ** env)6210 static void JimRestoreEnv(char **env)
6211 {
6212     JimFreeEnv(env, Jim_GetEnviron());
6213 }
6214 
JimOriginalEnviron(void)6215 static char **JimOriginalEnviron(void)
6216 {
6217     return NULL;
6218 }
6219 
6220 static Jim_Obj *
JimWinBuildCommandLine(Jim_Interp * interp,char ** argv)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
JimStartWinProcess(Jim_Interp * interp,char ** argv,char ** env,int inputId,int outputId,int errorId)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 
JimOriginalEnviron(void)6385 static char **JimOriginalEnviron(void)
6386 {
6387     return Jim_GetEnviron();
6388 }
6389 
JimSaveEnv(char ** env)6390 static char **JimSaveEnv(char **env)
6391 {
6392     char **saveenv = Jim_GetEnviron();
6393     Jim_SetEnviron(env);
6394     return saveenv;
6395 }
6396 
JimRestoreEnv(char ** env)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 
parse_clock_options(Jim_Interp * interp,int argc,Jim_Obj * const * argv,struct clock_options * opts)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 
clock_cmd_format(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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
jim_timegm(const struct tm * tm)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 
clock_cmd_scan(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
clock_cmd_seconds(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
clock_cmd_clicks(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
clock_cmd_micros(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
clock_cmd_millis(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_clockInit(Jim_Interp * interp)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 
array_cmd_exists(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
array_cmd_get(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
array_cmd_names(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
array_cmd_unset(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
array_cmd_size(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
array_cmd_stat(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
array_cmd_set(Jim_Interp * interp,int argc,Jim_Obj * const * argv)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 
Jim_arrayInit(Jim_Interp * interp)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 }
Jim_InitStaticExtensions(Jim_Interp * interp)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 
utf8_tounicode_case(const char * s,int * uc,int upper)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 
JimPushInterpObjImpl(Jim_Obj ** iop,Jim_Obj * no)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 
JimCharsetMatch(const char * pattern,int plen,int c,int flags)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 
JimGlobMatch(const char * pattern,int plen,const char * string,int slen,int nocase)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 
JimStringCompareUtf8(const char * s1,int l1,const char * s2,int l2,int nocase)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 
JimStringFirst(const char * s1,int l1,const char * s2,int l2,int idx)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 
JimStringLast(const char * s1,int l1,const char * s2,int l2)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
JimStringLastUtf8(const char * s1,int l1,const char * s2,int l2)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 
JimCheckConversion(const char * str,const char * endptr)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 
JimNumberBase(const char * str,int * base,int * sign)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 
jim_strtol(const char * str,char ** endptr)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 
jim_strtoull(const char * str,char ** endptr)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 
Jim_StringToWide(const char * str,jim_wide * widePtr,int base)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 
Jim_StringToDouble(const char * str,double * doublePtr)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 
JimPowWide(jim_wide b,jim_wide e)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
JimPanicDump(int condition,const char * fmt,...)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 
JimDefaultAllocator(void * ptr,size_t size)7406 void *JimDefaultAllocator(void *ptr, size_t size)
7407 {
7408     if (size == 0) {
7409         free(ptr);
7410         return NULL;
7411     }
7412     else if (ptr) {
7413         return realloc(ptr, size);
7414     }
7415     else {
7416         return malloc(size);
7417     }
7418 }
7419 
7420 void *(*Jim_Allocator)(void *ptr, size_t size) = JimDefaultAllocator;
7421 
Jim_StrDup(const char * s)7422 char *Jim_StrDup(const char *s)
7423 {
7424     return Jim_StrDupLen(s, strlen(s));
7425 }
7426 
Jim_StrDupLen(const char * s,int l)7427 char *Jim_StrDupLen(const char *s, int l)
7428 {
7429     char *copy = Jim_Alloc(l + 1);
7430 
7431     memcpy(copy, s, l);
7432     copy[l] = 0;
7433     return copy;
7434 }
7435 
7436 
Jim_GetTimeUsec(unsigned type)7437 jim_wide Jim_GetTimeUsec(unsigned type)
7438 {
7439     long long now;
7440     struct timeval tv;
7441 
7442 #if defined(HAVE_CLOCK_GETTIME)
7443     struct timespec ts;
7444 
7445     if (clock_gettime(type, &ts) == 0) {
7446         now = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
7447     }
7448     else
7449 #endif
7450     {
7451         gettimeofday(&tv, NULL);
7452 
7453         now = tv.tv_sec * 1000000LL + tv.tv_usec;
7454     }
7455 
7456     return now;
7457 }
7458 
7459 
7460 
7461 
7462 
7463 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
7464 static unsigned int JimHashTableNextPower(unsigned int size);
7465 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
7466 
7467 
7468 
7469 
Jim_IntHashFunction(unsigned int key)7470 unsigned int Jim_IntHashFunction(unsigned int key)
7471 {
7472     key += ~(key << 15);
7473     key ^= (key >> 10);
7474     key += (key << 3);
7475     key ^= (key >> 6);
7476     key += ~(key << 11);
7477     key ^= (key >> 16);
7478     return key;
7479 }
7480 
7481 
Jim_GenHashFunction(const unsigned char * string,int length)7482 unsigned int Jim_GenHashFunction(const unsigned char *string, int length)
7483 {
7484     unsigned result = 0;
7485     string += length;
7486     while (length--) {
7487         result += (result << 3) + (unsigned char)(*--string);
7488     }
7489     return result;
7490 }
7491 
7492 
7493 
JimResetHashTable(Jim_HashTable * ht)7494 static void JimResetHashTable(Jim_HashTable *ht)
7495 {
7496     ht->table = NULL;
7497     ht->size = 0;
7498     ht->sizemask = 0;
7499     ht->used = 0;
7500     ht->collisions = 0;
7501 #ifdef JIM_RANDOMISE_HASH
7502     ht->uniq = (rand() ^ time(NULL) ^ clock());
7503 #else
7504     ht->uniq = 0;
7505 #endif
7506 }
7507 
JimInitHashTableIterator(Jim_HashTable * ht,Jim_HashTableIterator * iter)7508 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
7509 {
7510     iter->ht = ht;
7511     iter->index = -1;
7512     iter->entry = NULL;
7513     iter->nextEntry = NULL;
7514 }
7515 
7516 
Jim_InitHashTable(Jim_HashTable * ht,const Jim_HashTableType * type,void * privDataPtr)7517 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
7518 {
7519     JimResetHashTable(ht);
7520     ht->type = type;
7521     ht->privdata = privDataPtr;
7522     return JIM_OK;
7523 }
7524 
7525 
Jim_ExpandHashTable(Jim_HashTable * ht,unsigned int size)7526 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
7527 {
7528     Jim_HashTable n;
7529     unsigned int realsize = JimHashTableNextPower(size), i;
7530 
7531      if (size <= ht->used)
7532         return;
7533 
7534     Jim_InitHashTable(&n, ht->type, ht->privdata);
7535     n.size = realsize;
7536     n.sizemask = realsize - 1;
7537     n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
7538 
7539     n.uniq = ht->uniq;
7540 
7541 
7542     memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
7543 
7544     n.used = ht->used;
7545     for (i = 0; ht->used > 0; i++) {
7546         Jim_HashEntry *he, *nextHe;
7547 
7548         if (ht->table[i] == NULL)
7549             continue;
7550 
7551 
7552         he = ht->table[i];
7553         while (he) {
7554             unsigned int h;
7555 
7556             nextHe = he->next;
7557 
7558             h = Jim_HashKey(ht, he->key) & n.sizemask;
7559             he->next = n.table[h];
7560             n.table[h] = he;
7561             ht->used--;
7562 
7563             he = nextHe;
7564         }
7565     }
7566     assert(ht->used == 0);
7567     Jim_Free(ht->table);
7568 
7569 
7570     *ht = n;
7571 }
7572 
Jim_AddHashEntry(Jim_HashTable * ht,const void * key,void * val)7573 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
7574 {
7575     Jim_HashEntry *entry = JimInsertHashEntry(ht, key, 0);;
7576     if (entry == NULL)
7577         return JIM_ERR;
7578 
7579 
7580     Jim_SetHashKey(ht, entry, key);
7581     Jim_SetHashVal(ht, entry, val);
7582     return JIM_OK;
7583 }
7584 
7585 
Jim_ReplaceHashEntry(Jim_HashTable * ht,const void * key,void * val)7586 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
7587 {
7588     int existed;
7589     Jim_HashEntry *entry;
7590 
7591     entry = JimInsertHashEntry(ht, key, 1);
7592     if (entry->key) {
7593         if (ht->type->valDestructor && ht->type->valDup) {
7594             void *newval = ht->type->valDup(ht->privdata, val);
7595             ht->type->valDestructor(ht->privdata, entry->u.val);
7596             entry->u.val = newval;
7597         }
7598         else {
7599             Jim_FreeEntryVal(ht, entry);
7600             Jim_SetHashVal(ht, entry, val);
7601         }
7602         existed = 1;
7603     }
7604     else {
7605 
7606         Jim_SetHashKey(ht, entry, key);
7607         Jim_SetHashVal(ht, entry, val);
7608         existed = 0;
7609     }
7610 
7611     return existed;
7612 }
7613 
Jim_DeleteHashEntry(Jim_HashTable * ht,const void * key)7614 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
7615 {
7616     if (ht->used) {
7617         unsigned int h = Jim_HashKey(ht, key) & ht->sizemask;
7618         Jim_HashEntry *prevHe = NULL;
7619         Jim_HashEntry *he = ht->table[h];
7620 
7621         while (he) {
7622             if (Jim_CompareHashKeys(ht, key, he->key)) {
7623 
7624                 if (prevHe)
7625                     prevHe->next = he->next;
7626                 else
7627                     ht->table[h] = he->next;
7628                 ht->used--;
7629                 Jim_FreeEntryKey(ht, he);
7630                 Jim_FreeEntryVal(ht, he);
7631                 Jim_Free(he);
7632                 return JIM_OK;
7633             }
7634             prevHe = he;
7635             he = he->next;
7636         }
7637     }
7638 
7639     return JIM_ERR;
7640 }
7641 
Jim_ClearHashTable(Jim_HashTable * ht)7642 void Jim_ClearHashTable(Jim_HashTable *ht)
7643 {
7644     unsigned int i;
7645 
7646 
7647     for (i = 0; ht->used > 0; i++) {
7648         Jim_HashEntry *he, *nextHe;
7649 
7650         he = ht->table[i];
7651         while (he) {
7652             nextHe = he->next;
7653             Jim_FreeEntryKey(ht, he);
7654             Jim_FreeEntryVal(ht, he);
7655             Jim_Free(he);
7656             ht->used--;
7657             he = nextHe;
7658         }
7659         ht->table[i] = NULL;
7660     }
7661 }
7662 
Jim_FreeHashTable(Jim_HashTable * ht)7663 int Jim_FreeHashTable(Jim_HashTable *ht)
7664 {
7665     Jim_ClearHashTable(ht);
7666 
7667     Jim_Free(ht->table);
7668 
7669     JimResetHashTable(ht);
7670     return JIM_OK;
7671 }
7672 
Jim_FindHashEntry(Jim_HashTable * ht,const void * key)7673 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
7674 {
7675     Jim_HashEntry *he;
7676     unsigned int h;
7677 
7678     if (ht->used == 0)
7679         return NULL;
7680     h = Jim_HashKey(ht, key) & ht->sizemask;
7681     he = ht->table[h];
7682     while (he) {
7683         if (Jim_CompareHashKeys(ht, key, he->key))
7684             return he;
7685         he = he->next;
7686     }
7687     return NULL;
7688 }
7689 
Jim_GetHashTableIterator(Jim_HashTable * ht)7690 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
7691 {
7692     Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
7693     JimInitHashTableIterator(ht, iter);
7694     return iter;
7695 }
7696 
Jim_NextHashEntry(Jim_HashTableIterator * iter)7697 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
7698 {
7699     while (1) {
7700         if (iter->entry == NULL) {
7701             iter->index++;
7702             if (iter->index >= (signed)iter->ht->size)
7703                 break;
7704             iter->entry = iter->ht->table[iter->index];
7705         }
7706         else {
7707             iter->entry = iter->nextEntry;
7708         }
7709         if (iter->entry) {
7710             iter->nextEntry = iter->entry->next;
7711             return iter->entry;
7712         }
7713     }
7714     return NULL;
7715 }
7716 
7717 
7718 
7719 
JimExpandHashTableIfNeeded(Jim_HashTable * ht)7720 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
7721 {
7722     if (ht->size == 0)
7723         Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
7724     if (ht->size == ht->used)
7725         Jim_ExpandHashTable(ht, ht->size * 2);
7726 }
7727 
7728 
JimHashTableNextPower(unsigned int size)7729 static unsigned int JimHashTableNextPower(unsigned int size)
7730 {
7731     unsigned int i = JIM_HT_INITIAL_SIZE;
7732 
7733     if (size >= 2147483648U)
7734         return 2147483648U;
7735     while (1) {
7736         if (i >= size)
7737             return i;
7738         i *= 2;
7739     }
7740 }
7741 
JimInsertHashEntry(Jim_HashTable * ht,const void * key,int replace)7742 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
7743 {
7744     unsigned int h;
7745     Jim_HashEntry *he;
7746 
7747 
7748     JimExpandHashTableIfNeeded(ht);
7749 
7750 
7751     h = Jim_HashKey(ht, key) & ht->sizemask;
7752 
7753     he = ht->table[h];
7754     while (he) {
7755         if (Jim_CompareHashKeys(ht, key, he->key))
7756             return replace ? he : NULL;
7757         he = he->next;
7758     }
7759 
7760 
7761     he = Jim_Alloc(sizeof(*he));
7762     he->next = ht->table[h];
7763     ht->table[h] = he;
7764     ht->used++;
7765     he->key = NULL;
7766 
7767     return he;
7768 }
7769 
7770 
7771 
JimStringCopyHTHashFunction(const void * key)7772 static unsigned int JimStringCopyHTHashFunction(const void *key)
7773 {
7774     return Jim_GenHashFunction(key, strlen(key));
7775 }
7776 
JimStringCopyHTDup(void * privdata,const void * key)7777 static void *JimStringCopyHTDup(void *privdata, const void *key)
7778 {
7779     return Jim_StrDup(key);
7780 }
7781 
JimStringCopyHTKeyCompare(void * privdata,const void * key1,const void * key2)7782 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
7783 {
7784     return strcmp(key1, key2) == 0;
7785 }
7786 
JimStringCopyHTKeyDestructor(void * privdata,void * key)7787 static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
7788 {
7789     Jim_Free(key);
7790 }
7791 
7792 static const Jim_HashTableType JimPackageHashTableType = {
7793     JimStringCopyHTHashFunction,
7794     JimStringCopyHTDup,
7795     NULL,
7796     JimStringCopyHTKeyCompare,
7797     JimStringCopyHTKeyDestructor,
7798     NULL
7799 };
7800 
7801 typedef struct AssocDataValue
7802 {
7803     Jim_InterpDeleteProc *delProc;
7804     void *data;
7805 } AssocDataValue;
7806 
JimAssocDataHashTableValueDestructor(void * privdata,void * data)7807 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
7808 {
7809     AssocDataValue *assocPtr = (AssocDataValue *) data;
7810 
7811     if (assocPtr->delProc != NULL)
7812         assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
7813     Jim_Free(data);
7814 }
7815 
7816 static const Jim_HashTableType JimAssocDataHashTableType = {
7817     JimStringCopyHTHashFunction,
7818     JimStringCopyHTDup,
7819     NULL,
7820     JimStringCopyHTKeyCompare,
7821     JimStringCopyHTKeyDestructor,
7822     JimAssocDataHashTableValueDestructor
7823 };
7824 
Jim_InitStack(Jim_Stack * stack)7825 void Jim_InitStack(Jim_Stack *stack)
7826 {
7827     stack->len = 0;
7828     stack->maxlen = 0;
7829     stack->vector = NULL;
7830 }
7831 
Jim_FreeStack(Jim_Stack * stack)7832 void Jim_FreeStack(Jim_Stack *stack)
7833 {
7834     Jim_Free(stack->vector);
7835 }
7836 
Jim_StackLen(Jim_Stack * stack)7837 int Jim_StackLen(Jim_Stack *stack)
7838 {
7839     return stack->len;
7840 }
7841 
Jim_StackPush(Jim_Stack * stack,void * element)7842 void Jim_StackPush(Jim_Stack *stack, void *element)
7843 {
7844     int neededLen = stack->len + 1;
7845 
7846     if (neededLen > stack->maxlen) {
7847         stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
7848         stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
7849     }
7850     stack->vector[stack->len] = element;
7851     stack->len++;
7852 }
7853 
Jim_StackPop(Jim_Stack * stack)7854 void *Jim_StackPop(Jim_Stack *stack)
7855 {
7856     if (stack->len == 0)
7857         return NULL;
7858     stack->len--;
7859     return stack->vector[stack->len];
7860 }
7861 
Jim_StackPeek(Jim_Stack * stack)7862 void *Jim_StackPeek(Jim_Stack *stack)
7863 {
7864     if (stack->len == 0)
7865         return NULL;
7866     return stack->vector[stack->len - 1];
7867 }
7868 
Jim_FreeStackElements(Jim_Stack * stack,void (* freeFunc)(void * ptr))7869 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
7870 {
7871     int i;
7872 
7873     for (i = 0; i < stack->len; i++)
7874         freeFunc(stack->vector[i]);
7875 }
7876 
7877 
7878 
7879 #define JIM_TT_NONE    0
7880 #define JIM_TT_STR     1
7881 #define JIM_TT_ESC     2
7882 #define JIM_TT_VAR     3
7883 #define JIM_TT_DICTSUGAR   4
7884 #define JIM_TT_CMD     5
7885 
7886 #define JIM_TT_SEP     6
7887 #define JIM_TT_EOL     7
7888 #define JIM_TT_EOF     8
7889 
7890 #define JIM_TT_LINE    9
7891 #define JIM_TT_WORD   10
7892 
7893 
7894 #define JIM_TT_SUBEXPR_START  11
7895 #define JIM_TT_SUBEXPR_END    12
7896 #define JIM_TT_SUBEXPR_COMMA  13
7897 #define JIM_TT_EXPR_INT       14
7898 #define JIM_TT_EXPR_DOUBLE    15
7899 #define JIM_TT_EXPR_BOOLEAN   16
7900 
7901 #define JIM_TT_EXPRSUGAR      17
7902 
7903 
7904 #define JIM_TT_EXPR_OP        20
7905 
7906 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
7907 
7908 #define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA)
7909 
7910 #define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP)
7911 
7912 struct JimParseMissing {
7913     int ch;
7914     int line;
7915 };
7916 
7917 struct JimParserCtx
7918 {
7919     const char *p;
7920     int len;
7921     int linenr;
7922     const char *tstart;
7923     const char *tend;
7924     int tline;
7925     int tt;
7926     int eof;
7927     int inquote;
7928     int comment;
7929     struct JimParseMissing missing;
7930     const char *errmsg;
7931 };
7932 
7933 static int JimParseScript(struct JimParserCtx *pc);
7934 static int JimParseSep(struct JimParserCtx *pc);
7935 static int JimParseEol(struct JimParserCtx *pc);
7936 static int JimParseCmd(struct JimParserCtx *pc);
7937 static int JimParseQuote(struct JimParserCtx *pc);
7938 static int JimParseVar(struct JimParserCtx *pc);
7939 static int JimParseBrace(struct JimParserCtx *pc);
7940 static int JimParseStr(struct JimParserCtx *pc);
7941 static int JimParseComment(struct JimParserCtx *pc);
7942 static void JimParseSubCmd(struct JimParserCtx *pc);
7943 static int JimParseSubQuote(struct JimParserCtx *pc);
7944 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
7945 
JimParserInit(struct JimParserCtx * pc,const char * prg,int len,int linenr)7946 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
7947 {
7948     pc->p = prg;
7949     pc->len = len;
7950     pc->tstart = NULL;
7951     pc->tend = NULL;
7952     pc->tline = 0;
7953     pc->tt = JIM_TT_NONE;
7954     pc->eof = 0;
7955     pc->inquote = 0;
7956     pc->linenr = linenr;
7957     pc->comment = 1;
7958     pc->missing.ch = ' ';
7959     pc->missing.line = linenr;
7960 }
7961 
JimParseScript(struct JimParserCtx * pc)7962 static int JimParseScript(struct JimParserCtx *pc)
7963 {
7964     while (1) {
7965         if (!pc->len) {
7966             pc->tstart = pc->p;
7967             pc->tend = pc->p - 1;
7968             pc->tline = pc->linenr;
7969             pc->tt = JIM_TT_EOL;
7970             if (pc->inquote) {
7971                 pc->missing.ch = '"';
7972             }
7973             pc->eof = 1;
7974             return JIM_OK;
7975         }
7976         switch (*(pc->p)) {
7977             case '\\':
7978                 if (*(pc->p + 1) == '\n' && !pc->inquote) {
7979                     return JimParseSep(pc);
7980                 }
7981                 pc->comment = 0;
7982                 return JimParseStr(pc);
7983             case ' ':
7984             case '\t':
7985             case '\r':
7986             case '\f':
7987                 if (!pc->inquote)
7988                     return JimParseSep(pc);
7989                 pc->comment = 0;
7990                 return JimParseStr(pc);
7991             case '\n':
7992             case ';':
7993                 pc->comment = 1;
7994                 if (!pc->inquote)
7995                     return JimParseEol(pc);
7996                 return JimParseStr(pc);
7997             case '[':
7998                 pc->comment = 0;
7999                 return JimParseCmd(pc);
8000             case '$':
8001                 pc->comment = 0;
8002                 if (JimParseVar(pc) == JIM_ERR) {
8003 
8004                     pc->tstart = pc->tend = pc->p++;
8005                     pc->len--;
8006                     pc->tt = JIM_TT_ESC;
8007                 }
8008                 return JIM_OK;
8009             case '#':
8010                 if (pc->comment) {
8011                     JimParseComment(pc);
8012                     continue;
8013                 }
8014                 return JimParseStr(pc);
8015             default:
8016                 pc->comment = 0;
8017                 return JimParseStr(pc);
8018         }
8019         return JIM_OK;
8020     }
8021 }
8022 
JimParseSep(struct JimParserCtx * pc)8023 static int JimParseSep(struct JimParserCtx *pc)
8024 {
8025     pc->tstart = pc->p;
8026     pc->tline = pc->linenr;
8027     while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
8028         if (*pc->p == '\n') {
8029             break;
8030         }
8031         if (*pc->p == '\\') {
8032             pc->p++;
8033             pc->len--;
8034             pc->linenr++;
8035         }
8036         pc->p++;
8037         pc->len--;
8038     }
8039     pc->tend = pc->p - 1;
8040     pc->tt = JIM_TT_SEP;
8041     return JIM_OK;
8042 }
8043 
JimParseEol(struct JimParserCtx * pc)8044 static int JimParseEol(struct JimParserCtx *pc)
8045 {
8046     pc->tstart = pc->p;
8047     pc->tline = pc->linenr;
8048     while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
8049         if (*pc->p == '\n')
8050             pc->linenr++;
8051         pc->p++;
8052         pc->len--;
8053     }
8054     pc->tend = pc->p - 1;
8055     pc->tt = JIM_TT_EOL;
8056     return JIM_OK;
8057 }
8058 
8059 
JimParseSubBrace(struct JimParserCtx * pc)8060 static void JimParseSubBrace(struct JimParserCtx *pc)
8061 {
8062     int level = 1;
8063 
8064 
8065     pc->p++;
8066     pc->len--;
8067     while (pc->len) {
8068         switch (*pc->p) {
8069             case '\\':
8070                 if (pc->len > 1) {
8071                     if (*++pc->p == '\n') {
8072                         pc->linenr++;
8073                     }
8074                     pc->len--;
8075                 }
8076                 break;
8077 
8078             case '{':
8079                 level++;
8080                 break;
8081 
8082             case '}':
8083                 if (--level == 0) {
8084                     pc->tend = pc->p - 1;
8085                     pc->p++;
8086                     pc->len--;
8087                     return;
8088                 }
8089                 break;
8090 
8091             case '\n':
8092                 pc->linenr++;
8093                 break;
8094         }
8095         pc->p++;
8096         pc->len--;
8097     }
8098     pc->missing.ch = '{';
8099     pc->missing.line = pc->tline;
8100     pc->tend = pc->p - 1;
8101 }
8102 
JimParseSubQuote(struct JimParserCtx * pc)8103 static int JimParseSubQuote(struct JimParserCtx *pc)
8104 {
8105     int tt = JIM_TT_STR;
8106     int line = pc->tline;
8107 
8108 
8109     pc->p++;
8110     pc->len--;
8111     while (pc->len) {
8112         switch (*pc->p) {
8113             case '\\':
8114                 if (pc->len > 1) {
8115                     if (*++pc->p == '\n') {
8116                         pc->linenr++;
8117                     }
8118                     pc->len--;
8119                     tt = JIM_TT_ESC;
8120                 }
8121                 break;
8122 
8123             case '"':
8124                 pc->tend = pc->p - 1;
8125                 pc->p++;
8126                 pc->len--;
8127                 return tt;
8128 
8129             case '[':
8130                 JimParseSubCmd(pc);
8131                 tt = JIM_TT_ESC;
8132                 continue;
8133 
8134             case '\n':
8135                 pc->linenr++;
8136                 break;
8137 
8138             case '$':
8139                 tt = JIM_TT_ESC;
8140                 break;
8141         }
8142         pc->p++;
8143         pc->len--;
8144     }
8145     pc->missing.ch = '"';
8146     pc->missing.line = line;
8147     pc->tend = pc->p - 1;
8148     return tt;
8149 }
8150 
JimParseSubCmd(struct JimParserCtx * pc)8151 static void JimParseSubCmd(struct JimParserCtx *pc)
8152 {
8153     int level = 1;
8154     int startofword = 1;
8155     int line = pc->tline;
8156 
8157 
8158     pc->p++;
8159     pc->len--;
8160     while (pc->len) {
8161         switch (*pc->p) {
8162             case '\\':
8163                 if (pc->len > 1) {
8164                     if (*++pc->p == '\n') {
8165                         pc->linenr++;
8166                     }
8167                     pc->len--;
8168                 }
8169                 break;
8170 
8171             case '[':
8172                 level++;
8173                 break;
8174 
8175             case ']':
8176                 if (--level == 0) {
8177                     pc->tend = pc->p - 1;
8178                     pc->p++;
8179                     pc->len--;
8180                     return;
8181                 }
8182                 break;
8183 
8184             case '"':
8185                 if (startofword) {
8186                     JimParseSubQuote(pc);
8187                     if (pc->missing.ch == '"') {
8188                         return;
8189                     }
8190                     continue;
8191                 }
8192                 break;
8193 
8194             case '{':
8195                 JimParseSubBrace(pc);
8196                 startofword = 0;
8197                 continue;
8198 
8199             case '\n':
8200                 pc->linenr++;
8201                 break;
8202         }
8203         startofword = isspace(UCHAR(*pc->p));
8204         pc->p++;
8205         pc->len--;
8206     }
8207     pc->missing.ch = '[';
8208     pc->missing.line = line;
8209     pc->tend = pc->p - 1;
8210 }
8211 
JimParseBrace(struct JimParserCtx * pc)8212 static int JimParseBrace(struct JimParserCtx *pc)
8213 {
8214     pc->tstart = pc->p + 1;
8215     pc->tline = pc->linenr;
8216     pc->tt = JIM_TT_STR;
8217     JimParseSubBrace(pc);
8218     return JIM_OK;
8219 }
8220 
JimParseCmd(struct JimParserCtx * pc)8221 static int JimParseCmd(struct JimParserCtx *pc)
8222 {
8223     pc->tstart = pc->p + 1;
8224     pc->tline = pc->linenr;
8225     pc->tt = JIM_TT_CMD;
8226     JimParseSubCmd(pc);
8227     return JIM_OK;
8228 }
8229 
JimParseQuote(struct JimParserCtx * pc)8230 static int JimParseQuote(struct JimParserCtx *pc)
8231 {
8232     pc->tstart = pc->p + 1;
8233     pc->tline = pc->linenr;
8234     pc->tt = JimParseSubQuote(pc);
8235     return JIM_OK;
8236 }
8237 
JimParseVar(struct JimParserCtx * pc)8238 static int JimParseVar(struct JimParserCtx *pc)
8239 {
8240 
8241     pc->p++;
8242     pc->len--;
8243 
8244 #ifdef EXPRSUGAR_BRACKET
8245     if (*pc->p == '[') {
8246 
8247         JimParseCmd(pc);
8248         pc->tt = JIM_TT_EXPRSUGAR;
8249         return JIM_OK;
8250     }
8251 #endif
8252 
8253     pc->tstart = pc->p;
8254     pc->tt = JIM_TT_VAR;
8255     pc->tline = pc->linenr;
8256 
8257     if (*pc->p == '{') {
8258         pc->tstart = ++pc->p;
8259         pc->len--;
8260 
8261         while (pc->len && *pc->p != '}') {
8262             if (*pc->p == '\n') {
8263                 pc->linenr++;
8264             }
8265             pc->p++;
8266             pc->len--;
8267         }
8268         pc->tend = pc->p - 1;
8269         if (pc->len) {
8270             pc->p++;
8271             pc->len--;
8272         }
8273     }
8274     else {
8275         while (1) {
8276 
8277             if (pc->p[0] == ':' && pc->p[1] == ':') {
8278                 while (*pc->p == ':') {
8279                     pc->p++;
8280                     pc->len--;
8281                 }
8282                 continue;
8283             }
8284             if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
8285                 pc->p++;
8286                 pc->len--;
8287                 continue;
8288             }
8289             break;
8290         }
8291 
8292         if (*pc->p == '(') {
8293             int count = 1;
8294             const char *paren = NULL;
8295 
8296             pc->tt = JIM_TT_DICTSUGAR;
8297 
8298             while (count && pc->len) {
8299                 pc->p++;
8300                 pc->len--;
8301                 if (*pc->p == '\\' && pc->len >= 1) {
8302                     pc->p++;
8303                     pc->len--;
8304                 }
8305                 else if (*pc->p == '(') {
8306                     count++;
8307                 }
8308                 else if (*pc->p == ')') {
8309                     paren = pc->p;
8310                     count--;
8311                 }
8312             }
8313             if (count == 0) {
8314                 pc->p++;
8315                 pc->len--;
8316             }
8317             else if (paren) {
8318 
8319                 paren++;
8320                 pc->len += (pc->p - paren);
8321                 pc->p = paren;
8322             }
8323 #ifndef EXPRSUGAR_BRACKET
8324             if (*pc->tstart == '(') {
8325                 pc->tt = JIM_TT_EXPRSUGAR;
8326             }
8327 #endif
8328         }
8329         pc->tend = pc->p - 1;
8330     }
8331     if (pc->tstart == pc->p) {
8332         pc->p--;
8333         pc->len++;
8334         return JIM_ERR;
8335     }
8336     return JIM_OK;
8337 }
8338 
JimParseStr(struct JimParserCtx * pc)8339 static int JimParseStr(struct JimParserCtx *pc)
8340 {
8341     if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
8342         pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
8343 
8344         if (*pc->p == '{') {
8345             return JimParseBrace(pc);
8346         }
8347         if (*pc->p == '"') {
8348             pc->inquote = 1;
8349             pc->p++;
8350             pc->len--;
8351 
8352             pc->missing.line = pc->tline;
8353         }
8354     }
8355     pc->tstart = pc->p;
8356     pc->tline = pc->linenr;
8357     while (1) {
8358         if (pc->len == 0) {
8359             if (pc->inquote) {
8360                 pc->missing.ch = '"';
8361             }
8362             pc->tend = pc->p - 1;
8363             pc->tt = JIM_TT_ESC;
8364             return JIM_OK;
8365         }
8366         switch (*pc->p) {
8367             case '\\':
8368                 if (!pc->inquote && *(pc->p + 1) == '\n') {
8369                     pc->tend = pc->p - 1;
8370                     pc->tt = JIM_TT_ESC;
8371                     return JIM_OK;
8372                 }
8373                 if (pc->len >= 2) {
8374                     if (*(pc->p + 1) == '\n') {
8375                         pc->linenr++;
8376                     }
8377                     pc->p++;
8378                     pc->len--;
8379                 }
8380                 else if (pc->len == 1) {
8381 
8382                     pc->missing.ch = '\\';
8383                 }
8384                 break;
8385             case '(':
8386 
8387                 if (pc->len > 1 && pc->p[1] != '$') {
8388                     break;
8389                 }
8390 
8391             case ')':
8392 
8393                 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
8394                     if (pc->p == pc->tstart) {
8395 
8396                         pc->p++;
8397                         pc->len--;
8398                     }
8399                     pc->tend = pc->p - 1;
8400                     pc->tt = JIM_TT_ESC;
8401                     return JIM_OK;
8402                 }
8403                 break;
8404 
8405             case '$':
8406             case '[':
8407                 pc->tend = pc->p - 1;
8408                 pc->tt = JIM_TT_ESC;
8409                 return JIM_OK;
8410             case ' ':
8411             case '\t':
8412             case '\n':
8413             case '\r':
8414             case '\f':
8415             case ';':
8416                 if (!pc->inquote) {
8417                     pc->tend = pc->p - 1;
8418                     pc->tt = JIM_TT_ESC;
8419                     return JIM_OK;
8420                 }
8421                 else if (*pc->p == '\n') {
8422                     pc->linenr++;
8423                 }
8424                 break;
8425             case '"':
8426                 if (pc->inquote) {
8427                     pc->tend = pc->p - 1;
8428                     pc->tt = JIM_TT_ESC;
8429                     pc->p++;
8430                     pc->len--;
8431                     pc->inquote = 0;
8432                     return JIM_OK;
8433                 }
8434                 break;
8435         }
8436         pc->p++;
8437         pc->len--;
8438     }
8439     return JIM_OK;
8440 }
8441 
JimParseComment(struct JimParserCtx * pc)8442 static int JimParseComment(struct JimParserCtx *pc)
8443 {
8444     while (*pc->p) {
8445         if (*pc->p == '\\') {
8446             pc->p++;
8447             pc->len--;
8448             if (pc->len == 0) {
8449                 pc->missing.ch = '\\';
8450                 return JIM_OK;
8451             }
8452             if (*pc->p == '\n') {
8453                 pc->linenr++;
8454             }
8455         }
8456         else if (*pc->p == '\n') {
8457             pc->p++;
8458             pc->len--;
8459             pc->linenr++;
8460             break;
8461         }
8462         pc->p++;
8463         pc->len--;
8464     }
8465     return JIM_OK;
8466 }
8467 
8468 
xdigitval(int c)8469 static int xdigitval(int c)
8470 {
8471     if (c >= '0' && c <= '9')
8472         return c - '0';
8473     if (c >= 'a' && c <= 'f')
8474         return c - 'a' + 10;
8475     if (c >= 'A' && c <= 'F')
8476         return c - 'A' + 10;
8477     return -1;
8478 }
8479 
odigitval(int c)8480 static int odigitval(int c)
8481 {
8482     if (c >= '0' && c <= '7')
8483         return c - '0';
8484     return -1;
8485 }
8486 
JimEscape(char * dest,const char * s,int slen)8487 static int JimEscape(char *dest, const char *s, int slen)
8488 {
8489     char *p = dest;
8490     int i, len;
8491 
8492     for (i = 0; i < slen; i++) {
8493         switch (s[i]) {
8494             case '\\':
8495                 switch (s[i + 1]) {
8496                     case 'a':
8497                         *p++ = 0x7;
8498                         i++;
8499                         break;
8500                     case 'b':
8501                         *p++ = 0x8;
8502                         i++;
8503                         break;
8504                     case 'f':
8505                         *p++ = 0xc;
8506                         i++;
8507                         break;
8508                     case 'n':
8509                         *p++ = 0xa;
8510                         i++;
8511                         break;
8512                     case 'r':
8513                         *p++ = 0xd;
8514                         i++;
8515                         break;
8516                     case 't':
8517                         *p++ = 0x9;
8518                         i++;
8519                         break;
8520                     case 'u':
8521                     case 'U':
8522                     case 'x':
8523                         {
8524                             unsigned val = 0;
8525                             int k;
8526                             int maxchars = 2;
8527 
8528                             i++;
8529 
8530                             if (s[i] == 'U') {
8531                                 maxchars = 8;
8532                             }
8533                             else if (s[i] == 'u') {
8534                                 if (s[i + 1] == '{') {
8535                                     maxchars = 6;
8536                                     i++;
8537                                 }
8538                                 else {
8539                                     maxchars = 4;
8540                                 }
8541                             }
8542 
8543                             for (k = 0; k < maxchars; k++) {
8544                                 int c = xdigitval(s[i + k + 1]);
8545                                 if (c == -1) {
8546                                     break;
8547                                 }
8548                                 val = (val << 4) | c;
8549                             }
8550 
8551                             if (s[i] == '{') {
8552                                 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
8553 
8554                                     i--;
8555                                     k = 0;
8556                                 }
8557                                 else {
8558 
8559                                     k++;
8560                                 }
8561                             }
8562                             if (k) {
8563 
8564                                 if (s[i] == 'x') {
8565                                     *p++ = val;
8566                                 }
8567                                 else {
8568                                     p += utf8_fromunicode(p, val);
8569                                 }
8570                                 i += k;
8571                                 break;
8572                             }
8573 
8574                             *p++ = s[i];
8575                         }
8576                         break;
8577                     case 'v':
8578                         *p++ = 0xb;
8579                         i++;
8580                         break;
8581                     case '\0':
8582                         *p++ = '\\';
8583                         i++;
8584                         break;
8585                     case '\n':
8586 
8587                         *p++ = ' ';
8588                         do {
8589                             i++;
8590                         } while (s[i + 1] == ' ' || s[i + 1] == '\t');
8591                         break;
8592                     case '0':
8593                     case '1':
8594                     case '2':
8595                     case '3':
8596                     case '4':
8597                     case '5':
8598                     case '6':
8599                     case '7':
8600 
8601                         {
8602                             int val = 0;
8603                             int c = odigitval(s[i + 1]);
8604 
8605                             val = c;
8606                             c = odigitval(s[i + 2]);
8607                             if (c == -1) {
8608                                 *p++ = val;
8609                                 i++;
8610                                 break;
8611                             }
8612                             val = (val * 8) + c;
8613                             c = odigitval(s[i + 3]);
8614                             if (c == -1) {
8615                                 *p++ = val;
8616                                 i += 2;
8617                                 break;
8618                             }
8619                             val = (val * 8) + c;
8620                             *p++ = val;
8621                             i += 3;
8622                         }
8623                         break;
8624                     default:
8625                         *p++ = s[i + 1];
8626                         i++;
8627                         break;
8628                 }
8629                 break;
8630             default:
8631                 *p++ = s[i];
8632                 break;
8633         }
8634     }
8635     len = p - dest;
8636     *p = '\0';
8637     return len;
8638 }
8639 
JimParserGetTokenObj(Jim_Interp * interp,struct JimParserCtx * pc)8640 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
8641 {
8642     const char *start, *end;
8643     char *token;
8644     int len;
8645 
8646     start = pc->tstart;
8647     end = pc->tend;
8648     len = (end - start) + 1;
8649     if (len < 0) {
8650         len = 0;
8651     }
8652     token = Jim_Alloc(len + 1);
8653     if (pc->tt != JIM_TT_ESC) {
8654 
8655         memcpy(token, start, len);
8656         token[len] = '\0';
8657     }
8658     else {
8659 
8660         len = JimEscape(token, start, len);
8661     }
8662 
8663     return Jim_NewStringObjNoAlloc(interp, token, len);
8664 }
8665 
8666 static int JimParseListSep(struct JimParserCtx *pc);
8667 static int JimParseListStr(struct JimParserCtx *pc);
8668 static int JimParseListQuote(struct JimParserCtx *pc);
8669 
JimParseList(struct JimParserCtx * pc)8670 static int JimParseList(struct JimParserCtx *pc)
8671 {
8672     if (isspace(UCHAR(*pc->p))) {
8673         return JimParseListSep(pc);
8674     }
8675     switch (*pc->p) {
8676         case '"':
8677             return JimParseListQuote(pc);
8678 
8679         case '{':
8680             return JimParseBrace(pc);
8681 
8682         default:
8683             if (pc->len) {
8684                 return JimParseListStr(pc);
8685             }
8686             break;
8687     }
8688 
8689     pc->tstart = pc->tend = pc->p;
8690     pc->tline = pc->linenr;
8691     pc->tt = JIM_TT_EOL;
8692     pc->eof = 1;
8693     return JIM_OK;
8694 }
8695 
JimParseListSep(struct JimParserCtx * pc)8696 static int JimParseListSep(struct JimParserCtx *pc)
8697 {
8698     pc->tstart = pc->p;
8699     pc->tline = pc->linenr;
8700     while (isspace(UCHAR(*pc->p))) {
8701         if (*pc->p == '\n') {
8702             pc->linenr++;
8703         }
8704         pc->p++;
8705         pc->len--;
8706     }
8707     pc->tend = pc->p - 1;
8708     pc->tt = JIM_TT_SEP;
8709     return JIM_OK;
8710 }
8711 
JimParseListQuote(struct JimParserCtx * pc)8712 static int JimParseListQuote(struct JimParserCtx *pc)
8713 {
8714     pc->p++;
8715     pc->len--;
8716 
8717     pc->tstart = pc->p;
8718     pc->tline = pc->linenr;
8719     pc->tt = JIM_TT_STR;
8720 
8721     while (pc->len) {
8722         switch (*pc->p) {
8723             case '\\':
8724                 pc->tt = JIM_TT_ESC;
8725                 if (--pc->len == 0) {
8726 
8727                     pc->tend = pc->p;
8728                     return JIM_OK;
8729                 }
8730                 pc->p++;
8731                 break;
8732             case '\n':
8733                 pc->linenr++;
8734                 break;
8735             case '"':
8736                 pc->tend = pc->p - 1;
8737                 pc->p++;
8738                 pc->len--;
8739                 return JIM_OK;
8740         }
8741         pc->p++;
8742         pc->len--;
8743     }
8744 
8745     pc->tend = pc->p - 1;
8746     return JIM_OK;
8747 }
8748 
JimParseListStr(struct JimParserCtx * pc)8749 static int JimParseListStr(struct JimParserCtx *pc)
8750 {
8751     pc->tstart = pc->p;
8752     pc->tline = pc->linenr;
8753     pc->tt = JIM_TT_STR;
8754 
8755     while (pc->len) {
8756         if (isspace(UCHAR(*pc->p))) {
8757             pc->tend = pc->p - 1;
8758             return JIM_OK;
8759         }
8760         if (*pc->p == '\\') {
8761             if (--pc->len == 0) {
8762 
8763                 pc->tend = pc->p;
8764                 return JIM_OK;
8765             }
8766             pc->tt = JIM_TT_ESC;
8767             pc->p++;
8768         }
8769         pc->p++;
8770         pc->len--;
8771     }
8772     pc->tend = pc->p - 1;
8773     return JIM_OK;
8774 }
8775 
8776 
8777 
Jim_NewObj(Jim_Interp * interp)8778 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
8779 {
8780     Jim_Obj *objPtr;
8781 
8782 
8783     if (interp->freeList != NULL) {
8784 
8785         objPtr = interp->freeList;
8786         interp->freeList = objPtr->nextObjPtr;
8787     }
8788     else {
8789 
8790         objPtr = Jim_Alloc(sizeof(*objPtr));
8791     }
8792 
8793     objPtr->refCount = 0;
8794 
8795 
8796     objPtr->prevObjPtr = NULL;
8797     objPtr->nextObjPtr = interp->liveList;
8798     if (interp->liveList)
8799         interp->liveList->prevObjPtr = objPtr;
8800     interp->liveList = objPtr;
8801 
8802     return objPtr;
8803 }
8804 
Jim_FreeObj(Jim_Interp * interp,Jim_Obj * objPtr)8805 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
8806 {
8807 
8808     JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
8809         objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
8810 
8811 
8812     Jim_FreeIntRep(interp, objPtr);
8813 
8814     if (objPtr->bytes != NULL) {
8815         if (objPtr->bytes != JimEmptyStringRep)
8816             Jim_Free(objPtr->bytes);
8817     }
8818 
8819     if (objPtr->prevObjPtr)
8820         objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
8821     if (objPtr->nextObjPtr)
8822         objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
8823     if (interp->liveList == objPtr)
8824         interp->liveList = objPtr->nextObjPtr;
8825 #ifdef JIM_DISABLE_OBJECT_POOL
8826     Jim_Free(objPtr);
8827 #else
8828 
8829     objPtr->prevObjPtr = NULL;
8830     objPtr->nextObjPtr = interp->freeList;
8831     if (interp->freeList)
8832         interp->freeList->prevObjPtr = objPtr;
8833     interp->freeList = objPtr;
8834     objPtr->refCount = -1;
8835 #endif
8836 }
8837 
8838 
Jim_InvalidateStringRep(Jim_Obj * objPtr)8839 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
8840 {
8841     if (objPtr->bytes != NULL) {
8842         if (objPtr->bytes != JimEmptyStringRep)
8843             Jim_Free(objPtr->bytes);
8844     }
8845     objPtr->bytes = NULL;
8846 }
8847 
8848 
Jim_DuplicateObj(Jim_Interp * interp,Jim_Obj * objPtr)8849 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
8850 {
8851     Jim_Obj *dupPtr;
8852 
8853     dupPtr = Jim_NewObj(interp);
8854     if (objPtr->bytes == NULL) {
8855 
8856         dupPtr->bytes = NULL;
8857     }
8858     else if (objPtr->length == 0) {
8859         dupPtr->bytes = JimEmptyStringRep;
8860         dupPtr->length = 0;
8861         dupPtr->typePtr = NULL;
8862         return dupPtr;
8863     }
8864     else {
8865         dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
8866         dupPtr->length = objPtr->length;
8867 
8868         memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
8869     }
8870 
8871 
8872     dupPtr->typePtr = objPtr->typePtr;
8873     if (objPtr->typePtr != NULL) {
8874         if (objPtr->typePtr->dupIntRepProc == NULL) {
8875             dupPtr->internalRep = objPtr->internalRep;
8876         }
8877         else {
8878 
8879             objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
8880         }
8881     }
8882     return dupPtr;
8883 }
8884 
Jim_GetString(Jim_Obj * objPtr,int * lenPtr)8885 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
8886 {
8887     if (objPtr->bytes == NULL) {
8888 
8889         JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8890         objPtr->typePtr->updateStringProc(objPtr);
8891     }
8892     if (lenPtr)
8893         *lenPtr = objPtr->length;
8894     return objPtr->bytes;
8895 }
8896 
8897 
Jim_Length(Jim_Obj * objPtr)8898 int Jim_Length(Jim_Obj *objPtr)
8899 {
8900     if (objPtr->bytes == NULL) {
8901 
8902         Jim_GetString(objPtr, NULL);
8903     }
8904     return objPtr->length;
8905 }
8906 
8907 
Jim_String(Jim_Obj * objPtr)8908 const char *Jim_String(Jim_Obj *objPtr)
8909 {
8910     if (objPtr->bytes == NULL) {
8911 
8912         Jim_GetString(objPtr, NULL);
8913     }
8914     return objPtr->bytes;
8915 }
8916 
JimSetStringBytes(Jim_Obj * objPtr,const char * str)8917 static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
8918 {
8919     objPtr->bytes = Jim_StrDup(str);
8920     objPtr->length = strlen(str);
8921 }
8922 
8923 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8924 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8925 
8926 static const Jim_ObjType dictSubstObjType = {
8927     "dict-substitution",
8928     FreeDictSubstInternalRep,
8929     DupDictSubstInternalRep,
8930     NULL,
8931     JIM_TYPE_NONE,
8932 };
8933 
8934 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8935 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8936 
8937 static const Jim_ObjType interpolatedObjType = {
8938     "interpolated",
8939     FreeInterpolatedInternalRep,
8940     DupInterpolatedInternalRep,
8941     NULL,
8942     JIM_TYPE_NONE,
8943 };
8944 
FreeInterpolatedInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)8945 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8946 {
8947     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
8948 }
8949 
DupInterpolatedInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)8950 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8951 {
8952 
8953     dupPtr->internalRep = srcPtr->internalRep;
8954 
8955     Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
8956 }
8957 
8958 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8959 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8960 
8961 static const Jim_ObjType stringObjType = {
8962     "string",
8963     NULL,
8964     DupStringInternalRep,
8965     NULL,
8966     JIM_TYPE_REFERENCES,
8967 };
8968 
DupStringInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)8969 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8970 {
8971     JIM_NOTUSED(interp);
8972 
8973     dupPtr->internalRep.strValue.maxLength = srcPtr->length;
8974     dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
8975 }
8976 
SetStringFromAny(Jim_Interp * interp,Jim_Obj * objPtr)8977 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
8978 {
8979     if (objPtr->typePtr != &stringObjType) {
8980 
8981         if (objPtr->bytes == NULL) {
8982 
8983             JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8984             objPtr->typePtr->updateStringProc(objPtr);
8985         }
8986 
8987         Jim_FreeIntRep(interp, objPtr);
8988 
8989         objPtr->typePtr = &stringObjType;
8990         objPtr->internalRep.strValue.maxLength = objPtr->length;
8991 
8992         objPtr->internalRep.strValue.charLength = -1;
8993     }
8994     return JIM_OK;
8995 }
8996 
Jim_Utf8Length(Jim_Interp * interp,Jim_Obj * objPtr)8997 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
8998 {
8999 #ifdef JIM_UTF8
9000     SetStringFromAny(interp, objPtr);
9001 
9002     if (objPtr->internalRep.strValue.charLength < 0) {
9003         objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
9004     }
9005     return objPtr->internalRep.strValue.charLength;
9006 #else
9007     return Jim_Length(objPtr);
9008 #endif
9009 }
9010 
9011 
Jim_NewStringObj(Jim_Interp * interp,const char * s,int len)9012 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
9013 {
9014     Jim_Obj *objPtr = Jim_NewObj(interp);
9015 
9016 
9017     if (len == -1)
9018         len = strlen(s);
9019 
9020     if (len == 0) {
9021         objPtr->bytes = JimEmptyStringRep;
9022     }
9023     else {
9024         objPtr->bytes = Jim_StrDupLen(s, len);
9025     }
9026     objPtr->length = len;
9027 
9028 
9029     objPtr->typePtr = NULL;
9030     return objPtr;
9031 }
9032 
9033 
Jim_NewStringObjUtf8(Jim_Interp * interp,const char * s,int charlen)9034 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
9035 {
9036 #ifdef JIM_UTF8
9037 
9038     int bytelen = utf8_index(s, charlen);
9039 
9040     Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
9041 
9042 
9043     objPtr->typePtr = &stringObjType;
9044     objPtr->internalRep.strValue.maxLength = bytelen;
9045     objPtr->internalRep.strValue.charLength = charlen;
9046 
9047     return objPtr;
9048 #else
9049     return Jim_NewStringObj(interp, s, charlen);
9050 #endif
9051 }
9052 
Jim_NewStringObjNoAlloc(Jim_Interp * interp,char * s,int len)9053 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
9054 {
9055     Jim_Obj *objPtr = Jim_NewObj(interp);
9056 
9057     objPtr->bytes = s;
9058     objPtr->length = (len == -1) ? strlen(s) : len;
9059     objPtr->typePtr = NULL;
9060     return objPtr;
9061 }
9062 
StringAppendString(Jim_Obj * objPtr,const char * str,int len)9063 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
9064 {
9065     int needlen;
9066 
9067     if (len == -1)
9068         len = strlen(str);
9069     needlen = objPtr->length + len;
9070     if (objPtr->internalRep.strValue.maxLength < needlen ||
9071         objPtr->internalRep.strValue.maxLength == 0) {
9072         needlen *= 2;
9073 
9074         if (needlen < 7) {
9075             needlen = 7;
9076         }
9077         if (objPtr->bytes == JimEmptyStringRep) {
9078             objPtr->bytes = Jim_Alloc(needlen + 1);
9079         }
9080         else {
9081             objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
9082         }
9083         objPtr->internalRep.strValue.maxLength = needlen;
9084     }
9085     memcpy(objPtr->bytes + objPtr->length, str, len);
9086     objPtr->bytes[objPtr->length + len] = '\0';
9087 
9088     if (objPtr->internalRep.strValue.charLength >= 0) {
9089 
9090         objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
9091     }
9092     objPtr->length += len;
9093 }
9094 
Jim_AppendString(Jim_Interp * interp,Jim_Obj * objPtr,const char * str,int len)9095 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
9096 {
9097     JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
9098     SetStringFromAny(interp, objPtr);
9099     StringAppendString(objPtr, str, len);
9100 }
9101 
Jim_AppendObj(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * appendObjPtr)9102 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
9103 {
9104     int len;
9105     const char *str = Jim_GetString(appendObjPtr, &len);
9106     Jim_AppendString(interp, objPtr, str, len);
9107 }
9108 
Jim_AppendStrings(Jim_Interp * interp,Jim_Obj * objPtr,...)9109 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
9110 {
9111     va_list ap;
9112 
9113     SetStringFromAny(interp, objPtr);
9114     va_start(ap, objPtr);
9115     while (1) {
9116         const char *s = va_arg(ap, const char *);
9117 
9118         if (s == NULL)
9119             break;
9120         Jim_AppendString(interp, objPtr, s, -1);
9121     }
9122     va_end(ap);
9123 }
9124 
Jim_StringEqObj(Jim_Obj * aObjPtr,Jim_Obj * bObjPtr)9125 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
9126 {
9127     if (aObjPtr == bObjPtr) {
9128         return 1;
9129     }
9130     else {
9131         int Alen, Blen;
9132         const char *sA = Jim_GetString(aObjPtr, &Alen);
9133         const char *sB = Jim_GetString(bObjPtr, &Blen);
9134 
9135         return Alen == Blen && *sA == *sB && memcmp(sA, sB, Alen) == 0;
9136     }
9137 }
9138 
Jim_StringMatchObj(Jim_Interp * interp,Jim_Obj * patternObjPtr,Jim_Obj * objPtr,int nocase)9139 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
9140 {
9141     int plen, slen;
9142     const char *pattern = Jim_GetString(patternObjPtr, &plen);
9143     const char *string = Jim_GetString(objPtr, &slen);
9144     return JimGlobMatch(pattern, plen, string, slen, nocase);
9145 }
9146 
Jim_StringCompareObj(Jim_Interp * interp,Jim_Obj * firstObjPtr,Jim_Obj * secondObjPtr,int nocase)9147 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
9148 {
9149     const char *s1 = Jim_String(firstObjPtr);
9150     int l1 = Jim_Utf8Length(interp, firstObjPtr);
9151     const char *s2 = Jim_String(secondObjPtr);
9152     int l2 = Jim_Utf8Length(interp, secondObjPtr);
9153     return JimStringCompareUtf8(s1, l1, s2, l2, nocase);
9154 }
9155 
JimRelToAbsIndex(int len,int idx)9156 static int JimRelToAbsIndex(int len, int idx)
9157 {
9158     if (idx < 0 && idx > -INT_MAX)
9159         return len + idx;
9160     return idx;
9161 }
9162 
JimRelToAbsRange(int len,int * firstPtr,int * lastPtr,int * rangeLenPtr)9163 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
9164 {
9165     int rangeLen;
9166 
9167     if (*firstPtr > *lastPtr) {
9168         rangeLen = 0;
9169     }
9170     else {
9171         rangeLen = *lastPtr - *firstPtr + 1;
9172         if (rangeLen) {
9173             if (*firstPtr < 0) {
9174                 rangeLen += *firstPtr;
9175                 *firstPtr = 0;
9176             }
9177             if (*lastPtr >= len) {
9178                 rangeLen -= (*lastPtr - (len - 1));
9179                 *lastPtr = len - 1;
9180             }
9181         }
9182     }
9183     if (rangeLen < 0)
9184         rangeLen = 0;
9185 
9186     *rangeLenPtr = rangeLen;
9187 }
9188 
JimStringGetRange(Jim_Interp * interp,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr,int len,int * first,int * last,int * range)9189 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
9190     int len, int *first, int *last, int *range)
9191 {
9192     if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
9193         return JIM_ERR;
9194     }
9195     if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
9196         return JIM_ERR;
9197     }
9198     *first = JimRelToAbsIndex(len, *first);
9199     *last = JimRelToAbsIndex(len, *last);
9200     JimRelToAbsRange(len, first, last, range);
9201     return JIM_OK;
9202 }
9203 
Jim_StringByteRangeObj(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr)9204 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
9205     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
9206 {
9207     int first, last;
9208     const char *str;
9209     int rangeLen;
9210     int bytelen;
9211 
9212     str = Jim_GetString(strObjPtr, &bytelen);
9213 
9214     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
9215         return NULL;
9216     }
9217 
9218     if (first == 0 && rangeLen == bytelen) {
9219         return strObjPtr;
9220     }
9221     return Jim_NewStringObj(interp, str + first, rangeLen);
9222 }
9223 
Jim_StringRangeObj(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr)9224 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
9225     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
9226 {
9227 #ifdef JIM_UTF8
9228     int first, last;
9229     const char *str;
9230     int len, rangeLen;
9231     int bytelen;
9232 
9233     str = Jim_GetString(strObjPtr, &bytelen);
9234     len = Jim_Utf8Length(interp, strObjPtr);
9235 
9236     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
9237         return NULL;
9238     }
9239 
9240     if (first == 0 && rangeLen == len) {
9241         return strObjPtr;
9242     }
9243     if (len == bytelen) {
9244 
9245         return Jim_NewStringObj(interp, str + first, rangeLen);
9246     }
9247     return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
9248 #else
9249     return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
9250 #endif
9251 }
9252 
JimStringReplaceObj(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr,Jim_Obj * newStrObj)9253 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
9254     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
9255 {
9256     int first, last;
9257     const char *str;
9258     int len, rangeLen;
9259     Jim_Obj *objPtr;
9260 
9261     len = Jim_Utf8Length(interp, strObjPtr);
9262 
9263     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
9264         return NULL;
9265     }
9266 
9267     if (last < first) {
9268         return strObjPtr;
9269     }
9270 
9271     str = Jim_String(strObjPtr);
9272 
9273 
9274     objPtr = Jim_NewStringObjUtf8(interp, str, first);
9275 
9276 
9277     if (newStrObj) {
9278         Jim_AppendObj(interp, objPtr, newStrObj);
9279     }
9280 
9281 
9282     Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
9283 
9284     return objPtr;
9285 }
9286 
JimStrCopyUpperLower(char * dest,const char * str,int uc)9287 static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
9288 {
9289     while (*str) {
9290         int c;
9291         str += utf8_tounicode(str, &c);
9292         dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c));
9293     }
9294     *dest = 0;
9295 }
9296 
JimStringToLower(Jim_Interp * interp,Jim_Obj * strObjPtr)9297 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
9298 {
9299     char *buf;
9300     int len;
9301     const char *str;
9302 
9303     str = Jim_GetString(strObjPtr, &len);
9304 
9305 #ifdef JIM_UTF8
9306     len *= 2;
9307 #endif
9308     buf = Jim_Alloc(len + 1);
9309     JimStrCopyUpperLower(buf, str, 0);
9310     return Jim_NewStringObjNoAlloc(interp, buf, -1);
9311 }
9312 
JimStringToUpper(Jim_Interp * interp,Jim_Obj * strObjPtr)9313 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
9314 {
9315     char *buf;
9316     const char *str;
9317     int len;
9318 
9319     str = Jim_GetString(strObjPtr, &len);
9320 
9321 #ifdef JIM_UTF8
9322     len *= 2;
9323 #endif
9324     buf = Jim_Alloc(len + 1);
9325     JimStrCopyUpperLower(buf, str, 1);
9326     return Jim_NewStringObjNoAlloc(interp, buf, -1);
9327 }
9328 
JimStringToTitle(Jim_Interp * interp,Jim_Obj * strObjPtr)9329 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
9330 {
9331     char *buf, *p;
9332     int len;
9333     int c;
9334     const char *str;
9335 
9336     str = Jim_GetString(strObjPtr, &len);
9337 
9338 #ifdef JIM_UTF8
9339     len *= 2;
9340 #endif
9341     buf = p = Jim_Alloc(len + 1);
9342 
9343     str += utf8_tounicode(str, &c);
9344     p += utf8_getchars(p, utf8_title(c));
9345 
9346     JimStrCopyUpperLower(p, str, 0);
9347 
9348     return Jim_NewStringObjNoAlloc(interp, buf, -1);
9349 }
9350 
utf8_memchr(const char * str,int len,int c)9351 static const char *utf8_memchr(const char *str, int len, int c)
9352 {
9353 #ifdef JIM_UTF8
9354     while (len) {
9355         int sc;
9356         int n = utf8_tounicode(str, &sc);
9357         if (sc == c) {
9358             return str;
9359         }
9360         str += n;
9361         len -= n;
9362     }
9363     return NULL;
9364 #else
9365     return memchr(str, c, len);
9366 #endif
9367 }
9368 
JimFindTrimLeft(const char * str,int len,const char * trimchars,int trimlen)9369 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
9370 {
9371     while (len) {
9372         int c;
9373         int n = utf8_tounicode(str, &c);
9374 
9375         if (utf8_memchr(trimchars, trimlen, c) == NULL) {
9376 
9377             break;
9378         }
9379         str += n;
9380         len -= n;
9381     }
9382     return str;
9383 }
9384 
JimFindTrimRight(const char * str,int len,const char * trimchars,int trimlen)9385 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
9386 {
9387     str += len;
9388 
9389     while (len) {
9390         int c;
9391         int n = utf8_prev_len(str, len);
9392 
9393         len -= n;
9394         str -= n;
9395 
9396         n = utf8_tounicode(str, &c);
9397 
9398         if (utf8_memchr(trimchars, trimlen, c) == NULL) {
9399             return str + n;
9400         }
9401     }
9402 
9403     return NULL;
9404 }
9405 
9406 static const char default_trim_chars[] = " \t\n\r";
9407 
9408 static int default_trim_chars_len = sizeof(default_trim_chars);
9409 
JimStringTrimLeft(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * trimcharsObjPtr)9410 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
9411 {
9412     int len;
9413     const char *str = Jim_GetString(strObjPtr, &len);
9414     const char *trimchars = default_trim_chars;
9415     int trimcharslen = default_trim_chars_len;
9416     const char *newstr;
9417 
9418     if (trimcharsObjPtr) {
9419         trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
9420     }
9421 
9422     newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
9423     if (newstr == str) {
9424         return strObjPtr;
9425     }
9426 
9427     return Jim_NewStringObj(interp, newstr, len - (newstr - str));
9428 }
9429 
JimStringTrimRight(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * trimcharsObjPtr)9430 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
9431 {
9432     int len;
9433     const char *trimchars = default_trim_chars;
9434     int trimcharslen = default_trim_chars_len;
9435     const char *nontrim;
9436 
9437     if (trimcharsObjPtr) {
9438         trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
9439     }
9440 
9441     SetStringFromAny(interp, strObjPtr);
9442 
9443     len = Jim_Length(strObjPtr);
9444     nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
9445 
9446     if (nontrim == NULL) {
9447 
9448         return Jim_NewEmptyStringObj(interp);
9449     }
9450     if (nontrim == strObjPtr->bytes + len) {
9451 
9452         return strObjPtr;
9453     }
9454 
9455     if (Jim_IsShared(strObjPtr)) {
9456         strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
9457     }
9458     else {
9459 
9460         strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
9461         strObjPtr->length = (nontrim - strObjPtr->bytes);
9462     }
9463 
9464     return strObjPtr;
9465 }
9466 
JimStringTrim(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * trimcharsObjPtr)9467 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
9468 {
9469 
9470     Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
9471 
9472 
9473     strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
9474 
9475 
9476     if (objPtr != strObjPtr && objPtr->refCount == 0) {
9477 
9478         Jim_FreeNewObj(interp, objPtr);
9479     }
9480 
9481     return strObjPtr;
9482 }
9483 
9484 
9485 #ifdef HAVE_ISASCII
9486 #define jim_isascii isascii
9487 #else
jim_isascii(int c)9488 static int jim_isascii(int c)
9489 {
9490     return !(c & ~0x7f);
9491 }
9492 #endif
9493 
JimStringIs(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * strClass,int strict)9494 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
9495 {
9496     static const char * const strclassnames[] = {
9497         "integer", "alpha", "alnum", "ascii", "digit",
9498         "double", "lower", "upper", "space", "xdigit",
9499         "control", "print", "graph", "punct", "boolean",
9500         NULL
9501     };
9502     enum {
9503         STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
9504         STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
9505         STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN,
9506     };
9507     int strclass;
9508     int len;
9509     int i;
9510     const char *str;
9511     int (*isclassfunc)(int c) = NULL;
9512 
9513     if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
9514         return JIM_ERR;
9515     }
9516 
9517     str = Jim_GetString(strObjPtr, &len);
9518     if (len == 0) {
9519         Jim_SetResultBool(interp, !strict);
9520         return JIM_OK;
9521     }
9522 
9523     switch (strclass) {
9524         case STR_IS_INTEGER:
9525             {
9526                 jim_wide w;
9527                 Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
9528                 return JIM_OK;
9529             }
9530 
9531         case STR_IS_DOUBLE:
9532             {
9533                 double d;
9534                 Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
9535                 return JIM_OK;
9536             }
9537 
9538         case STR_IS_BOOLEAN:
9539             {
9540                 int b;
9541                 Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK);
9542                 return JIM_OK;
9543             }
9544 
9545         case STR_IS_ALPHA: isclassfunc = isalpha; break;
9546         case STR_IS_ALNUM: isclassfunc = isalnum; break;
9547         case STR_IS_ASCII: isclassfunc = jim_isascii; break;
9548         case STR_IS_DIGIT: isclassfunc = isdigit; break;
9549         case STR_IS_LOWER: isclassfunc = islower; break;
9550         case STR_IS_UPPER: isclassfunc = isupper; break;
9551         case STR_IS_SPACE: isclassfunc = isspace; break;
9552         case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
9553         case STR_IS_CONTROL: isclassfunc = iscntrl; break;
9554         case STR_IS_PRINT: isclassfunc = isprint; break;
9555         case STR_IS_GRAPH: isclassfunc = isgraph; break;
9556         case STR_IS_PUNCT: isclassfunc = ispunct; break;
9557         default:
9558             return JIM_ERR;
9559     }
9560 
9561     for (i = 0; i < len; i++) {
9562         if (!isclassfunc(UCHAR(str[i]))) {
9563             Jim_SetResultBool(interp, 0);
9564             return JIM_OK;
9565         }
9566     }
9567     Jim_SetResultBool(interp, 1);
9568     return JIM_OK;
9569 }
9570 
9571 
9572 
9573 static const Jim_ObjType comparedStringObjType = {
9574     "compared-string",
9575     NULL,
9576     NULL,
9577     NULL,
9578     JIM_TYPE_REFERENCES,
9579 };
9580 
Jim_CompareStringImmediate(Jim_Interp * interp,Jim_Obj * objPtr,const char * str)9581 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
9582 {
9583     if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
9584         return 1;
9585     }
9586     else {
9587         if (strcmp(str, Jim_String(objPtr)) != 0)
9588             return 0;
9589 
9590         if (objPtr->typePtr != &comparedStringObjType) {
9591             Jim_FreeIntRep(interp, objPtr);
9592             objPtr->typePtr = &comparedStringObjType;
9593         }
9594         objPtr->internalRep.ptr = (char *)str;
9595         return 1;
9596     }
9597 }
9598 
qsortCompareStringPointers(const void * a,const void * b)9599 static int qsortCompareStringPointers(const void *a, const void *b)
9600 {
9601     char *const *sa = (char *const *)a;
9602     char *const *sb = (char *const *)b;
9603 
9604     return strcmp(*sa, *sb);
9605 }
9606 
9607 
9608 
9609 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
9610 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
9611 
9612 static const Jim_ObjType sourceObjType = {
9613     "source",
9614     FreeSourceInternalRep,
9615     DupSourceInternalRep,
9616     NULL,
9617     JIM_TYPE_REFERENCES,
9618 };
9619 
FreeSourceInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)9620 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9621 {
9622     Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
9623 }
9624 
DupSourceInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)9625 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9626 {
9627     dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
9628     Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
9629 }
9630 
9631 static const Jim_ObjType scriptLineObjType = {
9632     "scriptline",
9633     NULL,
9634     NULL,
9635     NULL,
9636     JIM_NONE,
9637 };
9638 
JimNewScriptLineObj(Jim_Interp * interp,int argc,int line)9639 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
9640 {
9641     Jim_Obj *objPtr;
9642 
9643 #ifdef DEBUG_SHOW_SCRIPT
9644     char buf[100];
9645     snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
9646     objPtr = Jim_NewStringObj(interp, buf, -1);
9647 #else
9648     objPtr = Jim_NewEmptyStringObj(interp);
9649 #endif
9650     objPtr->typePtr = &scriptLineObjType;
9651     objPtr->internalRep.scriptLineValue.argc = argc;
9652     objPtr->internalRep.scriptLineValue.line = line;
9653 
9654     return objPtr;
9655 }
9656 
9657 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
9658 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
9659 
9660 static const Jim_ObjType scriptObjType = {
9661     "script",
9662     FreeScriptInternalRep,
9663     DupScriptInternalRep,
9664     NULL,
9665     JIM_TYPE_NONE,
9666 };
9667 
9668 typedef struct ScriptToken
9669 {
9670     Jim_Obj *objPtr;
9671     int type;
9672 } ScriptToken;
9673 
9674 typedef struct ScriptObj
9675 {
9676     ScriptToken *token;
9677     Jim_Obj *fileNameObj;
9678     int len;
9679     int substFlags;
9680     int inUse;                  /* Used to share a ScriptObj. Currently
9681                                    only used by Jim_EvalObj() as protection against
9682                                    shimmering of the currently evaluated object. */
9683     int firstline;
9684     int linenr;
9685     int missing;
9686 } ScriptObj;
9687 
9688 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
9689 static int JimParseCheckMissing(Jim_Interp *interp, int ch);
9690 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
9691 static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script);
9692 
FreeScriptInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)9693 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9694 {
9695     int i;
9696     struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
9697 
9698     if (--script->inUse != 0)
9699         return;
9700     for (i = 0; i < script->len; i++) {
9701         Jim_DecrRefCount(interp, script->token[i].objPtr);
9702     }
9703     Jim_Free(script->token);
9704     Jim_DecrRefCount(interp, script->fileNameObj);
9705     Jim_Free(script);
9706 }
9707 
DupScriptInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)9708 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9709 {
9710     JIM_NOTUSED(interp);
9711     JIM_NOTUSED(srcPtr);
9712 
9713     dupPtr->typePtr = NULL;
9714 }
9715 
9716 typedef struct
9717 {
9718     const char *token;
9719     int len;
9720     int type;
9721     int line;
9722 } ParseToken;
9723 
9724 typedef struct
9725 {
9726 
9727     ParseToken *list;
9728     int size;
9729     int count;
9730     ParseToken static_list[20];
9731 } ParseTokenList;
9732 
ScriptTokenListInit(ParseTokenList * tokenlist)9733 static void ScriptTokenListInit(ParseTokenList *tokenlist)
9734 {
9735     tokenlist->list = tokenlist->static_list;
9736     tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
9737     tokenlist->count = 0;
9738 }
9739 
ScriptTokenListFree(ParseTokenList * tokenlist)9740 static void ScriptTokenListFree(ParseTokenList *tokenlist)
9741 {
9742     if (tokenlist->list != tokenlist->static_list) {
9743         Jim_Free(tokenlist->list);
9744     }
9745 }
9746 
ScriptAddToken(ParseTokenList * tokenlist,const char * token,int len,int type,int line)9747 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
9748     int line)
9749 {
9750     ParseToken *t;
9751 
9752     if (tokenlist->count == tokenlist->size) {
9753 
9754         tokenlist->size *= 2;
9755         if (tokenlist->list != tokenlist->static_list) {
9756             tokenlist->list =
9757                 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
9758         }
9759         else {
9760 
9761             tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
9762             memcpy(tokenlist->list, tokenlist->static_list,
9763                 tokenlist->count * sizeof(*tokenlist->list));
9764         }
9765     }
9766     t = &tokenlist->list[tokenlist->count++];
9767     t->token = token;
9768     t->len = len;
9769     t->type = type;
9770     t->line = line;
9771 }
9772 
JimCountWordTokens(struct ScriptObj * script,ParseToken * t)9773 static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t)
9774 {
9775     int expand = 1;
9776     int count = 0;
9777 
9778 
9779     if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
9780         if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
9781 
9782             expand = -1;
9783             t++;
9784         }
9785         else {
9786             if (script->missing == ' ') {
9787 
9788                 script->missing = '}';
9789                 script->linenr = t[1].line;
9790             }
9791         }
9792     }
9793 
9794 
9795     while (!TOKEN_IS_SEP(t->type)) {
9796         t++;
9797         count++;
9798     }
9799 
9800     return count * expand;
9801 }
9802 
JimMakeScriptObj(Jim_Interp * interp,const ParseToken * t)9803 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
9804 {
9805     Jim_Obj *objPtr;
9806 
9807     if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
9808 
9809         int len = t->len;
9810         char *str = Jim_Alloc(len + 1);
9811         len = JimEscape(str, t->token, len);
9812         objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
9813     }
9814     else {
9815         objPtr = Jim_NewStringObj(interp, t->token, t->len);
9816     }
9817     return objPtr;
9818 }
9819 
ScriptObjAddTokens(Jim_Interp * interp,struct ScriptObj * script,ParseTokenList * tokenlist)9820 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
9821     ParseTokenList *tokenlist)
9822 {
9823     int i;
9824     struct ScriptToken *token;
9825 
9826     int lineargs = 0;
9827 
9828     ScriptToken *linefirst;
9829     int count;
9830     int linenr;
9831 
9832 #ifdef DEBUG_SHOW_SCRIPT_TOKENS
9833     printf("==== Tokens ====\n");
9834     for (i = 0; i < tokenlist->count; i++) {
9835         printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
9836             tokenlist->list[i].len, tokenlist->list[i].token);
9837     }
9838 #endif
9839 
9840 
9841     count = tokenlist->count;
9842     for (i = 0; i < tokenlist->count; i++) {
9843         if (tokenlist->list[i].type == JIM_TT_EOL) {
9844             count++;
9845         }
9846     }
9847     linenr = script->firstline = tokenlist->list[0].line;
9848 
9849     token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
9850 
9851 
9852     linefirst = token++;
9853 
9854     for (i = 0; i < tokenlist->count; ) {
9855 
9856         int wordtokens;
9857 
9858 
9859         while (tokenlist->list[i].type == JIM_TT_SEP) {
9860             i++;
9861         }
9862 
9863         wordtokens = JimCountWordTokens(script, tokenlist->list + i);
9864 
9865         if (wordtokens == 0) {
9866 
9867             if (lineargs) {
9868                 linefirst->type = JIM_TT_LINE;
9869                 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
9870                 Jim_IncrRefCount(linefirst->objPtr);
9871 
9872 
9873                 lineargs = 0;
9874                 linefirst = token++;
9875             }
9876             i++;
9877             continue;
9878         }
9879         else if (wordtokens != 1) {
9880 
9881             token->type = JIM_TT_WORD;
9882             token->objPtr = Jim_NewIntObj(interp, wordtokens);
9883             Jim_IncrRefCount(token->objPtr);
9884             token++;
9885             if (wordtokens < 0) {
9886 
9887                 i++;
9888                 wordtokens = -wordtokens - 1;
9889                 lineargs--;
9890             }
9891         }
9892 
9893         if (lineargs == 0) {
9894 
9895             linenr = tokenlist->list[i].line;
9896         }
9897         lineargs++;
9898 
9899 
9900         while (wordtokens--) {
9901             const ParseToken *t = &tokenlist->list[i++];
9902 
9903             token->type = t->type;
9904             token->objPtr = JimMakeScriptObj(interp, t);
9905             Jim_IncrRefCount(token->objPtr);
9906 
9907             Jim_SetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
9908             token++;
9909         }
9910     }
9911 
9912     if (lineargs == 0) {
9913         token--;
9914     }
9915 
9916     script->len = token - script->token;
9917 
9918     JimPanic((script->len >= count, "allocated script array is too short"));
9919 
9920 #ifdef DEBUG_SHOW_SCRIPT
9921     printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj));
9922     for (i = 0; i < script->len; i++) {
9923         const ScriptToken *t = &script->token[i];
9924         printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
9925     }
9926 #endif
9927 
9928 }
9929 
Jim_ScriptIsComplete(Jim_Interp * interp,Jim_Obj * scriptObj,char * stateCharPtr)9930 int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
9931 {
9932     ScriptObj *script = JimGetScript(interp, scriptObj);
9933     if (stateCharPtr) {
9934         *stateCharPtr = script->missing;
9935     }
9936     return script->missing == ' ' || script->missing == '}';
9937 }
9938 
JimParseCheckMissing(Jim_Interp * interp,int ch)9939 static int JimParseCheckMissing(Jim_Interp *interp, int ch)
9940 {
9941     const char *msg;
9942 
9943     switch (ch) {
9944         case '\\':
9945         case ' ':
9946             return JIM_OK;
9947 
9948         case '[':
9949             msg = "unmatched \"[\"";
9950             break;
9951         case '{':
9952             msg = "missing close-brace";
9953             break;
9954         case '}':
9955             msg = "extra characters after close-brace";
9956             break;
9957         case '"':
9958         default:
9959             msg = "missing quote";
9960             break;
9961     }
9962 
9963     Jim_SetResultString(interp, msg, -1);
9964     return JIM_ERR;
9965 }
9966 
Jim_GetSourceInfo(Jim_Interp * interp,Jim_Obj * objPtr,int * lineptr)9967 Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, int *lineptr)
9968 {
9969     int line;
9970     Jim_Obj *fileNameObj;
9971 
9972     if (objPtr->typePtr == &sourceObjType) {
9973         fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
9974         line = objPtr->internalRep.sourceValue.lineNumber;
9975     }
9976     else if (objPtr->typePtr == &scriptObjType) {
9977         ScriptObj *script = JimGetScript(interp, objPtr);
9978         fileNameObj = script->fileNameObj;
9979         line = script->firstline;
9980     }
9981     else {
9982         fileNameObj = interp->emptyObj;
9983         line = 1;
9984     }
9985     *lineptr = line;
9986     return fileNameObj;
9987 }
9988 
Jim_SetSourceInfo(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * fileNameObj,int lineNumber)9989 void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
9990     Jim_Obj *fileNameObj, int lineNumber)
9991 {
9992     JimPanic((Jim_IsShared(objPtr), "Jim_SetSourceInfo called with shared object"));
9993     Jim_FreeIntRep(interp, objPtr);
9994     Jim_IncrRefCount(fileNameObj);
9995     objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
9996     objPtr->internalRep.sourceValue.lineNumber = lineNumber;
9997     objPtr->typePtr = &sourceObjType;
9998 }
9999 
SubstObjAddTokens(Jim_Interp * interp,struct ScriptObj * script,ParseTokenList * tokenlist)10000 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
10001     ParseTokenList *tokenlist)
10002 {
10003     int i;
10004     struct ScriptToken *token;
10005 
10006     token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
10007 
10008     for (i = 0; i < tokenlist->count; i++) {
10009         const ParseToken *t = &tokenlist->list[i];
10010 
10011 
10012         token->type = t->type;
10013         token->objPtr = JimMakeScriptObj(interp, t);
10014         Jim_IncrRefCount(token->objPtr);
10015         token++;
10016     }
10017 
10018     script->len = i;
10019 }
10020 
JimSetScriptFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)10021 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
10022 {
10023     int scriptTextLen;
10024     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
10025     struct JimParserCtx parser;
10026     struct ScriptObj *script;
10027     ParseTokenList tokenlist;
10028     Jim_Obj *fileNameObj;
10029     int line;
10030 
10031 
10032     fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
10033 
10034 
10035     ScriptTokenListInit(&tokenlist);
10036 
10037     JimParserInit(&parser, scriptText, scriptTextLen, line);
10038     while (!parser.eof) {
10039         JimParseScript(&parser);
10040         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
10041             parser.tline);
10042     }
10043 
10044 
10045     ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
10046 
10047 
10048     script = Jim_Alloc(sizeof(*script));
10049     memset(script, 0, sizeof(*script));
10050     script->inUse = 1;
10051     script->fileNameObj = fileNameObj;
10052     Jim_IncrRefCount(script->fileNameObj);
10053     script->missing = parser.missing.ch;
10054     script->linenr = parser.missing.line;
10055 
10056     ScriptObjAddTokens(interp, script, &tokenlist);
10057 
10058 
10059     ScriptTokenListFree(&tokenlist);
10060 
10061 
10062     Jim_FreeIntRep(interp, objPtr);
10063     Jim_SetIntRepPtr(objPtr, script);
10064     objPtr->typePtr = &scriptObjType;
10065 }
10066 
JimGetScript(Jim_Interp * interp,Jim_Obj * objPtr)10067 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
10068 {
10069     if (objPtr == interp->emptyObj) {
10070 
10071         objPtr = interp->nullScriptObj;
10072     }
10073 
10074     if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
10075         JimSetScriptFromAny(interp, objPtr);
10076     }
10077 
10078     return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
10079 }
10080 
Jim_InterpIncrProcEpoch(Jim_Interp * interp)10081 void Jim_InterpIncrProcEpoch(Jim_Interp *interp)
10082 {
10083     interp->procEpoch++;
10084 
10085 
10086     while (interp->oldCmdCache) {
10087         Jim_Cmd *next = interp->oldCmdCache->prevCmd;
10088         Jim_Free(interp->oldCmdCache);
10089         interp->oldCmdCache = next;
10090     }
10091     interp->oldCmdCacheSize = 0;
10092 }
10093 
JimIncrCmdRefCount(Jim_Cmd * cmdPtr)10094 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
10095 {
10096     cmdPtr->inUse++;
10097 }
10098 
JimDecrCmdRefCount(Jim_Interp * interp,Jim_Cmd * cmdPtr)10099 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
10100 {
10101     if (--cmdPtr->inUse == 0) {
10102         if (cmdPtr->isproc) {
10103             Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
10104             Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
10105             Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
10106             if (cmdPtr->u.proc.staticVars) {
10107                 Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
10108                 Jim_Free(cmdPtr->u.proc.staticVars);
10109             }
10110         }
10111         else {
10112 
10113             if (cmdPtr->u.native.delProc) {
10114                 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
10115             }
10116         }
10117         if (cmdPtr->prevCmd) {
10118 
10119             JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
10120         }
10121 
10122         cmdPtr->prevCmd = interp->oldCmdCache;
10123         interp->oldCmdCache = cmdPtr;
10124         if (!interp->quitting && ++interp->oldCmdCacheSize >= 1000) {
10125             Jim_InterpIncrProcEpoch(interp);
10126         }
10127     }
10128 }
10129 
JimIncrVarRef(Jim_VarVal * vv)10130 static void JimIncrVarRef(Jim_VarVal *vv)
10131 {
10132     vv->refCount++;
10133 }
10134 
JimDecrVarRef(Jim_Interp * interp,Jim_VarVal * vv)10135 static void JimDecrVarRef(Jim_Interp *interp, Jim_VarVal *vv)
10136 {
10137     assert(vv->refCount > 0);
10138     if (--vv->refCount == 0) {
10139         if (vv->objPtr) {
10140             Jim_DecrRefCount(interp, vv->objPtr);
10141         }
10142         Jim_Free(vv);
10143     }
10144 }
10145 
JimVariablesHTValDestructor(void * interp,void * val)10146 static void JimVariablesHTValDestructor(void *interp, void *val)
10147 {
10148     JimDecrVarRef(interp, val);
10149 }
10150 
JimObjectHTHashFunction(const void * key)10151 static unsigned int JimObjectHTHashFunction(const void *key)
10152 {
10153     Jim_Obj *keyObj = (Jim_Obj *)key;
10154     int length;
10155     const char *string;
10156 
10157 #ifdef JIM_OPTIMIZATION
10158     if (JimIsWide(keyObj) && keyObj->bytes == NULL) {
10159 
10160         jim_wide objValue = JimWideValue(keyObj);
10161         if (objValue > INT_MIN && objValue < INT_MAX) {
10162             unsigned result = 0;
10163             unsigned value = (unsigned)objValue;
10164 
10165             if (objValue < 0) {
10166                 value = (unsigned)-objValue;
10167             }
10168 
10169 
10170             do {
10171                 result += (result << 3) + (value % 10 + '0');
10172                 value /= 10;
10173             } while (value);
10174 
10175             if (objValue < 0) {
10176                 result += (result << 3) + '-';
10177             }
10178             return result;
10179         }
10180     }
10181 #endif
10182     string = Jim_GetString(keyObj, &length);
10183     return Jim_GenHashFunction((const unsigned char *)string, length);
10184 }
10185 
JimObjectHTKeyCompare(void * privdata,const void * key1,const void * key2)10186 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
10187 {
10188     return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
10189 }
10190 
JimObjectHTKeyValDup(void * privdata,const void * val)10191 static void *JimObjectHTKeyValDup(void *privdata, const void *val)
10192 {
10193     Jim_IncrRefCount((Jim_Obj *)val);
10194     return (void *)val;
10195 }
10196 
JimObjectHTKeyValDestructor(void * interp,void * val)10197 static void JimObjectHTKeyValDestructor(void *interp, void *val)
10198 {
10199     Jim_DecrRefCount(interp, (Jim_Obj *)val);
10200 }
10201 
10202 
JimVariablesHTValDup(void * privdata,const void * val)10203 static void *JimVariablesHTValDup(void *privdata, const void *val)
10204 {
10205     JimIncrVarRef((Jim_VarVal *)val);
10206     return (void *)val;
10207 }
10208 
10209 static const Jim_HashTableType JimVariablesHashTableType = {
10210     JimObjectHTHashFunction,
10211     JimObjectHTKeyValDup,
10212     JimVariablesHTValDup,
10213     JimObjectHTKeyCompare,
10214     JimObjectHTKeyValDestructor,
10215     JimVariablesHTValDestructor
10216 };
10217 
10218 
Jim_GetStringNoQualifier(Jim_Obj * objPtr,int * length)10219 static const char *Jim_GetStringNoQualifier(Jim_Obj *objPtr, int *length)
10220 {
10221     int len;
10222     const char *str = Jim_GetString(objPtr, &len);
10223     if (len >= 2 && str[0] == ':' && str[1] == ':') {
10224         while (len && *str == ':') {
10225             len--;
10226             str++;
10227         }
10228     }
10229     *length = len;
10230     return str;
10231 }
10232 
JimCommandsHT_HashFunction(const void * key)10233 static unsigned int JimCommandsHT_HashFunction(const void *key)
10234 {
10235     int len;
10236     const char *str = Jim_GetStringNoQualifier((Jim_Obj *)key, &len);
10237     return Jim_GenHashFunction((const unsigned char *)str, len);
10238 }
10239 
JimCommandsHT_KeyCompare(void * privdata,const void * key1,const void * key2)10240 static int JimCommandsHT_KeyCompare(void *privdata, const void *key1, const void *key2)
10241 {
10242     int len1, len2;
10243     const char *str1 = Jim_GetStringNoQualifier((Jim_Obj *)key1, &len1);
10244     const char *str2 = Jim_GetStringNoQualifier((Jim_Obj *)key2, &len2);
10245     return len1 == len2 && *str1 == *str2 && memcmp(str1, str2, len1) == 0;
10246 }
10247 
JimCommandsHT_ValDestructor(void * interp,void * val)10248 static void JimCommandsHT_ValDestructor(void *interp, void *val)
10249 {
10250     JimDecrCmdRefCount(interp, val);
10251 }
10252 
10253 static const Jim_HashTableType JimCommandsHashTableType = {
10254     JimCommandsHT_HashFunction,
10255     JimObjectHTKeyValDup,
10256     NULL,
10257     JimCommandsHT_KeyCompare,
10258     JimObjectHTKeyValDestructor,
10259     JimCommandsHT_ValDestructor
10260 };
10261 
10262 
10263 
Jim_MakeGlobalNamespaceName(Jim_Interp * interp,Jim_Obj * nameObjPtr)10264 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
10265 {
10266 #ifdef jim_ext_namespace
10267     Jim_Obj *resultObj;
10268 
10269     const char *name = Jim_String(nameObjPtr);
10270     if (name[0] == ':' && name[1] == ':') {
10271         return nameObjPtr;
10272     }
10273     Jim_IncrRefCount(nameObjPtr);
10274     resultObj = Jim_NewStringObj(interp, "::", -1);
10275     Jim_AppendObj(interp, resultObj, nameObjPtr);
10276     Jim_DecrRefCount(interp, nameObjPtr);
10277 
10278     return resultObj;
10279 #else
10280     return nameObjPtr;
10281 #endif
10282 }
10283 
JimQualifyName(Jim_Interp * interp,Jim_Obj * objPtr)10284 static Jim_Obj *JimQualifyName(Jim_Interp *interp, Jim_Obj *objPtr)
10285 {
10286 #ifdef jim_ext_namespace
10287     if (Jim_Length(interp->framePtr->nsObj)) {
10288         int len;
10289         const char *name = Jim_GetString(objPtr, &len);
10290         if (len < 2 || name[0] != ':' || name[1] != ':') {
10291 
10292             objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
10293             Jim_AppendStrings(interp, objPtr, "::", name, NULL);
10294         }
10295     }
10296 #endif
10297     Jim_IncrRefCount(objPtr);
10298     return objPtr;
10299 }
10300 
JimCreateCommand(Jim_Interp * interp,Jim_Obj * nameObjPtr,Jim_Cmd * cmd)10301 static void JimCreateCommand(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Cmd *cmd)
10302 {
10303     JimPanic((nameObjPtr->refCount == 0, "JimCreateCommand called with zero ref count name"));
10304 
10305     if (interp->local) {
10306         Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, nameObjPtr);
10307         if (he) {
10308 
10309             cmd->prevCmd = Jim_GetHashEntryVal(he);
10310             Jim_SetHashVal(&interp->commands, he, cmd);
10311 
10312             Jim_InterpIncrProcEpoch(interp);
10313             return;
10314         }
10315     }
10316 
10317 
10318 
10319     Jim_ReplaceHashEntry(&interp->commands, nameObjPtr, cmd);
10320 }
10321 
Jim_CreateCommandObj(Jim_Interp * interp,Jim_Obj * cmdNameObj,Jim_CmdProc * cmdProc,void * privData,Jim_DelCmdProc * delProc)10322 int Jim_CreateCommandObj(Jim_Interp *interp, Jim_Obj *cmdNameObj,
10323     Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
10324 {
10325     Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
10326 
10327 
10328     memset(cmdPtr, 0, sizeof(*cmdPtr));
10329     cmdPtr->inUse = 1;
10330     cmdPtr->u.native.delProc = delProc;
10331     cmdPtr->u.native.cmdProc = cmdProc;
10332     cmdPtr->u.native.privData = privData;
10333 
10334     Jim_IncrRefCount(cmdNameObj);
10335     JimCreateCommand(interp, cmdNameObj, cmdPtr);
10336     Jim_DecrRefCount(interp, cmdNameObj);
10337 
10338     return JIM_OK;
10339 }
10340 
10341 
Jim_CreateCommand(Jim_Interp * interp,const char * cmdNameStr,Jim_CmdProc * cmdProc,void * privData,Jim_DelCmdProc * delProc)10342 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
10343     Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
10344 {
10345     return Jim_CreateCommandObj(interp, Jim_NewStringObj(interp, cmdNameStr, -1), cmdProc, privData, delProc);
10346 }
10347 
JimCreateProcedureStatics(Jim_Interp * interp,Jim_Cmd * cmdPtr,Jim_Obj * staticsListObjPtr)10348 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
10349 {
10350     int len, i;
10351 
10352     len = Jim_ListLength(interp, staticsListObjPtr);
10353     if (len == 0) {
10354         return JIM_OK;
10355     }
10356 
10357     cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
10358     Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
10359     for (i = 0; i < len; i++) {
10360         Jim_Obj *initObjPtr = NULL;
10361         Jim_Obj *nameObjPtr;
10362         Jim_VarVal *vv = NULL;
10363         Jim_Obj *objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
10364         int subLen = Jim_ListLength(interp, objPtr);
10365         int byref = 0;
10366 
10367 
10368         if (subLen != 1 && subLen != 2) {
10369             Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
10370                 objPtr);
10371             return JIM_ERR;
10372         }
10373 
10374         nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
10375 
10376 
10377         if (subLen == 1) {
10378             int len;
10379             const char *pt = Jim_GetString(nameObjPtr, &len);
10380             if (*pt == '&') {
10381 
10382                 nameObjPtr = Jim_NewStringObj(interp, pt + 1, len - 1);
10383                 byref = 1;
10384             }
10385         }
10386         Jim_IncrRefCount(nameObjPtr);
10387 
10388         if (subLen == 1) {
10389             switch (SetVariableFromAny(interp, nameObjPtr)) {
10390                 case JIM_DICT_SUGAR:
10391 
10392                     if (byref) {
10393                         Jim_SetResultFormatted(interp, "Can't link to array element \"%#s\"", nameObjPtr);
10394                     }
10395                     else {
10396                         Jim_SetResultFormatted(interp, "Can't initialise array element \"%#s\"", nameObjPtr);
10397                     }
10398                     Jim_DecrRefCount(interp, nameObjPtr);
10399                     return JIM_ERR;
10400 
10401                 case JIM_OK:
10402                     if (byref) {
10403                         vv = nameObjPtr->internalRep.varValue.vv;
10404                     }
10405                     else {
10406                         initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
10407                     }
10408                     break;
10409 
10410                 case JIM_ERR:
10411 
10412                     Jim_SetResultFormatted(interp,
10413                         "variable for initialization of static \"%#s\" not found in the local context",
10414                         nameObjPtr);
10415                     Jim_DecrRefCount(interp, nameObjPtr);
10416                     return JIM_ERR;
10417             }
10418         }
10419         else {
10420             initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
10421         }
10422 
10423         if (vv == NULL) {
10424             vv = Jim_Alloc(sizeof(*vv));
10425             vv->objPtr = initObjPtr;
10426             Jim_IncrRefCount(vv->objPtr);
10427             vv->linkFramePtr = NULL;
10428             vv->refCount = 0;
10429         }
10430 
10431         if (JimSetNewVariable(cmdPtr->u.proc.staticVars, nameObjPtr, vv) != JIM_OK) {
10432             Jim_SetResultFormatted(interp,
10433                 "static variable name \"%#s\" duplicated in statics list", nameObjPtr);
10434             JimIncrVarRef(vv);
10435             JimDecrVarRef(interp, vv);
10436             Jim_DecrRefCount(interp, nameObjPtr);
10437             return JIM_ERR;
10438         }
10439 
10440         Jim_DecrRefCount(interp, nameObjPtr);
10441     }
10442     return JIM_OK;
10443 }
10444 
10445 
10446 #ifdef jim_ext_namespace
Jim_memrchr(const char * p,int c,int len)10447 static const char *Jim_memrchr(const char *p, int c, int len)
10448 {
10449     int i;
10450     for (i = len; i > 0; i--) {
10451         if (p[i] == c) {
10452             return p + i;
10453         }
10454     }
10455     return NULL;
10456 }
10457 #endif
10458 
JimUpdateProcNamespace(Jim_Interp * interp,Jim_Cmd * cmdPtr,Jim_Obj * nameObjPtr)10459 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *nameObjPtr)
10460 {
10461 #ifdef jim_ext_namespace
10462     if (cmdPtr->isproc) {
10463         int len;
10464         const char *cmdname = Jim_GetStringNoQualifier(nameObjPtr, &len);
10465 
10466         const char *pt = Jim_memrchr(cmdname, ':', len);
10467         if (pt && pt != cmdname && pt[-1] == ':') {
10468             pt++;
10469             Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
10470             cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 2);
10471             Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
10472 
10473             Jim_Obj *tempObj = Jim_NewStringObj(interp, pt, len - (pt - cmdname));
10474             if (Jim_FindHashEntry(&interp->commands, tempObj)) {
10475 
10476                 Jim_InterpIncrProcEpoch(interp);
10477             }
10478             Jim_FreeNewObj(interp, tempObj);
10479         }
10480     }
10481 #endif
10482 }
10483 
JimCreateProcedureCmd(Jim_Interp * interp,Jim_Obj * argListObjPtr,Jim_Obj * staticsListObjPtr,Jim_Obj * bodyObjPtr,Jim_Obj * nsObj)10484 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
10485     Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
10486 {
10487     Jim_Cmd *cmdPtr;
10488     int argListLen;
10489     int i;
10490 
10491     argListLen = Jim_ListLength(interp, argListObjPtr);
10492 
10493 
10494     cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
10495     assert(cmdPtr);
10496     memset(cmdPtr, 0, sizeof(*cmdPtr));
10497     cmdPtr->inUse = 1;
10498     cmdPtr->isproc = 1;
10499     cmdPtr->u.proc.argListObjPtr = argListObjPtr;
10500     cmdPtr->u.proc.argListLen = argListLen;
10501     cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
10502     cmdPtr->u.proc.argsPos = -1;
10503     cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
10504     cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
10505     Jim_IncrRefCount(argListObjPtr);
10506     Jim_IncrRefCount(bodyObjPtr);
10507     Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
10508 
10509 
10510     if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
10511         goto err;
10512     }
10513 
10514 
10515 
10516     for (i = 0; i < argListLen; i++) {
10517         Jim_Obj *argPtr;
10518         Jim_Obj *nameObjPtr;
10519         Jim_Obj *defaultObjPtr;
10520         int len;
10521 
10522 
10523         argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
10524         len = Jim_ListLength(interp, argPtr);
10525         if (len == 0) {
10526             Jim_SetResultString(interp, "argument with no name", -1);
10527 err:
10528             JimDecrCmdRefCount(interp, cmdPtr);
10529             return NULL;
10530         }
10531         if (len > 2) {
10532             Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
10533             goto err;
10534         }
10535 
10536         if (len == 2) {
10537 
10538             nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
10539             defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
10540         }
10541         else {
10542 
10543             nameObjPtr = argPtr;
10544             defaultObjPtr = NULL;
10545         }
10546 
10547 
10548         if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
10549             if (cmdPtr->u.proc.argsPos >= 0) {
10550                 Jim_SetResultString(interp, "'args' specified more than once", -1);
10551                 goto err;
10552             }
10553             cmdPtr->u.proc.argsPos = i;
10554         }
10555         else {
10556             if (len == 2) {
10557                 cmdPtr->u.proc.optArity++;
10558             }
10559             else {
10560                 cmdPtr->u.proc.reqArity++;
10561             }
10562         }
10563 
10564         cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
10565         cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
10566     }
10567 
10568     return cmdPtr;
10569 }
10570 
Jim_DeleteCommand(Jim_Interp * interp,Jim_Obj * nameObj)10571 int Jim_DeleteCommand(Jim_Interp *interp, Jim_Obj *nameObj)
10572 {
10573     int ret = JIM_OK;
10574 
10575     nameObj = JimQualifyName(interp, nameObj);
10576 
10577     if (Jim_DeleteHashEntry(&interp->commands, nameObj) == JIM_ERR) {
10578         Jim_SetResultFormatted(interp, "can't delete \"%#s\": command doesn't exist", nameObj);
10579         ret = JIM_ERR;
10580     }
10581     Jim_DecrRefCount(interp, nameObj);
10582 
10583     return ret;
10584 }
10585 
Jim_RenameCommand(Jim_Interp * interp,Jim_Obj * oldNameObj,Jim_Obj * newNameObj)10586 int Jim_RenameCommand(Jim_Interp *interp, Jim_Obj *oldNameObj, Jim_Obj *newNameObj)
10587 {
10588     int ret = JIM_ERR;
10589     Jim_HashEntry *he;
10590     Jim_Cmd *cmdPtr;
10591 
10592     if (Jim_Length(newNameObj) == 0) {
10593         return Jim_DeleteCommand(interp, oldNameObj);
10594     }
10595 
10596 
10597 
10598     oldNameObj = JimQualifyName(interp, oldNameObj);
10599     newNameObj = JimQualifyName(interp, newNameObj);
10600 
10601 
10602     he = Jim_FindHashEntry(&interp->commands, oldNameObj);
10603     if (he == NULL) {
10604         Jim_SetResultFormatted(interp, "can't rename \"%#s\": command doesn't exist", oldNameObj);
10605     }
10606     else if (Jim_FindHashEntry(&interp->commands, newNameObj)) {
10607         Jim_SetResultFormatted(interp, "can't rename to \"%#s\": command already exists", newNameObj);
10608     }
10609     else {
10610         cmdPtr = Jim_GetHashEntryVal(he);
10611         if (cmdPtr->prevCmd) {
10612             Jim_SetResultFormatted(interp, "can't rename local command \"%#s\"", oldNameObj);
10613         }
10614         else {
10615 
10616             JimIncrCmdRefCount(cmdPtr);
10617             JimUpdateProcNamespace(interp, cmdPtr, newNameObj);
10618             Jim_AddHashEntry(&interp->commands, newNameObj, cmdPtr);
10619 
10620 
10621             Jim_DeleteHashEntry(&interp->commands, oldNameObj);
10622 
10623 
10624             Jim_InterpIncrProcEpoch(interp);
10625 
10626             ret = JIM_OK;
10627         }
10628     }
10629 
10630     Jim_DecrRefCount(interp, oldNameObj);
10631     Jim_DecrRefCount(interp, newNameObj);
10632 
10633     return ret;
10634 }
10635 
10636 
FreeCommandInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)10637 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
10638 {
10639     Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
10640 }
10641 
DupCommandInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)10642 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
10643 {
10644     dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
10645     dupPtr->typePtr = srcPtr->typePtr;
10646     Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
10647 }
10648 
10649 static const Jim_ObjType commandObjType = {
10650     "command",
10651     FreeCommandInternalRep,
10652     DupCommandInternalRep,
10653     NULL,
10654     JIM_TYPE_REFERENCES,
10655 };
10656 
Jim_GetCommand(Jim_Interp * interp,Jim_Obj * objPtr,int flags)10657 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10658 {
10659     Jim_Cmd *cmd;
10660 
10661     if (objPtr->typePtr == &commandObjType
10662         && objPtr->internalRep.cmdValue.procEpoch == interp->procEpoch
10663 #ifdef jim_ext_namespace
10664         && Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
10665 #endif
10666         && objPtr->internalRep.cmdValue.cmdPtr->inUse) {
10667 
10668         cmd = objPtr->internalRep.cmdValue.cmdPtr;
10669     }
10670     else {
10671         Jim_Obj *qualifiedNameObj = JimQualifyName(interp, objPtr);
10672         Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, qualifiedNameObj);
10673 #ifdef jim_ext_namespace
10674         if (he == NULL && Jim_Length(interp->framePtr->nsObj)) {
10675             he = Jim_FindHashEntry(&interp->commands, objPtr);
10676         }
10677 #endif
10678         if (he == NULL) {
10679             if (flags & JIM_ERRMSG) {
10680                 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
10681             }
10682             Jim_DecrRefCount(interp, qualifiedNameObj);
10683             return NULL;
10684         }
10685         cmd = Jim_GetHashEntryVal(he);
10686 
10687         cmd->cmdNameObj = Jim_GetHashEntryKey(he);
10688 
10689 
10690         Jim_FreeIntRep(interp, objPtr);
10691         objPtr->typePtr = &commandObjType;
10692         objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
10693         objPtr->internalRep.cmdValue.cmdPtr = cmd;
10694         objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
10695         Jim_IncrRefCount(interp->framePtr->nsObj);
10696         Jim_DecrRefCount(interp, qualifiedNameObj);
10697     }
10698     while (cmd->u.proc.upcall) {
10699         cmd = cmd->prevCmd;
10700     }
10701     return cmd;
10702 }
10703 
10704 
10705 
10706 static const Jim_ObjType variableObjType = {
10707     "variable",
10708     NULL,
10709     NULL,
10710     NULL,
10711     JIM_TYPE_REFERENCES,
10712 };
10713 
SetVariableFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)10714 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
10715 {
10716     const char *varName;
10717     Jim_CallFrame *framePtr;
10718     int global;
10719     int len;
10720     Jim_VarVal *vv;
10721 
10722 
10723     if (objPtr->typePtr == &variableObjType) {
10724         framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
10725         if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
10726 
10727             return JIM_OK;
10728         }
10729 
10730     }
10731     else if (objPtr->typePtr == &dictSubstObjType) {
10732         return JIM_DICT_SUGAR;
10733     }
10734 
10735     varName = Jim_GetString(objPtr, &len);
10736 
10737 
10738     if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
10739         return JIM_DICT_SUGAR;
10740     }
10741 
10742     if (varName[0] == ':' && varName[1] == ':') {
10743         while (*varName == ':') {
10744             varName++;
10745             len--;
10746         }
10747         global = 1;
10748         framePtr = interp->topFramePtr;
10749 
10750         Jim_Obj *tempObj = Jim_NewStringObj(interp, varName, len);
10751         vv = JimFindVariable(&framePtr->vars, tempObj);
10752         Jim_FreeNewObj(interp, tempObj);
10753     }
10754     else {
10755         global = 0;
10756         framePtr = interp->framePtr;
10757 
10758         vv = JimFindVariable(&framePtr->vars, objPtr);
10759         if (vv == NULL && framePtr->staticVars) {
10760 
10761             vv = JimFindVariable(framePtr->staticVars, objPtr);
10762         }
10763     }
10764 
10765     if (vv == NULL) {
10766         return JIM_ERR;
10767     }
10768 
10769 
10770     Jim_FreeIntRep(interp, objPtr);
10771     objPtr->typePtr = &variableObjType;
10772     objPtr->internalRep.varValue.callFrameId = framePtr->id;
10773     objPtr->internalRep.varValue.vv = vv;
10774     objPtr->internalRep.varValue.global = global;
10775     return JIM_OK;
10776 }
10777 
10778 
10779 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
10780 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
10781 
JimSetNewVariable(Jim_HashTable * ht,Jim_Obj * nameObjPtr,Jim_VarVal * vv)10782 static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv)
10783 {
10784     return Jim_AddHashEntry(ht, nameObjPtr, vv);
10785 }
10786 
JimFindVariable(Jim_HashTable * ht,Jim_Obj * nameObjPtr)10787 static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
10788 {
10789     Jim_HashEntry *he = Jim_FindHashEntry(ht, nameObjPtr);
10790     if (he) {
10791         return (Jim_VarVal *)Jim_GetHashEntryVal(he);
10792     }
10793     return NULL;
10794 }
10795 
JimUnsetVariable(Jim_HashTable * ht,Jim_Obj * nameObjPtr)10796 static int JimUnsetVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
10797 {
10798     return Jim_DeleteHashEntry(ht, nameObjPtr);
10799 }
10800 
JimCreateVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,Jim_Obj * valObjPtr)10801 static Jim_VarVal *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
10802 {
10803     const char *name;
10804     Jim_CallFrame *framePtr;
10805     int global;
10806     int len;
10807 
10808 
10809     Jim_VarVal *vv = Jim_Alloc(sizeof(*vv));
10810 
10811     vv->objPtr = valObjPtr;
10812     Jim_IncrRefCount(valObjPtr);
10813     vv->linkFramePtr = NULL;
10814     vv->refCount = 0;
10815 
10816     name = Jim_GetString(nameObjPtr, &len);
10817     if (name[0] == ':' && name[1] == ':') {
10818         while (*name == ':') {
10819             name++;
10820             len--;
10821         }
10822         framePtr = interp->topFramePtr;
10823         global = 1;
10824         JimSetNewVariable(&framePtr->vars, Jim_NewStringObj(interp, name, len), vv);
10825     }
10826     else {
10827         framePtr = interp->framePtr;
10828         global = 0;
10829         JimSetNewVariable(&framePtr->vars, nameObjPtr, vv);
10830     }
10831 
10832 
10833     Jim_FreeIntRep(interp, nameObjPtr);
10834     nameObjPtr->typePtr = &variableObjType;
10835     nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
10836     nameObjPtr->internalRep.varValue.vv = vv;
10837     nameObjPtr->internalRep.varValue.global = global;
10838 
10839     return vv;
10840 }
10841 
Jim_SetVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,Jim_Obj * valObjPtr)10842 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
10843 {
10844     int err;
10845     Jim_VarVal *vv;
10846 
10847     switch (SetVariableFromAny(interp, nameObjPtr)) {
10848         case JIM_DICT_SUGAR:
10849             return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
10850 
10851         case JIM_ERR:
10852             JimCreateVariable(interp, nameObjPtr, valObjPtr);
10853             break;
10854 
10855         case JIM_OK:
10856             vv = nameObjPtr->internalRep.varValue.vv;
10857             if (vv->linkFramePtr == NULL) {
10858                 Jim_IncrRefCount(valObjPtr);
10859                 Jim_DecrRefCount(interp, vv->objPtr);
10860                 vv->objPtr = valObjPtr;
10861             }
10862             else {
10863                 Jim_CallFrame *savedCallFrame;
10864 
10865                 savedCallFrame = interp->framePtr;
10866                 interp->framePtr = vv->linkFramePtr;
10867                 err = Jim_SetVariable(interp, vv->objPtr, valObjPtr);
10868                 interp->framePtr = savedCallFrame;
10869                 if (err != JIM_OK)
10870                     return err;
10871             }
10872     }
10873     return JIM_OK;
10874 }
10875 
Jim_SetVariableStr(Jim_Interp * interp,const char * name,Jim_Obj * objPtr)10876 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
10877 {
10878     Jim_Obj *nameObjPtr;
10879     int result;
10880 
10881     nameObjPtr = Jim_NewStringObj(interp, name, -1);
10882     Jim_IncrRefCount(nameObjPtr);
10883     result = Jim_SetVariable(interp, nameObjPtr, objPtr);
10884     Jim_DecrRefCount(interp, nameObjPtr);
10885     return result;
10886 }
10887 
Jim_SetGlobalVariableStr(Jim_Interp * interp,const char * name,Jim_Obj * objPtr)10888 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
10889 {
10890     Jim_CallFrame *savedFramePtr;
10891     int result;
10892 
10893     savedFramePtr = interp->framePtr;
10894     interp->framePtr = interp->topFramePtr;
10895     result = Jim_SetVariableStr(interp, name, objPtr);
10896     interp->framePtr = savedFramePtr;
10897     return result;
10898 }
10899 
Jim_SetVariableStrWithStr(Jim_Interp * interp,const char * name,const char * val)10900 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
10901 {
10902     Jim_Obj *valObjPtr;
10903     int result;
10904 
10905     valObjPtr = Jim_NewStringObj(interp, val, -1);
10906     Jim_IncrRefCount(valObjPtr);
10907     result = Jim_SetVariableStr(interp, name, valObjPtr);
10908     Jim_DecrRefCount(interp, valObjPtr);
10909     return result;
10910 }
10911 
Jim_SetVariableLink(Jim_Interp * interp,Jim_Obj * nameObjPtr,Jim_Obj * targetNameObjPtr,Jim_CallFrame * targetCallFrame)10912 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
10913     Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
10914 {
10915     const char *varName;
10916     const char *targetName;
10917     Jim_CallFrame *framePtr;
10918     Jim_VarVal *vv;
10919     int len;
10920     int varnamelen;
10921 
10922 
10923     switch (SetVariableFromAny(interp, nameObjPtr)) {
10924         case JIM_DICT_SUGAR:
10925 
10926             Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
10927             return JIM_ERR;
10928 
10929         case JIM_OK:
10930             vv = nameObjPtr->internalRep.varValue.vv;
10931 
10932             if (vv->linkFramePtr == NULL) {
10933                 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
10934                 return JIM_ERR;
10935             }
10936 
10937 
10938             vv->linkFramePtr = NULL;
10939             break;
10940     }
10941 
10942 
10943 
10944     varName = Jim_GetString(nameObjPtr, &varnamelen);
10945 
10946     if (varName[0] == ':' && varName[1] == ':') {
10947         while (*varName == ':') {
10948             varName++;
10949             varnamelen--;
10950         }
10951 
10952         framePtr = interp->topFramePtr;
10953     }
10954     else {
10955         framePtr = interp->framePtr;
10956     }
10957 
10958     targetName = Jim_GetString(targetNameObjPtr, &len);
10959     if (targetName[0] == ':' && targetName[1] == ':') {
10960         while (*targetName == ':') {
10961             targetName++;
10962             len--;
10963         }
10964         targetNameObjPtr = Jim_NewStringObj(interp, targetName, len);
10965         targetCallFrame = interp->topFramePtr;
10966     }
10967     Jim_IncrRefCount(targetNameObjPtr);
10968 
10969     if (framePtr->level < targetCallFrame->level) {
10970         Jim_SetResultFormatted(interp,
10971             "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
10972             nameObjPtr);
10973         Jim_DecrRefCount(interp, targetNameObjPtr);
10974         return JIM_ERR;
10975     }
10976 
10977 
10978     if (framePtr == targetCallFrame) {
10979         Jim_Obj *objPtr = targetNameObjPtr;
10980 
10981 
10982         while (1) {
10983             if (Jim_Length(objPtr) == varnamelen && memcmp(Jim_String(objPtr), varName, varnamelen) == 0) {
10984                 Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
10985                 Jim_DecrRefCount(interp, targetNameObjPtr);
10986                 return JIM_ERR;
10987             }
10988             if (SetVariableFromAny(interp, objPtr) != JIM_OK)
10989                 break;
10990             vv = objPtr->internalRep.varValue.vv;
10991             if (vv->linkFramePtr != targetCallFrame)
10992                 break;
10993             objPtr = vv->objPtr;
10994         }
10995     }
10996 
10997 
10998     Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
10999 
11000     nameObjPtr->internalRep.varValue.vv->linkFramePtr = targetCallFrame;
11001     Jim_DecrRefCount(interp, targetNameObjPtr);
11002     return JIM_OK;
11003 }
11004 
Jim_GetVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,int flags)11005 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
11006 {
11007     if (interp->safeexpr) {
11008         return nameObjPtr;
11009     }
11010     switch (SetVariableFromAny(interp, nameObjPtr)) {
11011         case JIM_OK:{
11012                 Jim_VarVal *vv = nameObjPtr->internalRep.varValue.vv;
11013 
11014                 if (vv->linkFramePtr == NULL) {
11015                     return vv->objPtr;
11016                 }
11017                 else {
11018                     Jim_Obj *objPtr;
11019 
11020 
11021                     Jim_CallFrame *savedCallFrame = interp->framePtr;
11022 
11023                     interp->framePtr = vv->linkFramePtr;
11024                     objPtr = Jim_GetVariable(interp, vv->objPtr, flags);
11025                     interp->framePtr = savedCallFrame;
11026                     if (objPtr) {
11027                         return objPtr;
11028                     }
11029 
11030                 }
11031             }
11032             break;
11033 
11034         case JIM_DICT_SUGAR:
11035 
11036             return JimDictSugarGet(interp, nameObjPtr, flags);
11037     }
11038     if (flags & JIM_ERRMSG) {
11039         Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
11040     }
11041     return NULL;
11042 }
11043 
Jim_GetGlobalVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,int flags)11044 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
11045 {
11046     Jim_CallFrame *savedFramePtr;
11047     Jim_Obj *objPtr;
11048 
11049     savedFramePtr = interp->framePtr;
11050     interp->framePtr = interp->topFramePtr;
11051     objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
11052     interp->framePtr = savedFramePtr;
11053 
11054     return objPtr;
11055 }
11056 
Jim_GetVariableStr(Jim_Interp * interp,const char * name,int flags)11057 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
11058 {
11059     Jim_Obj *nameObjPtr, *varObjPtr;
11060 
11061     nameObjPtr = Jim_NewStringObj(interp, name, -1);
11062     Jim_IncrRefCount(nameObjPtr);
11063     varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
11064     Jim_DecrRefCount(interp, nameObjPtr);
11065     return varObjPtr;
11066 }
11067 
Jim_GetGlobalVariableStr(Jim_Interp * interp,const char * name,int flags)11068 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
11069 {
11070     Jim_CallFrame *savedFramePtr;
11071     Jim_Obj *objPtr;
11072 
11073     savedFramePtr = interp->framePtr;
11074     interp->framePtr = interp->topFramePtr;
11075     objPtr = Jim_GetVariableStr(interp, name, flags);
11076     interp->framePtr = savedFramePtr;
11077 
11078     return objPtr;
11079 }
11080 
Jim_UnsetVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,int flags)11081 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
11082 {
11083     Jim_VarVal *vv;
11084     int retval;
11085     Jim_CallFrame *framePtr;
11086 
11087     retval = SetVariableFromAny(interp, nameObjPtr);
11088     if (retval == JIM_DICT_SUGAR) {
11089 
11090         return JimDictSugarSet(interp, nameObjPtr, NULL);
11091     }
11092     else if (retval == JIM_OK) {
11093         vv = nameObjPtr->internalRep.varValue.vv;
11094 
11095 
11096         if (vv->linkFramePtr) {
11097             framePtr = interp->framePtr;
11098             interp->framePtr = vv->linkFramePtr;
11099             retval = Jim_UnsetVariable(interp, vv->objPtr, JIM_NONE);
11100             interp->framePtr = framePtr;
11101         }
11102         else {
11103             if (nameObjPtr->internalRep.varValue.global) {
11104                 int len;
11105                 const char *name = Jim_GetString(nameObjPtr, &len);
11106                 while (*name == ':') {
11107                     name++;
11108                     len--;
11109                 }
11110                 framePtr = interp->topFramePtr;
11111                 Jim_Obj *tempObj = Jim_NewStringObj(interp, name, len);
11112                 retval = JimUnsetVariable(&framePtr->vars, tempObj);
11113                 Jim_FreeNewObj(interp, tempObj);
11114             }
11115             else {
11116                 framePtr = interp->framePtr;
11117                 retval = JimUnsetVariable(&framePtr->vars, nameObjPtr);
11118             }
11119 
11120             if (retval == JIM_OK) {
11121 
11122                 framePtr->id = interp->callFrameEpoch++;
11123             }
11124         }
11125     }
11126     if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
11127         Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
11128     }
11129     return retval;
11130 }
11131 
11132 
11133 
JimDictSugarParseVarKey(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj ** varPtrPtr,Jim_Obj ** keyPtrPtr)11134 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
11135     Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
11136 {
11137     const char *str, *p;
11138     int len, keyLen;
11139     Jim_Obj *varObjPtr, *keyObjPtr;
11140 
11141     str = Jim_GetString(objPtr, &len);
11142 
11143     p = strchr(str, '(');
11144     JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
11145 
11146     varObjPtr = Jim_NewStringObj(interp, str, p - str);
11147 
11148     p++;
11149     keyLen = (str + len) - p;
11150     if (str[len - 1] == ')') {
11151         keyLen--;
11152     }
11153 
11154 
11155     keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
11156 
11157     Jim_IncrRefCount(varObjPtr);
11158     Jim_IncrRefCount(keyObjPtr);
11159     *varPtrPtr = varObjPtr;
11160     *keyPtrPtr = keyObjPtr;
11161 }
11162 
JimDictSugarSet(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * valObjPtr)11163 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
11164 {
11165     int err;
11166 
11167     SetDictSubstFromAny(interp, objPtr);
11168 
11169     err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
11170         &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);
11171 
11172     if (err == JIM_OK) {
11173 
11174         Jim_SetEmptyResult(interp);
11175     }
11176     else {
11177         if (!valObjPtr) {
11178 
11179             if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
11180                 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
11181                     objPtr);
11182                 return err;
11183             }
11184         }
11185 
11186         Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
11187             (valObjPtr ? "set" : "unset"), objPtr);
11188     }
11189     return err;
11190 }
11191 
JimDictExpandArrayVariable(Jim_Interp * interp,Jim_Obj * varObjPtr,Jim_Obj * keyObjPtr,int flags)11192 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
11193     Jim_Obj *keyObjPtr, int flags)
11194 {
11195     Jim_Obj *dictObjPtr;
11196     Jim_Obj *resObjPtr = NULL;
11197     int ret;
11198 
11199     dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
11200     if (!dictObjPtr) {
11201         return NULL;
11202     }
11203 
11204     ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
11205     if (ret != JIM_OK) {
11206         Jim_SetResultFormatted(interp,
11207             "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
11208             ret < 0 ? "variable isn't" : "no such element in");
11209     }
11210     else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
11211 
11212         Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
11213     }
11214 
11215     return resObjPtr;
11216 }
11217 
11218 
JimDictSugarGet(Jim_Interp * interp,Jim_Obj * objPtr,int flags)11219 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
11220 {
11221     SetDictSubstFromAny(interp, objPtr);
11222 
11223     return JimDictExpandArrayVariable(interp,
11224         objPtr->internalRep.dictSubstValue.varNameObjPtr,
11225         objPtr->internalRep.dictSubstValue.indexObjPtr, flags);
11226 }
11227 
11228 
11229 
FreeDictSubstInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)11230 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11231 {
11232     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
11233     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
11234 }
11235 
DupDictSubstInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)11236 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11237 {
11238 
11239     dupPtr->internalRep = srcPtr->internalRep;
11240 
11241     Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr);
11242     Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
11243 }
11244 
11245 
SetDictSubstFromAny(Jim_Interp * interp,Jim_Obj * objPtr)11246 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
11247 {
11248     if (objPtr->typePtr != &dictSubstObjType) {
11249         Jim_Obj *varObjPtr, *keyObjPtr;
11250 
11251         if (objPtr->typePtr == &interpolatedObjType) {
11252 
11253 
11254             varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
11255             keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
11256 
11257             Jim_IncrRefCount(varObjPtr);
11258             Jim_IncrRefCount(keyObjPtr);
11259         }
11260         else {
11261             JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
11262         }
11263 
11264         Jim_FreeIntRep(interp, objPtr);
11265         objPtr->typePtr = &dictSubstObjType;
11266         objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
11267         objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
11268     }
11269 }
11270 
JimExpandDictSugar(Jim_Interp * interp,Jim_Obj * objPtr)11271 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
11272 {
11273     Jim_Obj *resObjPtr = NULL;
11274     Jim_Obj *substKeyObjPtr = NULL;
11275 
11276     if (interp->safeexpr) {
11277         return objPtr;
11278     }
11279 
11280     SetDictSubstFromAny(interp, objPtr);
11281 
11282     if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
11283             &substKeyObjPtr, JIM_NONE)
11284         != JIM_OK) {
11285         return NULL;
11286     }
11287     Jim_IncrRefCount(substKeyObjPtr);
11288     resObjPtr =
11289         JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
11290         substKeyObjPtr, 0);
11291     Jim_DecrRefCount(interp, substKeyObjPtr);
11292 
11293     return resObjPtr;
11294 }
11295 
11296 
JimCreateCallFrame(Jim_Interp * interp,Jim_CallFrame * parent,Jim_Obj * nsObj)11297 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
11298 {
11299     Jim_CallFrame *cf;
11300 
11301     if (interp->freeFramesList) {
11302         cf = interp->freeFramesList;
11303         interp->freeFramesList = cf->next;
11304 
11305         cf->argv = NULL;
11306         cf->argc = 0;
11307         cf->procArgsObjPtr = NULL;
11308         cf->procBodyObjPtr = NULL;
11309         cf->next = NULL;
11310         cf->staticVars = NULL;
11311         cf->localCommands = NULL;
11312         cf->tailcallObj = NULL;
11313         cf->tailcallCmd = NULL;
11314     }
11315     else {
11316         cf = Jim_Alloc(sizeof(*cf));
11317         memset(cf, 0, sizeof(*cf));
11318 
11319         Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
11320     }
11321 
11322     cf->id = interp->callFrameEpoch++;
11323     cf->parent = parent;
11324     cf->level = parent ? parent->level + 1 : 0;
11325     cf->nsObj = nsObj;
11326     Jim_IncrRefCount(nsObj);
11327 
11328     return cf;
11329 }
11330 
JimDeleteLocalProcs(Jim_Interp * interp,Jim_Stack * localCommands)11331 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
11332 {
11333 
11334     if (localCommands) {
11335         Jim_Obj *cmdNameObj;
11336 
11337         while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
11338             Jim_HashTable *ht = &interp->commands;
11339             Jim_HashEntry *he = Jim_FindHashEntry(ht, cmdNameObj);
11340             if (he) {
11341                 Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
11342                 if (cmd->prevCmd) {
11343                     Jim_Cmd *prevCmd = cmd->prevCmd;
11344                     cmd->prevCmd = NULL;
11345 
11346 
11347                     JimDecrCmdRefCount(interp, cmd);
11348 
11349 
11350                     Jim_SetHashVal(ht, he, prevCmd);
11351                 }
11352                 else {
11353                     Jim_DeleteHashEntry(ht, cmdNameObj);
11354                 }
11355             }
11356             Jim_DecrRefCount(interp, cmdNameObj);
11357         }
11358         Jim_FreeStack(localCommands);
11359         Jim_Free(localCommands);
11360     }
11361     return JIM_OK;
11362 }
11363 
JimInvokeDefer(Jim_Interp * interp,int retcode)11364 static int JimInvokeDefer(Jim_Interp *interp, int retcode)
11365 {
11366     Jim_Obj *objPtr;
11367 
11368 
11369     if (JimFindVariable(&interp->framePtr->vars, interp->defer) == NULL) {
11370         return retcode;
11371     }
11372     objPtr = Jim_GetVariable(interp, interp->defer, JIM_NONE);
11373 
11374     if (objPtr) {
11375         int ret = JIM_OK;
11376         int i;
11377         int listLen = Jim_ListLength(interp, objPtr);
11378         Jim_Obj *resultObjPtr;
11379 
11380         Jim_IncrRefCount(objPtr);
11381 
11382         resultObjPtr = Jim_GetResult(interp);
11383         Jim_IncrRefCount(resultObjPtr);
11384         Jim_SetEmptyResult(interp);
11385 
11386 
11387         for (i = listLen; i > 0; i--) {
11388 
11389             Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1);
11390             ret = Jim_EvalObj(interp, scriptObjPtr);
11391             if (ret != JIM_OK) {
11392                 break;
11393             }
11394         }
11395 
11396         if (ret == JIM_OK || retcode == JIM_ERR) {
11397 
11398             Jim_SetResult(interp, resultObjPtr);
11399         }
11400         else {
11401             retcode = ret;
11402         }
11403 
11404         Jim_DecrRefCount(interp, resultObjPtr);
11405         Jim_DecrRefCount(interp, objPtr);
11406     }
11407     return retcode;
11408 }
11409 
11410 #define JIM_FCF_FULL 0
11411 #define JIM_FCF_REUSE 1
JimFreeCallFrame(Jim_Interp * interp,Jim_CallFrame * cf,int action)11412 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
11413  {
11414     JimDeleteLocalProcs(interp, cf->localCommands);
11415 
11416     if (cf->procArgsObjPtr)
11417         Jim_DecrRefCount(interp, cf->procArgsObjPtr);
11418     if (cf->procBodyObjPtr)
11419         Jim_DecrRefCount(interp, cf->procBodyObjPtr);
11420     Jim_DecrRefCount(interp, cf->nsObj);
11421     if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
11422         Jim_FreeHashTable(&cf->vars);
11423     else {
11424         Jim_ClearHashTable(&cf->vars);
11425     }
11426     cf->next = interp->freeFramesList;
11427     interp->freeFramesList = cf;
11428 }
11429 
11430 
11431 
Jim_IsBigEndian(void)11432 int Jim_IsBigEndian(void)
11433 {
11434     union {
11435         unsigned short s;
11436         unsigned char c[2];
11437     } uval = {0x0102};
11438 
11439     return uval.c[0] == 1;
11440 }
11441 
11442 
Jim_CreateInterp(void)11443 Jim_Interp *Jim_CreateInterp(void)
11444 {
11445     Jim_Interp *i = Jim_Alloc(sizeof(*i));
11446 
11447     memset(i, 0, sizeof(*i));
11448 
11449     i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
11450     i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
11451     i->lastCollectTime = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
11452 
11453     Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
11454 #ifdef JIM_REFERENCES
11455     Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
11456 #endif
11457     Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
11458     Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
11459     i->emptyObj = Jim_NewEmptyStringObj(i);
11460     i->trueObj = Jim_NewIntObj(i, 1);
11461     i->falseObj = Jim_NewIntObj(i, 0);
11462     i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
11463     i->result = i->emptyObj;
11464     i->stackTrace = Jim_NewListObj(i, NULL, 0);
11465     i->unknown = Jim_NewStringObj(i, "unknown", -1);
11466     i->defer = Jim_NewStringObj(i, "jim::defer", -1);
11467     i->errorProc = i->emptyObj;
11468     i->nullScriptObj = Jim_NewEmptyStringObj(i);
11469     i->evalFrame = &i->topEvalFrame;
11470     i->currentFilenameObj = Jim_NewEmptyStringObj(i);
11471     Jim_IncrRefCount(i->emptyObj);
11472     Jim_IncrRefCount(i->result);
11473     Jim_IncrRefCount(i->stackTrace);
11474     Jim_IncrRefCount(i->unknown);
11475     Jim_IncrRefCount(i->defer);
11476     Jim_IncrRefCount(i->nullScriptObj);
11477     Jim_IncrRefCount(i->errorProc);
11478     Jim_IncrRefCount(i->trueObj);
11479     Jim_IncrRefCount(i->falseObj);
11480     Jim_IncrRefCount(i->currentFilenameObj);
11481 
11482 
11483     Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
11484     Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
11485 
11486     Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
11487     Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
11488     Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
11489     Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
11490     Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
11491     Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
11492     Jim_SetVariableStrWithStr(i, "tcl_platform(bootstrap)", "0");
11493     Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
11494     Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
11495     Jim_SetVariableStr(i, "tcl_platform(stackFormat)", Jim_NewIntObj(i, 4));
11496 
11497     return i;
11498 }
11499 
Jim_FreeInterp(Jim_Interp * i)11500 void Jim_FreeInterp(Jim_Interp *i)
11501 {
11502     Jim_CallFrame *cf, *cfx;
11503 
11504     Jim_Obj *objPtr, *nextObjPtr;
11505 
11506     i->quitting = 1;
11507 
11508 
11509     for (cf = i->framePtr; cf; cf = cfx) {
11510 
11511         JimInvokeDefer(i, JIM_OK);
11512         cfx = cf->parent;
11513         JimFreeCallFrame(i, cf, JIM_FCF_FULL);
11514     }
11515 
11516 
11517     Jim_FreeHashTable(&i->commands);
11518 
11519     Jim_DecrRefCount(i, i->emptyObj);
11520     Jim_DecrRefCount(i, i->trueObj);
11521     Jim_DecrRefCount(i, i->falseObj);
11522     Jim_DecrRefCount(i, i->result);
11523     Jim_DecrRefCount(i, i->stackTrace);
11524     Jim_DecrRefCount(i, i->errorProc);
11525     Jim_DecrRefCount(i, i->unknown);
11526     Jim_DecrRefCount(i, i->defer);
11527     Jim_DecrRefCount(i, i->nullScriptObj);
11528     Jim_DecrRefCount(i, i->currentFilenameObj);
11529 
11530 
11531     Jim_InterpIncrProcEpoch(i);
11532 
11533 #ifdef JIM_REFERENCES
11534     Jim_FreeHashTable(&i->references);
11535 #endif
11536     Jim_FreeHashTable(&i->packages);
11537     Jim_Free(i->prngState);
11538     Jim_FreeHashTable(&i->assocData);
11539     if (i->traceCmdObj) {
11540         Jim_DecrRefCount(i, i->traceCmdObj);
11541     }
11542 
11543 #ifdef JIM_MAINTAINER
11544     if (i->liveList != NULL) {
11545         objPtr = i->liveList;
11546 
11547         printf("\n-------------------------------------\n");
11548         printf("Objects still in the free list:\n");
11549         while (objPtr) {
11550             const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
11551             Jim_String(objPtr);
11552 
11553             if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
11554                 printf("%p (%d) %-10s: '%.20s...'\n",
11555                     (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
11556             }
11557             else {
11558                 printf("%p (%d) %-10s: '%s'\n",
11559                     (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
11560             }
11561             if (objPtr->typePtr == &sourceObjType) {
11562                 printf("FILE %s LINE %d\n",
11563                     Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
11564                     objPtr->internalRep.sourceValue.lineNumber);
11565             }
11566             objPtr = objPtr->nextObjPtr;
11567         }
11568         printf("-------------------------------------\n\n");
11569         JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
11570     }
11571 #endif
11572 
11573 
11574     objPtr = i->freeList;
11575     while (objPtr) {
11576         nextObjPtr = objPtr->nextObjPtr;
11577         Jim_Free(objPtr);
11578         objPtr = nextObjPtr;
11579     }
11580 
11581 
11582     for (cf = i->freeFramesList; cf; cf = cfx) {
11583         cfx = cf->next;
11584         if (cf->vars.table)
11585             Jim_FreeHashTable(&cf->vars);
11586         Jim_Free(cf);
11587     }
11588 
11589 
11590     Jim_Free(i);
11591 }
11592 
Jim_GetCallFrameByLevel(Jim_Interp * interp,Jim_Obj * levelObjPtr)11593 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
11594 {
11595     long level;
11596     const char *str;
11597     Jim_CallFrame *framePtr;
11598 
11599     if (levelObjPtr) {
11600         str = Jim_String(levelObjPtr);
11601         if (str[0] == '#') {
11602             char *endptr;
11603 
11604             level = jim_strtol(str + 1, &endptr);
11605             if (str[1] == '\0' || endptr[0] != '\0') {
11606                 level = -1;
11607             }
11608         }
11609         else {
11610             if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
11611                 level = -1;
11612             }
11613             else {
11614 
11615                 level = interp->framePtr->level - level;
11616             }
11617         }
11618     }
11619     else {
11620         str = "1";
11621         level = interp->framePtr->level - 1;
11622     }
11623 
11624     if (level == 0) {
11625         return interp->topFramePtr;
11626     }
11627     if (level > 0) {
11628 
11629         for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
11630             if (framePtr->level == level) {
11631                 return framePtr;
11632             }
11633         }
11634     }
11635 
11636     Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
11637     return NULL;
11638 }
11639 
JimGetCallFrameByInteger(Jim_Interp * interp,long level)11640 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, long level)
11641 {
11642     Jim_CallFrame *framePtr;
11643 
11644     if (level == 0) {
11645         return interp->framePtr;
11646     }
11647 
11648     if (level < 0) {
11649 
11650         level = interp->framePtr->level + level;
11651     }
11652 
11653     if (level > 0) {
11654 
11655         for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
11656             if (framePtr->level == level) {
11657                 return framePtr;
11658             }
11659         }
11660     }
11661     return NULL;
11662 }
11663 
JimGetEvalFrameByProcLevel(Jim_Interp * interp,int proclevel)11664 static Jim_EvalFrame *JimGetEvalFrameByProcLevel(Jim_Interp *interp, int proclevel)
11665 {
11666     Jim_EvalFrame *evalFrame;
11667 
11668     if (proclevel == 0) {
11669         return interp->evalFrame;
11670     }
11671 
11672     if (proclevel < 0) {
11673 
11674         proclevel = interp->procLevel + proclevel;
11675     }
11676 
11677     if (proclevel >= 0) {
11678 
11679         for (evalFrame = interp->evalFrame; evalFrame; evalFrame = evalFrame->parent) {
11680             if (evalFrame->procLevel == proclevel) {
11681                 return evalFrame;
11682             }
11683         }
11684     }
11685     return NULL;
11686 }
11687 
JimProcForEvalFrame(Jim_Interp * interp,Jim_EvalFrame * frame)11688 static Jim_Obj *JimProcForEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame)
11689 {
11690     if (frame == interp->evalFrame || (frame->cmd && frame->cmd->cmdNameObj)) {
11691         Jim_EvalFrame *e;
11692         for (e = frame->parent; e; e = e->parent) {
11693             if (e->cmd && e->cmd->isproc && e->cmd->cmdNameObj) {
11694                 break;
11695             }
11696         }
11697         if (e && e->cmd && e->cmd->cmdNameObj) {
11698             return e->cmd->cmdNameObj;
11699         }
11700     }
11701     return NULL;
11702 }
11703 
JimAddStackFrame(Jim_Interp * interp,Jim_EvalFrame * frame,Jim_Obj * listObj)11704 static void JimAddStackFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *listObj)
11705 {
11706     Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
11707     Jim_Obj *fileNameObj = interp->emptyObj;
11708     int linenr = 1;
11709 
11710     if (frame->scriptObj) {
11711         ScriptObj *script = JimGetScript(interp, frame->scriptObj);
11712         fileNameObj = script->fileNameObj;
11713         linenr = script->linenr;
11714     }
11715 
11716     Jim_ListAppendElement(interp, listObj, procNameObj ? procNameObj : interp->emptyObj);
11717     Jim_ListAppendElement(interp, listObj, fileNameObj);
11718     Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, linenr));
11719     Jim_ListAppendElement(interp, listObj, Jim_NewListObj(interp, frame->argv, frame->argc));
11720 }
11721 
JimSetStackTrace(Jim_Interp * interp,Jim_Obj * stackTraceObj)11722 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
11723 {
11724 
11725     Jim_IncrRefCount(stackTraceObj);
11726     Jim_DecrRefCount(interp, interp->stackTrace);
11727     interp->stackTrace = stackTraceObj;
11728     interp->errorFlag = 1;
11729 }
11730 
JimSetErrorStack(Jim_Interp * interp,ScriptObj * script)11731 static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script)
11732 {
11733     if (!interp->errorFlag) {
11734         int i;
11735         Jim_Obj *stackTrace = Jim_NewListObj(interp, NULL, 0);
11736 
11737         if (interp->procLevel == 0 && script) {
11738             Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
11739             Jim_ListAppendElement(interp, stackTrace, script->fileNameObj);
11740             Jim_ListAppendElement(interp, stackTrace, Jim_NewIntObj(interp, script->linenr));
11741             Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
11742         }
11743         else {
11744             for (i = 0; i <= interp->procLevel; i++) {
11745                 Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
11746                 if (frame) {
11747                     JimAddStackFrame(interp, frame, stackTrace);
11748                 }
11749             }
11750         }
11751         JimSetStackTrace(interp, stackTrace);
11752     }
11753 }
11754 
Jim_SetAssocData(Jim_Interp * interp,const char * key,Jim_InterpDeleteProc * delProc,void * data)11755 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
11756     void *data)
11757 {
11758     AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
11759 
11760     assocEntryPtr->delProc = delProc;
11761     assocEntryPtr->data = data;
11762     return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
11763 }
11764 
Jim_GetAssocData(Jim_Interp * interp,const char * key)11765 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
11766 {
11767     Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
11768 
11769     if (entryPtr != NULL) {
11770         AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr);
11771         return assocEntryPtr->data;
11772     }
11773     return NULL;
11774 }
11775 
Jim_DeleteAssocData(Jim_Interp * interp,const char * key)11776 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
11777 {
11778     return Jim_DeleteHashEntry(&interp->assocData, key);
11779 }
11780 
Jim_GetExitCode(Jim_Interp * interp)11781 int Jim_GetExitCode(Jim_Interp *interp)
11782 {
11783     return interp->exitCode;
11784 }
11785 
11786 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
11787 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
11788 
11789 static const Jim_ObjType intObjType = {
11790     "int",
11791     NULL,
11792     NULL,
11793     UpdateStringOfInt,
11794     JIM_TYPE_NONE,
11795 };
11796 
11797 static const Jim_ObjType coercedDoubleObjType = {
11798     "coerced-double",
11799     NULL,
11800     NULL,
11801     UpdateStringOfInt,
11802     JIM_TYPE_NONE,
11803 };
11804 
11805 
UpdateStringOfInt(struct Jim_Obj * objPtr)11806 static void UpdateStringOfInt(struct Jim_Obj *objPtr)
11807 {
11808     char buf[JIM_INTEGER_SPACE + 1];
11809     jim_wide wideValue = JimWideValue(objPtr);
11810     int pos = 0;
11811 
11812     if (wideValue == 0) {
11813         buf[pos++] = '0';
11814     }
11815     else {
11816         char tmp[JIM_INTEGER_SPACE];
11817         int num = 0;
11818         int i;
11819 
11820         if (wideValue < 0) {
11821             buf[pos++] = '-';
11822             i = wideValue % 10;
11823             tmp[num++] = (i > 0) ? (10 - i) : -i;
11824             wideValue /= -10;
11825         }
11826 
11827         while (wideValue) {
11828             tmp[num++] = wideValue % 10;
11829             wideValue /= 10;
11830         }
11831 
11832         for (i = 0; i < num; i++) {
11833             buf[pos++] = '0' + tmp[num - i - 1];
11834         }
11835     }
11836     buf[pos] = 0;
11837 
11838     JimSetStringBytes(objPtr, buf);
11839 }
11840 
SetIntFromAny(Jim_Interp * interp,Jim_Obj * objPtr,int flags)11841 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
11842 {
11843     jim_wide wideValue;
11844     const char *str;
11845 
11846     if (objPtr->typePtr == &coercedDoubleObjType) {
11847 
11848         objPtr->typePtr = &intObjType;
11849         return JIM_OK;
11850     }
11851 
11852 
11853     str = Jim_String(objPtr);
11854 
11855     if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
11856         if (flags & JIM_ERRMSG) {
11857             Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
11858         }
11859         return JIM_ERR;
11860     }
11861     if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
11862         Jim_SetResultString(interp, "Integer value too big to be represented", -1);
11863         return JIM_ERR;
11864     }
11865 
11866     Jim_FreeIntRep(interp, objPtr);
11867     objPtr->typePtr = &intObjType;
11868     objPtr->internalRep.wideValue = wideValue;
11869     return JIM_OK;
11870 }
11871 
11872 #ifdef JIM_OPTIMIZATION
JimIsWide(Jim_Obj * objPtr)11873 static int JimIsWide(Jim_Obj *objPtr)
11874 {
11875     return objPtr->typePtr == &intObjType;
11876 }
11877 #endif
11878 
Jim_GetWide(Jim_Interp * interp,Jim_Obj * objPtr,jim_wide * widePtr)11879 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
11880 {
11881     if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
11882         return JIM_ERR;
11883     *widePtr = JimWideValue(objPtr);
11884     return JIM_OK;
11885 }
11886 
Jim_GetWideExpr(Jim_Interp * interp,Jim_Obj * objPtr,jim_wide * widePtr)11887 int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
11888 {
11889     int ret = JIM_OK;
11890 
11891     if (objPtr->typePtr == &sourceObjType || objPtr->typePtr == NULL) {
11892         SetIntFromAny(interp, objPtr, 0);
11893     }
11894     if (objPtr->typePtr == &intObjType) {
11895         *widePtr = JimWideValue(objPtr);
11896     }
11897     else {
11898         JimPanic((interp->safeexpr, "interp->safeexpr is set"));
11899         interp->safeexpr++;
11900         ret = Jim_EvalExpression(interp, objPtr);
11901         interp->safeexpr--;
11902 
11903         if (ret == JIM_OK) {
11904             ret = Jim_GetWide(interp, Jim_GetResult(interp), widePtr);
11905         }
11906         if (ret != JIM_OK) {
11907             Jim_SetResultFormatted(interp, "expected integer expression but got \"%#s\"", objPtr);
11908         }
11909     }
11910     return ret;
11911 }
11912 
11913 
JimGetWideNoErr(Jim_Interp * interp,Jim_Obj * objPtr,jim_wide * widePtr)11914 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
11915 {
11916     if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
11917         return JIM_ERR;
11918     *widePtr = JimWideValue(objPtr);
11919     return JIM_OK;
11920 }
11921 
Jim_GetLong(Jim_Interp * interp,Jim_Obj * objPtr,long * longPtr)11922 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
11923 {
11924     jim_wide wideValue;
11925     int retval;
11926 
11927     retval = Jim_GetWide(interp, objPtr, &wideValue);
11928     if (retval == JIM_OK) {
11929         *longPtr = (long)wideValue;
11930         return JIM_OK;
11931     }
11932     return JIM_ERR;
11933 }
11934 
Jim_NewIntObj(Jim_Interp * interp,jim_wide wideValue)11935 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
11936 {
11937     Jim_Obj *objPtr;
11938 
11939     objPtr = Jim_NewObj(interp);
11940     objPtr->typePtr = &intObjType;
11941     objPtr->bytes = NULL;
11942     objPtr->internalRep.wideValue = wideValue;
11943     return objPtr;
11944 }
11945 
11946 #define JIM_DOUBLE_SPACE 30
11947 
11948 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
11949 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
11950 
11951 static const Jim_ObjType doubleObjType = {
11952     "double",
11953     NULL,
11954     NULL,
11955     UpdateStringOfDouble,
11956     JIM_TYPE_NONE,
11957 };
11958 
11959 #if !HAVE_DECL_ISNAN
11960 #undef isnan
11961 #define isnan(X) ((X) != (X))
11962 #endif
11963 #if !HAVE_DECL_ISINF
11964 #undef isinf
11965 #define isinf(X) (1.0 / (X) == 0.0)
11966 #endif
11967 
UpdateStringOfDouble(struct Jim_Obj * objPtr)11968 static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
11969 {
11970     double value = objPtr->internalRep.doubleValue;
11971 
11972     if (isnan(value)) {
11973         JimSetStringBytes(objPtr, "NaN");
11974         return;
11975     }
11976     if (isinf(value)) {
11977         if (value < 0) {
11978             JimSetStringBytes(objPtr, "-Inf");
11979         }
11980         else {
11981             JimSetStringBytes(objPtr, "Inf");
11982         }
11983         return;
11984     }
11985     {
11986         char buf[JIM_DOUBLE_SPACE + 1];
11987         int i;
11988         int len = sprintf(buf, "%.12g", value);
11989 
11990 
11991         for (i = 0; i < len; i++) {
11992             if (buf[i] == '.' || buf[i] == 'e') {
11993 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
11994                 char *e = strchr(buf, 'e');
11995                 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
11996 
11997                     e += 2;
11998                     memmove(e, e + 1, len - (e - buf));
11999                 }
12000 #endif
12001                 break;
12002             }
12003         }
12004         if (buf[i] == '\0') {
12005             buf[i++] = '.';
12006             buf[i++] = '0';
12007             buf[i] = '\0';
12008         }
12009         JimSetStringBytes(objPtr, buf);
12010     }
12011 }
12012 
SetDoubleFromAny(Jim_Interp * interp,Jim_Obj * objPtr)12013 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12014 {
12015     double doubleValue;
12016     jim_wide wideValue;
12017     const char *str;
12018 
12019 #ifdef HAVE_LONG_LONG
12020 
12021 #define MIN_INT_IN_DOUBLE -(1LL << 53)
12022 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)
12023 
12024     if (objPtr->typePtr == &intObjType
12025         && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
12026         && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {
12027 
12028 
12029         objPtr->typePtr = &coercedDoubleObjType;
12030         return JIM_OK;
12031     }
12032 #endif
12033     str = Jim_String(objPtr);
12034 
12035     if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
12036 
12037         Jim_FreeIntRep(interp, objPtr);
12038         objPtr->typePtr = &coercedDoubleObjType;
12039         objPtr->internalRep.wideValue = wideValue;
12040         return JIM_OK;
12041     }
12042     else {
12043 
12044         if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
12045             Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
12046             return JIM_ERR;
12047         }
12048 
12049         Jim_FreeIntRep(interp, objPtr);
12050     }
12051     objPtr->typePtr = &doubleObjType;
12052     objPtr->internalRep.doubleValue = doubleValue;
12053     return JIM_OK;
12054 }
12055 
Jim_GetDouble(Jim_Interp * interp,Jim_Obj * objPtr,double * doublePtr)12056 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
12057 {
12058     if (objPtr->typePtr == &coercedDoubleObjType) {
12059         *doublePtr = JimWideValue(objPtr);
12060         return JIM_OK;
12061     }
12062     if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
12063         return JIM_ERR;
12064 
12065     if (objPtr->typePtr == &coercedDoubleObjType) {
12066         *doublePtr = JimWideValue(objPtr);
12067     }
12068     else {
12069         *doublePtr = objPtr->internalRep.doubleValue;
12070     }
12071     return JIM_OK;
12072 }
12073 
Jim_NewDoubleObj(Jim_Interp * interp,double doubleValue)12074 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
12075 {
12076     Jim_Obj *objPtr;
12077 
12078     objPtr = Jim_NewObj(interp);
12079     objPtr->typePtr = &doubleObjType;
12080     objPtr->bytes = NULL;
12081     objPtr->internalRep.doubleValue = doubleValue;
12082     return objPtr;
12083 }
12084 
12085 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
12086 
Jim_GetBoolean(Jim_Interp * interp,Jim_Obj * objPtr,int * booleanPtr)12087 int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
12088 {
12089     if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
12090         return JIM_ERR;
12091     *booleanPtr = (int) JimWideValue(objPtr);
12092     return JIM_OK;
12093 }
12094 
12095 static const char * const jim_true_false_strings[8] = {
12096     "1", "true", "yes", "on",
12097     "0", "false", "no", "off"
12098 };
12099 
12100 static const int jim_true_false_lens[8] = {
12101     1, 4, 3, 2,
12102     1, 5, 2, 3,
12103 };
12104 
SetBooleanFromAny(Jim_Interp * interp,Jim_Obj * objPtr,int flags)12105 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
12106 {
12107     int index = Jim_FindByName(Jim_String(objPtr), jim_true_false_strings,
12108         sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings));
12109     if (index < 0) {
12110         if (flags & JIM_ERRMSG) {
12111             Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr);
12112         }
12113         return JIM_ERR;
12114     }
12115 
12116 
12117     Jim_FreeIntRep(interp, objPtr);
12118     objPtr->typePtr = &intObjType;
12119 
12120     objPtr->internalRep.wideValue = index < 4 ? 1 : 0;
12121     return JIM_OK;
12122 }
12123 
12124 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
12125 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
12126 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
12127 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
12128 static void UpdateStringOfList(struct Jim_Obj *objPtr);
12129 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
12130 
12131 static const Jim_ObjType listObjType = {
12132     "list",
12133     FreeListInternalRep,
12134     DupListInternalRep,
12135     UpdateStringOfList,
12136     JIM_TYPE_NONE,
12137 };
12138 
FreeListInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)12139 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
12140 {
12141     int i;
12142 
12143     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
12144         Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
12145     }
12146     Jim_Free(objPtr->internalRep.listValue.ele);
12147 }
12148 
DupListInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)12149 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
12150 {
12151     int i;
12152 
12153     JIM_NOTUSED(interp);
12154 
12155     dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
12156     dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
12157     dupPtr->internalRep.listValue.ele =
12158         Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
12159     memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
12160         sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
12161     for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
12162         Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
12163     }
12164     dupPtr->typePtr = &listObjType;
12165 }
12166 
12167 #define JIM_ELESTR_SIMPLE 0
12168 #define JIM_ELESTR_BRACE 1
12169 #define JIM_ELESTR_QUOTE 2
ListElementQuotingType(const char * s,int len)12170 static unsigned char ListElementQuotingType(const char *s, int len)
12171 {
12172     int i, level, blevel, trySimple = 1;
12173 
12174 
12175     if (len == 0)
12176         return JIM_ELESTR_BRACE;
12177     if (s[0] == '"' || s[0] == '{') {
12178         trySimple = 0;
12179         goto testbrace;
12180     }
12181     for (i = 0; i < len; i++) {
12182         switch (s[i]) {
12183             case ' ':
12184             case '$':
12185             case '"':
12186             case '[':
12187             case ']':
12188             case ';':
12189             case '\\':
12190             case '\r':
12191             case '\n':
12192             case '\t':
12193             case '\f':
12194             case '\v':
12195                 trySimple = 0;
12196 
12197             case '{':
12198             case '}':
12199                 goto testbrace;
12200         }
12201     }
12202     return JIM_ELESTR_SIMPLE;
12203 
12204   testbrace:
12205 
12206     if (s[len - 1] == '\\')
12207         return JIM_ELESTR_QUOTE;
12208     level = 0;
12209     blevel = 0;
12210     for (i = 0; i < len; i++) {
12211         switch (s[i]) {
12212             case '{':
12213                 level++;
12214                 break;
12215             case '}':
12216                 level--;
12217                 if (level < 0)
12218                     return JIM_ELESTR_QUOTE;
12219                 break;
12220             case '[':
12221                 blevel++;
12222                 break;
12223             case ']':
12224                 blevel--;
12225                 break;
12226             case '\\':
12227                 if (s[i + 1] == '\n')
12228                     return JIM_ELESTR_QUOTE;
12229                 else if (s[i + 1] != '\0')
12230                     i++;
12231                 break;
12232         }
12233     }
12234     if (blevel < 0) {
12235         return JIM_ELESTR_QUOTE;
12236     }
12237 
12238     if (level == 0) {
12239         if (!trySimple)
12240             return JIM_ELESTR_BRACE;
12241         for (i = 0; i < len; i++) {
12242             switch (s[i]) {
12243                 case ' ':
12244                 case '$':
12245                 case '"':
12246                 case '[':
12247                 case ']':
12248                 case ';':
12249                 case '\\':
12250                 case '\r':
12251                 case '\n':
12252                 case '\t':
12253                 case '\f':
12254                 case '\v':
12255                     return JIM_ELESTR_BRACE;
12256                     break;
12257             }
12258         }
12259         return JIM_ELESTR_SIMPLE;
12260     }
12261     return JIM_ELESTR_QUOTE;
12262 }
12263 
BackslashQuoteString(const char * s,int len,char * q)12264 static int BackslashQuoteString(const char *s, int len, char *q)
12265 {
12266     char *p = q;
12267 
12268     while (len--) {
12269         switch (*s) {
12270             case ' ':
12271             case '$':
12272             case '"':
12273             case '[':
12274             case ']':
12275             case '{':
12276             case '}':
12277             case ';':
12278             case '\\':
12279                 *p++ = '\\';
12280                 *p++ = *s++;
12281                 break;
12282             case '\n':
12283                 *p++ = '\\';
12284                 *p++ = 'n';
12285                 s++;
12286                 break;
12287             case '\r':
12288                 *p++ = '\\';
12289                 *p++ = 'r';
12290                 s++;
12291                 break;
12292             case '\t':
12293                 *p++ = '\\';
12294                 *p++ = 't';
12295                 s++;
12296                 break;
12297             case '\f':
12298                 *p++ = '\\';
12299                 *p++ = 'f';
12300                 s++;
12301                 break;
12302             case '\v':
12303                 *p++ = '\\';
12304                 *p++ = 'v';
12305                 s++;
12306                 break;
12307             default:
12308                 *p++ = *s++;
12309                 break;
12310         }
12311     }
12312     *p = '\0';
12313 
12314     return p - q;
12315 }
12316 
JimMakeListStringRep(Jim_Obj * objPtr,Jim_Obj ** objv,int objc)12317 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
12318 {
12319     #define STATIC_QUOTING_LEN 32
12320     int i, bufLen, realLength;
12321     const char *strRep;
12322     char *p;
12323     unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
12324 
12325 
12326     if (objc > STATIC_QUOTING_LEN) {
12327         quotingType = Jim_Alloc(objc);
12328     }
12329     else {
12330         quotingType = staticQuoting;
12331     }
12332     bufLen = 0;
12333     for (i = 0; i < objc; i++) {
12334         int len;
12335 
12336         strRep = Jim_GetString(objv[i], &len);
12337         quotingType[i] = ListElementQuotingType(strRep, len);
12338         switch (quotingType[i]) {
12339             case JIM_ELESTR_SIMPLE:
12340                 if (i != 0 || strRep[0] != '#') {
12341                     bufLen += len;
12342                     break;
12343                 }
12344 
12345                 quotingType[i] = JIM_ELESTR_BRACE;
12346 
12347             case JIM_ELESTR_BRACE:
12348                 bufLen += len + 2;
12349                 break;
12350             case JIM_ELESTR_QUOTE:
12351                 bufLen += len * 2;
12352                 break;
12353         }
12354         bufLen++;
12355     }
12356     bufLen++;
12357 
12358 
12359     p = objPtr->bytes = Jim_Alloc(bufLen + 1);
12360     realLength = 0;
12361     for (i = 0; i < objc; i++) {
12362         int len, qlen;
12363 
12364         strRep = Jim_GetString(objv[i], &len);
12365 
12366         switch (quotingType[i]) {
12367             case JIM_ELESTR_SIMPLE:
12368                 memcpy(p, strRep, len);
12369                 p += len;
12370                 realLength += len;
12371                 break;
12372             case JIM_ELESTR_BRACE:
12373                 *p++ = '{';
12374                 memcpy(p, strRep, len);
12375                 p += len;
12376                 *p++ = '}';
12377                 realLength += len + 2;
12378                 break;
12379             case JIM_ELESTR_QUOTE:
12380                 if (i == 0 && strRep[0] == '#') {
12381                     *p++ = '\\';
12382                     realLength++;
12383                 }
12384                 qlen = BackslashQuoteString(strRep, len, p);
12385                 p += qlen;
12386                 realLength += qlen;
12387                 break;
12388         }
12389 
12390         if (i + 1 != objc) {
12391             *p++ = ' ';
12392             realLength++;
12393         }
12394     }
12395     *p = '\0';
12396     objPtr->length = realLength;
12397 
12398     if (quotingType != staticQuoting) {
12399         Jim_Free(quotingType);
12400     }
12401 }
12402 
UpdateStringOfList(struct Jim_Obj * objPtr)12403 static void UpdateStringOfList(struct Jim_Obj *objPtr)
12404 {
12405     JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
12406 }
12407 
SetListFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)12408 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
12409 {
12410     struct JimParserCtx parser;
12411     const char *str;
12412     int strLen;
12413     Jim_Obj *fileNameObj;
12414     int linenr;
12415 
12416     if (objPtr->typePtr == &listObjType) {
12417         return JIM_OK;
12418     }
12419 
12420 
12421     if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
12422         Jim_Dict *dict = objPtr->internalRep.dictValue;
12423 
12424 
12425         objPtr->typePtr = &listObjType;
12426         objPtr->internalRep.listValue.len = dict->len;
12427         objPtr->internalRep.listValue.maxLen = dict->maxLen;
12428         objPtr->internalRep.listValue.ele = dict->table;
12429 
12430 
12431         Jim_Free(dict->ht);
12432 
12433 
12434         Jim_Free(dict);
12435         return JIM_OK;
12436     }
12437 
12438 
12439     fileNameObj = Jim_GetSourceInfo(interp, objPtr, &linenr);
12440     Jim_IncrRefCount(fileNameObj);
12441 
12442 
12443     str = Jim_GetString(objPtr, &strLen);
12444 
12445     Jim_FreeIntRep(interp, objPtr);
12446     objPtr->typePtr = &listObjType;
12447     objPtr->internalRep.listValue.len = 0;
12448     objPtr->internalRep.listValue.maxLen = 0;
12449     objPtr->internalRep.listValue.ele = NULL;
12450 
12451 
12452     if (strLen) {
12453         JimParserInit(&parser, str, strLen, linenr);
12454         while (!parser.eof) {
12455             Jim_Obj *elementPtr;
12456 
12457             JimParseList(&parser);
12458             if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
12459                 continue;
12460             elementPtr = JimParserGetTokenObj(interp, &parser);
12461             Jim_SetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
12462             ListAppendElement(objPtr, elementPtr);
12463         }
12464     }
12465     Jim_DecrRefCount(interp, fileNameObj);
12466     return JIM_OK;
12467 }
12468 
Jim_NewListObj(Jim_Interp * interp,Jim_Obj * const * elements,int len)12469 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
12470 {
12471     Jim_Obj *objPtr;
12472 
12473     objPtr = Jim_NewObj(interp);
12474     objPtr->typePtr = &listObjType;
12475     objPtr->bytes = NULL;
12476     objPtr->internalRep.listValue.ele = NULL;
12477     objPtr->internalRep.listValue.len = 0;
12478     objPtr->internalRep.listValue.maxLen = 0;
12479 
12480     if (len) {
12481         ListInsertElements(objPtr, 0, len, elements);
12482     }
12483 
12484     return objPtr;
12485 }
12486 
JimListGetElements(Jim_Interp * interp,Jim_Obj * listObj,int * listLen,Jim_Obj *** listVec)12487 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
12488     Jim_Obj ***listVec)
12489 {
12490     *listLen = Jim_ListLength(interp, listObj);
12491     *listVec = listObj->internalRep.listValue.ele;
12492 }
12493 
12494 
JimSign(jim_wide w)12495 static int JimSign(jim_wide w)
12496 {
12497     if (w == 0) {
12498         return 0;
12499     }
12500     else if (w < 0) {
12501         return -1;
12502     }
12503     return 1;
12504 }
12505 
12506 
12507 struct lsort_info {
12508     jmp_buf jmpbuf;
12509     Jim_Obj *command;
12510     Jim_Interp *interp;
12511     enum {
12512         JIM_LSORT_ASCII,
12513         JIM_LSORT_NOCASE,
12514         JIM_LSORT_INTEGER,
12515         JIM_LSORT_REAL,
12516         JIM_LSORT_COMMAND,
12517         JIM_LSORT_DICT
12518     } type;
12519     int order;
12520     Jim_Obj **indexv;
12521     int indexc;
12522     int unique;
12523     int (*subfn)(Jim_Obj **, Jim_Obj **);
12524 };
12525 
12526 static struct lsort_info *sort_info;
12527 
ListSortIndexHelper(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)12528 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12529 {
12530     Jim_Obj *lObj, *rObj;
12531 
12532     if (Jim_ListIndices(sort_info->interp, *lhsObj, sort_info->indexv, sort_info->indexc, &lObj, JIM_ERRMSG) != JIM_OK ||
12533         Jim_ListIndices(sort_info->interp, *rhsObj, sort_info->indexv, sort_info->indexc, &rObj, JIM_ERRMSG) != JIM_OK) {
12534         longjmp(sort_info->jmpbuf, JIM_ERR);
12535     }
12536     return sort_info->subfn(&lObj, &rObj);
12537 }
12538 
12539 
ListSortString(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)12540 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12541 {
12542     return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
12543 }
12544 
ListSortStringNoCase(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)12545 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12546 {
12547     return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
12548 }
12549 
ListSortDict(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)12550 static int ListSortDict(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12551 {
12552 
12553     const char *left = Jim_String(*lhsObj);
12554     const char *right = Jim_String(*rhsObj);
12555 
12556     while (1) {
12557         if (isdigit(UCHAR(*left)) && isdigit(UCHAR(*right))) {
12558 
12559             jim_wide lint, rint;
12560             char *lend, *rend;
12561             lint = jim_strtoull(left, &lend);
12562             rint = jim_strtoull(right, &rend);
12563             if (lint != rint) {
12564                 return JimSign(lint - rint) * sort_info->order;
12565             }
12566             if (lend -left != rend - right) {
12567                 return JimSign((lend - left) - (rend - right)) * sort_info->order;
12568             }
12569             left = lend;
12570             right = rend;
12571         }
12572         else {
12573             int cl, cr;
12574             left += utf8_tounicode_case(left, &cl, 1);
12575             right += utf8_tounicode_case(right, &cr, 1);
12576             if (cl != cr) {
12577                 return JimSign(cl - cr) * sort_info->order;
12578             }
12579             if (cl == 0) {
12580 
12581                 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
12582             }
12583         }
12584     }
12585 }
12586 
ListSortInteger(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)12587 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12588 {
12589     jim_wide lhs = 0, rhs = 0;
12590 
12591     if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
12592         Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
12593         longjmp(sort_info->jmpbuf, JIM_ERR);
12594     }
12595 
12596     return JimSign(lhs - rhs) * sort_info->order;
12597 }
12598 
ListSortReal(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)12599 static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12600 {
12601     double lhs = 0, rhs = 0;
12602 
12603     if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
12604         Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
12605         longjmp(sort_info->jmpbuf, JIM_ERR);
12606     }
12607     if (lhs == rhs) {
12608         return 0;
12609     }
12610     if (lhs > rhs) {
12611         return sort_info->order;
12612     }
12613     return -sort_info->order;
12614 }
12615 
ListSortCommand(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)12616 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
12617 {
12618     Jim_Obj *compare_script;
12619     int rc;
12620 
12621     jim_wide ret = 0;
12622 
12623 
12624     compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
12625     Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
12626     Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);
12627 
12628     rc = Jim_EvalObj(sort_info->interp, compare_script);
12629 
12630     if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
12631         longjmp(sort_info->jmpbuf, rc);
12632     }
12633 
12634     return JimSign(ret) * sort_info->order;
12635 }
12636 
ListRemoveDuplicates(Jim_Obj * listObjPtr,int (* comp)(Jim_Obj ** lhs,Jim_Obj ** rhs))12637 static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
12638 {
12639     int src;
12640     int dst = 0;
12641     Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
12642 
12643     for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
12644         if (comp(&ele[dst], &ele[src]) == 0) {
12645 
12646             Jim_DecrRefCount(sort_info->interp, ele[dst]);
12647         }
12648         else {
12649 
12650             dst++;
12651         }
12652         ele[dst] = ele[src];
12653     }
12654 
12655 
12656     dst++;
12657     if (dst < listObjPtr->internalRep.listValue.len) {
12658         ele[dst] = ele[src];
12659     }
12660 
12661 
12662     listObjPtr->internalRep.listValue.len = dst;
12663 }
12664 
12665 
ListSortElements(Jim_Interp * interp,Jim_Obj * listObjPtr,struct lsort_info * info)12666 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
12667 {
12668     struct lsort_info *prev_info;
12669 
12670     typedef int (qsort_comparator) (const void *, const void *);
12671     int (*fn) (Jim_Obj **, Jim_Obj **);
12672     Jim_Obj **vector;
12673     int len;
12674     int rc;
12675 
12676     JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
12677     SetListFromAny(interp, listObjPtr);
12678 
12679 
12680     prev_info = sort_info;
12681     sort_info = info;
12682 
12683     vector = listObjPtr->internalRep.listValue.ele;
12684     len = listObjPtr->internalRep.listValue.len;
12685     switch (info->type) {
12686         case JIM_LSORT_ASCII:
12687             fn = ListSortString;
12688             break;
12689         case JIM_LSORT_NOCASE:
12690             fn = ListSortStringNoCase;
12691             break;
12692         case JIM_LSORT_INTEGER:
12693             fn = ListSortInteger;
12694             break;
12695         case JIM_LSORT_REAL:
12696             fn = ListSortReal;
12697             break;
12698         case JIM_LSORT_COMMAND:
12699             fn = ListSortCommand;
12700             break;
12701         case JIM_LSORT_DICT:
12702             fn = ListSortDict;
12703             break;
12704         default:
12705             fn = NULL;
12706             JimPanic((1, "ListSort called with invalid sort type"));
12707             return -1;
12708     }
12709 
12710     if (info->indexc) {
12711 
12712         info->subfn = fn;
12713         fn = ListSortIndexHelper;
12714     }
12715 
12716     if ((rc = setjmp(info->jmpbuf)) == 0) {
12717         qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
12718 
12719         if (info->unique && len > 1) {
12720             ListRemoveDuplicates(listObjPtr, fn);
12721         }
12722 
12723         Jim_InvalidateStringRep(listObjPtr);
12724     }
12725     sort_info = prev_info;
12726 
12727     return rc;
12728 }
12729 
12730 
ListEnsureLength(Jim_Obj * listPtr,int idx)12731 static void ListEnsureLength(Jim_Obj *listPtr, int idx)
12732 {
12733     assert(idx >= 0);
12734     if (idx >= listPtr->internalRep.listValue.maxLen) {
12735         if (idx < 4) {
12736 
12737             idx = 4;
12738         }
12739         listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
12740             sizeof(Jim_Obj *) * idx);
12741 
12742         listPtr->internalRep.listValue.maxLen = idx;
12743     }
12744 }
12745 
ListInsertElements(Jim_Obj * listPtr,int idx,int elemc,Jim_Obj * const * elemVec)12746 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
12747 {
12748     int currentLen = listPtr->internalRep.listValue.len;
12749     int requiredLen = currentLen + elemc;
12750     int i;
12751     Jim_Obj **point;
12752 
12753     if (elemc == 0) {
12754 
12755         return;
12756     }
12757 
12758     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
12759         if (currentLen) {
12760 
12761             requiredLen *= 2;
12762         }
12763         ListEnsureLength(listPtr, requiredLen);
12764     }
12765     if (idx < 0) {
12766         idx = currentLen;
12767     }
12768     point = listPtr->internalRep.listValue.ele + idx;
12769     memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
12770     for (i = 0; i < elemc; ++i) {
12771         point[i] = elemVec[i];
12772         Jim_IncrRefCount(point[i]);
12773     }
12774     listPtr->internalRep.listValue.len += elemc;
12775 }
12776 
ListAppendElement(Jim_Obj * listPtr,Jim_Obj * objPtr)12777 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
12778 {
12779     ListInsertElements(listPtr, -1, 1, &objPtr);
12780 }
12781 
ListAppendList(Jim_Obj * listPtr,Jim_Obj * appendListPtr)12782 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
12783 {
12784     ListInsertElements(listPtr, -1,
12785         appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele);
12786 }
12787 
Jim_ListAppendElement(Jim_Interp * interp,Jim_Obj * listPtr,Jim_Obj * objPtr)12788 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
12789 {
12790     JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object"));
12791     SetListFromAny(interp, listPtr);
12792     Jim_InvalidateStringRep(listPtr);
12793     ListAppendElement(listPtr, objPtr);
12794 }
12795 
Jim_ListAppendList(Jim_Interp * interp,Jim_Obj * listPtr,Jim_Obj * appendListPtr)12796 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
12797 {
12798     JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object"));
12799     SetListFromAny(interp, listPtr);
12800     SetListFromAny(interp, appendListPtr);
12801     Jim_InvalidateStringRep(listPtr);
12802     ListAppendList(listPtr, appendListPtr);
12803 }
12804 
Jim_ListLength(Jim_Interp * interp,Jim_Obj * objPtr)12805 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
12806 {
12807     SetListFromAny(interp, objPtr);
12808     return objPtr->internalRep.listValue.len;
12809 }
12810 
Jim_ListInsertElements(Jim_Interp * interp,Jim_Obj * listPtr,int idx,int objc,Jim_Obj * const * objVec)12811 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
12812     int objc, Jim_Obj *const *objVec)
12813 {
12814     JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
12815     SetListFromAny(interp, listPtr);
12816     if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
12817         idx = listPtr->internalRep.listValue.len;
12818     else if (idx < 0)
12819         idx = 0;
12820     Jim_InvalidateStringRep(listPtr);
12821     ListInsertElements(listPtr, idx, objc, objVec);
12822 }
12823 
Jim_ListGetIndex(Jim_Interp * interp,Jim_Obj * listPtr,int idx)12824 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
12825 {
12826     SetListFromAny(interp, listPtr);
12827     if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
12828         (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
12829         return NULL;
12830     }
12831     if (idx < 0)
12832         idx = listPtr->internalRep.listValue.len + idx;
12833     return listPtr->internalRep.listValue.ele[idx];
12834 }
12835 
Jim_ListIndex(Jim_Interp * interp,Jim_Obj * listPtr,int idx,Jim_Obj ** objPtrPtr,int flags)12836 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
12837 {
12838     *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
12839     if (*objPtrPtr == NULL) {
12840         if (flags & JIM_ERRMSG) {
12841             Jim_SetResultString(interp, "list index out of range", -1);
12842         }
12843         return JIM_ERR;
12844     }
12845     return JIM_OK;
12846 }
12847 
Jim_ListIndices(Jim_Interp * interp,Jim_Obj * listPtr,Jim_Obj * const * indexv,int indexc,Jim_Obj ** resultObj,int flags)12848 static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr,
12849     Jim_Obj *const *indexv, int indexc, Jim_Obj **resultObj, int flags)
12850 {
12851     int i;
12852     int static_idxes[5];
12853     int *idxes = static_idxes;
12854     int ret = JIM_OK;
12855 
12856     if (indexc > sizeof(static_idxes) / sizeof(*static_idxes)) {
12857         idxes = Jim_Alloc(indexc * sizeof(*idxes));
12858     }
12859 
12860     for (i = 0; i < indexc; i++) {
12861         ret = Jim_GetIndex(interp, indexv[i], &idxes[i]);
12862         if (ret != JIM_OK) {
12863             goto err;
12864         }
12865     }
12866 
12867     for (i = 0; i < indexc; i++) {
12868         Jim_Obj *objPtr = Jim_ListGetIndex(interp, listPtr, idxes[i]);
12869         if (!objPtr) {
12870             if (flags & JIM_ERRMSG) {
12871                 if (idxes[i] < 0 || idxes[i] > Jim_ListLength(interp, listPtr)) {
12872                     Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
12873                 }
12874                 else {
12875                     Jim_SetResultFormatted(interp, "element %#s missing from sublist \"%#s\"", indexv[i], listPtr);
12876                 }
12877             }
12878             return -1;
12879         }
12880         listPtr = objPtr;
12881     }
12882     *resultObj = listPtr;
12883 err:
12884     if (idxes != static_idxes)
12885         Jim_Free(idxes);
12886     return ret;
12887 }
12888 
ListSetIndex(Jim_Interp * interp,Jim_Obj * listPtr,int idx,Jim_Obj * newObjPtr,int flags)12889 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
12890     Jim_Obj *newObjPtr, int flags)
12891 {
12892     SetListFromAny(interp, listPtr);
12893     if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
12894         (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
12895         if (flags & JIM_ERRMSG) {
12896             Jim_SetResultString(interp, "list index out of range", -1);
12897         }
12898         return JIM_ERR;
12899     }
12900     if (idx < 0)
12901         idx = listPtr->internalRep.listValue.len + idx;
12902     Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
12903     listPtr->internalRep.listValue.ele[idx] = newObjPtr;
12904     Jim_IncrRefCount(newObjPtr);
12905     return JIM_OK;
12906 }
12907 
Jim_ListSetIndex(Jim_Interp * interp,Jim_Obj * varNamePtr,Jim_Obj * const * indexv,int indexc,Jim_Obj * newObjPtr)12908 int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
12909     Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
12910 {
12911     Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
12912     int shared, i, idx;
12913 
12914     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
12915     if (objPtr == NULL)
12916         return JIM_ERR;
12917     if ((shared = Jim_IsShared(objPtr)))
12918         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
12919     for (i = 0; i < indexc - 1; i++) {
12920         listObjPtr = objPtr;
12921         if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
12922             goto err;
12923 
12924         objPtr = Jim_ListGetIndex(interp, listObjPtr, idx);
12925         if (objPtr == NULL) {
12926             Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
12927             goto err;
12928         }
12929         if (Jim_IsShared(objPtr)) {
12930             objPtr = Jim_DuplicateObj(interp, objPtr);
12931             ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
12932         }
12933         Jim_InvalidateStringRep(listObjPtr);
12934     }
12935     if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
12936         goto err;
12937     if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
12938         goto err;
12939     Jim_InvalidateStringRep(objPtr);
12940     Jim_InvalidateStringRep(varObjPtr);
12941     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
12942         goto err;
12943     Jim_SetResult(interp, varObjPtr);
12944     return JIM_OK;
12945   err:
12946     if (shared) {
12947         Jim_FreeNewObj(interp, varObjPtr);
12948     }
12949     return JIM_ERR;
12950 }
12951 
Jim_ListJoin(Jim_Interp * interp,Jim_Obj * listObjPtr,const char * joinStr,int joinStrLen)12952 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
12953 {
12954     int i;
12955     int listLen = Jim_ListLength(interp, listObjPtr);
12956     Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
12957 
12958     for (i = 0; i < listLen; ) {
12959         Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i));
12960         if (++i != listLen) {
12961             Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
12962         }
12963     }
12964     return resObjPtr;
12965 }
12966 
Jim_ConcatObj(Jim_Interp * interp,int objc,Jim_Obj * const * objv)12967 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
12968 {
12969     int i;
12970 
12971     for (i = 0; i < objc; i++) {
12972         if (!Jim_IsList(objv[i]))
12973             break;
12974     }
12975     if (i == objc) {
12976         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
12977 
12978         for (i = 0; i < objc; i++)
12979             ListAppendList(objPtr, objv[i]);
12980         return objPtr;
12981     }
12982     else {
12983 
12984         int len = 0, objLen;
12985         char *bytes, *p;
12986 
12987 
12988         for (i = 0; i < objc; i++) {
12989             len += Jim_Length(objv[i]);
12990         }
12991         if (objc)
12992             len += objc - 1;
12993 
12994         p = bytes = Jim_Alloc(len + 1);
12995         for (i = 0; i < objc; i++) {
12996             const char *s = Jim_GetString(objv[i], &objLen);
12997 
12998 
12999             while (objLen && isspace(UCHAR(*s))) {
13000                 s++;
13001                 objLen--;
13002                 len--;
13003             }
13004 
13005             while (objLen && isspace(UCHAR(s[objLen - 1]))) {
13006 
13007                 if (objLen > 1 && s[objLen - 2] == '\\') {
13008                     break;
13009                 }
13010                 objLen--;
13011                 len--;
13012             }
13013             memcpy(p, s, objLen);
13014             p += objLen;
13015             if (i + 1 != objc) {
13016                 if (objLen)
13017                     *p++ = ' ';
13018                 else {
13019                     len--;
13020                 }
13021             }
13022         }
13023         *p = '\0';
13024         return Jim_NewStringObjNoAlloc(interp, bytes, len);
13025     }
13026 }
13027 
Jim_ListRange(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr)13028 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr,
13029     Jim_Obj *lastObjPtr)
13030 {
13031     int first, last;
13032     int len, rangeLen;
13033 
13034     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
13035         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
13036         return NULL;
13037     len = Jim_ListLength(interp, listObjPtr);
13038     first = JimRelToAbsIndex(len, first);
13039     last = JimRelToAbsIndex(len, last);
13040     JimRelToAbsRange(len, &first, &last, &rangeLen);
13041     if (first == 0 && last == len) {
13042         return listObjPtr;
13043     }
13044     return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
13045 }
13046 
13047 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
13048 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
13049 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
13050 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13051 
13052 
13053 static const Jim_ObjType dictObjType = {
13054     "dict",
13055     FreeDictInternalRep,
13056     DupDictInternalRep,
13057     UpdateStringOfDict,
13058     JIM_TYPE_NONE,
13059 };
13060 
JimFreeDict(Jim_Interp * interp,Jim_Dict * dict)13061 static void JimFreeDict(Jim_Interp *interp, Jim_Dict *dict)
13062 {
13063     int i;
13064     for (i = 0; i < dict->len; i++) {
13065         Jim_DecrRefCount(interp, dict->table[i]);
13066     }
13067     Jim_Free(dict->table);
13068     Jim_Free(dict->ht);
13069     Jim_Free(dict);
13070 }
13071 
13072 enum {
13073     DICT_HASH_FIND = -1,
13074     DICT_HASH_REMOVE = -2,
13075     DICT_HASH_ADD = -3,
13076 };
13077 
JimDictHashFind(Jim_Dict * dict,Jim_Obj * keyObjPtr,int op_tvoffset)13078 static int JimDictHashFind(Jim_Dict *dict, Jim_Obj *keyObjPtr, int op_tvoffset)
13079 {
13080     unsigned h = (JimObjectHTHashFunction(keyObjPtr) + dict->uniq);
13081     unsigned idx = h & dict->sizemask;
13082     int tvoffset = 0;
13083     unsigned peturb = h;
13084     unsigned first_removed = ~0;
13085 
13086     if (dict->len) {
13087         while ((tvoffset = dict->ht[idx].offset)) {
13088             if (tvoffset == -1) {
13089                 if (first_removed == ~0) {
13090                     first_removed = idx;
13091                 }
13092             }
13093             else if (dict->ht[idx].hash == h) {
13094                 if (Jim_StringEqObj(keyObjPtr, dict->table[tvoffset - 1])) {
13095                     break;
13096                 }
13097             }
13098 
13099             peturb >>= 5;
13100             idx = (5 * idx + 1 + peturb) & dict->sizemask;
13101         }
13102     }
13103 
13104     switch (op_tvoffset) {
13105         case DICT_HASH_FIND:
13106 
13107             break;
13108         case DICT_HASH_REMOVE:
13109             if (tvoffset) {
13110 
13111                 dict->ht[idx].offset = -1;
13112                 dict->dummy++;
13113             }
13114 
13115             break;
13116         case DICT_HASH_ADD:
13117             if (tvoffset == 0) {
13118 
13119                 if (first_removed != ~0) {
13120                     idx = first_removed;
13121                     dict->dummy--;
13122                 }
13123                 dict->ht[idx].offset = dict->len + 1;
13124                 dict->ht[idx].hash = h;
13125             }
13126 
13127             break;
13128         default:
13129             assert(tvoffset);
13130 
13131             dict->ht[idx].offset = op_tvoffset;
13132             break;
13133     }
13134 
13135     return tvoffset;
13136 }
13137 
JimDictExpandHashTable(Jim_Dict * dict,unsigned int size)13138 static void JimDictExpandHashTable(Jim_Dict *dict, unsigned int size)
13139 {
13140     int i;
13141     struct JimDictHashEntry *prevht = dict->ht;
13142     int prevsize = dict->size;
13143 
13144     dict->size = JimHashTableNextPower(size);
13145     dict->sizemask = dict->size - 1;
13146 
13147 
13148     dict->ht = Jim_Alloc(dict->size * sizeof(*dict->ht));
13149     memset(dict->ht, 0, dict->size * sizeof(*dict->ht));
13150 
13151 
13152     for (i = 0; i < prevsize; i++) {
13153         if (prevht[i].offset > 0) {
13154 
13155             unsigned h = prevht[i].hash;
13156             unsigned idx = h & dict->sizemask;
13157             unsigned peturb = h;
13158 
13159             while (dict->ht[idx].offset) {
13160                 peturb >>= 5;
13161                 idx = (5 * idx + 1 + peturb) & dict->sizemask;
13162             }
13163             dict->ht[idx].offset = prevht[i].offset;
13164             dict->ht[idx].hash = h;
13165         }
13166     }
13167     Jim_Free(prevht);
13168 }
13169 
JimDictAdd(Jim_Dict * dict,Jim_Obj * keyObjPtr)13170 static int JimDictAdd(Jim_Dict *dict, Jim_Obj *keyObjPtr)
13171 {
13172     if (dict->size <= dict->len + dict->dummy) {
13173         JimDictExpandHashTable(dict, dict->size ? dict->size * 2 : 8);
13174     }
13175     return JimDictHashFind(dict, keyObjPtr, DICT_HASH_ADD);
13176 }
13177 
JimDictNew(Jim_Interp * interp,int table_size,int ht_size)13178 static Jim_Dict *JimDictNew(Jim_Interp *interp, int table_size, int ht_size)
13179 {
13180     Jim_Dict *dict = Jim_Alloc(sizeof(*dict));
13181     memset(dict, 0, sizeof(*dict));
13182 
13183     if (ht_size) {
13184         JimDictExpandHashTable(dict, ht_size);
13185     }
13186     if (table_size) {
13187         dict->table = Jim_Alloc(table_size * sizeof(*dict->table));
13188         dict->maxLen = table_size;
13189     }
13190 #ifdef JIM_RANDOMISE_HASH
13191     dict->uniq = (rand() ^ time(NULL) ^ clock());
13192 #endif
13193     return dict;
13194 }
13195 
FreeDictInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)13196 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
13197 {
13198     JimFreeDict(interp, objPtr->internalRep.dictValue);
13199 }
13200 
DupDictInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)13201 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
13202 {
13203     Jim_Dict *oldDict = srcPtr->internalRep.dictValue;
13204     int i;
13205 
13206 
13207     Jim_Dict *newDict = JimDictNew(interp, oldDict->maxLen, oldDict->size);
13208 
13209 
13210     for (i = 0; i < oldDict->len; i++) {
13211         newDict->table[i] = oldDict->table[i];
13212         Jim_IncrRefCount(newDict->table[i]);
13213     }
13214     newDict->len = oldDict->len;
13215 
13216 
13217     newDict->uniq = oldDict->uniq;
13218 
13219 
13220     memcpy(newDict->ht, oldDict->ht, sizeof(*oldDict->ht) * oldDict->size);
13221 
13222     dupPtr->internalRep.dictValue = newDict;
13223     dupPtr->typePtr = &dictObjType;
13224 }
13225 
UpdateStringOfDict(struct Jim_Obj * objPtr)13226 static void UpdateStringOfDict(struct Jim_Obj *objPtr)
13227 {
13228     JimMakeListStringRep(objPtr, objPtr->internalRep.dictValue->table, objPtr->internalRep.dictValue->len);
13229 }
13230 
SetDictFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)13231 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
13232 {
13233     int listlen;
13234 
13235     if (objPtr->typePtr == &dictObjType) {
13236         return JIM_OK;
13237     }
13238 
13239     if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
13240         Jim_String(objPtr);
13241     }
13242 
13243     listlen = Jim_ListLength(interp, objPtr);
13244     if (listlen % 2) {
13245         Jim_SetResultString(interp, "missing value to go with key", -1);
13246         return JIM_ERR;
13247     }
13248     else {
13249 
13250         Jim_Dict *dict = JimDictNew(interp, 0, listlen);
13251         int i;
13252 
13253 
13254         dict->table = objPtr->internalRep.listValue.ele;
13255         dict->maxLen = objPtr->internalRep.listValue.maxLen;
13256 
13257 
13258         for (i = 0; i < listlen; i += 2) {
13259             int tvoffset = JimDictAdd(dict, dict->table[i]);
13260             if (tvoffset) {
13261 
13262 
13263                 Jim_DecrRefCount(interp, dict->table[tvoffset]);
13264 
13265                 dict->table[tvoffset] = dict->table[i + 1];
13266 
13267                 Jim_DecrRefCount(interp, dict->table[i]);
13268             }
13269             else {
13270                 if (dict->len != i) {
13271                     dict->table[dict->len++] = dict->table[i];
13272                     dict->table[dict->len++] = dict->table[i + 1];
13273                 }
13274                 else {
13275                     dict->len += 2;
13276                 }
13277             }
13278         }
13279 
13280         objPtr->typePtr = &dictObjType;
13281         objPtr->internalRep.dictValue = dict;
13282 
13283         return JIM_OK;
13284     }
13285 }
13286 
13287 
13288 
DictAddElement(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * keyObjPtr,Jim_Obj * valueObjPtr)13289 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
13290     Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
13291 {
13292     Jim_Dict *dict = objPtr->internalRep.dictValue;
13293     if (valueObjPtr == NULL) {
13294 
13295         int tvoffset = JimDictHashFind(dict, keyObjPtr, DICT_HASH_REMOVE);
13296         if (tvoffset) {
13297 
13298             Jim_DecrRefCount(interp, dict->table[tvoffset - 1]);
13299             Jim_DecrRefCount(interp, dict->table[tvoffset]);
13300             dict->len -= 2;
13301             if (tvoffset != dict->len + 1) {
13302 
13303                 dict->table[tvoffset - 1] = dict->table[dict->len];
13304                 dict->table[tvoffset] = dict->table[dict->len + 1];
13305 
13306 
13307                 JimDictHashFind(dict, dict->table[tvoffset - 1], tvoffset);
13308             }
13309             return JIM_OK;
13310         }
13311         return JIM_ERR;
13312     }
13313     else {
13314 
13315         int tvoffset = JimDictAdd(dict, keyObjPtr);
13316         if (tvoffset) {
13317 
13318             Jim_IncrRefCount(valueObjPtr);
13319             Jim_DecrRefCount(interp, dict->table[tvoffset]);
13320             dict->table[tvoffset] = valueObjPtr;
13321         }
13322         else {
13323             if (dict->maxLen == dict->len) {
13324 
13325                 if (dict->maxLen < 4) {
13326                     dict->maxLen = 4;
13327                 }
13328                 else {
13329                     dict->maxLen *= 2;
13330                 }
13331                 dict->table = Jim_Realloc(dict->table, dict->maxLen * sizeof(*dict->table));
13332             }
13333             Jim_IncrRefCount(keyObjPtr);
13334             Jim_IncrRefCount(valueObjPtr);
13335 
13336             dict->table[dict->len++] = keyObjPtr;
13337             dict->table[dict->len++] = valueObjPtr;
13338 
13339         }
13340         return JIM_OK;
13341     }
13342 }
13343 
Jim_DictAddElement(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * keyObjPtr,Jim_Obj * valueObjPtr)13344 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
13345     Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
13346 {
13347     JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
13348     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
13349         return JIM_ERR;
13350     }
13351     Jim_InvalidateStringRep(objPtr);
13352     return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
13353 }
13354 
Jim_NewDictObj(Jim_Interp * interp,Jim_Obj * const * elements,int len)13355 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
13356 {
13357     Jim_Obj *objPtr;
13358     int i;
13359 
13360     JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
13361 
13362     objPtr = Jim_NewObj(interp);
13363     objPtr->typePtr = &dictObjType;
13364     objPtr->bytes = NULL;
13365 
13366     objPtr->internalRep.dictValue = JimDictNew(interp, len, len);
13367     for (i = 0; i < len; i += 2)
13368         DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
13369     return objPtr;
13370 }
13371 
Jim_DictKey(Jim_Interp * interp,Jim_Obj * dictPtr,Jim_Obj * keyPtr,Jim_Obj ** objPtrPtr,int flags)13372 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
13373     Jim_Obj **objPtrPtr, int flags)
13374 {
13375     int tvoffset;
13376     Jim_Dict *dict;
13377 
13378     if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
13379         return -1;
13380     }
13381     dict = dictPtr->internalRep.dictValue;
13382     tvoffset = JimDictHashFind(dict, keyPtr, DICT_HASH_FIND);
13383     if (tvoffset == 0) {
13384         if (flags & JIM_ERRMSG) {
13385             Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
13386         }
13387         return JIM_ERR;
13388     }
13389     *objPtrPtr = dict->table[tvoffset];
13390     return JIM_OK;
13391 }
13392 
Jim_DictPairs(Jim_Interp * interp,Jim_Obj * dictPtr,int * len)13393 Jim_Obj **Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, int *len)
13394 {
13395 
13396     if (Jim_IsList(dictPtr)) {
13397         Jim_Obj **table;
13398         JimListGetElements(interp, dictPtr, len, &table);
13399         if (*len % 2 == 0) {
13400             return table;
13401         }
13402 
13403     }
13404     if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
13405 
13406         *len = 1;
13407         return NULL;
13408     }
13409     *len = dictPtr->internalRep.dictValue->len;
13410     return dictPtr->internalRep.dictValue->table;
13411 }
13412 
13413 
Jim_DictKeysVector(Jim_Interp * interp,Jim_Obj * dictPtr,Jim_Obj * const * keyv,int keyc,Jim_Obj ** objPtrPtr,int flags)13414 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
13415     Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
13416 {
13417     int i;
13418 
13419     if (keyc == 0) {
13420         *objPtrPtr = dictPtr;
13421         return JIM_OK;
13422     }
13423 
13424     for (i = 0; i < keyc; i++) {
13425         Jim_Obj *objPtr;
13426 
13427         int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
13428         if (rc != JIM_OK) {
13429             return rc;
13430         }
13431         dictPtr = objPtr;
13432     }
13433     *objPtrPtr = dictPtr;
13434     return JIM_OK;
13435 }
13436 
Jim_SetDictKeysVector(Jim_Interp * interp,Jim_Obj * varNamePtr,Jim_Obj * const * keyv,int keyc,Jim_Obj * newObjPtr,int flags)13437 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
13438     Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags)
13439 {
13440     Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
13441     int shared, i;
13442 
13443     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
13444     if (objPtr == NULL) {
13445         if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
13446 
13447             return JIM_ERR;
13448         }
13449         varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
13450         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
13451             Jim_FreeNewObj(interp, varObjPtr);
13452             return JIM_ERR;
13453         }
13454     }
13455     if ((shared = Jim_IsShared(objPtr)))
13456         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
13457     for (i = 0; i < keyc; i++) {
13458         dictObjPtr = objPtr;
13459 
13460 
13461         if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
13462             goto err;
13463         }
13464 
13465         if (i == keyc - 1) {
13466 
13467             if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
13468                 if (newObjPtr || (flags & JIM_MUSTEXIST)) {
13469                     goto err;
13470                 }
13471             }
13472             break;
13473         }
13474 
13475 
13476         Jim_InvalidateStringRep(dictObjPtr);
13477         if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
13478                 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
13479             if (Jim_IsShared(objPtr)) {
13480                 objPtr = Jim_DuplicateObj(interp, objPtr);
13481                 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
13482             }
13483         }
13484         else {
13485             if (newObjPtr == NULL) {
13486                 goto err;
13487             }
13488             objPtr = Jim_NewDictObj(interp, NULL, 0);
13489             DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
13490         }
13491     }
13492 
13493     Jim_InvalidateStringRep(objPtr);
13494     Jim_InvalidateStringRep(varObjPtr);
13495     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
13496         goto err;
13497     }
13498 
13499     if (!(flags & JIM_NORESULT)) {
13500         Jim_SetResult(interp, varObjPtr);
13501     }
13502     return JIM_OK;
13503   err:
13504     if (shared) {
13505         Jim_FreeNewObj(interp, varObjPtr);
13506     }
13507     return JIM_ERR;
13508 }
13509 
13510 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
13511 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13512 
13513 static const Jim_ObjType indexObjType = {
13514     "index",
13515     NULL,
13516     NULL,
13517     UpdateStringOfIndex,
13518     JIM_TYPE_NONE,
13519 };
13520 
UpdateStringOfIndex(struct Jim_Obj * objPtr)13521 static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
13522 {
13523     if (objPtr->internalRep.intValue == -1) {
13524         JimSetStringBytes(objPtr, "end");
13525     }
13526     else {
13527         char buf[JIM_INTEGER_SPACE + 1];
13528         if (objPtr->internalRep.intValue >= 0 || objPtr->internalRep.intValue == -INT_MAX) {
13529             sprintf(buf, "%d", objPtr->internalRep.intValue);
13530         }
13531         else {
13532 
13533             sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
13534         }
13535         JimSetStringBytes(objPtr, buf);
13536     }
13537 }
13538 
SetIndexFromAny(Jim_Interp * interp,Jim_Obj * objPtr)13539 static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
13540 {
13541     jim_wide idx;
13542     int end = 0;
13543     const char *str;
13544     Jim_Obj *exprObj = objPtr;
13545 
13546     JimPanic((objPtr->refCount == 0, "SetIndexFromAny() called with zero refcount object"));
13547 
13548 
13549     str = Jim_String(objPtr);
13550 
13551 
13552     if (strncmp(str, "end", 3) == 0) {
13553         end = 1;
13554         str += 3;
13555         idx = 0;
13556         switch (*str) {
13557             case '\0':
13558                 exprObj = NULL;
13559                 break;
13560 
13561             case '-':
13562             case '+':
13563                 exprObj = Jim_NewStringObj(interp, str, -1);
13564                 break;
13565 
13566             default:
13567                 goto badindex;
13568         }
13569     }
13570     if (exprObj) {
13571         int ret;
13572         Jim_IncrRefCount(exprObj);
13573         ret = Jim_GetWideExpr(interp, exprObj, &idx);
13574         Jim_DecrRefCount(interp, exprObj);
13575         if (ret != JIM_OK) {
13576             goto badindex;
13577         }
13578     }
13579 
13580     if (end) {
13581         if (idx > 0) {
13582             idx = INT_MAX;
13583         }
13584         else {
13585 
13586             idx--;
13587         }
13588     }
13589     else if (idx < 0) {
13590         idx = -INT_MAX;
13591     }
13592 
13593 
13594     Jim_FreeIntRep(interp, objPtr);
13595     objPtr->typePtr = &indexObjType;
13596     objPtr->internalRep.intValue = idx;
13597     return JIM_OK;
13598 
13599   badindex:
13600     Jim_SetResultFormatted(interp,
13601         "bad index \"%#s\": must be intexpr or end?[+-]intexpr?", objPtr);
13602     return JIM_ERR;
13603 }
13604 
Jim_GetIndex(Jim_Interp * interp,Jim_Obj * objPtr,int * indexPtr)13605 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
13606 {
13607 
13608     if (objPtr->typePtr == &intObjType) {
13609         jim_wide val = JimWideValue(objPtr);
13610 
13611         if (val < 0)
13612             *indexPtr = -INT_MAX;
13613         else if (val > INT_MAX)
13614             *indexPtr = INT_MAX;
13615         else
13616             *indexPtr = (int)val;
13617         return JIM_OK;
13618     }
13619     if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
13620         return JIM_ERR;
13621     *indexPtr = objPtr->internalRep.intValue;
13622     return JIM_OK;
13623 }
13624 
13625 
13626 
13627 static const char * const jimReturnCodes[] = {
13628     "ok",
13629     "error",
13630     "return",
13631     "break",
13632     "continue",
13633     "signal",
13634     "exit",
13635     "eval",
13636     NULL
13637 };
13638 
13639 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1)
13640 
13641 static const Jim_ObjType returnCodeObjType = {
13642     "return-code",
13643     NULL,
13644     NULL,
13645     NULL,
13646     JIM_TYPE_NONE,
13647 };
13648 
Jim_ReturnCode(int code)13649 const char *Jim_ReturnCode(int code)
13650 {
13651     if (code < 0 || code >= (int)jimReturnCodesSize) {
13652         return "?";
13653     }
13654     else {
13655         return jimReturnCodes[code];
13656     }
13657 }
13658 
SetReturnCodeFromAny(Jim_Interp * interp,Jim_Obj * objPtr)13659 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
13660 {
13661     int returnCode;
13662     jim_wide wideValue;
13663 
13664 
13665     if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
13666         returnCode = (int)wideValue;
13667     else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
13668         Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
13669         return JIM_ERR;
13670     }
13671 
13672     Jim_FreeIntRep(interp, objPtr);
13673     objPtr->typePtr = &returnCodeObjType;
13674     objPtr->internalRep.intValue = returnCode;
13675     return JIM_OK;
13676 }
13677 
Jim_GetReturnCode(Jim_Interp * interp,Jim_Obj * objPtr,int * intPtr)13678 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
13679 {
13680     if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
13681         return JIM_ERR;
13682     *intPtr = objPtr->internalRep.intValue;
13683     return JIM_OK;
13684 }
13685 
13686 static int JimParseExprOperator(struct JimParserCtx *pc);
13687 static int JimParseExprNumber(struct JimParserCtx *pc);
13688 static int JimParseExprIrrational(struct JimParserCtx *pc);
13689 static int JimParseExprBoolean(struct JimParserCtx *pc);
13690 
13691 
13692 enum
13693 {
13694 
13695 
13696 
13697     JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
13698     JIM_EXPROP_DIV,
13699     JIM_EXPROP_MOD,
13700     JIM_EXPROP_SUB,
13701     JIM_EXPROP_ADD,
13702     JIM_EXPROP_LSHIFT,
13703     JIM_EXPROP_RSHIFT,
13704     JIM_EXPROP_ROTL,
13705     JIM_EXPROP_ROTR,
13706     JIM_EXPROP_LT,
13707     JIM_EXPROP_GT,
13708     JIM_EXPROP_LTE,
13709     JIM_EXPROP_GTE,
13710     JIM_EXPROP_NUMEQ,
13711     JIM_EXPROP_NUMNE,
13712     JIM_EXPROP_BITAND,
13713     JIM_EXPROP_BITXOR,
13714     JIM_EXPROP_BITOR,
13715     JIM_EXPROP_LOGICAND,
13716     JIM_EXPROP_LOGICOR,
13717     JIM_EXPROP_TERNARY,
13718     JIM_EXPROP_COLON,
13719     JIM_EXPROP_POW,
13720 
13721 
13722     JIM_EXPROP_STREQ,
13723     JIM_EXPROP_STRNE,
13724     JIM_EXPROP_STRIN,
13725     JIM_EXPROP_STRNI,
13726     JIM_EXPROP_STRLT,
13727     JIM_EXPROP_STRGT,
13728     JIM_EXPROP_STRLE,
13729     JIM_EXPROP_STRGE,
13730 
13731 
13732     JIM_EXPROP_NOT,
13733     JIM_EXPROP_BITNOT,
13734     JIM_EXPROP_UNARYMINUS,
13735     JIM_EXPROP_UNARYPLUS,
13736 
13737 
13738     JIM_EXPROP_FUNC_INT,
13739     JIM_EXPROP_FUNC_WIDE,
13740     JIM_EXPROP_FUNC_ABS,
13741     JIM_EXPROP_FUNC_DOUBLE,
13742     JIM_EXPROP_FUNC_ROUND,
13743     JIM_EXPROP_FUNC_RAND,
13744     JIM_EXPROP_FUNC_SRAND,
13745 
13746 
13747     JIM_EXPROP_FUNC_SIN,
13748     JIM_EXPROP_FUNC_COS,
13749     JIM_EXPROP_FUNC_TAN,
13750     JIM_EXPROP_FUNC_ASIN,
13751     JIM_EXPROP_FUNC_ACOS,
13752     JIM_EXPROP_FUNC_ATAN,
13753     JIM_EXPROP_FUNC_ATAN2,
13754     JIM_EXPROP_FUNC_SINH,
13755     JIM_EXPROP_FUNC_COSH,
13756     JIM_EXPROP_FUNC_TANH,
13757     JIM_EXPROP_FUNC_CEIL,
13758     JIM_EXPROP_FUNC_FLOOR,
13759     JIM_EXPROP_FUNC_EXP,
13760     JIM_EXPROP_FUNC_LOG,
13761     JIM_EXPROP_FUNC_LOG10,
13762     JIM_EXPROP_FUNC_SQRT,
13763     JIM_EXPROP_FUNC_POW,
13764     JIM_EXPROP_FUNC_HYPOT,
13765     JIM_EXPROP_FUNC_FMOD,
13766 };
13767 
13768 struct JimExprNode {
13769     int type;
13770     struct Jim_Obj *objPtr;
13771 
13772     struct JimExprNode *left;
13773     struct JimExprNode *right;
13774     struct JimExprNode *ternary;
13775 };
13776 
13777 
13778 typedef struct Jim_ExprOperator
13779 {
13780     const char *name;
13781     int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode);
13782     unsigned char precedence;
13783     unsigned char arity;
13784     unsigned char attr;
13785     unsigned char namelen;
13786 } Jim_ExprOperator;
13787 
13788 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr);
13789 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
13790 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);
13791 
JimExprOpNumUnary(Jim_Interp * interp,struct JimExprNode * node)13792 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
13793 {
13794     int intresult = 1;
13795     int rc, bA = 0;
13796     double dA, dC = 0;
13797     jim_wide wA, wC = 0;
13798     Jim_Obj *A;
13799 
13800     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13801         return rc;
13802     }
13803 
13804     if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
13805         switch (node->type) {
13806             case JIM_EXPROP_FUNC_INT:
13807             case JIM_EXPROP_FUNC_WIDE:
13808             case JIM_EXPROP_FUNC_ROUND:
13809             case JIM_EXPROP_UNARYPLUS:
13810                 wC = wA;
13811                 break;
13812             case JIM_EXPROP_FUNC_DOUBLE:
13813                 dC = wA;
13814                 intresult = 0;
13815                 break;
13816             case JIM_EXPROP_FUNC_ABS:
13817                 wC = wA >= 0 ? wA : -wA;
13818                 break;
13819             case JIM_EXPROP_UNARYMINUS:
13820                 wC = -wA;
13821                 break;
13822             case JIM_EXPROP_NOT:
13823                 wC = !wA;
13824                 break;
13825             default:
13826                 abort();
13827         }
13828     }
13829     else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
13830         switch (node->type) {
13831             case JIM_EXPROP_FUNC_INT:
13832             case JIM_EXPROP_FUNC_WIDE:
13833                 wC = dA;
13834                 break;
13835             case JIM_EXPROP_FUNC_ROUND:
13836                 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
13837                 break;
13838             case JIM_EXPROP_FUNC_DOUBLE:
13839             case JIM_EXPROP_UNARYPLUS:
13840                 dC = dA;
13841                 intresult = 0;
13842                 break;
13843             case JIM_EXPROP_FUNC_ABS:
13844 #ifdef JIM_MATH_FUNCTIONS
13845                 dC = fabs(dA);
13846 #else
13847                 dC = dA >= 0 ? dA : -dA;
13848 #endif
13849                 intresult = 0;
13850                 break;
13851             case JIM_EXPROP_UNARYMINUS:
13852                 dC = -dA;
13853                 intresult = 0;
13854                 break;
13855             case JIM_EXPROP_NOT:
13856                 wC = !dA;
13857                 break;
13858             default:
13859                 abort();
13860         }
13861     }
13862     else if ((rc = Jim_GetBoolean(interp, A, &bA)) == JIM_OK) {
13863         switch (node->type) {
13864             case JIM_EXPROP_NOT:
13865                 wC = !bA;
13866                 break;
13867             case JIM_EXPROP_UNARYPLUS:
13868             case JIM_EXPROP_UNARYMINUS:
13869                 rc = JIM_ERR;
13870                 Jim_SetResultFormatted(interp,
13871                     "can't use non-numeric string as operand of \"%s\"",
13872                         node->type == JIM_EXPROP_UNARYPLUS ? "+" : "-");
13873                 break;
13874             default:
13875                 abort();
13876         }
13877     }
13878 
13879     if (rc == JIM_OK) {
13880         if (intresult) {
13881             Jim_SetResultInt(interp, wC);
13882         }
13883         else {
13884             Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
13885         }
13886     }
13887 
13888     Jim_DecrRefCount(interp, A);
13889 
13890     return rc;
13891 }
13892 
JimRandDouble(Jim_Interp * interp)13893 static double JimRandDouble(Jim_Interp *interp)
13894 {
13895     unsigned long x;
13896     JimRandomBytes(interp, &x, sizeof(x));
13897 
13898     return (double)x / (double)~0UL;
13899 }
13900 
JimExprOpIntUnary(Jim_Interp * interp,struct JimExprNode * node)13901 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node)
13902 {
13903     jim_wide wA;
13904     Jim_Obj *A;
13905     int rc;
13906 
13907     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13908         return rc;
13909     }
13910 
13911     rc = Jim_GetWide(interp, A, &wA);
13912     if (rc == JIM_OK) {
13913         switch (node->type) {
13914             case JIM_EXPROP_BITNOT:
13915                 Jim_SetResultInt(interp, ~wA);
13916                 break;
13917             case JIM_EXPROP_FUNC_SRAND:
13918                 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
13919                 Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
13920                 break;
13921             default:
13922                 abort();
13923         }
13924     }
13925 
13926     Jim_DecrRefCount(interp, A);
13927 
13928     return rc;
13929 }
13930 
JimExprOpNone(Jim_Interp * interp,struct JimExprNode * node)13931 static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node)
13932 {
13933     JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
13934 
13935     Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
13936 
13937     return JIM_OK;
13938 }
13939 
13940 #ifdef JIM_MATH_FUNCTIONS
JimExprOpDoubleUnary(Jim_Interp * interp,struct JimExprNode * node)13941 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node)
13942 {
13943     int rc;
13944     double dA, dC;
13945     Jim_Obj *A;
13946 
13947     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13948         return rc;
13949     }
13950 
13951     rc = Jim_GetDouble(interp, A, &dA);
13952     if (rc == JIM_OK) {
13953         switch (node->type) {
13954             case JIM_EXPROP_FUNC_SIN:
13955                 dC = sin(dA);
13956                 break;
13957             case JIM_EXPROP_FUNC_COS:
13958                 dC = cos(dA);
13959                 break;
13960             case JIM_EXPROP_FUNC_TAN:
13961                 dC = tan(dA);
13962                 break;
13963             case JIM_EXPROP_FUNC_ASIN:
13964                 dC = asin(dA);
13965                 break;
13966             case JIM_EXPROP_FUNC_ACOS:
13967                 dC = acos(dA);
13968                 break;
13969             case JIM_EXPROP_FUNC_ATAN:
13970                 dC = atan(dA);
13971                 break;
13972             case JIM_EXPROP_FUNC_SINH:
13973                 dC = sinh(dA);
13974                 break;
13975             case JIM_EXPROP_FUNC_COSH:
13976                 dC = cosh(dA);
13977                 break;
13978             case JIM_EXPROP_FUNC_TANH:
13979                 dC = tanh(dA);
13980                 break;
13981             case JIM_EXPROP_FUNC_CEIL:
13982                 dC = ceil(dA);
13983                 break;
13984             case JIM_EXPROP_FUNC_FLOOR:
13985                 dC = floor(dA);
13986                 break;
13987             case JIM_EXPROP_FUNC_EXP:
13988                 dC = exp(dA);
13989                 break;
13990             case JIM_EXPROP_FUNC_LOG:
13991                 dC = log(dA);
13992                 break;
13993             case JIM_EXPROP_FUNC_LOG10:
13994                 dC = log10(dA);
13995                 break;
13996             case JIM_EXPROP_FUNC_SQRT:
13997                 dC = sqrt(dA);
13998                 break;
13999             default:
14000                 abort();
14001         }
14002         Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
14003     }
14004 
14005     Jim_DecrRefCount(interp, A);
14006 
14007     return rc;
14008 }
14009 #endif
14010 
14011 
JimExprOpIntBin(Jim_Interp * interp,struct JimExprNode * node)14012 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node)
14013 {
14014     jim_wide wA, wB;
14015     int rc;
14016     Jim_Obj *A, *B;
14017 
14018     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
14019         return rc;
14020     }
14021     if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
14022         Jim_DecrRefCount(interp, A);
14023         return rc;
14024     }
14025 
14026     rc = JIM_ERR;
14027 
14028     if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
14029         jim_wide wC;
14030 
14031         rc = JIM_OK;
14032 
14033         switch (node->type) {
14034             case JIM_EXPROP_LSHIFT:
14035                 wC = wA << wB;
14036                 break;
14037             case JIM_EXPROP_RSHIFT:
14038                 wC = wA >> wB;
14039                 break;
14040             case JIM_EXPROP_BITAND:
14041                 wC = wA & wB;
14042                 break;
14043             case JIM_EXPROP_BITXOR:
14044                 wC = wA ^ wB;
14045                 break;
14046             case JIM_EXPROP_BITOR:
14047                 wC = wA | wB;
14048                 break;
14049             case JIM_EXPROP_MOD:
14050                 if (wB == 0) {
14051                     wC = 0;
14052                     Jim_SetResultString(interp, "Division by zero", -1);
14053                     rc = JIM_ERR;
14054                 }
14055                 else {
14056                     int negative = 0;
14057 
14058                     if (wB < 0) {
14059                         wB = -wB;
14060                         wA = -wA;
14061                         negative = 1;
14062                     }
14063                     wC = wA % wB;
14064                     if (wC < 0) {
14065                         wC += wB;
14066                     }
14067                     if (negative) {
14068                         wC = -wC;
14069                     }
14070                 }
14071                 break;
14072             case JIM_EXPROP_ROTL:
14073             case JIM_EXPROP_ROTR:{
14074 
14075                     unsigned long uA = (unsigned long)wA;
14076                     unsigned long uB = (unsigned long)wB;
14077                     const unsigned int S = sizeof(unsigned long) * 8;
14078 
14079 
14080                     uB %= S;
14081 
14082                     if (node->type == JIM_EXPROP_ROTR) {
14083                         uB = S - uB;
14084                     }
14085                     wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
14086                     break;
14087                 }
14088             default:
14089                 abort();
14090         }
14091         Jim_SetResultInt(interp, wC);
14092     }
14093 
14094     Jim_DecrRefCount(interp, A);
14095     Jim_DecrRefCount(interp, B);
14096 
14097     return rc;
14098 }
14099 
14100 
14101 
JimExprOpBin(Jim_Interp * interp,struct JimExprNode * node)14102 static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node)
14103 {
14104     int rc = JIM_OK;
14105     double dA, dB, dC = 0;
14106     jim_wide wA, wB, wC = 0;
14107     Jim_Obj *A, *B;
14108 
14109     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
14110         return rc;
14111     }
14112     if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
14113         Jim_DecrRefCount(interp, A);
14114         return rc;
14115     }
14116 
14117     if ((A->typePtr != &doubleObjType || A->bytes) &&
14118         (B->typePtr != &doubleObjType || B->bytes) &&
14119         JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
14120 
14121 
14122 
14123         switch (node->type) {
14124             case JIM_EXPROP_POW:
14125             case JIM_EXPROP_FUNC_POW:
14126                 if (wA == 0 && wB < 0) {
14127                     Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
14128                     rc = JIM_ERR;
14129                     goto done;
14130                 }
14131                 wC = JimPowWide(wA, wB);
14132                 goto intresult;
14133             case JIM_EXPROP_ADD:
14134                 wC = wA + wB;
14135                 goto intresult;
14136             case JIM_EXPROP_SUB:
14137                 wC = wA - wB;
14138                 goto intresult;
14139             case JIM_EXPROP_MUL:
14140                 wC = wA * wB;
14141                 goto intresult;
14142             case JIM_EXPROP_DIV:
14143                 if (wB == 0) {
14144                     Jim_SetResultString(interp, "Division by zero", -1);
14145                     rc = JIM_ERR;
14146                     goto done;
14147                 }
14148                 else {
14149                     if (wB < 0) {
14150                         wB = -wB;
14151                         wA = -wA;
14152                     }
14153                     wC = wA / wB;
14154                     if (wA % wB < 0) {
14155                         wC--;
14156                     }
14157                     goto intresult;
14158                 }
14159             case JIM_EXPROP_LT:
14160                 wC = wA < wB;
14161                 goto intresult;
14162             case JIM_EXPROP_GT:
14163                 wC = wA > wB;
14164                 goto intresult;
14165             case JIM_EXPROP_LTE:
14166                 wC = wA <= wB;
14167                 goto intresult;
14168             case JIM_EXPROP_GTE:
14169                 wC = wA >= wB;
14170                 goto intresult;
14171             case JIM_EXPROP_NUMEQ:
14172                 wC = wA == wB;
14173                 goto intresult;
14174             case JIM_EXPROP_NUMNE:
14175                 wC = wA != wB;
14176                 goto intresult;
14177         }
14178     }
14179     if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
14180         switch (node->type) {
14181 #ifndef JIM_MATH_FUNCTIONS
14182             case JIM_EXPROP_POW:
14183             case JIM_EXPROP_FUNC_POW:
14184             case JIM_EXPROP_FUNC_ATAN2:
14185             case JIM_EXPROP_FUNC_HYPOT:
14186             case JIM_EXPROP_FUNC_FMOD:
14187                 Jim_SetResultString(interp, "unsupported", -1);
14188                 rc = JIM_ERR;
14189                 goto done;
14190 #else
14191             case JIM_EXPROP_POW:
14192             case JIM_EXPROP_FUNC_POW:
14193                 dC = pow(dA, dB);
14194                 goto doubleresult;
14195             case JIM_EXPROP_FUNC_ATAN2:
14196                 dC = atan2(dA, dB);
14197                 goto doubleresult;
14198             case JIM_EXPROP_FUNC_HYPOT:
14199                 dC = hypot(dA, dB);
14200                 goto doubleresult;
14201             case JIM_EXPROP_FUNC_FMOD:
14202                 dC = fmod(dA, dB);
14203                 goto doubleresult;
14204 #endif
14205             case JIM_EXPROP_ADD:
14206                 dC = dA + dB;
14207                 goto doubleresult;
14208             case JIM_EXPROP_SUB:
14209                 dC = dA - dB;
14210                 goto doubleresult;
14211             case JIM_EXPROP_MUL:
14212                 dC = dA * dB;
14213                 goto doubleresult;
14214             case JIM_EXPROP_DIV:
14215                 if (dB == 0) {
14216 #ifdef INFINITY
14217                     dC = dA < 0 ? -INFINITY : INFINITY;
14218 #else
14219                     dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
14220 #endif
14221                 }
14222                 else {
14223                     dC = dA / dB;
14224                 }
14225                 goto doubleresult;
14226             case JIM_EXPROP_LT:
14227                 wC = dA < dB;
14228                 goto intresult;
14229             case JIM_EXPROP_GT:
14230                 wC = dA > dB;
14231                 goto intresult;
14232             case JIM_EXPROP_LTE:
14233                 wC = dA <= dB;
14234                 goto intresult;
14235             case JIM_EXPROP_GTE:
14236                 wC = dA >= dB;
14237                 goto intresult;
14238             case JIM_EXPROP_NUMEQ:
14239                 wC = dA == dB;
14240                 goto intresult;
14241             case JIM_EXPROP_NUMNE:
14242                 wC = dA != dB;
14243                 goto intresult;
14244         }
14245     }
14246     else {
14247 
14248 
14249 
14250         int i = Jim_StringCompareObj(interp, A, B, 0);
14251 
14252         switch (node->type) {
14253             case JIM_EXPROP_LT:
14254                 wC = i < 0;
14255                 goto intresult;
14256             case JIM_EXPROP_GT:
14257                 wC = i > 0;
14258                 goto intresult;
14259             case JIM_EXPROP_LTE:
14260                 wC = i <= 0;
14261                 goto intresult;
14262             case JIM_EXPROP_GTE:
14263                 wC = i >= 0;
14264                 goto intresult;
14265             case JIM_EXPROP_NUMEQ:
14266                 wC = i == 0;
14267                 goto intresult;
14268             case JIM_EXPROP_NUMNE:
14269                 wC = i != 0;
14270                 goto intresult;
14271         }
14272     }
14273 
14274     rc = JIM_ERR;
14275 done:
14276     Jim_DecrRefCount(interp, A);
14277     Jim_DecrRefCount(interp, B);
14278     return rc;
14279 intresult:
14280     Jim_SetResultInt(interp, wC);
14281     goto done;
14282 doubleresult:
14283     Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
14284     goto done;
14285 }
14286 
JimSearchList(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_Obj * valObj)14287 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
14288 {
14289     int listlen;
14290     int i;
14291 
14292     listlen = Jim_ListLength(interp, listObjPtr);
14293     for (i = 0; i < listlen; i++) {
14294         if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
14295             return 1;
14296         }
14297     }
14298     return 0;
14299 }
14300 
14301 
14302 
JimExprOpStrBin(Jim_Interp * interp,struct JimExprNode * node)14303 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
14304 {
14305     Jim_Obj *A, *B;
14306     jim_wide wC;
14307     int comp, rc;
14308 
14309     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
14310         return rc;
14311     }
14312     if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
14313         Jim_DecrRefCount(interp, A);
14314         return rc;
14315     }
14316 
14317     switch (node->type) {
14318         case JIM_EXPROP_STREQ:
14319         case JIM_EXPROP_STRNE:
14320             wC = Jim_StringEqObj(A, B);
14321             if (node->type == JIM_EXPROP_STRNE) {
14322                 wC = !wC;
14323             }
14324             break;
14325         case JIM_EXPROP_STRLT:
14326         case JIM_EXPROP_STRGT:
14327         case JIM_EXPROP_STRLE:
14328         case JIM_EXPROP_STRGE:
14329             comp = Jim_StringCompareObj(interp, A, B, 0);
14330             if (node->type == JIM_EXPROP_STRLT) {
14331                 wC = comp == -1;
14332             } else if (node->type == JIM_EXPROP_STRGT) {
14333                 wC = comp == 1;
14334             } else if (node->type == JIM_EXPROP_STRLE) {
14335                 wC = comp == -1 || comp == 0;
14336             } else {
14337                 wC = comp == 0 || comp == 1;
14338             }
14339             break;
14340         case JIM_EXPROP_STRIN:
14341             wC = JimSearchList(interp, B, A);
14342             break;
14343         case JIM_EXPROP_STRNI:
14344             wC = !JimSearchList(interp, B, A);
14345             break;
14346         default:
14347             abort();
14348     }
14349     Jim_SetResultInt(interp, wC);
14350 
14351     Jim_DecrRefCount(interp, A);
14352     Jim_DecrRefCount(interp, B);
14353 
14354     return rc;
14355 }
14356 
ExprBool(Jim_Interp * interp,Jim_Obj * obj)14357 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
14358 {
14359     long l;
14360     double d;
14361     int b;
14362     int ret = -1;
14363 
14364 
14365     Jim_IncrRefCount(obj);
14366 
14367     if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
14368         ret = (l != 0);
14369     }
14370     else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
14371         ret = (d != 0);
14372     }
14373     else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
14374         ret = (b != 0);
14375     }
14376 
14377     Jim_DecrRefCount(interp, obj);
14378     return ret;
14379 }
14380 
JimExprOpAnd(Jim_Interp * interp,struct JimExprNode * node)14381 static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node)
14382 {
14383 
14384     int result = JimExprGetTermBoolean(interp, node->left);
14385 
14386     if (result == 1) {
14387 
14388         result = JimExprGetTermBoolean(interp, node->right);
14389     }
14390     if (result == -1) {
14391         return JIM_ERR;
14392     }
14393     Jim_SetResultInt(interp, result);
14394     return JIM_OK;
14395 }
14396 
JimExprOpOr(Jim_Interp * interp,struct JimExprNode * node)14397 static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node)
14398 {
14399 
14400     int result = JimExprGetTermBoolean(interp, node->left);
14401 
14402     if (result == 0) {
14403 
14404         result = JimExprGetTermBoolean(interp, node->right);
14405     }
14406     if (result == -1) {
14407         return JIM_ERR;
14408     }
14409     Jim_SetResultInt(interp, result);
14410     return JIM_OK;
14411 }
14412 
JimExprOpTernary(Jim_Interp * interp,struct JimExprNode * node)14413 static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node)
14414 {
14415 
14416     int result = JimExprGetTermBoolean(interp, node->left);
14417 
14418     if (result == 1) {
14419 
14420         return JimExprEvalTermNode(interp, node->right);
14421     }
14422     else if (result == 0) {
14423 
14424         return JimExprEvalTermNode(interp, node->ternary);
14425     }
14426 
14427     return JIM_ERR;
14428 }
14429 
14430 enum
14431 {
14432     OP_FUNC = 0x0001,
14433     OP_RIGHT_ASSOC = 0x0002,
14434 };
14435 
14436 #define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
14437 #define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0)
14438 
14439 static const struct Jim_ExprOperator Jim_ExprOperators[] = {
14440     OPRINIT("*", 110, 2, JimExprOpBin),
14441     OPRINIT("/", 110, 2, JimExprOpBin),
14442     OPRINIT("%", 110, 2, JimExprOpIntBin),
14443 
14444     OPRINIT("-", 100, 2, JimExprOpBin),
14445     OPRINIT("+", 100, 2, JimExprOpBin),
14446 
14447     OPRINIT("<<", 90, 2, JimExprOpIntBin),
14448     OPRINIT(">>", 90, 2, JimExprOpIntBin),
14449 
14450     OPRINIT("<<<", 90, 2, JimExprOpIntBin),
14451     OPRINIT(">>>", 90, 2, JimExprOpIntBin),
14452 
14453     OPRINIT("<", 80, 2, JimExprOpBin),
14454     OPRINIT(">", 80, 2, JimExprOpBin),
14455     OPRINIT("<=", 80, 2, JimExprOpBin),
14456     OPRINIT(">=", 80, 2, JimExprOpBin),
14457 
14458     OPRINIT("==", 70, 2, JimExprOpBin),
14459     OPRINIT("!=", 70, 2, JimExprOpBin),
14460 
14461     OPRINIT("&", 50, 2, JimExprOpIntBin),
14462     OPRINIT("^", 49, 2, JimExprOpIntBin),
14463     OPRINIT("|", 48, 2, JimExprOpIntBin),
14464 
14465     OPRINIT("&&", 10, 2, JimExprOpAnd),
14466     OPRINIT("||", 9, 2, JimExprOpOr),
14467     OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC),
14468     OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC),
14469 
14470 
14471     OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),
14472 
14473     OPRINIT("eq", 60, 2, JimExprOpStrBin),
14474     OPRINIT("ne", 60, 2, JimExprOpStrBin),
14475 
14476     OPRINIT("in", 55, 2, JimExprOpStrBin),
14477     OPRINIT("ni", 55, 2, JimExprOpStrBin),
14478 
14479     OPRINIT("lt", 75, 2, JimExprOpStrBin),
14480     OPRINIT("gt", 75, 2, JimExprOpStrBin),
14481     OPRINIT("le", 75, 2, JimExprOpStrBin),
14482     OPRINIT("ge", 75, 2, JimExprOpStrBin),
14483 
14484     OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
14485     OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
14486     OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
14487     OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
14488 
14489 
14490 
14491     OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC),
14492     OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC),
14493     OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC),
14494     OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC),
14495     OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC),
14496     OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC),
14497     OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC),
14498 
14499 #ifdef JIM_MATH_FUNCTIONS
14500     OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14501     OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14502     OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14503     OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14504     OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14505     OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14506     OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC),
14507     OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14508     OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14509     OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14510     OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14511     OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14512     OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14513     OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14514     OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14515     OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
14516     OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC),
14517     OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC),
14518     OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC),
14519 #endif
14520 };
14521 #undef OPRINIT
14522 #undef OPRINIT_ATTR
14523 
14524 #define JIM_EXPR_OPERATORS_NUM \
14525     (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
14526 
JimParseExpression(struct JimParserCtx * pc)14527 static int JimParseExpression(struct JimParserCtx *pc)
14528 {
14529     pc->errmsg = NULL;
14530 
14531     while (1) {
14532 
14533         while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
14534             if (*pc->p == '\n') {
14535                 pc->linenr++;
14536             }
14537             pc->p++;
14538             pc->len--;
14539         }
14540 
14541         if (*pc->p == '#') {
14542             JimParseComment(pc);
14543 
14544             continue;
14545         }
14546         break;
14547     }
14548 
14549 
14550     pc->tline = pc->linenr;
14551     pc->tstart = pc->p;
14552 
14553     if (pc->len == 0) {
14554         pc->tend = pc->p;
14555         pc->tt = JIM_TT_EOL;
14556         pc->eof = 1;
14557         return JIM_OK;
14558     }
14559     switch (*(pc->p)) {
14560         case '(':
14561                 pc->tt = JIM_TT_SUBEXPR_START;
14562                 goto singlechar;
14563         case ')':
14564                 pc->tt = JIM_TT_SUBEXPR_END;
14565                 goto singlechar;
14566         case ',':
14567             pc->tt = JIM_TT_SUBEXPR_COMMA;
14568 singlechar:
14569             pc->tend = pc->p;
14570             pc->p++;
14571             pc->len--;
14572             break;
14573         case '[':
14574             return JimParseCmd(pc);
14575         case '$':
14576             if (JimParseVar(pc) == JIM_ERR)
14577                 return JimParseExprOperator(pc);
14578             else {
14579 
14580                 if (pc->tt == JIM_TT_EXPRSUGAR) {
14581                     pc->errmsg = "nesting expr in expr is not allowed";
14582                     return JIM_ERR;
14583                 }
14584                 return JIM_OK;
14585             }
14586             break;
14587         case '0':
14588         case '1':
14589         case '2':
14590         case '3':
14591         case '4':
14592         case '5':
14593         case '6':
14594         case '7':
14595         case '8':
14596         case '9':
14597         case '.':
14598             return JimParseExprNumber(pc);
14599         case '"':
14600             return JimParseQuote(pc);
14601         case '{':
14602             return JimParseBrace(pc);
14603 
14604         case 'N':
14605         case 'I':
14606         case 'n':
14607         case 'i':
14608             if (JimParseExprIrrational(pc) == JIM_ERR)
14609                 if (JimParseExprBoolean(pc) == JIM_ERR)
14610                     return JimParseExprOperator(pc);
14611             break;
14612         case 't':
14613         case 'f':
14614         case 'o':
14615         case 'y':
14616             if (JimParseExprBoolean(pc) == JIM_ERR)
14617                 return JimParseExprOperator(pc);
14618             break;
14619         default:
14620             return JimParseExprOperator(pc);
14621             break;
14622     }
14623     return JIM_OK;
14624 }
14625 
JimParseExprNumber(struct JimParserCtx * pc)14626 static int JimParseExprNumber(struct JimParserCtx *pc)
14627 {
14628     char *end;
14629 
14630 
14631     pc->tt = JIM_TT_EXPR_INT;
14632 
14633     jim_strtoull(pc->p, (char **)&pc->p);
14634 
14635     if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
14636         if (strtod(pc->tstart, &end)) { }
14637         if (end == pc->tstart)
14638             return JIM_ERR;
14639         if (end > pc->p) {
14640 
14641             pc->tt = JIM_TT_EXPR_DOUBLE;
14642             pc->p = end;
14643         }
14644     }
14645     pc->tend = pc->p - 1;
14646     pc->len -= (pc->p - pc->tstart);
14647     return JIM_OK;
14648 }
14649 
JimParseExprIrrational(struct JimParserCtx * pc)14650 static int JimParseExprIrrational(struct JimParserCtx *pc)
14651 {
14652     const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
14653     int i;
14654 
14655     for (i = 0; irrationals[i]; i++) {
14656         const char *irr = irrationals[i];
14657 
14658         if (strncmp(irr, pc->p, 3) == 0) {
14659             pc->p += 3;
14660             pc->len -= 3;
14661             pc->tend = pc->p - 1;
14662             pc->tt = JIM_TT_EXPR_DOUBLE;
14663             return JIM_OK;
14664         }
14665     }
14666     return JIM_ERR;
14667 }
14668 
JimParseExprBoolean(struct JimParserCtx * pc)14669 static int JimParseExprBoolean(struct JimParserCtx *pc)
14670 {
14671     int i;
14672     for (i = 0; i < sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings); i++) {
14673         if (strncmp(pc->p, jim_true_false_strings[i], jim_true_false_lens[i]) == 0) {
14674             pc->p += jim_true_false_lens[i];
14675             pc->len -= jim_true_false_lens[i];
14676             pc->tend = pc->p - 1;
14677             pc->tt = JIM_TT_EXPR_BOOLEAN;
14678             return JIM_OK;
14679         }
14680     }
14681     return JIM_ERR;
14682 }
14683 
JimExprOperatorInfoByOpcode(int opcode)14684 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
14685 {
14686     static Jim_ExprOperator dummy_op;
14687     if (opcode < JIM_TT_EXPR_OP) {
14688         return &dummy_op;
14689     }
14690     return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
14691 }
14692 
JimParseExprOperator(struct JimParserCtx * pc)14693 static int JimParseExprOperator(struct JimParserCtx *pc)
14694 {
14695     int i;
14696     const struct Jim_ExprOperator *bestOp = NULL;
14697     int bestLen = 0;
14698 
14699 
14700     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
14701         const struct Jim_ExprOperator *op = &Jim_ExprOperators[i];
14702 
14703         if (op->name[0] != pc->p[0]) {
14704             continue;
14705         }
14706 
14707         if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) {
14708             bestOp = op;
14709             bestLen = op->namelen;
14710         }
14711     }
14712     if (bestOp == NULL) {
14713         return JIM_ERR;
14714     }
14715 
14716 
14717     if (bestOp->attr & OP_FUNC) {
14718         const char *p = pc->p + bestLen;
14719         int len = pc->len - bestLen;
14720 
14721         while (len && isspace(UCHAR(*p))) {
14722             len--;
14723             p++;
14724         }
14725         if (*p != '(') {
14726             pc->errmsg = "function requires parentheses";
14727             return JIM_ERR;
14728         }
14729     }
14730     pc->tend = pc->p + bestLen - 1;
14731     pc->p += bestLen;
14732     pc->len -= bestLen;
14733 
14734     pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
14735     return JIM_OK;
14736 }
14737 
14738 
14739 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
14740 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
14741 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
14742 
14743 static const Jim_ObjType exprObjType = {
14744     "expression",
14745     FreeExprInternalRep,
14746     DupExprInternalRep,
14747     NULL,
14748     JIM_TYPE_NONE,
14749 };
14750 
14751 
14752 struct ExprTree
14753 {
14754     struct JimExprNode *expr;
14755     struct JimExprNode *nodes;
14756     int len;
14757     int inUse;
14758 };
14759 
ExprTreeFreeNodes(Jim_Interp * interp,struct JimExprNode * nodes,int num)14760 static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num)
14761 {
14762     int i;
14763     for (i = 0; i < num; i++) {
14764         if (nodes[i].objPtr) {
14765             Jim_DecrRefCount(interp, nodes[i].objPtr);
14766         }
14767     }
14768     Jim_Free(nodes);
14769 }
14770 
ExprTreeFree(Jim_Interp * interp,struct ExprTree * expr)14771 static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr)
14772 {
14773     ExprTreeFreeNodes(interp, expr->nodes, expr->len);
14774     Jim_Free(expr);
14775 }
14776 
FreeExprInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)14777 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
14778 {
14779     struct ExprTree *expr = (void *)objPtr->internalRep.ptr;
14780 
14781     if (expr) {
14782         if (--expr->inUse != 0) {
14783             return;
14784         }
14785 
14786         ExprTreeFree(interp, expr);
14787     }
14788 }
14789 
DupExprInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)14790 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
14791 {
14792     JIM_NOTUSED(interp);
14793     JIM_NOTUSED(srcPtr);
14794 
14795 
14796     dupPtr->typePtr = NULL;
14797 }
14798 
14799 struct ExprBuilder {
14800     int parencount;
14801     int level;
14802     ParseToken *token;
14803     ParseToken *first_token;
14804     Jim_Stack stack;
14805     Jim_Obj *exprObjPtr;
14806     Jim_Obj *fileNameObj;
14807     struct JimExprNode *nodes;
14808     struct JimExprNode *next;
14809 };
14810 
14811 #ifdef DEBUG_SHOW_EXPR
JimShowExprNode(struct JimExprNode * node,int level)14812 static void JimShowExprNode(struct JimExprNode *node, int level)
14813 {
14814     int i;
14815     for (i = 0; i < level; i++) {
14816         printf("  ");
14817     }
14818     if (TOKEN_IS_EXPR_OP(node->type)) {
14819         printf("%s\n", jim_tt_name(node->type));
14820         if (node->left) {
14821             JimShowExprNode(node->left, level + 1);
14822         }
14823         if (node->right) {
14824             JimShowExprNode(node->right, level + 1);
14825         }
14826         if (node->ternary) {
14827             JimShowExprNode(node->ternary, level + 1);
14828         }
14829     }
14830     else {
14831         printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr));
14832     }
14833 }
14834 #endif
14835 
14836 #define EXPR_UNTIL_CLOSE 0x0001
14837 #define EXPR_FUNC_ARGS   0x0002
14838 #define EXPR_TERNARY     0x0004
14839 
ExprTreeBuildTree(Jim_Interp * interp,struct ExprBuilder * builder,int precedence,int flags,int exp_numterms)14840 static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms) {
14841     int rc;
14842     struct JimExprNode *node;
14843 
14844     int exp_stacklen = builder->stack.len + exp_numterms;
14845 
14846     if (builder->level++ > 200) {
14847         Jim_SetResultString(interp, "Expression too complex", -1);
14848         return JIM_ERR;
14849     }
14850 
14851     while (builder->token->type != JIM_TT_EOL) {
14852         ParseToken *t = builder->token++;
14853         int prevtt;
14854 
14855         if (t == builder->first_token) {
14856             prevtt = JIM_TT_NONE;
14857         }
14858         else {
14859             prevtt = t[-1].type;
14860         }
14861 
14862         if (t->type == JIM_TT_SUBEXPR_START) {
14863             if (builder->stack.len == exp_stacklen) {
14864                 Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr);
14865                 return JIM_ERR;
14866             }
14867             builder->parencount++;
14868             rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1);
14869             if (rc != JIM_OK) {
14870                 return rc;
14871             }
14872 
14873         }
14874         else if (t->type == JIM_TT_SUBEXPR_END) {
14875             if (!(flags & EXPR_UNTIL_CLOSE)) {
14876                 if (builder->stack.len == exp_stacklen && builder->level > 1) {
14877                     builder->token--;
14878                     builder->level--;
14879                     return JIM_OK;
14880                 }
14881                 Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr);
14882                 return JIM_ERR;
14883             }
14884             builder->parencount--;
14885             if (builder->stack.len == exp_stacklen) {
14886 
14887                 break;
14888             }
14889         }
14890         else if (t->type == JIM_TT_SUBEXPR_COMMA) {
14891             if (!(flags & EXPR_FUNC_ARGS)) {
14892                 if (builder->stack.len == exp_stacklen) {
14893 
14894                     builder->token--;
14895                     builder->level--;
14896                     return JIM_OK;
14897                 }
14898                 Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr);
14899                 return JIM_ERR;
14900             }
14901             else {
14902 
14903                 if (builder->stack.len > exp_stacklen) {
14904                     Jim_SetResultFormatted(interp, "too many arguments to math function");
14905                     return JIM_ERR;
14906                 }
14907             }
14908 
14909         }
14910         else if (t->type == JIM_EXPROP_COLON) {
14911             if (!(flags & EXPR_TERNARY)) {
14912                 if (builder->level != 1) {
14913 
14914                     builder->token--;
14915                     builder->level--;
14916                     return JIM_OK;
14917                 }
14918                 Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr);
14919                 return JIM_ERR;
14920             }
14921             if (builder->stack.len == exp_stacklen) {
14922 
14923                 builder->token--;
14924                 builder->level--;
14925                 return JIM_OK;
14926             }
14927 
14928         }
14929         else if (TOKEN_IS_EXPR_OP(t->type)) {
14930             const struct Jim_ExprOperator *op;
14931 
14932 
14933             if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) {
14934                 if (t->type == JIM_EXPROP_SUB) {
14935                     t->type = JIM_EXPROP_UNARYMINUS;
14936                 }
14937                 else if (t->type == JIM_EXPROP_ADD) {
14938                     t->type = JIM_EXPROP_UNARYPLUS;
14939                 }
14940             }
14941 
14942             op = JimExprOperatorInfoByOpcode(t->type);
14943 
14944             if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) {
14945 
14946                 builder->token--;
14947                 break;
14948             }
14949 
14950             if (op->attr & OP_FUNC) {
14951                 if (builder->token->type != JIM_TT_SUBEXPR_START) {
14952                     Jim_SetResultString(interp, "missing arguments for math function", -1);
14953                     return JIM_ERR;
14954                 }
14955                 builder->token++;
14956                 if (op->arity == 0) {
14957                     if (builder->token->type != JIM_TT_SUBEXPR_END) {
14958                         Jim_SetResultString(interp, "too many arguments for math function", -1);
14959                         return JIM_ERR;
14960                     }
14961                     builder->token++;
14962                     goto noargs;
14963                 }
14964                 builder->parencount++;
14965 
14966 
14967                 rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity);
14968             }
14969             else if (t->type == JIM_EXPROP_TERNARY) {
14970 
14971                 rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2);
14972             }
14973             else {
14974                 rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1);
14975             }
14976 
14977             if (rc != JIM_OK) {
14978                 return rc;
14979             }
14980 
14981 noargs:
14982             node = builder->next++;
14983             node->type = t->type;
14984 
14985             if (op->arity >= 3) {
14986                 node->ternary = Jim_StackPop(&builder->stack);
14987                 if (node->ternary == NULL) {
14988                     goto missingoperand;
14989                 }
14990             }
14991             if (op->arity >= 2) {
14992                 node->right = Jim_StackPop(&builder->stack);
14993                 if (node->right == NULL) {
14994                     goto missingoperand;
14995                 }
14996             }
14997             if (op->arity >= 1) {
14998                 node->left = Jim_StackPop(&builder->stack);
14999                 if (node->left == NULL) {
15000 missingoperand:
15001                     Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr);
15002                     builder->next--;
15003                     return JIM_ERR;
15004 
15005                 }
15006             }
15007 
15008 
15009             Jim_StackPush(&builder->stack, node);
15010         }
15011         else {
15012             Jim_Obj *objPtr = NULL;
15013 
15014 
15015 
15016 
15017             if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
15018                 Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr);
15019                 return JIM_ERR;
15020             }
15021 
15022 
15023             if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
15024                 char *endptr;
15025                 if (t->type == JIM_TT_EXPR_INT) {
15026                     objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
15027                 }
15028                 else {
15029                     objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
15030                 }
15031                 if (endptr != t->token + t->len) {
15032 
15033                     Jim_FreeNewObj(interp, objPtr);
15034                     objPtr = NULL;
15035                 }
15036             }
15037 
15038             if (!objPtr) {
15039 
15040                 objPtr = Jim_NewStringObj(interp, t->token, t->len);
15041                 if (t->type == JIM_TT_CMD) {
15042 
15043                     Jim_SetSourceInfo(interp, objPtr, builder->fileNameObj, t->line);
15044                 }
15045             }
15046 
15047 
15048             node = builder->next++;
15049             node->objPtr = objPtr;
15050             Jim_IncrRefCount(node->objPtr);
15051             node->type = t->type;
15052             Jim_StackPush(&builder->stack, node);
15053         }
15054     }
15055 
15056     if (builder->stack.len == exp_stacklen) {
15057         builder->level--;
15058         return JIM_OK;
15059     }
15060 
15061     if ((flags & EXPR_FUNC_ARGS)) {
15062         Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many");
15063     }
15064     else {
15065         if (builder->stack.len < exp_stacklen) {
15066             if (builder->level == 0) {
15067                 Jim_SetResultFormatted(interp, "empty expression");
15068             }
15069             else {
15070                 Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr);
15071             }
15072         }
15073         else {
15074             Jim_SetResultFormatted(interp, "extra terms after expression");
15075         }
15076     }
15077 
15078     return JIM_ERR;
15079 }
15080 
ExprTreeCreateTree(Jim_Interp * interp,const ParseTokenList * tokenlist,Jim_Obj * exprObjPtr,Jim_Obj * fileNameObj)15081 static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
15082 {
15083     struct ExprTree *expr;
15084     struct ExprBuilder builder;
15085     int rc;
15086     struct JimExprNode *top = NULL;
15087 
15088     builder.parencount = 0;
15089     builder.level = 0;
15090     builder.token = builder.first_token = tokenlist->list;
15091     builder.exprObjPtr = exprObjPtr;
15092     builder.fileNameObj = fileNameObj;
15093 
15094     builder.nodes = Jim_Alloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
15095     memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
15096     builder.next = builder.nodes;
15097     Jim_InitStack(&builder.stack);
15098 
15099     rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);
15100 
15101     if (rc == JIM_OK) {
15102         top = Jim_StackPop(&builder.stack);
15103 
15104         if (builder.parencount) {
15105             Jim_SetResultString(interp, "missing close parenthesis", -1);
15106             rc = JIM_ERR;
15107         }
15108     }
15109 
15110 
15111     Jim_FreeStack(&builder.stack);
15112 
15113     if (rc != JIM_OK) {
15114         ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes);
15115         return NULL;
15116     }
15117 
15118     expr = Jim_Alloc(sizeof(*expr));
15119     expr->inUse = 1;
15120     expr->expr = top;
15121     expr->nodes = builder.nodes;
15122     expr->len = builder.next - builder.nodes;
15123 
15124     assert(expr->len <= tokenlist->count - 1);
15125 
15126     return expr;
15127 }
15128 
SetExprFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)15129 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
15130 {
15131     int exprTextLen;
15132     const char *exprText;
15133     struct JimParserCtx parser;
15134     struct ExprTree *expr;
15135     ParseTokenList tokenlist;
15136     int line;
15137     Jim_Obj *fileNameObj;
15138     int rc = JIM_ERR;
15139 
15140 
15141     fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
15142     Jim_IncrRefCount(fileNameObj);
15143 
15144     exprText = Jim_GetString(objPtr, &exprTextLen);
15145 
15146 
15147     ScriptTokenListInit(&tokenlist);
15148 
15149     JimParserInit(&parser, exprText, exprTextLen, line);
15150     while (!parser.eof) {
15151         if (JimParseExpression(&parser) != JIM_OK) {
15152             ScriptTokenListFree(&tokenlist);
15153             Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
15154             if (parser.errmsg) {
15155                 Jim_AppendStrings(interp, Jim_GetResult(interp), ": ", parser.errmsg, NULL);
15156             }
15157             expr = NULL;
15158             goto err;
15159         }
15160 
15161         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
15162             parser.tline);
15163     }
15164 
15165 #ifdef DEBUG_SHOW_EXPR_TOKENS
15166     {
15167         int i;
15168         printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
15169         for (i = 0; i < tokenlist.count; i++) {
15170             printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
15171                 tokenlist.list[i].len, tokenlist.list[i].token);
15172         }
15173     }
15174 #endif
15175 
15176     if (tokenlist.count <= 1) {
15177         Jim_SetResultString(interp, "empty expression", -1);
15178         rc = JIM_ERR;
15179     }
15180     else {
15181         rc = JimParseCheckMissing(interp, parser.missing.ch);
15182     }
15183     if (rc != JIM_OK) {
15184         ScriptTokenListFree(&tokenlist);
15185         Jim_DecrRefCount(interp, fileNameObj);
15186         return rc;
15187     }
15188 
15189 
15190     expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj);
15191 
15192 
15193     ScriptTokenListFree(&tokenlist);
15194 
15195     if (!expr) {
15196         goto err;
15197     }
15198 
15199 #ifdef DEBUG_SHOW_EXPR
15200     printf("==== Expr ====\n");
15201     JimShowExprNode(expr->expr, 0);
15202 #endif
15203 
15204     rc = JIM_OK;
15205 
15206   err:
15207 
15208     Jim_DecrRefCount(interp, fileNameObj);
15209     Jim_FreeIntRep(interp, objPtr);
15210     Jim_SetIntRepPtr(objPtr, expr);
15211     objPtr->typePtr = &exprObjType;
15212     return rc;
15213 }
15214 
JimGetExpression(Jim_Interp * interp,Jim_Obj * objPtr)15215 static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
15216 {
15217     if (objPtr->typePtr != &exprObjType) {
15218         if (SetExprFromAny(interp, objPtr) != JIM_OK) {
15219             return NULL;
15220         }
15221     }
15222     return (struct ExprTree *) Jim_GetIntRepPtr(objPtr);
15223 }
15224 
15225 #ifdef JIM_OPTIMIZATION
JimExprIntValOrVar(Jim_Interp * interp,struct JimExprNode * node)15226 static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node)
15227 {
15228     if (node->type == JIM_TT_EXPR_INT)
15229         return node->objPtr;
15230     else if (node->type == JIM_TT_VAR)
15231         return Jim_GetVariable(interp, node->objPtr, JIM_NONE);
15232     else if (node->type == JIM_TT_DICTSUGAR)
15233         return JimExpandDictSugar(interp, node->objPtr);
15234     else
15235         return NULL;
15236 }
15237 #endif
15238 
15239 
JimExprEvalTermNode(Jim_Interp * interp,struct JimExprNode * node)15240 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node)
15241 {
15242     if (TOKEN_IS_EXPR_OP(node->type)) {
15243         const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type);
15244         return op->funcop(interp, node);
15245     }
15246     else {
15247         Jim_Obj *objPtr;
15248 
15249 
15250         switch (node->type) {
15251             case JIM_TT_EXPR_INT:
15252             case JIM_TT_EXPR_DOUBLE:
15253             case JIM_TT_EXPR_BOOLEAN:
15254             case JIM_TT_STR:
15255                 Jim_SetResult(interp, node->objPtr);
15256                 return JIM_OK;
15257 
15258             case JIM_TT_VAR:
15259                 objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG);
15260                 if (objPtr) {
15261                     Jim_SetResult(interp, objPtr);
15262                     return JIM_OK;
15263                 }
15264                 return JIM_ERR;
15265 
15266             case JIM_TT_DICTSUGAR:
15267                 objPtr = JimExpandDictSugar(interp, node->objPtr);
15268                 if (objPtr) {
15269                     Jim_SetResult(interp, objPtr);
15270                     return JIM_OK;
15271                 }
15272                 return JIM_ERR;
15273 
15274             case JIM_TT_ESC:
15275                 if (interp->safeexpr) {
15276                     return JIM_ERR;
15277                 }
15278                 if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
15279                     Jim_SetResult(interp, objPtr);
15280                     return JIM_OK;
15281                 }
15282                 return JIM_ERR;
15283 
15284             case JIM_TT_CMD:
15285                 if (interp->safeexpr) {
15286                     return JIM_ERR;
15287                 }
15288                 return Jim_EvalObj(interp, node->objPtr);
15289 
15290             default:
15291 
15292                 return JIM_ERR;
15293         }
15294     }
15295 }
15296 
JimExprGetTerm(Jim_Interp * interp,struct JimExprNode * node,Jim_Obj ** objPtrPtr)15297 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr)
15298 {
15299     int rc = JimExprEvalTermNode(interp, node);
15300     if (rc == JIM_OK) {
15301         *objPtrPtr = Jim_GetResult(interp);
15302         Jim_IncrRefCount(*objPtrPtr);
15303     }
15304     return rc;
15305 }
15306 
JimExprGetTermBoolean(Jim_Interp * interp,struct JimExprNode * node)15307 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node)
15308 {
15309     if (JimExprEvalTermNode(interp, node) == JIM_OK) {
15310         return ExprBool(interp, Jim_GetResult(interp));
15311     }
15312     return -1;
15313 }
15314 
Jim_EvalExpression(Jim_Interp * interp,Jim_Obj * exprObjPtr)15315 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
15316 {
15317     struct ExprTree *expr;
15318     int retcode = JIM_OK;
15319 
15320     Jim_IncrRefCount(exprObjPtr);
15321     expr = JimGetExpression(interp, exprObjPtr);
15322     if (!expr) {
15323         retcode = JIM_ERR;
15324         goto done;
15325     }
15326 
15327 #ifdef JIM_OPTIMIZATION
15328     if (!interp->safeexpr) {
15329         Jim_Obj *objPtr;
15330 
15331 
15332         switch (expr->len) {
15333             case 1:
15334                 objPtr = JimExprIntValOrVar(interp, expr->expr);
15335                 if (objPtr) {
15336                     Jim_SetResult(interp, objPtr);
15337                     goto done;
15338                 }
15339                 break;
15340 
15341             case 2:
15342                 if (expr->expr->type == JIM_EXPROP_NOT) {
15343                     objPtr = JimExprIntValOrVar(interp, expr->expr->left);
15344 
15345                     if (objPtr && JimIsWide(objPtr)) {
15346                         Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj);
15347                         goto done;
15348                     }
15349                 }
15350                 break;
15351 
15352             case 3:
15353                 objPtr = JimExprIntValOrVar(interp, expr->expr->left);
15354                 if (objPtr && JimIsWide(objPtr)) {
15355                     Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right);
15356                     if (objPtr2 && JimIsWide(objPtr2)) {
15357                         jim_wide wideValueA = JimWideValue(objPtr);
15358                         jim_wide wideValueB = JimWideValue(objPtr2);
15359                         int cmpRes;
15360                         switch (expr->expr->type) {
15361                             case JIM_EXPROP_LT:
15362                                 cmpRes = wideValueA < wideValueB;
15363                                 break;
15364                             case JIM_EXPROP_LTE:
15365                                 cmpRes = wideValueA <= wideValueB;
15366                                 break;
15367                             case JIM_EXPROP_GT:
15368                                 cmpRes = wideValueA > wideValueB;
15369                                 break;
15370                             case JIM_EXPROP_GTE:
15371                                 cmpRes = wideValueA >= wideValueB;
15372                                 break;
15373                             case JIM_EXPROP_NUMEQ:
15374                                 cmpRes = wideValueA == wideValueB;
15375                                 break;
15376                             case JIM_EXPROP_NUMNE:
15377                                 cmpRes = wideValueA != wideValueB;
15378                                 break;
15379                             default:
15380                                 goto noopt;
15381                         }
15382                         Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);
15383                         goto done;
15384                     }
15385                 }
15386                 break;
15387         }
15388     }
15389 noopt:
15390 #endif
15391 
15392     expr->inUse++;
15393 
15394 
15395     retcode = JimExprEvalTermNode(interp, expr->expr);
15396 
15397 
15398     Jim_FreeIntRep(interp, exprObjPtr);
15399     exprObjPtr->typePtr = &exprObjType;
15400     Jim_SetIntRepPtr(exprObjPtr, expr);
15401 
15402 done:
15403     Jim_DecrRefCount(interp, exprObjPtr);
15404 
15405     return retcode;
15406 }
15407 
Jim_GetBoolFromExpr(Jim_Interp * interp,Jim_Obj * exprObjPtr,int * boolPtr)15408 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
15409 {
15410     int retcode = Jim_EvalExpression(interp, exprObjPtr);
15411 
15412     if (retcode == JIM_OK) {
15413         switch (ExprBool(interp, Jim_GetResult(interp))) {
15414             case 0:
15415                 *boolPtr = 0;
15416                 break;
15417 
15418             case 1:
15419                 *boolPtr = 1;
15420                 break;
15421 
15422             case -1:
15423                 retcode = JIM_ERR;
15424                 break;
15425         }
15426     }
15427     return retcode;
15428 }
15429 
15430 
15431 
15432 
15433 typedef struct ScanFmtPartDescr
15434 {
15435     const char *arg;
15436     const char *prefix;
15437     size_t width;
15438     int pos;
15439     char type;
15440     char modifier;
15441 } ScanFmtPartDescr;
15442 
15443 
15444 typedef struct ScanFmtStringObj
15445 {
15446     jim_wide size;
15447     char *stringRep;
15448     size_t count;
15449     size_t convCount;
15450     size_t maxPos;
15451     const char *error;
15452     char *scratch;
15453     ScanFmtPartDescr descr[1];
15454 } ScanFmtStringObj;
15455 
15456 
15457 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
15458 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
15459 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
15460 
15461 static const Jim_ObjType scanFmtStringObjType = {
15462     "scanformatstring",
15463     FreeScanFmtInternalRep,
15464     DupScanFmtInternalRep,
15465     UpdateStringOfScanFmt,
15466     JIM_TYPE_NONE,
15467 };
15468 
FreeScanFmtInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)15469 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
15470 {
15471     JIM_NOTUSED(interp);
15472     Jim_Free((char *)objPtr->internalRep.ptr);
15473     objPtr->internalRep.ptr = 0;
15474 }
15475 
DupScanFmtInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)15476 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
15477 {
15478     size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
15479     ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
15480 
15481     JIM_NOTUSED(interp);
15482     memcpy(newVec, srcPtr->internalRep.ptr, size);
15483     dupPtr->internalRep.ptr = newVec;
15484     dupPtr->typePtr = &scanFmtStringObjType;
15485 }
15486 
UpdateStringOfScanFmt(Jim_Obj * objPtr)15487 static void UpdateStringOfScanFmt(Jim_Obj *objPtr)
15488 {
15489     JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep);
15490 }
15491 
15492 
SetScanFmtFromAny(Jim_Interp * interp,Jim_Obj * objPtr)15493 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
15494 {
15495     ScanFmtStringObj *fmtObj;
15496     char *buffer;
15497     int maxCount, i, approxSize, lastPos = -1;
15498     const char *fmt = Jim_String(objPtr);
15499     int maxFmtLen = Jim_Length(objPtr);
15500     const char *fmtEnd = fmt + maxFmtLen;
15501     int curr;
15502 
15503     Jim_FreeIntRep(interp, objPtr);
15504 
15505     for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
15506         if (fmt[i] == '%')
15507             ++maxCount;
15508 
15509     approxSize = sizeof(ScanFmtStringObj)
15510         +(maxCount + 1) * sizeof(ScanFmtPartDescr)
15511         +maxFmtLen * sizeof(char) + 3 + 1
15512         + maxFmtLen * sizeof(char) + 1
15513         + maxFmtLen * sizeof(char)
15514         +(maxCount + 1) * sizeof(char)
15515         +1;
15516     fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
15517     memset(fmtObj, 0, approxSize);
15518     fmtObj->size = approxSize;
15519     fmtObj->maxPos = 0;
15520     fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
15521     fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
15522     memcpy(fmtObj->stringRep, fmt, maxFmtLen);
15523     buffer = fmtObj->stringRep + maxFmtLen + 1;
15524     objPtr->internalRep.ptr = fmtObj;
15525     objPtr->typePtr = &scanFmtStringObjType;
15526     for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
15527         int width = 0, skip;
15528         ScanFmtPartDescr *descr = &fmtObj->descr[curr];
15529 
15530         fmtObj->count++;
15531         descr->width = 0;
15532 
15533         if (*fmt != '%' || fmt[1] == '%') {
15534             descr->type = 0;
15535             descr->prefix = &buffer[i];
15536             for (; fmt < fmtEnd; ++fmt) {
15537                 if (*fmt == '%') {
15538                     if (fmt[1] != '%')
15539                         break;
15540                     ++fmt;
15541                 }
15542                 buffer[i++] = *fmt;
15543             }
15544             buffer[i++] = 0;
15545         }
15546 
15547         ++fmt;
15548 
15549         if (fmt >= fmtEnd)
15550             goto done;
15551         descr->pos = 0;
15552         if (*fmt == '*') {
15553             descr->pos = -1;
15554             ++fmt;
15555         }
15556         else
15557             fmtObj->convCount++;
15558 
15559         if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
15560             fmt += skip;
15561 
15562             if (descr->pos != -1 && *fmt == '$') {
15563                 int prev;
15564 
15565                 ++fmt;
15566                 descr->pos = width;
15567                 width = 0;
15568 
15569                 if ((lastPos == 0 && descr->pos > 0)
15570                     || (lastPos > 0 && descr->pos == 0)) {
15571                     fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
15572                     return JIM_ERR;
15573                 }
15574 
15575                 for (prev = 0; prev < curr; ++prev) {
15576                     if (fmtObj->descr[prev].pos == -1)
15577                         continue;
15578                     if (fmtObj->descr[prev].pos == descr->pos) {
15579                         fmtObj->error =
15580                             "variable is assigned by multiple \"%n$\" conversion specifiers";
15581                         return JIM_ERR;
15582                     }
15583                 }
15584                 if (descr->pos < 0) {
15585                     fmtObj->error =
15586                         "\"%n$\" conversion specifier is negative";
15587                     return JIM_ERR;
15588                 }
15589 
15590                 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
15591                     descr->width = width;
15592                     fmt += skip;
15593                 }
15594                 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
15595                     fmtObj->maxPos = descr->pos;
15596             }
15597             else {
15598 
15599                 descr->width = width;
15600             }
15601         }
15602 
15603         if (lastPos == -1)
15604             lastPos = descr->pos;
15605 
15606         if (*fmt == '[') {
15607             int swapped = 1, beg = i, end, j;
15608 
15609             descr->type = '[';
15610             descr->arg = &buffer[i];
15611             ++fmt;
15612             if (*fmt == '^')
15613                 buffer[i++] = *fmt++;
15614             if (*fmt == ']')
15615                 buffer[i++] = *fmt++;
15616             while (*fmt && *fmt != ']')
15617                 buffer[i++] = *fmt++;
15618             if (*fmt != ']') {
15619                 fmtObj->error = "unmatched [ in format string";
15620                 return JIM_ERR;
15621             }
15622             end = i;
15623             buffer[i++] = 0;
15624 
15625             while (swapped) {
15626                 swapped = 0;
15627                 for (j = beg + 1; j < end - 1; ++j) {
15628                     if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
15629                         char tmp = buffer[j - 1];
15630 
15631                         buffer[j - 1] = buffer[j + 1];
15632                         buffer[j + 1] = tmp;
15633                         swapped = 1;
15634                     }
15635                 }
15636             }
15637         }
15638         else {
15639 
15640             if (fmt < fmtEnd && strchr("hlL", *fmt))
15641                 descr->modifier = tolower((int)*fmt++);
15642 
15643             if (fmt >= fmtEnd) {
15644                 fmtObj->error = "missing scan conversion character";
15645                 return JIM_ERR;
15646             }
15647 
15648             descr->type = *fmt;
15649             if (strchr("efgcsndoxui", *fmt) == 0) {
15650                 fmtObj->error = "bad scan conversion character";
15651                 return JIM_ERR;
15652             }
15653             else if (*fmt == 'c' && descr->width != 0) {
15654                 fmtObj->error = "field width may not be specified in %c " "conversion";
15655                 return JIM_ERR;
15656             }
15657             else if (*fmt == 'u' && descr->modifier == 'l') {
15658                 fmtObj->error = "unsigned wide not supported";
15659                 return JIM_ERR;
15660             }
15661         }
15662         curr++;
15663     }
15664   done:
15665     return JIM_OK;
15666 }
15667 
15668 
15669 
15670 #define FormatGetCnvCount(_fo_) \
15671     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
15672 #define FormatGetMaxPos(_fo_) \
15673     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
15674 #define FormatGetError(_fo_) \
15675     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
15676 
JimScanAString(Jim_Interp * interp,const char * sdescr,const char * str)15677 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
15678 {
15679     char *buffer = Jim_StrDup(str);
15680     char *p = buffer;
15681 
15682     while (*str) {
15683         int c;
15684         int n;
15685 
15686         if (!sdescr && isspace(UCHAR(*str)))
15687             break;
15688 
15689         n = utf8_tounicode(str, &c);
15690         if (sdescr && !JimCharsetMatch(sdescr, strlen(sdescr), c, JIM_CHARSET_SCAN))
15691             break;
15692         while (n--)
15693             *p++ = *str++;
15694     }
15695     *p = 0;
15696     return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
15697 }
15698 
15699 
ScanOneEntry(Jim_Interp * interp,const char * str,int pos,int str_bytelen,ScanFmtStringObj * fmtObj,long idx,Jim_Obj ** valObjPtr)15700 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int str_bytelen,
15701     ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
15702 {
15703     const char *tok;
15704     const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
15705     size_t scanned = 0;
15706     size_t anchor = pos;
15707     int i;
15708     Jim_Obj *tmpObj = NULL;
15709 
15710 
15711     *valObjPtr = 0;
15712     if (descr->prefix) {
15713         for (i = 0; pos < str_bytelen && descr->prefix[i]; ++i) {
15714 
15715             if (isspace(UCHAR(descr->prefix[i])))
15716                 while (pos < str_bytelen && isspace(UCHAR(str[pos])))
15717                     ++pos;
15718             else if (descr->prefix[i] != str[pos])
15719                 break;
15720             else
15721                 ++pos;
15722         }
15723         if (pos >= str_bytelen) {
15724             return -1;
15725         }
15726         else if (descr->prefix[i] != 0)
15727             return 0;
15728     }
15729 
15730     if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
15731         while (isspace(UCHAR(str[pos])))
15732             ++pos;
15733 
15734 
15735     scanned = pos - anchor;
15736 
15737 
15738     if (descr->type == 'n') {
15739 
15740         *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
15741     }
15742     else if (pos >= str_bytelen) {
15743 
15744         return -1;
15745     }
15746     else if (descr->type == 'c') {
15747         int c;
15748         scanned += utf8_tounicode(&str[pos], &c);
15749         *valObjPtr = Jim_NewIntObj(interp, c);
15750         return scanned;
15751     }
15752     else {
15753 
15754         if (descr->width > 0) {
15755             size_t sLen = utf8_strlen(&str[pos], str_bytelen - pos);
15756             size_t tLen = descr->width > sLen ? sLen : descr->width;
15757 
15758             tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
15759             tok = tmpObj->bytes;
15760         }
15761         else {
15762 
15763             tok = &str[pos];
15764         }
15765         switch (descr->type) {
15766             case 'd':
15767             case 'o':
15768             case 'x':
15769             case 'u':
15770             case 'i':{
15771                     char *endp;
15772                     jim_wide w;
15773 
15774                     int base = descr->type == 'o' ? 8
15775                         : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
15776 
15777 
15778                     if (base == 0) {
15779                         w = jim_strtoull(tok, &endp);
15780                     }
15781                     else {
15782                         w = strtoull(tok, &endp, base);
15783                     }
15784 
15785                     if (endp != tok) {
15786 
15787                         *valObjPtr = Jim_NewIntObj(interp, w);
15788 
15789 
15790                         scanned += endp - tok;
15791                     }
15792                     else {
15793                         scanned = *tok ? 0 : -1;
15794                     }
15795                     break;
15796                 }
15797             case 's':
15798             case '[':{
15799                     *valObjPtr = JimScanAString(interp, descr->arg, tok);
15800                     scanned += Jim_Length(*valObjPtr);
15801                     break;
15802                 }
15803             case 'e':
15804             case 'f':
15805             case 'g':{
15806                     char *endp;
15807                     double value = strtod(tok, &endp);
15808 
15809                     if (endp != tok) {
15810 
15811                         *valObjPtr = Jim_NewDoubleObj(interp, value);
15812 
15813                         scanned += endp - tok;
15814                     }
15815                     else {
15816                         scanned = *tok ? 0 : -1;
15817                     }
15818                     break;
15819                 }
15820         }
15821         if (tmpObj) {
15822             Jim_FreeNewObj(interp, tmpObj);
15823         }
15824     }
15825     return scanned;
15826 }
15827 
15828 
Jim_ScanString(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * fmtObjPtr,int flags)15829 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
15830 {
15831     size_t i, pos;
15832     int scanned = 1;
15833     const char *str = Jim_String(strObjPtr);
15834     int str_bytelen = Jim_Length(strObjPtr);
15835     Jim_Obj *resultList = 0;
15836     Jim_Obj **resultVec = 0;
15837     int resultc;
15838     Jim_Obj *emptyStr = 0;
15839     ScanFmtStringObj *fmtObj;
15840 
15841 
15842     JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
15843 
15844     fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
15845 
15846     if (fmtObj->error != 0) {
15847         if (flags & JIM_ERRMSG)
15848             Jim_SetResultString(interp, fmtObj->error, -1);
15849         return 0;
15850     }
15851 
15852     emptyStr = Jim_NewEmptyStringObj(interp);
15853     Jim_IncrRefCount(emptyStr);
15854 
15855     resultList = Jim_NewListObj(interp, NULL, 0);
15856     if (fmtObj->maxPos > 0) {
15857         for (i = 0; i < fmtObj->maxPos; ++i)
15858             Jim_ListAppendElement(interp, resultList, emptyStr);
15859         JimListGetElements(interp, resultList, &resultc, &resultVec);
15860     }
15861 
15862     for (i = 0, pos = 0; i < fmtObj->count; ++i) {
15863         ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
15864         Jim_Obj *value = 0;
15865 
15866 
15867         if (descr->type == 0)
15868             continue;
15869 
15870         if (scanned > 0)
15871             scanned = ScanOneEntry(interp, str, pos, str_bytelen, fmtObj, i, &value);
15872 
15873         if (scanned == -1 && i == 0)
15874             goto eof;
15875 
15876         pos += scanned;
15877 
15878 
15879         if (value == 0)
15880             value = Jim_NewEmptyStringObj(interp);
15881 
15882         if (descr->pos == -1) {
15883             Jim_FreeNewObj(interp, value);
15884         }
15885         else if (descr->pos == 0)
15886 
15887             Jim_ListAppendElement(interp, resultList, value);
15888         else if (resultVec[descr->pos - 1] == emptyStr) {
15889 
15890             Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
15891             Jim_IncrRefCount(value);
15892             resultVec[descr->pos - 1] = value;
15893         }
15894         else {
15895 
15896             Jim_FreeNewObj(interp, value);
15897             goto err;
15898         }
15899     }
15900     Jim_DecrRefCount(interp, emptyStr);
15901     return resultList;
15902   eof:
15903     Jim_DecrRefCount(interp, emptyStr);
15904     Jim_FreeNewObj(interp, resultList);
15905     return (Jim_Obj *)EOF;
15906   err:
15907     Jim_DecrRefCount(interp, emptyStr);
15908     Jim_FreeNewObj(interp, resultList);
15909     return 0;
15910 }
15911 
15912 
JimPrngInit(Jim_Interp * interp)15913 static void JimPrngInit(Jim_Interp *interp)
15914 {
15915 #define PRNG_SEED_SIZE 256
15916     int i;
15917     unsigned int *seed;
15918     time_t t = time(NULL);
15919 
15920     interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
15921 
15922     seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
15923     for (i = 0; i < PRNG_SEED_SIZE; i++) {
15924         seed[i] = (rand() ^ t ^ clock());
15925     }
15926     JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
15927     Jim_Free(seed);
15928 }
15929 
15930 
JimRandomBytes(Jim_Interp * interp,void * dest,unsigned int len)15931 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
15932 {
15933     Jim_PrngState *prng;
15934     unsigned char *destByte = (unsigned char *)dest;
15935     unsigned int si, sj, x;
15936 
15937 
15938     if (interp->prngState == NULL)
15939         JimPrngInit(interp);
15940     prng = interp->prngState;
15941 
15942     for (x = 0; x < len; x++) {
15943         prng->i = (prng->i + 1) & 0xff;
15944         si = prng->sbox[prng->i];
15945         prng->j = (prng->j + si) & 0xff;
15946         sj = prng->sbox[prng->j];
15947         prng->sbox[prng->i] = sj;
15948         prng->sbox[prng->j] = si;
15949         *destByte++ = prng->sbox[(si + sj) & 0xff];
15950     }
15951 }
15952 
15953 
JimPrngSeed(Jim_Interp * interp,unsigned char * seed,int seedLen)15954 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
15955 {
15956     int i;
15957     Jim_PrngState *prng;
15958 
15959 
15960     if (interp->prngState == NULL)
15961         JimPrngInit(interp);
15962     prng = interp->prngState;
15963 
15964 
15965     for (i = 0; i < 256; i++)
15966         prng->sbox[i] = i;
15967 
15968     for (i = 0; i < seedLen; i++) {
15969         unsigned char t;
15970 
15971         t = prng->sbox[i & 0xFF];
15972         prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
15973         prng->sbox[seed[i]] = t;
15974     }
15975     prng->i = prng->j = 0;
15976 
15977     for (i = 0; i < 256; i += seedLen) {
15978         JimRandomBytes(interp, seed, seedLen);
15979     }
15980 }
15981 
15982 
Jim_IncrCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)15983 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15984 {
15985     jim_wide wideValue, increment = 1;
15986     Jim_Obj *intObjPtr;
15987 
15988     if (argc != 2 && argc != 3) {
15989         Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
15990         return JIM_ERR;
15991     }
15992     if (argc == 3) {
15993         if (Jim_GetWideExpr(interp, argv[2], &increment) != JIM_OK)
15994             return JIM_ERR;
15995     }
15996     intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
15997     if (!intObjPtr) {
15998 
15999         wideValue = 0;
16000     }
16001     else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
16002         return JIM_ERR;
16003     }
16004     if (!intObjPtr || Jim_IsShared(intObjPtr)) {
16005         intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
16006         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
16007             Jim_FreeNewObj(interp, intObjPtr);
16008             return JIM_ERR;
16009         }
16010     }
16011     else {
16012 
16013         Jim_InvalidateStringRep(intObjPtr);
16014         JimWideValue(intObjPtr) = wideValue + increment;
16015 
16016         if (argv[1]->typePtr != &variableObjType) {
16017 
16018             Jim_SetVariable(interp, argv[1], intObjPtr);
16019         }
16020     }
16021     Jim_SetResult(interp, intObjPtr);
16022     return JIM_OK;
16023 }
16024 
16025 
16026 #define JIM_EVAL_SARGV_LEN 8
16027 #define JIM_EVAL_SINTV_LEN 8
16028 
JimTraceCallback(Jim_Interp * interp,const char * type,int argc,Jim_Obj * const * argv)16029 static int JimTraceCallback(Jim_Interp *interp, const char *type, int argc, Jim_Obj *const *argv)
16030 {
16031     JimPanic((interp->traceCmdObj == NULL, "xtrace invoked with no object"));
16032 
16033     int ret;
16034     Jim_Obj *nargv[7];
16035     Jim_Obj *traceCmdObj = interp->traceCmdObj;
16036     Jim_Obj *resultObj = Jim_GetResult(interp);
16037     ScriptObj *script = NULL;
16038 
16039 
16040 
16041     if (interp->evalFrame->scriptObj) {
16042         script = JimGetScript(interp, interp->evalFrame->scriptObj);
16043     }
16044 
16045     nargv[0] = traceCmdObj;
16046     nargv[1] = Jim_NewStringObj(interp, type, -1);
16047     nargv[2] = script ? script->fileNameObj : interp->emptyObj;
16048     nargv[3] = Jim_NewIntObj(interp, script ? script->linenr : 1);
16049     nargv[4] = resultObj;
16050     nargv[5] = argv[0];
16051     nargv[6] = Jim_NewListObj(interp, argv + 1, argc - 1);
16052 
16053 
16054     interp->traceCmdObj = NULL;
16055 
16056     Jim_IncrRefCount(resultObj);
16057     ret = Jim_EvalObjVector(interp, 7, nargv);
16058     Jim_DecrRefCount(interp, resultObj);
16059 
16060     if (ret == JIM_OK || ret == JIM_RETURN) {
16061 
16062         interp->traceCmdObj = traceCmdObj;
16063         Jim_SetEmptyResult(interp);
16064         ret = JIM_OK;
16065     }
16066     else {
16067 
16068         Jim_DecrRefCount(interp, traceCmdObj);
16069     }
16070     return ret;
16071 }
16072 
16073 
JimUnknown(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16074 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16075 {
16076     int retcode;
16077 
16078     if (interp->unknown_called > 50) {
16079         return JIM_ERR;
16080     }
16081 
16082 
16083 
16084     if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
16085         return JIM_ERR;
16086 
16087     interp->unknown_called++;
16088 
16089     retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
16090     interp->unknown_called--;
16091 
16092     return retcode;
16093 }
16094 
JimPushEvalFrame(Jim_Interp * interp,Jim_EvalFrame * frame,Jim_Obj * scriptObj)16095 static void JimPushEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *scriptObj)
16096 {
16097     memset(frame, 0, sizeof(*frame));
16098     frame->parent = interp->evalFrame;
16099     frame->level = frame->parent->level + 1;
16100     frame->procLevel = interp->procLevel;
16101     frame->framePtr = interp->framePtr;
16102     if (scriptObj) {
16103         frame->scriptObj = scriptObj;
16104     }
16105     else {
16106         frame->scriptObj = frame->parent->scriptObj;
16107     }
16108     interp->evalFrame = frame;
16109 #if 0
16110     if (frame->scriptObj) {
16111         printf("script: %.*s\n", 20, Jim_String(frame->scriptObj));
16112     }
16113 #endif
16114 }
16115 
JimPopEvalFrame(Jim_Interp * interp)16116 static void JimPopEvalFrame(Jim_Interp *interp)
16117 {
16118     interp->evalFrame = interp->evalFrame->parent;
16119 }
16120 
16121 
JimInvokeCommand(Jim_Interp * interp,int objc,Jim_Obj * const * objv)16122 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
16123 {
16124     int retcode;
16125     Jim_Cmd *cmdPtr;
16126     void *prevPrivData;
16127     Jim_Obj *tailcallObj = NULL;
16128 
16129 #if 0
16130     printf("invoke");
16131     int j;
16132     for (j = 0; j < objc; j++) {
16133         printf(" '%s'", Jim_String(objv[j]));
16134     }
16135     printf("\n");
16136 #endif
16137 
16138     cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
16139     if (cmdPtr == NULL) {
16140         return JimUnknown(interp, objc, objv);
16141     }
16142     JimIncrCmdRefCount(cmdPtr);
16143 
16144     if (interp->evalDepth == interp->maxEvalDepth) {
16145         Jim_SetResultString(interp, "Infinite eval recursion", -1);
16146         retcode = JIM_ERR;
16147         goto out;
16148     }
16149     interp->evalDepth++;
16150     prevPrivData = interp->cmdPrivData;
16151 
16152 tailcall:
16153 
16154     interp->evalFrame->argc = objc;
16155     interp->evalFrame->argv = objv;
16156     interp->evalFrame->cmd = cmdPtr;
16157 
16158     if (!interp->traceCmdObj ||
16159         (retcode = JimTraceCallback(interp, "cmd", objc, objv)) == JIM_OK) {
16160 
16161         Jim_SetEmptyResult(interp);
16162         if (cmdPtr->isproc) {
16163             retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
16164         }
16165         else {
16166             interp->cmdPrivData = cmdPtr->u.native.privData;
16167             retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
16168         }
16169         if (retcode == JIM_ERR) {
16170             JimSetErrorStack(interp, NULL);
16171         }
16172     }
16173 
16174     if (tailcallObj) {
16175 
16176         Jim_DecrRefCount(interp, tailcallObj);
16177         tailcallObj = NULL;
16178     }
16179 
16180 
16181     interp->evalFrame->argc = 0;
16182     interp->evalFrame->argv = NULL;
16183 
16184 
16185     if (retcode == JIM_EVAL && interp->framePtr->tailcallObj) {
16186         JimDecrCmdRefCount(interp, cmdPtr);
16187 
16188 
16189         cmdPtr = interp->framePtr->tailcallCmd;
16190         interp->framePtr->tailcallCmd = NULL;
16191         tailcallObj = interp->framePtr->tailcallObj;
16192         interp->framePtr->tailcallObj = NULL;
16193         objc = tailcallObj->internalRep.listValue.len;
16194         objv = tailcallObj->internalRep.listValue.ele;
16195         goto tailcall;
16196     }
16197 
16198     interp->cmdPrivData = prevPrivData;
16199     interp->evalDepth--;
16200 
16201 out:
16202     JimDecrCmdRefCount(interp, cmdPtr);
16203 
16204     if (retcode == JIM_ERR) {
16205         JimSetErrorStack(interp, NULL);
16206     }
16207 
16208     if (interp->framePtr->tailcallObj) {
16209         JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
16210         Jim_DecrRefCount(interp, interp->framePtr->tailcallObj);
16211         interp->framePtr->tailcallCmd = NULL;
16212         interp->framePtr->tailcallObj = NULL;
16213     }
16214 
16215     return retcode;
16216 }
16217 
Jim_EvalObjVector(Jim_Interp * interp,int objc,Jim_Obj * const * objv)16218 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
16219 {
16220     int i, retcode;
16221     Jim_EvalFrame frame;
16222 
16223 
16224     for (i = 0; i < objc; i++)
16225         Jim_IncrRefCount(objv[i]);
16226 
16227 
16228     JimPushEvalFrame(interp, &frame, NULL);
16229 
16230     retcode = JimInvokeCommand(interp, objc, objv);
16231 
16232     JimPopEvalFrame(interp);
16233 
16234 
16235     for (i = 0; i < objc; i++)
16236         Jim_DecrRefCount(interp, objv[i]);
16237 
16238     return retcode;
16239 }
16240 
Jim_EvalObjPrefix(Jim_Interp * interp,Jim_Obj * prefix,int objc,Jim_Obj * const * objv)16241 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
16242 {
16243     int ret;
16244     Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
16245 
16246     nargv[0] = prefix;
16247     memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
16248     ret = Jim_EvalObjVector(interp, objc + 1, nargv);
16249     Jim_Free(nargv);
16250     return ret;
16251 }
16252 
JimSubstOneToken(Jim_Interp * interp,const ScriptToken * token,Jim_Obj ** objPtrPtr)16253 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
16254 {
16255     Jim_Obj *objPtr;
16256     int ret = JIM_ERR;
16257 
16258     switch (token->type) {
16259         case JIM_TT_STR:
16260         case JIM_TT_ESC:
16261             objPtr = token->objPtr;
16262             break;
16263         case JIM_TT_VAR:
16264             objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
16265             break;
16266         case JIM_TT_DICTSUGAR:
16267             objPtr = JimExpandDictSugar(interp, token->objPtr);
16268             break;
16269         case JIM_TT_EXPRSUGAR:
16270             ret = Jim_EvalExpression(interp, token->objPtr);
16271             if (ret == JIM_OK) {
16272                 objPtr = Jim_GetResult(interp);
16273             }
16274             else {
16275                 objPtr = NULL;
16276             }
16277             break;
16278         case JIM_TT_CMD:
16279             ret = Jim_EvalObj(interp, token->objPtr);
16280             if (ret == JIM_OK || ret == JIM_RETURN) {
16281                 objPtr = interp->result;
16282             } else {
16283 
16284                 objPtr = NULL;
16285             }
16286             break;
16287         default:
16288             JimPanic((1,
16289                 "default token type (%d) reached " "in Jim_SubstObj().", token->type));
16290             objPtr = NULL;
16291             break;
16292     }
16293     if (objPtr) {
16294         *objPtrPtr = objPtr;
16295         return JIM_OK;
16296     }
16297     return ret;
16298 }
16299 
JimInterpolateTokens(Jim_Interp * interp,const ScriptToken * token,int tokens,int flags)16300 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
16301 {
16302     int totlen = 0, i;
16303     Jim_Obj **intv;
16304     Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
16305     Jim_Obj *objPtr;
16306     char *s;
16307 
16308     if (tokens <= JIM_EVAL_SINTV_LEN)
16309         intv = sintv;
16310     else
16311         intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
16312 
16313     for (i = 0; i < tokens; i++) {
16314         switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
16315             case JIM_OK:
16316             case JIM_RETURN:
16317                 break;
16318             case JIM_BREAK:
16319                 if (flags & JIM_SUBST_FLAG) {
16320 
16321                     tokens = i;
16322                     continue;
16323                 }
16324 
16325 
16326             case JIM_CONTINUE:
16327                 if (flags & JIM_SUBST_FLAG) {
16328                     intv[i] = NULL;
16329                     continue;
16330                 }
16331 
16332 
16333             default:
16334                 while (i--) {
16335                     Jim_DecrRefCount(interp, intv[i]);
16336                 }
16337                 if (intv != sintv) {
16338                     Jim_Free(intv);
16339                 }
16340                 return NULL;
16341         }
16342         Jim_IncrRefCount(intv[i]);
16343         Jim_String(intv[i]);
16344         totlen += intv[i]->length;
16345     }
16346 
16347 
16348     if (tokens == 1 && intv[0] && intv == sintv) {
16349 
16350         intv[0]->refCount--;
16351         return intv[0];
16352     }
16353 
16354     objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
16355 
16356     if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
16357         && token[2].type == JIM_TT_VAR) {
16358 
16359         objPtr->typePtr = &interpolatedObjType;
16360         objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
16361         objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
16362         Jim_IncrRefCount(intv[2]);
16363     }
16364     else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
16365 
16366         int line;
16367         Jim_Obj *fileNameObj = Jim_GetSourceInfo(interp, intv[0], &line);
16368         Jim_SetSourceInfo(interp, objPtr, fileNameObj, line);
16369     }
16370 
16371 
16372     s = objPtr->bytes = Jim_Alloc(totlen + 1);
16373     objPtr->length = totlen;
16374     for (i = 0; i < tokens; i++) {
16375         if (intv[i]) {
16376             memcpy(s, intv[i]->bytes, intv[i]->length);
16377             s += intv[i]->length;
16378             Jim_DecrRefCount(interp, intv[i]);
16379         }
16380     }
16381     objPtr->bytes[totlen] = '\0';
16382 
16383     if (intv != sintv) {
16384         Jim_Free(intv);
16385     }
16386 
16387     return objPtr;
16388 }
16389 
16390 
JimEvalObjList(Jim_Interp * interp,Jim_Obj * listPtr)16391 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
16392 {
16393     int retcode = JIM_OK;
16394     Jim_EvalFrame frame;
16395 
16396     JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
16397 
16398     JimPushEvalFrame(interp, &frame, NULL);
16399 
16400     if (listPtr->internalRep.listValue.len) {
16401         Jim_IncrRefCount(listPtr);
16402         retcode = JimInvokeCommand(interp,
16403             listPtr->internalRep.listValue.len,
16404             listPtr->internalRep.listValue.ele);
16405         Jim_DecrRefCount(interp, listPtr);
16406     }
16407 
16408     JimPopEvalFrame(interp);
16409 
16410     return retcode;
16411 }
16412 
Jim_EvalObjList(Jim_Interp * interp,Jim_Obj * listPtr)16413 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
16414 {
16415     SetListFromAny(interp, listPtr);
16416     return JimEvalObjList(interp, listPtr);
16417 }
16418 
Jim_EvalObj(Jim_Interp * interp,Jim_Obj * scriptObjPtr)16419 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
16420 {
16421     int i;
16422     ScriptObj *script;
16423     ScriptToken *token;
16424     int retcode = JIM_OK;
16425     Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
16426     Jim_EvalFrame frame;
16427 
16428     if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
16429         return JimEvalObjList(interp, scriptObjPtr);
16430     }
16431 
16432     Jim_IncrRefCount(scriptObjPtr);
16433     script = JimGetScript(interp, scriptObjPtr);
16434     if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
16435         JimSetErrorStack(interp, script);
16436         Jim_DecrRefCount(interp, scriptObjPtr);
16437         return JIM_ERR;
16438     }
16439 
16440     Jim_SetEmptyResult(interp);
16441 
16442     token = script->token;
16443 
16444 #ifdef JIM_OPTIMIZATION
16445     if (script->len == 0) {
16446         Jim_DecrRefCount(interp, scriptObjPtr);
16447         return JIM_OK;
16448     }
16449     if (script->len == 3
16450         && token[1].objPtr->typePtr == &commandObjType
16451         && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
16452         && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
16453         && token[2].objPtr->typePtr == &variableObjType) {
16454 
16455         Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
16456 
16457         if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16458             JimWideValue(objPtr)++;
16459             Jim_InvalidateStringRep(objPtr);
16460             Jim_DecrRefCount(interp, scriptObjPtr);
16461             Jim_SetResult(interp, objPtr);
16462             return JIM_OK;
16463         }
16464     }
16465 #endif
16466 
16467     script->inUse++;
16468 
16469     JimPushEvalFrame(interp, &frame, scriptObjPtr);
16470 
16471 
16472     interp->errorFlag = 0;
16473     argv = sargv;
16474 
16475     for (i = 0; i < script->len && retcode == JIM_OK; ) {
16476         int argc;
16477         int j;
16478 
16479 
16480         argc = token[i].objPtr->internalRep.scriptLineValue.argc;
16481         script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
16482 
16483 
16484         if (argc > JIM_EVAL_SARGV_LEN)
16485             argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
16486 
16487 
16488         i++;
16489 
16490         for (j = 0; j < argc; j++) {
16491             long wordtokens = 1;
16492             int expand = 0;
16493             Jim_Obj *wordObjPtr = NULL;
16494 
16495             if (token[i].type == JIM_TT_WORD) {
16496                 wordtokens = JimWideValue(token[i++].objPtr);
16497                 if (wordtokens < 0) {
16498                     expand = 1;
16499                     wordtokens = -wordtokens;
16500                 }
16501             }
16502 
16503             if (wordtokens == 1) {
16504 
16505                 switch (token[i].type) {
16506                     case JIM_TT_ESC:
16507                     case JIM_TT_STR:
16508                         wordObjPtr = token[i].objPtr;
16509                         break;
16510                     case JIM_TT_VAR:
16511                         wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
16512                         break;
16513                     case JIM_TT_EXPRSUGAR:
16514                         retcode = Jim_EvalExpression(interp, token[i].objPtr);
16515                         if (retcode == JIM_OK) {
16516                             wordObjPtr = Jim_GetResult(interp);
16517                         }
16518                         else {
16519                             wordObjPtr = NULL;
16520                         }
16521                         break;
16522                     case JIM_TT_DICTSUGAR:
16523                         wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
16524                         break;
16525                     case JIM_TT_CMD:
16526                         retcode = Jim_EvalObj(interp, token[i].objPtr);
16527                         if (retcode == JIM_OK) {
16528                             wordObjPtr = Jim_GetResult(interp);
16529                         }
16530                         break;
16531                     default:
16532                         JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
16533                 }
16534             }
16535             else {
16536                 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
16537             }
16538 
16539             if (!wordObjPtr) {
16540                 if (retcode == JIM_OK) {
16541                     retcode = JIM_ERR;
16542                 }
16543                 break;
16544             }
16545 
16546             Jim_IncrRefCount(wordObjPtr);
16547             i += wordtokens;
16548 
16549             if (!expand) {
16550                 argv[j] = wordObjPtr;
16551             }
16552             else {
16553 
16554                 int len = Jim_ListLength(interp, wordObjPtr);
16555                 int newargc = argc + len - 1;
16556                 int k;
16557 
16558                 if (len > 1) {
16559                     if (argv == sargv) {
16560                         if (newargc > JIM_EVAL_SARGV_LEN) {
16561                             argv = Jim_Alloc(sizeof(*argv) * newargc);
16562                             memcpy(argv, sargv, sizeof(*argv) * j);
16563                         }
16564                     }
16565                     else {
16566 
16567                         argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
16568                     }
16569                 }
16570 
16571 
16572                 for (k = 0; k < len; k++) {
16573                     argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
16574                     Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
16575                 }
16576 
16577                 Jim_DecrRefCount(interp, wordObjPtr);
16578 
16579 
16580                 j--;
16581                 argc += len - 1;
16582             }
16583         }
16584 
16585         if (retcode == JIM_OK && argc) {
16586 
16587             retcode = JimInvokeCommand(interp, argc, argv);
16588 
16589             if (Jim_CheckSignal(interp)) {
16590                 retcode = JIM_SIGNAL;
16591             }
16592         }
16593 
16594 
16595         while (j-- > 0) {
16596             Jim_DecrRefCount(interp, argv[j]);
16597         }
16598 
16599         if (argv != sargv) {
16600             Jim_Free(argv);
16601             argv = sargv;
16602         }
16603     }
16604 
16605 
16606     if (retcode == JIM_ERR) {
16607         JimSetErrorStack(interp, NULL);
16608     }
16609 
16610     JimPopEvalFrame(interp);
16611 
16612     Jim_FreeIntRep(interp, scriptObjPtr);
16613     scriptObjPtr->typePtr = &scriptObjType;
16614     Jim_SetIntRepPtr(scriptObjPtr, script);
16615     Jim_DecrRefCount(interp, scriptObjPtr);
16616 
16617     return retcode;
16618 }
16619 
JimSetProcArg(Jim_Interp * interp,Jim_Obj * argNameObj,Jim_Obj * argValObj)16620 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
16621 {
16622     int retcode;
16623 
16624     const char *varname = Jim_String(argNameObj);
16625     if (*varname == '&') {
16626 
16627         Jim_Obj *objPtr;
16628         Jim_CallFrame *savedCallFrame = interp->framePtr;
16629 
16630         interp->framePtr = interp->framePtr->parent;
16631         objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
16632         interp->framePtr = savedCallFrame;
16633         if (!objPtr) {
16634             return JIM_ERR;
16635         }
16636 
16637 
16638         objPtr = Jim_NewStringObj(interp, varname + 1, -1);
16639         Jim_IncrRefCount(objPtr);
16640         retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
16641         Jim_DecrRefCount(interp, objPtr);
16642     }
16643     else {
16644         retcode = Jim_SetVariable(interp, argNameObj, argValObj);
16645     }
16646     return retcode;
16647 }
16648 
JimSetProcWrongArgs(Jim_Interp * interp,Jim_Obj * procNameObj,Jim_Cmd * cmd)16649 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
16650 {
16651 
16652     Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
16653     int i;
16654 
16655     for (i = 0; i < cmd->u.proc.argListLen; i++) {
16656         Jim_AppendString(interp, argmsg, " ", 1);
16657 
16658         if (i == cmd->u.proc.argsPos) {
16659             if (cmd->u.proc.arglist[i].defaultObjPtr) {
16660 
16661                 Jim_AppendString(interp, argmsg, "?", 1);
16662                 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
16663                 Jim_AppendString(interp, argmsg, " ...?", -1);
16664             }
16665             else {
16666 
16667                 Jim_AppendString(interp, argmsg, "?arg ...?", -1);
16668             }
16669         }
16670         else {
16671             if (cmd->u.proc.arglist[i].defaultObjPtr) {
16672                 Jim_AppendString(interp, argmsg, "?", 1);
16673                 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
16674                 Jim_AppendString(interp, argmsg, "?", 1);
16675             }
16676             else {
16677                 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
16678                 if (*arg == '&') {
16679                     arg++;
16680                 }
16681                 Jim_AppendString(interp, argmsg, arg, -1);
16682             }
16683         }
16684     }
16685     Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
16686 }
16687 
16688 #ifdef jim_ext_namespace
Jim_EvalNamespace(Jim_Interp * interp,Jim_Obj * scriptObj,Jim_Obj * nsObj)16689 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
16690 {
16691     Jim_CallFrame *callFramePtr;
16692     int retcode;
16693 
16694 
16695     callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
16696     callFramePtr->argv = interp->evalFrame->argv;
16697     callFramePtr->argc = interp->evalFrame->argc;
16698     callFramePtr->procArgsObjPtr = NULL;
16699     callFramePtr->procBodyObjPtr = scriptObj;
16700     callFramePtr->staticVars = NULL;
16701     Jim_IncrRefCount(scriptObj);
16702     interp->framePtr = callFramePtr;
16703 
16704 
16705     if (interp->framePtr->level == interp->maxCallFrameDepth) {
16706         Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
16707         retcode = JIM_ERR;
16708     }
16709     else {
16710 
16711         retcode = Jim_EvalObj(interp, scriptObj);
16712     }
16713 
16714 
16715     interp->framePtr = interp->framePtr->parent;
16716     JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
16717 
16718     return retcode;
16719 }
16720 #endif
16721 
JimCallProcedure(Jim_Interp * interp,Jim_Cmd * cmd,int argc,Jim_Obj * const * argv)16722 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
16723 {
16724     Jim_CallFrame *callFramePtr;
16725     int i, d, retcode, optargs;
16726 
16727 
16728     if (argc - 1 < cmd->u.proc.reqArity ||
16729         (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
16730         JimSetProcWrongArgs(interp, argv[0], cmd);
16731         return JIM_ERR;
16732     }
16733 
16734     if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
16735 
16736         return JIM_OK;
16737     }
16738 
16739 
16740     if (interp->framePtr->level == interp->maxCallFrameDepth) {
16741         Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
16742         return JIM_ERR;
16743     }
16744 
16745 
16746     callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
16747     callFramePtr->argv = argv;
16748     callFramePtr->argc = argc;
16749     callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
16750     callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
16751     callFramePtr->staticVars = cmd->u.proc.staticVars;
16752 
16753     interp->procLevel++;
16754 
16755     Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
16756     Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
16757     interp->framePtr = callFramePtr;
16758 
16759 
16760     optargs = (argc - 1 - cmd->u.proc.reqArity);
16761 
16762 
16763     i = 1;
16764     for (d = 0; d < cmd->u.proc.argListLen; d++) {
16765         Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
16766         if (d == cmd->u.proc.argsPos) {
16767 
16768             Jim_Obj *listObjPtr;
16769             int argsLen = 0;
16770             if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
16771                 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
16772             }
16773             listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
16774 
16775 
16776             if (cmd->u.proc.arglist[d].defaultObjPtr) {
16777                 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
16778             }
16779             retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
16780             if (retcode != JIM_OK) {
16781                 goto badargset;
16782             }
16783 
16784             i += argsLen;
16785             continue;
16786         }
16787 
16788 
16789         if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
16790             retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
16791         }
16792         else {
16793 
16794             retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
16795         }
16796         if (retcode != JIM_OK) {
16797             goto badargset;
16798         }
16799     }
16800 
16801     if (interp->traceCmdObj == NULL ||
16802         (retcode = JimTraceCallback(interp, "proc", argc, argv)) == JIM_OK) {
16803 
16804         retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
16805     }
16806 
16807 badargset:
16808 
16809 
16810     retcode = JimInvokeDefer(interp, retcode);
16811     interp->framePtr = interp->framePtr->parent;
16812     JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
16813 
16814 
16815     if (retcode == JIM_RETURN) {
16816         if (--interp->returnLevel <= 0) {
16817             retcode = interp->returnCode;
16818             interp->returnCode = JIM_OK;
16819             interp->returnLevel = 0;
16820         }
16821     }
16822     interp->procLevel--;
16823 
16824     return retcode;
16825 }
16826 
Jim_EvalSource(Jim_Interp * interp,const char * filename,int lineno,const char * script)16827 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
16828 {
16829     int retval;
16830     Jim_Obj *scriptObjPtr;
16831 
16832     scriptObjPtr = Jim_NewStringObj(interp, script, -1);
16833     Jim_IncrRefCount(scriptObjPtr);
16834     if (filename) {
16835         Jim_SetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
16836     }
16837     retval = Jim_EvalObj(interp, scriptObjPtr);
16838     Jim_DecrRefCount(interp, scriptObjPtr);
16839     return retval;
16840 }
16841 
Jim_Eval(Jim_Interp * interp,const char * script)16842 int Jim_Eval(Jim_Interp *interp, const char *script)
16843 {
16844     return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
16845 }
16846 
16847 
Jim_EvalGlobal(Jim_Interp * interp,const char * script)16848 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
16849 {
16850     int retval;
16851     Jim_CallFrame *savedFramePtr = interp->framePtr;
16852 
16853     interp->framePtr = interp->topFramePtr;
16854     retval = Jim_Eval(interp, script);
16855     interp->framePtr = savedFramePtr;
16856 
16857     return retval;
16858 }
16859 
Jim_EvalFileGlobal(Jim_Interp * interp,const char * filename)16860 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
16861 {
16862     int retval;
16863     Jim_CallFrame *savedFramePtr = interp->framePtr;
16864 
16865     interp->framePtr = interp->topFramePtr;
16866     retval = Jim_EvalFile(interp, filename);
16867     interp->framePtr = savedFramePtr;
16868 
16869     return retval;
16870 }
16871 
16872 #include <sys/stat.h>
16873 
JimReadTextFile(Jim_Interp * interp,const char * filename)16874 static Jim_Obj *JimReadTextFile(Jim_Interp *interp, const char *filename)
16875 {
16876     jim_stat_t sb;
16877     int fd;
16878     char *buf;
16879     int readlen;
16880 
16881     if (Jim_Stat(filename, &sb) == -1 || (fd = open(filename, O_RDONLY | O_TEXT, 0666)) < 0) {
16882         Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
16883         return NULL;
16884     }
16885     buf = Jim_Alloc(sb.st_size + 1);
16886     readlen = read(fd, buf, sb.st_size);
16887     close(fd);
16888     if (readlen < 0) {
16889         Jim_Free(buf);
16890         Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
16891         return NULL;
16892     }
16893     else {
16894         Jim_Obj *objPtr;
16895         buf[readlen] = 0;
16896 
16897         objPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
16898 
16899         return objPtr;
16900     }
16901 }
16902 
16903 
Jim_EvalFile(Jim_Interp * interp,const char * filename)16904 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
16905 {
16906     Jim_Obj *filenameObj;
16907     Jim_Obj *oldFilenameObj;
16908     Jim_Obj *scriptObjPtr;
16909     int retcode;
16910 
16911     scriptObjPtr = JimReadTextFile(interp, filename);
16912     if (!scriptObjPtr) {
16913         return JIM_ERR;
16914     }
16915 
16916     filenameObj = Jim_NewStringObj(interp, filename, -1);
16917     Jim_SetSourceInfo(interp, scriptObjPtr, filenameObj, 1);
16918 
16919     oldFilenameObj = JimPushInterpObj(interp->currentFilenameObj, filenameObj);
16920 
16921     retcode = Jim_EvalObj(interp, scriptObjPtr);
16922 
16923     JimPopInterpObj(interp, interp->currentFilenameObj, oldFilenameObj);
16924 
16925 
16926     if (retcode == JIM_RETURN) {
16927         if (--interp->returnLevel <= 0) {
16928             retcode = interp->returnCode;
16929             interp->returnCode = JIM_OK;
16930             interp->returnLevel = 0;
16931         }
16932     }
16933 
16934     return retcode;
16935 }
16936 
JimParseSubst(struct JimParserCtx * pc,int flags)16937 static void JimParseSubst(struct JimParserCtx *pc, int flags)
16938 {
16939     pc->tstart = pc->p;
16940     pc->tline = pc->linenr;
16941 
16942     if (pc->len == 0) {
16943         pc->tend = pc->p;
16944         pc->tt = JIM_TT_EOL;
16945         pc->eof = 1;
16946         return;
16947     }
16948     if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
16949         JimParseCmd(pc);
16950         return;
16951     }
16952     if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
16953         if (JimParseVar(pc) == JIM_OK) {
16954             return;
16955         }
16956 
16957         pc->tstart = pc->p;
16958 
16959         pc->p++;
16960         pc->len--;
16961     }
16962     while (pc->len) {
16963         if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
16964             break;
16965         }
16966         if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
16967             break;
16968         }
16969         if (*pc->p == '\\' && pc->len > 1) {
16970             pc->p++;
16971             pc->len--;
16972         }
16973         pc->p++;
16974         pc->len--;
16975     }
16976     pc->tend = pc->p - 1;
16977     pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
16978 }
16979 
16980 
SetSubstFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr,int flags)16981 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
16982 {
16983     int scriptTextLen;
16984     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
16985     struct JimParserCtx parser;
16986     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
16987     ParseTokenList tokenlist;
16988 
16989 
16990     ScriptTokenListInit(&tokenlist);
16991 
16992     JimParserInit(&parser, scriptText, scriptTextLen, 1);
16993     while (1) {
16994         JimParseSubst(&parser, flags);
16995         if (parser.eof) {
16996 
16997             break;
16998         }
16999         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
17000             parser.tline);
17001     }
17002 
17003 
17004     script->inUse = 1;
17005     script->substFlags = flags;
17006     script->fileNameObj = interp->emptyObj;
17007     Jim_IncrRefCount(script->fileNameObj);
17008     SubstObjAddTokens(interp, script, &tokenlist);
17009 
17010 
17011     ScriptTokenListFree(&tokenlist);
17012 
17013 #ifdef DEBUG_SHOW_SUBST
17014     {
17015         int i;
17016 
17017         printf("==== Subst ====\n");
17018         for (i = 0; i < script->len; i++) {
17019             printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
17020                 Jim_String(script->token[i].objPtr));
17021         }
17022     }
17023 #endif
17024 
17025 
17026     Jim_FreeIntRep(interp, objPtr);
17027     Jim_SetIntRepPtr(objPtr, script);
17028     objPtr->typePtr = &scriptObjType;
17029     return JIM_OK;
17030 }
17031 
Jim_GetSubst(Jim_Interp * interp,Jim_Obj * objPtr,int flags)17032 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
17033 {
17034     if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
17035         SetSubstFromAny(interp, objPtr, flags);
17036     return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
17037 }
17038 
Jim_SubstObj(Jim_Interp * interp,Jim_Obj * substObjPtr,Jim_Obj ** resObjPtrPtr,int flags)17039 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
17040 {
17041     ScriptObj *script;
17042 
17043     JimPanic((substObjPtr->refCount == 0, "Jim_SubstObj() called with zero refcount object"));
17044 
17045     script = Jim_GetSubst(interp, substObjPtr, flags);
17046 
17047     Jim_IncrRefCount(substObjPtr);
17048     script->inUse++;
17049 
17050     *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
17051 
17052     script->inUse--;
17053     Jim_DecrRefCount(interp, substObjPtr);
17054     if (*resObjPtrPtr == NULL) {
17055         return JIM_ERR;
17056     }
17057     return JIM_OK;
17058 }
17059 
Jim_WrongNumArgs(Jim_Interp * interp,int argc,Jim_Obj * const * argv,const char * msg)17060 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
17061 {
17062     Jim_Obj *objPtr;
17063     Jim_Obj *listObjPtr;
17064 
17065     JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0"));
17066 
17067     listObjPtr = Jim_NewListObj(interp, argv, argc);
17068 
17069     if (msg && *msg) {
17070         Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
17071     }
17072     Jim_IncrRefCount(listObjPtr);
17073     objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
17074     Jim_DecrRefCount(interp, listObjPtr);
17075 
17076     Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
17077 }
17078 
17079 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
17080     Jim_Obj *keyObjPtr, void *value, Jim_Obj *patternObjPtr, int type);
17081 
17082 #define JimTrivialMatch(pattern)    (strpbrk((pattern), "*[?\\") == NULL)
17083 
JimHashtablePatternMatch(Jim_Interp * interp,Jim_HashTable * ht,Jim_Obj * patternObjPtr,JimHashtableIteratorCallbackType * callback,int type)17084 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
17085     JimHashtableIteratorCallbackType *callback, int type)
17086 {
17087     Jim_HashEntry *he;
17088     Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
17089 
17090 
17091     if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
17092         he = Jim_FindHashEntry(ht, patternObjPtr);
17093         if (he) {
17094             callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
17095                 patternObjPtr, type);
17096         }
17097     }
17098     else {
17099         Jim_HashTableIterator htiter;
17100         JimInitHashTableIterator(ht, &htiter);
17101         while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
17102             callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
17103                 patternObjPtr, type);
17104         }
17105     }
17106     return listObjPtr;
17107 }
17108 
17109 
17110 #define JIM_CMDLIST_COMMANDS 0
17111 #define JIM_CMDLIST_PROCS 1
17112 #define JIM_CMDLIST_CHANNELS 2
17113 
JimCommandMatch(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_Obj * keyObj,void * value,Jim_Obj * patternObj,int type)17114 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
17115     Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
17116 {
17117     Jim_Cmd *cmdPtr = (Jim_Cmd *)value;
17118 
17119     if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
17120 
17121         return;
17122     }
17123 
17124     Jim_IncrRefCount(keyObj);
17125 
17126     if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, keyObj) >= 0) {
17127         int match = 1;
17128         if (patternObj) {
17129             int plen, slen;
17130             const char *pattern = Jim_GetStringNoQualifier(patternObj, &plen);
17131             const char *str = Jim_GetStringNoQualifier(keyObj, &slen);
17132 #ifdef JIM_NO_INTROSPECTION
17133 
17134             match = (JimStringCompareUtf8(pattern, plen, str, slen, 0) == 0);
17135 #else
17136             match = JimGlobMatch(pattern, plen, str, slen, 0);
17137 #endif
17138         }
17139         if (match) {
17140             Jim_ListAppendElement(interp, listObjPtr, keyObj);
17141         }
17142     }
17143     Jim_DecrRefCount(interp, keyObj);
17144 }
17145 
JimCommandsList(Jim_Interp * interp,Jim_Obj * patternObjPtr,int type)17146 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
17147 {
17148     return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
17149 }
17150 
17151 
17152 #define JIM_VARLIST_GLOBALS 0
17153 #define JIM_VARLIST_LOCALS 1
17154 #define JIM_VARLIST_VARS 2
17155 #define JIM_VARLIST_MASK 0x000f
17156 
17157 #define JIM_VARLIST_VALUES 0x1000
17158 
JimVariablesMatch(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_Obj * keyObj,void * value,Jim_Obj * patternObj,int type)17159 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
17160     Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
17161 {
17162     Jim_VarVal *vv = (Jim_VarVal *)value;
17163 
17164     if ((type & JIM_VARLIST_MASK) != JIM_VARLIST_LOCALS || vv->linkFramePtr == NULL) {
17165         if (patternObj == NULL || Jim_StringMatchObj(interp, patternObj, keyObj, 0)) {
17166             Jim_ListAppendElement(interp, listObjPtr, keyObj);
17167             if (type & JIM_VARLIST_VALUES) {
17168                 Jim_ListAppendElement(interp, listObjPtr, vv->objPtr);
17169             }
17170         }
17171     }
17172 }
17173 
17174 
JimVariablesList(Jim_Interp * interp,Jim_Obj * patternObjPtr,int mode)17175 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
17176 {
17177     if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
17178         return interp->emptyObj;
17179     }
17180     else {
17181         Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
17182         return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch,
17183             mode);
17184     }
17185 }
17186 
JimInfoLevel(Jim_Interp * interp,Jim_Obj * levelObjPtr,Jim_Obj ** objPtrPtr)17187 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
17188 {
17189     long level;
17190 
17191     if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
17192         Jim_CallFrame *targetCallFrame = JimGetCallFrameByInteger(interp, level);
17193         if (targetCallFrame && targetCallFrame != interp->topFramePtr) {
17194 #ifdef JIM_NO_INTROSPECTION
17195 
17196             *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, 1);
17197 #else
17198             *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
17199 #endif
17200             return JIM_OK;
17201         }
17202     }
17203     Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
17204     return JIM_ERR;
17205 }
17206 
JimInfoFrame(Jim_Interp * interp,Jim_Obj * levelObjPtr,Jim_Obj ** objPtrPtr)17207 static int JimInfoFrame(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
17208 {
17209     long level;
17210 
17211     if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
17212         Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, level);
17213         if (frame) {
17214             Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
17215 
17216             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
17217             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "source", -1));
17218             if (frame->scriptObj) {
17219                 ScriptObj *script = JimGetScript(interp, frame->scriptObj);
17220                 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "line", -1));
17221                 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, script->linenr));
17222                 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "file", -1));
17223                 Jim_ListAppendElement(interp, listObj, script->fileNameObj);
17224             }
17225 #ifndef JIM_NO_INTROSPECTION
17226             {
17227                 Jim_Obj *cmdObj = Jim_NewListObj(interp, frame->argv, frame->argc);
17228 
17229                 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "cmd", -1));
17230                 Jim_ListAppendElement(interp, listObj, cmdObj);
17231             }
17232 #endif
17233             {
17234                 Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
17235                 if (procNameObj) {
17236                     Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "proc", -1));
17237                     Jim_ListAppendElement(interp, listObj, procNameObj);
17238                  }
17239             }
17240             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "level", -1));
17241             Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, interp->framePtr->level - frame->framePtr->level));
17242 
17243             *objPtrPtr = listObj;
17244             return JIM_OK;
17245         }
17246     }
17247     Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
17248     return JIM_ERR;
17249 }
17250 
17251 
Jim_PutsCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17252 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17253 {
17254     if (argc != 2 && argc != 3) {
17255         Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
17256         return JIM_ERR;
17257     }
17258     if (argc == 3) {
17259         if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
17260             Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
17261             return JIM_ERR;
17262         }
17263         else {
17264             fputs(Jim_String(argv[2]), stdout);
17265         }
17266     }
17267     else {
17268         puts(Jim_String(argv[1]));
17269     }
17270     return JIM_OK;
17271 }
17272 
17273 
JimAddMulHelper(Jim_Interp * interp,int argc,Jim_Obj * const * argv,int op)17274 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
17275 {
17276     jim_wide wideValue, res;
17277     double doubleValue, doubleRes;
17278     int i;
17279 
17280     res = (op == JIM_EXPROP_ADD) ? 0 : 1;
17281 
17282     for (i = 1; i < argc; i++) {
17283         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
17284             goto trydouble;
17285         if (op == JIM_EXPROP_ADD)
17286             res += wideValue;
17287         else
17288             res *= wideValue;
17289     }
17290     Jim_SetResultInt(interp, res);
17291     return JIM_OK;
17292   trydouble:
17293     doubleRes = (double)res;
17294     for (; i < argc; i++) {
17295         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
17296             return JIM_ERR;
17297         if (op == JIM_EXPROP_ADD)
17298             doubleRes += doubleValue;
17299         else
17300             doubleRes *= doubleValue;
17301     }
17302     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17303     return JIM_OK;
17304 }
17305 
17306 
JimSubDivHelper(Jim_Interp * interp,int argc,Jim_Obj * const * argv,int op)17307 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
17308 {
17309     jim_wide wideValue, res = 0;
17310     double doubleValue, doubleRes = 0;
17311     int i = 2;
17312 
17313     if (argc < 2) {
17314         Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
17315         return JIM_ERR;
17316     }
17317     else if (argc == 2) {
17318         if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
17319             if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
17320                 return JIM_ERR;
17321             }
17322             else {
17323                 if (op == JIM_EXPROP_SUB)
17324                     doubleRes = -doubleValue;
17325                 else
17326                     doubleRes = 1.0 / doubleValue;
17327                 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17328                 return JIM_OK;
17329             }
17330         }
17331         if (op == JIM_EXPROP_SUB) {
17332             res = -wideValue;
17333             Jim_SetResultInt(interp, res);
17334         }
17335         else {
17336             doubleRes = 1.0 / wideValue;
17337             Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17338         }
17339         return JIM_OK;
17340     }
17341     else {
17342         if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
17343             if (Jim_GetDouble(interp, argv[1], &doubleRes)
17344                 != JIM_OK) {
17345                 return JIM_ERR;
17346             }
17347             else {
17348                 goto trydouble;
17349             }
17350         }
17351     }
17352     for (i = 2; i < argc; i++) {
17353         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
17354             doubleRes = (double)res;
17355             goto trydouble;
17356         }
17357         if (op == JIM_EXPROP_SUB)
17358             res -= wideValue;
17359         else {
17360             if (wideValue == 0) {
17361                 Jim_SetResultString(interp, "Division by zero", -1);
17362                 return JIM_ERR;
17363             }
17364             res /= wideValue;
17365         }
17366     }
17367     Jim_SetResultInt(interp, res);
17368     return JIM_OK;
17369   trydouble:
17370     for (; i < argc; i++) {
17371         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
17372             return JIM_ERR;
17373         if (op == JIM_EXPROP_SUB)
17374             doubleRes -= doubleValue;
17375         else
17376             doubleRes /= doubleValue;
17377     }
17378     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
17379     return JIM_OK;
17380 }
17381 
17382 
17383 
Jim_AddCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17384 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17385 {
17386     return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
17387 }
17388 
17389 
Jim_MulCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17390 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17391 {
17392     return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
17393 }
17394 
17395 
Jim_SubCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17396 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17397 {
17398     return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
17399 }
17400 
17401 
Jim_DivCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17402 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17403 {
17404     return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
17405 }
17406 
17407 
Jim_SetCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17408 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17409 {
17410     if (argc != 2 && argc != 3) {
17411         Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
17412         return JIM_ERR;
17413     }
17414     if (argc == 2) {
17415         Jim_Obj *objPtr;
17416 
17417         objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17418         if (!objPtr)
17419             return JIM_ERR;
17420         Jim_SetResult(interp, objPtr);
17421         return JIM_OK;
17422     }
17423 
17424     if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
17425         return JIM_ERR;
17426     Jim_SetResult(interp, argv[2]);
17427     return JIM_OK;
17428 }
17429 
Jim_UnsetCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17430 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17431 {
17432     int i = 1;
17433     int complain = 1;
17434 
17435     while (i < argc) {
17436         if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
17437             i++;
17438             break;
17439         }
17440         if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
17441             complain = 0;
17442             i++;
17443             continue;
17444         }
17445         break;
17446     }
17447 
17448     while (i < argc) {
17449         if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
17450             && complain) {
17451             return JIM_ERR;
17452         }
17453         i++;
17454     }
17455 
17456     Jim_SetEmptyResult(interp);
17457     return JIM_OK;
17458 }
17459 
JimCheckLoopRetcode(Jim_Interp * interp,int retval)17460 static int JimCheckLoopRetcode(Jim_Interp *interp, int retval)
17461 {
17462     if (retval == JIM_BREAK || retval == JIM_CONTINUE) {
17463         if (--interp->break_level > 0) {
17464             return 1;
17465         }
17466     }
17467     return 0;
17468 }
17469 
17470 
Jim_WhileCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17471 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17472 {
17473     if (argc != 3) {
17474         Jim_WrongNumArgs(interp, 1, argv, "condition body");
17475         return JIM_ERR;
17476     }
17477 
17478 
17479     while (1) {
17480         int boolean = 0, retval;
17481 
17482         if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
17483             return retval;
17484         if (!boolean)
17485             break;
17486 
17487         if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
17488             if (JimCheckLoopRetcode(interp, retval)) {
17489                 return retval;
17490             }
17491             switch (retval) {
17492                 case JIM_BREAK:
17493                     goto out;
17494                 case JIM_CONTINUE:
17495                     continue;
17496                 default:
17497                     return retval;
17498             }
17499         }
17500     }
17501   out:
17502     Jim_SetEmptyResult(interp);
17503     return JIM_OK;
17504 }
17505 
17506 
Jim_ForCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17507 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17508 {
17509     int retval;
17510     int boolean = 1;
17511     int immediate = 0;
17512     Jim_Obj *varNamePtr = NULL;
17513     Jim_Obj *stopVarNamePtr = NULL;
17514 
17515     if (argc != 5) {
17516         Jim_WrongNumArgs(interp, 1, argv, "start test next body");
17517         return JIM_ERR;
17518     }
17519 
17520 
17521     if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
17522         return retval;
17523     }
17524 
17525     retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
17526 
17527 
17528 #ifdef JIM_OPTIMIZATION
17529     if (retval == JIM_OK && boolean) {
17530         ScriptObj *incrScript;
17531         struct ExprTree *expr;
17532         jim_wide stop, currentVal;
17533         Jim_Obj *objPtr;
17534         int cmpOffset;
17535 
17536 
17537         expr = JimGetExpression(interp, argv[2]);
17538         incrScript = JimGetScript(interp, argv[3]);
17539 
17540 
17541         if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
17542             goto evalstart;
17543         }
17544 
17545         if (incrScript->token[1].type != JIM_TT_ESC) {
17546             goto evalstart;
17547         }
17548 
17549         if (expr->expr->type == JIM_EXPROP_LT) {
17550             cmpOffset = 0;
17551         }
17552         else if (expr->expr->type == JIM_EXPROP_LTE) {
17553             cmpOffset = 1;
17554         }
17555         else {
17556             goto evalstart;
17557         }
17558 
17559         if (expr->expr->left->type != JIM_TT_VAR) {
17560             goto evalstart;
17561         }
17562 
17563         if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) {
17564             goto evalstart;
17565         }
17566 
17567 
17568         if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
17569             goto evalstart;
17570         }
17571 
17572 
17573         if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
17574             goto evalstart;
17575         }
17576 
17577 
17578         if (expr->expr->right->type == JIM_TT_EXPR_INT) {
17579             if (Jim_GetWideExpr(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) {
17580                 goto evalstart;
17581             }
17582         }
17583         else {
17584             stopVarNamePtr = expr->expr->right->objPtr;
17585             Jim_IncrRefCount(stopVarNamePtr);
17586 
17587             stop = 0;
17588         }
17589 
17590 
17591         varNamePtr = expr->expr->left->objPtr;
17592         Jim_IncrRefCount(varNamePtr);
17593 
17594         objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
17595         if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
17596             goto testcond;
17597         }
17598 
17599 
17600         while (retval == JIM_OK) {
17601 
17602 
17603 
17604 
17605             if (stopVarNamePtr) {
17606                 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
17607                 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
17608                     goto testcond;
17609                 }
17610             }
17611 
17612             if (currentVal >= stop + cmpOffset) {
17613                 break;
17614             }
17615 
17616 
17617             retval = Jim_EvalObj(interp, argv[4]);
17618             if (JimCheckLoopRetcode(interp, retval)) {
17619                 immediate++;
17620                 goto out;
17621             }
17622             if (retval == JIM_OK || retval == JIM_CONTINUE) {
17623                 retval = JIM_OK;
17624 
17625                 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
17626 
17627 
17628                 if (objPtr == NULL) {
17629                     retval = JIM_ERR;
17630                     goto out;
17631                 }
17632                 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
17633                     currentVal = ++JimWideValue(objPtr);
17634                     Jim_InvalidateStringRep(objPtr);
17635                 }
17636                 else {
17637                     if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
17638                         Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
17639                                 ++currentVal)) != JIM_OK) {
17640                         goto evalnext;
17641                     }
17642                 }
17643             }
17644         }
17645         goto out;
17646     }
17647   evalstart:
17648 #endif
17649 
17650     while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
17651 
17652         retval = Jim_EvalObj(interp, argv[4]);
17653         if (JimCheckLoopRetcode(interp, retval)) {
17654             immediate++;
17655             break;
17656         }
17657         if (retval == JIM_OK || retval == JIM_CONTINUE) {
17658 
17659 JIM_IF_OPTIM(evalnext:)
17660             retval = Jim_EvalObj(interp, argv[3]);
17661             if (retval == JIM_OK || retval == JIM_CONTINUE) {
17662 
17663 JIM_IF_OPTIM(testcond:)
17664                 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
17665             }
17666         }
17667     }
17668 JIM_IF_OPTIM(out:)
17669     if (stopVarNamePtr) {
17670         Jim_DecrRefCount(interp, stopVarNamePtr);
17671     }
17672     if (varNamePtr) {
17673         Jim_DecrRefCount(interp, varNamePtr);
17674     }
17675 
17676     if (!immediate) {
17677         if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
17678             Jim_SetEmptyResult(interp);
17679             return JIM_OK;
17680         }
17681     }
17682 
17683     return retval;
17684 }
17685 
17686 
Jim_LoopCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17687 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17688 {
17689     int retval;
17690     jim_wide i;
17691     jim_wide limit = 0;
17692     jim_wide incr = 1;
17693     Jim_Obj *bodyObjPtr;
17694 
17695     if (argc < 4 || argc > 6) {
17696         Jim_WrongNumArgs(interp, 1, argv, "var ?first? limit ?incr? body");
17697         return JIM_ERR;
17698     }
17699 
17700     retval = Jim_GetWideExpr(interp, argv[2], &i);
17701     if (argc > 4 && retval == JIM_OK) {
17702         retval = Jim_GetWideExpr(interp, argv[3], &limit);
17703     }
17704     if (argc > 5 && retval == JIM_OK) {
17705         Jim_GetWideExpr(interp, argv[4], &incr);
17706     }
17707     if (retval != JIM_OK) {
17708         return retval;
17709     }
17710     if (argc == 4) {
17711         limit = i;
17712         i = 0;
17713     }
17714     bodyObjPtr = argv[argc - 1];
17715 
17716     retval = Jim_SetVariable(interp, argv[1], Jim_NewIntObj(interp, i));
17717 
17718     while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
17719         retval = Jim_EvalObj(interp, bodyObjPtr);
17720         if (JimCheckLoopRetcode(interp, retval)) {
17721             return retval;
17722         }
17723         if (retval == JIM_OK || retval == JIM_CONTINUE) {
17724             Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17725 
17726             retval = JIM_OK;
17727 
17728 
17729             i += incr;
17730 
17731             if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
17732                 if (argv[1]->typePtr != &variableObjType) {
17733                     if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
17734                         return JIM_ERR;
17735                     }
17736                 }
17737                 JimWideValue(objPtr) = i;
17738                 Jim_InvalidateStringRep(objPtr);
17739 
17740                 if (argv[1]->typePtr != &variableObjType) {
17741                     if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
17742                         retval = JIM_ERR;
17743                         break;
17744                     }
17745                 }
17746             }
17747             else {
17748                 objPtr = Jim_NewIntObj(interp, i);
17749                 retval = Jim_SetVariable(interp, argv[1], objPtr);
17750                 if (retval != JIM_OK) {
17751                     Jim_FreeNewObj(interp, objPtr);
17752                 }
17753             }
17754         }
17755     }
17756 
17757     if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
17758         Jim_SetEmptyResult(interp);
17759         return JIM_OK;
17760     }
17761     return retval;
17762 }
17763 
17764 typedef struct {
17765     Jim_Obj *objPtr;
17766     int idx;
17767 } Jim_ListIter;
17768 
JimListIterInit(Jim_ListIter * iter,Jim_Obj * objPtr)17769 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
17770 {
17771     iter->objPtr = objPtr;
17772     iter->idx = 0;
17773 }
17774 
JimListIterNext(Jim_Interp * interp,Jim_ListIter * iter)17775 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
17776 {
17777     if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
17778         return NULL;
17779     }
17780     return iter->objPtr->internalRep.listValue.ele[iter->idx++];
17781 }
17782 
JimListIterDone(Jim_Interp * interp,Jim_ListIter * iter)17783 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
17784 {
17785     return iter->idx >= Jim_ListLength(interp, iter->objPtr);
17786 }
17787 
17788 
JimForeachMapHelper(Jim_Interp * interp,int argc,Jim_Obj * const * argv,int doMap)17789 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
17790 {
17791     int result = JIM_OK;
17792     int i, numargs;
17793     Jim_ListIter twoiters[2];
17794     Jim_ListIter *iters;
17795     Jim_Obj *script;
17796     Jim_Obj *resultObj;
17797 
17798     if (argc < 4 || argc % 2 != 0) {
17799         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
17800         return JIM_ERR;
17801     }
17802     script = argv[argc - 1];
17803     numargs = (argc - 1 - 1);
17804 
17805     if (numargs == 2) {
17806         iters = twoiters;
17807     }
17808     else {
17809         iters = Jim_Alloc(numargs * sizeof(*iters));
17810     }
17811     for (i = 0; i < numargs; i++) {
17812         JimListIterInit(&iters[i], argv[i + 1]);
17813         if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
17814             result = JIM_ERR;
17815         }
17816     }
17817     if (result != JIM_OK) {
17818         Jim_SetResultString(interp, "foreach varlist is empty", -1);
17819         goto empty_varlist;
17820     }
17821 
17822     if (doMap) {
17823         resultObj = Jim_NewListObj(interp, NULL, 0);
17824     }
17825     else {
17826         resultObj = interp->emptyObj;
17827     }
17828     Jim_IncrRefCount(resultObj);
17829 
17830     while (1) {
17831 
17832         for (i = 0; i < numargs; i += 2) {
17833             if (!JimListIterDone(interp, &iters[i + 1])) {
17834                 break;
17835             }
17836         }
17837         if (i == numargs) {
17838 
17839             break;
17840         }
17841 
17842 
17843         for (i = 0; i < numargs; i += 2) {
17844             Jim_Obj *varName;
17845 
17846 
17847             JimListIterInit(&iters[i], argv[i + 1]);
17848             while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
17849                 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
17850                 if (!valObj) {
17851 
17852                     valObj = interp->emptyObj;
17853                 }
17854 
17855                 Jim_IncrRefCount(valObj);
17856                 result = Jim_SetVariable(interp, varName, valObj);
17857                 Jim_DecrRefCount(interp, valObj);
17858                 if (result != JIM_OK) {
17859                     goto err;
17860                 }
17861             }
17862         }
17863         result = Jim_EvalObj(interp, script);
17864         if (JimCheckLoopRetcode(interp, result)) {
17865             goto err;
17866         }
17867         switch (result) {
17868             case JIM_OK:
17869                 if (doMap) {
17870                     Jim_ListAppendElement(interp, resultObj, interp->result);
17871                 }
17872                 break;
17873             case JIM_CONTINUE:
17874                 break;
17875             case JIM_BREAK:
17876                 goto out;
17877             default:
17878                 goto err;
17879         }
17880     }
17881   out:
17882     result = JIM_OK;
17883     Jim_SetResult(interp, resultObj);
17884   err:
17885     Jim_DecrRefCount(interp, resultObj);
17886   empty_varlist:
17887     if (numargs > 2) {
17888         Jim_Free(iters);
17889     }
17890     return result;
17891 }
17892 
17893 
Jim_ForeachCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17894 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17895 {
17896     return JimForeachMapHelper(interp, argc, argv, 0);
17897 }
17898 
17899 
Jim_LmapCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17900 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17901 {
17902     return JimForeachMapHelper(interp, argc, argv, 1);
17903 }
17904 
17905 
Jim_LassignCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17906 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17907 {
17908     int result = JIM_ERR;
17909     int i;
17910     Jim_ListIter iter;
17911     Jim_Obj *resultObj;
17912 
17913     if (argc < 2) {
17914         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
17915         return JIM_ERR;
17916     }
17917 
17918     JimListIterInit(&iter, argv[1]);
17919 
17920     for (i = 2; i < argc; i++) {
17921         Jim_Obj *valObj = JimListIterNext(interp, &iter);
17922         result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
17923         if (result != JIM_OK) {
17924             return result;
17925         }
17926     }
17927 
17928     resultObj = Jim_NewListObj(interp, NULL, 0);
17929     while (!JimListIterDone(interp, &iter)) {
17930         Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
17931     }
17932 
17933     Jim_SetResult(interp, resultObj);
17934 
17935     return JIM_OK;
17936 }
17937 
17938 
Jim_IfCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17939 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17940 {
17941     int boolean, retval, current = 1, falsebody = 0;
17942 
17943     if (argc >= 3) {
17944         while (1) {
17945 
17946             if (current >= argc)
17947                 goto err;
17948             if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
17949                 != JIM_OK)
17950                 return retval;
17951 
17952             if (current >= argc)
17953                 goto err;
17954             if (Jim_CompareStringImmediate(interp, argv[current], "then"))
17955                 current++;
17956 
17957             if (current >= argc)
17958                 goto err;
17959             if (boolean)
17960                 return Jim_EvalObj(interp, argv[current]);
17961 
17962             if (++current >= argc) {
17963                 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
17964                 return JIM_OK;
17965             }
17966             falsebody = current++;
17967             if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
17968 
17969                 if (current != argc - 1)
17970                     goto err;
17971                 return Jim_EvalObj(interp, argv[current]);
17972             }
17973             else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
17974                 continue;
17975 
17976             else if (falsebody != argc - 1)
17977                 goto err;
17978             return Jim_EvalObj(interp, argv[falsebody]);
17979         }
17980         return JIM_OK;
17981     }
17982   err:
17983     Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
17984     return JIM_ERR;
17985 }
17986 
17987 
Jim_CommandMatchObj(Jim_Interp * interp,Jim_Obj * commandObj,Jim_Obj * patternObj,Jim_Obj * stringObj,int flags)17988 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
17989     Jim_Obj *stringObj, int flags)
17990 {
17991     Jim_Obj *parms[5];
17992     int argc = 0;
17993     long eq;
17994     int rc;
17995 
17996     parms[argc++] = commandObj;
17997     if (flags & JIM_NOCASE) {
17998         parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
17999     }
18000     if (flags & JIM_OPT_END) {
18001         parms[argc++] = Jim_NewStringObj(interp, "--", -1);
18002     }
18003     parms[argc++] = patternObj;
18004     parms[argc++] = stringObj;
18005 
18006     rc = Jim_EvalObjVector(interp, argc, parms);
18007 
18008     if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
18009         eq = -rc;
18010     }
18011 
18012     return eq;
18013 }
18014 
18015 
Jim_SwitchCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18016 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18017 {
18018     enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
18019     int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
18020     int match_flags = 0;
18021     Jim_Obj *command = NULL, *scriptObj = NULL, *strObj;
18022     Jim_Obj **caseList;
18023 
18024     if (argc < 3) {
18025       wrongnumargs:
18026         Jim_WrongNumArgs(interp, 1, argv, "?options? string "
18027             "pattern body ... ?default body?   or   " "{pattern body ?pattern body ...?}");
18028         return JIM_ERR;
18029     }
18030     for (opt = 1; opt < argc; ++opt) {
18031         const char *option = Jim_String(argv[opt]);
18032 
18033         if (*option != '-')
18034             break;
18035         else if (strncmp(option, "--", 2) == 0) {
18036             ++opt;
18037             break;
18038         }
18039         else if (strncmp(option, "-exact", 2) == 0)
18040             matchOpt = SWITCH_EXACT;
18041         else if (strncmp(option, "-glob", 2) == 0)
18042             matchOpt = SWITCH_GLOB;
18043         else if (strncmp(option, "-regexp", 2) == 0) {
18044             matchOpt = SWITCH_RE;
18045             match_flags |= JIM_OPT_END;
18046         }
18047         else if (strncmp(option, "-command", 2) == 0) {
18048             matchOpt = SWITCH_CMD;
18049             if ((argc - opt) < 2)
18050                 goto wrongnumargs;
18051             command = argv[++opt];
18052         }
18053         else {
18054             Jim_SetResultFormatted(interp,
18055                 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
18056                 argv[opt]);
18057             return JIM_ERR;
18058         }
18059         if ((argc - opt) < 2)
18060             goto wrongnumargs;
18061     }
18062     strObj = argv[opt++];
18063     patCount = argc - opt;
18064     if (patCount == 1) {
18065         JimListGetElements(interp, argv[opt], &patCount, &caseList);
18066     }
18067     else
18068         caseList = (Jim_Obj **)&argv[opt];
18069     if (patCount == 0 || patCount % 2 != 0)
18070         goto wrongnumargs;
18071     for (i = 0; scriptObj == NULL && i < patCount; i += 2) {
18072         Jim_Obj *patObj = caseList[i];
18073 
18074         if (!Jim_CompareStringImmediate(interp, patObj, "default")
18075             || i < (patCount - 2)) {
18076             switch (matchOpt) {
18077                 case SWITCH_EXACT:
18078                     if (Jim_StringEqObj(strObj, patObj))
18079                         scriptObj = caseList[i + 1];
18080                     break;
18081                 case SWITCH_GLOB:
18082                     if (Jim_StringMatchObj(interp, patObj, strObj, 0))
18083                         scriptObj = caseList[i + 1];
18084                     break;
18085                 case SWITCH_RE:
18086                     command = Jim_NewStringObj(interp, "regexp", -1);
18087 
18088                 case SWITCH_CMD:{
18089                         int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, match_flags);
18090 
18091                         if (argc - opt == 1) {
18092                             JimListGetElements(interp, argv[opt], &patCount, &caseList);
18093                         }
18094 
18095                         if (rc < 0) {
18096                             return -rc;
18097                         }
18098                         if (rc)
18099                             scriptObj = caseList[i + 1];
18100                         break;
18101                     }
18102             }
18103         }
18104         else {
18105             scriptObj = caseList[i + 1];
18106         }
18107     }
18108     for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2)
18109         scriptObj = caseList[i + 1];
18110     if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) {
18111         Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
18112         return JIM_ERR;
18113     }
18114     Jim_SetEmptyResult(interp);
18115     if (scriptObj) {
18116         return Jim_EvalObj(interp, scriptObj);
18117     }
18118     return JIM_OK;
18119 }
18120 
18121 
Jim_ListCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18122 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18123 {
18124     Jim_Obj *listObjPtr;
18125 
18126     listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1);
18127     Jim_SetResult(interp, listObjPtr);
18128     return JIM_OK;
18129 }
18130 
18131 
Jim_LindexCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18132 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18133 {
18134     Jim_Obj *objPtr;
18135     int ret;
18136 
18137     if (argc < 2) {
18138         Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
18139         return JIM_ERR;
18140     }
18141     ret = Jim_ListIndices(interp, argv[1], argv + 2, argc - 2, &objPtr, JIM_NONE);
18142     if (ret < 0) {
18143         ret = JIM_OK;
18144         Jim_SetEmptyResult(interp);
18145     }
18146     else if (ret == JIM_OK) {
18147         Jim_SetResult(interp, objPtr);
18148     }
18149     return ret;
18150 }
18151 
18152 
Jim_LlengthCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18153 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18154 {
18155     if (argc != 2) {
18156         Jim_WrongNumArgs(interp, 1, argv, "list");
18157         return JIM_ERR;
18158     }
18159     Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
18160     return JIM_OK;
18161 }
18162 
18163 
Jim_LsearchCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18164 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18165 {
18166     static const char * const options[] = {
18167         "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
18168         "-stride", "-index", NULL
18169     };
18170     enum
18171     { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
18172             OPT_COMMAND, OPT_STRIDE, OPT_INDEX };
18173     int i;
18174     int opt_bool = 0;
18175     int opt_not = 0;
18176     int opt_all = 0;
18177     int opt_inline = 0;
18178     int opt_match = OPT_EXACT;
18179     int listlen;
18180     int rc = JIM_OK;
18181     Jim_Obj *listObjPtr = NULL;
18182     Jim_Obj *commandObj = NULL;
18183     Jim_Obj *indexObj = NULL;
18184     int match_flags = 0;
18185     long stride = 1;
18186 
18187     if (argc < 3) {
18188       wrongargs:
18189         Jim_WrongNumArgs(interp, 1, argv,
18190             "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? ?-stride len? ?-index val? list value");
18191         return JIM_ERR;
18192     }
18193 
18194     for (i = 1; i < argc - 2; i++) {
18195         int option;
18196 
18197         if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
18198             return JIM_ERR;
18199         }
18200         switch (option) {
18201             case OPT_BOOL:
18202                 opt_bool = 1;
18203                 opt_inline = 0;
18204                 break;
18205             case OPT_NOT:
18206                 opt_not = 1;
18207                 break;
18208             case OPT_NOCASE:
18209                 match_flags |= JIM_NOCASE;
18210                 break;
18211             case OPT_INLINE:
18212                 opt_inline = 1;
18213                 opt_bool = 0;
18214                 break;
18215             case OPT_ALL:
18216                 opt_all = 1;
18217                 break;
18218             case OPT_REGEXP:
18219                 opt_match = option;
18220                 match_flags |= JIM_OPT_END;
18221                 break;
18222             case OPT_COMMAND:
18223                 if (i >= argc - 2) {
18224                     goto wrongargs;
18225                 }
18226                 commandObj = argv[++i];
18227 
18228             case OPT_EXACT:
18229             case OPT_GLOB:
18230                 opt_match = option;
18231                 break;
18232             case OPT_INDEX:
18233                 if (i >= argc - 2) {
18234                     goto wrongargs;
18235                 }
18236                 indexObj = argv[++i];
18237                 break;
18238             case OPT_STRIDE:
18239                 if (i >= argc - 2) {
18240                     goto wrongargs;
18241                 }
18242                 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
18243                     return JIM_ERR;
18244                 }
18245                 if (stride < 1) {
18246                     Jim_SetResultString(interp, "stride length must be at least 1", -1);
18247                     return JIM_ERR;
18248                 }
18249                 break;
18250         }
18251     }
18252 
18253     argc -= i;
18254     if (argc < 2) {
18255         goto wrongargs;
18256     }
18257     argv += i;
18258 
18259     listlen = Jim_ListLength(interp, argv[0]);
18260     if (listlen % stride) {
18261         Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
18262         return JIM_ERR;
18263     }
18264 
18265     if (opt_all) {
18266         listObjPtr = Jim_NewListObj(interp, NULL, 0);
18267     }
18268     if (opt_match == OPT_REGEXP) {
18269         commandObj = Jim_NewStringObj(interp, "regexp", -1);
18270     }
18271     if (commandObj) {
18272         Jim_IncrRefCount(commandObj);
18273     }
18274 
18275     for (i = 0; i < listlen; i += stride) {
18276         int eq = 0;
18277         Jim_Obj *searchListObj;
18278         Jim_Obj *objPtr;
18279         int offset;
18280 
18281         if (indexObj) {
18282             int indexlen = Jim_ListLength(interp, indexObj);
18283             if (stride == 1) {
18284                 searchListObj = Jim_ListGetIndex(interp, argv[0], i);
18285             }
18286             else {
18287                 searchListObj = Jim_NewListObj(interp, argv[0]->internalRep.listValue.ele + i, stride);
18288             }
18289             Jim_IncrRefCount(searchListObj);
18290             rc = Jim_ListIndices(interp, searchListObj, indexObj->internalRep.listValue.ele, indexlen, &objPtr, JIM_ERRMSG);
18291             if (rc != JIM_OK) {
18292                 Jim_DecrRefCount(interp, searchListObj);
18293                 rc = JIM_ERR;
18294                 goto done;
18295             }
18296 
18297             offset = 0;
18298         }
18299         else {
18300 
18301             searchListObj = argv[0];
18302             offset = i;
18303             objPtr = Jim_ListGetIndex(interp, searchListObj, i);
18304             Jim_IncrRefCount(searchListObj);
18305         }
18306 
18307         switch (opt_match) {
18308             case OPT_EXACT:
18309                 eq = Jim_StringCompareObj(interp, argv[1], objPtr, match_flags) == 0;
18310                 break;
18311 
18312             case OPT_GLOB:
18313                 eq = Jim_StringMatchObj(interp, argv[1], objPtr, match_flags);
18314                 break;
18315 
18316             case OPT_REGEXP:
18317             case OPT_COMMAND:
18318                 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, match_flags);
18319                 if (eq < 0) {
18320                     Jim_DecrRefCount(interp, searchListObj);
18321                     rc = JIM_ERR;
18322                     goto done;
18323                 }
18324                 break;
18325         }
18326 
18327 
18328         if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
18329             Jim_Obj *resultObj;
18330 
18331             if (opt_bool) {
18332                 resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
18333             }
18334             else if (!opt_inline) {
18335                 resultObj = Jim_NewIntObj(interp, i);
18336             }
18337             else if (stride == 1) {
18338                 resultObj = objPtr;
18339             }
18340             else if (opt_all) {
18341 
18342                 ListInsertElements(listObjPtr, -1, stride,
18343                     searchListObj->internalRep.listValue.ele + offset);
18344 
18345                 resultObj = NULL;
18346             }
18347             else {
18348                 resultObj = Jim_NewListObj(interp, searchListObj->internalRep.listValue.ele + offset, stride);
18349             }
18350 
18351             if (opt_all) {
18352 
18353                 if (stride == 1) {
18354                     Jim_ListAppendElement(interp, listObjPtr, resultObj);
18355                 }
18356             }
18357             else {
18358                 Jim_SetResult(interp, resultObj);
18359                 Jim_DecrRefCount(interp, searchListObj);
18360                 goto done;
18361             }
18362         }
18363         Jim_DecrRefCount(interp, searchListObj);
18364     }
18365 
18366     if (opt_all) {
18367         Jim_SetResult(interp, listObjPtr);
18368         listObjPtr = NULL;
18369     }
18370     else {
18371 
18372         if (opt_bool) {
18373             Jim_SetResultBool(interp, opt_not);
18374         }
18375         else if (!opt_inline) {
18376             Jim_SetResultInt(interp, -1);
18377         }
18378     }
18379 
18380   done:
18381     if (listObjPtr) {
18382         Jim_FreeNewObj(interp, listObjPtr);
18383     }
18384     if (commandObj) {
18385         Jim_DecrRefCount(interp, commandObj);
18386     }
18387     return rc;
18388 }
18389 
18390 
Jim_LappendCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18391 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18392 {
18393     Jim_Obj *listObjPtr;
18394     int new_obj = 0;
18395     int i;
18396 
18397     if (argc < 2) {
18398         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
18399         return JIM_ERR;
18400     }
18401     listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
18402     if (!listObjPtr) {
18403 
18404         listObjPtr = Jim_NewListObj(interp, NULL, 0);
18405         new_obj = 1;
18406     }
18407     else if (Jim_IsShared(listObjPtr)) {
18408         listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
18409         new_obj = 1;
18410     }
18411     for (i = 2; i < argc; i++)
18412         Jim_ListAppendElement(interp, listObjPtr, argv[i]);
18413     if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
18414         if (new_obj)
18415             Jim_FreeNewObj(interp, listObjPtr);
18416         return JIM_ERR;
18417     }
18418     Jim_SetResult(interp, listObjPtr);
18419     return JIM_OK;
18420 }
18421 
18422 
Jim_LinsertCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18423 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18424 {
18425     int idx, len;
18426     Jim_Obj *listPtr;
18427 
18428     if (argc < 3) {
18429         Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
18430         return JIM_ERR;
18431     }
18432     listPtr = argv[1];
18433     if (Jim_IsShared(listPtr))
18434         listPtr = Jim_DuplicateObj(interp, listPtr);
18435     if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
18436         goto err;
18437     len = Jim_ListLength(interp, listPtr);
18438     if (idx >= len)
18439         idx = len;
18440     else if (idx < 0)
18441         idx = len + idx + 1;
18442     Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
18443     Jim_SetResult(interp, listPtr);
18444     return JIM_OK;
18445   err:
18446     if (listPtr != argv[1]) {
18447         Jim_FreeNewObj(interp, listPtr);
18448     }
18449     return JIM_ERR;
18450 }
18451 
18452 
Jim_LreplaceCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18453 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18454 {
18455     int first, last, len, rangeLen;
18456     Jim_Obj *listObj;
18457     Jim_Obj *newListObj;
18458 
18459     if (argc < 4) {
18460         Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
18461         return JIM_ERR;
18462     }
18463     if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
18464         Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
18465         return JIM_ERR;
18466     }
18467 
18468     listObj = argv[1];
18469     len = Jim_ListLength(interp, listObj);
18470 
18471     first = JimRelToAbsIndex(len, first);
18472     last = JimRelToAbsIndex(len, last);
18473     JimRelToAbsRange(len, &first, &last, &rangeLen);
18474 
18475 
18476     if (first > len) {
18477         first = len;
18478     }
18479 
18480 
18481     newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
18482 
18483 
18484     ListInsertElements(newListObj, -1, argc - 4, argv + 4);
18485 
18486 
18487     ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
18488 
18489     Jim_SetResult(interp, newListObj);
18490     return JIM_OK;
18491 }
18492 
18493 
Jim_LsetCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18494 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18495 {
18496     if (argc < 3) {
18497         Jim_WrongNumArgs(interp, 1, argv, "listVar ?index ...? value");
18498         return JIM_ERR;
18499     }
18500     else if (argc == 3) {
18501 
18502         if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
18503             return JIM_ERR;
18504         Jim_SetResult(interp, argv[2]);
18505         return JIM_OK;
18506     }
18507     return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
18508 }
18509 
18510 
Jim_LsortCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const argv[])18511 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
18512 {
18513     static const char * const options[] = {
18514         "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique",
18515         "-stride", "-dictionary", NULL
18516     };
18517     enum {
18518         OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE,
18519         OPT_STRIDE, OPT_DICT
18520     };
18521     Jim_Obj *resObj;
18522     int i;
18523     int retCode;
18524     int shared;
18525     long stride = 1;
18526     Jim_Obj **elements;
18527     int listlen;
18528 
18529     struct lsort_info info;
18530 
18531     if (argc < 2) {
18532 wrongargs:
18533         Jim_WrongNumArgs(interp, 1, argv, "?options? list");
18534         return JIM_ERR;
18535     }
18536 
18537     info.type = JIM_LSORT_ASCII;
18538     info.order = 1;
18539     info.indexc = 0;
18540     info.unique = 0;
18541     info.command = NULL;
18542     info.interp = interp;
18543 
18544     for (i = 1; i < (argc - 1); i++) {
18545         int option;
18546 
18547         if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
18548             != JIM_OK)
18549             return JIM_ERR;
18550         switch (option) {
18551             case OPT_ASCII:
18552                 info.type = JIM_LSORT_ASCII;
18553                 break;
18554             case OPT_DICT:
18555                 info.type = JIM_LSORT_DICT;
18556                 break;
18557             case OPT_NOCASE:
18558                 info.type = JIM_LSORT_NOCASE;
18559                 break;
18560             case OPT_INTEGER:
18561                 info.type = JIM_LSORT_INTEGER;
18562                 break;
18563             case OPT_REAL:
18564                 info.type = JIM_LSORT_REAL;
18565                 break;
18566             case OPT_INCREASING:
18567                 info.order = 1;
18568                 break;
18569             case OPT_DECREASING:
18570                 info.order = -1;
18571                 break;
18572             case OPT_UNIQUE:
18573                 info.unique = 1;
18574                 break;
18575             case OPT_COMMAND:
18576                 if (i >= (argc - 2)) {
18577                     Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
18578                     return JIM_ERR;
18579                 }
18580                 info.type = JIM_LSORT_COMMAND;
18581                 info.command = argv[i + 1];
18582                 i++;
18583                 break;
18584             case OPT_STRIDE:
18585                 if (i >= argc - 2) {
18586                     goto wrongargs;
18587                 }
18588                 if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
18589                     return JIM_ERR;
18590                 }
18591                 if (stride < 2) {
18592                     Jim_SetResultString(interp, "stride length must be at least 2", -1);
18593                     return JIM_ERR;
18594                 }
18595                 break;
18596             case OPT_INDEX:
18597                 if (i >= (argc - 2)) {
18598 badindex:
18599                     Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
18600                     return JIM_ERR;
18601                 }
18602                 JimListGetElements(interp, argv[i + 1], &info.indexc, &info.indexv);
18603                 if (info.indexc == 0) {
18604                     goto badindex;
18605                 }
18606                 i++;
18607                 break;
18608         }
18609     }
18610     resObj = argv[argc - 1];
18611     JimListGetElements(interp, resObj, &listlen, &elements);
18612     if (listlen <= 1) {
18613 
18614         Jim_SetResult(interp, resObj);
18615         return JIM_OK;
18616     }
18617 
18618     if (stride > 1) {
18619         Jim_Obj *tmpListObj;
18620         int i;
18621 
18622         if (listlen % stride) {
18623             Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
18624             return JIM_ERR;
18625         }
18626 
18627         tmpListObj = Jim_NewListObj(interp, NULL, 0);
18628         Jim_IncrRefCount(tmpListObj);
18629         for (i = 0; i < listlen; i += stride) {
18630             Jim_ListAppendElement(interp, tmpListObj, Jim_NewListObj(interp, elements + i, stride));
18631         }
18632         retCode = ListSortElements(interp, tmpListObj, &info);
18633         if (retCode == JIM_OK) {
18634             resObj = Jim_NewListObj(interp, NULL, 0);
18635 
18636             for (i = 0; i < listlen; i += stride) {
18637                 Jim_ListAppendList(interp, resObj, Jim_ListGetIndex(interp, tmpListObj, i / stride));
18638             }
18639             Jim_SetResult(interp, resObj);
18640         }
18641         Jim_DecrRefCount(interp, tmpListObj);
18642     }
18643     else {
18644         if ((shared = Jim_IsShared(resObj))) {
18645             resObj = Jim_DuplicateObj(interp, resObj);
18646         }
18647         retCode = ListSortElements(interp, resObj, &info);
18648         if (retCode == JIM_OK) {
18649             Jim_SetResult(interp, resObj);
18650         }
18651         else if (shared) {
18652             Jim_FreeNewObj(interp, resObj);
18653         }
18654     }
18655     return retCode;
18656 }
18657 
18658 
Jim_AppendCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18659 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18660 {
18661     Jim_Obj *stringObjPtr;
18662     int i;
18663 
18664     if (argc < 2) {
18665         Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
18666         return JIM_ERR;
18667     }
18668     if (argc == 2) {
18669         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
18670         if (!stringObjPtr)
18671             return JIM_ERR;
18672     }
18673     else {
18674         int new_obj = 0;
18675         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
18676         if (!stringObjPtr) {
18677 
18678             stringObjPtr = Jim_NewEmptyStringObj(interp);
18679             new_obj = 1;
18680         }
18681         else if (Jim_IsShared(stringObjPtr)) {
18682             new_obj = 1;
18683             stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
18684         }
18685         for (i = 2; i < argc; i++) {
18686             Jim_AppendObj(interp, stringObjPtr, argv[i]);
18687         }
18688         if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
18689             if (new_obj) {
18690                 Jim_FreeNewObj(interp, stringObjPtr);
18691             }
18692             return JIM_ERR;
18693         }
18694     }
18695     Jim_SetResult(interp, stringObjPtr);
18696     return JIM_OK;
18697 }
18698 
18699 
18700 
18701 
18702 
Jim_EvalCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18703 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18704 {
18705     int rc;
18706 
18707     if (argc < 2) {
18708         Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
18709         return JIM_ERR;
18710     }
18711 
18712     if (argc == 2) {
18713         rc = Jim_EvalObj(interp, argv[1]);
18714     }
18715     else {
18716         rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
18717     }
18718 
18719     return rc;
18720 }
18721 
18722 
Jim_UplevelCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18723 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18724 {
18725     if (argc >= 2) {
18726         int retcode;
18727         Jim_CallFrame *savedCallFrame, *targetCallFrame;
18728         const char *str;
18729 
18730 
18731         savedCallFrame = interp->framePtr;
18732 
18733 
18734         str = Jim_String(argv[1]);
18735         if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
18736             targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
18737             argc--;
18738             argv++;
18739         }
18740         else {
18741             targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
18742         }
18743         if (targetCallFrame == NULL) {
18744             return JIM_ERR;
18745         }
18746         if (argc < 2) {
18747             Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
18748             return JIM_ERR;
18749         }
18750 
18751         interp->framePtr = targetCallFrame;
18752         if (argc == 2) {
18753             retcode = Jim_EvalObj(interp, argv[1]);
18754         }
18755         else {
18756             retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
18757         }
18758         interp->framePtr = savedCallFrame;
18759         return retcode;
18760     }
18761     else {
18762         Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
18763         return JIM_ERR;
18764     }
18765 }
18766 
18767 
Jim_ExprCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18768 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18769 {
18770     int retcode;
18771 
18772     if (argc == 2) {
18773         retcode = Jim_EvalExpression(interp, argv[1]);
18774     }
18775 #ifndef JIM_COMPAT
18776     else {
18777         Jim_WrongNumArgs(interp, 1, argv, "expression");
18778         retcode = JIM_ERR;
18779     }
18780 #else
18781     else if (argc > 2) {
18782         Jim_Obj *objPtr;
18783 
18784         objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
18785         Jim_IncrRefCount(objPtr);
18786         retcode = Jim_EvalExpression(interp, objPtr);
18787         Jim_DecrRefCount(interp, objPtr);
18788     }
18789     else {
18790         Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
18791         return JIM_ERR;
18792     }
18793 #endif
18794     return retcode;
18795 }
18796 
JimBreakContinueHelper(Jim_Interp * interp,int argc,Jim_Obj * const * argv,int retcode)18797 static int JimBreakContinueHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int retcode)
18798 {
18799     if (argc != 1 && argc != 2) {
18800         Jim_WrongNumArgs(interp, 1, argv, "?level?");
18801         return JIM_ERR;
18802     }
18803     if (argc == 2) {
18804         long level;
18805         int ret = Jim_GetLong(interp, argv[1], &level);
18806         if (ret != JIM_OK) {
18807             return ret;
18808         }
18809         interp->break_level = level;
18810     }
18811     return retcode;
18812 }
18813 
18814 
Jim_BreakCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18815 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18816 {
18817     return JimBreakContinueHelper(interp, argc, argv, JIM_BREAK);
18818 }
18819 
18820 
Jim_ContinueCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18821 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18822 {
18823     return JimBreakContinueHelper(interp, argc, argv, JIM_CONTINUE);
18824 }
18825 
18826 
Jim_StacktraceCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18827 static int Jim_StacktraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18828 {
18829     Jim_Obj *listObj;
18830     int i;
18831     jim_wide skip = 0;
18832     jim_wide last = 0;
18833 
18834     if (argc > 1) {
18835         if (Jim_GetWideExpr(interp, argv[1], &skip) != JIM_OK) {
18836             return JIM_ERR;
18837         }
18838     }
18839     if (argc > 2) {
18840         if (Jim_GetWideExpr(interp, argv[2], &last) != JIM_OK) {
18841             return JIM_ERR;
18842         }
18843     }
18844 
18845     listObj = Jim_NewListObj(interp, NULL, 0);
18846     for (i = skip; i <= interp->procLevel; i++) {
18847         Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
18848         if (frame->procLevel < last) {
18849             break;
18850         }
18851         JimAddStackFrame(interp, frame, listObj);
18852     }
18853     Jim_SetResult(interp, listObj);
18854     return JIM_OK;
18855 }
18856 
18857 
Jim_ReturnCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18858 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18859 {
18860     int i;
18861     Jim_Obj *stackTraceObj = NULL;
18862     Jim_Obj *errorCodeObj = NULL;
18863     int returnCode = JIM_OK;
18864     long level = 1;
18865 
18866     for (i = 1; i < argc - 1; i += 2) {
18867         if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
18868             if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
18869                 return JIM_ERR;
18870             }
18871         }
18872         else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
18873             stackTraceObj = argv[i + 1];
18874         }
18875         else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
18876             errorCodeObj = argv[i + 1];
18877         }
18878         else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
18879             if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
18880                 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
18881                 return JIM_ERR;
18882             }
18883         }
18884         else {
18885             break;
18886         }
18887     }
18888 
18889     if (i != argc - 1 && i != argc) {
18890         Jim_WrongNumArgs(interp, 1, argv,
18891             "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
18892     }
18893 
18894 
18895     if (stackTraceObj && returnCode == JIM_ERR) {
18896         JimSetStackTrace(interp, stackTraceObj);
18897     }
18898 
18899     if (errorCodeObj && returnCode == JIM_ERR) {
18900         Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
18901     }
18902     interp->returnCode = returnCode;
18903     interp->returnLevel = level;
18904 
18905     if (i == argc - 1) {
18906         Jim_SetResult(interp, argv[i]);
18907     }
18908     return level == 0 ? returnCode : JIM_RETURN;
18909 }
18910 
18911 
Jim_TailcallCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18912 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18913 {
18914     if (interp->framePtr->level == 0) {
18915         Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
18916         return JIM_ERR;
18917     }
18918     else if (argc >= 2) {
18919 
18920         Jim_CallFrame *cf = interp->framePtr->parent;
18921 
18922         Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
18923         if (cmdPtr == NULL) {
18924             return JIM_ERR;
18925         }
18926 
18927         JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));
18928 
18929 
18930         JimIncrCmdRefCount(cmdPtr);
18931         cf->tailcallCmd = cmdPtr;
18932 
18933 
18934         JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));
18935 
18936         cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
18937         Jim_IncrRefCount(cf->tailcallObj);
18938 
18939 
18940         return JIM_EVAL;
18941     }
18942     return JIM_OK;
18943 }
18944 
JimAliasCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18945 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18946 {
18947     Jim_Obj *cmdList;
18948     Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
18949 
18950 
18951     cmdList = Jim_DuplicateObj(interp, prefixListObj);
18952     Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
18953 
18954     return JimEvalObjList(interp, cmdList);
18955 }
18956 
JimAliasCmdDelete(Jim_Interp * interp,void * privData)18957 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
18958 {
18959     Jim_Obj *prefixListObj = privData;
18960     Jim_DecrRefCount(interp, prefixListObj);
18961 }
18962 
Jim_AliasCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18963 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18964 {
18965     Jim_Obj *prefixListObj;
18966 
18967     if (argc < 3) {
18968         Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
18969         return JIM_ERR;
18970     }
18971 
18972     prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
18973     Jim_IncrRefCount(prefixListObj);
18974     Jim_SetResult(interp, argv[1]);
18975 
18976     return Jim_CreateCommandObj(interp, argv[1], JimAliasCmd, prefixListObj, JimAliasCmdDelete);
18977 }
18978 
18979 
Jim_ProcCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18980 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18981 {
18982     Jim_Cmd *cmd;
18983 
18984     if (argc != 4 && argc != 5) {
18985         Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
18986         return JIM_ERR;
18987     }
18988 
18989     if (argc == 4) {
18990         cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
18991     }
18992     else {
18993         cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
18994     }
18995 
18996     if (cmd) {
18997 
18998         Jim_Obj *nameObjPtr = JimQualifyName(interp, argv[1]);
18999         JimCreateCommand(interp, nameObjPtr, cmd);
19000 
19001 
19002         JimUpdateProcNamespace(interp, cmd, nameObjPtr);
19003         Jim_DecrRefCount(interp, nameObjPtr);
19004 
19005 
19006         Jim_SetResult(interp, argv[1]);
19007         return JIM_OK;
19008     }
19009     return JIM_ERR;
19010 }
19011 
19012 
Jim_XtraceCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19013 static int Jim_XtraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19014 {
19015     if (argc != 2) {
19016         Jim_WrongNumArgs(interp, 1, argv, "callback");
19017         return JIM_ERR;
19018     }
19019 
19020     if (interp->traceCmdObj) {
19021         Jim_DecrRefCount(interp, interp->traceCmdObj);
19022         interp->traceCmdObj = NULL;
19023     }
19024 
19025     if (Jim_Length(argv[1])) {
19026 
19027         interp->traceCmdObj = argv[1];
19028         Jim_IncrRefCount(interp->traceCmdObj);
19029     }
19030     return JIM_OK;
19031 }
19032 
19033 
Jim_LocalCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19034 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19035 {
19036     int retcode;
19037 
19038     if (argc < 2) {
19039         Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
19040         return JIM_ERR;
19041     }
19042 
19043 
19044     interp->local++;
19045     retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
19046     interp->local--;
19047 
19048 
19049 
19050     if (retcode == 0) {
19051         Jim_Obj *cmdNameObj = Jim_GetResult(interp);
19052 
19053         if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
19054             return JIM_ERR;
19055         }
19056         if (interp->framePtr->localCommands == NULL) {
19057             interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
19058             Jim_InitStack(interp->framePtr->localCommands);
19059         }
19060         Jim_IncrRefCount(cmdNameObj);
19061         Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
19062     }
19063 
19064     return retcode;
19065 }
19066 
19067 
Jim_UpcallCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19068 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19069 {
19070     if (argc < 2) {
19071         Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
19072         return JIM_ERR;
19073     }
19074     else {
19075         int retcode;
19076 
19077         Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
19078         if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
19079             Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
19080             return JIM_ERR;
19081         }
19082 
19083         cmdPtr->u.proc.upcall++;
19084         JimIncrCmdRefCount(cmdPtr);
19085 
19086 
19087         retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
19088 
19089 
19090         cmdPtr->u.proc.upcall--;
19091         JimDecrCmdRefCount(interp, cmdPtr);
19092 
19093         return retcode;
19094     }
19095 }
19096 
19097 
Jim_ApplyCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19098 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19099 {
19100     if (argc < 2) {
19101         Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
19102         return JIM_ERR;
19103     }
19104     else {
19105         int ret;
19106         Jim_Cmd *cmd;
19107         Jim_Obj *argListObjPtr;
19108         Jim_Obj *bodyObjPtr;
19109         Jim_Obj *nsObj = NULL;
19110         Jim_Obj **nargv;
19111 
19112         int len = Jim_ListLength(interp, argv[1]);
19113         if (len != 2 && len != 3) {
19114             Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
19115             return JIM_ERR;
19116         }
19117 
19118         if (len == 3) {
19119 #ifdef jim_ext_namespace
19120 
19121             nsObj = Jim_ListGetIndex(interp, argv[1], 2);
19122 #else
19123             Jim_SetResultString(interp, "namespaces not enabled", -1);
19124             return JIM_ERR;
19125 #endif
19126         }
19127         argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
19128         bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
19129 
19130         cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
19131 
19132         if (cmd) {
19133 
19134             nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
19135             nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
19136             Jim_IncrRefCount(nargv[0]);
19137             memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
19138             ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
19139             Jim_DecrRefCount(interp, nargv[0]);
19140             Jim_Free(nargv);
19141 
19142             JimDecrCmdRefCount(interp, cmd);
19143             return ret;
19144         }
19145         return JIM_ERR;
19146     }
19147 }
19148 
19149 
19150 
Jim_ConcatCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19151 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19152 {
19153     Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
19154     return JIM_OK;
19155 }
19156 
19157 
Jim_UpvarCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19158 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19159 {
19160     int i;
19161     Jim_CallFrame *targetCallFrame;
19162 
19163 
19164     if (argc > 3 && (argc % 2 == 0)) {
19165         targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
19166         argc--;
19167         argv++;
19168     }
19169     else {
19170         targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
19171     }
19172     if (targetCallFrame == NULL) {
19173         return JIM_ERR;
19174     }
19175 
19176 
19177     if (argc < 3) {
19178         Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
19179         return JIM_ERR;
19180     }
19181 
19182 
19183     for (i = 1; i < argc; i += 2) {
19184         if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
19185             return JIM_ERR;
19186     }
19187     return JIM_OK;
19188 }
19189 
19190 
Jim_GlobalCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19191 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19192 {
19193     int i;
19194 
19195     if (argc < 2) {
19196         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
19197         return JIM_ERR;
19198     }
19199 
19200     if (interp->framePtr->level == 0)
19201         return JIM_OK;
19202     for (i = 1; i < argc; i++) {
19203 
19204         const char *name = Jim_String(argv[i]);
19205         if (name[0] != ':' || name[1] != ':') {
19206             if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
19207                 return JIM_ERR;
19208         }
19209     }
19210     return JIM_OK;
19211 }
19212 
JimStringMap(Jim_Interp * interp,Jim_Obj * mapListObjPtr,Jim_Obj * objPtr,int nocase)19213 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
19214     Jim_Obj *objPtr, int nocase)
19215 {
19216     int numMaps;
19217     const char *str, *noMatchStart = NULL;
19218     int strLen, i;
19219     Jim_Obj *resultObjPtr;
19220 
19221     numMaps = Jim_ListLength(interp, mapListObjPtr);
19222     if (numMaps % 2) {
19223         Jim_SetResultString(interp, "list must contain an even number of elements", -1);
19224         return NULL;
19225     }
19226 
19227     str = Jim_String(objPtr);
19228     strLen = Jim_Utf8Length(interp, objPtr);
19229 
19230 
19231     resultObjPtr = Jim_NewStringObj(interp, "", 0);
19232     while (strLen) {
19233         for (i = 0; i < numMaps; i += 2) {
19234             Jim_Obj *eachObjPtr;
19235             const char *k;
19236             int kl;
19237 
19238             eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
19239             k = Jim_String(eachObjPtr);
19240             kl = Jim_Utf8Length(interp, eachObjPtr);
19241 
19242             if (strLen >= kl && kl) {
19243                 int rc;
19244                 rc = JimStringCompareUtf8(str, kl, k, kl, nocase);
19245                 if (rc == 0) {
19246                     if (noMatchStart) {
19247                         Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
19248                         noMatchStart = NULL;
19249                     }
19250                     Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
19251                     str += utf8_index(str, kl);
19252                     strLen -= kl;
19253                     break;
19254                 }
19255             }
19256         }
19257         if (i == numMaps) {
19258             int c;
19259             if (noMatchStart == NULL)
19260                 noMatchStart = str;
19261             str += utf8_tounicode(str, &c);
19262             strLen--;
19263         }
19264     }
19265     if (noMatchStart) {
19266         Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
19267     }
19268     return resultObjPtr;
19269 }
19270 
19271 
Jim_StringCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19272 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19273 {
19274     int len;
19275     int opt_case = 1;
19276     int option;
19277     static const char * const nocase_options[] = {
19278         "-nocase", NULL
19279     };
19280     static const char * const nocase_length_options[] = {
19281         "-nocase", "-length", NULL
19282     };
19283 
19284     enum {
19285         OPT_BYTELENGTH,
19286         OPT_BYTERANGE,
19287         OPT_CAT,
19288         OPT_COMPARE,
19289         OPT_EQUAL,
19290         OPT_FIRST,
19291         OPT_INDEX,
19292         OPT_IS,
19293         OPT_LAST,
19294         OPT_LENGTH,
19295         OPT_MAP,
19296         OPT_MATCH,
19297         OPT_RANGE,
19298         OPT_REPEAT,
19299         OPT_REPLACE,
19300         OPT_REVERSE,
19301         OPT_TOLOWER,
19302         OPT_TOTITLE,
19303         OPT_TOUPPER,
19304         OPT_TRIM,
19305         OPT_TRIMLEFT,
19306         OPT_TRIMRIGHT,
19307         OPT_COUNT
19308     };
19309     static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
19310         JIM_DEF_SUBCMD("bytelength", "string", 1, 1),
19311         JIM_DEF_SUBCMD("byterange", "string first last", 3, 3),
19312         JIM_DEF_SUBCMD("cat", "?...?", 0, -1),
19313         JIM_DEF_SUBCMD("compare", "?-nocase? ?-length int? string1 string2", 2, 5),
19314         JIM_DEF_SUBCMD("equal", "?-nocase? ?-length int? string1 string2", 2, 5),
19315         JIM_DEF_SUBCMD("first", "subString string ?index?", 2, 3),
19316         JIM_DEF_SUBCMD("index", "string index", 2, 2),
19317         JIM_DEF_SUBCMD("is", "class ?-strict? str", 2, 3),
19318         JIM_DEF_SUBCMD("last", "subString string ?index?", 2, 3),
19319         JIM_DEF_SUBCMD("length","string", 1, 1),
19320         JIM_DEF_SUBCMD("map", "?-nocase? mapList string", 2, 3),
19321         JIM_DEF_SUBCMD("match", "?-nocase? pattern string", 2, 3),
19322         JIM_DEF_SUBCMD("range", "string first last", 3, 3),
19323         JIM_DEF_SUBCMD("repeat", "string count", 2, 2),
19324         JIM_DEF_SUBCMD("replace", "string first last ?string?", 3, 4),
19325         JIM_DEF_SUBCMD("reverse", "string", 1, 1),
19326         JIM_DEF_SUBCMD("tolower", "string", 1, 1),
19327         JIM_DEF_SUBCMD("totitle", "string", 1, 1),
19328         JIM_DEF_SUBCMD("toupper", "string", 1, 1),
19329         JIM_DEF_SUBCMD("trim", "string ?trimchars?", 1, 2),
19330         JIM_DEF_SUBCMD("trimleft", "string ?trimchars?", 1, 2),
19331         JIM_DEF_SUBCMD("trimright", "string ?trimchars?", 1, 2),
19332         { NULL }
19333     };
19334     const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
19335     if (!ct) {
19336         return JIM_ERR;
19337     }
19338     if (ct->function) {
19339 
19340         return ct->function(interp, argc, argv);
19341     }
19342 
19343     option = ct - cmds;
19344 
19345     switch (option) {
19346         case OPT_LENGTH:
19347             Jim_SetResultInt(interp, Jim_Utf8Length(interp, argv[2]));
19348             return JIM_OK;
19349 
19350         case OPT_BYTELENGTH:
19351             Jim_SetResultInt(interp, Jim_Length(argv[2]));
19352             return JIM_OK;
19353 
19354         case OPT_CAT:{
19355                 Jim_Obj *objPtr;
19356                 if (argc == 3) {
19357 
19358                     objPtr = argv[2];
19359                 }
19360                 else {
19361                     int i;
19362 
19363                     objPtr = Jim_NewStringObj(interp, "", 0);
19364 
19365                     for (i = 2; i < argc; i++) {
19366                         Jim_AppendObj(interp, objPtr, argv[i]);
19367                     }
19368                 }
19369                 Jim_SetResult(interp, objPtr);
19370                 return JIM_OK;
19371             }
19372 
19373         case OPT_COMPARE:
19374         case OPT_EQUAL:
19375             {
19376 
19377                 long opt_length = -1;
19378                 int n = argc - 4;
19379                 int i = 2;
19380                 while (n > 0) {
19381                     int subopt;
19382                     if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
19383                             JIM_ENUM_ABBREV) != JIM_OK) {
19384 badcompareargs:
19385                         Jim_SubCmdArgError(interp, ct, argv[0]);
19386                         return JIM_ERR;
19387                     }
19388                     if (subopt == 0) {
19389 
19390                         opt_case = 0;
19391                         n--;
19392                     }
19393                     else {
19394 
19395                         if (n < 2) {
19396                             goto badcompareargs;
19397                         }
19398                         if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
19399                             return JIM_ERR;
19400                         }
19401                         n -= 2;
19402                     }
19403                 }
19404                 if (n) {
19405                     goto badcompareargs;
19406                 }
19407                 argv += argc - 2;
19408                 if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
19409 
19410                     Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
19411                 }
19412                 else {
19413                     const char *s1 = Jim_String(argv[0]);
19414                     int l1 = Jim_Utf8Length(interp, argv[0]);
19415                     const char *s2 = Jim_String(argv[1]);
19416                     int l2 = Jim_Utf8Length(interp, argv[1]);
19417                     if (opt_length >= 0) {
19418                         if (l1 > opt_length) {
19419                             l1 = opt_length;
19420                         }
19421                         if (l2 > opt_length) {
19422                             l2 = opt_length;
19423                         }
19424                     }
19425                     n = JimStringCompareUtf8(s1, l1, s2, l2, !opt_case);
19426                     Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
19427                 }
19428                 return JIM_OK;
19429             }
19430 
19431         case OPT_MATCH:
19432             if (argc != 4 &&
19433                 (argc != 5 ||
19434                     Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
19435                         JIM_ENUM_ABBREV) != JIM_OK)) {
19436                 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
19437                 return JIM_ERR;
19438             }
19439             if (opt_case == 0) {
19440                 argv++;
19441             }
19442             Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
19443             return JIM_OK;
19444 
19445         case OPT_MAP:{
19446                 Jim_Obj *objPtr;
19447 
19448                 if (argc != 4 &&
19449                     (argc != 5 ||
19450                         Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
19451                             JIM_ENUM_ABBREV) != JIM_OK)) {
19452                     Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
19453                     return JIM_ERR;
19454                 }
19455 
19456                 if (opt_case == 0) {
19457                     argv++;
19458                 }
19459                 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
19460                 if (objPtr == NULL) {
19461                     return JIM_ERR;
19462                 }
19463                 Jim_SetResult(interp, objPtr);
19464                 return JIM_OK;
19465             }
19466 
19467         case OPT_RANGE:{
19468                 Jim_Obj *objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
19469                 if (objPtr == NULL) {
19470                     return JIM_ERR;
19471                 }
19472                 Jim_SetResult(interp, objPtr);
19473                 return JIM_OK;
19474             }
19475 
19476         case OPT_BYTERANGE:{
19477                 Jim_Obj *objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
19478                 if (objPtr == NULL) {
19479                     return JIM_ERR;
19480                 }
19481                 Jim_SetResult(interp, objPtr);
19482                 return JIM_OK;
19483             }
19484 
19485         case OPT_REPLACE:{
19486                 Jim_Obj *objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
19487                 if (objPtr == NULL) {
19488                     return JIM_ERR;
19489                 }
19490                 Jim_SetResult(interp, objPtr);
19491                 return JIM_OK;
19492             }
19493 
19494 
19495         case OPT_REPEAT:{
19496                 Jim_Obj *objPtr;
19497                 jim_wide count;
19498 
19499                 if (Jim_GetWideExpr(interp, argv[3], &count) != JIM_OK) {
19500                     return JIM_ERR;
19501                 }
19502                 objPtr = Jim_NewStringObj(interp, "", 0);
19503                 if (count > 0) {
19504                     while (count--) {
19505                         Jim_AppendObj(interp, objPtr, argv[2]);
19506                     }
19507                 }
19508                 Jim_SetResult(interp, objPtr);
19509                 return JIM_OK;
19510             }
19511 
19512         case OPT_REVERSE:{
19513                 char *buf, *p;
19514                 const char *str;
19515                 int i;
19516 
19517                 str = Jim_GetString(argv[2], &len);
19518                 buf = Jim_Alloc(len + 1);
19519                 assert(buf);
19520                 p = buf + len;
19521                 *p = 0;
19522                 for (i = 0; i < len; ) {
19523                     int c;
19524                     int l = utf8_tounicode(str, &c);
19525                     memcpy(p - l, str, l);
19526                     p -= l;
19527                     i += l;
19528                     str += l;
19529                 }
19530                 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
19531                 return JIM_OK;
19532             }
19533 
19534         case OPT_INDEX:{
19535                 int idx;
19536                 const char *str;
19537 
19538                 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
19539                     return JIM_ERR;
19540                 }
19541                 str = Jim_String(argv[2]);
19542                 len = Jim_Utf8Length(interp, argv[2]);
19543                 idx = JimRelToAbsIndex(len, idx);
19544                 if (idx < 0 || idx >= len || str == NULL) {
19545                     Jim_SetResultString(interp, "", 0);
19546                 }
19547                 else if (len == Jim_Length(argv[2])) {
19548 
19549                     Jim_SetResultString(interp, str + idx, 1);
19550                 }
19551                 else {
19552                     int c;
19553                     int i = utf8_index(str, idx);
19554                     Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
19555                 }
19556                 return JIM_OK;
19557             }
19558 
19559         case OPT_FIRST:
19560         case OPT_LAST:{
19561                 int idx = 0, l1, l2;
19562                 const char *s1, *s2;
19563 
19564                 s1 = Jim_String(argv[2]);
19565                 s2 = Jim_String(argv[3]);
19566                 l1 = Jim_Utf8Length(interp, argv[2]);
19567                 l2 = Jim_Utf8Length(interp, argv[3]);
19568                 if (argc == 5) {
19569                     if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
19570                         return JIM_ERR;
19571                     }
19572                     idx = JimRelToAbsIndex(l2, idx);
19573                     if (idx < 0) {
19574                         idx = 0;
19575                     }
19576                 }
19577                 else if (option == OPT_LAST) {
19578                     idx = l2;
19579                 }
19580                 if (option == OPT_FIRST) {
19581                     Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
19582                 }
19583                 else {
19584 #ifdef JIM_UTF8
19585                     Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
19586 #else
19587                     Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
19588 #endif
19589                 }
19590                 return JIM_OK;
19591             }
19592 
19593         case OPT_TRIM:
19594                 Jim_SetResult(interp, JimStringTrim(interp, argv[2], argc == 4 ? argv[3] : NULL));
19595                 return JIM_OK;
19596         case OPT_TRIMLEFT:
19597                 Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], argc == 4 ? argv[3] : NULL));
19598                 return JIM_OK;
19599         case OPT_TRIMRIGHT:{
19600                 Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], argc == 4 ? argv[3] : NULL));
19601                 return JIM_OK;
19602         }
19603 
19604         case OPT_TOLOWER:
19605                 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
19606                 return JIM_OK;
19607         case OPT_TOUPPER:
19608                 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
19609                 return JIM_OK;
19610         case OPT_TOTITLE:
19611                 Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
19612                 return JIM_OK;
19613 
19614         case OPT_IS:
19615             if (argc == 5 && !Jim_CompareStringImmediate(interp, argv[3], "-strict")) {
19616                 Jim_SubCmdArgError(interp, ct, argv[0]);
19617                 return JIM_ERR;
19618             }
19619             return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
19620     }
19621     return JIM_OK;
19622 }
19623 
19624 
Jim_TimeCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19625 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19626 {
19627     long i, count = 1;
19628     jim_wide start, elapsed;
19629 
19630     if (argc < 2) {
19631         Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
19632         return JIM_ERR;
19633     }
19634     if (argc == 3) {
19635         if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
19636             return JIM_ERR;
19637     }
19638     if (count < 0)
19639         return JIM_OK;
19640     i = count;
19641     start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
19642     while (i-- > 0) {
19643         int retval;
19644 
19645         retval = Jim_EvalObj(interp, argv[1]);
19646         if (retval != JIM_OK) {
19647             return retval;
19648         }
19649     }
19650     elapsed = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
19651     if (elapsed < count * 10) {
19652         Jim_SetResult(interp, Jim_NewDoubleObj(interp, elapsed * 1.0 / count));
19653     }
19654     else {
19655         Jim_SetResultInt(interp, count == 0 ? 0 : elapsed / count);
19656     }
19657     Jim_AppendString(interp, Jim_GetResult(interp)," microseconds per iteration", -1);
19658     return JIM_OK;
19659 }
19660 
19661 
Jim_TimeRateCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19662 static int Jim_TimeRateCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19663 {
19664     long us = 0;
19665     jim_wide start, delta, overhead;
19666     Jim_Obj *objPtr;
19667     double us_per_iter;
19668     int count;
19669     int n;
19670 
19671     if (argc < 2) {
19672         Jim_WrongNumArgs(interp, 1, argv, "script ?milliseconds?");
19673         return JIM_ERR;
19674     }
19675     if (argc == 3) {
19676         if (Jim_GetLong(interp, argv[2], &us) != JIM_OK)
19677             return JIM_ERR;
19678         us *= 1000;
19679     }
19680     if (us < 1) {
19681 
19682         us = 1000 * 1000;
19683     }
19684 
19685 
19686     start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
19687     count = 0;
19688     do {
19689         int retval = Jim_EvalObj(interp, argv[1]);
19690         delta = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
19691         if (retval != JIM_OK) {
19692             return retval;
19693         }
19694         count++;
19695     } while (delta < us);
19696 
19697 
19698     start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
19699     n = 0;
19700     do {
19701         int retval = Jim_EvalObj(interp, interp->nullScriptObj);
19702         overhead = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
19703         if (retval != JIM_OK) {
19704             return retval;
19705         }
19706         n++;
19707     } while (n < count);
19708 
19709     delta -= overhead;
19710 
19711     us_per_iter = (double)delta / count;
19712     objPtr = Jim_NewListObj(interp, NULL, 0);
19713 
19714     Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "us_per_iter", -1));
19715     Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, us_per_iter));
19716     Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "iters_per_sec", -1));
19717     Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, 1e6 / us_per_iter));
19718     Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "count", -1));
19719     Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, count));
19720     Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "elapsed_us", -1));
19721     Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, delta));
19722     Jim_SetResult(interp, objPtr);
19723     return JIM_OK;
19724 }
19725 
19726 
Jim_ExitCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19727 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19728 {
19729     long exitCode = 0;
19730 
19731     if (argc > 2) {
19732         Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
19733         return JIM_ERR;
19734     }
19735     if (argc == 2) {
19736         if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
19737             return JIM_ERR;
19738         Jim_SetResult(interp, argv[1]);
19739     }
19740     interp->exitCode = exitCode;
19741     return JIM_EXIT;
19742 }
19743 
JimMatchReturnCodes(Jim_Interp * interp,Jim_Obj * retcodeListObj,int rc)19744 static int JimMatchReturnCodes(Jim_Interp *interp, Jim_Obj *retcodeListObj, int rc)
19745 {
19746     int len = Jim_ListLength(interp, retcodeListObj);
19747     int i;
19748     for (i = 0; i < len; i++) {
19749         int returncode;
19750         if (Jim_GetReturnCode(interp, Jim_ListGetIndex(interp, retcodeListObj, i), &returncode) != JIM_OK) {
19751             return JIM_ERR;
19752         }
19753         if (rc == returncode) {
19754             return JIM_OK;
19755         }
19756     }
19757     return -1;
19758 }
19759 
19760 
JimCatchTryHelper(Jim_Interp * interp,int istry,int argc,Jim_Obj * const * argv)19761 static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *const *argv)
19762 {
19763     static const char * const wrongargs_catchtry[2] = {
19764         "?-?no?code ... --? script ?resultVarName? ?optionVarName?",
19765         "?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?"
19766     };
19767     int exitCode = 0;
19768     int i;
19769     int sig = 0;
19770     int ok;
19771     Jim_Obj *finallyScriptObj = NULL;
19772     Jim_Obj *msgVarObj = NULL;
19773     Jim_Obj *optsVarObj = NULL;
19774     Jim_Obj *handlerScriptObj = NULL;
19775     Jim_Obj *errorCodeObj;
19776     int idx;
19777 
19778 
19779     jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
19780     static const int max_ignore_code = sizeof(ignore_mask) * 8;
19781 
19782     JimPanic((istry != 0 && istry != 1, "wrong args to JimCatchTryHelper"));
19783 
19784     Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
19785 
19786     for (i = 1; i < argc - 1; i++) {
19787         const char *arg = Jim_String(argv[i]);
19788         jim_wide option;
19789         int ignore;
19790 
19791 
19792         if (strcmp(arg, "--") == 0) {
19793             i++;
19794             break;
19795         }
19796         if (*arg != '-') {
19797             break;
19798         }
19799 
19800         if (strncmp(arg, "-no", 3) == 0) {
19801             arg += 3;
19802             ignore = 1;
19803         }
19804         else {
19805             arg++;
19806             ignore = 0;
19807         }
19808 
19809         if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
19810             option = -1;
19811         }
19812         if (option < 0) {
19813             option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
19814         }
19815         if (option < 0) {
19816             goto wrongargs;
19817         }
19818 
19819         if (ignore) {
19820             ignore_mask |= ((jim_wide)1 << option);
19821         }
19822         else {
19823             ignore_mask &= (~((jim_wide)1 << option));
19824         }
19825     }
19826 
19827     idx = i;
19828 
19829     if (argc - idx < 1) {
19830 wrongargs:
19831         Jim_WrongNumArgs(interp, 1, argv, wrongargs_catchtry[istry]);
19832         return JIM_ERR;
19833     }
19834 
19835     if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
19836         sig++;
19837     }
19838 
19839     interp->signal_level += sig;
19840     if (Jim_CheckSignal(interp)) {
19841 
19842         exitCode = JIM_SIGNAL;
19843     }
19844     else {
19845         exitCode = Jim_EvalObj(interp, argv[idx]);
19846 
19847         interp->errorFlag = 0;
19848     }
19849     interp->signal_level -= sig;
19850 
19851     errorCodeObj = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
19852 
19853     idx++;
19854     if (istry) {
19855         while (idx < argc) {
19856             int option;
19857             int ret;
19858             static const char * const try_options[] = { "on", "trap", "finally", NULL };
19859             enum { TRY_ON, TRY_TRAP, TRY_FINALLY, };
19860 
19861             if (Jim_GetEnum(interp, argv[idx], try_options, &option, "handler", JIM_ERRMSG) != JIM_OK) {
19862                 return JIM_ERR;
19863             }
19864             switch (option) {
19865                 case TRY_ON:
19866                 case TRY_TRAP:
19867                     if (idx + 4 > argc) {
19868                         goto wrongargs;
19869                     }
19870                     if (option == TRY_ON) {
19871                         ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode);
19872                         if (ret > JIM_OK) {
19873                             goto wrongargs;
19874                         }
19875                     }
19876                     else if (errorCodeObj) {
19877                         int len = Jim_ListLength(interp, argv[idx + 1]);
19878 
19879                         if (len > Jim_ListLength(interp, errorCodeObj)) {
19880 
19881                             ret = -1;
19882                         }
19883                         else {
19884                             int i;
19885                             ret = JIM_OK;
19886 
19887                             for (i = 0; i < len; i++) {
19888                                 Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i);
19889                                 Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i);
19890                                 if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) {
19891                                     ret = -1;
19892                                     break;
19893                                 }
19894                             }
19895                         }
19896                     }
19897                     else {
19898 
19899                         ret = -1;
19900                     }
19901 
19902                     if (ret == JIM_OK && handlerScriptObj == NULL) {
19903                         msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0);
19904                         optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1);
19905                         handlerScriptObj = argv[idx + 3];
19906                     }
19907                     idx += 4;
19908                     break;
19909                 case TRY_FINALLY:
19910                     if (idx + 2 != argc) {
19911                         goto wrongargs;
19912                     }
19913                     finallyScriptObj = argv[idx + 1];
19914                     idx += 2;
19915                     break;
19916             }
19917         }
19918     }
19919     else {
19920         if (argc - idx >= 1) {
19921             msgVarObj = argv[idx];
19922             idx++;
19923             if (argc - idx >= 1) {
19924                 optsVarObj = argv[idx];
19925                 idx++;
19926             }
19927         }
19928     }
19929 
19930 
19931     if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
19932 
19933         if (finallyScriptObj) {
19934             Jim_EvalObj(interp, finallyScriptObj);
19935         }
19936         return exitCode;
19937     }
19938 
19939     if (sig && exitCode == JIM_SIGNAL) {
19940 
19941         if (interp->signal_set_result) {
19942             interp->signal_set_result(interp, interp->sigmask);
19943         }
19944         else if (!istry) {
19945             Jim_SetResultInt(interp, interp->sigmask);
19946         }
19947         interp->sigmask = 0;
19948     }
19949 
19950     ok = 1;
19951     if (msgVarObj && Jim_Length(msgVarObj)) {
19952         if (Jim_SetVariable(interp, msgVarObj, Jim_GetResult(interp)) != JIM_OK) {
19953             ok = 0;
19954         }
19955     }
19956     if (ok && optsVarObj && Jim_Length(optsVarObj)) {
19957         Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
19958 
19959         Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
19960         Jim_ListAppendElement(interp, optListObj,
19961             Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
19962         Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
19963         Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
19964         if (exitCode == JIM_ERR) {
19965             Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
19966                 -1));
19967             Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
19968 
19969             if (errorCodeObj) {
19970                 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
19971                 Jim_ListAppendElement(interp, optListObj, errorCodeObj);
19972             }
19973         }
19974         if (Jim_SetVariable(interp, optsVarObj, optListObj) != JIM_OK) {
19975             ok = 0;
19976         }
19977     }
19978     if (ok && handlerScriptObj) {
19979 
19980         exitCode = Jim_EvalObj(interp, handlerScriptObj);
19981     }
19982 
19983     if (finallyScriptObj) {
19984 
19985         Jim_Obj *prevResultObj = Jim_GetResult(interp);
19986         Jim_IncrRefCount(prevResultObj);
19987         int ret = Jim_EvalObj(interp, finallyScriptObj);
19988         if (ret == JIM_OK) {
19989             Jim_SetResult(interp, prevResultObj);
19990         }
19991         else {
19992             exitCode = ret;
19993         }
19994         Jim_DecrRefCount(interp, prevResultObj);
19995     }
19996     if (!istry) {
19997         Jim_SetResultInt(interp, exitCode);
19998         exitCode = JIM_OK;
19999     }
20000     return exitCode;
20001 }
20002 
20003 
Jim_CatchCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20004 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20005 {
20006     return JimCatchTryHelper(interp, 0, argc, argv);
20007 }
20008 
20009 
Jim_TryCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20010 static int Jim_TryCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20011 {
20012     return JimCatchTryHelper(interp, 1, argc, argv);
20013 }
20014 
20015 
20016 
Jim_RenameCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20017 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20018 {
20019     if (argc != 3) {
20020         Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
20021         return JIM_ERR;
20022     }
20023 
20024     return Jim_RenameCommand(interp, argv[1], argv[2]);
20025 }
20026 
20027 #define JIM_DICTMATCH_KEYS 0x0001
20028 #define JIM_DICTMATCH_VALUES 0x002
20029 
Jim_DictMatchTypes(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * patternObj,int match_type,int return_types)20030 int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
20031 {
20032     Jim_Obj *listObjPtr;
20033     Jim_Dict *dict;
20034     int i;
20035 
20036     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
20037         return JIM_ERR;
20038     }
20039     dict = objPtr->internalRep.dictValue;
20040 
20041     listObjPtr = Jim_NewListObj(interp, NULL, 0);
20042 
20043     for (i = 0; i < dict->len; i += 2 ) {
20044         Jim_Obj *keyObj = dict->table[i];
20045         Jim_Obj *valObj = dict->table[i + 1];
20046         if (patternObj) {
20047             Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? keyObj : valObj;
20048             if (!Jim_StringMatchObj(interp, patternObj, matchObj, 0)) {
20049 
20050                 continue;
20051             }
20052         }
20053         if (return_types & JIM_DICTMATCH_KEYS) {
20054             Jim_ListAppendElement(interp, listObjPtr, keyObj);
20055         }
20056         if (return_types & JIM_DICTMATCH_VALUES) {
20057             Jim_ListAppendElement(interp, listObjPtr, valObj);
20058         }
20059     }
20060 
20061     Jim_SetResult(interp, listObjPtr);
20062     return JIM_OK;
20063 }
20064 
Jim_DictSize(Jim_Interp * interp,Jim_Obj * objPtr)20065 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
20066 {
20067     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
20068         return -1;
20069     }
20070     return objPtr->internalRep.dictValue->len / 2;
20071 }
20072 
Jim_DictMerge(Jim_Interp * interp,int objc,Jim_Obj * const * objv)20073 Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
20074 {
20075     Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
20076     int i;
20077 
20078     JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));
20079 
20080 
20081 
20082     for (i = 0; i < objc; i++) {
20083         Jim_Obj **table;
20084         int tablelen;
20085         int j;
20086 
20087         table = Jim_DictPairs(interp, objv[i], &tablelen);
20088         if (tablelen && !table) {
20089             Jim_FreeNewObj(interp, objPtr);
20090             return NULL;
20091         }
20092         for (j = 0; j < tablelen; j += 2) {
20093             DictAddElement(interp, objPtr, table[j], table[j + 1]);
20094         }
20095     }
20096     return objPtr;
20097 }
20098 
Jim_DictInfo(Jim_Interp * interp,Jim_Obj * objPtr)20099 int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
20100 {
20101     char buffer[100];
20102     Jim_Obj *output;
20103     Jim_Dict *dict;
20104 
20105     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
20106         return JIM_ERR;
20107     }
20108 
20109     dict = objPtr->internalRep.dictValue;
20110 
20111 
20112     snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets", dict->len, dict->size);
20113     output = Jim_NewStringObj(interp, buffer, -1);
20114     Jim_SetResult(interp, output);
20115     return JIM_OK;
20116 }
20117 
Jim_EvalEnsemble(Jim_Interp * interp,const char * basecmd,const char * subcmd,int argc,Jim_Obj * const * argv)20118 static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
20119 {
20120     Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
20121 
20122     Jim_AppendString(interp, prefixObj, " ", 1);
20123     Jim_AppendString(interp, prefixObj, subcmd, -1);
20124 
20125     return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
20126 }
20127 
JimDictWith(Jim_Interp * interp,Jim_Obj * dictVarName,Jim_Obj * const * keyv,int keyc,Jim_Obj * scriptObj)20128 static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj)
20129 {
20130     int i;
20131     Jim_Obj *objPtr;
20132     Jim_Obj *dictObj;
20133     Jim_Obj **dictValues;
20134     int len;
20135     int ret = JIM_OK;
20136 
20137 
20138     dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
20139     if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
20140         return JIM_ERR;
20141     }
20142 
20143     dictValues = Jim_DictPairs(interp, objPtr, &len);
20144     if (len && dictValues == NULL) {
20145         return JIM_ERR;
20146     }
20147     for (i = 0; i < len; i += 2) {
20148         if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
20149             return JIM_ERR;
20150         }
20151     }
20152 
20153 
20154     if (Jim_Length(scriptObj)) {
20155         ret = Jim_EvalObj(interp, scriptObj);
20156 
20157 
20158         if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {
20159 
20160             Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
20161             for (i = 0; i < keyc; i++) {
20162                 newkeyv[i] = keyv[i];
20163             }
20164 
20165             for (i = 0; i < len; i += 2) {
20166 
20167                 if (Jim_StringCompareObj(interp, dictVarName, dictValues[i], 0) != 0) {
20168 
20169                     objPtr = Jim_GetVariable(interp, dictValues[i], 0);
20170                     newkeyv[keyc] = dictValues[i];
20171                     Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, JIM_NORESULT);
20172                 }
20173             }
20174             Jim_Free(newkeyv);
20175         }
20176     }
20177 
20178     return ret;
20179 }
20180 
20181 
Jim_DictCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20182 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20183 {
20184     Jim_Obj *objPtr;
20185     int types = JIM_DICTMATCH_KEYS;
20186 
20187     enum {
20188         OPT_CREATE,
20189         OPT_GET,
20190         OPT_GETDEF,
20191         OPT_GETWITHDEFAULT,
20192         OPT_SET,
20193         OPT_UNSET,
20194         OPT_EXISTS,
20195         OPT_KEYS,
20196         OPT_SIZE,
20197         OPT_INFO,
20198         OPT_MERGE,
20199         OPT_WITH,
20200         OPT_APPEND,
20201         OPT_LAPPEND,
20202         OPT_INCR,
20203         OPT_REMOVE,
20204         OPT_VALUES,
20205         OPT_FOR,
20206         OPT_REPLACE,
20207         OPT_UPDATE,
20208         OPT_COUNT
20209     };
20210     static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
20211         JIM_DEF_SUBCMD("create", "?key value ...?", 0, -2),
20212         JIM_DEF_SUBCMD("get", "dictionary ?key ...?", 1, -1),
20213         JIM_DEF_SUBCMD_HIDDEN("getdef", "dictionary ?key ...? key default", 3, -1),
20214         JIM_DEF_SUBCMD("getwithdefault", "dictionary ?key ...? key default", 3, -1),
20215         JIM_DEF_SUBCMD("set", "varName key ?key ...? value", 3, -1),
20216         JIM_DEF_SUBCMD("unset", "varName key ?key ...?", 2, -1),
20217         JIM_DEF_SUBCMD("exists", "dictionary key ?key ...?", 2, -1),
20218         JIM_DEF_SUBCMD("keys", "dictionary ?pattern?", 1, 2),
20219         JIM_DEF_SUBCMD("size", "dictionary", 1, 1),
20220         JIM_DEF_SUBCMD("info", "dictionary", 1, 1),
20221         JIM_DEF_SUBCMD("merge", "?...?", 0, -1),
20222         JIM_DEF_SUBCMD("with", "dictVar ?key ...? script", 2, -1),
20223         JIM_DEF_SUBCMD("append", "varName key ?value ...?", 2, -1),
20224         JIM_DEF_SUBCMD("lappend",  "varName key ?value ...?", 2, -1),
20225         JIM_DEF_SUBCMD("incr", "varName key ?increment?", 2, 3),
20226         JIM_DEF_SUBCMD("remove", "dictionary ?key ...?", 1, -1),
20227         JIM_DEF_SUBCMD("values", "dictionary ?pattern?", 1, 2),
20228         JIM_DEF_SUBCMD("for", "vars dictionary script", 3, 3),
20229         JIM_DEF_SUBCMD("replace", "dictionary ?key value ...?", 1, -1),
20230         JIM_DEF_SUBCMD("update", "varName ?arg ...? script", 2, -1),
20231         { NULL }
20232     };
20233     const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
20234     if (!ct) {
20235         return JIM_ERR;
20236     }
20237     if (ct->function) {
20238 
20239         return ct->function(interp, argc, argv);
20240     }
20241 
20242 
20243     switch (ct - cmds) {
20244         case OPT_GET:
20245             if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
20246                     JIM_ERRMSG) != JIM_OK) {
20247                 return JIM_ERR;
20248             }
20249             Jim_SetResult(interp, objPtr);
20250             return JIM_OK;
20251 
20252         case OPT_GETDEF:
20253         case OPT_GETWITHDEFAULT:{
20254             int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 4, &objPtr, JIM_ERRMSG);
20255             if (rc == -1) {
20256 
20257                 return JIM_ERR;
20258             }
20259             if (rc == JIM_ERR) {
20260                 Jim_SetResult(interp, argv[argc - 1]);
20261             }
20262             else {
20263                 Jim_SetResult(interp, objPtr);
20264             }
20265             return JIM_OK;
20266         }
20267 
20268         case OPT_SET:
20269             return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG | JIM_UNSHARED);
20270 
20271         case OPT_EXISTS:{
20272                 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_NONE);
20273                 if (rc < 0) {
20274                     return JIM_ERR;
20275                 }
20276                 Jim_SetResultBool(interp,  rc == JIM_OK);
20277                 return JIM_OK;
20278             }
20279 
20280         case OPT_UNSET:
20281             if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_UNSHARED) != JIM_OK) {
20282                 return JIM_ERR;
20283             }
20284             return JIM_OK;
20285 
20286         case OPT_VALUES:
20287             types = JIM_DICTMATCH_VALUES;
20288 
20289         case OPT_KEYS:
20290             return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);
20291 
20292         case OPT_SIZE:
20293             if (Jim_DictSize(interp, argv[2]) < 0) {
20294                 return JIM_ERR;
20295             }
20296             Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
20297             return JIM_OK;
20298 
20299         case OPT_MERGE:
20300             if (argc == 2) {
20301                 return JIM_OK;
20302             }
20303             objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
20304             if (objPtr == NULL) {
20305                 return JIM_ERR;
20306             }
20307             Jim_SetResult(interp, objPtr);
20308             return JIM_OK;
20309 
20310         case OPT_CREATE:
20311             objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
20312             Jim_SetResult(interp, objPtr);
20313             return JIM_OK;
20314 
20315         case OPT_INFO:
20316             return Jim_DictInfo(interp, argv[2]);
20317 
20318         case OPT_WITH:
20319             return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
20320 
20321         case OPT_UPDATE:
20322             if (argc < 6 || argc % 2) {
20323 
20324                 argc = 2;
20325             }
20326 
20327         default:
20328             return Jim_EvalEnsemble(interp, "dict", Jim_String(argv[1]), argc - 2, argv + 2);
20329     }
20330 }
20331 
20332 
Jim_SubstCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20333 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20334 {
20335     static const char * const options[] = {
20336         "-nobackslashes", "-nocommands", "-novariables", NULL
20337     };
20338     enum
20339     { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
20340     int i;
20341     int flags = JIM_SUBST_FLAG;
20342     Jim_Obj *objPtr;
20343 
20344     if (argc < 2) {
20345         Jim_WrongNumArgs(interp, 1, argv, "?options? string");
20346         return JIM_ERR;
20347     }
20348     for (i = 1; i < (argc - 1); i++) {
20349         int option;
20350 
20351         if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
20352                 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
20353             return JIM_ERR;
20354         }
20355         switch (option) {
20356             case OPT_NOBACKSLASHES:
20357                 flags |= JIM_SUBST_NOESC;
20358                 break;
20359             case OPT_NOCOMMANDS:
20360                 flags |= JIM_SUBST_NOCMD;
20361                 break;
20362             case OPT_NOVARIABLES:
20363                 flags |= JIM_SUBST_NOVAR;
20364                 break;
20365         }
20366     }
20367     if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
20368         return JIM_ERR;
20369     }
20370     Jim_SetResult(interp, objPtr);
20371     return JIM_OK;
20372 }
20373 
20374 #ifdef jim_ext_namespace
JimIsGlobalNamespace(Jim_Obj * objPtr)20375 static int JimIsGlobalNamespace(Jim_Obj *objPtr)
20376 {
20377     int len;
20378     const char *str = Jim_GetString(objPtr, &len);
20379     return len >= 2 && str[0] == ':' && str[1] == ':';
20380 }
20381 #endif
20382 
20383 
Jim_InfoCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20384 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20385 {
20386     Jim_Obj *objPtr;
20387     int mode = 0;
20388 
20389 
20390     enum {
20391         INFO_ALIAS,
20392         INFO_ARGS,
20393         INFO_BODY,
20394         INFO_CHANNELS,
20395         INFO_COMMANDS,
20396         INFO_COMPLETE,
20397         INFO_EXISTS,
20398         INFO_FRAME,
20399         INFO_GLOBALS,
20400         INFO_HOSTNAME,
20401         INFO_LEVEL,
20402         INFO_LOCALS,
20403         INFO_NAMEOFEXECUTABLE,
20404         INFO_PATCHLEVEL,
20405         INFO_PROCS,
20406         INFO_REFERENCES,
20407         INFO_RETURNCODES,
20408         INFO_SCRIPT,
20409         INFO_SOURCE,
20410         INFO_STACKTRACE,
20411         INFO_STATICS,
20412         INFO_VARS,
20413         INFO_VERSION,
20414         INFO_COUNT
20415     };
20416     static const jim_subcmd_type cmds[INFO_COUNT + 1] = {
20417         JIM_DEF_SUBCMD("alias", "command", 1, 1),
20418         JIM_DEF_SUBCMD("args", "procname", 1, 1),
20419         JIM_DEF_SUBCMD("body", "procname", 1, 1),
20420         JIM_DEF_SUBCMD("channels", "?pattern?", 0, 1),
20421         JIM_DEF_SUBCMD("commands", "?pattern?", 0, 1),
20422         JIM_DEF_SUBCMD("complete", "script ?missing?", 1, 2),
20423         JIM_DEF_SUBCMD("exists", "varName", 1, 1),
20424         JIM_DEF_SUBCMD("frame", "?levelNum?", 0, 1),
20425         JIM_DEF_SUBCMD("globals", "?pattern?", 0, 1),
20426         JIM_DEF_SUBCMD("hostname", NULL, 0, 0),
20427         JIM_DEF_SUBCMD("level", "?levelNum?", 0, 1),
20428         JIM_DEF_SUBCMD("locals", "?pattern?", 0, 1),
20429         JIM_DEF_SUBCMD("nameofexecutable", NULL, 0, 0),
20430         JIM_DEF_SUBCMD("patchlevel", NULL, 0, 0),
20431         JIM_DEF_SUBCMD("procs", "?pattern?", 0, 1),
20432         JIM_DEF_SUBCMD("references", NULL, 0, 0),
20433         JIM_DEF_SUBCMD("returncodes", "?code?", 0, 1),
20434         JIM_DEF_SUBCMD("script", "?filename?", 0, 1),
20435         JIM_DEF_SUBCMD("source", "source ?filename line?", 1, 3),
20436         JIM_DEF_SUBCMD("stacktrace", NULL, 0, 0),
20437         JIM_DEF_SUBCMD("statics", "procname", 1, 1),
20438         JIM_DEF_SUBCMD("vars", "?pattern?", 0, 1),
20439         JIM_DEF_SUBCMD("version", NULL, 0, 0),
20440         { NULL }
20441     };
20442     const jim_subcmd_type *ct;
20443 #ifdef jim_ext_namespace
20444     int nons = 0;
20445 
20446     if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
20447 
20448         argc--;
20449         argv++;
20450         nons = 1;
20451     }
20452 #endif
20453     ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
20454     if (!ct) {
20455         return JIM_ERR;
20456     }
20457     if (ct->function) {
20458 
20459         return ct->function(interp, argc, argv);
20460     }
20461 
20462     int option = ct - cmds;
20463 
20464     switch (option) {
20465         case INFO_EXISTS:
20466             Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
20467             return JIM_OK;
20468 
20469         case INFO_ALIAS:{
20470             Jim_Cmd *cmdPtr;
20471 
20472             if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
20473                 return JIM_ERR;
20474             }
20475             if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
20476                 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
20477                 return JIM_ERR;
20478             }
20479             Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
20480             return JIM_OK;
20481         }
20482 
20483         case INFO_CHANNELS:
20484             mode++;
20485 #ifndef jim_ext_aio
20486             Jim_SetResultString(interp, "aio not enabled", -1);
20487             return JIM_ERR;
20488 #endif
20489 
20490         case INFO_PROCS:
20491             mode++;
20492 
20493         case INFO_COMMANDS:
20494 
20495 #ifdef jim_ext_namespace
20496             if (!nons) {
20497                 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
20498                     return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
20499                 }
20500             }
20501 #endif
20502             Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
20503             return JIM_OK;
20504 
20505         case INFO_VARS:
20506             mode++;
20507 
20508         case INFO_LOCALS:
20509             mode++;
20510 
20511         case INFO_GLOBALS:
20512 
20513 #ifdef jim_ext_namespace
20514             if (!nons) {
20515                 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
20516                     return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
20517                 }
20518             }
20519 #endif
20520             Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
20521             return JIM_OK;
20522 
20523         case INFO_SCRIPT:
20524             if (argc == 3) {
20525                 Jim_IncrRefCount(argv[2]);
20526                 Jim_DecrRefCount(interp, interp->currentFilenameObj);
20527                 interp->currentFilenameObj = argv[2];
20528             }
20529             Jim_SetResult(interp, interp->currentFilenameObj);
20530             return JIM_OK;
20531 
20532         case INFO_SOURCE:{
20533                 Jim_Obj *resObjPtr;
20534                 Jim_Obj *fileNameObj;
20535 
20536                 if (argc == 4) {
20537                     Jim_SubCmdArgError(interp, ct, argv[0]);
20538                     return JIM_ERR;
20539                 }
20540                 if (argc == 5) {
20541                     jim_wide line;
20542                     if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
20543                         return JIM_ERR;
20544                     }
20545                     resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
20546                     Jim_SetSourceInfo(interp, resObjPtr, argv[3], line);
20547                 }
20548                 else {
20549                     int line;
20550                     fileNameObj = Jim_GetSourceInfo(interp, argv[2], &line);
20551                     resObjPtr = Jim_NewListObj(interp, NULL, 0);
20552                     Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
20553                     Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
20554                 }
20555                 Jim_SetResult(interp, resObjPtr);
20556                 return JIM_OK;
20557             }
20558 
20559         case INFO_STACKTRACE:
20560             Jim_SetResult(interp, interp->stackTrace);
20561             return JIM_OK;
20562 
20563         case INFO_LEVEL:
20564             if (argc == 2) {
20565                 Jim_SetResultInt(interp, interp->framePtr->level);
20566             }
20567             else {
20568                 if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK) {
20569                     return JIM_ERR;
20570                 }
20571                 Jim_SetResult(interp, objPtr);
20572             }
20573             return JIM_OK;
20574 
20575         case INFO_FRAME:
20576             if (argc == 2) {
20577                 Jim_SetResultInt(interp, interp->procLevel + 1);
20578             }
20579             else {
20580                 if (JimInfoFrame(interp, argv[2], &objPtr) != JIM_OK) {
20581                     return JIM_ERR;
20582                 }
20583                 Jim_SetResult(interp, objPtr);
20584             }
20585             return JIM_OK;
20586 
20587         case INFO_BODY:
20588         case INFO_STATICS:
20589         case INFO_ARGS:{
20590                 Jim_Cmd *cmdPtr;
20591 
20592                 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
20593                     return JIM_ERR;
20594                 }
20595                 if (!cmdPtr->isproc) {
20596                     Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
20597                     return JIM_ERR;
20598                 }
20599                 switch (option) {
20600 #ifdef JIM_NO_INTROSPECTION
20601                     default:
20602                         Jim_SetResultString(interp, "unsupported", -1);
20603                         return JIM_ERR;
20604 #else
20605                     case INFO_BODY:
20606                         Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
20607                         break;
20608                     case INFO_ARGS:
20609                         Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
20610                         break;
20611 #endif
20612                     case INFO_STATICS:
20613                         if (cmdPtr->u.proc.staticVars) {
20614                             Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
20615                                 NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES));
20616                         }
20617                         break;
20618                 }
20619                 return JIM_OK;
20620             }
20621 
20622         case INFO_VERSION:
20623         case INFO_PATCHLEVEL:{
20624                 char buf[(JIM_INTEGER_SPACE * 2) + 1];
20625 
20626                 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
20627                 Jim_SetResultString(interp, buf, -1);
20628                 return JIM_OK;
20629             }
20630 
20631         case INFO_COMPLETE: {
20632                 char missing;
20633 
20634                 Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing));
20635                 if (missing != ' ' && argc == 4) {
20636                     Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
20637                 }
20638                 return JIM_OK;
20639             }
20640 
20641         case INFO_HOSTNAME:
20642 
20643             return Jim_Eval(interp, "os.gethostname");
20644 
20645         case INFO_NAMEOFEXECUTABLE:
20646 
20647             return Jim_Eval(interp, "{info nameofexecutable}");
20648 
20649         case INFO_RETURNCODES:
20650             if (argc == 2) {
20651                 int i;
20652                 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
20653 
20654                 for (i = 0; jimReturnCodes[i]; i++) {
20655                     Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
20656                     Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
20657                             jimReturnCodes[i], -1));
20658                 }
20659 
20660                 Jim_SetResult(interp, listObjPtr);
20661             }
20662             else if (argc == 3) {
20663                 long code;
20664                 const char *name;
20665 
20666                 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
20667                     return JIM_ERR;
20668                 }
20669                 name = Jim_ReturnCode(code);
20670                 if (*name == '?') {
20671                     Jim_SetResultInt(interp, code);
20672                 }
20673                 else {
20674                     Jim_SetResultString(interp, name, -1);
20675                 }
20676             }
20677             return JIM_OK;
20678         case INFO_REFERENCES:
20679 #ifdef JIM_REFERENCES
20680             return JimInfoReferences(interp, argc, argv);
20681 #else
20682             Jim_SetResultString(interp, "not supported", -1);
20683             return JIM_ERR;
20684 #endif
20685         default:
20686             abort();
20687     }
20688 }
20689 
20690 
Jim_ExistsCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20691 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20692 {
20693     Jim_Obj *objPtr;
20694     int result = 0;
20695 
20696     static const char * const options[] = {
20697         "-command", "-proc", "-alias", "-var", NULL
20698     };
20699     enum
20700     {
20701         OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
20702     };
20703     int option;
20704 
20705     if (argc == 2) {
20706         option = OPT_VAR;
20707         objPtr = argv[1];
20708     }
20709     else if (argc == 3) {
20710         if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
20711             return JIM_ERR;
20712         }
20713         objPtr = argv[2];
20714     }
20715     else {
20716         Jim_WrongNumArgs(interp, 1, argv, "?option? name");
20717         return JIM_ERR;
20718     }
20719 
20720     if (option == OPT_VAR) {
20721         result = Jim_GetVariable(interp, objPtr, 0) != NULL;
20722     }
20723     else {
20724 
20725         Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
20726 
20727         if (cmd) {
20728             switch (option) {
20729             case OPT_COMMAND:
20730                 result = 1;
20731                 break;
20732 
20733             case OPT_ALIAS:
20734                 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
20735                 break;
20736 
20737             case OPT_PROC:
20738                 result = cmd->isproc;
20739                 break;
20740             }
20741         }
20742     }
20743     Jim_SetResultBool(interp, result);
20744     return JIM_OK;
20745 }
20746 
20747 
Jim_SplitCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20748 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20749 {
20750     const char *str, *splitChars, *noMatchStart;
20751     int splitLen, strLen;
20752     Jim_Obj *resObjPtr;
20753     int c;
20754     int len;
20755 
20756     if (argc != 2 && argc != 3) {
20757         Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
20758         return JIM_ERR;
20759     }
20760 
20761     str = Jim_GetString(argv[1], &len);
20762     if (len == 0) {
20763         return JIM_OK;
20764     }
20765     strLen = Jim_Utf8Length(interp, argv[1]);
20766 
20767 
20768     if (argc == 2) {
20769         splitChars = " \n\t\r";
20770         splitLen = 4;
20771     }
20772     else {
20773         splitChars = Jim_String(argv[2]);
20774         splitLen = Jim_Utf8Length(interp, argv[2]);
20775     }
20776 
20777     noMatchStart = str;
20778     resObjPtr = Jim_NewListObj(interp, NULL, 0);
20779 
20780 
20781     if (splitLen) {
20782         Jim_Obj *objPtr;
20783         while (strLen--) {
20784             const char *sc = splitChars;
20785             int scLen = splitLen;
20786             int sl = utf8_tounicode(str, &c);
20787             while (scLen--) {
20788                 int pc;
20789                 sc += utf8_tounicode(sc, &pc);
20790                 if (c == pc) {
20791                     objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
20792                     Jim_ListAppendElement(interp, resObjPtr, objPtr);
20793                     noMatchStart = str + sl;
20794                     break;
20795                 }
20796             }
20797             str += sl;
20798         }
20799         objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
20800         Jim_ListAppendElement(interp, resObjPtr, objPtr);
20801     }
20802     else {
20803         Jim_Obj **commonObj = NULL;
20804 #define NUM_COMMON (128 - 9)
20805         while (strLen--) {
20806             int n = utf8_tounicode(str, &c);
20807 #ifdef JIM_OPTIMIZATION
20808             if (c >= 9 && c < 128) {
20809 
20810                 c -= 9;
20811                 if (!commonObj) {
20812                     commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
20813                     memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
20814                 }
20815                 if (!commonObj[c]) {
20816                     commonObj[c] = Jim_NewStringObj(interp, str, 1);
20817                 }
20818                 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
20819                 str++;
20820                 continue;
20821             }
20822 #endif
20823             Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
20824             str += n;
20825         }
20826         Jim_Free(commonObj);
20827     }
20828 
20829     Jim_SetResult(interp, resObjPtr);
20830     return JIM_OK;
20831 }
20832 
20833 
Jim_JoinCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20834 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20835 {
20836     const char *joinStr;
20837     int joinStrLen;
20838 
20839     if (argc != 2 && argc != 3) {
20840         Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
20841         return JIM_ERR;
20842     }
20843 
20844     if (argc == 2) {
20845         joinStr = " ";
20846         joinStrLen = 1;
20847     }
20848     else {
20849         joinStr = Jim_GetString(argv[2], &joinStrLen);
20850     }
20851     Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
20852     return JIM_OK;
20853 }
20854 
20855 
Jim_FormatCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20856 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20857 {
20858     Jim_Obj *objPtr;
20859 
20860     if (argc < 2) {
20861         Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
20862         return JIM_ERR;
20863     }
20864     objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
20865     if (objPtr == NULL)
20866         return JIM_ERR;
20867     Jim_SetResult(interp, objPtr);
20868     return JIM_OK;
20869 }
20870 
20871 
Jim_ScanCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20872 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20873 {
20874     Jim_Obj *listPtr, **outVec;
20875     int outc, i;
20876 
20877     if (argc < 3) {
20878         Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
20879         return JIM_ERR;
20880     }
20881     if (argv[2]->typePtr != &scanFmtStringObjType)
20882         SetScanFmtFromAny(interp, argv[2]);
20883     if (FormatGetError(argv[2]) != 0) {
20884         Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
20885         return JIM_ERR;
20886     }
20887     if (argc > 3) {
20888         int maxPos = FormatGetMaxPos(argv[2]);
20889         int count = FormatGetCnvCount(argv[2]);
20890 
20891         if (maxPos > argc - 3) {
20892             Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
20893             return JIM_ERR;
20894         }
20895         else if (count > argc - 3) {
20896             Jim_SetResultString(interp, "different numbers of variable names and "
20897                 "field specifiers", -1);
20898             return JIM_ERR;
20899         }
20900         else if (count < argc - 3) {
20901             Jim_SetResultString(interp, "variable is not assigned by any "
20902                 "conversion specifiers", -1);
20903             return JIM_ERR;
20904         }
20905     }
20906     listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
20907     if (listPtr == 0)
20908         return JIM_ERR;
20909     if (argc > 3) {
20910         int rc = JIM_OK;
20911         int count = 0;
20912 
20913         if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
20914             int len = Jim_ListLength(interp, listPtr);
20915 
20916             if (len != 0) {
20917                 JimListGetElements(interp, listPtr, &outc, &outVec);
20918                 for (i = 0; i < outc; ++i) {
20919                     if (Jim_Length(outVec[i]) > 0) {
20920                         ++count;
20921                         if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
20922                             rc = JIM_ERR;
20923                         }
20924                     }
20925                 }
20926             }
20927             Jim_FreeNewObj(interp, listPtr);
20928         }
20929         else {
20930             count = -1;
20931         }
20932         if (rc == JIM_OK) {
20933             Jim_SetResultInt(interp, count);
20934         }
20935         return rc;
20936     }
20937     else {
20938         if (listPtr == (Jim_Obj *)EOF) {
20939             Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
20940             return JIM_OK;
20941         }
20942         Jim_SetResult(interp, listPtr);
20943     }
20944     return JIM_OK;
20945 }
20946 
20947 
Jim_ErrorCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20948 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20949 {
20950     if (argc != 2 && argc != 3) {
20951         Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
20952         return JIM_ERR;
20953     }
20954     Jim_SetResult(interp, argv[1]);
20955     if (argc == 3) {
20956         JimSetStackTrace(interp, argv[2]);
20957         return JIM_ERR;
20958     }
20959     return JIM_ERR;
20960 }
20961 
20962 
Jim_LrangeCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20963 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20964 {
20965     Jim_Obj *objPtr;
20966 
20967     if (argc != 4) {
20968         Jim_WrongNumArgs(interp, 1, argv, "list first last");
20969         return JIM_ERR;
20970     }
20971     if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
20972         return JIM_ERR;
20973     Jim_SetResult(interp, objPtr);
20974     return JIM_OK;
20975 }
20976 
20977 
Jim_LrepeatCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)20978 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
20979 {
20980     Jim_Obj *objPtr;
20981     jim_wide count;
20982 
20983     if (argc < 2 || Jim_GetWideExpr(interp, argv[1], &count) != JIM_OK || count < 0) {
20984         Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
20985         return JIM_ERR;
20986     }
20987     if (count == 0 || argc == 2) {
20988         Jim_SetEmptyResult(interp);
20989         return JIM_OK;
20990     }
20991 
20992     argc -= 2;
20993     argv += 2;
20994 
20995     objPtr = Jim_NewListObj(interp, NULL, 0);
20996     ListEnsureLength(objPtr, argc * count);
20997     while (count--) {
20998         ListInsertElements(objPtr, -1, argc, argv);
20999     }
21000 
21001     Jim_SetResult(interp, objPtr);
21002     return JIM_OK;
21003 }
21004 
Jim_GetEnviron(void)21005 char **Jim_GetEnviron(void)
21006 {
21007 #if defined(HAVE__NSGETENVIRON)
21008     return *_NSGetEnviron();
21009 #elif defined(_environ)
21010     return _environ;
21011 #else
21012     #if !defined(NO_ENVIRON_EXTERN)
21013     extern char **environ;
21014     #endif
21015     return environ;
21016 #endif
21017 }
21018 
Jim_SetEnviron(char ** env)21019 void Jim_SetEnviron(char **env)
21020 {
21021 #if defined(HAVE__NSGETENVIRON)
21022     *_NSGetEnviron() = env;
21023 #elif defined(_environ)
21024     _environ = env;
21025 #else
21026     #if !defined(NO_ENVIRON_EXTERN)
21027     extern char **environ;
21028     #endif
21029 
21030     environ = env;
21031 #endif
21032 }
21033 
21034 
Jim_EnvCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)21035 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21036 {
21037     const char *key;
21038     const char *val;
21039 
21040     if (argc == 1) {
21041         char **e = Jim_GetEnviron();
21042 
21043         int i;
21044         Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
21045 
21046         for (i = 0; e[i]; i++) {
21047             const char *equals = strchr(e[i], '=');
21048 
21049             if (equals) {
21050                 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
21051                         equals - e[i]));
21052                 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
21053             }
21054         }
21055 
21056         Jim_SetResult(interp, listObjPtr);
21057         return JIM_OK;
21058     }
21059 
21060     if (argc > 3) {
21061         Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
21062         return JIM_ERR;
21063     }
21064     key = Jim_String(argv[1]);
21065     val = getenv(key);
21066     if (val == NULL) {
21067         if (argc < 3) {
21068             Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
21069             return JIM_ERR;
21070         }
21071         val = Jim_String(argv[2]);
21072     }
21073     Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
21074     return JIM_OK;
21075 }
21076 
21077 
Jim_SourceCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)21078 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21079 {
21080     int retval;
21081 
21082     if (argc != 2) {
21083         Jim_WrongNumArgs(interp, 1, argv, "fileName");
21084         return JIM_ERR;
21085     }
21086     retval = Jim_EvalFile(interp, Jim_String(argv[1]));
21087     if (retval == JIM_RETURN)
21088         return JIM_OK;
21089     return retval;
21090 }
21091 
21092 
Jim_LreverseCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)21093 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21094 {
21095     Jim_Obj *revObjPtr, **ele;
21096     int len;
21097 
21098     if (argc != 2) {
21099         Jim_WrongNumArgs(interp, 1, argv, "list");
21100         return JIM_ERR;
21101     }
21102     JimListGetElements(interp, argv[1], &len, &ele);
21103     revObjPtr = Jim_NewListObj(interp, NULL, 0);
21104     ListEnsureLength(revObjPtr, len);
21105     len--;
21106     while (len >= 0)
21107         ListAppendElement(revObjPtr, ele[len--]);
21108     Jim_SetResult(interp, revObjPtr);
21109     return JIM_OK;
21110 }
21111 
JimRangeLen(jim_wide start,jim_wide end,jim_wide step)21112 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
21113 {
21114     jim_wide len;
21115 
21116     if (step == 0)
21117         return -1;
21118     if (start == end)
21119         return 0;
21120     else if (step > 0 && start > end)
21121         return -1;
21122     else if (step < 0 && end > start)
21123         return -1;
21124     len = end - start;
21125     if (len < 0)
21126         len = -len;
21127     if (step < 0)
21128         step = -step;
21129     len = 1 + ((len - 1) / step);
21130     if (len > INT_MAX)
21131         len = INT_MAX;
21132     return (int)((len < 0) ? -1 : len);
21133 }
21134 
21135 
Jim_RangeCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)21136 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21137 {
21138     jim_wide start = 0, end, step = 1;
21139     int len, i;
21140     Jim_Obj *objPtr;
21141 
21142     if (argc < 2 || argc > 4) {
21143         Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
21144         return JIM_ERR;
21145     }
21146     if (argc == 2) {
21147         if (Jim_GetWideExpr(interp, argv[1], &end) != JIM_OK)
21148             return JIM_ERR;
21149     }
21150     else {
21151         if (Jim_GetWideExpr(interp, argv[1], &start) != JIM_OK ||
21152             Jim_GetWideExpr(interp, argv[2], &end) != JIM_OK)
21153             return JIM_ERR;
21154         if (argc == 4 && Jim_GetWideExpr(interp, argv[3], &step) != JIM_OK)
21155             return JIM_ERR;
21156     }
21157     if ((len = JimRangeLen(start, end, step)) == -1) {
21158         Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
21159         return JIM_ERR;
21160     }
21161     objPtr = Jim_NewListObj(interp, NULL, 0);
21162     ListEnsureLength(objPtr, len);
21163     for (i = 0; i < len; i++)
21164         ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
21165     Jim_SetResult(interp, objPtr);
21166     return JIM_OK;
21167 }
21168 
21169 
Jim_RandCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)21170 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21171 {
21172     jim_wide min = 0, max = 0, len, maxMul;
21173 
21174     if (argc < 1 || argc > 3) {
21175         Jim_WrongNumArgs(interp, 1, argv, "?min? max");
21176         return JIM_ERR;
21177     }
21178     if (argc == 1) {
21179         max = JIM_WIDE_MAX;
21180     } else if (argc == 2) {
21181         if (Jim_GetWideExpr(interp, argv[1], &max) != JIM_OK)
21182             return JIM_ERR;
21183     } else if (argc == 3) {
21184         if (Jim_GetWideExpr(interp, argv[1], &min) != JIM_OK ||
21185             Jim_GetWideExpr(interp, argv[2], &max) != JIM_OK)
21186             return JIM_ERR;
21187     }
21188     len = max-min;
21189     if (len < 0) {
21190         Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
21191         return JIM_ERR;
21192     }
21193     maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
21194     while (1) {
21195         jim_wide r;
21196 
21197         JimRandomBytes(interp, &r, sizeof(jim_wide));
21198         if (r < 0 || r >= maxMul) continue;
21199         r = (len == 0) ? 0 : r%len;
21200         Jim_SetResultInt(interp, min+r);
21201         return JIM_OK;
21202     }
21203 }
21204 
21205 static const struct {
21206     const char *name;
21207     Jim_CmdProc *cmdProc;
21208 } Jim_CoreCommandsTable[] = {
21209     {"alias", Jim_AliasCoreCommand},
21210     {"set", Jim_SetCoreCommand},
21211     {"unset", Jim_UnsetCoreCommand},
21212     {"puts", Jim_PutsCoreCommand},
21213     {"+", Jim_AddCoreCommand},
21214     {"*", Jim_MulCoreCommand},
21215     {"-", Jim_SubCoreCommand},
21216     {"/", Jim_DivCoreCommand},
21217     {"incr", Jim_IncrCoreCommand},
21218     {"while", Jim_WhileCoreCommand},
21219     {"loop", Jim_LoopCoreCommand},
21220     {"for", Jim_ForCoreCommand},
21221     {"foreach", Jim_ForeachCoreCommand},
21222     {"lmap", Jim_LmapCoreCommand},
21223     {"lassign", Jim_LassignCoreCommand},
21224     {"if", Jim_IfCoreCommand},
21225     {"switch", Jim_SwitchCoreCommand},
21226     {"list", Jim_ListCoreCommand},
21227     {"lindex", Jim_LindexCoreCommand},
21228     {"lset", Jim_LsetCoreCommand},
21229     {"lsearch", Jim_LsearchCoreCommand},
21230     {"llength", Jim_LlengthCoreCommand},
21231     {"lappend", Jim_LappendCoreCommand},
21232     {"linsert", Jim_LinsertCoreCommand},
21233     {"lreplace", Jim_LreplaceCoreCommand},
21234     {"lsort", Jim_LsortCoreCommand},
21235     {"append", Jim_AppendCoreCommand},
21236     {"eval", Jim_EvalCoreCommand},
21237     {"uplevel", Jim_UplevelCoreCommand},
21238     {"expr", Jim_ExprCoreCommand},
21239     {"break", Jim_BreakCoreCommand},
21240     {"continue", Jim_ContinueCoreCommand},
21241     {"proc", Jim_ProcCoreCommand},
21242     {"xtrace", Jim_XtraceCoreCommand},
21243     {"concat", Jim_ConcatCoreCommand},
21244     {"return", Jim_ReturnCoreCommand},
21245     {"upvar", Jim_UpvarCoreCommand},
21246     {"global", Jim_GlobalCoreCommand},
21247     {"string", Jim_StringCoreCommand},
21248     {"time", Jim_TimeCoreCommand},
21249     {"timerate", Jim_TimeRateCoreCommand},
21250     {"exit", Jim_ExitCoreCommand},
21251     {"catch", Jim_CatchCoreCommand},
21252     {"try", Jim_TryCoreCommand},
21253 #ifdef JIM_REFERENCES
21254     {"ref", Jim_RefCoreCommand},
21255     {"getref", Jim_GetrefCoreCommand},
21256     {"setref", Jim_SetrefCoreCommand},
21257     {"finalize", Jim_FinalizeCoreCommand},
21258     {"collect", Jim_CollectCoreCommand},
21259 #endif
21260     {"rename", Jim_RenameCoreCommand},
21261     {"dict", Jim_DictCoreCommand},
21262     {"subst", Jim_SubstCoreCommand},
21263     {"info", Jim_InfoCoreCommand},
21264     {"exists", Jim_ExistsCoreCommand},
21265     {"split", Jim_SplitCoreCommand},
21266     {"join", Jim_JoinCoreCommand},
21267     {"format", Jim_FormatCoreCommand},
21268     {"scan", Jim_ScanCoreCommand},
21269     {"error", Jim_ErrorCoreCommand},
21270     {"lrange", Jim_LrangeCoreCommand},
21271     {"lrepeat", Jim_LrepeatCoreCommand},
21272     {"env", Jim_EnvCoreCommand},
21273     {"source", Jim_SourceCoreCommand},
21274     {"lreverse", Jim_LreverseCoreCommand},
21275     {"range", Jim_RangeCoreCommand},
21276     {"rand", Jim_RandCoreCommand},
21277     {"tailcall", Jim_TailcallCoreCommand},
21278     {"local", Jim_LocalCoreCommand},
21279     {"upcall", Jim_UpcallCoreCommand},
21280     {"apply", Jim_ApplyCoreCommand},
21281     {"stacktrace", Jim_StacktraceCoreCommand},
21282     {NULL, NULL},
21283 };
21284 
Jim_RegisterCoreCommands(Jim_Interp * interp)21285 void Jim_RegisterCoreCommands(Jim_Interp *interp)
21286 {
21287     int i = 0;
21288 
21289     while (Jim_CoreCommandsTable[i].name != NULL) {
21290         Jim_CreateCommand(interp,
21291             Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
21292         i++;
21293     }
21294 }
21295 
Jim_MakeErrorMessage(Jim_Interp * interp)21296 void Jim_MakeErrorMessage(Jim_Interp *interp)
21297 {
21298     Jim_Obj *argv[2];
21299 
21300     argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
21301     argv[1] = interp->result;
21302 
21303     Jim_EvalObjVector(interp, 2, argv);
21304 }
21305 
JimSortStringTable(const char * const * tablePtr)21306 static char **JimSortStringTable(const char *const *tablePtr)
21307 {
21308     int count;
21309     char **tablePtrSorted;
21310 
21311 
21312     for (count = 0; tablePtr[count]; count++) {
21313     }
21314 
21315 
21316     tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1));
21317     memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
21318     qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
21319     tablePtrSorted[count] = NULL;
21320 
21321     return tablePtrSorted;
21322 }
21323 
JimSetFailedEnumResult(Jim_Interp * interp,const char * arg,const char * badtype,const char * prefix,const char * const * tablePtr,const char * name)21324 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
21325     const char *prefix, const char *const *tablePtr, const char *name)
21326 {
21327     char **tablePtrSorted;
21328     int i;
21329 
21330     if (name == NULL) {
21331         name = "option";
21332     }
21333 
21334     Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
21335     tablePtrSorted = JimSortStringTable(tablePtr);
21336     for (i = 0; tablePtrSorted[i]; i++) {
21337         if (tablePtrSorted[i + 1] == NULL && i > 0) {
21338             Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
21339         }
21340         Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
21341         if (tablePtrSorted[i + 1]) {
21342             Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
21343         }
21344     }
21345     Jim_Free(tablePtrSorted);
21346 }
21347 
21348 
Jim_CheckShowCommands(Jim_Interp * interp,Jim_Obj * objPtr,const char * const * tablePtr)21349 int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr)
21350 {
21351     if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) {
21352         int i;
21353         char **tablePtrSorted = JimSortStringTable(tablePtr);
21354         Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
21355         for (i = 0; tablePtrSorted[i]; i++) {
21356             Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1));
21357         }
21358         Jim_Free(tablePtrSorted);
21359         return JIM_OK;
21360     }
21361     return JIM_ERR;
21362 }
21363 
21364 static const Jim_ObjType getEnumObjType = {
21365     "get-enum",
21366     NULL,
21367     NULL,
21368     NULL,
21369     JIM_TYPE_REFERENCES
21370 };
21371 
Jim_GetEnum(Jim_Interp * interp,Jim_Obj * objPtr,const char * const * tablePtr,int * indexPtr,const char * name,int flags)21372 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
21373     const char *const *tablePtr, int *indexPtr, const char *name, int flags)
21374 {
21375     const char *bad = "bad ";
21376     const char *const *entryPtr = NULL;
21377     int i;
21378     int match = -1;
21379     int arglen;
21380     const char *arg;
21381 
21382     if (objPtr->typePtr == &getEnumObjType) {
21383         if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) {
21384             *indexPtr = objPtr->internalRep.ptrIntValue.int2;
21385             return JIM_OK;
21386         }
21387     }
21388 
21389     arg = Jim_GetString(objPtr, &arglen);
21390 
21391     *indexPtr = -1;
21392 
21393     for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
21394         if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
21395 
21396             match = i;
21397             goto found;
21398         }
21399         if (flags & JIM_ENUM_ABBREV) {
21400             if (strncmp(arg, *entryPtr, arglen) == 0) {
21401                 if (*arg == '-' && arglen == 1) {
21402                     break;
21403                 }
21404                 if (match >= 0) {
21405                     bad = "ambiguous ";
21406                     goto ambiguous;
21407                 }
21408                 match = i;
21409             }
21410         }
21411     }
21412 
21413 
21414     if (match >= 0) {
21415   found:
21416 
21417         Jim_FreeIntRep(interp, objPtr);
21418         objPtr->typePtr = &getEnumObjType;
21419         objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr;
21420         objPtr->internalRep.ptrIntValue.int1 = flags;
21421         objPtr->internalRep.ptrIntValue.int2 = match;
21422 
21423         *indexPtr = match;
21424         return JIM_OK;
21425     }
21426 
21427   ambiguous:
21428     if (flags & JIM_ERRMSG) {
21429         JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
21430     }
21431     return JIM_ERR;
21432 }
21433 
Jim_FindByName(const char * name,const char * const array[],size_t len)21434 int Jim_FindByName(const char *name, const char * const array[], size_t len)
21435 {
21436     int i;
21437 
21438     for (i = 0; i < (int)len; i++) {
21439         if (array[i] && strcmp(array[i], name) == 0) {
21440             return i;
21441         }
21442     }
21443     return -1;
21444 }
21445 
Jim_IsDict(Jim_Obj * objPtr)21446 int Jim_IsDict(Jim_Obj *objPtr)
21447 {
21448     return objPtr->typePtr == &dictObjType;
21449 }
21450 
Jim_IsList(Jim_Obj * objPtr)21451 int Jim_IsList(Jim_Obj *objPtr)
21452 {
21453     return objPtr->typePtr == &listObjType;
21454 }
21455 
Jim_SetResultFormatted(Jim_Interp * interp,const char * format,...)21456 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
21457 {
21458 
21459     int len = strlen(format);
21460     int extra = 0;
21461     int n = 0;
21462     const char *params[5];
21463     int nobjparam = 0;
21464     Jim_Obj *objparam[5];
21465     char *buf;
21466     va_list args;
21467     int i;
21468 
21469     va_start(args, format);
21470 
21471     for (i = 0; i < len && n < 5; i++) {
21472         int l;
21473 
21474         if (strncmp(format + i, "%s", 2) == 0) {
21475             params[n] = va_arg(args, char *);
21476 
21477             l = strlen(params[n]);
21478         }
21479         else if (strncmp(format + i, "%#s", 3) == 0) {
21480             Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
21481 
21482             params[n] = Jim_GetString(objPtr, &l);
21483             objparam[nobjparam++] = objPtr;
21484             Jim_IncrRefCount(objPtr);
21485         }
21486         else {
21487             if (format[i] == '%') {
21488                 i++;
21489             }
21490             continue;
21491         }
21492         n++;
21493         extra += l;
21494     }
21495 
21496     len += extra;
21497     buf = Jim_Alloc(len + 1);
21498     len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
21499 
21500     va_end(args);
21501 
21502     Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
21503 
21504     for (i = 0; i < nobjparam; i++) {
21505         Jim_DecrRefCount(interp, objparam[i]);
21506     }
21507 }
21508 
Jim_CheckAbiVersion(Jim_Interp * interp,int abi_version)21509 int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version)
21510 {
21511     if (abi_version != JIM_ABI_VERSION) {
21512         Jim_SetResultString(interp, "ABI version mismatch", -1);
21513         return JIM_ERR;
21514     }
21515     return JIM_OK;
21516 }
21517 
21518 
21519 #ifndef jim_ext_package
Jim_PackageProvide(Jim_Interp * interp,const char * name,const char * ver,int flags)21520 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
21521 {
21522     return JIM_OK;
21523 }
21524 #endif
21525 #ifndef jim_ext_aio
Jim_AioFilehandle(Jim_Interp * interp,Jim_Obj * fhObj)21526 int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
21527 {
21528     return -1;
21529 }
21530 #endif
21531 
21532 
21533 #include <stdio.h>
21534 #include <string.h>
21535 
21536 
subcmd_null(Jim_Interp * interp,int argc,Jim_Obj * const * argv)21537 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21538 {
21539 
21540     return JIM_OK;
21541 }
21542 
21543 static const jim_subcmd_type dummy_subcmd = {
21544     "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
21545 };
21546 
subcmd_cmd_list(Jim_Interp * interp,const jim_subcmd_type * ct,const char * sep)21547 static Jim_Obj *subcmd_cmd_list(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
21548 {
21549 
21550     Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
21551     Jim_Obj *sortCmd[2];
21552 
21553     for (; ct->cmd; ct++) {
21554         if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
21555             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, ct->cmd, -1));
21556         }
21557     }
21558 
21559 
21560     sortCmd[0] = Jim_NewStringObj(interp, "lsort", -1);
21561     sortCmd[1] = listObj;
21562 
21563     if (Jim_EvalObjVector(interp, 2, sortCmd) == JIM_OK) {
21564         return Jim_ListJoin(interp, Jim_GetResult(interp), sep, strlen(sep));
21565     }
21566 
21567     return Jim_GetResult(interp);
21568 }
21569 
bad_subcmd(Jim_Interp * interp,const jim_subcmd_type * command_table,const char * type,Jim_Obj * cmd,Jim_Obj * subcmd)21570 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
21571     Jim_Obj *cmd, Jim_Obj *subcmd)
21572 {
21573     Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be %#s", cmd, type,
21574         subcmd, subcmd_cmd_list(interp, command_table, ", "));
21575 }
21576 
show_cmd_usage(Jim_Interp * interp,const jim_subcmd_type * command_table,int argc,Jim_Obj * const * argv)21577 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
21578     Jim_Obj *const *argv)
21579 {
21580     Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: %#s",
21581         argv[0], subcmd_cmd_list(interp, command_table, ", "));
21582 }
21583 
add_cmd_usage(Jim_Interp * interp,const jim_subcmd_type * ct,Jim_Obj * cmd)21584 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
21585 {
21586     if (cmd) {
21587         Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
21588     }
21589     Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
21590     if (ct->args && *ct->args) {
21591         Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
21592     }
21593 }
21594 
Jim_SubCmdArgError(Jim_Interp * interp,const jim_subcmd_type * ct,Jim_Obj * subcmd)21595 void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *subcmd)
21596 {
21597     Jim_SetResultString(interp, "wrong # args: should be \"", -1);
21598     add_cmd_usage(interp, ct, subcmd);
21599     Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
21600 }
21601 
21602 static const Jim_ObjType subcmdLookupObjType = {
21603     "subcmd-lookup",
21604     NULL,
21605     NULL,
21606     NULL,
21607     JIM_TYPE_REFERENCES
21608 };
21609 
Jim_ParseSubCmd(Jim_Interp * interp,const jim_subcmd_type * command_table,int argc,Jim_Obj * const * argv)21610 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
21611     int argc, Jim_Obj *const *argv)
21612 {
21613     const jim_subcmd_type *ct;
21614     const jim_subcmd_type *partial = 0;
21615     int cmdlen;
21616     Jim_Obj *cmd;
21617     const char *cmdstr;
21618     int help = 0;
21619     int argsok = 1;
21620 
21621     if (argc < 2) {
21622         Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n"
21623             "Use \"%#s -help ?command?\" for help", argv[0], argv[0]);
21624         return 0;
21625     }
21626 
21627     cmd = argv[1];
21628 
21629 
21630     if (cmd->typePtr == &subcmdLookupObjType) {
21631         if (cmd->internalRep.ptrIntValue.ptr == command_table) {
21632             ct = command_table + cmd->internalRep.ptrIntValue.int1;
21633             goto found;
21634         }
21635     }
21636 
21637 
21638     if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
21639         if (argc == 2) {
21640 
21641             show_cmd_usage(interp, command_table, argc, argv);
21642             return &dummy_subcmd;
21643         }
21644         help = 1;
21645 
21646 
21647         cmd = argv[2];
21648     }
21649 
21650 
21651     if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
21652         Jim_SetResult(interp, subcmd_cmd_list(interp, command_table, " "));
21653         return &dummy_subcmd;
21654     }
21655 
21656     cmdstr = Jim_GetString(cmd, &cmdlen);
21657 
21658     for (ct = command_table; ct->cmd; ct++) {
21659         if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
21660 
21661             break;
21662         }
21663         if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
21664             if (partial) {
21665 
21666                 if (help) {
21667 
21668                     show_cmd_usage(interp, command_table, argc, argv);
21669                     return &dummy_subcmd;
21670                 }
21671                 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
21672                 return 0;
21673             }
21674             partial = ct;
21675         }
21676         continue;
21677     }
21678 
21679 
21680     if (partial && !ct->cmd) {
21681         ct = partial;
21682     }
21683 
21684     if (!ct->cmd) {
21685 
21686         if (help) {
21687 
21688             show_cmd_usage(interp, command_table, argc, argv);
21689             return &dummy_subcmd;
21690         }
21691         bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
21692         return 0;
21693     }
21694 
21695     if (help) {
21696         Jim_SetResultString(interp, "Usage: ", -1);
21697 
21698         add_cmd_usage(interp, ct, argv[0]);
21699         return &dummy_subcmd;
21700     }
21701 
21702 
21703     Jim_FreeIntRep(interp, cmd);
21704     cmd->typePtr = &subcmdLookupObjType;
21705     cmd->internalRep.ptrIntValue.ptr = (void *)command_table;
21706     cmd->internalRep.ptrIntValue.int1 = ct - command_table;
21707 
21708 found:
21709 
21710 
21711     if (argc - 2 < ct->minargs) {
21712         argsok = 0;
21713     }
21714     else if (ct->maxargs >= 0 && argc - 2 > ct->maxargs) {
21715         argsok = 0;
21716     }
21717     else if (ct->maxargs < -1 && (argc - 2) % -ct->maxargs != 0) {
21718 
21719         argsok = 0;
21720     }
21721     if (!argsok) {
21722         Jim_SetResultString(interp, "wrong # args: should be \"", -1);
21723 
21724         add_cmd_usage(interp, ct, argv[0]);
21725         Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
21726 
21727         return 0;
21728     }
21729 
21730 
21731     return ct;
21732 }
21733 
Jim_CallSubCmd(Jim_Interp * interp,const jim_subcmd_type * ct,int argc,Jim_Obj * const * argv)21734 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
21735 {
21736     int ret = JIM_ERR;
21737 
21738     if (ct) {
21739         if (ct->flags & JIM_MODFLAG_FULLARGV) {
21740             ret = ct->function(interp, argc, argv);
21741         }
21742         else {
21743             ret = ct->function(interp, argc - 2, argv + 2);
21744         }
21745         if (ret < 0) {
21746             Jim_SubCmdArgError(interp, ct, argv[0]);
21747             ret = JIM_ERR;
21748         }
21749     }
21750     return ret;
21751 }
21752 
Jim_SubCmdProc(Jim_Interp * interp,int argc,Jim_Obj * const * argv)21753 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
21754 {
21755     const jim_subcmd_type *ct =
21756         Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
21757 
21758     return Jim_CallSubCmd(interp, ct, argc, argv);
21759 }
21760 
21761 #include <ctype.h>
21762 #include <stdlib.h>
21763 #include <string.h>
21764 #include <stdio.h>
21765 #include <assert.h>
21766 
21767 
utf8_fromunicode(char * p,unsigned uc)21768 int utf8_fromunicode(char *p, unsigned uc)
21769 {
21770     if (uc <= 0x7f) {
21771         *p = uc;
21772         return 1;
21773     }
21774     else if (uc <= 0x7ff) {
21775         *p++ = 0xc0 | ((uc & 0x7c0) >> 6);
21776         *p = 0x80 | (uc & 0x3f);
21777         return 2;
21778     }
21779     else if (uc <= 0xffff) {
21780         *p++ = 0xe0 | ((uc & 0xf000) >> 12);
21781         *p++ = 0x80 | ((uc & 0xfc0) >> 6);
21782         *p = 0x80 | (uc & 0x3f);
21783         return 3;
21784     }
21785 
21786     else {
21787         *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
21788         *p++ = 0x80 | ((uc & 0x3f000) >> 12);
21789         *p++ = 0x80 | ((uc & 0xfc0) >> 6);
21790         *p = 0x80 | (uc & 0x3f);
21791         return 4;
21792     }
21793 }
21794 
21795 #include <ctype.h>
21796 #include <string.h>
21797 #include <stdio.h>
21798 
21799 
21800 #define JIM_INTEGER_SPACE 24
21801 #define MAX_FLOAT_WIDTH 320
21802 
Jim_FormatString(Jim_Interp * interp,Jim_Obj * fmtObjPtr,int objc,Jim_Obj * const * objv)21803 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
21804 {
21805     const char *span, *format, *formatEnd, *msg;
21806     int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
21807     static const char * const mixedXPG =
21808             "cannot mix \"%\" and \"%n$\" conversion specifiers";
21809     static const char * const badIndex[2] = {
21810         "not enough arguments for all format specifiers",
21811         "\"%n$\" argument index out of range"
21812     };
21813     int formatLen;
21814     Jim_Obj *resultPtr;
21815 
21816     char *num_buffer = NULL;
21817     int num_buffer_size = 0;
21818 
21819     span = format = Jim_GetString(fmtObjPtr, &formatLen);
21820     formatEnd = format + formatLen;
21821     resultPtr = Jim_NewEmptyStringObj(interp);
21822 
21823     while (format != formatEnd) {
21824         char *end;
21825         int gotMinus, sawFlag;
21826         int gotPrecision, useShort;
21827         long width, precision;
21828         int newXpg;
21829         int ch;
21830         int step;
21831         int doubleType;
21832         char pad = ' ';
21833         char spec[2*JIM_INTEGER_SPACE + 12];
21834         char *p;
21835 
21836         int formatted_chars;
21837         int formatted_bytes;
21838         const char *formatted_buf;
21839 
21840         step = utf8_tounicode(format, &ch);
21841         format += step;
21842         if (ch != '%') {
21843             numBytes += step;
21844             continue;
21845         }
21846         if (numBytes) {
21847             Jim_AppendString(interp, resultPtr, span, numBytes);
21848             numBytes = 0;
21849         }
21850 
21851 
21852         step = utf8_tounicode(format, &ch);
21853         if (ch == '%') {
21854             span = format;
21855             numBytes = step;
21856             format += step;
21857             continue;
21858         }
21859 
21860 
21861         newXpg = 0;
21862         if (isdigit(ch)) {
21863             int position = strtoul(format, &end, 10);
21864             if (*end == '$') {
21865                 newXpg = 1;
21866                 objIndex = position - 1;
21867                 format = end + 1;
21868                 step = utf8_tounicode(format, &ch);
21869             }
21870         }
21871         if (newXpg) {
21872             if (gotSequential) {
21873                 msg = mixedXPG;
21874                 goto errorMsg;
21875             }
21876             gotXpg = 1;
21877         } else {
21878             if (gotXpg) {
21879                 msg = mixedXPG;
21880                 goto errorMsg;
21881             }
21882             gotSequential = 1;
21883         }
21884         if ((objIndex < 0) || (objIndex >= objc)) {
21885             msg = badIndex[gotXpg];
21886             goto errorMsg;
21887         }
21888 
21889         p = spec;
21890         *p++ = '%';
21891 
21892         gotMinus = 0;
21893         sawFlag = 1;
21894         do {
21895             switch (ch) {
21896             case '-':
21897                 gotMinus = 1;
21898                 break;
21899             case '0':
21900                 pad = ch;
21901                 break;
21902             case ' ':
21903             case '+':
21904             case '#':
21905                 break;
21906             default:
21907                 sawFlag = 0;
21908                 continue;
21909             }
21910             *p++ = ch;
21911             format += step;
21912             step = utf8_tounicode(format, &ch);
21913 
21914         } while (sawFlag && (p - spec <= 5));
21915 
21916 
21917         width = 0;
21918         if (isdigit(ch)) {
21919             width = strtoul(format, &end, 10);
21920             format = end;
21921             step = utf8_tounicode(format, &ch);
21922         } else if (ch == '*') {
21923             if (objIndex >= objc - 1) {
21924                 msg = badIndex[gotXpg];
21925                 goto errorMsg;
21926             }
21927             if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) {
21928                 goto error;
21929             }
21930             if (width < 0) {
21931                 width = -width;
21932                 if (!gotMinus) {
21933                     *p++ = '-';
21934                     gotMinus = 1;
21935                 }
21936             }
21937             objIndex++;
21938             format += step;
21939             step = utf8_tounicode(format, &ch);
21940         }
21941 
21942 
21943         gotPrecision = precision = 0;
21944         if (ch == '.') {
21945             gotPrecision = 1;
21946             format += step;
21947             step = utf8_tounicode(format, &ch);
21948         }
21949         if (isdigit(ch)) {
21950             precision = strtoul(format, &end, 10);
21951             format = end;
21952             step = utf8_tounicode(format, &ch);
21953         } else if (ch == '*') {
21954             if (objIndex >= objc - 1) {
21955                 msg = badIndex[gotXpg];
21956                 goto errorMsg;
21957             }
21958             if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) {
21959                 goto error;
21960             }
21961 
21962 
21963             if (precision < 0) {
21964                 precision = 0;
21965             }
21966             objIndex++;
21967             format += step;
21968             step = utf8_tounicode(format, &ch);
21969         }
21970 
21971 
21972         useShort = 0;
21973         if (ch == 'h') {
21974             useShort = 1;
21975             format += step;
21976             step = utf8_tounicode(format, &ch);
21977         } else if (ch == 'l') {
21978 
21979             format += step;
21980             step = utf8_tounicode(format, &ch);
21981             if (ch == 'l') {
21982                 format += step;
21983                 step = utf8_tounicode(format, &ch);
21984             }
21985         }
21986 
21987         format += step;
21988         span = format;
21989 
21990 
21991         if (ch == 'i') {
21992             ch = 'd';
21993         }
21994 
21995         doubleType = 0;
21996 
21997         switch (ch) {
21998         case '\0':
21999             msg = "format string ended in middle of field specifier";
22000             goto errorMsg;
22001         case 's': {
22002             formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
22003             formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
22004             if (gotPrecision && (precision < formatted_chars)) {
22005 
22006                 formatted_chars = precision;
22007                 formatted_bytes = utf8_index(formatted_buf, precision);
22008             }
22009             break;
22010         }
22011         case 'c': {
22012             jim_wide code;
22013 
22014             if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
22015                 goto error;
22016             }
22017 
22018             formatted_bytes = utf8_getchars(spec, code);
22019             formatted_buf = spec;
22020             formatted_chars = 1;
22021             break;
22022         }
22023         case 'b': {
22024                 unsigned jim_wide w;
22025                 int length;
22026                 int i;
22027                 int j;
22028 
22029                 if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
22030                     goto error;
22031                 }
22032                 length = sizeof(w) * 8;
22033 
22034 
22035 
22036                 if (num_buffer_size < length + 1) {
22037                     num_buffer_size = length + 1;
22038                     num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
22039                 }
22040 
22041                 j = 0;
22042                 for (i = length; i > 0; ) {
22043                         i--;
22044                         if (w & ((unsigned jim_wide)1 << i)) {
22045                                 num_buffer[j++] = '1';
22046                         }
22047                         else if (j || i == 0) {
22048                                 num_buffer[j++] = '0';
22049                         }
22050                 }
22051                 num_buffer[j] = 0;
22052                 formatted_chars = formatted_bytes = j;
22053                 formatted_buf = num_buffer;
22054                 break;
22055         }
22056 
22057         case 'e':
22058         case 'E':
22059         case 'f':
22060         case 'g':
22061         case 'G':
22062             doubleType = 1;
22063 
22064         case 'd':
22065         case 'u':
22066         case 'o':
22067         case 'x':
22068         case 'X': {
22069             jim_wide w;
22070             double d;
22071             int length;
22072 
22073 
22074             if (width) {
22075                 p += sprintf(p, "%ld", width);
22076             }
22077             if (gotPrecision) {
22078                 p += sprintf(p, ".%ld", precision);
22079             }
22080 
22081 
22082             if (doubleType) {
22083                 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
22084                     goto error;
22085                 }
22086                 length = MAX_FLOAT_WIDTH;
22087             }
22088             else {
22089                 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) {
22090                     goto error;
22091                 }
22092                 length = JIM_INTEGER_SPACE;
22093                 if (useShort) {
22094                     if (ch == 'd') {
22095                         w = (short)w;
22096                     }
22097                     else {
22098                         w = (unsigned short)w;
22099                     }
22100                 }
22101                 *p++ = 'l';
22102 #ifdef HAVE_LONG_LONG
22103                 if (sizeof(long long) == sizeof(jim_wide)) {
22104                     *p++ = 'l';
22105                 }
22106 #endif
22107             }
22108 
22109             *p++ = (char) ch;
22110             *p = '\0';
22111 
22112 
22113             if (width > 10000 || length > 10000 || precision > 10000) {
22114                 Jim_SetResultString(interp, "format too long", -1);
22115                 goto error;
22116             }
22117 
22118 
22119 
22120             if (width > length) {
22121                 length = width;
22122             }
22123             if (gotPrecision) {
22124                 length += precision;
22125             }
22126 
22127 
22128             if (num_buffer_size < length + 1) {
22129                 num_buffer_size = length + 1;
22130                 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
22131             }
22132 
22133             if (doubleType) {
22134                 snprintf(num_buffer, length + 1, spec, d);
22135             }
22136             else {
22137                 formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
22138             }
22139             formatted_chars = formatted_bytes = strlen(num_buffer);
22140             formatted_buf = num_buffer;
22141             break;
22142         }
22143 
22144         default: {
22145 
22146             spec[0] = ch;
22147             spec[1] = '\0';
22148             Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
22149             goto error;
22150         }
22151         }
22152 
22153         if (!gotMinus) {
22154             while (formatted_chars < width) {
22155                 Jim_AppendString(interp, resultPtr, &pad, 1);
22156                 formatted_chars++;
22157             }
22158         }
22159 
22160         Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes);
22161 
22162         while (formatted_chars < width) {
22163             Jim_AppendString(interp, resultPtr, &pad, 1);
22164             formatted_chars++;
22165         }
22166 
22167         objIndex += gotSequential;
22168     }
22169     if (numBytes) {
22170         Jim_AppendString(interp, resultPtr, span, numBytes);
22171     }
22172 
22173     Jim_Free(num_buffer);
22174     return resultPtr;
22175 
22176   errorMsg:
22177     Jim_SetResultString(interp, msg, -1);
22178   error:
22179     Jim_FreeNewObj(interp, resultPtr);
22180     Jim_Free(num_buffer);
22181     return NULL;
22182 }
22183 
22184 
22185 #if defined(JIM_REGEXP)
22186 #include <stdio.h>
22187 #include <ctype.h>
22188 #include <stdlib.h>
22189 #include <string.h>
22190 
22191 
22192 
22193 #define REG_MAX_PAREN 100
22194 
22195 
22196 
22197 #define	END	0
22198 #define	BOL	1
22199 #define	EOL	2
22200 #define	ANY	3
22201 #define	ANYOF	4
22202 #define	ANYBUT	5
22203 #define	BRANCH	6
22204 #define	BACK	7
22205 #define	EXACTLY	8
22206 #define	NOTHING	9
22207 #define	REP	10
22208 #define	REPMIN	11
22209 #define	REPX	12
22210 #define	REPXMIN	13
22211 #define	BOLX	14
22212 #define	EOLX	15
22213 #define	WORDA	16
22214 #define	WORDZ	17
22215 
22216 #define	OPENNC 	1000
22217 #define	OPEN   	1001
22218 
22219 
22220 
22221 
22222 #define	CLOSENC	2000
22223 #define	CLOSE	2001
22224 #define	CLOSE_END	(CLOSE+REG_MAX_PAREN)
22225 
22226 #define	REG_MAGIC	0xFADED00D
22227 
22228 
22229 #define	OP(preg, p)	(preg->program[p])
22230 #define	NEXT(preg, p)	(preg->program[p + 1])
22231 #define	OPERAND(p)	((p) + 2)
22232 
22233 
22234 
22235 
22236 #define	FAIL(R,M)	{ (R)->err = (M); return (M); }
22237 #define	ISMULT(c)	((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
22238 #define	META		"^$.[()|?{+*"
22239 
22240 #define	HASWIDTH	1
22241 #define	SIMPLE		2
22242 #define	SPSTART		4
22243 #define	WORST		0
22244 
22245 #define MAX_REP_COUNT 1000000
22246 
22247 static int reg(regex_t *preg, int paren, int *flagp );
22248 static int regpiece(regex_t *preg, int *flagp );
22249 static int regbranch(regex_t *preg, int *flagp );
22250 static int regatom(regex_t *preg, int *flagp );
22251 static int regnode(regex_t *preg, int op );
22252 static int regnext(regex_t *preg, int p );
22253 static void regc(regex_t *preg, int b );
22254 static int reginsert(regex_t *preg, int op, int size, int opnd );
22255 static void regtail(regex_t *preg, int p, int val);
22256 static void regoptail(regex_t *preg, int p, int val );
22257 static int regopsize(regex_t *preg, int p );
22258 
22259 static int reg_range_find(const int *string, int c);
22260 static const char *str_find(const char *string, int c, int nocase);
22261 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase);
22262 
22263 
22264 #ifdef DEBUG
22265 static int regnarrate = 0;
22266 static void regdump(regex_t *preg);
22267 static const char *regprop( int op );
22268 #endif
22269 
22270 
str_int_len(const int * seq)22271 static int str_int_len(const int *seq)
22272 {
22273 	int n = 0;
22274 	while (*seq++) {
22275 		n++;
22276 	}
22277 	return n;
22278 }
22279 
jim_regcomp(regex_t * preg,const char * exp,int cflags)22280 int jim_regcomp(regex_t *preg, const char *exp, int cflags)
22281 {
22282 	int scan;
22283 	int longest;
22284 	unsigned len;
22285 	int flags;
22286 
22287 #ifdef DEBUG
22288 	fprintf(stderr, "Compiling: '%s'\n", exp);
22289 #endif
22290 	memset(preg, 0, sizeof(*preg));
22291 
22292 	if (exp == NULL)
22293 		FAIL(preg, REG_ERR_NULL_ARGUMENT);
22294 
22295 
22296 	preg->cflags = cflags;
22297 	preg->regparse = exp;
22298 
22299 
22300 	preg->proglen = (strlen(exp) + 1) * 5;
22301 	preg->program = malloc(preg->proglen * sizeof(int));
22302 	if (preg->program == NULL)
22303 		FAIL(preg, REG_ERR_NOMEM);
22304 
22305 	regc(preg, REG_MAGIC);
22306 	if (reg(preg, 0, &flags) == 0) {
22307 		return preg->err;
22308 	}
22309 
22310 
22311 	if (preg->re_nsub >= REG_MAX_PAREN)
22312 		FAIL(preg,REG_ERR_TOO_BIG);
22313 
22314 
22315 	preg->regstart = 0;
22316 	preg->reganch = 0;
22317 	preg->regmust = 0;
22318 	preg->regmlen = 0;
22319 	scan = 1;
22320 	if (OP(preg, regnext(preg, scan)) == END) {
22321 		scan = OPERAND(scan);
22322 
22323 
22324 		if (OP(preg, scan) == EXACTLY) {
22325 			preg->regstart = preg->program[OPERAND(scan)];
22326 		}
22327 		else if (OP(preg, scan) == BOL)
22328 			preg->reganch++;
22329 
22330 		if (flags&SPSTART) {
22331 			longest = 0;
22332 			len = 0;
22333 			for (; scan != 0; scan = regnext(preg, scan)) {
22334 				if (OP(preg, scan) == EXACTLY) {
22335 					int plen = str_int_len(preg->program + OPERAND(scan));
22336 					if (plen >= len) {
22337 						longest = OPERAND(scan);
22338 						len = plen;
22339 					}
22340 				}
22341 			}
22342 			preg->regmust = longest;
22343 			preg->regmlen = len;
22344 		}
22345 	}
22346 
22347 #ifdef DEBUG
22348 	regdump(preg);
22349 #endif
22350 
22351 	return 0;
22352 }
22353 
reg(regex_t * preg,int paren,int * flagp)22354 static int reg(regex_t *preg, int paren, int *flagp )
22355 {
22356 	int ret;
22357 	int br;
22358 	int ender;
22359 	int parno = 0;
22360 	int flags;
22361 
22362 	*flagp = HASWIDTH;
22363 
22364 
22365 	if (paren) {
22366 		if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
22367 
22368 			preg->regparse += 2;
22369 			parno = -1;
22370 		}
22371 		else {
22372 			parno = ++preg->re_nsub;
22373 		}
22374 		ret = regnode(preg, OPEN+parno);
22375 	} else
22376 		ret = 0;
22377 
22378 
22379 	br = regbranch(preg, &flags);
22380 	if (br == 0)
22381 		return 0;
22382 	if (ret != 0)
22383 		regtail(preg, ret, br);
22384 	else
22385 		ret = br;
22386 	if (!(flags&HASWIDTH))
22387 		*flagp &= ~HASWIDTH;
22388 	*flagp |= flags&SPSTART;
22389 	while (*preg->regparse == '|') {
22390 		preg->regparse++;
22391 		br = regbranch(preg, &flags);
22392 		if (br == 0)
22393 			return 0;
22394 		regtail(preg, ret, br);
22395 		if (!(flags&HASWIDTH))
22396 			*flagp &= ~HASWIDTH;
22397 		*flagp |= flags&SPSTART;
22398 	}
22399 
22400 
22401 	ender = regnode(preg, (paren) ? CLOSE+parno : END);
22402 	regtail(preg, ret, ender);
22403 
22404 
22405 	for (br = ret; br != 0; br = regnext(preg, br))
22406 		regoptail(preg, br, ender);
22407 
22408 
22409 	if (paren && *preg->regparse++ != ')') {
22410 		preg->err = REG_ERR_UNMATCHED_PAREN;
22411 		return 0;
22412 	} else if (!paren && *preg->regparse != '\0') {
22413 		if (*preg->regparse == ')') {
22414 			preg->err = REG_ERR_UNMATCHED_PAREN;
22415 			return 0;
22416 		} else {
22417 			preg->err = REG_ERR_JUNK_ON_END;
22418 			return 0;
22419 		}
22420 	}
22421 
22422 	return(ret);
22423 }
22424 
regbranch(regex_t * preg,int * flagp)22425 static int regbranch(regex_t *preg, int *flagp )
22426 {
22427 	int ret;
22428 	int chain;
22429 	int latest;
22430 	int flags;
22431 
22432 	*flagp = WORST;
22433 
22434 	ret = regnode(preg, BRANCH);
22435 	chain = 0;
22436 	while (*preg->regparse != '\0' && *preg->regparse != ')' &&
22437 	       *preg->regparse != '|') {
22438 		latest = regpiece(preg, &flags);
22439 		if (latest == 0)
22440 			return 0;
22441 		*flagp |= flags&HASWIDTH;
22442 		if (chain == 0) {
22443 			*flagp |= flags&SPSTART;
22444 		}
22445 		else {
22446 			regtail(preg, chain, latest);
22447 		}
22448 		chain = latest;
22449 	}
22450 	if (chain == 0)
22451 		(void) regnode(preg, NOTHING);
22452 
22453 	return(ret);
22454 }
22455 
regpiece(regex_t * preg,int * flagp)22456 static int regpiece(regex_t *preg, int *flagp)
22457 {
22458 	int ret;
22459 	char op;
22460 	int next;
22461 	int flags;
22462 	int min;
22463 	int max;
22464 
22465 	ret = regatom(preg, &flags);
22466 	if (ret == 0)
22467 		return 0;
22468 
22469 	op = *preg->regparse;
22470 	if (!ISMULT(op)) {
22471 		*flagp = flags;
22472 		return(ret);
22473 	}
22474 
22475 	if (!(flags&HASWIDTH) && op != '?') {
22476 		preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
22477 		return 0;
22478 	}
22479 
22480 
22481 	if (op == '{') {
22482 		char *end;
22483 
22484 		min = strtoul(preg->regparse + 1, &end, 10);
22485 		if (end == preg->regparse + 1) {
22486 			preg->err = REG_ERR_BAD_COUNT;
22487 			return 0;
22488 		}
22489 		if (*end == '}') {
22490 			max = min;
22491 		}
22492 		else if (*end == '\0') {
22493 			preg->err = REG_ERR_UNMATCHED_BRACES;
22494 			return 0;
22495 		}
22496 		else {
22497 			preg->regparse = end;
22498 			max = strtoul(preg->regparse + 1, &end, 10);
22499 			if (*end != '}') {
22500 				preg->err = REG_ERR_UNMATCHED_BRACES;
22501 				return 0;
22502 			}
22503 		}
22504 		if (end == preg->regparse + 1) {
22505 			max = MAX_REP_COUNT;
22506 		}
22507 		else if (max < min || max >= 100) {
22508 			preg->err = REG_ERR_BAD_COUNT;
22509 			return 0;
22510 		}
22511 		if (min >= 100) {
22512 			preg->err = REG_ERR_BAD_COUNT;
22513 			return 0;
22514 		}
22515 
22516 		preg->regparse = strchr(preg->regparse, '}');
22517 	}
22518 	else {
22519 		min = (op == '+');
22520 		max = (op == '?' ? 1 : MAX_REP_COUNT);
22521 	}
22522 
22523 	if (preg->regparse[1] == '?') {
22524 		preg->regparse++;
22525 		next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret);
22526 	}
22527 	else {
22528 		next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret);
22529 	}
22530 	preg->program[ret + 2] = max;
22531 	preg->program[ret + 3] = min;
22532 	preg->program[ret + 4] = 0;
22533 
22534 	*flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART);
22535 
22536 	if (!(flags & SIMPLE)) {
22537 		int back = regnode(preg, BACK);
22538 		regtail(preg, back, ret);
22539 		regtail(preg, next, back);
22540 	}
22541 
22542 	preg->regparse++;
22543 	if (ISMULT(*preg->regparse)) {
22544 		preg->err = REG_ERR_NESTED_COUNT;
22545 		return 0;
22546 	}
22547 
22548 	return ret;
22549 }
22550 
reg_addrange(regex_t * preg,int lower,int upper)22551 static void reg_addrange(regex_t *preg, int lower, int upper)
22552 {
22553 	if (lower > upper) {
22554 		reg_addrange(preg, upper, lower);
22555 	}
22556 
22557 	regc(preg, upper - lower + 1);
22558 	regc(preg, lower);
22559 }
22560 
reg_addrange_str(regex_t * preg,const char * str)22561 static void reg_addrange_str(regex_t *preg, const char *str)
22562 {
22563 	while (*str) {
22564 		reg_addrange(preg, *str, *str);
22565 		str++;
22566 	}
22567 }
22568 
reg_utf8_tounicode_case(const char * s,int * uc,int upper)22569 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper)
22570 {
22571 	int l = utf8_tounicode(s, uc);
22572 	if (upper) {
22573 		*uc = utf8_upper(*uc);
22574 	}
22575 	return l;
22576 }
22577 
hexdigitval(int c)22578 static int hexdigitval(int c)
22579 {
22580 	if (c >= '0' && c <= '9')
22581 		return c - '0';
22582 	if (c >= 'a' && c <= 'f')
22583 		return c - 'a' + 10;
22584 	if (c >= 'A' && c <= 'F')
22585 		return c - 'A' + 10;
22586 	return -1;
22587 }
22588 
parse_hex(const char * s,int n,int * uc)22589 static int parse_hex(const char *s, int n, int *uc)
22590 {
22591 	int val = 0;
22592 	int k;
22593 
22594 	for (k = 0; k < n; k++) {
22595 		int c = hexdigitval(*s++);
22596 		if (c == -1) {
22597 			break;
22598 		}
22599 		val = (val << 4) | c;
22600 	}
22601 	if (k) {
22602 		*uc = val;
22603 	}
22604 	return k;
22605 }
22606 
reg_decode_escape(const char * s,int * ch)22607 static int reg_decode_escape(const char *s, int *ch)
22608 {
22609 	int n;
22610 	const char *s0 = s;
22611 
22612 	*ch = *s++;
22613 
22614 	switch (*ch) {
22615 		case 'b': *ch = '\b'; break;
22616 		case 'e': *ch = 27; break;
22617 		case 'f': *ch = '\f'; break;
22618 		case 'n': *ch = '\n'; break;
22619 		case 'r': *ch = '\r'; break;
22620 		case 't': *ch = '\t'; break;
22621 		case 'v': *ch = '\v'; break;
22622 		case 'u':
22623 			if (*s == '{') {
22624 
22625 				n = parse_hex(s + 1, 6, ch);
22626 				if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
22627 					s += n + 2;
22628 				}
22629 				else {
22630 
22631 					*ch = 'u';
22632 				}
22633 			}
22634 			else if ((n = parse_hex(s, 4, ch)) > 0) {
22635 				s += n;
22636 			}
22637 			break;
22638 		case 'U':
22639 			if ((n = parse_hex(s, 8, ch)) > 0) {
22640 				s += n;
22641 			}
22642 			break;
22643 		case 'x':
22644 			if ((n = parse_hex(s, 2, ch)) > 0) {
22645 				s += n;
22646 			}
22647 			break;
22648 		case '\0':
22649 			s--;
22650 			*ch = '\\';
22651 			break;
22652 	}
22653 	return s - s0;
22654 }
22655 
regatom(regex_t * preg,int * flagp)22656 static int regatom(regex_t *preg, int *flagp)
22657 {
22658 	int ret;
22659 	int flags;
22660 	int nocase = (preg->cflags & REG_ICASE);
22661 
22662 	int ch;
22663 	int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);
22664 
22665 	*flagp = WORST;
22666 
22667 	preg->regparse += n;
22668 	switch (ch) {
22669 
22670 	case '^':
22671 		ret = regnode(preg, BOL);
22672 		break;
22673 	case '$':
22674 		ret = regnode(preg, EOL);
22675 		break;
22676 	case '.':
22677 		ret = regnode(preg, ANY);
22678 		*flagp |= HASWIDTH|SIMPLE;
22679 		break;
22680 	case '[': {
22681 			const char *pattern = preg->regparse;
22682 
22683 			if (*pattern == '^') {
22684 				ret = regnode(preg, ANYBUT);
22685 				pattern++;
22686 			} else
22687 				ret = regnode(preg, ANYOF);
22688 
22689 
22690 			if (*pattern == ']' || *pattern == '-') {
22691 				reg_addrange(preg, *pattern, *pattern);
22692 				pattern++;
22693 			}
22694 
22695 			while (*pattern != ']') {
22696 
22697 				int start;
22698 				int end;
22699 
22700 				enum {
22701 					CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
22702 					CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
22703 					CC_NUM
22704 				};
22705 				int cc;
22706 
22707 				if (!*pattern) {
22708 					preg->err = REG_ERR_UNMATCHED_BRACKET;
22709 					return 0;
22710 				}
22711 
22712 				pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
22713 				if (start == '\\') {
22714 
22715 					switch (*pattern) {
22716 						case 's':
22717 							pattern++;
22718 							cc = CC_SPACE;
22719 							goto cc_switch;
22720 						case 'd':
22721 							pattern++;
22722 							cc = CC_DIGIT;
22723 							goto cc_switch;
22724 						case 'w':
22725 							pattern++;
22726 							reg_addrange(preg, '_', '_');
22727 							cc = CC_ALNUM;
22728 							goto cc_switch;
22729 					}
22730 					pattern += reg_decode_escape(pattern, &start);
22731 					if (start == 0) {
22732 						preg->err = REG_ERR_NULL_CHAR;
22733 						return 0;
22734 					}
22735 					if (start == '\\' && *pattern == 0) {
22736 						preg->err = REG_ERR_INVALID_ESCAPE;
22737 						return 0;
22738 					}
22739 				}
22740 				if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
22741 
22742 					pattern += utf8_tounicode(pattern, &end);
22743 					pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
22744 					if (end == '\\') {
22745 						pattern += reg_decode_escape(pattern, &end);
22746 						if (end == 0) {
22747 							preg->err = REG_ERR_NULL_CHAR;
22748 							return 0;
22749 						}
22750 						if (end == '\\' && *pattern == 0) {
22751 							preg->err = REG_ERR_INVALID_ESCAPE;
22752 							return 0;
22753 						}
22754 					}
22755 
22756 					reg_addrange(preg, start, end);
22757 					continue;
22758 				}
22759 				if (start == '[' && pattern[0] == ':') {
22760 					static const char *character_class[] = {
22761 						":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
22762 						":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
22763 					};
22764 
22765 					for (cc = 0; cc < CC_NUM; cc++) {
22766 						n = strlen(character_class[cc]);
22767 						if (strncmp(pattern, character_class[cc], n) == 0) {
22768 							if (pattern[n] != ']') {
22769 								preg->err = REG_ERR_UNMATCHED_BRACKET;
22770 								return 0;
22771 							}
22772 
22773 							pattern += n + 1;
22774 							break;
22775 						}
22776 					}
22777 					if (cc != CC_NUM) {
22778 cc_switch:
22779 						switch (cc) {
22780 							case CC_ALNUM:
22781 								reg_addrange(preg, '0', '9');
22782 
22783 							case CC_ALPHA:
22784 								if ((preg->cflags & REG_ICASE) == 0) {
22785 									reg_addrange(preg, 'a', 'z');
22786 								}
22787 								reg_addrange(preg, 'A', 'Z');
22788 								break;
22789 							case CC_SPACE:
22790 								reg_addrange_str(preg, " \t\r\n\f\v");
22791 								break;
22792 							case CC_BLANK:
22793 								reg_addrange_str(preg, " \t");
22794 								break;
22795 							case CC_UPPER:
22796 								reg_addrange(preg, 'A', 'Z');
22797 								break;
22798 							case CC_LOWER:
22799 								reg_addrange(preg, 'a', 'z');
22800 								break;
22801 							case CC_XDIGIT:
22802 								reg_addrange(preg, 'a', 'f');
22803 								reg_addrange(preg, 'A', 'F');
22804 
22805 							case CC_DIGIT:
22806 								reg_addrange(preg, '0', '9');
22807 								break;
22808 							case CC_CNTRL:
22809 								reg_addrange(preg, 0, 31);
22810 								reg_addrange(preg, 127, 127);
22811 								break;
22812 							case CC_PRINT:
22813 								reg_addrange(preg, ' ', '~');
22814 								break;
22815 							case CC_GRAPH:
22816 								reg_addrange(preg, '!', '~');
22817 								break;
22818 							case CC_PUNCT:
22819 								reg_addrange(preg, '!', '/');
22820 								reg_addrange(preg, ':', '@');
22821 								reg_addrange(preg, '[', '`');
22822 								reg_addrange(preg, '{', '~');
22823 								break;
22824 						}
22825 						continue;
22826 					}
22827 				}
22828 
22829 				reg_addrange(preg, start, start);
22830 			}
22831 			regc(preg, '\0');
22832 
22833 			if (*pattern) {
22834 				pattern++;
22835 			}
22836 			preg->regparse = pattern;
22837 
22838 			*flagp |= HASWIDTH|SIMPLE;
22839 		}
22840 		break;
22841 	case '(':
22842 		ret = reg(preg, 1, &flags);
22843 		if (ret == 0)
22844 			return 0;
22845 		*flagp |= flags&(HASWIDTH|SPSTART);
22846 		break;
22847 	case '\0':
22848 	case '|':
22849 	case ')':
22850 		preg->err = REG_ERR_INTERNAL;
22851 		return 0;
22852 	case '?':
22853 	case '+':
22854 	case '*':
22855 	case '{':
22856 		preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
22857 		return 0;
22858 	case '\\':
22859 		ch = *preg->regparse++;
22860 		switch (ch) {
22861 		case '\0':
22862 			preg->err = REG_ERR_INVALID_ESCAPE;
22863 			return 0;
22864 		case 'A':
22865 			ret = regnode(preg, BOLX);
22866 			break;
22867 		case 'Z':
22868 			ret = regnode(preg, EOLX);
22869 			break;
22870 		case '<':
22871 		case 'm':
22872 			ret = regnode(preg, WORDA);
22873 			break;
22874 		case '>':
22875 		case 'M':
22876 			ret = regnode(preg, WORDZ);
22877 			break;
22878 		case 'd':
22879 		case 'D':
22880 			ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT);
22881 			reg_addrange(preg, '0', '9');
22882 			regc(preg, '\0');
22883 			*flagp |= HASWIDTH|SIMPLE;
22884 			break;
22885 		case 'w':
22886 		case 'W':
22887 			ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT);
22888 			if ((preg->cflags & REG_ICASE) == 0) {
22889 				reg_addrange(preg, 'a', 'z');
22890 			}
22891 			reg_addrange(preg, 'A', 'Z');
22892 			reg_addrange(preg, '0', '9');
22893 			reg_addrange(preg, '_', '_');
22894 			regc(preg, '\0');
22895 			*flagp |= HASWIDTH|SIMPLE;
22896 			break;
22897 		case 's':
22898 		case 'S':
22899 			ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT);
22900 			reg_addrange_str(preg," \t\r\n\f\v");
22901 			regc(preg, '\0');
22902 			*flagp |= HASWIDTH|SIMPLE;
22903 			break;
22904 
22905 		default:
22906 
22907 
22908 			preg->regparse--;
22909 			goto de_fault;
22910 		}
22911 		break;
22912 	de_fault:
22913 	default: {
22914 			int added = 0;
22915 
22916 
22917 			preg->regparse -= n;
22918 
22919 			ret = regnode(preg, EXACTLY);
22920 
22921 
22922 
22923 			while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
22924 				n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
22925 				if (ch == '\\' && preg->regparse[n]) {
22926 					if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) {
22927 
22928 						break;
22929 					}
22930 					n += reg_decode_escape(preg->regparse + n, &ch);
22931 					if (ch == 0) {
22932 						preg->err = REG_ERR_NULL_CHAR;
22933 						return 0;
22934 					}
22935 				}
22936 
22937 
22938 				if (ISMULT(preg->regparse[n])) {
22939 
22940 					if (added) {
22941 
22942 						break;
22943 					}
22944 
22945 					regc(preg, ch);
22946 					added++;
22947 					preg->regparse += n;
22948 					break;
22949 				}
22950 
22951 
22952 				regc(preg, ch);
22953 				added++;
22954 				preg->regparse += n;
22955 			}
22956 			regc(preg, '\0');
22957 
22958 			*flagp |= HASWIDTH;
22959 			if (added == 1)
22960 				*flagp |= SIMPLE;
22961 			break;
22962 		}
22963 		break;
22964 	}
22965 
22966 	return(ret);
22967 }
22968 
reg_grow(regex_t * preg,int n)22969 static void reg_grow(regex_t *preg, int n)
22970 {
22971 	if (preg->p + n >= preg->proglen) {
22972 		preg->proglen = (preg->p + n) * 2;
22973 		preg->program = realloc(preg->program, preg->proglen * sizeof(int));
22974 	}
22975 }
22976 
22977 
regnode(regex_t * preg,int op)22978 static int regnode(regex_t *preg, int op)
22979 {
22980 	reg_grow(preg, 2);
22981 
22982 
22983 	preg->program[preg->p++] = op;
22984 	preg->program[preg->p++] = 0;
22985 
22986 
22987 	return preg->p - 2;
22988 }
22989 
regc(regex_t * preg,int b)22990 static void regc(regex_t *preg, int b )
22991 {
22992 	reg_grow(preg, 1);
22993 	preg->program[preg->p++] = b;
22994 }
22995 
reginsert(regex_t * preg,int op,int size,int opnd)22996 static int reginsert(regex_t *preg, int op, int size, int opnd )
22997 {
22998 	reg_grow(preg, size);
22999 
23000 
23001 	memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
23002 
23003 	memset(preg->program + opnd, 0, sizeof(int) * size);
23004 
23005 	preg->program[opnd] = op;
23006 
23007 	preg->p += size;
23008 
23009 	return opnd + size;
23010 }
23011 
regtail(regex_t * preg,int p,int val)23012 static void regtail(regex_t *preg, int p, int val)
23013 {
23014 	int scan;
23015 	int temp;
23016 	int offset;
23017 
23018 
23019 	scan = p;
23020 	for (;;) {
23021 		temp = regnext(preg, scan);
23022 		if (temp == 0)
23023 			break;
23024 		scan = temp;
23025 	}
23026 
23027 	if (OP(preg, scan) == BACK)
23028 		offset = scan - val;
23029 	else
23030 		offset = val - scan;
23031 
23032 	preg->program[scan + 1] = offset;
23033 }
23034 
23035 
regoptail(regex_t * preg,int p,int val)23036 static void regoptail(regex_t *preg, int p, int val )
23037 {
23038 
23039 	if (p != 0 && OP(preg, p) == BRANCH) {
23040 		regtail(preg, OPERAND(p), val);
23041 	}
23042 }
23043 
23044 
23045 static int regtry(regex_t *preg, const char *string );
23046 static int regmatch(regex_t *preg, int prog);
23047 static int regrepeat(regex_t *preg, int p, int max);
23048 
jim_regexec(regex_t * preg,const char * string,size_t nmatch,regmatch_t pmatch[],int eflags)23049 int jim_regexec(regex_t  *preg,  const  char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
23050 {
23051 	const char *s;
23052 	int scan;
23053 
23054 
23055 	if (preg == NULL || preg->program == NULL || string == NULL) {
23056 		return REG_ERR_NULL_ARGUMENT;
23057 	}
23058 
23059 
23060 	if (*preg->program != REG_MAGIC) {
23061 		return REG_ERR_CORRUPTED;
23062 	}
23063 
23064 #ifdef DEBUG
23065 	fprintf(stderr, "regexec: %s\n", string);
23066 	regdump(preg);
23067 #endif
23068 
23069 	preg->eflags = eflags;
23070 	preg->pmatch = pmatch;
23071 	preg->nmatch = nmatch;
23072 	preg->start = string;
23073 
23074 
23075 	for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) {
23076 		int op = OP(preg, scan);
23077 		if (op == END)
23078 			break;
23079 		if (op == REPX || op == REPXMIN)
23080 			preg->program[scan + 4] = 0;
23081 	}
23082 
23083 
23084 	if (preg->regmust != 0) {
23085 		s = string;
23086 		while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
23087 			if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
23088 				break;
23089 			}
23090 			s++;
23091 		}
23092 		if (s == NULL)
23093 			return REG_NOMATCH;
23094 	}
23095 
23096 
23097 	preg->regbol = string;
23098 
23099 
23100 	if (preg->reganch) {
23101 		if (eflags & REG_NOTBOL) {
23102 
23103 			goto nextline;
23104 		}
23105 		while (1) {
23106 			if (regtry(preg, string)) {
23107 				return REG_NOERROR;
23108 			}
23109 			if (*string) {
23110 nextline:
23111 				if (preg->cflags & REG_NEWLINE) {
23112 
23113 					string = strchr(string, '\n');
23114 					if (string) {
23115 						preg->regbol = ++string;
23116 						continue;
23117 					}
23118 				}
23119 			}
23120 			return REG_NOMATCH;
23121 		}
23122 	}
23123 
23124 
23125 	s = string;
23126 	if (preg->regstart != '\0') {
23127 
23128 		while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
23129 			if (regtry(preg, s))
23130 				return REG_NOERROR;
23131 			s++;
23132 		}
23133 	}
23134 	else
23135 
23136 		while (1) {
23137 			if (regtry(preg, s))
23138 				return REG_NOERROR;
23139 			if (*s == '\0') {
23140 				break;
23141 			}
23142 			else {
23143 				int c;
23144 				s += utf8_tounicode(s, &c);
23145 			}
23146 		}
23147 
23148 
23149 	return REG_NOMATCH;
23150 }
23151 
23152 
regtry(regex_t * preg,const char * string)23153 static int regtry( regex_t *preg, const char *string )
23154 {
23155 	int i;
23156 
23157 	preg->reginput = string;
23158 
23159 	for (i = 0; i < preg->nmatch; i++) {
23160 		preg->pmatch[i].rm_so = -1;
23161 		preg->pmatch[i].rm_eo = -1;
23162 	}
23163 	if (regmatch(preg, 1)) {
23164 		preg->pmatch[0].rm_so = string - preg->start;
23165 		preg->pmatch[0].rm_eo = preg->reginput - preg->start;
23166 		return(1);
23167 	} else
23168 		return(0);
23169 }
23170 
prefix_cmp(const int * prog,int proglen,const char * string,int nocase)23171 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase)
23172 {
23173 	const char *s = string;
23174 	while (proglen && *s) {
23175 		int ch;
23176 		int n = reg_utf8_tounicode_case(s, &ch, nocase);
23177 		if (ch != *prog) {
23178 			return -1;
23179 		}
23180 		prog++;
23181 		s += n;
23182 		proglen--;
23183 	}
23184 	if (proglen == 0) {
23185 		return s - string;
23186 	}
23187 	return -1;
23188 }
23189 
reg_range_find(const int * range,int c)23190 static int reg_range_find(const int *range, int c)
23191 {
23192 	while (*range) {
23193 
23194 		if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
23195 			return 1;
23196 		}
23197 		range += 2;
23198 	}
23199 	return 0;
23200 }
23201 
str_find(const char * string,int c,int nocase)23202 static const char *str_find(const char *string, int c, int nocase)
23203 {
23204 	if (nocase) {
23205 
23206 		c = utf8_upper(c);
23207 	}
23208 	while (*string) {
23209 		int ch;
23210 		int n = reg_utf8_tounicode_case(string, &ch, nocase);
23211 		if (c == ch) {
23212 			return string;
23213 		}
23214 		string += n;
23215 	}
23216 	return NULL;
23217 }
23218 
reg_iseol(regex_t * preg,int ch)23219 static int reg_iseol(regex_t *preg, int ch)
23220 {
23221 	if (preg->cflags & REG_NEWLINE) {
23222 		return ch == '\0' || ch == '\n';
23223 	}
23224 	else {
23225 		return ch == '\0';
23226 	}
23227 }
23228 
regmatchsimplerepeat(regex_t * preg,int scan,int matchmin)23229 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin)
23230 {
23231 	int nextch = '\0';
23232 	const char *save;
23233 	int no;
23234 	int c;
23235 
23236 	int max = preg->program[scan + 2];
23237 	int min = preg->program[scan + 3];
23238 	int next = regnext(preg, scan);
23239 
23240 	if (OP(preg, next) == EXACTLY) {
23241 		nextch = preg->program[OPERAND(next)];
23242 	}
23243 	save = preg->reginput;
23244 	no = regrepeat(preg, scan + 5, max);
23245 	if (no < min) {
23246 		return 0;
23247 	}
23248 	if (matchmin) {
23249 
23250 		max = no;
23251 		no = min;
23252 	}
23253 
23254 	while (1) {
23255 		if (matchmin) {
23256 			if (no > max) {
23257 				break;
23258 			}
23259 		}
23260 		else {
23261 			if (no < min) {
23262 				break;
23263 			}
23264 		}
23265 		preg->reginput = save + utf8_index(save, no);
23266 		reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
23267 
23268 		if (reg_iseol(preg, nextch) || c == nextch) {
23269 			if (regmatch(preg, next)) {
23270 				return(1);
23271 			}
23272 		}
23273 		if (matchmin) {
23274 
23275 			no++;
23276 		}
23277 		else {
23278 
23279 			no--;
23280 		}
23281 	}
23282 	return(0);
23283 }
23284 
regmatchrepeat(regex_t * preg,int scan,int matchmin)23285 static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
23286 {
23287 	int *scanpt = preg->program + scan;
23288 
23289 	int max = scanpt[2];
23290 	int min = scanpt[3];
23291 
23292 
23293 	if (scanpt[4] < min) {
23294 
23295 		scanpt[4]++;
23296 		if (regmatch(preg, scan + 5)) {
23297 			return 1;
23298 		}
23299 		scanpt[4]--;
23300 		return 0;
23301 	}
23302 	if (scanpt[4] > max) {
23303 		return 0;
23304 	}
23305 
23306 	if (matchmin) {
23307 
23308 		if (regmatch(preg, regnext(preg, scan))) {
23309 			return 1;
23310 		}
23311 
23312 		scanpt[4]++;
23313 		if (regmatch(preg, scan + 5)) {
23314 			return 1;
23315 		}
23316 		scanpt[4]--;
23317 		return 0;
23318 	}
23319 
23320 	if (scanpt[4] < max) {
23321 		scanpt[4]++;
23322 		if (regmatch(preg, scan + 5)) {
23323 			return 1;
23324 		}
23325 		scanpt[4]--;
23326 	}
23327 
23328 	return regmatch(preg, regnext(preg, scan));
23329 }
23330 
23331 
regmatch(regex_t * preg,int prog)23332 static int regmatch(regex_t *preg, int prog)
23333 {
23334 	int scan;
23335 	int next;
23336 	const char *save;
23337 
23338 	scan = prog;
23339 
23340 #ifdef DEBUG
23341 	if (scan != 0 && regnarrate)
23342 		fprintf(stderr, "%s(\n", regprop(scan));
23343 #endif
23344 	while (scan != 0) {
23345 		int n;
23346 		int c;
23347 #ifdef DEBUG
23348 		if (regnarrate) {
23349 			fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
23350 		}
23351 #endif
23352 		next = regnext(preg, scan);
23353 		n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
23354 
23355 		switch (OP(preg, scan)) {
23356 		case BOLX:
23357 			if ((preg->eflags & REG_NOTBOL)) {
23358 				return(0);
23359 			}
23360 
23361 		case BOL:
23362 			if (preg->reginput != preg->regbol) {
23363 				return(0);
23364 			}
23365 			break;
23366 		case EOLX:
23367 			if (c != 0) {
23368 
23369 				return 0;
23370 			}
23371 			break;
23372 		case EOL:
23373 			if (!reg_iseol(preg, c)) {
23374 				return(0);
23375 			}
23376 			break;
23377 		case WORDA:
23378 
23379 			if ((!isalnum(UCHAR(c))) && c != '_')
23380 				return(0);
23381 
23382 			if (preg->reginput > preg->regbol &&
23383 				(isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
23384 				return(0);
23385 			break;
23386 		case WORDZ:
23387 
23388 			if (preg->reginput > preg->regbol) {
23389 
23390 				if (reg_iseol(preg, c) || !(isalnum(UCHAR(c)) || c == '_')) {
23391 					c = preg->reginput[-1];
23392 
23393 					if (isalnum(UCHAR(c)) || c == '_') {
23394 						break;
23395 					}
23396 				}
23397 			}
23398 
23399 			return(0);
23400 
23401 		case ANY:
23402 			if (reg_iseol(preg, c))
23403 				return 0;
23404 			preg->reginput += n;
23405 			break;
23406 		case EXACTLY: {
23407 				int opnd;
23408 				int len;
23409 				int slen;
23410 
23411 				opnd = OPERAND(scan);
23412 				len = str_int_len(preg->program + opnd);
23413 
23414 				slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE);
23415 				if (slen < 0) {
23416 					return(0);
23417 				}
23418 				preg->reginput += slen;
23419 			}
23420 			break;
23421 		case ANYOF:
23422 			if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) {
23423 				return(0);
23424 			}
23425 			preg->reginput += n;
23426 			break;
23427 		case ANYBUT:
23428 			if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) {
23429 				return(0);
23430 			}
23431 			preg->reginput += n;
23432 			break;
23433 		case NOTHING:
23434 			break;
23435 		case BACK:
23436 			break;
23437 		case BRANCH:
23438 			if (OP(preg, next) != BRANCH)
23439 				next = OPERAND(scan);
23440 			else {
23441 				do {
23442 					save = preg->reginput;
23443 					if (regmatch(preg, OPERAND(scan))) {
23444 						return(1);
23445 					}
23446 					preg->reginput = save;
23447 					scan = regnext(preg, scan);
23448 				} while (scan != 0 && OP(preg, scan) == BRANCH);
23449 				return(0);
23450 
23451 			}
23452 			break;
23453 		case REP:
23454 		case REPMIN:
23455 			return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);
23456 
23457 		case REPX:
23458 		case REPXMIN:
23459 			return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);
23460 
23461 		case END:
23462 			return 1;
23463 
23464 		case OPENNC:
23465 		case CLOSENC:
23466 			return regmatch(preg, next);
23467 
23468 		default:
23469 			if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
23470 				save = preg->reginput;
23471 				if (regmatch(preg, next)) {
23472 					if (OP(preg, scan) < CLOSE) {
23473 						int no = OP(preg, scan) - OPEN;
23474 						if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
23475 							preg->pmatch[no].rm_so = save - preg->start;
23476 						}
23477 					}
23478 					else {
23479 						int no = OP(preg, scan) - CLOSE;
23480 						if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
23481 							preg->pmatch[no].rm_eo = save - preg->start;
23482 						}
23483 					}
23484 					return(1);
23485 				}
23486 
23487 				preg->reginput = save;
23488 				return(0);
23489 			}
23490 			return REG_ERR_INTERNAL;
23491 		}
23492 
23493 		scan = next;
23494 	}
23495 
23496 	return REG_ERR_INTERNAL;
23497 }
23498 
regrepeat(regex_t * preg,int p,int max)23499 static int regrepeat(regex_t *preg, int p, int max)
23500 {
23501 	int count = 0;
23502 	const char *scan;
23503 	int opnd;
23504 	int ch;
23505 	int n;
23506 
23507 	scan = preg->reginput;
23508 	opnd = OPERAND(p);
23509 	switch (OP(preg, p)) {
23510 	case ANY:
23511 		while (!reg_iseol(preg, *scan) && count < max) {
23512 			count++;
23513 			scan += utf8_charlen(*scan);
23514 		}
23515 		break;
23516 	case EXACTLY:
23517 		while (count < max) {
23518 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
23519 			if (preg->program[opnd] != ch) {
23520 				break;
23521 			}
23522 			count++;
23523 			scan += n;
23524 		}
23525 		break;
23526 	case ANYOF:
23527 		while (count < max) {
23528 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
23529 			if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) {
23530 				break;
23531 			}
23532 			count++;
23533 			scan += n;
23534 		}
23535 		break;
23536 	case ANYBUT:
23537 		while (count < max) {
23538 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
23539 			if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
23540 				break;
23541 			}
23542 			count++;
23543 			scan += n;
23544 		}
23545 		break;
23546 	default:
23547 		preg->err = REG_ERR_INTERNAL;
23548 		count = 0;
23549 		break;
23550 	}
23551 	preg->reginput = scan;
23552 
23553 	return(count);
23554 }
23555 
regnext(regex_t * preg,int p)23556 static int regnext(regex_t *preg, int p )
23557 {
23558 	int offset;
23559 
23560 	offset = NEXT(preg, p);
23561 
23562 	if (offset == 0)
23563 		return 0;
23564 
23565 	if (OP(preg, p) == BACK)
23566 		return(p-offset);
23567 	else
23568 		return(p+offset);
23569 }
23570 
regopsize(regex_t * preg,int p)23571 static int regopsize(regex_t *preg, int p )
23572 {
23573 
23574 	switch (OP(preg, p)) {
23575 		case REP:
23576 		case REPMIN:
23577 		case REPX:
23578 		case REPXMIN:
23579 			return 5;
23580 
23581 		case ANYOF:
23582 		case ANYBUT:
23583 		case EXACTLY: {
23584 			int s = p + 2;
23585 			while (preg->program[s++]) {
23586 			}
23587 			return s - p;
23588 		}
23589 	}
23590 	return 2;
23591 }
23592 
23593 
jim_regerror(int errcode,const regex_t * preg,char * errbuf,size_t errbuf_size)23594 size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf,  size_t errbuf_size)
23595 {
23596 	static const char *error_strings[] = {
23597 		"success",
23598 		"no match",
23599 		"bad pattern",
23600 		"null argument",
23601 		"unknown error",
23602 		"too big",
23603 		"out of memory",
23604 		"too many ()",
23605 		"parentheses () not balanced",
23606 		"braces {} not balanced",
23607 		"invalid repetition count(s)",
23608 		"extra characters",
23609 		"*+ of empty atom",
23610 		"nested count",
23611 		"internal error",
23612 		"count follows nothing",
23613 		"invalid escape \\ sequence",
23614 		"corrupted program",
23615 		"contains null char",
23616 		"brackets [] not balanced",
23617 	};
23618 	const char *err;
23619 
23620 	if (errcode < 0 || errcode >= REG_ERR_NUM) {
23621 		err = "Bad error code";
23622 	}
23623 	else {
23624 		err = error_strings[errcode];
23625 	}
23626 
23627 	return snprintf(errbuf, errbuf_size, "%s", err);
23628 }
23629 
jim_regfree(regex_t * preg)23630 void jim_regfree(regex_t *preg)
23631 {
23632 	free(preg->program);
23633 }
23634 
23635 #endif
23636 #include <string.h>
23637 
Jim_SetResultErrno(Jim_Interp * interp,const char * msg)23638 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
23639 {
23640     Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno()));
23641 }
23642 
23643 #if defined(_WIN32) || defined(WIN32)
23644 #include <sys/stat.h>
23645 
Jim_Errno(void)23646 int Jim_Errno(void)
23647 {
23648     switch (GetLastError()) {
23649     case ERROR_FILE_NOT_FOUND: return ENOENT;
23650     case ERROR_PATH_NOT_FOUND: return ENOENT;
23651     case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
23652     case ERROR_ACCESS_DENIED: return EACCES;
23653     case ERROR_INVALID_HANDLE: return EBADF;
23654     case ERROR_BAD_ENVIRONMENT: return E2BIG;
23655     case ERROR_BAD_FORMAT: return ENOEXEC;
23656     case ERROR_INVALID_ACCESS: return EACCES;
23657     case ERROR_INVALID_DRIVE: return ENOENT;
23658     case ERROR_CURRENT_DIRECTORY: return EACCES;
23659     case ERROR_NOT_SAME_DEVICE: return EXDEV;
23660     case ERROR_NO_MORE_FILES: return ENOENT;
23661     case ERROR_WRITE_PROTECT: return EROFS;
23662     case ERROR_BAD_UNIT: return ENXIO;
23663     case ERROR_NOT_READY: return EBUSY;
23664     case ERROR_BAD_COMMAND: return EIO;
23665     case ERROR_CRC: return EIO;
23666     case ERROR_BAD_LENGTH: return EIO;
23667     case ERROR_SEEK: return EIO;
23668     case ERROR_WRITE_FAULT: return EIO;
23669     case ERROR_READ_FAULT: return EIO;
23670     case ERROR_GEN_FAILURE: return EIO;
23671     case ERROR_SHARING_VIOLATION: return EACCES;
23672     case ERROR_LOCK_VIOLATION: return EACCES;
23673     case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
23674     case ERROR_HANDLE_DISK_FULL: return ENOSPC;
23675     case ERROR_NOT_SUPPORTED: return ENODEV;
23676     case ERROR_REM_NOT_LIST: return EBUSY;
23677     case ERROR_DUP_NAME: return EEXIST;
23678     case ERROR_BAD_NETPATH: return ENOENT;
23679     case ERROR_NETWORK_BUSY: return EBUSY;
23680     case ERROR_DEV_NOT_EXIST: return ENODEV;
23681     case ERROR_TOO_MANY_CMDS: return EAGAIN;
23682     case ERROR_ADAP_HDW_ERR: return EIO;
23683     case ERROR_BAD_NET_RESP: return EIO;
23684     case ERROR_UNEXP_NET_ERR: return EIO;
23685     case ERROR_NETNAME_DELETED: return ENOENT;
23686     case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
23687     case ERROR_BAD_DEV_TYPE: return ENODEV;
23688     case ERROR_BAD_NET_NAME: return ENOENT;
23689     case ERROR_TOO_MANY_NAMES: return ENFILE;
23690     case ERROR_TOO_MANY_SESS: return EIO;
23691     case ERROR_SHARING_PAUSED: return EAGAIN;
23692     case ERROR_REDIR_PAUSED: return EAGAIN;
23693     case ERROR_FILE_EXISTS: return EEXIST;
23694     case ERROR_CANNOT_MAKE: return ENOSPC;
23695     case ERROR_OUT_OF_STRUCTURES: return ENFILE;
23696     case ERROR_ALREADY_ASSIGNED: return EEXIST;
23697     case ERROR_INVALID_PASSWORD: return EPERM;
23698     case ERROR_NET_WRITE_FAULT: return EIO;
23699     case ERROR_NO_PROC_SLOTS: return EAGAIN;
23700     case ERROR_DISK_CHANGE: return EXDEV;
23701     case ERROR_BROKEN_PIPE: return EPIPE;
23702     case ERROR_OPEN_FAILED: return ENOENT;
23703     case ERROR_DISK_FULL: return ENOSPC;
23704     case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
23705     case ERROR_INVALID_TARGET_HANDLE: return EBADF;
23706     case ERROR_INVALID_NAME: return ENOENT;
23707     case ERROR_PROC_NOT_FOUND: return ESRCH;
23708     case ERROR_WAIT_NO_CHILDREN: return ECHILD;
23709     case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
23710     case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
23711     case ERROR_SEEK_ON_DEVICE: return ESPIPE;
23712     case ERROR_BUSY_DRIVE: return EAGAIN;
23713     case ERROR_DIR_NOT_EMPTY: return EEXIST;
23714     case ERROR_NOT_LOCKED: return EACCES;
23715     case ERROR_BAD_PATHNAME: return ENOENT;
23716     case ERROR_LOCK_FAILED: return EACCES;
23717     case ERROR_ALREADY_EXISTS: return EEXIST;
23718     case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
23719     case ERROR_BAD_PIPE: return EPIPE;
23720     case ERROR_PIPE_BUSY: return EAGAIN;
23721     case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
23722     case ERROR_DIRECTORY: return ENOTDIR;
23723     }
23724     return EINVAL;
23725 }
23726 
JimProcessPid(phandle_t pid)23727 long JimProcessPid(phandle_t pid)
23728 {
23729     if (pid == INVALID_HANDLE_VALUE) {
23730         return -1;
23731     }
23732     return GetProcessId(pid);
23733 }
23734 
JimWaitPid(long pid,int * status,int nohang)23735 phandle_t JimWaitPid(long pid, int *status, int nohang)
23736 {
23737     if (pid > 0) {
23738         HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid);
23739         if (h) {
23740             long pid = waitpid(h, status, nohang);
23741             CloseHandle(h);
23742             if (pid > 0) {
23743                 return h;
23744             }
23745         }
23746     }
23747     return JIM_BAD_PHANDLE;
23748 }
23749 
waitpid(phandle_t phandle,int * status,int nohang)23750 long waitpid(phandle_t phandle, int *status, int nohang)
23751 {
23752     long pid;
23753     DWORD ret = WaitForSingleObject(phandle, nohang ? 0 : INFINITE);
23754     if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
23755 
23756         return -1;
23757     }
23758     GetExitCodeProcess(phandle, &ret);
23759     *status = ret;
23760 
23761     pid = GetProcessId(phandle);
23762     CloseHandle(phandle);
23763     return pid;
23764 }
23765 
Jim_MakeTempFile(Jim_Interp * interp,const char * filename_template,int unlink_file)23766 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
23767 {
23768     char name[MAX_PATH];
23769     HANDLE handle;
23770 
23771     if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) {
23772         return -1;
23773     }
23774 
23775     handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
23776             CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0),
23777             NULL);
23778 
23779     if (handle == INVALID_HANDLE_VALUE) {
23780         goto error;
23781     }
23782 
23783     Jim_SetResultString(interp, name, -1);
23784     return _open_osfhandle((intptr_t)handle, _O_RDWR | _O_TEXT);
23785 
23786   error:
23787     Jim_SetResultErrno(interp, name);
23788     DeleteFile(name);
23789     return -1;
23790 }
23791 
Jim_OpenForWrite(const char * filename,int append)23792 int Jim_OpenForWrite(const char *filename, int append)
23793 {
23794     if (strcmp(filename, "/dev/null") == 0) {
23795         filename = "nul:";
23796     }
23797     int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE);
23798     if (fd >= 0 && append) {
23799 
23800         _lseek(fd, 0L, SEEK_END);
23801     }
23802     return fd;
23803 }
23804 
Jim_OpenForRead(const char * filename)23805 int Jim_OpenForRead(const char *filename)
23806 {
23807     if (strcmp(filename, "/dev/null") == 0) {
23808         filename = "nul:";
23809     }
23810     return _open(filename, _O_RDONLY | _O_TEXT, 0);
23811 }
23812 
23813 #elif defined(HAVE_UNISTD_H)
23814 
23815 
23816 
Jim_MakeTempFile(Jim_Interp * interp,const char * filename_template,int unlink_file)23817 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
23818 {
23819     int fd;
23820     mode_t mask;
23821     Jim_Obj *filenameObj;
23822 
23823     if (filename_template == NULL) {
23824         const char *tmpdir = getenv("TMPDIR");
23825         if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
23826             tmpdir = "/tmp/";
23827         }
23828         filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
23829         if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
23830             Jim_AppendString(interp, filenameObj, "/", 1);
23831         }
23832         Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
23833     }
23834     else {
23835         filenameObj = Jim_NewStringObj(interp, filename_template, -1);
23836     }
23837 
23838 
23839 #ifdef HAVE_UMASK
23840     mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
23841 #endif
23842 #ifdef HAVE_MKSTEMP
23843     fd = mkstemp(filenameObj->bytes);
23844 #else
23845     if (mktemp(filenameObj->bytes) == NULL) {
23846         fd = -1;
23847     }
23848     else {
23849         fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC);
23850     }
23851 #endif
23852 #ifdef HAVE_UMASK
23853     umask(mask);
23854 #endif
23855     if (fd < 0) {
23856         Jim_SetResultErrno(interp, Jim_String(filenameObj));
23857         Jim_FreeNewObj(interp, filenameObj);
23858         return -1;
23859     }
23860     if (unlink_file) {
23861         remove(Jim_String(filenameObj));
23862     }
23863 
23864     Jim_SetResult(interp, filenameObj);
23865     return fd;
23866 }
23867 
Jim_OpenForWrite(const char * filename,int append)23868 int Jim_OpenForWrite(const char *filename, int append)
23869 {
23870     return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
23871 }
23872 
Jim_OpenForRead(const char * filename)23873 int Jim_OpenForRead(const char *filename)
23874 {
23875     return open(filename, O_RDONLY, 0);
23876 }
23877 
23878 #endif
23879 
23880 #if defined(_WIN32) || defined(WIN32)
23881 #ifndef STRICT
23882 #define STRICT
23883 #endif
23884 #define WIN32_LEAN_AND_MEAN
23885 #include <windows.h>
23886 
23887 #if defined(HAVE_DLOPEN_COMPAT)
dlopen(const char * path,int mode)23888 void *dlopen(const char *path, int mode)
23889 {
23890     JIM_NOTUSED(mode);
23891 
23892     return (void *)LoadLibraryA(path);
23893 }
23894 
dlclose(void * handle)23895 int dlclose(void *handle)
23896 {
23897     FreeLibrary((HANDLE)handle);
23898     return 0;
23899 }
23900 
dlsym(void * handle,const char * symbol)23901 void *dlsym(void *handle, const char *symbol)
23902 {
23903     return GetProcAddress((HMODULE)handle, symbol);
23904 }
23905 
dlerror(void)23906 char *dlerror(void)
23907 {
23908     static char msg[121];
23909     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
23910                    LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
23911     return msg;
23912 }
23913 #endif
23914 
23915 #ifdef _MSC_VER
23916 
23917 #include <sys/timeb.h>
23918 
23919 
gettimeofday(struct timeval * tv,void * unused)23920 int gettimeofday(struct timeval *tv, void *unused)
23921 {
23922     struct _timeb tb;
23923 
23924     _ftime(&tb);
23925     tv->tv_sec = tb.time;
23926     tv->tv_usec = tb.millitm * 1000;
23927 
23928     return 0;
23929 }
23930 
23931 
opendir(const char * name)23932 DIR *opendir(const char *name)
23933 {
23934     DIR *dir = 0;
23935 
23936     if (name && name[0]) {
23937         size_t base_length = strlen(name);
23938         const char *all =
23939             strchr("/\\", name[base_length - 1]) ? "*" : "/*";
23940 
23941         if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
23942             (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
23943             strcat(strcpy(dir->name, name), all);
23944 
23945             if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
23946                 dir->result.d_name = 0;
23947             else {
23948                 Jim_Free(dir->name);
23949                 Jim_Free(dir);
23950                 dir = 0;
23951             }
23952         }
23953         else {
23954             Jim_Free(dir);
23955             dir = 0;
23956             errno = ENOMEM;
23957         }
23958     }
23959     else {
23960         errno = EINVAL;
23961     }
23962     return dir;
23963 }
23964 
closedir(DIR * dir)23965 int closedir(DIR * dir)
23966 {
23967     int result = -1;
23968 
23969     if (dir) {
23970         if (dir->handle != -1)
23971             result = _findclose(dir->handle);
23972         Jim_Free(dir->name);
23973         Jim_Free(dir);
23974     }
23975     if (result == -1)
23976         errno = EBADF;
23977     return result;
23978 }
23979 
readdir(DIR * dir)23980 struct dirent *readdir(DIR * dir)
23981 {
23982     struct dirent *result = 0;
23983 
23984     if (dir && dir->handle != -1) {
23985         if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
23986             result = &dir->result;
23987             result->d_name = dir->info.name;
23988         }
23989     }
23990     else {
23991         errno = EBADF;
23992     }
23993     return result;
23994 }
23995 #endif
23996 #endif
23997 #include <stdio.h>
23998 #include <signal.h>
23999 
24000 
24001 
24002 
24003 
24004 
24005 #ifndef SIGPIPE
24006 #define SIGPIPE 13
24007 #endif
24008 #ifndef SIGINT
24009 #define SIGINT 2
24010 #endif
24011 
Jim_SignalId(int sig)24012 const char *Jim_SignalId(int sig)
24013 {
24014 	static char buf[10];
24015 	switch (sig) {
24016 		case SIGINT: return "SIGINT";
24017 		case SIGPIPE: return "SIGPIPE";
24018 
24019 	}
24020 	snprintf(buf, sizeof(buf), "%d", sig);
24021 	return buf;
24022 }
24023 #ifndef JIM_BOOTSTRAP_LIB_ONLY
24024 #include <errno.h>
24025 #include <string.h>
24026 #include <stdio.h>
24027 
24028 
24029 #ifdef USE_LINENOISE
24030 #ifdef HAVE_UNISTD_H
24031     #include <unistd.h>
24032 #endif
24033 #ifdef HAVE_SYS_STAT_H
24034     #include <sys/stat.h>
24035 #endif
24036 #include "linenoise.h"
24037 #else
24038 #define MAX_LINE_LEN 512
24039 #endif
24040 
24041 #ifdef USE_LINENOISE
24042 struct JimCompletionInfo {
24043     Jim_Interp *interp;
24044     Jim_Obj *completion_command;
24045     Jim_Obj *hints_command;
24046 
24047 };
24048 
24049 static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp);
24050 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata);
24051 static const char completion_callback_assoc_key[] = "interactive-completion";
24052 static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata);
24053 static void JimFreeHintsCallback(void *hint, void *userdata);
24054 #endif
24055 
Jim_HistoryGetline(Jim_Interp * interp,const char * prompt)24056 char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
24057 {
24058 #ifdef USE_LINENOISE
24059     struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
24060     char *result;
24061     Jim_Obj *objPtr;
24062     long mlmode = 0;
24063     if (compinfo->completion_command) {
24064         linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
24065     }
24066     if (compinfo->hints_command) {
24067         linenoiseSetHintsCallback(JimHintsCallback, compinfo);
24068         linenoiseSetFreeHintsCallback(JimFreeHintsCallback);
24069     }
24070     objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE);
24071     if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) {
24072         linenoiseSetMultiLine(mlmode);
24073     }
24074 
24075     result = linenoise(prompt);
24076 
24077     linenoiseSetCompletionCallback(NULL, NULL);
24078     linenoiseSetHintsCallback(NULL, NULL);
24079     linenoiseSetFreeHintsCallback(NULL);
24080     return result;
24081 #else
24082     int len;
24083     char *line = Jim_Alloc(MAX_LINE_LEN);
24084 
24085     fputs(prompt, stdout);
24086     fflush(stdout);
24087 
24088     if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
24089         Jim_Free(line);
24090         return NULL;
24091     }
24092     len = strlen(line);
24093     if (len && line[len - 1] == '\n') {
24094         line[len - 1] = '\0';
24095     }
24096     return line;
24097 #endif
24098 }
24099 
Jim_HistoryLoad(const char * filename)24100 void Jim_HistoryLoad(const char *filename)
24101 {
24102 #ifdef USE_LINENOISE
24103     linenoiseHistoryLoad(filename);
24104 #endif
24105 }
24106 
Jim_HistoryAdd(const char * line)24107 void Jim_HistoryAdd(const char *line)
24108 {
24109 #ifdef USE_LINENOISE
24110     linenoiseHistoryAdd(line);
24111 #endif
24112 }
24113 
Jim_HistorySave(const char * filename)24114 void Jim_HistorySave(const char *filename)
24115 {
24116 #ifdef USE_LINENOISE
24117 #ifdef HAVE_UMASK
24118     mode_t mask;
24119 
24120     mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
24121 #endif
24122     linenoiseHistorySave(filename);
24123 #ifdef HAVE_UMASK
24124     umask(mask);
24125 #endif
24126 #endif
24127 }
24128 
Jim_HistoryShow(void)24129 void Jim_HistoryShow(void)
24130 {
24131 #ifdef USE_LINENOISE
24132 
24133     int i;
24134     int len;
24135     char **history = linenoiseHistory(&len);
24136     for (i = 0; i < len; i++) {
24137         printf("%4d %s\n", i + 1, history[i]);
24138     }
24139 #endif
24140 }
24141 
Jim_HistorySetMaxLen(int length)24142 void Jim_HistorySetMaxLen(int length)
24143 {
24144 #ifdef USE_LINENOISE
24145     linenoiseHistorySetMaxLen(length);
24146 #endif
24147 }
24148 
Jim_HistoryGetMaxLen(void)24149 int Jim_HistoryGetMaxLen(void)
24150 {
24151 #ifdef USE_LINENOISE
24152     return linenoiseHistoryGetMaxLen();
24153 #endif
24154     return 0;
24155 }
24156 
24157 #ifdef USE_LINENOISE
JimCompletionCallback(const char * prefix,linenoiseCompletions * comp,void * userdata)24158 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
24159 {
24160     struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
24161     Jim_Obj *objv[2];
24162     int ret;
24163 
24164     objv[0] = info->completion_command;
24165     objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
24166 
24167     ret = Jim_EvalObjVector(info->interp, 2, objv);
24168 
24169 
24170     if (ret == JIM_OK) {
24171         int i;
24172         Jim_Obj *listObj = Jim_GetResult(info->interp);
24173         int len = Jim_ListLength(info->interp, listObj);
24174         for (i = 0; i < len; i++) {
24175             linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
24176         }
24177     }
24178 }
24179 
JimHintsCallback(const char * prefix,int * color,int * bold,void * userdata)24180 static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata)
24181 {
24182     struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
24183     Jim_Obj *objv[2];
24184     int ret;
24185     char *result = NULL;
24186 
24187     objv[0] = info->hints_command;
24188     objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
24189 
24190     ret = Jim_EvalObjVector(info->interp, 2, objv);
24191 
24192 
24193     if (ret == JIM_OK) {
24194         Jim_Obj *listObj = Jim_GetResult(info->interp);
24195         Jim_IncrRefCount(listObj);
24196 
24197         int len = Jim_ListLength(info->interp, listObj);
24198         if (len >= 1) {
24199             long x;
24200             result = Jim_StrDup(Jim_String(Jim_ListGetIndex(info->interp, listObj, 0)));
24201             if (len >= 2 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 1), &x) == JIM_OK) {
24202                 *color = x;
24203             }
24204             if (len >= 3 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 2), &x) == JIM_OK) {
24205                 *bold = x;
24206             }
24207         }
24208         Jim_DecrRefCount(info->interp, listObj);
24209     }
24210     return result;
24211 }
24212 
JimFreeHintsCallback(void * hint,void * userdata)24213 static void JimFreeHintsCallback(void *hint, void *userdata)
24214 {
24215     Jim_Free(hint);
24216 }
24217 
JimHistoryFreeCompletion(Jim_Interp * interp,void * data)24218 static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
24219 {
24220     struct JimCompletionInfo *compinfo = data;
24221 
24222     if (compinfo->completion_command) {
24223         Jim_DecrRefCount(interp, compinfo->completion_command);
24224     }
24225     if (compinfo->hints_command) {
24226         Jim_DecrRefCount(interp, compinfo->hints_command);
24227     }
24228 
24229     Jim_Free(compinfo);
24230 }
24231 
JimGetCompletionInfo(Jim_Interp * interp)24232 static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp)
24233 {
24234     struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key);
24235     if (compinfo == NULL) {
24236         compinfo = Jim_Alloc(sizeof(*compinfo));
24237         compinfo->interp = interp;
24238         compinfo->completion_command = NULL;
24239         compinfo->hints_command = NULL;
24240         Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
24241     }
24242     return compinfo;
24243 }
24244 #endif
24245 
Jim_HistorySetCompletion(Jim_Interp * interp,Jim_Obj * completionCommandObj)24246 void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj)
24247 {
24248 #ifdef USE_LINENOISE
24249     struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
24250 
24251     if (completionCommandObj) {
24252 
24253         Jim_IncrRefCount(completionCommandObj);
24254     }
24255     if (compinfo->completion_command) {
24256         Jim_DecrRefCount(interp, compinfo->completion_command);
24257     }
24258     compinfo->completion_command = completionCommandObj;
24259 #endif
24260 }
24261 
Jim_HistorySetHints(Jim_Interp * interp,Jim_Obj * hintsCommandObj)24262 void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj)
24263 {
24264 #ifdef USE_LINENOISE
24265     struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
24266 
24267     if (hintsCommandObj) {
24268 
24269         Jim_IncrRefCount(hintsCommandObj);
24270     }
24271     if (compinfo->hints_command) {
24272         Jim_DecrRefCount(interp, compinfo->hints_command);
24273     }
24274     compinfo->hints_command = hintsCommandObj;
24275 #endif
24276 }
24277 
Jim_InteractivePrompt(Jim_Interp * interp)24278 int Jim_InteractivePrompt(Jim_Interp *interp)
24279 {
24280     int retcode = JIM_OK;
24281     char *history_file = NULL;
24282 #ifdef USE_LINENOISE
24283     const char *home;
24284 
24285     home = getenv("HOME");
24286     if (home && isatty(STDIN_FILENO)) {
24287         int history_len = strlen(home) + sizeof("/.jim_history");
24288         history_file = Jim_Alloc(history_len);
24289         snprintf(history_file, history_len, "%s/.jim_history", home);
24290         Jim_HistoryLoad(history_file);
24291     }
24292 
24293     Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
24294     Jim_HistorySetHints(interp, Jim_NewStringObj(interp, "tcl::stdhint", -1));
24295 #endif
24296 
24297     printf("Welcome to Jim version %d.%d\n",
24298         JIM_VERSION / 100, JIM_VERSION % 100);
24299     Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
24300 
24301     while (1) {
24302         Jim_Obj *scriptObjPtr;
24303         const char *result;
24304         int reslen;
24305         char prompt[20];
24306 
24307         if (retcode != JIM_OK) {
24308             const char *retcodestr = Jim_ReturnCode(retcode);
24309 
24310             if (*retcodestr == '?') {
24311                 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
24312             }
24313             else {
24314                 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
24315             }
24316         }
24317         else {
24318             strcpy(prompt, ". ");
24319         }
24320 
24321         scriptObjPtr = Jim_NewStringObj(interp, "", 0);
24322         Jim_IncrRefCount(scriptObjPtr);
24323         while (1) {
24324             char state;
24325             char *line;
24326 
24327             line = Jim_HistoryGetline(interp, prompt);
24328             if (line == NULL) {
24329                 if (errno == EINTR) {
24330                     continue;
24331                 }
24332                 Jim_DecrRefCount(interp, scriptObjPtr);
24333                 retcode = JIM_OK;
24334                 goto out;
24335             }
24336             if (Jim_Length(scriptObjPtr) != 0) {
24337 
24338                 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
24339             }
24340             Jim_AppendString(interp, scriptObjPtr, line, -1);
24341             Jim_Free(line);
24342             if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
24343                 break;
24344 
24345             snprintf(prompt, sizeof(prompt), "%c> ", state);
24346         }
24347 #ifdef USE_LINENOISE
24348         if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
24349 
24350             Jim_HistoryShow();
24351             Jim_DecrRefCount(interp, scriptObjPtr);
24352             continue;
24353         }
24354 
24355         Jim_HistoryAdd(Jim_String(scriptObjPtr));
24356         if (history_file) {
24357             Jim_HistorySave(history_file);
24358         }
24359 #endif
24360         retcode = Jim_EvalObj(interp, scriptObjPtr);
24361         Jim_DecrRefCount(interp, scriptObjPtr);
24362 
24363         if (retcode == JIM_EXIT) {
24364             break;
24365         }
24366         if (retcode == JIM_ERR) {
24367             Jim_MakeErrorMessage(interp);
24368         }
24369         result = Jim_GetString(Jim_GetResult(interp), &reslen);
24370         if (reslen) {
24371             if (fwrite(result, reslen, 1, stdout) == 0) {
24372 
24373             }
24374             putchar('\n');
24375         }
24376     }
24377   out:
24378     Jim_Free(history_file);
24379 
24380     return retcode;
24381 }
24382 
24383 #include <stdio.h>
24384 #include <stdlib.h>
24385 #include <string.h>
24386 
24387 
24388 
24389 extern int Jim_initjimshInit(Jim_Interp *interp);
24390 
JimSetArgv(Jim_Interp * interp,int argc,char * const argv[])24391 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
24392 {
24393     int n;
24394     Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
24395 
24396 
24397     for (n = 0; n < argc; n++) {
24398         Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
24399 
24400         Jim_ListAppendElement(interp, listObj, obj);
24401     }
24402 
24403     Jim_SetVariableStr(interp, "argv", listObj);
24404     Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
24405 }
24406 
JimPrintErrorMessage(Jim_Interp * interp)24407 static void JimPrintErrorMessage(Jim_Interp *interp)
24408 {
24409     Jim_MakeErrorMessage(interp);
24410     fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
24411 }
24412 
usage(const char * executable_name)24413 void usage(const char* executable_name)
24414 {
24415     printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
24416     printf("Usage: %s\n", executable_name);
24417     printf("or   : %s [options] [filename]\n", executable_name);
24418     printf("\n");
24419     printf("Without options: Interactive mode\n");
24420     printf("\n");
24421     printf("Options:\n");
24422     printf("      --version  : prints the version string\n");
24423     printf("      --help     : prints this text\n");
24424     printf("      -e CMD     : executes command CMD\n");
24425     printf("                   NOTE: all subsequent options will be passed as arguments to the command\n");
24426     printf("    [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n");
24427     printf("                   NOTE: all subsequent options will be passed to the script\n\n");
24428 }
24429 
main(int argc,char * const argv[])24430 int main(int argc, char *const argv[])
24431 {
24432     int retcode;
24433     Jim_Interp *interp;
24434     char *const orig_argv0 = argv[0];
24435 
24436 
24437     if (argc > 1 && strcmp(argv[1], "--version") == 0) {
24438         printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
24439         return 0;
24440     }
24441     else if (argc > 1 && strcmp(argv[1], "--help") == 0) {
24442         usage(argv[0]);
24443         return 0;
24444     }
24445 
24446 
24447     interp = Jim_CreateInterp();
24448     Jim_RegisterCoreCommands(interp);
24449 
24450 
24451     if (Jim_InitStaticExtensions(interp) != JIM_OK) {
24452         JimPrintErrorMessage(interp);
24453     }
24454 
24455     Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
24456     Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
24457 #ifdef USE_LINENOISE
24458     Jim_SetVariableStrWithStr(interp, "jim::lineedit", "1");
24459 #else
24460     Jim_SetVariableStrWithStr(interp, "jim::lineedit", "0");
24461 #endif
24462     retcode = Jim_initjimshInit(interp);
24463 
24464     if (argc == 1) {
24465 
24466         if (retcode == JIM_ERR) {
24467             JimPrintErrorMessage(interp);
24468         }
24469         if (retcode != JIM_EXIT) {
24470             JimSetArgv(interp, 0, NULL);
24471             if (!isatty(STDIN_FILENO)) {
24472 
24473                 goto eval_stdin;
24474             }
24475             retcode = Jim_InteractivePrompt(interp);
24476         }
24477     }
24478     else {
24479 
24480         if (argc > 2 && strcmp(argv[1], "-e") == 0) {
24481 
24482             JimSetArgv(interp, argc - 3, argv + 3);
24483             retcode = Jim_Eval(interp, argv[2]);
24484             if (retcode != JIM_ERR) {
24485                 int len;
24486                 const char *msg = Jim_GetString(Jim_GetResult(interp), &len);
24487                 if (fwrite(msg, len, 1, stdout) == 0) {
24488 
24489                 }
24490                 putchar('\n');
24491             }
24492         }
24493         else {
24494             Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
24495             JimSetArgv(interp, argc - 2, argv + 2);
24496             if (strcmp(argv[1], "-") == 0) {
24497 eval_stdin:
24498                 retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]");
24499             } else {
24500                 retcode = Jim_EvalFile(interp, argv[1]);
24501             }
24502         }
24503         if (retcode == JIM_ERR) {
24504             JimPrintErrorMessage(interp);
24505         }
24506     }
24507     if (retcode == JIM_EXIT) {
24508         retcode = Jim_GetExitCode(interp);
24509     }
24510     else if (retcode == JIM_ERR) {
24511         retcode = 1;
24512     }
24513     else {
24514         retcode = 0;
24515     }
24516     Jim_FreeInterp(interp);
24517     return retcode;
24518 }
24519 #endif
24520