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 77
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 #elif defined(__MINGW32__)
133 
134 #include <stdlib.h>
135 #define strtod __strtod
136 
137 #endif
138 
139 #endif
140 
141 #ifdef __cplusplus
142 }
143 #endif
144 
145 #endif
146 #ifndef UTF8_UTIL_H
147 #define UTF8_UTIL_H
148 
149 #ifdef __cplusplus
150 extern "C" {
151 #endif
152 
153 
154 
155 #define MAX_UTF8_LEN 4
156 
157 int utf8_fromunicode(char *p, unsigned uc);
158 
159 #ifndef JIM_UTF8
160 #include <ctype.h>
161 
162 
163 #define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
164 #define utf8_strwidth(S, B) utf8_strlen((S), (B))
165 #define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
166 #define utf8_getchars(CP, C) (*(CP) = (C), 1)
167 #define utf8_upper(C) toupper(C)
168 #define utf8_title(C) toupper(C)
169 #define utf8_lower(C) tolower(C)
170 #define utf8_index(C, I) (I)
171 #define utf8_charlen(C) 1
172 #define utf8_prev_len(S, L) 1
173 #define utf8_width(C) 1
174 
175 #else
176 
177 #endif
178 
179 #ifdef __cplusplus
180 }
181 #endif
182 
183 #endif
184 
185 #ifndef __JIM__H
186 #define __JIM__H
187 
188 #ifdef __cplusplus
189 extern "C" {
190 #endif
191 
192 #include <time.h>
193 #include <limits.h>
194 #include <stdio.h>
195 #include <stdlib.h>
196 #include <stdarg.h>
197 
198 
199 #ifndef HAVE_NO_AUTOCONF
200 #endif
201 
202 
203 
204 #ifndef jim_wide
205 #  ifdef HAVE_LONG_LONG
206 #    define jim_wide long long
207 #    ifndef LLONG_MAX
208 #      define LLONG_MAX    9223372036854775807LL
209 #    endif
210 #    ifndef LLONG_MIN
211 #      define LLONG_MIN    (-LLONG_MAX - 1LL)
212 #    endif
213 #    define JIM_WIDE_MIN LLONG_MIN
214 #    define JIM_WIDE_MAX LLONG_MAX
215 #  else
216 #    define jim_wide long
217 #    define JIM_WIDE_MIN LONG_MIN
218 #    define JIM_WIDE_MAX LONG_MAX
219 #  endif
220 
221 
222 #  ifdef HAVE_LONG_LONG
223 #    define JIM_WIDE_MODIFIER "lld"
224 #  else
225 #    define JIM_WIDE_MODIFIER "ld"
226 #    define strtoull strtoul
227 #  endif
228 #endif
229 
230 #define UCHAR(c) ((unsigned char)(c))
231 
232 
233 #define JIM_OK 0
234 #define JIM_ERR 1
235 #define JIM_RETURN 2
236 #define JIM_BREAK 3
237 #define JIM_CONTINUE 4
238 #define JIM_SIGNAL 5
239 #define JIM_EXIT 6
240 
241 #define JIM_EVAL 7
242 
243 #define JIM_MAX_CALLFRAME_DEPTH 1000
244 #define JIM_MAX_EVAL_DEPTH 2000
245 
246 
247 #define JIM_PRIV_FLAG_SHIFT 20
248 
249 #define JIM_NONE 0
250 #define JIM_ERRMSG 1
251 #define JIM_ENUM_ABBREV 2
252 #define JIM_UNSHARED 4
253 #define JIM_MUSTEXIST 8
254 
255 
256 #define JIM_SUBST_NOVAR 1
257 #define JIM_SUBST_NOCMD 2
258 #define JIM_SUBST_NOESC 4
259 #define JIM_SUBST_FLAG 128
260 
261 
262 #define JIM_CASESENS    0
263 #define JIM_NOCASE      1
264 
265 
266 #define JIM_PATH_LEN 1024
267 
268 
269 #define JIM_NOTUSED(V) ((void) V)
270 
271 #define JIM_LIBPATH "auto_path"
272 #define JIM_INTERACTIVE "tcl_interactive"
273 
274 
275 typedef struct Jim_Stack {
276     int len;
277     int maxlen;
278     void **vector;
279 } Jim_Stack;
280 
281 
282 typedef struct Jim_HashEntry {
283     void *key;
284     union {
285         void *val;
286         int intval;
287     } u;
288     struct Jim_HashEntry *next;
289 } Jim_HashEntry;
290 
291 typedef struct Jim_HashTableType {
292     unsigned int (*hashFunction)(const void *key);
293     void *(*keyDup)(void *privdata, const void *key);
294     void *(*valDup)(void *privdata, const void *obj);
295     int (*keyCompare)(void *privdata, const void *key1, const void *key2);
296     void (*keyDestructor)(void *privdata, void *key);
297     void (*valDestructor)(void *privdata, void *obj);
298 } Jim_HashTableType;
299 
300 typedef struct Jim_HashTable {
301     Jim_HashEntry **table;
302     const Jim_HashTableType *type;
303     void *privdata;
304     unsigned int size;
305     unsigned int sizemask;
306     unsigned int used;
307     unsigned int collisions;
308     unsigned int uniq;
309 } Jim_HashTable;
310 
311 typedef struct Jim_HashTableIterator {
312     Jim_HashTable *ht;
313     Jim_HashEntry *entry, *nextEntry;
314     int index;
315 } Jim_HashTableIterator;
316 
317 
318 #define JIM_HT_INITIAL_SIZE     16
319 
320 
321 #define Jim_FreeEntryVal(ht, entry) \
322     if ((ht)->type->valDestructor) \
323         (ht)->type->valDestructor((ht)->privdata, (entry)->u.val)
324 
325 #define Jim_SetHashVal(ht, entry, _val_) do { \
326     if ((ht)->type->valDup) \
327         (entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \
328     else \
329         (entry)->u.val = (_val_); \
330 } while(0)
331 
332 #define Jim_FreeEntryKey(ht, entry) \
333     if ((ht)->type->keyDestructor) \
334         (ht)->type->keyDestructor((ht)->privdata, (entry)->key)
335 
336 #define Jim_SetHashKey(ht, entry, _key_) do { \
337     if ((ht)->type->keyDup) \
338         (entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \
339     else \
340         (entry)->key = (void *)(_key_); \
341 } while(0)
342 
343 #define Jim_CompareHashKeys(ht, key1, key2) \
344     (((ht)->type->keyCompare) ? \
345         (ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \
346         (key1) == (key2))
347 
348 #define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq)
349 
350 #define Jim_GetHashEntryKey(he) ((he)->key)
351 #define Jim_GetHashEntryVal(he) ((he)->u.val)
352 #define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
353 #define Jim_GetHashTableSize(ht) ((ht)->size)
354 #define Jim_GetHashTableUsed(ht) ((ht)->used)
355 
356 
357 typedef struct Jim_Obj {
358     char *bytes;
359     const struct Jim_ObjType *typePtr;
360     int refCount;
361     int length;
362 
363     union {
364 
365         jim_wide wideValue;
366 
367         int intValue;
368 
369         double doubleValue;
370 
371         void *ptr;
372 
373         struct {
374             void *ptr1;
375             void *ptr2;
376         } twoPtrValue;
377 
378         struct {
379             struct Jim_Var *varPtr;
380             unsigned long callFrameId;
381             int global;
382         } varValue;
383 
384         struct {
385             struct Jim_Obj *nsObj;
386             struct Jim_Cmd *cmdPtr;
387             unsigned long procEpoch;
388         } cmdValue;
389 
390         struct {
391             struct Jim_Obj **ele;
392             int len;
393             int maxLen;
394         } listValue;
395 
396         struct {
397             int maxLength;
398             int charLength;
399         } strValue;
400 
401         struct {
402             unsigned long id;
403             struct Jim_Reference *refPtr;
404         } refValue;
405 
406         struct {
407             struct Jim_Obj *fileNameObj;
408             int lineNumber;
409         } sourceValue;
410 
411         struct {
412             struct Jim_Obj *varNameObjPtr;
413             struct Jim_Obj *indexObjPtr;
414         } dictSubstValue;
415 
416         struct {
417             void *compre;
418             unsigned flags;
419         } regexpValue;
420         struct {
421             int line;
422             int argc;
423         } scriptLineValue;
424     } internalRep;
425     struct Jim_Obj *prevObjPtr;
426     struct Jim_Obj *nextObjPtr;
427 } Jim_Obj;
428 
429 
430 #define Jim_IncrRefCount(objPtr) \
431     ++(objPtr)->refCount
432 #define Jim_DecrRefCount(interp, objPtr) \
433     if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr)
434 #define Jim_IsShared(objPtr) \
435     ((objPtr)->refCount > 1)
436 
437 #define Jim_FreeNewObj Jim_FreeObj
438 
439 
440 #define Jim_FreeIntRep(i,o) \
441     if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \
442         (o)->typePtr->freeIntRepProc(i, o)
443 
444 
445 #define Jim_GetIntRepPtr(o) (o)->internalRep.ptr
446 
447 
448 #define Jim_SetIntRepPtr(o, p) \
449     (o)->internalRep.ptr = (p)
450 
451 
452 struct Jim_Interp;
453 
454 typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp,
455         struct Jim_Obj *objPtr);
456 typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp,
457         struct Jim_Obj *srcPtr, Jim_Obj *dupPtr);
458 typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr);
459 
460 typedef struct Jim_ObjType {
461     const char *name;
462     Jim_FreeInternalRepProc *freeIntRepProc;
463     Jim_DupInternalRepProc *dupIntRepProc;
464     Jim_UpdateStringProc *updateStringProc;
465     int flags;
466 } Jim_ObjType;
467 
468 
469 #define JIM_TYPE_NONE 0
470 #define JIM_TYPE_REFERENCES 1
471 
472 
473 
474 typedef struct Jim_CallFrame {
475     unsigned long id;
476     int level;
477     struct Jim_HashTable vars;
478     struct Jim_HashTable *staticVars;
479     struct Jim_CallFrame *parent;
480     Jim_Obj *const *argv;
481     int argc;
482     Jim_Obj *procArgsObjPtr;
483     Jim_Obj *procBodyObjPtr;
484     struct Jim_CallFrame *next;
485     Jim_Obj *nsObj;
486     Jim_Obj *fileNameObj;
487     int line;
488     Jim_Stack *localCommands;
489     struct Jim_Obj *tailcallObj;
490     struct Jim_Cmd *tailcallCmd;
491 } Jim_CallFrame;
492 
493 typedef struct Jim_Var {
494     Jim_Obj *objPtr;
495     struct Jim_CallFrame *linkFramePtr;
496 } Jim_Var;
497 
498 
499 typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
500     Jim_Obj *const *argv);
501 typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);
502 
503 
504 
505 typedef struct Jim_Cmd {
506     int inUse;
507     int isproc;
508     struct Jim_Cmd *prevCmd;
509     union {
510         struct {
511 
512             Jim_CmdProc *cmdProc;
513             Jim_DelCmdProc *delProc;
514             void *privData;
515         } native;
516         struct {
517 
518             Jim_Obj *argListObjPtr;
519             Jim_Obj *bodyObjPtr;
520             Jim_HashTable *staticVars;
521             int argListLen;
522             int reqArity;
523             int optArity;
524             int argsPos;
525             int upcall;
526             struct Jim_ProcArg {
527                 Jim_Obj *nameObjPtr;
528                 Jim_Obj *defaultObjPtr;
529             } *arglist;
530             Jim_Obj *nsObj;
531         } proc;
532     } u;
533 } Jim_Cmd;
534 
535 
536 typedef struct Jim_PrngState {
537     unsigned char sbox[256];
538     unsigned int i, j;
539 } Jim_PrngState;
540 
541 typedef struct Jim_Interp {
542     Jim_Obj *result;
543     int errorLine;
544     Jim_Obj *errorFileNameObj;
545     int addStackTrace;
546     int maxCallFrameDepth;
547     int maxEvalDepth;
548     int evalDepth;
549     int returnCode;
550     int returnLevel;
551     int exitCode;
552     long id;
553     int signal_level;
554     jim_wide sigmask;
555     int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask);
556     Jim_CallFrame *framePtr;
557     Jim_CallFrame *topFramePtr;
558     struct Jim_HashTable commands;
559     unsigned long procEpoch; /* Incremented every time the result
560                 of procedures names lookup caching
561                 may no longer be valid. */
562     unsigned long callFrameEpoch; /* Incremented every time a new
563                 callframe is created. This id is used for the
564                 'ID' field contained in the Jim_CallFrame
565                 structure. */
566     int local;
567     Jim_Obj *liveList;
568     Jim_Obj *freeList;
569     Jim_Obj *currentScriptObj;
570     Jim_Obj *nullScriptObj;
571     Jim_Obj *emptyObj;
572     Jim_Obj *trueObj;
573     Jim_Obj *falseObj;
574     unsigned long referenceNextId;
575     struct Jim_HashTable references;
576     unsigned long lastCollectId; /* reference max Id of the last GC
577                 execution. It's set to -1 while the collection
578                 is running as sentinel to avoid to recursive
579                 calls via the [collect] command inside
580                 finalizers. */
581     time_t lastCollectTime;
582     Jim_Obj *stackTrace;
583     Jim_Obj *errorProc;
584     Jim_Obj *unknown;
585     int unknown_called;
586     int errorFlag;
587     void *cmdPrivData; /* Used to pass the private data pointer to
588                   a command. It is set to what the user specified
589                   via Jim_CreateCommand(). */
590 
591     struct Jim_CallFrame *freeFramesList;
592     struct Jim_HashTable assocData;
593     Jim_PrngState *prngState;
594     struct Jim_HashTable packages;
595     Jim_Stack *loadHandles;
596 } Jim_Interp;
597 
598 #define Jim_InterpIncrProcEpoch(i) (i)->procEpoch++
599 #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
600 #define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))
601 
602 #define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
603 #define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj)
604 #define Jim_GetResult(i) ((i)->result)
605 #define Jim_CmdPrivData(i) ((i)->cmdPrivData)
606 
607 #define Jim_SetResult(i,o) do {     \
608     Jim_Obj *_resultObjPtr_ = (o);    \
609     Jim_IncrRefCount(_resultObjPtr_); \
610     Jim_DecrRefCount(i,(i)->result);  \
611     (i)->result = _resultObjPtr_;     \
612 } while(0)
613 
614 
615 #define Jim_GetId(i) (++(i)->id)
616 
617 
618 #define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference
619                                   string representation must be fixed length. */
620 typedef struct Jim_Reference {
621     Jim_Obj *objPtr;
622     Jim_Obj *finalizerCmdNamePtr;
623     char tag[JIM_REFERENCE_TAGLEN+1];
624 } Jim_Reference;
625 
626 
627 #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
628 #define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
629 
630 #define JIM_EXPORT
631 
632 
633 JIM_EXPORT void *Jim_Alloc (int size);
634 JIM_EXPORT void *Jim_Realloc(void *ptr, int size);
635 JIM_EXPORT void Jim_Free (void *ptr);
636 JIM_EXPORT char * Jim_StrDup (const char *s);
637 JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
638 
639 
640 JIM_EXPORT char **Jim_GetEnviron(void);
641 JIM_EXPORT void Jim_SetEnviron(char **env);
642 JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template);
643 
644 
645 JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
646 
647 
648 JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);
649 
650 #define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S))
651 
652 JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script);
653 JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename);
654 JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename);
655 JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr);
656 JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc,
657         Jim_Obj *const *objv);
658 JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj);
659 JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
660         int objc, Jim_Obj *const *objv);
661 #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
662 JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
663 JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
664         Jim_Obj **resObjPtrPtr, int flags);
665 
666 
667 JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
668 JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
669 JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
670 JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
671 JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
672 JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
673 JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr));
674 
675 
676 JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
677         const Jim_HashTableType *type, void *privdata);
678 JIM_EXPORT void Jim_ExpandHashTable (Jim_HashTable *ht,
679         unsigned int size);
680 JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key,
681         void *val);
682 JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
683         const void *key, void *val);
684 JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
685         const void *key);
686 JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
687 JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
688         const void *key);
689 JIM_EXPORT void Jim_ResizeHashTable (Jim_HashTable *ht);
690 JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator
691         (Jim_HashTable *ht);
692 JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
693         (Jim_HashTableIterator *iter);
694 
695 
696 JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
697 JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr);
698 JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr);
699 JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
700         Jim_Obj *objPtr);
701 JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
702         int *lenPtr);
703 JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
704 JIM_EXPORT int Jim_Length(Jim_Obj *objPtr);
705 
706 
707 JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp,
708         const char *s, int len);
709 JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp,
710         const char *s, int charlen);
711 JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp,
712         char *s, int len);
713 JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr,
714         const char *str, int len);
715 JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr,
716         Jim_Obj *appendObjPtr);
717 JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp,
718         Jim_Obj *objPtr, ...);
719 JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr);
720 JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr,
721         Jim_Obj *objPtr, int nocase);
722 JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp,
723         Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr,
724         Jim_Obj *lastObjPtr);
725 JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp,
726         Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
727 JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
728         Jim_Obj *fmtObjPtr, int flags);
729 JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
730         Jim_Obj *objPtr, const char *str);
731 JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
732         Jim_Obj *secondObjPtr, int nocase);
733 JIM_EXPORT int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
734         Jim_Obj *secondObjPtr, int nocase);
735 JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr);
736 
737 
738 JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp,
739         Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr);
740 JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp,
741         Jim_Obj *objPtr);
742 JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr);
743 JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr);
744 
745 
746 JIM_EXPORT Jim_Interp * Jim_CreateInterp (void);
747 JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i);
748 JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp);
749 JIM_EXPORT const char *Jim_ReturnCode(int code);
750 JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...);
751 
752 
753 JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
754 JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
755         const char *cmdName, Jim_CmdProc *cmdProc, void *privData,
756          Jim_DelCmdProc *delProc);
757 JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
758         const char *cmdName);
759 JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
760         const char *oldName, const char *newName);
761 JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
762         Jim_Obj *objPtr, int flags);
763 JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
764         Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
765 JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
766         const char *name, Jim_Obj *objPtr);
767 JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp,
768         const char *name, Jim_Obj *objPtr);
769 JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp,
770         const char *name, const char *val);
771 JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp,
772         Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr,
773         Jim_CallFrame *targetCallFrame);
774 JIM_EXPORT Jim_Obj * Jim_MakeGlobalNamespaceName(Jim_Interp *interp,
775         Jim_Obj *nameObjPtr);
776 JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp,
777         Jim_Obj *nameObjPtr, int flags);
778 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp,
779         Jim_Obj *nameObjPtr, int flags);
780 JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp,
781         const char *name, int flags);
782 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp,
783         const char *name, int flags);
784 JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp,
785         Jim_Obj *nameObjPtr, int flags);
786 
787 
788 JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp,
789         Jim_Obj *levelObjPtr);
790 
791 
792 JIM_EXPORT int Jim_Collect (Jim_Interp *interp);
793 JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp);
794 
795 
796 JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr,
797         int *indexPtr);
798 
799 
800 JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp,
801         Jim_Obj *const *elements, int len);
802 JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp,
803         Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec);
804 JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp,
805         Jim_Obj *listPtr, Jim_Obj *objPtr);
806 JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp,
807         Jim_Obj *listPtr, Jim_Obj *appendListPtr);
808 JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr);
809 JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt,
810         int listindex, Jim_Obj **objPtrPtr, int seterr);
811 JIM_EXPORT Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx);
812 JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp,
813         Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc,
814         Jim_Obj *newObjPtr);
815 JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc,
816         Jim_Obj *const *objv);
817 JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp,
818         Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen);
819 
820 
821 JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp,
822         Jim_Obj *const *elements, int len);
823 JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr,
824         Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
825 JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
826         Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
827         Jim_Obj **objPtrPtr, int flags);
828 JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
829         Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
830         Jim_Obj *newObjPtr, int flags);
831 JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp,
832         Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len);
833 JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
834         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
835 
836 #define JIM_DICTMATCH_KEYS 0x0001
837 #define JIM_DICTMATCH_VALUES 0x002
838 
839 JIM_EXPORT int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types);
840 JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
841 JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr);
842 JIM_EXPORT Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv);
843 
844 
845 JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
846         int *intPtr);
847 
848 
849 JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
850         Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr);
851 JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
852         Jim_Obj *exprObjPtr, int *boolPtr);
853 
854 
855 JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
856         int *booleanPtr);
857 
858 
859 JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
860         jim_wide *widePtr);
861 JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
862         long *longPtr);
863 #define Jim_NewWideObj  Jim_NewIntObj
864 JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
865         jim_wide wideValue);
866 
867 
868 JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
869         double *doublePtr);
870 JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
871         double doubleValue);
872 JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue);
873 
874 
875 JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc,
876         Jim_Obj *const *argv, const char *msg);
877 JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
878         const char * const *tablePtr, int *indexPtr, const char *name, int flags);
879 JIM_EXPORT int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr,
880         const char *const *tablePtr);
881 JIM_EXPORT int Jim_ScriptIsComplete(Jim_Interp *interp,
882         Jim_Obj *scriptObj, char *stateCharPtr);
883 
884 JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);
885 
886 
887 typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
888 JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
889 JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
890         Jim_InterpDeleteProc *delProc, void *data);
891 JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
892 
893 
894 
895 JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
896         const char *name, const char *ver, int flags);
897 JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
898         const char *name, int flags);
899 
900 
901 JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
902 
903 
904 JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
905 JIM_EXPORT void Jim_HistoryLoad(const char *filename);
906 JIM_EXPORT void Jim_HistorySave(const char *filename);
907 JIM_EXPORT char *Jim_HistoryGetline(const char *prompt);
908 JIM_EXPORT void Jim_HistoryAdd(const char *line);
909 JIM_EXPORT void Jim_HistoryShow(void);
910 
911 
912 JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
913 JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
914 JIM_EXPORT int Jim_IsBigEndian(void);
915 
916 #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
917 
918 
919 JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
920 JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
921 
922 
923 JIM_EXPORT FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
924 
925 
926 JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
927 JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
928 
929 #ifdef __cplusplus
930 }
931 #endif
932 
933 #endif
934 
935 #ifndef JIM_SUBCMD_H
936 #define JIM_SUBCMD_H
937 
938 
939 #ifdef __cplusplus
940 extern "C" {
941 #endif
942 
943 
944 #define JIM_MODFLAG_HIDDEN   0x0001
945 #define JIM_MODFLAG_FULLARGV 0x0002
946 
947 
948 
949 typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
950 
951 typedef struct {
952 	const char *cmd;
953 	const char *args;
954 	jim_subcmd_function *function;
955 	short minargs;
956 	short maxargs;
957 	unsigned short flags;
958 } jim_subcmd_type;
959 
960 const jim_subcmd_type *
961 Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
962 
963 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
964 
965 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
966 
967 #ifdef __cplusplus
968 }
969 #endif
970 
971 #endif
972 #ifndef JIMREGEXP_H
973 #define JIMREGEXP_H
974 
975 
976 #ifdef __cplusplus
977 extern "C" {
978 #endif
979 
980 #include <stdlib.h>
981 
982 typedef struct {
983 	int rm_so;
984 	int rm_eo;
985 } regmatch_t;
986 
987 
988 typedef struct regexp {
989 
990 	int re_nsub;
991 
992 
993 	int cflags;
994 	int err;
995 	int regstart;
996 	int reganch;
997 	int regmust;
998 	int regmlen;
999 	int *program;
1000 
1001 
1002 	const char *regparse;
1003 	int p;
1004 	int proglen;
1005 
1006 
1007 	int eflags;
1008 	const char *start;
1009 	const char *reginput;
1010 	const char *regbol;
1011 
1012 
1013 	regmatch_t *pmatch;
1014 	int nmatch;
1015 } regexp;
1016 
1017 typedef regexp regex_t;
1018 
1019 #define REG_EXTENDED 0
1020 #define REG_NEWLINE 1
1021 #define REG_ICASE 2
1022 
1023 #define REG_NOTBOL 16
1024 
1025 enum {
1026 	REG_NOERROR,
1027 	REG_NOMATCH,
1028 	REG_BADPAT,
1029 	REG_ERR_NULL_ARGUMENT,
1030 	REG_ERR_UNKNOWN,
1031 	REG_ERR_TOO_BIG,
1032 	REG_ERR_NOMEM,
1033 	REG_ERR_TOO_MANY_PAREN,
1034 	REG_ERR_UNMATCHED_PAREN,
1035 	REG_ERR_UNMATCHED_BRACES,
1036 	REG_ERR_BAD_COUNT,
1037 	REG_ERR_JUNK_ON_END,
1038 	REG_ERR_OPERAND_COULD_BE_EMPTY,
1039 	REG_ERR_NESTED_COUNT,
1040 	REG_ERR_INTERNAL,
1041 	REG_ERR_COUNT_FOLLOWS_NOTHING,
1042 	REG_ERR_TRAILING_BACKSLASH,
1043 	REG_ERR_CORRUPTED,
1044 	REG_ERR_NULL_CHAR,
1045 	REG_ERR_NUM
1046 };
1047 
1048 int regcomp(regex_t *preg, const char *regex, int cflags);
1049 int regexec(regex_t  *preg,  const  char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
1050 size_t regerror(int errcode, const regex_t *preg, char *errbuf,  size_t errbuf_size);
1051 void regfree(regex_t *preg);
1052 
1053 #ifdef __cplusplus
1054 }
1055 #endif
1056 
1057 #endif
Jim_bootstrapInit(Jim_Interp * interp)1058 int Jim_bootstrapInit(Jim_Interp *interp)
1059 {
1060 	if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
1061 		return JIM_ERR;
1062 
1063 	return Jim_EvalSource(interp, "bootstrap.tcl", 1,
1064 "\n"
1065 "\n"
1066 "proc package {cmd pkg} {\n"
1067 "	if {$cmd eq \"require\"} {\n"
1068 "		foreach path $::auto_path {\n"
1069 "			if {[file exists $path/$pkg.tcl]} {\n"
1070 "				uplevel #0 [list source $path/$pkg.tcl]\n"
1071 "				return\n"
1072 "			}\n"
1073 "		}\n"
1074 "	}\n"
1075 "}\n"
1076 );
1077 }
Jim_initjimshInit(Jim_Interp * interp)1078 int Jim_initjimshInit(Jim_Interp *interp)
1079 {
1080 	if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
1081 		return JIM_ERR;
1082 
1083 	return Jim_EvalSource(interp, "initjimsh.tcl", 1,
1084 "\n"
1085 "\n"
1086 "\n"
1087 "proc _jimsh_init {} {\n"
1088 "	rename _jimsh_init {}\n"
1089 "	global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
1090 "\n"
1091 "\n"
1092 "	if {[exists jim::argv0]} {\n"
1093 "		if {[string match \"*/*\" $jim::argv0]} {\n"
1094 "			set jim::exe [file join [pwd] $jim::argv0]\n"
1095 "		} else {\n"
1096 "			foreach path [split [env PATH \"\"] $tcl_platform(pathSeparator)] {\n"
1097 "				set exec [file join [pwd] [string map {\\\\ /} $path] $jim::argv0]\n"
1098 "				if {[file executable $exec]} {\n"
1099 "					set jim::exe $exec\n"
1100 "					break\n"
1101 "				}\n"
1102 "			}\n"
1103 "		}\n"
1104 "	}\n"
1105 "\n"
1106 "\n"
1107 "	lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
1108 "	if {[exists jim::exe]} {\n"
1109 "		lappend p [file dirname $jim::exe]\n"
1110 "	}\n"
1111 "	lappend p {*}$auto_path\n"
1112 "	set auto_path $p\n"
1113 "\n"
1114 "	if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
1115 "		foreach src {.jimrc jimrc.tcl} {\n"
1116 "			if {[file exists [env HOME]/$src]} {\n"
1117 "				uplevel #0 source [env HOME]/$src\n"
1118 "				break\n"
1119 "			}\n"
1120 "		}\n"
1121 "	}\n"
1122 "	return \"\"\n"
1123 "}\n"
1124 "\n"
1125 "if {$tcl_platform(platform) eq \"windows\"} {\n"
1126 "	set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
1127 "}\n"
1128 "\n"
1129 "\n"
1130 "set tcl::autocomplete_commands {info tcl::prefix socket namespace array clock file package string dict signal history}\n"
1131 "\n"
1132 "\n"
1133 "\n"
1134 "proc tcl::autocomplete {prefix} {\n"
1135 "	if {[set space [string first \" \" $prefix]] != -1} {\n"
1136 "		set cmd [string range $prefix 0 $space-1]\n"
1137 "		if {$cmd in $::tcl::autocomplete_commands || [info channel $cmd] ne \"\"} {\n"
1138 "			set arg [string range $prefix $space+1 end]\n"
1139 "\n"
1140 "			return [lmap p [$cmd -commands] {\n"
1141 "				if {![string match \"${arg}*\" $p]} continue\n"
1142 "				function \"$cmd $p\"\n"
1143 "			}]\n"
1144 "		}\n"
1145 "	}\n"
1146 "\n"
1147 "	if {[string match \"source *\" $prefix]} {\n"
1148 "		set path [string range $prefix 7 end]\n"
1149 "		return [lmap p [glob -nocomplain \"${path}*\"] {\n"
1150 "			function \"source $p\"\n"
1151 "		}]\n"
1152 "	}\n"
1153 "\n"
1154 "	return [lmap p [lsort [info commands $prefix*]] {\n"
1155 "		if {[string match \"* *\" $p]} {\n"
1156 "			continue\n"
1157 "		}\n"
1158 "		function $p\n"
1159 "	}]\n"
1160 "}\n"
1161 "\n"
1162 "_jimsh_init\n"
1163 );
1164 }
Jim_globInit(Jim_Interp * interp)1165 int Jim_globInit(Jim_Interp *interp)
1166 {
1167 	if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
1168 		return JIM_ERR;
1169 
1170 	return Jim_EvalSource(interp, "glob.tcl", 1,
1171 "\n"
1172 "\n"
1173 "\n"
1174 "\n"
1175 "\n"
1176 "\n"
1177 "\n"
1178 "package require readdir\n"
1179 "\n"
1180 "\n"
1181 "proc glob.globdir {dir pattern} {\n"
1182 "	if {[file exists $dir/$pattern]} {\n"
1183 "\n"
1184 "		return [list $pattern]\n"
1185 "	}\n"
1186 "\n"
1187 "	set result {}\n"
1188 "	set files [readdir $dir]\n"
1189 "	lappend files . ..\n"
1190 "\n"
1191 "	foreach name $files {\n"
1192 "		if {[string match $pattern $name]} {\n"
1193 "\n"
1194 "			if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n"
1195 "				continue\n"
1196 "			}\n"
1197 "			lappend result $name\n"
1198 "		}\n"
1199 "	}\n"
1200 "\n"
1201 "	return $result\n"
1202 "}\n"
1203 "\n"
1204 "\n"
1205 "\n"
1206 "\n"
1207 "proc glob.explode {pattern} {\n"
1208 "	set oldexp {}\n"
1209 "	set newexp {\"\"}\n"
1210 "\n"
1211 "	while 1 {\n"
1212 "		set oldexp $newexp\n"
1213 "		set newexp {}\n"
1214 "		set ob [string first \\{ $pattern]\n"
1215 "		set cb [string first \\} $pattern]\n"
1216 "\n"
1217 "		if {$ob < $cb && $ob != -1} {\n"
1218 "			set mid [string range $pattern 0 $ob-1]\n"
1219 "			set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n"
1220 "			if {$pattern eq \"\"} {\n"
1221 "				error \"unmatched open brace in glob pattern\"\n"
1222 "			}\n"
1223 "			set pattern [string range $pattern 1 end]\n"
1224 "\n"
1225 "			foreach subs $subexp {\n"
1226 "				foreach sub [split $subs ,] {\n"
1227 "					foreach old $oldexp {\n"
1228 "						lappend newexp $old$mid$sub\n"
1229 "					}\n"
1230 "				}\n"
1231 "			}\n"
1232 "		} elseif {$cb != -1} {\n"
1233 "			set suf  [string range $pattern 0 $cb-1]\n"
1234 "			set rest [string range $pattern $cb end]\n"
1235 "			break\n"
1236 "		} else {\n"
1237 "			set suf  $pattern\n"
1238 "			set rest \"\"\n"
1239 "			break\n"
1240 "		}\n"
1241 "	}\n"
1242 "\n"
1243 "	foreach old $oldexp {\n"
1244 "		lappend newexp $old$suf\n"
1245 "	}\n"
1246 "	list $rest {*}$newexp\n"
1247 "}\n"
1248 "\n"
1249 "\n"
1250 "\n"
1251 "proc glob.glob {base pattern} {\n"
1252 "	set dir [file dirname $pattern]\n"
1253 "	if {$pattern eq $dir || $pattern eq \"\"} {\n"
1254 "		return [list [file join $base $dir] $pattern]\n"
1255 "	} elseif {$pattern eq [file tail $pattern]} {\n"
1256 "		set dir \"\"\n"
1257 "	}\n"
1258 "\n"
1259 "\n"
1260 "	set dirlist [glob.glob $base $dir]\n"
1261 "	set pattern [file tail $pattern]\n"
1262 "\n"
1263 "\n"
1264 "	set result {}\n"
1265 "	foreach {realdir dir} $dirlist {\n"
1266 "		if {![file isdir $realdir]} {\n"
1267 "			continue\n"
1268 "		}\n"
1269 "		if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n"
1270 "			append dir /\n"
1271 "		}\n"
1272 "		foreach name [glob.globdir $realdir $pattern] {\n"
1273 "			lappend result [file join $realdir $name] $dir$name\n"
1274 "		}\n"
1275 "	}\n"
1276 "	return $result\n"
1277 "}\n"
1278 "\n"
1279 "\n"
1280 "\n"
1281 "\n"
1282 "\n"
1283 "\n"
1284 "\n"
1285 "\n"
1286 "\n"
1287 "\n"
1288 "\n"
1289 "\n"
1290 "proc glob {args} {\n"
1291 "	set nocomplain 0\n"
1292 "	set base \"\"\n"
1293 "	set tails 0\n"
1294 "\n"
1295 "	set n 0\n"
1296 "	foreach arg $args {\n"
1297 "		if {[info exists param]} {\n"
1298 "			set $param $arg\n"
1299 "			unset param\n"
1300 "			incr n\n"
1301 "			continue\n"
1302 "		}\n"
1303 "		switch -glob -- $arg {\n"
1304 "			-d* {\n"
1305 "				set switch $arg\n"
1306 "				set param base\n"
1307 "			}\n"
1308 "			-n* {\n"
1309 "				set nocomplain 1\n"
1310 "			}\n"
1311 "			-ta* {\n"
1312 "				set tails 1\n"
1313 "			}\n"
1314 "			-- {\n"
1315 "				incr n\n"
1316 "				break\n"
1317 "			}\n"
1318 "			-* {\n"
1319 "				return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1320 "			}\n"
1321 "			*  {\n"
1322 "				break\n"
1323 "			}\n"
1324 "		}\n"
1325 "		incr n\n"
1326 "	}\n"
1327 "	if {[info exists param]} {\n"
1328 "		return -code error \"missing argument to \\\"$switch\\\"\"\n"
1329 "	}\n"
1330 "	if {[llength $args] <= $n} {\n"
1331 "		return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n"
1332 "	}\n"
1333 "\n"
1334 "	set args [lrange $args $n end]\n"
1335 "\n"
1336 "	set result {}\n"
1337 "	foreach pattern $args {\n"
1338 "		set escpattern [string map {\n"
1339 "			\\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
1340 "		} $pattern]\n"
1341 "		set patexps [lassign [glob.explode $escpattern] rest]\n"
1342 "		if {$rest ne \"\"} {\n"
1343 "			return -code error \"unmatched close brace in glob pattern\"\n"
1344 "		}\n"
1345 "		foreach patexp $patexps {\n"
1346 "			set patexp [string map {\n"
1347 "				\\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
1348 "			} $patexp]\n"
1349 "			foreach {realname name} [glob.glob $base $patexp] {\n"
1350 "				incr n\n"
1351 "				if {$tails} {\n"
1352 "					lappend result $name\n"
1353 "				} else {\n"
1354 "					lappend result [file join $base $name]\n"
1355 "				}\n"
1356 "			}\n"
1357 "		}\n"
1358 "	}\n"
1359 "\n"
1360 "	if {!$nocomplain && [llength $result] == 0} {\n"
1361 "		set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
1362 "		return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
1363 "	}\n"
1364 "\n"
1365 "	return $result\n"
1366 "}\n"
1367 );
1368 }
Jim_stdlibInit(Jim_Interp * interp)1369 int Jim_stdlibInit(Jim_Interp *interp)
1370 {
1371 	if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
1372 		return JIM_ERR;
1373 
1374 	return Jim_EvalSource(interp, "stdlib.tcl", 1,
1375 "\n"
1376 "\n"
1377 "\n"
1378 "proc lambda {arglist args} {\n"
1379 "	tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
1380 "}\n"
1381 "\n"
1382 "proc lambda.finalizer {name val} {\n"
1383 "	rename $name {}\n"
1384 "}\n"
1385 "\n"
1386 "\n"
1387 "proc curry {args} {\n"
1388 "	alias [ref {} function lambda.finalizer] {*}$args\n"
1389 "}\n"
1390 "\n"
1391 "\n"
1392 "\n"
1393 "\n"
1394 "\n"
1395 "\n"
1396 "\n"
1397 "\n"
1398 "\n"
1399 "proc function {value} {\n"
1400 "	return $value\n"
1401 "}\n"
1402 "\n"
1403 "\n"
1404 "\n"
1405 "\n"
1406 "proc stacktrace {{skip 0}} {\n"
1407 "	set trace {}\n"
1408 "	incr skip\n"
1409 "	foreach level [range $skip [info level]] {\n"
1410 "		lappend trace {*}[info frame -$level]\n"
1411 "	}\n"
1412 "	return $trace\n"
1413 "}\n"
1414 "\n"
1415 "\n"
1416 "proc stackdump {stacktrace} {\n"
1417 "	set lines {}\n"
1418 "	foreach {l f p} [lreverse $stacktrace] {\n"
1419 "		set line {}\n"
1420 "		if {$p ne \"\"} {\n"
1421 "			append line \"in procedure '$p' \"\n"
1422 "			if {$f ne \"\"} {\n"
1423 "				append line \"called \"\n"
1424 "			}\n"
1425 "		}\n"
1426 "		if {$f ne \"\"} {\n"
1427 "			append line \"at file \\\"$f\\\", line $l\"\n"
1428 "		}\n"
1429 "		if {$line ne \"\"} {\n"
1430 "			lappend lines $line\n"
1431 "		}\n"
1432 "	}\n"
1433 "	join $lines \\n\n"
1434 "}\n"
1435 "\n"
1436 "\n"
1437 "\n"
1438 "proc errorInfo {msg {stacktrace \"\"}} {\n"
1439 "	if {$stacktrace eq \"\"} {\n"
1440 "\n"
1441 "		set stacktrace [info stacktrace]\n"
1442 "\n"
1443 "		lappend stacktrace {*}[stacktrace 1]\n"
1444 "	}\n"
1445 "	lassign $stacktrace p f l\n"
1446 "	if {$f ne \"\"} {\n"
1447 "		set result \"$f:$l: Error: \"\n"
1448 "	}\n"
1449 "	append result \"$msg\\n\"\n"
1450 "	append result [stackdump $stacktrace]\n"
1451 "\n"
1452 "\n"
1453 "	string trim $result\n"
1454 "}\n"
1455 "\n"
1456 "\n"
1457 "\n"
1458 "proc {info nameofexecutable} {} {\n"
1459 "	if {[exists ::jim::exe]} {\n"
1460 "		return $::jim::exe\n"
1461 "	}\n"
1462 "}\n"
1463 "\n"
1464 "\n"
1465 "proc {dict update} {&varName args script} {\n"
1466 "	set keys {}\n"
1467 "	foreach {n v} $args {\n"
1468 "		upvar $v var_$v\n"
1469 "		if {[dict exists $varName $n]} {\n"
1470 "			set var_$v [dict get $varName $n]\n"
1471 "		}\n"
1472 "	}\n"
1473 "	catch {uplevel 1 $script} msg opts\n"
1474 "	if {[info exists varName]} {\n"
1475 "		foreach {n v} $args {\n"
1476 "			if {[info exists var_$v]} {\n"
1477 "				dict set varName $n [set var_$v]\n"
1478 "			} else {\n"
1479 "				dict unset varName $n\n"
1480 "			}\n"
1481 "		}\n"
1482 "	}\n"
1483 "	return {*}$opts $msg\n"
1484 "}\n"
1485 "\n"
1486 "proc {dict replace} {dictionary {args {key value}}} {\n"
1487 "	if {[llength ${key value}] % 2} {\n"
1488 "		tailcall {dict replace}\n"
1489 "	}\n"
1490 "	tailcall dict merge $dictionary ${key value}\n"
1491 "}\n"
1492 "\n"
1493 "\n"
1494 "proc {dict lappend} {varName key {args value}} {\n"
1495 "	upvar $varName dict\n"
1496 "	if {[exists dict] && [dict exists $dict $key]} {\n"
1497 "		set list [dict get $dict $key]\n"
1498 "	}\n"
1499 "	lappend list {*}$value\n"
1500 "	dict set dict $key $list\n"
1501 "}\n"
1502 "\n"
1503 "\n"
1504 "proc {dict append} {varName key {args value}} {\n"
1505 "	upvar $varName dict\n"
1506 "	if {[exists dict] && [dict exists $dict $key]} {\n"
1507 "		set str [dict get $dict $key]\n"
1508 "	}\n"
1509 "	append str {*}$value\n"
1510 "	dict set dict $key $str\n"
1511 "}\n"
1512 "\n"
1513 "\n"
1514 "proc {dict incr} {varName key {increment 1}} {\n"
1515 "	upvar $varName dict\n"
1516 "	if {[exists dict] && [dict exists $dict $key]} {\n"
1517 "		set value [dict get $dict $key]\n"
1518 "	}\n"
1519 "	incr value $increment\n"
1520 "	dict set dict $key $value\n"
1521 "}\n"
1522 "\n"
1523 "\n"
1524 "proc {dict remove} {dictionary {args key}} {\n"
1525 "	foreach k $key {\n"
1526 "		dict unset dictionary $k\n"
1527 "	}\n"
1528 "	return $dictionary\n"
1529 "}\n"
1530 "\n"
1531 "\n"
1532 "proc {dict for} {vars dictionary script} {\n"
1533 "	if {[llength $vars] != 2} {\n"
1534 "		return -code error \"must have exactly two variable names\"\n"
1535 "	}\n"
1536 "	dict size $dictionary\n"
1537 "	tailcall foreach $vars $dictionary $script\n"
1538 "}\n"
1539 );
1540 }
Jim_tclcompatInit(Jim_Interp * interp)1541 int Jim_tclcompatInit(Jim_Interp *interp)
1542 {
1543 	if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG))
1544 		return JIM_ERR;
1545 
1546 	return Jim_EvalSource(interp, "tclcompat.tcl", 1,
1547 "\n"
1548 "\n"
1549 "\n"
1550 "\n"
1551 "\n"
1552 "\n"
1553 "\n"
1554 "\n"
1555 "set env [env]\n"
1556 "\n"
1557 "\n"
1558 "if {[info commands stdout] ne \"\"} {\n"
1559 "\n"
1560 "	foreach p {gets flush close eof seek tell} {\n"
1561 "		proc $p {chan args} {p} {\n"
1562 "			tailcall $chan $p {*}$args\n"
1563 "		}\n"
1564 "	}\n"
1565 "	unset p\n"
1566 "\n"
1567 "\n"
1568 "\n"
1569 "	proc puts {{-nonewline {}} {chan stdout} msg} {\n"
1570 "		if {${-nonewline} ni {-nonewline {}}} {\n"
1571 "			tailcall ${-nonewline} puts $msg\n"
1572 "		}\n"
1573 "		tailcall $chan puts {*}${-nonewline} $msg\n"
1574 "	}\n"
1575 "\n"
1576 "\n"
1577 "\n"
1578 "\n"
1579 "\n"
1580 "	proc read {{-nonewline {}} chan} {\n"
1581 "		if {${-nonewline} ni {-nonewline {}}} {\n"
1582 "			tailcall ${-nonewline} read {*}${chan}\n"
1583 "		}\n"
1584 "		tailcall $chan read {*}${-nonewline}\n"
1585 "	}\n"
1586 "\n"
1587 "	proc fconfigure {f args} {\n"
1588 "		foreach {n v} $args {\n"
1589 "			switch -glob -- $n {\n"
1590 "				-bl* {\n"
1591 "					$f ndelay $(!$v)\n"
1592 "				}\n"
1593 "				-bu* {\n"
1594 "					$f buffering $v\n"
1595 "				}\n"
1596 "				-tr* {\n"
1597 "\n"
1598 "				}\n"
1599 "				default {\n"
1600 "					return -code error \"fconfigure: unknown option $n\"\n"
1601 "				}\n"
1602 "			}\n"
1603 "		}\n"
1604 "	}\n"
1605 "}\n"
1606 "\n"
1607 "\n"
1608 "proc fileevent {args} {\n"
1609 "	tailcall {*}$args\n"
1610 "}\n"
1611 "\n"
1612 "\n"
1613 "\n"
1614 "proc parray {arrayname {pattern *} {puts puts}} {\n"
1615 "	upvar $arrayname a\n"
1616 "\n"
1617 "	set max 0\n"
1618 "	foreach name [array names a $pattern]] {\n"
1619 "		if {[string length $name] > $max} {\n"
1620 "			set max [string length $name]\n"
1621 "		}\n"
1622 "	}\n"
1623 "	incr max [string length $arrayname]\n"
1624 "	incr max 2\n"
1625 "	foreach name [lsort [array names a $pattern]] {\n"
1626 "		$puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
1627 "	}\n"
1628 "}\n"
1629 "\n"
1630 "\n"
1631 "proc {file copy} {{force {}} source target} {\n"
1632 "	try {\n"
1633 "		if {$force ni {{} -force}} {\n"
1634 "			error \"bad option \\\"$force\\\": should be -force\"\n"
1635 "		}\n"
1636 "\n"
1637 "		set in [open $source rb]\n"
1638 "\n"
1639 "		if {[file exists $target]} {\n"
1640 "			if {$force eq \"\"} {\n"
1641 "				error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
1642 "			}\n"
1643 "\n"
1644 "			if {$source eq $target} {\n"
1645 "				return\n"
1646 "			}\n"
1647 "\n"
1648 "\n"
1649 "			file stat $source ss\n"
1650 "			file stat $target ts\n"
1651 "			if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
1652 "				return\n"
1653 "			}\n"
1654 "		}\n"
1655 "		set out [open $target wb]\n"
1656 "		$in copyto $out\n"
1657 "		$out close\n"
1658 "	} on error {msg opts} {\n"
1659 "		incr opts(-level)\n"
1660 "		return {*}$opts $msg\n"
1661 "	} finally {\n"
1662 "		catch {$in close}\n"
1663 "	}\n"
1664 "}\n"
1665 "\n"
1666 "\n"
1667 "\n"
1668 "proc popen {cmd {mode r}} {\n"
1669 "	lassign [socket pipe] r w\n"
1670 "	try {\n"
1671 "		if {[string match \"w*\" $mode]} {\n"
1672 "			lappend cmd <@$r &\n"
1673 "			set pids [exec {*}$cmd]\n"
1674 "			$r close\n"
1675 "			set f $w\n"
1676 "		} else {\n"
1677 "			lappend cmd >@$w &\n"
1678 "			set pids [exec {*}$cmd]\n"
1679 "			$w close\n"
1680 "			set f $r\n"
1681 "		}\n"
1682 "		lambda {cmd args} {f pids} {\n"
1683 "			if {$cmd eq \"pid\"} {\n"
1684 "				return $pids\n"
1685 "			}\n"
1686 "			if {$cmd eq \"close\"} {\n"
1687 "				$f close\n"
1688 "\n"
1689 "				foreach p $pids { os.wait $p }\n"
1690 "				return\n"
1691 "			}\n"
1692 "			tailcall $f $cmd {*}$args\n"
1693 "		}\n"
1694 "	} on error {error opts} {\n"
1695 "		$r close\n"
1696 "		$w close\n"
1697 "		error $error\n"
1698 "	}\n"
1699 "}\n"
1700 "\n"
1701 "\n"
1702 "local proc pid {{channelId {}}} {\n"
1703 "	if {$channelId eq \"\"} {\n"
1704 "		tailcall upcall pid\n"
1705 "	}\n"
1706 "	if {[catch {$channelId tell}]} {\n"
1707 "		return -code error \"can not find channel named \\\"$channelId\\\"\"\n"
1708 "	}\n"
1709 "	if {[catch {$channelId pid} pids]} {\n"
1710 "		return \"\"\n"
1711 "	}\n"
1712 "	return $pids\n"
1713 "}\n"
1714 "\n"
1715 "\n"
1716 "\n"
1717 "\n"
1718 "\n"
1719 "\n"
1720 "\n"
1721 "\n"
1722 "\n"
1723 "proc try {args} {\n"
1724 "	set catchopts {}\n"
1725 "	while {[string match -* [lindex $args 0]]} {\n"
1726 "		set args [lassign $args opt]\n"
1727 "		if {$opt eq \"--\"} {\n"
1728 "			break\n"
1729 "		}\n"
1730 "		lappend catchopts $opt\n"
1731 "	}\n"
1732 "	if {[llength $args] == 0} {\n"
1733 "		return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n"
1734 "	}\n"
1735 "	set args [lassign $args script]\n"
1736 "	set code [catch -eval {*}$catchopts {uplevel 1 $script} msg opts]\n"
1737 "\n"
1738 "	set handled 0\n"
1739 "\n"
1740 "	foreach {on codes vars script} $args {\n"
1741 "		switch -- $on \\\n"
1742 "			on {\n"
1743 "				if {!$handled && ($codes eq \"*\" || [info returncode $code] in $codes)} {\n"
1744 "					lassign $vars msgvar optsvar\n"
1745 "					if {$msgvar ne \"\"} {\n"
1746 "						upvar $msgvar hmsg\n"
1747 "						set hmsg $msg\n"
1748 "					}\n"
1749 "					if {$optsvar ne \"\"} {\n"
1750 "						upvar $optsvar hopts\n"
1751 "						set hopts $opts\n"
1752 "					}\n"
1753 "\n"
1754 "					set code [catch {uplevel 1 $script} msg opts]\n"
1755 "					incr handled\n"
1756 "				}\n"
1757 "			} \\\n"
1758 "			finally {\n"
1759 "				set finalcode [catch {uplevel 1 $codes} finalmsg finalopts]\n"
1760 "				if {$finalcode} {\n"
1761 "\n"
1762 "					set code $finalcode\n"
1763 "					set msg $finalmsg\n"
1764 "					set opts $finalopts\n"
1765 "				}\n"
1766 "				break\n"
1767 "			} \\\n"
1768 "			default {\n"
1769 "				return -code error \"try: expected 'on' or 'finally', got '$on'\"\n"
1770 "			}\n"
1771 "	}\n"
1772 "\n"
1773 "	if {$code} {\n"
1774 "		incr opts(-level)\n"
1775 "		return {*}$opts $msg\n"
1776 "	}\n"
1777 "	return $msg\n"
1778 "}\n"
1779 "\n"
1780 "\n"
1781 "\n"
1782 "proc throw {code {msg \"\"}} {\n"
1783 "	return -code $code $msg\n"
1784 "}\n"
1785 "\n"
1786 "\n"
1787 "proc {file delete force} {path} {\n"
1788 "	foreach e [readdir $path] {\n"
1789 "		file delete -force $path/$e\n"
1790 "	}\n"
1791 "	file delete $path\n"
1792 "}\n"
1793 );
1794 }
1795 
1796 
1797 #ifndef _GNU_SOURCE
1798 #define _GNU_SOURCE
1799 #endif
1800 #include <stdio.h>
1801 #include <string.h>
1802 #include <errno.h>
1803 #include <fcntl.h>
1804 #ifdef HAVE_UNISTD_H
1805 #include <unistd.h>
1806 #include <sys/stat.h>
1807 #endif
1808 
1809 
1810 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
1811 #include <sys/socket.h>
1812 #include <netinet/in.h>
1813 #include <arpa/inet.h>
1814 #include <netdb.h>
1815 #ifdef HAVE_SYS_UN_H
1816 #include <sys/un.h>
1817 #endif
1818 #else
1819 #define JIM_ANSIC
1820 #endif
1821 
1822 #if defined(JIM_SSL)
1823 #include <openssl/ssl.h>
1824 #include <openssl/err.h>
1825 #endif
1826 
1827 #ifdef HAVE_TERMIOS_H
1828 #endif
1829 
1830 
1831 #define AIO_CMD_LEN 32
1832 #define AIO_BUF_LEN 256
1833 
1834 #ifndef HAVE_FTELLO
1835     #define ftello ftell
1836 #endif
1837 #ifndef HAVE_FSEEKO
1838     #define fseeko fseek
1839 #endif
1840 
1841 #define AIO_KEEPOPEN 1
1842 
1843 #if defined(JIM_IPV6)
1844 #define IPV6 1
1845 #else
1846 #define IPV6 0
1847 #ifndef PF_INET6
1848 #define PF_INET6 0
1849 #endif
1850 #endif
1851 
1852 #define JimCheckStreamError(interp, af) af->fops->error(af)
1853 
1854 
1855 struct AioFile;
1856 
1857 typedef struct {
1858     int (*writer)(struct AioFile *af, const char *buf, int len);
1859     int (*reader)(struct AioFile *af, char *buf, int len);
1860     const char *(*getline)(struct AioFile *af, char *buf, int len);
1861     int (*error)(const struct AioFile *af);
1862     const char *(*strerror)(struct AioFile *af);
1863     int (*verify)(struct AioFile *af);
1864 } JimAioFopsType;
1865 
1866 typedef struct AioFile
1867 {
1868     FILE *fp;
1869     Jim_Obj *filename;
1870     int type;
1871     int openFlags;
1872     int fd;
1873     Jim_Obj *rEvent;
1874     Jim_Obj *wEvent;
1875     Jim_Obj *eEvent;
1876     int addr_family;
1877     void *ssl;
1878     const JimAioFopsType *fops;
1879 } AioFile;
1880 
stdio_writer(struct AioFile * af,const char * buf,int len)1881 static int stdio_writer(struct AioFile *af, const char *buf, int len)
1882 {
1883     return fwrite(buf, 1, len, af->fp);
1884 }
1885 
stdio_reader(struct AioFile * af,char * buf,int len)1886 static int stdio_reader(struct AioFile *af, char *buf, int len)
1887 {
1888     return fread(buf, 1, len, af->fp);
1889 }
1890 
stdio_getline(struct AioFile * af,char * buf,int len)1891 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
1892 {
1893     return fgets(buf, len, af->fp);
1894 }
1895 
stdio_error(const AioFile * af)1896 static int stdio_error(const AioFile *af)
1897 {
1898     if (!ferror(af->fp)) {
1899         return JIM_OK;
1900     }
1901     clearerr(af->fp);
1902 
1903     if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
1904         return JIM_OK;
1905     }
1906 #ifdef ECONNRESET
1907     if (errno == ECONNRESET) {
1908         return JIM_OK;
1909     }
1910 #endif
1911 #ifdef ECONNABORTED
1912     if (errno == ECONNABORTED) {
1913         return JIM_OK;
1914     }
1915 #endif
1916     return JIM_ERR;
1917 }
1918 
stdio_strerror(struct AioFile * af)1919 static const char *stdio_strerror(struct AioFile *af)
1920 {
1921     return strerror(errno);
1922 }
1923 
1924 static const JimAioFopsType stdio_fops = {
1925     stdio_writer,
1926     stdio_reader,
1927     stdio_getline,
1928     stdio_error,
1929     stdio_strerror,
1930     NULL
1931 };
1932 
1933 
1934 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
1935 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1936     const char *hdlfmt, int family, const char *mode);
1937 
1938 
JimAioErrorString(AioFile * af)1939 static const char *JimAioErrorString(AioFile *af)
1940 {
1941     if (af && af->fops)
1942         return af->fops->strerror(af);
1943 
1944     return strerror(errno);
1945 }
1946 
JimAioSetError(Jim_Interp * interp,Jim_Obj * name)1947 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
1948 {
1949     AioFile *af = Jim_CmdPrivData(interp);
1950 
1951     if (name) {
1952         Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
1953     }
1954     else {
1955         Jim_SetResultString(interp, JimAioErrorString(af), -1);
1956     }
1957 }
1958 
JimAioDelProc(Jim_Interp * interp,void * privData)1959 static void JimAioDelProc(Jim_Interp *interp, void *privData)
1960 {
1961     AioFile *af = privData;
1962 
1963     JIM_NOTUSED(interp);
1964 
1965     Jim_DecrRefCount(interp, af->filename);
1966 
1967 #ifdef jim_ext_eventloop
1968 
1969     Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
1970 #endif
1971 
1972 #if defined(JIM_SSL)
1973     if (af->ssl != NULL) {
1974         SSL_free(af->ssl);
1975     }
1976 #endif
1977     if (!(af->openFlags & AIO_KEEPOPEN)) {
1978         fclose(af->fp);
1979     }
1980 
1981     Jim_Free(af);
1982 }
1983 
aio_cmd_read(Jim_Interp * interp,int argc,Jim_Obj * const * argv)1984 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1985 {
1986     AioFile *af = Jim_CmdPrivData(interp);
1987     char buf[AIO_BUF_LEN];
1988     Jim_Obj *objPtr;
1989     int nonewline = 0;
1990     jim_wide neededLen = -1;
1991 
1992     if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
1993         nonewline = 1;
1994         argv++;
1995         argc--;
1996     }
1997     if (argc == 1) {
1998         if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
1999             return JIM_ERR;
2000         if (neededLen < 0) {
2001             Jim_SetResultString(interp, "invalid parameter: negative len", -1);
2002             return JIM_ERR;
2003         }
2004     }
2005     else if (argc) {
2006         return -1;
2007     }
2008     objPtr = Jim_NewStringObj(interp, NULL, 0);
2009     while (neededLen != 0) {
2010         int retval;
2011         int readlen;
2012 
2013         if (neededLen == -1) {
2014             readlen = AIO_BUF_LEN;
2015         }
2016         else {
2017             readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
2018         }
2019         retval = af->fops->reader(af, buf, readlen);
2020         if (retval > 0) {
2021             Jim_AppendString(interp, objPtr, buf, retval);
2022             if (neededLen != -1) {
2023                 neededLen -= retval;
2024             }
2025         }
2026         if (retval != readlen)
2027             break;
2028     }
2029 
2030     if (JimCheckStreamError(interp, af)) {
2031         Jim_FreeNewObj(interp, objPtr);
2032         return JIM_ERR;
2033     }
2034     if (nonewline) {
2035         int len;
2036         const char *s = Jim_GetString(objPtr, &len);
2037 
2038         if (len > 0 && s[len - 1] == '\n') {
2039             objPtr->length--;
2040             objPtr->bytes[objPtr->length] = '\0';
2041         }
2042     }
2043     Jim_SetResult(interp, objPtr);
2044     return JIM_OK;
2045 }
2046 
Jim_AioFile(Jim_Interp * interp,Jim_Obj * command)2047 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
2048 {
2049     Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
2050 
2051 
2052     if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
2053         return (AioFile *) cmdPtr->u.native.privData;
2054     }
2055     Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
2056     return NULL;
2057 }
2058 
Jim_AioFilehandle(Jim_Interp * interp,Jim_Obj * command)2059 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
2060 {
2061     AioFile *af;
2062 
2063     af = Jim_AioFile(interp, command);
2064     if (af == NULL) {
2065         return NULL;
2066     }
2067 
2068     return af->fp;
2069 }
2070 
aio_cmd_copy(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2071 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2072 {
2073     AioFile *af = Jim_CmdPrivData(interp);
2074     jim_wide count = 0;
2075     jim_wide maxlen = JIM_WIDE_MAX;
2076     AioFile *outf = Jim_AioFile(interp, argv[0]);
2077 
2078     if (outf == NULL) {
2079         return JIM_ERR;
2080     }
2081 
2082     if (argc == 2) {
2083         if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
2084             return JIM_ERR;
2085         }
2086     }
2087 
2088     while (count < maxlen) {
2089         char ch;
2090 
2091         if (af->fops->reader(af, &ch, 1) != 1) {
2092             break;
2093         }
2094         if (outf->fops->writer(outf, &ch, 1) != 1) {
2095             break;
2096         }
2097         count++;
2098     }
2099 
2100     if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
2101         return JIM_ERR;
2102     }
2103 
2104     Jim_SetResultInt(interp, count);
2105 
2106     return JIM_OK;
2107 }
2108 
aio_cmd_gets(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2109 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2110 {
2111     AioFile *af = Jim_CmdPrivData(interp);
2112     char buf[AIO_BUF_LEN];
2113     Jim_Obj *objPtr;
2114     int len;
2115 
2116     errno = 0;
2117 
2118     objPtr = Jim_NewStringObj(interp, NULL, 0);
2119     while (1) {
2120         buf[AIO_BUF_LEN - 1] = '_';
2121 
2122         if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
2123             break;
2124 
2125         if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
2126             Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
2127         }
2128         else {
2129             len = strlen(buf);
2130 
2131             if (len && (buf[len - 1] == '\n')) {
2132 
2133                 len--;
2134             }
2135 
2136             Jim_AppendString(interp, objPtr, buf, len);
2137             break;
2138         }
2139     }
2140 
2141     if (JimCheckStreamError(interp, af)) {
2142 
2143         Jim_FreeNewObj(interp, objPtr);
2144         return JIM_ERR;
2145     }
2146 
2147     if (argc) {
2148         if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
2149             Jim_FreeNewObj(interp, objPtr);
2150             return JIM_ERR;
2151         }
2152 
2153         len = Jim_Length(objPtr);
2154 
2155         if (len == 0 && feof(af->fp)) {
2156 
2157             len = -1;
2158         }
2159         Jim_SetResultInt(interp, len);
2160     }
2161     else {
2162         Jim_SetResult(interp, objPtr);
2163     }
2164     return JIM_OK;
2165 }
2166 
aio_cmd_puts(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2167 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2168 {
2169     AioFile *af = Jim_CmdPrivData(interp);
2170     int wlen;
2171     const char *wdata;
2172     Jim_Obj *strObj;
2173 
2174     if (argc == 2) {
2175         if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
2176             return -1;
2177         }
2178         strObj = argv[1];
2179     }
2180     else {
2181         strObj = argv[0];
2182     }
2183 
2184     wdata = Jim_GetString(strObj, &wlen);
2185     if (af->fops->writer(af, wdata, wlen) == wlen) {
2186         if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
2187             return JIM_OK;
2188         }
2189     }
2190     JimAioSetError(interp, af->filename);
2191     return JIM_ERR;
2192 }
2193 
aio_cmd_isatty(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2194 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2195 {
2196 #ifdef HAVE_ISATTY
2197     AioFile *af = Jim_CmdPrivData(interp);
2198     Jim_SetResultInt(interp, isatty(fileno(af->fp)));
2199 #else
2200     Jim_SetResultInt(interp, 0);
2201 #endif
2202 
2203     return JIM_OK;
2204 }
2205 
2206 
aio_cmd_flush(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2207 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2208 {
2209     AioFile *af = Jim_CmdPrivData(interp);
2210 
2211     if (fflush(af->fp) == EOF) {
2212         JimAioSetError(interp, af->filename);
2213         return JIM_ERR;
2214     }
2215     return JIM_OK;
2216 }
2217 
aio_cmd_eof(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2218 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2219 {
2220     AioFile *af = Jim_CmdPrivData(interp);
2221 
2222     Jim_SetResultInt(interp, feof(af->fp));
2223     return JIM_OK;
2224 }
2225 
aio_cmd_close(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2226 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2227 {
2228     if (argc == 3) {
2229 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
2230         static const char * const options[] = { "r", "w", NULL };
2231         enum { OPT_R, OPT_W, };
2232         int option;
2233         AioFile *af = Jim_CmdPrivData(interp);
2234 
2235         if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2236             return JIM_ERR;
2237         }
2238         if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
2239             return JIM_OK;
2240         }
2241         JimAioSetError(interp, NULL);
2242 #else
2243         Jim_SetResultString(interp, "async close not supported", -1);
2244 #endif
2245         return JIM_ERR;
2246     }
2247 
2248     return Jim_DeleteCommand(interp, Jim_String(argv[0]));
2249 }
2250 
aio_cmd_seek(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2251 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2252 {
2253     AioFile *af = Jim_CmdPrivData(interp);
2254     int orig = SEEK_SET;
2255     jim_wide offset;
2256 
2257     if (argc == 2) {
2258         if (Jim_CompareStringImmediate(interp, argv[1], "start"))
2259             orig = SEEK_SET;
2260         else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
2261             orig = SEEK_CUR;
2262         else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
2263             orig = SEEK_END;
2264         else {
2265             return -1;
2266         }
2267     }
2268     if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
2269         return JIM_ERR;
2270     }
2271     if (fseeko(af->fp, offset, orig) == -1) {
2272         JimAioSetError(interp, af->filename);
2273         return JIM_ERR;
2274     }
2275     return JIM_OK;
2276 }
2277 
aio_cmd_tell(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2278 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2279 {
2280     AioFile *af = Jim_CmdPrivData(interp);
2281 
2282     Jim_SetResultInt(interp, ftello(af->fp));
2283     return JIM_OK;
2284 }
2285 
aio_cmd_filename(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2286 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2287 {
2288     AioFile *af = Jim_CmdPrivData(interp);
2289 
2290     Jim_SetResult(interp, af->filename);
2291     return JIM_OK;
2292 }
2293 
2294 #ifdef O_NDELAY
aio_cmd_ndelay(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2295 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2296 {
2297     AioFile *af = Jim_CmdPrivData(interp);
2298 
2299     int fmode = fcntl(af->fd, F_GETFL);
2300 
2301     if (argc) {
2302         long nb;
2303 
2304         if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
2305             return JIM_ERR;
2306         }
2307         if (nb) {
2308             fmode |= O_NDELAY;
2309         }
2310         else {
2311             fmode &= ~O_NDELAY;
2312         }
2313         (void)fcntl(af->fd, F_SETFL, fmode);
2314     }
2315     Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
2316     return JIM_OK;
2317 }
2318 #endif
2319 
2320 #ifdef HAVE_FSYNC
aio_cmd_sync(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2321 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2322 {
2323     AioFile *af = Jim_CmdPrivData(interp);
2324 
2325     fflush(af->fp);
2326     fsync(af->fd);
2327     return JIM_OK;
2328 }
2329 #endif
2330 
aio_cmd_buffering(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2331 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2332 {
2333     AioFile *af = Jim_CmdPrivData(interp);
2334 
2335     static const char * const options[] = {
2336         "none",
2337         "line",
2338         "full",
2339         NULL
2340     };
2341     enum
2342     {
2343         OPT_NONE,
2344         OPT_LINE,
2345         OPT_FULL,
2346     };
2347     int option;
2348 
2349     if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2350         return JIM_ERR;
2351     }
2352     switch (option) {
2353         case OPT_NONE:
2354             setvbuf(af->fp, NULL, _IONBF, 0);
2355             break;
2356         case OPT_LINE:
2357             setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
2358             break;
2359         case OPT_FULL:
2360             setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
2361             break;
2362     }
2363     return JIM_OK;
2364 }
2365 
2366 #ifdef jim_ext_eventloop
JimAioFileEventFinalizer(Jim_Interp * interp,void * clientData)2367 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
2368 {
2369     Jim_Obj **objPtrPtr = clientData;
2370 
2371     Jim_DecrRefCount(interp, *objPtrPtr);
2372     *objPtrPtr = NULL;
2373 }
2374 
JimAioFileEventHandler(Jim_Interp * interp,void * clientData,int mask)2375 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
2376 {
2377     Jim_Obj **objPtrPtr = clientData;
2378 
2379     return Jim_EvalObjBackground(interp, *objPtrPtr);
2380 }
2381 
aio_eventinfo(Jim_Interp * interp,AioFile * af,unsigned mask,Jim_Obj ** scriptHandlerObj,int argc,Jim_Obj * const * argv)2382 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
2383     int argc, Jim_Obj * const *argv)
2384 {
2385     if (argc == 0) {
2386 
2387         if (*scriptHandlerObj) {
2388             Jim_SetResult(interp, *scriptHandlerObj);
2389         }
2390         return JIM_OK;
2391     }
2392 
2393     if (*scriptHandlerObj) {
2394 
2395         Jim_DeleteFileHandler(interp, af->fd, mask);
2396     }
2397 
2398 
2399     if (Jim_Length(argv[0]) == 0) {
2400 
2401         return JIM_OK;
2402     }
2403 
2404 
2405     Jim_IncrRefCount(argv[0]);
2406     *scriptHandlerObj = argv[0];
2407 
2408     Jim_CreateFileHandler(interp, af->fd, mask,
2409         JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
2410 
2411     return JIM_OK;
2412 }
2413 
aio_cmd_readable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2414 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2415 {
2416     AioFile *af = Jim_CmdPrivData(interp);
2417 
2418     return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
2419 }
2420 
aio_cmd_writable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2421 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2422 {
2423     AioFile *af = Jim_CmdPrivData(interp);
2424 
2425     return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
2426 }
2427 
aio_cmd_onexception(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2428 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2429 {
2430     AioFile *af = Jim_CmdPrivData(interp);
2431 
2432     return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
2433 }
2434 #endif
2435 
2436 
2437 
2438 
2439 static const jim_subcmd_type aio_command_table[] = {
2440     {   "read",
2441         "?-nonewline? ?len?",
2442         aio_cmd_read,
2443         0,
2444         2,
2445 
2446     },
2447     {   "copyto",
2448         "handle ?size?",
2449         aio_cmd_copy,
2450         1,
2451         2,
2452 
2453     },
2454     {   "gets",
2455         "?var?",
2456         aio_cmd_gets,
2457         0,
2458         1,
2459 
2460     },
2461     {   "puts",
2462         "?-nonewline? str",
2463         aio_cmd_puts,
2464         1,
2465         2,
2466 
2467     },
2468     {   "isatty",
2469         NULL,
2470         aio_cmd_isatty,
2471         0,
2472         0,
2473 
2474     },
2475     {   "flush",
2476         NULL,
2477         aio_cmd_flush,
2478         0,
2479         0,
2480 
2481     },
2482     {   "eof",
2483         NULL,
2484         aio_cmd_eof,
2485         0,
2486         0,
2487 
2488     },
2489     {   "close",
2490         "?r(ead)|w(rite)?",
2491         aio_cmd_close,
2492         0,
2493         1,
2494         JIM_MODFLAG_FULLARGV,
2495 
2496     },
2497     {   "seek",
2498         "offset ?start|current|end",
2499         aio_cmd_seek,
2500         1,
2501         2,
2502 
2503     },
2504     {   "tell",
2505         NULL,
2506         aio_cmd_tell,
2507         0,
2508         0,
2509 
2510     },
2511     {   "filename",
2512         NULL,
2513         aio_cmd_filename,
2514         0,
2515         0,
2516 
2517     },
2518 #ifdef O_NDELAY
2519     {   "ndelay",
2520         "?0|1?",
2521         aio_cmd_ndelay,
2522         0,
2523         1,
2524 
2525     },
2526 #endif
2527 #ifdef HAVE_FSYNC
2528     {   "sync",
2529         NULL,
2530         aio_cmd_sync,
2531         0,
2532         0,
2533 
2534     },
2535 #endif
2536     {   "buffering",
2537         "none|line|full",
2538         aio_cmd_buffering,
2539         1,
2540         1,
2541 
2542     },
2543 #ifdef jim_ext_eventloop
2544     {   "readable",
2545         "?readable-script?",
2546         aio_cmd_readable,
2547         0,
2548         1,
2549 
2550     },
2551     {   "writable",
2552         "?writable-script?",
2553         aio_cmd_writable,
2554         0,
2555         1,
2556 
2557     },
2558     {   "onexception",
2559         "?exception-script?",
2560         aio_cmd_onexception,
2561         0,
2562         1,
2563 
2564     },
2565 #endif
2566     { NULL }
2567 };
2568 
JimAioSubCmdProc(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2569 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2570 {
2571     return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
2572 }
2573 
JimAioOpenCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2574 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
2575         Jim_Obj *const *argv)
2576 {
2577     const char *mode;
2578 
2579     if (argc != 2 && argc != 3) {
2580         Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
2581         return JIM_ERR;
2582     }
2583 
2584     mode = (argc == 3) ? Jim_String(argv[2]) : "r";
2585 
2586 #ifdef jim_ext_tclcompat
2587     {
2588         const char *filename = Jim_String(argv[1]);
2589 
2590 
2591         if (*filename == '|') {
2592             Jim_Obj *evalObj[3];
2593 
2594             evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
2595             evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
2596             evalObj[2] = Jim_NewStringObj(interp, mode, -1);
2597 
2598             return Jim_EvalObjVector(interp, 3, evalObj);
2599         }
2600     }
2601 #endif
2602     return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
2603 }
2604 
2605 
JimMakeChannel(Jim_Interp * interp,FILE * fh,int fd,Jim_Obj * filename,const char * hdlfmt,int family,const char * mode)2606 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
2607     const char *hdlfmt, int family, const char *mode)
2608 {
2609     AioFile *af;
2610     char buf[AIO_CMD_LEN];
2611     int openFlags = 0;
2612 
2613     snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2614 
2615     if (fh) {
2616         openFlags = AIO_KEEPOPEN;
2617     }
2618 
2619     snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2620     if (!filename) {
2621         filename = Jim_NewStringObj(interp, buf, -1);
2622     }
2623 
2624     Jim_IncrRefCount(filename);
2625 
2626     if (fh == NULL) {
2627 #if !defined(JIM_ANSIC)
2628         if (fd >= 0) {
2629             fh = fdopen(fd, mode);
2630         }
2631         else
2632 #endif
2633             fh = fopen(Jim_String(filename), mode);
2634 
2635         if (fh == NULL) {
2636             JimAioSetError(interp, filename);
2637 #if !defined(JIM_ANSIC)
2638             if (fd >= 0) {
2639                 close(fd);
2640             }
2641 #endif
2642             Jim_DecrRefCount(interp, filename);
2643             return NULL;
2644         }
2645     }
2646 
2647 
2648     af = Jim_Alloc(sizeof(*af));
2649     memset(af, 0, sizeof(*af));
2650     af->fp = fh;
2651     af->fd = fileno(fh);
2652     af->filename = filename;
2653 #ifdef FD_CLOEXEC
2654     if ((openFlags & AIO_KEEPOPEN) == 0) {
2655         (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
2656     }
2657 #endif
2658     af->openFlags = openFlags;
2659     af->addr_family = family;
2660     af->fops = &stdio_fops;
2661     af->ssl = NULL;
2662 
2663     Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
2664 
2665     Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
2666 
2667     return af;
2668 }
2669 
2670 #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])2671 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
2672     const char *hdlfmt, int family, const char *mode[2])
2673 {
2674     if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
2675         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
2676         Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2677 
2678         if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
2679             Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2680             Jim_SetResult(interp, objPtr);
2681             return JIM_OK;
2682         }
2683     }
2684 
2685 
2686     close(p[0]);
2687     close(p[1]);
2688     JimAioSetError(interp, NULL);
2689     return JIM_ERR;
2690 }
2691 #endif
2692 
2693 
Jim_MakeTempFile(Jim_Interp * interp,const char * filename_template)2694 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template)
2695 {
2696 #ifdef HAVE_MKSTEMP
2697     int fd;
2698     mode_t mask;
2699     Jim_Obj *filenameObj;
2700 
2701     if (filename_template == NULL) {
2702         const char *tmpdir = getenv("TMPDIR");
2703         if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
2704             tmpdir = "/tmp/";
2705         }
2706         filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
2707         if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
2708             Jim_AppendString(interp, filenameObj, "/", 1);
2709         }
2710         Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
2711     }
2712     else {
2713         filenameObj = Jim_NewStringObj(interp, filename_template, -1);
2714     }
2715 
2716 
2717     mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
2718     fd = mkstemp(filenameObj->bytes);
2719     umask(mask);
2720     if (fd < 0) {
2721         JimAioSetError(interp, filenameObj);
2722         Jim_FreeNewObj(interp, filenameObj);
2723         return -1;
2724     }
2725 
2726     Jim_SetResult(interp, filenameObj);
2727     return fd;
2728 #else
2729     Jim_SetResultString(interp, "platform has no tempfile support", -1);
2730     return -1;
2731 #endif
2732 }
2733 
2734 
Jim_aioInit(Jim_Interp * interp)2735 int Jim_aioInit(Jim_Interp *interp)
2736 {
2737     if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2738         return JIM_ERR;
2739 
2740 #if defined(JIM_SSL)
2741     Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2742 #endif
2743 
2744     Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2745 #ifndef JIM_ANSIC
2746     Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2747 #endif
2748 
2749 
2750     JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
2751     JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
2752     JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
2753 
2754     return JIM_OK;
2755 }
2756 
2757 #include <errno.h>
2758 #include <stdio.h>
2759 #include <string.h>
2760 
2761 
2762 #ifdef HAVE_DIRENT_H
2763 #include <dirent.h>
2764 #endif
2765 
Jim_ReaddirCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2766 int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2767 {
2768     const char *dirPath;
2769     DIR *dirPtr;
2770     struct dirent *entryPtr;
2771     int nocomplain = 0;
2772 
2773     if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) {
2774         nocomplain = 1;
2775     }
2776     if (argc != 2 && !nocomplain) {
2777         Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
2778         return JIM_ERR;
2779     }
2780 
2781     dirPath = Jim_String(argv[1 + nocomplain]);
2782 
2783     dirPtr = opendir(dirPath);
2784     if (dirPtr == NULL) {
2785         if (nocomplain) {
2786             return JIM_OK;
2787         }
2788         Jim_SetResultString(interp, strerror(errno), -1);
2789         return JIM_ERR;
2790     }
2791     else {
2792         Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
2793 
2794         while ((entryPtr = readdir(dirPtr)) != NULL) {
2795             if (entryPtr->d_name[0] == '.') {
2796                 if (entryPtr->d_name[1] == '\0') {
2797                     continue;
2798                 }
2799                 if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0'))
2800                     continue;
2801             }
2802             Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1));
2803         }
2804         closedir(dirPtr);
2805 
2806         Jim_SetResult(interp, listObj);
2807 
2808         return JIM_OK;
2809     }
2810 }
2811 
Jim_readdirInit(Jim_Interp * interp)2812 int Jim_readdirInit(Jim_Interp *interp)
2813 {
2814     if (Jim_PackageProvide(interp, "readdir", "1.0", JIM_ERRMSG))
2815         return JIM_ERR;
2816 
2817     Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
2818     return JIM_OK;
2819 }
2820 
2821 #include <stdlib.h>
2822 #include <string.h>
2823 
2824 #if defined(JIM_REGEXP)
2825 #else
2826     #include <regex.h>
2827 #endif
2828 
FreeRegexpInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)2829 static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2830 {
2831     regfree(objPtr->internalRep.regexpValue.compre);
2832     Jim_Free(objPtr->internalRep.regexpValue.compre);
2833 }
2834 
2835 static const Jim_ObjType regexpObjType = {
2836     "regexp",
2837     FreeRegexpInternalRep,
2838     NULL,
2839     NULL,
2840     JIM_TYPE_NONE
2841 };
2842 
SetRegexpFromAny(Jim_Interp * interp,Jim_Obj * objPtr,unsigned flags)2843 static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
2844 {
2845     regex_t *compre;
2846     const char *pattern;
2847     int ret;
2848 
2849 
2850     if (objPtr->typePtr == &regexpObjType &&
2851         objPtr->internalRep.regexpValue.compre && objPtr->internalRep.regexpValue.flags == flags) {
2852 
2853         return objPtr->internalRep.regexpValue.compre;
2854     }
2855 
2856 
2857 
2858 
2859     pattern = Jim_String(objPtr);
2860     compre = Jim_Alloc(sizeof(regex_t));
2861 
2862     if ((ret = regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
2863         char buf[100];
2864 
2865         regerror(ret, compre, buf, sizeof(buf));
2866         Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
2867         regfree(compre);
2868         Jim_Free(compre);
2869         return NULL;
2870     }
2871 
2872     Jim_FreeIntRep(interp, objPtr);
2873 
2874     objPtr->typePtr = &regexpObjType;
2875     objPtr->internalRep.regexpValue.flags = flags;
2876     objPtr->internalRep.regexpValue.compre = compre;
2877 
2878     return compre;
2879 }
2880 
Jim_RegexpCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)2881 int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2882 {
2883     int opt_indices = 0;
2884     int opt_all = 0;
2885     int opt_inline = 0;
2886     regex_t *regex;
2887     int match, i, j;
2888     int offset = 0;
2889     regmatch_t *pmatch = NULL;
2890     int source_len;
2891     int result = JIM_OK;
2892     const char *pattern;
2893     const char *source_str;
2894     int num_matches = 0;
2895     int num_vars;
2896     Jim_Obj *resultListObj = NULL;
2897     int regcomp_flags = 0;
2898     int eflags = 0;
2899     int option;
2900     enum {
2901         OPT_INDICES,  OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
2902     };
2903     static const char * const options[] = {
2904         "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
2905     };
2906 
2907     if (argc < 3) {
2908       wrongNumArgs:
2909         Jim_WrongNumArgs(interp, 1, argv,
2910             "?-switch ...? exp string ?matchVar? ?subMatchVar ...?");
2911         return JIM_ERR;
2912     }
2913 
2914     for (i = 1; i < argc; i++) {
2915         const char *opt = Jim_String(argv[i]);
2916 
2917         if (*opt != '-') {
2918             break;
2919         }
2920         if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
2921             return JIM_ERR;
2922         }
2923         if (option == OPT_END) {
2924             i++;
2925             break;
2926         }
2927         switch (option) {
2928             case OPT_INDICES:
2929                 opt_indices = 1;
2930                 break;
2931 
2932             case OPT_NOCASE:
2933                 regcomp_flags |= REG_ICASE;
2934                 break;
2935 
2936             case OPT_LINE:
2937                 regcomp_flags |= REG_NEWLINE;
2938                 break;
2939 
2940             case OPT_ALL:
2941                 opt_all = 1;
2942                 break;
2943 
2944             case OPT_INLINE:
2945                 opt_inline = 1;
2946                 break;
2947 
2948             case OPT_START:
2949                 if (++i == argc) {
2950                     goto wrongNumArgs;
2951                 }
2952                 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
2953                     return JIM_ERR;
2954                 }
2955                 break;
2956         }
2957     }
2958     if (argc - i < 2) {
2959         goto wrongNumArgs;
2960     }
2961 
2962     regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
2963     if (!regex) {
2964         return JIM_ERR;
2965     }
2966 
2967     pattern = Jim_String(argv[i]);
2968     source_str = Jim_GetString(argv[i + 1], &source_len);
2969 
2970     num_vars = argc - i - 2;
2971 
2972     if (opt_inline) {
2973         if (num_vars) {
2974             Jim_SetResultString(interp, "regexp match variables not allowed when using -inline",
2975                 -1);
2976             result = JIM_ERR;
2977             goto done;
2978         }
2979         num_vars = regex->re_nsub + 1;
2980     }
2981 
2982     pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch));
2983 
2984     if (offset) {
2985         if (offset < 0) {
2986             offset += source_len + 1;
2987         }
2988         if (offset > source_len) {
2989             source_str += source_len;
2990         }
2991         else if (offset > 0) {
2992             source_str += offset;
2993         }
2994         eflags |= REG_NOTBOL;
2995     }
2996 
2997     if (opt_inline) {
2998         resultListObj = Jim_NewListObj(interp, NULL, 0);
2999     }
3000 
3001   next_match:
3002     match = regexec(regex, source_str, num_vars + 1, pmatch, eflags);
3003     if (match >= REG_BADPAT) {
3004         char buf[100];
3005 
3006         regerror(match, regex, buf, sizeof(buf));
3007         Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3008         result = JIM_ERR;
3009         goto done;
3010     }
3011 
3012     if (match == REG_NOMATCH) {
3013         goto done;
3014     }
3015 
3016     num_matches++;
3017 
3018     if (opt_all && !opt_inline) {
3019 
3020         goto try_next_match;
3021     }
3022 
3023 
3024     j = 0;
3025     for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
3026         Jim_Obj *resultObj;
3027 
3028         if (opt_indices) {
3029             resultObj = Jim_NewListObj(interp, NULL, 0);
3030         }
3031         else {
3032             resultObj = Jim_NewStringObj(interp, "", 0);
3033         }
3034 
3035         if (pmatch[j].rm_so == -1) {
3036             if (opt_indices) {
3037                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3038                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3039             }
3040         }
3041         else {
3042             int len = pmatch[j].rm_eo - pmatch[j].rm_so;
3043 
3044             if (opt_indices) {
3045                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
3046                         offset + pmatch[j].rm_so));
3047                 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
3048                         offset + pmatch[j].rm_so + len - 1));
3049             }
3050             else {
3051                 Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, len);
3052             }
3053         }
3054 
3055         if (opt_inline) {
3056             Jim_ListAppendElement(interp, resultListObj, resultObj);
3057         }
3058         else {
3059 
3060             result = Jim_SetVariable(interp, argv[i], resultObj);
3061 
3062             if (result != JIM_OK) {
3063                 Jim_FreeObj(interp, resultObj);
3064                 break;
3065             }
3066         }
3067     }
3068 
3069   try_next_match:
3070     if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
3071         if (pmatch[0].rm_eo) {
3072             offset += pmatch[0].rm_eo;
3073             source_str += pmatch[0].rm_eo;
3074         }
3075         else {
3076             source_str++;
3077             offset++;
3078         }
3079         if (*source_str) {
3080             eflags = REG_NOTBOL;
3081             goto next_match;
3082         }
3083     }
3084 
3085   done:
3086     if (result == JIM_OK) {
3087         if (opt_inline) {
3088             Jim_SetResult(interp, resultListObj);
3089         }
3090         else {
3091             Jim_SetResultInt(interp, num_matches);
3092         }
3093     }
3094 
3095     Jim_Free(pmatch);
3096     return result;
3097 }
3098 
3099 #define MAX_SUB_MATCHES 50
3100 
Jim_RegsubCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3101 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3102 {
3103     int regcomp_flags = 0;
3104     int regexec_flags = 0;
3105     int opt_all = 0;
3106     int offset = 0;
3107     regex_t *regex;
3108     const char *p;
3109     int result;
3110     regmatch_t pmatch[MAX_SUB_MATCHES + 1];
3111     int num_matches = 0;
3112 
3113     int i, j, n;
3114     Jim_Obj *varname;
3115     Jim_Obj *resultObj;
3116     const char *source_str;
3117     int source_len;
3118     const char *replace_str;
3119     int replace_len;
3120     const char *pattern;
3121     int option;
3122     enum {
3123         OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_END
3124     };
3125     static const char * const options[] = {
3126         "-nocase", "-line", "-all", "-start", "--", NULL
3127     };
3128 
3129     if (argc < 4) {
3130       wrongNumArgs:
3131         Jim_WrongNumArgs(interp, 1, argv,
3132             "?-switch ...? exp string subSpec ?varName?");
3133         return JIM_ERR;
3134     }
3135 
3136     for (i = 1; i < argc; i++) {
3137         const char *opt = Jim_String(argv[i]);
3138 
3139         if (*opt != '-') {
3140             break;
3141         }
3142         if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3143             return JIM_ERR;
3144         }
3145         if (option == OPT_END) {
3146             i++;
3147             break;
3148         }
3149         switch (option) {
3150             case OPT_NOCASE:
3151                 regcomp_flags |= REG_ICASE;
3152                 break;
3153 
3154             case OPT_LINE:
3155                 regcomp_flags |= REG_NEWLINE;
3156                 break;
3157 
3158             case OPT_ALL:
3159                 opt_all = 1;
3160                 break;
3161 
3162             case OPT_START:
3163                 if (++i == argc) {
3164                     goto wrongNumArgs;
3165                 }
3166                 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3167                     return JIM_ERR;
3168                 }
3169                 break;
3170         }
3171     }
3172     if (argc - i != 3 && argc - i != 4) {
3173         goto wrongNumArgs;
3174     }
3175 
3176     regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
3177     if (!regex) {
3178         return JIM_ERR;
3179     }
3180     pattern = Jim_String(argv[i]);
3181 
3182     source_str = Jim_GetString(argv[i + 1], &source_len);
3183     replace_str = Jim_GetString(argv[i + 2], &replace_len);
3184     varname = argv[i + 3];
3185 
3186 
3187     resultObj = Jim_NewStringObj(interp, "", 0);
3188 
3189     if (offset) {
3190         if (offset < 0) {
3191             offset += source_len + 1;
3192         }
3193         if (offset > source_len) {
3194             offset = source_len;
3195         }
3196         else if (offset < 0) {
3197             offset = 0;
3198         }
3199     }
3200 
3201 
3202     Jim_AppendString(interp, resultObj, source_str, offset);
3203 
3204 
3205     n = source_len - offset;
3206     p = source_str + offset;
3207     do {
3208         int match = regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
3209 
3210         if (match >= REG_BADPAT) {
3211             char buf[100];
3212 
3213             regerror(match, regex, buf, sizeof(buf));
3214             Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3215             return JIM_ERR;
3216         }
3217         if (match == REG_NOMATCH) {
3218             break;
3219         }
3220 
3221         num_matches++;
3222 
3223         Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
3224 
3225 
3226         for (j = 0; j < replace_len; j++) {
3227             int idx;
3228             int c = replace_str[j];
3229 
3230             if (c == '&') {
3231                 idx = 0;
3232             }
3233             else if (c == '\\' && j < replace_len) {
3234                 c = replace_str[++j];
3235                 if ((c >= '0') && (c <= '9')) {
3236                     idx = c - '0';
3237                 }
3238                 else if ((c == '\\') || (c == '&')) {
3239                     Jim_AppendString(interp, resultObj, replace_str + j, 1);
3240                     continue;
3241                 }
3242                 else {
3243                     Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2);
3244                     continue;
3245                 }
3246             }
3247             else {
3248                 Jim_AppendString(interp, resultObj, replace_str + j, 1);
3249                 continue;
3250             }
3251             if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
3252                 Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
3253                     pmatch[idx].rm_eo - pmatch[idx].rm_so);
3254             }
3255         }
3256 
3257         p += pmatch[0].rm_eo;
3258         n -= pmatch[0].rm_eo;
3259 
3260 
3261         if (!opt_all || n == 0) {
3262             break;
3263         }
3264 
3265 
3266         if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
3267             break;
3268         }
3269 
3270 
3271         if (pattern[0] == '\0' && n) {
3272 
3273             Jim_AppendString(interp, resultObj, p, 1);
3274             p++;
3275             n--;
3276         }
3277 
3278         regexec_flags |= REG_NOTBOL;
3279     } while (n);
3280 
3281     Jim_AppendString(interp, resultObj, p, -1);
3282 
3283 
3284     if (argc - i == 4) {
3285         result = Jim_SetVariable(interp, varname, resultObj);
3286 
3287         if (result == JIM_OK) {
3288             Jim_SetResultInt(interp, num_matches);
3289         }
3290         else {
3291             Jim_FreeObj(interp, resultObj);
3292         }
3293     }
3294     else {
3295         Jim_SetResult(interp, resultObj);
3296         result = JIM_OK;
3297     }
3298 
3299     return result;
3300 }
3301 
Jim_regexpInit(Jim_Interp * interp)3302 int Jim_regexpInit(Jim_Interp *interp)
3303 {
3304     if (Jim_PackageProvide(interp, "regexp", "1.0", JIM_ERRMSG))
3305         return JIM_ERR;
3306 
3307     Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
3308     Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
3309     return JIM_OK;
3310 }
3311 
3312 #include <limits.h>
3313 #include <stdlib.h>
3314 #include <string.h>
3315 #include <stdio.h>
3316 #include <errno.h>
3317 #include <sys/stat.h>
3318 
3319 
3320 #ifdef HAVE_UTIMES
3321 #include <sys/time.h>
3322 #endif
3323 #ifdef HAVE_UNISTD_H
3324 #include <unistd.h>
3325 #elif defined(_MSC_VER)
3326 #include <direct.h>
3327 #define F_OK 0
3328 #define W_OK 2
3329 #define R_OK 4
3330 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
3331 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
3332 #endif
3333 
3334 # ifndef MAXPATHLEN
3335 # define MAXPATHLEN JIM_PATH_LEN
3336 # endif
3337 
3338 #if defined(__MINGW32__) || defined(_MSC_VER)
3339 #define ISWINDOWS 1
3340 #else
3341 #define ISWINDOWS 0
3342 #endif
3343 
3344 
JimGetFileType(int mode)3345 static const char *JimGetFileType(int mode)
3346 {
3347     if (S_ISREG(mode)) {
3348         return "file";
3349     }
3350     else if (S_ISDIR(mode)) {
3351         return "directory";
3352     }
3353 #ifdef S_ISCHR
3354     else if (S_ISCHR(mode)) {
3355         return "characterSpecial";
3356     }
3357 #endif
3358 #ifdef S_ISBLK
3359     else if (S_ISBLK(mode)) {
3360         return "blockSpecial";
3361     }
3362 #endif
3363 #ifdef S_ISFIFO
3364     else if (S_ISFIFO(mode)) {
3365         return "fifo";
3366     }
3367 #endif
3368 #ifdef S_ISLNK
3369     else if (S_ISLNK(mode)) {
3370         return "link";
3371     }
3372 #endif
3373 #ifdef S_ISSOCK
3374     else if (S_ISSOCK(mode)) {
3375         return "socket";
3376     }
3377 #endif
3378     return "unknown";
3379 }
3380 
AppendStatElement(Jim_Interp * interp,Jim_Obj * listObj,const char * key,jim_wide value)3381 static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
3382 {
3383     Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
3384     Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
3385 }
3386 
StoreStatData(Jim_Interp * interp,Jim_Obj * varName,const struct stat * sb)3387 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
3388 {
3389 
3390     Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
3391 
3392     AppendStatElement(interp, listObj, "dev", sb->st_dev);
3393     AppendStatElement(interp, listObj, "ino", sb->st_ino);
3394     AppendStatElement(interp, listObj, "mode", sb->st_mode);
3395     AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
3396     AppendStatElement(interp, listObj, "uid", sb->st_uid);
3397     AppendStatElement(interp, listObj, "gid", sb->st_gid);
3398     AppendStatElement(interp, listObj, "size", sb->st_size);
3399     AppendStatElement(interp, listObj, "atime", sb->st_atime);
3400     AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
3401     AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
3402     Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
3403     Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));
3404 
3405 
3406     if (varName) {
3407         Jim_Obj *objPtr;
3408         objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
3409 
3410         if (objPtr) {
3411             Jim_Obj *objv[2];
3412 
3413             objv[0] = objPtr;
3414             objv[1] = listObj;
3415 
3416             objPtr = Jim_DictMerge(interp, 2, objv);
3417             if (objPtr == NULL) {
3418 
3419                 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
3420                 Jim_FreeNewObj(interp, listObj);
3421                 return JIM_ERR;
3422             }
3423 
3424             Jim_InvalidateStringRep(objPtr);
3425 
3426             Jim_FreeNewObj(interp, listObj);
3427             listObj = objPtr;
3428         }
3429         Jim_SetVariable(interp, varName, listObj);
3430     }
3431 
3432 
3433     Jim_SetResult(interp, listObj);
3434 
3435     return JIM_OK;
3436 }
3437 
file_cmd_dirname(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3438 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3439 {
3440     const char *path = Jim_String(argv[0]);
3441     const char *p = strrchr(path, '/');
3442 
3443     if (!p && path[0] == '.' && path[1] == '.' && path[2] == '\0') {
3444         Jim_SetResultString(interp, "..", -1);
3445     } else if (!p) {
3446         Jim_SetResultString(interp, ".", -1);
3447     }
3448     else if (p == path) {
3449         Jim_SetResultString(interp, "/", -1);
3450     }
3451     else if (ISWINDOWS && p[-1] == ':') {
3452 
3453         Jim_SetResultString(interp, path, p - path + 1);
3454     }
3455     else {
3456         Jim_SetResultString(interp, path, p - path);
3457     }
3458     return JIM_OK;
3459 }
3460 
file_cmd_rootname(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3461 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3462 {
3463     const char *path = Jim_String(argv[0]);
3464     const char *lastSlash = strrchr(path, '/');
3465     const char *p = strrchr(path, '.');
3466 
3467     if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
3468         Jim_SetResult(interp, argv[0]);
3469     }
3470     else {
3471         Jim_SetResultString(interp, path, p - path);
3472     }
3473     return JIM_OK;
3474 }
3475 
file_cmd_extension(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3476 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3477 {
3478     const char *path = Jim_String(argv[0]);
3479     const char *lastSlash = strrchr(path, '/');
3480     const char *p = strrchr(path, '.');
3481 
3482     if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
3483         p = "";
3484     }
3485     Jim_SetResultString(interp, p, -1);
3486     return JIM_OK;
3487 }
3488 
file_cmd_tail(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3489 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3490 {
3491     const char *path = Jim_String(argv[0]);
3492     const char *lastSlash = strrchr(path, '/');
3493 
3494     if (lastSlash) {
3495         Jim_SetResultString(interp, lastSlash + 1, -1);
3496     }
3497     else {
3498         Jim_SetResult(interp, argv[0]);
3499     }
3500     return JIM_OK;
3501 }
3502 
file_cmd_normalize(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3503 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3504 {
3505 #ifdef HAVE_REALPATH
3506     const char *path = Jim_String(argv[0]);
3507     char *newname = Jim_Alloc(MAXPATHLEN + 1);
3508 
3509     if (realpath(path, newname)) {
3510         Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
3511         return JIM_OK;
3512     }
3513     else {
3514         Jim_Free(newname);
3515         Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
3516         return JIM_ERR;
3517     }
3518 #else
3519     Jim_SetResultString(interp, "Not implemented", -1);
3520     return JIM_ERR;
3521 #endif
3522 }
3523 
file_cmd_join(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3524 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3525 {
3526     int i;
3527     char *newname = Jim_Alloc(MAXPATHLEN + 1);
3528     char *last = newname;
3529 
3530     *newname = 0;
3531 
3532 
3533     for (i = 0; i < argc; i++) {
3534         int len;
3535         const char *part = Jim_GetString(argv[i], &len);
3536 
3537         if (*part == '/') {
3538 
3539             last = newname;
3540         }
3541         else if (ISWINDOWS && strchr(part, ':')) {
3542 
3543             last = newname;
3544         }
3545         else if (part[0] == '.') {
3546             if (part[1] == '/') {
3547                 part += 2;
3548                 len -= 2;
3549             }
3550             else if (part[1] == 0 && last != newname) {
3551 
3552                 continue;
3553             }
3554         }
3555 
3556 
3557         if (last != newname && last[-1] != '/') {
3558             *last++ = '/';
3559         }
3560 
3561         if (len) {
3562             if (last + len - newname >= MAXPATHLEN) {
3563                 Jim_Free(newname);
3564                 Jim_SetResultString(interp, "Path too long", -1);
3565                 return JIM_ERR;
3566             }
3567             memcpy(last, part, len);
3568             last += len;
3569         }
3570 
3571 
3572         if (last > newname + 1 && last[-1] == '/') {
3573 
3574             if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
3575                 *--last = 0;
3576             }
3577         }
3578     }
3579 
3580     *last = 0;
3581 
3582 
3583 
3584     Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
3585 
3586     return JIM_OK;
3587 }
3588 
file_access(Jim_Interp * interp,Jim_Obj * filename,int mode)3589 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
3590 {
3591     Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1);
3592 
3593     return JIM_OK;
3594 }
3595 
file_cmd_readable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3596 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3597 {
3598     return file_access(interp, argv[0], R_OK);
3599 }
3600 
file_cmd_writable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3601 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3602 {
3603     return file_access(interp, argv[0], W_OK);
3604 }
3605 
file_cmd_executable(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3606 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3607 {
3608 #ifdef X_OK
3609     return file_access(interp, argv[0], X_OK);
3610 #else
3611 
3612     Jim_SetResultBool(interp, 1);
3613     return JIM_OK;
3614 #endif
3615 }
3616 
file_cmd_exists(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3617 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3618 {
3619     return file_access(interp, argv[0], F_OK);
3620 }
3621 
file_cmd_delete(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3622 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3623 {
3624     int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
3625 
3626     if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
3627         argc++;
3628         argv--;
3629     }
3630 
3631     while (argc--) {
3632         const char *path = Jim_String(argv[0]);
3633 
3634         if (unlink(path) == -1 && errno != ENOENT) {
3635             if (rmdir(path) == -1) {
3636 
3637                 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
3638                     Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
3639                         strerror(errno));
3640                     return JIM_ERR;
3641                 }
3642             }
3643         }
3644         argv++;
3645     }
3646     return JIM_OK;
3647 }
3648 
3649 #ifdef HAVE_MKDIR_ONE_ARG
3650 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
3651 #else
3652 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
3653 #endif
3654 
mkdir_all(char * path)3655 static int mkdir_all(char *path)
3656 {
3657     int ok = 1;
3658 
3659 
3660     goto first;
3661 
3662     while (ok--) {
3663 
3664         {
3665             char *slash = strrchr(path, '/');
3666 
3667             if (slash && slash != path) {
3668                 *slash = 0;
3669                 if (mkdir_all(path) != 0) {
3670                     return -1;
3671                 }
3672                 *slash = '/';
3673             }
3674         }
3675       first:
3676         if (MKDIR_DEFAULT(path) == 0) {
3677             return 0;
3678         }
3679         if (errno == ENOENT) {
3680 
3681             continue;
3682         }
3683 
3684         if (errno == EEXIST) {
3685             struct stat sb;
3686 
3687             if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
3688                 return 0;
3689             }
3690 
3691             errno = EEXIST;
3692         }
3693 
3694         break;
3695     }
3696     return -1;
3697 }
3698 
file_cmd_mkdir(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3699 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3700 {
3701     while (argc--) {
3702         char *path = Jim_StrDup(Jim_String(argv[0]));
3703         int rc = mkdir_all(path);
3704 
3705         Jim_Free(path);
3706         if (rc != 0) {
3707             Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
3708                 strerror(errno));
3709             return JIM_ERR;
3710         }
3711         argv++;
3712     }
3713     return JIM_OK;
3714 }
3715 
file_cmd_tempfile(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3716 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3717 {
3718     int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL);
3719 
3720     if (fd < 0) {
3721         return JIM_ERR;
3722     }
3723     close(fd);
3724 
3725     return JIM_OK;
3726 }
3727 
file_cmd_rename(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3728 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3729 {
3730     const char *source;
3731     const char *dest;
3732     int force = 0;
3733 
3734     if (argc == 3) {
3735         if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
3736             return -1;
3737         }
3738         force++;
3739         argv++;
3740         argc--;
3741     }
3742 
3743     source = Jim_String(argv[0]);
3744     dest = Jim_String(argv[1]);
3745 
3746     if (!force && access(dest, F_OK) == 0) {
3747         Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
3748             argv[1]);
3749         return JIM_ERR;
3750     }
3751 
3752     if (rename(source, dest) != 0) {
3753         Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3754             strerror(errno));
3755         return JIM_ERR;
3756     }
3757 
3758     return JIM_OK;
3759 }
3760 
3761 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
file_cmd_link(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3762 static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3763 {
3764     int ret;
3765     const char *source;
3766     const char *dest;
3767     static const char * const options[] = { "-hard", "-symbolic", NULL };
3768     enum { OPT_HARD, OPT_SYMBOLIC, };
3769     int option = OPT_HARD;
3770 
3771     if (argc == 3) {
3772         if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
3773             return JIM_ERR;
3774         }
3775         argv++;
3776         argc--;
3777     }
3778 
3779     dest = Jim_String(argv[0]);
3780     source = Jim_String(argv[1]);
3781 
3782     if (option == OPT_HARD) {
3783         ret = link(source, dest);
3784     }
3785     else {
3786         ret = symlink(source, dest);
3787     }
3788 
3789     if (ret != 0) {
3790         Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3791             strerror(errno));
3792         return JIM_ERR;
3793     }
3794 
3795     return JIM_OK;
3796 }
3797 #endif
3798 
file_stat(Jim_Interp * interp,Jim_Obj * filename,struct stat * sb)3799 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3800 {
3801     const char *path = Jim_String(filename);
3802 
3803     if (stat(path, sb) == -1) {
3804         Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3805         return JIM_ERR;
3806     }
3807     return JIM_OK;
3808 }
3809 
3810 #ifdef HAVE_LSTAT
file_lstat(Jim_Interp * interp,Jim_Obj * filename,struct stat * sb)3811 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3812 {
3813     const char *path = Jim_String(filename);
3814 
3815     if (lstat(path, sb) == -1) {
3816         Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3817         return JIM_ERR;
3818     }
3819     return JIM_OK;
3820 }
3821 #else
3822 #define file_lstat file_stat
3823 #endif
3824 
file_cmd_atime(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3825 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3826 {
3827     struct stat sb;
3828 
3829     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3830         return JIM_ERR;
3831     }
3832     Jim_SetResultInt(interp, sb.st_atime);
3833     return JIM_OK;
3834 }
3835 
file_cmd_mtime(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3836 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3837 {
3838     struct stat sb;
3839 
3840     if (argc == 2) {
3841 #ifdef HAVE_UTIMES
3842         jim_wide newtime;
3843         struct timeval times[2];
3844 
3845         if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) {
3846             return JIM_ERR;
3847         }
3848 
3849         times[1].tv_sec = times[0].tv_sec = newtime;
3850         times[1].tv_usec = times[0].tv_usec = 0;
3851 
3852         if (utimes(Jim_String(argv[0]), times) != 0) {
3853             Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno));
3854             return JIM_ERR;
3855         }
3856 #else
3857         Jim_SetResultString(interp, "Not implemented", -1);
3858         return JIM_ERR;
3859 #endif
3860     }
3861     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3862         return JIM_ERR;
3863     }
3864     Jim_SetResultInt(interp, sb.st_mtime);
3865     return JIM_OK;
3866 }
3867 
file_cmd_copy(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3868 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3869 {
3870     return Jim_EvalPrefix(interp, "file copy", argc, argv);
3871 }
3872 
file_cmd_size(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3873 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3874 {
3875     struct stat sb;
3876 
3877     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3878         return JIM_ERR;
3879     }
3880     Jim_SetResultInt(interp, sb.st_size);
3881     return JIM_OK;
3882 }
3883 
file_cmd_isdirectory(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3884 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3885 {
3886     struct stat sb;
3887     int ret = 0;
3888 
3889     if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3890         ret = S_ISDIR(sb.st_mode);
3891     }
3892     Jim_SetResultInt(interp, ret);
3893     return JIM_OK;
3894 }
3895 
file_cmd_isfile(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3896 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3897 {
3898     struct stat sb;
3899     int ret = 0;
3900 
3901     if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3902         ret = S_ISREG(sb.st_mode);
3903     }
3904     Jim_SetResultInt(interp, ret);
3905     return JIM_OK;
3906 }
3907 
3908 #ifdef HAVE_GETEUID
file_cmd_owned(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3909 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3910 {
3911     struct stat sb;
3912     int ret = 0;
3913 
3914     if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3915         ret = (geteuid() == sb.st_uid);
3916     }
3917     Jim_SetResultInt(interp, ret);
3918     return JIM_OK;
3919 }
3920 #endif
3921 
3922 #if defined(HAVE_READLINK)
file_cmd_readlink(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3923 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3924 {
3925     const char *path = Jim_String(argv[0]);
3926     char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
3927 
3928     int linkLength = readlink(path, linkValue, MAXPATHLEN);
3929 
3930     if (linkLength == -1) {
3931         Jim_Free(linkValue);
3932         Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
3933         return JIM_ERR;
3934     }
3935     linkValue[linkLength] = 0;
3936     Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
3937     return JIM_OK;
3938 }
3939 #endif
3940 
file_cmd_type(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3941 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3942 {
3943     struct stat sb;
3944 
3945     if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
3946         return JIM_ERR;
3947     }
3948     Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
3949     return JIM_OK;
3950 }
3951 
3952 #ifdef HAVE_LSTAT
file_cmd_lstat(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3953 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3954 {
3955     struct stat sb;
3956 
3957     if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
3958         return JIM_ERR;
3959     }
3960     return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
3961 }
3962 #else
3963 #define file_cmd_lstat file_cmd_stat
3964 #endif
3965 
file_cmd_stat(Jim_Interp * interp,int argc,Jim_Obj * const * argv)3966 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3967 {
3968     struct stat sb;
3969 
3970     if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3971         return JIM_ERR;
3972     }
3973     return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
3974 }
3975 
3976 static const jim_subcmd_type file_command_table[] = {
3977     {   "atime",
3978         "name",
3979         file_cmd_atime,
3980         1,
3981         1,
3982 
3983     },
3984     {   "mtime",
3985         "name ?time?",
3986         file_cmd_mtime,
3987         1,
3988         2,
3989 
3990     },
3991     {   "copy",
3992         "?-force? source dest",
3993         file_cmd_copy,
3994         2,
3995         3,
3996 
3997     },
3998     {   "dirname",
3999         "name",
4000         file_cmd_dirname,
4001         1,
4002         1,
4003 
4004     },
4005     {   "rootname",
4006         "name",
4007         file_cmd_rootname,
4008         1,
4009         1,
4010 
4011     },
4012     {   "extension",
4013         "name",
4014         file_cmd_extension,
4015         1,
4016         1,
4017 
4018     },
4019     {   "tail",
4020         "name",
4021         file_cmd_tail,
4022         1,
4023         1,
4024 
4025     },
4026     {   "normalize",
4027         "name",
4028         file_cmd_normalize,
4029         1,
4030         1,
4031 
4032     },
4033     {   "join",
4034         "name ?name ...?",
4035         file_cmd_join,
4036         1,
4037         -1,
4038 
4039     },
4040     {   "readable",
4041         "name",
4042         file_cmd_readable,
4043         1,
4044         1,
4045 
4046     },
4047     {   "writable",
4048         "name",
4049         file_cmd_writable,
4050         1,
4051         1,
4052 
4053     },
4054     {   "executable",
4055         "name",
4056         file_cmd_executable,
4057         1,
4058         1,
4059 
4060     },
4061     {   "exists",
4062         "name",
4063         file_cmd_exists,
4064         1,
4065         1,
4066 
4067     },
4068     {   "delete",
4069         "?-force|--? name ...",
4070         file_cmd_delete,
4071         1,
4072         -1,
4073 
4074     },
4075     {   "mkdir",
4076         "dir ...",
4077         file_cmd_mkdir,
4078         1,
4079         -1,
4080 
4081     },
4082     {   "tempfile",
4083         "?template?",
4084         file_cmd_tempfile,
4085         0,
4086         1,
4087 
4088     },
4089     {   "rename",
4090         "?-force? source dest",
4091         file_cmd_rename,
4092         2,
4093         3,
4094 
4095     },
4096 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
4097     {   "link",
4098         "?-symbolic|-hard? newname target",
4099         file_cmd_link,
4100         2,
4101         3,
4102 
4103     },
4104 #endif
4105 #if defined(HAVE_READLINK)
4106     {   "readlink",
4107         "name",
4108         file_cmd_readlink,
4109         1,
4110         1,
4111 
4112     },
4113 #endif
4114     {   "size",
4115         "name",
4116         file_cmd_size,
4117         1,
4118         1,
4119 
4120     },
4121     {   "stat",
4122         "name ?var?",
4123         file_cmd_stat,
4124         1,
4125         2,
4126 
4127     },
4128     {   "lstat",
4129         "name ?var?",
4130         file_cmd_lstat,
4131         1,
4132         2,
4133 
4134     },
4135     {   "type",
4136         "name",
4137         file_cmd_type,
4138         1,
4139         1,
4140 
4141     },
4142 #ifdef HAVE_GETEUID
4143     {   "owned",
4144         "name",
4145         file_cmd_owned,
4146         1,
4147         1,
4148 
4149     },
4150 #endif
4151     {   "isdirectory",
4152         "name",
4153         file_cmd_isdirectory,
4154         1,
4155         1,
4156 
4157     },
4158     {   "isfile",
4159         "name",
4160         file_cmd_isfile,
4161         1,
4162         1,
4163 
4164     },
4165     {
4166         NULL
4167     }
4168 };
4169 
Jim_CdCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4170 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4171 {
4172     const char *path;
4173 
4174     if (argc != 2) {
4175         Jim_WrongNumArgs(interp, 1, argv, "dirname");
4176         return JIM_ERR;
4177     }
4178 
4179     path = Jim_String(argv[1]);
4180 
4181     if (chdir(path) != 0) {
4182         Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
4183             strerror(errno));
4184         return JIM_ERR;
4185     }
4186     return JIM_OK;
4187 }
4188 
Jim_PwdCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4189 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4190 {
4191     char *cwd = Jim_Alloc(MAXPATHLEN);
4192 
4193     if (getcwd(cwd, MAXPATHLEN) == NULL) {
4194         Jim_SetResultString(interp, "Failed to get pwd", -1);
4195         Jim_Free(cwd);
4196         return JIM_ERR;
4197     }
4198     else if (ISWINDOWS) {
4199 
4200         char *p = cwd;
4201         while ((p = strchr(p, '\\')) != NULL) {
4202             *p++ = '/';
4203         }
4204     }
4205 
4206     Jim_SetResultString(interp, cwd, -1);
4207 
4208     Jim_Free(cwd);
4209     return JIM_OK;
4210 }
4211 
Jim_fileInit(Jim_Interp * interp)4212 int Jim_fileInit(Jim_Interp *interp)
4213 {
4214     if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
4215         return JIM_ERR;
4216 
4217     Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
4218     Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
4219     Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
4220     return JIM_OK;
4221 }
4222 
4223 #ifndef _GNU_SOURCE
4224 #define _GNU_SOURCE
4225 #endif
4226 #include <string.h>
4227 #include <ctype.h>
4228 
4229 
4230 #if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
Jim_ExecCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4231 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4232 {
4233     Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
4234     int i, j;
4235     int rc;
4236 
4237 
4238     for (i = 1; i < argc; i++) {
4239         int len;
4240         const char *arg = Jim_GetString(argv[i], &len);
4241 
4242         if (i > 1) {
4243             Jim_AppendString(interp, cmdlineObj, " ", 1);
4244         }
4245         if (strpbrk(arg, "\\\" ") == NULL) {
4246 
4247             Jim_AppendString(interp, cmdlineObj, arg, len);
4248             continue;
4249         }
4250 
4251         Jim_AppendString(interp, cmdlineObj, "\"", 1);
4252         for (j = 0; j < len; j++) {
4253             if (arg[j] == '\\' || arg[j] == '"') {
4254                 Jim_AppendString(interp, cmdlineObj, "\\", 1);
4255             }
4256             Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
4257         }
4258         Jim_AppendString(interp, cmdlineObj, "\"", 1);
4259     }
4260     rc = system(Jim_String(cmdlineObj));
4261 
4262     Jim_FreeNewObj(interp, cmdlineObj);
4263 
4264     if (rc) {
4265         Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
4266         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4267         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
4268         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
4269         Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
4270         return JIM_ERR;
4271     }
4272 
4273     return JIM_OK;
4274 }
4275 
Jim_execInit(Jim_Interp * interp)4276 int Jim_execInit(Jim_Interp *interp)
4277 {
4278     if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
4279         return JIM_ERR;
4280 
4281     Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
4282     return JIM_OK;
4283 }
4284 #else
4285 
4286 
4287 #include <errno.h>
4288 #include <signal.h>
4289 
4290 #if defined(__MINGW32__)
4291 
4292     #ifndef STRICT
4293     #define STRICT
4294     #endif
4295     #define WIN32_LEAN_AND_MEAN
4296     #include <windows.h>
4297     #include <fcntl.h>
4298 
4299     typedef HANDLE fdtype;
4300     typedef HANDLE pidtype;
4301     #define JIM_BAD_FD INVALID_HANDLE_VALUE
4302     #define JIM_BAD_PID INVALID_HANDLE_VALUE
4303     #define JimCloseFd CloseHandle
4304 
4305     #define WIFEXITED(STATUS) 1
4306     #define WEXITSTATUS(STATUS) (STATUS)
4307     #define WIFSIGNALED(STATUS) 0
4308     #define WTERMSIG(STATUS) 0
4309     #define WNOHANG 1
4310 
4311     static fdtype JimFileno(FILE *fh);
4312     static pidtype JimWaitPid(pidtype pid, int *status, int nohang);
4313     static fdtype JimDupFd(fdtype infd);
4314     static fdtype JimOpenForRead(const char *filename);
4315     static FILE *JimFdOpenForRead(fdtype fd);
4316     static int JimPipe(fdtype pipefd[2]);
4317     static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char **env,
4318         fdtype inputId, fdtype outputId, fdtype errorId);
4319     static int JimErrno(void);
4320 #else
4321     #include <unistd.h>
4322     #include <fcntl.h>
4323     #include <sys/wait.h>
4324     #include <sys/stat.h>
4325 
4326     typedef int fdtype;
4327     typedef int pidtype;
4328     #define JimPipe pipe
4329     #define JimErrno() errno
4330     #define JIM_BAD_FD -1
4331     #define JIM_BAD_PID -1
4332     #define JimFileno fileno
4333     #define JimReadFd read
4334     #define JimCloseFd close
4335     #define JimWaitPid waitpid
4336     #define JimDupFd dup
4337     #define JimFdOpenForRead(FD) fdopen((FD), "r")
4338     #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0)
4339 
4340     #ifndef HAVE_EXECVPE
4341         #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
4342     #endif
4343 #endif
4344 
4345 static const char *JimStrError(void);
4346 static char **JimOriginalEnviron(void);
4347 static char **JimSaveEnv(char **env);
4348 static void JimRestoreEnv(char **env);
4349 static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
4350     pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr);
4351 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr);
4352 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj);
4353 static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents, int len);
4354 static fdtype JimOpenForWrite(const char *filename, int append);
4355 static int JimRewindFd(fdtype fd);
4356 
Jim_SetResultErrno(Jim_Interp * interp,const char * msg)4357 static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
4358 {
4359     Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError());
4360 }
4361 
JimStrError(void)4362 static const char *JimStrError(void)
4363 {
4364     return strerror(JimErrno());
4365 }
4366 
Jim_RemoveTrailingNewline(Jim_Obj * objPtr)4367 static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
4368 {
4369     int len;
4370     const char *s = Jim_GetString(objPtr, &len);
4371 
4372     if (len > 0 && s[len - 1] == '\n') {
4373         objPtr->length--;
4374         objPtr->bytes[objPtr->length] = '\0';
4375     }
4376 }
4377 
JimAppendStreamToString(Jim_Interp * interp,fdtype fd,Jim_Obj * strObj)4378 static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj)
4379 {
4380     char buf[256];
4381     FILE *fh = JimFdOpenForRead(fd);
4382     int ret = 0;
4383 
4384     if (fh == NULL) {
4385         return -1;
4386     }
4387 
4388     while (1) {
4389         int retval = fread(buf, 1, sizeof(buf), fh);
4390         if (retval > 0) {
4391             ret = 1;
4392             Jim_AppendString(interp, strObj, buf, retval);
4393         }
4394         if (retval != sizeof(buf)) {
4395             break;
4396         }
4397     }
4398     fclose(fh);
4399     return ret;
4400 }
4401 
JimBuildEnv(Jim_Interp * interp)4402 static char **JimBuildEnv(Jim_Interp *interp)
4403 {
4404     int i;
4405     int size;
4406     int num;
4407     int n;
4408     char **envptr;
4409     char *envdata;
4410 
4411     Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
4412 
4413     if (!objPtr) {
4414         return JimOriginalEnviron();
4415     }
4416 
4417 
4418 
4419     num = Jim_ListLength(interp, objPtr);
4420     if (num % 2) {
4421 
4422         num--;
4423     }
4424     size = Jim_Length(objPtr) + 2;
4425 
4426     envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
4427     envdata = (char *)&envptr[num / 2 + 1];
4428 
4429     n = 0;
4430     for (i = 0; i < num; i += 2) {
4431         const char *s1, *s2;
4432         Jim_Obj *elemObj;
4433 
4434         Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
4435         s1 = Jim_String(elemObj);
4436         Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
4437         s2 = Jim_String(elemObj);
4438 
4439         envptr[n] = envdata;
4440         envdata += sprintf(envdata, "%s=%s", s1, s2);
4441         envdata++;
4442         n++;
4443     }
4444     envptr[n] = NULL;
4445     *envdata = 0;
4446 
4447     return envptr;
4448 }
4449 
JimFreeEnv(char ** env,char ** original_environ)4450 static void JimFreeEnv(char **env, char **original_environ)
4451 {
4452     if (env != original_environ) {
4453         Jim_Free(env);
4454     }
4455 }
4456 
4457 #ifndef jim_ext_signal
4458 
Jim_SignalId(int sig)4459 const char *Jim_SignalId(int sig)
4460 {
4461     static char buf[10];
4462     snprintf(buf, sizeof(buf), "%d", sig);
4463     return buf;
4464 }
4465 
Jim_SignalName(int sig)4466 const char *Jim_SignalName(int sig)
4467 {
4468     return Jim_SignalId(sig);
4469 }
4470 #endif
4471 
JimCheckWaitStatus(Jim_Interp * interp,pidtype pid,int waitStatus,Jim_Obj * errStrObj)4472 static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
4473 {
4474     Jim_Obj *errorCode;
4475 
4476     if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
4477         return JIM_OK;
4478     }
4479     errorCode = Jim_NewListObj(interp, NULL, 0);
4480 
4481     if (WIFEXITED(waitStatus)) {
4482         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4483         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4484         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
4485     }
4486     else {
4487         const char *type;
4488         const char *action;
4489 
4490         if (WIFSIGNALED(waitStatus)) {
4491             type = "CHILDKILLED";
4492             action = "killed";
4493         }
4494         else {
4495             type = "CHILDSUSP";
4496             action = "suspended";
4497         }
4498 
4499         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
4500 
4501         if (errStrObj) {
4502             Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
4503         }
4504 
4505         Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4506         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalId(WTERMSIG(waitStatus)), -1));
4507         Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1));
4508     }
4509     Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
4510 
4511     return JIM_ERR;
4512 }
4513 
4514 
4515 struct WaitInfo
4516 {
4517     pidtype pid;
4518     int status;
4519     int flags;
4520 };
4521 
4522 struct WaitInfoTable {
4523     struct WaitInfo *info;
4524     int size;
4525     int used;
4526 };
4527 
4528 
4529 #define WI_DETACHED 2
4530 
4531 #define WAIT_TABLE_GROW_BY 4
4532 
JimFreeWaitInfoTable(struct Jim_Interp * interp,void * privData)4533 static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
4534 {
4535     struct WaitInfoTable *table = privData;
4536 
4537     Jim_Free(table->info);
4538     Jim_Free(table);
4539 }
4540 
JimAllocWaitInfoTable(void)4541 static struct WaitInfoTable *JimAllocWaitInfoTable(void)
4542 {
4543     struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
4544     table->info = NULL;
4545     table->size = table->used = 0;
4546 
4547     return table;
4548 }
4549 
Jim_ExecCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)4550 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4551 {
4552     fdtype outputId;
4553     fdtype errorId;
4554     pidtype *pidPtr;
4555     int numPids, result;
4556     int child_siginfo = 1;
4557     Jim_Obj *childErrObj;
4558     Jim_Obj *errStrObj;
4559 
4560     if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
4561         Jim_Obj *listObj;
4562         int i;
4563 
4564         argc--;
4565         numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
4566         if (numPids < 0) {
4567             return JIM_ERR;
4568         }
4569 
4570         listObj = Jim_NewListObj(interp, NULL, 0);
4571         for (i = 0; i < numPids; i++) {
4572             Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i]));
4573         }
4574         Jim_SetResult(interp, listObj);
4575         JimDetachPids(interp, numPids, pidPtr);
4576         Jim_Free(pidPtr);
4577         return JIM_OK;
4578     }
4579 
4580     numPids =
4581         JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
4582 
4583     if (numPids < 0) {
4584         return JIM_ERR;
4585     }
4586 
4587     result = JIM_OK;
4588 
4589     errStrObj = Jim_NewStringObj(interp, "", 0);
4590 
4591 
4592     if (outputId != JIM_BAD_FD) {
4593         if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) {
4594             result = JIM_ERR;
4595             Jim_SetResultErrno(interp, "error reading from output pipe");
4596         }
4597     }
4598 
4599 
4600     childErrObj = Jim_NewStringObj(interp, "", 0);
4601     Jim_IncrRefCount(childErrObj);
4602 
4603     if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
4604         result = JIM_ERR;
4605     }
4606 
4607     if (errorId != JIM_BAD_FD) {
4608         int ret;
4609         JimRewindFd(errorId);
4610         ret = JimAppendStreamToString(interp, errorId, errStrObj);
4611         if (ret < 0) {
4612             Jim_SetResultErrno(interp, "error reading from error pipe");
4613             result = JIM_ERR;
4614         }
4615         else if (ret > 0) {
4616 
4617             child_siginfo = 0;
4618         }
4619     }
4620 
4621     if (child_siginfo) {
4622 
4623         Jim_AppendObj(interp, errStrObj, childErrObj);
4624     }
4625     Jim_DecrRefCount(interp, childErrObj);
4626 
4627 
4628     Jim_RemoveTrailingNewline(errStrObj);
4629 
4630 
4631     Jim_SetResult(interp, errStrObj);
4632 
4633     return result;
4634 }
4635 
JimReapDetachedPids(struct WaitInfoTable * table)4636 static void JimReapDetachedPids(struct WaitInfoTable *table)
4637 {
4638     struct WaitInfo *waitPtr;
4639     int count;
4640     int dest;
4641 
4642     if (!table) {
4643         return;
4644     }
4645 
4646     waitPtr = table->info;
4647     dest = 0;
4648     for (count = table->used; count > 0; waitPtr++, count--) {
4649         if (waitPtr->flags & WI_DETACHED) {
4650             int status;
4651             pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG);
4652             if (pid == waitPtr->pid) {
4653 
4654                 table->used--;
4655                 continue;
4656             }
4657         }
4658         if (waitPtr != &table->info[dest]) {
4659             table->info[dest] = *waitPtr;
4660         }
4661         dest++;
4662     }
4663 }
4664 
JimWaitForProcess(struct WaitInfoTable * table,pidtype pid,int * statusPtr)4665 static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
4666 {
4667     int i;
4668 
4669 
4670     for (i = 0; i < table->used; i++) {
4671         if (pid == table->info[i].pid) {
4672 
4673             JimWaitPid(pid, statusPtr, 0);
4674 
4675 
4676             if (i != table->used - 1) {
4677                 table->info[i] = table->info[table->used - 1];
4678             }
4679             table->used--;
4680             return pid;
4681         }
4682     }
4683 
4684 
4685     return JIM_BAD_PID;
4686 }
4687 
JimDetachPids(Jim_Interp * interp,int numPids,const pidtype * pidPtr)4688 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr)
4689 {
4690     int j;
4691     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4692 
4693     for (j = 0; j < numPids; j++) {
4694 
4695         int i;
4696         for (i = 0; i < table->used; i++) {
4697             if (pidPtr[j] == table->info[i].pid) {
4698                 table->info[i].flags |= WI_DETACHED;
4699                 break;
4700             }
4701         }
4702     }
4703 }
4704 
JimGetAioFilehandle(Jim_Interp * interp,const char * name)4705 static FILE *JimGetAioFilehandle(Jim_Interp *interp, const char *name)
4706 {
4707     FILE *fh;
4708     Jim_Obj *fhObj;
4709 
4710     fhObj = Jim_NewStringObj(interp, name, -1);
4711     Jim_IncrRefCount(fhObj);
4712     fh = Jim_AioFilehandle(interp, fhObj);
4713     Jim_DecrRefCount(interp, fhObj);
4714 
4715     return fh;
4716 }
4717 
4718 static int
JimCreatePipeline(Jim_Interp * interp,int argc,Jim_Obj * const * argv,pidtype ** pidArrayPtr,fdtype * inPipePtr,fdtype * outPipePtr,fdtype * errFilePtr)4719 JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr,
4720     fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr)
4721 {
4722     pidtype *pidPtr = NULL;         /* Points to malloc-ed array holding all
4723                                  * the pids of child processes. */
4724     int numPids = 0;            /* Actual number of processes that exist
4725                                  * at *pidPtr right now. */
4726     int cmdCount;               /* Count of number of distinct commands
4727                                  * found in argc/argv. */
4728     const char *input = NULL;   /* Describes input for pipeline, depending
4729                                  * on "inputFile".  NULL means take input
4730                                  * from stdin/pipe. */
4731     int input_len = 0;
4732 
4733 #define FILE_NAME   0
4734 #define FILE_APPEND 1
4735 #define FILE_HANDLE 2
4736 #define FILE_TEXT   3
4737 
4738     int inputFile = FILE_NAME;  /* 1 means input is name of input file.
4739                                  * 2 means input is filehandle name.
4740                                  * 0 means input holds actual
4741                                  * text to be input to command. */
4742 
4743     int outputFile = FILE_NAME; /* 0 means output is the name of output file.
4744                                  * 1 means output is the name of output file, and append.
4745                                  * 2 means output is filehandle name.
4746                                  * All this is ignored if output is NULL
4747                                  */
4748     int errorFile = FILE_NAME;  /* 0 means error is the name of error file.
4749                                  * 1 means error is the name of error file, and append.
4750                                  * 2 means error is filehandle name.
4751                                  * All this is ignored if error is NULL
4752                                  */
4753     const char *output = NULL;  /* Holds name of output file to pipe to,
4754                                  * or NULL if output goes to stdout/pipe. */
4755     const char *error = NULL;   /* Holds name of stderr file to pipe to,
4756                                  * or NULL if stderr goes to stderr/pipe. */
4757     fdtype inputId = JIM_BAD_FD;
4758     fdtype outputId = JIM_BAD_FD;
4759     fdtype errorId = JIM_BAD_FD;
4760     fdtype lastOutputId = JIM_BAD_FD;
4761     fdtype pipeIds[2];
4762     int firstArg, lastArg;      /* Indexes of first and last arguments in
4763                                  * current command. */
4764     int lastBar;
4765     int i;
4766     pidtype pid;
4767     char **save_environ;
4768     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4769 
4770 
4771     char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
4772     int arg_count = 0;
4773 
4774     JimReapDetachedPids(table);
4775 
4776     if (inPipePtr != NULL) {
4777         *inPipePtr = JIM_BAD_FD;
4778     }
4779     if (outPipePtr != NULL) {
4780         *outPipePtr = JIM_BAD_FD;
4781     }
4782     if (errFilePtr != NULL) {
4783         *errFilePtr = JIM_BAD_FD;
4784     }
4785     pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4786 
4787     cmdCount = 1;
4788     lastBar = -1;
4789     for (i = 0; i < argc; i++) {
4790         const char *arg = Jim_String(argv[i]);
4791 
4792         if (arg[0] == '<') {
4793             inputFile = FILE_NAME;
4794             input = arg + 1;
4795             if (*input == '<') {
4796                 inputFile = FILE_TEXT;
4797                 input_len = Jim_Length(argv[i]) - 2;
4798                 input++;
4799             }
4800             else if (*input == '@') {
4801                 inputFile = FILE_HANDLE;
4802                 input++;
4803             }
4804 
4805             if (!*input && ++i < argc) {
4806                 input = Jim_GetString(argv[i], &input_len);
4807             }
4808         }
4809         else if (arg[0] == '>') {
4810             int dup_error = 0;
4811 
4812             outputFile = FILE_NAME;
4813 
4814             output = arg + 1;
4815             if (*output == '>') {
4816                 outputFile = FILE_APPEND;
4817                 output++;
4818             }
4819             if (*output == '&') {
4820 
4821                 output++;
4822                 dup_error = 1;
4823             }
4824             if (*output == '@') {
4825                 outputFile = FILE_HANDLE;
4826                 output++;
4827             }
4828             if (!*output && ++i < argc) {
4829                 output = Jim_String(argv[i]);
4830             }
4831             if (dup_error) {
4832                 errorFile = outputFile;
4833                 error = output;
4834             }
4835         }
4836         else if (arg[0] == '2' && arg[1] == '>') {
4837             error = arg + 2;
4838             errorFile = FILE_NAME;
4839 
4840             if (*error == '@') {
4841                 errorFile = FILE_HANDLE;
4842                 error++;
4843             }
4844             else if (*error == '>') {
4845                 errorFile = FILE_APPEND;
4846                 error++;
4847             }
4848             if (!*error && ++i < argc) {
4849                 error = Jim_String(argv[i]);
4850             }
4851         }
4852         else {
4853             if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
4854                 if (i == lastBar + 1 || i == argc - 1) {
4855                     Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
4856                     goto badargs;
4857                 }
4858                 lastBar = i;
4859                 cmdCount++;
4860             }
4861 
4862             arg_array[arg_count++] = (char *)arg;
4863             continue;
4864         }
4865 
4866         if (i >= argc) {
4867             Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
4868             goto badargs;
4869         }
4870     }
4871 
4872     if (arg_count == 0) {
4873         Jim_SetResultString(interp, "didn't specify command to execute", -1);
4874 badargs:
4875         Jim_Free(arg_array);
4876         return -1;
4877     }
4878 
4879 
4880     save_environ = JimSaveEnv(JimBuildEnv(interp));
4881 
4882     if (input != NULL) {
4883         if (inputFile == FILE_TEXT) {
4884             inputId = JimCreateTemp(interp, input, input_len);
4885             if (inputId == JIM_BAD_FD) {
4886                 goto error;
4887             }
4888         }
4889         else if (inputFile == FILE_HANDLE) {
4890 
4891             FILE *fh = JimGetAioFilehandle(interp, input);
4892 
4893             if (fh == NULL) {
4894                 goto error;
4895             }
4896             inputId = JimDupFd(JimFileno(fh));
4897         }
4898         else {
4899             inputId = JimOpenForRead(input);
4900             if (inputId == JIM_BAD_FD) {
4901                 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError());
4902                 goto error;
4903             }
4904         }
4905     }
4906     else if (inPipePtr != NULL) {
4907         if (JimPipe(pipeIds) != 0) {
4908             Jim_SetResultErrno(interp, "couldn't create input pipe for command");
4909             goto error;
4910         }
4911         inputId = pipeIds[0];
4912         *inPipePtr = pipeIds[1];
4913         pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4914     }
4915 
4916     if (output != NULL) {
4917         if (outputFile == FILE_HANDLE) {
4918             FILE *fh = JimGetAioFilehandle(interp, output);
4919             if (fh == NULL) {
4920                 goto error;
4921             }
4922             fflush(fh);
4923             lastOutputId = JimDupFd(JimFileno(fh));
4924         }
4925         else {
4926             lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND);
4927             if (lastOutputId == JIM_BAD_FD) {
4928                 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError());
4929                 goto error;
4930             }
4931         }
4932     }
4933     else if (outPipePtr != NULL) {
4934         if (JimPipe(pipeIds) != 0) {
4935             Jim_SetResultErrno(interp, "couldn't create output pipe");
4936             goto error;
4937         }
4938         lastOutputId = pipeIds[1];
4939         *outPipePtr = pipeIds[0];
4940         pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4941     }
4942 
4943     if (error != NULL) {
4944         if (errorFile == FILE_HANDLE) {
4945             if (strcmp(error, "1") == 0) {
4946 
4947                 if (lastOutputId != JIM_BAD_FD) {
4948                     errorId = JimDupFd(lastOutputId);
4949                 }
4950                 else {
4951 
4952                     error = "stdout";
4953                 }
4954             }
4955             if (errorId == JIM_BAD_FD) {
4956                 FILE *fh = JimGetAioFilehandle(interp, error);
4957                 if (fh == NULL) {
4958                     goto error;
4959                 }
4960                 fflush(fh);
4961                 errorId = JimDupFd(JimFileno(fh));
4962             }
4963         }
4964         else {
4965             errorId = JimOpenForWrite(error, errorFile == FILE_APPEND);
4966             if (errorId == JIM_BAD_FD) {
4967                 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError());
4968                 goto error;
4969             }
4970         }
4971     }
4972     else if (errFilePtr != NULL) {
4973         errorId = JimCreateTemp(interp, NULL, 0);
4974         if (errorId == JIM_BAD_FD) {
4975             goto error;
4976         }
4977         *errFilePtr = JimDupFd(errorId);
4978     }
4979 
4980 
4981     pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
4982     for (i = 0; i < numPids; i++) {
4983         pidPtr[i] = JIM_BAD_PID;
4984     }
4985     for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
4986         int pipe_dup_err = 0;
4987         fdtype origErrorId = errorId;
4988 
4989         for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
4990             if (arg_array[lastArg][0] == '|') {
4991                 if (arg_array[lastArg][1] == '&') {
4992                     pipe_dup_err = 1;
4993                 }
4994                 break;
4995             }
4996         }
4997 
4998         arg_array[lastArg] = NULL;
4999         if (lastArg == arg_count) {
5000             outputId = lastOutputId;
5001         }
5002         else {
5003             if (JimPipe(pipeIds) != 0) {
5004                 Jim_SetResultErrno(interp, "couldn't create pipe");
5005                 goto error;
5006             }
5007             outputId = pipeIds[1];
5008         }
5009 
5010 
5011         if (pipe_dup_err) {
5012             errorId = outputId;
5013         }
5014 
5015 
5016 
5017 #ifdef __MINGW32__
5018         pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId);
5019         if (pid == JIM_BAD_PID) {
5020             Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
5021             goto error;
5022         }
5023 #else
5024         pid = vfork();
5025         if (pid < 0) {
5026             Jim_SetResultErrno(interp, "couldn't fork child process");
5027             goto error;
5028         }
5029         if (pid == 0) {
5030 
5031 
5032             if (inputId != -1) dup2(inputId, 0);
5033             if (outputId != -1) dup2(outputId, 1);
5034             if (errorId != -1) dup2(errorId, 2);
5035 
5036             for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) {
5037                 close(i);
5038             }
5039 
5040 
5041             (void)signal(SIGPIPE, SIG_DFL);
5042 
5043             execvpe(arg_array[firstArg], &arg_array[firstArg], Jim_GetEnviron());
5044 
5045 
5046             fprintf(stderr, "couldn't exec \"%s\"\n", arg_array[firstArg]);
5047 #ifdef JIM_MAINTAINER
5048             {
5049 
5050                 static char *const false_argv[2] = {"false", NULL};
5051                 execvp(false_argv[0],false_argv);
5052             }
5053 #endif
5054             _exit(127);
5055         }
5056 #endif
5057 
5058 
5059 
5060         if (table->used == table->size) {
5061             table->size += WAIT_TABLE_GROW_BY;
5062             table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
5063         }
5064 
5065         table->info[table->used].pid = pid;
5066         table->info[table->used].flags = 0;
5067         table->used++;
5068 
5069         pidPtr[numPids] = pid;
5070 
5071 
5072         errorId = origErrorId;
5073 
5074 
5075         if (inputId != JIM_BAD_FD) {
5076             JimCloseFd(inputId);
5077         }
5078         if (outputId != JIM_BAD_FD) {
5079             JimCloseFd(outputId);
5080             outputId = JIM_BAD_FD;
5081         }
5082         inputId = pipeIds[0];
5083         pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
5084     }
5085     *pidArrayPtr = pidPtr;
5086 
5087 
5088   cleanup:
5089     if (inputId != JIM_BAD_FD) {
5090         JimCloseFd(inputId);
5091     }
5092     if (lastOutputId != JIM_BAD_FD) {
5093         JimCloseFd(lastOutputId);
5094     }
5095     if (errorId != JIM_BAD_FD) {
5096         JimCloseFd(errorId);
5097     }
5098     Jim_Free(arg_array);
5099 
5100     JimRestoreEnv(save_environ);
5101 
5102     return numPids;
5103 
5104 
5105   error:
5106     if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) {
5107         JimCloseFd(*inPipePtr);
5108         *inPipePtr = JIM_BAD_FD;
5109     }
5110     if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) {
5111         JimCloseFd(*outPipePtr);
5112         *outPipePtr = JIM_BAD_FD;
5113     }
5114     if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) {
5115         JimCloseFd(*errFilePtr);
5116         *errFilePtr = JIM_BAD_FD;
5117     }
5118     if (pipeIds[0] != JIM_BAD_FD) {
5119         JimCloseFd(pipeIds[0]);
5120     }
5121     if (pipeIds[1] != JIM_BAD_FD) {
5122         JimCloseFd(pipeIds[1]);
5123     }
5124     if (pidPtr != NULL) {
5125         for (i = 0; i < numPids; i++) {
5126             if (pidPtr[i] != JIM_BAD_PID) {
5127                 JimDetachPids(interp, 1, &pidPtr[i]);
5128             }
5129         }
5130         Jim_Free(pidPtr);
5131     }
5132     numPids = -1;
5133     goto cleanup;
5134 }
5135 
5136 
JimCleanupChildren(Jim_Interp * interp,int numPids,pidtype * pidPtr,Jim_Obj * errStrObj)5137 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj)
5138 {
5139     struct WaitInfoTable *table = Jim_CmdPrivData(interp);
5140     int result = JIM_OK;
5141     int i;
5142 
5143 
5144     for (i = 0; i < numPids; i++) {
5145         int waitStatus = 0;
5146         if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) {
5147             if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus, errStrObj) != JIM_OK) {
5148                 result = JIM_ERR;
5149             }
5150         }
5151     }
5152     Jim_Free(pidPtr);
5153 
5154     return result;
5155 }
5156 
Jim_execInit(Jim_Interp * interp)5157 int Jim_execInit(Jim_Interp *interp)
5158 {
5159     if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
5160         return JIM_ERR;
5161 
5162 #ifdef SIGPIPE
5163     (void)signal(SIGPIPE, SIG_IGN);
5164 #endif
5165 
5166     Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable);
5167     return JIM_OK;
5168 }
5169 
5170 #if defined(__MINGW32__)
5171 
5172 
JimStdSecAttrs(void)5173 static SECURITY_ATTRIBUTES *JimStdSecAttrs(void)
5174 {
5175     static SECURITY_ATTRIBUTES secAtts;
5176 
5177     secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
5178     secAtts.lpSecurityDescriptor = NULL;
5179     secAtts.bInheritHandle = TRUE;
5180     return &secAtts;
5181 }
5182 
JimErrno(void)5183 static int JimErrno(void)
5184 {
5185     switch (GetLastError()) {
5186     case ERROR_FILE_NOT_FOUND: return ENOENT;
5187     case ERROR_PATH_NOT_FOUND: return ENOENT;
5188     case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
5189     case ERROR_ACCESS_DENIED: return EACCES;
5190     case ERROR_INVALID_HANDLE: return EBADF;
5191     case ERROR_BAD_ENVIRONMENT: return E2BIG;
5192     case ERROR_BAD_FORMAT: return ENOEXEC;
5193     case ERROR_INVALID_ACCESS: return EACCES;
5194     case ERROR_INVALID_DRIVE: return ENOENT;
5195     case ERROR_CURRENT_DIRECTORY: return EACCES;
5196     case ERROR_NOT_SAME_DEVICE: return EXDEV;
5197     case ERROR_NO_MORE_FILES: return ENOENT;
5198     case ERROR_WRITE_PROTECT: return EROFS;
5199     case ERROR_BAD_UNIT: return ENXIO;
5200     case ERROR_NOT_READY: return EBUSY;
5201     case ERROR_BAD_COMMAND: return EIO;
5202     case ERROR_CRC: return EIO;
5203     case ERROR_BAD_LENGTH: return EIO;
5204     case ERROR_SEEK: return EIO;
5205     case ERROR_WRITE_FAULT: return EIO;
5206     case ERROR_READ_FAULT: return EIO;
5207     case ERROR_GEN_FAILURE: return EIO;
5208     case ERROR_SHARING_VIOLATION: return EACCES;
5209     case ERROR_LOCK_VIOLATION: return EACCES;
5210     case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
5211     case ERROR_HANDLE_DISK_FULL: return ENOSPC;
5212     case ERROR_NOT_SUPPORTED: return ENODEV;
5213     case ERROR_REM_NOT_LIST: return EBUSY;
5214     case ERROR_DUP_NAME: return EEXIST;
5215     case ERROR_BAD_NETPATH: return ENOENT;
5216     case ERROR_NETWORK_BUSY: return EBUSY;
5217     case ERROR_DEV_NOT_EXIST: return ENODEV;
5218     case ERROR_TOO_MANY_CMDS: return EAGAIN;
5219     case ERROR_ADAP_HDW_ERR: return EIO;
5220     case ERROR_BAD_NET_RESP: return EIO;
5221     case ERROR_UNEXP_NET_ERR: return EIO;
5222     case ERROR_NETNAME_DELETED: return ENOENT;
5223     case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
5224     case ERROR_BAD_DEV_TYPE: return ENODEV;
5225     case ERROR_BAD_NET_NAME: return ENOENT;
5226     case ERROR_TOO_MANY_NAMES: return ENFILE;
5227     case ERROR_TOO_MANY_SESS: return EIO;
5228     case ERROR_SHARING_PAUSED: return EAGAIN;
5229     case ERROR_REDIR_PAUSED: return EAGAIN;
5230     case ERROR_FILE_EXISTS: return EEXIST;
5231     case ERROR_CANNOT_MAKE: return ENOSPC;
5232     case ERROR_OUT_OF_STRUCTURES: return ENFILE;
5233     case ERROR_ALREADY_ASSIGNED: return EEXIST;
5234     case ERROR_INVALID_PASSWORD: return EPERM;
5235     case ERROR_NET_WRITE_FAULT: return EIO;
5236     case ERROR_NO_PROC_SLOTS: return EAGAIN;
5237     case ERROR_DISK_CHANGE: return EXDEV;
5238     case ERROR_BROKEN_PIPE: return EPIPE;
5239     case ERROR_OPEN_FAILED: return ENOENT;
5240     case ERROR_DISK_FULL: return ENOSPC;
5241     case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
5242     case ERROR_INVALID_TARGET_HANDLE: return EBADF;
5243     case ERROR_INVALID_NAME: return ENOENT;
5244     case ERROR_PROC_NOT_FOUND: return ESRCH;
5245     case ERROR_WAIT_NO_CHILDREN: return ECHILD;
5246     case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
5247     case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
5248     case ERROR_SEEK_ON_DEVICE: return ESPIPE;
5249     case ERROR_BUSY_DRIVE: return EAGAIN;
5250     case ERROR_DIR_NOT_EMPTY: return EEXIST;
5251     case ERROR_NOT_LOCKED: return EACCES;
5252     case ERROR_BAD_PATHNAME: return ENOENT;
5253     case ERROR_LOCK_FAILED: return EACCES;
5254     case ERROR_ALREADY_EXISTS: return EEXIST;
5255     case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
5256     case ERROR_BAD_PIPE: return EPIPE;
5257     case ERROR_PIPE_BUSY: return EAGAIN;
5258     case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
5259     case ERROR_DIRECTORY: return ENOTDIR;
5260     }
5261     return EINVAL;
5262 }
5263 
JimPipe(fdtype pipefd[2])5264 static int JimPipe(fdtype pipefd[2])
5265 {
5266     if (CreatePipe(&pipefd[0], &pipefd[1], NULL, 0)) {
5267         return 0;
5268     }
5269     return -1;
5270 }
5271 
JimDupFd(fdtype infd)5272 static fdtype JimDupFd(fdtype infd)
5273 {
5274     fdtype dupfd;
5275     pidtype pid = GetCurrentProcess();
5276 
5277     if (DuplicateHandle(pid, infd, pid, &dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
5278         return dupfd;
5279     }
5280     return JIM_BAD_FD;
5281 }
5282 
JimRewindFd(fdtype fd)5283 static int JimRewindFd(fdtype fd)
5284 {
5285     return SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ? -1 : 0;
5286 }
5287 
5288 #if 0
5289 static int JimReadFd(fdtype fd, char *buffer, size_t len)
5290 {
5291     DWORD num;
5292 
5293     if (ReadFile(fd, buffer, len, &num, NULL)) {
5294         return num;
5295     }
5296     if (GetLastError() == ERROR_HANDLE_EOF || GetLastError() == ERROR_BROKEN_PIPE) {
5297         return 0;
5298     }
5299     return -1;
5300 }
5301 #endif
5302 
JimFdOpenForRead(fdtype fd)5303 static FILE *JimFdOpenForRead(fdtype fd)
5304 {
5305     return _fdopen(_open_osfhandle((int)fd, _O_RDONLY | _O_TEXT), "r");
5306 }
5307 
JimFileno(FILE * fh)5308 static fdtype JimFileno(FILE *fh)
5309 {
5310     return (fdtype)_get_osfhandle(_fileno(fh));
5311 }
5312 
JimOpenForRead(const char * filename)5313 static fdtype JimOpenForRead(const char *filename)
5314 {
5315     return CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
5316         JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
5317 }
5318 
JimOpenForWrite(const char * filename,int append)5319 static fdtype JimOpenForWrite(const char *filename, int append)
5320 {
5321     fdtype fd = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
5322         JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
5323     if (append && fd != JIM_BAD_FD) {
5324         SetFilePointer(fd, 0, NULL, FILE_END);
5325     }
5326     return fd;
5327 }
5328 
JimFdOpenForWrite(fdtype fd)5329 static FILE *JimFdOpenForWrite(fdtype fd)
5330 {
5331     return _fdopen(_open_osfhandle((int)fd, _O_TEXT), "w");
5332 }
5333 
JimWaitPid(pidtype pid,int * status,int nohang)5334 static pidtype JimWaitPid(pidtype pid, int *status, int nohang)
5335 {
5336     DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
5337     if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
5338 
5339         return JIM_BAD_PID;
5340     }
5341     GetExitCodeProcess(pid, &ret);
5342     *status = ret;
5343     CloseHandle(pid);
5344     return pid;
5345 }
5346 
JimCreateTemp(Jim_Interp * interp,const char * contents,int len)5347 static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
5348 {
5349     char name[MAX_PATH];
5350     HANDLE handle;
5351 
5352     if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) {
5353         return JIM_BAD_FD;
5354     }
5355 
5356     handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, JimStdSecAttrs(),
5357             CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
5358             NULL);
5359 
5360     if (handle == INVALID_HANDLE_VALUE) {
5361         goto error;
5362     }
5363 
5364     if (contents != NULL) {
5365 
5366         FILE *fh = JimFdOpenForWrite(JimDupFd(handle));
5367         if (fh == NULL) {
5368             goto error;
5369         }
5370 
5371         if (fwrite(contents, len, 1, fh) != 1) {
5372             fclose(fh);
5373             goto error;
5374         }
5375         fseek(fh, 0, SEEK_SET);
5376         fclose(fh);
5377     }
5378     return handle;
5379 
5380   error:
5381     Jim_SetResultErrno(interp, "failed to create temp file");
5382     CloseHandle(handle);
5383     DeleteFile(name);
5384     return JIM_BAD_FD;
5385 }
5386 
5387 static int
JimWinFindExecutable(const char * originalName,char fullPath[MAX_PATH])5388 JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
5389 {
5390     int i;
5391     static char extensions[][5] = {".exe", "", ".bat"};
5392 
5393     for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
5394         snprintf(fullPath, MAX_PATH, "%s%s", originalName, extensions[i]);
5395 
5396         if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
5397             continue;
5398         }
5399         if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
5400             continue;
5401         }
5402         return 0;
5403     }
5404 
5405     return -1;
5406 }
5407 
JimSaveEnv(char ** env)5408 static char **JimSaveEnv(char **env)
5409 {
5410     return env;
5411 }
5412 
JimRestoreEnv(char ** env)5413 static void JimRestoreEnv(char **env)
5414 {
5415     JimFreeEnv(env, Jim_GetEnviron());
5416 }
5417 
JimOriginalEnviron(void)5418 static char **JimOriginalEnviron(void)
5419 {
5420     return NULL;
5421 }
5422 
5423 static Jim_Obj *
JimWinBuildCommandLine(Jim_Interp * interp,char ** argv)5424 JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
5425 {
5426     char *start, *special;
5427     int quote, i;
5428 
5429     Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
5430 
5431     for (i = 0; argv[i]; i++) {
5432         if (i > 0) {
5433             Jim_AppendString(interp, strObj, " ", 1);
5434         }
5435 
5436         if (argv[i][0] == '\0') {
5437             quote = 1;
5438         }
5439         else {
5440             quote = 0;
5441             for (start = argv[i]; *start != '\0'; start++) {
5442                 if (isspace(UCHAR(*start))) {
5443                     quote = 1;
5444                     break;
5445                 }
5446             }
5447         }
5448         if (quote) {
5449             Jim_AppendString(interp, strObj, "\"" , 1);
5450         }
5451 
5452         start = argv[i];
5453         for (special = argv[i]; ; ) {
5454             if ((*special == '\\') && (special[1] == '\\' ||
5455                     special[1] == '"' || (quote && special[1] == '\0'))) {
5456                 Jim_AppendString(interp, strObj, start, special - start);
5457                 start = special;
5458                 while (1) {
5459                     special++;
5460                     if (*special == '"' || (quote && *special == '\0')) {
5461 
5462                         Jim_AppendString(interp, strObj, start, special - start);
5463                         break;
5464                     }
5465                     if (*special != '\\') {
5466                         break;
5467                     }
5468                 }
5469                 Jim_AppendString(interp, strObj, start, special - start);
5470                 start = special;
5471             }
5472             if (*special == '"') {
5473         if (special == start) {
5474             Jim_AppendString(interp, strObj, "\"", 1);
5475         }
5476         else {
5477             Jim_AppendString(interp, strObj, start, special - start);
5478         }
5479                 Jim_AppendString(interp, strObj, "\\\"", 2);
5480                 start = special + 1;
5481             }
5482             if (*special == '\0') {
5483                 break;
5484             }
5485             special++;
5486         }
5487         Jim_AppendString(interp, strObj, start, special - start);
5488         if (quote) {
5489             Jim_AppendString(interp, strObj, "\"", 1);
5490         }
5491     }
5492     return strObj;
5493 }
5494 
5495 static pidtype
JimStartWinProcess(Jim_Interp * interp,char ** argv,char ** env,fdtype inputId,fdtype outputId,fdtype errorId)5496 JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, fdtype inputId, fdtype outputId, fdtype errorId)
5497 {
5498     STARTUPINFO startInfo;
5499     PROCESS_INFORMATION procInfo;
5500     HANDLE hProcess, h;
5501     char execPath[MAX_PATH];
5502     pidtype pid = JIM_BAD_PID;
5503     Jim_Obj *cmdLineObj;
5504     char *winenv;
5505 
5506     if (JimWinFindExecutable(argv[0], execPath) < 0) {
5507         return JIM_BAD_PID;
5508     }
5509     argv[0] = execPath;
5510 
5511     hProcess = GetCurrentProcess();
5512     cmdLineObj = JimWinBuildCommandLine(interp, argv);
5513 
5514 
5515     ZeroMemory(&startInfo, sizeof(startInfo));
5516     startInfo.cb = sizeof(startInfo);
5517     startInfo.dwFlags   = STARTF_USESTDHANDLES;
5518     startInfo.hStdInput = INVALID_HANDLE_VALUE;
5519     startInfo.hStdOutput= INVALID_HANDLE_VALUE;
5520     startInfo.hStdError = INVALID_HANDLE_VALUE;
5521 
5522     if (inputId == JIM_BAD_FD) {
5523         if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) {
5524             CloseHandle(h);
5525         }
5526     } else {
5527         DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput,
5528                 0, TRUE, DUPLICATE_SAME_ACCESS);
5529     }
5530     if (startInfo.hStdInput == JIM_BAD_FD) {
5531         goto end;
5532     }
5533 
5534     if (outputId == JIM_BAD_FD) {
5535         startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
5536                 JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
5537     } else {
5538         DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput,
5539                 0, TRUE, DUPLICATE_SAME_ACCESS);
5540     }
5541     if (startInfo.hStdOutput == JIM_BAD_FD) {
5542         goto end;
5543     }
5544 
5545     if (errorId == JIM_BAD_FD) {
5546 
5547         startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
5548                 JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
5549     } else {
5550         DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError,
5551                 0, TRUE, DUPLICATE_SAME_ACCESS);
5552     }
5553     if (startInfo.hStdError == JIM_BAD_FD) {
5554         goto end;
5555     }
5556 
5557     if (env == NULL) {
5558 
5559         winenv = NULL;
5560     }
5561     else if (env[0] == NULL) {
5562         winenv = (char *)"\0";
5563     }
5564     else {
5565         winenv = env[0];
5566     }
5567 
5568     if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
5569             0, winenv, NULL, &startInfo, &procInfo)) {
5570         goto end;
5571     }
5572 
5573 
5574     WaitForInputIdle(procInfo.hProcess, 5000);
5575     CloseHandle(procInfo.hThread);
5576 
5577     pid = procInfo.hProcess;
5578 
5579     end:
5580     Jim_FreeNewObj(interp, cmdLineObj);
5581     if (startInfo.hStdInput != JIM_BAD_FD) {
5582         CloseHandle(startInfo.hStdInput);
5583     }
5584     if (startInfo.hStdOutput != JIM_BAD_FD) {
5585         CloseHandle(startInfo.hStdOutput);
5586     }
5587     if (startInfo.hStdError != JIM_BAD_FD) {
5588         CloseHandle(startInfo.hStdError);
5589     }
5590     return pid;
5591 }
5592 #else
5593 
JimOpenForWrite(const char * filename,int append)5594 static int JimOpenForWrite(const char *filename, int append)
5595 {
5596     return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
5597 }
5598 
JimRewindFd(int fd)5599 static int JimRewindFd(int fd)
5600 {
5601     return lseek(fd, 0L, SEEK_SET);
5602 }
5603 
JimCreateTemp(Jim_Interp * interp,const char * contents,int len)5604 static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
5605 {
5606     int fd = Jim_MakeTempFile(interp, NULL);
5607 
5608     if (fd != JIM_BAD_FD) {
5609         unlink(Jim_String(Jim_GetResult(interp)));
5610         if (contents) {
5611             if (write(fd, contents, len) != len) {
5612                 Jim_SetResultErrno(interp, "couldn't write temp file");
5613                 close(fd);
5614                 return -1;
5615             }
5616             lseek(fd, 0L, SEEK_SET);
5617         }
5618     }
5619     return fd;
5620 }
5621 
JimOriginalEnviron(void)5622 static char **JimOriginalEnviron(void)
5623 {
5624     return Jim_GetEnviron();
5625 }
5626 
JimSaveEnv(char ** env)5627 static char **JimSaveEnv(char **env)
5628 {
5629     char **saveenv = Jim_GetEnviron();
5630     Jim_SetEnviron(env);
5631     return saveenv;
5632 }
5633 
JimRestoreEnv(char ** env)5634 static void JimRestoreEnv(char **env)
5635 {
5636     JimFreeEnv(Jim_GetEnviron(), env);
5637     Jim_SetEnviron(env);
5638 }
5639 #endif
5640 #endif
5641 
5642 
5643 #ifndef _XOPEN_SOURCE
5644 #define _XOPEN_SOURCE 500
5645 #endif
5646 
5647 #include <stdlib.h>
5648 #include <string.h>
5649 #include <stdio.h>
5650 #include <time.h>
5651 
5652 
5653 #ifdef HAVE_SYS_TIME_H
5654 #include <sys/time.h>
5655 #endif
5656 
clock_cmd_format(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5657 static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5658 {
5659 
5660     char buf[100];
5661     time_t t;
5662     long seconds;
5663 
5664     const char *format = "%a %b %d %H:%M:%S %Z %Y";
5665 
5666     if (argc == 2 || (argc == 3 && !Jim_CompareStringImmediate(interp, argv[1], "-format"))) {
5667         return -1;
5668     }
5669 
5670     if (argc == 3) {
5671         format = Jim_String(argv[2]);
5672     }
5673 
5674     if (Jim_GetLong(interp, argv[0], &seconds) != JIM_OK) {
5675         return JIM_ERR;
5676     }
5677     t = seconds;
5678 
5679     if (strftime(buf, sizeof(buf), format, localtime(&t)) == 0) {
5680         Jim_SetResultString(interp, "format string too long", -1);
5681         return JIM_ERR;
5682     }
5683 
5684     Jim_SetResultString(interp, buf, -1);
5685 
5686     return JIM_OK;
5687 }
5688 
5689 #ifdef HAVE_STRPTIME
clock_cmd_scan(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5690 static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5691 {
5692     char *pt;
5693     struct tm tm;
5694     time_t now = time(0);
5695 
5696     if (!Jim_CompareStringImmediate(interp, argv[1], "-format")) {
5697         return -1;
5698     }
5699 
5700 
5701     localtime_r(&now, &tm);
5702 
5703     pt = strptime(Jim_String(argv[0]), Jim_String(argv[2]), &tm);
5704     if (pt == 0 || *pt != 0) {
5705         Jim_SetResultString(interp, "Failed to parse time according to format", -1);
5706         return JIM_ERR;
5707     }
5708 
5709 
5710     Jim_SetResultInt(interp, mktime(&tm));
5711 
5712     return JIM_OK;
5713 }
5714 #endif
5715 
clock_cmd_seconds(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5716 static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5717 {
5718     Jim_SetResultInt(interp, time(NULL));
5719 
5720     return JIM_OK;
5721 }
5722 
clock_cmd_micros(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5723 static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5724 {
5725     struct timeval tv;
5726 
5727     gettimeofday(&tv, NULL);
5728 
5729     Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec);
5730 
5731     return JIM_OK;
5732 }
5733 
clock_cmd_millis(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5734 static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5735 {
5736     struct timeval tv;
5737 
5738     gettimeofday(&tv, NULL);
5739 
5740     Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000 + tv.tv_usec / 1000);
5741 
5742     return JIM_OK;
5743 }
5744 
5745 static const jim_subcmd_type clock_command_table[] = {
5746     {   "seconds",
5747         NULL,
5748         clock_cmd_seconds,
5749         0,
5750         0,
5751 
5752     },
5753     {   "clicks",
5754         NULL,
5755         clock_cmd_micros,
5756         0,
5757         0,
5758 
5759     },
5760     {   "microseconds",
5761         NULL,
5762         clock_cmd_micros,
5763         0,
5764         0,
5765 
5766     },
5767     {   "milliseconds",
5768         NULL,
5769         clock_cmd_millis,
5770         0,
5771         0,
5772 
5773     },
5774     {   "format",
5775         "seconds ?-format format?",
5776         clock_cmd_format,
5777         1,
5778         3,
5779 
5780     },
5781 #ifdef HAVE_STRPTIME
5782     {   "scan",
5783         "str -format format",
5784         clock_cmd_scan,
5785         3,
5786         3,
5787 
5788     },
5789 #endif
5790     { NULL }
5791 };
5792 
Jim_clockInit(Jim_Interp * interp)5793 int Jim_clockInit(Jim_Interp *interp)
5794 {
5795     if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG))
5796         return JIM_ERR;
5797 
5798     Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
5799     return JIM_OK;
5800 }
5801 
5802 #include <limits.h>
5803 #include <stdlib.h>
5804 #include <string.h>
5805 #include <stdio.h>
5806 #include <errno.h>
5807 
5808 
array_cmd_exists(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5809 static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5810 {
5811 
5812     Jim_Obj *dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
5813     Jim_SetResultInt(interp, dictObj && Jim_DictSize(interp, dictObj) != -1);
5814     return JIM_OK;
5815 }
5816 
array_cmd_get(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5817 static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5818 {
5819     Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5820     Jim_Obj *patternObj;
5821 
5822     if (!objPtr) {
5823         return JIM_OK;
5824     }
5825 
5826     patternObj = (argc == 1) ? NULL : argv[1];
5827 
5828 
5829     if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) {
5830         if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) {
5831 
5832             Jim_SetResult(interp, objPtr);
5833             return JIM_OK;
5834         }
5835     }
5836 
5837     return Jim_DictMatchTypes(interp, objPtr, patternObj, JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS | JIM_DICTMATCH_VALUES);
5838 }
5839 
array_cmd_names(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5840 static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5841 {
5842     Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5843 
5844     if (!objPtr) {
5845         return JIM_OK;
5846     }
5847 
5848     return Jim_DictMatchTypes(interp, objPtr, argc == 1 ? NULL : argv[1], JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS);
5849 }
5850 
array_cmd_unset(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5851 static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5852 {
5853     int i;
5854     int len;
5855     Jim_Obj *resultObj;
5856     Jim_Obj *objPtr;
5857     Jim_Obj **dictValuesObj;
5858 
5859     if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
5860 
5861         Jim_UnsetVariable(interp, argv[0], JIM_NONE);
5862         return JIM_OK;
5863     }
5864 
5865     objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5866 
5867     if (objPtr == NULL) {
5868 
5869         return JIM_OK;
5870     }
5871 
5872     if (Jim_DictPairs(interp, objPtr, &dictValuesObj, &len) != JIM_OK) {
5873 
5874         Jim_SetResultString(interp, "", -1);
5875         return JIM_OK;
5876     }
5877 
5878 
5879     resultObj = Jim_NewDictObj(interp, NULL, 0);
5880 
5881     for (i = 0; i < len; i += 2) {
5882         if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
5883             Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
5884         }
5885     }
5886     Jim_Free(dictValuesObj);
5887 
5888     Jim_SetVariable(interp, argv[0], resultObj);
5889     return JIM_OK;
5890 }
5891 
array_cmd_size(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5892 static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5893 {
5894     Jim_Obj *objPtr;
5895     int len = 0;
5896 
5897 
5898     objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5899     if (objPtr) {
5900         len = Jim_DictSize(interp, objPtr);
5901         if (len < 0) {
5902 
5903             Jim_SetResultInt(interp, 0);
5904             return JIM_OK;
5905         }
5906     }
5907 
5908     Jim_SetResultInt(interp, len);
5909 
5910     return JIM_OK;
5911 }
5912 
array_cmd_stat(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5913 static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5914 {
5915     Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5916     if (objPtr) {
5917         return Jim_DictInfo(interp, objPtr);
5918     }
5919     Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL);
5920     return JIM_ERR;
5921 }
5922 
array_cmd_set(Jim_Interp * interp,int argc,Jim_Obj * const * argv)5923 static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5924 {
5925     int i;
5926     int len;
5927     Jim_Obj *listObj = argv[1];
5928     Jim_Obj *dictObj;
5929 
5930     len = Jim_ListLength(interp, listObj);
5931     if (len % 2) {
5932         Jim_SetResultString(interp, "list must have an even number of elements", -1);
5933         return JIM_ERR;
5934     }
5935 
5936     dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
5937     if (!dictObj) {
5938 
5939         return Jim_SetVariable(interp, argv[0], listObj);
5940     }
5941     else if (Jim_DictSize(interp, dictObj) < 0) {
5942         return JIM_ERR;
5943     }
5944 
5945     if (Jim_IsShared(dictObj)) {
5946         dictObj = Jim_DuplicateObj(interp, dictObj);
5947     }
5948 
5949     for (i = 0; i < len; i += 2) {
5950         Jim_Obj *nameObj;
5951         Jim_Obj *valueObj;
5952 
5953         Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE);
5954         Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE);
5955 
5956         Jim_DictAddElement(interp, dictObj, nameObj, valueObj);
5957     }
5958     return Jim_SetVariable(interp, argv[0], dictObj);
5959 }
5960 
5961 static const jim_subcmd_type array_command_table[] = {
5962         {       "exists",
5963                 "arrayName",
5964                 array_cmd_exists,
5965                 1,
5966                 1,
5967 
5968         },
5969         {       "get",
5970                 "arrayName ?pattern?",
5971                 array_cmd_get,
5972                 1,
5973                 2,
5974 
5975         },
5976         {       "names",
5977                 "arrayName ?pattern?",
5978                 array_cmd_names,
5979                 1,
5980                 2,
5981 
5982         },
5983         {       "set",
5984                 "arrayName list",
5985                 array_cmd_set,
5986                 2,
5987                 2,
5988 
5989         },
5990         {       "size",
5991                 "arrayName",
5992                 array_cmd_size,
5993                 1,
5994                 1,
5995 
5996         },
5997         {       "stat",
5998                 "arrayName",
5999                 array_cmd_stat,
6000                 1,
6001                 1,
6002 
6003         },
6004         {       "unset",
6005                 "arrayName ?pattern?",
6006                 array_cmd_unset,
6007                 1,
6008                 2,
6009 
6010         },
6011         {       NULL
6012         }
6013 };
6014 
Jim_arrayInit(Jim_Interp * interp)6015 int Jim_arrayInit(Jim_Interp *interp)
6016 {
6017     if (Jim_PackageProvide(interp, "array", "1.0", JIM_ERRMSG))
6018         return JIM_ERR;
6019 
6020     Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
6021     return JIM_OK;
6022 }
Jim_InitStaticExtensions(Jim_Interp * interp)6023 int Jim_InitStaticExtensions(Jim_Interp *interp)
6024 {
6025 extern int Jim_bootstrapInit(Jim_Interp *);
6026 extern int Jim_aioInit(Jim_Interp *);
6027 extern int Jim_readdirInit(Jim_Interp *);
6028 extern int Jim_regexpInit(Jim_Interp *);
6029 extern int Jim_fileInit(Jim_Interp *);
6030 extern int Jim_globInit(Jim_Interp *);
6031 extern int Jim_execInit(Jim_Interp *);
6032 extern int Jim_clockInit(Jim_Interp *);
6033 extern int Jim_arrayInit(Jim_Interp *);
6034 extern int Jim_stdlibInit(Jim_Interp *);
6035 extern int Jim_tclcompatInit(Jim_Interp *);
6036 Jim_bootstrapInit(interp);
6037 Jim_aioInit(interp);
6038 Jim_readdirInit(interp);
6039 Jim_regexpInit(interp);
6040 Jim_fileInit(interp);
6041 Jim_globInit(interp);
6042 Jim_execInit(interp);
6043 Jim_clockInit(interp);
6044 Jim_arrayInit(interp);
6045 Jim_stdlibInit(interp);
6046 Jim_tclcompatInit(interp);
6047 return JIM_OK;
6048 }
6049 #define JIM_OPTIMIZATION
6050 #ifndef _GNU_SOURCE
6051 #define _GNU_SOURCE
6052 #endif
6053 
6054 #include <stdio.h>
6055 #include <stdlib.h>
6056 
6057 #include <string.h>
6058 #include <stdarg.h>
6059 #include <ctype.h>
6060 #include <limits.h>
6061 #include <assert.h>
6062 #include <errno.h>
6063 #include <time.h>
6064 #include <setjmp.h>
6065 
6066 
6067 #ifdef HAVE_SYS_TIME_H
6068 #include <sys/time.h>
6069 #endif
6070 #ifdef HAVE_BACKTRACE
6071 #include <execinfo.h>
6072 #endif
6073 #ifdef HAVE_CRT_EXTERNS_H
6074 #include <crt_externs.h>
6075 #endif
6076 
6077 
6078 #include <math.h>
6079 
6080 
6081 
6082 
6083 
6084 #ifndef TCL_LIBRARY
6085 #define TCL_LIBRARY "."
6086 #endif
6087 #ifndef TCL_PLATFORM_OS
6088 #define TCL_PLATFORM_OS "unknown"
6089 #endif
6090 #ifndef TCL_PLATFORM_PLATFORM
6091 #define TCL_PLATFORM_PLATFORM "unknown"
6092 #endif
6093 #ifndef TCL_PLATFORM_PATH_SEPARATOR
6094 #define TCL_PLATFORM_PATH_SEPARATOR ":"
6095 #endif
6096 
6097 
6098 
6099 
6100 
6101 
6102 
6103 #ifdef JIM_MAINTAINER
6104 #define JIM_DEBUG_COMMAND
6105 #define JIM_DEBUG_PANIC
6106 #endif
6107 
6108 
6109 
6110 #define JIM_INTEGER_SPACE 24
6111 
6112 const char *jim_tt_name(int type);
6113 
6114 #ifdef JIM_DEBUG_PANIC
6115 static void JimPanicDump(int fail_condition, const char *fmt, ...);
6116 #define JimPanic(X) JimPanicDump X
6117 #else
6118 #define JimPanic(X)
6119 #endif
6120 
6121 #ifdef JIM_OPTIMIZATION
6122 #define JIM_IF_OPTIM(X) X
6123 #else
6124 #define JIM_IF_OPTIM(X)
6125 #endif
6126 
6127 
6128 static char JimEmptyStringRep[] = "";
6129 
6130 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
6131 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
6132     int flags);
6133 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
6134 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
6135 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
6136 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len);
6137 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
6138     const char *prefix, const char *const *tablePtr, const char *name);
6139 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
6140 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
6141 static int JimSign(jim_wide w);
6142 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr);
6143 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
6144 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
6145 
6146 
6147 
6148 #define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
6149 
6150 #define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none")
6151 
utf8_tounicode_case(const char * s,int * uc,int upper)6152 static int utf8_tounicode_case(const char *s, int *uc, int upper)
6153 {
6154     int l = utf8_tounicode(s, uc);
6155     if (upper) {
6156         *uc = utf8_upper(*uc);
6157     }
6158     return l;
6159 }
6160 
6161 
6162 #define JIM_CHARSET_SCAN 2
6163 #define JIM_CHARSET_GLOB 0
6164 
JimCharsetMatch(const char * pattern,int c,int flags)6165 static const char *JimCharsetMatch(const char *pattern, int c, int flags)
6166 {
6167     int not = 0;
6168     int pchar;
6169     int match = 0;
6170     int nocase = 0;
6171 
6172     if (flags & JIM_NOCASE) {
6173         nocase++;
6174         c = utf8_upper(c);
6175     }
6176 
6177     if (flags & JIM_CHARSET_SCAN) {
6178         if (*pattern == '^') {
6179             not++;
6180             pattern++;
6181         }
6182 
6183 
6184         if (*pattern == ']') {
6185             goto first;
6186         }
6187     }
6188 
6189     while (*pattern && *pattern != ']') {
6190 
6191         if (pattern[0] == '\\') {
6192 first:
6193             pattern += utf8_tounicode_case(pattern, &pchar, nocase);
6194         }
6195         else {
6196 
6197             int start;
6198             int end;
6199 
6200             pattern += utf8_tounicode_case(pattern, &start, nocase);
6201             if (pattern[0] == '-' && pattern[1]) {
6202 
6203                 pattern += utf8_tounicode(pattern, &pchar);
6204                 pattern += utf8_tounicode_case(pattern, &end, nocase);
6205 
6206 
6207                 if ((c >= start && c <= end) || (c >= end && c <= start)) {
6208                     match = 1;
6209                 }
6210                 continue;
6211             }
6212             pchar = start;
6213         }
6214 
6215         if (pchar == c) {
6216             match = 1;
6217         }
6218     }
6219     if (not) {
6220         match = !match;
6221     }
6222 
6223     return match ? pattern : NULL;
6224 }
6225 
6226 
6227 
JimGlobMatch(const char * pattern,const char * string,int nocase)6228 static int JimGlobMatch(const char *pattern, const char *string, int nocase)
6229 {
6230     int c;
6231     int pchar;
6232     while (*pattern) {
6233         switch (pattern[0]) {
6234             case '*':
6235                 while (pattern[1] == '*') {
6236                     pattern++;
6237                 }
6238                 pattern++;
6239                 if (!pattern[0]) {
6240                     return 1;
6241                 }
6242                 while (*string) {
6243 
6244                     if (JimGlobMatch(pattern, string, nocase))
6245                         return 1;
6246                     string += utf8_tounicode(string, &c);
6247                 }
6248                 return 0;
6249 
6250             case '?':
6251                 string += utf8_tounicode(string, &c);
6252                 break;
6253 
6254             case '[': {
6255                     string += utf8_tounicode(string, &c);
6256                     pattern = JimCharsetMatch(pattern + 1, c, nocase ? JIM_NOCASE : 0);
6257                     if (!pattern) {
6258                         return 0;
6259                     }
6260                     if (!*pattern) {
6261 
6262                         continue;
6263                     }
6264                     break;
6265                 }
6266             case '\\':
6267                 if (pattern[1]) {
6268                     pattern++;
6269                 }
6270 
6271             default:
6272                 string += utf8_tounicode_case(string, &c, nocase);
6273                 utf8_tounicode_case(pattern, &pchar, nocase);
6274                 if (pchar != c) {
6275                     return 0;
6276                 }
6277                 break;
6278         }
6279         pattern += utf8_tounicode_case(pattern, &pchar, nocase);
6280         if (!*string) {
6281             while (*pattern == '*') {
6282                 pattern++;
6283             }
6284             break;
6285         }
6286     }
6287     if (!*pattern && !*string) {
6288         return 1;
6289     }
6290     return 0;
6291 }
6292 
JimStringCompare(const char * s1,int l1,const char * s2,int l2)6293 static int JimStringCompare(const char *s1, int l1, const char *s2, int l2)
6294 {
6295     if (l1 < l2) {
6296         return memcmp(s1, s2, l1) <= 0 ? -1 : 1;
6297     }
6298     else if (l2 < l1) {
6299         return memcmp(s1, s2, l2) >= 0 ? 1 : -1;
6300     }
6301     else {
6302         return JimSign(memcmp(s1, s2, l1));
6303     }
6304 }
6305 
JimStringCompareLen(const char * s1,const char * s2,int maxchars,int nocase)6306 static int JimStringCompareLen(const char *s1, const char *s2, int maxchars, int nocase)
6307 {
6308     while (*s1 && *s2 && maxchars) {
6309         int c1, c2;
6310         s1 += utf8_tounicode_case(s1, &c1, nocase);
6311         s2 += utf8_tounicode_case(s2, &c2, nocase);
6312         if (c1 != c2) {
6313             return JimSign(c1 - c2);
6314         }
6315         maxchars--;
6316     }
6317     if (!maxchars) {
6318         return 0;
6319     }
6320 
6321     if (*s1) {
6322         return 1;
6323     }
6324     if (*s2) {
6325         return -1;
6326     }
6327     return 0;
6328 }
6329 
JimStringFirst(const char * s1,int l1,const char * s2,int l2,int idx)6330 static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
6331 {
6332     int i;
6333     int l1bytelen;
6334 
6335     if (!l1 || !l2 || l1 > l2) {
6336         return -1;
6337     }
6338     if (idx < 0)
6339         idx = 0;
6340     s2 += utf8_index(s2, idx);
6341 
6342     l1bytelen = utf8_index(s1, l1);
6343 
6344     for (i = idx; i <= l2 - l1; i++) {
6345         int c;
6346         if (memcmp(s2, s1, l1bytelen) == 0) {
6347             return i;
6348         }
6349         s2 += utf8_tounicode(s2, &c);
6350     }
6351     return -1;
6352 }
6353 
JimStringLast(const char * s1,int l1,const char * s2,int l2)6354 static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
6355 {
6356     const char *p;
6357 
6358     if (!l1 || !l2 || l1 > l2)
6359         return -1;
6360 
6361 
6362     for (p = s2 + l2 - 1; p != s2 - 1; p--) {
6363         if (*p == *s1 && memcmp(s1, p, l1) == 0) {
6364             return p - s2;
6365         }
6366     }
6367     return -1;
6368 }
6369 
6370 #ifdef JIM_UTF8
JimStringLastUtf8(const char * s1,int l1,const char * s2,int l2)6371 static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2)
6372 {
6373     int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2));
6374     if (n > 0) {
6375         n = utf8_strlen(s2, n);
6376     }
6377     return n;
6378 }
6379 #endif
6380 
JimCheckConversion(const char * str,const char * endptr)6381 static int JimCheckConversion(const char *str, const char *endptr)
6382 {
6383     if (str[0] == '\0' || str == endptr) {
6384         return JIM_ERR;
6385     }
6386 
6387     if (endptr[0] != '\0') {
6388         while (*endptr) {
6389             if (!isspace(UCHAR(*endptr))) {
6390                 return JIM_ERR;
6391             }
6392             endptr++;
6393         }
6394     }
6395     return JIM_OK;
6396 }
6397 
JimNumberBase(const char * str,int * base,int * sign)6398 static int JimNumberBase(const char *str, int *base, int *sign)
6399 {
6400     int i = 0;
6401 
6402     *base = 10;
6403 
6404     while (isspace(UCHAR(str[i]))) {
6405         i++;
6406     }
6407 
6408     if (str[i] == '-') {
6409         *sign = -1;
6410         i++;
6411     }
6412     else {
6413         if (str[i] == '+') {
6414             i++;
6415         }
6416         *sign = 1;
6417     }
6418 
6419     if (str[i] != '0') {
6420 
6421         return 0;
6422     }
6423 
6424 
6425     switch (str[i + 1]) {
6426         case 'x': case 'X': *base = 16; break;
6427         case 'o': case 'O': *base = 8; break;
6428         case 'b': case 'B': *base = 2; break;
6429         default: return 0;
6430     }
6431     i += 2;
6432 
6433     if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
6434 
6435         return i;
6436     }
6437 
6438     *base = 10;
6439     return 0;
6440 }
6441 
jim_strtol(const char * str,char ** endptr)6442 static long jim_strtol(const char *str, char **endptr)
6443 {
6444     int sign;
6445     int base;
6446     int i = JimNumberBase(str, &base, &sign);
6447 
6448     if (base != 10) {
6449         long value = strtol(str + i, endptr, base);
6450         if (endptr == NULL || *endptr != str + i) {
6451             return value * sign;
6452         }
6453     }
6454 
6455 
6456     return strtol(str, endptr, 10);
6457 }
6458 
6459 
jim_strtoull(const char * str,char ** endptr)6460 static jim_wide jim_strtoull(const char *str, char **endptr)
6461 {
6462 #ifdef HAVE_LONG_LONG
6463     int sign;
6464     int base;
6465     int i = JimNumberBase(str, &base, &sign);
6466 
6467     if (base != 10) {
6468         jim_wide value = strtoull(str + i, endptr, base);
6469         if (endptr == NULL || *endptr != str + i) {
6470             return value * sign;
6471         }
6472     }
6473 
6474 
6475     return strtoull(str, endptr, 10);
6476 #else
6477     return (unsigned long)jim_strtol(str, endptr);
6478 #endif
6479 }
6480 
Jim_StringToWide(const char * str,jim_wide * widePtr,int base)6481 int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
6482 {
6483     char *endptr;
6484 
6485     if (base) {
6486         *widePtr = strtoull(str, &endptr, base);
6487     }
6488     else {
6489         *widePtr = jim_strtoull(str, &endptr);
6490     }
6491 
6492     return JimCheckConversion(str, endptr);
6493 }
6494 
Jim_StringToDouble(const char * str,double * doublePtr)6495 int Jim_StringToDouble(const char *str, double *doublePtr)
6496 {
6497     char *endptr;
6498 
6499 
6500     errno = 0;
6501 
6502     *doublePtr = strtod(str, &endptr);
6503 
6504     return JimCheckConversion(str, endptr);
6505 }
6506 
JimPowWide(jim_wide b,jim_wide e)6507 static jim_wide JimPowWide(jim_wide b, jim_wide e)
6508 {
6509     jim_wide res = 1;
6510 
6511 
6512     if (b == 1) {
6513 
6514         return 1;
6515     }
6516     if (e < 0) {
6517         if (b != -1) {
6518             return 0;
6519         }
6520         e = -e;
6521     }
6522     while (e)
6523     {
6524         if (e & 1) {
6525             res *= b;
6526         }
6527         e >>= 1;
6528         b *= b;
6529     }
6530     return res;
6531 }
6532 
6533 #ifdef JIM_DEBUG_PANIC
JimPanicDump(int condition,const char * fmt,...)6534 static void JimPanicDump(int condition, const char *fmt, ...)
6535 {
6536     va_list ap;
6537 
6538     if (!condition) {
6539         return;
6540     }
6541 
6542     va_start(ap, fmt);
6543 
6544     fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
6545     vfprintf(stderr, fmt, ap);
6546     fprintf(stderr, "\n\n");
6547     va_end(ap);
6548 
6549 #ifdef HAVE_BACKTRACE
6550     {
6551         void *array[40];
6552         int size, i;
6553         char **strings;
6554 
6555         size = backtrace(array, 40);
6556         strings = backtrace_symbols(array, size);
6557         for (i = 0; i < size; i++)
6558             fprintf(stderr, "[backtrace] %s\n", strings[i]);
6559         fprintf(stderr, "[backtrace] Include the above lines and the output\n");
6560         fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n");
6561     }
6562 #endif
6563 
6564     exit(1);
6565 }
6566 #endif
6567 
6568 
Jim_Alloc(int size)6569 void *Jim_Alloc(int size)
6570 {
6571     return size ? malloc(size) : NULL;
6572 }
6573 
Jim_Free(void * ptr)6574 void Jim_Free(void *ptr)
6575 {
6576     free(ptr);
6577 }
6578 
Jim_Realloc(void * ptr,int size)6579 void *Jim_Realloc(void *ptr, int size)
6580 {
6581     return realloc(ptr, size);
6582 }
6583 
Jim_StrDup(const char * s)6584 char *Jim_StrDup(const char *s)
6585 {
6586     return strdup(s);
6587 }
6588 
Jim_StrDupLen(const char * s,int l)6589 char *Jim_StrDupLen(const char *s, int l)
6590 {
6591     char *copy = Jim_Alloc(l + 1);
6592 
6593     memcpy(copy, s, l + 1);
6594     copy[l] = 0;
6595     return copy;
6596 }
6597 
6598 
6599 
JimClock(void)6600 static jim_wide JimClock(void)
6601 {
6602     struct timeval tv;
6603 
6604     gettimeofday(&tv, NULL);
6605     return (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec;
6606 }
6607 
6608 
6609 
6610 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
6611 static unsigned int JimHashTableNextPower(unsigned int size);
6612 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
6613 
6614 
6615 
6616 
Jim_IntHashFunction(unsigned int key)6617 unsigned int Jim_IntHashFunction(unsigned int key)
6618 {
6619     key += ~(key << 15);
6620     key ^= (key >> 10);
6621     key += (key << 3);
6622     key ^= (key >> 6);
6623     key += ~(key << 11);
6624     key ^= (key >> 16);
6625     return key;
6626 }
6627 
Jim_GenHashFunction(const unsigned char * buf,int len)6628 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
6629 {
6630     unsigned int h = 0;
6631 
6632     while (len--)
6633         h += (h << 3) + *buf++;
6634     return h;
6635 }
6636 
6637 
6638 
6639 
JimResetHashTable(Jim_HashTable * ht)6640 static void JimResetHashTable(Jim_HashTable *ht)
6641 {
6642     ht->table = NULL;
6643     ht->size = 0;
6644     ht->sizemask = 0;
6645     ht->used = 0;
6646     ht->collisions = 0;
6647 #ifdef JIM_RANDOMISE_HASH
6648     ht->uniq = (rand() ^ time(NULL) ^ clock());
6649 #else
6650     ht->uniq = 0;
6651 #endif
6652 }
6653 
JimInitHashTableIterator(Jim_HashTable * ht,Jim_HashTableIterator * iter)6654 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
6655 {
6656     iter->ht = ht;
6657     iter->index = -1;
6658     iter->entry = NULL;
6659     iter->nextEntry = NULL;
6660 }
6661 
6662 
Jim_InitHashTable(Jim_HashTable * ht,const Jim_HashTableType * type,void * privDataPtr)6663 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
6664 {
6665     JimResetHashTable(ht);
6666     ht->type = type;
6667     ht->privdata = privDataPtr;
6668     return JIM_OK;
6669 }
6670 
Jim_ResizeHashTable(Jim_HashTable * ht)6671 void Jim_ResizeHashTable(Jim_HashTable *ht)
6672 {
6673     int minimal = ht->used;
6674 
6675     if (minimal < JIM_HT_INITIAL_SIZE)
6676         minimal = JIM_HT_INITIAL_SIZE;
6677     Jim_ExpandHashTable(ht, minimal);
6678 }
6679 
6680 
Jim_ExpandHashTable(Jim_HashTable * ht,unsigned int size)6681 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
6682 {
6683     Jim_HashTable n;
6684     unsigned int realsize = JimHashTableNextPower(size), i;
6685 
6686      if (size <= ht->used)
6687         return;
6688 
6689     Jim_InitHashTable(&n, ht->type, ht->privdata);
6690     n.size = realsize;
6691     n.sizemask = realsize - 1;
6692     n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
6693 
6694     n.uniq = ht->uniq;
6695 
6696 
6697     memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
6698 
6699     n.used = ht->used;
6700     for (i = 0; ht->used > 0; i++) {
6701         Jim_HashEntry *he, *nextHe;
6702 
6703         if (ht->table[i] == NULL)
6704             continue;
6705 
6706 
6707         he = ht->table[i];
6708         while (he) {
6709             unsigned int h;
6710 
6711             nextHe = he->next;
6712 
6713             h = Jim_HashKey(ht, he->key) & n.sizemask;
6714             he->next = n.table[h];
6715             n.table[h] = he;
6716             ht->used--;
6717 
6718             he = nextHe;
6719         }
6720     }
6721     assert(ht->used == 0);
6722     Jim_Free(ht->table);
6723 
6724 
6725     *ht = n;
6726 }
6727 
6728 
Jim_AddHashEntry(Jim_HashTable * ht,const void * key,void * val)6729 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
6730 {
6731     Jim_HashEntry *entry;
6732 
6733     entry = JimInsertHashEntry(ht, key, 0);
6734     if (entry == NULL)
6735         return JIM_ERR;
6736 
6737 
6738     Jim_SetHashKey(ht, entry, key);
6739     Jim_SetHashVal(ht, entry, val);
6740     return JIM_OK;
6741 }
6742 
6743 
Jim_ReplaceHashEntry(Jim_HashTable * ht,const void * key,void * val)6744 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
6745 {
6746     int existed;
6747     Jim_HashEntry *entry;
6748 
6749     entry = JimInsertHashEntry(ht, key, 1);
6750     if (entry->key) {
6751         if (ht->type->valDestructor && ht->type->valDup) {
6752             void *newval = ht->type->valDup(ht->privdata, val);
6753             ht->type->valDestructor(ht->privdata, entry->u.val);
6754             entry->u.val = newval;
6755         }
6756         else {
6757             Jim_FreeEntryVal(ht, entry);
6758             Jim_SetHashVal(ht, entry, val);
6759         }
6760         existed = 1;
6761     }
6762     else {
6763 
6764         Jim_SetHashKey(ht, entry, key);
6765         Jim_SetHashVal(ht, entry, val);
6766         existed = 0;
6767     }
6768 
6769     return existed;
6770 }
6771 
6772 
Jim_DeleteHashEntry(Jim_HashTable * ht,const void * key)6773 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
6774 {
6775     unsigned int h;
6776     Jim_HashEntry *he, *prevHe;
6777 
6778     if (ht->used == 0)
6779         return JIM_ERR;
6780     h = Jim_HashKey(ht, key) & ht->sizemask;
6781     he = ht->table[h];
6782 
6783     prevHe = NULL;
6784     while (he) {
6785         if (Jim_CompareHashKeys(ht, key, he->key)) {
6786 
6787             if (prevHe)
6788                 prevHe->next = he->next;
6789             else
6790                 ht->table[h] = he->next;
6791             Jim_FreeEntryKey(ht, he);
6792             Jim_FreeEntryVal(ht, he);
6793             Jim_Free(he);
6794             ht->used--;
6795             return JIM_OK;
6796         }
6797         prevHe = he;
6798         he = he->next;
6799     }
6800     return JIM_ERR;
6801 }
6802 
6803 
Jim_FreeHashTable(Jim_HashTable * ht)6804 int Jim_FreeHashTable(Jim_HashTable *ht)
6805 {
6806     unsigned int i;
6807 
6808 
6809     for (i = 0; ht->used > 0; i++) {
6810         Jim_HashEntry *he, *nextHe;
6811 
6812         if ((he = ht->table[i]) == NULL)
6813             continue;
6814         while (he) {
6815             nextHe = he->next;
6816             Jim_FreeEntryKey(ht, he);
6817             Jim_FreeEntryVal(ht, he);
6818             Jim_Free(he);
6819             ht->used--;
6820             he = nextHe;
6821         }
6822     }
6823 
6824     Jim_Free(ht->table);
6825 
6826     JimResetHashTable(ht);
6827     return JIM_OK;
6828 }
6829 
Jim_FindHashEntry(Jim_HashTable * ht,const void * key)6830 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
6831 {
6832     Jim_HashEntry *he;
6833     unsigned int h;
6834 
6835     if (ht->used == 0)
6836         return NULL;
6837     h = Jim_HashKey(ht, key) & ht->sizemask;
6838     he = ht->table[h];
6839     while (he) {
6840         if (Jim_CompareHashKeys(ht, key, he->key))
6841             return he;
6842         he = he->next;
6843     }
6844     return NULL;
6845 }
6846 
Jim_GetHashTableIterator(Jim_HashTable * ht)6847 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
6848 {
6849     Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
6850     JimInitHashTableIterator(ht, iter);
6851     return iter;
6852 }
6853 
Jim_NextHashEntry(Jim_HashTableIterator * iter)6854 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
6855 {
6856     while (1) {
6857         if (iter->entry == NULL) {
6858             iter->index++;
6859             if (iter->index >= (signed)iter->ht->size)
6860                 break;
6861             iter->entry = iter->ht->table[iter->index];
6862         }
6863         else {
6864             iter->entry = iter->nextEntry;
6865         }
6866         if (iter->entry) {
6867             iter->nextEntry = iter->entry->next;
6868             return iter->entry;
6869         }
6870     }
6871     return NULL;
6872 }
6873 
6874 
6875 
6876 
JimExpandHashTableIfNeeded(Jim_HashTable * ht)6877 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
6878 {
6879     if (ht->size == 0)
6880         Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
6881     if (ht->size == ht->used)
6882         Jim_ExpandHashTable(ht, ht->size * 2);
6883 }
6884 
6885 
JimHashTableNextPower(unsigned int size)6886 static unsigned int JimHashTableNextPower(unsigned int size)
6887 {
6888     unsigned int i = JIM_HT_INITIAL_SIZE;
6889 
6890     if (size >= 2147483648U)
6891         return 2147483648U;
6892     while (1) {
6893         if (i >= size)
6894             return i;
6895         i *= 2;
6896     }
6897 }
6898 
JimInsertHashEntry(Jim_HashTable * ht,const void * key,int replace)6899 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
6900 {
6901     unsigned int h;
6902     Jim_HashEntry *he;
6903 
6904 
6905     JimExpandHashTableIfNeeded(ht);
6906 
6907 
6908     h = Jim_HashKey(ht, key) & ht->sizemask;
6909 
6910     he = ht->table[h];
6911     while (he) {
6912         if (Jim_CompareHashKeys(ht, key, he->key))
6913             return replace ? he : NULL;
6914         he = he->next;
6915     }
6916 
6917 
6918     he = Jim_Alloc(sizeof(*he));
6919     he->next = ht->table[h];
6920     ht->table[h] = he;
6921     ht->used++;
6922     he->key = NULL;
6923 
6924     return he;
6925 }
6926 
6927 
6928 
JimStringCopyHTHashFunction(const void * key)6929 static unsigned int JimStringCopyHTHashFunction(const void *key)
6930 {
6931     return Jim_GenHashFunction(key, strlen(key));
6932 }
6933 
JimStringCopyHTDup(void * privdata,const void * key)6934 static void *JimStringCopyHTDup(void *privdata, const void *key)
6935 {
6936     return Jim_StrDup(key);
6937 }
6938 
JimStringCopyHTKeyCompare(void * privdata,const void * key1,const void * key2)6939 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
6940 {
6941     return strcmp(key1, key2) == 0;
6942 }
6943 
JimStringCopyHTKeyDestructor(void * privdata,void * key)6944 static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
6945 {
6946     Jim_Free(key);
6947 }
6948 
6949 static const Jim_HashTableType JimPackageHashTableType = {
6950     JimStringCopyHTHashFunction,
6951     JimStringCopyHTDup,
6952     NULL,
6953     JimStringCopyHTKeyCompare,
6954     JimStringCopyHTKeyDestructor,
6955     NULL
6956 };
6957 
6958 typedef struct AssocDataValue
6959 {
6960     Jim_InterpDeleteProc *delProc;
6961     void *data;
6962 } AssocDataValue;
6963 
JimAssocDataHashTableValueDestructor(void * privdata,void * data)6964 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
6965 {
6966     AssocDataValue *assocPtr = (AssocDataValue *) data;
6967 
6968     if (assocPtr->delProc != NULL)
6969         assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
6970     Jim_Free(data);
6971 }
6972 
6973 static const Jim_HashTableType JimAssocDataHashTableType = {
6974     JimStringCopyHTHashFunction,
6975     JimStringCopyHTDup,
6976     NULL,
6977     JimStringCopyHTKeyCompare,
6978     JimStringCopyHTKeyDestructor,
6979     JimAssocDataHashTableValueDestructor
6980 };
6981 
Jim_InitStack(Jim_Stack * stack)6982 void Jim_InitStack(Jim_Stack *stack)
6983 {
6984     stack->len = 0;
6985     stack->maxlen = 0;
6986     stack->vector = NULL;
6987 }
6988 
Jim_FreeStack(Jim_Stack * stack)6989 void Jim_FreeStack(Jim_Stack *stack)
6990 {
6991     Jim_Free(stack->vector);
6992 }
6993 
Jim_StackLen(Jim_Stack * stack)6994 int Jim_StackLen(Jim_Stack *stack)
6995 {
6996     return stack->len;
6997 }
6998 
Jim_StackPush(Jim_Stack * stack,void * element)6999 void Jim_StackPush(Jim_Stack *stack, void *element)
7000 {
7001     int neededLen = stack->len + 1;
7002 
7003     if (neededLen > stack->maxlen) {
7004         stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
7005         stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
7006     }
7007     stack->vector[stack->len] = element;
7008     stack->len++;
7009 }
7010 
Jim_StackPop(Jim_Stack * stack)7011 void *Jim_StackPop(Jim_Stack *stack)
7012 {
7013     if (stack->len == 0)
7014         return NULL;
7015     stack->len--;
7016     return stack->vector[stack->len];
7017 }
7018 
Jim_StackPeek(Jim_Stack * stack)7019 void *Jim_StackPeek(Jim_Stack *stack)
7020 {
7021     if (stack->len == 0)
7022         return NULL;
7023     return stack->vector[stack->len - 1];
7024 }
7025 
Jim_FreeStackElements(Jim_Stack * stack,void (* freeFunc)(void * ptr))7026 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
7027 {
7028     int i;
7029 
7030     for (i = 0; i < stack->len; i++)
7031         freeFunc(stack->vector[i]);
7032 }
7033 
7034 
7035 
7036 #define JIM_TT_NONE    0
7037 #define JIM_TT_STR     1
7038 #define JIM_TT_ESC     2
7039 #define JIM_TT_VAR     3
7040 #define JIM_TT_DICTSUGAR   4
7041 #define JIM_TT_CMD     5
7042 
7043 #define JIM_TT_SEP     6
7044 #define JIM_TT_EOL     7
7045 #define JIM_TT_EOF     8
7046 
7047 #define JIM_TT_LINE    9
7048 #define JIM_TT_WORD   10
7049 
7050 
7051 #define JIM_TT_SUBEXPR_START  11
7052 #define JIM_TT_SUBEXPR_END    12
7053 #define JIM_TT_SUBEXPR_COMMA  13
7054 #define JIM_TT_EXPR_INT       14
7055 #define JIM_TT_EXPR_DOUBLE    15
7056 #define JIM_TT_EXPR_BOOLEAN   16
7057 
7058 #define JIM_TT_EXPRSUGAR      17
7059 
7060 
7061 #define JIM_TT_EXPR_OP        20
7062 
7063 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
7064 
7065 #define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA)
7066 
7067 #define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP)
7068 
7069 struct JimParseMissing {
7070     int ch;
7071     int line;
7072 };
7073 
7074 struct JimParserCtx
7075 {
7076     const char *p;
7077     int len;
7078     int linenr;
7079     const char *tstart;
7080     const char *tend;
7081     int tline;
7082     int tt;
7083     int eof;
7084     int inquote;
7085     int comment;
7086     struct JimParseMissing missing;
7087 };
7088 
7089 static int JimParseScript(struct JimParserCtx *pc);
7090 static int JimParseSep(struct JimParserCtx *pc);
7091 static int JimParseEol(struct JimParserCtx *pc);
7092 static int JimParseCmd(struct JimParserCtx *pc);
7093 static int JimParseQuote(struct JimParserCtx *pc);
7094 static int JimParseVar(struct JimParserCtx *pc);
7095 static int JimParseBrace(struct JimParserCtx *pc);
7096 static int JimParseStr(struct JimParserCtx *pc);
7097 static int JimParseComment(struct JimParserCtx *pc);
7098 static void JimParseSubCmd(struct JimParserCtx *pc);
7099 static int JimParseSubQuote(struct JimParserCtx *pc);
7100 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
7101 
JimParserInit(struct JimParserCtx * pc,const char * prg,int len,int linenr)7102 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
7103 {
7104     pc->p = prg;
7105     pc->len = len;
7106     pc->tstart = NULL;
7107     pc->tend = NULL;
7108     pc->tline = 0;
7109     pc->tt = JIM_TT_NONE;
7110     pc->eof = 0;
7111     pc->inquote = 0;
7112     pc->linenr = linenr;
7113     pc->comment = 1;
7114     pc->missing.ch = ' ';
7115     pc->missing.line = linenr;
7116 }
7117 
JimParseScript(struct JimParserCtx * pc)7118 static int JimParseScript(struct JimParserCtx *pc)
7119 {
7120     while (1) {
7121         if (!pc->len) {
7122             pc->tstart = pc->p;
7123             pc->tend = pc->p - 1;
7124             pc->tline = pc->linenr;
7125             pc->tt = JIM_TT_EOL;
7126             pc->eof = 1;
7127             return JIM_OK;
7128         }
7129         switch (*(pc->p)) {
7130             case '\\':
7131                 if (*(pc->p + 1) == '\n' && !pc->inquote) {
7132                     return JimParseSep(pc);
7133                 }
7134                 pc->comment = 0;
7135                 return JimParseStr(pc);
7136             case ' ':
7137             case '\t':
7138             case '\r':
7139             case '\f':
7140                 if (!pc->inquote)
7141                     return JimParseSep(pc);
7142                 pc->comment = 0;
7143                 return JimParseStr(pc);
7144             case '\n':
7145             case ';':
7146                 pc->comment = 1;
7147                 if (!pc->inquote)
7148                     return JimParseEol(pc);
7149                 return JimParseStr(pc);
7150             case '[':
7151                 pc->comment = 0;
7152                 return JimParseCmd(pc);
7153             case '$':
7154                 pc->comment = 0;
7155                 if (JimParseVar(pc) == JIM_ERR) {
7156 
7157                     pc->tstart = pc->tend = pc->p++;
7158                     pc->len--;
7159                     pc->tt = JIM_TT_ESC;
7160                 }
7161                 return JIM_OK;
7162             case '#':
7163                 if (pc->comment) {
7164                     JimParseComment(pc);
7165                     continue;
7166                 }
7167                 return JimParseStr(pc);
7168             default:
7169                 pc->comment = 0;
7170                 return JimParseStr(pc);
7171         }
7172         return JIM_OK;
7173     }
7174 }
7175 
JimParseSep(struct JimParserCtx * pc)7176 static int JimParseSep(struct JimParserCtx *pc)
7177 {
7178     pc->tstart = pc->p;
7179     pc->tline = pc->linenr;
7180     while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
7181         if (*pc->p == '\n') {
7182             break;
7183         }
7184         if (*pc->p == '\\') {
7185             pc->p++;
7186             pc->len--;
7187             pc->linenr++;
7188         }
7189         pc->p++;
7190         pc->len--;
7191     }
7192     pc->tend = pc->p - 1;
7193     pc->tt = JIM_TT_SEP;
7194     return JIM_OK;
7195 }
7196 
JimParseEol(struct JimParserCtx * pc)7197 static int JimParseEol(struct JimParserCtx *pc)
7198 {
7199     pc->tstart = pc->p;
7200     pc->tline = pc->linenr;
7201     while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
7202         if (*pc->p == '\n')
7203             pc->linenr++;
7204         pc->p++;
7205         pc->len--;
7206     }
7207     pc->tend = pc->p - 1;
7208     pc->tt = JIM_TT_EOL;
7209     return JIM_OK;
7210 }
7211 
7212 
JimParseSubBrace(struct JimParserCtx * pc)7213 static void JimParseSubBrace(struct JimParserCtx *pc)
7214 {
7215     int level = 1;
7216 
7217 
7218     pc->p++;
7219     pc->len--;
7220     while (pc->len) {
7221         switch (*pc->p) {
7222             case '\\':
7223                 if (pc->len > 1) {
7224                     if (*++pc->p == '\n') {
7225                         pc->linenr++;
7226                     }
7227                     pc->len--;
7228                 }
7229                 break;
7230 
7231             case '{':
7232                 level++;
7233                 break;
7234 
7235             case '}':
7236                 if (--level == 0) {
7237                     pc->tend = pc->p - 1;
7238                     pc->p++;
7239                     pc->len--;
7240                     return;
7241                 }
7242                 break;
7243 
7244             case '\n':
7245                 pc->linenr++;
7246                 break;
7247         }
7248         pc->p++;
7249         pc->len--;
7250     }
7251     pc->missing.ch = '{';
7252     pc->missing.line = pc->tline;
7253     pc->tend = pc->p - 1;
7254 }
7255 
JimParseSubQuote(struct JimParserCtx * pc)7256 static int JimParseSubQuote(struct JimParserCtx *pc)
7257 {
7258     int tt = JIM_TT_STR;
7259     int line = pc->tline;
7260 
7261 
7262     pc->p++;
7263     pc->len--;
7264     while (pc->len) {
7265         switch (*pc->p) {
7266             case '\\':
7267                 if (pc->len > 1) {
7268                     if (*++pc->p == '\n') {
7269                         pc->linenr++;
7270                     }
7271                     pc->len--;
7272                     tt = JIM_TT_ESC;
7273                 }
7274                 break;
7275 
7276             case '"':
7277                 pc->tend = pc->p - 1;
7278                 pc->p++;
7279                 pc->len--;
7280                 return tt;
7281 
7282             case '[':
7283                 JimParseSubCmd(pc);
7284                 tt = JIM_TT_ESC;
7285                 continue;
7286 
7287             case '\n':
7288                 pc->linenr++;
7289                 break;
7290 
7291             case '$':
7292                 tt = JIM_TT_ESC;
7293                 break;
7294         }
7295         pc->p++;
7296         pc->len--;
7297     }
7298     pc->missing.ch = '"';
7299     pc->missing.line = line;
7300     pc->tend = pc->p - 1;
7301     return tt;
7302 }
7303 
JimParseSubCmd(struct JimParserCtx * pc)7304 static void JimParseSubCmd(struct JimParserCtx *pc)
7305 {
7306     int level = 1;
7307     int startofword = 1;
7308     int line = pc->tline;
7309 
7310 
7311     pc->p++;
7312     pc->len--;
7313     while (pc->len) {
7314         switch (*pc->p) {
7315             case '\\':
7316                 if (pc->len > 1) {
7317                     if (*++pc->p == '\n') {
7318                         pc->linenr++;
7319                     }
7320                     pc->len--;
7321                 }
7322                 break;
7323 
7324             case '[':
7325                 level++;
7326                 break;
7327 
7328             case ']':
7329                 if (--level == 0) {
7330                     pc->tend = pc->p - 1;
7331                     pc->p++;
7332                     pc->len--;
7333                     return;
7334                 }
7335                 break;
7336 
7337             case '"':
7338                 if (startofword) {
7339                     JimParseSubQuote(pc);
7340                     continue;
7341                 }
7342                 break;
7343 
7344             case '{':
7345                 JimParseSubBrace(pc);
7346                 startofword = 0;
7347                 continue;
7348 
7349             case '\n':
7350                 pc->linenr++;
7351                 break;
7352         }
7353         startofword = isspace(UCHAR(*pc->p));
7354         pc->p++;
7355         pc->len--;
7356     }
7357     pc->missing.ch = '[';
7358     pc->missing.line = line;
7359     pc->tend = pc->p - 1;
7360 }
7361 
JimParseBrace(struct JimParserCtx * pc)7362 static int JimParseBrace(struct JimParserCtx *pc)
7363 {
7364     pc->tstart = pc->p + 1;
7365     pc->tline = pc->linenr;
7366     pc->tt = JIM_TT_STR;
7367     JimParseSubBrace(pc);
7368     return JIM_OK;
7369 }
7370 
JimParseCmd(struct JimParserCtx * pc)7371 static int JimParseCmd(struct JimParserCtx *pc)
7372 {
7373     pc->tstart = pc->p + 1;
7374     pc->tline = pc->linenr;
7375     pc->tt = JIM_TT_CMD;
7376     JimParseSubCmd(pc);
7377     return JIM_OK;
7378 }
7379 
JimParseQuote(struct JimParserCtx * pc)7380 static int JimParseQuote(struct JimParserCtx *pc)
7381 {
7382     pc->tstart = pc->p + 1;
7383     pc->tline = pc->linenr;
7384     pc->tt = JimParseSubQuote(pc);
7385     return JIM_OK;
7386 }
7387 
JimParseVar(struct JimParserCtx * pc)7388 static int JimParseVar(struct JimParserCtx *pc)
7389 {
7390 
7391     pc->p++;
7392     pc->len--;
7393 
7394 #ifdef EXPRSUGAR_BRACKET
7395     if (*pc->p == '[') {
7396 
7397         JimParseCmd(pc);
7398         pc->tt = JIM_TT_EXPRSUGAR;
7399         return JIM_OK;
7400     }
7401 #endif
7402 
7403     pc->tstart = pc->p;
7404     pc->tt = JIM_TT_VAR;
7405     pc->tline = pc->linenr;
7406 
7407     if (*pc->p == '{') {
7408         pc->tstart = ++pc->p;
7409         pc->len--;
7410 
7411         while (pc->len && *pc->p != '}') {
7412             if (*pc->p == '\n') {
7413                 pc->linenr++;
7414             }
7415             pc->p++;
7416             pc->len--;
7417         }
7418         pc->tend = pc->p - 1;
7419         if (pc->len) {
7420             pc->p++;
7421             pc->len--;
7422         }
7423     }
7424     else {
7425         while (1) {
7426 
7427             if (pc->p[0] == ':' && pc->p[1] == ':') {
7428                 while (*pc->p == ':') {
7429                     pc->p++;
7430                     pc->len--;
7431                 }
7432                 continue;
7433             }
7434             if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
7435                 pc->p++;
7436                 pc->len--;
7437                 continue;
7438             }
7439             break;
7440         }
7441 
7442         if (*pc->p == '(') {
7443             int count = 1;
7444             const char *paren = NULL;
7445 
7446             pc->tt = JIM_TT_DICTSUGAR;
7447 
7448             while (count && pc->len) {
7449                 pc->p++;
7450                 pc->len--;
7451                 if (*pc->p == '\\' && pc->len >= 1) {
7452                     pc->p++;
7453                     pc->len--;
7454                 }
7455                 else if (*pc->p == '(') {
7456                     count++;
7457                 }
7458                 else if (*pc->p == ')') {
7459                     paren = pc->p;
7460                     count--;
7461                 }
7462             }
7463             if (count == 0) {
7464                 pc->p++;
7465                 pc->len--;
7466             }
7467             else if (paren) {
7468 
7469                 paren++;
7470                 pc->len += (pc->p - paren);
7471                 pc->p = paren;
7472             }
7473 #ifndef EXPRSUGAR_BRACKET
7474             if (*pc->tstart == '(') {
7475                 pc->tt = JIM_TT_EXPRSUGAR;
7476             }
7477 #endif
7478         }
7479         pc->tend = pc->p - 1;
7480     }
7481     if (pc->tstart == pc->p) {
7482         pc->p--;
7483         pc->len++;
7484         return JIM_ERR;
7485     }
7486     return JIM_OK;
7487 }
7488 
JimParseStr(struct JimParserCtx * pc)7489 static int JimParseStr(struct JimParserCtx *pc)
7490 {
7491     if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
7492         pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
7493 
7494         if (*pc->p == '{') {
7495             return JimParseBrace(pc);
7496         }
7497         if (*pc->p == '"') {
7498             pc->inquote = 1;
7499             pc->p++;
7500             pc->len--;
7501 
7502             pc->missing.line = pc->tline;
7503         }
7504     }
7505     pc->tstart = pc->p;
7506     pc->tline = pc->linenr;
7507     while (1) {
7508         if (pc->len == 0) {
7509             if (pc->inquote) {
7510                 pc->missing.ch = '"';
7511             }
7512             pc->tend = pc->p - 1;
7513             pc->tt = JIM_TT_ESC;
7514             return JIM_OK;
7515         }
7516         switch (*pc->p) {
7517             case '\\':
7518                 if (!pc->inquote && *(pc->p + 1) == '\n') {
7519                     pc->tend = pc->p - 1;
7520                     pc->tt = JIM_TT_ESC;
7521                     return JIM_OK;
7522                 }
7523                 if (pc->len >= 2) {
7524                     if (*(pc->p + 1) == '\n') {
7525                         pc->linenr++;
7526                     }
7527                     pc->p++;
7528                     pc->len--;
7529                 }
7530                 else if (pc->len == 1) {
7531 
7532                     pc->missing.ch = '\\';
7533                 }
7534                 break;
7535             case '(':
7536 
7537                 if (pc->len > 1 && pc->p[1] != '$') {
7538                     break;
7539                 }
7540 
7541             case ')':
7542 
7543                 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
7544                     if (pc->p == pc->tstart) {
7545 
7546                         pc->p++;
7547                         pc->len--;
7548                     }
7549                     pc->tend = pc->p - 1;
7550                     pc->tt = JIM_TT_ESC;
7551                     return JIM_OK;
7552                 }
7553                 break;
7554 
7555             case '$':
7556             case '[':
7557                 pc->tend = pc->p - 1;
7558                 pc->tt = JIM_TT_ESC;
7559                 return JIM_OK;
7560             case ' ':
7561             case '\t':
7562             case '\n':
7563             case '\r':
7564             case '\f':
7565             case ';':
7566                 if (!pc->inquote) {
7567                     pc->tend = pc->p - 1;
7568                     pc->tt = JIM_TT_ESC;
7569                     return JIM_OK;
7570                 }
7571                 else if (*pc->p == '\n') {
7572                     pc->linenr++;
7573                 }
7574                 break;
7575             case '"':
7576                 if (pc->inquote) {
7577                     pc->tend = pc->p - 1;
7578                     pc->tt = JIM_TT_ESC;
7579                     pc->p++;
7580                     pc->len--;
7581                     pc->inquote = 0;
7582                     return JIM_OK;
7583                 }
7584                 break;
7585         }
7586         pc->p++;
7587         pc->len--;
7588     }
7589     return JIM_OK;
7590 }
7591 
JimParseComment(struct JimParserCtx * pc)7592 static int JimParseComment(struct JimParserCtx *pc)
7593 {
7594     while (*pc->p) {
7595         if (*pc->p == '\\') {
7596             pc->p++;
7597             pc->len--;
7598             if (pc->len == 0) {
7599                 pc->missing.ch = '\\';
7600                 return JIM_OK;
7601             }
7602             if (*pc->p == '\n') {
7603                 pc->linenr++;
7604             }
7605         }
7606         else if (*pc->p == '\n') {
7607             pc->p++;
7608             pc->len--;
7609             pc->linenr++;
7610             break;
7611         }
7612         pc->p++;
7613         pc->len--;
7614     }
7615     return JIM_OK;
7616 }
7617 
7618 
xdigitval(int c)7619 static int xdigitval(int c)
7620 {
7621     if (c >= '0' && c <= '9')
7622         return c - '0';
7623     if (c >= 'a' && c <= 'f')
7624         return c - 'a' + 10;
7625     if (c >= 'A' && c <= 'F')
7626         return c - 'A' + 10;
7627     return -1;
7628 }
7629 
odigitval(int c)7630 static int odigitval(int c)
7631 {
7632     if (c >= '0' && c <= '7')
7633         return c - '0';
7634     return -1;
7635 }
7636 
JimEscape(char * dest,const char * s,int slen)7637 static int JimEscape(char *dest, const char *s, int slen)
7638 {
7639     char *p = dest;
7640     int i, len;
7641 
7642     for (i = 0; i < slen; i++) {
7643         switch (s[i]) {
7644             case '\\':
7645                 switch (s[i + 1]) {
7646                     case 'a':
7647                         *p++ = 0x7;
7648                         i++;
7649                         break;
7650                     case 'b':
7651                         *p++ = 0x8;
7652                         i++;
7653                         break;
7654                     case 'f':
7655                         *p++ = 0xc;
7656                         i++;
7657                         break;
7658                     case 'n':
7659                         *p++ = 0xa;
7660                         i++;
7661                         break;
7662                     case 'r':
7663                         *p++ = 0xd;
7664                         i++;
7665                         break;
7666                     case 't':
7667                         *p++ = 0x9;
7668                         i++;
7669                         break;
7670                     case 'u':
7671                     case 'U':
7672                     case 'x':
7673                         {
7674                             unsigned val = 0;
7675                             int k;
7676                             int maxchars = 2;
7677 
7678                             i++;
7679 
7680                             if (s[i] == 'U') {
7681                                 maxchars = 8;
7682                             }
7683                             else if (s[i] == 'u') {
7684                                 if (s[i + 1] == '{') {
7685                                     maxchars = 6;
7686                                     i++;
7687                                 }
7688                                 else {
7689                                     maxchars = 4;
7690                                 }
7691                             }
7692 
7693                             for (k = 0; k < maxchars; k++) {
7694                                 int c = xdigitval(s[i + k + 1]);
7695                                 if (c == -1) {
7696                                     break;
7697                                 }
7698                                 val = (val << 4) | c;
7699                             }
7700 
7701                             if (s[i] == '{') {
7702                                 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
7703 
7704                                     i--;
7705                                     k = 0;
7706                                 }
7707                                 else {
7708 
7709                                     k++;
7710                                 }
7711                             }
7712                             if (k) {
7713 
7714                                 if (s[i] == 'x') {
7715                                     *p++ = val;
7716                                 }
7717                                 else {
7718                                     p += utf8_fromunicode(p, val);
7719                                 }
7720                                 i += k;
7721                                 break;
7722                             }
7723 
7724                             *p++ = s[i];
7725                         }
7726                         break;
7727                     case 'v':
7728                         *p++ = 0xb;
7729                         i++;
7730                         break;
7731                     case '\0':
7732                         *p++ = '\\';
7733                         i++;
7734                         break;
7735                     case '\n':
7736 
7737                         *p++ = ' ';
7738                         do {
7739                             i++;
7740                         } while (s[i + 1] == ' ' || s[i + 1] == '\t');
7741                         break;
7742                     case '0':
7743                     case '1':
7744                     case '2':
7745                     case '3':
7746                     case '4':
7747                     case '5':
7748                     case '6':
7749                     case '7':
7750 
7751                         {
7752                             int val = 0;
7753                             int c = odigitval(s[i + 1]);
7754 
7755                             val = c;
7756                             c = odigitval(s[i + 2]);
7757                             if (c == -1) {
7758                                 *p++ = val;
7759                                 i++;
7760                                 break;
7761                             }
7762                             val = (val * 8) + c;
7763                             c = odigitval(s[i + 3]);
7764                             if (c == -1) {
7765                                 *p++ = val;
7766                                 i += 2;
7767                                 break;
7768                             }
7769                             val = (val * 8) + c;
7770                             *p++ = val;
7771                             i += 3;
7772                         }
7773                         break;
7774                     default:
7775                         *p++ = s[i + 1];
7776                         i++;
7777                         break;
7778                 }
7779                 break;
7780             default:
7781                 *p++ = s[i];
7782                 break;
7783         }
7784     }
7785     len = p - dest;
7786     *p = '\0';
7787     return len;
7788 }
7789 
JimParserGetTokenObj(Jim_Interp * interp,struct JimParserCtx * pc)7790 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
7791 {
7792     const char *start, *end;
7793     char *token;
7794     int len;
7795 
7796     start = pc->tstart;
7797     end = pc->tend;
7798     if (start > end) {
7799         len = 0;
7800         token = Jim_Alloc(1);
7801         token[0] = '\0';
7802     }
7803     else {
7804         len = (end - start) + 1;
7805         token = Jim_Alloc(len + 1);
7806         if (pc->tt != JIM_TT_ESC) {
7807 
7808             memcpy(token, start, len);
7809             token[len] = '\0';
7810         }
7811         else {
7812 
7813             len = JimEscape(token, start, len);
7814         }
7815     }
7816 
7817     return Jim_NewStringObjNoAlloc(interp, token, len);
7818 }
7819 
7820 static int JimParseListSep(struct JimParserCtx *pc);
7821 static int JimParseListStr(struct JimParserCtx *pc);
7822 static int JimParseListQuote(struct JimParserCtx *pc);
7823 
JimParseList(struct JimParserCtx * pc)7824 static int JimParseList(struct JimParserCtx *pc)
7825 {
7826     if (isspace(UCHAR(*pc->p))) {
7827         return JimParseListSep(pc);
7828     }
7829     switch (*pc->p) {
7830         case '"':
7831             return JimParseListQuote(pc);
7832 
7833         case '{':
7834             return JimParseBrace(pc);
7835 
7836         default:
7837             if (pc->len) {
7838                 return JimParseListStr(pc);
7839             }
7840             break;
7841     }
7842 
7843     pc->tstart = pc->tend = pc->p;
7844     pc->tline = pc->linenr;
7845     pc->tt = JIM_TT_EOL;
7846     pc->eof = 1;
7847     return JIM_OK;
7848 }
7849 
JimParseListSep(struct JimParserCtx * pc)7850 static int JimParseListSep(struct JimParserCtx *pc)
7851 {
7852     pc->tstart = pc->p;
7853     pc->tline = pc->linenr;
7854     while (isspace(UCHAR(*pc->p))) {
7855         if (*pc->p == '\n') {
7856             pc->linenr++;
7857         }
7858         pc->p++;
7859         pc->len--;
7860     }
7861     pc->tend = pc->p - 1;
7862     pc->tt = JIM_TT_SEP;
7863     return JIM_OK;
7864 }
7865 
JimParseListQuote(struct JimParserCtx * pc)7866 static int JimParseListQuote(struct JimParserCtx *pc)
7867 {
7868     pc->p++;
7869     pc->len--;
7870 
7871     pc->tstart = pc->p;
7872     pc->tline = pc->linenr;
7873     pc->tt = JIM_TT_STR;
7874 
7875     while (pc->len) {
7876         switch (*pc->p) {
7877             case '\\':
7878                 pc->tt = JIM_TT_ESC;
7879                 if (--pc->len == 0) {
7880 
7881                     pc->tend = pc->p;
7882                     return JIM_OK;
7883                 }
7884                 pc->p++;
7885                 break;
7886             case '\n':
7887                 pc->linenr++;
7888                 break;
7889             case '"':
7890                 pc->tend = pc->p - 1;
7891                 pc->p++;
7892                 pc->len--;
7893                 return JIM_OK;
7894         }
7895         pc->p++;
7896         pc->len--;
7897     }
7898 
7899     pc->tend = pc->p - 1;
7900     return JIM_OK;
7901 }
7902 
JimParseListStr(struct JimParserCtx * pc)7903 static int JimParseListStr(struct JimParserCtx *pc)
7904 {
7905     pc->tstart = pc->p;
7906     pc->tline = pc->linenr;
7907     pc->tt = JIM_TT_STR;
7908 
7909     while (pc->len) {
7910         if (isspace(UCHAR(*pc->p))) {
7911             pc->tend = pc->p - 1;
7912             return JIM_OK;
7913         }
7914         if (*pc->p == '\\') {
7915             if (--pc->len == 0) {
7916 
7917                 pc->tend = pc->p;
7918                 return JIM_OK;
7919             }
7920             pc->tt = JIM_TT_ESC;
7921             pc->p++;
7922         }
7923         pc->p++;
7924         pc->len--;
7925     }
7926     pc->tend = pc->p - 1;
7927     return JIM_OK;
7928 }
7929 
7930 
7931 
Jim_NewObj(Jim_Interp * interp)7932 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
7933 {
7934     Jim_Obj *objPtr;
7935 
7936 
7937     if (interp->freeList != NULL) {
7938 
7939         objPtr = interp->freeList;
7940         interp->freeList = objPtr->nextObjPtr;
7941     }
7942     else {
7943 
7944         objPtr = Jim_Alloc(sizeof(*objPtr));
7945     }
7946 
7947     objPtr->refCount = 0;
7948 
7949 
7950     objPtr->prevObjPtr = NULL;
7951     objPtr->nextObjPtr = interp->liveList;
7952     if (interp->liveList)
7953         interp->liveList->prevObjPtr = objPtr;
7954     interp->liveList = objPtr;
7955 
7956     return objPtr;
7957 }
7958 
Jim_FreeObj(Jim_Interp * interp,Jim_Obj * objPtr)7959 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
7960 {
7961 
7962     JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
7963         objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
7964 
7965 
7966     Jim_FreeIntRep(interp, objPtr);
7967 
7968     if (objPtr->bytes != NULL) {
7969         if (objPtr->bytes != JimEmptyStringRep)
7970             Jim_Free(objPtr->bytes);
7971     }
7972 
7973     if (objPtr->prevObjPtr)
7974         objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
7975     if (objPtr->nextObjPtr)
7976         objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
7977     if (interp->liveList == objPtr)
7978         interp->liveList = objPtr->nextObjPtr;
7979 #ifdef JIM_DISABLE_OBJECT_POOL
7980     Jim_Free(objPtr);
7981 #else
7982 
7983     objPtr->prevObjPtr = NULL;
7984     objPtr->nextObjPtr = interp->freeList;
7985     if (interp->freeList)
7986         interp->freeList->prevObjPtr = objPtr;
7987     interp->freeList = objPtr;
7988     objPtr->refCount = -1;
7989 #endif
7990 }
7991 
7992 
Jim_InvalidateStringRep(Jim_Obj * objPtr)7993 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
7994 {
7995     if (objPtr->bytes != NULL) {
7996         if (objPtr->bytes != JimEmptyStringRep)
7997             Jim_Free(objPtr->bytes);
7998     }
7999     objPtr->bytes = NULL;
8000 }
8001 
8002 
Jim_DuplicateObj(Jim_Interp * interp,Jim_Obj * objPtr)8003 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
8004 {
8005     Jim_Obj *dupPtr;
8006 
8007     dupPtr = Jim_NewObj(interp);
8008     if (objPtr->bytes == NULL) {
8009 
8010         dupPtr->bytes = NULL;
8011     }
8012     else if (objPtr->length == 0) {
8013 
8014         dupPtr->bytes = JimEmptyStringRep;
8015         dupPtr->length = 0;
8016         dupPtr->typePtr = NULL;
8017         return dupPtr;
8018     }
8019     else {
8020         dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
8021         dupPtr->length = objPtr->length;
8022 
8023         memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
8024     }
8025 
8026 
8027     dupPtr->typePtr = objPtr->typePtr;
8028     if (objPtr->typePtr != NULL) {
8029         if (objPtr->typePtr->dupIntRepProc == NULL) {
8030             dupPtr->internalRep = objPtr->internalRep;
8031         }
8032         else {
8033 
8034             objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
8035         }
8036     }
8037     return dupPtr;
8038 }
8039 
Jim_GetString(Jim_Obj * objPtr,int * lenPtr)8040 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
8041 {
8042     if (objPtr->bytes == NULL) {
8043 
8044         JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8045         objPtr->typePtr->updateStringProc(objPtr);
8046     }
8047     if (lenPtr)
8048         *lenPtr = objPtr->length;
8049     return objPtr->bytes;
8050 }
8051 
8052 
Jim_Length(Jim_Obj * objPtr)8053 int Jim_Length(Jim_Obj *objPtr)
8054 {
8055     if (objPtr->bytes == NULL) {
8056 
8057         JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8058         objPtr->typePtr->updateStringProc(objPtr);
8059     }
8060     return objPtr->length;
8061 }
8062 
8063 
Jim_String(Jim_Obj * objPtr)8064 const char *Jim_String(Jim_Obj *objPtr)
8065 {
8066     if (objPtr->bytes == NULL) {
8067 
8068         JimPanic((objPtr->typePtr == NULL, "UpdateStringProc called against typeless value."));
8069         JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8070         objPtr->typePtr->updateStringProc(objPtr);
8071     }
8072     return objPtr->bytes;
8073 }
8074 
JimSetStringBytes(Jim_Obj * objPtr,const char * str)8075 static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
8076 {
8077     objPtr->bytes = Jim_StrDup(str);
8078     objPtr->length = strlen(str);
8079 }
8080 
8081 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8082 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8083 
8084 static const Jim_ObjType dictSubstObjType = {
8085     "dict-substitution",
8086     FreeDictSubstInternalRep,
8087     DupDictSubstInternalRep,
8088     NULL,
8089     JIM_TYPE_NONE,
8090 };
8091 
FreeInterpolatedInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)8092 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8093 {
8094     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
8095 }
8096 
8097 static const Jim_ObjType interpolatedObjType = {
8098     "interpolated",
8099     FreeInterpolatedInternalRep,
8100     NULL,
8101     NULL,
8102     JIM_TYPE_NONE,
8103 };
8104 
8105 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8106 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8107 
8108 static const Jim_ObjType stringObjType = {
8109     "string",
8110     NULL,
8111     DupStringInternalRep,
8112     NULL,
8113     JIM_TYPE_REFERENCES,
8114 };
8115 
DupStringInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)8116 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8117 {
8118     JIM_NOTUSED(interp);
8119 
8120     dupPtr->internalRep.strValue.maxLength = srcPtr->length;
8121     dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
8122 }
8123 
SetStringFromAny(Jim_Interp * interp,Jim_Obj * objPtr)8124 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
8125 {
8126     if (objPtr->typePtr != &stringObjType) {
8127 
8128         if (objPtr->bytes == NULL) {
8129 
8130             JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8131             objPtr->typePtr->updateStringProc(objPtr);
8132         }
8133 
8134         Jim_FreeIntRep(interp, objPtr);
8135 
8136         objPtr->typePtr = &stringObjType;
8137         objPtr->internalRep.strValue.maxLength = objPtr->length;
8138 
8139         objPtr->internalRep.strValue.charLength = -1;
8140     }
8141     return JIM_OK;
8142 }
8143 
Jim_Utf8Length(Jim_Interp * interp,Jim_Obj * objPtr)8144 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
8145 {
8146 #ifdef JIM_UTF8
8147     SetStringFromAny(interp, objPtr);
8148 
8149     if (objPtr->internalRep.strValue.charLength < 0) {
8150         objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
8151     }
8152     return objPtr->internalRep.strValue.charLength;
8153 #else
8154     return Jim_Length(objPtr);
8155 #endif
8156 }
8157 
8158 
Jim_NewStringObj(Jim_Interp * interp,const char * s,int len)8159 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
8160 {
8161     Jim_Obj *objPtr = Jim_NewObj(interp);
8162 
8163 
8164     if (len == -1)
8165         len = strlen(s);
8166 
8167     if (len == 0) {
8168         objPtr->bytes = JimEmptyStringRep;
8169     }
8170     else {
8171         objPtr->bytes = Jim_Alloc(len + 1);
8172         memcpy(objPtr->bytes, s, len);
8173         objPtr->bytes[len] = '\0';
8174     }
8175     objPtr->length = len;
8176 
8177 
8178     objPtr->typePtr = NULL;
8179     return objPtr;
8180 }
8181 
8182 
Jim_NewStringObjUtf8(Jim_Interp * interp,const char * s,int charlen)8183 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
8184 {
8185 #ifdef JIM_UTF8
8186 
8187     int bytelen = utf8_index(s, charlen);
8188 
8189     Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
8190 
8191 
8192     objPtr->typePtr = &stringObjType;
8193     objPtr->internalRep.strValue.maxLength = bytelen;
8194     objPtr->internalRep.strValue.charLength = charlen;
8195 
8196     return objPtr;
8197 #else
8198     return Jim_NewStringObj(interp, s, charlen);
8199 #endif
8200 }
8201 
Jim_NewStringObjNoAlloc(Jim_Interp * interp,char * s,int len)8202 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
8203 {
8204     Jim_Obj *objPtr = Jim_NewObj(interp);
8205 
8206     objPtr->bytes = s;
8207     objPtr->length = (len == -1) ? strlen(s) : len;
8208     objPtr->typePtr = NULL;
8209     return objPtr;
8210 }
8211 
StringAppendString(Jim_Obj * objPtr,const char * str,int len)8212 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
8213 {
8214     int needlen;
8215 
8216     if (len == -1)
8217         len = strlen(str);
8218     needlen = objPtr->length + len;
8219     if (objPtr->internalRep.strValue.maxLength < needlen ||
8220         objPtr->internalRep.strValue.maxLength == 0) {
8221         needlen *= 2;
8222 
8223         if (needlen < 7) {
8224             needlen = 7;
8225         }
8226         if (objPtr->bytes == JimEmptyStringRep) {
8227             objPtr->bytes = Jim_Alloc(needlen + 1);
8228         }
8229         else {
8230             objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
8231         }
8232         objPtr->internalRep.strValue.maxLength = needlen;
8233     }
8234     memcpy(objPtr->bytes + objPtr->length, str, len);
8235     objPtr->bytes[objPtr->length + len] = '\0';
8236 
8237     if (objPtr->internalRep.strValue.charLength >= 0) {
8238 
8239         objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
8240     }
8241     objPtr->length += len;
8242 }
8243 
Jim_AppendString(Jim_Interp * interp,Jim_Obj * objPtr,const char * str,int len)8244 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
8245 {
8246     JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
8247     SetStringFromAny(interp, objPtr);
8248     StringAppendString(objPtr, str, len);
8249 }
8250 
Jim_AppendObj(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * appendObjPtr)8251 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
8252 {
8253     int len;
8254     const char *str = Jim_GetString(appendObjPtr, &len);
8255     Jim_AppendString(interp, objPtr, str, len);
8256 }
8257 
Jim_AppendStrings(Jim_Interp * interp,Jim_Obj * objPtr,...)8258 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
8259 {
8260     va_list ap;
8261 
8262     SetStringFromAny(interp, objPtr);
8263     va_start(ap, objPtr);
8264     while (1) {
8265         const char *s = va_arg(ap, const char *);
8266 
8267         if (s == NULL)
8268             break;
8269         Jim_AppendString(interp, objPtr, s, -1);
8270     }
8271     va_end(ap);
8272 }
8273 
Jim_StringEqObj(Jim_Obj * aObjPtr,Jim_Obj * bObjPtr)8274 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
8275 {
8276     if (aObjPtr == bObjPtr) {
8277         return 1;
8278     }
8279     else {
8280         int Alen, Blen;
8281         const char *sA = Jim_GetString(aObjPtr, &Alen);
8282         const char *sB = Jim_GetString(bObjPtr, &Blen);
8283 
8284         return Alen == Blen && memcmp(sA, sB, Alen) == 0;
8285     }
8286 }
8287 
Jim_StringMatchObj(Jim_Interp * interp,Jim_Obj * patternObjPtr,Jim_Obj * objPtr,int nocase)8288 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
8289 {
8290     return JimGlobMatch(Jim_String(patternObjPtr), Jim_String(objPtr), nocase);
8291 }
8292 
Jim_StringCompareObj(Jim_Interp * interp,Jim_Obj * firstObjPtr,Jim_Obj * secondObjPtr,int nocase)8293 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8294 {
8295     int l1, l2;
8296     const char *s1 = Jim_GetString(firstObjPtr, &l1);
8297     const char *s2 = Jim_GetString(secondObjPtr, &l2);
8298 
8299     if (nocase) {
8300 
8301         return JimStringCompareLen(s1, s2, -1, nocase);
8302     }
8303     return JimStringCompare(s1, l1, s2, l2);
8304 }
8305 
Jim_StringCompareLenObj(Jim_Interp * interp,Jim_Obj * firstObjPtr,Jim_Obj * secondObjPtr,int nocase)8306 int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8307 {
8308     const char *s1 = Jim_String(firstObjPtr);
8309     const char *s2 = Jim_String(secondObjPtr);
8310 
8311     return JimStringCompareLen(s1, s2, Jim_Utf8Length(interp, firstObjPtr), nocase);
8312 }
8313 
JimRelToAbsIndex(int len,int idx)8314 static int JimRelToAbsIndex(int len, int idx)
8315 {
8316     if (idx < 0)
8317         return len + idx;
8318     return idx;
8319 }
8320 
JimRelToAbsRange(int len,int * firstPtr,int * lastPtr,int * rangeLenPtr)8321 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
8322 {
8323     int rangeLen;
8324 
8325     if (*firstPtr > *lastPtr) {
8326         rangeLen = 0;
8327     }
8328     else {
8329         rangeLen = *lastPtr - *firstPtr + 1;
8330         if (rangeLen) {
8331             if (*firstPtr < 0) {
8332                 rangeLen += *firstPtr;
8333                 *firstPtr = 0;
8334             }
8335             if (*lastPtr >= len) {
8336                 rangeLen -= (*lastPtr - (len - 1));
8337                 *lastPtr = len - 1;
8338             }
8339         }
8340     }
8341     if (rangeLen < 0)
8342         rangeLen = 0;
8343 
8344     *rangeLenPtr = rangeLen;
8345 }
8346 
JimStringGetRange(Jim_Interp * interp,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr,int len,int * first,int * last,int * range)8347 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
8348     int len, int *first, int *last, int *range)
8349 {
8350     if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
8351         return JIM_ERR;
8352     }
8353     if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
8354         return JIM_ERR;
8355     }
8356     *first = JimRelToAbsIndex(len, *first);
8357     *last = JimRelToAbsIndex(len, *last);
8358     JimRelToAbsRange(len, first, last, range);
8359     return JIM_OK;
8360 }
8361 
Jim_StringByteRangeObj(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr)8362 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
8363     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8364 {
8365     int first, last;
8366     const char *str;
8367     int rangeLen;
8368     int bytelen;
8369 
8370     str = Jim_GetString(strObjPtr, &bytelen);
8371 
8372     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
8373         return NULL;
8374     }
8375 
8376     if (first == 0 && rangeLen == bytelen) {
8377         return strObjPtr;
8378     }
8379     return Jim_NewStringObj(interp, str + first, rangeLen);
8380 }
8381 
Jim_StringRangeObj(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr)8382 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
8383     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8384 {
8385 #ifdef JIM_UTF8
8386     int first, last;
8387     const char *str;
8388     int len, rangeLen;
8389     int bytelen;
8390 
8391     str = Jim_GetString(strObjPtr, &bytelen);
8392     len = Jim_Utf8Length(interp, strObjPtr);
8393 
8394     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8395         return NULL;
8396     }
8397 
8398     if (first == 0 && rangeLen == len) {
8399         return strObjPtr;
8400     }
8401     if (len == bytelen) {
8402 
8403         return Jim_NewStringObj(interp, str + first, rangeLen);
8404     }
8405     return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
8406 #else
8407     return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
8408 #endif
8409 }
8410 
JimStringReplaceObj(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr,Jim_Obj * newStrObj)8411 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
8412     Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
8413 {
8414     int first, last;
8415     const char *str;
8416     int len, rangeLen;
8417     Jim_Obj *objPtr;
8418 
8419     len = Jim_Utf8Length(interp, strObjPtr);
8420 
8421     if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8422         return NULL;
8423     }
8424 
8425     if (last < first) {
8426         return strObjPtr;
8427     }
8428 
8429     str = Jim_String(strObjPtr);
8430 
8431 
8432     objPtr = Jim_NewStringObjUtf8(interp, str, first);
8433 
8434 
8435     if (newStrObj) {
8436         Jim_AppendObj(interp, objPtr, newStrObj);
8437     }
8438 
8439 
8440     Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
8441 
8442     return objPtr;
8443 }
8444 
JimStrCopyUpperLower(char * dest,const char * str,int uc)8445 static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
8446 {
8447     while (*str) {
8448         int c;
8449         str += utf8_tounicode(str, &c);
8450         dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c));
8451     }
8452     *dest = 0;
8453 }
8454 
JimStringToLower(Jim_Interp * interp,Jim_Obj * strObjPtr)8455 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
8456 {
8457     char *buf;
8458     int len;
8459     const char *str;
8460 
8461     SetStringFromAny(interp, strObjPtr);
8462 
8463     str = Jim_GetString(strObjPtr, &len);
8464 
8465 #ifdef JIM_UTF8
8466     len *= 2;
8467 #endif
8468     buf = Jim_Alloc(len + 1);
8469     JimStrCopyUpperLower(buf, str, 0);
8470     return Jim_NewStringObjNoAlloc(interp, buf, -1);
8471 }
8472 
JimStringToUpper(Jim_Interp * interp,Jim_Obj * strObjPtr)8473 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
8474 {
8475     char *buf;
8476     const char *str;
8477     int len;
8478 
8479     if (strObjPtr->typePtr != &stringObjType) {
8480         SetStringFromAny(interp, strObjPtr);
8481     }
8482 
8483     str = Jim_GetString(strObjPtr, &len);
8484 
8485 #ifdef JIM_UTF8
8486     len *= 2;
8487 #endif
8488     buf = Jim_Alloc(len + 1);
8489     JimStrCopyUpperLower(buf, str, 1);
8490     return Jim_NewStringObjNoAlloc(interp, buf, -1);
8491 }
8492 
JimStringToTitle(Jim_Interp * interp,Jim_Obj * strObjPtr)8493 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
8494 {
8495     char *buf, *p;
8496     int len;
8497     int c;
8498     const char *str;
8499 
8500     str = Jim_GetString(strObjPtr, &len);
8501     if (len == 0) {
8502         return strObjPtr;
8503     }
8504 #ifdef JIM_UTF8
8505     len *= 2;
8506 #endif
8507     buf = p = Jim_Alloc(len + 1);
8508 
8509     str += utf8_tounicode(str, &c);
8510     p += utf8_getchars(p, utf8_title(c));
8511 
8512     JimStrCopyUpperLower(p, str, 0);
8513 
8514     return Jim_NewStringObjNoAlloc(interp, buf, -1);
8515 }
8516 
utf8_memchr(const char * str,int len,int c)8517 static const char *utf8_memchr(const char *str, int len, int c)
8518 {
8519 #ifdef JIM_UTF8
8520     while (len) {
8521         int sc;
8522         int n = utf8_tounicode(str, &sc);
8523         if (sc == c) {
8524             return str;
8525         }
8526         str += n;
8527         len -= n;
8528     }
8529     return NULL;
8530 #else
8531     return memchr(str, c, len);
8532 #endif
8533 }
8534 
JimFindTrimLeft(const char * str,int len,const char * trimchars,int trimlen)8535 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
8536 {
8537     while (len) {
8538         int c;
8539         int n = utf8_tounicode(str, &c);
8540 
8541         if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8542 
8543             break;
8544         }
8545         str += n;
8546         len -= n;
8547     }
8548     return str;
8549 }
8550 
JimFindTrimRight(const char * str,int len,const char * trimchars,int trimlen)8551 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
8552 {
8553     str += len;
8554 
8555     while (len) {
8556         int c;
8557         int n = utf8_prev_len(str, len);
8558 
8559         len -= n;
8560         str -= n;
8561 
8562         n = utf8_tounicode(str, &c);
8563 
8564         if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8565             return str + n;
8566         }
8567     }
8568 
8569     return NULL;
8570 }
8571 
8572 static const char default_trim_chars[] = " \t\n\r";
8573 
8574 static int default_trim_chars_len = sizeof(default_trim_chars);
8575 
JimStringTrimLeft(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * trimcharsObjPtr)8576 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8577 {
8578     int len;
8579     const char *str = Jim_GetString(strObjPtr, &len);
8580     const char *trimchars = default_trim_chars;
8581     int trimcharslen = default_trim_chars_len;
8582     const char *newstr;
8583 
8584     if (trimcharsObjPtr) {
8585         trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8586     }
8587 
8588     newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
8589     if (newstr == str) {
8590         return strObjPtr;
8591     }
8592 
8593     return Jim_NewStringObj(interp, newstr, len - (newstr - str));
8594 }
8595 
JimStringTrimRight(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * trimcharsObjPtr)8596 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8597 {
8598     int len;
8599     const char *trimchars = default_trim_chars;
8600     int trimcharslen = default_trim_chars_len;
8601     const char *nontrim;
8602 
8603     if (trimcharsObjPtr) {
8604         trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8605     }
8606 
8607     SetStringFromAny(interp, strObjPtr);
8608 
8609     len = Jim_Length(strObjPtr);
8610     nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
8611 
8612     if (nontrim == NULL) {
8613 
8614         return Jim_NewEmptyStringObj(interp);
8615     }
8616     if (nontrim == strObjPtr->bytes + len) {
8617 
8618         return strObjPtr;
8619     }
8620 
8621     if (Jim_IsShared(strObjPtr)) {
8622         strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
8623     }
8624     else {
8625 
8626         strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
8627         strObjPtr->length = (nontrim - strObjPtr->bytes);
8628     }
8629 
8630     return strObjPtr;
8631 }
8632 
JimStringTrim(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * trimcharsObjPtr)8633 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8634 {
8635 
8636     Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
8637 
8638 
8639     strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
8640 
8641 
8642     if (objPtr != strObjPtr && objPtr->refCount == 0) {
8643 
8644         Jim_FreeNewObj(interp, objPtr);
8645     }
8646 
8647     return strObjPtr;
8648 }
8649 
8650 
8651 #ifdef HAVE_ISASCII
8652 #define jim_isascii isascii
8653 #else
jim_isascii(int c)8654 static int jim_isascii(int c)
8655 {
8656     return !(c & ~0x7f);
8657 }
8658 #endif
8659 
JimStringIs(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * strClass,int strict)8660 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
8661 {
8662     static const char * const strclassnames[] = {
8663         "integer", "alpha", "alnum", "ascii", "digit",
8664         "double", "lower", "upper", "space", "xdigit",
8665         "control", "print", "graph", "punct", "boolean",
8666         NULL
8667     };
8668     enum {
8669         STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
8670         STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
8671         STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN,
8672     };
8673     int strclass;
8674     int len;
8675     int i;
8676     const char *str;
8677     int (*isclassfunc)(int c) = NULL;
8678 
8679     if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
8680         return JIM_ERR;
8681     }
8682 
8683     str = Jim_GetString(strObjPtr, &len);
8684     if (len == 0) {
8685         Jim_SetResultBool(interp, !strict);
8686         return JIM_OK;
8687     }
8688 
8689     switch (strclass) {
8690         case STR_IS_INTEGER:
8691             {
8692                 jim_wide w;
8693                 Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
8694                 return JIM_OK;
8695             }
8696 
8697         case STR_IS_DOUBLE:
8698             {
8699                 double d;
8700                 Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
8701                 return JIM_OK;
8702             }
8703 
8704         case STR_IS_BOOLEAN:
8705             {
8706                 int b;
8707                 Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK);
8708                 return JIM_OK;
8709             }
8710 
8711         case STR_IS_ALPHA: isclassfunc = isalpha; break;
8712         case STR_IS_ALNUM: isclassfunc = isalnum; break;
8713         case STR_IS_ASCII: isclassfunc = jim_isascii; break;
8714         case STR_IS_DIGIT: isclassfunc = isdigit; break;
8715         case STR_IS_LOWER: isclassfunc = islower; break;
8716         case STR_IS_UPPER: isclassfunc = isupper; break;
8717         case STR_IS_SPACE: isclassfunc = isspace; break;
8718         case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
8719         case STR_IS_CONTROL: isclassfunc = iscntrl; break;
8720         case STR_IS_PRINT: isclassfunc = isprint; break;
8721         case STR_IS_GRAPH: isclassfunc = isgraph; break;
8722         case STR_IS_PUNCT: isclassfunc = ispunct; break;
8723         default:
8724             return JIM_ERR;
8725     }
8726 
8727     for (i = 0; i < len; i++) {
8728         if (!isclassfunc(UCHAR(str[i]))) {
8729             Jim_SetResultBool(interp, 0);
8730             return JIM_OK;
8731         }
8732     }
8733     Jim_SetResultBool(interp, 1);
8734     return JIM_OK;
8735 }
8736 
8737 
8738 
8739 static const Jim_ObjType comparedStringObjType = {
8740     "compared-string",
8741     NULL,
8742     NULL,
8743     NULL,
8744     JIM_TYPE_REFERENCES,
8745 };
8746 
Jim_CompareStringImmediate(Jim_Interp * interp,Jim_Obj * objPtr,const char * str)8747 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
8748 {
8749     if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
8750         return 1;
8751     }
8752     else {
8753         const char *objStr = Jim_String(objPtr);
8754 
8755         if (strcmp(str, objStr) != 0)
8756             return 0;
8757 
8758         if (objPtr->typePtr != &comparedStringObjType) {
8759             Jim_FreeIntRep(interp, objPtr);
8760             objPtr->typePtr = &comparedStringObjType;
8761         }
8762         objPtr->internalRep.ptr = (char *)str;
8763         return 1;
8764     }
8765 }
8766 
qsortCompareStringPointers(const void * a,const void * b)8767 static int qsortCompareStringPointers(const void *a, const void *b)
8768 {
8769     char *const *sa = (char *const *)a;
8770     char *const *sb = (char *const *)b;
8771 
8772     return strcmp(*sa, *sb);
8773 }
8774 
8775 
8776 
8777 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8778 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8779 
8780 static const Jim_ObjType sourceObjType = {
8781     "source",
8782     FreeSourceInternalRep,
8783     DupSourceInternalRep,
8784     NULL,
8785     JIM_TYPE_REFERENCES,
8786 };
8787 
FreeSourceInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)8788 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8789 {
8790     Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
8791 }
8792 
DupSourceInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)8793 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8794 {
8795     dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
8796     Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
8797 }
8798 
JimSetSourceInfo(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * fileNameObj,int lineNumber)8799 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
8800     Jim_Obj *fileNameObj, int lineNumber)
8801 {
8802     JimPanic((Jim_IsShared(objPtr), "JimSetSourceInfo called with shared object"));
8803     JimPanic((objPtr->typePtr != NULL, "JimSetSourceInfo called with typed object"));
8804     Jim_IncrRefCount(fileNameObj);
8805     objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
8806     objPtr->internalRep.sourceValue.lineNumber = lineNumber;
8807     objPtr->typePtr = &sourceObjType;
8808 }
8809 
8810 static const Jim_ObjType scriptLineObjType = {
8811     "scriptline",
8812     NULL,
8813     NULL,
8814     NULL,
8815     JIM_NONE,
8816 };
8817 
JimNewScriptLineObj(Jim_Interp * interp,int argc,int line)8818 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
8819 {
8820     Jim_Obj *objPtr;
8821 
8822 #ifdef DEBUG_SHOW_SCRIPT
8823     char buf[100];
8824     snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
8825     objPtr = Jim_NewStringObj(interp, buf, -1);
8826 #else
8827     objPtr = Jim_NewEmptyStringObj(interp);
8828 #endif
8829     objPtr->typePtr = &scriptLineObjType;
8830     objPtr->internalRep.scriptLineValue.argc = argc;
8831     objPtr->internalRep.scriptLineValue.line = line;
8832 
8833     return objPtr;
8834 }
8835 
8836 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8837 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8838 
8839 static const Jim_ObjType scriptObjType = {
8840     "script",
8841     FreeScriptInternalRep,
8842     DupScriptInternalRep,
8843     NULL,
8844     JIM_TYPE_REFERENCES,
8845 };
8846 
8847 typedef struct ScriptToken
8848 {
8849     Jim_Obj *objPtr;
8850     int type;
8851 } ScriptToken;
8852 
8853 typedef struct ScriptObj
8854 {
8855     ScriptToken *token;
8856     Jim_Obj *fileNameObj;
8857     int len;
8858     int substFlags;
8859     int inUse;                  /* Used to share a ScriptObj. Currently
8860                                    only used by Jim_EvalObj() as protection against
8861                                    shimmering of the currently evaluated object. */
8862     int firstline;
8863     int linenr;
8864     int missing;
8865 } ScriptObj;
8866 
8867 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8868 static int JimParseCheckMissing(Jim_Interp *interp, int ch);
8869 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
8870 
FreeScriptInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)8871 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8872 {
8873     int i;
8874     struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
8875 
8876     if (--script->inUse != 0)
8877         return;
8878     for (i = 0; i < script->len; i++) {
8879         Jim_DecrRefCount(interp, script->token[i].objPtr);
8880     }
8881     Jim_Free(script->token);
8882     Jim_DecrRefCount(interp, script->fileNameObj);
8883     Jim_Free(script);
8884 }
8885 
DupScriptInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)8886 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8887 {
8888     JIM_NOTUSED(interp);
8889     JIM_NOTUSED(srcPtr);
8890 
8891     dupPtr->typePtr = NULL;
8892 }
8893 
8894 typedef struct
8895 {
8896     const char *token;
8897     int len;
8898     int type;
8899     int line;
8900 } ParseToken;
8901 
8902 typedef struct
8903 {
8904 
8905     ParseToken *list;
8906     int size;
8907     int count;
8908     ParseToken static_list[20];
8909 } ParseTokenList;
8910 
ScriptTokenListInit(ParseTokenList * tokenlist)8911 static void ScriptTokenListInit(ParseTokenList *tokenlist)
8912 {
8913     tokenlist->list = tokenlist->static_list;
8914     tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
8915     tokenlist->count = 0;
8916 }
8917 
ScriptTokenListFree(ParseTokenList * tokenlist)8918 static void ScriptTokenListFree(ParseTokenList *tokenlist)
8919 {
8920     if (tokenlist->list != tokenlist->static_list) {
8921         Jim_Free(tokenlist->list);
8922     }
8923 }
8924 
ScriptAddToken(ParseTokenList * tokenlist,const char * token,int len,int type,int line)8925 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
8926     int line)
8927 {
8928     ParseToken *t;
8929 
8930     if (tokenlist->count == tokenlist->size) {
8931 
8932         tokenlist->size *= 2;
8933         if (tokenlist->list != tokenlist->static_list) {
8934             tokenlist->list =
8935                 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
8936         }
8937         else {
8938 
8939             tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
8940             memcpy(tokenlist->list, tokenlist->static_list,
8941                 tokenlist->count * sizeof(*tokenlist->list));
8942         }
8943     }
8944     t = &tokenlist->list[tokenlist->count++];
8945     t->token = token;
8946     t->len = len;
8947     t->type = type;
8948     t->line = line;
8949 }
8950 
JimCountWordTokens(ParseToken * t)8951 static int JimCountWordTokens(ParseToken *t)
8952 {
8953     int expand = 1;
8954     int count = 0;
8955 
8956 
8957     if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
8958         if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
8959 
8960             expand = -1;
8961             t++;
8962         }
8963     }
8964 
8965 
8966     while (!TOKEN_IS_SEP(t->type)) {
8967         t++;
8968         count++;
8969     }
8970 
8971     return count * expand;
8972 }
8973 
JimMakeScriptObj(Jim_Interp * interp,const ParseToken * t)8974 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
8975 {
8976     Jim_Obj *objPtr;
8977 
8978     if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
8979 
8980         int len = t->len;
8981         char *str = Jim_Alloc(len + 1);
8982         len = JimEscape(str, t->token, len);
8983         objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
8984     }
8985     else {
8986         objPtr = Jim_NewStringObj(interp, t->token, t->len);
8987     }
8988     return objPtr;
8989 }
8990 
ScriptObjAddTokens(Jim_Interp * interp,struct ScriptObj * script,ParseTokenList * tokenlist)8991 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
8992     ParseTokenList *tokenlist)
8993 {
8994     int i;
8995     struct ScriptToken *token;
8996 
8997     int lineargs = 0;
8998 
8999     ScriptToken *linefirst;
9000     int count;
9001     int linenr;
9002 
9003 #ifdef DEBUG_SHOW_SCRIPT_TOKENS
9004     printf("==== Tokens ====\n");
9005     for (i = 0; i < tokenlist->count; i++) {
9006         printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
9007             tokenlist->list[i].len, tokenlist->list[i].token);
9008     }
9009 #endif
9010 
9011 
9012     count = tokenlist->count;
9013     for (i = 0; i < tokenlist->count; i++) {
9014         if (tokenlist->list[i].type == JIM_TT_EOL) {
9015             count++;
9016         }
9017     }
9018     linenr = script->firstline = tokenlist->list[0].line;
9019 
9020     token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
9021 
9022 
9023     linefirst = token++;
9024 
9025     for (i = 0; i < tokenlist->count; ) {
9026 
9027         int wordtokens;
9028 
9029 
9030         while (tokenlist->list[i].type == JIM_TT_SEP) {
9031             i++;
9032         }
9033 
9034         wordtokens = JimCountWordTokens(tokenlist->list + i);
9035 
9036         if (wordtokens == 0) {
9037 
9038             if (lineargs) {
9039                 linefirst->type = JIM_TT_LINE;
9040                 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
9041                 Jim_IncrRefCount(linefirst->objPtr);
9042 
9043 
9044                 lineargs = 0;
9045                 linefirst = token++;
9046             }
9047             i++;
9048             continue;
9049         }
9050         else if (wordtokens != 1) {
9051 
9052             token->type = JIM_TT_WORD;
9053             token->objPtr = Jim_NewIntObj(interp, wordtokens);
9054             Jim_IncrRefCount(token->objPtr);
9055             token++;
9056             if (wordtokens < 0) {
9057 
9058                 i++;
9059                 wordtokens = -wordtokens - 1;
9060                 lineargs--;
9061             }
9062         }
9063 
9064         if (lineargs == 0) {
9065 
9066             linenr = tokenlist->list[i].line;
9067         }
9068         lineargs++;
9069 
9070 
9071         while (wordtokens--) {
9072             const ParseToken *t = &tokenlist->list[i++];
9073 
9074             token->type = t->type;
9075             token->objPtr = JimMakeScriptObj(interp, t);
9076             Jim_IncrRefCount(token->objPtr);
9077 
9078             JimSetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
9079             token++;
9080         }
9081     }
9082 
9083     if (lineargs == 0) {
9084         token--;
9085     }
9086 
9087     script->len = token - script->token;
9088 
9089     JimPanic((script->len >= count, "allocated script array is too short"));
9090 
9091 #ifdef DEBUG_SHOW_SCRIPT
9092     printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj));
9093     for (i = 0; i < script->len; i++) {
9094         const ScriptToken *t = &script->token[i];
9095         printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
9096     }
9097 #endif
9098 
9099 }
9100 
Jim_ScriptIsComplete(Jim_Interp * interp,Jim_Obj * scriptObj,char * stateCharPtr)9101 int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
9102 {
9103     ScriptObj *script = JimGetScript(interp, scriptObj);
9104     if (stateCharPtr) {
9105         *stateCharPtr = script->missing;
9106     }
9107     return (script->missing == ' ');
9108 }
9109 
JimParseCheckMissing(Jim_Interp * interp,int ch)9110 static int JimParseCheckMissing(Jim_Interp *interp, int ch)
9111 {
9112     const char *msg;
9113 
9114     switch (ch) {
9115         case '\\':
9116         case ' ':
9117             return JIM_OK;
9118 
9119         case '[':
9120             msg = "unmatched \"[\"";
9121             break;
9122         case '{':
9123             msg = "missing close-brace";
9124             break;
9125         case '"':
9126         default:
9127             msg = "missing quote";
9128             break;
9129     }
9130 
9131     Jim_SetResultString(interp, msg, -1);
9132     return JIM_ERR;
9133 }
9134 
SubstObjAddTokens(Jim_Interp * interp,struct ScriptObj * script,ParseTokenList * tokenlist)9135 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
9136     ParseTokenList *tokenlist)
9137 {
9138     int i;
9139     struct ScriptToken *token;
9140 
9141     token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
9142 
9143     for (i = 0; i < tokenlist->count; i++) {
9144         const ParseToken *t = &tokenlist->list[i];
9145 
9146 
9147         token->type = t->type;
9148         token->objPtr = JimMakeScriptObj(interp, t);
9149         Jim_IncrRefCount(token->objPtr);
9150         token++;
9151     }
9152 
9153     script->len = i;
9154 }
9155 
JimSetScriptFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)9156 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9157 {
9158     int scriptTextLen;
9159     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
9160     struct JimParserCtx parser;
9161     struct ScriptObj *script;
9162     ParseTokenList tokenlist;
9163     int line = 1;
9164 
9165 
9166     if (objPtr->typePtr == &sourceObjType) {
9167         line = objPtr->internalRep.sourceValue.lineNumber;
9168     }
9169 
9170 
9171     ScriptTokenListInit(&tokenlist);
9172 
9173     JimParserInit(&parser, scriptText, scriptTextLen, line);
9174     while (!parser.eof) {
9175         JimParseScript(&parser);
9176         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
9177             parser.tline);
9178     }
9179 
9180 
9181     ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
9182 
9183 
9184     script = Jim_Alloc(sizeof(*script));
9185     memset(script, 0, sizeof(*script));
9186     script->inUse = 1;
9187     if (objPtr->typePtr == &sourceObjType) {
9188         script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
9189     }
9190     else {
9191         script->fileNameObj = interp->emptyObj;
9192     }
9193     Jim_IncrRefCount(script->fileNameObj);
9194     script->missing = parser.missing.ch;
9195     script->linenr = parser.missing.line;
9196 
9197     ScriptObjAddTokens(interp, script, &tokenlist);
9198 
9199 
9200     ScriptTokenListFree(&tokenlist);
9201 
9202 
9203     Jim_FreeIntRep(interp, objPtr);
9204     Jim_SetIntRepPtr(objPtr, script);
9205     objPtr->typePtr = &scriptObjType;
9206 }
9207 
9208 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script);
9209 
JimGetScript(Jim_Interp * interp,Jim_Obj * objPtr)9210 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
9211 {
9212     if (objPtr == interp->emptyObj) {
9213 
9214         objPtr = interp->nullScriptObj;
9215     }
9216 
9217     if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
9218         JimSetScriptFromAny(interp, objPtr);
9219     }
9220 
9221     return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
9222 }
9223 
JimScriptValid(Jim_Interp * interp,ScriptObj * script)9224 static int JimScriptValid(Jim_Interp *interp, ScriptObj *script)
9225 {
9226     if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
9227         JimAddErrorToStack(interp, script);
9228         return 0;
9229     }
9230     return 1;
9231 }
9232 
9233 
JimIncrCmdRefCount(Jim_Cmd * cmdPtr)9234 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
9235 {
9236     cmdPtr->inUse++;
9237 }
9238 
JimDecrCmdRefCount(Jim_Interp * interp,Jim_Cmd * cmdPtr)9239 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
9240 {
9241     if (--cmdPtr->inUse == 0) {
9242         if (cmdPtr->isproc) {
9243             Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
9244             Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
9245             Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9246             if (cmdPtr->u.proc.staticVars) {
9247                 Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
9248                 Jim_Free(cmdPtr->u.proc.staticVars);
9249             }
9250         }
9251         else {
9252 
9253             if (cmdPtr->u.native.delProc) {
9254                 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
9255             }
9256         }
9257         if (cmdPtr->prevCmd) {
9258 
9259             JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
9260         }
9261         Jim_Free(cmdPtr);
9262     }
9263 }
9264 
9265 
JimVariablesHTValDestructor(void * interp,void * val)9266 static void JimVariablesHTValDestructor(void *interp, void *val)
9267 {
9268     Jim_DecrRefCount(interp, ((Jim_Var *)val)->objPtr);
9269     Jim_Free(val);
9270 }
9271 
9272 static const Jim_HashTableType JimVariablesHashTableType = {
9273     JimStringCopyHTHashFunction,
9274     JimStringCopyHTDup,
9275     NULL,
9276     JimStringCopyHTKeyCompare,
9277     JimStringCopyHTKeyDestructor,
9278     JimVariablesHTValDestructor
9279 };
9280 
JimCommandsHT_ValDestructor(void * interp,void * val)9281 static void JimCommandsHT_ValDestructor(void *interp, void *val)
9282 {
9283     JimDecrCmdRefCount(interp, val);
9284 }
9285 
9286 static const Jim_HashTableType JimCommandsHashTableType = {
9287     JimStringCopyHTHashFunction,
9288     JimStringCopyHTDup,
9289     NULL,
9290     JimStringCopyHTKeyCompare,
9291     JimStringCopyHTKeyDestructor,
9292     JimCommandsHT_ValDestructor
9293 };
9294 
9295 
9296 
9297 #ifdef jim_ext_namespace
JimQualifyNameObj(Jim_Interp * interp,Jim_Obj * nsObj)9298 static Jim_Obj *JimQualifyNameObj(Jim_Interp *interp, Jim_Obj *nsObj)
9299 {
9300     const char *name = Jim_String(nsObj);
9301     if (name[0] == ':' && name[1] == ':') {
9302 
9303         while (*++name == ':') {
9304         }
9305         nsObj = Jim_NewStringObj(interp, name, -1);
9306     }
9307     else if (Jim_Length(interp->framePtr->nsObj)) {
9308 
9309         nsObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9310         Jim_AppendStrings(interp, nsObj, "::", name, NULL);
9311     }
9312     return nsObj;
9313 }
9314 
Jim_MakeGlobalNamespaceName(Jim_Interp * interp,Jim_Obj * nameObjPtr)9315 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9316 {
9317     Jim_Obj *resultObj;
9318 
9319     const char *name = Jim_String(nameObjPtr);
9320     if (name[0] == ':' && name[1] == ':') {
9321         return nameObjPtr;
9322     }
9323     Jim_IncrRefCount(nameObjPtr);
9324     resultObj = Jim_NewStringObj(interp, "::", -1);
9325     Jim_AppendObj(interp, resultObj, nameObjPtr);
9326     Jim_DecrRefCount(interp, nameObjPtr);
9327 
9328     return resultObj;
9329 }
9330 
JimQualifyName(Jim_Interp * interp,const char * name,Jim_Obj ** objPtrPtr)9331 static const char *JimQualifyName(Jim_Interp *interp, const char *name, Jim_Obj **objPtrPtr)
9332 {
9333     Jim_Obj *objPtr = interp->emptyObj;
9334 
9335     if (name[0] == ':' && name[1] == ':') {
9336 
9337         while (*++name == ':') {
9338         }
9339     }
9340     else if (Jim_Length(interp->framePtr->nsObj)) {
9341 
9342         objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9343         Jim_AppendStrings(interp, objPtr, "::", name, NULL);
9344         name = Jim_String(objPtr);
9345     }
9346     Jim_IncrRefCount(objPtr);
9347     *objPtrPtr = objPtr;
9348     return name;
9349 }
9350 
9351     #define JimFreeQualifiedName(INTERP, OBJ) Jim_DecrRefCount((INTERP), (OBJ))
9352 
9353 #else
9354 
9355     #define JimQualifyName(INTERP, NAME, DUMMY) (((NAME)[0] == ':' && (NAME)[1] == ':') ? (NAME) + 2 : (NAME))
9356     #define JimFreeQualifiedName(INTERP, DUMMY) (void)(DUMMY)
9357 
Jim_MakeGlobalNamespaceName(Jim_Interp * interp,Jim_Obj * nameObjPtr)9358 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9359 {
9360     return nameObjPtr;
9361 }
9362 #endif
9363 
JimCreateCommand(Jim_Interp * interp,const char * name,Jim_Cmd * cmd)9364 static int JimCreateCommand(Jim_Interp *interp, const char *name, Jim_Cmd *cmd)
9365 {
9366     Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, name);
9367     if (he) {
9368 
9369         Jim_InterpIncrProcEpoch(interp);
9370     }
9371 
9372     if (he && interp->local) {
9373 
9374         cmd->prevCmd = Jim_GetHashEntryVal(he);
9375         Jim_SetHashVal(&interp->commands, he, cmd);
9376     }
9377     else {
9378         if (he) {
9379 
9380             Jim_DeleteHashEntry(&interp->commands, name);
9381         }
9382 
9383         Jim_AddHashEntry(&interp->commands, name, cmd);
9384     }
9385     return JIM_OK;
9386 }
9387 
9388 
Jim_CreateCommand(Jim_Interp * interp,const char * cmdNameStr,Jim_CmdProc * cmdProc,void * privData,Jim_DelCmdProc * delProc)9389 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
9390     Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
9391 {
9392     Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
9393 
9394 
9395     memset(cmdPtr, 0, sizeof(*cmdPtr));
9396     cmdPtr->inUse = 1;
9397     cmdPtr->u.native.delProc = delProc;
9398     cmdPtr->u.native.cmdProc = cmdProc;
9399     cmdPtr->u.native.privData = privData;
9400 
9401     JimCreateCommand(interp, cmdNameStr, cmdPtr);
9402 
9403     return JIM_OK;
9404 }
9405 
JimCreateProcedureStatics(Jim_Interp * interp,Jim_Cmd * cmdPtr,Jim_Obj * staticsListObjPtr)9406 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
9407 {
9408     int len, i;
9409 
9410     len = Jim_ListLength(interp, staticsListObjPtr);
9411     if (len == 0) {
9412         return JIM_OK;
9413     }
9414 
9415     cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
9416     Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
9417     for (i = 0; i < len; i++) {
9418         Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
9419         Jim_Var *varPtr;
9420         int subLen;
9421 
9422         objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
9423 
9424         subLen = Jim_ListLength(interp, objPtr);
9425         if (subLen == 1 || subLen == 2) {
9426             nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
9427             if (subLen == 1) {
9428                 initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
9429                 if (initObjPtr == NULL) {
9430                     Jim_SetResultFormatted(interp,
9431                         "variable for initialization of static \"%#s\" not found in the local context",
9432                         nameObjPtr);
9433                     return JIM_ERR;
9434                 }
9435             }
9436             else {
9437                 initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
9438             }
9439             if (JimValidName(interp, "static variable", nameObjPtr) != JIM_OK) {
9440                 return JIM_ERR;
9441             }
9442 
9443             varPtr = Jim_Alloc(sizeof(*varPtr));
9444             varPtr->objPtr = initObjPtr;
9445             Jim_IncrRefCount(initObjPtr);
9446             varPtr->linkFramePtr = NULL;
9447             if (Jim_AddHashEntry(cmdPtr->u.proc.staticVars,
9448                 Jim_String(nameObjPtr), varPtr) != JIM_OK) {
9449                 Jim_SetResultFormatted(interp,
9450                     "static variable name \"%#s\" duplicated in statics list", nameObjPtr);
9451                 Jim_DecrRefCount(interp, initObjPtr);
9452                 Jim_Free(varPtr);
9453                 return JIM_ERR;
9454             }
9455         }
9456         else {
9457             Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
9458                 objPtr);
9459             return JIM_ERR;
9460         }
9461     }
9462     return JIM_OK;
9463 }
9464 
JimUpdateProcNamespace(Jim_Interp * interp,Jim_Cmd * cmdPtr,const char * cmdname)9465 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, const char *cmdname)
9466 {
9467 #ifdef jim_ext_namespace
9468     if (cmdPtr->isproc) {
9469 
9470         const char *pt = strrchr(cmdname, ':');
9471         if (pt && pt != cmdname && pt[-1] == ':') {
9472             Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9473             cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 1);
9474             Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9475 
9476             if (Jim_FindHashEntry(&interp->commands, pt + 1)) {
9477 
9478                 Jim_InterpIncrProcEpoch(interp);
9479             }
9480         }
9481     }
9482 #endif
9483 }
9484 
JimCreateProcedureCmd(Jim_Interp * interp,Jim_Obj * argListObjPtr,Jim_Obj * staticsListObjPtr,Jim_Obj * bodyObjPtr,Jim_Obj * nsObj)9485 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
9486     Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
9487 {
9488     Jim_Cmd *cmdPtr;
9489     int argListLen;
9490     int i;
9491 
9492     argListLen = Jim_ListLength(interp, argListObjPtr);
9493 
9494 
9495     cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
9496     memset(cmdPtr, 0, sizeof(*cmdPtr));
9497     cmdPtr->inUse = 1;
9498     cmdPtr->isproc = 1;
9499     cmdPtr->u.proc.argListObjPtr = argListObjPtr;
9500     cmdPtr->u.proc.argListLen = argListLen;
9501     cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
9502     cmdPtr->u.proc.argsPos = -1;
9503     cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
9504     cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
9505     Jim_IncrRefCount(argListObjPtr);
9506     Jim_IncrRefCount(bodyObjPtr);
9507     Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9508 
9509 
9510     if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
9511         goto err;
9512     }
9513 
9514 
9515 
9516     for (i = 0; i < argListLen; i++) {
9517         Jim_Obj *argPtr;
9518         Jim_Obj *nameObjPtr;
9519         Jim_Obj *defaultObjPtr;
9520         int len;
9521 
9522 
9523         argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
9524         len = Jim_ListLength(interp, argPtr);
9525         if (len == 0) {
9526             Jim_SetResultString(interp, "argument with no name", -1);
9527 err:
9528             JimDecrCmdRefCount(interp, cmdPtr);
9529             return NULL;
9530         }
9531         if (len > 2) {
9532             Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
9533             goto err;
9534         }
9535 
9536         if (len == 2) {
9537 
9538             nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
9539             defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
9540         }
9541         else {
9542 
9543             nameObjPtr = argPtr;
9544             defaultObjPtr = NULL;
9545         }
9546 
9547 
9548         if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
9549             if (cmdPtr->u.proc.argsPos >= 0) {
9550                 Jim_SetResultString(interp, "'args' specified more than once", -1);
9551                 goto err;
9552             }
9553             cmdPtr->u.proc.argsPos = i;
9554         }
9555         else {
9556             if (len == 2) {
9557                 cmdPtr->u.proc.optArity++;
9558             }
9559             else {
9560                 cmdPtr->u.proc.reqArity++;
9561             }
9562         }
9563 
9564         cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
9565         cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
9566     }
9567 
9568     return cmdPtr;
9569 }
9570 
Jim_DeleteCommand(Jim_Interp * interp,const char * name)9571 int Jim_DeleteCommand(Jim_Interp *interp, const char *name)
9572 {
9573     int ret = JIM_OK;
9574     Jim_Obj *qualifiedNameObj;
9575     const char *qualname = JimQualifyName(interp, name, &qualifiedNameObj);
9576 
9577     if (Jim_DeleteHashEntry(&interp->commands, qualname) == JIM_ERR) {
9578         Jim_SetResultFormatted(interp, "can't delete \"%s\": command doesn't exist", name);
9579         ret = JIM_ERR;
9580     }
9581     else {
9582         Jim_InterpIncrProcEpoch(interp);
9583     }
9584 
9585     JimFreeQualifiedName(interp, qualifiedNameObj);
9586 
9587     return ret;
9588 }
9589 
Jim_RenameCommand(Jim_Interp * interp,const char * oldName,const char * newName)9590 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, const char *newName)
9591 {
9592     int ret = JIM_ERR;
9593     Jim_HashEntry *he;
9594     Jim_Cmd *cmdPtr;
9595     Jim_Obj *qualifiedOldNameObj;
9596     Jim_Obj *qualifiedNewNameObj;
9597     const char *fqold;
9598     const char *fqnew;
9599 
9600     if (newName[0] == 0) {
9601         return Jim_DeleteCommand(interp, oldName);
9602     }
9603 
9604     fqold = JimQualifyName(interp, oldName, &qualifiedOldNameObj);
9605     fqnew = JimQualifyName(interp, newName, &qualifiedNewNameObj);
9606 
9607 
9608     he = Jim_FindHashEntry(&interp->commands, fqold);
9609     if (he == NULL) {
9610         Jim_SetResultFormatted(interp, "can't rename \"%s\": command doesn't exist", oldName);
9611     }
9612     else if (Jim_FindHashEntry(&interp->commands, fqnew)) {
9613         Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName);
9614     }
9615     else {
9616 
9617         cmdPtr = Jim_GetHashEntryVal(he);
9618         JimIncrCmdRefCount(cmdPtr);
9619         JimUpdateProcNamespace(interp, cmdPtr, fqnew);
9620         Jim_AddHashEntry(&interp->commands, fqnew, cmdPtr);
9621 
9622 
9623         Jim_DeleteHashEntry(&interp->commands, fqold);
9624 
9625 
9626         Jim_InterpIncrProcEpoch(interp);
9627 
9628         ret = JIM_OK;
9629     }
9630 
9631     JimFreeQualifiedName(interp, qualifiedOldNameObj);
9632     JimFreeQualifiedName(interp, qualifiedNewNameObj);
9633 
9634     return ret;
9635 }
9636 
9637 
FreeCommandInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)9638 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9639 {
9640     Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
9641 }
9642 
DupCommandInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)9643 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9644 {
9645     dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
9646     dupPtr->typePtr = srcPtr->typePtr;
9647     Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
9648 }
9649 
9650 static const Jim_ObjType commandObjType = {
9651     "command",
9652     FreeCommandInternalRep,
9653     DupCommandInternalRep,
9654     NULL,
9655     JIM_TYPE_REFERENCES,
9656 };
9657 
Jim_GetCommand(Jim_Interp * interp,Jim_Obj * objPtr,int flags)9658 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9659 {
9660     Jim_Cmd *cmd;
9661 
9662     if (objPtr->typePtr != &commandObjType ||
9663             objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch
9664 #ifdef jim_ext_namespace
9665             || !Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
9666 #endif
9667         ) {
9668 
9669 
9670 
9671         const char *name = Jim_String(objPtr);
9672         Jim_HashEntry *he;
9673 
9674         if (name[0] == ':' && name[1] == ':') {
9675             while (*++name == ':') {
9676             }
9677         }
9678 #ifdef jim_ext_namespace
9679         else if (Jim_Length(interp->framePtr->nsObj)) {
9680 
9681             Jim_Obj *nameObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9682             Jim_AppendStrings(interp, nameObj, "::", name, NULL);
9683             he = Jim_FindHashEntry(&interp->commands, Jim_String(nameObj));
9684             Jim_FreeNewObj(interp, nameObj);
9685             if (he) {
9686                 goto found;
9687             }
9688         }
9689 #endif
9690 
9691 
9692         he = Jim_FindHashEntry(&interp->commands, name);
9693         if (he == NULL) {
9694             if (flags & JIM_ERRMSG) {
9695                 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
9696             }
9697             return NULL;
9698         }
9699 #ifdef jim_ext_namespace
9700 found:
9701 #endif
9702         cmd = Jim_GetHashEntryVal(he);
9703 
9704 
9705         Jim_FreeIntRep(interp, objPtr);
9706         objPtr->typePtr = &commandObjType;
9707         objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
9708         objPtr->internalRep.cmdValue.cmdPtr = cmd;
9709         objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
9710         Jim_IncrRefCount(interp->framePtr->nsObj);
9711     }
9712     else {
9713         cmd = objPtr->internalRep.cmdValue.cmdPtr;
9714     }
9715     while (cmd->u.proc.upcall) {
9716         cmd = cmd->prevCmd;
9717     }
9718     return cmd;
9719 }
9720 
9721 
9722 
9723 #define JIM_DICT_SUGAR 100
9724 
9725 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
9726 
9727 static const Jim_ObjType variableObjType = {
9728     "variable",
9729     NULL,
9730     NULL,
9731     NULL,
9732     JIM_TYPE_REFERENCES,
9733 };
9734 
JimValidName(Jim_Interp * interp,const char * type,Jim_Obj * nameObjPtr)9735 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr)
9736 {
9737 
9738     if (nameObjPtr->typePtr != &variableObjType) {
9739         int len;
9740         const char *str = Jim_GetString(nameObjPtr, &len);
9741         if (memchr(str, '\0', len)) {
9742             Jim_SetResultFormatted(interp, "%s name contains embedded null", type);
9743             return JIM_ERR;
9744         }
9745     }
9746     return JIM_OK;
9747 }
9748 
SetVariableFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)9749 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9750 {
9751     const char *varName;
9752     Jim_CallFrame *framePtr;
9753     Jim_HashEntry *he;
9754     int global;
9755     int len;
9756 
9757 
9758     if (objPtr->typePtr == &variableObjType) {
9759         framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
9760         if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
9761 
9762             return JIM_OK;
9763         }
9764 
9765     }
9766     else if (objPtr->typePtr == &dictSubstObjType) {
9767         return JIM_DICT_SUGAR;
9768     }
9769     else if (JimValidName(interp, "variable", objPtr) != JIM_OK) {
9770         return JIM_ERR;
9771     }
9772 
9773 
9774     varName = Jim_GetString(objPtr, &len);
9775 
9776 
9777     if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
9778         return JIM_DICT_SUGAR;
9779     }
9780 
9781     if (varName[0] == ':' && varName[1] == ':') {
9782         while (*++varName == ':') {
9783         }
9784         global = 1;
9785         framePtr = interp->topFramePtr;
9786     }
9787     else {
9788         global = 0;
9789         framePtr = interp->framePtr;
9790     }
9791 
9792 
9793     he = Jim_FindHashEntry(&framePtr->vars, varName);
9794     if (he == NULL) {
9795         if (!global && framePtr->staticVars) {
9796 
9797             he = Jim_FindHashEntry(framePtr->staticVars, varName);
9798         }
9799         if (he == NULL) {
9800             return JIM_ERR;
9801         }
9802     }
9803 
9804 
9805     Jim_FreeIntRep(interp, objPtr);
9806     objPtr->typePtr = &variableObjType;
9807     objPtr->internalRep.varValue.callFrameId = framePtr->id;
9808     objPtr->internalRep.varValue.varPtr = Jim_GetHashEntryVal(he);
9809     objPtr->internalRep.varValue.global = global;
9810     return JIM_OK;
9811 }
9812 
9813 
9814 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
9815 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
9816 
JimCreateVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,Jim_Obj * valObjPtr)9817 static Jim_Var *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9818 {
9819     const char *name;
9820     Jim_CallFrame *framePtr;
9821     int global;
9822 
9823 
9824     Jim_Var *var = Jim_Alloc(sizeof(*var));
9825 
9826     var->objPtr = valObjPtr;
9827     Jim_IncrRefCount(valObjPtr);
9828     var->linkFramePtr = NULL;
9829 
9830     name = Jim_String(nameObjPtr);
9831     if (name[0] == ':' && name[1] == ':') {
9832         while (*++name == ':') {
9833         }
9834         framePtr = interp->topFramePtr;
9835         global = 1;
9836     }
9837     else {
9838         framePtr = interp->framePtr;
9839         global = 0;
9840     }
9841 
9842 
9843     Jim_AddHashEntry(&framePtr->vars, name, var);
9844 
9845 
9846     Jim_FreeIntRep(interp, nameObjPtr);
9847     nameObjPtr->typePtr = &variableObjType;
9848     nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
9849     nameObjPtr->internalRep.varValue.varPtr = var;
9850     nameObjPtr->internalRep.varValue.global = global;
9851 
9852     return var;
9853 }
9854 
9855 
Jim_SetVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,Jim_Obj * valObjPtr)9856 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9857 {
9858     int err;
9859     Jim_Var *var;
9860 
9861     switch (SetVariableFromAny(interp, nameObjPtr)) {
9862         case JIM_DICT_SUGAR:
9863             return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
9864 
9865         case JIM_ERR:
9866             if (JimValidName(interp, "variable", nameObjPtr) != JIM_OK) {
9867                 return JIM_ERR;
9868             }
9869             JimCreateVariable(interp, nameObjPtr, valObjPtr);
9870             break;
9871 
9872         case JIM_OK:
9873             var = nameObjPtr->internalRep.varValue.varPtr;
9874             if (var->linkFramePtr == NULL) {
9875                 Jim_IncrRefCount(valObjPtr);
9876                 Jim_DecrRefCount(interp, var->objPtr);
9877                 var->objPtr = valObjPtr;
9878             }
9879             else {
9880                 Jim_CallFrame *savedCallFrame;
9881 
9882                 savedCallFrame = interp->framePtr;
9883                 interp->framePtr = var->linkFramePtr;
9884                 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
9885                 interp->framePtr = savedCallFrame;
9886                 if (err != JIM_OK)
9887                     return err;
9888             }
9889     }
9890     return JIM_OK;
9891 }
9892 
Jim_SetVariableStr(Jim_Interp * interp,const char * name,Jim_Obj * objPtr)9893 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9894 {
9895     Jim_Obj *nameObjPtr;
9896     int result;
9897 
9898     nameObjPtr = Jim_NewStringObj(interp, name, -1);
9899     Jim_IncrRefCount(nameObjPtr);
9900     result = Jim_SetVariable(interp, nameObjPtr, objPtr);
9901     Jim_DecrRefCount(interp, nameObjPtr);
9902     return result;
9903 }
9904 
Jim_SetGlobalVariableStr(Jim_Interp * interp,const char * name,Jim_Obj * objPtr)9905 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9906 {
9907     Jim_CallFrame *savedFramePtr;
9908     int result;
9909 
9910     savedFramePtr = interp->framePtr;
9911     interp->framePtr = interp->topFramePtr;
9912     result = Jim_SetVariableStr(interp, name, objPtr);
9913     interp->framePtr = savedFramePtr;
9914     return result;
9915 }
9916 
Jim_SetVariableStrWithStr(Jim_Interp * interp,const char * name,const char * val)9917 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
9918 {
9919     Jim_Obj *nameObjPtr, *valObjPtr;
9920     int result;
9921 
9922     nameObjPtr = Jim_NewStringObj(interp, name, -1);
9923     valObjPtr = Jim_NewStringObj(interp, val, -1);
9924     Jim_IncrRefCount(nameObjPtr);
9925     Jim_IncrRefCount(valObjPtr);
9926     result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
9927     Jim_DecrRefCount(interp, nameObjPtr);
9928     Jim_DecrRefCount(interp, valObjPtr);
9929     return result;
9930 }
9931 
Jim_SetVariableLink(Jim_Interp * interp,Jim_Obj * nameObjPtr,Jim_Obj * targetNameObjPtr,Jim_CallFrame * targetCallFrame)9932 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
9933     Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
9934 {
9935     const char *varName;
9936     const char *targetName;
9937     Jim_CallFrame *framePtr;
9938     Jim_Var *varPtr;
9939 
9940 
9941     switch (SetVariableFromAny(interp, nameObjPtr)) {
9942         case JIM_DICT_SUGAR:
9943 
9944             Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
9945             return JIM_ERR;
9946 
9947         case JIM_OK:
9948             varPtr = nameObjPtr->internalRep.varValue.varPtr;
9949 
9950             if (varPtr->linkFramePtr == NULL) {
9951                 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
9952                 return JIM_ERR;
9953             }
9954 
9955 
9956             varPtr->linkFramePtr = NULL;
9957             break;
9958     }
9959 
9960 
9961 
9962     varName = Jim_String(nameObjPtr);
9963 
9964     if (varName[0] == ':' && varName[1] == ':') {
9965         while (*++varName == ':') {
9966         }
9967 
9968         framePtr = interp->topFramePtr;
9969     }
9970     else {
9971         framePtr = interp->framePtr;
9972     }
9973 
9974     targetName = Jim_String(targetNameObjPtr);
9975     if (targetName[0] == ':' && targetName[1] == ':') {
9976         while (*++targetName == ':') {
9977         }
9978         targetNameObjPtr = Jim_NewStringObj(interp, targetName, -1);
9979         targetCallFrame = interp->topFramePtr;
9980     }
9981     Jim_IncrRefCount(targetNameObjPtr);
9982 
9983     if (framePtr->level < targetCallFrame->level) {
9984         Jim_SetResultFormatted(interp,
9985             "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
9986             nameObjPtr);
9987         Jim_DecrRefCount(interp, targetNameObjPtr);
9988         return JIM_ERR;
9989     }
9990 
9991 
9992     if (framePtr == targetCallFrame) {
9993         Jim_Obj *objPtr = targetNameObjPtr;
9994 
9995 
9996         while (1) {
9997             if (strcmp(Jim_String(objPtr), varName) == 0) {
9998                 Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
9999                 Jim_DecrRefCount(interp, targetNameObjPtr);
10000                 return JIM_ERR;
10001             }
10002             if (SetVariableFromAny(interp, objPtr) != JIM_OK)
10003                 break;
10004             varPtr = objPtr->internalRep.varValue.varPtr;
10005             if (varPtr->linkFramePtr != targetCallFrame)
10006                 break;
10007             objPtr = varPtr->objPtr;
10008         }
10009     }
10010 
10011 
10012     Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
10013 
10014     nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
10015     Jim_DecrRefCount(interp, targetNameObjPtr);
10016     return JIM_OK;
10017 }
10018 
Jim_GetVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,int flags)10019 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10020 {
10021     switch (SetVariableFromAny(interp, nameObjPtr)) {
10022         case JIM_OK:{
10023                 Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr;
10024 
10025                 if (varPtr->linkFramePtr == NULL) {
10026                     return varPtr->objPtr;
10027                 }
10028                 else {
10029                     Jim_Obj *objPtr;
10030 
10031 
10032                     Jim_CallFrame *savedCallFrame = interp->framePtr;
10033 
10034                     interp->framePtr = varPtr->linkFramePtr;
10035                     objPtr = Jim_GetVariable(interp, varPtr->objPtr, flags);
10036                     interp->framePtr = savedCallFrame;
10037                     if (objPtr) {
10038                         return objPtr;
10039                     }
10040 
10041                 }
10042             }
10043             break;
10044 
10045         case JIM_DICT_SUGAR:
10046 
10047             return JimDictSugarGet(interp, nameObjPtr, flags);
10048     }
10049     if (flags & JIM_ERRMSG) {
10050         Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
10051     }
10052     return NULL;
10053 }
10054 
Jim_GetGlobalVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,int flags)10055 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10056 {
10057     Jim_CallFrame *savedFramePtr;
10058     Jim_Obj *objPtr;
10059 
10060     savedFramePtr = interp->framePtr;
10061     interp->framePtr = interp->topFramePtr;
10062     objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
10063     interp->framePtr = savedFramePtr;
10064 
10065     return objPtr;
10066 }
10067 
Jim_GetVariableStr(Jim_Interp * interp,const char * name,int flags)10068 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
10069 {
10070     Jim_Obj *nameObjPtr, *varObjPtr;
10071 
10072     nameObjPtr = Jim_NewStringObj(interp, name, -1);
10073     Jim_IncrRefCount(nameObjPtr);
10074     varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
10075     Jim_DecrRefCount(interp, nameObjPtr);
10076     return varObjPtr;
10077 }
10078 
Jim_GetGlobalVariableStr(Jim_Interp * interp,const char * name,int flags)10079 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
10080 {
10081     Jim_CallFrame *savedFramePtr;
10082     Jim_Obj *objPtr;
10083 
10084     savedFramePtr = interp->framePtr;
10085     interp->framePtr = interp->topFramePtr;
10086     objPtr = Jim_GetVariableStr(interp, name, flags);
10087     interp->framePtr = savedFramePtr;
10088 
10089     return objPtr;
10090 }
10091 
Jim_UnsetVariable(Jim_Interp * interp,Jim_Obj * nameObjPtr,int flags)10092 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10093 {
10094     Jim_Var *varPtr;
10095     int retval;
10096     Jim_CallFrame *framePtr;
10097 
10098     retval = SetVariableFromAny(interp, nameObjPtr);
10099     if (retval == JIM_DICT_SUGAR) {
10100 
10101         return JimDictSugarSet(interp, nameObjPtr, NULL);
10102     }
10103     else if (retval == JIM_OK) {
10104         varPtr = nameObjPtr->internalRep.varValue.varPtr;
10105 
10106 
10107         if (varPtr->linkFramePtr) {
10108             framePtr = interp->framePtr;
10109             interp->framePtr = varPtr->linkFramePtr;
10110             retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
10111             interp->framePtr = framePtr;
10112         }
10113         else {
10114             const char *name = Jim_String(nameObjPtr);
10115             if (nameObjPtr->internalRep.varValue.global) {
10116                 name += 2;
10117                 framePtr = interp->topFramePtr;
10118             }
10119             else {
10120                 framePtr = interp->framePtr;
10121             }
10122 
10123             retval = Jim_DeleteHashEntry(&framePtr->vars, name);
10124             if (retval == JIM_OK) {
10125 
10126                 framePtr->id = interp->callFrameEpoch++;
10127             }
10128         }
10129     }
10130     if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
10131         Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
10132     }
10133     return retval;
10134 }
10135 
10136 
10137 
JimDictSugarParseVarKey(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj ** varPtrPtr,Jim_Obj ** keyPtrPtr)10138 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
10139     Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
10140 {
10141     const char *str, *p;
10142     int len, keyLen;
10143     Jim_Obj *varObjPtr, *keyObjPtr;
10144 
10145     str = Jim_GetString(objPtr, &len);
10146 
10147     p = strchr(str, '(');
10148     JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
10149 
10150     varObjPtr = Jim_NewStringObj(interp, str, p - str);
10151 
10152     p++;
10153     keyLen = (str + len) - p;
10154     if (str[len - 1] == ')') {
10155         keyLen--;
10156     }
10157 
10158 
10159     keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
10160 
10161     Jim_IncrRefCount(varObjPtr);
10162     Jim_IncrRefCount(keyObjPtr);
10163     *varPtrPtr = varObjPtr;
10164     *keyPtrPtr = keyObjPtr;
10165 }
10166 
JimDictSugarSet(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * valObjPtr)10167 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
10168 {
10169     int err;
10170 
10171     SetDictSubstFromAny(interp, objPtr);
10172 
10173     err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
10174         &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);
10175 
10176     if (err == JIM_OK) {
10177 
10178         Jim_SetEmptyResult(interp);
10179     }
10180     else {
10181         if (!valObjPtr) {
10182 
10183             if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
10184                 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
10185                     objPtr);
10186                 return err;
10187             }
10188         }
10189 
10190         Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
10191             (valObjPtr ? "set" : "unset"), objPtr);
10192     }
10193     return err;
10194 }
10195 
JimDictExpandArrayVariable(Jim_Interp * interp,Jim_Obj * varObjPtr,Jim_Obj * keyObjPtr,int flags)10196 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
10197     Jim_Obj *keyObjPtr, int flags)
10198 {
10199     Jim_Obj *dictObjPtr;
10200     Jim_Obj *resObjPtr = NULL;
10201     int ret;
10202 
10203     dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
10204     if (!dictObjPtr) {
10205         return NULL;
10206     }
10207 
10208     ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
10209     if (ret != JIM_OK) {
10210         Jim_SetResultFormatted(interp,
10211             "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
10212             ret < 0 ? "variable isn't" : "no such element in");
10213     }
10214     else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
10215 
10216         Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
10217     }
10218 
10219     return resObjPtr;
10220 }
10221 
10222 
JimDictSugarGet(Jim_Interp * interp,Jim_Obj * objPtr,int flags)10223 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10224 {
10225     SetDictSubstFromAny(interp, objPtr);
10226 
10227     return JimDictExpandArrayVariable(interp,
10228         objPtr->internalRep.dictSubstValue.varNameObjPtr,
10229         objPtr->internalRep.dictSubstValue.indexObjPtr, flags);
10230 }
10231 
10232 
10233 
FreeDictSubstInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)10234 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
10235 {
10236     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
10237     Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
10238 }
10239 
DupDictSubstInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)10240 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
10241 {
10242     JIM_NOTUSED(interp);
10243 
10244     dupPtr->internalRep.dictSubstValue.varNameObjPtr =
10245         srcPtr->internalRep.dictSubstValue.varNameObjPtr;
10246     dupPtr->internalRep.dictSubstValue.indexObjPtr = srcPtr->internalRep.dictSubstValue.indexObjPtr;
10247     dupPtr->typePtr = &dictSubstObjType;
10248 }
10249 
10250 
SetDictSubstFromAny(Jim_Interp * interp,Jim_Obj * objPtr)10251 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10252 {
10253     if (objPtr->typePtr != &dictSubstObjType) {
10254         Jim_Obj *varObjPtr, *keyObjPtr;
10255 
10256         if (objPtr->typePtr == &interpolatedObjType) {
10257 
10258 
10259             varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
10260             keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
10261 
10262             Jim_IncrRefCount(varObjPtr);
10263             Jim_IncrRefCount(keyObjPtr);
10264         }
10265         else {
10266             JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
10267         }
10268 
10269         Jim_FreeIntRep(interp, objPtr);
10270         objPtr->typePtr = &dictSubstObjType;
10271         objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
10272         objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
10273     }
10274 }
10275 
JimExpandDictSugar(Jim_Interp * interp,Jim_Obj * objPtr)10276 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10277 {
10278     Jim_Obj *resObjPtr = NULL;
10279     Jim_Obj *substKeyObjPtr = NULL;
10280 
10281     SetDictSubstFromAny(interp, objPtr);
10282 
10283     if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
10284             &substKeyObjPtr, JIM_NONE)
10285         != JIM_OK) {
10286         return NULL;
10287     }
10288     Jim_IncrRefCount(substKeyObjPtr);
10289     resObjPtr =
10290         JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
10291         substKeyObjPtr, 0);
10292     Jim_DecrRefCount(interp, substKeyObjPtr);
10293 
10294     return resObjPtr;
10295 }
10296 
JimExpandExprSugar(Jim_Interp * interp,Jim_Obj * objPtr)10297 static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10298 {
10299     Jim_Obj *resultObjPtr;
10300 
10301     if (Jim_EvalExpression(interp, objPtr, &resultObjPtr) == JIM_OK) {
10302 
10303         resultObjPtr->refCount--;
10304         return resultObjPtr;
10305     }
10306     return NULL;
10307 }
10308 
10309 
JimCreateCallFrame(Jim_Interp * interp,Jim_CallFrame * parent,Jim_Obj * nsObj)10310 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
10311 {
10312     Jim_CallFrame *cf;
10313 
10314     if (interp->freeFramesList) {
10315         cf = interp->freeFramesList;
10316         interp->freeFramesList = cf->next;
10317 
10318         cf->argv = NULL;
10319         cf->argc = 0;
10320         cf->procArgsObjPtr = NULL;
10321         cf->procBodyObjPtr = NULL;
10322         cf->next = NULL;
10323         cf->staticVars = NULL;
10324         cf->localCommands = NULL;
10325         cf->tailcallObj = NULL;
10326         cf->tailcallCmd = NULL;
10327     }
10328     else {
10329         cf = Jim_Alloc(sizeof(*cf));
10330         memset(cf, 0, sizeof(*cf));
10331 
10332         Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
10333     }
10334 
10335     cf->id = interp->callFrameEpoch++;
10336     cf->parent = parent;
10337     cf->level = parent ? parent->level + 1 : 0;
10338     cf->nsObj = nsObj;
10339     Jim_IncrRefCount(nsObj);
10340 
10341     return cf;
10342 }
10343 
JimDeleteLocalProcs(Jim_Interp * interp,Jim_Stack * localCommands)10344 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
10345 {
10346 
10347     if (localCommands) {
10348         Jim_Obj *cmdNameObj;
10349 
10350         while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
10351             Jim_HashEntry *he;
10352             Jim_Obj *fqObjName;
10353             Jim_HashTable *ht = &interp->commands;
10354 
10355             const char *fqname = JimQualifyName(interp, Jim_String(cmdNameObj), &fqObjName);
10356 
10357             he = Jim_FindHashEntry(ht, fqname);
10358 
10359             if (he) {
10360                 Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
10361                 if (cmd->prevCmd) {
10362                     Jim_Cmd *prevCmd = cmd->prevCmd;
10363                     cmd->prevCmd = NULL;
10364 
10365 
10366                     JimDecrCmdRefCount(interp, cmd);
10367 
10368 
10369                     Jim_SetHashVal(ht, he, prevCmd);
10370                 }
10371                 else {
10372                     Jim_DeleteHashEntry(ht, fqname);
10373                 }
10374                 Jim_InterpIncrProcEpoch(interp);
10375             }
10376             Jim_DecrRefCount(interp, cmdNameObj);
10377             JimFreeQualifiedName(interp, fqObjName);
10378         }
10379         Jim_FreeStack(localCommands);
10380         Jim_Free(localCommands);
10381     }
10382     return JIM_OK;
10383 }
10384 
10385 
10386 #define JIM_FCF_FULL 0
10387 #define JIM_FCF_REUSE 1
JimFreeCallFrame(Jim_Interp * interp,Jim_CallFrame * cf,int action)10388 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
10389  {
10390     JimDeleteLocalProcs(interp, cf->localCommands);
10391 
10392     if (cf->procArgsObjPtr)
10393         Jim_DecrRefCount(interp, cf->procArgsObjPtr);
10394     if (cf->procBodyObjPtr)
10395         Jim_DecrRefCount(interp, cf->procBodyObjPtr);
10396     Jim_DecrRefCount(interp, cf->nsObj);
10397     if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
10398         Jim_FreeHashTable(&cf->vars);
10399     else {
10400         int i;
10401         Jim_HashEntry **table = cf->vars.table, *he;
10402 
10403         for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
10404             he = table[i];
10405             while (he != NULL) {
10406                 Jim_HashEntry *nextEntry = he->next;
10407                 Jim_Var *varPtr = Jim_GetHashEntryVal(he);
10408 
10409                 Jim_DecrRefCount(interp, varPtr->objPtr);
10410                 Jim_Free(Jim_GetHashEntryKey(he));
10411                 Jim_Free(varPtr);
10412                 Jim_Free(he);
10413                 table[i] = NULL;
10414                 he = nextEntry;
10415             }
10416         }
10417         cf->vars.used = 0;
10418     }
10419     cf->next = interp->freeFramesList;
10420     interp->freeFramesList = cf;
10421 }
10422 
10423 
10424 
Jim_IsBigEndian(void)10425 int Jim_IsBigEndian(void)
10426 {
10427     union {
10428         unsigned short s;
10429         unsigned char c[2];
10430     } uval = {0x0102};
10431 
10432     return uval.c[0] == 1;
10433 }
10434 
10435 
Jim_CreateInterp(void)10436 Jim_Interp *Jim_CreateInterp(void)
10437 {
10438     Jim_Interp *i = Jim_Alloc(sizeof(*i));
10439 
10440     memset(i, 0, sizeof(*i));
10441 
10442     i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
10443     i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
10444     i->lastCollectTime = time(NULL);
10445 
10446     Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
10447 #ifdef JIM_REFERENCES
10448     Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
10449 #endif
10450     Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
10451     Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
10452     i->emptyObj = Jim_NewEmptyStringObj(i);
10453     i->trueObj = Jim_NewIntObj(i, 1);
10454     i->falseObj = Jim_NewIntObj(i, 0);
10455     i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
10456     i->errorFileNameObj = i->emptyObj;
10457     i->result = i->emptyObj;
10458     i->stackTrace = Jim_NewListObj(i, NULL, 0);
10459     i->unknown = Jim_NewStringObj(i, "unknown", -1);
10460     i->errorProc = i->emptyObj;
10461     i->currentScriptObj = Jim_NewEmptyStringObj(i);
10462     i->nullScriptObj = Jim_NewEmptyStringObj(i);
10463     Jim_IncrRefCount(i->emptyObj);
10464     Jim_IncrRefCount(i->errorFileNameObj);
10465     Jim_IncrRefCount(i->result);
10466     Jim_IncrRefCount(i->stackTrace);
10467     Jim_IncrRefCount(i->unknown);
10468     Jim_IncrRefCount(i->currentScriptObj);
10469     Jim_IncrRefCount(i->nullScriptObj);
10470     Jim_IncrRefCount(i->errorProc);
10471     Jim_IncrRefCount(i->trueObj);
10472     Jim_IncrRefCount(i->falseObj);
10473 
10474 
10475     Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
10476     Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
10477 
10478     Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
10479     Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
10480     Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
10481     Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
10482     Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
10483     Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
10484     Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
10485     Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
10486 
10487     return i;
10488 }
10489 
Jim_FreeInterp(Jim_Interp * i)10490 void Jim_FreeInterp(Jim_Interp *i)
10491 {
10492     Jim_CallFrame *cf, *cfx;
10493 
10494     Jim_Obj *objPtr, *nextObjPtr;
10495 
10496 
10497     for (cf = i->framePtr; cf; cf = cfx) {
10498         cfx = cf->parent;
10499         JimFreeCallFrame(i, cf, JIM_FCF_FULL);
10500     }
10501 
10502     Jim_DecrRefCount(i, i->emptyObj);
10503     Jim_DecrRefCount(i, i->trueObj);
10504     Jim_DecrRefCount(i, i->falseObj);
10505     Jim_DecrRefCount(i, i->result);
10506     Jim_DecrRefCount(i, i->stackTrace);
10507     Jim_DecrRefCount(i, i->errorProc);
10508     Jim_DecrRefCount(i, i->unknown);
10509     Jim_DecrRefCount(i, i->errorFileNameObj);
10510     Jim_DecrRefCount(i, i->currentScriptObj);
10511     Jim_DecrRefCount(i, i->nullScriptObj);
10512     Jim_FreeHashTable(&i->commands);
10513 #ifdef JIM_REFERENCES
10514     Jim_FreeHashTable(&i->references);
10515 #endif
10516     Jim_FreeHashTable(&i->packages);
10517     Jim_Free(i->prngState);
10518     Jim_FreeHashTable(&i->assocData);
10519 
10520 #ifdef JIM_MAINTAINER
10521     if (i->liveList != NULL) {
10522         objPtr = i->liveList;
10523 
10524         printf("\n-------------------------------------\n");
10525         printf("Objects still in the free list:\n");
10526         while (objPtr) {
10527             const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
10528 
10529             if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
10530                 printf("%p (%d) %-10s: '%.20s...'\n",
10531                     (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
10532             }
10533             else {
10534                 printf("%p (%d) %-10s: '%s'\n",
10535                     (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
10536             }
10537             if (objPtr->typePtr == &sourceObjType) {
10538                 printf("FILE %s LINE %d\n",
10539                     Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
10540                     objPtr->internalRep.sourceValue.lineNumber);
10541             }
10542             objPtr = objPtr->nextObjPtr;
10543         }
10544         printf("-------------------------------------\n\n");
10545         JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
10546     }
10547 #endif
10548 
10549 
10550     objPtr = i->freeList;
10551     while (objPtr) {
10552         nextObjPtr = objPtr->nextObjPtr;
10553         Jim_Free(objPtr);
10554         objPtr = nextObjPtr;
10555     }
10556 
10557 
10558     for (cf = i->freeFramesList; cf; cf = cfx) {
10559         cfx = cf->next;
10560         if (cf->vars.table)
10561             Jim_FreeHashTable(&cf->vars);
10562         Jim_Free(cf);
10563     }
10564 
10565 
10566     Jim_Free(i);
10567 }
10568 
Jim_GetCallFrameByLevel(Jim_Interp * interp,Jim_Obj * levelObjPtr)10569 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10570 {
10571     long level;
10572     const char *str;
10573     Jim_CallFrame *framePtr;
10574 
10575     if (levelObjPtr) {
10576         str = Jim_String(levelObjPtr);
10577         if (str[0] == '#') {
10578             char *endptr;
10579 
10580             level = jim_strtol(str + 1, &endptr);
10581             if (str[1] == '\0' || endptr[0] != '\0') {
10582                 level = -1;
10583             }
10584         }
10585         else {
10586             if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
10587                 level = -1;
10588             }
10589             else {
10590 
10591                 level = interp->framePtr->level - level;
10592             }
10593         }
10594     }
10595     else {
10596         str = "1";
10597         level = interp->framePtr->level - 1;
10598     }
10599 
10600     if (level == 0) {
10601         return interp->topFramePtr;
10602     }
10603     if (level > 0) {
10604 
10605         for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10606             if (framePtr->level == level) {
10607                 return framePtr;
10608             }
10609         }
10610     }
10611 
10612     Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
10613     return NULL;
10614 }
10615 
JimGetCallFrameByInteger(Jim_Interp * interp,Jim_Obj * levelObjPtr)10616 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10617 {
10618     long level;
10619     Jim_CallFrame *framePtr;
10620 
10621     if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
10622         if (level <= 0) {
10623 
10624             level = interp->framePtr->level + level;
10625         }
10626 
10627         if (level == 0) {
10628             return interp->topFramePtr;
10629         }
10630 
10631 
10632         for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10633             if (framePtr->level == level) {
10634                 return framePtr;
10635             }
10636         }
10637     }
10638 
10639     Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
10640     return NULL;
10641 }
10642 
JimResetStackTrace(Jim_Interp * interp)10643 static void JimResetStackTrace(Jim_Interp *interp)
10644 {
10645     Jim_DecrRefCount(interp, interp->stackTrace);
10646     interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
10647     Jim_IncrRefCount(interp->stackTrace);
10648 }
10649 
JimSetStackTrace(Jim_Interp * interp,Jim_Obj * stackTraceObj)10650 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
10651 {
10652     int len;
10653 
10654 
10655     Jim_IncrRefCount(stackTraceObj);
10656     Jim_DecrRefCount(interp, interp->stackTrace);
10657     interp->stackTrace = stackTraceObj;
10658     interp->errorFlag = 1;
10659 
10660     len = Jim_ListLength(interp, interp->stackTrace);
10661     if (len >= 3) {
10662         if (Jim_Length(Jim_ListGetIndex(interp, interp->stackTrace, len - 2)) == 0) {
10663             interp->addStackTrace = 1;
10664         }
10665     }
10666 }
10667 
JimAppendStackTrace(Jim_Interp * interp,const char * procname,Jim_Obj * fileNameObj,int linenr)10668 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
10669     Jim_Obj *fileNameObj, int linenr)
10670 {
10671     if (strcmp(procname, "unknown") == 0) {
10672         procname = "";
10673     }
10674     if (!*procname && !Jim_Length(fileNameObj)) {
10675 
10676         return;
10677     }
10678 
10679     if (Jim_IsShared(interp->stackTrace)) {
10680         Jim_DecrRefCount(interp, interp->stackTrace);
10681         interp->stackTrace = Jim_DuplicateObj(interp, interp->stackTrace);
10682         Jim_IncrRefCount(interp->stackTrace);
10683     }
10684 
10685 
10686     if (!*procname && Jim_Length(fileNameObj)) {
10687 
10688         int len = Jim_ListLength(interp, interp->stackTrace);
10689 
10690         if (len >= 3) {
10691             Jim_Obj *objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 3);
10692             if (Jim_Length(objPtr)) {
10693 
10694                 objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 2);
10695                 if (Jim_Length(objPtr) == 0) {
10696 
10697                     ListSetIndex(interp, interp->stackTrace, len - 2, fileNameObj, 0);
10698                     ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), 0);
10699                     return;
10700                 }
10701             }
10702         }
10703     }
10704 
10705     Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewStringObj(interp, procname, -1));
10706     Jim_ListAppendElement(interp, interp->stackTrace, fileNameObj);
10707     Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewIntObj(interp, linenr));
10708 }
10709 
Jim_SetAssocData(Jim_Interp * interp,const char * key,Jim_InterpDeleteProc * delProc,void * data)10710 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
10711     void *data)
10712 {
10713     AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
10714 
10715     assocEntryPtr->delProc = delProc;
10716     assocEntryPtr->data = data;
10717     return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
10718 }
10719 
Jim_GetAssocData(Jim_Interp * interp,const char * key)10720 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
10721 {
10722     Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
10723 
10724     if (entryPtr != NULL) {
10725         AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr);
10726         return assocEntryPtr->data;
10727     }
10728     return NULL;
10729 }
10730 
Jim_DeleteAssocData(Jim_Interp * interp,const char * key)10731 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
10732 {
10733     return Jim_DeleteHashEntry(&interp->assocData, key);
10734 }
10735 
Jim_GetExitCode(Jim_Interp * interp)10736 int Jim_GetExitCode(Jim_Interp *interp)
10737 {
10738     return interp->exitCode;
10739 }
10740 
10741 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
10742 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
10743 
10744 static const Jim_ObjType intObjType = {
10745     "int",
10746     NULL,
10747     NULL,
10748     UpdateStringOfInt,
10749     JIM_TYPE_NONE,
10750 };
10751 
10752 static const Jim_ObjType coercedDoubleObjType = {
10753     "coerced-double",
10754     NULL,
10755     NULL,
10756     UpdateStringOfInt,
10757     JIM_TYPE_NONE,
10758 };
10759 
10760 
UpdateStringOfInt(struct Jim_Obj * objPtr)10761 static void UpdateStringOfInt(struct Jim_Obj *objPtr)
10762 {
10763     char buf[JIM_INTEGER_SPACE + 1];
10764     jim_wide wideValue = JimWideValue(objPtr);
10765     int pos = 0;
10766 
10767     if (wideValue == 0) {
10768         buf[pos++] = '0';
10769     }
10770     else {
10771         char tmp[JIM_INTEGER_SPACE];
10772         int num = 0;
10773         int i;
10774 
10775         if (wideValue < 0) {
10776             buf[pos++] = '-';
10777             i = wideValue % 10;
10778             tmp[num++] = (i > 0) ? (10 - i) : -i;
10779             wideValue /= -10;
10780         }
10781 
10782         while (wideValue) {
10783             tmp[num++] = wideValue % 10;
10784             wideValue /= 10;
10785         }
10786 
10787         for (i = 0; i < num; i++) {
10788             buf[pos++] = '0' + tmp[num - i - 1];
10789         }
10790     }
10791     buf[pos] = 0;
10792 
10793     JimSetStringBytes(objPtr, buf);
10794 }
10795 
SetIntFromAny(Jim_Interp * interp,Jim_Obj * objPtr,int flags)10796 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10797 {
10798     jim_wide wideValue;
10799     const char *str;
10800 
10801     if (objPtr->typePtr == &coercedDoubleObjType) {
10802 
10803         objPtr->typePtr = &intObjType;
10804         return JIM_OK;
10805     }
10806 
10807 
10808     str = Jim_String(objPtr);
10809 
10810     if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
10811         if (flags & JIM_ERRMSG) {
10812             Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
10813         }
10814         return JIM_ERR;
10815     }
10816     if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
10817         Jim_SetResultString(interp, "Integer value too big to be represented", -1);
10818         return JIM_ERR;
10819     }
10820 
10821     Jim_FreeIntRep(interp, objPtr);
10822     objPtr->typePtr = &intObjType;
10823     objPtr->internalRep.wideValue = wideValue;
10824     return JIM_OK;
10825 }
10826 
10827 #ifdef JIM_OPTIMIZATION
JimIsWide(Jim_Obj * objPtr)10828 static int JimIsWide(Jim_Obj *objPtr)
10829 {
10830     return objPtr->typePtr == &intObjType;
10831 }
10832 #endif
10833 
Jim_GetWide(Jim_Interp * interp,Jim_Obj * objPtr,jim_wide * widePtr)10834 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10835 {
10836     if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
10837         return JIM_ERR;
10838     *widePtr = JimWideValue(objPtr);
10839     return JIM_OK;
10840 }
10841 
10842 
JimGetWideNoErr(Jim_Interp * interp,Jim_Obj * objPtr,jim_wide * widePtr)10843 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10844 {
10845     if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
10846         return JIM_ERR;
10847     *widePtr = JimWideValue(objPtr);
10848     return JIM_OK;
10849 }
10850 
Jim_GetLong(Jim_Interp * interp,Jim_Obj * objPtr,long * longPtr)10851 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
10852 {
10853     jim_wide wideValue;
10854     int retval;
10855 
10856     retval = Jim_GetWide(interp, objPtr, &wideValue);
10857     if (retval == JIM_OK) {
10858         *longPtr = (long)wideValue;
10859         return JIM_OK;
10860     }
10861     return JIM_ERR;
10862 }
10863 
Jim_NewIntObj(Jim_Interp * interp,jim_wide wideValue)10864 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
10865 {
10866     Jim_Obj *objPtr;
10867 
10868     objPtr = Jim_NewObj(interp);
10869     objPtr->typePtr = &intObjType;
10870     objPtr->bytes = NULL;
10871     objPtr->internalRep.wideValue = wideValue;
10872     return objPtr;
10873 }
10874 
10875 #define JIM_DOUBLE_SPACE 30
10876 
10877 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
10878 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
10879 
10880 static const Jim_ObjType doubleObjType = {
10881     "double",
10882     NULL,
10883     NULL,
10884     UpdateStringOfDouble,
10885     JIM_TYPE_NONE,
10886 };
10887 
10888 #ifndef HAVE_ISNAN
10889 #undef isnan
10890 #define isnan(X) ((X) != (X))
10891 #endif
10892 #ifndef HAVE_ISINF
10893 #undef isinf
10894 #define isinf(X) (1.0 / (X) == 0.0)
10895 #endif
10896 
UpdateStringOfDouble(struct Jim_Obj * objPtr)10897 static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
10898 {
10899     double value = objPtr->internalRep.doubleValue;
10900 
10901     if (isnan(value)) {
10902         JimSetStringBytes(objPtr, "NaN");
10903         return;
10904     }
10905     if (isinf(value)) {
10906         if (value < 0) {
10907             JimSetStringBytes(objPtr, "-Inf");
10908         }
10909         else {
10910             JimSetStringBytes(objPtr, "Inf");
10911         }
10912         return;
10913     }
10914     {
10915         char buf[JIM_DOUBLE_SPACE + 1];
10916         int i;
10917         int len = sprintf(buf, "%.12g", value);
10918 
10919 
10920         for (i = 0; i < len; i++) {
10921             if (buf[i] == '.' || buf[i] == 'e') {
10922 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
10923                 char *e = strchr(buf, 'e');
10924                 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
10925 
10926                     e += 2;
10927                     memmove(e, e + 1, len - (e - buf));
10928                 }
10929 #endif
10930                 break;
10931             }
10932         }
10933         if (buf[i] == '\0') {
10934             buf[i++] = '.';
10935             buf[i++] = '0';
10936             buf[i] = '\0';
10937         }
10938         JimSetStringBytes(objPtr, buf);
10939     }
10940 }
10941 
SetDoubleFromAny(Jim_Interp * interp,Jim_Obj * objPtr)10942 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10943 {
10944     double doubleValue;
10945     jim_wide wideValue;
10946     const char *str;
10947 
10948     str = Jim_String(objPtr);
10949 
10950 #ifdef HAVE_LONG_LONG
10951 
10952 #define MIN_INT_IN_DOUBLE -(1LL << 53)
10953 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)
10954 
10955     if (objPtr->typePtr == &intObjType
10956         && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
10957         && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {
10958 
10959 
10960         objPtr->typePtr = &coercedDoubleObjType;
10961         return JIM_OK;
10962     }
10963     else
10964 #endif
10965     if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
10966 
10967         Jim_FreeIntRep(interp, objPtr);
10968         objPtr->typePtr = &coercedDoubleObjType;
10969         objPtr->internalRep.wideValue = wideValue;
10970         return JIM_OK;
10971     }
10972     else {
10973 
10974         if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
10975             Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
10976             return JIM_ERR;
10977         }
10978 
10979         Jim_FreeIntRep(interp, objPtr);
10980     }
10981     objPtr->typePtr = &doubleObjType;
10982     objPtr->internalRep.doubleValue = doubleValue;
10983     return JIM_OK;
10984 }
10985 
Jim_GetDouble(Jim_Interp * interp,Jim_Obj * objPtr,double * doublePtr)10986 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
10987 {
10988     if (objPtr->typePtr == &coercedDoubleObjType) {
10989         *doublePtr = JimWideValue(objPtr);
10990         return JIM_OK;
10991     }
10992     if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
10993         return JIM_ERR;
10994 
10995     if (objPtr->typePtr == &coercedDoubleObjType) {
10996         *doublePtr = JimWideValue(objPtr);
10997     }
10998     else {
10999         *doublePtr = objPtr->internalRep.doubleValue;
11000     }
11001     return JIM_OK;
11002 }
11003 
Jim_NewDoubleObj(Jim_Interp * interp,double doubleValue)11004 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
11005 {
11006     Jim_Obj *objPtr;
11007 
11008     objPtr = Jim_NewObj(interp);
11009     objPtr->typePtr = &doubleObjType;
11010     objPtr->bytes = NULL;
11011     objPtr->internalRep.doubleValue = doubleValue;
11012     return objPtr;
11013 }
11014 
11015 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
11016 
Jim_GetBoolean(Jim_Interp * interp,Jim_Obj * objPtr,int * booleanPtr)11017 int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
11018 {
11019     if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
11020         return JIM_ERR;
11021     *booleanPtr = (int) JimWideValue(objPtr);
11022     return JIM_OK;
11023 }
11024 
SetBooleanFromAny(Jim_Interp * interp,Jim_Obj * objPtr,int flags)11025 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
11026 {
11027     static const char * const falses[] = {
11028         "0", "false", "no", "off", NULL
11029     };
11030     static const char * const trues[] = {
11031         "1", "true", "yes", "on", NULL
11032     };
11033 
11034     int boolean;
11035 
11036     int index;
11037     if (Jim_GetEnum(interp, objPtr, falses, &index, NULL, 0) == JIM_OK) {
11038         boolean = 0;
11039     } else if (Jim_GetEnum(interp, objPtr, trues, &index, NULL, 0) == JIM_OK) {
11040         boolean = 1;
11041     } else {
11042         if (flags & JIM_ERRMSG) {
11043             Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr);
11044         }
11045         return JIM_ERR;
11046     }
11047 
11048 
11049     Jim_FreeIntRep(interp, objPtr);
11050     objPtr->typePtr = &intObjType;
11051     objPtr->internalRep.wideValue = boolean;
11052     return JIM_OK;
11053 }
11054 
11055 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
11056 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
11057 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11058 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11059 static void UpdateStringOfList(struct Jim_Obj *objPtr);
11060 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11061 
11062 static const Jim_ObjType listObjType = {
11063     "list",
11064     FreeListInternalRep,
11065     DupListInternalRep,
11066     UpdateStringOfList,
11067     JIM_TYPE_NONE,
11068 };
11069 
FreeListInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)11070 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11071 {
11072     int i;
11073 
11074     for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
11075         Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
11076     }
11077     Jim_Free(objPtr->internalRep.listValue.ele);
11078 }
11079 
DupListInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)11080 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11081 {
11082     int i;
11083 
11084     JIM_NOTUSED(interp);
11085 
11086     dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
11087     dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
11088     dupPtr->internalRep.listValue.ele =
11089         Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
11090     memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
11091         sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
11092     for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
11093         Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
11094     }
11095     dupPtr->typePtr = &listObjType;
11096 }
11097 
11098 #define JIM_ELESTR_SIMPLE 0
11099 #define JIM_ELESTR_BRACE 1
11100 #define JIM_ELESTR_QUOTE 2
ListElementQuotingType(const char * s,int len)11101 static unsigned char ListElementQuotingType(const char *s, int len)
11102 {
11103     int i, level, blevel, trySimple = 1;
11104 
11105 
11106     if (len == 0)
11107         return JIM_ELESTR_BRACE;
11108     if (s[0] == '"' || s[0] == '{') {
11109         trySimple = 0;
11110         goto testbrace;
11111     }
11112     for (i = 0; i < len; i++) {
11113         switch (s[i]) {
11114             case ' ':
11115             case '$':
11116             case '"':
11117             case '[':
11118             case ']':
11119             case ';':
11120             case '\\':
11121             case '\r':
11122             case '\n':
11123             case '\t':
11124             case '\f':
11125             case '\v':
11126                 trySimple = 0;
11127 
11128             case '{':
11129             case '}':
11130                 goto testbrace;
11131         }
11132     }
11133     return JIM_ELESTR_SIMPLE;
11134 
11135   testbrace:
11136 
11137     if (s[len - 1] == '\\')
11138         return JIM_ELESTR_QUOTE;
11139     level = 0;
11140     blevel = 0;
11141     for (i = 0; i < len; i++) {
11142         switch (s[i]) {
11143             case '{':
11144                 level++;
11145                 break;
11146             case '}':
11147                 level--;
11148                 if (level < 0)
11149                     return JIM_ELESTR_QUOTE;
11150                 break;
11151             case '[':
11152                 blevel++;
11153                 break;
11154             case ']':
11155                 blevel--;
11156                 break;
11157             case '\\':
11158                 if (s[i + 1] == '\n')
11159                     return JIM_ELESTR_QUOTE;
11160                 else if (s[i + 1] != '\0')
11161                     i++;
11162                 break;
11163         }
11164     }
11165     if (blevel < 0) {
11166         return JIM_ELESTR_QUOTE;
11167     }
11168 
11169     if (level == 0) {
11170         if (!trySimple)
11171             return JIM_ELESTR_BRACE;
11172         for (i = 0; i < len; i++) {
11173             switch (s[i]) {
11174                 case ' ':
11175                 case '$':
11176                 case '"':
11177                 case '[':
11178                 case ']':
11179                 case ';':
11180                 case '\\':
11181                 case '\r':
11182                 case '\n':
11183                 case '\t':
11184                 case '\f':
11185                 case '\v':
11186                     return JIM_ELESTR_BRACE;
11187                     break;
11188             }
11189         }
11190         return JIM_ELESTR_SIMPLE;
11191     }
11192     return JIM_ELESTR_QUOTE;
11193 }
11194 
BackslashQuoteString(const char * s,int len,char * q)11195 static int BackslashQuoteString(const char *s, int len, char *q)
11196 {
11197     char *p = q;
11198 
11199     while (len--) {
11200         switch (*s) {
11201             case ' ':
11202             case '$':
11203             case '"':
11204             case '[':
11205             case ']':
11206             case '{':
11207             case '}':
11208             case ';':
11209             case '\\':
11210                 *p++ = '\\';
11211                 *p++ = *s++;
11212                 break;
11213             case '\n':
11214                 *p++ = '\\';
11215                 *p++ = 'n';
11216                 s++;
11217                 break;
11218             case '\r':
11219                 *p++ = '\\';
11220                 *p++ = 'r';
11221                 s++;
11222                 break;
11223             case '\t':
11224                 *p++ = '\\';
11225                 *p++ = 't';
11226                 s++;
11227                 break;
11228             case '\f':
11229                 *p++ = '\\';
11230                 *p++ = 'f';
11231                 s++;
11232                 break;
11233             case '\v':
11234                 *p++ = '\\';
11235                 *p++ = 'v';
11236                 s++;
11237                 break;
11238             default:
11239                 *p++ = *s++;
11240                 break;
11241         }
11242     }
11243     *p = '\0';
11244 
11245     return p - q;
11246 }
11247 
JimMakeListStringRep(Jim_Obj * objPtr,Jim_Obj ** objv,int objc)11248 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
11249 {
11250     #define STATIC_QUOTING_LEN 32
11251     int i, bufLen, realLength;
11252     const char *strRep;
11253     char *p;
11254     unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
11255 
11256 
11257     if (objc > STATIC_QUOTING_LEN) {
11258         quotingType = Jim_Alloc(objc);
11259     }
11260     else {
11261         quotingType = staticQuoting;
11262     }
11263     bufLen = 0;
11264     for (i = 0; i < objc; i++) {
11265         int len;
11266 
11267         strRep = Jim_GetString(objv[i], &len);
11268         quotingType[i] = ListElementQuotingType(strRep, len);
11269         switch (quotingType[i]) {
11270             case JIM_ELESTR_SIMPLE:
11271                 if (i != 0 || strRep[0] != '#') {
11272                     bufLen += len;
11273                     break;
11274                 }
11275 
11276                 quotingType[i] = JIM_ELESTR_BRACE;
11277 
11278             case JIM_ELESTR_BRACE:
11279                 bufLen += len + 2;
11280                 break;
11281             case JIM_ELESTR_QUOTE:
11282                 bufLen += len * 2;
11283                 break;
11284         }
11285         bufLen++;
11286     }
11287     bufLen++;
11288 
11289 
11290     p = objPtr->bytes = Jim_Alloc(bufLen + 1);
11291     realLength = 0;
11292     for (i = 0; i < objc; i++) {
11293         int len, qlen;
11294 
11295         strRep = Jim_GetString(objv[i], &len);
11296 
11297         switch (quotingType[i]) {
11298             case JIM_ELESTR_SIMPLE:
11299                 memcpy(p, strRep, len);
11300                 p += len;
11301                 realLength += len;
11302                 break;
11303             case JIM_ELESTR_BRACE:
11304                 *p++ = '{';
11305                 memcpy(p, strRep, len);
11306                 p += len;
11307                 *p++ = '}';
11308                 realLength += len + 2;
11309                 break;
11310             case JIM_ELESTR_QUOTE:
11311                 if (i == 0 && strRep[0] == '#') {
11312                     *p++ = '\\';
11313                     realLength++;
11314                 }
11315                 qlen = BackslashQuoteString(strRep, len, p);
11316                 p += qlen;
11317                 realLength += qlen;
11318                 break;
11319         }
11320 
11321         if (i + 1 != objc) {
11322             *p++ = ' ';
11323             realLength++;
11324         }
11325     }
11326     *p = '\0';
11327     objPtr->length = realLength;
11328 
11329     if (quotingType != staticQuoting) {
11330         Jim_Free(quotingType);
11331     }
11332 }
11333 
UpdateStringOfList(struct Jim_Obj * objPtr)11334 static void UpdateStringOfList(struct Jim_Obj *objPtr)
11335 {
11336     JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
11337 }
11338 
SetListFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)11339 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
11340 {
11341     struct JimParserCtx parser;
11342     const char *str;
11343     int strLen;
11344     Jim_Obj *fileNameObj;
11345     int linenr;
11346 
11347     if (objPtr->typePtr == &listObjType) {
11348         return JIM_OK;
11349     }
11350 
11351     if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
11352         Jim_Obj **listObjPtrPtr;
11353         int len;
11354         int i;
11355 
11356         listObjPtrPtr = JimDictPairs(objPtr, &len);
11357         for (i = 0; i < len; i++) {
11358             Jim_IncrRefCount(listObjPtrPtr[i]);
11359         }
11360 
11361 
11362         Jim_FreeIntRep(interp, objPtr);
11363         objPtr->typePtr = &listObjType;
11364         objPtr->internalRep.listValue.len = len;
11365         objPtr->internalRep.listValue.maxLen = len;
11366         objPtr->internalRep.listValue.ele = listObjPtrPtr;
11367 
11368         return JIM_OK;
11369     }
11370 
11371 
11372     if (objPtr->typePtr == &sourceObjType) {
11373         fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
11374         linenr = objPtr->internalRep.sourceValue.lineNumber;
11375     }
11376     else {
11377         fileNameObj = interp->emptyObj;
11378         linenr = 1;
11379     }
11380     Jim_IncrRefCount(fileNameObj);
11381 
11382 
11383     str = Jim_GetString(objPtr, &strLen);
11384 
11385     Jim_FreeIntRep(interp, objPtr);
11386     objPtr->typePtr = &listObjType;
11387     objPtr->internalRep.listValue.len = 0;
11388     objPtr->internalRep.listValue.maxLen = 0;
11389     objPtr->internalRep.listValue.ele = NULL;
11390 
11391 
11392     if (strLen) {
11393         JimParserInit(&parser, str, strLen, linenr);
11394         while (!parser.eof) {
11395             Jim_Obj *elementPtr;
11396 
11397             JimParseList(&parser);
11398             if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
11399                 continue;
11400             elementPtr = JimParserGetTokenObj(interp, &parser);
11401             JimSetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
11402             ListAppendElement(objPtr, elementPtr);
11403         }
11404     }
11405     Jim_DecrRefCount(interp, fileNameObj);
11406     return JIM_OK;
11407 }
11408 
Jim_NewListObj(Jim_Interp * interp,Jim_Obj * const * elements,int len)11409 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
11410 {
11411     Jim_Obj *objPtr;
11412 
11413     objPtr = Jim_NewObj(interp);
11414     objPtr->typePtr = &listObjType;
11415     objPtr->bytes = NULL;
11416     objPtr->internalRep.listValue.ele = NULL;
11417     objPtr->internalRep.listValue.len = 0;
11418     objPtr->internalRep.listValue.maxLen = 0;
11419 
11420     if (len) {
11421         ListInsertElements(objPtr, 0, len, elements);
11422     }
11423 
11424     return objPtr;
11425 }
11426 
JimListGetElements(Jim_Interp * interp,Jim_Obj * listObj,int * listLen,Jim_Obj *** listVec)11427 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
11428     Jim_Obj ***listVec)
11429 {
11430     *listLen = Jim_ListLength(interp, listObj);
11431     *listVec = listObj->internalRep.listValue.ele;
11432 }
11433 
11434 
JimSign(jim_wide w)11435 static int JimSign(jim_wide w)
11436 {
11437     if (w == 0) {
11438         return 0;
11439     }
11440     else if (w < 0) {
11441         return -1;
11442     }
11443     return 1;
11444 }
11445 
11446 
11447 struct lsort_info {
11448     jmp_buf jmpbuf;
11449     Jim_Obj *command;
11450     Jim_Interp *interp;
11451     enum {
11452         JIM_LSORT_ASCII,
11453         JIM_LSORT_NOCASE,
11454         JIM_LSORT_INTEGER,
11455         JIM_LSORT_REAL,
11456         JIM_LSORT_COMMAND
11457     } type;
11458     int order;
11459     int index;
11460     int indexed;
11461     int unique;
11462     int (*subfn)(Jim_Obj **, Jim_Obj **);
11463 };
11464 
11465 static struct lsort_info *sort_info;
11466 
ListSortIndexHelper(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11467 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11468 {
11469     Jim_Obj *lObj, *rObj;
11470 
11471     if (Jim_ListIndex(sort_info->interp, *lhsObj, sort_info->index, &lObj, JIM_ERRMSG) != JIM_OK ||
11472         Jim_ListIndex(sort_info->interp, *rhsObj, sort_info->index, &rObj, JIM_ERRMSG) != JIM_OK) {
11473         longjmp(sort_info->jmpbuf, JIM_ERR);
11474     }
11475     return sort_info->subfn(&lObj, &rObj);
11476 }
11477 
11478 
ListSortString(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11479 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11480 {
11481     return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
11482 }
11483 
ListSortStringNoCase(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11484 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11485 {
11486     return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
11487 }
11488 
ListSortInteger(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11489 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11490 {
11491     jim_wide lhs = 0, rhs = 0;
11492 
11493     if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11494         Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11495         longjmp(sort_info->jmpbuf, JIM_ERR);
11496     }
11497 
11498     return JimSign(lhs - rhs) * sort_info->order;
11499 }
11500 
ListSortReal(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11501 static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11502 {
11503     double lhs = 0, rhs = 0;
11504 
11505     if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11506         Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11507         longjmp(sort_info->jmpbuf, JIM_ERR);
11508     }
11509     if (lhs == rhs) {
11510         return 0;
11511     }
11512     if (lhs > rhs) {
11513         return sort_info->order;
11514     }
11515     return -sort_info->order;
11516 }
11517 
ListSortCommand(Jim_Obj ** lhsObj,Jim_Obj ** rhsObj)11518 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11519 {
11520     Jim_Obj *compare_script;
11521     int rc;
11522 
11523     jim_wide ret = 0;
11524 
11525 
11526     compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
11527     Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
11528     Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);
11529 
11530     rc = Jim_EvalObj(sort_info->interp, compare_script);
11531 
11532     if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
11533         longjmp(sort_info->jmpbuf, rc);
11534     }
11535 
11536     return JimSign(ret) * sort_info->order;
11537 }
11538 
ListRemoveDuplicates(Jim_Obj * listObjPtr,int (* comp)(Jim_Obj ** lhs,Jim_Obj ** rhs))11539 static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
11540 {
11541     int src;
11542     int dst = 0;
11543     Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
11544 
11545     for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
11546         if (comp(&ele[dst], &ele[src]) == 0) {
11547 
11548             Jim_DecrRefCount(sort_info->interp, ele[dst]);
11549         }
11550         else {
11551 
11552             dst++;
11553         }
11554         ele[dst] = ele[src];
11555     }
11556 
11557     ele[++dst] = ele[src];
11558 
11559 
11560     listObjPtr->internalRep.listValue.len = dst;
11561 }
11562 
11563 
ListSortElements(Jim_Interp * interp,Jim_Obj * listObjPtr,struct lsort_info * info)11564 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
11565 {
11566     struct lsort_info *prev_info;
11567 
11568     typedef int (qsort_comparator) (const void *, const void *);
11569     int (*fn) (Jim_Obj **, Jim_Obj **);
11570     Jim_Obj **vector;
11571     int len;
11572     int rc;
11573 
11574     JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
11575     SetListFromAny(interp, listObjPtr);
11576 
11577 
11578     prev_info = sort_info;
11579     sort_info = info;
11580 
11581     vector = listObjPtr->internalRep.listValue.ele;
11582     len = listObjPtr->internalRep.listValue.len;
11583     switch (info->type) {
11584         case JIM_LSORT_ASCII:
11585             fn = ListSortString;
11586             break;
11587         case JIM_LSORT_NOCASE:
11588             fn = ListSortStringNoCase;
11589             break;
11590         case JIM_LSORT_INTEGER:
11591             fn = ListSortInteger;
11592             break;
11593         case JIM_LSORT_REAL:
11594             fn = ListSortReal;
11595             break;
11596         case JIM_LSORT_COMMAND:
11597             fn = ListSortCommand;
11598             break;
11599         default:
11600             fn = NULL;
11601             JimPanic((1, "ListSort called with invalid sort type"));
11602             return -1;
11603     }
11604 
11605     if (info->indexed) {
11606 
11607         info->subfn = fn;
11608         fn = ListSortIndexHelper;
11609     }
11610 
11611     if ((rc = setjmp(info->jmpbuf)) == 0) {
11612         qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
11613 
11614         if (info->unique && len > 1) {
11615             ListRemoveDuplicates(listObjPtr, fn);
11616         }
11617 
11618         Jim_InvalidateStringRep(listObjPtr);
11619     }
11620     sort_info = prev_info;
11621 
11622     return rc;
11623 }
11624 
ListInsertElements(Jim_Obj * listPtr,int idx,int elemc,Jim_Obj * const * elemVec)11625 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
11626 {
11627     int currentLen = listPtr->internalRep.listValue.len;
11628     int requiredLen = currentLen + elemc;
11629     int i;
11630     Jim_Obj **point;
11631 
11632     if (requiredLen > listPtr->internalRep.listValue.maxLen) {
11633         if (requiredLen < 2) {
11634 
11635             requiredLen = 4;
11636         }
11637         else {
11638             requiredLen *= 2;
11639         }
11640 
11641         listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
11642             sizeof(Jim_Obj *) * requiredLen);
11643 
11644         listPtr->internalRep.listValue.maxLen = requiredLen;
11645     }
11646     if (idx < 0) {
11647         idx = currentLen;
11648     }
11649     point = listPtr->internalRep.listValue.ele + idx;
11650     memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
11651     for (i = 0; i < elemc; ++i) {
11652         point[i] = elemVec[i];
11653         Jim_IncrRefCount(point[i]);
11654     }
11655     listPtr->internalRep.listValue.len += elemc;
11656 }
11657 
ListAppendElement(Jim_Obj * listPtr,Jim_Obj * objPtr)11658 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
11659 {
11660     ListInsertElements(listPtr, -1, 1, &objPtr);
11661 }
11662 
ListAppendList(Jim_Obj * listPtr,Jim_Obj * appendListPtr)11663 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11664 {
11665     ListInsertElements(listPtr, -1,
11666         appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele);
11667 }
11668 
Jim_ListAppendElement(Jim_Interp * interp,Jim_Obj * listPtr,Jim_Obj * objPtr)11669 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
11670 {
11671     JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object"));
11672     SetListFromAny(interp, listPtr);
11673     Jim_InvalidateStringRep(listPtr);
11674     ListAppendElement(listPtr, objPtr);
11675 }
11676 
Jim_ListAppendList(Jim_Interp * interp,Jim_Obj * listPtr,Jim_Obj * appendListPtr)11677 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11678 {
11679     JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object"));
11680     SetListFromAny(interp, listPtr);
11681     SetListFromAny(interp, appendListPtr);
11682     Jim_InvalidateStringRep(listPtr);
11683     ListAppendList(listPtr, appendListPtr);
11684 }
11685 
Jim_ListLength(Jim_Interp * interp,Jim_Obj * objPtr)11686 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
11687 {
11688     SetListFromAny(interp, objPtr);
11689     return objPtr->internalRep.listValue.len;
11690 }
11691 
Jim_ListInsertElements(Jim_Interp * interp,Jim_Obj * listPtr,int idx,int objc,Jim_Obj * const * objVec)11692 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11693     int objc, Jim_Obj *const *objVec)
11694 {
11695     JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
11696     SetListFromAny(interp, listPtr);
11697     if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
11698         idx = listPtr->internalRep.listValue.len;
11699     else if (idx < 0)
11700         idx = 0;
11701     Jim_InvalidateStringRep(listPtr);
11702     ListInsertElements(listPtr, idx, objc, objVec);
11703 }
11704 
Jim_ListGetIndex(Jim_Interp * interp,Jim_Obj * listPtr,int idx)11705 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
11706 {
11707     SetListFromAny(interp, listPtr);
11708     if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11709         (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11710         return NULL;
11711     }
11712     if (idx < 0)
11713         idx = listPtr->internalRep.listValue.len + idx;
11714     return listPtr->internalRep.listValue.ele[idx];
11715 }
11716 
Jim_ListIndex(Jim_Interp * interp,Jim_Obj * listPtr,int idx,Jim_Obj ** objPtrPtr,int flags)11717 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
11718 {
11719     *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
11720     if (*objPtrPtr == NULL) {
11721         if (flags & JIM_ERRMSG) {
11722             Jim_SetResultString(interp, "list index out of range", -1);
11723         }
11724         return JIM_ERR;
11725     }
11726     return JIM_OK;
11727 }
11728 
ListSetIndex(Jim_Interp * interp,Jim_Obj * listPtr,int idx,Jim_Obj * newObjPtr,int flags)11729 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11730     Jim_Obj *newObjPtr, int flags)
11731 {
11732     SetListFromAny(interp, listPtr);
11733     if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11734         (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11735         if (flags & JIM_ERRMSG) {
11736             Jim_SetResultString(interp, "list index out of range", -1);
11737         }
11738         return JIM_ERR;
11739     }
11740     if (idx < 0)
11741         idx = listPtr->internalRep.listValue.len + idx;
11742     Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
11743     listPtr->internalRep.listValue.ele[idx] = newObjPtr;
11744     Jim_IncrRefCount(newObjPtr);
11745     return JIM_OK;
11746 }
11747 
Jim_ListSetIndex(Jim_Interp * interp,Jim_Obj * varNamePtr,Jim_Obj * const * indexv,int indexc,Jim_Obj * newObjPtr)11748 int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
11749     Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
11750 {
11751     Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
11752     int shared, i, idx;
11753 
11754     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
11755     if (objPtr == NULL)
11756         return JIM_ERR;
11757     if ((shared = Jim_IsShared(objPtr)))
11758         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
11759     for (i = 0; i < indexc - 1; i++) {
11760         listObjPtr = objPtr;
11761         if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
11762             goto err;
11763         if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_ERRMSG) != JIM_OK) {
11764             goto err;
11765         }
11766         if (Jim_IsShared(objPtr)) {
11767             objPtr = Jim_DuplicateObj(interp, objPtr);
11768             ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
11769         }
11770         Jim_InvalidateStringRep(listObjPtr);
11771     }
11772     if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
11773         goto err;
11774     if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
11775         goto err;
11776     Jim_InvalidateStringRep(objPtr);
11777     Jim_InvalidateStringRep(varObjPtr);
11778     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
11779         goto err;
11780     Jim_SetResult(interp, varObjPtr);
11781     return JIM_OK;
11782   err:
11783     if (shared) {
11784         Jim_FreeNewObj(interp, varObjPtr);
11785     }
11786     return JIM_ERR;
11787 }
11788 
Jim_ListJoin(Jim_Interp * interp,Jim_Obj * listObjPtr,const char * joinStr,int joinStrLen)11789 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
11790 {
11791     int i;
11792     int listLen = Jim_ListLength(interp, listObjPtr);
11793     Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
11794 
11795     for (i = 0; i < listLen; ) {
11796         Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i));
11797         if (++i != listLen) {
11798             Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11799         }
11800     }
11801     return resObjPtr;
11802 }
11803 
Jim_ConcatObj(Jim_Interp * interp,int objc,Jim_Obj * const * objv)11804 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
11805 {
11806     int i;
11807 
11808     for (i = 0; i < objc; i++) {
11809         if (!Jim_IsList(objv[i]))
11810             break;
11811     }
11812     if (i == objc) {
11813         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
11814 
11815         for (i = 0; i < objc; i++)
11816             ListAppendList(objPtr, objv[i]);
11817         return objPtr;
11818     }
11819     else {
11820 
11821         int len = 0, objLen;
11822         char *bytes, *p;
11823 
11824 
11825         for (i = 0; i < objc; i++) {
11826             len += Jim_Length(objv[i]);
11827         }
11828         if (objc)
11829             len += objc - 1;
11830 
11831         p = bytes = Jim_Alloc(len + 1);
11832         for (i = 0; i < objc; i++) {
11833             const char *s = Jim_GetString(objv[i], &objLen);
11834 
11835 
11836             while (objLen && isspace(UCHAR(*s))) {
11837                 s++;
11838                 objLen--;
11839                 len--;
11840             }
11841 
11842             while (objLen && isspace(UCHAR(s[objLen - 1]))) {
11843 
11844                 if (objLen > 1 && s[objLen - 2] == '\\') {
11845                     break;
11846                 }
11847                 objLen--;
11848                 len--;
11849             }
11850             memcpy(p, s, objLen);
11851             p += objLen;
11852             if (i + 1 != objc) {
11853                 if (objLen)
11854                     *p++ = ' ';
11855                 else {
11856                     len--;
11857                 }
11858             }
11859         }
11860         *p = '\0';
11861         return Jim_NewStringObjNoAlloc(interp, bytes, len);
11862     }
11863 }
11864 
Jim_ListRange(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_Obj * firstObjPtr,Jim_Obj * lastObjPtr)11865 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr,
11866     Jim_Obj *lastObjPtr)
11867 {
11868     int first, last;
11869     int len, rangeLen;
11870 
11871     if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
11872         Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
11873         return NULL;
11874     len = Jim_ListLength(interp, listObjPtr);
11875     first = JimRelToAbsIndex(len, first);
11876     last = JimRelToAbsIndex(len, last);
11877     JimRelToAbsRange(len, &first, &last, &rangeLen);
11878     if (first == 0 && last == len) {
11879         return listObjPtr;
11880     }
11881     return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
11882 }
11883 
11884 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11885 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11886 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
11887 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11888 
11889 
JimObjectHTHashFunction(const void * key)11890 static unsigned int JimObjectHTHashFunction(const void *key)
11891 {
11892     int len;
11893     const char *str = Jim_GetString((Jim_Obj *)key, &len);
11894     return Jim_GenHashFunction((const unsigned char *)str, len);
11895 }
11896 
JimObjectHTKeyCompare(void * privdata,const void * key1,const void * key2)11897 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
11898 {
11899     return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
11900 }
11901 
JimObjectHTKeyValDup(void * privdata,const void * val)11902 static void *JimObjectHTKeyValDup(void *privdata, const void *val)
11903 {
11904     Jim_IncrRefCount((Jim_Obj *)val);
11905     return (void *)val;
11906 }
11907 
JimObjectHTKeyValDestructor(void * interp,void * val)11908 static void JimObjectHTKeyValDestructor(void *interp, void *val)
11909 {
11910     Jim_DecrRefCount(interp, (Jim_Obj *)val);
11911 }
11912 
11913 static const Jim_HashTableType JimDictHashTableType = {
11914     JimObjectHTHashFunction,
11915     JimObjectHTKeyValDup,
11916     JimObjectHTKeyValDup,
11917     JimObjectHTKeyCompare,
11918     JimObjectHTKeyValDestructor,
11919     JimObjectHTKeyValDestructor
11920 };
11921 
11922 static const Jim_ObjType dictObjType = {
11923     "dict",
11924     FreeDictInternalRep,
11925     DupDictInternalRep,
11926     UpdateStringOfDict,
11927     JIM_TYPE_NONE,
11928 };
11929 
FreeDictInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)11930 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11931 {
11932     JIM_NOTUSED(interp);
11933 
11934     Jim_FreeHashTable(objPtr->internalRep.ptr);
11935     Jim_Free(objPtr->internalRep.ptr);
11936 }
11937 
DupDictInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)11938 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11939 {
11940     Jim_HashTable *ht, *dupHt;
11941     Jim_HashTableIterator htiter;
11942     Jim_HashEntry *he;
11943 
11944 
11945     ht = srcPtr->internalRep.ptr;
11946     dupHt = Jim_Alloc(sizeof(*dupHt));
11947     Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
11948     if (ht->size != 0)
11949         Jim_ExpandHashTable(dupHt, ht->size);
11950 
11951     JimInitHashTableIterator(ht, &htiter);
11952     while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
11953         Jim_AddHashEntry(dupHt, he->key, he->u.val);
11954     }
11955 
11956     dupPtr->internalRep.ptr = dupHt;
11957     dupPtr->typePtr = &dictObjType;
11958 }
11959 
JimDictPairs(Jim_Obj * dictPtr,int * len)11960 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len)
11961 {
11962     Jim_HashTable *ht;
11963     Jim_HashTableIterator htiter;
11964     Jim_HashEntry *he;
11965     Jim_Obj **objv;
11966     int i;
11967 
11968     ht = dictPtr->internalRep.ptr;
11969 
11970 
11971     objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *));
11972     JimInitHashTableIterator(ht, &htiter);
11973     i = 0;
11974     while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
11975         objv[i++] = Jim_GetHashEntryKey(he);
11976         objv[i++] = Jim_GetHashEntryVal(he);
11977     }
11978     *len = i;
11979     return objv;
11980 }
11981 
UpdateStringOfDict(struct Jim_Obj * objPtr)11982 static void UpdateStringOfDict(struct Jim_Obj *objPtr)
11983 {
11984 
11985     int len;
11986     Jim_Obj **objv = JimDictPairs(objPtr, &len);
11987 
11988 
11989     JimMakeListStringRep(objPtr, objv, len);
11990 
11991     Jim_Free(objv);
11992 }
11993 
SetDictFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)11994 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
11995 {
11996     int listlen;
11997 
11998     if (objPtr->typePtr == &dictObjType) {
11999         return JIM_OK;
12000     }
12001 
12002     if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
12003         Jim_String(objPtr);
12004     }
12005 
12006 
12007     listlen = Jim_ListLength(interp, objPtr);
12008     if (listlen % 2) {
12009         Jim_SetResultString(interp, "missing value to go with key", -1);
12010         return JIM_ERR;
12011     }
12012     else {
12013 
12014         Jim_HashTable *ht;
12015         int i;
12016 
12017         ht = Jim_Alloc(sizeof(*ht));
12018         Jim_InitHashTable(ht, &JimDictHashTableType, interp);
12019 
12020         for (i = 0; i < listlen; i += 2) {
12021             Jim_Obj *keyObjPtr = Jim_ListGetIndex(interp, objPtr, i);
12022             Jim_Obj *valObjPtr = Jim_ListGetIndex(interp, objPtr, i + 1);
12023 
12024             Jim_ReplaceHashEntry(ht, keyObjPtr, valObjPtr);
12025         }
12026 
12027         Jim_FreeIntRep(interp, objPtr);
12028         objPtr->typePtr = &dictObjType;
12029         objPtr->internalRep.ptr = ht;
12030 
12031         return JIM_OK;
12032     }
12033 }
12034 
12035 
12036 
DictAddElement(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * keyObjPtr,Jim_Obj * valueObjPtr)12037 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12038     Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12039 {
12040     Jim_HashTable *ht = objPtr->internalRep.ptr;
12041 
12042     if (valueObjPtr == NULL) {
12043         return Jim_DeleteHashEntry(ht, keyObjPtr);
12044     }
12045     Jim_ReplaceHashEntry(ht, keyObjPtr, valueObjPtr);
12046     return JIM_OK;
12047 }
12048 
Jim_DictAddElement(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * keyObjPtr,Jim_Obj * valueObjPtr)12049 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12050     Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12051 {
12052     JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
12053     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
12054         return JIM_ERR;
12055     }
12056     Jim_InvalidateStringRep(objPtr);
12057     return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
12058 }
12059 
Jim_NewDictObj(Jim_Interp * interp,Jim_Obj * const * elements,int len)12060 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
12061 {
12062     Jim_Obj *objPtr;
12063     int i;
12064 
12065     JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
12066 
12067     objPtr = Jim_NewObj(interp);
12068     objPtr->typePtr = &dictObjType;
12069     objPtr->bytes = NULL;
12070     objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
12071     Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
12072     for (i = 0; i < len; i += 2)
12073         DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
12074     return objPtr;
12075 }
12076 
Jim_DictKey(Jim_Interp * interp,Jim_Obj * dictPtr,Jim_Obj * keyPtr,Jim_Obj ** objPtrPtr,int flags)12077 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
12078     Jim_Obj **objPtrPtr, int flags)
12079 {
12080     Jim_HashEntry *he;
12081     Jim_HashTable *ht;
12082 
12083     if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12084         return -1;
12085     }
12086     ht = dictPtr->internalRep.ptr;
12087     if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
12088         if (flags & JIM_ERRMSG) {
12089             Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
12090         }
12091         return JIM_ERR;
12092     }
12093     else {
12094         *objPtrPtr = Jim_GetHashEntryVal(he);
12095         return JIM_OK;
12096     }
12097 }
12098 
12099 
Jim_DictPairs(Jim_Interp * interp,Jim_Obj * dictPtr,Jim_Obj *** objPtrPtr,int * len)12100 int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len)
12101 {
12102     if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12103         return JIM_ERR;
12104     }
12105     *objPtrPtr = JimDictPairs(dictPtr, len);
12106 
12107     return JIM_OK;
12108 }
12109 
12110 
12111 
Jim_DictKeysVector(Jim_Interp * interp,Jim_Obj * dictPtr,Jim_Obj * const * keyv,int keyc,Jim_Obj ** objPtrPtr,int flags)12112 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
12113     Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
12114 {
12115     int i;
12116 
12117     if (keyc == 0) {
12118         *objPtrPtr = dictPtr;
12119         return JIM_OK;
12120     }
12121 
12122     for (i = 0; i < keyc; i++) {
12123         Jim_Obj *objPtr;
12124 
12125         int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
12126         if (rc != JIM_OK) {
12127             return rc;
12128         }
12129         dictPtr = objPtr;
12130     }
12131     *objPtrPtr = dictPtr;
12132     return JIM_OK;
12133 }
12134 
Jim_SetDictKeysVector(Jim_Interp * interp,Jim_Obj * varNamePtr,Jim_Obj * const * keyv,int keyc,Jim_Obj * newObjPtr,int flags)12135 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
12136     Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags)
12137 {
12138     Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
12139     int shared, i;
12140 
12141     varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
12142     if (objPtr == NULL) {
12143         if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
12144 
12145             return JIM_ERR;
12146         }
12147         varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
12148         if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
12149             Jim_FreeNewObj(interp, varObjPtr);
12150             return JIM_ERR;
12151         }
12152     }
12153     if ((shared = Jim_IsShared(objPtr)))
12154         varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
12155     for (i = 0; i < keyc; i++) {
12156         dictObjPtr = objPtr;
12157 
12158 
12159         if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
12160             goto err;
12161         }
12162 
12163         if (i == keyc - 1) {
12164 
12165             if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
12166                 if (newObjPtr || (flags & JIM_MUSTEXIST)) {
12167                     goto err;
12168                 }
12169             }
12170             break;
12171         }
12172 
12173 
12174         Jim_InvalidateStringRep(dictObjPtr);
12175         if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
12176                 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
12177             if (Jim_IsShared(objPtr)) {
12178                 objPtr = Jim_DuplicateObj(interp, objPtr);
12179                 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12180             }
12181         }
12182         else {
12183             if (newObjPtr == NULL) {
12184                 goto err;
12185             }
12186             objPtr = Jim_NewDictObj(interp, NULL, 0);
12187             DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12188         }
12189     }
12190 
12191     Jim_InvalidateStringRep(objPtr);
12192     Jim_InvalidateStringRep(varObjPtr);
12193     if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
12194         goto err;
12195     }
12196     Jim_SetResult(interp, varObjPtr);
12197     return JIM_OK;
12198   err:
12199     if (shared) {
12200         Jim_FreeNewObj(interp, varObjPtr);
12201     }
12202     return JIM_ERR;
12203 }
12204 
12205 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
12206 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
12207 
12208 static const Jim_ObjType indexObjType = {
12209     "index",
12210     NULL,
12211     NULL,
12212     UpdateStringOfIndex,
12213     JIM_TYPE_NONE,
12214 };
12215 
UpdateStringOfIndex(struct Jim_Obj * objPtr)12216 static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
12217 {
12218     if (objPtr->internalRep.intValue == -1) {
12219         JimSetStringBytes(objPtr, "end");
12220     }
12221     else {
12222         char buf[JIM_INTEGER_SPACE + 1];
12223         if (objPtr->internalRep.intValue >= 0) {
12224             sprintf(buf, "%d", objPtr->internalRep.intValue);
12225         }
12226         else {
12227 
12228             sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
12229         }
12230         JimSetStringBytes(objPtr, buf);
12231     }
12232 }
12233 
SetIndexFromAny(Jim_Interp * interp,Jim_Obj * objPtr)12234 static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12235 {
12236     int idx, end = 0;
12237     const char *str;
12238     char *endptr;
12239 
12240 
12241     str = Jim_String(objPtr);
12242 
12243 
12244     if (strncmp(str, "end", 3) == 0) {
12245         end = 1;
12246         str += 3;
12247         idx = 0;
12248     }
12249     else {
12250         idx = jim_strtol(str, &endptr);
12251 
12252         if (endptr == str) {
12253             goto badindex;
12254         }
12255         str = endptr;
12256     }
12257 
12258 
12259     if (*str == '+' || *str == '-') {
12260         int sign = (*str == '+' ? 1 : -1);
12261 
12262         idx += sign * jim_strtol(++str, &endptr);
12263         if (str == endptr || *endptr) {
12264             goto badindex;
12265         }
12266         str = endptr;
12267     }
12268 
12269     while (isspace(UCHAR(*str))) {
12270         str++;
12271     }
12272     if (*str) {
12273         goto badindex;
12274     }
12275     if (end) {
12276         if (idx > 0) {
12277             idx = INT_MAX;
12278         }
12279         else {
12280 
12281             idx--;
12282         }
12283     }
12284     else if (idx < 0) {
12285         idx = -INT_MAX;
12286     }
12287 
12288 
12289     Jim_FreeIntRep(interp, objPtr);
12290     objPtr->typePtr = &indexObjType;
12291     objPtr->internalRep.intValue = idx;
12292     return JIM_OK;
12293 
12294   badindex:
12295     Jim_SetResultFormatted(interp,
12296         "bad index \"%#s\": must be integer?[+-]integer? or end?[+-]integer?", objPtr);
12297     return JIM_ERR;
12298 }
12299 
Jim_GetIndex(Jim_Interp * interp,Jim_Obj * objPtr,int * indexPtr)12300 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
12301 {
12302 
12303     if (objPtr->typePtr == &intObjType) {
12304         jim_wide val = JimWideValue(objPtr);
12305 
12306         if (val < 0)
12307             *indexPtr = -INT_MAX;
12308         else if (val > INT_MAX)
12309             *indexPtr = INT_MAX;
12310         else
12311             *indexPtr = (int)val;
12312         return JIM_OK;
12313     }
12314     if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
12315         return JIM_ERR;
12316     *indexPtr = objPtr->internalRep.intValue;
12317     return JIM_OK;
12318 }
12319 
12320 
12321 
12322 static const char * const jimReturnCodes[] = {
12323     "ok",
12324     "error",
12325     "return",
12326     "break",
12327     "continue",
12328     "signal",
12329     "exit",
12330     "eval",
12331     NULL
12332 };
12333 
12334 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes))
12335 
12336 static const Jim_ObjType returnCodeObjType = {
12337     "return-code",
12338     NULL,
12339     NULL,
12340     NULL,
12341     JIM_TYPE_NONE,
12342 };
12343 
Jim_ReturnCode(int code)12344 const char *Jim_ReturnCode(int code)
12345 {
12346     if (code < 0 || code >= (int)jimReturnCodesSize) {
12347         return "?";
12348     }
12349     else {
12350         return jimReturnCodes[code];
12351     }
12352 }
12353 
SetReturnCodeFromAny(Jim_Interp * interp,Jim_Obj * objPtr)12354 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12355 {
12356     int returnCode;
12357     jim_wide wideValue;
12358 
12359 
12360     if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
12361         returnCode = (int)wideValue;
12362     else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
12363         Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
12364         return JIM_ERR;
12365     }
12366 
12367     Jim_FreeIntRep(interp, objPtr);
12368     objPtr->typePtr = &returnCodeObjType;
12369     objPtr->internalRep.intValue = returnCode;
12370     return JIM_OK;
12371 }
12372 
Jim_GetReturnCode(Jim_Interp * interp,Jim_Obj * objPtr,int * intPtr)12373 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
12374 {
12375     if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
12376         return JIM_ERR;
12377     *intPtr = objPtr->internalRep.intValue;
12378     return JIM_OK;
12379 }
12380 
12381 static int JimParseExprOperator(struct JimParserCtx *pc);
12382 static int JimParseExprNumber(struct JimParserCtx *pc);
12383 static int JimParseExprIrrational(struct JimParserCtx *pc);
12384 static int JimParseExprBoolean(struct JimParserCtx *pc);
12385 
12386 
12387 
12388 
12389 enum
12390 {
12391 
12392 
12393     JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
12394     JIM_EXPROP_DIV,
12395     JIM_EXPROP_MOD,
12396     JIM_EXPROP_SUB,
12397     JIM_EXPROP_ADD,
12398     JIM_EXPROP_LSHIFT,
12399     JIM_EXPROP_RSHIFT,
12400     JIM_EXPROP_ROTL,
12401     JIM_EXPROP_ROTR,
12402     JIM_EXPROP_LT,
12403     JIM_EXPROP_GT,
12404     JIM_EXPROP_LTE,
12405     JIM_EXPROP_GTE,
12406     JIM_EXPROP_NUMEQ,
12407     JIM_EXPROP_NUMNE,
12408     JIM_EXPROP_BITAND,
12409     JIM_EXPROP_BITXOR,
12410     JIM_EXPROP_BITOR,
12411 
12412 
12413     JIM_EXPROP_LOGICAND,
12414     JIM_EXPROP_LOGICAND_LEFT,
12415     JIM_EXPROP_LOGICAND_RIGHT,
12416 
12417 
12418     JIM_EXPROP_LOGICOR,
12419     JIM_EXPROP_LOGICOR_LEFT,
12420     JIM_EXPROP_LOGICOR_RIGHT,
12421 
12422 
12423 
12424     JIM_EXPROP_TERNARY,
12425     JIM_EXPROP_TERNARY_LEFT,
12426     JIM_EXPROP_TERNARY_RIGHT,
12427 
12428 
12429     JIM_EXPROP_COLON,
12430     JIM_EXPROP_COLON_LEFT,
12431     JIM_EXPROP_COLON_RIGHT,
12432 
12433     JIM_EXPROP_POW,
12434 
12435 
12436     JIM_EXPROP_STREQ,
12437     JIM_EXPROP_STRNE,
12438     JIM_EXPROP_STRIN,
12439     JIM_EXPROP_STRNI,
12440 
12441 
12442     JIM_EXPROP_NOT,
12443     JIM_EXPROP_BITNOT,
12444     JIM_EXPROP_UNARYMINUS,
12445     JIM_EXPROP_UNARYPLUS,
12446 
12447 
12448     JIM_EXPROP_FUNC_FIRST,
12449     JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST,
12450     JIM_EXPROP_FUNC_WIDE,
12451     JIM_EXPROP_FUNC_ABS,
12452     JIM_EXPROP_FUNC_DOUBLE,
12453     JIM_EXPROP_FUNC_ROUND,
12454     JIM_EXPROP_FUNC_RAND,
12455     JIM_EXPROP_FUNC_SRAND,
12456 
12457 
12458     JIM_EXPROP_FUNC_SIN,
12459     JIM_EXPROP_FUNC_COS,
12460     JIM_EXPROP_FUNC_TAN,
12461     JIM_EXPROP_FUNC_ASIN,
12462     JIM_EXPROP_FUNC_ACOS,
12463     JIM_EXPROP_FUNC_ATAN,
12464     JIM_EXPROP_FUNC_ATAN2,
12465     JIM_EXPROP_FUNC_SINH,
12466     JIM_EXPROP_FUNC_COSH,
12467     JIM_EXPROP_FUNC_TANH,
12468     JIM_EXPROP_FUNC_CEIL,
12469     JIM_EXPROP_FUNC_FLOOR,
12470     JIM_EXPROP_FUNC_EXP,
12471     JIM_EXPROP_FUNC_LOG,
12472     JIM_EXPROP_FUNC_LOG10,
12473     JIM_EXPROP_FUNC_SQRT,
12474     JIM_EXPROP_FUNC_POW,
12475     JIM_EXPROP_FUNC_HYPOT,
12476     JIM_EXPROP_FUNC_FMOD,
12477 };
12478 
12479 struct JimExprState
12480 {
12481     Jim_Obj **stack;
12482     int stacklen;
12483     int opcode;
12484     int skip;
12485 };
12486 
12487 
12488 typedef struct Jim_ExprOperator
12489 {
12490     const char *name;
12491     int (*funcop) (Jim_Interp *interp, struct JimExprState * e);
12492     unsigned char precedence;
12493     unsigned char arity;
12494     unsigned char lazy;
12495     unsigned char namelen;
12496 } Jim_ExprOperator;
12497 
ExprPush(struct JimExprState * e,Jim_Obj * obj)12498 static void ExprPush(struct JimExprState *e, Jim_Obj *obj)
12499 {
12500     Jim_IncrRefCount(obj);
12501     e->stack[e->stacklen++] = obj;
12502 }
12503 
ExprPop(struct JimExprState * e)12504 static Jim_Obj *ExprPop(struct JimExprState *e)
12505 {
12506     return e->stack[--e->stacklen];
12507 }
12508 
JimExprOpNumUnary(Jim_Interp * interp,struct JimExprState * e)12509 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e)
12510 {
12511     int intresult = 1;
12512     int rc = JIM_OK;
12513     Jim_Obj *A = ExprPop(e);
12514     double dA, dC = 0;
12515     jim_wide wA, wC = 0;
12516 
12517     if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
12518         switch (e->opcode) {
12519             case JIM_EXPROP_FUNC_INT:
12520             case JIM_EXPROP_FUNC_WIDE:
12521             case JIM_EXPROP_FUNC_ROUND:
12522             case JIM_EXPROP_UNARYPLUS:
12523                 wC = wA;
12524                 break;
12525             case JIM_EXPROP_FUNC_DOUBLE:
12526                 dC = wA;
12527                 intresult = 0;
12528                 break;
12529             case JIM_EXPROP_FUNC_ABS:
12530                 wC = wA >= 0 ? wA : -wA;
12531                 break;
12532             case JIM_EXPROP_UNARYMINUS:
12533                 wC = -wA;
12534                 break;
12535             case JIM_EXPROP_NOT:
12536                 wC = !wA;
12537                 break;
12538             default:
12539                 abort();
12540         }
12541     }
12542     else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
12543         switch (e->opcode) {
12544             case JIM_EXPROP_FUNC_INT:
12545             case JIM_EXPROP_FUNC_WIDE:
12546                 wC = dA;
12547                 break;
12548             case JIM_EXPROP_FUNC_ROUND:
12549                 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
12550                 break;
12551             case JIM_EXPROP_FUNC_DOUBLE:
12552             case JIM_EXPROP_UNARYPLUS:
12553                 dC = dA;
12554                 intresult = 0;
12555                 break;
12556             case JIM_EXPROP_FUNC_ABS:
12557 #ifdef JIM_MATH_FUNCTIONS
12558                 dC = fabs(dA);
12559 #else
12560                 dC = dA >= 0 ? dA : -dA;
12561 #endif
12562                 intresult = 0;
12563                 break;
12564             case JIM_EXPROP_UNARYMINUS:
12565                 dC = -dA;
12566                 intresult = 0;
12567                 break;
12568             case JIM_EXPROP_NOT:
12569                 wC = !dA;
12570                 break;
12571             default:
12572                 abort();
12573         }
12574     }
12575 
12576     if (rc == JIM_OK) {
12577         if (intresult) {
12578             ExprPush(e, Jim_NewIntObj(interp, wC));
12579         }
12580         else {
12581             ExprPush(e, Jim_NewDoubleObj(interp, dC));
12582         }
12583     }
12584 
12585     Jim_DecrRefCount(interp, A);
12586 
12587     return rc;
12588 }
12589 
JimRandDouble(Jim_Interp * interp)12590 static double JimRandDouble(Jim_Interp *interp)
12591 {
12592     unsigned long x;
12593     JimRandomBytes(interp, &x, sizeof(x));
12594 
12595     return (double)x / (unsigned long)~0;
12596 }
12597 
JimExprOpIntUnary(Jim_Interp * interp,struct JimExprState * e)12598 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e)
12599 {
12600     Jim_Obj *A = ExprPop(e);
12601     jim_wide wA;
12602 
12603     int rc = Jim_GetWide(interp, A, &wA);
12604     if (rc == JIM_OK) {
12605         switch (e->opcode) {
12606             case JIM_EXPROP_BITNOT:
12607                 ExprPush(e, Jim_NewIntObj(interp, ~wA));
12608                 break;
12609             case JIM_EXPROP_FUNC_SRAND:
12610                 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
12611                 ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12612                 break;
12613             default:
12614                 abort();
12615         }
12616     }
12617 
12618     Jim_DecrRefCount(interp, A);
12619 
12620     return rc;
12621 }
12622 
JimExprOpNone(Jim_Interp * interp,struct JimExprState * e)12623 static int JimExprOpNone(Jim_Interp *interp, struct JimExprState *e)
12624 {
12625     JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
12626 
12627     ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12628 
12629     return JIM_OK;
12630 }
12631 
12632 #ifdef JIM_MATH_FUNCTIONS
JimExprOpDoubleUnary(Jim_Interp * interp,struct JimExprState * e)12633 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e)
12634 {
12635     int rc;
12636     Jim_Obj *A = ExprPop(e);
12637     double dA, dC;
12638 
12639     rc = Jim_GetDouble(interp, A, &dA);
12640     if (rc == JIM_OK) {
12641         switch (e->opcode) {
12642             case JIM_EXPROP_FUNC_SIN:
12643                 dC = sin(dA);
12644                 break;
12645             case JIM_EXPROP_FUNC_COS:
12646                 dC = cos(dA);
12647                 break;
12648             case JIM_EXPROP_FUNC_TAN:
12649                 dC = tan(dA);
12650                 break;
12651             case JIM_EXPROP_FUNC_ASIN:
12652                 dC = asin(dA);
12653                 break;
12654             case JIM_EXPROP_FUNC_ACOS:
12655                 dC = acos(dA);
12656                 break;
12657             case JIM_EXPROP_FUNC_ATAN:
12658                 dC = atan(dA);
12659                 break;
12660             case JIM_EXPROP_FUNC_SINH:
12661                 dC = sinh(dA);
12662                 break;
12663             case JIM_EXPROP_FUNC_COSH:
12664                 dC = cosh(dA);
12665                 break;
12666             case JIM_EXPROP_FUNC_TANH:
12667                 dC = tanh(dA);
12668                 break;
12669             case JIM_EXPROP_FUNC_CEIL:
12670                 dC = ceil(dA);
12671                 break;
12672             case JIM_EXPROP_FUNC_FLOOR:
12673                 dC = floor(dA);
12674                 break;
12675             case JIM_EXPROP_FUNC_EXP:
12676                 dC = exp(dA);
12677                 break;
12678             case JIM_EXPROP_FUNC_LOG:
12679                 dC = log(dA);
12680                 break;
12681             case JIM_EXPROP_FUNC_LOG10:
12682                 dC = log10(dA);
12683                 break;
12684             case JIM_EXPROP_FUNC_SQRT:
12685                 dC = sqrt(dA);
12686                 break;
12687             default:
12688                 abort();
12689         }
12690         ExprPush(e, Jim_NewDoubleObj(interp, dC));
12691     }
12692 
12693     Jim_DecrRefCount(interp, A);
12694 
12695     return rc;
12696 }
12697 #endif
12698 
12699 
JimExprOpIntBin(Jim_Interp * interp,struct JimExprState * e)12700 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e)
12701 {
12702     Jim_Obj *B = ExprPop(e);
12703     Jim_Obj *A = ExprPop(e);
12704     jim_wide wA, wB;
12705     int rc = JIM_ERR;
12706 
12707     if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
12708         jim_wide wC;
12709 
12710         rc = JIM_OK;
12711 
12712         switch (e->opcode) {
12713             case JIM_EXPROP_LSHIFT:
12714                 wC = wA << wB;
12715                 break;
12716             case JIM_EXPROP_RSHIFT:
12717                 wC = wA >> wB;
12718                 break;
12719             case JIM_EXPROP_BITAND:
12720                 wC = wA & wB;
12721                 break;
12722             case JIM_EXPROP_BITXOR:
12723                 wC = wA ^ wB;
12724                 break;
12725             case JIM_EXPROP_BITOR:
12726                 wC = wA | wB;
12727                 break;
12728             case JIM_EXPROP_MOD:
12729                 if (wB == 0) {
12730                     wC = 0;
12731                     Jim_SetResultString(interp, "Division by zero", -1);
12732                     rc = JIM_ERR;
12733                 }
12734                 else {
12735                     int negative = 0;
12736 
12737                     if (wB < 0) {
12738                         wB = -wB;
12739                         wA = -wA;
12740                         negative = 1;
12741                     }
12742                     wC = wA % wB;
12743                     if (wC < 0) {
12744                         wC += wB;
12745                     }
12746                     if (negative) {
12747                         wC = -wC;
12748                     }
12749                 }
12750                 break;
12751             case JIM_EXPROP_ROTL:
12752             case JIM_EXPROP_ROTR:{
12753 
12754                     unsigned long uA = (unsigned long)wA;
12755                     unsigned long uB = (unsigned long)wB;
12756                     const unsigned int S = sizeof(unsigned long) * 8;
12757 
12758 
12759                     uB %= S;
12760 
12761                     if (e->opcode == JIM_EXPROP_ROTR) {
12762                         uB = S - uB;
12763                     }
12764                     wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
12765                     break;
12766                 }
12767             default:
12768                 abort();
12769         }
12770         ExprPush(e, Jim_NewIntObj(interp, wC));
12771 
12772     }
12773 
12774     Jim_DecrRefCount(interp, A);
12775     Jim_DecrRefCount(interp, B);
12776 
12777     return rc;
12778 }
12779 
12780 
12781 
JimExprOpBin(Jim_Interp * interp,struct JimExprState * e)12782 static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e)
12783 {
12784     int rc = JIM_OK;
12785     double dA, dB, dC = 0;
12786     jim_wide wA, wB, wC = 0;
12787 
12788     Jim_Obj *B = ExprPop(e);
12789     Jim_Obj *A = ExprPop(e);
12790 
12791     if ((A->typePtr != &doubleObjType || A->bytes) &&
12792         (B->typePtr != &doubleObjType || B->bytes) &&
12793         JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
12794 
12795 
12796 
12797         switch (e->opcode) {
12798             case JIM_EXPROP_POW:
12799             case JIM_EXPROP_FUNC_POW:
12800                 if (wA == 0 && wB < 0) {
12801                     Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
12802                     rc = JIM_ERR;
12803                     goto done;
12804                 }
12805                 wC = JimPowWide(wA, wB);
12806                 goto intresult;
12807             case JIM_EXPROP_ADD:
12808                 wC = wA + wB;
12809                 goto intresult;
12810             case JIM_EXPROP_SUB:
12811                 wC = wA - wB;
12812                 goto intresult;
12813             case JIM_EXPROP_MUL:
12814                 wC = wA * wB;
12815                 goto intresult;
12816             case JIM_EXPROP_DIV:
12817                 if (wB == 0) {
12818                     Jim_SetResultString(interp, "Division by zero", -1);
12819                     rc = JIM_ERR;
12820                     goto done;
12821                 }
12822                 else {
12823                     if (wB < 0) {
12824                         wB = -wB;
12825                         wA = -wA;
12826                     }
12827                     wC = wA / wB;
12828                     if (wA % wB < 0) {
12829                         wC--;
12830                     }
12831                     goto intresult;
12832                 }
12833             case JIM_EXPROP_LT:
12834                 wC = wA < wB;
12835                 goto intresult;
12836             case JIM_EXPROP_GT:
12837                 wC = wA > wB;
12838                 goto intresult;
12839             case JIM_EXPROP_LTE:
12840                 wC = wA <= wB;
12841                 goto intresult;
12842             case JIM_EXPROP_GTE:
12843                 wC = wA >= wB;
12844                 goto intresult;
12845             case JIM_EXPROP_NUMEQ:
12846                 wC = wA == wB;
12847                 goto intresult;
12848             case JIM_EXPROP_NUMNE:
12849                 wC = wA != wB;
12850                 goto intresult;
12851         }
12852     }
12853     if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
12854         switch (e->opcode) {
12855 #ifndef JIM_MATH_FUNCTIONS
12856             case JIM_EXPROP_POW:
12857             case JIM_EXPROP_FUNC_POW:
12858             case JIM_EXPROP_FUNC_ATAN2:
12859             case JIM_EXPROP_FUNC_HYPOT:
12860             case JIM_EXPROP_FUNC_FMOD:
12861                 Jim_SetResultString(interp, "unsupported", -1);
12862                 rc = JIM_ERR;
12863                 goto done;
12864 #else
12865             case JIM_EXPROP_POW:
12866             case JIM_EXPROP_FUNC_POW:
12867                 dC = pow(dA, dB);
12868                 goto doubleresult;
12869             case JIM_EXPROP_FUNC_ATAN2:
12870                 dC = atan2(dA, dB);
12871                 goto doubleresult;
12872             case JIM_EXPROP_FUNC_HYPOT:
12873                 dC = hypot(dA, dB);
12874                 goto doubleresult;
12875             case JIM_EXPROP_FUNC_FMOD:
12876                 dC = fmod(dA, dB);
12877                 goto doubleresult;
12878 #endif
12879             case JIM_EXPROP_ADD:
12880                 dC = dA + dB;
12881                 goto doubleresult;
12882             case JIM_EXPROP_SUB:
12883                 dC = dA - dB;
12884                 goto doubleresult;
12885             case JIM_EXPROP_MUL:
12886                 dC = dA * dB;
12887                 goto doubleresult;
12888             case JIM_EXPROP_DIV:
12889                 if (dB == 0) {
12890 #ifdef INFINITY
12891                     dC = dA < 0 ? -INFINITY : INFINITY;
12892 #else
12893                     dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
12894 #endif
12895                 }
12896                 else {
12897                     dC = dA / dB;
12898                 }
12899                 goto doubleresult;
12900             case JIM_EXPROP_LT:
12901                 wC = dA < dB;
12902                 goto intresult;
12903             case JIM_EXPROP_GT:
12904                 wC = dA > dB;
12905                 goto intresult;
12906             case JIM_EXPROP_LTE:
12907                 wC = dA <= dB;
12908                 goto intresult;
12909             case JIM_EXPROP_GTE:
12910                 wC = dA >= dB;
12911                 goto intresult;
12912             case JIM_EXPROP_NUMEQ:
12913                 wC = dA == dB;
12914                 goto intresult;
12915             case JIM_EXPROP_NUMNE:
12916                 wC = dA != dB;
12917                 goto intresult;
12918         }
12919     }
12920     else {
12921 
12922 
12923 
12924         int i = Jim_StringCompareObj(interp, A, B, 0);
12925 
12926         switch (e->opcode) {
12927             case JIM_EXPROP_LT:
12928                 wC = i < 0;
12929                 goto intresult;
12930             case JIM_EXPROP_GT:
12931                 wC = i > 0;
12932                 goto intresult;
12933             case JIM_EXPROP_LTE:
12934                 wC = i <= 0;
12935                 goto intresult;
12936             case JIM_EXPROP_GTE:
12937                 wC = i >= 0;
12938                 goto intresult;
12939             case JIM_EXPROP_NUMEQ:
12940                 wC = i == 0;
12941                 goto intresult;
12942             case JIM_EXPROP_NUMNE:
12943                 wC = i != 0;
12944                 goto intresult;
12945         }
12946     }
12947 
12948     rc = JIM_ERR;
12949 done:
12950     Jim_DecrRefCount(interp, A);
12951     Jim_DecrRefCount(interp, B);
12952     return rc;
12953 intresult:
12954     ExprPush(e, Jim_NewIntObj(interp, wC));
12955     goto done;
12956 doubleresult:
12957     ExprPush(e, Jim_NewDoubleObj(interp, dC));
12958     goto done;
12959 }
12960 
JimSearchList(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_Obj * valObj)12961 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
12962 {
12963     int listlen;
12964     int i;
12965 
12966     listlen = Jim_ListLength(interp, listObjPtr);
12967     for (i = 0; i < listlen; i++) {
12968         if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
12969             return 1;
12970         }
12971     }
12972     return 0;
12973 }
12974 
JimExprOpStrBin(Jim_Interp * interp,struct JimExprState * e)12975 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e)
12976 {
12977     Jim_Obj *B = ExprPop(e);
12978     Jim_Obj *A = ExprPop(e);
12979 
12980     jim_wide wC;
12981 
12982     switch (e->opcode) {
12983         case JIM_EXPROP_STREQ:
12984         case JIM_EXPROP_STRNE:
12985             wC = Jim_StringEqObj(A, B);
12986             if (e->opcode == JIM_EXPROP_STRNE) {
12987                 wC = !wC;
12988             }
12989             break;
12990         case JIM_EXPROP_STRIN:
12991             wC = JimSearchList(interp, B, A);
12992             break;
12993         case JIM_EXPROP_STRNI:
12994             wC = !JimSearchList(interp, B, A);
12995             break;
12996         default:
12997             abort();
12998     }
12999     ExprPush(e, Jim_NewIntObj(interp, wC));
13000 
13001     Jim_DecrRefCount(interp, A);
13002     Jim_DecrRefCount(interp, B);
13003 
13004     return JIM_OK;
13005 }
13006 
ExprBool(Jim_Interp * interp,Jim_Obj * obj)13007 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
13008 {
13009     long l;
13010     double d;
13011     int b;
13012 
13013     if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
13014         return l != 0;
13015     }
13016     if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
13017         return d != 0;
13018     }
13019     if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
13020         return b != 0;
13021     }
13022     return -1;
13023 }
13024 
JimExprOpAndLeft(Jim_Interp * interp,struct JimExprState * e)13025 static int JimExprOpAndLeft(Jim_Interp *interp, struct JimExprState *e)
13026 {
13027     Jim_Obj *skip = ExprPop(e);
13028     Jim_Obj *A = ExprPop(e);
13029     int rc = JIM_OK;
13030 
13031     switch (ExprBool(interp, A)) {
13032         case 0:
13033 
13034             e->skip = JimWideValue(skip);
13035             ExprPush(e, Jim_NewIntObj(interp, 0));
13036             break;
13037 
13038         case 1:
13039 
13040             break;
13041 
13042         case -1:
13043 
13044             rc = JIM_ERR;
13045     }
13046     Jim_DecrRefCount(interp, A);
13047     Jim_DecrRefCount(interp, skip);
13048 
13049     return rc;
13050 }
13051 
JimExprOpOrLeft(Jim_Interp * interp,struct JimExprState * e)13052 static int JimExprOpOrLeft(Jim_Interp *interp, struct JimExprState *e)
13053 {
13054     Jim_Obj *skip = ExprPop(e);
13055     Jim_Obj *A = ExprPop(e);
13056     int rc = JIM_OK;
13057 
13058     switch (ExprBool(interp, A)) {
13059         case 0:
13060 
13061             break;
13062 
13063         case 1:
13064 
13065             e->skip = JimWideValue(skip);
13066             ExprPush(e, Jim_NewIntObj(interp, 1));
13067             break;
13068 
13069         case -1:
13070 
13071             rc = JIM_ERR;
13072             break;
13073     }
13074     Jim_DecrRefCount(interp, A);
13075     Jim_DecrRefCount(interp, skip);
13076 
13077     return rc;
13078 }
13079 
JimExprOpAndOrRight(Jim_Interp * interp,struct JimExprState * e)13080 static int JimExprOpAndOrRight(Jim_Interp *interp, struct JimExprState *e)
13081 {
13082     Jim_Obj *A = ExprPop(e);
13083     int rc = JIM_OK;
13084 
13085     switch (ExprBool(interp, A)) {
13086         case 0:
13087             ExprPush(e, Jim_NewIntObj(interp, 0));
13088             break;
13089 
13090         case 1:
13091             ExprPush(e, Jim_NewIntObj(interp, 1));
13092             break;
13093 
13094         case -1:
13095 
13096             rc = JIM_ERR;
13097             break;
13098     }
13099     Jim_DecrRefCount(interp, A);
13100 
13101     return rc;
13102 }
13103 
JimExprOpTernaryLeft(Jim_Interp * interp,struct JimExprState * e)13104 static int JimExprOpTernaryLeft(Jim_Interp *interp, struct JimExprState *e)
13105 {
13106     Jim_Obj *skip = ExprPop(e);
13107     Jim_Obj *A = ExprPop(e);
13108     int rc = JIM_OK;
13109 
13110 
13111     ExprPush(e, A);
13112 
13113     switch (ExprBool(interp, A)) {
13114         case 0:
13115 
13116             e->skip = JimWideValue(skip);
13117 
13118             ExprPush(e, Jim_NewIntObj(interp, 0));
13119             break;
13120 
13121         case 1:
13122 
13123             break;
13124 
13125         case -1:
13126 
13127             rc = JIM_ERR;
13128             break;
13129     }
13130     Jim_DecrRefCount(interp, A);
13131     Jim_DecrRefCount(interp, skip);
13132 
13133     return rc;
13134 }
13135 
JimExprOpColonLeft(Jim_Interp * interp,struct JimExprState * e)13136 static int JimExprOpColonLeft(Jim_Interp *interp, struct JimExprState *e)
13137 {
13138     Jim_Obj *skip = ExprPop(e);
13139     Jim_Obj *B = ExprPop(e);
13140     Jim_Obj *A = ExprPop(e);
13141 
13142 
13143     if (ExprBool(interp, A)) {
13144 
13145         e->skip = JimWideValue(skip);
13146 
13147         ExprPush(e, B);
13148     }
13149 
13150     Jim_DecrRefCount(interp, skip);
13151     Jim_DecrRefCount(interp, A);
13152     Jim_DecrRefCount(interp, B);
13153     return JIM_OK;
13154 }
13155 
JimExprOpNull(Jim_Interp * interp,struct JimExprState * e)13156 static int JimExprOpNull(Jim_Interp *interp, struct JimExprState *e)
13157 {
13158     return JIM_OK;
13159 }
13160 
13161 enum
13162 {
13163     LAZY_NONE,
13164     LAZY_OP,
13165     LAZY_LEFT,
13166     LAZY_RIGHT,
13167     RIGHT_ASSOC,
13168 };
13169 
13170 #define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
13171 #define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, LAZY_NONE)
13172 
13173 static const struct Jim_ExprOperator Jim_ExprOperators[] = {
13174     OPRINIT("*", 110, 2, JimExprOpBin),
13175     OPRINIT("/", 110, 2, JimExprOpBin),
13176     OPRINIT("%", 110, 2, JimExprOpIntBin),
13177 
13178     OPRINIT("-", 100, 2, JimExprOpBin),
13179     OPRINIT("+", 100, 2, JimExprOpBin),
13180 
13181     OPRINIT("<<", 90, 2, JimExprOpIntBin),
13182     OPRINIT(">>", 90, 2, JimExprOpIntBin),
13183 
13184     OPRINIT("<<<", 90, 2, JimExprOpIntBin),
13185     OPRINIT(">>>", 90, 2, JimExprOpIntBin),
13186 
13187     OPRINIT("<", 80, 2, JimExprOpBin),
13188     OPRINIT(">", 80, 2, JimExprOpBin),
13189     OPRINIT("<=", 80, 2, JimExprOpBin),
13190     OPRINIT(">=", 80, 2, JimExprOpBin),
13191 
13192     OPRINIT("==", 70, 2, JimExprOpBin),
13193     OPRINIT("!=", 70, 2, JimExprOpBin),
13194 
13195     OPRINIT("&", 50, 2, JimExprOpIntBin),
13196     OPRINIT("^", 49, 2, JimExprOpIntBin),
13197     OPRINIT("|", 48, 2, JimExprOpIntBin),
13198 
13199     OPRINIT_ATTR("&&", 10, 2, NULL, LAZY_OP),
13200     OPRINIT_ATTR(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT),
13201     OPRINIT_ATTR(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT),
13202 
13203     OPRINIT_ATTR("||", 9, 2, NULL, LAZY_OP),
13204     OPRINIT_ATTR(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT),
13205     OPRINIT_ATTR(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT),
13206 
13207     OPRINIT_ATTR("?", 5, 2, JimExprOpNull, LAZY_OP),
13208     OPRINIT_ATTR(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT),
13209     OPRINIT_ATTR(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
13210 
13211     OPRINIT_ATTR(":", 5, 2, JimExprOpNull, LAZY_OP),
13212     OPRINIT_ATTR(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT),
13213     OPRINIT_ATTR(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
13214 
13215 
13216     OPRINIT_ATTR("**", 120, 2, JimExprOpBin, RIGHT_ASSOC),
13217 
13218     OPRINIT("eq", 60, 2, JimExprOpStrBin),
13219     OPRINIT("ne", 60, 2, JimExprOpStrBin),
13220 
13221     OPRINIT("in", 55, 2, JimExprOpStrBin),
13222     OPRINIT("ni", 55, 2, JimExprOpStrBin),
13223 
13224     OPRINIT("!", 150, 1, JimExprOpNumUnary),
13225     OPRINIT("~", 150, 1, JimExprOpIntUnary),
13226     OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
13227     OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
13228 
13229 
13230 
13231     OPRINIT("int", 200, 1, JimExprOpNumUnary),
13232     OPRINIT("wide", 200, 1, JimExprOpNumUnary),
13233     OPRINIT("abs", 200, 1, JimExprOpNumUnary),
13234     OPRINIT("double", 200, 1, JimExprOpNumUnary),
13235     OPRINIT("round", 200, 1, JimExprOpNumUnary),
13236     OPRINIT("rand", 200, 0, JimExprOpNone),
13237     OPRINIT("srand", 200, 1, JimExprOpIntUnary),
13238 
13239 #ifdef JIM_MATH_FUNCTIONS
13240     OPRINIT("sin", 200, 1, JimExprOpDoubleUnary),
13241     OPRINIT("cos", 200, 1, JimExprOpDoubleUnary),
13242     OPRINIT("tan", 200, 1, JimExprOpDoubleUnary),
13243     OPRINIT("asin", 200, 1, JimExprOpDoubleUnary),
13244     OPRINIT("acos", 200, 1, JimExprOpDoubleUnary),
13245     OPRINIT("atan", 200, 1, JimExprOpDoubleUnary),
13246     OPRINIT("atan2", 200, 2, JimExprOpBin),
13247     OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary),
13248     OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary),
13249     OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary),
13250     OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary),
13251     OPRINIT("floor", 200, 1, JimExprOpDoubleUnary),
13252     OPRINIT("exp", 200, 1, JimExprOpDoubleUnary),
13253     OPRINIT("log", 200, 1, JimExprOpDoubleUnary),
13254     OPRINIT("log10", 200, 1, JimExprOpDoubleUnary),
13255     OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary),
13256     OPRINIT("pow", 200, 2, JimExprOpBin),
13257     OPRINIT("hypot", 200, 2, JimExprOpBin),
13258     OPRINIT("fmod", 200, 2, JimExprOpBin),
13259 #endif
13260 };
13261 #undef OPRINIT
13262 #undef OPRINIT_LAZY
13263 
13264 #define JIM_EXPR_OPERATORS_NUM \
13265     (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
13266 
JimParseExpression(struct JimParserCtx * pc)13267 static int JimParseExpression(struct JimParserCtx *pc)
13268 {
13269 
13270     while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
13271         if (*pc->p == '\n') {
13272             pc->linenr++;
13273         }
13274         pc->p++;
13275         pc->len--;
13276     }
13277 
13278 
13279     pc->tline = pc->linenr;
13280     pc->tstart = pc->p;
13281 
13282     if (pc->len == 0) {
13283         pc->tend = pc->p;
13284         pc->tt = JIM_TT_EOL;
13285         pc->eof = 1;
13286         return JIM_OK;
13287     }
13288     switch (*(pc->p)) {
13289         case '(':
13290                 pc->tt = JIM_TT_SUBEXPR_START;
13291                 goto singlechar;
13292         case ')':
13293                 pc->tt = JIM_TT_SUBEXPR_END;
13294                 goto singlechar;
13295         case ',':
13296             pc->tt = JIM_TT_SUBEXPR_COMMA;
13297 singlechar:
13298             pc->tend = pc->p;
13299             pc->p++;
13300             pc->len--;
13301             break;
13302         case '[':
13303             return JimParseCmd(pc);
13304         case '$':
13305             if (JimParseVar(pc) == JIM_ERR)
13306                 return JimParseExprOperator(pc);
13307             else {
13308 
13309                 if (pc->tt == JIM_TT_EXPRSUGAR) {
13310                     return JIM_ERR;
13311                 }
13312                 return JIM_OK;
13313             }
13314             break;
13315         case '0':
13316         case '1':
13317         case '2':
13318         case '3':
13319         case '4':
13320         case '5':
13321         case '6':
13322         case '7':
13323         case '8':
13324         case '9':
13325         case '.':
13326             return JimParseExprNumber(pc);
13327         case '"':
13328             return JimParseQuote(pc);
13329         case '{':
13330             return JimParseBrace(pc);
13331 
13332         case 'N':
13333         case 'I':
13334         case 'n':
13335         case 'i':
13336             if (JimParseExprIrrational(pc) == JIM_ERR)
13337                 if (JimParseExprBoolean(pc) == JIM_ERR)
13338                     return JimParseExprOperator(pc);
13339             break;
13340         case 't':
13341         case 'f':
13342         case 'o':
13343         case 'y':
13344             if (JimParseExprBoolean(pc) == JIM_ERR)
13345                 return JimParseExprOperator(pc);
13346             break;
13347         default:
13348             return JimParseExprOperator(pc);
13349             break;
13350     }
13351     return JIM_OK;
13352 }
13353 
JimParseExprNumber(struct JimParserCtx * pc)13354 static int JimParseExprNumber(struct JimParserCtx *pc)
13355 {
13356     char *end;
13357 
13358 
13359     pc->tt = JIM_TT_EXPR_INT;
13360 
13361     jim_strtoull(pc->p, (char **)&pc->p);
13362 
13363     if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
13364         if (strtod(pc->tstart, &end)) { }
13365         if (end == pc->tstart)
13366             return JIM_ERR;
13367         if (end > pc->p) {
13368 
13369             pc->tt = JIM_TT_EXPR_DOUBLE;
13370             pc->p = end;
13371         }
13372     }
13373     pc->tend = pc->p - 1;
13374     pc->len -= (pc->p - pc->tstart);
13375     return JIM_OK;
13376 }
13377 
JimParseExprIrrational(struct JimParserCtx * pc)13378 static int JimParseExprIrrational(struct JimParserCtx *pc)
13379 {
13380     const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
13381     int i;
13382 
13383     for (i = 0; irrationals[i]; i++) {
13384         const char *irr = irrationals[i];
13385 
13386         if (strncmp(irr, pc->p, 3) == 0) {
13387             pc->p += 3;
13388             pc->len -= 3;
13389             pc->tend = pc->p - 1;
13390             pc->tt = JIM_TT_EXPR_DOUBLE;
13391             return JIM_OK;
13392         }
13393     }
13394     return JIM_ERR;
13395 }
13396 
JimParseExprBoolean(struct JimParserCtx * pc)13397 static int JimParseExprBoolean(struct JimParserCtx *pc)
13398 {
13399     const char *booleans[] = { "false", "no", "off", "true", "yes", "on", NULL };
13400     const int lengths[] = { 5, 2, 3, 4, 3, 2, 0 };
13401     int i;
13402 
13403     for (i = 0; booleans[i]; i++) {
13404         const char *boolean = booleans[i];
13405         int length = lengths[i];
13406 
13407         if (strncmp(boolean, pc->p, length) == 0) {
13408             pc->p += length;
13409             pc->len -= length;
13410             pc->tend = pc->p - 1;
13411             pc->tt = JIM_TT_EXPR_BOOLEAN;
13412             return JIM_OK;
13413         }
13414     }
13415     return JIM_ERR;
13416 }
13417 
JimParseExprOperator(struct JimParserCtx * pc)13418 static int JimParseExprOperator(struct JimParserCtx *pc)
13419 {
13420     int i;
13421     int bestIdx = -1, bestLen = 0;
13422 
13423 
13424     for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
13425         const char * const opname = Jim_ExprOperators[i].name;
13426         const int oplen = Jim_ExprOperators[i].namelen;
13427 
13428         if (opname == NULL || opname[0] != pc->p[0]) {
13429             continue;
13430         }
13431 
13432         if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) {
13433             bestIdx = i + JIM_TT_EXPR_OP;
13434             bestLen = oplen;
13435         }
13436     }
13437     if (bestIdx == -1) {
13438         return JIM_ERR;
13439     }
13440 
13441 
13442     if (bestIdx >= JIM_EXPROP_FUNC_FIRST) {
13443         const char *p = pc->p + bestLen;
13444         int len = pc->len - bestLen;
13445 
13446         while (len && isspace(UCHAR(*p))) {
13447             len--;
13448             p++;
13449         }
13450         if (*p != '(') {
13451             return JIM_ERR;
13452         }
13453     }
13454     pc->tend = pc->p + bestLen - 1;
13455     pc->p += bestLen;
13456     pc->len -= bestLen;
13457 
13458     pc->tt = bestIdx;
13459     return JIM_OK;
13460 }
13461 
JimExprOperatorInfoByOpcode(int opcode)13462 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
13463 {
13464     static Jim_ExprOperator dummy_op;
13465     if (opcode < JIM_TT_EXPR_OP) {
13466         return &dummy_op;
13467     }
13468     return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
13469 }
13470 
jim_tt_name(int type)13471 const char *jim_tt_name(int type)
13472 {
13473     static const char * const tt_names[JIM_TT_EXPR_OP] =
13474         { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT",
13475             "DBL", "BOO", "$()" };
13476     if (type < JIM_TT_EXPR_OP) {
13477         return tt_names[type];
13478     }
13479     else if (type == JIM_EXPROP_UNARYMINUS) {
13480         return "-VE";
13481     }
13482     else if (type == JIM_EXPROP_UNARYPLUS) {
13483         return "+VE";
13484     }
13485     else {
13486         const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(type);
13487         static char buf[20];
13488 
13489         if (op->name) {
13490             return op->name;
13491         }
13492         sprintf(buf, "(%d)", type);
13493         return buf;
13494     }
13495 }
13496 
13497 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
13498 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
13499 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13500 
13501 static const Jim_ObjType exprObjType = {
13502     "expression",
13503     FreeExprInternalRep,
13504     DupExprInternalRep,
13505     NULL,
13506     JIM_TYPE_REFERENCES,
13507 };
13508 
13509 
13510 typedef struct ExprByteCode
13511 {
13512     ScriptToken *token;
13513     int len;
13514     int inUse;
13515 } ExprByteCode;
13516 
ExprFreeByteCode(Jim_Interp * interp,ExprByteCode * expr)13517 static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr)
13518 {
13519     int i;
13520 
13521     for (i = 0; i < expr->len; i++) {
13522         Jim_DecrRefCount(interp, expr->token[i].objPtr);
13523     }
13524     Jim_Free(expr->token);
13525     Jim_Free(expr);
13526 }
13527 
FreeExprInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)13528 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
13529 {
13530     ExprByteCode *expr = (void *)objPtr->internalRep.ptr;
13531 
13532     if (expr) {
13533         if (--expr->inUse != 0) {
13534             return;
13535         }
13536 
13537         ExprFreeByteCode(interp, expr);
13538     }
13539 }
13540 
DupExprInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)13541 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
13542 {
13543     JIM_NOTUSED(interp);
13544     JIM_NOTUSED(srcPtr);
13545 
13546 
13547     dupPtr->typePtr = NULL;
13548 }
13549 
ExprCheckCorrectness(Jim_Interp * interp,Jim_Obj * exprObjPtr,ExprByteCode * expr)13550 static int ExprCheckCorrectness(Jim_Interp *interp, Jim_Obj *exprObjPtr, ExprByteCode * expr)
13551 {
13552     int i;
13553     int stacklen = 0;
13554     int ternary = 0;
13555     int lasttt = JIM_TT_NONE;
13556     const char *errmsg;
13557 
13558     for (i = 0; i < expr->len; i++) {
13559         ScriptToken *t = &expr->token[i];
13560         const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13561         lasttt = t->type;
13562 
13563         stacklen -= op->arity;
13564 
13565         if (stacklen < 0) {
13566             break;
13567         }
13568         if (t->type == JIM_EXPROP_TERNARY || t->type == JIM_EXPROP_TERNARY_LEFT) {
13569             ternary++;
13570         }
13571         else if (t->type == JIM_EXPROP_COLON || t->type == JIM_EXPROP_COLON_LEFT) {
13572             ternary--;
13573         }
13574 
13575 
13576         stacklen++;
13577     }
13578     if (stacklen == 1 && ternary == 0) {
13579         return JIM_OK;
13580     }
13581 
13582     if (stacklen <= 0) {
13583 
13584         if (lasttt >= JIM_EXPROP_FUNC_FIRST) {
13585             errmsg = "too few arguments for math function";
13586             Jim_SetResultString(interp, "too few arguments for math function", -1);
13587         } else {
13588             errmsg = "premature end of expression";
13589         }
13590     }
13591     else if (stacklen > 1) {
13592         if (lasttt >= JIM_EXPROP_FUNC_FIRST) {
13593             errmsg = "too many arguments for math function";
13594         } else {
13595             errmsg = "extra tokens at end of expression";
13596         }
13597     }
13598     else {
13599         errmsg = "invalid ternary expression";
13600     }
13601     Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": %s", exprObjPtr, errmsg);
13602     return JIM_ERR;
13603 }
13604 
ExprAddLazyOperator(Jim_Interp * interp,ExprByteCode * expr,ParseToken * t)13605 static int ExprAddLazyOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
13606 {
13607     int i;
13608 
13609     int leftindex, arity, offset;
13610 
13611 
13612     leftindex = expr->len - 1;
13613 
13614     arity = 1;
13615     while (arity) {
13616         ScriptToken *tt = &expr->token[leftindex];
13617 
13618         if (tt->type >= JIM_TT_EXPR_OP) {
13619             arity += JimExprOperatorInfoByOpcode(tt->type)->arity;
13620         }
13621         arity--;
13622         if (--leftindex < 0) {
13623             return JIM_ERR;
13624         }
13625     }
13626     leftindex++;
13627 
13628 
13629     memmove(&expr->token[leftindex + 2], &expr->token[leftindex],
13630         sizeof(*expr->token) * (expr->len - leftindex));
13631     expr->len += 2;
13632     offset = (expr->len - leftindex) - 1;
13633 
13634     expr->token[leftindex + 1].type = t->type + 1;
13635     expr->token[leftindex + 1].objPtr = interp->emptyObj;
13636 
13637     expr->token[leftindex].type = JIM_TT_EXPR_INT;
13638     expr->token[leftindex].objPtr = Jim_NewIntObj(interp, offset);
13639 
13640 
13641     expr->token[expr->len].objPtr = interp->emptyObj;
13642     expr->token[expr->len].type = t->type + 2;
13643     expr->len++;
13644 
13645 
13646     for (i = leftindex - 1; i > 0; i--) {
13647         const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(expr->token[i].type);
13648         if (op->lazy == LAZY_LEFT) {
13649             if (JimWideValue(expr->token[i - 1].objPtr) + i - 1 >= leftindex) {
13650                 JimWideValue(expr->token[i - 1].objPtr) += 2;
13651             }
13652         }
13653     }
13654     return JIM_OK;
13655 }
13656 
ExprAddOperator(Jim_Interp * interp,ExprByteCode * expr,ParseToken * t)13657 static int ExprAddOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
13658 {
13659     struct ScriptToken *token = &expr->token[expr->len];
13660     const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13661 
13662     if (op->lazy == LAZY_OP) {
13663         if (ExprAddLazyOperator(interp, expr, t) != JIM_OK) {
13664             Jim_SetResultFormatted(interp, "Expression has bad operands to %s", op->name);
13665             return JIM_ERR;
13666         }
13667     }
13668     else {
13669         token->objPtr = interp->emptyObj;
13670         token->type = t->type;
13671         expr->len++;
13672     }
13673     return JIM_OK;
13674 }
13675 
ExprTernaryGetColonLeftIndex(ExprByteCode * expr,int right_index)13676 static int ExprTernaryGetColonLeftIndex(ExprByteCode *expr, int right_index)
13677 {
13678     int ternary_count = 1;
13679 
13680     right_index--;
13681 
13682     while (right_index > 1) {
13683         if (expr->token[right_index].type == JIM_EXPROP_TERNARY_LEFT) {
13684             ternary_count--;
13685         }
13686         else if (expr->token[right_index].type == JIM_EXPROP_COLON_RIGHT) {
13687             ternary_count++;
13688         }
13689         else if (expr->token[right_index].type == JIM_EXPROP_COLON_LEFT && ternary_count == 1) {
13690             return right_index;
13691         }
13692         right_index--;
13693     }
13694 
13695 
13696     return -1;
13697 }
13698 
ExprTernaryGetMoveIndices(ExprByteCode * expr,int right_index,int * prev_right_index,int * prev_left_index)13699 static int ExprTernaryGetMoveIndices(ExprByteCode *expr, int right_index, int *prev_right_index, int *prev_left_index)
13700 {
13701     int i = right_index - 1;
13702     int ternary_count = 1;
13703 
13704     while (i > 1) {
13705         if (expr->token[i].type == JIM_EXPROP_TERNARY_LEFT) {
13706             if (--ternary_count == 0 && expr->token[i - 2].type == JIM_EXPROP_COLON_RIGHT) {
13707                 *prev_right_index = i - 2;
13708                 *prev_left_index = ExprTernaryGetColonLeftIndex(expr, *prev_right_index);
13709                 return 1;
13710             }
13711         }
13712         else if (expr->token[i].type == JIM_EXPROP_COLON_RIGHT) {
13713             if (ternary_count == 0) {
13714                 return 0;
13715             }
13716             ternary_count++;
13717         }
13718         i--;
13719     }
13720     return 0;
13721 }
13722 
ExprTernaryReorderExpression(Jim_Interp * interp,ExprByteCode * expr)13723 static void ExprTernaryReorderExpression(Jim_Interp *interp, ExprByteCode *expr)
13724 {
13725     int i;
13726 
13727     for (i = expr->len - 1; i > 1; i--) {
13728         int prev_right_index;
13729         int prev_left_index;
13730         int j;
13731         ScriptToken tmp;
13732 
13733         if (expr->token[i].type != JIM_EXPROP_COLON_RIGHT) {
13734             continue;
13735         }
13736 
13737 
13738         if (ExprTernaryGetMoveIndices(expr, i, &prev_right_index, &prev_left_index) == 0) {
13739             continue;
13740         }
13741 
13742         tmp = expr->token[prev_right_index];
13743         for (j = prev_right_index; j < i; j++) {
13744             expr->token[j] = expr->token[j + 1];
13745         }
13746         expr->token[i] = tmp;
13747 
13748         JimWideValue(expr->token[prev_left_index-1].objPtr) += (i - prev_right_index);
13749 
13750 
13751         i++;
13752     }
13753 }
13754 
ExprCreateByteCode(Jim_Interp * interp,const ParseTokenList * tokenlist,Jim_Obj * exprObjPtr,Jim_Obj * fileNameObj)13755 static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
13756 {
13757     Jim_Stack stack;
13758     ExprByteCode *expr;
13759     int ok = 1;
13760     int i;
13761     int prevtt = JIM_TT_NONE;
13762     int have_ternary = 0;
13763 
13764 
13765     int count = tokenlist->count - 1;
13766 
13767     expr = Jim_Alloc(sizeof(*expr));
13768     expr->inUse = 1;
13769     expr->len = 0;
13770 
13771     Jim_InitStack(&stack);
13772 
13773     for (i = 0; i < tokenlist->count; i++) {
13774         ParseToken *t = &tokenlist->list[i];
13775         const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13776 
13777         if (op->lazy == LAZY_OP) {
13778             count += 2;
13779 
13780             if (t->type == JIM_EXPROP_TERNARY) {
13781                 have_ternary = 1;
13782             }
13783         }
13784     }
13785 
13786     expr->token = Jim_Alloc(sizeof(ScriptToken) * count);
13787 
13788     for (i = 0; i < tokenlist->count && ok; i++) {
13789         ParseToken *t = &tokenlist->list[i];
13790 
13791 
13792         struct ScriptToken *token = &expr->token[expr->len];
13793 
13794         if (t->type == JIM_TT_EOL) {
13795             break;
13796         }
13797 
13798         if (TOKEN_IS_EXPR_OP(t->type)) {
13799             const struct Jim_ExprOperator *op;
13800             ParseToken *tt;
13801 
13802 
13803             if (prevtt == JIM_TT_NONE || prevtt == JIM_TT_SUBEXPR_START || prevtt == JIM_TT_SUBEXPR_COMMA || prevtt >= JIM_TT_EXPR_OP) {
13804                 if (t->type == JIM_EXPROP_SUB) {
13805                     t->type = JIM_EXPROP_UNARYMINUS;
13806                 }
13807                 else if (t->type == JIM_EXPROP_ADD) {
13808                     t->type = JIM_EXPROP_UNARYPLUS;
13809                 }
13810             }
13811 
13812             op = JimExprOperatorInfoByOpcode(t->type);
13813 
13814 
13815             while ((tt = Jim_StackPeek(&stack)) != NULL) {
13816                 const struct Jim_ExprOperator *tt_op =
13817                     JimExprOperatorInfoByOpcode(tt->type);
13818 
13819 
13820                 if (op->arity != 1 && tt_op->precedence >= op->precedence) {
13821 
13822                     if (tt_op->precedence == op->precedence && tt_op->lazy == RIGHT_ASSOC) {
13823                         break;
13824                     }
13825                     if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13826                         ok = 0;
13827                         goto err;
13828                     }
13829                     Jim_StackPop(&stack);
13830                 }
13831                 else {
13832                     break;
13833                 }
13834             }
13835             Jim_StackPush(&stack, t);
13836         }
13837         else if (t->type == JIM_TT_SUBEXPR_START) {
13838             Jim_StackPush(&stack, t);
13839         }
13840         else if (t->type == JIM_TT_SUBEXPR_END || t->type == JIM_TT_SUBEXPR_COMMA) {
13841 
13842             ok = 0;
13843             while (Jim_StackLen(&stack)) {
13844                 ParseToken *tt = Jim_StackPop(&stack);
13845 
13846                 if (tt->type == JIM_TT_SUBEXPR_START || tt->type == JIM_TT_SUBEXPR_COMMA) {
13847                     if (t->type == JIM_TT_SUBEXPR_COMMA) {
13848 
13849                         Jim_StackPush(&stack, tt);
13850                     }
13851                     ok = 1;
13852                     break;
13853                 }
13854                 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13855                     goto err;
13856                 }
13857             }
13858             if (!ok) {
13859                 Jim_SetResultFormatted(interp, "Unexpected close parenthesis in expression: \"%#s\"", exprObjPtr);
13860                 goto err;
13861             }
13862         }
13863         else {
13864             Jim_Obj *objPtr = NULL;
13865 
13866 
13867             token->type = t->type;
13868 
13869 
13870             if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
13871                 Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", exprObjPtr);
13872                 ok = 0;
13873                 goto err;
13874             }
13875 
13876 
13877             if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
13878                 char *endptr;
13879                 if (t->type == JIM_TT_EXPR_INT) {
13880                     objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
13881                 }
13882                 else {
13883                     objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
13884                 }
13885                 if (endptr != t->token + t->len) {
13886 
13887                     Jim_FreeNewObj(interp, objPtr);
13888                     objPtr = NULL;
13889                 }
13890             }
13891 
13892             if (objPtr) {
13893                 token->objPtr = objPtr;
13894             }
13895             else {
13896 
13897                 token->objPtr = Jim_NewStringObj(interp, t->token, t->len);
13898                 if (t->type == JIM_TT_CMD) {
13899 
13900                     JimSetSourceInfo(interp, token->objPtr, fileNameObj, t->line);
13901                 }
13902             }
13903             expr->len++;
13904         }
13905         prevtt = t->type;
13906     }
13907 
13908 
13909     while (Jim_StackLen(&stack)) {
13910         ParseToken *tt = Jim_StackPop(&stack);
13911 
13912         if (tt->type == JIM_TT_SUBEXPR_START) {
13913             ok = 0;
13914             Jim_SetResultString(interp, "Missing close parenthesis", -1);
13915             goto err;
13916         }
13917         if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13918             ok = 0;
13919             goto err;
13920         }
13921     }
13922 
13923     if (have_ternary) {
13924         ExprTernaryReorderExpression(interp, expr);
13925     }
13926 
13927   err:
13928 
13929     Jim_FreeStack(&stack);
13930 
13931     for (i = 0; i < expr->len; i++) {
13932         Jim_IncrRefCount(expr->token[i].objPtr);
13933     }
13934 
13935     if (!ok) {
13936         ExprFreeByteCode(interp, expr);
13937         return NULL;
13938     }
13939 
13940     return expr;
13941 }
13942 
13943 
SetExprFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr)13944 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
13945 {
13946     int exprTextLen;
13947     const char *exprText;
13948     struct JimParserCtx parser;
13949     struct ExprByteCode *expr;
13950     ParseTokenList tokenlist;
13951     int line;
13952     Jim_Obj *fileNameObj;
13953     int rc = JIM_ERR;
13954 
13955 
13956     if (objPtr->typePtr == &sourceObjType) {
13957         fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
13958         line = objPtr->internalRep.sourceValue.lineNumber;
13959     }
13960     else {
13961         fileNameObj = interp->emptyObj;
13962         line = 1;
13963     }
13964     Jim_IncrRefCount(fileNameObj);
13965 
13966     exprText = Jim_GetString(objPtr, &exprTextLen);
13967 
13968 
13969     ScriptTokenListInit(&tokenlist);
13970 
13971     JimParserInit(&parser, exprText, exprTextLen, line);
13972     while (!parser.eof) {
13973         if (JimParseExpression(&parser) != JIM_OK) {
13974             ScriptTokenListFree(&tokenlist);
13975             Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
13976             expr = NULL;
13977             goto err;
13978         }
13979 
13980         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
13981             parser.tline);
13982     }
13983 
13984 #ifdef DEBUG_SHOW_EXPR_TOKENS
13985     {
13986         int i;
13987         printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
13988         for (i = 0; i < tokenlist.count; i++) {
13989             printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
13990                 tokenlist.list[i].len, tokenlist.list[i].token);
13991         }
13992     }
13993 #endif
13994 
13995     if (JimParseCheckMissing(interp, parser.missing.ch) == JIM_ERR) {
13996         ScriptTokenListFree(&tokenlist);
13997         Jim_DecrRefCount(interp, fileNameObj);
13998         return JIM_ERR;
13999     }
14000 
14001 
14002     expr = ExprCreateByteCode(interp, &tokenlist, objPtr, fileNameObj);
14003 
14004 
14005     ScriptTokenListFree(&tokenlist);
14006 
14007     if (!expr) {
14008         goto err;
14009     }
14010 
14011 #ifdef DEBUG_SHOW_EXPR
14012     {
14013         int i;
14014 
14015         printf("==== Expr ====\n");
14016         for (i = 0; i < expr->len; i++) {
14017             ScriptToken *t = &expr->token[i];
14018 
14019             printf("[%2d] %s '%s'\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
14020         }
14021     }
14022 #endif
14023 
14024 
14025     if (ExprCheckCorrectness(interp, objPtr, expr) != JIM_OK) {
14026 
14027         ExprFreeByteCode(interp, expr);
14028         expr = NULL;
14029         goto err;
14030     }
14031 
14032     rc = JIM_OK;
14033 
14034   err:
14035 
14036     Jim_DecrRefCount(interp, fileNameObj);
14037     Jim_FreeIntRep(interp, objPtr);
14038     Jim_SetIntRepPtr(objPtr, expr);
14039     objPtr->typePtr = &exprObjType;
14040     return rc;
14041 }
14042 
JimGetExpression(Jim_Interp * interp,Jim_Obj * objPtr)14043 static ExprByteCode *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
14044 {
14045     if (objPtr->typePtr != &exprObjType) {
14046         if (SetExprFromAny(interp, objPtr) != JIM_OK) {
14047             return NULL;
14048         }
14049     }
14050     return (ExprByteCode *) Jim_GetIntRepPtr(objPtr);
14051 }
14052 
14053 #ifdef JIM_OPTIMIZATION
JimExprIntValOrVar(Jim_Interp * interp,const ScriptToken * token)14054 static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, const ScriptToken *token)
14055 {
14056     if (token->type == JIM_TT_EXPR_INT)
14057         return token->objPtr;
14058     else if (token->type == JIM_TT_VAR)
14059         return Jim_GetVariable(interp, token->objPtr, JIM_NONE);
14060     else if (token->type == JIM_TT_DICTSUGAR)
14061         return JimExpandDictSugar(interp, token->objPtr);
14062     else
14063         return NULL;
14064 }
14065 #endif
14066 
14067 #define JIM_EE_STATICSTACK_LEN 10
14068 
Jim_EvalExpression(Jim_Interp * interp,Jim_Obj * exprObjPtr,Jim_Obj ** exprResultPtrPtr)14069 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr)
14070 {
14071     ExprByteCode *expr;
14072     Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN];
14073     int i;
14074     int retcode = JIM_OK;
14075     struct JimExprState e;
14076 
14077     expr = JimGetExpression(interp, exprObjPtr);
14078     if (!expr) {
14079         return JIM_ERR;
14080     }
14081 
14082 #ifdef JIM_OPTIMIZATION
14083     {
14084         Jim_Obj *objPtr;
14085 
14086 
14087         switch (expr->len) {
14088             case 1:
14089                 objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
14090                 if (objPtr) {
14091                     Jim_IncrRefCount(objPtr);
14092                     *exprResultPtrPtr = objPtr;
14093                     return JIM_OK;
14094                 }
14095                 break;
14096 
14097             case 2:
14098                 if (expr->token[1].type == JIM_EXPROP_NOT) {
14099                     objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
14100 
14101                     if (objPtr && JimIsWide(objPtr)) {
14102                         *exprResultPtrPtr = JimWideValue(objPtr) ? interp->falseObj : interp->trueObj;
14103                         Jim_IncrRefCount(*exprResultPtrPtr);
14104                         return JIM_OK;
14105                     }
14106                 }
14107                 break;
14108 
14109             case 3:
14110                 objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
14111                 if (objPtr && JimIsWide(objPtr)) {
14112                     Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, &expr->token[1]);
14113                     if (objPtr2 && JimIsWide(objPtr2)) {
14114                         jim_wide wideValueA = JimWideValue(objPtr);
14115                         jim_wide wideValueB = JimWideValue(objPtr2);
14116                         int cmpRes;
14117                         switch (expr->token[2].type) {
14118                             case JIM_EXPROP_LT:
14119                                 cmpRes = wideValueA < wideValueB;
14120                                 break;
14121                             case JIM_EXPROP_LTE:
14122                                 cmpRes = wideValueA <= wideValueB;
14123                                 break;
14124                             case JIM_EXPROP_GT:
14125                                 cmpRes = wideValueA > wideValueB;
14126                                 break;
14127                             case JIM_EXPROP_GTE:
14128                                 cmpRes = wideValueA >= wideValueB;
14129                                 break;
14130                             case JIM_EXPROP_NUMEQ:
14131                                 cmpRes = wideValueA == wideValueB;
14132                                 break;
14133                             case JIM_EXPROP_NUMNE:
14134                                 cmpRes = wideValueA != wideValueB;
14135                                 break;
14136                             default:
14137                                 goto noopt;
14138                         }
14139                         *exprResultPtrPtr = cmpRes ? interp->trueObj : interp->falseObj;
14140                         Jim_IncrRefCount(*exprResultPtrPtr);
14141                         return JIM_OK;
14142                     }
14143                 }
14144                 break;
14145         }
14146     }
14147 noopt:
14148 #endif
14149 
14150     expr->inUse++;
14151 
14152 
14153 
14154     if (expr->len > JIM_EE_STATICSTACK_LEN)
14155         e.stack = Jim_Alloc(sizeof(Jim_Obj *) * expr->len);
14156     else
14157         e.stack = staticStack;
14158 
14159     e.stacklen = 0;
14160 
14161 
14162     for (i = 0; i < expr->len && retcode == JIM_OK; i++) {
14163         Jim_Obj *objPtr;
14164 
14165         switch (expr->token[i].type) {
14166             case JIM_TT_EXPR_INT:
14167             case JIM_TT_EXPR_DOUBLE:
14168             case JIM_TT_EXPR_BOOLEAN:
14169             case JIM_TT_STR:
14170                 ExprPush(&e, expr->token[i].objPtr);
14171                 break;
14172 
14173             case JIM_TT_VAR:
14174                 objPtr = Jim_GetVariable(interp, expr->token[i].objPtr, JIM_ERRMSG);
14175                 if (objPtr) {
14176                     ExprPush(&e, objPtr);
14177                 }
14178                 else {
14179                     retcode = JIM_ERR;
14180                 }
14181                 break;
14182 
14183             case JIM_TT_DICTSUGAR:
14184                 objPtr = JimExpandDictSugar(interp, expr->token[i].objPtr);
14185                 if (objPtr) {
14186                     ExprPush(&e, objPtr);
14187                 }
14188                 else {
14189                     retcode = JIM_ERR;
14190                 }
14191                 break;
14192 
14193             case JIM_TT_ESC:
14194                 retcode = Jim_SubstObj(interp, expr->token[i].objPtr, &objPtr, JIM_NONE);
14195                 if (retcode == JIM_OK) {
14196                     ExprPush(&e, objPtr);
14197                 }
14198                 break;
14199 
14200             case JIM_TT_CMD:
14201                 retcode = Jim_EvalObj(interp, expr->token[i].objPtr);
14202                 if (retcode == JIM_OK) {
14203                     ExprPush(&e, Jim_GetResult(interp));
14204                 }
14205                 break;
14206 
14207             default:{
14208 
14209                     e.skip = 0;
14210                     e.opcode = expr->token[i].type;
14211 
14212                     retcode = JimExprOperatorInfoByOpcode(e.opcode)->funcop(interp, &e);
14213 
14214                     i += e.skip;
14215                     continue;
14216                 }
14217         }
14218     }
14219 
14220     expr->inUse--;
14221 
14222     if (retcode == JIM_OK) {
14223         *exprResultPtrPtr = ExprPop(&e);
14224     }
14225     else {
14226         for (i = 0; i < e.stacklen; i++) {
14227             Jim_DecrRefCount(interp, e.stack[i]);
14228         }
14229     }
14230     if (e.stack != staticStack) {
14231         Jim_Free(e.stack);
14232     }
14233     return retcode;
14234 }
14235 
Jim_GetBoolFromExpr(Jim_Interp * interp,Jim_Obj * exprObjPtr,int * boolPtr)14236 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
14237 {
14238     int retcode;
14239     jim_wide wideValue;
14240     double doubleValue;
14241     int booleanValue;
14242     Jim_Obj *exprResultPtr;
14243 
14244     retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
14245     if (retcode != JIM_OK)
14246         return retcode;
14247 
14248     if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
14249         if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK) {
14250             if (Jim_GetBoolean(interp, exprResultPtr, &booleanValue) != JIM_OK) {
14251                 Jim_DecrRefCount(interp, exprResultPtr);
14252                 return JIM_ERR;
14253             } else {
14254                 Jim_DecrRefCount(interp, exprResultPtr);
14255                 *boolPtr = booleanValue;
14256                 return JIM_OK;
14257             }
14258         }
14259         else {
14260             Jim_DecrRefCount(interp, exprResultPtr);
14261             *boolPtr = doubleValue != 0;
14262             return JIM_OK;
14263         }
14264     }
14265     *boolPtr = wideValue != 0;
14266 
14267     Jim_DecrRefCount(interp, exprResultPtr);
14268     return JIM_OK;
14269 }
14270 
14271 
14272 
14273 
14274 typedef struct ScanFmtPartDescr
14275 {
14276     char *arg;
14277     char *prefix;
14278     size_t width;
14279     int pos;
14280     char type;
14281     char modifier;
14282 } ScanFmtPartDescr;
14283 
14284 
14285 typedef struct ScanFmtStringObj
14286 {
14287     jim_wide size;
14288     char *stringRep;
14289     size_t count;
14290     size_t convCount;
14291     size_t maxPos;
14292     const char *error;
14293     char *scratch;
14294     ScanFmtPartDescr descr[1];
14295 } ScanFmtStringObj;
14296 
14297 
14298 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
14299 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
14300 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
14301 
14302 static const Jim_ObjType scanFmtStringObjType = {
14303     "scanformatstring",
14304     FreeScanFmtInternalRep,
14305     DupScanFmtInternalRep,
14306     UpdateStringOfScanFmt,
14307     JIM_TYPE_NONE,
14308 };
14309 
FreeScanFmtInternalRep(Jim_Interp * interp,Jim_Obj * objPtr)14310 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
14311 {
14312     JIM_NOTUSED(interp);
14313     Jim_Free((char *)objPtr->internalRep.ptr);
14314     objPtr->internalRep.ptr = 0;
14315 }
14316 
DupScanFmtInternalRep(Jim_Interp * interp,Jim_Obj * srcPtr,Jim_Obj * dupPtr)14317 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
14318 {
14319     size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
14320     ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
14321 
14322     JIM_NOTUSED(interp);
14323     memcpy(newVec, srcPtr->internalRep.ptr, size);
14324     dupPtr->internalRep.ptr = newVec;
14325     dupPtr->typePtr = &scanFmtStringObjType;
14326 }
14327 
UpdateStringOfScanFmt(Jim_Obj * objPtr)14328 static void UpdateStringOfScanFmt(Jim_Obj *objPtr)
14329 {
14330     JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep);
14331 }
14332 
14333 
SetScanFmtFromAny(Jim_Interp * interp,Jim_Obj * objPtr)14334 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
14335 {
14336     ScanFmtStringObj *fmtObj;
14337     char *buffer;
14338     int maxCount, i, approxSize, lastPos = -1;
14339     const char *fmt = objPtr->bytes;
14340     int maxFmtLen = objPtr->length;
14341     const char *fmtEnd = fmt + maxFmtLen;
14342     int curr;
14343 
14344     Jim_FreeIntRep(interp, objPtr);
14345 
14346     for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
14347         if (fmt[i] == '%')
14348             ++maxCount;
14349 
14350     approxSize = sizeof(ScanFmtStringObj)
14351         +(maxCount + 1) * sizeof(ScanFmtPartDescr)
14352         +maxFmtLen * sizeof(char) + 3 + 1
14353         + maxFmtLen * sizeof(char) + 1
14354         + maxFmtLen * sizeof(char)
14355         +(maxCount + 1) * sizeof(char)
14356         +1;
14357     fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
14358     memset(fmtObj, 0, approxSize);
14359     fmtObj->size = approxSize;
14360     fmtObj->maxPos = 0;
14361     fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
14362     fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
14363     memcpy(fmtObj->stringRep, fmt, maxFmtLen);
14364     buffer = fmtObj->stringRep + maxFmtLen + 1;
14365     objPtr->internalRep.ptr = fmtObj;
14366     objPtr->typePtr = &scanFmtStringObjType;
14367     for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
14368         int width = 0, skip;
14369         ScanFmtPartDescr *descr = &fmtObj->descr[curr];
14370 
14371         fmtObj->count++;
14372         descr->width = 0;
14373 
14374         if (*fmt != '%' || fmt[1] == '%') {
14375             descr->type = 0;
14376             descr->prefix = &buffer[i];
14377             for (; fmt < fmtEnd; ++fmt) {
14378                 if (*fmt == '%') {
14379                     if (fmt[1] != '%')
14380                         break;
14381                     ++fmt;
14382                 }
14383                 buffer[i++] = *fmt;
14384             }
14385             buffer[i++] = 0;
14386         }
14387 
14388         ++fmt;
14389 
14390         if (fmt >= fmtEnd)
14391             goto done;
14392         descr->pos = 0;
14393         if (*fmt == '*') {
14394             descr->pos = -1;
14395             ++fmt;
14396         }
14397         else
14398             fmtObj->convCount++;
14399 
14400         if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14401             fmt += skip;
14402 
14403             if (descr->pos != -1 && *fmt == '$') {
14404                 int prev;
14405 
14406                 ++fmt;
14407                 descr->pos = width;
14408                 width = 0;
14409 
14410                 if ((lastPos == 0 && descr->pos > 0)
14411                     || (lastPos > 0 && descr->pos == 0)) {
14412                     fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
14413                     return JIM_ERR;
14414                 }
14415 
14416                 for (prev = 0; prev < curr; ++prev) {
14417                     if (fmtObj->descr[prev].pos == -1)
14418                         continue;
14419                     if (fmtObj->descr[prev].pos == descr->pos) {
14420                         fmtObj->error =
14421                             "variable is assigned by multiple \"%n$\" conversion specifiers";
14422                         return JIM_ERR;
14423                     }
14424                 }
14425 
14426                 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14427                     descr->width = width;
14428                     fmt += skip;
14429                 }
14430                 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
14431                     fmtObj->maxPos = descr->pos;
14432             }
14433             else {
14434 
14435                 descr->width = width;
14436             }
14437         }
14438 
14439         if (lastPos == -1)
14440             lastPos = descr->pos;
14441 
14442         if (*fmt == '[') {
14443             int swapped = 1, beg = i, end, j;
14444 
14445             descr->type = '[';
14446             descr->arg = &buffer[i];
14447             ++fmt;
14448             if (*fmt == '^')
14449                 buffer[i++] = *fmt++;
14450             if (*fmt == ']')
14451                 buffer[i++] = *fmt++;
14452             while (*fmt && *fmt != ']')
14453                 buffer[i++] = *fmt++;
14454             if (*fmt != ']') {
14455                 fmtObj->error = "unmatched [ in format string";
14456                 return JIM_ERR;
14457             }
14458             end = i;
14459             buffer[i++] = 0;
14460 
14461             while (swapped) {
14462                 swapped = 0;
14463                 for (j = beg + 1; j < end - 1; ++j) {
14464                     if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
14465                         char tmp = buffer[j - 1];
14466 
14467                         buffer[j - 1] = buffer[j + 1];
14468                         buffer[j + 1] = tmp;
14469                         swapped = 1;
14470                     }
14471                 }
14472             }
14473         }
14474         else {
14475 
14476             if (strchr("hlL", *fmt) != 0)
14477                 descr->modifier = tolower((int)*fmt++);
14478 
14479             descr->type = *fmt;
14480             if (strchr("efgcsndoxui", *fmt) == 0) {
14481                 fmtObj->error = "bad scan conversion character";
14482                 return JIM_ERR;
14483             }
14484             else if (*fmt == 'c' && descr->width != 0) {
14485                 fmtObj->error = "field width may not be specified in %c " "conversion";
14486                 return JIM_ERR;
14487             }
14488             else if (*fmt == 'u' && descr->modifier == 'l') {
14489                 fmtObj->error = "unsigned wide not supported";
14490                 return JIM_ERR;
14491             }
14492         }
14493         curr++;
14494     }
14495   done:
14496     return JIM_OK;
14497 }
14498 
14499 
14500 
14501 #define FormatGetCnvCount(_fo_) \
14502     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
14503 #define FormatGetMaxPos(_fo_) \
14504     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
14505 #define FormatGetError(_fo_) \
14506     ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
14507 
JimScanAString(Jim_Interp * interp,const char * sdescr,const char * str)14508 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
14509 {
14510     char *buffer = Jim_StrDup(str);
14511     char *p = buffer;
14512 
14513     while (*str) {
14514         int c;
14515         int n;
14516 
14517         if (!sdescr && isspace(UCHAR(*str)))
14518             break;
14519 
14520         n = utf8_tounicode(str, &c);
14521         if (sdescr && !JimCharsetMatch(sdescr, c, JIM_CHARSET_SCAN))
14522             break;
14523         while (n--)
14524             *p++ = *str++;
14525     }
14526     *p = 0;
14527     return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
14528 }
14529 
14530 
ScanOneEntry(Jim_Interp * interp,const char * str,int pos,int strLen,ScanFmtStringObj * fmtObj,long idx,Jim_Obj ** valObjPtr)14531 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int strLen,
14532     ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
14533 {
14534     const char *tok;
14535     const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
14536     size_t scanned = 0;
14537     size_t anchor = pos;
14538     int i;
14539     Jim_Obj *tmpObj = NULL;
14540 
14541 
14542     *valObjPtr = 0;
14543     if (descr->prefix) {
14544         for (i = 0; pos < strLen && descr->prefix[i]; ++i) {
14545 
14546             if (isspace(UCHAR(descr->prefix[i])))
14547                 while (pos < strLen && isspace(UCHAR(str[pos])))
14548                     ++pos;
14549             else if (descr->prefix[i] != str[pos])
14550                 break;
14551             else
14552                 ++pos;
14553         }
14554         if (pos >= strLen) {
14555             return -1;
14556         }
14557         else if (descr->prefix[i] != 0)
14558             return 0;
14559     }
14560 
14561     if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
14562         while (isspace(UCHAR(str[pos])))
14563             ++pos;
14564 
14565     scanned = pos - anchor;
14566 
14567 
14568     if (descr->type == 'n') {
14569 
14570         *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
14571     }
14572     else if (pos >= strLen) {
14573 
14574         return -1;
14575     }
14576     else if (descr->type == 'c') {
14577             int c;
14578             scanned += utf8_tounicode(&str[pos], &c);
14579             *valObjPtr = Jim_NewIntObj(interp, c);
14580             return scanned;
14581     }
14582     else {
14583 
14584         if (descr->width > 0) {
14585             size_t sLen = utf8_strlen(&str[pos], strLen - pos);
14586             size_t tLen = descr->width > sLen ? sLen : descr->width;
14587 
14588             tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
14589             tok = tmpObj->bytes;
14590         }
14591         else {
14592 
14593             tok = &str[pos];
14594         }
14595         switch (descr->type) {
14596             case 'd':
14597             case 'o':
14598             case 'x':
14599             case 'u':
14600             case 'i':{
14601                     char *endp;
14602                     jim_wide w;
14603 
14604                     int base = descr->type == 'o' ? 8
14605                         : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
14606 
14607 
14608                     if (base == 0) {
14609                         w = jim_strtoull(tok, &endp);
14610                     }
14611                     else {
14612                         w = strtoull(tok, &endp, base);
14613                     }
14614 
14615                     if (endp != tok) {
14616 
14617                         *valObjPtr = Jim_NewIntObj(interp, w);
14618 
14619 
14620                         scanned += endp - tok;
14621                     }
14622                     else {
14623                         scanned = *tok ? 0 : -1;
14624                     }
14625                     break;
14626                 }
14627             case 's':
14628             case '[':{
14629                     *valObjPtr = JimScanAString(interp, descr->arg, tok);
14630                     scanned += Jim_Length(*valObjPtr);
14631                     break;
14632                 }
14633             case 'e':
14634             case 'f':
14635             case 'g':{
14636                     char *endp;
14637                     double value = strtod(tok, &endp);
14638 
14639                     if (endp != tok) {
14640 
14641                         *valObjPtr = Jim_NewDoubleObj(interp, value);
14642 
14643                         scanned += endp - tok;
14644                     }
14645                     else {
14646                         scanned = *tok ? 0 : -1;
14647                     }
14648                     break;
14649                 }
14650         }
14651         if (tmpObj) {
14652             Jim_FreeNewObj(interp, tmpObj);
14653         }
14654     }
14655     return scanned;
14656 }
14657 
14658 
Jim_ScanString(Jim_Interp * interp,Jim_Obj * strObjPtr,Jim_Obj * fmtObjPtr,int flags)14659 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
14660 {
14661     size_t i, pos;
14662     int scanned = 1;
14663     const char *str = Jim_String(strObjPtr);
14664     int strLen = Jim_Utf8Length(interp, strObjPtr);
14665     Jim_Obj *resultList = 0;
14666     Jim_Obj **resultVec = 0;
14667     int resultc;
14668     Jim_Obj *emptyStr = 0;
14669     ScanFmtStringObj *fmtObj;
14670 
14671 
14672     JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
14673 
14674     fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
14675 
14676     if (fmtObj->error != 0) {
14677         if (flags & JIM_ERRMSG)
14678             Jim_SetResultString(interp, fmtObj->error, -1);
14679         return 0;
14680     }
14681 
14682     emptyStr = Jim_NewEmptyStringObj(interp);
14683     Jim_IncrRefCount(emptyStr);
14684 
14685     resultList = Jim_NewListObj(interp, NULL, 0);
14686     if (fmtObj->maxPos > 0) {
14687         for (i = 0; i < fmtObj->maxPos; ++i)
14688             Jim_ListAppendElement(interp, resultList, emptyStr);
14689         JimListGetElements(interp, resultList, &resultc, &resultVec);
14690     }
14691 
14692     for (i = 0, pos = 0; i < fmtObj->count; ++i) {
14693         ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
14694         Jim_Obj *value = 0;
14695 
14696 
14697         if (descr->type == 0)
14698             continue;
14699 
14700         if (scanned > 0)
14701             scanned = ScanOneEntry(interp, str, pos, strLen, fmtObj, i, &value);
14702 
14703         if (scanned == -1 && i == 0)
14704             goto eof;
14705 
14706         pos += scanned;
14707 
14708 
14709         if (value == 0)
14710             value = Jim_NewEmptyStringObj(interp);
14711 
14712         if (descr->pos == -1) {
14713             Jim_FreeNewObj(interp, value);
14714         }
14715         else if (descr->pos == 0)
14716 
14717             Jim_ListAppendElement(interp, resultList, value);
14718         else if (resultVec[descr->pos - 1] == emptyStr) {
14719 
14720             Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
14721             Jim_IncrRefCount(value);
14722             resultVec[descr->pos - 1] = value;
14723         }
14724         else {
14725 
14726             Jim_FreeNewObj(interp, value);
14727             goto err;
14728         }
14729     }
14730     Jim_DecrRefCount(interp, emptyStr);
14731     return resultList;
14732   eof:
14733     Jim_DecrRefCount(interp, emptyStr);
14734     Jim_FreeNewObj(interp, resultList);
14735     return (Jim_Obj *)EOF;
14736   err:
14737     Jim_DecrRefCount(interp, emptyStr);
14738     Jim_FreeNewObj(interp, resultList);
14739     return 0;
14740 }
14741 
14742 
JimPrngInit(Jim_Interp * interp)14743 static void JimPrngInit(Jim_Interp *interp)
14744 {
14745 #define PRNG_SEED_SIZE 256
14746     int i;
14747     unsigned int *seed;
14748     time_t t = time(NULL);
14749 
14750     interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
14751 
14752     seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
14753     for (i = 0; i < PRNG_SEED_SIZE; i++) {
14754         seed[i] = (rand() ^ t ^ clock());
14755     }
14756     JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
14757     Jim_Free(seed);
14758 }
14759 
14760 
JimRandomBytes(Jim_Interp * interp,void * dest,unsigned int len)14761 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
14762 {
14763     Jim_PrngState *prng;
14764     unsigned char *destByte = (unsigned char *)dest;
14765     unsigned int si, sj, x;
14766 
14767 
14768     if (interp->prngState == NULL)
14769         JimPrngInit(interp);
14770     prng = interp->prngState;
14771 
14772     for (x = 0; x < len; x++) {
14773         prng->i = (prng->i + 1) & 0xff;
14774         si = prng->sbox[prng->i];
14775         prng->j = (prng->j + si) & 0xff;
14776         sj = prng->sbox[prng->j];
14777         prng->sbox[prng->i] = sj;
14778         prng->sbox[prng->j] = si;
14779         *destByte++ = prng->sbox[(si + sj) & 0xff];
14780     }
14781 }
14782 
14783 
JimPrngSeed(Jim_Interp * interp,unsigned char * seed,int seedLen)14784 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
14785 {
14786     int i;
14787     Jim_PrngState *prng;
14788 
14789 
14790     if (interp->prngState == NULL)
14791         JimPrngInit(interp);
14792     prng = interp->prngState;
14793 
14794 
14795     for (i = 0; i < 256; i++)
14796         prng->sbox[i] = i;
14797 
14798     for (i = 0; i < seedLen; i++) {
14799         unsigned char t;
14800 
14801         t = prng->sbox[i & 0xFF];
14802         prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
14803         prng->sbox[seed[i]] = t;
14804     }
14805     prng->i = prng->j = 0;
14806 
14807     for (i = 0; i < 256; i += seedLen) {
14808         JimRandomBytes(interp, seed, seedLen);
14809     }
14810 }
14811 
14812 
Jim_IncrCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)14813 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14814 {
14815     jim_wide wideValue, increment = 1;
14816     Jim_Obj *intObjPtr;
14817 
14818     if (argc != 2 && argc != 3) {
14819         Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
14820         return JIM_ERR;
14821     }
14822     if (argc == 3) {
14823         if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
14824             return JIM_ERR;
14825     }
14826     intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
14827     if (!intObjPtr) {
14828 
14829         wideValue = 0;
14830     }
14831     else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
14832         return JIM_ERR;
14833     }
14834     if (!intObjPtr || Jim_IsShared(intObjPtr)) {
14835         intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
14836         if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
14837             Jim_FreeNewObj(interp, intObjPtr);
14838             return JIM_ERR;
14839         }
14840     }
14841     else {
14842 
14843         Jim_InvalidateStringRep(intObjPtr);
14844         JimWideValue(intObjPtr) = wideValue + increment;
14845 
14846         if (argv[1]->typePtr != &variableObjType) {
14847 
14848             Jim_SetVariable(interp, argv[1], intObjPtr);
14849         }
14850     }
14851     Jim_SetResult(interp, intObjPtr);
14852     return JIM_OK;
14853 }
14854 
14855 
14856 #define JIM_EVAL_SARGV_LEN 8
14857 #define JIM_EVAL_SINTV_LEN 8
14858 
14859 
JimUnknown(Jim_Interp * interp,int argc,Jim_Obj * const * argv)14860 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14861 {
14862     int retcode;
14863 
14864     if (interp->unknown_called > 50) {
14865         return JIM_ERR;
14866     }
14867 
14868 
14869 
14870     if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
14871         return JIM_ERR;
14872 
14873     interp->unknown_called++;
14874 
14875     retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
14876     interp->unknown_called--;
14877 
14878     return retcode;
14879 }
14880 
JimInvokeCommand(Jim_Interp * interp,int objc,Jim_Obj * const * objv)14881 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14882 {
14883     int retcode;
14884     Jim_Cmd *cmdPtr;
14885 
14886 #if 0
14887     printf("invoke");
14888     int j;
14889     for (j = 0; j < objc; j++) {
14890         printf(" '%s'", Jim_String(objv[j]));
14891     }
14892     printf("\n");
14893 #endif
14894 
14895     if (interp->framePtr->tailcallCmd) {
14896 
14897         cmdPtr = interp->framePtr->tailcallCmd;
14898         interp->framePtr->tailcallCmd = NULL;
14899     }
14900     else {
14901         cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
14902         if (cmdPtr == NULL) {
14903             return JimUnknown(interp, objc, objv);
14904         }
14905         JimIncrCmdRefCount(cmdPtr);
14906     }
14907 
14908     if (interp->evalDepth == interp->maxEvalDepth) {
14909         Jim_SetResultString(interp, "Infinite eval recursion", -1);
14910         retcode = JIM_ERR;
14911         goto out;
14912     }
14913     interp->evalDepth++;
14914 
14915 
14916     Jim_SetEmptyResult(interp);
14917     if (cmdPtr->isproc) {
14918         retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
14919     }
14920     else {
14921         interp->cmdPrivData = cmdPtr->u.native.privData;
14922         retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
14923     }
14924     interp->evalDepth--;
14925 
14926 out:
14927     JimDecrCmdRefCount(interp, cmdPtr);
14928 
14929     return retcode;
14930 }
14931 
Jim_EvalObjVector(Jim_Interp * interp,int objc,Jim_Obj * const * objv)14932 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14933 {
14934     int i, retcode;
14935 
14936 
14937     for (i = 0; i < objc; i++)
14938         Jim_IncrRefCount(objv[i]);
14939 
14940     retcode = JimInvokeCommand(interp, objc, objv);
14941 
14942 
14943     for (i = 0; i < objc; i++)
14944         Jim_DecrRefCount(interp, objv[i]);
14945 
14946     return retcode;
14947 }
14948 
Jim_EvalObjPrefix(Jim_Interp * interp,Jim_Obj * prefix,int objc,Jim_Obj * const * objv)14949 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
14950 {
14951     int ret;
14952     Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
14953 
14954     nargv[0] = prefix;
14955     memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
14956     ret = Jim_EvalObjVector(interp, objc + 1, nargv);
14957     Jim_Free(nargv);
14958     return ret;
14959 }
14960 
JimAddErrorToStack(Jim_Interp * interp,ScriptObj * script)14961 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script)
14962 {
14963     if (!interp->errorFlag) {
14964 
14965         interp->errorFlag = 1;
14966         Jim_IncrRefCount(script->fileNameObj);
14967         Jim_DecrRefCount(interp, interp->errorFileNameObj);
14968         interp->errorFileNameObj = script->fileNameObj;
14969         interp->errorLine = script->linenr;
14970 
14971         JimResetStackTrace(interp);
14972 
14973         interp->addStackTrace++;
14974     }
14975 
14976 
14977     if (interp->addStackTrace > 0) {
14978 
14979 
14980         JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr);
14981 
14982         if (Jim_Length(script->fileNameObj)) {
14983             interp->addStackTrace = 0;
14984         }
14985 
14986         Jim_DecrRefCount(interp, interp->errorProc);
14987         interp->errorProc = interp->emptyObj;
14988         Jim_IncrRefCount(interp->errorProc);
14989     }
14990 }
14991 
JimSubstOneToken(Jim_Interp * interp,const ScriptToken * token,Jim_Obj ** objPtrPtr)14992 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
14993 {
14994     Jim_Obj *objPtr;
14995 
14996     switch (token->type) {
14997         case JIM_TT_STR:
14998         case JIM_TT_ESC:
14999             objPtr = token->objPtr;
15000             break;
15001         case JIM_TT_VAR:
15002             objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
15003             break;
15004         case JIM_TT_DICTSUGAR:
15005             objPtr = JimExpandDictSugar(interp, token->objPtr);
15006             break;
15007         case JIM_TT_EXPRSUGAR:
15008             objPtr = JimExpandExprSugar(interp, token->objPtr);
15009             break;
15010         case JIM_TT_CMD:
15011             switch (Jim_EvalObj(interp, token->objPtr)) {
15012                 case JIM_OK:
15013                 case JIM_RETURN:
15014                     objPtr = interp->result;
15015                     break;
15016                 case JIM_BREAK:
15017 
15018                     return JIM_BREAK;
15019                 case JIM_CONTINUE:
15020 
15021                     return JIM_CONTINUE;
15022                 default:
15023                     return JIM_ERR;
15024             }
15025             break;
15026         default:
15027             JimPanic((1,
15028                 "default token type (%d) reached " "in Jim_SubstObj().", token->type));
15029             objPtr = NULL;
15030             break;
15031     }
15032     if (objPtr) {
15033         *objPtrPtr = objPtr;
15034         return JIM_OK;
15035     }
15036     return JIM_ERR;
15037 }
15038 
JimInterpolateTokens(Jim_Interp * interp,const ScriptToken * token,int tokens,int flags)15039 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
15040 {
15041     int totlen = 0, i;
15042     Jim_Obj **intv;
15043     Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
15044     Jim_Obj *objPtr;
15045     char *s;
15046 
15047     if (tokens <= JIM_EVAL_SINTV_LEN)
15048         intv = sintv;
15049     else
15050         intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
15051 
15052     for (i = 0; i < tokens; i++) {
15053         switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
15054             case JIM_OK:
15055             case JIM_RETURN:
15056                 break;
15057             case JIM_BREAK:
15058                 if (flags & JIM_SUBST_FLAG) {
15059 
15060                     tokens = i;
15061                     continue;
15062                 }
15063 
15064 
15065             case JIM_CONTINUE:
15066                 if (flags & JIM_SUBST_FLAG) {
15067                     intv[i] = NULL;
15068                     continue;
15069                 }
15070 
15071 
15072             default:
15073                 while (i--) {
15074                     Jim_DecrRefCount(interp, intv[i]);
15075                 }
15076                 if (intv != sintv) {
15077                     Jim_Free(intv);
15078                 }
15079                 return NULL;
15080         }
15081         Jim_IncrRefCount(intv[i]);
15082         Jim_String(intv[i]);
15083         totlen += intv[i]->length;
15084     }
15085 
15086 
15087     if (tokens == 1 && intv[0] && intv == sintv) {
15088         Jim_DecrRefCount(interp, intv[0]);
15089         return intv[0];
15090     }
15091 
15092     objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
15093 
15094     if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
15095         && token[2].type == JIM_TT_VAR) {
15096 
15097         objPtr->typePtr = &interpolatedObjType;
15098         objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
15099         objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
15100         Jim_IncrRefCount(intv[2]);
15101     }
15102     else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
15103 
15104         JimSetSourceInfo(interp, objPtr, intv[0]->internalRep.sourceValue.fileNameObj, intv[0]->internalRep.sourceValue.lineNumber);
15105     }
15106 
15107 
15108     s = objPtr->bytes = Jim_Alloc(totlen + 1);
15109     objPtr->length = totlen;
15110     for (i = 0; i < tokens; i++) {
15111         if (intv[i]) {
15112             memcpy(s, intv[i]->bytes, intv[i]->length);
15113             s += intv[i]->length;
15114             Jim_DecrRefCount(interp, intv[i]);
15115         }
15116     }
15117     objPtr->bytes[totlen] = '\0';
15118 
15119     if (intv != sintv) {
15120         Jim_Free(intv);
15121     }
15122 
15123     return objPtr;
15124 }
15125 
15126 
JimEvalObjList(Jim_Interp * interp,Jim_Obj * listPtr)15127 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15128 {
15129     int retcode = JIM_OK;
15130 
15131     JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
15132 
15133     if (listPtr->internalRep.listValue.len) {
15134         Jim_IncrRefCount(listPtr);
15135         retcode = JimInvokeCommand(interp,
15136             listPtr->internalRep.listValue.len,
15137             listPtr->internalRep.listValue.ele);
15138         Jim_DecrRefCount(interp, listPtr);
15139     }
15140     return retcode;
15141 }
15142 
Jim_EvalObjList(Jim_Interp * interp,Jim_Obj * listPtr)15143 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15144 {
15145     SetListFromAny(interp, listPtr);
15146     return JimEvalObjList(interp, listPtr);
15147 }
15148 
Jim_EvalObj(Jim_Interp * interp,Jim_Obj * scriptObjPtr)15149 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
15150 {
15151     int i;
15152     ScriptObj *script;
15153     ScriptToken *token;
15154     int retcode = JIM_OK;
15155     Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
15156     Jim_Obj *prevScriptObj;
15157 
15158     if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
15159         return JimEvalObjList(interp, scriptObjPtr);
15160     }
15161 
15162     Jim_IncrRefCount(scriptObjPtr);
15163     script = JimGetScript(interp, scriptObjPtr);
15164     if (!JimScriptValid(interp, script)) {
15165         Jim_DecrRefCount(interp, scriptObjPtr);
15166         return JIM_ERR;
15167     }
15168 
15169     Jim_SetEmptyResult(interp);
15170 
15171     token = script->token;
15172 
15173 #ifdef JIM_OPTIMIZATION
15174     if (script->len == 0) {
15175         Jim_DecrRefCount(interp, scriptObjPtr);
15176         return JIM_OK;
15177     }
15178     if (script->len == 3
15179         && token[1].objPtr->typePtr == &commandObjType
15180         && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
15181         && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
15182         && token[2].objPtr->typePtr == &variableObjType) {
15183 
15184         Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
15185 
15186         if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
15187             JimWideValue(objPtr)++;
15188             Jim_InvalidateStringRep(objPtr);
15189             Jim_DecrRefCount(interp, scriptObjPtr);
15190             Jim_SetResult(interp, objPtr);
15191             return JIM_OK;
15192         }
15193     }
15194 #endif
15195 
15196     script->inUse++;
15197 
15198 
15199     prevScriptObj = interp->currentScriptObj;
15200     interp->currentScriptObj = scriptObjPtr;
15201 
15202     interp->errorFlag = 0;
15203     argv = sargv;
15204 
15205     for (i = 0; i < script->len && retcode == JIM_OK; ) {
15206         int argc;
15207         int j;
15208 
15209 
15210         argc = token[i].objPtr->internalRep.scriptLineValue.argc;
15211         script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
15212 
15213 
15214         if (argc > JIM_EVAL_SARGV_LEN)
15215             argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
15216 
15217 
15218         i++;
15219 
15220         for (j = 0; j < argc; j++) {
15221             long wordtokens = 1;
15222             int expand = 0;
15223             Jim_Obj *wordObjPtr = NULL;
15224 
15225             if (token[i].type == JIM_TT_WORD) {
15226                 wordtokens = JimWideValue(token[i++].objPtr);
15227                 if (wordtokens < 0) {
15228                     expand = 1;
15229                     wordtokens = -wordtokens;
15230                 }
15231             }
15232 
15233             if (wordtokens == 1) {
15234 
15235                 switch (token[i].type) {
15236                     case JIM_TT_ESC:
15237                     case JIM_TT_STR:
15238                         wordObjPtr = token[i].objPtr;
15239                         break;
15240                     case JIM_TT_VAR:
15241                         wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
15242                         break;
15243                     case JIM_TT_EXPRSUGAR:
15244                         wordObjPtr = JimExpandExprSugar(interp, token[i].objPtr);
15245                         break;
15246                     case JIM_TT_DICTSUGAR:
15247                         wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
15248                         break;
15249                     case JIM_TT_CMD:
15250                         retcode = Jim_EvalObj(interp, token[i].objPtr);
15251                         if (retcode == JIM_OK) {
15252                             wordObjPtr = Jim_GetResult(interp);
15253                         }
15254                         break;
15255                     default:
15256                         JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
15257                 }
15258             }
15259             else {
15260                 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
15261             }
15262 
15263             if (!wordObjPtr) {
15264                 if (retcode == JIM_OK) {
15265                     retcode = JIM_ERR;
15266                 }
15267                 break;
15268             }
15269 
15270             Jim_IncrRefCount(wordObjPtr);
15271             i += wordtokens;
15272 
15273             if (!expand) {
15274                 argv[j] = wordObjPtr;
15275             }
15276             else {
15277 
15278                 int len = Jim_ListLength(interp, wordObjPtr);
15279                 int newargc = argc + len - 1;
15280                 int k;
15281 
15282                 if (len > 1) {
15283                     if (argv == sargv) {
15284                         if (newargc > JIM_EVAL_SARGV_LEN) {
15285                             argv = Jim_Alloc(sizeof(*argv) * newargc);
15286                             memcpy(argv, sargv, sizeof(*argv) * j);
15287                         }
15288                     }
15289                     else {
15290 
15291                         argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
15292                     }
15293                 }
15294 
15295 
15296                 for (k = 0; k < len; k++) {
15297                     argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
15298                     Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
15299                 }
15300 
15301                 Jim_DecrRefCount(interp, wordObjPtr);
15302 
15303 
15304                 j--;
15305                 argc += len - 1;
15306             }
15307         }
15308 
15309         if (retcode == JIM_OK && argc) {
15310 
15311             retcode = JimInvokeCommand(interp, argc, argv);
15312 
15313             if (Jim_CheckSignal(interp)) {
15314                 retcode = JIM_SIGNAL;
15315             }
15316         }
15317 
15318 
15319         while (j-- > 0) {
15320             Jim_DecrRefCount(interp, argv[j]);
15321         }
15322 
15323         if (argv != sargv) {
15324             Jim_Free(argv);
15325             argv = sargv;
15326         }
15327     }
15328 
15329 
15330     if (retcode == JIM_ERR) {
15331         JimAddErrorToStack(interp, script);
15332     }
15333 
15334     else if (retcode != JIM_RETURN || interp->returnCode != JIM_ERR) {
15335 
15336         interp->addStackTrace = 0;
15337     }
15338 
15339 
15340     interp->currentScriptObj = prevScriptObj;
15341 
15342     Jim_FreeIntRep(interp, scriptObjPtr);
15343     scriptObjPtr->typePtr = &scriptObjType;
15344     Jim_SetIntRepPtr(scriptObjPtr, script);
15345     Jim_DecrRefCount(interp, scriptObjPtr);
15346 
15347     return retcode;
15348 }
15349 
JimSetProcArg(Jim_Interp * interp,Jim_Obj * argNameObj,Jim_Obj * argValObj)15350 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
15351 {
15352     int retcode;
15353 
15354     const char *varname = Jim_String(argNameObj);
15355     if (*varname == '&') {
15356 
15357         Jim_Obj *objPtr;
15358         Jim_CallFrame *savedCallFrame = interp->framePtr;
15359 
15360         interp->framePtr = interp->framePtr->parent;
15361         objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
15362         interp->framePtr = savedCallFrame;
15363         if (!objPtr) {
15364             return JIM_ERR;
15365         }
15366 
15367 
15368         objPtr = Jim_NewStringObj(interp, varname + 1, -1);
15369         Jim_IncrRefCount(objPtr);
15370         retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
15371         Jim_DecrRefCount(interp, objPtr);
15372     }
15373     else {
15374         retcode = Jim_SetVariable(interp, argNameObj, argValObj);
15375     }
15376     return retcode;
15377 }
15378 
JimSetProcWrongArgs(Jim_Interp * interp,Jim_Obj * procNameObj,Jim_Cmd * cmd)15379 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
15380 {
15381 
15382     Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
15383     int i;
15384 
15385     for (i = 0; i < cmd->u.proc.argListLen; i++) {
15386         Jim_AppendString(interp, argmsg, " ", 1);
15387 
15388         if (i == cmd->u.proc.argsPos) {
15389             if (cmd->u.proc.arglist[i].defaultObjPtr) {
15390 
15391                 Jim_AppendString(interp, argmsg, "?", 1);
15392                 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
15393                 Jim_AppendString(interp, argmsg, " ...?", -1);
15394             }
15395             else {
15396 
15397                 Jim_AppendString(interp, argmsg, "?arg...?", -1);
15398             }
15399         }
15400         else {
15401             if (cmd->u.proc.arglist[i].defaultObjPtr) {
15402                 Jim_AppendString(interp, argmsg, "?", 1);
15403                 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
15404                 Jim_AppendString(interp, argmsg, "?", 1);
15405             }
15406             else {
15407                 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
15408                 if (*arg == '&') {
15409                     arg++;
15410                 }
15411                 Jim_AppendString(interp, argmsg, arg, -1);
15412             }
15413         }
15414     }
15415     Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
15416 }
15417 
15418 #ifdef jim_ext_namespace
Jim_EvalNamespace(Jim_Interp * interp,Jim_Obj * scriptObj,Jim_Obj * nsObj)15419 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
15420 {
15421     Jim_CallFrame *callFramePtr;
15422     int retcode;
15423 
15424 
15425     callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
15426     callFramePtr->argv = &interp->emptyObj;
15427     callFramePtr->argc = 0;
15428     callFramePtr->procArgsObjPtr = NULL;
15429     callFramePtr->procBodyObjPtr = scriptObj;
15430     callFramePtr->staticVars = NULL;
15431     callFramePtr->fileNameObj = interp->emptyObj;
15432     callFramePtr->line = 0;
15433     Jim_IncrRefCount(scriptObj);
15434     interp->framePtr = callFramePtr;
15435 
15436 
15437     if (interp->framePtr->level == interp->maxCallFrameDepth) {
15438         Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15439         retcode = JIM_ERR;
15440     }
15441     else {
15442 
15443         retcode = Jim_EvalObj(interp, scriptObj);
15444     }
15445 
15446 
15447     interp->framePtr = interp->framePtr->parent;
15448     JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15449 
15450     return retcode;
15451 }
15452 #endif
15453 
JimCallProcedure(Jim_Interp * interp,Jim_Cmd * cmd,int argc,Jim_Obj * const * argv)15454 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
15455 {
15456     Jim_CallFrame *callFramePtr;
15457     int i, d, retcode, optargs;
15458     ScriptObj *script;
15459 
15460 
15461     if (argc - 1 < cmd->u.proc.reqArity ||
15462         (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
15463         JimSetProcWrongArgs(interp, argv[0], cmd);
15464         return JIM_ERR;
15465     }
15466 
15467     if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
15468 
15469         return JIM_OK;
15470     }
15471 
15472 
15473     if (interp->framePtr->level == interp->maxCallFrameDepth) {
15474         Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15475         return JIM_ERR;
15476     }
15477 
15478 
15479     callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
15480     callFramePtr->argv = argv;
15481     callFramePtr->argc = argc;
15482     callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
15483     callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
15484     callFramePtr->staticVars = cmd->u.proc.staticVars;
15485 
15486 
15487     script = JimGetScript(interp, interp->currentScriptObj);
15488     callFramePtr->fileNameObj = script->fileNameObj;
15489     callFramePtr->line = script->linenr;
15490 
15491     Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
15492     Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
15493     interp->framePtr = callFramePtr;
15494 
15495 
15496     optargs = (argc - 1 - cmd->u.proc.reqArity);
15497 
15498 
15499     i = 1;
15500     for (d = 0; d < cmd->u.proc.argListLen; d++) {
15501         Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
15502         if (d == cmd->u.proc.argsPos) {
15503 
15504             Jim_Obj *listObjPtr;
15505             int argsLen = 0;
15506             if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
15507                 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
15508             }
15509             listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
15510 
15511 
15512             if (cmd->u.proc.arglist[d].defaultObjPtr) {
15513                 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
15514             }
15515             retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
15516             if (retcode != JIM_OK) {
15517                 goto badargset;
15518             }
15519 
15520             i += argsLen;
15521             continue;
15522         }
15523 
15524 
15525         if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
15526             retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
15527         }
15528         else {
15529 
15530             retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
15531         }
15532         if (retcode != JIM_OK) {
15533             goto badargset;
15534         }
15535     }
15536 
15537 
15538     retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
15539 
15540 badargset:
15541 
15542 
15543     interp->framePtr = interp->framePtr->parent;
15544     JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15545 
15546 
15547     if (interp->framePtr->tailcallObj) {
15548         do {
15549             Jim_Obj *tailcallObj = interp->framePtr->tailcallObj;
15550 
15551             interp->framePtr->tailcallObj = NULL;
15552 
15553             if (retcode == JIM_EVAL) {
15554                 retcode = Jim_EvalObjList(interp, tailcallObj);
15555                 if (retcode == JIM_RETURN) {
15556                     interp->returnLevel++;
15557                 }
15558             }
15559             Jim_DecrRefCount(interp, tailcallObj);
15560         } while (interp->framePtr->tailcallObj);
15561 
15562 
15563         if (interp->framePtr->tailcallCmd) {
15564             JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
15565             interp->framePtr->tailcallCmd = NULL;
15566         }
15567     }
15568 
15569 
15570     if (retcode == JIM_RETURN) {
15571         if (--interp->returnLevel <= 0) {
15572             retcode = interp->returnCode;
15573             interp->returnCode = JIM_OK;
15574             interp->returnLevel = 0;
15575         }
15576     }
15577     else if (retcode == JIM_ERR) {
15578         interp->addStackTrace++;
15579         Jim_DecrRefCount(interp, interp->errorProc);
15580         interp->errorProc = argv[0];
15581         Jim_IncrRefCount(interp->errorProc);
15582     }
15583 
15584     return retcode;
15585 }
15586 
Jim_EvalSource(Jim_Interp * interp,const char * filename,int lineno,const char * script)15587 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
15588 {
15589     int retval;
15590     Jim_Obj *scriptObjPtr;
15591 
15592     scriptObjPtr = Jim_NewStringObj(interp, script, -1);
15593     Jim_IncrRefCount(scriptObjPtr);
15594 
15595     if (filename) {
15596         Jim_Obj *prevScriptObj;
15597 
15598         JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
15599 
15600         prevScriptObj = interp->currentScriptObj;
15601         interp->currentScriptObj = scriptObjPtr;
15602 
15603         retval = Jim_EvalObj(interp, scriptObjPtr);
15604 
15605         interp->currentScriptObj = prevScriptObj;
15606     }
15607     else {
15608         retval = Jim_EvalObj(interp, scriptObjPtr);
15609     }
15610     Jim_DecrRefCount(interp, scriptObjPtr);
15611     return retval;
15612 }
15613 
Jim_Eval(Jim_Interp * interp,const char * script)15614 int Jim_Eval(Jim_Interp *interp, const char *script)
15615 {
15616     return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
15617 }
15618 
15619 
Jim_EvalGlobal(Jim_Interp * interp,const char * script)15620 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
15621 {
15622     int retval;
15623     Jim_CallFrame *savedFramePtr = interp->framePtr;
15624 
15625     interp->framePtr = interp->topFramePtr;
15626     retval = Jim_Eval(interp, script);
15627     interp->framePtr = savedFramePtr;
15628 
15629     return retval;
15630 }
15631 
Jim_EvalFileGlobal(Jim_Interp * interp,const char * filename)15632 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
15633 {
15634     int retval;
15635     Jim_CallFrame *savedFramePtr = interp->framePtr;
15636 
15637     interp->framePtr = interp->topFramePtr;
15638     retval = Jim_EvalFile(interp, filename);
15639     interp->framePtr = savedFramePtr;
15640 
15641     return retval;
15642 }
15643 
15644 #include <sys/stat.h>
15645 
Jim_EvalFile(Jim_Interp * interp,const char * filename)15646 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
15647 {
15648     FILE *fp;
15649     char *buf;
15650     Jim_Obj *scriptObjPtr;
15651     Jim_Obj *prevScriptObj;
15652     struct stat sb;
15653     int retcode;
15654     int readlen;
15655 
15656     if (stat(filename, &sb) != 0 || (fp = fopen(filename, "rt")) == NULL) {
15657         Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
15658         return JIM_ERR;
15659     }
15660     if (sb.st_size == 0) {
15661         fclose(fp);
15662         return JIM_OK;
15663     }
15664 
15665     buf = Jim_Alloc(sb.st_size + 1);
15666     readlen = fread(buf, 1, sb.st_size, fp);
15667     if (ferror(fp)) {
15668         fclose(fp);
15669         Jim_Free(buf);
15670         Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
15671         return JIM_ERR;
15672     }
15673     fclose(fp);
15674     buf[readlen] = 0;
15675 
15676     scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
15677     JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1);
15678     Jim_IncrRefCount(scriptObjPtr);
15679 
15680     prevScriptObj = interp->currentScriptObj;
15681     interp->currentScriptObj = scriptObjPtr;
15682 
15683     retcode = Jim_EvalObj(interp, scriptObjPtr);
15684 
15685 
15686     if (retcode == JIM_RETURN) {
15687         if (--interp->returnLevel <= 0) {
15688             retcode = interp->returnCode;
15689             interp->returnCode = JIM_OK;
15690             interp->returnLevel = 0;
15691         }
15692     }
15693     if (retcode == JIM_ERR) {
15694 
15695         interp->addStackTrace++;
15696     }
15697 
15698     interp->currentScriptObj = prevScriptObj;
15699 
15700     Jim_DecrRefCount(interp, scriptObjPtr);
15701 
15702     return retcode;
15703 }
15704 
JimParseSubst(struct JimParserCtx * pc,int flags)15705 static void JimParseSubst(struct JimParserCtx *pc, int flags)
15706 {
15707     pc->tstart = pc->p;
15708     pc->tline = pc->linenr;
15709 
15710     if (pc->len == 0) {
15711         pc->tend = pc->p;
15712         pc->tt = JIM_TT_EOL;
15713         pc->eof = 1;
15714         return;
15715     }
15716     if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15717         JimParseCmd(pc);
15718         return;
15719     }
15720     if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15721         if (JimParseVar(pc) == JIM_OK) {
15722             return;
15723         }
15724 
15725         pc->tstart = pc->p;
15726         flags |= JIM_SUBST_NOVAR;
15727     }
15728     while (pc->len) {
15729         if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15730             break;
15731         }
15732         if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15733             break;
15734         }
15735         if (*pc->p == '\\' && pc->len > 1) {
15736             pc->p++;
15737             pc->len--;
15738         }
15739         pc->p++;
15740         pc->len--;
15741     }
15742     pc->tend = pc->p - 1;
15743     pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
15744 }
15745 
15746 
SetSubstFromAny(Jim_Interp * interp,struct Jim_Obj * objPtr,int flags)15747 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
15748 {
15749     int scriptTextLen;
15750     const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
15751     struct JimParserCtx parser;
15752     struct ScriptObj *script = Jim_Alloc(sizeof(*script));
15753     ParseTokenList tokenlist;
15754 
15755 
15756     ScriptTokenListInit(&tokenlist);
15757 
15758     JimParserInit(&parser, scriptText, scriptTextLen, 1);
15759     while (1) {
15760         JimParseSubst(&parser, flags);
15761         if (parser.eof) {
15762 
15763             break;
15764         }
15765         ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
15766             parser.tline);
15767     }
15768 
15769 
15770     script->inUse = 1;
15771     script->substFlags = flags;
15772     script->fileNameObj = interp->emptyObj;
15773     Jim_IncrRefCount(script->fileNameObj);
15774     SubstObjAddTokens(interp, script, &tokenlist);
15775 
15776 
15777     ScriptTokenListFree(&tokenlist);
15778 
15779 #ifdef DEBUG_SHOW_SUBST
15780     {
15781         int i;
15782 
15783         printf("==== Subst ====\n");
15784         for (i = 0; i < script->len; i++) {
15785             printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
15786                 Jim_String(script->token[i].objPtr));
15787         }
15788     }
15789 #endif
15790 
15791 
15792     Jim_FreeIntRep(interp, objPtr);
15793     Jim_SetIntRepPtr(objPtr, script);
15794     objPtr->typePtr = &scriptObjType;
15795     return JIM_OK;
15796 }
15797 
Jim_GetSubst(Jim_Interp * interp,Jim_Obj * objPtr,int flags)15798 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
15799 {
15800     if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
15801         SetSubstFromAny(interp, objPtr, flags);
15802     return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
15803 }
15804 
Jim_SubstObj(Jim_Interp * interp,Jim_Obj * substObjPtr,Jim_Obj ** resObjPtrPtr,int flags)15805 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
15806 {
15807     ScriptObj *script = Jim_GetSubst(interp, substObjPtr, flags);
15808 
15809     Jim_IncrRefCount(substObjPtr);
15810     script->inUse++;
15811 
15812     *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
15813 
15814     script->inUse--;
15815     Jim_DecrRefCount(interp, substObjPtr);
15816     if (*resObjPtrPtr == NULL) {
15817         return JIM_ERR;
15818     }
15819     return JIM_OK;
15820 }
15821 
Jim_WrongNumArgs(Jim_Interp * interp,int argc,Jim_Obj * const * argv,const char * msg)15822 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
15823 {
15824     Jim_Obj *objPtr;
15825     Jim_Obj *listObjPtr;
15826 
15827     JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0"));
15828 
15829     listObjPtr = Jim_NewListObj(interp, argv, argc);
15830 
15831     if (*msg) {
15832         Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
15833     }
15834     Jim_IncrRefCount(listObjPtr);
15835     objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
15836     Jim_DecrRefCount(interp, listObjPtr);
15837 
15838     Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
15839 }
15840 
15841 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
15842     Jim_HashEntry *he, int type);
15843 
15844 #define JimTrivialMatch(pattern)    (strpbrk((pattern), "*[?\\") == NULL)
15845 
JimHashtablePatternMatch(Jim_Interp * interp,Jim_HashTable * ht,Jim_Obj * patternObjPtr,JimHashtableIteratorCallbackType * callback,int type)15846 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
15847     JimHashtableIteratorCallbackType *callback, int type)
15848 {
15849     Jim_HashEntry *he;
15850     Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
15851 
15852 
15853     if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
15854         he = Jim_FindHashEntry(ht, Jim_String(patternObjPtr));
15855         if (he) {
15856             callback(interp, listObjPtr, he, type);
15857         }
15858     }
15859     else {
15860         Jim_HashTableIterator htiter;
15861         JimInitHashTableIterator(ht, &htiter);
15862         while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
15863             if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), he->key, 0)) {
15864                 callback(interp, listObjPtr, he, type);
15865             }
15866         }
15867     }
15868     return listObjPtr;
15869 }
15870 
15871 
15872 #define JIM_CMDLIST_COMMANDS 0
15873 #define JIM_CMDLIST_PROCS 1
15874 #define JIM_CMDLIST_CHANNELS 2
15875 
JimCommandMatch(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_HashEntry * he,int type)15876 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15877     Jim_HashEntry *he, int type)
15878 {
15879     Jim_Cmd *cmdPtr = Jim_GetHashEntryVal(he);
15880     Jim_Obj *objPtr;
15881 
15882     if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
15883 
15884         return;
15885     }
15886 
15887     objPtr = Jim_NewStringObj(interp, he->key, -1);
15888     Jim_IncrRefCount(objPtr);
15889 
15890     if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, objPtr)) {
15891         Jim_ListAppendElement(interp, listObjPtr, objPtr);
15892     }
15893     Jim_DecrRefCount(interp, objPtr);
15894 }
15895 
15896 
JimCommandsList(Jim_Interp * interp,Jim_Obj * patternObjPtr,int type)15897 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
15898 {
15899     return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
15900 }
15901 
15902 
15903 #define JIM_VARLIST_GLOBALS 0
15904 #define JIM_VARLIST_LOCALS 1
15905 #define JIM_VARLIST_VARS 2
15906 
15907 #define JIM_VARLIST_VALUES 0x1000
15908 
JimVariablesMatch(Jim_Interp * interp,Jim_Obj * listObjPtr,Jim_HashEntry * he,int type)15909 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15910     Jim_HashEntry *he, int type)
15911 {
15912     Jim_Var *varPtr = Jim_GetHashEntryVal(he);
15913 
15914     if (type != JIM_VARLIST_LOCALS || varPtr->linkFramePtr == NULL) {
15915         Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1));
15916         if (type & JIM_VARLIST_VALUES) {
15917             Jim_ListAppendElement(interp, listObjPtr, varPtr->objPtr);
15918         }
15919     }
15920 }
15921 
15922 
JimVariablesList(Jim_Interp * interp,Jim_Obj * patternObjPtr,int mode)15923 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
15924 {
15925     if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
15926         return interp->emptyObj;
15927     }
15928     else {
15929         Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
15930         return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch, mode);
15931     }
15932 }
15933 
JimInfoLevel(Jim_Interp * interp,Jim_Obj * levelObjPtr,Jim_Obj ** objPtrPtr,int info_level_cmd)15934 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
15935     Jim_Obj **objPtrPtr, int info_level_cmd)
15936 {
15937     Jim_CallFrame *targetCallFrame;
15938 
15939     targetCallFrame = JimGetCallFrameByInteger(interp, levelObjPtr);
15940     if (targetCallFrame == NULL) {
15941         return JIM_ERR;
15942     }
15943 
15944     if (targetCallFrame == interp->topFramePtr) {
15945         Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
15946         return JIM_ERR;
15947     }
15948     if (info_level_cmd) {
15949         *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
15950     }
15951     else {
15952         Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
15953 
15954         Jim_ListAppendElement(interp, listObj, targetCallFrame->argv[0]);
15955         Jim_ListAppendElement(interp, listObj, targetCallFrame->fileNameObj);
15956         Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, targetCallFrame->line));
15957         *objPtrPtr = listObj;
15958     }
15959     return JIM_OK;
15960 }
15961 
15962 
15963 
Jim_PutsCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)15964 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15965 {
15966     if (argc != 2 && argc != 3) {
15967         Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
15968         return JIM_ERR;
15969     }
15970     if (argc == 3) {
15971         if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
15972             Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
15973             return JIM_ERR;
15974         }
15975         else {
15976             fputs(Jim_String(argv[2]), stdout);
15977         }
15978     }
15979     else {
15980         puts(Jim_String(argv[1]));
15981     }
15982     return JIM_OK;
15983 }
15984 
15985 
JimAddMulHelper(Jim_Interp * interp,int argc,Jim_Obj * const * argv,int op)15986 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
15987 {
15988     jim_wide wideValue, res;
15989     double doubleValue, doubleRes;
15990     int i;
15991 
15992     res = (op == JIM_EXPROP_ADD) ? 0 : 1;
15993 
15994     for (i = 1; i < argc; i++) {
15995         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
15996             goto trydouble;
15997         if (op == JIM_EXPROP_ADD)
15998             res += wideValue;
15999         else
16000             res *= wideValue;
16001     }
16002     Jim_SetResultInt(interp, res);
16003     return JIM_OK;
16004   trydouble:
16005     doubleRes = (double)res;
16006     for (; i < argc; i++) {
16007         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
16008             return JIM_ERR;
16009         if (op == JIM_EXPROP_ADD)
16010             doubleRes += doubleValue;
16011         else
16012             doubleRes *= doubleValue;
16013     }
16014     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16015     return JIM_OK;
16016 }
16017 
16018 
JimSubDivHelper(Jim_Interp * interp,int argc,Jim_Obj * const * argv,int op)16019 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
16020 {
16021     jim_wide wideValue, res = 0;
16022     double doubleValue, doubleRes = 0;
16023     int i = 2;
16024 
16025     if (argc < 2) {
16026         Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
16027         return JIM_ERR;
16028     }
16029     else if (argc == 2) {
16030         if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
16031             if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
16032                 return JIM_ERR;
16033             }
16034             else {
16035                 if (op == JIM_EXPROP_SUB)
16036                     doubleRes = -doubleValue;
16037                 else
16038                     doubleRes = 1.0 / doubleValue;
16039                 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16040                 return JIM_OK;
16041             }
16042         }
16043         if (op == JIM_EXPROP_SUB) {
16044             res = -wideValue;
16045             Jim_SetResultInt(interp, res);
16046         }
16047         else {
16048             doubleRes = 1.0 / wideValue;
16049             Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16050         }
16051         return JIM_OK;
16052     }
16053     else {
16054         if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
16055             if (Jim_GetDouble(interp, argv[1], &doubleRes)
16056                 != JIM_OK) {
16057                 return JIM_ERR;
16058             }
16059             else {
16060                 goto trydouble;
16061             }
16062         }
16063     }
16064     for (i = 2; i < argc; i++) {
16065         if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
16066             doubleRes = (double)res;
16067             goto trydouble;
16068         }
16069         if (op == JIM_EXPROP_SUB)
16070             res -= wideValue;
16071         else
16072             res /= wideValue;
16073     }
16074     Jim_SetResultInt(interp, res);
16075     return JIM_OK;
16076   trydouble:
16077     for (; i < argc; i++) {
16078         if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
16079             return JIM_ERR;
16080         if (op == JIM_EXPROP_SUB)
16081             doubleRes -= doubleValue;
16082         else
16083             doubleRes /= doubleValue;
16084     }
16085     Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16086     return JIM_OK;
16087 }
16088 
16089 
16090 
Jim_AddCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16091 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16092 {
16093     return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
16094 }
16095 
16096 
Jim_MulCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16097 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16098 {
16099     return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
16100 }
16101 
16102 
Jim_SubCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16103 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16104 {
16105     return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
16106 }
16107 
16108 
Jim_DivCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16109 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16110 {
16111     return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
16112 }
16113 
16114 
Jim_SetCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16115 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16116 {
16117     if (argc != 2 && argc != 3) {
16118         Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
16119         return JIM_ERR;
16120     }
16121     if (argc == 2) {
16122         Jim_Obj *objPtr;
16123 
16124         objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16125         if (!objPtr)
16126             return JIM_ERR;
16127         Jim_SetResult(interp, objPtr);
16128         return JIM_OK;
16129     }
16130 
16131     if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
16132         return JIM_ERR;
16133     Jim_SetResult(interp, argv[2]);
16134     return JIM_OK;
16135 }
16136 
Jim_UnsetCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16137 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16138 {
16139     int i = 1;
16140     int complain = 1;
16141 
16142     while (i < argc) {
16143         if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
16144             i++;
16145             break;
16146         }
16147         if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
16148             complain = 0;
16149             i++;
16150             continue;
16151         }
16152         break;
16153     }
16154 
16155     while (i < argc) {
16156         if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
16157             && complain) {
16158             return JIM_ERR;
16159         }
16160         i++;
16161     }
16162     return JIM_OK;
16163 }
16164 
16165 
Jim_WhileCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16166 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16167 {
16168     if (argc != 3) {
16169         Jim_WrongNumArgs(interp, 1, argv, "condition body");
16170         return JIM_ERR;
16171     }
16172 
16173 
16174     while (1) {
16175         int boolean, retval;
16176 
16177         if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
16178             return retval;
16179         if (!boolean)
16180             break;
16181 
16182         if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
16183             switch (retval) {
16184                 case JIM_BREAK:
16185                     goto out;
16186                     break;
16187                 case JIM_CONTINUE:
16188                     continue;
16189                     break;
16190                 default:
16191                     return retval;
16192             }
16193         }
16194     }
16195   out:
16196     Jim_SetEmptyResult(interp);
16197     return JIM_OK;
16198 }
16199 
16200 
Jim_ForCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16201 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16202 {
16203     int retval;
16204     int boolean = 1;
16205     Jim_Obj *varNamePtr = NULL;
16206     Jim_Obj *stopVarNamePtr = NULL;
16207 
16208     if (argc != 5) {
16209         Jim_WrongNumArgs(interp, 1, argv, "start test next body");
16210         return JIM_ERR;
16211     }
16212 
16213 
16214     if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
16215         return retval;
16216     }
16217 
16218     retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16219 
16220 
16221 #ifdef JIM_OPTIMIZATION
16222     if (retval == JIM_OK && boolean) {
16223         ScriptObj *incrScript;
16224         ExprByteCode *expr;
16225         jim_wide stop, currentVal;
16226         Jim_Obj *objPtr;
16227         int cmpOffset;
16228 
16229 
16230         expr = JimGetExpression(interp, argv[2]);
16231         incrScript = JimGetScript(interp, argv[3]);
16232 
16233 
16234         if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
16235             goto evalstart;
16236         }
16237 
16238         if (incrScript->token[1].type != JIM_TT_ESC ||
16239             expr->token[0].type != JIM_TT_VAR ||
16240             (expr->token[1].type != JIM_TT_EXPR_INT && expr->token[1].type != JIM_TT_VAR)) {
16241             goto evalstart;
16242         }
16243 
16244         if (expr->token[2].type == JIM_EXPROP_LT) {
16245             cmpOffset = 0;
16246         }
16247         else if (expr->token[2].type == JIM_EXPROP_LTE) {
16248             cmpOffset = 1;
16249         }
16250         else {
16251             goto evalstart;
16252         }
16253 
16254 
16255         if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
16256             goto evalstart;
16257         }
16258 
16259 
16260         if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->token[0].objPtr)) {
16261             goto evalstart;
16262         }
16263 
16264 
16265         if (expr->token[1].type == JIM_TT_EXPR_INT) {
16266             if (Jim_GetWide(interp, expr->token[1].objPtr, &stop) == JIM_ERR) {
16267                 goto evalstart;
16268             }
16269         }
16270         else {
16271             stopVarNamePtr = expr->token[1].objPtr;
16272             Jim_IncrRefCount(stopVarNamePtr);
16273 
16274             stop = 0;
16275         }
16276 
16277 
16278         varNamePtr = expr->token[0].objPtr;
16279         Jim_IncrRefCount(varNamePtr);
16280 
16281         objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
16282         if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
16283             goto testcond;
16284         }
16285 
16286 
16287         while (retval == JIM_OK) {
16288 
16289 
16290 
16291 
16292             if (stopVarNamePtr) {
16293                 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
16294                 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
16295                     goto testcond;
16296                 }
16297             }
16298 
16299             if (currentVal >= stop + cmpOffset) {
16300                 break;
16301             }
16302 
16303 
16304             retval = Jim_EvalObj(interp, argv[4]);
16305             if (retval == JIM_OK || retval == JIM_CONTINUE) {
16306                 retval = JIM_OK;
16307 
16308                 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
16309 
16310 
16311                 if (objPtr == NULL) {
16312                     retval = JIM_ERR;
16313                     goto out;
16314                 }
16315                 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16316                     currentVal = ++JimWideValue(objPtr);
16317                     Jim_InvalidateStringRep(objPtr);
16318                 }
16319                 else {
16320                     if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
16321                         Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
16322                                 ++currentVal)) != JIM_OK) {
16323                         goto evalnext;
16324                     }
16325                 }
16326             }
16327         }
16328         goto out;
16329     }
16330   evalstart:
16331 #endif
16332 
16333     while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
16334 
16335         retval = Jim_EvalObj(interp, argv[4]);
16336 
16337         if (retval == JIM_OK || retval == JIM_CONTINUE) {
16338 
16339 JIM_IF_OPTIM(evalnext:)
16340             retval = Jim_EvalObj(interp, argv[3]);
16341             if (retval == JIM_OK || retval == JIM_CONTINUE) {
16342 
16343 JIM_IF_OPTIM(testcond:)
16344                 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16345             }
16346         }
16347     }
16348 JIM_IF_OPTIM(out:)
16349     if (stopVarNamePtr) {
16350         Jim_DecrRefCount(interp, stopVarNamePtr);
16351     }
16352     if (varNamePtr) {
16353         Jim_DecrRefCount(interp, varNamePtr);
16354     }
16355 
16356     if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
16357         Jim_SetEmptyResult(interp);
16358         return JIM_OK;
16359     }
16360 
16361     return retval;
16362 }
16363 
16364 
Jim_LoopCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16365 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16366 {
16367     int retval;
16368     jim_wide i;
16369     jim_wide limit;
16370     jim_wide incr = 1;
16371     Jim_Obj *bodyObjPtr;
16372 
16373     if (argc != 5 && argc != 6) {
16374         Jim_WrongNumArgs(interp, 1, argv, "var first limit ?incr? body");
16375         return JIM_ERR;
16376     }
16377 
16378     if (Jim_GetWide(interp, argv[2], &i) != JIM_OK ||
16379         Jim_GetWide(interp, argv[3], &limit) != JIM_OK ||
16380           (argc == 6 && Jim_GetWide(interp, argv[4], &incr) != JIM_OK)) {
16381         return JIM_ERR;
16382     }
16383     bodyObjPtr = (argc == 5) ? argv[4] : argv[5];
16384 
16385     retval = Jim_SetVariable(interp, argv[1], argv[2]);
16386 
16387     while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
16388         retval = Jim_EvalObj(interp, bodyObjPtr);
16389         if (retval == JIM_OK || retval == JIM_CONTINUE) {
16390             Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16391 
16392             retval = JIM_OK;
16393 
16394 
16395             i += incr;
16396 
16397             if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16398                 if (argv[1]->typePtr != &variableObjType) {
16399                     if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16400                         return JIM_ERR;
16401                     }
16402                 }
16403                 JimWideValue(objPtr) = i;
16404                 Jim_InvalidateStringRep(objPtr);
16405 
16406                 if (argv[1]->typePtr != &variableObjType) {
16407                     if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16408                         retval = JIM_ERR;
16409                         break;
16410                     }
16411                 }
16412             }
16413             else {
16414                 objPtr = Jim_NewIntObj(interp, i);
16415                 retval = Jim_SetVariable(interp, argv[1], objPtr);
16416                 if (retval != JIM_OK) {
16417                     Jim_FreeNewObj(interp, objPtr);
16418                 }
16419             }
16420         }
16421     }
16422 
16423     if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
16424         Jim_SetEmptyResult(interp);
16425         return JIM_OK;
16426     }
16427     return retval;
16428 }
16429 
16430 typedef struct {
16431     Jim_Obj *objPtr;
16432     int idx;
16433 } Jim_ListIter;
16434 
JimListIterInit(Jim_ListIter * iter,Jim_Obj * objPtr)16435 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
16436 {
16437     iter->objPtr = objPtr;
16438     iter->idx = 0;
16439 }
16440 
JimListIterNext(Jim_Interp * interp,Jim_ListIter * iter)16441 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
16442 {
16443     if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
16444         return NULL;
16445     }
16446     return iter->objPtr->internalRep.listValue.ele[iter->idx++];
16447 }
16448 
JimListIterDone(Jim_Interp * interp,Jim_ListIter * iter)16449 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
16450 {
16451     return iter->idx >= Jim_ListLength(interp, iter->objPtr);
16452 }
16453 
16454 
JimForeachMapHelper(Jim_Interp * interp,int argc,Jim_Obj * const * argv,int doMap)16455 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
16456 {
16457     int result = JIM_OK;
16458     int i, numargs;
16459     Jim_ListIter twoiters[2];
16460     Jim_ListIter *iters;
16461     Jim_Obj *script;
16462     Jim_Obj *resultObj;
16463 
16464     if (argc < 4 || argc % 2 != 0) {
16465         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
16466         return JIM_ERR;
16467     }
16468     script = argv[argc - 1];
16469     numargs = (argc - 1 - 1);
16470 
16471     if (numargs == 2) {
16472         iters = twoiters;
16473     }
16474     else {
16475         iters = Jim_Alloc(numargs * sizeof(*iters));
16476     }
16477     for (i = 0; i < numargs; i++) {
16478         JimListIterInit(&iters[i], argv[i + 1]);
16479         if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
16480             result = JIM_ERR;
16481         }
16482     }
16483     if (result != JIM_OK) {
16484         Jim_SetResultString(interp, "foreach varlist is empty", -1);
16485         return result;
16486     }
16487 
16488     if (doMap) {
16489         resultObj = Jim_NewListObj(interp, NULL, 0);
16490     }
16491     else {
16492         resultObj = interp->emptyObj;
16493     }
16494     Jim_IncrRefCount(resultObj);
16495 
16496     while (1) {
16497 
16498         for (i = 0; i < numargs; i += 2) {
16499             if (!JimListIterDone(interp, &iters[i + 1])) {
16500                 break;
16501             }
16502         }
16503         if (i == numargs) {
16504 
16505             break;
16506         }
16507 
16508 
16509         for (i = 0; i < numargs; i += 2) {
16510             Jim_Obj *varName;
16511 
16512 
16513             JimListIterInit(&iters[i], argv[i + 1]);
16514             while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
16515                 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
16516                 if (!valObj) {
16517 
16518                     valObj = interp->emptyObj;
16519                 }
16520 
16521                 Jim_IncrRefCount(valObj);
16522                 result = Jim_SetVariable(interp, varName, valObj);
16523                 Jim_DecrRefCount(interp, valObj);
16524                 if (result != JIM_OK) {
16525                     goto err;
16526                 }
16527             }
16528         }
16529         switch (result = Jim_EvalObj(interp, script)) {
16530             case JIM_OK:
16531                 if (doMap) {
16532                     Jim_ListAppendElement(interp, resultObj, interp->result);
16533                 }
16534                 break;
16535             case JIM_CONTINUE:
16536                 break;
16537             case JIM_BREAK:
16538                 goto out;
16539             default:
16540                 goto err;
16541         }
16542     }
16543   out:
16544     result = JIM_OK;
16545     Jim_SetResult(interp, resultObj);
16546   err:
16547     Jim_DecrRefCount(interp, resultObj);
16548     if (numargs > 2) {
16549         Jim_Free(iters);
16550     }
16551     return result;
16552 }
16553 
16554 
Jim_ForeachCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16555 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16556 {
16557     return JimForeachMapHelper(interp, argc, argv, 0);
16558 }
16559 
16560 
Jim_LmapCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16561 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16562 {
16563     return JimForeachMapHelper(interp, argc, argv, 1);
16564 }
16565 
16566 
Jim_LassignCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16567 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16568 {
16569     int result = JIM_ERR;
16570     int i;
16571     Jim_ListIter iter;
16572     Jim_Obj *resultObj;
16573 
16574     if (argc < 2) {
16575         Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
16576         return JIM_ERR;
16577     }
16578 
16579     JimListIterInit(&iter, argv[1]);
16580 
16581     for (i = 2; i < argc; i++) {
16582         Jim_Obj *valObj = JimListIterNext(interp, &iter);
16583         result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
16584         if (result != JIM_OK) {
16585             return result;
16586         }
16587     }
16588 
16589     resultObj = Jim_NewListObj(interp, NULL, 0);
16590     while (!JimListIterDone(interp, &iter)) {
16591         Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
16592     }
16593 
16594     Jim_SetResult(interp, resultObj);
16595 
16596     return JIM_OK;
16597 }
16598 
16599 
Jim_IfCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16600 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16601 {
16602     int boolean, retval, current = 1, falsebody = 0;
16603 
16604     if (argc >= 3) {
16605         while (1) {
16606 
16607             if (current >= argc)
16608                 goto err;
16609             if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
16610                 != JIM_OK)
16611                 return retval;
16612 
16613             if (current >= argc)
16614                 goto err;
16615             if (Jim_CompareStringImmediate(interp, argv[current], "then"))
16616                 current++;
16617 
16618             if (current >= argc)
16619                 goto err;
16620             if (boolean)
16621                 return Jim_EvalObj(interp, argv[current]);
16622 
16623             if (++current >= argc) {
16624                 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
16625                 return JIM_OK;
16626             }
16627             falsebody = current++;
16628             if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
16629 
16630                 if (current != argc - 1)
16631                     goto err;
16632                 return Jim_EvalObj(interp, argv[current]);
16633             }
16634             else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
16635                 continue;
16636 
16637             else if (falsebody != argc - 1)
16638                 goto err;
16639             return Jim_EvalObj(interp, argv[falsebody]);
16640         }
16641         return JIM_OK;
16642     }
16643   err:
16644     Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
16645     return JIM_ERR;
16646 }
16647 
16648 
16649 
Jim_CommandMatchObj(Jim_Interp * interp,Jim_Obj * commandObj,Jim_Obj * patternObj,Jim_Obj * stringObj,int nocase)16650 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
16651     Jim_Obj *stringObj, int nocase)
16652 {
16653     Jim_Obj *parms[4];
16654     int argc = 0;
16655     long eq;
16656     int rc;
16657 
16658     parms[argc++] = commandObj;
16659     if (nocase) {
16660         parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
16661     }
16662     parms[argc++] = patternObj;
16663     parms[argc++] = stringObj;
16664 
16665     rc = Jim_EvalObjVector(interp, argc, parms);
16666 
16667     if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
16668         eq = -rc;
16669     }
16670 
16671     return eq;
16672 }
16673 
16674 enum
16675 { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
16676 
16677 
Jim_SwitchCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16678 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16679 {
16680     int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
16681     Jim_Obj *command = 0, *const *caseList = 0, *strObj;
16682     Jim_Obj *script = 0;
16683 
16684     if (argc < 3) {
16685       wrongnumargs:
16686         Jim_WrongNumArgs(interp, 1, argv, "?options? string "
16687             "pattern body ... ?default body?   or   " "{pattern body ?pattern body ...?}");
16688         return JIM_ERR;
16689     }
16690     for (opt = 1; opt < argc; ++opt) {
16691         const char *option = Jim_String(argv[opt]);
16692 
16693         if (*option != '-')
16694             break;
16695         else if (strncmp(option, "--", 2) == 0) {
16696             ++opt;
16697             break;
16698         }
16699         else if (strncmp(option, "-exact", 2) == 0)
16700             matchOpt = SWITCH_EXACT;
16701         else if (strncmp(option, "-glob", 2) == 0)
16702             matchOpt = SWITCH_GLOB;
16703         else if (strncmp(option, "-regexp", 2) == 0)
16704             matchOpt = SWITCH_RE;
16705         else if (strncmp(option, "-command", 2) == 0) {
16706             matchOpt = SWITCH_CMD;
16707             if ((argc - opt) < 2)
16708                 goto wrongnumargs;
16709             command = argv[++opt];
16710         }
16711         else {
16712             Jim_SetResultFormatted(interp,
16713                 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
16714                 argv[opt]);
16715             return JIM_ERR;
16716         }
16717         if ((argc - opt) < 2)
16718             goto wrongnumargs;
16719     }
16720     strObj = argv[opt++];
16721     patCount = argc - opt;
16722     if (patCount == 1) {
16723         Jim_Obj **vector;
16724 
16725         JimListGetElements(interp, argv[opt], &patCount, &vector);
16726         caseList = vector;
16727     }
16728     else
16729         caseList = &argv[opt];
16730     if (patCount == 0 || patCount % 2 != 0)
16731         goto wrongnumargs;
16732     for (i = 0; script == 0 && i < patCount; i += 2) {
16733         Jim_Obj *patObj = caseList[i];
16734 
16735         if (!Jim_CompareStringImmediate(interp, patObj, "default")
16736             || i < (patCount - 2)) {
16737             switch (matchOpt) {
16738                 case SWITCH_EXACT:
16739                     if (Jim_StringEqObj(strObj, patObj))
16740                         script = caseList[i + 1];
16741                     break;
16742                 case SWITCH_GLOB:
16743                     if (Jim_StringMatchObj(interp, patObj, strObj, 0))
16744                         script = caseList[i + 1];
16745                     break;
16746                 case SWITCH_RE:
16747                     command = Jim_NewStringObj(interp, "regexp", -1);
16748 
16749                 case SWITCH_CMD:{
16750                         int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, 0);
16751 
16752                         if (argc - opt == 1) {
16753                             Jim_Obj **vector;
16754 
16755                             JimListGetElements(interp, argv[opt], &patCount, &vector);
16756                             caseList = vector;
16757                         }
16758 
16759                         if (rc < 0) {
16760                             return -rc;
16761                         }
16762                         if (rc)
16763                             script = caseList[i + 1];
16764                         break;
16765                     }
16766             }
16767         }
16768         else {
16769             script = caseList[i + 1];
16770         }
16771     }
16772     for (; i < patCount && Jim_CompareStringImmediate(interp, script, "-"); i += 2)
16773         script = caseList[i + 1];
16774     if (script && Jim_CompareStringImmediate(interp, script, "-")) {
16775         Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
16776         return JIM_ERR;
16777     }
16778     Jim_SetEmptyResult(interp);
16779     if (script) {
16780         return Jim_EvalObj(interp, script);
16781     }
16782     return JIM_OK;
16783 }
16784 
16785 
Jim_ListCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16786 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16787 {
16788     Jim_Obj *listObjPtr;
16789 
16790     listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1);
16791     Jim_SetResult(interp, listObjPtr);
16792     return JIM_OK;
16793 }
16794 
16795 
Jim_LindexCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16796 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16797 {
16798     Jim_Obj *objPtr, *listObjPtr;
16799     int i;
16800     int idx;
16801 
16802     if (argc < 2) {
16803         Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
16804         return JIM_ERR;
16805     }
16806     objPtr = argv[1];
16807     Jim_IncrRefCount(objPtr);
16808     for (i = 2; i < argc; i++) {
16809         listObjPtr = objPtr;
16810         if (Jim_GetIndex(interp, argv[i], &idx) != JIM_OK) {
16811             Jim_DecrRefCount(interp, listObjPtr);
16812             return JIM_ERR;
16813         }
16814         if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_NONE) != JIM_OK) {
16815             Jim_DecrRefCount(interp, listObjPtr);
16816             Jim_SetEmptyResult(interp);
16817             return JIM_OK;
16818         }
16819         Jim_IncrRefCount(objPtr);
16820         Jim_DecrRefCount(interp, listObjPtr);
16821     }
16822     Jim_SetResult(interp, objPtr);
16823     Jim_DecrRefCount(interp, objPtr);
16824     return JIM_OK;
16825 }
16826 
16827 
Jim_LlengthCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16828 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16829 {
16830     if (argc != 2) {
16831         Jim_WrongNumArgs(interp, 1, argv, "list");
16832         return JIM_ERR;
16833     }
16834     Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
16835     return JIM_OK;
16836 }
16837 
16838 
Jim_LsearchCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16839 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16840 {
16841     static const char * const options[] = {
16842         "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
16843             NULL
16844     };
16845     enum
16846     { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
16847             OPT_COMMAND };
16848     int i;
16849     int opt_bool = 0;
16850     int opt_not = 0;
16851     int opt_nocase = 0;
16852     int opt_all = 0;
16853     int opt_inline = 0;
16854     int opt_match = OPT_EXACT;
16855     int listlen;
16856     int rc = JIM_OK;
16857     Jim_Obj *listObjPtr = NULL;
16858     Jim_Obj *commandObj = NULL;
16859 
16860     if (argc < 3) {
16861       wrongargs:
16862         Jim_WrongNumArgs(interp, 1, argv,
16863             "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? list value");
16864         return JIM_ERR;
16865     }
16866 
16867     for (i = 1; i < argc - 2; i++) {
16868         int option;
16869 
16870         if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
16871             return JIM_ERR;
16872         }
16873         switch (option) {
16874             case OPT_BOOL:
16875                 opt_bool = 1;
16876                 opt_inline = 0;
16877                 break;
16878             case OPT_NOT:
16879                 opt_not = 1;
16880                 break;
16881             case OPT_NOCASE:
16882                 opt_nocase = 1;
16883                 break;
16884             case OPT_INLINE:
16885                 opt_inline = 1;
16886                 opt_bool = 0;
16887                 break;
16888             case OPT_ALL:
16889                 opt_all = 1;
16890                 break;
16891             case OPT_COMMAND:
16892                 if (i >= argc - 2) {
16893                     goto wrongargs;
16894                 }
16895                 commandObj = argv[++i];
16896 
16897             case OPT_EXACT:
16898             case OPT_GLOB:
16899             case OPT_REGEXP:
16900                 opt_match = option;
16901                 break;
16902         }
16903     }
16904 
16905     argv += i;
16906 
16907     if (opt_all) {
16908         listObjPtr = Jim_NewListObj(interp, NULL, 0);
16909     }
16910     if (opt_match == OPT_REGEXP) {
16911         commandObj = Jim_NewStringObj(interp, "regexp", -1);
16912     }
16913     if (commandObj) {
16914         Jim_IncrRefCount(commandObj);
16915     }
16916 
16917     listlen = Jim_ListLength(interp, argv[0]);
16918     for (i = 0; i < listlen; i++) {
16919         int eq = 0;
16920         Jim_Obj *objPtr = Jim_ListGetIndex(interp, argv[0], i);
16921 
16922         switch (opt_match) {
16923             case OPT_EXACT:
16924                 eq = Jim_StringCompareObj(interp, argv[1], objPtr, opt_nocase) == 0;
16925                 break;
16926 
16927             case OPT_GLOB:
16928                 eq = Jim_StringMatchObj(interp, argv[1], objPtr, opt_nocase);
16929                 break;
16930 
16931             case OPT_REGEXP:
16932             case OPT_COMMAND:
16933                 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, opt_nocase);
16934                 if (eq < 0) {
16935                     if (listObjPtr) {
16936                         Jim_FreeNewObj(interp, listObjPtr);
16937                     }
16938                     rc = JIM_ERR;
16939                     goto done;
16940                 }
16941                 break;
16942         }
16943 
16944 
16945         if (!eq && opt_bool && opt_not && !opt_all) {
16946             continue;
16947         }
16948 
16949         if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
16950 
16951             Jim_Obj *resultObj;
16952 
16953             if (opt_bool) {
16954                 resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
16955             }
16956             else if (!opt_inline) {
16957                 resultObj = Jim_NewIntObj(interp, i);
16958             }
16959             else {
16960                 resultObj = objPtr;
16961             }
16962 
16963             if (opt_all) {
16964                 Jim_ListAppendElement(interp, listObjPtr, resultObj);
16965             }
16966             else {
16967                 Jim_SetResult(interp, resultObj);
16968                 goto done;
16969             }
16970         }
16971     }
16972 
16973     if (opt_all) {
16974         Jim_SetResult(interp, listObjPtr);
16975     }
16976     else {
16977 
16978         if (opt_bool) {
16979             Jim_SetResultBool(interp, opt_not);
16980         }
16981         else if (!opt_inline) {
16982             Jim_SetResultInt(interp, -1);
16983         }
16984     }
16985 
16986   done:
16987     if (commandObj) {
16988         Jim_DecrRefCount(interp, commandObj);
16989     }
16990     return rc;
16991 }
16992 
16993 
Jim_LappendCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)16994 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16995 {
16996     Jim_Obj *listObjPtr;
16997     int new_obj = 0;
16998     int i;
16999 
17000     if (argc < 2) {
17001         Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
17002         return JIM_ERR;
17003     }
17004     listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
17005     if (!listObjPtr) {
17006 
17007         listObjPtr = Jim_NewListObj(interp, NULL, 0);
17008         new_obj = 1;
17009     }
17010     else if (Jim_IsShared(listObjPtr)) {
17011         listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
17012         new_obj = 1;
17013     }
17014     for (i = 2; i < argc; i++)
17015         Jim_ListAppendElement(interp, listObjPtr, argv[i]);
17016     if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
17017         if (new_obj)
17018             Jim_FreeNewObj(interp, listObjPtr);
17019         return JIM_ERR;
17020     }
17021     Jim_SetResult(interp, listObjPtr);
17022     return JIM_OK;
17023 }
17024 
17025 
Jim_LinsertCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17026 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17027 {
17028     int idx, len;
17029     Jim_Obj *listPtr;
17030 
17031     if (argc < 3) {
17032         Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
17033         return JIM_ERR;
17034     }
17035     listPtr = argv[1];
17036     if (Jim_IsShared(listPtr))
17037         listPtr = Jim_DuplicateObj(interp, listPtr);
17038     if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
17039         goto err;
17040     len = Jim_ListLength(interp, listPtr);
17041     if (idx >= len)
17042         idx = len;
17043     else if (idx < 0)
17044         idx = len + idx + 1;
17045     Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
17046     Jim_SetResult(interp, listPtr);
17047     return JIM_OK;
17048   err:
17049     if (listPtr != argv[1]) {
17050         Jim_FreeNewObj(interp, listPtr);
17051     }
17052     return JIM_ERR;
17053 }
17054 
17055 
Jim_LreplaceCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17056 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17057 {
17058     int first, last, len, rangeLen;
17059     Jim_Obj *listObj;
17060     Jim_Obj *newListObj;
17061 
17062     if (argc < 4) {
17063         Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
17064         return JIM_ERR;
17065     }
17066     if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
17067         Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
17068         return JIM_ERR;
17069     }
17070 
17071     listObj = argv[1];
17072     len = Jim_ListLength(interp, listObj);
17073 
17074     first = JimRelToAbsIndex(len, first);
17075     last = JimRelToAbsIndex(len, last);
17076     JimRelToAbsRange(len, &first, &last, &rangeLen);
17077 
17078 
17079 
17080     if (first < len) {
17081 
17082     }
17083     else if (len == 0) {
17084 
17085         first = 0;
17086     }
17087     else {
17088         Jim_SetResultString(interp, "list doesn't contain element ", -1);
17089         Jim_AppendObj(interp, Jim_GetResult(interp), argv[2]);
17090         return JIM_ERR;
17091     }
17092 
17093 
17094     newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
17095 
17096 
17097     ListInsertElements(newListObj, -1, argc - 4, argv + 4);
17098 
17099 
17100     ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
17101 
17102     Jim_SetResult(interp, newListObj);
17103     return JIM_OK;
17104 }
17105 
17106 
Jim_LsetCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17107 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17108 {
17109     if (argc < 3) {
17110         Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
17111         return JIM_ERR;
17112     }
17113     else if (argc == 3) {
17114 
17115         if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
17116             return JIM_ERR;
17117         Jim_SetResult(interp, argv[2]);
17118         return JIM_OK;
17119     }
17120     return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
17121 }
17122 
17123 
Jim_LsortCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const argv[])17124 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
17125 {
17126     static const char * const options[] = {
17127         "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", NULL
17128     };
17129     enum
17130     { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE };
17131     Jim_Obj *resObj;
17132     int i;
17133     int retCode;
17134 
17135     struct lsort_info info;
17136 
17137     if (argc < 2) {
17138         Jim_WrongNumArgs(interp, 1, argv, "?options? list");
17139         return JIM_ERR;
17140     }
17141 
17142     info.type = JIM_LSORT_ASCII;
17143     info.order = 1;
17144     info.indexed = 0;
17145     info.unique = 0;
17146     info.command = NULL;
17147     info.interp = interp;
17148 
17149     for (i = 1; i < (argc - 1); i++) {
17150         int option;
17151 
17152         if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
17153             != JIM_OK)
17154             return JIM_ERR;
17155         switch (option) {
17156             case OPT_ASCII:
17157                 info.type = JIM_LSORT_ASCII;
17158                 break;
17159             case OPT_NOCASE:
17160                 info.type = JIM_LSORT_NOCASE;
17161                 break;
17162             case OPT_INTEGER:
17163                 info.type = JIM_LSORT_INTEGER;
17164                 break;
17165             case OPT_REAL:
17166                 info.type = JIM_LSORT_REAL;
17167                 break;
17168             case OPT_INCREASING:
17169                 info.order = 1;
17170                 break;
17171             case OPT_DECREASING:
17172                 info.order = -1;
17173                 break;
17174             case OPT_UNIQUE:
17175                 info.unique = 1;
17176                 break;
17177             case OPT_COMMAND:
17178                 if (i >= (argc - 2)) {
17179                     Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
17180                     return JIM_ERR;
17181                 }
17182                 info.type = JIM_LSORT_COMMAND;
17183                 info.command = argv[i + 1];
17184                 i++;
17185                 break;
17186             case OPT_INDEX:
17187                 if (i >= (argc - 2)) {
17188                     Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
17189                     return JIM_ERR;
17190                 }
17191                 if (Jim_GetIndex(interp, argv[i + 1], &info.index) != JIM_OK) {
17192                     return JIM_ERR;
17193                 }
17194                 info.indexed = 1;
17195                 i++;
17196                 break;
17197         }
17198     }
17199     resObj = Jim_DuplicateObj(interp, argv[argc - 1]);
17200     retCode = ListSortElements(interp, resObj, &info);
17201     if (retCode == JIM_OK) {
17202         Jim_SetResult(interp, resObj);
17203     }
17204     else {
17205         Jim_FreeNewObj(interp, resObj);
17206     }
17207     return retCode;
17208 }
17209 
17210 
Jim_AppendCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17211 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17212 {
17213     Jim_Obj *stringObjPtr;
17214     int i;
17215 
17216     if (argc < 2) {
17217         Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
17218         return JIM_ERR;
17219     }
17220     if (argc == 2) {
17221         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17222         if (!stringObjPtr)
17223             return JIM_ERR;
17224     }
17225     else {
17226         int new_obj = 0;
17227         stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
17228         if (!stringObjPtr) {
17229 
17230             stringObjPtr = Jim_NewEmptyStringObj(interp);
17231             new_obj = 1;
17232         }
17233         else if (Jim_IsShared(stringObjPtr)) {
17234             new_obj = 1;
17235             stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
17236         }
17237         for (i = 2; i < argc; i++) {
17238             Jim_AppendObj(interp, stringObjPtr, argv[i]);
17239         }
17240         if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
17241             if (new_obj) {
17242                 Jim_FreeNewObj(interp, stringObjPtr);
17243             }
17244             return JIM_ERR;
17245         }
17246     }
17247     Jim_SetResult(interp, stringObjPtr);
17248     return JIM_OK;
17249 }
17250 
17251 
Jim_DebugCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17252 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17253 {
17254 #if !defined(JIM_DEBUG_COMMAND)
17255     Jim_SetResultString(interp, "unsupported", -1);
17256     return JIM_ERR;
17257 #endif
17258 }
17259 
17260 
Jim_EvalCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17261 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17262 {
17263     int rc;
17264 
17265     if (argc < 2) {
17266         Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
17267         return JIM_ERR;
17268     }
17269 
17270     if (argc == 2) {
17271         rc = Jim_EvalObj(interp, argv[1]);
17272     }
17273     else {
17274         rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17275     }
17276 
17277     if (rc == JIM_ERR) {
17278 
17279         interp->addStackTrace++;
17280     }
17281     return rc;
17282 }
17283 
17284 
Jim_UplevelCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17285 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17286 {
17287     if (argc >= 2) {
17288         int retcode;
17289         Jim_CallFrame *savedCallFrame, *targetCallFrame;
17290         const char *str;
17291 
17292 
17293         savedCallFrame = interp->framePtr;
17294 
17295 
17296         str = Jim_String(argv[1]);
17297         if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
17298             targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17299             argc--;
17300             argv++;
17301         }
17302         else {
17303             targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17304         }
17305         if (targetCallFrame == NULL) {
17306             return JIM_ERR;
17307         }
17308         if (argc < 2) {
17309             Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
17310             return JIM_ERR;
17311         }
17312 
17313         interp->framePtr = targetCallFrame;
17314         if (argc == 2) {
17315             retcode = Jim_EvalObj(interp, argv[1]);
17316         }
17317         else {
17318             retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17319         }
17320         interp->framePtr = savedCallFrame;
17321         return retcode;
17322     }
17323     else {
17324         Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
17325         return JIM_ERR;
17326     }
17327 }
17328 
17329 
Jim_ExprCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17330 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17331 {
17332     Jim_Obj *exprResultPtr;
17333     int retcode;
17334 
17335     if (argc == 2) {
17336         retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
17337     }
17338     else if (argc > 2) {
17339         Jim_Obj *objPtr;
17340 
17341         objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
17342         Jim_IncrRefCount(objPtr);
17343         retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
17344         Jim_DecrRefCount(interp, objPtr);
17345     }
17346     else {
17347         Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
17348         return JIM_ERR;
17349     }
17350     if (retcode != JIM_OK)
17351         return retcode;
17352     Jim_SetResult(interp, exprResultPtr);
17353     Jim_DecrRefCount(interp, exprResultPtr);
17354     return JIM_OK;
17355 }
17356 
17357 
Jim_BreakCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17358 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17359 {
17360     if (argc != 1) {
17361         Jim_WrongNumArgs(interp, 1, argv, "");
17362         return JIM_ERR;
17363     }
17364     return JIM_BREAK;
17365 }
17366 
17367 
Jim_ContinueCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17368 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17369 {
17370     if (argc != 1) {
17371         Jim_WrongNumArgs(interp, 1, argv, "");
17372         return JIM_ERR;
17373     }
17374     return JIM_CONTINUE;
17375 }
17376 
17377 
Jim_ReturnCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17378 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17379 {
17380     int i;
17381     Jim_Obj *stackTraceObj = NULL;
17382     Jim_Obj *errorCodeObj = NULL;
17383     int returnCode = JIM_OK;
17384     long level = 1;
17385 
17386     for (i = 1; i < argc - 1; i += 2) {
17387         if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
17388             if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
17389                 return JIM_ERR;
17390             }
17391         }
17392         else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
17393             stackTraceObj = argv[i + 1];
17394         }
17395         else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
17396             errorCodeObj = argv[i + 1];
17397         }
17398         else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
17399             if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
17400                 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
17401                 return JIM_ERR;
17402             }
17403         }
17404         else {
17405             break;
17406         }
17407     }
17408 
17409     if (i != argc - 1 && i != argc) {
17410         Jim_WrongNumArgs(interp, 1, argv,
17411             "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
17412     }
17413 
17414 
17415     if (stackTraceObj && returnCode == JIM_ERR) {
17416         JimSetStackTrace(interp, stackTraceObj);
17417     }
17418 
17419     if (errorCodeObj && returnCode == JIM_ERR) {
17420         Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
17421     }
17422     interp->returnCode = returnCode;
17423     interp->returnLevel = level;
17424 
17425     if (i == argc - 1) {
17426         Jim_SetResult(interp, argv[i]);
17427     }
17428     return JIM_RETURN;
17429 }
17430 
17431 
Jim_TailcallCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17432 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17433 {
17434     if (interp->framePtr->level == 0) {
17435         Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
17436         return JIM_ERR;
17437     }
17438     else if (argc >= 2) {
17439 
17440         Jim_CallFrame *cf = interp->framePtr->parent;
17441 
17442         Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17443         if (cmdPtr == NULL) {
17444             return JIM_ERR;
17445         }
17446 
17447         JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));
17448 
17449 
17450         JimIncrCmdRefCount(cmdPtr);
17451         cf->tailcallCmd = cmdPtr;
17452 
17453 
17454         JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));
17455 
17456         cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
17457         Jim_IncrRefCount(cf->tailcallObj);
17458 
17459 
17460         return JIM_EVAL;
17461     }
17462     return JIM_OK;
17463 }
17464 
JimAliasCmd(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17465 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17466 {
17467     Jim_Obj *cmdList;
17468     Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
17469 
17470 
17471     cmdList = Jim_DuplicateObj(interp, prefixListObj);
17472     Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
17473 
17474     return JimEvalObjList(interp, cmdList);
17475 }
17476 
JimAliasCmdDelete(Jim_Interp * interp,void * privData)17477 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
17478 {
17479     Jim_Obj *prefixListObj = privData;
17480     Jim_DecrRefCount(interp, prefixListObj);
17481 }
17482 
Jim_AliasCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17483 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17484 {
17485     Jim_Obj *prefixListObj;
17486     const char *newname;
17487 
17488     if (argc < 3) {
17489         Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
17490         return JIM_ERR;
17491     }
17492 
17493     prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
17494     Jim_IncrRefCount(prefixListObj);
17495     newname = Jim_String(argv[1]);
17496     if (newname[0] == ':' && newname[1] == ':') {
17497         while (*++newname == ':') {
17498         }
17499     }
17500 
17501     Jim_SetResult(interp, argv[1]);
17502 
17503     return Jim_CreateCommand(interp, newname, JimAliasCmd, prefixListObj, JimAliasCmdDelete);
17504 }
17505 
17506 
Jim_ProcCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17507 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17508 {
17509     Jim_Cmd *cmd;
17510 
17511     if (argc != 4 && argc != 5) {
17512         Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
17513         return JIM_ERR;
17514     }
17515 
17516     if (JimValidName(interp, "procedure", argv[1]) != JIM_OK) {
17517         return JIM_ERR;
17518     }
17519 
17520     if (argc == 4) {
17521         cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
17522     }
17523     else {
17524         cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
17525     }
17526 
17527     if (cmd) {
17528 
17529         Jim_Obj *qualifiedCmdNameObj;
17530         const char *cmdname = JimQualifyName(interp, Jim_String(argv[1]), &qualifiedCmdNameObj);
17531 
17532         JimCreateCommand(interp, cmdname, cmd);
17533 
17534 
17535         JimUpdateProcNamespace(interp, cmd, cmdname);
17536 
17537         JimFreeQualifiedName(interp, qualifiedCmdNameObj);
17538 
17539 
17540         Jim_SetResult(interp, argv[1]);
17541         return JIM_OK;
17542     }
17543     return JIM_ERR;
17544 }
17545 
17546 
Jim_LocalCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17547 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17548 {
17549     int retcode;
17550 
17551     if (argc < 2) {
17552         Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17553         return JIM_ERR;
17554     }
17555 
17556 
17557     interp->local++;
17558     retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17559     interp->local--;
17560 
17561 
17562 
17563     if (retcode == 0) {
17564         Jim_Obj *cmdNameObj = Jim_GetResult(interp);
17565 
17566         if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
17567             return JIM_ERR;
17568         }
17569         if (interp->framePtr->localCommands == NULL) {
17570             interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
17571             Jim_InitStack(interp->framePtr->localCommands);
17572         }
17573         Jim_IncrRefCount(cmdNameObj);
17574         Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
17575     }
17576 
17577     return retcode;
17578 }
17579 
17580 
Jim_UpcallCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17581 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17582 {
17583     if (argc < 2) {
17584         Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17585         return JIM_ERR;
17586     }
17587     else {
17588         int retcode;
17589 
17590         Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17591         if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
17592             Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
17593             return JIM_ERR;
17594         }
17595 
17596         cmdPtr->u.proc.upcall++;
17597         JimIncrCmdRefCount(cmdPtr);
17598 
17599 
17600         retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17601 
17602 
17603         cmdPtr->u.proc.upcall--;
17604         JimDecrCmdRefCount(interp, cmdPtr);
17605 
17606         return retcode;
17607     }
17608 }
17609 
17610 
Jim_ApplyCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17611 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17612 {
17613     if (argc < 2) {
17614         Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
17615         return JIM_ERR;
17616     }
17617     else {
17618         int ret;
17619         Jim_Cmd *cmd;
17620         Jim_Obj *argListObjPtr;
17621         Jim_Obj *bodyObjPtr;
17622         Jim_Obj *nsObj = NULL;
17623         Jim_Obj **nargv;
17624 
17625         int len = Jim_ListLength(interp, argv[1]);
17626         if (len != 2 && len != 3) {
17627             Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
17628             return JIM_ERR;
17629         }
17630 
17631         if (len == 3) {
17632 #ifdef jim_ext_namespace
17633 
17634             nsObj = JimQualifyNameObj(interp, Jim_ListGetIndex(interp, argv[1], 2));
17635 #else
17636             Jim_SetResultString(interp, "namespaces not enabled", -1);
17637             return JIM_ERR;
17638 #endif
17639         }
17640         argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
17641         bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
17642 
17643         cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
17644 
17645         if (cmd) {
17646 
17647             nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
17648             nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
17649             Jim_IncrRefCount(nargv[0]);
17650             memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
17651             ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
17652             Jim_DecrRefCount(interp, nargv[0]);
17653             Jim_Free(nargv);
17654 
17655             JimDecrCmdRefCount(interp, cmd);
17656             return ret;
17657         }
17658         return JIM_ERR;
17659     }
17660 }
17661 
17662 
17663 
Jim_ConcatCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17664 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17665 {
17666     Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17667     return JIM_OK;
17668 }
17669 
17670 
Jim_UpvarCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17671 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17672 {
17673     int i;
17674     Jim_CallFrame *targetCallFrame;
17675 
17676 
17677     if (argc > 3 && (argc % 2 == 0)) {
17678         targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17679         argc--;
17680         argv++;
17681     }
17682     else {
17683         targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17684     }
17685     if (targetCallFrame == NULL) {
17686         return JIM_ERR;
17687     }
17688 
17689 
17690     if (argc < 3) {
17691         Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
17692         return JIM_ERR;
17693     }
17694 
17695 
17696     for (i = 1; i < argc; i += 2) {
17697         if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
17698             return JIM_ERR;
17699     }
17700     return JIM_OK;
17701 }
17702 
17703 
Jim_GlobalCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17704 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17705 {
17706     int i;
17707 
17708     if (argc < 2) {
17709         Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
17710         return JIM_ERR;
17711     }
17712 
17713     if (interp->framePtr->level == 0)
17714         return JIM_OK;
17715     for (i = 1; i < argc; i++) {
17716 
17717         const char *name = Jim_String(argv[i]);
17718         if (name[0] != ':' || name[1] != ':') {
17719             if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
17720                 return JIM_ERR;
17721         }
17722     }
17723     return JIM_OK;
17724 }
17725 
JimStringMap(Jim_Interp * interp,Jim_Obj * mapListObjPtr,Jim_Obj * objPtr,int nocase)17726 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
17727     Jim_Obj *objPtr, int nocase)
17728 {
17729     int numMaps;
17730     const char *str, *noMatchStart = NULL;
17731     int strLen, i;
17732     Jim_Obj *resultObjPtr;
17733 
17734     numMaps = Jim_ListLength(interp, mapListObjPtr);
17735     if (numMaps % 2) {
17736         Jim_SetResultString(interp, "list must contain an even number of elements", -1);
17737         return NULL;
17738     }
17739 
17740     str = Jim_String(objPtr);
17741     strLen = Jim_Utf8Length(interp, objPtr);
17742 
17743 
17744     resultObjPtr = Jim_NewStringObj(interp, "", 0);
17745     while (strLen) {
17746         for (i = 0; i < numMaps; i += 2) {
17747             Jim_Obj *eachObjPtr;
17748             const char *k;
17749             int kl;
17750 
17751             eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
17752             k = Jim_String(eachObjPtr);
17753             kl = Jim_Utf8Length(interp, eachObjPtr);
17754 
17755             if (strLen >= kl && kl) {
17756                 int rc;
17757                 rc = JimStringCompareLen(str, k, kl, nocase);
17758                 if (rc == 0) {
17759                     if (noMatchStart) {
17760                         Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17761                         noMatchStart = NULL;
17762                     }
17763                     Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
17764                     str += utf8_index(str, kl);
17765                     strLen -= kl;
17766                     break;
17767                 }
17768             }
17769         }
17770         if (i == numMaps) {
17771             int c;
17772             if (noMatchStart == NULL)
17773                 noMatchStart = str;
17774             str += utf8_tounicode(str, &c);
17775             strLen--;
17776         }
17777     }
17778     if (noMatchStart) {
17779         Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17780     }
17781     return resultObjPtr;
17782 }
17783 
17784 
Jim_StringCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)17785 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17786 {
17787     int len;
17788     int opt_case = 1;
17789     int option;
17790     static const char * const options[] = {
17791         "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace",
17792         "map", "repeat", "reverse", "index", "first", "last", "cat",
17793         "trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL
17794     };
17795     enum
17796     {
17797         OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_REPLACE,
17798         OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST, OPT_CAT,
17799         OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE
17800     };
17801     static const char * const nocase_options[] = {
17802         "-nocase", NULL
17803     };
17804     static const char * const nocase_length_options[] = {
17805         "-nocase", "-length", NULL
17806     };
17807 
17808     if (argc < 2) {
17809         Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
17810         return JIM_ERR;
17811     }
17812     if (Jim_GetEnum(interp, argv[1], options, &option, NULL,
17813             JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
17814         return Jim_CheckShowCommands(interp, argv[1], options);
17815 
17816     switch (option) {
17817         case OPT_LENGTH:
17818         case OPT_BYTELENGTH:
17819             if (argc != 3) {
17820                 Jim_WrongNumArgs(interp, 2, argv, "string");
17821                 return JIM_ERR;
17822             }
17823             if (option == OPT_LENGTH) {
17824                 len = Jim_Utf8Length(interp, argv[2]);
17825             }
17826             else {
17827                 len = Jim_Length(argv[2]);
17828             }
17829             Jim_SetResultInt(interp, len);
17830             return JIM_OK;
17831 
17832         case OPT_CAT:{
17833                 Jim_Obj *objPtr;
17834                 if (argc == 3) {
17835 
17836                     objPtr = argv[2];
17837                 }
17838                 else {
17839                     int i;
17840 
17841                     objPtr = Jim_NewStringObj(interp, "", 0);
17842 
17843                     for (i = 2; i < argc; i++) {
17844                         Jim_AppendObj(interp, objPtr, argv[i]);
17845                     }
17846                 }
17847                 Jim_SetResult(interp, objPtr);
17848                 return JIM_OK;
17849             }
17850 
17851         case OPT_COMPARE:
17852         case OPT_EQUAL:
17853             {
17854 
17855                 long opt_length = -1;
17856                 int n = argc - 4;
17857                 int i = 2;
17858                 while (n > 0) {
17859                     int subopt;
17860                     if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
17861                             JIM_ENUM_ABBREV) != JIM_OK) {
17862 badcompareargs:
17863                         Jim_WrongNumArgs(interp, 2, argv, "?-nocase? ?-length int? string1 string2");
17864                         return JIM_ERR;
17865                     }
17866                     if (subopt == 0) {
17867 
17868                         opt_case = 0;
17869                         n--;
17870                     }
17871                     else {
17872 
17873                         if (n < 2) {
17874                             goto badcompareargs;
17875                         }
17876                         if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
17877                             return JIM_ERR;
17878                         }
17879                         n -= 2;
17880                     }
17881                 }
17882                 if (n) {
17883                     goto badcompareargs;
17884                 }
17885                 argv += argc - 2;
17886                 if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
17887 
17888                     Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
17889                 }
17890                 else {
17891                     if (opt_length >= 0) {
17892                         n = JimStringCompareLen(Jim_String(argv[0]), Jim_String(argv[1]), opt_length, !opt_case);
17893                     }
17894                     else {
17895                         n = Jim_StringCompareObj(interp, argv[0], argv[1], !opt_case);
17896                     }
17897                     Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
17898                 }
17899                 return JIM_OK;
17900             }
17901 
17902         case OPT_MATCH:
17903             if (argc != 4 &&
17904                 (argc != 5 ||
17905                     Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17906                         JIM_ENUM_ABBREV) != JIM_OK)) {
17907                 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
17908                 return JIM_ERR;
17909             }
17910             if (opt_case == 0) {
17911                 argv++;
17912             }
17913             Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
17914             return JIM_OK;
17915 
17916         case OPT_MAP:{
17917                 Jim_Obj *objPtr;
17918 
17919                 if (argc != 4 &&
17920                     (argc != 5 ||
17921                         Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17922                             JIM_ENUM_ABBREV) != JIM_OK)) {
17923                     Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
17924                     return JIM_ERR;
17925                 }
17926 
17927                 if (opt_case == 0) {
17928                     argv++;
17929                 }
17930                 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
17931                 if (objPtr == NULL) {
17932                     return JIM_ERR;
17933                 }
17934                 Jim_SetResult(interp, objPtr);
17935                 return JIM_OK;
17936             }
17937 
17938         case OPT_RANGE:
17939         case OPT_BYTERANGE:{
17940                 Jim_Obj *objPtr;
17941 
17942                 if (argc != 5) {
17943                     Jim_WrongNumArgs(interp, 2, argv, "string first last");
17944                     return JIM_ERR;
17945                 }
17946                 if (option == OPT_RANGE) {
17947                     objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
17948                 }
17949                 else
17950                 {
17951                     objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
17952                 }
17953 
17954                 if (objPtr == NULL) {
17955                     return JIM_ERR;
17956                 }
17957                 Jim_SetResult(interp, objPtr);
17958                 return JIM_OK;
17959             }
17960 
17961         case OPT_REPLACE:{
17962                 Jim_Obj *objPtr;
17963 
17964                 if (argc != 5 && argc != 6) {
17965                     Jim_WrongNumArgs(interp, 2, argv, "string first last ?string?");
17966                     return JIM_ERR;
17967                 }
17968                 objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
17969                 if (objPtr == NULL) {
17970                     return JIM_ERR;
17971                 }
17972                 Jim_SetResult(interp, objPtr);
17973                 return JIM_OK;
17974             }
17975 
17976 
17977         case OPT_REPEAT:{
17978                 Jim_Obj *objPtr;
17979                 jim_wide count;
17980 
17981                 if (argc != 4) {
17982                     Jim_WrongNumArgs(interp, 2, argv, "string count");
17983                     return JIM_ERR;
17984                 }
17985                 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK) {
17986                     return JIM_ERR;
17987                 }
17988                 objPtr = Jim_NewStringObj(interp, "", 0);
17989                 if (count > 0) {
17990                     while (count--) {
17991                         Jim_AppendObj(interp, objPtr, argv[2]);
17992                     }
17993                 }
17994                 Jim_SetResult(interp, objPtr);
17995                 return JIM_OK;
17996             }
17997 
17998         case OPT_REVERSE:{
17999                 char *buf, *p;
18000                 const char *str;
18001                 int i;
18002 
18003                 if (argc != 3) {
18004                     Jim_WrongNumArgs(interp, 2, argv, "string");
18005                     return JIM_ERR;
18006                 }
18007 
18008                 str = Jim_GetString(argv[2], &len);
18009                 buf = Jim_Alloc(len + 1);
18010                 p = buf + len;
18011                 *p = 0;
18012                 for (i = 0; i < len; ) {
18013                     int c;
18014                     int l = utf8_tounicode(str, &c);
18015                     memcpy(p - l, str, l);
18016                     p -= l;
18017                     i += l;
18018                     str += l;
18019                 }
18020                 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
18021                 return JIM_OK;
18022             }
18023 
18024         case OPT_INDEX:{
18025                 int idx;
18026                 const char *str;
18027 
18028                 if (argc != 4) {
18029                     Jim_WrongNumArgs(interp, 2, argv, "string index");
18030                     return JIM_ERR;
18031                 }
18032                 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
18033                     return JIM_ERR;
18034                 }
18035                 str = Jim_String(argv[2]);
18036                 len = Jim_Utf8Length(interp, argv[2]);
18037                 if (idx != INT_MIN && idx != INT_MAX) {
18038                     idx = JimRelToAbsIndex(len, idx);
18039                 }
18040                 if (idx < 0 || idx >= len || str == NULL) {
18041                     Jim_SetResultString(interp, "", 0);
18042                 }
18043                 else if (len == Jim_Length(argv[2])) {
18044 
18045                     Jim_SetResultString(interp, str + idx, 1);
18046                 }
18047                 else {
18048                     int c;
18049                     int i = utf8_index(str, idx);
18050                     Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
18051                 }
18052                 return JIM_OK;
18053             }
18054 
18055         case OPT_FIRST:
18056         case OPT_LAST:{
18057                 int idx = 0, l1, l2;
18058                 const char *s1, *s2;
18059 
18060                 if (argc != 4 && argc != 5) {
18061                     Jim_WrongNumArgs(interp, 2, argv, "subString string ?index?");
18062                     return JIM_ERR;
18063                 }
18064                 s1 = Jim_String(argv[2]);
18065                 s2 = Jim_String(argv[3]);
18066                 l1 = Jim_Utf8Length(interp, argv[2]);
18067                 l2 = Jim_Utf8Length(interp, argv[3]);
18068                 if (argc == 5) {
18069                     if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
18070                         return JIM_ERR;
18071                     }
18072                     idx = JimRelToAbsIndex(l2, idx);
18073                 }
18074                 else if (option == OPT_LAST) {
18075                     idx = l2;
18076                 }
18077                 if (option == OPT_FIRST) {
18078                     Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
18079                 }
18080                 else {
18081 #ifdef JIM_UTF8
18082                     Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
18083 #else
18084                     Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
18085 #endif
18086                 }
18087                 return JIM_OK;
18088             }
18089 
18090         case OPT_TRIM:
18091         case OPT_TRIMLEFT:
18092         case OPT_TRIMRIGHT:{
18093                 Jim_Obj *trimchars;
18094 
18095                 if (argc != 3 && argc != 4) {
18096                     Jim_WrongNumArgs(interp, 2, argv, "string ?trimchars?");
18097                     return JIM_ERR;
18098                 }
18099                 trimchars = (argc == 4 ? argv[3] : NULL);
18100                 if (option == OPT_TRIM) {
18101                     Jim_SetResult(interp, JimStringTrim(interp, argv[2], trimchars));
18102                 }
18103                 else if (option == OPT_TRIMLEFT) {
18104                     Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], trimchars));
18105                 }
18106                 else if (option == OPT_TRIMRIGHT) {
18107                     Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], trimchars));
18108                 }
18109                 return JIM_OK;
18110             }
18111 
18112         case OPT_TOLOWER:
18113         case OPT_TOUPPER:
18114         case OPT_TOTITLE:
18115             if (argc != 3) {
18116                 Jim_WrongNumArgs(interp, 2, argv, "string");
18117                 return JIM_ERR;
18118             }
18119             if (option == OPT_TOLOWER) {
18120                 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
18121             }
18122             else if (option == OPT_TOUPPER) {
18123                 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
18124             }
18125             else {
18126                 Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
18127             }
18128             return JIM_OK;
18129 
18130         case OPT_IS:
18131             if (argc == 4 || (argc == 5 && Jim_CompareStringImmediate(interp, argv[3], "-strict"))) {
18132                 return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
18133             }
18134             Jim_WrongNumArgs(interp, 2, argv, "class ?-strict? str");
18135             return JIM_ERR;
18136     }
18137     return JIM_OK;
18138 }
18139 
18140 
Jim_TimeCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18141 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18142 {
18143     long i, count = 1;
18144     jim_wide start, elapsed;
18145     char buf[60];
18146     const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
18147 
18148     if (argc < 2) {
18149         Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
18150         return JIM_ERR;
18151     }
18152     if (argc == 3) {
18153         if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
18154             return JIM_ERR;
18155     }
18156     if (count < 0)
18157         return JIM_OK;
18158     i = count;
18159     start = JimClock();
18160     while (i-- > 0) {
18161         int retval;
18162 
18163         retval = Jim_EvalObj(interp, argv[1]);
18164         if (retval != JIM_OK) {
18165             return retval;
18166         }
18167     }
18168     elapsed = JimClock() - start;
18169     sprintf(buf, fmt, count == 0 ? 0 : elapsed / count);
18170     Jim_SetResultString(interp, buf, -1);
18171     return JIM_OK;
18172 }
18173 
18174 
Jim_ExitCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18175 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18176 {
18177     long exitCode = 0;
18178 
18179     if (argc > 2) {
18180         Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
18181         return JIM_ERR;
18182     }
18183     if (argc == 2) {
18184         if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
18185             return JIM_ERR;
18186     }
18187     interp->exitCode = exitCode;
18188     return JIM_EXIT;
18189 }
18190 
18191 
Jim_CatchCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18192 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18193 {
18194     int exitCode = 0;
18195     int i;
18196     int sig = 0;
18197 
18198 
18199     jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
18200     static const int max_ignore_code = sizeof(ignore_mask) * 8;
18201 
18202     Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
18203 
18204     for (i = 1; i < argc - 1; i++) {
18205         const char *arg = Jim_String(argv[i]);
18206         jim_wide option;
18207         int ignore;
18208 
18209 
18210         if (strcmp(arg, "--") == 0) {
18211             i++;
18212             break;
18213         }
18214         if (*arg != '-') {
18215             break;
18216         }
18217 
18218         if (strncmp(arg, "-no", 3) == 0) {
18219             arg += 3;
18220             ignore = 1;
18221         }
18222         else {
18223             arg++;
18224             ignore = 0;
18225         }
18226 
18227         if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
18228             option = -1;
18229         }
18230         if (option < 0) {
18231             option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
18232         }
18233         if (option < 0) {
18234             goto wrongargs;
18235         }
18236 
18237         if (ignore) {
18238             ignore_mask |= ((jim_wide)1 << option);
18239         }
18240         else {
18241             ignore_mask &= (~((jim_wide)1 << option));
18242         }
18243     }
18244 
18245     argc -= i;
18246     if (argc < 1 || argc > 3) {
18247       wrongargs:
18248         Jim_WrongNumArgs(interp, 1, argv,
18249             "?-?no?code ... --? script ?resultVarName? ?optionVarName?");
18250         return JIM_ERR;
18251     }
18252     argv += i;
18253 
18254     if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
18255         sig++;
18256     }
18257 
18258     interp->signal_level += sig;
18259     if (Jim_CheckSignal(interp)) {
18260 
18261         exitCode = JIM_SIGNAL;
18262     }
18263     else {
18264         exitCode = Jim_EvalObj(interp, argv[0]);
18265 
18266         interp->errorFlag = 0;
18267     }
18268     interp->signal_level -= sig;
18269 
18270 
18271     if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
18272 
18273         return exitCode;
18274     }
18275 
18276     if (sig && exitCode == JIM_SIGNAL) {
18277 
18278         if (interp->signal_set_result) {
18279             interp->signal_set_result(interp, interp->sigmask);
18280         }
18281         else {
18282             Jim_SetResultInt(interp, interp->sigmask);
18283         }
18284         interp->sigmask = 0;
18285     }
18286 
18287     if (argc >= 2) {
18288         if (Jim_SetVariable(interp, argv[1], Jim_GetResult(interp)) != JIM_OK) {
18289             return JIM_ERR;
18290         }
18291         if (argc == 3) {
18292             Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
18293 
18294             Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
18295             Jim_ListAppendElement(interp, optListObj,
18296                 Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
18297             Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
18298             Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
18299             if (exitCode == JIM_ERR) {
18300                 Jim_Obj *errorCode;
18301                 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
18302                     -1));
18303                 Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
18304 
18305                 errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
18306                 if (errorCode) {
18307                     Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
18308                     Jim_ListAppendElement(interp, optListObj, errorCode);
18309                 }
18310             }
18311             if (Jim_SetVariable(interp, argv[2], optListObj) != JIM_OK) {
18312                 return JIM_ERR;
18313             }
18314         }
18315     }
18316     Jim_SetResultInt(interp, exitCode);
18317     return JIM_OK;
18318 }
18319 
18320 
18321 
Jim_RenameCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18322 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18323 {
18324     if (argc != 3) {
18325         Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
18326         return JIM_ERR;
18327     }
18328 
18329     if (JimValidName(interp, "new procedure", argv[2])) {
18330         return JIM_ERR;
18331     }
18332 
18333     return Jim_RenameCommand(interp, Jim_String(argv[1]), Jim_String(argv[2]));
18334 }
18335 
18336 #define JIM_DICTMATCH_KEYS 0x0001
18337 #define JIM_DICTMATCH_VALUES 0x002
18338 
Jim_DictMatchTypes(Jim_Interp * interp,Jim_Obj * objPtr,Jim_Obj * patternObj,int match_type,int return_types)18339 int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
18340 {
18341     Jim_HashEntry *he;
18342     Jim_Obj *listObjPtr;
18343     Jim_HashTableIterator htiter;
18344 
18345     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18346         return JIM_ERR;
18347     }
18348 
18349     listObjPtr = Jim_NewListObj(interp, NULL, 0);
18350 
18351     JimInitHashTableIterator(objPtr->internalRep.ptr, &htiter);
18352     while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18353         if (patternObj) {
18354             Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? (Jim_Obj *)he->key : Jim_GetHashEntryVal(he);
18355             if (!JimGlobMatch(Jim_String(patternObj), Jim_String(matchObj), 0)) {
18356 
18357                 continue;
18358             }
18359         }
18360         if (return_types & JIM_DICTMATCH_KEYS) {
18361             Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key);
18362         }
18363         if (return_types & JIM_DICTMATCH_VALUES) {
18364             Jim_ListAppendElement(interp, listObjPtr, Jim_GetHashEntryVal(he));
18365         }
18366     }
18367 
18368     Jim_SetResult(interp, listObjPtr);
18369     return JIM_OK;
18370 }
18371 
Jim_DictSize(Jim_Interp * interp,Jim_Obj * objPtr)18372 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
18373 {
18374     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18375         return -1;
18376     }
18377     return ((Jim_HashTable *)objPtr->internalRep.ptr)->used;
18378 }
18379 
Jim_DictMerge(Jim_Interp * interp,int objc,Jim_Obj * const * objv)18380 Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
18381 {
18382     Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
18383     int i;
18384 
18385     JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));
18386 
18387 
18388 
18389     for (i = 0; i < objc; i++) {
18390         Jim_HashTable *ht;
18391         Jim_HashTableIterator htiter;
18392         Jim_HashEntry *he;
18393 
18394         if (SetDictFromAny(interp, objv[i]) != JIM_OK) {
18395             Jim_FreeNewObj(interp, objPtr);
18396             return NULL;
18397         }
18398         ht = objv[i]->internalRep.ptr;
18399         JimInitHashTableIterator(ht, &htiter);
18400         while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18401             Jim_ReplaceHashEntry(objPtr->internalRep.ptr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he));
18402         }
18403     }
18404     return objPtr;
18405 }
18406 
Jim_DictInfo(Jim_Interp * interp,Jim_Obj * objPtr)18407 int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
18408 {
18409     Jim_HashTable *ht;
18410     unsigned int i;
18411     char buffer[100];
18412     int sum = 0;
18413     int nonzero_count = 0;
18414     Jim_Obj *output;
18415     int bucket_counts[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
18416 
18417     if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18418         return JIM_ERR;
18419     }
18420 
18421     ht = (Jim_HashTable *)objPtr->internalRep.ptr;
18422 
18423 
18424     snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets\n", ht->used, ht->size);
18425     output = Jim_NewStringObj(interp, buffer, -1);
18426 
18427     for (i = 0; i < ht->size; i++) {
18428         Jim_HashEntry *he = ht->table[i];
18429         int entries = 0;
18430         while (he) {
18431             entries++;
18432             he = he->next;
18433         }
18434         if (entries > 9) {
18435             bucket_counts[10]++;
18436         }
18437         else {
18438             bucket_counts[entries]++;
18439         }
18440         if (entries) {
18441             sum += entries;
18442             nonzero_count++;
18443         }
18444     }
18445     for (i = 0; i < 10; i++) {
18446         snprintf(buffer, sizeof(buffer), "number of buckets with %d entries: %d\n", i, bucket_counts[i]);
18447         Jim_AppendString(interp, output, buffer, -1);
18448     }
18449     snprintf(buffer, sizeof(buffer), "number of buckets with 10 or more entries: %d\n", bucket_counts[10]);
18450     Jim_AppendString(interp, output, buffer, -1);
18451     snprintf(buffer, sizeof(buffer), "average search distance for entry: %.1f", nonzero_count ? (double)sum / nonzero_count : 0.0);
18452     Jim_AppendString(interp, output, buffer, -1);
18453     Jim_SetResult(interp, output);
18454     return JIM_OK;
18455 }
18456 
Jim_EvalEnsemble(Jim_Interp * interp,const char * basecmd,const char * subcmd,int argc,Jim_Obj * const * argv)18457 static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
18458 {
18459     Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
18460 
18461     Jim_AppendString(interp, prefixObj, " ", 1);
18462     Jim_AppendString(interp, prefixObj, subcmd, -1);
18463 
18464     return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
18465 }
18466 
JimDictWith(Jim_Interp * interp,Jim_Obj * dictVarName,Jim_Obj * const * keyv,int keyc,Jim_Obj * scriptObj)18467 static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj)
18468 {
18469     int i;
18470     Jim_Obj *objPtr;
18471     Jim_Obj *dictObj;
18472     Jim_Obj **dictValues;
18473     int len;
18474     int ret = JIM_OK;
18475 
18476 
18477     dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
18478     if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
18479         return JIM_ERR;
18480     }
18481 
18482     if (Jim_DictPairs(interp, objPtr, &dictValues, &len) == JIM_ERR) {
18483         return JIM_ERR;
18484     }
18485     for (i = 0; i < len; i += 2) {
18486         if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
18487             Jim_Free(dictValues);
18488             return JIM_ERR;
18489         }
18490     }
18491 
18492 
18493     if (Jim_Length(scriptObj)) {
18494         ret = Jim_EvalObj(interp, scriptObj);
18495 
18496 
18497         if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {
18498 
18499             Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
18500             for (i = 0; i < keyc; i++) {
18501                 newkeyv[i] = keyv[i];
18502             }
18503 
18504             for (i = 0; i < len; i += 2) {
18505 
18506                 objPtr = Jim_GetVariable(interp, dictValues[i], 0);
18507                 newkeyv[keyc] = dictValues[i];
18508                 Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, 0);
18509             }
18510             Jim_Free(newkeyv);
18511         }
18512     }
18513 
18514     Jim_Free(dictValues);
18515 
18516     return ret;
18517 }
18518 
18519 
Jim_DictCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18520 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18521 {
18522     Jim_Obj *objPtr;
18523     int types = JIM_DICTMATCH_KEYS;
18524     int option;
18525     static const char * const options[] = {
18526         "create", "get", "set", "unset", "exists", "keys", "size", "info",
18527         "merge", "with", "append", "lappend", "incr", "remove", "values", "for",
18528         "replace", "update", NULL
18529     };
18530     enum
18531     {
18532         OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXISTS, OPT_KEYS, OPT_SIZE, OPT_INFO,
18533         OPT_MERGE, OPT_WITH, OPT_APPEND, OPT_LAPPEND, OPT_INCR, OPT_REMOVE, OPT_VALUES, OPT_FOR,
18534         OPT_REPLACE, OPT_UPDATE,
18535     };
18536 
18537     if (argc < 2) {
18538         Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arguments ...?");
18539         return JIM_ERR;
18540     }
18541 
18542     if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) {
18543         return Jim_CheckShowCommands(interp, argv[1], options);
18544     }
18545 
18546     switch (option) {
18547         case OPT_GET:
18548             if (argc < 3) {
18549                 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?key ...?");
18550                 return JIM_ERR;
18551             }
18552             if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
18553                     JIM_ERRMSG) != JIM_OK) {
18554                 return JIM_ERR;
18555             }
18556             Jim_SetResult(interp, objPtr);
18557             return JIM_OK;
18558 
18559         case OPT_SET:
18560             if (argc < 5) {
18561                 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
18562                 return JIM_ERR;
18563             }
18564             return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
18565 
18566         case OPT_EXISTS:
18567             if (argc < 4) {
18568                 Jim_WrongNumArgs(interp, 2, argv, "dictionary key ?key ...?");
18569                 return JIM_ERR;
18570             }
18571             else {
18572                 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_ERRMSG);
18573                 if (rc < 0) {
18574                     return JIM_ERR;
18575                 }
18576                 Jim_SetResultBool(interp,  rc == JIM_OK);
18577                 return JIM_OK;
18578             }
18579 
18580         case OPT_UNSET:
18581             if (argc < 4) {
18582                 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
18583                 return JIM_ERR;
18584             }
18585             if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, 0) != JIM_OK) {
18586                 return JIM_ERR;
18587             }
18588             return JIM_OK;
18589 
18590         case OPT_VALUES:
18591             types = JIM_DICTMATCH_VALUES;
18592 
18593         case OPT_KEYS:
18594             if (argc != 3 && argc != 4) {
18595                 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?pattern?");
18596                 return JIM_ERR;
18597             }
18598             return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);
18599 
18600         case OPT_SIZE:
18601             if (argc != 3) {
18602                 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18603                 return JIM_ERR;
18604             }
18605             else if (Jim_DictSize(interp, argv[2]) < 0) {
18606                 return JIM_ERR;
18607             }
18608             Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
18609             return JIM_OK;
18610 
18611         case OPT_MERGE:
18612             if (argc == 2) {
18613                 return JIM_OK;
18614             }
18615             objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
18616             if (objPtr == NULL) {
18617                 return JIM_ERR;
18618             }
18619             Jim_SetResult(interp, objPtr);
18620             return JIM_OK;
18621 
18622         case OPT_UPDATE:
18623             if (argc < 6 || argc % 2) {
18624 
18625                 argc = 2;
18626             }
18627             break;
18628 
18629         case OPT_CREATE:
18630             if (argc % 2) {
18631                 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
18632                 return JIM_ERR;
18633             }
18634             objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
18635             Jim_SetResult(interp, objPtr);
18636             return JIM_OK;
18637 
18638         case OPT_INFO:
18639             if (argc != 3) {
18640                 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18641                 return JIM_ERR;
18642             }
18643             return Jim_DictInfo(interp, argv[2]);
18644 
18645         case OPT_WITH:
18646             if (argc < 4) {
18647                 Jim_WrongNumArgs(interp, 2, argv, "dictVar ?key ...? script");
18648                 return JIM_ERR;
18649             }
18650             return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
18651     }
18652 
18653     return Jim_EvalEnsemble(interp, "dict", options[option], argc - 2, argv + 2);
18654 }
18655 
18656 
Jim_SubstCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18657 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18658 {
18659     static const char * const options[] = {
18660         "-nobackslashes", "-nocommands", "-novariables", NULL
18661     };
18662     enum
18663     { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
18664     int i;
18665     int flags = JIM_SUBST_FLAG;
18666     Jim_Obj *objPtr;
18667 
18668     if (argc < 2) {
18669         Jim_WrongNumArgs(interp, 1, argv, "?options? string");
18670         return JIM_ERR;
18671     }
18672     for (i = 1; i < (argc - 1); i++) {
18673         int option;
18674 
18675         if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
18676                 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18677             return JIM_ERR;
18678         }
18679         switch (option) {
18680             case OPT_NOBACKSLASHES:
18681                 flags |= JIM_SUBST_NOESC;
18682                 break;
18683             case OPT_NOCOMMANDS:
18684                 flags |= JIM_SUBST_NOCMD;
18685                 break;
18686             case OPT_NOVARIABLES:
18687                 flags |= JIM_SUBST_NOVAR;
18688                 break;
18689         }
18690     }
18691     if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
18692         return JIM_ERR;
18693     }
18694     Jim_SetResult(interp, objPtr);
18695     return JIM_OK;
18696 }
18697 
18698 
Jim_InfoCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18699 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18700 {
18701     int cmd;
18702     Jim_Obj *objPtr;
18703     int mode = 0;
18704 
18705     static const char * const commands[] = {
18706         "body", "statics", "commands", "procs", "channels", "exists", "globals", "level", "frame", "locals",
18707         "vars", "version", "patchlevel", "complete", "args", "hostname",
18708         "script", "source", "stacktrace", "nameofexecutable", "returncodes",
18709         "references", "alias", NULL
18710     };
18711     enum
18712     { INFO_BODY, INFO_STATICS, INFO_COMMANDS, INFO_PROCS, INFO_CHANNELS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
18713         INFO_FRAME, INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_PATCHLEVEL, INFO_COMPLETE, INFO_ARGS,
18714         INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE,
18715         INFO_RETURNCODES, INFO_REFERENCES, INFO_ALIAS,
18716     };
18717 
18718 #ifdef jim_ext_namespace
18719     int nons = 0;
18720 
18721     if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
18722 
18723         argc--;
18724         argv++;
18725         nons = 1;
18726     }
18727 #endif
18728 
18729     if (argc < 2) {
18730         Jim_WrongNumArgs(interp, 1, argv, "subcommand ?args ...?");
18731         return JIM_ERR;
18732     }
18733     if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18734         return Jim_CheckShowCommands(interp, argv[1], commands);
18735     }
18736 
18737 
18738     switch (cmd) {
18739         case INFO_EXISTS:
18740             if (argc != 3) {
18741                 Jim_WrongNumArgs(interp, 2, argv, "varName");
18742                 return JIM_ERR;
18743             }
18744             Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
18745             break;
18746 
18747         case INFO_ALIAS:{
18748             Jim_Cmd *cmdPtr;
18749 
18750             if (argc != 3) {
18751                 Jim_WrongNumArgs(interp, 2, argv, "command");
18752                 return JIM_ERR;
18753             }
18754             if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18755                 return JIM_ERR;
18756             }
18757             if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
18758                 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
18759                 return JIM_ERR;
18760             }
18761             Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
18762             return JIM_OK;
18763         }
18764 
18765         case INFO_CHANNELS:
18766             mode++;
18767 #ifndef jim_ext_aio
18768             Jim_SetResultString(interp, "aio not enabled", -1);
18769             return JIM_ERR;
18770 #endif
18771 
18772         case INFO_PROCS:
18773             mode++;
18774 
18775         case INFO_COMMANDS:
18776 
18777             if (argc != 2 && argc != 3) {
18778                 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18779                 return JIM_ERR;
18780             }
18781 #ifdef jim_ext_namespace
18782             if (!nons) {
18783                 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18784                     return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18785                 }
18786             }
18787 #endif
18788             Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
18789             break;
18790 
18791         case INFO_VARS:
18792             mode++;
18793 
18794         case INFO_LOCALS:
18795             mode++;
18796 
18797         case INFO_GLOBALS:
18798 
18799             if (argc != 2 && argc != 3) {
18800                 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18801                 return JIM_ERR;
18802             }
18803 #ifdef jim_ext_namespace
18804             if (!nons) {
18805                 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18806                     return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18807                 }
18808             }
18809 #endif
18810             Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
18811             break;
18812 
18813         case INFO_SCRIPT:
18814             if (argc != 2) {
18815                 Jim_WrongNumArgs(interp, 2, argv, "");
18816                 return JIM_ERR;
18817             }
18818             Jim_SetResult(interp, JimGetScript(interp, interp->currentScriptObj)->fileNameObj);
18819             break;
18820 
18821         case INFO_SOURCE:{
18822                 jim_wide line;
18823                 Jim_Obj *resObjPtr;
18824                 Jim_Obj *fileNameObj;
18825 
18826                 if (argc != 3 && argc != 5) {
18827                     Jim_WrongNumArgs(interp, 2, argv, "source ?filename line?");
18828                     return JIM_ERR;
18829                 }
18830                 if (argc == 5) {
18831                     if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
18832                         return JIM_ERR;
18833                     }
18834                     resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
18835                     JimSetSourceInfo(interp, resObjPtr, argv[3], line);
18836                 }
18837                 else {
18838                     if (argv[2]->typePtr == &sourceObjType) {
18839                         fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj;
18840                         line = argv[2]->internalRep.sourceValue.lineNumber;
18841                     }
18842                     else if (argv[2]->typePtr == &scriptObjType) {
18843                         ScriptObj *script = JimGetScript(interp, argv[2]);
18844                         fileNameObj = script->fileNameObj;
18845                         line = script->firstline;
18846                     }
18847                     else {
18848                         fileNameObj = interp->emptyObj;
18849                         line = 1;
18850                     }
18851                     resObjPtr = Jim_NewListObj(interp, NULL, 0);
18852                     Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
18853                     Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
18854                 }
18855                 Jim_SetResult(interp, resObjPtr);
18856                 break;
18857             }
18858 
18859         case INFO_STACKTRACE:
18860             Jim_SetResult(interp, interp->stackTrace);
18861             break;
18862 
18863         case INFO_LEVEL:
18864         case INFO_FRAME:
18865             switch (argc) {
18866                 case 2:
18867                     Jim_SetResultInt(interp, interp->framePtr->level);
18868                     break;
18869 
18870                 case 3:
18871                     if (JimInfoLevel(interp, argv[2], &objPtr, cmd == INFO_LEVEL) != JIM_OK) {
18872                         return JIM_ERR;
18873                     }
18874                     Jim_SetResult(interp, objPtr);
18875                     break;
18876 
18877                 default:
18878                     Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
18879                     return JIM_ERR;
18880             }
18881             break;
18882 
18883         case INFO_BODY:
18884         case INFO_STATICS:
18885         case INFO_ARGS:{
18886                 Jim_Cmd *cmdPtr;
18887 
18888                 if (argc != 3) {
18889                     Jim_WrongNumArgs(interp, 2, argv, "procname");
18890                     return JIM_ERR;
18891                 }
18892                 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18893                     return JIM_ERR;
18894                 }
18895                 if (!cmdPtr->isproc) {
18896                     Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
18897                     return JIM_ERR;
18898                 }
18899                 switch (cmd) {
18900                     case INFO_BODY:
18901                         Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
18902                         break;
18903                     case INFO_ARGS:
18904                         Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
18905                         break;
18906                     case INFO_STATICS:
18907                         if (cmdPtr->u.proc.staticVars) {
18908                             Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
18909                                 NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES));
18910                         }
18911                         break;
18912                 }
18913                 break;
18914             }
18915 
18916         case INFO_VERSION:
18917         case INFO_PATCHLEVEL:{
18918                 char buf[(JIM_INTEGER_SPACE * 2) + 1];
18919 
18920                 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
18921                 Jim_SetResultString(interp, buf, -1);
18922                 break;
18923             }
18924 
18925         case INFO_COMPLETE:
18926             if (argc != 3 && argc != 4) {
18927                 Jim_WrongNumArgs(interp, 2, argv, "script ?missing?");
18928                 return JIM_ERR;
18929             }
18930             else {
18931                 char missing;
18932 
18933                 Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing));
18934                 if (missing != ' ' && argc == 4) {
18935                     Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
18936                 }
18937             }
18938             break;
18939 
18940         case INFO_HOSTNAME:
18941 
18942             return Jim_Eval(interp, "os.gethostname");
18943 
18944         case INFO_NAMEOFEXECUTABLE:
18945 
18946             return Jim_Eval(interp, "{info nameofexecutable}");
18947 
18948         case INFO_RETURNCODES:
18949             if (argc == 2) {
18950                 int i;
18951                 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
18952 
18953                 for (i = 0; jimReturnCodes[i]; i++) {
18954                     Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
18955                     Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
18956                             jimReturnCodes[i], -1));
18957                 }
18958 
18959                 Jim_SetResult(interp, listObjPtr);
18960             }
18961             else if (argc == 3) {
18962                 long code;
18963                 const char *name;
18964 
18965                 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
18966                     return JIM_ERR;
18967                 }
18968                 name = Jim_ReturnCode(code);
18969                 if (*name == '?') {
18970                     Jim_SetResultInt(interp, code);
18971                 }
18972                 else {
18973                     Jim_SetResultString(interp, name, -1);
18974                 }
18975             }
18976             else {
18977                 Jim_WrongNumArgs(interp, 2, argv, "?code?");
18978                 return JIM_ERR;
18979             }
18980             break;
18981         case INFO_REFERENCES:
18982 #ifdef JIM_REFERENCES
18983             return JimInfoReferences(interp, argc, argv);
18984 #else
18985             Jim_SetResultString(interp, "not supported", -1);
18986             return JIM_ERR;
18987 #endif
18988     }
18989     return JIM_OK;
18990 }
18991 
18992 
Jim_ExistsCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)18993 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18994 {
18995     Jim_Obj *objPtr;
18996     int result = 0;
18997 
18998     static const char * const options[] = {
18999         "-command", "-proc", "-alias", "-var", NULL
19000     };
19001     enum
19002     {
19003         OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
19004     };
19005     int option;
19006 
19007     if (argc == 2) {
19008         option = OPT_VAR;
19009         objPtr = argv[1];
19010     }
19011     else if (argc == 3) {
19012         if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
19013             return JIM_ERR;
19014         }
19015         objPtr = argv[2];
19016     }
19017     else {
19018         Jim_WrongNumArgs(interp, 1, argv, "?option? name");
19019         return JIM_ERR;
19020     }
19021 
19022     if (option == OPT_VAR) {
19023         result = Jim_GetVariable(interp, objPtr, 0) != NULL;
19024     }
19025     else {
19026 
19027         Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
19028 
19029         if (cmd) {
19030             switch (option) {
19031             case OPT_COMMAND:
19032                 result = 1;
19033                 break;
19034 
19035             case OPT_ALIAS:
19036                 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
19037                 break;
19038 
19039             case OPT_PROC:
19040                 result = cmd->isproc;
19041                 break;
19042             }
19043         }
19044     }
19045     Jim_SetResultBool(interp, result);
19046     return JIM_OK;
19047 }
19048 
19049 
Jim_SplitCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19050 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19051 {
19052     const char *str, *splitChars, *noMatchStart;
19053     int splitLen, strLen;
19054     Jim_Obj *resObjPtr;
19055     int c;
19056     int len;
19057 
19058     if (argc != 2 && argc != 3) {
19059         Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
19060         return JIM_ERR;
19061     }
19062 
19063     str = Jim_GetString(argv[1], &len);
19064     if (len == 0) {
19065         return JIM_OK;
19066     }
19067     strLen = Jim_Utf8Length(interp, argv[1]);
19068 
19069 
19070     if (argc == 2) {
19071         splitChars = " \n\t\r";
19072         splitLen = 4;
19073     }
19074     else {
19075         splitChars = Jim_String(argv[2]);
19076         splitLen = Jim_Utf8Length(interp, argv[2]);
19077     }
19078 
19079     noMatchStart = str;
19080     resObjPtr = Jim_NewListObj(interp, NULL, 0);
19081 
19082 
19083     if (splitLen) {
19084         Jim_Obj *objPtr;
19085         while (strLen--) {
19086             const char *sc = splitChars;
19087             int scLen = splitLen;
19088             int sl = utf8_tounicode(str, &c);
19089             while (scLen--) {
19090                 int pc;
19091                 sc += utf8_tounicode(sc, &pc);
19092                 if (c == pc) {
19093                     objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
19094                     Jim_ListAppendElement(interp, resObjPtr, objPtr);
19095                     noMatchStart = str + sl;
19096                     break;
19097                 }
19098             }
19099             str += sl;
19100         }
19101         objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
19102         Jim_ListAppendElement(interp, resObjPtr, objPtr);
19103     }
19104     else {
19105         Jim_Obj **commonObj = NULL;
19106 #define NUM_COMMON (128 - 9)
19107         while (strLen--) {
19108             int n = utf8_tounicode(str, &c);
19109 #ifdef JIM_OPTIMIZATION
19110             if (c >= 9 && c < 128) {
19111 
19112                 c -= 9;
19113                 if (!commonObj) {
19114                     commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
19115                     memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
19116                 }
19117                 if (!commonObj[c]) {
19118                     commonObj[c] = Jim_NewStringObj(interp, str, 1);
19119                 }
19120                 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
19121                 str++;
19122                 continue;
19123             }
19124 #endif
19125             Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
19126             str += n;
19127         }
19128         Jim_Free(commonObj);
19129     }
19130 
19131     Jim_SetResult(interp, resObjPtr);
19132     return JIM_OK;
19133 }
19134 
19135 
Jim_JoinCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19136 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19137 {
19138     const char *joinStr;
19139     int joinStrLen;
19140 
19141     if (argc != 2 && argc != 3) {
19142         Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
19143         return JIM_ERR;
19144     }
19145 
19146     if (argc == 2) {
19147         joinStr = " ";
19148         joinStrLen = 1;
19149     }
19150     else {
19151         joinStr = Jim_GetString(argv[2], &joinStrLen);
19152     }
19153     Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
19154     return JIM_OK;
19155 }
19156 
19157 
Jim_FormatCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19158 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19159 {
19160     Jim_Obj *objPtr;
19161 
19162     if (argc < 2) {
19163         Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
19164         return JIM_ERR;
19165     }
19166     objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
19167     if (objPtr == NULL)
19168         return JIM_ERR;
19169     Jim_SetResult(interp, objPtr);
19170     return JIM_OK;
19171 }
19172 
19173 
Jim_ScanCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19174 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19175 {
19176     Jim_Obj *listPtr, **outVec;
19177     int outc, i;
19178 
19179     if (argc < 3) {
19180         Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
19181         return JIM_ERR;
19182     }
19183     if (argv[2]->typePtr != &scanFmtStringObjType)
19184         SetScanFmtFromAny(interp, argv[2]);
19185     if (FormatGetError(argv[2]) != 0) {
19186         Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
19187         return JIM_ERR;
19188     }
19189     if (argc > 3) {
19190         int maxPos = FormatGetMaxPos(argv[2]);
19191         int count = FormatGetCnvCount(argv[2]);
19192 
19193         if (maxPos > argc - 3) {
19194             Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
19195             return JIM_ERR;
19196         }
19197         else if (count > argc - 3) {
19198             Jim_SetResultString(interp, "different numbers of variable names and "
19199                 "field specifiers", -1);
19200             return JIM_ERR;
19201         }
19202         else if (count < argc - 3) {
19203             Jim_SetResultString(interp, "variable is not assigned by any "
19204                 "conversion specifiers", -1);
19205             return JIM_ERR;
19206         }
19207     }
19208     listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
19209     if (listPtr == 0)
19210         return JIM_ERR;
19211     if (argc > 3) {
19212         int rc = JIM_OK;
19213         int count = 0;
19214 
19215         if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
19216             int len = Jim_ListLength(interp, listPtr);
19217 
19218             if (len != 0) {
19219                 JimListGetElements(interp, listPtr, &outc, &outVec);
19220                 for (i = 0; i < outc; ++i) {
19221                     if (Jim_Length(outVec[i]) > 0) {
19222                         ++count;
19223                         if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
19224                             rc = JIM_ERR;
19225                         }
19226                     }
19227                 }
19228             }
19229             Jim_FreeNewObj(interp, listPtr);
19230         }
19231         else {
19232             count = -1;
19233         }
19234         if (rc == JIM_OK) {
19235             Jim_SetResultInt(interp, count);
19236         }
19237         return rc;
19238     }
19239     else {
19240         if (listPtr == (Jim_Obj *)EOF) {
19241             Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
19242             return JIM_OK;
19243         }
19244         Jim_SetResult(interp, listPtr);
19245     }
19246     return JIM_OK;
19247 }
19248 
19249 
Jim_ErrorCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19250 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19251 {
19252     if (argc != 2 && argc != 3) {
19253         Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
19254         return JIM_ERR;
19255     }
19256     Jim_SetResult(interp, argv[1]);
19257     if (argc == 3) {
19258         JimSetStackTrace(interp, argv[2]);
19259         return JIM_ERR;
19260     }
19261     interp->addStackTrace++;
19262     return JIM_ERR;
19263 }
19264 
19265 
Jim_LrangeCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19266 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19267 {
19268     Jim_Obj *objPtr;
19269 
19270     if (argc != 4) {
19271         Jim_WrongNumArgs(interp, 1, argv, "list first last");
19272         return JIM_ERR;
19273     }
19274     if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
19275         return JIM_ERR;
19276     Jim_SetResult(interp, objPtr);
19277     return JIM_OK;
19278 }
19279 
19280 
Jim_LrepeatCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19281 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19282 {
19283     Jim_Obj *objPtr;
19284     long count;
19285 
19286     if (argc < 2 || Jim_GetLong(interp, argv[1], &count) != JIM_OK || count < 0) {
19287         Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
19288         return JIM_ERR;
19289     }
19290 
19291     if (count == 0 || argc == 2) {
19292         return JIM_OK;
19293     }
19294 
19295     argc -= 2;
19296     argv += 2;
19297 
19298     objPtr = Jim_NewListObj(interp, argv, argc);
19299     while (--count) {
19300         ListInsertElements(objPtr, -1, argc, argv);
19301     }
19302 
19303     Jim_SetResult(interp, objPtr);
19304     return JIM_OK;
19305 }
19306 
Jim_GetEnviron(void)19307 char **Jim_GetEnviron(void)
19308 {
19309 #if defined(HAVE__NSGETENVIRON)
19310     return *_NSGetEnviron();
19311 #else
19312     #if !defined(NO_ENVIRON_EXTERN)
19313     extern char **environ;
19314     #endif
19315 
19316     return environ;
19317 #endif
19318 }
19319 
Jim_SetEnviron(char ** env)19320 void Jim_SetEnviron(char **env)
19321 {
19322 #if defined(HAVE__NSGETENVIRON)
19323     *_NSGetEnviron() = env;
19324 #else
19325     #if !defined(NO_ENVIRON_EXTERN)
19326     extern char **environ;
19327     #endif
19328 
19329     environ = env;
19330 #endif
19331 }
19332 
19333 
Jim_EnvCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19334 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19335 {
19336     const char *key;
19337     const char *val;
19338 
19339     if (argc == 1) {
19340         char **e = Jim_GetEnviron();
19341 
19342         int i;
19343         Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
19344 
19345         for (i = 0; e[i]; i++) {
19346             const char *equals = strchr(e[i], '=');
19347 
19348             if (equals) {
19349                 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
19350                         equals - e[i]));
19351                 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
19352             }
19353         }
19354 
19355         Jim_SetResult(interp, listObjPtr);
19356         return JIM_OK;
19357     }
19358 
19359     if (argc < 2) {
19360         Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
19361         return JIM_ERR;
19362     }
19363     key = Jim_String(argv[1]);
19364     val = getenv(key);
19365     if (val == NULL) {
19366         if (argc < 3) {
19367             Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
19368             return JIM_ERR;
19369         }
19370         val = Jim_String(argv[2]);
19371     }
19372     Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
19373     return JIM_OK;
19374 }
19375 
19376 
Jim_SourceCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19377 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19378 {
19379     int retval;
19380 
19381     if (argc != 2) {
19382         Jim_WrongNumArgs(interp, 1, argv, "fileName");
19383         return JIM_ERR;
19384     }
19385     retval = Jim_EvalFile(interp, Jim_String(argv[1]));
19386     if (retval == JIM_RETURN)
19387         return JIM_OK;
19388     return retval;
19389 }
19390 
19391 
Jim_LreverseCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19392 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19393 {
19394     Jim_Obj *revObjPtr, **ele;
19395     int len;
19396 
19397     if (argc != 2) {
19398         Jim_WrongNumArgs(interp, 1, argv, "list");
19399         return JIM_ERR;
19400     }
19401     JimListGetElements(interp, argv[1], &len, &ele);
19402     len--;
19403     revObjPtr = Jim_NewListObj(interp, NULL, 0);
19404     while (len >= 0)
19405         ListAppendElement(revObjPtr, ele[len--]);
19406     Jim_SetResult(interp, revObjPtr);
19407     return JIM_OK;
19408 }
19409 
JimRangeLen(jim_wide start,jim_wide end,jim_wide step)19410 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
19411 {
19412     jim_wide len;
19413 
19414     if (step == 0)
19415         return -1;
19416     if (start == end)
19417         return 0;
19418     else if (step > 0 && start > end)
19419         return -1;
19420     else if (step < 0 && end > start)
19421         return -1;
19422     len = end - start;
19423     if (len < 0)
19424         len = -len;
19425     if (step < 0)
19426         step = -step;
19427     len = 1 + ((len - 1) / step);
19428     if (len > INT_MAX)
19429         len = INT_MAX;
19430     return (int)((len < 0) ? -1 : len);
19431 }
19432 
19433 
Jim_RangeCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19434 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19435 {
19436     jim_wide start = 0, end, step = 1;
19437     int len, i;
19438     Jim_Obj *objPtr;
19439 
19440     if (argc < 2 || argc > 4) {
19441         Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
19442         return JIM_ERR;
19443     }
19444     if (argc == 2) {
19445         if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
19446             return JIM_ERR;
19447     }
19448     else {
19449         if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
19450             Jim_GetWide(interp, argv[2], &end) != JIM_OK)
19451             return JIM_ERR;
19452         if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
19453             return JIM_ERR;
19454     }
19455     if ((len = JimRangeLen(start, end, step)) == -1) {
19456         Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
19457         return JIM_ERR;
19458     }
19459     objPtr = Jim_NewListObj(interp, NULL, 0);
19460     for (i = 0; i < len; i++)
19461         ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
19462     Jim_SetResult(interp, objPtr);
19463     return JIM_OK;
19464 }
19465 
19466 
Jim_RandCoreCommand(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19467 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19468 {
19469     jim_wide min = 0, max = 0, len, maxMul;
19470 
19471     if (argc < 1 || argc > 3) {
19472         Jim_WrongNumArgs(interp, 1, argv, "?min? max");
19473         return JIM_ERR;
19474     }
19475     if (argc == 1) {
19476         max = JIM_WIDE_MAX;
19477     } else if (argc == 2) {
19478         if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
19479             return JIM_ERR;
19480     } else if (argc == 3) {
19481         if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
19482             Jim_GetWide(interp, argv[2], &max) != JIM_OK)
19483             return JIM_ERR;
19484     }
19485     len = max-min;
19486     if (len < 0) {
19487         Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
19488         return JIM_ERR;
19489     }
19490     maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
19491     while (1) {
19492         jim_wide r;
19493 
19494         JimRandomBytes(interp, &r, sizeof(jim_wide));
19495         if (r < 0 || r >= maxMul) continue;
19496         r = (len == 0) ? 0 : r%len;
19497         Jim_SetResultInt(interp, min+r);
19498         return JIM_OK;
19499     }
19500 }
19501 
19502 static const struct {
19503     const char *name;
19504     Jim_CmdProc *cmdProc;
19505 } Jim_CoreCommandsTable[] = {
19506     {"alias", Jim_AliasCoreCommand},
19507     {"set", Jim_SetCoreCommand},
19508     {"unset", Jim_UnsetCoreCommand},
19509     {"puts", Jim_PutsCoreCommand},
19510     {"+", Jim_AddCoreCommand},
19511     {"*", Jim_MulCoreCommand},
19512     {"-", Jim_SubCoreCommand},
19513     {"/", Jim_DivCoreCommand},
19514     {"incr", Jim_IncrCoreCommand},
19515     {"while", Jim_WhileCoreCommand},
19516     {"loop", Jim_LoopCoreCommand},
19517     {"for", Jim_ForCoreCommand},
19518     {"foreach", Jim_ForeachCoreCommand},
19519     {"lmap", Jim_LmapCoreCommand},
19520     {"lassign", Jim_LassignCoreCommand},
19521     {"if", Jim_IfCoreCommand},
19522     {"switch", Jim_SwitchCoreCommand},
19523     {"list", Jim_ListCoreCommand},
19524     {"lindex", Jim_LindexCoreCommand},
19525     {"lset", Jim_LsetCoreCommand},
19526     {"lsearch", Jim_LsearchCoreCommand},
19527     {"llength", Jim_LlengthCoreCommand},
19528     {"lappend", Jim_LappendCoreCommand},
19529     {"linsert", Jim_LinsertCoreCommand},
19530     {"lreplace", Jim_LreplaceCoreCommand},
19531     {"lsort", Jim_LsortCoreCommand},
19532     {"append", Jim_AppendCoreCommand},
19533     {"debug", Jim_DebugCoreCommand},
19534     {"eval", Jim_EvalCoreCommand},
19535     {"uplevel", Jim_UplevelCoreCommand},
19536     {"expr", Jim_ExprCoreCommand},
19537     {"break", Jim_BreakCoreCommand},
19538     {"continue", Jim_ContinueCoreCommand},
19539     {"proc", Jim_ProcCoreCommand},
19540     {"concat", Jim_ConcatCoreCommand},
19541     {"return", Jim_ReturnCoreCommand},
19542     {"upvar", Jim_UpvarCoreCommand},
19543     {"global", Jim_GlobalCoreCommand},
19544     {"string", Jim_StringCoreCommand},
19545     {"time", Jim_TimeCoreCommand},
19546     {"exit", Jim_ExitCoreCommand},
19547     {"catch", Jim_CatchCoreCommand},
19548 #ifdef JIM_REFERENCES
19549     {"ref", Jim_RefCoreCommand},
19550     {"getref", Jim_GetrefCoreCommand},
19551     {"setref", Jim_SetrefCoreCommand},
19552     {"finalize", Jim_FinalizeCoreCommand},
19553     {"collect", Jim_CollectCoreCommand},
19554 #endif
19555     {"rename", Jim_RenameCoreCommand},
19556     {"dict", Jim_DictCoreCommand},
19557     {"subst", Jim_SubstCoreCommand},
19558     {"info", Jim_InfoCoreCommand},
19559     {"exists", Jim_ExistsCoreCommand},
19560     {"split", Jim_SplitCoreCommand},
19561     {"join", Jim_JoinCoreCommand},
19562     {"format", Jim_FormatCoreCommand},
19563     {"scan", Jim_ScanCoreCommand},
19564     {"error", Jim_ErrorCoreCommand},
19565     {"lrange", Jim_LrangeCoreCommand},
19566     {"lrepeat", Jim_LrepeatCoreCommand},
19567     {"env", Jim_EnvCoreCommand},
19568     {"source", Jim_SourceCoreCommand},
19569     {"lreverse", Jim_LreverseCoreCommand},
19570     {"range", Jim_RangeCoreCommand},
19571     {"rand", Jim_RandCoreCommand},
19572     {"tailcall", Jim_TailcallCoreCommand},
19573     {"local", Jim_LocalCoreCommand},
19574     {"upcall", Jim_UpcallCoreCommand},
19575     {"apply", Jim_ApplyCoreCommand},
19576     {NULL, NULL},
19577 };
19578 
Jim_RegisterCoreCommands(Jim_Interp * interp)19579 void Jim_RegisterCoreCommands(Jim_Interp *interp)
19580 {
19581     int i = 0;
19582 
19583     while (Jim_CoreCommandsTable[i].name != NULL) {
19584         Jim_CreateCommand(interp,
19585             Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
19586         i++;
19587     }
19588 }
19589 
Jim_MakeErrorMessage(Jim_Interp * interp)19590 void Jim_MakeErrorMessage(Jim_Interp *interp)
19591 {
19592     Jim_Obj *argv[2];
19593 
19594     argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
19595     argv[1] = interp->result;
19596 
19597     Jim_EvalObjVector(interp, 2, argv);
19598 }
19599 
JimSortStringTable(const char * const * tablePtr)19600 static char **JimSortStringTable(const char *const *tablePtr)
19601 {
19602     int count;
19603     char **tablePtrSorted;
19604 
19605 
19606     for (count = 0; tablePtr[count]; count++) {
19607     }
19608 
19609 
19610     tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1));
19611     memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
19612     qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
19613     tablePtrSorted[count] = NULL;
19614 
19615     return tablePtrSorted;
19616 }
19617 
JimSetFailedEnumResult(Jim_Interp * interp,const char * arg,const char * badtype,const char * prefix,const char * const * tablePtr,const char * name)19618 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
19619     const char *prefix, const char *const *tablePtr, const char *name)
19620 {
19621     char **tablePtrSorted;
19622     int i;
19623 
19624     if (name == NULL) {
19625         name = "option";
19626     }
19627 
19628     Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
19629     tablePtrSorted = JimSortStringTable(tablePtr);
19630     for (i = 0; tablePtrSorted[i]; i++) {
19631         if (tablePtrSorted[i + 1] == NULL && i > 0) {
19632             Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
19633         }
19634         Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
19635         if (tablePtrSorted[i + 1]) {
19636             Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
19637         }
19638     }
19639     Jim_Free(tablePtrSorted);
19640 }
19641 
19642 
Jim_CheckShowCommands(Jim_Interp * interp,Jim_Obj * objPtr,const char * const * tablePtr)19643 int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr)
19644 {
19645     if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) {
19646         int i;
19647         char **tablePtrSorted = JimSortStringTable(tablePtr);
19648         Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
19649         for (i = 0; tablePtrSorted[i]; i++) {
19650             Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1));
19651         }
19652         Jim_Free(tablePtrSorted);
19653         return JIM_OK;
19654     }
19655     return JIM_ERR;
19656 }
19657 
Jim_GetEnum(Jim_Interp * interp,Jim_Obj * objPtr,const char * const * tablePtr,int * indexPtr,const char * name,int flags)19658 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
19659     const char *const *tablePtr, int *indexPtr, const char *name, int flags)
19660 {
19661     const char *bad = "bad ";
19662     const char *const *entryPtr = NULL;
19663     int i;
19664     int match = -1;
19665     int arglen;
19666     const char *arg = Jim_GetString(objPtr, &arglen);
19667 
19668     *indexPtr = -1;
19669 
19670     for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
19671         if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
19672 
19673             *indexPtr = i;
19674             return JIM_OK;
19675         }
19676         if (flags & JIM_ENUM_ABBREV) {
19677             if (strncmp(arg, *entryPtr, arglen) == 0) {
19678                 if (*arg == '-' && arglen == 1) {
19679                     break;
19680                 }
19681                 if (match >= 0) {
19682                     bad = "ambiguous ";
19683                     goto ambiguous;
19684                 }
19685                 match = i;
19686             }
19687         }
19688     }
19689 
19690 
19691     if (match >= 0) {
19692         *indexPtr = match;
19693         return JIM_OK;
19694     }
19695 
19696   ambiguous:
19697     if (flags & JIM_ERRMSG) {
19698         JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
19699     }
19700     return JIM_ERR;
19701 }
19702 
Jim_FindByName(const char * name,const char * const array[],size_t len)19703 int Jim_FindByName(const char *name, const char * const array[], size_t len)
19704 {
19705     int i;
19706 
19707     for (i = 0; i < (int)len; i++) {
19708         if (array[i] && strcmp(array[i], name) == 0) {
19709             return i;
19710         }
19711     }
19712     return -1;
19713 }
19714 
Jim_IsDict(Jim_Obj * objPtr)19715 int Jim_IsDict(Jim_Obj *objPtr)
19716 {
19717     return objPtr->typePtr == &dictObjType;
19718 }
19719 
Jim_IsList(Jim_Obj * objPtr)19720 int Jim_IsList(Jim_Obj *objPtr)
19721 {
19722     return objPtr->typePtr == &listObjType;
19723 }
19724 
Jim_SetResultFormatted(Jim_Interp * interp,const char * format,...)19725 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
19726 {
19727 
19728     int len = strlen(format);
19729     int extra = 0;
19730     int n = 0;
19731     const char *params[5];
19732     int nobjparam = 0;
19733     Jim_Obj *objparam[5];
19734     char *buf;
19735     va_list args;
19736     int i;
19737 
19738     va_start(args, format);
19739 
19740     for (i = 0; i < len && n < 5; i++) {
19741         int l;
19742 
19743         if (strncmp(format + i, "%s", 2) == 0) {
19744             params[n] = va_arg(args, char *);
19745 
19746             l = strlen(params[n]);
19747         }
19748         else if (strncmp(format + i, "%#s", 3) == 0) {
19749             Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
19750 
19751             params[n] = Jim_GetString(objPtr, &l);
19752             objparam[nobjparam++] = objPtr;
19753             Jim_IncrRefCount(objPtr);
19754         }
19755         else {
19756             if (format[i] == '%') {
19757                 i++;
19758             }
19759             continue;
19760         }
19761         n++;
19762         extra += l;
19763     }
19764 
19765     len += extra;
19766     buf = Jim_Alloc(len + 1);
19767     len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
19768 
19769     va_end(args);
19770 
19771     Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
19772 
19773     for (i = 0; i < nobjparam; i++) {
19774         Jim_DecrRefCount(interp, objparam[i]);
19775     }
19776 }
19777 
19778 
19779 #ifndef jim_ext_package
Jim_PackageProvide(Jim_Interp * interp,const char * name,const char * ver,int flags)19780 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
19781 {
19782     return JIM_OK;
19783 }
19784 #endif
19785 #ifndef jim_ext_aio
Jim_AioFilehandle(Jim_Interp * interp,Jim_Obj * fhObj)19786 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
19787 {
19788     Jim_SetResultString(interp, "aio not enabled", -1);
19789     return NULL;
19790 }
19791 #endif
19792 
19793 
19794 #include <stdio.h>
19795 #include <string.h>
19796 
19797 
subcmd_null(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19798 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19799 {
19800 
19801     return JIM_OK;
19802 }
19803 
19804 static const jim_subcmd_type dummy_subcmd = {
19805     "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
19806 };
19807 
add_commands(Jim_Interp * interp,const jim_subcmd_type * ct,const char * sep)19808 static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
19809 {
19810     const char *s = "";
19811 
19812     for (; ct->cmd; ct++) {
19813         if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
19814             Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, NULL);
19815             s = sep;
19816         }
19817     }
19818 }
19819 
bad_subcmd(Jim_Interp * interp,const jim_subcmd_type * command_table,const char * type,Jim_Obj * cmd,Jim_Obj * subcmd)19820 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
19821     Jim_Obj *cmd, Jim_Obj *subcmd)
19822 {
19823     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19824     Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), ", ", type,
19825         " command \"", Jim_String(subcmd), "\": should be ", NULL);
19826     add_commands(interp, command_table, ", ");
19827 }
19828 
show_cmd_usage(Jim_Interp * interp,const jim_subcmd_type * command_table,int argc,Jim_Obj * const * argv)19829 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
19830     Jim_Obj *const *argv)
19831 {
19832     Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19833     Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]),
19834         " command ... \", where command is one of: ", NULL);
19835     add_commands(interp, command_table, ", ");
19836 }
19837 
add_cmd_usage(Jim_Interp * interp,const jim_subcmd_type * ct,Jim_Obj * cmd)19838 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
19839 {
19840     if (cmd) {
19841         Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
19842     }
19843     Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
19844     if (ct->args && *ct->args) {
19845         Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
19846     }
19847 }
19848 
set_wrong_args(Jim_Interp * interp,const jim_subcmd_type * command_table,Jim_Obj * subcmd)19849 static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd)
19850 {
19851     Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19852     add_cmd_usage(interp, command_table, subcmd);
19853     Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19854 }
19855 
Jim_ParseSubCmd(Jim_Interp * interp,const jim_subcmd_type * command_table,int argc,Jim_Obj * const * argv)19856 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
19857     int argc, Jim_Obj *const *argv)
19858 {
19859     const jim_subcmd_type *ct;
19860     const jim_subcmd_type *partial = 0;
19861     int cmdlen;
19862     Jim_Obj *cmd;
19863     const char *cmdstr;
19864     const char *cmdname;
19865     int help = 0;
19866 
19867     cmdname = Jim_String(argv[0]);
19868 
19869     if (argc < 2) {
19870         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19871         Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname,
19872             " command ...\"\n", NULL);
19873         Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help ?command?\" for help", NULL);
19874         return 0;
19875     }
19876 
19877     cmd = argv[1];
19878 
19879 
19880     if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
19881         if (argc == 2) {
19882 
19883             show_cmd_usage(interp, command_table, argc, argv);
19884             return &dummy_subcmd;
19885         }
19886         help = 1;
19887 
19888 
19889         cmd = argv[2];
19890     }
19891 
19892 
19893     if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
19894 
19895         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19896         add_commands(interp, command_table, " ");
19897         return &dummy_subcmd;
19898     }
19899 
19900     cmdstr = Jim_GetString(cmd, &cmdlen);
19901 
19902     for (ct = command_table; ct->cmd; ct++) {
19903         if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
19904 
19905             break;
19906         }
19907         if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
19908             if (partial) {
19909 
19910                 if (help) {
19911 
19912                     show_cmd_usage(interp, command_table, argc, argv);
19913                     return &dummy_subcmd;
19914                 }
19915                 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
19916                 return 0;
19917             }
19918             partial = ct;
19919         }
19920         continue;
19921     }
19922 
19923 
19924     if (partial && !ct->cmd) {
19925         ct = partial;
19926     }
19927 
19928     if (!ct->cmd) {
19929 
19930         if (help) {
19931 
19932             show_cmd_usage(interp, command_table, argc, argv);
19933             return &dummy_subcmd;
19934         }
19935         bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
19936         return 0;
19937     }
19938 
19939     if (help) {
19940         Jim_SetResultString(interp, "Usage: ", -1);
19941 
19942         add_cmd_usage(interp, ct, argv[0]);
19943         return &dummy_subcmd;
19944     }
19945 
19946 
19947     if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) {
19948         Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19949 
19950         add_cmd_usage(interp, ct, argv[0]);
19951         Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19952 
19953         return 0;
19954     }
19955 
19956 
19957     return ct;
19958 }
19959 
Jim_CallSubCmd(Jim_Interp * interp,const jim_subcmd_type * ct,int argc,Jim_Obj * const * argv)19960 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
19961 {
19962     int ret = JIM_ERR;
19963 
19964     if (ct) {
19965         if (ct->flags & JIM_MODFLAG_FULLARGV) {
19966             ret = ct->function(interp, argc, argv);
19967         }
19968         else {
19969             ret = ct->function(interp, argc - 2, argv + 2);
19970         }
19971         if (ret < 0) {
19972             set_wrong_args(interp, ct, argv[0]);
19973             ret = JIM_ERR;
19974         }
19975     }
19976     return ret;
19977 }
19978 
Jim_SubCmdProc(Jim_Interp * interp,int argc,Jim_Obj * const * argv)19979 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19980 {
19981     const jim_subcmd_type *ct =
19982         Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
19983 
19984     return Jim_CallSubCmd(interp, ct, argc, argv);
19985 }
19986 
19987 #include <ctype.h>
19988 #include <stdlib.h>
19989 #include <string.h>
19990 #include <stdio.h>
19991 #include <assert.h>
19992 
19993 
utf8_fromunicode(char * p,unsigned uc)19994 int utf8_fromunicode(char *p, unsigned uc)
19995 {
19996     if (uc <= 0x7f) {
19997         *p = uc;
19998         return 1;
19999     }
20000     else if (uc <= 0x7ff) {
20001         *p++ = 0xc0 | ((uc & 0x7c0) >> 6);
20002         *p = 0x80 | (uc & 0x3f);
20003         return 2;
20004     }
20005     else if (uc <= 0xffff) {
20006         *p++ = 0xe0 | ((uc & 0xf000) >> 12);
20007         *p++ = 0x80 | ((uc & 0xfc0) >> 6);
20008         *p = 0x80 | (uc & 0x3f);
20009         return 3;
20010     }
20011 
20012     else {
20013         *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
20014         *p++ = 0x80 | ((uc & 0x3f000) >> 12);
20015         *p++ = 0x80 | ((uc & 0xfc0) >> 6);
20016         *p = 0x80 | (uc & 0x3f);
20017         return 4;
20018     }
20019 }
20020 
20021 #include <ctype.h>
20022 #include <string.h>
20023 
20024 
20025 #define JIM_INTEGER_SPACE 24
20026 #define MAX_FLOAT_WIDTH 320
20027 
Jim_FormatString(Jim_Interp * interp,Jim_Obj * fmtObjPtr,int objc,Jim_Obj * const * objv)20028 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
20029 {
20030     const char *span, *format, *formatEnd, *msg;
20031     int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
20032     static const char * const mixedXPG =
20033             "cannot mix \"%\" and \"%n$\" conversion specifiers";
20034     static const char * const badIndex[2] = {
20035         "not enough arguments for all format specifiers",
20036         "\"%n$\" argument index out of range"
20037     };
20038     int formatLen;
20039     Jim_Obj *resultPtr;
20040 
20041     char *num_buffer = NULL;
20042     int num_buffer_size = 0;
20043 
20044     span = format = Jim_GetString(fmtObjPtr, &formatLen);
20045     formatEnd = format + formatLen;
20046     resultPtr = Jim_NewEmptyStringObj(interp);
20047 
20048     while (format != formatEnd) {
20049         char *end;
20050         int gotMinus, sawFlag;
20051         int gotPrecision, useShort;
20052         long width, precision;
20053         int newXpg;
20054         int ch;
20055         int step;
20056         int doubleType;
20057         char pad = ' ';
20058         char spec[2*JIM_INTEGER_SPACE + 12];
20059         char *p;
20060 
20061         int formatted_chars;
20062         int formatted_bytes;
20063         const char *formatted_buf;
20064 
20065         step = utf8_tounicode(format, &ch);
20066         format += step;
20067         if (ch != '%') {
20068             numBytes += step;
20069             continue;
20070         }
20071         if (numBytes) {
20072             Jim_AppendString(interp, resultPtr, span, numBytes);
20073             numBytes = 0;
20074         }
20075 
20076 
20077         step = utf8_tounicode(format, &ch);
20078         if (ch == '%') {
20079             span = format;
20080             numBytes = step;
20081             format += step;
20082             continue;
20083         }
20084 
20085 
20086         newXpg = 0;
20087         if (isdigit(ch)) {
20088             int position = strtoul(format, &end, 10);
20089             if (*end == '$') {
20090                 newXpg = 1;
20091                 objIndex = position - 1;
20092                 format = end + 1;
20093                 step = utf8_tounicode(format, &ch);
20094             }
20095         }
20096         if (newXpg) {
20097             if (gotSequential) {
20098                 msg = mixedXPG;
20099                 goto errorMsg;
20100             }
20101             gotXpg = 1;
20102         } else {
20103             if (gotXpg) {
20104                 msg = mixedXPG;
20105                 goto errorMsg;
20106             }
20107             gotSequential = 1;
20108         }
20109         if ((objIndex < 0) || (objIndex >= objc)) {
20110             msg = badIndex[gotXpg];
20111             goto errorMsg;
20112         }
20113 
20114         p = spec;
20115         *p++ = '%';
20116 
20117         gotMinus = 0;
20118         sawFlag = 1;
20119         do {
20120             switch (ch) {
20121             case '-':
20122                 gotMinus = 1;
20123                 break;
20124             case '0':
20125                 pad = ch;
20126                 break;
20127             case ' ':
20128             case '+':
20129             case '#':
20130                 break;
20131             default:
20132                 sawFlag = 0;
20133                 continue;
20134             }
20135             *p++ = ch;
20136             format += step;
20137             step = utf8_tounicode(format, &ch);
20138         } while (sawFlag);
20139 
20140 
20141         width = 0;
20142         if (isdigit(ch)) {
20143             width = strtoul(format, &end, 10);
20144             format = end;
20145             step = utf8_tounicode(format, &ch);
20146         } else if (ch == '*') {
20147             if (objIndex >= objc - 1) {
20148                 msg = badIndex[gotXpg];
20149                 goto errorMsg;
20150             }
20151             if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) {
20152                 goto error;
20153             }
20154             if (width < 0) {
20155                 width = -width;
20156                 if (!gotMinus) {
20157                     *p++ = '-';
20158                     gotMinus = 1;
20159                 }
20160             }
20161             objIndex++;
20162             format += step;
20163             step = utf8_tounicode(format, &ch);
20164         }
20165 
20166 
20167         gotPrecision = precision = 0;
20168         if (ch == '.') {
20169             gotPrecision = 1;
20170             format += step;
20171             step = utf8_tounicode(format, &ch);
20172         }
20173         if (isdigit(ch)) {
20174             precision = strtoul(format, &end, 10);
20175             format = end;
20176             step = utf8_tounicode(format, &ch);
20177         } else if (ch == '*') {
20178             if (objIndex >= objc - 1) {
20179                 msg = badIndex[gotXpg];
20180                 goto errorMsg;
20181             }
20182             if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) {
20183                 goto error;
20184             }
20185 
20186 
20187             if (precision < 0) {
20188                 precision = 0;
20189             }
20190             objIndex++;
20191             format += step;
20192             step = utf8_tounicode(format, &ch);
20193         }
20194 
20195 
20196         useShort = 0;
20197         if (ch == 'h') {
20198             useShort = 1;
20199             format += step;
20200             step = utf8_tounicode(format, &ch);
20201         } else if (ch == 'l') {
20202 
20203             format += step;
20204             step = utf8_tounicode(format, &ch);
20205             if (ch == 'l') {
20206                 format += step;
20207                 step = utf8_tounicode(format, &ch);
20208             }
20209         }
20210 
20211         format += step;
20212         span = format;
20213 
20214 
20215         if (ch == 'i') {
20216             ch = 'd';
20217         }
20218 
20219         doubleType = 0;
20220 
20221         switch (ch) {
20222         case '\0':
20223             msg = "format string ended in middle of field specifier";
20224             goto errorMsg;
20225         case 's': {
20226             formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
20227             formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
20228             if (gotPrecision && (precision < formatted_chars)) {
20229 
20230                 formatted_chars = precision;
20231                 formatted_bytes = utf8_index(formatted_buf, precision);
20232             }
20233             break;
20234         }
20235         case 'c': {
20236             jim_wide code;
20237 
20238             if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
20239                 goto error;
20240             }
20241 
20242             formatted_bytes = utf8_getchars(spec, code);
20243             formatted_buf = spec;
20244             formatted_chars = 1;
20245             break;
20246         }
20247         case 'b': {
20248                 unsigned jim_wide w;
20249                 int length;
20250                 int i;
20251                 int j;
20252 
20253                 if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
20254                     goto error;
20255                 }
20256                 length = sizeof(w) * 8;
20257 
20258 
20259 
20260                 if (num_buffer_size < length + 1) {
20261                     num_buffer_size = length + 1;
20262                     num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20263                 }
20264 
20265                 j = 0;
20266                 for (i = length; i > 0; ) {
20267                         i--;
20268                     if (w & ((unsigned jim_wide)1 << i)) {
20269                                 num_buffer[j++] = '1';
20270                         }
20271                         else if (j || i == 0) {
20272                                 num_buffer[j++] = '0';
20273                         }
20274                 }
20275                 num_buffer[j] = 0;
20276                 formatted_chars = formatted_bytes = j;
20277                 formatted_buf = num_buffer;
20278                 break;
20279         }
20280 
20281         case 'e':
20282         case 'E':
20283         case 'f':
20284         case 'g':
20285         case 'G':
20286             doubleType = 1;
20287 
20288         case 'd':
20289         case 'u':
20290         case 'o':
20291         case 'x':
20292         case 'X': {
20293             jim_wide w;
20294             double d;
20295             int length;
20296 
20297 
20298             if (width) {
20299                 p += sprintf(p, "%ld", width);
20300             }
20301             if (gotPrecision) {
20302                 p += sprintf(p, ".%ld", precision);
20303             }
20304 
20305 
20306             if (doubleType) {
20307                 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
20308                     goto error;
20309                 }
20310                 length = MAX_FLOAT_WIDTH;
20311             }
20312             else {
20313                 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) {
20314                     goto error;
20315                 }
20316                 length = JIM_INTEGER_SPACE;
20317                 if (useShort) {
20318                     if (ch == 'd') {
20319                         w = (short)w;
20320                     }
20321                     else {
20322                         w = (unsigned short)w;
20323                     }
20324                 }
20325                 *p++ = 'l';
20326 #ifdef HAVE_LONG_LONG
20327                 if (sizeof(long long) == sizeof(jim_wide)) {
20328                     *p++ = 'l';
20329                 }
20330 #endif
20331             }
20332 
20333             *p++ = (char) ch;
20334             *p = '\0';
20335 
20336 
20337             if (width > length) {
20338                 length = width;
20339             }
20340             if (gotPrecision) {
20341                 length += precision;
20342             }
20343 
20344 
20345             if (num_buffer_size < length + 1) {
20346                 num_buffer_size = length + 1;
20347                 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20348             }
20349 
20350             if (doubleType) {
20351                 snprintf(num_buffer, length + 1, spec, d);
20352             }
20353             else {
20354                 formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
20355             }
20356             formatted_chars = formatted_bytes = strlen(num_buffer);
20357             formatted_buf = num_buffer;
20358             break;
20359         }
20360 
20361         default: {
20362 
20363             spec[0] = ch;
20364             spec[1] = '\0';
20365             Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
20366             goto error;
20367         }
20368         }
20369 
20370         if (!gotMinus) {
20371             while (formatted_chars < width) {
20372                 Jim_AppendString(interp, resultPtr, &pad, 1);
20373                 formatted_chars++;
20374             }
20375         }
20376 
20377         Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes);
20378 
20379         while (formatted_chars < width) {
20380             Jim_AppendString(interp, resultPtr, &pad, 1);
20381             formatted_chars++;
20382         }
20383 
20384         objIndex += gotSequential;
20385     }
20386     if (numBytes) {
20387         Jim_AppendString(interp, resultPtr, span, numBytes);
20388     }
20389 
20390     Jim_Free(num_buffer);
20391     return resultPtr;
20392 
20393   errorMsg:
20394     Jim_SetResultString(interp, msg, -1);
20395   error:
20396     Jim_FreeNewObj(interp, resultPtr);
20397     Jim_Free(num_buffer);
20398     return NULL;
20399 }
20400 
20401 
20402 #if defined(JIM_REGEXP)
20403 #include <stdio.h>
20404 #include <ctype.h>
20405 #include <stdlib.h>
20406 #include <string.h>
20407 
20408 
20409 
20410 #define REG_MAX_PAREN 100
20411 
20412 
20413 
20414 #define	END	0
20415 #define	BOL	1
20416 #define	EOL	2
20417 #define	ANY	3
20418 #define	ANYOF	4
20419 #define	ANYBUT	5
20420 #define	BRANCH	6
20421 #define	BACK	7
20422 #define	EXACTLY	8
20423 #define	NOTHING	9
20424 #define	REP	10
20425 #define	REPMIN	11
20426 #define	REPX	12
20427 #define	REPXMIN	13
20428 #define	BOLX	14
20429 #define	EOLX	15
20430 #define	WORDA	16
20431 #define	WORDZ	17
20432 
20433 #define	OPENNC 	1000
20434 #define	OPEN   	1001
20435 
20436 
20437 
20438 
20439 #define	CLOSENC	2000
20440 #define	CLOSE	2001
20441 #define	CLOSE_END	(CLOSE+REG_MAX_PAREN)
20442 
20443 #define	REG_MAGIC	0xFADED00D
20444 
20445 
20446 #define	OP(preg, p)	(preg->program[p])
20447 #define	NEXT(preg, p)	(preg->program[p + 1])
20448 #define	OPERAND(p)	((p) + 2)
20449 
20450 
20451 
20452 
20453 #define	FAIL(R,M)	{ (R)->err = (M); return (M); }
20454 #define	ISMULT(c)	((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
20455 #define	META		"^$.[()|?{+*"
20456 
20457 #define	HASWIDTH	1
20458 #define	SIMPLE		2
20459 #define	SPSTART		4
20460 #define	WORST		0
20461 
20462 #define MAX_REP_COUNT 1000000
20463 
20464 static int reg(regex_t *preg, int paren, int *flagp );
20465 static int regpiece(regex_t *preg, int *flagp );
20466 static int regbranch(regex_t *preg, int *flagp );
20467 static int regatom(regex_t *preg, int *flagp );
20468 static int regnode(regex_t *preg, int op );
20469 static int regnext(regex_t *preg, int p );
20470 static void regc(regex_t *preg, int b );
20471 static int reginsert(regex_t *preg, int op, int size, int opnd );
20472 static void regtail(regex_t *preg, int p, int val);
20473 static void regoptail(regex_t *preg, int p, int val );
20474 static int regopsize(regex_t *preg, int p );
20475 
20476 static int reg_range_find(const int *string, int c);
20477 static const char *str_find(const char *string, int c, int nocase);
20478 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase);
20479 
20480 
20481 #ifdef DEBUG
20482 static int regnarrate = 0;
20483 static void regdump(regex_t *preg);
20484 static const char *regprop( int op );
20485 #endif
20486 
20487 
str_int_len(const int * seq)20488 static int str_int_len(const int *seq)
20489 {
20490 	int n = 0;
20491 	while (*seq++) {
20492 		n++;
20493 	}
20494 	return n;
20495 }
20496 
regcomp(regex_t * preg,const char * exp,int cflags)20497 int regcomp(regex_t *preg, const char *exp, int cflags)
20498 {
20499 	int scan;
20500 	int longest;
20501 	unsigned len;
20502 	int flags;
20503 
20504 #ifdef DEBUG
20505 	fprintf(stderr, "Compiling: '%s'\n", exp);
20506 #endif
20507 	memset(preg, 0, sizeof(*preg));
20508 
20509 	if (exp == NULL)
20510 		FAIL(preg, REG_ERR_NULL_ARGUMENT);
20511 
20512 
20513 	preg->cflags = cflags;
20514 	preg->regparse = exp;
20515 
20516 
20517 	preg->proglen = (strlen(exp) + 1) * 5;
20518 	preg->program = malloc(preg->proglen * sizeof(int));
20519 	if (preg->program == NULL)
20520 		FAIL(preg, REG_ERR_NOMEM);
20521 
20522 	regc(preg, REG_MAGIC);
20523 	if (reg(preg, 0, &flags) == 0) {
20524 		return preg->err;
20525 	}
20526 
20527 
20528 	if (preg->re_nsub >= REG_MAX_PAREN)
20529 		FAIL(preg,REG_ERR_TOO_BIG);
20530 
20531 
20532 	preg->regstart = 0;
20533 	preg->reganch = 0;
20534 	preg->regmust = 0;
20535 	preg->regmlen = 0;
20536 	scan = 1;
20537 	if (OP(preg, regnext(preg, scan)) == END) {
20538 		scan = OPERAND(scan);
20539 
20540 
20541 		if (OP(preg, scan) == EXACTLY) {
20542 			preg->regstart = preg->program[OPERAND(scan)];
20543 		}
20544 		else if (OP(preg, scan) == BOL)
20545 			preg->reganch++;
20546 
20547 		if (flags&SPSTART) {
20548 			longest = 0;
20549 			len = 0;
20550 			for (; scan != 0; scan = regnext(preg, scan)) {
20551 				if (OP(preg, scan) == EXACTLY) {
20552 					int plen = str_int_len(preg->program + OPERAND(scan));
20553 					if (plen >= len) {
20554 						longest = OPERAND(scan);
20555 						len = plen;
20556 					}
20557 				}
20558 			}
20559 			preg->regmust = longest;
20560 			preg->regmlen = len;
20561 		}
20562 	}
20563 
20564 #ifdef DEBUG
20565 	regdump(preg);
20566 #endif
20567 
20568 	return 0;
20569 }
20570 
reg(regex_t * preg,int paren,int * flagp)20571 static int reg(regex_t *preg, int paren, int *flagp )
20572 {
20573 	int ret;
20574 	int br;
20575 	int ender;
20576 	int parno = 0;
20577 	int flags;
20578 
20579 	*flagp = HASWIDTH;
20580 
20581 
20582 	if (paren) {
20583 		if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
20584 
20585 			preg->regparse += 2;
20586 			parno = -1;
20587 		}
20588 		else {
20589 			parno = ++preg->re_nsub;
20590 		}
20591 		ret = regnode(preg, OPEN+parno);
20592 	} else
20593 		ret = 0;
20594 
20595 
20596 	br = regbranch(preg, &flags);
20597 	if (br == 0)
20598 		return 0;
20599 	if (ret != 0)
20600 		regtail(preg, ret, br);
20601 	else
20602 		ret = br;
20603 	if (!(flags&HASWIDTH))
20604 		*flagp &= ~HASWIDTH;
20605 	*flagp |= flags&SPSTART;
20606 	while (*preg->regparse == '|') {
20607 		preg->regparse++;
20608 		br = regbranch(preg, &flags);
20609 		if (br == 0)
20610 			return 0;
20611 		regtail(preg, ret, br);
20612 		if (!(flags&HASWIDTH))
20613 			*flagp &= ~HASWIDTH;
20614 		*flagp |= flags&SPSTART;
20615 	}
20616 
20617 
20618 	ender = regnode(preg, (paren) ? CLOSE+parno : END);
20619 	regtail(preg, ret, ender);
20620 
20621 
20622 	for (br = ret; br != 0; br = regnext(preg, br))
20623 		regoptail(preg, br, ender);
20624 
20625 
20626 	if (paren && *preg->regparse++ != ')') {
20627 		preg->err = REG_ERR_UNMATCHED_PAREN;
20628 		return 0;
20629 	} else if (!paren && *preg->regparse != '\0') {
20630 		if (*preg->regparse == ')') {
20631 			preg->err = REG_ERR_UNMATCHED_PAREN;
20632 			return 0;
20633 		} else {
20634 			preg->err = REG_ERR_JUNK_ON_END;
20635 			return 0;
20636 		}
20637 	}
20638 
20639 	return(ret);
20640 }
20641 
regbranch(regex_t * preg,int * flagp)20642 static int regbranch(regex_t *preg, int *flagp )
20643 {
20644 	int ret;
20645 	int chain;
20646 	int latest;
20647 	int flags;
20648 
20649 	*flagp = WORST;
20650 
20651 	ret = regnode(preg, BRANCH);
20652 	chain = 0;
20653 	while (*preg->regparse != '\0' && *preg->regparse != ')' &&
20654 	       *preg->regparse != '|') {
20655 		latest = regpiece(preg, &flags);
20656 		if (latest == 0)
20657 			return 0;
20658 		*flagp |= flags&HASWIDTH;
20659 		if (chain == 0) {
20660 			*flagp |= flags&SPSTART;
20661 		}
20662 		else {
20663 			regtail(preg, chain, latest);
20664 		}
20665 		chain = latest;
20666 	}
20667 	if (chain == 0)
20668 		(void) regnode(preg, NOTHING);
20669 
20670 	return(ret);
20671 }
20672 
regpiece(regex_t * preg,int * flagp)20673 static int regpiece(regex_t *preg, int *flagp)
20674 {
20675 	int ret;
20676 	char op;
20677 	int next;
20678 	int flags;
20679 	int min;
20680 	int max;
20681 
20682 	ret = regatom(preg, &flags);
20683 	if (ret == 0)
20684 		return 0;
20685 
20686 	op = *preg->regparse;
20687 	if (!ISMULT(op)) {
20688 		*flagp = flags;
20689 		return(ret);
20690 	}
20691 
20692 	if (!(flags&HASWIDTH) && op != '?') {
20693 		preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
20694 		return 0;
20695 	}
20696 
20697 
20698 	if (op == '{') {
20699 		char *end;
20700 
20701 		min = strtoul(preg->regparse + 1, &end, 10);
20702 		if (end == preg->regparse + 1) {
20703 			preg->err = REG_ERR_BAD_COUNT;
20704 			return 0;
20705 		}
20706 		if (*end == '}') {
20707 			max = min;
20708 		}
20709 		else {
20710 			preg->regparse = end;
20711 			max = strtoul(preg->regparse + 1, &end, 10);
20712 			if (*end != '}') {
20713 				preg->err = REG_ERR_UNMATCHED_BRACES;
20714 				return 0;
20715 			}
20716 		}
20717 		if (end == preg->regparse + 1) {
20718 			max = MAX_REP_COUNT;
20719 		}
20720 		else if (max < min || max >= 100) {
20721 			preg->err = REG_ERR_BAD_COUNT;
20722 			return 0;
20723 		}
20724 		if (min >= 100) {
20725 			preg->err = REG_ERR_BAD_COUNT;
20726 			return 0;
20727 		}
20728 
20729 		preg->regparse = strchr(preg->regparse, '}');
20730 	}
20731 	else {
20732 		min = (op == '+');
20733 		max = (op == '?' ? 1 : MAX_REP_COUNT);
20734 	}
20735 
20736 	if (preg->regparse[1] == '?') {
20737 		preg->regparse++;
20738 		next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret);
20739 	}
20740 	else {
20741 		next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret);
20742 	}
20743 	preg->program[ret + 2] = max;
20744 	preg->program[ret + 3] = min;
20745 	preg->program[ret + 4] = 0;
20746 
20747 	*flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART);
20748 
20749 	if (!(flags & SIMPLE)) {
20750 		int back = regnode(preg, BACK);
20751 		regtail(preg, back, ret);
20752 		regtail(preg, next, back);
20753 	}
20754 
20755 	preg->regparse++;
20756 	if (ISMULT(*preg->regparse)) {
20757 		preg->err = REG_ERR_NESTED_COUNT;
20758 		return 0;
20759 	}
20760 
20761 	return ret;
20762 }
20763 
reg_addrange(regex_t * preg,int lower,int upper)20764 static void reg_addrange(regex_t *preg, int lower, int upper)
20765 {
20766 	if (lower > upper) {
20767 		reg_addrange(preg, upper, lower);
20768 	}
20769 
20770 	regc(preg, upper - lower + 1);
20771 	regc(preg, lower);
20772 }
20773 
reg_addrange_str(regex_t * preg,const char * str)20774 static void reg_addrange_str(regex_t *preg, const char *str)
20775 {
20776 	while (*str) {
20777 		reg_addrange(preg, *str, *str);
20778 		str++;
20779 	}
20780 }
20781 
reg_utf8_tounicode_case(const char * s,int * uc,int upper)20782 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper)
20783 {
20784 	int l = utf8_tounicode(s, uc);
20785 	if (upper) {
20786 		*uc = utf8_upper(*uc);
20787 	}
20788 	return l;
20789 }
20790 
hexdigitval(int c)20791 static int hexdigitval(int c)
20792 {
20793 	if (c >= '0' && c <= '9')
20794 		return c - '0';
20795 	if (c >= 'a' && c <= 'f')
20796 		return c - 'a' + 10;
20797 	if (c >= 'A' && c <= 'F')
20798 		return c - 'A' + 10;
20799 	return -1;
20800 }
20801 
parse_hex(const char * s,int n,int * uc)20802 static int parse_hex(const char *s, int n, int *uc)
20803 {
20804 	int val = 0;
20805 	int k;
20806 
20807 	for (k = 0; k < n; k++) {
20808 		int c = hexdigitval(*s++);
20809 		if (c == -1) {
20810 			break;
20811 		}
20812 		val = (val << 4) | c;
20813 	}
20814 	if (k) {
20815 		*uc = val;
20816 	}
20817 	return k;
20818 }
20819 
reg_decode_escape(const char * s,int * ch)20820 static int reg_decode_escape(const char *s, int *ch)
20821 {
20822 	int n;
20823 	const char *s0 = s;
20824 
20825 	*ch = *s++;
20826 
20827 	switch (*ch) {
20828 		case 'b': *ch = '\b'; break;
20829 		case 'e': *ch = 27; break;
20830 		case 'f': *ch = '\f'; break;
20831 		case 'n': *ch = '\n'; break;
20832 		case 'r': *ch = '\r'; break;
20833 		case 't': *ch = '\t'; break;
20834 		case 'v': *ch = '\v'; break;
20835 		case 'u':
20836 			if (*s == '{') {
20837 
20838 				n = parse_hex(s + 1, 6, ch);
20839 				if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
20840 					s += n + 2;
20841 				}
20842 				else {
20843 
20844 					*ch = 'u';
20845 				}
20846 			}
20847 			else if ((n = parse_hex(s, 4, ch)) > 0) {
20848 				s += n;
20849 			}
20850 			break;
20851 		case 'U':
20852 			if ((n = parse_hex(s, 8, ch)) > 0) {
20853 				s += n;
20854 			}
20855 			break;
20856 		case 'x':
20857 			if ((n = parse_hex(s, 2, ch)) > 0) {
20858 				s += n;
20859 			}
20860 			break;
20861 		case '\0':
20862 			s--;
20863 			*ch = '\\';
20864 			break;
20865 	}
20866 	return s - s0;
20867 }
20868 
regatom(regex_t * preg,int * flagp)20869 static int regatom(regex_t *preg, int *flagp)
20870 {
20871 	int ret;
20872 	int flags;
20873 	int nocase = (preg->cflags & REG_ICASE);
20874 
20875 	int ch;
20876 	int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);
20877 
20878 	*flagp = WORST;
20879 
20880 	preg->regparse += n;
20881 	switch (ch) {
20882 
20883 	case '^':
20884 		ret = regnode(preg, BOL);
20885 		break;
20886 	case '$':
20887 		ret = regnode(preg, EOL);
20888 		break;
20889 	case '.':
20890 		ret = regnode(preg, ANY);
20891 		*flagp |= HASWIDTH|SIMPLE;
20892 		break;
20893 	case '[': {
20894 			const char *pattern = preg->regparse;
20895 
20896 			if (*pattern == '^') {
20897 				ret = regnode(preg, ANYBUT);
20898 				pattern++;
20899 			} else
20900 				ret = regnode(preg, ANYOF);
20901 
20902 
20903 			if (*pattern == ']' || *pattern == '-') {
20904 				reg_addrange(preg, *pattern, *pattern);
20905 				pattern++;
20906 			}
20907 
20908 			while (*pattern && *pattern != ']') {
20909 
20910 				int start;
20911 				int end;
20912 
20913 				pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
20914 				if (start == '\\') {
20915 					pattern += reg_decode_escape(pattern, &start);
20916 					if (start == 0) {
20917 						preg->err = REG_ERR_NULL_CHAR;
20918 						return 0;
20919 					}
20920 				}
20921 				if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
20922 
20923 					pattern += utf8_tounicode(pattern, &end);
20924 					pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
20925 					if (end == '\\') {
20926 						pattern += reg_decode_escape(pattern, &end);
20927 						if (end == 0) {
20928 							preg->err = REG_ERR_NULL_CHAR;
20929 							return 0;
20930 						}
20931 					}
20932 
20933 					reg_addrange(preg, start, end);
20934 					continue;
20935 				}
20936 				if (start == '[' && pattern[0] == ':') {
20937 					static const char *character_class[] = {
20938 						":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
20939 						":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
20940 					};
20941 					enum {
20942 						CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
20943 						CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
20944 						CC_NUM
20945 					};
20946 					int i;
20947 
20948 					for (i = 0; i < CC_NUM; i++) {
20949 						n = strlen(character_class[i]);
20950 						if (strncmp(pattern, character_class[i], n) == 0) {
20951 
20952 							pattern += n + 1;
20953 							break;
20954 						}
20955 					}
20956 					if (i != CC_NUM) {
20957 						switch (i) {
20958 							case CC_ALNUM:
20959 								reg_addrange(preg, '0', '9');
20960 
20961 							case CC_ALPHA:
20962 								if ((preg->cflags & REG_ICASE) == 0) {
20963 									reg_addrange(preg, 'a', 'z');
20964 								}
20965 								reg_addrange(preg, 'A', 'Z');
20966 								break;
20967 							case CC_SPACE:
20968 								reg_addrange_str(preg, " \t\r\n\f\v");
20969 								break;
20970 							case CC_BLANK:
20971 								reg_addrange_str(preg, " \t");
20972 								break;
20973 							case CC_UPPER:
20974 								reg_addrange(preg, 'A', 'Z');
20975 								break;
20976 							case CC_LOWER:
20977 								reg_addrange(preg, 'a', 'z');
20978 								break;
20979 							case CC_XDIGIT:
20980 								reg_addrange(preg, 'a', 'f');
20981 								reg_addrange(preg, 'A', 'F');
20982 
20983 							case CC_DIGIT:
20984 								reg_addrange(preg, '0', '9');
20985 								break;
20986 							case CC_CNTRL:
20987 								reg_addrange(preg, 0, 31);
20988 								reg_addrange(preg, 127, 127);
20989 								break;
20990 							case CC_PRINT:
20991 								reg_addrange(preg, ' ', '~');
20992 								break;
20993 							case CC_GRAPH:
20994 								reg_addrange(preg, '!', '~');
20995 								break;
20996 							case CC_PUNCT:
20997 								reg_addrange(preg, '!', '/');
20998 								reg_addrange(preg, ':', '@');
20999 								reg_addrange(preg, '[', '`');
21000 								reg_addrange(preg, '{', '~');
21001 								break;
21002 						}
21003 						continue;
21004 					}
21005 				}
21006 
21007 				reg_addrange(preg, start, start);
21008 			}
21009 			regc(preg, '\0');
21010 
21011 			if (*pattern) {
21012 				pattern++;
21013 			}
21014 			preg->regparse = pattern;
21015 
21016 			*flagp |= HASWIDTH|SIMPLE;
21017 		}
21018 		break;
21019 	case '(':
21020 		ret = reg(preg, 1, &flags);
21021 		if (ret == 0)
21022 			return 0;
21023 		*flagp |= flags&(HASWIDTH|SPSTART);
21024 		break;
21025 	case '\0':
21026 	case '|':
21027 	case ')':
21028 		preg->err = REG_ERR_INTERNAL;
21029 		return 0;
21030 	case '?':
21031 	case '+':
21032 	case '*':
21033 	case '{':
21034 		preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
21035 		return 0;
21036 	case '\\':
21037 		ch = *preg->regparse++;
21038 		switch (ch) {
21039 		case '\0':
21040 			preg->err = REG_ERR_TRAILING_BACKSLASH;
21041 			return 0;
21042 		case 'A':
21043 			ret = regnode(preg, BOLX);
21044 			break;
21045 		case 'Z':
21046 			ret = regnode(preg, EOLX);
21047 			break;
21048 		case '<':
21049 		case 'm':
21050 			ret = regnode(preg, WORDA);
21051 			break;
21052 		case '>':
21053 		case 'M':
21054 			ret = regnode(preg, WORDZ);
21055 			break;
21056 		case 'd':
21057 		case 'D':
21058 			ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT);
21059 			reg_addrange(preg, '0', '9');
21060 			regc(preg, '\0');
21061 			*flagp |= HASWIDTH|SIMPLE;
21062 			break;
21063 		case 'w':
21064 		case 'W':
21065 			ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT);
21066 			if ((preg->cflags & REG_ICASE) == 0) {
21067 				reg_addrange(preg, 'a', 'z');
21068 			}
21069 			reg_addrange(preg, 'A', 'Z');
21070 			reg_addrange(preg, '0', '9');
21071 			reg_addrange(preg, '_', '_');
21072 			regc(preg, '\0');
21073 			*flagp |= HASWIDTH|SIMPLE;
21074 			break;
21075 		case 's':
21076 		case 'S':
21077 			ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT);
21078 			reg_addrange_str(preg," \t\r\n\f\v");
21079 			regc(preg, '\0');
21080 			*flagp |= HASWIDTH|SIMPLE;
21081 			break;
21082 
21083 		default:
21084 
21085 
21086 			preg->regparse--;
21087 			goto de_fault;
21088 		}
21089 		break;
21090 	de_fault:
21091 	default: {
21092 			int added = 0;
21093 
21094 
21095 			preg->regparse -= n;
21096 
21097 			ret = regnode(preg, EXACTLY);
21098 
21099 
21100 
21101 			while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
21102 				n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
21103 				if (ch == '\\' && preg->regparse[n]) {
21104 					if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) {
21105 
21106 						break;
21107 					}
21108 					n += reg_decode_escape(preg->regparse + n, &ch);
21109 					if (ch == 0) {
21110 						preg->err = REG_ERR_NULL_CHAR;
21111 						return 0;
21112 					}
21113 				}
21114 
21115 
21116 				if (ISMULT(preg->regparse[n])) {
21117 
21118 					if (added) {
21119 
21120 						break;
21121 					}
21122 
21123 					regc(preg, ch);
21124 					added++;
21125 					preg->regparse += n;
21126 					break;
21127 				}
21128 
21129 
21130 				regc(preg, ch);
21131 				added++;
21132 				preg->regparse += n;
21133 			}
21134 			regc(preg, '\0');
21135 
21136 			*flagp |= HASWIDTH;
21137 			if (added == 1)
21138 				*flagp |= SIMPLE;
21139 			break;
21140 		}
21141 		break;
21142 	}
21143 
21144 	return(ret);
21145 }
21146 
reg_grow(regex_t * preg,int n)21147 static void reg_grow(regex_t *preg, int n)
21148 {
21149 	if (preg->p + n >= preg->proglen) {
21150 		preg->proglen = (preg->p + n) * 2;
21151 		preg->program = realloc(preg->program, preg->proglen * sizeof(int));
21152 	}
21153 }
21154 
21155 
regnode(regex_t * preg,int op)21156 static int regnode(regex_t *preg, int op)
21157 {
21158 	reg_grow(preg, 2);
21159 
21160 
21161 	preg->program[preg->p++] = op;
21162 	preg->program[preg->p++] = 0;
21163 
21164 
21165 	return preg->p - 2;
21166 }
21167 
regc(regex_t * preg,int b)21168 static void regc(regex_t *preg, int b )
21169 {
21170 	reg_grow(preg, 1);
21171 	preg->program[preg->p++] = b;
21172 }
21173 
reginsert(regex_t * preg,int op,int size,int opnd)21174 static int reginsert(regex_t *preg, int op, int size, int opnd )
21175 {
21176 	reg_grow(preg, size);
21177 
21178 
21179 	memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
21180 
21181 	memset(preg->program + opnd, 0, sizeof(int) * size);
21182 
21183 	preg->program[opnd] = op;
21184 
21185 	preg->p += size;
21186 
21187 	return opnd + size;
21188 }
21189 
regtail(regex_t * preg,int p,int val)21190 static void regtail(regex_t *preg, int p, int val)
21191 {
21192 	int scan;
21193 	int temp;
21194 	int offset;
21195 
21196 
21197 	scan = p;
21198 	for (;;) {
21199 		temp = regnext(preg, scan);
21200 		if (temp == 0)
21201 			break;
21202 		scan = temp;
21203 	}
21204 
21205 	if (OP(preg, scan) == BACK)
21206 		offset = scan - val;
21207 	else
21208 		offset = val - scan;
21209 
21210 	preg->program[scan + 1] = offset;
21211 }
21212 
21213 
regoptail(regex_t * preg,int p,int val)21214 static void regoptail(regex_t *preg, int p, int val )
21215 {
21216 
21217 	if (p != 0 && OP(preg, p) == BRANCH) {
21218 		regtail(preg, OPERAND(p), val);
21219 	}
21220 }
21221 
21222 
21223 static int regtry(regex_t *preg, const char *string );
21224 static int regmatch(regex_t *preg, int prog);
21225 static int regrepeat(regex_t *preg, int p, int max);
21226 
regexec(regex_t * preg,const char * string,size_t nmatch,regmatch_t pmatch[],int eflags)21227 int regexec(regex_t  *preg,  const  char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
21228 {
21229 	const char *s;
21230 	int scan;
21231 
21232 
21233 	if (preg == NULL || preg->program == NULL || string == NULL) {
21234 		return REG_ERR_NULL_ARGUMENT;
21235 	}
21236 
21237 
21238 	if (*preg->program != REG_MAGIC) {
21239 		return REG_ERR_CORRUPTED;
21240 	}
21241 
21242 #ifdef DEBUG
21243 	fprintf(stderr, "regexec: %s\n", string);
21244 	regdump(preg);
21245 #endif
21246 
21247 	preg->eflags = eflags;
21248 	preg->pmatch = pmatch;
21249 	preg->nmatch = nmatch;
21250 	preg->start = string;
21251 
21252 
21253 	for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) {
21254 		int op = OP(preg, scan);
21255 		if (op == END)
21256 			break;
21257 		if (op == REPX || op == REPXMIN)
21258 			preg->program[scan + 4] = 0;
21259 	}
21260 
21261 
21262 	if (preg->regmust != 0) {
21263 		s = string;
21264 		while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
21265 			if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
21266 				break;
21267 			}
21268 			s++;
21269 		}
21270 		if (s == NULL)
21271 			return REG_NOMATCH;
21272 	}
21273 
21274 
21275 	preg->regbol = string;
21276 
21277 
21278 	if (preg->reganch) {
21279 		if (eflags & REG_NOTBOL) {
21280 
21281 			goto nextline;
21282 		}
21283 		while (1) {
21284 			if (regtry(preg, string)) {
21285 				return REG_NOERROR;
21286 			}
21287 			if (*string) {
21288 nextline:
21289 				if (preg->cflags & REG_NEWLINE) {
21290 
21291 					string = strchr(string, '\n');
21292 					if (string) {
21293 						preg->regbol = ++string;
21294 						continue;
21295 					}
21296 				}
21297 			}
21298 			return REG_NOMATCH;
21299 		}
21300 	}
21301 
21302 
21303 	s = string;
21304 	if (preg->regstart != '\0') {
21305 
21306 		while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
21307 			if (regtry(preg, s))
21308 				return REG_NOERROR;
21309 			s++;
21310 		}
21311 	}
21312 	else
21313 
21314 		while (1) {
21315 			if (regtry(preg, s))
21316 				return REG_NOERROR;
21317 			if (*s == '\0') {
21318 				break;
21319 			}
21320 			else {
21321 				int c;
21322 				s += utf8_tounicode(s, &c);
21323 			}
21324 		}
21325 
21326 
21327 	return REG_NOMATCH;
21328 }
21329 
21330 
regtry(regex_t * preg,const char * string)21331 static int regtry( regex_t *preg, const char *string )
21332 {
21333 	int i;
21334 
21335 	preg->reginput = string;
21336 
21337 	for (i = 0; i < preg->nmatch; i++) {
21338 		preg->pmatch[i].rm_so = -1;
21339 		preg->pmatch[i].rm_eo = -1;
21340 	}
21341 	if (regmatch(preg, 1)) {
21342 		preg->pmatch[0].rm_so = string - preg->start;
21343 		preg->pmatch[0].rm_eo = preg->reginput - preg->start;
21344 		return(1);
21345 	} else
21346 		return(0);
21347 }
21348 
prefix_cmp(const int * prog,int proglen,const char * string,int nocase)21349 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase)
21350 {
21351 	const char *s = string;
21352 	while (proglen && *s) {
21353 		int ch;
21354 		int n = reg_utf8_tounicode_case(s, &ch, nocase);
21355 		if (ch != *prog) {
21356 			return -1;
21357 		}
21358 		prog++;
21359 		s += n;
21360 		proglen--;
21361 	}
21362 	if (proglen == 0) {
21363 		return s - string;
21364 	}
21365 	return -1;
21366 }
21367 
reg_range_find(const int * range,int c)21368 static int reg_range_find(const int *range, int c)
21369 {
21370 	while (*range) {
21371 
21372 		if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
21373 			return 1;
21374 		}
21375 		range += 2;
21376 	}
21377 	return 0;
21378 }
21379 
str_find(const char * string,int c,int nocase)21380 static const char *str_find(const char *string, int c, int nocase)
21381 {
21382 	if (nocase) {
21383 
21384 		c = utf8_upper(c);
21385 	}
21386 	while (*string) {
21387 		int ch;
21388 		int n = reg_utf8_tounicode_case(string, &ch, nocase);
21389 		if (c == ch) {
21390 			return string;
21391 		}
21392 		string += n;
21393 	}
21394 	return NULL;
21395 }
21396 
reg_iseol(regex_t * preg,int ch)21397 static int reg_iseol(regex_t *preg, int ch)
21398 {
21399 	if (preg->cflags & REG_NEWLINE) {
21400 		return ch == '\0' || ch == '\n';
21401 	}
21402 	else {
21403 		return ch == '\0';
21404 	}
21405 }
21406 
regmatchsimplerepeat(regex_t * preg,int scan,int matchmin)21407 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin)
21408 {
21409 	int nextch = '\0';
21410 	const char *save;
21411 	int no;
21412 	int c;
21413 
21414 	int max = preg->program[scan + 2];
21415 	int min = preg->program[scan + 3];
21416 	int next = regnext(preg, scan);
21417 
21418 	if (OP(preg, next) == EXACTLY) {
21419 		nextch = preg->program[OPERAND(next)];
21420 	}
21421 	save = preg->reginput;
21422 	no = regrepeat(preg, scan + 5, max);
21423 	if (no < min) {
21424 		return 0;
21425 	}
21426 	if (matchmin) {
21427 
21428 		max = no;
21429 		no = min;
21430 	}
21431 
21432 	while (1) {
21433 		if (matchmin) {
21434 			if (no > max) {
21435 				break;
21436 			}
21437 		}
21438 		else {
21439 			if (no < min) {
21440 				break;
21441 			}
21442 		}
21443 		preg->reginput = save + utf8_index(save, no);
21444 		reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21445 
21446 		if (reg_iseol(preg, nextch) || c == nextch) {
21447 			if (regmatch(preg, next)) {
21448 				return(1);
21449 			}
21450 		}
21451 		if (matchmin) {
21452 
21453 			no++;
21454 		}
21455 		else {
21456 
21457 			no--;
21458 		}
21459 	}
21460 	return(0);
21461 }
21462 
regmatchrepeat(regex_t * preg,int scan,int matchmin)21463 static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
21464 {
21465 	int *scanpt = preg->program + scan;
21466 
21467 	int max = scanpt[2];
21468 	int min = scanpt[3];
21469 
21470 
21471 	if (scanpt[4] < min) {
21472 
21473 		scanpt[4]++;
21474 		if (regmatch(preg, scan + 5)) {
21475 			return 1;
21476 		}
21477 		scanpt[4]--;
21478 		return 0;
21479 	}
21480 	if (scanpt[4] > max) {
21481 		return 0;
21482 	}
21483 
21484 	if (matchmin) {
21485 
21486 		if (regmatch(preg, regnext(preg, scan))) {
21487 			return 1;
21488 		}
21489 
21490 		scanpt[4]++;
21491 		if (regmatch(preg, scan + 5)) {
21492 			return 1;
21493 		}
21494 		scanpt[4]--;
21495 		return 0;
21496 	}
21497 
21498 	if (scanpt[4] < max) {
21499 		scanpt[4]++;
21500 		if (regmatch(preg, scan + 5)) {
21501 			return 1;
21502 		}
21503 		scanpt[4]--;
21504 	}
21505 
21506 	return regmatch(preg, regnext(preg, scan));
21507 }
21508 
21509 
regmatch(regex_t * preg,int prog)21510 static int regmatch(regex_t *preg, int prog)
21511 {
21512 	int scan;
21513 	int next;
21514 	const char *save;
21515 
21516 	scan = prog;
21517 
21518 #ifdef DEBUG
21519 	if (scan != 0 && regnarrate)
21520 		fprintf(stderr, "%s(\n", regprop(scan));
21521 #endif
21522 	while (scan != 0) {
21523 		int n;
21524 		int c;
21525 #ifdef DEBUG
21526 		if (regnarrate) {
21527 			fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
21528 		}
21529 #endif
21530 		next = regnext(preg, scan);
21531 		n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21532 
21533 		switch (OP(preg, scan)) {
21534 		case BOLX:
21535 			if ((preg->eflags & REG_NOTBOL)) {
21536 				return(0);
21537 			}
21538 
21539 		case BOL:
21540 			if (preg->reginput != preg->regbol) {
21541 				return(0);
21542 			}
21543 			break;
21544 		case EOLX:
21545 			if (c != 0) {
21546 
21547 				return 0;
21548 			}
21549 			break;
21550 		case EOL:
21551 			if (!reg_iseol(preg, c)) {
21552 				return(0);
21553 			}
21554 			break;
21555 		case WORDA:
21556 
21557 			if ((!isalnum(UCHAR(c))) && c != '_')
21558 				return(0);
21559 
21560 			if (preg->reginput > preg->regbol &&
21561 				(isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
21562 				return(0);
21563 			break;
21564 		case WORDZ:
21565 
21566 			if (preg->reginput > preg->regbol) {
21567 
21568 				if (reg_iseol(preg, c) || !isalnum(UCHAR(c)) || c != '_') {
21569 					c = preg->reginput[-1];
21570 
21571 					if (isalnum(UCHAR(c)) || c == '_') {
21572 						break;
21573 					}
21574 				}
21575 			}
21576 
21577 			return(0);
21578 
21579 		case ANY:
21580 			if (reg_iseol(preg, c))
21581 				return 0;
21582 			preg->reginput += n;
21583 			break;
21584 		case EXACTLY: {
21585 				int opnd;
21586 				int len;
21587 				int slen;
21588 
21589 				opnd = OPERAND(scan);
21590 				len = str_int_len(preg->program + opnd);
21591 
21592 				slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE);
21593 				if (slen < 0) {
21594 					return(0);
21595 				}
21596 				preg->reginput += slen;
21597 			}
21598 			break;
21599 		case ANYOF:
21600 			if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) {
21601 				return(0);
21602 			}
21603 			preg->reginput += n;
21604 			break;
21605 		case ANYBUT:
21606 			if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) {
21607 				return(0);
21608 			}
21609 			preg->reginput += n;
21610 			break;
21611 		case NOTHING:
21612 			break;
21613 		case BACK:
21614 			break;
21615 		case BRANCH:
21616 			if (OP(preg, next) != BRANCH)
21617 				next = OPERAND(scan);
21618 			else {
21619 				do {
21620 					save = preg->reginput;
21621 					if (regmatch(preg, OPERAND(scan))) {
21622 						return(1);
21623 					}
21624 					preg->reginput = save;
21625 					scan = regnext(preg, scan);
21626 				} while (scan != 0 && OP(preg, scan) == BRANCH);
21627 				return(0);
21628 
21629 			}
21630 			break;
21631 		case REP:
21632 		case REPMIN:
21633 			return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);
21634 
21635 		case REPX:
21636 		case REPXMIN:
21637 			return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);
21638 
21639 		case END:
21640 			return 1;
21641 
21642 		case OPENNC:
21643 		case CLOSENC:
21644 			return regmatch(preg, next);
21645 
21646 		default:
21647 			if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
21648 				save = preg->reginput;
21649 				if (regmatch(preg, next)) {
21650 					if (OP(preg, scan) < CLOSE) {
21651 						int no = OP(preg, scan) - OPEN;
21652 						if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
21653 							preg->pmatch[no].rm_so = save - preg->start;
21654 						}
21655 					}
21656 					else {
21657 						int no = OP(preg, scan) - CLOSE;
21658 						if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
21659 							preg->pmatch[no].rm_eo = save - preg->start;
21660 						}
21661 					}
21662 					return(1);
21663 				}
21664 				return(0);
21665 			}
21666 			return REG_ERR_INTERNAL;
21667 		}
21668 
21669 		scan = next;
21670 	}
21671 
21672 	return REG_ERR_INTERNAL;
21673 }
21674 
regrepeat(regex_t * preg,int p,int max)21675 static int regrepeat(regex_t *preg, int p, int max)
21676 {
21677 	int count = 0;
21678 	const char *scan;
21679 	int opnd;
21680 	int ch;
21681 	int n;
21682 
21683 	scan = preg->reginput;
21684 	opnd = OPERAND(p);
21685 	switch (OP(preg, p)) {
21686 	case ANY:
21687 
21688 		while (!reg_iseol(preg, *scan) && count < max) {
21689 			count++;
21690 			scan++;
21691 		}
21692 		break;
21693 	case EXACTLY:
21694 		while (count < max) {
21695 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21696 			if (preg->program[opnd] != ch) {
21697 				break;
21698 			}
21699 			count++;
21700 			scan += n;
21701 		}
21702 		break;
21703 	case ANYOF:
21704 		while (count < max) {
21705 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21706 			if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) {
21707 				break;
21708 			}
21709 			count++;
21710 			scan += n;
21711 		}
21712 		break;
21713 	case ANYBUT:
21714 		while (count < max) {
21715 			n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21716 			if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
21717 				break;
21718 			}
21719 			count++;
21720 			scan += n;
21721 		}
21722 		break;
21723 	default:
21724 		preg->err = REG_ERR_INTERNAL;
21725 		count = 0;
21726 		break;
21727 	}
21728 	preg->reginput = scan;
21729 
21730 	return(count);
21731 }
21732 
regnext(regex_t * preg,int p)21733 static int regnext(regex_t *preg, int p )
21734 {
21735 	int offset;
21736 
21737 	offset = NEXT(preg, p);
21738 
21739 	if (offset == 0)
21740 		return 0;
21741 
21742 	if (OP(preg, p) == BACK)
21743 		return(p-offset);
21744 	else
21745 		return(p+offset);
21746 }
21747 
regopsize(regex_t * preg,int p)21748 static int regopsize(regex_t *preg, int p )
21749 {
21750 
21751 	switch (OP(preg, p)) {
21752 		case REP:
21753 		case REPMIN:
21754 		case REPX:
21755 		case REPXMIN:
21756 			return 5;
21757 
21758 		case ANYOF:
21759 		case ANYBUT:
21760 		case EXACTLY: {
21761 			int s = p + 2;
21762 			while (preg->program[s++]) {
21763 			}
21764 			return s - p;
21765 		}
21766 	}
21767 	return 2;
21768 }
21769 
21770 
regerror(int errcode,const regex_t * preg,char * errbuf,size_t errbuf_size)21771 size_t regerror(int errcode, const regex_t *preg, char *errbuf,  size_t errbuf_size)
21772 {
21773 	static const char *error_strings[] = {
21774 		"success",
21775 		"no match",
21776 		"bad pattern",
21777 		"null argument",
21778 		"unknown error",
21779 		"too big",
21780 		"out of memory",
21781 		"too many ()",
21782 		"parentheses () not balanced",
21783 		"braces {} not balanced",
21784 		"invalid repetition count(s)",
21785 		"extra characters",
21786 		"*+ of empty atom",
21787 		"nested count",
21788 		"internal error",
21789 		"count follows nothing",
21790 		"trailing backslash",
21791 		"corrupted program",
21792 		"contains null char",
21793 	};
21794 	const char *err;
21795 
21796 	if (errcode < 0 || errcode >= REG_ERR_NUM) {
21797 		err = "Bad error code";
21798 	}
21799 	else {
21800 		err = error_strings[errcode];
21801 	}
21802 
21803 	return snprintf(errbuf, errbuf_size, "%s", err);
21804 }
21805 
regfree(regex_t * preg)21806 void regfree(regex_t *preg)
21807 {
21808 	free(preg->program);
21809 }
21810 
21811 #endif
21812 
21813 #if defined(_WIN32) || defined(WIN32)
21814 #ifndef STRICT
21815 #define STRICT
21816 #endif
21817 #define WIN32_LEAN_AND_MEAN
21818 #include <windows.h>
21819 
21820 #if defined(HAVE_DLOPEN_COMPAT)
dlopen(const char * path,int mode)21821 void *dlopen(const char *path, int mode)
21822 {
21823     JIM_NOTUSED(mode);
21824 
21825     return (void *)LoadLibraryA(path);
21826 }
21827 
dlclose(void * handle)21828 int dlclose(void *handle)
21829 {
21830     FreeLibrary((HANDLE)handle);
21831     return 0;
21832 }
21833 
dlsym(void * handle,const char * symbol)21834 void *dlsym(void *handle, const char *symbol)
21835 {
21836     return GetProcAddress((HMODULE)handle, symbol);
21837 }
21838 
dlerror(void)21839 char *dlerror(void)
21840 {
21841     static char msg[121];
21842     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
21843                    LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
21844     return msg;
21845 }
21846 #endif
21847 
21848 #ifdef _MSC_VER
21849 
21850 #include <sys/timeb.h>
21851 
21852 
gettimeofday(struct timeval * tv,void * unused)21853 int gettimeofday(struct timeval *tv, void *unused)
21854 {
21855     struct _timeb tb;
21856 
21857     _ftime(&tb);
21858     tv->tv_sec = tb.time;
21859     tv->tv_usec = tb.millitm * 1000;
21860 
21861     return 0;
21862 }
21863 
21864 
opendir(const char * name)21865 DIR *opendir(const char *name)
21866 {
21867     DIR *dir = 0;
21868 
21869     if (name && name[0]) {
21870         size_t base_length = strlen(name);
21871         const char *all =
21872             strchr("/\\", name[base_length - 1]) ? "*" : "/*";
21873 
21874         if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
21875             (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
21876             strcat(strcpy(dir->name, name), all);
21877 
21878             if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
21879                 dir->result.d_name = 0;
21880             else {
21881                 Jim_Free(dir->name);
21882                 Jim_Free(dir);
21883                 dir = 0;
21884             }
21885         }
21886         else {
21887             Jim_Free(dir);
21888             dir = 0;
21889             errno = ENOMEM;
21890         }
21891     }
21892     else {
21893         errno = EINVAL;
21894     }
21895     return dir;
21896 }
21897 
closedir(DIR * dir)21898 int closedir(DIR * dir)
21899 {
21900     int result = -1;
21901 
21902     if (dir) {
21903         if (dir->handle != -1)
21904             result = _findclose(dir->handle);
21905         Jim_Free(dir->name);
21906         Jim_Free(dir);
21907     }
21908     if (result == -1)
21909         errno = EBADF;
21910     return result;
21911 }
21912 
readdir(DIR * dir)21913 struct dirent *readdir(DIR * dir)
21914 {
21915     struct dirent *result = 0;
21916 
21917     if (dir && dir->handle != -1) {
21918         if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
21919             result = &dir->result;
21920             result->d_name = dir->info.name;
21921         }
21922     }
21923     else {
21924         errno = EBADF;
21925     }
21926     return result;
21927 }
21928 #endif
21929 #endif
21930 #ifndef JIM_BOOTSTRAP_LIB_ONLY
21931 #include <errno.h>
21932 #include <string.h>
21933 
21934 
21935 #ifdef USE_LINENOISE
21936 #ifdef HAVE_UNISTD_H
21937     #include <unistd.h>
21938 #endif
21939 #ifdef HAVE_SYS_STAT_H
21940     #include <sys/stat.h>
21941 #endif
21942 #include "linenoise.h"
21943 #else
21944 #define MAX_LINE_LEN 512
21945 #endif
21946 
Jim_HistoryGetline(const char * prompt)21947 char *Jim_HistoryGetline(const char *prompt)
21948 {
21949 #ifdef USE_LINENOISE
21950     return linenoise(prompt);
21951 #else
21952     int len;
21953     char *line = malloc(MAX_LINE_LEN);
21954 
21955     fputs(prompt, stdout);
21956     fflush(stdout);
21957 
21958     if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
21959         free(line);
21960         return NULL;
21961     }
21962     len = strlen(line);
21963     if (len && line[len - 1] == '\n') {
21964         line[len - 1] = '\0';
21965     }
21966     return line;
21967 #endif
21968 }
21969 
Jim_HistoryLoad(const char * filename)21970 void Jim_HistoryLoad(const char *filename)
21971 {
21972 #ifdef USE_LINENOISE
21973     linenoiseHistoryLoad(filename);
21974 #endif
21975 }
21976 
Jim_HistoryAdd(const char * line)21977 void Jim_HistoryAdd(const char *line)
21978 {
21979 #ifdef USE_LINENOISE
21980     linenoiseHistoryAdd(line);
21981 #endif
21982 }
21983 
Jim_HistorySave(const char * filename)21984 void Jim_HistorySave(const char *filename)
21985 {
21986 #ifdef USE_LINENOISE
21987 #ifdef HAVE_UMASK
21988     mode_t mask;
21989 
21990     mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
21991 #endif
21992     linenoiseHistorySave(filename);
21993 #ifdef HAVE_UMASK
21994     mask = umask(mask);
21995 #endif
21996 #endif
21997 }
21998 
Jim_HistoryShow(void)21999 void Jim_HistoryShow(void)
22000 {
22001 #ifdef USE_LINENOISE
22002 
22003     int i;
22004     int len;
22005     char **history = linenoiseHistory(&len);
22006     for (i = 0; i < len; i++) {
22007         printf("%4d %s\n", i + 1, history[i]);
22008     }
22009 #endif
22010 }
22011 
22012 #ifdef USE_LINENOISE
22013 struct JimCompletionInfo {
22014     Jim_Interp *interp;
22015     Jim_Obj *command;
22016 };
22017 
JimCompletionCallback(const char * prefix,linenoiseCompletions * comp,void * userdata)22018 void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
22019 {
22020     struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
22021     Jim_Obj *objv[2];
22022     int ret;
22023 
22024     objv[0] = info->command;
22025     objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
22026 
22027     ret = Jim_EvalObjVector(info->interp, 2, objv);
22028 
22029 
22030     if (ret == JIM_OK) {
22031         int i;
22032         Jim_Obj *listObj = Jim_GetResult(info->interp);
22033         int len = Jim_ListLength(info->interp, listObj);
22034         for (i = 0; i < len; i++) {
22035             linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
22036         }
22037     }
22038 }
22039 #endif
22040 
Jim_InteractivePrompt(Jim_Interp * interp)22041 int Jim_InteractivePrompt(Jim_Interp *interp)
22042 {
22043     int retcode = JIM_OK;
22044     char *history_file = NULL;
22045 #ifdef USE_LINENOISE
22046     const char *home;
22047     struct JimCompletionInfo compinfo;
22048 
22049     home = getenv("HOME");
22050     if (home && isatty(STDIN_FILENO)) {
22051         int history_len = strlen(home) + sizeof("/.jim_history");
22052         history_file = Jim_Alloc(history_len);
22053         snprintf(history_file, history_len, "%s/.jim_history", home);
22054         Jim_HistoryLoad(history_file);
22055     }
22056 
22057     compinfo.interp = interp;
22058     compinfo.command = Jim_NewStringObj(interp, "tcl::autocomplete", -1);
22059     Jim_IncrRefCount(compinfo.command);
22060 
22061 
22062     linenoiseSetCompletionCallback(JimCompletionCallback, &compinfo);
22063 #endif
22064 
22065     printf("Welcome to Jim version %d.%d\n",
22066         JIM_VERSION / 100, JIM_VERSION % 100);
22067     Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
22068 
22069     while (1) {
22070         Jim_Obj *scriptObjPtr;
22071         const char *result;
22072         int reslen;
22073         char prompt[20];
22074 
22075         if (retcode != JIM_OK) {
22076             const char *retcodestr = Jim_ReturnCode(retcode);
22077 
22078             if (*retcodestr == '?') {
22079                 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
22080             }
22081             else {
22082                 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
22083             }
22084         }
22085         else {
22086             strcpy(prompt, ". ");
22087         }
22088 
22089         scriptObjPtr = Jim_NewStringObj(interp, "", 0);
22090         Jim_IncrRefCount(scriptObjPtr);
22091         while (1) {
22092             char state;
22093             char *line;
22094 
22095             line = Jim_HistoryGetline(prompt);
22096             if (line == NULL) {
22097                 if (errno == EINTR) {
22098                     continue;
22099                 }
22100                 Jim_DecrRefCount(interp, scriptObjPtr);
22101                 retcode = JIM_OK;
22102                 goto out;
22103             }
22104             if (Jim_Length(scriptObjPtr) != 0) {
22105 
22106                 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
22107             }
22108             Jim_AppendString(interp, scriptObjPtr, line, -1);
22109             free(line);
22110             if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
22111                 break;
22112 
22113             snprintf(prompt, sizeof(prompt), "%c> ", state);
22114         }
22115 #ifdef USE_LINENOISE
22116         if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
22117 
22118             Jim_HistoryShow();
22119             Jim_DecrRefCount(interp, scriptObjPtr);
22120             continue;
22121         }
22122 
22123         Jim_HistoryAdd(Jim_String(scriptObjPtr));
22124         if (history_file) {
22125             Jim_HistorySave(history_file);
22126         }
22127 #endif
22128         retcode = Jim_EvalObj(interp, scriptObjPtr);
22129         Jim_DecrRefCount(interp, scriptObjPtr);
22130 
22131         if (retcode == JIM_EXIT) {
22132             break;
22133         }
22134         if (retcode == JIM_ERR) {
22135             Jim_MakeErrorMessage(interp);
22136         }
22137         result = Jim_GetString(Jim_GetResult(interp), &reslen);
22138         if (reslen) {
22139             printf("%s\n", result);
22140         }
22141     }
22142   out:
22143     Jim_Free(history_file);
22144 
22145 #ifdef USE_LINENOISE
22146     Jim_DecrRefCount(interp, compinfo.command);
22147     linenoiseSetCompletionCallback(NULL, NULL);
22148 #endif
22149 
22150     return retcode;
22151 }
22152 
22153 #include <stdio.h>
22154 #include <stdlib.h>
22155 #include <string.h>
22156 
22157 
22158 
22159 extern int Jim_initjimshInit(Jim_Interp *interp);
22160 
JimSetArgv(Jim_Interp * interp,int argc,char * const argv[])22161 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
22162 {
22163     int n;
22164     Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
22165 
22166 
22167     for (n = 0; n < argc; n++) {
22168         Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
22169 
22170         Jim_ListAppendElement(interp, listObj, obj);
22171     }
22172 
22173     Jim_SetVariableStr(interp, "argv", listObj);
22174     Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
22175 }
22176 
JimPrintErrorMessage(Jim_Interp * interp)22177 static void JimPrintErrorMessage(Jim_Interp *interp)
22178 {
22179     Jim_MakeErrorMessage(interp);
22180     fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
22181 }
22182 
usage(const char * executable_name)22183 void usage(const char* executable_name)
22184 {
22185     printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
22186     printf("Usage: %s\n", executable_name);
22187     printf("or   : %s [options] [filename]\n", executable_name);
22188     printf("\n");
22189     printf("Without options: Interactive mode\n");
22190     printf("\n");
22191     printf("Options:\n");
22192     printf("      --version  : prints the version string\n");
22193     printf("      --help     : prints this text\n");
22194     printf("      -e CMD     : executes command CMD\n");
22195     printf("                   NOTE: all subsequent options will be passed as arguments to the command\n");
22196     printf("    [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n");
22197     printf("                   NOTE: all subsequent options will be passed to the script\n\n");
22198 }
22199 
main(int argc,char * const argv[])22200 int main(int argc, char *const argv[])
22201 {
22202     int retcode;
22203     Jim_Interp *interp;
22204     char *const orig_argv0 = argv[0];
22205 
22206 
22207     if (argc > 1 && strcmp(argv[1], "--version") == 0) {
22208         printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
22209         return 0;
22210     }
22211     else if (argc > 1 && strcmp(argv[1], "--help") == 0) {
22212         usage(argv[0]);
22213         return 0;
22214     }
22215 
22216 
22217     interp = Jim_CreateInterp();
22218     Jim_RegisterCoreCommands(interp);
22219 
22220 
22221     if (Jim_InitStaticExtensions(interp) != JIM_OK) {
22222         JimPrintErrorMessage(interp);
22223     }
22224 
22225     Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
22226     Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
22227     retcode = Jim_initjimshInit(interp);
22228 
22229     if (argc == 1) {
22230 
22231         if (retcode == JIM_ERR) {
22232             JimPrintErrorMessage(interp);
22233         }
22234         if (retcode != JIM_EXIT) {
22235             JimSetArgv(interp, 0, NULL);
22236             retcode = Jim_InteractivePrompt(interp);
22237         }
22238     }
22239     else {
22240 
22241         if (argc > 2 && strcmp(argv[1], "-e") == 0) {
22242 
22243             JimSetArgv(interp, argc - 3, argv + 3);
22244             retcode = Jim_Eval(interp, argv[2]);
22245             if (retcode != JIM_ERR) {
22246                 printf("%s\n", Jim_String(Jim_GetResult(interp)));
22247             }
22248         }
22249         else {
22250             Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
22251             JimSetArgv(interp, argc - 2, argv + 2);
22252             if (strcmp(argv[1], "-") == 0) {
22253                 retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]");
22254             } else {
22255                 retcode = Jim_EvalFile(interp, argv[1]);
22256             }
22257         }
22258         if (retcode == JIM_ERR) {
22259             JimPrintErrorMessage(interp);
22260         }
22261     }
22262     if (retcode == JIM_EXIT) {
22263         retcode = Jim_GetExitCode(interp);
22264     }
22265     else if (retcode == JIM_ERR) {
22266         retcode = 1;
22267     }
22268     else {
22269         retcode = 0;
22270     }
22271     Jim_FreeInterp(interp);
22272     return retcode;
22273 }
22274 #endif
22275