expr: prevent stack overflow
[jimtcl.git] / autosetup / jimsh0.c
blobfc2133fa431a1c8e14afa2f410bd7f76d5f4a163
1 /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */
2 #define JIM_TCL_COMPAT
3 #define JIM_ANSIC
4 #define JIM_REGEXP
5 #define HAVE_NO_AUTOCONF
6 #define _JIMAUTOCONF_H
7 #define TCL_LIBRARY "."
8 #define jim_ext_bootstrap
9 #define jim_ext_aio
10 #define jim_ext_readdir
11 #define jim_ext_regexp
12 #define jim_ext_file
13 #define jim_ext_glob
14 #define jim_ext_exec
15 #define jim_ext_clock
16 #define jim_ext_array
17 #define jim_ext_stdlib
18 #define jim_ext_tclcompat
19 #if defined(_MSC_VER)
20 #define TCL_PLATFORM_OS "windows"
21 #define TCL_PLATFORM_PLATFORM "windows"
22 #define TCL_PLATFORM_PATH_SEPARATOR ";"
23 #define HAVE_MKDIR_ONE_ARG
24 #define HAVE_SYSTEM
25 #elif defined(__MINGW32__)
26 #define TCL_PLATFORM_OS "mingw"
27 #define TCL_PLATFORM_PLATFORM "windows"
28 #define TCL_PLATFORM_PATH_SEPARATOR ";"
29 #define HAVE_MKDIR_ONE_ARG
30 #define HAVE_SYSTEM
31 #define HAVE_SYS_TIME_H
32 #define HAVE_DIRENT_H
33 #define HAVE_UNISTD_H
34 #define HAVE_UMASK
35 #include <sys/stat.h>
36 #ifndef S_IRWXG
37 #define S_IRWXG 0
38 #endif
39 #ifndef S_IRWXO
40 #define S_IRWXO 0
41 #endif
42 #else
43 #define TCL_PLATFORM_OS "unknown"
44 #define TCL_PLATFORM_PLATFORM "unix"
45 #define TCL_PLATFORM_PATH_SEPARATOR ":"
46 #ifdef _MINIX
47 #define vfork fork
48 #define _POSIX_SOURCE
49 #else
50 #define _GNU_SOURCE
51 #endif
52 #define HAVE_VFORK
53 #define HAVE_WAITPID
54 #define HAVE_ISATTY
55 #define HAVE_MKSTEMP
56 #define HAVE_LINK
57 #define HAVE_SYS_TIME_H
58 #define HAVE_DIRENT_H
59 #define HAVE_UNISTD_H
60 #define HAVE_UMASK
61 #endif
62 #define JIM_VERSION 77
63 #ifndef JIM_WIN32COMPAT_H
64 #define JIM_WIN32COMPAT_H
68 #ifdef __cplusplus
69 extern "C" {
70 #endif
73 #if defined(_WIN32) || defined(WIN32)
75 #define HAVE_DLOPEN
76 void *dlopen(const char *path, int mode);
77 int dlclose(void *handle);
78 void *dlsym(void *handle, const char *symbol);
79 char *dlerror(void);
82 #if defined(__MINGW32__)
83 #define JIM_SPRINTF_DOUBLE_NEEDS_FIX
84 #endif
86 #ifdef _MSC_VER
89 #if _MSC_VER >= 1000
90 #pragma warning(disable:4146)
91 #endif
93 #include <limits.h>
94 #define jim_wide _int64
95 #ifndef LLONG_MAX
96 #define LLONG_MAX 9223372036854775807I64
97 #endif
98 #ifndef LLONG_MIN
99 #define LLONG_MIN (-LLONG_MAX - 1I64)
100 #endif
101 #define JIM_WIDE_MIN LLONG_MIN
102 #define JIM_WIDE_MAX LLONG_MAX
103 #define JIM_WIDE_MODIFIER "I64d"
104 #define strcasecmp _stricmp
105 #define strtoull _strtoui64
107 #include <io.h>
109 struct timeval {
110 long tv_sec;
111 long tv_usec;
114 int gettimeofday(struct timeval *tv, void *unused);
116 #define HAVE_OPENDIR
117 struct dirent {
118 char *d_name;
121 typedef struct DIR {
122 long handle;
123 struct _finddata_t info;
124 struct dirent result;
125 char *name;
126 } DIR;
128 DIR *opendir(const char *name);
129 int closedir(DIR *dir);
130 struct dirent *readdir(DIR *dir);
132 #elif defined(__MINGW32__)
134 #include <stdlib.h>
135 #define strtod __strtod
137 #endif
139 #endif
141 #ifdef __cplusplus
143 #endif
145 #endif
146 #ifndef UTF8_UTIL_H
147 #define UTF8_UTIL_H
149 #ifdef __cplusplus
150 extern "C" {
151 #endif
155 #define MAX_UTF8_LEN 4
157 int utf8_fromunicode(char *p, unsigned uc);
159 #ifndef JIM_UTF8
160 #include <ctype.h>
163 #define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
164 #define utf8_strwidth(S, B) utf8_strlen((S), (B))
165 #define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
166 #define utf8_getchars(CP, C) (*(CP) = (C), 1)
167 #define utf8_upper(C) toupper(C)
168 #define utf8_title(C) toupper(C)
169 #define utf8_lower(C) tolower(C)
170 #define utf8_index(C, I) (I)
171 #define utf8_charlen(C) 1
172 #define utf8_prev_len(S, L) 1
173 #define utf8_width(C) 1
175 #else
177 #endif
179 #ifdef __cplusplus
181 #endif
183 #endif
185 #ifndef __JIM__H
186 #define __JIM__H
188 #ifdef __cplusplus
189 extern "C" {
190 #endif
192 #include <time.h>
193 #include <limits.h>
194 #include <stdio.h>
195 #include <stdlib.h>
196 #include <stdarg.h>
199 #ifndef HAVE_NO_AUTOCONF
200 #endif
204 #ifndef jim_wide
205 # ifdef HAVE_LONG_LONG
206 # define jim_wide long long
207 # ifndef LLONG_MAX
208 # define LLONG_MAX 9223372036854775807LL
209 # endif
210 # ifndef LLONG_MIN
211 # define LLONG_MIN (-LLONG_MAX - 1LL)
212 # endif
213 # define JIM_WIDE_MIN LLONG_MIN
214 # define JIM_WIDE_MAX LLONG_MAX
215 # else
216 # define jim_wide long
217 # define JIM_WIDE_MIN LONG_MIN
218 # define JIM_WIDE_MAX LONG_MAX
219 # endif
222 # ifdef HAVE_LONG_LONG
223 # define JIM_WIDE_MODIFIER "lld"
224 # else
225 # define JIM_WIDE_MODIFIER "ld"
226 # define strtoull strtoul
227 # endif
228 #endif
230 #define UCHAR(c) ((unsigned char)(c))
233 #define JIM_OK 0
234 #define JIM_ERR 1
235 #define JIM_RETURN 2
236 #define JIM_BREAK 3
237 #define JIM_CONTINUE 4
238 #define JIM_SIGNAL 5
239 #define JIM_EXIT 6
241 #define JIM_EVAL 7
243 #define JIM_MAX_CALLFRAME_DEPTH 1000
244 #define JIM_MAX_EVAL_DEPTH 2000
247 #define JIM_PRIV_FLAG_SHIFT 20
249 #define JIM_NONE 0
250 #define JIM_ERRMSG 1
251 #define JIM_ENUM_ABBREV 2
252 #define JIM_UNSHARED 4
253 #define JIM_MUSTEXIST 8
256 #define JIM_SUBST_NOVAR 1
257 #define JIM_SUBST_NOCMD 2
258 #define JIM_SUBST_NOESC 4
259 #define JIM_SUBST_FLAG 128
262 #define JIM_CASESENS 0
263 #define JIM_NOCASE 1
266 #define JIM_PATH_LEN 1024
269 #define JIM_NOTUSED(V) ((void) V)
271 #define JIM_LIBPATH "auto_path"
272 #define JIM_INTERACTIVE "tcl_interactive"
275 typedef struct Jim_Stack {
276 int len;
277 int maxlen;
278 void **vector;
279 } Jim_Stack;
282 typedef struct Jim_HashEntry {
283 void *key;
284 union {
285 void *val;
286 int intval;
287 } u;
288 struct Jim_HashEntry *next;
289 } Jim_HashEntry;
291 typedef struct Jim_HashTableType {
292 unsigned int (*hashFunction)(const void *key);
293 void *(*keyDup)(void *privdata, const void *key);
294 void *(*valDup)(void *privdata, const void *obj);
295 int (*keyCompare)(void *privdata, const void *key1, const void *key2);
296 void (*keyDestructor)(void *privdata, void *key);
297 void (*valDestructor)(void *privdata, void *obj);
298 } Jim_HashTableType;
300 typedef struct Jim_HashTable {
301 Jim_HashEntry **table;
302 const Jim_HashTableType *type;
303 void *privdata;
304 unsigned int size;
305 unsigned int sizemask;
306 unsigned int used;
307 unsigned int collisions;
308 unsigned int uniq;
309 } Jim_HashTable;
311 typedef struct Jim_HashTableIterator {
312 Jim_HashTable *ht;
313 Jim_HashEntry *entry, *nextEntry;
314 int index;
315 } Jim_HashTableIterator;
318 #define JIM_HT_INITIAL_SIZE 16
321 #define Jim_FreeEntryVal(ht, entry) \
322 if ((ht)->type->valDestructor) \
323 (ht)->type->valDestructor((ht)->privdata, (entry)->u.val)
325 #define Jim_SetHashVal(ht, entry, _val_) do { \
326 if ((ht)->type->valDup) \
327 (entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \
328 else \
329 (entry)->u.val = (_val_); \
330 } while(0)
332 #define Jim_FreeEntryKey(ht, entry) \
333 if ((ht)->type->keyDestructor) \
334 (ht)->type->keyDestructor((ht)->privdata, (entry)->key)
336 #define Jim_SetHashKey(ht, entry, _key_) do { \
337 if ((ht)->type->keyDup) \
338 (entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \
339 else \
340 (entry)->key = (void *)(_key_); \
341 } while(0)
343 #define Jim_CompareHashKeys(ht, key1, key2) \
344 (((ht)->type->keyCompare) ? \
345 (ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \
346 (key1) == (key2))
348 #define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq)
350 #define Jim_GetHashEntryKey(he) ((he)->key)
351 #define Jim_GetHashEntryVal(he) ((he)->u.val)
352 #define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
353 #define Jim_GetHashTableSize(ht) ((ht)->size)
354 #define Jim_GetHashTableUsed(ht) ((ht)->used)
357 typedef struct Jim_Obj {
358 char *bytes;
359 const struct Jim_ObjType *typePtr;
360 int refCount;
361 int length;
363 union {
365 jim_wide wideValue;
367 int intValue;
369 double doubleValue;
371 void *ptr;
373 struct {
374 void *ptr1;
375 void *ptr2;
376 } twoPtrValue;
378 struct {
379 struct Jim_Var *varPtr;
380 unsigned long callFrameId;
381 int global;
382 } varValue;
384 struct {
385 struct Jim_Obj *nsObj;
386 struct Jim_Cmd *cmdPtr;
387 unsigned long procEpoch;
388 } cmdValue;
390 struct {
391 struct Jim_Obj **ele;
392 int len;
393 int maxLen;
394 } listValue;
396 struct {
397 int maxLength;
398 int charLength;
399 } strValue;
401 struct {
402 unsigned long id;
403 struct Jim_Reference *refPtr;
404 } refValue;
406 struct {
407 struct Jim_Obj *fileNameObj;
408 int lineNumber;
409 } sourceValue;
411 struct {
412 struct Jim_Obj *varNameObjPtr;
413 struct Jim_Obj *indexObjPtr;
414 } dictSubstValue;
416 struct {
417 void *compre;
418 unsigned flags;
419 } regexpValue;
420 struct {
421 int line;
422 int argc;
423 } scriptLineValue;
424 } internalRep;
425 struct Jim_Obj *prevObjPtr;
426 struct Jim_Obj *nextObjPtr;
427 } Jim_Obj;
430 #define Jim_IncrRefCount(objPtr) \
431 ++(objPtr)->refCount
432 #define Jim_DecrRefCount(interp, objPtr) \
433 if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr)
434 #define Jim_IsShared(objPtr) \
435 ((objPtr)->refCount > 1)
437 #define Jim_FreeNewObj Jim_FreeObj
440 #define Jim_FreeIntRep(i,o) \
441 if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \
442 (o)->typePtr->freeIntRepProc(i, o)
445 #define Jim_GetIntRepPtr(o) (o)->internalRep.ptr
448 #define Jim_SetIntRepPtr(o, p) \
449 (o)->internalRep.ptr = (p)
452 struct Jim_Interp;
454 typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp,
455 struct Jim_Obj *objPtr);
456 typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp,
457 struct Jim_Obj *srcPtr, Jim_Obj *dupPtr);
458 typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr);
460 typedef struct Jim_ObjType {
461 const char *name;
462 Jim_FreeInternalRepProc *freeIntRepProc;
463 Jim_DupInternalRepProc *dupIntRepProc;
464 Jim_UpdateStringProc *updateStringProc;
465 int flags;
466 } Jim_ObjType;
469 #define JIM_TYPE_NONE 0
470 #define JIM_TYPE_REFERENCES 1
474 typedef struct Jim_CallFrame {
475 unsigned long id;
476 int level;
477 struct Jim_HashTable vars;
478 struct Jim_HashTable *staticVars;
479 struct Jim_CallFrame *parent;
480 Jim_Obj *const *argv;
481 int argc;
482 Jim_Obj *procArgsObjPtr;
483 Jim_Obj *procBodyObjPtr;
484 struct Jim_CallFrame *next;
485 Jim_Obj *nsObj;
486 Jim_Obj *fileNameObj;
487 int line;
488 Jim_Stack *localCommands;
489 struct Jim_Obj *tailcallObj;
490 struct Jim_Cmd *tailcallCmd;
491 } Jim_CallFrame;
493 typedef struct Jim_Var {
494 Jim_Obj *objPtr;
495 struct Jim_CallFrame *linkFramePtr;
496 } Jim_Var;
499 typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
500 Jim_Obj *const *argv);
501 typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);
505 typedef struct Jim_Cmd {
506 int inUse;
507 int isproc;
508 struct Jim_Cmd *prevCmd;
509 union {
510 struct {
512 Jim_CmdProc *cmdProc;
513 Jim_DelCmdProc *delProc;
514 void *privData;
515 } native;
516 struct {
518 Jim_Obj *argListObjPtr;
519 Jim_Obj *bodyObjPtr;
520 Jim_HashTable *staticVars;
521 int argListLen;
522 int reqArity;
523 int optArity;
524 int argsPos;
525 int upcall;
526 struct Jim_ProcArg {
527 Jim_Obj *nameObjPtr;
528 Jim_Obj *defaultObjPtr;
529 } *arglist;
530 Jim_Obj *nsObj;
531 } proc;
532 } u;
533 } Jim_Cmd;
536 typedef struct Jim_PrngState {
537 unsigned char sbox[256];
538 unsigned int i, j;
539 } Jim_PrngState;
541 typedef struct Jim_Interp {
542 Jim_Obj *result;
543 int errorLine;
544 Jim_Obj *errorFileNameObj;
545 int addStackTrace;
546 int maxCallFrameDepth;
547 int maxEvalDepth;
548 int evalDepth;
549 int returnCode;
550 int returnLevel;
551 int exitCode;
552 long id;
553 int signal_level;
554 jim_wide sigmask;
555 int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask);
556 Jim_CallFrame *framePtr;
557 Jim_CallFrame *topFramePtr;
558 struct Jim_HashTable commands;
559 unsigned long procEpoch; /* Incremented every time the result
560 of procedures names lookup caching
561 may no longer be valid. */
562 unsigned long callFrameEpoch; /* Incremented every time a new
563 callframe is created. This id is used for the
564 'ID' field contained in the Jim_CallFrame
565 structure. */
566 int local;
567 Jim_Obj *liveList;
568 Jim_Obj *freeList;
569 Jim_Obj *currentScriptObj;
570 Jim_Obj *nullScriptObj;
571 Jim_Obj *emptyObj;
572 Jim_Obj *trueObj;
573 Jim_Obj *falseObj;
574 unsigned long referenceNextId;
575 struct Jim_HashTable references;
576 unsigned long lastCollectId; /* reference max Id of the last GC
577 execution. It's set to -1 while the collection
578 is running as sentinel to avoid to recursive
579 calls via the [collect] command inside
580 finalizers. */
581 time_t lastCollectTime;
582 Jim_Obj *stackTrace;
583 Jim_Obj *errorProc;
584 Jim_Obj *unknown;
585 int unknown_called;
586 int errorFlag;
587 void *cmdPrivData; /* Used to pass the private data pointer to
588 a command. It is set to what the user specified
589 via Jim_CreateCommand(). */
591 struct Jim_CallFrame *freeFramesList;
592 struct Jim_HashTable assocData;
593 Jim_PrngState *prngState;
594 struct Jim_HashTable packages;
595 Jim_Stack *loadHandles;
596 } Jim_Interp;
598 #define Jim_InterpIncrProcEpoch(i) (i)->procEpoch++
599 #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
600 #define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))
602 #define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
603 #define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj)
604 #define Jim_GetResult(i) ((i)->result)
605 #define Jim_CmdPrivData(i) ((i)->cmdPrivData)
607 #define Jim_SetResult(i,o) do { \
608 Jim_Obj *_resultObjPtr_ = (o); \
609 Jim_IncrRefCount(_resultObjPtr_); \
610 Jim_DecrRefCount(i,(i)->result); \
611 (i)->result = _resultObjPtr_; \
612 } while(0)
615 #define Jim_GetId(i) (++(i)->id)
618 #define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference
619 string representation must be fixed length. */
620 typedef struct Jim_Reference {
621 Jim_Obj *objPtr;
622 Jim_Obj *finalizerCmdNamePtr;
623 char tag[JIM_REFERENCE_TAGLEN+1];
624 } Jim_Reference;
627 #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
628 #define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
630 #define JIM_EXPORT
633 JIM_EXPORT void *Jim_Alloc (int size);
634 JIM_EXPORT void *Jim_Realloc(void *ptr, int size);
635 JIM_EXPORT void Jim_Free (void *ptr);
636 JIM_EXPORT char * Jim_StrDup (const char *s);
637 JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
640 JIM_EXPORT char **Jim_GetEnviron(void);
641 JIM_EXPORT void Jim_SetEnviron(char **env);
642 JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template);
645 JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
648 JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);
650 #define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S))
652 JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script);
653 JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename);
654 JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename);
655 JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr);
656 JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc,
657 Jim_Obj *const *objv);
658 JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj);
659 JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
660 int objc, Jim_Obj *const *objv);
661 #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
662 JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
663 JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
664 Jim_Obj **resObjPtrPtr, int flags);
667 JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
668 JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
669 JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
670 JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
671 JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
672 JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
673 JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr));
676 JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
677 const Jim_HashTableType *type, void *privdata);
678 JIM_EXPORT void Jim_ExpandHashTable (Jim_HashTable *ht,
679 unsigned int size);
680 JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key,
681 void *val);
682 JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
683 const void *key, void *val);
684 JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
685 const void *key);
686 JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
687 JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
688 const void *key);
689 JIM_EXPORT void Jim_ResizeHashTable (Jim_HashTable *ht);
690 JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator
691 (Jim_HashTable *ht);
692 JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
693 (Jim_HashTableIterator *iter);
696 JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
697 JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr);
698 JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr);
699 JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
700 Jim_Obj *objPtr);
701 JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
702 int *lenPtr);
703 JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
704 JIM_EXPORT int Jim_Length(Jim_Obj *objPtr);
707 JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp,
708 const char *s, int len);
709 JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp,
710 const char *s, int charlen);
711 JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp,
712 char *s, int len);
713 JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr,
714 const char *str, int len);
715 JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr,
716 Jim_Obj *appendObjPtr);
717 JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp,
718 Jim_Obj *objPtr, ...);
719 JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr);
720 JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr,
721 Jim_Obj *objPtr, int nocase);
722 JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp,
723 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr,
724 Jim_Obj *lastObjPtr);
725 JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp,
726 Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
727 JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
728 Jim_Obj *fmtObjPtr, int flags);
729 JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
730 Jim_Obj *objPtr, const char *str);
731 JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
732 Jim_Obj *secondObjPtr, int nocase);
733 JIM_EXPORT int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
734 Jim_Obj *secondObjPtr, int nocase);
735 JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr);
738 JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp,
739 Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr);
740 JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp,
741 Jim_Obj *objPtr);
742 JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr);
743 JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr);
746 JIM_EXPORT Jim_Interp * Jim_CreateInterp (void);
747 JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i);
748 JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp);
749 JIM_EXPORT const char *Jim_ReturnCode(int code);
750 JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...);
753 JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
754 JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
755 const char *cmdName, Jim_CmdProc *cmdProc, void *privData,
756 Jim_DelCmdProc *delProc);
757 JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
758 const char *cmdName);
759 JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
760 const char *oldName, const char *newName);
761 JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
762 Jim_Obj *objPtr, int flags);
763 JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
764 Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
765 JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
766 const char *name, Jim_Obj *objPtr);
767 JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp,
768 const char *name, Jim_Obj *objPtr);
769 JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp,
770 const char *name, const char *val);
771 JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp,
772 Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr,
773 Jim_CallFrame *targetCallFrame);
774 JIM_EXPORT Jim_Obj * Jim_MakeGlobalNamespaceName(Jim_Interp *interp,
775 Jim_Obj *nameObjPtr);
776 JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp,
777 Jim_Obj *nameObjPtr, int flags);
778 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp,
779 Jim_Obj *nameObjPtr, int flags);
780 JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp,
781 const char *name, int flags);
782 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp,
783 const char *name, int flags);
784 JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp,
785 Jim_Obj *nameObjPtr, int flags);
788 JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp,
789 Jim_Obj *levelObjPtr);
792 JIM_EXPORT int Jim_Collect (Jim_Interp *interp);
793 JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp);
796 JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr,
797 int *indexPtr);
800 JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp,
801 Jim_Obj *const *elements, int len);
802 JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp,
803 Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec);
804 JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp,
805 Jim_Obj *listPtr, Jim_Obj *objPtr);
806 JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp,
807 Jim_Obj *listPtr, Jim_Obj *appendListPtr);
808 JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr);
809 JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt,
810 int listindex, Jim_Obj **objPtrPtr, int seterr);
811 JIM_EXPORT Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx);
812 JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp,
813 Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc,
814 Jim_Obj *newObjPtr);
815 JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc,
816 Jim_Obj *const *objv);
817 JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp,
818 Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen);
821 JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp,
822 Jim_Obj *const *elements, int len);
823 JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr,
824 Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
825 JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
826 Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
827 Jim_Obj **objPtrPtr, int flags);
828 JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
829 Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
830 Jim_Obj *newObjPtr, int flags);
831 JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp,
832 Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len);
833 JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
834 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
836 #define JIM_DICTMATCH_KEYS 0x0001
837 #define JIM_DICTMATCH_VALUES 0x002
839 JIM_EXPORT int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types);
840 JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
841 JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr);
842 JIM_EXPORT Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv);
845 JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
846 int *intPtr);
849 JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
850 Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr);
851 JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
852 Jim_Obj *exprObjPtr, int *boolPtr);
855 JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
856 int *booleanPtr);
859 JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
860 jim_wide *widePtr);
861 JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
862 long *longPtr);
863 #define Jim_NewWideObj Jim_NewIntObj
864 JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
865 jim_wide wideValue);
868 JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
869 double *doublePtr);
870 JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
871 double doubleValue);
872 JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue);
875 JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc,
876 Jim_Obj *const *argv, const char *msg);
877 JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
878 const char * const *tablePtr, int *indexPtr, const char *name, int flags);
879 JIM_EXPORT int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr,
880 const char *const *tablePtr);
881 JIM_EXPORT int Jim_ScriptIsComplete(Jim_Interp *interp,
882 Jim_Obj *scriptObj, char *stateCharPtr);
884 JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);
887 typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
888 JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
889 JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
890 Jim_InterpDeleteProc *delProc, void *data);
891 JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
895 JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
896 const char *name, const char *ver, int flags);
897 JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
898 const char *name, int flags);
901 JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
904 JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
905 JIM_EXPORT void Jim_HistoryLoad(const char *filename);
906 JIM_EXPORT void Jim_HistorySave(const char *filename);
907 JIM_EXPORT char *Jim_HistoryGetline(const char *prompt);
908 JIM_EXPORT void Jim_HistoryAdd(const char *line);
909 JIM_EXPORT void Jim_HistoryShow(void);
912 JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
913 JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
914 JIM_EXPORT int Jim_IsBigEndian(void);
916 #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
919 JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
920 JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
923 JIM_EXPORT FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
926 JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
927 JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
929 #ifdef __cplusplus
931 #endif
933 #endif
935 #ifndef JIM_SUBCMD_H
936 #define JIM_SUBCMD_H
939 #ifdef __cplusplus
940 extern "C" {
941 #endif
944 #define JIM_MODFLAG_HIDDEN 0x0001
945 #define JIM_MODFLAG_FULLARGV 0x0002
949 typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
951 typedef struct {
952 const char *cmd;
953 const char *args;
954 jim_subcmd_function *function;
955 short minargs;
956 short maxargs;
957 unsigned short flags;
958 } jim_subcmd_type;
960 const jim_subcmd_type *
961 Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
963 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
965 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
967 #ifdef __cplusplus
969 #endif
971 #endif
972 #ifndef JIMREGEXP_H
973 #define JIMREGEXP_H
976 #ifdef __cplusplus
977 extern "C" {
978 #endif
980 #include <stdlib.h>
982 typedef struct {
983 int rm_so;
984 int rm_eo;
985 } regmatch_t;
988 typedef struct regexp {
990 int re_nsub;
993 int cflags;
994 int err;
995 int regstart;
996 int reganch;
997 int regmust;
998 int regmlen;
999 int *program;
1002 const char *regparse;
1003 int p;
1004 int proglen;
1007 int eflags;
1008 const char *start;
1009 const char *reginput;
1010 const char *regbol;
1013 regmatch_t *pmatch;
1014 int nmatch;
1015 } regexp;
1017 typedef regexp regex_t;
1019 #define REG_EXTENDED 0
1020 #define REG_NEWLINE 1
1021 #define REG_ICASE 2
1023 #define REG_NOTBOL 16
1025 enum {
1026 REG_NOERROR,
1027 REG_NOMATCH,
1028 REG_BADPAT,
1029 REG_ERR_NULL_ARGUMENT,
1030 REG_ERR_UNKNOWN,
1031 REG_ERR_TOO_BIG,
1032 REG_ERR_NOMEM,
1033 REG_ERR_TOO_MANY_PAREN,
1034 REG_ERR_UNMATCHED_PAREN,
1035 REG_ERR_UNMATCHED_BRACES,
1036 REG_ERR_BAD_COUNT,
1037 REG_ERR_JUNK_ON_END,
1038 REG_ERR_OPERAND_COULD_BE_EMPTY,
1039 REG_ERR_NESTED_COUNT,
1040 REG_ERR_INTERNAL,
1041 REG_ERR_COUNT_FOLLOWS_NOTHING,
1042 REG_ERR_TRAILING_BACKSLASH,
1043 REG_ERR_CORRUPTED,
1044 REG_ERR_NULL_CHAR,
1045 REG_ERR_NUM
1048 int regcomp(regex_t *preg, const char *regex, int cflags);
1049 int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
1050 size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
1051 void regfree(regex_t *preg);
1053 #ifdef __cplusplus
1055 #endif
1057 #endif
1058 int Jim_bootstrapInit(Jim_Interp *interp)
1060 if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
1061 return JIM_ERR;
1063 return Jim_EvalSource(interp, "bootstrap.tcl", 1,
1064 "\n"
1065 "\n"
1066 "proc package {cmd pkg} {\n"
1067 " if {$cmd eq \"require\"} {\n"
1068 " foreach path $::auto_path {\n"
1069 " if {[file exists $path/$pkg.tcl]} {\n"
1070 " uplevel #0 [list source $path/$pkg.tcl]\n"
1071 " return\n"
1072 " }\n"
1073 " }\n"
1074 " }\n"
1075 "}\n"
1078 int Jim_initjimshInit(Jim_Interp *interp)
1080 if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
1081 return JIM_ERR;
1083 return Jim_EvalSource(interp, "initjimsh.tcl", 1,
1084 "\n"
1085 "\n"
1086 "\n"
1087 "proc _jimsh_init {} {\n"
1088 " rename _jimsh_init {}\n"
1089 " global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
1090 "\n"
1091 "\n"
1092 " if {[exists jim::argv0]} {\n"
1093 " if {[string match \"*/*\" $jim::argv0]} {\n"
1094 " set jim::exe [file join [pwd] $jim::argv0]\n"
1095 " } else {\n"
1096 " foreach path [split [env PATH \"\"] $tcl_platform(pathSeparator)] {\n"
1097 " set exec [file join [pwd] [string map {\\\\ /} $path] $jim::argv0]\n"
1098 " if {[file executable $exec]} {\n"
1099 " set jim::exe $exec\n"
1100 " break\n"
1101 " }\n"
1102 " }\n"
1103 " }\n"
1104 " }\n"
1105 "\n"
1106 "\n"
1107 " lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
1108 " if {[exists jim::exe]} {\n"
1109 " lappend p [file dirname $jim::exe]\n"
1110 " }\n"
1111 " lappend p {*}$auto_path\n"
1112 " set auto_path $p\n"
1113 "\n"
1114 " if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
1115 " foreach src {.jimrc jimrc.tcl} {\n"
1116 " if {[file exists [env HOME]/$src]} {\n"
1117 " uplevel #0 source [env HOME]/$src\n"
1118 " break\n"
1119 " }\n"
1120 " }\n"
1121 " }\n"
1122 " return \"\"\n"
1123 "}\n"
1124 "\n"
1125 "if {$tcl_platform(platform) eq \"windows\"} {\n"
1126 " set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
1127 "}\n"
1128 "\n"
1129 "\n"
1130 "set tcl::autocomplete_commands {info tcl::prefix socket namespace array clock file package string dict signal history}\n"
1131 "\n"
1132 "\n"
1133 "\n"
1134 "proc tcl::autocomplete {prefix} {\n"
1135 " if {[set space [string first \" \" $prefix]] != -1} {\n"
1136 " set cmd [string range $prefix 0 $space-1]\n"
1137 " if {$cmd in $::tcl::autocomplete_commands || [info channel $cmd] ne \"\"} {\n"
1138 " set arg [string range $prefix $space+1 end]\n"
1139 "\n"
1140 " return [lmap p [$cmd -commands] {\n"
1141 " if {![string match \"${arg}*\" $p]} continue\n"
1142 " function \"$cmd $p\"\n"
1143 " }]\n"
1144 " }\n"
1145 " }\n"
1146 "\n"
1147 " if {[string match \"source *\" $prefix]} {\n"
1148 " set path [string range $prefix 7 end]\n"
1149 " return [lmap p [glob -nocomplain \"${path}*\"] {\n"
1150 " function \"source $p\"\n"
1151 " }]\n"
1152 " }\n"
1153 "\n"
1154 " return [lmap p [lsort [info commands $prefix*]] {\n"
1155 " if {[string match \"* *\" $p]} {\n"
1156 " continue\n"
1157 " }\n"
1158 " function $p\n"
1159 " }]\n"
1160 "}\n"
1161 "\n"
1162 "_jimsh_init\n"
1165 int Jim_globInit(Jim_Interp *interp)
1167 if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
1168 return JIM_ERR;
1170 return Jim_EvalSource(interp, "glob.tcl", 1,
1171 "\n"
1172 "\n"
1173 "\n"
1174 "\n"
1175 "\n"
1176 "\n"
1177 "\n"
1178 "package require readdir\n"
1179 "\n"
1180 "\n"
1181 "proc glob.globdir {dir pattern} {\n"
1182 " if {[file exists $dir/$pattern]} {\n"
1183 "\n"
1184 " return [list $pattern]\n"
1185 " }\n"
1186 "\n"
1187 " set result {}\n"
1188 " set files [readdir $dir]\n"
1189 " lappend files . ..\n"
1190 "\n"
1191 " foreach name $files {\n"
1192 " if {[string match $pattern $name]} {\n"
1193 "\n"
1194 " if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n"
1195 " continue\n"
1196 " }\n"
1197 " lappend result $name\n"
1198 " }\n"
1199 " }\n"
1200 "\n"
1201 " return $result\n"
1202 "}\n"
1203 "\n"
1204 "\n"
1205 "\n"
1206 "\n"
1207 "proc glob.explode {pattern} {\n"
1208 " set oldexp {}\n"
1209 " set newexp {\"\"}\n"
1210 "\n"
1211 " while 1 {\n"
1212 " set oldexp $newexp\n"
1213 " set newexp {}\n"
1214 " set ob [string first \\{ $pattern]\n"
1215 " set cb [string first \\} $pattern]\n"
1216 "\n"
1217 " if {$ob < $cb && $ob != -1} {\n"
1218 " set mid [string range $pattern 0 $ob-1]\n"
1219 " set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n"
1220 " if {$pattern eq \"\"} {\n"
1221 " error \"unmatched open brace in glob pattern\"\n"
1222 " }\n"
1223 " set pattern [string range $pattern 1 end]\n"
1224 "\n"
1225 " foreach subs $subexp {\n"
1226 " foreach sub [split $subs ,] {\n"
1227 " foreach old $oldexp {\n"
1228 " lappend newexp $old$mid$sub\n"
1229 " }\n"
1230 " }\n"
1231 " }\n"
1232 " } elseif {$cb != -1} {\n"
1233 " set suf [string range $pattern 0 $cb-1]\n"
1234 " set rest [string range $pattern $cb end]\n"
1235 " break\n"
1236 " } else {\n"
1237 " set suf $pattern\n"
1238 " set rest \"\"\n"
1239 " break\n"
1240 " }\n"
1241 " }\n"
1242 "\n"
1243 " foreach old $oldexp {\n"
1244 " lappend newexp $old$suf\n"
1245 " }\n"
1246 " list $rest {*}$newexp\n"
1247 "}\n"
1248 "\n"
1249 "\n"
1250 "\n"
1251 "proc glob.glob {base pattern} {\n"
1252 " set dir [file dirname $pattern]\n"
1253 " if {$pattern eq $dir || $pattern eq \"\"} {\n"
1254 " return [list [file join $base $dir] $pattern]\n"
1255 " } elseif {$pattern eq [file tail $pattern]} {\n"
1256 " set dir \"\"\n"
1257 " }\n"
1258 "\n"
1259 "\n"
1260 " set dirlist [glob.glob $base $dir]\n"
1261 " set pattern [file tail $pattern]\n"
1262 "\n"
1263 "\n"
1264 " set result {}\n"
1265 " foreach {realdir dir} $dirlist {\n"
1266 " if {![file isdir $realdir]} {\n"
1267 " continue\n"
1268 " }\n"
1269 " if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n"
1270 " append dir /\n"
1271 " }\n"
1272 " foreach name [glob.globdir $realdir $pattern] {\n"
1273 " lappend result [file join $realdir $name] $dir$name\n"
1274 " }\n"
1275 " }\n"
1276 " return $result\n"
1277 "}\n"
1278 "\n"
1279 "\n"
1280 "\n"
1281 "\n"
1282 "\n"
1283 "\n"
1284 "\n"
1285 "\n"
1286 "\n"
1287 "\n"
1288 "\n"
1289 "\n"
1290 "proc glob {args} {\n"
1291 " set nocomplain 0\n"
1292 " set base \"\"\n"
1293 " set tails 0\n"
1294 "\n"
1295 " set n 0\n"
1296 " foreach arg $args {\n"
1297 " if {[info exists param]} {\n"
1298 " set $param $arg\n"
1299 " unset param\n"
1300 " incr n\n"
1301 " continue\n"
1302 " }\n"
1303 " switch -glob -- $arg {\n"
1304 " -d* {\n"
1305 " set switch $arg\n"
1306 " set param base\n"
1307 " }\n"
1308 " -n* {\n"
1309 " set nocomplain 1\n"
1310 " }\n"
1311 " -ta* {\n"
1312 " set tails 1\n"
1313 " }\n"
1314 " -- {\n"
1315 " incr n\n"
1316 " break\n"
1317 " }\n"
1318 " -* {\n"
1319 " return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1320 " }\n"
1321 " * {\n"
1322 " break\n"
1323 " }\n"
1324 " }\n"
1325 " incr n\n"
1326 " }\n"
1327 " if {[info exists param]} {\n"
1328 " return -code error \"missing argument to \\\"$switch\\\"\"\n"
1329 " }\n"
1330 " if {[llength $args] <= $n} {\n"
1331 " return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n"
1332 " }\n"
1333 "\n"
1334 " set args [lrange $args $n end]\n"
1335 "\n"
1336 " set result {}\n"
1337 " foreach pattern $args {\n"
1338 " set escpattern [string map {\n"
1339 " \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
1340 " } $pattern]\n"
1341 " set patexps [lassign [glob.explode $escpattern] rest]\n"
1342 " if {$rest ne \"\"} {\n"
1343 " return -code error \"unmatched close brace in glob pattern\"\n"
1344 " }\n"
1345 " foreach patexp $patexps {\n"
1346 " set patexp [string map {\n"
1347 " \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
1348 " } $patexp]\n"
1349 " foreach {realname name} [glob.glob $base $patexp] {\n"
1350 " incr n\n"
1351 " if {$tails} {\n"
1352 " lappend result $name\n"
1353 " } else {\n"
1354 " lappend result [file join $base $name]\n"
1355 " }\n"
1356 " }\n"
1357 " }\n"
1358 " }\n"
1359 "\n"
1360 " if {!$nocomplain && [llength $result] == 0} {\n"
1361 " set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
1362 " return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
1363 " }\n"
1364 "\n"
1365 " return $result\n"
1366 "}\n"
1369 int Jim_stdlibInit(Jim_Interp *interp)
1371 if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
1372 return JIM_ERR;
1374 return Jim_EvalSource(interp, "stdlib.tcl", 1,
1375 "\n"
1376 "\n"
1377 "\n"
1378 "proc lambda {arglist args} {\n"
1379 " tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
1380 "}\n"
1381 "\n"
1382 "proc lambda.finalizer {name val} {\n"
1383 " rename $name {}\n"
1384 "}\n"
1385 "\n"
1386 "\n"
1387 "proc curry {args} {\n"
1388 " alias [ref {} function lambda.finalizer] {*}$args\n"
1389 "}\n"
1390 "\n"
1391 "\n"
1392 "\n"
1393 "\n"
1394 "\n"
1395 "\n"
1396 "\n"
1397 "\n"
1398 "\n"
1399 "proc function {value} {\n"
1400 " return $value\n"
1401 "}\n"
1402 "\n"
1403 "\n"
1404 "\n"
1405 "\n"
1406 "proc stacktrace {{skip 0}} {\n"
1407 " set trace {}\n"
1408 " incr skip\n"
1409 " foreach level [range $skip [info level]] {\n"
1410 " lappend trace {*}[info frame -$level]\n"
1411 " }\n"
1412 " return $trace\n"
1413 "}\n"
1414 "\n"
1415 "\n"
1416 "proc stackdump {stacktrace} {\n"
1417 " set lines {}\n"
1418 " foreach {l f p} [lreverse $stacktrace] {\n"
1419 " set line {}\n"
1420 " if {$p ne \"\"} {\n"
1421 " append line \"in procedure '$p' \"\n"
1422 " if {$f ne \"\"} {\n"
1423 " append line \"called \"\n"
1424 " }\n"
1425 " }\n"
1426 " if {$f ne \"\"} {\n"
1427 " append line \"at file \\\"$f\\\", line $l\"\n"
1428 " }\n"
1429 " if {$line ne \"\"} {\n"
1430 " lappend lines $line\n"
1431 " }\n"
1432 " }\n"
1433 " join $lines \\n\n"
1434 "}\n"
1435 "\n"
1436 "\n"
1437 "\n"
1438 "proc errorInfo {msg {stacktrace \"\"}} {\n"
1439 " if {$stacktrace eq \"\"} {\n"
1440 "\n"
1441 " set stacktrace [info stacktrace]\n"
1442 "\n"
1443 " lappend stacktrace {*}[stacktrace 1]\n"
1444 " }\n"
1445 " lassign $stacktrace p f l\n"
1446 " if {$f ne \"\"} {\n"
1447 " set result \"$f:$l: Error: \"\n"
1448 " }\n"
1449 " append result \"$msg\\n\"\n"
1450 " append result [stackdump $stacktrace]\n"
1451 "\n"
1452 "\n"
1453 " string trim $result\n"
1454 "}\n"
1455 "\n"
1456 "\n"
1457 "\n"
1458 "proc {info nameofexecutable} {} {\n"
1459 " if {[exists ::jim::exe]} {\n"
1460 " return $::jim::exe\n"
1461 " }\n"
1462 "}\n"
1463 "\n"
1464 "\n"
1465 "proc {dict update} {&varName args script} {\n"
1466 " set keys {}\n"
1467 " foreach {n v} $args {\n"
1468 " upvar $v var_$v\n"
1469 " if {[dict exists $varName $n]} {\n"
1470 " set var_$v [dict get $varName $n]\n"
1471 " }\n"
1472 " }\n"
1473 " catch {uplevel 1 $script} msg opts\n"
1474 " if {[info exists varName]} {\n"
1475 " foreach {n v} $args {\n"
1476 " if {[info exists var_$v]} {\n"
1477 " dict set varName $n [set var_$v]\n"
1478 " } else {\n"
1479 " dict unset varName $n\n"
1480 " }\n"
1481 " }\n"
1482 " }\n"
1483 " return {*}$opts $msg\n"
1484 "}\n"
1485 "\n"
1486 "proc {dict replace} {dictionary {args {key value}}} {\n"
1487 " if {[llength ${key value}] % 2} {\n"
1488 " tailcall {dict replace}\n"
1489 " }\n"
1490 " tailcall dict merge $dictionary ${key value}\n"
1491 "}\n"
1492 "\n"
1493 "\n"
1494 "proc {dict lappend} {varName key {args value}} {\n"
1495 " upvar $varName dict\n"
1496 " if {[exists dict] && [dict exists $dict $key]} {\n"
1497 " set list [dict get $dict $key]\n"
1498 " }\n"
1499 " lappend list {*}$value\n"
1500 " dict set dict $key $list\n"
1501 "}\n"
1502 "\n"
1503 "\n"
1504 "proc {dict append} {varName key {args value}} {\n"
1505 " upvar $varName dict\n"
1506 " if {[exists dict] && [dict exists $dict $key]} {\n"
1507 " set str [dict get $dict $key]\n"
1508 " }\n"
1509 " append str {*}$value\n"
1510 " dict set dict $key $str\n"
1511 "}\n"
1512 "\n"
1513 "\n"
1514 "proc {dict incr} {varName key {increment 1}} {\n"
1515 " upvar $varName dict\n"
1516 " if {[exists dict] && [dict exists $dict $key]} {\n"
1517 " set value [dict get $dict $key]\n"
1518 " }\n"
1519 " incr value $increment\n"
1520 " dict set dict $key $value\n"
1521 "}\n"
1522 "\n"
1523 "\n"
1524 "proc {dict remove} {dictionary {args key}} {\n"
1525 " foreach k $key {\n"
1526 " dict unset dictionary $k\n"
1527 " }\n"
1528 " return $dictionary\n"
1529 "}\n"
1530 "\n"
1531 "\n"
1532 "proc {dict for} {vars dictionary script} {\n"
1533 " if {[llength $vars] != 2} {\n"
1534 " return -code error \"must have exactly two variable names\"\n"
1535 " }\n"
1536 " dict size $dictionary\n"
1537 " tailcall foreach $vars $dictionary $script\n"
1538 "}\n"
1541 int Jim_tclcompatInit(Jim_Interp *interp)
1543 if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG))
1544 return JIM_ERR;
1546 return Jim_EvalSource(interp, "tclcompat.tcl", 1,
1547 "\n"
1548 "\n"
1549 "\n"
1550 "\n"
1551 "\n"
1552 "\n"
1553 "\n"
1554 "\n"
1555 "set env [env]\n"
1556 "\n"
1557 "\n"
1558 "if {[info commands stdout] ne \"\"} {\n"
1559 "\n"
1560 " foreach p {gets flush close eof seek tell} {\n"
1561 " proc $p {chan args} {p} {\n"
1562 " tailcall $chan $p {*}$args\n"
1563 " }\n"
1564 " }\n"
1565 " unset p\n"
1566 "\n"
1567 "\n"
1568 "\n"
1569 " proc puts {{-nonewline {}} {chan stdout} msg} {\n"
1570 " if {${-nonewline} ni {-nonewline {}}} {\n"
1571 " tailcall ${-nonewline} puts $msg\n"
1572 " }\n"
1573 " tailcall $chan puts {*}${-nonewline} $msg\n"
1574 " }\n"
1575 "\n"
1576 "\n"
1577 "\n"
1578 "\n"
1579 "\n"
1580 " proc read {{-nonewline {}} chan} {\n"
1581 " if {${-nonewline} ni {-nonewline {}}} {\n"
1582 " tailcall ${-nonewline} read {*}${chan}\n"
1583 " }\n"
1584 " tailcall $chan read {*}${-nonewline}\n"
1585 " }\n"
1586 "\n"
1587 " proc fconfigure {f args} {\n"
1588 " foreach {n v} $args {\n"
1589 " switch -glob -- $n {\n"
1590 " -bl* {\n"
1591 " $f ndelay $(!$v)\n"
1592 " }\n"
1593 " -bu* {\n"
1594 " $f buffering $v\n"
1595 " }\n"
1596 " -tr* {\n"
1597 "\n"
1598 " }\n"
1599 " default {\n"
1600 " return -code error \"fconfigure: unknown option $n\"\n"
1601 " }\n"
1602 " }\n"
1603 " }\n"
1604 " }\n"
1605 "}\n"
1606 "\n"
1607 "\n"
1608 "proc fileevent {args} {\n"
1609 " tailcall {*}$args\n"
1610 "}\n"
1611 "\n"
1612 "\n"
1613 "\n"
1614 "proc parray {arrayname {pattern *} {puts puts}} {\n"
1615 " upvar $arrayname a\n"
1616 "\n"
1617 " set max 0\n"
1618 " foreach name [array names a $pattern]] {\n"
1619 " if {[string length $name] > $max} {\n"
1620 " set max [string length $name]\n"
1621 " }\n"
1622 " }\n"
1623 " incr max [string length $arrayname]\n"
1624 " incr max 2\n"
1625 " foreach name [lsort [array names a $pattern]] {\n"
1626 " $puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
1627 " }\n"
1628 "}\n"
1629 "\n"
1630 "\n"
1631 "proc {file copy} {{force {}} source target} {\n"
1632 " try {\n"
1633 " if {$force ni {{} -force}} {\n"
1634 " error \"bad option \\\"$force\\\": should be -force\"\n"
1635 " }\n"
1636 "\n"
1637 " set in [open $source rb]\n"
1638 "\n"
1639 " if {[file exists $target]} {\n"
1640 " if {$force eq \"\"} {\n"
1641 " error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
1642 " }\n"
1643 "\n"
1644 " if {$source eq $target} {\n"
1645 " return\n"
1646 " }\n"
1647 "\n"
1648 "\n"
1649 " file stat $source ss\n"
1650 " file stat $target ts\n"
1651 " if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
1652 " return\n"
1653 " }\n"
1654 " }\n"
1655 " set out [open $target wb]\n"
1656 " $in copyto $out\n"
1657 " $out close\n"
1658 " } on error {msg opts} {\n"
1659 " incr opts(-level)\n"
1660 " return {*}$opts $msg\n"
1661 " } finally {\n"
1662 " catch {$in close}\n"
1663 " }\n"
1664 "}\n"
1665 "\n"
1666 "\n"
1667 "\n"
1668 "proc popen {cmd {mode r}} {\n"
1669 " lassign [socket pipe] r w\n"
1670 " try {\n"
1671 " if {[string match \"w*\" $mode]} {\n"
1672 " lappend cmd <@$r &\n"
1673 " set pids [exec {*}$cmd]\n"
1674 " $r close\n"
1675 " set f $w\n"
1676 " } else {\n"
1677 " lappend cmd >@$w &\n"
1678 " set pids [exec {*}$cmd]\n"
1679 " $w close\n"
1680 " set f $r\n"
1681 " }\n"
1682 " lambda {cmd args} {f pids} {\n"
1683 " if {$cmd eq \"pid\"} {\n"
1684 " return $pids\n"
1685 " }\n"
1686 " if {$cmd eq \"close\"} {\n"
1687 " $f close\n"
1688 "\n"
1689 " foreach p $pids { os.wait $p }\n"
1690 " return\n"
1691 " }\n"
1692 " tailcall $f $cmd {*}$args\n"
1693 " }\n"
1694 " } on error {error opts} {\n"
1695 " $r close\n"
1696 " $w close\n"
1697 " error $error\n"
1698 " }\n"
1699 "}\n"
1700 "\n"
1701 "\n"
1702 "local proc pid {{channelId {}}} {\n"
1703 " if {$channelId eq \"\"} {\n"
1704 " tailcall upcall pid\n"
1705 " }\n"
1706 " if {[catch {$channelId tell}]} {\n"
1707 " return -code error \"can not find channel named \\\"$channelId\\\"\"\n"
1708 " }\n"
1709 " if {[catch {$channelId pid} pids]} {\n"
1710 " return \"\"\n"
1711 " }\n"
1712 " return $pids\n"
1713 "}\n"
1714 "\n"
1715 "\n"
1716 "\n"
1717 "\n"
1718 "\n"
1719 "\n"
1720 "\n"
1721 "\n"
1722 "\n"
1723 "proc try {args} {\n"
1724 " set catchopts {}\n"
1725 " while {[string match -* [lindex $args 0]]} {\n"
1726 " set args [lassign $args opt]\n"
1727 " if {$opt eq \"--\"} {\n"
1728 " break\n"
1729 " }\n"
1730 " lappend catchopts $opt\n"
1731 " }\n"
1732 " if {[llength $args] == 0} {\n"
1733 " return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n"
1734 " }\n"
1735 " set args [lassign $args script]\n"
1736 " set code [catch -eval {*}$catchopts {uplevel 1 $script} msg opts]\n"
1737 "\n"
1738 " set handled 0\n"
1739 "\n"
1740 " foreach {on codes vars script} $args {\n"
1741 " switch -- $on \\\n"
1742 " on {\n"
1743 " if {!$handled && ($codes eq \"*\" || [info returncode $code] in $codes)} {\n"
1744 " lassign $vars msgvar optsvar\n"
1745 " if {$msgvar ne \"\"} {\n"
1746 " upvar $msgvar hmsg\n"
1747 " set hmsg $msg\n"
1748 " }\n"
1749 " if {$optsvar ne \"\"} {\n"
1750 " upvar $optsvar hopts\n"
1751 " set hopts $opts\n"
1752 " }\n"
1753 "\n"
1754 " set code [catch {uplevel 1 $script} msg opts]\n"
1755 " incr handled\n"
1756 " }\n"
1757 " } \\\n"
1758 " finally {\n"
1759 " set finalcode [catch {uplevel 1 $codes} finalmsg finalopts]\n"
1760 " if {$finalcode} {\n"
1761 "\n"
1762 " set code $finalcode\n"
1763 " set msg $finalmsg\n"
1764 " set opts $finalopts\n"
1765 " }\n"
1766 " break\n"
1767 " } \\\n"
1768 " default {\n"
1769 " return -code error \"try: expected 'on' or 'finally', got '$on'\"\n"
1770 " }\n"
1771 " }\n"
1772 "\n"
1773 " if {$code} {\n"
1774 " incr opts(-level)\n"
1775 " return {*}$opts $msg\n"
1776 " }\n"
1777 " return $msg\n"
1778 "}\n"
1779 "\n"
1780 "\n"
1781 "\n"
1782 "proc throw {code {msg \"\"}} {\n"
1783 " return -code $code $msg\n"
1784 "}\n"
1785 "\n"
1786 "\n"
1787 "proc {file delete force} {path} {\n"
1788 " foreach e [readdir $path] {\n"
1789 " file delete -force $path/$e\n"
1790 " }\n"
1791 " file delete $path\n"
1792 "}\n"
1797 #ifndef _GNU_SOURCE
1798 #define _GNU_SOURCE
1799 #endif
1800 #include <stdio.h>
1801 #include <string.h>
1802 #include <errno.h>
1803 #include <fcntl.h>
1804 #ifdef HAVE_UNISTD_H
1805 #include <unistd.h>
1806 #include <sys/stat.h>
1807 #endif
1810 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
1811 #include <sys/socket.h>
1812 #include <netinet/in.h>
1813 #include <arpa/inet.h>
1814 #include <netdb.h>
1815 #ifdef HAVE_SYS_UN_H
1816 #include <sys/un.h>
1817 #endif
1818 #else
1819 #define JIM_ANSIC
1820 #endif
1822 #if defined(JIM_SSL)
1823 #include <openssl/ssl.h>
1824 #include <openssl/err.h>
1825 #endif
1827 #ifdef HAVE_TERMIOS_H
1828 #endif
1831 #define AIO_CMD_LEN 32
1832 #define AIO_BUF_LEN 256
1834 #ifndef HAVE_FTELLO
1835 #define ftello ftell
1836 #endif
1837 #ifndef HAVE_FSEEKO
1838 #define fseeko fseek
1839 #endif
1841 #define AIO_KEEPOPEN 1
1843 #if defined(JIM_IPV6)
1844 #define IPV6 1
1845 #else
1846 #define IPV6 0
1847 #ifndef PF_INET6
1848 #define PF_INET6 0
1849 #endif
1850 #endif
1852 #define JimCheckStreamError(interp, af) af->fops->error(af)
1855 struct AioFile;
1857 typedef struct {
1858 int (*writer)(struct AioFile *af, const char *buf, int len);
1859 int (*reader)(struct AioFile *af, char *buf, int len);
1860 const char *(*getline)(struct AioFile *af, char *buf, int len);
1861 int (*error)(const struct AioFile *af);
1862 const char *(*strerror)(struct AioFile *af);
1863 int (*verify)(struct AioFile *af);
1864 } JimAioFopsType;
1866 typedef struct AioFile
1868 FILE *fp;
1869 Jim_Obj *filename;
1870 int type;
1871 int openFlags;
1872 int fd;
1873 Jim_Obj *rEvent;
1874 Jim_Obj *wEvent;
1875 Jim_Obj *eEvent;
1876 int addr_family;
1877 void *ssl;
1878 const JimAioFopsType *fops;
1879 } AioFile;
1881 static int stdio_writer(struct AioFile *af, const char *buf, int len)
1883 return fwrite(buf, 1, len, af->fp);
1886 static int stdio_reader(struct AioFile *af, char *buf, int len)
1888 return fread(buf, 1, len, af->fp);
1891 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
1893 return fgets(buf, len, af->fp);
1896 static int stdio_error(const AioFile *af)
1898 if (!ferror(af->fp)) {
1899 return JIM_OK;
1901 clearerr(af->fp);
1903 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
1904 return JIM_OK;
1906 #ifdef ECONNRESET
1907 if (errno == ECONNRESET) {
1908 return JIM_OK;
1910 #endif
1911 #ifdef ECONNABORTED
1912 if (errno == ECONNABORTED) {
1913 return JIM_OK;
1915 #endif
1916 return JIM_ERR;
1919 static const char *stdio_strerror(struct AioFile *af)
1921 return strerror(errno);
1924 static const JimAioFopsType stdio_fops = {
1925 stdio_writer,
1926 stdio_reader,
1927 stdio_getline,
1928 stdio_error,
1929 stdio_strerror,
1930 NULL
1934 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
1935 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1936 const char *hdlfmt, int family, const char *mode);
1939 static const char *JimAioErrorString(AioFile *af)
1941 if (af && af->fops)
1942 return af->fops->strerror(af);
1944 return strerror(errno);
1947 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
1949 AioFile *af = Jim_CmdPrivData(interp);
1951 if (name) {
1952 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
1954 else {
1955 Jim_SetResultString(interp, JimAioErrorString(af), -1);
1959 static void JimAioDelProc(Jim_Interp *interp, void *privData)
1961 AioFile *af = privData;
1963 JIM_NOTUSED(interp);
1965 Jim_DecrRefCount(interp, af->filename);
1967 #ifdef jim_ext_eventloop
1969 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
1970 #endif
1972 #if defined(JIM_SSL)
1973 if (af->ssl != NULL) {
1974 SSL_free(af->ssl);
1976 #endif
1977 if (!(af->openFlags & AIO_KEEPOPEN)) {
1978 fclose(af->fp);
1981 Jim_Free(af);
1984 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1986 AioFile *af = Jim_CmdPrivData(interp);
1987 char buf[AIO_BUF_LEN];
1988 Jim_Obj *objPtr;
1989 int nonewline = 0;
1990 jim_wide neededLen = -1;
1992 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
1993 nonewline = 1;
1994 argv++;
1995 argc--;
1997 if (argc == 1) {
1998 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
1999 return JIM_ERR;
2000 if (neededLen < 0) {
2001 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
2002 return JIM_ERR;
2005 else if (argc) {
2006 return -1;
2008 objPtr = Jim_NewStringObj(interp, NULL, 0);
2009 while (neededLen != 0) {
2010 int retval;
2011 int readlen;
2013 if (neededLen == -1) {
2014 readlen = AIO_BUF_LEN;
2016 else {
2017 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
2019 retval = af->fops->reader(af, buf, readlen);
2020 if (retval > 0) {
2021 Jim_AppendString(interp, objPtr, buf, retval);
2022 if (neededLen != -1) {
2023 neededLen -= retval;
2026 if (retval != readlen)
2027 break;
2030 if (JimCheckStreamError(interp, af)) {
2031 Jim_FreeNewObj(interp, objPtr);
2032 return JIM_ERR;
2034 if (nonewline) {
2035 int len;
2036 const char *s = Jim_GetString(objPtr, &len);
2038 if (len > 0 && s[len - 1] == '\n') {
2039 objPtr->length--;
2040 objPtr->bytes[objPtr->length] = '\0';
2043 Jim_SetResult(interp, objPtr);
2044 return JIM_OK;
2047 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
2049 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
2052 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
2053 return (AioFile *) cmdPtr->u.native.privData;
2055 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
2056 return NULL;
2059 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
2061 AioFile *af;
2063 af = Jim_AioFile(interp, command);
2064 if (af == NULL) {
2065 return NULL;
2068 return af->fp;
2071 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2073 AioFile *af = Jim_CmdPrivData(interp);
2074 jim_wide count = 0;
2075 jim_wide maxlen = JIM_WIDE_MAX;
2076 AioFile *outf = Jim_AioFile(interp, argv[0]);
2078 if (outf == NULL) {
2079 return JIM_ERR;
2082 if (argc == 2) {
2083 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
2084 return JIM_ERR;
2088 while (count < maxlen) {
2089 char ch;
2091 if (af->fops->reader(af, &ch, 1) != 1) {
2092 break;
2094 if (outf->fops->writer(outf, &ch, 1) != 1) {
2095 break;
2097 count++;
2100 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
2101 return JIM_ERR;
2104 Jim_SetResultInt(interp, count);
2106 return JIM_OK;
2109 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2111 AioFile *af = Jim_CmdPrivData(interp);
2112 char buf[AIO_BUF_LEN];
2113 Jim_Obj *objPtr;
2114 int len;
2116 errno = 0;
2118 objPtr = Jim_NewStringObj(interp, NULL, 0);
2119 while (1) {
2120 buf[AIO_BUF_LEN - 1] = '_';
2122 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
2123 break;
2125 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
2126 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
2128 else {
2129 len = strlen(buf);
2131 if (len && (buf[len - 1] == '\n')) {
2133 len--;
2136 Jim_AppendString(interp, objPtr, buf, len);
2137 break;
2141 if (JimCheckStreamError(interp, af)) {
2143 Jim_FreeNewObj(interp, objPtr);
2144 return JIM_ERR;
2147 if (argc) {
2148 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
2149 Jim_FreeNewObj(interp, objPtr);
2150 return JIM_ERR;
2153 len = Jim_Length(objPtr);
2155 if (len == 0 && feof(af->fp)) {
2157 len = -1;
2159 Jim_SetResultInt(interp, len);
2161 else {
2162 Jim_SetResult(interp, objPtr);
2164 return JIM_OK;
2167 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2169 AioFile *af = Jim_CmdPrivData(interp);
2170 int wlen;
2171 const char *wdata;
2172 Jim_Obj *strObj;
2174 if (argc == 2) {
2175 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
2176 return -1;
2178 strObj = argv[1];
2180 else {
2181 strObj = argv[0];
2184 wdata = Jim_GetString(strObj, &wlen);
2185 if (af->fops->writer(af, wdata, wlen) == wlen) {
2186 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
2187 return JIM_OK;
2190 JimAioSetError(interp, af->filename);
2191 return JIM_ERR;
2194 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2196 #ifdef HAVE_ISATTY
2197 AioFile *af = Jim_CmdPrivData(interp);
2198 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
2199 #else
2200 Jim_SetResultInt(interp, 0);
2201 #endif
2203 return JIM_OK;
2207 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2209 AioFile *af = Jim_CmdPrivData(interp);
2211 if (fflush(af->fp) == EOF) {
2212 JimAioSetError(interp, af->filename);
2213 return JIM_ERR;
2215 return JIM_OK;
2218 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2220 AioFile *af = Jim_CmdPrivData(interp);
2222 Jim_SetResultInt(interp, feof(af->fp));
2223 return JIM_OK;
2226 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2228 if (argc == 3) {
2229 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
2230 static const char * const options[] = { "r", "w", NULL };
2231 enum { OPT_R, OPT_W, };
2232 int option;
2233 AioFile *af = Jim_CmdPrivData(interp);
2235 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2236 return JIM_ERR;
2238 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
2239 return JIM_OK;
2241 JimAioSetError(interp, NULL);
2242 #else
2243 Jim_SetResultString(interp, "async close not supported", -1);
2244 #endif
2245 return JIM_ERR;
2248 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
2251 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2253 AioFile *af = Jim_CmdPrivData(interp);
2254 int orig = SEEK_SET;
2255 jim_wide offset;
2257 if (argc == 2) {
2258 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
2259 orig = SEEK_SET;
2260 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
2261 orig = SEEK_CUR;
2262 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
2263 orig = SEEK_END;
2264 else {
2265 return -1;
2268 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
2269 return JIM_ERR;
2271 if (fseeko(af->fp, offset, orig) == -1) {
2272 JimAioSetError(interp, af->filename);
2273 return JIM_ERR;
2275 return JIM_OK;
2278 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2280 AioFile *af = Jim_CmdPrivData(interp);
2282 Jim_SetResultInt(interp, ftello(af->fp));
2283 return JIM_OK;
2286 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2288 AioFile *af = Jim_CmdPrivData(interp);
2290 Jim_SetResult(interp, af->filename);
2291 return JIM_OK;
2294 #ifdef O_NDELAY
2295 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2297 AioFile *af = Jim_CmdPrivData(interp);
2299 int fmode = fcntl(af->fd, F_GETFL);
2301 if (argc) {
2302 long nb;
2304 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
2305 return JIM_ERR;
2307 if (nb) {
2308 fmode |= O_NDELAY;
2310 else {
2311 fmode &= ~O_NDELAY;
2313 (void)fcntl(af->fd, F_SETFL, fmode);
2315 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
2316 return JIM_OK;
2318 #endif
2320 #ifdef HAVE_FSYNC
2321 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2323 AioFile *af = Jim_CmdPrivData(interp);
2325 fflush(af->fp);
2326 fsync(af->fd);
2327 return JIM_OK;
2329 #endif
2331 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2333 AioFile *af = Jim_CmdPrivData(interp);
2335 static const char * const options[] = {
2336 "none",
2337 "line",
2338 "full",
2339 NULL
2341 enum
2343 OPT_NONE,
2344 OPT_LINE,
2345 OPT_FULL,
2347 int option;
2349 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2350 return JIM_ERR;
2352 switch (option) {
2353 case OPT_NONE:
2354 setvbuf(af->fp, NULL, _IONBF, 0);
2355 break;
2356 case OPT_LINE:
2357 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
2358 break;
2359 case OPT_FULL:
2360 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
2361 break;
2363 return JIM_OK;
2366 #ifdef jim_ext_eventloop
2367 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
2369 Jim_Obj **objPtrPtr = clientData;
2371 Jim_DecrRefCount(interp, *objPtrPtr);
2372 *objPtrPtr = NULL;
2375 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
2377 Jim_Obj **objPtrPtr = clientData;
2379 return Jim_EvalObjBackground(interp, *objPtrPtr);
2382 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
2383 int argc, Jim_Obj * const *argv)
2385 if (argc == 0) {
2387 if (*scriptHandlerObj) {
2388 Jim_SetResult(interp, *scriptHandlerObj);
2390 return JIM_OK;
2393 if (*scriptHandlerObj) {
2395 Jim_DeleteFileHandler(interp, af->fd, mask);
2399 if (Jim_Length(argv[0]) == 0) {
2401 return JIM_OK;
2405 Jim_IncrRefCount(argv[0]);
2406 *scriptHandlerObj = argv[0];
2408 Jim_CreateFileHandler(interp, af->fd, mask,
2409 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
2411 return JIM_OK;
2414 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2416 AioFile *af = Jim_CmdPrivData(interp);
2418 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
2421 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2423 AioFile *af = Jim_CmdPrivData(interp);
2425 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
2428 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2430 AioFile *af = Jim_CmdPrivData(interp);
2432 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
2434 #endif
2439 static const jim_subcmd_type aio_command_table[] = {
2440 { "read",
2441 "?-nonewline? ?len?",
2442 aio_cmd_read,
2447 { "copyto",
2448 "handle ?size?",
2449 aio_cmd_copy,
2454 { "gets",
2455 "?var?",
2456 aio_cmd_gets,
2461 { "puts",
2462 "?-nonewline? str",
2463 aio_cmd_puts,
2468 { "isatty",
2469 NULL,
2470 aio_cmd_isatty,
2475 { "flush",
2476 NULL,
2477 aio_cmd_flush,
2482 { "eof",
2483 NULL,
2484 aio_cmd_eof,
2489 { "close",
2490 "?r(ead)|w(rite)?",
2491 aio_cmd_close,
2494 JIM_MODFLAG_FULLARGV,
2497 { "seek",
2498 "offset ?start|current|end",
2499 aio_cmd_seek,
2504 { "tell",
2505 NULL,
2506 aio_cmd_tell,
2511 { "filename",
2512 NULL,
2513 aio_cmd_filename,
2518 #ifdef O_NDELAY
2519 { "ndelay",
2520 "?0|1?",
2521 aio_cmd_ndelay,
2526 #endif
2527 #ifdef HAVE_FSYNC
2528 { "sync",
2529 NULL,
2530 aio_cmd_sync,
2535 #endif
2536 { "buffering",
2537 "none|line|full",
2538 aio_cmd_buffering,
2543 #ifdef jim_ext_eventloop
2544 { "readable",
2545 "?readable-script?",
2546 aio_cmd_readable,
2551 { "writable",
2552 "?writable-script?",
2553 aio_cmd_writable,
2558 { "onexception",
2559 "?exception-script?",
2560 aio_cmd_onexception,
2565 #endif
2566 { NULL }
2569 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2571 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
2574 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
2575 Jim_Obj *const *argv)
2577 const char *mode;
2579 if (argc != 2 && argc != 3) {
2580 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
2581 return JIM_ERR;
2584 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
2586 #ifdef jim_ext_tclcompat
2588 const char *filename = Jim_String(argv[1]);
2591 if (*filename == '|') {
2592 Jim_Obj *evalObj[3];
2594 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
2595 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
2596 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
2598 return Jim_EvalObjVector(interp, 3, evalObj);
2601 #endif
2602 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
2606 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
2607 const char *hdlfmt, int family, const char *mode)
2609 AioFile *af;
2610 char buf[AIO_CMD_LEN];
2611 int openFlags = 0;
2613 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2615 if (fh) {
2616 openFlags = AIO_KEEPOPEN;
2619 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2620 if (!filename) {
2621 filename = Jim_NewStringObj(interp, buf, -1);
2624 Jim_IncrRefCount(filename);
2626 if (fh == NULL) {
2627 #if !defined(JIM_ANSIC)
2628 if (fd >= 0) {
2629 fh = fdopen(fd, mode);
2631 else
2632 #endif
2633 fh = fopen(Jim_String(filename), mode);
2635 if (fh == NULL) {
2636 JimAioSetError(interp, filename);
2637 #if !defined(JIM_ANSIC)
2638 if (fd >= 0) {
2639 close(fd);
2641 #endif
2642 Jim_DecrRefCount(interp, filename);
2643 return NULL;
2648 af = Jim_Alloc(sizeof(*af));
2649 memset(af, 0, sizeof(*af));
2650 af->fp = fh;
2651 af->fd = fileno(fh);
2652 af->filename = filename;
2653 #ifdef FD_CLOEXEC
2654 if ((openFlags & AIO_KEEPOPEN) == 0) {
2655 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
2657 #endif
2658 af->openFlags = openFlags;
2659 af->addr_family = family;
2660 af->fops = &stdio_fops;
2661 af->ssl = NULL;
2663 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
2665 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
2667 return af;
2670 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
2671 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
2672 const char *hdlfmt, int family, const char *mode[2])
2674 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
2675 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
2676 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2678 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
2679 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2680 Jim_SetResult(interp, objPtr);
2681 return JIM_OK;
2686 close(p[0]);
2687 close(p[1]);
2688 JimAioSetError(interp, NULL);
2689 return JIM_ERR;
2691 #endif
2694 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template)
2696 #ifdef HAVE_MKSTEMP
2697 int fd;
2698 mode_t mask;
2699 Jim_Obj *filenameObj;
2701 if (filename_template == NULL) {
2702 const char *tmpdir = getenv("TMPDIR");
2703 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
2704 tmpdir = "/tmp/";
2706 filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
2707 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
2708 Jim_AppendString(interp, filenameObj, "/", 1);
2710 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
2712 else {
2713 filenameObj = Jim_NewStringObj(interp, filename_template, -1);
2717 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
2718 fd = mkstemp(filenameObj->bytes);
2719 umask(mask);
2720 if (fd < 0) {
2721 JimAioSetError(interp, filenameObj);
2722 Jim_FreeNewObj(interp, filenameObj);
2723 return -1;
2726 Jim_SetResult(interp, filenameObj);
2727 return fd;
2728 #else
2729 Jim_SetResultString(interp, "platform has no tempfile support", -1);
2730 return -1;
2731 #endif
2735 int Jim_aioInit(Jim_Interp *interp)
2737 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2738 return JIM_ERR;
2740 #if defined(JIM_SSL)
2741 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2742 #endif
2744 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2745 #ifndef JIM_ANSIC
2746 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2747 #endif
2750 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
2751 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
2752 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
2754 return JIM_OK;
2757 #include <errno.h>
2758 #include <stdio.h>
2759 #include <string.h>
2762 #ifdef HAVE_DIRENT_H
2763 #include <dirent.h>
2764 #endif
2766 int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2768 const char *dirPath;
2769 DIR *dirPtr;
2770 struct dirent *entryPtr;
2771 int nocomplain = 0;
2773 if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) {
2774 nocomplain = 1;
2776 if (argc != 2 && !nocomplain) {
2777 Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
2778 return JIM_ERR;
2781 dirPath = Jim_String(argv[1 + nocomplain]);
2783 dirPtr = opendir(dirPath);
2784 if (dirPtr == NULL) {
2785 if (nocomplain) {
2786 return JIM_OK;
2788 Jim_SetResultString(interp, strerror(errno), -1);
2789 return JIM_ERR;
2791 else {
2792 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
2794 while ((entryPtr = readdir(dirPtr)) != NULL) {
2795 if (entryPtr->d_name[0] == '.') {
2796 if (entryPtr->d_name[1] == '\0') {
2797 continue;
2799 if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0'))
2800 continue;
2802 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1));
2804 closedir(dirPtr);
2806 Jim_SetResult(interp, listObj);
2808 return JIM_OK;
2812 int Jim_readdirInit(Jim_Interp *interp)
2814 if (Jim_PackageProvide(interp, "readdir", "1.0", JIM_ERRMSG))
2815 return JIM_ERR;
2817 Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
2818 return JIM_OK;
2821 #include <stdlib.h>
2822 #include <string.h>
2824 #if defined(JIM_REGEXP)
2825 #else
2826 #include <regex.h>
2827 #endif
2829 static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2831 regfree(objPtr->internalRep.regexpValue.compre);
2832 Jim_Free(objPtr->internalRep.regexpValue.compre);
2835 static const Jim_ObjType regexpObjType = {
2836 "regexp",
2837 FreeRegexpInternalRep,
2838 NULL,
2839 NULL,
2840 JIM_TYPE_NONE
2843 static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
2845 regex_t *compre;
2846 const char *pattern;
2847 int ret;
2850 if (objPtr->typePtr == &regexpObjType &&
2851 objPtr->internalRep.regexpValue.compre && objPtr->internalRep.regexpValue.flags == flags) {
2853 return objPtr->internalRep.regexpValue.compre;
2859 pattern = Jim_String(objPtr);
2860 compre = Jim_Alloc(sizeof(regex_t));
2862 if ((ret = regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
2863 char buf[100];
2865 regerror(ret, compre, buf, sizeof(buf));
2866 Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
2867 regfree(compre);
2868 Jim_Free(compre);
2869 return NULL;
2872 Jim_FreeIntRep(interp, objPtr);
2874 objPtr->typePtr = &regexpObjType;
2875 objPtr->internalRep.regexpValue.flags = flags;
2876 objPtr->internalRep.regexpValue.compre = compre;
2878 return compre;
2881 int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2883 int opt_indices = 0;
2884 int opt_all = 0;
2885 int opt_inline = 0;
2886 regex_t *regex;
2887 int match, i, j;
2888 int offset = 0;
2889 regmatch_t *pmatch = NULL;
2890 int source_len;
2891 int result = JIM_OK;
2892 const char *pattern;
2893 const char *source_str;
2894 int num_matches = 0;
2895 int num_vars;
2896 Jim_Obj *resultListObj = NULL;
2897 int regcomp_flags = 0;
2898 int eflags = 0;
2899 int option;
2900 enum {
2901 OPT_INDICES, OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
2903 static const char * const options[] = {
2904 "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
2907 if (argc < 3) {
2908 wrongNumArgs:
2909 Jim_WrongNumArgs(interp, 1, argv,
2910 "?-switch ...? exp string ?matchVar? ?subMatchVar ...?");
2911 return JIM_ERR;
2914 for (i = 1; i < argc; i++) {
2915 const char *opt = Jim_String(argv[i]);
2917 if (*opt != '-') {
2918 break;
2920 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
2921 return JIM_ERR;
2923 if (option == OPT_END) {
2924 i++;
2925 break;
2927 switch (option) {
2928 case OPT_INDICES:
2929 opt_indices = 1;
2930 break;
2932 case OPT_NOCASE:
2933 regcomp_flags |= REG_ICASE;
2934 break;
2936 case OPT_LINE:
2937 regcomp_flags |= REG_NEWLINE;
2938 break;
2940 case OPT_ALL:
2941 opt_all = 1;
2942 break;
2944 case OPT_INLINE:
2945 opt_inline = 1;
2946 break;
2948 case OPT_START:
2949 if (++i == argc) {
2950 goto wrongNumArgs;
2952 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
2953 return JIM_ERR;
2955 break;
2958 if (argc - i < 2) {
2959 goto wrongNumArgs;
2962 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
2963 if (!regex) {
2964 return JIM_ERR;
2967 pattern = Jim_String(argv[i]);
2968 source_str = Jim_GetString(argv[i + 1], &source_len);
2970 num_vars = argc - i - 2;
2972 if (opt_inline) {
2973 if (num_vars) {
2974 Jim_SetResultString(interp, "regexp match variables not allowed when using -inline",
2975 -1);
2976 result = JIM_ERR;
2977 goto done;
2979 num_vars = regex->re_nsub + 1;
2982 pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch));
2984 if (offset) {
2985 if (offset < 0) {
2986 offset += source_len + 1;
2988 if (offset > source_len) {
2989 source_str += source_len;
2991 else if (offset > 0) {
2992 source_str += offset;
2994 eflags |= REG_NOTBOL;
2997 if (opt_inline) {
2998 resultListObj = Jim_NewListObj(interp, NULL, 0);
3001 next_match:
3002 match = regexec(regex, source_str, num_vars + 1, pmatch, eflags);
3003 if (match >= REG_BADPAT) {
3004 char buf[100];
3006 regerror(match, regex, buf, sizeof(buf));
3007 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3008 result = JIM_ERR;
3009 goto done;
3012 if (match == REG_NOMATCH) {
3013 goto done;
3016 num_matches++;
3018 if (opt_all && !opt_inline) {
3020 goto try_next_match;
3024 j = 0;
3025 for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
3026 Jim_Obj *resultObj;
3028 if (opt_indices) {
3029 resultObj = Jim_NewListObj(interp, NULL, 0);
3031 else {
3032 resultObj = Jim_NewStringObj(interp, "", 0);
3035 if (pmatch[j].rm_so == -1) {
3036 if (opt_indices) {
3037 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3038 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3041 else {
3042 int len = pmatch[j].rm_eo - pmatch[j].rm_so;
3044 if (opt_indices) {
3045 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
3046 offset + pmatch[j].rm_so));
3047 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
3048 offset + pmatch[j].rm_so + len - 1));
3050 else {
3051 Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, len);
3055 if (opt_inline) {
3056 Jim_ListAppendElement(interp, resultListObj, resultObj);
3058 else {
3060 result = Jim_SetVariable(interp, argv[i], resultObj);
3062 if (result != JIM_OK) {
3063 Jim_FreeObj(interp, resultObj);
3064 break;
3069 try_next_match:
3070 if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
3071 if (pmatch[0].rm_eo) {
3072 offset += pmatch[0].rm_eo;
3073 source_str += pmatch[0].rm_eo;
3075 else {
3076 source_str++;
3077 offset++;
3079 if (*source_str) {
3080 eflags = REG_NOTBOL;
3081 goto next_match;
3085 done:
3086 if (result == JIM_OK) {
3087 if (opt_inline) {
3088 Jim_SetResult(interp, resultListObj);
3090 else {
3091 Jim_SetResultInt(interp, num_matches);
3095 Jim_Free(pmatch);
3096 return result;
3099 #define MAX_SUB_MATCHES 50
3101 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3103 int regcomp_flags = 0;
3104 int regexec_flags = 0;
3105 int opt_all = 0;
3106 int offset = 0;
3107 regex_t *regex;
3108 const char *p;
3109 int result;
3110 regmatch_t pmatch[MAX_SUB_MATCHES + 1];
3111 int num_matches = 0;
3113 int i, j, n;
3114 Jim_Obj *varname;
3115 Jim_Obj *resultObj;
3116 const char *source_str;
3117 int source_len;
3118 const char *replace_str;
3119 int replace_len;
3120 const char *pattern;
3121 int option;
3122 enum {
3123 OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_END
3125 static const char * const options[] = {
3126 "-nocase", "-line", "-all", "-start", "--", NULL
3129 if (argc < 4) {
3130 wrongNumArgs:
3131 Jim_WrongNumArgs(interp, 1, argv,
3132 "?-switch ...? exp string subSpec ?varName?");
3133 return JIM_ERR;
3136 for (i = 1; i < argc; i++) {
3137 const char *opt = Jim_String(argv[i]);
3139 if (*opt != '-') {
3140 break;
3142 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3143 return JIM_ERR;
3145 if (option == OPT_END) {
3146 i++;
3147 break;
3149 switch (option) {
3150 case OPT_NOCASE:
3151 regcomp_flags |= REG_ICASE;
3152 break;
3154 case OPT_LINE:
3155 regcomp_flags |= REG_NEWLINE;
3156 break;
3158 case OPT_ALL:
3159 opt_all = 1;
3160 break;
3162 case OPT_START:
3163 if (++i == argc) {
3164 goto wrongNumArgs;
3166 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3167 return JIM_ERR;
3169 break;
3172 if (argc - i != 3 && argc - i != 4) {
3173 goto wrongNumArgs;
3176 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
3177 if (!regex) {
3178 return JIM_ERR;
3180 pattern = Jim_String(argv[i]);
3182 source_str = Jim_GetString(argv[i + 1], &source_len);
3183 replace_str = Jim_GetString(argv[i + 2], &replace_len);
3184 varname = argv[i + 3];
3187 resultObj = Jim_NewStringObj(interp, "", 0);
3189 if (offset) {
3190 if (offset < 0) {
3191 offset += source_len + 1;
3193 if (offset > source_len) {
3194 offset = source_len;
3196 else if (offset < 0) {
3197 offset = 0;
3202 Jim_AppendString(interp, resultObj, source_str, offset);
3205 n = source_len - offset;
3206 p = source_str + offset;
3207 do {
3208 int match = regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
3210 if (match >= REG_BADPAT) {
3211 char buf[100];
3213 regerror(match, regex, buf, sizeof(buf));
3214 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3215 return JIM_ERR;
3217 if (match == REG_NOMATCH) {
3218 break;
3221 num_matches++;
3223 Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
3226 for (j = 0; j < replace_len; j++) {
3227 int idx;
3228 int c = replace_str[j];
3230 if (c == '&') {
3231 idx = 0;
3233 else if (c == '\\' && j < replace_len) {
3234 c = replace_str[++j];
3235 if ((c >= '0') && (c <= '9')) {
3236 idx = c - '0';
3238 else if ((c == '\\') || (c == '&')) {
3239 Jim_AppendString(interp, resultObj, replace_str + j, 1);
3240 continue;
3242 else {
3243 Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2);
3244 continue;
3247 else {
3248 Jim_AppendString(interp, resultObj, replace_str + j, 1);
3249 continue;
3251 if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
3252 Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
3253 pmatch[idx].rm_eo - pmatch[idx].rm_so);
3257 p += pmatch[0].rm_eo;
3258 n -= pmatch[0].rm_eo;
3261 if (!opt_all || n == 0) {
3262 break;
3266 if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
3267 break;
3271 if (pattern[0] == '\0' && n) {
3273 Jim_AppendString(interp, resultObj, p, 1);
3274 p++;
3275 n--;
3278 regexec_flags |= REG_NOTBOL;
3279 } while (n);
3281 Jim_AppendString(interp, resultObj, p, -1);
3284 if (argc - i == 4) {
3285 result = Jim_SetVariable(interp, varname, resultObj);
3287 if (result == JIM_OK) {
3288 Jim_SetResultInt(interp, num_matches);
3290 else {
3291 Jim_FreeObj(interp, resultObj);
3294 else {
3295 Jim_SetResult(interp, resultObj);
3296 result = JIM_OK;
3299 return result;
3302 int Jim_regexpInit(Jim_Interp *interp)
3304 if (Jim_PackageProvide(interp, "regexp", "1.0", JIM_ERRMSG))
3305 return JIM_ERR;
3307 Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
3308 Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
3309 return JIM_OK;
3312 #include <limits.h>
3313 #include <stdlib.h>
3314 #include <string.h>
3315 #include <stdio.h>
3316 #include <errno.h>
3317 #include <sys/stat.h>
3320 #ifdef HAVE_UTIMES
3321 #include <sys/time.h>
3322 #endif
3323 #ifdef HAVE_UNISTD_H
3324 #include <unistd.h>
3325 #elif defined(_MSC_VER)
3326 #include <direct.h>
3327 #define F_OK 0
3328 #define W_OK 2
3329 #define R_OK 4
3330 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
3331 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
3332 #endif
3334 # ifndef MAXPATHLEN
3335 # define MAXPATHLEN JIM_PATH_LEN
3336 # endif
3338 #if defined(__MINGW32__) || defined(_MSC_VER)
3339 #define ISWINDOWS 1
3340 #else
3341 #define ISWINDOWS 0
3342 #endif
3345 static const char *JimGetFileType(int mode)
3347 if (S_ISREG(mode)) {
3348 return "file";
3350 else if (S_ISDIR(mode)) {
3351 return "directory";
3353 #ifdef S_ISCHR
3354 else if (S_ISCHR(mode)) {
3355 return "characterSpecial";
3357 #endif
3358 #ifdef S_ISBLK
3359 else if (S_ISBLK(mode)) {
3360 return "blockSpecial";
3362 #endif
3363 #ifdef S_ISFIFO
3364 else if (S_ISFIFO(mode)) {
3365 return "fifo";
3367 #endif
3368 #ifdef S_ISLNK
3369 else if (S_ISLNK(mode)) {
3370 return "link";
3372 #endif
3373 #ifdef S_ISSOCK
3374 else if (S_ISSOCK(mode)) {
3375 return "socket";
3377 #endif
3378 return "unknown";
3381 static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
3383 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
3384 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
3387 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
3390 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
3392 AppendStatElement(interp, listObj, "dev", sb->st_dev);
3393 AppendStatElement(interp, listObj, "ino", sb->st_ino);
3394 AppendStatElement(interp, listObj, "mode", sb->st_mode);
3395 AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
3396 AppendStatElement(interp, listObj, "uid", sb->st_uid);
3397 AppendStatElement(interp, listObj, "gid", sb->st_gid);
3398 AppendStatElement(interp, listObj, "size", sb->st_size);
3399 AppendStatElement(interp, listObj, "atime", sb->st_atime);
3400 AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
3401 AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
3402 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
3403 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));
3406 if (varName) {
3407 Jim_Obj *objPtr;
3408 objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
3410 if (objPtr) {
3411 Jim_Obj *objv[2];
3413 objv[0] = objPtr;
3414 objv[1] = listObj;
3416 objPtr = Jim_DictMerge(interp, 2, objv);
3417 if (objPtr == NULL) {
3419 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
3420 Jim_FreeNewObj(interp, listObj);
3421 return JIM_ERR;
3424 Jim_InvalidateStringRep(objPtr);
3426 Jim_FreeNewObj(interp, listObj);
3427 listObj = objPtr;
3429 Jim_SetVariable(interp, varName, listObj);
3433 Jim_SetResult(interp, listObj);
3435 return JIM_OK;
3438 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3440 const char *path = Jim_String(argv[0]);
3441 const char *p = strrchr(path, '/');
3443 if (!p && path[0] == '.' && path[1] == '.' && path[2] == '\0') {
3444 Jim_SetResultString(interp, "..", -1);
3445 } else if (!p) {
3446 Jim_SetResultString(interp, ".", -1);
3448 else if (p == path) {
3449 Jim_SetResultString(interp, "/", -1);
3451 else if (ISWINDOWS && p[-1] == ':') {
3453 Jim_SetResultString(interp, path, p - path + 1);
3455 else {
3456 Jim_SetResultString(interp, path, p - path);
3458 return JIM_OK;
3461 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3463 const char *path = Jim_String(argv[0]);
3464 const char *lastSlash = strrchr(path, '/');
3465 const char *p = strrchr(path, '.');
3467 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
3468 Jim_SetResult(interp, argv[0]);
3470 else {
3471 Jim_SetResultString(interp, path, p - path);
3473 return JIM_OK;
3476 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3478 const char *path = Jim_String(argv[0]);
3479 const char *lastSlash = strrchr(path, '/');
3480 const char *p = strrchr(path, '.');
3482 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
3483 p = "";
3485 Jim_SetResultString(interp, p, -1);
3486 return JIM_OK;
3489 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3491 const char *path = Jim_String(argv[0]);
3492 const char *lastSlash = strrchr(path, '/');
3494 if (lastSlash) {
3495 Jim_SetResultString(interp, lastSlash + 1, -1);
3497 else {
3498 Jim_SetResult(interp, argv[0]);
3500 return JIM_OK;
3503 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3505 #ifdef HAVE_REALPATH
3506 const char *path = Jim_String(argv[0]);
3507 char *newname = Jim_Alloc(MAXPATHLEN + 1);
3509 if (realpath(path, newname)) {
3510 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
3511 return JIM_OK;
3513 else {
3514 Jim_Free(newname);
3515 Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
3516 return JIM_ERR;
3518 #else
3519 Jim_SetResultString(interp, "Not implemented", -1);
3520 return JIM_ERR;
3521 #endif
3524 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3526 int i;
3527 char *newname = Jim_Alloc(MAXPATHLEN + 1);
3528 char *last = newname;
3530 *newname = 0;
3533 for (i = 0; i < argc; i++) {
3534 int len;
3535 const char *part = Jim_GetString(argv[i], &len);
3537 if (*part == '/') {
3539 last = newname;
3541 else if (ISWINDOWS && strchr(part, ':')) {
3543 last = newname;
3545 else if (part[0] == '.') {
3546 if (part[1] == '/') {
3547 part += 2;
3548 len -= 2;
3550 else if (part[1] == 0 && last != newname) {
3552 continue;
3557 if (last != newname && last[-1] != '/') {
3558 *last++ = '/';
3561 if (len) {
3562 if (last + len - newname >= MAXPATHLEN) {
3563 Jim_Free(newname);
3564 Jim_SetResultString(interp, "Path too long", -1);
3565 return JIM_ERR;
3567 memcpy(last, part, len);
3568 last += len;
3572 if (last > newname + 1 && last[-1] == '/') {
3574 if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
3575 *--last = 0;
3580 *last = 0;
3584 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
3586 return JIM_OK;
3589 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
3591 Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1);
3593 return JIM_OK;
3596 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3598 return file_access(interp, argv[0], R_OK);
3601 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3603 return file_access(interp, argv[0], W_OK);
3606 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3608 #ifdef X_OK
3609 return file_access(interp, argv[0], X_OK);
3610 #else
3612 Jim_SetResultBool(interp, 1);
3613 return JIM_OK;
3614 #endif
3617 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3619 return file_access(interp, argv[0], F_OK);
3622 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3624 int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
3626 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
3627 argc++;
3628 argv--;
3631 while (argc--) {
3632 const char *path = Jim_String(argv[0]);
3634 if (unlink(path) == -1 && errno != ENOENT) {
3635 if (rmdir(path) == -1) {
3637 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
3638 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
3639 strerror(errno));
3640 return JIM_ERR;
3644 argv++;
3646 return JIM_OK;
3649 #ifdef HAVE_MKDIR_ONE_ARG
3650 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
3651 #else
3652 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
3653 #endif
3655 static int mkdir_all(char *path)
3657 int ok = 1;
3660 goto first;
3662 while (ok--) {
3665 char *slash = strrchr(path, '/');
3667 if (slash && slash != path) {
3668 *slash = 0;
3669 if (mkdir_all(path) != 0) {
3670 return -1;
3672 *slash = '/';
3675 first:
3676 if (MKDIR_DEFAULT(path) == 0) {
3677 return 0;
3679 if (errno == ENOENT) {
3681 continue;
3684 if (errno == EEXIST) {
3685 struct stat sb;
3687 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
3688 return 0;
3691 errno = EEXIST;
3694 break;
3696 return -1;
3699 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3701 while (argc--) {
3702 char *path = Jim_StrDup(Jim_String(argv[0]));
3703 int rc = mkdir_all(path);
3705 Jim_Free(path);
3706 if (rc != 0) {
3707 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
3708 strerror(errno));
3709 return JIM_ERR;
3711 argv++;
3713 return JIM_OK;
3716 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3718 int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL);
3720 if (fd < 0) {
3721 return JIM_ERR;
3723 close(fd);
3725 return JIM_OK;
3728 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3730 const char *source;
3731 const char *dest;
3732 int force = 0;
3734 if (argc == 3) {
3735 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
3736 return -1;
3738 force++;
3739 argv++;
3740 argc--;
3743 source = Jim_String(argv[0]);
3744 dest = Jim_String(argv[1]);
3746 if (!force && access(dest, F_OK) == 0) {
3747 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
3748 argv[1]);
3749 return JIM_ERR;
3752 if (rename(source, dest) != 0) {
3753 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3754 strerror(errno));
3755 return JIM_ERR;
3758 return JIM_OK;
3761 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
3762 static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3764 int ret;
3765 const char *source;
3766 const char *dest;
3767 static const char * const options[] = { "-hard", "-symbolic", NULL };
3768 enum { OPT_HARD, OPT_SYMBOLIC, };
3769 int option = OPT_HARD;
3771 if (argc == 3) {
3772 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
3773 return JIM_ERR;
3775 argv++;
3776 argc--;
3779 dest = Jim_String(argv[0]);
3780 source = Jim_String(argv[1]);
3782 if (option == OPT_HARD) {
3783 ret = link(source, dest);
3785 else {
3786 ret = symlink(source, dest);
3789 if (ret != 0) {
3790 Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3791 strerror(errno));
3792 return JIM_ERR;
3795 return JIM_OK;
3797 #endif
3799 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3801 const char *path = Jim_String(filename);
3803 if (stat(path, sb) == -1) {
3804 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3805 return JIM_ERR;
3807 return JIM_OK;
3810 #ifdef HAVE_LSTAT
3811 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3813 const char *path = Jim_String(filename);
3815 if (lstat(path, sb) == -1) {
3816 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3817 return JIM_ERR;
3819 return JIM_OK;
3821 #else
3822 #define file_lstat file_stat
3823 #endif
3825 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3827 struct stat sb;
3829 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3830 return JIM_ERR;
3832 Jim_SetResultInt(interp, sb.st_atime);
3833 return JIM_OK;
3836 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3838 struct stat sb;
3840 if (argc == 2) {
3841 #ifdef HAVE_UTIMES
3842 jim_wide newtime;
3843 struct timeval times[2];
3845 if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) {
3846 return JIM_ERR;
3849 times[1].tv_sec = times[0].tv_sec = newtime;
3850 times[1].tv_usec = times[0].tv_usec = 0;
3852 if (utimes(Jim_String(argv[0]), times) != 0) {
3853 Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno));
3854 return JIM_ERR;
3856 #else
3857 Jim_SetResultString(interp, "Not implemented", -1);
3858 return JIM_ERR;
3859 #endif
3861 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3862 return JIM_ERR;
3864 Jim_SetResultInt(interp, sb.st_mtime);
3865 return JIM_OK;
3868 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3870 return Jim_EvalPrefix(interp, "file copy", argc, argv);
3873 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3875 struct stat sb;
3877 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3878 return JIM_ERR;
3880 Jim_SetResultInt(interp, sb.st_size);
3881 return JIM_OK;
3884 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3886 struct stat sb;
3887 int ret = 0;
3889 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3890 ret = S_ISDIR(sb.st_mode);
3892 Jim_SetResultInt(interp, ret);
3893 return JIM_OK;
3896 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3898 struct stat sb;
3899 int ret = 0;
3901 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3902 ret = S_ISREG(sb.st_mode);
3904 Jim_SetResultInt(interp, ret);
3905 return JIM_OK;
3908 #ifdef HAVE_GETEUID
3909 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3911 struct stat sb;
3912 int ret = 0;
3914 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3915 ret = (geteuid() == sb.st_uid);
3917 Jim_SetResultInt(interp, ret);
3918 return JIM_OK;
3920 #endif
3922 #if defined(HAVE_READLINK)
3923 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3925 const char *path = Jim_String(argv[0]);
3926 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
3928 int linkLength = readlink(path, linkValue, MAXPATHLEN);
3930 if (linkLength == -1) {
3931 Jim_Free(linkValue);
3932 Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
3933 return JIM_ERR;
3935 linkValue[linkLength] = 0;
3936 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
3937 return JIM_OK;
3939 #endif
3941 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3943 struct stat sb;
3945 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
3946 return JIM_ERR;
3948 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
3949 return JIM_OK;
3952 #ifdef HAVE_LSTAT
3953 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3955 struct stat sb;
3957 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
3958 return JIM_ERR;
3960 return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
3962 #else
3963 #define file_cmd_lstat file_cmd_stat
3964 #endif
3966 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3968 struct stat sb;
3970 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3971 return JIM_ERR;
3973 return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
3976 static const jim_subcmd_type file_command_table[] = {
3977 { "atime",
3978 "name",
3979 file_cmd_atime,
3984 { "mtime",
3985 "name ?time?",
3986 file_cmd_mtime,
3991 { "copy",
3992 "?-force? source dest",
3993 file_cmd_copy,
3998 { "dirname",
3999 "name",
4000 file_cmd_dirname,
4005 { "rootname",
4006 "name",
4007 file_cmd_rootname,
4012 { "extension",
4013 "name",
4014 file_cmd_extension,
4019 { "tail",
4020 "name",
4021 file_cmd_tail,
4026 { "normalize",
4027 "name",
4028 file_cmd_normalize,
4033 { "join",
4034 "name ?name ...?",
4035 file_cmd_join,
4040 { "readable",
4041 "name",
4042 file_cmd_readable,
4047 { "writable",
4048 "name",
4049 file_cmd_writable,
4054 { "executable",
4055 "name",
4056 file_cmd_executable,
4061 { "exists",
4062 "name",
4063 file_cmd_exists,
4068 { "delete",
4069 "?-force|--? name ...",
4070 file_cmd_delete,
4075 { "mkdir",
4076 "dir ...",
4077 file_cmd_mkdir,
4082 { "tempfile",
4083 "?template?",
4084 file_cmd_tempfile,
4089 { "rename",
4090 "?-force? source dest",
4091 file_cmd_rename,
4096 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
4097 { "link",
4098 "?-symbolic|-hard? newname target",
4099 file_cmd_link,
4104 #endif
4105 #if defined(HAVE_READLINK)
4106 { "readlink",
4107 "name",
4108 file_cmd_readlink,
4113 #endif
4114 { "size",
4115 "name",
4116 file_cmd_size,
4121 { "stat",
4122 "name ?var?",
4123 file_cmd_stat,
4128 { "lstat",
4129 "name ?var?",
4130 file_cmd_lstat,
4135 { "type",
4136 "name",
4137 file_cmd_type,
4142 #ifdef HAVE_GETEUID
4143 { "owned",
4144 "name",
4145 file_cmd_owned,
4150 #endif
4151 { "isdirectory",
4152 "name",
4153 file_cmd_isdirectory,
4158 { "isfile",
4159 "name",
4160 file_cmd_isfile,
4166 NULL
4170 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4172 const char *path;
4174 if (argc != 2) {
4175 Jim_WrongNumArgs(interp, 1, argv, "dirname");
4176 return JIM_ERR;
4179 path = Jim_String(argv[1]);
4181 if (chdir(path) != 0) {
4182 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
4183 strerror(errno));
4184 return JIM_ERR;
4186 return JIM_OK;
4189 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4191 char *cwd = Jim_Alloc(MAXPATHLEN);
4193 if (getcwd(cwd, MAXPATHLEN) == NULL) {
4194 Jim_SetResultString(interp, "Failed to get pwd", -1);
4195 Jim_Free(cwd);
4196 return JIM_ERR;
4198 else if (ISWINDOWS) {
4200 char *p = cwd;
4201 while ((p = strchr(p, '\\')) != NULL) {
4202 *p++ = '/';
4206 Jim_SetResultString(interp, cwd, -1);
4208 Jim_Free(cwd);
4209 return JIM_OK;
4212 int Jim_fileInit(Jim_Interp *interp)
4214 if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
4215 return JIM_ERR;
4217 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
4218 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
4219 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
4220 return JIM_OK;
4223 #ifndef _GNU_SOURCE
4224 #define _GNU_SOURCE
4225 #endif
4226 #include <string.h>
4227 #include <ctype.h>
4230 #if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
4231 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4233 Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
4234 int i, j;
4235 int rc;
4238 for (i = 1; i < argc; i++) {
4239 int len;
4240 const char *arg = Jim_GetString(argv[i], &len);
4242 if (i > 1) {
4243 Jim_AppendString(interp, cmdlineObj, " ", 1);
4245 if (strpbrk(arg, "\\\" ") == NULL) {
4247 Jim_AppendString(interp, cmdlineObj, arg, len);
4248 continue;
4251 Jim_AppendString(interp, cmdlineObj, "\"", 1);
4252 for (j = 0; j < len; j++) {
4253 if (arg[j] == '\\' || arg[j] == '"') {
4254 Jim_AppendString(interp, cmdlineObj, "\\", 1);
4256 Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
4258 Jim_AppendString(interp, cmdlineObj, "\"", 1);
4260 rc = system(Jim_String(cmdlineObj));
4262 Jim_FreeNewObj(interp, cmdlineObj);
4264 if (rc) {
4265 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
4266 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4267 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
4268 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
4269 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
4270 return JIM_ERR;
4273 return JIM_OK;
4276 int Jim_execInit(Jim_Interp *interp)
4278 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
4279 return JIM_ERR;
4281 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
4282 return JIM_OK;
4284 #else
4287 #include <errno.h>
4288 #include <signal.h>
4290 #if defined(__MINGW32__)
4292 #ifndef STRICT
4293 #define STRICT
4294 #endif
4295 #define WIN32_LEAN_AND_MEAN
4296 #include <windows.h>
4297 #include <fcntl.h>
4299 typedef HANDLE fdtype;
4300 typedef HANDLE pidtype;
4301 #define JIM_BAD_FD INVALID_HANDLE_VALUE
4302 #define JIM_BAD_PID INVALID_HANDLE_VALUE
4303 #define JimCloseFd CloseHandle
4305 #define WIFEXITED(STATUS) 1
4306 #define WEXITSTATUS(STATUS) (STATUS)
4307 #define WIFSIGNALED(STATUS) 0
4308 #define WTERMSIG(STATUS) 0
4309 #define WNOHANG 1
4311 static fdtype JimFileno(FILE *fh);
4312 static pidtype JimWaitPid(pidtype pid, int *status, int nohang);
4313 static fdtype JimDupFd(fdtype infd);
4314 static fdtype JimOpenForRead(const char *filename);
4315 static FILE *JimFdOpenForRead(fdtype fd);
4316 static int JimPipe(fdtype pipefd[2]);
4317 static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char **env,
4318 fdtype inputId, fdtype outputId, fdtype errorId);
4319 static int JimErrno(void);
4320 #else
4321 #include <unistd.h>
4322 #include <fcntl.h>
4323 #include <sys/wait.h>
4324 #include <sys/stat.h>
4326 typedef int fdtype;
4327 typedef int pidtype;
4328 #define JimPipe pipe
4329 #define JimErrno() errno
4330 #define JIM_BAD_FD -1
4331 #define JIM_BAD_PID -1
4332 #define JimFileno fileno
4333 #define JimReadFd read
4334 #define JimCloseFd close
4335 #define JimWaitPid waitpid
4336 #define JimDupFd dup
4337 #define JimFdOpenForRead(FD) fdopen((FD), "r")
4338 #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0)
4340 #ifndef HAVE_EXECVPE
4341 #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
4342 #endif
4343 #endif
4345 static const char *JimStrError(void);
4346 static char **JimOriginalEnviron(void);
4347 static char **JimSaveEnv(char **env);
4348 static void JimRestoreEnv(char **env);
4349 static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
4350 pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr);
4351 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr);
4352 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj);
4353 static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents, int len);
4354 static fdtype JimOpenForWrite(const char *filename, int append);
4355 static int JimRewindFd(fdtype fd);
4357 static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
4359 Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError());
4362 static const char *JimStrError(void)
4364 return strerror(JimErrno());
4367 static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
4369 int len;
4370 const char *s = Jim_GetString(objPtr, &len);
4372 if (len > 0 && s[len - 1] == '\n') {
4373 objPtr->length--;
4374 objPtr->bytes[objPtr->length] = '\0';
4378 static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj)
4380 char buf[256];
4381 FILE *fh = JimFdOpenForRead(fd);
4382 int ret = 0;
4384 if (fh == NULL) {
4385 return -1;
4388 while (1) {
4389 int retval = fread(buf, 1, sizeof(buf), fh);
4390 if (retval > 0) {
4391 ret = 1;
4392 Jim_AppendString(interp, strObj, buf, retval);
4394 if (retval != sizeof(buf)) {
4395 break;
4398 fclose(fh);
4399 return ret;
4402 static char **JimBuildEnv(Jim_Interp *interp)
4404 int i;
4405 int size;
4406 int num;
4407 int n;
4408 char **envptr;
4409 char *envdata;
4411 Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
4413 if (!objPtr) {
4414 return JimOriginalEnviron();
4419 num = Jim_ListLength(interp, objPtr);
4420 if (num % 2) {
4422 num--;
4424 size = Jim_Length(objPtr) + 2;
4426 envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
4427 envdata = (char *)&envptr[num / 2 + 1];
4429 n = 0;
4430 for (i = 0; i < num; i += 2) {
4431 const char *s1, *s2;
4432 Jim_Obj *elemObj;
4434 Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
4435 s1 = Jim_String(elemObj);
4436 Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
4437 s2 = Jim_String(elemObj);
4439 envptr[n] = envdata;
4440 envdata += sprintf(envdata, "%s=%s", s1, s2);
4441 envdata++;
4442 n++;
4444 envptr[n] = NULL;
4445 *envdata = 0;
4447 return envptr;
4450 static void JimFreeEnv(char **env, char **original_environ)
4452 if (env != original_environ) {
4453 Jim_Free(env);
4457 #ifndef jim_ext_signal
4459 const char *Jim_SignalId(int sig)
4461 static char buf[10];
4462 snprintf(buf, sizeof(buf), "%d", sig);
4463 return buf;
4466 const char *Jim_SignalName(int sig)
4468 return Jim_SignalId(sig);
4470 #endif
4472 static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
4474 Jim_Obj *errorCode;
4476 if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
4477 return JIM_OK;
4479 errorCode = Jim_NewListObj(interp, NULL, 0);
4481 if (WIFEXITED(waitStatus)) {
4482 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4483 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4484 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
4486 else {
4487 const char *type;
4488 const char *action;
4490 if (WIFSIGNALED(waitStatus)) {
4491 type = "CHILDKILLED";
4492 action = "killed";
4494 else {
4495 type = "CHILDSUSP";
4496 action = "suspended";
4499 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
4501 if (errStrObj) {
4502 Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
4505 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4506 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalId(WTERMSIG(waitStatus)), -1));
4507 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1));
4509 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
4511 return JIM_ERR;
4515 struct WaitInfo
4517 pidtype pid;
4518 int status;
4519 int flags;
4522 struct WaitInfoTable {
4523 struct WaitInfo *info;
4524 int size;
4525 int used;
4529 #define WI_DETACHED 2
4531 #define WAIT_TABLE_GROW_BY 4
4533 static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
4535 struct WaitInfoTable *table = privData;
4537 Jim_Free(table->info);
4538 Jim_Free(table);
4541 static struct WaitInfoTable *JimAllocWaitInfoTable(void)
4543 struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
4544 table->info = NULL;
4545 table->size = table->used = 0;
4547 return table;
4550 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4552 fdtype outputId;
4553 fdtype errorId;
4554 pidtype *pidPtr;
4555 int numPids, result;
4556 int child_siginfo = 1;
4557 Jim_Obj *childErrObj;
4558 Jim_Obj *errStrObj;
4560 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
4561 Jim_Obj *listObj;
4562 int i;
4564 argc--;
4565 numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
4566 if (numPids < 0) {
4567 return JIM_ERR;
4570 listObj = Jim_NewListObj(interp, NULL, 0);
4571 for (i = 0; i < numPids; i++) {
4572 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i]));
4574 Jim_SetResult(interp, listObj);
4575 JimDetachPids(interp, numPids, pidPtr);
4576 Jim_Free(pidPtr);
4577 return JIM_OK;
4580 numPids =
4581 JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
4583 if (numPids < 0) {
4584 return JIM_ERR;
4587 result = JIM_OK;
4589 errStrObj = Jim_NewStringObj(interp, "", 0);
4592 if (outputId != JIM_BAD_FD) {
4593 if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) {
4594 result = JIM_ERR;
4595 Jim_SetResultErrno(interp, "error reading from output pipe");
4600 childErrObj = Jim_NewStringObj(interp, "", 0);
4601 Jim_IncrRefCount(childErrObj);
4603 if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
4604 result = JIM_ERR;
4607 if (errorId != JIM_BAD_FD) {
4608 int ret;
4609 JimRewindFd(errorId);
4610 ret = JimAppendStreamToString(interp, errorId, errStrObj);
4611 if (ret < 0) {
4612 Jim_SetResultErrno(interp, "error reading from error pipe");
4613 result = JIM_ERR;
4615 else if (ret > 0) {
4617 child_siginfo = 0;
4621 if (child_siginfo) {
4623 Jim_AppendObj(interp, errStrObj, childErrObj);
4625 Jim_DecrRefCount(interp, childErrObj);
4628 Jim_RemoveTrailingNewline(errStrObj);
4631 Jim_SetResult(interp, errStrObj);
4633 return result;
4636 static void JimReapDetachedPids(struct WaitInfoTable *table)
4638 struct WaitInfo *waitPtr;
4639 int count;
4640 int dest;
4642 if (!table) {
4643 return;
4646 waitPtr = table->info;
4647 dest = 0;
4648 for (count = table->used; count > 0; waitPtr++, count--) {
4649 if (waitPtr->flags & WI_DETACHED) {
4650 int status;
4651 pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG);
4652 if (pid == waitPtr->pid) {
4654 table->used--;
4655 continue;
4658 if (waitPtr != &table->info[dest]) {
4659 table->info[dest] = *waitPtr;
4661 dest++;
4665 static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
4667 int i;
4670 for (i = 0; i < table->used; i++) {
4671 if (pid == table->info[i].pid) {
4673 JimWaitPid(pid, statusPtr, 0);
4676 if (i != table->used - 1) {
4677 table->info[i] = table->info[table->used - 1];
4679 table->used--;
4680 return pid;
4685 return JIM_BAD_PID;
4688 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr)
4690 int j;
4691 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4693 for (j = 0; j < numPids; j++) {
4695 int i;
4696 for (i = 0; i < table->used; i++) {
4697 if (pidPtr[j] == table->info[i].pid) {
4698 table->info[i].flags |= WI_DETACHED;
4699 break;
4705 static FILE *JimGetAioFilehandle(Jim_Interp *interp, const char *name)
4707 FILE *fh;
4708 Jim_Obj *fhObj;
4710 fhObj = Jim_NewStringObj(interp, name, -1);
4711 Jim_IncrRefCount(fhObj);
4712 fh = Jim_AioFilehandle(interp, fhObj);
4713 Jim_DecrRefCount(interp, fhObj);
4715 return fh;
4718 static int
4719 JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr,
4720 fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr)
4722 pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all
4723 * the pids of child processes. */
4724 int numPids = 0; /* Actual number of processes that exist
4725 * at *pidPtr right now. */
4726 int cmdCount; /* Count of number of distinct commands
4727 * found in argc/argv. */
4728 const char *input = NULL; /* Describes input for pipeline, depending
4729 * on "inputFile". NULL means take input
4730 * from stdin/pipe. */
4731 int input_len = 0;
4733 #define FILE_NAME 0
4734 #define FILE_APPEND 1
4735 #define FILE_HANDLE 2
4736 #define FILE_TEXT 3
4738 int inputFile = FILE_NAME; /* 1 means input is name of input file.
4739 * 2 means input is filehandle name.
4740 * 0 means input holds actual
4741 * text to be input to command. */
4743 int outputFile = FILE_NAME; /* 0 means output is the name of output file.
4744 * 1 means output is the name of output file, and append.
4745 * 2 means output is filehandle name.
4746 * All this is ignored if output is NULL
4748 int errorFile = FILE_NAME; /* 0 means error is the name of error file.
4749 * 1 means error is the name of error file, and append.
4750 * 2 means error is filehandle name.
4751 * All this is ignored if error is NULL
4753 const char *output = NULL; /* Holds name of output file to pipe to,
4754 * or NULL if output goes to stdout/pipe. */
4755 const char *error = NULL; /* Holds name of stderr file to pipe to,
4756 * or NULL if stderr goes to stderr/pipe. */
4757 fdtype inputId = JIM_BAD_FD;
4758 fdtype outputId = JIM_BAD_FD;
4759 fdtype errorId = JIM_BAD_FD;
4760 fdtype lastOutputId = JIM_BAD_FD;
4761 fdtype pipeIds[2];
4762 int firstArg, lastArg; /* Indexes of first and last arguments in
4763 * current command. */
4764 int lastBar;
4765 int i;
4766 pidtype pid;
4767 char **save_environ;
4768 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4771 char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
4772 int arg_count = 0;
4774 JimReapDetachedPids(table);
4776 if (inPipePtr != NULL) {
4777 *inPipePtr = JIM_BAD_FD;
4779 if (outPipePtr != NULL) {
4780 *outPipePtr = JIM_BAD_FD;
4782 if (errFilePtr != NULL) {
4783 *errFilePtr = JIM_BAD_FD;
4785 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4787 cmdCount = 1;
4788 lastBar = -1;
4789 for (i = 0; i < argc; i++) {
4790 const char *arg = Jim_String(argv[i]);
4792 if (arg[0] == '<') {
4793 inputFile = FILE_NAME;
4794 input = arg + 1;
4795 if (*input == '<') {
4796 inputFile = FILE_TEXT;
4797 input_len = Jim_Length(argv[i]) - 2;
4798 input++;
4800 else if (*input == '@') {
4801 inputFile = FILE_HANDLE;
4802 input++;
4805 if (!*input && ++i < argc) {
4806 input = Jim_GetString(argv[i], &input_len);
4809 else if (arg[0] == '>') {
4810 int dup_error = 0;
4812 outputFile = FILE_NAME;
4814 output = arg + 1;
4815 if (*output == '>') {
4816 outputFile = FILE_APPEND;
4817 output++;
4819 if (*output == '&') {
4821 output++;
4822 dup_error = 1;
4824 if (*output == '@') {
4825 outputFile = FILE_HANDLE;
4826 output++;
4828 if (!*output && ++i < argc) {
4829 output = Jim_String(argv[i]);
4831 if (dup_error) {
4832 errorFile = outputFile;
4833 error = output;
4836 else if (arg[0] == '2' && arg[1] == '>') {
4837 error = arg + 2;
4838 errorFile = FILE_NAME;
4840 if (*error == '@') {
4841 errorFile = FILE_HANDLE;
4842 error++;
4844 else if (*error == '>') {
4845 errorFile = FILE_APPEND;
4846 error++;
4848 if (!*error && ++i < argc) {
4849 error = Jim_String(argv[i]);
4852 else {
4853 if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
4854 if (i == lastBar + 1 || i == argc - 1) {
4855 Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
4856 goto badargs;
4858 lastBar = i;
4859 cmdCount++;
4862 arg_array[arg_count++] = (char *)arg;
4863 continue;
4866 if (i >= argc) {
4867 Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
4868 goto badargs;
4872 if (arg_count == 0) {
4873 Jim_SetResultString(interp, "didn't specify command to execute", -1);
4874 badargs:
4875 Jim_Free(arg_array);
4876 return -1;
4880 save_environ = JimSaveEnv(JimBuildEnv(interp));
4882 if (input != NULL) {
4883 if (inputFile == FILE_TEXT) {
4884 inputId = JimCreateTemp(interp, input, input_len);
4885 if (inputId == JIM_BAD_FD) {
4886 goto error;
4889 else if (inputFile == FILE_HANDLE) {
4891 FILE *fh = JimGetAioFilehandle(interp, input);
4893 if (fh == NULL) {
4894 goto error;
4896 inputId = JimDupFd(JimFileno(fh));
4898 else {
4899 inputId = JimOpenForRead(input);
4900 if (inputId == JIM_BAD_FD) {
4901 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError());
4902 goto error;
4906 else if (inPipePtr != NULL) {
4907 if (JimPipe(pipeIds) != 0) {
4908 Jim_SetResultErrno(interp, "couldn't create input pipe for command");
4909 goto error;
4911 inputId = pipeIds[0];
4912 *inPipePtr = pipeIds[1];
4913 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4916 if (output != NULL) {
4917 if (outputFile == FILE_HANDLE) {
4918 FILE *fh = JimGetAioFilehandle(interp, output);
4919 if (fh == NULL) {
4920 goto error;
4922 fflush(fh);
4923 lastOutputId = JimDupFd(JimFileno(fh));
4925 else {
4926 lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND);
4927 if (lastOutputId == JIM_BAD_FD) {
4928 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError());
4929 goto error;
4933 else if (outPipePtr != NULL) {
4934 if (JimPipe(pipeIds) != 0) {
4935 Jim_SetResultErrno(interp, "couldn't create output pipe");
4936 goto error;
4938 lastOutputId = pipeIds[1];
4939 *outPipePtr = pipeIds[0];
4940 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4943 if (error != NULL) {
4944 if (errorFile == FILE_HANDLE) {
4945 if (strcmp(error, "1") == 0) {
4947 if (lastOutputId != JIM_BAD_FD) {
4948 errorId = JimDupFd(lastOutputId);
4950 else {
4952 error = "stdout";
4955 if (errorId == JIM_BAD_FD) {
4956 FILE *fh = JimGetAioFilehandle(interp, error);
4957 if (fh == NULL) {
4958 goto error;
4960 fflush(fh);
4961 errorId = JimDupFd(JimFileno(fh));
4964 else {
4965 errorId = JimOpenForWrite(error, errorFile == FILE_APPEND);
4966 if (errorId == JIM_BAD_FD) {
4967 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError());
4968 goto error;
4972 else if (errFilePtr != NULL) {
4973 errorId = JimCreateTemp(interp, NULL, 0);
4974 if (errorId == JIM_BAD_FD) {
4975 goto error;
4977 *errFilePtr = JimDupFd(errorId);
4981 pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
4982 for (i = 0; i < numPids; i++) {
4983 pidPtr[i] = JIM_BAD_PID;
4985 for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
4986 int pipe_dup_err = 0;
4987 fdtype origErrorId = errorId;
4989 for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
4990 if (arg_array[lastArg][0] == '|') {
4991 if (arg_array[lastArg][1] == '&') {
4992 pipe_dup_err = 1;
4994 break;
4998 arg_array[lastArg] = NULL;
4999 if (lastArg == arg_count) {
5000 outputId = lastOutputId;
5002 else {
5003 if (JimPipe(pipeIds) != 0) {
5004 Jim_SetResultErrno(interp, "couldn't create pipe");
5005 goto error;
5007 outputId = pipeIds[1];
5011 if (pipe_dup_err) {
5012 errorId = outputId;
5017 #ifdef __MINGW32__
5018 pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId);
5019 if (pid == JIM_BAD_PID) {
5020 Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
5021 goto error;
5023 #else
5024 pid = vfork();
5025 if (pid < 0) {
5026 Jim_SetResultErrno(interp, "couldn't fork child process");
5027 goto error;
5029 if (pid == 0) {
5032 if (inputId != -1) dup2(inputId, 0);
5033 if (outputId != -1) dup2(outputId, 1);
5034 if (errorId != -1) dup2(errorId, 2);
5036 for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) {
5037 close(i);
5041 (void)signal(SIGPIPE, SIG_DFL);
5043 execvpe(arg_array[firstArg], &arg_array[firstArg], Jim_GetEnviron());
5046 fprintf(stderr, "couldn't exec \"%s\"\n", arg_array[firstArg]);
5047 #ifdef JIM_MAINTAINER
5050 static char *const false_argv[2] = {"false", NULL};
5051 execvp(false_argv[0],false_argv);
5053 #endif
5054 _exit(127);
5056 #endif
5060 if (table->used == table->size) {
5061 table->size += WAIT_TABLE_GROW_BY;
5062 table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
5065 table->info[table->used].pid = pid;
5066 table->info[table->used].flags = 0;
5067 table->used++;
5069 pidPtr[numPids] = pid;
5072 errorId = origErrorId;
5075 if (inputId != JIM_BAD_FD) {
5076 JimCloseFd(inputId);
5078 if (outputId != JIM_BAD_FD) {
5079 JimCloseFd(outputId);
5080 outputId = JIM_BAD_FD;
5082 inputId = pipeIds[0];
5083 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
5085 *pidArrayPtr = pidPtr;
5088 cleanup:
5089 if (inputId != JIM_BAD_FD) {
5090 JimCloseFd(inputId);
5092 if (lastOutputId != JIM_BAD_FD) {
5093 JimCloseFd(lastOutputId);
5095 if (errorId != JIM_BAD_FD) {
5096 JimCloseFd(errorId);
5098 Jim_Free(arg_array);
5100 JimRestoreEnv(save_environ);
5102 return numPids;
5105 error:
5106 if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) {
5107 JimCloseFd(*inPipePtr);
5108 *inPipePtr = JIM_BAD_FD;
5110 if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) {
5111 JimCloseFd(*outPipePtr);
5112 *outPipePtr = JIM_BAD_FD;
5114 if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) {
5115 JimCloseFd(*errFilePtr);
5116 *errFilePtr = JIM_BAD_FD;
5118 if (pipeIds[0] != JIM_BAD_FD) {
5119 JimCloseFd(pipeIds[0]);
5121 if (pipeIds[1] != JIM_BAD_FD) {
5122 JimCloseFd(pipeIds[1]);
5124 if (pidPtr != NULL) {
5125 for (i = 0; i < numPids; i++) {
5126 if (pidPtr[i] != JIM_BAD_PID) {
5127 JimDetachPids(interp, 1, &pidPtr[i]);
5130 Jim_Free(pidPtr);
5132 numPids = -1;
5133 goto cleanup;
5137 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj)
5139 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
5140 int result = JIM_OK;
5141 int i;
5144 for (i = 0; i < numPids; i++) {
5145 int waitStatus = 0;
5146 if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) {
5147 if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus, errStrObj) != JIM_OK) {
5148 result = JIM_ERR;
5152 Jim_Free(pidPtr);
5154 return result;
5157 int Jim_execInit(Jim_Interp *interp)
5159 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
5160 return JIM_ERR;
5162 #ifdef SIGPIPE
5163 (void)signal(SIGPIPE, SIG_IGN);
5164 #endif
5166 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable);
5167 return JIM_OK;
5170 #if defined(__MINGW32__)
5173 static SECURITY_ATTRIBUTES *JimStdSecAttrs(void)
5175 static SECURITY_ATTRIBUTES secAtts;
5177 secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
5178 secAtts.lpSecurityDescriptor = NULL;
5179 secAtts.bInheritHandle = TRUE;
5180 return &secAtts;
5183 static int JimErrno(void)
5185 switch (GetLastError()) {
5186 case ERROR_FILE_NOT_FOUND: return ENOENT;
5187 case ERROR_PATH_NOT_FOUND: return ENOENT;
5188 case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
5189 case ERROR_ACCESS_DENIED: return EACCES;
5190 case ERROR_INVALID_HANDLE: return EBADF;
5191 case ERROR_BAD_ENVIRONMENT: return E2BIG;
5192 case ERROR_BAD_FORMAT: return ENOEXEC;
5193 case ERROR_INVALID_ACCESS: return EACCES;
5194 case ERROR_INVALID_DRIVE: return ENOENT;
5195 case ERROR_CURRENT_DIRECTORY: return EACCES;
5196 case ERROR_NOT_SAME_DEVICE: return EXDEV;
5197 case ERROR_NO_MORE_FILES: return ENOENT;
5198 case ERROR_WRITE_PROTECT: return EROFS;
5199 case ERROR_BAD_UNIT: return ENXIO;
5200 case ERROR_NOT_READY: return EBUSY;
5201 case ERROR_BAD_COMMAND: return EIO;
5202 case ERROR_CRC: return EIO;
5203 case ERROR_BAD_LENGTH: return EIO;
5204 case ERROR_SEEK: return EIO;
5205 case ERROR_WRITE_FAULT: return EIO;
5206 case ERROR_READ_FAULT: return EIO;
5207 case ERROR_GEN_FAILURE: return EIO;
5208 case ERROR_SHARING_VIOLATION: return EACCES;
5209 case ERROR_LOCK_VIOLATION: return EACCES;
5210 case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
5211 case ERROR_HANDLE_DISK_FULL: return ENOSPC;
5212 case ERROR_NOT_SUPPORTED: return ENODEV;
5213 case ERROR_REM_NOT_LIST: return EBUSY;
5214 case ERROR_DUP_NAME: return EEXIST;
5215 case ERROR_BAD_NETPATH: return ENOENT;
5216 case ERROR_NETWORK_BUSY: return EBUSY;
5217 case ERROR_DEV_NOT_EXIST: return ENODEV;
5218 case ERROR_TOO_MANY_CMDS: return EAGAIN;
5219 case ERROR_ADAP_HDW_ERR: return EIO;
5220 case ERROR_BAD_NET_RESP: return EIO;
5221 case ERROR_UNEXP_NET_ERR: return EIO;
5222 case ERROR_NETNAME_DELETED: return ENOENT;
5223 case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
5224 case ERROR_BAD_DEV_TYPE: return ENODEV;
5225 case ERROR_BAD_NET_NAME: return ENOENT;
5226 case ERROR_TOO_MANY_NAMES: return ENFILE;
5227 case ERROR_TOO_MANY_SESS: return EIO;
5228 case ERROR_SHARING_PAUSED: return EAGAIN;
5229 case ERROR_REDIR_PAUSED: return EAGAIN;
5230 case ERROR_FILE_EXISTS: return EEXIST;
5231 case ERROR_CANNOT_MAKE: return ENOSPC;
5232 case ERROR_OUT_OF_STRUCTURES: return ENFILE;
5233 case ERROR_ALREADY_ASSIGNED: return EEXIST;
5234 case ERROR_INVALID_PASSWORD: return EPERM;
5235 case ERROR_NET_WRITE_FAULT: return EIO;
5236 case ERROR_NO_PROC_SLOTS: return EAGAIN;
5237 case ERROR_DISK_CHANGE: return EXDEV;
5238 case ERROR_BROKEN_PIPE: return EPIPE;
5239 case ERROR_OPEN_FAILED: return ENOENT;
5240 case ERROR_DISK_FULL: return ENOSPC;
5241 case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
5242 case ERROR_INVALID_TARGET_HANDLE: return EBADF;
5243 case ERROR_INVALID_NAME: return ENOENT;
5244 case ERROR_PROC_NOT_FOUND: return ESRCH;
5245 case ERROR_WAIT_NO_CHILDREN: return ECHILD;
5246 case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
5247 case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
5248 case ERROR_SEEK_ON_DEVICE: return ESPIPE;
5249 case ERROR_BUSY_DRIVE: return EAGAIN;
5250 case ERROR_DIR_NOT_EMPTY: return EEXIST;
5251 case ERROR_NOT_LOCKED: return EACCES;
5252 case ERROR_BAD_PATHNAME: return ENOENT;
5253 case ERROR_LOCK_FAILED: return EACCES;
5254 case ERROR_ALREADY_EXISTS: return EEXIST;
5255 case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
5256 case ERROR_BAD_PIPE: return EPIPE;
5257 case ERROR_PIPE_BUSY: return EAGAIN;
5258 case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
5259 case ERROR_DIRECTORY: return ENOTDIR;
5261 return EINVAL;
5264 static int JimPipe(fdtype pipefd[2])
5266 if (CreatePipe(&pipefd[0], &pipefd[1], NULL, 0)) {
5267 return 0;
5269 return -1;
5272 static fdtype JimDupFd(fdtype infd)
5274 fdtype dupfd;
5275 pidtype pid = GetCurrentProcess();
5277 if (DuplicateHandle(pid, infd, pid, &dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
5278 return dupfd;
5280 return JIM_BAD_FD;
5283 static int JimRewindFd(fdtype fd)
5285 return SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ? -1 : 0;
5288 #if 0
5289 static int JimReadFd(fdtype fd, char *buffer, size_t len)
5291 DWORD num;
5293 if (ReadFile(fd, buffer, len, &num, NULL)) {
5294 return num;
5296 if (GetLastError() == ERROR_HANDLE_EOF || GetLastError() == ERROR_BROKEN_PIPE) {
5297 return 0;
5299 return -1;
5301 #endif
5303 static FILE *JimFdOpenForRead(fdtype fd)
5305 return _fdopen(_open_osfhandle((int)fd, _O_RDONLY | _O_TEXT), "r");
5308 static fdtype JimFileno(FILE *fh)
5310 return (fdtype)_get_osfhandle(_fileno(fh));
5313 static fdtype JimOpenForRead(const char *filename)
5315 return CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
5316 JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
5319 static fdtype JimOpenForWrite(const char *filename, int append)
5321 fdtype fd = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
5322 JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
5323 if (append && fd != JIM_BAD_FD) {
5324 SetFilePointer(fd, 0, NULL, FILE_END);
5326 return fd;
5329 static FILE *JimFdOpenForWrite(fdtype fd)
5331 return _fdopen(_open_osfhandle((int)fd, _O_TEXT), "w");
5334 static pidtype JimWaitPid(pidtype pid, int *status, int nohang)
5336 DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
5337 if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
5339 return JIM_BAD_PID;
5341 GetExitCodeProcess(pid, &ret);
5342 *status = ret;
5343 CloseHandle(pid);
5344 return pid;
5347 static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
5349 char name[MAX_PATH];
5350 HANDLE handle;
5352 if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) {
5353 return JIM_BAD_FD;
5356 handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, JimStdSecAttrs(),
5357 CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
5358 NULL);
5360 if (handle == INVALID_HANDLE_VALUE) {
5361 goto error;
5364 if (contents != NULL) {
5366 FILE *fh = JimFdOpenForWrite(JimDupFd(handle));
5367 if (fh == NULL) {
5368 goto error;
5371 if (fwrite(contents, len, 1, fh) != 1) {
5372 fclose(fh);
5373 goto error;
5375 fseek(fh, 0, SEEK_SET);
5376 fclose(fh);
5378 return handle;
5380 error:
5381 Jim_SetResultErrno(interp, "failed to create temp file");
5382 CloseHandle(handle);
5383 DeleteFile(name);
5384 return JIM_BAD_FD;
5387 static int
5388 JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
5390 int i;
5391 static char extensions[][5] = {".exe", "", ".bat"};
5393 for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
5394 snprintf(fullPath, MAX_PATH, "%s%s", originalName, extensions[i]);
5396 if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
5397 continue;
5399 if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
5400 continue;
5402 return 0;
5405 return -1;
5408 static char **JimSaveEnv(char **env)
5410 return env;
5413 static void JimRestoreEnv(char **env)
5415 JimFreeEnv(env, Jim_GetEnviron());
5418 static char **JimOriginalEnviron(void)
5420 return NULL;
5423 static Jim_Obj *
5424 JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
5426 char *start, *special;
5427 int quote, i;
5429 Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
5431 for (i = 0; argv[i]; i++) {
5432 if (i > 0) {
5433 Jim_AppendString(interp, strObj, " ", 1);
5436 if (argv[i][0] == '\0') {
5437 quote = 1;
5439 else {
5440 quote = 0;
5441 for (start = argv[i]; *start != '\0'; start++) {
5442 if (isspace(UCHAR(*start))) {
5443 quote = 1;
5444 break;
5448 if (quote) {
5449 Jim_AppendString(interp, strObj, "\"" , 1);
5452 start = argv[i];
5453 for (special = argv[i]; ; ) {
5454 if ((*special == '\\') && (special[1] == '\\' ||
5455 special[1] == '"' || (quote && special[1] == '\0'))) {
5456 Jim_AppendString(interp, strObj, start, special - start);
5457 start = special;
5458 while (1) {
5459 special++;
5460 if (*special == '"' || (quote && *special == '\0')) {
5462 Jim_AppendString(interp, strObj, start, special - start);
5463 break;
5465 if (*special != '\\') {
5466 break;
5469 Jim_AppendString(interp, strObj, start, special - start);
5470 start = special;
5472 if (*special == '"') {
5473 if (special == start) {
5474 Jim_AppendString(interp, strObj, "\"", 1);
5476 else {
5477 Jim_AppendString(interp, strObj, start, special - start);
5479 Jim_AppendString(interp, strObj, "\\\"", 2);
5480 start = special + 1;
5482 if (*special == '\0') {
5483 break;
5485 special++;
5487 Jim_AppendString(interp, strObj, start, special - start);
5488 if (quote) {
5489 Jim_AppendString(interp, strObj, "\"", 1);
5492 return strObj;
5495 static pidtype
5496 JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, fdtype inputId, fdtype outputId, fdtype errorId)
5498 STARTUPINFO startInfo;
5499 PROCESS_INFORMATION procInfo;
5500 HANDLE hProcess, h;
5501 char execPath[MAX_PATH];
5502 pidtype pid = JIM_BAD_PID;
5503 Jim_Obj *cmdLineObj;
5504 char *winenv;
5506 if (JimWinFindExecutable(argv[0], execPath) < 0) {
5507 return JIM_BAD_PID;
5509 argv[0] = execPath;
5511 hProcess = GetCurrentProcess();
5512 cmdLineObj = JimWinBuildCommandLine(interp, argv);
5515 ZeroMemory(&startInfo, sizeof(startInfo));
5516 startInfo.cb = sizeof(startInfo);
5517 startInfo.dwFlags = STARTF_USESTDHANDLES;
5518 startInfo.hStdInput = INVALID_HANDLE_VALUE;
5519 startInfo.hStdOutput= INVALID_HANDLE_VALUE;
5520 startInfo.hStdError = INVALID_HANDLE_VALUE;
5522 if (inputId == JIM_BAD_FD) {
5523 if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) {
5524 CloseHandle(h);
5526 } else {
5527 DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput,
5528 0, TRUE, DUPLICATE_SAME_ACCESS);
5530 if (startInfo.hStdInput == JIM_BAD_FD) {
5531 goto end;
5534 if (outputId == JIM_BAD_FD) {
5535 startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
5536 JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
5537 } else {
5538 DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput,
5539 0, TRUE, DUPLICATE_SAME_ACCESS);
5541 if (startInfo.hStdOutput == JIM_BAD_FD) {
5542 goto end;
5545 if (errorId == JIM_BAD_FD) {
5547 startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
5548 JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
5549 } else {
5550 DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError,
5551 0, TRUE, DUPLICATE_SAME_ACCESS);
5553 if (startInfo.hStdError == JIM_BAD_FD) {
5554 goto end;
5557 if (env == NULL) {
5559 winenv = NULL;
5561 else if (env[0] == NULL) {
5562 winenv = (char *)"\0";
5564 else {
5565 winenv = env[0];
5568 if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
5569 0, winenv, NULL, &startInfo, &procInfo)) {
5570 goto end;
5574 WaitForInputIdle(procInfo.hProcess, 5000);
5575 CloseHandle(procInfo.hThread);
5577 pid = procInfo.hProcess;
5579 end:
5580 Jim_FreeNewObj(interp, cmdLineObj);
5581 if (startInfo.hStdInput != JIM_BAD_FD) {
5582 CloseHandle(startInfo.hStdInput);
5584 if (startInfo.hStdOutput != JIM_BAD_FD) {
5585 CloseHandle(startInfo.hStdOutput);
5587 if (startInfo.hStdError != JIM_BAD_FD) {
5588 CloseHandle(startInfo.hStdError);
5590 return pid;
5592 #else
5594 static int JimOpenForWrite(const char *filename, int append)
5596 return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
5599 static int JimRewindFd(int fd)
5601 return lseek(fd, 0L, SEEK_SET);
5604 static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
5606 int fd = Jim_MakeTempFile(interp, NULL);
5608 if (fd != JIM_BAD_FD) {
5609 unlink(Jim_String(Jim_GetResult(interp)));
5610 if (contents) {
5611 if (write(fd, contents, len) != len) {
5612 Jim_SetResultErrno(interp, "couldn't write temp file");
5613 close(fd);
5614 return -1;
5616 lseek(fd, 0L, SEEK_SET);
5619 return fd;
5622 static char **JimOriginalEnviron(void)
5624 return Jim_GetEnviron();
5627 static char **JimSaveEnv(char **env)
5629 char **saveenv = Jim_GetEnviron();
5630 Jim_SetEnviron(env);
5631 return saveenv;
5634 static void JimRestoreEnv(char **env)
5636 JimFreeEnv(Jim_GetEnviron(), env);
5637 Jim_SetEnviron(env);
5639 #endif
5640 #endif
5643 #ifndef _XOPEN_SOURCE
5644 #define _XOPEN_SOURCE 500
5645 #endif
5647 #include <stdlib.h>
5648 #include <string.h>
5649 #include <stdio.h>
5650 #include <time.h>
5653 #ifdef HAVE_SYS_TIME_H
5654 #include <sys/time.h>
5655 #endif
5657 static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5660 char buf[100];
5661 time_t t;
5662 long seconds;
5664 const char *format = "%a %b %d %H:%M:%S %Z %Y";
5666 if (argc == 2 || (argc == 3 && !Jim_CompareStringImmediate(interp, argv[1], "-format"))) {
5667 return -1;
5670 if (argc == 3) {
5671 format = Jim_String(argv[2]);
5674 if (Jim_GetLong(interp, argv[0], &seconds) != JIM_OK) {
5675 return JIM_ERR;
5677 t = seconds;
5679 if (strftime(buf, sizeof(buf), format, localtime(&t)) == 0) {
5680 Jim_SetResultString(interp, "format string too long", -1);
5681 return JIM_ERR;
5684 Jim_SetResultString(interp, buf, -1);
5686 return JIM_OK;
5689 #ifdef HAVE_STRPTIME
5690 static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5692 char *pt;
5693 struct tm tm;
5694 time_t now = time(0);
5696 if (!Jim_CompareStringImmediate(interp, argv[1], "-format")) {
5697 return -1;
5701 localtime_r(&now, &tm);
5703 pt = strptime(Jim_String(argv[0]), Jim_String(argv[2]), &tm);
5704 if (pt == 0 || *pt != 0) {
5705 Jim_SetResultString(interp, "Failed to parse time according to format", -1);
5706 return JIM_ERR;
5710 Jim_SetResultInt(interp, mktime(&tm));
5712 return JIM_OK;
5714 #endif
5716 static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5718 Jim_SetResultInt(interp, time(NULL));
5720 return JIM_OK;
5723 static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5725 struct timeval tv;
5727 gettimeofday(&tv, NULL);
5729 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec);
5731 return JIM_OK;
5734 static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5736 struct timeval tv;
5738 gettimeofday(&tv, NULL);
5740 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000 + tv.tv_usec / 1000);
5742 return JIM_OK;
5745 static const jim_subcmd_type clock_command_table[] = {
5746 { "seconds",
5747 NULL,
5748 clock_cmd_seconds,
5753 { "clicks",
5754 NULL,
5755 clock_cmd_micros,
5760 { "microseconds",
5761 NULL,
5762 clock_cmd_micros,
5767 { "milliseconds",
5768 NULL,
5769 clock_cmd_millis,
5774 { "format",
5775 "seconds ?-format format?",
5776 clock_cmd_format,
5781 #ifdef HAVE_STRPTIME
5782 { "scan",
5783 "str -format format",
5784 clock_cmd_scan,
5789 #endif
5790 { NULL }
5793 int Jim_clockInit(Jim_Interp *interp)
5795 if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG))
5796 return JIM_ERR;
5798 Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
5799 return JIM_OK;
5802 #include <limits.h>
5803 #include <stdlib.h>
5804 #include <string.h>
5805 #include <stdio.h>
5806 #include <errno.h>
5809 static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5812 Jim_Obj *dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
5813 Jim_SetResultInt(interp, dictObj && Jim_DictSize(interp, dictObj) != -1);
5814 return JIM_OK;
5817 static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5819 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5820 Jim_Obj *patternObj;
5822 if (!objPtr) {
5823 return JIM_OK;
5826 patternObj = (argc == 1) ? NULL : argv[1];
5829 if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) {
5830 if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) {
5832 Jim_SetResult(interp, objPtr);
5833 return JIM_OK;
5837 return Jim_DictMatchTypes(interp, objPtr, patternObj, JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS | JIM_DICTMATCH_VALUES);
5840 static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5842 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5844 if (!objPtr) {
5845 return JIM_OK;
5848 return Jim_DictMatchTypes(interp, objPtr, argc == 1 ? NULL : argv[1], JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS);
5851 static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5853 int i;
5854 int len;
5855 Jim_Obj *resultObj;
5856 Jim_Obj *objPtr;
5857 Jim_Obj **dictValuesObj;
5859 if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
5861 Jim_UnsetVariable(interp, argv[0], JIM_NONE);
5862 return JIM_OK;
5865 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5867 if (objPtr == NULL) {
5869 return JIM_OK;
5872 if (Jim_DictPairs(interp, objPtr, &dictValuesObj, &len) != JIM_OK) {
5874 Jim_SetResultString(interp, "", -1);
5875 return JIM_OK;
5879 resultObj = Jim_NewDictObj(interp, NULL, 0);
5881 for (i = 0; i < len; i += 2) {
5882 if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
5883 Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
5886 Jim_Free(dictValuesObj);
5888 Jim_SetVariable(interp, argv[0], resultObj);
5889 return JIM_OK;
5892 static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5894 Jim_Obj *objPtr;
5895 int len = 0;
5898 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5899 if (objPtr) {
5900 len = Jim_DictSize(interp, objPtr);
5901 if (len < 0) {
5903 Jim_SetResultInt(interp, 0);
5904 return JIM_OK;
5908 Jim_SetResultInt(interp, len);
5910 return JIM_OK;
5913 static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5915 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5916 if (objPtr) {
5917 return Jim_DictInfo(interp, objPtr);
5919 Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL);
5920 return JIM_ERR;
5923 static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5925 int i;
5926 int len;
5927 Jim_Obj *listObj = argv[1];
5928 Jim_Obj *dictObj;
5930 len = Jim_ListLength(interp, listObj);
5931 if (len % 2) {
5932 Jim_SetResultString(interp, "list must have an even number of elements", -1);
5933 return JIM_ERR;
5936 dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
5937 if (!dictObj) {
5939 return Jim_SetVariable(interp, argv[0], listObj);
5941 else if (Jim_DictSize(interp, dictObj) < 0) {
5942 return JIM_ERR;
5945 if (Jim_IsShared(dictObj)) {
5946 dictObj = Jim_DuplicateObj(interp, dictObj);
5949 for (i = 0; i < len; i += 2) {
5950 Jim_Obj *nameObj;
5951 Jim_Obj *valueObj;
5953 Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE);
5954 Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE);
5956 Jim_DictAddElement(interp, dictObj, nameObj, valueObj);
5958 return Jim_SetVariable(interp, argv[0], dictObj);
5961 static const jim_subcmd_type array_command_table[] = {
5962 { "exists",
5963 "arrayName",
5964 array_cmd_exists,
5969 { "get",
5970 "arrayName ?pattern?",
5971 array_cmd_get,
5976 { "names",
5977 "arrayName ?pattern?",
5978 array_cmd_names,
5983 { "set",
5984 "arrayName list",
5985 array_cmd_set,
5990 { "size",
5991 "arrayName",
5992 array_cmd_size,
5997 { "stat",
5998 "arrayName",
5999 array_cmd_stat,
6004 { "unset",
6005 "arrayName ?pattern?",
6006 array_cmd_unset,
6011 { NULL
6015 int Jim_arrayInit(Jim_Interp *interp)
6017 if (Jim_PackageProvide(interp, "array", "1.0", JIM_ERRMSG))
6018 return JIM_ERR;
6020 Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
6021 return JIM_OK;
6023 int Jim_InitStaticExtensions(Jim_Interp *interp)
6025 extern int Jim_bootstrapInit(Jim_Interp *);
6026 extern int Jim_aioInit(Jim_Interp *);
6027 extern int Jim_readdirInit(Jim_Interp *);
6028 extern int Jim_regexpInit(Jim_Interp *);
6029 extern int Jim_fileInit(Jim_Interp *);
6030 extern int Jim_globInit(Jim_Interp *);
6031 extern int Jim_execInit(Jim_Interp *);
6032 extern int Jim_clockInit(Jim_Interp *);
6033 extern int Jim_arrayInit(Jim_Interp *);
6034 extern int Jim_stdlibInit(Jim_Interp *);
6035 extern int Jim_tclcompatInit(Jim_Interp *);
6036 Jim_bootstrapInit(interp);
6037 Jim_aioInit(interp);
6038 Jim_readdirInit(interp);
6039 Jim_regexpInit(interp);
6040 Jim_fileInit(interp);
6041 Jim_globInit(interp);
6042 Jim_execInit(interp);
6043 Jim_clockInit(interp);
6044 Jim_arrayInit(interp);
6045 Jim_stdlibInit(interp);
6046 Jim_tclcompatInit(interp);
6047 return JIM_OK;
6049 #define JIM_OPTIMIZATION
6050 #ifndef _GNU_SOURCE
6051 #define _GNU_SOURCE
6052 #endif
6054 #include <stdio.h>
6055 #include <stdlib.h>
6057 #include <string.h>
6058 #include <stdarg.h>
6059 #include <ctype.h>
6060 #include <limits.h>
6061 #include <assert.h>
6062 #include <errno.h>
6063 #include <time.h>
6064 #include <setjmp.h>
6067 #ifdef HAVE_SYS_TIME_H
6068 #include <sys/time.h>
6069 #endif
6070 #ifdef HAVE_BACKTRACE
6071 #include <execinfo.h>
6072 #endif
6073 #ifdef HAVE_CRT_EXTERNS_H
6074 #include <crt_externs.h>
6075 #endif
6078 #include <math.h>
6084 #ifndef TCL_LIBRARY
6085 #define TCL_LIBRARY "."
6086 #endif
6087 #ifndef TCL_PLATFORM_OS
6088 #define TCL_PLATFORM_OS "unknown"
6089 #endif
6090 #ifndef TCL_PLATFORM_PLATFORM
6091 #define TCL_PLATFORM_PLATFORM "unknown"
6092 #endif
6093 #ifndef TCL_PLATFORM_PATH_SEPARATOR
6094 #define TCL_PLATFORM_PATH_SEPARATOR ":"
6095 #endif
6103 #ifdef JIM_MAINTAINER
6104 #define JIM_DEBUG_COMMAND
6105 #define JIM_DEBUG_PANIC
6106 #endif
6110 #define JIM_INTEGER_SPACE 24
6112 const char *jim_tt_name(int type);
6114 #ifdef JIM_DEBUG_PANIC
6115 static void JimPanicDump(int fail_condition, const char *fmt, ...);
6116 #define JimPanic(X) JimPanicDump X
6117 #else
6118 #define JimPanic(X)
6119 #endif
6121 #ifdef JIM_OPTIMIZATION
6122 #define JIM_IF_OPTIM(X) X
6123 #else
6124 #define JIM_IF_OPTIM(X)
6125 #endif
6128 static char JimEmptyStringRep[] = "";
6130 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
6131 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
6132 int flags);
6133 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
6134 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
6135 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
6136 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len);
6137 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
6138 const char *prefix, const char *const *tablePtr, const char *name);
6139 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
6140 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
6141 static int JimSign(jim_wide w);
6142 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr);
6143 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
6144 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
6148 #define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
6150 #define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none")
6152 static int utf8_tounicode_case(const char *s, int *uc, int upper)
6154 int l = utf8_tounicode(s, uc);
6155 if (upper) {
6156 *uc = utf8_upper(*uc);
6158 return l;
6162 #define JIM_CHARSET_SCAN 2
6163 #define JIM_CHARSET_GLOB 0
6165 static const char *JimCharsetMatch(const char *pattern, int c, int flags)
6167 int not = 0;
6168 int pchar;
6169 int match = 0;
6170 int nocase = 0;
6172 if (flags & JIM_NOCASE) {
6173 nocase++;
6174 c = utf8_upper(c);
6177 if (flags & JIM_CHARSET_SCAN) {
6178 if (*pattern == '^') {
6179 not++;
6180 pattern++;
6184 if (*pattern == ']') {
6185 goto first;
6189 while (*pattern && *pattern != ']') {
6191 if (pattern[0] == '\\') {
6192 first:
6193 pattern += utf8_tounicode_case(pattern, &pchar, nocase);
6195 else {
6197 int start;
6198 int end;
6200 pattern += utf8_tounicode_case(pattern, &start, nocase);
6201 if (pattern[0] == '-' && pattern[1]) {
6203 pattern += utf8_tounicode(pattern, &pchar);
6204 pattern += utf8_tounicode_case(pattern, &end, nocase);
6207 if ((c >= start && c <= end) || (c >= end && c <= start)) {
6208 match = 1;
6210 continue;
6212 pchar = start;
6215 if (pchar == c) {
6216 match = 1;
6219 if (not) {
6220 match = !match;
6223 return match ? pattern : NULL;
6228 static int JimGlobMatch(const char *pattern, const char *string, int nocase)
6230 int c;
6231 int pchar;
6232 while (*pattern) {
6233 switch (pattern[0]) {
6234 case '*':
6235 while (pattern[1] == '*') {
6236 pattern++;
6238 pattern++;
6239 if (!pattern[0]) {
6240 return 1;
6242 while (*string) {
6244 if (JimGlobMatch(pattern, string, nocase))
6245 return 1;
6246 string += utf8_tounicode(string, &c);
6248 return 0;
6250 case '?':
6251 string += utf8_tounicode(string, &c);
6252 break;
6254 case '[': {
6255 string += utf8_tounicode(string, &c);
6256 pattern = JimCharsetMatch(pattern + 1, c, nocase ? JIM_NOCASE : 0);
6257 if (!pattern) {
6258 return 0;
6260 if (!*pattern) {
6262 continue;
6264 break;
6266 case '\\':
6267 if (pattern[1]) {
6268 pattern++;
6271 default:
6272 string += utf8_tounicode_case(string, &c, nocase);
6273 utf8_tounicode_case(pattern, &pchar, nocase);
6274 if (pchar != c) {
6275 return 0;
6277 break;
6279 pattern += utf8_tounicode_case(pattern, &pchar, nocase);
6280 if (!*string) {
6281 while (*pattern == '*') {
6282 pattern++;
6284 break;
6287 if (!*pattern && !*string) {
6288 return 1;
6290 return 0;
6293 static int JimStringCompare(const char *s1, int l1, const char *s2, int l2)
6295 if (l1 < l2) {
6296 return memcmp(s1, s2, l1) <= 0 ? -1 : 1;
6298 else if (l2 < l1) {
6299 return memcmp(s1, s2, l2) >= 0 ? 1 : -1;
6301 else {
6302 return JimSign(memcmp(s1, s2, l1));
6306 static int JimStringCompareLen(const char *s1, const char *s2, int maxchars, int nocase)
6308 while (*s1 && *s2 && maxchars) {
6309 int c1, c2;
6310 s1 += utf8_tounicode_case(s1, &c1, nocase);
6311 s2 += utf8_tounicode_case(s2, &c2, nocase);
6312 if (c1 != c2) {
6313 return JimSign(c1 - c2);
6315 maxchars--;
6317 if (!maxchars) {
6318 return 0;
6321 if (*s1) {
6322 return 1;
6324 if (*s2) {
6325 return -1;
6327 return 0;
6330 static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
6332 int i;
6333 int l1bytelen;
6335 if (!l1 || !l2 || l1 > l2) {
6336 return -1;
6338 if (idx < 0)
6339 idx = 0;
6340 s2 += utf8_index(s2, idx);
6342 l1bytelen = utf8_index(s1, l1);
6344 for (i = idx; i <= l2 - l1; i++) {
6345 int c;
6346 if (memcmp(s2, s1, l1bytelen) == 0) {
6347 return i;
6349 s2 += utf8_tounicode(s2, &c);
6351 return -1;
6354 static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
6356 const char *p;
6358 if (!l1 || !l2 || l1 > l2)
6359 return -1;
6362 for (p = s2 + l2 - 1; p != s2 - 1; p--) {
6363 if (*p == *s1 && memcmp(s1, p, l1) == 0) {
6364 return p - s2;
6367 return -1;
6370 #ifdef JIM_UTF8
6371 static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2)
6373 int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2));
6374 if (n > 0) {
6375 n = utf8_strlen(s2, n);
6377 return n;
6379 #endif
6381 static int JimCheckConversion(const char *str, const char *endptr)
6383 if (str[0] == '\0' || str == endptr) {
6384 return JIM_ERR;
6387 if (endptr[0] != '\0') {
6388 while (*endptr) {
6389 if (!isspace(UCHAR(*endptr))) {
6390 return JIM_ERR;
6392 endptr++;
6395 return JIM_OK;
6398 static int JimNumberBase(const char *str, int *base, int *sign)
6400 int i = 0;
6402 *base = 10;
6404 while (isspace(UCHAR(str[i]))) {
6405 i++;
6408 if (str[i] == '-') {
6409 *sign = -1;
6410 i++;
6412 else {
6413 if (str[i] == '+') {
6414 i++;
6416 *sign = 1;
6419 if (str[i] != '0') {
6421 return 0;
6425 switch (str[i + 1]) {
6426 case 'x': case 'X': *base = 16; break;
6427 case 'o': case 'O': *base = 8; break;
6428 case 'b': case 'B': *base = 2; break;
6429 default: return 0;
6431 i += 2;
6433 if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
6435 return i;
6438 *base = 10;
6439 return 0;
6442 static long jim_strtol(const char *str, char **endptr)
6444 int sign;
6445 int base;
6446 int i = JimNumberBase(str, &base, &sign);
6448 if (base != 10) {
6449 long value = strtol(str + i, endptr, base);
6450 if (endptr == NULL || *endptr != str + i) {
6451 return value * sign;
6456 return strtol(str, endptr, 10);
6460 static jim_wide jim_strtoull(const char *str, char **endptr)
6462 #ifdef HAVE_LONG_LONG
6463 int sign;
6464 int base;
6465 int i = JimNumberBase(str, &base, &sign);
6467 if (base != 10) {
6468 jim_wide value = strtoull(str + i, endptr, base);
6469 if (endptr == NULL || *endptr != str + i) {
6470 return value * sign;
6475 return strtoull(str, endptr, 10);
6476 #else
6477 return (unsigned long)jim_strtol(str, endptr);
6478 #endif
6481 int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
6483 char *endptr;
6485 if (base) {
6486 *widePtr = strtoull(str, &endptr, base);
6488 else {
6489 *widePtr = jim_strtoull(str, &endptr);
6492 return JimCheckConversion(str, endptr);
6495 int Jim_StringToDouble(const char *str, double *doublePtr)
6497 char *endptr;
6500 errno = 0;
6502 *doublePtr = strtod(str, &endptr);
6504 return JimCheckConversion(str, endptr);
6507 static jim_wide JimPowWide(jim_wide b, jim_wide e)
6509 jim_wide res = 1;
6512 if (b == 1) {
6514 return 1;
6516 if (e < 0) {
6517 if (b != -1) {
6518 return 0;
6520 e = -e;
6522 while (e)
6524 if (e & 1) {
6525 res *= b;
6527 e >>= 1;
6528 b *= b;
6530 return res;
6533 #ifdef JIM_DEBUG_PANIC
6534 static void JimPanicDump(int condition, const char *fmt, ...)
6536 va_list ap;
6538 if (!condition) {
6539 return;
6542 va_start(ap, fmt);
6544 fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
6545 vfprintf(stderr, fmt, ap);
6546 fprintf(stderr, "\n\n");
6547 va_end(ap);
6549 #ifdef HAVE_BACKTRACE
6551 void *array[40];
6552 int size, i;
6553 char **strings;
6555 size = backtrace(array, 40);
6556 strings = backtrace_symbols(array, size);
6557 for (i = 0; i < size; i++)
6558 fprintf(stderr, "[backtrace] %s\n", strings[i]);
6559 fprintf(stderr, "[backtrace] Include the above lines and the output\n");
6560 fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n");
6562 #endif
6564 exit(1);
6566 #endif
6569 void *Jim_Alloc(int size)
6571 return size ? malloc(size) : NULL;
6574 void Jim_Free(void *ptr)
6576 free(ptr);
6579 void *Jim_Realloc(void *ptr, int size)
6581 return realloc(ptr, size);
6584 char *Jim_StrDup(const char *s)
6586 return strdup(s);
6589 char *Jim_StrDupLen(const char *s, int l)
6591 char *copy = Jim_Alloc(l + 1);
6593 memcpy(copy, s, l + 1);
6594 copy[l] = 0;
6595 return copy;
6600 static jim_wide JimClock(void)
6602 struct timeval tv;
6604 gettimeofday(&tv, NULL);
6605 return (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec;
6610 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
6611 static unsigned int JimHashTableNextPower(unsigned int size);
6612 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
6617 unsigned int Jim_IntHashFunction(unsigned int key)
6619 key += ~(key << 15);
6620 key ^= (key >> 10);
6621 key += (key << 3);
6622 key ^= (key >> 6);
6623 key += ~(key << 11);
6624 key ^= (key >> 16);
6625 return key;
6628 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
6630 unsigned int h = 0;
6632 while (len--)
6633 h += (h << 3) + *buf++;
6634 return h;
6640 static void JimResetHashTable(Jim_HashTable *ht)
6642 ht->table = NULL;
6643 ht->size = 0;
6644 ht->sizemask = 0;
6645 ht->used = 0;
6646 ht->collisions = 0;
6647 #ifdef JIM_RANDOMISE_HASH
6648 ht->uniq = (rand() ^ time(NULL) ^ clock());
6649 #else
6650 ht->uniq = 0;
6651 #endif
6654 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
6656 iter->ht = ht;
6657 iter->index = -1;
6658 iter->entry = NULL;
6659 iter->nextEntry = NULL;
6663 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
6665 JimResetHashTable(ht);
6666 ht->type = type;
6667 ht->privdata = privDataPtr;
6668 return JIM_OK;
6671 void Jim_ResizeHashTable(Jim_HashTable *ht)
6673 int minimal = ht->used;
6675 if (minimal < JIM_HT_INITIAL_SIZE)
6676 minimal = JIM_HT_INITIAL_SIZE;
6677 Jim_ExpandHashTable(ht, minimal);
6681 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
6683 Jim_HashTable n;
6684 unsigned int realsize = JimHashTableNextPower(size), i;
6686 if (size <= ht->used)
6687 return;
6689 Jim_InitHashTable(&n, ht->type, ht->privdata);
6690 n.size = realsize;
6691 n.sizemask = realsize - 1;
6692 n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
6694 n.uniq = ht->uniq;
6697 memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
6699 n.used = ht->used;
6700 for (i = 0; ht->used > 0; i++) {
6701 Jim_HashEntry *he, *nextHe;
6703 if (ht->table[i] == NULL)
6704 continue;
6707 he = ht->table[i];
6708 while (he) {
6709 unsigned int h;
6711 nextHe = he->next;
6713 h = Jim_HashKey(ht, he->key) & n.sizemask;
6714 he->next = n.table[h];
6715 n.table[h] = he;
6716 ht->used--;
6718 he = nextHe;
6721 assert(ht->used == 0);
6722 Jim_Free(ht->table);
6725 *ht = n;
6729 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
6731 Jim_HashEntry *entry;
6733 entry = JimInsertHashEntry(ht, key, 0);
6734 if (entry == NULL)
6735 return JIM_ERR;
6738 Jim_SetHashKey(ht, entry, key);
6739 Jim_SetHashVal(ht, entry, val);
6740 return JIM_OK;
6744 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
6746 int existed;
6747 Jim_HashEntry *entry;
6749 entry = JimInsertHashEntry(ht, key, 1);
6750 if (entry->key) {
6751 if (ht->type->valDestructor && ht->type->valDup) {
6752 void *newval = ht->type->valDup(ht->privdata, val);
6753 ht->type->valDestructor(ht->privdata, entry->u.val);
6754 entry->u.val = newval;
6756 else {
6757 Jim_FreeEntryVal(ht, entry);
6758 Jim_SetHashVal(ht, entry, val);
6760 existed = 1;
6762 else {
6764 Jim_SetHashKey(ht, entry, key);
6765 Jim_SetHashVal(ht, entry, val);
6766 existed = 0;
6769 return existed;
6773 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
6775 unsigned int h;
6776 Jim_HashEntry *he, *prevHe;
6778 if (ht->used == 0)
6779 return JIM_ERR;
6780 h = Jim_HashKey(ht, key) & ht->sizemask;
6781 he = ht->table[h];
6783 prevHe = NULL;
6784 while (he) {
6785 if (Jim_CompareHashKeys(ht, key, he->key)) {
6787 if (prevHe)
6788 prevHe->next = he->next;
6789 else
6790 ht->table[h] = he->next;
6791 Jim_FreeEntryKey(ht, he);
6792 Jim_FreeEntryVal(ht, he);
6793 Jim_Free(he);
6794 ht->used--;
6795 return JIM_OK;
6797 prevHe = he;
6798 he = he->next;
6800 return JIM_ERR;
6804 int Jim_FreeHashTable(Jim_HashTable *ht)
6806 unsigned int i;
6809 for (i = 0; ht->used > 0; i++) {
6810 Jim_HashEntry *he, *nextHe;
6812 if ((he = ht->table[i]) == NULL)
6813 continue;
6814 while (he) {
6815 nextHe = he->next;
6816 Jim_FreeEntryKey(ht, he);
6817 Jim_FreeEntryVal(ht, he);
6818 Jim_Free(he);
6819 ht->used--;
6820 he = nextHe;
6824 Jim_Free(ht->table);
6826 JimResetHashTable(ht);
6827 return JIM_OK;
6830 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
6832 Jim_HashEntry *he;
6833 unsigned int h;
6835 if (ht->used == 0)
6836 return NULL;
6837 h = Jim_HashKey(ht, key) & ht->sizemask;
6838 he = ht->table[h];
6839 while (he) {
6840 if (Jim_CompareHashKeys(ht, key, he->key))
6841 return he;
6842 he = he->next;
6844 return NULL;
6847 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
6849 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
6850 JimInitHashTableIterator(ht, iter);
6851 return iter;
6854 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
6856 while (1) {
6857 if (iter->entry == NULL) {
6858 iter->index++;
6859 if (iter->index >= (signed)iter->ht->size)
6860 break;
6861 iter->entry = iter->ht->table[iter->index];
6863 else {
6864 iter->entry = iter->nextEntry;
6866 if (iter->entry) {
6867 iter->nextEntry = iter->entry->next;
6868 return iter->entry;
6871 return NULL;
6877 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
6879 if (ht->size == 0)
6880 Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
6881 if (ht->size == ht->used)
6882 Jim_ExpandHashTable(ht, ht->size * 2);
6886 static unsigned int JimHashTableNextPower(unsigned int size)
6888 unsigned int i = JIM_HT_INITIAL_SIZE;
6890 if (size >= 2147483648U)
6891 return 2147483648U;
6892 while (1) {
6893 if (i >= size)
6894 return i;
6895 i *= 2;
6899 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
6901 unsigned int h;
6902 Jim_HashEntry *he;
6905 JimExpandHashTableIfNeeded(ht);
6908 h = Jim_HashKey(ht, key) & ht->sizemask;
6910 he = ht->table[h];
6911 while (he) {
6912 if (Jim_CompareHashKeys(ht, key, he->key))
6913 return replace ? he : NULL;
6914 he = he->next;
6918 he = Jim_Alloc(sizeof(*he));
6919 he->next = ht->table[h];
6920 ht->table[h] = he;
6921 ht->used++;
6922 he->key = NULL;
6924 return he;
6929 static unsigned int JimStringCopyHTHashFunction(const void *key)
6931 return Jim_GenHashFunction(key, strlen(key));
6934 static void *JimStringCopyHTDup(void *privdata, const void *key)
6936 return Jim_StrDup(key);
6939 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
6941 return strcmp(key1, key2) == 0;
6944 static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
6946 Jim_Free(key);
6949 static const Jim_HashTableType JimPackageHashTableType = {
6950 JimStringCopyHTHashFunction,
6951 JimStringCopyHTDup,
6952 NULL,
6953 JimStringCopyHTKeyCompare,
6954 JimStringCopyHTKeyDestructor,
6955 NULL
6958 typedef struct AssocDataValue
6960 Jim_InterpDeleteProc *delProc;
6961 void *data;
6962 } AssocDataValue;
6964 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
6966 AssocDataValue *assocPtr = (AssocDataValue *) data;
6968 if (assocPtr->delProc != NULL)
6969 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
6970 Jim_Free(data);
6973 static const Jim_HashTableType JimAssocDataHashTableType = {
6974 JimStringCopyHTHashFunction,
6975 JimStringCopyHTDup,
6976 NULL,
6977 JimStringCopyHTKeyCompare,
6978 JimStringCopyHTKeyDestructor,
6979 JimAssocDataHashTableValueDestructor
6982 void Jim_InitStack(Jim_Stack *stack)
6984 stack->len = 0;
6985 stack->maxlen = 0;
6986 stack->vector = NULL;
6989 void Jim_FreeStack(Jim_Stack *stack)
6991 Jim_Free(stack->vector);
6994 int Jim_StackLen(Jim_Stack *stack)
6996 return stack->len;
6999 void Jim_StackPush(Jim_Stack *stack, void *element)
7001 int neededLen = stack->len + 1;
7003 if (neededLen > stack->maxlen) {
7004 stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
7005 stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
7007 stack->vector[stack->len] = element;
7008 stack->len++;
7011 void *Jim_StackPop(Jim_Stack *stack)
7013 if (stack->len == 0)
7014 return NULL;
7015 stack->len--;
7016 return stack->vector[stack->len];
7019 void *Jim_StackPeek(Jim_Stack *stack)
7021 if (stack->len == 0)
7022 return NULL;
7023 return stack->vector[stack->len - 1];
7026 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
7028 int i;
7030 for (i = 0; i < stack->len; i++)
7031 freeFunc(stack->vector[i]);
7036 #define JIM_TT_NONE 0
7037 #define JIM_TT_STR 1
7038 #define JIM_TT_ESC 2
7039 #define JIM_TT_VAR 3
7040 #define JIM_TT_DICTSUGAR 4
7041 #define JIM_TT_CMD 5
7043 #define JIM_TT_SEP 6
7044 #define JIM_TT_EOL 7
7045 #define JIM_TT_EOF 8
7047 #define JIM_TT_LINE 9
7048 #define JIM_TT_WORD 10
7051 #define JIM_TT_SUBEXPR_START 11
7052 #define JIM_TT_SUBEXPR_END 12
7053 #define JIM_TT_SUBEXPR_COMMA 13
7054 #define JIM_TT_EXPR_INT 14
7055 #define JIM_TT_EXPR_DOUBLE 15
7056 #define JIM_TT_EXPR_BOOLEAN 16
7058 #define JIM_TT_EXPRSUGAR 17
7061 #define JIM_TT_EXPR_OP 20
7063 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
7065 #define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA)
7067 #define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP)
7069 struct JimParseMissing {
7070 int ch;
7071 int line;
7074 struct JimParserCtx
7076 const char *p;
7077 int len;
7078 int linenr;
7079 const char *tstart;
7080 const char *tend;
7081 int tline;
7082 int tt;
7083 int eof;
7084 int inquote;
7085 int comment;
7086 struct JimParseMissing missing;
7089 static int JimParseScript(struct JimParserCtx *pc);
7090 static int JimParseSep(struct JimParserCtx *pc);
7091 static int JimParseEol(struct JimParserCtx *pc);
7092 static int JimParseCmd(struct JimParserCtx *pc);
7093 static int JimParseQuote(struct JimParserCtx *pc);
7094 static int JimParseVar(struct JimParserCtx *pc);
7095 static int JimParseBrace(struct JimParserCtx *pc);
7096 static int JimParseStr(struct JimParserCtx *pc);
7097 static int JimParseComment(struct JimParserCtx *pc);
7098 static void JimParseSubCmd(struct JimParserCtx *pc);
7099 static int JimParseSubQuote(struct JimParserCtx *pc);
7100 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
7102 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
7104 pc->p = prg;
7105 pc->len = len;
7106 pc->tstart = NULL;
7107 pc->tend = NULL;
7108 pc->tline = 0;
7109 pc->tt = JIM_TT_NONE;
7110 pc->eof = 0;
7111 pc->inquote = 0;
7112 pc->linenr = linenr;
7113 pc->comment = 1;
7114 pc->missing.ch = ' ';
7115 pc->missing.line = linenr;
7118 static int JimParseScript(struct JimParserCtx *pc)
7120 while (1) {
7121 if (!pc->len) {
7122 pc->tstart = pc->p;
7123 pc->tend = pc->p - 1;
7124 pc->tline = pc->linenr;
7125 pc->tt = JIM_TT_EOL;
7126 pc->eof = 1;
7127 return JIM_OK;
7129 switch (*(pc->p)) {
7130 case '\\':
7131 if (*(pc->p + 1) == '\n' && !pc->inquote) {
7132 return JimParseSep(pc);
7134 pc->comment = 0;
7135 return JimParseStr(pc);
7136 case ' ':
7137 case '\t':
7138 case '\r':
7139 case '\f':
7140 if (!pc->inquote)
7141 return JimParseSep(pc);
7142 pc->comment = 0;
7143 return JimParseStr(pc);
7144 case '\n':
7145 case ';':
7146 pc->comment = 1;
7147 if (!pc->inquote)
7148 return JimParseEol(pc);
7149 return JimParseStr(pc);
7150 case '[':
7151 pc->comment = 0;
7152 return JimParseCmd(pc);
7153 case '$':
7154 pc->comment = 0;
7155 if (JimParseVar(pc) == JIM_ERR) {
7157 pc->tstart = pc->tend = pc->p++;
7158 pc->len--;
7159 pc->tt = JIM_TT_ESC;
7161 return JIM_OK;
7162 case '#':
7163 if (pc->comment) {
7164 JimParseComment(pc);
7165 continue;
7167 return JimParseStr(pc);
7168 default:
7169 pc->comment = 0;
7170 return JimParseStr(pc);
7172 return JIM_OK;
7176 static int JimParseSep(struct JimParserCtx *pc)
7178 pc->tstart = pc->p;
7179 pc->tline = pc->linenr;
7180 while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
7181 if (*pc->p == '\n') {
7182 break;
7184 if (*pc->p == '\\') {
7185 pc->p++;
7186 pc->len--;
7187 pc->linenr++;
7189 pc->p++;
7190 pc->len--;
7192 pc->tend = pc->p - 1;
7193 pc->tt = JIM_TT_SEP;
7194 return JIM_OK;
7197 static int JimParseEol(struct JimParserCtx *pc)
7199 pc->tstart = pc->p;
7200 pc->tline = pc->linenr;
7201 while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
7202 if (*pc->p == '\n')
7203 pc->linenr++;
7204 pc->p++;
7205 pc->len--;
7207 pc->tend = pc->p - 1;
7208 pc->tt = JIM_TT_EOL;
7209 return JIM_OK;
7213 static void JimParseSubBrace(struct JimParserCtx *pc)
7215 int level = 1;
7218 pc->p++;
7219 pc->len--;
7220 while (pc->len) {
7221 switch (*pc->p) {
7222 case '\\':
7223 if (pc->len > 1) {
7224 if (*++pc->p == '\n') {
7225 pc->linenr++;
7227 pc->len--;
7229 break;
7231 case '{':
7232 level++;
7233 break;
7235 case '}':
7236 if (--level == 0) {
7237 pc->tend = pc->p - 1;
7238 pc->p++;
7239 pc->len--;
7240 return;
7242 break;
7244 case '\n':
7245 pc->linenr++;
7246 break;
7248 pc->p++;
7249 pc->len--;
7251 pc->missing.ch = '{';
7252 pc->missing.line = pc->tline;
7253 pc->tend = pc->p - 1;
7256 static int JimParseSubQuote(struct JimParserCtx *pc)
7258 int tt = JIM_TT_STR;
7259 int line = pc->tline;
7262 pc->p++;
7263 pc->len--;
7264 while (pc->len) {
7265 switch (*pc->p) {
7266 case '\\':
7267 if (pc->len > 1) {
7268 if (*++pc->p == '\n') {
7269 pc->linenr++;
7271 pc->len--;
7272 tt = JIM_TT_ESC;
7274 break;
7276 case '"':
7277 pc->tend = pc->p - 1;
7278 pc->p++;
7279 pc->len--;
7280 return tt;
7282 case '[':
7283 JimParseSubCmd(pc);
7284 tt = JIM_TT_ESC;
7285 continue;
7287 case '\n':
7288 pc->linenr++;
7289 break;
7291 case '$':
7292 tt = JIM_TT_ESC;
7293 break;
7295 pc->p++;
7296 pc->len--;
7298 pc->missing.ch = '"';
7299 pc->missing.line = line;
7300 pc->tend = pc->p - 1;
7301 return tt;
7304 static void JimParseSubCmd(struct JimParserCtx *pc)
7306 int level = 1;
7307 int startofword = 1;
7308 int line = pc->tline;
7311 pc->p++;
7312 pc->len--;
7313 while (pc->len) {
7314 switch (*pc->p) {
7315 case '\\':
7316 if (pc->len > 1) {
7317 if (*++pc->p == '\n') {
7318 pc->linenr++;
7320 pc->len--;
7322 break;
7324 case '[':
7325 level++;
7326 break;
7328 case ']':
7329 if (--level == 0) {
7330 pc->tend = pc->p - 1;
7331 pc->p++;
7332 pc->len--;
7333 return;
7335 break;
7337 case '"':
7338 if (startofword) {
7339 JimParseSubQuote(pc);
7340 continue;
7342 break;
7344 case '{':
7345 JimParseSubBrace(pc);
7346 startofword = 0;
7347 continue;
7349 case '\n':
7350 pc->linenr++;
7351 break;
7353 startofword = isspace(UCHAR(*pc->p));
7354 pc->p++;
7355 pc->len--;
7357 pc->missing.ch = '[';
7358 pc->missing.line = line;
7359 pc->tend = pc->p - 1;
7362 static int JimParseBrace(struct JimParserCtx *pc)
7364 pc->tstart = pc->p + 1;
7365 pc->tline = pc->linenr;
7366 pc->tt = JIM_TT_STR;
7367 JimParseSubBrace(pc);
7368 return JIM_OK;
7371 static int JimParseCmd(struct JimParserCtx *pc)
7373 pc->tstart = pc->p + 1;
7374 pc->tline = pc->linenr;
7375 pc->tt = JIM_TT_CMD;
7376 JimParseSubCmd(pc);
7377 return JIM_OK;
7380 static int JimParseQuote(struct JimParserCtx *pc)
7382 pc->tstart = pc->p + 1;
7383 pc->tline = pc->linenr;
7384 pc->tt = JimParseSubQuote(pc);
7385 return JIM_OK;
7388 static int JimParseVar(struct JimParserCtx *pc)
7391 pc->p++;
7392 pc->len--;
7394 #ifdef EXPRSUGAR_BRACKET
7395 if (*pc->p == '[') {
7397 JimParseCmd(pc);
7398 pc->tt = JIM_TT_EXPRSUGAR;
7399 return JIM_OK;
7401 #endif
7403 pc->tstart = pc->p;
7404 pc->tt = JIM_TT_VAR;
7405 pc->tline = pc->linenr;
7407 if (*pc->p == '{') {
7408 pc->tstart = ++pc->p;
7409 pc->len--;
7411 while (pc->len && *pc->p != '}') {
7412 if (*pc->p == '\n') {
7413 pc->linenr++;
7415 pc->p++;
7416 pc->len--;
7418 pc->tend = pc->p - 1;
7419 if (pc->len) {
7420 pc->p++;
7421 pc->len--;
7424 else {
7425 while (1) {
7427 if (pc->p[0] == ':' && pc->p[1] == ':') {
7428 while (*pc->p == ':') {
7429 pc->p++;
7430 pc->len--;
7432 continue;
7434 if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
7435 pc->p++;
7436 pc->len--;
7437 continue;
7439 break;
7442 if (*pc->p == '(') {
7443 int count = 1;
7444 const char *paren = NULL;
7446 pc->tt = JIM_TT_DICTSUGAR;
7448 while (count && pc->len) {
7449 pc->p++;
7450 pc->len--;
7451 if (*pc->p == '\\' && pc->len >= 1) {
7452 pc->p++;
7453 pc->len--;
7455 else if (*pc->p == '(') {
7456 count++;
7458 else if (*pc->p == ')') {
7459 paren = pc->p;
7460 count--;
7463 if (count == 0) {
7464 pc->p++;
7465 pc->len--;
7467 else if (paren) {
7469 paren++;
7470 pc->len += (pc->p - paren);
7471 pc->p = paren;
7473 #ifndef EXPRSUGAR_BRACKET
7474 if (*pc->tstart == '(') {
7475 pc->tt = JIM_TT_EXPRSUGAR;
7477 #endif
7479 pc->tend = pc->p - 1;
7481 if (pc->tstart == pc->p) {
7482 pc->p--;
7483 pc->len++;
7484 return JIM_ERR;
7486 return JIM_OK;
7489 static int JimParseStr(struct JimParserCtx *pc)
7491 if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
7492 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
7494 if (*pc->p == '{') {
7495 return JimParseBrace(pc);
7497 if (*pc->p == '"') {
7498 pc->inquote = 1;
7499 pc->p++;
7500 pc->len--;
7502 pc->missing.line = pc->tline;
7505 pc->tstart = pc->p;
7506 pc->tline = pc->linenr;
7507 while (1) {
7508 if (pc->len == 0) {
7509 if (pc->inquote) {
7510 pc->missing.ch = '"';
7512 pc->tend = pc->p - 1;
7513 pc->tt = JIM_TT_ESC;
7514 return JIM_OK;
7516 switch (*pc->p) {
7517 case '\\':
7518 if (!pc->inquote && *(pc->p + 1) == '\n') {
7519 pc->tend = pc->p - 1;
7520 pc->tt = JIM_TT_ESC;
7521 return JIM_OK;
7523 if (pc->len >= 2) {
7524 if (*(pc->p + 1) == '\n') {
7525 pc->linenr++;
7527 pc->p++;
7528 pc->len--;
7530 else if (pc->len == 1) {
7532 pc->missing.ch = '\\';
7534 break;
7535 case '(':
7537 if (pc->len > 1 && pc->p[1] != '$') {
7538 break;
7541 case ')':
7543 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
7544 if (pc->p == pc->tstart) {
7546 pc->p++;
7547 pc->len--;
7549 pc->tend = pc->p - 1;
7550 pc->tt = JIM_TT_ESC;
7551 return JIM_OK;
7553 break;
7555 case '$':
7556 case '[':
7557 pc->tend = pc->p - 1;
7558 pc->tt = JIM_TT_ESC;
7559 return JIM_OK;
7560 case ' ':
7561 case '\t':
7562 case '\n':
7563 case '\r':
7564 case '\f':
7565 case ';':
7566 if (!pc->inquote) {
7567 pc->tend = pc->p - 1;
7568 pc->tt = JIM_TT_ESC;
7569 return JIM_OK;
7571 else if (*pc->p == '\n') {
7572 pc->linenr++;
7574 break;
7575 case '"':
7576 if (pc->inquote) {
7577 pc->tend = pc->p - 1;
7578 pc->tt = JIM_TT_ESC;
7579 pc->p++;
7580 pc->len--;
7581 pc->inquote = 0;
7582 return JIM_OK;
7584 break;
7586 pc->p++;
7587 pc->len--;
7589 return JIM_OK;
7592 static int JimParseComment(struct JimParserCtx *pc)
7594 while (*pc->p) {
7595 if (*pc->p == '\\') {
7596 pc->p++;
7597 pc->len--;
7598 if (pc->len == 0) {
7599 pc->missing.ch = '\\';
7600 return JIM_OK;
7602 if (*pc->p == '\n') {
7603 pc->linenr++;
7606 else if (*pc->p == '\n') {
7607 pc->p++;
7608 pc->len--;
7609 pc->linenr++;
7610 break;
7612 pc->p++;
7613 pc->len--;
7615 return JIM_OK;
7619 static int xdigitval(int c)
7621 if (c >= '0' && c <= '9')
7622 return c - '0';
7623 if (c >= 'a' && c <= 'f')
7624 return c - 'a' + 10;
7625 if (c >= 'A' && c <= 'F')
7626 return c - 'A' + 10;
7627 return -1;
7630 static int odigitval(int c)
7632 if (c >= '0' && c <= '7')
7633 return c - '0';
7634 return -1;
7637 static int JimEscape(char *dest, const char *s, int slen)
7639 char *p = dest;
7640 int i, len;
7642 for (i = 0; i < slen; i++) {
7643 switch (s[i]) {
7644 case '\\':
7645 switch (s[i + 1]) {
7646 case 'a':
7647 *p++ = 0x7;
7648 i++;
7649 break;
7650 case 'b':
7651 *p++ = 0x8;
7652 i++;
7653 break;
7654 case 'f':
7655 *p++ = 0xc;
7656 i++;
7657 break;
7658 case 'n':
7659 *p++ = 0xa;
7660 i++;
7661 break;
7662 case 'r':
7663 *p++ = 0xd;
7664 i++;
7665 break;
7666 case 't':
7667 *p++ = 0x9;
7668 i++;
7669 break;
7670 case 'u':
7671 case 'U':
7672 case 'x':
7674 unsigned val = 0;
7675 int k;
7676 int maxchars = 2;
7678 i++;
7680 if (s[i] == 'U') {
7681 maxchars = 8;
7683 else if (s[i] == 'u') {
7684 if (s[i + 1] == '{') {
7685 maxchars = 6;
7686 i++;
7688 else {
7689 maxchars = 4;
7693 for (k = 0; k < maxchars; k++) {
7694 int c = xdigitval(s[i + k + 1]);
7695 if (c == -1) {
7696 break;
7698 val = (val << 4) | c;
7701 if (s[i] == '{') {
7702 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
7704 i--;
7705 k = 0;
7707 else {
7709 k++;
7712 if (k) {
7714 if (s[i] == 'x') {
7715 *p++ = val;
7717 else {
7718 p += utf8_fromunicode(p, val);
7720 i += k;
7721 break;
7724 *p++ = s[i];
7726 break;
7727 case 'v':
7728 *p++ = 0xb;
7729 i++;
7730 break;
7731 case '\0':
7732 *p++ = '\\';
7733 i++;
7734 break;
7735 case '\n':
7737 *p++ = ' ';
7738 do {
7739 i++;
7740 } while (s[i + 1] == ' ' || s[i + 1] == '\t');
7741 break;
7742 case '0':
7743 case '1':
7744 case '2':
7745 case '3':
7746 case '4':
7747 case '5':
7748 case '6':
7749 case '7':
7752 int val = 0;
7753 int c = odigitval(s[i + 1]);
7755 val = c;
7756 c = odigitval(s[i + 2]);
7757 if (c == -1) {
7758 *p++ = val;
7759 i++;
7760 break;
7762 val = (val * 8) + c;
7763 c = odigitval(s[i + 3]);
7764 if (c == -1) {
7765 *p++ = val;
7766 i += 2;
7767 break;
7769 val = (val * 8) + c;
7770 *p++ = val;
7771 i += 3;
7773 break;
7774 default:
7775 *p++ = s[i + 1];
7776 i++;
7777 break;
7779 break;
7780 default:
7781 *p++ = s[i];
7782 break;
7785 len = p - dest;
7786 *p = '\0';
7787 return len;
7790 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
7792 const char *start, *end;
7793 char *token;
7794 int len;
7796 start = pc->tstart;
7797 end = pc->tend;
7798 if (start > end) {
7799 len = 0;
7800 token = Jim_Alloc(1);
7801 token[0] = '\0';
7803 else {
7804 len = (end - start) + 1;
7805 token = Jim_Alloc(len + 1);
7806 if (pc->tt != JIM_TT_ESC) {
7808 memcpy(token, start, len);
7809 token[len] = '\0';
7811 else {
7813 len = JimEscape(token, start, len);
7817 return Jim_NewStringObjNoAlloc(interp, token, len);
7820 static int JimParseListSep(struct JimParserCtx *pc);
7821 static int JimParseListStr(struct JimParserCtx *pc);
7822 static int JimParseListQuote(struct JimParserCtx *pc);
7824 static int JimParseList(struct JimParserCtx *pc)
7826 if (isspace(UCHAR(*pc->p))) {
7827 return JimParseListSep(pc);
7829 switch (*pc->p) {
7830 case '"':
7831 return JimParseListQuote(pc);
7833 case '{':
7834 return JimParseBrace(pc);
7836 default:
7837 if (pc->len) {
7838 return JimParseListStr(pc);
7840 break;
7843 pc->tstart = pc->tend = pc->p;
7844 pc->tline = pc->linenr;
7845 pc->tt = JIM_TT_EOL;
7846 pc->eof = 1;
7847 return JIM_OK;
7850 static int JimParseListSep(struct JimParserCtx *pc)
7852 pc->tstart = pc->p;
7853 pc->tline = pc->linenr;
7854 while (isspace(UCHAR(*pc->p))) {
7855 if (*pc->p == '\n') {
7856 pc->linenr++;
7858 pc->p++;
7859 pc->len--;
7861 pc->tend = pc->p - 1;
7862 pc->tt = JIM_TT_SEP;
7863 return JIM_OK;
7866 static int JimParseListQuote(struct JimParserCtx *pc)
7868 pc->p++;
7869 pc->len--;
7871 pc->tstart = pc->p;
7872 pc->tline = pc->linenr;
7873 pc->tt = JIM_TT_STR;
7875 while (pc->len) {
7876 switch (*pc->p) {
7877 case '\\':
7878 pc->tt = JIM_TT_ESC;
7879 if (--pc->len == 0) {
7881 pc->tend = pc->p;
7882 return JIM_OK;
7884 pc->p++;
7885 break;
7886 case '\n':
7887 pc->linenr++;
7888 break;
7889 case '"':
7890 pc->tend = pc->p - 1;
7891 pc->p++;
7892 pc->len--;
7893 return JIM_OK;
7895 pc->p++;
7896 pc->len--;
7899 pc->tend = pc->p - 1;
7900 return JIM_OK;
7903 static int JimParseListStr(struct JimParserCtx *pc)
7905 pc->tstart = pc->p;
7906 pc->tline = pc->linenr;
7907 pc->tt = JIM_TT_STR;
7909 while (pc->len) {
7910 if (isspace(UCHAR(*pc->p))) {
7911 pc->tend = pc->p - 1;
7912 return JIM_OK;
7914 if (*pc->p == '\\') {
7915 if (--pc->len == 0) {
7917 pc->tend = pc->p;
7918 return JIM_OK;
7920 pc->tt = JIM_TT_ESC;
7921 pc->p++;
7923 pc->p++;
7924 pc->len--;
7926 pc->tend = pc->p - 1;
7927 return JIM_OK;
7932 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
7934 Jim_Obj *objPtr;
7937 if (interp->freeList != NULL) {
7939 objPtr = interp->freeList;
7940 interp->freeList = objPtr->nextObjPtr;
7942 else {
7944 objPtr = Jim_Alloc(sizeof(*objPtr));
7947 objPtr->refCount = 0;
7950 objPtr->prevObjPtr = NULL;
7951 objPtr->nextObjPtr = interp->liveList;
7952 if (interp->liveList)
7953 interp->liveList->prevObjPtr = objPtr;
7954 interp->liveList = objPtr;
7956 return objPtr;
7959 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
7962 JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
7963 objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
7966 Jim_FreeIntRep(interp, objPtr);
7968 if (objPtr->bytes != NULL) {
7969 if (objPtr->bytes != JimEmptyStringRep)
7970 Jim_Free(objPtr->bytes);
7973 if (objPtr->prevObjPtr)
7974 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
7975 if (objPtr->nextObjPtr)
7976 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
7977 if (interp->liveList == objPtr)
7978 interp->liveList = objPtr->nextObjPtr;
7979 #ifdef JIM_DISABLE_OBJECT_POOL
7980 Jim_Free(objPtr);
7981 #else
7983 objPtr->prevObjPtr = NULL;
7984 objPtr->nextObjPtr = interp->freeList;
7985 if (interp->freeList)
7986 interp->freeList->prevObjPtr = objPtr;
7987 interp->freeList = objPtr;
7988 objPtr->refCount = -1;
7989 #endif
7993 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
7995 if (objPtr->bytes != NULL) {
7996 if (objPtr->bytes != JimEmptyStringRep)
7997 Jim_Free(objPtr->bytes);
7999 objPtr->bytes = NULL;
8003 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
8005 Jim_Obj *dupPtr;
8007 dupPtr = Jim_NewObj(interp);
8008 if (objPtr->bytes == NULL) {
8010 dupPtr->bytes = NULL;
8012 else if (objPtr->length == 0) {
8014 dupPtr->bytes = JimEmptyStringRep;
8015 dupPtr->length = 0;
8016 dupPtr->typePtr = NULL;
8017 return dupPtr;
8019 else {
8020 dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
8021 dupPtr->length = objPtr->length;
8023 memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
8027 dupPtr->typePtr = objPtr->typePtr;
8028 if (objPtr->typePtr != NULL) {
8029 if (objPtr->typePtr->dupIntRepProc == NULL) {
8030 dupPtr->internalRep = objPtr->internalRep;
8032 else {
8034 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
8037 return dupPtr;
8040 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
8042 if (objPtr->bytes == NULL) {
8044 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8045 objPtr->typePtr->updateStringProc(objPtr);
8047 if (lenPtr)
8048 *lenPtr = objPtr->length;
8049 return objPtr->bytes;
8053 int Jim_Length(Jim_Obj *objPtr)
8055 if (objPtr->bytes == NULL) {
8057 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8058 objPtr->typePtr->updateStringProc(objPtr);
8060 return objPtr->length;
8064 const char *Jim_String(Jim_Obj *objPtr)
8066 if (objPtr->bytes == NULL) {
8068 JimPanic((objPtr->typePtr == NULL, "UpdateStringProc called against typeless value."));
8069 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8070 objPtr->typePtr->updateStringProc(objPtr);
8072 return objPtr->bytes;
8075 static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
8077 objPtr->bytes = Jim_StrDup(str);
8078 objPtr->length = strlen(str);
8081 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8082 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8084 static const Jim_ObjType dictSubstObjType = {
8085 "dict-substitution",
8086 FreeDictSubstInternalRep,
8087 DupDictSubstInternalRep,
8088 NULL,
8089 JIM_TYPE_NONE,
8092 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8094 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
8097 static const Jim_ObjType interpolatedObjType = {
8098 "interpolated",
8099 FreeInterpolatedInternalRep,
8100 NULL,
8101 NULL,
8102 JIM_TYPE_NONE,
8105 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8106 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8108 static const Jim_ObjType stringObjType = {
8109 "string",
8110 NULL,
8111 DupStringInternalRep,
8112 NULL,
8113 JIM_TYPE_REFERENCES,
8116 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8118 JIM_NOTUSED(interp);
8120 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
8121 dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
8124 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
8126 if (objPtr->typePtr != &stringObjType) {
8128 if (objPtr->bytes == NULL) {
8130 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8131 objPtr->typePtr->updateStringProc(objPtr);
8134 Jim_FreeIntRep(interp, objPtr);
8136 objPtr->typePtr = &stringObjType;
8137 objPtr->internalRep.strValue.maxLength = objPtr->length;
8139 objPtr->internalRep.strValue.charLength = -1;
8141 return JIM_OK;
8144 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
8146 #ifdef JIM_UTF8
8147 SetStringFromAny(interp, objPtr);
8149 if (objPtr->internalRep.strValue.charLength < 0) {
8150 objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
8152 return objPtr->internalRep.strValue.charLength;
8153 #else
8154 return Jim_Length(objPtr);
8155 #endif
8159 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
8161 Jim_Obj *objPtr = Jim_NewObj(interp);
8164 if (len == -1)
8165 len = strlen(s);
8167 if (len == 0) {
8168 objPtr->bytes = JimEmptyStringRep;
8170 else {
8171 objPtr->bytes = Jim_Alloc(len + 1);
8172 memcpy(objPtr->bytes, s, len);
8173 objPtr->bytes[len] = '\0';
8175 objPtr->length = len;
8178 objPtr->typePtr = NULL;
8179 return objPtr;
8183 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
8185 #ifdef JIM_UTF8
8187 int bytelen = utf8_index(s, charlen);
8189 Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
8192 objPtr->typePtr = &stringObjType;
8193 objPtr->internalRep.strValue.maxLength = bytelen;
8194 objPtr->internalRep.strValue.charLength = charlen;
8196 return objPtr;
8197 #else
8198 return Jim_NewStringObj(interp, s, charlen);
8199 #endif
8202 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
8204 Jim_Obj *objPtr = Jim_NewObj(interp);
8206 objPtr->bytes = s;
8207 objPtr->length = (len == -1) ? strlen(s) : len;
8208 objPtr->typePtr = NULL;
8209 return objPtr;
8212 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
8214 int needlen;
8216 if (len == -1)
8217 len = strlen(str);
8218 needlen = objPtr->length + len;
8219 if (objPtr->internalRep.strValue.maxLength < needlen ||
8220 objPtr->internalRep.strValue.maxLength == 0) {
8221 needlen *= 2;
8223 if (needlen < 7) {
8224 needlen = 7;
8226 if (objPtr->bytes == JimEmptyStringRep) {
8227 objPtr->bytes = Jim_Alloc(needlen + 1);
8229 else {
8230 objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
8232 objPtr->internalRep.strValue.maxLength = needlen;
8234 memcpy(objPtr->bytes + objPtr->length, str, len);
8235 objPtr->bytes[objPtr->length + len] = '\0';
8237 if (objPtr->internalRep.strValue.charLength >= 0) {
8239 objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
8241 objPtr->length += len;
8244 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
8246 JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
8247 SetStringFromAny(interp, objPtr);
8248 StringAppendString(objPtr, str, len);
8251 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
8253 int len;
8254 const char *str = Jim_GetString(appendObjPtr, &len);
8255 Jim_AppendString(interp, objPtr, str, len);
8258 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
8260 va_list ap;
8262 SetStringFromAny(interp, objPtr);
8263 va_start(ap, objPtr);
8264 while (1) {
8265 const char *s = va_arg(ap, const char *);
8267 if (s == NULL)
8268 break;
8269 Jim_AppendString(interp, objPtr, s, -1);
8271 va_end(ap);
8274 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
8276 if (aObjPtr == bObjPtr) {
8277 return 1;
8279 else {
8280 int Alen, Blen;
8281 const char *sA = Jim_GetString(aObjPtr, &Alen);
8282 const char *sB = Jim_GetString(bObjPtr, &Blen);
8284 return Alen == Blen && memcmp(sA, sB, Alen) == 0;
8288 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
8290 return JimGlobMatch(Jim_String(patternObjPtr), Jim_String(objPtr), nocase);
8293 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8295 int l1, l2;
8296 const char *s1 = Jim_GetString(firstObjPtr, &l1);
8297 const char *s2 = Jim_GetString(secondObjPtr, &l2);
8299 if (nocase) {
8301 return JimStringCompareLen(s1, s2, -1, nocase);
8303 return JimStringCompare(s1, l1, s2, l2);
8306 int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8308 const char *s1 = Jim_String(firstObjPtr);
8309 const char *s2 = Jim_String(secondObjPtr);
8311 return JimStringCompareLen(s1, s2, Jim_Utf8Length(interp, firstObjPtr), nocase);
8314 static int JimRelToAbsIndex(int len, int idx)
8316 if (idx < 0)
8317 return len + idx;
8318 return idx;
8321 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
8323 int rangeLen;
8325 if (*firstPtr > *lastPtr) {
8326 rangeLen = 0;
8328 else {
8329 rangeLen = *lastPtr - *firstPtr + 1;
8330 if (rangeLen) {
8331 if (*firstPtr < 0) {
8332 rangeLen += *firstPtr;
8333 *firstPtr = 0;
8335 if (*lastPtr >= len) {
8336 rangeLen -= (*lastPtr - (len - 1));
8337 *lastPtr = len - 1;
8341 if (rangeLen < 0)
8342 rangeLen = 0;
8344 *rangeLenPtr = rangeLen;
8347 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
8348 int len, int *first, int *last, int *range)
8350 if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
8351 return JIM_ERR;
8353 if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
8354 return JIM_ERR;
8356 *first = JimRelToAbsIndex(len, *first);
8357 *last = JimRelToAbsIndex(len, *last);
8358 JimRelToAbsRange(len, first, last, range);
8359 return JIM_OK;
8362 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
8363 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8365 int first, last;
8366 const char *str;
8367 int rangeLen;
8368 int bytelen;
8370 str = Jim_GetString(strObjPtr, &bytelen);
8372 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
8373 return NULL;
8376 if (first == 0 && rangeLen == bytelen) {
8377 return strObjPtr;
8379 return Jim_NewStringObj(interp, str + first, rangeLen);
8382 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
8383 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8385 #ifdef JIM_UTF8
8386 int first, last;
8387 const char *str;
8388 int len, rangeLen;
8389 int bytelen;
8391 str = Jim_GetString(strObjPtr, &bytelen);
8392 len = Jim_Utf8Length(interp, strObjPtr);
8394 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8395 return NULL;
8398 if (first == 0 && rangeLen == len) {
8399 return strObjPtr;
8401 if (len == bytelen) {
8403 return Jim_NewStringObj(interp, str + first, rangeLen);
8405 return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
8406 #else
8407 return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
8408 #endif
8411 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
8412 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
8414 int first, last;
8415 const char *str;
8416 int len, rangeLen;
8417 Jim_Obj *objPtr;
8419 len = Jim_Utf8Length(interp, strObjPtr);
8421 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8422 return NULL;
8425 if (last < first) {
8426 return strObjPtr;
8429 str = Jim_String(strObjPtr);
8432 objPtr = Jim_NewStringObjUtf8(interp, str, first);
8435 if (newStrObj) {
8436 Jim_AppendObj(interp, objPtr, newStrObj);
8440 Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
8442 return objPtr;
8445 static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
8447 while (*str) {
8448 int c;
8449 str += utf8_tounicode(str, &c);
8450 dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c));
8452 *dest = 0;
8455 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
8457 char *buf;
8458 int len;
8459 const char *str;
8461 SetStringFromAny(interp, strObjPtr);
8463 str = Jim_GetString(strObjPtr, &len);
8465 #ifdef JIM_UTF8
8466 len *= 2;
8467 #endif
8468 buf = Jim_Alloc(len + 1);
8469 JimStrCopyUpperLower(buf, str, 0);
8470 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8473 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
8475 char *buf;
8476 const char *str;
8477 int len;
8479 if (strObjPtr->typePtr != &stringObjType) {
8480 SetStringFromAny(interp, strObjPtr);
8483 str = Jim_GetString(strObjPtr, &len);
8485 #ifdef JIM_UTF8
8486 len *= 2;
8487 #endif
8488 buf = Jim_Alloc(len + 1);
8489 JimStrCopyUpperLower(buf, str, 1);
8490 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8493 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
8495 char *buf, *p;
8496 int len;
8497 int c;
8498 const char *str;
8500 str = Jim_GetString(strObjPtr, &len);
8501 if (len == 0) {
8502 return strObjPtr;
8504 #ifdef JIM_UTF8
8505 len *= 2;
8506 #endif
8507 buf = p = Jim_Alloc(len + 1);
8509 str += utf8_tounicode(str, &c);
8510 p += utf8_getchars(p, utf8_title(c));
8512 JimStrCopyUpperLower(p, str, 0);
8514 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8517 static const char *utf8_memchr(const char *str, int len, int c)
8519 #ifdef JIM_UTF8
8520 while (len) {
8521 int sc;
8522 int n = utf8_tounicode(str, &sc);
8523 if (sc == c) {
8524 return str;
8526 str += n;
8527 len -= n;
8529 return NULL;
8530 #else
8531 return memchr(str, c, len);
8532 #endif
8535 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
8537 while (len) {
8538 int c;
8539 int n = utf8_tounicode(str, &c);
8541 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8543 break;
8545 str += n;
8546 len -= n;
8548 return str;
8551 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
8553 str += len;
8555 while (len) {
8556 int c;
8557 int n = utf8_prev_len(str, len);
8559 len -= n;
8560 str -= n;
8562 n = utf8_tounicode(str, &c);
8564 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8565 return str + n;
8569 return NULL;
8572 static const char default_trim_chars[] = " \t\n\r";
8574 static int default_trim_chars_len = sizeof(default_trim_chars);
8576 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8578 int len;
8579 const char *str = Jim_GetString(strObjPtr, &len);
8580 const char *trimchars = default_trim_chars;
8581 int trimcharslen = default_trim_chars_len;
8582 const char *newstr;
8584 if (trimcharsObjPtr) {
8585 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8588 newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
8589 if (newstr == str) {
8590 return strObjPtr;
8593 return Jim_NewStringObj(interp, newstr, len - (newstr - str));
8596 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8598 int len;
8599 const char *trimchars = default_trim_chars;
8600 int trimcharslen = default_trim_chars_len;
8601 const char *nontrim;
8603 if (trimcharsObjPtr) {
8604 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8607 SetStringFromAny(interp, strObjPtr);
8609 len = Jim_Length(strObjPtr);
8610 nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
8612 if (nontrim == NULL) {
8614 return Jim_NewEmptyStringObj(interp);
8616 if (nontrim == strObjPtr->bytes + len) {
8618 return strObjPtr;
8621 if (Jim_IsShared(strObjPtr)) {
8622 strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
8624 else {
8626 strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
8627 strObjPtr->length = (nontrim - strObjPtr->bytes);
8630 return strObjPtr;
8633 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8636 Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
8639 strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
8642 if (objPtr != strObjPtr && objPtr->refCount == 0) {
8644 Jim_FreeNewObj(interp, objPtr);
8647 return strObjPtr;
8651 #ifdef HAVE_ISASCII
8652 #define jim_isascii isascii
8653 #else
8654 static int jim_isascii(int c)
8656 return !(c & ~0x7f);
8658 #endif
8660 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
8662 static const char * const strclassnames[] = {
8663 "integer", "alpha", "alnum", "ascii", "digit",
8664 "double", "lower", "upper", "space", "xdigit",
8665 "control", "print", "graph", "punct", "boolean",
8666 NULL
8668 enum {
8669 STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
8670 STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
8671 STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN,
8673 int strclass;
8674 int len;
8675 int i;
8676 const char *str;
8677 int (*isclassfunc)(int c) = NULL;
8679 if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
8680 return JIM_ERR;
8683 str = Jim_GetString(strObjPtr, &len);
8684 if (len == 0) {
8685 Jim_SetResultBool(interp, !strict);
8686 return JIM_OK;
8689 switch (strclass) {
8690 case STR_IS_INTEGER:
8692 jim_wide w;
8693 Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
8694 return JIM_OK;
8697 case STR_IS_DOUBLE:
8699 double d;
8700 Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
8701 return JIM_OK;
8704 case STR_IS_BOOLEAN:
8706 int b;
8707 Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK);
8708 return JIM_OK;
8711 case STR_IS_ALPHA: isclassfunc = isalpha; break;
8712 case STR_IS_ALNUM: isclassfunc = isalnum; break;
8713 case STR_IS_ASCII: isclassfunc = jim_isascii; break;
8714 case STR_IS_DIGIT: isclassfunc = isdigit; break;
8715 case STR_IS_LOWER: isclassfunc = islower; break;
8716 case STR_IS_UPPER: isclassfunc = isupper; break;
8717 case STR_IS_SPACE: isclassfunc = isspace; break;
8718 case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
8719 case STR_IS_CONTROL: isclassfunc = iscntrl; break;
8720 case STR_IS_PRINT: isclassfunc = isprint; break;
8721 case STR_IS_GRAPH: isclassfunc = isgraph; break;
8722 case STR_IS_PUNCT: isclassfunc = ispunct; break;
8723 default:
8724 return JIM_ERR;
8727 for (i = 0; i < len; i++) {
8728 if (!isclassfunc(UCHAR(str[i]))) {
8729 Jim_SetResultBool(interp, 0);
8730 return JIM_OK;
8733 Jim_SetResultBool(interp, 1);
8734 return JIM_OK;
8739 static const Jim_ObjType comparedStringObjType = {
8740 "compared-string",
8741 NULL,
8742 NULL,
8743 NULL,
8744 JIM_TYPE_REFERENCES,
8747 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
8749 if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
8750 return 1;
8752 else {
8753 const char *objStr = Jim_String(objPtr);
8755 if (strcmp(str, objStr) != 0)
8756 return 0;
8758 if (objPtr->typePtr != &comparedStringObjType) {
8759 Jim_FreeIntRep(interp, objPtr);
8760 objPtr->typePtr = &comparedStringObjType;
8762 objPtr->internalRep.ptr = (char *)str;
8763 return 1;
8767 static int qsortCompareStringPointers(const void *a, const void *b)
8769 char *const *sa = (char *const *)a;
8770 char *const *sb = (char *const *)b;
8772 return strcmp(*sa, *sb);
8777 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8778 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8780 static const Jim_ObjType sourceObjType = {
8781 "source",
8782 FreeSourceInternalRep,
8783 DupSourceInternalRep,
8784 NULL,
8785 JIM_TYPE_REFERENCES,
8788 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8790 Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
8793 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8795 dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
8796 Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
8799 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
8800 Jim_Obj *fileNameObj, int lineNumber)
8802 JimPanic((Jim_IsShared(objPtr), "JimSetSourceInfo called with shared object"));
8803 JimPanic((objPtr->typePtr != NULL, "JimSetSourceInfo called with typed object"));
8804 Jim_IncrRefCount(fileNameObj);
8805 objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
8806 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
8807 objPtr->typePtr = &sourceObjType;
8810 static const Jim_ObjType scriptLineObjType = {
8811 "scriptline",
8812 NULL,
8813 NULL,
8814 NULL,
8815 JIM_NONE,
8818 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
8820 Jim_Obj *objPtr;
8822 #ifdef DEBUG_SHOW_SCRIPT
8823 char buf[100];
8824 snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
8825 objPtr = Jim_NewStringObj(interp, buf, -1);
8826 #else
8827 objPtr = Jim_NewEmptyStringObj(interp);
8828 #endif
8829 objPtr->typePtr = &scriptLineObjType;
8830 objPtr->internalRep.scriptLineValue.argc = argc;
8831 objPtr->internalRep.scriptLineValue.line = line;
8833 return objPtr;
8836 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8837 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8839 static const Jim_ObjType scriptObjType = {
8840 "script",
8841 FreeScriptInternalRep,
8842 DupScriptInternalRep,
8843 NULL,
8844 JIM_TYPE_REFERENCES,
8847 typedef struct ScriptToken
8849 Jim_Obj *objPtr;
8850 int type;
8851 } ScriptToken;
8853 typedef struct ScriptObj
8855 ScriptToken *token;
8856 Jim_Obj *fileNameObj;
8857 int len;
8858 int substFlags;
8859 int inUse; /* Used to share a ScriptObj. Currently
8860 only used by Jim_EvalObj() as protection against
8861 shimmering of the currently evaluated object. */
8862 int firstline;
8863 int linenr;
8864 int missing;
8865 } ScriptObj;
8867 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8868 static int JimParseCheckMissing(Jim_Interp *interp, int ch);
8869 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
8871 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8873 int i;
8874 struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
8876 if (--script->inUse != 0)
8877 return;
8878 for (i = 0; i < script->len; i++) {
8879 Jim_DecrRefCount(interp, script->token[i].objPtr);
8881 Jim_Free(script->token);
8882 Jim_DecrRefCount(interp, script->fileNameObj);
8883 Jim_Free(script);
8886 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8888 JIM_NOTUSED(interp);
8889 JIM_NOTUSED(srcPtr);
8891 dupPtr->typePtr = NULL;
8894 typedef struct
8896 const char *token;
8897 int len;
8898 int type;
8899 int line;
8900 } ParseToken;
8902 typedef struct
8905 ParseToken *list;
8906 int size;
8907 int count;
8908 ParseToken static_list[20];
8909 } ParseTokenList;
8911 static void ScriptTokenListInit(ParseTokenList *tokenlist)
8913 tokenlist->list = tokenlist->static_list;
8914 tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
8915 tokenlist->count = 0;
8918 static void ScriptTokenListFree(ParseTokenList *tokenlist)
8920 if (tokenlist->list != tokenlist->static_list) {
8921 Jim_Free(tokenlist->list);
8925 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
8926 int line)
8928 ParseToken *t;
8930 if (tokenlist->count == tokenlist->size) {
8932 tokenlist->size *= 2;
8933 if (tokenlist->list != tokenlist->static_list) {
8934 tokenlist->list =
8935 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
8937 else {
8939 tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
8940 memcpy(tokenlist->list, tokenlist->static_list,
8941 tokenlist->count * sizeof(*tokenlist->list));
8944 t = &tokenlist->list[tokenlist->count++];
8945 t->token = token;
8946 t->len = len;
8947 t->type = type;
8948 t->line = line;
8951 static int JimCountWordTokens(ParseToken *t)
8953 int expand = 1;
8954 int count = 0;
8957 if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
8958 if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
8960 expand = -1;
8961 t++;
8966 while (!TOKEN_IS_SEP(t->type)) {
8967 t++;
8968 count++;
8971 return count * expand;
8974 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
8976 Jim_Obj *objPtr;
8978 if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
8980 int len = t->len;
8981 char *str = Jim_Alloc(len + 1);
8982 len = JimEscape(str, t->token, len);
8983 objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
8985 else {
8986 objPtr = Jim_NewStringObj(interp, t->token, t->len);
8988 return objPtr;
8991 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
8992 ParseTokenList *tokenlist)
8994 int i;
8995 struct ScriptToken *token;
8997 int lineargs = 0;
8999 ScriptToken *linefirst;
9000 int count;
9001 int linenr;
9003 #ifdef DEBUG_SHOW_SCRIPT_TOKENS
9004 printf("==== Tokens ====\n");
9005 for (i = 0; i < tokenlist->count; i++) {
9006 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
9007 tokenlist->list[i].len, tokenlist->list[i].token);
9009 #endif
9012 count = tokenlist->count;
9013 for (i = 0; i < tokenlist->count; i++) {
9014 if (tokenlist->list[i].type == JIM_TT_EOL) {
9015 count++;
9018 linenr = script->firstline = tokenlist->list[0].line;
9020 token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
9023 linefirst = token++;
9025 for (i = 0; i < tokenlist->count; ) {
9027 int wordtokens;
9030 while (tokenlist->list[i].type == JIM_TT_SEP) {
9031 i++;
9034 wordtokens = JimCountWordTokens(tokenlist->list + i);
9036 if (wordtokens == 0) {
9038 if (lineargs) {
9039 linefirst->type = JIM_TT_LINE;
9040 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
9041 Jim_IncrRefCount(linefirst->objPtr);
9044 lineargs = 0;
9045 linefirst = token++;
9047 i++;
9048 continue;
9050 else if (wordtokens != 1) {
9052 token->type = JIM_TT_WORD;
9053 token->objPtr = Jim_NewIntObj(interp, wordtokens);
9054 Jim_IncrRefCount(token->objPtr);
9055 token++;
9056 if (wordtokens < 0) {
9058 i++;
9059 wordtokens = -wordtokens - 1;
9060 lineargs--;
9064 if (lineargs == 0) {
9066 linenr = tokenlist->list[i].line;
9068 lineargs++;
9071 while (wordtokens--) {
9072 const ParseToken *t = &tokenlist->list[i++];
9074 token->type = t->type;
9075 token->objPtr = JimMakeScriptObj(interp, t);
9076 Jim_IncrRefCount(token->objPtr);
9078 JimSetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
9079 token++;
9083 if (lineargs == 0) {
9084 token--;
9087 script->len = token - script->token;
9089 JimPanic((script->len >= count, "allocated script array is too short"));
9091 #ifdef DEBUG_SHOW_SCRIPT
9092 printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj));
9093 for (i = 0; i < script->len; i++) {
9094 const ScriptToken *t = &script->token[i];
9095 printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
9097 #endif
9101 int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
9103 ScriptObj *script = JimGetScript(interp, scriptObj);
9104 if (stateCharPtr) {
9105 *stateCharPtr = script->missing;
9107 return (script->missing == ' ');
9110 static int JimParseCheckMissing(Jim_Interp *interp, int ch)
9112 const char *msg;
9114 switch (ch) {
9115 case '\\':
9116 case ' ':
9117 return JIM_OK;
9119 case '[':
9120 msg = "unmatched \"[\"";
9121 break;
9122 case '{':
9123 msg = "missing close-brace";
9124 break;
9125 case '"':
9126 default:
9127 msg = "missing quote";
9128 break;
9131 Jim_SetResultString(interp, msg, -1);
9132 return JIM_ERR;
9135 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
9136 ParseTokenList *tokenlist)
9138 int i;
9139 struct ScriptToken *token;
9141 token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
9143 for (i = 0; i < tokenlist->count; i++) {
9144 const ParseToken *t = &tokenlist->list[i];
9147 token->type = t->type;
9148 token->objPtr = JimMakeScriptObj(interp, t);
9149 Jim_IncrRefCount(token->objPtr);
9150 token++;
9153 script->len = i;
9156 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9158 int scriptTextLen;
9159 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
9160 struct JimParserCtx parser;
9161 struct ScriptObj *script;
9162 ParseTokenList tokenlist;
9163 int line = 1;
9166 if (objPtr->typePtr == &sourceObjType) {
9167 line = objPtr->internalRep.sourceValue.lineNumber;
9171 ScriptTokenListInit(&tokenlist);
9173 JimParserInit(&parser, scriptText, scriptTextLen, line);
9174 while (!parser.eof) {
9175 JimParseScript(&parser);
9176 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
9177 parser.tline);
9181 ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
9184 script = Jim_Alloc(sizeof(*script));
9185 memset(script, 0, sizeof(*script));
9186 script->inUse = 1;
9187 if (objPtr->typePtr == &sourceObjType) {
9188 script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
9190 else {
9191 script->fileNameObj = interp->emptyObj;
9193 Jim_IncrRefCount(script->fileNameObj);
9194 script->missing = parser.missing.ch;
9195 script->linenr = parser.missing.line;
9197 ScriptObjAddTokens(interp, script, &tokenlist);
9200 ScriptTokenListFree(&tokenlist);
9203 Jim_FreeIntRep(interp, objPtr);
9204 Jim_SetIntRepPtr(objPtr, script);
9205 objPtr->typePtr = &scriptObjType;
9208 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script);
9210 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
9212 if (objPtr == interp->emptyObj) {
9214 objPtr = interp->nullScriptObj;
9217 if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
9218 JimSetScriptFromAny(interp, objPtr);
9221 return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
9224 static int JimScriptValid(Jim_Interp *interp, ScriptObj *script)
9226 if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
9227 JimAddErrorToStack(interp, script);
9228 return 0;
9230 return 1;
9234 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
9236 cmdPtr->inUse++;
9239 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
9241 if (--cmdPtr->inUse == 0) {
9242 if (cmdPtr->isproc) {
9243 Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
9244 Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
9245 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9246 if (cmdPtr->u.proc.staticVars) {
9247 Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
9248 Jim_Free(cmdPtr->u.proc.staticVars);
9251 else {
9253 if (cmdPtr->u.native.delProc) {
9254 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
9257 if (cmdPtr->prevCmd) {
9259 JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
9261 Jim_Free(cmdPtr);
9266 static void JimVariablesHTValDestructor(void *interp, void *val)
9268 Jim_DecrRefCount(interp, ((Jim_Var *)val)->objPtr);
9269 Jim_Free(val);
9272 static const Jim_HashTableType JimVariablesHashTableType = {
9273 JimStringCopyHTHashFunction,
9274 JimStringCopyHTDup,
9275 NULL,
9276 JimStringCopyHTKeyCompare,
9277 JimStringCopyHTKeyDestructor,
9278 JimVariablesHTValDestructor
9281 static void JimCommandsHT_ValDestructor(void *interp, void *val)
9283 JimDecrCmdRefCount(interp, val);
9286 static const Jim_HashTableType JimCommandsHashTableType = {
9287 JimStringCopyHTHashFunction,
9288 JimStringCopyHTDup,
9289 NULL,
9290 JimStringCopyHTKeyCompare,
9291 JimStringCopyHTKeyDestructor,
9292 JimCommandsHT_ValDestructor
9297 #ifdef jim_ext_namespace
9298 static Jim_Obj *JimQualifyNameObj(Jim_Interp *interp, Jim_Obj *nsObj)
9300 const char *name = Jim_String(nsObj);
9301 if (name[0] == ':' && name[1] == ':') {
9303 while (*++name == ':') {
9305 nsObj = Jim_NewStringObj(interp, name, -1);
9307 else if (Jim_Length(interp->framePtr->nsObj)) {
9309 nsObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9310 Jim_AppendStrings(interp, nsObj, "::", name, NULL);
9312 return nsObj;
9315 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9317 Jim_Obj *resultObj;
9319 const char *name = Jim_String(nameObjPtr);
9320 if (name[0] == ':' && name[1] == ':') {
9321 return nameObjPtr;
9323 Jim_IncrRefCount(nameObjPtr);
9324 resultObj = Jim_NewStringObj(interp, "::", -1);
9325 Jim_AppendObj(interp, resultObj, nameObjPtr);
9326 Jim_DecrRefCount(interp, nameObjPtr);
9328 return resultObj;
9331 static const char *JimQualifyName(Jim_Interp *interp, const char *name, Jim_Obj **objPtrPtr)
9333 Jim_Obj *objPtr = interp->emptyObj;
9335 if (name[0] == ':' && name[1] == ':') {
9337 while (*++name == ':') {
9340 else if (Jim_Length(interp->framePtr->nsObj)) {
9342 objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9343 Jim_AppendStrings(interp, objPtr, "::", name, NULL);
9344 name = Jim_String(objPtr);
9346 Jim_IncrRefCount(objPtr);
9347 *objPtrPtr = objPtr;
9348 return name;
9351 #define JimFreeQualifiedName(INTERP, OBJ) Jim_DecrRefCount((INTERP), (OBJ))
9353 #else
9355 #define JimQualifyName(INTERP, NAME, DUMMY) (((NAME)[0] == ':' && (NAME)[1] == ':') ? (NAME) + 2 : (NAME))
9356 #define JimFreeQualifiedName(INTERP, DUMMY) (void)(DUMMY)
9358 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9360 return nameObjPtr;
9362 #endif
9364 static int JimCreateCommand(Jim_Interp *interp, const char *name, Jim_Cmd *cmd)
9366 Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, name);
9367 if (he) {
9369 Jim_InterpIncrProcEpoch(interp);
9372 if (he && interp->local) {
9374 cmd->prevCmd = Jim_GetHashEntryVal(he);
9375 Jim_SetHashVal(&interp->commands, he, cmd);
9377 else {
9378 if (he) {
9380 Jim_DeleteHashEntry(&interp->commands, name);
9383 Jim_AddHashEntry(&interp->commands, name, cmd);
9385 return JIM_OK;
9389 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
9390 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
9392 Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
9395 memset(cmdPtr, 0, sizeof(*cmdPtr));
9396 cmdPtr->inUse = 1;
9397 cmdPtr->u.native.delProc = delProc;
9398 cmdPtr->u.native.cmdProc = cmdProc;
9399 cmdPtr->u.native.privData = privData;
9401 JimCreateCommand(interp, cmdNameStr, cmdPtr);
9403 return JIM_OK;
9406 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
9408 int len, i;
9410 len = Jim_ListLength(interp, staticsListObjPtr);
9411 if (len == 0) {
9412 return JIM_OK;
9415 cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
9416 Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
9417 for (i = 0; i < len; i++) {
9418 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
9419 Jim_Var *varPtr;
9420 int subLen;
9422 objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
9424 subLen = Jim_ListLength(interp, objPtr);
9425 if (subLen == 1 || subLen == 2) {
9426 nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
9427 if (subLen == 1) {
9428 initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
9429 if (initObjPtr == NULL) {
9430 Jim_SetResultFormatted(interp,
9431 "variable for initialization of static \"%#s\" not found in the local context",
9432 nameObjPtr);
9433 return JIM_ERR;
9436 else {
9437 initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
9439 if (JimValidName(interp, "static variable", nameObjPtr) != JIM_OK) {
9440 return JIM_ERR;
9443 varPtr = Jim_Alloc(sizeof(*varPtr));
9444 varPtr->objPtr = initObjPtr;
9445 Jim_IncrRefCount(initObjPtr);
9446 varPtr->linkFramePtr = NULL;
9447 if (Jim_AddHashEntry(cmdPtr->u.proc.staticVars,
9448 Jim_String(nameObjPtr), varPtr) != JIM_OK) {
9449 Jim_SetResultFormatted(interp,
9450 "static variable name \"%#s\" duplicated in statics list", nameObjPtr);
9451 Jim_DecrRefCount(interp, initObjPtr);
9452 Jim_Free(varPtr);
9453 return JIM_ERR;
9456 else {
9457 Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
9458 objPtr);
9459 return JIM_ERR;
9462 return JIM_OK;
9465 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, const char *cmdname)
9467 #ifdef jim_ext_namespace
9468 if (cmdPtr->isproc) {
9470 const char *pt = strrchr(cmdname, ':');
9471 if (pt && pt != cmdname && pt[-1] == ':') {
9472 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9473 cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 1);
9474 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9476 if (Jim_FindHashEntry(&interp->commands, pt + 1)) {
9478 Jim_InterpIncrProcEpoch(interp);
9482 #endif
9485 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
9486 Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
9488 Jim_Cmd *cmdPtr;
9489 int argListLen;
9490 int i;
9492 argListLen = Jim_ListLength(interp, argListObjPtr);
9495 cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
9496 memset(cmdPtr, 0, sizeof(*cmdPtr));
9497 cmdPtr->inUse = 1;
9498 cmdPtr->isproc = 1;
9499 cmdPtr->u.proc.argListObjPtr = argListObjPtr;
9500 cmdPtr->u.proc.argListLen = argListLen;
9501 cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
9502 cmdPtr->u.proc.argsPos = -1;
9503 cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
9504 cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
9505 Jim_IncrRefCount(argListObjPtr);
9506 Jim_IncrRefCount(bodyObjPtr);
9507 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9510 if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
9511 goto err;
9516 for (i = 0; i < argListLen; i++) {
9517 Jim_Obj *argPtr;
9518 Jim_Obj *nameObjPtr;
9519 Jim_Obj *defaultObjPtr;
9520 int len;
9523 argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
9524 len = Jim_ListLength(interp, argPtr);
9525 if (len == 0) {
9526 Jim_SetResultString(interp, "argument with no name", -1);
9527 err:
9528 JimDecrCmdRefCount(interp, cmdPtr);
9529 return NULL;
9531 if (len > 2) {
9532 Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
9533 goto err;
9536 if (len == 2) {
9538 nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
9539 defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
9541 else {
9543 nameObjPtr = argPtr;
9544 defaultObjPtr = NULL;
9548 if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
9549 if (cmdPtr->u.proc.argsPos >= 0) {
9550 Jim_SetResultString(interp, "'args' specified more than once", -1);
9551 goto err;
9553 cmdPtr->u.proc.argsPos = i;
9555 else {
9556 if (len == 2) {
9557 cmdPtr->u.proc.optArity++;
9559 else {
9560 cmdPtr->u.proc.reqArity++;
9564 cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
9565 cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
9568 return cmdPtr;
9571 int Jim_DeleteCommand(Jim_Interp *interp, const char *name)
9573 int ret = JIM_OK;
9574 Jim_Obj *qualifiedNameObj;
9575 const char *qualname = JimQualifyName(interp, name, &qualifiedNameObj);
9577 if (Jim_DeleteHashEntry(&interp->commands, qualname) == JIM_ERR) {
9578 Jim_SetResultFormatted(interp, "can't delete \"%s\": command doesn't exist", name);
9579 ret = JIM_ERR;
9581 else {
9582 Jim_InterpIncrProcEpoch(interp);
9585 JimFreeQualifiedName(interp, qualifiedNameObj);
9587 return ret;
9590 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, const char *newName)
9592 int ret = JIM_ERR;
9593 Jim_HashEntry *he;
9594 Jim_Cmd *cmdPtr;
9595 Jim_Obj *qualifiedOldNameObj;
9596 Jim_Obj *qualifiedNewNameObj;
9597 const char *fqold;
9598 const char *fqnew;
9600 if (newName[0] == 0) {
9601 return Jim_DeleteCommand(interp, oldName);
9604 fqold = JimQualifyName(interp, oldName, &qualifiedOldNameObj);
9605 fqnew = JimQualifyName(interp, newName, &qualifiedNewNameObj);
9608 he = Jim_FindHashEntry(&interp->commands, fqold);
9609 if (he == NULL) {
9610 Jim_SetResultFormatted(interp, "can't rename \"%s\": command doesn't exist", oldName);
9612 else if (Jim_FindHashEntry(&interp->commands, fqnew)) {
9613 Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName);
9615 else {
9617 cmdPtr = Jim_GetHashEntryVal(he);
9618 JimIncrCmdRefCount(cmdPtr);
9619 JimUpdateProcNamespace(interp, cmdPtr, fqnew);
9620 Jim_AddHashEntry(&interp->commands, fqnew, cmdPtr);
9623 Jim_DeleteHashEntry(&interp->commands, fqold);
9626 Jim_InterpIncrProcEpoch(interp);
9628 ret = JIM_OK;
9631 JimFreeQualifiedName(interp, qualifiedOldNameObj);
9632 JimFreeQualifiedName(interp, qualifiedNewNameObj);
9634 return ret;
9638 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9640 Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
9643 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9645 dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
9646 dupPtr->typePtr = srcPtr->typePtr;
9647 Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
9650 static const Jim_ObjType commandObjType = {
9651 "command",
9652 FreeCommandInternalRep,
9653 DupCommandInternalRep,
9654 NULL,
9655 JIM_TYPE_REFERENCES,
9658 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9660 Jim_Cmd *cmd;
9662 if (objPtr->typePtr != &commandObjType ||
9663 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch
9664 #ifdef jim_ext_namespace
9665 || !Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
9666 #endif
9671 const char *name = Jim_String(objPtr);
9672 Jim_HashEntry *he;
9674 if (name[0] == ':' && name[1] == ':') {
9675 while (*++name == ':') {
9678 #ifdef jim_ext_namespace
9679 else if (Jim_Length(interp->framePtr->nsObj)) {
9681 Jim_Obj *nameObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9682 Jim_AppendStrings(interp, nameObj, "::", name, NULL);
9683 he = Jim_FindHashEntry(&interp->commands, Jim_String(nameObj));
9684 Jim_FreeNewObj(interp, nameObj);
9685 if (he) {
9686 goto found;
9689 #endif
9692 he = Jim_FindHashEntry(&interp->commands, name);
9693 if (he == NULL) {
9694 if (flags & JIM_ERRMSG) {
9695 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
9697 return NULL;
9699 #ifdef jim_ext_namespace
9700 found:
9701 #endif
9702 cmd = Jim_GetHashEntryVal(he);
9705 Jim_FreeIntRep(interp, objPtr);
9706 objPtr->typePtr = &commandObjType;
9707 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
9708 objPtr->internalRep.cmdValue.cmdPtr = cmd;
9709 objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
9710 Jim_IncrRefCount(interp->framePtr->nsObj);
9712 else {
9713 cmd = objPtr->internalRep.cmdValue.cmdPtr;
9715 while (cmd->u.proc.upcall) {
9716 cmd = cmd->prevCmd;
9718 return cmd;
9723 #define JIM_DICT_SUGAR 100
9725 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
9727 static const Jim_ObjType variableObjType = {
9728 "variable",
9729 NULL,
9730 NULL,
9731 NULL,
9732 JIM_TYPE_REFERENCES,
9735 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr)
9738 if (nameObjPtr->typePtr != &variableObjType) {
9739 int len;
9740 const char *str = Jim_GetString(nameObjPtr, &len);
9741 if (memchr(str, '\0', len)) {
9742 Jim_SetResultFormatted(interp, "%s name contains embedded null", type);
9743 return JIM_ERR;
9746 return JIM_OK;
9749 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9751 const char *varName;
9752 Jim_CallFrame *framePtr;
9753 Jim_HashEntry *he;
9754 int global;
9755 int len;
9758 if (objPtr->typePtr == &variableObjType) {
9759 framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
9760 if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
9762 return JIM_OK;
9766 else if (objPtr->typePtr == &dictSubstObjType) {
9767 return JIM_DICT_SUGAR;
9769 else if (JimValidName(interp, "variable", objPtr) != JIM_OK) {
9770 return JIM_ERR;
9774 varName = Jim_GetString(objPtr, &len);
9777 if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
9778 return JIM_DICT_SUGAR;
9781 if (varName[0] == ':' && varName[1] == ':') {
9782 while (*++varName == ':') {
9784 global = 1;
9785 framePtr = interp->topFramePtr;
9787 else {
9788 global = 0;
9789 framePtr = interp->framePtr;
9793 he = Jim_FindHashEntry(&framePtr->vars, varName);
9794 if (he == NULL) {
9795 if (!global && framePtr->staticVars) {
9797 he = Jim_FindHashEntry(framePtr->staticVars, varName);
9799 if (he == NULL) {
9800 return JIM_ERR;
9805 Jim_FreeIntRep(interp, objPtr);
9806 objPtr->typePtr = &variableObjType;
9807 objPtr->internalRep.varValue.callFrameId = framePtr->id;
9808 objPtr->internalRep.varValue.varPtr = Jim_GetHashEntryVal(he);
9809 objPtr->internalRep.varValue.global = global;
9810 return JIM_OK;
9814 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
9815 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
9817 static Jim_Var *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9819 const char *name;
9820 Jim_CallFrame *framePtr;
9821 int global;
9824 Jim_Var *var = Jim_Alloc(sizeof(*var));
9826 var->objPtr = valObjPtr;
9827 Jim_IncrRefCount(valObjPtr);
9828 var->linkFramePtr = NULL;
9830 name = Jim_String(nameObjPtr);
9831 if (name[0] == ':' && name[1] == ':') {
9832 while (*++name == ':') {
9834 framePtr = interp->topFramePtr;
9835 global = 1;
9837 else {
9838 framePtr = interp->framePtr;
9839 global = 0;
9843 Jim_AddHashEntry(&framePtr->vars, name, var);
9846 Jim_FreeIntRep(interp, nameObjPtr);
9847 nameObjPtr->typePtr = &variableObjType;
9848 nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
9849 nameObjPtr->internalRep.varValue.varPtr = var;
9850 nameObjPtr->internalRep.varValue.global = global;
9852 return var;
9856 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9858 int err;
9859 Jim_Var *var;
9861 switch (SetVariableFromAny(interp, nameObjPtr)) {
9862 case JIM_DICT_SUGAR:
9863 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
9865 case JIM_ERR:
9866 if (JimValidName(interp, "variable", nameObjPtr) != JIM_OK) {
9867 return JIM_ERR;
9869 JimCreateVariable(interp, nameObjPtr, valObjPtr);
9870 break;
9872 case JIM_OK:
9873 var = nameObjPtr->internalRep.varValue.varPtr;
9874 if (var->linkFramePtr == NULL) {
9875 Jim_IncrRefCount(valObjPtr);
9876 Jim_DecrRefCount(interp, var->objPtr);
9877 var->objPtr = valObjPtr;
9879 else {
9880 Jim_CallFrame *savedCallFrame;
9882 savedCallFrame = interp->framePtr;
9883 interp->framePtr = var->linkFramePtr;
9884 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
9885 interp->framePtr = savedCallFrame;
9886 if (err != JIM_OK)
9887 return err;
9890 return JIM_OK;
9893 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9895 Jim_Obj *nameObjPtr;
9896 int result;
9898 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9899 Jim_IncrRefCount(nameObjPtr);
9900 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
9901 Jim_DecrRefCount(interp, nameObjPtr);
9902 return result;
9905 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9907 Jim_CallFrame *savedFramePtr;
9908 int result;
9910 savedFramePtr = interp->framePtr;
9911 interp->framePtr = interp->topFramePtr;
9912 result = Jim_SetVariableStr(interp, name, objPtr);
9913 interp->framePtr = savedFramePtr;
9914 return result;
9917 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
9919 Jim_Obj *nameObjPtr, *valObjPtr;
9920 int result;
9922 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9923 valObjPtr = Jim_NewStringObj(interp, val, -1);
9924 Jim_IncrRefCount(nameObjPtr);
9925 Jim_IncrRefCount(valObjPtr);
9926 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
9927 Jim_DecrRefCount(interp, nameObjPtr);
9928 Jim_DecrRefCount(interp, valObjPtr);
9929 return result;
9932 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
9933 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
9935 const char *varName;
9936 const char *targetName;
9937 Jim_CallFrame *framePtr;
9938 Jim_Var *varPtr;
9941 switch (SetVariableFromAny(interp, nameObjPtr)) {
9942 case JIM_DICT_SUGAR:
9944 Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
9945 return JIM_ERR;
9947 case JIM_OK:
9948 varPtr = nameObjPtr->internalRep.varValue.varPtr;
9950 if (varPtr->linkFramePtr == NULL) {
9951 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
9952 return JIM_ERR;
9956 varPtr->linkFramePtr = NULL;
9957 break;
9962 varName = Jim_String(nameObjPtr);
9964 if (varName[0] == ':' && varName[1] == ':') {
9965 while (*++varName == ':') {
9968 framePtr = interp->topFramePtr;
9970 else {
9971 framePtr = interp->framePtr;
9974 targetName = Jim_String(targetNameObjPtr);
9975 if (targetName[0] == ':' && targetName[1] == ':') {
9976 while (*++targetName == ':') {
9978 targetNameObjPtr = Jim_NewStringObj(interp, targetName, -1);
9979 targetCallFrame = interp->topFramePtr;
9981 Jim_IncrRefCount(targetNameObjPtr);
9983 if (framePtr->level < targetCallFrame->level) {
9984 Jim_SetResultFormatted(interp,
9985 "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
9986 nameObjPtr);
9987 Jim_DecrRefCount(interp, targetNameObjPtr);
9988 return JIM_ERR;
9992 if (framePtr == targetCallFrame) {
9993 Jim_Obj *objPtr = targetNameObjPtr;
9996 while (1) {
9997 if (strcmp(Jim_String(objPtr), varName) == 0) {
9998 Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
9999 Jim_DecrRefCount(interp, targetNameObjPtr);
10000 return JIM_ERR;
10002 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
10003 break;
10004 varPtr = objPtr->internalRep.varValue.varPtr;
10005 if (varPtr->linkFramePtr != targetCallFrame)
10006 break;
10007 objPtr = varPtr->objPtr;
10012 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
10014 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
10015 Jim_DecrRefCount(interp, targetNameObjPtr);
10016 return JIM_OK;
10019 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10021 switch (SetVariableFromAny(interp, nameObjPtr)) {
10022 case JIM_OK:{
10023 Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr;
10025 if (varPtr->linkFramePtr == NULL) {
10026 return varPtr->objPtr;
10028 else {
10029 Jim_Obj *objPtr;
10032 Jim_CallFrame *savedCallFrame = interp->framePtr;
10034 interp->framePtr = varPtr->linkFramePtr;
10035 objPtr = Jim_GetVariable(interp, varPtr->objPtr, flags);
10036 interp->framePtr = savedCallFrame;
10037 if (objPtr) {
10038 return objPtr;
10043 break;
10045 case JIM_DICT_SUGAR:
10047 return JimDictSugarGet(interp, nameObjPtr, flags);
10049 if (flags & JIM_ERRMSG) {
10050 Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
10052 return NULL;
10055 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10057 Jim_CallFrame *savedFramePtr;
10058 Jim_Obj *objPtr;
10060 savedFramePtr = interp->framePtr;
10061 interp->framePtr = interp->topFramePtr;
10062 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
10063 interp->framePtr = savedFramePtr;
10065 return objPtr;
10068 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
10070 Jim_Obj *nameObjPtr, *varObjPtr;
10072 nameObjPtr = Jim_NewStringObj(interp, name, -1);
10073 Jim_IncrRefCount(nameObjPtr);
10074 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
10075 Jim_DecrRefCount(interp, nameObjPtr);
10076 return varObjPtr;
10079 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
10081 Jim_CallFrame *savedFramePtr;
10082 Jim_Obj *objPtr;
10084 savedFramePtr = interp->framePtr;
10085 interp->framePtr = interp->topFramePtr;
10086 objPtr = Jim_GetVariableStr(interp, name, flags);
10087 interp->framePtr = savedFramePtr;
10089 return objPtr;
10092 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10094 Jim_Var *varPtr;
10095 int retval;
10096 Jim_CallFrame *framePtr;
10098 retval = SetVariableFromAny(interp, nameObjPtr);
10099 if (retval == JIM_DICT_SUGAR) {
10101 return JimDictSugarSet(interp, nameObjPtr, NULL);
10103 else if (retval == JIM_OK) {
10104 varPtr = nameObjPtr->internalRep.varValue.varPtr;
10107 if (varPtr->linkFramePtr) {
10108 framePtr = interp->framePtr;
10109 interp->framePtr = varPtr->linkFramePtr;
10110 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
10111 interp->framePtr = framePtr;
10113 else {
10114 const char *name = Jim_String(nameObjPtr);
10115 if (nameObjPtr->internalRep.varValue.global) {
10116 name += 2;
10117 framePtr = interp->topFramePtr;
10119 else {
10120 framePtr = interp->framePtr;
10123 retval = Jim_DeleteHashEntry(&framePtr->vars, name);
10124 if (retval == JIM_OK) {
10126 framePtr->id = interp->callFrameEpoch++;
10130 if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
10131 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
10133 return retval;
10138 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
10139 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
10141 const char *str, *p;
10142 int len, keyLen;
10143 Jim_Obj *varObjPtr, *keyObjPtr;
10145 str = Jim_GetString(objPtr, &len);
10147 p = strchr(str, '(');
10148 JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
10150 varObjPtr = Jim_NewStringObj(interp, str, p - str);
10152 p++;
10153 keyLen = (str + len) - p;
10154 if (str[len - 1] == ')') {
10155 keyLen--;
10159 keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
10161 Jim_IncrRefCount(varObjPtr);
10162 Jim_IncrRefCount(keyObjPtr);
10163 *varPtrPtr = varObjPtr;
10164 *keyPtrPtr = keyObjPtr;
10167 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
10169 int err;
10171 SetDictSubstFromAny(interp, objPtr);
10173 err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
10174 &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);
10176 if (err == JIM_OK) {
10178 Jim_SetEmptyResult(interp);
10180 else {
10181 if (!valObjPtr) {
10183 if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
10184 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
10185 objPtr);
10186 return err;
10190 Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
10191 (valObjPtr ? "set" : "unset"), objPtr);
10193 return err;
10196 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
10197 Jim_Obj *keyObjPtr, int flags)
10199 Jim_Obj *dictObjPtr;
10200 Jim_Obj *resObjPtr = NULL;
10201 int ret;
10203 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
10204 if (!dictObjPtr) {
10205 return NULL;
10208 ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
10209 if (ret != JIM_OK) {
10210 Jim_SetResultFormatted(interp,
10211 "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
10212 ret < 0 ? "variable isn't" : "no such element in");
10214 else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
10216 Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
10219 return resObjPtr;
10223 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10225 SetDictSubstFromAny(interp, objPtr);
10227 return JimDictExpandArrayVariable(interp,
10228 objPtr->internalRep.dictSubstValue.varNameObjPtr,
10229 objPtr->internalRep.dictSubstValue.indexObjPtr, flags);
10234 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
10236 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
10237 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
10240 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
10242 JIM_NOTUSED(interp);
10244 dupPtr->internalRep.dictSubstValue.varNameObjPtr =
10245 srcPtr->internalRep.dictSubstValue.varNameObjPtr;
10246 dupPtr->internalRep.dictSubstValue.indexObjPtr = srcPtr->internalRep.dictSubstValue.indexObjPtr;
10247 dupPtr->typePtr = &dictSubstObjType;
10251 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10253 if (objPtr->typePtr != &dictSubstObjType) {
10254 Jim_Obj *varObjPtr, *keyObjPtr;
10256 if (objPtr->typePtr == &interpolatedObjType) {
10259 varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
10260 keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
10262 Jim_IncrRefCount(varObjPtr);
10263 Jim_IncrRefCount(keyObjPtr);
10265 else {
10266 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
10269 Jim_FreeIntRep(interp, objPtr);
10270 objPtr->typePtr = &dictSubstObjType;
10271 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
10272 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
10276 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10278 Jim_Obj *resObjPtr = NULL;
10279 Jim_Obj *substKeyObjPtr = NULL;
10281 SetDictSubstFromAny(interp, objPtr);
10283 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
10284 &substKeyObjPtr, JIM_NONE)
10285 != JIM_OK) {
10286 return NULL;
10288 Jim_IncrRefCount(substKeyObjPtr);
10289 resObjPtr =
10290 JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
10291 substKeyObjPtr, 0);
10292 Jim_DecrRefCount(interp, substKeyObjPtr);
10294 return resObjPtr;
10297 static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10299 Jim_Obj *resultObjPtr;
10301 if (Jim_EvalExpression(interp, objPtr, &resultObjPtr) == JIM_OK) {
10303 resultObjPtr->refCount--;
10304 return resultObjPtr;
10306 return NULL;
10310 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
10312 Jim_CallFrame *cf;
10314 if (interp->freeFramesList) {
10315 cf = interp->freeFramesList;
10316 interp->freeFramesList = cf->next;
10318 cf->argv = NULL;
10319 cf->argc = 0;
10320 cf->procArgsObjPtr = NULL;
10321 cf->procBodyObjPtr = NULL;
10322 cf->next = NULL;
10323 cf->staticVars = NULL;
10324 cf->localCommands = NULL;
10325 cf->tailcallObj = NULL;
10326 cf->tailcallCmd = NULL;
10328 else {
10329 cf = Jim_Alloc(sizeof(*cf));
10330 memset(cf, 0, sizeof(*cf));
10332 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
10335 cf->id = interp->callFrameEpoch++;
10336 cf->parent = parent;
10337 cf->level = parent ? parent->level + 1 : 0;
10338 cf->nsObj = nsObj;
10339 Jim_IncrRefCount(nsObj);
10341 return cf;
10344 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
10347 if (localCommands) {
10348 Jim_Obj *cmdNameObj;
10350 while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
10351 Jim_HashEntry *he;
10352 Jim_Obj *fqObjName;
10353 Jim_HashTable *ht = &interp->commands;
10355 const char *fqname = JimQualifyName(interp, Jim_String(cmdNameObj), &fqObjName);
10357 he = Jim_FindHashEntry(ht, fqname);
10359 if (he) {
10360 Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
10361 if (cmd->prevCmd) {
10362 Jim_Cmd *prevCmd = cmd->prevCmd;
10363 cmd->prevCmd = NULL;
10366 JimDecrCmdRefCount(interp, cmd);
10369 Jim_SetHashVal(ht, he, prevCmd);
10371 else {
10372 Jim_DeleteHashEntry(ht, fqname);
10374 Jim_InterpIncrProcEpoch(interp);
10376 Jim_DecrRefCount(interp, cmdNameObj);
10377 JimFreeQualifiedName(interp, fqObjName);
10379 Jim_FreeStack(localCommands);
10380 Jim_Free(localCommands);
10382 return JIM_OK;
10386 #define JIM_FCF_FULL 0
10387 #define JIM_FCF_REUSE 1
10388 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
10390 JimDeleteLocalProcs(interp, cf->localCommands);
10392 if (cf->procArgsObjPtr)
10393 Jim_DecrRefCount(interp, cf->procArgsObjPtr);
10394 if (cf->procBodyObjPtr)
10395 Jim_DecrRefCount(interp, cf->procBodyObjPtr);
10396 Jim_DecrRefCount(interp, cf->nsObj);
10397 if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
10398 Jim_FreeHashTable(&cf->vars);
10399 else {
10400 int i;
10401 Jim_HashEntry **table = cf->vars.table, *he;
10403 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
10404 he = table[i];
10405 while (he != NULL) {
10406 Jim_HashEntry *nextEntry = he->next;
10407 Jim_Var *varPtr = Jim_GetHashEntryVal(he);
10409 Jim_DecrRefCount(interp, varPtr->objPtr);
10410 Jim_Free(Jim_GetHashEntryKey(he));
10411 Jim_Free(varPtr);
10412 Jim_Free(he);
10413 table[i] = NULL;
10414 he = nextEntry;
10417 cf->vars.used = 0;
10419 cf->next = interp->freeFramesList;
10420 interp->freeFramesList = cf;
10425 int Jim_IsBigEndian(void)
10427 union {
10428 unsigned short s;
10429 unsigned char c[2];
10430 } uval = {0x0102};
10432 return uval.c[0] == 1;
10436 Jim_Interp *Jim_CreateInterp(void)
10438 Jim_Interp *i = Jim_Alloc(sizeof(*i));
10440 memset(i, 0, sizeof(*i));
10442 i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
10443 i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
10444 i->lastCollectTime = time(NULL);
10446 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
10447 #ifdef JIM_REFERENCES
10448 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
10449 #endif
10450 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
10451 Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
10452 i->emptyObj = Jim_NewEmptyStringObj(i);
10453 i->trueObj = Jim_NewIntObj(i, 1);
10454 i->falseObj = Jim_NewIntObj(i, 0);
10455 i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
10456 i->errorFileNameObj = i->emptyObj;
10457 i->result = i->emptyObj;
10458 i->stackTrace = Jim_NewListObj(i, NULL, 0);
10459 i->unknown = Jim_NewStringObj(i, "unknown", -1);
10460 i->errorProc = i->emptyObj;
10461 i->currentScriptObj = Jim_NewEmptyStringObj(i);
10462 i->nullScriptObj = Jim_NewEmptyStringObj(i);
10463 Jim_IncrRefCount(i->emptyObj);
10464 Jim_IncrRefCount(i->errorFileNameObj);
10465 Jim_IncrRefCount(i->result);
10466 Jim_IncrRefCount(i->stackTrace);
10467 Jim_IncrRefCount(i->unknown);
10468 Jim_IncrRefCount(i->currentScriptObj);
10469 Jim_IncrRefCount(i->nullScriptObj);
10470 Jim_IncrRefCount(i->errorProc);
10471 Jim_IncrRefCount(i->trueObj);
10472 Jim_IncrRefCount(i->falseObj);
10475 Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
10476 Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
10478 Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
10479 Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
10480 Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
10481 Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
10482 Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
10483 Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
10484 Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
10485 Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
10487 return i;
10490 void Jim_FreeInterp(Jim_Interp *i)
10492 Jim_CallFrame *cf, *cfx;
10494 Jim_Obj *objPtr, *nextObjPtr;
10497 for (cf = i->framePtr; cf; cf = cfx) {
10498 cfx = cf->parent;
10499 JimFreeCallFrame(i, cf, JIM_FCF_FULL);
10502 Jim_DecrRefCount(i, i->emptyObj);
10503 Jim_DecrRefCount(i, i->trueObj);
10504 Jim_DecrRefCount(i, i->falseObj);
10505 Jim_DecrRefCount(i, i->result);
10506 Jim_DecrRefCount(i, i->stackTrace);
10507 Jim_DecrRefCount(i, i->errorProc);
10508 Jim_DecrRefCount(i, i->unknown);
10509 Jim_DecrRefCount(i, i->errorFileNameObj);
10510 Jim_DecrRefCount(i, i->currentScriptObj);
10511 Jim_DecrRefCount(i, i->nullScriptObj);
10512 Jim_FreeHashTable(&i->commands);
10513 #ifdef JIM_REFERENCES
10514 Jim_FreeHashTable(&i->references);
10515 #endif
10516 Jim_FreeHashTable(&i->packages);
10517 Jim_Free(i->prngState);
10518 Jim_FreeHashTable(&i->assocData);
10520 #ifdef JIM_MAINTAINER
10521 if (i->liveList != NULL) {
10522 objPtr = i->liveList;
10524 printf("\n-------------------------------------\n");
10525 printf("Objects still in the free list:\n");
10526 while (objPtr) {
10527 const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
10529 if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
10530 printf("%p (%d) %-10s: '%.20s...'\n",
10531 (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
10533 else {
10534 printf("%p (%d) %-10s: '%s'\n",
10535 (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
10537 if (objPtr->typePtr == &sourceObjType) {
10538 printf("FILE %s LINE %d\n",
10539 Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
10540 objPtr->internalRep.sourceValue.lineNumber);
10542 objPtr = objPtr->nextObjPtr;
10544 printf("-------------------------------------\n\n");
10545 JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
10547 #endif
10550 objPtr = i->freeList;
10551 while (objPtr) {
10552 nextObjPtr = objPtr->nextObjPtr;
10553 Jim_Free(objPtr);
10554 objPtr = nextObjPtr;
10558 for (cf = i->freeFramesList; cf; cf = cfx) {
10559 cfx = cf->next;
10560 if (cf->vars.table)
10561 Jim_FreeHashTable(&cf->vars);
10562 Jim_Free(cf);
10566 Jim_Free(i);
10569 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10571 long level;
10572 const char *str;
10573 Jim_CallFrame *framePtr;
10575 if (levelObjPtr) {
10576 str = Jim_String(levelObjPtr);
10577 if (str[0] == '#') {
10578 char *endptr;
10580 level = jim_strtol(str + 1, &endptr);
10581 if (str[1] == '\0' || endptr[0] != '\0') {
10582 level = -1;
10585 else {
10586 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
10587 level = -1;
10589 else {
10591 level = interp->framePtr->level - level;
10595 else {
10596 str = "1";
10597 level = interp->framePtr->level - 1;
10600 if (level == 0) {
10601 return interp->topFramePtr;
10603 if (level > 0) {
10605 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10606 if (framePtr->level == level) {
10607 return framePtr;
10612 Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
10613 return NULL;
10616 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10618 long level;
10619 Jim_CallFrame *framePtr;
10621 if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
10622 if (level <= 0) {
10624 level = interp->framePtr->level + level;
10627 if (level == 0) {
10628 return interp->topFramePtr;
10632 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10633 if (framePtr->level == level) {
10634 return framePtr;
10639 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
10640 return NULL;
10643 static void JimResetStackTrace(Jim_Interp *interp)
10645 Jim_DecrRefCount(interp, interp->stackTrace);
10646 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
10647 Jim_IncrRefCount(interp->stackTrace);
10650 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
10652 int len;
10655 Jim_IncrRefCount(stackTraceObj);
10656 Jim_DecrRefCount(interp, interp->stackTrace);
10657 interp->stackTrace = stackTraceObj;
10658 interp->errorFlag = 1;
10660 len = Jim_ListLength(interp, interp->stackTrace);
10661 if (len >= 3) {
10662 if (Jim_Length(Jim_ListGetIndex(interp, interp->stackTrace, len - 2)) == 0) {
10663 interp->addStackTrace = 1;
10668 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
10669 Jim_Obj *fileNameObj, int linenr)
10671 if (strcmp(procname, "unknown") == 0) {
10672 procname = "";
10674 if (!*procname && !Jim_Length(fileNameObj)) {
10676 return;
10679 if (Jim_IsShared(interp->stackTrace)) {
10680 Jim_DecrRefCount(interp, interp->stackTrace);
10681 interp->stackTrace = Jim_DuplicateObj(interp, interp->stackTrace);
10682 Jim_IncrRefCount(interp->stackTrace);
10686 if (!*procname && Jim_Length(fileNameObj)) {
10688 int len = Jim_ListLength(interp, interp->stackTrace);
10690 if (len >= 3) {
10691 Jim_Obj *objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 3);
10692 if (Jim_Length(objPtr)) {
10694 objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 2);
10695 if (Jim_Length(objPtr) == 0) {
10697 ListSetIndex(interp, interp->stackTrace, len - 2, fileNameObj, 0);
10698 ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), 0);
10699 return;
10705 Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewStringObj(interp, procname, -1));
10706 Jim_ListAppendElement(interp, interp->stackTrace, fileNameObj);
10707 Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewIntObj(interp, linenr));
10710 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
10711 void *data)
10713 AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
10715 assocEntryPtr->delProc = delProc;
10716 assocEntryPtr->data = data;
10717 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
10720 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
10722 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
10724 if (entryPtr != NULL) {
10725 AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr);
10726 return assocEntryPtr->data;
10728 return NULL;
10731 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
10733 return Jim_DeleteHashEntry(&interp->assocData, key);
10736 int Jim_GetExitCode(Jim_Interp *interp)
10738 return interp->exitCode;
10741 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
10742 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
10744 static const Jim_ObjType intObjType = {
10745 "int",
10746 NULL,
10747 NULL,
10748 UpdateStringOfInt,
10749 JIM_TYPE_NONE,
10752 static const Jim_ObjType coercedDoubleObjType = {
10753 "coerced-double",
10754 NULL,
10755 NULL,
10756 UpdateStringOfInt,
10757 JIM_TYPE_NONE,
10761 static void UpdateStringOfInt(struct Jim_Obj *objPtr)
10763 char buf[JIM_INTEGER_SPACE + 1];
10764 jim_wide wideValue = JimWideValue(objPtr);
10765 int pos = 0;
10767 if (wideValue == 0) {
10768 buf[pos++] = '0';
10770 else {
10771 char tmp[JIM_INTEGER_SPACE];
10772 int num = 0;
10773 int i;
10775 if (wideValue < 0) {
10776 buf[pos++] = '-';
10777 i = wideValue % 10;
10778 tmp[num++] = (i > 0) ? (10 - i) : -i;
10779 wideValue /= -10;
10782 while (wideValue) {
10783 tmp[num++] = wideValue % 10;
10784 wideValue /= 10;
10787 for (i = 0; i < num; i++) {
10788 buf[pos++] = '0' + tmp[num - i - 1];
10791 buf[pos] = 0;
10793 JimSetStringBytes(objPtr, buf);
10796 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10798 jim_wide wideValue;
10799 const char *str;
10801 if (objPtr->typePtr == &coercedDoubleObjType) {
10803 objPtr->typePtr = &intObjType;
10804 return JIM_OK;
10808 str = Jim_String(objPtr);
10810 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
10811 if (flags & JIM_ERRMSG) {
10812 Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
10814 return JIM_ERR;
10816 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
10817 Jim_SetResultString(interp, "Integer value too big to be represented", -1);
10818 return JIM_ERR;
10821 Jim_FreeIntRep(interp, objPtr);
10822 objPtr->typePtr = &intObjType;
10823 objPtr->internalRep.wideValue = wideValue;
10824 return JIM_OK;
10827 #ifdef JIM_OPTIMIZATION
10828 static int JimIsWide(Jim_Obj *objPtr)
10830 return objPtr->typePtr == &intObjType;
10832 #endif
10834 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10836 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
10837 return JIM_ERR;
10838 *widePtr = JimWideValue(objPtr);
10839 return JIM_OK;
10843 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10845 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
10846 return JIM_ERR;
10847 *widePtr = JimWideValue(objPtr);
10848 return JIM_OK;
10851 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
10853 jim_wide wideValue;
10854 int retval;
10856 retval = Jim_GetWide(interp, objPtr, &wideValue);
10857 if (retval == JIM_OK) {
10858 *longPtr = (long)wideValue;
10859 return JIM_OK;
10861 return JIM_ERR;
10864 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
10866 Jim_Obj *objPtr;
10868 objPtr = Jim_NewObj(interp);
10869 objPtr->typePtr = &intObjType;
10870 objPtr->bytes = NULL;
10871 objPtr->internalRep.wideValue = wideValue;
10872 return objPtr;
10875 #define JIM_DOUBLE_SPACE 30
10877 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
10878 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
10880 static const Jim_ObjType doubleObjType = {
10881 "double",
10882 NULL,
10883 NULL,
10884 UpdateStringOfDouble,
10885 JIM_TYPE_NONE,
10888 #ifndef HAVE_ISNAN
10889 #undef isnan
10890 #define isnan(X) ((X) != (X))
10891 #endif
10892 #ifndef HAVE_ISINF
10893 #undef isinf
10894 #define isinf(X) (1.0 / (X) == 0.0)
10895 #endif
10897 static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
10899 double value = objPtr->internalRep.doubleValue;
10901 if (isnan(value)) {
10902 JimSetStringBytes(objPtr, "NaN");
10903 return;
10905 if (isinf(value)) {
10906 if (value < 0) {
10907 JimSetStringBytes(objPtr, "-Inf");
10909 else {
10910 JimSetStringBytes(objPtr, "Inf");
10912 return;
10915 char buf[JIM_DOUBLE_SPACE + 1];
10916 int i;
10917 int len = sprintf(buf, "%.12g", value);
10920 for (i = 0; i < len; i++) {
10921 if (buf[i] == '.' || buf[i] == 'e') {
10922 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
10923 char *e = strchr(buf, 'e');
10924 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
10926 e += 2;
10927 memmove(e, e + 1, len - (e - buf));
10929 #endif
10930 break;
10933 if (buf[i] == '\0') {
10934 buf[i++] = '.';
10935 buf[i++] = '0';
10936 buf[i] = '\0';
10938 JimSetStringBytes(objPtr, buf);
10942 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10944 double doubleValue;
10945 jim_wide wideValue;
10946 const char *str;
10948 str = Jim_String(objPtr);
10950 #ifdef HAVE_LONG_LONG
10952 #define MIN_INT_IN_DOUBLE -(1LL << 53)
10953 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)
10955 if (objPtr->typePtr == &intObjType
10956 && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
10957 && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {
10960 objPtr->typePtr = &coercedDoubleObjType;
10961 return JIM_OK;
10963 else
10964 #endif
10965 if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
10967 Jim_FreeIntRep(interp, objPtr);
10968 objPtr->typePtr = &coercedDoubleObjType;
10969 objPtr->internalRep.wideValue = wideValue;
10970 return JIM_OK;
10972 else {
10974 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
10975 Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
10976 return JIM_ERR;
10979 Jim_FreeIntRep(interp, objPtr);
10981 objPtr->typePtr = &doubleObjType;
10982 objPtr->internalRep.doubleValue = doubleValue;
10983 return JIM_OK;
10986 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
10988 if (objPtr->typePtr == &coercedDoubleObjType) {
10989 *doublePtr = JimWideValue(objPtr);
10990 return JIM_OK;
10992 if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
10993 return JIM_ERR;
10995 if (objPtr->typePtr == &coercedDoubleObjType) {
10996 *doublePtr = JimWideValue(objPtr);
10998 else {
10999 *doublePtr = objPtr->internalRep.doubleValue;
11001 return JIM_OK;
11004 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
11006 Jim_Obj *objPtr;
11008 objPtr = Jim_NewObj(interp);
11009 objPtr->typePtr = &doubleObjType;
11010 objPtr->bytes = NULL;
11011 objPtr->internalRep.doubleValue = doubleValue;
11012 return objPtr;
11015 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
11017 int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
11019 if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
11020 return JIM_ERR;
11021 *booleanPtr = (int) JimWideValue(objPtr);
11022 return JIM_OK;
11025 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
11027 static const char * const falses[] = {
11028 "0", "false", "no", "off", NULL
11030 static const char * const trues[] = {
11031 "1", "true", "yes", "on", NULL
11034 int boolean;
11036 int index;
11037 if (Jim_GetEnum(interp, objPtr, falses, &index, NULL, 0) == JIM_OK) {
11038 boolean = 0;
11039 } else if (Jim_GetEnum(interp, objPtr, trues, &index, NULL, 0) == JIM_OK) {
11040 boolean = 1;
11041 } else {
11042 if (flags & JIM_ERRMSG) {
11043 Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr);
11045 return JIM_ERR;
11049 Jim_FreeIntRep(interp, objPtr);
11050 objPtr->typePtr = &intObjType;
11051 objPtr->internalRep.wideValue = boolean;
11052 return JIM_OK;
11055 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
11056 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
11057 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11058 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11059 static void UpdateStringOfList(struct Jim_Obj *objPtr);
11060 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11062 static const Jim_ObjType listObjType = {
11063 "list",
11064 FreeListInternalRep,
11065 DupListInternalRep,
11066 UpdateStringOfList,
11067 JIM_TYPE_NONE,
11070 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11072 int i;
11074 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
11075 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
11077 Jim_Free(objPtr->internalRep.listValue.ele);
11080 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11082 int i;
11084 JIM_NOTUSED(interp);
11086 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
11087 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
11088 dupPtr->internalRep.listValue.ele =
11089 Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
11090 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
11091 sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
11092 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
11093 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
11095 dupPtr->typePtr = &listObjType;
11098 #define JIM_ELESTR_SIMPLE 0
11099 #define JIM_ELESTR_BRACE 1
11100 #define JIM_ELESTR_QUOTE 2
11101 static unsigned char ListElementQuotingType(const char *s, int len)
11103 int i, level, blevel, trySimple = 1;
11106 if (len == 0)
11107 return JIM_ELESTR_BRACE;
11108 if (s[0] == '"' || s[0] == '{') {
11109 trySimple = 0;
11110 goto testbrace;
11112 for (i = 0; i < len; i++) {
11113 switch (s[i]) {
11114 case ' ':
11115 case '$':
11116 case '"':
11117 case '[':
11118 case ']':
11119 case ';':
11120 case '\\':
11121 case '\r':
11122 case '\n':
11123 case '\t':
11124 case '\f':
11125 case '\v':
11126 trySimple = 0;
11128 case '{':
11129 case '}':
11130 goto testbrace;
11133 return JIM_ELESTR_SIMPLE;
11135 testbrace:
11137 if (s[len - 1] == '\\')
11138 return JIM_ELESTR_QUOTE;
11139 level = 0;
11140 blevel = 0;
11141 for (i = 0; i < len; i++) {
11142 switch (s[i]) {
11143 case '{':
11144 level++;
11145 break;
11146 case '}':
11147 level--;
11148 if (level < 0)
11149 return JIM_ELESTR_QUOTE;
11150 break;
11151 case '[':
11152 blevel++;
11153 break;
11154 case ']':
11155 blevel--;
11156 break;
11157 case '\\':
11158 if (s[i + 1] == '\n')
11159 return JIM_ELESTR_QUOTE;
11160 else if (s[i + 1] != '\0')
11161 i++;
11162 break;
11165 if (blevel < 0) {
11166 return JIM_ELESTR_QUOTE;
11169 if (level == 0) {
11170 if (!trySimple)
11171 return JIM_ELESTR_BRACE;
11172 for (i = 0; i < len; i++) {
11173 switch (s[i]) {
11174 case ' ':
11175 case '$':
11176 case '"':
11177 case '[':
11178 case ']':
11179 case ';':
11180 case '\\':
11181 case '\r':
11182 case '\n':
11183 case '\t':
11184 case '\f':
11185 case '\v':
11186 return JIM_ELESTR_BRACE;
11187 break;
11190 return JIM_ELESTR_SIMPLE;
11192 return JIM_ELESTR_QUOTE;
11195 static int BackslashQuoteString(const char *s, int len, char *q)
11197 char *p = q;
11199 while (len--) {
11200 switch (*s) {
11201 case ' ':
11202 case '$':
11203 case '"':
11204 case '[':
11205 case ']':
11206 case '{':
11207 case '}':
11208 case ';':
11209 case '\\':
11210 *p++ = '\\';
11211 *p++ = *s++;
11212 break;
11213 case '\n':
11214 *p++ = '\\';
11215 *p++ = 'n';
11216 s++;
11217 break;
11218 case '\r':
11219 *p++ = '\\';
11220 *p++ = 'r';
11221 s++;
11222 break;
11223 case '\t':
11224 *p++ = '\\';
11225 *p++ = 't';
11226 s++;
11227 break;
11228 case '\f':
11229 *p++ = '\\';
11230 *p++ = 'f';
11231 s++;
11232 break;
11233 case '\v':
11234 *p++ = '\\';
11235 *p++ = 'v';
11236 s++;
11237 break;
11238 default:
11239 *p++ = *s++;
11240 break;
11243 *p = '\0';
11245 return p - q;
11248 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
11250 #define STATIC_QUOTING_LEN 32
11251 int i, bufLen, realLength;
11252 const char *strRep;
11253 char *p;
11254 unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
11257 if (objc > STATIC_QUOTING_LEN) {
11258 quotingType = Jim_Alloc(objc);
11260 else {
11261 quotingType = staticQuoting;
11263 bufLen = 0;
11264 for (i = 0; i < objc; i++) {
11265 int len;
11267 strRep = Jim_GetString(objv[i], &len);
11268 quotingType[i] = ListElementQuotingType(strRep, len);
11269 switch (quotingType[i]) {
11270 case JIM_ELESTR_SIMPLE:
11271 if (i != 0 || strRep[0] != '#') {
11272 bufLen += len;
11273 break;
11276 quotingType[i] = JIM_ELESTR_BRACE;
11278 case JIM_ELESTR_BRACE:
11279 bufLen += len + 2;
11280 break;
11281 case JIM_ELESTR_QUOTE:
11282 bufLen += len * 2;
11283 break;
11285 bufLen++;
11287 bufLen++;
11290 p = objPtr->bytes = Jim_Alloc(bufLen + 1);
11291 realLength = 0;
11292 for (i = 0; i < objc; i++) {
11293 int len, qlen;
11295 strRep = Jim_GetString(objv[i], &len);
11297 switch (quotingType[i]) {
11298 case JIM_ELESTR_SIMPLE:
11299 memcpy(p, strRep, len);
11300 p += len;
11301 realLength += len;
11302 break;
11303 case JIM_ELESTR_BRACE:
11304 *p++ = '{';
11305 memcpy(p, strRep, len);
11306 p += len;
11307 *p++ = '}';
11308 realLength += len + 2;
11309 break;
11310 case JIM_ELESTR_QUOTE:
11311 if (i == 0 && strRep[0] == '#') {
11312 *p++ = '\\';
11313 realLength++;
11315 qlen = BackslashQuoteString(strRep, len, p);
11316 p += qlen;
11317 realLength += qlen;
11318 break;
11321 if (i + 1 != objc) {
11322 *p++ = ' ';
11323 realLength++;
11326 *p = '\0';
11327 objPtr->length = realLength;
11329 if (quotingType != staticQuoting) {
11330 Jim_Free(quotingType);
11334 static void UpdateStringOfList(struct Jim_Obj *objPtr)
11336 JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
11339 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
11341 struct JimParserCtx parser;
11342 const char *str;
11343 int strLen;
11344 Jim_Obj *fileNameObj;
11345 int linenr;
11347 if (objPtr->typePtr == &listObjType) {
11348 return JIM_OK;
11351 if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
11352 Jim_Obj **listObjPtrPtr;
11353 int len;
11354 int i;
11356 listObjPtrPtr = JimDictPairs(objPtr, &len);
11357 for (i = 0; i < len; i++) {
11358 Jim_IncrRefCount(listObjPtrPtr[i]);
11362 Jim_FreeIntRep(interp, objPtr);
11363 objPtr->typePtr = &listObjType;
11364 objPtr->internalRep.listValue.len = len;
11365 objPtr->internalRep.listValue.maxLen = len;
11366 objPtr->internalRep.listValue.ele = listObjPtrPtr;
11368 return JIM_OK;
11372 if (objPtr->typePtr == &sourceObjType) {
11373 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
11374 linenr = objPtr->internalRep.sourceValue.lineNumber;
11376 else {
11377 fileNameObj = interp->emptyObj;
11378 linenr = 1;
11380 Jim_IncrRefCount(fileNameObj);
11383 str = Jim_GetString(objPtr, &strLen);
11385 Jim_FreeIntRep(interp, objPtr);
11386 objPtr->typePtr = &listObjType;
11387 objPtr->internalRep.listValue.len = 0;
11388 objPtr->internalRep.listValue.maxLen = 0;
11389 objPtr->internalRep.listValue.ele = NULL;
11392 if (strLen) {
11393 JimParserInit(&parser, str, strLen, linenr);
11394 while (!parser.eof) {
11395 Jim_Obj *elementPtr;
11397 JimParseList(&parser);
11398 if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
11399 continue;
11400 elementPtr = JimParserGetTokenObj(interp, &parser);
11401 JimSetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
11402 ListAppendElement(objPtr, elementPtr);
11405 Jim_DecrRefCount(interp, fileNameObj);
11406 return JIM_OK;
11409 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
11411 Jim_Obj *objPtr;
11413 objPtr = Jim_NewObj(interp);
11414 objPtr->typePtr = &listObjType;
11415 objPtr->bytes = NULL;
11416 objPtr->internalRep.listValue.ele = NULL;
11417 objPtr->internalRep.listValue.len = 0;
11418 objPtr->internalRep.listValue.maxLen = 0;
11420 if (len) {
11421 ListInsertElements(objPtr, 0, len, elements);
11424 return objPtr;
11427 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
11428 Jim_Obj ***listVec)
11430 *listLen = Jim_ListLength(interp, listObj);
11431 *listVec = listObj->internalRep.listValue.ele;
11435 static int JimSign(jim_wide w)
11437 if (w == 0) {
11438 return 0;
11440 else if (w < 0) {
11441 return -1;
11443 return 1;
11447 struct lsort_info {
11448 jmp_buf jmpbuf;
11449 Jim_Obj *command;
11450 Jim_Interp *interp;
11451 enum {
11452 JIM_LSORT_ASCII,
11453 JIM_LSORT_NOCASE,
11454 JIM_LSORT_INTEGER,
11455 JIM_LSORT_REAL,
11456 JIM_LSORT_COMMAND
11457 } type;
11458 int order;
11459 int index;
11460 int indexed;
11461 int unique;
11462 int (*subfn)(Jim_Obj **, Jim_Obj **);
11465 static struct lsort_info *sort_info;
11467 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11469 Jim_Obj *lObj, *rObj;
11471 if (Jim_ListIndex(sort_info->interp, *lhsObj, sort_info->index, &lObj, JIM_ERRMSG) != JIM_OK ||
11472 Jim_ListIndex(sort_info->interp, *rhsObj, sort_info->index, &rObj, JIM_ERRMSG) != JIM_OK) {
11473 longjmp(sort_info->jmpbuf, JIM_ERR);
11475 return sort_info->subfn(&lObj, &rObj);
11479 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11481 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
11484 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11486 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
11489 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11491 jim_wide lhs = 0, rhs = 0;
11493 if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11494 Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11495 longjmp(sort_info->jmpbuf, JIM_ERR);
11498 return JimSign(lhs - rhs) * sort_info->order;
11501 static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11503 double lhs = 0, rhs = 0;
11505 if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11506 Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11507 longjmp(sort_info->jmpbuf, JIM_ERR);
11509 if (lhs == rhs) {
11510 return 0;
11512 if (lhs > rhs) {
11513 return sort_info->order;
11515 return -sort_info->order;
11518 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11520 Jim_Obj *compare_script;
11521 int rc;
11523 jim_wide ret = 0;
11526 compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
11527 Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
11528 Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);
11530 rc = Jim_EvalObj(sort_info->interp, compare_script);
11532 if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
11533 longjmp(sort_info->jmpbuf, rc);
11536 return JimSign(ret) * sort_info->order;
11539 static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
11541 int src;
11542 int dst = 0;
11543 Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
11545 for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
11546 if (comp(&ele[dst], &ele[src]) == 0) {
11548 Jim_DecrRefCount(sort_info->interp, ele[dst]);
11550 else {
11552 dst++;
11554 ele[dst] = ele[src];
11557 ele[++dst] = ele[src];
11560 listObjPtr->internalRep.listValue.len = dst;
11564 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
11566 struct lsort_info *prev_info;
11568 typedef int (qsort_comparator) (const void *, const void *);
11569 int (*fn) (Jim_Obj **, Jim_Obj **);
11570 Jim_Obj **vector;
11571 int len;
11572 int rc;
11574 JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
11575 SetListFromAny(interp, listObjPtr);
11578 prev_info = sort_info;
11579 sort_info = info;
11581 vector = listObjPtr->internalRep.listValue.ele;
11582 len = listObjPtr->internalRep.listValue.len;
11583 switch (info->type) {
11584 case JIM_LSORT_ASCII:
11585 fn = ListSortString;
11586 break;
11587 case JIM_LSORT_NOCASE:
11588 fn = ListSortStringNoCase;
11589 break;
11590 case JIM_LSORT_INTEGER:
11591 fn = ListSortInteger;
11592 break;
11593 case JIM_LSORT_REAL:
11594 fn = ListSortReal;
11595 break;
11596 case JIM_LSORT_COMMAND:
11597 fn = ListSortCommand;
11598 break;
11599 default:
11600 fn = NULL;
11601 JimPanic((1, "ListSort called with invalid sort type"));
11602 return -1;
11605 if (info->indexed) {
11607 info->subfn = fn;
11608 fn = ListSortIndexHelper;
11611 if ((rc = setjmp(info->jmpbuf)) == 0) {
11612 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
11614 if (info->unique && len > 1) {
11615 ListRemoveDuplicates(listObjPtr, fn);
11618 Jim_InvalidateStringRep(listObjPtr);
11620 sort_info = prev_info;
11622 return rc;
11625 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
11627 int currentLen = listPtr->internalRep.listValue.len;
11628 int requiredLen = currentLen + elemc;
11629 int i;
11630 Jim_Obj **point;
11632 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
11633 if (requiredLen < 2) {
11635 requiredLen = 4;
11637 else {
11638 requiredLen *= 2;
11641 listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
11642 sizeof(Jim_Obj *) * requiredLen);
11644 listPtr->internalRep.listValue.maxLen = requiredLen;
11646 if (idx < 0) {
11647 idx = currentLen;
11649 point = listPtr->internalRep.listValue.ele + idx;
11650 memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
11651 for (i = 0; i < elemc; ++i) {
11652 point[i] = elemVec[i];
11653 Jim_IncrRefCount(point[i]);
11655 listPtr->internalRep.listValue.len += elemc;
11658 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
11660 ListInsertElements(listPtr, -1, 1, &objPtr);
11663 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11665 ListInsertElements(listPtr, -1,
11666 appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele);
11669 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
11671 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object"));
11672 SetListFromAny(interp, listPtr);
11673 Jim_InvalidateStringRep(listPtr);
11674 ListAppendElement(listPtr, objPtr);
11677 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11679 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object"));
11680 SetListFromAny(interp, listPtr);
11681 SetListFromAny(interp, appendListPtr);
11682 Jim_InvalidateStringRep(listPtr);
11683 ListAppendList(listPtr, appendListPtr);
11686 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
11688 SetListFromAny(interp, objPtr);
11689 return objPtr->internalRep.listValue.len;
11692 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11693 int objc, Jim_Obj *const *objVec)
11695 JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
11696 SetListFromAny(interp, listPtr);
11697 if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
11698 idx = listPtr->internalRep.listValue.len;
11699 else if (idx < 0)
11700 idx = 0;
11701 Jim_InvalidateStringRep(listPtr);
11702 ListInsertElements(listPtr, idx, objc, objVec);
11705 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
11707 SetListFromAny(interp, listPtr);
11708 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11709 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11710 return NULL;
11712 if (idx < 0)
11713 idx = listPtr->internalRep.listValue.len + idx;
11714 return listPtr->internalRep.listValue.ele[idx];
11717 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
11719 *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
11720 if (*objPtrPtr == NULL) {
11721 if (flags & JIM_ERRMSG) {
11722 Jim_SetResultString(interp, "list index out of range", -1);
11724 return JIM_ERR;
11726 return JIM_OK;
11729 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11730 Jim_Obj *newObjPtr, int flags)
11732 SetListFromAny(interp, listPtr);
11733 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11734 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11735 if (flags & JIM_ERRMSG) {
11736 Jim_SetResultString(interp, "list index out of range", -1);
11738 return JIM_ERR;
11740 if (idx < 0)
11741 idx = listPtr->internalRep.listValue.len + idx;
11742 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
11743 listPtr->internalRep.listValue.ele[idx] = newObjPtr;
11744 Jim_IncrRefCount(newObjPtr);
11745 return JIM_OK;
11748 int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
11749 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
11751 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
11752 int shared, i, idx;
11754 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
11755 if (objPtr == NULL)
11756 return JIM_ERR;
11757 if ((shared = Jim_IsShared(objPtr)))
11758 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
11759 for (i = 0; i < indexc - 1; i++) {
11760 listObjPtr = objPtr;
11761 if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
11762 goto err;
11763 if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_ERRMSG) != JIM_OK) {
11764 goto err;
11766 if (Jim_IsShared(objPtr)) {
11767 objPtr = Jim_DuplicateObj(interp, objPtr);
11768 ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
11770 Jim_InvalidateStringRep(listObjPtr);
11772 if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
11773 goto err;
11774 if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
11775 goto err;
11776 Jim_InvalidateStringRep(objPtr);
11777 Jim_InvalidateStringRep(varObjPtr);
11778 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
11779 goto err;
11780 Jim_SetResult(interp, varObjPtr);
11781 return JIM_OK;
11782 err:
11783 if (shared) {
11784 Jim_FreeNewObj(interp, varObjPtr);
11786 return JIM_ERR;
11789 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
11791 int i;
11792 int listLen = Jim_ListLength(interp, listObjPtr);
11793 Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
11795 for (i = 0; i < listLen; ) {
11796 Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i));
11797 if (++i != listLen) {
11798 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11801 return resObjPtr;
11804 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
11806 int i;
11808 for (i = 0; i < objc; i++) {
11809 if (!Jim_IsList(objv[i]))
11810 break;
11812 if (i == objc) {
11813 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
11815 for (i = 0; i < objc; i++)
11816 ListAppendList(objPtr, objv[i]);
11817 return objPtr;
11819 else {
11821 int len = 0, objLen;
11822 char *bytes, *p;
11825 for (i = 0; i < objc; i++) {
11826 len += Jim_Length(objv[i]);
11828 if (objc)
11829 len += objc - 1;
11831 p = bytes = Jim_Alloc(len + 1);
11832 for (i = 0; i < objc; i++) {
11833 const char *s = Jim_GetString(objv[i], &objLen);
11836 while (objLen && isspace(UCHAR(*s))) {
11837 s++;
11838 objLen--;
11839 len--;
11842 while (objLen && isspace(UCHAR(s[objLen - 1]))) {
11844 if (objLen > 1 && s[objLen - 2] == '\\') {
11845 break;
11847 objLen--;
11848 len--;
11850 memcpy(p, s, objLen);
11851 p += objLen;
11852 if (i + 1 != objc) {
11853 if (objLen)
11854 *p++ = ' ';
11855 else {
11856 len--;
11860 *p = '\0';
11861 return Jim_NewStringObjNoAlloc(interp, bytes, len);
11865 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr,
11866 Jim_Obj *lastObjPtr)
11868 int first, last;
11869 int len, rangeLen;
11871 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
11872 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
11873 return NULL;
11874 len = Jim_ListLength(interp, listObjPtr);
11875 first = JimRelToAbsIndex(len, first);
11876 last = JimRelToAbsIndex(len, last);
11877 JimRelToAbsRange(len, &first, &last, &rangeLen);
11878 if (first == 0 && last == len) {
11879 return listObjPtr;
11881 return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
11884 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11885 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11886 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
11887 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11890 static unsigned int JimObjectHTHashFunction(const void *key)
11892 int len;
11893 const char *str = Jim_GetString((Jim_Obj *)key, &len);
11894 return Jim_GenHashFunction((const unsigned char *)str, len);
11897 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
11899 return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
11902 static void *JimObjectHTKeyValDup(void *privdata, const void *val)
11904 Jim_IncrRefCount((Jim_Obj *)val);
11905 return (void *)val;
11908 static void JimObjectHTKeyValDestructor(void *interp, void *val)
11910 Jim_DecrRefCount(interp, (Jim_Obj *)val);
11913 static const Jim_HashTableType JimDictHashTableType = {
11914 JimObjectHTHashFunction,
11915 JimObjectHTKeyValDup,
11916 JimObjectHTKeyValDup,
11917 JimObjectHTKeyCompare,
11918 JimObjectHTKeyValDestructor,
11919 JimObjectHTKeyValDestructor
11922 static const Jim_ObjType dictObjType = {
11923 "dict",
11924 FreeDictInternalRep,
11925 DupDictInternalRep,
11926 UpdateStringOfDict,
11927 JIM_TYPE_NONE,
11930 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11932 JIM_NOTUSED(interp);
11934 Jim_FreeHashTable(objPtr->internalRep.ptr);
11935 Jim_Free(objPtr->internalRep.ptr);
11938 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11940 Jim_HashTable *ht, *dupHt;
11941 Jim_HashTableIterator htiter;
11942 Jim_HashEntry *he;
11945 ht = srcPtr->internalRep.ptr;
11946 dupHt = Jim_Alloc(sizeof(*dupHt));
11947 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
11948 if (ht->size != 0)
11949 Jim_ExpandHashTable(dupHt, ht->size);
11951 JimInitHashTableIterator(ht, &htiter);
11952 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
11953 Jim_AddHashEntry(dupHt, he->key, he->u.val);
11956 dupPtr->internalRep.ptr = dupHt;
11957 dupPtr->typePtr = &dictObjType;
11960 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len)
11962 Jim_HashTable *ht;
11963 Jim_HashTableIterator htiter;
11964 Jim_HashEntry *he;
11965 Jim_Obj **objv;
11966 int i;
11968 ht = dictPtr->internalRep.ptr;
11971 objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *));
11972 JimInitHashTableIterator(ht, &htiter);
11973 i = 0;
11974 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
11975 objv[i++] = Jim_GetHashEntryKey(he);
11976 objv[i++] = Jim_GetHashEntryVal(he);
11978 *len = i;
11979 return objv;
11982 static void UpdateStringOfDict(struct Jim_Obj *objPtr)
11985 int len;
11986 Jim_Obj **objv = JimDictPairs(objPtr, &len);
11989 JimMakeListStringRep(objPtr, objv, len);
11991 Jim_Free(objv);
11994 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
11996 int listlen;
11998 if (objPtr->typePtr == &dictObjType) {
11999 return JIM_OK;
12002 if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
12003 Jim_String(objPtr);
12007 listlen = Jim_ListLength(interp, objPtr);
12008 if (listlen % 2) {
12009 Jim_SetResultString(interp, "missing value to go with key", -1);
12010 return JIM_ERR;
12012 else {
12014 Jim_HashTable *ht;
12015 int i;
12017 ht = Jim_Alloc(sizeof(*ht));
12018 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
12020 for (i = 0; i < listlen; i += 2) {
12021 Jim_Obj *keyObjPtr = Jim_ListGetIndex(interp, objPtr, i);
12022 Jim_Obj *valObjPtr = Jim_ListGetIndex(interp, objPtr, i + 1);
12024 Jim_ReplaceHashEntry(ht, keyObjPtr, valObjPtr);
12027 Jim_FreeIntRep(interp, objPtr);
12028 objPtr->typePtr = &dictObjType;
12029 objPtr->internalRep.ptr = ht;
12031 return JIM_OK;
12037 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12038 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12040 Jim_HashTable *ht = objPtr->internalRep.ptr;
12042 if (valueObjPtr == NULL) {
12043 return Jim_DeleteHashEntry(ht, keyObjPtr);
12045 Jim_ReplaceHashEntry(ht, keyObjPtr, valueObjPtr);
12046 return JIM_OK;
12049 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12050 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12052 JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
12053 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
12054 return JIM_ERR;
12056 Jim_InvalidateStringRep(objPtr);
12057 return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
12060 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
12062 Jim_Obj *objPtr;
12063 int i;
12065 JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
12067 objPtr = Jim_NewObj(interp);
12068 objPtr->typePtr = &dictObjType;
12069 objPtr->bytes = NULL;
12070 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
12071 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
12072 for (i = 0; i < len; i += 2)
12073 DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
12074 return objPtr;
12077 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
12078 Jim_Obj **objPtrPtr, int flags)
12080 Jim_HashEntry *he;
12081 Jim_HashTable *ht;
12083 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12084 return -1;
12086 ht = dictPtr->internalRep.ptr;
12087 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
12088 if (flags & JIM_ERRMSG) {
12089 Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
12091 return JIM_ERR;
12093 else {
12094 *objPtrPtr = Jim_GetHashEntryVal(he);
12095 return JIM_OK;
12100 int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len)
12102 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12103 return JIM_ERR;
12105 *objPtrPtr = JimDictPairs(dictPtr, len);
12107 return JIM_OK;
12112 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
12113 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
12115 int i;
12117 if (keyc == 0) {
12118 *objPtrPtr = dictPtr;
12119 return JIM_OK;
12122 for (i = 0; i < keyc; i++) {
12123 Jim_Obj *objPtr;
12125 int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
12126 if (rc != JIM_OK) {
12127 return rc;
12129 dictPtr = objPtr;
12131 *objPtrPtr = dictPtr;
12132 return JIM_OK;
12135 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
12136 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags)
12138 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
12139 int shared, i;
12141 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
12142 if (objPtr == NULL) {
12143 if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
12145 return JIM_ERR;
12147 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
12148 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
12149 Jim_FreeNewObj(interp, varObjPtr);
12150 return JIM_ERR;
12153 if ((shared = Jim_IsShared(objPtr)))
12154 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
12155 for (i = 0; i < keyc; i++) {
12156 dictObjPtr = objPtr;
12159 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
12160 goto err;
12163 if (i == keyc - 1) {
12165 if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
12166 if (newObjPtr || (flags & JIM_MUSTEXIST)) {
12167 goto err;
12170 break;
12174 Jim_InvalidateStringRep(dictObjPtr);
12175 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
12176 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
12177 if (Jim_IsShared(objPtr)) {
12178 objPtr = Jim_DuplicateObj(interp, objPtr);
12179 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12182 else {
12183 if (newObjPtr == NULL) {
12184 goto err;
12186 objPtr = Jim_NewDictObj(interp, NULL, 0);
12187 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12191 Jim_InvalidateStringRep(objPtr);
12192 Jim_InvalidateStringRep(varObjPtr);
12193 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
12194 goto err;
12196 Jim_SetResult(interp, varObjPtr);
12197 return JIM_OK;
12198 err:
12199 if (shared) {
12200 Jim_FreeNewObj(interp, varObjPtr);
12202 return JIM_ERR;
12205 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
12206 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
12208 static const Jim_ObjType indexObjType = {
12209 "index",
12210 NULL,
12211 NULL,
12212 UpdateStringOfIndex,
12213 JIM_TYPE_NONE,
12216 static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
12218 if (objPtr->internalRep.intValue == -1) {
12219 JimSetStringBytes(objPtr, "end");
12221 else {
12222 char buf[JIM_INTEGER_SPACE + 1];
12223 if (objPtr->internalRep.intValue >= 0) {
12224 sprintf(buf, "%d", objPtr->internalRep.intValue);
12226 else {
12228 sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
12230 JimSetStringBytes(objPtr, buf);
12234 static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12236 int idx, end = 0;
12237 const char *str;
12238 char *endptr;
12241 str = Jim_String(objPtr);
12244 if (strncmp(str, "end", 3) == 0) {
12245 end = 1;
12246 str += 3;
12247 idx = 0;
12249 else {
12250 idx = jim_strtol(str, &endptr);
12252 if (endptr == str) {
12253 goto badindex;
12255 str = endptr;
12259 if (*str == '+' || *str == '-') {
12260 int sign = (*str == '+' ? 1 : -1);
12262 idx += sign * jim_strtol(++str, &endptr);
12263 if (str == endptr || *endptr) {
12264 goto badindex;
12266 str = endptr;
12269 while (isspace(UCHAR(*str))) {
12270 str++;
12272 if (*str) {
12273 goto badindex;
12275 if (end) {
12276 if (idx > 0) {
12277 idx = INT_MAX;
12279 else {
12281 idx--;
12284 else if (idx < 0) {
12285 idx = -INT_MAX;
12289 Jim_FreeIntRep(interp, objPtr);
12290 objPtr->typePtr = &indexObjType;
12291 objPtr->internalRep.intValue = idx;
12292 return JIM_OK;
12294 badindex:
12295 Jim_SetResultFormatted(interp,
12296 "bad index \"%#s\": must be integer?[+-]integer? or end?[+-]integer?", objPtr);
12297 return JIM_ERR;
12300 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
12303 if (objPtr->typePtr == &intObjType) {
12304 jim_wide val = JimWideValue(objPtr);
12306 if (val < 0)
12307 *indexPtr = -INT_MAX;
12308 else if (val > INT_MAX)
12309 *indexPtr = INT_MAX;
12310 else
12311 *indexPtr = (int)val;
12312 return JIM_OK;
12314 if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
12315 return JIM_ERR;
12316 *indexPtr = objPtr->internalRep.intValue;
12317 return JIM_OK;
12322 static const char * const jimReturnCodes[] = {
12323 "ok",
12324 "error",
12325 "return",
12326 "break",
12327 "continue",
12328 "signal",
12329 "exit",
12330 "eval",
12331 NULL
12334 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes))
12336 static const Jim_ObjType returnCodeObjType = {
12337 "return-code",
12338 NULL,
12339 NULL,
12340 NULL,
12341 JIM_TYPE_NONE,
12344 const char *Jim_ReturnCode(int code)
12346 if (code < 0 || code >= (int)jimReturnCodesSize) {
12347 return "?";
12349 else {
12350 return jimReturnCodes[code];
12354 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12356 int returnCode;
12357 jim_wide wideValue;
12360 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
12361 returnCode = (int)wideValue;
12362 else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
12363 Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
12364 return JIM_ERR;
12367 Jim_FreeIntRep(interp, objPtr);
12368 objPtr->typePtr = &returnCodeObjType;
12369 objPtr->internalRep.intValue = returnCode;
12370 return JIM_OK;
12373 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
12375 if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
12376 return JIM_ERR;
12377 *intPtr = objPtr->internalRep.intValue;
12378 return JIM_OK;
12381 static int JimParseExprOperator(struct JimParserCtx *pc);
12382 static int JimParseExprNumber(struct JimParserCtx *pc);
12383 static int JimParseExprIrrational(struct JimParserCtx *pc);
12384 static int JimParseExprBoolean(struct JimParserCtx *pc);
12389 enum
12393 JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
12394 JIM_EXPROP_DIV,
12395 JIM_EXPROP_MOD,
12396 JIM_EXPROP_SUB,
12397 JIM_EXPROP_ADD,
12398 JIM_EXPROP_LSHIFT,
12399 JIM_EXPROP_RSHIFT,
12400 JIM_EXPROP_ROTL,
12401 JIM_EXPROP_ROTR,
12402 JIM_EXPROP_LT,
12403 JIM_EXPROP_GT,
12404 JIM_EXPROP_LTE,
12405 JIM_EXPROP_GTE,
12406 JIM_EXPROP_NUMEQ,
12407 JIM_EXPROP_NUMNE,
12408 JIM_EXPROP_BITAND,
12409 JIM_EXPROP_BITXOR,
12410 JIM_EXPROP_BITOR,
12413 JIM_EXPROP_LOGICAND,
12414 JIM_EXPROP_LOGICAND_LEFT,
12415 JIM_EXPROP_LOGICAND_RIGHT,
12418 JIM_EXPROP_LOGICOR,
12419 JIM_EXPROP_LOGICOR_LEFT,
12420 JIM_EXPROP_LOGICOR_RIGHT,
12424 JIM_EXPROP_TERNARY,
12425 JIM_EXPROP_TERNARY_LEFT,
12426 JIM_EXPROP_TERNARY_RIGHT,
12429 JIM_EXPROP_COLON,
12430 JIM_EXPROP_COLON_LEFT,
12431 JIM_EXPROP_COLON_RIGHT,
12433 JIM_EXPROP_POW,
12436 JIM_EXPROP_STREQ,
12437 JIM_EXPROP_STRNE,
12438 JIM_EXPROP_STRIN,
12439 JIM_EXPROP_STRNI,
12442 JIM_EXPROP_NOT,
12443 JIM_EXPROP_BITNOT,
12444 JIM_EXPROP_UNARYMINUS,
12445 JIM_EXPROP_UNARYPLUS,
12448 JIM_EXPROP_FUNC_FIRST,
12449 JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST,
12450 JIM_EXPROP_FUNC_WIDE,
12451 JIM_EXPROP_FUNC_ABS,
12452 JIM_EXPROP_FUNC_DOUBLE,
12453 JIM_EXPROP_FUNC_ROUND,
12454 JIM_EXPROP_FUNC_RAND,
12455 JIM_EXPROP_FUNC_SRAND,
12458 JIM_EXPROP_FUNC_SIN,
12459 JIM_EXPROP_FUNC_COS,
12460 JIM_EXPROP_FUNC_TAN,
12461 JIM_EXPROP_FUNC_ASIN,
12462 JIM_EXPROP_FUNC_ACOS,
12463 JIM_EXPROP_FUNC_ATAN,
12464 JIM_EXPROP_FUNC_ATAN2,
12465 JIM_EXPROP_FUNC_SINH,
12466 JIM_EXPROP_FUNC_COSH,
12467 JIM_EXPROP_FUNC_TANH,
12468 JIM_EXPROP_FUNC_CEIL,
12469 JIM_EXPROP_FUNC_FLOOR,
12470 JIM_EXPROP_FUNC_EXP,
12471 JIM_EXPROP_FUNC_LOG,
12472 JIM_EXPROP_FUNC_LOG10,
12473 JIM_EXPROP_FUNC_SQRT,
12474 JIM_EXPROP_FUNC_POW,
12475 JIM_EXPROP_FUNC_HYPOT,
12476 JIM_EXPROP_FUNC_FMOD,
12479 struct JimExprState
12481 Jim_Obj **stack;
12482 int stacklen;
12483 int opcode;
12484 int skip;
12488 typedef struct Jim_ExprOperator
12490 const char *name;
12491 int (*funcop) (Jim_Interp *interp, struct JimExprState * e);
12492 unsigned char precedence;
12493 unsigned char arity;
12494 unsigned char lazy;
12495 unsigned char namelen;
12496 } Jim_ExprOperator;
12498 static void ExprPush(struct JimExprState *e, Jim_Obj *obj)
12500 Jim_IncrRefCount(obj);
12501 e->stack[e->stacklen++] = obj;
12504 static Jim_Obj *ExprPop(struct JimExprState *e)
12506 return e->stack[--e->stacklen];
12509 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e)
12511 int intresult = 1;
12512 int rc = JIM_OK;
12513 Jim_Obj *A = ExprPop(e);
12514 double dA, dC = 0;
12515 jim_wide wA, wC = 0;
12517 if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
12518 switch (e->opcode) {
12519 case JIM_EXPROP_FUNC_INT:
12520 case JIM_EXPROP_FUNC_WIDE:
12521 case JIM_EXPROP_FUNC_ROUND:
12522 case JIM_EXPROP_UNARYPLUS:
12523 wC = wA;
12524 break;
12525 case JIM_EXPROP_FUNC_DOUBLE:
12526 dC = wA;
12527 intresult = 0;
12528 break;
12529 case JIM_EXPROP_FUNC_ABS:
12530 wC = wA >= 0 ? wA : -wA;
12531 break;
12532 case JIM_EXPROP_UNARYMINUS:
12533 wC = -wA;
12534 break;
12535 case JIM_EXPROP_NOT:
12536 wC = !wA;
12537 break;
12538 default:
12539 abort();
12542 else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
12543 switch (e->opcode) {
12544 case JIM_EXPROP_FUNC_INT:
12545 case JIM_EXPROP_FUNC_WIDE:
12546 wC = dA;
12547 break;
12548 case JIM_EXPROP_FUNC_ROUND:
12549 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
12550 break;
12551 case JIM_EXPROP_FUNC_DOUBLE:
12552 case JIM_EXPROP_UNARYPLUS:
12553 dC = dA;
12554 intresult = 0;
12555 break;
12556 case JIM_EXPROP_FUNC_ABS:
12557 #ifdef JIM_MATH_FUNCTIONS
12558 dC = fabs(dA);
12559 #else
12560 dC = dA >= 0 ? dA : -dA;
12561 #endif
12562 intresult = 0;
12563 break;
12564 case JIM_EXPROP_UNARYMINUS:
12565 dC = -dA;
12566 intresult = 0;
12567 break;
12568 case JIM_EXPROP_NOT:
12569 wC = !dA;
12570 break;
12571 default:
12572 abort();
12576 if (rc == JIM_OK) {
12577 if (intresult) {
12578 ExprPush(e, Jim_NewIntObj(interp, wC));
12580 else {
12581 ExprPush(e, Jim_NewDoubleObj(interp, dC));
12585 Jim_DecrRefCount(interp, A);
12587 return rc;
12590 static double JimRandDouble(Jim_Interp *interp)
12592 unsigned long x;
12593 JimRandomBytes(interp, &x, sizeof(x));
12595 return (double)x / (unsigned long)~0;
12598 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e)
12600 Jim_Obj *A = ExprPop(e);
12601 jim_wide wA;
12603 int rc = Jim_GetWide(interp, A, &wA);
12604 if (rc == JIM_OK) {
12605 switch (e->opcode) {
12606 case JIM_EXPROP_BITNOT:
12607 ExprPush(e, Jim_NewIntObj(interp, ~wA));
12608 break;
12609 case JIM_EXPROP_FUNC_SRAND:
12610 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
12611 ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12612 break;
12613 default:
12614 abort();
12618 Jim_DecrRefCount(interp, A);
12620 return rc;
12623 static int JimExprOpNone(Jim_Interp *interp, struct JimExprState *e)
12625 JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
12627 ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12629 return JIM_OK;
12632 #ifdef JIM_MATH_FUNCTIONS
12633 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e)
12635 int rc;
12636 Jim_Obj *A = ExprPop(e);
12637 double dA, dC;
12639 rc = Jim_GetDouble(interp, A, &dA);
12640 if (rc == JIM_OK) {
12641 switch (e->opcode) {
12642 case JIM_EXPROP_FUNC_SIN:
12643 dC = sin(dA);
12644 break;
12645 case JIM_EXPROP_FUNC_COS:
12646 dC = cos(dA);
12647 break;
12648 case JIM_EXPROP_FUNC_TAN:
12649 dC = tan(dA);
12650 break;
12651 case JIM_EXPROP_FUNC_ASIN:
12652 dC = asin(dA);
12653 break;
12654 case JIM_EXPROP_FUNC_ACOS:
12655 dC = acos(dA);
12656 break;
12657 case JIM_EXPROP_FUNC_ATAN:
12658 dC = atan(dA);
12659 break;
12660 case JIM_EXPROP_FUNC_SINH:
12661 dC = sinh(dA);
12662 break;
12663 case JIM_EXPROP_FUNC_COSH:
12664 dC = cosh(dA);
12665 break;
12666 case JIM_EXPROP_FUNC_TANH:
12667 dC = tanh(dA);
12668 break;
12669 case JIM_EXPROP_FUNC_CEIL:
12670 dC = ceil(dA);
12671 break;
12672 case JIM_EXPROP_FUNC_FLOOR:
12673 dC = floor(dA);
12674 break;
12675 case JIM_EXPROP_FUNC_EXP:
12676 dC = exp(dA);
12677 break;
12678 case JIM_EXPROP_FUNC_LOG:
12679 dC = log(dA);
12680 break;
12681 case JIM_EXPROP_FUNC_LOG10:
12682 dC = log10(dA);
12683 break;
12684 case JIM_EXPROP_FUNC_SQRT:
12685 dC = sqrt(dA);
12686 break;
12687 default:
12688 abort();
12690 ExprPush(e, Jim_NewDoubleObj(interp, dC));
12693 Jim_DecrRefCount(interp, A);
12695 return rc;
12697 #endif
12700 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e)
12702 Jim_Obj *B = ExprPop(e);
12703 Jim_Obj *A = ExprPop(e);
12704 jim_wide wA, wB;
12705 int rc = JIM_ERR;
12707 if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
12708 jim_wide wC;
12710 rc = JIM_OK;
12712 switch (e->opcode) {
12713 case JIM_EXPROP_LSHIFT:
12714 wC = wA << wB;
12715 break;
12716 case JIM_EXPROP_RSHIFT:
12717 wC = wA >> wB;
12718 break;
12719 case JIM_EXPROP_BITAND:
12720 wC = wA & wB;
12721 break;
12722 case JIM_EXPROP_BITXOR:
12723 wC = wA ^ wB;
12724 break;
12725 case JIM_EXPROP_BITOR:
12726 wC = wA | wB;
12727 break;
12728 case JIM_EXPROP_MOD:
12729 if (wB == 0) {
12730 wC = 0;
12731 Jim_SetResultString(interp, "Division by zero", -1);
12732 rc = JIM_ERR;
12734 else {
12735 int negative = 0;
12737 if (wB < 0) {
12738 wB = -wB;
12739 wA = -wA;
12740 negative = 1;
12742 wC = wA % wB;
12743 if (wC < 0) {
12744 wC += wB;
12746 if (negative) {
12747 wC = -wC;
12750 break;
12751 case JIM_EXPROP_ROTL:
12752 case JIM_EXPROP_ROTR:{
12754 unsigned long uA = (unsigned long)wA;
12755 unsigned long uB = (unsigned long)wB;
12756 const unsigned int S = sizeof(unsigned long) * 8;
12759 uB %= S;
12761 if (e->opcode == JIM_EXPROP_ROTR) {
12762 uB = S - uB;
12764 wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
12765 break;
12767 default:
12768 abort();
12770 ExprPush(e, Jim_NewIntObj(interp, wC));
12774 Jim_DecrRefCount(interp, A);
12775 Jim_DecrRefCount(interp, B);
12777 return rc;
12782 static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e)
12784 int rc = JIM_OK;
12785 double dA, dB, dC = 0;
12786 jim_wide wA, wB, wC = 0;
12788 Jim_Obj *B = ExprPop(e);
12789 Jim_Obj *A = ExprPop(e);
12791 if ((A->typePtr != &doubleObjType || A->bytes) &&
12792 (B->typePtr != &doubleObjType || B->bytes) &&
12793 JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
12797 switch (e->opcode) {
12798 case JIM_EXPROP_POW:
12799 case JIM_EXPROP_FUNC_POW:
12800 if (wA == 0 && wB < 0) {
12801 Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
12802 rc = JIM_ERR;
12803 goto done;
12805 wC = JimPowWide(wA, wB);
12806 goto intresult;
12807 case JIM_EXPROP_ADD:
12808 wC = wA + wB;
12809 goto intresult;
12810 case JIM_EXPROP_SUB:
12811 wC = wA - wB;
12812 goto intresult;
12813 case JIM_EXPROP_MUL:
12814 wC = wA * wB;
12815 goto intresult;
12816 case JIM_EXPROP_DIV:
12817 if (wB == 0) {
12818 Jim_SetResultString(interp, "Division by zero", -1);
12819 rc = JIM_ERR;
12820 goto done;
12822 else {
12823 if (wB < 0) {
12824 wB = -wB;
12825 wA = -wA;
12827 wC = wA / wB;
12828 if (wA % wB < 0) {
12829 wC--;
12831 goto intresult;
12833 case JIM_EXPROP_LT:
12834 wC = wA < wB;
12835 goto intresult;
12836 case JIM_EXPROP_GT:
12837 wC = wA > wB;
12838 goto intresult;
12839 case JIM_EXPROP_LTE:
12840 wC = wA <= wB;
12841 goto intresult;
12842 case JIM_EXPROP_GTE:
12843 wC = wA >= wB;
12844 goto intresult;
12845 case JIM_EXPROP_NUMEQ:
12846 wC = wA == wB;
12847 goto intresult;
12848 case JIM_EXPROP_NUMNE:
12849 wC = wA != wB;
12850 goto intresult;
12853 if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
12854 switch (e->opcode) {
12855 #ifndef JIM_MATH_FUNCTIONS
12856 case JIM_EXPROP_POW:
12857 case JIM_EXPROP_FUNC_POW:
12858 case JIM_EXPROP_FUNC_ATAN2:
12859 case JIM_EXPROP_FUNC_HYPOT:
12860 case JIM_EXPROP_FUNC_FMOD:
12861 Jim_SetResultString(interp, "unsupported", -1);
12862 rc = JIM_ERR;
12863 goto done;
12864 #else
12865 case JIM_EXPROP_POW:
12866 case JIM_EXPROP_FUNC_POW:
12867 dC = pow(dA, dB);
12868 goto doubleresult;
12869 case JIM_EXPROP_FUNC_ATAN2:
12870 dC = atan2(dA, dB);
12871 goto doubleresult;
12872 case JIM_EXPROP_FUNC_HYPOT:
12873 dC = hypot(dA, dB);
12874 goto doubleresult;
12875 case JIM_EXPROP_FUNC_FMOD:
12876 dC = fmod(dA, dB);
12877 goto doubleresult;
12878 #endif
12879 case JIM_EXPROP_ADD:
12880 dC = dA + dB;
12881 goto doubleresult;
12882 case JIM_EXPROP_SUB:
12883 dC = dA - dB;
12884 goto doubleresult;
12885 case JIM_EXPROP_MUL:
12886 dC = dA * dB;
12887 goto doubleresult;
12888 case JIM_EXPROP_DIV:
12889 if (dB == 0) {
12890 #ifdef INFINITY
12891 dC = dA < 0 ? -INFINITY : INFINITY;
12892 #else
12893 dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
12894 #endif
12896 else {
12897 dC = dA / dB;
12899 goto doubleresult;
12900 case JIM_EXPROP_LT:
12901 wC = dA < dB;
12902 goto intresult;
12903 case JIM_EXPROP_GT:
12904 wC = dA > dB;
12905 goto intresult;
12906 case JIM_EXPROP_LTE:
12907 wC = dA <= dB;
12908 goto intresult;
12909 case JIM_EXPROP_GTE:
12910 wC = dA >= dB;
12911 goto intresult;
12912 case JIM_EXPROP_NUMEQ:
12913 wC = dA == dB;
12914 goto intresult;
12915 case JIM_EXPROP_NUMNE:
12916 wC = dA != dB;
12917 goto intresult;
12920 else {
12924 int i = Jim_StringCompareObj(interp, A, B, 0);
12926 switch (e->opcode) {
12927 case JIM_EXPROP_LT:
12928 wC = i < 0;
12929 goto intresult;
12930 case JIM_EXPROP_GT:
12931 wC = i > 0;
12932 goto intresult;
12933 case JIM_EXPROP_LTE:
12934 wC = i <= 0;
12935 goto intresult;
12936 case JIM_EXPROP_GTE:
12937 wC = i >= 0;
12938 goto intresult;
12939 case JIM_EXPROP_NUMEQ:
12940 wC = i == 0;
12941 goto intresult;
12942 case JIM_EXPROP_NUMNE:
12943 wC = i != 0;
12944 goto intresult;
12948 rc = JIM_ERR;
12949 done:
12950 Jim_DecrRefCount(interp, A);
12951 Jim_DecrRefCount(interp, B);
12952 return rc;
12953 intresult:
12954 ExprPush(e, Jim_NewIntObj(interp, wC));
12955 goto done;
12956 doubleresult:
12957 ExprPush(e, Jim_NewDoubleObj(interp, dC));
12958 goto done;
12961 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
12963 int listlen;
12964 int i;
12966 listlen = Jim_ListLength(interp, listObjPtr);
12967 for (i = 0; i < listlen; i++) {
12968 if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
12969 return 1;
12972 return 0;
12975 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e)
12977 Jim_Obj *B = ExprPop(e);
12978 Jim_Obj *A = ExprPop(e);
12980 jim_wide wC;
12982 switch (e->opcode) {
12983 case JIM_EXPROP_STREQ:
12984 case JIM_EXPROP_STRNE:
12985 wC = Jim_StringEqObj(A, B);
12986 if (e->opcode == JIM_EXPROP_STRNE) {
12987 wC = !wC;
12989 break;
12990 case JIM_EXPROP_STRIN:
12991 wC = JimSearchList(interp, B, A);
12992 break;
12993 case JIM_EXPROP_STRNI:
12994 wC = !JimSearchList(interp, B, A);
12995 break;
12996 default:
12997 abort();
12999 ExprPush(e, Jim_NewIntObj(interp, wC));
13001 Jim_DecrRefCount(interp, A);
13002 Jim_DecrRefCount(interp, B);
13004 return JIM_OK;
13007 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
13009 long l;
13010 double d;
13011 int b;
13013 if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
13014 return l != 0;
13016 if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
13017 return d != 0;
13019 if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
13020 return b != 0;
13022 return -1;
13025 static int JimExprOpAndLeft(Jim_Interp *interp, struct JimExprState *e)
13027 Jim_Obj *skip = ExprPop(e);
13028 Jim_Obj *A = ExprPop(e);
13029 int rc = JIM_OK;
13031 switch (ExprBool(interp, A)) {
13032 case 0:
13034 e->skip = JimWideValue(skip);
13035 ExprPush(e, Jim_NewIntObj(interp, 0));
13036 break;
13038 case 1:
13040 break;
13042 case -1:
13044 rc = JIM_ERR;
13046 Jim_DecrRefCount(interp, A);
13047 Jim_DecrRefCount(interp, skip);
13049 return rc;
13052 static int JimExprOpOrLeft(Jim_Interp *interp, struct JimExprState *e)
13054 Jim_Obj *skip = ExprPop(e);
13055 Jim_Obj *A = ExprPop(e);
13056 int rc = JIM_OK;
13058 switch (ExprBool(interp, A)) {
13059 case 0:
13061 break;
13063 case 1:
13065 e->skip = JimWideValue(skip);
13066 ExprPush(e, Jim_NewIntObj(interp, 1));
13067 break;
13069 case -1:
13071 rc = JIM_ERR;
13072 break;
13074 Jim_DecrRefCount(interp, A);
13075 Jim_DecrRefCount(interp, skip);
13077 return rc;
13080 static int JimExprOpAndOrRight(Jim_Interp *interp, struct JimExprState *e)
13082 Jim_Obj *A = ExprPop(e);
13083 int rc = JIM_OK;
13085 switch (ExprBool(interp, A)) {
13086 case 0:
13087 ExprPush(e, Jim_NewIntObj(interp, 0));
13088 break;
13090 case 1:
13091 ExprPush(e, Jim_NewIntObj(interp, 1));
13092 break;
13094 case -1:
13096 rc = JIM_ERR;
13097 break;
13099 Jim_DecrRefCount(interp, A);
13101 return rc;
13104 static int JimExprOpTernaryLeft(Jim_Interp *interp, struct JimExprState *e)
13106 Jim_Obj *skip = ExprPop(e);
13107 Jim_Obj *A = ExprPop(e);
13108 int rc = JIM_OK;
13111 ExprPush(e, A);
13113 switch (ExprBool(interp, A)) {
13114 case 0:
13116 e->skip = JimWideValue(skip);
13118 ExprPush(e, Jim_NewIntObj(interp, 0));
13119 break;
13121 case 1:
13123 break;
13125 case -1:
13127 rc = JIM_ERR;
13128 break;
13130 Jim_DecrRefCount(interp, A);
13131 Jim_DecrRefCount(interp, skip);
13133 return rc;
13136 static int JimExprOpColonLeft(Jim_Interp *interp, struct JimExprState *e)
13138 Jim_Obj *skip = ExprPop(e);
13139 Jim_Obj *B = ExprPop(e);
13140 Jim_Obj *A = ExprPop(e);
13143 if (ExprBool(interp, A)) {
13145 e->skip = JimWideValue(skip);
13147 ExprPush(e, B);
13150 Jim_DecrRefCount(interp, skip);
13151 Jim_DecrRefCount(interp, A);
13152 Jim_DecrRefCount(interp, B);
13153 return JIM_OK;
13156 static int JimExprOpNull(Jim_Interp *interp, struct JimExprState *e)
13158 return JIM_OK;
13161 enum
13163 LAZY_NONE,
13164 LAZY_OP,
13165 LAZY_LEFT,
13166 LAZY_RIGHT,
13167 RIGHT_ASSOC,
13170 #define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
13171 #define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, LAZY_NONE)
13173 static const struct Jim_ExprOperator Jim_ExprOperators[] = {
13174 OPRINIT("*", 110, 2, JimExprOpBin),
13175 OPRINIT("/", 110, 2, JimExprOpBin),
13176 OPRINIT("%", 110, 2, JimExprOpIntBin),
13178 OPRINIT("-", 100, 2, JimExprOpBin),
13179 OPRINIT("+", 100, 2, JimExprOpBin),
13181 OPRINIT("<<", 90, 2, JimExprOpIntBin),
13182 OPRINIT(">>", 90, 2, JimExprOpIntBin),
13184 OPRINIT("<<<", 90, 2, JimExprOpIntBin),
13185 OPRINIT(">>>", 90, 2, JimExprOpIntBin),
13187 OPRINIT("<", 80, 2, JimExprOpBin),
13188 OPRINIT(">", 80, 2, JimExprOpBin),
13189 OPRINIT("<=", 80, 2, JimExprOpBin),
13190 OPRINIT(">=", 80, 2, JimExprOpBin),
13192 OPRINIT("==", 70, 2, JimExprOpBin),
13193 OPRINIT("!=", 70, 2, JimExprOpBin),
13195 OPRINIT("&", 50, 2, JimExprOpIntBin),
13196 OPRINIT("^", 49, 2, JimExprOpIntBin),
13197 OPRINIT("|", 48, 2, JimExprOpIntBin),
13199 OPRINIT_ATTR("&&", 10, 2, NULL, LAZY_OP),
13200 OPRINIT_ATTR(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT),
13201 OPRINIT_ATTR(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT),
13203 OPRINIT_ATTR("||", 9, 2, NULL, LAZY_OP),
13204 OPRINIT_ATTR(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT),
13205 OPRINIT_ATTR(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT),
13207 OPRINIT_ATTR("?", 5, 2, JimExprOpNull, LAZY_OP),
13208 OPRINIT_ATTR(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT),
13209 OPRINIT_ATTR(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
13211 OPRINIT_ATTR(":", 5, 2, JimExprOpNull, LAZY_OP),
13212 OPRINIT_ATTR(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT),
13213 OPRINIT_ATTR(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
13216 OPRINIT_ATTR("**", 120, 2, JimExprOpBin, RIGHT_ASSOC),
13218 OPRINIT("eq", 60, 2, JimExprOpStrBin),
13219 OPRINIT("ne", 60, 2, JimExprOpStrBin),
13221 OPRINIT("in", 55, 2, JimExprOpStrBin),
13222 OPRINIT("ni", 55, 2, JimExprOpStrBin),
13224 OPRINIT("!", 150, 1, JimExprOpNumUnary),
13225 OPRINIT("~", 150, 1, JimExprOpIntUnary),
13226 OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
13227 OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
13231 OPRINIT("int", 200, 1, JimExprOpNumUnary),
13232 OPRINIT("wide", 200, 1, JimExprOpNumUnary),
13233 OPRINIT("abs", 200, 1, JimExprOpNumUnary),
13234 OPRINIT("double", 200, 1, JimExprOpNumUnary),
13235 OPRINIT("round", 200, 1, JimExprOpNumUnary),
13236 OPRINIT("rand", 200, 0, JimExprOpNone),
13237 OPRINIT("srand", 200, 1, JimExprOpIntUnary),
13239 #ifdef JIM_MATH_FUNCTIONS
13240 OPRINIT("sin", 200, 1, JimExprOpDoubleUnary),
13241 OPRINIT("cos", 200, 1, JimExprOpDoubleUnary),
13242 OPRINIT("tan", 200, 1, JimExprOpDoubleUnary),
13243 OPRINIT("asin", 200, 1, JimExprOpDoubleUnary),
13244 OPRINIT("acos", 200, 1, JimExprOpDoubleUnary),
13245 OPRINIT("atan", 200, 1, JimExprOpDoubleUnary),
13246 OPRINIT("atan2", 200, 2, JimExprOpBin),
13247 OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary),
13248 OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary),
13249 OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary),
13250 OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary),
13251 OPRINIT("floor", 200, 1, JimExprOpDoubleUnary),
13252 OPRINIT("exp", 200, 1, JimExprOpDoubleUnary),
13253 OPRINIT("log", 200, 1, JimExprOpDoubleUnary),
13254 OPRINIT("log10", 200, 1, JimExprOpDoubleUnary),
13255 OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary),
13256 OPRINIT("pow", 200, 2, JimExprOpBin),
13257 OPRINIT("hypot", 200, 2, JimExprOpBin),
13258 OPRINIT("fmod", 200, 2, JimExprOpBin),
13259 #endif
13261 #undef OPRINIT
13262 #undef OPRINIT_LAZY
13264 #define JIM_EXPR_OPERATORS_NUM \
13265 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
13267 static int JimParseExpression(struct JimParserCtx *pc)
13270 while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
13271 if (*pc->p == '\n') {
13272 pc->linenr++;
13274 pc->p++;
13275 pc->len--;
13279 pc->tline = pc->linenr;
13280 pc->tstart = pc->p;
13282 if (pc->len == 0) {
13283 pc->tend = pc->p;
13284 pc->tt = JIM_TT_EOL;
13285 pc->eof = 1;
13286 return JIM_OK;
13288 switch (*(pc->p)) {
13289 case '(':
13290 pc->tt = JIM_TT_SUBEXPR_START;
13291 goto singlechar;
13292 case ')':
13293 pc->tt = JIM_TT_SUBEXPR_END;
13294 goto singlechar;
13295 case ',':
13296 pc->tt = JIM_TT_SUBEXPR_COMMA;
13297 singlechar:
13298 pc->tend = pc->p;
13299 pc->p++;
13300 pc->len--;
13301 break;
13302 case '[':
13303 return JimParseCmd(pc);
13304 case '$':
13305 if (JimParseVar(pc) == JIM_ERR)
13306 return JimParseExprOperator(pc);
13307 else {
13309 if (pc->tt == JIM_TT_EXPRSUGAR) {
13310 return JIM_ERR;
13312 return JIM_OK;
13314 break;
13315 case '0':
13316 case '1':
13317 case '2':
13318 case '3':
13319 case '4':
13320 case '5':
13321 case '6':
13322 case '7':
13323 case '8':
13324 case '9':
13325 case '.':
13326 return JimParseExprNumber(pc);
13327 case '"':
13328 return JimParseQuote(pc);
13329 case '{':
13330 return JimParseBrace(pc);
13332 case 'N':
13333 case 'I':
13334 case 'n':
13335 case 'i':
13336 if (JimParseExprIrrational(pc) == JIM_ERR)
13337 if (JimParseExprBoolean(pc) == JIM_ERR)
13338 return JimParseExprOperator(pc);
13339 break;
13340 case 't':
13341 case 'f':
13342 case 'o':
13343 case 'y':
13344 if (JimParseExprBoolean(pc) == JIM_ERR)
13345 return JimParseExprOperator(pc);
13346 break;
13347 default:
13348 return JimParseExprOperator(pc);
13349 break;
13351 return JIM_OK;
13354 static int JimParseExprNumber(struct JimParserCtx *pc)
13356 char *end;
13359 pc->tt = JIM_TT_EXPR_INT;
13361 jim_strtoull(pc->p, (char **)&pc->p);
13363 if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
13364 if (strtod(pc->tstart, &end)) { }
13365 if (end == pc->tstart)
13366 return JIM_ERR;
13367 if (end > pc->p) {
13369 pc->tt = JIM_TT_EXPR_DOUBLE;
13370 pc->p = end;
13373 pc->tend = pc->p - 1;
13374 pc->len -= (pc->p - pc->tstart);
13375 return JIM_OK;
13378 static int JimParseExprIrrational(struct JimParserCtx *pc)
13380 const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
13381 int i;
13383 for (i = 0; irrationals[i]; i++) {
13384 const char *irr = irrationals[i];
13386 if (strncmp(irr, pc->p, 3) == 0) {
13387 pc->p += 3;
13388 pc->len -= 3;
13389 pc->tend = pc->p - 1;
13390 pc->tt = JIM_TT_EXPR_DOUBLE;
13391 return JIM_OK;
13394 return JIM_ERR;
13397 static int JimParseExprBoolean(struct JimParserCtx *pc)
13399 const char *booleans[] = { "false", "no", "off", "true", "yes", "on", NULL };
13400 const int lengths[] = { 5, 2, 3, 4, 3, 2, 0 };
13401 int i;
13403 for (i = 0; booleans[i]; i++) {
13404 const char *boolean = booleans[i];
13405 int length = lengths[i];
13407 if (strncmp(boolean, pc->p, length) == 0) {
13408 pc->p += length;
13409 pc->len -= length;
13410 pc->tend = pc->p - 1;
13411 pc->tt = JIM_TT_EXPR_BOOLEAN;
13412 return JIM_OK;
13415 return JIM_ERR;
13418 static int JimParseExprOperator(struct JimParserCtx *pc)
13420 int i;
13421 int bestIdx = -1, bestLen = 0;
13424 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
13425 const char * const opname = Jim_ExprOperators[i].name;
13426 const int oplen = Jim_ExprOperators[i].namelen;
13428 if (opname == NULL || opname[0] != pc->p[0]) {
13429 continue;
13432 if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) {
13433 bestIdx = i + JIM_TT_EXPR_OP;
13434 bestLen = oplen;
13437 if (bestIdx == -1) {
13438 return JIM_ERR;
13442 if (bestIdx >= JIM_EXPROP_FUNC_FIRST) {
13443 const char *p = pc->p + bestLen;
13444 int len = pc->len - bestLen;
13446 while (len && isspace(UCHAR(*p))) {
13447 len--;
13448 p++;
13450 if (*p != '(') {
13451 return JIM_ERR;
13454 pc->tend = pc->p + bestLen - 1;
13455 pc->p += bestLen;
13456 pc->len -= bestLen;
13458 pc->tt = bestIdx;
13459 return JIM_OK;
13462 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
13464 static Jim_ExprOperator dummy_op;
13465 if (opcode < JIM_TT_EXPR_OP) {
13466 return &dummy_op;
13468 return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
13471 const char *jim_tt_name(int type)
13473 static const char * const tt_names[JIM_TT_EXPR_OP] =
13474 { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT",
13475 "DBL", "BOO", "$()" };
13476 if (type < JIM_TT_EXPR_OP) {
13477 return tt_names[type];
13479 else if (type == JIM_EXPROP_UNARYMINUS) {
13480 return "-VE";
13482 else if (type == JIM_EXPROP_UNARYPLUS) {
13483 return "+VE";
13485 else {
13486 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(type);
13487 static char buf[20];
13489 if (op->name) {
13490 return op->name;
13492 sprintf(buf, "(%d)", type);
13493 return buf;
13497 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
13498 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
13499 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13501 static const Jim_ObjType exprObjType = {
13502 "expression",
13503 FreeExprInternalRep,
13504 DupExprInternalRep,
13505 NULL,
13506 JIM_TYPE_REFERENCES,
13510 typedef struct ExprByteCode
13512 ScriptToken *token;
13513 int len;
13514 int inUse;
13515 } ExprByteCode;
13517 static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr)
13519 int i;
13521 for (i = 0; i < expr->len; i++) {
13522 Jim_DecrRefCount(interp, expr->token[i].objPtr);
13524 Jim_Free(expr->token);
13525 Jim_Free(expr);
13528 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
13530 ExprByteCode *expr = (void *)objPtr->internalRep.ptr;
13532 if (expr) {
13533 if (--expr->inUse != 0) {
13534 return;
13537 ExprFreeByteCode(interp, expr);
13541 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
13543 JIM_NOTUSED(interp);
13544 JIM_NOTUSED(srcPtr);
13547 dupPtr->typePtr = NULL;
13550 static int ExprCheckCorrectness(Jim_Interp *interp, Jim_Obj *exprObjPtr, ExprByteCode * expr)
13552 int i;
13553 int stacklen = 0;
13554 int ternary = 0;
13555 int lasttt = JIM_TT_NONE;
13556 const char *errmsg;
13558 for (i = 0; i < expr->len; i++) {
13559 ScriptToken *t = &expr->token[i];
13560 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13561 lasttt = t->type;
13563 stacklen -= op->arity;
13565 if (stacklen < 0) {
13566 break;
13568 if (t->type == JIM_EXPROP_TERNARY || t->type == JIM_EXPROP_TERNARY_LEFT) {
13569 ternary++;
13571 else if (t->type == JIM_EXPROP_COLON || t->type == JIM_EXPROP_COLON_LEFT) {
13572 ternary--;
13576 stacklen++;
13578 if (stacklen == 1 && ternary == 0) {
13579 return JIM_OK;
13582 if (stacklen <= 0) {
13584 if (lasttt >= JIM_EXPROP_FUNC_FIRST) {
13585 errmsg = "too few arguments for math function";
13586 Jim_SetResultString(interp, "too few arguments for math function", -1);
13587 } else {
13588 errmsg = "premature end of expression";
13591 else if (stacklen > 1) {
13592 if (lasttt >= JIM_EXPROP_FUNC_FIRST) {
13593 errmsg = "too many arguments for math function";
13594 } else {
13595 errmsg = "extra tokens at end of expression";
13598 else {
13599 errmsg = "invalid ternary expression";
13601 Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": %s", exprObjPtr, errmsg);
13602 return JIM_ERR;
13605 static int ExprAddLazyOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
13607 int i;
13609 int leftindex, arity, offset;
13612 leftindex = expr->len - 1;
13614 arity = 1;
13615 while (arity) {
13616 ScriptToken *tt = &expr->token[leftindex];
13618 if (tt->type >= JIM_TT_EXPR_OP) {
13619 arity += JimExprOperatorInfoByOpcode(tt->type)->arity;
13621 arity--;
13622 if (--leftindex < 0) {
13623 return JIM_ERR;
13626 leftindex++;
13629 memmove(&expr->token[leftindex + 2], &expr->token[leftindex],
13630 sizeof(*expr->token) * (expr->len - leftindex));
13631 expr->len += 2;
13632 offset = (expr->len - leftindex) - 1;
13634 expr->token[leftindex + 1].type = t->type + 1;
13635 expr->token[leftindex + 1].objPtr = interp->emptyObj;
13637 expr->token[leftindex].type = JIM_TT_EXPR_INT;
13638 expr->token[leftindex].objPtr = Jim_NewIntObj(interp, offset);
13641 expr->token[expr->len].objPtr = interp->emptyObj;
13642 expr->token[expr->len].type = t->type + 2;
13643 expr->len++;
13646 for (i = leftindex - 1; i > 0; i--) {
13647 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(expr->token[i].type);
13648 if (op->lazy == LAZY_LEFT) {
13649 if (JimWideValue(expr->token[i - 1].objPtr) + i - 1 >= leftindex) {
13650 JimWideValue(expr->token[i - 1].objPtr) += 2;
13654 return JIM_OK;
13657 static int ExprAddOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
13659 struct ScriptToken *token = &expr->token[expr->len];
13660 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13662 if (op->lazy == LAZY_OP) {
13663 if (ExprAddLazyOperator(interp, expr, t) != JIM_OK) {
13664 Jim_SetResultFormatted(interp, "Expression has bad operands to %s", op->name);
13665 return JIM_ERR;
13668 else {
13669 token->objPtr = interp->emptyObj;
13670 token->type = t->type;
13671 expr->len++;
13673 return JIM_OK;
13676 static int ExprTernaryGetColonLeftIndex(ExprByteCode *expr, int right_index)
13678 int ternary_count = 1;
13680 right_index--;
13682 while (right_index > 1) {
13683 if (expr->token[right_index].type == JIM_EXPROP_TERNARY_LEFT) {
13684 ternary_count--;
13686 else if (expr->token[right_index].type == JIM_EXPROP_COLON_RIGHT) {
13687 ternary_count++;
13689 else if (expr->token[right_index].type == JIM_EXPROP_COLON_LEFT && ternary_count == 1) {
13690 return right_index;
13692 right_index--;
13696 return -1;
13699 static int ExprTernaryGetMoveIndices(ExprByteCode *expr, int right_index, int *prev_right_index, int *prev_left_index)
13701 int i = right_index - 1;
13702 int ternary_count = 1;
13704 while (i > 1) {
13705 if (expr->token[i].type == JIM_EXPROP_TERNARY_LEFT) {
13706 if (--ternary_count == 0 && expr->token[i - 2].type == JIM_EXPROP_COLON_RIGHT) {
13707 *prev_right_index = i - 2;
13708 *prev_left_index = ExprTernaryGetColonLeftIndex(expr, *prev_right_index);
13709 return 1;
13712 else if (expr->token[i].type == JIM_EXPROP_COLON_RIGHT) {
13713 if (ternary_count == 0) {
13714 return 0;
13716 ternary_count++;
13718 i--;
13720 return 0;
13723 static void ExprTernaryReorderExpression(Jim_Interp *interp, ExprByteCode *expr)
13725 int i;
13727 for (i = expr->len - 1; i > 1; i--) {
13728 int prev_right_index;
13729 int prev_left_index;
13730 int j;
13731 ScriptToken tmp;
13733 if (expr->token[i].type != JIM_EXPROP_COLON_RIGHT) {
13734 continue;
13738 if (ExprTernaryGetMoveIndices(expr, i, &prev_right_index, &prev_left_index) == 0) {
13739 continue;
13742 tmp = expr->token[prev_right_index];
13743 for (j = prev_right_index; j < i; j++) {
13744 expr->token[j] = expr->token[j + 1];
13746 expr->token[i] = tmp;
13748 JimWideValue(expr->token[prev_left_index-1].objPtr) += (i - prev_right_index);
13751 i++;
13755 static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
13757 Jim_Stack stack;
13758 ExprByteCode *expr;
13759 int ok = 1;
13760 int i;
13761 int prevtt = JIM_TT_NONE;
13762 int have_ternary = 0;
13765 int count = tokenlist->count - 1;
13767 expr = Jim_Alloc(sizeof(*expr));
13768 expr->inUse = 1;
13769 expr->len = 0;
13771 Jim_InitStack(&stack);
13773 for (i = 0; i < tokenlist->count; i++) {
13774 ParseToken *t = &tokenlist->list[i];
13775 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13777 if (op->lazy == LAZY_OP) {
13778 count += 2;
13780 if (t->type == JIM_EXPROP_TERNARY) {
13781 have_ternary = 1;
13786 expr->token = Jim_Alloc(sizeof(ScriptToken) * count);
13788 for (i = 0; i < tokenlist->count && ok; i++) {
13789 ParseToken *t = &tokenlist->list[i];
13792 struct ScriptToken *token = &expr->token[expr->len];
13794 if (t->type == JIM_TT_EOL) {
13795 break;
13798 if (TOKEN_IS_EXPR_OP(t->type)) {
13799 const struct Jim_ExprOperator *op;
13800 ParseToken *tt;
13803 if (prevtt == JIM_TT_NONE || prevtt == JIM_TT_SUBEXPR_START || prevtt == JIM_TT_SUBEXPR_COMMA || prevtt >= JIM_TT_EXPR_OP) {
13804 if (t->type == JIM_EXPROP_SUB) {
13805 t->type = JIM_EXPROP_UNARYMINUS;
13807 else if (t->type == JIM_EXPROP_ADD) {
13808 t->type = JIM_EXPROP_UNARYPLUS;
13812 op = JimExprOperatorInfoByOpcode(t->type);
13815 while ((tt = Jim_StackPeek(&stack)) != NULL) {
13816 const struct Jim_ExprOperator *tt_op =
13817 JimExprOperatorInfoByOpcode(tt->type);
13820 if (op->arity != 1 && tt_op->precedence >= op->precedence) {
13822 if (tt_op->precedence == op->precedence && tt_op->lazy == RIGHT_ASSOC) {
13823 break;
13825 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13826 ok = 0;
13827 goto err;
13829 Jim_StackPop(&stack);
13831 else {
13832 break;
13835 Jim_StackPush(&stack, t);
13837 else if (t->type == JIM_TT_SUBEXPR_START) {
13838 Jim_StackPush(&stack, t);
13840 else if (t->type == JIM_TT_SUBEXPR_END || t->type == JIM_TT_SUBEXPR_COMMA) {
13842 ok = 0;
13843 while (Jim_StackLen(&stack)) {
13844 ParseToken *tt = Jim_StackPop(&stack);
13846 if (tt->type == JIM_TT_SUBEXPR_START || tt->type == JIM_TT_SUBEXPR_COMMA) {
13847 if (t->type == JIM_TT_SUBEXPR_COMMA) {
13849 Jim_StackPush(&stack, tt);
13851 ok = 1;
13852 break;
13854 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13855 goto err;
13858 if (!ok) {
13859 Jim_SetResultFormatted(interp, "Unexpected close parenthesis in expression: \"%#s\"", exprObjPtr);
13860 goto err;
13863 else {
13864 Jim_Obj *objPtr = NULL;
13867 token->type = t->type;
13870 if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
13871 Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", exprObjPtr);
13872 ok = 0;
13873 goto err;
13877 if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
13878 char *endptr;
13879 if (t->type == JIM_TT_EXPR_INT) {
13880 objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
13882 else {
13883 objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
13885 if (endptr != t->token + t->len) {
13887 Jim_FreeNewObj(interp, objPtr);
13888 objPtr = NULL;
13892 if (objPtr) {
13893 token->objPtr = objPtr;
13895 else {
13897 token->objPtr = Jim_NewStringObj(interp, t->token, t->len);
13898 if (t->type == JIM_TT_CMD) {
13900 JimSetSourceInfo(interp, token->objPtr, fileNameObj, t->line);
13903 expr->len++;
13905 prevtt = t->type;
13909 while (Jim_StackLen(&stack)) {
13910 ParseToken *tt = Jim_StackPop(&stack);
13912 if (tt->type == JIM_TT_SUBEXPR_START) {
13913 ok = 0;
13914 Jim_SetResultString(interp, "Missing close parenthesis", -1);
13915 goto err;
13917 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13918 ok = 0;
13919 goto err;
13923 if (have_ternary) {
13924 ExprTernaryReorderExpression(interp, expr);
13927 err:
13929 Jim_FreeStack(&stack);
13931 for (i = 0; i < expr->len; i++) {
13932 Jim_IncrRefCount(expr->token[i].objPtr);
13935 if (!ok) {
13936 ExprFreeByteCode(interp, expr);
13937 return NULL;
13940 return expr;
13944 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
13946 int exprTextLen;
13947 const char *exprText;
13948 struct JimParserCtx parser;
13949 struct ExprByteCode *expr;
13950 ParseTokenList tokenlist;
13951 int line;
13952 Jim_Obj *fileNameObj;
13953 int rc = JIM_ERR;
13956 if (objPtr->typePtr == &sourceObjType) {
13957 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
13958 line = objPtr->internalRep.sourceValue.lineNumber;
13960 else {
13961 fileNameObj = interp->emptyObj;
13962 line = 1;
13964 Jim_IncrRefCount(fileNameObj);
13966 exprText = Jim_GetString(objPtr, &exprTextLen);
13969 ScriptTokenListInit(&tokenlist);
13971 JimParserInit(&parser, exprText, exprTextLen, line);
13972 while (!parser.eof) {
13973 if (JimParseExpression(&parser) != JIM_OK) {
13974 ScriptTokenListFree(&tokenlist);
13975 Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
13976 expr = NULL;
13977 goto err;
13980 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
13981 parser.tline);
13984 #ifdef DEBUG_SHOW_EXPR_TOKENS
13986 int i;
13987 printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
13988 for (i = 0; i < tokenlist.count; i++) {
13989 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
13990 tokenlist.list[i].len, tokenlist.list[i].token);
13993 #endif
13995 if (JimParseCheckMissing(interp, parser.missing.ch) == JIM_ERR) {
13996 ScriptTokenListFree(&tokenlist);
13997 Jim_DecrRefCount(interp, fileNameObj);
13998 return JIM_ERR;
14002 expr = ExprCreateByteCode(interp, &tokenlist, objPtr, fileNameObj);
14005 ScriptTokenListFree(&tokenlist);
14007 if (!expr) {
14008 goto err;
14011 #ifdef DEBUG_SHOW_EXPR
14013 int i;
14015 printf("==== Expr ====\n");
14016 for (i = 0; i < expr->len; i++) {
14017 ScriptToken *t = &expr->token[i];
14019 printf("[%2d] %s '%s'\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
14022 #endif
14025 if (ExprCheckCorrectness(interp, objPtr, expr) != JIM_OK) {
14027 ExprFreeByteCode(interp, expr);
14028 expr = NULL;
14029 goto err;
14032 rc = JIM_OK;
14034 err:
14036 Jim_DecrRefCount(interp, fileNameObj);
14037 Jim_FreeIntRep(interp, objPtr);
14038 Jim_SetIntRepPtr(objPtr, expr);
14039 objPtr->typePtr = &exprObjType;
14040 return rc;
14043 static ExprByteCode *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
14045 if (objPtr->typePtr != &exprObjType) {
14046 if (SetExprFromAny(interp, objPtr) != JIM_OK) {
14047 return NULL;
14050 return (ExprByteCode *) Jim_GetIntRepPtr(objPtr);
14053 #ifdef JIM_OPTIMIZATION
14054 static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, const ScriptToken *token)
14056 if (token->type == JIM_TT_EXPR_INT)
14057 return token->objPtr;
14058 else if (token->type == JIM_TT_VAR)
14059 return Jim_GetVariable(interp, token->objPtr, JIM_NONE);
14060 else if (token->type == JIM_TT_DICTSUGAR)
14061 return JimExpandDictSugar(interp, token->objPtr);
14062 else
14063 return NULL;
14065 #endif
14067 #define JIM_EE_STATICSTACK_LEN 10
14069 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr)
14071 ExprByteCode *expr;
14072 Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN];
14073 int i;
14074 int retcode = JIM_OK;
14075 struct JimExprState e;
14077 expr = JimGetExpression(interp, exprObjPtr);
14078 if (!expr) {
14079 return JIM_ERR;
14082 #ifdef JIM_OPTIMIZATION
14084 Jim_Obj *objPtr;
14087 switch (expr->len) {
14088 case 1:
14089 objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
14090 if (objPtr) {
14091 Jim_IncrRefCount(objPtr);
14092 *exprResultPtrPtr = objPtr;
14093 return JIM_OK;
14095 break;
14097 case 2:
14098 if (expr->token[1].type == JIM_EXPROP_NOT) {
14099 objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
14101 if (objPtr && JimIsWide(objPtr)) {
14102 *exprResultPtrPtr = JimWideValue(objPtr) ? interp->falseObj : interp->trueObj;
14103 Jim_IncrRefCount(*exprResultPtrPtr);
14104 return JIM_OK;
14107 break;
14109 case 3:
14110 objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
14111 if (objPtr && JimIsWide(objPtr)) {
14112 Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, &expr->token[1]);
14113 if (objPtr2 && JimIsWide(objPtr2)) {
14114 jim_wide wideValueA = JimWideValue(objPtr);
14115 jim_wide wideValueB = JimWideValue(objPtr2);
14116 int cmpRes;
14117 switch (expr->token[2].type) {
14118 case JIM_EXPROP_LT:
14119 cmpRes = wideValueA < wideValueB;
14120 break;
14121 case JIM_EXPROP_LTE:
14122 cmpRes = wideValueA <= wideValueB;
14123 break;
14124 case JIM_EXPROP_GT:
14125 cmpRes = wideValueA > wideValueB;
14126 break;
14127 case JIM_EXPROP_GTE:
14128 cmpRes = wideValueA >= wideValueB;
14129 break;
14130 case JIM_EXPROP_NUMEQ:
14131 cmpRes = wideValueA == wideValueB;
14132 break;
14133 case JIM_EXPROP_NUMNE:
14134 cmpRes = wideValueA != wideValueB;
14135 break;
14136 default:
14137 goto noopt;
14139 *exprResultPtrPtr = cmpRes ? interp->trueObj : interp->falseObj;
14140 Jim_IncrRefCount(*exprResultPtrPtr);
14141 return JIM_OK;
14144 break;
14147 noopt:
14148 #endif
14150 expr->inUse++;
14154 if (expr->len > JIM_EE_STATICSTACK_LEN)
14155 e.stack = Jim_Alloc(sizeof(Jim_Obj *) * expr->len);
14156 else
14157 e.stack = staticStack;
14159 e.stacklen = 0;
14162 for (i = 0; i < expr->len && retcode == JIM_OK; i++) {
14163 Jim_Obj *objPtr;
14165 switch (expr->token[i].type) {
14166 case JIM_TT_EXPR_INT:
14167 case JIM_TT_EXPR_DOUBLE:
14168 case JIM_TT_EXPR_BOOLEAN:
14169 case JIM_TT_STR:
14170 ExprPush(&e, expr->token[i].objPtr);
14171 break;
14173 case JIM_TT_VAR:
14174 objPtr = Jim_GetVariable(interp, expr->token[i].objPtr, JIM_ERRMSG);
14175 if (objPtr) {
14176 ExprPush(&e, objPtr);
14178 else {
14179 retcode = JIM_ERR;
14181 break;
14183 case JIM_TT_DICTSUGAR:
14184 objPtr = JimExpandDictSugar(interp, expr->token[i].objPtr);
14185 if (objPtr) {
14186 ExprPush(&e, objPtr);
14188 else {
14189 retcode = JIM_ERR;
14191 break;
14193 case JIM_TT_ESC:
14194 retcode = Jim_SubstObj(interp, expr->token[i].objPtr, &objPtr, JIM_NONE);
14195 if (retcode == JIM_OK) {
14196 ExprPush(&e, objPtr);
14198 break;
14200 case JIM_TT_CMD:
14201 retcode = Jim_EvalObj(interp, expr->token[i].objPtr);
14202 if (retcode == JIM_OK) {
14203 ExprPush(&e, Jim_GetResult(interp));
14205 break;
14207 default:{
14209 e.skip = 0;
14210 e.opcode = expr->token[i].type;
14212 retcode = JimExprOperatorInfoByOpcode(e.opcode)->funcop(interp, &e);
14214 i += e.skip;
14215 continue;
14220 expr->inUse--;
14222 if (retcode == JIM_OK) {
14223 *exprResultPtrPtr = ExprPop(&e);
14225 else {
14226 for (i = 0; i < e.stacklen; i++) {
14227 Jim_DecrRefCount(interp, e.stack[i]);
14230 if (e.stack != staticStack) {
14231 Jim_Free(e.stack);
14233 return retcode;
14236 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
14238 int retcode;
14239 jim_wide wideValue;
14240 double doubleValue;
14241 int booleanValue;
14242 Jim_Obj *exprResultPtr;
14244 retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
14245 if (retcode != JIM_OK)
14246 return retcode;
14248 if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
14249 if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK) {
14250 if (Jim_GetBoolean(interp, exprResultPtr, &booleanValue) != JIM_OK) {
14251 Jim_DecrRefCount(interp, exprResultPtr);
14252 return JIM_ERR;
14253 } else {
14254 Jim_DecrRefCount(interp, exprResultPtr);
14255 *boolPtr = booleanValue;
14256 return JIM_OK;
14259 else {
14260 Jim_DecrRefCount(interp, exprResultPtr);
14261 *boolPtr = doubleValue != 0;
14262 return JIM_OK;
14265 *boolPtr = wideValue != 0;
14267 Jim_DecrRefCount(interp, exprResultPtr);
14268 return JIM_OK;
14274 typedef struct ScanFmtPartDescr
14276 char *arg;
14277 char *prefix;
14278 size_t width;
14279 int pos;
14280 char type;
14281 char modifier;
14282 } ScanFmtPartDescr;
14285 typedef struct ScanFmtStringObj
14287 jim_wide size;
14288 char *stringRep;
14289 size_t count;
14290 size_t convCount;
14291 size_t maxPos;
14292 const char *error;
14293 char *scratch;
14294 ScanFmtPartDescr descr[1];
14295 } ScanFmtStringObj;
14298 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
14299 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
14300 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
14302 static const Jim_ObjType scanFmtStringObjType = {
14303 "scanformatstring",
14304 FreeScanFmtInternalRep,
14305 DupScanFmtInternalRep,
14306 UpdateStringOfScanFmt,
14307 JIM_TYPE_NONE,
14310 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
14312 JIM_NOTUSED(interp);
14313 Jim_Free((char *)objPtr->internalRep.ptr);
14314 objPtr->internalRep.ptr = 0;
14317 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
14319 size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
14320 ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
14322 JIM_NOTUSED(interp);
14323 memcpy(newVec, srcPtr->internalRep.ptr, size);
14324 dupPtr->internalRep.ptr = newVec;
14325 dupPtr->typePtr = &scanFmtStringObjType;
14328 static void UpdateStringOfScanFmt(Jim_Obj *objPtr)
14330 JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep);
14334 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
14336 ScanFmtStringObj *fmtObj;
14337 char *buffer;
14338 int maxCount, i, approxSize, lastPos = -1;
14339 const char *fmt = objPtr->bytes;
14340 int maxFmtLen = objPtr->length;
14341 const char *fmtEnd = fmt + maxFmtLen;
14342 int curr;
14344 Jim_FreeIntRep(interp, objPtr);
14346 for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
14347 if (fmt[i] == '%')
14348 ++maxCount;
14350 approxSize = sizeof(ScanFmtStringObj)
14351 +(maxCount + 1) * sizeof(ScanFmtPartDescr)
14352 +maxFmtLen * sizeof(char) + 3 + 1
14353 + maxFmtLen * sizeof(char) + 1
14354 + maxFmtLen * sizeof(char)
14355 +(maxCount + 1) * sizeof(char)
14357 fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
14358 memset(fmtObj, 0, approxSize);
14359 fmtObj->size = approxSize;
14360 fmtObj->maxPos = 0;
14361 fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
14362 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
14363 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
14364 buffer = fmtObj->stringRep + maxFmtLen + 1;
14365 objPtr->internalRep.ptr = fmtObj;
14366 objPtr->typePtr = &scanFmtStringObjType;
14367 for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
14368 int width = 0, skip;
14369 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
14371 fmtObj->count++;
14372 descr->width = 0;
14374 if (*fmt != '%' || fmt[1] == '%') {
14375 descr->type = 0;
14376 descr->prefix = &buffer[i];
14377 for (; fmt < fmtEnd; ++fmt) {
14378 if (*fmt == '%') {
14379 if (fmt[1] != '%')
14380 break;
14381 ++fmt;
14383 buffer[i++] = *fmt;
14385 buffer[i++] = 0;
14388 ++fmt;
14390 if (fmt >= fmtEnd)
14391 goto done;
14392 descr->pos = 0;
14393 if (*fmt == '*') {
14394 descr->pos = -1;
14395 ++fmt;
14397 else
14398 fmtObj->convCount++;
14400 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14401 fmt += skip;
14403 if (descr->pos != -1 && *fmt == '$') {
14404 int prev;
14406 ++fmt;
14407 descr->pos = width;
14408 width = 0;
14410 if ((lastPos == 0 && descr->pos > 0)
14411 || (lastPos > 0 && descr->pos == 0)) {
14412 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
14413 return JIM_ERR;
14416 for (prev = 0; prev < curr; ++prev) {
14417 if (fmtObj->descr[prev].pos == -1)
14418 continue;
14419 if (fmtObj->descr[prev].pos == descr->pos) {
14420 fmtObj->error =
14421 "variable is assigned by multiple \"%n$\" conversion specifiers";
14422 return JIM_ERR;
14426 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14427 descr->width = width;
14428 fmt += skip;
14430 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
14431 fmtObj->maxPos = descr->pos;
14433 else {
14435 descr->width = width;
14439 if (lastPos == -1)
14440 lastPos = descr->pos;
14442 if (*fmt == '[') {
14443 int swapped = 1, beg = i, end, j;
14445 descr->type = '[';
14446 descr->arg = &buffer[i];
14447 ++fmt;
14448 if (*fmt == '^')
14449 buffer[i++] = *fmt++;
14450 if (*fmt == ']')
14451 buffer[i++] = *fmt++;
14452 while (*fmt && *fmt != ']')
14453 buffer[i++] = *fmt++;
14454 if (*fmt != ']') {
14455 fmtObj->error = "unmatched [ in format string";
14456 return JIM_ERR;
14458 end = i;
14459 buffer[i++] = 0;
14461 while (swapped) {
14462 swapped = 0;
14463 for (j = beg + 1; j < end - 1; ++j) {
14464 if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
14465 char tmp = buffer[j - 1];
14467 buffer[j - 1] = buffer[j + 1];
14468 buffer[j + 1] = tmp;
14469 swapped = 1;
14474 else {
14476 if (strchr("hlL", *fmt) != 0)
14477 descr->modifier = tolower((int)*fmt++);
14479 descr->type = *fmt;
14480 if (strchr("efgcsndoxui", *fmt) == 0) {
14481 fmtObj->error = "bad scan conversion character";
14482 return JIM_ERR;
14484 else if (*fmt == 'c' && descr->width != 0) {
14485 fmtObj->error = "field width may not be specified in %c " "conversion";
14486 return JIM_ERR;
14488 else if (*fmt == 'u' && descr->modifier == 'l') {
14489 fmtObj->error = "unsigned wide not supported";
14490 return JIM_ERR;
14493 curr++;
14495 done:
14496 return JIM_OK;
14501 #define FormatGetCnvCount(_fo_) \
14502 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
14503 #define FormatGetMaxPos(_fo_) \
14504 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
14505 #define FormatGetError(_fo_) \
14506 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
14508 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
14510 char *buffer = Jim_StrDup(str);
14511 char *p = buffer;
14513 while (*str) {
14514 int c;
14515 int n;
14517 if (!sdescr && isspace(UCHAR(*str)))
14518 break;
14520 n = utf8_tounicode(str, &c);
14521 if (sdescr && !JimCharsetMatch(sdescr, c, JIM_CHARSET_SCAN))
14522 break;
14523 while (n--)
14524 *p++ = *str++;
14526 *p = 0;
14527 return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
14531 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int strLen,
14532 ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
14534 const char *tok;
14535 const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
14536 size_t scanned = 0;
14537 size_t anchor = pos;
14538 int i;
14539 Jim_Obj *tmpObj = NULL;
14542 *valObjPtr = 0;
14543 if (descr->prefix) {
14544 for (i = 0; pos < strLen && descr->prefix[i]; ++i) {
14546 if (isspace(UCHAR(descr->prefix[i])))
14547 while (pos < strLen && isspace(UCHAR(str[pos])))
14548 ++pos;
14549 else if (descr->prefix[i] != str[pos])
14550 break;
14551 else
14552 ++pos;
14554 if (pos >= strLen) {
14555 return -1;
14557 else if (descr->prefix[i] != 0)
14558 return 0;
14561 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
14562 while (isspace(UCHAR(str[pos])))
14563 ++pos;
14565 scanned = pos - anchor;
14568 if (descr->type == 'n') {
14570 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
14572 else if (pos >= strLen) {
14574 return -1;
14576 else if (descr->type == 'c') {
14577 int c;
14578 scanned += utf8_tounicode(&str[pos], &c);
14579 *valObjPtr = Jim_NewIntObj(interp, c);
14580 return scanned;
14582 else {
14584 if (descr->width > 0) {
14585 size_t sLen = utf8_strlen(&str[pos], strLen - pos);
14586 size_t tLen = descr->width > sLen ? sLen : descr->width;
14588 tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
14589 tok = tmpObj->bytes;
14591 else {
14593 tok = &str[pos];
14595 switch (descr->type) {
14596 case 'd':
14597 case 'o':
14598 case 'x':
14599 case 'u':
14600 case 'i':{
14601 char *endp;
14602 jim_wide w;
14604 int base = descr->type == 'o' ? 8
14605 : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
14608 if (base == 0) {
14609 w = jim_strtoull(tok, &endp);
14611 else {
14612 w = strtoull(tok, &endp, base);
14615 if (endp != tok) {
14617 *valObjPtr = Jim_NewIntObj(interp, w);
14620 scanned += endp - tok;
14622 else {
14623 scanned = *tok ? 0 : -1;
14625 break;
14627 case 's':
14628 case '[':{
14629 *valObjPtr = JimScanAString(interp, descr->arg, tok);
14630 scanned += Jim_Length(*valObjPtr);
14631 break;
14633 case 'e':
14634 case 'f':
14635 case 'g':{
14636 char *endp;
14637 double value = strtod(tok, &endp);
14639 if (endp != tok) {
14641 *valObjPtr = Jim_NewDoubleObj(interp, value);
14643 scanned += endp - tok;
14645 else {
14646 scanned = *tok ? 0 : -1;
14648 break;
14651 if (tmpObj) {
14652 Jim_FreeNewObj(interp, tmpObj);
14655 return scanned;
14659 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
14661 size_t i, pos;
14662 int scanned = 1;
14663 const char *str = Jim_String(strObjPtr);
14664 int strLen = Jim_Utf8Length(interp, strObjPtr);
14665 Jim_Obj *resultList = 0;
14666 Jim_Obj **resultVec = 0;
14667 int resultc;
14668 Jim_Obj *emptyStr = 0;
14669 ScanFmtStringObj *fmtObj;
14672 JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
14674 fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
14676 if (fmtObj->error != 0) {
14677 if (flags & JIM_ERRMSG)
14678 Jim_SetResultString(interp, fmtObj->error, -1);
14679 return 0;
14682 emptyStr = Jim_NewEmptyStringObj(interp);
14683 Jim_IncrRefCount(emptyStr);
14685 resultList = Jim_NewListObj(interp, NULL, 0);
14686 if (fmtObj->maxPos > 0) {
14687 for (i = 0; i < fmtObj->maxPos; ++i)
14688 Jim_ListAppendElement(interp, resultList, emptyStr);
14689 JimListGetElements(interp, resultList, &resultc, &resultVec);
14692 for (i = 0, pos = 0; i < fmtObj->count; ++i) {
14693 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
14694 Jim_Obj *value = 0;
14697 if (descr->type == 0)
14698 continue;
14700 if (scanned > 0)
14701 scanned = ScanOneEntry(interp, str, pos, strLen, fmtObj, i, &value);
14703 if (scanned == -1 && i == 0)
14704 goto eof;
14706 pos += scanned;
14709 if (value == 0)
14710 value = Jim_NewEmptyStringObj(interp);
14712 if (descr->pos == -1) {
14713 Jim_FreeNewObj(interp, value);
14715 else if (descr->pos == 0)
14717 Jim_ListAppendElement(interp, resultList, value);
14718 else if (resultVec[descr->pos - 1] == emptyStr) {
14720 Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
14721 Jim_IncrRefCount(value);
14722 resultVec[descr->pos - 1] = value;
14724 else {
14726 Jim_FreeNewObj(interp, value);
14727 goto err;
14730 Jim_DecrRefCount(interp, emptyStr);
14731 return resultList;
14732 eof:
14733 Jim_DecrRefCount(interp, emptyStr);
14734 Jim_FreeNewObj(interp, resultList);
14735 return (Jim_Obj *)EOF;
14736 err:
14737 Jim_DecrRefCount(interp, emptyStr);
14738 Jim_FreeNewObj(interp, resultList);
14739 return 0;
14743 static void JimPrngInit(Jim_Interp *interp)
14745 #define PRNG_SEED_SIZE 256
14746 int i;
14747 unsigned int *seed;
14748 time_t t = time(NULL);
14750 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
14752 seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
14753 for (i = 0; i < PRNG_SEED_SIZE; i++) {
14754 seed[i] = (rand() ^ t ^ clock());
14756 JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
14757 Jim_Free(seed);
14761 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
14763 Jim_PrngState *prng;
14764 unsigned char *destByte = (unsigned char *)dest;
14765 unsigned int si, sj, x;
14768 if (interp->prngState == NULL)
14769 JimPrngInit(interp);
14770 prng = interp->prngState;
14772 for (x = 0; x < len; x++) {
14773 prng->i = (prng->i + 1) & 0xff;
14774 si = prng->sbox[prng->i];
14775 prng->j = (prng->j + si) & 0xff;
14776 sj = prng->sbox[prng->j];
14777 prng->sbox[prng->i] = sj;
14778 prng->sbox[prng->j] = si;
14779 *destByte++ = prng->sbox[(si + sj) & 0xff];
14784 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
14786 int i;
14787 Jim_PrngState *prng;
14790 if (interp->prngState == NULL)
14791 JimPrngInit(interp);
14792 prng = interp->prngState;
14795 for (i = 0; i < 256; i++)
14796 prng->sbox[i] = i;
14798 for (i = 0; i < seedLen; i++) {
14799 unsigned char t;
14801 t = prng->sbox[i & 0xFF];
14802 prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
14803 prng->sbox[seed[i]] = t;
14805 prng->i = prng->j = 0;
14807 for (i = 0; i < 256; i += seedLen) {
14808 JimRandomBytes(interp, seed, seedLen);
14813 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14815 jim_wide wideValue, increment = 1;
14816 Jim_Obj *intObjPtr;
14818 if (argc != 2 && argc != 3) {
14819 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
14820 return JIM_ERR;
14822 if (argc == 3) {
14823 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
14824 return JIM_ERR;
14826 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
14827 if (!intObjPtr) {
14829 wideValue = 0;
14831 else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
14832 return JIM_ERR;
14834 if (!intObjPtr || Jim_IsShared(intObjPtr)) {
14835 intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
14836 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
14837 Jim_FreeNewObj(interp, intObjPtr);
14838 return JIM_ERR;
14841 else {
14843 Jim_InvalidateStringRep(intObjPtr);
14844 JimWideValue(intObjPtr) = wideValue + increment;
14846 if (argv[1]->typePtr != &variableObjType) {
14848 Jim_SetVariable(interp, argv[1], intObjPtr);
14851 Jim_SetResult(interp, intObjPtr);
14852 return JIM_OK;
14856 #define JIM_EVAL_SARGV_LEN 8
14857 #define JIM_EVAL_SINTV_LEN 8
14860 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14862 int retcode;
14864 if (interp->unknown_called > 50) {
14865 return JIM_ERR;
14870 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
14871 return JIM_ERR;
14873 interp->unknown_called++;
14875 retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
14876 interp->unknown_called--;
14878 return retcode;
14881 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14883 int retcode;
14884 Jim_Cmd *cmdPtr;
14886 #if 0
14887 printf("invoke");
14888 int j;
14889 for (j = 0; j < objc; j++) {
14890 printf(" '%s'", Jim_String(objv[j]));
14892 printf("\n");
14893 #endif
14895 if (interp->framePtr->tailcallCmd) {
14897 cmdPtr = interp->framePtr->tailcallCmd;
14898 interp->framePtr->tailcallCmd = NULL;
14900 else {
14901 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
14902 if (cmdPtr == NULL) {
14903 return JimUnknown(interp, objc, objv);
14905 JimIncrCmdRefCount(cmdPtr);
14908 if (interp->evalDepth == interp->maxEvalDepth) {
14909 Jim_SetResultString(interp, "Infinite eval recursion", -1);
14910 retcode = JIM_ERR;
14911 goto out;
14913 interp->evalDepth++;
14916 Jim_SetEmptyResult(interp);
14917 if (cmdPtr->isproc) {
14918 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
14920 else {
14921 interp->cmdPrivData = cmdPtr->u.native.privData;
14922 retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
14924 interp->evalDepth--;
14926 out:
14927 JimDecrCmdRefCount(interp, cmdPtr);
14929 return retcode;
14932 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14934 int i, retcode;
14937 for (i = 0; i < objc; i++)
14938 Jim_IncrRefCount(objv[i]);
14940 retcode = JimInvokeCommand(interp, objc, objv);
14943 for (i = 0; i < objc; i++)
14944 Jim_DecrRefCount(interp, objv[i]);
14946 return retcode;
14949 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
14951 int ret;
14952 Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
14954 nargv[0] = prefix;
14955 memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
14956 ret = Jim_EvalObjVector(interp, objc + 1, nargv);
14957 Jim_Free(nargv);
14958 return ret;
14961 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script)
14963 if (!interp->errorFlag) {
14965 interp->errorFlag = 1;
14966 Jim_IncrRefCount(script->fileNameObj);
14967 Jim_DecrRefCount(interp, interp->errorFileNameObj);
14968 interp->errorFileNameObj = script->fileNameObj;
14969 interp->errorLine = script->linenr;
14971 JimResetStackTrace(interp);
14973 interp->addStackTrace++;
14977 if (interp->addStackTrace > 0) {
14980 JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr);
14982 if (Jim_Length(script->fileNameObj)) {
14983 interp->addStackTrace = 0;
14986 Jim_DecrRefCount(interp, interp->errorProc);
14987 interp->errorProc = interp->emptyObj;
14988 Jim_IncrRefCount(interp->errorProc);
14992 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
14994 Jim_Obj *objPtr;
14996 switch (token->type) {
14997 case JIM_TT_STR:
14998 case JIM_TT_ESC:
14999 objPtr = token->objPtr;
15000 break;
15001 case JIM_TT_VAR:
15002 objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
15003 break;
15004 case JIM_TT_DICTSUGAR:
15005 objPtr = JimExpandDictSugar(interp, token->objPtr);
15006 break;
15007 case JIM_TT_EXPRSUGAR:
15008 objPtr = JimExpandExprSugar(interp, token->objPtr);
15009 break;
15010 case JIM_TT_CMD:
15011 switch (Jim_EvalObj(interp, token->objPtr)) {
15012 case JIM_OK:
15013 case JIM_RETURN:
15014 objPtr = interp->result;
15015 break;
15016 case JIM_BREAK:
15018 return JIM_BREAK;
15019 case JIM_CONTINUE:
15021 return JIM_CONTINUE;
15022 default:
15023 return JIM_ERR;
15025 break;
15026 default:
15027 JimPanic((1,
15028 "default token type (%d) reached " "in Jim_SubstObj().", token->type));
15029 objPtr = NULL;
15030 break;
15032 if (objPtr) {
15033 *objPtrPtr = objPtr;
15034 return JIM_OK;
15036 return JIM_ERR;
15039 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
15041 int totlen = 0, i;
15042 Jim_Obj **intv;
15043 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
15044 Jim_Obj *objPtr;
15045 char *s;
15047 if (tokens <= JIM_EVAL_SINTV_LEN)
15048 intv = sintv;
15049 else
15050 intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
15052 for (i = 0; i < tokens; i++) {
15053 switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
15054 case JIM_OK:
15055 case JIM_RETURN:
15056 break;
15057 case JIM_BREAK:
15058 if (flags & JIM_SUBST_FLAG) {
15060 tokens = i;
15061 continue;
15065 case JIM_CONTINUE:
15066 if (flags & JIM_SUBST_FLAG) {
15067 intv[i] = NULL;
15068 continue;
15072 default:
15073 while (i--) {
15074 Jim_DecrRefCount(interp, intv[i]);
15076 if (intv != sintv) {
15077 Jim_Free(intv);
15079 return NULL;
15081 Jim_IncrRefCount(intv[i]);
15082 Jim_String(intv[i]);
15083 totlen += intv[i]->length;
15087 if (tokens == 1 && intv[0] && intv == sintv) {
15088 Jim_DecrRefCount(interp, intv[0]);
15089 return intv[0];
15092 objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
15094 if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
15095 && token[2].type == JIM_TT_VAR) {
15097 objPtr->typePtr = &interpolatedObjType;
15098 objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
15099 objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
15100 Jim_IncrRefCount(intv[2]);
15102 else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
15104 JimSetSourceInfo(interp, objPtr, intv[0]->internalRep.sourceValue.fileNameObj, intv[0]->internalRep.sourceValue.lineNumber);
15108 s = objPtr->bytes = Jim_Alloc(totlen + 1);
15109 objPtr->length = totlen;
15110 for (i = 0; i < tokens; i++) {
15111 if (intv[i]) {
15112 memcpy(s, intv[i]->bytes, intv[i]->length);
15113 s += intv[i]->length;
15114 Jim_DecrRefCount(interp, intv[i]);
15117 objPtr->bytes[totlen] = '\0';
15119 if (intv != sintv) {
15120 Jim_Free(intv);
15123 return objPtr;
15127 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15129 int retcode = JIM_OK;
15131 JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
15133 if (listPtr->internalRep.listValue.len) {
15134 Jim_IncrRefCount(listPtr);
15135 retcode = JimInvokeCommand(interp,
15136 listPtr->internalRep.listValue.len,
15137 listPtr->internalRep.listValue.ele);
15138 Jim_DecrRefCount(interp, listPtr);
15140 return retcode;
15143 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15145 SetListFromAny(interp, listPtr);
15146 return JimEvalObjList(interp, listPtr);
15149 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
15151 int i;
15152 ScriptObj *script;
15153 ScriptToken *token;
15154 int retcode = JIM_OK;
15155 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
15156 Jim_Obj *prevScriptObj;
15158 if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
15159 return JimEvalObjList(interp, scriptObjPtr);
15162 Jim_IncrRefCount(scriptObjPtr);
15163 script = JimGetScript(interp, scriptObjPtr);
15164 if (!JimScriptValid(interp, script)) {
15165 Jim_DecrRefCount(interp, scriptObjPtr);
15166 return JIM_ERR;
15169 Jim_SetEmptyResult(interp);
15171 token = script->token;
15173 #ifdef JIM_OPTIMIZATION
15174 if (script->len == 0) {
15175 Jim_DecrRefCount(interp, scriptObjPtr);
15176 return JIM_OK;
15178 if (script->len == 3
15179 && token[1].objPtr->typePtr == &commandObjType
15180 && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
15181 && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
15182 && token[2].objPtr->typePtr == &variableObjType) {
15184 Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
15186 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
15187 JimWideValue(objPtr)++;
15188 Jim_InvalidateStringRep(objPtr);
15189 Jim_DecrRefCount(interp, scriptObjPtr);
15190 Jim_SetResult(interp, objPtr);
15191 return JIM_OK;
15194 #endif
15196 script->inUse++;
15199 prevScriptObj = interp->currentScriptObj;
15200 interp->currentScriptObj = scriptObjPtr;
15202 interp->errorFlag = 0;
15203 argv = sargv;
15205 for (i = 0; i < script->len && retcode == JIM_OK; ) {
15206 int argc;
15207 int j;
15210 argc = token[i].objPtr->internalRep.scriptLineValue.argc;
15211 script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
15214 if (argc > JIM_EVAL_SARGV_LEN)
15215 argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
15218 i++;
15220 for (j = 0; j < argc; j++) {
15221 long wordtokens = 1;
15222 int expand = 0;
15223 Jim_Obj *wordObjPtr = NULL;
15225 if (token[i].type == JIM_TT_WORD) {
15226 wordtokens = JimWideValue(token[i++].objPtr);
15227 if (wordtokens < 0) {
15228 expand = 1;
15229 wordtokens = -wordtokens;
15233 if (wordtokens == 1) {
15235 switch (token[i].type) {
15236 case JIM_TT_ESC:
15237 case JIM_TT_STR:
15238 wordObjPtr = token[i].objPtr;
15239 break;
15240 case JIM_TT_VAR:
15241 wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
15242 break;
15243 case JIM_TT_EXPRSUGAR:
15244 wordObjPtr = JimExpandExprSugar(interp, token[i].objPtr);
15245 break;
15246 case JIM_TT_DICTSUGAR:
15247 wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
15248 break;
15249 case JIM_TT_CMD:
15250 retcode = Jim_EvalObj(interp, token[i].objPtr);
15251 if (retcode == JIM_OK) {
15252 wordObjPtr = Jim_GetResult(interp);
15254 break;
15255 default:
15256 JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
15259 else {
15260 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
15263 if (!wordObjPtr) {
15264 if (retcode == JIM_OK) {
15265 retcode = JIM_ERR;
15267 break;
15270 Jim_IncrRefCount(wordObjPtr);
15271 i += wordtokens;
15273 if (!expand) {
15274 argv[j] = wordObjPtr;
15276 else {
15278 int len = Jim_ListLength(interp, wordObjPtr);
15279 int newargc = argc + len - 1;
15280 int k;
15282 if (len > 1) {
15283 if (argv == sargv) {
15284 if (newargc > JIM_EVAL_SARGV_LEN) {
15285 argv = Jim_Alloc(sizeof(*argv) * newargc);
15286 memcpy(argv, sargv, sizeof(*argv) * j);
15289 else {
15291 argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
15296 for (k = 0; k < len; k++) {
15297 argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
15298 Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
15301 Jim_DecrRefCount(interp, wordObjPtr);
15304 j--;
15305 argc += len - 1;
15309 if (retcode == JIM_OK && argc) {
15311 retcode = JimInvokeCommand(interp, argc, argv);
15313 if (Jim_CheckSignal(interp)) {
15314 retcode = JIM_SIGNAL;
15319 while (j-- > 0) {
15320 Jim_DecrRefCount(interp, argv[j]);
15323 if (argv != sargv) {
15324 Jim_Free(argv);
15325 argv = sargv;
15330 if (retcode == JIM_ERR) {
15331 JimAddErrorToStack(interp, script);
15334 else if (retcode != JIM_RETURN || interp->returnCode != JIM_ERR) {
15336 interp->addStackTrace = 0;
15340 interp->currentScriptObj = prevScriptObj;
15342 Jim_FreeIntRep(interp, scriptObjPtr);
15343 scriptObjPtr->typePtr = &scriptObjType;
15344 Jim_SetIntRepPtr(scriptObjPtr, script);
15345 Jim_DecrRefCount(interp, scriptObjPtr);
15347 return retcode;
15350 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
15352 int retcode;
15354 const char *varname = Jim_String(argNameObj);
15355 if (*varname == '&') {
15357 Jim_Obj *objPtr;
15358 Jim_CallFrame *savedCallFrame = interp->framePtr;
15360 interp->framePtr = interp->framePtr->parent;
15361 objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
15362 interp->framePtr = savedCallFrame;
15363 if (!objPtr) {
15364 return JIM_ERR;
15368 objPtr = Jim_NewStringObj(interp, varname + 1, -1);
15369 Jim_IncrRefCount(objPtr);
15370 retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
15371 Jim_DecrRefCount(interp, objPtr);
15373 else {
15374 retcode = Jim_SetVariable(interp, argNameObj, argValObj);
15376 return retcode;
15379 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
15382 Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
15383 int i;
15385 for (i = 0; i < cmd->u.proc.argListLen; i++) {
15386 Jim_AppendString(interp, argmsg, " ", 1);
15388 if (i == cmd->u.proc.argsPos) {
15389 if (cmd->u.proc.arglist[i].defaultObjPtr) {
15391 Jim_AppendString(interp, argmsg, "?", 1);
15392 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
15393 Jim_AppendString(interp, argmsg, " ...?", -1);
15395 else {
15397 Jim_AppendString(interp, argmsg, "?arg...?", -1);
15400 else {
15401 if (cmd->u.proc.arglist[i].defaultObjPtr) {
15402 Jim_AppendString(interp, argmsg, "?", 1);
15403 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
15404 Jim_AppendString(interp, argmsg, "?", 1);
15406 else {
15407 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
15408 if (*arg == '&') {
15409 arg++;
15411 Jim_AppendString(interp, argmsg, arg, -1);
15415 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
15418 #ifdef jim_ext_namespace
15419 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
15421 Jim_CallFrame *callFramePtr;
15422 int retcode;
15425 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
15426 callFramePtr->argv = &interp->emptyObj;
15427 callFramePtr->argc = 0;
15428 callFramePtr->procArgsObjPtr = NULL;
15429 callFramePtr->procBodyObjPtr = scriptObj;
15430 callFramePtr->staticVars = NULL;
15431 callFramePtr->fileNameObj = interp->emptyObj;
15432 callFramePtr->line = 0;
15433 Jim_IncrRefCount(scriptObj);
15434 interp->framePtr = callFramePtr;
15437 if (interp->framePtr->level == interp->maxCallFrameDepth) {
15438 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15439 retcode = JIM_ERR;
15441 else {
15443 retcode = Jim_EvalObj(interp, scriptObj);
15447 interp->framePtr = interp->framePtr->parent;
15448 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15450 return retcode;
15452 #endif
15454 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
15456 Jim_CallFrame *callFramePtr;
15457 int i, d, retcode, optargs;
15458 ScriptObj *script;
15461 if (argc - 1 < cmd->u.proc.reqArity ||
15462 (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
15463 JimSetProcWrongArgs(interp, argv[0], cmd);
15464 return JIM_ERR;
15467 if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
15469 return JIM_OK;
15473 if (interp->framePtr->level == interp->maxCallFrameDepth) {
15474 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15475 return JIM_ERR;
15479 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
15480 callFramePtr->argv = argv;
15481 callFramePtr->argc = argc;
15482 callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
15483 callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
15484 callFramePtr->staticVars = cmd->u.proc.staticVars;
15487 script = JimGetScript(interp, interp->currentScriptObj);
15488 callFramePtr->fileNameObj = script->fileNameObj;
15489 callFramePtr->line = script->linenr;
15491 Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
15492 Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
15493 interp->framePtr = callFramePtr;
15496 optargs = (argc - 1 - cmd->u.proc.reqArity);
15499 i = 1;
15500 for (d = 0; d < cmd->u.proc.argListLen; d++) {
15501 Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
15502 if (d == cmd->u.proc.argsPos) {
15504 Jim_Obj *listObjPtr;
15505 int argsLen = 0;
15506 if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
15507 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
15509 listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
15512 if (cmd->u.proc.arglist[d].defaultObjPtr) {
15513 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
15515 retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
15516 if (retcode != JIM_OK) {
15517 goto badargset;
15520 i += argsLen;
15521 continue;
15525 if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
15526 retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
15528 else {
15530 retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
15532 if (retcode != JIM_OK) {
15533 goto badargset;
15538 retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
15540 badargset:
15543 interp->framePtr = interp->framePtr->parent;
15544 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15547 if (interp->framePtr->tailcallObj) {
15548 do {
15549 Jim_Obj *tailcallObj = interp->framePtr->tailcallObj;
15551 interp->framePtr->tailcallObj = NULL;
15553 if (retcode == JIM_EVAL) {
15554 retcode = Jim_EvalObjList(interp, tailcallObj);
15555 if (retcode == JIM_RETURN) {
15556 interp->returnLevel++;
15559 Jim_DecrRefCount(interp, tailcallObj);
15560 } while (interp->framePtr->tailcallObj);
15563 if (interp->framePtr->tailcallCmd) {
15564 JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
15565 interp->framePtr->tailcallCmd = NULL;
15570 if (retcode == JIM_RETURN) {
15571 if (--interp->returnLevel <= 0) {
15572 retcode = interp->returnCode;
15573 interp->returnCode = JIM_OK;
15574 interp->returnLevel = 0;
15577 else if (retcode == JIM_ERR) {
15578 interp->addStackTrace++;
15579 Jim_DecrRefCount(interp, interp->errorProc);
15580 interp->errorProc = argv[0];
15581 Jim_IncrRefCount(interp->errorProc);
15584 return retcode;
15587 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
15589 int retval;
15590 Jim_Obj *scriptObjPtr;
15592 scriptObjPtr = Jim_NewStringObj(interp, script, -1);
15593 Jim_IncrRefCount(scriptObjPtr);
15595 if (filename) {
15596 Jim_Obj *prevScriptObj;
15598 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
15600 prevScriptObj = interp->currentScriptObj;
15601 interp->currentScriptObj = scriptObjPtr;
15603 retval = Jim_EvalObj(interp, scriptObjPtr);
15605 interp->currentScriptObj = prevScriptObj;
15607 else {
15608 retval = Jim_EvalObj(interp, scriptObjPtr);
15610 Jim_DecrRefCount(interp, scriptObjPtr);
15611 return retval;
15614 int Jim_Eval(Jim_Interp *interp, const char *script)
15616 return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
15620 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
15622 int retval;
15623 Jim_CallFrame *savedFramePtr = interp->framePtr;
15625 interp->framePtr = interp->topFramePtr;
15626 retval = Jim_Eval(interp, script);
15627 interp->framePtr = savedFramePtr;
15629 return retval;
15632 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
15634 int retval;
15635 Jim_CallFrame *savedFramePtr = interp->framePtr;
15637 interp->framePtr = interp->topFramePtr;
15638 retval = Jim_EvalFile(interp, filename);
15639 interp->framePtr = savedFramePtr;
15641 return retval;
15644 #include <sys/stat.h>
15646 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
15648 FILE *fp;
15649 char *buf;
15650 Jim_Obj *scriptObjPtr;
15651 Jim_Obj *prevScriptObj;
15652 struct stat sb;
15653 int retcode;
15654 int readlen;
15656 if (stat(filename, &sb) != 0 || (fp = fopen(filename, "rt")) == NULL) {
15657 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
15658 return JIM_ERR;
15660 if (sb.st_size == 0) {
15661 fclose(fp);
15662 return JIM_OK;
15665 buf = Jim_Alloc(sb.st_size + 1);
15666 readlen = fread(buf, 1, sb.st_size, fp);
15667 if (ferror(fp)) {
15668 fclose(fp);
15669 Jim_Free(buf);
15670 Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
15671 return JIM_ERR;
15673 fclose(fp);
15674 buf[readlen] = 0;
15676 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
15677 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1);
15678 Jim_IncrRefCount(scriptObjPtr);
15680 prevScriptObj = interp->currentScriptObj;
15681 interp->currentScriptObj = scriptObjPtr;
15683 retcode = Jim_EvalObj(interp, scriptObjPtr);
15686 if (retcode == JIM_RETURN) {
15687 if (--interp->returnLevel <= 0) {
15688 retcode = interp->returnCode;
15689 interp->returnCode = JIM_OK;
15690 interp->returnLevel = 0;
15693 if (retcode == JIM_ERR) {
15695 interp->addStackTrace++;
15698 interp->currentScriptObj = prevScriptObj;
15700 Jim_DecrRefCount(interp, scriptObjPtr);
15702 return retcode;
15705 static void JimParseSubst(struct JimParserCtx *pc, int flags)
15707 pc->tstart = pc->p;
15708 pc->tline = pc->linenr;
15710 if (pc->len == 0) {
15711 pc->tend = pc->p;
15712 pc->tt = JIM_TT_EOL;
15713 pc->eof = 1;
15714 return;
15716 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15717 JimParseCmd(pc);
15718 return;
15720 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15721 if (JimParseVar(pc) == JIM_OK) {
15722 return;
15725 pc->tstart = pc->p;
15726 flags |= JIM_SUBST_NOVAR;
15728 while (pc->len) {
15729 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15730 break;
15732 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15733 break;
15735 if (*pc->p == '\\' && pc->len > 1) {
15736 pc->p++;
15737 pc->len--;
15739 pc->p++;
15740 pc->len--;
15742 pc->tend = pc->p - 1;
15743 pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
15747 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
15749 int scriptTextLen;
15750 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
15751 struct JimParserCtx parser;
15752 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
15753 ParseTokenList tokenlist;
15756 ScriptTokenListInit(&tokenlist);
15758 JimParserInit(&parser, scriptText, scriptTextLen, 1);
15759 while (1) {
15760 JimParseSubst(&parser, flags);
15761 if (parser.eof) {
15763 break;
15765 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
15766 parser.tline);
15770 script->inUse = 1;
15771 script->substFlags = flags;
15772 script->fileNameObj = interp->emptyObj;
15773 Jim_IncrRefCount(script->fileNameObj);
15774 SubstObjAddTokens(interp, script, &tokenlist);
15777 ScriptTokenListFree(&tokenlist);
15779 #ifdef DEBUG_SHOW_SUBST
15781 int i;
15783 printf("==== Subst ====\n");
15784 for (i = 0; i < script->len; i++) {
15785 printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
15786 Jim_String(script->token[i].objPtr));
15789 #endif
15792 Jim_FreeIntRep(interp, objPtr);
15793 Jim_SetIntRepPtr(objPtr, script);
15794 objPtr->typePtr = &scriptObjType;
15795 return JIM_OK;
15798 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
15800 if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
15801 SetSubstFromAny(interp, objPtr, flags);
15802 return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
15805 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
15807 ScriptObj *script = Jim_GetSubst(interp, substObjPtr, flags);
15809 Jim_IncrRefCount(substObjPtr);
15810 script->inUse++;
15812 *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
15814 script->inUse--;
15815 Jim_DecrRefCount(interp, substObjPtr);
15816 if (*resObjPtrPtr == NULL) {
15817 return JIM_ERR;
15819 return JIM_OK;
15822 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
15824 Jim_Obj *objPtr;
15825 Jim_Obj *listObjPtr;
15827 JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0"));
15829 listObjPtr = Jim_NewListObj(interp, argv, argc);
15831 if (*msg) {
15832 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
15834 Jim_IncrRefCount(listObjPtr);
15835 objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
15836 Jim_DecrRefCount(interp, listObjPtr);
15838 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
15841 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
15842 Jim_HashEntry *he, int type);
15844 #define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL)
15846 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
15847 JimHashtableIteratorCallbackType *callback, int type)
15849 Jim_HashEntry *he;
15850 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
15853 if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
15854 he = Jim_FindHashEntry(ht, Jim_String(patternObjPtr));
15855 if (he) {
15856 callback(interp, listObjPtr, he, type);
15859 else {
15860 Jim_HashTableIterator htiter;
15861 JimInitHashTableIterator(ht, &htiter);
15862 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
15863 if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), he->key, 0)) {
15864 callback(interp, listObjPtr, he, type);
15868 return listObjPtr;
15872 #define JIM_CMDLIST_COMMANDS 0
15873 #define JIM_CMDLIST_PROCS 1
15874 #define JIM_CMDLIST_CHANNELS 2
15876 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15877 Jim_HashEntry *he, int type)
15879 Jim_Cmd *cmdPtr = Jim_GetHashEntryVal(he);
15880 Jim_Obj *objPtr;
15882 if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
15884 return;
15887 objPtr = Jim_NewStringObj(interp, he->key, -1);
15888 Jim_IncrRefCount(objPtr);
15890 if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, objPtr)) {
15891 Jim_ListAppendElement(interp, listObjPtr, objPtr);
15893 Jim_DecrRefCount(interp, objPtr);
15897 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
15899 return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
15903 #define JIM_VARLIST_GLOBALS 0
15904 #define JIM_VARLIST_LOCALS 1
15905 #define JIM_VARLIST_VARS 2
15907 #define JIM_VARLIST_VALUES 0x1000
15909 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15910 Jim_HashEntry *he, int type)
15912 Jim_Var *varPtr = Jim_GetHashEntryVal(he);
15914 if (type != JIM_VARLIST_LOCALS || varPtr->linkFramePtr == NULL) {
15915 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1));
15916 if (type & JIM_VARLIST_VALUES) {
15917 Jim_ListAppendElement(interp, listObjPtr, varPtr->objPtr);
15923 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
15925 if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
15926 return interp->emptyObj;
15928 else {
15929 Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
15930 return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch, mode);
15934 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
15935 Jim_Obj **objPtrPtr, int info_level_cmd)
15937 Jim_CallFrame *targetCallFrame;
15939 targetCallFrame = JimGetCallFrameByInteger(interp, levelObjPtr);
15940 if (targetCallFrame == NULL) {
15941 return JIM_ERR;
15944 if (targetCallFrame == interp->topFramePtr) {
15945 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
15946 return JIM_ERR;
15948 if (info_level_cmd) {
15949 *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
15951 else {
15952 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
15954 Jim_ListAppendElement(interp, listObj, targetCallFrame->argv[0]);
15955 Jim_ListAppendElement(interp, listObj, targetCallFrame->fileNameObj);
15956 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, targetCallFrame->line));
15957 *objPtrPtr = listObj;
15959 return JIM_OK;
15964 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15966 if (argc != 2 && argc != 3) {
15967 Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
15968 return JIM_ERR;
15970 if (argc == 3) {
15971 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
15972 Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
15973 return JIM_ERR;
15975 else {
15976 fputs(Jim_String(argv[2]), stdout);
15979 else {
15980 puts(Jim_String(argv[1]));
15982 return JIM_OK;
15986 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
15988 jim_wide wideValue, res;
15989 double doubleValue, doubleRes;
15990 int i;
15992 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
15994 for (i = 1; i < argc; i++) {
15995 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
15996 goto trydouble;
15997 if (op == JIM_EXPROP_ADD)
15998 res += wideValue;
15999 else
16000 res *= wideValue;
16002 Jim_SetResultInt(interp, res);
16003 return JIM_OK;
16004 trydouble:
16005 doubleRes = (double)res;
16006 for (; i < argc; i++) {
16007 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
16008 return JIM_ERR;
16009 if (op == JIM_EXPROP_ADD)
16010 doubleRes += doubleValue;
16011 else
16012 doubleRes *= doubleValue;
16014 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16015 return JIM_OK;
16019 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
16021 jim_wide wideValue, res = 0;
16022 double doubleValue, doubleRes = 0;
16023 int i = 2;
16025 if (argc < 2) {
16026 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
16027 return JIM_ERR;
16029 else if (argc == 2) {
16030 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
16031 if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
16032 return JIM_ERR;
16034 else {
16035 if (op == JIM_EXPROP_SUB)
16036 doubleRes = -doubleValue;
16037 else
16038 doubleRes = 1.0 / doubleValue;
16039 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16040 return JIM_OK;
16043 if (op == JIM_EXPROP_SUB) {
16044 res = -wideValue;
16045 Jim_SetResultInt(interp, res);
16047 else {
16048 doubleRes = 1.0 / wideValue;
16049 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16051 return JIM_OK;
16053 else {
16054 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
16055 if (Jim_GetDouble(interp, argv[1], &doubleRes)
16056 != JIM_OK) {
16057 return JIM_ERR;
16059 else {
16060 goto trydouble;
16064 for (i = 2; i < argc; i++) {
16065 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
16066 doubleRes = (double)res;
16067 goto trydouble;
16069 if (op == JIM_EXPROP_SUB)
16070 res -= wideValue;
16071 else
16072 res /= wideValue;
16074 Jim_SetResultInt(interp, res);
16075 return JIM_OK;
16076 trydouble:
16077 for (; i < argc; i++) {
16078 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
16079 return JIM_ERR;
16080 if (op == JIM_EXPROP_SUB)
16081 doubleRes -= doubleValue;
16082 else
16083 doubleRes /= doubleValue;
16085 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16086 return JIM_OK;
16091 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16093 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
16097 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16099 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
16103 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16105 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
16109 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16111 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
16115 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16117 if (argc != 2 && argc != 3) {
16118 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
16119 return JIM_ERR;
16121 if (argc == 2) {
16122 Jim_Obj *objPtr;
16124 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16125 if (!objPtr)
16126 return JIM_ERR;
16127 Jim_SetResult(interp, objPtr);
16128 return JIM_OK;
16131 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
16132 return JIM_ERR;
16133 Jim_SetResult(interp, argv[2]);
16134 return JIM_OK;
16137 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16139 int i = 1;
16140 int complain = 1;
16142 while (i < argc) {
16143 if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
16144 i++;
16145 break;
16147 if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
16148 complain = 0;
16149 i++;
16150 continue;
16152 break;
16155 while (i < argc) {
16156 if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
16157 && complain) {
16158 return JIM_ERR;
16160 i++;
16162 return JIM_OK;
16166 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16168 if (argc != 3) {
16169 Jim_WrongNumArgs(interp, 1, argv, "condition body");
16170 return JIM_ERR;
16174 while (1) {
16175 int boolean, retval;
16177 if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
16178 return retval;
16179 if (!boolean)
16180 break;
16182 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
16183 switch (retval) {
16184 case JIM_BREAK:
16185 goto out;
16186 break;
16187 case JIM_CONTINUE:
16188 continue;
16189 break;
16190 default:
16191 return retval;
16195 out:
16196 Jim_SetEmptyResult(interp);
16197 return JIM_OK;
16201 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16203 int retval;
16204 int boolean = 1;
16205 Jim_Obj *varNamePtr = NULL;
16206 Jim_Obj *stopVarNamePtr = NULL;
16208 if (argc != 5) {
16209 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
16210 return JIM_ERR;
16214 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
16215 return retval;
16218 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16221 #ifdef JIM_OPTIMIZATION
16222 if (retval == JIM_OK && boolean) {
16223 ScriptObj *incrScript;
16224 ExprByteCode *expr;
16225 jim_wide stop, currentVal;
16226 Jim_Obj *objPtr;
16227 int cmpOffset;
16230 expr = JimGetExpression(interp, argv[2]);
16231 incrScript = JimGetScript(interp, argv[3]);
16234 if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
16235 goto evalstart;
16238 if (incrScript->token[1].type != JIM_TT_ESC ||
16239 expr->token[0].type != JIM_TT_VAR ||
16240 (expr->token[1].type != JIM_TT_EXPR_INT && expr->token[1].type != JIM_TT_VAR)) {
16241 goto evalstart;
16244 if (expr->token[2].type == JIM_EXPROP_LT) {
16245 cmpOffset = 0;
16247 else if (expr->token[2].type == JIM_EXPROP_LTE) {
16248 cmpOffset = 1;
16250 else {
16251 goto evalstart;
16255 if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
16256 goto evalstart;
16260 if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->token[0].objPtr)) {
16261 goto evalstart;
16265 if (expr->token[1].type == JIM_TT_EXPR_INT) {
16266 if (Jim_GetWide(interp, expr->token[1].objPtr, &stop) == JIM_ERR) {
16267 goto evalstart;
16270 else {
16271 stopVarNamePtr = expr->token[1].objPtr;
16272 Jim_IncrRefCount(stopVarNamePtr);
16274 stop = 0;
16278 varNamePtr = expr->token[0].objPtr;
16279 Jim_IncrRefCount(varNamePtr);
16281 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
16282 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
16283 goto testcond;
16287 while (retval == JIM_OK) {
16292 if (stopVarNamePtr) {
16293 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
16294 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
16295 goto testcond;
16299 if (currentVal >= stop + cmpOffset) {
16300 break;
16304 retval = Jim_EvalObj(interp, argv[4]);
16305 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16306 retval = JIM_OK;
16308 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
16311 if (objPtr == NULL) {
16312 retval = JIM_ERR;
16313 goto out;
16315 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16316 currentVal = ++JimWideValue(objPtr);
16317 Jim_InvalidateStringRep(objPtr);
16319 else {
16320 if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
16321 Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
16322 ++currentVal)) != JIM_OK) {
16323 goto evalnext;
16328 goto out;
16330 evalstart:
16331 #endif
16333 while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
16335 retval = Jim_EvalObj(interp, argv[4]);
16337 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16339 JIM_IF_OPTIM(evalnext:)
16340 retval = Jim_EvalObj(interp, argv[3]);
16341 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16343 JIM_IF_OPTIM(testcond:)
16344 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16348 JIM_IF_OPTIM(out:)
16349 if (stopVarNamePtr) {
16350 Jim_DecrRefCount(interp, stopVarNamePtr);
16352 if (varNamePtr) {
16353 Jim_DecrRefCount(interp, varNamePtr);
16356 if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
16357 Jim_SetEmptyResult(interp);
16358 return JIM_OK;
16361 return retval;
16365 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16367 int retval;
16368 jim_wide i;
16369 jim_wide limit;
16370 jim_wide incr = 1;
16371 Jim_Obj *bodyObjPtr;
16373 if (argc != 5 && argc != 6) {
16374 Jim_WrongNumArgs(interp, 1, argv, "var first limit ?incr? body");
16375 return JIM_ERR;
16378 if (Jim_GetWide(interp, argv[2], &i) != JIM_OK ||
16379 Jim_GetWide(interp, argv[3], &limit) != JIM_OK ||
16380 (argc == 6 && Jim_GetWide(interp, argv[4], &incr) != JIM_OK)) {
16381 return JIM_ERR;
16383 bodyObjPtr = (argc == 5) ? argv[4] : argv[5];
16385 retval = Jim_SetVariable(interp, argv[1], argv[2]);
16387 while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
16388 retval = Jim_EvalObj(interp, bodyObjPtr);
16389 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16390 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16392 retval = JIM_OK;
16395 i += incr;
16397 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16398 if (argv[1]->typePtr != &variableObjType) {
16399 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16400 return JIM_ERR;
16403 JimWideValue(objPtr) = i;
16404 Jim_InvalidateStringRep(objPtr);
16406 if (argv[1]->typePtr != &variableObjType) {
16407 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16408 retval = JIM_ERR;
16409 break;
16413 else {
16414 objPtr = Jim_NewIntObj(interp, i);
16415 retval = Jim_SetVariable(interp, argv[1], objPtr);
16416 if (retval != JIM_OK) {
16417 Jim_FreeNewObj(interp, objPtr);
16423 if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
16424 Jim_SetEmptyResult(interp);
16425 return JIM_OK;
16427 return retval;
16430 typedef struct {
16431 Jim_Obj *objPtr;
16432 int idx;
16433 } Jim_ListIter;
16435 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
16437 iter->objPtr = objPtr;
16438 iter->idx = 0;
16441 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
16443 if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
16444 return NULL;
16446 return iter->objPtr->internalRep.listValue.ele[iter->idx++];
16449 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
16451 return iter->idx >= Jim_ListLength(interp, iter->objPtr);
16455 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
16457 int result = JIM_OK;
16458 int i, numargs;
16459 Jim_ListIter twoiters[2];
16460 Jim_ListIter *iters;
16461 Jim_Obj *script;
16462 Jim_Obj *resultObj;
16464 if (argc < 4 || argc % 2 != 0) {
16465 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
16466 return JIM_ERR;
16468 script = argv[argc - 1];
16469 numargs = (argc - 1 - 1);
16471 if (numargs == 2) {
16472 iters = twoiters;
16474 else {
16475 iters = Jim_Alloc(numargs * sizeof(*iters));
16477 for (i = 0; i < numargs; i++) {
16478 JimListIterInit(&iters[i], argv[i + 1]);
16479 if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
16480 result = JIM_ERR;
16483 if (result != JIM_OK) {
16484 Jim_SetResultString(interp, "foreach varlist is empty", -1);
16485 return result;
16488 if (doMap) {
16489 resultObj = Jim_NewListObj(interp, NULL, 0);
16491 else {
16492 resultObj = interp->emptyObj;
16494 Jim_IncrRefCount(resultObj);
16496 while (1) {
16498 for (i = 0; i < numargs; i += 2) {
16499 if (!JimListIterDone(interp, &iters[i + 1])) {
16500 break;
16503 if (i == numargs) {
16505 break;
16509 for (i = 0; i < numargs; i += 2) {
16510 Jim_Obj *varName;
16513 JimListIterInit(&iters[i], argv[i + 1]);
16514 while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
16515 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
16516 if (!valObj) {
16518 valObj = interp->emptyObj;
16521 Jim_IncrRefCount(valObj);
16522 result = Jim_SetVariable(interp, varName, valObj);
16523 Jim_DecrRefCount(interp, valObj);
16524 if (result != JIM_OK) {
16525 goto err;
16529 switch (result = Jim_EvalObj(interp, script)) {
16530 case JIM_OK:
16531 if (doMap) {
16532 Jim_ListAppendElement(interp, resultObj, interp->result);
16534 break;
16535 case JIM_CONTINUE:
16536 break;
16537 case JIM_BREAK:
16538 goto out;
16539 default:
16540 goto err;
16543 out:
16544 result = JIM_OK;
16545 Jim_SetResult(interp, resultObj);
16546 err:
16547 Jim_DecrRefCount(interp, resultObj);
16548 if (numargs > 2) {
16549 Jim_Free(iters);
16551 return result;
16555 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16557 return JimForeachMapHelper(interp, argc, argv, 0);
16561 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16563 return JimForeachMapHelper(interp, argc, argv, 1);
16567 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16569 int result = JIM_ERR;
16570 int i;
16571 Jim_ListIter iter;
16572 Jim_Obj *resultObj;
16574 if (argc < 2) {
16575 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
16576 return JIM_ERR;
16579 JimListIterInit(&iter, argv[1]);
16581 for (i = 2; i < argc; i++) {
16582 Jim_Obj *valObj = JimListIterNext(interp, &iter);
16583 result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
16584 if (result != JIM_OK) {
16585 return result;
16589 resultObj = Jim_NewListObj(interp, NULL, 0);
16590 while (!JimListIterDone(interp, &iter)) {
16591 Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
16594 Jim_SetResult(interp, resultObj);
16596 return JIM_OK;
16600 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16602 int boolean, retval, current = 1, falsebody = 0;
16604 if (argc >= 3) {
16605 while (1) {
16607 if (current >= argc)
16608 goto err;
16609 if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
16610 != JIM_OK)
16611 return retval;
16613 if (current >= argc)
16614 goto err;
16615 if (Jim_CompareStringImmediate(interp, argv[current], "then"))
16616 current++;
16618 if (current >= argc)
16619 goto err;
16620 if (boolean)
16621 return Jim_EvalObj(interp, argv[current]);
16623 if (++current >= argc) {
16624 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
16625 return JIM_OK;
16627 falsebody = current++;
16628 if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
16630 if (current != argc - 1)
16631 goto err;
16632 return Jim_EvalObj(interp, argv[current]);
16634 else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
16635 continue;
16637 else if (falsebody != argc - 1)
16638 goto err;
16639 return Jim_EvalObj(interp, argv[falsebody]);
16641 return JIM_OK;
16643 err:
16644 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
16645 return JIM_ERR;
16650 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
16651 Jim_Obj *stringObj, int nocase)
16653 Jim_Obj *parms[4];
16654 int argc = 0;
16655 long eq;
16656 int rc;
16658 parms[argc++] = commandObj;
16659 if (nocase) {
16660 parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
16662 parms[argc++] = patternObj;
16663 parms[argc++] = stringObj;
16665 rc = Jim_EvalObjVector(interp, argc, parms);
16667 if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
16668 eq = -rc;
16671 return eq;
16674 enum
16675 { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
16678 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16680 int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
16681 Jim_Obj *command = 0, *const *caseList = 0, *strObj;
16682 Jim_Obj *script = 0;
16684 if (argc < 3) {
16685 wrongnumargs:
16686 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
16687 "pattern body ... ?default body? or " "{pattern body ?pattern body ...?}");
16688 return JIM_ERR;
16690 for (opt = 1; opt < argc; ++opt) {
16691 const char *option = Jim_String(argv[opt]);
16693 if (*option != '-')
16694 break;
16695 else if (strncmp(option, "--", 2) == 0) {
16696 ++opt;
16697 break;
16699 else if (strncmp(option, "-exact", 2) == 0)
16700 matchOpt = SWITCH_EXACT;
16701 else if (strncmp(option, "-glob", 2) == 0)
16702 matchOpt = SWITCH_GLOB;
16703 else if (strncmp(option, "-regexp", 2) == 0)
16704 matchOpt = SWITCH_RE;
16705 else if (strncmp(option, "-command", 2) == 0) {
16706 matchOpt = SWITCH_CMD;
16707 if ((argc - opt) < 2)
16708 goto wrongnumargs;
16709 command = argv[++opt];
16711 else {
16712 Jim_SetResultFormatted(interp,
16713 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
16714 argv[opt]);
16715 return JIM_ERR;
16717 if ((argc - opt) < 2)
16718 goto wrongnumargs;
16720 strObj = argv[opt++];
16721 patCount = argc - opt;
16722 if (patCount == 1) {
16723 Jim_Obj **vector;
16725 JimListGetElements(interp, argv[opt], &patCount, &vector);
16726 caseList = vector;
16728 else
16729 caseList = &argv[opt];
16730 if (patCount == 0 || patCount % 2 != 0)
16731 goto wrongnumargs;
16732 for (i = 0; script == 0 && i < patCount; i += 2) {
16733 Jim_Obj *patObj = caseList[i];
16735 if (!Jim_CompareStringImmediate(interp, patObj, "default")
16736 || i < (patCount - 2)) {
16737 switch (matchOpt) {
16738 case SWITCH_EXACT:
16739 if (Jim_StringEqObj(strObj, patObj))
16740 script = caseList[i + 1];
16741 break;
16742 case SWITCH_GLOB:
16743 if (Jim_StringMatchObj(interp, patObj, strObj, 0))
16744 script = caseList[i + 1];
16745 break;
16746 case SWITCH_RE:
16747 command = Jim_NewStringObj(interp, "regexp", -1);
16749 case SWITCH_CMD:{
16750 int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, 0);
16752 if (argc - opt == 1) {
16753 Jim_Obj **vector;
16755 JimListGetElements(interp, argv[opt], &patCount, &vector);
16756 caseList = vector;
16759 if (rc < 0) {
16760 return -rc;
16762 if (rc)
16763 script = caseList[i + 1];
16764 break;
16768 else {
16769 script = caseList[i + 1];
16772 for (; i < patCount && Jim_CompareStringImmediate(interp, script, "-"); i += 2)
16773 script = caseList[i + 1];
16774 if (script && Jim_CompareStringImmediate(interp, script, "-")) {
16775 Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
16776 return JIM_ERR;
16778 Jim_SetEmptyResult(interp);
16779 if (script) {
16780 return Jim_EvalObj(interp, script);
16782 return JIM_OK;
16786 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16788 Jim_Obj *listObjPtr;
16790 listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1);
16791 Jim_SetResult(interp, listObjPtr);
16792 return JIM_OK;
16796 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16798 Jim_Obj *objPtr, *listObjPtr;
16799 int i;
16800 int idx;
16802 if (argc < 2) {
16803 Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
16804 return JIM_ERR;
16806 objPtr = argv[1];
16807 Jim_IncrRefCount(objPtr);
16808 for (i = 2; i < argc; i++) {
16809 listObjPtr = objPtr;
16810 if (Jim_GetIndex(interp, argv[i], &idx) != JIM_OK) {
16811 Jim_DecrRefCount(interp, listObjPtr);
16812 return JIM_ERR;
16814 if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_NONE) != JIM_OK) {
16815 Jim_DecrRefCount(interp, listObjPtr);
16816 Jim_SetEmptyResult(interp);
16817 return JIM_OK;
16819 Jim_IncrRefCount(objPtr);
16820 Jim_DecrRefCount(interp, listObjPtr);
16822 Jim_SetResult(interp, objPtr);
16823 Jim_DecrRefCount(interp, objPtr);
16824 return JIM_OK;
16828 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16830 if (argc != 2) {
16831 Jim_WrongNumArgs(interp, 1, argv, "list");
16832 return JIM_ERR;
16834 Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
16835 return JIM_OK;
16839 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16841 static const char * const options[] = {
16842 "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
16843 NULL
16845 enum
16846 { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
16847 OPT_COMMAND };
16848 int i;
16849 int opt_bool = 0;
16850 int opt_not = 0;
16851 int opt_nocase = 0;
16852 int opt_all = 0;
16853 int opt_inline = 0;
16854 int opt_match = OPT_EXACT;
16855 int listlen;
16856 int rc = JIM_OK;
16857 Jim_Obj *listObjPtr = NULL;
16858 Jim_Obj *commandObj = NULL;
16860 if (argc < 3) {
16861 wrongargs:
16862 Jim_WrongNumArgs(interp, 1, argv,
16863 "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? list value");
16864 return JIM_ERR;
16867 for (i = 1; i < argc - 2; i++) {
16868 int option;
16870 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
16871 return JIM_ERR;
16873 switch (option) {
16874 case OPT_BOOL:
16875 opt_bool = 1;
16876 opt_inline = 0;
16877 break;
16878 case OPT_NOT:
16879 opt_not = 1;
16880 break;
16881 case OPT_NOCASE:
16882 opt_nocase = 1;
16883 break;
16884 case OPT_INLINE:
16885 opt_inline = 1;
16886 opt_bool = 0;
16887 break;
16888 case OPT_ALL:
16889 opt_all = 1;
16890 break;
16891 case OPT_COMMAND:
16892 if (i >= argc - 2) {
16893 goto wrongargs;
16895 commandObj = argv[++i];
16897 case OPT_EXACT:
16898 case OPT_GLOB:
16899 case OPT_REGEXP:
16900 opt_match = option;
16901 break;
16905 argv += i;
16907 if (opt_all) {
16908 listObjPtr = Jim_NewListObj(interp, NULL, 0);
16910 if (opt_match == OPT_REGEXP) {
16911 commandObj = Jim_NewStringObj(interp, "regexp", -1);
16913 if (commandObj) {
16914 Jim_IncrRefCount(commandObj);
16917 listlen = Jim_ListLength(interp, argv[0]);
16918 for (i = 0; i < listlen; i++) {
16919 int eq = 0;
16920 Jim_Obj *objPtr = Jim_ListGetIndex(interp, argv[0], i);
16922 switch (opt_match) {
16923 case OPT_EXACT:
16924 eq = Jim_StringCompareObj(interp, argv[1], objPtr, opt_nocase) == 0;
16925 break;
16927 case OPT_GLOB:
16928 eq = Jim_StringMatchObj(interp, argv[1], objPtr, opt_nocase);
16929 break;
16931 case OPT_REGEXP:
16932 case OPT_COMMAND:
16933 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, opt_nocase);
16934 if (eq < 0) {
16935 if (listObjPtr) {
16936 Jim_FreeNewObj(interp, listObjPtr);
16938 rc = JIM_ERR;
16939 goto done;
16941 break;
16945 if (!eq && opt_bool && opt_not && !opt_all) {
16946 continue;
16949 if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
16951 Jim_Obj *resultObj;
16953 if (opt_bool) {
16954 resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
16956 else if (!opt_inline) {
16957 resultObj = Jim_NewIntObj(interp, i);
16959 else {
16960 resultObj = objPtr;
16963 if (opt_all) {
16964 Jim_ListAppendElement(interp, listObjPtr, resultObj);
16966 else {
16967 Jim_SetResult(interp, resultObj);
16968 goto done;
16973 if (opt_all) {
16974 Jim_SetResult(interp, listObjPtr);
16976 else {
16978 if (opt_bool) {
16979 Jim_SetResultBool(interp, opt_not);
16981 else if (!opt_inline) {
16982 Jim_SetResultInt(interp, -1);
16986 done:
16987 if (commandObj) {
16988 Jim_DecrRefCount(interp, commandObj);
16990 return rc;
16994 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16996 Jim_Obj *listObjPtr;
16997 int new_obj = 0;
16998 int i;
17000 if (argc < 2) {
17001 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
17002 return JIM_ERR;
17004 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
17005 if (!listObjPtr) {
17007 listObjPtr = Jim_NewListObj(interp, NULL, 0);
17008 new_obj = 1;
17010 else if (Jim_IsShared(listObjPtr)) {
17011 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
17012 new_obj = 1;
17014 for (i = 2; i < argc; i++)
17015 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
17016 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
17017 if (new_obj)
17018 Jim_FreeNewObj(interp, listObjPtr);
17019 return JIM_ERR;
17021 Jim_SetResult(interp, listObjPtr);
17022 return JIM_OK;
17026 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17028 int idx, len;
17029 Jim_Obj *listPtr;
17031 if (argc < 3) {
17032 Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
17033 return JIM_ERR;
17035 listPtr = argv[1];
17036 if (Jim_IsShared(listPtr))
17037 listPtr = Jim_DuplicateObj(interp, listPtr);
17038 if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
17039 goto err;
17040 len = Jim_ListLength(interp, listPtr);
17041 if (idx >= len)
17042 idx = len;
17043 else if (idx < 0)
17044 idx = len + idx + 1;
17045 Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
17046 Jim_SetResult(interp, listPtr);
17047 return JIM_OK;
17048 err:
17049 if (listPtr != argv[1]) {
17050 Jim_FreeNewObj(interp, listPtr);
17052 return JIM_ERR;
17056 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17058 int first, last, len, rangeLen;
17059 Jim_Obj *listObj;
17060 Jim_Obj *newListObj;
17062 if (argc < 4) {
17063 Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
17064 return JIM_ERR;
17066 if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
17067 Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
17068 return JIM_ERR;
17071 listObj = argv[1];
17072 len = Jim_ListLength(interp, listObj);
17074 first = JimRelToAbsIndex(len, first);
17075 last = JimRelToAbsIndex(len, last);
17076 JimRelToAbsRange(len, &first, &last, &rangeLen);
17080 if (first < len) {
17083 else if (len == 0) {
17085 first = 0;
17087 else {
17088 Jim_SetResultString(interp, "list doesn't contain element ", -1);
17089 Jim_AppendObj(interp, Jim_GetResult(interp), argv[2]);
17090 return JIM_ERR;
17094 newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
17097 ListInsertElements(newListObj, -1, argc - 4, argv + 4);
17100 ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
17102 Jim_SetResult(interp, newListObj);
17103 return JIM_OK;
17107 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17109 if (argc < 3) {
17110 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
17111 return JIM_ERR;
17113 else if (argc == 3) {
17115 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
17116 return JIM_ERR;
17117 Jim_SetResult(interp, argv[2]);
17118 return JIM_OK;
17120 return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
17124 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
17126 static const char * const options[] = {
17127 "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", NULL
17129 enum
17130 { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE };
17131 Jim_Obj *resObj;
17132 int i;
17133 int retCode;
17135 struct lsort_info info;
17137 if (argc < 2) {
17138 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
17139 return JIM_ERR;
17142 info.type = JIM_LSORT_ASCII;
17143 info.order = 1;
17144 info.indexed = 0;
17145 info.unique = 0;
17146 info.command = NULL;
17147 info.interp = interp;
17149 for (i = 1; i < (argc - 1); i++) {
17150 int option;
17152 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
17153 != JIM_OK)
17154 return JIM_ERR;
17155 switch (option) {
17156 case OPT_ASCII:
17157 info.type = JIM_LSORT_ASCII;
17158 break;
17159 case OPT_NOCASE:
17160 info.type = JIM_LSORT_NOCASE;
17161 break;
17162 case OPT_INTEGER:
17163 info.type = JIM_LSORT_INTEGER;
17164 break;
17165 case OPT_REAL:
17166 info.type = JIM_LSORT_REAL;
17167 break;
17168 case OPT_INCREASING:
17169 info.order = 1;
17170 break;
17171 case OPT_DECREASING:
17172 info.order = -1;
17173 break;
17174 case OPT_UNIQUE:
17175 info.unique = 1;
17176 break;
17177 case OPT_COMMAND:
17178 if (i >= (argc - 2)) {
17179 Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
17180 return JIM_ERR;
17182 info.type = JIM_LSORT_COMMAND;
17183 info.command = argv[i + 1];
17184 i++;
17185 break;
17186 case OPT_INDEX:
17187 if (i >= (argc - 2)) {
17188 Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
17189 return JIM_ERR;
17191 if (Jim_GetIndex(interp, argv[i + 1], &info.index) != JIM_OK) {
17192 return JIM_ERR;
17194 info.indexed = 1;
17195 i++;
17196 break;
17199 resObj = Jim_DuplicateObj(interp, argv[argc - 1]);
17200 retCode = ListSortElements(interp, resObj, &info);
17201 if (retCode == JIM_OK) {
17202 Jim_SetResult(interp, resObj);
17204 else {
17205 Jim_FreeNewObj(interp, resObj);
17207 return retCode;
17211 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17213 Jim_Obj *stringObjPtr;
17214 int i;
17216 if (argc < 2) {
17217 Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
17218 return JIM_ERR;
17220 if (argc == 2) {
17221 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17222 if (!stringObjPtr)
17223 return JIM_ERR;
17225 else {
17226 int new_obj = 0;
17227 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
17228 if (!stringObjPtr) {
17230 stringObjPtr = Jim_NewEmptyStringObj(interp);
17231 new_obj = 1;
17233 else if (Jim_IsShared(stringObjPtr)) {
17234 new_obj = 1;
17235 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
17237 for (i = 2; i < argc; i++) {
17238 Jim_AppendObj(interp, stringObjPtr, argv[i]);
17240 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
17241 if (new_obj) {
17242 Jim_FreeNewObj(interp, stringObjPtr);
17244 return JIM_ERR;
17247 Jim_SetResult(interp, stringObjPtr);
17248 return JIM_OK;
17252 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17254 #if !defined(JIM_DEBUG_COMMAND)
17255 Jim_SetResultString(interp, "unsupported", -1);
17256 return JIM_ERR;
17257 #endif
17261 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17263 int rc;
17265 if (argc < 2) {
17266 Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
17267 return JIM_ERR;
17270 if (argc == 2) {
17271 rc = Jim_EvalObj(interp, argv[1]);
17273 else {
17274 rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17277 if (rc == JIM_ERR) {
17279 interp->addStackTrace++;
17281 return rc;
17285 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17287 if (argc >= 2) {
17288 int retcode;
17289 Jim_CallFrame *savedCallFrame, *targetCallFrame;
17290 const char *str;
17293 savedCallFrame = interp->framePtr;
17296 str = Jim_String(argv[1]);
17297 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
17298 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17299 argc--;
17300 argv++;
17302 else {
17303 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17305 if (targetCallFrame == NULL) {
17306 return JIM_ERR;
17308 if (argc < 2) {
17309 Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
17310 return JIM_ERR;
17313 interp->framePtr = targetCallFrame;
17314 if (argc == 2) {
17315 retcode = Jim_EvalObj(interp, argv[1]);
17317 else {
17318 retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17320 interp->framePtr = savedCallFrame;
17321 return retcode;
17323 else {
17324 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
17325 return JIM_ERR;
17330 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17332 Jim_Obj *exprResultPtr;
17333 int retcode;
17335 if (argc == 2) {
17336 retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
17338 else if (argc > 2) {
17339 Jim_Obj *objPtr;
17341 objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
17342 Jim_IncrRefCount(objPtr);
17343 retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
17344 Jim_DecrRefCount(interp, objPtr);
17346 else {
17347 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
17348 return JIM_ERR;
17350 if (retcode != JIM_OK)
17351 return retcode;
17352 Jim_SetResult(interp, exprResultPtr);
17353 Jim_DecrRefCount(interp, exprResultPtr);
17354 return JIM_OK;
17358 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17360 if (argc != 1) {
17361 Jim_WrongNumArgs(interp, 1, argv, "");
17362 return JIM_ERR;
17364 return JIM_BREAK;
17368 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17370 if (argc != 1) {
17371 Jim_WrongNumArgs(interp, 1, argv, "");
17372 return JIM_ERR;
17374 return JIM_CONTINUE;
17378 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17380 int i;
17381 Jim_Obj *stackTraceObj = NULL;
17382 Jim_Obj *errorCodeObj = NULL;
17383 int returnCode = JIM_OK;
17384 long level = 1;
17386 for (i = 1; i < argc - 1; i += 2) {
17387 if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
17388 if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
17389 return JIM_ERR;
17392 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
17393 stackTraceObj = argv[i + 1];
17395 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
17396 errorCodeObj = argv[i + 1];
17398 else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
17399 if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
17400 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
17401 return JIM_ERR;
17404 else {
17405 break;
17409 if (i != argc - 1 && i != argc) {
17410 Jim_WrongNumArgs(interp, 1, argv,
17411 "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
17415 if (stackTraceObj && returnCode == JIM_ERR) {
17416 JimSetStackTrace(interp, stackTraceObj);
17419 if (errorCodeObj && returnCode == JIM_ERR) {
17420 Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
17422 interp->returnCode = returnCode;
17423 interp->returnLevel = level;
17425 if (i == argc - 1) {
17426 Jim_SetResult(interp, argv[i]);
17428 return JIM_RETURN;
17432 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17434 if (interp->framePtr->level == 0) {
17435 Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
17436 return JIM_ERR;
17438 else if (argc >= 2) {
17440 Jim_CallFrame *cf = interp->framePtr->parent;
17442 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17443 if (cmdPtr == NULL) {
17444 return JIM_ERR;
17447 JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));
17450 JimIncrCmdRefCount(cmdPtr);
17451 cf->tailcallCmd = cmdPtr;
17454 JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));
17456 cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
17457 Jim_IncrRefCount(cf->tailcallObj);
17460 return JIM_EVAL;
17462 return JIM_OK;
17465 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17467 Jim_Obj *cmdList;
17468 Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
17471 cmdList = Jim_DuplicateObj(interp, prefixListObj);
17472 Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
17474 return JimEvalObjList(interp, cmdList);
17477 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
17479 Jim_Obj *prefixListObj = privData;
17480 Jim_DecrRefCount(interp, prefixListObj);
17483 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17485 Jim_Obj *prefixListObj;
17486 const char *newname;
17488 if (argc < 3) {
17489 Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
17490 return JIM_ERR;
17493 prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
17494 Jim_IncrRefCount(prefixListObj);
17495 newname = Jim_String(argv[1]);
17496 if (newname[0] == ':' && newname[1] == ':') {
17497 while (*++newname == ':') {
17501 Jim_SetResult(interp, argv[1]);
17503 return Jim_CreateCommand(interp, newname, JimAliasCmd, prefixListObj, JimAliasCmdDelete);
17507 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17509 Jim_Cmd *cmd;
17511 if (argc != 4 && argc != 5) {
17512 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
17513 return JIM_ERR;
17516 if (JimValidName(interp, "procedure", argv[1]) != JIM_OK) {
17517 return JIM_ERR;
17520 if (argc == 4) {
17521 cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
17523 else {
17524 cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
17527 if (cmd) {
17529 Jim_Obj *qualifiedCmdNameObj;
17530 const char *cmdname = JimQualifyName(interp, Jim_String(argv[1]), &qualifiedCmdNameObj);
17532 JimCreateCommand(interp, cmdname, cmd);
17535 JimUpdateProcNamespace(interp, cmd, cmdname);
17537 JimFreeQualifiedName(interp, qualifiedCmdNameObj);
17540 Jim_SetResult(interp, argv[1]);
17541 return JIM_OK;
17543 return JIM_ERR;
17547 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17549 int retcode;
17551 if (argc < 2) {
17552 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17553 return JIM_ERR;
17557 interp->local++;
17558 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17559 interp->local--;
17563 if (retcode == 0) {
17564 Jim_Obj *cmdNameObj = Jim_GetResult(interp);
17566 if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
17567 return JIM_ERR;
17569 if (interp->framePtr->localCommands == NULL) {
17570 interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
17571 Jim_InitStack(interp->framePtr->localCommands);
17573 Jim_IncrRefCount(cmdNameObj);
17574 Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
17577 return retcode;
17581 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17583 if (argc < 2) {
17584 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17585 return JIM_ERR;
17587 else {
17588 int retcode;
17590 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17591 if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
17592 Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
17593 return JIM_ERR;
17596 cmdPtr->u.proc.upcall++;
17597 JimIncrCmdRefCount(cmdPtr);
17600 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17603 cmdPtr->u.proc.upcall--;
17604 JimDecrCmdRefCount(interp, cmdPtr);
17606 return retcode;
17611 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17613 if (argc < 2) {
17614 Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
17615 return JIM_ERR;
17617 else {
17618 int ret;
17619 Jim_Cmd *cmd;
17620 Jim_Obj *argListObjPtr;
17621 Jim_Obj *bodyObjPtr;
17622 Jim_Obj *nsObj = NULL;
17623 Jim_Obj **nargv;
17625 int len = Jim_ListLength(interp, argv[1]);
17626 if (len != 2 && len != 3) {
17627 Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
17628 return JIM_ERR;
17631 if (len == 3) {
17632 #ifdef jim_ext_namespace
17634 nsObj = JimQualifyNameObj(interp, Jim_ListGetIndex(interp, argv[1], 2));
17635 #else
17636 Jim_SetResultString(interp, "namespaces not enabled", -1);
17637 return JIM_ERR;
17638 #endif
17640 argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
17641 bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
17643 cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
17645 if (cmd) {
17647 nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
17648 nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
17649 Jim_IncrRefCount(nargv[0]);
17650 memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
17651 ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
17652 Jim_DecrRefCount(interp, nargv[0]);
17653 Jim_Free(nargv);
17655 JimDecrCmdRefCount(interp, cmd);
17656 return ret;
17658 return JIM_ERR;
17664 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17666 Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17667 return JIM_OK;
17671 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17673 int i;
17674 Jim_CallFrame *targetCallFrame;
17677 if (argc > 3 && (argc % 2 == 0)) {
17678 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17679 argc--;
17680 argv++;
17682 else {
17683 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17685 if (targetCallFrame == NULL) {
17686 return JIM_ERR;
17690 if (argc < 3) {
17691 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
17692 return JIM_ERR;
17696 for (i = 1; i < argc; i += 2) {
17697 if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
17698 return JIM_ERR;
17700 return JIM_OK;
17704 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17706 int i;
17708 if (argc < 2) {
17709 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
17710 return JIM_ERR;
17713 if (interp->framePtr->level == 0)
17714 return JIM_OK;
17715 for (i = 1; i < argc; i++) {
17717 const char *name = Jim_String(argv[i]);
17718 if (name[0] != ':' || name[1] != ':') {
17719 if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
17720 return JIM_ERR;
17723 return JIM_OK;
17726 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
17727 Jim_Obj *objPtr, int nocase)
17729 int numMaps;
17730 const char *str, *noMatchStart = NULL;
17731 int strLen, i;
17732 Jim_Obj *resultObjPtr;
17734 numMaps = Jim_ListLength(interp, mapListObjPtr);
17735 if (numMaps % 2) {
17736 Jim_SetResultString(interp, "list must contain an even number of elements", -1);
17737 return NULL;
17740 str = Jim_String(objPtr);
17741 strLen = Jim_Utf8Length(interp, objPtr);
17744 resultObjPtr = Jim_NewStringObj(interp, "", 0);
17745 while (strLen) {
17746 for (i = 0; i < numMaps; i += 2) {
17747 Jim_Obj *eachObjPtr;
17748 const char *k;
17749 int kl;
17751 eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
17752 k = Jim_String(eachObjPtr);
17753 kl = Jim_Utf8Length(interp, eachObjPtr);
17755 if (strLen >= kl && kl) {
17756 int rc;
17757 rc = JimStringCompareLen(str, k, kl, nocase);
17758 if (rc == 0) {
17759 if (noMatchStart) {
17760 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17761 noMatchStart = NULL;
17763 Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
17764 str += utf8_index(str, kl);
17765 strLen -= kl;
17766 break;
17770 if (i == numMaps) {
17771 int c;
17772 if (noMatchStart == NULL)
17773 noMatchStart = str;
17774 str += utf8_tounicode(str, &c);
17775 strLen--;
17778 if (noMatchStart) {
17779 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17781 return resultObjPtr;
17785 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17787 int len;
17788 int opt_case = 1;
17789 int option;
17790 static const char * const options[] = {
17791 "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace",
17792 "map", "repeat", "reverse", "index", "first", "last", "cat",
17793 "trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL
17795 enum
17797 OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_REPLACE,
17798 OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST, OPT_CAT,
17799 OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE
17801 static const char * const nocase_options[] = {
17802 "-nocase", NULL
17804 static const char * const nocase_length_options[] = {
17805 "-nocase", "-length", NULL
17808 if (argc < 2) {
17809 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
17810 return JIM_ERR;
17812 if (Jim_GetEnum(interp, argv[1], options, &option, NULL,
17813 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
17814 return Jim_CheckShowCommands(interp, argv[1], options);
17816 switch (option) {
17817 case OPT_LENGTH:
17818 case OPT_BYTELENGTH:
17819 if (argc != 3) {
17820 Jim_WrongNumArgs(interp, 2, argv, "string");
17821 return JIM_ERR;
17823 if (option == OPT_LENGTH) {
17824 len = Jim_Utf8Length(interp, argv[2]);
17826 else {
17827 len = Jim_Length(argv[2]);
17829 Jim_SetResultInt(interp, len);
17830 return JIM_OK;
17832 case OPT_CAT:{
17833 Jim_Obj *objPtr;
17834 if (argc == 3) {
17836 objPtr = argv[2];
17838 else {
17839 int i;
17841 objPtr = Jim_NewStringObj(interp, "", 0);
17843 for (i = 2; i < argc; i++) {
17844 Jim_AppendObj(interp, objPtr, argv[i]);
17847 Jim_SetResult(interp, objPtr);
17848 return JIM_OK;
17851 case OPT_COMPARE:
17852 case OPT_EQUAL:
17855 long opt_length = -1;
17856 int n = argc - 4;
17857 int i = 2;
17858 while (n > 0) {
17859 int subopt;
17860 if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
17861 JIM_ENUM_ABBREV) != JIM_OK) {
17862 badcompareargs:
17863 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? ?-length int? string1 string2");
17864 return JIM_ERR;
17866 if (subopt == 0) {
17868 opt_case = 0;
17869 n--;
17871 else {
17873 if (n < 2) {
17874 goto badcompareargs;
17876 if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
17877 return JIM_ERR;
17879 n -= 2;
17882 if (n) {
17883 goto badcompareargs;
17885 argv += argc - 2;
17886 if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
17888 Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
17890 else {
17891 if (opt_length >= 0) {
17892 n = JimStringCompareLen(Jim_String(argv[0]), Jim_String(argv[1]), opt_length, !opt_case);
17894 else {
17895 n = Jim_StringCompareObj(interp, argv[0], argv[1], !opt_case);
17897 Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
17899 return JIM_OK;
17902 case OPT_MATCH:
17903 if (argc != 4 &&
17904 (argc != 5 ||
17905 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17906 JIM_ENUM_ABBREV) != JIM_OK)) {
17907 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
17908 return JIM_ERR;
17910 if (opt_case == 0) {
17911 argv++;
17913 Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
17914 return JIM_OK;
17916 case OPT_MAP:{
17917 Jim_Obj *objPtr;
17919 if (argc != 4 &&
17920 (argc != 5 ||
17921 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17922 JIM_ENUM_ABBREV) != JIM_OK)) {
17923 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
17924 return JIM_ERR;
17927 if (opt_case == 0) {
17928 argv++;
17930 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
17931 if (objPtr == NULL) {
17932 return JIM_ERR;
17934 Jim_SetResult(interp, objPtr);
17935 return JIM_OK;
17938 case OPT_RANGE:
17939 case OPT_BYTERANGE:{
17940 Jim_Obj *objPtr;
17942 if (argc != 5) {
17943 Jim_WrongNumArgs(interp, 2, argv, "string first last");
17944 return JIM_ERR;
17946 if (option == OPT_RANGE) {
17947 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
17949 else
17951 objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
17954 if (objPtr == NULL) {
17955 return JIM_ERR;
17957 Jim_SetResult(interp, objPtr);
17958 return JIM_OK;
17961 case OPT_REPLACE:{
17962 Jim_Obj *objPtr;
17964 if (argc != 5 && argc != 6) {
17965 Jim_WrongNumArgs(interp, 2, argv, "string first last ?string?");
17966 return JIM_ERR;
17968 objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
17969 if (objPtr == NULL) {
17970 return JIM_ERR;
17972 Jim_SetResult(interp, objPtr);
17973 return JIM_OK;
17977 case OPT_REPEAT:{
17978 Jim_Obj *objPtr;
17979 jim_wide count;
17981 if (argc != 4) {
17982 Jim_WrongNumArgs(interp, 2, argv, "string count");
17983 return JIM_ERR;
17985 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK) {
17986 return JIM_ERR;
17988 objPtr = Jim_NewStringObj(interp, "", 0);
17989 if (count > 0) {
17990 while (count--) {
17991 Jim_AppendObj(interp, objPtr, argv[2]);
17994 Jim_SetResult(interp, objPtr);
17995 return JIM_OK;
17998 case OPT_REVERSE:{
17999 char *buf, *p;
18000 const char *str;
18001 int i;
18003 if (argc != 3) {
18004 Jim_WrongNumArgs(interp, 2, argv, "string");
18005 return JIM_ERR;
18008 str = Jim_GetString(argv[2], &len);
18009 buf = Jim_Alloc(len + 1);
18010 p = buf + len;
18011 *p = 0;
18012 for (i = 0; i < len; ) {
18013 int c;
18014 int l = utf8_tounicode(str, &c);
18015 memcpy(p - l, str, l);
18016 p -= l;
18017 i += l;
18018 str += l;
18020 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
18021 return JIM_OK;
18024 case OPT_INDEX:{
18025 int idx;
18026 const char *str;
18028 if (argc != 4) {
18029 Jim_WrongNumArgs(interp, 2, argv, "string index");
18030 return JIM_ERR;
18032 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
18033 return JIM_ERR;
18035 str = Jim_String(argv[2]);
18036 len = Jim_Utf8Length(interp, argv[2]);
18037 if (idx != INT_MIN && idx != INT_MAX) {
18038 idx = JimRelToAbsIndex(len, idx);
18040 if (idx < 0 || idx >= len || str == NULL) {
18041 Jim_SetResultString(interp, "", 0);
18043 else if (len == Jim_Length(argv[2])) {
18045 Jim_SetResultString(interp, str + idx, 1);
18047 else {
18048 int c;
18049 int i = utf8_index(str, idx);
18050 Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
18052 return JIM_OK;
18055 case OPT_FIRST:
18056 case OPT_LAST:{
18057 int idx = 0, l1, l2;
18058 const char *s1, *s2;
18060 if (argc != 4 && argc != 5) {
18061 Jim_WrongNumArgs(interp, 2, argv, "subString string ?index?");
18062 return JIM_ERR;
18064 s1 = Jim_String(argv[2]);
18065 s2 = Jim_String(argv[3]);
18066 l1 = Jim_Utf8Length(interp, argv[2]);
18067 l2 = Jim_Utf8Length(interp, argv[3]);
18068 if (argc == 5) {
18069 if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
18070 return JIM_ERR;
18072 idx = JimRelToAbsIndex(l2, idx);
18074 else if (option == OPT_LAST) {
18075 idx = l2;
18077 if (option == OPT_FIRST) {
18078 Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
18080 else {
18081 #ifdef JIM_UTF8
18082 Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
18083 #else
18084 Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
18085 #endif
18087 return JIM_OK;
18090 case OPT_TRIM:
18091 case OPT_TRIMLEFT:
18092 case OPT_TRIMRIGHT:{
18093 Jim_Obj *trimchars;
18095 if (argc != 3 && argc != 4) {
18096 Jim_WrongNumArgs(interp, 2, argv, "string ?trimchars?");
18097 return JIM_ERR;
18099 trimchars = (argc == 4 ? argv[3] : NULL);
18100 if (option == OPT_TRIM) {
18101 Jim_SetResult(interp, JimStringTrim(interp, argv[2], trimchars));
18103 else if (option == OPT_TRIMLEFT) {
18104 Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], trimchars));
18106 else if (option == OPT_TRIMRIGHT) {
18107 Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], trimchars));
18109 return JIM_OK;
18112 case OPT_TOLOWER:
18113 case OPT_TOUPPER:
18114 case OPT_TOTITLE:
18115 if (argc != 3) {
18116 Jim_WrongNumArgs(interp, 2, argv, "string");
18117 return JIM_ERR;
18119 if (option == OPT_TOLOWER) {
18120 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
18122 else if (option == OPT_TOUPPER) {
18123 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
18125 else {
18126 Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
18128 return JIM_OK;
18130 case OPT_IS:
18131 if (argc == 4 || (argc == 5 && Jim_CompareStringImmediate(interp, argv[3], "-strict"))) {
18132 return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
18134 Jim_WrongNumArgs(interp, 2, argv, "class ?-strict? str");
18135 return JIM_ERR;
18137 return JIM_OK;
18141 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18143 long i, count = 1;
18144 jim_wide start, elapsed;
18145 char buf[60];
18146 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
18148 if (argc < 2) {
18149 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
18150 return JIM_ERR;
18152 if (argc == 3) {
18153 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
18154 return JIM_ERR;
18156 if (count < 0)
18157 return JIM_OK;
18158 i = count;
18159 start = JimClock();
18160 while (i-- > 0) {
18161 int retval;
18163 retval = Jim_EvalObj(interp, argv[1]);
18164 if (retval != JIM_OK) {
18165 return retval;
18168 elapsed = JimClock() - start;
18169 sprintf(buf, fmt, count == 0 ? 0 : elapsed / count);
18170 Jim_SetResultString(interp, buf, -1);
18171 return JIM_OK;
18175 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18177 long exitCode = 0;
18179 if (argc > 2) {
18180 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
18181 return JIM_ERR;
18183 if (argc == 2) {
18184 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
18185 return JIM_ERR;
18187 interp->exitCode = exitCode;
18188 return JIM_EXIT;
18192 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18194 int exitCode = 0;
18195 int i;
18196 int sig = 0;
18199 jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
18200 static const int max_ignore_code = sizeof(ignore_mask) * 8;
18202 Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
18204 for (i = 1; i < argc - 1; i++) {
18205 const char *arg = Jim_String(argv[i]);
18206 jim_wide option;
18207 int ignore;
18210 if (strcmp(arg, "--") == 0) {
18211 i++;
18212 break;
18214 if (*arg != '-') {
18215 break;
18218 if (strncmp(arg, "-no", 3) == 0) {
18219 arg += 3;
18220 ignore = 1;
18222 else {
18223 arg++;
18224 ignore = 0;
18227 if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
18228 option = -1;
18230 if (option < 0) {
18231 option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
18233 if (option < 0) {
18234 goto wrongargs;
18237 if (ignore) {
18238 ignore_mask |= ((jim_wide)1 << option);
18240 else {
18241 ignore_mask &= (~((jim_wide)1 << option));
18245 argc -= i;
18246 if (argc < 1 || argc > 3) {
18247 wrongargs:
18248 Jim_WrongNumArgs(interp, 1, argv,
18249 "?-?no?code ... --? script ?resultVarName? ?optionVarName?");
18250 return JIM_ERR;
18252 argv += i;
18254 if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
18255 sig++;
18258 interp->signal_level += sig;
18259 if (Jim_CheckSignal(interp)) {
18261 exitCode = JIM_SIGNAL;
18263 else {
18264 exitCode = Jim_EvalObj(interp, argv[0]);
18266 interp->errorFlag = 0;
18268 interp->signal_level -= sig;
18271 if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
18273 return exitCode;
18276 if (sig && exitCode == JIM_SIGNAL) {
18278 if (interp->signal_set_result) {
18279 interp->signal_set_result(interp, interp->sigmask);
18281 else {
18282 Jim_SetResultInt(interp, interp->sigmask);
18284 interp->sigmask = 0;
18287 if (argc >= 2) {
18288 if (Jim_SetVariable(interp, argv[1], Jim_GetResult(interp)) != JIM_OK) {
18289 return JIM_ERR;
18291 if (argc == 3) {
18292 Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
18294 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
18295 Jim_ListAppendElement(interp, optListObj,
18296 Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
18297 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
18298 Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
18299 if (exitCode == JIM_ERR) {
18300 Jim_Obj *errorCode;
18301 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
18302 -1));
18303 Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
18305 errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
18306 if (errorCode) {
18307 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
18308 Jim_ListAppendElement(interp, optListObj, errorCode);
18311 if (Jim_SetVariable(interp, argv[2], optListObj) != JIM_OK) {
18312 return JIM_ERR;
18316 Jim_SetResultInt(interp, exitCode);
18317 return JIM_OK;
18322 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18324 if (argc != 3) {
18325 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
18326 return JIM_ERR;
18329 if (JimValidName(interp, "new procedure", argv[2])) {
18330 return JIM_ERR;
18333 return Jim_RenameCommand(interp, Jim_String(argv[1]), Jim_String(argv[2]));
18336 #define JIM_DICTMATCH_KEYS 0x0001
18337 #define JIM_DICTMATCH_VALUES 0x002
18339 int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
18341 Jim_HashEntry *he;
18342 Jim_Obj *listObjPtr;
18343 Jim_HashTableIterator htiter;
18345 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18346 return JIM_ERR;
18349 listObjPtr = Jim_NewListObj(interp, NULL, 0);
18351 JimInitHashTableIterator(objPtr->internalRep.ptr, &htiter);
18352 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18353 if (patternObj) {
18354 Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? (Jim_Obj *)he->key : Jim_GetHashEntryVal(he);
18355 if (!JimGlobMatch(Jim_String(patternObj), Jim_String(matchObj), 0)) {
18357 continue;
18360 if (return_types & JIM_DICTMATCH_KEYS) {
18361 Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key);
18363 if (return_types & JIM_DICTMATCH_VALUES) {
18364 Jim_ListAppendElement(interp, listObjPtr, Jim_GetHashEntryVal(he));
18368 Jim_SetResult(interp, listObjPtr);
18369 return JIM_OK;
18372 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
18374 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18375 return -1;
18377 return ((Jim_HashTable *)objPtr->internalRep.ptr)->used;
18380 Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
18382 Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
18383 int i;
18385 JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));
18389 for (i = 0; i < objc; i++) {
18390 Jim_HashTable *ht;
18391 Jim_HashTableIterator htiter;
18392 Jim_HashEntry *he;
18394 if (SetDictFromAny(interp, objv[i]) != JIM_OK) {
18395 Jim_FreeNewObj(interp, objPtr);
18396 return NULL;
18398 ht = objv[i]->internalRep.ptr;
18399 JimInitHashTableIterator(ht, &htiter);
18400 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18401 Jim_ReplaceHashEntry(objPtr->internalRep.ptr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he));
18404 return objPtr;
18407 int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
18409 Jim_HashTable *ht;
18410 unsigned int i;
18411 char buffer[100];
18412 int sum = 0;
18413 int nonzero_count = 0;
18414 Jim_Obj *output;
18415 int bucket_counts[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
18417 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18418 return JIM_ERR;
18421 ht = (Jim_HashTable *)objPtr->internalRep.ptr;
18424 snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets\n", ht->used, ht->size);
18425 output = Jim_NewStringObj(interp, buffer, -1);
18427 for (i = 0; i < ht->size; i++) {
18428 Jim_HashEntry *he = ht->table[i];
18429 int entries = 0;
18430 while (he) {
18431 entries++;
18432 he = he->next;
18434 if (entries > 9) {
18435 bucket_counts[10]++;
18437 else {
18438 bucket_counts[entries]++;
18440 if (entries) {
18441 sum += entries;
18442 nonzero_count++;
18445 for (i = 0; i < 10; i++) {
18446 snprintf(buffer, sizeof(buffer), "number of buckets with %d entries: %d\n", i, bucket_counts[i]);
18447 Jim_AppendString(interp, output, buffer, -1);
18449 snprintf(buffer, sizeof(buffer), "number of buckets with 10 or more entries: %d\n", bucket_counts[10]);
18450 Jim_AppendString(interp, output, buffer, -1);
18451 snprintf(buffer, sizeof(buffer), "average search distance for entry: %.1f", nonzero_count ? (double)sum / nonzero_count : 0.0);
18452 Jim_AppendString(interp, output, buffer, -1);
18453 Jim_SetResult(interp, output);
18454 return JIM_OK;
18457 static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
18459 Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
18461 Jim_AppendString(interp, prefixObj, " ", 1);
18462 Jim_AppendString(interp, prefixObj, subcmd, -1);
18464 return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
18467 static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj)
18469 int i;
18470 Jim_Obj *objPtr;
18471 Jim_Obj *dictObj;
18472 Jim_Obj **dictValues;
18473 int len;
18474 int ret = JIM_OK;
18477 dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
18478 if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
18479 return JIM_ERR;
18482 if (Jim_DictPairs(interp, objPtr, &dictValues, &len) == JIM_ERR) {
18483 return JIM_ERR;
18485 for (i = 0; i < len; i += 2) {
18486 if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
18487 Jim_Free(dictValues);
18488 return JIM_ERR;
18493 if (Jim_Length(scriptObj)) {
18494 ret = Jim_EvalObj(interp, scriptObj);
18497 if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {
18499 Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
18500 for (i = 0; i < keyc; i++) {
18501 newkeyv[i] = keyv[i];
18504 for (i = 0; i < len; i += 2) {
18506 objPtr = Jim_GetVariable(interp, dictValues[i], 0);
18507 newkeyv[keyc] = dictValues[i];
18508 Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, 0);
18510 Jim_Free(newkeyv);
18514 Jim_Free(dictValues);
18516 return ret;
18520 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18522 Jim_Obj *objPtr;
18523 int types = JIM_DICTMATCH_KEYS;
18524 int option;
18525 static const char * const options[] = {
18526 "create", "get", "set", "unset", "exists", "keys", "size", "info",
18527 "merge", "with", "append", "lappend", "incr", "remove", "values", "for",
18528 "replace", "update", NULL
18530 enum
18532 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXISTS, OPT_KEYS, OPT_SIZE, OPT_INFO,
18533 OPT_MERGE, OPT_WITH, OPT_APPEND, OPT_LAPPEND, OPT_INCR, OPT_REMOVE, OPT_VALUES, OPT_FOR,
18534 OPT_REPLACE, OPT_UPDATE,
18537 if (argc < 2) {
18538 Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arguments ...?");
18539 return JIM_ERR;
18542 if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) {
18543 return Jim_CheckShowCommands(interp, argv[1], options);
18546 switch (option) {
18547 case OPT_GET:
18548 if (argc < 3) {
18549 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?key ...?");
18550 return JIM_ERR;
18552 if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
18553 JIM_ERRMSG) != JIM_OK) {
18554 return JIM_ERR;
18556 Jim_SetResult(interp, objPtr);
18557 return JIM_OK;
18559 case OPT_SET:
18560 if (argc < 5) {
18561 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
18562 return JIM_ERR;
18564 return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
18566 case OPT_EXISTS:
18567 if (argc < 4) {
18568 Jim_WrongNumArgs(interp, 2, argv, "dictionary key ?key ...?");
18569 return JIM_ERR;
18571 else {
18572 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_ERRMSG);
18573 if (rc < 0) {
18574 return JIM_ERR;
18576 Jim_SetResultBool(interp, rc == JIM_OK);
18577 return JIM_OK;
18580 case OPT_UNSET:
18581 if (argc < 4) {
18582 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
18583 return JIM_ERR;
18585 if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, 0) != JIM_OK) {
18586 return JIM_ERR;
18588 return JIM_OK;
18590 case OPT_VALUES:
18591 types = JIM_DICTMATCH_VALUES;
18593 case OPT_KEYS:
18594 if (argc != 3 && argc != 4) {
18595 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?pattern?");
18596 return JIM_ERR;
18598 return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);
18600 case OPT_SIZE:
18601 if (argc != 3) {
18602 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18603 return JIM_ERR;
18605 else if (Jim_DictSize(interp, argv[2]) < 0) {
18606 return JIM_ERR;
18608 Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
18609 return JIM_OK;
18611 case OPT_MERGE:
18612 if (argc == 2) {
18613 return JIM_OK;
18615 objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
18616 if (objPtr == NULL) {
18617 return JIM_ERR;
18619 Jim_SetResult(interp, objPtr);
18620 return JIM_OK;
18622 case OPT_UPDATE:
18623 if (argc < 6 || argc % 2) {
18625 argc = 2;
18627 break;
18629 case OPT_CREATE:
18630 if (argc % 2) {
18631 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
18632 return JIM_ERR;
18634 objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
18635 Jim_SetResult(interp, objPtr);
18636 return JIM_OK;
18638 case OPT_INFO:
18639 if (argc != 3) {
18640 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18641 return JIM_ERR;
18643 return Jim_DictInfo(interp, argv[2]);
18645 case OPT_WITH:
18646 if (argc < 4) {
18647 Jim_WrongNumArgs(interp, 2, argv, "dictVar ?key ...? script");
18648 return JIM_ERR;
18650 return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
18653 return Jim_EvalEnsemble(interp, "dict", options[option], argc - 2, argv + 2);
18657 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18659 static const char * const options[] = {
18660 "-nobackslashes", "-nocommands", "-novariables", NULL
18662 enum
18663 { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
18664 int i;
18665 int flags = JIM_SUBST_FLAG;
18666 Jim_Obj *objPtr;
18668 if (argc < 2) {
18669 Jim_WrongNumArgs(interp, 1, argv, "?options? string");
18670 return JIM_ERR;
18672 for (i = 1; i < (argc - 1); i++) {
18673 int option;
18675 if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
18676 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18677 return JIM_ERR;
18679 switch (option) {
18680 case OPT_NOBACKSLASHES:
18681 flags |= JIM_SUBST_NOESC;
18682 break;
18683 case OPT_NOCOMMANDS:
18684 flags |= JIM_SUBST_NOCMD;
18685 break;
18686 case OPT_NOVARIABLES:
18687 flags |= JIM_SUBST_NOVAR;
18688 break;
18691 if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
18692 return JIM_ERR;
18694 Jim_SetResult(interp, objPtr);
18695 return JIM_OK;
18699 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18701 int cmd;
18702 Jim_Obj *objPtr;
18703 int mode = 0;
18705 static const char * const commands[] = {
18706 "body", "statics", "commands", "procs", "channels", "exists", "globals", "level", "frame", "locals",
18707 "vars", "version", "patchlevel", "complete", "args", "hostname",
18708 "script", "source", "stacktrace", "nameofexecutable", "returncodes",
18709 "references", "alias", NULL
18711 enum
18712 { INFO_BODY, INFO_STATICS, INFO_COMMANDS, INFO_PROCS, INFO_CHANNELS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
18713 INFO_FRAME, INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_PATCHLEVEL, INFO_COMPLETE, INFO_ARGS,
18714 INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE,
18715 INFO_RETURNCODES, INFO_REFERENCES, INFO_ALIAS,
18718 #ifdef jim_ext_namespace
18719 int nons = 0;
18721 if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
18723 argc--;
18724 argv++;
18725 nons = 1;
18727 #endif
18729 if (argc < 2) {
18730 Jim_WrongNumArgs(interp, 1, argv, "subcommand ?args ...?");
18731 return JIM_ERR;
18733 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18734 return Jim_CheckShowCommands(interp, argv[1], commands);
18738 switch (cmd) {
18739 case INFO_EXISTS:
18740 if (argc != 3) {
18741 Jim_WrongNumArgs(interp, 2, argv, "varName");
18742 return JIM_ERR;
18744 Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
18745 break;
18747 case INFO_ALIAS:{
18748 Jim_Cmd *cmdPtr;
18750 if (argc != 3) {
18751 Jim_WrongNumArgs(interp, 2, argv, "command");
18752 return JIM_ERR;
18754 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18755 return JIM_ERR;
18757 if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
18758 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
18759 return JIM_ERR;
18761 Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
18762 return JIM_OK;
18765 case INFO_CHANNELS:
18766 mode++;
18767 #ifndef jim_ext_aio
18768 Jim_SetResultString(interp, "aio not enabled", -1);
18769 return JIM_ERR;
18770 #endif
18772 case INFO_PROCS:
18773 mode++;
18775 case INFO_COMMANDS:
18777 if (argc != 2 && argc != 3) {
18778 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18779 return JIM_ERR;
18781 #ifdef jim_ext_namespace
18782 if (!nons) {
18783 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18784 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18787 #endif
18788 Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
18789 break;
18791 case INFO_VARS:
18792 mode++;
18794 case INFO_LOCALS:
18795 mode++;
18797 case INFO_GLOBALS:
18799 if (argc != 2 && argc != 3) {
18800 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18801 return JIM_ERR;
18803 #ifdef jim_ext_namespace
18804 if (!nons) {
18805 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18806 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18809 #endif
18810 Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
18811 break;
18813 case INFO_SCRIPT:
18814 if (argc != 2) {
18815 Jim_WrongNumArgs(interp, 2, argv, "");
18816 return JIM_ERR;
18818 Jim_SetResult(interp, JimGetScript(interp, interp->currentScriptObj)->fileNameObj);
18819 break;
18821 case INFO_SOURCE:{
18822 jim_wide line;
18823 Jim_Obj *resObjPtr;
18824 Jim_Obj *fileNameObj;
18826 if (argc != 3 && argc != 5) {
18827 Jim_WrongNumArgs(interp, 2, argv, "source ?filename line?");
18828 return JIM_ERR;
18830 if (argc == 5) {
18831 if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
18832 return JIM_ERR;
18834 resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
18835 JimSetSourceInfo(interp, resObjPtr, argv[3], line);
18837 else {
18838 if (argv[2]->typePtr == &sourceObjType) {
18839 fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj;
18840 line = argv[2]->internalRep.sourceValue.lineNumber;
18842 else if (argv[2]->typePtr == &scriptObjType) {
18843 ScriptObj *script = JimGetScript(interp, argv[2]);
18844 fileNameObj = script->fileNameObj;
18845 line = script->firstline;
18847 else {
18848 fileNameObj = interp->emptyObj;
18849 line = 1;
18851 resObjPtr = Jim_NewListObj(interp, NULL, 0);
18852 Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
18853 Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
18855 Jim_SetResult(interp, resObjPtr);
18856 break;
18859 case INFO_STACKTRACE:
18860 Jim_SetResult(interp, interp->stackTrace);
18861 break;
18863 case INFO_LEVEL:
18864 case INFO_FRAME:
18865 switch (argc) {
18866 case 2:
18867 Jim_SetResultInt(interp, interp->framePtr->level);
18868 break;
18870 case 3:
18871 if (JimInfoLevel(interp, argv[2], &objPtr, cmd == INFO_LEVEL) != JIM_OK) {
18872 return JIM_ERR;
18874 Jim_SetResult(interp, objPtr);
18875 break;
18877 default:
18878 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
18879 return JIM_ERR;
18881 break;
18883 case INFO_BODY:
18884 case INFO_STATICS:
18885 case INFO_ARGS:{
18886 Jim_Cmd *cmdPtr;
18888 if (argc != 3) {
18889 Jim_WrongNumArgs(interp, 2, argv, "procname");
18890 return JIM_ERR;
18892 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18893 return JIM_ERR;
18895 if (!cmdPtr->isproc) {
18896 Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
18897 return JIM_ERR;
18899 switch (cmd) {
18900 case INFO_BODY:
18901 Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
18902 break;
18903 case INFO_ARGS:
18904 Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
18905 break;
18906 case INFO_STATICS:
18907 if (cmdPtr->u.proc.staticVars) {
18908 Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
18909 NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES));
18911 break;
18913 break;
18916 case INFO_VERSION:
18917 case INFO_PATCHLEVEL:{
18918 char buf[(JIM_INTEGER_SPACE * 2) + 1];
18920 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
18921 Jim_SetResultString(interp, buf, -1);
18922 break;
18925 case INFO_COMPLETE:
18926 if (argc != 3 && argc != 4) {
18927 Jim_WrongNumArgs(interp, 2, argv, "script ?missing?");
18928 return JIM_ERR;
18930 else {
18931 char missing;
18933 Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing));
18934 if (missing != ' ' && argc == 4) {
18935 Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
18938 break;
18940 case INFO_HOSTNAME:
18942 return Jim_Eval(interp, "os.gethostname");
18944 case INFO_NAMEOFEXECUTABLE:
18946 return Jim_Eval(interp, "{info nameofexecutable}");
18948 case INFO_RETURNCODES:
18949 if (argc == 2) {
18950 int i;
18951 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
18953 for (i = 0; jimReturnCodes[i]; i++) {
18954 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
18955 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
18956 jimReturnCodes[i], -1));
18959 Jim_SetResult(interp, listObjPtr);
18961 else if (argc == 3) {
18962 long code;
18963 const char *name;
18965 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
18966 return JIM_ERR;
18968 name = Jim_ReturnCode(code);
18969 if (*name == '?') {
18970 Jim_SetResultInt(interp, code);
18972 else {
18973 Jim_SetResultString(interp, name, -1);
18976 else {
18977 Jim_WrongNumArgs(interp, 2, argv, "?code?");
18978 return JIM_ERR;
18980 break;
18981 case INFO_REFERENCES:
18982 #ifdef JIM_REFERENCES
18983 return JimInfoReferences(interp, argc, argv);
18984 #else
18985 Jim_SetResultString(interp, "not supported", -1);
18986 return JIM_ERR;
18987 #endif
18989 return JIM_OK;
18993 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18995 Jim_Obj *objPtr;
18996 int result = 0;
18998 static const char * const options[] = {
18999 "-command", "-proc", "-alias", "-var", NULL
19001 enum
19003 OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
19005 int option;
19007 if (argc == 2) {
19008 option = OPT_VAR;
19009 objPtr = argv[1];
19011 else if (argc == 3) {
19012 if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
19013 return JIM_ERR;
19015 objPtr = argv[2];
19017 else {
19018 Jim_WrongNumArgs(interp, 1, argv, "?option? name");
19019 return JIM_ERR;
19022 if (option == OPT_VAR) {
19023 result = Jim_GetVariable(interp, objPtr, 0) != NULL;
19025 else {
19027 Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
19029 if (cmd) {
19030 switch (option) {
19031 case OPT_COMMAND:
19032 result = 1;
19033 break;
19035 case OPT_ALIAS:
19036 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
19037 break;
19039 case OPT_PROC:
19040 result = cmd->isproc;
19041 break;
19045 Jim_SetResultBool(interp, result);
19046 return JIM_OK;
19050 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19052 const char *str, *splitChars, *noMatchStart;
19053 int splitLen, strLen;
19054 Jim_Obj *resObjPtr;
19055 int c;
19056 int len;
19058 if (argc != 2 && argc != 3) {
19059 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
19060 return JIM_ERR;
19063 str = Jim_GetString(argv[1], &len);
19064 if (len == 0) {
19065 return JIM_OK;
19067 strLen = Jim_Utf8Length(interp, argv[1]);
19070 if (argc == 2) {
19071 splitChars = " \n\t\r";
19072 splitLen = 4;
19074 else {
19075 splitChars = Jim_String(argv[2]);
19076 splitLen = Jim_Utf8Length(interp, argv[2]);
19079 noMatchStart = str;
19080 resObjPtr = Jim_NewListObj(interp, NULL, 0);
19083 if (splitLen) {
19084 Jim_Obj *objPtr;
19085 while (strLen--) {
19086 const char *sc = splitChars;
19087 int scLen = splitLen;
19088 int sl = utf8_tounicode(str, &c);
19089 while (scLen--) {
19090 int pc;
19091 sc += utf8_tounicode(sc, &pc);
19092 if (c == pc) {
19093 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
19094 Jim_ListAppendElement(interp, resObjPtr, objPtr);
19095 noMatchStart = str + sl;
19096 break;
19099 str += sl;
19101 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
19102 Jim_ListAppendElement(interp, resObjPtr, objPtr);
19104 else {
19105 Jim_Obj **commonObj = NULL;
19106 #define NUM_COMMON (128 - 9)
19107 while (strLen--) {
19108 int n = utf8_tounicode(str, &c);
19109 #ifdef JIM_OPTIMIZATION
19110 if (c >= 9 && c < 128) {
19112 c -= 9;
19113 if (!commonObj) {
19114 commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
19115 memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
19117 if (!commonObj[c]) {
19118 commonObj[c] = Jim_NewStringObj(interp, str, 1);
19120 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
19121 str++;
19122 continue;
19124 #endif
19125 Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
19126 str += n;
19128 Jim_Free(commonObj);
19131 Jim_SetResult(interp, resObjPtr);
19132 return JIM_OK;
19136 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19138 const char *joinStr;
19139 int joinStrLen;
19141 if (argc != 2 && argc != 3) {
19142 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
19143 return JIM_ERR;
19146 if (argc == 2) {
19147 joinStr = " ";
19148 joinStrLen = 1;
19150 else {
19151 joinStr = Jim_GetString(argv[2], &joinStrLen);
19153 Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
19154 return JIM_OK;
19158 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19160 Jim_Obj *objPtr;
19162 if (argc < 2) {
19163 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
19164 return JIM_ERR;
19166 objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
19167 if (objPtr == NULL)
19168 return JIM_ERR;
19169 Jim_SetResult(interp, objPtr);
19170 return JIM_OK;
19174 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19176 Jim_Obj *listPtr, **outVec;
19177 int outc, i;
19179 if (argc < 3) {
19180 Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
19181 return JIM_ERR;
19183 if (argv[2]->typePtr != &scanFmtStringObjType)
19184 SetScanFmtFromAny(interp, argv[2]);
19185 if (FormatGetError(argv[2]) != 0) {
19186 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
19187 return JIM_ERR;
19189 if (argc > 3) {
19190 int maxPos = FormatGetMaxPos(argv[2]);
19191 int count = FormatGetCnvCount(argv[2]);
19193 if (maxPos > argc - 3) {
19194 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
19195 return JIM_ERR;
19197 else if (count > argc - 3) {
19198 Jim_SetResultString(interp, "different numbers of variable names and "
19199 "field specifiers", -1);
19200 return JIM_ERR;
19202 else if (count < argc - 3) {
19203 Jim_SetResultString(interp, "variable is not assigned by any "
19204 "conversion specifiers", -1);
19205 return JIM_ERR;
19208 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
19209 if (listPtr == 0)
19210 return JIM_ERR;
19211 if (argc > 3) {
19212 int rc = JIM_OK;
19213 int count = 0;
19215 if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
19216 int len = Jim_ListLength(interp, listPtr);
19218 if (len != 0) {
19219 JimListGetElements(interp, listPtr, &outc, &outVec);
19220 for (i = 0; i < outc; ++i) {
19221 if (Jim_Length(outVec[i]) > 0) {
19222 ++count;
19223 if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
19224 rc = JIM_ERR;
19229 Jim_FreeNewObj(interp, listPtr);
19231 else {
19232 count = -1;
19234 if (rc == JIM_OK) {
19235 Jim_SetResultInt(interp, count);
19237 return rc;
19239 else {
19240 if (listPtr == (Jim_Obj *)EOF) {
19241 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
19242 return JIM_OK;
19244 Jim_SetResult(interp, listPtr);
19246 return JIM_OK;
19250 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19252 if (argc != 2 && argc != 3) {
19253 Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
19254 return JIM_ERR;
19256 Jim_SetResult(interp, argv[1]);
19257 if (argc == 3) {
19258 JimSetStackTrace(interp, argv[2]);
19259 return JIM_ERR;
19261 interp->addStackTrace++;
19262 return JIM_ERR;
19266 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19268 Jim_Obj *objPtr;
19270 if (argc != 4) {
19271 Jim_WrongNumArgs(interp, 1, argv, "list first last");
19272 return JIM_ERR;
19274 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
19275 return JIM_ERR;
19276 Jim_SetResult(interp, objPtr);
19277 return JIM_OK;
19281 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19283 Jim_Obj *objPtr;
19284 long count;
19286 if (argc < 2 || Jim_GetLong(interp, argv[1], &count) != JIM_OK || count < 0) {
19287 Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
19288 return JIM_ERR;
19291 if (count == 0 || argc == 2) {
19292 return JIM_OK;
19295 argc -= 2;
19296 argv += 2;
19298 objPtr = Jim_NewListObj(interp, argv, argc);
19299 while (--count) {
19300 ListInsertElements(objPtr, -1, argc, argv);
19303 Jim_SetResult(interp, objPtr);
19304 return JIM_OK;
19307 char **Jim_GetEnviron(void)
19309 #if defined(HAVE__NSGETENVIRON)
19310 return *_NSGetEnviron();
19311 #else
19312 #if !defined(NO_ENVIRON_EXTERN)
19313 extern char **environ;
19314 #endif
19316 return environ;
19317 #endif
19320 void Jim_SetEnviron(char **env)
19322 #if defined(HAVE__NSGETENVIRON)
19323 *_NSGetEnviron() = env;
19324 #else
19325 #if !defined(NO_ENVIRON_EXTERN)
19326 extern char **environ;
19327 #endif
19329 environ = env;
19330 #endif
19334 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19336 const char *key;
19337 const char *val;
19339 if (argc == 1) {
19340 char **e = Jim_GetEnviron();
19342 int i;
19343 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
19345 for (i = 0; e[i]; i++) {
19346 const char *equals = strchr(e[i], '=');
19348 if (equals) {
19349 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
19350 equals - e[i]));
19351 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
19355 Jim_SetResult(interp, listObjPtr);
19356 return JIM_OK;
19359 if (argc < 2) {
19360 Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
19361 return JIM_ERR;
19363 key = Jim_String(argv[1]);
19364 val = getenv(key);
19365 if (val == NULL) {
19366 if (argc < 3) {
19367 Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
19368 return JIM_ERR;
19370 val = Jim_String(argv[2]);
19372 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
19373 return JIM_OK;
19377 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19379 int retval;
19381 if (argc != 2) {
19382 Jim_WrongNumArgs(interp, 1, argv, "fileName");
19383 return JIM_ERR;
19385 retval = Jim_EvalFile(interp, Jim_String(argv[1]));
19386 if (retval == JIM_RETURN)
19387 return JIM_OK;
19388 return retval;
19392 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19394 Jim_Obj *revObjPtr, **ele;
19395 int len;
19397 if (argc != 2) {
19398 Jim_WrongNumArgs(interp, 1, argv, "list");
19399 return JIM_ERR;
19401 JimListGetElements(interp, argv[1], &len, &ele);
19402 len--;
19403 revObjPtr = Jim_NewListObj(interp, NULL, 0);
19404 while (len >= 0)
19405 ListAppendElement(revObjPtr, ele[len--]);
19406 Jim_SetResult(interp, revObjPtr);
19407 return JIM_OK;
19410 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
19412 jim_wide len;
19414 if (step == 0)
19415 return -1;
19416 if (start == end)
19417 return 0;
19418 else if (step > 0 && start > end)
19419 return -1;
19420 else if (step < 0 && end > start)
19421 return -1;
19422 len = end - start;
19423 if (len < 0)
19424 len = -len;
19425 if (step < 0)
19426 step = -step;
19427 len = 1 + ((len - 1) / step);
19428 if (len > INT_MAX)
19429 len = INT_MAX;
19430 return (int)((len < 0) ? -1 : len);
19434 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19436 jim_wide start = 0, end, step = 1;
19437 int len, i;
19438 Jim_Obj *objPtr;
19440 if (argc < 2 || argc > 4) {
19441 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
19442 return JIM_ERR;
19444 if (argc == 2) {
19445 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
19446 return JIM_ERR;
19448 else {
19449 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
19450 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
19451 return JIM_ERR;
19452 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
19453 return JIM_ERR;
19455 if ((len = JimRangeLen(start, end, step)) == -1) {
19456 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
19457 return JIM_ERR;
19459 objPtr = Jim_NewListObj(interp, NULL, 0);
19460 for (i = 0; i < len; i++)
19461 ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
19462 Jim_SetResult(interp, objPtr);
19463 return JIM_OK;
19467 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19469 jim_wide min = 0, max = 0, len, maxMul;
19471 if (argc < 1 || argc > 3) {
19472 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
19473 return JIM_ERR;
19475 if (argc == 1) {
19476 max = JIM_WIDE_MAX;
19477 } else if (argc == 2) {
19478 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
19479 return JIM_ERR;
19480 } else if (argc == 3) {
19481 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
19482 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
19483 return JIM_ERR;
19485 len = max-min;
19486 if (len < 0) {
19487 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
19488 return JIM_ERR;
19490 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
19491 while (1) {
19492 jim_wide r;
19494 JimRandomBytes(interp, &r, sizeof(jim_wide));
19495 if (r < 0 || r >= maxMul) continue;
19496 r = (len == 0) ? 0 : r%len;
19497 Jim_SetResultInt(interp, min+r);
19498 return JIM_OK;
19502 static const struct {
19503 const char *name;
19504 Jim_CmdProc *cmdProc;
19505 } Jim_CoreCommandsTable[] = {
19506 {"alias", Jim_AliasCoreCommand},
19507 {"set", Jim_SetCoreCommand},
19508 {"unset", Jim_UnsetCoreCommand},
19509 {"puts", Jim_PutsCoreCommand},
19510 {"+", Jim_AddCoreCommand},
19511 {"*", Jim_MulCoreCommand},
19512 {"-", Jim_SubCoreCommand},
19513 {"/", Jim_DivCoreCommand},
19514 {"incr", Jim_IncrCoreCommand},
19515 {"while", Jim_WhileCoreCommand},
19516 {"loop", Jim_LoopCoreCommand},
19517 {"for", Jim_ForCoreCommand},
19518 {"foreach", Jim_ForeachCoreCommand},
19519 {"lmap", Jim_LmapCoreCommand},
19520 {"lassign", Jim_LassignCoreCommand},
19521 {"if", Jim_IfCoreCommand},
19522 {"switch", Jim_SwitchCoreCommand},
19523 {"list", Jim_ListCoreCommand},
19524 {"lindex", Jim_LindexCoreCommand},
19525 {"lset", Jim_LsetCoreCommand},
19526 {"lsearch", Jim_LsearchCoreCommand},
19527 {"llength", Jim_LlengthCoreCommand},
19528 {"lappend", Jim_LappendCoreCommand},
19529 {"linsert", Jim_LinsertCoreCommand},
19530 {"lreplace", Jim_LreplaceCoreCommand},
19531 {"lsort", Jim_LsortCoreCommand},
19532 {"append", Jim_AppendCoreCommand},
19533 {"debug", Jim_DebugCoreCommand},
19534 {"eval", Jim_EvalCoreCommand},
19535 {"uplevel", Jim_UplevelCoreCommand},
19536 {"expr", Jim_ExprCoreCommand},
19537 {"break", Jim_BreakCoreCommand},
19538 {"continue", Jim_ContinueCoreCommand},
19539 {"proc", Jim_ProcCoreCommand},
19540 {"concat", Jim_ConcatCoreCommand},
19541 {"return", Jim_ReturnCoreCommand},
19542 {"upvar", Jim_UpvarCoreCommand},
19543 {"global", Jim_GlobalCoreCommand},
19544 {"string", Jim_StringCoreCommand},
19545 {"time", Jim_TimeCoreCommand},
19546 {"exit", Jim_ExitCoreCommand},
19547 {"catch", Jim_CatchCoreCommand},
19548 #ifdef JIM_REFERENCES
19549 {"ref", Jim_RefCoreCommand},
19550 {"getref", Jim_GetrefCoreCommand},
19551 {"setref", Jim_SetrefCoreCommand},
19552 {"finalize", Jim_FinalizeCoreCommand},
19553 {"collect", Jim_CollectCoreCommand},
19554 #endif
19555 {"rename", Jim_RenameCoreCommand},
19556 {"dict", Jim_DictCoreCommand},
19557 {"subst", Jim_SubstCoreCommand},
19558 {"info", Jim_InfoCoreCommand},
19559 {"exists", Jim_ExistsCoreCommand},
19560 {"split", Jim_SplitCoreCommand},
19561 {"join", Jim_JoinCoreCommand},
19562 {"format", Jim_FormatCoreCommand},
19563 {"scan", Jim_ScanCoreCommand},
19564 {"error", Jim_ErrorCoreCommand},
19565 {"lrange", Jim_LrangeCoreCommand},
19566 {"lrepeat", Jim_LrepeatCoreCommand},
19567 {"env", Jim_EnvCoreCommand},
19568 {"source", Jim_SourceCoreCommand},
19569 {"lreverse", Jim_LreverseCoreCommand},
19570 {"range", Jim_RangeCoreCommand},
19571 {"rand", Jim_RandCoreCommand},
19572 {"tailcall", Jim_TailcallCoreCommand},
19573 {"local", Jim_LocalCoreCommand},
19574 {"upcall", Jim_UpcallCoreCommand},
19575 {"apply", Jim_ApplyCoreCommand},
19576 {NULL, NULL},
19579 void Jim_RegisterCoreCommands(Jim_Interp *interp)
19581 int i = 0;
19583 while (Jim_CoreCommandsTable[i].name != NULL) {
19584 Jim_CreateCommand(interp,
19585 Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
19586 i++;
19590 void Jim_MakeErrorMessage(Jim_Interp *interp)
19592 Jim_Obj *argv[2];
19594 argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
19595 argv[1] = interp->result;
19597 Jim_EvalObjVector(interp, 2, argv);
19600 static char **JimSortStringTable(const char *const *tablePtr)
19602 int count;
19603 char **tablePtrSorted;
19606 for (count = 0; tablePtr[count]; count++) {
19610 tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1));
19611 memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
19612 qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
19613 tablePtrSorted[count] = NULL;
19615 return tablePtrSorted;
19618 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
19619 const char *prefix, const char *const *tablePtr, const char *name)
19621 char **tablePtrSorted;
19622 int i;
19624 if (name == NULL) {
19625 name = "option";
19628 Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
19629 tablePtrSorted = JimSortStringTable(tablePtr);
19630 for (i = 0; tablePtrSorted[i]; i++) {
19631 if (tablePtrSorted[i + 1] == NULL && i > 0) {
19632 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
19634 Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
19635 if (tablePtrSorted[i + 1]) {
19636 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
19639 Jim_Free(tablePtrSorted);
19643 int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr)
19645 if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) {
19646 int i;
19647 char **tablePtrSorted = JimSortStringTable(tablePtr);
19648 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
19649 for (i = 0; tablePtrSorted[i]; i++) {
19650 Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1));
19652 Jim_Free(tablePtrSorted);
19653 return JIM_OK;
19655 return JIM_ERR;
19658 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
19659 const char *const *tablePtr, int *indexPtr, const char *name, int flags)
19661 const char *bad = "bad ";
19662 const char *const *entryPtr = NULL;
19663 int i;
19664 int match = -1;
19665 int arglen;
19666 const char *arg = Jim_GetString(objPtr, &arglen);
19668 *indexPtr = -1;
19670 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
19671 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
19673 *indexPtr = i;
19674 return JIM_OK;
19676 if (flags & JIM_ENUM_ABBREV) {
19677 if (strncmp(arg, *entryPtr, arglen) == 0) {
19678 if (*arg == '-' && arglen == 1) {
19679 break;
19681 if (match >= 0) {
19682 bad = "ambiguous ";
19683 goto ambiguous;
19685 match = i;
19691 if (match >= 0) {
19692 *indexPtr = match;
19693 return JIM_OK;
19696 ambiguous:
19697 if (flags & JIM_ERRMSG) {
19698 JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
19700 return JIM_ERR;
19703 int Jim_FindByName(const char *name, const char * const array[], size_t len)
19705 int i;
19707 for (i = 0; i < (int)len; i++) {
19708 if (array[i] && strcmp(array[i], name) == 0) {
19709 return i;
19712 return -1;
19715 int Jim_IsDict(Jim_Obj *objPtr)
19717 return objPtr->typePtr == &dictObjType;
19720 int Jim_IsList(Jim_Obj *objPtr)
19722 return objPtr->typePtr == &listObjType;
19725 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
19728 int len = strlen(format);
19729 int extra = 0;
19730 int n = 0;
19731 const char *params[5];
19732 int nobjparam = 0;
19733 Jim_Obj *objparam[5];
19734 char *buf;
19735 va_list args;
19736 int i;
19738 va_start(args, format);
19740 for (i = 0; i < len && n < 5; i++) {
19741 int l;
19743 if (strncmp(format + i, "%s", 2) == 0) {
19744 params[n] = va_arg(args, char *);
19746 l = strlen(params[n]);
19748 else if (strncmp(format + i, "%#s", 3) == 0) {
19749 Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
19751 params[n] = Jim_GetString(objPtr, &l);
19752 objparam[nobjparam++] = objPtr;
19753 Jim_IncrRefCount(objPtr);
19755 else {
19756 if (format[i] == '%') {
19757 i++;
19759 continue;
19761 n++;
19762 extra += l;
19765 len += extra;
19766 buf = Jim_Alloc(len + 1);
19767 len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
19769 va_end(args);
19771 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
19773 for (i = 0; i < nobjparam; i++) {
19774 Jim_DecrRefCount(interp, objparam[i]);
19779 #ifndef jim_ext_package
19780 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
19782 return JIM_OK;
19784 #endif
19785 #ifndef jim_ext_aio
19786 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
19788 Jim_SetResultString(interp, "aio not enabled", -1);
19789 return NULL;
19791 #endif
19794 #include <stdio.h>
19795 #include <string.h>
19798 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19801 return JIM_OK;
19804 static const jim_subcmd_type dummy_subcmd = {
19805 "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
19808 static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
19810 const char *s = "";
19812 for (; ct->cmd; ct++) {
19813 if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
19814 Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, NULL);
19815 s = sep;
19820 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
19821 Jim_Obj *cmd, Jim_Obj *subcmd)
19823 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19824 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), ", ", type,
19825 " command \"", Jim_String(subcmd), "\": should be ", NULL);
19826 add_commands(interp, command_table, ", ");
19829 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
19830 Jim_Obj *const *argv)
19832 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19833 Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]),
19834 " command ... \", where command is one of: ", NULL);
19835 add_commands(interp, command_table, ", ");
19838 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
19840 if (cmd) {
19841 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
19843 Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
19844 if (ct->args && *ct->args) {
19845 Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
19849 static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd)
19851 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19852 add_cmd_usage(interp, command_table, subcmd);
19853 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19856 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
19857 int argc, Jim_Obj *const *argv)
19859 const jim_subcmd_type *ct;
19860 const jim_subcmd_type *partial = 0;
19861 int cmdlen;
19862 Jim_Obj *cmd;
19863 const char *cmdstr;
19864 const char *cmdname;
19865 int help = 0;
19867 cmdname = Jim_String(argv[0]);
19869 if (argc < 2) {
19870 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19871 Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname,
19872 " command ...\"\n", NULL);
19873 Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help ?command?\" for help", NULL);
19874 return 0;
19877 cmd = argv[1];
19880 if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
19881 if (argc == 2) {
19883 show_cmd_usage(interp, command_table, argc, argv);
19884 return &dummy_subcmd;
19886 help = 1;
19889 cmd = argv[2];
19893 if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
19895 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19896 add_commands(interp, command_table, " ");
19897 return &dummy_subcmd;
19900 cmdstr = Jim_GetString(cmd, &cmdlen);
19902 for (ct = command_table; ct->cmd; ct++) {
19903 if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
19905 break;
19907 if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
19908 if (partial) {
19910 if (help) {
19912 show_cmd_usage(interp, command_table, argc, argv);
19913 return &dummy_subcmd;
19915 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
19916 return 0;
19918 partial = ct;
19920 continue;
19924 if (partial && !ct->cmd) {
19925 ct = partial;
19928 if (!ct->cmd) {
19930 if (help) {
19932 show_cmd_usage(interp, command_table, argc, argv);
19933 return &dummy_subcmd;
19935 bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
19936 return 0;
19939 if (help) {
19940 Jim_SetResultString(interp, "Usage: ", -1);
19942 add_cmd_usage(interp, ct, argv[0]);
19943 return &dummy_subcmd;
19947 if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) {
19948 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19950 add_cmd_usage(interp, ct, argv[0]);
19951 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19953 return 0;
19957 return ct;
19960 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
19962 int ret = JIM_ERR;
19964 if (ct) {
19965 if (ct->flags & JIM_MODFLAG_FULLARGV) {
19966 ret = ct->function(interp, argc, argv);
19968 else {
19969 ret = ct->function(interp, argc - 2, argv + 2);
19971 if (ret < 0) {
19972 set_wrong_args(interp, ct, argv[0]);
19973 ret = JIM_ERR;
19976 return ret;
19979 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19981 const jim_subcmd_type *ct =
19982 Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
19984 return Jim_CallSubCmd(interp, ct, argc, argv);
19987 #include <ctype.h>
19988 #include <stdlib.h>
19989 #include <string.h>
19990 #include <stdio.h>
19991 #include <assert.h>
19994 int utf8_fromunicode(char *p, unsigned uc)
19996 if (uc <= 0x7f) {
19997 *p = uc;
19998 return 1;
20000 else if (uc <= 0x7ff) {
20001 *p++ = 0xc0 | ((uc & 0x7c0) >> 6);
20002 *p = 0x80 | (uc & 0x3f);
20003 return 2;
20005 else if (uc <= 0xffff) {
20006 *p++ = 0xe0 | ((uc & 0xf000) >> 12);
20007 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
20008 *p = 0x80 | (uc & 0x3f);
20009 return 3;
20012 else {
20013 *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
20014 *p++ = 0x80 | ((uc & 0x3f000) >> 12);
20015 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
20016 *p = 0x80 | (uc & 0x3f);
20017 return 4;
20021 #include <ctype.h>
20022 #include <string.h>
20025 #define JIM_INTEGER_SPACE 24
20026 #define MAX_FLOAT_WIDTH 320
20028 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
20030 const char *span, *format, *formatEnd, *msg;
20031 int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
20032 static const char * const mixedXPG =
20033 "cannot mix \"%\" and \"%n$\" conversion specifiers";
20034 static const char * const badIndex[2] = {
20035 "not enough arguments for all format specifiers",
20036 "\"%n$\" argument index out of range"
20038 int formatLen;
20039 Jim_Obj *resultPtr;
20041 char *num_buffer = NULL;
20042 int num_buffer_size = 0;
20044 span = format = Jim_GetString(fmtObjPtr, &formatLen);
20045 formatEnd = format + formatLen;
20046 resultPtr = Jim_NewEmptyStringObj(interp);
20048 while (format != formatEnd) {
20049 char *end;
20050 int gotMinus, sawFlag;
20051 int gotPrecision, useShort;
20052 long width, precision;
20053 int newXpg;
20054 int ch;
20055 int step;
20056 int doubleType;
20057 char pad = ' ';
20058 char spec[2*JIM_INTEGER_SPACE + 12];
20059 char *p;
20061 int formatted_chars;
20062 int formatted_bytes;
20063 const char *formatted_buf;
20065 step = utf8_tounicode(format, &ch);
20066 format += step;
20067 if (ch != '%') {
20068 numBytes += step;
20069 continue;
20071 if (numBytes) {
20072 Jim_AppendString(interp, resultPtr, span, numBytes);
20073 numBytes = 0;
20077 step = utf8_tounicode(format, &ch);
20078 if (ch == '%') {
20079 span = format;
20080 numBytes = step;
20081 format += step;
20082 continue;
20086 newXpg = 0;
20087 if (isdigit(ch)) {
20088 int position = strtoul(format, &end, 10);
20089 if (*end == '$') {
20090 newXpg = 1;
20091 objIndex = position - 1;
20092 format = end + 1;
20093 step = utf8_tounicode(format, &ch);
20096 if (newXpg) {
20097 if (gotSequential) {
20098 msg = mixedXPG;
20099 goto errorMsg;
20101 gotXpg = 1;
20102 } else {
20103 if (gotXpg) {
20104 msg = mixedXPG;
20105 goto errorMsg;
20107 gotSequential = 1;
20109 if ((objIndex < 0) || (objIndex >= objc)) {
20110 msg = badIndex[gotXpg];
20111 goto errorMsg;
20114 p = spec;
20115 *p++ = '%';
20117 gotMinus = 0;
20118 sawFlag = 1;
20119 do {
20120 switch (ch) {
20121 case '-':
20122 gotMinus = 1;
20123 break;
20124 case '0':
20125 pad = ch;
20126 break;
20127 case ' ':
20128 case '+':
20129 case '#':
20130 break;
20131 default:
20132 sawFlag = 0;
20133 continue;
20135 *p++ = ch;
20136 format += step;
20137 step = utf8_tounicode(format, &ch);
20138 } while (sawFlag);
20141 width = 0;
20142 if (isdigit(ch)) {
20143 width = strtoul(format, &end, 10);
20144 format = end;
20145 step = utf8_tounicode(format, &ch);
20146 } else if (ch == '*') {
20147 if (objIndex >= objc - 1) {
20148 msg = badIndex[gotXpg];
20149 goto errorMsg;
20151 if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) {
20152 goto error;
20154 if (width < 0) {
20155 width = -width;
20156 if (!gotMinus) {
20157 *p++ = '-';
20158 gotMinus = 1;
20161 objIndex++;
20162 format += step;
20163 step = utf8_tounicode(format, &ch);
20167 gotPrecision = precision = 0;
20168 if (ch == '.') {
20169 gotPrecision = 1;
20170 format += step;
20171 step = utf8_tounicode(format, &ch);
20173 if (isdigit(ch)) {
20174 precision = strtoul(format, &end, 10);
20175 format = end;
20176 step = utf8_tounicode(format, &ch);
20177 } else if (ch == '*') {
20178 if (objIndex >= objc - 1) {
20179 msg = badIndex[gotXpg];
20180 goto errorMsg;
20182 if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) {
20183 goto error;
20187 if (precision < 0) {
20188 precision = 0;
20190 objIndex++;
20191 format += step;
20192 step = utf8_tounicode(format, &ch);
20196 useShort = 0;
20197 if (ch == 'h') {
20198 useShort = 1;
20199 format += step;
20200 step = utf8_tounicode(format, &ch);
20201 } else if (ch == 'l') {
20203 format += step;
20204 step = utf8_tounicode(format, &ch);
20205 if (ch == 'l') {
20206 format += step;
20207 step = utf8_tounicode(format, &ch);
20211 format += step;
20212 span = format;
20215 if (ch == 'i') {
20216 ch = 'd';
20219 doubleType = 0;
20221 switch (ch) {
20222 case '\0':
20223 msg = "format string ended in middle of field specifier";
20224 goto errorMsg;
20225 case 's': {
20226 formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
20227 formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
20228 if (gotPrecision && (precision < formatted_chars)) {
20230 formatted_chars = precision;
20231 formatted_bytes = utf8_index(formatted_buf, precision);
20233 break;
20235 case 'c': {
20236 jim_wide code;
20238 if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
20239 goto error;
20242 formatted_bytes = utf8_getchars(spec, code);
20243 formatted_buf = spec;
20244 formatted_chars = 1;
20245 break;
20247 case 'b': {
20248 unsigned jim_wide w;
20249 int length;
20250 int i;
20251 int j;
20253 if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
20254 goto error;
20256 length = sizeof(w) * 8;
20260 if (num_buffer_size < length + 1) {
20261 num_buffer_size = length + 1;
20262 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20265 j = 0;
20266 for (i = length; i > 0; ) {
20267 i--;
20268 if (w & ((unsigned jim_wide)1 << i)) {
20269 num_buffer[j++] = '1';
20271 else if (j || i == 0) {
20272 num_buffer[j++] = '0';
20275 num_buffer[j] = 0;
20276 formatted_chars = formatted_bytes = j;
20277 formatted_buf = num_buffer;
20278 break;
20281 case 'e':
20282 case 'E':
20283 case 'f':
20284 case 'g':
20285 case 'G':
20286 doubleType = 1;
20288 case 'd':
20289 case 'u':
20290 case 'o':
20291 case 'x':
20292 case 'X': {
20293 jim_wide w;
20294 double d;
20295 int length;
20298 if (width) {
20299 p += sprintf(p, "%ld", width);
20301 if (gotPrecision) {
20302 p += sprintf(p, ".%ld", precision);
20306 if (doubleType) {
20307 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
20308 goto error;
20310 length = MAX_FLOAT_WIDTH;
20312 else {
20313 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) {
20314 goto error;
20316 length = JIM_INTEGER_SPACE;
20317 if (useShort) {
20318 if (ch == 'd') {
20319 w = (short)w;
20321 else {
20322 w = (unsigned short)w;
20325 *p++ = 'l';
20326 #ifdef HAVE_LONG_LONG
20327 if (sizeof(long long) == sizeof(jim_wide)) {
20328 *p++ = 'l';
20330 #endif
20333 *p++ = (char) ch;
20334 *p = '\0';
20337 if (width > length) {
20338 length = width;
20340 if (gotPrecision) {
20341 length += precision;
20345 if (num_buffer_size < length + 1) {
20346 num_buffer_size = length + 1;
20347 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20350 if (doubleType) {
20351 snprintf(num_buffer, length + 1, spec, d);
20353 else {
20354 formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
20356 formatted_chars = formatted_bytes = strlen(num_buffer);
20357 formatted_buf = num_buffer;
20358 break;
20361 default: {
20363 spec[0] = ch;
20364 spec[1] = '\0';
20365 Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
20366 goto error;
20370 if (!gotMinus) {
20371 while (formatted_chars < width) {
20372 Jim_AppendString(interp, resultPtr, &pad, 1);
20373 formatted_chars++;
20377 Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes);
20379 while (formatted_chars < width) {
20380 Jim_AppendString(interp, resultPtr, &pad, 1);
20381 formatted_chars++;
20384 objIndex += gotSequential;
20386 if (numBytes) {
20387 Jim_AppendString(interp, resultPtr, span, numBytes);
20390 Jim_Free(num_buffer);
20391 return resultPtr;
20393 errorMsg:
20394 Jim_SetResultString(interp, msg, -1);
20395 error:
20396 Jim_FreeNewObj(interp, resultPtr);
20397 Jim_Free(num_buffer);
20398 return NULL;
20402 #if defined(JIM_REGEXP)
20403 #include <stdio.h>
20404 #include <ctype.h>
20405 #include <stdlib.h>
20406 #include <string.h>
20410 #define REG_MAX_PAREN 100
20414 #define END 0
20415 #define BOL 1
20416 #define EOL 2
20417 #define ANY 3
20418 #define ANYOF 4
20419 #define ANYBUT 5
20420 #define BRANCH 6
20421 #define BACK 7
20422 #define EXACTLY 8
20423 #define NOTHING 9
20424 #define REP 10
20425 #define REPMIN 11
20426 #define REPX 12
20427 #define REPXMIN 13
20428 #define BOLX 14
20429 #define EOLX 15
20430 #define WORDA 16
20431 #define WORDZ 17
20433 #define OPENNC 1000
20434 #define OPEN 1001
20439 #define CLOSENC 2000
20440 #define CLOSE 2001
20441 #define CLOSE_END (CLOSE+REG_MAX_PAREN)
20443 #define REG_MAGIC 0xFADED00D
20446 #define OP(preg, p) (preg->program[p])
20447 #define NEXT(preg, p) (preg->program[p + 1])
20448 #define OPERAND(p) ((p) + 2)
20453 #define FAIL(R,M) { (R)->err = (M); return (M); }
20454 #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
20455 #define META "^$.[()|?{+*"
20457 #define HASWIDTH 1
20458 #define SIMPLE 2
20459 #define SPSTART 4
20460 #define WORST 0
20462 #define MAX_REP_COUNT 1000000
20464 static int reg(regex_t *preg, int paren, int *flagp );
20465 static int regpiece(regex_t *preg, int *flagp );
20466 static int regbranch(regex_t *preg, int *flagp );
20467 static int regatom(regex_t *preg, int *flagp );
20468 static int regnode(regex_t *preg, int op );
20469 static int regnext(regex_t *preg, int p );
20470 static void regc(regex_t *preg, int b );
20471 static int reginsert(regex_t *preg, int op, int size, int opnd );
20472 static void regtail(regex_t *preg, int p, int val);
20473 static void regoptail(regex_t *preg, int p, int val );
20474 static int regopsize(regex_t *preg, int p );
20476 static int reg_range_find(const int *string, int c);
20477 static const char *str_find(const char *string, int c, int nocase);
20478 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase);
20481 #ifdef DEBUG
20482 static int regnarrate = 0;
20483 static void regdump(regex_t *preg);
20484 static const char *regprop( int op );
20485 #endif
20488 static int str_int_len(const int *seq)
20490 int n = 0;
20491 while (*seq++) {
20492 n++;
20494 return n;
20497 int regcomp(regex_t *preg, const char *exp, int cflags)
20499 int scan;
20500 int longest;
20501 unsigned len;
20502 int flags;
20504 #ifdef DEBUG
20505 fprintf(stderr, "Compiling: '%s'\n", exp);
20506 #endif
20507 memset(preg, 0, sizeof(*preg));
20509 if (exp == NULL)
20510 FAIL(preg, REG_ERR_NULL_ARGUMENT);
20513 preg->cflags = cflags;
20514 preg->regparse = exp;
20517 preg->proglen = (strlen(exp) + 1) * 5;
20518 preg->program = malloc(preg->proglen * sizeof(int));
20519 if (preg->program == NULL)
20520 FAIL(preg, REG_ERR_NOMEM);
20522 regc(preg, REG_MAGIC);
20523 if (reg(preg, 0, &flags) == 0) {
20524 return preg->err;
20528 if (preg->re_nsub >= REG_MAX_PAREN)
20529 FAIL(preg,REG_ERR_TOO_BIG);
20532 preg->regstart = 0;
20533 preg->reganch = 0;
20534 preg->regmust = 0;
20535 preg->regmlen = 0;
20536 scan = 1;
20537 if (OP(preg, regnext(preg, scan)) == END) {
20538 scan = OPERAND(scan);
20541 if (OP(preg, scan) == EXACTLY) {
20542 preg->regstart = preg->program[OPERAND(scan)];
20544 else if (OP(preg, scan) == BOL)
20545 preg->reganch++;
20547 if (flags&SPSTART) {
20548 longest = 0;
20549 len = 0;
20550 for (; scan != 0; scan = regnext(preg, scan)) {
20551 if (OP(preg, scan) == EXACTLY) {
20552 int plen = str_int_len(preg->program + OPERAND(scan));
20553 if (plen >= len) {
20554 longest = OPERAND(scan);
20555 len = plen;
20559 preg->regmust = longest;
20560 preg->regmlen = len;
20564 #ifdef DEBUG
20565 regdump(preg);
20566 #endif
20568 return 0;
20571 static int reg(regex_t *preg, int paren, int *flagp )
20573 int ret;
20574 int br;
20575 int ender;
20576 int parno = 0;
20577 int flags;
20579 *flagp = HASWIDTH;
20582 if (paren) {
20583 if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
20585 preg->regparse += 2;
20586 parno = -1;
20588 else {
20589 parno = ++preg->re_nsub;
20591 ret = regnode(preg, OPEN+parno);
20592 } else
20593 ret = 0;
20596 br = regbranch(preg, &flags);
20597 if (br == 0)
20598 return 0;
20599 if (ret != 0)
20600 regtail(preg, ret, br);
20601 else
20602 ret = br;
20603 if (!(flags&HASWIDTH))
20604 *flagp &= ~HASWIDTH;
20605 *flagp |= flags&SPSTART;
20606 while (*preg->regparse == '|') {
20607 preg->regparse++;
20608 br = regbranch(preg, &flags);
20609 if (br == 0)
20610 return 0;
20611 regtail(preg, ret, br);
20612 if (!(flags&HASWIDTH))
20613 *flagp &= ~HASWIDTH;
20614 *flagp |= flags&SPSTART;
20618 ender = regnode(preg, (paren) ? CLOSE+parno : END);
20619 regtail(preg, ret, ender);
20622 for (br = ret; br != 0; br = regnext(preg, br))
20623 regoptail(preg, br, ender);
20626 if (paren && *preg->regparse++ != ')') {
20627 preg->err = REG_ERR_UNMATCHED_PAREN;
20628 return 0;
20629 } else if (!paren && *preg->regparse != '\0') {
20630 if (*preg->regparse == ')') {
20631 preg->err = REG_ERR_UNMATCHED_PAREN;
20632 return 0;
20633 } else {
20634 preg->err = REG_ERR_JUNK_ON_END;
20635 return 0;
20639 return(ret);
20642 static int regbranch(regex_t *preg, int *flagp )
20644 int ret;
20645 int chain;
20646 int latest;
20647 int flags;
20649 *flagp = WORST;
20651 ret = regnode(preg, BRANCH);
20652 chain = 0;
20653 while (*preg->regparse != '\0' && *preg->regparse != ')' &&
20654 *preg->regparse != '|') {
20655 latest = regpiece(preg, &flags);
20656 if (latest == 0)
20657 return 0;
20658 *flagp |= flags&HASWIDTH;
20659 if (chain == 0) {
20660 *flagp |= flags&SPSTART;
20662 else {
20663 regtail(preg, chain, latest);
20665 chain = latest;
20667 if (chain == 0)
20668 (void) regnode(preg, NOTHING);
20670 return(ret);
20673 static int regpiece(regex_t *preg, int *flagp)
20675 int ret;
20676 char op;
20677 int next;
20678 int flags;
20679 int min;
20680 int max;
20682 ret = regatom(preg, &flags);
20683 if (ret == 0)
20684 return 0;
20686 op = *preg->regparse;
20687 if (!ISMULT(op)) {
20688 *flagp = flags;
20689 return(ret);
20692 if (!(flags&HASWIDTH) && op != '?') {
20693 preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
20694 return 0;
20698 if (op == '{') {
20699 char *end;
20701 min = strtoul(preg->regparse + 1, &end, 10);
20702 if (end == preg->regparse + 1) {
20703 preg->err = REG_ERR_BAD_COUNT;
20704 return 0;
20706 if (*end == '}') {
20707 max = min;
20709 else {
20710 preg->regparse = end;
20711 max = strtoul(preg->regparse + 1, &end, 10);
20712 if (*end != '}') {
20713 preg->err = REG_ERR_UNMATCHED_BRACES;
20714 return 0;
20717 if (end == preg->regparse + 1) {
20718 max = MAX_REP_COUNT;
20720 else if (max < min || max >= 100) {
20721 preg->err = REG_ERR_BAD_COUNT;
20722 return 0;
20724 if (min >= 100) {
20725 preg->err = REG_ERR_BAD_COUNT;
20726 return 0;
20729 preg->regparse = strchr(preg->regparse, '}');
20731 else {
20732 min = (op == '+');
20733 max = (op == '?' ? 1 : MAX_REP_COUNT);
20736 if (preg->regparse[1] == '?') {
20737 preg->regparse++;
20738 next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret);
20740 else {
20741 next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret);
20743 preg->program[ret + 2] = max;
20744 preg->program[ret + 3] = min;
20745 preg->program[ret + 4] = 0;
20747 *flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART);
20749 if (!(flags & SIMPLE)) {
20750 int back = regnode(preg, BACK);
20751 regtail(preg, back, ret);
20752 regtail(preg, next, back);
20755 preg->regparse++;
20756 if (ISMULT(*preg->regparse)) {
20757 preg->err = REG_ERR_NESTED_COUNT;
20758 return 0;
20761 return ret;
20764 static void reg_addrange(regex_t *preg, int lower, int upper)
20766 if (lower > upper) {
20767 reg_addrange(preg, upper, lower);
20770 regc(preg, upper - lower + 1);
20771 regc(preg, lower);
20774 static void reg_addrange_str(regex_t *preg, const char *str)
20776 while (*str) {
20777 reg_addrange(preg, *str, *str);
20778 str++;
20782 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper)
20784 int l = utf8_tounicode(s, uc);
20785 if (upper) {
20786 *uc = utf8_upper(*uc);
20788 return l;
20791 static int hexdigitval(int c)
20793 if (c >= '0' && c <= '9')
20794 return c - '0';
20795 if (c >= 'a' && c <= 'f')
20796 return c - 'a' + 10;
20797 if (c >= 'A' && c <= 'F')
20798 return c - 'A' + 10;
20799 return -1;
20802 static int parse_hex(const char *s, int n, int *uc)
20804 int val = 0;
20805 int k;
20807 for (k = 0; k < n; k++) {
20808 int c = hexdigitval(*s++);
20809 if (c == -1) {
20810 break;
20812 val = (val << 4) | c;
20814 if (k) {
20815 *uc = val;
20817 return k;
20820 static int reg_decode_escape(const char *s, int *ch)
20822 int n;
20823 const char *s0 = s;
20825 *ch = *s++;
20827 switch (*ch) {
20828 case 'b': *ch = '\b'; break;
20829 case 'e': *ch = 27; break;
20830 case 'f': *ch = '\f'; break;
20831 case 'n': *ch = '\n'; break;
20832 case 'r': *ch = '\r'; break;
20833 case 't': *ch = '\t'; break;
20834 case 'v': *ch = '\v'; break;
20835 case 'u':
20836 if (*s == '{') {
20838 n = parse_hex(s + 1, 6, ch);
20839 if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
20840 s += n + 2;
20842 else {
20844 *ch = 'u';
20847 else if ((n = parse_hex(s, 4, ch)) > 0) {
20848 s += n;
20850 break;
20851 case 'U':
20852 if ((n = parse_hex(s, 8, ch)) > 0) {
20853 s += n;
20855 break;
20856 case 'x':
20857 if ((n = parse_hex(s, 2, ch)) > 0) {
20858 s += n;
20860 break;
20861 case '\0':
20862 s--;
20863 *ch = '\\';
20864 break;
20866 return s - s0;
20869 static int regatom(regex_t *preg, int *flagp)
20871 int ret;
20872 int flags;
20873 int nocase = (preg->cflags & REG_ICASE);
20875 int ch;
20876 int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);
20878 *flagp = WORST;
20880 preg->regparse += n;
20881 switch (ch) {
20883 case '^':
20884 ret = regnode(preg, BOL);
20885 break;
20886 case '$':
20887 ret = regnode(preg, EOL);
20888 break;
20889 case '.':
20890 ret = regnode(preg, ANY);
20891 *flagp |= HASWIDTH|SIMPLE;
20892 break;
20893 case '[': {
20894 const char *pattern = preg->regparse;
20896 if (*pattern == '^') {
20897 ret = regnode(preg, ANYBUT);
20898 pattern++;
20899 } else
20900 ret = regnode(preg, ANYOF);
20903 if (*pattern == ']' || *pattern == '-') {
20904 reg_addrange(preg, *pattern, *pattern);
20905 pattern++;
20908 while (*pattern && *pattern != ']') {
20910 int start;
20911 int end;
20913 pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
20914 if (start == '\\') {
20915 pattern += reg_decode_escape(pattern, &start);
20916 if (start == 0) {
20917 preg->err = REG_ERR_NULL_CHAR;
20918 return 0;
20921 if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
20923 pattern += utf8_tounicode(pattern, &end);
20924 pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
20925 if (end == '\\') {
20926 pattern += reg_decode_escape(pattern, &end);
20927 if (end == 0) {
20928 preg->err = REG_ERR_NULL_CHAR;
20929 return 0;
20933 reg_addrange(preg, start, end);
20934 continue;
20936 if (start == '[' && pattern[0] == ':') {
20937 static const char *character_class[] = {
20938 ":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
20939 ":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
20941 enum {
20942 CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
20943 CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
20944 CC_NUM
20946 int i;
20948 for (i = 0; i < CC_NUM; i++) {
20949 n = strlen(character_class[i]);
20950 if (strncmp(pattern, character_class[i], n) == 0) {
20952 pattern += n + 1;
20953 break;
20956 if (i != CC_NUM) {
20957 switch (i) {
20958 case CC_ALNUM:
20959 reg_addrange(preg, '0', '9');
20961 case CC_ALPHA:
20962 if ((preg->cflags & REG_ICASE) == 0) {
20963 reg_addrange(preg, 'a', 'z');
20965 reg_addrange(preg, 'A', 'Z');
20966 break;
20967 case CC_SPACE:
20968 reg_addrange_str(preg, " \t\r\n\f\v");
20969 break;
20970 case CC_BLANK:
20971 reg_addrange_str(preg, " \t");
20972 break;
20973 case CC_UPPER:
20974 reg_addrange(preg, 'A', 'Z');
20975 break;
20976 case CC_LOWER:
20977 reg_addrange(preg, 'a', 'z');
20978 break;
20979 case CC_XDIGIT:
20980 reg_addrange(preg, 'a', 'f');
20981 reg_addrange(preg, 'A', 'F');
20983 case CC_DIGIT:
20984 reg_addrange(preg, '0', '9');
20985 break;
20986 case CC_CNTRL:
20987 reg_addrange(preg, 0, 31);
20988 reg_addrange(preg, 127, 127);
20989 break;
20990 case CC_PRINT:
20991 reg_addrange(preg, ' ', '~');
20992 break;
20993 case CC_GRAPH:
20994 reg_addrange(preg, '!', '~');
20995 break;
20996 case CC_PUNCT:
20997 reg_addrange(preg, '!', '/');
20998 reg_addrange(preg, ':', '@');
20999 reg_addrange(preg, '[', '`');
21000 reg_addrange(preg, '{', '~');
21001 break;
21003 continue;
21007 reg_addrange(preg, start, start);
21009 regc(preg, '\0');
21011 if (*pattern) {
21012 pattern++;
21014 preg->regparse = pattern;
21016 *flagp |= HASWIDTH|SIMPLE;
21018 break;
21019 case '(':
21020 ret = reg(preg, 1, &flags);
21021 if (ret == 0)
21022 return 0;
21023 *flagp |= flags&(HASWIDTH|SPSTART);
21024 break;
21025 case '\0':
21026 case '|':
21027 case ')':
21028 preg->err = REG_ERR_INTERNAL;
21029 return 0;
21030 case '?':
21031 case '+':
21032 case '*':
21033 case '{':
21034 preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
21035 return 0;
21036 case '\\':
21037 ch = *preg->regparse++;
21038 switch (ch) {
21039 case '\0':
21040 preg->err = REG_ERR_TRAILING_BACKSLASH;
21041 return 0;
21042 case 'A':
21043 ret = regnode(preg, BOLX);
21044 break;
21045 case 'Z':
21046 ret = regnode(preg, EOLX);
21047 break;
21048 case '<':
21049 case 'm':
21050 ret = regnode(preg, WORDA);
21051 break;
21052 case '>':
21053 case 'M':
21054 ret = regnode(preg, WORDZ);
21055 break;
21056 case 'd':
21057 case 'D':
21058 ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT);
21059 reg_addrange(preg, '0', '9');
21060 regc(preg, '\0');
21061 *flagp |= HASWIDTH|SIMPLE;
21062 break;
21063 case 'w':
21064 case 'W':
21065 ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT);
21066 if ((preg->cflags & REG_ICASE) == 0) {
21067 reg_addrange(preg, 'a', 'z');
21069 reg_addrange(preg, 'A', 'Z');
21070 reg_addrange(preg, '0', '9');
21071 reg_addrange(preg, '_', '_');
21072 regc(preg, '\0');
21073 *flagp |= HASWIDTH|SIMPLE;
21074 break;
21075 case 's':
21076 case 'S':
21077 ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT);
21078 reg_addrange_str(preg," \t\r\n\f\v");
21079 regc(preg, '\0');
21080 *flagp |= HASWIDTH|SIMPLE;
21081 break;
21083 default:
21086 preg->regparse--;
21087 goto de_fault;
21089 break;
21090 de_fault:
21091 default: {
21092 int added = 0;
21095 preg->regparse -= n;
21097 ret = regnode(preg, EXACTLY);
21101 while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
21102 n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
21103 if (ch == '\\' && preg->regparse[n]) {
21104 if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) {
21106 break;
21108 n += reg_decode_escape(preg->regparse + n, &ch);
21109 if (ch == 0) {
21110 preg->err = REG_ERR_NULL_CHAR;
21111 return 0;
21116 if (ISMULT(preg->regparse[n])) {
21118 if (added) {
21120 break;
21123 regc(preg, ch);
21124 added++;
21125 preg->regparse += n;
21126 break;
21130 regc(preg, ch);
21131 added++;
21132 preg->regparse += n;
21134 regc(preg, '\0');
21136 *flagp |= HASWIDTH;
21137 if (added == 1)
21138 *flagp |= SIMPLE;
21139 break;
21141 break;
21144 return(ret);
21147 static void reg_grow(regex_t *preg, int n)
21149 if (preg->p + n >= preg->proglen) {
21150 preg->proglen = (preg->p + n) * 2;
21151 preg->program = realloc(preg->program, preg->proglen * sizeof(int));
21156 static int regnode(regex_t *preg, int op)
21158 reg_grow(preg, 2);
21161 preg->program[preg->p++] = op;
21162 preg->program[preg->p++] = 0;
21165 return preg->p - 2;
21168 static void regc(regex_t *preg, int b )
21170 reg_grow(preg, 1);
21171 preg->program[preg->p++] = b;
21174 static int reginsert(regex_t *preg, int op, int size, int opnd )
21176 reg_grow(preg, size);
21179 memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
21181 memset(preg->program + opnd, 0, sizeof(int) * size);
21183 preg->program[opnd] = op;
21185 preg->p += size;
21187 return opnd + size;
21190 static void regtail(regex_t *preg, int p, int val)
21192 int scan;
21193 int temp;
21194 int offset;
21197 scan = p;
21198 for (;;) {
21199 temp = regnext(preg, scan);
21200 if (temp == 0)
21201 break;
21202 scan = temp;
21205 if (OP(preg, scan) == BACK)
21206 offset = scan - val;
21207 else
21208 offset = val - scan;
21210 preg->program[scan + 1] = offset;
21214 static void regoptail(regex_t *preg, int p, int val )
21217 if (p != 0 && OP(preg, p) == BRANCH) {
21218 regtail(preg, OPERAND(p), val);
21223 static int regtry(regex_t *preg, const char *string );
21224 static int regmatch(regex_t *preg, int prog);
21225 static int regrepeat(regex_t *preg, int p, int max);
21227 int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
21229 const char *s;
21230 int scan;
21233 if (preg == NULL || preg->program == NULL || string == NULL) {
21234 return REG_ERR_NULL_ARGUMENT;
21238 if (*preg->program != REG_MAGIC) {
21239 return REG_ERR_CORRUPTED;
21242 #ifdef DEBUG
21243 fprintf(stderr, "regexec: %s\n", string);
21244 regdump(preg);
21245 #endif
21247 preg->eflags = eflags;
21248 preg->pmatch = pmatch;
21249 preg->nmatch = nmatch;
21250 preg->start = string;
21253 for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) {
21254 int op = OP(preg, scan);
21255 if (op == END)
21256 break;
21257 if (op == REPX || op == REPXMIN)
21258 preg->program[scan + 4] = 0;
21262 if (preg->regmust != 0) {
21263 s = string;
21264 while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
21265 if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
21266 break;
21268 s++;
21270 if (s == NULL)
21271 return REG_NOMATCH;
21275 preg->regbol = string;
21278 if (preg->reganch) {
21279 if (eflags & REG_NOTBOL) {
21281 goto nextline;
21283 while (1) {
21284 if (regtry(preg, string)) {
21285 return REG_NOERROR;
21287 if (*string) {
21288 nextline:
21289 if (preg->cflags & REG_NEWLINE) {
21291 string = strchr(string, '\n');
21292 if (string) {
21293 preg->regbol = ++string;
21294 continue;
21298 return REG_NOMATCH;
21303 s = string;
21304 if (preg->regstart != '\0') {
21306 while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
21307 if (regtry(preg, s))
21308 return REG_NOERROR;
21309 s++;
21312 else
21314 while (1) {
21315 if (regtry(preg, s))
21316 return REG_NOERROR;
21317 if (*s == '\0') {
21318 break;
21320 else {
21321 int c;
21322 s += utf8_tounicode(s, &c);
21327 return REG_NOMATCH;
21331 static int regtry( regex_t *preg, const char *string )
21333 int i;
21335 preg->reginput = string;
21337 for (i = 0; i < preg->nmatch; i++) {
21338 preg->pmatch[i].rm_so = -1;
21339 preg->pmatch[i].rm_eo = -1;
21341 if (regmatch(preg, 1)) {
21342 preg->pmatch[0].rm_so = string - preg->start;
21343 preg->pmatch[0].rm_eo = preg->reginput - preg->start;
21344 return(1);
21345 } else
21346 return(0);
21349 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase)
21351 const char *s = string;
21352 while (proglen && *s) {
21353 int ch;
21354 int n = reg_utf8_tounicode_case(s, &ch, nocase);
21355 if (ch != *prog) {
21356 return -1;
21358 prog++;
21359 s += n;
21360 proglen--;
21362 if (proglen == 0) {
21363 return s - string;
21365 return -1;
21368 static int reg_range_find(const int *range, int c)
21370 while (*range) {
21372 if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
21373 return 1;
21375 range += 2;
21377 return 0;
21380 static const char *str_find(const char *string, int c, int nocase)
21382 if (nocase) {
21384 c = utf8_upper(c);
21386 while (*string) {
21387 int ch;
21388 int n = reg_utf8_tounicode_case(string, &ch, nocase);
21389 if (c == ch) {
21390 return string;
21392 string += n;
21394 return NULL;
21397 static int reg_iseol(regex_t *preg, int ch)
21399 if (preg->cflags & REG_NEWLINE) {
21400 return ch == '\0' || ch == '\n';
21402 else {
21403 return ch == '\0';
21407 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin)
21409 int nextch = '\0';
21410 const char *save;
21411 int no;
21412 int c;
21414 int max = preg->program[scan + 2];
21415 int min = preg->program[scan + 3];
21416 int next = regnext(preg, scan);
21418 if (OP(preg, next) == EXACTLY) {
21419 nextch = preg->program[OPERAND(next)];
21421 save = preg->reginput;
21422 no = regrepeat(preg, scan + 5, max);
21423 if (no < min) {
21424 return 0;
21426 if (matchmin) {
21428 max = no;
21429 no = min;
21432 while (1) {
21433 if (matchmin) {
21434 if (no > max) {
21435 break;
21438 else {
21439 if (no < min) {
21440 break;
21443 preg->reginput = save + utf8_index(save, no);
21444 reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21446 if (reg_iseol(preg, nextch) || c == nextch) {
21447 if (regmatch(preg, next)) {
21448 return(1);
21451 if (matchmin) {
21453 no++;
21455 else {
21457 no--;
21460 return(0);
21463 static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
21465 int *scanpt = preg->program + scan;
21467 int max = scanpt[2];
21468 int min = scanpt[3];
21471 if (scanpt[4] < min) {
21473 scanpt[4]++;
21474 if (regmatch(preg, scan + 5)) {
21475 return 1;
21477 scanpt[4]--;
21478 return 0;
21480 if (scanpt[4] > max) {
21481 return 0;
21484 if (matchmin) {
21486 if (regmatch(preg, regnext(preg, scan))) {
21487 return 1;
21490 scanpt[4]++;
21491 if (regmatch(preg, scan + 5)) {
21492 return 1;
21494 scanpt[4]--;
21495 return 0;
21498 if (scanpt[4] < max) {
21499 scanpt[4]++;
21500 if (regmatch(preg, scan + 5)) {
21501 return 1;
21503 scanpt[4]--;
21506 return regmatch(preg, regnext(preg, scan));
21510 static int regmatch(regex_t *preg, int prog)
21512 int scan;
21513 int next;
21514 const char *save;
21516 scan = prog;
21518 #ifdef DEBUG
21519 if (scan != 0 && regnarrate)
21520 fprintf(stderr, "%s(\n", regprop(scan));
21521 #endif
21522 while (scan != 0) {
21523 int n;
21524 int c;
21525 #ifdef DEBUG
21526 if (regnarrate) {
21527 fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
21529 #endif
21530 next = regnext(preg, scan);
21531 n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21533 switch (OP(preg, scan)) {
21534 case BOLX:
21535 if ((preg->eflags & REG_NOTBOL)) {
21536 return(0);
21539 case BOL:
21540 if (preg->reginput != preg->regbol) {
21541 return(0);
21543 break;
21544 case EOLX:
21545 if (c != 0) {
21547 return 0;
21549 break;
21550 case EOL:
21551 if (!reg_iseol(preg, c)) {
21552 return(0);
21554 break;
21555 case WORDA:
21557 if ((!isalnum(UCHAR(c))) && c != '_')
21558 return(0);
21560 if (preg->reginput > preg->regbol &&
21561 (isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
21562 return(0);
21563 break;
21564 case WORDZ:
21566 if (preg->reginput > preg->regbol) {
21568 if (reg_iseol(preg, c) || !isalnum(UCHAR(c)) || c != '_') {
21569 c = preg->reginput[-1];
21571 if (isalnum(UCHAR(c)) || c == '_') {
21572 break;
21577 return(0);
21579 case ANY:
21580 if (reg_iseol(preg, c))
21581 return 0;
21582 preg->reginput += n;
21583 break;
21584 case EXACTLY: {
21585 int opnd;
21586 int len;
21587 int slen;
21589 opnd = OPERAND(scan);
21590 len = str_int_len(preg->program + opnd);
21592 slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE);
21593 if (slen < 0) {
21594 return(0);
21596 preg->reginput += slen;
21598 break;
21599 case ANYOF:
21600 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) {
21601 return(0);
21603 preg->reginput += n;
21604 break;
21605 case ANYBUT:
21606 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) {
21607 return(0);
21609 preg->reginput += n;
21610 break;
21611 case NOTHING:
21612 break;
21613 case BACK:
21614 break;
21615 case BRANCH:
21616 if (OP(preg, next) != BRANCH)
21617 next = OPERAND(scan);
21618 else {
21619 do {
21620 save = preg->reginput;
21621 if (regmatch(preg, OPERAND(scan))) {
21622 return(1);
21624 preg->reginput = save;
21625 scan = regnext(preg, scan);
21626 } while (scan != 0 && OP(preg, scan) == BRANCH);
21627 return(0);
21630 break;
21631 case REP:
21632 case REPMIN:
21633 return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);
21635 case REPX:
21636 case REPXMIN:
21637 return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);
21639 case END:
21640 return 1;
21642 case OPENNC:
21643 case CLOSENC:
21644 return regmatch(preg, next);
21646 default:
21647 if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
21648 save = preg->reginput;
21649 if (regmatch(preg, next)) {
21650 if (OP(preg, scan) < CLOSE) {
21651 int no = OP(preg, scan) - OPEN;
21652 if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
21653 preg->pmatch[no].rm_so = save - preg->start;
21656 else {
21657 int no = OP(preg, scan) - CLOSE;
21658 if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
21659 preg->pmatch[no].rm_eo = save - preg->start;
21662 return(1);
21664 return(0);
21666 return REG_ERR_INTERNAL;
21669 scan = next;
21672 return REG_ERR_INTERNAL;
21675 static int regrepeat(regex_t *preg, int p, int max)
21677 int count = 0;
21678 const char *scan;
21679 int opnd;
21680 int ch;
21681 int n;
21683 scan = preg->reginput;
21684 opnd = OPERAND(p);
21685 switch (OP(preg, p)) {
21686 case ANY:
21688 while (!reg_iseol(preg, *scan) && count < max) {
21689 count++;
21690 scan++;
21692 break;
21693 case EXACTLY:
21694 while (count < max) {
21695 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21696 if (preg->program[opnd] != ch) {
21697 break;
21699 count++;
21700 scan += n;
21702 break;
21703 case ANYOF:
21704 while (count < max) {
21705 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21706 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) {
21707 break;
21709 count++;
21710 scan += n;
21712 break;
21713 case ANYBUT:
21714 while (count < max) {
21715 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21716 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
21717 break;
21719 count++;
21720 scan += n;
21722 break;
21723 default:
21724 preg->err = REG_ERR_INTERNAL;
21725 count = 0;
21726 break;
21728 preg->reginput = scan;
21730 return(count);
21733 static int regnext(regex_t *preg, int p )
21735 int offset;
21737 offset = NEXT(preg, p);
21739 if (offset == 0)
21740 return 0;
21742 if (OP(preg, p) == BACK)
21743 return(p-offset);
21744 else
21745 return(p+offset);
21748 static int regopsize(regex_t *preg, int p )
21751 switch (OP(preg, p)) {
21752 case REP:
21753 case REPMIN:
21754 case REPX:
21755 case REPXMIN:
21756 return 5;
21758 case ANYOF:
21759 case ANYBUT:
21760 case EXACTLY: {
21761 int s = p + 2;
21762 while (preg->program[s++]) {
21764 return s - p;
21767 return 2;
21771 size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
21773 static const char *error_strings[] = {
21774 "success",
21775 "no match",
21776 "bad pattern",
21777 "null argument",
21778 "unknown error",
21779 "too big",
21780 "out of memory",
21781 "too many ()",
21782 "parentheses () not balanced",
21783 "braces {} not balanced",
21784 "invalid repetition count(s)",
21785 "extra characters",
21786 "*+ of empty atom",
21787 "nested count",
21788 "internal error",
21789 "count follows nothing",
21790 "trailing backslash",
21791 "corrupted program",
21792 "contains null char",
21794 const char *err;
21796 if (errcode < 0 || errcode >= REG_ERR_NUM) {
21797 err = "Bad error code";
21799 else {
21800 err = error_strings[errcode];
21803 return snprintf(errbuf, errbuf_size, "%s", err);
21806 void regfree(regex_t *preg)
21808 free(preg->program);
21811 #endif
21813 #if defined(_WIN32) || defined(WIN32)
21814 #ifndef STRICT
21815 #define STRICT
21816 #endif
21817 #define WIN32_LEAN_AND_MEAN
21818 #include <windows.h>
21820 #if defined(HAVE_DLOPEN_COMPAT)
21821 void *dlopen(const char *path, int mode)
21823 JIM_NOTUSED(mode);
21825 return (void *)LoadLibraryA(path);
21828 int dlclose(void *handle)
21830 FreeLibrary((HANDLE)handle);
21831 return 0;
21834 void *dlsym(void *handle, const char *symbol)
21836 return GetProcAddress((HMODULE)handle, symbol);
21839 char *dlerror(void)
21841 static char msg[121];
21842 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
21843 LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
21844 return msg;
21846 #endif
21848 #ifdef _MSC_VER
21850 #include <sys/timeb.h>
21853 int gettimeofday(struct timeval *tv, void *unused)
21855 struct _timeb tb;
21857 _ftime(&tb);
21858 tv->tv_sec = tb.time;
21859 tv->tv_usec = tb.millitm * 1000;
21861 return 0;
21865 DIR *opendir(const char *name)
21867 DIR *dir = 0;
21869 if (name && name[0]) {
21870 size_t base_length = strlen(name);
21871 const char *all =
21872 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
21874 if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
21875 (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
21876 strcat(strcpy(dir->name, name), all);
21878 if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
21879 dir->result.d_name = 0;
21880 else {
21881 Jim_Free(dir->name);
21882 Jim_Free(dir);
21883 dir = 0;
21886 else {
21887 Jim_Free(dir);
21888 dir = 0;
21889 errno = ENOMEM;
21892 else {
21893 errno = EINVAL;
21895 return dir;
21898 int closedir(DIR * dir)
21900 int result = -1;
21902 if (dir) {
21903 if (dir->handle != -1)
21904 result = _findclose(dir->handle);
21905 Jim_Free(dir->name);
21906 Jim_Free(dir);
21908 if (result == -1)
21909 errno = EBADF;
21910 return result;
21913 struct dirent *readdir(DIR * dir)
21915 struct dirent *result = 0;
21917 if (dir && dir->handle != -1) {
21918 if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
21919 result = &dir->result;
21920 result->d_name = dir->info.name;
21923 else {
21924 errno = EBADF;
21926 return result;
21928 #endif
21929 #endif
21930 #ifndef JIM_BOOTSTRAP_LIB_ONLY
21931 #include <errno.h>
21932 #include <string.h>
21935 #ifdef USE_LINENOISE
21936 #ifdef HAVE_UNISTD_H
21937 #include <unistd.h>
21938 #endif
21939 #ifdef HAVE_SYS_STAT_H
21940 #include <sys/stat.h>
21941 #endif
21942 #include "linenoise.h"
21943 #else
21944 #define MAX_LINE_LEN 512
21945 #endif
21947 char *Jim_HistoryGetline(const char *prompt)
21949 #ifdef USE_LINENOISE
21950 return linenoise(prompt);
21951 #else
21952 int len;
21953 char *line = malloc(MAX_LINE_LEN);
21955 fputs(prompt, stdout);
21956 fflush(stdout);
21958 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
21959 free(line);
21960 return NULL;
21962 len = strlen(line);
21963 if (len && line[len - 1] == '\n') {
21964 line[len - 1] = '\0';
21966 return line;
21967 #endif
21970 void Jim_HistoryLoad(const char *filename)
21972 #ifdef USE_LINENOISE
21973 linenoiseHistoryLoad(filename);
21974 #endif
21977 void Jim_HistoryAdd(const char *line)
21979 #ifdef USE_LINENOISE
21980 linenoiseHistoryAdd(line);
21981 #endif
21984 void Jim_HistorySave(const char *filename)
21986 #ifdef USE_LINENOISE
21987 #ifdef HAVE_UMASK
21988 mode_t mask;
21990 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
21991 #endif
21992 linenoiseHistorySave(filename);
21993 #ifdef HAVE_UMASK
21994 mask = umask(mask);
21995 #endif
21996 #endif
21999 void Jim_HistoryShow(void)
22001 #ifdef USE_LINENOISE
22003 int i;
22004 int len;
22005 char **history = linenoiseHistory(&len);
22006 for (i = 0; i < len; i++) {
22007 printf("%4d %s\n", i + 1, history[i]);
22009 #endif
22012 #ifdef USE_LINENOISE
22013 struct JimCompletionInfo {
22014 Jim_Interp *interp;
22015 Jim_Obj *command;
22018 void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
22020 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
22021 Jim_Obj *objv[2];
22022 int ret;
22024 objv[0] = info->command;
22025 objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
22027 ret = Jim_EvalObjVector(info->interp, 2, objv);
22030 if (ret == JIM_OK) {
22031 int i;
22032 Jim_Obj *listObj = Jim_GetResult(info->interp);
22033 int len = Jim_ListLength(info->interp, listObj);
22034 for (i = 0; i < len; i++) {
22035 linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
22039 #endif
22041 int Jim_InteractivePrompt(Jim_Interp *interp)
22043 int retcode = JIM_OK;
22044 char *history_file = NULL;
22045 #ifdef USE_LINENOISE
22046 const char *home;
22047 struct JimCompletionInfo compinfo;
22049 home = getenv("HOME");
22050 if (home && isatty(STDIN_FILENO)) {
22051 int history_len = strlen(home) + sizeof("/.jim_history");
22052 history_file = Jim_Alloc(history_len);
22053 snprintf(history_file, history_len, "%s/.jim_history", home);
22054 Jim_HistoryLoad(history_file);
22057 compinfo.interp = interp;
22058 compinfo.command = Jim_NewStringObj(interp, "tcl::autocomplete", -1);
22059 Jim_IncrRefCount(compinfo.command);
22062 linenoiseSetCompletionCallback(JimCompletionCallback, &compinfo);
22063 #endif
22065 printf("Welcome to Jim version %d.%d\n",
22066 JIM_VERSION / 100, JIM_VERSION % 100);
22067 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
22069 while (1) {
22070 Jim_Obj *scriptObjPtr;
22071 const char *result;
22072 int reslen;
22073 char prompt[20];
22075 if (retcode != JIM_OK) {
22076 const char *retcodestr = Jim_ReturnCode(retcode);
22078 if (*retcodestr == '?') {
22079 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
22081 else {
22082 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
22085 else {
22086 strcpy(prompt, ". ");
22089 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
22090 Jim_IncrRefCount(scriptObjPtr);
22091 while (1) {
22092 char state;
22093 char *line;
22095 line = Jim_HistoryGetline(prompt);
22096 if (line == NULL) {
22097 if (errno == EINTR) {
22098 continue;
22100 Jim_DecrRefCount(interp, scriptObjPtr);
22101 retcode = JIM_OK;
22102 goto out;
22104 if (Jim_Length(scriptObjPtr) != 0) {
22106 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
22108 Jim_AppendString(interp, scriptObjPtr, line, -1);
22109 free(line);
22110 if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
22111 break;
22113 snprintf(prompt, sizeof(prompt), "%c> ", state);
22115 #ifdef USE_LINENOISE
22116 if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
22118 Jim_HistoryShow();
22119 Jim_DecrRefCount(interp, scriptObjPtr);
22120 continue;
22123 Jim_HistoryAdd(Jim_String(scriptObjPtr));
22124 if (history_file) {
22125 Jim_HistorySave(history_file);
22127 #endif
22128 retcode = Jim_EvalObj(interp, scriptObjPtr);
22129 Jim_DecrRefCount(interp, scriptObjPtr);
22131 if (retcode == JIM_EXIT) {
22132 break;
22134 if (retcode == JIM_ERR) {
22135 Jim_MakeErrorMessage(interp);
22137 result = Jim_GetString(Jim_GetResult(interp), &reslen);
22138 if (reslen) {
22139 printf("%s\n", result);
22142 out:
22143 Jim_Free(history_file);
22145 #ifdef USE_LINENOISE
22146 Jim_DecrRefCount(interp, compinfo.command);
22147 linenoiseSetCompletionCallback(NULL, NULL);
22148 #endif
22150 return retcode;
22153 #include <stdio.h>
22154 #include <stdlib.h>
22155 #include <string.h>
22159 extern int Jim_initjimshInit(Jim_Interp *interp);
22161 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
22163 int n;
22164 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
22167 for (n = 0; n < argc; n++) {
22168 Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
22170 Jim_ListAppendElement(interp, listObj, obj);
22173 Jim_SetVariableStr(interp, "argv", listObj);
22174 Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
22177 static void JimPrintErrorMessage(Jim_Interp *interp)
22179 Jim_MakeErrorMessage(interp);
22180 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
22183 void usage(const char* executable_name)
22185 printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
22186 printf("Usage: %s\n", executable_name);
22187 printf("or : %s [options] [filename]\n", executable_name);
22188 printf("\n");
22189 printf("Without options: Interactive mode\n");
22190 printf("\n");
22191 printf("Options:\n");
22192 printf(" --version : prints the version string\n");
22193 printf(" --help : prints this text\n");
22194 printf(" -e CMD : executes command CMD\n");
22195 printf(" NOTE: all subsequent options will be passed as arguments to the command\n");
22196 printf(" [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n");
22197 printf(" NOTE: all subsequent options will be passed to the script\n\n");
22200 int main(int argc, char *const argv[])
22202 int retcode;
22203 Jim_Interp *interp;
22204 char *const orig_argv0 = argv[0];
22207 if (argc > 1 && strcmp(argv[1], "--version") == 0) {
22208 printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
22209 return 0;
22211 else if (argc > 1 && strcmp(argv[1], "--help") == 0) {
22212 usage(argv[0]);
22213 return 0;
22217 interp = Jim_CreateInterp();
22218 Jim_RegisterCoreCommands(interp);
22221 if (Jim_InitStaticExtensions(interp) != JIM_OK) {
22222 JimPrintErrorMessage(interp);
22225 Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
22226 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
22227 retcode = Jim_initjimshInit(interp);
22229 if (argc == 1) {
22231 if (retcode == JIM_ERR) {
22232 JimPrintErrorMessage(interp);
22234 if (retcode != JIM_EXIT) {
22235 JimSetArgv(interp, 0, NULL);
22236 retcode = Jim_InteractivePrompt(interp);
22239 else {
22241 if (argc > 2 && strcmp(argv[1], "-e") == 0) {
22243 JimSetArgv(interp, argc - 3, argv + 3);
22244 retcode = Jim_Eval(interp, argv[2]);
22245 if (retcode != JIM_ERR) {
22246 printf("%s\n", Jim_String(Jim_GetResult(interp)));
22249 else {
22250 Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
22251 JimSetArgv(interp, argc - 2, argv + 2);
22252 if (strcmp(argv[1], "-") == 0) {
22253 retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]");
22254 } else {
22255 retcode = Jim_EvalFile(interp, argv[1]);
22258 if (retcode == JIM_ERR) {
22259 JimPrintErrorMessage(interp);
22262 if (retcode == JIM_EXIT) {
22263 retcode = Jim_GetExitCode(interp);
22265 else if (retcode == JIM_ERR) {
22266 retcode = 1;
22268 else {
22269 retcode = 0;
22271 Jim_FreeInterp(interp);
22272 return retcode;
22274 #endif