Add support for sub-interpreters
[jimtcl.git] / autosetup / jimsh0.c
blob4d6ec32e652537ff231dc015be202c4b4a9cb0de
1 /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */
2 #define _GNU_SOURCE
3 #define JIM_TCL_COMPAT
4 #define JIM_REFERENCES
5 #define JIM_ANSIC
6 #define JIM_REGEXP
7 #define HAVE_NO_AUTOCONF
8 #define _JIMAUTOCONF_H
9 #define TCL_LIBRARY "."
10 #define jim_ext_bootstrap
11 #define jim_ext_aio
12 #define jim_ext_readdir
13 #define jim_ext_glob
14 #define jim_ext_regexp
15 #define jim_ext_file
16 #define jim_ext_exec
17 #define jim_ext_clock
18 #define jim_ext_array
19 #define jim_ext_stdlib
20 #define jim_ext_tclcompat
21 #if defined(_MSC_VER)
22 #define TCL_PLATFORM_OS "windows"
23 #define TCL_PLATFORM_PLATFORM "windows"
24 #define TCL_PLATFORM_PATH_SEPARATOR ";"
25 #define HAVE_MKDIR_ONE_ARG
26 #define HAVE_SYSTEM
27 #elif defined(__MINGW32__)
28 #define TCL_PLATFORM_OS "mingw"
29 #define TCL_PLATFORM_PLATFORM "windows"
30 #define TCL_PLATFORM_PATH_SEPARATOR ";"
31 #define HAVE_MKDIR_ONE_ARG
32 #define HAVE_SYSTEM
33 #define HAVE_SYS_TIME_H
34 #define HAVE_DIRENT_H
35 #define HAVE_UNISTD_H
36 #else
37 #define TCL_PLATFORM_OS "unknown"
38 #define TCL_PLATFORM_PLATFORM "unix"
39 #define TCL_PLATFORM_PATH_SEPARATOR ":"
40 #define HAVE_VFORK
41 #define HAVE_WAITPID
42 #define HAVE_ISATTY
43 #define HAVE_MKSTEMP
44 #define HAVE_LINK
45 #define HAVE_SYS_TIME_H
46 #define HAVE_DIRENT_H
47 #define HAVE_UNISTD_H
48 #endif
49 #define JIM_VERSION 76
50 #ifndef JIM_WIN32COMPAT_H
51 #define JIM_WIN32COMPAT_H
55 #ifdef __cplusplus
56 extern "C" {
57 #endif
60 #if defined(_WIN32) || defined(WIN32)
62 #define HAVE_DLOPEN
63 void *dlopen(const char *path, int mode);
64 int dlclose(void *handle);
65 void *dlsym(void *handle, const char *symbol);
66 char *dlerror(void);
69 #define JIM_SPRINTF_DOUBLE_NEEDS_FIX
71 #ifdef _MSC_VER
74 #if _MSC_VER >= 1000
75 #pragma warning(disable:4146)
76 #endif
78 #include <limits.h>
79 #define jim_wide _int64
80 #ifndef LLONG_MAX
81 #define LLONG_MAX 9223372036854775807I64
82 #endif
83 #ifndef LLONG_MIN
84 #define LLONG_MIN (-LLONG_MAX - 1I64)
85 #endif
86 #define JIM_WIDE_MIN LLONG_MIN
87 #define JIM_WIDE_MAX LLONG_MAX
88 #define JIM_WIDE_MODIFIER "I64d"
89 #define strcasecmp _stricmp
90 #define strtoull _strtoui64
91 #define snprintf _snprintf
93 #include <io.h>
95 struct timeval {
96 long tv_sec;
97 long tv_usec;
100 int gettimeofday(struct timeval *tv, void *unused);
102 #define HAVE_OPENDIR
103 struct dirent {
104 char *d_name;
107 typedef struct DIR {
108 long handle;
109 struct _finddata_t info;
110 struct dirent result;
111 char *name;
112 } DIR;
114 DIR *opendir(const char *name);
115 int closedir(DIR *dir);
116 struct dirent *readdir(DIR *dir);
118 #elif defined(__MINGW32__)
120 #include <stdlib.h>
121 #define strtod __strtod
123 #endif
125 #endif
127 #ifdef __cplusplus
129 #endif
131 #endif
132 #ifndef UTF8_UTIL_H
133 #define UTF8_UTIL_H
135 #ifdef __cplusplus
136 extern "C" {
137 #endif
141 #define MAX_UTF8_LEN 4
143 int utf8_fromunicode(char *p, unsigned uc);
145 #ifndef JIM_UTF8
146 #include <ctype.h>
149 #define utf8_strlen(S, B) ((B) < 0 ? strlen(S) : (B))
150 #define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
151 #define utf8_getchars(CP, C) (*(CP) = (C), 1)
152 #define utf8_upper(C) toupper(C)
153 #define utf8_title(C) toupper(C)
154 #define utf8_lower(C) tolower(C)
155 #define utf8_index(C, I) (I)
156 #define utf8_charlen(C) 1
157 #define utf8_prev_len(S, L) 1
159 #else
161 #endif
163 #ifdef __cplusplus
165 #endif
167 #endif
169 #ifndef __JIM__H
170 #define __JIM__H
172 #ifdef __cplusplus
173 extern "C" {
174 #endif
176 #include <time.h>
177 #include <limits.h>
178 #include <stdio.h>
179 #include <stdlib.h>
180 #include <stdarg.h>
183 #ifndef HAVE_NO_AUTOCONF
184 #endif
188 #ifndef jim_wide
189 # ifdef HAVE_LONG_LONG
190 # define jim_wide long long
191 # ifndef LLONG_MAX
192 # define LLONG_MAX 9223372036854775807LL
193 # endif
194 # ifndef LLONG_MIN
195 # define LLONG_MIN (-LLONG_MAX - 1LL)
196 # endif
197 # define JIM_WIDE_MIN LLONG_MIN
198 # define JIM_WIDE_MAX LLONG_MAX
199 # else
200 # define jim_wide long
201 # define JIM_WIDE_MIN LONG_MIN
202 # define JIM_WIDE_MAX LONG_MAX
203 # endif
206 # ifdef HAVE_LONG_LONG
207 # define JIM_WIDE_MODIFIER "lld"
208 # else
209 # define JIM_WIDE_MODIFIER "ld"
210 # define strtoull strtoul
211 # endif
212 #endif
214 #define UCHAR(c) ((unsigned char)(c))
217 #define JIM_OK 0
218 #define JIM_ERR 1
219 #define JIM_RETURN 2
220 #define JIM_BREAK 3
221 #define JIM_CONTINUE 4
222 #define JIM_SIGNAL 5
223 #define JIM_EXIT 6
225 #define JIM_EVAL 7
227 #define JIM_MAX_CALLFRAME_DEPTH 1000
228 #define JIM_MAX_EVAL_DEPTH 2000
231 #define JIM_PRIV_FLAG_SHIFT 20
233 #define JIM_NONE 0
234 #define JIM_ERRMSG 1
235 #define JIM_ENUM_ABBREV 2
236 #define JIM_UNSHARED 4
237 #define JIM_MUSTEXIST 8
240 #define JIM_SUBST_NOVAR 1
241 #define JIM_SUBST_NOCMD 2
242 #define JIM_SUBST_NOESC 4
243 #define JIM_SUBST_FLAG 128
246 #define JIM_CASESENS 0
247 #define JIM_NOCASE 1
250 #define JIM_PATH_LEN 1024
253 #define JIM_NOTUSED(V) ((void) V)
255 #define JIM_LIBPATH "auto_path"
256 #define JIM_INTERACTIVE "tcl_interactive"
259 typedef struct Jim_Stack {
260 int len;
261 int maxlen;
262 void **vector;
263 } Jim_Stack;
266 typedef struct Jim_HashEntry {
267 void *key;
268 union {
269 void *val;
270 int intval;
271 } u;
272 struct Jim_HashEntry *next;
273 } Jim_HashEntry;
275 typedef struct Jim_HashTableType {
276 unsigned int (*hashFunction)(const void *key);
277 void *(*keyDup)(void *privdata, const void *key);
278 void *(*valDup)(void *privdata, const void *obj);
279 int (*keyCompare)(void *privdata, const void *key1, const void *key2);
280 void (*keyDestructor)(void *privdata, void *key);
281 void (*valDestructor)(void *privdata, void *obj);
282 } Jim_HashTableType;
284 typedef struct Jim_HashTable {
285 Jim_HashEntry **table;
286 const Jim_HashTableType *type;
287 void *privdata;
288 unsigned int size;
289 unsigned int sizemask;
290 unsigned int used;
291 unsigned int collisions;
292 unsigned int uniq;
293 } Jim_HashTable;
295 typedef struct Jim_HashTableIterator {
296 Jim_HashTable *ht;
297 Jim_HashEntry *entry, *nextEntry;
298 int index;
299 } Jim_HashTableIterator;
302 #define JIM_HT_INITIAL_SIZE 16
305 #define Jim_FreeEntryVal(ht, entry) \
306 if ((ht)->type->valDestructor) \
307 (ht)->type->valDestructor((ht)->privdata, (entry)->u.val)
309 #define Jim_SetHashVal(ht, entry, _val_) do { \
310 if ((ht)->type->valDup) \
311 (entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \
312 else \
313 (entry)->u.val = (_val_); \
314 } while(0)
316 #define Jim_FreeEntryKey(ht, entry) \
317 if ((ht)->type->keyDestructor) \
318 (ht)->type->keyDestructor((ht)->privdata, (entry)->key)
320 #define Jim_SetHashKey(ht, entry, _key_) do { \
321 if ((ht)->type->keyDup) \
322 (entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \
323 else \
324 (entry)->key = (void *)(_key_); \
325 } while(0)
327 #define Jim_CompareHashKeys(ht, key1, key2) \
328 (((ht)->type->keyCompare) ? \
329 (ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \
330 (key1) == (key2))
332 #define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq)
334 #define Jim_GetHashEntryKey(he) ((he)->key)
335 #define Jim_GetHashEntryVal(he) ((he)->u.val)
336 #define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
337 #define Jim_GetHashTableSize(ht) ((ht)->size)
338 #define Jim_GetHashTableUsed(ht) ((ht)->used)
341 typedef struct Jim_Obj {
342 char *bytes;
343 const struct Jim_ObjType *typePtr;
344 int refCount;
345 int length;
347 union {
349 jim_wide wideValue;
351 int intValue;
353 double doubleValue;
355 void *ptr;
357 struct {
358 void *ptr1;
359 void *ptr2;
360 } twoPtrValue;
362 struct {
363 struct Jim_Var *varPtr;
364 unsigned long callFrameId;
365 int global;
366 } varValue;
368 struct {
369 struct Jim_Obj *nsObj;
370 struct Jim_Cmd *cmdPtr;
371 unsigned long procEpoch;
372 } cmdValue;
374 struct {
375 struct Jim_Obj **ele;
376 int len;
377 int maxLen;
378 } listValue;
380 struct {
381 int maxLength;
382 int charLength;
383 } strValue;
385 struct {
386 unsigned long id;
387 struct Jim_Reference *refPtr;
388 } refValue;
390 struct {
391 struct Jim_Obj *fileNameObj;
392 int lineNumber;
393 } sourceValue;
395 struct {
396 struct Jim_Obj *varNameObjPtr;
397 struct Jim_Obj *indexObjPtr;
398 } dictSubstValue;
400 struct {
401 void *compre;
402 unsigned flags;
403 } regexpValue;
404 struct {
405 int line;
406 int argc;
407 } scriptLineValue;
408 } internalRep;
409 struct Jim_Obj *prevObjPtr;
410 struct Jim_Obj *nextObjPtr;
411 } Jim_Obj;
414 #define Jim_IncrRefCount(objPtr) \
415 ++(objPtr)->refCount
416 #define Jim_DecrRefCount(interp, objPtr) \
417 if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr)
418 #define Jim_IsShared(objPtr) \
419 ((objPtr)->refCount > 1)
421 #define Jim_FreeNewObj Jim_FreeObj
424 #define Jim_FreeIntRep(i,o) \
425 if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \
426 (o)->typePtr->freeIntRepProc(i, o)
429 #define Jim_GetIntRepPtr(o) (o)->internalRep.ptr
432 #define Jim_SetIntRepPtr(o, p) \
433 (o)->internalRep.ptr = (p)
436 struct Jim_Interp;
438 typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp,
439 struct Jim_Obj *objPtr);
440 typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp,
441 struct Jim_Obj *srcPtr, Jim_Obj *dupPtr);
442 typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr);
444 typedef struct Jim_ObjType {
445 const char *name;
446 Jim_FreeInternalRepProc *freeIntRepProc;
447 Jim_DupInternalRepProc *dupIntRepProc;
448 Jim_UpdateStringProc *updateStringProc;
449 int flags;
450 } Jim_ObjType;
453 #define JIM_TYPE_NONE 0
454 #define JIM_TYPE_REFERENCES 1
456 #define JIM_PRIV_FLAG_SHIFT 20
460 typedef struct Jim_CallFrame {
461 unsigned long id;
462 int level;
463 struct Jim_HashTable vars;
464 struct Jim_HashTable *staticVars;
465 struct Jim_CallFrame *parent;
466 Jim_Obj *const *argv;
467 int argc;
468 Jim_Obj *procArgsObjPtr;
469 Jim_Obj *procBodyObjPtr;
470 struct Jim_CallFrame *next;
471 Jim_Obj *nsObj;
472 Jim_Obj *fileNameObj;
473 int line;
474 Jim_Stack *localCommands;
475 int tailcall;
476 struct Jim_Obj *tailcallObj;
477 struct Jim_Cmd *tailcallCmd;
478 } Jim_CallFrame;
480 typedef struct Jim_Var {
481 Jim_Obj *objPtr;
482 struct Jim_CallFrame *linkFramePtr;
483 } Jim_Var;
486 typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
487 Jim_Obj *const *argv);
488 typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);
492 typedef struct Jim_Cmd {
493 int inUse;
494 int isproc;
495 struct Jim_Cmd *prevCmd;
496 union {
497 struct {
499 Jim_CmdProc *cmdProc;
500 Jim_DelCmdProc *delProc;
501 void *privData;
502 } native;
503 struct {
505 Jim_Obj *argListObjPtr;
506 Jim_Obj *bodyObjPtr;
507 Jim_HashTable *staticVars;
508 int argListLen;
509 int reqArity;
510 int optArity;
511 int argsPos;
512 int upcall;
513 struct Jim_ProcArg {
514 Jim_Obj *nameObjPtr;
515 Jim_Obj *defaultObjPtr;
516 } *arglist;
517 Jim_Obj *nsObj;
518 } proc;
519 } u;
520 } Jim_Cmd;
523 typedef struct Jim_PrngState {
524 unsigned char sbox[256];
525 unsigned int i, j;
526 } Jim_PrngState;
528 typedef struct Jim_Interp {
529 Jim_Obj *result;
530 int errorLine;
531 Jim_Obj *errorFileNameObj;
532 int addStackTrace;
533 int maxCallFrameDepth;
534 int maxEvalDepth;
535 int evalDepth;
536 int returnCode;
537 int returnLevel;
538 int exitCode;
539 long id;
540 int signal_level;
541 jim_wide sigmask;
542 int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask);
543 Jim_CallFrame *framePtr;
544 Jim_CallFrame *topFramePtr;
545 struct Jim_HashTable commands;
546 unsigned long procEpoch; /* Incremented every time the result
547 of procedures names lookup caching
548 may no longer be valid. */
549 unsigned long callFrameEpoch; /* Incremented every time a new
550 callframe is created. This id is used for the
551 'ID' field contained in the Jim_CallFrame
552 structure. */
553 int local;
554 Jim_Obj *liveList;
555 Jim_Obj *freeList;
556 Jim_Obj *currentScriptObj;
557 Jim_Obj *nullScriptObj;
558 Jim_Obj *emptyObj;
559 Jim_Obj *trueObj;
560 Jim_Obj *falseObj;
561 unsigned long referenceNextId;
562 struct Jim_HashTable references;
563 unsigned long lastCollectId; /* reference max Id of the last GC
564 execution. It's set to -1 while the collection
565 is running as sentinel to avoid to recursive
566 calls via the [collect] command inside
567 finalizers. */
568 time_t lastCollectTime;
569 Jim_Obj *stackTrace;
570 Jim_Obj *errorProc;
571 Jim_Obj *unknown;
572 int unknown_called;
573 int errorFlag;
574 void *cmdPrivData; /* Used to pass the private data pointer to
575 a command. It is set to what the user specified
576 via Jim_CreateCommand(). */
578 struct Jim_CallFrame *freeFramesList;
579 struct Jim_HashTable assocData;
580 Jim_PrngState *prngState;
581 struct Jim_HashTable packages;
582 Jim_Stack *loadHandles;
583 } Jim_Interp;
585 #define Jim_InterpIncrProcEpoch(i) (i)->procEpoch++
586 #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
587 #define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))
589 #define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
590 #define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj)
591 #define Jim_GetResult(i) ((i)->result)
592 #define Jim_CmdPrivData(i) ((i)->cmdPrivData)
594 #define Jim_SetResult(i,o) do { \
595 Jim_Obj *_resultObjPtr_ = (o); \
596 Jim_IncrRefCount(_resultObjPtr_); \
597 Jim_DecrRefCount(i,(i)->result); \
598 (i)->result = _resultObjPtr_; \
599 } while(0)
602 #define Jim_GetId(i) (++(i)->id)
605 #define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference
606 string representation must be fixed length. */
607 typedef struct Jim_Reference {
608 Jim_Obj *objPtr;
609 Jim_Obj *finalizerCmdNamePtr;
610 char tag[JIM_REFERENCE_TAGLEN+1];
611 } Jim_Reference;
614 #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
615 #define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
617 #define JIM_EXPORT
620 JIM_EXPORT void *Jim_Alloc (int size);
621 JIM_EXPORT void *Jim_Realloc(void *ptr, int size);
622 JIM_EXPORT void Jim_Free (void *ptr);
623 JIM_EXPORT char * Jim_StrDup (const char *s);
624 JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
627 JIM_EXPORT char **Jim_GetEnviron(void);
628 JIM_EXPORT void Jim_SetEnviron(char **env);
629 JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *template);
632 JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
635 JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);
637 #define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S))
639 JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script);
640 JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename);
641 JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename);
642 JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr);
643 JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc,
644 Jim_Obj *const *objv);
645 JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj);
646 JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
647 int objc, Jim_Obj *const *objv);
648 #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
649 JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
650 JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
651 Jim_Obj **resObjPtrPtr, int flags);
654 JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
655 JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
656 JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
657 JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
658 JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
659 JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
660 JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr));
663 JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
664 const Jim_HashTableType *type, void *privdata);
665 JIM_EXPORT void Jim_ExpandHashTable (Jim_HashTable *ht,
666 unsigned int size);
667 JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key,
668 void *val);
669 JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
670 const void *key, void *val);
671 JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
672 const void *key);
673 JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
674 JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
675 const void *key);
676 JIM_EXPORT void Jim_ResizeHashTable (Jim_HashTable *ht);
677 JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator
678 (Jim_HashTable *ht);
679 JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
680 (Jim_HashTableIterator *iter);
683 JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
684 JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr);
685 JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr);
686 JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
687 Jim_Obj *objPtr);
688 JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
689 int *lenPtr);
690 JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
691 JIM_EXPORT int Jim_Length(Jim_Obj *objPtr);
694 JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp,
695 const char *s, int len);
696 JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp,
697 const char *s, int charlen);
698 JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp,
699 char *s, int len);
700 JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr,
701 const char *str, int len);
702 JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr,
703 Jim_Obj *appendObjPtr);
704 JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp,
705 Jim_Obj *objPtr, ...);
706 JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr);
707 JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr,
708 Jim_Obj *objPtr, int nocase);
709 JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp,
710 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr,
711 Jim_Obj *lastObjPtr);
712 JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp,
713 Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
714 JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
715 Jim_Obj *fmtObjPtr, int flags);
716 JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
717 Jim_Obj *objPtr, const char *str);
718 JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
719 Jim_Obj *secondObjPtr, int nocase);
720 JIM_EXPORT int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
721 Jim_Obj *secondObjPtr, int nocase);
722 JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr);
725 JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp,
726 Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr);
727 JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp,
728 Jim_Obj *objPtr);
729 JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr);
730 JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr);
733 JIM_EXPORT Jim_Interp * Jim_CreateInterp (void);
734 JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i);
735 JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp);
736 JIM_EXPORT const char *Jim_ReturnCode(int code);
737 JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...);
740 JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
741 JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
742 const char *cmdName, Jim_CmdProc cmdProc, void *privData,
743 Jim_DelCmdProc delProc);
744 JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
745 const char *cmdName);
746 JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
747 const char *oldName, const char *newName);
748 JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
749 Jim_Obj *objPtr, int flags);
750 JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
751 Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
752 JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
753 const char *name, Jim_Obj *objPtr);
754 JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp,
755 const char *name, Jim_Obj *objPtr);
756 JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp,
757 const char *name, const char *val);
758 JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp,
759 Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr,
760 Jim_CallFrame *targetCallFrame);
761 JIM_EXPORT Jim_Obj * Jim_MakeGlobalNamespaceName(Jim_Interp *interp,
762 Jim_Obj *nameObjPtr);
763 JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp,
764 Jim_Obj *nameObjPtr, int flags);
765 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp,
766 Jim_Obj *nameObjPtr, int flags);
767 JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp,
768 const char *name, int flags);
769 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp,
770 const char *name, int flags);
771 JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp,
772 Jim_Obj *nameObjPtr, int flags);
775 JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp,
776 Jim_Obj *levelObjPtr);
779 JIM_EXPORT int Jim_Collect (Jim_Interp *interp);
780 JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp);
783 JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr,
784 int *indexPtr);
787 JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp,
788 Jim_Obj *const *elements, int len);
789 JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp,
790 Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec);
791 JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp,
792 Jim_Obj *listPtr, Jim_Obj *objPtr);
793 JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp,
794 Jim_Obj *listPtr, Jim_Obj *appendListPtr);
795 JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr);
796 JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt,
797 int listindex, Jim_Obj **objPtrPtr, int seterr);
798 JIM_EXPORT Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx);
799 JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp,
800 Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc,
801 Jim_Obj *newObjPtr);
802 JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc,
803 Jim_Obj *const *objv);
804 JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp,
805 Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen);
808 JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp,
809 Jim_Obj *const *elements, int len);
810 JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr,
811 Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
812 JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
813 Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
814 Jim_Obj **objPtrPtr, int flags);
815 JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
816 Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
817 Jim_Obj *newObjPtr, int flags);
818 JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp,
819 Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len);
820 JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
821 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
822 JIM_EXPORT int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj);
823 JIM_EXPORT int Jim_DictValues(Jim_Interp *interp, Jim_Obj *dictObjPtr, Jim_Obj *patternObjPtr);
824 JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
825 JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr);
828 JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
829 int *intPtr);
832 JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
833 Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr);
834 JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
835 Jim_Obj *exprObjPtr, int *boolPtr);
838 JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
839 jim_wide *widePtr);
840 JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
841 long *longPtr);
842 #define Jim_NewWideObj Jim_NewIntObj
843 JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
844 jim_wide wideValue);
847 JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
848 double *doublePtr);
849 JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
850 double doubleValue);
851 JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue);
854 JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc,
855 Jim_Obj *const *argv, const char *msg);
856 JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
857 const char * const *tablePtr, int *indexPtr, const char *name, int flags);
858 JIM_EXPORT int Jim_ScriptIsComplete (const char *s, int len,
859 char *stateCharPtr);
860 JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);
863 typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
864 JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
865 JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
866 Jim_InterpDeleteProc *delProc, void *data);
867 JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
871 JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
872 const char *name, const char *ver, int flags);
873 JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
874 const char *name, int flags);
877 JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
880 JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
881 JIM_EXPORT void Jim_HistoryLoad(const char *filename);
882 JIM_EXPORT void Jim_HistorySave(const char *filename);
883 JIM_EXPORT char *Jim_HistoryGetline(const char *prompt);
884 JIM_EXPORT void Jim_HistoryAdd(const char *line);
885 JIM_EXPORT void Jim_HistoryShow(void);
888 JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
889 JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
890 JIM_EXPORT int Jim_IsBigEndian(void);
892 #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
895 JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
896 JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
899 JIM_EXPORT FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
902 JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
903 JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
905 #ifdef __cplusplus
907 #endif
909 #endif
911 #ifndef JIM_SUBCMD_H
912 #define JIM_SUBCMD_H
915 #ifdef __cplusplus
916 extern "C" {
917 #endif
920 #define JIM_MODFLAG_HIDDEN 0x0001
921 #define JIM_MODFLAG_FULLARGV 0x0002
925 typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
927 typedef struct {
928 const char *cmd;
929 const char *args;
930 jim_subcmd_function *function;
931 short minargs;
932 short maxargs;
933 unsigned short flags;
934 } jim_subcmd_type;
936 const jim_subcmd_type *
937 Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
939 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
941 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
943 #ifdef __cplusplus
945 #endif
947 #endif
948 #ifndef JIMREGEXP_H
949 #define JIMREGEXP_H
952 #ifdef __cplusplus
953 extern "C" {
954 #endif
956 #include <stdlib.h>
958 typedef struct {
959 int rm_so;
960 int rm_eo;
961 } regmatch_t;
964 typedef struct regexp {
966 int re_nsub;
969 int cflags;
970 int err;
971 int regstart;
972 int reganch;
973 int regmust;
974 int regmlen;
975 int *program;
978 const char *regparse;
979 int p;
980 int proglen;
983 int eflags;
984 const char *start;
985 const char *reginput;
986 const char *regbol;
989 regmatch_t *pmatch;
990 int nmatch;
991 } regexp;
993 typedef regexp regex_t;
995 #define REG_EXTENDED 0
996 #define REG_NEWLINE 1
997 #define REG_ICASE 2
999 #define REG_NOTBOL 16
1001 enum {
1002 REG_NOERROR,
1003 REG_NOMATCH,
1004 REG_BADPAT,
1005 REG_ERR_NULL_ARGUMENT,
1006 REG_ERR_UNKNOWN,
1007 REG_ERR_TOO_BIG,
1008 REG_ERR_NOMEM,
1009 REG_ERR_TOO_MANY_PAREN,
1010 REG_ERR_UNMATCHED_PAREN,
1011 REG_ERR_UNMATCHED_BRACES,
1012 REG_ERR_BAD_COUNT,
1013 REG_ERR_JUNK_ON_END,
1014 REG_ERR_OPERAND_COULD_BE_EMPTY,
1015 REG_ERR_NESTED_COUNT,
1016 REG_ERR_INTERNAL,
1017 REG_ERR_COUNT_FOLLOWS_NOTHING,
1018 REG_ERR_TRAILING_BACKSLASH,
1019 REG_ERR_CORRUPTED,
1020 REG_ERR_NULL_CHAR,
1021 REG_ERR_NUM
1024 int regcomp(regex_t *preg, const char *regex, int cflags);
1025 int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
1026 size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
1027 void regfree(regex_t *preg);
1029 #ifdef __cplusplus
1031 #endif
1033 #endif
1034 int Jim_bootstrapInit(Jim_Interp *interp)
1036 if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
1037 return JIM_ERR;
1039 return Jim_EvalSource(interp, "bootstrap.tcl", 1,
1040 "\n"
1041 "\n"
1042 "proc package {args} {}\n"
1045 int Jim_initjimshInit(Jim_Interp *interp)
1047 if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
1048 return JIM_ERR;
1050 return Jim_EvalSource(interp, "initjimsh.tcl", 1,
1051 "\n"
1052 "\n"
1053 "\n"
1054 "proc _jimsh_init {} {\n"
1055 " rename _jimsh_init {}\n"
1056 " global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
1057 "\n"
1058 "\n"
1059 " if {[exists jim::argv0]} {\n"
1060 " if {[string match \"*/*\" $jim::argv0]} {\n"
1061 " set jim::exe [file join [pwd] $jim::argv0]\n"
1062 " } else {\n"
1063 " foreach path [split [env PATH \"\"] $tcl_platform(pathSeparator)] {\n"
1064 " set exec [file join [pwd] [string map {\\\\ /} $path] $jim::argv0]\n"
1065 " if {[file executable $exec]} {\n"
1066 " set jim::exe $exec\n"
1067 " break\n"
1068 " }\n"
1069 " }\n"
1070 " }\n"
1071 " }\n"
1072 "\n"
1073 "\n"
1074 " lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
1075 " if {[exists jim::exe]} {\n"
1076 " lappend p [file dirname $jim::exe]\n"
1077 " }\n"
1078 " lappend p {*}$auto_path\n"
1079 " set auto_path $p\n"
1080 "\n"
1081 " if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
1082 " foreach src {.jimrc jimrc.tcl} {\n"
1083 " if {[file exists [env HOME]/$src]} {\n"
1084 " uplevel #0 source [env HOME]/$src\n"
1085 " break\n"
1086 " }\n"
1087 " }\n"
1088 " }\n"
1089 " return \"\"\n"
1090 "}\n"
1091 "\n"
1092 "if {$tcl_platform(platform) eq \"windows\"} {\n"
1093 " set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
1094 "}\n"
1095 "\n"
1096 "_jimsh_init\n"
1099 int Jim_globInit(Jim_Interp *interp)
1101 if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
1102 return JIM_ERR;
1104 return Jim_EvalSource(interp, "glob.tcl", 1,
1105 "\n"
1106 "\n"
1107 "\n"
1108 "\n"
1109 "\n"
1110 "\n"
1111 "\n"
1112 "package require readdir\n"
1113 "\n"
1114 "\n"
1115 "proc glob.globdir {dir pattern} {\n"
1116 " if {[file exists $dir/$pattern]} {\n"
1117 "\n"
1118 " return [list $pattern]\n"
1119 " }\n"
1120 "\n"
1121 " set result {}\n"
1122 " set files [readdir $dir]\n"
1123 " lappend files . ..\n"
1124 "\n"
1125 " foreach name $files {\n"
1126 " if {[string match $pattern $name]} {\n"
1127 "\n"
1128 " if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n"
1129 " continue\n"
1130 " }\n"
1131 " lappend result $name\n"
1132 " }\n"
1133 " }\n"
1134 "\n"
1135 " return $result\n"
1136 "}\n"
1137 "\n"
1138 "\n"
1139 "\n"
1140 "\n"
1141 "proc glob.explode {pattern} {\n"
1142 " set oldexp {}\n"
1143 " set newexp {\"\"}\n"
1144 "\n"
1145 " while 1 {\n"
1146 " set oldexp $newexp\n"
1147 " set newexp {}\n"
1148 " set ob [string first \\{ $pattern]\n"
1149 " set cb [string first \\} $pattern]\n"
1150 "\n"
1151 " if {$ob < $cb && $ob != -1} {\n"
1152 " set mid [string range $pattern 0 $ob-1]\n"
1153 " set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n"
1154 " if {$pattern eq \"\"} {\n"
1155 " error \"unmatched open brace in glob pattern\"\n"
1156 " }\n"
1157 " set pattern [string range $pattern 1 end]\n"
1158 "\n"
1159 " foreach subs $subexp {\n"
1160 " foreach sub [split $subs ,] {\n"
1161 " foreach old $oldexp {\n"
1162 " lappend newexp $old$mid$sub\n"
1163 " }\n"
1164 " }\n"
1165 " }\n"
1166 " } elseif {$cb != -1} {\n"
1167 " set suf [string range $pattern 0 $cb-1]\n"
1168 " set rest [string range $pattern $cb end]\n"
1169 " break\n"
1170 " } else {\n"
1171 " set suf $pattern\n"
1172 " set rest \"\"\n"
1173 " break\n"
1174 " }\n"
1175 " }\n"
1176 "\n"
1177 " foreach old $oldexp {\n"
1178 " lappend newexp $old$suf\n"
1179 " }\n"
1180 " list $rest {*}$newexp\n"
1181 "}\n"
1182 "\n"
1183 "\n"
1184 "\n"
1185 "proc glob.glob {base pattern} {\n"
1186 " set dir [file dirname $pattern]\n"
1187 " if {$pattern eq $dir || $pattern eq \"\"} {\n"
1188 " return [list [file join $base $dir] $pattern]\n"
1189 " } elseif {$pattern eq [file tail $pattern]} {\n"
1190 " set dir \"\"\n"
1191 " }\n"
1192 "\n"
1193 "\n"
1194 " set dirlist [glob.glob $base $dir]\n"
1195 " set pattern [file tail $pattern]\n"
1196 "\n"
1197 "\n"
1198 " set result {}\n"
1199 " foreach {realdir dir} $dirlist {\n"
1200 " if {![file isdir $realdir]} {\n"
1201 " continue\n"
1202 " }\n"
1203 " if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n"
1204 " append dir /\n"
1205 " }\n"
1206 " foreach name [glob.globdir $realdir $pattern] {\n"
1207 " lappend result [file join $realdir $name] $dir$name\n"
1208 " }\n"
1209 " }\n"
1210 " return $result\n"
1211 "}\n"
1212 "\n"
1213 "\n"
1214 "\n"
1215 "\n"
1216 "\n"
1217 "\n"
1218 "\n"
1219 "\n"
1220 "\n"
1221 "\n"
1222 "\n"
1223 "\n"
1224 "proc glob {args} {\n"
1225 " set nocomplain 0\n"
1226 " set base \"\"\n"
1227 " set tails 0\n"
1228 "\n"
1229 " set n 0\n"
1230 " foreach arg $args {\n"
1231 " if {[info exists param]} {\n"
1232 " set $param $arg\n"
1233 " unset param\n"
1234 " incr n\n"
1235 " continue\n"
1236 " }\n"
1237 " switch -glob -- $arg {\n"
1238 " -d* {\n"
1239 " set switch $arg\n"
1240 " set param base\n"
1241 " }\n"
1242 " -n* {\n"
1243 " set nocomplain 1\n"
1244 " }\n"
1245 " -ta* {\n"
1246 " set tails 1\n"
1247 " }\n"
1248 " -- {\n"
1249 " incr n\n"
1250 " break\n"
1251 " }\n"
1252 " -* {\n"
1253 " return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1254 " }\n"
1255 " * {\n"
1256 " break\n"
1257 " }\n"
1258 " }\n"
1259 " incr n\n"
1260 " }\n"
1261 " if {[info exists param]} {\n"
1262 " return -code error \"missing argument to \\\"$switch\\\"\"\n"
1263 " }\n"
1264 " if {[llength $args] <= $n} {\n"
1265 " return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n"
1266 " }\n"
1267 "\n"
1268 " set args [lrange $args $n end]\n"
1269 "\n"
1270 " set result {}\n"
1271 " foreach pattern $args {\n"
1272 " set escpattern [string map {\n"
1273 " \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
1274 " } $pattern]\n"
1275 " set patexps [lassign [glob.explode $escpattern] rest]\n"
1276 " if {$rest ne \"\"} {\n"
1277 " return -code error \"unmatched close brace in glob pattern\"\n"
1278 " }\n"
1279 " foreach patexp $patexps {\n"
1280 " set patexp [string map {\n"
1281 " \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
1282 " } $patexp]\n"
1283 " foreach {realname name} [glob.glob $base $patexp] {\n"
1284 " incr n\n"
1285 " if {$tails} {\n"
1286 " lappend result $name\n"
1287 " } else {\n"
1288 " lappend result [file join $base $name]\n"
1289 " }\n"
1290 " }\n"
1291 " }\n"
1292 " }\n"
1293 "\n"
1294 " if {!$nocomplain && [llength $result] == 0} {\n"
1295 " set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
1296 " return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
1297 " }\n"
1298 "\n"
1299 " return $result\n"
1300 "}\n"
1303 int Jim_stdlibInit(Jim_Interp *interp)
1305 if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
1306 return JIM_ERR;
1308 return Jim_EvalSource(interp, "stdlib.tcl", 1,
1309 "\n"
1310 "\n"
1311 "\n"
1312 "proc lambda {arglist args} {\n"
1313 " tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
1314 "}\n"
1315 "\n"
1316 "proc lambda.finalizer {name val} {\n"
1317 " rename $name {}\n"
1318 "}\n"
1319 "\n"
1320 "\n"
1321 "proc curry {args} {\n"
1322 " alias [ref {} function lambda.finalizer] {*}$args\n"
1323 "}\n"
1324 "\n"
1325 "\n"
1326 "\n"
1327 "\n"
1328 "\n"
1329 "\n"
1330 "\n"
1331 "\n"
1332 "\n"
1333 "proc function {value} {\n"
1334 " return $value\n"
1335 "}\n"
1336 "\n"
1337 "\n"
1338 "\n"
1339 "\n"
1340 "proc stacktrace {{skip 0}} {\n"
1341 " set trace {}\n"
1342 " incr skip\n"
1343 " foreach level [range $skip [info level]] {\n"
1344 " lappend trace {*}[info frame -$level]\n"
1345 " }\n"
1346 " return $trace\n"
1347 "}\n"
1348 "\n"
1349 "\n"
1350 "proc stackdump {stacktrace} {\n"
1351 " set lines {}\n"
1352 " foreach {l f p} [lreverse $stacktrace] {\n"
1353 " set line {}\n"
1354 " if {$p ne \"\"} {\n"
1355 " append line \"in procedure '$p' \"\n"
1356 " if {$f ne \"\"} {\n"
1357 " append line \"called \"\n"
1358 " }\n"
1359 " }\n"
1360 " if {$f ne \"\"} {\n"
1361 " append line \"at file \\\"$f\\\", line $l\"\n"
1362 " }\n"
1363 " if {$line ne \"\"} {\n"
1364 " lappend lines $line\n"
1365 " }\n"
1366 " }\n"
1367 " join $lines \\n\n"
1368 "}\n"
1369 "\n"
1370 "\n"
1371 "\n"
1372 "proc errorInfo {msg {stacktrace \"\"}} {\n"
1373 " if {$stacktrace eq \"\"} {\n"
1374 "\n"
1375 " set stacktrace [info stacktrace]\n"
1376 "\n"
1377 " lappend stacktrace {*}[stacktrace 1]\n"
1378 " }\n"
1379 " lassign $stacktrace p f l\n"
1380 " if {$f ne \"\"} {\n"
1381 " set result \"$f:$l: Error: \"\n"
1382 " }\n"
1383 " append result \"$msg\\n\"\n"
1384 " append result [stackdump $stacktrace]\n"
1385 "\n"
1386 "\n"
1387 " string trim $result\n"
1388 "}\n"
1389 "\n"
1390 "\n"
1391 "\n"
1392 "proc {info nameofexecutable} {} {\n"
1393 " if {[exists ::jim::exe]} {\n"
1394 " return $::jim::exe\n"
1395 " }\n"
1396 "}\n"
1397 "\n"
1398 "\n"
1399 "proc {dict with} {&dictVar {args key} script} {\n"
1400 " set keys {}\n"
1401 " foreach {n v} [dict get $dictVar {*}$key] {\n"
1402 " upvar $n var_$n\n"
1403 " set var_$n $v\n"
1404 " lappend keys $n\n"
1405 " }\n"
1406 " catch {uplevel 1 $script} msg opts\n"
1407 " if {[info exists dictVar] && ([llength $key] == 0 || [dict exists $dictVar {*}$key])} {\n"
1408 " foreach n $keys {\n"
1409 " if {[info exists var_$n]} {\n"
1410 " dict set dictVar {*}$key $n [set var_$n]\n"
1411 " } else {\n"
1412 " dict unset dictVar {*}$key $n\n"
1413 " }\n"
1414 " }\n"
1415 " }\n"
1416 " return {*}$opts $msg\n"
1417 "}\n"
1418 "\n"
1419 "\n"
1420 "proc {dict update} {&varName args script} {\n"
1421 " set keys {}\n"
1422 " foreach {n v} $args {\n"
1423 " upvar $v var_$v\n"
1424 " if {[dict exists $varName $n]} {\n"
1425 " set var_$v [dict get $varName $n]\n"
1426 " }\n"
1427 " }\n"
1428 " catch {uplevel 1 $script} msg opts\n"
1429 " if {[info exists varName]} {\n"
1430 " foreach {n v} $args {\n"
1431 " if {[info exists var_$v]} {\n"
1432 " dict set varName $n [set var_$v]\n"
1433 " } else {\n"
1434 " dict unset varName $n\n"
1435 " }\n"
1436 " }\n"
1437 " }\n"
1438 " return {*}$opts $msg\n"
1439 "}\n"
1440 "\n"
1441 "\n"
1442 "\n"
1443 "proc {dict merge} {dict args} {\n"
1444 " foreach d $args {\n"
1445 "\n"
1446 " dict size $d\n"
1447 " foreach {k v} $d {\n"
1448 " dict set dict $k $v\n"
1449 " }\n"
1450 " }\n"
1451 " return $dict\n"
1452 "}\n"
1453 "\n"
1454 "proc {dict replace} {dictionary {args {key value}}} {\n"
1455 " if {[llength ${key value}] % 2} {\n"
1456 " tailcall {dict replace}\n"
1457 " }\n"
1458 " tailcall dict merge $dictionary ${key value}\n"
1459 "}\n"
1460 "\n"
1461 "\n"
1462 "proc {dict lappend} {varName key {args value}} {\n"
1463 " upvar $varName dict\n"
1464 " if {[exists dict] && [dict exists $dict $key]} {\n"
1465 " set list [dict get $dict $key]\n"
1466 " }\n"
1467 " lappend list {*}$value\n"
1468 " dict set dict $key $list\n"
1469 "}\n"
1470 "\n"
1471 "\n"
1472 "proc {dict append} {varName key {args value}} {\n"
1473 " upvar $varName dict\n"
1474 " if {[exists dict] && [dict exists $dict $key]} {\n"
1475 " set str [dict get $dict $key]\n"
1476 " }\n"
1477 " append str {*}$value\n"
1478 " dict set dict $key $str\n"
1479 "}\n"
1480 "\n"
1481 "\n"
1482 "proc {dict incr} {varName key {increment 1}} {\n"
1483 " upvar $varName dict\n"
1484 " if {[exists dict] && [dict exists $dict $key]} {\n"
1485 " set value [dict get $dict $key]\n"
1486 " }\n"
1487 " incr value $increment\n"
1488 " dict set dict $key $value\n"
1489 "}\n"
1490 "\n"
1491 "\n"
1492 "proc {dict remove} {dictionary {args key}} {\n"
1493 " foreach k $key {\n"
1494 " dict unset dictionary $k\n"
1495 " }\n"
1496 " return $dictionary\n"
1497 "}\n"
1498 "\n"
1499 "\n"
1500 "proc {dict values} {dictionary {pattern *}} {\n"
1501 " dict keys [lreverse $dictionary] $pattern\n"
1502 "}\n"
1503 "\n"
1504 "\n"
1505 "proc {dict for} {vars dictionary script} {\n"
1506 " if {[llength $vars] != 2} {\n"
1507 " return -code error \"must have exactly two variable names\"\n"
1508 " }\n"
1509 " dict size $dictionary\n"
1510 " tailcall foreach $vars $dictionary $script\n"
1511 "}\n"
1514 int Jim_tclcompatInit(Jim_Interp *interp)
1516 if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG))
1517 return JIM_ERR;
1519 return Jim_EvalSource(interp, "tclcompat.tcl", 1,
1520 "\n"
1521 "\n"
1522 "\n"
1523 "\n"
1524 "\n"
1525 "\n"
1526 "\n"
1527 "\n"
1528 "set env [env]\n"
1529 "\n"
1530 "\n"
1531 "if {[info commands stdout] ne \"\"} {\n"
1532 "\n"
1533 " foreach p {gets flush close eof seek tell} {\n"
1534 " proc $p {chan args} {p} {\n"
1535 " tailcall $chan $p {*}$args\n"
1536 " }\n"
1537 " }\n"
1538 " unset p\n"
1539 "\n"
1540 "\n"
1541 "\n"
1542 " proc puts {{-nonewline {}} {chan stdout} msg} {\n"
1543 " if {${-nonewline} ni {-nonewline {}}} {\n"
1544 " tailcall ${-nonewline} puts $msg\n"
1545 " }\n"
1546 " tailcall $chan puts {*}${-nonewline} $msg\n"
1547 " }\n"
1548 "\n"
1549 "\n"
1550 "\n"
1551 "\n"
1552 "\n"
1553 " proc read {{-nonewline {}} chan} {\n"
1554 " if {${-nonewline} ni {-nonewline {}}} {\n"
1555 " tailcall ${-nonewline} read {*}${chan}\n"
1556 " }\n"
1557 " tailcall $chan read {*}${-nonewline}\n"
1558 " }\n"
1559 "\n"
1560 " proc fconfigure {f args} {\n"
1561 " foreach {n v} $args {\n"
1562 " switch -glob -- $n {\n"
1563 " -bl* {\n"
1564 " $f ndelay $(!$v)\n"
1565 " }\n"
1566 " -bu* {\n"
1567 " $f buffering $v\n"
1568 " }\n"
1569 " -tr* {\n"
1570 "\n"
1571 " }\n"
1572 " default {\n"
1573 " return -code error \"fconfigure: unknown option $n\"\n"
1574 " }\n"
1575 " }\n"
1576 " }\n"
1577 " }\n"
1578 "}\n"
1579 "\n"
1580 "\n"
1581 "proc fileevent {args} {\n"
1582 " tailcall {*}$args\n"
1583 "}\n"
1584 "\n"
1585 "\n"
1586 "\n"
1587 "\n"
1588 "proc parray {arrayname {pattern *} {puts puts}} {\n"
1589 " upvar $arrayname a\n"
1590 "\n"
1591 " set max 0\n"
1592 " foreach name [array names a $pattern]] {\n"
1593 " if {[string length $name] > $max} {\n"
1594 " set max [string length $name]\n"
1595 " }\n"
1596 " }\n"
1597 " incr max [string length $arrayname]\n"
1598 " incr max 2\n"
1599 " foreach name [lsort [array names a $pattern]] {\n"
1600 " $puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
1601 " }\n"
1602 "}\n"
1603 "\n"
1604 "\n"
1605 "proc {file copy} {{force {}} source target} {\n"
1606 " try {\n"
1607 " if {$force ni {{} -force}} {\n"
1608 " error \"bad option \\\"$force\\\": should be -force\"\n"
1609 " }\n"
1610 "\n"
1611 " set in [open $source rb]\n"
1612 "\n"
1613 " if {[file exists $target]} {\n"
1614 " if {$force eq \"\"} {\n"
1615 " error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
1616 " }\n"
1617 "\n"
1618 " if {$source eq $target} {\n"
1619 " return\n"
1620 " }\n"
1621 "\n"
1622 "\n"
1623 " file stat $source ss\n"
1624 " file stat $target ts\n"
1625 " if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
1626 " return\n"
1627 " }\n"
1628 " }\n"
1629 " set out [open $target wb]\n"
1630 " $in copyto $out\n"
1631 " $out close\n"
1632 " } on error {msg opts} {\n"
1633 " incr opts(-level)\n"
1634 " return {*}$opts $msg\n"
1635 " } finally {\n"
1636 " catch {$in close}\n"
1637 " }\n"
1638 "}\n"
1639 "\n"
1640 "\n"
1641 "\n"
1642 "proc popen {cmd {mode r}} {\n"
1643 " lassign [socket pipe] r w\n"
1644 " try {\n"
1645 " if {[string match \"w*\" $mode]} {\n"
1646 " lappend cmd <@$r &\n"
1647 " set pids [exec {*}$cmd]\n"
1648 " $r close\n"
1649 " set f $w\n"
1650 " } else {\n"
1651 " lappend cmd >@$w &\n"
1652 " set pids [exec {*}$cmd]\n"
1653 " $w close\n"
1654 " set f $r\n"
1655 " }\n"
1656 " lambda {cmd args} {f pids} {\n"
1657 " if {$cmd eq \"pid\"} {\n"
1658 " return $pids\n"
1659 " }\n"
1660 " if {$cmd eq \"close\"} {\n"
1661 " $f close\n"
1662 "\n"
1663 " foreach p $pids { os.wait $p }\n"
1664 " return\n"
1665 " }\n"
1666 " tailcall $f $cmd {*}$args\n"
1667 " }\n"
1668 " } on error {error opts} {\n"
1669 " $r close\n"
1670 " $w close\n"
1671 " error $error\n"
1672 " }\n"
1673 "}\n"
1674 "\n"
1675 "\n"
1676 "local proc pid {{channelId {}}} {\n"
1677 " if {$channelId eq \"\"} {\n"
1678 " tailcall upcall pid\n"
1679 " }\n"
1680 " if {[catch {$channelId tell}]} {\n"
1681 " return -code error \"can not find channel named \\\"$channelId\\\"\"\n"
1682 " }\n"
1683 " if {[catch {$channelId pid} pids]} {\n"
1684 " return \"\"\n"
1685 " }\n"
1686 " return $pids\n"
1687 "}\n"
1688 "\n"
1689 "\n"
1690 "\n"
1691 "\n"
1692 "\n"
1693 "\n"
1694 "\n"
1695 "\n"
1696 "\n"
1697 "\n"
1698 "\n"
1699 "\n"
1700 "\n"
1701 "\n"
1702 "proc try {args} {\n"
1703 " set catchopts {}\n"
1704 " while {[string match -* [lindex $args 0]]} {\n"
1705 " set args [lassign $args opt]\n"
1706 " if {$opt eq \"--\"} {\n"
1707 " break\n"
1708 " }\n"
1709 " lappend catchopts $opt\n"
1710 " }\n"
1711 " if {[llength $args] == 0} {\n"
1712 " return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n"
1713 " }\n"
1714 " set args [lassign $args script]\n"
1715 " set code [catch -eval {*}$catchopts {uplevel 1 $script} msg opts]\n"
1716 "\n"
1717 " set handled 0\n"
1718 "\n"
1719 " foreach {on codes vars script} $args {\n"
1720 " switch -- $on \\\n"
1721 " on {\n"
1722 " if {!$handled && ($codes eq \"*\" || [info returncode $code] in $codes)} {\n"
1723 " lassign $vars msgvar optsvar\n"
1724 " if {$msgvar ne \"\"} {\n"
1725 " upvar $msgvar hmsg\n"
1726 " set hmsg $msg\n"
1727 " }\n"
1728 " if {$optsvar ne \"\"} {\n"
1729 " upvar $optsvar hopts\n"
1730 " set hopts $opts\n"
1731 " }\n"
1732 "\n"
1733 " set code [catch {uplevel 1 $script} msg opts]\n"
1734 " incr handled\n"
1735 " }\n"
1736 " } \\\n"
1737 " finally {\n"
1738 " set finalcode [catch {uplevel 1 $codes} finalmsg finalopts]\n"
1739 " if {$finalcode} {\n"
1740 "\n"
1741 " set code $finalcode\n"
1742 " set msg $finalmsg\n"
1743 " set opts $finalopts\n"
1744 " }\n"
1745 " break\n"
1746 " } \\\n"
1747 " default {\n"
1748 " return -code error \"try: expected 'on' or 'finally', got '$on'\"\n"
1749 " }\n"
1750 " }\n"
1751 "\n"
1752 " if {$code} {\n"
1753 " incr opts(-level)\n"
1754 " return {*}$opts $msg\n"
1755 " }\n"
1756 " return $msg\n"
1757 "}\n"
1758 "\n"
1759 "\n"
1760 "\n"
1761 "proc throw {code {msg \"\"}} {\n"
1762 " return -code $code $msg\n"
1763 "}\n"
1764 "\n"
1765 "\n"
1766 "proc {file delete force} {path} {\n"
1767 " foreach e [readdir $path] {\n"
1768 " file delete -force $path/$e\n"
1769 " }\n"
1770 " file delete $path\n"
1771 "}\n"
1776 #include <stdio.h>
1777 #include <string.h>
1778 #include <errno.h>
1779 #include <fcntl.h>
1780 #ifdef HAVE_UNISTD_H
1781 #include <unistd.h>
1782 #include <sys/stat.h>
1783 #endif
1786 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
1787 #include <sys/socket.h>
1788 #include <netinet/in.h>
1789 #include <arpa/inet.h>
1790 #include <netdb.h>
1791 #ifdef HAVE_SYS_UN_H
1792 #include <sys/un.h>
1793 #endif
1794 #else
1795 #define JIM_ANSIC
1796 #endif
1799 #define AIO_CMD_LEN 32
1800 #define AIO_BUF_LEN 256
1802 #ifndef HAVE_FTELLO
1803 #define ftello ftell
1804 #endif
1805 #ifndef HAVE_FSEEKO
1806 #define fseeko fseek
1807 #endif
1809 #define AIO_KEEPOPEN 1
1811 #if defined(JIM_IPV6)
1812 #define IPV6 1
1813 #else
1814 #define IPV6 0
1815 #ifndef PF_INET6
1816 #define PF_INET6 0
1817 #endif
1818 #endif
1821 typedef struct AioFile
1823 FILE *fp;
1824 Jim_Obj *filename;
1825 int type;
1826 int openFlags;
1827 int fd;
1828 Jim_Obj *rEvent;
1829 Jim_Obj *wEvent;
1830 Jim_Obj *eEvent;
1831 int addr_family;
1832 } AioFile;
1834 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
1835 static int JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1836 const char *hdlfmt, int family, const char *mode);
1839 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
1841 if (name) {
1842 Jim_SetResultFormatted(interp, "%#s: %s", name, strerror(errno));
1844 else {
1845 Jim_SetResultString(interp, strerror(errno), -1);
1849 static void JimAioDelProc(Jim_Interp *interp, void *privData)
1851 AioFile *af = privData;
1853 JIM_NOTUSED(interp);
1855 Jim_DecrRefCount(interp, af->filename);
1857 #ifdef jim_ext_eventloop
1859 Jim_DeleteFileHandler(interp, af->fp, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
1860 #endif
1862 if (!(af->openFlags & AIO_KEEPOPEN)) {
1863 fclose(af->fp);
1866 Jim_Free(af);
1869 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
1871 if (!ferror(af->fp)) {
1872 return JIM_OK;
1874 clearerr(af->fp);
1876 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
1877 return JIM_OK;
1879 #ifdef ECONNRESET
1880 if (errno == ECONNRESET) {
1881 return JIM_OK;
1883 #endif
1884 #ifdef ECONNABORTED
1885 if (errno != ECONNABORTED) {
1886 return JIM_OK;
1888 #endif
1889 JimAioSetError(interp, af->filename);
1890 return JIM_ERR;
1893 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1895 AioFile *af = Jim_CmdPrivData(interp);
1896 char buf[AIO_BUF_LEN];
1897 Jim_Obj *objPtr;
1898 int nonewline = 0;
1899 jim_wide neededLen = -1;
1901 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
1902 nonewline = 1;
1903 argv++;
1904 argc--;
1906 if (argc == 1) {
1907 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
1908 return JIM_ERR;
1909 if (neededLen < 0) {
1910 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
1911 return JIM_ERR;
1914 else if (argc) {
1915 return -1;
1917 objPtr = Jim_NewStringObj(interp, NULL, 0);
1918 while (neededLen != 0) {
1919 int retval;
1920 int readlen;
1922 if (neededLen == -1) {
1923 readlen = AIO_BUF_LEN;
1925 else {
1926 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
1928 retval = fread(buf, 1, readlen, af->fp);
1929 if (retval > 0) {
1930 Jim_AppendString(interp, objPtr, buf, retval);
1931 if (neededLen != -1) {
1932 neededLen -= retval;
1935 if (retval != readlen)
1936 break;
1939 if (JimCheckStreamError(interp, af)) {
1940 Jim_FreeNewObj(interp, objPtr);
1941 return JIM_ERR;
1943 if (nonewline) {
1944 int len;
1945 const char *s = Jim_GetString(objPtr, &len);
1947 if (len > 0 && s[len - 1] == '\n') {
1948 objPtr->length--;
1949 objPtr->bytes[objPtr->length] = '\0';
1952 Jim_SetResult(interp, objPtr);
1953 return JIM_OK;
1956 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1958 AioFile *af = Jim_CmdPrivData(interp);
1959 jim_wide count = 0;
1960 jim_wide maxlen = JIM_WIDE_MAX;
1961 FILE *outfh = Jim_AioFilehandle(interp, argv[0]);
1963 if (outfh == NULL) {
1964 return JIM_ERR;
1967 if (argc == 2) {
1968 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
1969 return JIM_ERR;
1973 while (count < maxlen) {
1974 int ch = fgetc(af->fp);
1976 if (ch == EOF || fputc(ch, outfh) == EOF) {
1977 break;
1979 count++;
1982 if (ferror(af->fp)) {
1983 Jim_SetResultFormatted(interp, "error while reading: %s", strerror(errno));
1984 clearerr(af->fp);
1985 return JIM_ERR;
1988 if (ferror(outfh)) {
1989 Jim_SetResultFormatted(interp, "error while writing: %s", strerror(errno));
1990 clearerr(outfh);
1991 return JIM_ERR;
1994 Jim_SetResultInt(interp, count);
1996 return JIM_OK;
1999 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2001 AioFile *af = Jim_CmdPrivData(interp);
2002 char buf[AIO_BUF_LEN];
2003 Jim_Obj *objPtr;
2004 int len;
2006 errno = 0;
2008 objPtr = Jim_NewStringObj(interp, NULL, 0);
2009 while (1) {
2010 buf[AIO_BUF_LEN - 1] = '_';
2011 if (fgets(buf, AIO_BUF_LEN, af->fp) == NULL)
2012 break;
2014 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
2015 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
2017 else {
2018 len = strlen(buf);
2020 if (len && (buf[len - 1] == '\n')) {
2022 len--;
2025 Jim_AppendString(interp, objPtr, buf, len);
2026 break;
2029 if (JimCheckStreamError(interp, af)) {
2031 Jim_FreeNewObj(interp, objPtr);
2032 return JIM_ERR;
2035 if (argc) {
2036 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
2037 Jim_FreeNewObj(interp, objPtr);
2038 return JIM_ERR;
2041 len = Jim_Length(objPtr);
2043 if (len == 0 && feof(af->fp)) {
2045 len = -1;
2047 Jim_SetResultInt(interp, len);
2049 else {
2050 Jim_SetResult(interp, objPtr);
2052 return JIM_OK;
2055 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2057 AioFile *af = Jim_CmdPrivData(interp);
2058 int wlen;
2059 const char *wdata;
2060 Jim_Obj *strObj;
2062 if (argc == 2) {
2063 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
2064 return -1;
2066 strObj = argv[1];
2068 else {
2069 strObj = argv[0];
2072 wdata = Jim_GetString(strObj, &wlen);
2073 if (fwrite(wdata, 1, wlen, af->fp) == (unsigned)wlen) {
2074 if (argc == 2 || putc('\n', af->fp) != EOF) {
2075 return JIM_OK;
2078 JimAioSetError(interp, af->filename);
2079 return JIM_ERR;
2082 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2084 #ifdef HAVE_ISATTY
2085 AioFile *af = Jim_CmdPrivData(interp);
2086 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
2087 #else
2088 Jim_SetResultInt(interp, 0);
2089 #endif
2091 return JIM_OK;
2095 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2097 AioFile *af = Jim_CmdPrivData(interp);
2099 if (fflush(af->fp) == EOF) {
2100 JimAioSetError(interp, af->filename);
2101 return JIM_ERR;
2103 return JIM_OK;
2106 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2108 AioFile *af = Jim_CmdPrivData(interp);
2110 Jim_SetResultInt(interp, feof(af->fp));
2111 return JIM_OK;
2114 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2116 if (argc == 3) {
2117 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
2118 static const char * const options[] = { "r", "w", NULL };
2119 enum { OPT_R, OPT_W, };
2120 int option;
2121 AioFile *af = Jim_CmdPrivData(interp);
2123 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2124 return JIM_ERR;
2126 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
2127 return JIM_OK;
2129 JimAioSetError(interp, NULL);
2130 #else
2131 Jim_SetResultString(interp, "async close not supported", -1);
2132 #endif
2133 return JIM_ERR;
2136 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
2139 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2141 AioFile *af = Jim_CmdPrivData(interp);
2142 int orig = SEEK_SET;
2143 jim_wide offset;
2145 if (argc == 2) {
2146 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
2147 orig = SEEK_SET;
2148 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
2149 orig = SEEK_CUR;
2150 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
2151 orig = SEEK_END;
2152 else {
2153 return -1;
2156 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
2157 return JIM_ERR;
2159 if (fseeko(af->fp, offset, orig) == -1) {
2160 JimAioSetError(interp, af->filename);
2161 return JIM_ERR;
2163 return JIM_OK;
2166 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2168 AioFile *af = Jim_CmdPrivData(interp);
2170 Jim_SetResultInt(interp, ftello(af->fp));
2171 return JIM_OK;
2174 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2176 AioFile *af = Jim_CmdPrivData(interp);
2178 Jim_SetResult(interp, af->filename);
2179 return JIM_OK;
2182 #ifdef O_NDELAY
2183 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2185 AioFile *af = Jim_CmdPrivData(interp);
2187 int fmode = fcntl(af->fd, F_GETFL);
2189 if (argc) {
2190 long nb;
2192 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
2193 return JIM_ERR;
2195 if (nb) {
2196 fmode |= O_NDELAY;
2198 else {
2199 fmode &= ~O_NDELAY;
2201 (void)fcntl(af->fd, F_SETFL, fmode);
2203 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
2204 return JIM_OK;
2206 #endif
2208 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2210 AioFile *af = Jim_CmdPrivData(interp);
2212 static const char * const options[] = {
2213 "none",
2214 "line",
2215 "full",
2216 NULL
2218 enum
2220 OPT_NONE,
2221 OPT_LINE,
2222 OPT_FULL,
2224 int option;
2226 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2227 return JIM_ERR;
2229 switch (option) {
2230 case OPT_NONE:
2231 setvbuf(af->fp, NULL, _IONBF, 0);
2232 break;
2233 case OPT_LINE:
2234 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
2235 break;
2236 case OPT_FULL:
2237 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
2238 break;
2240 return JIM_OK;
2243 #ifdef jim_ext_eventloop
2244 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
2246 Jim_Obj **objPtrPtr = clientData;
2248 Jim_DecrRefCount(interp, *objPtrPtr);
2249 *objPtrPtr = NULL;
2252 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
2254 Jim_Obj **objPtrPtr = clientData;
2256 return Jim_EvalObjBackground(interp, *objPtrPtr);
2259 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
2260 int argc, Jim_Obj * const *argv)
2262 if (argc == 0) {
2264 if (*scriptHandlerObj) {
2265 Jim_SetResult(interp, *scriptHandlerObj);
2267 return JIM_OK;
2270 if (*scriptHandlerObj) {
2272 Jim_DeleteFileHandler(interp, af->fp, mask);
2276 if (Jim_Length(argv[0]) == 0) {
2278 return JIM_OK;
2282 Jim_IncrRefCount(argv[0]);
2283 *scriptHandlerObj = argv[0];
2285 Jim_CreateFileHandler(interp, af->fp, mask,
2286 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
2288 return JIM_OK;
2291 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2293 AioFile *af = Jim_CmdPrivData(interp);
2295 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
2298 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2300 AioFile *af = Jim_CmdPrivData(interp);
2302 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
2305 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2307 AioFile *af = Jim_CmdPrivData(interp);
2309 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
2311 #endif
2313 static const jim_subcmd_type aio_command_table[] = {
2314 { "read",
2315 "?-nonewline? ?len?",
2316 aio_cmd_read,
2321 { "copyto",
2322 "handle ?size?",
2323 aio_cmd_copy,
2328 { "gets",
2329 "?var?",
2330 aio_cmd_gets,
2335 { "puts",
2336 "?-nonewline? str",
2337 aio_cmd_puts,
2342 { "isatty",
2343 NULL,
2344 aio_cmd_isatty,
2349 { "flush",
2350 NULL,
2351 aio_cmd_flush,
2356 { "eof",
2357 NULL,
2358 aio_cmd_eof,
2363 { "close",
2364 "?r(ead)|w(rite)?",
2365 aio_cmd_close,
2368 JIM_MODFLAG_FULLARGV,
2371 { "seek",
2372 "offset ?start|current|end",
2373 aio_cmd_seek,
2378 { "tell",
2379 NULL,
2380 aio_cmd_tell,
2385 { "filename",
2386 NULL,
2387 aio_cmd_filename,
2392 #ifdef O_NDELAY
2393 { "ndelay",
2394 "?0|1?",
2395 aio_cmd_ndelay,
2400 #endif
2401 { "buffering",
2402 "none|line|full",
2403 aio_cmd_buffering,
2408 #ifdef jim_ext_eventloop
2409 { "readable",
2410 "?readable-script?",
2411 aio_cmd_readable,
2416 { "writable",
2417 "?writable-script?",
2418 aio_cmd_writable,
2423 { "onexception",
2424 "?exception-script?",
2425 aio_cmd_onexception,
2430 #endif
2431 { NULL }
2434 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2436 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
2439 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
2440 Jim_Obj *const *argv)
2442 const char *mode;
2444 if (argc != 2 && argc != 3) {
2445 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
2446 return JIM_ERR;
2449 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
2451 #ifdef jim_ext_tclcompat
2453 const char *filename = Jim_String(argv[1]);
2456 if (*filename == '|') {
2457 Jim_Obj *evalObj[3];
2459 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
2460 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
2461 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
2463 return Jim_EvalObjVector(interp, 3, evalObj);
2466 #endif
2467 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode);
2470 static int JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
2471 const char *hdlfmt, int family, const char *mode)
2473 AioFile *af;
2474 char buf[AIO_CMD_LEN];
2475 int openFlags = 0;
2477 if (fh) {
2478 filename = Jim_NewStringObj(interp, hdlfmt, -1);
2479 openFlags = AIO_KEEPOPEN;
2482 Jim_IncrRefCount(filename);
2484 if (fh == NULL) {
2485 #if !defined(JIM_ANSIC)
2486 if (fd >= 0) {
2487 fh = fdopen(fd, mode);
2489 else
2490 #endif
2491 fh = fopen(Jim_String(filename), mode);
2493 if (fh == NULL) {
2494 JimAioSetError(interp, filename);
2495 #if !defined(JIM_ANSIC)
2496 if (fd >= 0) {
2497 close(fd);
2499 #endif
2500 Jim_DecrRefCount(interp, filename);
2501 return JIM_ERR;
2506 af = Jim_Alloc(sizeof(*af));
2507 memset(af, 0, sizeof(*af));
2508 af->fp = fh;
2509 af->fd = fileno(fh);
2510 af->filename = filename;
2511 #ifdef FD_CLOEXEC
2512 if ((openFlags & AIO_KEEPOPEN) == 0) {
2513 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
2515 #endif
2516 af->openFlags = openFlags;
2517 af->addr_family = family;
2518 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2519 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
2521 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
2523 return JIM_OK;
2526 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
2527 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
2528 const char *hdlfmt, int family, const char *mode[2])
2530 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0]) == JIM_OK) {
2531 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
2532 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2534 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1]) == JIM_OK) {
2535 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2536 Jim_SetResult(interp, objPtr);
2537 return JIM_OK;
2542 close(p[0]);
2543 close(p[1]);
2544 JimAioSetError(interp, NULL);
2545 return JIM_ERR;
2547 #endif
2550 int Jim_MakeTempFile(Jim_Interp *interp, const char *template)
2552 #ifdef HAVE_MKSTEMP
2553 int fd;
2554 mode_t mask;
2555 Jim_Obj *filenameObj;
2557 if (template == NULL) {
2558 const char *tmpdir = getenv("TMPDIR");
2559 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
2560 tmpdir = "/tmp/";
2562 filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
2563 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
2564 Jim_AppendString(interp, filenameObj, "/", 1);
2566 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
2568 else {
2569 filenameObj = Jim_NewStringObj(interp, template, -1);
2572 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
2575 fd = mkstemp(filenameObj->bytes);
2576 umask(mask);
2577 if (fd < 0) {
2578 JimAioSetError(interp, filenameObj);
2579 Jim_FreeNewObj(interp, filenameObj);
2580 return -1;
2583 Jim_SetResult(interp, filenameObj);
2584 return fd;
2585 #else
2586 Jim_SetResultString(interp, "platform has no tempfile support", -1);
2587 return -1;
2588 #endif
2591 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
2593 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
2596 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
2597 return ((AioFile *) cmdPtr->u.native.privData)->fp;
2599 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
2600 return NULL;
2603 int Jim_aioInit(Jim_Interp *interp)
2605 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2606 return JIM_ERR;
2608 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2609 #ifndef JIM_ANSIC
2610 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2611 #endif
2614 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
2615 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
2616 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
2618 return JIM_OK;
2621 #include <errno.h>
2622 #include <stdio.h>
2623 #include <string.h>
2626 #ifdef HAVE_DIRENT_H
2627 #include <dirent.h>
2628 #endif
2630 int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2632 const char *dirPath;
2633 DIR *dirPtr;
2634 struct dirent *entryPtr;
2635 int nocomplain = 0;
2637 if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) {
2638 nocomplain = 1;
2640 if (argc != 2 && !nocomplain) {
2641 Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
2642 return JIM_ERR;
2645 dirPath = Jim_String(argv[1 + nocomplain]);
2647 dirPtr = opendir(dirPath);
2648 if (dirPtr == NULL) {
2649 if (nocomplain) {
2650 return JIM_OK;
2652 Jim_SetResultString(interp, strerror(errno), -1);
2653 return JIM_ERR;
2655 else {
2656 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
2658 while ((entryPtr = readdir(dirPtr)) != NULL) {
2659 if (entryPtr->d_name[0] == '.') {
2660 if (entryPtr->d_name[1] == '\0') {
2661 continue;
2663 if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0'))
2664 continue;
2666 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1));
2668 closedir(dirPtr);
2670 Jim_SetResult(interp, listObj);
2672 return JIM_OK;
2676 int Jim_readdirInit(Jim_Interp *interp)
2678 if (Jim_PackageProvide(interp, "readdir", "1.0", JIM_ERRMSG))
2679 return JIM_ERR;
2681 Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
2682 return JIM_OK;
2685 #include <stdlib.h>
2686 #include <string.h>
2688 #if defined(JIM_REGEXP)
2689 #else
2690 #include <regex.h>
2691 #endif
2693 static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2695 regfree(objPtr->internalRep.regexpValue.compre);
2696 Jim_Free(objPtr->internalRep.regexpValue.compre);
2699 static const Jim_ObjType regexpObjType = {
2700 "regexp",
2701 FreeRegexpInternalRep,
2702 NULL,
2703 NULL,
2704 JIM_TYPE_NONE
2707 static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
2709 regex_t *compre;
2710 const char *pattern;
2711 int ret;
2714 if (objPtr->typePtr == &regexpObjType &&
2715 objPtr->internalRep.regexpValue.compre && objPtr->internalRep.regexpValue.flags == flags) {
2717 return objPtr->internalRep.regexpValue.compre;
2723 pattern = Jim_String(objPtr);
2724 compre = Jim_Alloc(sizeof(regex_t));
2726 if ((ret = regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
2727 char buf[100];
2729 regerror(ret, compre, buf, sizeof(buf));
2730 Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
2731 regfree(compre);
2732 Jim_Free(compre);
2733 return NULL;
2736 Jim_FreeIntRep(interp, objPtr);
2738 objPtr->typePtr = &regexpObjType;
2739 objPtr->internalRep.regexpValue.flags = flags;
2740 objPtr->internalRep.regexpValue.compre = compre;
2742 return compre;
2745 int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2747 int opt_indices = 0;
2748 int opt_all = 0;
2749 int opt_inline = 0;
2750 regex_t *regex;
2751 int match, i, j;
2752 int offset = 0;
2753 regmatch_t *pmatch = NULL;
2754 int source_len;
2755 int result = JIM_OK;
2756 const char *pattern;
2757 const char *source_str;
2758 int num_matches = 0;
2759 int num_vars;
2760 Jim_Obj *resultListObj = NULL;
2761 int regcomp_flags = 0;
2762 int eflags = 0;
2763 int option;
2764 enum {
2765 OPT_INDICES, OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
2767 static const char * const options[] = {
2768 "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
2771 if (argc < 3) {
2772 wrongNumArgs:
2773 Jim_WrongNumArgs(interp, 1, argv,
2774 "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");
2775 return JIM_ERR;
2778 for (i = 1; i < argc; i++) {
2779 const char *opt = Jim_String(argv[i]);
2781 if (*opt != '-') {
2782 break;
2784 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
2785 return JIM_ERR;
2787 if (option == OPT_END) {
2788 i++;
2789 break;
2791 switch (option) {
2792 case OPT_INDICES:
2793 opt_indices = 1;
2794 break;
2796 case OPT_NOCASE:
2797 regcomp_flags |= REG_ICASE;
2798 break;
2800 case OPT_LINE:
2801 regcomp_flags |= REG_NEWLINE;
2802 break;
2804 case OPT_ALL:
2805 opt_all = 1;
2806 break;
2808 case OPT_INLINE:
2809 opt_inline = 1;
2810 break;
2812 case OPT_START:
2813 if (++i == argc) {
2814 goto wrongNumArgs;
2816 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
2817 return JIM_ERR;
2819 break;
2822 if (argc - i < 2) {
2823 goto wrongNumArgs;
2826 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
2827 if (!regex) {
2828 return JIM_ERR;
2831 pattern = Jim_String(argv[i]);
2832 source_str = Jim_GetString(argv[i + 1], &source_len);
2834 num_vars = argc - i - 2;
2836 if (opt_inline) {
2837 if (num_vars) {
2838 Jim_SetResultString(interp, "regexp match variables not allowed when using -inline",
2839 -1);
2840 result = JIM_ERR;
2841 goto done;
2843 num_vars = regex->re_nsub + 1;
2846 pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch));
2848 if (offset) {
2849 if (offset < 0) {
2850 offset += source_len + 1;
2852 if (offset > source_len) {
2853 source_str += source_len;
2855 else if (offset > 0) {
2856 source_str += offset;
2858 eflags |= REG_NOTBOL;
2861 if (opt_inline) {
2862 resultListObj = Jim_NewListObj(interp, NULL, 0);
2865 next_match:
2866 match = regexec(regex, source_str, num_vars + 1, pmatch, eflags);
2867 if (match >= REG_BADPAT) {
2868 char buf[100];
2870 regerror(match, regex, buf, sizeof(buf));
2871 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
2872 result = JIM_ERR;
2873 goto done;
2876 if (match == REG_NOMATCH) {
2877 goto done;
2880 num_matches++;
2882 if (opt_all && !opt_inline) {
2884 goto try_next_match;
2888 j = 0;
2889 for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
2890 Jim_Obj *resultObj;
2892 if (opt_indices) {
2893 resultObj = Jim_NewListObj(interp, NULL, 0);
2895 else {
2896 resultObj = Jim_NewStringObj(interp, "", 0);
2899 if (pmatch[j].rm_so == -1) {
2900 if (opt_indices) {
2901 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
2902 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
2905 else {
2906 int len = pmatch[j].rm_eo - pmatch[j].rm_so;
2908 if (opt_indices) {
2909 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
2910 offset + pmatch[j].rm_so));
2911 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
2912 offset + pmatch[j].rm_so + len - 1));
2914 else {
2915 Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, len);
2919 if (opt_inline) {
2920 Jim_ListAppendElement(interp, resultListObj, resultObj);
2922 else {
2924 result = Jim_SetVariable(interp, argv[i], resultObj);
2926 if (result != JIM_OK) {
2927 Jim_FreeObj(interp, resultObj);
2928 break;
2933 try_next_match:
2934 if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
2935 if (pmatch[0].rm_eo) {
2936 offset += pmatch[0].rm_eo;
2937 source_str += pmatch[0].rm_eo;
2939 else {
2940 source_str++;
2941 offset++;
2943 if (*source_str) {
2944 eflags = REG_NOTBOL;
2945 goto next_match;
2949 done:
2950 if (result == JIM_OK) {
2951 if (opt_inline) {
2952 Jim_SetResult(interp, resultListObj);
2954 else {
2955 Jim_SetResultInt(interp, num_matches);
2959 Jim_Free(pmatch);
2960 return result;
2963 #define MAX_SUB_MATCHES 50
2965 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2967 int regcomp_flags = 0;
2968 int regexec_flags = 0;
2969 int opt_all = 0;
2970 int offset = 0;
2971 regex_t *regex;
2972 const char *p;
2973 int result;
2974 regmatch_t pmatch[MAX_SUB_MATCHES + 1];
2975 int num_matches = 0;
2977 int i, j, n;
2978 Jim_Obj *varname;
2979 Jim_Obj *resultObj;
2980 const char *source_str;
2981 int source_len;
2982 const char *replace_str;
2983 int replace_len;
2984 const char *pattern;
2985 int option;
2986 enum {
2987 OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_END
2989 static const char * const options[] = {
2990 "-nocase", "-line", "-all", "-start", "--", NULL
2993 if (argc < 4) {
2994 wrongNumArgs:
2995 Jim_WrongNumArgs(interp, 1, argv,
2996 "?switches? exp string subSpec ?varName?");
2997 return JIM_ERR;
3000 for (i = 1; i < argc; i++) {
3001 const char *opt = Jim_String(argv[i]);
3003 if (*opt != '-') {
3004 break;
3006 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3007 return JIM_ERR;
3009 if (option == OPT_END) {
3010 i++;
3011 break;
3013 switch (option) {
3014 case OPT_NOCASE:
3015 regcomp_flags |= REG_ICASE;
3016 break;
3018 case OPT_LINE:
3019 regcomp_flags |= REG_NEWLINE;
3020 break;
3022 case OPT_ALL:
3023 opt_all = 1;
3024 break;
3026 case OPT_START:
3027 if (++i == argc) {
3028 goto wrongNumArgs;
3030 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3031 return JIM_ERR;
3033 break;
3036 if (argc - i != 3 && argc - i != 4) {
3037 goto wrongNumArgs;
3040 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
3041 if (!regex) {
3042 return JIM_ERR;
3044 pattern = Jim_String(argv[i]);
3046 source_str = Jim_GetString(argv[i + 1], &source_len);
3047 replace_str = Jim_GetString(argv[i + 2], &replace_len);
3048 varname = argv[i + 3];
3051 resultObj = Jim_NewStringObj(interp, "", 0);
3053 if (offset) {
3054 if (offset < 0) {
3055 offset += source_len + 1;
3057 if (offset > source_len) {
3058 offset = source_len;
3060 else if (offset < 0) {
3061 offset = 0;
3066 Jim_AppendString(interp, resultObj, source_str, offset);
3069 n = source_len - offset;
3070 p = source_str + offset;
3071 do {
3072 int match = regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
3074 if (match >= REG_BADPAT) {
3075 char buf[100];
3077 regerror(match, regex, buf, sizeof(buf));
3078 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3079 return JIM_ERR;
3081 if (match == REG_NOMATCH) {
3082 break;
3085 num_matches++;
3087 Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
3090 for (j = 0; j < replace_len; j++) {
3091 int idx;
3092 int c = replace_str[j];
3094 if (c == '&') {
3095 idx = 0;
3097 else if (c == '\\' && j < replace_len) {
3098 c = replace_str[++j];
3099 if ((c >= '0') && (c <= '9')) {
3100 idx = c - '0';
3102 else if ((c == '\\') || (c == '&')) {
3103 Jim_AppendString(interp, resultObj, replace_str + j, 1);
3104 continue;
3106 else {
3107 Jim_AppendString(interp, resultObj, replace_str + j - 1, 2);
3108 continue;
3111 else {
3112 Jim_AppendString(interp, resultObj, replace_str + j, 1);
3113 continue;
3115 if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
3116 Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
3117 pmatch[idx].rm_eo - pmatch[idx].rm_so);
3121 p += pmatch[0].rm_eo;
3122 n -= pmatch[0].rm_eo;
3125 if (!opt_all || n == 0) {
3126 break;
3130 if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
3131 break;
3135 if (pattern[0] == '\0' && n) {
3137 Jim_AppendString(interp, resultObj, p, 1);
3138 p++;
3139 n--;
3142 regexec_flags |= REG_NOTBOL;
3143 } while (n);
3145 Jim_AppendString(interp, resultObj, p, -1);
3148 if (argc - i == 4) {
3149 result = Jim_SetVariable(interp, varname, resultObj);
3151 if (result == JIM_OK) {
3152 Jim_SetResultInt(interp, num_matches);
3154 else {
3155 Jim_FreeObj(interp, resultObj);
3158 else {
3159 Jim_SetResult(interp, resultObj);
3160 result = JIM_OK;
3163 return result;
3166 int Jim_regexpInit(Jim_Interp *interp)
3168 if (Jim_PackageProvide(interp, "regexp", "1.0", JIM_ERRMSG))
3169 return JIM_ERR;
3171 Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
3172 Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
3173 return JIM_OK;
3176 #include <limits.h>
3177 #include <stdlib.h>
3178 #include <string.h>
3179 #include <stdio.h>
3180 #include <errno.h>
3181 #include <sys/stat.h>
3184 #ifdef HAVE_UTIMES
3185 #include <sys/time.h>
3186 #endif
3187 #ifdef HAVE_UNISTD_H
3188 #include <unistd.h>
3189 #elif defined(_MSC_VER)
3190 #include <direct.h>
3191 #define F_OK 0
3192 #define W_OK 2
3193 #define R_OK 4
3194 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
3195 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
3196 #endif
3198 # ifndef MAXPATHLEN
3199 # define MAXPATHLEN JIM_PATH_LEN
3200 # endif
3202 #if defined(__MINGW32__) || defined(_MSC_VER)
3203 #define ISWINDOWS 1
3204 #else
3205 #define ISWINDOWS 0
3206 #endif
3209 static const char *JimGetFileType(int mode)
3211 if (S_ISREG(mode)) {
3212 return "file";
3214 else if (S_ISDIR(mode)) {
3215 return "directory";
3217 #ifdef S_ISCHR
3218 else if (S_ISCHR(mode)) {
3219 return "characterSpecial";
3221 #endif
3222 #ifdef S_ISBLK
3223 else if (S_ISBLK(mode)) {
3224 return "blockSpecial";
3226 #endif
3227 #ifdef S_ISFIFO
3228 else if (S_ISFIFO(mode)) {
3229 return "fifo";
3231 #endif
3232 #ifdef S_ISLNK
3233 else if (S_ISLNK(mode)) {
3234 return "link";
3236 #endif
3237 #ifdef S_ISSOCK
3238 else if (S_ISSOCK(mode)) {
3239 return "socket";
3241 #endif
3242 return "unknown";
3245 static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
3247 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
3248 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
3251 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
3254 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
3256 AppendStatElement(interp, listObj, "dev", sb->st_dev);
3257 AppendStatElement(interp, listObj, "ino", sb->st_ino);
3258 AppendStatElement(interp, listObj, "mode", sb->st_mode);
3259 AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
3260 AppendStatElement(interp, listObj, "uid", sb->st_uid);
3261 AppendStatElement(interp, listObj, "gid", sb->st_gid);
3262 AppendStatElement(interp, listObj, "size", sb->st_size);
3263 AppendStatElement(interp, listObj, "atime", sb->st_atime);
3264 AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
3265 AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
3266 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
3267 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));
3270 if (varName) {
3271 Jim_Obj *objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
3272 if (objPtr) {
3273 if (Jim_DictSize(interp, objPtr) < 0) {
3275 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
3276 Jim_FreeNewObj(interp, listObj);
3277 return JIM_ERR;
3280 if (Jim_IsShared(objPtr))
3281 objPtr = Jim_DuplicateObj(interp, objPtr);
3284 Jim_ListAppendList(interp, objPtr, listObj);
3285 Jim_DictSize(interp, objPtr);
3286 Jim_InvalidateStringRep(objPtr);
3288 Jim_FreeNewObj(interp, listObj);
3289 listObj = objPtr;
3291 Jim_SetVariable(interp, varName, listObj);
3295 Jim_SetResult(interp, listObj);
3297 return JIM_OK;
3300 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3302 const char *path = Jim_String(argv[0]);
3303 const char *p = strrchr(path, '/');
3305 if (!p && path[0] == '.' && path[1] == '.' && path[2] == '\0') {
3306 Jim_SetResultString(interp, "..", -1);
3307 } else if (!p) {
3308 Jim_SetResultString(interp, ".", -1);
3310 else if (p == path) {
3311 Jim_SetResultString(interp, "/", -1);
3313 else if (ISWINDOWS && p[-1] == ':') {
3315 Jim_SetResultString(interp, path, p - path + 1);
3317 else {
3318 Jim_SetResultString(interp, path, p - path);
3320 return JIM_OK;
3323 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3325 const char *path = Jim_String(argv[0]);
3326 const char *lastSlash = strrchr(path, '/');
3327 const char *p = strrchr(path, '.');
3329 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
3330 Jim_SetResult(interp, argv[0]);
3332 else {
3333 Jim_SetResultString(interp, path, p - path);
3335 return JIM_OK;
3338 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3340 const char *path = Jim_String(argv[0]);
3341 const char *lastSlash = strrchr(path, '/');
3342 const char *p = strrchr(path, '.');
3344 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
3345 p = "";
3347 Jim_SetResultString(interp, p, -1);
3348 return JIM_OK;
3351 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3353 const char *path = Jim_String(argv[0]);
3354 const char *lastSlash = strrchr(path, '/');
3356 if (lastSlash) {
3357 Jim_SetResultString(interp, lastSlash + 1, -1);
3359 else {
3360 Jim_SetResult(interp, argv[0]);
3362 return JIM_OK;
3365 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3367 #ifdef HAVE_REALPATH
3368 const char *path = Jim_String(argv[0]);
3369 char *newname = Jim_Alloc(MAXPATHLEN + 1);
3371 if (realpath(path, newname)) {
3372 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
3373 return JIM_OK;
3375 else {
3376 Jim_Free(newname);
3377 Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
3378 return JIM_ERR;
3380 #else
3381 Jim_SetResultString(interp, "Not implemented", -1);
3382 return JIM_ERR;
3383 #endif
3386 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3388 int i;
3389 char *newname = Jim_Alloc(MAXPATHLEN + 1);
3390 char *last = newname;
3392 *newname = 0;
3395 for (i = 0; i < argc; i++) {
3396 int len;
3397 const char *part = Jim_GetString(argv[i], &len);
3399 if (*part == '/') {
3401 last = newname;
3403 else if (ISWINDOWS && strchr(part, ':')) {
3405 last = newname;
3407 else if (part[0] == '.') {
3408 if (part[1] == '/') {
3409 part += 2;
3410 len -= 2;
3412 else if (part[1] == 0 && last != newname) {
3414 continue;
3419 if (last != newname && last[-1] != '/') {
3420 *last++ = '/';
3423 if (len) {
3424 if (last + len - newname >= MAXPATHLEN) {
3425 Jim_Free(newname);
3426 Jim_SetResultString(interp, "Path too long", -1);
3427 return JIM_ERR;
3429 memcpy(last, part, len);
3430 last += len;
3434 if (last > newname + 1 && last[-1] == '/') {
3436 if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
3437 *--last = 0;
3442 *last = 0;
3446 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
3448 return JIM_OK;
3451 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
3453 Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1);
3455 return JIM_OK;
3458 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3460 return file_access(interp, argv[0], R_OK);
3463 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3465 return file_access(interp, argv[0], W_OK);
3468 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3470 #ifdef X_OK
3471 return file_access(interp, argv[0], X_OK);
3472 #else
3474 Jim_SetResultBool(interp, 1);
3475 return JIM_OK;
3476 #endif
3479 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3481 return file_access(interp, argv[0], F_OK);
3484 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3486 int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
3488 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
3489 argc++;
3490 argv--;
3493 while (argc--) {
3494 const char *path = Jim_String(argv[0]);
3496 if (unlink(path) == -1 && errno != ENOENT) {
3497 if (rmdir(path) == -1) {
3499 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
3500 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
3501 strerror(errno));
3502 return JIM_ERR;
3506 argv++;
3508 return JIM_OK;
3511 #ifdef HAVE_MKDIR_ONE_ARG
3512 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
3513 #else
3514 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
3515 #endif
3517 static int mkdir_all(char *path)
3519 int ok = 1;
3522 goto first;
3524 while (ok--) {
3527 char *slash = strrchr(path, '/');
3529 if (slash && slash != path) {
3530 *slash = 0;
3531 if (mkdir_all(path) != 0) {
3532 return -1;
3534 *slash = '/';
3537 first:
3538 if (MKDIR_DEFAULT(path) == 0) {
3539 return 0;
3541 if (errno == ENOENT) {
3543 continue;
3546 if (errno == EEXIST) {
3547 struct stat sb;
3549 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
3550 return 0;
3553 errno = EEXIST;
3556 break;
3558 return -1;
3561 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3563 while (argc--) {
3564 char *path = Jim_StrDup(Jim_String(argv[0]));
3565 int rc = mkdir_all(path);
3567 Jim_Free(path);
3568 if (rc != 0) {
3569 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
3570 strerror(errno));
3571 return JIM_ERR;
3573 argv++;
3575 return JIM_OK;
3578 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3580 int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL);
3582 if (fd < 0) {
3583 return JIM_ERR;
3585 close(fd);
3587 return JIM_OK;
3590 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3592 const char *source;
3593 const char *dest;
3594 int force = 0;
3596 if (argc == 3) {
3597 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
3598 return -1;
3600 force++;
3601 argv++;
3602 argc--;
3605 source = Jim_String(argv[0]);
3606 dest = Jim_String(argv[1]);
3608 if (!force && access(dest, F_OK) == 0) {
3609 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
3610 argv[1]);
3611 return JIM_ERR;
3614 if (rename(source, dest) != 0) {
3615 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3616 strerror(errno));
3617 return JIM_ERR;
3620 return JIM_OK;
3623 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
3624 static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3626 int ret;
3627 const char *source;
3628 const char *dest;
3629 static const char * const options[] = { "-hard", "-symbolic", NULL };
3630 enum { OPT_HARD, OPT_SYMBOLIC, };
3631 int option = OPT_HARD;
3633 if (argc == 3) {
3634 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
3635 return JIM_ERR;
3637 argv++;
3638 argc--;
3641 dest = Jim_String(argv[0]);
3642 source = Jim_String(argv[1]);
3644 if (option == OPT_HARD) {
3645 ret = link(source, dest);
3647 else {
3648 ret = symlink(source, dest);
3651 if (ret != 0) {
3652 Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3653 strerror(errno));
3654 return JIM_ERR;
3657 return JIM_OK;
3659 #endif
3661 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3663 const char *path = Jim_String(filename);
3665 if (stat(path, sb) == -1) {
3666 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3667 return JIM_ERR;
3669 return JIM_OK;
3672 #ifdef HAVE_LSTAT
3673 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3675 const char *path = Jim_String(filename);
3677 if (lstat(path, sb) == -1) {
3678 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3679 return JIM_ERR;
3681 return JIM_OK;
3683 #else
3684 #define file_lstat file_stat
3685 #endif
3687 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3689 struct stat sb;
3691 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3692 return JIM_ERR;
3694 Jim_SetResultInt(interp, sb.st_atime);
3695 return JIM_OK;
3698 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3700 struct stat sb;
3702 if (argc == 2) {
3703 #ifdef HAVE_UTIMES
3704 jim_wide newtime;
3705 struct timeval times[2];
3707 if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) {
3708 return JIM_ERR;
3711 times[1].tv_sec = times[0].tv_sec = newtime;
3712 times[1].tv_usec = times[0].tv_usec = 0;
3714 if (utimes(Jim_String(argv[0]), times) != 0) {
3715 Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno));
3716 return JIM_ERR;
3718 #else
3719 Jim_SetResultString(interp, "Not implemented", -1);
3720 return JIM_ERR;
3721 #endif
3723 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3724 return JIM_ERR;
3726 Jim_SetResultInt(interp, sb.st_mtime);
3727 return JIM_OK;
3730 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3732 return Jim_EvalPrefix(interp, "file copy", argc, argv);
3735 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3737 struct stat sb;
3739 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3740 return JIM_ERR;
3742 Jim_SetResultInt(interp, sb.st_size);
3743 return JIM_OK;
3746 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3748 struct stat sb;
3749 int ret = 0;
3751 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3752 ret = S_ISDIR(sb.st_mode);
3754 Jim_SetResultInt(interp, ret);
3755 return JIM_OK;
3758 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3760 struct stat sb;
3761 int ret = 0;
3763 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3764 ret = S_ISREG(sb.st_mode);
3766 Jim_SetResultInt(interp, ret);
3767 return JIM_OK;
3770 #ifdef HAVE_GETEUID
3771 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3773 struct stat sb;
3774 int ret = 0;
3776 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3777 ret = (geteuid() == sb.st_uid);
3779 Jim_SetResultInt(interp, ret);
3780 return JIM_OK;
3782 #endif
3784 #if defined(HAVE_READLINK)
3785 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3787 const char *path = Jim_String(argv[0]);
3788 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
3790 int linkLength = readlink(path, linkValue, MAXPATHLEN);
3792 if (linkLength == -1) {
3793 Jim_Free(linkValue);
3794 Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
3795 return JIM_ERR;
3797 linkValue[linkLength] = 0;
3798 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
3799 return JIM_OK;
3801 #endif
3803 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3805 struct stat sb;
3807 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
3808 return JIM_ERR;
3810 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
3811 return JIM_OK;
3814 #ifdef HAVE_LSTAT
3815 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3817 struct stat sb;
3819 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
3820 return JIM_ERR;
3822 return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
3824 #else
3825 #define file_cmd_lstat file_cmd_stat
3826 #endif
3828 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3830 struct stat sb;
3832 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3833 return JIM_ERR;
3835 return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
3838 static const jim_subcmd_type file_command_table[] = {
3839 { "atime",
3840 "name",
3841 file_cmd_atime,
3846 { "mtime",
3847 "name ?time?",
3848 file_cmd_mtime,
3853 { "copy",
3854 "?-force? source dest",
3855 file_cmd_copy,
3860 { "dirname",
3861 "name",
3862 file_cmd_dirname,
3867 { "rootname",
3868 "name",
3869 file_cmd_rootname,
3874 { "extension",
3875 "name",
3876 file_cmd_extension,
3881 { "tail",
3882 "name",
3883 file_cmd_tail,
3888 { "normalize",
3889 "name",
3890 file_cmd_normalize,
3895 { "join",
3896 "name ?name ...?",
3897 file_cmd_join,
3902 { "readable",
3903 "name",
3904 file_cmd_readable,
3909 { "writable",
3910 "name",
3911 file_cmd_writable,
3916 { "executable",
3917 "name",
3918 file_cmd_executable,
3923 { "exists",
3924 "name",
3925 file_cmd_exists,
3930 { "delete",
3931 "?-force|--? name ...",
3932 file_cmd_delete,
3937 { "mkdir",
3938 "dir ...",
3939 file_cmd_mkdir,
3944 { "tempfile",
3945 "?template?",
3946 file_cmd_tempfile,
3951 { "rename",
3952 "?-force? source dest",
3953 file_cmd_rename,
3958 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
3959 { "link",
3960 "?-symbolic|-hard? newname target",
3961 file_cmd_link,
3966 #endif
3967 #if defined(HAVE_READLINK)
3968 { "readlink",
3969 "name",
3970 file_cmd_readlink,
3975 #endif
3976 { "size",
3977 "name",
3978 file_cmd_size,
3983 { "stat",
3984 "name ?var?",
3985 file_cmd_stat,
3990 { "lstat",
3991 "name ?var?",
3992 file_cmd_lstat,
3997 { "type",
3998 "name",
3999 file_cmd_type,
4004 #ifdef HAVE_GETEUID
4005 { "owned",
4006 "name",
4007 file_cmd_owned,
4012 #endif
4013 { "isdirectory",
4014 "name",
4015 file_cmd_isdirectory,
4020 { "isfile",
4021 "name",
4022 file_cmd_isfile,
4028 NULL
4032 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4034 const char *path;
4036 if (argc != 2) {
4037 Jim_WrongNumArgs(interp, 1, argv, "dirname");
4038 return JIM_ERR;
4041 path = Jim_String(argv[1]);
4043 if (chdir(path) != 0) {
4044 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
4045 strerror(errno));
4046 return JIM_ERR;
4048 return JIM_OK;
4051 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4053 char *cwd = Jim_Alloc(MAXPATHLEN);
4055 if (getcwd(cwd, MAXPATHLEN) == NULL) {
4056 Jim_SetResultString(interp, "Failed to get pwd", -1);
4057 Jim_Free(cwd);
4058 return JIM_ERR;
4060 else if (ISWINDOWS) {
4062 char *p = cwd;
4063 while ((p = strchr(p, '\\')) != NULL) {
4064 *p++ = '/';
4068 Jim_SetResultString(interp, cwd, -1);
4070 Jim_Free(cwd);
4071 return JIM_OK;
4074 int Jim_fileInit(Jim_Interp *interp)
4076 if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
4077 return JIM_ERR;
4079 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
4080 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
4081 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
4082 return JIM_OK;
4085 #include <string.h>
4086 #include <ctype.h>
4089 #if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
4090 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4092 Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
4093 int i, j;
4094 int rc;
4097 for (i = 1; i < argc; i++) {
4098 int len;
4099 const char *arg = Jim_GetString(argv[i], &len);
4101 if (i > 1) {
4102 Jim_AppendString(interp, cmdlineObj, " ", 1);
4104 if (strpbrk(arg, "\\\" ") == NULL) {
4106 Jim_AppendString(interp, cmdlineObj, arg, len);
4107 continue;
4110 Jim_AppendString(interp, cmdlineObj, "\"", 1);
4111 for (j = 0; j < len; j++) {
4112 if (arg[j] == '\\' || arg[j] == '"') {
4113 Jim_AppendString(interp, cmdlineObj, "\\", 1);
4115 Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
4117 Jim_AppendString(interp, cmdlineObj, "\"", 1);
4119 rc = system(Jim_String(cmdlineObj));
4121 Jim_FreeNewObj(interp, cmdlineObj);
4123 if (rc) {
4124 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
4125 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4126 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
4127 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
4128 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
4129 return JIM_ERR;
4132 return JIM_OK;
4135 int Jim_execInit(Jim_Interp *interp)
4137 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
4138 return JIM_ERR;
4140 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
4141 return JIM_OK;
4143 #else
4146 #include <errno.h>
4147 #include <signal.h>
4149 #if defined(__MINGW32__)
4151 #ifndef STRICT
4152 #define STRICT
4153 #endif
4154 #define WIN32_LEAN_AND_MEAN
4155 #include <windows.h>
4156 #include <fcntl.h>
4158 typedef HANDLE fdtype;
4159 typedef HANDLE pidtype;
4160 #define JIM_BAD_FD INVALID_HANDLE_VALUE
4161 #define JIM_BAD_PID INVALID_HANDLE_VALUE
4162 #define JimCloseFd CloseHandle
4164 #define WIFEXITED(STATUS) 1
4165 #define WEXITSTATUS(STATUS) (STATUS)
4166 #define WIFSIGNALED(STATUS) 0
4167 #define WTERMSIG(STATUS) 0
4168 #define WNOHANG 1
4170 static fdtype JimFileno(FILE *fh);
4171 static pidtype JimWaitPid(pidtype pid, int *status, int nohang);
4172 static fdtype JimDupFd(fdtype infd);
4173 static fdtype JimOpenForRead(const char *filename);
4174 static FILE *JimFdOpenForRead(fdtype fd);
4175 static int JimPipe(fdtype pipefd[2]);
4176 static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env,
4177 fdtype inputId, fdtype outputId, fdtype errorId);
4178 static int JimErrno(void);
4179 #else
4180 #include <unistd.h>
4181 #include <fcntl.h>
4182 #include <sys/wait.h>
4183 #include <sys/stat.h>
4185 typedef int fdtype;
4186 typedef int pidtype;
4187 #define JimPipe pipe
4188 #define JimErrno() errno
4189 #define JIM_BAD_FD -1
4190 #define JIM_BAD_PID -1
4191 #define JimFileno fileno
4192 #define JimReadFd read
4193 #define JimCloseFd close
4194 #define JimWaitPid waitpid
4195 #define JimDupFd dup
4196 #define JimFdOpenForRead(FD) fdopen((FD), "r")
4197 #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0)
4199 #ifndef HAVE_EXECVPE
4200 #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
4201 #endif
4202 #endif
4204 static const char *JimStrError(void);
4205 static char **JimSaveEnv(char **env);
4206 static void JimRestoreEnv(char **env);
4207 static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
4208 pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr);
4209 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr);
4210 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId);
4211 static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents, int len);
4212 static fdtype JimOpenForWrite(const char *filename, int append);
4213 static int JimRewindFd(fdtype fd);
4215 static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
4217 Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError());
4220 static const char *JimStrError(void)
4222 return strerror(JimErrno());
4225 static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
4227 int len;
4228 const char *s = Jim_GetString(objPtr, &len);
4230 if (len > 0 && s[len - 1] == '\n') {
4231 objPtr->length--;
4232 objPtr->bytes[objPtr->length] = '\0';
4236 static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj)
4238 char buf[256];
4239 FILE *fh = JimFdOpenForRead(fd);
4240 if (fh == NULL) {
4241 return JIM_ERR;
4244 while (1) {
4245 int retval = fread(buf, 1, sizeof(buf), fh);
4246 if (retval > 0) {
4247 Jim_AppendString(interp, strObj, buf, retval);
4249 if (retval != sizeof(buf)) {
4250 break;
4253 Jim_RemoveTrailingNewline(strObj);
4254 fclose(fh);
4255 return JIM_OK;
4258 static char **JimBuildEnv(Jim_Interp *interp)
4260 int i;
4261 int size;
4262 int num;
4263 int n;
4264 char **envptr;
4265 char *envdata;
4267 Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
4269 if (!objPtr) {
4270 return Jim_GetEnviron();
4275 num = Jim_ListLength(interp, objPtr);
4276 if (num % 2) {
4278 num--;
4280 size = Jim_Length(objPtr) + 2;
4282 envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
4283 envdata = (char *)&envptr[num / 2 + 1];
4285 n = 0;
4286 for (i = 0; i < num; i += 2) {
4287 const char *s1, *s2;
4288 Jim_Obj *elemObj;
4290 Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
4291 s1 = Jim_String(elemObj);
4292 Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
4293 s2 = Jim_String(elemObj);
4295 envptr[n] = envdata;
4296 envdata += sprintf(envdata, "%s=%s", s1, s2);
4297 envdata++;
4298 n++;
4300 envptr[n] = NULL;
4301 *envdata = 0;
4303 return envptr;
4306 static void JimFreeEnv(char **env, char **original_environ)
4308 if (env != original_environ) {
4309 Jim_Free(env);
4313 static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus)
4315 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
4316 int rc = JIM_ERR;
4318 if (WIFEXITED(waitStatus)) {
4319 if (WEXITSTATUS(waitStatus) == 0) {
4320 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
4321 rc = JIM_OK;
4323 else {
4324 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4325 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4326 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
4329 else {
4330 const char *type;
4331 const char *action;
4333 if (WIFSIGNALED(waitStatus)) {
4334 type = "CHILDKILLED";
4335 action = "killed";
4337 else {
4338 type = "CHILDSUSP";
4339 action = "suspended";
4342 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
4344 #ifdef jim_ext_signal
4345 Jim_SetResultFormatted(interp, "child %s by signal %s", action, Jim_SignalId(WTERMSIG(waitStatus)));
4346 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalId(WTERMSIG(waitStatus)), -1));
4347 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
4348 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1));
4349 #else
4350 Jim_SetResultFormatted(interp, "child %s by signal %d", action, WTERMSIG(waitStatus));
4351 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus)));
4352 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4353 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus)));
4354 #endif
4356 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
4357 return rc;
4361 struct WaitInfo
4363 pidtype pid;
4364 int status;
4365 int flags;
4368 struct WaitInfoTable {
4369 struct WaitInfo *info;
4370 int size;
4371 int used;
4375 #define WI_DETACHED 2
4377 #define WAIT_TABLE_GROW_BY 4
4379 static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
4381 struct WaitInfoTable *table = privData;
4383 Jim_Free(table->info);
4384 Jim_Free(table);
4387 static struct WaitInfoTable *JimAllocWaitInfoTable(void)
4389 struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
4390 table->info = NULL;
4391 table->size = table->used = 0;
4393 return table;
4396 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4398 fdtype outputId;
4399 fdtype errorId;
4400 pidtype *pidPtr;
4401 int numPids, result;
4403 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
4404 Jim_Obj *listObj;
4405 int i;
4407 argc--;
4408 numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
4409 if (numPids < 0) {
4410 return JIM_ERR;
4413 listObj = Jim_NewListObj(interp, NULL, 0);
4414 for (i = 0; i < numPids; i++) {
4415 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i]));
4417 Jim_SetResult(interp, listObj);
4418 JimDetachPids(interp, numPids, pidPtr);
4419 Jim_Free(pidPtr);
4420 return JIM_OK;
4423 numPids =
4424 JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
4426 if (numPids < 0) {
4427 return JIM_ERR;
4430 Jim_SetResultString(interp, "", 0);
4432 result = JIM_OK;
4433 if (outputId != JIM_BAD_FD) {
4434 result = JimAppendStreamToString(interp, outputId, Jim_GetResult(interp));
4435 if (result < 0) {
4436 Jim_SetResultErrno(interp, "error reading from output pipe");
4440 if (JimCleanupChildren(interp, numPids, pidPtr, errorId) != JIM_OK) {
4441 result = JIM_ERR;
4443 return result;
4446 static void JimReapDetachedPids(struct WaitInfoTable *table)
4448 struct WaitInfo *waitPtr;
4449 int count;
4450 int dest;
4452 if (!table) {
4453 return;
4456 waitPtr = table->info;
4457 dest = 0;
4458 for (count = table->used; count > 0; waitPtr++, count--) {
4459 if (waitPtr->flags & WI_DETACHED) {
4460 int status;
4461 pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG);
4462 if (pid == waitPtr->pid) {
4464 table->used--;
4465 continue;
4468 if (waitPtr != &table->info[dest]) {
4469 table->info[dest] = *waitPtr;
4471 dest++;
4475 static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
4477 int i;
4480 for (i = 0; i < table->used; i++) {
4481 if (pid == table->info[i].pid) {
4483 JimWaitPid(pid, statusPtr, 0);
4486 if (i != table->used - 1) {
4487 table->info[i] = table->info[table->used - 1];
4489 table->used--;
4490 return pid;
4495 return JIM_BAD_PID;
4498 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr)
4500 int j;
4501 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4503 for (j = 0; j < numPids; j++) {
4505 int i;
4506 for (i = 0; i < table->used; i++) {
4507 if (pidPtr[j] == table->info[i].pid) {
4508 table->info[i].flags |= WI_DETACHED;
4509 break;
4515 static FILE *JimGetAioFilehandle(Jim_Interp *interp, const char *name)
4517 FILE *fh;
4518 Jim_Obj *fhObj;
4520 fhObj = Jim_NewStringObj(interp, name, -1);
4521 Jim_IncrRefCount(fhObj);
4522 fh = Jim_AioFilehandle(interp, fhObj);
4523 Jim_DecrRefCount(interp, fhObj);
4525 return fh;
4528 static int
4529 JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr,
4530 fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr)
4532 pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all
4533 * the pids of child processes. */
4534 int numPids = 0; /* Actual number of processes that exist
4535 * at *pidPtr right now. */
4536 int cmdCount; /* Count of number of distinct commands
4537 * found in argc/argv. */
4538 const char *input = NULL; /* Describes input for pipeline, depending
4539 * on "inputFile". NULL means take input
4540 * from stdin/pipe. */
4541 int input_len = 0;
4543 #define FILE_NAME 0
4544 #define FILE_APPEND 1
4545 #define FILE_HANDLE 2
4546 #define FILE_TEXT 3
4548 int inputFile = FILE_NAME; /* 1 means input is name of input file.
4549 * 2 means input is filehandle name.
4550 * 0 means input holds actual
4551 * text to be input to command. */
4553 int outputFile = FILE_NAME; /* 0 means output is the name of output file.
4554 * 1 means output is the name of output file, and append.
4555 * 2 means output is filehandle name.
4556 * All this is ignored if output is NULL
4558 int errorFile = FILE_NAME; /* 0 means error is the name of error file.
4559 * 1 means error is the name of error file, and append.
4560 * 2 means error is filehandle name.
4561 * All this is ignored if error is NULL
4563 const char *output = NULL; /* Holds name of output file to pipe to,
4564 * or NULL if output goes to stdout/pipe. */
4565 const char *error = NULL; /* Holds name of stderr file to pipe to,
4566 * or NULL if stderr goes to stderr/pipe. */
4567 fdtype inputId = JIM_BAD_FD;
4568 fdtype outputId = JIM_BAD_FD;
4569 fdtype errorId = JIM_BAD_FD;
4570 fdtype lastOutputId = JIM_BAD_FD;
4571 fdtype pipeIds[2];
4572 int firstArg, lastArg; /* Indexes of first and last arguments in
4573 * current command. */
4574 int lastBar;
4575 int i;
4576 pidtype pid;
4577 char **save_environ;
4578 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4581 char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
4582 int arg_count = 0;
4584 JimReapDetachedPids(table);
4586 if (inPipePtr != NULL) {
4587 *inPipePtr = JIM_BAD_FD;
4589 if (outPipePtr != NULL) {
4590 *outPipePtr = JIM_BAD_FD;
4592 if (errFilePtr != NULL) {
4593 *errFilePtr = JIM_BAD_FD;
4595 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4597 cmdCount = 1;
4598 lastBar = -1;
4599 for (i = 0; i < argc; i++) {
4600 const char *arg = Jim_String(argv[i]);
4602 if (arg[0] == '<') {
4603 inputFile = FILE_NAME;
4604 input = arg + 1;
4605 if (*input == '<') {
4606 inputFile = FILE_TEXT;
4607 input_len = Jim_Length(argv[i]) - 2;
4608 input++;
4610 else if (*input == '@') {
4611 inputFile = FILE_HANDLE;
4612 input++;
4615 if (!*input && ++i < argc) {
4616 input = Jim_GetString(argv[i], &input_len);
4619 else if (arg[0] == '>') {
4620 int dup_error = 0;
4622 outputFile = FILE_NAME;
4624 output = arg + 1;
4625 if (*output == '>') {
4626 outputFile = FILE_APPEND;
4627 output++;
4629 if (*output == '&') {
4631 output++;
4632 dup_error = 1;
4634 if (*output == '@') {
4635 outputFile = FILE_HANDLE;
4636 output++;
4638 if (!*output && ++i < argc) {
4639 output = Jim_String(argv[i]);
4641 if (dup_error) {
4642 errorFile = outputFile;
4643 error = output;
4646 else if (arg[0] == '2' && arg[1] == '>') {
4647 error = arg + 2;
4648 errorFile = FILE_NAME;
4650 if (*error == '@') {
4651 errorFile = FILE_HANDLE;
4652 error++;
4654 else if (*error == '>') {
4655 errorFile = FILE_APPEND;
4656 error++;
4658 if (!*error && ++i < argc) {
4659 error = Jim_String(argv[i]);
4662 else {
4663 if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
4664 if (i == lastBar + 1 || i == argc - 1) {
4665 Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
4666 goto badargs;
4668 lastBar = i;
4669 cmdCount++;
4672 arg_array[arg_count++] = (char *)arg;
4673 continue;
4676 if (i >= argc) {
4677 Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
4678 goto badargs;
4682 if (arg_count == 0) {
4683 Jim_SetResultString(interp, "didn't specify command to execute", -1);
4684 badargs:
4685 Jim_Free(arg_array);
4686 return -1;
4690 save_environ = JimSaveEnv(JimBuildEnv(interp));
4692 if (input != NULL) {
4693 if (inputFile == FILE_TEXT) {
4694 inputId = JimCreateTemp(interp, input, input_len);
4695 if (inputId == JIM_BAD_FD) {
4696 goto error;
4699 else if (inputFile == FILE_HANDLE) {
4701 FILE *fh = JimGetAioFilehandle(interp, input);
4703 if (fh == NULL) {
4704 goto error;
4706 inputId = JimDupFd(JimFileno(fh));
4708 else {
4709 inputId = JimOpenForRead(input);
4710 if (inputId == JIM_BAD_FD) {
4711 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError());
4712 goto error;
4716 else if (inPipePtr != NULL) {
4717 if (JimPipe(pipeIds) != 0) {
4718 Jim_SetResultErrno(interp, "couldn't create input pipe for command");
4719 goto error;
4721 inputId = pipeIds[0];
4722 *inPipePtr = pipeIds[1];
4723 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4726 if (output != NULL) {
4727 if (outputFile == FILE_HANDLE) {
4728 FILE *fh = JimGetAioFilehandle(interp, output);
4729 if (fh == NULL) {
4730 goto error;
4732 fflush(fh);
4733 lastOutputId = JimDupFd(JimFileno(fh));
4735 else {
4736 lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND);
4737 if (lastOutputId == JIM_BAD_FD) {
4738 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError());
4739 goto error;
4743 else if (outPipePtr != NULL) {
4744 if (JimPipe(pipeIds) != 0) {
4745 Jim_SetResultErrno(interp, "couldn't create output pipe");
4746 goto error;
4748 lastOutputId = pipeIds[1];
4749 *outPipePtr = pipeIds[0];
4750 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4753 if (error != NULL) {
4754 if (errorFile == FILE_HANDLE) {
4755 if (strcmp(error, "1") == 0) {
4757 if (lastOutputId != JIM_BAD_FD) {
4758 errorId = JimDupFd(lastOutputId);
4760 else {
4762 error = "stdout";
4765 if (errorId == JIM_BAD_FD) {
4766 FILE *fh = JimGetAioFilehandle(interp, error);
4767 if (fh == NULL) {
4768 goto error;
4770 fflush(fh);
4771 errorId = JimDupFd(JimFileno(fh));
4774 else {
4775 errorId = JimOpenForWrite(error, errorFile == FILE_APPEND);
4776 if (errorId == JIM_BAD_FD) {
4777 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError());
4778 goto error;
4782 else if (errFilePtr != NULL) {
4783 errorId = JimCreateTemp(interp, NULL, 0);
4784 if (errorId == JIM_BAD_FD) {
4785 goto error;
4787 *errFilePtr = JimDupFd(errorId);
4791 pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
4792 for (i = 0; i < numPids; i++) {
4793 pidPtr[i] = JIM_BAD_PID;
4795 for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
4796 int pipe_dup_err = 0;
4797 fdtype origErrorId = errorId;
4799 for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
4800 if (arg_array[lastArg][0] == '|') {
4801 if (arg_array[lastArg][1] == '&') {
4802 pipe_dup_err = 1;
4804 break;
4808 arg_array[lastArg] = NULL;
4809 if (lastArg == arg_count) {
4810 outputId = lastOutputId;
4812 else {
4813 if (JimPipe(pipeIds) != 0) {
4814 Jim_SetResultErrno(interp, "couldn't create pipe");
4815 goto error;
4817 outputId = pipeIds[1];
4821 if (pipe_dup_err) {
4822 errorId = outputId;
4827 #ifdef __MINGW32__
4828 pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId);
4829 if (pid == JIM_BAD_PID) {
4830 Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
4831 goto error;
4833 #else
4834 pid = vfork();
4835 if (pid < 0) {
4836 Jim_SetResultErrno(interp, "couldn't fork child process");
4837 goto error;
4839 if (pid == 0) {
4842 if (inputId != -1) dup2(inputId, 0);
4843 if (outputId != -1) dup2(outputId, 1);
4844 if (errorId != -1) dup2(errorId, 2);
4846 for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) {
4847 close(i);
4851 (void)signal(SIGPIPE, SIG_DFL);
4853 execvpe(arg_array[firstArg], &arg_array[firstArg], Jim_GetEnviron());
4856 fprintf(stderr, "couldn't exec \"%s\"\n", arg_array[firstArg]);
4857 _exit(127);
4859 #endif
4863 if (table->used == table->size) {
4864 table->size += WAIT_TABLE_GROW_BY;
4865 table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
4868 table->info[table->used].pid = pid;
4869 table->info[table->used].flags = 0;
4870 table->used++;
4872 pidPtr[numPids] = pid;
4875 errorId = origErrorId;
4878 if (inputId != JIM_BAD_FD) {
4879 JimCloseFd(inputId);
4881 if (outputId != JIM_BAD_FD) {
4882 JimCloseFd(outputId);
4884 inputId = pipeIds[0];
4885 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4887 *pidArrayPtr = pidPtr;
4890 cleanup:
4891 if (inputId != JIM_BAD_FD) {
4892 JimCloseFd(inputId);
4894 if (lastOutputId != JIM_BAD_FD) {
4895 JimCloseFd(lastOutputId);
4897 if (errorId != JIM_BAD_FD) {
4898 JimCloseFd(errorId);
4900 Jim_Free(arg_array);
4902 JimRestoreEnv(save_environ);
4904 return numPids;
4907 error:
4908 if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) {
4909 JimCloseFd(*inPipePtr);
4910 *inPipePtr = JIM_BAD_FD;
4912 if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) {
4913 JimCloseFd(*outPipePtr);
4914 *outPipePtr = JIM_BAD_FD;
4916 if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) {
4917 JimCloseFd(*errFilePtr);
4918 *errFilePtr = JIM_BAD_FD;
4920 if (pipeIds[0] != JIM_BAD_FD) {
4921 JimCloseFd(pipeIds[0]);
4923 if (pipeIds[1] != JIM_BAD_FD) {
4924 JimCloseFd(pipeIds[1]);
4926 if (pidPtr != NULL) {
4927 for (i = 0; i < numPids; i++) {
4928 if (pidPtr[i] != JIM_BAD_PID) {
4929 JimDetachPids(interp, 1, &pidPtr[i]);
4932 Jim_Free(pidPtr);
4934 numPids = -1;
4935 goto cleanup;
4939 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId)
4941 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4942 int result = JIM_OK;
4943 int i;
4945 for (i = 0; i < numPids; i++) {
4946 int waitStatus = 0;
4947 if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) {
4948 if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus) != JIM_OK) {
4949 result = JIM_ERR;
4953 Jim_Free(pidPtr);
4955 if (errorId != JIM_BAD_FD) {
4956 JimRewindFd(errorId);
4957 if (JimAppendStreamToString(interp, errorId, Jim_GetResult(interp)) != JIM_OK) {
4958 result = JIM_ERR;
4962 Jim_RemoveTrailingNewline(Jim_GetResult(interp));
4964 return result;
4967 int Jim_execInit(Jim_Interp *interp)
4969 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
4970 return JIM_ERR;
4972 #ifdef SIGPIPE
4973 (void)signal(SIGPIPE, SIG_IGN);
4974 #endif
4976 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable);
4977 return JIM_OK;
4980 #if defined(__MINGW32__)
4983 static SECURITY_ATTRIBUTES *JimStdSecAttrs(void)
4985 static SECURITY_ATTRIBUTES secAtts;
4987 secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
4988 secAtts.lpSecurityDescriptor = NULL;
4989 secAtts.bInheritHandle = TRUE;
4990 return &secAtts;
4993 static int JimErrno(void)
4995 switch (GetLastError()) {
4996 case ERROR_FILE_NOT_FOUND: return ENOENT;
4997 case ERROR_PATH_NOT_FOUND: return ENOENT;
4998 case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
4999 case ERROR_ACCESS_DENIED: return EACCES;
5000 case ERROR_INVALID_HANDLE: return EBADF;
5001 case ERROR_BAD_ENVIRONMENT: return E2BIG;
5002 case ERROR_BAD_FORMAT: return ENOEXEC;
5003 case ERROR_INVALID_ACCESS: return EACCES;
5004 case ERROR_INVALID_DRIVE: return ENOENT;
5005 case ERROR_CURRENT_DIRECTORY: return EACCES;
5006 case ERROR_NOT_SAME_DEVICE: return EXDEV;
5007 case ERROR_NO_MORE_FILES: return ENOENT;
5008 case ERROR_WRITE_PROTECT: return EROFS;
5009 case ERROR_BAD_UNIT: return ENXIO;
5010 case ERROR_NOT_READY: return EBUSY;
5011 case ERROR_BAD_COMMAND: return EIO;
5012 case ERROR_CRC: return EIO;
5013 case ERROR_BAD_LENGTH: return EIO;
5014 case ERROR_SEEK: return EIO;
5015 case ERROR_WRITE_FAULT: return EIO;
5016 case ERROR_READ_FAULT: return EIO;
5017 case ERROR_GEN_FAILURE: return EIO;
5018 case ERROR_SHARING_VIOLATION: return EACCES;
5019 case ERROR_LOCK_VIOLATION: return EACCES;
5020 case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
5021 case ERROR_HANDLE_DISK_FULL: return ENOSPC;
5022 case ERROR_NOT_SUPPORTED: return ENODEV;
5023 case ERROR_REM_NOT_LIST: return EBUSY;
5024 case ERROR_DUP_NAME: return EEXIST;
5025 case ERROR_BAD_NETPATH: return ENOENT;
5026 case ERROR_NETWORK_BUSY: return EBUSY;
5027 case ERROR_DEV_NOT_EXIST: return ENODEV;
5028 case ERROR_TOO_MANY_CMDS: return EAGAIN;
5029 case ERROR_ADAP_HDW_ERR: return EIO;
5030 case ERROR_BAD_NET_RESP: return EIO;
5031 case ERROR_UNEXP_NET_ERR: return EIO;
5032 case ERROR_NETNAME_DELETED: return ENOENT;
5033 case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
5034 case ERROR_BAD_DEV_TYPE: return ENODEV;
5035 case ERROR_BAD_NET_NAME: return ENOENT;
5036 case ERROR_TOO_MANY_NAMES: return ENFILE;
5037 case ERROR_TOO_MANY_SESS: return EIO;
5038 case ERROR_SHARING_PAUSED: return EAGAIN;
5039 case ERROR_REDIR_PAUSED: return EAGAIN;
5040 case ERROR_FILE_EXISTS: return EEXIST;
5041 case ERROR_CANNOT_MAKE: return ENOSPC;
5042 case ERROR_OUT_OF_STRUCTURES: return ENFILE;
5043 case ERROR_ALREADY_ASSIGNED: return EEXIST;
5044 case ERROR_INVALID_PASSWORD: return EPERM;
5045 case ERROR_NET_WRITE_FAULT: return EIO;
5046 case ERROR_NO_PROC_SLOTS: return EAGAIN;
5047 case ERROR_DISK_CHANGE: return EXDEV;
5048 case ERROR_BROKEN_PIPE: return EPIPE;
5049 case ERROR_OPEN_FAILED: return ENOENT;
5050 case ERROR_DISK_FULL: return ENOSPC;
5051 case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
5052 case ERROR_INVALID_TARGET_HANDLE: return EBADF;
5053 case ERROR_INVALID_NAME: return ENOENT;
5054 case ERROR_PROC_NOT_FOUND: return ESRCH;
5055 case ERROR_WAIT_NO_CHILDREN: return ECHILD;
5056 case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
5057 case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
5058 case ERROR_SEEK_ON_DEVICE: return ESPIPE;
5059 case ERROR_BUSY_DRIVE: return EAGAIN;
5060 case ERROR_DIR_NOT_EMPTY: return EEXIST;
5061 case ERROR_NOT_LOCKED: return EACCES;
5062 case ERROR_BAD_PATHNAME: return ENOENT;
5063 case ERROR_LOCK_FAILED: return EACCES;
5064 case ERROR_ALREADY_EXISTS: return EEXIST;
5065 case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
5066 case ERROR_BAD_PIPE: return EPIPE;
5067 case ERROR_PIPE_BUSY: return EAGAIN;
5068 case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
5069 case ERROR_DIRECTORY: return ENOTDIR;
5071 return EINVAL;
5074 static int JimPipe(fdtype pipefd[2])
5076 if (CreatePipe(&pipefd[0], &pipefd[1], NULL, 0)) {
5077 return 0;
5079 return -1;
5082 static fdtype JimDupFd(fdtype infd)
5084 fdtype dupfd;
5085 pidtype pid = GetCurrentProcess();
5087 if (DuplicateHandle(pid, infd, pid, &dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
5088 return dupfd;
5090 return JIM_BAD_FD;
5093 static int JimRewindFd(fdtype fd)
5095 return SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ? -1 : 0;
5098 #if 0
5099 static int JimReadFd(fdtype fd, char *buffer, size_t len)
5101 DWORD num;
5103 if (ReadFile(fd, buffer, len, &num, NULL)) {
5104 return num;
5106 if (GetLastError() == ERROR_HANDLE_EOF || GetLastError() == ERROR_BROKEN_PIPE) {
5107 return 0;
5109 return -1;
5111 #endif
5113 static FILE *JimFdOpenForRead(fdtype fd)
5115 return _fdopen(_open_osfhandle((int)fd, _O_RDONLY | _O_TEXT), "r");
5118 static fdtype JimFileno(FILE *fh)
5120 return (fdtype)_get_osfhandle(_fileno(fh));
5123 static fdtype JimOpenForRead(const char *filename)
5125 return CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
5126 JimStdSecAttrs(), OPEN_EXISTING, 0, NULL);
5129 static fdtype JimOpenForWrite(const char *filename, int append)
5131 return CreateFile(filename, append ? FILE_APPEND_DATA : GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
5132 JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, 0, (HANDLE) NULL);
5135 static FILE *JimFdOpenForWrite(fdtype fd)
5137 return _fdopen(_open_osfhandle((int)fd, _O_TEXT), "w");
5140 static pidtype JimWaitPid(pidtype pid, int *status, int nohang)
5142 DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
5143 if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
5145 return JIM_BAD_PID;
5147 GetExitCodeProcess(pid, &ret);
5148 *status = ret;
5149 CloseHandle(pid);
5150 return pid;
5153 static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
5155 char name[MAX_PATH];
5156 HANDLE handle;
5158 if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) {
5159 return JIM_BAD_FD;
5162 handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, JimStdSecAttrs(),
5163 CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
5164 NULL);
5166 if (handle == INVALID_HANDLE_VALUE) {
5167 goto error;
5170 if (contents != NULL) {
5172 FILE *fh = JimFdOpenForWrite(JimDupFd(handle));
5173 if (fh == NULL) {
5174 goto error;
5177 if (fwrite(contents, len, 1, fh) != 1) {
5178 fclose(fh);
5179 goto error;
5181 fseek(fh, 0, SEEK_SET);
5182 fclose(fh);
5184 return handle;
5186 error:
5187 Jim_SetResultErrno(interp, "failed to create temp file");
5188 CloseHandle(handle);
5189 DeleteFile(name);
5190 return JIM_BAD_FD;
5193 static int
5194 JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
5196 int i;
5197 static char extensions[][5] = {".exe", "", ".bat"};
5199 for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
5200 lstrcpyn(fullPath, originalName, MAX_PATH - 5);
5201 lstrcat(fullPath, extensions[i]);
5203 if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
5204 continue;
5206 if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
5207 continue;
5209 return 0;
5212 return -1;
5215 static char **JimSaveEnv(char **env)
5217 return env;
5220 static void JimRestoreEnv(char **env)
5222 JimFreeEnv(env, Jim_GetEnviron());
5225 static Jim_Obj *
5226 JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
5228 char *start, *special;
5229 int quote, i;
5231 Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
5233 for (i = 0; argv[i]; i++) {
5234 if (i > 0) {
5235 Jim_AppendString(interp, strObj, " ", 1);
5238 if (argv[i][0] == '\0') {
5239 quote = 1;
5241 else {
5242 quote = 0;
5243 for (start = argv[i]; *start != '\0'; start++) {
5244 if (isspace(UCHAR(*start))) {
5245 quote = 1;
5246 break;
5250 if (quote) {
5251 Jim_AppendString(interp, strObj, "\"" , 1);
5254 start = argv[i];
5255 for (special = argv[i]; ; ) {
5256 if ((*special == '\\') && (special[1] == '\\' ||
5257 special[1] == '"' || (quote && special[1] == '\0'))) {
5258 Jim_AppendString(interp, strObj, start, special - start);
5259 start = special;
5260 while (1) {
5261 special++;
5262 if (*special == '"' || (quote && *special == '\0')) {
5264 Jim_AppendString(interp, strObj, start, special - start);
5265 break;
5267 if (*special != '\\') {
5268 break;
5271 Jim_AppendString(interp, strObj, start, special - start);
5272 start = special;
5274 if (*special == '"') {
5275 if (special == start) {
5276 Jim_AppendString(interp, strObj, "\"", 1);
5278 else {
5279 Jim_AppendString(interp, strObj, start, special - start);
5281 Jim_AppendString(interp, strObj, "\\\"", 2);
5282 start = special + 1;
5284 if (*special == '\0') {
5285 break;
5287 special++;
5289 Jim_AppendString(interp, strObj, start, special - start);
5290 if (quote) {
5291 Jim_AppendString(interp, strObj, "\"", 1);
5294 return strObj;
5297 static pidtype
5298 JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId)
5300 STARTUPINFO startInfo;
5301 PROCESS_INFORMATION procInfo;
5302 HANDLE hProcess, h;
5303 char execPath[MAX_PATH];
5304 pidtype pid = JIM_BAD_PID;
5305 Jim_Obj *cmdLineObj;
5307 if (JimWinFindExecutable(argv[0], execPath) < 0) {
5308 return JIM_BAD_PID;
5310 argv[0] = execPath;
5312 hProcess = GetCurrentProcess();
5313 cmdLineObj = JimWinBuildCommandLine(interp, argv);
5316 ZeroMemory(&startInfo, sizeof(startInfo));
5317 startInfo.cb = sizeof(startInfo);
5318 startInfo.dwFlags = STARTF_USESTDHANDLES;
5319 startInfo.hStdInput = INVALID_HANDLE_VALUE;
5320 startInfo.hStdOutput= INVALID_HANDLE_VALUE;
5321 startInfo.hStdError = INVALID_HANDLE_VALUE;
5323 if (inputId == JIM_BAD_FD) {
5324 if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) {
5325 CloseHandle(h);
5327 } else {
5328 DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput,
5329 0, TRUE, DUPLICATE_SAME_ACCESS);
5331 if (startInfo.hStdInput == JIM_BAD_FD) {
5332 goto end;
5335 if (outputId == JIM_BAD_FD) {
5336 startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
5337 JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
5338 } else {
5339 DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput,
5340 0, TRUE, DUPLICATE_SAME_ACCESS);
5342 if (startInfo.hStdOutput == JIM_BAD_FD) {
5343 goto end;
5346 if (errorId == JIM_BAD_FD) {
5348 startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
5349 JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
5350 } else {
5351 DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError,
5352 0, TRUE, DUPLICATE_SAME_ACCESS);
5354 if (startInfo.hStdError == JIM_BAD_FD) {
5355 goto end;
5358 if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
5359 0, env, NULL, &startInfo, &procInfo)) {
5360 goto end;
5364 WaitForInputIdle(procInfo.hProcess, 5000);
5365 CloseHandle(procInfo.hThread);
5367 pid = procInfo.hProcess;
5369 end:
5370 Jim_FreeNewObj(interp, cmdLineObj);
5371 if (startInfo.hStdInput != JIM_BAD_FD) {
5372 CloseHandle(startInfo.hStdInput);
5374 if (startInfo.hStdOutput != JIM_BAD_FD) {
5375 CloseHandle(startInfo.hStdOutput);
5377 if (startInfo.hStdError != JIM_BAD_FD) {
5378 CloseHandle(startInfo.hStdError);
5380 return pid;
5382 #else
5384 static int JimOpenForWrite(const char *filename, int append)
5386 return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
5389 static int JimRewindFd(int fd)
5391 return lseek(fd, 0L, SEEK_SET);
5394 static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
5396 int fd = Jim_MakeTempFile(interp, NULL);
5398 if (fd != JIM_BAD_FD) {
5399 unlink(Jim_String(Jim_GetResult(interp)));
5400 if (contents) {
5401 if (write(fd, contents, len) != len) {
5402 Jim_SetResultErrno(interp, "couldn't write temp file");
5403 close(fd);
5404 return -1;
5406 lseek(fd, 0L, SEEK_SET);
5409 return fd;
5412 static char **JimSaveEnv(char **env)
5414 char **saveenv = Jim_GetEnviron();
5415 Jim_SetEnviron(env);
5416 return saveenv;
5419 static void JimRestoreEnv(char **env)
5421 JimFreeEnv(Jim_GetEnviron(), env);
5422 Jim_SetEnviron(env);
5424 #endif
5425 #endif
5428 #ifndef _XOPEN_SOURCE
5429 #define _XOPEN_SOURCE 500
5430 #endif
5432 #include <stdlib.h>
5433 #include <string.h>
5434 #include <stdio.h>
5435 #include <time.h>
5438 #ifdef HAVE_SYS_TIME_H
5439 #include <sys/time.h>
5440 #endif
5442 static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5445 char buf[100];
5446 time_t t;
5447 long seconds;
5449 const char *format = "%a %b %d %H:%M:%S %Z %Y";
5451 if (argc == 2 || (argc == 3 && !Jim_CompareStringImmediate(interp, argv[1], "-format"))) {
5452 return -1;
5455 if (argc == 3) {
5456 format = Jim_String(argv[2]);
5459 if (Jim_GetLong(interp, argv[0], &seconds) != JIM_OK) {
5460 return JIM_ERR;
5462 t = seconds;
5464 if (strftime(buf, sizeof(buf), format, localtime(&t)) == 0) {
5465 Jim_SetResultString(interp, "format string too long", -1);
5466 return JIM_ERR;
5469 Jim_SetResultString(interp, buf, -1);
5471 return JIM_OK;
5474 #ifdef HAVE_STRPTIME
5475 static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5477 char *pt;
5478 struct tm tm;
5479 time_t now = time(0);
5481 if (!Jim_CompareStringImmediate(interp, argv[1], "-format")) {
5482 return -1;
5486 localtime_r(&now, &tm);
5488 pt = strptime(Jim_String(argv[0]), Jim_String(argv[2]), &tm);
5489 if (pt == 0 || *pt != 0) {
5490 Jim_SetResultString(interp, "Failed to parse time according to format", -1);
5491 return JIM_ERR;
5495 Jim_SetResultInt(interp, mktime(&tm));
5497 return JIM_OK;
5499 #endif
5501 static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5503 Jim_SetResultInt(interp, time(NULL));
5505 return JIM_OK;
5508 static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5510 struct timeval tv;
5512 gettimeofday(&tv, NULL);
5514 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec);
5516 return JIM_OK;
5519 static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5521 struct timeval tv;
5523 gettimeofday(&tv, NULL);
5525 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000 + tv.tv_usec / 1000);
5527 return JIM_OK;
5530 static const jim_subcmd_type clock_command_table[] = {
5531 { "seconds",
5532 NULL,
5533 clock_cmd_seconds,
5538 { "clicks",
5539 NULL,
5540 clock_cmd_micros,
5545 { "microseconds",
5546 NULL,
5547 clock_cmd_micros,
5552 { "milliseconds",
5553 NULL,
5554 clock_cmd_millis,
5559 { "format",
5560 "seconds ?-format format?",
5561 clock_cmd_format,
5566 #ifdef HAVE_STRPTIME
5567 { "scan",
5568 "str -format format",
5569 clock_cmd_scan,
5574 #endif
5575 { NULL }
5578 int Jim_clockInit(Jim_Interp *interp)
5580 if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG))
5581 return JIM_ERR;
5583 Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
5584 return JIM_OK;
5587 #include <limits.h>
5588 #include <stdlib.h>
5589 #include <string.h>
5590 #include <stdio.h>
5591 #include <errno.h>
5594 static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5597 Jim_SetResultInt(interp, Jim_GetVariable(interp, argv[0], 0) != 0);
5598 return JIM_OK;
5601 static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5603 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5604 Jim_Obj *patternObj;
5606 if (!objPtr) {
5607 return JIM_OK;
5610 patternObj = (argc == 1) ? NULL : argv[1];
5613 if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) {
5614 if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) {
5616 Jim_SetResult(interp, objPtr);
5617 return JIM_OK;
5622 return Jim_DictValues(interp, objPtr, patternObj);
5625 static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5627 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5629 if (!objPtr) {
5630 return JIM_OK;
5633 return Jim_DictKeys(interp, objPtr, argc == 1 ? NULL : argv[1]);
5636 static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5638 int i;
5639 int len;
5640 Jim_Obj *resultObj;
5641 Jim_Obj *objPtr;
5642 Jim_Obj **dictValuesObj;
5644 if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
5646 Jim_UnsetVariable(interp, argv[0], JIM_NONE);
5647 return JIM_OK;
5650 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5652 if (objPtr == NULL) {
5654 return JIM_OK;
5657 if (Jim_DictPairs(interp, objPtr, &dictValuesObj, &len) != JIM_OK) {
5658 return JIM_ERR;
5662 resultObj = Jim_NewDictObj(interp, NULL, 0);
5664 for (i = 0; i < len; i += 2) {
5665 if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
5666 Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
5669 Jim_Free(dictValuesObj);
5671 Jim_SetVariable(interp, argv[0], resultObj);
5672 return JIM_OK;
5675 static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5677 Jim_Obj *objPtr;
5678 int len = 0;
5681 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5682 if (objPtr) {
5683 len = Jim_DictSize(interp, objPtr);
5684 if (len < 0) {
5685 return JIM_ERR;
5689 Jim_SetResultInt(interp, len);
5691 return JIM_OK;
5694 static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5696 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5697 if (objPtr) {
5698 return Jim_DictInfo(interp, objPtr);
5700 Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL);
5701 return JIM_ERR;
5704 static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5706 int i;
5707 int len;
5708 Jim_Obj *listObj = argv[1];
5709 Jim_Obj *dictObj;
5711 len = Jim_ListLength(interp, listObj);
5712 if (len % 2) {
5713 Jim_SetResultString(interp, "list must have an even number of elements", -1);
5714 return JIM_ERR;
5717 dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
5718 if (!dictObj) {
5720 return Jim_SetVariable(interp, argv[0], listObj);
5722 else if (Jim_DictSize(interp, dictObj) < 0) {
5723 return JIM_ERR;
5726 if (Jim_IsShared(dictObj)) {
5727 dictObj = Jim_DuplicateObj(interp, dictObj);
5730 for (i = 0; i < len; i += 2) {
5731 Jim_Obj *nameObj;
5732 Jim_Obj *valueObj;
5734 Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE);
5735 Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE);
5737 Jim_DictAddElement(interp, dictObj, nameObj, valueObj);
5739 return Jim_SetVariable(interp, argv[0], dictObj);
5742 static const jim_subcmd_type array_command_table[] = {
5743 { "exists",
5744 "arrayName",
5745 array_cmd_exists,
5750 { "get",
5751 "arrayName ?pattern?",
5752 array_cmd_get,
5757 { "names",
5758 "arrayName ?pattern?",
5759 array_cmd_names,
5764 { "set",
5765 "arrayName list",
5766 array_cmd_set,
5771 { "size",
5772 "arrayName",
5773 array_cmd_size,
5778 { "stat",
5779 "arrayName",
5780 array_cmd_stat,
5785 { "unset",
5786 "arrayName ?pattern?",
5787 array_cmd_unset,
5792 { NULL
5796 int Jim_arrayInit(Jim_Interp *interp)
5798 if (Jim_PackageProvide(interp, "array", "1.0", JIM_ERRMSG))
5799 return JIM_ERR;
5801 Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
5802 return JIM_OK;
5804 int Jim_InitStaticExtensions(Jim_Interp *interp)
5806 extern int Jim_bootstrapInit(Jim_Interp *);
5807 extern int Jim_aioInit(Jim_Interp *);
5808 extern int Jim_readdirInit(Jim_Interp *);
5809 extern int Jim_globInit(Jim_Interp *);
5810 extern int Jim_regexpInit(Jim_Interp *);
5811 extern int Jim_fileInit(Jim_Interp *);
5812 extern int Jim_execInit(Jim_Interp *);
5813 extern int Jim_clockInit(Jim_Interp *);
5814 extern int Jim_arrayInit(Jim_Interp *);
5815 extern int Jim_stdlibInit(Jim_Interp *);
5816 extern int Jim_tclcompatInit(Jim_Interp *);
5817 Jim_bootstrapInit(interp);
5818 Jim_aioInit(interp);
5819 Jim_readdirInit(interp);
5820 Jim_globInit(interp);
5821 Jim_regexpInit(interp);
5822 Jim_fileInit(interp);
5823 Jim_execInit(interp);
5824 Jim_clockInit(interp);
5825 Jim_arrayInit(interp);
5826 Jim_stdlibInit(interp);
5827 Jim_tclcompatInit(interp);
5828 return JIM_OK;
5830 #define JIM_OPTIMIZATION
5832 #include <stdio.h>
5833 #include <stdlib.h>
5835 #include <string.h>
5836 #include <stdarg.h>
5837 #include <ctype.h>
5838 #include <limits.h>
5839 #include <assert.h>
5840 #include <errno.h>
5841 #include <time.h>
5842 #include <setjmp.h>
5845 #ifdef HAVE_SYS_TIME_H
5846 #include <sys/time.h>
5847 #endif
5848 #ifdef HAVE_BACKTRACE
5849 #include <execinfo.h>
5850 #endif
5851 #ifdef HAVE_CRT_EXTERNS_H
5852 #include <crt_externs.h>
5853 #endif
5856 #include <math.h>
5862 #ifndef TCL_LIBRARY
5863 #define TCL_LIBRARY "."
5864 #endif
5865 #ifndef TCL_PLATFORM_OS
5866 #define TCL_PLATFORM_OS "unknown"
5867 #endif
5868 #ifndef TCL_PLATFORM_PLATFORM
5869 #define TCL_PLATFORM_PLATFORM "unknown"
5870 #endif
5871 #ifndef TCL_PLATFORM_PATH_SEPARATOR
5872 #define TCL_PLATFORM_PATH_SEPARATOR ":"
5873 #endif
5881 #ifdef JIM_MAINTAINER
5882 #define JIM_DEBUG_COMMAND
5883 #define JIM_DEBUG_PANIC
5884 #endif
5888 #define JIM_INTEGER_SPACE 24
5890 const char *jim_tt_name(int type);
5892 #ifdef JIM_DEBUG_PANIC
5893 static void JimPanicDump(int fail_condition, const char *fmt, ...);
5894 #define JimPanic(X) JimPanicDump X
5895 #else
5896 #define JimPanic(X)
5897 #endif
5900 static char JimEmptyStringRep[] = "";
5902 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
5903 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
5904 int flags);
5905 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
5906 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
5907 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
5908 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len);
5909 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
5910 const char *prefix, const char *const *tablePtr, const char *name);
5911 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
5912 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
5913 static int JimSign(jim_wide w);
5914 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr);
5915 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
5916 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
5920 #define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
5922 #define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none")
5924 static int utf8_tounicode_case(const char *s, int *uc, int upper)
5926 int l = utf8_tounicode(s, uc);
5927 if (upper) {
5928 *uc = utf8_upper(*uc);
5930 return l;
5934 #define JIM_CHARSET_SCAN 2
5935 #define JIM_CHARSET_GLOB 0
5937 static const char *JimCharsetMatch(const char *pattern, int c, int flags)
5939 int not = 0;
5940 int pchar;
5941 int match = 0;
5942 int nocase = 0;
5944 if (flags & JIM_NOCASE) {
5945 nocase++;
5946 c = utf8_upper(c);
5949 if (flags & JIM_CHARSET_SCAN) {
5950 if (*pattern == '^') {
5951 not++;
5952 pattern++;
5956 if (*pattern == ']') {
5957 goto first;
5961 while (*pattern && *pattern != ']') {
5963 if (pattern[0] == '\\') {
5964 first:
5965 pattern += utf8_tounicode_case(pattern, &pchar, nocase);
5967 else {
5969 int start;
5970 int end;
5972 pattern += utf8_tounicode_case(pattern, &start, nocase);
5973 if (pattern[0] == '-' && pattern[1]) {
5975 pattern += utf8_tounicode(pattern, &pchar);
5976 pattern += utf8_tounicode_case(pattern, &end, nocase);
5979 if ((c >= start && c <= end) || (c >= end && c <= start)) {
5980 match = 1;
5982 continue;
5984 pchar = start;
5987 if (pchar == c) {
5988 match = 1;
5991 if (not) {
5992 match = !match;
5995 return match ? pattern : NULL;
6000 static int JimGlobMatch(const char *pattern, const char *string, int nocase)
6002 int c;
6003 int pchar;
6004 while (*pattern) {
6005 switch (pattern[0]) {
6006 case '*':
6007 while (pattern[1] == '*') {
6008 pattern++;
6010 pattern++;
6011 if (!pattern[0]) {
6012 return 1;
6014 while (*string) {
6016 if (JimGlobMatch(pattern, string, nocase))
6017 return 1;
6018 string += utf8_tounicode(string, &c);
6020 return 0;
6022 case '?':
6023 string += utf8_tounicode(string, &c);
6024 break;
6026 case '[': {
6027 string += utf8_tounicode(string, &c);
6028 pattern = JimCharsetMatch(pattern + 1, c, nocase ? JIM_NOCASE : 0);
6029 if (!pattern) {
6030 return 0;
6032 if (!*pattern) {
6034 continue;
6036 break;
6038 case '\\':
6039 if (pattern[1]) {
6040 pattern++;
6043 default:
6044 string += utf8_tounicode_case(string, &c, nocase);
6045 utf8_tounicode_case(pattern, &pchar, nocase);
6046 if (pchar != c) {
6047 return 0;
6049 break;
6051 pattern += utf8_tounicode_case(pattern, &pchar, nocase);
6052 if (!*string) {
6053 while (*pattern == '*') {
6054 pattern++;
6056 break;
6059 if (!*pattern && !*string) {
6060 return 1;
6062 return 0;
6065 static int JimStringCompare(const char *s1, int l1, const char *s2, int l2)
6067 if (l1 < l2) {
6068 return memcmp(s1, s2, l1) <= 0 ? -1 : 1;
6070 else if (l2 < l1) {
6071 return memcmp(s1, s2, l2) >= 0 ? 1 : -1;
6073 else {
6074 return JimSign(memcmp(s1, s2, l1));
6078 static int JimStringCompareLen(const char *s1, const char *s2, int maxchars, int nocase)
6080 while (*s1 && *s2 && maxchars) {
6081 int c1, c2;
6082 s1 += utf8_tounicode_case(s1, &c1, nocase);
6083 s2 += utf8_tounicode_case(s2, &c2, nocase);
6084 if (c1 != c2) {
6085 return JimSign(c1 - c2);
6087 maxchars--;
6089 if (!maxchars) {
6090 return 0;
6093 if (*s1) {
6094 return 1;
6096 if (*s2) {
6097 return -1;
6099 return 0;
6102 static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
6104 int i;
6105 int l1bytelen;
6107 if (!l1 || !l2 || l1 > l2) {
6108 return -1;
6110 if (idx < 0)
6111 idx = 0;
6112 s2 += utf8_index(s2, idx);
6114 l1bytelen = utf8_index(s1, l1);
6116 for (i = idx; i <= l2 - l1; i++) {
6117 int c;
6118 if (memcmp(s2, s1, l1bytelen) == 0) {
6119 return i;
6121 s2 += utf8_tounicode(s2, &c);
6123 return -1;
6126 static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
6128 const char *p;
6130 if (!l1 || !l2 || l1 > l2)
6131 return -1;
6134 for (p = s2 + l2 - 1; p != s2 - 1; p--) {
6135 if (*p == *s1 && memcmp(s1, p, l1) == 0) {
6136 return p - s2;
6139 return -1;
6142 #ifdef JIM_UTF8
6143 static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2)
6145 int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2));
6146 if (n > 0) {
6147 n = utf8_strlen(s2, n);
6149 return n;
6151 #endif
6153 static int JimCheckConversion(const char *str, const char *endptr)
6155 if (str[0] == '\0' || str == endptr) {
6156 return JIM_ERR;
6159 if (endptr[0] != '\0') {
6160 while (*endptr) {
6161 if (!isspace(UCHAR(*endptr))) {
6162 return JIM_ERR;
6164 endptr++;
6167 return JIM_OK;
6170 static int JimNumberBase(const char *str, int *base, int *sign)
6172 int i = 0;
6174 *base = 10;
6176 while (isspace(UCHAR(str[i]))) {
6177 i++;
6180 if (str[i] == '-') {
6181 *sign = -1;
6182 i++;
6184 else {
6185 if (str[i] == '+') {
6186 i++;
6188 *sign = 1;
6191 if (str[i] != '0') {
6193 return 0;
6197 switch (str[i + 1]) {
6198 case 'x': case 'X': *base = 16; break;
6199 case 'o': case 'O': *base = 8; break;
6200 case 'b': case 'B': *base = 2; break;
6201 default: return 0;
6203 i += 2;
6205 if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
6207 return i;
6210 *base = 10;
6211 return 0;
6214 static long jim_strtol(const char *str, char **endptr)
6216 int sign;
6217 int base;
6218 int i = JimNumberBase(str, &base, &sign);
6220 if (base != 10) {
6221 long value = strtol(str + i, endptr, base);
6222 if (endptr == NULL || *endptr != str + i) {
6223 return value * sign;
6228 return strtol(str, endptr, 10);
6232 static jim_wide jim_strtoull(const char *str, char **endptr)
6234 #ifdef HAVE_LONG_LONG
6235 int sign;
6236 int base;
6237 int i = JimNumberBase(str, &base, &sign);
6239 if (base != 10) {
6240 jim_wide value = strtoull(str + i, endptr, base);
6241 if (endptr == NULL || *endptr != str + i) {
6242 return value * sign;
6247 return strtoull(str, endptr, 10);
6248 #else
6249 return (unsigned long)jim_strtol(str, endptr);
6250 #endif
6253 int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
6255 char *endptr;
6257 if (base) {
6258 *widePtr = strtoull(str, &endptr, base);
6260 else {
6261 *widePtr = jim_strtoull(str, &endptr);
6264 return JimCheckConversion(str, endptr);
6267 int Jim_StringToDouble(const char *str, double *doublePtr)
6269 char *endptr;
6272 errno = 0;
6274 *doublePtr = strtod(str, &endptr);
6276 return JimCheckConversion(str, endptr);
6279 static jim_wide JimPowWide(jim_wide b, jim_wide e)
6281 jim_wide i, res = 1;
6283 if ((b == 0 && e != 0) || (e < 0))
6284 return 0;
6285 for (i = 0; i < e; i++) {
6286 res *= b;
6288 return res;
6291 #ifdef JIM_DEBUG_PANIC
6292 static void JimPanicDump(int condition, const char *fmt, ...)
6294 va_list ap;
6296 if (!condition) {
6297 return;
6300 va_start(ap, fmt);
6302 fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
6303 vfprintf(stderr, fmt, ap);
6304 fprintf(stderr, "\n\n");
6305 va_end(ap);
6307 #ifdef HAVE_BACKTRACE
6309 void *array[40];
6310 int size, i;
6311 char **strings;
6313 size = backtrace(array, 40);
6314 strings = backtrace_symbols(array, size);
6315 for (i = 0; i < size; i++)
6316 fprintf(stderr, "[backtrace] %s\n", strings[i]);
6317 fprintf(stderr, "[backtrace] Include the above lines and the output\n");
6318 fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n");
6320 #endif
6322 exit(1);
6324 #endif
6327 void *Jim_Alloc(int size)
6329 return size ? malloc(size) : NULL;
6332 void Jim_Free(void *ptr)
6334 free(ptr);
6337 void *Jim_Realloc(void *ptr, int size)
6339 return realloc(ptr, size);
6342 char *Jim_StrDup(const char *s)
6344 return strdup(s);
6347 char *Jim_StrDupLen(const char *s, int l)
6349 char *copy = Jim_Alloc(l + 1);
6351 memcpy(copy, s, l + 1);
6352 copy[l] = 0;
6353 return copy;
6358 static jim_wide JimClock(void)
6360 struct timeval tv;
6362 gettimeofday(&tv, NULL);
6363 return (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec;
6368 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
6369 static unsigned int JimHashTableNextPower(unsigned int size);
6370 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
6375 unsigned int Jim_IntHashFunction(unsigned int key)
6377 key += ~(key << 15);
6378 key ^= (key >> 10);
6379 key += (key << 3);
6380 key ^= (key >> 6);
6381 key += ~(key << 11);
6382 key ^= (key >> 16);
6383 return key;
6386 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
6388 unsigned int h = 0;
6390 while (len--)
6391 h += (h << 3) + *buf++;
6392 return h;
6398 static void JimResetHashTable(Jim_HashTable *ht)
6400 ht->table = NULL;
6401 ht->size = 0;
6402 ht->sizemask = 0;
6403 ht->used = 0;
6404 ht->collisions = 0;
6405 #ifdef JIM_RANDOMISE_HASH
6406 ht->uniq = (rand() ^ time(NULL) ^ clock());
6407 #else
6408 ht->uniq = 0;
6409 #endif
6412 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
6414 iter->ht = ht;
6415 iter->index = -1;
6416 iter->entry = NULL;
6417 iter->nextEntry = NULL;
6421 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
6423 JimResetHashTable(ht);
6424 ht->type = type;
6425 ht->privdata = privDataPtr;
6426 return JIM_OK;
6429 void Jim_ResizeHashTable(Jim_HashTable *ht)
6431 int minimal = ht->used;
6433 if (minimal < JIM_HT_INITIAL_SIZE)
6434 minimal = JIM_HT_INITIAL_SIZE;
6435 Jim_ExpandHashTable(ht, minimal);
6439 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
6441 Jim_HashTable n;
6442 unsigned int realsize = JimHashTableNextPower(size), i;
6444 if (size <= ht->used)
6445 return;
6447 Jim_InitHashTable(&n, ht->type, ht->privdata);
6448 n.size = realsize;
6449 n.sizemask = realsize - 1;
6450 n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
6452 n.uniq = ht->uniq;
6455 memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
6457 n.used = ht->used;
6458 for (i = 0; ht->used > 0; i++) {
6459 Jim_HashEntry *he, *nextHe;
6461 if (ht->table[i] == NULL)
6462 continue;
6465 he = ht->table[i];
6466 while (he) {
6467 unsigned int h;
6469 nextHe = he->next;
6471 h = Jim_HashKey(ht, he->key) & n.sizemask;
6472 he->next = n.table[h];
6473 n.table[h] = he;
6474 ht->used--;
6476 he = nextHe;
6479 assert(ht->used == 0);
6480 Jim_Free(ht->table);
6483 *ht = n;
6487 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
6489 Jim_HashEntry *entry;
6491 entry = JimInsertHashEntry(ht, key, 0);
6492 if (entry == NULL)
6493 return JIM_ERR;
6496 Jim_SetHashKey(ht, entry, key);
6497 Jim_SetHashVal(ht, entry, val);
6498 return JIM_OK;
6502 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
6504 int existed;
6505 Jim_HashEntry *entry;
6507 entry = JimInsertHashEntry(ht, key, 1);
6508 if (entry->key) {
6509 if (ht->type->valDestructor && ht->type->valDup) {
6510 void *newval = ht->type->valDup(ht->privdata, val);
6511 ht->type->valDestructor(ht->privdata, entry->u.val);
6512 entry->u.val = newval;
6514 else {
6515 Jim_FreeEntryVal(ht, entry);
6516 Jim_SetHashVal(ht, entry, val);
6518 existed = 1;
6520 else {
6522 Jim_SetHashKey(ht, entry, key);
6523 Jim_SetHashVal(ht, entry, val);
6524 existed = 0;
6527 return existed;
6531 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
6533 unsigned int h;
6534 Jim_HashEntry *he, *prevHe;
6536 if (ht->used == 0)
6537 return JIM_ERR;
6538 h = Jim_HashKey(ht, key) & ht->sizemask;
6539 he = ht->table[h];
6541 prevHe = NULL;
6542 while (he) {
6543 if (Jim_CompareHashKeys(ht, key, he->key)) {
6545 if (prevHe)
6546 prevHe->next = he->next;
6547 else
6548 ht->table[h] = he->next;
6549 Jim_FreeEntryKey(ht, he);
6550 Jim_FreeEntryVal(ht, he);
6551 Jim_Free(he);
6552 ht->used--;
6553 return JIM_OK;
6555 prevHe = he;
6556 he = he->next;
6558 return JIM_ERR;
6562 int Jim_FreeHashTable(Jim_HashTable *ht)
6564 unsigned int i;
6567 for (i = 0; ht->used > 0; i++) {
6568 Jim_HashEntry *he, *nextHe;
6570 if ((he = ht->table[i]) == NULL)
6571 continue;
6572 while (he) {
6573 nextHe = he->next;
6574 Jim_FreeEntryKey(ht, he);
6575 Jim_FreeEntryVal(ht, he);
6576 Jim_Free(he);
6577 ht->used--;
6578 he = nextHe;
6582 Jim_Free(ht->table);
6584 JimResetHashTable(ht);
6585 return JIM_OK;
6588 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
6590 Jim_HashEntry *he;
6591 unsigned int h;
6593 if (ht->used == 0)
6594 return NULL;
6595 h = Jim_HashKey(ht, key) & ht->sizemask;
6596 he = ht->table[h];
6597 while (he) {
6598 if (Jim_CompareHashKeys(ht, key, he->key))
6599 return he;
6600 he = he->next;
6602 return NULL;
6605 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
6607 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
6608 JimInitHashTableIterator(ht, iter);
6609 return iter;
6612 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
6614 while (1) {
6615 if (iter->entry == NULL) {
6616 iter->index++;
6617 if (iter->index >= (signed)iter->ht->size)
6618 break;
6619 iter->entry = iter->ht->table[iter->index];
6621 else {
6622 iter->entry = iter->nextEntry;
6624 if (iter->entry) {
6625 iter->nextEntry = iter->entry->next;
6626 return iter->entry;
6629 return NULL;
6635 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
6637 if (ht->size == 0)
6638 Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
6639 if (ht->size == ht->used)
6640 Jim_ExpandHashTable(ht, ht->size * 2);
6644 static unsigned int JimHashTableNextPower(unsigned int size)
6646 unsigned int i = JIM_HT_INITIAL_SIZE;
6648 if (size >= 2147483648U)
6649 return 2147483648U;
6650 while (1) {
6651 if (i >= size)
6652 return i;
6653 i *= 2;
6657 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
6659 unsigned int h;
6660 Jim_HashEntry *he;
6663 JimExpandHashTableIfNeeded(ht);
6666 h = Jim_HashKey(ht, key) & ht->sizemask;
6668 he = ht->table[h];
6669 while (he) {
6670 if (Jim_CompareHashKeys(ht, key, he->key))
6671 return replace ? he : NULL;
6672 he = he->next;
6676 he = Jim_Alloc(sizeof(*he));
6677 he->next = ht->table[h];
6678 ht->table[h] = he;
6679 ht->used++;
6680 he->key = NULL;
6682 return he;
6687 static unsigned int JimStringCopyHTHashFunction(const void *key)
6689 return Jim_GenHashFunction(key, strlen(key));
6692 static void *JimStringCopyHTDup(void *privdata, const void *key)
6694 return Jim_StrDup(key);
6697 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
6699 return strcmp(key1, key2) == 0;
6702 static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
6704 Jim_Free(key);
6707 static const Jim_HashTableType JimPackageHashTableType = {
6708 JimStringCopyHTHashFunction,
6709 JimStringCopyHTDup,
6710 NULL,
6711 JimStringCopyHTKeyCompare,
6712 JimStringCopyHTKeyDestructor,
6713 NULL
6716 typedef struct AssocDataValue
6718 Jim_InterpDeleteProc *delProc;
6719 void *data;
6720 } AssocDataValue;
6722 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
6724 AssocDataValue *assocPtr = (AssocDataValue *) data;
6726 if (assocPtr->delProc != NULL)
6727 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
6728 Jim_Free(data);
6731 static const Jim_HashTableType JimAssocDataHashTableType = {
6732 JimStringCopyHTHashFunction,
6733 JimStringCopyHTDup,
6734 NULL,
6735 JimStringCopyHTKeyCompare,
6736 JimStringCopyHTKeyDestructor,
6737 JimAssocDataHashTableValueDestructor
6740 void Jim_InitStack(Jim_Stack *stack)
6742 stack->len = 0;
6743 stack->maxlen = 0;
6744 stack->vector = NULL;
6747 void Jim_FreeStack(Jim_Stack *stack)
6749 Jim_Free(stack->vector);
6752 int Jim_StackLen(Jim_Stack *stack)
6754 return stack->len;
6757 void Jim_StackPush(Jim_Stack *stack, void *element)
6759 int neededLen = stack->len + 1;
6761 if (neededLen > stack->maxlen) {
6762 stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
6763 stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
6765 stack->vector[stack->len] = element;
6766 stack->len++;
6769 void *Jim_StackPop(Jim_Stack *stack)
6771 if (stack->len == 0)
6772 return NULL;
6773 stack->len--;
6774 return stack->vector[stack->len];
6777 void *Jim_StackPeek(Jim_Stack *stack)
6779 if (stack->len == 0)
6780 return NULL;
6781 return stack->vector[stack->len - 1];
6784 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
6786 int i;
6788 for (i = 0; i < stack->len; i++)
6789 freeFunc(stack->vector[i]);
6794 #define JIM_TT_NONE 0
6795 #define JIM_TT_STR 1
6796 #define JIM_TT_ESC 2
6797 #define JIM_TT_VAR 3
6798 #define JIM_TT_DICTSUGAR 4
6799 #define JIM_TT_CMD 5
6801 #define JIM_TT_SEP 6
6802 #define JIM_TT_EOL 7
6803 #define JIM_TT_EOF 8
6805 #define JIM_TT_LINE 9
6806 #define JIM_TT_WORD 10
6809 #define JIM_TT_SUBEXPR_START 11
6810 #define JIM_TT_SUBEXPR_END 12
6811 #define JIM_TT_SUBEXPR_COMMA 13
6812 #define JIM_TT_EXPR_INT 14
6813 #define JIM_TT_EXPR_DOUBLE 15
6815 #define JIM_TT_EXPRSUGAR 16
6818 #define JIM_TT_EXPR_OP 20
6820 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
6823 #define JIM_PS_DEF 0
6824 #define JIM_PS_QUOTE 1
6825 #define JIM_PS_DICTSUGAR 2
6827 struct JimParseMissing {
6828 int ch;
6829 int line;
6832 struct JimParserCtx
6834 const char *p;
6835 int len;
6836 int linenr;
6837 const char *tstart;
6838 const char *tend;
6839 int tline;
6840 int tt;
6841 int eof;
6842 int state;
6843 int comment;
6844 struct JimParseMissing missing;
6847 static int JimParseScript(struct JimParserCtx *pc);
6848 static int JimParseSep(struct JimParserCtx *pc);
6849 static int JimParseEol(struct JimParserCtx *pc);
6850 static int JimParseCmd(struct JimParserCtx *pc);
6851 static int JimParseQuote(struct JimParserCtx *pc);
6852 static int JimParseVar(struct JimParserCtx *pc);
6853 static int JimParseBrace(struct JimParserCtx *pc);
6854 static int JimParseStr(struct JimParserCtx *pc);
6855 static int JimParseComment(struct JimParserCtx *pc);
6856 static void JimParseSubCmd(struct JimParserCtx *pc);
6857 static int JimParseSubQuote(struct JimParserCtx *pc);
6858 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
6860 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
6862 pc->p = prg;
6863 pc->len = len;
6864 pc->tstart = NULL;
6865 pc->tend = NULL;
6866 pc->tline = 0;
6867 pc->tt = JIM_TT_NONE;
6868 pc->eof = 0;
6869 pc->state = JIM_PS_DEF;
6870 pc->linenr = linenr;
6871 pc->comment = 1;
6872 pc->missing.ch = ' ';
6873 pc->missing.line = linenr;
6876 static int JimParseScript(struct JimParserCtx *pc)
6878 while (1) {
6879 if (!pc->len) {
6880 pc->tstart = pc->p;
6881 pc->tend = pc->p - 1;
6882 pc->tline = pc->linenr;
6883 pc->tt = JIM_TT_EOL;
6884 pc->eof = 1;
6885 return JIM_OK;
6887 switch (*(pc->p)) {
6888 case '\\':
6889 if (*(pc->p + 1) == '\n' && pc->state == JIM_PS_DEF) {
6890 return JimParseSep(pc);
6892 pc->comment = 0;
6893 return JimParseStr(pc);
6894 case ' ':
6895 case '\t':
6896 case '\r':
6897 case '\f':
6898 if (pc->state == JIM_PS_DEF)
6899 return JimParseSep(pc);
6900 pc->comment = 0;
6901 return JimParseStr(pc);
6902 case '\n':
6903 case ';':
6904 pc->comment = 1;
6905 if (pc->state == JIM_PS_DEF)
6906 return JimParseEol(pc);
6907 return JimParseStr(pc);
6908 case '[':
6909 pc->comment = 0;
6910 return JimParseCmd(pc);
6911 case '$':
6912 pc->comment = 0;
6913 if (JimParseVar(pc) == JIM_ERR) {
6915 pc->tstart = pc->tend = pc->p++;
6916 pc->len--;
6917 pc->tt = JIM_TT_ESC;
6919 return JIM_OK;
6920 case '#':
6921 if (pc->comment) {
6922 JimParseComment(pc);
6923 continue;
6925 return JimParseStr(pc);
6926 default:
6927 pc->comment = 0;
6928 return JimParseStr(pc);
6930 return JIM_OK;
6934 static int JimParseSep(struct JimParserCtx *pc)
6936 pc->tstart = pc->p;
6937 pc->tline = pc->linenr;
6938 while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
6939 if (*pc->p == '\n') {
6940 break;
6942 if (*pc->p == '\\') {
6943 pc->p++;
6944 pc->len--;
6945 pc->linenr++;
6947 pc->p++;
6948 pc->len--;
6950 pc->tend = pc->p - 1;
6951 pc->tt = JIM_TT_SEP;
6952 return JIM_OK;
6955 static int JimParseEol(struct JimParserCtx *pc)
6957 pc->tstart = pc->p;
6958 pc->tline = pc->linenr;
6959 while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
6960 if (*pc->p == '\n')
6961 pc->linenr++;
6962 pc->p++;
6963 pc->len--;
6965 pc->tend = pc->p - 1;
6966 pc->tt = JIM_TT_EOL;
6967 return JIM_OK;
6971 static void JimParseSubBrace(struct JimParserCtx *pc)
6973 int level = 1;
6976 pc->p++;
6977 pc->len--;
6978 while (pc->len) {
6979 switch (*pc->p) {
6980 case '\\':
6981 if (pc->len > 1) {
6982 if (*++pc->p == '\n') {
6983 pc->linenr++;
6985 pc->len--;
6987 break;
6989 case '{':
6990 level++;
6991 break;
6993 case '}':
6994 if (--level == 0) {
6995 pc->tend = pc->p - 1;
6996 pc->p++;
6997 pc->len--;
6998 return;
7000 break;
7002 case '\n':
7003 pc->linenr++;
7004 break;
7006 pc->p++;
7007 pc->len--;
7009 pc->missing.ch = '{';
7010 pc->missing.line = pc->tline;
7011 pc->tend = pc->p - 1;
7014 static int JimParseSubQuote(struct JimParserCtx *pc)
7016 int tt = JIM_TT_STR;
7017 int line = pc->tline;
7020 pc->p++;
7021 pc->len--;
7022 while (pc->len) {
7023 switch (*pc->p) {
7024 case '\\':
7025 if (pc->len > 1) {
7026 if (*++pc->p == '\n') {
7027 pc->linenr++;
7029 pc->len--;
7030 tt = JIM_TT_ESC;
7032 break;
7034 case '"':
7035 pc->tend = pc->p - 1;
7036 pc->p++;
7037 pc->len--;
7038 return tt;
7040 case '[':
7041 JimParseSubCmd(pc);
7042 tt = JIM_TT_ESC;
7043 continue;
7045 case '\n':
7046 pc->linenr++;
7047 break;
7049 case '$':
7050 tt = JIM_TT_ESC;
7051 break;
7053 pc->p++;
7054 pc->len--;
7056 pc->missing.ch = '"';
7057 pc->missing.line = line;
7058 pc->tend = pc->p - 1;
7059 return tt;
7062 static void JimParseSubCmd(struct JimParserCtx *pc)
7064 int level = 1;
7065 int startofword = 1;
7066 int line = pc->tline;
7069 pc->p++;
7070 pc->len--;
7071 while (pc->len) {
7072 switch (*pc->p) {
7073 case '\\':
7074 if (pc->len > 1) {
7075 if (*++pc->p == '\n') {
7076 pc->linenr++;
7078 pc->len--;
7080 break;
7082 case '[':
7083 level++;
7084 break;
7086 case ']':
7087 if (--level == 0) {
7088 pc->tend = pc->p - 1;
7089 pc->p++;
7090 pc->len--;
7091 return;
7093 break;
7095 case '"':
7096 if (startofword) {
7097 JimParseSubQuote(pc);
7098 continue;
7100 break;
7102 case '{':
7103 JimParseSubBrace(pc);
7104 startofword = 0;
7105 continue;
7107 case '\n':
7108 pc->linenr++;
7109 break;
7111 startofword = isspace(UCHAR(*pc->p));
7112 pc->p++;
7113 pc->len--;
7115 pc->missing.ch = '[';
7116 pc->missing.line = line;
7117 pc->tend = pc->p - 1;
7120 static int JimParseBrace(struct JimParserCtx *pc)
7122 pc->tstart = pc->p + 1;
7123 pc->tline = pc->linenr;
7124 pc->tt = JIM_TT_STR;
7125 JimParseSubBrace(pc);
7126 return JIM_OK;
7129 static int JimParseCmd(struct JimParserCtx *pc)
7131 pc->tstart = pc->p + 1;
7132 pc->tline = pc->linenr;
7133 pc->tt = JIM_TT_CMD;
7134 JimParseSubCmd(pc);
7135 return JIM_OK;
7138 static int JimParseQuote(struct JimParserCtx *pc)
7140 pc->tstart = pc->p + 1;
7141 pc->tline = pc->linenr;
7142 pc->tt = JimParseSubQuote(pc);
7143 return JIM_OK;
7146 static int JimParseVar(struct JimParserCtx *pc)
7149 pc->p++;
7150 pc->len--;
7152 #ifdef EXPRSUGAR_BRACKET
7153 if (*pc->p == '[') {
7155 JimParseCmd(pc);
7156 pc->tt = JIM_TT_EXPRSUGAR;
7157 return JIM_OK;
7159 #endif
7161 pc->tstart = pc->p;
7162 pc->tt = JIM_TT_VAR;
7163 pc->tline = pc->linenr;
7165 if (*pc->p == '{') {
7166 pc->tstart = ++pc->p;
7167 pc->len--;
7169 while (pc->len && *pc->p != '}') {
7170 if (*pc->p == '\n') {
7171 pc->linenr++;
7173 pc->p++;
7174 pc->len--;
7176 pc->tend = pc->p - 1;
7177 if (pc->len) {
7178 pc->p++;
7179 pc->len--;
7182 else {
7183 while (1) {
7185 if (pc->p[0] == ':' && pc->p[1] == ':') {
7186 while (*pc->p == ':') {
7187 pc->p++;
7188 pc->len--;
7190 continue;
7192 if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
7193 pc->p++;
7194 pc->len--;
7195 continue;
7197 break;
7200 if (*pc->p == '(') {
7201 int count = 1;
7202 const char *paren = NULL;
7204 pc->tt = JIM_TT_DICTSUGAR;
7206 while (count && pc->len) {
7207 pc->p++;
7208 pc->len--;
7209 if (*pc->p == '\\' && pc->len >= 1) {
7210 pc->p++;
7211 pc->len--;
7213 else if (*pc->p == '(') {
7214 count++;
7216 else if (*pc->p == ')') {
7217 paren = pc->p;
7218 count--;
7221 if (count == 0) {
7222 pc->p++;
7223 pc->len--;
7225 else if (paren) {
7227 paren++;
7228 pc->len += (pc->p - paren);
7229 pc->p = paren;
7231 #ifndef EXPRSUGAR_BRACKET
7232 if (*pc->tstart == '(') {
7233 pc->tt = JIM_TT_EXPRSUGAR;
7235 #endif
7237 pc->tend = pc->p - 1;
7239 if (pc->tstart == pc->p) {
7240 pc->p--;
7241 pc->len++;
7242 return JIM_ERR;
7244 return JIM_OK;
7247 static int JimParseStr(struct JimParserCtx *pc)
7249 if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
7250 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
7252 if (*pc->p == '{') {
7253 return JimParseBrace(pc);
7255 if (*pc->p == '"') {
7256 pc->state = JIM_PS_QUOTE;
7257 pc->p++;
7258 pc->len--;
7260 pc->missing.line = pc->tline;
7263 pc->tstart = pc->p;
7264 pc->tline = pc->linenr;
7265 while (1) {
7266 if (pc->len == 0) {
7267 if (pc->state == JIM_PS_QUOTE) {
7268 pc->missing.ch = '"';
7270 pc->tend = pc->p - 1;
7271 pc->tt = JIM_TT_ESC;
7272 return JIM_OK;
7274 switch (*pc->p) {
7275 case '\\':
7276 if (pc->state == JIM_PS_DEF && *(pc->p + 1) == '\n') {
7277 pc->tend = pc->p - 1;
7278 pc->tt = JIM_TT_ESC;
7279 return JIM_OK;
7281 if (pc->len >= 2) {
7282 if (*(pc->p + 1) == '\n') {
7283 pc->linenr++;
7285 pc->p++;
7286 pc->len--;
7288 else if (pc->len == 1) {
7290 pc->missing.ch = '\\';
7292 break;
7293 case '(':
7295 if (pc->len > 1 && pc->p[1] != '$') {
7296 break;
7298 case ')':
7300 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
7301 if (pc->p == pc->tstart) {
7303 pc->p++;
7304 pc->len--;
7306 pc->tend = pc->p - 1;
7307 pc->tt = JIM_TT_ESC;
7308 return JIM_OK;
7310 break;
7312 case '$':
7313 case '[':
7314 pc->tend = pc->p - 1;
7315 pc->tt = JIM_TT_ESC;
7316 return JIM_OK;
7317 case ' ':
7318 case '\t':
7319 case '\n':
7320 case '\r':
7321 case '\f':
7322 case ';':
7323 if (pc->state == JIM_PS_DEF) {
7324 pc->tend = pc->p - 1;
7325 pc->tt = JIM_TT_ESC;
7326 return JIM_OK;
7328 else if (*pc->p == '\n') {
7329 pc->linenr++;
7331 break;
7332 case '"':
7333 if (pc->state == JIM_PS_QUOTE) {
7334 pc->tend = pc->p - 1;
7335 pc->tt = JIM_TT_ESC;
7336 pc->p++;
7337 pc->len--;
7338 pc->state = JIM_PS_DEF;
7339 return JIM_OK;
7341 break;
7343 pc->p++;
7344 pc->len--;
7346 return JIM_OK;
7349 static int JimParseComment(struct JimParserCtx *pc)
7351 while (*pc->p) {
7352 if (*pc->p == '\\') {
7353 pc->p++;
7354 pc->len--;
7355 if (pc->len == 0) {
7356 pc->missing.ch = '\\';
7357 return JIM_OK;
7359 if (*pc->p == '\n') {
7360 pc->linenr++;
7363 else if (*pc->p == '\n') {
7364 pc->p++;
7365 pc->len--;
7366 pc->linenr++;
7367 break;
7369 pc->p++;
7370 pc->len--;
7372 return JIM_OK;
7376 static int xdigitval(int c)
7378 if (c >= '0' && c <= '9')
7379 return c - '0';
7380 if (c >= 'a' && c <= 'f')
7381 return c - 'a' + 10;
7382 if (c >= 'A' && c <= 'F')
7383 return c - 'A' + 10;
7384 return -1;
7387 static int odigitval(int c)
7389 if (c >= '0' && c <= '7')
7390 return c - '0';
7391 return -1;
7394 static int JimEscape(char *dest, const char *s, int slen)
7396 char *p = dest;
7397 int i, len;
7399 if (slen == -1)
7400 slen = strlen(s);
7402 for (i = 0; i < slen; i++) {
7403 switch (s[i]) {
7404 case '\\':
7405 switch (s[i + 1]) {
7406 case 'a':
7407 *p++ = 0x7;
7408 i++;
7409 break;
7410 case 'b':
7411 *p++ = 0x8;
7412 i++;
7413 break;
7414 case 'f':
7415 *p++ = 0xc;
7416 i++;
7417 break;
7418 case 'n':
7419 *p++ = 0xa;
7420 i++;
7421 break;
7422 case 'r':
7423 *p++ = 0xd;
7424 i++;
7425 break;
7426 case 't':
7427 *p++ = 0x9;
7428 i++;
7429 break;
7430 case 'u':
7431 case 'U':
7432 case 'x':
7434 unsigned val = 0;
7435 int k;
7436 int maxchars = 2;
7438 i++;
7440 if (s[i] == 'U') {
7441 maxchars = 8;
7443 else if (s[i] == 'u') {
7444 if (s[i + 1] == '{') {
7445 maxchars = 6;
7446 i++;
7448 else {
7449 maxchars = 4;
7453 for (k = 0; k < maxchars; k++) {
7454 int c = xdigitval(s[i + k + 1]);
7455 if (c == -1) {
7456 break;
7458 val = (val << 4) | c;
7461 if (s[i] == '{') {
7462 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
7464 i--;
7465 k = 0;
7467 else {
7469 k++;
7472 if (k) {
7474 if (s[i] == 'x') {
7475 *p++ = val;
7477 else {
7478 p += utf8_fromunicode(p, val);
7480 i += k;
7481 break;
7484 *p++ = s[i];
7486 break;
7487 case 'v':
7488 *p++ = 0xb;
7489 i++;
7490 break;
7491 case '\0':
7492 *p++ = '\\';
7493 i++;
7494 break;
7495 case '\n':
7497 *p++ = ' ';
7498 do {
7499 i++;
7500 } while (s[i + 1] == ' ' || s[i + 1] == '\t');
7501 break;
7502 case '0':
7503 case '1':
7504 case '2':
7505 case '3':
7506 case '4':
7507 case '5':
7508 case '6':
7509 case '7':
7512 int val = 0;
7513 int c = odigitval(s[i + 1]);
7515 val = c;
7516 c = odigitval(s[i + 2]);
7517 if (c == -1) {
7518 *p++ = val;
7519 i++;
7520 break;
7522 val = (val * 8) + c;
7523 c = odigitval(s[i + 3]);
7524 if (c == -1) {
7525 *p++ = val;
7526 i += 2;
7527 break;
7529 val = (val * 8) + c;
7530 *p++ = val;
7531 i += 3;
7533 break;
7534 default:
7535 *p++ = s[i + 1];
7536 i++;
7537 break;
7539 break;
7540 default:
7541 *p++ = s[i];
7542 break;
7545 len = p - dest;
7546 *p = '\0';
7547 return len;
7550 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
7552 const char *start, *end;
7553 char *token;
7554 int len;
7556 start = pc->tstart;
7557 end = pc->tend;
7558 if (start > end) {
7559 len = 0;
7560 token = Jim_Alloc(1);
7561 token[0] = '\0';
7563 else {
7564 len = (end - start) + 1;
7565 token = Jim_Alloc(len + 1);
7566 if (pc->tt != JIM_TT_ESC) {
7568 memcpy(token, start, len);
7569 token[len] = '\0';
7571 else {
7573 len = JimEscape(token, start, len);
7577 return Jim_NewStringObjNoAlloc(interp, token, len);
7580 int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr)
7582 struct JimParserCtx parser;
7584 JimParserInit(&parser, s, len, 1);
7585 while (!parser.eof) {
7586 JimParseScript(&parser);
7588 if (stateCharPtr) {
7589 *stateCharPtr = parser.missing.ch;
7591 return parser.missing.ch == ' ';
7594 static int JimParseListSep(struct JimParserCtx *pc);
7595 static int JimParseListStr(struct JimParserCtx *pc);
7596 static int JimParseListQuote(struct JimParserCtx *pc);
7598 static int JimParseList(struct JimParserCtx *pc)
7600 if (isspace(UCHAR(*pc->p))) {
7601 return JimParseListSep(pc);
7603 switch (*pc->p) {
7604 case '"':
7605 return JimParseListQuote(pc);
7607 case '{':
7608 return JimParseBrace(pc);
7610 default:
7611 if (pc->len) {
7612 return JimParseListStr(pc);
7614 break;
7617 pc->tstart = pc->tend = pc->p;
7618 pc->tline = pc->linenr;
7619 pc->tt = JIM_TT_EOL;
7620 pc->eof = 1;
7621 return JIM_OK;
7624 static int JimParseListSep(struct JimParserCtx *pc)
7626 pc->tstart = pc->p;
7627 pc->tline = pc->linenr;
7628 while (isspace(UCHAR(*pc->p))) {
7629 if (*pc->p == '\n') {
7630 pc->linenr++;
7632 pc->p++;
7633 pc->len--;
7635 pc->tend = pc->p - 1;
7636 pc->tt = JIM_TT_SEP;
7637 return JIM_OK;
7640 static int JimParseListQuote(struct JimParserCtx *pc)
7642 pc->p++;
7643 pc->len--;
7645 pc->tstart = pc->p;
7646 pc->tline = pc->linenr;
7647 pc->tt = JIM_TT_STR;
7649 while (pc->len) {
7650 switch (*pc->p) {
7651 case '\\':
7652 pc->tt = JIM_TT_ESC;
7653 if (--pc->len == 0) {
7655 pc->tend = pc->p;
7656 return JIM_OK;
7658 pc->p++;
7659 break;
7660 case '\n':
7661 pc->linenr++;
7662 break;
7663 case '"':
7664 pc->tend = pc->p - 1;
7665 pc->p++;
7666 pc->len--;
7667 return JIM_OK;
7669 pc->p++;
7670 pc->len--;
7673 pc->tend = pc->p - 1;
7674 return JIM_OK;
7677 static int JimParseListStr(struct JimParserCtx *pc)
7679 pc->tstart = pc->p;
7680 pc->tline = pc->linenr;
7681 pc->tt = JIM_TT_STR;
7683 while (pc->len) {
7684 if (isspace(UCHAR(*pc->p))) {
7685 pc->tend = pc->p - 1;
7686 return JIM_OK;
7688 if (*pc->p == '\\') {
7689 if (--pc->len == 0) {
7691 pc->tend = pc->p;
7692 return JIM_OK;
7694 pc->tt = JIM_TT_ESC;
7695 pc->p++;
7697 pc->p++;
7698 pc->len--;
7700 pc->tend = pc->p - 1;
7701 return JIM_OK;
7706 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
7708 Jim_Obj *objPtr;
7711 if (interp->freeList != NULL) {
7713 objPtr = interp->freeList;
7714 interp->freeList = objPtr->nextObjPtr;
7716 else {
7718 objPtr = Jim_Alloc(sizeof(*objPtr));
7721 objPtr->refCount = 0;
7724 objPtr->prevObjPtr = NULL;
7725 objPtr->nextObjPtr = interp->liveList;
7726 if (interp->liveList)
7727 interp->liveList->prevObjPtr = objPtr;
7728 interp->liveList = objPtr;
7730 return objPtr;
7733 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
7736 JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
7737 objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
7740 Jim_FreeIntRep(interp, objPtr);
7742 if (objPtr->bytes != NULL) {
7743 if (objPtr->bytes != JimEmptyStringRep)
7744 Jim_Free(objPtr->bytes);
7747 if (objPtr->prevObjPtr)
7748 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
7749 if (objPtr->nextObjPtr)
7750 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
7751 if (interp->liveList == objPtr)
7752 interp->liveList = objPtr->nextObjPtr;
7753 #ifdef JIM_DISABLE_OBJECT_POOL
7754 Jim_Free(objPtr);
7755 #else
7757 objPtr->prevObjPtr = NULL;
7758 objPtr->nextObjPtr = interp->freeList;
7759 if (interp->freeList)
7760 interp->freeList->prevObjPtr = objPtr;
7761 interp->freeList = objPtr;
7762 objPtr->refCount = -1;
7763 #endif
7767 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
7769 if (objPtr->bytes != NULL) {
7770 if (objPtr->bytes != JimEmptyStringRep)
7771 Jim_Free(objPtr->bytes);
7773 objPtr->bytes = NULL;
7777 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
7779 Jim_Obj *dupPtr;
7781 dupPtr = Jim_NewObj(interp);
7782 if (objPtr->bytes == NULL) {
7784 dupPtr->bytes = NULL;
7786 else if (objPtr->length == 0) {
7788 dupPtr->bytes = JimEmptyStringRep;
7789 dupPtr->length = 0;
7790 dupPtr->typePtr = NULL;
7791 return dupPtr;
7793 else {
7794 dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
7795 dupPtr->length = objPtr->length;
7797 memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
7801 dupPtr->typePtr = objPtr->typePtr;
7802 if (objPtr->typePtr != NULL) {
7803 if (objPtr->typePtr->dupIntRepProc == NULL) {
7804 dupPtr->internalRep = objPtr->internalRep;
7806 else {
7808 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
7811 return dupPtr;
7814 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
7816 if (objPtr->bytes == NULL) {
7818 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7819 objPtr->typePtr->updateStringProc(objPtr);
7821 if (lenPtr)
7822 *lenPtr = objPtr->length;
7823 return objPtr->bytes;
7827 int Jim_Length(Jim_Obj *objPtr)
7829 if (objPtr->bytes == NULL) {
7831 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7832 objPtr->typePtr->updateStringProc(objPtr);
7834 return objPtr->length;
7838 const char *Jim_String(Jim_Obj *objPtr)
7840 if (objPtr->bytes == NULL) {
7842 JimPanic((objPtr->typePtr == NULL, "UpdateStringProc called against typeless value."));
7843 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7844 objPtr->typePtr->updateStringProc(objPtr);
7846 return objPtr->bytes;
7849 static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
7851 objPtr->bytes = Jim_StrDup(str);
7852 objPtr->length = strlen(str);
7855 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
7856 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7858 static const Jim_ObjType dictSubstObjType = {
7859 "dict-substitution",
7860 FreeDictSubstInternalRep,
7861 DupDictSubstInternalRep,
7862 NULL,
7863 JIM_TYPE_NONE,
7866 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
7868 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
7871 static const Jim_ObjType interpolatedObjType = {
7872 "interpolated",
7873 FreeInterpolatedInternalRep,
7874 NULL,
7875 NULL,
7876 JIM_TYPE_NONE,
7879 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7880 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
7882 static const Jim_ObjType stringObjType = {
7883 "string",
7884 NULL,
7885 DupStringInternalRep,
7886 NULL,
7887 JIM_TYPE_REFERENCES,
7890 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
7892 JIM_NOTUSED(interp);
7894 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
7895 dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
7898 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
7900 if (objPtr->typePtr != &stringObjType) {
7902 if (objPtr->bytes == NULL) {
7904 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7905 objPtr->typePtr->updateStringProc(objPtr);
7908 Jim_FreeIntRep(interp, objPtr);
7910 objPtr->typePtr = &stringObjType;
7911 objPtr->internalRep.strValue.maxLength = objPtr->length;
7913 objPtr->internalRep.strValue.charLength = -1;
7915 return JIM_OK;
7918 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
7920 #ifdef JIM_UTF8
7921 SetStringFromAny(interp, objPtr);
7923 if (objPtr->internalRep.strValue.charLength < 0) {
7924 objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
7926 return objPtr->internalRep.strValue.charLength;
7927 #else
7928 return Jim_Length(objPtr);
7929 #endif
7933 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
7935 Jim_Obj *objPtr = Jim_NewObj(interp);
7938 if (len == -1)
7939 len = strlen(s);
7941 if (len == 0) {
7942 objPtr->bytes = JimEmptyStringRep;
7944 else {
7945 objPtr->bytes = Jim_Alloc(len + 1);
7946 memcpy(objPtr->bytes, s, len);
7947 objPtr->bytes[len] = '\0';
7949 objPtr->length = len;
7952 objPtr->typePtr = NULL;
7953 return objPtr;
7957 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
7959 #ifdef JIM_UTF8
7961 int bytelen = utf8_index(s, charlen);
7963 Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
7966 objPtr->typePtr = &stringObjType;
7967 objPtr->internalRep.strValue.maxLength = bytelen;
7968 objPtr->internalRep.strValue.charLength = charlen;
7970 return objPtr;
7971 #else
7972 return Jim_NewStringObj(interp, s, charlen);
7973 #endif
7976 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
7978 Jim_Obj *objPtr = Jim_NewObj(interp);
7980 objPtr->bytes = s;
7981 objPtr->length = (len == -1) ? strlen(s) : len;
7982 objPtr->typePtr = NULL;
7983 return objPtr;
7986 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
7988 int needlen;
7990 if (len == -1)
7991 len = strlen(str);
7992 needlen = objPtr->length + len;
7993 if (objPtr->internalRep.strValue.maxLength < needlen ||
7994 objPtr->internalRep.strValue.maxLength == 0) {
7995 needlen *= 2;
7997 if (needlen < 7) {
7998 needlen = 7;
8000 if (objPtr->bytes == JimEmptyStringRep) {
8001 objPtr->bytes = Jim_Alloc(needlen + 1);
8003 else {
8004 objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
8006 objPtr->internalRep.strValue.maxLength = needlen;
8008 memcpy(objPtr->bytes + objPtr->length, str, len);
8009 objPtr->bytes[objPtr->length + len] = '\0';
8011 if (objPtr->internalRep.strValue.charLength >= 0) {
8013 objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
8015 objPtr->length += len;
8018 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
8020 JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
8021 SetStringFromAny(interp, objPtr);
8022 StringAppendString(objPtr, str, len);
8025 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
8027 int len;
8028 const char *str = Jim_GetString(appendObjPtr, &len);
8029 Jim_AppendString(interp, objPtr, str, len);
8032 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
8034 va_list ap;
8036 SetStringFromAny(interp, objPtr);
8037 va_start(ap, objPtr);
8038 while (1) {
8039 const char *s = va_arg(ap, const char *);
8041 if (s == NULL)
8042 break;
8043 Jim_AppendString(interp, objPtr, s, -1);
8045 va_end(ap);
8048 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
8050 if (aObjPtr == bObjPtr) {
8051 return 1;
8053 else {
8054 int Alen, Blen;
8055 const char *sA = Jim_GetString(aObjPtr, &Alen);
8056 const char *sB = Jim_GetString(bObjPtr, &Blen);
8058 return Alen == Blen && memcmp(sA, sB, Alen) == 0;
8062 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
8064 return JimGlobMatch(Jim_String(patternObjPtr), Jim_String(objPtr), nocase);
8067 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8069 int l1, l2;
8070 const char *s1 = Jim_GetString(firstObjPtr, &l1);
8071 const char *s2 = Jim_GetString(secondObjPtr, &l2);
8073 if (nocase) {
8075 return JimStringCompareLen(s1, s2, -1, nocase);
8077 return JimStringCompare(s1, l1, s2, l2);
8080 int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8082 const char *s1 = Jim_String(firstObjPtr);
8083 const char *s2 = Jim_String(secondObjPtr);
8085 return JimStringCompareLen(s1, s2, Jim_Utf8Length(interp, firstObjPtr), nocase);
8088 static int JimRelToAbsIndex(int len, int idx)
8090 if (idx < 0)
8091 return len + idx;
8092 return idx;
8095 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
8097 int rangeLen;
8099 if (*firstPtr > *lastPtr) {
8100 rangeLen = 0;
8102 else {
8103 rangeLen = *lastPtr - *firstPtr + 1;
8104 if (rangeLen) {
8105 if (*firstPtr < 0) {
8106 rangeLen += *firstPtr;
8107 *firstPtr = 0;
8109 if (*lastPtr >= len) {
8110 rangeLen -= (*lastPtr - (len - 1));
8111 *lastPtr = len - 1;
8115 if (rangeLen < 0)
8116 rangeLen = 0;
8118 *rangeLenPtr = rangeLen;
8121 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
8122 int len, int *first, int *last, int *range)
8124 if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
8125 return JIM_ERR;
8127 if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
8128 return JIM_ERR;
8130 *first = JimRelToAbsIndex(len, *first);
8131 *last = JimRelToAbsIndex(len, *last);
8132 JimRelToAbsRange(len, first, last, range);
8133 return JIM_OK;
8136 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
8137 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8139 int first, last;
8140 const char *str;
8141 int rangeLen;
8142 int bytelen;
8144 str = Jim_GetString(strObjPtr, &bytelen);
8146 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
8147 return NULL;
8150 if (first == 0 && rangeLen == bytelen) {
8151 return strObjPtr;
8153 return Jim_NewStringObj(interp, str + first, rangeLen);
8156 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
8157 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8159 #ifdef JIM_UTF8
8160 int first, last;
8161 const char *str;
8162 int len, rangeLen;
8163 int bytelen;
8165 str = Jim_GetString(strObjPtr, &bytelen);
8166 len = Jim_Utf8Length(interp, strObjPtr);
8168 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8169 return NULL;
8172 if (first == 0 && rangeLen == len) {
8173 return strObjPtr;
8175 if (len == bytelen) {
8177 return Jim_NewStringObj(interp, str + first, rangeLen);
8179 return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
8180 #else
8181 return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
8182 #endif
8185 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
8186 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
8188 int first, last;
8189 const char *str;
8190 int len, rangeLen;
8191 Jim_Obj *objPtr;
8193 len = Jim_Utf8Length(interp, strObjPtr);
8195 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8196 return NULL;
8199 if (last < first) {
8200 return strObjPtr;
8203 str = Jim_String(strObjPtr);
8206 objPtr = Jim_NewStringObjUtf8(interp, str, first);
8209 if (newStrObj) {
8210 Jim_AppendObj(interp, objPtr, newStrObj);
8214 Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
8216 return objPtr;
8219 static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
8221 while (*str) {
8222 int c;
8223 str += utf8_tounicode(str, &c);
8224 dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c));
8226 *dest = 0;
8229 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
8231 char *buf;
8232 int len;
8233 const char *str;
8235 SetStringFromAny(interp, strObjPtr);
8237 str = Jim_GetString(strObjPtr, &len);
8239 #ifdef JIM_UTF8
8240 len *= 2;
8241 #endif
8242 buf = Jim_Alloc(len + 1);
8243 JimStrCopyUpperLower(buf, str, 0);
8244 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8247 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
8249 char *buf;
8250 const char *str;
8251 int len;
8253 if (strObjPtr->typePtr != &stringObjType) {
8254 SetStringFromAny(interp, strObjPtr);
8257 str = Jim_GetString(strObjPtr, &len);
8259 #ifdef JIM_UTF8
8260 len *= 2;
8261 #endif
8262 buf = Jim_Alloc(len + 1);
8263 JimStrCopyUpperLower(buf, str, 1);
8264 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8267 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
8269 char *buf, *p;
8270 int len;
8271 int c;
8272 const char *str;
8274 str = Jim_GetString(strObjPtr, &len);
8275 if (len == 0) {
8276 return strObjPtr;
8278 #ifdef JIM_UTF8
8279 len *= 2;
8280 #endif
8281 buf = p = Jim_Alloc(len + 1);
8283 str += utf8_tounicode(str, &c);
8284 p += utf8_getchars(p, utf8_title(c));
8286 JimStrCopyUpperLower(p, str, 0);
8288 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8291 static const char *utf8_memchr(const char *str, int len, int c)
8293 #ifdef JIM_UTF8
8294 while (len) {
8295 int sc;
8296 int n = utf8_tounicode(str, &sc);
8297 if (sc == c) {
8298 return str;
8300 str += n;
8301 len -= n;
8303 return NULL;
8304 #else
8305 return memchr(str, c, len);
8306 #endif
8309 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
8311 while (len) {
8312 int c;
8313 int n = utf8_tounicode(str, &c);
8315 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8317 break;
8319 str += n;
8320 len -= n;
8322 return str;
8325 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
8327 str += len;
8329 while (len) {
8330 int c;
8331 int n = utf8_prev_len(str, len);
8333 len -= n;
8334 str -= n;
8336 n = utf8_tounicode(str, &c);
8338 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8339 return str + n;
8343 return NULL;
8346 static const char default_trim_chars[] = " \t\n\r";
8348 static int default_trim_chars_len = sizeof(default_trim_chars);
8350 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8352 int len;
8353 const char *str = Jim_GetString(strObjPtr, &len);
8354 const char *trimchars = default_trim_chars;
8355 int trimcharslen = default_trim_chars_len;
8356 const char *newstr;
8358 if (trimcharsObjPtr) {
8359 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8362 newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
8363 if (newstr == str) {
8364 return strObjPtr;
8367 return Jim_NewStringObj(interp, newstr, len - (newstr - str));
8370 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8372 int len;
8373 const char *trimchars = default_trim_chars;
8374 int trimcharslen = default_trim_chars_len;
8375 const char *nontrim;
8377 if (trimcharsObjPtr) {
8378 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8381 SetStringFromAny(interp, strObjPtr);
8383 len = Jim_Length(strObjPtr);
8384 nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
8386 if (nontrim == NULL) {
8388 return Jim_NewEmptyStringObj(interp);
8390 if (nontrim == strObjPtr->bytes + len) {
8392 return strObjPtr;
8395 if (Jim_IsShared(strObjPtr)) {
8396 strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
8398 else {
8400 strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
8401 strObjPtr->length = (nontrim - strObjPtr->bytes);
8404 return strObjPtr;
8407 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8410 Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
8413 strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
8416 if (objPtr != strObjPtr && objPtr->refCount == 0) {
8418 Jim_FreeNewObj(interp, objPtr);
8421 return strObjPtr;
8425 #ifdef HAVE_ISASCII
8426 #define jim_isascii isascii
8427 #else
8428 static int jim_isascii(int c)
8430 return !(c & ~0x7f);
8432 #endif
8434 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
8436 static const char * const strclassnames[] = {
8437 "integer", "alpha", "alnum", "ascii", "digit",
8438 "double", "lower", "upper", "space", "xdigit",
8439 "control", "print", "graph", "punct",
8440 NULL
8442 enum {
8443 STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
8444 STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
8445 STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT
8447 int strclass;
8448 int len;
8449 int i;
8450 const char *str;
8451 int (*isclassfunc)(int c) = NULL;
8453 if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
8454 return JIM_ERR;
8457 str = Jim_GetString(strObjPtr, &len);
8458 if (len == 0) {
8459 Jim_SetResultBool(interp, !strict);
8460 return JIM_OK;
8463 switch (strclass) {
8464 case STR_IS_INTEGER:
8466 jim_wide w;
8467 Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
8468 return JIM_OK;
8471 case STR_IS_DOUBLE:
8473 double d;
8474 Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
8475 return JIM_OK;
8478 case STR_IS_ALPHA: isclassfunc = isalpha; break;
8479 case STR_IS_ALNUM: isclassfunc = isalnum; break;
8480 case STR_IS_ASCII: isclassfunc = jim_isascii; break;
8481 case STR_IS_DIGIT: isclassfunc = isdigit; break;
8482 case STR_IS_LOWER: isclassfunc = islower; break;
8483 case STR_IS_UPPER: isclassfunc = isupper; break;
8484 case STR_IS_SPACE: isclassfunc = isspace; break;
8485 case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
8486 case STR_IS_CONTROL: isclassfunc = iscntrl; break;
8487 case STR_IS_PRINT: isclassfunc = isprint; break;
8488 case STR_IS_GRAPH: isclassfunc = isgraph; break;
8489 case STR_IS_PUNCT: isclassfunc = ispunct; break;
8490 default:
8491 return JIM_ERR;
8494 for (i = 0; i < len; i++) {
8495 if (!isclassfunc(str[i])) {
8496 Jim_SetResultBool(interp, 0);
8497 return JIM_OK;
8500 Jim_SetResultBool(interp, 1);
8501 return JIM_OK;
8506 static const Jim_ObjType comparedStringObjType = {
8507 "compared-string",
8508 NULL,
8509 NULL,
8510 NULL,
8511 JIM_TYPE_REFERENCES,
8514 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
8516 if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
8517 return 1;
8519 else {
8520 const char *objStr = Jim_String(objPtr);
8522 if (strcmp(str, objStr) != 0)
8523 return 0;
8525 if (objPtr->typePtr != &comparedStringObjType) {
8526 Jim_FreeIntRep(interp, objPtr);
8527 objPtr->typePtr = &comparedStringObjType;
8529 objPtr->internalRep.ptr = (char *)str;
8530 return 1;
8534 static int qsortCompareStringPointers(const void *a, const void *b)
8536 char *const *sa = (char *const *)a;
8537 char *const *sb = (char *const *)b;
8539 return strcmp(*sa, *sb);
8544 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8545 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8547 static const Jim_ObjType sourceObjType = {
8548 "source",
8549 FreeSourceInternalRep,
8550 DupSourceInternalRep,
8551 NULL,
8552 JIM_TYPE_REFERENCES,
8555 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8557 Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
8560 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8562 dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
8563 Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
8566 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
8567 Jim_Obj *fileNameObj, int lineNumber)
8569 JimPanic((Jim_IsShared(objPtr), "JimSetSourceInfo called with shared object"));
8570 JimPanic((objPtr->typePtr != NULL, "JimSetSourceInfo called with typed object"));
8571 Jim_IncrRefCount(fileNameObj);
8572 objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
8573 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
8574 objPtr->typePtr = &sourceObjType;
8577 static const Jim_ObjType scriptLineObjType = {
8578 "scriptline",
8579 NULL,
8580 NULL,
8581 NULL,
8582 JIM_NONE,
8585 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
8587 Jim_Obj *objPtr;
8589 #ifdef DEBUG_SHOW_SCRIPT
8590 char buf[100];
8591 snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
8592 objPtr = Jim_NewStringObj(interp, buf, -1);
8593 #else
8594 objPtr = Jim_NewEmptyStringObj(interp);
8595 #endif
8596 objPtr->typePtr = &scriptLineObjType;
8597 objPtr->internalRep.scriptLineValue.argc = argc;
8598 objPtr->internalRep.scriptLineValue.line = line;
8600 return objPtr;
8603 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8604 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8605 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8606 static int JimParseCheckMissing(Jim_Interp *interp, int ch);
8608 static const Jim_ObjType scriptObjType = {
8609 "script",
8610 FreeScriptInternalRep,
8611 DupScriptInternalRep,
8612 NULL,
8613 JIM_TYPE_REFERENCES,
8616 typedef struct ScriptToken
8618 Jim_Obj *objPtr;
8619 int type;
8620 } ScriptToken;
8622 typedef struct ScriptObj
8624 ScriptToken *token;
8625 Jim_Obj *fileNameObj;
8626 int len;
8627 int substFlags;
8628 int inUse; /* Used to share a ScriptObj. Currently
8629 only used by Jim_EvalObj() as protection against
8630 shimmering of the currently evaluated object. */
8631 int firstline;
8632 int linenr;
8633 int missing;
8634 } ScriptObj;
8636 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8638 int i;
8639 struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
8641 if (--script->inUse != 0)
8642 return;
8643 for (i = 0; i < script->len; i++) {
8644 Jim_DecrRefCount(interp, script->token[i].objPtr);
8646 Jim_Free(script->token);
8647 Jim_DecrRefCount(interp, script->fileNameObj);
8648 Jim_Free(script);
8651 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8653 JIM_NOTUSED(interp);
8654 JIM_NOTUSED(srcPtr);
8656 dupPtr->typePtr = NULL;
8659 typedef struct
8661 const char *token;
8662 int len;
8663 int type;
8664 int line;
8665 } ParseToken;
8667 typedef struct
8670 ParseToken *list;
8671 int size;
8672 int count;
8673 ParseToken static_list[20];
8674 } ParseTokenList;
8676 static void ScriptTokenListInit(ParseTokenList *tokenlist)
8678 tokenlist->list = tokenlist->static_list;
8679 tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
8680 tokenlist->count = 0;
8683 static void ScriptTokenListFree(ParseTokenList *tokenlist)
8685 if (tokenlist->list != tokenlist->static_list) {
8686 Jim_Free(tokenlist->list);
8690 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
8691 int line)
8693 ParseToken *t;
8695 if (tokenlist->count == tokenlist->size) {
8697 tokenlist->size *= 2;
8698 if (tokenlist->list != tokenlist->static_list) {
8699 tokenlist->list =
8700 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
8702 else {
8704 tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
8705 memcpy(tokenlist->list, tokenlist->static_list,
8706 tokenlist->count * sizeof(*tokenlist->list));
8709 t = &tokenlist->list[tokenlist->count++];
8710 t->token = token;
8711 t->len = len;
8712 t->type = type;
8713 t->line = line;
8716 static int JimCountWordTokens(ParseToken *t)
8718 int expand = 1;
8719 int count = 0;
8722 if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
8723 if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
8725 expand = -1;
8726 t++;
8731 while (!TOKEN_IS_SEP(t->type)) {
8732 t++;
8733 count++;
8736 return count * expand;
8739 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
8741 Jim_Obj *objPtr;
8743 if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
8745 int len = t->len;
8746 char *str = Jim_Alloc(len + 1);
8747 len = JimEscape(str, t->token, len);
8748 objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
8750 else {
8751 objPtr = Jim_NewStringObj(interp, t->token, t->len);
8753 return objPtr;
8756 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
8757 ParseTokenList *tokenlist)
8759 int i;
8760 struct ScriptToken *token;
8762 int lineargs = 0;
8764 ScriptToken *linefirst;
8765 int count;
8766 int linenr;
8768 #ifdef DEBUG_SHOW_SCRIPT_TOKENS
8769 printf("==== Tokens ====\n");
8770 for (i = 0; i < tokenlist->count; i++) {
8771 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
8772 tokenlist->list[i].len, tokenlist->list[i].token);
8774 #endif
8777 count = tokenlist->count;
8778 for (i = 0; i < tokenlist->count; i++) {
8779 if (tokenlist->list[i].type == JIM_TT_EOL) {
8780 count++;
8783 linenr = script->firstline = tokenlist->list[0].line;
8785 token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
8788 linefirst = token++;
8790 for (i = 0; i < tokenlist->count; ) {
8792 int wordtokens;
8795 while (tokenlist->list[i].type == JIM_TT_SEP) {
8796 i++;
8799 wordtokens = JimCountWordTokens(tokenlist->list + i);
8801 if (wordtokens == 0) {
8803 if (lineargs) {
8804 linefirst->type = JIM_TT_LINE;
8805 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
8806 Jim_IncrRefCount(linefirst->objPtr);
8809 lineargs = 0;
8810 linefirst = token++;
8812 i++;
8813 continue;
8815 else if (wordtokens != 1) {
8817 token->type = JIM_TT_WORD;
8818 token->objPtr = Jim_NewIntObj(interp, wordtokens);
8819 Jim_IncrRefCount(token->objPtr);
8820 token++;
8821 if (wordtokens < 0) {
8823 i++;
8824 wordtokens = -wordtokens - 1;
8825 lineargs--;
8829 if (lineargs == 0) {
8831 linenr = tokenlist->list[i].line;
8833 lineargs++;
8836 while (wordtokens--) {
8837 const ParseToken *t = &tokenlist->list[i++];
8839 token->type = t->type;
8840 token->objPtr = JimMakeScriptObj(interp, t);
8841 Jim_IncrRefCount(token->objPtr);
8843 JimSetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
8844 token++;
8848 if (lineargs == 0) {
8849 token--;
8852 script->len = token - script->token;
8854 JimPanic((script->len >= count, "allocated script array is too short"));
8856 #ifdef DEBUG_SHOW_SCRIPT
8857 printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj));
8858 for (i = 0; i < script->len; i++) {
8859 const ScriptToken *t = &script->token[i];
8860 printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
8862 #endif
8866 static int JimParseCheckMissing(Jim_Interp *interp, int ch)
8868 const char *msg;
8870 switch (ch) {
8871 case '\\':
8872 case ' ':
8873 return JIM_OK;
8875 case '[':
8876 msg = "unmatched \"[\"";
8877 break;
8878 case '{':
8879 msg = "missing close-brace";
8880 break;
8881 case '"':
8882 default:
8883 msg = "missing quote";
8884 break;
8887 Jim_SetResultString(interp, msg, -1);
8888 return JIM_ERR;
8891 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
8892 ParseTokenList *tokenlist)
8894 int i;
8895 struct ScriptToken *token;
8897 token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
8899 for (i = 0; i < tokenlist->count; i++) {
8900 const ParseToken *t = &tokenlist->list[i];
8903 token->type = t->type;
8904 token->objPtr = JimMakeScriptObj(interp, t);
8905 Jim_IncrRefCount(token->objPtr);
8906 token++;
8909 script->len = i;
8912 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
8914 int scriptTextLen;
8915 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8916 struct JimParserCtx parser;
8917 struct ScriptObj *script;
8918 ParseTokenList tokenlist;
8919 int line = 1;
8922 if (objPtr->typePtr == &sourceObjType) {
8923 line = objPtr->internalRep.sourceValue.lineNumber;
8927 ScriptTokenListInit(&tokenlist);
8929 JimParserInit(&parser, scriptText, scriptTextLen, line);
8930 while (!parser.eof) {
8931 JimParseScript(&parser);
8932 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
8933 parser.tline);
8937 ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
8940 script = Jim_Alloc(sizeof(*script));
8941 memset(script, 0, sizeof(*script));
8942 script->inUse = 1;
8943 if (objPtr->typePtr == &sourceObjType) {
8944 script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
8946 else {
8947 script->fileNameObj = interp->emptyObj;
8949 Jim_IncrRefCount(script->fileNameObj);
8950 script->missing = parser.missing.ch;
8951 script->linenr = parser.missing.line;
8953 ScriptObjAddTokens(interp, script, &tokenlist);
8956 ScriptTokenListFree(&tokenlist);
8959 Jim_FreeIntRep(interp, objPtr);
8960 Jim_SetIntRepPtr(objPtr, script);
8961 objPtr->typePtr = &scriptObjType;
8964 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script);
8966 ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
8968 if (objPtr == interp->emptyObj) {
8970 objPtr = interp->nullScriptObj;
8973 if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
8974 JimSetScriptFromAny(interp, objPtr);
8977 return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
8980 static int JimScriptValid(Jim_Interp *interp, ScriptObj *script)
8982 if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
8983 JimAddErrorToStack(interp, script);
8984 return 0;
8986 return 1;
8990 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
8992 cmdPtr->inUse++;
8995 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
8997 if (--cmdPtr->inUse == 0) {
8998 if (cmdPtr->isproc) {
8999 Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
9000 Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
9001 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9002 if (cmdPtr->u.proc.staticVars) {
9003 Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
9004 Jim_Free(cmdPtr->u.proc.staticVars);
9007 else {
9009 if (cmdPtr->u.native.delProc) {
9010 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
9013 if (cmdPtr->prevCmd) {
9015 JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
9017 Jim_Free(cmdPtr);
9022 static void JimVariablesHTValDestructor(void *interp, void *val)
9024 Jim_DecrRefCount(interp, ((Jim_Var *)val)->objPtr);
9025 Jim_Free(val);
9028 static const Jim_HashTableType JimVariablesHashTableType = {
9029 JimStringCopyHTHashFunction,
9030 JimStringCopyHTDup,
9031 NULL,
9032 JimStringCopyHTKeyCompare,
9033 JimStringCopyHTKeyDestructor,
9034 JimVariablesHTValDestructor
9037 static void JimCommandsHT_ValDestructor(void *interp, void *val)
9039 JimDecrCmdRefCount(interp, val);
9042 static const Jim_HashTableType JimCommandsHashTableType = {
9043 JimStringCopyHTHashFunction,
9044 JimStringCopyHTDup,
9045 NULL,
9046 JimStringCopyHTKeyCompare,
9047 JimStringCopyHTKeyDestructor,
9048 JimCommandsHT_ValDestructor
9053 #ifdef jim_ext_namespace
9054 static Jim_Obj *JimQualifyNameObj(Jim_Interp *interp, Jim_Obj *nsObj)
9056 const char *name = Jim_String(nsObj);
9057 if (name[0] == ':' && name[1] == ':') {
9059 while (*++name == ':') {
9061 nsObj = Jim_NewStringObj(interp, name, -1);
9063 else if (Jim_Length(interp->framePtr->nsObj)) {
9065 nsObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9066 Jim_AppendStrings(interp, nsObj, "::", name, NULL);
9068 return nsObj;
9071 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9073 Jim_Obj *resultObj;
9075 const char *name = Jim_String(nameObjPtr);
9076 if (name[0] == ':' && name[1] == ':') {
9077 return nameObjPtr;
9079 Jim_IncrRefCount(nameObjPtr);
9080 resultObj = Jim_NewStringObj(interp, "::", -1);
9081 Jim_AppendObj(interp, resultObj, nameObjPtr);
9082 Jim_DecrRefCount(interp, nameObjPtr);
9084 return resultObj;
9087 static const char *JimQualifyName(Jim_Interp *interp, const char *name, Jim_Obj **objPtrPtr)
9089 Jim_Obj *objPtr = interp->emptyObj;
9091 if (name[0] == ':' && name[1] == ':') {
9093 while (*++name == ':') {
9096 else if (Jim_Length(interp->framePtr->nsObj)) {
9098 objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9099 Jim_AppendStrings(interp, objPtr, "::", name, NULL);
9100 name = Jim_String(objPtr);
9102 Jim_IncrRefCount(objPtr);
9103 *objPtrPtr = objPtr;
9104 return name;
9107 #define JimFreeQualifiedName(INTERP, OBJ) Jim_DecrRefCount((INTERP), (OBJ))
9109 #else
9111 #define JimQualifyName(INTERP, NAME, DUMMY) (((NAME)[0] == ':' && (NAME)[1] == ':') ? (NAME) + 2 : (NAME))
9112 #define JimFreeQualifiedName(INTERP, DUMMY) (void)(DUMMY)
9114 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9116 return nameObjPtr;
9118 #endif
9120 static int JimCreateCommand(Jim_Interp *interp, const char *name, Jim_Cmd *cmd)
9122 Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, name);
9123 if (he) {
9125 Jim_InterpIncrProcEpoch(interp);
9128 if (he && interp->local) {
9130 cmd->prevCmd = Jim_GetHashEntryVal(he);
9131 Jim_SetHashVal(&interp->commands, he, cmd);
9133 else {
9134 if (he) {
9136 Jim_DeleteHashEntry(&interp->commands, name);
9139 Jim_AddHashEntry(&interp->commands, name, cmd);
9141 return JIM_OK;
9145 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
9146 Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc)
9148 Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
9151 memset(cmdPtr, 0, sizeof(*cmdPtr));
9152 cmdPtr->inUse = 1;
9153 cmdPtr->u.native.delProc = delProc;
9154 cmdPtr->u.native.cmdProc = cmdProc;
9155 cmdPtr->u.native.privData = privData;
9157 JimCreateCommand(interp, cmdNameStr, cmdPtr);
9159 return JIM_OK;
9162 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
9164 int len, i;
9166 len = Jim_ListLength(interp, staticsListObjPtr);
9167 if (len == 0) {
9168 return JIM_OK;
9171 cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
9172 Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
9173 for (i = 0; i < len; i++) {
9174 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
9175 Jim_Var *varPtr;
9176 int subLen;
9178 objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
9180 subLen = Jim_ListLength(interp, objPtr);
9181 if (subLen == 1 || subLen == 2) {
9182 nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
9183 if (subLen == 1) {
9184 initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
9185 if (initObjPtr == NULL) {
9186 Jim_SetResultFormatted(interp,
9187 "variable for initialization of static \"%#s\" not found in the local context",
9188 nameObjPtr);
9189 return JIM_ERR;
9192 else {
9193 initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
9195 if (JimValidName(interp, "static variable", nameObjPtr) != JIM_OK) {
9196 return JIM_ERR;
9199 varPtr = Jim_Alloc(sizeof(*varPtr));
9200 varPtr->objPtr = initObjPtr;
9201 Jim_IncrRefCount(initObjPtr);
9202 varPtr->linkFramePtr = NULL;
9203 if (Jim_AddHashEntry(cmdPtr->u.proc.staticVars,
9204 Jim_String(nameObjPtr), varPtr) != JIM_OK) {
9205 Jim_SetResultFormatted(interp,
9206 "static variable name \"%#s\" duplicated in statics list", nameObjPtr);
9207 Jim_DecrRefCount(interp, initObjPtr);
9208 Jim_Free(varPtr);
9209 return JIM_ERR;
9212 else {
9213 Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
9214 objPtr);
9215 return JIM_ERR;
9218 return JIM_OK;
9221 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, const char *cmdname)
9223 #ifdef jim_ext_namespace
9224 if (cmdPtr->isproc) {
9226 const char *pt = strrchr(cmdname, ':');
9227 if (pt && pt != cmdname && pt[-1] == ':') {
9228 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9229 cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 1);
9230 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9232 if (Jim_FindHashEntry(&interp->commands, pt + 1)) {
9234 Jim_InterpIncrProcEpoch(interp);
9238 #endif
9241 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
9242 Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
9244 Jim_Cmd *cmdPtr;
9245 int argListLen;
9246 int i;
9248 argListLen = Jim_ListLength(interp, argListObjPtr);
9251 cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
9252 memset(cmdPtr, 0, sizeof(*cmdPtr));
9253 cmdPtr->inUse = 1;
9254 cmdPtr->isproc = 1;
9255 cmdPtr->u.proc.argListObjPtr = argListObjPtr;
9256 cmdPtr->u.proc.argListLen = argListLen;
9257 cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
9258 cmdPtr->u.proc.argsPos = -1;
9259 cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
9260 cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
9261 Jim_IncrRefCount(argListObjPtr);
9262 Jim_IncrRefCount(bodyObjPtr);
9263 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9266 if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
9267 goto err;
9272 for (i = 0; i < argListLen; i++) {
9273 Jim_Obj *argPtr;
9274 Jim_Obj *nameObjPtr;
9275 Jim_Obj *defaultObjPtr;
9276 int len;
9279 argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
9280 len = Jim_ListLength(interp, argPtr);
9281 if (len == 0) {
9282 Jim_SetResultString(interp, "argument with no name", -1);
9283 err:
9284 JimDecrCmdRefCount(interp, cmdPtr);
9285 return NULL;
9287 if (len > 2) {
9288 Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
9289 goto err;
9292 if (len == 2) {
9294 nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
9295 defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
9297 else {
9299 nameObjPtr = argPtr;
9300 defaultObjPtr = NULL;
9304 if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
9305 if (cmdPtr->u.proc.argsPos >= 0) {
9306 Jim_SetResultString(interp, "'args' specified more than once", -1);
9307 goto err;
9309 cmdPtr->u.proc.argsPos = i;
9311 else {
9312 if (len == 2) {
9313 cmdPtr->u.proc.optArity++;
9315 else {
9316 cmdPtr->u.proc.reqArity++;
9320 cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
9321 cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
9324 return cmdPtr;
9327 int Jim_DeleteCommand(Jim_Interp *interp, const char *name)
9329 int ret = JIM_OK;
9330 Jim_Obj *qualifiedNameObj;
9331 const char *qualname = JimQualifyName(interp, name, &qualifiedNameObj);
9333 if (Jim_DeleteHashEntry(&interp->commands, qualname) == JIM_ERR) {
9334 Jim_SetResultFormatted(interp, "can't delete \"%s\": command doesn't exist", name);
9335 ret = JIM_ERR;
9337 else {
9338 Jim_InterpIncrProcEpoch(interp);
9341 JimFreeQualifiedName(interp, qualifiedNameObj);
9343 return ret;
9346 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, const char *newName)
9348 int ret = JIM_ERR;
9349 Jim_HashEntry *he;
9350 Jim_Cmd *cmdPtr;
9351 Jim_Obj *qualifiedOldNameObj;
9352 Jim_Obj *qualifiedNewNameObj;
9353 const char *fqold;
9354 const char *fqnew;
9356 if (newName[0] == 0) {
9357 return Jim_DeleteCommand(interp, oldName);
9360 fqold = JimQualifyName(interp, oldName, &qualifiedOldNameObj);
9361 fqnew = JimQualifyName(interp, newName, &qualifiedNewNameObj);
9364 he = Jim_FindHashEntry(&interp->commands, fqold);
9365 if (he == NULL) {
9366 Jim_SetResultFormatted(interp, "can't rename \"%s\": command doesn't exist", oldName);
9368 else if (Jim_FindHashEntry(&interp->commands, fqnew)) {
9369 Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName);
9371 else {
9373 cmdPtr = Jim_GetHashEntryVal(he);
9374 JimIncrCmdRefCount(cmdPtr);
9375 JimUpdateProcNamespace(interp, cmdPtr, fqnew);
9376 Jim_AddHashEntry(&interp->commands, fqnew, cmdPtr);
9379 Jim_DeleteHashEntry(&interp->commands, fqold);
9382 Jim_InterpIncrProcEpoch(interp);
9384 ret = JIM_OK;
9387 JimFreeQualifiedName(interp, qualifiedOldNameObj);
9388 JimFreeQualifiedName(interp, qualifiedNewNameObj);
9390 return ret;
9394 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9396 Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
9399 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9401 dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
9402 dupPtr->typePtr = srcPtr->typePtr;
9403 Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
9406 static const Jim_ObjType commandObjType = {
9407 "command",
9408 FreeCommandInternalRep,
9409 DupCommandInternalRep,
9410 NULL,
9411 JIM_TYPE_REFERENCES,
9414 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9416 Jim_Cmd *cmd;
9418 if (objPtr->typePtr != &commandObjType ||
9419 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch
9420 #ifdef jim_ext_namespace
9421 || !Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
9422 #endif
9427 const char *name = Jim_String(objPtr);
9428 Jim_HashEntry *he;
9430 if (name[0] == ':' && name[1] == ':') {
9431 while (*++name == ':') {
9434 #ifdef jim_ext_namespace
9435 else if (Jim_Length(interp->framePtr->nsObj)) {
9437 Jim_Obj *nameObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9438 Jim_AppendStrings(interp, nameObj, "::", name, NULL);
9439 he = Jim_FindHashEntry(&interp->commands, Jim_String(nameObj));
9440 Jim_FreeNewObj(interp, nameObj);
9441 if (he) {
9442 goto found;
9445 #endif
9448 he = Jim_FindHashEntry(&interp->commands, name);
9449 if (he == NULL) {
9450 if (flags & JIM_ERRMSG) {
9451 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
9453 return NULL;
9455 #ifdef jim_ext_namespace
9456 found:
9457 #endif
9458 cmd = Jim_GetHashEntryVal(he);
9461 Jim_FreeIntRep(interp, objPtr);
9462 objPtr->typePtr = &commandObjType;
9463 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
9464 objPtr->internalRep.cmdValue.cmdPtr = cmd;
9465 objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
9466 Jim_IncrRefCount(interp->framePtr->nsObj);
9468 else {
9469 cmd = objPtr->internalRep.cmdValue.cmdPtr;
9471 while (cmd->u.proc.upcall) {
9472 cmd = cmd->prevCmd;
9474 return cmd;
9479 #define JIM_DICT_SUGAR 100
9481 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
9483 static const Jim_ObjType variableObjType = {
9484 "variable",
9485 NULL,
9486 NULL,
9487 NULL,
9488 JIM_TYPE_REFERENCES,
9491 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr)
9494 if (nameObjPtr->typePtr != &variableObjType) {
9495 int len;
9496 const char *str = Jim_GetString(nameObjPtr, &len);
9497 if (memchr(str, '\0', len)) {
9498 Jim_SetResultFormatted(interp, "%s name contains embedded null", type);
9499 return JIM_ERR;
9502 return JIM_OK;
9505 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9507 const char *varName;
9508 Jim_CallFrame *framePtr;
9509 Jim_HashEntry *he;
9510 int global;
9511 int len;
9514 if (objPtr->typePtr == &variableObjType) {
9515 framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
9516 if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
9518 return JIM_OK;
9522 else if (objPtr->typePtr == &dictSubstObjType) {
9523 return JIM_DICT_SUGAR;
9525 else if (JimValidName(interp, "variable", objPtr) != JIM_OK) {
9526 return JIM_ERR;
9530 varName = Jim_GetString(objPtr, &len);
9533 if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
9534 return JIM_DICT_SUGAR;
9537 if (varName[0] == ':' && varName[1] == ':') {
9538 while (*++varName == ':') {
9540 global = 1;
9541 framePtr = interp->topFramePtr;
9543 else {
9544 global = 0;
9545 framePtr = interp->framePtr;
9549 he = Jim_FindHashEntry(&framePtr->vars, varName);
9550 if (he == NULL) {
9551 if (!global && framePtr->staticVars) {
9553 he = Jim_FindHashEntry(framePtr->staticVars, varName);
9555 if (he == NULL) {
9556 return JIM_ERR;
9561 Jim_FreeIntRep(interp, objPtr);
9562 objPtr->typePtr = &variableObjType;
9563 objPtr->internalRep.varValue.callFrameId = framePtr->id;
9564 objPtr->internalRep.varValue.varPtr = Jim_GetHashEntryVal(he);
9565 objPtr->internalRep.varValue.global = global;
9566 return JIM_OK;
9570 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
9571 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
9573 static Jim_Var *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9575 const char *name;
9576 Jim_CallFrame *framePtr;
9577 int global;
9580 Jim_Var *var = Jim_Alloc(sizeof(*var));
9582 var->objPtr = valObjPtr;
9583 Jim_IncrRefCount(valObjPtr);
9584 var->linkFramePtr = NULL;
9586 name = Jim_String(nameObjPtr);
9587 if (name[0] == ':' && name[1] == ':') {
9588 while (*++name == ':') {
9590 framePtr = interp->topFramePtr;
9591 global = 1;
9593 else {
9594 framePtr = interp->framePtr;
9595 global = 0;
9599 Jim_AddHashEntry(&framePtr->vars, name, var);
9602 Jim_FreeIntRep(interp, nameObjPtr);
9603 nameObjPtr->typePtr = &variableObjType;
9604 nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
9605 nameObjPtr->internalRep.varValue.varPtr = var;
9606 nameObjPtr->internalRep.varValue.global = global;
9608 return var;
9612 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9614 int err;
9615 Jim_Var *var;
9617 switch (SetVariableFromAny(interp, nameObjPtr)) {
9618 case JIM_DICT_SUGAR:
9619 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
9621 case JIM_ERR:
9622 if (JimValidName(interp, "variable", nameObjPtr) != JIM_OK) {
9623 return JIM_ERR;
9625 JimCreateVariable(interp, nameObjPtr, valObjPtr);
9626 break;
9628 case JIM_OK:
9629 var = nameObjPtr->internalRep.varValue.varPtr;
9630 if (var->linkFramePtr == NULL) {
9631 Jim_IncrRefCount(valObjPtr);
9632 Jim_DecrRefCount(interp, var->objPtr);
9633 var->objPtr = valObjPtr;
9635 else {
9636 Jim_CallFrame *savedCallFrame;
9638 savedCallFrame = interp->framePtr;
9639 interp->framePtr = var->linkFramePtr;
9640 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
9641 interp->framePtr = savedCallFrame;
9642 if (err != JIM_OK)
9643 return err;
9646 return JIM_OK;
9649 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9651 Jim_Obj *nameObjPtr;
9652 int result;
9654 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9655 Jim_IncrRefCount(nameObjPtr);
9656 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
9657 Jim_DecrRefCount(interp, nameObjPtr);
9658 return result;
9661 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9663 Jim_CallFrame *savedFramePtr;
9664 int result;
9666 savedFramePtr = interp->framePtr;
9667 interp->framePtr = interp->topFramePtr;
9668 result = Jim_SetVariableStr(interp, name, objPtr);
9669 interp->framePtr = savedFramePtr;
9670 return result;
9673 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
9675 Jim_Obj *nameObjPtr, *valObjPtr;
9676 int result;
9678 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9679 valObjPtr = Jim_NewStringObj(interp, val, -1);
9680 Jim_IncrRefCount(nameObjPtr);
9681 Jim_IncrRefCount(valObjPtr);
9682 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
9683 Jim_DecrRefCount(interp, nameObjPtr);
9684 Jim_DecrRefCount(interp, valObjPtr);
9685 return result;
9688 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
9689 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
9691 const char *varName;
9692 const char *targetName;
9693 Jim_CallFrame *framePtr;
9694 Jim_Var *varPtr;
9697 switch (SetVariableFromAny(interp, nameObjPtr)) {
9698 case JIM_DICT_SUGAR:
9700 Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
9701 return JIM_ERR;
9703 case JIM_OK:
9704 varPtr = nameObjPtr->internalRep.varValue.varPtr;
9706 if (varPtr->linkFramePtr == NULL) {
9707 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
9708 return JIM_ERR;
9712 varPtr->linkFramePtr = NULL;
9713 break;
9718 varName = Jim_String(nameObjPtr);
9720 if (varName[0] == ':' && varName[1] == ':') {
9721 while (*++varName == ':') {
9724 framePtr = interp->topFramePtr;
9726 else {
9727 framePtr = interp->framePtr;
9730 targetName = Jim_String(targetNameObjPtr);
9731 if (targetName[0] == ':' && targetName[1] == ':') {
9732 while (*++targetName == ':') {
9734 targetNameObjPtr = Jim_NewStringObj(interp, targetName, -1);
9735 targetCallFrame = interp->topFramePtr;
9737 Jim_IncrRefCount(targetNameObjPtr);
9739 if (framePtr->level < targetCallFrame->level) {
9740 Jim_SetResultFormatted(interp,
9741 "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
9742 nameObjPtr);
9743 Jim_DecrRefCount(interp, targetNameObjPtr);
9744 return JIM_ERR;
9748 if (framePtr == targetCallFrame) {
9749 Jim_Obj *objPtr = targetNameObjPtr;
9752 while (1) {
9753 if (strcmp(Jim_String(objPtr), varName) == 0) {
9754 Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
9755 Jim_DecrRefCount(interp, targetNameObjPtr);
9756 return JIM_ERR;
9758 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
9759 break;
9760 varPtr = objPtr->internalRep.varValue.varPtr;
9761 if (varPtr->linkFramePtr != targetCallFrame)
9762 break;
9763 objPtr = varPtr->objPtr;
9768 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
9770 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
9771 Jim_DecrRefCount(interp, targetNameObjPtr);
9772 return JIM_OK;
9775 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
9777 switch (SetVariableFromAny(interp, nameObjPtr)) {
9778 case JIM_OK:{
9779 Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr;
9781 if (varPtr->linkFramePtr == NULL) {
9782 return varPtr->objPtr;
9784 else {
9785 Jim_Obj *objPtr;
9788 Jim_CallFrame *savedCallFrame = interp->framePtr;
9790 interp->framePtr = varPtr->linkFramePtr;
9791 objPtr = Jim_GetVariable(interp, varPtr->objPtr, flags);
9792 interp->framePtr = savedCallFrame;
9793 if (objPtr) {
9794 return objPtr;
9799 break;
9801 case JIM_DICT_SUGAR:
9803 return JimDictSugarGet(interp, nameObjPtr, flags);
9805 if (flags & JIM_ERRMSG) {
9806 Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
9808 return NULL;
9811 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
9813 Jim_CallFrame *savedFramePtr;
9814 Jim_Obj *objPtr;
9816 savedFramePtr = interp->framePtr;
9817 interp->framePtr = interp->topFramePtr;
9818 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
9819 interp->framePtr = savedFramePtr;
9821 return objPtr;
9824 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
9826 Jim_Obj *nameObjPtr, *varObjPtr;
9828 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9829 Jim_IncrRefCount(nameObjPtr);
9830 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
9831 Jim_DecrRefCount(interp, nameObjPtr);
9832 return varObjPtr;
9835 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
9837 Jim_CallFrame *savedFramePtr;
9838 Jim_Obj *objPtr;
9840 savedFramePtr = interp->framePtr;
9841 interp->framePtr = interp->topFramePtr;
9842 objPtr = Jim_GetVariableStr(interp, name, flags);
9843 interp->framePtr = savedFramePtr;
9845 return objPtr;
9848 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
9850 Jim_Var *varPtr;
9851 int retval;
9852 Jim_CallFrame *framePtr;
9854 retval = SetVariableFromAny(interp, nameObjPtr);
9855 if (retval == JIM_DICT_SUGAR) {
9857 return JimDictSugarSet(interp, nameObjPtr, NULL);
9859 else if (retval == JIM_OK) {
9860 varPtr = nameObjPtr->internalRep.varValue.varPtr;
9863 if (varPtr->linkFramePtr) {
9864 framePtr = interp->framePtr;
9865 interp->framePtr = varPtr->linkFramePtr;
9866 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
9867 interp->framePtr = framePtr;
9869 else {
9870 const char *name = Jim_String(nameObjPtr);
9871 if (nameObjPtr->internalRep.varValue.global) {
9872 name += 2;
9873 framePtr = interp->topFramePtr;
9875 else {
9876 framePtr = interp->framePtr;
9879 retval = Jim_DeleteHashEntry(&framePtr->vars, name);
9880 if (retval == JIM_OK) {
9882 framePtr->id = interp->callFrameEpoch++;
9886 if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
9887 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
9889 return retval;
9894 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
9895 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
9897 const char *str, *p;
9898 int len, keyLen;
9899 Jim_Obj *varObjPtr, *keyObjPtr;
9901 str = Jim_GetString(objPtr, &len);
9903 p = strchr(str, '(');
9904 JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
9906 varObjPtr = Jim_NewStringObj(interp, str, p - str);
9908 p++;
9909 keyLen = (str + len) - p;
9910 if (str[len - 1] == ')') {
9911 keyLen--;
9915 keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
9917 Jim_IncrRefCount(varObjPtr);
9918 Jim_IncrRefCount(keyObjPtr);
9919 *varPtrPtr = varObjPtr;
9920 *keyPtrPtr = keyObjPtr;
9923 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
9925 int err;
9927 SetDictSubstFromAny(interp, objPtr);
9929 err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
9930 &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);
9932 if (err == JIM_OK) {
9934 Jim_SetEmptyResult(interp);
9936 else {
9937 if (!valObjPtr) {
9939 if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
9940 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
9941 objPtr);
9942 return err;
9946 Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
9947 (valObjPtr ? "set" : "unset"), objPtr);
9949 return err;
9952 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
9953 Jim_Obj *keyObjPtr, int flags)
9955 Jim_Obj *dictObjPtr;
9956 Jim_Obj *resObjPtr = NULL;
9957 int ret;
9959 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
9960 if (!dictObjPtr) {
9961 return NULL;
9964 ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
9965 if (ret != JIM_OK) {
9966 Jim_SetResultFormatted(interp,
9967 "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
9968 ret < 0 ? "variable isn't" : "no such element in");
9970 else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
9972 Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
9975 return resObjPtr;
9979 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9981 SetDictSubstFromAny(interp, objPtr);
9983 return JimDictExpandArrayVariable(interp,
9984 objPtr->internalRep.dictSubstValue.varNameObjPtr,
9985 objPtr->internalRep.dictSubstValue.indexObjPtr, flags);
9990 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9992 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
9993 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
9996 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9998 JIM_NOTUSED(interp);
10000 dupPtr->internalRep.dictSubstValue.varNameObjPtr =
10001 srcPtr->internalRep.dictSubstValue.varNameObjPtr;
10002 dupPtr->internalRep.dictSubstValue.indexObjPtr = srcPtr->internalRep.dictSubstValue.indexObjPtr;
10003 dupPtr->typePtr = &dictSubstObjType;
10007 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10009 if (objPtr->typePtr != &dictSubstObjType) {
10010 Jim_Obj *varObjPtr, *keyObjPtr;
10012 if (objPtr->typePtr == &interpolatedObjType) {
10015 varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
10016 keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
10018 Jim_IncrRefCount(varObjPtr);
10019 Jim_IncrRefCount(keyObjPtr);
10021 else {
10022 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
10025 Jim_FreeIntRep(interp, objPtr);
10026 objPtr->typePtr = &dictSubstObjType;
10027 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
10028 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
10032 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10034 Jim_Obj *resObjPtr = NULL;
10035 Jim_Obj *substKeyObjPtr = NULL;
10037 SetDictSubstFromAny(interp, objPtr);
10039 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
10040 &substKeyObjPtr, JIM_NONE)
10041 != JIM_OK) {
10042 return NULL;
10044 Jim_IncrRefCount(substKeyObjPtr);
10045 resObjPtr =
10046 JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
10047 substKeyObjPtr, 0);
10048 Jim_DecrRefCount(interp, substKeyObjPtr);
10050 return resObjPtr;
10053 static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10055 Jim_Obj *resultObjPtr;
10057 if (Jim_EvalExpression(interp, objPtr, &resultObjPtr) == JIM_OK) {
10059 resultObjPtr->refCount--;
10060 return resultObjPtr;
10062 return NULL;
10066 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
10068 Jim_CallFrame *cf;
10070 if (interp->freeFramesList) {
10071 cf = interp->freeFramesList;
10072 interp->freeFramesList = cf->next;
10074 cf->argv = NULL;
10075 cf->argc = 0;
10076 cf->procArgsObjPtr = NULL;
10077 cf->procBodyObjPtr = NULL;
10078 cf->next = NULL;
10079 cf->staticVars = NULL;
10080 cf->localCommands = NULL;
10081 cf->tailcall = 0;
10082 cf->tailcallObj = NULL;
10083 cf->tailcallCmd = NULL;
10085 else {
10086 cf = Jim_Alloc(sizeof(*cf));
10087 memset(cf, 0, sizeof(*cf));
10089 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
10092 cf->id = interp->callFrameEpoch++;
10093 cf->parent = parent;
10094 cf->level = parent ? parent->level + 1 : 0;
10095 cf->nsObj = nsObj;
10096 Jim_IncrRefCount(nsObj);
10098 return cf;
10101 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
10104 if (localCommands) {
10105 Jim_Obj *cmdNameObj;
10107 while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
10108 Jim_HashEntry *he;
10109 Jim_Obj *fqObjName;
10110 Jim_HashTable *ht = &interp->commands;
10112 const char *fqname = JimQualifyName(interp, Jim_String(cmdNameObj), &fqObjName);
10114 he = Jim_FindHashEntry(ht, fqname);
10116 if (he) {
10117 Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
10118 if (cmd->prevCmd) {
10119 Jim_Cmd *prevCmd = cmd->prevCmd;
10120 cmd->prevCmd = NULL;
10123 JimDecrCmdRefCount(interp, cmd);
10126 Jim_SetHashVal(ht, he, prevCmd);
10128 else {
10129 Jim_DeleteHashEntry(ht, fqname);
10130 Jim_InterpIncrProcEpoch(interp);
10133 Jim_DecrRefCount(interp, cmdNameObj);
10134 JimFreeQualifiedName(interp, fqObjName);
10136 Jim_FreeStack(localCommands);
10137 Jim_Free(localCommands);
10139 return JIM_OK;
10143 #define JIM_FCF_FULL 0
10144 #define JIM_FCF_REUSE 1
10145 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
10147 JimDeleteLocalProcs(interp, cf->localCommands);
10149 if (cf->procArgsObjPtr)
10150 Jim_DecrRefCount(interp, cf->procArgsObjPtr);
10151 if (cf->procBodyObjPtr)
10152 Jim_DecrRefCount(interp, cf->procBodyObjPtr);
10153 Jim_DecrRefCount(interp, cf->nsObj);
10154 if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
10155 Jim_FreeHashTable(&cf->vars);
10156 else {
10157 int i;
10158 Jim_HashEntry **table = cf->vars.table, *he;
10160 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
10161 he = table[i];
10162 while (he != NULL) {
10163 Jim_HashEntry *nextEntry = he->next;
10164 Jim_Var *varPtr = Jim_GetHashEntryVal(he);
10166 Jim_DecrRefCount(interp, varPtr->objPtr);
10167 Jim_Free(Jim_GetHashEntryKey(he));
10168 Jim_Free(varPtr);
10169 Jim_Free(he);
10170 table[i] = NULL;
10171 he = nextEntry;
10174 cf->vars.used = 0;
10176 cf->next = interp->freeFramesList;
10177 interp->freeFramesList = cf;
10181 #ifdef JIM_REFERENCES
10183 static void JimReferencesHTValDestructor(void *interp, void *val)
10185 Jim_Reference *refPtr = (void *)val;
10187 Jim_DecrRefCount(interp, refPtr->objPtr);
10188 if (refPtr->finalizerCmdNamePtr != NULL) {
10189 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
10191 Jim_Free(val);
10194 static unsigned int JimReferencesHTHashFunction(const void *key)
10197 const unsigned long *widePtr = key;
10198 unsigned int intValue = (unsigned int)*widePtr;
10200 return Jim_IntHashFunction(intValue);
10203 static void *JimReferencesHTKeyDup(void *privdata, const void *key)
10205 void *copy = Jim_Alloc(sizeof(unsigned long));
10207 JIM_NOTUSED(privdata);
10209 memcpy(copy, key, sizeof(unsigned long));
10210 return copy;
10213 static int JimReferencesHTKeyCompare(void *privdata, const void *key1, const void *key2)
10215 JIM_NOTUSED(privdata);
10217 return memcmp(key1, key2, sizeof(unsigned long)) == 0;
10220 static void JimReferencesHTKeyDestructor(void *privdata, void *key)
10222 JIM_NOTUSED(privdata);
10224 Jim_Free(key);
10227 static const Jim_HashTableType JimReferencesHashTableType = {
10228 JimReferencesHTHashFunction,
10229 JimReferencesHTKeyDup,
10230 NULL,
10231 JimReferencesHTKeyCompare,
10232 JimReferencesHTKeyDestructor,
10233 JimReferencesHTValDestructor
10238 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
10240 static int JimFormatReference(char *buf, Jim_Reference *refPtr, unsigned long id)
10242 const char *fmt = "<reference.<%s>.%020lu>";
10244 sprintf(buf, fmt, refPtr->tag, id);
10245 return JIM_REFERENCE_SPACE;
10248 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
10250 static const Jim_ObjType referenceObjType = {
10251 "reference",
10252 NULL,
10253 NULL,
10254 UpdateStringOfReference,
10255 JIM_TYPE_REFERENCES,
10258 static void UpdateStringOfReference(struct Jim_Obj *objPtr)
10260 char buf[JIM_REFERENCE_SPACE + 1];
10262 JimFormatReference(buf, objPtr->internalRep.refValue.refPtr, objPtr->internalRep.refValue.id);
10263 JimSetStringBytes(objPtr, buf);
10266 static int isrefchar(int c)
10268 return (c == '_' || isalnum(c));
10271 static int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10273 unsigned long value;
10274 int i, len;
10275 const char *str, *start, *end;
10276 char refId[21];
10277 Jim_Reference *refPtr;
10278 Jim_HashEntry *he;
10279 char *endptr;
10282 str = Jim_GetString(objPtr, &len);
10284 if (len < JIM_REFERENCE_SPACE)
10285 goto badformat;
10287 start = str;
10288 end = str + len - 1;
10289 while (*start == ' ')
10290 start++;
10291 while (*end == ' ' && end > start)
10292 end--;
10293 if (end - start + 1 != JIM_REFERENCE_SPACE)
10294 goto badformat;
10296 if (memcmp(start, "<reference.<", 12) != 0)
10297 goto badformat;
10298 if (start[12 + JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>')
10299 goto badformat;
10301 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
10302 if (!isrefchar(start[12 + i]))
10303 goto badformat;
10306 memcpy(refId, start + 14 + JIM_REFERENCE_TAGLEN, 20);
10307 refId[20] = '\0';
10309 value = strtoul(refId, &endptr, 10);
10310 if (JimCheckConversion(refId, endptr) != JIM_OK)
10311 goto badformat;
10313 he = Jim_FindHashEntry(&interp->references, &value);
10314 if (he == NULL) {
10315 Jim_SetResultFormatted(interp, "invalid reference id \"%#s\"", objPtr);
10316 return JIM_ERR;
10318 refPtr = Jim_GetHashEntryVal(he);
10320 Jim_FreeIntRep(interp, objPtr);
10321 objPtr->typePtr = &referenceObjType;
10322 objPtr->internalRep.refValue.id = value;
10323 objPtr->internalRep.refValue.refPtr = refPtr;
10324 return JIM_OK;
10326 badformat:
10327 Jim_SetResultFormatted(interp, "expected reference but got \"%#s\"", objPtr);
10328 return JIM_ERR;
10331 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr)
10333 struct Jim_Reference *refPtr;
10334 unsigned long id;
10335 Jim_Obj *refObjPtr;
10336 const char *tag;
10337 int tagLen, i;
10340 Jim_CollectIfNeeded(interp);
10342 refPtr = Jim_Alloc(sizeof(*refPtr));
10343 refPtr->objPtr = objPtr;
10344 Jim_IncrRefCount(objPtr);
10345 refPtr->finalizerCmdNamePtr = cmdNamePtr;
10346 if (cmdNamePtr)
10347 Jim_IncrRefCount(cmdNamePtr);
10348 id = interp->referenceNextId++;
10349 Jim_AddHashEntry(&interp->references, &id, refPtr);
10350 refObjPtr = Jim_NewObj(interp);
10351 refObjPtr->typePtr = &referenceObjType;
10352 refObjPtr->bytes = NULL;
10353 refObjPtr->internalRep.refValue.id = id;
10354 refObjPtr->internalRep.refValue.refPtr = refPtr;
10355 interp->referenceNextId++;
10356 tag = Jim_GetString(tagPtr, &tagLen);
10357 if (tagLen > JIM_REFERENCE_TAGLEN)
10358 tagLen = JIM_REFERENCE_TAGLEN;
10359 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
10360 if (i < tagLen && isrefchar(tag[i]))
10361 refPtr->tag[i] = tag[i];
10362 else
10363 refPtr->tag[i] = '_';
10365 refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
10366 return refObjPtr;
10369 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
10371 if (objPtr->typePtr != &referenceObjType && SetReferenceFromAny(interp, objPtr) == JIM_ERR)
10372 return NULL;
10373 return objPtr->internalRep.refValue.refPtr;
10376 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
10378 Jim_Reference *refPtr;
10380 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
10381 return JIM_ERR;
10382 Jim_IncrRefCount(cmdNamePtr);
10383 if (refPtr->finalizerCmdNamePtr)
10384 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
10385 refPtr->finalizerCmdNamePtr = cmdNamePtr;
10386 return JIM_OK;
10389 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
10391 Jim_Reference *refPtr;
10393 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
10394 return JIM_ERR;
10395 *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
10396 return JIM_OK;
10401 static const Jim_HashTableType JimRefMarkHashTableType = {
10402 JimReferencesHTHashFunction,
10403 JimReferencesHTKeyDup,
10404 NULL,
10405 JimReferencesHTKeyCompare,
10406 JimReferencesHTKeyDestructor,
10407 NULL
10411 int Jim_Collect(Jim_Interp *interp)
10413 int collected = 0;
10414 return collected;
10417 #define JIM_COLLECT_ID_PERIOD 5000
10418 #define JIM_COLLECT_TIME_PERIOD 300
10420 void Jim_CollectIfNeeded(Jim_Interp *interp)
10422 unsigned long elapsedId;
10423 int elapsedTime;
10425 elapsedId = interp->referenceNextId - interp->lastCollectId;
10426 elapsedTime = time(NULL) - interp->lastCollectTime;
10429 if (elapsedId > JIM_COLLECT_ID_PERIOD || elapsedTime > JIM_COLLECT_TIME_PERIOD) {
10430 Jim_Collect(interp);
10433 #endif
10435 int Jim_IsBigEndian(void)
10437 union {
10438 unsigned short s;
10439 unsigned char c[2];
10440 } uval = {0x0102};
10442 return uval.c[0] == 1;
10446 Jim_Interp *Jim_CreateInterp(void)
10448 Jim_Interp *i = Jim_Alloc(sizeof(*i));
10450 memset(i, 0, sizeof(*i));
10452 i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
10453 i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
10454 i->lastCollectTime = time(NULL);
10456 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
10457 #ifdef JIM_REFERENCES
10458 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
10459 #endif
10460 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
10461 Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
10462 i->emptyObj = Jim_NewEmptyStringObj(i);
10463 i->trueObj = Jim_NewIntObj(i, 1);
10464 i->falseObj = Jim_NewIntObj(i, 0);
10465 i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
10466 i->errorFileNameObj = i->emptyObj;
10467 i->result = i->emptyObj;
10468 i->stackTrace = Jim_NewListObj(i, NULL, 0);
10469 i->unknown = Jim_NewStringObj(i, "unknown", -1);
10470 i->errorProc = i->emptyObj;
10471 i->currentScriptObj = Jim_NewEmptyStringObj(i);
10472 i->nullScriptObj = Jim_NewEmptyStringObj(i);
10473 Jim_IncrRefCount(i->emptyObj);
10474 Jim_IncrRefCount(i->errorFileNameObj);
10475 Jim_IncrRefCount(i->result);
10476 Jim_IncrRefCount(i->stackTrace);
10477 Jim_IncrRefCount(i->unknown);
10478 Jim_IncrRefCount(i->currentScriptObj);
10479 Jim_IncrRefCount(i->nullScriptObj);
10480 Jim_IncrRefCount(i->errorProc);
10481 Jim_IncrRefCount(i->trueObj);
10482 Jim_IncrRefCount(i->falseObj);
10485 Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
10486 Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
10488 Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
10489 Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
10490 Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
10491 Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
10492 Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
10493 Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
10494 Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
10496 return i;
10499 void Jim_FreeInterp(Jim_Interp *i)
10501 Jim_CallFrame *cf, *cfx;
10503 Jim_Obj *objPtr, *nextObjPtr;
10506 for (cf = i->framePtr; cf; cf = cfx) {
10507 cfx = cf->parent;
10508 JimFreeCallFrame(i, cf, JIM_FCF_FULL);
10511 Jim_DecrRefCount(i, i->emptyObj);
10512 Jim_DecrRefCount(i, i->trueObj);
10513 Jim_DecrRefCount(i, i->falseObj);
10514 Jim_DecrRefCount(i, i->result);
10515 Jim_DecrRefCount(i, i->stackTrace);
10516 Jim_DecrRefCount(i, i->errorProc);
10517 Jim_DecrRefCount(i, i->unknown);
10518 Jim_DecrRefCount(i, i->errorFileNameObj);
10519 Jim_DecrRefCount(i, i->currentScriptObj);
10520 Jim_DecrRefCount(i, i->nullScriptObj);
10521 Jim_FreeHashTable(&i->commands);
10522 #ifdef JIM_REFERENCES
10523 Jim_FreeHashTable(&i->references);
10524 #endif
10525 Jim_FreeHashTable(&i->packages);
10526 Jim_Free(i->prngState);
10527 Jim_FreeHashTable(&i->assocData);
10529 #ifdef JIM_MAINTAINER
10530 if (i->liveList != NULL) {
10531 objPtr = i->liveList;
10533 printf("\n-------------------------------------\n");
10534 printf("Objects still in the free list:\n");
10535 while (objPtr) {
10536 const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
10538 if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
10539 printf("%p (%d) %-10s: '%.20s...'\n",
10540 (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
10542 else {
10543 printf("%p (%d) %-10s: '%s'\n",
10544 (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
10546 if (objPtr->typePtr == &sourceObjType) {
10547 printf("FILE %s LINE %d\n",
10548 Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
10549 objPtr->internalRep.sourceValue.lineNumber);
10551 objPtr = objPtr->nextObjPtr;
10553 printf("-------------------------------------\n\n");
10554 JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
10556 #endif
10559 objPtr = i->freeList;
10560 while (objPtr) {
10561 nextObjPtr = objPtr->nextObjPtr;
10562 Jim_Free(objPtr);
10563 objPtr = nextObjPtr;
10567 for (cf = i->freeFramesList; cf; cf = cfx) {
10568 cfx = cf->next;
10569 if (cf->vars.table)
10570 Jim_FreeHashTable(&cf->vars);
10571 Jim_Free(cf);
10575 Jim_Free(i);
10578 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10580 long level;
10581 const char *str;
10582 Jim_CallFrame *framePtr;
10584 if (levelObjPtr) {
10585 str = Jim_String(levelObjPtr);
10586 if (str[0] == '#') {
10587 char *endptr;
10589 level = jim_strtol(str + 1, &endptr);
10590 if (str[1] == '\0' || endptr[0] != '\0') {
10591 level = -1;
10594 else {
10595 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
10596 level = -1;
10598 else {
10600 level = interp->framePtr->level - level;
10604 else {
10605 str = "1";
10606 level = interp->framePtr->level - 1;
10609 if (level == 0) {
10610 return interp->topFramePtr;
10612 if (level > 0) {
10614 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10615 if (framePtr->level == level) {
10616 return framePtr;
10621 Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
10622 return NULL;
10625 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10627 long level;
10628 Jim_CallFrame *framePtr;
10630 if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
10631 if (level <= 0) {
10633 level = interp->framePtr->level + level;
10636 if (level == 0) {
10637 return interp->topFramePtr;
10641 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10642 if (framePtr->level == level) {
10643 return framePtr;
10648 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
10649 return NULL;
10652 static void JimResetStackTrace(Jim_Interp *interp)
10654 Jim_DecrRefCount(interp, interp->stackTrace);
10655 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
10656 Jim_IncrRefCount(interp->stackTrace);
10659 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
10661 int len;
10664 Jim_IncrRefCount(stackTraceObj);
10665 Jim_DecrRefCount(interp, interp->stackTrace);
10666 interp->stackTrace = stackTraceObj;
10667 interp->errorFlag = 1;
10669 len = Jim_ListLength(interp, interp->stackTrace);
10670 if (len >= 3) {
10671 if (Jim_Length(Jim_ListGetIndex(interp, interp->stackTrace, len - 2)) == 0) {
10672 interp->addStackTrace = 1;
10677 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
10678 Jim_Obj *fileNameObj, int linenr)
10680 if (strcmp(procname, "unknown") == 0) {
10681 procname = "";
10683 if (!*procname && !Jim_Length(fileNameObj)) {
10685 return;
10688 if (Jim_IsShared(interp->stackTrace)) {
10689 Jim_DecrRefCount(interp, interp->stackTrace);
10690 interp->stackTrace = Jim_DuplicateObj(interp, interp->stackTrace);
10691 Jim_IncrRefCount(interp->stackTrace);
10695 if (!*procname && Jim_Length(fileNameObj)) {
10697 int len = Jim_ListLength(interp, interp->stackTrace);
10699 if (len >= 3) {
10700 Jim_Obj *objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 3);
10701 if (Jim_Length(objPtr)) {
10703 objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 2);
10704 if (Jim_Length(objPtr) == 0) {
10706 ListSetIndex(interp, interp->stackTrace, len - 2, fileNameObj, 0);
10707 ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), 0);
10708 return;
10714 Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewStringObj(interp, procname, -1));
10715 Jim_ListAppendElement(interp, interp->stackTrace, fileNameObj);
10716 Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewIntObj(interp, linenr));
10719 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
10720 void *data)
10722 AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
10724 assocEntryPtr->delProc = delProc;
10725 assocEntryPtr->data = data;
10726 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
10729 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
10731 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
10733 if (entryPtr != NULL) {
10734 AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr);
10735 return assocEntryPtr->data;
10737 return NULL;
10740 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
10742 return Jim_DeleteHashEntry(&interp->assocData, key);
10745 int Jim_GetExitCode(Jim_Interp *interp)
10747 return interp->exitCode;
10750 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
10751 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
10753 static const Jim_ObjType intObjType = {
10754 "int",
10755 NULL,
10756 NULL,
10757 UpdateStringOfInt,
10758 JIM_TYPE_NONE,
10761 static const Jim_ObjType coercedDoubleObjType = {
10762 "coerced-double",
10763 NULL,
10764 NULL,
10765 UpdateStringOfInt,
10766 JIM_TYPE_NONE,
10770 static void UpdateStringOfInt(struct Jim_Obj *objPtr)
10772 char buf[JIM_INTEGER_SPACE + 1];
10773 jim_wide wideValue = JimWideValue(objPtr);
10774 int pos = 0;
10776 if (wideValue == 0) {
10777 buf[pos++] = '0';
10779 else {
10780 char tmp[JIM_INTEGER_SPACE];
10781 int num = 0;
10782 int i;
10784 if (wideValue < 0) {
10785 buf[pos++] = '-';
10786 i = wideValue % 10;
10787 tmp[num++] = (i > 0) ? (10 - i) : -i;
10788 wideValue /= -10;
10791 while (wideValue) {
10792 tmp[num++] = wideValue % 10;
10793 wideValue /= 10;
10796 for (i = 0; i < num; i++) {
10797 buf[pos++] = '0' + tmp[num - i - 1];
10800 buf[pos] = 0;
10802 JimSetStringBytes(objPtr, buf);
10805 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10807 jim_wide wideValue;
10808 const char *str;
10810 if (objPtr->typePtr == &coercedDoubleObjType) {
10812 objPtr->typePtr = &intObjType;
10813 return JIM_OK;
10817 str = Jim_String(objPtr);
10819 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
10820 if (flags & JIM_ERRMSG) {
10821 Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
10823 return JIM_ERR;
10825 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
10826 Jim_SetResultString(interp, "Integer value too big to be represented", -1);
10827 return JIM_ERR;
10830 Jim_FreeIntRep(interp, objPtr);
10831 objPtr->typePtr = &intObjType;
10832 objPtr->internalRep.wideValue = wideValue;
10833 return JIM_OK;
10836 #ifdef JIM_OPTIMIZATION
10837 static int JimIsWide(Jim_Obj *objPtr)
10839 return objPtr->typePtr == &intObjType;
10841 #endif
10843 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10845 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
10846 return JIM_ERR;
10847 *widePtr = JimWideValue(objPtr);
10848 return JIM_OK;
10852 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10854 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
10855 return JIM_ERR;
10856 *widePtr = JimWideValue(objPtr);
10857 return JIM_OK;
10860 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
10862 jim_wide wideValue;
10863 int retval;
10865 retval = Jim_GetWide(interp, objPtr, &wideValue);
10866 if (retval == JIM_OK) {
10867 *longPtr = (long)wideValue;
10868 return JIM_OK;
10870 return JIM_ERR;
10873 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
10875 Jim_Obj *objPtr;
10877 objPtr = Jim_NewObj(interp);
10878 objPtr->typePtr = &intObjType;
10879 objPtr->bytes = NULL;
10880 objPtr->internalRep.wideValue = wideValue;
10881 return objPtr;
10884 #define JIM_DOUBLE_SPACE 30
10886 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
10887 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
10889 static const Jim_ObjType doubleObjType = {
10890 "double",
10891 NULL,
10892 NULL,
10893 UpdateStringOfDouble,
10894 JIM_TYPE_NONE,
10897 #ifndef HAVE_ISNAN
10898 #undef isnan
10899 #define isnan(X) ((X) != (X))
10900 #endif
10901 #ifndef HAVE_ISINF
10902 #undef isinf
10903 #define isinf(X) (1.0 / (X) == 0.0)
10904 #endif
10906 static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
10908 double value = objPtr->internalRep.doubleValue;
10910 if (isnan(value)) {
10911 JimSetStringBytes(objPtr, "NaN");
10912 return;
10914 if (isinf(value)) {
10915 if (value < 0) {
10916 JimSetStringBytes(objPtr, "-Inf");
10918 else {
10919 JimSetStringBytes(objPtr, "Inf");
10921 return;
10924 char buf[JIM_DOUBLE_SPACE + 1];
10925 int i;
10926 int len = sprintf(buf, "%.12g", value);
10929 for (i = 0; i < len; i++) {
10930 if (buf[i] == '.' || buf[i] == 'e') {
10931 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
10932 char *e = strchr(buf, 'e');
10933 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
10935 e += 2;
10936 memmove(e, e + 1, len - (e - buf));
10938 #endif
10939 break;
10942 if (buf[i] == '\0') {
10943 buf[i++] = '.';
10944 buf[i++] = '0';
10945 buf[i] = '\0';
10947 JimSetStringBytes(objPtr, buf);
10951 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10953 double doubleValue;
10954 jim_wide wideValue;
10955 const char *str;
10957 str = Jim_String(objPtr);
10959 #ifdef HAVE_LONG_LONG
10961 #define MIN_INT_IN_DOUBLE -(1LL << 53)
10962 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)
10964 if (objPtr->typePtr == &intObjType
10965 && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
10966 && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {
10969 objPtr->typePtr = &coercedDoubleObjType;
10970 return JIM_OK;
10972 else
10973 #endif
10974 if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
10976 Jim_FreeIntRep(interp, objPtr);
10977 objPtr->typePtr = &coercedDoubleObjType;
10978 objPtr->internalRep.wideValue = wideValue;
10979 return JIM_OK;
10981 else {
10983 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
10984 Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
10985 return JIM_ERR;
10988 Jim_FreeIntRep(interp, objPtr);
10990 objPtr->typePtr = &doubleObjType;
10991 objPtr->internalRep.doubleValue = doubleValue;
10992 return JIM_OK;
10995 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
10997 if (objPtr->typePtr == &coercedDoubleObjType) {
10998 *doublePtr = JimWideValue(objPtr);
10999 return JIM_OK;
11001 if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
11002 return JIM_ERR;
11004 if (objPtr->typePtr == &coercedDoubleObjType) {
11005 *doublePtr = JimWideValue(objPtr);
11007 else {
11008 *doublePtr = objPtr->internalRep.doubleValue;
11010 return JIM_OK;
11013 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
11015 Jim_Obj *objPtr;
11017 objPtr = Jim_NewObj(interp);
11018 objPtr->typePtr = &doubleObjType;
11019 objPtr->bytes = NULL;
11020 objPtr->internalRep.doubleValue = doubleValue;
11021 return objPtr;
11024 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
11025 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
11026 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11027 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11028 static void UpdateStringOfList(struct Jim_Obj *objPtr);
11029 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11031 static const Jim_ObjType listObjType = {
11032 "list",
11033 FreeListInternalRep,
11034 DupListInternalRep,
11035 UpdateStringOfList,
11036 JIM_TYPE_NONE,
11039 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11041 int i;
11043 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
11044 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
11046 Jim_Free(objPtr->internalRep.listValue.ele);
11049 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11051 int i;
11053 JIM_NOTUSED(interp);
11055 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
11056 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
11057 dupPtr->internalRep.listValue.ele =
11058 Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
11059 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
11060 sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
11061 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
11062 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
11064 dupPtr->typePtr = &listObjType;
11067 #define JIM_ELESTR_SIMPLE 0
11068 #define JIM_ELESTR_BRACE 1
11069 #define JIM_ELESTR_QUOTE 2
11070 static unsigned char ListElementQuotingType(const char *s, int len)
11072 int i, level, blevel, trySimple = 1;
11075 if (len == 0)
11076 return JIM_ELESTR_BRACE;
11077 if (s[0] == '"' || s[0] == '{') {
11078 trySimple = 0;
11079 goto testbrace;
11081 for (i = 0; i < len; i++) {
11082 switch (s[i]) {
11083 case ' ':
11084 case '$':
11085 case '"':
11086 case '[':
11087 case ']':
11088 case ';':
11089 case '\\':
11090 case '\r':
11091 case '\n':
11092 case '\t':
11093 case '\f':
11094 case '\v':
11095 trySimple = 0;
11096 case '{':
11097 case '}':
11098 goto testbrace;
11101 return JIM_ELESTR_SIMPLE;
11103 testbrace:
11105 if (s[len - 1] == '\\')
11106 return JIM_ELESTR_QUOTE;
11107 level = 0;
11108 blevel = 0;
11109 for (i = 0; i < len; i++) {
11110 switch (s[i]) {
11111 case '{':
11112 level++;
11113 break;
11114 case '}':
11115 level--;
11116 if (level < 0)
11117 return JIM_ELESTR_QUOTE;
11118 break;
11119 case '[':
11120 blevel++;
11121 break;
11122 case ']':
11123 blevel--;
11124 break;
11125 case '\\':
11126 if (s[i + 1] == '\n')
11127 return JIM_ELESTR_QUOTE;
11128 else if (s[i + 1] != '\0')
11129 i++;
11130 break;
11133 if (blevel < 0) {
11134 return JIM_ELESTR_QUOTE;
11137 if (level == 0) {
11138 if (!trySimple)
11139 return JIM_ELESTR_BRACE;
11140 for (i = 0; i < len; i++) {
11141 switch (s[i]) {
11142 case ' ':
11143 case '$':
11144 case '"':
11145 case '[':
11146 case ']':
11147 case ';':
11148 case '\\':
11149 case '\r':
11150 case '\n':
11151 case '\t':
11152 case '\f':
11153 case '\v':
11154 return JIM_ELESTR_BRACE;
11155 break;
11158 return JIM_ELESTR_SIMPLE;
11160 return JIM_ELESTR_QUOTE;
11163 static int BackslashQuoteString(const char *s, int len, char *q)
11165 char *p = q;
11167 while (len--) {
11168 switch (*s) {
11169 case ' ':
11170 case '$':
11171 case '"':
11172 case '[':
11173 case ']':
11174 case '{':
11175 case '}':
11176 case ';':
11177 case '\\':
11178 *p++ = '\\';
11179 *p++ = *s++;
11180 break;
11181 case '\n':
11182 *p++ = '\\';
11183 *p++ = 'n';
11184 s++;
11185 break;
11186 case '\r':
11187 *p++ = '\\';
11188 *p++ = 'r';
11189 s++;
11190 break;
11191 case '\t':
11192 *p++ = '\\';
11193 *p++ = 't';
11194 s++;
11195 break;
11196 case '\f':
11197 *p++ = '\\';
11198 *p++ = 'f';
11199 s++;
11200 break;
11201 case '\v':
11202 *p++ = '\\';
11203 *p++ = 'v';
11204 s++;
11205 break;
11206 default:
11207 *p++ = *s++;
11208 break;
11211 *p = '\0';
11213 return p - q;
11216 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
11218 #define STATIC_QUOTING_LEN 32
11219 int i, bufLen, realLength;
11220 const char *strRep;
11221 char *p;
11222 unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
11225 if (objc > STATIC_QUOTING_LEN) {
11226 quotingType = Jim_Alloc(objc);
11228 else {
11229 quotingType = staticQuoting;
11231 bufLen = 0;
11232 for (i = 0; i < objc; i++) {
11233 int len;
11235 strRep = Jim_GetString(objv[i], &len);
11236 quotingType[i] = ListElementQuotingType(strRep, len);
11237 switch (quotingType[i]) {
11238 case JIM_ELESTR_SIMPLE:
11239 if (i != 0 || strRep[0] != '#') {
11240 bufLen += len;
11241 break;
11244 quotingType[i] = JIM_ELESTR_BRACE;
11246 case JIM_ELESTR_BRACE:
11247 bufLen += len + 2;
11248 break;
11249 case JIM_ELESTR_QUOTE:
11250 bufLen += len * 2;
11251 break;
11253 bufLen++;
11255 bufLen++;
11258 p = objPtr->bytes = Jim_Alloc(bufLen + 1);
11259 realLength = 0;
11260 for (i = 0; i < objc; i++) {
11261 int len, qlen;
11263 strRep = Jim_GetString(objv[i], &len);
11265 switch (quotingType[i]) {
11266 case JIM_ELESTR_SIMPLE:
11267 memcpy(p, strRep, len);
11268 p += len;
11269 realLength += len;
11270 break;
11271 case JIM_ELESTR_BRACE:
11272 *p++ = '{';
11273 memcpy(p, strRep, len);
11274 p += len;
11275 *p++ = '}';
11276 realLength += len + 2;
11277 break;
11278 case JIM_ELESTR_QUOTE:
11279 if (i == 0 && strRep[0] == '#') {
11280 *p++ = '\\';
11281 realLength++;
11283 qlen = BackslashQuoteString(strRep, len, p);
11284 p += qlen;
11285 realLength += qlen;
11286 break;
11289 if (i + 1 != objc) {
11290 *p++ = ' ';
11291 realLength++;
11294 *p = '\0';
11295 objPtr->length = realLength;
11297 if (quotingType != staticQuoting) {
11298 Jim_Free(quotingType);
11302 static void UpdateStringOfList(struct Jim_Obj *objPtr)
11304 JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
11307 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
11309 struct JimParserCtx parser;
11310 const char *str;
11311 int strLen;
11312 Jim_Obj *fileNameObj;
11313 int linenr;
11315 if (objPtr->typePtr == &listObjType) {
11316 return JIM_OK;
11319 if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
11320 Jim_Obj **listObjPtrPtr;
11321 int len;
11322 int i;
11324 listObjPtrPtr = JimDictPairs(objPtr, &len);
11325 for (i = 0; i < len; i++) {
11326 Jim_IncrRefCount(listObjPtrPtr[i]);
11330 Jim_FreeIntRep(interp, objPtr);
11331 objPtr->typePtr = &listObjType;
11332 objPtr->internalRep.listValue.len = len;
11333 objPtr->internalRep.listValue.maxLen = len;
11334 objPtr->internalRep.listValue.ele = listObjPtrPtr;
11336 return JIM_OK;
11340 if (objPtr->typePtr == &sourceObjType) {
11341 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
11342 linenr = objPtr->internalRep.sourceValue.lineNumber;
11344 else {
11345 fileNameObj = interp->emptyObj;
11346 linenr = 1;
11348 Jim_IncrRefCount(fileNameObj);
11351 str = Jim_GetString(objPtr, &strLen);
11353 Jim_FreeIntRep(interp, objPtr);
11354 objPtr->typePtr = &listObjType;
11355 objPtr->internalRep.listValue.len = 0;
11356 objPtr->internalRep.listValue.maxLen = 0;
11357 objPtr->internalRep.listValue.ele = NULL;
11360 if (strLen) {
11361 JimParserInit(&parser, str, strLen, linenr);
11362 while (!parser.eof) {
11363 Jim_Obj *elementPtr;
11365 JimParseList(&parser);
11366 if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
11367 continue;
11368 elementPtr = JimParserGetTokenObj(interp, &parser);
11369 JimSetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
11370 ListAppendElement(objPtr, elementPtr);
11373 Jim_DecrRefCount(interp, fileNameObj);
11374 return JIM_OK;
11377 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
11379 Jim_Obj *objPtr;
11381 objPtr = Jim_NewObj(interp);
11382 objPtr->typePtr = &listObjType;
11383 objPtr->bytes = NULL;
11384 objPtr->internalRep.listValue.ele = NULL;
11385 objPtr->internalRep.listValue.len = 0;
11386 objPtr->internalRep.listValue.maxLen = 0;
11388 if (len) {
11389 ListInsertElements(objPtr, 0, len, elements);
11392 return objPtr;
11395 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
11396 Jim_Obj ***listVec)
11398 *listLen = Jim_ListLength(interp, listObj);
11399 *listVec = listObj->internalRep.listValue.ele;
11403 static int JimSign(jim_wide w)
11405 if (w == 0) {
11406 return 0;
11408 else if (w < 0) {
11409 return -1;
11411 return 1;
11415 struct lsort_info {
11416 jmp_buf jmpbuf;
11417 Jim_Obj *command;
11418 Jim_Interp *interp;
11419 enum {
11420 JIM_LSORT_ASCII,
11421 JIM_LSORT_NOCASE,
11422 JIM_LSORT_INTEGER,
11423 JIM_LSORT_REAL,
11424 JIM_LSORT_COMMAND
11425 } type;
11426 int order;
11427 int index;
11428 int indexed;
11429 int unique;
11430 int (*subfn)(Jim_Obj **, Jim_Obj **);
11433 static struct lsort_info *sort_info;
11435 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11437 Jim_Obj *lObj, *rObj;
11439 if (Jim_ListIndex(sort_info->interp, *lhsObj, sort_info->index, &lObj, JIM_ERRMSG) != JIM_OK ||
11440 Jim_ListIndex(sort_info->interp, *rhsObj, sort_info->index, &rObj, JIM_ERRMSG) != JIM_OK) {
11441 longjmp(sort_info->jmpbuf, JIM_ERR);
11443 return sort_info->subfn(&lObj, &rObj);
11447 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11449 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
11452 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11454 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
11457 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11459 jim_wide lhs = 0, rhs = 0;
11461 if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11462 Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11463 longjmp(sort_info->jmpbuf, JIM_ERR);
11466 return JimSign(lhs - rhs) * sort_info->order;
11469 static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11471 double lhs = 0, rhs = 0;
11473 if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11474 Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11475 longjmp(sort_info->jmpbuf, JIM_ERR);
11477 if (lhs == rhs) {
11478 return 0;
11480 if (lhs > rhs) {
11481 return sort_info->order;
11483 return -sort_info->order;
11486 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11488 Jim_Obj *compare_script;
11489 int rc;
11491 jim_wide ret = 0;
11494 compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
11495 Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
11496 Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);
11498 rc = Jim_EvalObj(sort_info->interp, compare_script);
11500 if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
11501 longjmp(sort_info->jmpbuf, rc);
11504 return JimSign(ret) * sort_info->order;
11507 static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
11509 int src;
11510 int dst = 0;
11511 Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
11513 for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
11514 if (comp(&ele[dst], &ele[src]) == 0) {
11516 Jim_DecrRefCount(sort_info->interp, ele[dst]);
11518 else {
11520 dst++;
11522 ele[dst] = ele[src];
11525 ele[++dst] = ele[src];
11528 listObjPtr->internalRep.listValue.len = dst;
11532 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
11534 struct lsort_info *prev_info;
11536 typedef int (qsort_comparator) (const void *, const void *);
11537 int (*fn) (Jim_Obj **, Jim_Obj **);
11538 Jim_Obj **vector;
11539 int len;
11540 int rc;
11542 JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
11543 SetListFromAny(interp, listObjPtr);
11546 prev_info = sort_info;
11547 sort_info = info;
11549 vector = listObjPtr->internalRep.listValue.ele;
11550 len = listObjPtr->internalRep.listValue.len;
11551 switch (info->type) {
11552 case JIM_LSORT_ASCII:
11553 fn = ListSortString;
11554 break;
11555 case JIM_LSORT_NOCASE:
11556 fn = ListSortStringNoCase;
11557 break;
11558 case JIM_LSORT_INTEGER:
11559 fn = ListSortInteger;
11560 break;
11561 case JIM_LSORT_REAL:
11562 fn = ListSortReal;
11563 break;
11564 case JIM_LSORT_COMMAND:
11565 fn = ListSortCommand;
11566 break;
11567 default:
11568 fn = NULL;
11569 JimPanic((1, "ListSort called with invalid sort type"));
11572 if (info->indexed) {
11574 info->subfn = fn;
11575 fn = ListSortIndexHelper;
11578 if ((rc = setjmp(info->jmpbuf)) == 0) {
11579 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
11581 if (info->unique && len > 1) {
11582 ListRemoveDuplicates(listObjPtr, fn);
11585 Jim_InvalidateStringRep(listObjPtr);
11587 sort_info = prev_info;
11589 return rc;
11592 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
11594 int currentLen = listPtr->internalRep.listValue.len;
11595 int requiredLen = currentLen + elemc;
11596 int i;
11597 Jim_Obj **point;
11599 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
11600 if (requiredLen < 2) {
11602 requiredLen = 4;
11604 else {
11605 requiredLen *= 2;
11608 listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
11609 sizeof(Jim_Obj *) * requiredLen);
11611 listPtr->internalRep.listValue.maxLen = requiredLen;
11613 if (idx < 0) {
11614 idx = currentLen;
11616 point = listPtr->internalRep.listValue.ele + idx;
11617 memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
11618 for (i = 0; i < elemc; ++i) {
11619 point[i] = elemVec[i];
11620 Jim_IncrRefCount(point[i]);
11622 listPtr->internalRep.listValue.len += elemc;
11625 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
11627 ListInsertElements(listPtr, -1, 1, &objPtr);
11630 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11632 ListInsertElements(listPtr, -1,
11633 appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele);
11636 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
11638 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object"));
11639 SetListFromAny(interp, listPtr);
11640 Jim_InvalidateStringRep(listPtr);
11641 ListAppendElement(listPtr, objPtr);
11644 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11646 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object"));
11647 SetListFromAny(interp, listPtr);
11648 SetListFromAny(interp, appendListPtr);
11649 Jim_InvalidateStringRep(listPtr);
11650 ListAppendList(listPtr, appendListPtr);
11653 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
11655 SetListFromAny(interp, objPtr);
11656 return objPtr->internalRep.listValue.len;
11659 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11660 int objc, Jim_Obj *const *objVec)
11662 JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
11663 SetListFromAny(interp, listPtr);
11664 if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
11665 idx = listPtr->internalRep.listValue.len;
11666 else if (idx < 0)
11667 idx = 0;
11668 Jim_InvalidateStringRep(listPtr);
11669 ListInsertElements(listPtr, idx, objc, objVec);
11672 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
11674 SetListFromAny(interp, listPtr);
11675 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11676 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11677 return NULL;
11679 if (idx < 0)
11680 idx = listPtr->internalRep.listValue.len + idx;
11681 return listPtr->internalRep.listValue.ele[idx];
11684 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
11686 *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
11687 if (*objPtrPtr == NULL) {
11688 if (flags & JIM_ERRMSG) {
11689 Jim_SetResultString(interp, "list index out of range", -1);
11691 return JIM_ERR;
11693 return JIM_OK;
11696 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11697 Jim_Obj *newObjPtr, int flags)
11699 SetListFromAny(interp, listPtr);
11700 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11701 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11702 if (flags & JIM_ERRMSG) {
11703 Jim_SetResultString(interp, "list index out of range", -1);
11705 return JIM_ERR;
11707 if (idx < 0)
11708 idx = listPtr->internalRep.listValue.len + idx;
11709 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
11710 listPtr->internalRep.listValue.ele[idx] = newObjPtr;
11711 Jim_IncrRefCount(newObjPtr);
11712 return JIM_OK;
11715 int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
11716 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
11718 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
11719 int shared, i, idx;
11721 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
11722 if (objPtr == NULL)
11723 return JIM_ERR;
11724 if ((shared = Jim_IsShared(objPtr)))
11725 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
11726 for (i = 0; i < indexc - 1; i++) {
11727 listObjPtr = objPtr;
11728 if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
11729 goto err;
11730 if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_ERRMSG) != JIM_OK) {
11731 goto err;
11733 if (Jim_IsShared(objPtr)) {
11734 objPtr = Jim_DuplicateObj(interp, objPtr);
11735 ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
11737 Jim_InvalidateStringRep(listObjPtr);
11739 if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
11740 goto err;
11741 if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
11742 goto err;
11743 Jim_InvalidateStringRep(objPtr);
11744 Jim_InvalidateStringRep(varObjPtr);
11745 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
11746 goto err;
11747 Jim_SetResult(interp, varObjPtr);
11748 return JIM_OK;
11749 err:
11750 if (shared) {
11751 Jim_FreeNewObj(interp, varObjPtr);
11753 return JIM_ERR;
11756 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
11758 int i;
11759 int listLen = Jim_ListLength(interp, listObjPtr);
11760 Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
11762 for (i = 0; i < listLen; ) {
11763 Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i));
11764 if (++i != listLen) {
11765 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11768 return resObjPtr;
11771 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
11773 int i;
11775 for (i = 0; i < objc; i++) {
11776 if (!Jim_IsList(objv[i]))
11777 break;
11779 if (i == objc) {
11780 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
11782 for (i = 0; i < objc; i++)
11783 ListAppendList(objPtr, objv[i]);
11784 return objPtr;
11786 else {
11788 int len = 0, objLen;
11789 char *bytes, *p;
11792 for (i = 0; i < objc; i++) {
11793 len += Jim_Length(objv[i]);
11795 if (objc)
11796 len += objc - 1;
11798 p = bytes = Jim_Alloc(len + 1);
11799 for (i = 0; i < objc; i++) {
11800 const char *s = Jim_GetString(objv[i], &objLen);
11803 while (objLen && isspace(UCHAR(*s))) {
11804 s++;
11805 objLen--;
11806 len--;
11809 while (objLen && isspace(UCHAR(s[objLen - 1]))) {
11811 if (objLen > 1 && s[objLen - 2] == '\\') {
11812 break;
11814 objLen--;
11815 len--;
11817 memcpy(p, s, objLen);
11818 p += objLen;
11819 if (i + 1 != objc) {
11820 if (objLen)
11821 *p++ = ' ';
11822 else {
11823 len--;
11827 *p = '\0';
11828 return Jim_NewStringObjNoAlloc(interp, bytes, len);
11832 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr,
11833 Jim_Obj *lastObjPtr)
11835 int first, last;
11836 int len, rangeLen;
11838 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
11839 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
11840 return NULL;
11841 len = Jim_ListLength(interp, listObjPtr);
11842 first = JimRelToAbsIndex(len, first);
11843 last = JimRelToAbsIndex(len, last);
11844 JimRelToAbsRange(len, &first, &last, &rangeLen);
11845 if (first == 0 && last == len) {
11846 return listObjPtr;
11848 return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
11851 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11852 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11853 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
11854 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11857 static unsigned int JimObjectHTHashFunction(const void *key)
11859 int len;
11860 const char *str = Jim_GetString((Jim_Obj *)key, &len);
11861 return Jim_GenHashFunction((const unsigned char *)str, len);
11864 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
11866 return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
11869 static void *JimObjectHTKeyValDup(void *privdata, const void *val)
11871 Jim_IncrRefCount((Jim_Obj *)val);
11872 return (void *)val;
11875 static void JimObjectHTKeyValDestructor(void *interp, void *val)
11877 Jim_DecrRefCount(interp, (Jim_Obj *)val);
11880 static const Jim_HashTableType JimDictHashTableType = {
11881 JimObjectHTHashFunction,
11882 JimObjectHTKeyValDup,
11883 JimObjectHTKeyValDup,
11884 JimObjectHTKeyCompare,
11885 JimObjectHTKeyValDestructor,
11886 JimObjectHTKeyValDestructor
11889 static const Jim_ObjType dictObjType = {
11890 "dict",
11891 FreeDictInternalRep,
11892 DupDictInternalRep,
11893 UpdateStringOfDict,
11894 JIM_TYPE_NONE,
11897 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11899 JIM_NOTUSED(interp);
11901 Jim_FreeHashTable(objPtr->internalRep.ptr);
11902 Jim_Free(objPtr->internalRep.ptr);
11905 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11907 Jim_HashTable *ht, *dupHt;
11908 Jim_HashTableIterator htiter;
11909 Jim_HashEntry *he;
11912 ht = srcPtr->internalRep.ptr;
11913 dupHt = Jim_Alloc(sizeof(*dupHt));
11914 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
11915 if (ht->size != 0)
11916 Jim_ExpandHashTable(dupHt, ht->size);
11918 JimInitHashTableIterator(ht, &htiter);
11919 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
11920 Jim_AddHashEntry(dupHt, he->key, he->u.val);
11923 dupPtr->internalRep.ptr = dupHt;
11924 dupPtr->typePtr = &dictObjType;
11927 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len)
11929 Jim_HashTable *ht;
11930 Jim_HashTableIterator htiter;
11931 Jim_HashEntry *he;
11932 Jim_Obj **objv;
11933 int i;
11935 ht = dictPtr->internalRep.ptr;
11938 objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *));
11939 JimInitHashTableIterator(ht, &htiter);
11940 i = 0;
11941 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
11942 objv[i++] = Jim_GetHashEntryKey(he);
11943 objv[i++] = Jim_GetHashEntryVal(he);
11945 *len = i;
11946 return objv;
11949 static void UpdateStringOfDict(struct Jim_Obj *objPtr)
11952 int len;
11953 Jim_Obj **objv = JimDictPairs(objPtr, &len);
11956 JimMakeListStringRep(objPtr, objv, len);
11958 Jim_Free(objv);
11961 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
11963 int listlen;
11965 if (objPtr->typePtr == &dictObjType) {
11966 return JIM_OK;
11969 if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
11970 Jim_String(objPtr);
11974 listlen = Jim_ListLength(interp, objPtr);
11975 if (listlen % 2) {
11976 Jim_SetResultString(interp, "missing value to go with key", -1);
11977 return JIM_ERR;
11979 else {
11981 Jim_HashTable *ht;
11982 int i;
11984 ht = Jim_Alloc(sizeof(*ht));
11985 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
11987 for (i = 0; i < listlen; i += 2) {
11988 Jim_Obj *keyObjPtr = Jim_ListGetIndex(interp, objPtr, i);
11989 Jim_Obj *valObjPtr = Jim_ListGetIndex(interp, objPtr, i + 1);
11991 Jim_ReplaceHashEntry(ht, keyObjPtr, valObjPtr);
11994 Jim_FreeIntRep(interp, objPtr);
11995 objPtr->typePtr = &dictObjType;
11996 objPtr->internalRep.ptr = ht;
11998 return JIM_OK;
12004 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12005 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12007 Jim_HashTable *ht = objPtr->internalRep.ptr;
12009 if (valueObjPtr == NULL) {
12010 return Jim_DeleteHashEntry(ht, keyObjPtr);
12012 Jim_ReplaceHashEntry(ht, keyObjPtr, valueObjPtr);
12013 return JIM_OK;
12016 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12017 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12019 JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
12020 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
12021 return JIM_ERR;
12023 Jim_InvalidateStringRep(objPtr);
12024 return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
12027 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
12029 Jim_Obj *objPtr;
12030 int i;
12032 JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
12034 objPtr = Jim_NewObj(interp);
12035 objPtr->typePtr = &dictObjType;
12036 objPtr->bytes = NULL;
12037 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
12038 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
12039 for (i = 0; i < len; i += 2)
12040 DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
12041 return objPtr;
12044 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
12045 Jim_Obj **objPtrPtr, int flags)
12047 Jim_HashEntry *he;
12048 Jim_HashTable *ht;
12050 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12051 return -1;
12053 ht = dictPtr->internalRep.ptr;
12054 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
12055 if (flags & JIM_ERRMSG) {
12056 Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
12058 return JIM_ERR;
12060 *objPtrPtr = he->u.val;
12061 return JIM_OK;
12065 int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len)
12067 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12068 return JIM_ERR;
12070 *objPtrPtr = JimDictPairs(dictPtr, len);
12072 return JIM_OK;
12077 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
12078 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
12080 int i;
12082 if (keyc == 0) {
12083 *objPtrPtr = dictPtr;
12084 return JIM_OK;
12087 for (i = 0; i < keyc; i++) {
12088 Jim_Obj *objPtr;
12090 int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
12091 if (rc != JIM_OK) {
12092 return rc;
12094 dictPtr = objPtr;
12096 *objPtrPtr = dictPtr;
12097 return JIM_OK;
12100 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
12101 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags)
12103 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
12104 int shared, i;
12106 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
12107 if (objPtr == NULL) {
12108 if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
12110 return JIM_ERR;
12112 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
12113 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
12114 Jim_FreeNewObj(interp, varObjPtr);
12115 return JIM_ERR;
12118 if ((shared = Jim_IsShared(objPtr)))
12119 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
12120 for (i = 0; i < keyc; i++) {
12121 dictObjPtr = objPtr;
12124 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
12125 goto err;
12128 if (i == keyc - 1) {
12130 if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
12131 if (newObjPtr || (flags & JIM_MUSTEXIST)) {
12132 goto err;
12135 break;
12139 Jim_InvalidateStringRep(dictObjPtr);
12140 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
12141 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
12142 if (Jim_IsShared(objPtr)) {
12143 objPtr = Jim_DuplicateObj(interp, objPtr);
12144 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12147 else {
12148 if (newObjPtr == NULL) {
12149 goto err;
12151 objPtr = Jim_NewDictObj(interp, NULL, 0);
12152 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12156 Jim_InvalidateStringRep(objPtr);
12157 Jim_InvalidateStringRep(varObjPtr);
12158 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
12159 goto err;
12161 Jim_SetResult(interp, varObjPtr);
12162 return JIM_OK;
12163 err:
12164 if (shared) {
12165 Jim_FreeNewObj(interp, varObjPtr);
12167 return JIM_ERR;
12170 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
12171 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
12173 static const Jim_ObjType indexObjType = {
12174 "index",
12175 NULL,
12176 NULL,
12177 UpdateStringOfIndex,
12178 JIM_TYPE_NONE,
12181 static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
12183 if (objPtr->internalRep.intValue == -1) {
12184 JimSetStringBytes(objPtr, "end");
12186 else {
12187 char buf[JIM_INTEGER_SPACE + 1];
12188 if (objPtr->internalRep.intValue >= 0) {
12189 sprintf(buf, "%d", objPtr->internalRep.intValue);
12191 else {
12193 sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
12195 JimSetStringBytes(objPtr, buf);
12199 static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12201 int idx, end = 0;
12202 const char *str;
12203 char *endptr;
12206 str = Jim_String(objPtr);
12209 if (strncmp(str, "end", 3) == 0) {
12210 end = 1;
12211 str += 3;
12212 idx = 0;
12214 else {
12215 idx = jim_strtol(str, &endptr);
12217 if (endptr == str) {
12218 goto badindex;
12220 str = endptr;
12224 if (*str == '+' || *str == '-') {
12225 int sign = (*str == '+' ? 1 : -1);
12227 idx += sign * jim_strtol(++str, &endptr);
12228 if (str == endptr || *endptr) {
12229 goto badindex;
12231 str = endptr;
12234 while (isspace(UCHAR(*str))) {
12235 str++;
12237 if (*str) {
12238 goto badindex;
12240 if (end) {
12241 if (idx > 0) {
12242 idx = INT_MAX;
12244 else {
12246 idx--;
12249 else if (idx < 0) {
12250 idx = -INT_MAX;
12254 Jim_FreeIntRep(interp, objPtr);
12255 objPtr->typePtr = &indexObjType;
12256 objPtr->internalRep.intValue = idx;
12257 return JIM_OK;
12259 badindex:
12260 Jim_SetResultFormatted(interp,
12261 "bad index \"%#s\": must be integer?[+-]integer? or end?[+-]integer?", objPtr);
12262 return JIM_ERR;
12265 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
12268 if (objPtr->typePtr == &intObjType) {
12269 jim_wide val = JimWideValue(objPtr);
12271 if (val < 0)
12272 *indexPtr = -INT_MAX;
12273 else if (val > INT_MAX)
12274 *indexPtr = INT_MAX;
12275 else
12276 *indexPtr = (int)val;
12277 return JIM_OK;
12279 if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
12280 return JIM_ERR;
12281 *indexPtr = objPtr->internalRep.intValue;
12282 return JIM_OK;
12287 static const char * const jimReturnCodes[] = {
12288 "ok",
12289 "error",
12290 "return",
12291 "break",
12292 "continue",
12293 "signal",
12294 "exit",
12295 "eval",
12296 NULL
12299 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes))
12301 static const Jim_ObjType returnCodeObjType = {
12302 "return-code",
12303 NULL,
12304 NULL,
12305 NULL,
12306 JIM_TYPE_NONE,
12309 const char *Jim_ReturnCode(int code)
12311 if (code < 0 || code >= (int)jimReturnCodesSize) {
12312 return "?";
12314 else {
12315 return jimReturnCodes[code];
12319 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12321 int returnCode;
12322 jim_wide wideValue;
12325 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
12326 returnCode = (int)wideValue;
12327 else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
12328 Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
12329 return JIM_ERR;
12332 Jim_FreeIntRep(interp, objPtr);
12333 objPtr->typePtr = &returnCodeObjType;
12334 objPtr->internalRep.intValue = returnCode;
12335 return JIM_OK;
12338 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
12340 if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
12341 return JIM_ERR;
12342 *intPtr = objPtr->internalRep.intValue;
12343 return JIM_OK;
12346 static int JimParseExprOperator(struct JimParserCtx *pc);
12347 static int JimParseExprNumber(struct JimParserCtx *pc);
12348 static int JimParseExprIrrational(struct JimParserCtx *pc);
12353 enum
12357 JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
12358 JIM_EXPROP_DIV,
12359 JIM_EXPROP_MOD,
12360 JIM_EXPROP_SUB,
12361 JIM_EXPROP_ADD,
12362 JIM_EXPROP_LSHIFT,
12363 JIM_EXPROP_RSHIFT,
12364 JIM_EXPROP_ROTL,
12365 JIM_EXPROP_ROTR,
12366 JIM_EXPROP_LT,
12367 JIM_EXPROP_GT,
12368 JIM_EXPROP_LTE,
12369 JIM_EXPROP_GTE,
12370 JIM_EXPROP_NUMEQ,
12371 JIM_EXPROP_NUMNE,
12372 JIM_EXPROP_BITAND,
12373 JIM_EXPROP_BITXOR,
12374 JIM_EXPROP_BITOR,
12377 JIM_EXPROP_LOGICAND,
12378 JIM_EXPROP_LOGICAND_LEFT,
12379 JIM_EXPROP_LOGICAND_RIGHT,
12382 JIM_EXPROP_LOGICOR,
12383 JIM_EXPROP_LOGICOR_LEFT,
12384 JIM_EXPROP_LOGICOR_RIGHT,
12388 JIM_EXPROP_TERNARY,
12389 JIM_EXPROP_TERNARY_LEFT,
12390 JIM_EXPROP_TERNARY_RIGHT,
12393 JIM_EXPROP_COLON,
12394 JIM_EXPROP_COLON_LEFT,
12395 JIM_EXPROP_COLON_RIGHT,
12397 JIM_EXPROP_POW,
12400 JIM_EXPROP_STREQ,
12401 JIM_EXPROP_STRNE,
12402 JIM_EXPROP_STRIN,
12403 JIM_EXPROP_STRNI,
12406 JIM_EXPROP_NOT,
12407 JIM_EXPROP_BITNOT,
12408 JIM_EXPROP_UNARYMINUS,
12409 JIM_EXPROP_UNARYPLUS,
12412 JIM_EXPROP_FUNC_FIRST,
12413 JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST,
12414 JIM_EXPROP_FUNC_WIDE,
12415 JIM_EXPROP_FUNC_ABS,
12416 JIM_EXPROP_FUNC_DOUBLE,
12417 JIM_EXPROP_FUNC_ROUND,
12418 JIM_EXPROP_FUNC_RAND,
12419 JIM_EXPROP_FUNC_SRAND,
12422 JIM_EXPROP_FUNC_SIN,
12423 JIM_EXPROP_FUNC_COS,
12424 JIM_EXPROP_FUNC_TAN,
12425 JIM_EXPROP_FUNC_ASIN,
12426 JIM_EXPROP_FUNC_ACOS,
12427 JIM_EXPROP_FUNC_ATAN,
12428 JIM_EXPROP_FUNC_SINH,
12429 JIM_EXPROP_FUNC_COSH,
12430 JIM_EXPROP_FUNC_TANH,
12431 JIM_EXPROP_FUNC_CEIL,
12432 JIM_EXPROP_FUNC_FLOOR,
12433 JIM_EXPROP_FUNC_EXP,
12434 JIM_EXPROP_FUNC_LOG,
12435 JIM_EXPROP_FUNC_LOG10,
12436 JIM_EXPROP_FUNC_SQRT,
12437 JIM_EXPROP_FUNC_POW,
12440 struct JimExprState
12442 Jim_Obj **stack;
12443 int stacklen;
12444 int opcode;
12445 int skip;
12449 typedef struct Jim_ExprOperator
12451 const char *name;
12452 int (*funcop) (Jim_Interp *interp, struct JimExprState * e);
12453 unsigned char precedence;
12454 unsigned char arity;
12455 unsigned char lazy;
12456 unsigned char namelen;
12457 } Jim_ExprOperator;
12459 static void ExprPush(struct JimExprState *e, Jim_Obj *obj)
12461 Jim_IncrRefCount(obj);
12462 e->stack[e->stacklen++] = obj;
12465 static Jim_Obj *ExprPop(struct JimExprState *e)
12467 return e->stack[--e->stacklen];
12470 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e)
12472 int intresult = 1;
12473 int rc = JIM_OK;
12474 Jim_Obj *A = ExprPop(e);
12475 double dA, dC = 0;
12476 jim_wide wA, wC = 0;
12478 if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
12479 switch (e->opcode) {
12480 case JIM_EXPROP_FUNC_INT:
12481 case JIM_EXPROP_FUNC_WIDE:
12482 case JIM_EXPROP_FUNC_ROUND:
12483 case JIM_EXPROP_UNARYPLUS:
12484 wC = wA;
12485 break;
12486 case JIM_EXPROP_FUNC_DOUBLE:
12487 dC = wA;
12488 intresult = 0;
12489 break;
12490 case JIM_EXPROP_FUNC_ABS:
12491 wC = wA >= 0 ? wA : -wA;
12492 break;
12493 case JIM_EXPROP_UNARYMINUS:
12494 wC = -wA;
12495 break;
12496 case JIM_EXPROP_NOT:
12497 wC = !wA;
12498 break;
12499 default:
12500 abort();
12503 else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
12504 switch (e->opcode) {
12505 case JIM_EXPROP_FUNC_INT:
12506 case JIM_EXPROP_FUNC_WIDE:
12507 wC = dA;
12508 break;
12509 case JIM_EXPROP_FUNC_ROUND:
12510 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
12511 break;
12512 case JIM_EXPROP_FUNC_DOUBLE:
12513 case JIM_EXPROP_UNARYPLUS:
12514 dC = dA;
12515 intresult = 0;
12516 break;
12517 case JIM_EXPROP_FUNC_ABS:
12518 dC = dA >= 0 ? dA : -dA;
12519 intresult = 0;
12520 break;
12521 case JIM_EXPROP_UNARYMINUS:
12522 dC = -dA;
12523 intresult = 0;
12524 break;
12525 case JIM_EXPROP_NOT:
12526 wC = !dA;
12527 break;
12528 default:
12529 abort();
12533 if (rc == JIM_OK) {
12534 if (intresult) {
12535 ExprPush(e, Jim_NewIntObj(interp, wC));
12537 else {
12538 ExprPush(e, Jim_NewDoubleObj(interp, dC));
12542 Jim_DecrRefCount(interp, A);
12544 return rc;
12547 static double JimRandDouble(Jim_Interp *interp)
12549 unsigned long x;
12550 JimRandomBytes(interp, &x, sizeof(x));
12552 return (double)x / (unsigned long)~0;
12555 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e)
12557 Jim_Obj *A = ExprPop(e);
12558 jim_wide wA;
12560 int rc = Jim_GetWide(interp, A, &wA);
12561 if (rc == JIM_OK) {
12562 switch (e->opcode) {
12563 case JIM_EXPROP_BITNOT:
12564 ExprPush(e, Jim_NewIntObj(interp, ~wA));
12565 break;
12566 case JIM_EXPROP_FUNC_SRAND:
12567 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
12568 ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12569 break;
12570 default:
12571 abort();
12575 Jim_DecrRefCount(interp, A);
12577 return rc;
12580 static int JimExprOpNone(Jim_Interp *interp, struct JimExprState *e)
12582 JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
12584 ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12586 return JIM_OK;
12589 #ifdef JIM_MATH_FUNCTIONS
12590 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e)
12592 int rc;
12593 Jim_Obj *A = ExprPop(e);
12594 double dA, dC;
12596 rc = Jim_GetDouble(interp, A, &dA);
12597 if (rc == JIM_OK) {
12598 switch (e->opcode) {
12599 case JIM_EXPROP_FUNC_SIN:
12600 dC = sin(dA);
12601 break;
12602 case JIM_EXPROP_FUNC_COS:
12603 dC = cos(dA);
12604 break;
12605 case JIM_EXPROP_FUNC_TAN:
12606 dC = tan(dA);
12607 break;
12608 case JIM_EXPROP_FUNC_ASIN:
12609 dC = asin(dA);
12610 break;
12611 case JIM_EXPROP_FUNC_ACOS:
12612 dC = acos(dA);
12613 break;
12614 case JIM_EXPROP_FUNC_ATAN:
12615 dC = atan(dA);
12616 break;
12617 case JIM_EXPROP_FUNC_SINH:
12618 dC = sinh(dA);
12619 break;
12620 case JIM_EXPROP_FUNC_COSH:
12621 dC = cosh(dA);
12622 break;
12623 case JIM_EXPROP_FUNC_TANH:
12624 dC = tanh(dA);
12625 break;
12626 case JIM_EXPROP_FUNC_CEIL:
12627 dC = ceil(dA);
12628 break;
12629 case JIM_EXPROP_FUNC_FLOOR:
12630 dC = floor(dA);
12631 break;
12632 case JIM_EXPROP_FUNC_EXP:
12633 dC = exp(dA);
12634 break;
12635 case JIM_EXPROP_FUNC_LOG:
12636 dC = log(dA);
12637 break;
12638 case JIM_EXPROP_FUNC_LOG10:
12639 dC = log10(dA);
12640 break;
12641 case JIM_EXPROP_FUNC_SQRT:
12642 dC = sqrt(dA);
12643 break;
12644 default:
12645 abort();
12647 ExprPush(e, Jim_NewDoubleObj(interp, dC));
12650 Jim_DecrRefCount(interp, A);
12652 return rc;
12654 #endif
12657 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e)
12659 Jim_Obj *B = ExprPop(e);
12660 Jim_Obj *A = ExprPop(e);
12661 jim_wide wA, wB;
12662 int rc = JIM_ERR;
12664 if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
12665 jim_wide wC;
12667 rc = JIM_OK;
12669 switch (e->opcode) {
12670 case JIM_EXPROP_LSHIFT:
12671 wC = wA << wB;
12672 break;
12673 case JIM_EXPROP_RSHIFT:
12674 wC = wA >> wB;
12675 break;
12676 case JIM_EXPROP_BITAND:
12677 wC = wA & wB;
12678 break;
12679 case JIM_EXPROP_BITXOR:
12680 wC = wA ^ wB;
12681 break;
12682 case JIM_EXPROP_BITOR:
12683 wC = wA | wB;
12684 break;
12685 case JIM_EXPROP_MOD:
12686 if (wB == 0) {
12687 wC = 0;
12688 Jim_SetResultString(interp, "Division by zero", -1);
12689 rc = JIM_ERR;
12691 else {
12692 int negative = 0;
12694 if (wB < 0) {
12695 wB = -wB;
12696 wA = -wA;
12697 negative = 1;
12699 wC = wA % wB;
12700 if (wC < 0) {
12701 wC += wB;
12703 if (negative) {
12704 wC = -wC;
12707 break;
12708 case JIM_EXPROP_ROTL:
12709 case JIM_EXPROP_ROTR:{
12711 unsigned long uA = (unsigned long)wA;
12712 unsigned long uB = (unsigned long)wB;
12713 const unsigned int S = sizeof(unsigned long) * 8;
12716 uB %= S;
12718 if (e->opcode == JIM_EXPROP_ROTR) {
12719 uB = S - uB;
12721 wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
12722 break;
12724 default:
12725 abort();
12727 ExprPush(e, Jim_NewIntObj(interp, wC));
12731 Jim_DecrRefCount(interp, A);
12732 Jim_DecrRefCount(interp, B);
12734 return rc;
12739 static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e)
12741 int intresult = 1;
12742 int rc = JIM_OK;
12743 double dA, dB, dC = 0;
12744 jim_wide wA, wB, wC = 0;
12746 Jim_Obj *B = ExprPop(e);
12747 Jim_Obj *A = ExprPop(e);
12749 if ((A->typePtr != &doubleObjType || A->bytes) &&
12750 (B->typePtr != &doubleObjType || B->bytes) &&
12751 JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
12755 switch (e->opcode) {
12756 case JIM_EXPROP_POW:
12757 case JIM_EXPROP_FUNC_POW:
12758 wC = JimPowWide(wA, wB);
12759 break;
12760 case JIM_EXPROP_ADD:
12761 wC = wA + wB;
12762 break;
12763 case JIM_EXPROP_SUB:
12764 wC = wA - wB;
12765 break;
12766 case JIM_EXPROP_MUL:
12767 wC = wA * wB;
12768 break;
12769 case JIM_EXPROP_DIV:
12770 if (wB == 0) {
12771 Jim_SetResultString(interp, "Division by zero", -1);
12772 rc = JIM_ERR;
12774 else {
12775 if (wB < 0) {
12776 wB = -wB;
12777 wA = -wA;
12779 wC = wA / wB;
12780 if (wA % wB < 0) {
12781 wC--;
12784 break;
12785 case JIM_EXPROP_LT:
12786 wC = wA < wB;
12787 break;
12788 case JIM_EXPROP_GT:
12789 wC = wA > wB;
12790 break;
12791 case JIM_EXPROP_LTE:
12792 wC = wA <= wB;
12793 break;
12794 case JIM_EXPROP_GTE:
12795 wC = wA >= wB;
12796 break;
12797 case JIM_EXPROP_NUMEQ:
12798 wC = wA == wB;
12799 break;
12800 case JIM_EXPROP_NUMNE:
12801 wC = wA != wB;
12802 break;
12803 default:
12804 abort();
12807 else if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
12808 intresult = 0;
12809 switch (e->opcode) {
12810 case JIM_EXPROP_POW:
12811 case JIM_EXPROP_FUNC_POW:
12812 #ifdef JIM_MATH_FUNCTIONS
12813 dC = pow(dA, dB);
12814 #else
12815 Jim_SetResultString(interp, "unsupported", -1);
12816 rc = JIM_ERR;
12817 #endif
12818 break;
12819 case JIM_EXPROP_ADD:
12820 dC = dA + dB;
12821 break;
12822 case JIM_EXPROP_SUB:
12823 dC = dA - dB;
12824 break;
12825 case JIM_EXPROP_MUL:
12826 dC = dA * dB;
12827 break;
12828 case JIM_EXPROP_DIV:
12829 if (dB == 0) {
12830 #ifdef INFINITY
12831 dC = dA < 0 ? -INFINITY : INFINITY;
12832 #else
12833 dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
12834 #endif
12836 else {
12837 dC = dA / dB;
12839 break;
12840 case JIM_EXPROP_LT:
12841 wC = dA < dB;
12842 intresult = 1;
12843 break;
12844 case JIM_EXPROP_GT:
12845 wC = dA > dB;
12846 intresult = 1;
12847 break;
12848 case JIM_EXPROP_LTE:
12849 wC = dA <= dB;
12850 intresult = 1;
12851 break;
12852 case JIM_EXPROP_GTE:
12853 wC = dA >= dB;
12854 intresult = 1;
12855 break;
12856 case JIM_EXPROP_NUMEQ:
12857 wC = dA == dB;
12858 intresult = 1;
12859 break;
12860 case JIM_EXPROP_NUMNE:
12861 wC = dA != dB;
12862 intresult = 1;
12863 break;
12864 default:
12865 abort();
12868 else {
12872 int i = Jim_StringCompareObj(interp, A, B, 0);
12874 switch (e->opcode) {
12875 case JIM_EXPROP_LT:
12876 wC = i < 0;
12877 break;
12878 case JIM_EXPROP_GT:
12879 wC = i > 0;
12880 break;
12881 case JIM_EXPROP_LTE:
12882 wC = i <= 0;
12883 break;
12884 case JIM_EXPROP_GTE:
12885 wC = i >= 0;
12886 break;
12887 case JIM_EXPROP_NUMEQ:
12888 wC = i == 0;
12889 break;
12890 case JIM_EXPROP_NUMNE:
12891 wC = i != 0;
12892 break;
12893 default:
12894 rc = JIM_ERR;
12895 break;
12899 if (rc == JIM_OK) {
12900 if (intresult) {
12901 ExprPush(e, Jim_NewIntObj(interp, wC));
12903 else {
12904 ExprPush(e, Jim_NewDoubleObj(interp, dC));
12908 Jim_DecrRefCount(interp, A);
12909 Jim_DecrRefCount(interp, B);
12911 return rc;
12914 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
12916 int listlen;
12917 int i;
12919 listlen = Jim_ListLength(interp, listObjPtr);
12920 for (i = 0; i < listlen; i++) {
12921 if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
12922 return 1;
12925 return 0;
12928 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e)
12930 Jim_Obj *B = ExprPop(e);
12931 Jim_Obj *A = ExprPop(e);
12933 jim_wide wC;
12935 switch (e->opcode) {
12936 case JIM_EXPROP_STREQ:
12937 case JIM_EXPROP_STRNE:
12938 wC = Jim_StringEqObj(A, B);
12939 if (e->opcode == JIM_EXPROP_STRNE) {
12940 wC = !wC;
12942 break;
12943 case JIM_EXPROP_STRIN:
12944 wC = JimSearchList(interp, B, A);
12945 break;
12946 case JIM_EXPROP_STRNI:
12947 wC = !JimSearchList(interp, B, A);
12948 break;
12949 default:
12950 abort();
12952 ExprPush(e, Jim_NewIntObj(interp, wC));
12954 Jim_DecrRefCount(interp, A);
12955 Jim_DecrRefCount(interp, B);
12957 return JIM_OK;
12960 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
12962 long l;
12963 double d;
12965 if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
12966 return l != 0;
12968 if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
12969 return d != 0;
12971 return -1;
12974 static int JimExprOpAndLeft(Jim_Interp *interp, struct JimExprState *e)
12976 Jim_Obj *skip = ExprPop(e);
12977 Jim_Obj *A = ExprPop(e);
12978 int rc = JIM_OK;
12980 switch (ExprBool(interp, A)) {
12981 case 0:
12983 e->skip = JimWideValue(skip);
12984 ExprPush(e, Jim_NewIntObj(interp, 0));
12985 break;
12987 case 1:
12989 break;
12991 case -1:
12993 rc = JIM_ERR;
12995 Jim_DecrRefCount(interp, A);
12996 Jim_DecrRefCount(interp, skip);
12998 return rc;
13001 static int JimExprOpOrLeft(Jim_Interp *interp, struct JimExprState *e)
13003 Jim_Obj *skip = ExprPop(e);
13004 Jim_Obj *A = ExprPop(e);
13005 int rc = JIM_OK;
13007 switch (ExprBool(interp, A)) {
13008 case 0:
13010 break;
13012 case 1:
13014 e->skip = JimWideValue(skip);
13015 ExprPush(e, Jim_NewIntObj(interp, 1));
13016 break;
13018 case -1:
13020 rc = JIM_ERR;
13021 break;
13023 Jim_DecrRefCount(interp, A);
13024 Jim_DecrRefCount(interp, skip);
13026 return rc;
13029 static int JimExprOpAndOrRight(Jim_Interp *interp, struct JimExprState *e)
13031 Jim_Obj *A = ExprPop(e);
13032 int rc = JIM_OK;
13034 switch (ExprBool(interp, A)) {
13035 case 0:
13036 ExprPush(e, Jim_NewIntObj(interp, 0));
13037 break;
13039 case 1:
13040 ExprPush(e, Jim_NewIntObj(interp, 1));
13041 break;
13043 case -1:
13045 rc = JIM_ERR;
13046 break;
13048 Jim_DecrRefCount(interp, A);
13050 return rc;
13053 static int JimExprOpTernaryLeft(Jim_Interp *interp, struct JimExprState *e)
13055 Jim_Obj *skip = ExprPop(e);
13056 Jim_Obj *A = ExprPop(e);
13057 int rc = JIM_OK;
13060 ExprPush(e, A);
13062 switch (ExprBool(interp, A)) {
13063 case 0:
13065 e->skip = JimWideValue(skip);
13067 ExprPush(e, Jim_NewIntObj(interp, 0));
13068 break;
13070 case 1:
13072 break;
13074 case -1:
13076 rc = JIM_ERR;
13077 break;
13079 Jim_DecrRefCount(interp, A);
13080 Jim_DecrRefCount(interp, skip);
13082 return rc;
13085 static int JimExprOpColonLeft(Jim_Interp *interp, struct JimExprState *e)
13087 Jim_Obj *skip = ExprPop(e);
13088 Jim_Obj *B = ExprPop(e);
13089 Jim_Obj *A = ExprPop(e);
13092 if (ExprBool(interp, A)) {
13094 e->skip = JimWideValue(skip);
13096 ExprPush(e, B);
13099 Jim_DecrRefCount(interp, skip);
13100 Jim_DecrRefCount(interp, A);
13101 Jim_DecrRefCount(interp, B);
13102 return JIM_OK;
13105 static int JimExprOpNull(Jim_Interp *interp, struct JimExprState *e)
13107 return JIM_OK;
13110 enum
13112 LAZY_NONE,
13113 LAZY_OP,
13114 LAZY_LEFT,
13115 LAZY_RIGHT
13118 #define OPRINIT(N, P, A, F) {N, F, P, A, LAZY_NONE, sizeof(N) - 1}
13119 #define OPRINIT_LAZY(N, P, A, F, L) {N, F, P, A, L, sizeof(N) - 1}
13121 static const struct Jim_ExprOperator Jim_ExprOperators[] = {
13122 OPRINIT("*", 110, 2, JimExprOpBin),
13123 OPRINIT("/", 110, 2, JimExprOpBin),
13124 OPRINIT("%", 110, 2, JimExprOpIntBin),
13126 OPRINIT("-", 100, 2, JimExprOpBin),
13127 OPRINIT("+", 100, 2, JimExprOpBin),
13129 OPRINIT("<<", 90, 2, JimExprOpIntBin),
13130 OPRINIT(">>", 90, 2, JimExprOpIntBin),
13132 OPRINIT("<<<", 90, 2, JimExprOpIntBin),
13133 OPRINIT(">>>", 90, 2, JimExprOpIntBin),
13135 OPRINIT("<", 80, 2, JimExprOpBin),
13136 OPRINIT(">", 80, 2, JimExprOpBin),
13137 OPRINIT("<=", 80, 2, JimExprOpBin),
13138 OPRINIT(">=", 80, 2, JimExprOpBin),
13140 OPRINIT("==", 70, 2, JimExprOpBin),
13141 OPRINIT("!=", 70, 2, JimExprOpBin),
13143 OPRINIT("&", 50, 2, JimExprOpIntBin),
13144 OPRINIT("^", 49, 2, JimExprOpIntBin),
13145 OPRINIT("|", 48, 2, JimExprOpIntBin),
13147 OPRINIT_LAZY("&&", 10, 2, NULL, LAZY_OP),
13148 OPRINIT_LAZY(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT),
13149 OPRINIT_LAZY(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT),
13151 OPRINIT_LAZY("||", 9, 2, NULL, LAZY_OP),
13152 OPRINIT_LAZY(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT),
13153 OPRINIT_LAZY(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT),
13155 OPRINIT_LAZY("?", 5, 2, JimExprOpNull, LAZY_OP),
13156 OPRINIT_LAZY(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT),
13157 OPRINIT_LAZY(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
13159 OPRINIT_LAZY(":", 5, 2, JimExprOpNull, LAZY_OP),
13160 OPRINIT_LAZY(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT),
13161 OPRINIT_LAZY(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
13163 OPRINIT("**", 250, 2, JimExprOpBin),
13165 OPRINIT("eq", 60, 2, JimExprOpStrBin),
13166 OPRINIT("ne", 60, 2, JimExprOpStrBin),
13168 OPRINIT("in", 55, 2, JimExprOpStrBin),
13169 OPRINIT("ni", 55, 2, JimExprOpStrBin),
13171 OPRINIT("!", 150, 1, JimExprOpNumUnary),
13172 OPRINIT("~", 150, 1, JimExprOpIntUnary),
13173 OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
13174 OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
13178 OPRINIT("int", 200, 1, JimExprOpNumUnary),
13179 OPRINIT("wide", 200, 1, JimExprOpNumUnary),
13180 OPRINIT("abs", 200, 1, JimExprOpNumUnary),
13181 OPRINIT("double", 200, 1, JimExprOpNumUnary),
13182 OPRINIT("round", 200, 1, JimExprOpNumUnary),
13183 OPRINIT("rand", 200, 0, JimExprOpNone),
13184 OPRINIT("srand", 200, 1, JimExprOpIntUnary),
13186 #ifdef JIM_MATH_FUNCTIONS
13187 OPRINIT("sin", 200, 1, JimExprOpDoubleUnary),
13188 OPRINIT("cos", 200, 1, JimExprOpDoubleUnary),
13189 OPRINIT("tan", 200, 1, JimExprOpDoubleUnary),
13190 OPRINIT("asin", 200, 1, JimExprOpDoubleUnary),
13191 OPRINIT("acos", 200, 1, JimExprOpDoubleUnary),
13192 OPRINIT("atan", 200, 1, JimExprOpDoubleUnary),
13193 OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary),
13194 OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary),
13195 OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary),
13196 OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary),
13197 OPRINIT("floor", 200, 1, JimExprOpDoubleUnary),
13198 OPRINIT("exp", 200, 1, JimExprOpDoubleUnary),
13199 OPRINIT("log", 200, 1, JimExprOpDoubleUnary),
13200 OPRINIT("log10", 200, 1, JimExprOpDoubleUnary),
13201 OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary),
13202 OPRINIT("pow", 200, 2, JimExprOpBin),
13203 #endif
13205 #undef OPRINIT
13206 #undef OPRINIT_LAZY
13208 #define JIM_EXPR_OPERATORS_NUM \
13209 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
13211 static int JimParseExpression(struct JimParserCtx *pc)
13214 while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
13215 if (*pc->p == '\n') {
13216 pc->linenr++;
13218 pc->p++;
13219 pc->len--;
13223 pc->tline = pc->linenr;
13224 pc->tstart = pc->p;
13226 if (pc->len == 0) {
13227 pc->tend = pc->p;
13228 pc->tt = JIM_TT_EOL;
13229 pc->eof = 1;
13230 return JIM_OK;
13232 switch (*(pc->p)) {
13233 case '(':
13234 pc->tt = JIM_TT_SUBEXPR_START;
13235 goto singlechar;
13236 case ')':
13237 pc->tt = JIM_TT_SUBEXPR_END;
13238 goto singlechar;
13239 case ',':
13240 pc->tt = JIM_TT_SUBEXPR_COMMA;
13241 singlechar:
13242 pc->tend = pc->p;
13243 pc->p++;
13244 pc->len--;
13245 break;
13246 case '[':
13247 return JimParseCmd(pc);
13248 case '$':
13249 if (JimParseVar(pc) == JIM_ERR)
13250 return JimParseExprOperator(pc);
13251 else {
13253 if (pc->tt == JIM_TT_EXPRSUGAR) {
13254 return JIM_ERR;
13256 return JIM_OK;
13258 break;
13259 case '0':
13260 case '1':
13261 case '2':
13262 case '3':
13263 case '4':
13264 case '5':
13265 case '6':
13266 case '7':
13267 case '8':
13268 case '9':
13269 case '.':
13270 return JimParseExprNumber(pc);
13271 case '"':
13272 return JimParseQuote(pc);
13273 case '{':
13274 return JimParseBrace(pc);
13276 case 'N':
13277 case 'I':
13278 case 'n':
13279 case 'i':
13280 if (JimParseExprIrrational(pc) == JIM_ERR)
13281 return JimParseExprOperator(pc);
13282 break;
13283 default:
13284 return JimParseExprOperator(pc);
13285 break;
13287 return JIM_OK;
13290 static int JimParseExprNumber(struct JimParserCtx *pc)
13292 char *end;
13295 pc->tt = JIM_TT_EXPR_INT;
13297 jim_strtoull(pc->p, (char **)&pc->p);
13299 if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
13300 if (strtod(pc->tstart, &end)) { }
13301 if (end == pc->tstart)
13302 return JIM_ERR;
13303 if (end > pc->p) {
13305 pc->tt = JIM_TT_EXPR_DOUBLE;
13306 pc->p = end;
13309 pc->tend = pc->p - 1;
13310 pc->len -= (pc->p - pc->tstart);
13311 return JIM_OK;
13314 static int JimParseExprIrrational(struct JimParserCtx *pc)
13316 const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
13317 int i;
13319 for (i = 0; irrationals[i]; i++) {
13320 const char *irr = irrationals[i];
13322 if (strncmp(irr, pc->p, 3) == 0) {
13323 pc->p += 3;
13324 pc->len -= 3;
13325 pc->tend = pc->p - 1;
13326 pc->tt = JIM_TT_EXPR_DOUBLE;
13327 return JIM_OK;
13330 return JIM_ERR;
13333 static int JimParseExprOperator(struct JimParserCtx *pc)
13335 int i;
13336 int bestIdx = -1, bestLen = 0;
13339 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
13340 const char * const opname = Jim_ExprOperators[i].name;
13341 const int oplen = Jim_ExprOperators[i].namelen;
13343 if (opname == NULL || opname[0] != pc->p[0]) {
13344 continue;
13347 if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) {
13348 bestIdx = i + JIM_TT_EXPR_OP;
13349 bestLen = oplen;
13352 if (bestIdx == -1) {
13353 return JIM_ERR;
13357 if (bestIdx >= JIM_EXPROP_FUNC_FIRST) {
13358 const char *p = pc->p + bestLen;
13359 int len = pc->len - bestLen;
13361 while (len && isspace(UCHAR(*p))) {
13362 len--;
13363 p++;
13365 if (*p != '(') {
13366 return JIM_ERR;
13369 pc->tend = pc->p + bestLen - 1;
13370 pc->p += bestLen;
13371 pc->len -= bestLen;
13373 pc->tt = bestIdx;
13374 return JIM_OK;
13377 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
13379 static Jim_ExprOperator dummy_op;
13380 if (opcode < JIM_TT_EXPR_OP) {
13381 return &dummy_op;
13383 return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
13386 const char *jim_tt_name(int type)
13388 static const char * const tt_names[JIM_TT_EXPR_OP] =
13389 { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT",
13390 "DBL", "$()" };
13391 if (type < JIM_TT_EXPR_OP) {
13392 return tt_names[type];
13394 else {
13395 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(type);
13396 static char buf[20];
13398 if (op->name) {
13399 return op->name;
13401 sprintf(buf, "(%d)", type);
13402 return buf;
13406 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
13407 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
13408 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13410 static const Jim_ObjType exprObjType = {
13411 "expression",
13412 FreeExprInternalRep,
13413 DupExprInternalRep,
13414 NULL,
13415 JIM_TYPE_REFERENCES,
13419 typedef struct ExprByteCode
13421 ScriptToken *token;
13422 int len;
13423 int inUse;
13424 } ExprByteCode;
13426 static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr)
13428 int i;
13430 for (i = 0; i < expr->len; i++) {
13431 Jim_DecrRefCount(interp, expr->token[i].objPtr);
13433 Jim_Free(expr->token);
13434 Jim_Free(expr);
13437 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
13439 ExprByteCode *expr = (void *)objPtr->internalRep.ptr;
13441 if (expr) {
13442 if (--expr->inUse != 0) {
13443 return;
13446 ExprFreeByteCode(interp, expr);
13450 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
13452 JIM_NOTUSED(interp);
13453 JIM_NOTUSED(srcPtr);
13456 dupPtr->typePtr = NULL;
13460 static int ExprCheckCorrectness(ExprByteCode * expr)
13462 int i;
13463 int stacklen = 0;
13464 int ternary = 0;
13466 for (i = 0; i < expr->len; i++) {
13467 ScriptToken *t = &expr->token[i];
13468 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13470 stacklen -= op->arity;
13471 if (stacklen < 0) {
13472 break;
13474 if (t->type == JIM_EXPROP_TERNARY || t->type == JIM_EXPROP_TERNARY_LEFT) {
13475 ternary++;
13477 else if (t->type == JIM_EXPROP_COLON || t->type == JIM_EXPROP_COLON_LEFT) {
13478 ternary--;
13482 stacklen++;
13484 if (stacklen != 1 || ternary != 0) {
13485 return JIM_ERR;
13487 return JIM_OK;
13490 static int ExprAddLazyOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
13492 int i;
13494 int leftindex, arity, offset;
13497 leftindex = expr->len - 1;
13499 arity = 1;
13500 while (arity) {
13501 ScriptToken *tt = &expr->token[leftindex];
13503 if (tt->type >= JIM_TT_EXPR_OP) {
13504 arity += JimExprOperatorInfoByOpcode(tt->type)->arity;
13506 arity--;
13507 if (--leftindex < 0) {
13508 return JIM_ERR;
13511 leftindex++;
13514 memmove(&expr->token[leftindex + 2], &expr->token[leftindex],
13515 sizeof(*expr->token) * (expr->len - leftindex));
13516 expr->len += 2;
13517 offset = (expr->len - leftindex) - 1;
13519 expr->token[leftindex + 1].type = t->type + 1;
13520 expr->token[leftindex + 1].objPtr = interp->emptyObj;
13522 expr->token[leftindex].type = JIM_TT_EXPR_INT;
13523 expr->token[leftindex].objPtr = Jim_NewIntObj(interp, offset);
13526 expr->token[expr->len].objPtr = interp->emptyObj;
13527 expr->token[expr->len].type = t->type + 2;
13528 expr->len++;
13531 for (i = leftindex - 1; i > 0; i--) {
13532 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(expr->token[i].type);
13533 if (op->lazy == LAZY_LEFT) {
13534 if (JimWideValue(expr->token[i - 1].objPtr) + i - 1 >= leftindex) {
13535 JimWideValue(expr->token[i - 1].objPtr) += 2;
13539 return JIM_OK;
13542 static int ExprAddOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
13544 struct ScriptToken *token = &expr->token[expr->len];
13545 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13547 if (op->lazy == LAZY_OP) {
13548 if (ExprAddLazyOperator(interp, expr, t) != JIM_OK) {
13549 Jim_SetResultFormatted(interp, "Expression has bad operands to %s", op->name);
13550 return JIM_ERR;
13553 else {
13554 token->objPtr = interp->emptyObj;
13555 token->type = t->type;
13556 expr->len++;
13558 return JIM_OK;
13561 static int ExprTernaryGetColonLeftIndex(ExprByteCode *expr, int right_index)
13563 int ternary_count = 1;
13565 right_index--;
13567 while (right_index > 1) {
13568 if (expr->token[right_index].type == JIM_EXPROP_TERNARY_LEFT) {
13569 ternary_count--;
13571 else if (expr->token[right_index].type == JIM_EXPROP_COLON_RIGHT) {
13572 ternary_count++;
13574 else if (expr->token[right_index].type == JIM_EXPROP_COLON_LEFT && ternary_count == 1) {
13575 return right_index;
13577 right_index--;
13581 return -1;
13584 static int ExprTernaryGetMoveIndices(ExprByteCode *expr, int right_index, int *prev_right_index, int *prev_left_index)
13586 int i = right_index - 1;
13587 int ternary_count = 1;
13589 while (i > 1) {
13590 if (expr->token[i].type == JIM_EXPROP_TERNARY_LEFT) {
13591 if (--ternary_count == 0 && expr->token[i - 2].type == JIM_EXPROP_COLON_RIGHT) {
13592 *prev_right_index = i - 2;
13593 *prev_left_index = ExprTernaryGetColonLeftIndex(expr, *prev_right_index);
13594 return 1;
13597 else if (expr->token[i].type == JIM_EXPROP_COLON_RIGHT) {
13598 if (ternary_count == 0) {
13599 return 0;
13601 ternary_count++;
13603 i--;
13605 return 0;
13608 static void ExprTernaryReorderExpression(Jim_Interp *interp, ExprByteCode *expr)
13610 int i;
13612 for (i = expr->len - 1; i > 1; i--) {
13613 int prev_right_index;
13614 int prev_left_index;
13615 int j;
13616 ScriptToken tmp;
13618 if (expr->token[i].type != JIM_EXPROP_COLON_RIGHT) {
13619 continue;
13623 if (ExprTernaryGetMoveIndices(expr, i, &prev_right_index, &prev_left_index) == 0) {
13624 continue;
13627 tmp = expr->token[prev_right_index];
13628 for (j = prev_right_index; j < i; j++) {
13629 expr->token[j] = expr->token[j + 1];
13631 expr->token[i] = tmp;
13633 JimWideValue(expr->token[prev_left_index-1].objPtr) += (i - prev_right_index);
13636 i++;
13640 static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *fileNameObj)
13642 Jim_Stack stack;
13643 ExprByteCode *expr;
13644 int ok = 1;
13645 int i;
13646 int prevtt = JIM_TT_NONE;
13647 int have_ternary = 0;
13650 int count = tokenlist->count - 1;
13652 expr = Jim_Alloc(sizeof(*expr));
13653 expr->inUse = 1;
13654 expr->len = 0;
13656 Jim_InitStack(&stack);
13658 for (i = 0; i < tokenlist->count; i++) {
13659 ParseToken *t = &tokenlist->list[i];
13660 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13662 if (op->lazy == LAZY_OP) {
13663 count += 2;
13665 if (t->type == JIM_EXPROP_TERNARY) {
13666 have_ternary = 1;
13671 expr->token = Jim_Alloc(sizeof(ScriptToken) * count);
13673 for (i = 0; i < tokenlist->count && ok; i++) {
13674 ParseToken *t = &tokenlist->list[i];
13677 struct ScriptToken *token = &expr->token[expr->len];
13679 if (t->type == JIM_TT_EOL) {
13680 break;
13683 switch (t->type) {
13684 case JIM_TT_STR:
13685 case JIM_TT_ESC:
13686 case JIM_TT_VAR:
13687 case JIM_TT_DICTSUGAR:
13688 case JIM_TT_EXPRSUGAR:
13689 case JIM_TT_CMD:
13690 token->type = t->type;
13691 strexpr:
13692 token->objPtr = Jim_NewStringObj(interp, t->token, t->len);
13693 if (t->type == JIM_TT_CMD) {
13695 JimSetSourceInfo(interp, token->objPtr, fileNameObj, t->line);
13697 expr->len++;
13698 break;
13700 case JIM_TT_EXPR_INT:
13701 case JIM_TT_EXPR_DOUBLE:
13703 char *endptr;
13704 if (t->type == JIM_TT_EXPR_INT) {
13705 token->objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
13707 else {
13708 token->objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
13710 if (endptr != t->token + t->len) {
13712 Jim_FreeNewObj(interp, token->objPtr);
13713 token->type = JIM_TT_STR;
13714 goto strexpr;
13716 token->type = t->type;
13717 expr->len++;
13719 break;
13721 case JIM_TT_SUBEXPR_START:
13722 Jim_StackPush(&stack, t);
13723 prevtt = JIM_TT_NONE;
13724 continue;
13726 case JIM_TT_SUBEXPR_COMMA:
13728 continue;
13730 case JIM_TT_SUBEXPR_END:
13731 ok = 0;
13732 while (Jim_StackLen(&stack)) {
13733 ParseToken *tt = Jim_StackPop(&stack);
13735 if (tt->type == JIM_TT_SUBEXPR_START) {
13736 ok = 1;
13737 break;
13740 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13741 goto err;
13744 if (!ok) {
13745 Jim_SetResultString(interp, "Unexpected close parenthesis", -1);
13746 goto err;
13748 break;
13751 default:{
13753 const struct Jim_ExprOperator *op;
13754 ParseToken *tt;
13757 if (prevtt == JIM_TT_NONE || prevtt >= JIM_TT_EXPR_OP) {
13758 if (t->type == JIM_EXPROP_SUB) {
13759 t->type = JIM_EXPROP_UNARYMINUS;
13761 else if (t->type == JIM_EXPROP_ADD) {
13762 t->type = JIM_EXPROP_UNARYPLUS;
13766 op = JimExprOperatorInfoByOpcode(t->type);
13769 while ((tt = Jim_StackPeek(&stack)) != NULL) {
13770 const struct Jim_ExprOperator *tt_op =
13771 JimExprOperatorInfoByOpcode(tt->type);
13775 if (op->arity != 1 && tt_op->precedence >= op->precedence) {
13776 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13777 ok = 0;
13778 goto err;
13780 Jim_StackPop(&stack);
13782 else {
13783 break;
13786 Jim_StackPush(&stack, t);
13787 break;
13790 prevtt = t->type;
13794 while (Jim_StackLen(&stack)) {
13795 ParseToken *tt = Jim_StackPop(&stack);
13797 if (tt->type == JIM_TT_SUBEXPR_START) {
13798 ok = 0;
13799 Jim_SetResultString(interp, "Missing close parenthesis", -1);
13800 goto err;
13802 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13803 ok = 0;
13804 goto err;
13808 if (have_ternary) {
13809 ExprTernaryReorderExpression(interp, expr);
13812 err:
13814 Jim_FreeStack(&stack);
13816 for (i = 0; i < expr->len; i++) {
13817 Jim_IncrRefCount(expr->token[i].objPtr);
13820 if (!ok) {
13821 ExprFreeByteCode(interp, expr);
13822 return NULL;
13825 return expr;
13829 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
13831 int exprTextLen;
13832 const char *exprText;
13833 struct JimParserCtx parser;
13834 struct ExprByteCode *expr;
13835 ParseTokenList tokenlist;
13836 int line;
13837 Jim_Obj *fileNameObj;
13838 int rc = JIM_ERR;
13841 if (objPtr->typePtr == &sourceObjType) {
13842 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
13843 line = objPtr->internalRep.sourceValue.lineNumber;
13845 else {
13846 fileNameObj = interp->emptyObj;
13847 line = 1;
13849 Jim_IncrRefCount(fileNameObj);
13851 exprText = Jim_GetString(objPtr, &exprTextLen);
13854 ScriptTokenListInit(&tokenlist);
13856 JimParserInit(&parser, exprText, exprTextLen, line);
13857 while (!parser.eof) {
13858 if (JimParseExpression(&parser) != JIM_OK) {
13859 ScriptTokenListFree(&tokenlist);
13860 invalidexpr:
13861 Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
13862 expr = NULL;
13863 goto err;
13866 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
13867 parser.tline);
13870 #ifdef DEBUG_SHOW_EXPR_TOKENS
13872 int i;
13873 printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
13874 for (i = 0; i < tokenlist.count; i++) {
13875 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
13876 tokenlist.list[i].len, tokenlist.list[i].token);
13879 #endif
13881 if (JimParseCheckMissing(interp, parser.missing.ch) == JIM_ERR) {
13882 ScriptTokenListFree(&tokenlist);
13883 Jim_DecrRefCount(interp, fileNameObj);
13884 return JIM_ERR;
13888 expr = ExprCreateByteCode(interp, &tokenlist, fileNameObj);
13891 ScriptTokenListFree(&tokenlist);
13893 if (!expr) {
13894 goto err;
13897 #ifdef DEBUG_SHOW_EXPR
13899 int i;
13901 printf("==== Expr ====\n");
13902 for (i = 0; i < expr->len; i++) {
13903 ScriptToken *t = &expr->token[i];
13905 printf("[%2d] %s '%s'\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
13908 #endif
13911 if (ExprCheckCorrectness(expr) != JIM_OK) {
13912 ExprFreeByteCode(interp, expr);
13913 goto invalidexpr;
13916 rc = JIM_OK;
13918 err:
13920 Jim_DecrRefCount(interp, fileNameObj);
13921 Jim_FreeIntRep(interp, objPtr);
13922 Jim_SetIntRepPtr(objPtr, expr);
13923 objPtr->typePtr = &exprObjType;
13924 return rc;
13927 static ExprByteCode *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
13929 if (objPtr->typePtr != &exprObjType) {
13930 if (SetExprFromAny(interp, objPtr) != JIM_OK) {
13931 return NULL;
13934 return (ExprByteCode *) Jim_GetIntRepPtr(objPtr);
13937 #ifdef JIM_OPTIMIZATION
13938 static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, const ScriptToken *token)
13940 if (token->type == JIM_TT_EXPR_INT)
13941 return token->objPtr;
13942 else if (token->type == JIM_TT_VAR)
13943 return Jim_GetVariable(interp, token->objPtr, JIM_NONE);
13944 else if (token->type == JIM_TT_DICTSUGAR)
13945 return JimExpandDictSugar(interp, token->objPtr);
13946 else
13947 return NULL;
13949 #endif
13951 #define JIM_EE_STATICSTACK_LEN 10
13953 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr)
13955 ExprByteCode *expr;
13956 Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN];
13957 int i;
13958 int retcode = JIM_OK;
13959 struct JimExprState e;
13961 expr = JimGetExpression(interp, exprObjPtr);
13962 if (!expr) {
13963 return JIM_ERR;
13966 #ifdef JIM_OPTIMIZATION
13968 Jim_Obj *objPtr;
13971 switch (expr->len) {
13972 case 1:
13973 objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
13974 if (objPtr) {
13975 Jim_IncrRefCount(objPtr);
13976 *exprResultPtrPtr = objPtr;
13977 return JIM_OK;
13979 break;
13981 case 2:
13982 if (expr->token[1].type == JIM_EXPROP_NOT) {
13983 objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
13985 if (objPtr && JimIsWide(objPtr)) {
13986 *exprResultPtrPtr = JimWideValue(objPtr) ? interp->falseObj : interp->trueObj;
13987 Jim_IncrRefCount(*exprResultPtrPtr);
13988 return JIM_OK;
13991 break;
13993 case 3:
13994 objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
13995 if (objPtr && JimIsWide(objPtr)) {
13996 Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, &expr->token[1]);
13997 if (objPtr2 && JimIsWide(objPtr2)) {
13998 jim_wide wideValueA = JimWideValue(objPtr);
13999 jim_wide wideValueB = JimWideValue(objPtr2);
14000 int cmpRes;
14001 switch (expr->token[2].type) {
14002 case JIM_EXPROP_LT:
14003 cmpRes = wideValueA < wideValueB;
14004 break;
14005 case JIM_EXPROP_LTE:
14006 cmpRes = wideValueA <= wideValueB;
14007 break;
14008 case JIM_EXPROP_GT:
14009 cmpRes = wideValueA > wideValueB;
14010 break;
14011 case JIM_EXPROP_GTE:
14012 cmpRes = wideValueA >= wideValueB;
14013 break;
14014 case JIM_EXPROP_NUMEQ:
14015 cmpRes = wideValueA == wideValueB;
14016 break;
14017 case JIM_EXPROP_NUMNE:
14018 cmpRes = wideValueA != wideValueB;
14019 break;
14020 default:
14021 goto noopt;
14023 *exprResultPtrPtr = cmpRes ? interp->trueObj : interp->falseObj;
14024 Jim_IncrRefCount(*exprResultPtrPtr);
14025 return JIM_OK;
14028 break;
14031 noopt:
14032 #endif
14034 expr->inUse++;
14038 if (expr->len > JIM_EE_STATICSTACK_LEN)
14039 e.stack = Jim_Alloc(sizeof(Jim_Obj *) * expr->len);
14040 else
14041 e.stack = staticStack;
14043 e.stacklen = 0;
14046 for (i = 0; i < expr->len && retcode == JIM_OK; i++) {
14047 Jim_Obj *objPtr;
14049 switch (expr->token[i].type) {
14050 case JIM_TT_EXPR_INT:
14051 case JIM_TT_EXPR_DOUBLE:
14052 case JIM_TT_STR:
14053 ExprPush(&e, expr->token[i].objPtr);
14054 break;
14056 case JIM_TT_VAR:
14057 objPtr = Jim_GetVariable(interp, expr->token[i].objPtr, JIM_ERRMSG);
14058 if (objPtr) {
14059 ExprPush(&e, objPtr);
14061 else {
14062 retcode = JIM_ERR;
14064 break;
14066 case JIM_TT_DICTSUGAR:
14067 objPtr = JimExpandDictSugar(interp, expr->token[i].objPtr);
14068 if (objPtr) {
14069 ExprPush(&e, objPtr);
14071 else {
14072 retcode = JIM_ERR;
14074 break;
14076 case JIM_TT_ESC:
14077 retcode = Jim_SubstObj(interp, expr->token[i].objPtr, &objPtr, JIM_NONE);
14078 if (retcode == JIM_OK) {
14079 ExprPush(&e, objPtr);
14081 break;
14083 case JIM_TT_CMD:
14084 retcode = Jim_EvalObj(interp, expr->token[i].objPtr);
14085 if (retcode == JIM_OK) {
14086 ExprPush(&e, Jim_GetResult(interp));
14088 break;
14090 default:{
14092 e.skip = 0;
14093 e.opcode = expr->token[i].type;
14095 retcode = JimExprOperatorInfoByOpcode(e.opcode)->funcop(interp, &e);
14097 i += e.skip;
14098 continue;
14103 expr->inUse--;
14105 if (retcode == JIM_OK) {
14106 *exprResultPtrPtr = ExprPop(&e);
14108 else {
14109 for (i = 0; i < e.stacklen; i++) {
14110 Jim_DecrRefCount(interp, e.stack[i]);
14113 if (e.stack != staticStack) {
14114 Jim_Free(e.stack);
14116 return retcode;
14119 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
14121 int retcode;
14122 jim_wide wideValue;
14123 double doubleValue;
14124 Jim_Obj *exprResultPtr;
14126 retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
14127 if (retcode != JIM_OK)
14128 return retcode;
14130 if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
14131 if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK) {
14132 Jim_DecrRefCount(interp, exprResultPtr);
14133 return JIM_ERR;
14135 else {
14136 Jim_DecrRefCount(interp, exprResultPtr);
14137 *boolPtr = doubleValue != 0;
14138 return JIM_OK;
14141 *boolPtr = wideValue != 0;
14143 Jim_DecrRefCount(interp, exprResultPtr);
14144 return JIM_OK;
14150 typedef struct ScanFmtPartDescr
14152 char *arg;
14153 char *prefix;
14154 size_t width;
14155 int pos;
14156 char type;
14157 char modifier;
14158 } ScanFmtPartDescr;
14161 typedef struct ScanFmtStringObj
14163 jim_wide size;
14164 char *stringRep;
14165 size_t count;
14166 size_t convCount;
14167 size_t maxPos;
14168 const char *error;
14169 char *scratch;
14170 ScanFmtPartDescr descr[1];
14171 } ScanFmtStringObj;
14174 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
14175 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
14176 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
14178 static const Jim_ObjType scanFmtStringObjType = {
14179 "scanformatstring",
14180 FreeScanFmtInternalRep,
14181 DupScanFmtInternalRep,
14182 UpdateStringOfScanFmt,
14183 JIM_TYPE_NONE,
14186 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
14188 JIM_NOTUSED(interp);
14189 Jim_Free((char *)objPtr->internalRep.ptr);
14190 objPtr->internalRep.ptr = 0;
14193 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
14195 size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
14196 ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
14198 JIM_NOTUSED(interp);
14199 memcpy(newVec, srcPtr->internalRep.ptr, size);
14200 dupPtr->internalRep.ptr = newVec;
14201 dupPtr->typePtr = &scanFmtStringObjType;
14204 static void UpdateStringOfScanFmt(Jim_Obj *objPtr)
14206 JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep);
14210 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
14212 ScanFmtStringObj *fmtObj;
14213 char *buffer;
14214 int maxCount, i, approxSize, lastPos = -1;
14215 const char *fmt = objPtr->bytes;
14216 int maxFmtLen = objPtr->length;
14217 const char *fmtEnd = fmt + maxFmtLen;
14218 int curr;
14220 Jim_FreeIntRep(interp, objPtr);
14222 for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
14223 if (fmt[i] == '%')
14224 ++maxCount;
14226 approxSize = sizeof(ScanFmtStringObj)
14227 +(maxCount + 1) * sizeof(ScanFmtPartDescr)
14228 +maxFmtLen * sizeof(char) + 3 + 1
14229 + maxFmtLen * sizeof(char) + 1
14230 + maxFmtLen * sizeof(char)
14231 +(maxCount + 1) * sizeof(char)
14232 +1;
14233 fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
14234 memset(fmtObj, 0, approxSize);
14235 fmtObj->size = approxSize;
14236 fmtObj->maxPos = 0;
14237 fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
14238 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
14239 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
14240 buffer = fmtObj->stringRep + maxFmtLen + 1;
14241 objPtr->internalRep.ptr = fmtObj;
14242 objPtr->typePtr = &scanFmtStringObjType;
14243 for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
14244 int width = 0, skip;
14245 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
14247 fmtObj->count++;
14248 descr->width = 0;
14250 if (*fmt != '%' || fmt[1] == '%') {
14251 descr->type = 0;
14252 descr->prefix = &buffer[i];
14253 for (; fmt < fmtEnd; ++fmt) {
14254 if (*fmt == '%') {
14255 if (fmt[1] != '%')
14256 break;
14257 ++fmt;
14259 buffer[i++] = *fmt;
14261 buffer[i++] = 0;
14264 ++fmt;
14266 if (fmt >= fmtEnd)
14267 goto done;
14268 descr->pos = 0;
14269 if (*fmt == '*') {
14270 descr->pos = -1;
14271 ++fmt;
14273 else
14274 fmtObj->convCount++;
14276 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14277 fmt += skip;
14279 if (descr->pos != -1 && *fmt == '$') {
14280 int prev;
14282 ++fmt;
14283 descr->pos = width;
14284 width = 0;
14286 if ((lastPos == 0 && descr->pos > 0)
14287 || (lastPos > 0 && descr->pos == 0)) {
14288 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
14289 return JIM_ERR;
14292 for (prev = 0; prev < curr; ++prev) {
14293 if (fmtObj->descr[prev].pos == -1)
14294 continue;
14295 if (fmtObj->descr[prev].pos == descr->pos) {
14296 fmtObj->error =
14297 "variable is assigned by multiple \"%n$\" conversion specifiers";
14298 return JIM_ERR;
14302 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14303 descr->width = width;
14304 fmt += skip;
14306 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
14307 fmtObj->maxPos = descr->pos;
14309 else {
14311 descr->width = width;
14315 if (lastPos == -1)
14316 lastPos = descr->pos;
14318 if (*fmt == '[') {
14319 int swapped = 1, beg = i, end, j;
14321 descr->type = '[';
14322 descr->arg = &buffer[i];
14323 ++fmt;
14324 if (*fmt == '^')
14325 buffer[i++] = *fmt++;
14326 if (*fmt == ']')
14327 buffer[i++] = *fmt++;
14328 while (*fmt && *fmt != ']')
14329 buffer[i++] = *fmt++;
14330 if (*fmt != ']') {
14331 fmtObj->error = "unmatched [ in format string";
14332 return JIM_ERR;
14334 end = i;
14335 buffer[i++] = 0;
14337 while (swapped) {
14338 swapped = 0;
14339 for (j = beg + 1; j < end - 1; ++j) {
14340 if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
14341 char tmp = buffer[j - 1];
14343 buffer[j - 1] = buffer[j + 1];
14344 buffer[j + 1] = tmp;
14345 swapped = 1;
14350 else {
14352 if (strchr("hlL", *fmt) != 0)
14353 descr->modifier = tolower((int)*fmt++);
14355 descr->type = *fmt;
14356 if (strchr("efgcsndoxui", *fmt) == 0) {
14357 fmtObj->error = "bad scan conversion character";
14358 return JIM_ERR;
14360 else if (*fmt == 'c' && descr->width != 0) {
14361 fmtObj->error = "field width may not be specified in %c " "conversion";
14362 return JIM_ERR;
14364 else if (*fmt == 'u' && descr->modifier == 'l') {
14365 fmtObj->error = "unsigned wide not supported";
14366 return JIM_ERR;
14369 curr++;
14371 done:
14372 return JIM_OK;
14377 #define FormatGetCnvCount(_fo_) \
14378 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
14379 #define FormatGetMaxPos(_fo_) \
14380 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
14381 #define FormatGetError(_fo_) \
14382 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
14384 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
14386 char *buffer = Jim_StrDup(str);
14387 char *p = buffer;
14389 while (*str) {
14390 int c;
14391 int n;
14393 if (!sdescr && isspace(UCHAR(*str)))
14394 break;
14396 n = utf8_tounicode(str, &c);
14397 if (sdescr && !JimCharsetMatch(sdescr, c, JIM_CHARSET_SCAN))
14398 break;
14399 while (n--)
14400 *p++ = *str++;
14402 *p = 0;
14403 return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
14407 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int strLen,
14408 ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
14410 const char *tok;
14411 const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
14412 size_t scanned = 0;
14413 size_t anchor = pos;
14414 int i;
14415 Jim_Obj *tmpObj = NULL;
14418 *valObjPtr = 0;
14419 if (descr->prefix) {
14420 for (i = 0; pos < strLen && descr->prefix[i]; ++i) {
14422 if (isspace(UCHAR(descr->prefix[i])))
14423 while (pos < strLen && isspace(UCHAR(str[pos])))
14424 ++pos;
14425 else if (descr->prefix[i] != str[pos])
14426 break;
14427 else
14428 ++pos;
14430 if (pos >= strLen) {
14431 return -1;
14433 else if (descr->prefix[i] != 0)
14434 return 0;
14437 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
14438 while (isspace(UCHAR(str[pos])))
14439 ++pos;
14441 scanned = pos - anchor;
14444 if (descr->type == 'n') {
14446 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
14448 else if (pos >= strLen) {
14450 return -1;
14452 else if (descr->type == 'c') {
14453 int c;
14454 scanned += utf8_tounicode(&str[pos], &c);
14455 *valObjPtr = Jim_NewIntObj(interp, c);
14456 return scanned;
14458 else {
14460 if (descr->width > 0) {
14461 size_t sLen = utf8_strlen(&str[pos], strLen - pos);
14462 size_t tLen = descr->width > sLen ? sLen : descr->width;
14464 tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
14465 tok = tmpObj->bytes;
14467 else {
14469 tok = &str[pos];
14471 switch (descr->type) {
14472 case 'd':
14473 case 'o':
14474 case 'x':
14475 case 'u':
14476 case 'i':{
14477 char *endp;
14478 jim_wide w;
14480 int base = descr->type == 'o' ? 8
14481 : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
14484 if (base == 0) {
14485 w = jim_strtoull(tok, &endp);
14487 else {
14488 w = strtoull(tok, &endp, base);
14491 if (endp != tok) {
14493 *valObjPtr = Jim_NewIntObj(interp, w);
14496 scanned += endp - tok;
14498 else {
14499 scanned = *tok ? 0 : -1;
14501 break;
14503 case 's':
14504 case '[':{
14505 *valObjPtr = JimScanAString(interp, descr->arg, tok);
14506 scanned += Jim_Length(*valObjPtr);
14507 break;
14509 case 'e':
14510 case 'f':
14511 case 'g':{
14512 char *endp;
14513 double value = strtod(tok, &endp);
14515 if (endp != tok) {
14517 *valObjPtr = Jim_NewDoubleObj(interp, value);
14519 scanned += endp - tok;
14521 else {
14522 scanned = *tok ? 0 : -1;
14524 break;
14527 if (tmpObj) {
14528 Jim_FreeNewObj(interp, tmpObj);
14531 return scanned;
14535 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
14537 size_t i, pos;
14538 int scanned = 1;
14539 const char *str = Jim_String(strObjPtr);
14540 int strLen = Jim_Utf8Length(interp, strObjPtr);
14541 Jim_Obj *resultList = 0;
14542 Jim_Obj **resultVec = 0;
14543 int resultc;
14544 Jim_Obj *emptyStr = 0;
14545 ScanFmtStringObj *fmtObj;
14548 JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
14550 fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
14552 if (fmtObj->error != 0) {
14553 if (flags & JIM_ERRMSG)
14554 Jim_SetResultString(interp, fmtObj->error, -1);
14555 return 0;
14558 emptyStr = Jim_NewEmptyStringObj(interp);
14559 Jim_IncrRefCount(emptyStr);
14561 resultList = Jim_NewListObj(interp, NULL, 0);
14562 if (fmtObj->maxPos > 0) {
14563 for (i = 0; i < fmtObj->maxPos; ++i)
14564 Jim_ListAppendElement(interp, resultList, emptyStr);
14565 JimListGetElements(interp, resultList, &resultc, &resultVec);
14568 for (i = 0, pos = 0; i < fmtObj->count; ++i) {
14569 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
14570 Jim_Obj *value = 0;
14573 if (descr->type == 0)
14574 continue;
14576 if (scanned > 0)
14577 scanned = ScanOneEntry(interp, str, pos, strLen, fmtObj, i, &value);
14579 if (scanned == -1 && i == 0)
14580 goto eof;
14582 pos += scanned;
14585 if (value == 0)
14586 value = Jim_NewEmptyStringObj(interp);
14588 if (descr->pos == -1) {
14589 Jim_FreeNewObj(interp, value);
14591 else if (descr->pos == 0)
14593 Jim_ListAppendElement(interp, resultList, value);
14594 else if (resultVec[descr->pos - 1] == emptyStr) {
14596 Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
14597 Jim_IncrRefCount(value);
14598 resultVec[descr->pos - 1] = value;
14600 else {
14602 Jim_FreeNewObj(interp, value);
14603 goto err;
14606 Jim_DecrRefCount(interp, emptyStr);
14607 return resultList;
14608 eof:
14609 Jim_DecrRefCount(interp, emptyStr);
14610 Jim_FreeNewObj(interp, resultList);
14611 return (Jim_Obj *)EOF;
14612 err:
14613 Jim_DecrRefCount(interp, emptyStr);
14614 Jim_FreeNewObj(interp, resultList);
14615 return 0;
14619 static void JimPrngInit(Jim_Interp *interp)
14621 #define PRNG_SEED_SIZE 256
14622 int i;
14623 unsigned int *seed;
14624 time_t t = time(NULL);
14626 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
14628 seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
14629 for (i = 0; i < PRNG_SEED_SIZE; i++) {
14630 seed[i] = (rand() ^ t ^ clock());
14632 JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
14633 Jim_Free(seed);
14637 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
14639 Jim_PrngState *prng;
14640 unsigned char *destByte = (unsigned char *)dest;
14641 unsigned int si, sj, x;
14644 if (interp->prngState == NULL)
14645 JimPrngInit(interp);
14646 prng = interp->prngState;
14648 for (x = 0; x < len; x++) {
14649 prng->i = (prng->i + 1) & 0xff;
14650 si = prng->sbox[prng->i];
14651 prng->j = (prng->j + si) & 0xff;
14652 sj = prng->sbox[prng->j];
14653 prng->sbox[prng->i] = sj;
14654 prng->sbox[prng->j] = si;
14655 *destByte++ = prng->sbox[(si + sj) & 0xff];
14660 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
14662 int i;
14663 Jim_PrngState *prng;
14666 if (interp->prngState == NULL)
14667 JimPrngInit(interp);
14668 prng = interp->prngState;
14671 for (i = 0; i < 256; i++)
14672 prng->sbox[i] = i;
14674 for (i = 0; i < seedLen; i++) {
14675 unsigned char t;
14677 t = prng->sbox[i & 0xFF];
14678 prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
14679 prng->sbox[seed[i]] = t;
14681 prng->i = prng->j = 0;
14683 for (i = 0; i < 256; i += seedLen) {
14684 JimRandomBytes(interp, seed, seedLen);
14689 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14691 jim_wide wideValue, increment = 1;
14692 Jim_Obj *intObjPtr;
14694 if (argc != 2 && argc != 3) {
14695 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
14696 return JIM_ERR;
14698 if (argc == 3) {
14699 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
14700 return JIM_ERR;
14702 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
14703 if (!intObjPtr) {
14705 wideValue = 0;
14707 else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
14708 return JIM_ERR;
14710 if (!intObjPtr || Jim_IsShared(intObjPtr)) {
14711 intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
14712 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
14713 Jim_FreeNewObj(interp, intObjPtr);
14714 return JIM_ERR;
14717 else {
14719 Jim_InvalidateStringRep(intObjPtr);
14720 JimWideValue(intObjPtr) = wideValue + increment;
14722 if (argv[1]->typePtr != &variableObjType) {
14724 Jim_SetVariable(interp, argv[1], intObjPtr);
14727 Jim_SetResult(interp, intObjPtr);
14728 return JIM_OK;
14732 #define JIM_EVAL_SARGV_LEN 8
14733 #define JIM_EVAL_SINTV_LEN 8
14736 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14738 int retcode;
14740 if (interp->unknown_called > 50) {
14741 return JIM_ERR;
14746 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
14747 return JIM_ERR;
14749 interp->unknown_called++;
14751 retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
14752 interp->unknown_called--;
14754 return retcode;
14757 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14759 int retcode;
14760 Jim_Cmd *cmdPtr;
14762 #if 0
14763 printf("invoke");
14764 int j;
14765 for (j = 0; j < objc; j++) {
14766 printf(" '%s'", Jim_String(objv[j]));
14768 printf("\n");
14769 #endif
14771 if (interp->framePtr->tailcallCmd) {
14773 cmdPtr = interp->framePtr->tailcallCmd;
14774 interp->framePtr->tailcallCmd = NULL;
14776 else {
14777 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
14778 if (cmdPtr == NULL) {
14779 return JimUnknown(interp, objc, objv);
14781 JimIncrCmdRefCount(cmdPtr);
14784 if (interp->evalDepth == interp->maxEvalDepth) {
14785 Jim_SetResultString(interp, "Infinite eval recursion", -1);
14786 retcode = JIM_ERR;
14787 goto out;
14789 interp->evalDepth++;
14792 Jim_SetEmptyResult(interp);
14793 if (cmdPtr->isproc) {
14794 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
14796 else {
14797 interp->cmdPrivData = cmdPtr->u.native.privData;
14798 retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
14800 interp->evalDepth--;
14802 out:
14803 JimDecrCmdRefCount(interp, cmdPtr);
14805 return retcode;
14808 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14810 int i, retcode;
14813 for (i = 0; i < objc; i++)
14814 Jim_IncrRefCount(objv[i]);
14816 retcode = JimInvokeCommand(interp, objc, objv);
14819 for (i = 0; i < objc; i++)
14820 Jim_DecrRefCount(interp, objv[i]);
14822 return retcode;
14825 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
14827 int ret;
14828 Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
14830 nargv[0] = prefix;
14831 memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
14832 ret = Jim_EvalObjVector(interp, objc + 1, nargv);
14833 Jim_Free(nargv);
14834 return ret;
14837 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script)
14839 if (!interp->errorFlag) {
14841 interp->errorFlag = 1;
14842 Jim_IncrRefCount(script->fileNameObj);
14843 Jim_DecrRefCount(interp, interp->errorFileNameObj);
14844 interp->errorFileNameObj = script->fileNameObj;
14845 interp->errorLine = script->linenr;
14847 JimResetStackTrace(interp);
14849 interp->addStackTrace++;
14853 if (interp->addStackTrace > 0) {
14856 JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr);
14858 if (Jim_Length(script->fileNameObj)) {
14859 interp->addStackTrace = 0;
14862 Jim_DecrRefCount(interp, interp->errorProc);
14863 interp->errorProc = interp->emptyObj;
14864 Jim_IncrRefCount(interp->errorProc);
14868 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
14870 Jim_Obj *objPtr;
14872 switch (token->type) {
14873 case JIM_TT_STR:
14874 case JIM_TT_ESC:
14875 objPtr = token->objPtr;
14876 break;
14877 case JIM_TT_VAR:
14878 objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
14879 break;
14880 case JIM_TT_DICTSUGAR:
14881 objPtr = JimExpandDictSugar(interp, token->objPtr);
14882 break;
14883 case JIM_TT_EXPRSUGAR:
14884 objPtr = JimExpandExprSugar(interp, token->objPtr);
14885 break;
14886 case JIM_TT_CMD:
14887 switch (Jim_EvalObj(interp, token->objPtr)) {
14888 case JIM_OK:
14889 case JIM_RETURN:
14890 objPtr = interp->result;
14891 break;
14892 case JIM_BREAK:
14894 return JIM_BREAK;
14895 case JIM_CONTINUE:
14897 return JIM_CONTINUE;
14898 default:
14899 return JIM_ERR;
14901 break;
14902 default:
14903 JimPanic((1,
14904 "default token type (%d) reached " "in Jim_SubstObj().", token->type));
14905 objPtr = NULL;
14906 break;
14908 if (objPtr) {
14909 *objPtrPtr = objPtr;
14910 return JIM_OK;
14912 return JIM_ERR;
14915 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
14917 int totlen = 0, i;
14918 Jim_Obj **intv;
14919 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
14920 Jim_Obj *objPtr;
14921 char *s;
14923 if (tokens <= JIM_EVAL_SINTV_LEN)
14924 intv = sintv;
14925 else
14926 intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
14928 for (i = 0; i < tokens; i++) {
14929 switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
14930 case JIM_OK:
14931 case JIM_RETURN:
14932 break;
14933 case JIM_BREAK:
14934 if (flags & JIM_SUBST_FLAG) {
14936 tokens = i;
14937 continue;
14941 case JIM_CONTINUE:
14942 if (flags & JIM_SUBST_FLAG) {
14943 intv[i] = NULL;
14944 continue;
14948 default:
14949 while (i--) {
14950 Jim_DecrRefCount(interp, intv[i]);
14952 if (intv != sintv) {
14953 Jim_Free(intv);
14955 return NULL;
14957 Jim_IncrRefCount(intv[i]);
14958 Jim_String(intv[i]);
14959 totlen += intv[i]->length;
14963 if (tokens == 1 && intv[0] && intv == sintv) {
14964 Jim_DecrRefCount(interp, intv[0]);
14965 return intv[0];
14968 objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
14970 if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
14971 && token[2].type == JIM_TT_VAR) {
14973 objPtr->typePtr = &interpolatedObjType;
14974 objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
14975 objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
14976 Jim_IncrRefCount(intv[2]);
14978 else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
14980 JimSetSourceInfo(interp, objPtr, intv[0]->internalRep.sourceValue.fileNameObj, intv[0]->internalRep.sourceValue.lineNumber);
14984 s = objPtr->bytes = Jim_Alloc(totlen + 1);
14985 objPtr->length = totlen;
14986 for (i = 0; i < tokens; i++) {
14987 if (intv[i]) {
14988 memcpy(s, intv[i]->bytes, intv[i]->length);
14989 s += intv[i]->length;
14990 Jim_DecrRefCount(interp, intv[i]);
14993 objPtr->bytes[totlen] = '\0';
14995 if (intv != sintv) {
14996 Jim_Free(intv);
14999 return objPtr;
15003 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15005 int retcode = JIM_OK;
15007 JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
15009 if (listPtr->internalRep.listValue.len) {
15010 Jim_IncrRefCount(listPtr);
15011 retcode = JimInvokeCommand(interp,
15012 listPtr->internalRep.listValue.len,
15013 listPtr->internalRep.listValue.ele);
15014 Jim_DecrRefCount(interp, listPtr);
15016 return retcode;
15019 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15021 SetListFromAny(interp, listPtr);
15022 return JimEvalObjList(interp, listPtr);
15025 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
15027 int i;
15028 ScriptObj *script;
15029 ScriptToken *token;
15030 int retcode = JIM_OK;
15031 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
15032 Jim_Obj *prevScriptObj;
15034 if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
15035 return JimEvalObjList(interp, scriptObjPtr);
15038 Jim_IncrRefCount(scriptObjPtr);
15039 script = JimGetScript(interp, scriptObjPtr);
15040 if (!JimScriptValid(interp, script)) {
15041 Jim_DecrRefCount(interp, scriptObjPtr);
15042 return JIM_ERR;
15045 Jim_SetEmptyResult(interp);
15047 token = script->token;
15049 #ifdef JIM_OPTIMIZATION
15050 if (script->len == 0) {
15051 Jim_DecrRefCount(interp, scriptObjPtr);
15052 return JIM_OK;
15054 if (script->len == 3
15055 && token[1].objPtr->typePtr == &commandObjType
15056 && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
15057 && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
15058 && token[2].objPtr->typePtr == &variableObjType) {
15060 Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
15062 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
15063 JimWideValue(objPtr)++;
15064 Jim_InvalidateStringRep(objPtr);
15065 Jim_DecrRefCount(interp, scriptObjPtr);
15066 Jim_SetResult(interp, objPtr);
15067 return JIM_OK;
15070 #endif
15072 script->inUse++;
15075 prevScriptObj = interp->currentScriptObj;
15076 interp->currentScriptObj = scriptObjPtr;
15078 interp->errorFlag = 0;
15079 argv = sargv;
15081 for (i = 0; i < script->len && retcode == JIM_OK; ) {
15082 int argc;
15083 int j;
15086 argc = token[i].objPtr->internalRep.scriptLineValue.argc;
15087 script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
15090 if (argc > JIM_EVAL_SARGV_LEN)
15091 argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
15094 i++;
15096 for (j = 0; j < argc; j++) {
15097 long wordtokens = 1;
15098 int expand = 0;
15099 Jim_Obj *wordObjPtr = NULL;
15101 if (token[i].type == JIM_TT_WORD) {
15102 wordtokens = JimWideValue(token[i++].objPtr);
15103 if (wordtokens < 0) {
15104 expand = 1;
15105 wordtokens = -wordtokens;
15109 if (wordtokens == 1) {
15111 switch (token[i].type) {
15112 case JIM_TT_ESC:
15113 case JIM_TT_STR:
15114 wordObjPtr = token[i].objPtr;
15115 break;
15116 case JIM_TT_VAR:
15117 wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
15118 break;
15119 case JIM_TT_EXPRSUGAR:
15120 wordObjPtr = JimExpandExprSugar(interp, token[i].objPtr);
15121 break;
15122 case JIM_TT_DICTSUGAR:
15123 wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
15124 break;
15125 case JIM_TT_CMD:
15126 retcode = Jim_EvalObj(interp, token[i].objPtr);
15127 if (retcode == JIM_OK) {
15128 wordObjPtr = Jim_GetResult(interp);
15130 break;
15131 default:
15132 JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
15135 else {
15136 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
15139 if (!wordObjPtr) {
15140 if (retcode == JIM_OK) {
15141 retcode = JIM_ERR;
15143 break;
15146 Jim_IncrRefCount(wordObjPtr);
15147 i += wordtokens;
15149 if (!expand) {
15150 argv[j] = wordObjPtr;
15152 else {
15154 int len = Jim_ListLength(interp, wordObjPtr);
15155 int newargc = argc + len - 1;
15156 int k;
15158 if (len > 1) {
15159 if (argv == sargv) {
15160 if (newargc > JIM_EVAL_SARGV_LEN) {
15161 argv = Jim_Alloc(sizeof(*argv) * newargc);
15162 memcpy(argv, sargv, sizeof(*argv) * j);
15165 else {
15167 argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
15172 for (k = 0; k < len; k++) {
15173 argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
15174 Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
15177 Jim_DecrRefCount(interp, wordObjPtr);
15180 j--;
15181 argc += len - 1;
15185 if (retcode == JIM_OK && argc) {
15187 retcode = JimInvokeCommand(interp, argc, argv);
15189 if (Jim_CheckSignal(interp)) {
15190 retcode = JIM_SIGNAL;
15195 while (j-- > 0) {
15196 Jim_DecrRefCount(interp, argv[j]);
15199 if (argv != sargv) {
15200 Jim_Free(argv);
15201 argv = sargv;
15206 if (retcode == JIM_ERR) {
15207 JimAddErrorToStack(interp, script);
15210 else if (retcode != JIM_RETURN || interp->returnCode != JIM_ERR) {
15212 interp->addStackTrace = 0;
15216 interp->currentScriptObj = prevScriptObj;
15218 Jim_FreeIntRep(interp, scriptObjPtr);
15219 scriptObjPtr->typePtr = &scriptObjType;
15220 Jim_SetIntRepPtr(scriptObjPtr, script);
15221 Jim_DecrRefCount(interp, scriptObjPtr);
15223 return retcode;
15226 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
15228 int retcode;
15230 const char *varname = Jim_String(argNameObj);
15231 if (*varname == '&') {
15233 Jim_Obj *objPtr;
15234 Jim_CallFrame *savedCallFrame = interp->framePtr;
15236 interp->framePtr = interp->framePtr->parent;
15237 objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
15238 interp->framePtr = savedCallFrame;
15239 if (!objPtr) {
15240 return JIM_ERR;
15244 objPtr = Jim_NewStringObj(interp, varname + 1, -1);
15245 Jim_IncrRefCount(objPtr);
15246 retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
15247 Jim_DecrRefCount(interp, objPtr);
15249 else {
15250 retcode = Jim_SetVariable(interp, argNameObj, argValObj);
15252 return retcode;
15255 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
15258 Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
15259 int i;
15261 for (i = 0; i < cmd->u.proc.argListLen; i++) {
15262 Jim_AppendString(interp, argmsg, " ", 1);
15264 if (i == cmd->u.proc.argsPos) {
15265 if (cmd->u.proc.arglist[i].defaultObjPtr) {
15267 Jim_AppendString(interp, argmsg, "?", 1);
15268 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
15269 Jim_AppendString(interp, argmsg, " ...?", -1);
15271 else {
15273 Jim_AppendString(interp, argmsg, "?arg...?", -1);
15276 else {
15277 if (cmd->u.proc.arglist[i].defaultObjPtr) {
15278 Jim_AppendString(interp, argmsg, "?", 1);
15279 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
15280 Jim_AppendString(interp, argmsg, "?", 1);
15282 else {
15283 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
15284 if (*arg == '&') {
15285 arg++;
15287 Jim_AppendString(interp, argmsg, arg, -1);
15291 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
15292 Jim_FreeNewObj(interp, argmsg);
15295 #ifdef jim_ext_namespace
15296 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
15298 Jim_CallFrame *callFramePtr;
15299 int retcode;
15302 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
15303 callFramePtr->argv = &interp->emptyObj;
15304 callFramePtr->argc = 0;
15305 callFramePtr->procArgsObjPtr = NULL;
15306 callFramePtr->procBodyObjPtr = scriptObj;
15307 callFramePtr->staticVars = NULL;
15308 callFramePtr->fileNameObj = interp->emptyObj;
15309 callFramePtr->line = 0;
15310 Jim_IncrRefCount(scriptObj);
15311 interp->framePtr = callFramePtr;
15314 if (interp->framePtr->level == interp->maxCallFrameDepth) {
15315 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15316 retcode = JIM_ERR;
15318 else {
15320 retcode = Jim_EvalObj(interp, scriptObj);
15324 interp->framePtr = interp->framePtr->parent;
15325 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15327 return retcode;
15329 #endif
15331 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
15333 Jim_CallFrame *callFramePtr;
15334 int i, d, retcode, optargs;
15335 ScriptObj *script;
15338 if (argc - 1 < cmd->u.proc.reqArity ||
15339 (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
15340 JimSetProcWrongArgs(interp, argv[0], cmd);
15341 return JIM_ERR;
15344 if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
15346 return JIM_OK;
15350 if (interp->framePtr->level == interp->maxCallFrameDepth) {
15351 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15352 return JIM_ERR;
15356 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
15357 callFramePtr->argv = argv;
15358 callFramePtr->argc = argc;
15359 callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
15360 callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
15361 callFramePtr->staticVars = cmd->u.proc.staticVars;
15364 script = JimGetScript(interp, interp->currentScriptObj);
15365 callFramePtr->fileNameObj = script->fileNameObj;
15366 callFramePtr->line = script->linenr;
15368 Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
15369 Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
15370 interp->framePtr = callFramePtr;
15373 optargs = (argc - 1 - cmd->u.proc.reqArity);
15376 i = 1;
15377 for (d = 0; d < cmd->u.proc.argListLen; d++) {
15378 Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
15379 if (d == cmd->u.proc.argsPos) {
15381 Jim_Obj *listObjPtr;
15382 int argsLen = 0;
15383 if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
15384 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
15386 listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
15389 if (cmd->u.proc.arglist[d].defaultObjPtr) {
15390 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
15392 retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
15393 if (retcode != JIM_OK) {
15394 goto badargset;
15397 i += argsLen;
15398 continue;
15402 if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
15403 retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
15405 else {
15407 retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
15409 if (retcode != JIM_OK) {
15410 goto badargset;
15415 retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
15417 badargset:
15420 interp->framePtr = interp->framePtr->parent;
15421 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15423 if (interp->framePtr->tailcallObj) {
15425 if (interp->framePtr->tailcall++ == 0) {
15427 do {
15428 Jim_Obj *tailcallObj = interp->framePtr->tailcallObj;
15430 interp->framePtr->tailcallObj = NULL;
15432 if (retcode == JIM_EVAL) {
15433 retcode = Jim_EvalObjList(interp, tailcallObj);
15434 if (retcode == JIM_RETURN) {
15435 interp->returnLevel++;
15438 Jim_DecrRefCount(interp, tailcallObj);
15439 } while (interp->framePtr->tailcallObj);
15442 if (interp->framePtr->tailcallCmd) {
15443 JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
15444 interp->framePtr->tailcallCmd = NULL;
15447 interp->framePtr->tailcall--;
15451 if (retcode == JIM_RETURN) {
15452 if (--interp->returnLevel <= 0) {
15453 retcode = interp->returnCode;
15454 interp->returnCode = JIM_OK;
15455 interp->returnLevel = 0;
15458 else if (retcode == JIM_ERR) {
15459 interp->addStackTrace++;
15460 Jim_DecrRefCount(interp, interp->errorProc);
15461 interp->errorProc = argv[0];
15462 Jim_IncrRefCount(interp->errorProc);
15465 return retcode;
15468 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
15470 int retval;
15471 Jim_Obj *scriptObjPtr;
15473 scriptObjPtr = Jim_NewStringObj(interp, script, -1);
15474 Jim_IncrRefCount(scriptObjPtr);
15476 if (filename) {
15477 Jim_Obj *prevScriptObj;
15479 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
15481 prevScriptObj = interp->currentScriptObj;
15482 interp->currentScriptObj = scriptObjPtr;
15484 retval = Jim_EvalObj(interp, scriptObjPtr);
15486 interp->currentScriptObj = prevScriptObj;
15488 else {
15489 retval = Jim_EvalObj(interp, scriptObjPtr);
15491 Jim_DecrRefCount(interp, scriptObjPtr);
15492 return retval;
15495 int Jim_Eval(Jim_Interp *interp, const char *script)
15497 return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
15501 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
15503 int retval;
15504 Jim_CallFrame *savedFramePtr = interp->framePtr;
15506 interp->framePtr = interp->topFramePtr;
15507 retval = Jim_Eval(interp, script);
15508 interp->framePtr = savedFramePtr;
15510 return retval;
15513 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
15515 int retval;
15516 Jim_CallFrame *savedFramePtr = interp->framePtr;
15518 interp->framePtr = interp->topFramePtr;
15519 retval = Jim_EvalFile(interp, filename);
15520 interp->framePtr = savedFramePtr;
15522 return retval;
15525 #include <sys/stat.h>
15527 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
15529 FILE *fp;
15530 char *buf;
15531 Jim_Obj *scriptObjPtr;
15532 Jim_Obj *prevScriptObj;
15533 struct stat sb;
15534 int retcode;
15535 int readlen;
15537 if (stat(filename, &sb) != 0 || (fp = fopen(filename, "rt")) == NULL) {
15538 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
15539 return JIM_ERR;
15541 if (sb.st_size == 0) {
15542 fclose(fp);
15543 return JIM_OK;
15546 buf = Jim_Alloc(sb.st_size + 1);
15547 readlen = fread(buf, 1, sb.st_size, fp);
15548 if (ferror(fp)) {
15549 fclose(fp);
15550 Jim_Free(buf);
15551 Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
15552 return JIM_ERR;
15554 fclose(fp);
15555 buf[readlen] = 0;
15557 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
15558 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1);
15559 Jim_IncrRefCount(scriptObjPtr);
15561 prevScriptObj = interp->currentScriptObj;
15562 interp->currentScriptObj = scriptObjPtr;
15564 retcode = Jim_EvalObj(interp, scriptObjPtr);
15567 if (retcode == JIM_RETURN) {
15568 if (--interp->returnLevel <= 0) {
15569 retcode = interp->returnCode;
15570 interp->returnCode = JIM_OK;
15571 interp->returnLevel = 0;
15574 if (retcode == JIM_ERR) {
15576 interp->addStackTrace++;
15579 interp->currentScriptObj = prevScriptObj;
15581 Jim_DecrRefCount(interp, scriptObjPtr);
15583 return retcode;
15586 static void JimParseSubst(struct JimParserCtx *pc, int flags)
15588 pc->tstart = pc->p;
15589 pc->tline = pc->linenr;
15591 if (pc->len == 0) {
15592 pc->tend = pc->p;
15593 pc->tt = JIM_TT_EOL;
15594 pc->eof = 1;
15595 return;
15597 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15598 JimParseCmd(pc);
15599 return;
15601 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15602 if (JimParseVar(pc) == JIM_OK) {
15603 return;
15606 pc->tstart = pc->p;
15607 flags |= JIM_SUBST_NOVAR;
15609 while (pc->len) {
15610 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15611 break;
15613 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15614 break;
15616 if (*pc->p == '\\' && pc->len > 1) {
15617 pc->p++;
15618 pc->len--;
15620 pc->p++;
15621 pc->len--;
15623 pc->tend = pc->p - 1;
15624 pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
15628 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
15630 int scriptTextLen;
15631 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
15632 struct JimParserCtx parser;
15633 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
15634 ParseTokenList tokenlist;
15637 ScriptTokenListInit(&tokenlist);
15639 JimParserInit(&parser, scriptText, scriptTextLen, 1);
15640 while (1) {
15641 JimParseSubst(&parser, flags);
15642 if (parser.eof) {
15644 break;
15646 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
15647 parser.tline);
15651 script->inUse = 1;
15652 script->substFlags = flags;
15653 script->fileNameObj = interp->emptyObj;
15654 Jim_IncrRefCount(script->fileNameObj);
15655 SubstObjAddTokens(interp, script, &tokenlist);
15658 ScriptTokenListFree(&tokenlist);
15660 #ifdef DEBUG_SHOW_SUBST
15662 int i;
15664 printf("==== Subst ====\n");
15665 for (i = 0; i < script->len; i++) {
15666 printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
15667 Jim_String(script->token[i].objPtr));
15670 #endif
15673 Jim_FreeIntRep(interp, objPtr);
15674 Jim_SetIntRepPtr(objPtr, script);
15675 objPtr->typePtr = &scriptObjType;
15676 return JIM_OK;
15679 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
15681 if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
15682 SetSubstFromAny(interp, objPtr, flags);
15683 return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
15686 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
15688 ScriptObj *script = Jim_GetSubst(interp, substObjPtr, flags);
15690 Jim_IncrRefCount(substObjPtr);
15691 script->inUse++;
15693 *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
15695 script->inUse--;
15696 Jim_DecrRefCount(interp, substObjPtr);
15697 if (*resObjPtrPtr == NULL) {
15698 return JIM_ERR;
15700 return JIM_OK;
15703 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
15705 Jim_Obj *objPtr;
15706 Jim_Obj *listObjPtr = Jim_NewListObj(interp, argv, argc);
15708 if (*msg) {
15709 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
15711 Jim_IncrRefCount(listObjPtr);
15712 objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
15713 Jim_DecrRefCount(interp, listObjPtr);
15715 Jim_IncrRefCount(objPtr);
15716 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
15717 Jim_DecrRefCount(interp, objPtr);
15720 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
15721 Jim_HashEntry *he, int type);
15723 #define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL)
15725 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
15726 JimHashtableIteratorCallbackType *callback, int type)
15728 Jim_HashEntry *he;
15729 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
15732 if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
15733 he = Jim_FindHashEntry(ht, Jim_String(patternObjPtr));
15734 if (he) {
15735 callback(interp, listObjPtr, he, type);
15738 else {
15739 Jim_HashTableIterator htiter;
15740 JimInitHashTableIterator(ht, &htiter);
15741 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
15742 if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), he->key, 0)) {
15743 callback(interp, listObjPtr, he, type);
15747 return listObjPtr;
15751 #define JIM_CMDLIST_COMMANDS 0
15752 #define JIM_CMDLIST_PROCS 1
15753 #define JIM_CMDLIST_CHANNELS 2
15755 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15756 Jim_HashEntry *he, int type)
15758 Jim_Cmd *cmdPtr = Jim_GetHashEntryVal(he);
15759 Jim_Obj *objPtr;
15761 if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
15763 return;
15766 objPtr = Jim_NewStringObj(interp, he->key, -1);
15767 Jim_IncrRefCount(objPtr);
15769 if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, objPtr)) {
15770 Jim_ListAppendElement(interp, listObjPtr, objPtr);
15772 Jim_DecrRefCount(interp, objPtr);
15776 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
15778 return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
15782 #define JIM_VARLIST_GLOBALS 0
15783 #define JIM_VARLIST_LOCALS 1
15784 #define JIM_VARLIST_VARS 2
15786 #define JIM_VARLIST_VALUES 0x1000
15788 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15789 Jim_HashEntry *he, int type)
15791 Jim_Var *varPtr = Jim_GetHashEntryVal(he);
15793 if (type != JIM_VARLIST_LOCALS || varPtr->linkFramePtr == NULL) {
15794 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1));
15795 if (type & JIM_VARLIST_VALUES) {
15796 Jim_ListAppendElement(interp, listObjPtr, varPtr->objPtr);
15802 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
15804 if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
15805 return interp->emptyObj;
15807 else {
15808 Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
15809 return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch, mode);
15813 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
15814 Jim_Obj **objPtrPtr, int info_level_cmd)
15816 Jim_CallFrame *targetCallFrame;
15818 targetCallFrame = JimGetCallFrameByInteger(interp, levelObjPtr);
15819 if (targetCallFrame == NULL) {
15820 return JIM_ERR;
15823 if (targetCallFrame == interp->topFramePtr) {
15824 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
15825 return JIM_ERR;
15827 if (info_level_cmd) {
15828 *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
15830 else {
15831 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
15833 Jim_ListAppendElement(interp, listObj, targetCallFrame->argv[0]);
15834 Jim_ListAppendElement(interp, listObj, targetCallFrame->fileNameObj);
15835 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, targetCallFrame->line));
15836 *objPtrPtr = listObj;
15838 return JIM_OK;
15843 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15845 if (argc != 2 && argc != 3) {
15846 Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
15847 return JIM_ERR;
15849 if (argc == 3) {
15850 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
15851 Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
15852 return JIM_ERR;
15854 else {
15855 fputs(Jim_String(argv[2]), stdout);
15858 else {
15859 puts(Jim_String(argv[1]));
15861 return JIM_OK;
15865 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
15867 jim_wide wideValue, res;
15868 double doubleValue, doubleRes;
15869 int i;
15871 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
15873 for (i = 1; i < argc; i++) {
15874 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
15875 goto trydouble;
15876 if (op == JIM_EXPROP_ADD)
15877 res += wideValue;
15878 else
15879 res *= wideValue;
15881 Jim_SetResultInt(interp, res);
15882 return JIM_OK;
15883 trydouble:
15884 doubleRes = (double)res;
15885 for (; i < argc; i++) {
15886 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
15887 return JIM_ERR;
15888 if (op == JIM_EXPROP_ADD)
15889 doubleRes += doubleValue;
15890 else
15891 doubleRes *= doubleValue;
15893 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15894 return JIM_OK;
15898 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
15900 jim_wide wideValue, res = 0;
15901 double doubleValue, doubleRes = 0;
15902 int i = 2;
15904 if (argc < 2) {
15905 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
15906 return JIM_ERR;
15908 else if (argc == 2) {
15909 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
15910 if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
15911 return JIM_ERR;
15913 else {
15914 if (op == JIM_EXPROP_SUB)
15915 doubleRes = -doubleValue;
15916 else
15917 doubleRes = 1.0 / doubleValue;
15918 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15919 return JIM_OK;
15922 if (op == JIM_EXPROP_SUB) {
15923 res = -wideValue;
15924 Jim_SetResultInt(interp, res);
15926 else {
15927 doubleRes = 1.0 / wideValue;
15928 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15930 return JIM_OK;
15932 else {
15933 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
15934 if (Jim_GetDouble(interp, argv[1], &doubleRes)
15935 != JIM_OK) {
15936 return JIM_ERR;
15938 else {
15939 goto trydouble;
15943 for (i = 2; i < argc; i++) {
15944 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
15945 doubleRes = (double)res;
15946 goto trydouble;
15948 if (op == JIM_EXPROP_SUB)
15949 res -= wideValue;
15950 else
15951 res /= wideValue;
15953 Jim_SetResultInt(interp, res);
15954 return JIM_OK;
15955 trydouble:
15956 for (; i < argc; i++) {
15957 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
15958 return JIM_ERR;
15959 if (op == JIM_EXPROP_SUB)
15960 doubleRes -= doubleValue;
15961 else
15962 doubleRes /= doubleValue;
15964 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15965 return JIM_OK;
15970 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15972 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
15976 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15978 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
15982 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15984 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
15988 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15990 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
15994 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15996 if (argc != 2 && argc != 3) {
15997 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
15998 return JIM_ERR;
16000 if (argc == 2) {
16001 Jim_Obj *objPtr;
16003 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16004 if (!objPtr)
16005 return JIM_ERR;
16006 Jim_SetResult(interp, objPtr);
16007 return JIM_OK;
16010 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
16011 return JIM_ERR;
16012 Jim_SetResult(interp, argv[2]);
16013 return JIM_OK;
16016 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16018 int i = 1;
16019 int complain = 1;
16021 while (i < argc) {
16022 if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
16023 i++;
16024 break;
16026 if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
16027 complain = 0;
16028 i++;
16029 continue;
16031 break;
16034 while (i < argc) {
16035 if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
16036 && complain) {
16037 return JIM_ERR;
16039 i++;
16041 return JIM_OK;
16045 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16047 if (argc != 3) {
16048 Jim_WrongNumArgs(interp, 1, argv, "condition body");
16049 return JIM_ERR;
16053 while (1) {
16054 int boolean, retval;
16056 if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
16057 return retval;
16058 if (!boolean)
16059 break;
16061 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
16062 switch (retval) {
16063 case JIM_BREAK:
16064 goto out;
16065 break;
16066 case JIM_CONTINUE:
16067 continue;
16068 break;
16069 default:
16070 return retval;
16074 out:
16075 Jim_SetEmptyResult(interp);
16076 return JIM_OK;
16080 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16082 int retval;
16083 int boolean = 1;
16084 Jim_Obj *varNamePtr = NULL;
16085 Jim_Obj *stopVarNamePtr = NULL;
16087 if (argc != 5) {
16088 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
16089 return JIM_ERR;
16093 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
16094 return retval;
16097 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16100 #ifdef JIM_OPTIMIZATION
16101 if (retval == JIM_OK && boolean) {
16102 ScriptObj *incrScript;
16103 ExprByteCode *expr;
16104 jim_wide stop, currentVal;
16105 Jim_Obj *objPtr;
16106 int cmpOffset;
16109 expr = JimGetExpression(interp, argv[2]);
16110 incrScript = JimGetScript(interp, argv[3]);
16113 if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
16114 goto evalstart;
16117 if (incrScript->token[1].type != JIM_TT_ESC ||
16118 expr->token[0].type != JIM_TT_VAR ||
16119 (expr->token[1].type != JIM_TT_EXPR_INT && expr->token[1].type != JIM_TT_VAR)) {
16120 goto evalstart;
16123 if (expr->token[2].type == JIM_EXPROP_LT) {
16124 cmpOffset = 0;
16126 else if (expr->token[2].type == JIM_EXPROP_LTE) {
16127 cmpOffset = 1;
16129 else {
16130 goto evalstart;
16134 if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
16135 goto evalstart;
16139 if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->token[0].objPtr)) {
16140 goto evalstart;
16144 if (expr->token[1].type == JIM_TT_EXPR_INT) {
16145 if (Jim_GetWide(interp, expr->token[1].objPtr, &stop) == JIM_ERR) {
16146 goto evalstart;
16149 else {
16150 stopVarNamePtr = expr->token[1].objPtr;
16151 Jim_IncrRefCount(stopVarNamePtr);
16153 stop = 0;
16157 varNamePtr = expr->token[0].objPtr;
16158 Jim_IncrRefCount(varNamePtr);
16160 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
16161 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
16162 goto testcond;
16166 while (retval == JIM_OK) {
16171 if (stopVarNamePtr) {
16172 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
16173 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
16174 goto testcond;
16178 if (currentVal >= stop + cmpOffset) {
16179 break;
16183 retval = Jim_EvalObj(interp, argv[4]);
16184 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16185 retval = JIM_OK;
16187 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
16190 if (objPtr == NULL) {
16191 retval = JIM_ERR;
16192 goto out;
16194 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16195 currentVal = ++JimWideValue(objPtr);
16196 Jim_InvalidateStringRep(objPtr);
16198 else {
16199 if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
16200 Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
16201 ++currentVal)) != JIM_OK) {
16202 goto evalnext;
16207 goto out;
16209 evalstart:
16210 #endif
16212 while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
16214 retval = Jim_EvalObj(interp, argv[4]);
16216 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16218 evalnext:
16219 retval = Jim_EvalObj(interp, argv[3]);
16220 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16222 testcond:
16223 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16227 out:
16228 if (stopVarNamePtr) {
16229 Jim_DecrRefCount(interp, stopVarNamePtr);
16231 if (varNamePtr) {
16232 Jim_DecrRefCount(interp, varNamePtr);
16235 if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
16236 Jim_SetEmptyResult(interp);
16237 return JIM_OK;
16240 return retval;
16244 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16246 int retval;
16247 jim_wide i;
16248 jim_wide limit;
16249 jim_wide incr = 1;
16250 Jim_Obj *bodyObjPtr;
16252 if (argc != 5 && argc != 6) {
16253 Jim_WrongNumArgs(interp, 1, argv, "var first limit ?incr? body");
16254 return JIM_ERR;
16257 if (Jim_GetWide(interp, argv[2], &i) != JIM_OK ||
16258 Jim_GetWide(interp, argv[3], &limit) != JIM_OK ||
16259 (argc == 6 && Jim_GetWide(interp, argv[4], &incr) != JIM_OK)) {
16260 return JIM_ERR;
16262 bodyObjPtr = (argc == 5) ? argv[4] : argv[5];
16264 retval = Jim_SetVariable(interp, argv[1], argv[2]);
16266 while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
16267 retval = Jim_EvalObj(interp, bodyObjPtr);
16268 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16269 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16271 retval = JIM_OK;
16274 i += incr;
16276 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16277 if (argv[1]->typePtr != &variableObjType) {
16278 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16279 return JIM_ERR;
16282 JimWideValue(objPtr) = i;
16283 Jim_InvalidateStringRep(objPtr);
16285 if (argv[1]->typePtr != &variableObjType) {
16286 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16287 retval = JIM_ERR;
16288 break;
16292 else {
16293 objPtr = Jim_NewIntObj(interp, i);
16294 retval = Jim_SetVariable(interp, argv[1], objPtr);
16295 if (retval != JIM_OK) {
16296 Jim_FreeNewObj(interp, objPtr);
16302 if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
16303 Jim_SetEmptyResult(interp);
16304 return JIM_OK;
16306 return retval;
16309 typedef struct {
16310 Jim_Obj *objPtr;
16311 int idx;
16312 } Jim_ListIter;
16314 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
16316 iter->objPtr = objPtr;
16317 iter->idx = 0;
16320 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
16322 if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
16323 return NULL;
16325 return iter->objPtr->internalRep.listValue.ele[iter->idx++];
16328 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
16330 return iter->idx >= Jim_ListLength(interp, iter->objPtr);
16334 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
16336 int result = JIM_OK;
16337 int i, numargs;
16338 Jim_ListIter twoiters[2];
16339 Jim_ListIter *iters;
16340 Jim_Obj *script;
16341 Jim_Obj *resultObj;
16343 if (argc < 4 || argc % 2 != 0) {
16344 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
16345 return JIM_ERR;
16347 script = argv[argc - 1];
16348 numargs = (argc - 1 - 1);
16350 if (numargs == 2) {
16351 iters = twoiters;
16353 else {
16354 iters = Jim_Alloc(numargs * sizeof(*iters));
16356 for (i = 0; i < numargs; i++) {
16357 JimListIterInit(&iters[i], argv[i + 1]);
16358 if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
16359 result = JIM_ERR;
16362 if (result != JIM_OK) {
16363 Jim_SetResultString(interp, "foreach varlist is empty", -1);
16364 return result;
16367 if (doMap) {
16368 resultObj = Jim_NewListObj(interp, NULL, 0);
16370 else {
16371 resultObj = interp->emptyObj;
16373 Jim_IncrRefCount(resultObj);
16375 while (1) {
16377 for (i = 0; i < numargs; i += 2) {
16378 if (!JimListIterDone(interp, &iters[i + 1])) {
16379 break;
16382 if (i == numargs) {
16384 break;
16388 for (i = 0; i < numargs; i += 2) {
16389 Jim_Obj *varName;
16392 JimListIterInit(&iters[i], argv[i + 1]);
16393 while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
16394 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
16395 if (!valObj) {
16397 valObj = interp->emptyObj;
16400 Jim_IncrRefCount(valObj);
16401 result = Jim_SetVariable(interp, varName, valObj);
16402 Jim_DecrRefCount(interp, valObj);
16403 if (result != JIM_OK) {
16404 goto err;
16408 switch (result = Jim_EvalObj(interp, script)) {
16409 case JIM_OK:
16410 if (doMap) {
16411 Jim_ListAppendElement(interp, resultObj, interp->result);
16413 break;
16414 case JIM_CONTINUE:
16415 break;
16416 case JIM_BREAK:
16417 goto out;
16418 default:
16419 goto err;
16422 out:
16423 result = JIM_OK;
16424 Jim_SetResult(interp, resultObj);
16425 err:
16426 Jim_DecrRefCount(interp, resultObj);
16427 if (numargs > 2) {
16428 Jim_Free(iters);
16430 return result;
16434 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16436 return JimForeachMapHelper(interp, argc, argv, 0);
16440 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16442 return JimForeachMapHelper(interp, argc, argv, 1);
16446 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16448 int result = JIM_ERR;
16449 int i;
16450 Jim_ListIter iter;
16451 Jim_Obj *resultObj;
16453 if (argc < 2) {
16454 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
16455 return JIM_ERR;
16458 JimListIterInit(&iter, argv[1]);
16460 for (i = 2; i < argc; i++) {
16461 Jim_Obj *valObj = JimListIterNext(interp, &iter);
16462 result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
16463 if (result != JIM_OK) {
16464 return result;
16468 resultObj = Jim_NewListObj(interp, NULL, 0);
16469 while (!JimListIterDone(interp, &iter)) {
16470 Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
16473 Jim_SetResult(interp, resultObj);
16475 return JIM_OK;
16479 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16481 int boolean, retval, current = 1, falsebody = 0;
16483 if (argc >= 3) {
16484 while (1) {
16486 if (current >= argc)
16487 goto err;
16488 if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
16489 != JIM_OK)
16490 return retval;
16492 if (current >= argc)
16493 goto err;
16494 if (Jim_CompareStringImmediate(interp, argv[current], "then"))
16495 current++;
16497 if (current >= argc)
16498 goto err;
16499 if (boolean)
16500 return Jim_EvalObj(interp, argv[current]);
16502 if (++current >= argc) {
16503 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
16504 return JIM_OK;
16506 falsebody = current++;
16507 if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
16509 if (current != argc - 1)
16510 goto err;
16511 return Jim_EvalObj(interp, argv[current]);
16513 else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
16514 continue;
16516 else if (falsebody != argc - 1)
16517 goto err;
16518 return Jim_EvalObj(interp, argv[falsebody]);
16520 return JIM_OK;
16522 err:
16523 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
16524 return JIM_ERR;
16529 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
16530 Jim_Obj *stringObj, int nocase)
16532 Jim_Obj *parms[4];
16533 int argc = 0;
16534 long eq;
16535 int rc;
16537 parms[argc++] = commandObj;
16538 if (nocase) {
16539 parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
16541 parms[argc++] = patternObj;
16542 parms[argc++] = stringObj;
16544 rc = Jim_EvalObjVector(interp, argc, parms);
16546 if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
16547 eq = -rc;
16550 return eq;
16553 enum
16554 { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
16557 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16559 int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
16560 Jim_Obj *command = 0, *const *caseList = 0, *strObj;
16561 Jim_Obj *script = 0;
16563 if (argc < 3) {
16564 wrongnumargs:
16565 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
16566 "pattern body ... ?default body? or " "{pattern body ?pattern body ...?}");
16567 return JIM_ERR;
16569 for (opt = 1; opt < argc; ++opt) {
16570 const char *option = Jim_String(argv[opt]);
16572 if (*option != '-')
16573 break;
16574 else if (strncmp(option, "--", 2) == 0) {
16575 ++opt;
16576 break;
16578 else if (strncmp(option, "-exact", 2) == 0)
16579 matchOpt = SWITCH_EXACT;
16580 else if (strncmp(option, "-glob", 2) == 0)
16581 matchOpt = SWITCH_GLOB;
16582 else if (strncmp(option, "-regexp", 2) == 0)
16583 matchOpt = SWITCH_RE;
16584 else if (strncmp(option, "-command", 2) == 0) {
16585 matchOpt = SWITCH_CMD;
16586 if ((argc - opt) < 2)
16587 goto wrongnumargs;
16588 command = argv[++opt];
16590 else {
16591 Jim_SetResultFormatted(interp,
16592 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
16593 argv[opt]);
16594 return JIM_ERR;
16596 if ((argc - opt) < 2)
16597 goto wrongnumargs;
16599 strObj = argv[opt++];
16600 patCount = argc - opt;
16601 if (patCount == 1) {
16602 Jim_Obj **vector;
16604 JimListGetElements(interp, argv[opt], &patCount, &vector);
16605 caseList = vector;
16607 else
16608 caseList = &argv[opt];
16609 if (patCount == 0 || patCount % 2 != 0)
16610 goto wrongnumargs;
16611 for (i = 0; script == 0 && i < patCount; i += 2) {
16612 Jim_Obj *patObj = caseList[i];
16614 if (!Jim_CompareStringImmediate(interp, patObj, "default")
16615 || i < (patCount - 2)) {
16616 switch (matchOpt) {
16617 case SWITCH_EXACT:
16618 if (Jim_StringEqObj(strObj, patObj))
16619 script = caseList[i + 1];
16620 break;
16621 case SWITCH_GLOB:
16622 if (Jim_StringMatchObj(interp, patObj, strObj, 0))
16623 script = caseList[i + 1];
16624 break;
16625 case SWITCH_RE:
16626 command = Jim_NewStringObj(interp, "regexp", -1);
16628 case SWITCH_CMD:{
16629 int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, 0);
16631 if (argc - opt == 1) {
16632 Jim_Obj **vector;
16634 JimListGetElements(interp, argv[opt], &patCount, &vector);
16635 caseList = vector;
16638 if (rc < 0) {
16639 return -rc;
16641 if (rc)
16642 script = caseList[i + 1];
16643 break;
16647 else {
16648 script = caseList[i + 1];
16651 for (; i < patCount && Jim_CompareStringImmediate(interp, script, "-"); i += 2)
16652 script = caseList[i + 1];
16653 if (script && Jim_CompareStringImmediate(interp, script, "-")) {
16654 Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
16655 return JIM_ERR;
16657 Jim_SetEmptyResult(interp);
16658 if (script) {
16659 return Jim_EvalObj(interp, script);
16661 return JIM_OK;
16665 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16667 Jim_Obj *listObjPtr;
16669 listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1);
16670 Jim_SetResult(interp, listObjPtr);
16671 return JIM_OK;
16675 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16677 Jim_Obj *objPtr, *listObjPtr;
16678 int i;
16679 int idx;
16681 if (argc < 2) {
16682 Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
16683 return JIM_ERR;
16685 objPtr = argv[1];
16686 Jim_IncrRefCount(objPtr);
16687 for (i = 2; i < argc; i++) {
16688 listObjPtr = objPtr;
16689 if (Jim_GetIndex(interp, argv[i], &idx) != JIM_OK) {
16690 Jim_DecrRefCount(interp, listObjPtr);
16691 return JIM_ERR;
16693 if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_NONE) != JIM_OK) {
16694 Jim_DecrRefCount(interp, listObjPtr);
16695 Jim_SetEmptyResult(interp);
16696 return JIM_OK;
16698 Jim_IncrRefCount(objPtr);
16699 Jim_DecrRefCount(interp, listObjPtr);
16701 Jim_SetResult(interp, objPtr);
16702 Jim_DecrRefCount(interp, objPtr);
16703 return JIM_OK;
16707 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16709 if (argc != 2) {
16710 Jim_WrongNumArgs(interp, 1, argv, "list");
16711 return JIM_ERR;
16713 Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
16714 return JIM_OK;
16718 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16720 static const char * const options[] = {
16721 "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
16722 NULL
16724 enum
16725 { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
16726 OPT_COMMAND };
16727 int i;
16728 int opt_bool = 0;
16729 int opt_not = 0;
16730 int opt_nocase = 0;
16731 int opt_all = 0;
16732 int opt_inline = 0;
16733 int opt_match = OPT_EXACT;
16734 int listlen;
16735 int rc = JIM_OK;
16736 Jim_Obj *listObjPtr = NULL;
16737 Jim_Obj *commandObj = NULL;
16739 if (argc < 3) {
16740 wrongargs:
16741 Jim_WrongNumArgs(interp, 1, argv,
16742 "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? list value");
16743 return JIM_ERR;
16746 for (i = 1; i < argc - 2; i++) {
16747 int option;
16749 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
16750 return JIM_ERR;
16752 switch (option) {
16753 case OPT_BOOL:
16754 opt_bool = 1;
16755 opt_inline = 0;
16756 break;
16757 case OPT_NOT:
16758 opt_not = 1;
16759 break;
16760 case OPT_NOCASE:
16761 opt_nocase = 1;
16762 break;
16763 case OPT_INLINE:
16764 opt_inline = 1;
16765 opt_bool = 0;
16766 break;
16767 case OPT_ALL:
16768 opt_all = 1;
16769 break;
16770 case OPT_COMMAND:
16771 if (i >= argc - 2) {
16772 goto wrongargs;
16774 commandObj = argv[++i];
16776 case OPT_EXACT:
16777 case OPT_GLOB:
16778 case OPT_REGEXP:
16779 opt_match = option;
16780 break;
16784 argv += i;
16786 if (opt_all) {
16787 listObjPtr = Jim_NewListObj(interp, NULL, 0);
16789 if (opt_match == OPT_REGEXP) {
16790 commandObj = Jim_NewStringObj(interp, "regexp", -1);
16792 if (commandObj) {
16793 Jim_IncrRefCount(commandObj);
16796 listlen = Jim_ListLength(interp, argv[0]);
16797 for (i = 0; i < listlen; i++) {
16798 int eq = 0;
16799 Jim_Obj *objPtr = Jim_ListGetIndex(interp, argv[0], i);
16801 switch (opt_match) {
16802 case OPT_EXACT:
16803 eq = Jim_StringCompareObj(interp, argv[1], objPtr, opt_nocase) == 0;
16804 break;
16806 case OPT_GLOB:
16807 eq = Jim_StringMatchObj(interp, argv[1], objPtr, opt_nocase);
16808 break;
16810 case OPT_REGEXP:
16811 case OPT_COMMAND:
16812 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, opt_nocase);
16813 if (eq < 0) {
16814 if (listObjPtr) {
16815 Jim_FreeNewObj(interp, listObjPtr);
16817 rc = JIM_ERR;
16818 goto done;
16820 break;
16824 if (!eq && opt_bool && opt_not && !opt_all) {
16825 continue;
16828 if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
16830 Jim_Obj *resultObj;
16832 if (opt_bool) {
16833 resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
16835 else if (!opt_inline) {
16836 resultObj = Jim_NewIntObj(interp, i);
16838 else {
16839 resultObj = objPtr;
16842 if (opt_all) {
16843 Jim_ListAppendElement(interp, listObjPtr, resultObj);
16845 else {
16846 Jim_SetResult(interp, resultObj);
16847 goto done;
16852 if (opt_all) {
16853 Jim_SetResult(interp, listObjPtr);
16855 else {
16857 if (opt_bool) {
16858 Jim_SetResultBool(interp, opt_not);
16860 else if (!opt_inline) {
16861 Jim_SetResultInt(interp, -1);
16865 done:
16866 if (commandObj) {
16867 Jim_DecrRefCount(interp, commandObj);
16869 return rc;
16873 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16875 Jim_Obj *listObjPtr;
16876 int shared, i;
16878 if (argc < 2) {
16879 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
16880 return JIM_ERR;
16882 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
16883 if (!listObjPtr) {
16885 listObjPtr = Jim_NewListObj(interp, NULL, 0);
16886 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
16887 Jim_FreeNewObj(interp, listObjPtr);
16888 return JIM_ERR;
16891 shared = Jim_IsShared(listObjPtr);
16892 if (shared)
16893 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
16894 for (i = 2; i < argc; i++)
16895 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
16896 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
16897 if (shared)
16898 Jim_FreeNewObj(interp, listObjPtr);
16899 return JIM_ERR;
16901 Jim_SetResult(interp, listObjPtr);
16902 return JIM_OK;
16906 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16908 int idx, len;
16909 Jim_Obj *listPtr;
16911 if (argc < 3) {
16912 Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
16913 return JIM_ERR;
16915 listPtr = argv[1];
16916 if (Jim_IsShared(listPtr))
16917 listPtr = Jim_DuplicateObj(interp, listPtr);
16918 if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
16919 goto err;
16920 len = Jim_ListLength(interp, listPtr);
16921 if (idx >= len)
16922 idx = len;
16923 else if (idx < 0)
16924 idx = len + idx + 1;
16925 Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
16926 Jim_SetResult(interp, listPtr);
16927 return JIM_OK;
16928 err:
16929 if (listPtr != argv[1]) {
16930 Jim_FreeNewObj(interp, listPtr);
16932 return JIM_ERR;
16936 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16938 int first, last, len, rangeLen;
16939 Jim_Obj *listObj;
16940 Jim_Obj *newListObj;
16942 if (argc < 4) {
16943 Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
16944 return JIM_ERR;
16946 if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
16947 Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
16948 return JIM_ERR;
16951 listObj = argv[1];
16952 len = Jim_ListLength(interp, listObj);
16954 first = JimRelToAbsIndex(len, first);
16955 last = JimRelToAbsIndex(len, last);
16956 JimRelToAbsRange(len, &first, &last, &rangeLen);
16960 if (first < len) {
16963 else if (len == 0) {
16965 first = 0;
16967 else {
16968 Jim_SetResultString(interp, "list doesn't contain element ", -1);
16969 Jim_AppendObj(interp, Jim_GetResult(interp), argv[2]);
16970 return JIM_ERR;
16974 newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
16977 ListInsertElements(newListObj, -1, argc - 4, argv + 4);
16980 ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
16982 Jim_SetResult(interp, newListObj);
16983 return JIM_OK;
16987 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16989 if (argc < 3) {
16990 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
16991 return JIM_ERR;
16993 else if (argc == 3) {
16995 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
16996 return JIM_ERR;
16997 Jim_SetResult(interp, argv[2]);
16998 return JIM_OK;
17000 return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
17004 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
17006 static const char * const options[] = {
17007 "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", NULL
17009 enum
17010 { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE };
17011 Jim_Obj *resObj;
17012 int i;
17013 int retCode;
17015 struct lsort_info info;
17017 if (argc < 2) {
17018 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
17019 return JIM_ERR;
17022 info.type = JIM_LSORT_ASCII;
17023 info.order = 1;
17024 info.indexed = 0;
17025 info.unique = 0;
17026 info.command = NULL;
17027 info.interp = interp;
17029 for (i = 1; i < (argc - 1); i++) {
17030 int option;
17032 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
17033 != JIM_OK)
17034 return JIM_ERR;
17035 switch (option) {
17036 case OPT_ASCII:
17037 info.type = JIM_LSORT_ASCII;
17038 break;
17039 case OPT_NOCASE:
17040 info.type = JIM_LSORT_NOCASE;
17041 break;
17042 case OPT_INTEGER:
17043 info.type = JIM_LSORT_INTEGER;
17044 break;
17045 case OPT_REAL:
17046 info.type = JIM_LSORT_REAL;
17047 break;
17048 case OPT_INCREASING:
17049 info.order = 1;
17050 break;
17051 case OPT_DECREASING:
17052 info.order = -1;
17053 break;
17054 case OPT_UNIQUE:
17055 info.unique = 1;
17056 break;
17057 case OPT_COMMAND:
17058 if (i >= (argc - 2)) {
17059 Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
17060 return JIM_ERR;
17062 info.type = JIM_LSORT_COMMAND;
17063 info.command = argv[i + 1];
17064 i++;
17065 break;
17066 case OPT_INDEX:
17067 if (i >= (argc - 2)) {
17068 Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
17069 return JIM_ERR;
17071 if (Jim_GetIndex(interp, argv[i + 1], &info.index) != JIM_OK) {
17072 return JIM_ERR;
17074 info.indexed = 1;
17075 i++;
17076 break;
17079 resObj = Jim_DuplicateObj(interp, argv[argc - 1]);
17080 retCode = ListSortElements(interp, resObj, &info);
17081 if (retCode == JIM_OK) {
17082 Jim_SetResult(interp, resObj);
17084 else {
17085 Jim_FreeNewObj(interp, resObj);
17087 return retCode;
17091 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17093 Jim_Obj *stringObjPtr;
17094 int i;
17096 if (argc < 2) {
17097 Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
17098 return JIM_ERR;
17100 if (argc == 2) {
17101 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17102 if (!stringObjPtr)
17103 return JIM_ERR;
17105 else {
17106 int freeobj = 0;
17107 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
17108 if (!stringObjPtr) {
17110 stringObjPtr = Jim_NewEmptyStringObj(interp);
17111 freeobj = 1;
17113 else if (Jim_IsShared(stringObjPtr)) {
17114 freeobj = 1;
17115 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
17117 for (i = 2; i < argc; i++) {
17118 Jim_AppendObj(interp, stringObjPtr, argv[i]);
17120 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
17121 if (freeobj) {
17122 Jim_FreeNewObj(interp, stringObjPtr);
17124 return JIM_ERR;
17127 Jim_SetResult(interp, stringObjPtr);
17128 return JIM_OK;
17132 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17134 #if !defined(JIM_DEBUG_COMMAND)
17135 Jim_SetResultString(interp, "unsupported", -1);
17136 return JIM_ERR;
17137 #endif
17141 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17143 int rc;
17145 if (argc < 2) {
17146 Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
17147 return JIM_ERR;
17150 if (argc == 2) {
17151 rc = Jim_EvalObj(interp, argv[1]);
17153 else {
17154 rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17157 if (rc == JIM_ERR) {
17159 interp->addStackTrace++;
17161 return rc;
17165 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17167 if (argc >= 2) {
17168 int retcode;
17169 Jim_CallFrame *savedCallFrame, *targetCallFrame;
17170 int savedTailcall;
17171 const char *str;
17174 savedCallFrame = interp->framePtr;
17177 str = Jim_String(argv[1]);
17178 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
17179 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17180 argc--;
17181 argv++;
17183 else {
17184 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17186 if (targetCallFrame == NULL) {
17187 return JIM_ERR;
17189 if (argc < 2) {
17190 Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
17191 return JIM_ERR;
17194 interp->framePtr = targetCallFrame;
17196 savedTailcall = interp->framePtr->tailcall;
17197 interp->framePtr->tailcall = 0;
17198 if (argc == 2) {
17199 retcode = Jim_EvalObj(interp, argv[1]);
17201 else {
17202 retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17204 interp->framePtr->tailcall = savedTailcall;
17205 interp->framePtr = savedCallFrame;
17206 return retcode;
17208 else {
17209 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
17210 return JIM_ERR;
17215 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17217 Jim_Obj *exprResultPtr;
17218 int retcode;
17220 if (argc == 2) {
17221 retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
17223 else if (argc > 2) {
17224 Jim_Obj *objPtr;
17226 objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
17227 Jim_IncrRefCount(objPtr);
17228 retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
17229 Jim_DecrRefCount(interp, objPtr);
17231 else {
17232 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
17233 return JIM_ERR;
17235 if (retcode != JIM_OK)
17236 return retcode;
17237 Jim_SetResult(interp, exprResultPtr);
17238 Jim_DecrRefCount(interp, exprResultPtr);
17239 return JIM_OK;
17243 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17245 if (argc != 1) {
17246 Jim_WrongNumArgs(interp, 1, argv, "");
17247 return JIM_ERR;
17249 return JIM_BREAK;
17253 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17255 if (argc != 1) {
17256 Jim_WrongNumArgs(interp, 1, argv, "");
17257 return JIM_ERR;
17259 return JIM_CONTINUE;
17263 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17265 int i;
17266 Jim_Obj *stackTraceObj = NULL;
17267 Jim_Obj *errorCodeObj = NULL;
17268 int returnCode = JIM_OK;
17269 long level = 1;
17271 for (i = 1; i < argc - 1; i += 2) {
17272 if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
17273 if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
17274 return JIM_ERR;
17277 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
17278 stackTraceObj = argv[i + 1];
17280 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
17281 errorCodeObj = argv[i + 1];
17283 else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
17284 if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
17285 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
17286 return JIM_ERR;
17289 else {
17290 break;
17294 if (i != argc - 1 && i != argc) {
17295 Jim_WrongNumArgs(interp, 1, argv,
17296 "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
17300 if (stackTraceObj && returnCode == JIM_ERR) {
17301 JimSetStackTrace(interp, stackTraceObj);
17304 if (errorCodeObj && returnCode == JIM_ERR) {
17305 Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
17307 interp->returnCode = returnCode;
17308 interp->returnLevel = level;
17310 if (i == argc - 1) {
17311 Jim_SetResult(interp, argv[i]);
17313 return JIM_RETURN;
17317 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17319 if (interp->framePtr->level == 0) {
17320 Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
17321 return JIM_ERR;
17323 else if (argc >= 2) {
17325 Jim_CallFrame *cf = interp->framePtr->parent;
17327 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17328 if (cmdPtr == NULL) {
17329 return JIM_ERR;
17332 JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));
17335 JimIncrCmdRefCount(cmdPtr);
17336 cf->tailcallCmd = cmdPtr;
17339 JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));
17341 cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
17342 Jim_IncrRefCount(cf->tailcallObj);
17345 return JIM_EVAL;
17347 return JIM_OK;
17350 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17352 Jim_Obj *cmdList;
17353 Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
17356 cmdList = Jim_DuplicateObj(interp, prefixListObj);
17357 Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
17359 return JimEvalObjList(interp, cmdList);
17362 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
17364 Jim_Obj *prefixListObj = privData;
17365 Jim_DecrRefCount(interp, prefixListObj);
17368 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17370 Jim_Obj *prefixListObj;
17371 const char *newname;
17373 if (argc < 3) {
17374 Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
17375 return JIM_ERR;
17378 prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
17379 Jim_IncrRefCount(prefixListObj);
17380 newname = Jim_String(argv[1]);
17381 if (newname[0] == ':' && newname[1] == ':') {
17382 while (*++newname == ':') {
17386 Jim_SetResult(interp, argv[1]);
17388 return Jim_CreateCommand(interp, newname, JimAliasCmd, prefixListObj, JimAliasCmdDelete);
17392 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17394 Jim_Cmd *cmd;
17396 if (argc != 4 && argc != 5) {
17397 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
17398 return JIM_ERR;
17401 if (JimValidName(interp, "procedure", argv[1]) != JIM_OK) {
17402 return JIM_ERR;
17405 if (argc == 4) {
17406 cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
17408 else {
17409 cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
17412 if (cmd) {
17414 Jim_Obj *qualifiedCmdNameObj;
17415 const char *cmdname = JimQualifyName(interp, Jim_String(argv[1]), &qualifiedCmdNameObj);
17417 JimCreateCommand(interp, cmdname, cmd);
17420 JimUpdateProcNamespace(interp, cmd, cmdname);
17422 JimFreeQualifiedName(interp, qualifiedCmdNameObj);
17425 Jim_SetResult(interp, argv[1]);
17426 return JIM_OK;
17428 return JIM_ERR;
17432 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17434 int retcode;
17436 if (argc < 2) {
17437 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17438 return JIM_ERR;
17442 interp->local++;
17443 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17444 interp->local--;
17448 if (retcode == 0) {
17449 Jim_Obj *cmdNameObj = Jim_GetResult(interp);
17451 if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
17452 return JIM_ERR;
17454 if (interp->framePtr->localCommands == NULL) {
17455 interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
17456 Jim_InitStack(interp->framePtr->localCommands);
17458 Jim_IncrRefCount(cmdNameObj);
17459 Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
17462 return retcode;
17466 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17468 if (argc < 2) {
17469 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17470 return JIM_ERR;
17472 else {
17473 int retcode;
17475 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17476 if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
17477 Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
17478 return JIM_ERR;
17481 cmdPtr->u.proc.upcall++;
17482 JimIncrCmdRefCount(cmdPtr);
17485 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17488 cmdPtr->u.proc.upcall--;
17489 JimDecrCmdRefCount(interp, cmdPtr);
17491 return retcode;
17496 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17498 if (argc < 2) {
17499 Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
17500 return JIM_ERR;
17502 else {
17503 int ret;
17504 Jim_Cmd *cmd;
17505 Jim_Obj *argListObjPtr;
17506 Jim_Obj *bodyObjPtr;
17507 Jim_Obj *nsObj = NULL;
17508 Jim_Obj **nargv;
17510 int len = Jim_ListLength(interp, argv[1]);
17511 if (len != 2 && len != 3) {
17512 Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
17513 return JIM_ERR;
17516 if (len == 3) {
17517 #ifdef jim_ext_namespace
17519 nsObj = JimQualifyNameObj(interp, Jim_ListGetIndex(interp, argv[1], 2));
17520 #else
17521 Jim_SetResultString(interp, "namespaces not enabled", -1);
17522 return JIM_ERR;
17523 #endif
17525 argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
17526 bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
17528 cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
17530 if (cmd) {
17532 nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
17533 nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
17534 Jim_IncrRefCount(nargv[0]);
17535 memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
17536 ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
17537 Jim_DecrRefCount(interp, nargv[0]);
17538 Jim_Free(nargv);
17540 JimDecrCmdRefCount(interp, cmd);
17541 return ret;
17543 return JIM_ERR;
17549 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17551 Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17552 return JIM_OK;
17556 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17558 int i;
17559 Jim_CallFrame *targetCallFrame;
17562 if (argc > 3 && (argc % 2 == 0)) {
17563 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17564 argc--;
17565 argv++;
17567 else {
17568 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17570 if (targetCallFrame == NULL) {
17571 return JIM_ERR;
17575 if (argc < 3) {
17576 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
17577 return JIM_ERR;
17581 for (i = 1; i < argc; i += 2) {
17582 if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
17583 return JIM_ERR;
17585 return JIM_OK;
17589 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17591 int i;
17593 if (argc < 2) {
17594 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
17595 return JIM_ERR;
17598 if (interp->framePtr->level == 0)
17599 return JIM_OK;
17600 for (i = 1; i < argc; i++) {
17602 const char *name = Jim_String(argv[i]);
17603 if (name[0] != ':' || name[1] != ':') {
17604 if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
17605 return JIM_ERR;
17608 return JIM_OK;
17611 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
17612 Jim_Obj *objPtr, int nocase)
17614 int numMaps;
17615 const char *str, *noMatchStart = NULL;
17616 int strLen, i;
17617 Jim_Obj *resultObjPtr;
17619 numMaps = Jim_ListLength(interp, mapListObjPtr);
17620 if (numMaps % 2) {
17621 Jim_SetResultString(interp, "list must contain an even number of elements", -1);
17622 return NULL;
17625 str = Jim_String(objPtr);
17626 strLen = Jim_Utf8Length(interp, objPtr);
17629 resultObjPtr = Jim_NewStringObj(interp, "", 0);
17630 while (strLen) {
17631 for (i = 0; i < numMaps; i += 2) {
17632 Jim_Obj *objPtr;
17633 const char *k;
17634 int kl;
17636 objPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
17637 k = Jim_String(objPtr);
17638 kl = Jim_Utf8Length(interp, objPtr);
17640 if (strLen >= kl && kl) {
17641 int rc;
17642 rc = JimStringCompareLen(str, k, kl, nocase);
17643 if (rc == 0) {
17644 if (noMatchStart) {
17645 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17646 noMatchStart = NULL;
17648 Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
17649 str += utf8_index(str, kl);
17650 strLen -= kl;
17651 break;
17655 if (i == numMaps) {
17656 int c;
17657 if (noMatchStart == NULL)
17658 noMatchStart = str;
17659 str += utf8_tounicode(str, &c);
17660 strLen--;
17663 if (noMatchStart) {
17664 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17666 return resultObjPtr;
17670 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17672 int len;
17673 int opt_case = 1;
17674 int option;
17675 static const char * const options[] = {
17676 "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace",
17677 "map", "repeat", "reverse", "index", "first", "last", "cat",
17678 "trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL
17680 enum
17682 OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_REPLACE,
17683 OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST, OPT_CAT,
17684 OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE
17686 static const char * const nocase_options[] = {
17687 "-nocase", NULL
17689 static const char * const nocase_length_options[] = {
17690 "-nocase", "-length", NULL
17693 if (argc < 2) {
17694 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
17695 return JIM_ERR;
17697 if (Jim_GetEnum(interp, argv[1], options, &option, NULL,
17698 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
17699 return JIM_ERR;
17701 switch (option) {
17702 case OPT_LENGTH:
17703 case OPT_BYTELENGTH:
17704 if (argc != 3) {
17705 Jim_WrongNumArgs(interp, 2, argv, "string");
17706 return JIM_ERR;
17708 if (option == OPT_LENGTH) {
17709 len = Jim_Utf8Length(interp, argv[2]);
17711 else {
17712 len = Jim_Length(argv[2]);
17714 Jim_SetResultInt(interp, len);
17715 return JIM_OK;
17717 case OPT_CAT:{
17718 Jim_Obj *objPtr;
17719 if (argc == 3) {
17721 objPtr = argv[2];
17723 else {
17724 int i;
17726 objPtr = Jim_NewStringObj(interp, "", 0);
17728 for (i = 2; i < argc; i++) {
17729 Jim_AppendObj(interp, objPtr, argv[i]);
17732 Jim_SetResult(interp, objPtr);
17733 return JIM_OK;
17736 case OPT_COMPARE:
17737 case OPT_EQUAL:
17740 long opt_length = -1;
17741 int n = argc - 4;
17742 int i = 2;
17743 while (n > 0) {
17744 int subopt;
17745 if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
17746 JIM_ENUM_ABBREV) != JIM_OK) {
17747 badcompareargs:
17748 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? ?-length int? string1 string2");
17749 return JIM_ERR;
17751 if (subopt == 0) {
17753 opt_case = 0;
17754 n--;
17756 else {
17758 if (n < 2) {
17759 goto badcompareargs;
17761 if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
17762 return JIM_ERR;
17764 n -= 2;
17767 if (n) {
17768 goto badcompareargs;
17770 argv += argc - 2;
17771 if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
17773 Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
17775 else {
17776 if (opt_length >= 0) {
17777 n = JimStringCompareLen(Jim_String(argv[0]), Jim_String(argv[1]), opt_length, !opt_case);
17779 else {
17780 n = Jim_StringCompareObj(interp, argv[0], argv[1], !opt_case);
17782 Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
17784 return JIM_OK;
17787 case OPT_MATCH:
17788 if (argc != 4 &&
17789 (argc != 5 ||
17790 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17791 JIM_ENUM_ABBREV) != JIM_OK)) {
17792 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
17793 return JIM_ERR;
17795 if (opt_case == 0) {
17796 argv++;
17798 Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
17799 return JIM_OK;
17801 case OPT_MAP:{
17802 Jim_Obj *objPtr;
17804 if (argc != 4 &&
17805 (argc != 5 ||
17806 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17807 JIM_ENUM_ABBREV) != JIM_OK)) {
17808 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
17809 return JIM_ERR;
17812 if (opt_case == 0) {
17813 argv++;
17815 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
17816 if (objPtr == NULL) {
17817 return JIM_ERR;
17819 Jim_SetResult(interp, objPtr);
17820 return JIM_OK;
17823 case OPT_RANGE:
17824 case OPT_BYTERANGE:{
17825 Jim_Obj *objPtr;
17827 if (argc != 5) {
17828 Jim_WrongNumArgs(interp, 2, argv, "string first last");
17829 return JIM_ERR;
17831 if (option == OPT_RANGE) {
17832 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
17834 else
17836 objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
17839 if (objPtr == NULL) {
17840 return JIM_ERR;
17842 Jim_SetResult(interp, objPtr);
17843 return JIM_OK;
17846 case OPT_REPLACE:{
17847 Jim_Obj *objPtr;
17849 if (argc != 5 && argc != 6) {
17850 Jim_WrongNumArgs(interp, 2, argv, "string first last ?string?");
17851 return JIM_ERR;
17853 objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
17854 if (objPtr == NULL) {
17855 return JIM_ERR;
17857 Jim_SetResult(interp, objPtr);
17858 return JIM_OK;
17862 case OPT_REPEAT:{
17863 Jim_Obj *objPtr;
17864 jim_wide count;
17866 if (argc != 4) {
17867 Jim_WrongNumArgs(interp, 2, argv, "string count");
17868 return JIM_ERR;
17870 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK) {
17871 return JIM_ERR;
17873 objPtr = Jim_NewStringObj(interp, "", 0);
17874 if (count > 0) {
17875 while (count--) {
17876 Jim_AppendObj(interp, objPtr, argv[2]);
17879 Jim_SetResult(interp, objPtr);
17880 return JIM_OK;
17883 case OPT_REVERSE:{
17884 char *buf, *p;
17885 const char *str;
17886 int len;
17887 int i;
17889 if (argc != 3) {
17890 Jim_WrongNumArgs(interp, 2, argv, "string");
17891 return JIM_ERR;
17894 str = Jim_GetString(argv[2], &len);
17895 buf = Jim_Alloc(len + 1);
17896 p = buf + len;
17897 *p = 0;
17898 for (i = 0; i < len; ) {
17899 int c;
17900 int l = utf8_tounicode(str, &c);
17901 memcpy(p - l, str, l);
17902 p -= l;
17903 i += l;
17904 str += l;
17906 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
17907 return JIM_OK;
17910 case OPT_INDEX:{
17911 int idx;
17912 const char *str;
17914 if (argc != 4) {
17915 Jim_WrongNumArgs(interp, 2, argv, "string index");
17916 return JIM_ERR;
17918 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
17919 return JIM_ERR;
17921 str = Jim_String(argv[2]);
17922 len = Jim_Utf8Length(interp, argv[2]);
17923 if (idx != INT_MIN && idx != INT_MAX) {
17924 idx = JimRelToAbsIndex(len, idx);
17926 if (idx < 0 || idx >= len || str == NULL) {
17927 Jim_SetResultString(interp, "", 0);
17929 else if (len == Jim_Length(argv[2])) {
17931 Jim_SetResultString(interp, str + idx, 1);
17933 else {
17934 int c;
17935 int i = utf8_index(str, idx);
17936 Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
17938 return JIM_OK;
17941 case OPT_FIRST:
17942 case OPT_LAST:{
17943 int idx = 0, l1, l2;
17944 const char *s1, *s2;
17946 if (argc != 4 && argc != 5) {
17947 Jim_WrongNumArgs(interp, 2, argv, "subString string ?index?");
17948 return JIM_ERR;
17950 s1 = Jim_String(argv[2]);
17951 s2 = Jim_String(argv[3]);
17952 l1 = Jim_Utf8Length(interp, argv[2]);
17953 l2 = Jim_Utf8Length(interp, argv[3]);
17954 if (argc == 5) {
17955 if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
17956 return JIM_ERR;
17958 idx = JimRelToAbsIndex(l2, idx);
17960 else if (option == OPT_LAST) {
17961 idx = l2;
17963 if (option == OPT_FIRST) {
17964 Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
17966 else {
17967 #ifdef JIM_UTF8
17968 Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
17969 #else
17970 Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
17971 #endif
17973 return JIM_OK;
17976 case OPT_TRIM:
17977 case OPT_TRIMLEFT:
17978 case OPT_TRIMRIGHT:{
17979 Jim_Obj *trimchars;
17981 if (argc != 3 && argc != 4) {
17982 Jim_WrongNumArgs(interp, 2, argv, "string ?trimchars?");
17983 return JIM_ERR;
17985 trimchars = (argc == 4 ? argv[3] : NULL);
17986 if (option == OPT_TRIM) {
17987 Jim_SetResult(interp, JimStringTrim(interp, argv[2], trimchars));
17989 else if (option == OPT_TRIMLEFT) {
17990 Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], trimchars));
17992 else if (option == OPT_TRIMRIGHT) {
17993 Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], trimchars));
17995 return JIM_OK;
17998 case OPT_TOLOWER:
17999 case OPT_TOUPPER:
18000 case OPT_TOTITLE:
18001 if (argc != 3) {
18002 Jim_WrongNumArgs(interp, 2, argv, "string");
18003 return JIM_ERR;
18005 if (option == OPT_TOLOWER) {
18006 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
18008 else if (option == OPT_TOUPPER) {
18009 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
18011 else {
18012 Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
18014 return JIM_OK;
18016 case OPT_IS:
18017 if (argc == 4 || (argc == 5 && Jim_CompareStringImmediate(interp, argv[3], "-strict"))) {
18018 return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
18020 Jim_WrongNumArgs(interp, 2, argv, "class ?-strict? str");
18021 return JIM_ERR;
18023 return JIM_OK;
18027 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18029 long i, count = 1;
18030 jim_wide start, elapsed;
18031 char buf[60];
18032 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
18034 if (argc < 2) {
18035 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
18036 return JIM_ERR;
18038 if (argc == 3) {
18039 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
18040 return JIM_ERR;
18042 if (count < 0)
18043 return JIM_OK;
18044 i = count;
18045 start = JimClock();
18046 while (i-- > 0) {
18047 int retval;
18049 retval = Jim_EvalObj(interp, argv[1]);
18050 if (retval != JIM_OK) {
18051 return retval;
18054 elapsed = JimClock() - start;
18055 sprintf(buf, fmt, count == 0 ? 0 : elapsed / count);
18056 Jim_SetResultString(interp, buf, -1);
18057 return JIM_OK;
18061 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18063 long exitCode = 0;
18065 if (argc > 2) {
18066 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
18067 return JIM_ERR;
18069 if (argc == 2) {
18070 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
18071 return JIM_ERR;
18073 interp->exitCode = exitCode;
18074 return JIM_EXIT;
18078 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18080 int exitCode = 0;
18081 int i;
18082 int sig = 0;
18085 jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
18086 static const int max_ignore_code = sizeof(ignore_mask) * 8;
18088 Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
18090 for (i = 1; i < argc - 1; i++) {
18091 const char *arg = Jim_String(argv[i]);
18092 jim_wide option;
18093 int ignore;
18096 if (strcmp(arg, "--") == 0) {
18097 i++;
18098 break;
18100 if (*arg != '-') {
18101 break;
18104 if (strncmp(arg, "-no", 3) == 0) {
18105 arg += 3;
18106 ignore = 1;
18108 else {
18109 arg++;
18110 ignore = 0;
18113 if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
18114 option = -1;
18116 if (option < 0) {
18117 option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
18119 if (option < 0) {
18120 goto wrongargs;
18123 if (ignore) {
18124 ignore_mask |= (1 << option);
18126 else {
18127 ignore_mask &= ~(1 << option);
18131 argc -= i;
18132 if (argc < 1 || argc > 3) {
18133 wrongargs:
18134 Jim_WrongNumArgs(interp, 1, argv,
18135 "?-?no?code ... --? script ?resultVarName? ?optionVarName?");
18136 return JIM_ERR;
18138 argv += i;
18140 if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
18141 sig++;
18144 interp->signal_level += sig;
18145 if (Jim_CheckSignal(interp)) {
18147 exitCode = JIM_SIGNAL;
18149 else {
18150 exitCode = Jim_EvalObj(interp, argv[0]);
18152 interp->errorFlag = 0;
18154 interp->signal_level -= sig;
18157 if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
18159 return exitCode;
18162 if (sig && exitCode == JIM_SIGNAL) {
18164 if (interp->signal_set_result) {
18165 interp->signal_set_result(interp, interp->sigmask);
18167 else {
18168 Jim_SetResultInt(interp, interp->sigmask);
18170 interp->sigmask = 0;
18173 if (argc >= 2) {
18174 if (Jim_SetVariable(interp, argv[1], Jim_GetResult(interp)) != JIM_OK) {
18175 return JIM_ERR;
18177 if (argc == 3) {
18178 Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
18180 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
18181 Jim_ListAppendElement(interp, optListObj,
18182 Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
18183 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
18184 Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
18185 if (exitCode == JIM_ERR) {
18186 Jim_Obj *errorCode;
18187 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
18188 -1));
18189 Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
18191 errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
18192 if (errorCode) {
18193 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
18194 Jim_ListAppendElement(interp, optListObj, errorCode);
18197 if (Jim_SetVariable(interp, argv[2], optListObj) != JIM_OK) {
18198 return JIM_ERR;
18202 Jim_SetResultInt(interp, exitCode);
18203 return JIM_OK;
18206 #ifdef JIM_REFERENCES
18209 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18211 if (argc != 3 && argc != 4) {
18212 Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
18213 return JIM_ERR;
18215 if (argc == 3) {
18216 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
18218 else {
18219 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], argv[3]));
18221 return JIM_OK;
18225 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18227 Jim_Reference *refPtr;
18229 if (argc != 2) {
18230 Jim_WrongNumArgs(interp, 1, argv, "reference");
18231 return JIM_ERR;
18233 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
18234 return JIM_ERR;
18235 Jim_SetResult(interp, refPtr->objPtr);
18236 return JIM_OK;
18240 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18242 Jim_Reference *refPtr;
18244 if (argc != 3) {
18245 Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
18246 return JIM_ERR;
18248 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
18249 return JIM_ERR;
18250 Jim_IncrRefCount(argv[2]);
18251 Jim_DecrRefCount(interp, refPtr->objPtr);
18252 refPtr->objPtr = argv[2];
18253 Jim_SetResult(interp, argv[2]);
18254 return JIM_OK;
18258 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18260 if (argc != 1) {
18261 Jim_WrongNumArgs(interp, 1, argv, "");
18262 return JIM_ERR;
18264 Jim_SetResultInt(interp, Jim_Collect(interp));
18267 while (interp->freeList) {
18268 Jim_Obj *nextObjPtr = interp->freeList->nextObjPtr;
18269 Jim_Free(interp->freeList);
18270 interp->freeList = nextObjPtr;
18273 return JIM_OK;
18277 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18279 if (argc != 2 && argc != 3) {
18280 Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
18281 return JIM_ERR;
18283 if (argc == 2) {
18284 Jim_Obj *cmdNamePtr;
18286 if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
18287 return JIM_ERR;
18288 if (cmdNamePtr != NULL)
18289 Jim_SetResult(interp, cmdNamePtr);
18291 else {
18292 if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
18293 return JIM_ERR;
18294 Jim_SetResult(interp, argv[2]);
18296 return JIM_OK;
18300 static int JimInfoReferences(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18302 Jim_Obj *listObjPtr;
18303 Jim_HashTableIterator htiter;
18304 Jim_HashEntry *he;
18306 listObjPtr = Jim_NewListObj(interp, NULL, 0);
18308 JimInitHashTableIterator(&interp->references, &htiter);
18309 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18310 char buf[JIM_REFERENCE_SPACE + 1];
18311 Jim_Reference *refPtr = Jim_GetHashEntryVal(he);
18312 const unsigned long *refId = he->key;
18314 JimFormatReference(buf, refPtr, *refId);
18315 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, buf, -1));
18317 Jim_SetResult(interp, listObjPtr);
18318 return JIM_OK;
18320 #endif
18323 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18325 if (argc != 3) {
18326 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
18327 return JIM_ERR;
18330 if (JimValidName(interp, "new procedure", argv[2])) {
18331 return JIM_ERR;
18334 return Jim_RenameCommand(interp, Jim_String(argv[1]), Jim_String(argv[2]));
18337 #define JIM_DICTMATCH_VALUES 0x0001
18339 typedef void JimDictMatchCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type);
18341 static void JimDictMatchKeys(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type)
18343 Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key);
18344 if (type & JIM_DICTMATCH_VALUES) {
18345 Jim_ListAppendElement(interp, listObjPtr, Jim_GetHashEntryVal(he));
18349 static Jim_Obj *JimDictPatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
18350 JimDictMatchCallbackType *callback, int type)
18352 Jim_HashEntry *he;
18353 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
18356 Jim_HashTableIterator htiter;
18357 JimInitHashTableIterator(ht, &htiter);
18358 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18359 if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), Jim_String((Jim_Obj *)he->key), 0)) {
18360 callback(interp, listObjPtr, he, type);
18364 return listObjPtr;
18368 int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObjPtr)
18370 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18371 return JIM_ERR;
18373 Jim_SetResult(interp, JimDictPatternMatch(interp, objPtr->internalRep.ptr, patternObjPtr, JimDictMatchKeys, 0));
18374 return JIM_OK;
18377 int Jim_DictValues(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObjPtr)
18379 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18380 return JIM_ERR;
18382 Jim_SetResult(interp, JimDictPatternMatch(interp, objPtr->internalRep.ptr, patternObjPtr, JimDictMatchKeys, JIM_DICTMATCH_VALUES));
18383 return JIM_OK;
18386 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
18388 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18389 return -1;
18391 return ((Jim_HashTable *)objPtr->internalRep.ptr)->used;
18394 int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
18396 Jim_HashTable *ht;
18397 unsigned int i;
18399 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18400 return JIM_ERR;
18403 ht = (Jim_HashTable *)objPtr->internalRep.ptr;
18406 printf("%d entries in table, %d buckets\n", ht->used, ht->size);
18408 for (i = 0; i < ht->size; i++) {
18409 Jim_HashEntry *he = ht->table[i];
18411 if (he) {
18412 printf("%d: ", i);
18414 while (he) {
18415 printf(" %s", Jim_String(he->key));
18416 he = he->next;
18418 printf("\n");
18421 return JIM_OK;
18424 static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
18426 Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
18428 Jim_AppendString(interp, prefixObj, " ", 1);
18429 Jim_AppendString(interp, prefixObj, subcmd, -1);
18431 return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
18435 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18437 Jim_Obj *objPtr;
18438 int option;
18439 static const char * const options[] = {
18440 "create", "get", "set", "unset", "exists", "keys", "size", "info",
18441 "merge", "with", "append", "lappend", "incr", "remove", "values", "for",
18442 "replace", "update", NULL
18444 enum
18446 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXISTS, OPT_KEYS, OPT_SIZE, OPT_INFO,
18447 OPT_MERGE, OPT_WITH, OPT_APPEND, OPT_LAPPEND, OPT_INCR, OPT_REMOVE, OPT_VALUES, OPT_FOR,
18448 OPT_REPLACE, OPT_UPDATE,
18451 if (argc < 2) {
18452 Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arguments ...?");
18453 return JIM_ERR;
18456 if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) {
18457 return JIM_ERR;
18460 switch (option) {
18461 case OPT_GET:
18462 if (argc < 3) {
18463 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?key ...?");
18464 return JIM_ERR;
18466 if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
18467 JIM_ERRMSG) != JIM_OK) {
18468 return JIM_ERR;
18470 Jim_SetResult(interp, objPtr);
18471 return JIM_OK;
18473 case OPT_SET:
18474 if (argc < 5) {
18475 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
18476 return JIM_ERR;
18478 return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
18480 case OPT_EXISTS:
18481 if (argc < 4) {
18482 Jim_WrongNumArgs(interp, 2, argv, "dictionary key ?key ...?");
18483 return JIM_ERR;
18485 else {
18486 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_ERRMSG);
18487 if (rc < 0) {
18488 return JIM_ERR;
18490 Jim_SetResultBool(interp, rc == JIM_OK);
18491 return JIM_OK;
18494 case OPT_UNSET:
18495 if (argc < 4) {
18496 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
18497 return JIM_ERR;
18499 if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, 0) != JIM_OK) {
18500 return JIM_ERR;
18502 return JIM_OK;
18504 case OPT_KEYS:
18505 if (argc != 3 && argc != 4) {
18506 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?pattern?");
18507 return JIM_ERR;
18509 return Jim_DictKeys(interp, argv[2], argc == 4 ? argv[3] : NULL);
18511 case OPT_SIZE:
18512 if (argc != 3) {
18513 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18514 return JIM_ERR;
18516 else if (Jim_DictSize(interp, argv[2]) < 0) {
18517 return JIM_ERR;
18519 Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
18520 return JIM_OK;
18522 case OPT_MERGE:
18523 if (argc == 2) {
18524 return JIM_OK;
18526 if (Jim_DictSize(interp, argv[2]) < 0) {
18527 return JIM_ERR;
18530 break;
18532 case OPT_UPDATE:
18533 if (argc < 6 || argc % 2) {
18535 argc = 2;
18537 break;
18539 case OPT_CREATE:
18540 if (argc % 2) {
18541 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
18542 return JIM_ERR;
18544 objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
18545 Jim_SetResult(interp, objPtr);
18546 return JIM_OK;
18548 case OPT_INFO:
18549 if (argc != 3) {
18550 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18551 return JIM_ERR;
18553 return Jim_DictInfo(interp, argv[2]);
18556 return Jim_EvalEnsemble(interp, "dict", options[option], argc - 2, argv + 2);
18560 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18562 static const char * const options[] = {
18563 "-nobackslashes", "-nocommands", "-novariables", NULL
18565 enum
18566 { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
18567 int i;
18568 int flags = JIM_SUBST_FLAG;
18569 Jim_Obj *objPtr;
18571 if (argc < 2) {
18572 Jim_WrongNumArgs(interp, 1, argv, "?options? string");
18573 return JIM_ERR;
18575 for (i = 1; i < (argc - 1); i++) {
18576 int option;
18578 if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
18579 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18580 return JIM_ERR;
18582 switch (option) {
18583 case OPT_NOBACKSLASHES:
18584 flags |= JIM_SUBST_NOESC;
18585 break;
18586 case OPT_NOCOMMANDS:
18587 flags |= JIM_SUBST_NOCMD;
18588 break;
18589 case OPT_NOVARIABLES:
18590 flags |= JIM_SUBST_NOVAR;
18591 break;
18594 if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
18595 return JIM_ERR;
18597 Jim_SetResult(interp, objPtr);
18598 return JIM_OK;
18602 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18604 int cmd;
18605 Jim_Obj *objPtr;
18606 int mode = 0;
18608 static const char * const commands[] = {
18609 "body", "statics", "commands", "procs", "channels", "exists", "globals", "level", "frame", "locals",
18610 "vars", "version", "patchlevel", "complete", "args", "hostname",
18611 "script", "source", "stacktrace", "nameofexecutable", "returncodes",
18612 "references", "alias", NULL
18614 enum
18615 { INFO_BODY, INFO_STATICS, INFO_COMMANDS, INFO_PROCS, INFO_CHANNELS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
18616 INFO_FRAME, INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_PATCHLEVEL, INFO_COMPLETE, INFO_ARGS,
18617 INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE,
18618 INFO_RETURNCODES, INFO_REFERENCES, INFO_ALIAS,
18621 #ifdef jim_ext_namespace
18622 int nons = 0;
18624 if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
18626 argc--;
18627 argv++;
18628 nons = 1;
18630 #endif
18632 if (argc < 2) {
18633 Jim_WrongNumArgs(interp, 1, argv, "subcommand ?args ...?");
18634 return JIM_ERR;
18636 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV)
18637 != JIM_OK) {
18638 return JIM_ERR;
18642 switch (cmd) {
18643 case INFO_EXISTS:
18644 if (argc != 3) {
18645 Jim_WrongNumArgs(interp, 2, argv, "varName");
18646 return JIM_ERR;
18648 Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
18649 break;
18651 case INFO_ALIAS:{
18652 Jim_Cmd *cmdPtr;
18654 if (argc != 3) {
18655 Jim_WrongNumArgs(interp, 2, argv, "command");
18656 return JIM_ERR;
18658 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18659 return JIM_ERR;
18661 if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
18662 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
18663 return JIM_ERR;
18665 Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
18666 return JIM_OK;
18669 case INFO_CHANNELS:
18670 mode++;
18671 #ifndef jim_ext_aio
18672 Jim_SetResultString(interp, "aio not enabled", -1);
18673 return JIM_ERR;
18674 #endif
18675 case INFO_PROCS:
18676 mode++;
18677 case INFO_COMMANDS:
18679 if (argc != 2 && argc != 3) {
18680 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18681 return JIM_ERR;
18683 #ifdef jim_ext_namespace
18684 if (!nons) {
18685 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18686 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18689 #endif
18690 Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
18691 break;
18693 case INFO_VARS:
18694 mode++;
18695 case INFO_LOCALS:
18696 mode++;
18697 case INFO_GLOBALS:
18699 if (argc != 2 && argc != 3) {
18700 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18701 return JIM_ERR;
18703 #ifdef jim_ext_namespace
18704 if (!nons) {
18705 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18706 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18709 #endif
18710 Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
18711 break;
18713 case INFO_SCRIPT:
18714 if (argc != 2) {
18715 Jim_WrongNumArgs(interp, 2, argv, "");
18716 return JIM_ERR;
18718 Jim_SetResult(interp, JimGetScript(interp, interp->currentScriptObj)->fileNameObj);
18719 break;
18721 case INFO_SOURCE:{
18722 jim_wide line;
18723 Jim_Obj *resObjPtr;
18724 Jim_Obj *fileNameObj;
18726 if (argc != 3 && argc != 5) {
18727 Jim_WrongNumArgs(interp, 2, argv, "source ?filename line?");
18728 return JIM_ERR;
18730 if (argc == 5) {
18731 if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
18732 return JIM_ERR;
18734 resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
18735 JimSetSourceInfo(interp, resObjPtr, argv[3], line);
18737 else {
18738 if (argv[2]->typePtr == &sourceObjType) {
18739 fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj;
18740 line = argv[2]->internalRep.sourceValue.lineNumber;
18742 else if (argv[2]->typePtr == &scriptObjType) {
18743 ScriptObj *script = JimGetScript(interp, argv[2]);
18744 fileNameObj = script->fileNameObj;
18745 line = script->firstline;
18747 else {
18748 fileNameObj = interp->emptyObj;
18749 line = 1;
18751 resObjPtr = Jim_NewListObj(interp, NULL, 0);
18752 Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
18753 Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
18755 Jim_SetResult(interp, resObjPtr);
18756 break;
18759 case INFO_STACKTRACE:
18760 Jim_SetResult(interp, interp->stackTrace);
18761 break;
18763 case INFO_LEVEL:
18764 case INFO_FRAME:
18765 switch (argc) {
18766 case 2:
18767 Jim_SetResultInt(interp, interp->framePtr->level);
18768 break;
18770 case 3:
18771 if (JimInfoLevel(interp, argv[2], &objPtr, cmd == INFO_LEVEL) != JIM_OK) {
18772 return JIM_ERR;
18774 Jim_SetResult(interp, objPtr);
18775 break;
18777 default:
18778 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
18779 return JIM_ERR;
18781 break;
18783 case INFO_BODY:
18784 case INFO_STATICS:
18785 case INFO_ARGS:{
18786 Jim_Cmd *cmdPtr;
18788 if (argc != 3) {
18789 Jim_WrongNumArgs(interp, 2, argv, "procname");
18790 return JIM_ERR;
18792 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18793 return JIM_ERR;
18795 if (!cmdPtr->isproc) {
18796 Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
18797 return JIM_ERR;
18799 switch (cmd) {
18800 case INFO_BODY:
18801 Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
18802 break;
18803 case INFO_ARGS:
18804 Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
18805 break;
18806 case INFO_STATICS:
18807 if (cmdPtr->u.proc.staticVars) {
18808 int mode = JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES;
18809 Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
18810 NULL, JimVariablesMatch, mode));
18812 break;
18814 break;
18817 case INFO_VERSION:
18818 case INFO_PATCHLEVEL:{
18819 char buf[(JIM_INTEGER_SPACE * 2) + 1];
18821 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
18822 Jim_SetResultString(interp, buf, -1);
18823 break;
18826 case INFO_COMPLETE:
18827 if (argc != 3 && argc != 4) {
18828 Jim_WrongNumArgs(interp, 2, argv, "script ?missing?");
18829 return JIM_ERR;
18831 else {
18832 int len;
18833 const char *s = Jim_GetString(argv[2], &len);
18834 char missing;
18836 Jim_SetResultBool(interp, Jim_ScriptIsComplete(s, len, &missing));
18837 if (missing != ' ' && argc == 4) {
18838 Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
18841 break;
18843 case INFO_HOSTNAME:
18845 return Jim_Eval(interp, "os.gethostname");
18847 case INFO_NAMEOFEXECUTABLE:
18849 return Jim_Eval(interp, "{info nameofexecutable}");
18851 case INFO_RETURNCODES:
18852 if (argc == 2) {
18853 int i;
18854 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
18856 for (i = 0; jimReturnCodes[i]; i++) {
18857 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
18858 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
18859 jimReturnCodes[i], -1));
18862 Jim_SetResult(interp, listObjPtr);
18864 else if (argc == 3) {
18865 long code;
18866 const char *name;
18868 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
18869 return JIM_ERR;
18871 name = Jim_ReturnCode(code);
18872 if (*name == '?') {
18873 Jim_SetResultInt(interp, code);
18875 else {
18876 Jim_SetResultString(interp, name, -1);
18879 else {
18880 Jim_WrongNumArgs(interp, 2, argv, "?code?");
18881 return JIM_ERR;
18883 break;
18884 case INFO_REFERENCES:
18885 #ifdef JIM_REFERENCES
18886 return JimInfoReferences(interp, argc, argv);
18887 #else
18888 Jim_SetResultString(interp, "not supported", -1);
18889 return JIM_ERR;
18890 #endif
18892 return JIM_OK;
18896 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18898 Jim_Obj *objPtr;
18899 int result = 0;
18901 static const char * const options[] = {
18902 "-command", "-proc", "-alias", "-var", NULL
18904 enum
18906 OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
18908 int option;
18910 if (argc == 2) {
18911 option = OPT_VAR;
18912 objPtr = argv[1];
18914 else if (argc == 3) {
18915 if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18916 return JIM_ERR;
18918 objPtr = argv[2];
18920 else {
18921 Jim_WrongNumArgs(interp, 1, argv, "?option? name");
18922 return JIM_ERR;
18925 if (option == OPT_VAR) {
18926 result = Jim_GetVariable(interp, objPtr, 0) != NULL;
18928 else {
18930 Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
18932 if (cmd) {
18933 switch (option) {
18934 case OPT_COMMAND:
18935 result = 1;
18936 break;
18938 case OPT_ALIAS:
18939 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
18940 break;
18942 case OPT_PROC:
18943 result = cmd->isproc;
18944 break;
18948 Jim_SetResultBool(interp, result);
18949 return JIM_OK;
18953 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18955 const char *str, *splitChars, *noMatchStart;
18956 int splitLen, strLen;
18957 Jim_Obj *resObjPtr;
18958 int c;
18959 int len;
18961 if (argc != 2 && argc != 3) {
18962 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
18963 return JIM_ERR;
18966 str = Jim_GetString(argv[1], &len);
18967 if (len == 0) {
18968 return JIM_OK;
18970 strLen = Jim_Utf8Length(interp, argv[1]);
18973 if (argc == 2) {
18974 splitChars = " \n\t\r";
18975 splitLen = 4;
18977 else {
18978 splitChars = Jim_String(argv[2]);
18979 splitLen = Jim_Utf8Length(interp, argv[2]);
18982 noMatchStart = str;
18983 resObjPtr = Jim_NewListObj(interp, NULL, 0);
18986 if (splitLen) {
18987 Jim_Obj *objPtr;
18988 while (strLen--) {
18989 const char *sc = splitChars;
18990 int scLen = splitLen;
18991 int sl = utf8_tounicode(str, &c);
18992 while (scLen--) {
18993 int pc;
18994 sc += utf8_tounicode(sc, &pc);
18995 if (c == pc) {
18996 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
18997 Jim_ListAppendElement(interp, resObjPtr, objPtr);
18998 noMatchStart = str + sl;
18999 break;
19002 str += sl;
19004 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
19005 Jim_ListAppendElement(interp, resObjPtr, objPtr);
19007 else {
19008 Jim_Obj **commonObj = NULL;
19009 #define NUM_COMMON (128 - 9)
19010 while (strLen--) {
19011 int n = utf8_tounicode(str, &c);
19012 #ifdef JIM_OPTIMIZATION
19013 if (c >= 9 && c < 128) {
19015 c -= 9;
19016 if (!commonObj) {
19017 commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
19018 memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
19020 if (!commonObj[c]) {
19021 commonObj[c] = Jim_NewStringObj(interp, str, 1);
19023 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
19024 str++;
19025 continue;
19027 #endif
19028 Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
19029 str += n;
19031 Jim_Free(commonObj);
19034 Jim_SetResult(interp, resObjPtr);
19035 return JIM_OK;
19039 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19041 const char *joinStr;
19042 int joinStrLen;
19044 if (argc != 2 && argc != 3) {
19045 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
19046 return JIM_ERR;
19049 if (argc == 2) {
19050 joinStr = " ";
19051 joinStrLen = 1;
19053 else {
19054 joinStr = Jim_GetString(argv[2], &joinStrLen);
19056 Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
19057 return JIM_OK;
19061 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19063 Jim_Obj *objPtr;
19065 if (argc < 2) {
19066 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
19067 return JIM_ERR;
19069 objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
19070 if (objPtr == NULL)
19071 return JIM_ERR;
19072 Jim_SetResult(interp, objPtr);
19073 return JIM_OK;
19077 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19079 Jim_Obj *listPtr, **outVec;
19080 int outc, i;
19082 if (argc < 3) {
19083 Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
19084 return JIM_ERR;
19086 if (argv[2]->typePtr != &scanFmtStringObjType)
19087 SetScanFmtFromAny(interp, argv[2]);
19088 if (FormatGetError(argv[2]) != 0) {
19089 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
19090 return JIM_ERR;
19092 if (argc > 3) {
19093 int maxPos = FormatGetMaxPos(argv[2]);
19094 int count = FormatGetCnvCount(argv[2]);
19096 if (maxPos > argc - 3) {
19097 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
19098 return JIM_ERR;
19100 else if (count > argc - 3) {
19101 Jim_SetResultString(interp, "different numbers of variable names and "
19102 "field specifiers", -1);
19103 return JIM_ERR;
19105 else if (count < argc - 3) {
19106 Jim_SetResultString(interp, "variable is not assigned by any "
19107 "conversion specifiers", -1);
19108 return JIM_ERR;
19111 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
19112 if (listPtr == 0)
19113 return JIM_ERR;
19114 if (argc > 3) {
19115 int rc = JIM_OK;
19116 int count = 0;
19118 if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
19119 int len = Jim_ListLength(interp, listPtr);
19121 if (len != 0) {
19122 JimListGetElements(interp, listPtr, &outc, &outVec);
19123 for (i = 0; i < outc; ++i) {
19124 if (Jim_Length(outVec[i]) > 0) {
19125 ++count;
19126 if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
19127 rc = JIM_ERR;
19132 Jim_FreeNewObj(interp, listPtr);
19134 else {
19135 count = -1;
19137 if (rc == JIM_OK) {
19138 Jim_SetResultInt(interp, count);
19140 return rc;
19142 else {
19143 if (listPtr == (Jim_Obj *)EOF) {
19144 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
19145 return JIM_OK;
19147 Jim_SetResult(interp, listPtr);
19149 return JIM_OK;
19153 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19155 if (argc != 2 && argc != 3) {
19156 Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
19157 return JIM_ERR;
19159 Jim_SetResult(interp, argv[1]);
19160 if (argc == 3) {
19161 JimSetStackTrace(interp, argv[2]);
19162 return JIM_ERR;
19164 interp->addStackTrace++;
19165 return JIM_ERR;
19169 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19171 Jim_Obj *objPtr;
19173 if (argc != 4) {
19174 Jim_WrongNumArgs(interp, 1, argv, "list first last");
19175 return JIM_ERR;
19177 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
19178 return JIM_ERR;
19179 Jim_SetResult(interp, objPtr);
19180 return JIM_OK;
19184 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19186 Jim_Obj *objPtr;
19187 long count;
19189 if (argc < 2 || Jim_GetLong(interp, argv[1], &count) != JIM_OK || count < 0) {
19190 Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
19191 return JIM_ERR;
19194 if (count == 0 || argc == 2) {
19195 return JIM_OK;
19198 argc -= 2;
19199 argv += 2;
19201 objPtr = Jim_NewListObj(interp, argv, argc);
19202 while (--count) {
19203 ListInsertElements(objPtr, -1, argc, argv);
19206 Jim_SetResult(interp, objPtr);
19207 return JIM_OK;
19210 char **Jim_GetEnviron(void)
19212 #if defined(HAVE__NSGETENVIRON)
19213 return *_NSGetEnviron();
19214 #else
19215 #if !defined(NO_ENVIRON_EXTERN)
19216 extern char **environ;
19217 #endif
19219 return environ;
19220 #endif
19223 void Jim_SetEnviron(char **env)
19225 #if defined(HAVE__NSGETENVIRON)
19226 *_NSGetEnviron() = env;
19227 #else
19228 #if !defined(NO_ENVIRON_EXTERN)
19229 extern char **environ;
19230 #endif
19232 environ = env;
19233 #endif
19237 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19239 const char *key;
19240 const char *val;
19242 if (argc == 1) {
19243 char **e = Jim_GetEnviron();
19245 int i;
19246 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
19248 for (i = 0; e[i]; i++) {
19249 const char *equals = strchr(e[i], '=');
19251 if (equals) {
19252 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
19253 equals - e[i]));
19254 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
19258 Jim_SetResult(interp, listObjPtr);
19259 return JIM_OK;
19262 if (argc < 2) {
19263 Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
19264 return JIM_ERR;
19266 key = Jim_String(argv[1]);
19267 val = getenv(key);
19268 if (val == NULL) {
19269 if (argc < 3) {
19270 Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
19271 return JIM_ERR;
19273 val = Jim_String(argv[2]);
19275 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
19276 return JIM_OK;
19280 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19282 int retval;
19284 if (argc != 2) {
19285 Jim_WrongNumArgs(interp, 1, argv, "fileName");
19286 return JIM_ERR;
19288 retval = Jim_EvalFile(interp, Jim_String(argv[1]));
19289 if (retval == JIM_RETURN)
19290 return JIM_OK;
19291 return retval;
19295 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19297 Jim_Obj *revObjPtr, **ele;
19298 int len;
19300 if (argc != 2) {
19301 Jim_WrongNumArgs(interp, 1, argv, "list");
19302 return JIM_ERR;
19304 JimListGetElements(interp, argv[1], &len, &ele);
19305 len--;
19306 revObjPtr = Jim_NewListObj(interp, NULL, 0);
19307 while (len >= 0)
19308 ListAppendElement(revObjPtr, ele[len--]);
19309 Jim_SetResult(interp, revObjPtr);
19310 return JIM_OK;
19313 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
19315 jim_wide len;
19317 if (step == 0)
19318 return -1;
19319 if (start == end)
19320 return 0;
19321 else if (step > 0 && start > end)
19322 return -1;
19323 else if (step < 0 && end > start)
19324 return -1;
19325 len = end - start;
19326 if (len < 0)
19327 len = -len;
19328 if (step < 0)
19329 step = -step;
19330 len = 1 + ((len - 1) / step);
19331 if (len > INT_MAX)
19332 len = INT_MAX;
19333 return (int)((len < 0) ? -1 : len);
19337 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19339 jim_wide start = 0, end, step = 1;
19340 int len, i;
19341 Jim_Obj *objPtr;
19343 if (argc < 2 || argc > 4) {
19344 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
19345 return JIM_ERR;
19347 if (argc == 2) {
19348 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
19349 return JIM_ERR;
19351 else {
19352 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
19353 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
19354 return JIM_ERR;
19355 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
19356 return JIM_ERR;
19358 if ((len = JimRangeLen(start, end, step)) == -1) {
19359 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
19360 return JIM_ERR;
19362 objPtr = Jim_NewListObj(interp, NULL, 0);
19363 for (i = 0; i < len; i++)
19364 ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
19365 Jim_SetResult(interp, objPtr);
19366 return JIM_OK;
19370 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19372 jim_wide min = 0, max = 0, len, maxMul;
19374 if (argc < 1 || argc > 3) {
19375 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
19376 return JIM_ERR;
19378 if (argc == 1) {
19379 max = JIM_WIDE_MAX;
19380 } else if (argc == 2) {
19381 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
19382 return JIM_ERR;
19383 } else if (argc == 3) {
19384 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
19385 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
19386 return JIM_ERR;
19388 len = max-min;
19389 if (len < 0) {
19390 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
19391 return JIM_ERR;
19393 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
19394 while (1) {
19395 jim_wide r;
19397 JimRandomBytes(interp, &r, sizeof(jim_wide));
19398 if (r < 0 || r >= maxMul) continue;
19399 r = (len == 0) ? 0 : r%len;
19400 Jim_SetResultInt(interp, min+r);
19401 return JIM_OK;
19405 static const struct {
19406 const char *name;
19407 Jim_CmdProc *cmdProc;
19408 } Jim_CoreCommandsTable[] = {
19409 {"alias", Jim_AliasCoreCommand},
19410 {"set", Jim_SetCoreCommand},
19411 {"unset", Jim_UnsetCoreCommand},
19412 {"puts", Jim_PutsCoreCommand},
19413 {"+", Jim_AddCoreCommand},
19414 {"*", Jim_MulCoreCommand},
19415 {"-", Jim_SubCoreCommand},
19416 {"/", Jim_DivCoreCommand},
19417 {"incr", Jim_IncrCoreCommand},
19418 {"while", Jim_WhileCoreCommand},
19419 {"loop", Jim_LoopCoreCommand},
19420 {"for", Jim_ForCoreCommand},
19421 {"foreach", Jim_ForeachCoreCommand},
19422 {"lmap", Jim_LmapCoreCommand},
19423 {"lassign", Jim_LassignCoreCommand},
19424 {"if", Jim_IfCoreCommand},
19425 {"switch", Jim_SwitchCoreCommand},
19426 {"list", Jim_ListCoreCommand},
19427 {"lindex", Jim_LindexCoreCommand},
19428 {"lset", Jim_LsetCoreCommand},
19429 {"lsearch", Jim_LsearchCoreCommand},
19430 {"llength", Jim_LlengthCoreCommand},
19431 {"lappend", Jim_LappendCoreCommand},
19432 {"linsert", Jim_LinsertCoreCommand},
19433 {"lreplace", Jim_LreplaceCoreCommand},
19434 {"lsort", Jim_LsortCoreCommand},
19435 {"append", Jim_AppendCoreCommand},
19436 {"debug", Jim_DebugCoreCommand},
19437 {"eval", Jim_EvalCoreCommand},
19438 {"uplevel", Jim_UplevelCoreCommand},
19439 {"expr", Jim_ExprCoreCommand},
19440 {"break", Jim_BreakCoreCommand},
19441 {"continue", Jim_ContinueCoreCommand},
19442 {"proc", Jim_ProcCoreCommand},
19443 {"concat", Jim_ConcatCoreCommand},
19444 {"return", Jim_ReturnCoreCommand},
19445 {"upvar", Jim_UpvarCoreCommand},
19446 {"global", Jim_GlobalCoreCommand},
19447 {"string", Jim_StringCoreCommand},
19448 {"time", Jim_TimeCoreCommand},
19449 {"exit", Jim_ExitCoreCommand},
19450 {"catch", Jim_CatchCoreCommand},
19451 #ifdef JIM_REFERENCES
19452 {"ref", Jim_RefCoreCommand},
19453 {"getref", Jim_GetrefCoreCommand},
19454 {"setref", Jim_SetrefCoreCommand},
19455 {"finalize", Jim_FinalizeCoreCommand},
19456 {"collect", Jim_CollectCoreCommand},
19457 #endif
19458 {"rename", Jim_RenameCoreCommand},
19459 {"dict", Jim_DictCoreCommand},
19460 {"subst", Jim_SubstCoreCommand},
19461 {"info", Jim_InfoCoreCommand},
19462 {"exists", Jim_ExistsCoreCommand},
19463 {"split", Jim_SplitCoreCommand},
19464 {"join", Jim_JoinCoreCommand},
19465 {"format", Jim_FormatCoreCommand},
19466 {"scan", Jim_ScanCoreCommand},
19467 {"error", Jim_ErrorCoreCommand},
19468 {"lrange", Jim_LrangeCoreCommand},
19469 {"lrepeat", Jim_LrepeatCoreCommand},
19470 {"env", Jim_EnvCoreCommand},
19471 {"source", Jim_SourceCoreCommand},
19472 {"lreverse", Jim_LreverseCoreCommand},
19473 {"range", Jim_RangeCoreCommand},
19474 {"rand", Jim_RandCoreCommand},
19475 {"tailcall", Jim_TailcallCoreCommand},
19476 {"local", Jim_LocalCoreCommand},
19477 {"upcall", Jim_UpcallCoreCommand},
19478 {"apply", Jim_ApplyCoreCommand},
19479 {NULL, NULL},
19482 void Jim_RegisterCoreCommands(Jim_Interp *interp)
19484 int i = 0;
19486 while (Jim_CoreCommandsTable[i].name != NULL) {
19487 Jim_CreateCommand(interp,
19488 Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
19489 i++;
19493 void Jim_MakeErrorMessage(Jim_Interp *interp)
19495 Jim_Obj *argv[2];
19497 argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
19498 argv[1] = interp->result;
19500 Jim_EvalObjVector(interp, 2, argv);
19503 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
19504 const char *prefix, const char *const *tablePtr, const char *name)
19506 int count;
19507 char **tablePtrSorted;
19508 int i;
19510 for (count = 0; tablePtr[count]; count++) {
19513 if (name == NULL) {
19514 name = "option";
19517 Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
19518 tablePtrSorted = Jim_Alloc(sizeof(char *) * count);
19519 memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
19520 qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
19521 for (i = 0; i < count; i++) {
19522 if (i + 1 == count && count > 1) {
19523 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
19525 Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
19526 if (i + 1 != count) {
19527 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
19530 Jim_Free(tablePtrSorted);
19533 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
19534 const char *const *tablePtr, int *indexPtr, const char *name, int flags)
19536 const char *bad = "bad ";
19537 const char *const *entryPtr = NULL;
19538 int i;
19539 int match = -1;
19540 int arglen;
19541 const char *arg = Jim_GetString(objPtr, &arglen);
19543 *indexPtr = -1;
19545 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
19546 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
19548 *indexPtr = i;
19549 return JIM_OK;
19551 if (flags & JIM_ENUM_ABBREV) {
19552 if (strncmp(arg, *entryPtr, arglen) == 0) {
19553 if (*arg == '-' && arglen == 1) {
19554 break;
19556 if (match >= 0) {
19557 bad = "ambiguous ";
19558 goto ambiguous;
19560 match = i;
19566 if (match >= 0) {
19567 *indexPtr = match;
19568 return JIM_OK;
19571 ambiguous:
19572 if (flags & JIM_ERRMSG) {
19573 JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
19575 return JIM_ERR;
19578 int Jim_FindByName(const char *name, const char * const array[], size_t len)
19580 int i;
19582 for (i = 0; i < (int)len; i++) {
19583 if (array[i] && strcmp(array[i], name) == 0) {
19584 return i;
19587 return -1;
19590 int Jim_IsDict(Jim_Obj *objPtr)
19592 return objPtr->typePtr == &dictObjType;
19595 int Jim_IsList(Jim_Obj *objPtr)
19597 return objPtr->typePtr == &listObjType;
19600 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
19603 int len = strlen(format);
19604 int extra = 0;
19605 int n = 0;
19606 const char *params[5];
19607 char *buf;
19608 va_list args;
19609 int i;
19611 va_start(args, format);
19613 for (i = 0; i < len && n < 5; i++) {
19614 int l;
19616 if (strncmp(format + i, "%s", 2) == 0) {
19617 params[n] = va_arg(args, char *);
19619 l = strlen(params[n]);
19621 else if (strncmp(format + i, "%#s", 3) == 0) {
19622 Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
19624 params[n] = Jim_GetString(objPtr, &l);
19626 else {
19627 if (format[i] == '%') {
19628 i++;
19630 continue;
19632 n++;
19633 extra += l;
19636 len += extra;
19637 buf = Jim_Alloc(len + 1);
19638 len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
19640 va_end(args);
19642 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
19646 #ifndef jim_ext_package
19647 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
19649 return JIM_OK;
19651 #endif
19652 #ifndef jim_ext_aio
19653 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
19655 Jim_SetResultString(interp, "aio not enabled", -1);
19656 return NULL;
19658 #endif
19661 #include <stdio.h>
19662 #include <string.h>
19665 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19668 return JIM_OK;
19671 static const jim_subcmd_type dummy_subcmd = {
19672 "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
19675 static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
19677 const char *s = "";
19679 for (; ct->cmd; ct++) {
19680 if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
19681 Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, NULL);
19682 s = sep;
19687 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
19688 Jim_Obj *cmd, Jim_Obj *subcmd)
19690 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19691 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), ", ", type,
19692 " command \"", Jim_String(subcmd), "\": should be ", NULL);
19693 add_commands(interp, command_table, ", ");
19696 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
19697 Jim_Obj *const *argv)
19699 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19700 Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]),
19701 " command ... \", where command is one of: ", NULL);
19702 add_commands(interp, command_table, ", ");
19705 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
19707 if (cmd) {
19708 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
19710 Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
19711 if (ct->args && *ct->args) {
19712 Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
19716 static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd)
19718 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19719 add_cmd_usage(interp, command_table, subcmd);
19720 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19723 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
19724 int argc, Jim_Obj *const *argv)
19726 const jim_subcmd_type *ct;
19727 const jim_subcmd_type *partial = 0;
19728 int cmdlen;
19729 Jim_Obj *cmd;
19730 const char *cmdstr;
19731 const char *cmdname;
19732 int help = 0;
19734 cmdname = Jim_String(argv[0]);
19736 if (argc < 2) {
19737 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19738 Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname,
19739 " command ...\"\n", NULL);
19740 Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help ?command?\" for help", NULL);
19741 return 0;
19744 cmd = argv[1];
19747 if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
19748 if (argc == 2) {
19750 show_cmd_usage(interp, command_table, argc, argv);
19751 return &dummy_subcmd;
19753 help = 1;
19756 cmd = argv[2];
19760 if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
19762 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19763 add_commands(interp, command_table, " ");
19764 return &dummy_subcmd;
19767 cmdstr = Jim_GetString(cmd, &cmdlen);
19769 for (ct = command_table; ct->cmd; ct++) {
19770 if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
19772 break;
19774 if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
19775 if (partial) {
19777 if (help) {
19779 show_cmd_usage(interp, command_table, argc, argv);
19780 return &dummy_subcmd;
19782 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
19783 return 0;
19785 partial = ct;
19787 continue;
19791 if (partial && !ct->cmd) {
19792 ct = partial;
19795 if (!ct->cmd) {
19797 if (help) {
19799 show_cmd_usage(interp, command_table, argc, argv);
19800 return &dummy_subcmd;
19802 bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
19803 return 0;
19806 if (help) {
19807 Jim_SetResultString(interp, "Usage: ", -1);
19809 add_cmd_usage(interp, ct, argv[0]);
19810 return &dummy_subcmd;
19814 if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) {
19815 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19817 add_cmd_usage(interp, ct, argv[0]);
19818 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19820 return 0;
19824 return ct;
19827 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
19829 int ret = JIM_ERR;
19831 if (ct) {
19832 if (ct->flags & JIM_MODFLAG_FULLARGV) {
19833 ret = ct->function(interp, argc, argv);
19835 else {
19836 ret = ct->function(interp, argc - 2, argv + 2);
19838 if (ret < 0) {
19839 set_wrong_args(interp, ct, argv[0]);
19840 ret = JIM_ERR;
19843 return ret;
19846 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19848 const jim_subcmd_type *ct =
19849 Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
19851 return Jim_CallSubCmd(interp, ct, argc, argv);
19854 #include <ctype.h>
19855 #include <stdlib.h>
19856 #include <string.h>
19857 #include <stdio.h>
19858 #include <assert.h>
19861 int utf8_fromunicode(char *p, unsigned uc)
19863 if (uc <= 0x7f) {
19864 *p = uc;
19865 return 1;
19867 else if (uc <= 0x7ff) {
19868 *p++ = 0xc0 | ((uc & 0x7c0) >> 6);
19869 *p = 0x80 | (uc & 0x3f);
19870 return 2;
19872 else if (uc <= 0xffff) {
19873 *p++ = 0xe0 | ((uc & 0xf000) >> 12);
19874 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
19875 *p = 0x80 | (uc & 0x3f);
19876 return 3;
19879 else {
19880 *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
19881 *p++ = 0x80 | ((uc & 0x3f000) >> 12);
19882 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
19883 *p = 0x80 | (uc & 0x3f);
19884 return 4;
19888 #include <ctype.h>
19889 #include <string.h>
19892 #define JIM_INTEGER_SPACE 24
19893 #define MAX_FLOAT_WIDTH 320
19895 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
19897 const char *span, *format, *formatEnd, *msg;
19898 int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
19899 static const char * const mixedXPG =
19900 "cannot mix \"%\" and \"%n$\" conversion specifiers";
19901 static const char * const badIndex[2] = {
19902 "not enough arguments for all format specifiers",
19903 "\"%n$\" argument index out of range"
19905 int formatLen;
19906 Jim_Obj *resultPtr;
19908 char *num_buffer = NULL;
19909 int num_buffer_size = 0;
19911 span = format = Jim_GetString(fmtObjPtr, &formatLen);
19912 formatEnd = format + formatLen;
19913 resultPtr = Jim_NewEmptyStringObj(interp);
19915 while (format != formatEnd) {
19916 char *end;
19917 int gotMinus, sawFlag;
19918 int gotPrecision, useShort;
19919 long width, precision;
19920 int newXpg;
19921 int ch;
19922 int step;
19923 int doubleType;
19924 char pad = ' ';
19925 char spec[2*JIM_INTEGER_SPACE + 12];
19926 char *p;
19928 int formatted_chars;
19929 int formatted_bytes;
19930 const char *formatted_buf;
19932 step = utf8_tounicode(format, &ch);
19933 format += step;
19934 if (ch != '%') {
19935 numBytes += step;
19936 continue;
19938 if (numBytes) {
19939 Jim_AppendString(interp, resultPtr, span, numBytes);
19940 numBytes = 0;
19944 step = utf8_tounicode(format, &ch);
19945 if (ch == '%') {
19946 span = format;
19947 numBytes = step;
19948 format += step;
19949 continue;
19953 newXpg = 0;
19954 if (isdigit(ch)) {
19955 int position = strtoul(format, &end, 10);
19956 if (*end == '$') {
19957 newXpg = 1;
19958 objIndex = position - 1;
19959 format = end + 1;
19960 step = utf8_tounicode(format, &ch);
19963 if (newXpg) {
19964 if (gotSequential) {
19965 msg = mixedXPG;
19966 goto errorMsg;
19968 gotXpg = 1;
19969 } else {
19970 if (gotXpg) {
19971 msg = mixedXPG;
19972 goto errorMsg;
19974 gotSequential = 1;
19976 if ((objIndex < 0) || (objIndex >= objc)) {
19977 msg = badIndex[gotXpg];
19978 goto errorMsg;
19981 p = spec;
19982 *p++ = '%';
19984 gotMinus = 0;
19985 sawFlag = 1;
19986 do {
19987 switch (ch) {
19988 case '-':
19989 gotMinus = 1;
19990 break;
19991 case '0':
19992 pad = ch;
19993 break;
19994 case ' ':
19995 case '+':
19996 case '#':
19997 break;
19998 default:
19999 sawFlag = 0;
20000 continue;
20002 *p++ = ch;
20003 format += step;
20004 step = utf8_tounicode(format, &ch);
20005 } while (sawFlag);
20008 width = 0;
20009 if (isdigit(ch)) {
20010 width = strtoul(format, &end, 10);
20011 format = end;
20012 step = utf8_tounicode(format, &ch);
20013 } else if (ch == '*') {
20014 if (objIndex >= objc - 1) {
20015 msg = badIndex[gotXpg];
20016 goto errorMsg;
20018 if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) {
20019 goto error;
20021 if (width < 0) {
20022 width = -width;
20023 if (!gotMinus) {
20024 *p++ = '-';
20025 gotMinus = 1;
20028 objIndex++;
20029 format += step;
20030 step = utf8_tounicode(format, &ch);
20034 gotPrecision = precision = 0;
20035 if (ch == '.') {
20036 gotPrecision = 1;
20037 format += step;
20038 step = utf8_tounicode(format, &ch);
20040 if (isdigit(ch)) {
20041 precision = strtoul(format, &end, 10);
20042 format = end;
20043 step = utf8_tounicode(format, &ch);
20044 } else if (ch == '*') {
20045 if (objIndex >= objc - 1) {
20046 msg = badIndex[gotXpg];
20047 goto errorMsg;
20049 if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) {
20050 goto error;
20054 if (precision < 0) {
20055 precision = 0;
20057 objIndex++;
20058 format += step;
20059 step = utf8_tounicode(format, &ch);
20063 useShort = 0;
20064 if (ch == 'h') {
20065 useShort = 1;
20066 format += step;
20067 step = utf8_tounicode(format, &ch);
20068 } else if (ch == 'l') {
20070 format += step;
20071 step = utf8_tounicode(format, &ch);
20072 if (ch == 'l') {
20073 format += step;
20074 step = utf8_tounicode(format, &ch);
20078 format += step;
20079 span = format;
20082 if (ch == 'i') {
20083 ch = 'd';
20086 doubleType = 0;
20088 switch (ch) {
20089 case '\0':
20090 msg = "format string ended in middle of field specifier";
20091 goto errorMsg;
20092 case 's': {
20093 formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
20094 formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
20095 if (gotPrecision && (precision < formatted_chars)) {
20097 formatted_chars = precision;
20098 formatted_bytes = utf8_index(formatted_buf, precision);
20100 break;
20102 case 'c': {
20103 jim_wide code;
20105 if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
20106 goto error;
20109 formatted_bytes = utf8_getchars(spec, code);
20110 formatted_buf = spec;
20111 formatted_chars = 1;
20112 break;
20114 case 'b': {
20115 unsigned jim_wide w;
20116 int length;
20117 int i;
20118 int j;
20120 if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
20121 goto error;
20123 length = sizeof(w) * 8;
20127 if (num_buffer_size < length + 1) {
20128 num_buffer_size = length + 1;
20129 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20132 j = 0;
20133 for (i = length; i > 0; ) {
20134 i--;
20135 if (w & ((unsigned jim_wide)1 << i)) {
20136 num_buffer[j++] = '1';
20138 else if (j || i == 0) {
20139 num_buffer[j++] = '0';
20142 num_buffer[j] = 0;
20143 formatted_chars = formatted_bytes = j;
20144 formatted_buf = num_buffer;
20145 break;
20148 case 'e':
20149 case 'E':
20150 case 'f':
20151 case 'g':
20152 case 'G':
20153 doubleType = 1;
20155 case 'd':
20156 case 'u':
20157 case 'o':
20158 case 'x':
20159 case 'X': {
20160 jim_wide w;
20161 double d;
20162 int length;
20165 if (width) {
20166 p += sprintf(p, "%ld", width);
20168 if (gotPrecision) {
20169 p += sprintf(p, ".%ld", precision);
20173 if (doubleType) {
20174 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
20175 goto error;
20177 length = MAX_FLOAT_WIDTH;
20179 else {
20180 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) {
20181 goto error;
20183 length = JIM_INTEGER_SPACE;
20184 if (useShort) {
20185 if (ch == 'd') {
20186 w = (short)w;
20188 else {
20189 w = (unsigned short)w;
20192 *p++ = 'l';
20193 #ifdef HAVE_LONG_LONG
20194 if (sizeof(long long) == sizeof(jim_wide)) {
20195 *p++ = 'l';
20197 #endif
20200 *p++ = (char) ch;
20201 *p = '\0';
20204 if (width > length) {
20205 length = width;
20207 if (gotPrecision) {
20208 length += precision;
20212 if (num_buffer_size < length + 1) {
20213 num_buffer_size = length + 1;
20214 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20217 if (doubleType) {
20218 snprintf(num_buffer, length + 1, spec, d);
20220 else {
20221 formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
20223 formatted_chars = formatted_bytes = strlen(num_buffer);
20224 formatted_buf = num_buffer;
20225 break;
20228 default: {
20230 spec[0] = ch;
20231 spec[1] = '\0';
20232 Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
20233 goto error;
20237 if (!gotMinus) {
20238 while (formatted_chars < width) {
20239 Jim_AppendString(interp, resultPtr, &pad, 1);
20240 formatted_chars++;
20244 Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes);
20246 while (formatted_chars < width) {
20247 Jim_AppendString(interp, resultPtr, &pad, 1);
20248 formatted_chars++;
20251 objIndex += gotSequential;
20253 if (numBytes) {
20254 Jim_AppendString(interp, resultPtr, span, numBytes);
20257 Jim_Free(num_buffer);
20258 return resultPtr;
20260 errorMsg:
20261 Jim_SetResultString(interp, msg, -1);
20262 error:
20263 Jim_FreeNewObj(interp, resultPtr);
20264 Jim_Free(num_buffer);
20265 return NULL;
20269 #if defined(JIM_REGEXP)
20270 #include <stdio.h>
20271 #include <ctype.h>
20272 #include <stdlib.h>
20273 #include <string.h>
20277 #define REG_MAX_PAREN 100
20281 #define END 0
20282 #define BOL 1
20283 #define EOL 2
20284 #define ANY 3
20285 #define ANYOF 4
20286 #define ANYBUT 5
20287 #define BRANCH 6
20288 #define BACK 7
20289 #define EXACTLY 8
20290 #define NOTHING 9
20291 #define REP 10
20292 #define REPMIN 11
20293 #define REPX 12
20294 #define REPXMIN 13
20296 #define WORDA 15
20297 #define WORDZ 16
20299 #define OPENNC 1000
20300 #define OPEN 1001
20305 #define CLOSENC 2000
20306 #define CLOSE 2001
20307 #define CLOSE_END (CLOSE+REG_MAX_PAREN)
20309 #define REG_MAGIC 0xFADED00D
20312 #define OP(preg, p) (preg->program[p])
20313 #define NEXT(preg, p) (preg->program[p + 1])
20314 #define OPERAND(p) ((p) + 2)
20319 #define FAIL(R,M) { (R)->err = (M); return (M); }
20320 #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
20321 #define META "^$.[()|?{+*"
20323 #define HASWIDTH 1
20324 #define SIMPLE 2
20325 #define SPSTART 4
20326 #define WORST 0
20328 #define MAX_REP_COUNT 1000000
20330 static int reg(regex_t *preg, int paren , int *flagp );
20331 static int regpiece(regex_t *preg, int *flagp );
20332 static int regbranch(regex_t *preg, int *flagp );
20333 static int regatom(regex_t *preg, int *flagp );
20334 static int regnode(regex_t *preg, int op );
20335 static int regnext(regex_t *preg, int p );
20336 static void regc(regex_t *preg, int b );
20337 static int reginsert(regex_t *preg, int op, int size, int opnd );
20338 static void regtail(regex_t *preg, int p, int val);
20339 static void regoptail(regex_t *preg, int p, int val );
20340 static int regopsize(regex_t *preg, int p );
20342 static int reg_range_find(const int *string, int c);
20343 static const char *str_find(const char *string, int c, int nocase);
20344 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase);
20347 #ifdef DEBUG
20348 static int regnarrate = 0;
20349 static void regdump(regex_t *preg);
20350 static const char *regprop( int op );
20351 #endif
20354 static int str_int_len(const int *seq)
20356 int n = 0;
20357 while (*seq++) {
20358 n++;
20360 return n;
20363 int regcomp(regex_t *preg, const char *exp, int cflags)
20365 int scan;
20366 int longest;
20367 unsigned len;
20368 int flags;
20370 #ifdef DEBUG
20371 fprintf(stderr, "Compiling: '%s'\n", exp);
20372 #endif
20373 memset(preg, 0, sizeof(*preg));
20375 if (exp == NULL)
20376 FAIL(preg, REG_ERR_NULL_ARGUMENT);
20379 preg->cflags = cflags;
20380 preg->regparse = exp;
20383 preg->proglen = (strlen(exp) + 1) * 5;
20384 preg->program = malloc(preg->proglen * sizeof(int));
20385 if (preg->program == NULL)
20386 FAIL(preg, REG_ERR_NOMEM);
20388 regc(preg, REG_MAGIC);
20389 if (reg(preg, 0, &flags) == 0) {
20390 return preg->err;
20394 if (preg->re_nsub >= REG_MAX_PAREN)
20395 FAIL(preg,REG_ERR_TOO_BIG);
20398 preg->regstart = 0;
20399 preg->reganch = 0;
20400 preg->regmust = 0;
20401 preg->regmlen = 0;
20402 scan = 1;
20403 if (OP(preg, regnext(preg, scan)) == END) {
20404 scan = OPERAND(scan);
20407 if (OP(preg, scan) == EXACTLY) {
20408 preg->regstart = preg->program[OPERAND(scan)];
20410 else if (OP(preg, scan) == BOL)
20411 preg->reganch++;
20413 if (flags&SPSTART) {
20414 longest = 0;
20415 len = 0;
20416 for (; scan != 0; scan = regnext(preg, scan)) {
20417 if (OP(preg, scan) == EXACTLY) {
20418 int plen = str_int_len(preg->program + OPERAND(scan));
20419 if (plen >= len) {
20420 longest = OPERAND(scan);
20421 len = plen;
20425 preg->regmust = longest;
20426 preg->regmlen = len;
20430 #ifdef DEBUG
20431 regdump(preg);
20432 #endif
20434 return 0;
20437 static int reg(regex_t *preg, int paren , int *flagp )
20439 int ret;
20440 int br;
20441 int ender;
20442 int parno = 0;
20443 int flags;
20445 *flagp = HASWIDTH;
20448 if (paren) {
20449 if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
20451 preg->regparse += 2;
20452 parno = -1;
20454 else {
20455 parno = ++preg->re_nsub;
20457 ret = regnode(preg, OPEN+parno);
20458 } else
20459 ret = 0;
20462 br = regbranch(preg, &flags);
20463 if (br == 0)
20464 return 0;
20465 if (ret != 0)
20466 regtail(preg, ret, br);
20467 else
20468 ret = br;
20469 if (!(flags&HASWIDTH))
20470 *flagp &= ~HASWIDTH;
20471 *flagp |= flags&SPSTART;
20472 while (*preg->regparse == '|') {
20473 preg->regparse++;
20474 br = regbranch(preg, &flags);
20475 if (br == 0)
20476 return 0;
20477 regtail(preg, ret, br);
20478 if (!(flags&HASWIDTH))
20479 *flagp &= ~HASWIDTH;
20480 *flagp |= flags&SPSTART;
20484 ender = regnode(preg, (paren) ? CLOSE+parno : END);
20485 regtail(preg, ret, ender);
20488 for (br = ret; br != 0; br = regnext(preg, br))
20489 regoptail(preg, br, ender);
20492 if (paren && *preg->regparse++ != ')') {
20493 preg->err = REG_ERR_UNMATCHED_PAREN;
20494 return 0;
20495 } else if (!paren && *preg->regparse != '\0') {
20496 if (*preg->regparse == ')') {
20497 preg->err = REG_ERR_UNMATCHED_PAREN;
20498 return 0;
20499 } else {
20500 preg->err = REG_ERR_JUNK_ON_END;
20501 return 0;
20505 return(ret);
20508 static int regbranch(regex_t *preg, int *flagp )
20510 int ret;
20511 int chain;
20512 int latest;
20513 int flags;
20515 *flagp = WORST;
20517 ret = regnode(preg, BRANCH);
20518 chain = 0;
20519 while (*preg->regparse != '\0' && *preg->regparse != ')' &&
20520 *preg->regparse != '|') {
20521 latest = regpiece(preg, &flags);
20522 if (latest == 0)
20523 return 0;
20524 *flagp |= flags&HASWIDTH;
20525 if (chain == 0) {
20526 *flagp |= flags&SPSTART;
20528 else {
20529 regtail(preg, chain, latest);
20531 chain = latest;
20533 if (chain == 0)
20534 (void) regnode(preg, NOTHING);
20536 return(ret);
20539 static int regpiece(regex_t *preg, int *flagp)
20541 int ret;
20542 char op;
20543 int next;
20544 int flags;
20545 int min;
20546 int max;
20548 ret = regatom(preg, &flags);
20549 if (ret == 0)
20550 return 0;
20552 op = *preg->regparse;
20553 if (!ISMULT(op)) {
20554 *flagp = flags;
20555 return(ret);
20558 if (!(flags&HASWIDTH) && op != '?') {
20559 preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
20560 return 0;
20564 if (op == '{') {
20565 char *end;
20567 min = strtoul(preg->regparse + 1, &end, 10);
20568 if (end == preg->regparse + 1) {
20569 preg->err = REG_ERR_BAD_COUNT;
20570 return 0;
20572 if (*end == '}') {
20573 max = min;
20575 else {
20576 preg->regparse = end;
20577 max = strtoul(preg->regparse + 1, &end, 10);
20578 if (*end != '}') {
20579 preg->err = REG_ERR_UNMATCHED_BRACES;
20580 return 0;
20583 if (end == preg->regparse + 1) {
20584 max = MAX_REP_COUNT;
20586 else if (max < min || max >= 100) {
20587 preg->err = REG_ERR_BAD_COUNT;
20588 return 0;
20590 if (min >= 100) {
20591 preg->err = REG_ERR_BAD_COUNT;
20592 return 0;
20595 preg->regparse = strchr(preg->regparse, '}');
20597 else {
20598 min = (op == '+');
20599 max = (op == '?' ? 1 : MAX_REP_COUNT);
20602 if (preg->regparse[1] == '?') {
20603 preg->regparse++;
20604 next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret);
20606 else {
20607 next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret);
20609 preg->program[ret + 2] = max;
20610 preg->program[ret + 3] = min;
20611 preg->program[ret + 4] = 0;
20613 *flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART);
20615 if (!(flags & SIMPLE)) {
20616 int back = regnode(preg, BACK);
20617 regtail(preg, back, ret);
20618 regtail(preg, next, back);
20621 preg->regparse++;
20622 if (ISMULT(*preg->regparse)) {
20623 preg->err = REG_ERR_NESTED_COUNT;
20624 return 0;
20627 return ret;
20630 static void reg_addrange(regex_t *preg, int lower, int upper)
20632 if (lower > upper) {
20633 reg_addrange(preg, upper, lower);
20636 regc(preg, upper - lower + 1);
20637 regc(preg, lower);
20640 static void reg_addrange_str(regex_t *preg, const char *str)
20642 while (*str) {
20643 reg_addrange(preg, *str, *str);
20644 str++;
20648 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper)
20650 int l = utf8_tounicode(s, uc);
20651 if (upper) {
20652 *uc = utf8_upper(*uc);
20654 return l;
20657 static int hexdigitval(int c)
20659 if (c >= '0' && c <= '9')
20660 return c - '0';
20661 if (c >= 'a' && c <= 'f')
20662 return c - 'a' + 10;
20663 if (c >= 'A' && c <= 'F')
20664 return c - 'A' + 10;
20665 return -1;
20668 static int parse_hex(const char *s, int n, int *uc)
20670 int val = 0;
20671 int k;
20673 for (k = 0; k < n; k++) {
20674 int c = hexdigitval(*s++);
20675 if (c == -1) {
20676 break;
20678 val = (val << 4) | c;
20680 if (k) {
20681 *uc = val;
20683 return k;
20686 static int reg_decode_escape(const char *s, int *ch)
20688 int n;
20689 const char *s0 = s;
20691 *ch = *s++;
20693 switch (*ch) {
20694 case 'b': *ch = '\b'; break;
20695 case 'e': *ch = 27; break;
20696 case 'f': *ch = '\f'; break;
20697 case 'n': *ch = '\n'; break;
20698 case 'r': *ch = '\r'; break;
20699 case 't': *ch = '\t'; break;
20700 case 'v': *ch = '\v'; break;
20701 case 'u':
20702 if (*s == '{') {
20704 n = parse_hex(s + 1, 6, ch);
20705 if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
20706 s += n + 2;
20708 else {
20710 *ch = 'u';
20713 else if ((n = parse_hex(s, 4, ch)) > 0) {
20714 s += n;
20716 break;
20717 case 'U':
20718 if ((n = parse_hex(s, 8, ch)) > 0) {
20719 s += n;
20721 break;
20722 case 'x':
20723 if ((n = parse_hex(s, 2, ch)) > 0) {
20724 s += n;
20726 break;
20727 case '\0':
20728 s--;
20729 *ch = '\\';
20730 break;
20732 return s - s0;
20735 static int regatom(regex_t *preg, int *flagp)
20737 int ret;
20738 int flags;
20739 int nocase = (preg->cflags & REG_ICASE);
20741 int ch;
20742 int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);
20744 *flagp = WORST;
20746 preg->regparse += n;
20747 switch (ch) {
20749 case '^':
20750 ret = regnode(preg, BOL);
20751 break;
20752 case '$':
20753 ret = regnode(preg, EOL);
20754 break;
20755 case '.':
20756 ret = regnode(preg, ANY);
20757 *flagp |= HASWIDTH|SIMPLE;
20758 break;
20759 case '[': {
20760 const char *pattern = preg->regparse;
20762 if (*pattern == '^') {
20763 ret = regnode(preg, ANYBUT);
20764 pattern++;
20765 } else
20766 ret = regnode(preg, ANYOF);
20769 if (*pattern == ']' || *pattern == '-') {
20770 reg_addrange(preg, *pattern, *pattern);
20771 pattern++;
20774 while (*pattern && *pattern != ']') {
20776 int start;
20777 int end;
20779 pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
20780 if (start == '\\') {
20781 pattern += reg_decode_escape(pattern, &start);
20782 if (start == 0) {
20783 preg->err = REG_ERR_NULL_CHAR;
20784 return 0;
20787 if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
20789 pattern += utf8_tounicode(pattern, &end);
20790 pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
20791 if (end == '\\') {
20792 pattern += reg_decode_escape(pattern, &end);
20793 if (end == 0) {
20794 preg->err = REG_ERR_NULL_CHAR;
20795 return 0;
20799 reg_addrange(preg, start, end);
20800 continue;
20802 if (start == '[') {
20803 if (strncmp(pattern, ":alpha:]", 8) == 0) {
20804 if ((preg->cflags & REG_ICASE) == 0) {
20805 reg_addrange(preg, 'a', 'z');
20807 reg_addrange(preg, 'A', 'Z');
20808 pattern += 8;
20809 continue;
20811 if (strncmp(pattern, ":alnum:]", 8) == 0) {
20812 if ((preg->cflags & REG_ICASE) == 0) {
20813 reg_addrange(preg, 'a', 'z');
20815 reg_addrange(preg, 'A', 'Z');
20816 reg_addrange(preg, '0', '9');
20817 pattern += 8;
20818 continue;
20820 if (strncmp(pattern, ":space:]", 8) == 0) {
20821 reg_addrange_str(preg, " \t\r\n\f\v");
20822 pattern += 8;
20823 continue;
20827 reg_addrange(preg, start, start);
20829 regc(preg, '\0');
20831 if (*pattern) {
20832 pattern++;
20834 preg->regparse = pattern;
20836 *flagp |= HASWIDTH|SIMPLE;
20838 break;
20839 case '(':
20840 ret = reg(preg, 1, &flags);
20841 if (ret == 0)
20842 return 0;
20843 *flagp |= flags&(HASWIDTH|SPSTART);
20844 break;
20845 case '\0':
20846 case '|':
20847 case ')':
20848 preg->err = REG_ERR_INTERNAL;
20849 return 0;
20850 case '?':
20851 case '+':
20852 case '*':
20853 case '{':
20854 preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
20855 return 0;
20856 case '\\':
20857 switch (*preg->regparse++) {
20858 case '\0':
20859 preg->err = REG_ERR_TRAILING_BACKSLASH;
20860 return 0;
20861 case '<':
20862 case 'm':
20863 ret = regnode(preg, WORDA);
20864 break;
20865 case '>':
20866 case 'M':
20867 ret = regnode(preg, WORDZ);
20868 break;
20869 case 'd':
20870 ret = regnode(preg, ANYOF);
20871 reg_addrange(preg, '0', '9');
20872 regc(preg, '\0');
20873 *flagp |= HASWIDTH|SIMPLE;
20874 break;
20875 case 'w':
20876 ret = regnode(preg, ANYOF);
20877 if ((preg->cflags & REG_ICASE) == 0) {
20878 reg_addrange(preg, 'a', 'z');
20880 reg_addrange(preg, 'A', 'Z');
20881 reg_addrange(preg, '0', '9');
20882 reg_addrange(preg, '_', '_');
20883 regc(preg, '\0');
20884 *flagp |= HASWIDTH|SIMPLE;
20885 break;
20886 case 's':
20887 ret = regnode(preg, ANYOF);
20888 reg_addrange_str(preg," \t\r\n\f\v");
20889 regc(preg, '\0');
20890 *flagp |= HASWIDTH|SIMPLE;
20891 break;
20893 default:
20896 preg->regparse--;
20897 goto de_fault;
20899 break;
20900 de_fault:
20901 default: {
20902 int added = 0;
20905 preg->regparse -= n;
20907 ret = regnode(preg, EXACTLY);
20911 while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
20912 n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
20913 if (ch == '\\' && preg->regparse[n]) {
20914 if (strchr("<>mMwds", preg->regparse[n])) {
20916 break;
20918 n += reg_decode_escape(preg->regparse + n, &ch);
20919 if (ch == 0) {
20920 preg->err = REG_ERR_NULL_CHAR;
20921 return 0;
20926 if (ISMULT(preg->regparse[n])) {
20928 if (added) {
20930 break;
20933 regc(preg, ch);
20934 added++;
20935 preg->regparse += n;
20936 break;
20940 regc(preg, ch);
20941 added++;
20942 preg->regparse += n;
20944 regc(preg, '\0');
20946 *flagp |= HASWIDTH;
20947 if (added == 1)
20948 *flagp |= SIMPLE;
20949 break;
20951 break;
20954 return(ret);
20957 static void reg_grow(regex_t *preg, int n)
20959 if (preg->p + n >= preg->proglen) {
20960 preg->proglen = (preg->p + n) * 2;
20961 preg->program = realloc(preg->program, preg->proglen * sizeof(int));
20966 static int regnode(regex_t *preg, int op)
20968 reg_grow(preg, 2);
20971 preg->program[preg->p++] = op;
20972 preg->program[preg->p++] = 0;
20975 return preg->p - 2;
20978 static void regc(regex_t *preg, int b )
20980 reg_grow(preg, 1);
20981 preg->program[preg->p++] = b;
20984 static int reginsert(regex_t *preg, int op, int size, int opnd )
20986 reg_grow(preg, size);
20989 memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
20991 memset(preg->program + opnd, 0, sizeof(int) * size);
20993 preg->program[opnd] = op;
20995 preg->p += size;
20997 return opnd + size;
21000 static void regtail(regex_t *preg, int p, int val)
21002 int scan;
21003 int temp;
21004 int offset;
21007 scan = p;
21008 for (;;) {
21009 temp = regnext(preg, scan);
21010 if (temp == 0)
21011 break;
21012 scan = temp;
21015 if (OP(preg, scan) == BACK)
21016 offset = scan - val;
21017 else
21018 offset = val - scan;
21020 preg->program[scan + 1] = offset;
21024 static void regoptail(regex_t *preg, int p, int val )
21027 if (p != 0 && OP(preg, p) == BRANCH) {
21028 regtail(preg, OPERAND(p), val);
21033 static int regtry(regex_t *preg, const char *string );
21034 static int regmatch(regex_t *preg, int prog);
21035 static int regrepeat(regex_t *preg, int p, int max);
21037 int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
21039 const char *s;
21040 int scan;
21043 if (preg == NULL || preg->program == NULL || string == NULL) {
21044 return REG_ERR_NULL_ARGUMENT;
21048 if (*preg->program != REG_MAGIC) {
21049 return REG_ERR_CORRUPTED;
21052 #ifdef DEBUG
21053 fprintf(stderr, "regexec: %s\n", string);
21054 regdump(preg);
21055 #endif
21057 preg->eflags = eflags;
21058 preg->pmatch = pmatch;
21059 preg->nmatch = nmatch;
21060 preg->start = string;
21063 for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) {
21064 int op = OP(preg, scan);
21065 if (op == END)
21066 break;
21067 if (op == REPX || op == REPXMIN)
21068 preg->program[scan + 4] = 0;
21072 if (preg->regmust != 0) {
21073 s = string;
21074 while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
21075 if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
21076 break;
21078 s++;
21080 if (s == NULL)
21081 return REG_NOMATCH;
21085 preg->regbol = string;
21088 if (preg->reganch) {
21089 if (eflags & REG_NOTBOL) {
21091 goto nextline;
21093 while (1) {
21094 if (regtry(preg, string)) {
21095 return REG_NOERROR;
21097 if (*string) {
21098 nextline:
21099 if (preg->cflags & REG_NEWLINE) {
21101 string = strchr(string, '\n');
21102 if (string) {
21103 preg->regbol = ++string;
21104 continue;
21108 return REG_NOMATCH;
21113 s = string;
21114 if (preg->regstart != '\0') {
21116 while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
21117 if (regtry(preg, s))
21118 return REG_NOERROR;
21119 s++;
21122 else
21124 while (1) {
21125 if (regtry(preg, s))
21126 return REG_NOERROR;
21127 if (*s == '\0') {
21128 break;
21130 else {
21131 int c;
21132 s += utf8_tounicode(s, &c);
21137 return REG_NOMATCH;
21141 static int regtry( regex_t *preg, const char *string )
21143 int i;
21145 preg->reginput = string;
21147 for (i = 0; i < preg->nmatch; i++) {
21148 preg->pmatch[i].rm_so = -1;
21149 preg->pmatch[i].rm_eo = -1;
21151 if (regmatch(preg, 1)) {
21152 preg->pmatch[0].rm_so = string - preg->start;
21153 preg->pmatch[0].rm_eo = preg->reginput - preg->start;
21154 return(1);
21155 } else
21156 return(0);
21159 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase)
21161 const char *s = string;
21162 while (proglen && *s) {
21163 int ch;
21164 int n = reg_utf8_tounicode_case(s, &ch, nocase);
21165 if (ch != *prog) {
21166 return -1;
21168 prog++;
21169 s += n;
21170 proglen--;
21172 if (proglen == 0) {
21173 return s - string;
21175 return -1;
21178 static int reg_range_find(const int *range, int c)
21180 while (*range) {
21182 if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
21183 return 1;
21185 range += 2;
21187 return 0;
21190 static const char *str_find(const char *string, int c, int nocase)
21192 if (nocase) {
21194 c = utf8_upper(c);
21196 while (*string) {
21197 int ch;
21198 int n = reg_utf8_tounicode_case(string, &ch, nocase);
21199 if (c == ch) {
21200 return string;
21202 string += n;
21204 return NULL;
21207 static int reg_iseol(regex_t *preg, int ch)
21209 if (preg->cflags & REG_NEWLINE) {
21210 return ch == '\0' || ch == '\n';
21212 else {
21213 return ch == '\0';
21217 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin)
21219 int nextch = '\0';
21220 const char *save;
21221 int no;
21222 int c;
21224 int max = preg->program[scan + 2];
21225 int min = preg->program[scan + 3];
21226 int next = regnext(preg, scan);
21228 if (OP(preg, next) == EXACTLY) {
21229 nextch = preg->program[OPERAND(next)];
21231 save = preg->reginput;
21232 no = regrepeat(preg, scan + 5, max);
21233 if (no < min) {
21234 return 0;
21236 if (matchmin) {
21238 max = no;
21239 no = min;
21242 while (1) {
21243 if (matchmin) {
21244 if (no > max) {
21245 break;
21248 else {
21249 if (no < min) {
21250 break;
21253 preg->reginput = save + utf8_index(save, no);
21254 reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21256 if (reg_iseol(preg, nextch) || c == nextch) {
21257 if (regmatch(preg, next)) {
21258 return(1);
21261 if (matchmin) {
21263 no++;
21265 else {
21267 no--;
21270 return(0);
21273 static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
21275 int *scanpt = preg->program + scan;
21277 int max = scanpt[2];
21278 int min = scanpt[3];
21281 if (scanpt[4] < min) {
21283 scanpt[4]++;
21284 if (regmatch(preg, scan + 5)) {
21285 return 1;
21287 scanpt[4]--;
21288 return 0;
21290 if (scanpt[4] > max) {
21291 return 0;
21294 if (matchmin) {
21296 if (regmatch(preg, regnext(preg, scan))) {
21297 return 1;
21300 scanpt[4]++;
21301 if (regmatch(preg, scan + 5)) {
21302 return 1;
21304 scanpt[4]--;
21305 return 0;
21308 if (scanpt[4] < max) {
21309 scanpt[4]++;
21310 if (regmatch(preg, scan + 5)) {
21311 return 1;
21313 scanpt[4]--;
21316 return regmatch(preg, regnext(preg, scan));
21320 static int regmatch(regex_t *preg, int prog)
21322 int scan;
21323 int next;
21324 const char *save;
21326 scan = prog;
21328 #ifdef DEBUG
21329 if (scan != 0 && regnarrate)
21330 fprintf(stderr, "%s(\n", regprop(scan));
21331 #endif
21332 while (scan != 0) {
21333 int n;
21334 int c;
21335 #ifdef DEBUG
21336 if (regnarrate) {
21337 fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
21339 #endif
21340 next = regnext(preg, scan);
21341 n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21343 switch (OP(preg, scan)) {
21344 case BOL:
21345 if (preg->reginput != preg->regbol)
21346 return(0);
21347 break;
21348 case EOL:
21349 if (!reg_iseol(preg, c)) {
21350 return(0);
21352 break;
21353 case WORDA:
21355 if ((!isalnum(UCHAR(c))) && c != '_')
21356 return(0);
21358 if (preg->reginput > preg->regbol &&
21359 (isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
21360 return(0);
21361 break;
21362 case WORDZ:
21364 if (preg->reginput > preg->regbol) {
21366 if (reg_iseol(preg, c) || !isalnum(UCHAR(c)) || c != '_') {
21367 c = preg->reginput[-1];
21369 if (isalnum(UCHAR(c)) || c == '_') {
21370 break;
21375 return(0);
21377 case ANY:
21378 if (reg_iseol(preg, c))
21379 return 0;
21380 preg->reginput += n;
21381 break;
21382 case EXACTLY: {
21383 int opnd;
21384 int len;
21385 int slen;
21387 opnd = OPERAND(scan);
21388 len = str_int_len(preg->program + opnd);
21390 slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE);
21391 if (slen < 0) {
21392 return(0);
21394 preg->reginput += slen;
21396 break;
21397 case ANYOF:
21398 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) {
21399 return(0);
21401 preg->reginput += n;
21402 break;
21403 case ANYBUT:
21404 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) {
21405 return(0);
21407 preg->reginput += n;
21408 break;
21409 case NOTHING:
21410 break;
21411 case BACK:
21412 break;
21413 case BRANCH:
21414 if (OP(preg, next) != BRANCH)
21415 next = OPERAND(scan);
21416 else {
21417 do {
21418 save = preg->reginput;
21419 if (regmatch(preg, OPERAND(scan))) {
21420 return(1);
21422 preg->reginput = save;
21423 scan = regnext(preg, scan);
21424 } while (scan != 0 && OP(preg, scan) == BRANCH);
21425 return(0);
21428 break;
21429 case REP:
21430 case REPMIN:
21431 return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);
21433 case REPX:
21434 case REPXMIN:
21435 return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);
21437 case END:
21438 return 1;
21440 case OPENNC:
21441 case CLOSENC:
21442 return regmatch(preg, next);
21444 default:
21445 if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
21446 save = preg->reginput;
21447 if (regmatch(preg, next)) {
21448 if (OP(preg, scan) < CLOSE) {
21449 int no = OP(preg, scan) - OPEN;
21450 if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
21451 preg->pmatch[no].rm_so = save - preg->start;
21454 else {
21455 int no = OP(preg, scan) - CLOSE;
21456 if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
21457 preg->pmatch[no].rm_eo = save - preg->start;
21460 return(1);
21462 return(0);
21464 return REG_ERR_INTERNAL;
21467 scan = next;
21470 return REG_ERR_INTERNAL;
21473 static int regrepeat(regex_t *preg, int p, int max)
21475 int count = 0;
21476 const char *scan;
21477 int opnd;
21478 int ch;
21479 int n;
21481 scan = preg->reginput;
21482 opnd = OPERAND(p);
21483 switch (OP(preg, p)) {
21484 case ANY:
21486 while (!reg_iseol(preg, *scan) && count < max) {
21487 count++;
21488 scan++;
21490 break;
21491 case EXACTLY:
21492 while (count < max) {
21493 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21494 if (preg->program[opnd] != ch) {
21495 break;
21497 count++;
21498 scan += n;
21500 break;
21501 case ANYOF:
21502 while (count < max) {
21503 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21504 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) {
21505 break;
21507 count++;
21508 scan += n;
21510 break;
21511 case ANYBUT:
21512 while (count < max) {
21513 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21514 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
21515 break;
21517 count++;
21518 scan += n;
21520 break;
21521 default:
21522 preg->err = REG_ERR_INTERNAL;
21523 count = 0;
21524 break;
21526 preg->reginput = scan;
21528 return(count);
21531 static int regnext(regex_t *preg, int p )
21533 int offset;
21535 offset = NEXT(preg, p);
21537 if (offset == 0)
21538 return 0;
21540 if (OP(preg, p) == BACK)
21541 return(p-offset);
21542 else
21543 return(p+offset);
21546 static int regopsize(regex_t *preg, int p )
21549 switch (OP(preg, p)) {
21550 case REP:
21551 case REPMIN:
21552 case REPX:
21553 case REPXMIN:
21554 return 5;
21556 case ANYOF:
21557 case ANYBUT:
21558 case EXACTLY: {
21559 int s = p + 2;
21560 while (preg->program[s++]) {
21562 return s - p;
21565 return 2;
21569 size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
21571 static const char *error_strings[] = {
21572 "success",
21573 "no match",
21574 "bad pattern",
21575 "null argument",
21576 "unknown error",
21577 "too big",
21578 "out of memory",
21579 "too many ()",
21580 "parentheses () not balanced",
21581 "braces {} not balanced",
21582 "invalid repetition count(s)",
21583 "extra characters",
21584 "*+ of empty atom",
21585 "nested count",
21586 "internal error",
21587 "count follows nothing",
21588 "trailing backslash",
21589 "corrupted program",
21590 "contains null char",
21592 const char *err;
21594 if (errcode < 0 || errcode >= REG_ERR_NUM) {
21595 err = "Bad error code";
21597 else {
21598 err = error_strings[errcode];
21601 return snprintf(errbuf, errbuf_size, "%s", err);
21604 void regfree(regex_t *preg)
21606 free(preg->program);
21609 #endif
21611 #if defined(_WIN32) || defined(WIN32)
21612 #ifndef STRICT
21613 #define STRICT
21614 #endif
21615 #define WIN32_LEAN_AND_MEAN
21616 #include <windows.h>
21618 #if defined(HAVE_DLOPEN_COMPAT)
21619 void *dlopen(const char *path, int mode)
21621 JIM_NOTUSED(mode);
21623 return (void *)LoadLibraryA(path);
21626 int dlclose(void *handle)
21628 FreeLibrary((HANDLE)handle);
21629 return 0;
21632 void *dlsym(void *handle, const char *symbol)
21634 return GetProcAddress((HMODULE)handle, symbol);
21637 char *dlerror(void)
21639 static char msg[121];
21640 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
21641 LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
21642 return msg;
21644 #endif
21646 #ifdef _MSC_VER
21648 #include <sys/timeb.h>
21651 int gettimeofday(struct timeval *tv, void *unused)
21653 struct _timeb tb;
21655 _ftime(&tb);
21656 tv->tv_sec = tb.time;
21657 tv->tv_usec = tb.millitm * 1000;
21659 return 0;
21663 DIR *opendir(const char *name)
21665 DIR *dir = 0;
21667 if (name && name[0]) {
21668 size_t base_length = strlen(name);
21669 const char *all =
21670 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
21672 if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
21673 (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
21674 strcat(strcpy(dir->name, name), all);
21676 if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
21677 dir->result.d_name = 0;
21678 else {
21679 Jim_Free(dir->name);
21680 Jim_Free(dir);
21681 dir = 0;
21684 else {
21685 Jim_Free(dir);
21686 dir = 0;
21687 errno = ENOMEM;
21690 else {
21691 errno = EINVAL;
21693 return dir;
21696 int closedir(DIR * dir)
21698 int result = -1;
21700 if (dir) {
21701 if (dir->handle != -1)
21702 result = _findclose(dir->handle);
21703 Jim_Free(dir->name);
21704 Jim_Free(dir);
21706 if (result == -1)
21707 errno = EBADF;
21708 return result;
21711 struct dirent *readdir(DIR * dir)
21713 struct dirent *result = 0;
21715 if (dir && dir->handle != -1) {
21716 if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
21717 result = &dir->result;
21718 result->d_name = dir->info.name;
21721 else {
21722 errno = EBADF;
21724 return result;
21726 #endif
21727 #endif
21728 #ifndef JIM_BOOTSTRAP_LIB_ONLY
21729 #include <errno.h>
21730 #include <string.h>
21733 #ifdef USE_LINENOISE
21734 #include <unistd.h>
21735 #include "linenoise.h"
21736 #else
21737 #define MAX_LINE_LEN 512
21738 #endif
21740 char *Jim_HistoryGetline(const char *prompt)
21742 #ifdef USE_LINENOISE
21743 return linenoise(prompt);
21744 #else
21745 int len;
21746 char *line = malloc(MAX_LINE_LEN);
21748 fputs(prompt, stdout);
21749 fflush(stdout);
21751 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
21752 free(line);
21753 return NULL;
21755 len = strlen(line);
21756 if (len && line[len - 1] == '\n') {
21757 line[len - 1] = '\0';
21759 return line;
21760 #endif
21763 void Jim_HistoryLoad(const char *filename)
21765 #ifdef USE_LINENOISE
21766 linenoiseHistoryLoad(filename);
21767 #endif
21770 void Jim_HistoryAdd(const char *line)
21772 #ifdef USE_LINENOISE
21773 linenoiseHistoryAdd(line);
21774 #endif
21777 void Jim_HistorySave(const char *filename)
21779 #ifdef USE_LINENOISE
21780 linenoiseHistorySave(filename);
21781 #endif
21784 void Jim_HistoryShow(void)
21786 #ifdef USE_LINENOISE
21788 int i;
21789 int len;
21790 char **history = linenoiseHistory(&len);
21791 for (i = 0; i < len; i++) {
21792 printf("%4d %s\n", i + 1, history[i]);
21794 #endif
21797 int Jim_InteractivePrompt(Jim_Interp *interp)
21799 int retcode = JIM_OK;
21800 char *history_file = NULL;
21801 #ifdef USE_LINENOISE
21802 const char *home;
21804 home = getenv("HOME");
21805 if (home && isatty(STDIN_FILENO)) {
21806 int history_len = strlen(home) + sizeof("/.jim_history");
21807 history_file = Jim_Alloc(history_len);
21808 snprintf(history_file, history_len, "%s/.jim_history", home);
21809 Jim_HistoryLoad(history_file);
21811 #endif
21813 printf("Welcome to Jim version %d.%d\n",
21814 JIM_VERSION / 100, JIM_VERSION % 100);
21815 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
21817 while (1) {
21818 Jim_Obj *scriptObjPtr;
21819 const char *result;
21820 int reslen;
21821 char prompt[20];
21822 const char *str;
21824 if (retcode != 0) {
21825 const char *retcodestr = Jim_ReturnCode(retcode);
21827 if (*retcodestr == '?') {
21828 snprintf(prompt, sizeof(prompt) - 3, "[%d] ", retcode);
21830 else {
21831 snprintf(prompt, sizeof(prompt) - 3, "[%s] ", retcodestr);
21834 else {
21835 prompt[0] = '\0';
21837 strcat(prompt, ". ");
21839 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
21840 Jim_IncrRefCount(scriptObjPtr);
21841 while (1) {
21842 char state;
21843 int len;
21844 char *line;
21846 line = Jim_HistoryGetline(prompt);
21847 if (line == NULL) {
21848 if (errno == EINTR) {
21849 continue;
21851 Jim_DecrRefCount(interp, scriptObjPtr);
21852 retcode = JIM_OK;
21853 goto out;
21855 if (Jim_Length(scriptObjPtr) != 0) {
21856 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
21858 Jim_AppendString(interp, scriptObjPtr, line, -1);
21859 free(line);
21860 str = Jim_GetString(scriptObjPtr, &len);
21861 if (len == 0) {
21862 continue;
21864 if (Jim_ScriptIsComplete(str, len, &state))
21865 break;
21867 snprintf(prompt, sizeof(prompt), "%c> ", state);
21869 #ifdef USE_LINENOISE
21870 if (strcmp(str, "h") == 0) {
21872 Jim_HistoryShow();
21873 Jim_DecrRefCount(interp, scriptObjPtr);
21874 continue;
21877 Jim_HistoryAdd(Jim_String(scriptObjPtr));
21878 if (history_file) {
21879 Jim_HistorySave(history_file);
21881 #endif
21882 retcode = Jim_EvalObj(interp, scriptObjPtr);
21883 Jim_DecrRefCount(interp, scriptObjPtr);
21885 if (retcode == JIM_EXIT) {
21886 retcode = JIM_EXIT;
21887 break;
21889 if (retcode == JIM_ERR) {
21890 Jim_MakeErrorMessage(interp);
21892 result = Jim_GetString(Jim_GetResult(interp), &reslen);
21893 if (reslen) {
21894 printf("%s\n", result);
21897 out:
21898 Jim_Free(history_file);
21899 return retcode;
21902 #include <stdio.h>
21903 #include <stdlib.h>
21904 #include <string.h>
21908 extern int Jim_initjimshInit(Jim_Interp *interp);
21910 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
21912 int n;
21913 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
21916 for (n = 0; n < argc; n++) {
21917 Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
21919 Jim_ListAppendElement(interp, listObj, obj);
21922 Jim_SetVariableStr(interp, "argv", listObj);
21923 Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
21926 static void JimPrintErrorMessage(Jim_Interp *interp)
21928 Jim_MakeErrorMessage(interp);
21929 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
21932 int main(int argc, char *const argv[])
21934 int retcode;
21935 Jim_Interp *interp;
21937 if (argc > 1 && strcmp(argv[1], "--version") == 0) {
21938 printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
21939 return 0;
21943 interp = Jim_CreateInterp();
21944 Jim_RegisterCoreCommands(interp);
21947 if (Jim_InitStaticExtensions(interp) != JIM_OK) {
21948 JimPrintErrorMessage(interp);
21951 Jim_SetVariableStrWithStr(interp, "jim::argv0", argv[0]);
21952 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
21953 retcode = Jim_initjimshInit(interp);
21955 if (argc == 1) {
21956 if (retcode == JIM_ERR) {
21957 JimPrintErrorMessage(interp);
21959 if (retcode != JIM_EXIT) {
21960 JimSetArgv(interp, 0, NULL);
21961 retcode = Jim_InteractivePrompt(interp);
21964 else {
21965 if (argc > 2 && strcmp(argv[1], "-e") == 0) {
21966 JimSetArgv(interp, argc - 3, argv + 3);
21967 retcode = Jim_Eval(interp, argv[2]);
21968 if (retcode != JIM_ERR) {
21969 printf("%s\n", Jim_String(Jim_GetResult(interp)));
21972 else {
21973 Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
21974 JimSetArgv(interp, argc - 2, argv + 2);
21975 retcode = Jim_EvalFile(interp, argv[1]);
21977 if (retcode == JIM_ERR) {
21978 JimPrintErrorMessage(interp);
21981 if (retcode == JIM_EXIT) {
21982 retcode = Jim_GetExitCode(interp);
21984 else if (retcode == JIM_ERR) {
21985 retcode = 1;
21987 else {
21988 retcode = 0;
21990 Jim_FreeInterp(interp);
21991 return retcode;
21993 #endif