1 /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */
2 #define JIM_TCL_COMPAT
3 #define JIM_ANSIC
4 #define JIM_REGEXP
5 #define HAVE_NO_AUTOCONF
6 #define _JIMAUTOCONF_H
7 #define TCL_LIBRARY "."
8 #define jim_ext_bootstrap
9 #define jim_ext_aio
10 #define jim_ext_readdir
11 #define jim_ext_regexp
12 #define jim_ext_file
13 #define jim_ext_glob
14 #define jim_ext_exec
15 #define jim_ext_clock
16 #define jim_ext_array
17 #define jim_ext_stdlib
18 #define jim_ext_tclcompat
19 #if defined(_MSC_VER)
20 #define TCL_PLATFORM_OS "windows"
21 #define TCL_PLATFORM_PLATFORM "windows"
22 #define TCL_PLATFORM_PATH_SEPARATOR ";"
23 #define HAVE_MKDIR_ONE_ARG
24 #define HAVE_SYSTEM
25 #elif defined(__MINGW32__)
26 #define TCL_PLATFORM_OS "mingw"
27 #define TCL_PLATFORM_PLATFORM "windows"
28 #define TCL_PLATFORM_PATH_SEPARATOR ";"
29 #define HAVE_MKDIR_ONE_ARG
30 #define HAVE_SYSTEM
31 #define HAVE_SYS_TIME_H
32 #define HAVE_DIRENT_H
33 #define HAVE_UNISTD_H
34 #define HAVE_UMASK
35 #include <sys/stat.h>
36 #ifndef S_IRWXG
37 #define S_IRWXG 0
38 #endif
39 #ifndef S_IRWXO
40 #define S_IRWXO 0
41 #endif
42 #else
43 #define TCL_PLATFORM_OS "unknown"
44 #define TCL_PLATFORM_PLATFORM "unix"
45 #define TCL_PLATFORM_PATH_SEPARATOR ":"
46 #ifdef _MINIX
47 #define vfork fork
48 #define _POSIX_SOURCE
49 #else
50 #define _GNU_SOURCE
51 #endif
52 #define HAVE_VFORK
53 #define HAVE_WAITPID
54 #define HAVE_ISATTY
55 #define HAVE_MKSTEMP
56 #define HAVE_LINK
57 #define HAVE_SYS_TIME_H
58 #define HAVE_DIRENT_H
59 #define HAVE_UNISTD_H
60 #define HAVE_UMASK
61 #endif
62 #define JIM_VERSION 78
63 #ifndef JIM_WIN32COMPAT_H
64 #define JIM_WIN32COMPAT_H
65 
66 
67 
68 #ifdef __cplusplus
69 extern "C" {
70 #endif
71 
72 
73 #if defined(_WIN32) || defined(WIN32)
74 
75 #define HAVE_DLOPEN
76 void *dlopen(const char *path, int mode);
77 int dlclose(void *handle);
78 void *dlsym(void *handle, const char *symbol);
79 char *dlerror(void);
80 
81 
82 #if defined(__MINGW32__)
83     #define JIM_SPRINTF_DOUBLE_NEEDS_FIX
84 #endif
85 
86 #ifdef _MSC_VER
87 
88 
89 #if _MSC_VER >= 1000
90 	#pragma warning(disable:4146)
91 #endif
92 
93 #include <limits.h>
94 #define jim_wide _int64
95 #ifndef LLONG_MAX
96 	#define LLONG_MAX    9223372036854775807I64
97 #endif
98 #ifndef LLONG_MIN
99 	#define LLONG_MIN    (-LLONG_MAX - 1I64)
100 #endif
101 #define JIM_WIDE_MIN LLONG_MIN
102 #define JIM_WIDE_MAX LLONG_MAX
103 #define JIM_WIDE_MODIFIER "I64d"
104 #define strcasecmp _stricmp
105 #define strtoull _strtoui64
106 
107 #include <io.h>
108 
109 struct timeval {
110 	long tv_sec;
111 	long tv_usec;
112 };
113 
114 int gettimeofday(struct timeval *tv, void *unused);
115 
116 #define HAVE_OPENDIR
117 struct dirent {
118 	char *d_name;
119 };
120 
121 typedef struct DIR {
122 	long                handle;
123 	struct _finddata_t  info;
124 	struct dirent       result;
125 	char                *name;
126 } DIR;
127 
128 DIR *opendir(const char *name);
129 int closedir(DIR *dir);
130 struct dirent *readdir(DIR *dir);
131 
132 #endif
133 
134 #endif
135 
136 #ifdef __cplusplus
137 }
138 #endif
139 
140 #endif
141 #ifndef UTF8_UTIL_H
142 #define UTF8_UTIL_H
143 
144 #ifdef __cplusplus
145 extern "C" {
146 #endif
147 
148 
149 
150 #define MAX_UTF8_LEN 4
151 
152 int utf8_fromunicode(char *p, unsigned uc);
153 
154 #ifndef JIM_UTF8
155 #include <ctype.h>
156 
157 
158 #define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
159 #define utf8_strwidth(S, B) utf8_strlen((S), (B))
160 #define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
161 #define utf8_getchars(CP, C) (*(CP) = (C), 1)
162 #define utf8_upper(C) toupper(C)
163 #define utf8_title(C) toupper(C)
164 #define utf8_lower(C) tolower(C)
165 #define utf8_index(C, I) (I)
166 #define utf8_charlen(C) 1
167 #define utf8_prev_len(S, L) 1
168 #define utf8_width(C) 1
169 
170 #else
171 
172 #endif
173 
174 #ifdef __cplusplus
175 }
176 #endif
177 
178 #endif
179 
180 #ifndef __JIM__H
181 #define __JIM__H
182 
183 #ifdef __cplusplus
184 extern "C" {
185 #endif
186 
187 #include <time.h>
188 #include <limits.h>
189 #include <stdio.h>
190 #include <stdlib.h>
191 #include <stdarg.h>
192 
193 
194 #ifndef HAVE_NO_AUTOCONF
195 #endif
196 
197 
198 
199 #ifndef jim_wide
200 #  ifdef HAVE_LONG_LONG
201 #    define jim_wide long long
202 #    ifndef LLONG_MAX
203 #      define LLONG_MAX    9223372036854775807LL
204 #    endif
205 #    ifndef LLONG_MIN
206 #      define LLONG_MIN    (-LLONG_MAX - 1LL)
207 #    endif
208 #    define JIM_WIDE_MIN LLONG_MIN
209 #    define JIM_WIDE_MAX LLONG_MAX
210 #  else
211 #    define jim_wide long
212 #    define JIM_WIDE_MIN LONG_MIN
213 #    define JIM_WIDE_MAX LONG_MAX
214 #  endif
215 
216 
217 #  ifdef HAVE_LONG_LONG
218 #    define JIM_WIDE_MODIFIER "lld"
219 #  else
220 #    define JIM_WIDE_MODIFIER "ld"
221 #    define strtoull strtoul
222 #  endif
223 #endif
224 
225 #define UCHAR(c) ((unsigned char)(c))
226 
227 
228 #define JIM_OK 0
229 #define JIM_ERR 1
230 #define JIM_RETURN 2
231 #define JIM_BREAK 3
232 #define JIM_CONTINUE 4
233 #define JIM_SIGNAL 5
234 #define JIM_EXIT 6
235 
236 #define JIM_EVAL 7
237 
238 #define JIM_MAX_CALLFRAME_DEPTH 1000
239 #define JIM_MAX_EVAL_DEPTH 2000
240 
241 
242 #define JIM_PRIV_FLAG_SHIFT 20
243 
244 #define JIM_NONE 0
245 #define JIM_ERRMSG 1
246 #define JIM_ENUM_ABBREV 2
247 #define JIM_UNSHARED 4
248 #define JIM_MUSTEXIST 8
249 
250 
251 #define JIM_SUBST_NOVAR 1
252 #define JIM_SUBST_NOCMD 2
253 #define JIM_SUBST_NOESC 4
254 #define JIM_SUBST_FLAG 128
255 
256 
257 #define JIM_CASESENS    0
258 #define JIM_NOCASE      1
259 
260 
261 #define JIM_PATH_LEN 1024
262 
263 
264 #define JIM_NOTUSED(V) ((void) V)
265 
266 #define JIM_LIBPATH "auto_path"
267 #define JIM_INTERACTIVE "tcl_interactive"
268 
269 
270 typedef struct Jim_Stack {
271     int len;
272     int maxlen;
273     void **vector;
274 } Jim_Stack;
275 
276 
277 typedef struct Jim_HashEntry {
278     void *key;
279     union {
280         void *val;
281         int intval;
282     } u;
283     struct Jim_HashEntry *next;
284 } Jim_HashEntry;
285 
286 typedef struct Jim_HashTableType {
287     unsigned int (*hashFunction)(const void *key);
288     void *(*keyDup)(void *privdata, const void *key);
289     void *(*valDup)(void *privdata, const void *obj);
290     int (*keyCompare)(void *privdata, const void *key1, const void *key2);
291     void (*keyDestructor)(void *privdata, void *key);
292     void (*valDestructor)(void *privdata, void *obj);
293 } Jim_HashTableType;
294 
295 typedef struct Jim_HashTable {
296     Jim_HashEntry **table;
297     const Jim_HashTableType *type;
298     void *privdata;
299     unsigned int size;
300     unsigned int sizemask;
301     unsigned int used;
302     unsigned int collisions;
303     unsigned int uniq;
304 } Jim_HashTable;
305 
306 typedef struct Jim_HashTableIterator {
307     Jim_HashTable *ht;
308     Jim_HashEntry *entry, *nextEntry;
309     int index;
310 } Jim_HashTableIterator;
311 
312 
313 #define JIM_HT_INITIAL_SIZE     16
314 
315 
316 #define Jim_FreeEntryVal(ht, entry) \
317     if ((ht)->type->valDestructor) \
318         (ht)->type->valDestructor((ht)->privdata, (entry)->u.val)
319 
320 #define Jim_SetHashVal(ht, entry, _val_) do { \
321     if ((ht)->type->valDup) \
322         (entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \
323     else \
324         (entry)->u.val = (_val_); \
325 } while(0)
326 
327 #define Jim_FreeEntryKey(ht, entry) \
328     if ((ht)->type->keyDestructor) \
329         (ht)->type->keyDestructor((ht)->privdata, (entry)->key)
330 
331 #define Jim_SetHashKey(ht, entry, _key_) do { \
332     if ((ht)->type->keyDup) \
333         (entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \
334     else \
335         (entry)->key = (void *)(_key_); \
336 } while(0)
337 
338 #define Jim_CompareHashKeys(ht, key1, key2) \
339     (((ht)->type->keyCompare) ? \
340         (ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \
341         (key1) == (key2))
342 
343 #define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq)
344 
345 #define Jim_GetHashEntryKey(he) ((he)->key)
346 #define Jim_GetHashEntryVal(he) ((he)->u.val)
347 #define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
348 #define Jim_GetHashTableSize(ht) ((ht)->size)
349 #define Jim_GetHashTableUsed(ht) ((ht)->used)
350 
351 
352 typedef struct Jim_Obj {
353     char *bytes;
354     const struct Jim_ObjType *typePtr;
355     int refCount;
356     int length;
357 
358     union {
359 
360         jim_wide wideValue;
361 
362         int intValue;
363 
364         double doubleValue;
365 
366         void *ptr;
367 
368         struct {
369             void *ptr1;
370             void *ptr2;
371         } twoPtrValue;
372 
373         struct {
374             void *ptr;
375             int int1;
376             int int2;
377         } ptrIntValue;
378 
379         struct {
380             struct Jim_Var *varPtr;
381             unsigned long callFrameId;
382             int global;
383         } varValue;
384 
385         struct {
386             struct Jim_Obj *nsObj;
387             struct Jim_Cmd *cmdPtr;
388             unsigned long procEpoch;
389         } cmdValue;
390 
391         struct {
392             struct Jim_Obj **ele;
393             int len;
394             int maxLen;
395         } listValue;
396 
397         struct {
398             int maxLength;
399             int charLength;
400         } strValue;
401 
402         struct {
403             unsigned long id;
404             struct Jim_Reference *refPtr;
405         } refValue;
406 
407         struct {
408             struct Jim_Obj *fileNameObj;
409             int lineNumber;
410         } sourceValue;
411 
412         struct {
413             struct Jim_Obj *varNameObjPtr;
414             struct Jim_Obj *indexObjPtr;
415         } dictSubstValue;
416         struct {
417             int line;
418             int argc;
419         } scriptLineValue;
420     } internalRep;
421     struct Jim_Obj *prevObjPtr;
422     struct Jim_Obj *nextObjPtr;
423 } Jim_Obj;
424 
425 
426 #define Jim_IncrRefCount(objPtr) \
427     ++(objPtr)->refCount
428 #define Jim_DecrRefCount(interp, objPtr) \
429     if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr)
430 #define Jim_IsShared(objPtr) \
431     ((objPtr)->refCount > 1)
432 
433 #define Jim_FreeNewObj Jim_FreeObj
434 
435 
436 #define Jim_FreeIntRep(i,o) \
437     if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \
438         (o)->typePtr->freeIntRepProc(i, o)
439 
440 
441 #define Jim_GetIntRepPtr(o) (o)->internalRep.ptr
442 
443 
444 #define Jim_SetIntRepPtr(o, p) \
445     (o)->internalRep.ptr = (p)
446 
447 
448 struct Jim_Interp;
449 
450 typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp,
451         struct Jim_Obj *objPtr);
452 typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp,
453         struct Jim_Obj *srcPtr, Jim_Obj *dupPtr);
454 typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr);
455 
456 typedef struct Jim_ObjType {
457     const char *name;
458     Jim_FreeInternalRepProc *freeIntRepProc;
459     Jim_DupInternalRepProc *dupIntRepProc;
460     Jim_UpdateStringProc *updateStringProc;
461     int flags;
462 } Jim_ObjType;
463 
464 
465 #define JIM_TYPE_NONE 0
466 #define JIM_TYPE_REFERENCES 1
467 
468 
469 
470 typedef struct Jim_CallFrame {
471     unsigned long id;
472     int level;
473     struct Jim_HashTable vars;
474     struct Jim_HashTable *staticVars;
475     struct Jim_CallFrame *parent;
476     Jim_Obj *const *argv;
477     int argc;
478     Jim_Obj *procArgsObjPtr;
479     Jim_Obj *procBodyObjPtr;
480     struct Jim_CallFrame *next;
481     Jim_Obj *nsObj;
482     Jim_Obj *fileNameObj;
483     int line;
484     Jim_Stack *localCommands;
485     struct Jim_Obj *tailcallObj;
486     struct Jim_Cmd *tailcallCmd;
487 } Jim_CallFrame;
488 
489 typedef struct Jim_Var {
490     Jim_Obj *objPtr;
491     struct Jim_CallFrame *linkFramePtr;
492 } Jim_Var;
493 
494 
495 typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
496     Jim_Obj *const *argv);
497 typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);
498 
499 
500 
501 typedef struct Jim_Cmd {
502     int inUse;
503     int isproc;
504     struct Jim_Cmd *prevCmd;
505     union {
506         struct {
507 
508             Jim_CmdProc *cmdProc;
509             Jim_DelCmdProc *delProc;
510             void *privData;
511         } native;
512         struct {
513 
514             Jim_Obj *argListObjPtr;
515             Jim_Obj *bodyObjPtr;
516             Jim_HashTable *staticVars;
517             int argListLen;
518             int reqArity;
519             int optArity;
520             int argsPos;
521             int upcall;
522             struct Jim_ProcArg {
523                 Jim_Obj *nameObjPtr;
524                 Jim_Obj *defaultObjPtr;
525             } *arglist;
526             Jim_Obj *nsObj;
527         } proc;
528     } u;
529 } Jim_Cmd;
530 
531 
532 typedef struct Jim_PrngState {
533     unsigned char sbox[256];
534     unsigned int i, j;
535 } Jim_PrngState;
536 
537 typedef struct Jim_Interp {
538     Jim_Obj *result;
539     int errorLine;
540     Jim_Obj *errorFileNameObj;
541     int addStackTrace;
542     int maxCallFrameDepth;
543     int maxEvalDepth;
544     int evalDepth;
545     int returnCode;
546     int returnLevel;
547     int exitCode;
548     long id;
549     int signal_level;
550     jim_wide sigmask;
551     int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask);
552     Jim_CallFrame *framePtr;
553     Jim_CallFrame *topFramePtr;
554     struct Jim_HashTable commands;
555     unsigned long procEpoch; /* Incremented every time the result
556                 of procedures names lookup caching
557                 may no longer be valid. */
558     unsigned long callFrameEpoch; /* Incremented every time a new
559                 callframe is created. This id is used for the
560                 'ID' field contained in the Jim_CallFrame
561                 structure. */
562     int local;
563     Jim_Obj *liveList;
564     Jim_Obj *freeList;
565     Jim_Obj *currentScriptObj;
566     Jim_Obj *nullScriptObj;
567     Jim_Obj *emptyObj;
568     Jim_Obj *trueObj;
569     Jim_Obj *falseObj;
570     unsigned long referenceNextId;
571     struct Jim_HashTable references;
572     unsigned long lastCollectId; /* reference max Id of the last GC
573                 execution. It's set to ~0 while the collection
574                 is running as sentinel to avoid to recursive
575                 calls via the [collect] command inside
576                 finalizers. */
577     time_t lastCollectTime;
578     Jim_Obj *stackTrace;
579     Jim_Obj *errorProc;
580     Jim_Obj *unknown;
581     int unknown_called;
582     int errorFlag;
583     void *cmdPrivData; /* Used to pass the private data pointer to
584                   a command. It is set to what the user specified
585                   via Jim_CreateCommand(). */
586 
587     struct Jim_CallFrame *freeFramesList;
588     struct Jim_HashTable assocData;
589     Jim_PrngState *prngState;
590     struct Jim_HashTable packages;
591     Jim_Stack *loadHandles;
592 } Jim_Interp;
593 
594 #define Jim_InterpIncrProcEpoch(i) (i)->procEpoch++
595 #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
596 #define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))
597 
598 #define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
599 #define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj)
600 #define Jim_GetResult(i) ((i)->result)
601 #define Jim_CmdPrivData(i) ((i)->cmdPrivData)
602 
603 #define Jim_SetResult(i,o) do {     \
604     Jim_Obj *_resultObjPtr_ = (o);    \
605     Jim_IncrRefCount(_resultObjPtr_); \
606     Jim_DecrRefCount(i,(i)->result);  \
607     (i)->result = _resultObjPtr_;     \
608 } while(0)
609 
610 
611 #define Jim_GetId(i) (++(i)->id)
612 
613 
614 #define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference
615                                   string representation must be fixed length. */
616 typedef struct Jim_Reference {
617     Jim_Obj *objPtr;
618     Jim_Obj *finalizerCmdNamePtr;
619     char tag[JIM_REFERENCE_TAGLEN+1];
620 } Jim_Reference;
621 
622 
623 #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
624 #define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
625 
626 #define JIM_EXPORT
627 
628 
629 JIM_EXPORT void *Jim_Alloc (int size);
630 JIM_EXPORT void *Jim_Realloc(void *ptr, int size);
631 JIM_EXPORT void Jim_Free (void *ptr);
632 JIM_EXPORT char * Jim_StrDup (const char *s);
633 JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
634 
635 
636 JIM_EXPORT char **Jim_GetEnviron(void);
637 JIM_EXPORT void Jim_SetEnviron(char **env);
638 JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file);
639 
640 
641 JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
642 
643 
644 JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);
645 
646 #define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S))
647 
648 JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script);
649 JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename);
650 JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename);
651 JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr);
652 JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc,
653         Jim_Obj *const *objv);
654 JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj);
655 JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
656         int objc, Jim_Obj *const *objv);
657 #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
658 JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
659 JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
660         Jim_Obj **resObjPtrPtr, int flags);
661 
662 
663 JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
664 JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
665 JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
666 JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
667 JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
668 JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
669 JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr));
670 
671 
672 JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
673         const Jim_HashTableType *type, void *privdata);
674 JIM_EXPORT void Jim_ExpandHashTable (Jim_HashTable *ht,
675         unsigned int size);
676 JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key,
677         void *val);
678 JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
679         const void *key, void *val);
680 JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
681         const void *key);
682 JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
683 JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
684         const void *key);
685 JIM_EXPORT void Jim_ResizeHashTable (Jim_HashTable *ht);
686 JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator
687         (Jim_HashTable *ht);
688 JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
689         (Jim_HashTableIterator *iter);
690 
691 
692 JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
693 JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr);
694 JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr);
695 JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
696         Jim_Obj *objPtr);
697 JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
698         int *lenPtr);
699 JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
700 JIM_EXPORT int Jim_Length(Jim_Obj *objPtr);
701 
702 
703 JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp,
704         const char *s, int len);
705 JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp,
706         const char *s, int charlen);
707 JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp,
708         char *s, int len);
709 JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr,
710         const char *str, int len);
711 JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr,
712         Jim_Obj *appendObjPtr);
713 JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp,
714         Jim_Obj *objPtr, ...);
715 JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr);
716 JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr,
717         Jim_Obj *objPtr, int nocase);
718 JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp,
719         Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr,
720         Jim_Obj *lastObjPtr);
721 JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp,
722         Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
723 JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
724         Jim_Obj *fmtObjPtr, int flags);
725 JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
726         Jim_Obj *objPtr, const char *str);
727 JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
728         Jim_Obj *secondObjPtr, int nocase);
729 JIM_EXPORT int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
730         Jim_Obj *secondObjPtr, int nocase);
731 JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr);
732 
733 
734 JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp,
735         Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr);
736 JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp,
737         Jim_Obj *objPtr);
738 JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr);
739 JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr);
740 
741 
742 JIM_EXPORT Jim_Interp * Jim_CreateInterp (void);
743 JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i);
744 JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp);
745 JIM_EXPORT const char *Jim_ReturnCode(int code);
746 JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...);
747 
748 
749 JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
750 JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
751         const char *cmdName, Jim_CmdProc *cmdProc, void *privData,
752          Jim_DelCmdProc *delProc);
753 JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
754         const char *cmdName);
755 JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
756         const char *oldName, const char *newName);
757 JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
758         Jim_Obj *objPtr, int flags);
759 JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
760         Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
761 JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
762         const char *name, Jim_Obj *objPtr);
763 JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp,
764         const char *name, Jim_Obj *objPtr);
765 JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp,
766         const char *name, const char *val);
767 JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp,
768         Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr,
769         Jim_CallFrame *targetCallFrame);
770 JIM_EXPORT Jim_Obj * Jim_MakeGlobalNamespaceName(Jim_Interp *interp,
771         Jim_Obj *nameObjPtr);
772 JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp,
773         Jim_Obj *nameObjPtr, int flags);
774 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp,
775         Jim_Obj *nameObjPtr, int flags);
776 JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp,
777         const char *name, int flags);
778 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp,
779         const char *name, int flags);
780 JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp,
781         Jim_Obj *nameObjPtr, int flags);
782 
783 
784 JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp,
785         Jim_Obj *levelObjPtr);
786 
787 
788 JIM_EXPORT int Jim_Collect (Jim_Interp *interp);
789 JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp);
790 
791 
792 JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr,
793         int *indexPtr);
794 
795 
796 JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp,
797         Jim_Obj *const *elements, int len);
798 JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp,
799         Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec);
800 JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp,
801         Jim_Obj *listPtr, Jim_Obj *objPtr);
802 JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp,
803         Jim_Obj *listPtr, Jim_Obj *appendListPtr);
804 JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr);
805 JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt,
806         int listindex, Jim_Obj **objPtrPtr, int seterr);
807 JIM_EXPORT Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx);
808 JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp,
809         Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc,
810         Jim_Obj *newObjPtr);
811 JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc,
812         Jim_Obj *const *objv);
813 JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp,
814         Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen);
815 
816 
817 JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp,
818         Jim_Obj *const *elements, int len);
819 JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr,
820         Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
821 JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
822         Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
823         Jim_Obj **objPtrPtr, int flags);
824 JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
825         Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
826         Jim_Obj *newObjPtr, int flags);
827 JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp,
828         Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len);
829 JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
830         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
831 
832 #define JIM_DICTMATCH_KEYS 0x0001
833 #define JIM_DICTMATCH_VALUES 0x002
834 
835 JIM_EXPORT int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types);
836 JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
837 JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr);
838 JIM_EXPORT Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv);
839 
840 
841 JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
842         int *intPtr);
843 
844 
845 JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
846         Jim_Obj *exprObjPtr);
847 JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
848         Jim_Obj *exprObjPtr, int *boolPtr);
849 
850 
851 JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
852         int *booleanPtr);
853 
854 
855 JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
856         jim_wide *widePtr);
857 JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
858         long *longPtr);
859 #define Jim_NewWideObj  Jim_NewIntObj
860 JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
861         jim_wide wideValue);
862 
863 
864 JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
865         double *doublePtr);
866 JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
867         double doubleValue);
868 JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue);
869 
870 
871 JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc,
872         Jim_Obj *const *argv, const char *msg);
873 JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
874         const char * const *tablePtr, int *indexPtr, const char *name, int flags);
875 JIM_EXPORT int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr,
876         const char *const *tablePtr);
877 JIM_EXPORT int Jim_ScriptIsComplete(Jim_Interp *interp,
878         Jim_Obj *scriptObj, char *stateCharPtr);
879 
880 JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);
881 
882 
883 typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
884 JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
885 JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
886         Jim_InterpDeleteProc *delProc, void *data);
887 JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
888 
889 
890 
891 JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
892         const char *name, const char *ver, int flags);
893 JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
894         const char *name, int flags);
895 
896 
897 JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
898 
899 
900 JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
901 JIM_EXPORT void Jim_HistoryLoad(const char *filename);
902 JIM_EXPORT void Jim_HistorySave(const char *filename);
903 JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt);
904 JIM_EXPORT void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj);
905 JIM_EXPORT void Jim_HistoryAdd(const char *line);
906 JIM_EXPORT void Jim_HistoryShow(void);
907 
908 
909 JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
910 JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
911 JIM_EXPORT int Jim_IsBigEndian(void);
912 
913 #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
914 
915 
916 JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
917 JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
918 
919 
920 JIM_EXPORT FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
921 
922 
923 JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
924 JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
925 
926 #ifdef __cplusplus
927 }
928 #endif
929 
930 #endif
931 
932 #ifndef JIM_SUBCMD_H
933 #define JIM_SUBCMD_H
934 
935 
936 #ifdef __cplusplus
937 extern "C" {
938 #endif
939 
940 
941 #define JIM_MODFLAG_HIDDEN   0x0001
942 #define JIM_MODFLAG_FULLARGV 0x0002
943 
944 
945 
946 typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
947 
948 typedef struct {
949 	const char *cmd;
950 	const char *args;
951 	jim_subcmd_function *function;
952 	short minargs;
953 	short maxargs;
954 	unsigned short flags;
955 } jim_subcmd_type;
956 
957 const jim_subcmd_type *
958 Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
959 
960 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
961 
962 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
963 
964 #ifdef __cplusplus
965 }
966 #endif
967 
968 #endif
969 #ifndef JIMREGEXP_H
970 #define JIMREGEXP_H
971 
972 
973 #ifdef __cplusplus
974 extern "C" {
975 #endif
976 
977 #include <stdlib.h>
978 
979 typedef struct {
980 	int rm_so;
981 	int rm_eo;
982 } regmatch_t;
983 
984 
985 typedef struct regexp {
986 
987 	int re_nsub;
988 
989 
990 	int cflags;
991 	int err;
992 	int regstart;
993 	int reganch;
994 	int regmust;
995 	int regmlen;
996 	int *program;
997 
998 
999 	const char *regparse;
1000 	int p;
1001 	int proglen;
1002 
1003 
1004 	int eflags;
1005 	const char *start;
1006 	const char *reginput;
1007 	const char *regbol;
1008 
1009 
1010 	regmatch_t *pmatch;
1011 	int nmatch;
1012 } regexp;
1013 
1014 typedef regexp regex_t;
1015 
1016 #define REG_EXTENDED 0
1017 #define REG_NEWLINE 1
1018 #define REG_ICASE 2
1019 
1020 #define REG_NOTBOL 16
1021 
1022 enum {
1023 	REG_NOERROR,
1024 	REG_NOMATCH,
1025 	REG_BADPAT,
1026 	REG_ERR_NULL_ARGUMENT,
1027 	REG_ERR_UNKNOWN,
1028 	REG_ERR_TOO_BIG,
1029 	REG_ERR_NOMEM,
1030 	REG_ERR_TOO_MANY_PAREN,
1031 	REG_ERR_UNMATCHED_PAREN,
1032 	REG_ERR_UNMATCHED_BRACES,
1033 	REG_ERR_BAD_COUNT,
1034 	REG_ERR_JUNK_ON_END,
1035 	REG_ERR_OPERAND_COULD_BE_EMPTY,
1036 	REG_ERR_NESTED_COUNT,
1037 	REG_ERR_INTERNAL,
1038 	REG_ERR_COUNT_FOLLOWS_NOTHING,
1039 	REG_ERR_TRAILING_BACKSLASH,
1040 	REG_ERR_CORRUPTED,
1041 	REG_ERR_NULL_CHAR,
1042 	REG_ERR_NUM
1043 };
1044 
1045 int regcomp(regex_t *preg, const char *regex, int cflags);
1046 int regexec(regex_t  *preg,  const  char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
1047 size_t regerror(int errcode, const regex_t *preg, char *errbuf,  size_t errbuf_size);
1048 void regfree(regex_t *preg);
1049 
1050 #ifdef __cplusplus
1051 }
1052 #endif
1053 
1054 #endif
1055 #ifndef JIM_SIGNAL_H
1056 #define JIM_SIGNAL_H
1057 
1058 #ifdef __cplusplus
1059 extern "C" {
1060 #endif
1061 
1062 const char *Jim_SignalId(int sig);
1063 
1064 #ifdef __cplusplus
1065 }
1066 #endif
1067 
1068 #endif
1069 #ifndef JIMIOCOMPAT_H
1070 #define JIMIOCOMPAT_H
1071 
1072 
1073 #include <stdio.h>
1074 #include <errno.h>
1075 
1076 
1077 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg);
1078 
1079 int Jim_OpenForWrite(const char *filename, int append);
1080 
1081 int Jim_OpenForRead(const char *filename);
1082 
1083 #if defined(__MINGW32__)
1084     #ifndef STRICT
1085     #define STRICT
1086     #endif
1087     #define WIN32_LEAN_AND_MEAN
1088     #include <windows.h>
1089     #include <fcntl.h>
1090     #include <io.h>
1091     #include <process.h>
1092 
1093     typedef HANDLE pidtype;
1094     #define JIM_BAD_PID INVALID_HANDLE_VALUE
1095 
1096     #define JIM_NO_PID INVALID_HANDLE_VALUE
1097 
1098 
1099     #define WIFEXITED(STATUS) (((STATUS) & 0xff00) == 0)
1100     #define WEXITSTATUS(STATUS) ((STATUS) & 0x00ff)
1101     #define WIFSIGNALED(STATUS) (((STATUS) & 0xff00) != 0)
1102     #define WTERMSIG(STATUS) (((STATUS) >> 8) & 0xff)
1103     #define WNOHANG 1
1104 
1105     int Jim_Errno(void);
1106     pidtype waitpid(pidtype pid, int *status, int nohang);
1107 
1108     #define HAVE_PIPE
1109     #define pipe(P) _pipe((P), 0, O_NOINHERIT)
1110 
1111 #elif defined(HAVE_UNISTD_H)
1112     #include <unistd.h>
1113     #include <fcntl.h>
1114     #include <sys/wait.h>
1115     #include <sys/stat.h>
1116 
1117     typedef int pidtype;
1118     #define Jim_Errno() errno
1119     #define JIM_BAD_PID -1
1120     #define JIM_NO_PID 0
1121 
1122     #ifndef HAVE_EXECVPE
1123         #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
1124     #endif
1125 #endif
1126 
1127 #endif
Jim_bootstrapInit(Jim_Interp * interp)1128 int Jim_bootstrapInit(Jim_Interp *interp)
1129 {
1130 	if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
1131 		return JIM_ERR;
1132 
1133 	return Jim_EvalSource(interp, "bootstrap.tcl", 1,
1134 "\n"
1135 "\n"
1136 "proc package {cmd pkg args} {\n"
1137 "	if {$cmd eq \"require\"} {\n"
1138 "		foreach path $::auto_path {\n"
1139 "			set pkgpath $path/$pkg.tcl\n"
1140 "			if {$path eq \".\"} {\n"
1141 "				set pkgpath $pkg.tcl\n"
1142 "			}\n"
1143 "			if {[file exists $pkgpath]} {\n"
1144 "				uplevel #0 [list source $pkgpath]\n"
1145 "				return\n"
1146 "			}\n"
1147 "		}\n"
1148 "	}\n"
1149 "}\n"
1150 );
1151 }
Jim_initjimshInit(Jim_Interp * interp)1152 int Jim_initjimshInit(Jim_Interp *interp)
1153 {
1154 	if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
1155 		return JIM_ERR;
1156 
1157 	return Jim_EvalSource(interp, "initjimsh.tcl", 1,
1158 "\n"
1159 "\n"
1160 "\n"
1161 "proc _jimsh_init {} {\n"
1162 "	rename _jimsh_init {}\n"
1163 "	global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
1164 "\n"
1165 "\n"
1166 "	if {[exists jim::argv0]} {\n"
1167 "		if {[string match \"*/*\" $jim::argv0]} {\n"
1168 "			set jim::exe [file join [pwd] $jim::argv0]\n"
1169 "		} else {\n"
1170 "			foreach path [split [env PATH \"\"] $tcl_platform(pathSeparator)] {\n"
1171 "				set exec [file join [pwd] [string map {\\\\ /} $path] $jim::argv0]\n"
1172 "				if {[file executable $exec]} {\n"
1173 "					set jim::exe $exec\n"
1174 "					break\n"
1175 "				}\n"
1176 "			}\n"
1177 "		}\n"
1178 "	}\n"
1179 "\n"
1180 "\n"
1181 "	lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
1182 "	if {[exists jim::exe]} {\n"
1183 "		lappend p [file dirname $jim::exe]\n"
1184 "	}\n"
1185 "	lappend p {*}$auto_path\n"
1186 "	set auto_path $p\n"
1187 "\n"
1188 "	if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
1189 "		foreach src {.jimrc jimrc.tcl} {\n"
1190 "			if {[file exists [env HOME]/$src]} {\n"
1191 "				uplevel #0 source [env HOME]/$src\n"
1192 "				break\n"
1193 "			}\n"
1194 "		}\n"
1195 "	}\n"
1196 "	return \"\"\n"
1197 "}\n"
1198 "\n"
1199 "if {$tcl_platform(platform) eq \"windows\"} {\n"
1200 "	set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
1201 "}\n"
1202 "\n"
1203 "\n"
1204 "set tcl::autocomplete_commands {info tcl::prefix socket namespace array clock file package string dict signal history}\n"
1205 "\n"
1206 "\n"
1207 "\n"
1208 "proc tcl::autocomplete {prefix} {\n"
1209 "	if {[set space [string first \" \" $prefix]] != -1} {\n"
1210 "		set cmd [string range $prefix 0 $space-1]\n"
1211 "		if {$cmd in $::tcl::autocomplete_commands || [info channel $cmd] ne \"\"} {\n"
1212 "			set arg [string range $prefix $space+1 end]\n"
1213 "\n"
1214 "			return [lmap p [$cmd -commands] {\n"
1215 "				if {![string match \"${arg}*\" $p]} continue\n"
1216 "				function \"$cmd $p\"\n"
1217 "			}]\n"
1218 "		}\n"
1219 "	}\n"
1220 "\n"
1221 "	if {[string match \"source *\" $prefix]} {\n"
1222 "		set path [string range $prefix 7 end]\n"
1223 "		return [lmap p [glob -nocomplain \"${path}*\"] {\n"
1224 "			function \"source $p\"\n"
1225 "		}]\n"
1226 "	}\n"
1227 "\n"
1228 "	return [lmap p [lsort [info commands $prefix*]] {\n"
1229 "		if {[string match \"* *\" $p]} {\n"
1230 "			continue\n"
1231 "		}\n"
1232 "		function $p\n"
1233 "	}]\n"
1234 "}\n"
1235 "\n"
1236 "_jimsh_init\n"
1237 );
1238 }
Jim_globInit(Jim_Interp * interp)1239 int Jim_globInit(Jim_Interp *interp)
1240 {
1241 	if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
1242 		return JIM_ERR;
1243 
1244 	return Jim_EvalSource(interp, "glob.tcl", 1,
1245 "\n"
1246 "\n"
1247 "\n"
1248 "\n"
1249 "\n"
1250 "\n"
1251 "\n"
1252 "package require readdir\n"
1253 "\n"
1254 "\n"
1255 "proc glob.globdir {dir pattern} {\n"
1256 "	if {[file exists $dir/$pattern]} {\n"
1257 "\n"
1258 "		return [list $pattern]\n"
1259 "	}\n"
1260 "\n"
1261 "	set result {}\n"
1262 "	set files [readdir $dir]\n"
1263 "	lappend files . ..\n"
1264 "\n"
1265 "	foreach name $files {\n"
1266 "		if {[string match $pattern $name]} {\n"
1267 "\n"
1268 "			if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n"
1269 "				continue\n"
1270 "			}\n"
1271 "			lappend result $name\n"
1272 "		}\n"
1273 "	}\n"
1274 "\n"
1275 "	return $result\n"
1276 "}\n"
1277 "\n"
1278 "\n"
1279 "\n"
1280 "\n"
1281 "proc glob.explode {pattern} {\n"
1282 "	set oldexp {}\n"
1283 "	set newexp {\"\"}\n"
1284 "\n"
1285 "	while 1 {\n"
1286 "		set oldexp $newexp\n"
1287 "		set newexp {}\n"
1288 "		set ob [string first \\{ $pattern]\n"
1289 "		set cb [string first \\} $pattern]\n"
1290 "\n"
1291 "		if {$ob < $cb && $ob != -1} {\n"
1292 "			set mid [string range $pattern 0 $ob-1]\n"
1293 "			set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n"
1294 "			if {$pattern eq \"\"} {\n"
1295 "				error \"unmatched open brace in glob pattern\"\n"
1296 "			}\n"
1297 "			set pattern [string range $pattern 1 end]\n"
1298 "\n"
1299 "			foreach subs $subexp {\n"
1300 "				foreach sub [split $subs ,] {\n"
1301 "					foreach old $oldexp {\n"
1302 "						lappend newexp $old$mid$sub\n"
1303 "					}\n"
1304 "				}\n"
1305 "			}\n"
1306 "		} elseif {$cb != -1} {\n"
1307 "			set suf  [string range $pattern 0 $cb-1]\n"
1308 "			set rest [string range $pattern $cb end]\n"
1309 "			break\n"
1310 "		} else {\n"
1311 "			set suf  $pattern\n"
1312 "			set rest \"\"\n"
1313 "			break\n"
1314 "		}\n"
1315 "	}\n"
1316 "\n"
1317 "	foreach old $oldexp {\n"
1318 "		lappend newexp $old$suf\n"
1319 "	}\n"
1320 "	list $rest {*}$newexp\n"
1321 "}\n"
1322 "\n"
1323 "\n"
1324 "\n"
1325 "proc glob.glob {base pattern} {\n"
1326 "	set dir [file dirname $pattern]\n"
1327 "	if {$pattern eq $dir || $pattern eq \"\"} {\n"
1328 "		return [list [file join $base $dir] $pattern]\n"
1329 "	} elseif {$pattern eq [file tail $pattern]} {\n"
1330 "		set dir \"\"\n"
1331 "	}\n"
1332 "\n"
1333 "\n"
1334 "	set dirlist [glob.glob $base $dir]\n"
1335 "	set pattern [file tail $pattern]\n"
1336 "\n"
1337 "\n"
1338 "	set result {}\n"
1339 "	foreach {realdir dir} $dirlist {\n"
1340 "		if {![file isdir $realdir]} {\n"
1341 "			continue\n"
1342 "		}\n"
1343 "		if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n"
1344 "			append dir /\n"
1345 "		}\n"
1346 "		foreach name [glob.globdir $realdir $pattern] {\n"
1347 "			lappend result [file join $realdir $name] $dir$name\n"
1348 "		}\n"
1349 "	}\n"
1350 "	return $result\n"
1351 "}\n"
1352 "\n"
1353 "\n"
1354 "\n"
1355 "\n"
1356 "\n"
1357 "\n"
1358 "\n"
1359 "\n"
1360 "\n"
1361 "\n"
1362 "\n"
1363 "\n"
1364 "proc glob {args} {\n"
1365 "	set nocomplain 0\n"
1366 "	set base \"\"\n"
1367 "	set tails 0\n"
1368 "\n"
1369 "	set n 0\n"
1370 "	foreach arg $args {\n"
1371 "		if {[info exists param]} {\n"
1372 "			set $param $arg\n"
1373 "			unset param\n"
1374 "			incr n\n"
1375 "			continue\n"
1376 "		}\n"
1377 "		switch -glob -- $arg {\n"
1378 "			-d* {\n"
1379 "				set switch $arg\n"
1380 "				set param base\n"
1381 "			}\n"
1382 "			-n* {\n"
1383 "				set nocomplain 1\n"
1384 "			}\n"
1385 "			-ta* {\n"
1386 "				set tails 1\n"
1387 "			}\n"
1388 "			-- {\n"
1389 "				incr n\n"
1390 "				break\n"
1391 "			}\n"
1392 "			-* {\n"
1393 "				return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1394 "			}\n"
1395 "			*  {\n"
1396 "				break\n"
1397 "			}\n"
1398 "		}\n"
1399 "		incr n\n"
1400 "	}\n"
1401 "	if {[info exists param]} {\n"
1402 "		return -code error \"missing argument to \\\"$switch\\\"\"\n"
1403 "	}\n"
1404 "	if {[llength $args] <= $n} {\n"
1405 "		return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n"
1406 "	}\n"
1407 "\n"
1408 "	set args [lrange $args $n end]\n"
1409 "\n"
1410 "	set result {}\n"
1411 "	foreach pattern $args {\n"
1412 "		set escpattern [string map {\n"
1413 "			\\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
1414 "		} $pattern]\n"
1415 "		set patexps [lassign [glob.explode $escpattern] rest]\n"
1416 "		if {$rest ne \"\"} {\n"
1417 "			return -code error \"unmatched close brace in glob pattern\"\n"
1418 "		}\n"
1419 "		foreach patexp $patexps {\n"
1420 "			set patexp [string map {\n"
1421 "				\\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
1422 "			} $patexp]\n"
1423 "			foreach {realname name} [glob.glob $base $patexp] {\n"
1424 "				incr n\n"
1425 "				if {$tails} {\n"
1426 "					lappend result $name\n"
1427 "				} else {\n"
1428 "					lappend result [file join $base $name]\n"
1429 "				}\n"
1430 "			}\n"
1431 "		}\n"
1432 "	}\n"
1433 "\n"
1434 "	if {!$nocomplain && [llength $result] == 0} {\n"
1435 "		set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
1436 "		return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
1437 "	}\n"
1438 "\n"
1439 "	return $result\n"
1440 "}\n"
1441 );
1442 }
Jim_stdlibInit(Jim_Interp * interp)1443 int Jim_stdlibInit(Jim_Interp *interp)
1444 {
1445 	if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
1446 		return JIM_ERR;
1447 
1448 	return Jim_EvalSource(interp, "stdlib.tcl", 1,
1449 "\n"
1450 "\n"
1451 "if {![exists -command ref]} {\n"
1452 "\n"
1453 "	proc ref {args} {{count 0}} {\n"
1454 "		format %08x [incr count]\n"
1455 "	}\n"
1456 "}\n"
1457 "\n"
1458 "\n"
1459 "proc lambda {arglist args} {\n"
1460 "	tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
1461 "}\n"
1462 "\n"
1463 "proc lambda.finalizer {name val} {\n"
1464 "	rename $name {}\n"
1465 "}\n"
1466 "\n"
1467 "\n"
1468 "proc curry {args} {\n"
1469 "	alias [ref {} function lambda.finalizer] {*}$args\n"
1470 "}\n"
1471 "\n"
1472 "\n"
1473 "\n"
1474 "\n"
1475 "\n"
1476 "\n"
1477 "\n"
1478 "\n"
1479 "\n"
1480 "proc function {value} {\n"
1481 "	return $value\n"
1482 "}\n"
1483 "\n"
1484 "\n"
1485 "\n"
1486 "\n"
1487 "proc stacktrace {{skip 0}} {\n"
1488 "	set trace {}\n"
1489 "	incr skip\n"
1490 "	foreach level [range $skip [info level]] {\n"
1491 "		lappend trace {*}[info frame -$level]\n"
1492 "	}\n"
1493 "	return $trace\n"
1494 "}\n"
1495 "\n"
1496 "\n"
1497 "proc stackdump {stacktrace} {\n"
1498 "	set lines {}\n"
1499 "	foreach {l f p} [lreverse $stacktrace] {\n"
1500 "		set line {}\n"
1501 "		if {$p ne \"\"} {\n"
1502 "			append line \"in procedure '$p' \"\n"
1503 "			if {$f ne \"\"} {\n"
1504 "				append line \"called \"\n"
1505 "			}\n"
1506 "		}\n"
1507 "		if {$f ne \"\"} {\n"
1508 "			append line \"at file \\\"$f\\\", line $l\"\n"
1509 "		}\n"
1510 "		if {$line ne \"\"} {\n"
1511 "			lappend lines $line\n"
1512 "		}\n"
1513 "	}\n"
1514 "	join $lines \\n\n"
1515 "}\n"
1516 "\n"
1517 "\n"
1518 "\n"
1519 "proc defer {script} {\n"
1520 "	upvar jim::defer v\n"
1521 "	lappend v $script\n"
1522 "}\n"
1523 "\n"
1524 "\n"
1525 "\n"
1526 "proc errorInfo {msg {stacktrace \"\"}} {\n"
1527 "	if {$stacktrace eq \"\"} {\n"
1528 "\n"
1529 "		set stacktrace [info stacktrace]\n"
1530 "\n"
1531 "		lappend stacktrace {*}[stacktrace 1]\n"
1532 "	}\n"
1533 "	lassign $stacktrace p f l\n"
1534 "	if {$f ne \"\"} {\n"
1535 "		set result \"$f:$l: Error: \"\n"
1536 "	}\n"
1537 "	append result \"$msg\\n\"\n"
1538 "	append result [stackdump $stacktrace]\n"
1539 "\n"
1540 "\n"
1541 "	string trim $result\n"
1542 "}\n"
1543 "\n"
1544 "\n"
1545 "\n"
1546 "proc {info nameofexecutable} {} {\n"
1547 "	if {[exists ::jim::exe]} {\n"
1548 "		return $::jim::exe\n"
1549 "	}\n"
1550 "}\n"
1551 "\n"
1552 "\n"
1553 "proc {dict update} {&varName args script} {\n"
1554 "	set keys {}\n"
1555 "	foreach {n v} $args {\n"
1556 "		upvar $v var_$v\n"
1557 "		if {[dict exists $varName $n]} {\n"
1558 "			set var_$v [dict get $varName $n]\n"
1559 "		}\n"
1560 "	}\n"
1561 "	catch {uplevel 1 $script} msg opts\n"
1562 "	if {[info exists varName]} {\n"
1563 "		foreach {n v} $args {\n"
1564 "			if {[info exists var_$v]} {\n"
1565 "				dict set varName $n [set var_$v]\n"
1566 "			} else {\n"
1567 "				dict unset varName $n\n"
1568 "			}\n"
1569 "		}\n"
1570 "	}\n"
1571 "	return {*}$opts $msg\n"
1572 "}\n"
1573 "\n"
1574 "proc {dict replace} {dictionary {args {key value}}} {\n"
1575 "	if {[llength ${key value}] % 2} {\n"
1576 "		tailcall {dict replace}\n"
1577 "	}\n"
1578 "	tailcall dict merge $dictionary ${key value}\n"
1579 "}\n"
1580 "\n"
1581 "\n"
1582 "proc {dict lappend} {varName key {args value}} {\n"
1583 "	upvar $varName dict\n"
1584 "	if {[exists dict] && [dict exists $dict $key]} {\n"
1585 "		set list [dict get $dict $key]\n"
1586 "	}\n"
1587 "	lappend list {*}$value\n"
1588 "	dict set dict $key $list\n"
1589 "}\n"
1590 "\n"
1591 "\n"
1592 "proc {dict append} {varName key {args value}} {\n"
1593 "	upvar $varName dict\n"
1594 "	if {[exists dict] && [dict exists $dict $key]} {\n"
1595 "		set str [dict get $dict $key]\n"
1596 "	}\n"
1597 "	append str {*}$value\n"
1598 "	dict set dict $key $str\n"
1599 "}\n"
1600 "\n"
1601 "\n"
1602 "proc {dict incr} {varName key {increment 1}} {\n"
1603 "	upvar $varName dict\n"
1604 "	if {[exists dict] && [dict exists $dict $key]} {\n"
1605 "		set value [dict get $dict $key]\n"
1606 "	}\n"
1607 "	incr value $increment\n"
1608 "	dict set dict $key $value\n"
1609 "}\n"
1610 "\n"
1611 "\n"
1612 "proc {dict remove} {dictionary {args key}} {\n"
1613 "	foreach k $key {\n"
1614 "		dict unset dictionary $k\n"
1615 "	}\n"
1616 "	return $dictionary\n"
1617 "}\n"
1618 "\n"
1619 "\n"
1620 "proc {dict for} {vars dictionary script} {\n"
1621 "	if {[llength $vars] != 2} {\n"
1622 "		return -code error \"must have exactly two variable names\"\n"
1623 "	}\n"
1624 "	dict size $dictionary\n"
1625 "	tailcall foreach $vars $dictionary $script\n"
1626 "}\n"
1627 );
1628 }
Jim_tclcompatInit(Jim_Interp * interp)1629 int Jim_tclcompatInit(Jim_Interp *interp)
1630 {
1631 	if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG))
1632 		return JIM_ERR;
1633 
1634 	return Jim_EvalSource(interp, "tclcompat.tcl", 1,
1635 "\n"
1636 "\n"
1637 "\n"
1638 "\n"
1639 "\n"
1640 "\n"
1641 "\n"
1642 "\n"
1643 "set env [env]\n"
1644 "\n"
1645 "\n"
1646 "if {[info commands stdout] ne \"\"} {\n"
1647 "\n"
1648 "	foreach p {gets flush close eof seek tell} {\n"
1649 "		proc $p {chan args} {p} {\n"
1650 "			tailcall $chan $p {*}$args\n"
1651 "		}\n"
1652 "	}\n"
1653 "	unset p\n"
1654 "\n"
1655 "\n"
1656 "\n"
1657 "	proc puts {{-nonewline {}} {chan stdout} msg} {\n"
1658 "		if {${-nonewline} ni {-nonewline {}}} {\n"
1659 "			tailcall ${-nonewline} puts $msg\n"
1660 "		}\n"
1661 "		tailcall $chan puts {*}${-nonewline} $msg\n"
1662 "	}\n"
1663 "\n"
1664 "\n"
1665 "\n"
1666 "\n"
1667 "\n"
1668 "	proc read {{-nonewline {}} chan} {\n"
1669 "		if {${-nonewline} ni {-nonewline {}}} {\n"
1670 "			tailcall ${-nonewline} read {*}${chan}\n"
1671 "		}\n"
1672 "		tailcall $chan read {*}${-nonewline}\n"
1673 "	}\n"
1674 "\n"
1675 "	proc fconfigure {f args} {\n"
1676 "		foreach {n v} $args {\n"
1677 "			switch -glob -- $n {\n"
1678 "				-bl* {\n"
1679 "					$f ndelay $(!$v)\n"
1680 "				}\n"
1681 "				-bu* {\n"
1682 "					$f buffering $v\n"
1683 "				}\n"
1684 "				-tr* {\n"
1685 "\n"
1686 "				}\n"
1687 "				default {\n"
1688 "					return -code error \"fconfigure: unknown option $n\"\n"
1689 "				}\n"
1690 "			}\n"
1691 "		}\n"
1692 "	}\n"
1693 "}\n"
1694 "\n"
1695 "\n"
1696 "proc fileevent {args} {\n"
1697 "	tailcall {*}$args\n"
1698 "}\n"
1699 "\n"
1700 "\n"
1701 "\n"
1702 "proc parray {arrayname {pattern *} {puts puts}} {\n"
1703 "	upvar $arrayname a\n"
1704 "\n"
1705 "	set max 0\n"
1706 "	foreach name [array names a $pattern]] {\n"
1707 "		if {[string length $name] > $max} {\n"
1708 "			set max [string length $name]\n"
1709 "		}\n"
1710 "	}\n"
1711 "	incr max [string length $arrayname]\n"
1712 "	incr max 2\n"
1713 "	foreach name [lsort [array names a $pattern]] {\n"
1714 "		$puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
1715 "	}\n"
1716 "}\n"
1717 "\n"
1718 "\n"
1719 "proc {file copy} {{force {}} source target} {\n"
1720 "	try {\n"
1721 "		if {$force ni {{} -force}} {\n"
1722 "			error \"bad option \\\"$force\\\": should be -force\"\n"
1723 "		}\n"
1724 "\n"
1725 "		set in [open $source rb]\n"
1726 "\n"
1727 "		if {[file exists $target]} {\n"
1728 "			if {$force eq \"\"} {\n"
1729 "				error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
1730 "			}\n"
1731 "\n"
1732 "			if {$source eq $target} {\n"
1733 "				return\n"
1734 "			}\n"
1735 "\n"
1736 "\n"
1737 "			file stat $source ss\n"
1738 "			file stat $target ts\n"
1739 "			if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
1740 "				return\n"
1741 "			}\n"
1742 "		}\n"
1743 "		set out [open $target wb]\n"
1744 "		$in copyto $out\n"
1745 "		$out close\n"
1746 "	} on error {msg opts} {\n"
1747 "		incr opts(-level)\n"
1748 "		return {*}$opts $msg\n"
1749 "	} finally {\n"
1750 "		catch {$in close}\n"
1751 "	}\n"
1752 "}\n"
1753 "\n"
1754 "\n"
1755 "\n"
1756 "proc popen {cmd {mode r}} {\n"
1757 "	lassign [pipe] r w\n"
1758 "	try {\n"
1759 "		if {[string match \"w*\" $mode]} {\n"
1760 "			lappend cmd <@$r &\n"
1761 "			set pids [exec {*}$cmd]\n"
1762 "			$r close\n"
1763 "			set f $w\n"
1764 "		} else {\n"
1765 "			lappend cmd >@$w &\n"
1766 "			set pids [exec {*}$cmd]\n"
1767 "			$w close\n"
1768 "			set f $r\n"
1769 "		}\n"
1770 "		lambda {cmd args} {f pids} {\n"
1771 "			if {$cmd eq \"pid\"} {\n"
1772 "				return $pids\n"
1773 "			}\n"
1774 "			if {$cmd eq \"getfd\"} {\n"
1775 "				$f getfd\n"
1776 "			}\n"
1777 "			if {$cmd eq \"close\"} {\n"
1778 "				$f close\n"
1779 "\n"
1780 "				set retopts {}\n"
1781 "				foreach p $pids {\n"
1782 "					lassign [wait $p] status - rc\n"
1783 "					if {$status eq \"CHILDSTATUS\"} {\n"
1784 "						if {$rc == 0} {\n"
1785 "							continue\n"
1786 "						}\n"
1787 "						set msg \"child process exited abnormally\"\n"
1788 "					} else {\n"
1789 "						set msg \"child killed: received signal\"\n"
1790 "					}\n"
1791 "					set retopts [list -code error -errorcode [list $status $p $rc] $msg]\n"
1792 "				}\n"
1793 "				return {*}$retopts\n"
1794 "			}\n"
1795 "			tailcall $f $cmd {*}$args\n"
1796 "		}\n"
1797 "	} on error {error opts} {\n"
1798 "		$r close\n"
1799 "		$w close\n"
1800 "		error $error\n"
1801 "	}\n"
1802 "}\n"
1803 "\n"
1804 "\n"
1805 "local proc pid {{channelId {}}} {\n"
1806 "	if {$channelId eq \"\"} {\n"
1807 "		tailcall upcall pid\n"
1808 "	}\n"
1809 "	if {[catch {$channelId tell}]} {\n"
1810 "		return -code error \"can not find channel named \\\"$channelId\\\"\"\n"
1811 "	}\n"
1812 "	if {[catch {$channelId pid} pids]} {\n"
1813 "		return \"\"\n"
1814 "	}\n"
1815 "	return $pids\n"
1816 "}\n"
1817 "\n"
1818 "\n"
1819 "\n"
1820 "\n"
1821 "\n"
1822 "\n"
1823 "\n"
1824 "\n"
1825 "\n"
1826 "\n"
1827 "proc try {args} {\n"
1828 "	set catchopts {}\n"
1829 "	while {[string match -* [lindex $args 0]]} {\n"
1830 "		set args [lassign $args opt]\n"
1831 "		if {$opt eq \"--\"} {\n"
1832 "			break\n"
1833 "		}\n"
1834 "		lappend catchopts $opt\n"
1835 "	}\n"
1836 "	if {[llength $args] == 0} {\n"
1837 "		return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n"
1838 "	}\n"
1839 "	set args [lassign $args script]\n"
1840 "	set code [catch -eval {*}$catchopts {uplevel 1 $script} msg opts]\n"
1841 "\n"
1842 "	set handled 0\n"
1843 "\n"
1844 "	foreach {on codes vars script} $args {\n"
1845 "		switch -- $on \\\n"
1846 "			on {\n"
1847 "				if {!$handled && ($codes eq \"*\" || [info returncode $code] in $codes)} {\n"
1848 "					lassign $vars msgvar optsvar\n"
1849 "					if {$msgvar ne \"\"} {\n"
1850 "						upvar $msgvar hmsg\n"
1851 "						set hmsg $msg\n"
1852 "					}\n"
1853 "					if {$optsvar ne \"\"} {\n"
1854 "						upvar $optsvar hopts\n"
1855 "						set hopts $opts\n"
1856 "					}\n"
1857 "\n"
1858 "					set code [catch {uplevel 1 $script} msg opts]\n"
1859 "					incr handled\n"
1860 "				}\n"
1861 "			} \\\n"
1862 "			finally {\n"
1863 "				set finalcode [catch {uplevel 1 $codes} finalmsg finalopts]\n"
1864 "				if {$finalcode} {\n"
1865 "\n"
1866 "					set code $finalcode\n"
1867 "					set msg $finalmsg\n"
1868 "					set opts $finalopts\n"
1869 "				}\n"
1870 "				break\n"
1871 "			} \\\n"
1872 "			default {\n"
1873 "				return -code error \"try: expected 'on' or 'finally', got '$on'\"\n"
1874 "			}\n"
1875 "	}\n"
1876 "\n"
1877 "	if {$code} {\n"
1878 "		incr opts(-level)\n"
1879 "		return {*}$opts $msg\n"
1880 "	}\n"
1881 "	return $msg\n"
1882 "}\n"
1883 "\n"
1884 "\n"
1885 "\n"
1886 "proc throw {code {msg \"\"}} {\n"
1887 "	return -code $code $msg\n"
1888 "}\n"
1889 "\n"
1890 "\n"
1891 "proc {file delete force} {path} {\n"
1892 "	foreach e [readdir $path] {\n"
1893 "		file delete -force $path/$e\n"
1894 "	}\n"
1895 "	file delete $path\n"
1896 "}\n"
1897 );
1898 }
1899 
1900 
1901 #ifndef _GNU_SOURCE
1902 #define _GNU_SOURCE
1903 #endif
1904 #include <stdio.h>
1905 #include <string.h>
1906 #include <errno.h>
1907 #include <fcntl.h>
1908 #ifdef HAVE_UNISTD_H
1909 #include <unistd.h>
1910 #include <sys/stat.h>
1911 #endif
1912 
1913 
1914 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
1915 #include <sys/socket.h>
1916 #include <netinet/in.h>
1917 #include <netinet/tcp.h>
1918 #include <arpa/inet.h>
1919 #include <netdb.h>
1920 #ifdef HAVE_SYS_UN_H
1921 #include <sys/un.h>
1922 #endif
1923 #define HAVE_SOCKETS
1924 #elif defined (__MINGW32__)
1925 
1926 #else
1927 #define JIM_ANSIC
1928 #endif
1929 
1930 #if defined(JIM_SSL)
1931 #include <openssl/ssl.h>
1932 #include <openssl/err.h>
1933 #endif
1934 
1935 #ifdef HAVE_TERMIOS_H
1936 #endif
1937 
1938 
1939 #define AIO_CMD_LEN 32
1940 #define AIO_BUF_LEN 256
1941 
1942 #ifndef HAVE_FTELLO
1943     #define ftello ftell
1944 #endif
1945 #ifndef HAVE_FSEEKO
1946     #define fseeko fseek
1947 #endif
1948 
1949 #define AIO_KEEPOPEN 1
1950 
1951 #if defined(JIM_IPV6)
1952 #define IPV6 1
1953 #else
1954 #define IPV6 0
1955 #ifndef PF_INET6
1956 #define PF_INET6 0
1957 #endif
1958 #endif
1959 
1960 #ifdef JIM_ANSIC
1961 
1962 #undef HAVE_PIPE
1963 #undef HAVE_SOCKETPAIR
1964 #endif
1965 
1966 
1967 struct AioFile;
1968 
1969 typedef struct {
1970     int (*writer)(struct AioFile *af, const char *buf, int len);
1971     int (*reader)(struct AioFile *af, char *buf, int len);
1972     const char *(*getline)(struct AioFile *af, char *buf, int len);
1973     int (*error)(const struct AioFile *af);
1974     const char *(*strerror)(struct AioFile *af);
1975     int (*verify)(struct AioFile *af);
1976 } JimAioFopsType;
1977 
1978 typedef struct AioFile
1979 {
1980     FILE *fp;
1981     Jim_Obj *filename;
1982     int type;
1983     int openFlags;
1984     int fd;
1985     Jim_Obj *rEvent;
1986     Jim_Obj *wEvent;
1987     Jim_Obj *eEvent;
1988     int addr_family;
1989     void *ssl;
1990     const JimAioFopsType *fops;
1991 } AioFile;
1992 
stdio_writer(struct AioFile * af,const char * buf,int len)1993 static int stdio_writer(struct AioFile *af, const char *buf, int len)
1994 {
1995     return fwrite(buf, 1, len, af->fp);
1996 }
1997 
stdio_reader(struct AioFile * af,char * buf,int len)1998 static int stdio_reader(struct AioFile *af, char *buf, int len)
1999 {
2000     return fread(buf, 1, len, af->fp);
2001 }
2002 
stdio_getline(struct AioFile * af,char * buf,int len)2003 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
2004 {
2005     return fgets(buf, len, af->fp);
2006 }
2007 
stdio_error(const AioFile * af)2008 static int stdio_error(const AioFile *af)
2009 {
2010     if (!ferror(af->fp)) {
2011         return JIM_OK;
2012     }
2013     clearerr(af->fp);
2014 
2015     if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
2016         return JIM_OK;
2017     }
2018 #ifdef ECONNRESET
2019     if (errno == ECONNRESET) {
2020         return JIM_OK;
2021     }
2022 #endif
2023 #ifdef ECONNABORTED
2024     if (errno == ECONNABORTED) {
2025         return JIM_OK;
2026     }
2027 #endif
2028     return JIM_ERR;
2029 }
2030 
stdio_strerror(struct AioFile * af)2031 static const char *stdio_strerror(struct AioFile *af)
2032 {
2033     return strerror(errno);
2034 }
2035 
2036 static const JimAioFopsType stdio_fops = {
2037     stdio_writer,
2038     stdio_reader,
2039     stdio_getline,
2040     stdio_error,
2041     stdio_strerror,
2042     NULL
2043 };
2044 
2045 
2046 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
2047 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
2048     const char *hdlfmt, int family, const char *mode);
2049 
2050 
JimAioErrorString(AioFile * af)2051 static const char *JimAioErrorString(AioFile *af)
2052 {
2053     if (af && af->fops)
2054         return af->fops->strerror(af);
2055 
2056     return strerror(errno);
2057 }
2058 
JimAioSetError(Jim_Interp * interp,Jim_Obj * name)2059 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
2060 {
2061     AioFile *af = Jim_CmdPrivData(interp);
2062 
2063     if (name) {
2064         Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
2065     }
2066     else {
2067         Jim_SetResultString(interp, JimAioErrorString(af), -1);
2068     }
2069 }
2070 
JimCheckStreamError(Jim_Interp * interp,AioFile * af)2071 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
2072 {
2073 	int ret = af->fops->error(af);
2074 	if (ret) {
2075 		JimAioSetError(interp, af->filename);
2076 	}
2077 	return ret;
2078 }
2079 
JimAioDelProc(Jim_Interp * interp,void * privData)2080 static void JimAioDelProc(Jim_Interp *interp, void *privData)
2081 {
2082     AioFile *af = privData;
2083 
2084     JIM_NOTUSED(interp);
2085 
2086     Jim_DecrRefCount(interp, af->filename);
2087 
2088 #ifdef jim_ext_eventloop
2089 
2090     Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
2091 #endif
2092 
2093 #if defined(JIM_SSL)
2094     if (af->ssl != NULL) {
2095         SSL_free(af->ssl);
2096     }
2097 #endif
2098     if (!(af->openFlags & AIO_KEEPOPEN)) {
2099         fclose(af->fp);
2100     }
2101 
2102     Jim_Free(af);
2103 }
2104 
aio_cmd_read(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2105 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2106 {
2107     AioFile *af = Jim_CmdPrivData(interp);
2108     char buf[AIO_BUF_LEN];
2109     Jim_Obj *objPtr;
2110     int nonewline = 0;
2111     jim_wide neededLen = -1;
2112 
2113     if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
2114         nonewline = 1;
2115         argv++;
2116         argc--;
2117     }
2118     if (argc == 1) {
2119         if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
2120             return JIM_ERR;
2121         if (neededLen < 0) {
2122             Jim_SetResultString(interp, "invalid parameter: negative len", -1);
2123             return JIM_ERR;
2124         }
2125     }
2126     else if (argc) {
2127         return -1;
2128     }
2129     objPtr = Jim_NewStringObj(interp, NULL, 0);
2130     while (neededLen != 0) {
2131         int retval;
2132         int readlen;
2133 
2134         if (neededLen == -1) {
2135             readlen = AIO_BUF_LEN;
2136         }
2137         else {
2138             readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
2139         }
2140         retval = af->fops->reader(af, buf, readlen);
2141         if (retval > 0) {
2142             Jim_AppendString(interp, objPtr, buf, retval);
2143             if (neededLen != -1) {
2144                 neededLen -= retval;
2145             }
2146         }
2147         if (retval != readlen)
2148             break;
2149     }
2150 
2151     if (JimCheckStreamError(interp, af)) {
2152         Jim_FreeNewObj(interp, objPtr);
2153         return JIM_ERR;
2154     }
2155     if (nonewline) {
2156         int len;
2157         const char *s = Jim_GetString(objPtr, &len);
2158 
2159         if (len > 0 && s[len - 1] == '\n') {
2160             objPtr->length--;
2161             objPtr->bytes[objPtr->length] = '\0';
2162         }
2163     }
2164     Jim_SetResult(interp, objPtr);
2165     return JIM_OK;
2166 }
2167 
Jim_AioFile(Jim_Interp * interp,Jim_Obj * command)2168 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
2169 {
2170     Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
2171 
2172 
2173     if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
2174         return (AioFile *) cmdPtr->u.native.privData;
2175     }
2176     Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
2177     return NULL;
2178 }
2179 
Jim_AioFilehandle(Jim_Interp * interp,Jim_Obj * command)2180 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
2181 {
2182     AioFile *af;
2183 
2184     af = Jim_AioFile(interp, command);
2185     if (af == NULL) {
2186         return NULL;
2187     }
2188 
2189     return af->fp;
2190 }
2191 
aio_cmd_getfd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2192 static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2193 {
2194     AioFile *af = Jim_CmdPrivData(interp);
2195 
2196     fflush(af->fp);
2197     Jim_SetResultInt(interp, fileno(af->fp));
2198 
2199     return JIM_OK;
2200 }
2201 
aio_cmd_copy(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2202 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2203 {
2204     AioFile *af = Jim_CmdPrivData(interp);
2205     jim_wide count = 0;
2206     jim_wide maxlen = JIM_WIDE_MAX;
2207     AioFile *outf = Jim_AioFile(interp, argv[0]);
2208 
2209     if (outf == NULL) {
2210         return JIM_ERR;
2211     }
2212 
2213     if (argc == 2) {
2214         if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
2215             return JIM_ERR;
2216         }
2217     }
2218 
2219     while (count < maxlen) {
2220         char ch;
2221 
2222         if (af->fops->reader(af, &ch, 1) != 1) {
2223             break;
2224         }
2225         if (outf->fops->writer(outf, &ch, 1) != 1) {
2226             break;
2227         }
2228         count++;
2229     }
2230 
2231     if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
2232         return JIM_ERR;
2233     }
2234 
2235     Jim_SetResultInt(interp, count);
2236 
2237     return JIM_OK;
2238 }
2239 
aio_cmd_gets(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2240 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2241 {
2242     AioFile *af = Jim_CmdPrivData(interp);
2243     char buf[AIO_BUF_LEN];
2244     Jim_Obj *objPtr;
2245     int len;
2246 
2247     errno = 0;
2248 
2249     objPtr = Jim_NewStringObj(interp, NULL, 0);
2250     while (1) {
2251         buf[AIO_BUF_LEN - 1] = '_';
2252 
2253         if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
2254             break;
2255 
2256         if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
2257             Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
2258         }
2259         else {
2260             len = strlen(buf);
2261 
2262             if (len && (buf[len - 1] == '\n')) {
2263 
2264                 len--;
2265             }
2266 
2267             Jim_AppendString(interp, objPtr, buf, len);
2268             break;
2269         }
2270     }
2271 
2272     if (JimCheckStreamError(interp, af)) {
2273 
2274         Jim_FreeNewObj(interp, objPtr);
2275         return JIM_ERR;
2276     }
2277 
2278     if (argc) {
2279         if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
2280             Jim_FreeNewObj(interp, objPtr);
2281             return JIM_ERR;
2282         }
2283 
2284         len = Jim_Length(objPtr);
2285 
2286         if (len == 0 && feof(af->fp)) {
2287 
2288             len = -1;
2289         }
2290         Jim_SetResultInt(interp, len);
2291     }
2292     else {
2293         Jim_SetResult(interp, objPtr);
2294     }
2295     return JIM_OK;
2296 }
2297 
aio_cmd_puts(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2298 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2299 {
2300     AioFile *af = Jim_CmdPrivData(interp);
2301     int wlen;
2302     const char *wdata;
2303     Jim_Obj *strObj;
2304 
2305     if (argc == 2) {
2306         if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
2307             return -1;
2308         }
2309         strObj = argv[1];
2310     }
2311     else {
2312         strObj = argv[0];
2313     }
2314 
2315     wdata = Jim_GetString(strObj, &wlen);
2316     if (af->fops->writer(af, wdata, wlen) == wlen) {
2317         if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
2318             return JIM_OK;
2319         }
2320     }
2321     JimAioSetError(interp, af->filename);
2322     return JIM_ERR;
2323 }
2324 
aio_cmd_isatty(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2325 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2326 {
2327 #ifdef HAVE_ISATTY
2328     AioFile *af = Jim_CmdPrivData(interp);
2329     Jim_SetResultInt(interp, isatty(fileno(af->fp)));
2330 #else
2331     Jim_SetResultInt(interp, 0);
2332 #endif
2333 
2334     return JIM_OK;
2335 }
2336 
2337 
aio_cmd_flush(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2338 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2339 {
2340     AioFile *af = Jim_CmdPrivData(interp);
2341 
2342     if (fflush(af->fp) == EOF) {
2343         JimAioSetError(interp, af->filename);
2344         return JIM_ERR;
2345     }
2346     return JIM_OK;
2347 }
2348 
aio_cmd_eof(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2349 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2350 {
2351     AioFile *af = Jim_CmdPrivData(interp);
2352 
2353     Jim_SetResultInt(interp, feof(af->fp));
2354     return JIM_OK;
2355 }
2356 
aio_cmd_close(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2357 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2358 {
2359     if (argc == 3) {
2360 #if defined(HAVE_SOCKETS) && defined(HAVE_SHUTDOWN)
2361         static const char * const options[] = { "r", "w", NULL };
2362         enum { OPT_R, OPT_W, };
2363         int option;
2364         AioFile *af = Jim_CmdPrivData(interp);
2365 
2366         if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2367             return JIM_ERR;
2368         }
2369         if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
2370             return JIM_OK;
2371         }
2372         JimAioSetError(interp, NULL);
2373 #else
2374         Jim_SetResultString(interp, "async close not supported", -1);
2375 #endif
2376         return JIM_ERR;
2377     }
2378 
2379     return Jim_DeleteCommand(interp, Jim_String(argv[0]));
2380 }
2381 
aio_cmd_seek(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2382 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2383 {
2384     AioFile *af = Jim_CmdPrivData(interp);
2385     int orig = SEEK_SET;
2386     jim_wide offset;
2387 
2388     if (argc == 2) {
2389         if (Jim_CompareStringImmediate(interp, argv[1], "start"))
2390             orig = SEEK_SET;
2391         else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
2392             orig = SEEK_CUR;
2393         else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
2394             orig = SEEK_END;
2395         else {
2396             return -1;
2397         }
2398     }
2399     if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
2400         return JIM_ERR;
2401     }
2402     if (fseeko(af->fp, offset, orig) == -1) {
2403         JimAioSetError(interp, af->filename);
2404         return JIM_ERR;
2405     }
2406     return JIM_OK;
2407 }
2408 
aio_cmd_tell(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2409 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2410 {
2411     AioFile *af = Jim_CmdPrivData(interp);
2412 
2413     Jim_SetResultInt(interp, ftello(af->fp));
2414     return JIM_OK;
2415 }
2416 
aio_cmd_filename(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2417 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2418 {
2419     AioFile *af = Jim_CmdPrivData(interp);
2420 
2421     Jim_SetResult(interp, af->filename);
2422     return JIM_OK;
2423 }
2424 
2425 #ifdef O_NDELAY
aio_cmd_ndelay(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2426 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2427 {
2428     AioFile *af = Jim_CmdPrivData(interp);
2429 
2430     int fmode = fcntl(af->fd, F_GETFL);
2431 
2432     if (argc) {
2433         long nb;
2434 
2435         if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
2436             return JIM_ERR;
2437         }
2438         if (nb) {
2439             fmode |= O_NDELAY;
2440         }
2441         else {
2442             fmode &= ~O_NDELAY;
2443         }
2444         (void)fcntl(af->fd, F_SETFL, fmode);
2445     }
2446     Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
2447     return JIM_OK;
2448 }
2449 #endif
2450 
2451 
2452 #ifdef HAVE_FSYNC
aio_cmd_sync(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2453 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2454 {
2455     AioFile *af = Jim_CmdPrivData(interp);
2456 
2457     fflush(af->fp);
2458     fsync(af->fd);
2459     return JIM_OK;
2460 }
2461 #endif
2462 
aio_cmd_buffering(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2463 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2464 {
2465     AioFile *af = Jim_CmdPrivData(interp);
2466 
2467     static const char * const options[] = {
2468         "none",
2469         "line",
2470         "full",
2471         NULL
2472     };
2473     enum
2474     {
2475         OPT_NONE,
2476         OPT_LINE,
2477         OPT_FULL,
2478     };
2479     int option;
2480 
2481     if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2482         return JIM_ERR;
2483     }
2484     switch (option) {
2485         case OPT_NONE:
2486             setvbuf(af->fp, NULL, _IONBF, 0);
2487             break;
2488         case OPT_LINE:
2489             setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
2490             break;
2491         case OPT_FULL:
2492             setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
2493             break;
2494     }
2495     return JIM_OK;
2496 }
2497 
2498 #ifdef jim_ext_eventloop
JimAioFileEventFinalizer(Jim_Interp * interp,void * clientData)2499 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
2500 {
2501     Jim_Obj **objPtrPtr = clientData;
2502 
2503     Jim_DecrRefCount(interp, *objPtrPtr);
2504     *objPtrPtr = NULL;
2505 }
2506 
JimAioFileEventHandler(Jim_Interp * interp,void * clientData,int mask)2507 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
2508 {
2509     Jim_Obj **objPtrPtr = clientData;
2510 
2511     return Jim_EvalObjBackground(interp, *objPtrPtr);
2512 }
2513 
aio_eventinfo(Jim_Interp * interp,AioFile * af,unsigned mask,Jim_Obj ** scriptHandlerObj,int argc,Jim_Obj * const * argv)2514 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
2515     int argc, Jim_Obj * const *argv)
2516 {
2517     if (argc == 0) {
2518 
2519         if (*scriptHandlerObj) {
2520             Jim_SetResult(interp, *scriptHandlerObj);
2521         }
2522         return JIM_OK;
2523     }
2524 
2525     if (*scriptHandlerObj) {
2526 
2527         Jim_DeleteFileHandler(interp, af->fd, mask);
2528     }
2529 
2530 
2531     if (Jim_Length(argv[0]) == 0) {
2532 
2533         return JIM_OK;
2534     }
2535 
2536 
2537     Jim_IncrRefCount(argv[0]);
2538     *scriptHandlerObj = argv[0];
2539 
2540     Jim_CreateFileHandler(interp, af->fd, mask,
2541         JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
2542 
2543     return JIM_OK;
2544 }
2545 
aio_cmd_readable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2546 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2547 {
2548     AioFile *af = Jim_CmdPrivData(interp);
2549 
2550     return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
2551 }
2552 
aio_cmd_writable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2553 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2554 {
2555     AioFile *af = Jim_CmdPrivData(interp);
2556 
2557     return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
2558 }
2559 
aio_cmd_onexception(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2560 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2561 {
2562     AioFile *af = Jim_CmdPrivData(interp);
2563 
2564     return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
2565 }
2566 #endif
2567 
2568 
2569 
2570 
2571 static const jim_subcmd_type aio_command_table[] = {
2572     {   "read",
2573         "?-nonewline? ?len?",
2574         aio_cmd_read,
2575         0,
2576         2,
2577 
2578     },
2579     {   "copyto",
2580         "handle ?size?",
2581         aio_cmd_copy,
2582         1,
2583         2,
2584 
2585     },
2586     {   "getfd",
2587         NULL,
2588         aio_cmd_getfd,
2589         0,
2590         0,
2591 
2592     },
2593     {   "gets",
2594         "?var?",
2595         aio_cmd_gets,
2596         0,
2597         1,
2598 
2599     },
2600     {   "puts",
2601         "?-nonewline? str",
2602         aio_cmd_puts,
2603         1,
2604         2,
2605 
2606     },
2607     {   "isatty",
2608         NULL,
2609         aio_cmd_isatty,
2610         0,
2611         0,
2612 
2613     },
2614     {   "flush",
2615         NULL,
2616         aio_cmd_flush,
2617         0,
2618         0,
2619 
2620     },
2621     {   "eof",
2622         NULL,
2623         aio_cmd_eof,
2624         0,
2625         0,
2626 
2627     },
2628     {   "close",
2629         "?r(ead)|w(rite)?",
2630         aio_cmd_close,
2631         0,
2632         1,
2633         JIM_MODFLAG_FULLARGV,
2634 
2635     },
2636     {   "seek",
2637         "offset ?start|current|end",
2638         aio_cmd_seek,
2639         1,
2640         2,
2641 
2642     },
2643     {   "tell",
2644         NULL,
2645         aio_cmd_tell,
2646         0,
2647         0,
2648 
2649     },
2650     {   "filename",
2651         NULL,
2652         aio_cmd_filename,
2653         0,
2654         0,
2655 
2656     },
2657 #ifdef O_NDELAY
2658     {   "ndelay",
2659         "?0|1?",
2660         aio_cmd_ndelay,
2661         0,
2662         1,
2663 
2664     },
2665 #endif
2666 #ifdef HAVE_FSYNC
2667     {   "sync",
2668         NULL,
2669         aio_cmd_sync,
2670         0,
2671         0,
2672 
2673     },
2674 #endif
2675     {   "buffering",
2676         "none|line|full",
2677         aio_cmd_buffering,
2678         1,
2679         1,
2680 
2681     },
2682 #ifdef jim_ext_eventloop
2683     {   "readable",
2684         "?readable-script?",
2685         aio_cmd_readable,
2686         0,
2687         1,
2688 
2689     },
2690     {   "writable",
2691         "?writable-script?",
2692         aio_cmd_writable,
2693         0,
2694         1,
2695 
2696     },
2697     {   "onexception",
2698         "?exception-script?",
2699         aio_cmd_onexception,
2700         0,
2701         1,
2702 
2703     },
2704 #endif
2705     { NULL }
2706 };
2707 
JimAioSubCmdProc(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2708 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2709 {
2710     return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
2711 }
2712 
JimAioOpenCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2713 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
2714         Jim_Obj *const *argv)
2715 {
2716     const char *mode;
2717 
2718     if (argc != 2 && argc != 3) {
2719         Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
2720         return JIM_ERR;
2721     }
2722 
2723     mode = (argc == 3) ? Jim_String(argv[2]) : "r";
2724 
2725 #ifdef jim_ext_tclcompat
2726     {
2727         const char *filename = Jim_String(argv[1]);
2728 
2729 
2730         if (*filename == '|') {
2731             Jim_Obj *evalObj[3];
2732 
2733             evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
2734             evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
2735             evalObj[2] = Jim_NewStringObj(interp, mode, -1);
2736 
2737             return Jim_EvalObjVector(interp, 3, evalObj);
2738         }
2739     }
2740 #endif
2741     return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
2742 }
2743 
2744 
JimMakeChannel(Jim_Interp * interp,FILE * fh,int fd,Jim_Obj * filename,const char * hdlfmt,int family,const char * mode)2745 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
2746     const char *hdlfmt, int family, const char *mode)
2747 {
2748     AioFile *af;
2749     char buf[AIO_CMD_LEN];
2750     int openFlags = 0;
2751 
2752     snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2753 
2754     if (fh) {
2755         openFlags = AIO_KEEPOPEN;
2756     }
2757 
2758     snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2759     if (!filename) {
2760         filename = Jim_NewStringObj(interp, buf, -1);
2761     }
2762 
2763     Jim_IncrRefCount(filename);
2764 
2765     if (fh == NULL) {
2766         if (fd >= 0) {
2767 #ifndef JIM_ANSIC
2768             fh = fdopen(fd, mode);
2769 #endif
2770         }
2771         else
2772             fh = fopen(Jim_String(filename), mode);
2773 
2774         if (fh == NULL) {
2775             JimAioSetError(interp, filename);
2776 #ifndef JIM_ANSIC
2777             if (fd >= 0) {
2778                 close(fd);
2779             }
2780 #endif
2781             Jim_DecrRefCount(interp, filename);
2782             return NULL;
2783         }
2784     }
2785 
2786 
2787     af = Jim_Alloc(sizeof(*af));
2788     memset(af, 0, sizeof(*af));
2789     af->fp = fh;
2790     af->filename = filename;
2791     af->openFlags = openFlags;
2792 #ifndef JIM_ANSIC
2793     af->fd = fileno(fh);
2794 #ifdef FD_CLOEXEC
2795     if ((openFlags & AIO_KEEPOPEN) == 0) {
2796         (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
2797     }
2798 #endif
2799 #endif
2800     af->addr_family = family;
2801     af->fops = &stdio_fops;
2802     af->ssl = NULL;
2803 
2804     Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
2805 
2806     Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
2807 
2808     return af;
2809 }
2810 
2811 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
JimMakeChannelPair(Jim_Interp * interp,int p[2],Jim_Obj * filename,const char * hdlfmt,int family,const char * mode[2])2812 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
2813     const char *hdlfmt, int family, const char *mode[2])
2814 {
2815     if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
2816         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
2817         Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2818         if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
2819             Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2820             Jim_SetResult(interp, objPtr);
2821             return JIM_OK;
2822         }
2823     }
2824 
2825 
2826     close(p[0]);
2827     close(p[1]);
2828     JimAioSetError(interp, NULL);
2829     return JIM_ERR;
2830 }
2831 #endif
2832 
2833 #ifdef HAVE_PIPE
JimAioPipeCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2834 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2835 {
2836     int p[2];
2837     static const char *mode[2] = { "r", "w" };
2838 
2839     if (argc != 1) {
2840         Jim_WrongNumArgs(interp, 1, argv, "");
2841         return JIM_ERR;
2842     }
2843 
2844     if (pipe(p) != 0) {
2845         JimAioSetError(interp, NULL);
2846         return JIM_ERR;
2847     }
2848 
2849     return JimMakeChannelPair(interp, p, argv[0], "aio.pipe%ld", 0, mode);
2850 }
2851 #endif
2852 
2853 
2854 
Jim_aioInit(Jim_Interp * interp)2855 int Jim_aioInit(Jim_Interp *interp)
2856 {
2857     if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2858         return JIM_ERR;
2859 
2860 #if defined(JIM_SSL)
2861     Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2862 #endif
2863 
2864     Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2865 #ifdef HAVE_SOCKETS
2866     Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2867 #endif
2868 #ifdef HAVE_PIPE
2869     Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
2870 #endif
2871 
2872 
2873     JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
2874     JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
2875     JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
2876 
2877     return JIM_OK;
2878 }
2879 
2880 #include <errno.h>
2881 #include <stdio.h>
2882 #include <string.h>
2883 
2884 
2885 #ifdef HAVE_DIRENT_H
2886 #include <dirent.h>
2887 #endif
2888 
Jim_ReaddirCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2889 int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2890 {
2891     const char *dirPath;
2892     DIR *dirPtr;
2893     struct dirent *entryPtr;
2894     int nocomplain = 0;
2895 
2896     if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) {
2897         nocomplain = 1;
2898     }
2899     if (argc != 2 && !nocomplain) {
2900         Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
2901         return JIM_ERR;
2902     }
2903 
2904     dirPath = Jim_String(argv[1 + nocomplain]);
2905 
2906     dirPtr = opendir(dirPath);
2907     if (dirPtr == NULL) {
2908         if (nocomplain) {
2909             return JIM_OK;
2910         }
2911         Jim_SetResultString(interp, strerror(errno), -1);
2912         return JIM_ERR;
2913     }
2914     else {
2915         Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
2916 
2917         while ((entryPtr = readdir(dirPtr)) != NULL) {
2918             if (entryPtr->d_name[0] == '.') {
2919                 if (entryPtr->d_name[1] == '\0') {
2920                     continue;
2921                 }
2922                 if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0'))
2923                     continue;
2924             }
2925             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1));
2926         }
2927         closedir(dirPtr);
2928 
2929         Jim_SetResult(interp, listObj);
2930 
2931         return JIM_OK;
2932     }
2933 }
2934 
Jim_readdirInit(Jim_Interp * interp)2935 int Jim_readdirInit(Jim_Interp *interp)
2936 {
2937     if (Jim_PackageProvide(interp, "readdir", "1.0", JIM_ERRMSG))
2938         return JIM_ERR;
2939 
2940     Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
2941     return JIM_OK;
2942 }
2943 
2944 #include <stdlib.h>
2945 #include <string.h>
2946 
2947 #if defined(JIM_REGEXP)
2948 #else
2949     #include <regex.h>
2950 #endif
2951 
FreeRegexpInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)2952 static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2953 {
2954     regfree(objPtr->internalRep.ptrIntValue.ptr);
2955     Jim_Free(objPtr->internalRep.ptrIntValue.ptr);
2956 }
2957 
2958 static const Jim_ObjType regexpObjType = {
2959     "regexp",
2960     FreeRegexpInternalRep,
2961     NULL,
2962     NULL,
2963     JIM_TYPE_NONE
2964 };
2965 
SetRegexpFromAny(Jim_Interp * interp,Jim_Obj * objPtr,unsigned flags)2966 static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
2967 {
2968     regex_t *compre;
2969     const char *pattern;
2970     int ret;
2971 
2972 
2973     if (objPtr->typePtr == &regexpObjType &&
2974         objPtr->internalRep.ptrIntValue.ptr && objPtr->internalRep.ptrIntValue.int1 == flags) {
2975 
2976         return objPtr->internalRep.ptrIntValue.ptr;
2977     }
2978 
2979 
2980 
2981 
2982     pattern = Jim_String(objPtr);
2983     compre = Jim_Alloc(sizeof(regex_t));
2984 
2985     if ((ret = regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
2986         char buf[100];
2987 
2988         regerror(ret, compre, buf, sizeof(buf));
2989         Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
2990         regfree(compre);
2991         Jim_Free(compre);
2992         return NULL;
2993     }
2994 
2995     Jim_FreeIntRep(interp, objPtr);
2996 
2997     objPtr->typePtr = &regexpObjType;
2998     objPtr->internalRep.ptrIntValue.int1 = flags;
2999     objPtr->internalRep.ptrIntValue.ptr = compre;
3000 
3001     return compre;
3002 }
3003 
Jim_RegexpCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3004 int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3005 {
3006     int opt_indices = 0;
3007     int opt_all = 0;
3008     int opt_inline = 0;
3009     regex_t *regex;
3010     int match, i, j;
3011     int offset = 0;
3012     regmatch_t *pmatch = NULL;
3013     int source_len;
3014     int result = JIM_OK;
3015     const char *pattern;
3016     const char *source_str;
3017     int num_matches = 0;
3018     int num_vars;
3019     Jim_Obj *resultListObj = NULL;
3020     int regcomp_flags = 0;
3021     int eflags = 0;
3022     int option;
3023     enum {
3024         OPT_INDICES,  OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
3025     };
3026     static const char * const options[] = {
3027         "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
3028     };
3029 
3030     if (argc < 3) {
3031       wrongNumArgs:
3032         Jim_WrongNumArgs(interp, 1, argv,
3033             "?-switch ...? exp string ?matchVar? ?subMatchVar ...?");
3034         return JIM_ERR;
3035     }
3036 
3037     for (i = 1; i < argc; i++) {
3038         const char *opt = Jim_String(argv[i]);
3039 
3040         if (*opt != '-') {
3041             break;
3042         }
3043         if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3044             return JIM_ERR;
3045         }
3046         if (option == OPT_END) {
3047             i++;
3048             break;
3049         }
3050         switch (option) {
3051             case OPT_INDICES:
3052                 opt_indices = 1;
3053                 break;
3054 
3055             case OPT_NOCASE:
3056                 regcomp_flags |= REG_ICASE;
3057                 break;
3058 
3059             case OPT_LINE:
3060                 regcomp_flags |= REG_NEWLINE;
3061                 break;
3062 
3063             case OPT_ALL:
3064                 opt_all = 1;
3065                 break;
3066 
3067             case OPT_INLINE:
3068                 opt_inline = 1;
3069                 break;
3070 
3071             case OPT_START:
3072                 if (++i == argc) {
3073                     goto wrongNumArgs;
3074                 }
3075                 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3076                     return JIM_ERR;
3077                 }
3078                 break;
3079         }
3080     }
3081     if (argc - i < 2) {
3082         goto wrongNumArgs;
3083     }
3084 
3085     regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
3086     if (!regex) {
3087         return JIM_ERR;
3088     }
3089 
3090     pattern = Jim_String(argv[i]);
3091     source_str = Jim_GetString(argv[i + 1], &source_len);
3092 
3093     num_vars = argc - i - 2;
3094 
3095     if (opt_inline) {
3096         if (num_vars) {
3097             Jim_SetResultString(interp, "regexp match variables not allowed when using -inline",
3098                 -1);
3099             result = JIM_ERR;
3100             goto done;
3101         }
3102         num_vars = regex->re_nsub + 1;
3103     }
3104 
3105     pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch));
3106 
3107     if (offset) {
3108         if (offset < 0) {
3109             offset += source_len + 1;
3110         }
3111         if (offset > source_len) {
3112             source_str += source_len;
3113         }
3114         else if (offset > 0) {
3115             source_str += offset;
3116         }
3117         eflags |= REG_NOTBOL;
3118     }
3119 
3120     if (opt_inline) {
3121         resultListObj = Jim_NewListObj(interp, NULL, 0);
3122     }
3123 
3124   next_match:
3125     match = regexec(regex, source_str, num_vars + 1, pmatch, eflags);
3126     if (match >= REG_BADPAT) {
3127         char buf[100];
3128 
3129         regerror(match, regex, buf, sizeof(buf));
3130         Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3131         result = JIM_ERR;
3132         goto done;
3133     }
3134 
3135     if (match == REG_NOMATCH) {
3136         goto done;
3137     }
3138 
3139     num_matches++;
3140 
3141     if (opt_all && !opt_inline) {
3142 
3143         goto try_next_match;
3144     }
3145 
3146 
3147     j = 0;
3148     for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
3149         Jim_Obj *resultObj;
3150 
3151         if (opt_indices) {
3152             resultObj = Jim_NewListObj(interp, NULL, 0);
3153         }
3154         else {
3155             resultObj = Jim_NewStringObj(interp, "", 0);
3156         }
3157 
3158         if (pmatch[j].rm_so == -1) {
3159             if (opt_indices) {
3160                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3161                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3162             }
3163         }
3164         else {
3165             int len = pmatch[j].rm_eo - pmatch[j].rm_so;
3166 
3167             if (opt_indices) {
3168                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
3169                         offset + pmatch[j].rm_so));
3170                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
3171                         offset + pmatch[j].rm_so + len - 1));
3172             }
3173             else {
3174                 Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, len);
3175             }
3176         }
3177 
3178         if (opt_inline) {
3179             Jim_ListAppendElement(interp, resultListObj, resultObj);
3180         }
3181         else {
3182 
3183             result = Jim_SetVariable(interp, argv[i], resultObj);
3184 
3185             if (result != JIM_OK) {
3186                 Jim_FreeObj(interp, resultObj);
3187                 break;
3188             }
3189         }
3190     }
3191 
3192   try_next_match:
3193     if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
3194         if (pmatch[0].rm_eo) {
3195             offset += pmatch[0].rm_eo;
3196             source_str += pmatch[0].rm_eo;
3197         }
3198         else {
3199             source_str++;
3200             offset++;
3201         }
3202         if (*source_str) {
3203             eflags = REG_NOTBOL;
3204             goto next_match;
3205         }
3206     }
3207 
3208   done:
3209     if (result == JIM_OK) {
3210         if (opt_inline) {
3211             Jim_SetResult(interp, resultListObj);
3212         }
3213         else {
3214             Jim_SetResultInt(interp, num_matches);
3215         }
3216     }
3217 
3218     Jim_Free(pmatch);
3219     return result;
3220 }
3221 
3222 #define MAX_SUB_MATCHES 50
3223 
Jim_RegsubCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3224 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3225 {
3226     int regcomp_flags = 0;
3227     int regexec_flags = 0;
3228     int opt_all = 0;
3229     int offset = 0;
3230     regex_t *regex;
3231     const char *p;
3232     int result;
3233     regmatch_t pmatch[MAX_SUB_MATCHES + 1];
3234     int num_matches = 0;
3235 
3236     int i, j, n;
3237     Jim_Obj *varname;
3238     Jim_Obj *resultObj;
3239     const char *source_str;
3240     int source_len;
3241     const char *replace_str;
3242     int replace_len;
3243     const char *pattern;
3244     int option;
3245     enum {
3246         OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_END
3247     };
3248     static const char * const options[] = {
3249         "-nocase", "-line", "-all", "-start", "--", NULL
3250     };
3251 
3252     if (argc < 4) {
3253       wrongNumArgs:
3254         Jim_WrongNumArgs(interp, 1, argv,
3255             "?-switch ...? exp string subSpec ?varName?");
3256         return JIM_ERR;
3257     }
3258 
3259     for (i = 1; i < argc; i++) {
3260         const char *opt = Jim_String(argv[i]);
3261 
3262         if (*opt != '-') {
3263             break;
3264         }
3265         if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3266             return JIM_ERR;
3267         }
3268         if (option == OPT_END) {
3269             i++;
3270             break;
3271         }
3272         switch (option) {
3273             case OPT_NOCASE:
3274                 regcomp_flags |= REG_ICASE;
3275                 break;
3276 
3277             case OPT_LINE:
3278                 regcomp_flags |= REG_NEWLINE;
3279                 break;
3280 
3281             case OPT_ALL:
3282                 opt_all = 1;
3283                 break;
3284 
3285             case OPT_START:
3286                 if (++i == argc) {
3287                     goto wrongNumArgs;
3288                 }
3289                 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3290                     return JIM_ERR;
3291                 }
3292                 break;
3293         }
3294     }
3295     if (argc - i != 3 && argc - i != 4) {
3296         goto wrongNumArgs;
3297     }
3298 
3299     regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
3300     if (!regex) {
3301         return JIM_ERR;
3302     }
3303     pattern = Jim_String(argv[i]);
3304 
3305     source_str = Jim_GetString(argv[i + 1], &source_len);
3306     replace_str = Jim_GetString(argv[i + 2], &replace_len);
3307     varname = argv[i + 3];
3308 
3309 
3310     resultObj = Jim_NewStringObj(interp, "", 0);
3311 
3312     if (offset) {
3313         if (offset < 0) {
3314             offset += source_len + 1;
3315         }
3316         if (offset > source_len) {
3317             offset = source_len;
3318         }
3319         else if (offset < 0) {
3320             offset = 0;
3321         }
3322     }
3323 
3324 
3325     Jim_AppendString(interp, resultObj, source_str, offset);
3326 
3327 
3328     n = source_len - offset;
3329     p = source_str + offset;
3330     do {
3331         int match = regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
3332 
3333         if (match >= REG_BADPAT) {
3334             char buf[100];
3335 
3336             regerror(match, regex, buf, sizeof(buf));
3337             Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3338             return JIM_ERR;
3339         }
3340         if (match == REG_NOMATCH) {
3341             break;
3342         }
3343 
3344         num_matches++;
3345 
3346         Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
3347 
3348 
3349         for (j = 0; j < replace_len; j++) {
3350             int idx;
3351             int c = replace_str[j];
3352 
3353             if (c == '&') {
3354                 idx = 0;
3355             }
3356             else if (c == '\\' && j < replace_len) {
3357                 c = replace_str[++j];
3358                 if ((c >= '0') && (c <= '9')) {
3359                     idx = c - '0';
3360                 }
3361                 else if ((c == '\\') || (c == '&')) {
3362                     Jim_AppendString(interp, resultObj, replace_str + j, 1);
3363                     continue;
3364                 }
3365                 else {
3366                     Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2);
3367                     continue;
3368                 }
3369             }
3370             else {
3371                 Jim_AppendString(interp, resultObj, replace_str + j, 1);
3372                 continue;
3373             }
3374             if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
3375                 Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
3376                     pmatch[idx].rm_eo - pmatch[idx].rm_so);
3377             }
3378         }
3379 
3380         p += pmatch[0].rm_eo;
3381         n -= pmatch[0].rm_eo;
3382 
3383 
3384         if (!opt_all || n == 0) {
3385             break;
3386         }
3387 
3388 
3389         if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
3390             break;
3391         }
3392 
3393 
3394         if (pattern[0] == '\0' && n) {
3395 
3396             Jim_AppendString(interp, resultObj, p, 1);
3397             p++;
3398             n--;
3399         }
3400 
3401         regexec_flags |= REG_NOTBOL;
3402     } while (n);
3403 
3404     Jim_AppendString(interp, resultObj, p, -1);
3405 
3406 
3407     if (argc - i == 4) {
3408         result = Jim_SetVariable(interp, varname, resultObj);
3409 
3410         if (result == JIM_OK) {
3411             Jim_SetResultInt(interp, num_matches);
3412         }
3413         else {
3414             Jim_FreeObj(interp, resultObj);
3415         }
3416     }
3417     else {
3418         Jim_SetResult(interp, resultObj);
3419         result = JIM_OK;
3420     }
3421 
3422     return result;
3423 }
3424 
Jim_regexpInit(Jim_Interp * interp)3425 int Jim_regexpInit(Jim_Interp *interp)
3426 {
3427     if (Jim_PackageProvide(interp, "regexp", "1.0", JIM_ERRMSG))
3428         return JIM_ERR;
3429 
3430     Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
3431     Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
3432     return JIM_OK;
3433 }
3434 
3435 #include <limits.h>
3436 #include <stdlib.h>
3437 #include <string.h>
3438 #include <stdio.h>
3439 #include <errno.h>
3440 #include <sys/stat.h>
3441 
3442 
3443 #ifdef HAVE_UTIMES
3444 #include <sys/time.h>
3445 #endif
3446 #ifdef HAVE_UNISTD_H
3447 #include <unistd.h>
3448 #elif defined(_MSC_VER)
3449 #include <direct.h>
3450 #define F_OK 0
3451 #define W_OK 2
3452 #define R_OK 4
3453 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
3454 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
3455 #endif
3456 
3457 # ifndef MAXPATHLEN
3458 # define MAXPATHLEN JIM_PATH_LEN
3459 # endif
3460 
3461 #if defined(__MINGW32__) || defined(__MSYS__) || defined(_MSC_VER)
3462 #define ISWINDOWS 1
3463 #else
3464 #define ISWINDOWS 0
3465 #endif
3466 
3467 
3468 #if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
3469     #define STAT_MTIME_US(STAT) ((STAT).st_mtimespec.tv_sec * 1000000ll + (STAT).st_mtimespec.tv_nsec / 1000)
3470 #elif defined(HAVE_STRUCT_STAT_ST_MTIM)
3471     #define STAT_MTIME_US(STAT) ((STAT).st_mtim.tv_sec * 1000000ll + (STAT).st_mtim.tv_nsec / 1000)
3472 #endif
3473 
3474 
JimGetFileType(int mode)3475 static const char *JimGetFileType(int mode)
3476 {
3477     if (S_ISREG(mode)) {
3478         return "file";
3479     }
3480     else if (S_ISDIR(mode)) {
3481         return "directory";
3482     }
3483 #ifdef S_ISCHR
3484     else if (S_ISCHR(mode)) {
3485         return "characterSpecial";
3486     }
3487 #endif
3488 #ifdef S_ISBLK
3489     else if (S_ISBLK(mode)) {
3490         return "blockSpecial";
3491     }
3492 #endif
3493 #ifdef S_ISFIFO
3494     else if (S_ISFIFO(mode)) {
3495         return "fifo";
3496     }
3497 #endif
3498 #ifdef S_ISLNK
3499     else if (S_ISLNK(mode)) {
3500         return "link";
3501     }
3502 #endif
3503 #ifdef S_ISSOCK
3504     else if (S_ISSOCK(mode)) {
3505         return "socket";
3506     }
3507 #endif
3508     return "unknown";
3509 }
3510 
AppendStatElement(Jim_Interp * interp,Jim_Obj * listObj,const char * key,jim_wide value)3511 static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
3512 {
3513     Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
3514     Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
3515 }
3516 
StoreStatData(Jim_Interp * interp,Jim_Obj * varName,const struct stat * sb)3517 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
3518 {
3519 
3520     Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
3521 
3522     AppendStatElement(interp, listObj, "dev", sb->st_dev);
3523     AppendStatElement(interp, listObj, "ino", sb->st_ino);
3524     AppendStatElement(interp, listObj, "mode", sb->st_mode);
3525     AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
3526     AppendStatElement(interp, listObj, "uid", sb->st_uid);
3527     AppendStatElement(interp, listObj, "gid", sb->st_gid);
3528     AppendStatElement(interp, listObj, "size", sb->st_size);
3529     AppendStatElement(interp, listObj, "atime", sb->st_atime);
3530     AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
3531     AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
3532 #ifdef STAT_MTIME_US
3533     AppendStatElement(interp, listObj, "mtimeus", STAT_MTIME_US(*sb));
3534 #endif
3535     Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
3536     Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));
3537 
3538 
3539     if (varName) {
3540         Jim_Obj *objPtr;
3541         objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
3542 
3543         if (objPtr) {
3544             Jim_Obj *objv[2];
3545 
3546             objv[0] = objPtr;
3547             objv[1] = listObj;
3548 
3549             objPtr = Jim_DictMerge(interp, 2, objv);
3550             if (objPtr == NULL) {
3551 
3552                 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
3553                 Jim_FreeNewObj(interp, listObj);
3554                 return JIM_ERR;
3555             }
3556 
3557             Jim_InvalidateStringRep(objPtr);
3558 
3559             Jim_FreeNewObj(interp, listObj);
3560             listObj = objPtr;
3561         }
3562         Jim_SetVariable(interp, varName, listObj);
3563     }
3564 
3565 
3566     Jim_SetResult(interp, listObj);
3567 
3568     return JIM_OK;
3569 }
3570 
file_cmd_dirname(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3571 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3572 {
3573     const char *path = Jim_String(argv[0]);
3574     const char *p = strrchr(path, '/');
3575 
3576     if (!p && path[0] == '.' && path[1] == '.' && path[2] == '\0') {
3577         Jim_SetResultString(interp, "..", -1);
3578     } else if (!p) {
3579         Jim_SetResultString(interp, ".", -1);
3580     }
3581     else if (p == path) {
3582         Jim_SetResultString(interp, "/", -1);
3583     }
3584     else if (ISWINDOWS && p[-1] == ':') {
3585 
3586         Jim_SetResultString(interp, path, p - path + 1);
3587     }
3588     else {
3589         Jim_SetResultString(interp, path, p - path);
3590     }
3591     return JIM_OK;
3592 }
3593 
file_cmd_rootname(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3594 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3595 {
3596     const char *path = Jim_String(argv[0]);
3597     const char *lastSlash = strrchr(path, '/');
3598     const char *p = strrchr(path, '.');
3599 
3600     if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
3601         Jim_SetResult(interp, argv[0]);
3602     }
3603     else {
3604         Jim_SetResultString(interp, path, p - path);
3605     }
3606     return JIM_OK;
3607 }
3608 
file_cmd_extension(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3609 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3610 {
3611     const char *path = Jim_String(argv[0]);
3612     const char *lastSlash = strrchr(path, '/');
3613     const char *p = strrchr(path, '.');
3614 
3615     if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
3616         p = "";
3617     }
3618     Jim_SetResultString(interp, p, -1);
3619     return JIM_OK;
3620 }
3621 
file_cmd_tail(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3622 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3623 {
3624     const char *path = Jim_String(argv[0]);
3625     const char *lastSlash = strrchr(path, '/');
3626 
3627     if (lastSlash) {
3628         Jim_SetResultString(interp, lastSlash + 1, -1);
3629     }
3630     else {
3631         Jim_SetResult(interp, argv[0]);
3632     }
3633     return JIM_OK;
3634 }
3635 
file_cmd_normalize(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3636 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3637 {
3638 #ifdef HAVE_REALPATH
3639     const char *path = Jim_String(argv[0]);
3640     char *newname = Jim_Alloc(MAXPATHLEN + 1);
3641 
3642     if (realpath(path, newname)) {
3643         Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
3644         return JIM_OK;
3645     }
3646     else {
3647         Jim_Free(newname);
3648         Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
3649         return JIM_ERR;
3650     }
3651 #else
3652     Jim_SetResultString(interp, "Not implemented", -1);
3653     return JIM_ERR;
3654 #endif
3655 }
3656 
file_cmd_join(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3657 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3658 {
3659     int i;
3660     char *newname = Jim_Alloc(MAXPATHLEN + 1);
3661     char *last = newname;
3662 
3663     *newname = 0;
3664 
3665 
3666     for (i = 0; i < argc; i++) {
3667         int len;
3668         const char *part = Jim_GetString(argv[i], &len);
3669 
3670         if (*part == '/') {
3671 
3672             last = newname;
3673         }
3674         else if (ISWINDOWS && strchr(part, ':')) {
3675 
3676             last = newname;
3677         }
3678         else if (part[0] == '.') {
3679             if (part[1] == '/') {
3680                 part += 2;
3681                 len -= 2;
3682             }
3683             else if (part[1] == 0 && last != newname) {
3684 
3685                 continue;
3686             }
3687         }
3688 
3689 
3690         if (last != newname && last[-1] != '/') {
3691             *last++ = '/';
3692         }
3693 
3694         if (len) {
3695             if (last + len - newname >= MAXPATHLEN) {
3696                 Jim_Free(newname);
3697                 Jim_SetResultString(interp, "Path too long", -1);
3698                 return JIM_ERR;
3699             }
3700             memcpy(last, part, len);
3701             last += len;
3702         }
3703 
3704 
3705         if (last > newname + 1 && last[-1] == '/') {
3706 
3707             if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
3708                 *--last = 0;
3709             }
3710         }
3711     }
3712 
3713     *last = 0;
3714 
3715 
3716 
3717     Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
3718 
3719     return JIM_OK;
3720 }
3721 
file_access(Jim_Interp * interp,Jim_Obj * filename,int mode)3722 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
3723 {
3724     Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1);
3725 
3726     return JIM_OK;
3727 }
3728 
file_cmd_readable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3729 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3730 {
3731     return file_access(interp, argv[0], R_OK);
3732 }
3733 
file_cmd_writable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3734 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3735 {
3736     return file_access(interp, argv[0], W_OK);
3737 }
3738 
file_cmd_executable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3739 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3740 {
3741 #ifdef X_OK
3742     return file_access(interp, argv[0], X_OK);
3743 #else
3744 
3745     Jim_SetResultBool(interp, 1);
3746     return JIM_OK;
3747 #endif
3748 }
3749 
file_cmd_exists(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3750 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3751 {
3752     return file_access(interp, argv[0], F_OK);
3753 }
3754 
file_cmd_delete(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3755 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3756 {
3757     int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
3758 
3759     if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
3760         argc++;
3761         argv--;
3762     }
3763 
3764     while (argc--) {
3765         const char *path = Jim_String(argv[0]);
3766 
3767         if (unlink(path) == -1 && errno != ENOENT) {
3768             if (rmdir(path) == -1) {
3769 
3770                 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
3771                     Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
3772                         strerror(errno));
3773                     return JIM_ERR;
3774                 }
3775             }
3776         }
3777         argv++;
3778     }
3779     return JIM_OK;
3780 }
3781 
3782 #ifdef HAVE_MKDIR_ONE_ARG
3783 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
3784 #else
3785 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
3786 #endif
3787 
mkdir_all(char * path)3788 static int mkdir_all(char *path)
3789 {
3790     int ok = 1;
3791 
3792 
3793     goto first;
3794 
3795     while (ok--) {
3796 
3797         {
3798             char *slash = strrchr(path, '/');
3799 
3800             if (slash && slash != path) {
3801                 *slash = 0;
3802                 if (mkdir_all(path) != 0) {
3803                     return -1;
3804                 }
3805                 *slash = '/';
3806             }
3807         }
3808       first:
3809         if (MKDIR_DEFAULT(path) == 0) {
3810             return 0;
3811         }
3812         if (errno == ENOENT) {
3813 
3814             continue;
3815         }
3816 
3817         if (errno == EEXIST) {
3818             struct stat sb;
3819 
3820             if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
3821                 return 0;
3822             }
3823 
3824             errno = EEXIST;
3825         }
3826 
3827         break;
3828     }
3829     return -1;
3830 }
3831 
file_cmd_mkdir(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3832 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3833 {
3834     while (argc--) {
3835         char *path = Jim_StrDup(Jim_String(argv[0]));
3836         int rc = mkdir_all(path);
3837 
3838         Jim_Free(path);
3839         if (rc != 0) {
3840             Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
3841                 strerror(errno));
3842             return JIM_ERR;
3843         }
3844         argv++;
3845     }
3846     return JIM_OK;
3847 }
3848 
file_cmd_tempfile(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3849 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3850 {
3851     int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL, 0);
3852 
3853     if (fd < 0) {
3854         return JIM_ERR;
3855     }
3856     close(fd);
3857 
3858     return JIM_OK;
3859 }
3860 
file_cmd_rename(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3861 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3862 {
3863     const char *source;
3864     const char *dest;
3865     int force = 0;
3866 
3867     if (argc == 3) {
3868         if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
3869             return -1;
3870         }
3871         force++;
3872         argv++;
3873         argc--;
3874     }
3875 
3876     source = Jim_String(argv[0]);
3877     dest = Jim_String(argv[1]);
3878 
3879     if (!force && access(dest, F_OK) == 0) {
3880         Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
3881             argv[1]);
3882         return JIM_ERR;
3883     }
3884 
3885     if (rename(source, dest) != 0) {
3886         Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3887             strerror(errno));
3888         return JIM_ERR;
3889     }
3890 
3891     return JIM_OK;
3892 }
3893 
3894 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
file_cmd_link(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3895 static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3896 {
3897     int ret;
3898     const char *source;
3899     const char *dest;
3900     static const char * const options[] = { "-hard", "-symbolic", NULL };
3901     enum { OPT_HARD, OPT_SYMBOLIC, };
3902     int option = OPT_HARD;
3903 
3904     if (argc == 3) {
3905         if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
3906             return JIM_ERR;
3907         }
3908         argv++;
3909         argc--;
3910     }
3911 
3912     dest = Jim_String(argv[0]);
3913     source = Jim_String(argv[1]);
3914 
3915     if (option == OPT_HARD) {
3916         ret = link(source, dest);
3917     }
3918     else {
3919         ret = symlink(source, dest);
3920     }
3921 
3922     if (ret != 0) {
3923         Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3924             strerror(errno));
3925         return JIM_ERR;
3926     }
3927 
3928     return JIM_OK;
3929 }
3930 #endif
3931 
file_stat(Jim_Interp * interp,Jim_Obj * filename,struct stat * sb)3932 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3933 {
3934     const char *path = Jim_String(filename);
3935 
3936     if (stat(path, sb) == -1) {
3937         Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3938         return JIM_ERR;
3939     }
3940     return JIM_OK;
3941 }
3942 
3943 #ifdef HAVE_LSTAT
file_lstat(Jim_Interp * interp,Jim_Obj * filename,struct stat * sb)3944 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3945 {
3946     const char *path = Jim_String(filename);
3947 
3948     if (lstat(path, sb) == -1) {
3949         Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3950         return JIM_ERR;
3951     }
3952     return JIM_OK;
3953 }
3954 #else
3955 #define file_lstat file_stat
3956 #endif
3957 
file_cmd_atime(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3958 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3959 {
3960     struct stat sb;
3961 
3962     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3963         return JIM_ERR;
3964     }
3965     Jim_SetResultInt(interp, sb.st_atime);
3966     return JIM_OK;
3967 }
3968 
JimSetFileTimes(Jim_Interp * interp,const char * filename,jim_wide us)3969 static int JimSetFileTimes(Jim_Interp *interp, const char *filename, jim_wide us)
3970 {
3971 #ifdef HAVE_UTIMES
3972     struct timeval times[2];
3973 
3974     times[1].tv_sec = times[0].tv_sec = us / 1000000;
3975     times[1].tv_usec = times[0].tv_usec = us % 1000000;
3976 
3977     if (utimes(filename, times) != 0) {
3978         Jim_SetResultFormatted(interp, "can't set time on \"%s\": %s", filename, strerror(errno));
3979         return JIM_ERR;
3980     }
3981     return JIM_OK;
3982 #else
3983     Jim_SetResultString(interp, "Not implemented", -1);
3984     return JIM_ERR;
3985 #endif
3986 }
3987 
file_cmd_mtime(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3988 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3989 {
3990     struct stat sb;
3991 
3992     if (argc == 2) {
3993         jim_wide secs;
3994         if (Jim_GetWide(interp, argv[1], &secs) != JIM_OK) {
3995             return JIM_ERR;
3996         }
3997         return JimSetFileTimes(interp, Jim_String(argv[0]), secs * 1000000);
3998     }
3999     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4000         return JIM_ERR;
4001     }
4002     Jim_SetResultInt(interp, sb.st_mtime);
4003     return JIM_OK;
4004 }
4005 
4006 #ifdef STAT_MTIME_US
file_cmd_mtimeus(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4007 static int file_cmd_mtimeus(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4008 {
4009     struct stat sb;
4010 
4011     if (argc == 2) {
4012         jim_wide us;
4013         if (Jim_GetWide(interp, argv[1], &us) != JIM_OK) {
4014             return JIM_ERR;
4015         }
4016         return JimSetFileTimes(interp, Jim_String(argv[0]), us);
4017     }
4018     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4019         return JIM_ERR;
4020     }
4021     Jim_SetResultInt(interp, STAT_MTIME_US(sb));
4022     return JIM_OK;
4023 }
4024 #endif
4025 
file_cmd_copy(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4026 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4027 {
4028     return Jim_EvalPrefix(interp, "file copy", argc, argv);
4029 }
4030 
file_cmd_size(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4031 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4032 {
4033     struct stat sb;
4034 
4035     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4036         return JIM_ERR;
4037     }
4038     Jim_SetResultInt(interp, sb.st_size);
4039     return JIM_OK;
4040 }
4041 
file_cmd_isdirectory(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4042 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4043 {
4044     struct stat sb;
4045     int ret = 0;
4046 
4047     if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4048         ret = S_ISDIR(sb.st_mode);
4049     }
4050     Jim_SetResultInt(interp, ret);
4051     return JIM_OK;
4052 }
4053 
file_cmd_isfile(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4054 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4055 {
4056     struct stat sb;
4057     int ret = 0;
4058 
4059     if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4060         ret = S_ISREG(sb.st_mode);
4061     }
4062     Jim_SetResultInt(interp, ret);
4063     return JIM_OK;
4064 }
4065 
4066 #ifdef HAVE_GETEUID
file_cmd_owned(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4067 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4068 {
4069     struct stat sb;
4070     int ret = 0;
4071 
4072     if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4073         ret = (geteuid() == sb.st_uid);
4074     }
4075     Jim_SetResultInt(interp, ret);
4076     return JIM_OK;
4077 }
4078 #endif
4079 
4080 #if defined(HAVE_READLINK)
file_cmd_readlink(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4081 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4082 {
4083     const char *path = Jim_String(argv[0]);
4084     char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
4085 
4086     int linkLength = readlink(path, linkValue, MAXPATHLEN);
4087 
4088     if (linkLength == -1) {
4089         Jim_Free(linkValue);
4090         Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
4091         return JIM_ERR;
4092     }
4093     linkValue[linkLength] = 0;
4094     Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
4095     return JIM_OK;
4096 }
4097 #endif
4098 
file_cmd_type(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4099 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4100 {
4101     struct stat sb;
4102 
4103     if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
4104         return JIM_ERR;
4105     }
4106     Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
4107     return JIM_OK;
4108 }
4109 
4110 #ifdef HAVE_LSTAT
file_cmd_lstat(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4111 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4112 {
4113     struct stat sb;
4114 
4115     if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
4116         return JIM_ERR;
4117     }
4118     return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
4119 }
4120 #else
4121 #define file_cmd_lstat file_cmd_stat
4122 #endif
4123 
file_cmd_stat(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4124 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4125 {
4126     struct stat sb;
4127 
4128     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4129         return JIM_ERR;
4130     }
4131     return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
4132 }
4133 
4134 static const jim_subcmd_type file_command_table[] = {
4135     {   "atime",
4136         "name",
4137         file_cmd_atime,
4138         1,
4139         1,
4140 
4141     },
4142     {   "mtime",
4143         "name ?time?",
4144         file_cmd_mtime,
4145         1,
4146         2,
4147 
4148     },
4149 #ifdef STAT_MTIME_US
4150     {   "mtimeus",
4151         "name ?time?",
4152         file_cmd_mtimeus,
4153         1,
4154         2,
4155 
4156     },
4157 #endif
4158     {   "copy",
4159         "?-force? source dest",
4160         file_cmd_copy,
4161         2,
4162         3,
4163 
4164     },
4165     {   "dirname",
4166         "name",
4167         file_cmd_dirname,
4168         1,
4169         1,
4170 
4171     },
4172     {   "rootname",
4173         "name",
4174         file_cmd_rootname,
4175         1,
4176         1,
4177 
4178     },
4179     {   "extension",
4180         "name",
4181         file_cmd_extension,
4182         1,
4183         1,
4184 
4185     },
4186     {   "tail",
4187         "name",
4188         file_cmd_tail,
4189         1,
4190         1,
4191 
4192     },
4193     {   "normalize",
4194         "name",
4195         file_cmd_normalize,
4196         1,
4197         1,
4198 
4199     },
4200     {   "join",
4201         "name ?name ...?",
4202         file_cmd_join,
4203         1,
4204         -1,
4205 
4206     },
4207     {   "readable",
4208         "name",
4209         file_cmd_readable,
4210         1,
4211         1,
4212 
4213     },
4214     {   "writable",
4215         "name",
4216         file_cmd_writable,
4217         1,
4218         1,
4219 
4220     },
4221     {   "executable",
4222         "name",
4223         file_cmd_executable,
4224         1,
4225         1,
4226 
4227     },
4228     {   "exists",
4229         "name",
4230         file_cmd_exists,
4231         1,
4232         1,
4233 
4234     },
4235     {   "delete",
4236         "?-force|--? name ...",
4237         file_cmd_delete,
4238         1,
4239         -1,
4240 
4241     },
4242     {   "mkdir",
4243         "dir ...",
4244         file_cmd_mkdir,
4245         1,
4246         -1,
4247 
4248     },
4249     {   "tempfile",
4250         "?template?",
4251         file_cmd_tempfile,
4252         0,
4253         1,
4254 
4255     },
4256     {   "rename",
4257         "?-force? source dest",
4258         file_cmd_rename,
4259         2,
4260         3,
4261 
4262     },
4263 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
4264     {   "link",
4265         "?-symbolic|-hard? newname target",
4266         file_cmd_link,
4267         2,
4268         3,
4269 
4270     },
4271 #endif
4272 #if defined(HAVE_READLINK)
4273     {   "readlink",
4274         "name",
4275         file_cmd_readlink,
4276         1,
4277         1,
4278 
4279     },
4280 #endif
4281     {   "size",
4282         "name",
4283         file_cmd_size,
4284         1,
4285         1,
4286 
4287     },
4288     {   "stat",
4289         "name ?var?",
4290         file_cmd_stat,
4291         1,
4292         2,
4293 
4294     },
4295     {   "lstat",
4296         "name ?var?",
4297         file_cmd_lstat,
4298         1,
4299         2,
4300 
4301     },
4302     {   "type",
4303         "name",
4304         file_cmd_type,
4305         1,
4306         1,
4307 
4308     },
4309 #ifdef HAVE_GETEUID
4310     {   "owned",
4311         "name",
4312         file_cmd_owned,
4313         1,
4314         1,
4315 
4316     },
4317 #endif
4318     {   "isdirectory",
4319         "name",
4320         file_cmd_isdirectory,
4321         1,
4322         1,
4323 
4324     },
4325     {   "isfile",
4326         "name",
4327         file_cmd_isfile,
4328         1,
4329         1,
4330 
4331     },
4332     {
4333         NULL
4334     }
4335 };
4336 
Jim_CdCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4337 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4338 {
4339     const char *path;
4340 
4341     if (argc != 2) {
4342         Jim_WrongNumArgs(interp, 1, argv, "dirname");
4343         return JIM_ERR;
4344     }
4345 
4346     path = Jim_String(argv[1]);
4347 
4348     if (chdir(path) != 0) {
4349         Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
4350             strerror(errno));
4351         return JIM_ERR;
4352     }
4353     return JIM_OK;
4354 }
4355 
Jim_PwdCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4356 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4357 {
4358     char *cwd = Jim_Alloc(MAXPATHLEN);
4359 
4360     if (getcwd(cwd, MAXPATHLEN) == NULL) {
4361         Jim_SetResultString(interp, "Failed to get pwd", -1);
4362         Jim_Free(cwd);
4363         return JIM_ERR;
4364     }
4365     else if (ISWINDOWS) {
4366 
4367         char *p = cwd;
4368         while ((p = strchr(p, '\\')) != NULL) {
4369             *p++ = '/';
4370         }
4371     }
4372 
4373     Jim_SetResultString(interp, cwd, -1);
4374 
4375     Jim_Free(cwd);
4376     return JIM_OK;
4377 }
4378 
Jim_fileInit(Jim_Interp * interp)4379 int Jim_fileInit(Jim_Interp *interp)
4380 {
4381     if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
4382         return JIM_ERR;
4383 
4384     Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
4385     Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
4386     Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
4387     return JIM_OK;
4388 }
4389 
4390 #ifndef _GNU_SOURCE
4391 #define _GNU_SOURCE
4392 #endif
4393 #include <string.h>
4394 #include <ctype.h>
4395 
4396 
4397 #if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
Jim_ExecCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4398 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4399 {
4400     Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
4401     int i, j;
4402     int rc;
4403 
4404 
4405     for (i = 1; i < argc; i++) {
4406         int len;
4407         const char *arg = Jim_GetString(argv[i], &len);
4408 
4409         if (i > 1) {
4410             Jim_AppendString(interp, cmdlineObj, " ", 1);
4411         }
4412         if (strpbrk(arg, "\\\" ") == NULL) {
4413 
4414             Jim_AppendString(interp, cmdlineObj, arg, len);
4415             continue;
4416         }
4417 
4418         Jim_AppendString(interp, cmdlineObj, "\"", 1);
4419         for (j = 0; j < len; j++) {
4420             if (arg[j] == '\\' || arg[j] == '"') {
4421                 Jim_AppendString(interp, cmdlineObj, "\\", 1);
4422             }
4423             Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
4424         }
4425         Jim_AppendString(interp, cmdlineObj, "\"", 1);
4426     }
4427     rc = system(Jim_String(cmdlineObj));
4428 
4429     Jim_FreeNewObj(interp, cmdlineObj);
4430 
4431     if (rc) {
4432         Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
4433         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4434         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
4435         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
4436         Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
4437         return JIM_ERR;
4438     }
4439 
4440     return JIM_OK;
4441 }
4442 
Jim_execInit(Jim_Interp * interp)4443 int Jim_execInit(Jim_Interp *interp)
4444 {
4445     if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
4446         return JIM_ERR;
4447 
4448     Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
4449     return JIM_OK;
4450 }
4451 #else
4452 
4453 
4454 #include <errno.h>
4455 #include <signal.h>
4456 #include <sys/stat.h>
4457 
4458 struct WaitInfoTable;
4459 
4460 static char **JimOriginalEnviron(void);
4461 static char **JimSaveEnv(char **env);
4462 static void JimRestoreEnv(char **env);
4463 static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
4464     pidtype **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
4465 static void JimDetachPids(struct WaitInfoTable *table, int numPids, const pidtype *pidPtr);
4466 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj);
4467 static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
4468 
4469 #if defined(__MINGW32__)
4470 static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId);
4471 #endif
4472 
Jim_RemoveTrailingNewline(Jim_Obj * objPtr)4473 static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
4474 {
4475     int len;
4476     const char *s = Jim_GetString(objPtr, &len);
4477 
4478     if (len > 0 && s[len - 1] == '\n') {
4479         objPtr->length--;
4480         objPtr->bytes[objPtr->length] = '\0';
4481     }
4482 }
4483 
JimAppendStreamToString(Jim_Interp * interp,int fd,Jim_Obj * strObj)4484 static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj)
4485 {
4486     char buf[256];
4487     FILE *fh = fdopen(fd, "r");
4488     int ret = 0;
4489 
4490     if (fh == NULL) {
4491         return -1;
4492     }
4493 
4494     while (1) {
4495         int retval = fread(buf, 1, sizeof(buf), fh);
4496         if (retval > 0) {
4497             ret = 1;
4498             Jim_AppendString(interp, strObj, buf, retval);
4499         }
4500         if (retval != sizeof(buf)) {
4501             break;
4502         }
4503     }
4504     fclose(fh);
4505     return ret;
4506 }
4507 
JimBuildEnv(Jim_Interp * interp)4508 static char **JimBuildEnv(Jim_Interp *interp)
4509 {
4510     int i;
4511     int size;
4512     int num;
4513     int n;
4514     char **envptr;
4515     char *envdata;
4516 
4517     Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
4518 
4519     if (!objPtr) {
4520         return JimOriginalEnviron();
4521     }
4522 
4523 
4524 
4525     num = Jim_ListLength(interp, objPtr);
4526     if (num % 2) {
4527 
4528         num--;
4529     }
4530     size = Jim_Length(objPtr) + 2;
4531 
4532     envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
4533     envdata = (char *)&envptr[num / 2 + 1];
4534 
4535     n = 0;
4536     for (i = 0; i < num; i += 2) {
4537         const char *s1, *s2;
4538         Jim_Obj *elemObj;
4539 
4540         Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
4541         s1 = Jim_String(elemObj);
4542         Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
4543         s2 = Jim_String(elemObj);
4544 
4545         envptr[n] = envdata;
4546         envdata += sprintf(envdata, "%s=%s", s1, s2);
4547         envdata++;
4548         n++;
4549     }
4550     envptr[n] = NULL;
4551     *envdata = 0;
4552 
4553     return envptr;
4554 }
4555 
JimFreeEnv(char ** env,char ** original_environ)4556 static void JimFreeEnv(char **env, char **original_environ)
4557 {
4558     if (env != original_environ) {
4559         Jim_Free(env);
4560     }
4561 }
4562 
JimMakeErrorCode(Jim_Interp * interp,pidtype pid,int waitStatus,Jim_Obj * errStrObj)4563 static Jim_Obj *JimMakeErrorCode(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
4564 {
4565     Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
4566 
4567     if (pid == JIM_BAD_PID || pid == JIM_NO_PID) {
4568         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
4569         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4570         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, -1));
4571     }
4572     else if (WIFEXITED(waitStatus)) {
4573         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4574         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4575         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
4576     }
4577     else {
4578         const char *type;
4579         const char *action;
4580         const char *signame;
4581 
4582         if (WIFSIGNALED(waitStatus)) {
4583             type = "CHILDKILLED";
4584             action = "killed";
4585             signame = Jim_SignalId(WTERMSIG(waitStatus));
4586         }
4587         else {
4588             type = "CHILDSUSP";
4589             action = "suspended";
4590             signame = "none";
4591         }
4592 
4593         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
4594 
4595         if (errStrObj) {
4596             Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
4597         }
4598 
4599         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4600         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, signame, -1));
4601     }
4602     return errorCode;
4603 }
4604 
JimCheckWaitStatus(Jim_Interp * interp,pidtype pid,int waitStatus,Jim_Obj * errStrObj)4605 static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
4606 {
4607     if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
4608         return JIM_OK;
4609     }
4610     Jim_SetGlobalVariableStr(interp, "errorCode", JimMakeErrorCode(interp, pid, waitStatus, errStrObj));
4611 
4612     return JIM_ERR;
4613 }
4614 
4615 
4616 struct WaitInfo
4617 {
4618     pidtype pid;
4619     int status;
4620     int flags;
4621 };
4622 
4623 
4624 struct WaitInfoTable {
4625     struct WaitInfo *info;
4626     int size;
4627     int used;
4628     int refcount;
4629 };
4630 
4631 
4632 #define WI_DETACHED 2
4633 
4634 #define WAIT_TABLE_GROW_BY 4
4635 
JimFreeWaitInfoTable(struct Jim_Interp * interp,void * privData)4636 static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
4637 {
4638     struct WaitInfoTable *table = privData;
4639 
4640     if (--table->refcount == 0) {
4641         Jim_Free(table->info);
4642         Jim_Free(table);
4643     }
4644 }
4645 
JimAllocWaitInfoTable(void)4646 static struct WaitInfoTable *JimAllocWaitInfoTable(void)
4647 {
4648     struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
4649     table->info = NULL;
4650     table->size = table->used = 0;
4651     table->refcount = 1;
4652 
4653     return table;
4654 }
4655 
JimWaitRemove(struct WaitInfoTable * table,pidtype pid)4656 static int JimWaitRemove(struct WaitInfoTable *table, pidtype pid)
4657 {
4658     int i;
4659 
4660 
4661     for (i = 0; i < table->used; i++) {
4662         if (pid == table->info[i].pid) {
4663             if (i != table->used - 1) {
4664                 table->info[i] = table->info[table->used - 1];
4665             }
4666             table->used--;
4667             return 0;
4668         }
4669     }
4670     return -1;
4671 }
4672 
Jim_ExecCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4673 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4674 {
4675     int outputId;
4676     int errorId;
4677     pidtype *pidPtr;
4678     int numPids, result;
4679     int child_siginfo = 1;
4680     Jim_Obj *childErrObj;
4681     Jim_Obj *errStrObj;
4682     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4683 
4684     if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
4685         Jim_Obj *listObj;
4686         int i;
4687 
4688         argc--;
4689         numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
4690         if (numPids < 0) {
4691             return JIM_ERR;
4692         }
4693 
4694         listObj = Jim_NewListObj(interp, NULL, 0);
4695         for (i = 0; i < numPids; i++) {
4696             Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i]));
4697         }
4698         Jim_SetResult(interp, listObj);
4699         JimDetachPids(table, numPids, pidPtr);
4700         Jim_Free(pidPtr);
4701         return JIM_OK;
4702     }
4703 
4704     numPids =
4705         JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
4706 
4707     if (numPids < 0) {
4708         return JIM_ERR;
4709     }
4710 
4711     result = JIM_OK;
4712 
4713     errStrObj = Jim_NewStringObj(interp, "", 0);
4714 
4715 
4716     if (outputId != -1) {
4717         if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) {
4718             result = JIM_ERR;
4719             Jim_SetResultErrno(interp, "error reading from output pipe");
4720         }
4721     }
4722 
4723 
4724     childErrObj = Jim_NewStringObj(interp, "", 0);
4725     Jim_IncrRefCount(childErrObj);
4726 
4727     if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
4728         result = JIM_ERR;
4729     }
4730 
4731     if (errorId != -1) {
4732         int ret;
4733         lseek(errorId, 0, SEEK_SET);
4734         ret = JimAppendStreamToString(interp, errorId, errStrObj);
4735         if (ret < 0) {
4736             Jim_SetResultErrno(interp, "error reading from error pipe");
4737             result = JIM_ERR;
4738         }
4739         else if (ret > 0) {
4740 
4741             child_siginfo = 0;
4742         }
4743     }
4744 
4745     if (child_siginfo) {
4746 
4747         Jim_AppendObj(interp, errStrObj, childErrObj);
4748     }
4749     Jim_DecrRefCount(interp, childErrObj);
4750 
4751 
4752     Jim_RemoveTrailingNewline(errStrObj);
4753 
4754 
4755     Jim_SetResult(interp, errStrObj);
4756 
4757     return result;
4758 }
4759 
JimWaitForProcess(struct WaitInfoTable * table,pidtype pid,int * statusPtr)4760 static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
4761 {
4762     if (JimWaitRemove(table, pid) == 0) {
4763 
4764          waitpid(pid, statusPtr, 0);
4765          return pid;
4766     }
4767 
4768 
4769     return JIM_BAD_PID;
4770 }
4771 
JimDetachPids(struct WaitInfoTable * table,int numPids,const pidtype * pidPtr)4772 static void JimDetachPids(struct WaitInfoTable *table, int numPids, const pidtype *pidPtr)
4773 {
4774     int j;
4775 
4776     for (j = 0; j < numPids; j++) {
4777 
4778         int i;
4779         for (i = 0; i < table->used; i++) {
4780             if (pidPtr[j] == table->info[i].pid) {
4781                 table->info[i].flags |= WI_DETACHED;
4782                 break;
4783             }
4784         }
4785     }
4786 }
4787 
JimGetChannelFd(Jim_Interp * interp,const char * name)4788 static int JimGetChannelFd(Jim_Interp *interp, const char *name)
4789 {
4790     Jim_Obj *objv[2];
4791 
4792     objv[0] = Jim_NewStringObj(interp, name, -1);
4793     objv[1] = Jim_NewStringObj(interp, "getfd", -1);
4794 
4795     if (Jim_EvalObjVector(interp, 2, objv) == JIM_OK) {
4796         jim_wide fd;
4797         if (Jim_GetWide(interp, Jim_GetResult(interp), &fd) == JIM_OK) {
4798             return fd;
4799         }
4800     }
4801     return -1;
4802 }
4803 
JimReapDetachedPids(struct WaitInfoTable * table)4804 static void JimReapDetachedPids(struct WaitInfoTable *table)
4805 {
4806     struct WaitInfo *waitPtr;
4807     int count;
4808     int dest;
4809 
4810     if (!table) {
4811         return;
4812     }
4813 
4814     waitPtr = table->info;
4815     dest = 0;
4816     for (count = table->used; count > 0; waitPtr++, count--) {
4817         if (waitPtr->flags & WI_DETACHED) {
4818             int status;
4819             pidtype pid = waitpid(waitPtr->pid, &status, WNOHANG);
4820             if (pid == waitPtr->pid) {
4821 
4822                 table->used--;
4823                 continue;
4824             }
4825         }
4826         if (waitPtr != &table->info[dest]) {
4827             table->info[dest] = *waitPtr;
4828         }
4829         dest++;
4830     }
4831 }
4832 
Jim_WaitCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4833 static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4834 {
4835     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4836     int nohang = 0;
4837     pidtype pid;
4838     long pidarg;
4839     int status;
4840     Jim_Obj *errCodeObj;
4841 
4842 
4843     if (argc == 1) {
4844         JimReapDetachedPids(table);
4845         return JIM_OK;
4846     }
4847 
4848     if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) {
4849         nohang = 1;
4850     }
4851     if (argc != nohang + 2) {
4852         Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?");
4853         return JIM_ERR;
4854     }
4855     if (Jim_GetLong(interp, argv[nohang + 1], &pidarg) != JIM_OK) {
4856         return JIM_ERR;
4857     }
4858 
4859     pid = waitpid((pidtype)pidarg, &status, nohang ? WNOHANG : 0);
4860 
4861     errCodeObj = JimMakeErrorCode(interp, pid, status, NULL);
4862 
4863     if (pid != JIM_BAD_PID && (WIFEXITED(status) || WIFSIGNALED(status))) {
4864 
4865         JimWaitRemove(table, pid);
4866     }
4867     Jim_SetResult(interp, errCodeObj);
4868     return JIM_OK;
4869 }
4870 
Jim_PidCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4871 static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4872 {
4873     if (argc != 1) {
4874         Jim_WrongNumArgs(interp, 1, argv, "");
4875         return JIM_ERR;
4876     }
4877 
4878     Jim_SetResultInt(interp, (jim_wide)getpid());
4879     return JIM_OK;
4880 }
4881 
4882 static int
JimCreatePipeline(Jim_Interp * interp,int argc,Jim_Obj * const * argv,pidtype ** pidArrayPtr,int * inPipePtr,int * outPipePtr,int * errFilePtr)4883 JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr,
4884     int *inPipePtr, int *outPipePtr, int *errFilePtr)
4885 {
4886     pidtype *pidPtr = NULL;         /* Points to malloc-ed array holding all
4887                                  * the pids of child processes. */
4888     int numPids = 0;            /* Actual number of processes that exist
4889                                  * at *pidPtr right now. */
4890     int cmdCount;               /* Count of number of distinct commands
4891                                  * found in argc/argv. */
4892     const char *input = NULL;   /* Describes input for pipeline, depending
4893                                  * on "inputFile".  NULL means take input
4894                                  * from stdin/pipe. */
4895     int input_len = 0;
4896 
4897 #define FILE_NAME   0
4898 #define FILE_APPEND 1
4899 #define FILE_HANDLE 2
4900 #define FILE_TEXT   3
4901 
4902     int inputFile = FILE_NAME;  /* 1 means input is name of input file.
4903                                  * 2 means input is filehandle name.
4904                                  * 0 means input holds actual
4905                                  * text to be input to command. */
4906 
4907     int outputFile = FILE_NAME; /* 0 means output is the name of output file.
4908                                  * 1 means output is the name of output file, and append.
4909                                  * 2 means output is filehandle name.
4910                                  * All this is ignored if output is NULL
4911                                  */
4912     int errorFile = FILE_NAME;  /* 0 means error is the name of error file.
4913                                  * 1 means error is the name of error file, and append.
4914                                  * 2 means error is filehandle name.
4915                                  * All this is ignored if error is NULL
4916                                  */
4917     const char *output = NULL;  /* Holds name of output file to pipe to,
4918                                  * or NULL if output goes to stdout/pipe. */
4919     const char *error = NULL;   /* Holds name of stderr file to pipe to,
4920                                  * or NULL if stderr goes to stderr/pipe. */
4921     int inputId = -1;
4922     int outputId = -1;
4923     int errorId = -1;
4924     int lastOutputId = -1;
4925     int pipeIds[2];
4926     int firstArg, lastArg;      /* Indexes of first and last arguments in
4927                                  * current command. */
4928     int lastBar;
4929     int i;
4930     pidtype pid;
4931     char **save_environ;
4932 #ifndef __MINGW32__
4933     char **child_environ;
4934 #endif
4935     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4936 
4937 
4938     char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
4939     int arg_count = 0;
4940 
4941     if (inPipePtr != NULL) {
4942         *inPipePtr = -1;
4943     }
4944     if (outPipePtr != NULL) {
4945         *outPipePtr = -1;
4946     }
4947     if (errFilePtr != NULL) {
4948         *errFilePtr = -1;
4949     }
4950     pipeIds[0] = pipeIds[1] = -1;
4951 
4952     cmdCount = 1;
4953     lastBar = -1;
4954     for (i = 0; i < argc; i++) {
4955         const char *arg = Jim_String(argv[i]);
4956 
4957         if (arg[0] == '<') {
4958             inputFile = FILE_NAME;
4959             input = arg + 1;
4960             if (*input == '<') {
4961                 inputFile = FILE_TEXT;
4962                 input_len = Jim_Length(argv[i]) - 2;
4963                 input++;
4964             }
4965             else if (*input == '@') {
4966                 inputFile = FILE_HANDLE;
4967                 input++;
4968             }
4969 
4970             if (!*input && ++i < argc) {
4971                 input = Jim_GetString(argv[i], &input_len);
4972             }
4973         }
4974         else if (arg[0] == '>') {
4975             int dup_error = 0;
4976 
4977             outputFile = FILE_NAME;
4978 
4979             output = arg + 1;
4980             if (*output == '>') {
4981                 outputFile = FILE_APPEND;
4982                 output++;
4983             }
4984             if (*output == '&') {
4985 
4986                 output++;
4987                 dup_error = 1;
4988             }
4989             if (*output == '@') {
4990                 outputFile = FILE_HANDLE;
4991                 output++;
4992             }
4993             if (!*output && ++i < argc) {
4994                 output = Jim_String(argv[i]);
4995             }
4996             if (dup_error) {
4997                 errorFile = outputFile;
4998                 error = output;
4999             }
5000         }
5001         else if (arg[0] == '2' && arg[1] == '>') {
5002             error = arg + 2;
5003             errorFile = FILE_NAME;
5004 
5005             if (*error == '@') {
5006                 errorFile = FILE_HANDLE;
5007                 error++;
5008             }
5009             else if (*error == '>') {
5010                 errorFile = FILE_APPEND;
5011                 error++;
5012             }
5013             if (!*error && ++i < argc) {
5014                 error = Jim_String(argv[i]);
5015             }
5016         }
5017         else {
5018             if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
5019                 if (i == lastBar + 1 || i == argc - 1) {
5020                     Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
5021                     goto badargs;
5022                 }
5023                 lastBar = i;
5024                 cmdCount++;
5025             }
5026 
5027             arg_array[arg_count++] = (char *)arg;
5028             continue;
5029         }
5030 
5031         if (i >= argc) {
5032             Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
5033             goto badargs;
5034         }
5035     }
5036 
5037     if (arg_count == 0) {
5038         Jim_SetResultString(interp, "didn't specify command to execute", -1);
5039 badargs:
5040         Jim_Free(arg_array);
5041         return -1;
5042     }
5043 
5044 
5045     save_environ = JimSaveEnv(JimBuildEnv(interp));
5046 
5047     if (input != NULL) {
5048         if (inputFile == FILE_TEXT) {
5049             inputId = Jim_MakeTempFile(interp, NULL, 1);
5050             if (inputId == -1) {
5051                 goto error;
5052             }
5053             if (write(inputId, input, input_len) != input_len) {
5054                 Jim_SetResultErrno(interp, "couldn't write temp file");
5055                 close(inputId);
5056                 goto error;
5057             }
5058             lseek(inputId, 0L, SEEK_SET);
5059         }
5060         else if (inputFile == FILE_HANDLE) {
5061             int fd = JimGetChannelFd(interp, input);
5062 
5063             if (fd < 0) {
5064                 goto error;
5065             }
5066             inputId = dup(fd);
5067         }
5068         else {
5069             inputId = Jim_OpenForRead(input);
5070             if (inputId == -1) {
5071                 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, strerror(Jim_Errno()));
5072                 goto error;
5073             }
5074         }
5075     }
5076     else if (inPipePtr != NULL) {
5077         if (pipe(pipeIds) != 0) {
5078             Jim_SetResultErrno(interp, "couldn't create input pipe for command");
5079             goto error;
5080         }
5081         inputId = pipeIds[0];
5082         *inPipePtr = pipeIds[1];
5083         pipeIds[0] = pipeIds[1] = -1;
5084     }
5085 
5086     if (output != NULL) {
5087         if (outputFile == FILE_HANDLE) {
5088             int fd = JimGetChannelFd(interp, output);
5089             if (fd < 0) {
5090                 goto error;
5091             }
5092             lastOutputId = dup(fd);
5093         }
5094         else {
5095             lastOutputId = Jim_OpenForWrite(output, outputFile == FILE_APPEND);
5096             if (lastOutputId == -1) {
5097                 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, strerror(Jim_Errno()));
5098                 goto error;
5099             }
5100         }
5101     }
5102     else if (outPipePtr != NULL) {
5103         if (pipe(pipeIds) != 0) {
5104             Jim_SetResultErrno(interp, "couldn't create output pipe");
5105             goto error;
5106         }
5107         lastOutputId = pipeIds[1];
5108         *outPipePtr = pipeIds[0];
5109         pipeIds[0] = pipeIds[1] = -1;
5110     }
5111 
5112     if (error != NULL) {
5113         if (errorFile == FILE_HANDLE) {
5114             if (strcmp(error, "1") == 0) {
5115 
5116                 if (lastOutputId != -1) {
5117                     errorId = dup(lastOutputId);
5118                 }
5119                 else {
5120 
5121                     error = "stdout";
5122                 }
5123             }
5124             if (errorId == -1) {
5125                 int fd = JimGetChannelFd(interp, error);
5126                 if (fd < 0) {
5127                     goto error;
5128                 }
5129                 errorId = dup(fd);
5130             }
5131         }
5132         else {
5133             errorId = Jim_OpenForWrite(error, errorFile == FILE_APPEND);
5134             if (errorId == -1) {
5135                 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, strerror(Jim_Errno()));
5136                 goto error;
5137             }
5138         }
5139     }
5140     else if (errFilePtr != NULL) {
5141         errorId = Jim_MakeTempFile(interp, NULL, 1);
5142         if (errorId == -1) {
5143             goto error;
5144         }
5145         *errFilePtr = dup(errorId);
5146     }
5147 
5148 
5149     pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
5150     for (i = 0; i < numPids; i++) {
5151         pidPtr[i] = JIM_BAD_PID;
5152     }
5153     for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
5154         int pipe_dup_err = 0;
5155         int origErrorId = errorId;
5156 
5157         for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
5158             if (strcmp(arg_array[lastArg], "|") == 0) {
5159                 break;
5160             }
5161             if (strcmp(arg_array[lastArg], "|&") == 0) {
5162                 pipe_dup_err = 1;
5163                 break;
5164             }
5165         }
5166 
5167         if (lastArg == firstArg) {
5168             Jim_SetResultString(interp, "missing command to exec", -1);
5169             goto error;
5170         }
5171 
5172 
5173         arg_array[lastArg] = NULL;
5174         if (lastArg == arg_count) {
5175             outputId = lastOutputId;
5176             lastOutputId = -1;
5177         }
5178         else {
5179             if (pipe(pipeIds) != 0) {
5180                 Jim_SetResultErrno(interp, "couldn't create pipe");
5181                 goto error;
5182             }
5183             outputId = pipeIds[1];
5184         }
5185 
5186 
5187         if (pipe_dup_err) {
5188             errorId = outputId;
5189         }
5190 
5191 
5192 
5193 #ifdef __MINGW32__
5194         pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId);
5195         if (pid == JIM_BAD_PID) {
5196             Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
5197             goto error;
5198         }
5199 #else
5200         i = strlen(arg_array[firstArg]);
5201 
5202         child_environ = Jim_GetEnviron();
5203         pid = vfork();
5204         if (pid < 0) {
5205             Jim_SetResultErrno(interp, "couldn't fork child process");
5206             goto error;
5207         }
5208         if (pid == 0) {
5209 
5210 
5211             if (inputId != -1) {
5212                 dup2(inputId, fileno(stdin));
5213                 close(inputId);
5214             }
5215             if (outputId != -1) {
5216                 dup2(outputId, fileno(stdout));
5217                 if (outputId != errorId) {
5218                     close(outputId);
5219                 }
5220             }
5221             if (errorId != -1) {
5222                 dup2(errorId, fileno(stderr));
5223                 close(errorId);
5224             }
5225 
5226             if (outPipePtr) {
5227                 close(*outPipePtr);
5228             }
5229             if (errFilePtr) {
5230                 close(*errFilePtr);
5231             }
5232             if (pipeIds[0] != -1) {
5233                 close(pipeIds[0]);
5234             }
5235             if (lastOutputId != -1) {
5236                 close(lastOutputId);
5237             }
5238 
5239 
5240             (void)signal(SIGPIPE, SIG_DFL);
5241 
5242             execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ);
5243 
5244             if (write(fileno(stderr), "couldn't exec \"", 15) &&
5245                 write(fileno(stderr), arg_array[firstArg], i) &&
5246                 write(fileno(stderr), "\"\n", 2)) {
5247 
5248             }
5249 #ifdef JIM_MAINTAINER
5250             {
5251 
5252                 static char *const false_argv[2] = {"false", NULL};
5253                 execvp(false_argv[0],false_argv);
5254             }
5255 #endif
5256             _exit(127);
5257         }
5258 #endif
5259 
5260 
5261 
5262         if (table->used == table->size) {
5263             table->size += WAIT_TABLE_GROW_BY;
5264             table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
5265         }
5266 
5267         table->info[table->used].pid = pid;
5268         table->info[table->used].flags = 0;
5269         table->used++;
5270 
5271         pidPtr[numPids] = pid;
5272 
5273 
5274         errorId = origErrorId;
5275 
5276 
5277         if (inputId != -1) {
5278             close(inputId);
5279         }
5280         if (outputId != -1) {
5281             close(outputId);
5282         }
5283         inputId = pipeIds[0];
5284         pipeIds[0] = pipeIds[1] = -1;
5285     }
5286     *pidArrayPtr = pidPtr;
5287 
5288 
5289   cleanup:
5290     if (inputId != -1) {
5291         close(inputId);
5292     }
5293     if (lastOutputId != -1) {
5294         close(lastOutputId);
5295     }
5296     if (errorId != -1) {
5297         close(errorId);
5298     }
5299     Jim_Free(arg_array);
5300 
5301     JimRestoreEnv(save_environ);
5302 
5303     return numPids;
5304 
5305 
5306   error:
5307     if ((inPipePtr != NULL) && (*inPipePtr != -1)) {
5308         close(*inPipePtr);
5309         *inPipePtr = -1;
5310     }
5311     if ((outPipePtr != NULL) && (*outPipePtr != -1)) {
5312         close(*outPipePtr);
5313         *outPipePtr = -1;
5314     }
5315     if ((errFilePtr != NULL) && (*errFilePtr != -1)) {
5316         close(*errFilePtr);
5317         *errFilePtr = -1;
5318     }
5319     if (pipeIds[0] != -1) {
5320         close(pipeIds[0]);
5321     }
5322     if (pipeIds[1] != -1) {
5323         close(pipeIds[1]);
5324     }
5325     if (pidPtr != NULL) {
5326         for (i = 0; i < numPids; i++) {
5327             if (pidPtr[i] != JIM_BAD_PID) {
5328                 JimDetachPids(table, 1, &pidPtr[i]);
5329             }
5330         }
5331         Jim_Free(pidPtr);
5332     }
5333     numPids = -1;
5334     goto cleanup;
5335 }
5336 
5337 
JimCleanupChildren(Jim_Interp * interp,int numPids,pidtype * pidPtr,Jim_Obj * errStrObj)5338 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj)
5339 {
5340     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
5341     int result = JIM_OK;
5342     int i;
5343 
5344 
5345     for (i = 0; i < numPids; i++) {
5346         int waitStatus = 0;
5347         if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) {
5348             if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus, errStrObj) != JIM_OK) {
5349                 result = JIM_ERR;
5350             }
5351         }
5352     }
5353     Jim_Free(pidPtr);
5354 
5355     return result;
5356 }
5357 
Jim_execInit(Jim_Interp * interp)5358 int Jim_execInit(Jim_Interp *interp)
5359 {
5360     struct WaitInfoTable *waitinfo;
5361     if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
5362         return JIM_ERR;
5363 
5364 #ifdef SIGPIPE
5365     (void)signal(SIGPIPE, SIG_IGN);
5366 #endif
5367 
5368     waitinfo = JimAllocWaitInfoTable();
5369     Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable);
5370     waitinfo->refcount++;
5371     Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable);
5372     Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0);
5373 
5374     return JIM_OK;
5375 }
5376 
5377 #if defined(__MINGW32__)
5378 
5379 
5380 static int
JimWinFindExecutable(const char * originalName,char fullPath[MAX_PATH])5381 JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
5382 {
5383     int i;
5384     static char extensions[][5] = {".exe", "", ".bat"};
5385 
5386     for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
5387         snprintf(fullPath, MAX_PATH, "%s%s", originalName, extensions[i]);
5388 
5389         if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
5390             continue;
5391         }
5392         if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
5393             continue;
5394         }
5395         return 0;
5396     }
5397 
5398     return -1;
5399 }
5400 
JimSaveEnv(char ** env)5401 static char **JimSaveEnv(char **env)
5402 {
5403     return env;
5404 }
5405 
JimRestoreEnv(char ** env)5406 static void JimRestoreEnv(char **env)
5407 {
5408     JimFreeEnv(env, Jim_GetEnviron());
5409 }
5410 
JimOriginalEnviron(void)5411 static char **JimOriginalEnviron(void)
5412 {
5413     return NULL;
5414 }
5415 
5416 static Jim_Obj *
JimWinBuildCommandLine(Jim_Interp * interp,char ** argv)5417 JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
5418 {
5419     char *start, *special;
5420     int quote, i;
5421 
5422     Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
5423 
5424     for (i = 0; argv[i]; i++) {
5425         if (i > 0) {
5426             Jim_AppendString(interp, strObj, " ", 1);
5427         }
5428 
5429         if (argv[i][0] == '\0') {
5430             quote = 1;
5431         }
5432         else {
5433             quote = 0;
5434             for (start = argv[i]; *start != '\0'; start++) {
5435                 if (isspace(UCHAR(*start))) {
5436                     quote = 1;
5437                     break;
5438                 }
5439             }
5440         }
5441         if (quote) {
5442             Jim_AppendString(interp, strObj, "\"" , 1);
5443         }
5444 
5445         start = argv[i];
5446         for (special = argv[i]; ; ) {
5447             if ((*special == '\\') && (special[1] == '\\' ||
5448                     special[1] == '"' || (quote && special[1] == '\0'))) {
5449                 Jim_AppendString(interp, strObj, start, special - start);
5450                 start = special;
5451                 while (1) {
5452                     special++;
5453                     if (*special == '"' || (quote && *special == '\0')) {
5454 
5455                         Jim_AppendString(interp, strObj, start, special - start);
5456                         break;
5457                     }
5458                     if (*special != '\\') {
5459                         break;
5460                     }
5461                 }
5462                 Jim_AppendString(interp, strObj, start, special - start);
5463                 start = special;
5464             }
5465             if (*special == '"') {
5466         if (special == start) {
5467             Jim_AppendString(interp, strObj, "\"", 1);
5468         }
5469         else {
5470             Jim_AppendString(interp, strObj, start, special - start);
5471         }
5472                 Jim_AppendString(interp, strObj, "\\\"", 2);
5473                 start = special + 1;
5474             }
5475             if (*special == '\0') {
5476                 break;
5477             }
5478             special++;
5479         }
5480         Jim_AppendString(interp, strObj, start, special - start);
5481         if (quote) {
5482             Jim_AppendString(interp, strObj, "\"", 1);
5483         }
5484     }
5485     return strObj;
5486 }
5487 
5488 static pidtype
JimStartWinProcess(Jim_Interp * interp,char ** argv,char ** env,int inputId,int outputId,int errorId)5489 JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId)
5490 {
5491     STARTUPINFO startInfo;
5492     PROCESS_INFORMATION procInfo;
5493     HANDLE hProcess;
5494     char execPath[MAX_PATH];
5495     pidtype pid = JIM_BAD_PID;
5496     Jim_Obj *cmdLineObj;
5497     char *winenv;
5498 
5499     if (JimWinFindExecutable(argv[0], execPath) < 0) {
5500         return JIM_BAD_PID;
5501     }
5502     argv[0] = execPath;
5503 
5504     hProcess = GetCurrentProcess();
5505     cmdLineObj = JimWinBuildCommandLine(interp, argv);
5506 
5507 
5508     ZeroMemory(&startInfo, sizeof(startInfo));
5509     startInfo.cb = sizeof(startInfo);
5510     startInfo.dwFlags   = STARTF_USESTDHANDLES;
5511     startInfo.hStdInput = INVALID_HANDLE_VALUE;
5512     startInfo.hStdOutput= INVALID_HANDLE_VALUE;
5513     startInfo.hStdError = INVALID_HANDLE_VALUE;
5514 
5515     if (inputId == -1) {
5516         inputId = _fileno(stdin);
5517     }
5518     DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(inputId), hProcess, &startInfo.hStdInput,
5519             0, TRUE, DUPLICATE_SAME_ACCESS);
5520     if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
5521         goto end;
5522     }
5523 
5524     if (outputId == -1) {
5525         outputId = _fileno(stdout);
5526     }
5527     DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(outputId), hProcess, &startInfo.hStdOutput,
5528             0, TRUE, DUPLICATE_SAME_ACCESS);
5529     if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
5530         goto end;
5531     }
5532 
5533 
5534     if (errorId == -1) {
5535         errorId = _fileno(stderr);
5536     }
5537     DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(errorId), hProcess, &startInfo.hStdError,
5538             0, TRUE, DUPLICATE_SAME_ACCESS);
5539     if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
5540         goto end;
5541     }
5542 
5543     if (env == NULL) {
5544 
5545         winenv = NULL;
5546     }
5547     else if (env[0] == NULL) {
5548         winenv = (char *)"\0";
5549     }
5550     else {
5551         winenv = env[0];
5552     }
5553 
5554     if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
5555             0, winenv, NULL, &startInfo, &procInfo)) {
5556         goto end;
5557     }
5558 
5559 
5560     WaitForInputIdle(procInfo.hProcess, 5000);
5561     CloseHandle(procInfo.hThread);
5562 
5563     pid = procInfo.hProcess;
5564 
5565     end:
5566     Jim_FreeNewObj(interp, cmdLineObj);
5567     if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
5568         CloseHandle(startInfo.hStdInput);
5569     }
5570     if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
5571         CloseHandle(startInfo.hStdOutput);
5572     }
5573     if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
5574         CloseHandle(startInfo.hStdError);
5575     }
5576     return pid;
5577 }
5578 
5579 #else
5580 
JimOriginalEnviron(void)5581 static char **JimOriginalEnviron(void)
5582 {
5583     return Jim_GetEnviron();
5584 }
5585 
JimSaveEnv(char ** env)5586 static char **JimSaveEnv(char **env)
5587 {
5588     char **saveenv = Jim_GetEnviron();
5589     Jim_SetEnviron(env);
5590     return saveenv;
5591 }
5592 
JimRestoreEnv(char ** env)5593 static void JimRestoreEnv(char **env)
5594 {
5595     JimFreeEnv(Jim_GetEnviron(), env);
5596     Jim_SetEnviron(env);
5597 }
5598 #endif
5599 #endif
5600 
5601 
5602 
5603 #ifdef STRPTIME_NEEDS_XOPEN_SOURCE
5604 #ifndef _XOPEN_SOURCE
5605 #define _XOPEN_SOURCE 500
5606 #endif
5607 #endif
5608 
5609 
5610 #ifndef _GNU_SOURCE
5611 #define _GNU_SOURCE
5612 #endif
5613 
5614 #include <stdlib.h>
5615 #include <string.h>
5616 #include <stdio.h>
5617 #include <time.h>
5618 
5619 
5620 #ifdef HAVE_SYS_TIME_H
5621 #include <sys/time.h>
5622 #endif
5623 
5624 struct clock_options {
5625     int gmt;
5626     const char *format;
5627 };
5628 
parse_clock_options(Jim_Interp * interp,int argc,Jim_Obj * const * argv,struct clock_options * opts)5629 static int parse_clock_options(Jim_Interp *interp, int argc, Jim_Obj *const *argv, struct clock_options *opts)
5630 {
5631     static const char * const options[] = { "-gmt", "-format", NULL };
5632     enum { OPT_GMT, OPT_FORMAT, };
5633     int i;
5634 
5635     for (i = 0; i < argc; i += 2) {
5636         int option;
5637         if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
5638             return JIM_ERR;
5639         }
5640         switch (option) {
5641             case OPT_GMT:
5642                 if (Jim_GetBoolean(interp, argv[i + 1], &opts->gmt) != JIM_OK) {
5643                     return JIM_ERR;
5644                 }
5645                 break;
5646             case OPT_FORMAT:
5647                 opts->format = Jim_String(argv[i + 1]);
5648                 break;
5649         }
5650     }
5651     return JIM_OK;
5652 }
5653 
clock_cmd_format(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5654 static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5655 {
5656 
5657     char buf[100];
5658     time_t t;
5659     jim_wide seconds;
5660     struct clock_options options = { 0, "%a %b %d %H:%M:%S %Z %Y" };
5661     struct tm *tm;
5662 
5663     if (Jim_GetWide(interp, argv[0], &seconds) != JIM_OK) {
5664         return JIM_ERR;
5665     }
5666     if (argc % 2 == 0) {
5667         return -1;
5668     }
5669     if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
5670         return JIM_ERR;
5671     }
5672 
5673     t = seconds;
5674     tm = options.gmt ? gmtime(&t) : localtime(&t);
5675 
5676     if (tm == NULL || strftime(buf, sizeof(buf), options.format, tm) == 0) {
5677         Jim_SetResultString(interp, "format string too long or invalid time", -1);
5678         return JIM_ERR;
5679     }
5680 
5681     Jim_SetResultString(interp, buf, -1);
5682 
5683     return JIM_OK;
5684 }
5685 
5686 #ifdef HAVE_STRPTIME
jim_timegm(const struct tm * tm)5687 static time_t jim_timegm(const struct tm *tm)
5688 {
5689     int m = tm->tm_mon + 1;
5690     int y = 1900 + tm->tm_year - (m <= 2);
5691     int era = (y >= 0 ? y : y - 399) / 400;
5692     unsigned yoe = (unsigned)(y - era * 400);
5693     unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + tm->tm_mday - 1;
5694     unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
5695     long days = (era * 146097 + (int)doe - 719468);
5696     int secs = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
5697 
5698     return days * 24 * 60 * 60 + secs;
5699 }
5700 
clock_cmd_scan(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5701 static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5702 {
5703     char *pt;
5704     struct tm tm;
5705     time_t now = time(NULL);
5706 
5707     struct clock_options options = { 0, NULL };
5708 
5709     if (argc % 2 == 0) {
5710         return -1;
5711     }
5712 
5713     if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
5714         return JIM_ERR;
5715     }
5716     if (options.format == NULL) {
5717         return -1;
5718     }
5719 
5720     localtime_r(&now, &tm);
5721 
5722     pt = strptime(Jim_String(argv[0]), options.format, &tm);
5723     if (pt == 0 || *pt != 0) {
5724         Jim_SetResultString(interp, "Failed to parse time according to format", -1);
5725         return JIM_ERR;
5726     }
5727 
5728 
5729     Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm));
5730 
5731     return JIM_OK;
5732 }
5733 #endif
5734 
clock_cmd_seconds(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5735 static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5736 {
5737     Jim_SetResultInt(interp, time(NULL));
5738 
5739     return JIM_OK;
5740 }
5741 
clock_cmd_micros(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5742 static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5743 {
5744     struct timeval tv;
5745 
5746     gettimeofday(&tv, NULL);
5747 
5748     Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec);
5749 
5750     return JIM_OK;
5751 }
5752 
clock_cmd_millis(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5753 static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5754 {
5755     struct timeval tv;
5756 
5757     gettimeofday(&tv, NULL);
5758 
5759     Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000 + tv.tv_usec / 1000);
5760 
5761     return JIM_OK;
5762 }
5763 
5764 static const jim_subcmd_type clock_command_table[] = {
5765     {   "clicks",
5766         NULL,
5767         clock_cmd_micros,
5768         0,
5769         0,
5770 
5771     },
5772     {   "format",
5773         "seconds ?-format string? ?-gmt boolean?",
5774         clock_cmd_format,
5775         1,
5776         5,
5777 
5778     },
5779     {   "microseconds",
5780         NULL,
5781         clock_cmd_micros,
5782         0,
5783         0,
5784 
5785     },
5786     {   "milliseconds",
5787         NULL,
5788         clock_cmd_millis,
5789         0,
5790         0,
5791 
5792     },
5793 #ifdef HAVE_STRPTIME
5794     {   "scan",
5795         "str -format format ?-gmt boolean?",
5796         clock_cmd_scan,
5797         3,
5798         5,
5799 
5800     },
5801 #endif
5802     {   "seconds",
5803         NULL,
5804         clock_cmd_seconds,
5805         0,
5806         0,
5807 
5808     },
5809     { NULL }
5810 };
5811 
Jim_clockInit(Jim_Interp * interp)5812 int Jim_clockInit(Jim_Interp *interp)
5813 {
5814     if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG))
5815         return JIM_ERR;
5816 
5817     Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
5818     return JIM_OK;
5819 }
5820 
5821 #include <limits.h>
5822 #include <stdlib.h>
5823 #include <string.h>
5824 #include <stdio.h>
5825 #include <errno.h>
5826 
5827 
array_cmd_exists(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5828 static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5829 {
5830 
5831     Jim_Obj *dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
5832     Jim_SetResultInt(interp, dictObj && Jim_DictSize(interp, dictObj) != -1);
5833     return JIM_OK;
5834 }
5835 
array_cmd_get(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5836 static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5837 {
5838     Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5839     Jim_Obj *patternObj;
5840 
5841     if (!objPtr) {
5842         return JIM_OK;
5843     }
5844 
5845     patternObj = (argc == 1) ? NULL : argv[1];
5846 
5847 
5848     if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) {
5849         if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) {
5850 
5851             Jim_SetResult(interp, objPtr);
5852             return JIM_OK;
5853         }
5854     }
5855 
5856     return Jim_DictMatchTypes(interp, objPtr, patternObj, JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS | JIM_DICTMATCH_VALUES);
5857 }
5858 
array_cmd_names(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5859 static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5860 {
5861     Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5862 
5863     if (!objPtr) {
5864         return JIM_OK;
5865     }
5866 
5867     return Jim_DictMatchTypes(interp, objPtr, argc == 1 ? NULL : argv[1], JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS);
5868 }
5869 
array_cmd_unset(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5870 static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5871 {
5872     int i;
5873     int len;
5874     Jim_Obj *resultObj;
5875     Jim_Obj *objPtr;
5876     Jim_Obj **dictValuesObj;
5877 
5878     if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
5879 
5880         Jim_UnsetVariable(interp, argv[0], JIM_NONE);
5881         return JIM_OK;
5882     }
5883 
5884     objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5885 
5886     if (objPtr == NULL) {
5887 
5888         return JIM_OK;
5889     }
5890 
5891     if (Jim_DictPairs(interp, objPtr, &dictValuesObj, &len) != JIM_OK) {
5892 
5893         Jim_SetResultString(interp, "", -1);
5894         return JIM_OK;
5895     }
5896 
5897 
5898     resultObj = Jim_NewDictObj(interp, NULL, 0);
5899 
5900     for (i = 0; i < len; i += 2) {
5901         if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
5902             Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
5903         }
5904     }
5905     Jim_Free(dictValuesObj);
5906 
5907     Jim_SetVariable(interp, argv[0], resultObj);
5908     return JIM_OK;
5909 }
5910 
array_cmd_size(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5911 static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5912 {
5913     Jim_Obj *objPtr;
5914     int len = 0;
5915 
5916 
5917     objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5918     if (objPtr) {
5919         len = Jim_DictSize(interp, objPtr);
5920         if (len < 0) {
5921 
5922             Jim_SetResultInt(interp, 0);
5923             return JIM_OK;
5924         }
5925     }
5926 
5927     Jim_SetResultInt(interp, len);
5928 
5929     return JIM_OK;
5930 }
5931 
array_cmd_stat(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5932 static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5933 {
5934     Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5935     if (objPtr) {
5936         return Jim_DictInfo(interp, objPtr);
5937     }
5938     Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL);
5939     return JIM_ERR;
5940 }
5941 
array_cmd_set(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5942 static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5943 {
5944     int i;
5945     int len;
5946     Jim_Obj *listObj = argv[1];
5947     Jim_Obj *dictObj;
5948 
5949     len = Jim_ListLength(interp, listObj);
5950     if (len % 2) {
5951         Jim_SetResultString(interp, "list must have an even number of elements", -1);
5952         return JIM_ERR;
5953     }
5954 
5955     dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
5956     if (!dictObj) {
5957 
5958         return Jim_SetVariable(interp, argv[0], listObj);
5959     }
5960     else if (Jim_DictSize(interp, dictObj) < 0) {
5961         return JIM_ERR;
5962     }
5963 
5964     if (Jim_IsShared(dictObj)) {
5965         dictObj = Jim_DuplicateObj(interp, dictObj);
5966     }
5967 
5968     for (i = 0; i < len; i += 2) {
5969         Jim_Obj *nameObj;
5970         Jim_Obj *valueObj;
5971 
5972         Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE);
5973         Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE);
5974 
5975         Jim_DictAddElement(interp, dictObj, nameObj, valueObj);
5976     }
5977     return Jim_SetVariable(interp, argv[0], dictObj);
5978 }
5979 
5980 static const jim_subcmd_type array_command_table[] = {
5981         {       "exists",
5982                 "arrayName",
5983                 array_cmd_exists,
5984                 1,
5985                 1,
5986 
5987         },
5988         {       "get",
5989                 "arrayName ?pattern?",
5990                 array_cmd_get,
5991                 1,
5992                 2,
5993 
5994         },
5995         {       "names",
5996                 "arrayName ?pattern?",
5997                 array_cmd_names,
5998                 1,
5999                 2,
6000 
6001         },
6002         {       "set",
6003                 "arrayName list",
6004                 array_cmd_set,
6005                 2,
6006                 2,
6007 
6008         },
6009         {       "size",
6010                 "arrayName",
6011                 array_cmd_size,
6012                 1,
6013                 1,
6014 
6015         },
6016         {       "stat",
6017                 "arrayName",
6018                 array_cmd_stat,
6019                 1,
6020                 1,
6021 
6022         },
6023         {       "unset",
6024                 "arrayName ?pattern?",
6025                 array_cmd_unset,
6026                 1,
6027                 2,
6028 
6029         },
6030         {       NULL
6031         }
6032 };
6033 
Jim_arrayInit(Jim_Interp * interp)6034 int Jim_arrayInit(Jim_Interp *interp)
6035 {
6036     if (Jim_PackageProvide(interp, "array", "1.0", JIM_ERRMSG))
6037         return JIM_ERR;
6038 
6039     Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
6040     return JIM_OK;
6041 }
Jim_InitStaticExtensions(Jim_Interp * interp)6042 int Jim_InitStaticExtensions(Jim_Interp *interp)
6043 {
6044 extern int Jim_bootstrapInit(Jim_Interp *);
6045 extern int Jim_aioInit(Jim_Interp *);
6046 extern int Jim_readdirInit(Jim_Interp *);
6047 extern int Jim_regexpInit(Jim_Interp *);
6048 extern int Jim_fileInit(Jim_Interp *);
6049 extern int Jim_globInit(Jim_Interp *);
6050 extern int Jim_execInit(Jim_Interp *);
6051 extern int Jim_clockInit(Jim_Interp *);
6052 extern int Jim_arrayInit(Jim_Interp *);
6053 extern int Jim_stdlibInit(Jim_Interp *);
6054 extern int Jim_tclcompatInit(Jim_Interp *);
6055 Jim_bootstrapInit(interp);
6056 Jim_aioInit(interp);
6057 Jim_readdirInit(interp);
6058 Jim_regexpInit(interp);
6059 Jim_fileInit(interp);
6060 Jim_globInit(interp);
6061 Jim_execInit(interp);
6062 Jim_clockInit(interp);
6063 Jim_arrayInit(interp);
6064 Jim_stdlibInit(interp);
6065 Jim_tclcompatInit(interp);
6066 return JIM_OK;
6067 }
6068 #define JIM_OPTIMIZATION
6069 #ifndef _GNU_SOURCE
6070 #define _GNU_SOURCE
6071 #endif
6072 
6073 #include <stdio.h>
6074 #include <stdlib.h>
6075 
6076 #include <string.h>
6077 #include <stdarg.h>
6078 #include <ctype.h>
6079 #include <limits.h>
6080 #include <assert.h>
6081 #include <errno.h>
6082 #include <time.h>
6083 #include <setjmp.h>
6084 
6085 
6086 #ifdef HAVE_SYS_TIME_H
6087 #include <sys/time.h>
6088 #endif
6089 #ifdef HAVE_BACKTRACE
6090 #include <execinfo.h>
6091 #endif
6092 #ifdef HAVE_CRT_EXTERNS_H
6093 #include <crt_externs.h>
6094 #endif
6095 
6096 
6097 #include <math.h>
6098 
6099 
6100 
6101 
6102 
6103 #ifndef TCL_LIBRARY
6104 #define TCL_LIBRARY "."
6105 #endif
6106 #ifndef TCL_PLATFORM_OS
6107 #define TCL_PLATFORM_OS "unknown"
6108 #endif
6109 #ifndef TCL_PLATFORM_PLATFORM
6110 #define TCL_PLATFORM_PLATFORM "unknown"
6111 #endif
6112 #ifndef TCL_PLATFORM_PATH_SEPARATOR
6113 #define TCL_PLATFORM_PATH_SEPARATOR ":"
6114 #endif
6115 
6116 
6117 
6118 
6119 
6120 
6121 
6122 #ifdef JIM_MAINTAINER
6123 #define JIM_DEBUG_COMMAND
6124 #define JIM_DEBUG_PANIC
6125 #endif
6126 
6127 
6128 
6129 #define JIM_INTEGER_SPACE 24
6130 
6131 const char *jim_tt_name(int type);
6132 
6133 #ifdef JIM_DEBUG_PANIC
6134 static void JimPanicDump(int fail_condition, const char *fmt, ...);
6135 #define JimPanic(X) JimPanicDump X
6136 #else
6137 #define JimPanic(X)
6138 #endif
6139 
6140 #ifdef JIM_OPTIMIZATION
6141 #define JIM_IF_OPTIM(X) X
6142 #else
6143 #define JIM_IF_OPTIM(X)
6144 #endif
6145 
6146 
6147 static char JimEmptyStringRep[] = "";
6148 
6149 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
6150 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
6151     int flags);
6152 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
6153 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
6154 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
6155 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len);
6156 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
6157     const char *prefix, const char *const *tablePtr, const char *name);
6158 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
6159 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
6160 static int JimSign(jim_wide w);
6161 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr);
6162 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
6163 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
6164 
6165 
6166 
6167 #define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
6168 
6169 #define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none")
6170 
utf8_tounicode_case(const char * s,int * uc,int upper)6171 static int utf8_tounicode_case(const char *s, int *uc, int upper)
6172 {
6173     int l = utf8_tounicode(s, uc);
6174     if (upper) {
6175         *uc = utf8_upper(*uc);
6176     }
6177     return l;
6178 }
6179 
6180 
6181 #define JIM_CHARSET_SCAN 2
6182 #define JIM_CHARSET_GLOB 0
6183 
JimCharsetMatch(const char * pattern,int c,int flags)6184 static const char *JimCharsetMatch(const char *pattern, int c, int flags)
6185 {
6186     int not = 0;
6187     int pchar;
6188     int match = 0;
6189     int nocase = 0;
6190 
6191     if (flags & JIM_NOCASE) {
6192         nocase++;
6193         c = utf8_upper(c);
6194     }
6195 
6196     if (flags & JIM_CHARSET_SCAN) {
6197         if (*pattern == '^') {
6198             not++;
6199             pattern++;
6200         }
6201 
6202 
6203         if (*pattern == ']') {
6204             goto first;
6205         }
6206     }
6207 
6208     while (*pattern && *pattern != ']') {
6209 
6210         if (pattern[0] == '\\') {
6211 first:
6212             pattern += utf8_tounicode_case(pattern, &pchar, nocase);
6213         }
6214         else {
6215 
6216             int start;
6217             int end;
6218 
6219             pattern += utf8_tounicode_case(pattern, &start, nocase);
6220             if (pattern[0] == '-' && pattern[1]) {
6221 
6222                 pattern++;
6223                 pattern += utf8_tounicode_case(pattern, &end, nocase);
6224 
6225 
6226                 if ((c >= start && c <= end) || (c >= end && c <= start)) {
6227                     match = 1;
6228                 }
6229                 continue;
6230             }
6231             pchar = start;
6232         }
6233 
6234         if (pchar == c) {
6235             match = 1;
6236         }
6237     }
6238     if (not) {
6239         match = !match;
6240     }
6241 
6242     return match ? pattern : NULL;
6243 }
6244 
6245 
6246 
JimGlobMatch(const char * pattern,const char * string,int nocase)6247 static int JimGlobMatch(const char *pattern, const char *string, int nocase)
6248 {
6249     int c;
6250     int pchar;
6251     while (*pattern) {
6252         switch (pattern[0]) {
6253             case '*':
6254                 while (pattern[1] == '*') {
6255                     pattern++;
6256                 }
6257                 pattern++;
6258                 if (!pattern[0]) {
6259                     return 1;
6260                 }
6261                 while (*string) {
6262 
6263                     if (JimGlobMatch(pattern, string, nocase))
6264                         return 1;
6265                     string += utf8_tounicode(string, &c);
6266                 }
6267                 return 0;
6268 
6269             case '?':
6270                 string += utf8_tounicode(string, &c);
6271                 break;
6272 
6273             case '[': {
6274                     string += utf8_tounicode(string, &c);
6275                     pattern = JimCharsetMatch(pattern + 1, c, nocase ? JIM_NOCASE : 0);
6276                     if (!pattern) {
6277                         return 0;
6278                     }
6279                     if (!*pattern) {
6280 
6281                         continue;
6282                     }
6283                     break;
6284                 }
6285             case '\\':
6286                 if (pattern[1]) {
6287                     pattern++;
6288                 }
6289 
6290             default:
6291                 string += utf8_tounicode_case(string, &c, nocase);
6292                 utf8_tounicode_case(pattern, &pchar, nocase);
6293                 if (pchar != c) {
6294                     return 0;
6295                 }
6296                 break;
6297         }
6298         pattern += utf8_tounicode_case(pattern, &pchar, nocase);
6299         if (!*string) {
6300             while (*pattern == '*') {
6301                 pattern++;
6302             }
6303             break;
6304         }
6305     }
6306     if (!*pattern && !*string) {
6307         return 1;
6308     }
6309     return 0;
6310 }
6311 
JimStringCompare(const char * s1,int l1,const char * s2,int l2)6312 static int JimStringCompare(const char *s1, int l1, const char *s2, int l2)
6313 {
6314     if (l1 < l2) {
6315         return memcmp(s1, s2, l1) <= 0 ? -1 : 1;
6316     }
6317     else if (l2 < l1) {
6318         return memcmp(s1, s2, l2) >= 0 ? 1 : -1;
6319     }
6320     else {
6321         return JimSign(memcmp(s1, s2, l1));
6322     }
6323 }
6324 
JimStringCompareLen(const char * s1,const char * s2,int maxchars,int nocase)6325 static int JimStringCompareLen(const char *s1, const char *s2, int maxchars, int nocase)
6326 {
6327     while (*s1 && *s2 && maxchars) {
6328         int c1, c2;
6329         s1 += utf8_tounicode_case(s1, &c1, nocase);
6330         s2 += utf8_tounicode_case(s2, &c2, nocase);
6331         if (c1 != c2) {
6332             return JimSign(c1 - c2);
6333         }
6334         maxchars--;
6335     }
6336     if (!maxchars) {
6337         return 0;
6338     }
6339 
6340     if (*s1) {
6341         return 1;
6342     }
6343     if (*s2) {
6344         return -1;
6345     }
6346     return 0;
6347 }
6348 
JimStringFirst(const char * s1,int l1,const char * s2,int l2,int idx)6349 static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
6350 {
6351     int i;
6352     int l1bytelen;
6353 
6354     if (!l1 || !l2 || l1 > l2) {
6355         return -1;
6356     }
6357     if (idx < 0)
6358         idx = 0;
6359     s2 += utf8_index(s2, idx);
6360 
6361     l1bytelen = utf8_index(s1, l1);
6362 
6363     for (i = idx; i <= l2 - l1; i++) {
6364         int c;
6365         if (memcmp(s2, s1, l1bytelen) == 0) {
6366             return i;
6367         }
6368         s2 += utf8_tounicode(s2, &c);
6369     }
6370     return -1;
6371 }
6372 
JimStringLast(const char * s1,int l1,const char * s2,int l2)6373 static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
6374 {
6375     const char *p;
6376 
6377     if (!l1 || !l2 || l1 > l2)
6378         return -1;
6379 
6380 
6381     for (p = s2 + l2 - 1; p != s2 - 1; p--) {
6382         if (*p == *s1 && memcmp(s1, p, l1) == 0) {
6383             return p - s2;
6384         }
6385     }
6386     return -1;
6387 }
6388 
6389 #ifdef JIM_UTF8
JimStringLastUtf8(const char * s1,int l1,const char * s2,int l2)6390 static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2)
6391 {
6392     int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2));
6393     if (n > 0) {
6394         n = utf8_strlen(s2, n);
6395     }
6396     return n;
6397 }
6398 #endif
6399 
JimCheckConversion(const char * str,const char * endptr)6400 static int JimCheckConversion(const char *str, const char *endptr)
6401 {
6402     if (str[0] == '\0' || str == endptr) {
6403         return JIM_ERR;
6404     }
6405 
6406     if (endptr[0] != '\0') {
6407         while (*endptr) {
6408             if (!isspace(UCHAR(*endptr))) {
6409                 return JIM_ERR;
6410             }
6411             endptr++;
6412         }
6413     }
6414     return JIM_OK;
6415 }
6416 
JimNumberBase(const char * str,int * base,int * sign)6417 static int JimNumberBase(const char *str, int *base, int *sign)
6418 {
6419     int i = 0;
6420 
6421     *base = 10;
6422 
6423     while (isspace(UCHAR(str[i]))) {
6424         i++;
6425     }
6426 
6427     if (str[i] == '-') {
6428         *sign = -1;
6429         i++;
6430     }
6431     else {
6432         if (str[i] == '+') {
6433             i++;
6434         }
6435         *sign = 1;
6436     }
6437 
6438     if (str[i] != '0') {
6439 
6440         return 0;
6441     }
6442 
6443 
6444     switch (str[i + 1]) {
6445         case 'x': case 'X': *base = 16; break;
6446         case 'o': case 'O': *base = 8; break;
6447         case 'b': case 'B': *base = 2; break;
6448         default: return 0;
6449     }
6450     i += 2;
6451 
6452     if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
6453 
6454         return i;
6455     }
6456 
6457     *base = 10;
6458     return 0;
6459 }
6460 
jim_strtol(const char * str,char ** endptr)6461 static long jim_strtol(const char *str, char **endptr)
6462 {
6463     int sign;
6464     int base;
6465     int i = JimNumberBase(str, &base, &sign);
6466 
6467     if (base != 10) {
6468         long value = strtol(str + i, endptr, base);
6469         if (endptr == NULL || *endptr != str + i) {
6470             return value * sign;
6471         }
6472     }
6473 
6474 
6475     return strtol(str, endptr, 10);
6476 }
6477 
6478 
jim_strtoull(const char * str,char ** endptr)6479 static jim_wide jim_strtoull(const char *str, char **endptr)
6480 {
6481 #ifdef HAVE_LONG_LONG
6482     int sign;
6483     int base;
6484     int i = JimNumberBase(str, &base, &sign);
6485 
6486     if (base != 10) {
6487         jim_wide value = strtoull(str + i, endptr, base);
6488         if (endptr == NULL || *endptr != str + i) {
6489             return value * sign;
6490         }
6491     }
6492 
6493 
6494     return strtoull(str, endptr, 10);
6495 #else
6496     return (unsigned long)jim_strtol(str, endptr);
6497 #endif
6498 }
6499 
Jim_StringToWide(const char * str,jim_wide * widePtr,int base)6500 int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
6501 {
6502     char *endptr;
6503 
6504     if (base) {
6505         *widePtr = strtoull(str, &endptr, base);
6506     }
6507     else {
6508         *widePtr = jim_strtoull(str, &endptr);
6509     }
6510 
6511     return JimCheckConversion(str, endptr);
6512 }
6513 
Jim_StringToDouble(const char * str,double * doublePtr)6514 int Jim_StringToDouble(const char *str, double *doublePtr)
6515 {
6516     char *endptr;
6517 
6518 
6519     errno = 0;
6520 
6521     *doublePtr = strtod(str, &endptr);
6522 
6523     return JimCheckConversion(str, endptr);
6524 }
6525 
JimPowWide(jim_wide b,jim_wide e)6526 static jim_wide JimPowWide(jim_wide b, jim_wide e)
6527 {
6528     jim_wide res = 1;
6529 
6530 
6531     if (b == 1) {
6532 
6533         return 1;
6534     }
6535     if (e < 0) {
6536         if (b != -1) {
6537             return 0;
6538         }
6539         e = -e;
6540     }
6541     while (e)
6542     {
6543         if (e & 1) {
6544             res *= b;
6545         }
6546         e >>= 1;
6547         b *= b;
6548     }
6549     return res;
6550 }
6551 
6552 #ifdef JIM_DEBUG_PANIC
JimPanicDump(int condition,const char * fmt,...)6553 static void JimPanicDump(int condition, const char *fmt, ...)
6554 {
6555     va_list ap;
6556 
6557     if (!condition) {
6558         return;
6559     }
6560 
6561     va_start(ap, fmt);
6562 
6563     fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
6564     vfprintf(stderr, fmt, ap);
6565     fprintf(stderr, "\n\n");
6566     va_end(ap);
6567 
6568 #ifdef HAVE_BACKTRACE
6569     {
6570         void *array[40];
6571         int size, i;
6572         char **strings;
6573 
6574         size = backtrace(array, 40);
6575         strings = backtrace_symbols(array, size);
6576         for (i = 0; i < size; i++)
6577             fprintf(stderr, "[backtrace] %s\n", strings[i]);
6578         fprintf(stderr, "[backtrace] Include the above lines and the output\n");
6579         fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n");
6580     }
6581 #endif
6582 
6583     exit(1);
6584 }
6585 #endif
6586 
6587 
Jim_Alloc(int size)6588 void *Jim_Alloc(int size)
6589 {
6590     return size ? malloc(size) : NULL;
6591 }
6592 
Jim_Free(void * ptr)6593 void Jim_Free(void *ptr)
6594 {
6595     free(ptr);
6596 }
6597 
Jim_Realloc(void * ptr,int size)6598 void *Jim_Realloc(void *ptr, int size)
6599 {
6600     return realloc(ptr, size);
6601 }
6602 
Jim_StrDup(const char * s)6603 char *Jim_StrDup(const char *s)
6604 {
6605     return strdup(s);
6606 }
6607 
Jim_StrDupLen(const char * s,int l)6608 char *Jim_StrDupLen(const char *s, int l)
6609 {
6610     char *copy = Jim_Alloc(l + 1);
6611 
6612     memcpy(copy, s, l + 1);
6613     copy[l] = 0;
6614     return copy;
6615 }
6616 
6617 
6618 
JimClock(void)6619 static jim_wide JimClock(void)
6620 {
6621     struct timeval tv;
6622 
6623     gettimeofday(&tv, NULL);
6624     return (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec;
6625 }
6626 
6627 
6628 
6629 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
6630 static unsigned int JimHashTableNextPower(unsigned int size);
6631 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
6632 
6633 
6634 
6635 
Jim_IntHashFunction(unsigned int key)6636 unsigned int Jim_IntHashFunction(unsigned int key)
6637 {
6638     key += ~(key << 15);
6639     key ^= (key >> 10);
6640     key += (key << 3);
6641     key ^= (key >> 6);
6642     key += ~(key << 11);
6643     key ^= (key >> 16);
6644     return key;
6645 }
6646 
Jim_GenHashFunction(const unsigned char * buf,int len)6647 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
6648 {
6649     unsigned int h = 0;
6650 
6651     while (len--)
6652         h += (h << 3) + *buf++;
6653     return h;
6654 }
6655 
6656 
6657 
6658 
JimResetHashTable(Jim_HashTable * ht)6659 static void JimResetHashTable(Jim_HashTable *ht)
6660 {
6661     ht->table = NULL;
6662     ht->size = 0;
6663     ht->sizemask = 0;
6664     ht->used = 0;
6665     ht->collisions = 0;
6666 #ifdef JIM_RANDOMISE_HASH
6667     ht->uniq = (rand() ^ time(NULL) ^ clock());
6668 #else
6669     ht->uniq = 0;
6670 #endif
6671 }
6672 
JimInitHashTableIterator(Jim_HashTable * ht,Jim_HashTableIterator * iter)6673 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
6674 {
6675     iter->ht = ht;
6676     iter->index = -1;
6677     iter->entry = NULL;
6678     iter->nextEntry = NULL;
6679 }
6680 
6681 
Jim_InitHashTable(Jim_HashTable * ht,const Jim_HashTableType * type,void * privDataPtr)6682 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
6683 {
6684     JimResetHashTable(ht);
6685     ht->type = type;
6686     ht->privdata = privDataPtr;
6687     return JIM_OK;
6688 }
6689 
Jim_ResizeHashTable(Jim_HashTable * ht)6690 void Jim_ResizeHashTable(Jim_HashTable *ht)
6691 {
6692     int minimal = ht->used;
6693 
6694     if (minimal < JIM_HT_INITIAL_SIZE)
6695         minimal = JIM_HT_INITIAL_SIZE;
6696     Jim_ExpandHashTable(ht, minimal);
6697 }
6698 
6699 
Jim_ExpandHashTable(Jim_HashTable * ht,unsigned int size)6700 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
6701 {
6702     Jim_HashTable n;
6703     unsigned int realsize = JimHashTableNextPower(size), i;
6704 
6705      if (size <= ht->used)
6706         return;
6707 
6708     Jim_InitHashTable(&n, ht->type, ht->privdata);
6709     n.size = realsize;
6710     n.sizemask = realsize - 1;
6711     n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
6712 
6713     n.uniq = ht->uniq;
6714 
6715 
6716     memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
6717 
6718     n.used = ht->used;
6719     for (i = 0; ht->used > 0; i++) {
6720         Jim_HashEntry *he, *nextHe;
6721 
6722         if (ht->table[i] == NULL)
6723             continue;
6724 
6725 
6726         he = ht->table[i];
6727         while (he) {
6728             unsigned int h;
6729 
6730             nextHe = he->next;
6731 
6732             h = Jim_HashKey(ht, he->key) & n.sizemask;
6733             he->next = n.table[h];
6734             n.table[h] = he;
6735             ht->used--;
6736 
6737             he = nextHe;
6738         }
6739     }
6740     assert(ht->used == 0);
6741     Jim_Free(ht->table);
6742 
6743 
6744     *ht = n;
6745 }
6746 
6747 
Jim_AddHashEntry(Jim_HashTable * ht,const void * key,void * val)6748 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
6749 {
6750     Jim_HashEntry *entry;
6751 
6752     entry = JimInsertHashEntry(ht, key, 0);
6753     if (entry == NULL)
6754         return JIM_ERR;
6755 
6756 
6757     Jim_SetHashKey(ht, entry, key);
6758     Jim_SetHashVal(ht, entry, val);
6759     return JIM_OK;
6760 }
6761 
6762 
Jim_ReplaceHashEntry(Jim_HashTable * ht,const void * key,void * val)6763 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
6764 {
6765     int existed;
6766     Jim_HashEntry *entry;
6767 
6768     entry = JimInsertHashEntry(ht, key, 1);
6769     if (entry->key) {
6770         if (ht->type->valDestructor && ht->type->valDup) {
6771             void *newval = ht->type->valDup(ht->privdata, val);
6772             ht->type->valDestructor(ht->privdata, entry->u.val);
6773             entry->u.val = newval;
6774         }
6775         else {
6776             Jim_FreeEntryVal(ht, entry);
6777             Jim_SetHashVal(ht, entry, val);
6778         }
6779         existed = 1;
6780     }
6781     else {
6782 
6783         Jim_SetHashKey(ht, entry, key);
6784         Jim_SetHashVal(ht, entry, val);
6785         existed = 0;
6786     }
6787 
6788     return existed;
6789 }
6790 
6791 
Jim_DeleteHashEntry(Jim_HashTable * ht,const void * key)6792 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
6793 {
6794     unsigned int h;
6795     Jim_HashEntry *he, *prevHe;
6796 
6797     if (ht->used == 0)
6798         return JIM_ERR;
6799     h = Jim_HashKey(ht, key) & ht->sizemask;
6800     he = ht->table[h];
6801 
6802     prevHe = NULL;
6803     while (he) {
6804         if (Jim_CompareHashKeys(ht, key, he->key)) {
6805 
6806             if (prevHe)
6807                 prevHe->next = he->next;
6808             else
6809                 ht->table[h] = he->next;
6810             Jim_FreeEntryKey(ht, he);
6811             Jim_FreeEntryVal(ht, he);
6812             Jim_Free(he);
6813             ht->used--;
6814             return JIM_OK;
6815         }
6816         prevHe = he;
6817         he = he->next;
6818     }
6819     return JIM_ERR;
6820 }
6821 
6822 
Jim_FreeHashTable(Jim_HashTable * ht)6823 int Jim_FreeHashTable(Jim_HashTable *ht)
6824 {
6825     unsigned int i;
6826 
6827 
6828     for (i = 0; ht->used > 0; i++) {
6829         Jim_HashEntry *he, *nextHe;
6830 
6831         if ((he = ht->table[i]) == NULL)
6832             continue;
6833         while (he) {
6834             nextHe = he->next;
6835             Jim_FreeEntryKey(ht, he);
6836             Jim_FreeEntryVal(ht, he);
6837             Jim_Free(he);
6838             ht->used--;
6839             he = nextHe;
6840         }
6841     }
6842 
6843     Jim_Free(ht->table);
6844 
6845     JimResetHashTable(ht);
6846     return JIM_OK;
6847 }
6848 
Jim_FindHashEntry(Jim_HashTable * ht,const void * key)6849 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
6850 {
6851     Jim_HashEntry *he;
6852     unsigned int h;
6853 
6854     if (ht->used == 0)
6855         return NULL;
6856     h = Jim_HashKey(ht, key) & ht->sizemask;
6857     he = ht->table[h];
6858     while (he) {
6859         if (Jim_CompareHashKeys(ht, key, he->key))
6860             return he;
6861         he = he->next;
6862     }
6863     return NULL;
6864 }
6865 
Jim_GetHashTableIterator(Jim_HashTable * ht)6866 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
6867 {
6868     Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
6869     JimInitHashTableIterator(ht, iter);
6870     return iter;
6871 }
6872 
Jim_NextHashEntry(Jim_HashTableIterator * iter)6873 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
6874 {
6875     while (1) {
6876         if (iter->entry == NULL) {
6877             iter->index++;
6878             if (iter->index >= (signed)iter->ht->size)
6879                 break;
6880             iter->entry = iter->ht->table[iter->index];
6881         }
6882         else {
6883             iter->entry = iter->nextEntry;
6884         }
6885         if (iter->entry) {
6886             iter->nextEntry = iter->entry->next;
6887             return iter->entry;
6888         }
6889     }
6890     return NULL;
6891 }
6892 
6893 
6894 
6895 
JimExpandHashTableIfNeeded(Jim_HashTable * ht)6896 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
6897 {
6898     if (ht->size == 0)
6899         Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
6900     if (ht->size == ht->used)
6901         Jim_ExpandHashTable(ht, ht->size * 2);
6902 }
6903 
6904 
JimHashTableNextPower(unsigned int size)6905 static unsigned int JimHashTableNextPower(unsigned int size)
6906 {
6907     unsigned int i = JIM_HT_INITIAL_SIZE;
6908 
6909     if (size >= 2147483648U)
6910         return 2147483648U;
6911     while (1) {
6912         if (i >= size)
6913             return i;
6914         i *= 2;
6915     }
6916 }
6917 
JimInsertHashEntry(Jim_HashTable * ht,const void * key,int replace)6918 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
6919 {
6920     unsigned int h;
6921     Jim_HashEntry *he;
6922 
6923 
6924     JimExpandHashTableIfNeeded(ht);
6925 
6926 
6927     h = Jim_HashKey(ht, key) & ht->sizemask;
6928 
6929     he = ht->table[h];
6930     while (he) {
6931         if (Jim_CompareHashKeys(ht, key, he->key))
6932             return replace ? he : NULL;
6933         he = he->next;
6934     }
6935 
6936 
6937     he = Jim_Alloc(sizeof(*he));
6938     he->next = ht->table[h];
6939     ht->table[h] = he;
6940     ht->used++;
6941     he->key = NULL;
6942 
6943     return he;
6944 }
6945 
6946 
6947 
JimStringCopyHTHashFunction(const void * key)6948 static unsigned int JimStringCopyHTHashFunction(const void *key)
6949 {
6950     return Jim_GenHashFunction(key, strlen(key));
6951 }
6952 
JimStringCopyHTDup(void * privdata,const void * key)6953 static void *JimStringCopyHTDup(void *privdata, const void *key)
6954 {
6955     return Jim_StrDup(key);
6956 }
6957 
JimStringCopyHTKeyCompare(void * privdata,const void * key1,const void * key2)6958 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
6959 {
6960     return strcmp(key1, key2) == 0;
6961 }
6962 
JimStringCopyHTKeyDestructor(void * privdata,void * key)6963 static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
6964 {
6965     Jim_Free(key);
6966 }
6967 
6968 static const Jim_HashTableType JimPackageHashTableType = {
6969     JimStringCopyHTHashFunction,
6970     JimStringCopyHTDup,
6971     NULL,
6972     JimStringCopyHTKeyCompare,
6973     JimStringCopyHTKeyDestructor,
6974     NULL
6975 };
6976 
6977 typedef struct AssocDataValue
6978 {
6979     Jim_InterpDeleteProc *delProc;
6980     void *data;
6981 } AssocDataValue;
6982 
JimAssocDataHashTableValueDestructor(void * privdata,void * data)6983 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
6984 {
6985     AssocDataValue *assocPtr = (AssocDataValue *) data;
6986 
6987     if (assocPtr->delProc != NULL)
6988         assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
6989     Jim_Free(data);
6990 }
6991 
6992 static const Jim_HashTableType JimAssocDataHashTableType = {
6993     JimStringCopyHTHashFunction,
6994     JimStringCopyHTDup,
6995     NULL,
6996     JimStringCopyHTKeyCompare,
6997     JimStringCopyHTKeyDestructor,
6998     JimAssocDataHashTableValueDestructor
6999 };
7000 
Jim_InitStack(Jim_Stack * stack)7001 void Jim_InitStack(Jim_Stack *stack)
7002 {
7003     stack->len = 0;
7004     stack->maxlen = 0;
7005     stack->vector = NULL;
7006 }
7007 
Jim_FreeStack(Jim_Stack * stack)7008 void Jim_FreeStack(Jim_Stack *stack)
7009 {
7010     Jim_Free(stack->vector);
7011 }
7012 
Jim_StackLen(Jim_Stack * stack)7013 int Jim_StackLen(Jim_Stack *stack)
7014 {
7015     return stack->len;
7016 }
7017 
Jim_StackPush(Jim_Stack * stack,void * element)7018 void Jim_StackPush(Jim_Stack *stack, void *element)
7019 {
7020     int neededLen = stack->len + 1;
7021 
7022     if (neededLen > stack->maxlen) {
7023         stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
7024         stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
7025     }
7026     stack->vector[stack->len] = element;
7027     stack->len++;
7028 }
7029 
Jim_StackPop(Jim_Stack * stack)7030 void *Jim_StackPop(Jim_Stack *stack)
7031 {
7032     if (stack->len == 0)
7033         return NULL;
7034     stack->len--;
7035     return stack->vector[stack->len];
7036 }
7037 
Jim_StackPeek(Jim_Stack * stack)7038 void *Jim_StackPeek(Jim_Stack *stack)
7039 {
7040     if (stack->len == 0)
7041         return NULL;
7042     return stack->vector[stack->len - 1];
7043 }
7044 
Jim_FreeStackElements(Jim_Stack * stack,void (* freeFunc)(void * ptr))7045 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
7046 {
7047     int i;
7048 
7049     for (i = 0; i < stack->len; i++)
7050         freeFunc(stack->vector[i]);
7051 }
7052 
7053 
7054 
7055 #define JIM_TT_NONE    0
7056 #define JIM_TT_STR     1
7057 #define JIM_TT_ESC     2
7058 #define JIM_TT_VAR     3
7059 #define JIM_TT_DICTSUGAR   4
7060 #define JIM_TT_CMD     5
7061 
7062 #define JIM_TT_SEP     6
7063 #define JIM_TT_EOL     7
7064 #define JIM_TT_EOF     8
7065 
7066 #define JIM_TT_LINE    9
7067 #define JIM_TT_WORD   10
7068 
7069 
7070 #define JIM_TT_SUBEXPR_START  11
7071 #define JIM_TT_SUBEXPR_END    12
7072 #define JIM_TT_SUBEXPR_COMMA  13
7073 #define JIM_TT_EXPR_INT       14
7074 #define JIM_TT_EXPR_DOUBLE    15
7075 #define JIM_TT_EXPR_BOOLEAN   16
7076 
7077 #define JIM_TT_EXPRSUGAR      17
7078 
7079 
7080 #define JIM_TT_EXPR_OP        20
7081 
7082 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
7083 
7084 #define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA)
7085 
7086 #define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP)
7087 
7088 struct JimParseMissing {
7089     int ch;
7090     int line;
7091 };
7092 
7093 struct JimParserCtx
7094 {
7095     const char *p;
7096     int len;
7097     int linenr;
7098     const char *tstart;
7099     const char *tend;
7100     int tline;
7101     int tt;
7102     int eof;
7103     int inquote;
7104     int comment;
7105     struct JimParseMissing missing;
7106 };
7107 
7108 static int JimParseScript(struct JimParserCtx *pc);
7109 static int JimParseSep(struct JimParserCtx *pc);
7110 static int JimParseEol(struct JimParserCtx *pc);
7111 static int JimParseCmd(struct JimParserCtx *pc);
7112 static int JimParseQuote(struct JimParserCtx *pc);
7113 static int JimParseVar(struct JimParserCtx *pc);
7114 static int JimParseBrace(struct JimParserCtx *pc);
7115 static int JimParseStr(struct JimParserCtx *pc);
7116 static int JimParseComment(struct JimParserCtx *pc);
7117 static void JimParseSubCmd(struct JimParserCtx *pc);
7118 static int JimParseSubQuote(struct JimParserCtx *pc);
7119 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
7120 
JimParserInit(struct JimParserCtx * pc,const char * prg,int len,int linenr)7121 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
7122 {
7123     pc->p = prg;
7124     pc->len = len;
7125     pc->tstart = NULL;
7126     pc->tend = NULL;
7127     pc->tline = 0;
7128     pc->tt = JIM_TT_NONE;
7129     pc->eof = 0;
7130     pc->inquote = 0;
7131     pc->linenr = linenr;
7132     pc->comment = 1;
7133     pc->missing.ch = ' ';
7134     pc->missing.line = linenr;
7135 }
7136 
JimParseScript(struct JimParserCtx * pc)7137 static int JimParseScript(struct JimParserCtx *pc)
7138 {
7139     while (1) {
7140         if (!pc->len) {
7141             pc->tstart = pc->p;
7142             pc->tend = pc->p - 1;
7143             pc->tline = pc->linenr;
7144             pc->tt = JIM_TT_EOL;
7145             pc->eof = 1;
7146             return JIM_OK;
7147         }
7148         switch (*(pc->p)) {
7149             case '\\':
7150                 if (*(pc->p + 1) == '\n' && !pc->inquote) {
7151                     return JimParseSep(pc);
7152                 }
7153                 pc->comment = 0;
7154                 return JimParseStr(pc);
7155             case ' ':
7156             case '\t':
7157             case '\r':
7158             case '\f':
7159                 if (!pc->inquote)
7160                     return JimParseSep(pc);
7161                 pc->comment = 0;
7162                 return JimParseStr(pc);
7163             case '\n':
7164             case ';':
7165                 pc->comment = 1;
7166                 if (!pc->inquote)
7167                     return JimParseEol(pc);
7168                 return JimParseStr(pc);
7169             case '[':
7170                 pc->comment = 0;
7171                 return JimParseCmd(pc);
7172             case '$':
7173                 pc->comment = 0;
7174                 if (JimParseVar(pc) == JIM_ERR) {
7175 
7176                     pc->tstart = pc->tend = pc->p++;
7177                     pc->len--;
7178                     pc->tt = JIM_TT_ESC;
7179                 }
7180                 return JIM_OK;
7181             case '#':
7182                 if (pc->comment) {
7183                     JimParseComment(pc);
7184                     continue;
7185                 }
7186                 return JimParseStr(pc);
7187             default:
7188                 pc->comment = 0;
7189                 return JimParseStr(pc);
7190         }
7191         return JIM_OK;
7192     }
7193 }
7194 
JimParseSep(struct JimParserCtx * pc)7195 static int JimParseSep(struct JimParserCtx *pc)
7196 {
7197     pc->tstart = pc->p;
7198     pc->tline = pc->linenr;
7199     while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
7200         if (*pc->p == '\n') {
7201             break;
7202         }
7203         if (*pc->p == '\\') {
7204             pc->p++;
7205             pc->len--;
7206             pc->linenr++;
7207         }
7208         pc->p++;
7209         pc->len--;
7210     }
7211     pc->tend = pc->p - 1;
7212     pc->tt = JIM_TT_SEP;
7213     return JIM_OK;
7214 }
7215 
JimParseEol(struct JimParserCtx * pc)7216 static int JimParseEol(struct JimParserCtx *pc)
7217 {
7218     pc->tstart = pc->p;
7219     pc->tline = pc->linenr;
7220     while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
7221         if (*pc->p == '\n')
7222             pc->linenr++;
7223         pc->p++;
7224         pc->len--;
7225     }
7226     pc->tend = pc->p - 1;
7227     pc->tt = JIM_TT_EOL;
7228     return JIM_OK;
7229 }
7230 
7231 
JimParseSubBrace(struct JimParserCtx * pc)7232 static void JimParseSubBrace(struct JimParserCtx *pc)
7233 {
7234     int level = 1;
7235 
7236 
7237     pc->p++;
7238     pc->len--;
7239     while (pc->len) {
7240         switch (*pc->p) {
7241             case '\\':
7242                 if (pc->len > 1) {
7243                     if (*++pc->p == '\n') {
7244                         pc->linenr++;
7245                     }
7246                     pc->len--;
7247                 }
7248                 break;
7249 
7250             case '{':
7251                 level++;
7252                 break;
7253 
7254             case '}':
7255                 if (--level == 0) {
7256                     pc->tend = pc->p - 1;
7257                     pc->p++;
7258                     pc->len--;
7259                     return;
7260                 }
7261                 break;
7262 
7263             case '\n':
7264                 pc->linenr++;
7265                 break;
7266         }
7267         pc->p++;
7268         pc->len--;
7269     }
7270     pc->missing.ch = '{';
7271     pc->missing.line = pc->tline;
7272     pc->tend = pc->p - 1;
7273 }
7274 
JimParseSubQuote(struct JimParserCtx * pc)7275 static int JimParseSubQuote(struct JimParserCtx *pc)
7276 {
7277     int tt = JIM_TT_STR;
7278     int line = pc->tline;
7279 
7280 
7281     pc->p++;
7282     pc->len--;
7283     while (pc->len) {
7284         switch (*pc->p) {
7285             case '\\':
7286                 if (pc->len > 1) {
7287                     if (*++pc->p == '\n') {
7288                         pc->linenr++;
7289                     }
7290                     pc->len--;
7291                     tt = JIM_TT_ESC;
7292                 }
7293                 break;
7294 
7295             case '"':
7296                 pc->tend = pc->p - 1;
7297                 pc->p++;
7298                 pc->len--;
7299                 return tt;
7300 
7301             case '[':
7302                 JimParseSubCmd(pc);
7303                 tt = JIM_TT_ESC;
7304                 continue;
7305 
7306             case '\n':
7307                 pc->linenr++;
7308                 break;
7309 
7310             case '$':
7311                 tt = JIM_TT_ESC;
7312                 break;
7313         }
7314         pc->p++;
7315         pc->len--;
7316     }
7317     pc->missing.ch = '"';
7318     pc->missing.line = line;
7319     pc->tend = pc->p - 1;
7320     return tt;
7321 }
7322 
JimParseSubCmd(struct JimParserCtx * pc)7323 static void JimParseSubCmd(struct JimParserCtx *pc)
7324 {
7325     int level = 1;
7326     int startofword = 1;
7327     int line = pc->tline;
7328 
7329 
7330     pc->p++;
7331     pc->len--;
7332     while (pc->len) {
7333         switch (*pc->p) {
7334             case '\\':
7335                 if (pc->len > 1) {
7336                     if (*++pc->p == '\n') {
7337                         pc->linenr++;
7338                     }
7339                     pc->len--;
7340                 }
7341                 break;
7342 
7343             case '[':
7344                 level++;
7345                 break;
7346 
7347             case ']':
7348                 if (--level == 0) {
7349                     pc->tend = pc->p - 1;
7350                     pc->p++;
7351                     pc->len--;
7352                     return;
7353                 }
7354                 break;
7355 
7356             case '"':
7357                 if (startofword) {
7358                     JimParseSubQuote(pc);
7359                     continue;
7360                 }
7361                 break;
7362 
7363             case '{':
7364                 JimParseSubBrace(pc);
7365                 startofword = 0;
7366                 continue;
7367 
7368             case '\n':
7369                 pc->linenr++;
7370                 break;
7371         }
7372         startofword = isspace(UCHAR(*pc->p));
7373         pc->p++;
7374         pc->len--;
7375     }
7376     pc->missing.ch = '[';
7377     pc->missing.line = line;
7378     pc->tend = pc->p - 1;
7379 }
7380 
JimParseBrace(struct JimParserCtx * pc)7381 static int JimParseBrace(struct JimParserCtx *pc)
7382 {
7383     pc->tstart = pc->p + 1;
7384     pc->tline = pc->linenr;
7385     pc->tt = JIM_TT_STR;
7386     JimParseSubBrace(pc);
7387     return JIM_OK;
7388 }
7389 
JimParseCmd(struct JimParserCtx * pc)7390 static int JimParseCmd(struct JimParserCtx *pc)
7391 {
7392     pc->tstart = pc->p + 1;
7393     pc->tline = pc->linenr;
7394     pc->tt = JIM_TT_CMD;
7395     JimParseSubCmd(pc);
7396     return JIM_OK;
7397 }
7398 
JimParseQuote(struct JimParserCtx * pc)7399 static int JimParseQuote(struct JimParserCtx *pc)
7400 {
7401     pc->tstart = pc->p + 1;
7402     pc->tline = pc->linenr;
7403     pc->tt = JimParseSubQuote(pc);
7404     return JIM_OK;
7405 }
7406 
JimParseVar(struct JimParserCtx * pc)7407 static int JimParseVar(struct JimParserCtx *pc)
7408 {
7409 
7410     pc->p++;
7411     pc->len--;
7412 
7413 #ifdef EXPRSUGAR_BRACKET
7414     if (*pc->p == '[') {
7415 
7416         JimParseCmd(pc);
7417         pc->tt = JIM_TT_EXPRSUGAR;
7418         return JIM_OK;
7419     }
7420 #endif
7421 
7422     pc->tstart = pc->p;
7423     pc->tt = JIM_TT_VAR;
7424     pc->tline = pc->linenr;
7425 
7426     if (*pc->p == '{') {
7427         pc->tstart = ++pc->p;
7428         pc->len--;
7429 
7430         while (pc->len && *pc->p != '}') {
7431             if (*pc->p == '\n') {
7432                 pc->linenr++;
7433             }
7434             pc->p++;
7435             pc->len--;
7436         }
7437         pc->tend = pc->p - 1;
7438         if (pc->len) {
7439             pc->p++;
7440             pc->len--;
7441         }
7442     }
7443     else {
7444         while (1) {
7445 
7446             if (pc->p[0] == ':' && pc->p[1] == ':') {
7447                 while (*pc->p == ':') {
7448                     pc->p++;
7449                     pc->len--;
7450                 }
7451                 continue;
7452             }
7453             if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
7454                 pc->p++;
7455                 pc->len--;
7456                 continue;
7457             }
7458             break;
7459         }
7460 
7461         if (*pc->p == '(') {
7462             int count = 1;
7463             const char *paren = NULL;
7464 
7465             pc->tt = JIM_TT_DICTSUGAR;
7466 
7467             while (count && pc->len) {
7468                 pc->p++;
7469                 pc->len--;
7470                 if (*pc->p == '\\' && pc->len >= 1) {
7471                     pc->p++;
7472                     pc->len--;
7473                 }
7474                 else if (*pc->p == '(') {
7475                     count++;
7476                 }
7477                 else if (*pc->p == ')') {
7478                     paren = pc->p;
7479                     count--;
7480                 }
7481             }
7482             if (count == 0) {
7483                 pc->p++;
7484                 pc->len--;
7485             }
7486             else if (paren) {
7487 
7488                 paren++;
7489                 pc->len += (pc->p - paren);
7490                 pc->p = paren;
7491             }
7492 #ifndef EXPRSUGAR_BRACKET
7493             if (*pc->tstart == '(') {
7494                 pc->tt = JIM_TT_EXPRSUGAR;
7495             }
7496 #endif
7497         }
7498         pc->tend = pc->p - 1;
7499     }
7500     if (pc->tstart == pc->p) {
7501         pc->p--;
7502         pc->len++;
7503         return JIM_ERR;
7504     }
7505     return JIM_OK;
7506 }
7507 
JimParseStr(struct JimParserCtx * pc)7508 static int JimParseStr(struct JimParserCtx *pc)
7509 {
7510     if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
7511         pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
7512 
7513         if (*pc->p == '{') {
7514             return JimParseBrace(pc);
7515         }
7516         if (*pc->p == '"') {
7517             pc->inquote = 1;
7518             pc->p++;
7519             pc->len--;
7520 
7521             pc->missing.line = pc->tline;
7522         }
7523     }
7524     pc->tstart = pc->p;
7525     pc->tline = pc->linenr;
7526     while (1) {
7527         if (pc->len == 0) {
7528             if (pc->inquote) {
7529                 pc->missing.ch = '"';
7530             }
7531             pc->tend = pc->p - 1;
7532             pc->tt = JIM_TT_ESC;
7533             return JIM_OK;
7534         }
7535         switch (*pc->p) {
7536             case '\\':
7537                 if (!pc->inquote && *(pc->p + 1) == '\n') {
7538                     pc->tend = pc->p - 1;
7539                     pc->tt = JIM_TT_ESC;
7540                     return JIM_OK;
7541                 }
7542                 if (pc->len >= 2) {
7543                     if (*(pc->p + 1) == '\n') {
7544                         pc->linenr++;
7545                     }
7546                     pc->p++;
7547                     pc->len--;
7548                 }
7549                 else if (pc->len == 1) {
7550 
7551                     pc->missing.ch = '\\';
7552                 }
7553                 break;
7554             case '(':
7555 
7556                 if (pc->len > 1 && pc->p[1] != '$') {
7557                     break;
7558                 }
7559 
7560             case ')':
7561 
7562                 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
7563                     if (pc->p == pc->tstart) {
7564 
7565                         pc->p++;
7566                         pc->len--;
7567                     }
7568                     pc->tend = pc->p - 1;
7569                     pc->tt = JIM_TT_ESC;
7570                     return JIM_OK;
7571                 }
7572                 break;
7573 
7574             case '$':
7575             case '[':
7576                 pc->tend = pc->p - 1;
7577                 pc->tt = JIM_TT_ESC;
7578                 return JIM_OK;
7579             case ' ':
7580             case '\t':
7581             case '\n':
7582             case '\r':
7583             case '\f':
7584             case ';':
7585                 if (!pc->inquote) {
7586                     pc->tend = pc->p - 1;
7587                     pc->tt = JIM_TT_ESC;
7588                     return JIM_OK;
7589                 }
7590                 else if (*pc->p == '\n') {
7591                     pc->linenr++;
7592                 }
7593                 break;
7594             case '"':
7595                 if (pc->inquote) {
7596                     pc->tend = pc->p - 1;
7597                     pc->tt = JIM_TT_ESC;
7598                     pc->p++;
7599                     pc->len--;
7600                     pc->inquote = 0;
7601                     return JIM_OK;
7602                 }
7603                 break;
7604         }
7605         pc->p++;
7606         pc->len--;
7607     }
7608     return JIM_OK;
7609 }
7610 
JimParseComment(struct JimParserCtx * pc)7611 static int JimParseComment(struct JimParserCtx *pc)
7612 {
7613     while (*pc->p) {
7614         if (*pc->p == '\\') {
7615             pc->p++;
7616             pc->len--;
7617             if (pc->len == 0) {
7618                 pc->missing.ch = '\\';
7619                 return JIM_OK;
7620             }
7621             if (*pc->p == '\n') {
7622                 pc->linenr++;
7623             }
7624         }
7625         else if (*pc->p == '\n') {
7626             pc->p++;
7627             pc->len--;
7628             pc->linenr++;
7629             break;
7630         }
7631         pc->p++;
7632         pc->len--;
7633     }
7634     return JIM_OK;
7635 }
7636 
7637 
xdigitval(int c)7638 static int xdigitval(int c)
7639 {
7640     if (c >= '0' && c <= '9')
7641         return c - '0';
7642     if (c >= 'a' && c <= 'f')
7643         return c - 'a' + 10;
7644     if (c >= 'A' && c <= 'F')
7645         return c - 'A' + 10;
7646     return -1;
7647 }
7648 
odigitval(int c)7649 static int odigitval(int c)
7650 {
7651     if (c >= '0' && c <= '7')
7652         return c - '0';
7653     return -1;
7654 }
7655 
JimEscape(char * dest,const char * s,int slen)7656 static int JimEscape(char *dest, const char *s, int slen)
7657 {
7658     char *p = dest;
7659     int i, len;
7660 
7661     for (i = 0; i < slen; i++) {
7662         switch (s[i]) {
7663             case '\\':
7664                 switch (s[i + 1]) {
7665                     case 'a':
7666                         *p++ = 0x7;
7667                         i++;
7668                         break;
7669                     case 'b':
7670                         *p++ = 0x8;
7671                         i++;
7672                         break;
7673                     case 'f':
7674                         *p++ = 0xc;
7675                         i++;
7676                         break;
7677                     case 'n':
7678                         *p++ = 0xa;
7679                         i++;
7680                         break;
7681                     case 'r':
7682                         *p++ = 0xd;
7683                         i++;
7684                         break;
7685                     case 't':
7686                         *p++ = 0x9;
7687                         i++;
7688                         break;
7689                     case 'u':
7690                     case 'U':
7691                     case 'x':
7692                         {
7693                             unsigned val = 0;
7694                             int k;
7695                             int maxchars = 2;
7696 
7697                             i++;
7698 
7699                             if (s[i] == 'U') {
7700                                 maxchars = 8;
7701                             }
7702                             else if (s[i] == 'u') {
7703                                 if (s[i + 1] == '{') {
7704                                     maxchars = 6;
7705                                     i++;
7706                                 }
7707                                 else {
7708                                     maxchars = 4;
7709                                 }
7710                             }
7711 
7712                             for (k = 0; k < maxchars; k++) {
7713                                 int c = xdigitval(s[i + k + 1]);
7714                                 if (c == -1) {
7715                                     break;
7716                                 }
7717                                 val = (val << 4) | c;
7718                             }
7719 
7720                             if (s[i] == '{') {
7721                                 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
7722 
7723                                     i--;
7724                                     k = 0;
7725                                 }
7726                                 else {
7727 
7728                                     k++;
7729                                 }
7730                             }
7731                             if (k) {
7732 
7733                                 if (s[i] == 'x') {
7734                                     *p++ = val;
7735                                 }
7736                                 else {
7737                                     p += utf8_fromunicode(p, val);
7738                                 }
7739                                 i += k;
7740                                 break;
7741                             }
7742 
7743                             *p++ = s[i];
7744                         }
7745                         break;
7746                     case 'v':
7747                         *p++ = 0xb;
7748                         i++;
7749                         break;
7750                     case '\0':
7751                         *p++ = '\\';
7752                         i++;
7753                         break;
7754                     case '\n':
7755 
7756                         *p++ = ' ';
7757                         do {
7758                             i++;
7759                         } while (s[i + 1] == ' ' || s[i + 1] == '\t');
7760                         break;
7761                     case '0':
7762                     case '1':
7763                     case '2':
7764                     case '3':
7765                     case '4':
7766                     case '5':
7767                     case '6':
7768                     case '7':
7769 
7770                         {
7771                             int val = 0;
7772                             int c = odigitval(s[i + 1]);
7773 
7774                             val = c;
7775                             c = odigitval(s[i + 2]);
7776                             if (c == -1) {
7777                                 *p++ = val;
7778                                 i++;
7779                                 break;
7780                             }
7781                             val = (val * 8) + c;
7782                             c = odigitval(s[i + 3]);
7783                             if (c == -1) {
7784                                 *p++ = val;
7785                                 i += 2;
7786                                 break;
7787                             }
7788                             val = (val * 8) + c;
7789                             *p++ = val;
7790                             i += 3;
7791                         }
7792                         break;
7793                     default:
7794                         *p++ = s[i + 1];
7795                         i++;
7796                         break;
7797                 }
7798                 break;
7799             default:
7800                 *p++ = s[i];
7801                 break;
7802         }
7803     }
7804     len = p - dest;
7805     *p = '\0';
7806     return len;
7807 }
7808 
JimParserGetTokenObj(Jim_Interp * interp,struct JimParserCtx * pc)7809 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
7810 {
7811     const char *start, *end;
7812     char *token;
7813     int len;
7814 
7815     start = pc->tstart;
7816     end = pc->tend;
7817     len = (end - start) + 1;
7818     if (len < 0) {
7819         len = 0;
7820     }
7821     token = Jim_Alloc(len + 1);
7822     if (pc->tt != JIM_TT_ESC) {
7823 
7824         memcpy(token, start, len);
7825         token[len] = '\0';
7826     }
7827     else {
7828 
7829         len = JimEscape(token, start, len);
7830     }
7831 
7832     return Jim_NewStringObjNoAlloc(interp, token, len);
7833 }
7834 
7835 static int JimParseListSep(struct JimParserCtx *pc);
7836 static int JimParseListStr(struct JimParserCtx *pc);
7837 static int JimParseListQuote(struct JimParserCtx *pc);
7838 
JimParseList(struct JimParserCtx * pc)7839 static int JimParseList(struct JimParserCtx *pc)
7840 {
7841     if (isspace(UCHAR(*pc->p))) {
7842         return JimParseListSep(pc);
7843     }
7844     switch (*pc->p) {
7845         case '"':
7846             return JimParseListQuote(pc);
7847 
7848         case '{':
7849             return JimParseBrace(pc);
7850 
7851         default:
7852             if (pc->len) {
7853                 return JimParseListStr(pc);
7854             }
7855             break;
7856     }
7857 
7858     pc->tstart = pc->tend = pc->p;
7859     pc->tline = pc->linenr;
7860     pc->tt = JIM_TT_EOL;
7861     pc->eof = 1;
7862     return JIM_OK;
7863 }
7864 
JimParseListSep(struct JimParserCtx * pc)7865 static int JimParseListSep(struct JimParserCtx *pc)
7866 {
7867     pc->tstart = pc->p;
7868     pc->tline = pc->linenr;
7869     while (isspace(UCHAR(*pc->p))) {
7870         if (*pc->p == '\n') {
7871             pc->linenr++;
7872         }
7873         pc->p++;
7874         pc->len--;
7875     }
7876     pc->tend = pc->p - 1;
7877     pc->tt = JIM_TT_SEP;
7878     return JIM_OK;
7879 }
7880 
JimParseListQuote(struct JimParserCtx * pc)7881 static int JimParseListQuote(struct JimParserCtx *pc)
7882 {
7883     pc->p++;
7884     pc->len--;
7885 
7886     pc->tstart = pc->p;
7887     pc->tline = pc->linenr;
7888     pc->tt = JIM_TT_STR;
7889 
7890     while (pc->len) {
7891         switch (*pc->p) {
7892             case '\\':
7893                 pc->tt = JIM_TT_ESC;
7894                 if (--pc->len == 0) {
7895 
7896                     pc->tend = pc->p;
7897                     return JIM_OK;
7898                 }
7899                 pc->p++;
7900                 break;
7901             case '\n':
7902                 pc->linenr++;
7903                 break;
7904             case '"':
7905                 pc->tend = pc->p - 1;
7906                 pc->p++;
7907                 pc->len--;
7908                 return JIM_OK;
7909         }
7910         pc->p++;
7911         pc->len--;
7912     }
7913 
7914     pc->tend = pc->p - 1;
7915     return JIM_OK;
7916 }
7917 
JimParseListStr(struct JimParserCtx * pc)7918 static int JimParseListStr(struct JimParserCtx *pc)
7919 {
7920     pc->tstart = pc->p;
7921     pc->tline = pc->linenr;
7922     pc->tt = JIM_TT_STR;
7923 
7924     while (pc->len) {
7925         if (isspace(UCHAR(*pc->p))) {
7926             pc->tend = pc->p - 1;
7927             return JIM_OK;
7928         }
7929         if (*pc->p == '\\') {
7930             if (--pc->len == 0) {
7931 
7932                 pc->tend = pc->p;
7933                 return JIM_OK;
7934             }
7935             pc->tt = JIM_TT_ESC;
7936             pc->p++;
7937         }
7938         pc->p++;
7939         pc->len--;
7940     }
7941     pc->tend = pc->p - 1;
7942     return JIM_OK;
7943 }
7944 
7945 
7946 
Jim_NewObj(Jim_Interp * interp)7947 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
7948 {
7949     Jim_Obj *objPtr;
7950 
7951 
7952     if (interp->freeList != NULL) {
7953 
7954         objPtr = interp->freeList;
7955         interp->freeList = objPtr->nextObjPtr;
7956     }
7957     else {
7958 
7959         objPtr = Jim_Alloc(sizeof(*objPtr));
7960     }
7961 
7962     objPtr->refCount = 0;
7963 
7964 
7965     objPtr->prevObjPtr = NULL;
7966     objPtr->nextObjPtr = interp->liveList;
7967     if (interp->liveList)
7968         interp->liveList->prevObjPtr = objPtr;
7969     interp->liveList = objPtr;
7970 
7971     return objPtr;
7972 }
7973 
Jim_FreeObj(Jim_Interp * interp,Jim_Obj * objPtr)7974 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
7975 {
7976 
7977     JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
7978         objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
7979 
7980 
7981     Jim_FreeIntRep(interp, objPtr);
7982 
7983     if (objPtr->bytes != NULL) {
7984         if (objPtr->bytes != JimEmptyStringRep)
7985             Jim_Free(objPtr->bytes);
7986     }
7987 
7988     if (objPtr->prevObjPtr)
7989         objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
7990     if (objPtr->nextObjPtr)
7991         objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
7992     if (interp->liveList == objPtr)
7993         interp->liveList = objPtr->nextObjPtr;
7994 #ifdef JIM_DISABLE_OBJECT_POOL
7995     Jim_Free(objPtr);
7996 #else
7997 
7998     objPtr->prevObjPtr = NULL;
7999     objPtr->nextObjPtr = interp->freeList;
8000     if (interp->freeList)
8001         interp->freeList->prevObjPtr = objPtr;
8002     interp->freeList = objPtr;
8003     objPtr->refCount = -1;
8004 #endif
8005 }
8006 
8007 
Jim_InvalidateStringRep(Jim_Obj * objPtr)8008 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
8009 {
8010     if (objPtr->bytes != NULL) {
8011         if (objPtr->bytes != JimEmptyStringRep)
8012             Jim_Free(objPtr->bytes);
8013     }
8014     objPtr->bytes = NULL;
8015 }
8016 
8017 
Jim_DuplicateObj(Jim_Interp * interp,Jim_Obj * objPtr)8018 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
8019 {
8020     Jim_Obj *dupPtr;
8021 
8022     dupPtr = Jim_NewObj(interp);
8023     if (objPtr->bytes == NULL) {
8024 
8025         dupPtr->bytes = NULL;
8026     }
8027     else if (objPtr->length == 0) {
8028         dupPtr->bytes = JimEmptyStringRep;
8029         dupPtr->length = 0;
8030         dupPtr->typePtr = NULL;
8031         return dupPtr;
8032     }
8033     else {
8034         dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
8035         dupPtr->length = objPtr->length;
8036 
8037         memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
8038     }
8039 
8040 
8041     dupPtr->typePtr = objPtr->typePtr;
8042     if (objPtr->typePtr != NULL) {
8043         if (objPtr->typePtr->dupIntRepProc == NULL) {
8044             dupPtr->internalRep = objPtr->internalRep;
8045         }
8046         else {
8047 
8048             objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
8049         }
8050     }
8051     return dupPtr;
8052 }
8053 
Jim_GetString(Jim_Obj * objPtr,int * lenPtr)8054 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
8055 {
8056     if (objPtr->bytes == NULL) {
8057 
8058         JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8059         objPtr->typePtr->updateStringProc(objPtr);
8060     }
8061     if (lenPtr)
8062         *lenPtr = objPtr->length;
8063     return objPtr->bytes;
8064 }
8065 
8066 
Jim_Length(Jim_Obj * objPtr)8067 int Jim_Length(Jim_Obj *objPtr)
8068 {
8069     if (objPtr->bytes == NULL) {
8070 
8071         Jim_GetString(objPtr, NULL);
8072     }
8073     return objPtr->length;
8074 }
8075 
8076 
Jim_String(Jim_Obj * objPtr)8077 const char *Jim_String(Jim_Obj *objPtr)
8078 {
8079     if (objPtr->bytes == NULL) {
8080 
8081         Jim_GetString(objPtr, NULL);
8082     }
8083     return objPtr->bytes;
8084 }
8085 
JimSetStringBytes(Jim_Obj * objPtr,const char * str)8086 static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
8087 {
8088     objPtr->bytes = Jim_StrDup(str);
8089     objPtr->length = strlen(str);
8090 }
8091 
8092 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8093 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8094 
8095 static const Jim_ObjType dictSubstObjType = {
8096     "dict-substitution",
8097     FreeDictSubstInternalRep,
8098     DupDictSubstInternalRep,
8099     NULL,
8100     JIM_TYPE_NONE,
8101 };
8102 
8103 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8104 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8105 
8106 static const Jim_ObjType interpolatedObjType = {
8107     "interpolated",
8108     FreeInterpolatedInternalRep,
8109     DupInterpolatedInternalRep,
8110     NULL,
8111     JIM_TYPE_NONE,
8112 };
8113 
FreeInterpolatedInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)8114 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8115 {
8116     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
8117 }
8118 
DupInterpolatedInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)8119 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8120 {
8121 
8122     dupPtr->internalRep = srcPtr->internalRep;
8123 
8124     Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
8125 }
8126 
8127 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8128 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8129 
8130 static const Jim_ObjType stringObjType = {
8131     "string",
8132     NULL,
8133     DupStringInternalRep,
8134     NULL,
8135     JIM_TYPE_REFERENCES,
8136 };
8137 
DupStringInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)8138 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8139 {
8140     JIM_NOTUSED(interp);
8141 
8142     dupPtr->internalRep.strValue.maxLength = srcPtr->length;
8143     dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
8144 }
8145 
SetStringFromAny(Jim_Interp * interp,Jim_Obj * objPtr)8146 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
8147 {
8148     if (objPtr->typePtr != &stringObjType) {
8149 
8150         if (objPtr->bytes == NULL) {
8151 
8152             JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8153             objPtr->typePtr->updateStringProc(objPtr);
8154         }
8155 
8156         Jim_FreeIntRep(interp, objPtr);
8157 
8158         objPtr->typePtr = &stringObjType;
8159         objPtr->internalRep.strValue.maxLength = objPtr->length;
8160 
8161         objPtr->internalRep.strValue.charLength = -1;
8162     }
8163     return JIM_OK;
8164 }
8165 
Jim_Utf8Length(Jim_Interp * interp,Jim_Obj * objPtr)8166 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
8167 {
8168 #ifdef JIM_UTF8
8169     SetStringFromAny(interp, objPtr);
8170 
8171     if (objPtr->internalRep.strValue.charLength < 0) {
8172         objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
8173     }
8174     return objPtr->internalRep.strValue.charLength;
8175 #else
8176     return Jim_Length(objPtr);
8177 #endif
8178 }
8179 
8180 
Jim_NewStringObj(Jim_Interp * interp,const char * s,int len)8181 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
8182 {
8183     Jim_Obj *objPtr = Jim_NewObj(interp);
8184 
8185 
8186     if (len == -1)
8187         len = strlen(s);
8188 
8189     if (len == 0) {
8190         objPtr->bytes = JimEmptyStringRep;
8191     }
8192     else {
8193         objPtr->bytes = Jim_StrDupLen(s, len);
8194     }
8195     objPtr->length = len;
8196 
8197 
8198     objPtr->typePtr = NULL;
8199     return objPtr;
8200 }
8201 
8202 
Jim_NewStringObjUtf8(Jim_Interp * interp,const char * s,int charlen)8203 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
8204 {
8205 #ifdef JIM_UTF8
8206 
8207     int bytelen = utf8_index(s, charlen);
8208 
8209     Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
8210 
8211 
8212     objPtr->typePtr = &stringObjType;
8213     objPtr->internalRep.strValue.maxLength = bytelen;
8214     objPtr->internalRep.strValue.charLength = charlen;
8215 
8216     return objPtr;
8217 #else
8218     return Jim_NewStringObj(interp, s, charlen);
8219 #endif
8220 }
8221 
Jim_NewStringObjNoAlloc(Jim_Interp * interp,char * s,int len)8222 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
8223 {
8224     Jim_Obj *objPtr = Jim_NewObj(interp);
8225 
8226     objPtr->bytes = s;
8227     objPtr->length = (len == -1) ? strlen(s) : len;
8228     objPtr->typePtr = NULL;
8229     return objPtr;
8230 }
8231 
StringAppendString(Jim_Obj * objPtr,const char * str,int len)8232 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
8233 {
8234     int needlen;
8235 
8236     if (len == -1)
8237         len = strlen(str);
8238     needlen = objPtr->length + len;
8239     if (objPtr->internalRep.strValue.maxLength < needlen ||
8240         objPtr->internalRep.strValue.maxLength == 0) {
8241         needlen *= 2;
8242 
8243         if (needlen < 7) {
8244             needlen = 7;
8245         }
8246         if (objPtr->bytes == JimEmptyStringRep) {
8247             objPtr->bytes = Jim_Alloc(needlen + 1);
8248         }
8249         else {
8250             objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
8251         }
8252         objPtr->internalRep.strValue.maxLength = needlen;
8253     }
8254     memcpy(objPtr->bytes + objPtr->length, str, len);
8255     objPtr->bytes[objPtr->length + len] = '\0';
8256 
8257     if (objPtr->internalRep.strValue.charLength >= 0) {
8258 
8259         objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
8260     }
8261     objPtr->length += len;
8262 }
8263 
Jim_AppendString(Jim_Interp * interp,Jim_Obj * objPtr,const char * str,int len)8264 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
8265 {
8266     JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
8267     SetStringFromAny(interp, objPtr);
8268     StringAppendString(objPtr, str, len);
8269 }
8270 
Jim_AppendObj(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * appendObjPtr)8271 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
8272 {
8273     int len;
8274     const char *str = Jim_GetString(appendObjPtr, &len);
8275     Jim_AppendString(interp, objPtr, str, len);
8276 }
8277 
Jim_AppendStrings(Jim_Interp * interp,Jim_Obj * objPtr,...)8278 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
8279 {
8280     va_list ap;
8281 
8282     SetStringFromAny(interp, objPtr);
8283     va_start(ap, objPtr);
8284     while (1) {
8285         const char *s = va_arg(ap, const char *);
8286 
8287         if (s == NULL)
8288             break;
8289         Jim_AppendString(interp, objPtr, s, -1);
8290     }
8291     va_end(ap);
8292 }
8293 
Jim_StringEqObj(Jim_Obj * aObjPtr,Jim_Obj * bObjPtr)8294 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
8295 {
8296     if (aObjPtr == bObjPtr) {
8297         return 1;
8298     }
8299     else {
8300         int Alen, Blen;
8301         const char *sA = Jim_GetString(aObjPtr, &Alen);
8302         const char *sB = Jim_GetString(bObjPtr, &Blen);
8303 
8304         return Alen == Blen && memcmp(sA, sB, Alen) == 0;
8305     }
8306 }
8307 
Jim_StringMatchObj(Jim_Interp * interp,Jim_Obj * patternObjPtr,Jim_Obj * objPtr,int nocase)8308 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
8309 {
8310     return JimGlobMatch(Jim_String(patternObjPtr), Jim_String(objPtr), nocase);
8311 }
8312 
Jim_StringCompareObj(Jim_Interp * interp,Jim_Obj * firstObjPtr,Jim_Obj * secondObjPtr,int nocase)8313 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8314 {
8315     int l1, l2;
8316     const char *s1 = Jim_GetString(firstObjPtr, &l1);
8317     const char *s2 = Jim_GetString(secondObjPtr, &l2);
8318 
8319     if (nocase) {
8320 
8321         return JimStringCompareLen(s1, s2, -1, nocase);
8322     }
8323     return JimStringCompare(s1, l1, s2, l2);
8324 }
8325 
Jim_StringCompareLenObj(Jim_Interp * interp,Jim_Obj * firstObjPtr,Jim_Obj * secondObjPtr,int nocase)8326 int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8327 {
8328     const char *s1 = Jim_String(firstObjPtr);
8329     const char *s2 = Jim_String(secondObjPtr);
8330 
8331     return JimStringCompareLen(s1, s2, Jim_Utf8Length(interp, firstObjPtr), nocase);
8332 }
8333 
JimRelToAbsIndex(int len,int idx)8334 static int JimRelToAbsIndex(int len, int idx)
8335 {
8336     if (idx < 0)
8337         return len + idx;
8338     return idx;
8339 }
8340 
JimRelToAbsRange(int len,int * firstPtr,int * lastPtr,int * rangeLenPtr)8341 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
8342 {
8343     int rangeLen;
8344 
8345     if (*firstPtr > *lastPtr) {
8346         rangeLen = 0;
8347     }
8348     else {
8349         rangeLen = *lastPtr - *firstPtr + 1;
8350         if (rangeLen) {
8351             if (*firstPtr < 0) {
8352                 rangeLen += *firstPtr;
8353                 *firstPtr = 0;
8354             }
8355             if (*lastPtr >= len) {
8356                 rangeLen -= (*lastPtr - (len - 1));
8357                 *lastPtr = len - 1;
8358             }
8359         }
8360     }
8361     if (rangeLen < 0)
8362         rangeLen = 0;
8363 
8364     *rangeLenPtr = rangeLen;
8365 }
8366 
JimStringGetRange(Jim_Interp * interp,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr,int len,int * first,int * last,int * range)8367 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
8368     int len, int *first, int *last, int *range)
8369 {
8370     if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
8371         return JIM_ERR;
8372     }
8373     if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
8374         return JIM_ERR;
8375     }
8376     *first = JimRelToAbsIndex(len, *first);
8377     *last = JimRelToAbsIndex(len, *last);
8378     JimRelToAbsRange(len, first, last, range);
8379     return JIM_OK;
8380 }
8381 
Jim_StringByteRangeObj(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr)8382 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
8383     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8384 {
8385     int first, last;
8386     const char *str;
8387     int rangeLen;
8388     int bytelen;
8389 
8390     str = Jim_GetString(strObjPtr, &bytelen);
8391 
8392     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
8393         return NULL;
8394     }
8395 
8396     if (first == 0 && rangeLen == bytelen) {
8397         return strObjPtr;
8398     }
8399     return Jim_NewStringObj(interp, str + first, rangeLen);
8400 }
8401 
Jim_StringRangeObj(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr)8402 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
8403     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8404 {
8405 #ifdef JIM_UTF8
8406     int first, last;
8407     const char *str;
8408     int len, rangeLen;
8409     int bytelen;
8410 
8411     str = Jim_GetString(strObjPtr, &bytelen);
8412     len = Jim_Utf8Length(interp, strObjPtr);
8413 
8414     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8415         return NULL;
8416     }
8417 
8418     if (first == 0 && rangeLen == len) {
8419         return strObjPtr;
8420     }
8421     if (len == bytelen) {
8422 
8423         return Jim_NewStringObj(interp, str + first, rangeLen);
8424     }
8425     return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
8426 #else
8427     return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
8428 #endif
8429 }
8430 
JimStringReplaceObj(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr,Jim_Obj * newStrObj)8431 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
8432     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
8433 {
8434     int first, last;
8435     const char *str;
8436     int len, rangeLen;
8437     Jim_Obj *objPtr;
8438 
8439     len = Jim_Utf8Length(interp, strObjPtr);
8440 
8441     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8442         return NULL;
8443     }
8444 
8445     if (last < first) {
8446         return strObjPtr;
8447     }
8448 
8449     str = Jim_String(strObjPtr);
8450 
8451 
8452     objPtr = Jim_NewStringObjUtf8(interp, str, first);
8453 
8454 
8455     if (newStrObj) {
8456         Jim_AppendObj(interp, objPtr, newStrObj);
8457     }
8458 
8459 
8460     Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
8461 
8462     return objPtr;
8463 }
8464 
JimStrCopyUpperLower(char * dest,const char * str,int uc)8465 static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
8466 {
8467     while (*str) {
8468         int c;
8469         str += utf8_tounicode(str, &c);
8470         dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c));
8471     }
8472     *dest = 0;
8473 }
8474 
JimStringToLower(Jim_Interp * interp,Jim_Obj * strObjPtr)8475 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
8476 {
8477     char *buf;
8478     int len;
8479     const char *str;
8480 
8481     str = Jim_GetString(strObjPtr, &len);
8482 
8483 #ifdef JIM_UTF8
8484     len *= 2;
8485 #endif
8486     buf = Jim_Alloc(len + 1);
8487     JimStrCopyUpperLower(buf, str, 0);
8488     return Jim_NewStringObjNoAlloc(interp, buf, -1);
8489 }
8490 
JimStringToUpper(Jim_Interp * interp,Jim_Obj * strObjPtr)8491 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
8492 {
8493     char *buf;
8494     const char *str;
8495     int len;
8496 
8497     str = Jim_GetString(strObjPtr, &len);
8498 
8499 #ifdef JIM_UTF8
8500     len *= 2;
8501 #endif
8502     buf = Jim_Alloc(len + 1);
8503     JimStrCopyUpperLower(buf, str, 1);
8504     return Jim_NewStringObjNoAlloc(interp, buf, -1);
8505 }
8506 
JimStringToTitle(Jim_Interp * interp,Jim_Obj * strObjPtr)8507 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
8508 {
8509     char *buf, *p;
8510     int len;
8511     int c;
8512     const char *str;
8513 
8514     str = Jim_GetString(strObjPtr, &len);
8515 
8516 #ifdef JIM_UTF8
8517     len *= 2;
8518 #endif
8519     buf = p = Jim_Alloc(len + 1);
8520 
8521     str += utf8_tounicode(str, &c);
8522     p += utf8_getchars(p, utf8_title(c));
8523 
8524     JimStrCopyUpperLower(p, str, 0);
8525 
8526     return Jim_NewStringObjNoAlloc(interp, buf, -1);
8527 }
8528 
utf8_memchr(const char * str,int len,int c)8529 static const char *utf8_memchr(const char *str, int len, int c)
8530 {
8531 #ifdef JIM_UTF8
8532     while (len) {
8533         int sc;
8534         int n = utf8_tounicode(str, &sc);
8535         if (sc == c) {
8536             return str;
8537         }
8538         str += n;
8539         len -= n;
8540     }
8541     return NULL;
8542 #else
8543     return memchr(str, c, len);
8544 #endif
8545 }
8546 
JimFindTrimLeft(const char * str,int len,const char * trimchars,int trimlen)8547 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
8548 {
8549     while (len) {
8550         int c;
8551         int n = utf8_tounicode(str, &c);
8552 
8553         if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8554 
8555             break;
8556         }
8557         str += n;
8558         len -= n;
8559     }
8560     return str;
8561 }
8562 
JimFindTrimRight(const char * str,int len,const char * trimchars,int trimlen)8563 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
8564 {
8565     str += len;
8566 
8567     while (len) {
8568         int c;
8569         int n = utf8_prev_len(str, len);
8570 
8571         len -= n;
8572         str -= n;
8573 
8574         n = utf8_tounicode(str, &c);
8575 
8576         if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8577             return str + n;
8578         }
8579     }
8580 
8581     return NULL;
8582 }
8583 
8584 static const char default_trim_chars[] = " \t\n\r";
8585 
8586 static int default_trim_chars_len = sizeof(default_trim_chars);
8587 
JimStringTrimLeft(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * trimcharsObjPtr)8588 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8589 {
8590     int len;
8591     const char *str = Jim_GetString(strObjPtr, &len);
8592     const char *trimchars = default_trim_chars;
8593     int trimcharslen = default_trim_chars_len;
8594     const char *newstr;
8595 
8596     if (trimcharsObjPtr) {
8597         trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8598     }
8599 
8600     newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
8601     if (newstr == str) {
8602         return strObjPtr;
8603     }
8604 
8605     return Jim_NewStringObj(interp, newstr, len - (newstr - str));
8606 }
8607 
JimStringTrimRight(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * trimcharsObjPtr)8608 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8609 {
8610     int len;
8611     const char *trimchars = default_trim_chars;
8612     int trimcharslen = default_trim_chars_len;
8613     const char *nontrim;
8614 
8615     if (trimcharsObjPtr) {
8616         trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8617     }
8618 
8619     SetStringFromAny(interp, strObjPtr);
8620 
8621     len = Jim_Length(strObjPtr);
8622     nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
8623 
8624     if (nontrim == NULL) {
8625 
8626         return Jim_NewEmptyStringObj(interp);
8627     }
8628     if (nontrim == strObjPtr->bytes + len) {
8629 
8630         return strObjPtr;
8631     }
8632 
8633     if (Jim_IsShared(strObjPtr)) {
8634         strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
8635     }
8636     else {
8637 
8638         strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
8639         strObjPtr->length = (nontrim - strObjPtr->bytes);
8640     }
8641 
8642     return strObjPtr;
8643 }
8644 
JimStringTrim(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * trimcharsObjPtr)8645 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8646 {
8647 
8648     Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
8649 
8650 
8651     strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
8652 
8653 
8654     if (objPtr != strObjPtr && objPtr->refCount == 0) {
8655 
8656         Jim_FreeNewObj(interp, objPtr);
8657     }
8658 
8659     return strObjPtr;
8660 }
8661 
8662 
8663 #ifdef HAVE_ISASCII
8664 #define jim_isascii isascii
8665 #else
jim_isascii(int c)8666 static int jim_isascii(int c)
8667 {
8668     return !(c & ~0x7f);
8669 }
8670 #endif
8671 
JimStringIs(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * strClass,int strict)8672 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
8673 {
8674     static const char * const strclassnames[] = {
8675         "integer", "alpha", "alnum", "ascii", "digit",
8676         "double", "lower", "upper", "space", "xdigit",
8677         "control", "print", "graph", "punct", "boolean",
8678         NULL
8679     };
8680     enum {
8681         STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
8682         STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
8683         STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN,
8684     };
8685     int strclass;
8686     int len;
8687     int i;
8688     const char *str;
8689     int (*isclassfunc)(int c) = NULL;
8690 
8691     if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
8692         return JIM_ERR;
8693     }
8694 
8695     str = Jim_GetString(strObjPtr, &len);
8696     if (len == 0) {
8697         Jim_SetResultBool(interp, !strict);
8698         return JIM_OK;
8699     }
8700 
8701     switch (strclass) {
8702         case STR_IS_INTEGER:
8703             {
8704                 jim_wide w;
8705                 Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
8706                 return JIM_OK;
8707             }
8708 
8709         case STR_IS_DOUBLE:
8710             {
8711                 double d;
8712                 Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
8713                 return JIM_OK;
8714             }
8715 
8716         case STR_IS_BOOLEAN:
8717             {
8718                 int b;
8719                 Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK);
8720                 return JIM_OK;
8721             }
8722 
8723         case STR_IS_ALPHA: isclassfunc = isalpha; break;
8724         case STR_IS_ALNUM: isclassfunc = isalnum; break;
8725         case STR_IS_ASCII: isclassfunc = jim_isascii; break;
8726         case STR_IS_DIGIT: isclassfunc = isdigit; break;
8727         case STR_IS_LOWER: isclassfunc = islower; break;
8728         case STR_IS_UPPER: isclassfunc = isupper; break;
8729         case STR_IS_SPACE: isclassfunc = isspace; break;
8730         case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
8731         case STR_IS_CONTROL: isclassfunc = iscntrl; break;
8732         case STR_IS_PRINT: isclassfunc = isprint; break;
8733         case STR_IS_GRAPH: isclassfunc = isgraph; break;
8734         case STR_IS_PUNCT: isclassfunc = ispunct; break;
8735         default:
8736             return JIM_ERR;
8737     }
8738 
8739     for (i = 0; i < len; i++) {
8740         if (!isclassfunc(UCHAR(str[i]))) {
8741             Jim_SetResultBool(interp, 0);
8742             return JIM_OK;
8743         }
8744     }
8745     Jim_SetResultBool(interp, 1);
8746     return JIM_OK;
8747 }
8748 
8749 
8750 
8751 static const Jim_ObjType comparedStringObjType = {
8752     "compared-string",
8753     NULL,
8754     NULL,
8755     NULL,
8756     JIM_TYPE_REFERENCES,
8757 };
8758 
Jim_CompareStringImmediate(Jim_Interp * interp,Jim_Obj * objPtr,const char * str)8759 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
8760 {
8761     if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
8762         return 1;
8763     }
8764     else {
8765         if (strcmp(str, Jim_String(objPtr)) != 0)
8766             return 0;
8767 
8768         if (objPtr->typePtr != &comparedStringObjType) {
8769             Jim_FreeIntRep(interp, objPtr);
8770             objPtr->typePtr = &comparedStringObjType;
8771         }
8772         objPtr->internalRep.ptr = (char *)str;
8773         return 1;
8774     }
8775 }
8776 
qsortCompareStringPointers(const void * a,const void * b)8777 static int qsortCompareStringPointers(const void *a, const void *b)
8778 {
8779     char *const *sa = (char *const *)a;
8780     char *const *sb = (char *const *)b;
8781 
8782     return strcmp(*sa, *sb);
8783 }
8784 
8785 
8786 
8787 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8788 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8789 
8790 static const Jim_ObjType sourceObjType = {
8791     "source",
8792     FreeSourceInternalRep,
8793     DupSourceInternalRep,
8794     NULL,
8795     JIM_TYPE_REFERENCES,
8796 };
8797 
FreeSourceInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)8798 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8799 {
8800     Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
8801 }
8802 
DupSourceInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)8803 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8804 {
8805     dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
8806     Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
8807 }
8808 
JimSetSourceInfo(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * fileNameObj,int lineNumber)8809 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
8810     Jim_Obj *fileNameObj, int lineNumber)
8811 {
8812     JimPanic((Jim_IsShared(objPtr), "JimSetSourceInfo called with shared object"));
8813     JimPanic((objPtr->typePtr != NULL, "JimSetSourceInfo called with typed object"));
8814     Jim_IncrRefCount(fileNameObj);
8815     objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
8816     objPtr->internalRep.sourceValue.lineNumber = lineNumber;
8817     objPtr->typePtr = &sourceObjType;
8818 }
8819 
8820 static const Jim_ObjType scriptLineObjType = {
8821     "scriptline",
8822     NULL,
8823     NULL,
8824     NULL,
8825     JIM_NONE,
8826 };
8827 
JimNewScriptLineObj(Jim_Interp * interp,int argc,int line)8828 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
8829 {
8830     Jim_Obj *objPtr;
8831 
8832 #ifdef DEBUG_SHOW_SCRIPT
8833     char buf[100];
8834     snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
8835     objPtr = Jim_NewStringObj(interp, buf, -1);
8836 #else
8837     objPtr = Jim_NewEmptyStringObj(interp);
8838 #endif
8839     objPtr->typePtr = &scriptLineObjType;
8840     objPtr->internalRep.scriptLineValue.argc = argc;
8841     objPtr->internalRep.scriptLineValue.line = line;
8842 
8843     return objPtr;
8844 }
8845 
8846 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8847 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8848 
8849 static const Jim_ObjType scriptObjType = {
8850     "script",
8851     FreeScriptInternalRep,
8852     DupScriptInternalRep,
8853     NULL,
8854     JIM_TYPE_REFERENCES,
8855 };
8856 
8857 typedef struct ScriptToken
8858 {
8859     Jim_Obj *objPtr;
8860     int type;
8861 } ScriptToken;
8862 
8863 typedef struct ScriptObj
8864 {
8865     ScriptToken *token;
8866     Jim_Obj *fileNameObj;
8867     int len;
8868     int substFlags;
8869     int inUse;                  /* Used to share a ScriptObj. Currently
8870                                    only used by Jim_EvalObj() as protection against
8871                                    shimmering of the currently evaluated object. */
8872     int firstline;
8873     int linenr;
8874     int missing;
8875 } ScriptObj;
8876 
8877 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8878 static int JimParseCheckMissing(Jim_Interp *interp, int ch);
8879 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
8880 
FreeScriptInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)8881 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8882 {
8883     int i;
8884     struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
8885 
8886     if (--script->inUse != 0)
8887         return;
8888     for (i = 0; i < script->len; i++) {
8889         Jim_DecrRefCount(interp, script->token[i].objPtr);
8890     }
8891     Jim_Free(script->token);
8892     Jim_DecrRefCount(interp, script->fileNameObj);
8893     Jim_Free(script);
8894 }
8895 
DupScriptInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)8896 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8897 {
8898     JIM_NOTUSED(interp);
8899     JIM_NOTUSED(srcPtr);
8900 
8901     dupPtr->typePtr = NULL;
8902 }
8903 
8904 typedef struct
8905 {
8906     const char *token;
8907     int len;
8908     int type;
8909     int line;
8910 } ParseToken;
8911 
8912 typedef struct
8913 {
8914 
8915     ParseToken *list;
8916     int size;
8917     int count;
8918     ParseToken static_list[20];
8919 } ParseTokenList;
8920 
ScriptTokenListInit(ParseTokenList * tokenlist)8921 static void ScriptTokenListInit(ParseTokenList *tokenlist)
8922 {
8923     tokenlist->list = tokenlist->static_list;
8924     tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
8925     tokenlist->count = 0;
8926 }
8927 
ScriptTokenListFree(ParseTokenList * tokenlist)8928 static void ScriptTokenListFree(ParseTokenList *tokenlist)
8929 {
8930     if (tokenlist->list != tokenlist->static_list) {
8931         Jim_Free(tokenlist->list);
8932     }
8933 }
8934 
ScriptAddToken(ParseTokenList * tokenlist,const char * token,int len,int type,int line)8935 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
8936     int line)
8937 {
8938     ParseToken *t;
8939 
8940     if (tokenlist->count == tokenlist->size) {
8941 
8942         tokenlist->size *= 2;
8943         if (tokenlist->list != tokenlist->static_list) {
8944             tokenlist->list =
8945                 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
8946         }
8947         else {
8948 
8949             tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
8950             memcpy(tokenlist->list, tokenlist->static_list,
8951                 tokenlist->count * sizeof(*tokenlist->list));
8952         }
8953     }
8954     t = &tokenlist->list[tokenlist->count++];
8955     t->token = token;
8956     t->len = len;
8957     t->type = type;
8958     t->line = line;
8959 }
8960 
JimCountWordTokens(struct ScriptObj * script,ParseToken * t)8961 static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t)
8962 {
8963     int expand = 1;
8964     int count = 0;
8965 
8966 
8967     if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
8968         if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
8969 
8970             expand = -1;
8971             t++;
8972         }
8973         else {
8974             if (script->missing == ' ') {
8975 
8976                 script->missing = '}';
8977                 script->linenr = t[1].line;
8978             }
8979         }
8980     }
8981 
8982 
8983     while (!TOKEN_IS_SEP(t->type)) {
8984         t++;
8985         count++;
8986     }
8987 
8988     return count * expand;
8989 }
8990 
JimMakeScriptObj(Jim_Interp * interp,const ParseToken * t)8991 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
8992 {
8993     Jim_Obj *objPtr;
8994 
8995     if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
8996 
8997         int len = t->len;
8998         char *str = Jim_Alloc(len + 1);
8999         len = JimEscape(str, t->token, len);
9000         objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
9001     }
9002     else {
9003         objPtr = Jim_NewStringObj(interp, t->token, t->len);
9004     }
9005     return objPtr;
9006 }
9007 
ScriptObjAddTokens(Jim_Interp * interp,struct ScriptObj * script,ParseTokenList * tokenlist)9008 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
9009     ParseTokenList *tokenlist)
9010 {
9011     int i;
9012     struct ScriptToken *token;
9013 
9014     int lineargs = 0;
9015 
9016     ScriptToken *linefirst;
9017     int count;
9018     int linenr;
9019 
9020 #ifdef DEBUG_SHOW_SCRIPT_TOKENS
9021     printf("==== Tokens ====\n");
9022     for (i = 0; i < tokenlist->count; i++) {
9023         printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
9024             tokenlist->list[i].len, tokenlist->list[i].token);
9025     }
9026 #endif
9027 
9028 
9029     count = tokenlist->count;
9030     for (i = 0; i < tokenlist->count; i++) {
9031         if (tokenlist->list[i].type == JIM_TT_EOL) {
9032             count++;
9033         }
9034     }
9035     linenr = script->firstline = tokenlist->list[0].line;
9036 
9037     token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
9038 
9039 
9040     linefirst = token++;
9041 
9042     for (i = 0; i < tokenlist->count; ) {
9043 
9044         int wordtokens;
9045 
9046 
9047         while (tokenlist->list[i].type == JIM_TT_SEP) {
9048             i++;
9049         }
9050 
9051         wordtokens = JimCountWordTokens(script, tokenlist->list + i);
9052 
9053         if (wordtokens == 0) {
9054 
9055             if (lineargs) {
9056                 linefirst->type = JIM_TT_LINE;
9057                 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
9058                 Jim_IncrRefCount(linefirst->objPtr);
9059 
9060 
9061                 lineargs = 0;
9062                 linefirst = token++;
9063             }
9064             i++;
9065             continue;
9066         }
9067         else if (wordtokens != 1) {
9068 
9069             token->type = JIM_TT_WORD;
9070             token->objPtr = Jim_NewIntObj(interp, wordtokens);
9071             Jim_IncrRefCount(token->objPtr);
9072             token++;
9073             if (wordtokens < 0) {
9074 
9075                 i++;
9076                 wordtokens = -wordtokens - 1;
9077                 lineargs--;
9078             }
9079         }
9080 
9081         if (lineargs == 0) {
9082 
9083             linenr = tokenlist->list[i].line;
9084         }
9085         lineargs++;
9086 
9087 
9088         while (wordtokens--) {
9089             const ParseToken *t = &tokenlist->list[i++];
9090 
9091             token->type = t->type;
9092             token->objPtr = JimMakeScriptObj(interp, t);
9093             Jim_IncrRefCount(token->objPtr);
9094 
9095             JimSetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
9096             token++;
9097         }
9098     }
9099 
9100     if (lineargs == 0) {
9101         token--;
9102     }
9103 
9104     script->len = token - script->token;
9105 
9106     JimPanic((script->len >= count, "allocated script array is too short"));
9107 
9108 #ifdef DEBUG_SHOW_SCRIPT
9109     printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj));
9110     for (i = 0; i < script->len; i++) {
9111         const ScriptToken *t = &script->token[i];
9112         printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
9113     }
9114 #endif
9115 
9116 }
9117 
Jim_ScriptIsComplete(Jim_Interp * interp,Jim_Obj * scriptObj,char * stateCharPtr)9118 int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
9119 {
9120     ScriptObj *script = JimGetScript(interp, scriptObj);
9121     if (stateCharPtr) {
9122         *stateCharPtr = script->missing;
9123     }
9124     return script->missing == ' ' || script->missing == '}';
9125 }
9126 
JimParseCheckMissing(Jim_Interp * interp,int ch)9127 static int JimParseCheckMissing(Jim_Interp *interp, int ch)
9128 {
9129     const char *msg;
9130 
9131     switch (ch) {
9132         case '\\':
9133         case ' ':
9134             return JIM_OK;
9135 
9136         case '[':
9137             msg = "unmatched \"[\"";
9138             break;
9139         case '{':
9140             msg = "missing close-brace";
9141             break;
9142         case '}':
9143             msg = "extra characters after close-brace";
9144             break;
9145         case '"':
9146         default:
9147             msg = "missing quote";
9148             break;
9149     }
9150 
9151     Jim_SetResultString(interp, msg, -1);
9152     return JIM_ERR;
9153 }
9154 
SubstObjAddTokens(Jim_Interp * interp,struct ScriptObj * script,ParseTokenList * tokenlist)9155 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
9156     ParseTokenList *tokenlist)
9157 {
9158     int i;
9159     struct ScriptToken *token;
9160 
9161     token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
9162 
9163     for (i = 0; i < tokenlist->count; i++) {
9164         const ParseToken *t = &tokenlist->list[i];
9165 
9166 
9167         token->type = t->type;
9168         token->objPtr = JimMakeScriptObj(interp, t);
9169         Jim_IncrRefCount(token->objPtr);
9170         token++;
9171     }
9172 
9173     script->len = i;
9174 }
9175 
JimSetScriptFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)9176 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9177 {
9178     int scriptTextLen;
9179     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
9180     struct JimParserCtx parser;
9181     struct ScriptObj *script;
9182     ParseTokenList tokenlist;
9183     int line = 1;
9184 
9185 
9186     if (objPtr->typePtr == &sourceObjType) {
9187         line = objPtr->internalRep.sourceValue.lineNumber;
9188     }
9189 
9190 
9191     ScriptTokenListInit(&tokenlist);
9192 
9193     JimParserInit(&parser, scriptText, scriptTextLen, line);
9194     while (!parser.eof) {
9195         JimParseScript(&parser);
9196         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
9197             parser.tline);
9198     }
9199 
9200 
9201     ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
9202 
9203 
9204     script = Jim_Alloc(sizeof(*script));
9205     memset(script, 0, sizeof(*script));
9206     script->inUse = 1;
9207     if (objPtr->typePtr == &sourceObjType) {
9208         script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
9209     }
9210     else {
9211         script->fileNameObj = interp->emptyObj;
9212     }
9213     Jim_IncrRefCount(script->fileNameObj);
9214     script->missing = parser.missing.ch;
9215     script->linenr = parser.missing.line;
9216 
9217     ScriptObjAddTokens(interp, script, &tokenlist);
9218 
9219 
9220     ScriptTokenListFree(&tokenlist);
9221 
9222 
9223     Jim_FreeIntRep(interp, objPtr);
9224     Jim_SetIntRepPtr(objPtr, script);
9225     objPtr->typePtr = &scriptObjType;
9226 }
9227 
9228 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script);
9229 
JimGetScript(Jim_Interp * interp,Jim_Obj * objPtr)9230 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
9231 {
9232     if (objPtr == interp->emptyObj) {
9233 
9234         objPtr = interp->nullScriptObj;
9235     }
9236 
9237     if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
9238         JimSetScriptFromAny(interp, objPtr);
9239     }
9240 
9241     return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
9242 }
9243 
JimScriptValid(Jim_Interp * interp,ScriptObj * script)9244 static int JimScriptValid(Jim_Interp *interp, ScriptObj *script)
9245 {
9246     if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
9247         JimAddErrorToStack(interp, script);
9248         return 0;
9249     }
9250     return 1;
9251 }
9252 
9253 
JimIncrCmdRefCount(Jim_Cmd * cmdPtr)9254 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
9255 {
9256     cmdPtr->inUse++;
9257 }
9258 
JimDecrCmdRefCount(Jim_Interp * interp,Jim_Cmd * cmdPtr)9259 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
9260 {
9261     if (--cmdPtr->inUse == 0) {
9262         if (cmdPtr->isproc) {
9263             Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
9264             Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
9265             Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9266             if (cmdPtr->u.proc.staticVars) {
9267                 Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
9268                 Jim_Free(cmdPtr->u.proc.staticVars);
9269             }
9270         }
9271         else {
9272 
9273             if (cmdPtr->u.native.delProc) {
9274                 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
9275             }
9276         }
9277         if (cmdPtr->prevCmd) {
9278 
9279             JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
9280         }
9281         Jim_Free(cmdPtr);
9282     }
9283 }
9284 
JimVariablesHTValDestructor(void * interp,void * val)9285 static void JimVariablesHTValDestructor(void *interp, void *val)
9286 {
9287     Jim_DecrRefCount(interp, ((Jim_Var *)val)->objPtr);
9288     Jim_Free(val);
9289 }
9290 
9291 static const Jim_HashTableType JimVariablesHashTableType = {
9292     JimStringCopyHTHashFunction,
9293     JimStringCopyHTDup,
9294     NULL,
9295     JimStringCopyHTKeyCompare,
9296     JimStringCopyHTKeyDestructor,
9297     JimVariablesHTValDestructor
9298 };
9299 
JimCommandsHT_ValDestructor(void * interp,void * val)9300 static void JimCommandsHT_ValDestructor(void *interp, void *val)
9301 {
9302     JimDecrCmdRefCount(interp, val);
9303 }
9304 
9305 static const Jim_HashTableType JimCommandsHashTableType = {
9306     JimStringCopyHTHashFunction,
9307     JimStringCopyHTDup,
9308     NULL,
9309     JimStringCopyHTKeyCompare,
9310     JimStringCopyHTKeyDestructor,
9311     JimCommandsHT_ValDestructor
9312 };
9313 
9314 
9315 
9316 #ifdef jim_ext_namespace
JimQualifyNameObj(Jim_Interp * interp,Jim_Obj * nsObj)9317 static Jim_Obj *JimQualifyNameObj(Jim_Interp *interp, Jim_Obj *nsObj)
9318 {
9319     const char *name = Jim_String(nsObj);
9320     if (name[0] == ':' && name[1] == ':') {
9321 
9322         while (*++name == ':') {
9323         }
9324         nsObj = Jim_NewStringObj(interp, name, -1);
9325     }
9326     else if (Jim_Length(interp->framePtr->nsObj)) {
9327 
9328         nsObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9329         Jim_AppendStrings(interp, nsObj, "::", name, NULL);
9330     }
9331     return nsObj;
9332 }
9333 
Jim_MakeGlobalNamespaceName(Jim_Interp * interp,Jim_Obj * nameObjPtr)9334 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9335 {
9336     Jim_Obj *resultObj;
9337 
9338     const char *name = Jim_String(nameObjPtr);
9339     if (name[0] == ':' && name[1] == ':') {
9340         return nameObjPtr;
9341     }
9342     Jim_IncrRefCount(nameObjPtr);
9343     resultObj = Jim_NewStringObj(interp, "::", -1);
9344     Jim_AppendObj(interp, resultObj, nameObjPtr);
9345     Jim_DecrRefCount(interp, nameObjPtr);
9346 
9347     return resultObj;
9348 }
9349 
JimQualifyName(Jim_Interp * interp,const char * name,Jim_Obj ** objPtrPtr)9350 static const char *JimQualifyName(Jim_Interp *interp, const char *name, Jim_Obj **objPtrPtr)
9351 {
9352     Jim_Obj *objPtr = interp->emptyObj;
9353 
9354     if (name[0] == ':' && name[1] == ':') {
9355 
9356         while (*++name == ':') {
9357         }
9358     }
9359     else if (Jim_Length(interp->framePtr->nsObj)) {
9360 
9361         objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9362         Jim_AppendStrings(interp, objPtr, "::", name, NULL);
9363         name = Jim_String(objPtr);
9364     }
9365     Jim_IncrRefCount(objPtr);
9366     *objPtrPtr = objPtr;
9367     return name;
9368 }
9369 
9370     #define JimFreeQualifiedName(INTERP, OBJ) Jim_DecrRefCount((INTERP), (OBJ))
9371 
9372 #else
9373 
9374     #define JimQualifyName(INTERP, NAME, DUMMY) (((NAME)[0] == ':' && (NAME)[1] == ':') ? (NAME) + 2 : (NAME))
9375     #define JimFreeQualifiedName(INTERP, DUMMY) (void)(DUMMY)
9376 
Jim_MakeGlobalNamespaceName(Jim_Interp * interp,Jim_Obj * nameObjPtr)9377 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9378 {
9379     return nameObjPtr;
9380 }
9381 #endif
9382 
JimCreateCommand(Jim_Interp * interp,const char * name,Jim_Cmd * cmd)9383 static int JimCreateCommand(Jim_Interp *interp, const char *name, Jim_Cmd *cmd)
9384 {
9385     Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, name);
9386     if (he) {
9387 
9388         Jim_InterpIncrProcEpoch(interp);
9389     }
9390 
9391     if (he && interp->local) {
9392 
9393         cmd->prevCmd = Jim_GetHashEntryVal(he);
9394         Jim_SetHashVal(&interp->commands, he, cmd);
9395     }
9396     else {
9397         if (he) {
9398 
9399             Jim_DeleteHashEntry(&interp->commands, name);
9400         }
9401 
9402         Jim_AddHashEntry(&interp->commands, name, cmd);
9403     }
9404     return JIM_OK;
9405 }
9406 
9407 
Jim_CreateCommand(Jim_Interp * interp,const char * cmdNameStr,Jim_CmdProc * cmdProc,void * privData,Jim_DelCmdProc * delProc)9408 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
9409     Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
9410 {
9411     Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
9412 
9413 
9414     memset(cmdPtr, 0, sizeof(*cmdPtr));
9415     cmdPtr->inUse = 1;
9416     cmdPtr->u.native.delProc = delProc;
9417     cmdPtr->u.native.cmdProc = cmdProc;
9418     cmdPtr->u.native.privData = privData;
9419 
9420     JimCreateCommand(interp, cmdNameStr, cmdPtr);
9421 
9422     return JIM_OK;
9423 }
9424 
JimCreateProcedureStatics(Jim_Interp * interp,Jim_Cmd * cmdPtr,Jim_Obj * staticsListObjPtr)9425 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
9426 {
9427     int len, i;
9428 
9429     len = Jim_ListLength(interp, staticsListObjPtr);
9430     if (len == 0) {
9431         return JIM_OK;
9432     }
9433 
9434     cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
9435     Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
9436     for (i = 0; i < len; i++) {
9437         Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
9438         Jim_Var *varPtr;
9439         int subLen;
9440 
9441         objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
9442 
9443         subLen = Jim_ListLength(interp, objPtr);
9444         if (subLen == 1 || subLen == 2) {
9445             nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
9446             if (subLen == 1) {
9447                 initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
9448                 if (initObjPtr == NULL) {
9449                     Jim_SetResultFormatted(interp,
9450                         "variable for initialization of static \"%#s\" not found in the local context",
9451                         nameObjPtr);
9452                     return JIM_ERR;
9453                 }
9454             }
9455             else {
9456                 initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
9457             }
9458             if (JimValidName(interp, "static variable", nameObjPtr) != JIM_OK) {
9459                 return JIM_ERR;
9460             }
9461 
9462             varPtr = Jim_Alloc(sizeof(*varPtr));
9463             varPtr->objPtr = initObjPtr;
9464             Jim_IncrRefCount(initObjPtr);
9465             varPtr->linkFramePtr = NULL;
9466             if (Jim_AddHashEntry(cmdPtr->u.proc.staticVars,
9467                 Jim_String(nameObjPtr), varPtr) != JIM_OK) {
9468                 Jim_SetResultFormatted(interp,
9469                     "static variable name \"%#s\" duplicated in statics list", nameObjPtr);
9470                 Jim_DecrRefCount(interp, initObjPtr);
9471                 Jim_Free(varPtr);
9472                 return JIM_ERR;
9473             }
9474         }
9475         else {
9476             Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
9477                 objPtr);
9478             return JIM_ERR;
9479         }
9480     }
9481     return JIM_OK;
9482 }
9483 
JimUpdateProcNamespace(Jim_Interp * interp,Jim_Cmd * cmdPtr,const char * cmdname)9484 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, const char *cmdname)
9485 {
9486 #ifdef jim_ext_namespace
9487     if (cmdPtr->isproc) {
9488 
9489         const char *pt = strrchr(cmdname, ':');
9490         if (pt && pt != cmdname && pt[-1] == ':') {
9491             Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9492             cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 1);
9493             Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9494 
9495             if (Jim_FindHashEntry(&interp->commands, pt + 1)) {
9496 
9497                 Jim_InterpIncrProcEpoch(interp);
9498             }
9499         }
9500     }
9501 #endif
9502 }
9503 
JimCreateProcedureCmd(Jim_Interp * interp,Jim_Obj * argListObjPtr,Jim_Obj * staticsListObjPtr,Jim_Obj * bodyObjPtr,Jim_Obj * nsObj)9504 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
9505     Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
9506 {
9507     Jim_Cmd *cmdPtr;
9508     int argListLen;
9509     int i;
9510 
9511     argListLen = Jim_ListLength(interp, argListObjPtr);
9512 
9513 
9514     cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
9515     memset(cmdPtr, 0, sizeof(*cmdPtr));
9516     cmdPtr->inUse = 1;
9517     cmdPtr->isproc = 1;
9518     cmdPtr->u.proc.argListObjPtr = argListObjPtr;
9519     cmdPtr->u.proc.argListLen = argListLen;
9520     cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
9521     cmdPtr->u.proc.argsPos = -1;
9522     cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
9523     cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
9524     Jim_IncrRefCount(argListObjPtr);
9525     Jim_IncrRefCount(bodyObjPtr);
9526     Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9527 
9528 
9529     if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
9530         goto err;
9531     }
9532 
9533 
9534 
9535     for (i = 0; i < argListLen; i++) {
9536         Jim_Obj *argPtr;
9537         Jim_Obj *nameObjPtr;
9538         Jim_Obj *defaultObjPtr;
9539         int len;
9540 
9541 
9542         argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
9543         len = Jim_ListLength(interp, argPtr);
9544         if (len == 0) {
9545             Jim_SetResultString(interp, "argument with no name", -1);
9546 err:
9547             JimDecrCmdRefCount(interp, cmdPtr);
9548             return NULL;
9549         }
9550         if (len > 2) {
9551             Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
9552             goto err;
9553         }
9554 
9555         if (len == 2) {
9556 
9557             nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
9558             defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
9559         }
9560         else {
9561 
9562             nameObjPtr = argPtr;
9563             defaultObjPtr = NULL;
9564         }
9565 
9566 
9567         if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
9568             if (cmdPtr->u.proc.argsPos >= 0) {
9569                 Jim_SetResultString(interp, "'args' specified more than once", -1);
9570                 goto err;
9571             }
9572             cmdPtr->u.proc.argsPos = i;
9573         }
9574         else {
9575             if (len == 2) {
9576                 cmdPtr->u.proc.optArity++;
9577             }
9578             else {
9579                 cmdPtr->u.proc.reqArity++;
9580             }
9581         }
9582 
9583         cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
9584         cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
9585     }
9586 
9587     return cmdPtr;
9588 }
9589 
Jim_DeleteCommand(Jim_Interp * interp,const char * name)9590 int Jim_DeleteCommand(Jim_Interp *interp, const char *name)
9591 {
9592     int ret = JIM_OK;
9593     Jim_Obj *qualifiedNameObj;
9594     const char *qualname = JimQualifyName(interp, name, &qualifiedNameObj);
9595 
9596     if (Jim_DeleteHashEntry(&interp->commands, qualname) == JIM_ERR) {
9597         Jim_SetResultFormatted(interp, "can't delete \"%s\": command doesn't exist", name);
9598         ret = JIM_ERR;
9599     }
9600     else {
9601         Jim_InterpIncrProcEpoch(interp);
9602     }
9603 
9604     JimFreeQualifiedName(interp, qualifiedNameObj);
9605 
9606     return ret;
9607 }
9608 
Jim_RenameCommand(Jim_Interp * interp,const char * oldName,const char * newName)9609 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, const char *newName)
9610 {
9611     int ret = JIM_ERR;
9612     Jim_HashEntry *he;
9613     Jim_Cmd *cmdPtr;
9614     Jim_Obj *qualifiedOldNameObj;
9615     Jim_Obj *qualifiedNewNameObj;
9616     const char *fqold;
9617     const char *fqnew;
9618 
9619     if (newName[0] == 0) {
9620         return Jim_DeleteCommand(interp, oldName);
9621     }
9622 
9623     fqold = JimQualifyName(interp, oldName, &qualifiedOldNameObj);
9624     fqnew = JimQualifyName(interp, newName, &qualifiedNewNameObj);
9625 
9626 
9627     he = Jim_FindHashEntry(&interp->commands, fqold);
9628     if (he == NULL) {
9629         Jim_SetResultFormatted(interp, "can't rename \"%s\": command doesn't exist", oldName);
9630     }
9631     else if (Jim_FindHashEntry(&interp->commands, fqnew)) {
9632         Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName);
9633     }
9634     else {
9635 
9636         cmdPtr = Jim_GetHashEntryVal(he);
9637         JimIncrCmdRefCount(cmdPtr);
9638         JimUpdateProcNamespace(interp, cmdPtr, fqnew);
9639         Jim_AddHashEntry(&interp->commands, fqnew, cmdPtr);
9640 
9641 
9642         Jim_DeleteHashEntry(&interp->commands, fqold);
9643 
9644 
9645         Jim_InterpIncrProcEpoch(interp);
9646 
9647         ret = JIM_OK;
9648     }
9649 
9650     JimFreeQualifiedName(interp, qualifiedOldNameObj);
9651     JimFreeQualifiedName(interp, qualifiedNewNameObj);
9652 
9653     return ret;
9654 }
9655 
9656 
FreeCommandInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)9657 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9658 {
9659     Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
9660 }
9661 
DupCommandInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)9662 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9663 {
9664     dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
9665     dupPtr->typePtr = srcPtr->typePtr;
9666     Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
9667 }
9668 
9669 static const Jim_ObjType commandObjType = {
9670     "command",
9671     FreeCommandInternalRep,
9672     DupCommandInternalRep,
9673     NULL,
9674     JIM_TYPE_REFERENCES,
9675 };
9676 
Jim_GetCommand(Jim_Interp * interp,Jim_Obj * objPtr,int flags)9677 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9678 {
9679     Jim_Cmd *cmd;
9680 
9681     if (objPtr->typePtr != &commandObjType ||
9682             objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch
9683 #ifdef jim_ext_namespace
9684             || !Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
9685 #endif
9686         ) {
9687 
9688 
9689 
9690         const char *name = Jim_String(objPtr);
9691         Jim_HashEntry *he;
9692 
9693         if (name[0] == ':' && name[1] == ':') {
9694             while (*++name == ':') {
9695             }
9696         }
9697 #ifdef jim_ext_namespace
9698         else if (Jim_Length(interp->framePtr->nsObj)) {
9699 
9700             Jim_Obj *nameObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9701             Jim_AppendStrings(interp, nameObj, "::", name, NULL);
9702             he = Jim_FindHashEntry(&interp->commands, Jim_String(nameObj));
9703             Jim_FreeNewObj(interp, nameObj);
9704             if (he) {
9705                 goto found;
9706             }
9707         }
9708 #endif
9709 
9710 
9711         he = Jim_FindHashEntry(&interp->commands, name);
9712         if (he == NULL) {
9713             if (flags & JIM_ERRMSG) {
9714                 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
9715             }
9716             return NULL;
9717         }
9718 #ifdef jim_ext_namespace
9719 found:
9720 #endif
9721         cmd = Jim_GetHashEntryVal(he);
9722 
9723 
9724         Jim_FreeIntRep(interp, objPtr);
9725         objPtr->typePtr = &commandObjType;
9726         objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
9727         objPtr->internalRep.cmdValue.cmdPtr = cmd;
9728         objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
9729         Jim_IncrRefCount(interp->framePtr->nsObj);
9730     }
9731     else {
9732         cmd = objPtr->internalRep.cmdValue.cmdPtr;
9733     }
9734     while (cmd->u.proc.upcall) {
9735         cmd = cmd->prevCmd;
9736     }
9737     return cmd;
9738 }
9739 
9740 
9741 
9742 #define JIM_DICT_SUGAR 100
9743 
9744 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
9745 
9746 static const Jim_ObjType variableObjType = {
9747     "variable",
9748     NULL,
9749     NULL,
9750     NULL,
9751     JIM_TYPE_REFERENCES,
9752 };
9753 
JimValidName(Jim_Interp * interp,const char * type,Jim_Obj * nameObjPtr)9754 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr)
9755 {
9756 
9757     if (nameObjPtr->typePtr != &variableObjType) {
9758         int len;
9759         const char *str = Jim_GetString(nameObjPtr, &len);
9760         if (memchr(str, '\0', len)) {
9761             Jim_SetResultFormatted(interp, "%s name contains embedded null", type);
9762             return JIM_ERR;
9763         }
9764     }
9765     return JIM_OK;
9766 }
9767 
SetVariableFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)9768 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9769 {
9770     const char *varName;
9771     Jim_CallFrame *framePtr;
9772     Jim_HashEntry *he;
9773     int global;
9774     int len;
9775 
9776 
9777     if (objPtr->typePtr == &variableObjType) {
9778         framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
9779         if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
9780 
9781             return JIM_OK;
9782         }
9783 
9784     }
9785     else if (objPtr->typePtr == &dictSubstObjType) {
9786         return JIM_DICT_SUGAR;
9787     }
9788     else if (JimValidName(interp, "variable", objPtr) != JIM_OK) {
9789         return JIM_ERR;
9790     }
9791 
9792 
9793     varName = Jim_GetString(objPtr, &len);
9794 
9795 
9796     if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
9797         return JIM_DICT_SUGAR;
9798     }
9799 
9800     if (varName[0] == ':' && varName[1] == ':') {
9801         while (*++varName == ':') {
9802         }
9803         global = 1;
9804         framePtr = interp->topFramePtr;
9805     }
9806     else {
9807         global = 0;
9808         framePtr = interp->framePtr;
9809     }
9810 
9811 
9812     he = Jim_FindHashEntry(&framePtr->vars, varName);
9813     if (he == NULL) {
9814         if (!global && framePtr->staticVars) {
9815 
9816             he = Jim_FindHashEntry(framePtr->staticVars, varName);
9817         }
9818         if (he == NULL) {
9819             return JIM_ERR;
9820         }
9821     }
9822 
9823 
9824     Jim_FreeIntRep(interp, objPtr);
9825     objPtr->typePtr = &variableObjType;
9826     objPtr->internalRep.varValue.callFrameId = framePtr->id;
9827     objPtr->internalRep.varValue.varPtr = Jim_GetHashEntryVal(he);
9828     objPtr->internalRep.varValue.global = global;
9829     return JIM_OK;
9830 }
9831 
9832 
9833 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
9834 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
9835 
JimCreateVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,Jim_Obj * valObjPtr)9836 static Jim_Var *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9837 {
9838     const char *name;
9839     Jim_CallFrame *framePtr;
9840     int global;
9841 
9842 
9843     Jim_Var *var = Jim_Alloc(sizeof(*var));
9844 
9845     var->objPtr = valObjPtr;
9846     Jim_IncrRefCount(valObjPtr);
9847     var->linkFramePtr = NULL;
9848 
9849     name = Jim_String(nameObjPtr);
9850     if (name[0] == ':' && name[1] == ':') {
9851         while (*++name == ':') {
9852         }
9853         framePtr = interp->topFramePtr;
9854         global = 1;
9855     }
9856     else {
9857         framePtr = interp->framePtr;
9858         global = 0;
9859     }
9860 
9861 
9862     Jim_AddHashEntry(&framePtr->vars, name, var);
9863 
9864 
9865     Jim_FreeIntRep(interp, nameObjPtr);
9866     nameObjPtr->typePtr = &variableObjType;
9867     nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
9868     nameObjPtr->internalRep.varValue.varPtr = var;
9869     nameObjPtr->internalRep.varValue.global = global;
9870 
9871     return var;
9872 }
9873 
9874 
Jim_SetVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,Jim_Obj * valObjPtr)9875 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9876 {
9877     int err;
9878     Jim_Var *var;
9879 
9880     switch (SetVariableFromAny(interp, nameObjPtr)) {
9881         case JIM_DICT_SUGAR:
9882             return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
9883 
9884         case JIM_ERR:
9885             if (JimValidName(interp, "variable", nameObjPtr) != JIM_OK) {
9886                 return JIM_ERR;
9887             }
9888             JimCreateVariable(interp, nameObjPtr, valObjPtr);
9889             break;
9890 
9891         case JIM_OK:
9892             var = nameObjPtr->internalRep.varValue.varPtr;
9893             if (var->linkFramePtr == NULL) {
9894                 Jim_IncrRefCount(valObjPtr);
9895                 Jim_DecrRefCount(interp, var->objPtr);
9896                 var->objPtr = valObjPtr;
9897             }
9898             else {
9899                 Jim_CallFrame *savedCallFrame;
9900 
9901                 savedCallFrame = interp->framePtr;
9902                 interp->framePtr = var->linkFramePtr;
9903                 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
9904                 interp->framePtr = savedCallFrame;
9905                 if (err != JIM_OK)
9906                     return err;
9907             }
9908     }
9909     return JIM_OK;
9910 }
9911 
Jim_SetVariableStr(Jim_Interp * interp,const char * name,Jim_Obj * objPtr)9912 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9913 {
9914     Jim_Obj *nameObjPtr;
9915     int result;
9916 
9917     nameObjPtr = Jim_NewStringObj(interp, name, -1);
9918     Jim_IncrRefCount(nameObjPtr);
9919     result = Jim_SetVariable(interp, nameObjPtr, objPtr);
9920     Jim_DecrRefCount(interp, nameObjPtr);
9921     return result;
9922 }
9923 
Jim_SetGlobalVariableStr(Jim_Interp * interp,const char * name,Jim_Obj * objPtr)9924 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9925 {
9926     Jim_CallFrame *savedFramePtr;
9927     int result;
9928 
9929     savedFramePtr = interp->framePtr;
9930     interp->framePtr = interp->topFramePtr;
9931     result = Jim_SetVariableStr(interp, name, objPtr);
9932     interp->framePtr = savedFramePtr;
9933     return result;
9934 }
9935 
Jim_SetVariableStrWithStr(Jim_Interp * interp,const char * name,const char * val)9936 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
9937 {
9938     Jim_Obj *valObjPtr;
9939     int result;
9940 
9941     valObjPtr = Jim_NewStringObj(interp, val, -1);
9942     Jim_IncrRefCount(valObjPtr);
9943     result = Jim_SetVariableStr(interp, name, valObjPtr);
9944     Jim_DecrRefCount(interp, valObjPtr);
9945     return result;
9946 }
9947 
Jim_SetVariableLink(Jim_Interp * interp,Jim_Obj * nameObjPtr,Jim_Obj * targetNameObjPtr,Jim_CallFrame * targetCallFrame)9948 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
9949     Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
9950 {
9951     const char *varName;
9952     const char *targetName;
9953     Jim_CallFrame *framePtr;
9954     Jim_Var *varPtr;
9955 
9956 
9957     switch (SetVariableFromAny(interp, nameObjPtr)) {
9958         case JIM_DICT_SUGAR:
9959 
9960             Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
9961             return JIM_ERR;
9962 
9963         case JIM_OK:
9964             varPtr = nameObjPtr->internalRep.varValue.varPtr;
9965 
9966             if (varPtr->linkFramePtr == NULL) {
9967                 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
9968                 return JIM_ERR;
9969             }
9970 
9971 
9972             varPtr->linkFramePtr = NULL;
9973             break;
9974     }
9975 
9976 
9977 
9978     varName = Jim_String(nameObjPtr);
9979 
9980     if (varName[0] == ':' && varName[1] == ':') {
9981         while (*++varName == ':') {
9982         }
9983 
9984         framePtr = interp->topFramePtr;
9985     }
9986     else {
9987         framePtr = interp->framePtr;
9988     }
9989 
9990     targetName = Jim_String(targetNameObjPtr);
9991     if (targetName[0] == ':' && targetName[1] == ':') {
9992         while (*++targetName == ':') {
9993         }
9994         targetNameObjPtr = Jim_NewStringObj(interp, targetName, -1);
9995         targetCallFrame = interp->topFramePtr;
9996     }
9997     Jim_IncrRefCount(targetNameObjPtr);
9998 
9999     if (framePtr->level < targetCallFrame->level) {
10000         Jim_SetResultFormatted(interp,
10001             "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
10002             nameObjPtr);
10003         Jim_DecrRefCount(interp, targetNameObjPtr);
10004         return JIM_ERR;
10005     }
10006 
10007 
10008     if (framePtr == targetCallFrame) {
10009         Jim_Obj *objPtr = targetNameObjPtr;
10010 
10011 
10012         while (1) {
10013             if (strcmp(Jim_String(objPtr), varName) == 0) {
10014                 Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
10015                 Jim_DecrRefCount(interp, targetNameObjPtr);
10016                 return JIM_ERR;
10017             }
10018             if (SetVariableFromAny(interp, objPtr) != JIM_OK)
10019                 break;
10020             varPtr = objPtr->internalRep.varValue.varPtr;
10021             if (varPtr->linkFramePtr != targetCallFrame)
10022                 break;
10023             objPtr = varPtr->objPtr;
10024         }
10025     }
10026 
10027 
10028     Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
10029 
10030     nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
10031     Jim_DecrRefCount(interp, targetNameObjPtr);
10032     return JIM_OK;
10033 }
10034 
Jim_GetVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,int flags)10035 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10036 {
10037     switch (SetVariableFromAny(interp, nameObjPtr)) {
10038         case JIM_OK:{
10039                 Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr;
10040 
10041                 if (varPtr->linkFramePtr == NULL) {
10042                     return varPtr->objPtr;
10043                 }
10044                 else {
10045                     Jim_Obj *objPtr;
10046 
10047 
10048                     Jim_CallFrame *savedCallFrame = interp->framePtr;
10049 
10050                     interp->framePtr = varPtr->linkFramePtr;
10051                     objPtr = Jim_GetVariable(interp, varPtr->objPtr, flags);
10052                     interp->framePtr = savedCallFrame;
10053                     if (objPtr) {
10054                         return objPtr;
10055                     }
10056 
10057                 }
10058             }
10059             break;
10060 
10061         case JIM_DICT_SUGAR:
10062 
10063             return JimDictSugarGet(interp, nameObjPtr, flags);
10064     }
10065     if (flags & JIM_ERRMSG) {
10066         Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
10067     }
10068     return NULL;
10069 }
10070 
Jim_GetGlobalVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,int flags)10071 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10072 {
10073     Jim_CallFrame *savedFramePtr;
10074     Jim_Obj *objPtr;
10075 
10076     savedFramePtr = interp->framePtr;
10077     interp->framePtr = interp->topFramePtr;
10078     objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
10079     interp->framePtr = savedFramePtr;
10080 
10081     return objPtr;
10082 }
10083 
Jim_GetVariableStr(Jim_Interp * interp,const char * name,int flags)10084 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
10085 {
10086     Jim_Obj *nameObjPtr, *varObjPtr;
10087 
10088     nameObjPtr = Jim_NewStringObj(interp, name, -1);
10089     Jim_IncrRefCount(nameObjPtr);
10090     varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
10091     Jim_DecrRefCount(interp, nameObjPtr);
10092     return varObjPtr;
10093 }
10094 
Jim_GetGlobalVariableStr(Jim_Interp * interp,const char * name,int flags)10095 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
10096 {
10097     Jim_CallFrame *savedFramePtr;
10098     Jim_Obj *objPtr;
10099 
10100     savedFramePtr = interp->framePtr;
10101     interp->framePtr = interp->topFramePtr;
10102     objPtr = Jim_GetVariableStr(interp, name, flags);
10103     interp->framePtr = savedFramePtr;
10104 
10105     return objPtr;
10106 }
10107 
Jim_UnsetVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,int flags)10108 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10109 {
10110     Jim_Var *varPtr;
10111     int retval;
10112     Jim_CallFrame *framePtr;
10113 
10114     retval = SetVariableFromAny(interp, nameObjPtr);
10115     if (retval == JIM_DICT_SUGAR) {
10116 
10117         return JimDictSugarSet(interp, nameObjPtr, NULL);
10118     }
10119     else if (retval == JIM_OK) {
10120         varPtr = nameObjPtr->internalRep.varValue.varPtr;
10121 
10122 
10123         if (varPtr->linkFramePtr) {
10124             framePtr = interp->framePtr;
10125             interp->framePtr = varPtr->linkFramePtr;
10126             retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
10127             interp->framePtr = framePtr;
10128         }
10129         else {
10130             const char *name = Jim_String(nameObjPtr);
10131             if (nameObjPtr->internalRep.varValue.global) {
10132                 name += 2;
10133                 framePtr = interp->topFramePtr;
10134             }
10135             else {
10136                 framePtr = interp->framePtr;
10137             }
10138 
10139             retval = Jim_DeleteHashEntry(&framePtr->vars, name);
10140             if (retval == JIM_OK) {
10141 
10142                 framePtr->id = interp->callFrameEpoch++;
10143             }
10144         }
10145     }
10146     if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
10147         Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
10148     }
10149     return retval;
10150 }
10151 
10152 
10153 
JimDictSugarParseVarKey(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj ** varPtrPtr,Jim_Obj ** keyPtrPtr)10154 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
10155     Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
10156 {
10157     const char *str, *p;
10158     int len, keyLen;
10159     Jim_Obj *varObjPtr, *keyObjPtr;
10160 
10161     str = Jim_GetString(objPtr, &len);
10162 
10163     p = strchr(str, '(');
10164     JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
10165 
10166     varObjPtr = Jim_NewStringObj(interp, str, p - str);
10167 
10168     p++;
10169     keyLen = (str + len) - p;
10170     if (str[len - 1] == ')') {
10171         keyLen--;
10172     }
10173 
10174 
10175     keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
10176 
10177     Jim_IncrRefCount(varObjPtr);
10178     Jim_IncrRefCount(keyObjPtr);
10179     *varPtrPtr = varObjPtr;
10180     *keyPtrPtr = keyObjPtr;
10181 }
10182 
JimDictSugarSet(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * valObjPtr)10183 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
10184 {
10185     int err;
10186 
10187     SetDictSubstFromAny(interp, objPtr);
10188 
10189     err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
10190         &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);
10191 
10192     if (err == JIM_OK) {
10193 
10194         Jim_SetEmptyResult(interp);
10195     }
10196     else {
10197         if (!valObjPtr) {
10198 
10199             if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
10200                 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
10201                     objPtr);
10202                 return err;
10203             }
10204         }
10205 
10206         Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
10207             (valObjPtr ? "set" : "unset"), objPtr);
10208     }
10209     return err;
10210 }
10211 
JimDictExpandArrayVariable(Jim_Interp * interp,Jim_Obj * varObjPtr,Jim_Obj * keyObjPtr,int flags)10212 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
10213     Jim_Obj *keyObjPtr, int flags)
10214 {
10215     Jim_Obj *dictObjPtr;
10216     Jim_Obj *resObjPtr = NULL;
10217     int ret;
10218 
10219     dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
10220     if (!dictObjPtr) {
10221         return NULL;
10222     }
10223 
10224     ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
10225     if (ret != JIM_OK) {
10226         Jim_SetResultFormatted(interp,
10227             "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
10228             ret < 0 ? "variable isn't" : "no such element in");
10229     }
10230     else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
10231 
10232         Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
10233     }
10234 
10235     return resObjPtr;
10236 }
10237 
10238 
JimDictSugarGet(Jim_Interp * interp,Jim_Obj * objPtr,int flags)10239 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10240 {
10241     SetDictSubstFromAny(interp, objPtr);
10242 
10243     return JimDictExpandArrayVariable(interp,
10244         objPtr->internalRep.dictSubstValue.varNameObjPtr,
10245         objPtr->internalRep.dictSubstValue.indexObjPtr, flags);
10246 }
10247 
10248 
10249 
FreeDictSubstInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)10250 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
10251 {
10252     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
10253     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
10254 }
10255 
DupDictSubstInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)10256 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
10257 {
10258 
10259     dupPtr->internalRep = srcPtr->internalRep;
10260 
10261     Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr);
10262     Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
10263 }
10264 
10265 
SetDictSubstFromAny(Jim_Interp * interp,Jim_Obj * objPtr)10266 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10267 {
10268     if (objPtr->typePtr != &dictSubstObjType) {
10269         Jim_Obj *varObjPtr, *keyObjPtr;
10270 
10271         if (objPtr->typePtr == &interpolatedObjType) {
10272 
10273 
10274             varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
10275             keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
10276 
10277             Jim_IncrRefCount(varObjPtr);
10278             Jim_IncrRefCount(keyObjPtr);
10279         }
10280         else {
10281             JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
10282         }
10283 
10284         Jim_FreeIntRep(interp, objPtr);
10285         objPtr->typePtr = &dictSubstObjType;
10286         objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
10287         objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
10288     }
10289 }
10290 
JimExpandDictSugar(Jim_Interp * interp,Jim_Obj * objPtr)10291 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10292 {
10293     Jim_Obj *resObjPtr = NULL;
10294     Jim_Obj *substKeyObjPtr = NULL;
10295 
10296     SetDictSubstFromAny(interp, objPtr);
10297 
10298     if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
10299             &substKeyObjPtr, JIM_NONE)
10300         != JIM_OK) {
10301         return NULL;
10302     }
10303     Jim_IncrRefCount(substKeyObjPtr);
10304     resObjPtr =
10305         JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
10306         substKeyObjPtr, 0);
10307     Jim_DecrRefCount(interp, substKeyObjPtr);
10308 
10309     return resObjPtr;
10310 }
10311 
JimExpandExprSugar(Jim_Interp * interp,Jim_Obj * objPtr)10312 static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10313 {
10314     if (Jim_EvalExpression(interp, objPtr) == JIM_OK) {
10315         return Jim_GetResult(interp);
10316     }
10317     return NULL;
10318 }
10319 
10320 
JimCreateCallFrame(Jim_Interp * interp,Jim_CallFrame * parent,Jim_Obj * nsObj)10321 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
10322 {
10323     Jim_CallFrame *cf;
10324 
10325     if (interp->freeFramesList) {
10326         cf = interp->freeFramesList;
10327         interp->freeFramesList = cf->next;
10328 
10329         cf->argv = NULL;
10330         cf->argc = 0;
10331         cf->procArgsObjPtr = NULL;
10332         cf->procBodyObjPtr = NULL;
10333         cf->next = NULL;
10334         cf->staticVars = NULL;
10335         cf->localCommands = NULL;
10336         cf->tailcallObj = NULL;
10337         cf->tailcallCmd = NULL;
10338     }
10339     else {
10340         cf = Jim_Alloc(sizeof(*cf));
10341         memset(cf, 0, sizeof(*cf));
10342 
10343         Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
10344     }
10345 
10346     cf->id = interp->callFrameEpoch++;
10347     cf->parent = parent;
10348     cf->level = parent ? parent->level + 1 : 0;
10349     cf->nsObj = nsObj;
10350     Jim_IncrRefCount(nsObj);
10351 
10352     return cf;
10353 }
10354 
JimDeleteLocalProcs(Jim_Interp * interp,Jim_Stack * localCommands)10355 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
10356 {
10357 
10358     if (localCommands) {
10359         Jim_Obj *cmdNameObj;
10360 
10361         while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
10362             Jim_HashEntry *he;
10363             Jim_Obj *fqObjName;
10364             Jim_HashTable *ht = &interp->commands;
10365 
10366             const char *fqname = JimQualifyName(interp, Jim_String(cmdNameObj), &fqObjName);
10367 
10368             he = Jim_FindHashEntry(ht, fqname);
10369 
10370             if (he) {
10371                 Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
10372                 if (cmd->prevCmd) {
10373                     Jim_Cmd *prevCmd = cmd->prevCmd;
10374                     cmd->prevCmd = NULL;
10375 
10376 
10377                     JimDecrCmdRefCount(interp, cmd);
10378 
10379 
10380                     Jim_SetHashVal(ht, he, prevCmd);
10381                 }
10382                 else {
10383                     Jim_DeleteHashEntry(ht, fqname);
10384                 }
10385                 Jim_InterpIncrProcEpoch(interp);
10386             }
10387             Jim_DecrRefCount(interp, cmdNameObj);
10388             JimFreeQualifiedName(interp, fqObjName);
10389         }
10390         Jim_FreeStack(localCommands);
10391         Jim_Free(localCommands);
10392     }
10393     return JIM_OK;
10394 }
10395 
JimInvokeDefer(Jim_Interp * interp,int retcode)10396 static int JimInvokeDefer(Jim_Interp *interp, int retcode)
10397 {
10398     Jim_Obj *objPtr;
10399 
10400 
10401     if (Jim_FindHashEntry(&interp->framePtr->vars, "jim::defer") == NULL) {
10402         return retcode;
10403     }
10404 
10405     objPtr = Jim_GetVariableStr(interp, "jim::defer", JIM_NONE);
10406 
10407     if (objPtr) {
10408         int ret = JIM_OK;
10409         int i;
10410         int listLen = Jim_ListLength(interp, objPtr);
10411         Jim_Obj *resultObjPtr;
10412 
10413         Jim_IncrRefCount(objPtr);
10414 
10415         resultObjPtr = Jim_GetResult(interp);
10416         Jim_IncrRefCount(resultObjPtr);
10417         Jim_SetEmptyResult(interp);
10418 
10419 
10420         for (i = listLen; i > 0; i--) {
10421 
10422             Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1);
10423             ret = Jim_EvalObj(interp, scriptObjPtr);
10424             if (ret != JIM_OK) {
10425                 break;
10426             }
10427         }
10428 
10429         if (ret == JIM_OK || retcode == JIM_ERR) {
10430 
10431             Jim_SetResult(interp, resultObjPtr);
10432         }
10433         else {
10434             retcode = ret;
10435         }
10436 
10437         Jim_DecrRefCount(interp, resultObjPtr);
10438         Jim_DecrRefCount(interp, objPtr);
10439     }
10440     return retcode;
10441 }
10442 
10443 #define JIM_FCF_FULL 0
10444 #define JIM_FCF_REUSE 1
JimFreeCallFrame(Jim_Interp * interp,Jim_CallFrame * cf,int action)10445 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
10446  {
10447     JimDeleteLocalProcs(interp, cf->localCommands);
10448 
10449     if (cf->procArgsObjPtr)
10450         Jim_DecrRefCount(interp, cf->procArgsObjPtr);
10451     if (cf->procBodyObjPtr)
10452         Jim_DecrRefCount(interp, cf->procBodyObjPtr);
10453     Jim_DecrRefCount(interp, cf->nsObj);
10454     if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
10455         Jim_FreeHashTable(&cf->vars);
10456     else {
10457         int i;
10458         Jim_HashEntry **table = cf->vars.table, *he;
10459 
10460         for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
10461             he = table[i];
10462             while (he != NULL) {
10463                 Jim_HashEntry *nextEntry = he->next;
10464                 Jim_Var *varPtr = Jim_GetHashEntryVal(he);
10465 
10466                 Jim_DecrRefCount(interp, varPtr->objPtr);
10467                 Jim_Free(Jim_GetHashEntryKey(he));
10468                 Jim_Free(varPtr);
10469                 Jim_Free(he);
10470                 table[i] = NULL;
10471                 he = nextEntry;
10472             }
10473         }
10474         cf->vars.used = 0;
10475     }
10476     cf->next = interp->freeFramesList;
10477     interp->freeFramesList = cf;
10478 }
10479 
10480 
10481 
Jim_IsBigEndian(void)10482 int Jim_IsBigEndian(void)
10483 {
10484     union {
10485         unsigned short s;
10486         unsigned char c[2];
10487     } uval = {0x0102};
10488 
10489     return uval.c[0] == 1;
10490 }
10491 
10492 
Jim_CreateInterp(void)10493 Jim_Interp *Jim_CreateInterp(void)
10494 {
10495     Jim_Interp *i = Jim_Alloc(sizeof(*i));
10496 
10497     memset(i, 0, sizeof(*i));
10498 
10499     i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
10500     i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
10501     i->lastCollectTime = time(NULL);
10502 
10503     Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
10504 #ifdef JIM_REFERENCES
10505     Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
10506 #endif
10507     Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
10508     Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
10509     i->emptyObj = Jim_NewEmptyStringObj(i);
10510     i->trueObj = Jim_NewIntObj(i, 1);
10511     i->falseObj = Jim_NewIntObj(i, 0);
10512     i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
10513     i->errorFileNameObj = i->emptyObj;
10514     i->result = i->emptyObj;
10515     i->stackTrace = Jim_NewListObj(i, NULL, 0);
10516     i->unknown = Jim_NewStringObj(i, "unknown", -1);
10517     i->errorProc = i->emptyObj;
10518     i->currentScriptObj = Jim_NewEmptyStringObj(i);
10519     i->nullScriptObj = Jim_NewEmptyStringObj(i);
10520     Jim_IncrRefCount(i->emptyObj);
10521     Jim_IncrRefCount(i->errorFileNameObj);
10522     Jim_IncrRefCount(i->result);
10523     Jim_IncrRefCount(i->stackTrace);
10524     Jim_IncrRefCount(i->unknown);
10525     Jim_IncrRefCount(i->currentScriptObj);
10526     Jim_IncrRefCount(i->nullScriptObj);
10527     Jim_IncrRefCount(i->errorProc);
10528     Jim_IncrRefCount(i->trueObj);
10529     Jim_IncrRefCount(i->falseObj);
10530 
10531 
10532     Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
10533     Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
10534 
10535     Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
10536     Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
10537     Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
10538     Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
10539     Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
10540     Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
10541     Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
10542     Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
10543 
10544     return i;
10545 }
10546 
Jim_FreeInterp(Jim_Interp * i)10547 void Jim_FreeInterp(Jim_Interp *i)
10548 {
10549     Jim_CallFrame *cf, *cfx;
10550 
10551     Jim_Obj *objPtr, *nextObjPtr;
10552 
10553 
10554     for (cf = i->framePtr; cf; cf = cfx) {
10555 
10556         JimInvokeDefer(i, JIM_OK);
10557         cfx = cf->parent;
10558         JimFreeCallFrame(i, cf, JIM_FCF_FULL);
10559     }
10560 
10561     Jim_DecrRefCount(i, i->emptyObj);
10562     Jim_DecrRefCount(i, i->trueObj);
10563     Jim_DecrRefCount(i, i->falseObj);
10564     Jim_DecrRefCount(i, i->result);
10565     Jim_DecrRefCount(i, i->stackTrace);
10566     Jim_DecrRefCount(i, i->errorProc);
10567     Jim_DecrRefCount(i, i->unknown);
10568     Jim_DecrRefCount(i, i->errorFileNameObj);
10569     Jim_DecrRefCount(i, i->currentScriptObj);
10570     Jim_DecrRefCount(i, i->nullScriptObj);
10571     Jim_FreeHashTable(&i->commands);
10572 #ifdef JIM_REFERENCES
10573     Jim_FreeHashTable(&i->references);
10574 #endif
10575     Jim_FreeHashTable(&i->packages);
10576     Jim_Free(i->prngState);
10577     Jim_FreeHashTable(&i->assocData);
10578 
10579 #ifdef JIM_MAINTAINER
10580     if (i->liveList != NULL) {
10581         objPtr = i->liveList;
10582 
10583         printf("\n-------------------------------------\n");
10584         printf("Objects still in the free list:\n");
10585         while (objPtr) {
10586             const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
10587             Jim_String(objPtr);
10588 
10589             if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
10590                 printf("%p (%d) %-10s: '%.20s...'\n",
10591                     (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
10592             }
10593             else {
10594                 printf("%p (%d) %-10s: '%s'\n",
10595                     (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
10596             }
10597             if (objPtr->typePtr == &sourceObjType) {
10598                 printf("FILE %s LINE %d\n",
10599                     Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
10600                     objPtr->internalRep.sourceValue.lineNumber);
10601             }
10602             objPtr = objPtr->nextObjPtr;
10603         }
10604         printf("-------------------------------------\n\n");
10605         JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
10606     }
10607 #endif
10608 
10609 
10610     objPtr = i->freeList;
10611     while (objPtr) {
10612         nextObjPtr = objPtr->nextObjPtr;
10613         Jim_Free(objPtr);
10614         objPtr = nextObjPtr;
10615     }
10616 
10617 
10618     for (cf = i->freeFramesList; cf; cf = cfx) {
10619         cfx = cf->next;
10620         if (cf->vars.table)
10621             Jim_FreeHashTable(&cf->vars);
10622         Jim_Free(cf);
10623     }
10624 
10625 
10626     Jim_Free(i);
10627 }
10628 
Jim_GetCallFrameByLevel(Jim_Interp * interp,Jim_Obj * levelObjPtr)10629 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10630 {
10631     long level;
10632     const char *str;
10633     Jim_CallFrame *framePtr;
10634 
10635     if (levelObjPtr) {
10636         str = Jim_String(levelObjPtr);
10637         if (str[0] == '#') {
10638             char *endptr;
10639 
10640             level = jim_strtol(str + 1, &endptr);
10641             if (str[1] == '\0' || endptr[0] != '\0') {
10642                 level = -1;
10643             }
10644         }
10645         else {
10646             if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
10647                 level = -1;
10648             }
10649             else {
10650 
10651                 level = interp->framePtr->level - level;
10652             }
10653         }
10654     }
10655     else {
10656         str = "1";
10657         level = interp->framePtr->level - 1;
10658     }
10659 
10660     if (level == 0) {
10661         return interp->topFramePtr;
10662     }
10663     if (level > 0) {
10664 
10665         for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10666             if (framePtr->level == level) {
10667                 return framePtr;
10668             }
10669         }
10670     }
10671 
10672     Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
10673     return NULL;
10674 }
10675 
JimGetCallFrameByInteger(Jim_Interp * interp,Jim_Obj * levelObjPtr)10676 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10677 {
10678     long level;
10679     Jim_CallFrame *framePtr;
10680 
10681     if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
10682         if (level <= 0) {
10683 
10684             level = interp->framePtr->level + level;
10685         }
10686 
10687         if (level == 0) {
10688             return interp->topFramePtr;
10689         }
10690 
10691 
10692         for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10693             if (framePtr->level == level) {
10694                 return framePtr;
10695             }
10696         }
10697     }
10698 
10699     Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
10700     return NULL;
10701 }
10702 
JimResetStackTrace(Jim_Interp * interp)10703 static void JimResetStackTrace(Jim_Interp *interp)
10704 {
10705     Jim_DecrRefCount(interp, interp->stackTrace);
10706     interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
10707     Jim_IncrRefCount(interp->stackTrace);
10708 }
10709 
JimSetStackTrace(Jim_Interp * interp,Jim_Obj * stackTraceObj)10710 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
10711 {
10712     int len;
10713 
10714 
10715     Jim_IncrRefCount(stackTraceObj);
10716     Jim_DecrRefCount(interp, interp->stackTrace);
10717     interp->stackTrace = stackTraceObj;
10718     interp->errorFlag = 1;
10719 
10720     len = Jim_ListLength(interp, interp->stackTrace);
10721     if (len >= 3) {
10722         if (Jim_Length(Jim_ListGetIndex(interp, interp->stackTrace, len - 2)) == 0) {
10723             interp->addStackTrace = 1;
10724         }
10725     }
10726 }
10727 
JimAppendStackTrace(Jim_Interp * interp,const char * procname,Jim_Obj * fileNameObj,int linenr)10728 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
10729     Jim_Obj *fileNameObj, int linenr)
10730 {
10731     if (strcmp(procname, "unknown") == 0) {
10732         procname = "";
10733     }
10734     if (!*procname && !Jim_Length(fileNameObj)) {
10735 
10736         return;
10737     }
10738 
10739     if (Jim_IsShared(interp->stackTrace)) {
10740         Jim_DecrRefCount(interp, interp->stackTrace);
10741         interp->stackTrace = Jim_DuplicateObj(interp, interp->stackTrace);
10742         Jim_IncrRefCount(interp->stackTrace);
10743     }
10744 
10745 
10746     if (!*procname && Jim_Length(fileNameObj)) {
10747 
10748         int len = Jim_ListLength(interp, interp->stackTrace);
10749 
10750         if (len >= 3) {
10751             Jim_Obj *objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 3);
10752             if (Jim_Length(objPtr)) {
10753 
10754                 objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 2);
10755                 if (Jim_Length(objPtr) == 0) {
10756 
10757                     ListSetIndex(interp, interp->stackTrace, len - 2, fileNameObj, 0);
10758                     ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), 0);
10759                     return;
10760                 }
10761             }
10762         }
10763     }
10764 
10765     Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewStringObj(interp, procname, -1));
10766     Jim_ListAppendElement(interp, interp->stackTrace, fileNameObj);
10767     Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewIntObj(interp, linenr));
10768 }
10769 
Jim_SetAssocData(Jim_Interp * interp,const char * key,Jim_InterpDeleteProc * delProc,void * data)10770 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
10771     void *data)
10772 {
10773     AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
10774 
10775     assocEntryPtr->delProc = delProc;
10776     assocEntryPtr->data = data;
10777     return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
10778 }
10779 
Jim_GetAssocData(Jim_Interp * interp,const char * key)10780 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
10781 {
10782     Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
10783 
10784     if (entryPtr != NULL) {
10785         AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr);
10786         return assocEntryPtr->data;
10787     }
10788     return NULL;
10789 }
10790 
Jim_DeleteAssocData(Jim_Interp * interp,const char * key)10791 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
10792 {
10793     return Jim_DeleteHashEntry(&interp->assocData, key);
10794 }
10795 
Jim_GetExitCode(Jim_Interp * interp)10796 int Jim_GetExitCode(Jim_Interp *interp)
10797 {
10798     return interp->exitCode;
10799 }
10800 
10801 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
10802 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
10803 
10804 static const Jim_ObjType intObjType = {
10805     "int",
10806     NULL,
10807     NULL,
10808     UpdateStringOfInt,
10809     JIM_TYPE_NONE,
10810 };
10811 
10812 static const Jim_ObjType coercedDoubleObjType = {
10813     "coerced-double",
10814     NULL,
10815     NULL,
10816     UpdateStringOfInt,
10817     JIM_TYPE_NONE,
10818 };
10819 
10820 
UpdateStringOfInt(struct Jim_Obj * objPtr)10821 static void UpdateStringOfInt(struct Jim_Obj *objPtr)
10822 {
10823     char buf[JIM_INTEGER_SPACE + 1];
10824     jim_wide wideValue = JimWideValue(objPtr);
10825     int pos = 0;
10826 
10827     if (wideValue == 0) {
10828         buf[pos++] = '0';
10829     }
10830     else {
10831         char tmp[JIM_INTEGER_SPACE];
10832         int num = 0;
10833         int i;
10834 
10835         if (wideValue < 0) {
10836             buf[pos++] = '-';
10837             i = wideValue % 10;
10838             tmp[num++] = (i > 0) ? (10 - i) : -i;
10839             wideValue /= -10;
10840         }
10841 
10842         while (wideValue) {
10843             tmp[num++] = wideValue % 10;
10844             wideValue /= 10;
10845         }
10846 
10847         for (i = 0; i < num; i++) {
10848             buf[pos++] = '0' + tmp[num - i - 1];
10849         }
10850     }
10851     buf[pos] = 0;
10852 
10853     JimSetStringBytes(objPtr, buf);
10854 }
10855 
SetIntFromAny(Jim_Interp * interp,Jim_Obj * objPtr,int flags)10856 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10857 {
10858     jim_wide wideValue;
10859     const char *str;
10860 
10861     if (objPtr->typePtr == &coercedDoubleObjType) {
10862 
10863         objPtr->typePtr = &intObjType;
10864         return JIM_OK;
10865     }
10866 
10867 
10868     str = Jim_String(objPtr);
10869 
10870     if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
10871         if (flags & JIM_ERRMSG) {
10872             Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
10873         }
10874         return JIM_ERR;
10875     }
10876     if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
10877         Jim_SetResultString(interp, "Integer value too big to be represented", -1);
10878         return JIM_ERR;
10879     }
10880 
10881     Jim_FreeIntRep(interp, objPtr);
10882     objPtr->typePtr = &intObjType;
10883     objPtr->internalRep.wideValue = wideValue;
10884     return JIM_OK;
10885 }
10886 
10887 #ifdef JIM_OPTIMIZATION
JimIsWide(Jim_Obj * objPtr)10888 static int JimIsWide(Jim_Obj *objPtr)
10889 {
10890     return objPtr->typePtr == &intObjType;
10891 }
10892 #endif
10893 
Jim_GetWide(Jim_Interp * interp,Jim_Obj * objPtr,jim_wide * widePtr)10894 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10895 {
10896     if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
10897         return JIM_ERR;
10898     *widePtr = JimWideValue(objPtr);
10899     return JIM_OK;
10900 }
10901 
10902 
JimGetWideNoErr(Jim_Interp * interp,Jim_Obj * objPtr,jim_wide * widePtr)10903 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10904 {
10905     if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
10906         return JIM_ERR;
10907     *widePtr = JimWideValue(objPtr);
10908     return JIM_OK;
10909 }
10910 
Jim_GetLong(Jim_Interp * interp,Jim_Obj * objPtr,long * longPtr)10911 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
10912 {
10913     jim_wide wideValue;
10914     int retval;
10915 
10916     retval = Jim_GetWide(interp, objPtr, &wideValue);
10917     if (retval == JIM_OK) {
10918         *longPtr = (long)wideValue;
10919         return JIM_OK;
10920     }
10921     return JIM_ERR;
10922 }
10923 
Jim_NewIntObj(Jim_Interp * interp,jim_wide wideValue)10924 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
10925 {
10926     Jim_Obj *objPtr;
10927 
10928     objPtr = Jim_NewObj(interp);
10929     objPtr->typePtr = &intObjType;
10930     objPtr->bytes = NULL;
10931     objPtr->internalRep.wideValue = wideValue;
10932     return objPtr;
10933 }
10934 
10935 #define JIM_DOUBLE_SPACE 30
10936 
10937 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
10938 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
10939 
10940 static const Jim_ObjType doubleObjType = {
10941     "double",
10942     NULL,
10943     NULL,
10944     UpdateStringOfDouble,
10945     JIM_TYPE_NONE,
10946 };
10947 
10948 #ifndef HAVE_ISNAN
10949 #undef isnan
10950 #define isnan(X) ((X) != (X))
10951 #endif
10952 #ifndef HAVE_ISINF
10953 #undef isinf
10954 #define isinf(X) (1.0 / (X) == 0.0)
10955 #endif
10956 
UpdateStringOfDouble(struct Jim_Obj * objPtr)10957 static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
10958 {
10959     double value = objPtr->internalRep.doubleValue;
10960 
10961     if (isnan(value)) {
10962         JimSetStringBytes(objPtr, "NaN");
10963         return;
10964     }
10965     if (isinf(value)) {
10966         if (value < 0) {
10967             JimSetStringBytes(objPtr, "-Inf");
10968         }
10969         else {
10970             JimSetStringBytes(objPtr, "Inf");
10971         }
10972         return;
10973     }
10974     {
10975         char buf[JIM_DOUBLE_SPACE + 1];
10976         int i;
10977         int len = sprintf(buf, "%.12g", value);
10978 
10979 
10980         for (i = 0; i < len; i++) {
10981             if (buf[i] == '.' || buf[i] == 'e') {
10982 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
10983                 char *e = strchr(buf, 'e');
10984                 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
10985 
10986                     e += 2;
10987                     memmove(e, e + 1, len - (e - buf));
10988                 }
10989 #endif
10990                 break;
10991             }
10992         }
10993         if (buf[i] == '\0') {
10994             buf[i++] = '.';
10995             buf[i++] = '0';
10996             buf[i] = '\0';
10997         }
10998         JimSetStringBytes(objPtr, buf);
10999     }
11000 }
11001 
SetDoubleFromAny(Jim_Interp * interp,Jim_Obj * objPtr)11002 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
11003 {
11004     double doubleValue;
11005     jim_wide wideValue;
11006     const char *str;
11007 
11008 #ifdef HAVE_LONG_LONG
11009 
11010 #define MIN_INT_IN_DOUBLE -(1LL << 53)
11011 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)
11012 
11013     if (objPtr->typePtr == &intObjType
11014         && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
11015         && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {
11016 
11017 
11018         objPtr->typePtr = &coercedDoubleObjType;
11019         return JIM_OK;
11020     }
11021 #endif
11022     str = Jim_String(objPtr);
11023 
11024     if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
11025 
11026         Jim_FreeIntRep(interp, objPtr);
11027         objPtr->typePtr = &coercedDoubleObjType;
11028         objPtr->internalRep.wideValue = wideValue;
11029         return JIM_OK;
11030     }
11031     else {
11032 
11033         if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
11034             Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
11035             return JIM_ERR;
11036         }
11037 
11038         Jim_FreeIntRep(interp, objPtr);
11039     }
11040     objPtr->typePtr = &doubleObjType;
11041     objPtr->internalRep.doubleValue = doubleValue;
11042     return JIM_OK;
11043 }
11044 
Jim_GetDouble(Jim_Interp * interp,Jim_Obj * objPtr,double * doublePtr)11045 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
11046 {
11047     if (objPtr->typePtr == &coercedDoubleObjType) {
11048         *doublePtr = JimWideValue(objPtr);
11049         return JIM_OK;
11050     }
11051     if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
11052         return JIM_ERR;
11053 
11054     if (objPtr->typePtr == &coercedDoubleObjType) {
11055         *doublePtr = JimWideValue(objPtr);
11056     }
11057     else {
11058         *doublePtr = objPtr->internalRep.doubleValue;
11059     }
11060     return JIM_OK;
11061 }
11062 
Jim_NewDoubleObj(Jim_Interp * interp,double doubleValue)11063 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
11064 {
11065     Jim_Obj *objPtr;
11066 
11067     objPtr = Jim_NewObj(interp);
11068     objPtr->typePtr = &doubleObjType;
11069     objPtr->bytes = NULL;
11070     objPtr->internalRep.doubleValue = doubleValue;
11071     return objPtr;
11072 }
11073 
11074 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
11075 
Jim_GetBoolean(Jim_Interp * interp,Jim_Obj * objPtr,int * booleanPtr)11076 int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
11077 {
11078     if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
11079         return JIM_ERR;
11080     *booleanPtr = (int) JimWideValue(objPtr);
11081     return JIM_OK;
11082 }
11083 
SetBooleanFromAny(Jim_Interp * interp,Jim_Obj * objPtr,int flags)11084 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
11085 {
11086     static const char * const falses[] = {
11087         "0", "false", "no", "off", NULL
11088     };
11089     static const char * const trues[] = {
11090         "1", "true", "yes", "on", NULL
11091     };
11092 
11093     int boolean;
11094 
11095     int index;
11096     if (Jim_GetEnum(interp, objPtr, falses, &index, NULL, 0) == JIM_OK) {
11097         boolean = 0;
11098     } else if (Jim_GetEnum(interp, objPtr, trues, &index, NULL, 0) == JIM_OK) {
11099         boolean = 1;
11100     } else {
11101         if (flags & JIM_ERRMSG) {
11102             Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr);
11103         }
11104         return JIM_ERR;
11105     }
11106 
11107 
11108     Jim_FreeIntRep(interp, objPtr);
11109     objPtr->typePtr = &intObjType;
11110     objPtr->internalRep.wideValue = boolean;
11111     return JIM_OK;
11112 }
11113 
11114 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
11115 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
11116 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11117 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11118 static void UpdateStringOfList(struct Jim_Obj *objPtr);
11119 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11120 
11121 static const Jim_ObjType listObjType = {
11122     "list",
11123     FreeListInternalRep,
11124     DupListInternalRep,
11125     UpdateStringOfList,
11126     JIM_TYPE_NONE,
11127 };
11128 
FreeListInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)11129 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11130 {
11131     int i;
11132 
11133     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
11134         Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
11135     }
11136     Jim_Free(objPtr->internalRep.listValue.ele);
11137 }
11138 
DupListInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)11139 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11140 {
11141     int i;
11142 
11143     JIM_NOTUSED(interp);
11144 
11145     dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
11146     dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
11147     dupPtr->internalRep.listValue.ele =
11148         Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
11149     memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
11150         sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
11151     for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
11152         Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
11153     }
11154     dupPtr->typePtr = &listObjType;
11155 }
11156 
11157 #define JIM_ELESTR_SIMPLE 0
11158 #define JIM_ELESTR_BRACE 1
11159 #define JIM_ELESTR_QUOTE 2
ListElementQuotingType(const char * s,int len)11160 static unsigned char ListElementQuotingType(const char *s, int len)
11161 {
11162     int i, level, blevel, trySimple = 1;
11163 
11164 
11165     if (len == 0)
11166         return JIM_ELESTR_BRACE;
11167     if (s[0] == '"' || s[0] == '{') {
11168         trySimple = 0;
11169         goto testbrace;
11170     }
11171     for (i = 0; i < len; i++) {
11172         switch (s[i]) {
11173             case ' ':
11174             case '$':
11175             case '"':
11176             case '[':
11177             case ']':
11178             case ';':
11179             case '\\':
11180             case '\r':
11181             case '\n':
11182             case '\t':
11183             case '\f':
11184             case '\v':
11185                 trySimple = 0;
11186 
11187             case '{':
11188             case '}':
11189                 goto testbrace;
11190         }
11191     }
11192     return JIM_ELESTR_SIMPLE;
11193 
11194   testbrace:
11195 
11196     if (s[len - 1] == '\\')
11197         return JIM_ELESTR_QUOTE;
11198     level = 0;
11199     blevel = 0;
11200     for (i = 0; i < len; i++) {
11201         switch (s[i]) {
11202             case '{':
11203                 level++;
11204                 break;
11205             case '}':
11206                 level--;
11207                 if (level < 0)
11208                     return JIM_ELESTR_QUOTE;
11209                 break;
11210             case '[':
11211                 blevel++;
11212                 break;
11213             case ']':
11214                 blevel--;
11215                 break;
11216             case '\\':
11217                 if (s[i + 1] == '\n')
11218                     return JIM_ELESTR_QUOTE;
11219                 else if (s[i + 1] != '\0')
11220                     i++;
11221                 break;
11222         }
11223     }
11224     if (blevel < 0) {
11225         return JIM_ELESTR_QUOTE;
11226     }
11227 
11228     if (level == 0) {
11229         if (!trySimple)
11230             return JIM_ELESTR_BRACE;
11231         for (i = 0; i < len; i++) {
11232             switch (s[i]) {
11233                 case ' ':
11234                 case '$':
11235                 case '"':
11236                 case '[':
11237                 case ']':
11238                 case ';':
11239                 case '\\':
11240                 case '\r':
11241                 case '\n':
11242                 case '\t':
11243                 case '\f':
11244                 case '\v':
11245                     return JIM_ELESTR_BRACE;
11246                     break;
11247             }
11248         }
11249         return JIM_ELESTR_SIMPLE;
11250     }
11251     return JIM_ELESTR_QUOTE;
11252 }
11253 
BackslashQuoteString(const char * s,int len,char * q)11254 static int BackslashQuoteString(const char *s, int len, char *q)
11255 {
11256     char *p = q;
11257 
11258     while (len--) {
11259         switch (*s) {
11260             case ' ':
11261             case '$':
11262             case '"':
11263             case '[':
11264             case ']':
11265             case '{':
11266             case '}':
11267             case ';':
11268             case '\\':
11269                 *p++ = '\\';
11270                 *p++ = *s++;
11271                 break;
11272             case '\n':
11273                 *p++ = '\\';
11274                 *p++ = 'n';
11275                 s++;
11276                 break;
11277             case '\r':
11278                 *p++ = '\\';
11279                 *p++ = 'r';
11280                 s++;
11281                 break;
11282             case '\t':
11283                 *p++ = '\\';
11284                 *p++ = 't';
11285                 s++;
11286                 break;
11287             case '\f':
11288                 *p++ = '\\';
11289                 *p++ = 'f';
11290                 s++;
11291                 break;
11292             case '\v':
11293                 *p++ = '\\';
11294                 *p++ = 'v';
11295                 s++;
11296                 break;
11297             default:
11298                 *p++ = *s++;
11299                 break;
11300         }
11301     }
11302     *p = '\0';
11303 
11304     return p - q;
11305 }
11306 
JimMakeListStringRep(Jim_Obj * objPtr,Jim_Obj ** objv,int objc)11307 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
11308 {
11309     #define STATIC_QUOTING_LEN 32
11310     int i, bufLen, realLength;
11311     const char *strRep;
11312     char *p;
11313     unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
11314 
11315 
11316     if (objc > STATIC_QUOTING_LEN) {
11317         quotingType = Jim_Alloc(objc);
11318     }
11319     else {
11320         quotingType = staticQuoting;
11321     }
11322     bufLen = 0;
11323     for (i = 0; i < objc; i++) {
11324         int len;
11325 
11326         strRep = Jim_GetString(objv[i], &len);
11327         quotingType[i] = ListElementQuotingType(strRep, len);
11328         switch (quotingType[i]) {
11329             case JIM_ELESTR_SIMPLE:
11330                 if (i != 0 || strRep[0] != '#') {
11331                     bufLen += len;
11332                     break;
11333                 }
11334 
11335                 quotingType[i] = JIM_ELESTR_BRACE;
11336 
11337             case JIM_ELESTR_BRACE:
11338                 bufLen += len + 2;
11339                 break;
11340             case JIM_ELESTR_QUOTE:
11341                 bufLen += len * 2;
11342                 break;
11343         }
11344         bufLen++;
11345     }
11346     bufLen++;
11347 
11348 
11349     p = objPtr->bytes = Jim_Alloc(bufLen + 1);
11350     realLength = 0;
11351     for (i = 0; i < objc; i++) {
11352         int len, qlen;
11353 
11354         strRep = Jim_GetString(objv[i], &len);
11355 
11356         switch (quotingType[i]) {
11357             case JIM_ELESTR_SIMPLE:
11358                 memcpy(p, strRep, len);
11359                 p += len;
11360                 realLength += len;
11361                 break;
11362             case JIM_ELESTR_BRACE:
11363                 *p++ = '{';
11364                 memcpy(p, strRep, len);
11365                 p += len;
11366                 *p++ = '}';
11367                 realLength += len + 2;
11368                 break;
11369             case JIM_ELESTR_QUOTE:
11370                 if (i == 0 && strRep[0] == '#') {
11371                     *p++ = '\\';
11372                     realLength++;
11373                 }
11374                 qlen = BackslashQuoteString(strRep, len, p);
11375                 p += qlen;
11376                 realLength += qlen;
11377                 break;
11378         }
11379 
11380         if (i + 1 != objc) {
11381             *p++ = ' ';
11382             realLength++;
11383         }
11384     }
11385     *p = '\0';
11386     objPtr->length = realLength;
11387 
11388     if (quotingType != staticQuoting) {
11389         Jim_Free(quotingType);
11390     }
11391 }
11392 
UpdateStringOfList(struct Jim_Obj * objPtr)11393 static void UpdateStringOfList(struct Jim_Obj *objPtr)
11394 {
11395     JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
11396 }
11397 
SetListFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)11398 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
11399 {
11400     struct JimParserCtx parser;
11401     const char *str;
11402     int strLen;
11403     Jim_Obj *fileNameObj;
11404     int linenr;
11405 
11406     if (objPtr->typePtr == &listObjType) {
11407         return JIM_OK;
11408     }
11409 
11410     if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
11411         Jim_Obj **listObjPtrPtr;
11412         int len;
11413         int i;
11414 
11415         listObjPtrPtr = JimDictPairs(objPtr, &len);
11416         for (i = 0; i < len; i++) {
11417             Jim_IncrRefCount(listObjPtrPtr[i]);
11418         }
11419 
11420 
11421         Jim_FreeIntRep(interp, objPtr);
11422         objPtr->typePtr = &listObjType;
11423         objPtr->internalRep.listValue.len = len;
11424         objPtr->internalRep.listValue.maxLen = len;
11425         objPtr->internalRep.listValue.ele = listObjPtrPtr;
11426 
11427         return JIM_OK;
11428     }
11429 
11430 
11431     if (objPtr->typePtr == &sourceObjType) {
11432         fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
11433         linenr = objPtr->internalRep.sourceValue.lineNumber;
11434     }
11435     else {
11436         fileNameObj = interp->emptyObj;
11437         linenr = 1;
11438     }
11439     Jim_IncrRefCount(fileNameObj);
11440 
11441 
11442     str = Jim_GetString(objPtr, &strLen);
11443 
11444     Jim_FreeIntRep(interp, objPtr);
11445     objPtr->typePtr = &listObjType;
11446     objPtr->internalRep.listValue.len = 0;
11447     objPtr->internalRep.listValue.maxLen = 0;
11448     objPtr->internalRep.listValue.ele = NULL;
11449 
11450 
11451     if (strLen) {
11452         JimParserInit(&parser, str, strLen, linenr);
11453         while (!parser.eof) {
11454             Jim_Obj *elementPtr;
11455 
11456             JimParseList(&parser);
11457             if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
11458                 continue;
11459             elementPtr = JimParserGetTokenObj(interp, &parser);
11460             JimSetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
11461             ListAppendElement(objPtr, elementPtr);
11462         }
11463     }
11464     Jim_DecrRefCount(interp, fileNameObj);
11465     return JIM_OK;
11466 }
11467 
Jim_NewListObj(Jim_Interp * interp,Jim_Obj * const * elements,int len)11468 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
11469 {
11470     Jim_Obj *objPtr;
11471 
11472     objPtr = Jim_NewObj(interp);
11473     objPtr->typePtr = &listObjType;
11474     objPtr->bytes = NULL;
11475     objPtr->internalRep.listValue.ele = NULL;
11476     objPtr->internalRep.listValue.len = 0;
11477     objPtr->internalRep.listValue.maxLen = 0;
11478 
11479     if (len) {
11480         ListInsertElements(objPtr, 0, len, elements);
11481     }
11482 
11483     return objPtr;
11484 }
11485 
JimListGetElements(Jim_Interp * interp,Jim_Obj * listObj,int * listLen,Jim_Obj *** listVec)11486 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
11487     Jim_Obj ***listVec)
11488 {
11489     *listLen = Jim_ListLength(interp, listObj);
11490     *listVec = listObj->internalRep.listValue.ele;
11491 }
11492 
11493 
JimSign(jim_wide w)11494 static int JimSign(jim_wide w)
11495 {
11496     if (w == 0) {
11497         return 0;
11498     }
11499     else if (w < 0) {
11500         return -1;
11501     }
11502     return 1;
11503 }
11504 
11505 
11506 struct lsort_info {
11507     jmp_buf jmpbuf;
11508     Jim_Obj *command;
11509     Jim_Interp *interp;
11510     enum {
11511         JIM_LSORT_ASCII,
11512         JIM_LSORT_NOCASE,
11513         JIM_LSORT_INTEGER,
11514         JIM_LSORT_REAL,
11515         JIM_LSORT_COMMAND
11516     } type;
11517     int order;
11518     int index;
11519     int indexed;
11520     int unique;
11521     int (*subfn)(Jim_Obj **, Jim_Obj **);
11522 };
11523 
11524 static struct lsort_info *sort_info;
11525 
ListSortIndexHelper(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11526 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11527 {
11528     Jim_Obj *lObj, *rObj;
11529 
11530     if (Jim_ListIndex(sort_info->interp, *lhsObj, sort_info->index, &lObj, JIM_ERRMSG) != JIM_OK ||
11531         Jim_ListIndex(sort_info->interp, *rhsObj, sort_info->index, &rObj, JIM_ERRMSG) != JIM_OK) {
11532         longjmp(sort_info->jmpbuf, JIM_ERR);
11533     }
11534     return sort_info->subfn(&lObj, &rObj);
11535 }
11536 
11537 
ListSortString(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11538 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11539 {
11540     return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
11541 }
11542 
ListSortStringNoCase(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11543 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11544 {
11545     return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
11546 }
11547 
ListSortInteger(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11548 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11549 {
11550     jim_wide lhs = 0, rhs = 0;
11551 
11552     if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11553         Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11554         longjmp(sort_info->jmpbuf, JIM_ERR);
11555     }
11556 
11557     return JimSign(lhs - rhs) * sort_info->order;
11558 }
11559 
ListSortReal(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11560 static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11561 {
11562     double lhs = 0, rhs = 0;
11563 
11564     if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11565         Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11566         longjmp(sort_info->jmpbuf, JIM_ERR);
11567     }
11568     if (lhs == rhs) {
11569         return 0;
11570     }
11571     if (lhs > rhs) {
11572         return sort_info->order;
11573     }
11574     return -sort_info->order;
11575 }
11576 
ListSortCommand(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11577 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11578 {
11579     Jim_Obj *compare_script;
11580     int rc;
11581 
11582     jim_wide ret = 0;
11583 
11584 
11585     compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
11586     Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
11587     Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);
11588 
11589     rc = Jim_EvalObj(sort_info->interp, compare_script);
11590 
11591     if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
11592         longjmp(sort_info->jmpbuf, rc);
11593     }
11594 
11595     return JimSign(ret) * sort_info->order;
11596 }
11597 
ListRemoveDuplicates(Jim_Obj * listObjPtr,int (* comp)(Jim_Obj ** lhs,Jim_Obj ** rhs))11598 static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
11599 {
11600     int src;
11601     int dst = 0;
11602     Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
11603 
11604     for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
11605         if (comp(&ele[dst], &ele[src]) == 0) {
11606 
11607             Jim_DecrRefCount(sort_info->interp, ele[dst]);
11608         }
11609         else {
11610 
11611             dst++;
11612         }
11613         ele[dst] = ele[src];
11614     }
11615 
11616 
11617     dst++;
11618     if (dst < listObjPtr->internalRep.listValue.len) {
11619         ele[dst] = ele[src];
11620     }
11621 
11622 
11623     listObjPtr->internalRep.listValue.len = dst;
11624 }
11625 
11626 
ListSortElements(Jim_Interp * interp,Jim_Obj * listObjPtr,struct lsort_info * info)11627 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
11628 {
11629     struct lsort_info *prev_info;
11630 
11631     typedef int (qsort_comparator) (const void *, const void *);
11632     int (*fn) (Jim_Obj **, Jim_Obj **);
11633     Jim_Obj **vector;
11634     int len;
11635     int rc;
11636 
11637     JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
11638     SetListFromAny(interp, listObjPtr);
11639 
11640 
11641     prev_info = sort_info;
11642     sort_info = info;
11643 
11644     vector = listObjPtr->internalRep.listValue.ele;
11645     len = listObjPtr->internalRep.listValue.len;
11646     switch (info->type) {
11647         case JIM_LSORT_ASCII:
11648             fn = ListSortString;
11649             break;
11650         case JIM_LSORT_NOCASE:
11651             fn = ListSortStringNoCase;
11652             break;
11653         case JIM_LSORT_INTEGER:
11654             fn = ListSortInteger;
11655             break;
11656         case JIM_LSORT_REAL:
11657             fn = ListSortReal;
11658             break;
11659         case JIM_LSORT_COMMAND:
11660             fn = ListSortCommand;
11661             break;
11662         default:
11663             fn = NULL;
11664             JimPanic((1, "ListSort called with invalid sort type"));
11665             return -1;
11666     }
11667 
11668     if (info->indexed) {
11669 
11670         info->subfn = fn;
11671         fn = ListSortIndexHelper;
11672     }
11673 
11674     if ((rc = setjmp(info->jmpbuf)) == 0) {
11675         qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
11676 
11677         if (info->unique && len > 1) {
11678             ListRemoveDuplicates(listObjPtr, fn);
11679         }
11680 
11681         Jim_InvalidateStringRep(listObjPtr);
11682     }
11683     sort_info = prev_info;
11684 
11685     return rc;
11686 }
11687 
ListInsertElements(Jim_Obj * listPtr,int idx,int elemc,Jim_Obj * const * elemVec)11688 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
11689 {
11690     int currentLen = listPtr->internalRep.listValue.len;
11691     int requiredLen = currentLen + elemc;
11692     int i;
11693     Jim_Obj **point;
11694 
11695     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
11696         if (requiredLen < 2) {
11697 
11698             requiredLen = 4;
11699         }
11700         else {
11701             requiredLen *= 2;
11702         }
11703 
11704         listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
11705             sizeof(Jim_Obj *) * requiredLen);
11706 
11707         listPtr->internalRep.listValue.maxLen = requiredLen;
11708     }
11709     if (idx < 0) {
11710         idx = currentLen;
11711     }
11712     point = listPtr->internalRep.listValue.ele + idx;
11713     memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
11714     for (i = 0; i < elemc; ++i) {
11715         point[i] = elemVec[i];
11716         Jim_IncrRefCount(point[i]);
11717     }
11718     listPtr->internalRep.listValue.len += elemc;
11719 }
11720 
ListAppendElement(Jim_Obj * listPtr,Jim_Obj * objPtr)11721 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
11722 {
11723     ListInsertElements(listPtr, -1, 1, &objPtr);
11724 }
11725 
ListAppendList(Jim_Obj * listPtr,Jim_Obj * appendListPtr)11726 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11727 {
11728     ListInsertElements(listPtr, -1,
11729         appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele);
11730 }
11731 
Jim_ListAppendElement(Jim_Interp * interp,Jim_Obj * listPtr,Jim_Obj * objPtr)11732 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
11733 {
11734     JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object"));
11735     SetListFromAny(interp, listPtr);
11736     Jim_InvalidateStringRep(listPtr);
11737     ListAppendElement(listPtr, objPtr);
11738 }
11739 
Jim_ListAppendList(Jim_Interp * interp,Jim_Obj * listPtr,Jim_Obj * appendListPtr)11740 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11741 {
11742     JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object"));
11743     SetListFromAny(interp, listPtr);
11744     SetListFromAny(interp, appendListPtr);
11745     Jim_InvalidateStringRep(listPtr);
11746     ListAppendList(listPtr, appendListPtr);
11747 }
11748 
Jim_ListLength(Jim_Interp * interp,Jim_Obj * objPtr)11749 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
11750 {
11751     SetListFromAny(interp, objPtr);
11752     return objPtr->internalRep.listValue.len;
11753 }
11754 
Jim_ListInsertElements(Jim_Interp * interp,Jim_Obj * listPtr,int idx,int objc,Jim_Obj * const * objVec)11755 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11756     int objc, Jim_Obj *const *objVec)
11757 {
11758     JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
11759     SetListFromAny(interp, listPtr);
11760     if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
11761         idx = listPtr->internalRep.listValue.len;
11762     else if (idx < 0)
11763         idx = 0;
11764     Jim_InvalidateStringRep(listPtr);
11765     ListInsertElements(listPtr, idx, objc, objVec);
11766 }
11767 
Jim_ListGetIndex(Jim_Interp * interp,Jim_Obj * listPtr,int idx)11768 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
11769 {
11770     SetListFromAny(interp, listPtr);
11771     if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11772         (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11773         return NULL;
11774     }
11775     if (idx < 0)
11776         idx = listPtr->internalRep.listValue.len + idx;
11777     return listPtr->internalRep.listValue.ele[idx];
11778 }
11779 
Jim_ListIndex(Jim_Interp * interp,Jim_Obj * listPtr,int idx,Jim_Obj ** objPtrPtr,int flags)11780 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
11781 {
11782     *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
11783     if (*objPtrPtr == NULL) {
11784         if (flags & JIM_ERRMSG) {
11785             Jim_SetResultString(interp, "list index out of range", -1);
11786         }
11787         return JIM_ERR;
11788     }
11789     return JIM_OK;
11790 }
11791 
ListSetIndex(Jim_Interp * interp,Jim_Obj * listPtr,int idx,Jim_Obj * newObjPtr,int flags)11792 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11793     Jim_Obj *newObjPtr, int flags)
11794 {
11795     SetListFromAny(interp, listPtr);
11796     if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11797         (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11798         if (flags & JIM_ERRMSG) {
11799             Jim_SetResultString(interp, "list index out of range", -1);
11800         }
11801         return JIM_ERR;
11802     }
11803     if (idx < 0)
11804         idx = listPtr->internalRep.listValue.len + idx;
11805     Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
11806     listPtr->internalRep.listValue.ele[idx] = newObjPtr;
11807     Jim_IncrRefCount(newObjPtr);
11808     return JIM_OK;
11809 }
11810 
Jim_ListSetIndex(Jim_Interp * interp,Jim_Obj * varNamePtr,Jim_Obj * const * indexv,int indexc,Jim_Obj * newObjPtr)11811 int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
11812     Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
11813 {
11814     Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
11815     int shared, i, idx;
11816 
11817     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
11818     if (objPtr == NULL)
11819         return JIM_ERR;
11820     if ((shared = Jim_IsShared(objPtr)))
11821         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
11822     for (i = 0; i < indexc - 1; i++) {
11823         listObjPtr = objPtr;
11824         if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
11825             goto err;
11826         if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_ERRMSG) != JIM_OK) {
11827             goto err;
11828         }
11829         if (Jim_IsShared(objPtr)) {
11830             objPtr = Jim_DuplicateObj(interp, objPtr);
11831             ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
11832         }
11833         Jim_InvalidateStringRep(listObjPtr);
11834     }
11835     if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
11836         goto err;
11837     if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
11838         goto err;
11839     Jim_InvalidateStringRep(objPtr);
11840     Jim_InvalidateStringRep(varObjPtr);
11841     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
11842         goto err;
11843     Jim_SetResult(interp, varObjPtr);
11844     return JIM_OK;
11845   err:
11846     if (shared) {
11847         Jim_FreeNewObj(interp, varObjPtr);
11848     }
11849     return JIM_ERR;
11850 }
11851 
Jim_ListJoin(Jim_Interp * interp,Jim_Obj * listObjPtr,const char * joinStr,int joinStrLen)11852 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
11853 {
11854     int i;
11855     int listLen = Jim_ListLength(interp, listObjPtr);
11856     Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
11857 
11858     for (i = 0; i < listLen; ) {
11859         Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i));
11860         if (++i != listLen) {
11861             Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11862         }
11863     }
11864     return resObjPtr;
11865 }
11866 
Jim_ConcatObj(Jim_Interp * interp,int objc,Jim_Obj * const * objv)11867 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
11868 {
11869     int i;
11870 
11871     for (i = 0; i < objc; i++) {
11872         if (!Jim_IsList(objv[i]))
11873             break;
11874     }
11875     if (i == objc) {
11876         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
11877 
11878         for (i = 0; i < objc; i++)
11879             ListAppendList(objPtr, objv[i]);
11880         return objPtr;
11881     }
11882     else {
11883 
11884         int len = 0, objLen;
11885         char *bytes, *p;
11886 
11887 
11888         for (i = 0; i < objc; i++) {
11889             len += Jim_Length(objv[i]);
11890         }
11891         if (objc)
11892             len += objc - 1;
11893 
11894         p = bytes = Jim_Alloc(len + 1);
11895         for (i = 0; i < objc; i++) {
11896             const char *s = Jim_GetString(objv[i], &objLen);
11897 
11898 
11899             while (objLen && isspace(UCHAR(*s))) {
11900                 s++;
11901                 objLen--;
11902                 len--;
11903             }
11904 
11905             while (objLen && isspace(UCHAR(s[objLen - 1]))) {
11906 
11907                 if (objLen > 1 && s[objLen - 2] == '\\') {
11908                     break;
11909                 }
11910                 objLen--;
11911                 len--;
11912             }
11913             memcpy(p, s, objLen);
11914             p += objLen;
11915             if (i + 1 != objc) {
11916                 if (objLen)
11917                     *p++ = ' ';
11918                 else {
11919                     len--;
11920                 }
11921             }
11922         }
11923         *p = '\0';
11924         return Jim_NewStringObjNoAlloc(interp, bytes, len);
11925     }
11926 }
11927 
Jim_ListRange(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr)11928 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr,
11929     Jim_Obj *lastObjPtr)
11930 {
11931     int first, last;
11932     int len, rangeLen;
11933 
11934     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
11935         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
11936         return NULL;
11937     len = Jim_ListLength(interp, listObjPtr);
11938     first = JimRelToAbsIndex(len, first);
11939     last = JimRelToAbsIndex(len, last);
11940     JimRelToAbsRange(len, &first, &last, &rangeLen);
11941     if (first == 0 && last == len) {
11942         return listObjPtr;
11943     }
11944     return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
11945 }
11946 
11947 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11948 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11949 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
11950 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11951 
11952 
JimObjectHTHashFunction(const void * key)11953 static unsigned int JimObjectHTHashFunction(const void *key)
11954 {
11955     int len;
11956     const char *str = Jim_GetString((Jim_Obj *)key, &len);
11957     return Jim_GenHashFunction((const unsigned char *)str, len);
11958 }
11959 
JimObjectHTKeyCompare(void * privdata,const void * key1,const void * key2)11960 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
11961 {
11962     return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
11963 }
11964 
JimObjectHTKeyValDup(void * privdata,const void * val)11965 static void *JimObjectHTKeyValDup(void *privdata, const void *val)
11966 {
11967     Jim_IncrRefCount((Jim_Obj *)val);
11968     return (void *)val;
11969 }
11970 
JimObjectHTKeyValDestructor(void * interp,void * val)11971 static void JimObjectHTKeyValDestructor(void *interp, void *val)
11972 {
11973     Jim_DecrRefCount(interp, (Jim_Obj *)val);
11974 }
11975 
11976 static const Jim_HashTableType JimDictHashTableType = {
11977     JimObjectHTHashFunction,
11978     JimObjectHTKeyValDup,
11979     JimObjectHTKeyValDup,
11980     JimObjectHTKeyCompare,
11981     JimObjectHTKeyValDestructor,
11982     JimObjectHTKeyValDestructor
11983 };
11984 
11985 static const Jim_ObjType dictObjType = {
11986     "dict",
11987     FreeDictInternalRep,
11988     DupDictInternalRep,
11989     UpdateStringOfDict,
11990     JIM_TYPE_NONE,
11991 };
11992 
FreeDictInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)11993 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11994 {
11995     JIM_NOTUSED(interp);
11996 
11997     Jim_FreeHashTable(objPtr->internalRep.ptr);
11998     Jim_Free(objPtr->internalRep.ptr);
11999 }
12000 
DupDictInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)12001 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
12002 {
12003     Jim_HashTable *ht, *dupHt;
12004     Jim_HashTableIterator htiter;
12005     Jim_HashEntry *he;
12006 
12007 
12008     ht = srcPtr->internalRep.ptr;
12009     dupHt = Jim_Alloc(sizeof(*dupHt));
12010     Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
12011     if (ht->size != 0)
12012         Jim_ExpandHashTable(dupHt, ht->size);
12013 
12014     JimInitHashTableIterator(ht, &htiter);
12015     while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
12016         Jim_AddHashEntry(dupHt, he->key, he->u.val);
12017     }
12018 
12019     dupPtr->internalRep.ptr = dupHt;
12020     dupPtr->typePtr = &dictObjType;
12021 }
12022 
JimDictPairs(Jim_Obj * dictPtr,int * len)12023 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len)
12024 {
12025     Jim_HashTable *ht;
12026     Jim_HashTableIterator htiter;
12027     Jim_HashEntry *he;
12028     Jim_Obj **objv;
12029     int i;
12030 
12031     ht = dictPtr->internalRep.ptr;
12032 
12033 
12034     objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *));
12035     JimInitHashTableIterator(ht, &htiter);
12036     i = 0;
12037     while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
12038         objv[i++] = Jim_GetHashEntryKey(he);
12039         objv[i++] = Jim_GetHashEntryVal(he);
12040     }
12041     *len = i;
12042     return objv;
12043 }
12044 
UpdateStringOfDict(struct Jim_Obj * objPtr)12045 static void UpdateStringOfDict(struct Jim_Obj *objPtr)
12046 {
12047 
12048     int len;
12049     Jim_Obj **objv = JimDictPairs(objPtr, &len);
12050 
12051 
12052     JimMakeListStringRep(objPtr, objv, len);
12053 
12054     Jim_Free(objv);
12055 }
12056 
SetDictFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)12057 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
12058 {
12059     int listlen;
12060 
12061     if (objPtr->typePtr == &dictObjType) {
12062         return JIM_OK;
12063     }
12064 
12065     if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
12066         Jim_String(objPtr);
12067     }
12068 
12069 
12070     listlen = Jim_ListLength(interp, objPtr);
12071     if (listlen % 2) {
12072         Jim_SetResultString(interp, "missing value to go with key", -1);
12073         return JIM_ERR;
12074     }
12075     else {
12076 
12077         Jim_HashTable *ht;
12078         int i;
12079 
12080         ht = Jim_Alloc(sizeof(*ht));
12081         Jim_InitHashTable(ht, &JimDictHashTableType, interp);
12082 
12083         for (i = 0; i < listlen; i += 2) {
12084             Jim_Obj *keyObjPtr = Jim_ListGetIndex(interp, objPtr, i);
12085             Jim_Obj *valObjPtr = Jim_ListGetIndex(interp, objPtr, i + 1);
12086 
12087             Jim_ReplaceHashEntry(ht, keyObjPtr, valObjPtr);
12088         }
12089 
12090         Jim_FreeIntRep(interp, objPtr);
12091         objPtr->typePtr = &dictObjType;
12092         objPtr->internalRep.ptr = ht;
12093 
12094         return JIM_OK;
12095     }
12096 }
12097 
12098 
12099 
DictAddElement(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * keyObjPtr,Jim_Obj * valueObjPtr)12100 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12101     Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12102 {
12103     Jim_HashTable *ht = objPtr->internalRep.ptr;
12104 
12105     if (valueObjPtr == NULL) {
12106         return Jim_DeleteHashEntry(ht, keyObjPtr);
12107     }
12108     Jim_ReplaceHashEntry(ht, keyObjPtr, valueObjPtr);
12109     return JIM_OK;
12110 }
12111 
Jim_DictAddElement(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * keyObjPtr,Jim_Obj * valueObjPtr)12112 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12113     Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12114 {
12115     JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
12116     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
12117         return JIM_ERR;
12118     }
12119     Jim_InvalidateStringRep(objPtr);
12120     return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
12121 }
12122 
Jim_NewDictObj(Jim_Interp * interp,Jim_Obj * const * elements,int len)12123 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
12124 {
12125     Jim_Obj *objPtr;
12126     int i;
12127 
12128     JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
12129 
12130     objPtr = Jim_NewObj(interp);
12131     objPtr->typePtr = &dictObjType;
12132     objPtr->bytes = NULL;
12133     objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
12134     Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
12135     for (i = 0; i < len; i += 2)
12136         DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
12137     return objPtr;
12138 }
12139 
Jim_DictKey(Jim_Interp * interp,Jim_Obj * dictPtr,Jim_Obj * keyPtr,Jim_Obj ** objPtrPtr,int flags)12140 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
12141     Jim_Obj **objPtrPtr, int flags)
12142 {
12143     Jim_HashEntry *he;
12144     Jim_HashTable *ht;
12145 
12146     if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12147         return -1;
12148     }
12149     ht = dictPtr->internalRep.ptr;
12150     if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
12151         if (flags & JIM_ERRMSG) {
12152             Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
12153         }
12154         return JIM_ERR;
12155     }
12156     else {
12157         *objPtrPtr = Jim_GetHashEntryVal(he);
12158         return JIM_OK;
12159     }
12160 }
12161 
12162 
Jim_DictPairs(Jim_Interp * interp,Jim_Obj * dictPtr,Jim_Obj *** objPtrPtr,int * len)12163 int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len)
12164 {
12165     if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12166         return JIM_ERR;
12167     }
12168     *objPtrPtr = JimDictPairs(dictPtr, len);
12169 
12170     return JIM_OK;
12171 }
12172 
12173 
12174 
Jim_DictKeysVector(Jim_Interp * interp,Jim_Obj * dictPtr,Jim_Obj * const * keyv,int keyc,Jim_Obj ** objPtrPtr,int flags)12175 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
12176     Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
12177 {
12178     int i;
12179 
12180     if (keyc == 0) {
12181         *objPtrPtr = dictPtr;
12182         return JIM_OK;
12183     }
12184 
12185     for (i = 0; i < keyc; i++) {
12186         Jim_Obj *objPtr;
12187 
12188         int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
12189         if (rc != JIM_OK) {
12190             return rc;
12191         }
12192         dictPtr = objPtr;
12193     }
12194     *objPtrPtr = dictPtr;
12195     return JIM_OK;
12196 }
12197 
Jim_SetDictKeysVector(Jim_Interp * interp,Jim_Obj * varNamePtr,Jim_Obj * const * keyv,int keyc,Jim_Obj * newObjPtr,int flags)12198 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
12199     Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags)
12200 {
12201     Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
12202     int shared, i;
12203 
12204     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
12205     if (objPtr == NULL) {
12206         if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
12207 
12208             return JIM_ERR;
12209         }
12210         varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
12211         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
12212             Jim_FreeNewObj(interp, varObjPtr);
12213             return JIM_ERR;
12214         }
12215     }
12216     if ((shared = Jim_IsShared(objPtr)))
12217         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
12218     for (i = 0; i < keyc; i++) {
12219         dictObjPtr = objPtr;
12220 
12221 
12222         if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
12223             goto err;
12224         }
12225 
12226         if (i == keyc - 1) {
12227 
12228             if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
12229                 if (newObjPtr || (flags & JIM_MUSTEXIST)) {
12230                     goto err;
12231                 }
12232             }
12233             break;
12234         }
12235 
12236 
12237         Jim_InvalidateStringRep(dictObjPtr);
12238         if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
12239                 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
12240             if (Jim_IsShared(objPtr)) {
12241                 objPtr = Jim_DuplicateObj(interp, objPtr);
12242                 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12243             }
12244         }
12245         else {
12246             if (newObjPtr == NULL) {
12247                 goto err;
12248             }
12249             objPtr = Jim_NewDictObj(interp, NULL, 0);
12250             DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12251         }
12252     }
12253 
12254     Jim_InvalidateStringRep(objPtr);
12255     Jim_InvalidateStringRep(varObjPtr);
12256     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
12257         goto err;
12258     }
12259     Jim_SetResult(interp, varObjPtr);
12260     return JIM_OK;
12261   err:
12262     if (shared) {
12263         Jim_FreeNewObj(interp, varObjPtr);
12264     }
12265     return JIM_ERR;
12266 }
12267 
12268 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
12269 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
12270 
12271 static const Jim_ObjType indexObjType = {
12272     "index",
12273     NULL,
12274     NULL,
12275     UpdateStringOfIndex,
12276     JIM_TYPE_NONE,
12277 };
12278 
UpdateStringOfIndex(struct Jim_Obj * objPtr)12279 static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
12280 {
12281     if (objPtr->internalRep.intValue == -1) {
12282         JimSetStringBytes(objPtr, "end");
12283     }
12284     else {
12285         char buf[JIM_INTEGER_SPACE + 1];
12286         if (objPtr->internalRep.intValue >= 0) {
12287             sprintf(buf, "%d", objPtr->internalRep.intValue);
12288         }
12289         else {
12290 
12291             sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
12292         }
12293         JimSetStringBytes(objPtr, buf);
12294     }
12295 }
12296 
SetIndexFromAny(Jim_Interp * interp,Jim_Obj * objPtr)12297 static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12298 {
12299     int idx, end = 0;
12300     const char *str;
12301     char *endptr;
12302 
12303 
12304     str = Jim_String(objPtr);
12305 
12306 
12307     if (strncmp(str, "end", 3) == 0) {
12308         end = 1;
12309         str += 3;
12310         idx = 0;
12311     }
12312     else {
12313         idx = jim_strtol(str, &endptr);
12314 
12315         if (endptr == str) {
12316             goto badindex;
12317         }
12318         str = endptr;
12319     }
12320 
12321 
12322     if (*str == '+' || *str == '-') {
12323         int sign = (*str == '+' ? 1 : -1);
12324 
12325         idx += sign * jim_strtol(++str, &endptr);
12326         if (str == endptr || *endptr) {
12327             goto badindex;
12328         }
12329         str = endptr;
12330     }
12331 
12332     while (isspace(UCHAR(*str))) {
12333         str++;
12334     }
12335     if (*str) {
12336         goto badindex;
12337     }
12338     if (end) {
12339         if (idx > 0) {
12340             idx = INT_MAX;
12341         }
12342         else {
12343 
12344             idx--;
12345         }
12346     }
12347     else if (idx < 0) {
12348         idx = -INT_MAX;
12349     }
12350 
12351 
12352     Jim_FreeIntRep(interp, objPtr);
12353     objPtr->typePtr = &indexObjType;
12354     objPtr->internalRep.intValue = idx;
12355     return JIM_OK;
12356 
12357   badindex:
12358     Jim_SetResultFormatted(interp,
12359         "bad index \"%#s\": must be integer?[+-]integer? or end?[+-]integer?", objPtr);
12360     return JIM_ERR;
12361 }
12362 
Jim_GetIndex(Jim_Interp * interp,Jim_Obj * objPtr,int * indexPtr)12363 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
12364 {
12365 
12366     if (objPtr->typePtr == &intObjType) {
12367         jim_wide val = JimWideValue(objPtr);
12368 
12369         if (val < 0)
12370             *indexPtr = -INT_MAX;
12371         else if (val > INT_MAX)
12372             *indexPtr = INT_MAX;
12373         else
12374             *indexPtr = (int)val;
12375         return JIM_OK;
12376     }
12377     if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
12378         return JIM_ERR;
12379     *indexPtr = objPtr->internalRep.intValue;
12380     return JIM_OK;
12381 }
12382 
12383 
12384 
12385 static const char * const jimReturnCodes[] = {
12386     "ok",
12387     "error",
12388     "return",
12389     "break",
12390     "continue",
12391     "signal",
12392     "exit",
12393     "eval",
12394     NULL
12395 };
12396 
12397 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1)
12398 
12399 static const Jim_ObjType returnCodeObjType = {
12400     "return-code",
12401     NULL,
12402     NULL,
12403     NULL,
12404     JIM_TYPE_NONE,
12405 };
12406 
Jim_ReturnCode(int code)12407 const char *Jim_ReturnCode(int code)
12408 {
12409     if (code < 0 || code >= (int)jimReturnCodesSize) {
12410         return "?";
12411     }
12412     else {
12413         return jimReturnCodes[code];
12414     }
12415 }
12416 
SetReturnCodeFromAny(Jim_Interp * interp,Jim_Obj * objPtr)12417 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12418 {
12419     int returnCode;
12420     jim_wide wideValue;
12421 
12422 
12423     if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
12424         returnCode = (int)wideValue;
12425     else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
12426         Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
12427         return JIM_ERR;
12428     }
12429 
12430     Jim_FreeIntRep(interp, objPtr);
12431     objPtr->typePtr = &returnCodeObjType;
12432     objPtr->internalRep.intValue = returnCode;
12433     return JIM_OK;
12434 }
12435 
Jim_GetReturnCode(Jim_Interp * interp,Jim_Obj * objPtr,int * intPtr)12436 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
12437 {
12438     if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
12439         return JIM_ERR;
12440     *intPtr = objPtr->internalRep.intValue;
12441     return JIM_OK;
12442 }
12443 
12444 static int JimParseExprOperator(struct JimParserCtx *pc);
12445 static int JimParseExprNumber(struct JimParserCtx *pc);
12446 static int JimParseExprIrrational(struct JimParserCtx *pc);
12447 static int JimParseExprBoolean(struct JimParserCtx *pc);
12448 
12449 
12450 enum
12451 {
12452 
12453 
12454 
12455     JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
12456     JIM_EXPROP_DIV,
12457     JIM_EXPROP_MOD,
12458     JIM_EXPROP_SUB,
12459     JIM_EXPROP_ADD,
12460     JIM_EXPROP_LSHIFT,
12461     JIM_EXPROP_RSHIFT,
12462     JIM_EXPROP_ROTL,
12463     JIM_EXPROP_ROTR,
12464     JIM_EXPROP_LT,
12465     JIM_EXPROP_GT,
12466     JIM_EXPROP_LTE,
12467     JIM_EXPROP_GTE,
12468     JIM_EXPROP_NUMEQ,
12469     JIM_EXPROP_NUMNE,
12470     JIM_EXPROP_BITAND,
12471     JIM_EXPROP_BITXOR,
12472     JIM_EXPROP_BITOR,
12473     JIM_EXPROP_LOGICAND,
12474     JIM_EXPROP_LOGICOR,
12475     JIM_EXPROP_TERNARY,
12476     JIM_EXPROP_COLON,
12477     JIM_EXPROP_POW,
12478 
12479 
12480     JIM_EXPROP_STREQ,
12481     JIM_EXPROP_STRNE,
12482     JIM_EXPROP_STRIN,
12483     JIM_EXPROP_STRNI,
12484 
12485 
12486     JIM_EXPROP_NOT,
12487     JIM_EXPROP_BITNOT,
12488     JIM_EXPROP_UNARYMINUS,
12489     JIM_EXPROP_UNARYPLUS,
12490 
12491 
12492     JIM_EXPROP_FUNC_INT,
12493     JIM_EXPROP_FUNC_WIDE,
12494     JIM_EXPROP_FUNC_ABS,
12495     JIM_EXPROP_FUNC_DOUBLE,
12496     JIM_EXPROP_FUNC_ROUND,
12497     JIM_EXPROP_FUNC_RAND,
12498     JIM_EXPROP_FUNC_SRAND,
12499 
12500 
12501     JIM_EXPROP_FUNC_SIN,
12502     JIM_EXPROP_FUNC_COS,
12503     JIM_EXPROP_FUNC_TAN,
12504     JIM_EXPROP_FUNC_ASIN,
12505     JIM_EXPROP_FUNC_ACOS,
12506     JIM_EXPROP_FUNC_ATAN,
12507     JIM_EXPROP_FUNC_ATAN2,
12508     JIM_EXPROP_FUNC_SINH,
12509     JIM_EXPROP_FUNC_COSH,
12510     JIM_EXPROP_FUNC_TANH,
12511     JIM_EXPROP_FUNC_CEIL,
12512     JIM_EXPROP_FUNC_FLOOR,
12513     JIM_EXPROP_FUNC_EXP,
12514     JIM_EXPROP_FUNC_LOG,
12515     JIM_EXPROP_FUNC_LOG10,
12516     JIM_EXPROP_FUNC_SQRT,
12517     JIM_EXPROP_FUNC_POW,
12518     JIM_EXPROP_FUNC_HYPOT,
12519     JIM_EXPROP_FUNC_FMOD,
12520 };
12521 
12522 struct JimExprNode {
12523     int type;
12524     struct Jim_Obj *objPtr;
12525 
12526     struct JimExprNode *left;
12527     struct JimExprNode *right;
12528     struct JimExprNode *ternary;
12529 };
12530 
12531 
12532 typedef struct Jim_ExprOperator
12533 {
12534     const char *name;
12535     int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode);
12536     unsigned char precedence;
12537     unsigned char arity;
12538     unsigned char attr;
12539     unsigned char namelen;
12540 } Jim_ExprOperator;
12541 
12542 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr);
12543 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
12544 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);
12545 
JimExprOpNumUnary(Jim_Interp * interp,struct JimExprNode * node)12546 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
12547 {
12548     int intresult = 1;
12549     int rc;
12550     double dA, dC = 0;
12551     jim_wide wA, wC = 0;
12552     Jim_Obj *A;
12553 
12554     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
12555         return rc;
12556     }
12557 
12558     if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
12559         switch (node->type) {
12560             case JIM_EXPROP_FUNC_INT:
12561             case JIM_EXPROP_FUNC_WIDE:
12562             case JIM_EXPROP_FUNC_ROUND:
12563             case JIM_EXPROP_UNARYPLUS:
12564                 wC = wA;
12565                 break;
12566             case JIM_EXPROP_FUNC_DOUBLE:
12567                 dC = wA;
12568                 intresult = 0;
12569                 break;
12570             case JIM_EXPROP_FUNC_ABS:
12571                 wC = wA >= 0 ? wA : -wA;
12572                 break;
12573             case JIM_EXPROP_UNARYMINUS:
12574                 wC = -wA;
12575                 break;
12576             case JIM_EXPROP_NOT:
12577                 wC = !wA;
12578                 break;
12579             default:
12580                 abort();
12581         }
12582     }
12583     else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
12584         switch (node->type) {
12585             case JIM_EXPROP_FUNC_INT:
12586             case JIM_EXPROP_FUNC_WIDE:
12587                 wC = dA;
12588                 break;
12589             case JIM_EXPROP_FUNC_ROUND:
12590                 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
12591                 break;
12592             case JIM_EXPROP_FUNC_DOUBLE:
12593             case JIM_EXPROP_UNARYPLUS:
12594                 dC = dA;
12595                 intresult = 0;
12596                 break;
12597             case JIM_EXPROP_FUNC_ABS:
12598 #ifdef JIM_MATH_FUNCTIONS
12599                 dC = fabs(dA);
12600 #else
12601                 dC = dA >= 0 ? dA : -dA;
12602 #endif
12603                 intresult = 0;
12604                 break;
12605             case JIM_EXPROP_UNARYMINUS:
12606                 dC = -dA;
12607                 intresult = 0;
12608                 break;
12609             case JIM_EXPROP_NOT:
12610                 wC = !dA;
12611                 break;
12612             default:
12613                 abort();
12614         }
12615     }
12616 
12617     if (rc == JIM_OK) {
12618         if (intresult) {
12619             Jim_SetResultInt(interp, wC);
12620         }
12621         else {
12622             Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
12623         }
12624     }
12625 
12626     Jim_DecrRefCount(interp, A);
12627 
12628     return rc;
12629 }
12630 
JimRandDouble(Jim_Interp * interp)12631 static double JimRandDouble(Jim_Interp *interp)
12632 {
12633     unsigned long x;
12634     JimRandomBytes(interp, &x, sizeof(x));
12635 
12636     return (double)x / (unsigned long)~0;
12637 }
12638 
JimExprOpIntUnary(Jim_Interp * interp,struct JimExprNode * node)12639 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node)
12640 {
12641     jim_wide wA;
12642     Jim_Obj *A;
12643     int rc;
12644 
12645     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
12646         return rc;
12647     }
12648 
12649     rc = Jim_GetWide(interp, A, &wA);
12650     if (rc == JIM_OK) {
12651         switch (node->type) {
12652             case JIM_EXPROP_BITNOT:
12653                 Jim_SetResultInt(interp, ~wA);
12654                 break;
12655             case JIM_EXPROP_FUNC_SRAND:
12656                 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
12657                 Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12658                 break;
12659             default:
12660                 abort();
12661         }
12662     }
12663 
12664     Jim_DecrRefCount(interp, A);
12665 
12666     return rc;
12667 }
12668 
JimExprOpNone(Jim_Interp * interp,struct JimExprNode * node)12669 static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node)
12670 {
12671     JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
12672 
12673     Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12674 
12675     return JIM_OK;
12676 }
12677 
12678 #ifdef JIM_MATH_FUNCTIONS
JimExprOpDoubleUnary(Jim_Interp * interp,struct JimExprNode * node)12679 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node)
12680 {
12681     int rc;
12682     double dA, dC;
12683     Jim_Obj *A;
12684 
12685     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
12686         return rc;
12687     }
12688 
12689     rc = Jim_GetDouble(interp, A, &dA);
12690     if (rc == JIM_OK) {
12691         switch (node->type) {
12692             case JIM_EXPROP_FUNC_SIN:
12693                 dC = sin(dA);
12694                 break;
12695             case JIM_EXPROP_FUNC_COS:
12696                 dC = cos(dA);
12697                 break;
12698             case JIM_EXPROP_FUNC_TAN:
12699                 dC = tan(dA);
12700                 break;
12701             case JIM_EXPROP_FUNC_ASIN:
12702                 dC = asin(dA);
12703                 break;
12704             case JIM_EXPROP_FUNC_ACOS:
12705                 dC = acos(dA);
12706                 break;
12707             case JIM_EXPROP_FUNC_ATAN:
12708                 dC = atan(dA);
12709                 break;
12710             case JIM_EXPROP_FUNC_SINH:
12711                 dC = sinh(dA);
12712                 break;
12713             case JIM_EXPROP_FUNC_COSH:
12714                 dC = cosh(dA);
12715                 break;
12716             case JIM_EXPROP_FUNC_TANH:
12717                 dC = tanh(dA);
12718                 break;
12719             case JIM_EXPROP_FUNC_CEIL:
12720                 dC = ceil(dA);
12721                 break;
12722             case JIM_EXPROP_FUNC_FLOOR:
12723                 dC = floor(dA);
12724                 break;
12725             case JIM_EXPROP_FUNC_EXP:
12726                 dC = exp(dA);
12727                 break;
12728             case JIM_EXPROP_FUNC_LOG:
12729                 dC = log(dA);
12730                 break;
12731             case JIM_EXPROP_FUNC_LOG10:
12732                 dC = log10(dA);
12733                 break;
12734             case JIM_EXPROP_FUNC_SQRT:
12735                 dC = sqrt(dA);
12736                 break;
12737             default:
12738                 abort();
12739         }
12740         Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
12741     }
12742 
12743     Jim_DecrRefCount(interp, A);
12744 
12745     return rc;
12746 }
12747 #endif
12748 
12749 
JimExprOpIntBin(Jim_Interp * interp,struct JimExprNode * node)12750 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node)
12751 {
12752     jim_wide wA, wB;
12753     int rc;
12754     Jim_Obj *A, *B;
12755 
12756     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
12757         return rc;
12758     }
12759     if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
12760         Jim_DecrRefCount(interp, A);
12761         return rc;
12762     }
12763 
12764     rc = JIM_ERR;
12765 
12766     if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
12767         jim_wide wC;
12768 
12769         rc = JIM_OK;
12770 
12771         switch (node->type) {
12772             case JIM_EXPROP_LSHIFT:
12773                 wC = wA << wB;
12774                 break;
12775             case JIM_EXPROP_RSHIFT:
12776                 wC = wA >> wB;
12777                 break;
12778             case JIM_EXPROP_BITAND:
12779                 wC = wA & wB;
12780                 break;
12781             case JIM_EXPROP_BITXOR:
12782                 wC = wA ^ wB;
12783                 break;
12784             case JIM_EXPROP_BITOR:
12785                 wC = wA | wB;
12786                 break;
12787             case JIM_EXPROP_MOD:
12788                 if (wB == 0) {
12789                     wC = 0;
12790                     Jim_SetResultString(interp, "Division by zero", -1);
12791                     rc = JIM_ERR;
12792                 }
12793                 else {
12794                     int negative = 0;
12795 
12796                     if (wB < 0) {
12797                         wB = -wB;
12798                         wA = -wA;
12799                         negative = 1;
12800                     }
12801                     wC = wA % wB;
12802                     if (wC < 0) {
12803                         wC += wB;
12804                     }
12805                     if (negative) {
12806                         wC = -wC;
12807                     }
12808                 }
12809                 break;
12810             case JIM_EXPROP_ROTL:
12811             case JIM_EXPROP_ROTR:{
12812 
12813                     unsigned long uA = (unsigned long)wA;
12814                     unsigned long uB = (unsigned long)wB;
12815                     const unsigned int S = sizeof(unsigned long) * 8;
12816 
12817 
12818                     uB %= S;
12819 
12820                     if (node->type == JIM_EXPROP_ROTR) {
12821                         uB = S - uB;
12822                     }
12823                     wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
12824                     break;
12825                 }
12826             default:
12827                 abort();
12828         }
12829         Jim_SetResultInt(interp, wC);
12830     }
12831 
12832     Jim_DecrRefCount(interp, A);
12833     Jim_DecrRefCount(interp, B);
12834 
12835     return rc;
12836 }
12837 
12838 
12839 
JimExprOpBin(Jim_Interp * interp,struct JimExprNode * node)12840 static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node)
12841 {
12842     int rc = JIM_OK;
12843     double dA, dB, dC = 0;
12844     jim_wide wA, wB, wC = 0;
12845     Jim_Obj *A, *B;
12846 
12847     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
12848         return rc;
12849     }
12850     if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
12851         Jim_DecrRefCount(interp, A);
12852         return rc;
12853     }
12854 
12855     if ((A->typePtr != &doubleObjType || A->bytes) &&
12856         (B->typePtr != &doubleObjType || B->bytes) &&
12857         JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
12858 
12859 
12860 
12861         switch (node->type) {
12862             case JIM_EXPROP_POW:
12863             case JIM_EXPROP_FUNC_POW:
12864                 if (wA == 0 && wB < 0) {
12865                     Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
12866                     rc = JIM_ERR;
12867                     goto done;
12868                 }
12869                 wC = JimPowWide(wA, wB);
12870                 goto intresult;
12871             case JIM_EXPROP_ADD:
12872                 wC = wA + wB;
12873                 goto intresult;
12874             case JIM_EXPROP_SUB:
12875                 wC = wA - wB;
12876                 goto intresult;
12877             case JIM_EXPROP_MUL:
12878                 wC = wA * wB;
12879                 goto intresult;
12880             case JIM_EXPROP_DIV:
12881                 if (wB == 0) {
12882                     Jim_SetResultString(interp, "Division by zero", -1);
12883                     rc = JIM_ERR;
12884                     goto done;
12885                 }
12886                 else {
12887                     if (wB < 0) {
12888                         wB = -wB;
12889                         wA = -wA;
12890                     }
12891                     wC = wA / wB;
12892                     if (wA % wB < 0) {
12893                         wC--;
12894                     }
12895                     goto intresult;
12896                 }
12897             case JIM_EXPROP_LT:
12898                 wC = wA < wB;
12899                 goto intresult;
12900             case JIM_EXPROP_GT:
12901                 wC = wA > wB;
12902                 goto intresult;
12903             case JIM_EXPROP_LTE:
12904                 wC = wA <= wB;
12905                 goto intresult;
12906             case JIM_EXPROP_GTE:
12907                 wC = wA >= wB;
12908                 goto intresult;
12909             case JIM_EXPROP_NUMEQ:
12910                 wC = wA == wB;
12911                 goto intresult;
12912             case JIM_EXPROP_NUMNE:
12913                 wC = wA != wB;
12914                 goto intresult;
12915         }
12916     }
12917     if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
12918         switch (node->type) {
12919 #ifndef JIM_MATH_FUNCTIONS
12920             case JIM_EXPROP_POW:
12921             case JIM_EXPROP_FUNC_POW:
12922             case JIM_EXPROP_FUNC_ATAN2:
12923             case JIM_EXPROP_FUNC_HYPOT:
12924             case JIM_EXPROP_FUNC_FMOD:
12925                 Jim_SetResultString(interp, "unsupported", -1);
12926                 rc = JIM_ERR;
12927                 goto done;
12928 #else
12929             case JIM_EXPROP_POW:
12930             case JIM_EXPROP_FUNC_POW:
12931                 dC = pow(dA, dB);
12932                 goto doubleresult;
12933             case JIM_EXPROP_FUNC_ATAN2:
12934                 dC = atan2(dA, dB);
12935                 goto doubleresult;
12936             case JIM_EXPROP_FUNC_HYPOT:
12937                 dC = hypot(dA, dB);
12938                 goto doubleresult;
12939             case JIM_EXPROP_FUNC_FMOD:
12940                 dC = fmod(dA, dB);
12941                 goto doubleresult;
12942 #endif
12943             case JIM_EXPROP_ADD:
12944                 dC = dA + dB;
12945                 goto doubleresult;
12946             case JIM_EXPROP_SUB:
12947                 dC = dA - dB;
12948                 goto doubleresult;
12949             case JIM_EXPROP_MUL:
12950                 dC = dA * dB;
12951                 goto doubleresult;
12952             case JIM_EXPROP_DIV:
12953                 if (dB == 0) {
12954 #ifdef INFINITY
12955                     dC = dA < 0 ? -INFINITY : INFINITY;
12956 #else
12957                     dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
12958 #endif
12959                 }
12960                 else {
12961                     dC = dA / dB;
12962                 }
12963                 goto doubleresult;
12964             case JIM_EXPROP_LT:
12965                 wC = dA < dB;
12966                 goto intresult;
12967             case JIM_EXPROP_GT:
12968                 wC = dA > dB;
12969                 goto intresult;
12970             case JIM_EXPROP_LTE:
12971                 wC = dA <= dB;
12972                 goto intresult;
12973             case JIM_EXPROP_GTE:
12974                 wC = dA >= dB;
12975                 goto intresult;
12976             case JIM_EXPROP_NUMEQ:
12977                 wC = dA == dB;
12978                 goto intresult;
12979             case JIM_EXPROP_NUMNE:
12980                 wC = dA != dB;
12981                 goto intresult;
12982         }
12983     }
12984     else {
12985 
12986 
12987 
12988         int i = Jim_StringCompareObj(interp, A, B, 0);
12989 
12990         switch (node->type) {
12991             case JIM_EXPROP_LT:
12992                 wC = i < 0;
12993                 goto intresult;
12994             case JIM_EXPROP_GT:
12995                 wC = i > 0;
12996                 goto intresult;
12997             case JIM_EXPROP_LTE:
12998                 wC = i <= 0;
12999                 goto intresult;
13000             case JIM_EXPROP_GTE:
13001                 wC = i >= 0;
13002                 goto intresult;
13003             case JIM_EXPROP_NUMEQ:
13004                 wC = i == 0;
13005                 goto intresult;
13006             case JIM_EXPROP_NUMNE:
13007                 wC = i != 0;
13008                 goto intresult;
13009         }
13010     }
13011 
13012     rc = JIM_ERR;
13013 done:
13014     Jim_DecrRefCount(interp, A);
13015     Jim_DecrRefCount(interp, B);
13016     return rc;
13017 intresult:
13018     Jim_SetResultInt(interp, wC);
13019     goto done;
13020 doubleresult:
13021     Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
13022     goto done;
13023 }
13024 
JimSearchList(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_Obj * valObj)13025 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
13026 {
13027     int listlen;
13028     int i;
13029 
13030     listlen = Jim_ListLength(interp, listObjPtr);
13031     for (i = 0; i < listlen; i++) {
13032         if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
13033             return 1;
13034         }
13035     }
13036     return 0;
13037 }
13038 
13039 
13040 
JimExprOpStrBin(Jim_Interp * interp,struct JimExprNode * node)13041 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
13042 {
13043     Jim_Obj *A, *B;
13044     jim_wide wC;
13045     int rc;
13046 
13047     if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13048         return rc;
13049     }
13050     if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
13051         Jim_DecrRefCount(interp, A);
13052         return rc;
13053     }
13054 
13055     switch (node->type) {
13056         case JIM_EXPROP_STREQ:
13057         case JIM_EXPROP_STRNE:
13058             wC = Jim_StringEqObj(A, B);
13059             if (node->type == JIM_EXPROP_STRNE) {
13060                 wC = !wC;
13061             }
13062             break;
13063         case JIM_EXPROP_STRIN:
13064             wC = JimSearchList(interp, B, A);
13065             break;
13066         case JIM_EXPROP_STRNI:
13067             wC = !JimSearchList(interp, B, A);
13068             break;
13069         default:
13070             abort();
13071     }
13072     Jim_SetResultInt(interp, wC);
13073 
13074     Jim_DecrRefCount(interp, A);
13075     Jim_DecrRefCount(interp, B);
13076 
13077     return rc;
13078 }
13079 
ExprBool(Jim_Interp * interp,Jim_Obj * obj)13080 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
13081 {
13082     long l;
13083     double d;
13084     int b;
13085     int ret = -1;
13086 
13087 
13088     Jim_IncrRefCount(obj);
13089 
13090     if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
13091         ret = (l != 0);
13092     }
13093     else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
13094         ret = (d != 0);
13095     }
13096     else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
13097         ret = (b != 0);
13098     }
13099 
13100     Jim_DecrRefCount(interp, obj);
13101     return ret;
13102 }
13103 
JimExprOpAnd(Jim_Interp * interp,struct JimExprNode * node)13104 static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node)
13105 {
13106 
13107     int result = JimExprGetTermBoolean(interp, node->left);
13108 
13109     if (result == 1) {
13110 
13111         result = JimExprGetTermBoolean(interp, node->right);
13112     }
13113     if (result == -1) {
13114         return JIM_ERR;
13115     }
13116     Jim_SetResultInt(interp, result);
13117     return JIM_OK;
13118 }
13119 
JimExprOpOr(Jim_Interp * interp,struct JimExprNode * node)13120 static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node)
13121 {
13122 
13123     int result = JimExprGetTermBoolean(interp, node->left);
13124 
13125     if (result == 0) {
13126 
13127         result = JimExprGetTermBoolean(interp, node->right);
13128     }
13129     if (result == -1) {
13130         return JIM_ERR;
13131     }
13132     Jim_SetResultInt(interp, result);
13133     return JIM_OK;
13134 }
13135 
JimExprOpTernary(Jim_Interp * interp,struct JimExprNode * node)13136 static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node)
13137 {
13138 
13139     int result = JimExprGetTermBoolean(interp, node->left);
13140 
13141     if (result == 1) {
13142 
13143         return JimExprEvalTermNode(interp, node->right);
13144     }
13145     else if (result == 0) {
13146 
13147         return JimExprEvalTermNode(interp, node->ternary);
13148     }
13149 
13150     return JIM_ERR;
13151 }
13152 
13153 enum
13154 {
13155     OP_FUNC = 0x0001,
13156     OP_RIGHT_ASSOC = 0x0002,
13157 };
13158 
13159 #define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
13160 #define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0)
13161 
13162 static const struct Jim_ExprOperator Jim_ExprOperators[] = {
13163     OPRINIT("*", 110, 2, JimExprOpBin),
13164     OPRINIT("/", 110, 2, JimExprOpBin),
13165     OPRINIT("%", 110, 2, JimExprOpIntBin),
13166 
13167     OPRINIT("-", 100, 2, JimExprOpBin),
13168     OPRINIT("+", 100, 2, JimExprOpBin),
13169 
13170     OPRINIT("<<", 90, 2, JimExprOpIntBin),
13171     OPRINIT(">>", 90, 2, JimExprOpIntBin),
13172 
13173     OPRINIT("<<<", 90, 2, JimExprOpIntBin),
13174     OPRINIT(">>>", 90, 2, JimExprOpIntBin),
13175 
13176     OPRINIT("<", 80, 2, JimExprOpBin),
13177     OPRINIT(">", 80, 2, JimExprOpBin),
13178     OPRINIT("<=", 80, 2, JimExprOpBin),
13179     OPRINIT(">=", 80, 2, JimExprOpBin),
13180 
13181     OPRINIT("==", 70, 2, JimExprOpBin),
13182     OPRINIT("!=", 70, 2, JimExprOpBin),
13183 
13184     OPRINIT("&", 50, 2, JimExprOpIntBin),
13185     OPRINIT("^", 49, 2, JimExprOpIntBin),
13186     OPRINIT("|", 48, 2, JimExprOpIntBin),
13187 
13188     OPRINIT("&&", 10, 2, JimExprOpAnd),
13189     OPRINIT("||", 9, 2, JimExprOpOr),
13190     OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC),
13191     OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC),
13192 
13193 
13194     OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),
13195 
13196     OPRINIT("eq", 60, 2, JimExprOpStrBin),
13197     OPRINIT("ne", 60, 2, JimExprOpStrBin),
13198 
13199     OPRINIT("in", 55, 2, JimExprOpStrBin),
13200     OPRINIT("ni", 55, 2, JimExprOpStrBin),
13201 
13202     OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
13203     OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
13204     OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
13205     OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
13206 
13207 
13208 
13209     OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC),
13210     OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC),
13211     OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC),
13212     OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC),
13213     OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC),
13214     OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC),
13215     OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC),
13216 
13217 #ifdef JIM_MATH_FUNCTIONS
13218     OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13219     OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13220     OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13221     OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13222     OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13223     OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13224     OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC),
13225     OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13226     OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13227     OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13228     OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13229     OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13230     OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13231     OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13232     OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13233     OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13234     OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC),
13235     OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC),
13236     OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC),
13237 #endif
13238 };
13239 #undef OPRINIT
13240 #undef OPRINIT_ATTR
13241 
13242 #define JIM_EXPR_OPERATORS_NUM \
13243     (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
13244 
JimParseExpression(struct JimParserCtx * pc)13245 static int JimParseExpression(struct JimParserCtx *pc)
13246 {
13247 
13248     while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
13249         if (*pc->p == '\n') {
13250             pc->linenr++;
13251         }
13252         pc->p++;
13253         pc->len--;
13254     }
13255 
13256 
13257     pc->tline = pc->linenr;
13258     pc->tstart = pc->p;
13259 
13260     if (pc->len == 0) {
13261         pc->tend = pc->p;
13262         pc->tt = JIM_TT_EOL;
13263         pc->eof = 1;
13264         return JIM_OK;
13265     }
13266     switch (*(pc->p)) {
13267         case '(':
13268                 pc->tt = JIM_TT_SUBEXPR_START;
13269                 goto singlechar;
13270         case ')':
13271                 pc->tt = JIM_TT_SUBEXPR_END;
13272                 goto singlechar;
13273         case ',':
13274             pc->tt = JIM_TT_SUBEXPR_COMMA;
13275 singlechar:
13276             pc->tend = pc->p;
13277             pc->p++;
13278             pc->len--;
13279             break;
13280         case '[':
13281             return JimParseCmd(pc);
13282         case '$':
13283             if (JimParseVar(pc) == JIM_ERR)
13284                 return JimParseExprOperator(pc);
13285             else {
13286 
13287                 if (pc->tt == JIM_TT_EXPRSUGAR) {
13288                     return JIM_ERR;
13289                 }
13290                 return JIM_OK;
13291             }
13292             break;
13293         case '0':
13294         case '1':
13295         case '2':
13296         case '3':
13297         case '4':
13298         case '5':
13299         case '6':
13300         case '7':
13301         case '8':
13302         case '9':
13303         case '.':
13304             return JimParseExprNumber(pc);
13305         case '"':
13306             return JimParseQuote(pc);
13307         case '{':
13308             return JimParseBrace(pc);
13309 
13310         case 'N':
13311         case 'I':
13312         case 'n':
13313         case 'i':
13314             if (JimParseExprIrrational(pc) == JIM_ERR)
13315                 if (JimParseExprBoolean(pc) == JIM_ERR)
13316                     return JimParseExprOperator(pc);
13317             break;
13318         case 't':
13319         case 'f':
13320         case 'o':
13321         case 'y':
13322             if (JimParseExprBoolean(pc) == JIM_ERR)
13323                 return JimParseExprOperator(pc);
13324             break;
13325         default:
13326             return JimParseExprOperator(pc);
13327             break;
13328     }
13329     return JIM_OK;
13330 }
13331 
JimParseExprNumber(struct JimParserCtx * pc)13332 static int JimParseExprNumber(struct JimParserCtx *pc)
13333 {
13334     char *end;
13335 
13336 
13337     pc->tt = JIM_TT_EXPR_INT;
13338 
13339     jim_strtoull(pc->p, (char **)&pc->p);
13340 
13341     if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
13342         if (strtod(pc->tstart, &end)) { }
13343         if (end == pc->tstart)
13344             return JIM_ERR;
13345         if (end > pc->p) {
13346 
13347             pc->tt = JIM_TT_EXPR_DOUBLE;
13348             pc->p = end;
13349         }
13350     }
13351     pc->tend = pc->p - 1;
13352     pc->len -= (pc->p - pc->tstart);
13353     return JIM_OK;
13354 }
13355 
JimParseExprIrrational(struct JimParserCtx * pc)13356 static int JimParseExprIrrational(struct JimParserCtx *pc)
13357 {
13358     const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
13359     int i;
13360 
13361     for (i = 0; irrationals[i]; i++) {
13362         const char *irr = irrationals[i];
13363 
13364         if (strncmp(irr, pc->p, 3) == 0) {
13365             pc->p += 3;
13366             pc->len -= 3;
13367             pc->tend = pc->p - 1;
13368             pc->tt = JIM_TT_EXPR_DOUBLE;
13369             return JIM_OK;
13370         }
13371     }
13372     return JIM_ERR;
13373 }
13374 
JimParseExprBoolean(struct JimParserCtx * pc)13375 static int JimParseExprBoolean(struct JimParserCtx *pc)
13376 {
13377     const char *booleans[] = { "false", "no", "off", "true", "yes", "on", NULL };
13378     const int lengths[] = { 5, 2, 3, 4, 3, 2, 0 };
13379     int i;
13380 
13381     for (i = 0; booleans[i]; i++) {
13382         const char *boolean = booleans[i];
13383         int length = lengths[i];
13384 
13385         if (strncmp(boolean, pc->p, length) == 0) {
13386             pc->p += length;
13387             pc->len -= length;
13388             pc->tend = pc->p - 1;
13389             pc->tt = JIM_TT_EXPR_BOOLEAN;
13390             return JIM_OK;
13391         }
13392     }
13393     return JIM_ERR;
13394 }
13395 
JimExprOperatorInfoByOpcode(int opcode)13396 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
13397 {
13398     static Jim_ExprOperator dummy_op;
13399     if (opcode < JIM_TT_EXPR_OP) {
13400         return &dummy_op;
13401     }
13402     return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
13403 }
13404 
JimParseExprOperator(struct JimParserCtx * pc)13405 static int JimParseExprOperator(struct JimParserCtx *pc)
13406 {
13407     int i;
13408     const struct Jim_ExprOperator *bestOp = NULL;
13409     int bestLen = 0;
13410 
13411 
13412     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
13413         const struct Jim_ExprOperator *op = &Jim_ExprOperators[i];
13414 
13415         if (op->name[0] != pc->p[0]) {
13416             continue;
13417         }
13418 
13419         if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) {
13420             bestOp = op;
13421             bestLen = op->namelen;
13422         }
13423     }
13424     if (bestOp == NULL) {
13425         return JIM_ERR;
13426     }
13427 
13428 
13429     if (bestOp->attr & OP_FUNC) {
13430         const char *p = pc->p + bestLen;
13431         int len = pc->len - bestLen;
13432 
13433         while (len && isspace(UCHAR(*p))) {
13434             len--;
13435             p++;
13436         }
13437         if (*p != '(') {
13438             return JIM_ERR;
13439         }
13440     }
13441     pc->tend = pc->p + bestLen - 1;
13442     pc->p += bestLen;
13443     pc->len -= bestLen;
13444 
13445     pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
13446     return JIM_OK;
13447 }
13448 
jim_tt_name(int type)13449 const char *jim_tt_name(int type)
13450 {
13451     static const char * const tt_names[JIM_TT_EXPR_OP] =
13452         { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT",
13453             "DBL", "BOO", "$()" };
13454     if (type < JIM_TT_EXPR_OP) {
13455         return tt_names[type];
13456     }
13457     else if (type == JIM_EXPROP_UNARYMINUS) {
13458         return "-VE";
13459     }
13460     else if (type == JIM_EXPROP_UNARYPLUS) {
13461         return "+VE";
13462     }
13463     else {
13464         const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(type);
13465         static char buf[20];
13466 
13467         if (op->name) {
13468             return op->name;
13469         }
13470         sprintf(buf, "(%d)", type);
13471         return buf;
13472     }
13473 }
13474 
13475 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
13476 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
13477 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13478 
13479 static const Jim_ObjType exprObjType = {
13480     "expression",
13481     FreeExprInternalRep,
13482     DupExprInternalRep,
13483     NULL,
13484     JIM_TYPE_REFERENCES,
13485 };
13486 
13487 
13488 struct ExprTree
13489 {
13490     struct JimExprNode *expr;
13491     struct JimExprNode *nodes;
13492     int len;
13493     int inUse;
13494 };
13495 
ExprTreeFreeNodes(Jim_Interp * interp,struct JimExprNode * nodes,int num)13496 static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num)
13497 {
13498     int i;
13499     for (i = 0; i < num; i++) {
13500         if (nodes[i].objPtr) {
13501             Jim_DecrRefCount(interp, nodes[i].objPtr);
13502         }
13503     }
13504     Jim_Free(nodes);
13505 }
13506 
ExprTreeFree(Jim_Interp * interp,struct ExprTree * expr)13507 static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr)
13508 {
13509     ExprTreeFreeNodes(interp, expr->nodes, expr->len);
13510     Jim_Free(expr);
13511 }
13512 
FreeExprInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)13513 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
13514 {
13515     struct ExprTree *expr = (void *)objPtr->internalRep.ptr;
13516 
13517     if (expr) {
13518         if (--expr->inUse != 0) {
13519             return;
13520         }
13521 
13522         ExprTreeFree(interp, expr);
13523     }
13524 }
13525 
DupExprInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)13526 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
13527 {
13528     JIM_NOTUSED(interp);
13529     JIM_NOTUSED(srcPtr);
13530 
13531 
13532     dupPtr->typePtr = NULL;
13533 }
13534 
13535 struct ExprBuilder {
13536     int parencount;
13537     int level;
13538     ParseToken *token;
13539     ParseToken *first_token;
13540     Jim_Stack stack;
13541     Jim_Obj *exprObjPtr;
13542     Jim_Obj *fileNameObj;
13543     struct JimExprNode *nodes;
13544     struct JimExprNode *next;
13545 };
13546 
13547 #ifdef DEBUG_SHOW_EXPR
JimShowExprNode(struct JimExprNode * node,int level)13548 static void JimShowExprNode(struct JimExprNode *node, int level)
13549 {
13550     int i;
13551     for (i = 0; i < level; i++) {
13552         printf("  ");
13553     }
13554     if (TOKEN_IS_EXPR_OP(node->type)) {
13555         printf("%s\n", jim_tt_name(node->type));
13556         if (node->left) {
13557             JimShowExprNode(node->left, level + 1);
13558         }
13559         if (node->right) {
13560             JimShowExprNode(node->right, level + 1);
13561         }
13562         if (node->ternary) {
13563             JimShowExprNode(node->ternary, level + 1);
13564         }
13565     }
13566     else {
13567         printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr));
13568     }
13569 }
13570 #endif
13571 
13572 #define EXPR_UNTIL_CLOSE 0x0001
13573 #define EXPR_FUNC_ARGS   0x0002
13574 #define EXPR_TERNARY     0x0004
13575 
ExprTreeBuildTree(Jim_Interp * interp,struct ExprBuilder * builder,int precedence,int flags,int exp_numterms)13576 static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms)
13577 {
13578     int rc;
13579     struct JimExprNode *node;
13580 
13581     int exp_stacklen = builder->stack.len + exp_numterms;
13582 
13583     if (builder->level++ > 200) {
13584         Jim_SetResultString(interp, "Expression too complex", -1);
13585         return JIM_ERR;
13586     }
13587 
13588     while (builder->token->type != JIM_TT_EOL) {
13589         ParseToken *t = builder->token++;
13590         int prevtt;
13591 
13592         if (t == builder->first_token) {
13593             prevtt = JIM_TT_NONE;
13594         }
13595         else {
13596             prevtt = t[-1].type;
13597         }
13598 
13599         if (t->type == JIM_TT_SUBEXPR_START) {
13600             if (builder->stack.len == exp_stacklen) {
13601                 Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr);
13602                 return JIM_ERR;
13603             }
13604             builder->parencount++;
13605             rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1);
13606             if (rc != JIM_OK) {
13607                 return rc;
13608             }
13609 
13610         }
13611         else if (t->type == JIM_TT_SUBEXPR_END) {
13612             if (!(flags & EXPR_UNTIL_CLOSE)) {
13613                 if (builder->stack.len == exp_stacklen && builder->level > 1) {
13614                     builder->token--;
13615                     builder->level--;
13616                     return JIM_OK;
13617                 }
13618                 Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr);
13619                 return JIM_ERR;
13620             }
13621             builder->parencount--;
13622             if (builder->stack.len == exp_stacklen) {
13623 
13624                 break;
13625             }
13626         }
13627         else if (t->type == JIM_TT_SUBEXPR_COMMA) {
13628             if (!(flags & EXPR_FUNC_ARGS)) {
13629                 if (builder->stack.len == exp_stacklen) {
13630 
13631                     builder->token--;
13632                     builder->level--;
13633                     return JIM_OK;
13634                 }
13635                 Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr);
13636                 return JIM_ERR;
13637             }
13638             else {
13639 
13640                 if (builder->stack.len > exp_stacklen) {
13641                     Jim_SetResultFormatted(interp, "too many arguments to math function");
13642                     return JIM_ERR;
13643                 }
13644             }
13645 
13646         }
13647         else if (t->type == JIM_EXPROP_COLON) {
13648             if (!(flags & EXPR_TERNARY)) {
13649                 if (builder->level != 1) {
13650 
13651                     builder->token--;
13652                     builder->level--;
13653                     return JIM_OK;
13654                 }
13655                 Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr);
13656                 return JIM_ERR;
13657             }
13658             if (builder->stack.len == exp_stacklen) {
13659 
13660                 builder->token--;
13661                 builder->level--;
13662                 return JIM_OK;
13663             }
13664 
13665         }
13666         else if (TOKEN_IS_EXPR_OP(t->type)) {
13667             const struct Jim_ExprOperator *op;
13668 
13669 
13670             if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) {
13671                 if (t->type == JIM_EXPROP_SUB) {
13672                     t->type = JIM_EXPROP_UNARYMINUS;
13673                 }
13674                 else if (t->type == JIM_EXPROP_ADD) {
13675                     t->type = JIM_EXPROP_UNARYPLUS;
13676                 }
13677             }
13678 
13679             op = JimExprOperatorInfoByOpcode(t->type);
13680 
13681             if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) {
13682 
13683                 builder->token--;
13684                 break;
13685             }
13686 
13687             if (op->attr & OP_FUNC) {
13688                 if (builder->token->type != JIM_TT_SUBEXPR_START) {
13689                     Jim_SetResultString(interp, "missing arguments for math function", -1);
13690                     return JIM_ERR;
13691                 }
13692                 builder->token++;
13693                 if (op->arity == 0) {
13694                     if (builder->token->type != JIM_TT_SUBEXPR_END) {
13695                         Jim_SetResultString(interp, "too many arguments for math function", -1);
13696                         return JIM_ERR;
13697                     }
13698                     builder->token++;
13699                     goto noargs;
13700                 }
13701                 builder->parencount++;
13702 
13703 
13704                 rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity);
13705             }
13706             else if (t->type == JIM_EXPROP_TERNARY) {
13707 
13708                 rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2);
13709             }
13710             else {
13711                 rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1);
13712             }
13713 
13714             if (rc != JIM_OK) {
13715                 return rc;
13716             }
13717 
13718 noargs:
13719             node = builder->next++;
13720             node->type = t->type;
13721 
13722             if (op->arity >= 3) {
13723                 node->ternary = Jim_StackPop(&builder->stack);
13724                 if (node->ternary == NULL) {
13725                     goto missingoperand;
13726                 }
13727             }
13728             if (op->arity >= 2) {
13729                 node->right = Jim_StackPop(&builder->stack);
13730                 if (node->right == NULL) {
13731                     goto missingoperand;
13732                 }
13733             }
13734             if (op->arity >= 1) {
13735                 node->left = Jim_StackPop(&builder->stack);
13736                 if (node->left == NULL) {
13737 missingoperand:
13738                     Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr);
13739                     builder->next--;
13740                     return JIM_ERR;
13741 
13742                 }
13743             }
13744 
13745 
13746             Jim_StackPush(&builder->stack, node);
13747         }
13748         else {
13749             Jim_Obj *objPtr = NULL;
13750 
13751 
13752 
13753 
13754             if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
13755                 Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr);
13756                 return JIM_ERR;
13757             }
13758 
13759 
13760             if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
13761                 char *endptr;
13762                 if (t->type == JIM_TT_EXPR_INT) {
13763                     objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
13764                 }
13765                 else {
13766                     objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
13767                 }
13768                 if (endptr != t->token + t->len) {
13769 
13770                     Jim_FreeNewObj(interp, objPtr);
13771                     objPtr = NULL;
13772                 }
13773             }
13774 
13775             if (!objPtr) {
13776 
13777                 objPtr = Jim_NewStringObj(interp, t->token, t->len);
13778                 if (t->type == JIM_TT_CMD) {
13779 
13780                     JimSetSourceInfo(interp, objPtr, builder->fileNameObj, t->line);
13781                 }
13782             }
13783 
13784 
13785             node = builder->next++;
13786             node->objPtr = objPtr;
13787             Jim_IncrRefCount(node->objPtr);
13788             node->type = t->type;
13789             Jim_StackPush(&builder->stack, node);
13790         }
13791     }
13792 
13793     if (builder->stack.len == exp_stacklen) {
13794         builder->level--;
13795         return JIM_OK;
13796     }
13797 
13798     if ((flags & EXPR_FUNC_ARGS)) {
13799         Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many");
13800     }
13801     else {
13802         if (builder->stack.len < exp_stacklen) {
13803             if (builder->level == 0) {
13804                 Jim_SetResultFormatted(interp, "empty expression");
13805             }
13806             else {
13807                 Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr);
13808             }
13809         }
13810         else {
13811             Jim_SetResultFormatted(interp, "extra terms after expression");
13812         }
13813     }
13814 
13815     return JIM_ERR;
13816 }
13817 
ExprTreeCreateTree(Jim_Interp * interp,const ParseTokenList * tokenlist,Jim_Obj * exprObjPtr,Jim_Obj * fileNameObj)13818 static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
13819 {
13820     struct ExprTree *expr;
13821     struct ExprBuilder builder;
13822     int rc;
13823     struct JimExprNode *top = NULL;
13824 
13825     builder.parencount = 0;
13826     builder.level = 0;
13827     builder.token = builder.first_token = tokenlist->list;
13828     builder.exprObjPtr = exprObjPtr;
13829     builder.fileNameObj = fileNameObj;
13830 
13831     builder.nodes = malloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
13832     memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
13833     builder.next = builder.nodes;
13834     Jim_InitStack(&builder.stack);
13835 
13836     rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);
13837 
13838     if (rc == JIM_OK) {
13839         top = Jim_StackPop(&builder.stack);
13840 
13841         if (builder.parencount) {
13842             Jim_SetResultString(interp, "missing close parenthesis", -1);
13843             rc = JIM_ERR;
13844         }
13845     }
13846 
13847 
13848     Jim_FreeStack(&builder.stack);
13849 
13850     if (rc != JIM_OK) {
13851         ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes);
13852         return NULL;
13853     }
13854 
13855     expr = Jim_Alloc(sizeof(*expr));
13856     expr->inUse = 1;
13857     expr->expr = top;
13858     expr->nodes = builder.nodes;
13859     expr->len = builder.next - builder.nodes;
13860 
13861     assert(expr->len <= tokenlist->count - 1);
13862 
13863     return expr;
13864 }
13865 
SetExprFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)13866 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
13867 {
13868     int exprTextLen;
13869     const char *exprText;
13870     struct JimParserCtx parser;
13871     struct ExprTree *expr;
13872     ParseTokenList tokenlist;
13873     int line;
13874     Jim_Obj *fileNameObj;
13875     int rc = JIM_ERR;
13876 
13877 
13878     if (objPtr->typePtr == &sourceObjType) {
13879         fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
13880         line = objPtr->internalRep.sourceValue.lineNumber;
13881     }
13882     else {
13883         fileNameObj = interp->emptyObj;
13884         line = 1;
13885     }
13886     Jim_IncrRefCount(fileNameObj);
13887 
13888     exprText = Jim_GetString(objPtr, &exprTextLen);
13889 
13890 
13891     ScriptTokenListInit(&tokenlist);
13892 
13893     JimParserInit(&parser, exprText, exprTextLen, line);
13894     while (!parser.eof) {
13895         if (JimParseExpression(&parser) != JIM_OK) {
13896             ScriptTokenListFree(&tokenlist);
13897             Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
13898             expr = NULL;
13899             goto err;
13900         }
13901 
13902         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
13903             parser.tline);
13904     }
13905 
13906 #ifdef DEBUG_SHOW_EXPR_TOKENS
13907     {
13908         int i;
13909         printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
13910         for (i = 0; i < tokenlist.count; i++) {
13911             printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
13912                 tokenlist.list[i].len, tokenlist.list[i].token);
13913         }
13914     }
13915 #endif
13916 
13917     if (JimParseCheckMissing(interp, parser.missing.ch) == JIM_ERR) {
13918         ScriptTokenListFree(&tokenlist);
13919         Jim_DecrRefCount(interp, fileNameObj);
13920         return JIM_ERR;
13921     }
13922 
13923 
13924     expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj);
13925 
13926 
13927     ScriptTokenListFree(&tokenlist);
13928 
13929     if (!expr) {
13930         goto err;
13931     }
13932 
13933 #ifdef DEBUG_SHOW_EXPR
13934     printf("==== Expr ====\n");
13935     JimShowExprNode(expr->expr, 0);
13936 #endif
13937 
13938     rc = JIM_OK;
13939 
13940   err:
13941 
13942     Jim_DecrRefCount(interp, fileNameObj);
13943     Jim_FreeIntRep(interp, objPtr);
13944     Jim_SetIntRepPtr(objPtr, expr);
13945     objPtr->typePtr = &exprObjType;
13946     return rc;
13947 }
13948 
JimGetExpression(Jim_Interp * interp,Jim_Obj * objPtr)13949 static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
13950 {
13951     if (objPtr->typePtr != &exprObjType) {
13952         if (SetExprFromAny(interp, objPtr) != JIM_OK) {
13953             return NULL;
13954         }
13955     }
13956     return (struct ExprTree *) Jim_GetIntRepPtr(objPtr);
13957 }
13958 
13959 #ifdef JIM_OPTIMIZATION
JimExprIntValOrVar(Jim_Interp * interp,struct JimExprNode * node)13960 static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node)
13961 {
13962     if (node->type == JIM_TT_EXPR_INT)
13963         return node->objPtr;
13964     else if (node->type == JIM_TT_VAR)
13965         return Jim_GetVariable(interp, node->objPtr, JIM_NONE);
13966     else if (node->type == JIM_TT_DICTSUGAR)
13967         return JimExpandDictSugar(interp, node->objPtr);
13968     else
13969         return NULL;
13970 }
13971 #endif
13972 
13973 
JimExprEvalTermNode(Jim_Interp * interp,struct JimExprNode * node)13974 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node)
13975 {
13976     if (TOKEN_IS_EXPR_OP(node->type)) {
13977         const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type);
13978         return op->funcop(interp, node);
13979     }
13980     else {
13981         Jim_Obj *objPtr;
13982 
13983 
13984         switch (node->type) {
13985             case JIM_TT_EXPR_INT:
13986             case JIM_TT_EXPR_DOUBLE:
13987             case JIM_TT_EXPR_BOOLEAN:
13988             case JIM_TT_STR:
13989                 Jim_SetResult(interp, node->objPtr);
13990                 return JIM_OK;
13991 
13992             case JIM_TT_VAR:
13993                 objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG);
13994                 if (objPtr) {
13995                     Jim_SetResult(interp, objPtr);
13996                     return JIM_OK;
13997                 }
13998                 return JIM_ERR;
13999 
14000             case JIM_TT_DICTSUGAR:
14001                 objPtr = JimExpandDictSugar(interp, node->objPtr);
14002                 if (objPtr) {
14003                     Jim_SetResult(interp, objPtr);
14004                     return JIM_OK;
14005                 }
14006                 return JIM_ERR;
14007 
14008             case JIM_TT_ESC:
14009                 if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
14010                     Jim_SetResult(interp, objPtr);
14011                     return JIM_OK;
14012                 }
14013                 return JIM_ERR;
14014 
14015             case JIM_TT_CMD:
14016                 return Jim_EvalObj(interp, node->objPtr);
14017 
14018             default:
14019 
14020                 return JIM_ERR;
14021         }
14022     }
14023 }
14024 
JimExprGetTerm(Jim_Interp * interp,struct JimExprNode * node,Jim_Obj ** objPtrPtr)14025 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr)
14026 {
14027     int rc = JimExprEvalTermNode(interp, node);
14028     if (rc == JIM_OK) {
14029         *objPtrPtr = Jim_GetResult(interp);
14030         Jim_IncrRefCount(*objPtrPtr);
14031     }
14032     return rc;
14033 }
14034 
JimExprGetTermBoolean(Jim_Interp * interp,struct JimExprNode * node)14035 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node)
14036 {
14037     if (JimExprEvalTermNode(interp, node) == JIM_OK) {
14038         return ExprBool(interp, Jim_GetResult(interp));
14039     }
14040     return -1;
14041 }
14042 
Jim_EvalExpression(Jim_Interp * interp,Jim_Obj * exprObjPtr)14043 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
14044 {
14045     struct ExprTree *expr;
14046     int retcode = JIM_OK;
14047 
14048     expr = JimGetExpression(interp, exprObjPtr);
14049     if (!expr) {
14050         return JIM_ERR;
14051     }
14052 
14053 #ifdef JIM_OPTIMIZATION
14054     {
14055         Jim_Obj *objPtr;
14056 
14057 
14058         switch (expr->len) {
14059             case 1:
14060                 objPtr = JimExprIntValOrVar(interp, expr->expr);
14061                 if (objPtr) {
14062                     Jim_SetResult(interp, objPtr);
14063                     return JIM_OK;
14064                 }
14065                 break;
14066 
14067             case 2:
14068                 if (expr->expr->type == JIM_EXPROP_NOT) {
14069                     objPtr = JimExprIntValOrVar(interp, expr->expr->left);
14070 
14071                     if (objPtr && JimIsWide(objPtr)) {
14072                         Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj);
14073                         return JIM_OK;
14074                     }
14075                 }
14076                 break;
14077 
14078             case 3:
14079                 objPtr = JimExprIntValOrVar(interp, expr->expr->left);
14080                 if (objPtr && JimIsWide(objPtr)) {
14081                     Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right);
14082                     if (objPtr2 && JimIsWide(objPtr2)) {
14083                         jim_wide wideValueA = JimWideValue(objPtr);
14084                         jim_wide wideValueB = JimWideValue(objPtr2);
14085                         int cmpRes;
14086                         switch (expr->expr->type) {
14087                             case JIM_EXPROP_LT:
14088                                 cmpRes = wideValueA < wideValueB;
14089                                 break;
14090                             case JIM_EXPROP_LTE:
14091                                 cmpRes = wideValueA <= wideValueB;
14092                                 break;
14093                             case JIM_EXPROP_GT:
14094                                 cmpRes = wideValueA > wideValueB;
14095                                 break;
14096                             case JIM_EXPROP_GTE:
14097                                 cmpRes = wideValueA >= wideValueB;
14098                                 break;
14099                             case JIM_EXPROP_NUMEQ:
14100                                 cmpRes = wideValueA == wideValueB;
14101                                 break;
14102                             case JIM_EXPROP_NUMNE:
14103                                 cmpRes = wideValueA != wideValueB;
14104                                 break;
14105                             default:
14106                                 goto noopt;
14107                         }
14108                         Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);
14109                         return JIM_OK;
14110                     }
14111                 }
14112                 break;
14113         }
14114     }
14115 noopt:
14116 #endif
14117 
14118     expr->inUse++;
14119 
14120 
14121     retcode = JimExprEvalTermNode(interp, expr->expr);
14122 
14123     expr->inUse--;
14124 
14125     return retcode;
14126 }
14127 
Jim_GetBoolFromExpr(Jim_Interp * interp,Jim_Obj * exprObjPtr,int * boolPtr)14128 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
14129 {
14130     int retcode = Jim_EvalExpression(interp, exprObjPtr);
14131 
14132     if (retcode == JIM_OK) {
14133         switch (ExprBool(interp, Jim_GetResult(interp))) {
14134             case 0:
14135                 *boolPtr = 0;
14136                 break;
14137 
14138             case 1:
14139                 *boolPtr = 1;
14140                 break;
14141 
14142             case -1:
14143                 retcode = JIM_ERR;
14144                 break;
14145         }
14146     }
14147     return retcode;
14148 }
14149 
14150 
14151 
14152 
14153 typedef struct ScanFmtPartDescr
14154 {
14155     const char *arg;
14156     const char *prefix;
14157     size_t width;
14158     int pos;
14159     char type;
14160     char modifier;
14161 } ScanFmtPartDescr;
14162 
14163 
14164 typedef struct ScanFmtStringObj
14165 {
14166     jim_wide size;
14167     char *stringRep;
14168     size_t count;
14169     size_t convCount;
14170     size_t maxPos;
14171     const char *error;
14172     char *scratch;
14173     ScanFmtPartDescr descr[1];
14174 } ScanFmtStringObj;
14175 
14176 
14177 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
14178 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
14179 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
14180 
14181 static const Jim_ObjType scanFmtStringObjType = {
14182     "scanformatstring",
14183     FreeScanFmtInternalRep,
14184     DupScanFmtInternalRep,
14185     UpdateStringOfScanFmt,
14186     JIM_TYPE_NONE,
14187 };
14188 
FreeScanFmtInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)14189 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
14190 {
14191     JIM_NOTUSED(interp);
14192     Jim_Free((char *)objPtr->internalRep.ptr);
14193     objPtr->internalRep.ptr = 0;
14194 }
14195 
DupScanFmtInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)14196 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
14197 {
14198     size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
14199     ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
14200 
14201     JIM_NOTUSED(interp);
14202     memcpy(newVec, srcPtr->internalRep.ptr, size);
14203     dupPtr->internalRep.ptr = newVec;
14204     dupPtr->typePtr = &scanFmtStringObjType;
14205 }
14206 
UpdateStringOfScanFmt(Jim_Obj * objPtr)14207 static void UpdateStringOfScanFmt(Jim_Obj *objPtr)
14208 {
14209     JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep);
14210 }
14211 
14212 
SetScanFmtFromAny(Jim_Interp * interp,Jim_Obj * objPtr)14213 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
14214 {
14215     ScanFmtStringObj *fmtObj;
14216     char *buffer;
14217     int maxCount, i, approxSize, lastPos = -1;
14218     const char *fmt = Jim_String(objPtr);
14219     int maxFmtLen = Jim_Length(objPtr);
14220     const char *fmtEnd = fmt + maxFmtLen;
14221     int curr;
14222 
14223     Jim_FreeIntRep(interp, objPtr);
14224 
14225     for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
14226         if (fmt[i] == '%')
14227             ++maxCount;
14228 
14229     approxSize = sizeof(ScanFmtStringObj)
14230         +(maxCount + 1) * sizeof(ScanFmtPartDescr)
14231         +maxFmtLen * sizeof(char) + 3 + 1
14232         + maxFmtLen * sizeof(char) + 1
14233         + maxFmtLen * sizeof(char)
14234         +(maxCount + 1) * sizeof(char)
14235         +1;
14236     fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
14237     memset(fmtObj, 0, approxSize);
14238     fmtObj->size = approxSize;
14239     fmtObj->maxPos = 0;
14240     fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
14241     fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
14242     memcpy(fmtObj->stringRep, fmt, maxFmtLen);
14243     buffer = fmtObj->stringRep + maxFmtLen + 1;
14244     objPtr->internalRep.ptr = fmtObj;
14245     objPtr->typePtr = &scanFmtStringObjType;
14246     for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
14247         int width = 0, skip;
14248         ScanFmtPartDescr *descr = &fmtObj->descr[curr];
14249 
14250         fmtObj->count++;
14251         descr->width = 0;
14252 
14253         if (*fmt != '%' || fmt[1] == '%') {
14254             descr->type = 0;
14255             descr->prefix = &buffer[i];
14256             for (; fmt < fmtEnd; ++fmt) {
14257                 if (*fmt == '%') {
14258                     if (fmt[1] != '%')
14259                         break;
14260                     ++fmt;
14261                 }
14262                 buffer[i++] = *fmt;
14263             }
14264             buffer[i++] = 0;
14265         }
14266 
14267         ++fmt;
14268 
14269         if (fmt >= fmtEnd)
14270             goto done;
14271         descr->pos = 0;
14272         if (*fmt == '*') {
14273             descr->pos = -1;
14274             ++fmt;
14275         }
14276         else
14277             fmtObj->convCount++;
14278 
14279         if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14280             fmt += skip;
14281 
14282             if (descr->pos != -1 && *fmt == '$') {
14283                 int prev;
14284 
14285                 ++fmt;
14286                 descr->pos = width;
14287                 width = 0;
14288 
14289                 if ((lastPos == 0 && descr->pos > 0)
14290                     || (lastPos > 0 && descr->pos == 0)) {
14291                     fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
14292                     return JIM_ERR;
14293                 }
14294 
14295                 for (prev = 0; prev < curr; ++prev) {
14296                     if (fmtObj->descr[prev].pos == -1)
14297                         continue;
14298                     if (fmtObj->descr[prev].pos == descr->pos) {
14299                         fmtObj->error =
14300                             "variable is assigned by multiple \"%n$\" conversion specifiers";
14301                         return JIM_ERR;
14302                     }
14303                 }
14304                 if (descr->pos < 0) {
14305                     fmtObj->error =
14306                         "\"%n$\" conversion specifier is negative";
14307                     return JIM_ERR;
14308                 }
14309 
14310                 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14311                     descr->width = width;
14312                     fmt += skip;
14313                 }
14314                 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
14315                     fmtObj->maxPos = descr->pos;
14316             }
14317             else {
14318 
14319                 descr->width = width;
14320             }
14321         }
14322 
14323         if (lastPos == -1)
14324             lastPos = descr->pos;
14325 
14326         if (*fmt == '[') {
14327             int swapped = 1, beg = i, end, j;
14328 
14329             descr->type = '[';
14330             descr->arg = &buffer[i];
14331             ++fmt;
14332             if (*fmt == '^')
14333                 buffer[i++] = *fmt++;
14334             if (*fmt == ']')
14335                 buffer[i++] = *fmt++;
14336             while (*fmt && *fmt != ']')
14337                 buffer[i++] = *fmt++;
14338             if (*fmt != ']') {
14339                 fmtObj->error = "unmatched [ in format string";
14340                 return JIM_ERR;
14341             }
14342             end = i;
14343             buffer[i++] = 0;
14344 
14345             while (swapped) {
14346                 swapped = 0;
14347                 for (j = beg + 1; j < end - 1; ++j) {
14348                     if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
14349                         char tmp = buffer[j - 1];
14350 
14351                         buffer[j - 1] = buffer[j + 1];
14352                         buffer[j + 1] = tmp;
14353                         swapped = 1;
14354                     }
14355                 }
14356             }
14357         }
14358         else {
14359 
14360             if (fmt < fmtEnd && strchr("hlL", *fmt))
14361                 descr->modifier = tolower((int)*fmt++);
14362 
14363             if (fmt >= fmtEnd) {
14364                 fmtObj->error = "missing scan conversion character";
14365                 return JIM_ERR;
14366             }
14367 
14368             descr->type = *fmt;
14369             if (strchr("efgcsndoxui", *fmt) == 0) {
14370                 fmtObj->error = "bad scan conversion character";
14371                 return JIM_ERR;
14372             }
14373             else if (*fmt == 'c' && descr->width != 0) {
14374                 fmtObj->error = "field width may not be specified in %c " "conversion";
14375                 return JIM_ERR;
14376             }
14377             else if (*fmt == 'u' && descr->modifier == 'l') {
14378                 fmtObj->error = "unsigned wide not supported";
14379                 return JIM_ERR;
14380             }
14381         }
14382         curr++;
14383     }
14384   done:
14385     return JIM_OK;
14386 }
14387 
14388 
14389 
14390 #define FormatGetCnvCount(_fo_) \
14391     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
14392 #define FormatGetMaxPos(_fo_) \
14393     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
14394 #define FormatGetError(_fo_) \
14395     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
14396 
JimScanAString(Jim_Interp * interp,const char * sdescr,const char * str)14397 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
14398 {
14399     char *buffer = Jim_StrDup(str);
14400     char *p = buffer;
14401 
14402     while (*str) {
14403         int c;
14404         int n;
14405 
14406         if (!sdescr && isspace(UCHAR(*str)))
14407             break;
14408 
14409         n = utf8_tounicode(str, &c);
14410         if (sdescr && !JimCharsetMatch(sdescr, c, JIM_CHARSET_SCAN))
14411             break;
14412         while (n--)
14413             *p++ = *str++;
14414     }
14415     *p = 0;
14416     return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
14417 }
14418 
14419 
ScanOneEntry(Jim_Interp * interp,const char * str,int pos,int strLen,ScanFmtStringObj * fmtObj,long idx,Jim_Obj ** valObjPtr)14420 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int strLen,
14421     ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
14422 {
14423     const char *tok;
14424     const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
14425     size_t scanned = 0;
14426     size_t anchor = pos;
14427     int i;
14428     Jim_Obj *tmpObj = NULL;
14429 
14430 
14431     *valObjPtr = 0;
14432     if (descr->prefix) {
14433         for (i = 0; pos < strLen && descr->prefix[i]; ++i) {
14434 
14435             if (isspace(UCHAR(descr->prefix[i])))
14436                 while (pos < strLen && isspace(UCHAR(str[pos])))
14437                     ++pos;
14438             else if (descr->prefix[i] != str[pos])
14439                 break;
14440             else
14441                 ++pos;
14442         }
14443         if (pos >= strLen) {
14444             return -1;
14445         }
14446         else if (descr->prefix[i] != 0)
14447             return 0;
14448     }
14449 
14450     if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
14451         while (isspace(UCHAR(str[pos])))
14452             ++pos;
14453 
14454     scanned = pos - anchor;
14455 
14456 
14457     if (descr->type == 'n') {
14458 
14459         *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
14460     }
14461     else if (pos >= strLen) {
14462 
14463         return -1;
14464     }
14465     else if (descr->type == 'c') {
14466             int c;
14467             scanned += utf8_tounicode(&str[pos], &c);
14468             *valObjPtr = Jim_NewIntObj(interp, c);
14469             return scanned;
14470     }
14471     else {
14472 
14473         if (descr->width > 0) {
14474             size_t sLen = utf8_strlen(&str[pos], strLen - pos);
14475             size_t tLen = descr->width > sLen ? sLen : descr->width;
14476 
14477             tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
14478             tok = tmpObj->bytes;
14479         }
14480         else {
14481 
14482             tok = &str[pos];
14483         }
14484         switch (descr->type) {
14485             case 'd':
14486             case 'o':
14487             case 'x':
14488             case 'u':
14489             case 'i':{
14490                     char *endp;
14491                     jim_wide w;
14492 
14493                     int base = descr->type == 'o' ? 8
14494                         : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
14495 
14496 
14497                     if (base == 0) {
14498                         w = jim_strtoull(tok, &endp);
14499                     }
14500                     else {
14501                         w = strtoull(tok, &endp, base);
14502                     }
14503 
14504                     if (endp != tok) {
14505 
14506                         *valObjPtr = Jim_NewIntObj(interp, w);
14507 
14508 
14509                         scanned += endp - tok;
14510                     }
14511                     else {
14512                         scanned = *tok ? 0 : -1;
14513                     }
14514                     break;
14515                 }
14516             case 's':
14517             case '[':{
14518                     *valObjPtr = JimScanAString(interp, descr->arg, tok);
14519                     scanned += Jim_Length(*valObjPtr);
14520                     break;
14521                 }
14522             case 'e':
14523             case 'f':
14524             case 'g':{
14525                     char *endp;
14526                     double value = strtod(tok, &endp);
14527 
14528                     if (endp != tok) {
14529 
14530                         *valObjPtr = Jim_NewDoubleObj(interp, value);
14531 
14532                         scanned += endp - tok;
14533                     }
14534                     else {
14535                         scanned = *tok ? 0 : -1;
14536                     }
14537                     break;
14538                 }
14539         }
14540         if (tmpObj) {
14541             Jim_FreeNewObj(interp, tmpObj);
14542         }
14543     }
14544     return scanned;
14545 }
14546 
14547 
Jim_ScanString(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * fmtObjPtr,int flags)14548 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
14549 {
14550     size_t i, pos;
14551     int scanned = 1;
14552     const char *str = Jim_String(strObjPtr);
14553     int strLen = Jim_Utf8Length(interp, strObjPtr);
14554     Jim_Obj *resultList = 0;
14555     Jim_Obj **resultVec = 0;
14556     int resultc;
14557     Jim_Obj *emptyStr = 0;
14558     ScanFmtStringObj *fmtObj;
14559 
14560 
14561     JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
14562 
14563     fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
14564 
14565     if (fmtObj->error != 0) {
14566         if (flags & JIM_ERRMSG)
14567             Jim_SetResultString(interp, fmtObj->error, -1);
14568         return 0;
14569     }
14570 
14571     emptyStr = Jim_NewEmptyStringObj(interp);
14572     Jim_IncrRefCount(emptyStr);
14573 
14574     resultList = Jim_NewListObj(interp, NULL, 0);
14575     if (fmtObj->maxPos > 0) {
14576         for (i = 0; i < fmtObj->maxPos; ++i)
14577             Jim_ListAppendElement(interp, resultList, emptyStr);
14578         JimListGetElements(interp, resultList, &resultc, &resultVec);
14579     }
14580 
14581     for (i = 0, pos = 0; i < fmtObj->count; ++i) {
14582         ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
14583         Jim_Obj *value = 0;
14584 
14585 
14586         if (descr->type == 0)
14587             continue;
14588 
14589         if (scanned > 0)
14590             scanned = ScanOneEntry(interp, str, pos, strLen, fmtObj, i, &value);
14591 
14592         if (scanned == -1 && i == 0)
14593             goto eof;
14594 
14595         pos += scanned;
14596 
14597 
14598         if (value == 0)
14599             value = Jim_NewEmptyStringObj(interp);
14600 
14601         if (descr->pos == -1) {
14602             Jim_FreeNewObj(interp, value);
14603         }
14604         else if (descr->pos == 0)
14605 
14606             Jim_ListAppendElement(interp, resultList, value);
14607         else if (resultVec[descr->pos - 1] == emptyStr) {
14608 
14609             Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
14610             Jim_IncrRefCount(value);
14611             resultVec[descr->pos - 1] = value;
14612         }
14613         else {
14614 
14615             Jim_FreeNewObj(interp, value);
14616             goto err;
14617         }
14618     }
14619     Jim_DecrRefCount(interp, emptyStr);
14620     return resultList;
14621   eof:
14622     Jim_DecrRefCount(interp, emptyStr);
14623     Jim_FreeNewObj(interp, resultList);
14624     return (Jim_Obj *)EOF;
14625   err:
14626     Jim_DecrRefCount(interp, emptyStr);
14627     Jim_FreeNewObj(interp, resultList);
14628     return 0;
14629 }
14630 
14631 
JimPrngInit(Jim_Interp * interp)14632 static void JimPrngInit(Jim_Interp *interp)
14633 {
14634 #define PRNG_SEED_SIZE 256
14635     int i;
14636     unsigned int *seed;
14637     time_t t = time(NULL);
14638 
14639     interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
14640 
14641     seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
14642     for (i = 0; i < PRNG_SEED_SIZE; i++) {
14643         seed[i] = (rand() ^ t ^ clock());
14644     }
14645     JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
14646     Jim_Free(seed);
14647 }
14648 
14649 
JimRandomBytes(Jim_Interp * interp,void * dest,unsigned int len)14650 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
14651 {
14652     Jim_PrngState *prng;
14653     unsigned char *destByte = (unsigned char *)dest;
14654     unsigned int si, sj, x;
14655 
14656 
14657     if (interp->prngState == NULL)
14658         JimPrngInit(interp);
14659     prng = interp->prngState;
14660 
14661     for (x = 0; x < len; x++) {
14662         prng->i = (prng->i + 1) & 0xff;
14663         si = prng->sbox[prng->i];
14664         prng->j = (prng->j + si) & 0xff;
14665         sj = prng->sbox[prng->j];
14666         prng->sbox[prng->i] = sj;
14667         prng->sbox[prng->j] = si;
14668         *destByte++ = prng->sbox[(si + sj) & 0xff];
14669     }
14670 }
14671 
14672 
JimPrngSeed(Jim_Interp * interp,unsigned char * seed,int seedLen)14673 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
14674 {
14675     int i;
14676     Jim_PrngState *prng;
14677 
14678 
14679     if (interp->prngState == NULL)
14680         JimPrngInit(interp);
14681     prng = interp->prngState;
14682 
14683 
14684     for (i = 0; i < 256; i++)
14685         prng->sbox[i] = i;
14686 
14687     for (i = 0; i < seedLen; i++) {
14688         unsigned char t;
14689 
14690         t = prng->sbox[i & 0xFF];
14691         prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
14692         prng->sbox[seed[i]] = t;
14693     }
14694     prng->i = prng->j = 0;
14695 
14696     for (i = 0; i < 256; i += seedLen) {
14697         JimRandomBytes(interp, seed, seedLen);
14698     }
14699 }
14700 
14701 
Jim_IncrCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)14702 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14703 {
14704     jim_wide wideValue, increment = 1;
14705     Jim_Obj *intObjPtr;
14706 
14707     if (argc != 2 && argc != 3) {
14708         Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
14709         return JIM_ERR;
14710     }
14711     if (argc == 3) {
14712         if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
14713             return JIM_ERR;
14714     }
14715     intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
14716     if (!intObjPtr) {
14717 
14718         wideValue = 0;
14719     }
14720     else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
14721         return JIM_ERR;
14722     }
14723     if (!intObjPtr || Jim_IsShared(intObjPtr)) {
14724         intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
14725         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
14726             Jim_FreeNewObj(interp, intObjPtr);
14727             return JIM_ERR;
14728         }
14729     }
14730     else {
14731 
14732         Jim_InvalidateStringRep(intObjPtr);
14733         JimWideValue(intObjPtr) = wideValue + increment;
14734 
14735         if (argv[1]->typePtr != &variableObjType) {
14736 
14737             Jim_SetVariable(interp, argv[1], intObjPtr);
14738         }
14739     }
14740     Jim_SetResult(interp, intObjPtr);
14741     return JIM_OK;
14742 }
14743 
14744 
14745 #define JIM_EVAL_SARGV_LEN 8
14746 #define JIM_EVAL_SINTV_LEN 8
14747 
14748 
JimUnknown(Jim_Interp * interp,int argc,Jim_Obj * const * argv)14749 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14750 {
14751     int retcode;
14752 
14753     if (interp->unknown_called > 50) {
14754         return JIM_ERR;
14755     }
14756 
14757 
14758 
14759     if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
14760         return JIM_ERR;
14761 
14762     interp->unknown_called++;
14763 
14764     retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
14765     interp->unknown_called--;
14766 
14767     return retcode;
14768 }
14769 
JimInvokeCommand(Jim_Interp * interp,int objc,Jim_Obj * const * objv)14770 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14771 {
14772     int retcode;
14773     Jim_Cmd *cmdPtr;
14774     void *prevPrivData;
14775 
14776 #if 0
14777     printf("invoke");
14778     int j;
14779     for (j = 0; j < objc; j++) {
14780         printf(" '%s'", Jim_String(objv[j]));
14781     }
14782     printf("\n");
14783 #endif
14784 
14785     if (interp->framePtr->tailcallCmd) {
14786 
14787         cmdPtr = interp->framePtr->tailcallCmd;
14788         interp->framePtr->tailcallCmd = NULL;
14789     }
14790     else {
14791         cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
14792         if (cmdPtr == NULL) {
14793             return JimUnknown(interp, objc, objv);
14794         }
14795         JimIncrCmdRefCount(cmdPtr);
14796     }
14797 
14798     if (interp->evalDepth == interp->maxEvalDepth) {
14799         Jim_SetResultString(interp, "Infinite eval recursion", -1);
14800         retcode = JIM_ERR;
14801         goto out;
14802     }
14803     interp->evalDepth++;
14804     prevPrivData = interp->cmdPrivData;
14805 
14806 
14807     Jim_SetEmptyResult(interp);
14808     if (cmdPtr->isproc) {
14809         retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
14810     }
14811     else {
14812         interp->cmdPrivData = cmdPtr->u.native.privData;
14813         retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
14814     }
14815     interp->cmdPrivData = prevPrivData;
14816     interp->evalDepth--;
14817 
14818 out:
14819     JimDecrCmdRefCount(interp, cmdPtr);
14820 
14821     return retcode;
14822 }
14823 
Jim_EvalObjVector(Jim_Interp * interp,int objc,Jim_Obj * const * objv)14824 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14825 {
14826     int i, retcode;
14827 
14828 
14829     for (i = 0; i < objc; i++)
14830         Jim_IncrRefCount(objv[i]);
14831 
14832     retcode = JimInvokeCommand(interp, objc, objv);
14833 
14834 
14835     for (i = 0; i < objc; i++)
14836         Jim_DecrRefCount(interp, objv[i]);
14837 
14838     return retcode;
14839 }
14840 
Jim_EvalObjPrefix(Jim_Interp * interp,Jim_Obj * prefix,int objc,Jim_Obj * const * objv)14841 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
14842 {
14843     int ret;
14844     Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
14845 
14846     nargv[0] = prefix;
14847     memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
14848     ret = Jim_EvalObjVector(interp, objc + 1, nargv);
14849     Jim_Free(nargv);
14850     return ret;
14851 }
14852 
JimAddErrorToStack(Jim_Interp * interp,ScriptObj * script)14853 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script)
14854 {
14855     if (!interp->errorFlag) {
14856 
14857         interp->errorFlag = 1;
14858         Jim_IncrRefCount(script->fileNameObj);
14859         Jim_DecrRefCount(interp, interp->errorFileNameObj);
14860         interp->errorFileNameObj = script->fileNameObj;
14861         interp->errorLine = script->linenr;
14862 
14863         JimResetStackTrace(interp);
14864 
14865         interp->addStackTrace++;
14866     }
14867 
14868 
14869     if (interp->addStackTrace > 0) {
14870 
14871 
14872         JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr);
14873 
14874         if (Jim_Length(script->fileNameObj)) {
14875             interp->addStackTrace = 0;
14876         }
14877 
14878         Jim_DecrRefCount(interp, interp->errorProc);
14879         interp->errorProc = interp->emptyObj;
14880         Jim_IncrRefCount(interp->errorProc);
14881     }
14882 }
14883 
JimSubstOneToken(Jim_Interp * interp,const ScriptToken * token,Jim_Obj ** objPtrPtr)14884 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
14885 {
14886     Jim_Obj *objPtr;
14887 
14888     switch (token->type) {
14889         case JIM_TT_STR:
14890         case JIM_TT_ESC:
14891             objPtr = token->objPtr;
14892             break;
14893         case JIM_TT_VAR:
14894             objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
14895             break;
14896         case JIM_TT_DICTSUGAR:
14897             objPtr = JimExpandDictSugar(interp, token->objPtr);
14898             break;
14899         case JIM_TT_EXPRSUGAR:
14900             objPtr = JimExpandExprSugar(interp, token->objPtr);
14901             break;
14902         case JIM_TT_CMD:
14903             switch (Jim_EvalObj(interp, token->objPtr)) {
14904                 case JIM_OK:
14905                 case JIM_RETURN:
14906                     objPtr = interp->result;
14907                     break;
14908                 case JIM_BREAK:
14909 
14910                     return JIM_BREAK;
14911                 case JIM_CONTINUE:
14912 
14913                     return JIM_CONTINUE;
14914                 default:
14915                     return JIM_ERR;
14916             }
14917             break;
14918         default:
14919             JimPanic((1,
14920                 "default token type (%d) reached " "in Jim_SubstObj().", token->type));
14921             objPtr = NULL;
14922             break;
14923     }
14924     if (objPtr) {
14925         *objPtrPtr = objPtr;
14926         return JIM_OK;
14927     }
14928     return JIM_ERR;
14929 }
14930 
JimInterpolateTokens(Jim_Interp * interp,const ScriptToken * token,int tokens,int flags)14931 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
14932 {
14933     int totlen = 0, i;
14934     Jim_Obj **intv;
14935     Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
14936     Jim_Obj *objPtr;
14937     char *s;
14938 
14939     if (tokens <= JIM_EVAL_SINTV_LEN)
14940         intv = sintv;
14941     else
14942         intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
14943 
14944     for (i = 0; i < tokens; i++) {
14945         switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
14946             case JIM_OK:
14947             case JIM_RETURN:
14948                 break;
14949             case JIM_BREAK:
14950                 if (flags & JIM_SUBST_FLAG) {
14951 
14952                     tokens = i;
14953                     continue;
14954                 }
14955 
14956 
14957             case JIM_CONTINUE:
14958                 if (flags & JIM_SUBST_FLAG) {
14959                     intv[i] = NULL;
14960                     continue;
14961                 }
14962 
14963 
14964             default:
14965                 while (i--) {
14966                     Jim_DecrRefCount(interp, intv[i]);
14967                 }
14968                 if (intv != sintv) {
14969                     Jim_Free(intv);
14970                 }
14971                 return NULL;
14972         }
14973         Jim_IncrRefCount(intv[i]);
14974         Jim_String(intv[i]);
14975         totlen += intv[i]->length;
14976     }
14977 
14978 
14979     if (tokens == 1 && intv[0] && intv == sintv) {
14980 
14981         intv[0]->refCount--;
14982         return intv[0];
14983     }
14984 
14985     objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
14986 
14987     if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
14988         && token[2].type == JIM_TT_VAR) {
14989 
14990         objPtr->typePtr = &interpolatedObjType;
14991         objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
14992         objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
14993         Jim_IncrRefCount(intv[2]);
14994     }
14995     else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
14996 
14997         JimSetSourceInfo(interp, objPtr, intv[0]->internalRep.sourceValue.fileNameObj, intv[0]->internalRep.sourceValue.lineNumber);
14998     }
14999 
15000 
15001     s = objPtr->bytes = Jim_Alloc(totlen + 1);
15002     objPtr->length = totlen;
15003     for (i = 0; i < tokens; i++) {
15004         if (intv[i]) {
15005             memcpy(s, intv[i]->bytes, intv[i]->length);
15006             s += intv[i]->length;
15007             Jim_DecrRefCount(interp, intv[i]);
15008         }
15009     }
15010     objPtr->bytes[totlen] = '\0';
15011 
15012     if (intv != sintv) {
15013         Jim_Free(intv);
15014     }
15015 
15016     return objPtr;
15017 }
15018 
15019 
JimEvalObjList(Jim_Interp * interp,Jim_Obj * listPtr)15020 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15021 {
15022     int retcode = JIM_OK;
15023 
15024     JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
15025 
15026     if (listPtr->internalRep.listValue.len) {
15027         Jim_IncrRefCount(listPtr);
15028         retcode = JimInvokeCommand(interp,
15029             listPtr->internalRep.listValue.len,
15030             listPtr->internalRep.listValue.ele);
15031         Jim_DecrRefCount(interp, listPtr);
15032     }
15033     return retcode;
15034 }
15035 
Jim_EvalObjList(Jim_Interp * interp,Jim_Obj * listPtr)15036 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15037 {
15038     SetListFromAny(interp, listPtr);
15039     return JimEvalObjList(interp, listPtr);
15040 }
15041 
Jim_EvalObj(Jim_Interp * interp,Jim_Obj * scriptObjPtr)15042 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
15043 {
15044     int i;
15045     ScriptObj *script;
15046     ScriptToken *token;
15047     int retcode = JIM_OK;
15048     Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
15049     Jim_Obj *prevScriptObj;
15050 
15051     if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
15052         return JimEvalObjList(interp, scriptObjPtr);
15053     }
15054 
15055     Jim_IncrRefCount(scriptObjPtr);
15056     script = JimGetScript(interp, scriptObjPtr);
15057     if (!JimScriptValid(interp, script)) {
15058         Jim_DecrRefCount(interp, scriptObjPtr);
15059         return JIM_ERR;
15060     }
15061 
15062     Jim_SetEmptyResult(interp);
15063 
15064     token = script->token;
15065 
15066 #ifdef JIM_OPTIMIZATION
15067     if (script->len == 0) {
15068         Jim_DecrRefCount(interp, scriptObjPtr);
15069         return JIM_OK;
15070     }
15071     if (script->len == 3
15072         && token[1].objPtr->typePtr == &commandObjType
15073         && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
15074         && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
15075         && token[2].objPtr->typePtr == &variableObjType) {
15076 
15077         Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
15078 
15079         if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
15080             JimWideValue(objPtr)++;
15081             Jim_InvalidateStringRep(objPtr);
15082             Jim_DecrRefCount(interp, scriptObjPtr);
15083             Jim_SetResult(interp, objPtr);
15084             return JIM_OK;
15085         }
15086     }
15087 #endif
15088 
15089     script->inUse++;
15090 
15091 
15092     prevScriptObj = interp->currentScriptObj;
15093     interp->currentScriptObj = scriptObjPtr;
15094 
15095     interp->errorFlag = 0;
15096     argv = sargv;
15097 
15098     for (i = 0; i < script->len && retcode == JIM_OK; ) {
15099         int argc;
15100         int j;
15101 
15102 
15103         argc = token[i].objPtr->internalRep.scriptLineValue.argc;
15104         script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
15105 
15106 
15107         if (argc > JIM_EVAL_SARGV_LEN)
15108             argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
15109 
15110 
15111         i++;
15112 
15113         for (j = 0; j < argc; j++) {
15114             long wordtokens = 1;
15115             int expand = 0;
15116             Jim_Obj *wordObjPtr = NULL;
15117 
15118             if (token[i].type == JIM_TT_WORD) {
15119                 wordtokens = JimWideValue(token[i++].objPtr);
15120                 if (wordtokens < 0) {
15121                     expand = 1;
15122                     wordtokens = -wordtokens;
15123                 }
15124             }
15125 
15126             if (wordtokens == 1) {
15127 
15128                 switch (token[i].type) {
15129                     case JIM_TT_ESC:
15130                     case JIM_TT_STR:
15131                         wordObjPtr = token[i].objPtr;
15132                         break;
15133                     case JIM_TT_VAR:
15134                         wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
15135                         break;
15136                     case JIM_TT_EXPRSUGAR:
15137                         wordObjPtr = JimExpandExprSugar(interp, token[i].objPtr);
15138                         break;
15139                     case JIM_TT_DICTSUGAR:
15140                         wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
15141                         break;
15142                     case JIM_TT_CMD:
15143                         retcode = Jim_EvalObj(interp, token[i].objPtr);
15144                         if (retcode == JIM_OK) {
15145                             wordObjPtr = Jim_GetResult(interp);
15146                         }
15147                         break;
15148                     default:
15149                         JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
15150                 }
15151             }
15152             else {
15153                 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
15154             }
15155 
15156             if (!wordObjPtr) {
15157                 if (retcode == JIM_OK) {
15158                     retcode = JIM_ERR;
15159                 }
15160                 break;
15161             }
15162 
15163             Jim_IncrRefCount(wordObjPtr);
15164             i += wordtokens;
15165 
15166             if (!expand) {
15167                 argv[j] = wordObjPtr;
15168             }
15169             else {
15170 
15171                 int len = Jim_ListLength(interp, wordObjPtr);
15172                 int newargc = argc + len - 1;
15173                 int k;
15174 
15175                 if (len > 1) {
15176                     if (argv == sargv) {
15177                         if (newargc > JIM_EVAL_SARGV_LEN) {
15178                             argv = Jim_Alloc(sizeof(*argv) * newargc);
15179                             memcpy(argv, sargv, sizeof(*argv) * j);
15180                         }
15181                     }
15182                     else {
15183 
15184                         argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
15185                     }
15186                 }
15187 
15188 
15189                 for (k = 0; k < len; k++) {
15190                     argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
15191                     Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
15192                 }
15193 
15194                 Jim_DecrRefCount(interp, wordObjPtr);
15195 
15196 
15197                 j--;
15198                 argc += len - 1;
15199             }
15200         }
15201 
15202         if (retcode == JIM_OK && argc) {
15203 
15204             retcode = JimInvokeCommand(interp, argc, argv);
15205 
15206             if (Jim_CheckSignal(interp)) {
15207                 retcode = JIM_SIGNAL;
15208             }
15209         }
15210 
15211 
15212         while (j-- > 0) {
15213             Jim_DecrRefCount(interp, argv[j]);
15214         }
15215 
15216         if (argv != sargv) {
15217             Jim_Free(argv);
15218             argv = sargv;
15219         }
15220     }
15221 
15222 
15223     if (retcode == JIM_ERR) {
15224         JimAddErrorToStack(interp, script);
15225     }
15226 
15227     else if (retcode != JIM_RETURN || interp->returnCode != JIM_ERR) {
15228 
15229         interp->addStackTrace = 0;
15230     }
15231 
15232 
15233     interp->currentScriptObj = prevScriptObj;
15234 
15235     Jim_FreeIntRep(interp, scriptObjPtr);
15236     scriptObjPtr->typePtr = &scriptObjType;
15237     Jim_SetIntRepPtr(scriptObjPtr, script);
15238     Jim_DecrRefCount(interp, scriptObjPtr);
15239 
15240     return retcode;
15241 }
15242 
JimSetProcArg(Jim_Interp * interp,Jim_Obj * argNameObj,Jim_Obj * argValObj)15243 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
15244 {
15245     int retcode;
15246 
15247     const char *varname = Jim_String(argNameObj);
15248     if (*varname == '&') {
15249 
15250         Jim_Obj *objPtr;
15251         Jim_CallFrame *savedCallFrame = interp->framePtr;
15252 
15253         interp->framePtr = interp->framePtr->parent;
15254         objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
15255         interp->framePtr = savedCallFrame;
15256         if (!objPtr) {
15257             return JIM_ERR;
15258         }
15259 
15260 
15261         objPtr = Jim_NewStringObj(interp, varname + 1, -1);
15262         Jim_IncrRefCount(objPtr);
15263         retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
15264         Jim_DecrRefCount(interp, objPtr);
15265     }
15266     else {
15267         retcode = Jim_SetVariable(interp, argNameObj, argValObj);
15268     }
15269     return retcode;
15270 }
15271 
JimSetProcWrongArgs(Jim_Interp * interp,Jim_Obj * procNameObj,Jim_Cmd * cmd)15272 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
15273 {
15274 
15275     Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
15276     int i;
15277 
15278     for (i = 0; i < cmd->u.proc.argListLen; i++) {
15279         Jim_AppendString(interp, argmsg, " ", 1);
15280 
15281         if (i == cmd->u.proc.argsPos) {
15282             if (cmd->u.proc.arglist[i].defaultObjPtr) {
15283 
15284                 Jim_AppendString(interp, argmsg, "?", 1);
15285                 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
15286                 Jim_AppendString(interp, argmsg, " ...?", -1);
15287             }
15288             else {
15289 
15290                 Jim_AppendString(interp, argmsg, "?arg...?", -1);
15291             }
15292         }
15293         else {
15294             if (cmd->u.proc.arglist[i].defaultObjPtr) {
15295                 Jim_AppendString(interp, argmsg, "?", 1);
15296                 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
15297                 Jim_AppendString(interp, argmsg, "?", 1);
15298             }
15299             else {
15300                 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
15301                 if (*arg == '&') {
15302                     arg++;
15303                 }
15304                 Jim_AppendString(interp, argmsg, arg, -1);
15305             }
15306         }
15307     }
15308     Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
15309 }
15310 
15311 #ifdef jim_ext_namespace
Jim_EvalNamespace(Jim_Interp * interp,Jim_Obj * scriptObj,Jim_Obj * nsObj)15312 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
15313 {
15314     Jim_CallFrame *callFramePtr;
15315     int retcode;
15316 
15317 
15318     callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
15319     callFramePtr->argv = &interp->emptyObj;
15320     callFramePtr->argc = 0;
15321     callFramePtr->procArgsObjPtr = NULL;
15322     callFramePtr->procBodyObjPtr = scriptObj;
15323     callFramePtr->staticVars = NULL;
15324     callFramePtr->fileNameObj = interp->emptyObj;
15325     callFramePtr->line = 0;
15326     Jim_IncrRefCount(scriptObj);
15327     interp->framePtr = callFramePtr;
15328 
15329 
15330     if (interp->framePtr->level == interp->maxCallFrameDepth) {
15331         Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15332         retcode = JIM_ERR;
15333     }
15334     else {
15335 
15336         retcode = Jim_EvalObj(interp, scriptObj);
15337     }
15338 
15339 
15340     interp->framePtr = interp->framePtr->parent;
15341     JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15342 
15343     return retcode;
15344 }
15345 #endif
15346 
JimCallProcedure(Jim_Interp * interp,Jim_Cmd * cmd,int argc,Jim_Obj * const * argv)15347 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
15348 {
15349     Jim_CallFrame *callFramePtr;
15350     int i, d, retcode, optargs;
15351     ScriptObj *script;
15352 
15353 
15354     if (argc - 1 < cmd->u.proc.reqArity ||
15355         (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
15356         JimSetProcWrongArgs(interp, argv[0], cmd);
15357         return JIM_ERR;
15358     }
15359 
15360     if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
15361 
15362         return JIM_OK;
15363     }
15364 
15365 
15366     if (interp->framePtr->level == interp->maxCallFrameDepth) {
15367         Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15368         return JIM_ERR;
15369     }
15370 
15371 
15372     callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
15373     callFramePtr->argv = argv;
15374     callFramePtr->argc = argc;
15375     callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
15376     callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
15377     callFramePtr->staticVars = cmd->u.proc.staticVars;
15378 
15379 
15380     script = JimGetScript(interp, interp->currentScriptObj);
15381     callFramePtr->fileNameObj = script->fileNameObj;
15382     callFramePtr->line = script->linenr;
15383 
15384     Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
15385     Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
15386     interp->framePtr = callFramePtr;
15387 
15388 
15389     optargs = (argc - 1 - cmd->u.proc.reqArity);
15390 
15391 
15392     i = 1;
15393     for (d = 0; d < cmd->u.proc.argListLen; d++) {
15394         Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
15395         if (d == cmd->u.proc.argsPos) {
15396 
15397             Jim_Obj *listObjPtr;
15398             int argsLen = 0;
15399             if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
15400                 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
15401             }
15402             listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
15403 
15404 
15405             if (cmd->u.proc.arglist[d].defaultObjPtr) {
15406                 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
15407             }
15408             retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
15409             if (retcode != JIM_OK) {
15410                 goto badargset;
15411             }
15412 
15413             i += argsLen;
15414             continue;
15415         }
15416 
15417 
15418         if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
15419             retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
15420         }
15421         else {
15422 
15423             retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
15424         }
15425         if (retcode != JIM_OK) {
15426             goto badargset;
15427         }
15428     }
15429 
15430 
15431     retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
15432 
15433 badargset:
15434 
15435 
15436     retcode = JimInvokeDefer(interp, retcode);
15437     interp->framePtr = interp->framePtr->parent;
15438     JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15439 
15440 
15441     if (interp->framePtr->tailcallObj) {
15442         do {
15443             Jim_Obj *tailcallObj = interp->framePtr->tailcallObj;
15444 
15445             interp->framePtr->tailcallObj = NULL;
15446 
15447             if (retcode == JIM_EVAL) {
15448                 retcode = Jim_EvalObjList(interp, tailcallObj);
15449                 if (retcode == JIM_RETURN) {
15450                     interp->returnLevel++;
15451                 }
15452             }
15453             Jim_DecrRefCount(interp, tailcallObj);
15454         } while (interp->framePtr->tailcallObj);
15455 
15456 
15457         if (interp->framePtr->tailcallCmd) {
15458             JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
15459             interp->framePtr->tailcallCmd = NULL;
15460         }
15461     }
15462 
15463 
15464     if (retcode == JIM_RETURN) {
15465         if (--interp->returnLevel <= 0) {
15466             retcode = interp->returnCode;
15467             interp->returnCode = JIM_OK;
15468             interp->returnLevel = 0;
15469         }
15470     }
15471     else if (retcode == JIM_ERR) {
15472         interp->addStackTrace++;
15473         Jim_DecrRefCount(interp, interp->errorProc);
15474         interp->errorProc = argv[0];
15475         Jim_IncrRefCount(interp->errorProc);
15476     }
15477 
15478     return retcode;
15479 }
15480 
Jim_EvalSource(Jim_Interp * interp,const char * filename,int lineno,const char * script)15481 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
15482 {
15483     int retval;
15484     Jim_Obj *scriptObjPtr;
15485 
15486     scriptObjPtr = Jim_NewStringObj(interp, script, -1);
15487     Jim_IncrRefCount(scriptObjPtr);
15488 
15489     if (filename) {
15490         Jim_Obj *prevScriptObj;
15491 
15492         JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
15493 
15494         prevScriptObj = interp->currentScriptObj;
15495         interp->currentScriptObj = scriptObjPtr;
15496 
15497         retval = Jim_EvalObj(interp, scriptObjPtr);
15498 
15499         interp->currentScriptObj = prevScriptObj;
15500     }
15501     else {
15502         retval = Jim_EvalObj(interp, scriptObjPtr);
15503     }
15504     Jim_DecrRefCount(interp, scriptObjPtr);
15505     return retval;
15506 }
15507 
Jim_Eval(Jim_Interp * interp,const char * script)15508 int Jim_Eval(Jim_Interp *interp, const char *script)
15509 {
15510     return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
15511 }
15512 
15513 
Jim_EvalGlobal(Jim_Interp * interp,const char * script)15514 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
15515 {
15516     int retval;
15517     Jim_CallFrame *savedFramePtr = interp->framePtr;
15518 
15519     interp->framePtr = interp->topFramePtr;
15520     retval = Jim_Eval(interp, script);
15521     interp->framePtr = savedFramePtr;
15522 
15523     return retval;
15524 }
15525 
Jim_EvalFileGlobal(Jim_Interp * interp,const char * filename)15526 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
15527 {
15528     int retval;
15529     Jim_CallFrame *savedFramePtr = interp->framePtr;
15530 
15531     interp->framePtr = interp->topFramePtr;
15532     retval = Jim_EvalFile(interp, filename);
15533     interp->framePtr = savedFramePtr;
15534 
15535     return retval;
15536 }
15537 
15538 #include <sys/stat.h>
15539 
Jim_EvalFile(Jim_Interp * interp,const char * filename)15540 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
15541 {
15542     FILE *fp;
15543     char *buf;
15544     Jim_Obj *scriptObjPtr;
15545     Jim_Obj *prevScriptObj;
15546     struct stat sb;
15547     int retcode;
15548     int readlen;
15549 
15550     if (stat(filename, &sb) != 0 || (fp = fopen(filename, "rt")) == NULL) {
15551         Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
15552         return JIM_ERR;
15553     }
15554     if (sb.st_size == 0) {
15555         fclose(fp);
15556         return JIM_OK;
15557     }
15558 
15559     buf = Jim_Alloc(sb.st_size + 1);
15560     readlen = fread(buf, 1, sb.st_size, fp);
15561     if (ferror(fp)) {
15562         fclose(fp);
15563         Jim_Free(buf);
15564         Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
15565         return JIM_ERR;
15566     }
15567     fclose(fp);
15568     buf[readlen] = 0;
15569 
15570     scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
15571     JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1);
15572     Jim_IncrRefCount(scriptObjPtr);
15573 
15574     prevScriptObj = interp->currentScriptObj;
15575     interp->currentScriptObj = scriptObjPtr;
15576 
15577     retcode = Jim_EvalObj(interp, scriptObjPtr);
15578 
15579 
15580     if (retcode == JIM_RETURN) {
15581         if (--interp->returnLevel <= 0) {
15582             retcode = interp->returnCode;
15583             interp->returnCode = JIM_OK;
15584             interp->returnLevel = 0;
15585         }
15586     }
15587     if (retcode == JIM_ERR) {
15588 
15589         interp->addStackTrace++;
15590     }
15591 
15592     interp->currentScriptObj = prevScriptObj;
15593 
15594     Jim_DecrRefCount(interp, scriptObjPtr);
15595 
15596     return retcode;
15597 }
15598 
JimParseSubst(struct JimParserCtx * pc,int flags)15599 static void JimParseSubst(struct JimParserCtx *pc, int flags)
15600 {
15601     pc->tstart = pc->p;
15602     pc->tline = pc->linenr;
15603 
15604     if (pc->len == 0) {
15605         pc->tend = pc->p;
15606         pc->tt = JIM_TT_EOL;
15607         pc->eof = 1;
15608         return;
15609     }
15610     if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15611         JimParseCmd(pc);
15612         return;
15613     }
15614     if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15615         if (JimParseVar(pc) == JIM_OK) {
15616             return;
15617         }
15618 
15619         pc->tstart = pc->p;
15620         flags |= JIM_SUBST_NOVAR;
15621     }
15622     while (pc->len) {
15623         if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15624             break;
15625         }
15626         if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15627             break;
15628         }
15629         if (*pc->p == '\\' && pc->len > 1) {
15630             pc->p++;
15631             pc->len--;
15632         }
15633         pc->p++;
15634         pc->len--;
15635     }
15636     pc->tend = pc->p - 1;
15637     pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
15638 }
15639 
15640 
SetSubstFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr,int flags)15641 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
15642 {
15643     int scriptTextLen;
15644     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
15645     struct JimParserCtx parser;
15646     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
15647     ParseTokenList tokenlist;
15648 
15649 
15650     ScriptTokenListInit(&tokenlist);
15651 
15652     JimParserInit(&parser, scriptText, scriptTextLen, 1);
15653     while (1) {
15654         JimParseSubst(&parser, flags);
15655         if (parser.eof) {
15656 
15657             break;
15658         }
15659         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
15660             parser.tline);
15661     }
15662 
15663 
15664     script->inUse = 1;
15665     script->substFlags = flags;
15666     script->fileNameObj = interp->emptyObj;
15667     Jim_IncrRefCount(script->fileNameObj);
15668     SubstObjAddTokens(interp, script, &tokenlist);
15669 
15670 
15671     ScriptTokenListFree(&tokenlist);
15672 
15673 #ifdef DEBUG_SHOW_SUBST
15674     {
15675         int i;
15676 
15677         printf("==== Subst ====\n");
15678         for (i = 0; i < script->len; i++) {
15679             printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
15680                 Jim_String(script->token[i].objPtr));
15681         }
15682     }
15683 #endif
15684 
15685 
15686     Jim_FreeIntRep(interp, objPtr);
15687     Jim_SetIntRepPtr(objPtr, script);
15688     objPtr->typePtr = &scriptObjType;
15689     return JIM_OK;
15690 }
15691 
Jim_GetSubst(Jim_Interp * interp,Jim_Obj * objPtr,int flags)15692 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
15693 {
15694     if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
15695         SetSubstFromAny(interp, objPtr, flags);
15696     return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
15697 }
15698 
Jim_SubstObj(Jim_Interp * interp,Jim_Obj * substObjPtr,Jim_Obj ** resObjPtrPtr,int flags)15699 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
15700 {
15701     ScriptObj *script = Jim_GetSubst(interp, substObjPtr, flags);
15702 
15703     Jim_IncrRefCount(substObjPtr);
15704     script->inUse++;
15705 
15706     *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
15707 
15708     script->inUse--;
15709     Jim_DecrRefCount(interp, substObjPtr);
15710     if (*resObjPtrPtr == NULL) {
15711         return JIM_ERR;
15712     }
15713     return JIM_OK;
15714 }
15715 
Jim_WrongNumArgs(Jim_Interp * interp,int argc,Jim_Obj * const * argv,const char * msg)15716 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
15717 {
15718     Jim_Obj *objPtr;
15719     Jim_Obj *listObjPtr;
15720 
15721     JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0"));
15722 
15723     listObjPtr = Jim_NewListObj(interp, argv, argc);
15724 
15725     if (msg && *msg) {
15726         Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
15727     }
15728     Jim_IncrRefCount(listObjPtr);
15729     objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
15730     Jim_DecrRefCount(interp, listObjPtr);
15731 
15732     Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
15733 }
15734 
15735 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
15736     Jim_HashEntry *he, int type);
15737 
15738 #define JimTrivialMatch(pattern)    (strpbrk((pattern), "*[?\\") == NULL)
15739 
JimHashtablePatternMatch(Jim_Interp * interp,Jim_HashTable * ht,Jim_Obj * patternObjPtr,JimHashtableIteratorCallbackType * callback,int type)15740 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
15741     JimHashtableIteratorCallbackType *callback, int type)
15742 {
15743     Jim_HashEntry *he;
15744     Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
15745 
15746 
15747     if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
15748         he = Jim_FindHashEntry(ht, Jim_String(patternObjPtr));
15749         if (he) {
15750             callback(interp, listObjPtr, he, type);
15751         }
15752     }
15753     else {
15754         Jim_HashTableIterator htiter;
15755         JimInitHashTableIterator(ht, &htiter);
15756         while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
15757             if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), he->key, 0)) {
15758                 callback(interp, listObjPtr, he, type);
15759             }
15760         }
15761     }
15762     return listObjPtr;
15763 }
15764 
15765 
15766 #define JIM_CMDLIST_COMMANDS 0
15767 #define JIM_CMDLIST_PROCS 1
15768 #define JIM_CMDLIST_CHANNELS 2
15769 
JimCommandMatch(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_HashEntry * he,int type)15770 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15771     Jim_HashEntry *he, int type)
15772 {
15773     Jim_Cmd *cmdPtr = Jim_GetHashEntryVal(he);
15774     Jim_Obj *objPtr;
15775 
15776     if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
15777 
15778         return;
15779     }
15780 
15781     objPtr = Jim_NewStringObj(interp, he->key, -1);
15782     Jim_IncrRefCount(objPtr);
15783 
15784     if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, objPtr)) {
15785         Jim_ListAppendElement(interp, listObjPtr, objPtr);
15786     }
15787     Jim_DecrRefCount(interp, objPtr);
15788 }
15789 
15790 
JimCommandsList(Jim_Interp * interp,Jim_Obj * patternObjPtr,int type)15791 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
15792 {
15793     return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
15794 }
15795 
15796 
15797 #define JIM_VARLIST_GLOBALS 0
15798 #define JIM_VARLIST_LOCALS 1
15799 #define JIM_VARLIST_VARS 2
15800 
15801 #define JIM_VARLIST_VALUES 0x1000
15802 
JimVariablesMatch(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_HashEntry * he,int type)15803 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15804     Jim_HashEntry *he, int type)
15805 {
15806     Jim_Var *varPtr = Jim_GetHashEntryVal(he);
15807 
15808     if (type != JIM_VARLIST_LOCALS || varPtr->linkFramePtr == NULL) {
15809         Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1));
15810         if (type & JIM_VARLIST_VALUES) {
15811             Jim_ListAppendElement(interp, listObjPtr, varPtr->objPtr);
15812         }
15813     }
15814 }
15815 
15816 
JimVariablesList(Jim_Interp * interp,Jim_Obj * patternObjPtr,int mode)15817 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
15818 {
15819     if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
15820         return interp->emptyObj;
15821     }
15822     else {
15823         Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
15824         return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch, mode);
15825     }
15826 }
15827 
JimInfoLevel(Jim_Interp * interp,Jim_Obj * levelObjPtr,Jim_Obj ** objPtrPtr,int info_level_cmd)15828 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
15829     Jim_Obj **objPtrPtr, int info_level_cmd)
15830 {
15831     Jim_CallFrame *targetCallFrame;
15832 
15833     targetCallFrame = JimGetCallFrameByInteger(interp, levelObjPtr);
15834     if (targetCallFrame == NULL) {
15835         return JIM_ERR;
15836     }
15837 
15838     if (targetCallFrame == interp->topFramePtr) {
15839         Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
15840         return JIM_ERR;
15841     }
15842     if (info_level_cmd) {
15843         *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
15844     }
15845     else {
15846         Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
15847 
15848         Jim_ListAppendElement(interp, listObj, targetCallFrame->argv[0]);
15849         Jim_ListAppendElement(interp, listObj, targetCallFrame->fileNameObj);
15850         Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, targetCallFrame->line));
15851         *objPtrPtr = listObj;
15852     }
15853     return JIM_OK;
15854 }
15855 
15856 
15857 
Jim_PutsCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)15858 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15859 {
15860     if (argc != 2 && argc != 3) {
15861         Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
15862         return JIM_ERR;
15863     }
15864     if (argc == 3) {
15865         if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
15866             Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
15867             return JIM_ERR;
15868         }
15869         else {
15870             fputs(Jim_String(argv[2]), stdout);
15871         }
15872     }
15873     else {
15874         puts(Jim_String(argv[1]));
15875     }
15876     return JIM_OK;
15877 }
15878 
15879 
JimAddMulHelper(Jim_Interp * interp,int argc,Jim_Obj * const * argv,int op)15880 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
15881 {
15882     jim_wide wideValue, res;
15883     double doubleValue, doubleRes;
15884     int i;
15885 
15886     res = (op == JIM_EXPROP_ADD) ? 0 : 1;
15887 
15888     for (i = 1; i < argc; i++) {
15889         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
15890             goto trydouble;
15891         if (op == JIM_EXPROP_ADD)
15892             res += wideValue;
15893         else
15894             res *= wideValue;
15895     }
15896     Jim_SetResultInt(interp, res);
15897     return JIM_OK;
15898   trydouble:
15899     doubleRes = (double)res;
15900     for (; i < argc; i++) {
15901         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
15902             return JIM_ERR;
15903         if (op == JIM_EXPROP_ADD)
15904             doubleRes += doubleValue;
15905         else
15906             doubleRes *= doubleValue;
15907     }
15908     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15909     return JIM_OK;
15910 }
15911 
15912 
JimSubDivHelper(Jim_Interp * interp,int argc,Jim_Obj * const * argv,int op)15913 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
15914 {
15915     jim_wide wideValue, res = 0;
15916     double doubleValue, doubleRes = 0;
15917     int i = 2;
15918 
15919     if (argc < 2) {
15920         Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
15921         return JIM_ERR;
15922     }
15923     else if (argc == 2) {
15924         if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
15925             if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
15926                 return JIM_ERR;
15927             }
15928             else {
15929                 if (op == JIM_EXPROP_SUB)
15930                     doubleRes = -doubleValue;
15931                 else
15932                     doubleRes = 1.0 / doubleValue;
15933                 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15934                 return JIM_OK;
15935             }
15936         }
15937         if (op == JIM_EXPROP_SUB) {
15938             res = -wideValue;
15939             Jim_SetResultInt(interp, res);
15940         }
15941         else {
15942             doubleRes = 1.0 / wideValue;
15943             Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15944         }
15945         return JIM_OK;
15946     }
15947     else {
15948         if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
15949             if (Jim_GetDouble(interp, argv[1], &doubleRes)
15950                 != JIM_OK) {
15951                 return JIM_ERR;
15952             }
15953             else {
15954                 goto trydouble;
15955             }
15956         }
15957     }
15958     for (i = 2; i < argc; i++) {
15959         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
15960             doubleRes = (double)res;
15961             goto trydouble;
15962         }
15963         if (op == JIM_EXPROP_SUB)
15964             res -= wideValue;
15965         else {
15966             if (wideValue == 0) {
15967                 Jim_SetResultString(interp, "Division by zero", -1);
15968                 return JIM_ERR;
15969             }
15970             res /= wideValue;
15971         }
15972     }
15973     Jim_SetResultInt(interp, res);
15974     return JIM_OK;
15975   trydouble:
15976     for (; i < argc; i++) {
15977         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
15978             return JIM_ERR;
15979         if (op == JIM_EXPROP_SUB)
15980             doubleRes -= doubleValue;
15981         else
15982             doubleRes /= doubleValue;
15983     }
15984     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15985     return JIM_OK;
15986 }
15987 
15988 
15989 
Jim_AddCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)15990 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15991 {
15992     return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
15993 }
15994 
15995 
Jim_MulCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)15996 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15997 {
15998     return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
15999 }
16000 
16001 
Jim_SubCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16002 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16003 {
16004     return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
16005 }
16006 
16007 
Jim_DivCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16008 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16009 {
16010     return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
16011 }
16012 
16013 
Jim_SetCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16014 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16015 {
16016     if (argc != 2 && argc != 3) {
16017         Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
16018         return JIM_ERR;
16019     }
16020     if (argc == 2) {
16021         Jim_Obj *objPtr;
16022 
16023         objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16024         if (!objPtr)
16025             return JIM_ERR;
16026         Jim_SetResult(interp, objPtr);
16027         return JIM_OK;
16028     }
16029 
16030     if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
16031         return JIM_ERR;
16032     Jim_SetResult(interp, argv[2]);
16033     return JIM_OK;
16034 }
16035 
Jim_UnsetCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16036 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16037 {
16038     int i = 1;
16039     int complain = 1;
16040 
16041     while (i < argc) {
16042         if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
16043             i++;
16044             break;
16045         }
16046         if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
16047             complain = 0;
16048             i++;
16049             continue;
16050         }
16051         break;
16052     }
16053 
16054     while (i < argc) {
16055         if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
16056             && complain) {
16057             return JIM_ERR;
16058         }
16059         i++;
16060     }
16061     return JIM_OK;
16062 }
16063 
16064 
Jim_WhileCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16065 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16066 {
16067     if (argc != 3) {
16068         Jim_WrongNumArgs(interp, 1, argv, "condition body");
16069         return JIM_ERR;
16070     }
16071 
16072 
16073     while (1) {
16074         int boolean, retval;
16075 
16076         if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
16077             return retval;
16078         if (!boolean)
16079             break;
16080 
16081         if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
16082             switch (retval) {
16083                 case JIM_BREAK:
16084                     goto out;
16085                     break;
16086                 case JIM_CONTINUE:
16087                     continue;
16088                     break;
16089                 default:
16090                     return retval;
16091             }
16092         }
16093     }
16094   out:
16095     Jim_SetEmptyResult(interp);
16096     return JIM_OK;
16097 }
16098 
16099 
Jim_ForCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16100 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16101 {
16102     int retval;
16103     int boolean = 1;
16104     Jim_Obj *varNamePtr = NULL;
16105     Jim_Obj *stopVarNamePtr = NULL;
16106 
16107     if (argc != 5) {
16108         Jim_WrongNumArgs(interp, 1, argv, "start test next body");
16109         return JIM_ERR;
16110     }
16111 
16112 
16113     if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
16114         return retval;
16115     }
16116 
16117     retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16118 
16119 
16120 #ifdef JIM_OPTIMIZATION
16121     if (retval == JIM_OK && boolean) {
16122         ScriptObj *incrScript;
16123         struct ExprTree *expr;
16124         jim_wide stop, currentVal;
16125         Jim_Obj *objPtr;
16126         int cmpOffset;
16127 
16128 
16129         expr = JimGetExpression(interp, argv[2]);
16130         incrScript = JimGetScript(interp, argv[3]);
16131 
16132 
16133         if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
16134             goto evalstart;
16135         }
16136 
16137         if (incrScript->token[1].type != JIM_TT_ESC) {
16138             goto evalstart;
16139         }
16140 
16141         if (expr->expr->type == JIM_EXPROP_LT) {
16142             cmpOffset = 0;
16143         }
16144         else if (expr->expr->type == JIM_EXPROP_LTE) {
16145             cmpOffset = 1;
16146         }
16147         else {
16148             goto evalstart;
16149         }
16150 
16151         if (expr->expr->left->type != JIM_TT_VAR) {
16152             goto evalstart;
16153         }
16154 
16155         if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) {
16156             goto evalstart;
16157         }
16158 
16159 
16160         if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
16161             goto evalstart;
16162         }
16163 
16164 
16165         if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
16166             goto evalstart;
16167         }
16168 
16169 
16170         if (expr->expr->right->type == JIM_TT_EXPR_INT) {
16171             if (Jim_GetWide(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) {
16172                 goto evalstart;
16173             }
16174         }
16175         else {
16176             stopVarNamePtr = expr->expr->right->objPtr;
16177             Jim_IncrRefCount(stopVarNamePtr);
16178 
16179             stop = 0;
16180         }
16181 
16182 
16183         varNamePtr = expr->expr->left->objPtr;
16184         Jim_IncrRefCount(varNamePtr);
16185 
16186         objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
16187         if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
16188             goto testcond;
16189         }
16190 
16191 
16192         while (retval == JIM_OK) {
16193 
16194 
16195 
16196 
16197             if (stopVarNamePtr) {
16198                 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
16199                 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
16200                     goto testcond;
16201                 }
16202             }
16203 
16204             if (currentVal >= stop + cmpOffset) {
16205                 break;
16206             }
16207 
16208 
16209             retval = Jim_EvalObj(interp, argv[4]);
16210             if (retval == JIM_OK || retval == JIM_CONTINUE) {
16211                 retval = JIM_OK;
16212 
16213                 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
16214 
16215 
16216                 if (objPtr == NULL) {
16217                     retval = JIM_ERR;
16218                     goto out;
16219                 }
16220                 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16221                     currentVal = ++JimWideValue(objPtr);
16222                     Jim_InvalidateStringRep(objPtr);
16223                 }
16224                 else {
16225                     if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
16226                         Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
16227                                 ++currentVal)) != JIM_OK) {
16228                         goto evalnext;
16229                     }
16230                 }
16231             }
16232         }
16233         goto out;
16234     }
16235   evalstart:
16236 #endif
16237 
16238     while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
16239 
16240         retval = Jim_EvalObj(interp, argv[4]);
16241 
16242         if (retval == JIM_OK || retval == JIM_CONTINUE) {
16243 
16244 JIM_IF_OPTIM(evalnext:)
16245             retval = Jim_EvalObj(interp, argv[3]);
16246             if (retval == JIM_OK || retval == JIM_CONTINUE) {
16247 
16248 JIM_IF_OPTIM(testcond:)
16249                 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16250             }
16251         }
16252     }
16253 JIM_IF_OPTIM(out:)
16254     if (stopVarNamePtr) {
16255         Jim_DecrRefCount(interp, stopVarNamePtr);
16256     }
16257     if (varNamePtr) {
16258         Jim_DecrRefCount(interp, varNamePtr);
16259     }
16260 
16261     if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
16262         Jim_SetEmptyResult(interp);
16263         return JIM_OK;
16264     }
16265 
16266     return retval;
16267 }
16268 
16269 
Jim_LoopCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16270 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16271 {
16272     int retval;
16273     jim_wide i;
16274     jim_wide limit;
16275     jim_wide incr = 1;
16276     Jim_Obj *bodyObjPtr;
16277 
16278     if (argc != 5 && argc != 6) {
16279         Jim_WrongNumArgs(interp, 1, argv, "var first limit ?incr? body");
16280         return JIM_ERR;
16281     }
16282 
16283     if (Jim_GetWide(interp, argv[2], &i) != JIM_OK ||
16284         Jim_GetWide(interp, argv[3], &limit) != JIM_OK ||
16285           (argc == 6 && Jim_GetWide(interp, argv[4], &incr) != JIM_OK)) {
16286         return JIM_ERR;
16287     }
16288     bodyObjPtr = (argc == 5) ? argv[4] : argv[5];
16289 
16290     retval = Jim_SetVariable(interp, argv[1], argv[2]);
16291 
16292     while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
16293         retval = Jim_EvalObj(interp, bodyObjPtr);
16294         if (retval == JIM_OK || retval == JIM_CONTINUE) {
16295             Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16296 
16297             retval = JIM_OK;
16298 
16299 
16300             i += incr;
16301 
16302             if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16303                 if (argv[1]->typePtr != &variableObjType) {
16304                     if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16305                         return JIM_ERR;
16306                     }
16307                 }
16308                 JimWideValue(objPtr) = i;
16309                 Jim_InvalidateStringRep(objPtr);
16310 
16311                 if (argv[1]->typePtr != &variableObjType) {
16312                     if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16313                         retval = JIM_ERR;
16314                         break;
16315                     }
16316                 }
16317             }
16318             else {
16319                 objPtr = Jim_NewIntObj(interp, i);
16320                 retval = Jim_SetVariable(interp, argv[1], objPtr);
16321                 if (retval != JIM_OK) {
16322                     Jim_FreeNewObj(interp, objPtr);
16323                 }
16324             }
16325         }
16326     }
16327 
16328     if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
16329         Jim_SetEmptyResult(interp);
16330         return JIM_OK;
16331     }
16332     return retval;
16333 }
16334 
16335 typedef struct {
16336     Jim_Obj *objPtr;
16337     int idx;
16338 } Jim_ListIter;
16339 
JimListIterInit(Jim_ListIter * iter,Jim_Obj * objPtr)16340 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
16341 {
16342     iter->objPtr = objPtr;
16343     iter->idx = 0;
16344 }
16345 
JimListIterNext(Jim_Interp * interp,Jim_ListIter * iter)16346 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
16347 {
16348     if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
16349         return NULL;
16350     }
16351     return iter->objPtr->internalRep.listValue.ele[iter->idx++];
16352 }
16353 
JimListIterDone(Jim_Interp * interp,Jim_ListIter * iter)16354 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
16355 {
16356     return iter->idx >= Jim_ListLength(interp, iter->objPtr);
16357 }
16358 
16359 
JimForeachMapHelper(Jim_Interp * interp,int argc,Jim_Obj * const * argv,int doMap)16360 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
16361 {
16362     int result = JIM_OK;
16363     int i, numargs;
16364     Jim_ListIter twoiters[2];
16365     Jim_ListIter *iters;
16366     Jim_Obj *script;
16367     Jim_Obj *resultObj;
16368 
16369     if (argc < 4 || argc % 2 != 0) {
16370         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
16371         return JIM_ERR;
16372     }
16373     script = argv[argc - 1];
16374     numargs = (argc - 1 - 1);
16375 
16376     if (numargs == 2) {
16377         iters = twoiters;
16378     }
16379     else {
16380         iters = Jim_Alloc(numargs * sizeof(*iters));
16381     }
16382     for (i = 0; i < numargs; i++) {
16383         JimListIterInit(&iters[i], argv[i + 1]);
16384         if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
16385             result = JIM_ERR;
16386         }
16387     }
16388     if (result != JIM_OK) {
16389         Jim_SetResultString(interp, "foreach varlist is empty", -1);
16390         goto empty_varlist;
16391     }
16392 
16393     if (doMap) {
16394         resultObj = Jim_NewListObj(interp, NULL, 0);
16395     }
16396     else {
16397         resultObj = interp->emptyObj;
16398     }
16399     Jim_IncrRefCount(resultObj);
16400 
16401     while (1) {
16402 
16403         for (i = 0; i < numargs; i += 2) {
16404             if (!JimListIterDone(interp, &iters[i + 1])) {
16405                 break;
16406             }
16407         }
16408         if (i == numargs) {
16409 
16410             break;
16411         }
16412 
16413 
16414         for (i = 0; i < numargs; i += 2) {
16415             Jim_Obj *varName;
16416 
16417 
16418             JimListIterInit(&iters[i], argv[i + 1]);
16419             while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
16420                 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
16421                 if (!valObj) {
16422 
16423                     valObj = interp->emptyObj;
16424                 }
16425 
16426                 Jim_IncrRefCount(valObj);
16427                 result = Jim_SetVariable(interp, varName, valObj);
16428                 Jim_DecrRefCount(interp, valObj);
16429                 if (result != JIM_OK) {
16430                     goto err;
16431                 }
16432             }
16433         }
16434         switch (result = Jim_EvalObj(interp, script)) {
16435             case JIM_OK:
16436                 if (doMap) {
16437                     Jim_ListAppendElement(interp, resultObj, interp->result);
16438                 }
16439                 break;
16440             case JIM_CONTINUE:
16441                 break;
16442             case JIM_BREAK:
16443                 goto out;
16444             default:
16445                 goto err;
16446         }
16447     }
16448   out:
16449     result = JIM_OK;
16450     Jim_SetResult(interp, resultObj);
16451   err:
16452     Jim_DecrRefCount(interp, resultObj);
16453   empty_varlist:
16454     if (numargs > 2) {
16455         Jim_Free(iters);
16456     }
16457     return result;
16458 }
16459 
16460 
Jim_ForeachCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16461 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16462 {
16463     return JimForeachMapHelper(interp, argc, argv, 0);
16464 }
16465 
16466 
Jim_LmapCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16467 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16468 {
16469     return JimForeachMapHelper(interp, argc, argv, 1);
16470 }
16471 
16472 
Jim_LassignCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16473 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16474 {
16475     int result = JIM_ERR;
16476     int i;
16477     Jim_ListIter iter;
16478     Jim_Obj *resultObj;
16479 
16480     if (argc < 2) {
16481         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
16482         return JIM_ERR;
16483     }
16484 
16485     JimListIterInit(&iter, argv[1]);
16486 
16487     for (i = 2; i < argc; i++) {
16488         Jim_Obj *valObj = JimListIterNext(interp, &iter);
16489         result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
16490         if (result != JIM_OK) {
16491             return result;
16492         }
16493     }
16494 
16495     resultObj = Jim_NewListObj(interp, NULL, 0);
16496     while (!JimListIterDone(interp, &iter)) {
16497         Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
16498     }
16499 
16500     Jim_SetResult(interp, resultObj);
16501 
16502     return JIM_OK;
16503 }
16504 
16505 
Jim_IfCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16506 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16507 {
16508     int boolean, retval, current = 1, falsebody = 0;
16509 
16510     if (argc >= 3) {
16511         while (1) {
16512 
16513             if (current >= argc)
16514                 goto err;
16515             if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
16516                 != JIM_OK)
16517                 return retval;
16518 
16519             if (current >= argc)
16520                 goto err;
16521             if (Jim_CompareStringImmediate(interp, argv[current], "then"))
16522                 current++;
16523 
16524             if (current >= argc)
16525                 goto err;
16526             if (boolean)
16527                 return Jim_EvalObj(interp, argv[current]);
16528 
16529             if (++current >= argc) {
16530                 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
16531                 return JIM_OK;
16532             }
16533             falsebody = current++;
16534             if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
16535 
16536                 if (current != argc - 1)
16537                     goto err;
16538                 return Jim_EvalObj(interp, argv[current]);
16539             }
16540             else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
16541                 continue;
16542 
16543             else if (falsebody != argc - 1)
16544                 goto err;
16545             return Jim_EvalObj(interp, argv[falsebody]);
16546         }
16547         return JIM_OK;
16548     }
16549   err:
16550     Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
16551     return JIM_ERR;
16552 }
16553 
16554 
16555 
Jim_CommandMatchObj(Jim_Interp * interp,Jim_Obj * commandObj,Jim_Obj * patternObj,Jim_Obj * stringObj,int nocase)16556 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
16557     Jim_Obj *stringObj, int nocase)
16558 {
16559     Jim_Obj *parms[4];
16560     int argc = 0;
16561     long eq;
16562     int rc;
16563 
16564     parms[argc++] = commandObj;
16565     if (nocase) {
16566         parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
16567     }
16568     parms[argc++] = patternObj;
16569     parms[argc++] = stringObj;
16570 
16571     rc = Jim_EvalObjVector(interp, argc, parms);
16572 
16573     if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
16574         eq = -rc;
16575     }
16576 
16577     return eq;
16578 }
16579 
16580 
Jim_SwitchCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16581 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16582 {
16583     enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
16584     int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
16585     Jim_Obj *command = NULL, *scriptObj = NULL, *strObj;
16586     Jim_Obj **caseList;
16587 
16588     if (argc < 3) {
16589       wrongnumargs:
16590         Jim_WrongNumArgs(interp, 1, argv, "?options? string "
16591             "pattern body ... ?default body?   or   " "{pattern body ?pattern body ...?}");
16592         return JIM_ERR;
16593     }
16594     for (opt = 1; opt < argc; ++opt) {
16595         const char *option = Jim_String(argv[opt]);
16596 
16597         if (*option != '-')
16598             break;
16599         else if (strncmp(option, "--", 2) == 0) {
16600             ++opt;
16601             break;
16602         }
16603         else if (strncmp(option, "-exact", 2) == 0)
16604             matchOpt = SWITCH_EXACT;
16605         else if (strncmp(option, "-glob", 2) == 0)
16606             matchOpt = SWITCH_GLOB;
16607         else if (strncmp(option, "-regexp", 2) == 0)
16608             matchOpt = SWITCH_RE;
16609         else if (strncmp(option, "-command", 2) == 0) {
16610             matchOpt = SWITCH_CMD;
16611             if ((argc - opt) < 2)
16612                 goto wrongnumargs;
16613             command = argv[++opt];
16614         }
16615         else {
16616             Jim_SetResultFormatted(interp,
16617                 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
16618                 argv[opt]);
16619             return JIM_ERR;
16620         }
16621         if ((argc - opt) < 2)
16622             goto wrongnumargs;
16623     }
16624     strObj = argv[opt++];
16625     patCount = argc - opt;
16626     if (patCount == 1) {
16627         JimListGetElements(interp, argv[opt], &patCount, &caseList);
16628     }
16629     else
16630         caseList = (Jim_Obj **)&argv[opt];
16631     if (patCount == 0 || patCount % 2 != 0)
16632         goto wrongnumargs;
16633     for (i = 0; scriptObj == NULL && i < patCount; i += 2) {
16634         Jim_Obj *patObj = caseList[i];
16635 
16636         if (!Jim_CompareStringImmediate(interp, patObj, "default")
16637             || i < (patCount - 2)) {
16638             switch (matchOpt) {
16639                 case SWITCH_EXACT:
16640                     if (Jim_StringEqObj(strObj, patObj))
16641                         scriptObj = caseList[i + 1];
16642                     break;
16643                 case SWITCH_GLOB:
16644                     if (Jim_StringMatchObj(interp, patObj, strObj, 0))
16645                         scriptObj = caseList[i + 1];
16646                     break;
16647                 case SWITCH_RE:
16648                     command = Jim_NewStringObj(interp, "regexp", -1);
16649 
16650                 case SWITCH_CMD:{
16651                         int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, 0);
16652 
16653                         if (argc - opt == 1) {
16654                             JimListGetElements(interp, argv[opt], &patCount, &caseList);
16655                         }
16656 
16657                         if (rc < 0) {
16658                             return -rc;
16659                         }
16660                         if (rc)
16661                             scriptObj = caseList[i + 1];
16662                         break;
16663                     }
16664             }
16665         }
16666         else {
16667             scriptObj = caseList[i + 1];
16668         }
16669     }
16670     for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2)
16671         scriptObj = caseList[i + 1];
16672     if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) {
16673         Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
16674         return JIM_ERR;
16675     }
16676     Jim_SetEmptyResult(interp);
16677     if (scriptObj) {
16678         return Jim_EvalObj(interp, scriptObj);
16679     }
16680     return JIM_OK;
16681 }
16682 
16683 
Jim_ListCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16684 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16685 {
16686     Jim_Obj *listObjPtr;
16687 
16688     listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1);
16689     Jim_SetResult(interp, listObjPtr);
16690     return JIM_OK;
16691 }
16692 
16693 
Jim_LindexCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16694 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16695 {
16696     Jim_Obj *objPtr, *listObjPtr;
16697     int i;
16698     int idx;
16699 
16700     if (argc < 2) {
16701         Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
16702         return JIM_ERR;
16703     }
16704     objPtr = argv[1];
16705     Jim_IncrRefCount(objPtr);
16706     for (i = 2; i < argc; i++) {
16707         listObjPtr = objPtr;
16708         if (Jim_GetIndex(interp, argv[i], &idx) != JIM_OK) {
16709             Jim_DecrRefCount(interp, listObjPtr);
16710             return JIM_ERR;
16711         }
16712         if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_NONE) != JIM_OK) {
16713             Jim_DecrRefCount(interp, listObjPtr);
16714             Jim_SetEmptyResult(interp);
16715             return JIM_OK;
16716         }
16717         Jim_IncrRefCount(objPtr);
16718         Jim_DecrRefCount(interp, listObjPtr);
16719     }
16720     Jim_SetResult(interp, objPtr);
16721     Jim_DecrRefCount(interp, objPtr);
16722     return JIM_OK;
16723 }
16724 
16725 
Jim_LlengthCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16726 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16727 {
16728     if (argc != 2) {
16729         Jim_WrongNumArgs(interp, 1, argv, "list");
16730         return JIM_ERR;
16731     }
16732     Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
16733     return JIM_OK;
16734 }
16735 
16736 
Jim_LsearchCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16737 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16738 {
16739     static const char * const options[] = {
16740         "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
16741             NULL
16742     };
16743     enum
16744     { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
16745             OPT_COMMAND };
16746     int i;
16747     int opt_bool = 0;
16748     int opt_not = 0;
16749     int opt_nocase = 0;
16750     int opt_all = 0;
16751     int opt_inline = 0;
16752     int opt_match = OPT_EXACT;
16753     int listlen;
16754     int rc = JIM_OK;
16755     Jim_Obj *listObjPtr = NULL;
16756     Jim_Obj *commandObj = NULL;
16757 
16758     if (argc < 3) {
16759       wrongargs:
16760         Jim_WrongNumArgs(interp, 1, argv,
16761             "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? list value");
16762         return JIM_ERR;
16763     }
16764 
16765     for (i = 1; i < argc - 2; i++) {
16766         int option;
16767 
16768         if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
16769             return JIM_ERR;
16770         }
16771         switch (option) {
16772             case OPT_BOOL:
16773                 opt_bool = 1;
16774                 opt_inline = 0;
16775                 break;
16776             case OPT_NOT:
16777                 opt_not = 1;
16778                 break;
16779             case OPT_NOCASE:
16780                 opt_nocase = 1;
16781                 break;
16782             case OPT_INLINE:
16783                 opt_inline = 1;
16784                 opt_bool = 0;
16785                 break;
16786             case OPT_ALL:
16787                 opt_all = 1;
16788                 break;
16789             case OPT_COMMAND:
16790                 if (i >= argc - 2) {
16791                     goto wrongargs;
16792                 }
16793                 commandObj = argv[++i];
16794 
16795             case OPT_EXACT:
16796             case OPT_GLOB:
16797             case OPT_REGEXP:
16798                 opt_match = option;
16799                 break;
16800         }
16801     }
16802 
16803     argv += i;
16804 
16805     if (opt_all) {
16806         listObjPtr = Jim_NewListObj(interp, NULL, 0);
16807     }
16808     if (opt_match == OPT_REGEXP) {
16809         commandObj = Jim_NewStringObj(interp, "regexp", -1);
16810     }
16811     if (commandObj) {
16812         Jim_IncrRefCount(commandObj);
16813     }
16814 
16815     listlen = Jim_ListLength(interp, argv[0]);
16816     for (i = 0; i < listlen; i++) {
16817         int eq = 0;
16818         Jim_Obj *objPtr = Jim_ListGetIndex(interp, argv[0], i);
16819 
16820         switch (opt_match) {
16821             case OPT_EXACT:
16822                 eq = Jim_StringCompareObj(interp, argv[1], objPtr, opt_nocase) == 0;
16823                 break;
16824 
16825             case OPT_GLOB:
16826                 eq = Jim_StringMatchObj(interp, argv[1], objPtr, opt_nocase);
16827                 break;
16828 
16829             case OPT_REGEXP:
16830             case OPT_COMMAND:
16831                 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, opt_nocase);
16832                 if (eq < 0) {
16833                     if (listObjPtr) {
16834                         Jim_FreeNewObj(interp, listObjPtr);
16835                     }
16836                     rc = JIM_ERR;
16837                     goto done;
16838                 }
16839                 break;
16840         }
16841 
16842 
16843         if (!eq && opt_bool && opt_not && !opt_all) {
16844             continue;
16845         }
16846 
16847         if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
16848 
16849             Jim_Obj *resultObj;
16850 
16851             if (opt_bool) {
16852                 resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
16853             }
16854             else if (!opt_inline) {
16855                 resultObj = Jim_NewIntObj(interp, i);
16856             }
16857             else {
16858                 resultObj = objPtr;
16859             }
16860 
16861             if (opt_all) {
16862                 Jim_ListAppendElement(interp, listObjPtr, resultObj);
16863             }
16864             else {
16865                 Jim_SetResult(interp, resultObj);
16866                 goto done;
16867             }
16868         }
16869     }
16870 
16871     if (opt_all) {
16872         Jim_SetResult(interp, listObjPtr);
16873     }
16874     else {
16875 
16876         if (opt_bool) {
16877             Jim_SetResultBool(interp, opt_not);
16878         }
16879         else if (!opt_inline) {
16880             Jim_SetResultInt(interp, -1);
16881         }
16882     }
16883 
16884   done:
16885     if (commandObj) {
16886         Jim_DecrRefCount(interp, commandObj);
16887     }
16888     return rc;
16889 }
16890 
16891 
Jim_LappendCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16892 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16893 {
16894     Jim_Obj *listObjPtr;
16895     int new_obj = 0;
16896     int i;
16897 
16898     if (argc < 2) {
16899         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
16900         return JIM_ERR;
16901     }
16902     listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
16903     if (!listObjPtr) {
16904 
16905         listObjPtr = Jim_NewListObj(interp, NULL, 0);
16906         new_obj = 1;
16907     }
16908     else if (Jim_IsShared(listObjPtr)) {
16909         listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
16910         new_obj = 1;
16911     }
16912     for (i = 2; i < argc; i++)
16913         Jim_ListAppendElement(interp, listObjPtr, argv[i]);
16914     if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
16915         if (new_obj)
16916             Jim_FreeNewObj(interp, listObjPtr);
16917         return JIM_ERR;
16918     }
16919     Jim_SetResult(interp, listObjPtr);
16920     return JIM_OK;
16921 }
16922 
16923 
Jim_LinsertCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16924 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16925 {
16926     int idx, len;
16927     Jim_Obj *listPtr;
16928 
16929     if (argc < 3) {
16930         Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
16931         return JIM_ERR;
16932     }
16933     listPtr = argv[1];
16934     if (Jim_IsShared(listPtr))
16935         listPtr = Jim_DuplicateObj(interp, listPtr);
16936     if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
16937         goto err;
16938     len = Jim_ListLength(interp, listPtr);
16939     if (idx >= len)
16940         idx = len;
16941     else if (idx < 0)
16942         idx = len + idx + 1;
16943     Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
16944     Jim_SetResult(interp, listPtr);
16945     return JIM_OK;
16946   err:
16947     if (listPtr != argv[1]) {
16948         Jim_FreeNewObj(interp, listPtr);
16949     }
16950     return JIM_ERR;
16951 }
16952 
16953 
Jim_LreplaceCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16954 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16955 {
16956     int first, last, len, rangeLen;
16957     Jim_Obj *listObj;
16958     Jim_Obj *newListObj;
16959 
16960     if (argc < 4) {
16961         Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
16962         return JIM_ERR;
16963     }
16964     if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
16965         Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
16966         return JIM_ERR;
16967     }
16968 
16969     listObj = argv[1];
16970     len = Jim_ListLength(interp, listObj);
16971 
16972     first = JimRelToAbsIndex(len, first);
16973     last = JimRelToAbsIndex(len, last);
16974     JimRelToAbsRange(len, &first, &last, &rangeLen);
16975 
16976 
16977     if (first > len) {
16978         first = len;
16979     }
16980 
16981 
16982     newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
16983 
16984 
16985     ListInsertElements(newListObj, -1, argc - 4, argv + 4);
16986 
16987 
16988     ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
16989 
16990     Jim_SetResult(interp, newListObj);
16991     return JIM_OK;
16992 }
16993 
16994 
Jim_LsetCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16995 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16996 {
16997     if (argc < 3) {
16998         Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
16999         return JIM_ERR;
17000     }
17001     else if (argc == 3) {
17002 
17003         if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
17004             return JIM_ERR;
17005         Jim_SetResult(interp, argv[2]);
17006         return JIM_OK;
17007     }
17008     return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
17009 }
17010 
17011 
Jim_LsortCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const argv[])17012 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
17013 {
17014     static const char * const options[] = {
17015         "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", NULL
17016     };
17017     enum
17018     { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE };
17019     Jim_Obj *resObj;
17020     int i;
17021     int retCode;
17022     int shared;
17023 
17024     struct lsort_info info;
17025 
17026     if (argc < 2) {
17027         Jim_WrongNumArgs(interp, 1, argv, "?options? list");
17028         return JIM_ERR;
17029     }
17030 
17031     info.type = JIM_LSORT_ASCII;
17032     info.order = 1;
17033     info.indexed = 0;
17034     info.unique = 0;
17035     info.command = NULL;
17036     info.interp = interp;
17037 
17038     for (i = 1; i < (argc - 1); i++) {
17039         int option;
17040 
17041         if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
17042             != JIM_OK)
17043             return JIM_ERR;
17044         switch (option) {
17045             case OPT_ASCII:
17046                 info.type = JIM_LSORT_ASCII;
17047                 break;
17048             case OPT_NOCASE:
17049                 info.type = JIM_LSORT_NOCASE;
17050                 break;
17051             case OPT_INTEGER:
17052                 info.type = JIM_LSORT_INTEGER;
17053                 break;
17054             case OPT_REAL:
17055                 info.type = JIM_LSORT_REAL;
17056                 break;
17057             case OPT_INCREASING:
17058                 info.order = 1;
17059                 break;
17060             case OPT_DECREASING:
17061                 info.order = -1;
17062                 break;
17063             case OPT_UNIQUE:
17064                 info.unique = 1;
17065                 break;
17066             case OPT_COMMAND:
17067                 if (i >= (argc - 2)) {
17068                     Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
17069                     return JIM_ERR;
17070                 }
17071                 info.type = JIM_LSORT_COMMAND;
17072                 info.command = argv[i + 1];
17073                 i++;
17074                 break;
17075             case OPT_INDEX:
17076                 if (i >= (argc - 2)) {
17077                     Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
17078                     return JIM_ERR;
17079                 }
17080                 if (Jim_GetIndex(interp, argv[i + 1], &info.index) != JIM_OK) {
17081                     return JIM_ERR;
17082                 }
17083                 info.indexed = 1;
17084                 i++;
17085                 break;
17086         }
17087     }
17088     resObj = argv[argc - 1];
17089     if ((shared = Jim_IsShared(resObj)))
17090         resObj = Jim_DuplicateObj(interp, resObj);
17091     retCode = ListSortElements(interp, resObj, &info);
17092     if (retCode == JIM_OK) {
17093         Jim_SetResult(interp, resObj);
17094     }
17095     else if (shared) {
17096         Jim_FreeNewObj(interp, resObj);
17097     }
17098     return retCode;
17099 }
17100 
17101 
Jim_AppendCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17102 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17103 {
17104     Jim_Obj *stringObjPtr;
17105     int i;
17106 
17107     if (argc < 2) {
17108         Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
17109         return JIM_ERR;
17110     }
17111     if (argc == 2) {
17112         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17113         if (!stringObjPtr)
17114             return JIM_ERR;
17115     }
17116     else {
17117         int new_obj = 0;
17118         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
17119         if (!stringObjPtr) {
17120 
17121             stringObjPtr = Jim_NewEmptyStringObj(interp);
17122             new_obj = 1;
17123         }
17124         else if (Jim_IsShared(stringObjPtr)) {
17125             new_obj = 1;
17126             stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
17127         }
17128         for (i = 2; i < argc; i++) {
17129             Jim_AppendObj(interp, stringObjPtr, argv[i]);
17130         }
17131         if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
17132             if (new_obj) {
17133                 Jim_FreeNewObj(interp, stringObjPtr);
17134             }
17135             return JIM_ERR;
17136         }
17137     }
17138     Jim_SetResult(interp, stringObjPtr);
17139     return JIM_OK;
17140 }
17141 
17142 
17143 
Jim_DebugCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17144 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17145 {
17146 #if !defined(JIM_DEBUG_COMMAND)
17147     Jim_SetResultString(interp, "unsupported", -1);
17148     return JIM_ERR;
17149 #endif
17150 }
17151 
17152 
Jim_EvalCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17153 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17154 {
17155     int rc;
17156 
17157     if (argc < 2) {
17158         Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
17159         return JIM_ERR;
17160     }
17161 
17162     if (argc == 2) {
17163         rc = Jim_EvalObj(interp, argv[1]);
17164     }
17165     else {
17166         rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17167     }
17168 
17169     if (rc == JIM_ERR) {
17170 
17171         interp->addStackTrace++;
17172     }
17173     return rc;
17174 }
17175 
17176 
Jim_UplevelCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17177 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17178 {
17179     if (argc >= 2) {
17180         int retcode;
17181         Jim_CallFrame *savedCallFrame, *targetCallFrame;
17182         const char *str;
17183 
17184 
17185         savedCallFrame = interp->framePtr;
17186 
17187 
17188         str = Jim_String(argv[1]);
17189         if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
17190             targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17191             argc--;
17192             argv++;
17193         }
17194         else {
17195             targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17196         }
17197         if (targetCallFrame == NULL) {
17198             return JIM_ERR;
17199         }
17200         if (argc < 2) {
17201             Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
17202             return JIM_ERR;
17203         }
17204 
17205         interp->framePtr = targetCallFrame;
17206         if (argc == 2) {
17207             retcode = Jim_EvalObj(interp, argv[1]);
17208         }
17209         else {
17210             retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17211         }
17212         interp->framePtr = savedCallFrame;
17213         return retcode;
17214     }
17215     else {
17216         Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
17217         return JIM_ERR;
17218     }
17219 }
17220 
17221 
Jim_ExprCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17222 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17223 {
17224     int retcode;
17225 
17226     if (argc == 2) {
17227         retcode = Jim_EvalExpression(interp, argv[1]);
17228     }
17229     else if (argc > 2) {
17230         Jim_Obj *objPtr;
17231 
17232         objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
17233         Jim_IncrRefCount(objPtr);
17234         retcode = Jim_EvalExpression(interp, objPtr);
17235         Jim_DecrRefCount(interp, objPtr);
17236     }
17237     else {
17238         Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
17239         return JIM_ERR;
17240     }
17241     if (retcode != JIM_OK)
17242         return retcode;
17243     return JIM_OK;
17244 }
17245 
17246 
Jim_BreakCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17247 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17248 {
17249     if (argc != 1) {
17250         Jim_WrongNumArgs(interp, 1, argv, "");
17251         return JIM_ERR;
17252     }
17253     return JIM_BREAK;
17254 }
17255 
17256 
Jim_ContinueCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17257 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17258 {
17259     if (argc != 1) {
17260         Jim_WrongNumArgs(interp, 1, argv, "");
17261         return JIM_ERR;
17262     }
17263     return JIM_CONTINUE;
17264 }
17265 
17266 
Jim_ReturnCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17267 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17268 {
17269     int i;
17270     Jim_Obj *stackTraceObj = NULL;
17271     Jim_Obj *errorCodeObj = NULL;
17272     int returnCode = JIM_OK;
17273     long level = 1;
17274 
17275     for (i = 1; i < argc - 1; i += 2) {
17276         if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
17277             if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
17278                 return JIM_ERR;
17279             }
17280         }
17281         else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
17282             stackTraceObj = argv[i + 1];
17283         }
17284         else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
17285             errorCodeObj = argv[i + 1];
17286         }
17287         else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
17288             if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
17289                 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
17290                 return JIM_ERR;
17291             }
17292         }
17293         else {
17294             break;
17295         }
17296     }
17297 
17298     if (i != argc - 1 && i != argc) {
17299         Jim_WrongNumArgs(interp, 1, argv,
17300             "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
17301     }
17302 
17303 
17304     if (stackTraceObj && returnCode == JIM_ERR) {
17305         JimSetStackTrace(interp, stackTraceObj);
17306     }
17307 
17308     if (errorCodeObj && returnCode == JIM_ERR) {
17309         Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
17310     }
17311     interp->returnCode = returnCode;
17312     interp->returnLevel = level;
17313 
17314     if (i == argc - 1) {
17315         Jim_SetResult(interp, argv[i]);
17316     }
17317     return JIM_RETURN;
17318 }
17319 
17320 
Jim_TailcallCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17321 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17322 {
17323     if (interp->framePtr->level == 0) {
17324         Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
17325         return JIM_ERR;
17326     }
17327     else if (argc >= 2) {
17328 
17329         Jim_CallFrame *cf = interp->framePtr->parent;
17330 
17331         Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17332         if (cmdPtr == NULL) {
17333             return JIM_ERR;
17334         }
17335 
17336         JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));
17337 
17338 
17339         JimIncrCmdRefCount(cmdPtr);
17340         cf->tailcallCmd = cmdPtr;
17341 
17342 
17343         JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));
17344 
17345         cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
17346         Jim_IncrRefCount(cf->tailcallObj);
17347 
17348 
17349         return JIM_EVAL;
17350     }
17351     return JIM_OK;
17352 }
17353 
JimAliasCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17354 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17355 {
17356     Jim_Obj *cmdList;
17357     Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
17358 
17359 
17360     cmdList = Jim_DuplicateObj(interp, prefixListObj);
17361     Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
17362 
17363     return JimEvalObjList(interp, cmdList);
17364 }
17365 
JimAliasCmdDelete(Jim_Interp * interp,void * privData)17366 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
17367 {
17368     Jim_Obj *prefixListObj = privData;
17369     Jim_DecrRefCount(interp, prefixListObj);
17370 }
17371 
Jim_AliasCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17372 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17373 {
17374     Jim_Obj *prefixListObj;
17375     const char *newname;
17376 
17377     if (argc < 3) {
17378         Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
17379         return JIM_ERR;
17380     }
17381 
17382     prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
17383     Jim_IncrRefCount(prefixListObj);
17384     newname = Jim_String(argv[1]);
17385     if (newname[0] == ':' && newname[1] == ':') {
17386         while (*++newname == ':') {
17387         }
17388     }
17389 
17390     Jim_SetResult(interp, argv[1]);
17391 
17392     return Jim_CreateCommand(interp, newname, JimAliasCmd, prefixListObj, JimAliasCmdDelete);
17393 }
17394 
17395 
Jim_ProcCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17396 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17397 {
17398     Jim_Cmd *cmd;
17399 
17400     if (argc != 4 && argc != 5) {
17401         Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
17402         return JIM_ERR;
17403     }
17404 
17405     if (JimValidName(interp, "procedure", argv[1]) != JIM_OK) {
17406         return JIM_ERR;
17407     }
17408 
17409     if (argc == 4) {
17410         cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
17411     }
17412     else {
17413         cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
17414     }
17415 
17416     if (cmd) {
17417 
17418         Jim_Obj *qualifiedCmdNameObj;
17419         const char *cmdname = JimQualifyName(interp, Jim_String(argv[1]), &qualifiedCmdNameObj);
17420 
17421         JimCreateCommand(interp, cmdname, cmd);
17422 
17423 
17424         JimUpdateProcNamespace(interp, cmd, cmdname);
17425 
17426         JimFreeQualifiedName(interp, qualifiedCmdNameObj);
17427 
17428 
17429         Jim_SetResult(interp, argv[1]);
17430         return JIM_OK;
17431     }
17432     return JIM_ERR;
17433 }
17434 
17435 
Jim_LocalCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17436 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17437 {
17438     int retcode;
17439 
17440     if (argc < 2) {
17441         Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17442         return JIM_ERR;
17443     }
17444 
17445 
17446     interp->local++;
17447     retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17448     interp->local--;
17449 
17450 
17451 
17452     if (retcode == 0) {
17453         Jim_Obj *cmdNameObj = Jim_GetResult(interp);
17454 
17455         if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
17456             return JIM_ERR;
17457         }
17458         if (interp->framePtr->localCommands == NULL) {
17459             interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
17460             Jim_InitStack(interp->framePtr->localCommands);
17461         }
17462         Jim_IncrRefCount(cmdNameObj);
17463         Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
17464     }
17465 
17466     return retcode;
17467 }
17468 
17469 
Jim_UpcallCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17470 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17471 {
17472     if (argc < 2) {
17473         Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17474         return JIM_ERR;
17475     }
17476     else {
17477         int retcode;
17478 
17479         Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17480         if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
17481             Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
17482             return JIM_ERR;
17483         }
17484 
17485         cmdPtr->u.proc.upcall++;
17486         JimIncrCmdRefCount(cmdPtr);
17487 
17488 
17489         retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17490 
17491 
17492         cmdPtr->u.proc.upcall--;
17493         JimDecrCmdRefCount(interp, cmdPtr);
17494 
17495         return retcode;
17496     }
17497 }
17498 
17499 
Jim_ApplyCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17500 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17501 {
17502     if (argc < 2) {
17503         Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
17504         return JIM_ERR;
17505     }
17506     else {
17507         int ret;
17508         Jim_Cmd *cmd;
17509         Jim_Obj *argListObjPtr;
17510         Jim_Obj *bodyObjPtr;
17511         Jim_Obj *nsObj = NULL;
17512         Jim_Obj **nargv;
17513 
17514         int len = Jim_ListLength(interp, argv[1]);
17515         if (len != 2 && len != 3) {
17516             Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
17517             return JIM_ERR;
17518         }
17519 
17520         if (len == 3) {
17521 #ifdef jim_ext_namespace
17522 
17523             nsObj = JimQualifyNameObj(interp, Jim_ListGetIndex(interp, argv[1], 2));
17524 #else
17525             Jim_SetResultString(interp, "namespaces not enabled", -1);
17526             return JIM_ERR;
17527 #endif
17528         }
17529         argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
17530         bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
17531 
17532         cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
17533 
17534         if (cmd) {
17535 
17536             nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
17537             nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
17538             Jim_IncrRefCount(nargv[0]);
17539             memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
17540             ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
17541             Jim_DecrRefCount(interp, nargv[0]);
17542             Jim_Free(nargv);
17543 
17544             JimDecrCmdRefCount(interp, cmd);
17545             return ret;
17546         }
17547         return JIM_ERR;
17548     }
17549 }
17550 
17551 
17552 
Jim_ConcatCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17553 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17554 {
17555     Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17556     return JIM_OK;
17557 }
17558 
17559 
Jim_UpvarCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17560 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17561 {
17562     int i;
17563     Jim_CallFrame *targetCallFrame;
17564 
17565 
17566     if (argc > 3 && (argc % 2 == 0)) {
17567         targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17568         argc--;
17569         argv++;
17570     }
17571     else {
17572         targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17573     }
17574     if (targetCallFrame == NULL) {
17575         return JIM_ERR;
17576     }
17577 
17578 
17579     if (argc < 3) {
17580         Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
17581         return JIM_ERR;
17582     }
17583 
17584 
17585     for (i = 1; i < argc; i += 2) {
17586         if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
17587             return JIM_ERR;
17588     }
17589     return JIM_OK;
17590 }
17591 
17592 
Jim_GlobalCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17593 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17594 {
17595     int i;
17596 
17597     if (argc < 2) {
17598         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
17599         return JIM_ERR;
17600     }
17601 
17602     if (interp->framePtr->level == 0)
17603         return JIM_OK;
17604     for (i = 1; i < argc; i++) {
17605 
17606         const char *name = Jim_String(argv[i]);
17607         if (name[0] != ':' || name[1] != ':') {
17608             if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
17609                 return JIM_ERR;
17610         }
17611     }
17612     return JIM_OK;
17613 }
17614 
JimStringMap(Jim_Interp * interp,Jim_Obj * mapListObjPtr,Jim_Obj * objPtr,int nocase)17615 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
17616     Jim_Obj *objPtr, int nocase)
17617 {
17618     int numMaps;
17619     const char *str, *noMatchStart = NULL;
17620     int strLen, i;
17621     Jim_Obj *resultObjPtr;
17622 
17623     numMaps = Jim_ListLength(interp, mapListObjPtr);
17624     if (numMaps % 2) {
17625         Jim_SetResultString(interp, "list must contain an even number of elements", -1);
17626         return NULL;
17627     }
17628 
17629     str = Jim_String(objPtr);
17630     strLen = Jim_Utf8Length(interp, objPtr);
17631 
17632 
17633     resultObjPtr = Jim_NewStringObj(interp, "", 0);
17634     while (strLen) {
17635         for (i = 0; i < numMaps; i += 2) {
17636             Jim_Obj *eachObjPtr;
17637             const char *k;
17638             int kl;
17639 
17640             eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
17641             k = Jim_String(eachObjPtr);
17642             kl = Jim_Utf8Length(interp, eachObjPtr);
17643 
17644             if (strLen >= kl && kl) {
17645                 int rc;
17646                 rc = JimStringCompareLen(str, k, kl, nocase);
17647                 if (rc == 0) {
17648                     if (noMatchStart) {
17649                         Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17650                         noMatchStart = NULL;
17651                     }
17652                     Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
17653                     str += utf8_index(str, kl);
17654                     strLen -= kl;
17655                     break;
17656                 }
17657             }
17658         }
17659         if (i == numMaps) {
17660             int c;
17661             if (noMatchStart == NULL)
17662                 noMatchStart = str;
17663             str += utf8_tounicode(str, &c);
17664             strLen--;
17665         }
17666     }
17667     if (noMatchStart) {
17668         Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17669     }
17670     return resultObjPtr;
17671 }
17672 
17673 
Jim_StringCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17674 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17675 {
17676     int len;
17677     int opt_case = 1;
17678     int option;
17679     static const char * const options[] = {
17680         "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace",
17681         "map", "repeat", "reverse", "index", "first", "last", "cat",
17682         "trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL
17683     };
17684     enum
17685     {
17686         OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_REPLACE,
17687         OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST, OPT_CAT,
17688         OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE
17689     };
17690     static const char * const nocase_options[] = {
17691         "-nocase", NULL
17692     };
17693     static const char * const nocase_length_options[] = {
17694         "-nocase", "-length", NULL
17695     };
17696 
17697     if (argc < 2) {
17698         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
17699         return JIM_ERR;
17700     }
17701     if (Jim_GetEnum(interp, argv[1], options, &option, NULL,
17702             JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
17703         return Jim_CheckShowCommands(interp, argv[1], options);
17704 
17705     switch (option) {
17706         case OPT_LENGTH:
17707         case OPT_BYTELENGTH:
17708             if (argc != 3) {
17709                 Jim_WrongNumArgs(interp, 2, argv, "string");
17710                 return JIM_ERR;
17711             }
17712             if (option == OPT_LENGTH) {
17713                 len = Jim_Utf8Length(interp, argv[2]);
17714             }
17715             else {
17716                 len = Jim_Length(argv[2]);
17717             }
17718             Jim_SetResultInt(interp, len);
17719             return JIM_OK;
17720 
17721         case OPT_CAT:{
17722                 Jim_Obj *objPtr;
17723                 if (argc == 3) {
17724 
17725                     objPtr = argv[2];
17726                 }
17727                 else {
17728                     int i;
17729 
17730                     objPtr = Jim_NewStringObj(interp, "", 0);
17731 
17732                     for (i = 2; i < argc; i++) {
17733                         Jim_AppendObj(interp, objPtr, argv[i]);
17734                     }
17735                 }
17736                 Jim_SetResult(interp, objPtr);
17737                 return JIM_OK;
17738             }
17739 
17740         case OPT_COMPARE:
17741         case OPT_EQUAL:
17742             {
17743 
17744                 long opt_length = -1;
17745                 int n = argc - 4;
17746                 int i = 2;
17747                 while (n > 0) {
17748                     int subopt;
17749                     if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
17750                             JIM_ENUM_ABBREV) != JIM_OK) {
17751 badcompareargs:
17752                         Jim_WrongNumArgs(interp, 2, argv, "?-nocase? ?-length int? string1 string2");
17753                         return JIM_ERR;
17754                     }
17755                     if (subopt == 0) {
17756 
17757                         opt_case = 0;
17758                         n--;
17759                     }
17760                     else {
17761 
17762                         if (n < 2) {
17763                             goto badcompareargs;
17764                         }
17765                         if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
17766                             return JIM_ERR;
17767                         }
17768                         n -= 2;
17769                     }
17770                 }
17771                 if (n) {
17772                     goto badcompareargs;
17773                 }
17774                 argv += argc - 2;
17775                 if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
17776 
17777                     Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
17778                 }
17779                 else {
17780                     if (opt_length >= 0) {
17781                         n = JimStringCompareLen(Jim_String(argv[0]), Jim_String(argv[1]), opt_length, !opt_case);
17782                     }
17783                     else {
17784                         n = Jim_StringCompareObj(interp, argv[0], argv[1], !opt_case);
17785                     }
17786                     Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
17787                 }
17788                 return JIM_OK;
17789             }
17790 
17791         case OPT_MATCH:
17792             if (argc != 4 &&
17793                 (argc != 5 ||
17794                     Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17795                         JIM_ENUM_ABBREV) != JIM_OK)) {
17796                 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
17797                 return JIM_ERR;
17798             }
17799             if (opt_case == 0) {
17800                 argv++;
17801             }
17802             Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
17803             return JIM_OK;
17804 
17805         case OPT_MAP:{
17806                 Jim_Obj *objPtr;
17807 
17808                 if (argc != 4 &&
17809                     (argc != 5 ||
17810                         Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17811                             JIM_ENUM_ABBREV) != JIM_OK)) {
17812                     Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
17813                     return JIM_ERR;
17814                 }
17815 
17816                 if (opt_case == 0) {
17817                     argv++;
17818                 }
17819                 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
17820                 if (objPtr == NULL) {
17821                     return JIM_ERR;
17822                 }
17823                 Jim_SetResult(interp, objPtr);
17824                 return JIM_OK;
17825             }
17826 
17827         case OPT_RANGE:
17828         case OPT_BYTERANGE:{
17829                 Jim_Obj *objPtr;
17830 
17831                 if (argc != 5) {
17832                     Jim_WrongNumArgs(interp, 2, argv, "string first last");
17833                     return JIM_ERR;
17834                 }
17835                 if (option == OPT_RANGE) {
17836                     objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
17837                 }
17838                 else
17839                 {
17840                     objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
17841                 }
17842 
17843                 if (objPtr == NULL) {
17844                     return JIM_ERR;
17845                 }
17846                 Jim_SetResult(interp, objPtr);
17847                 return JIM_OK;
17848             }
17849 
17850         case OPT_REPLACE:{
17851                 Jim_Obj *objPtr;
17852 
17853                 if (argc != 5 && argc != 6) {
17854                     Jim_WrongNumArgs(interp, 2, argv, "string first last ?string?");
17855                     return JIM_ERR;
17856                 }
17857                 objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
17858                 if (objPtr == NULL) {
17859                     return JIM_ERR;
17860                 }
17861                 Jim_SetResult(interp, objPtr);
17862                 return JIM_OK;
17863             }
17864 
17865 
17866         case OPT_REPEAT:{
17867                 Jim_Obj *objPtr;
17868                 jim_wide count;
17869 
17870                 if (argc != 4) {
17871                     Jim_WrongNumArgs(interp, 2, argv, "string count");
17872                     return JIM_ERR;
17873                 }
17874                 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK) {
17875                     return JIM_ERR;
17876                 }
17877                 objPtr = Jim_NewStringObj(interp, "", 0);
17878                 if (count > 0) {
17879                     while (count--) {
17880                         Jim_AppendObj(interp, objPtr, argv[2]);
17881                     }
17882                 }
17883                 Jim_SetResult(interp, objPtr);
17884                 return JIM_OK;
17885             }
17886 
17887         case OPT_REVERSE:{
17888                 char *buf, *p;
17889                 const char *str;
17890                 int i;
17891 
17892                 if (argc != 3) {
17893                     Jim_WrongNumArgs(interp, 2, argv, "string");
17894                     return JIM_ERR;
17895                 }
17896 
17897                 str = Jim_GetString(argv[2], &len);
17898                 buf = Jim_Alloc(len + 1);
17899                 p = buf + len;
17900                 *p = 0;
17901                 for (i = 0; i < len; ) {
17902                     int c;
17903                     int l = utf8_tounicode(str, &c);
17904                     memcpy(p - l, str, l);
17905                     p -= l;
17906                     i += l;
17907                     str += l;
17908                 }
17909                 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
17910                 return JIM_OK;
17911             }
17912 
17913         case OPT_INDEX:{
17914                 int idx;
17915                 const char *str;
17916 
17917                 if (argc != 4) {
17918                     Jim_WrongNumArgs(interp, 2, argv, "string index");
17919                     return JIM_ERR;
17920                 }
17921                 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
17922                     return JIM_ERR;
17923                 }
17924                 str = Jim_String(argv[2]);
17925                 len = Jim_Utf8Length(interp, argv[2]);
17926                 if (idx != INT_MIN && idx != INT_MAX) {
17927                     idx = JimRelToAbsIndex(len, idx);
17928                 }
17929                 if (idx < 0 || idx >= len || str == NULL) {
17930                     Jim_SetResultString(interp, "", 0);
17931                 }
17932                 else if (len == Jim_Length(argv[2])) {
17933 
17934                     Jim_SetResultString(interp, str + idx, 1);
17935                 }
17936                 else {
17937                     int c;
17938                     int i = utf8_index(str, idx);
17939                     Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
17940                 }
17941                 return JIM_OK;
17942             }
17943 
17944         case OPT_FIRST:
17945         case OPT_LAST:{
17946                 int idx = 0, l1, l2;
17947                 const char *s1, *s2;
17948 
17949                 if (argc != 4 && argc != 5) {
17950                     Jim_WrongNumArgs(interp, 2, argv, "subString string ?index?");
17951                     return JIM_ERR;
17952                 }
17953                 s1 = Jim_String(argv[2]);
17954                 s2 = Jim_String(argv[3]);
17955                 l1 = Jim_Utf8Length(interp, argv[2]);
17956                 l2 = Jim_Utf8Length(interp, argv[3]);
17957                 if (argc == 5) {
17958                     if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
17959                         return JIM_ERR;
17960                     }
17961                     idx = JimRelToAbsIndex(l2, idx);
17962                 }
17963                 else if (option == OPT_LAST) {
17964                     idx = l2;
17965                 }
17966                 if (option == OPT_FIRST) {
17967                     Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
17968                 }
17969                 else {
17970 #ifdef JIM_UTF8
17971                     Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
17972 #else
17973                     Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
17974 #endif
17975                 }
17976                 return JIM_OK;
17977             }
17978 
17979         case OPT_TRIM:
17980         case OPT_TRIMLEFT:
17981         case OPT_TRIMRIGHT:{
17982                 Jim_Obj *trimchars;
17983 
17984                 if (argc != 3 && argc != 4) {
17985                     Jim_WrongNumArgs(interp, 2, argv, "string ?trimchars?");
17986                     return JIM_ERR;
17987                 }
17988                 trimchars = (argc == 4 ? argv[3] : NULL);
17989                 if (option == OPT_TRIM) {
17990                     Jim_SetResult(interp, JimStringTrim(interp, argv[2], trimchars));
17991                 }
17992                 else if (option == OPT_TRIMLEFT) {
17993                     Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], trimchars));
17994                 }
17995                 else if (option == OPT_TRIMRIGHT) {
17996                     Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], trimchars));
17997                 }
17998                 return JIM_OK;
17999             }
18000 
18001         case OPT_TOLOWER:
18002         case OPT_TOUPPER:
18003         case OPT_TOTITLE:
18004             if (argc != 3) {
18005                 Jim_WrongNumArgs(interp, 2, argv, "string");
18006                 return JIM_ERR;
18007             }
18008             if (option == OPT_TOLOWER) {
18009                 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
18010             }
18011             else if (option == OPT_TOUPPER) {
18012                 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
18013             }
18014             else {
18015                 Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
18016             }
18017             return JIM_OK;
18018 
18019         case OPT_IS:
18020             if (argc == 4 || (argc == 5 && Jim_CompareStringImmediate(interp, argv[3], "-strict"))) {
18021                 return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
18022             }
18023             Jim_WrongNumArgs(interp, 2, argv, "class ?-strict? str");
18024             return JIM_ERR;
18025     }
18026     return JIM_OK;
18027 }
18028 
18029 
Jim_TimeCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18030 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18031 {
18032     long i, count = 1;
18033     jim_wide start, elapsed;
18034     char buf[60];
18035     const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
18036 
18037     if (argc < 2) {
18038         Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
18039         return JIM_ERR;
18040     }
18041     if (argc == 3) {
18042         if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
18043             return JIM_ERR;
18044     }
18045     if (count < 0)
18046         return JIM_OK;
18047     i = count;
18048     start = JimClock();
18049     while (i-- > 0) {
18050         int retval;
18051 
18052         retval = Jim_EvalObj(interp, argv[1]);
18053         if (retval != JIM_OK) {
18054             return retval;
18055         }
18056     }
18057     elapsed = JimClock() - start;
18058     sprintf(buf, fmt, count == 0 ? 0 : elapsed / count);
18059     Jim_SetResultString(interp, buf, -1);
18060     return JIM_OK;
18061 }
18062 
18063 
Jim_ExitCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18064 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18065 {
18066     long exitCode = 0;
18067 
18068     if (argc > 2) {
18069         Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
18070         return JIM_ERR;
18071     }
18072     if (argc == 2) {
18073         if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
18074             return JIM_ERR;
18075     }
18076     interp->exitCode = exitCode;
18077     return JIM_EXIT;
18078 }
18079 
18080 
Jim_CatchCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18081 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18082 {
18083     int exitCode = 0;
18084     int i;
18085     int sig = 0;
18086 
18087 
18088     jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
18089     static const int max_ignore_code = sizeof(ignore_mask) * 8;
18090 
18091     Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
18092 
18093     for (i = 1; i < argc - 1; i++) {
18094         const char *arg = Jim_String(argv[i]);
18095         jim_wide option;
18096         int ignore;
18097 
18098 
18099         if (strcmp(arg, "--") == 0) {
18100             i++;
18101             break;
18102         }
18103         if (*arg != '-') {
18104             break;
18105         }
18106 
18107         if (strncmp(arg, "-no", 3) == 0) {
18108             arg += 3;
18109             ignore = 1;
18110         }
18111         else {
18112             arg++;
18113             ignore = 0;
18114         }
18115 
18116         if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
18117             option = -1;
18118         }
18119         if (option < 0) {
18120             option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
18121         }
18122         if (option < 0) {
18123             goto wrongargs;
18124         }
18125 
18126         if (ignore) {
18127             ignore_mask |= ((jim_wide)1 << option);
18128         }
18129         else {
18130             ignore_mask &= (~((jim_wide)1 << option));
18131         }
18132     }
18133 
18134     argc -= i;
18135     if (argc < 1 || argc > 3) {
18136       wrongargs:
18137         Jim_WrongNumArgs(interp, 1, argv,
18138             "?-?no?code ... --? script ?resultVarName? ?optionVarName?");
18139         return JIM_ERR;
18140     }
18141     argv += i;
18142 
18143     if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
18144         sig++;
18145     }
18146 
18147     interp->signal_level += sig;
18148     if (Jim_CheckSignal(interp)) {
18149 
18150         exitCode = JIM_SIGNAL;
18151     }
18152     else {
18153         exitCode = Jim_EvalObj(interp, argv[0]);
18154 
18155         interp->errorFlag = 0;
18156     }
18157     interp->signal_level -= sig;
18158 
18159 
18160     if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
18161 
18162         return exitCode;
18163     }
18164 
18165     if (sig && exitCode == JIM_SIGNAL) {
18166 
18167         if (interp->signal_set_result) {
18168             interp->signal_set_result(interp, interp->sigmask);
18169         }
18170         else {
18171             Jim_SetResultInt(interp, interp->sigmask);
18172         }
18173         interp->sigmask = 0;
18174     }
18175 
18176     if (argc >= 2) {
18177         if (Jim_SetVariable(interp, argv[1], Jim_GetResult(interp)) != JIM_OK) {
18178             return JIM_ERR;
18179         }
18180         if (argc == 3) {
18181             Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
18182 
18183             Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
18184             Jim_ListAppendElement(interp, optListObj,
18185                 Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
18186             Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
18187             Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
18188             if (exitCode == JIM_ERR) {
18189                 Jim_Obj *errorCode;
18190                 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
18191                     -1));
18192                 Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
18193 
18194                 errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
18195                 if (errorCode) {
18196                     Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
18197                     Jim_ListAppendElement(interp, optListObj, errorCode);
18198                 }
18199             }
18200             if (Jim_SetVariable(interp, argv[2], optListObj) != JIM_OK) {
18201                 return JIM_ERR;
18202             }
18203         }
18204     }
18205     Jim_SetResultInt(interp, exitCode);
18206     return JIM_OK;
18207 }
18208 
18209 
18210 
Jim_RenameCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18211 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18212 {
18213     if (argc != 3) {
18214         Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
18215         return JIM_ERR;
18216     }
18217 
18218     if (JimValidName(interp, "new procedure", argv[2])) {
18219         return JIM_ERR;
18220     }
18221 
18222     return Jim_RenameCommand(interp, Jim_String(argv[1]), Jim_String(argv[2]));
18223 }
18224 
18225 #define JIM_DICTMATCH_KEYS 0x0001
18226 #define JIM_DICTMATCH_VALUES 0x002
18227 
Jim_DictMatchTypes(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * patternObj,int match_type,int return_types)18228 int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
18229 {
18230     Jim_HashEntry *he;
18231     Jim_Obj *listObjPtr;
18232     Jim_HashTableIterator htiter;
18233 
18234     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18235         return JIM_ERR;
18236     }
18237 
18238     listObjPtr = Jim_NewListObj(interp, NULL, 0);
18239 
18240     JimInitHashTableIterator(objPtr->internalRep.ptr, &htiter);
18241     while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18242         if (patternObj) {
18243             Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? (Jim_Obj *)he->key : Jim_GetHashEntryVal(he);
18244             if (!JimGlobMatch(Jim_String(patternObj), Jim_String(matchObj), 0)) {
18245 
18246                 continue;
18247             }
18248         }
18249         if (return_types & JIM_DICTMATCH_KEYS) {
18250             Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key);
18251         }
18252         if (return_types & JIM_DICTMATCH_VALUES) {
18253             Jim_ListAppendElement(interp, listObjPtr, Jim_GetHashEntryVal(he));
18254         }
18255     }
18256 
18257     Jim_SetResult(interp, listObjPtr);
18258     return JIM_OK;
18259 }
18260 
Jim_DictSize(Jim_Interp * interp,Jim_Obj * objPtr)18261 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
18262 {
18263     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18264         return -1;
18265     }
18266     return ((Jim_HashTable *)objPtr->internalRep.ptr)->used;
18267 }
18268 
Jim_DictMerge(Jim_Interp * interp,int objc,Jim_Obj * const * objv)18269 Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
18270 {
18271     Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
18272     int i;
18273 
18274     JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));
18275 
18276 
18277 
18278     for (i = 0; i < objc; i++) {
18279         Jim_HashTable *ht;
18280         Jim_HashTableIterator htiter;
18281         Jim_HashEntry *he;
18282 
18283         if (SetDictFromAny(interp, objv[i]) != JIM_OK) {
18284             Jim_FreeNewObj(interp, objPtr);
18285             return NULL;
18286         }
18287         ht = objv[i]->internalRep.ptr;
18288         JimInitHashTableIterator(ht, &htiter);
18289         while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18290             Jim_ReplaceHashEntry(objPtr->internalRep.ptr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he));
18291         }
18292     }
18293     return objPtr;
18294 }
18295 
Jim_DictInfo(Jim_Interp * interp,Jim_Obj * objPtr)18296 int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
18297 {
18298     Jim_HashTable *ht;
18299     unsigned int i;
18300     char buffer[100];
18301     int sum = 0;
18302     int nonzero_count = 0;
18303     Jim_Obj *output;
18304     int bucket_counts[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
18305 
18306     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18307         return JIM_ERR;
18308     }
18309 
18310     ht = (Jim_HashTable *)objPtr->internalRep.ptr;
18311 
18312 
18313     snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets\n", ht->used, ht->size);
18314     output = Jim_NewStringObj(interp, buffer, -1);
18315 
18316     for (i = 0; i < ht->size; i++) {
18317         Jim_HashEntry *he = ht->table[i];
18318         int entries = 0;
18319         while (he) {
18320             entries++;
18321             he = he->next;
18322         }
18323         if (entries > 9) {
18324             bucket_counts[10]++;
18325         }
18326         else {
18327             bucket_counts[entries]++;
18328         }
18329         if (entries) {
18330             sum += entries;
18331             nonzero_count++;
18332         }
18333     }
18334     for (i = 0; i < 10; i++) {
18335         snprintf(buffer, sizeof(buffer), "number of buckets with %d entries: %d\n", i, bucket_counts[i]);
18336         Jim_AppendString(interp, output, buffer, -1);
18337     }
18338     snprintf(buffer, sizeof(buffer), "number of buckets with 10 or more entries: %d\n", bucket_counts[10]);
18339     Jim_AppendString(interp, output, buffer, -1);
18340     snprintf(buffer, sizeof(buffer), "average search distance for entry: %.1f", nonzero_count ? (double)sum / nonzero_count : 0.0);
18341     Jim_AppendString(interp, output, buffer, -1);
18342     Jim_SetResult(interp, output);
18343     return JIM_OK;
18344 }
18345 
Jim_EvalEnsemble(Jim_Interp * interp,const char * basecmd,const char * subcmd,int argc,Jim_Obj * const * argv)18346 static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
18347 {
18348     Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
18349 
18350     Jim_AppendString(interp, prefixObj, " ", 1);
18351     Jim_AppendString(interp, prefixObj, subcmd, -1);
18352 
18353     return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
18354 }
18355 
JimDictWith(Jim_Interp * interp,Jim_Obj * dictVarName,Jim_Obj * const * keyv,int keyc,Jim_Obj * scriptObj)18356 static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj)
18357 {
18358     int i;
18359     Jim_Obj *objPtr;
18360     Jim_Obj *dictObj;
18361     Jim_Obj **dictValues;
18362     int len;
18363     int ret = JIM_OK;
18364 
18365 
18366     dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
18367     if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
18368         return JIM_ERR;
18369     }
18370 
18371     if (Jim_DictPairs(interp, objPtr, &dictValues, &len) == JIM_ERR) {
18372         return JIM_ERR;
18373     }
18374     for (i = 0; i < len; i += 2) {
18375         if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
18376             Jim_Free(dictValues);
18377             return JIM_ERR;
18378         }
18379     }
18380 
18381 
18382     if (Jim_Length(scriptObj)) {
18383         ret = Jim_EvalObj(interp, scriptObj);
18384 
18385 
18386         if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {
18387 
18388             Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
18389             for (i = 0; i < keyc; i++) {
18390                 newkeyv[i] = keyv[i];
18391             }
18392 
18393             for (i = 0; i < len; i += 2) {
18394 
18395                 objPtr = Jim_GetVariable(interp, dictValues[i], 0);
18396                 newkeyv[keyc] = dictValues[i];
18397                 Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, 0);
18398             }
18399             Jim_Free(newkeyv);
18400         }
18401     }
18402 
18403     Jim_Free(dictValues);
18404 
18405     return ret;
18406 }
18407 
18408 
Jim_DictCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18409 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18410 {
18411     Jim_Obj *objPtr;
18412     int types = JIM_DICTMATCH_KEYS;
18413     int option;
18414     static const char * const options[] = {
18415         "create", "get", "set", "unset", "exists", "keys", "size", "info",
18416         "merge", "with", "append", "lappend", "incr", "remove", "values", "for",
18417         "replace", "update", NULL
18418     };
18419     enum
18420     {
18421         OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXISTS, OPT_KEYS, OPT_SIZE, OPT_INFO,
18422         OPT_MERGE, OPT_WITH, OPT_APPEND, OPT_LAPPEND, OPT_INCR, OPT_REMOVE, OPT_VALUES, OPT_FOR,
18423         OPT_REPLACE, OPT_UPDATE,
18424     };
18425 
18426     if (argc < 2) {
18427         Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arguments ...?");
18428         return JIM_ERR;
18429     }
18430 
18431     if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) {
18432         return Jim_CheckShowCommands(interp, argv[1], options);
18433     }
18434 
18435     switch (option) {
18436         case OPT_GET:
18437             if (argc < 3) {
18438                 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?key ...?");
18439                 return JIM_ERR;
18440             }
18441             if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
18442                     JIM_ERRMSG) != JIM_OK) {
18443                 return JIM_ERR;
18444             }
18445             Jim_SetResult(interp, objPtr);
18446             return JIM_OK;
18447 
18448         case OPT_SET:
18449             if (argc < 5) {
18450                 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
18451                 return JIM_ERR;
18452             }
18453             return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
18454 
18455         case OPT_EXISTS:
18456             if (argc < 4) {
18457                 Jim_WrongNumArgs(interp, 2, argv, "dictionary key ?key ...?");
18458                 return JIM_ERR;
18459             }
18460             else {
18461                 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_ERRMSG);
18462                 if (rc < 0) {
18463                     return JIM_ERR;
18464                 }
18465                 Jim_SetResultBool(interp,  rc == JIM_OK);
18466                 return JIM_OK;
18467             }
18468 
18469         case OPT_UNSET:
18470             if (argc < 4) {
18471                 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
18472                 return JIM_ERR;
18473             }
18474             if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, 0) != JIM_OK) {
18475                 return JIM_ERR;
18476             }
18477             return JIM_OK;
18478 
18479         case OPT_VALUES:
18480             types = JIM_DICTMATCH_VALUES;
18481 
18482         case OPT_KEYS:
18483             if (argc != 3 && argc != 4) {
18484                 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?pattern?");
18485                 return JIM_ERR;
18486             }
18487             return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);
18488 
18489         case OPT_SIZE:
18490             if (argc != 3) {
18491                 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18492                 return JIM_ERR;
18493             }
18494             else if (Jim_DictSize(interp, argv[2]) < 0) {
18495                 return JIM_ERR;
18496             }
18497             Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
18498             return JIM_OK;
18499 
18500         case OPT_MERGE:
18501             if (argc == 2) {
18502                 return JIM_OK;
18503             }
18504             objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
18505             if (objPtr == NULL) {
18506                 return JIM_ERR;
18507             }
18508             Jim_SetResult(interp, objPtr);
18509             return JIM_OK;
18510 
18511         case OPT_UPDATE:
18512             if (argc < 6 || argc % 2) {
18513 
18514                 argc = 2;
18515             }
18516             break;
18517 
18518         case OPT_CREATE:
18519             if (argc % 2) {
18520                 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
18521                 return JIM_ERR;
18522             }
18523             objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
18524             Jim_SetResult(interp, objPtr);
18525             return JIM_OK;
18526 
18527         case OPT_INFO:
18528             if (argc != 3) {
18529                 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18530                 return JIM_ERR;
18531             }
18532             return Jim_DictInfo(interp, argv[2]);
18533 
18534         case OPT_WITH:
18535             if (argc < 4) {
18536                 Jim_WrongNumArgs(interp, 2, argv, "dictVar ?key ...? script");
18537                 return JIM_ERR;
18538             }
18539             return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
18540     }
18541 
18542     return Jim_EvalEnsemble(interp, "dict", options[option], argc - 2, argv + 2);
18543 }
18544 
18545 
Jim_SubstCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18546 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18547 {
18548     static const char * const options[] = {
18549         "-nobackslashes", "-nocommands", "-novariables", NULL
18550     };
18551     enum
18552     { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
18553     int i;
18554     int flags = JIM_SUBST_FLAG;
18555     Jim_Obj *objPtr;
18556 
18557     if (argc < 2) {
18558         Jim_WrongNumArgs(interp, 1, argv, "?options? string");
18559         return JIM_ERR;
18560     }
18561     for (i = 1; i < (argc - 1); i++) {
18562         int option;
18563 
18564         if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
18565                 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18566             return JIM_ERR;
18567         }
18568         switch (option) {
18569             case OPT_NOBACKSLASHES:
18570                 flags |= JIM_SUBST_NOESC;
18571                 break;
18572             case OPT_NOCOMMANDS:
18573                 flags |= JIM_SUBST_NOCMD;
18574                 break;
18575             case OPT_NOVARIABLES:
18576                 flags |= JIM_SUBST_NOVAR;
18577                 break;
18578         }
18579     }
18580     if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
18581         return JIM_ERR;
18582     }
18583     Jim_SetResult(interp, objPtr);
18584     return JIM_OK;
18585 }
18586 
18587 
Jim_InfoCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18588 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18589 {
18590     int cmd;
18591     Jim_Obj *objPtr;
18592     int mode = 0;
18593 
18594     static const char * const commands[] = {
18595         "body", "statics", "commands", "procs", "channels", "exists", "globals", "level", "frame", "locals",
18596         "vars", "version", "patchlevel", "complete", "args", "hostname",
18597         "script", "source", "stacktrace", "nameofexecutable", "returncodes",
18598         "references", "alias", NULL
18599     };
18600     enum
18601     { INFO_BODY, INFO_STATICS, INFO_COMMANDS, INFO_PROCS, INFO_CHANNELS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
18602         INFO_FRAME, INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_PATCHLEVEL, INFO_COMPLETE, INFO_ARGS,
18603         INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE,
18604         INFO_RETURNCODES, INFO_REFERENCES, INFO_ALIAS,
18605     };
18606 
18607 #ifdef jim_ext_namespace
18608     int nons = 0;
18609 
18610     if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
18611 
18612         argc--;
18613         argv++;
18614         nons = 1;
18615     }
18616 #endif
18617 
18618     if (argc < 2) {
18619         Jim_WrongNumArgs(interp, 1, argv, "subcommand ?args ...?");
18620         return JIM_ERR;
18621     }
18622     if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18623         return Jim_CheckShowCommands(interp, argv[1], commands);
18624     }
18625 
18626 
18627     switch (cmd) {
18628         case INFO_EXISTS:
18629             if (argc != 3) {
18630                 Jim_WrongNumArgs(interp, 2, argv, "varName");
18631                 return JIM_ERR;
18632             }
18633             Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
18634             break;
18635 
18636         case INFO_ALIAS:{
18637             Jim_Cmd *cmdPtr;
18638 
18639             if (argc != 3) {
18640                 Jim_WrongNumArgs(interp, 2, argv, "command");
18641                 return JIM_ERR;
18642             }
18643             if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18644                 return JIM_ERR;
18645             }
18646             if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
18647                 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
18648                 return JIM_ERR;
18649             }
18650             Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
18651             return JIM_OK;
18652         }
18653 
18654         case INFO_CHANNELS:
18655             mode++;
18656 #ifndef jim_ext_aio
18657             Jim_SetResultString(interp, "aio not enabled", -1);
18658             return JIM_ERR;
18659 #endif
18660 
18661         case INFO_PROCS:
18662             mode++;
18663 
18664         case INFO_COMMANDS:
18665 
18666             if (argc != 2 && argc != 3) {
18667                 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18668                 return JIM_ERR;
18669             }
18670 #ifdef jim_ext_namespace
18671             if (!nons) {
18672                 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18673                     return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18674                 }
18675             }
18676 #endif
18677             Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
18678             break;
18679 
18680         case INFO_VARS:
18681             mode++;
18682 
18683         case INFO_LOCALS:
18684             mode++;
18685 
18686         case INFO_GLOBALS:
18687 
18688             if (argc != 2 && argc != 3) {
18689                 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18690                 return JIM_ERR;
18691             }
18692 #ifdef jim_ext_namespace
18693             if (!nons) {
18694                 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18695                     return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18696                 }
18697             }
18698 #endif
18699             Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
18700             break;
18701 
18702         case INFO_SCRIPT:
18703             if (argc != 2) {
18704                 Jim_WrongNumArgs(interp, 2, argv, "");
18705                 return JIM_ERR;
18706             }
18707             Jim_SetResult(interp, JimGetScript(interp, interp->currentScriptObj)->fileNameObj);
18708             break;
18709 
18710         case INFO_SOURCE:{
18711                 jim_wide line;
18712                 Jim_Obj *resObjPtr;
18713                 Jim_Obj *fileNameObj;
18714 
18715                 if (argc != 3 && argc != 5) {
18716                     Jim_WrongNumArgs(interp, 2, argv, "source ?filename line?");
18717                     return JIM_ERR;
18718                 }
18719                 if (argc == 5) {
18720                     if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
18721                         return JIM_ERR;
18722                     }
18723                     resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
18724                     JimSetSourceInfo(interp, resObjPtr, argv[3], line);
18725                 }
18726                 else {
18727                     if (argv[2]->typePtr == &sourceObjType) {
18728                         fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj;
18729                         line = argv[2]->internalRep.sourceValue.lineNumber;
18730                     }
18731                     else if (argv[2]->typePtr == &scriptObjType) {
18732                         ScriptObj *script = JimGetScript(interp, argv[2]);
18733                         fileNameObj = script->fileNameObj;
18734                         line = script->firstline;
18735                     }
18736                     else {
18737                         fileNameObj = interp->emptyObj;
18738                         line = 1;
18739                     }
18740                     resObjPtr = Jim_NewListObj(interp, NULL, 0);
18741                     Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
18742                     Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
18743                 }
18744                 Jim_SetResult(interp, resObjPtr);
18745                 break;
18746             }
18747 
18748         case INFO_STACKTRACE:
18749             Jim_SetResult(interp, interp->stackTrace);
18750             break;
18751 
18752         case INFO_LEVEL:
18753         case INFO_FRAME:
18754             switch (argc) {
18755                 case 2:
18756                     Jim_SetResultInt(interp, interp->framePtr->level);
18757                     break;
18758 
18759                 case 3:
18760                     if (JimInfoLevel(interp, argv[2], &objPtr, cmd == INFO_LEVEL) != JIM_OK) {
18761                         return JIM_ERR;
18762                     }
18763                     Jim_SetResult(interp, objPtr);
18764                     break;
18765 
18766                 default:
18767                     Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
18768                     return JIM_ERR;
18769             }
18770             break;
18771 
18772         case INFO_BODY:
18773         case INFO_STATICS:
18774         case INFO_ARGS:{
18775                 Jim_Cmd *cmdPtr;
18776 
18777                 if (argc != 3) {
18778                     Jim_WrongNumArgs(interp, 2, argv, "procname");
18779                     return JIM_ERR;
18780                 }
18781                 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18782                     return JIM_ERR;
18783                 }
18784                 if (!cmdPtr->isproc) {
18785                     Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
18786                     return JIM_ERR;
18787                 }
18788                 switch (cmd) {
18789                     case INFO_BODY:
18790                         Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
18791                         break;
18792                     case INFO_ARGS:
18793                         Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
18794                         break;
18795                     case INFO_STATICS:
18796                         if (cmdPtr->u.proc.staticVars) {
18797                             Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
18798                                 NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES));
18799                         }
18800                         break;
18801                 }
18802                 break;
18803             }
18804 
18805         case INFO_VERSION:
18806         case INFO_PATCHLEVEL:{
18807                 char buf[(JIM_INTEGER_SPACE * 2) + 1];
18808 
18809                 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
18810                 Jim_SetResultString(interp, buf, -1);
18811                 break;
18812             }
18813 
18814         case INFO_COMPLETE:
18815             if (argc != 3 && argc != 4) {
18816                 Jim_WrongNumArgs(interp, 2, argv, "script ?missing?");
18817                 return JIM_ERR;
18818             }
18819             else {
18820                 char missing;
18821 
18822                 Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing));
18823                 if (missing != ' ' && argc == 4) {
18824                     Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
18825                 }
18826             }
18827             break;
18828 
18829         case INFO_HOSTNAME:
18830 
18831             return Jim_Eval(interp, "os.gethostname");
18832 
18833         case INFO_NAMEOFEXECUTABLE:
18834 
18835             return Jim_Eval(interp, "{info nameofexecutable}");
18836 
18837         case INFO_RETURNCODES:
18838             if (argc == 2) {
18839                 int i;
18840                 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
18841 
18842                 for (i = 0; jimReturnCodes[i]; i++) {
18843                     Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
18844                     Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
18845                             jimReturnCodes[i], -1));
18846                 }
18847 
18848                 Jim_SetResult(interp, listObjPtr);
18849             }
18850             else if (argc == 3) {
18851                 long code;
18852                 const char *name;
18853 
18854                 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
18855                     return JIM_ERR;
18856                 }
18857                 name = Jim_ReturnCode(code);
18858                 if (*name == '?') {
18859                     Jim_SetResultInt(interp, code);
18860                 }
18861                 else {
18862                     Jim_SetResultString(interp, name, -1);
18863                 }
18864             }
18865             else {
18866                 Jim_WrongNumArgs(interp, 2, argv, "?code?");
18867                 return JIM_ERR;
18868             }
18869             break;
18870         case INFO_REFERENCES:
18871 #ifdef JIM_REFERENCES
18872             return JimInfoReferences(interp, argc, argv);
18873 #else
18874             Jim_SetResultString(interp, "not supported", -1);
18875             return JIM_ERR;
18876 #endif
18877     }
18878     return JIM_OK;
18879 }
18880 
18881 
Jim_ExistsCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18882 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18883 {
18884     Jim_Obj *objPtr;
18885     int result = 0;
18886 
18887     static const char * const options[] = {
18888         "-command", "-proc", "-alias", "-var", NULL
18889     };
18890     enum
18891     {
18892         OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
18893     };
18894     int option;
18895 
18896     if (argc == 2) {
18897         option = OPT_VAR;
18898         objPtr = argv[1];
18899     }
18900     else if (argc == 3) {
18901         if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18902             return JIM_ERR;
18903         }
18904         objPtr = argv[2];
18905     }
18906     else {
18907         Jim_WrongNumArgs(interp, 1, argv, "?option? name");
18908         return JIM_ERR;
18909     }
18910 
18911     if (option == OPT_VAR) {
18912         result = Jim_GetVariable(interp, objPtr, 0) != NULL;
18913     }
18914     else {
18915 
18916         Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
18917 
18918         if (cmd) {
18919             switch (option) {
18920             case OPT_COMMAND:
18921                 result = 1;
18922                 break;
18923 
18924             case OPT_ALIAS:
18925                 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
18926                 break;
18927 
18928             case OPT_PROC:
18929                 result = cmd->isproc;
18930                 break;
18931             }
18932         }
18933     }
18934     Jim_SetResultBool(interp, result);
18935     return JIM_OK;
18936 }
18937 
18938 
Jim_SplitCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18939 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18940 {
18941     const char *str, *splitChars, *noMatchStart;
18942     int splitLen, strLen;
18943     Jim_Obj *resObjPtr;
18944     int c;
18945     int len;
18946 
18947     if (argc != 2 && argc != 3) {
18948         Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
18949         return JIM_ERR;
18950     }
18951 
18952     str = Jim_GetString(argv[1], &len);
18953     if (len == 0) {
18954         return JIM_OK;
18955     }
18956     strLen = Jim_Utf8Length(interp, argv[1]);
18957 
18958 
18959     if (argc == 2) {
18960         splitChars = " \n\t\r";
18961         splitLen = 4;
18962     }
18963     else {
18964         splitChars = Jim_String(argv[2]);
18965         splitLen = Jim_Utf8Length(interp, argv[2]);
18966     }
18967 
18968     noMatchStart = str;
18969     resObjPtr = Jim_NewListObj(interp, NULL, 0);
18970 
18971 
18972     if (splitLen) {
18973         Jim_Obj *objPtr;
18974         while (strLen--) {
18975             const char *sc = splitChars;
18976             int scLen = splitLen;
18977             int sl = utf8_tounicode(str, &c);
18978             while (scLen--) {
18979                 int pc;
18980                 sc += utf8_tounicode(sc, &pc);
18981                 if (c == pc) {
18982                     objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
18983                     Jim_ListAppendElement(interp, resObjPtr, objPtr);
18984                     noMatchStart = str + sl;
18985                     break;
18986                 }
18987             }
18988             str += sl;
18989         }
18990         objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
18991         Jim_ListAppendElement(interp, resObjPtr, objPtr);
18992     }
18993     else {
18994         Jim_Obj **commonObj = NULL;
18995 #define NUM_COMMON (128 - 9)
18996         while (strLen--) {
18997             int n = utf8_tounicode(str, &c);
18998 #ifdef JIM_OPTIMIZATION
18999             if (c >= 9 && c < 128) {
19000 
19001                 c -= 9;
19002                 if (!commonObj) {
19003                     commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
19004                     memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
19005                 }
19006                 if (!commonObj[c]) {
19007                     commonObj[c] = Jim_NewStringObj(interp, str, 1);
19008                 }
19009                 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
19010                 str++;
19011                 continue;
19012             }
19013 #endif
19014             Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
19015             str += n;
19016         }
19017         Jim_Free(commonObj);
19018     }
19019 
19020     Jim_SetResult(interp, resObjPtr);
19021     return JIM_OK;
19022 }
19023 
19024 
Jim_JoinCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19025 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19026 {
19027     const char *joinStr;
19028     int joinStrLen;
19029 
19030     if (argc != 2 && argc != 3) {
19031         Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
19032         return JIM_ERR;
19033     }
19034 
19035     if (argc == 2) {
19036         joinStr = " ";
19037         joinStrLen = 1;
19038     }
19039     else {
19040         joinStr = Jim_GetString(argv[2], &joinStrLen);
19041     }
19042     Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
19043     return JIM_OK;
19044 }
19045 
19046 
Jim_FormatCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19047 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19048 {
19049     Jim_Obj *objPtr;
19050 
19051     if (argc < 2) {
19052         Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
19053         return JIM_ERR;
19054     }
19055     objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
19056     if (objPtr == NULL)
19057         return JIM_ERR;
19058     Jim_SetResult(interp, objPtr);
19059     return JIM_OK;
19060 }
19061 
19062 
Jim_ScanCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19063 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19064 {
19065     Jim_Obj *listPtr, **outVec;
19066     int outc, i;
19067 
19068     if (argc < 3) {
19069         Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
19070         return JIM_ERR;
19071     }
19072     if (argv[2]->typePtr != &scanFmtStringObjType)
19073         SetScanFmtFromAny(interp, argv[2]);
19074     if (FormatGetError(argv[2]) != 0) {
19075         Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
19076         return JIM_ERR;
19077     }
19078     if (argc > 3) {
19079         int maxPos = FormatGetMaxPos(argv[2]);
19080         int count = FormatGetCnvCount(argv[2]);
19081 
19082         if (maxPos > argc - 3) {
19083             Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
19084             return JIM_ERR;
19085         }
19086         else if (count > argc - 3) {
19087             Jim_SetResultString(interp, "different numbers of variable names and "
19088                 "field specifiers", -1);
19089             return JIM_ERR;
19090         }
19091         else if (count < argc - 3) {
19092             Jim_SetResultString(interp, "variable is not assigned by any "
19093                 "conversion specifiers", -1);
19094             return JIM_ERR;
19095         }
19096     }
19097     listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
19098     if (listPtr == 0)
19099         return JIM_ERR;
19100     if (argc > 3) {
19101         int rc = JIM_OK;
19102         int count = 0;
19103 
19104         if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
19105             int len = Jim_ListLength(interp, listPtr);
19106 
19107             if (len != 0) {
19108                 JimListGetElements(interp, listPtr, &outc, &outVec);
19109                 for (i = 0; i < outc; ++i) {
19110                     if (Jim_Length(outVec[i]) > 0) {
19111                         ++count;
19112                         if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
19113                             rc = JIM_ERR;
19114                         }
19115                     }
19116                 }
19117             }
19118             Jim_FreeNewObj(interp, listPtr);
19119         }
19120         else {
19121             count = -1;
19122         }
19123         if (rc == JIM_OK) {
19124             Jim_SetResultInt(interp, count);
19125         }
19126         return rc;
19127     }
19128     else {
19129         if (listPtr == (Jim_Obj *)EOF) {
19130             Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
19131             return JIM_OK;
19132         }
19133         Jim_SetResult(interp, listPtr);
19134     }
19135     return JIM_OK;
19136 }
19137 
19138 
Jim_ErrorCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19139 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19140 {
19141     if (argc != 2 && argc != 3) {
19142         Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
19143         return JIM_ERR;
19144     }
19145     Jim_SetResult(interp, argv[1]);
19146     if (argc == 3) {
19147         JimSetStackTrace(interp, argv[2]);
19148         return JIM_ERR;
19149     }
19150     interp->addStackTrace++;
19151     return JIM_ERR;
19152 }
19153 
19154 
Jim_LrangeCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19155 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19156 {
19157     Jim_Obj *objPtr;
19158 
19159     if (argc != 4) {
19160         Jim_WrongNumArgs(interp, 1, argv, "list first last");
19161         return JIM_ERR;
19162     }
19163     if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
19164         return JIM_ERR;
19165     Jim_SetResult(interp, objPtr);
19166     return JIM_OK;
19167 }
19168 
19169 
Jim_LrepeatCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19170 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19171 {
19172     Jim_Obj *objPtr;
19173     long count;
19174 
19175     if (argc < 2 || Jim_GetLong(interp, argv[1], &count) != JIM_OK || count < 0) {
19176         Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
19177         return JIM_ERR;
19178     }
19179 
19180     if (count == 0 || argc == 2) {
19181         return JIM_OK;
19182     }
19183 
19184     argc -= 2;
19185     argv += 2;
19186 
19187     objPtr = Jim_NewListObj(interp, argv, argc);
19188     while (--count) {
19189         ListInsertElements(objPtr, -1, argc, argv);
19190     }
19191 
19192     Jim_SetResult(interp, objPtr);
19193     return JIM_OK;
19194 }
19195 
Jim_GetEnviron(void)19196 char **Jim_GetEnviron(void)
19197 {
19198 #if defined(HAVE__NSGETENVIRON)
19199     return *_NSGetEnviron();
19200 #else
19201     #if !defined(NO_ENVIRON_EXTERN)
19202     extern char **environ;
19203     #endif
19204 
19205     return environ;
19206 #endif
19207 }
19208 
Jim_SetEnviron(char ** env)19209 void Jim_SetEnviron(char **env)
19210 {
19211 #if defined(HAVE__NSGETENVIRON)
19212     *_NSGetEnviron() = env;
19213 #else
19214     #if !defined(NO_ENVIRON_EXTERN)
19215     extern char **environ;
19216     #endif
19217 
19218     environ = env;
19219 #endif
19220 }
19221 
19222 
Jim_EnvCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19223 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19224 {
19225     const char *key;
19226     const char *val;
19227 
19228     if (argc == 1) {
19229         char **e = Jim_GetEnviron();
19230 
19231         int i;
19232         Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
19233 
19234         for (i = 0; e[i]; i++) {
19235             const char *equals = strchr(e[i], '=');
19236 
19237             if (equals) {
19238                 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
19239                         equals - e[i]));
19240                 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
19241             }
19242         }
19243 
19244         Jim_SetResult(interp, listObjPtr);
19245         return JIM_OK;
19246     }
19247 
19248     if (argc < 2) {
19249         Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
19250         return JIM_ERR;
19251     }
19252     key = Jim_String(argv[1]);
19253     val = getenv(key);
19254     if (val == NULL) {
19255         if (argc < 3) {
19256             Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
19257             return JIM_ERR;
19258         }
19259         val = Jim_String(argv[2]);
19260     }
19261     Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
19262     return JIM_OK;
19263 }
19264 
19265 
Jim_SourceCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19266 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19267 {
19268     int retval;
19269 
19270     if (argc != 2) {
19271         Jim_WrongNumArgs(interp, 1, argv, "fileName");
19272         return JIM_ERR;
19273     }
19274     retval = Jim_EvalFile(interp, Jim_String(argv[1]));
19275     if (retval == JIM_RETURN)
19276         return JIM_OK;
19277     return retval;
19278 }
19279 
19280 
Jim_LreverseCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19281 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19282 {
19283     Jim_Obj *revObjPtr, **ele;
19284     int len;
19285 
19286     if (argc != 2) {
19287         Jim_WrongNumArgs(interp, 1, argv, "list");
19288         return JIM_ERR;
19289     }
19290     JimListGetElements(interp, argv[1], &len, &ele);
19291     len--;
19292     revObjPtr = Jim_NewListObj(interp, NULL, 0);
19293     while (len >= 0)
19294         ListAppendElement(revObjPtr, ele[len--]);
19295     Jim_SetResult(interp, revObjPtr);
19296     return JIM_OK;
19297 }
19298 
JimRangeLen(jim_wide start,jim_wide end,jim_wide step)19299 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
19300 {
19301     jim_wide len;
19302 
19303     if (step == 0)
19304         return -1;
19305     if (start == end)
19306         return 0;
19307     else if (step > 0 && start > end)
19308         return -1;
19309     else if (step < 0 && end > start)
19310         return -1;
19311     len = end - start;
19312     if (len < 0)
19313         len = -len;
19314     if (step < 0)
19315         step = -step;
19316     len = 1 + ((len - 1) / step);
19317     if (len > INT_MAX)
19318         len = INT_MAX;
19319     return (int)((len < 0) ? -1 : len);
19320 }
19321 
19322 
Jim_RangeCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19323 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19324 {
19325     jim_wide start = 0, end, step = 1;
19326     int len, i;
19327     Jim_Obj *objPtr;
19328 
19329     if (argc < 2 || argc > 4) {
19330         Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
19331         return JIM_ERR;
19332     }
19333     if (argc == 2) {
19334         if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
19335             return JIM_ERR;
19336     }
19337     else {
19338         if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
19339             Jim_GetWide(interp, argv[2], &end) != JIM_OK)
19340             return JIM_ERR;
19341         if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
19342             return JIM_ERR;
19343     }
19344     if ((len = JimRangeLen(start, end, step)) == -1) {
19345         Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
19346         return JIM_ERR;
19347     }
19348     objPtr = Jim_NewListObj(interp, NULL, 0);
19349     for (i = 0; i < len; i++)
19350         ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
19351     Jim_SetResult(interp, objPtr);
19352     return JIM_OK;
19353 }
19354 
19355 
Jim_RandCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19356 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19357 {
19358     jim_wide min = 0, max = 0, len, maxMul;
19359 
19360     if (argc < 1 || argc > 3) {
19361         Jim_WrongNumArgs(interp, 1, argv, "?min? max");
19362         return JIM_ERR;
19363     }
19364     if (argc == 1) {
19365         max = JIM_WIDE_MAX;
19366     } else if (argc == 2) {
19367         if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
19368             return JIM_ERR;
19369     } else if (argc == 3) {
19370         if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
19371             Jim_GetWide(interp, argv[2], &max) != JIM_OK)
19372             return JIM_ERR;
19373     }
19374     len = max-min;
19375     if (len < 0) {
19376         Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
19377         return JIM_ERR;
19378     }
19379     maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
19380     while (1) {
19381         jim_wide r;
19382 
19383         JimRandomBytes(interp, &r, sizeof(jim_wide));
19384         if (r < 0 || r >= maxMul) continue;
19385         r = (len == 0) ? 0 : r%len;
19386         Jim_SetResultInt(interp, min+r);
19387         return JIM_OK;
19388     }
19389 }
19390 
19391 static const struct {
19392     const char *name;
19393     Jim_CmdProc *cmdProc;
19394 } Jim_CoreCommandsTable[] = {
19395     {"alias", Jim_AliasCoreCommand},
19396     {"set", Jim_SetCoreCommand},
19397     {"unset", Jim_UnsetCoreCommand},
19398     {"puts", Jim_PutsCoreCommand},
19399     {"+", Jim_AddCoreCommand},
19400     {"*", Jim_MulCoreCommand},
19401     {"-", Jim_SubCoreCommand},
19402     {"/", Jim_DivCoreCommand},
19403     {"incr", Jim_IncrCoreCommand},
19404     {"while", Jim_WhileCoreCommand},
19405     {"loop", Jim_LoopCoreCommand},
19406     {"for", Jim_ForCoreCommand},
19407     {"foreach", Jim_ForeachCoreCommand},
19408     {"lmap", Jim_LmapCoreCommand},
19409     {"lassign", Jim_LassignCoreCommand},
19410     {"if", Jim_IfCoreCommand},
19411     {"switch", Jim_SwitchCoreCommand},
19412     {"list", Jim_ListCoreCommand},
19413     {"lindex", Jim_LindexCoreCommand},
19414     {"lset", Jim_LsetCoreCommand},
19415     {"lsearch", Jim_LsearchCoreCommand},
19416     {"llength", Jim_LlengthCoreCommand},
19417     {"lappend", Jim_LappendCoreCommand},
19418     {"linsert", Jim_LinsertCoreCommand},
19419     {"lreplace", Jim_LreplaceCoreCommand},
19420     {"lsort", Jim_LsortCoreCommand},
19421     {"append", Jim_AppendCoreCommand},
19422     {"debug", Jim_DebugCoreCommand},
19423     {"eval", Jim_EvalCoreCommand},
19424     {"uplevel", Jim_UplevelCoreCommand},
19425     {"expr", Jim_ExprCoreCommand},
19426     {"break", Jim_BreakCoreCommand},
19427     {"continue", Jim_ContinueCoreCommand},
19428     {"proc", Jim_ProcCoreCommand},
19429     {"concat", Jim_ConcatCoreCommand},
19430     {"return", Jim_ReturnCoreCommand},
19431     {"upvar", Jim_UpvarCoreCommand},
19432     {"global", Jim_GlobalCoreCommand},
19433     {"string", Jim_StringCoreCommand},
19434     {"time", Jim_TimeCoreCommand},
19435     {"exit", Jim_ExitCoreCommand},
19436     {"catch", Jim_CatchCoreCommand},
19437 #ifdef JIM_REFERENCES
19438     {"ref", Jim_RefCoreCommand},
19439     {"getref", Jim_GetrefCoreCommand},
19440     {"setref", Jim_SetrefCoreCommand},
19441     {"finalize", Jim_FinalizeCoreCommand},
19442     {"collect", Jim_CollectCoreCommand},
19443 #endif
19444     {"rename", Jim_RenameCoreCommand},
19445     {"dict", Jim_DictCoreCommand},
19446     {"subst", Jim_SubstCoreCommand},
19447     {"info", Jim_InfoCoreCommand},
19448     {"exists", Jim_ExistsCoreCommand},
19449     {"split", Jim_SplitCoreCommand},
19450     {"join", Jim_JoinCoreCommand},
19451     {"format", Jim_FormatCoreCommand},
19452     {"scan", Jim_ScanCoreCommand},
19453     {"error", Jim_ErrorCoreCommand},
19454     {"lrange", Jim_LrangeCoreCommand},
19455     {"lrepeat", Jim_LrepeatCoreCommand},
19456     {"env", Jim_EnvCoreCommand},
19457     {"source", Jim_SourceCoreCommand},
19458     {"lreverse", Jim_LreverseCoreCommand},
19459     {"range", Jim_RangeCoreCommand},
19460     {"rand", Jim_RandCoreCommand},
19461     {"tailcall", Jim_TailcallCoreCommand},
19462     {"local", Jim_LocalCoreCommand},
19463     {"upcall", Jim_UpcallCoreCommand},
19464     {"apply", Jim_ApplyCoreCommand},
19465     {NULL, NULL},
19466 };
19467 
Jim_RegisterCoreCommands(Jim_Interp * interp)19468 void Jim_RegisterCoreCommands(Jim_Interp *interp)
19469 {
19470     int i = 0;
19471 
19472     while (Jim_CoreCommandsTable[i].name != NULL) {
19473         Jim_CreateCommand(interp,
19474             Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
19475         i++;
19476     }
19477 }
19478 
Jim_MakeErrorMessage(Jim_Interp * interp)19479 void Jim_MakeErrorMessage(Jim_Interp *interp)
19480 {
19481     Jim_Obj *argv[2];
19482 
19483     argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
19484     argv[1] = interp->result;
19485 
19486     Jim_EvalObjVector(interp, 2, argv);
19487 }
19488 
JimSortStringTable(const char * const * tablePtr)19489 static char **JimSortStringTable(const char *const *tablePtr)
19490 {
19491     int count;
19492     char **tablePtrSorted;
19493 
19494 
19495     for (count = 0; tablePtr[count]; count++) {
19496     }
19497 
19498 
19499     tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1));
19500     memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
19501     qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
19502     tablePtrSorted[count] = NULL;
19503 
19504     return tablePtrSorted;
19505 }
19506 
JimSetFailedEnumResult(Jim_Interp * interp,const char * arg,const char * badtype,const char * prefix,const char * const * tablePtr,const char * name)19507 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
19508     const char *prefix, const char *const *tablePtr, const char *name)
19509 {
19510     char **tablePtrSorted;
19511     int i;
19512 
19513     if (name == NULL) {
19514         name = "option";
19515     }
19516 
19517     Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
19518     tablePtrSorted = JimSortStringTable(tablePtr);
19519     for (i = 0; tablePtrSorted[i]; i++) {
19520         if (tablePtrSorted[i + 1] == NULL && i > 0) {
19521             Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
19522         }
19523         Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
19524         if (tablePtrSorted[i + 1]) {
19525             Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
19526         }
19527     }
19528     Jim_Free(tablePtrSorted);
19529 }
19530 
19531 
Jim_CheckShowCommands(Jim_Interp * interp,Jim_Obj * objPtr,const char * const * tablePtr)19532 int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr)
19533 {
19534     if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) {
19535         int i;
19536         char **tablePtrSorted = JimSortStringTable(tablePtr);
19537         Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
19538         for (i = 0; tablePtrSorted[i]; i++) {
19539             Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1));
19540         }
19541         Jim_Free(tablePtrSorted);
19542         return JIM_OK;
19543     }
19544     return JIM_ERR;
19545 }
19546 
19547 static const Jim_ObjType getEnumObjType = {
19548     "get-enum",
19549     NULL,
19550     NULL,
19551     NULL,
19552     JIM_TYPE_REFERENCES
19553 };
19554 
Jim_GetEnum(Jim_Interp * interp,Jim_Obj * objPtr,const char * const * tablePtr,int * indexPtr,const char * name,int flags)19555 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
19556     const char *const *tablePtr, int *indexPtr, const char *name, int flags)
19557 {
19558     const char *bad = "bad ";
19559     const char *const *entryPtr = NULL;
19560     int i;
19561     int match = -1;
19562     int arglen;
19563     const char *arg;
19564 
19565     if (objPtr->typePtr == &getEnumObjType) {
19566         if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) {
19567             *indexPtr = objPtr->internalRep.ptrIntValue.int2;
19568             return JIM_OK;
19569         }
19570     }
19571 
19572     arg = Jim_GetString(objPtr, &arglen);
19573 
19574     *indexPtr = -1;
19575 
19576     for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
19577         if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
19578 
19579             match = i;
19580             goto found;
19581         }
19582         if (flags & JIM_ENUM_ABBREV) {
19583             if (strncmp(arg, *entryPtr, arglen) == 0) {
19584                 if (*arg == '-' && arglen == 1) {
19585                     break;
19586                 }
19587                 if (match >= 0) {
19588                     bad = "ambiguous ";
19589                     goto ambiguous;
19590                 }
19591                 match = i;
19592             }
19593         }
19594     }
19595 
19596 
19597     if (match >= 0) {
19598   found:
19599 
19600         Jim_FreeIntRep(interp, objPtr);
19601         objPtr->typePtr = &getEnumObjType;
19602         objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr;
19603         objPtr->internalRep.ptrIntValue.int1 = flags;
19604         objPtr->internalRep.ptrIntValue.int2 = match;
19605 
19606         *indexPtr = match;
19607         return JIM_OK;
19608     }
19609 
19610   ambiguous:
19611     if (flags & JIM_ERRMSG) {
19612         JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
19613     }
19614     return JIM_ERR;
19615 }
19616 
Jim_FindByName(const char * name,const char * const array[],size_t len)19617 int Jim_FindByName(const char *name, const char * const array[], size_t len)
19618 {
19619     int i;
19620 
19621     for (i = 0; i < (int)len; i++) {
19622         if (array[i] && strcmp(array[i], name) == 0) {
19623             return i;
19624         }
19625     }
19626     return -1;
19627 }
19628 
Jim_IsDict(Jim_Obj * objPtr)19629 int Jim_IsDict(Jim_Obj *objPtr)
19630 {
19631     return objPtr->typePtr == &dictObjType;
19632 }
19633 
Jim_IsList(Jim_Obj * objPtr)19634 int Jim_IsList(Jim_Obj *objPtr)
19635 {
19636     return objPtr->typePtr == &listObjType;
19637 }
19638 
Jim_SetResultFormatted(Jim_Interp * interp,const char * format,...)19639 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
19640 {
19641 
19642     int len = strlen(format);
19643     int extra = 0;
19644     int n = 0;
19645     const char *params[5];
19646     int nobjparam = 0;
19647     Jim_Obj *objparam[5];
19648     char *buf;
19649     va_list args;
19650     int i;
19651 
19652     va_start(args, format);
19653 
19654     for (i = 0; i < len && n < 5; i++) {
19655         int l;
19656 
19657         if (strncmp(format + i, "%s", 2) == 0) {
19658             params[n] = va_arg(args, char *);
19659 
19660             l = strlen(params[n]);
19661         }
19662         else if (strncmp(format + i, "%#s", 3) == 0) {
19663             Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
19664 
19665             params[n] = Jim_GetString(objPtr, &l);
19666             objparam[nobjparam++] = objPtr;
19667             Jim_IncrRefCount(objPtr);
19668         }
19669         else {
19670             if (format[i] == '%') {
19671                 i++;
19672             }
19673             continue;
19674         }
19675         n++;
19676         extra += l;
19677     }
19678 
19679     len += extra;
19680     buf = Jim_Alloc(len + 1);
19681     len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
19682 
19683     va_end(args);
19684 
19685     Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
19686 
19687     for (i = 0; i < nobjparam; i++) {
19688         Jim_DecrRefCount(interp, objparam[i]);
19689     }
19690 }
19691 
19692 
19693 #ifndef jim_ext_package
Jim_PackageProvide(Jim_Interp * interp,const char * name,const char * ver,int flags)19694 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
19695 {
19696     return JIM_OK;
19697 }
19698 #endif
19699 #ifndef jim_ext_aio
Jim_AioFilehandle(Jim_Interp * interp,Jim_Obj * fhObj)19700 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
19701 {
19702     Jim_SetResultString(interp, "aio not enabled", -1);
19703     return NULL;
19704 }
19705 #endif
19706 
19707 
19708 #include <stdio.h>
19709 #include <string.h>
19710 
19711 
subcmd_null(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19712 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19713 {
19714 
19715     return JIM_OK;
19716 }
19717 
19718 static const jim_subcmd_type dummy_subcmd = {
19719     "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
19720 };
19721 
add_commands(Jim_Interp * interp,const jim_subcmd_type * ct,const char * sep)19722 static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
19723 {
19724     const char *s = "";
19725 
19726     for (; ct->cmd; ct++) {
19727         if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
19728             Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, NULL);
19729             s = sep;
19730         }
19731     }
19732 }
19733 
bad_subcmd(Jim_Interp * interp,const jim_subcmd_type * command_table,const char * type,Jim_Obj * cmd,Jim_Obj * subcmd)19734 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
19735     Jim_Obj *cmd, Jim_Obj *subcmd)
19736 {
19737     Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be ", cmd, type, subcmd);
19738     add_commands(interp, command_table, ", ");
19739 }
19740 
show_cmd_usage(Jim_Interp * interp,const jim_subcmd_type * command_table,int argc,Jim_Obj * const * argv)19741 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
19742     Jim_Obj *const *argv)
19743 {
19744     Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: ", argv[0]);
19745     add_commands(interp, command_table, ", ");
19746 }
19747 
add_cmd_usage(Jim_Interp * interp,const jim_subcmd_type * ct,Jim_Obj * cmd)19748 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
19749 {
19750     if (cmd) {
19751         Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
19752     }
19753     Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
19754     if (ct->args && *ct->args) {
19755         Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
19756     }
19757 }
19758 
set_wrong_args(Jim_Interp * interp,const jim_subcmd_type * command_table,Jim_Obj * subcmd)19759 static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd)
19760 {
19761     Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19762     add_cmd_usage(interp, command_table, subcmd);
19763     Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19764 }
19765 
19766 static const Jim_ObjType subcmdLookupObjType = {
19767     "subcmd-lookup",
19768     NULL,
19769     NULL,
19770     NULL,
19771     JIM_TYPE_REFERENCES
19772 };
19773 
Jim_ParseSubCmd(Jim_Interp * interp,const jim_subcmd_type * command_table,int argc,Jim_Obj * const * argv)19774 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
19775     int argc, Jim_Obj *const *argv)
19776 {
19777     const jim_subcmd_type *ct;
19778     const jim_subcmd_type *partial = 0;
19779     int cmdlen;
19780     Jim_Obj *cmd;
19781     const char *cmdstr;
19782     int help = 0;
19783 
19784     if (argc < 2) {
19785         Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n"
19786             "Use \"%#s -help ?command?\" for help", argv[0], argv[0]);
19787         return 0;
19788     }
19789 
19790     cmd = argv[1];
19791 
19792 
19793     if (cmd->typePtr == &subcmdLookupObjType) {
19794         if (cmd->internalRep.ptrIntValue.ptr == command_table) {
19795             ct = command_table + cmd->internalRep.ptrIntValue.int1;
19796             goto found;
19797         }
19798     }
19799 
19800 
19801     if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
19802         if (argc == 2) {
19803 
19804             show_cmd_usage(interp, command_table, argc, argv);
19805             return &dummy_subcmd;
19806         }
19807         help = 1;
19808 
19809 
19810         cmd = argv[2];
19811     }
19812 
19813 
19814     if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
19815 
19816         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19817         add_commands(interp, command_table, " ");
19818         return &dummy_subcmd;
19819     }
19820 
19821     cmdstr = Jim_GetString(cmd, &cmdlen);
19822 
19823     for (ct = command_table; ct->cmd; ct++) {
19824         if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
19825 
19826             break;
19827         }
19828         if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
19829             if (partial) {
19830 
19831                 if (help) {
19832 
19833                     show_cmd_usage(interp, command_table, argc, argv);
19834                     return &dummy_subcmd;
19835                 }
19836                 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
19837                 return 0;
19838             }
19839             partial = ct;
19840         }
19841         continue;
19842     }
19843 
19844 
19845     if (partial && !ct->cmd) {
19846         ct = partial;
19847     }
19848 
19849     if (!ct->cmd) {
19850 
19851         if (help) {
19852 
19853             show_cmd_usage(interp, command_table, argc, argv);
19854             return &dummy_subcmd;
19855         }
19856         bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
19857         return 0;
19858     }
19859 
19860     if (help) {
19861         Jim_SetResultString(interp, "Usage: ", -1);
19862 
19863         add_cmd_usage(interp, ct, argv[0]);
19864         return &dummy_subcmd;
19865     }
19866 
19867 
19868     Jim_FreeIntRep(interp, cmd);
19869     cmd->typePtr = &subcmdLookupObjType;
19870     cmd->internalRep.ptrIntValue.ptr = (void *)command_table;
19871     cmd->internalRep.ptrIntValue.int1 = ct - command_table;
19872 
19873 found:
19874 
19875     if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) {
19876         Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19877 
19878         add_cmd_usage(interp, ct, argv[0]);
19879         Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19880 
19881         return 0;
19882     }
19883 
19884 
19885     return ct;
19886 }
19887 
Jim_CallSubCmd(Jim_Interp * interp,const jim_subcmd_type * ct,int argc,Jim_Obj * const * argv)19888 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
19889 {
19890     int ret = JIM_ERR;
19891 
19892     if (ct) {
19893         if (ct->flags & JIM_MODFLAG_FULLARGV) {
19894             ret = ct->function(interp, argc, argv);
19895         }
19896         else {
19897             ret = ct->function(interp, argc - 2, argv + 2);
19898         }
19899         if (ret < 0) {
19900             set_wrong_args(interp, ct, argv[0]);
19901             ret = JIM_ERR;
19902         }
19903     }
19904     return ret;
19905 }
19906 
Jim_SubCmdProc(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19907 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19908 {
19909     const jim_subcmd_type *ct =
19910         Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
19911 
19912     return Jim_CallSubCmd(interp, ct, argc, argv);
19913 }
19914 
19915 #include <ctype.h>
19916 #include <stdlib.h>
19917 #include <string.h>
19918 #include <stdio.h>
19919 #include <assert.h>
19920 
19921 
utf8_fromunicode(char * p,unsigned uc)19922 int utf8_fromunicode(char *p, unsigned uc)
19923 {
19924     if (uc <= 0x7f) {
19925         *p = uc;
19926         return 1;
19927     }
19928     else if (uc <= 0x7ff) {
19929         *p++ = 0xc0 | ((uc & 0x7c0) >> 6);
19930         *p = 0x80 | (uc & 0x3f);
19931         return 2;
19932     }
19933     else if (uc <= 0xffff) {
19934         *p++ = 0xe0 | ((uc & 0xf000) >> 12);
19935         *p++ = 0x80 | ((uc & 0xfc0) >> 6);
19936         *p = 0x80 | (uc & 0x3f);
19937         return 3;
19938     }
19939 
19940     else {
19941         *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
19942         *p++ = 0x80 | ((uc & 0x3f000) >> 12);
19943         *p++ = 0x80 | ((uc & 0xfc0) >> 6);
19944         *p = 0x80 | (uc & 0x3f);
19945         return 4;
19946     }
19947 }
19948 
19949 #include <ctype.h>
19950 #include <string.h>
19951 
19952 
19953 #define JIM_INTEGER_SPACE 24
19954 #define MAX_FLOAT_WIDTH 320
19955 
Jim_FormatString(Jim_Interp * interp,Jim_Obj * fmtObjPtr,int objc,Jim_Obj * const * objv)19956 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
19957 {
19958     const char *span, *format, *formatEnd, *msg;
19959     int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
19960     static const char * const mixedXPG =
19961             "cannot mix \"%\" and \"%n$\" conversion specifiers";
19962     static const char * const badIndex[2] = {
19963         "not enough arguments for all format specifiers",
19964         "\"%n$\" argument index out of range"
19965     };
19966     int formatLen;
19967     Jim_Obj *resultPtr;
19968 
19969     char *num_buffer = NULL;
19970     int num_buffer_size = 0;
19971 
19972     span = format = Jim_GetString(fmtObjPtr, &formatLen);
19973     formatEnd = format + formatLen;
19974     resultPtr = Jim_NewEmptyStringObj(interp);
19975 
19976     while (format != formatEnd) {
19977         char *end;
19978         int gotMinus, sawFlag;
19979         int gotPrecision, useShort;
19980         long width, precision;
19981         int newXpg;
19982         int ch;
19983         int step;
19984         int doubleType;
19985         char pad = ' ';
19986         char spec[2*JIM_INTEGER_SPACE + 12];
19987         char *p;
19988 
19989         int formatted_chars;
19990         int formatted_bytes;
19991         const char *formatted_buf;
19992 
19993         step = utf8_tounicode(format, &ch);
19994         format += step;
19995         if (ch != '%') {
19996             numBytes += step;
19997             continue;
19998         }
19999         if (numBytes) {
20000             Jim_AppendString(interp, resultPtr, span, numBytes);
20001             numBytes = 0;
20002         }
20003 
20004 
20005         step = utf8_tounicode(format, &ch);
20006         if (ch == '%') {
20007             span = format;
20008             numBytes = step;
20009             format += step;
20010             continue;
20011         }
20012 
20013 
20014         newXpg = 0;
20015         if (isdigit(ch)) {
20016             int position = strtoul(format, &end, 10);
20017             if (*end == '$') {
20018                 newXpg = 1;
20019                 objIndex = position - 1;
20020                 format = end + 1;
20021                 step = utf8_tounicode(format, &ch);
20022             }
20023         }
20024         if (newXpg) {
20025             if (gotSequential) {
20026                 msg = mixedXPG;
20027                 goto errorMsg;
20028             }
20029             gotXpg = 1;
20030         } else {
20031             if (gotXpg) {
20032                 msg = mixedXPG;
20033                 goto errorMsg;
20034             }
20035             gotSequential = 1;
20036         }
20037         if ((objIndex < 0) || (objIndex >= objc)) {
20038             msg = badIndex[gotXpg];
20039             goto errorMsg;
20040         }
20041 
20042         p = spec;
20043         *p++ = '%';
20044 
20045         gotMinus = 0;
20046         sawFlag = 1;
20047         do {
20048             switch (ch) {
20049             case '-':
20050                 gotMinus = 1;
20051                 break;
20052             case '0':
20053                 pad = ch;
20054                 break;
20055             case ' ':
20056             case '+':
20057             case '#':
20058                 break;
20059             default:
20060                 sawFlag = 0;
20061                 continue;
20062             }
20063             *p++ = ch;
20064             format += step;
20065             step = utf8_tounicode(format, &ch);
20066 
20067         } while (sawFlag && (p - spec <= 5));
20068 
20069 
20070         width = 0;
20071         if (isdigit(ch)) {
20072             width = strtoul(format, &end, 10);
20073             format = end;
20074             step = utf8_tounicode(format, &ch);
20075         } else if (ch == '*') {
20076             if (objIndex >= objc - 1) {
20077                 msg = badIndex[gotXpg];
20078                 goto errorMsg;
20079             }
20080             if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) {
20081                 goto error;
20082             }
20083             if (width < 0) {
20084                 width = -width;
20085                 if (!gotMinus) {
20086                     *p++ = '-';
20087                     gotMinus = 1;
20088                 }
20089             }
20090             objIndex++;
20091             format += step;
20092             step = utf8_tounicode(format, &ch);
20093         }
20094 
20095 
20096         gotPrecision = precision = 0;
20097         if (ch == '.') {
20098             gotPrecision = 1;
20099             format += step;
20100             step = utf8_tounicode(format, &ch);
20101         }
20102         if (isdigit(ch)) {
20103             precision = strtoul(format, &end, 10);
20104             format = end;
20105             step = utf8_tounicode(format, &ch);
20106         } else if (ch == '*') {
20107             if (objIndex >= objc - 1) {
20108                 msg = badIndex[gotXpg];
20109                 goto errorMsg;
20110             }
20111             if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) {
20112                 goto error;
20113             }
20114 
20115 
20116             if (precision < 0) {
20117                 precision = 0;
20118             }
20119             objIndex++;
20120             format += step;
20121             step = utf8_tounicode(format, &ch);
20122         }
20123 
20124 
20125         useShort = 0;
20126         if (ch == 'h') {
20127             useShort = 1;
20128             format += step;
20129             step = utf8_tounicode(format, &ch);
20130         } else if (ch == 'l') {
20131 
20132             format += step;
20133             step = utf8_tounicode(format, &ch);
20134             if (ch == 'l') {
20135                 format += step;
20136                 step = utf8_tounicode(format, &ch);
20137             }
20138         }
20139 
20140         format += step;
20141         span = format;
20142 
20143 
20144         if (ch == 'i') {
20145             ch = 'd';
20146         }
20147 
20148         doubleType = 0;
20149 
20150         switch (ch) {
20151         case '\0':
20152             msg = "format string ended in middle of field specifier";
20153             goto errorMsg;
20154         case 's': {
20155             formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
20156             formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
20157             if (gotPrecision && (precision < formatted_chars)) {
20158 
20159                 formatted_chars = precision;
20160                 formatted_bytes = utf8_index(formatted_buf, precision);
20161             }
20162             break;
20163         }
20164         case 'c': {
20165             jim_wide code;
20166 
20167             if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
20168                 goto error;
20169             }
20170 
20171             formatted_bytes = utf8_getchars(spec, code);
20172             formatted_buf = spec;
20173             formatted_chars = 1;
20174             break;
20175         }
20176         case 'b': {
20177                 unsigned jim_wide w;
20178                 int length;
20179                 int i;
20180                 int j;
20181 
20182                 if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
20183                     goto error;
20184                 }
20185                 length = sizeof(w) * 8;
20186 
20187 
20188 
20189                 if (num_buffer_size < length + 1) {
20190                     num_buffer_size = length + 1;
20191                     num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20192                 }
20193 
20194                 j = 0;
20195                 for (i = length; i > 0; ) {
20196                         i--;
20197                     if (w & ((unsigned jim_wide)1 << i)) {
20198                                 num_buffer[j++] = '1';
20199                         }
20200                         else if (j || i == 0) {
20201                                 num_buffer[j++] = '0';
20202                         }
20203                 }
20204                 num_buffer[j] = 0;
20205                 formatted_chars = formatted_bytes = j;
20206                 formatted_buf = num_buffer;
20207                 break;
20208         }
20209 
20210         case 'e':
20211         case 'E':
20212         case 'f':
20213         case 'g':
20214         case 'G':
20215             doubleType = 1;
20216 
20217         case 'd':
20218         case 'u':
20219         case 'o':
20220         case 'x':
20221         case 'X': {
20222             jim_wide w;
20223             double d;
20224             int length;
20225 
20226 
20227             if (width) {
20228                 p += sprintf(p, "%ld", width);
20229             }
20230             if (gotPrecision) {
20231                 p += sprintf(p, ".%ld", precision);
20232             }
20233 
20234 
20235             if (doubleType) {
20236                 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
20237                     goto error;
20238                 }
20239                 length = MAX_FLOAT_WIDTH;
20240             }
20241             else {
20242                 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) {
20243                     goto error;
20244                 }
20245                 length = JIM_INTEGER_SPACE;
20246                 if (useShort) {
20247                     if (ch == 'd') {
20248                         w = (short)w;
20249                     }
20250                     else {
20251                         w = (unsigned short)w;
20252                     }
20253                 }
20254                 *p++ = 'l';
20255 #ifdef HAVE_LONG_LONG
20256                 if (sizeof(long long) == sizeof(jim_wide)) {
20257                     *p++ = 'l';
20258                 }
20259 #endif
20260             }
20261 
20262             *p++ = (char) ch;
20263             *p = '\0';
20264 
20265 
20266             if (width > 10000 || length > 10000 || precision > 10000) {
20267                 Jim_SetResultString(interp, "format too long", -1);
20268                 goto error;
20269             }
20270 
20271 
20272 
20273             if (width > length) {
20274                 length = width;
20275             }
20276             if (gotPrecision) {
20277                 length += precision;
20278             }
20279 
20280 
20281             if (num_buffer_size < length + 1) {
20282                 num_buffer_size = length + 1;
20283                 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20284             }
20285 
20286             if (doubleType) {
20287                 snprintf(num_buffer, length + 1, spec, d);
20288             }
20289             else {
20290                 formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
20291             }
20292             formatted_chars = formatted_bytes = strlen(num_buffer);
20293             formatted_buf = num_buffer;
20294             break;
20295         }
20296 
20297         default: {
20298 
20299             spec[0] = ch;
20300             spec[1] = '\0';
20301             Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
20302             goto error;
20303         }
20304         }
20305 
20306         if (!gotMinus) {
20307             while (formatted_chars < width) {
20308                 Jim_AppendString(interp, resultPtr, &pad, 1);
20309                 formatted_chars++;
20310             }
20311         }
20312 
20313         Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes);
20314 
20315         while (formatted_chars < width) {
20316             Jim_AppendString(interp, resultPtr, &pad, 1);
20317             formatted_chars++;
20318         }
20319 
20320         objIndex += gotSequential;
20321     }
20322     if (numBytes) {
20323         Jim_AppendString(interp, resultPtr, span, numBytes);
20324     }
20325 
20326     Jim_Free(num_buffer);
20327     return resultPtr;
20328 
20329   errorMsg:
20330     Jim_SetResultString(interp, msg, -1);
20331   error:
20332     Jim_FreeNewObj(interp, resultPtr);
20333     Jim_Free(num_buffer);
20334     return NULL;
20335 }
20336 
20337 
20338 #if defined(JIM_REGEXP)
20339 #include <stdio.h>
20340 #include <ctype.h>
20341 #include <stdlib.h>
20342 #include <string.h>
20343 
20344 
20345 
20346 #define REG_MAX_PAREN 100
20347 
20348 
20349 
20350 #define	END	0
20351 #define	BOL	1
20352 #define	EOL	2
20353 #define	ANY	3
20354 #define	ANYOF	4
20355 #define	ANYBUT	5
20356 #define	BRANCH	6
20357 #define	BACK	7
20358 #define	EXACTLY	8
20359 #define	NOTHING	9
20360 #define	REP	10
20361 #define	REPMIN	11
20362 #define	REPX	12
20363 #define	REPXMIN	13
20364 #define	BOLX	14
20365 #define	EOLX	15
20366 #define	WORDA	16
20367 #define	WORDZ	17
20368 
20369 #define	OPENNC 	1000
20370 #define	OPEN   	1001
20371 
20372 
20373 
20374 
20375 #define	CLOSENC	2000
20376 #define	CLOSE	2001
20377 #define	CLOSE_END	(CLOSE+REG_MAX_PAREN)
20378 
20379 #define	REG_MAGIC	0xFADED00D
20380 
20381 
20382 #define	OP(preg, p)	(preg->program[p])
20383 #define	NEXT(preg, p)	(preg->program[p + 1])
20384 #define	OPERAND(p)	((p) + 2)
20385 
20386 
20387 
20388 
20389 #define	FAIL(R,M)	{ (R)->err = (M); return (M); }
20390 #define	ISMULT(c)	((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
20391 #define	META		"^$.[()|?{+*"
20392 
20393 #define	HASWIDTH	1
20394 #define	SIMPLE		2
20395 #define	SPSTART		4
20396 #define	WORST		0
20397 
20398 #define MAX_REP_COUNT 1000000
20399 
20400 static int reg(regex_t *preg, int paren, int *flagp );
20401 static int regpiece(regex_t *preg, int *flagp );
20402 static int regbranch(regex_t *preg, int *flagp );
20403 static int regatom(regex_t *preg, int *flagp );
20404 static int regnode(regex_t *preg, int op );
20405 static int regnext(regex_t *preg, int p );
20406 static void regc(regex_t *preg, int b );
20407 static int reginsert(regex_t *preg, int op, int size, int opnd );
20408 static void regtail(regex_t *preg, int p, int val);
20409 static void regoptail(regex_t *preg, int p, int val );
20410 static int regopsize(regex_t *preg, int p );
20411 
20412 static int reg_range_find(const int *string, int c);
20413 static const char *str_find(const char *string, int c, int nocase);
20414 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase);
20415 
20416 
20417 #ifdef DEBUG
20418 static int regnarrate = 0;
20419 static void regdump(regex_t *preg);
20420 static const char *regprop( int op );
20421 #endif
20422 
20423 
str_int_len(const int * seq)20424 static int str_int_len(const int *seq)
20425 {
20426 	int n = 0;
20427 	while (*seq++) {
20428 		n++;
20429 	}
20430 	return n;
20431 }
20432 
regcomp(regex_t * preg,const char * exp,int cflags)20433 int regcomp(regex_t *preg, const char *exp, int cflags)
20434 {
20435 	int scan;
20436 	int longest;
20437 	unsigned len;
20438 	int flags;
20439 
20440 #ifdef DEBUG
20441 	fprintf(stderr, "Compiling: '%s'\n", exp);
20442 #endif
20443 	memset(preg, 0, sizeof(*preg));
20444 
20445 	if (exp == NULL)
20446 		FAIL(preg, REG_ERR_NULL_ARGUMENT);
20447 
20448 
20449 	preg->cflags = cflags;
20450 	preg->regparse = exp;
20451 
20452 
20453 	preg->proglen = (strlen(exp) + 1) * 5;
20454 	preg->program = malloc(preg->proglen * sizeof(int));
20455 	if (preg->program == NULL)
20456 		FAIL(preg, REG_ERR_NOMEM);
20457 
20458 	regc(preg, REG_MAGIC);
20459 	if (reg(preg, 0, &flags) == 0) {
20460 		return preg->err;
20461 	}
20462 
20463 
20464 	if (preg->re_nsub >= REG_MAX_PAREN)
20465 		FAIL(preg,REG_ERR_TOO_BIG);
20466 
20467 
20468 	preg->regstart = 0;
20469 	preg->reganch = 0;
20470 	preg->regmust = 0;
20471 	preg->regmlen = 0;
20472 	scan = 1;
20473 	if (OP(preg, regnext(preg, scan)) == END) {
20474 		scan = OPERAND(scan);
20475 
20476 
20477 		if (OP(preg, scan) == EXACTLY) {
20478 			preg->regstart = preg->program[OPERAND(scan)];
20479 		}
20480 		else if (OP(preg, scan) == BOL)
20481 			preg->reganch++;
20482 
20483 		if (flags&SPSTART) {
20484 			longest = 0;
20485 			len = 0;
20486 			for (; scan != 0; scan = regnext(preg, scan)) {
20487 				if (OP(preg, scan) == EXACTLY) {
20488 					int plen = str_int_len(preg->program + OPERAND(scan));
20489 					if (plen >= len) {
20490 						longest = OPERAND(scan);
20491 						len = plen;
20492 					}
20493 				}
20494 			}
20495 			preg->regmust = longest;
20496 			preg->regmlen = len;
20497 		}
20498 	}
20499 
20500 #ifdef DEBUG
20501 	regdump(preg);
20502 #endif
20503 
20504 	return 0;
20505 }
20506 
reg(regex_t * preg,int paren,int * flagp)20507 static int reg(regex_t *preg, int paren, int *flagp )
20508 {
20509 	int ret;
20510 	int br;
20511 	int ender;
20512 	int parno = 0;
20513 	int flags;
20514 
20515 	*flagp = HASWIDTH;
20516 
20517 
20518 	if (paren) {
20519 		if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
20520 
20521 			preg->regparse += 2;
20522 			parno = -1;
20523 		}
20524 		else {
20525 			parno = ++preg->re_nsub;
20526 		}
20527 		ret = regnode(preg, OPEN+parno);
20528 	} else
20529 		ret = 0;
20530 
20531 
20532 	br = regbranch(preg, &flags);
20533 	if (br == 0)
20534 		return 0;
20535 	if (ret != 0)
20536 		regtail(preg, ret, br);
20537 	else
20538 		ret = br;
20539 	if (!(flags&HASWIDTH))
20540 		*flagp &= ~HASWIDTH;
20541 	*flagp |= flags&SPSTART;
20542 	while (*preg->regparse == '|') {
20543 		preg->regparse++;
20544 		br = regbranch(preg, &flags);
20545 		if (br == 0)
20546 			return 0;
20547 		regtail(preg, ret, br);
20548 		if (!(flags&HASWIDTH))
20549 			*flagp &= ~HASWIDTH;
20550 		*flagp |= flags&SPSTART;
20551 	}
20552 
20553 
20554 	ender = regnode(preg, (paren) ? CLOSE+parno : END);
20555 	regtail(preg, ret, ender);
20556 
20557 
20558 	for (br = ret; br != 0; br = regnext(preg, br))
20559 		regoptail(preg, br, ender);
20560 
20561 
20562 	if (paren && *preg->regparse++ != ')') {
20563 		preg->err = REG_ERR_UNMATCHED_PAREN;
20564 		return 0;
20565 	} else if (!paren && *preg->regparse != '\0') {
20566 		if (*preg->regparse == ')') {
20567 			preg->err = REG_ERR_UNMATCHED_PAREN;
20568 			return 0;
20569 		} else {
20570 			preg->err = REG_ERR_JUNK_ON_END;
20571 			return 0;
20572 		}
20573 	}
20574 
20575 	return(ret);
20576 }
20577 
regbranch(regex_t * preg,int * flagp)20578 static int regbranch(regex_t *preg, int *flagp )
20579 {
20580 	int ret;
20581 	int chain;
20582 	int latest;
20583 	int flags;
20584 
20585 	*flagp = WORST;
20586 
20587 	ret = regnode(preg, BRANCH);
20588 	chain = 0;
20589 	while (*preg->regparse != '\0' && *preg->regparse != ')' &&
20590 	       *preg->regparse != '|') {
20591 		latest = regpiece(preg, &flags);
20592 		if (latest == 0)
20593 			return 0;
20594 		*flagp |= flags&HASWIDTH;
20595 		if (chain == 0) {
20596 			*flagp |= flags&SPSTART;
20597 		}
20598 		else {
20599 			regtail(preg, chain, latest);
20600 		}
20601 		chain = latest;
20602 	}
20603 	if (chain == 0)
20604 		(void) regnode(preg, NOTHING);
20605 
20606 	return(ret);
20607 }
20608 
regpiece(regex_t * preg,int * flagp)20609 static int regpiece(regex_t *preg, int *flagp)
20610 {
20611 	int ret;
20612 	char op;
20613 	int next;
20614 	int flags;
20615 	int min;
20616 	int max;
20617 
20618 	ret = regatom(preg, &flags);
20619 	if (ret == 0)
20620 		return 0;
20621 
20622 	op = *preg->regparse;
20623 	if (!ISMULT(op)) {
20624 		*flagp = flags;
20625 		return(ret);
20626 	}
20627 
20628 	if (!(flags&HASWIDTH) && op != '?') {
20629 		preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
20630 		return 0;
20631 	}
20632 
20633 
20634 	if (op == '{') {
20635 		char *end;
20636 
20637 		min = strtoul(preg->regparse + 1, &end, 10);
20638 		if (end == preg->regparse + 1) {
20639 			preg->err = REG_ERR_BAD_COUNT;
20640 			return 0;
20641 		}
20642 		if (*end == '}') {
20643 			max = min;
20644 		}
20645 		else if (*end == '\0') {
20646 			preg->err = REG_ERR_UNMATCHED_BRACES;
20647 			return 0;
20648 		}
20649 		else {
20650 			preg->regparse = end;
20651 			max = strtoul(preg->regparse + 1, &end, 10);
20652 			if (*end != '}') {
20653 				preg->err = REG_ERR_UNMATCHED_BRACES;
20654 				return 0;
20655 			}
20656 		}
20657 		if (end == preg->regparse + 1) {
20658 			max = MAX_REP_COUNT;
20659 		}
20660 		else if (max < min || max >= 100) {
20661 			preg->err = REG_ERR_BAD_COUNT;
20662 			return 0;
20663 		}
20664 		if (min >= 100) {
20665 			preg->err = REG_ERR_BAD_COUNT;
20666 			return 0;
20667 		}
20668 
20669 		preg->regparse = strchr(preg->regparse, '}');
20670 	}
20671 	else {
20672 		min = (op == '+');
20673 		max = (op == '?' ? 1 : MAX_REP_COUNT);
20674 	}
20675 
20676 	if (preg->regparse[1] == '?') {
20677 		preg->regparse++;
20678 		next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret);
20679 	}
20680 	else {
20681 		next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret);
20682 	}
20683 	preg->program[ret + 2] = max;
20684 	preg->program[ret + 3] = min;
20685 	preg->program[ret + 4] = 0;
20686 
20687 	*flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART);
20688 
20689 	if (!(flags & SIMPLE)) {
20690 		int back = regnode(preg, BACK);
20691 		regtail(preg, back, ret);
20692 		regtail(preg, next, back);
20693 	}
20694 
20695 	preg->regparse++;
20696 	if (ISMULT(*preg->regparse)) {
20697 		preg->err = REG_ERR_NESTED_COUNT;
20698 		return 0;
20699 	}
20700 
20701 	return ret;
20702 }
20703 
reg_addrange(regex_t * preg,int lower,int upper)20704 static void reg_addrange(regex_t *preg, int lower, int upper)
20705 {
20706 	if (lower > upper) {
20707 		reg_addrange(preg, upper, lower);
20708 	}
20709 
20710 	regc(preg, upper - lower + 1);
20711 	regc(preg, lower);
20712 }
20713 
reg_addrange_str(regex_t * preg,const char * str)20714 static void reg_addrange_str(regex_t *preg, const char *str)
20715 {
20716 	while (*str) {
20717 		reg_addrange(preg, *str, *str);
20718 		str++;
20719 	}
20720 }
20721 
reg_utf8_tounicode_case(const char * s,int * uc,int upper)20722 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper)
20723 {
20724 	int l = utf8_tounicode(s, uc);
20725 	if (upper) {
20726 		*uc = utf8_upper(*uc);
20727 	}
20728 	return l;
20729 }
20730 
hexdigitval(int c)20731 static int hexdigitval(int c)
20732 {
20733 	if (c >= '0' && c <= '9')
20734 		return c - '0';
20735 	if (c >= 'a' && c <= 'f')
20736 		return c - 'a' + 10;
20737 	if (c >= 'A' && c <= 'F')
20738 		return c - 'A' + 10;
20739 	return -1;
20740 }
20741 
parse_hex(const char * s,int n,int * uc)20742 static int parse_hex(const char *s, int n, int *uc)
20743 {
20744 	int val = 0;
20745 	int k;
20746 
20747 	for (k = 0; k < n; k++) {
20748 		int c = hexdigitval(*s++);
20749 		if (c == -1) {
20750 			break;
20751 		}
20752 		val = (val << 4) | c;
20753 	}
20754 	if (k) {
20755 		*uc = val;
20756 	}
20757 	return k;
20758 }
20759 
reg_decode_escape(const char * s,int * ch)20760 static int reg_decode_escape(const char *s, int *ch)
20761 {
20762 	int n;
20763 	const char *s0 = s;
20764 
20765 	*ch = *s++;
20766 
20767 	switch (*ch) {
20768 		case 'b': *ch = '\b'; break;
20769 		case 'e': *ch = 27; break;
20770 		case 'f': *ch = '\f'; break;
20771 		case 'n': *ch = '\n'; break;
20772 		case 'r': *ch = '\r'; break;
20773 		case 't': *ch = '\t'; break;
20774 		case 'v': *ch = '\v'; break;
20775 		case 'u':
20776 			if (*s == '{') {
20777 
20778 				n = parse_hex(s + 1, 6, ch);
20779 				if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
20780 					s += n + 2;
20781 				}
20782 				else {
20783 
20784 					*ch = 'u';
20785 				}
20786 			}
20787 			else if ((n = parse_hex(s, 4, ch)) > 0) {
20788 				s += n;
20789 			}
20790 			break;
20791 		case 'U':
20792 			if ((n = parse_hex(s, 8, ch)) > 0) {
20793 				s += n;
20794 			}
20795 			break;
20796 		case 'x':
20797 			if ((n = parse_hex(s, 2, ch)) > 0) {
20798 				s += n;
20799 			}
20800 			break;
20801 		case '\0':
20802 			s--;
20803 			*ch = '\\';
20804 			break;
20805 	}
20806 	return s - s0;
20807 }
20808 
regatom(regex_t * preg,int * flagp)20809 static int regatom(regex_t *preg, int *flagp)
20810 {
20811 	int ret;
20812 	int flags;
20813 	int nocase = (preg->cflags & REG_ICASE);
20814 
20815 	int ch;
20816 	int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);
20817 
20818 	*flagp = WORST;
20819 
20820 	preg->regparse += n;
20821 	switch (ch) {
20822 
20823 	case '^':
20824 		ret = regnode(preg, BOL);
20825 		break;
20826 	case '$':
20827 		ret = regnode(preg, EOL);
20828 		break;
20829 	case '.':
20830 		ret = regnode(preg, ANY);
20831 		*flagp |= HASWIDTH|SIMPLE;
20832 		break;
20833 	case '[': {
20834 			const char *pattern = preg->regparse;
20835 
20836 			if (*pattern == '^') {
20837 				ret = regnode(preg, ANYBUT);
20838 				pattern++;
20839 			} else
20840 				ret = regnode(preg, ANYOF);
20841 
20842 
20843 			if (*pattern == ']' || *pattern == '-') {
20844 				reg_addrange(preg, *pattern, *pattern);
20845 				pattern++;
20846 			}
20847 
20848 			while (*pattern && *pattern != ']') {
20849 
20850 				int start;
20851 				int end;
20852 
20853 				enum {
20854 					CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
20855 					CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
20856 					CC_NUM
20857 				};
20858 				int cc;
20859 
20860 				pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
20861 				if (start == '\\') {
20862 
20863 					switch (*pattern) {
20864 						case 's':
20865 							pattern++;
20866 							cc = CC_SPACE;
20867 							goto cc_switch;
20868 						case 'd':
20869 							pattern++;
20870 							cc = CC_DIGIT;
20871 							goto cc_switch;
20872 						case 'w':
20873 							pattern++;
20874 							reg_addrange(preg, '_', '_');
20875 							cc = CC_ALNUM;
20876 							goto cc_switch;
20877 					}
20878 					pattern += reg_decode_escape(pattern, &start);
20879 					if (start == 0) {
20880 						preg->err = REG_ERR_NULL_CHAR;
20881 						return 0;
20882 					}
20883 				}
20884 				if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
20885 
20886 					pattern += utf8_tounicode(pattern, &end);
20887 					pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
20888 					if (end == '\\') {
20889 						pattern += reg_decode_escape(pattern, &end);
20890 						if (end == 0) {
20891 							preg->err = REG_ERR_NULL_CHAR;
20892 							return 0;
20893 						}
20894 					}
20895 
20896 					reg_addrange(preg, start, end);
20897 					continue;
20898 				}
20899 				if (start == '[' && pattern[0] == ':') {
20900 					static const char *character_class[] = {
20901 						":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
20902 						":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
20903 					};
20904 
20905 					for (cc = 0; cc < CC_NUM; cc++) {
20906 						n = strlen(character_class[cc]);
20907 						if (strncmp(pattern, character_class[cc], n) == 0) {
20908 
20909 							pattern += n + 1;
20910 							break;
20911 						}
20912 					}
20913 					if (cc != CC_NUM) {
20914 cc_switch:
20915 						switch (cc) {
20916 							case CC_ALNUM:
20917 								reg_addrange(preg, '0', '9');
20918 
20919 							case CC_ALPHA:
20920 								if ((preg->cflags & REG_ICASE) == 0) {
20921 									reg_addrange(preg, 'a', 'z');
20922 								}
20923 								reg_addrange(preg, 'A', 'Z');
20924 								break;
20925 							case CC_SPACE:
20926 								reg_addrange_str(preg, " \t\r\n\f\v");
20927 								break;
20928 							case CC_BLANK:
20929 								reg_addrange_str(preg, " \t");
20930 								break;
20931 							case CC_UPPER:
20932 								reg_addrange(preg, 'A', 'Z');
20933 								break;
20934 							case CC_LOWER:
20935 								reg_addrange(preg, 'a', 'z');
20936 								break;
20937 							case CC_XDIGIT:
20938 								reg_addrange(preg, 'a', 'f');
20939 								reg_addrange(preg, 'A', 'F');
20940 
20941 							case CC_DIGIT:
20942 								reg_addrange(preg, '0', '9');
20943 								break;
20944 							case CC_CNTRL:
20945 								reg_addrange(preg, 0, 31);
20946 								reg_addrange(preg, 127, 127);
20947 								break;
20948 							case CC_PRINT:
20949 								reg_addrange(preg, ' ', '~');
20950 								break;
20951 							case CC_GRAPH:
20952 								reg_addrange(preg, '!', '~');
20953 								break;
20954 							case CC_PUNCT:
20955 								reg_addrange(preg, '!', '/');
20956 								reg_addrange(preg, ':', '@');
20957 								reg_addrange(preg, '[', '`');
20958 								reg_addrange(preg, '{', '~');
20959 								break;
20960 						}
20961 						continue;
20962 					}
20963 				}
20964 
20965 				reg_addrange(preg, start, start);
20966 			}
20967 			regc(preg, '\0');
20968 
20969 			if (*pattern) {
20970 				pattern++;
20971 			}
20972 			preg->regparse = pattern;
20973 
20974 			*flagp |= HASWIDTH|SIMPLE;
20975 		}
20976 		break;
20977 	case '(':
20978 		ret = reg(preg, 1, &flags);
20979 		if (ret == 0)
20980 			return 0;
20981 		*flagp |= flags&(HASWIDTH|SPSTART);
20982 		break;
20983 	case '\0':
20984 	case '|':
20985 	case ')':
20986 		preg->err = REG_ERR_INTERNAL;
20987 		return 0;
20988 	case '?':
20989 	case '+':
20990 	case '*':
20991 	case '{':
20992 		preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
20993 		return 0;
20994 	case '\\':
20995 		ch = *preg->regparse++;
20996 		switch (ch) {
20997 		case '\0':
20998 			preg->err = REG_ERR_TRAILING_BACKSLASH;
20999 			return 0;
21000 		case 'A':
21001 			ret = regnode(preg, BOLX);
21002 			break;
21003 		case 'Z':
21004 			ret = regnode(preg, EOLX);
21005 			break;
21006 		case '<':
21007 		case 'm':
21008 			ret = regnode(preg, WORDA);
21009 			break;
21010 		case '>':
21011 		case 'M':
21012 			ret = regnode(preg, WORDZ);
21013 			break;
21014 		case 'd':
21015 		case 'D':
21016 			ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT);
21017 			reg_addrange(preg, '0', '9');
21018 			regc(preg, '\0');
21019 			*flagp |= HASWIDTH|SIMPLE;
21020 			break;
21021 		case 'w':
21022 		case 'W':
21023 			ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT);
21024 			if ((preg->cflags & REG_ICASE) == 0) {
21025 				reg_addrange(preg, 'a', 'z');
21026 			}
21027 			reg_addrange(preg, 'A', 'Z');
21028 			reg_addrange(preg, '0', '9');
21029 			reg_addrange(preg, '_', '_');
21030 			regc(preg, '\0');
21031 			*flagp |= HASWIDTH|SIMPLE;
21032 			break;
21033 		case 's':
21034 		case 'S':
21035 			ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT);
21036 			reg_addrange_str(preg," \t\r\n\f\v");
21037 			regc(preg, '\0');
21038 			*flagp |= HASWIDTH|SIMPLE;
21039 			break;
21040 
21041 		default:
21042 
21043 
21044 			preg->regparse--;
21045 			goto de_fault;
21046 		}
21047 		break;
21048 	de_fault:
21049 	default: {
21050 			int added = 0;
21051 
21052 
21053 			preg->regparse -= n;
21054 
21055 			ret = regnode(preg, EXACTLY);
21056 
21057 
21058 
21059 			while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
21060 				n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
21061 				if (ch == '\\' && preg->regparse[n]) {
21062 					if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) {
21063 
21064 						break;
21065 					}
21066 					n += reg_decode_escape(preg->regparse + n, &ch);
21067 					if (ch == 0) {
21068 						preg->err = REG_ERR_NULL_CHAR;
21069 						return 0;
21070 					}
21071 				}
21072 
21073 
21074 				if (ISMULT(preg->regparse[n])) {
21075 
21076 					if (added) {
21077 
21078 						break;
21079 					}
21080 
21081 					regc(preg, ch);
21082 					added++;
21083 					preg->regparse += n;
21084 					break;
21085 				}
21086 
21087 
21088 				regc(preg, ch);
21089 				added++;
21090 				preg->regparse += n;
21091 			}
21092 			regc(preg, '\0');
21093 
21094 			*flagp |= HASWIDTH;
21095 			if (added == 1)
21096 				*flagp |= SIMPLE;
21097 			break;
21098 		}
21099 		break;
21100 	}
21101 
21102 	return(ret);
21103 }
21104 
reg_grow(regex_t * preg,int n)21105 static void reg_grow(regex_t *preg, int n)
21106 {
21107 	if (preg->p + n >= preg->proglen) {
21108 		preg->proglen = (preg->p + n) * 2;
21109 		preg->program = realloc(preg->program, preg->proglen * sizeof(int));
21110 	}
21111 }
21112 
21113 
regnode(regex_t * preg,int op)21114 static int regnode(regex_t *preg, int op)
21115 {
21116 	reg_grow(preg, 2);
21117 
21118 
21119 	preg->program[preg->p++] = op;
21120 	preg->program[preg->p++] = 0;
21121 
21122 
21123 	return preg->p - 2;
21124 }
21125 
regc(regex_t * preg,int b)21126 static void regc(regex_t *preg, int b )
21127 {
21128 	reg_grow(preg, 1);
21129 	preg->program[preg->p++] = b;
21130 }
21131 
reginsert(regex_t * preg,int op,int size,int opnd)21132 static int reginsert(regex_t *preg, int op, int size, int opnd )
21133 {
21134 	reg_grow(preg, size);
21135 
21136 
21137 	memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
21138 
21139 	memset(preg->program + opnd, 0, sizeof(int) * size);
21140 
21141 	preg->program[opnd] = op;
21142 
21143 	preg->p += size;
21144 
21145 	return opnd + size;
21146 }
21147 
regtail(regex_t * preg,int p,int val)21148 static void regtail(regex_t *preg, int p, int val)
21149 {
21150 	int scan;
21151 	int temp;
21152 	int offset;
21153 
21154 
21155 	scan = p;
21156 	for (;;) {
21157 		temp = regnext(preg, scan);
21158 		if (temp == 0)
21159 			break;
21160 		scan = temp;
21161 	}
21162 
21163 	if (OP(preg, scan) == BACK)
21164 		offset = scan - val;
21165 	else
21166 		offset = val - scan;
21167 
21168 	preg->program[scan + 1] = offset;
21169 }
21170 
21171 
regoptail(regex_t * preg,int p,int val)21172 static void regoptail(regex_t *preg, int p, int val )
21173 {
21174 
21175 	if (p != 0 && OP(preg, p) == BRANCH) {
21176 		regtail(preg, OPERAND(p), val);
21177 	}
21178 }
21179 
21180 
21181 static int regtry(regex_t *preg, const char *string );
21182 static int regmatch(regex_t *preg, int prog);
21183 static int regrepeat(regex_t *preg, int p, int max);
21184 
regexec(regex_t * preg,const char * string,size_t nmatch,regmatch_t pmatch[],int eflags)21185 int regexec(regex_t  *preg,  const  char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
21186 {
21187 	const char *s;
21188 	int scan;
21189 
21190 
21191 	if (preg == NULL || preg->program == NULL || string == NULL) {
21192 		return REG_ERR_NULL_ARGUMENT;
21193 	}
21194 
21195 
21196 	if (*preg->program != REG_MAGIC) {
21197 		return REG_ERR_CORRUPTED;
21198 	}
21199 
21200 #ifdef DEBUG
21201 	fprintf(stderr, "regexec: %s\n", string);
21202 	regdump(preg);
21203 #endif
21204 
21205 	preg->eflags = eflags;
21206 	preg->pmatch = pmatch;
21207 	preg->nmatch = nmatch;
21208 	preg->start = string;
21209 
21210 
21211 	for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) {
21212 		int op = OP(preg, scan);
21213 		if (op == END)
21214 			break;
21215 		if (op == REPX || op == REPXMIN)
21216 			preg->program[scan + 4] = 0;
21217 	}
21218 
21219 
21220 	if (preg->regmust != 0) {
21221 		s = string;
21222 		while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
21223 			if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
21224 				break;
21225 			}
21226 			s++;
21227 		}
21228 		if (s == NULL)
21229 			return REG_NOMATCH;
21230 	}
21231 
21232 
21233 	preg->regbol = string;
21234 
21235 
21236 	if (preg->reganch) {
21237 		if (eflags & REG_NOTBOL) {
21238 
21239 			goto nextline;
21240 		}
21241 		while (1) {
21242 			if (regtry(preg, string)) {
21243 				return REG_NOERROR;
21244 			}
21245 			if (*string) {
21246 nextline:
21247 				if (preg->cflags & REG_NEWLINE) {
21248 
21249 					string = strchr(string, '\n');
21250 					if (string) {
21251 						preg->regbol = ++string;
21252 						continue;
21253 					}
21254 				}
21255 			}
21256 			return REG_NOMATCH;
21257 		}
21258 	}
21259 
21260 
21261 	s = string;
21262 	if (preg->regstart != '\0') {
21263 
21264 		while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
21265 			if (regtry(preg, s))
21266 				return REG_NOERROR;
21267 			s++;
21268 		}
21269 	}
21270 	else
21271 
21272 		while (1) {
21273 			if (regtry(preg, s))
21274 				return REG_NOERROR;
21275 			if (*s == '\0') {
21276 				break;
21277 			}
21278 			else {
21279 				int c;
21280 				s += utf8_tounicode(s, &c);
21281 			}
21282 		}
21283 
21284 
21285 	return REG_NOMATCH;
21286 }
21287 
21288 
regtry(regex_t * preg,const char * string)21289 static int regtry( regex_t *preg, const char *string )
21290 {
21291 	int i;
21292 
21293 	preg->reginput = string;
21294 
21295 	for (i = 0; i < preg->nmatch; i++) {
21296 		preg->pmatch[i].rm_so = -1;
21297 		preg->pmatch[i].rm_eo = -1;
21298 	}
21299 	if (regmatch(preg, 1)) {
21300 		preg->pmatch[0].rm_so = string - preg->start;
21301 		preg->pmatch[0].rm_eo = preg->reginput - preg->start;
21302 		return(1);
21303 	} else
21304 		return(0);
21305 }
21306 
prefix_cmp(const int * prog,int proglen,const char * string,int nocase)21307 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase)
21308 {
21309 	const char *s = string;
21310 	while (proglen && *s) {
21311 		int ch;
21312 		int n = reg_utf8_tounicode_case(s, &ch, nocase);
21313 		if (ch != *prog) {
21314 			return -1;
21315 		}
21316 		prog++;
21317 		s += n;
21318 		proglen--;
21319 	}
21320 	if (proglen == 0) {
21321 		return s - string;
21322 	}
21323 	return -1;
21324 }
21325 
reg_range_find(const int * range,int c)21326 static int reg_range_find(const int *range, int c)
21327 {
21328 	while (*range) {
21329 
21330 		if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
21331 			return 1;
21332 		}
21333 		range += 2;
21334 	}
21335 	return 0;
21336 }
21337 
str_find(const char * string,int c,int nocase)21338 static const char *str_find(const char *string, int c, int nocase)
21339 {
21340 	if (nocase) {
21341 
21342 		c = utf8_upper(c);
21343 	}
21344 	while (*string) {
21345 		int ch;
21346 		int n = reg_utf8_tounicode_case(string, &ch, nocase);
21347 		if (c == ch) {
21348 			return string;
21349 		}
21350 		string += n;
21351 	}
21352 	return NULL;
21353 }
21354 
reg_iseol(regex_t * preg,int ch)21355 static int reg_iseol(regex_t *preg, int ch)
21356 {
21357 	if (preg->cflags & REG_NEWLINE) {
21358 		return ch == '\0' || ch == '\n';
21359 	}
21360 	else {
21361 		return ch == '\0';
21362 	}
21363 }
21364 
regmatchsimplerepeat(regex_t * preg,int scan,int matchmin)21365 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin)
21366 {
21367 	int nextch = '\0';
21368 	const char *save;
21369 	int no;
21370 	int c;
21371 
21372 	int max = preg->program[scan + 2];
21373 	int min = preg->program[scan + 3];
21374 	int next = regnext(preg, scan);
21375 
21376 	if (OP(preg, next) == EXACTLY) {
21377 		nextch = preg->program[OPERAND(next)];
21378 	}
21379 	save = preg->reginput;
21380 	no = regrepeat(preg, scan + 5, max);
21381 	if (no < min) {
21382 		return 0;
21383 	}
21384 	if (matchmin) {
21385 
21386 		max = no;
21387 		no = min;
21388 	}
21389 
21390 	while (1) {
21391 		if (matchmin) {
21392 			if (no > max) {
21393 				break;
21394 			}
21395 		}
21396 		else {
21397 			if (no < min) {
21398 				break;
21399 			}
21400 		}
21401 		preg->reginput = save + utf8_index(save, no);
21402 		reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21403 
21404 		if (reg_iseol(preg, nextch) || c == nextch) {
21405 			if (regmatch(preg, next)) {
21406 				return(1);
21407 			}
21408 		}
21409 		if (matchmin) {
21410 
21411 			no++;
21412 		}
21413 		else {
21414 
21415 			no--;
21416 		}
21417 	}
21418 	return(0);
21419 }
21420 
regmatchrepeat(regex_t * preg,int scan,int matchmin)21421 static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
21422 {
21423 	int *scanpt = preg->program + scan;
21424 
21425 	int max = scanpt[2];
21426 	int min = scanpt[3];
21427 
21428 
21429 	if (scanpt[4] < min) {
21430 
21431 		scanpt[4]++;
21432 		if (regmatch(preg, scan + 5)) {
21433 			return 1;
21434 		}
21435 		scanpt[4]--;
21436 		return 0;
21437 	}
21438 	if (scanpt[4] > max) {
21439 		return 0;
21440 	}
21441 
21442 	if (matchmin) {
21443 
21444 		if (regmatch(preg, regnext(preg, scan))) {
21445 			return 1;
21446 		}
21447 
21448 		scanpt[4]++;
21449 		if (regmatch(preg, scan + 5)) {
21450 			return 1;
21451 		}
21452 		scanpt[4]--;
21453 		return 0;
21454 	}
21455 
21456 	if (scanpt[4] < max) {
21457 		scanpt[4]++;
21458 		if (regmatch(preg, scan + 5)) {
21459 			return 1;
21460 		}
21461 		scanpt[4]--;
21462 	}
21463 
21464 	return regmatch(preg, regnext(preg, scan));
21465 }
21466 
21467 
regmatch(regex_t * preg,int prog)21468 static int regmatch(regex_t *preg, int prog)
21469 {
21470 	int scan;
21471 	int next;
21472 	const char *save;
21473 
21474 	scan = prog;
21475 
21476 #ifdef DEBUG
21477 	if (scan != 0 && regnarrate)
21478 		fprintf(stderr, "%s(\n", regprop(scan));
21479 #endif
21480 	while (scan != 0) {
21481 		int n;
21482 		int c;
21483 #ifdef DEBUG
21484 		if (regnarrate) {
21485 			fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
21486 		}
21487 #endif
21488 		next = regnext(preg, scan);
21489 		n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21490 
21491 		switch (OP(preg, scan)) {
21492 		case BOLX:
21493 			if ((preg->eflags & REG_NOTBOL)) {
21494 				return(0);
21495 			}
21496 
21497 		case BOL:
21498 			if (preg->reginput != preg->regbol) {
21499 				return(0);
21500 			}
21501 			break;
21502 		case EOLX:
21503 			if (c != 0) {
21504 
21505 				return 0;
21506 			}
21507 			break;
21508 		case EOL:
21509 			if (!reg_iseol(preg, c)) {
21510 				return(0);
21511 			}
21512 			break;
21513 		case WORDA:
21514 
21515 			if ((!isalnum(UCHAR(c))) && c != '_')
21516 				return(0);
21517 
21518 			if (preg->reginput > preg->regbol &&
21519 				(isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
21520 				return(0);
21521 			break;
21522 		case WORDZ:
21523 
21524 			if (preg->reginput > preg->regbol) {
21525 
21526 				if (reg_iseol(preg, c) || !isalnum(UCHAR(c)) || c != '_') {
21527 					c = preg->reginput[-1];
21528 
21529 					if (isalnum(UCHAR(c)) || c == '_') {
21530 						break;
21531 					}
21532 				}
21533 			}
21534 
21535 			return(0);
21536 
21537 		case ANY:
21538 			if (reg_iseol(preg, c))
21539 				return 0;
21540 			preg->reginput += n;
21541 			break;
21542 		case EXACTLY: {
21543 				int opnd;
21544 				int len;
21545 				int slen;
21546 
21547 				opnd = OPERAND(scan);
21548 				len = str_int_len(preg->program + opnd);
21549 
21550 				slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE);
21551 				if (slen < 0) {
21552 					return(0);
21553 				}
21554 				preg->reginput += slen;
21555 			}
21556 			break;
21557 		case ANYOF:
21558 			if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) {
21559 				return(0);
21560 			}
21561 			preg->reginput += n;
21562 			break;
21563 		case ANYBUT:
21564 			if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) {
21565 				return(0);
21566 			}
21567 			preg->reginput += n;
21568 			break;
21569 		case NOTHING:
21570 			break;
21571 		case BACK:
21572 			break;
21573 		case BRANCH:
21574 			if (OP(preg, next) != BRANCH)
21575 				next = OPERAND(scan);
21576 			else {
21577 				do {
21578 					save = preg->reginput;
21579 					if (regmatch(preg, OPERAND(scan))) {
21580 						return(1);
21581 					}
21582 					preg->reginput = save;
21583 					scan = regnext(preg, scan);
21584 				} while (scan != 0 && OP(preg, scan) == BRANCH);
21585 				return(0);
21586 
21587 			}
21588 			break;
21589 		case REP:
21590 		case REPMIN:
21591 			return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);
21592 
21593 		case REPX:
21594 		case REPXMIN:
21595 			return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);
21596 
21597 		case END:
21598 			return 1;
21599 
21600 		case OPENNC:
21601 		case CLOSENC:
21602 			return regmatch(preg, next);
21603 
21604 		default:
21605 			if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
21606 				save = preg->reginput;
21607 				if (regmatch(preg, next)) {
21608 					if (OP(preg, scan) < CLOSE) {
21609 						int no = OP(preg, scan) - OPEN;
21610 						if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
21611 							preg->pmatch[no].rm_so = save - preg->start;
21612 						}
21613 					}
21614 					else {
21615 						int no = OP(preg, scan) - CLOSE;
21616 						if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
21617 							preg->pmatch[no].rm_eo = save - preg->start;
21618 						}
21619 					}
21620 					return(1);
21621 				}
21622 				return(0);
21623 			}
21624 			return REG_ERR_INTERNAL;
21625 		}
21626 
21627 		scan = next;
21628 	}
21629 
21630 	return REG_ERR_INTERNAL;
21631 }
21632 
regrepeat(regex_t * preg,int p,int max)21633 static int regrepeat(regex_t *preg, int p, int max)
21634 {
21635 	int count = 0;
21636 	const char *scan;
21637 	int opnd;
21638 	int ch;
21639 	int n;
21640 
21641 	scan = preg->reginput;
21642 	opnd = OPERAND(p);
21643 	switch (OP(preg, p)) {
21644 	case ANY:
21645 
21646 		while (!reg_iseol(preg, *scan) && count < max) {
21647 			count++;
21648 			scan++;
21649 		}
21650 		break;
21651 	case EXACTLY:
21652 		while (count < max) {
21653 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21654 			if (preg->program[opnd] != ch) {
21655 				break;
21656 			}
21657 			count++;
21658 			scan += n;
21659 		}
21660 		break;
21661 	case ANYOF:
21662 		while (count < max) {
21663 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21664 			if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) {
21665 				break;
21666 			}
21667 			count++;
21668 			scan += n;
21669 		}
21670 		break;
21671 	case ANYBUT:
21672 		while (count < max) {
21673 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21674 			if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
21675 				break;
21676 			}
21677 			count++;
21678 			scan += n;
21679 		}
21680 		break;
21681 	default:
21682 		preg->err = REG_ERR_INTERNAL;
21683 		count = 0;
21684 		break;
21685 	}
21686 	preg->reginput = scan;
21687 
21688 	return(count);
21689 }
21690 
regnext(regex_t * preg,int p)21691 static int regnext(regex_t *preg, int p )
21692 {
21693 	int offset;
21694 
21695 	offset = NEXT(preg, p);
21696 
21697 	if (offset == 0)
21698 		return 0;
21699 
21700 	if (OP(preg, p) == BACK)
21701 		return(p-offset);
21702 	else
21703 		return(p+offset);
21704 }
21705 
regopsize(regex_t * preg,int p)21706 static int regopsize(regex_t *preg, int p )
21707 {
21708 
21709 	switch (OP(preg, p)) {
21710 		case REP:
21711 		case REPMIN:
21712 		case REPX:
21713 		case REPXMIN:
21714 			return 5;
21715 
21716 		case ANYOF:
21717 		case ANYBUT:
21718 		case EXACTLY: {
21719 			int s = p + 2;
21720 			while (preg->program[s++]) {
21721 			}
21722 			return s - p;
21723 		}
21724 	}
21725 	return 2;
21726 }
21727 
21728 
regerror(int errcode,const regex_t * preg,char * errbuf,size_t errbuf_size)21729 size_t regerror(int errcode, const regex_t *preg, char *errbuf,  size_t errbuf_size)
21730 {
21731 	static const char *error_strings[] = {
21732 		"success",
21733 		"no match",
21734 		"bad pattern",
21735 		"null argument",
21736 		"unknown error",
21737 		"too big",
21738 		"out of memory",
21739 		"too many ()",
21740 		"parentheses () not balanced",
21741 		"braces {} not balanced",
21742 		"invalid repetition count(s)",
21743 		"extra characters",
21744 		"*+ of empty atom",
21745 		"nested count",
21746 		"internal error",
21747 		"count follows nothing",
21748 		"trailing backslash",
21749 		"corrupted program",
21750 		"contains null char",
21751 	};
21752 	const char *err;
21753 
21754 	if (errcode < 0 || errcode >= REG_ERR_NUM) {
21755 		err = "Bad error code";
21756 	}
21757 	else {
21758 		err = error_strings[errcode];
21759 	}
21760 
21761 	return snprintf(errbuf, errbuf_size, "%s", err);
21762 }
21763 
regfree(regex_t * preg)21764 void regfree(regex_t *preg)
21765 {
21766 	free(preg->program);
21767 }
21768 
21769 #endif
21770 #include <string.h>
21771 
Jim_SetResultErrno(Jim_Interp * interp,const char * msg)21772 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
21773 {
21774     Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno()));
21775 }
21776 
21777 #if defined(__MINGW32__)
21778 #include <sys/stat.h>
21779 
Jim_Errno(void)21780 int Jim_Errno(void)
21781 {
21782     switch (GetLastError()) {
21783     case ERROR_FILE_NOT_FOUND: return ENOENT;
21784     case ERROR_PATH_NOT_FOUND: return ENOENT;
21785     case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
21786     case ERROR_ACCESS_DENIED: return EACCES;
21787     case ERROR_INVALID_HANDLE: return EBADF;
21788     case ERROR_BAD_ENVIRONMENT: return E2BIG;
21789     case ERROR_BAD_FORMAT: return ENOEXEC;
21790     case ERROR_INVALID_ACCESS: return EACCES;
21791     case ERROR_INVALID_DRIVE: return ENOENT;
21792     case ERROR_CURRENT_DIRECTORY: return EACCES;
21793     case ERROR_NOT_SAME_DEVICE: return EXDEV;
21794     case ERROR_NO_MORE_FILES: return ENOENT;
21795     case ERROR_WRITE_PROTECT: return EROFS;
21796     case ERROR_BAD_UNIT: return ENXIO;
21797     case ERROR_NOT_READY: return EBUSY;
21798     case ERROR_BAD_COMMAND: return EIO;
21799     case ERROR_CRC: return EIO;
21800     case ERROR_BAD_LENGTH: return EIO;
21801     case ERROR_SEEK: return EIO;
21802     case ERROR_WRITE_FAULT: return EIO;
21803     case ERROR_READ_FAULT: return EIO;
21804     case ERROR_GEN_FAILURE: return EIO;
21805     case ERROR_SHARING_VIOLATION: return EACCES;
21806     case ERROR_LOCK_VIOLATION: return EACCES;
21807     case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
21808     case ERROR_HANDLE_DISK_FULL: return ENOSPC;
21809     case ERROR_NOT_SUPPORTED: return ENODEV;
21810     case ERROR_REM_NOT_LIST: return EBUSY;
21811     case ERROR_DUP_NAME: return EEXIST;
21812     case ERROR_BAD_NETPATH: return ENOENT;
21813     case ERROR_NETWORK_BUSY: return EBUSY;
21814     case ERROR_DEV_NOT_EXIST: return ENODEV;
21815     case ERROR_TOO_MANY_CMDS: return EAGAIN;
21816     case ERROR_ADAP_HDW_ERR: return EIO;
21817     case ERROR_BAD_NET_RESP: return EIO;
21818     case ERROR_UNEXP_NET_ERR: return EIO;
21819     case ERROR_NETNAME_DELETED: return ENOENT;
21820     case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
21821     case ERROR_BAD_DEV_TYPE: return ENODEV;
21822     case ERROR_BAD_NET_NAME: return ENOENT;
21823     case ERROR_TOO_MANY_NAMES: return ENFILE;
21824     case ERROR_TOO_MANY_SESS: return EIO;
21825     case ERROR_SHARING_PAUSED: return EAGAIN;
21826     case ERROR_REDIR_PAUSED: return EAGAIN;
21827     case ERROR_FILE_EXISTS: return EEXIST;
21828     case ERROR_CANNOT_MAKE: return ENOSPC;
21829     case ERROR_OUT_OF_STRUCTURES: return ENFILE;
21830     case ERROR_ALREADY_ASSIGNED: return EEXIST;
21831     case ERROR_INVALID_PASSWORD: return EPERM;
21832     case ERROR_NET_WRITE_FAULT: return EIO;
21833     case ERROR_NO_PROC_SLOTS: return EAGAIN;
21834     case ERROR_DISK_CHANGE: return EXDEV;
21835     case ERROR_BROKEN_PIPE: return EPIPE;
21836     case ERROR_OPEN_FAILED: return ENOENT;
21837     case ERROR_DISK_FULL: return ENOSPC;
21838     case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
21839     case ERROR_INVALID_TARGET_HANDLE: return EBADF;
21840     case ERROR_INVALID_NAME: return ENOENT;
21841     case ERROR_PROC_NOT_FOUND: return ESRCH;
21842     case ERROR_WAIT_NO_CHILDREN: return ECHILD;
21843     case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
21844     case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
21845     case ERROR_SEEK_ON_DEVICE: return ESPIPE;
21846     case ERROR_BUSY_DRIVE: return EAGAIN;
21847     case ERROR_DIR_NOT_EMPTY: return EEXIST;
21848     case ERROR_NOT_LOCKED: return EACCES;
21849     case ERROR_BAD_PATHNAME: return ENOENT;
21850     case ERROR_LOCK_FAILED: return EACCES;
21851     case ERROR_ALREADY_EXISTS: return EEXIST;
21852     case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
21853     case ERROR_BAD_PIPE: return EPIPE;
21854     case ERROR_PIPE_BUSY: return EAGAIN;
21855     case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
21856     case ERROR_DIRECTORY: return ENOTDIR;
21857     }
21858     return EINVAL;
21859 }
21860 
waitpid(pidtype pid,int * status,int nohang)21861 pidtype waitpid(pidtype pid, int *status, int nohang)
21862 {
21863     DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
21864     if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
21865 
21866         return JIM_BAD_PID;
21867     }
21868     GetExitCodeProcess(pid, &ret);
21869     *status = ret;
21870     CloseHandle(pid);
21871     return pid;
21872 }
21873 
Jim_MakeTempFile(Jim_Interp * interp,const char * filename_template,int unlink_file)21874 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
21875 {
21876     char name[MAX_PATH];
21877     HANDLE handle;
21878 
21879     if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) {
21880         return -1;
21881     }
21882 
21883     handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
21884             CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0),
21885             NULL);
21886 
21887     if (handle == INVALID_HANDLE_VALUE) {
21888         goto error;
21889     }
21890 
21891     Jim_SetResultString(interp, name, -1);
21892     return _open_osfhandle((int)handle, _O_RDWR | _O_TEXT);
21893 
21894   error:
21895     Jim_SetResultErrno(interp, name);
21896     DeleteFile(name);
21897     return -1;
21898 }
21899 
Jim_OpenForWrite(const char * filename,int append)21900 int Jim_OpenForWrite(const char *filename, int append)
21901 {
21902     if (strcmp(filename, "/dev/null") == 0) {
21903         filename = "nul:";
21904     }
21905     int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE);
21906     if (fd >= 0 && append) {
21907 
21908         _lseek(fd, 0L, SEEK_END);
21909     }
21910     return fd;
21911 }
21912 
Jim_OpenForRead(const char * filename)21913 int Jim_OpenForRead(const char *filename)
21914 {
21915     if (strcmp(filename, "/dev/null") == 0) {
21916         filename = "nul:";
21917     }
21918     return _open(filename, _O_RDONLY | _O_TEXT, 0);
21919 }
21920 
21921 #elif defined(HAVE_UNISTD_H)
21922 
21923 
21924 
Jim_MakeTempFile(Jim_Interp * interp,const char * filename_template,int unlink_file)21925 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
21926 {
21927     int fd;
21928     mode_t mask;
21929     Jim_Obj *filenameObj;
21930 
21931     if (filename_template == NULL) {
21932         const char *tmpdir = getenv("TMPDIR");
21933         if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
21934             tmpdir = "/tmp/";
21935         }
21936         filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
21937         if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
21938             Jim_AppendString(interp, filenameObj, "/", 1);
21939         }
21940         Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
21941     }
21942     else {
21943         filenameObj = Jim_NewStringObj(interp, filename_template, -1);
21944     }
21945 
21946 
21947     mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
21948 #ifdef HAVE_MKSTEMP
21949     fd = mkstemp(filenameObj->bytes);
21950 #else
21951     if (mktemp(filenameObj->bytes) == NULL) {
21952         fd = -1;
21953     }
21954     else {
21955         fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC);
21956     }
21957 #endif
21958     umask(mask);
21959     if (fd < 0) {
21960         Jim_SetResultErrno(interp, Jim_String(filenameObj));
21961         Jim_FreeNewObj(interp, filenameObj);
21962         return -1;
21963     }
21964     if (unlink_file) {
21965         remove(Jim_String(filenameObj));
21966     }
21967 
21968     Jim_SetResult(interp, filenameObj);
21969     return fd;
21970 }
21971 
Jim_OpenForWrite(const char * filename,int append)21972 int Jim_OpenForWrite(const char *filename, int append)
21973 {
21974     return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
21975 }
21976 
Jim_OpenForRead(const char * filename)21977 int Jim_OpenForRead(const char *filename)
21978 {
21979     return open(filename, O_RDONLY, 0);
21980 }
21981 
21982 #endif
21983 
21984 #if defined(_WIN32) || defined(WIN32)
21985 #ifndef STRICT
21986 #define STRICT
21987 #endif
21988 #define WIN32_LEAN_AND_MEAN
21989 #include <windows.h>
21990 
21991 #if defined(HAVE_DLOPEN_COMPAT)
dlopen(const char * path,int mode)21992 void *dlopen(const char *path, int mode)
21993 {
21994     JIM_NOTUSED(mode);
21995 
21996     return (void *)LoadLibraryA(path);
21997 }
21998 
dlclose(void * handle)21999 int dlclose(void *handle)
22000 {
22001     FreeLibrary((HANDLE)handle);
22002     return 0;
22003 }
22004 
dlsym(void * handle,const char * symbol)22005 void *dlsym(void *handle, const char *symbol)
22006 {
22007     return GetProcAddress((HMODULE)handle, symbol);
22008 }
22009 
dlerror(void)22010 char *dlerror(void)
22011 {
22012     static char msg[121];
22013     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
22014                    LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
22015     return msg;
22016 }
22017 #endif
22018 
22019 #ifdef _MSC_VER
22020 
22021 #include <sys/timeb.h>
22022 
22023 
gettimeofday(struct timeval * tv,void * unused)22024 int gettimeofday(struct timeval *tv, void *unused)
22025 {
22026     struct _timeb tb;
22027 
22028     _ftime(&tb);
22029     tv->tv_sec = tb.time;
22030     tv->tv_usec = tb.millitm * 1000;
22031 
22032     return 0;
22033 }
22034 
22035 
opendir(const char * name)22036 DIR *opendir(const char *name)
22037 {
22038     DIR *dir = 0;
22039 
22040     if (name && name[0]) {
22041         size_t base_length = strlen(name);
22042         const char *all =
22043             strchr("/\\", name[base_length - 1]) ? "*" : "/*";
22044 
22045         if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
22046             (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
22047             strcat(strcpy(dir->name, name), all);
22048 
22049             if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
22050                 dir->result.d_name = 0;
22051             else {
22052                 Jim_Free(dir->name);
22053                 Jim_Free(dir);
22054                 dir = 0;
22055             }
22056         }
22057         else {
22058             Jim_Free(dir);
22059             dir = 0;
22060             errno = ENOMEM;
22061         }
22062     }
22063     else {
22064         errno = EINVAL;
22065     }
22066     return dir;
22067 }
22068 
closedir(DIR * dir)22069 int closedir(DIR * dir)
22070 {
22071     int result = -1;
22072 
22073     if (dir) {
22074         if (dir->handle != -1)
22075             result = _findclose(dir->handle);
22076         Jim_Free(dir->name);
22077         Jim_Free(dir);
22078     }
22079     if (result == -1)
22080         errno = EBADF;
22081     return result;
22082 }
22083 
readdir(DIR * dir)22084 struct dirent *readdir(DIR * dir)
22085 {
22086     struct dirent *result = 0;
22087 
22088     if (dir && dir->handle != -1) {
22089         if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
22090             result = &dir->result;
22091             result->d_name = dir->info.name;
22092         }
22093     }
22094     else {
22095         errno = EBADF;
22096     }
22097     return result;
22098 }
22099 #endif
22100 #endif
22101 #include <stdio.h>
22102 #include <signal.h>
22103 
22104 
22105 
22106 
22107 
22108 
22109 #ifndef SIGPIPE
22110 #define SIGPIPE 13
22111 #endif
22112 #ifndef SIGINT
22113 #define SIGINT 2
22114 #endif
22115 
Jim_SignalId(int sig)22116 const char *Jim_SignalId(int sig)
22117 {
22118 	static char buf[10];
22119 	switch (sig) {
22120 		case SIGINT: return "SIGINT";
22121 		case SIGPIPE: return "SIGPIPE";
22122 
22123 	}
22124 	snprintf(buf, sizeof(buf), "%d", sig);
22125 	return buf;
22126 }
22127 #ifndef JIM_BOOTSTRAP_LIB_ONLY
22128 #include <errno.h>
22129 #include <string.h>
22130 
22131 
22132 #ifdef USE_LINENOISE
22133 #ifdef HAVE_UNISTD_H
22134     #include <unistd.h>
22135 #endif
22136 #ifdef HAVE_SYS_STAT_H
22137     #include <sys/stat.h>
22138 #endif
22139 #include "linenoise.h"
22140 #else
22141 #define MAX_LINE_LEN 512
22142 #endif
22143 
22144 #ifdef USE_LINENOISE
22145 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata);
22146 static const char completion_callback_assoc_key[] = "interactive-completion";
22147 #endif
22148 
Jim_HistoryGetline(Jim_Interp * interp,const char * prompt)22149 char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
22150 {
22151 #ifdef USE_LINENOISE
22152     struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key);
22153     char *result;
22154     Jim_Obj *objPtr;
22155     long mlmode = 0;
22156     if (compinfo) {
22157         linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
22158     }
22159     objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE);
22160     if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) {
22161         linenoiseSetMultiLine(mlmode);
22162     }
22163 
22164     result = linenoise(prompt);
22165 
22166     linenoiseSetCompletionCallback(NULL, NULL);
22167     return result;
22168 #else
22169     int len;
22170     char *line = malloc(MAX_LINE_LEN);
22171 
22172     fputs(prompt, stdout);
22173     fflush(stdout);
22174 
22175     if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
22176         free(line);
22177         return NULL;
22178     }
22179     len = strlen(line);
22180     if (len && line[len - 1] == '\n') {
22181         line[len - 1] = '\0';
22182     }
22183     return line;
22184 #endif
22185 }
22186 
Jim_HistoryLoad(const char * filename)22187 void Jim_HistoryLoad(const char *filename)
22188 {
22189 #ifdef USE_LINENOISE
22190     linenoiseHistoryLoad(filename);
22191 #endif
22192 }
22193 
Jim_HistoryAdd(const char * line)22194 void Jim_HistoryAdd(const char *line)
22195 {
22196 #ifdef USE_LINENOISE
22197     linenoiseHistoryAdd(line);
22198 #endif
22199 }
22200 
Jim_HistorySave(const char * filename)22201 void Jim_HistorySave(const char *filename)
22202 {
22203 #ifdef USE_LINENOISE
22204 #ifdef HAVE_UMASK
22205     mode_t mask;
22206 
22207     mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
22208 #endif
22209     linenoiseHistorySave(filename);
22210 #ifdef HAVE_UMASK
22211     umask(mask);
22212 #endif
22213 #endif
22214 }
22215 
Jim_HistoryShow(void)22216 void Jim_HistoryShow(void)
22217 {
22218 #ifdef USE_LINENOISE
22219 
22220     int i;
22221     int len;
22222     char **history = linenoiseHistory(&len);
22223     for (i = 0; i < len; i++) {
22224         printf("%4d %s\n", i + 1, history[i]);
22225     }
22226 #endif
22227 }
22228 
22229 #ifdef USE_LINENOISE
22230 struct JimCompletionInfo {
22231     Jim_Interp *interp;
22232     Jim_Obj *command;
22233 };
22234 
JimCompletionCallback(const char * prefix,linenoiseCompletions * comp,void * userdata)22235 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
22236 {
22237     struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
22238     Jim_Obj *objv[2];
22239     int ret;
22240 
22241     objv[0] = info->command;
22242     objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
22243 
22244     ret = Jim_EvalObjVector(info->interp, 2, objv);
22245 
22246 
22247     if (ret == JIM_OK) {
22248         int i;
22249         Jim_Obj *listObj = Jim_GetResult(info->interp);
22250         int len = Jim_ListLength(info->interp, listObj);
22251         for (i = 0; i < len; i++) {
22252             linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
22253         }
22254     }
22255 }
22256 
JimHistoryFreeCompletion(Jim_Interp * interp,void * data)22257 static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
22258 {
22259     struct JimCompletionInfo *compinfo = data;
22260 
22261     Jim_DecrRefCount(interp, compinfo->command);
22262 
22263     Jim_Free(compinfo);
22264 }
22265 #endif
22266 
Jim_HistorySetCompletion(Jim_Interp * interp,Jim_Obj * commandObj)22267 void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj)
22268 {
22269 #ifdef USE_LINENOISE
22270     if (commandObj) {
22271 
22272         Jim_IncrRefCount(commandObj);
22273     }
22274 
22275     Jim_DeleteAssocData(interp, completion_callback_assoc_key);
22276 
22277     if (commandObj) {
22278         struct JimCompletionInfo *compinfo = Jim_Alloc(sizeof(*compinfo));
22279         compinfo->interp = interp;
22280         compinfo->command = commandObj;
22281 
22282         Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
22283     }
22284 #endif
22285 }
22286 
Jim_InteractivePrompt(Jim_Interp * interp)22287 int Jim_InteractivePrompt(Jim_Interp *interp)
22288 {
22289     int retcode = JIM_OK;
22290     char *history_file = NULL;
22291 #ifdef USE_LINENOISE
22292     const char *home;
22293 
22294     home = getenv("HOME");
22295     if (home && isatty(STDIN_FILENO)) {
22296         int history_len = strlen(home) + sizeof("/.jim_history");
22297         history_file = Jim_Alloc(history_len);
22298         snprintf(history_file, history_len, "%s/.jim_history", home);
22299         Jim_HistoryLoad(history_file);
22300     }
22301 
22302     Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
22303 #endif
22304 
22305     printf("Welcome to Jim version %d.%d\n",
22306         JIM_VERSION / 100, JIM_VERSION % 100);
22307     Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
22308 
22309     while (1) {
22310         Jim_Obj *scriptObjPtr;
22311         const char *result;
22312         int reslen;
22313         char prompt[20];
22314 
22315         if (retcode != JIM_OK) {
22316             const char *retcodestr = Jim_ReturnCode(retcode);
22317 
22318             if (*retcodestr == '?') {
22319                 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
22320             }
22321             else {
22322                 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
22323             }
22324         }
22325         else {
22326             strcpy(prompt, ". ");
22327         }
22328 
22329         scriptObjPtr = Jim_NewStringObj(interp, "", 0);
22330         Jim_IncrRefCount(scriptObjPtr);
22331         while (1) {
22332             char state;
22333             char *line;
22334 
22335             line = Jim_HistoryGetline(interp, prompt);
22336             if (line == NULL) {
22337                 if (errno == EINTR) {
22338                     continue;
22339                 }
22340                 Jim_DecrRefCount(interp, scriptObjPtr);
22341                 retcode = JIM_OK;
22342                 goto out;
22343             }
22344             if (Jim_Length(scriptObjPtr) != 0) {
22345 
22346                 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
22347             }
22348             Jim_AppendString(interp, scriptObjPtr, line, -1);
22349             free(line);
22350             if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
22351                 break;
22352 
22353             snprintf(prompt, sizeof(prompt), "%c> ", state);
22354         }
22355 #ifdef USE_LINENOISE
22356         if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
22357 
22358             Jim_HistoryShow();
22359             Jim_DecrRefCount(interp, scriptObjPtr);
22360             continue;
22361         }
22362 
22363         Jim_HistoryAdd(Jim_String(scriptObjPtr));
22364         if (history_file) {
22365             Jim_HistorySave(history_file);
22366         }
22367 #endif
22368         retcode = Jim_EvalObj(interp, scriptObjPtr);
22369         Jim_DecrRefCount(interp, scriptObjPtr);
22370 
22371         if (retcode == JIM_EXIT) {
22372             break;
22373         }
22374         if (retcode == JIM_ERR) {
22375             Jim_MakeErrorMessage(interp);
22376         }
22377         result = Jim_GetString(Jim_GetResult(interp), &reslen);
22378         if (reslen) {
22379             printf("%s\n", result);
22380         }
22381     }
22382   out:
22383     Jim_Free(history_file);
22384 
22385     return retcode;
22386 }
22387 
22388 #include <stdio.h>
22389 #include <stdlib.h>
22390 #include <string.h>
22391 
22392 
22393 
22394 extern int Jim_initjimshInit(Jim_Interp *interp);
22395 
JimSetArgv(Jim_Interp * interp,int argc,char * const argv[])22396 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
22397 {
22398     int n;
22399     Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
22400 
22401 
22402     for (n = 0; n < argc; n++) {
22403         Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
22404 
22405         Jim_ListAppendElement(interp, listObj, obj);
22406     }
22407 
22408     Jim_SetVariableStr(interp, "argv", listObj);
22409     Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
22410 }
22411 
JimPrintErrorMessage(Jim_Interp * interp)22412 static void JimPrintErrorMessage(Jim_Interp *interp)
22413 {
22414     Jim_MakeErrorMessage(interp);
22415     fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
22416 }
22417 
usage(const char * executable_name)22418 void usage(const char* executable_name)
22419 {
22420     printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
22421     printf("Usage: %s\n", executable_name);
22422     printf("or   : %s [options] [filename]\n", executable_name);
22423     printf("\n");
22424     printf("Without options: Interactive mode\n");
22425     printf("\n");
22426     printf("Options:\n");
22427     printf("      --version  : prints the version string\n");
22428     printf("      --help     : prints this text\n");
22429     printf("      -e CMD     : executes command CMD\n");
22430     printf("                   NOTE: all subsequent options will be passed as arguments to the command\n");
22431     printf("    [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n");
22432     printf("                   NOTE: all subsequent options will be passed to the script\n\n");
22433 }
22434 
main(int argc,char * const argv[])22435 int main(int argc, char *const argv[])
22436 {
22437     int retcode;
22438     Jim_Interp *interp;
22439     char *const orig_argv0 = argv[0];
22440 
22441 
22442     if (argc > 1 && strcmp(argv[1], "--version") == 0) {
22443         printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
22444         return 0;
22445     }
22446     else if (argc > 1 && strcmp(argv[1], "--help") == 0) {
22447         usage(argv[0]);
22448         return 0;
22449     }
22450 
22451 
22452     interp = Jim_CreateInterp();
22453     Jim_RegisterCoreCommands(interp);
22454 
22455 
22456     if (Jim_InitStaticExtensions(interp) != JIM_OK) {
22457         JimPrintErrorMessage(interp);
22458     }
22459 
22460     Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
22461     Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
22462     retcode = Jim_initjimshInit(interp);
22463 
22464     if (argc == 1) {
22465 
22466         if (retcode == JIM_ERR) {
22467             JimPrintErrorMessage(interp);
22468         }
22469         if (retcode != JIM_EXIT) {
22470             JimSetArgv(interp, 0, NULL);
22471             retcode = Jim_InteractivePrompt(interp);
22472         }
22473     }
22474     else {
22475 
22476         if (argc > 2 && strcmp(argv[1], "-e") == 0) {
22477 
22478             JimSetArgv(interp, argc - 3, argv + 3);
22479             retcode = Jim_Eval(interp, argv[2]);
22480             if (retcode != JIM_ERR) {
22481                 printf("%s\n", Jim_String(Jim_GetResult(interp)));
22482             }
22483         }
22484         else {
22485             Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
22486             JimSetArgv(interp, argc - 2, argv + 2);
22487             if (strcmp(argv[1], "-") == 0) {
22488                 retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]");
22489             } else {
22490                 retcode = Jim_EvalFile(interp, argv[1]);
22491             }
22492         }
22493         if (retcode == JIM_ERR) {
22494             JimPrintErrorMessage(interp);
22495         }
22496     }
22497     if (retcode == JIM_EXIT) {
22498         retcode = Jim_GetExitCode(interp);
22499     }
22500     else if (retcode == JIM_ERR) {
22501         retcode = 1;
22502     }
22503     else {
22504         retcode = 0;
22505     }
22506     Jim_FreeInterp(interp);
22507     return retcode;
22508 }
22509 #endif
22510