tcltest: do a better job of cleanup up after tests
[jimtcl.git] / autosetup / jimsh0.c
blob463c6c329842a2fe4f25697e35e2d291acdf834c
1 /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */
2 #define _GNU_SOURCE
3 #define JIM_TCL_COMPAT
4 #define JIM_REFERENCES
5 #define JIM_ANSIC
6 #define JIM_REGEXP
7 #define HAVE_NO_AUTOCONF
8 #define _JIMAUTOCONF_H
9 #define TCL_LIBRARY "."
10 #define jim_ext_bootstrap
11 #define jim_ext_aio
12 #define jim_ext_readdir
13 #define jim_ext_regexp
14 #define jim_ext_file
15 #define jim_ext_glob
16 #define jim_ext_exec
17 #define jim_ext_clock
18 #define jim_ext_array
19 #define jim_ext_stdlib
20 #define jim_ext_tclcompat
21 #if defined(_MSC_VER)
22 #define TCL_PLATFORM_OS "windows"
23 #define TCL_PLATFORM_PLATFORM "windows"
24 #define TCL_PLATFORM_PATH_SEPARATOR ";"
25 #define HAVE_MKDIR_ONE_ARG
26 #define HAVE_SYSTEM
27 #elif defined(__MINGW32__)
28 #define TCL_PLATFORM_OS "mingw"
29 #define TCL_PLATFORM_PLATFORM "windows"
30 #define TCL_PLATFORM_PATH_SEPARATOR ";"
31 #define HAVE_MKDIR_ONE_ARG
32 #define HAVE_SYSTEM
33 #define HAVE_SYS_TIME_H
34 #define HAVE_DIRENT_H
35 #define HAVE_UNISTD_H
36 #else
37 #define TCL_PLATFORM_OS "unknown"
38 #define TCL_PLATFORM_PLATFORM "unix"
39 #define TCL_PLATFORM_PATH_SEPARATOR ":"
40 #define HAVE_VFORK
41 #define HAVE_WAITPID
42 #define HAVE_ISATTY
43 #define HAVE_MKSTEMP
44 #define HAVE_LINK
45 #define HAVE_SYS_TIME_H
46 #define HAVE_DIRENT_H
47 #define HAVE_UNISTD_H
48 #endif
49 #define JIM_VERSION 76
50 #ifndef JIM_WIN32COMPAT_H
51 #define JIM_WIN32COMPAT_H
55 #ifdef __cplusplus
56 extern "C" {
57 #endif
60 #if defined(_WIN32) || defined(WIN32)
62 #define HAVE_DLOPEN
63 void *dlopen(const char *path, int mode);
64 int dlclose(void *handle);
65 void *dlsym(void *handle, const char *symbol);
66 char *dlerror(void);
69 #if defined(__MINGW32__)
70 #define JIM_SPRINTF_DOUBLE_NEEDS_FIX
71 #endif
73 #ifdef _MSC_VER
76 #if _MSC_VER >= 1000
77 #pragma warning(disable:4146)
78 #endif
80 #include <limits.h>
81 #define jim_wide _int64
82 #ifndef LLONG_MAX
83 #define LLONG_MAX 9223372036854775807I64
84 #endif
85 #ifndef LLONG_MIN
86 #define LLONG_MIN (-LLONG_MAX - 1I64)
87 #endif
88 #define JIM_WIDE_MIN LLONG_MIN
89 #define JIM_WIDE_MAX LLONG_MAX
90 #define JIM_WIDE_MODIFIER "I64d"
91 #define strcasecmp _stricmp
92 #define strtoull _strtoui64
93 #define snprintf _snprintf
95 #include <io.h>
97 struct timeval {
98 long tv_sec;
99 long tv_usec;
102 int gettimeofday(struct timeval *tv, void *unused);
104 #define HAVE_OPENDIR
105 struct dirent {
106 char *d_name;
109 typedef struct DIR {
110 long handle;
111 struct _finddata_t info;
112 struct dirent result;
113 char *name;
114 } DIR;
116 DIR *opendir(const char *name);
117 int closedir(DIR *dir);
118 struct dirent *readdir(DIR *dir);
120 #elif defined(__MINGW32__)
122 #include <stdlib.h>
123 #define strtod __strtod
125 #endif
127 #endif
129 #ifdef __cplusplus
131 #endif
133 #endif
134 #ifndef UTF8_UTIL_H
135 #define UTF8_UTIL_H
137 #ifdef __cplusplus
138 extern "C" {
139 #endif
143 #define MAX_UTF8_LEN 4
145 int utf8_fromunicode(char *p, unsigned uc);
147 #ifndef JIM_UTF8
148 #include <ctype.h>
151 #define utf8_strlen(S, B) ((B) < 0 ? strlen(S) : (B))
152 #define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
153 #define utf8_getchars(CP, C) (*(CP) = (C), 1)
154 #define utf8_upper(C) toupper(C)
155 #define utf8_title(C) toupper(C)
156 #define utf8_lower(C) tolower(C)
157 #define utf8_index(C, I) (I)
158 #define utf8_charlen(C) 1
159 #define utf8_prev_len(S, L) 1
161 #else
163 #endif
165 #ifdef __cplusplus
167 #endif
169 #endif
171 #ifndef __JIM__H
172 #define __JIM__H
174 #ifdef __cplusplus
175 extern "C" {
176 #endif
178 #include <time.h>
179 #include <limits.h>
180 #include <stdio.h>
181 #include <stdlib.h>
182 #include <stdarg.h>
185 #ifndef HAVE_NO_AUTOCONF
186 #endif
190 #ifndef jim_wide
191 # ifdef HAVE_LONG_LONG
192 # define jim_wide long long
193 # ifndef LLONG_MAX
194 # define LLONG_MAX 9223372036854775807LL
195 # endif
196 # ifndef LLONG_MIN
197 # define LLONG_MIN (-LLONG_MAX - 1LL)
198 # endif
199 # define JIM_WIDE_MIN LLONG_MIN
200 # define JIM_WIDE_MAX LLONG_MAX
201 # else
202 # define jim_wide long
203 # define JIM_WIDE_MIN LONG_MIN
204 # define JIM_WIDE_MAX LONG_MAX
205 # endif
208 # ifdef HAVE_LONG_LONG
209 # define JIM_WIDE_MODIFIER "lld"
210 # else
211 # define JIM_WIDE_MODIFIER "ld"
212 # define strtoull strtoul
213 # endif
214 #endif
216 #define UCHAR(c) ((unsigned char)(c))
219 #define JIM_OK 0
220 #define JIM_ERR 1
221 #define JIM_RETURN 2
222 #define JIM_BREAK 3
223 #define JIM_CONTINUE 4
224 #define JIM_SIGNAL 5
225 #define JIM_EXIT 6
227 #define JIM_EVAL 7
229 #define JIM_MAX_CALLFRAME_DEPTH 1000
230 #define JIM_MAX_EVAL_DEPTH 2000
233 #define JIM_PRIV_FLAG_SHIFT 20
235 #define JIM_NONE 0
236 #define JIM_ERRMSG 1
237 #define JIM_ENUM_ABBREV 2
238 #define JIM_UNSHARED 4
239 #define JIM_MUSTEXIST 8
242 #define JIM_SUBST_NOVAR 1
243 #define JIM_SUBST_NOCMD 2
244 #define JIM_SUBST_NOESC 4
245 #define JIM_SUBST_FLAG 128
248 #define JIM_CASESENS 0
249 #define JIM_NOCASE 1
252 #define JIM_PATH_LEN 1024
255 #define JIM_NOTUSED(V) ((void) V)
257 #define JIM_LIBPATH "auto_path"
258 #define JIM_INTERACTIVE "tcl_interactive"
261 typedef struct Jim_Stack {
262 int len;
263 int maxlen;
264 void **vector;
265 } Jim_Stack;
268 typedef struct Jim_HashEntry {
269 void *key;
270 union {
271 void *val;
272 int intval;
273 } u;
274 struct Jim_HashEntry *next;
275 } Jim_HashEntry;
277 typedef struct Jim_HashTableType {
278 unsigned int (*hashFunction)(const void *key);
279 void *(*keyDup)(void *privdata, const void *key);
280 void *(*valDup)(void *privdata, const void *obj);
281 int (*keyCompare)(void *privdata, const void *key1, const void *key2);
282 void (*keyDestructor)(void *privdata, void *key);
283 void (*valDestructor)(void *privdata, void *obj);
284 } Jim_HashTableType;
286 typedef struct Jim_HashTable {
287 Jim_HashEntry **table;
288 const Jim_HashTableType *type;
289 void *privdata;
290 unsigned int size;
291 unsigned int sizemask;
292 unsigned int used;
293 unsigned int collisions;
294 unsigned int uniq;
295 } Jim_HashTable;
297 typedef struct Jim_HashTableIterator {
298 Jim_HashTable *ht;
299 Jim_HashEntry *entry, *nextEntry;
300 int index;
301 } Jim_HashTableIterator;
304 #define JIM_HT_INITIAL_SIZE 16
307 #define Jim_FreeEntryVal(ht, entry) \
308 if ((ht)->type->valDestructor) \
309 (ht)->type->valDestructor((ht)->privdata, (entry)->u.val)
311 #define Jim_SetHashVal(ht, entry, _val_) do { \
312 if ((ht)->type->valDup) \
313 (entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \
314 else \
315 (entry)->u.val = (_val_); \
316 } while(0)
318 #define Jim_FreeEntryKey(ht, entry) \
319 if ((ht)->type->keyDestructor) \
320 (ht)->type->keyDestructor((ht)->privdata, (entry)->key)
322 #define Jim_SetHashKey(ht, entry, _key_) do { \
323 if ((ht)->type->keyDup) \
324 (entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \
325 else \
326 (entry)->key = (void *)(_key_); \
327 } while(0)
329 #define Jim_CompareHashKeys(ht, key1, key2) \
330 (((ht)->type->keyCompare) ? \
331 (ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \
332 (key1) == (key2))
334 #define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq)
336 #define Jim_GetHashEntryKey(he) ((he)->key)
337 #define Jim_GetHashEntryVal(he) ((he)->u.val)
338 #define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
339 #define Jim_GetHashTableSize(ht) ((ht)->size)
340 #define Jim_GetHashTableUsed(ht) ((ht)->used)
343 typedef struct Jim_Obj {
344 char *bytes;
345 const struct Jim_ObjType *typePtr;
346 int refCount;
347 int length;
349 union {
351 jim_wide wideValue;
353 int intValue;
355 double doubleValue;
357 void *ptr;
359 struct {
360 void *ptr1;
361 void *ptr2;
362 } twoPtrValue;
364 struct {
365 struct Jim_Var *varPtr;
366 unsigned long callFrameId;
367 int global;
368 } varValue;
370 struct {
371 struct Jim_Obj *nsObj;
372 struct Jim_Cmd *cmdPtr;
373 unsigned long procEpoch;
374 } cmdValue;
376 struct {
377 struct Jim_Obj **ele;
378 int len;
379 int maxLen;
380 } listValue;
382 struct {
383 int maxLength;
384 int charLength;
385 } strValue;
387 struct {
388 unsigned long id;
389 struct Jim_Reference *refPtr;
390 } refValue;
392 struct {
393 struct Jim_Obj *fileNameObj;
394 int lineNumber;
395 } sourceValue;
397 struct {
398 struct Jim_Obj *varNameObjPtr;
399 struct Jim_Obj *indexObjPtr;
400 } dictSubstValue;
402 struct {
403 void *compre;
404 unsigned flags;
405 } regexpValue;
406 struct {
407 int line;
408 int argc;
409 } scriptLineValue;
410 } internalRep;
411 struct Jim_Obj *prevObjPtr;
412 struct Jim_Obj *nextObjPtr;
413 } Jim_Obj;
416 #define Jim_IncrRefCount(objPtr) \
417 ++(objPtr)->refCount
418 #define Jim_DecrRefCount(interp, objPtr) \
419 if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr)
420 #define Jim_IsShared(objPtr) \
421 ((objPtr)->refCount > 1)
423 #define Jim_FreeNewObj Jim_FreeObj
426 #define Jim_FreeIntRep(i,o) \
427 if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \
428 (o)->typePtr->freeIntRepProc(i, o)
431 #define Jim_GetIntRepPtr(o) (o)->internalRep.ptr
434 #define Jim_SetIntRepPtr(o, p) \
435 (o)->internalRep.ptr = (p)
438 struct Jim_Interp;
440 typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp,
441 struct Jim_Obj *objPtr);
442 typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp,
443 struct Jim_Obj *srcPtr, Jim_Obj *dupPtr);
444 typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr);
446 typedef struct Jim_ObjType {
447 const char *name;
448 Jim_FreeInternalRepProc *freeIntRepProc;
449 Jim_DupInternalRepProc *dupIntRepProc;
450 Jim_UpdateStringProc *updateStringProc;
451 int flags;
452 } Jim_ObjType;
455 #define JIM_TYPE_NONE 0
456 #define JIM_TYPE_REFERENCES 1
460 typedef struct Jim_CallFrame {
461 unsigned long id;
462 int level;
463 struct Jim_HashTable vars;
464 struct Jim_HashTable *staticVars;
465 struct Jim_CallFrame *parent;
466 Jim_Obj *const *argv;
467 int argc;
468 Jim_Obj *procArgsObjPtr;
469 Jim_Obj *procBodyObjPtr;
470 struct Jim_CallFrame *next;
471 Jim_Obj *nsObj;
472 Jim_Obj *fileNameObj;
473 int line;
474 Jim_Stack *localCommands;
475 struct Jim_Obj *tailcallObj;
476 struct Jim_Cmd *tailcallCmd;
477 } Jim_CallFrame;
479 typedef struct Jim_Var {
480 Jim_Obj *objPtr;
481 struct Jim_CallFrame *linkFramePtr;
482 } Jim_Var;
485 typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
486 Jim_Obj *const *argv);
487 typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);
491 typedef struct Jim_Cmd {
492 int inUse;
493 int isproc;
494 struct Jim_Cmd *prevCmd;
495 union {
496 struct {
498 Jim_CmdProc *cmdProc;
499 Jim_DelCmdProc *delProc;
500 void *privData;
501 } native;
502 struct {
504 Jim_Obj *argListObjPtr;
505 Jim_Obj *bodyObjPtr;
506 Jim_HashTable *staticVars;
507 int argListLen;
508 int reqArity;
509 int optArity;
510 int argsPos;
511 int upcall;
512 struct Jim_ProcArg {
513 Jim_Obj *nameObjPtr;
514 Jim_Obj *defaultObjPtr;
515 } *arglist;
516 Jim_Obj *nsObj;
517 } proc;
518 } u;
519 } Jim_Cmd;
522 typedef struct Jim_PrngState {
523 unsigned char sbox[256];
524 unsigned int i, j;
525 } Jim_PrngState;
527 typedef struct Jim_Interp {
528 Jim_Obj *result;
529 int errorLine;
530 Jim_Obj *errorFileNameObj;
531 int addStackTrace;
532 int maxCallFrameDepth;
533 int maxEvalDepth;
534 int evalDepth;
535 int returnCode;
536 int returnLevel;
537 int exitCode;
538 long id;
539 int signal_level;
540 jim_wide sigmask;
541 int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask);
542 Jim_CallFrame *framePtr;
543 Jim_CallFrame *topFramePtr;
544 struct Jim_HashTable commands;
545 unsigned long procEpoch; /* Incremented every time the result
546 of procedures names lookup caching
547 may no longer be valid. */
548 unsigned long callFrameEpoch; /* Incremented every time a new
549 callframe is created. This id is used for the
550 'ID' field contained in the Jim_CallFrame
551 structure. */
552 int local;
553 Jim_Obj *liveList;
554 Jim_Obj *freeList;
555 Jim_Obj *currentScriptObj;
556 Jim_Obj *nullScriptObj;
557 Jim_Obj *emptyObj;
558 Jim_Obj *trueObj;
559 Jim_Obj *falseObj;
560 unsigned long referenceNextId;
561 struct Jim_HashTable references;
562 unsigned long lastCollectId; /* reference max Id of the last GC
563 execution. It's set to -1 while the collection
564 is running as sentinel to avoid to recursive
565 calls via the [collect] command inside
566 finalizers. */
567 time_t lastCollectTime;
568 Jim_Obj *stackTrace;
569 Jim_Obj *errorProc;
570 Jim_Obj *unknown;
571 int unknown_called;
572 int errorFlag;
573 void *cmdPrivData; /* Used to pass the private data pointer to
574 a command. It is set to what the user specified
575 via Jim_CreateCommand(). */
577 struct Jim_CallFrame *freeFramesList;
578 struct Jim_HashTable assocData;
579 Jim_PrngState *prngState;
580 struct Jim_HashTable packages;
581 Jim_Stack *loadHandles;
582 } Jim_Interp;
584 #define Jim_InterpIncrProcEpoch(i) (i)->procEpoch++
585 #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
586 #define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))
588 #define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
589 #define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj)
590 #define Jim_GetResult(i) ((i)->result)
591 #define Jim_CmdPrivData(i) ((i)->cmdPrivData)
593 #define Jim_SetResult(i,o) do { \
594 Jim_Obj *_resultObjPtr_ = (o); \
595 Jim_IncrRefCount(_resultObjPtr_); \
596 Jim_DecrRefCount(i,(i)->result); \
597 (i)->result = _resultObjPtr_; \
598 } while(0)
601 #define Jim_GetId(i) (++(i)->id)
604 #define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference
605 string representation must be fixed length. */
606 typedef struct Jim_Reference {
607 Jim_Obj *objPtr;
608 Jim_Obj *finalizerCmdNamePtr;
609 char tag[JIM_REFERENCE_TAGLEN+1];
610 } Jim_Reference;
613 #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
614 #define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
616 #define JIM_EXPORT
619 JIM_EXPORT void *Jim_Alloc (int size);
620 JIM_EXPORT void *Jim_Realloc(void *ptr, int size);
621 JIM_EXPORT void Jim_Free (void *ptr);
622 JIM_EXPORT char * Jim_StrDup (const char *s);
623 JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
626 JIM_EXPORT char **Jim_GetEnviron(void);
627 JIM_EXPORT void Jim_SetEnviron(char **env);
628 JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *template);
631 JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
634 JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);
636 #define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S))
638 JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script);
639 JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename);
640 JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename);
641 JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr);
642 JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc,
643 Jim_Obj *const *objv);
644 JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj);
645 JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
646 int objc, Jim_Obj *const *objv);
647 #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
648 JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
649 JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
650 Jim_Obj **resObjPtrPtr, int flags);
653 JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
654 JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
655 JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
656 JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
657 JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
658 JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
659 JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr));
662 JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
663 const Jim_HashTableType *type, void *privdata);
664 JIM_EXPORT void Jim_ExpandHashTable (Jim_HashTable *ht,
665 unsigned int size);
666 JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key,
667 void *val);
668 JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
669 const void *key, void *val);
670 JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
671 const void *key);
672 JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
673 JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
674 const void *key);
675 JIM_EXPORT void Jim_ResizeHashTable (Jim_HashTable *ht);
676 JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator
677 (Jim_HashTable *ht);
678 JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
679 (Jim_HashTableIterator *iter);
682 JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
683 JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr);
684 JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr);
685 JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
686 Jim_Obj *objPtr);
687 JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
688 int *lenPtr);
689 JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
690 JIM_EXPORT int Jim_Length(Jim_Obj *objPtr);
693 JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp,
694 const char *s, int len);
695 JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp,
696 const char *s, int charlen);
697 JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp,
698 char *s, int len);
699 JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr,
700 const char *str, int len);
701 JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr,
702 Jim_Obj *appendObjPtr);
703 JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp,
704 Jim_Obj *objPtr, ...);
705 JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr);
706 JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr,
707 Jim_Obj *objPtr, int nocase);
708 JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp,
709 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr,
710 Jim_Obj *lastObjPtr);
711 JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp,
712 Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
713 JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
714 Jim_Obj *fmtObjPtr, int flags);
715 JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
716 Jim_Obj *objPtr, const char *str);
717 JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
718 Jim_Obj *secondObjPtr, int nocase);
719 JIM_EXPORT int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
720 Jim_Obj *secondObjPtr, int nocase);
721 JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr);
724 JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp,
725 Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr);
726 JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp,
727 Jim_Obj *objPtr);
728 JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr);
729 JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr);
732 JIM_EXPORT Jim_Interp * Jim_CreateInterp (void);
733 JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i);
734 JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp);
735 JIM_EXPORT const char *Jim_ReturnCode(int code);
736 JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...);
739 JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
740 JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
741 const char *cmdName, Jim_CmdProc *cmdProc, void *privData,
742 Jim_DelCmdProc *delProc);
743 JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
744 const char *cmdName);
745 JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
746 const char *oldName, const char *newName);
747 JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
748 Jim_Obj *objPtr, int flags);
749 JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
750 Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
751 JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
752 const char *name, Jim_Obj *objPtr);
753 JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp,
754 const char *name, Jim_Obj *objPtr);
755 JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp,
756 const char *name, const char *val);
757 JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp,
758 Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr,
759 Jim_CallFrame *targetCallFrame);
760 JIM_EXPORT Jim_Obj * Jim_MakeGlobalNamespaceName(Jim_Interp *interp,
761 Jim_Obj *nameObjPtr);
762 JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp,
763 Jim_Obj *nameObjPtr, int flags);
764 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp,
765 Jim_Obj *nameObjPtr, int flags);
766 JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp,
767 const char *name, int flags);
768 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp,
769 const char *name, int flags);
770 JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp,
771 Jim_Obj *nameObjPtr, int flags);
774 JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp,
775 Jim_Obj *levelObjPtr);
778 JIM_EXPORT int Jim_Collect (Jim_Interp *interp);
779 JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp);
782 JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr,
783 int *indexPtr);
786 JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp,
787 Jim_Obj *const *elements, int len);
788 JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp,
789 Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec);
790 JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp,
791 Jim_Obj *listPtr, Jim_Obj *objPtr);
792 JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp,
793 Jim_Obj *listPtr, Jim_Obj *appendListPtr);
794 JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr);
795 JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt,
796 int listindex, Jim_Obj **objPtrPtr, int seterr);
797 JIM_EXPORT Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx);
798 JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp,
799 Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc,
800 Jim_Obj *newObjPtr);
801 JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc,
802 Jim_Obj *const *objv);
803 JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp,
804 Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen);
807 JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp,
808 Jim_Obj *const *elements, int len);
809 JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr,
810 Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
811 JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
812 Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
813 Jim_Obj **objPtrPtr, int flags);
814 JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
815 Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
816 Jim_Obj *newObjPtr, int flags);
817 JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp,
818 Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len);
819 JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
820 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
821 JIM_EXPORT int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj);
822 JIM_EXPORT int Jim_DictValues(Jim_Interp *interp, Jim_Obj *dictObjPtr, Jim_Obj *patternObjPtr);
823 JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
824 JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr);
827 JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
828 int *intPtr);
831 JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
832 Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr);
833 JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
834 Jim_Obj *exprObjPtr, int *boolPtr);
837 JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
838 jim_wide *widePtr);
839 JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
840 long *longPtr);
841 #define Jim_NewWideObj Jim_NewIntObj
842 JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
843 jim_wide wideValue);
846 JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
847 double *doublePtr);
848 JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
849 double doubleValue);
850 JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue);
853 JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc,
854 Jim_Obj *const *argv, const char *msg);
855 JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
856 const char * const *tablePtr, int *indexPtr, const char *name, int flags);
857 JIM_EXPORT int Jim_ScriptIsComplete(Jim_Interp *interp,
858 Jim_Obj *scriptObj, char *stateCharPtr);
860 JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);
863 typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
864 JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
865 JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
866 Jim_InterpDeleteProc *delProc, void *data);
867 JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
871 JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
872 const char *name, const char *ver, int flags);
873 JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
874 const char *name, int flags);
877 JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
880 JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
881 JIM_EXPORT void Jim_HistoryLoad(const char *filename);
882 JIM_EXPORT void Jim_HistorySave(const char *filename);
883 JIM_EXPORT char *Jim_HistoryGetline(const char *prompt);
884 JIM_EXPORT void Jim_HistoryAdd(const char *line);
885 JIM_EXPORT void Jim_HistoryShow(void);
888 JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
889 JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
890 JIM_EXPORT int Jim_IsBigEndian(void);
892 #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
895 JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
896 JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
899 JIM_EXPORT FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
902 JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
903 JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
905 #ifdef __cplusplus
907 #endif
909 #endif
911 #ifndef JIM_SUBCMD_H
912 #define JIM_SUBCMD_H
915 #ifdef __cplusplus
916 extern "C" {
917 #endif
920 #define JIM_MODFLAG_HIDDEN 0x0001
921 #define JIM_MODFLAG_FULLARGV 0x0002
925 typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
927 typedef struct {
928 const char *cmd;
929 const char *args;
930 jim_subcmd_function *function;
931 short minargs;
932 short maxargs;
933 unsigned short flags;
934 } jim_subcmd_type;
936 const jim_subcmd_type *
937 Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
939 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
941 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
943 #ifdef __cplusplus
945 #endif
947 #endif
948 #ifndef JIMREGEXP_H
949 #define JIMREGEXP_H
952 #ifdef __cplusplus
953 extern "C" {
954 #endif
956 #include <stdlib.h>
958 typedef struct {
959 int rm_so;
960 int rm_eo;
961 } regmatch_t;
964 typedef struct regexp {
966 int re_nsub;
969 int cflags;
970 int err;
971 int regstart;
972 int reganch;
973 int regmust;
974 int regmlen;
975 int *program;
978 const char *regparse;
979 int p;
980 int proglen;
983 int eflags;
984 const char *start;
985 const char *reginput;
986 const char *regbol;
989 regmatch_t *pmatch;
990 int nmatch;
991 } regexp;
993 typedef regexp regex_t;
995 #define REG_EXTENDED 0
996 #define REG_NEWLINE 1
997 #define REG_ICASE 2
999 #define REG_NOTBOL 16
1001 enum {
1002 REG_NOERROR,
1003 REG_NOMATCH,
1004 REG_BADPAT,
1005 REG_ERR_NULL_ARGUMENT,
1006 REG_ERR_UNKNOWN,
1007 REG_ERR_TOO_BIG,
1008 REG_ERR_NOMEM,
1009 REG_ERR_TOO_MANY_PAREN,
1010 REG_ERR_UNMATCHED_PAREN,
1011 REG_ERR_UNMATCHED_BRACES,
1012 REG_ERR_BAD_COUNT,
1013 REG_ERR_JUNK_ON_END,
1014 REG_ERR_OPERAND_COULD_BE_EMPTY,
1015 REG_ERR_NESTED_COUNT,
1016 REG_ERR_INTERNAL,
1017 REG_ERR_COUNT_FOLLOWS_NOTHING,
1018 REG_ERR_TRAILING_BACKSLASH,
1019 REG_ERR_CORRUPTED,
1020 REG_ERR_NULL_CHAR,
1021 REG_ERR_NUM
1024 int regcomp(regex_t *preg, const char *regex, int cflags);
1025 int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
1026 size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
1027 void regfree(regex_t *preg);
1029 #ifdef __cplusplus
1031 #endif
1033 #endif
1034 int Jim_bootstrapInit(Jim_Interp *interp)
1036 if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
1037 return JIM_ERR;
1039 return Jim_EvalSource(interp, "bootstrap.tcl", 1,
1040 "\n"
1041 "\n"
1042 "proc package {cmd pkg} {\n"
1043 " if {$cmd eq \"require\"} {\n"
1044 " foreach path $::auto_path {\n"
1045 " if {[file exists $path/$pkg.tcl]} {\n"
1046 " uplevel #0 [list source $path/$pkg.tcl]\n"
1047 " return\n"
1048 " }\n"
1049 " }\n"
1050 " }\n"
1051 "}\n"
1054 int Jim_initjimshInit(Jim_Interp *interp)
1056 if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
1057 return JIM_ERR;
1059 return Jim_EvalSource(interp, "initjimsh.tcl", 1,
1060 "\n"
1061 "\n"
1062 "\n"
1063 "proc _jimsh_init {} {\n"
1064 " rename _jimsh_init {}\n"
1065 " global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
1066 "\n"
1067 "\n"
1068 " if {[exists jim::argv0]} {\n"
1069 " if {[string match \"*/*\" $jim::argv0]} {\n"
1070 " set jim::exe [file join [pwd] $jim::argv0]\n"
1071 " } else {\n"
1072 " foreach path [split [env PATH \"\"] $tcl_platform(pathSeparator)] {\n"
1073 " set exec [file join [pwd] [string map {\\\\ /} $path] $jim::argv0]\n"
1074 " if {[file executable $exec]} {\n"
1075 " set jim::exe $exec\n"
1076 " break\n"
1077 " }\n"
1078 " }\n"
1079 " }\n"
1080 " }\n"
1081 "\n"
1082 "\n"
1083 " lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
1084 " if {[exists jim::exe]} {\n"
1085 " lappend p [file dirname $jim::exe]\n"
1086 " }\n"
1087 " lappend p {*}$auto_path\n"
1088 " set auto_path $p\n"
1089 "\n"
1090 " if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
1091 " foreach src {.jimrc jimrc.tcl} {\n"
1092 " if {[file exists [env HOME]/$src]} {\n"
1093 " uplevel #0 source [env HOME]/$src\n"
1094 " break\n"
1095 " }\n"
1096 " }\n"
1097 " }\n"
1098 " return \"\"\n"
1099 "}\n"
1100 "\n"
1101 "if {$tcl_platform(platform) eq \"windows\"} {\n"
1102 " set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
1103 "}\n"
1104 "\n"
1105 "_jimsh_init\n"
1108 int Jim_globInit(Jim_Interp *interp)
1110 if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
1111 return JIM_ERR;
1113 return Jim_EvalSource(interp, "glob.tcl", 1,
1114 "\n"
1115 "\n"
1116 "\n"
1117 "\n"
1118 "\n"
1119 "\n"
1120 "\n"
1121 "package require readdir\n"
1122 "\n"
1123 "\n"
1124 "proc glob.globdir {dir pattern} {\n"
1125 " if {[file exists $dir/$pattern]} {\n"
1126 "\n"
1127 " return [list $pattern]\n"
1128 " }\n"
1129 "\n"
1130 " set result {}\n"
1131 " set files [readdir $dir]\n"
1132 " lappend files . ..\n"
1133 "\n"
1134 " foreach name $files {\n"
1135 " if {[string match $pattern $name]} {\n"
1136 "\n"
1137 " if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n"
1138 " continue\n"
1139 " }\n"
1140 " lappend result $name\n"
1141 " }\n"
1142 " }\n"
1143 "\n"
1144 " return $result\n"
1145 "}\n"
1146 "\n"
1147 "\n"
1148 "\n"
1149 "\n"
1150 "proc glob.explode {pattern} {\n"
1151 " set oldexp {}\n"
1152 " set newexp {\"\"}\n"
1153 "\n"
1154 " while 1 {\n"
1155 " set oldexp $newexp\n"
1156 " set newexp {}\n"
1157 " set ob [string first \\{ $pattern]\n"
1158 " set cb [string first \\} $pattern]\n"
1159 "\n"
1160 " if {$ob < $cb && $ob != -1} {\n"
1161 " set mid [string range $pattern 0 $ob-1]\n"
1162 " set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n"
1163 " if {$pattern eq \"\"} {\n"
1164 " error \"unmatched open brace in glob pattern\"\n"
1165 " }\n"
1166 " set pattern [string range $pattern 1 end]\n"
1167 "\n"
1168 " foreach subs $subexp {\n"
1169 " foreach sub [split $subs ,] {\n"
1170 " foreach old $oldexp {\n"
1171 " lappend newexp $old$mid$sub\n"
1172 " }\n"
1173 " }\n"
1174 " }\n"
1175 " } elseif {$cb != -1} {\n"
1176 " set suf [string range $pattern 0 $cb-1]\n"
1177 " set rest [string range $pattern $cb end]\n"
1178 " break\n"
1179 " } else {\n"
1180 " set suf $pattern\n"
1181 " set rest \"\"\n"
1182 " break\n"
1183 " }\n"
1184 " }\n"
1185 "\n"
1186 " foreach old $oldexp {\n"
1187 " lappend newexp $old$suf\n"
1188 " }\n"
1189 " list $rest {*}$newexp\n"
1190 "}\n"
1191 "\n"
1192 "\n"
1193 "\n"
1194 "proc glob.glob {base pattern} {\n"
1195 " set dir [file dirname $pattern]\n"
1196 " if {$pattern eq $dir || $pattern eq \"\"} {\n"
1197 " return [list [file join $base $dir] $pattern]\n"
1198 " } elseif {$pattern eq [file tail $pattern]} {\n"
1199 " set dir \"\"\n"
1200 " }\n"
1201 "\n"
1202 "\n"
1203 " set dirlist [glob.glob $base $dir]\n"
1204 " set pattern [file tail $pattern]\n"
1205 "\n"
1206 "\n"
1207 " set result {}\n"
1208 " foreach {realdir dir} $dirlist {\n"
1209 " if {![file isdir $realdir]} {\n"
1210 " continue\n"
1211 " }\n"
1212 " if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n"
1213 " append dir /\n"
1214 " }\n"
1215 " foreach name [glob.globdir $realdir $pattern] {\n"
1216 " lappend result [file join $realdir $name] $dir$name\n"
1217 " }\n"
1218 " }\n"
1219 " return $result\n"
1220 "}\n"
1221 "\n"
1222 "\n"
1223 "\n"
1224 "\n"
1225 "\n"
1226 "\n"
1227 "\n"
1228 "\n"
1229 "\n"
1230 "\n"
1231 "\n"
1232 "\n"
1233 "proc glob {args} {\n"
1234 " set nocomplain 0\n"
1235 " set base \"\"\n"
1236 " set tails 0\n"
1237 "\n"
1238 " set n 0\n"
1239 " foreach arg $args {\n"
1240 " if {[info exists param]} {\n"
1241 " set $param $arg\n"
1242 " unset param\n"
1243 " incr n\n"
1244 " continue\n"
1245 " }\n"
1246 " switch -glob -- $arg {\n"
1247 " -d* {\n"
1248 " set switch $arg\n"
1249 " set param base\n"
1250 " }\n"
1251 " -n* {\n"
1252 " set nocomplain 1\n"
1253 " }\n"
1254 " -ta* {\n"
1255 " set tails 1\n"
1256 " }\n"
1257 " -- {\n"
1258 " incr n\n"
1259 " break\n"
1260 " }\n"
1261 " -* {\n"
1262 " return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1263 " }\n"
1264 " * {\n"
1265 " break\n"
1266 " }\n"
1267 " }\n"
1268 " incr n\n"
1269 " }\n"
1270 " if {[info exists param]} {\n"
1271 " return -code error \"missing argument to \\\"$switch\\\"\"\n"
1272 " }\n"
1273 " if {[llength $args] <= $n} {\n"
1274 " return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n"
1275 " }\n"
1276 "\n"
1277 " set args [lrange $args $n end]\n"
1278 "\n"
1279 " set result {}\n"
1280 " foreach pattern $args {\n"
1281 " set escpattern [string map {\n"
1282 " \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
1283 " } $pattern]\n"
1284 " set patexps [lassign [glob.explode $escpattern] rest]\n"
1285 " if {$rest ne \"\"} {\n"
1286 " return -code error \"unmatched close brace in glob pattern\"\n"
1287 " }\n"
1288 " foreach patexp $patexps {\n"
1289 " set patexp [string map {\n"
1290 " \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
1291 " } $patexp]\n"
1292 " foreach {realname name} [glob.glob $base $patexp] {\n"
1293 " incr n\n"
1294 " if {$tails} {\n"
1295 " lappend result $name\n"
1296 " } else {\n"
1297 " lappend result [file join $base $name]\n"
1298 " }\n"
1299 " }\n"
1300 " }\n"
1301 " }\n"
1302 "\n"
1303 " if {!$nocomplain && [llength $result] == 0} {\n"
1304 " set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
1305 " return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
1306 " }\n"
1307 "\n"
1308 " return $result\n"
1309 "}\n"
1312 int Jim_stdlibInit(Jim_Interp *interp)
1314 if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
1315 return JIM_ERR;
1317 return Jim_EvalSource(interp, "stdlib.tcl", 1,
1318 "\n"
1319 "\n"
1320 "\n"
1321 "proc lambda {arglist args} {\n"
1322 " tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
1323 "}\n"
1324 "\n"
1325 "proc lambda.finalizer {name val} {\n"
1326 " rename $name {}\n"
1327 "}\n"
1328 "\n"
1329 "\n"
1330 "proc curry {args} {\n"
1331 " alias [ref {} function lambda.finalizer] {*}$args\n"
1332 "}\n"
1333 "\n"
1334 "\n"
1335 "\n"
1336 "\n"
1337 "\n"
1338 "\n"
1339 "\n"
1340 "\n"
1341 "\n"
1342 "proc function {value} {\n"
1343 " return $value\n"
1344 "}\n"
1345 "\n"
1346 "\n"
1347 "\n"
1348 "\n"
1349 "proc stacktrace {{skip 0}} {\n"
1350 " set trace {}\n"
1351 " incr skip\n"
1352 " foreach level [range $skip [info level]] {\n"
1353 " lappend trace {*}[info frame -$level]\n"
1354 " }\n"
1355 " return $trace\n"
1356 "}\n"
1357 "\n"
1358 "\n"
1359 "proc stackdump {stacktrace} {\n"
1360 " set lines {}\n"
1361 " foreach {l f p} [lreverse $stacktrace] {\n"
1362 " set line {}\n"
1363 " if {$p ne \"\"} {\n"
1364 " append line \"in procedure '$p' \"\n"
1365 " if {$f ne \"\"} {\n"
1366 " append line \"called \"\n"
1367 " }\n"
1368 " }\n"
1369 " if {$f ne \"\"} {\n"
1370 " append line \"at file \\\"$f\\\", line $l\"\n"
1371 " }\n"
1372 " if {$line ne \"\"} {\n"
1373 " lappend lines $line\n"
1374 " }\n"
1375 " }\n"
1376 " join $lines \\n\n"
1377 "}\n"
1378 "\n"
1379 "\n"
1380 "\n"
1381 "proc errorInfo {msg {stacktrace \"\"}} {\n"
1382 " if {$stacktrace eq \"\"} {\n"
1383 "\n"
1384 " set stacktrace [info stacktrace]\n"
1385 "\n"
1386 " lappend stacktrace {*}[stacktrace 1]\n"
1387 " }\n"
1388 " lassign $stacktrace p f l\n"
1389 " if {$f ne \"\"} {\n"
1390 " set result \"$f:$l: Error: \"\n"
1391 " }\n"
1392 " append result \"$msg\\n\"\n"
1393 " append result [stackdump $stacktrace]\n"
1394 "\n"
1395 "\n"
1396 " string trim $result\n"
1397 "}\n"
1398 "\n"
1399 "\n"
1400 "\n"
1401 "proc {info nameofexecutable} {} {\n"
1402 " if {[exists ::jim::exe]} {\n"
1403 " return $::jim::exe\n"
1404 " }\n"
1405 "}\n"
1406 "\n"
1407 "\n"
1408 "proc {dict with} {&dictVar {args key} script} {\n"
1409 " set keys {}\n"
1410 " foreach {n v} [dict get $dictVar {*}$key] {\n"
1411 " upvar $n var_$n\n"
1412 " set var_$n $v\n"
1413 " lappend keys $n\n"
1414 " }\n"
1415 " catch {uplevel 1 $script} msg opts\n"
1416 " if {[info exists dictVar] && ([llength $key] == 0 || [dict exists $dictVar {*}$key])} {\n"
1417 " foreach n $keys {\n"
1418 " if {[info exists var_$n]} {\n"
1419 " dict set dictVar {*}$key $n [set var_$n]\n"
1420 " } else {\n"
1421 " dict unset dictVar {*}$key $n\n"
1422 " }\n"
1423 " }\n"
1424 " }\n"
1425 " return {*}$opts $msg\n"
1426 "}\n"
1427 "\n"
1428 "\n"
1429 "proc {dict update} {&varName args script} {\n"
1430 " set keys {}\n"
1431 " foreach {n v} $args {\n"
1432 " upvar $v var_$v\n"
1433 " if {[dict exists $varName $n]} {\n"
1434 " set var_$v [dict get $varName $n]\n"
1435 " }\n"
1436 " }\n"
1437 " catch {uplevel 1 $script} msg opts\n"
1438 " if {[info exists varName]} {\n"
1439 " foreach {n v} $args {\n"
1440 " if {[info exists var_$v]} {\n"
1441 " dict set varName $n [set var_$v]\n"
1442 " } else {\n"
1443 " dict unset varName $n\n"
1444 " }\n"
1445 " }\n"
1446 " }\n"
1447 " return {*}$opts $msg\n"
1448 "}\n"
1449 "\n"
1450 "\n"
1451 "\n"
1452 "proc {dict merge} {dict args} {\n"
1453 " foreach d $args {\n"
1454 "\n"
1455 " dict size $d\n"
1456 " foreach {k v} $d {\n"
1457 " dict set dict $k $v\n"
1458 " }\n"
1459 " }\n"
1460 " return $dict\n"
1461 "}\n"
1462 "\n"
1463 "proc {dict replace} {dictionary {args {key value}}} {\n"
1464 " if {[llength ${key value}] % 2} {\n"
1465 " tailcall {dict replace}\n"
1466 " }\n"
1467 " tailcall dict merge $dictionary ${key value}\n"
1468 "}\n"
1469 "\n"
1470 "\n"
1471 "proc {dict lappend} {varName key {args value}} {\n"
1472 " upvar $varName dict\n"
1473 " if {[exists dict] && [dict exists $dict $key]} {\n"
1474 " set list [dict get $dict $key]\n"
1475 " }\n"
1476 " lappend list {*}$value\n"
1477 " dict set dict $key $list\n"
1478 "}\n"
1479 "\n"
1480 "\n"
1481 "proc {dict append} {varName key {args value}} {\n"
1482 " upvar $varName dict\n"
1483 " if {[exists dict] && [dict exists $dict $key]} {\n"
1484 " set str [dict get $dict $key]\n"
1485 " }\n"
1486 " append str {*}$value\n"
1487 " dict set dict $key $str\n"
1488 "}\n"
1489 "\n"
1490 "\n"
1491 "proc {dict incr} {varName key {increment 1}} {\n"
1492 " upvar $varName dict\n"
1493 " if {[exists dict] && [dict exists $dict $key]} {\n"
1494 " set value [dict get $dict $key]\n"
1495 " }\n"
1496 " incr value $increment\n"
1497 " dict set dict $key $value\n"
1498 "}\n"
1499 "\n"
1500 "\n"
1501 "proc {dict remove} {dictionary {args key}} {\n"
1502 " foreach k $key {\n"
1503 " dict unset dictionary $k\n"
1504 " }\n"
1505 " return $dictionary\n"
1506 "}\n"
1507 "\n"
1508 "\n"
1509 "proc {dict values} {dictionary {pattern *}} {\n"
1510 " dict keys [lreverse $dictionary] $pattern\n"
1511 "}\n"
1512 "\n"
1513 "\n"
1514 "proc {dict for} {vars dictionary script} {\n"
1515 " if {[llength $vars] != 2} {\n"
1516 " return -code error \"must have exactly two variable names\"\n"
1517 " }\n"
1518 " dict size $dictionary\n"
1519 " tailcall foreach $vars $dictionary $script\n"
1520 "}\n"
1523 int Jim_tclcompatInit(Jim_Interp *interp)
1525 if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG))
1526 return JIM_ERR;
1528 return Jim_EvalSource(interp, "tclcompat.tcl", 1,
1529 "\n"
1530 "\n"
1531 "\n"
1532 "\n"
1533 "\n"
1534 "\n"
1535 "\n"
1536 "\n"
1537 "set env [env]\n"
1538 "\n"
1539 "\n"
1540 "if {[info commands stdout] ne \"\"} {\n"
1541 "\n"
1542 " foreach p {gets flush close eof seek tell} {\n"
1543 " proc $p {chan args} {p} {\n"
1544 " tailcall $chan $p {*}$args\n"
1545 " }\n"
1546 " }\n"
1547 " unset p\n"
1548 "\n"
1549 "\n"
1550 "\n"
1551 " proc puts {{-nonewline {}} {chan stdout} msg} {\n"
1552 " if {${-nonewline} ni {-nonewline {}}} {\n"
1553 " tailcall ${-nonewline} puts $msg\n"
1554 " }\n"
1555 " tailcall $chan puts {*}${-nonewline} $msg\n"
1556 " }\n"
1557 "\n"
1558 "\n"
1559 "\n"
1560 "\n"
1561 "\n"
1562 " proc read {{-nonewline {}} chan} {\n"
1563 " if {${-nonewline} ni {-nonewline {}}} {\n"
1564 " tailcall ${-nonewline} read {*}${chan}\n"
1565 " }\n"
1566 " tailcall $chan read {*}${-nonewline}\n"
1567 " }\n"
1568 "\n"
1569 " proc fconfigure {f args} {\n"
1570 " foreach {n v} $args {\n"
1571 " switch -glob -- $n {\n"
1572 " -bl* {\n"
1573 " $f ndelay $(!$v)\n"
1574 " }\n"
1575 " -bu* {\n"
1576 " $f buffering $v\n"
1577 " }\n"
1578 " -tr* {\n"
1579 "\n"
1580 " }\n"
1581 " default {\n"
1582 " return -code error \"fconfigure: unknown option $n\"\n"
1583 " }\n"
1584 " }\n"
1585 " }\n"
1586 " }\n"
1587 "}\n"
1588 "\n"
1589 "\n"
1590 "proc fileevent {args} {\n"
1591 " tailcall {*}$args\n"
1592 "}\n"
1593 "\n"
1594 "\n"
1595 "\n"
1596 "\n"
1597 "proc parray {arrayname {pattern *} {puts puts}} {\n"
1598 " upvar $arrayname a\n"
1599 "\n"
1600 " set max 0\n"
1601 " foreach name [array names a $pattern]] {\n"
1602 " if {[string length $name] > $max} {\n"
1603 " set max [string length $name]\n"
1604 " }\n"
1605 " }\n"
1606 " incr max [string length $arrayname]\n"
1607 " incr max 2\n"
1608 " foreach name [lsort [array names a $pattern]] {\n"
1609 " $puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
1610 " }\n"
1611 "}\n"
1612 "\n"
1613 "\n"
1614 "proc {file copy} {{force {}} source target} {\n"
1615 " try {\n"
1616 " if {$force ni {{} -force}} {\n"
1617 " error \"bad option \\\"$force\\\": should be -force\"\n"
1618 " }\n"
1619 "\n"
1620 " set in [open $source rb]\n"
1621 "\n"
1622 " if {[file exists $target]} {\n"
1623 " if {$force eq \"\"} {\n"
1624 " error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
1625 " }\n"
1626 "\n"
1627 " if {$source eq $target} {\n"
1628 " return\n"
1629 " }\n"
1630 "\n"
1631 "\n"
1632 " file stat $source ss\n"
1633 " file stat $target ts\n"
1634 " if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
1635 " return\n"
1636 " }\n"
1637 " }\n"
1638 " set out [open $target wb]\n"
1639 " $in copyto $out\n"
1640 " $out close\n"
1641 " } on error {msg opts} {\n"
1642 " incr opts(-level)\n"
1643 " return {*}$opts $msg\n"
1644 " } finally {\n"
1645 " catch {$in close}\n"
1646 " }\n"
1647 "}\n"
1648 "\n"
1649 "\n"
1650 "\n"
1651 "proc popen {cmd {mode r}} {\n"
1652 " lassign [socket pipe] r w\n"
1653 " try {\n"
1654 " if {[string match \"w*\" $mode]} {\n"
1655 " lappend cmd <@$r &\n"
1656 " set pids [exec {*}$cmd]\n"
1657 " $r close\n"
1658 " set f $w\n"
1659 " } else {\n"
1660 " lappend cmd >@$w &\n"
1661 " set pids [exec {*}$cmd]\n"
1662 " $w close\n"
1663 " set f $r\n"
1664 " }\n"
1665 " lambda {cmd args} {f pids} {\n"
1666 " if {$cmd eq \"pid\"} {\n"
1667 " return $pids\n"
1668 " }\n"
1669 " if {$cmd eq \"close\"} {\n"
1670 " $f close\n"
1671 "\n"
1672 " foreach p $pids { os.wait $p }\n"
1673 " return\n"
1674 " }\n"
1675 " tailcall $f $cmd {*}$args\n"
1676 " }\n"
1677 " } on error {error opts} {\n"
1678 " $r close\n"
1679 " $w close\n"
1680 " error $error\n"
1681 " }\n"
1682 "}\n"
1683 "\n"
1684 "\n"
1685 "local proc pid {{channelId {}}} {\n"
1686 " if {$channelId eq \"\"} {\n"
1687 " tailcall upcall pid\n"
1688 " }\n"
1689 " if {[catch {$channelId tell}]} {\n"
1690 " return -code error \"can not find channel named \\\"$channelId\\\"\"\n"
1691 " }\n"
1692 " if {[catch {$channelId pid} pids]} {\n"
1693 " return \"\"\n"
1694 " }\n"
1695 " return $pids\n"
1696 "}\n"
1697 "\n"
1698 "\n"
1699 "\n"
1700 "\n"
1701 "\n"
1702 "\n"
1703 "\n"
1704 "\n"
1705 "\n"
1706 "\n"
1707 "\n"
1708 "\n"
1709 "\n"
1710 "\n"
1711 "proc try {args} {\n"
1712 " set catchopts {}\n"
1713 " while {[string match -* [lindex $args 0]]} {\n"
1714 " set args [lassign $args opt]\n"
1715 " if {$opt eq \"--\"} {\n"
1716 " break\n"
1717 " }\n"
1718 " lappend catchopts $opt\n"
1719 " }\n"
1720 " if {[llength $args] == 0} {\n"
1721 " return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n"
1722 " }\n"
1723 " set args [lassign $args script]\n"
1724 " set code [catch -eval {*}$catchopts {uplevel 1 $script} msg opts]\n"
1725 "\n"
1726 " set handled 0\n"
1727 "\n"
1728 " foreach {on codes vars script} $args {\n"
1729 " switch -- $on \\\n"
1730 " on {\n"
1731 " if {!$handled && ($codes eq \"*\" || [info returncode $code] in $codes)} {\n"
1732 " lassign $vars msgvar optsvar\n"
1733 " if {$msgvar ne \"\"} {\n"
1734 " upvar $msgvar hmsg\n"
1735 " set hmsg $msg\n"
1736 " }\n"
1737 " if {$optsvar ne \"\"} {\n"
1738 " upvar $optsvar hopts\n"
1739 " set hopts $opts\n"
1740 " }\n"
1741 "\n"
1742 " set code [catch {uplevel 1 $script} msg opts]\n"
1743 " incr handled\n"
1744 " }\n"
1745 " } \\\n"
1746 " finally {\n"
1747 " set finalcode [catch {uplevel 1 $codes} finalmsg finalopts]\n"
1748 " if {$finalcode} {\n"
1749 "\n"
1750 " set code $finalcode\n"
1751 " set msg $finalmsg\n"
1752 " set opts $finalopts\n"
1753 " }\n"
1754 " break\n"
1755 " } \\\n"
1756 " default {\n"
1757 " return -code error \"try: expected 'on' or 'finally', got '$on'\"\n"
1758 " }\n"
1759 " }\n"
1760 "\n"
1761 " if {$code} {\n"
1762 " incr opts(-level)\n"
1763 " return {*}$opts $msg\n"
1764 " }\n"
1765 " return $msg\n"
1766 "}\n"
1767 "\n"
1768 "\n"
1769 "\n"
1770 "proc throw {code {msg \"\"}} {\n"
1771 " return -code $code $msg\n"
1772 "}\n"
1773 "\n"
1774 "\n"
1775 "proc {file delete force} {path} {\n"
1776 " foreach e [readdir $path] {\n"
1777 " file delete -force $path/$e\n"
1778 " }\n"
1779 " file delete $path\n"
1780 "}\n"
1785 #include <stdio.h>
1786 #include <string.h>
1787 #include <errno.h>
1788 #include <fcntl.h>
1789 #ifdef HAVE_UNISTD_H
1790 #include <unistd.h>
1791 #include <sys/stat.h>
1792 #endif
1795 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
1796 #include <sys/socket.h>
1797 #include <netinet/in.h>
1798 #include <arpa/inet.h>
1799 #include <netdb.h>
1800 #ifdef HAVE_SYS_UN_H
1801 #include <sys/un.h>
1802 #endif
1803 #else
1804 #define JIM_ANSIC
1805 #endif
1807 #if defined(JIM_SSL)
1808 #include <openssl/ssl.h>
1809 #include <openssl/err.h>
1810 #endif
1813 #define AIO_CMD_LEN 32
1814 #define AIO_BUF_LEN 256
1816 #ifndef HAVE_FTELLO
1817 #define ftello ftell
1818 #endif
1819 #ifndef HAVE_FSEEKO
1820 #define fseeko fseek
1821 #endif
1823 #define AIO_KEEPOPEN 1
1825 #if defined(JIM_IPV6)
1826 #define IPV6 1
1827 #else
1828 #define IPV6 0
1829 #ifndef PF_INET6
1830 #define PF_INET6 0
1831 #endif
1832 #endif
1834 #define JimCheckStreamError(interp, af) af->fops->error(af)
1837 struct AioFile;
1839 typedef struct {
1840 int (*writer)(struct AioFile *af, const char *buf, int len);
1841 int (*reader)(struct AioFile *af, char *buf, int len);
1842 const char *(*getline)(struct AioFile *af, char *buf, int len);
1843 int (*error)(const struct AioFile *af);
1844 const char *(*strerror)(struct AioFile *af);
1845 int (*verify)(struct AioFile *af);
1846 } JimAioFopsType;
1848 typedef struct AioFile
1850 FILE *fp;
1851 Jim_Obj *filename;
1852 int type;
1853 int openFlags;
1854 int fd;
1855 Jim_Obj *rEvent;
1856 Jim_Obj *wEvent;
1857 Jim_Obj *eEvent;
1858 int addr_family;
1859 void *ssl;
1860 const JimAioFopsType *fops;
1861 } AioFile;
1863 static int stdio_writer(struct AioFile *af, const char *buf, int len)
1865 return fwrite(buf, 1, len, af->fp);
1868 static int stdio_reader(struct AioFile *af, char *buf, int len)
1870 return fread(buf, 1, len, af->fp);
1873 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
1875 return fgets(buf, len, af->fp);
1878 static int stdio_error(const AioFile *af)
1880 if (!ferror(af->fp)) {
1881 return JIM_OK;
1883 clearerr(af->fp);
1885 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
1886 return JIM_OK;
1888 #ifdef ECONNRESET
1889 if (errno == ECONNRESET) {
1890 return JIM_OK;
1892 #endif
1893 #ifdef ECONNABORTED
1894 if (errno != ECONNABORTED) {
1895 return JIM_OK;
1897 #endif
1898 return JIM_ERR;
1901 static const char *stdio_strerror(struct AioFile *af)
1903 return strerror(errno);
1906 static const JimAioFopsType stdio_fops = {
1907 stdio_writer,
1908 stdio_reader,
1909 stdio_getline,
1910 stdio_error,
1911 stdio_strerror,
1912 NULL
1916 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
1917 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
1918 const char *hdlfmt, int family, const char *mode);
1921 static const char *JimAioErrorString(AioFile *af)
1923 if (af && af->fops)
1924 return af->fops->strerror(af);
1926 return strerror(errno);
1929 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
1931 AioFile *af = Jim_CmdPrivData(interp);
1933 if (name) {
1934 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
1936 else {
1937 Jim_SetResultString(interp, JimAioErrorString(af), -1);
1941 static void JimAioDelProc(Jim_Interp *interp, void *privData)
1943 AioFile *af = privData;
1945 JIM_NOTUSED(interp);
1947 Jim_DecrRefCount(interp, af->filename);
1949 #ifdef jim_ext_eventloop
1951 Jim_DeleteFileHandler(interp, af->fp, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
1952 #endif
1954 #if defined(JIM_SSL)
1955 if (af->ssl != NULL) {
1956 SSL_free(af->ssl);
1958 #endif
1960 if (!(af->openFlags & AIO_KEEPOPEN)) {
1961 fclose(af->fp);
1964 Jim_Free(af);
1967 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1969 AioFile *af = Jim_CmdPrivData(interp);
1970 char buf[AIO_BUF_LEN];
1971 Jim_Obj *objPtr;
1972 int nonewline = 0;
1973 jim_wide neededLen = -1;
1975 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
1976 nonewline = 1;
1977 argv++;
1978 argc--;
1980 if (argc == 1) {
1981 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
1982 return JIM_ERR;
1983 if (neededLen < 0) {
1984 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
1985 return JIM_ERR;
1988 else if (argc) {
1989 return -1;
1991 objPtr = Jim_NewStringObj(interp, NULL, 0);
1992 while (neededLen != 0) {
1993 int retval;
1994 int readlen;
1996 if (neededLen == -1) {
1997 readlen = AIO_BUF_LEN;
1999 else {
2000 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
2002 retval = af->fops->reader(af, buf, readlen);
2003 if (retval > 0) {
2004 Jim_AppendString(interp, objPtr, buf, retval);
2005 if (neededLen != -1) {
2006 neededLen -= retval;
2009 if (retval != readlen)
2010 break;
2013 if (JimCheckStreamError(interp, af)) {
2014 Jim_FreeNewObj(interp, objPtr);
2015 return JIM_ERR;
2017 if (nonewline) {
2018 int len;
2019 const char *s = Jim_GetString(objPtr, &len);
2021 if (len > 0 && s[len - 1] == '\n') {
2022 objPtr->length--;
2023 objPtr->bytes[objPtr->length] = '\0';
2026 Jim_SetResult(interp, objPtr);
2027 return JIM_OK;
2030 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
2032 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
2035 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
2036 return (AioFile *) cmdPtr->u.native.privData;
2038 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
2039 return NULL;
2042 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
2044 AioFile *af;
2046 af = Jim_AioFile(interp, command);
2047 if (af == NULL) {
2048 return NULL;
2051 return af->fp;
2054 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2056 AioFile *af = Jim_CmdPrivData(interp);
2057 jim_wide count = 0;
2058 jim_wide maxlen = JIM_WIDE_MAX;
2059 AioFile *outf = Jim_AioFile(interp, argv[0]);
2061 if (outf == NULL) {
2062 return JIM_ERR;
2065 if (argc == 2) {
2066 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
2067 return JIM_ERR;
2071 while (count < maxlen) {
2072 char ch;
2074 if (af->fops->reader(af, &ch, 1) != 1) {
2075 break;
2077 if (outf->fops->writer(outf, &ch, 1) != 1) {
2078 break;
2080 count++;
2083 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
2084 return JIM_ERR;
2087 Jim_SetResultInt(interp, count);
2089 return JIM_OK;
2092 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2094 AioFile *af = Jim_CmdPrivData(interp);
2095 char buf[AIO_BUF_LEN];
2096 Jim_Obj *objPtr;
2097 int len;
2099 errno = 0;
2101 objPtr = Jim_NewStringObj(interp, NULL, 0);
2102 while (1) {
2103 buf[AIO_BUF_LEN - 1] = '_';
2105 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
2106 break;
2108 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
2109 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
2111 else {
2112 len = strlen(buf);
2114 if (len && (buf[len - 1] == '\n')) {
2116 len--;
2119 Jim_AppendString(interp, objPtr, buf, len);
2120 break;
2124 if (JimCheckStreamError(interp, af)) {
2126 Jim_FreeNewObj(interp, objPtr);
2127 return JIM_ERR;
2130 if (argc) {
2131 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
2132 Jim_FreeNewObj(interp, objPtr);
2133 return JIM_ERR;
2136 len = Jim_Length(objPtr);
2138 if (len == 0 && feof(af->fp)) {
2140 len = -1;
2142 Jim_SetResultInt(interp, len);
2144 else {
2145 Jim_SetResult(interp, objPtr);
2147 return JIM_OK;
2150 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2152 AioFile *af = Jim_CmdPrivData(interp);
2153 int wlen;
2154 const char *wdata;
2155 Jim_Obj *strObj;
2157 if (argc == 2) {
2158 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
2159 return -1;
2161 strObj = argv[1];
2163 else {
2164 strObj = argv[0];
2167 wdata = Jim_GetString(strObj, &wlen);
2168 if (af->fops->writer(af, wdata, wlen) == wlen) {
2169 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
2170 return JIM_OK;
2173 JimAioSetError(interp, af->filename);
2174 return JIM_ERR;
2177 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2179 #ifdef HAVE_ISATTY
2180 AioFile *af = Jim_CmdPrivData(interp);
2181 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
2182 #else
2183 Jim_SetResultInt(interp, 0);
2184 #endif
2186 return JIM_OK;
2190 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2192 AioFile *af = Jim_CmdPrivData(interp);
2194 if (fflush(af->fp) == EOF) {
2195 JimAioSetError(interp, af->filename);
2196 return JIM_ERR;
2198 return JIM_OK;
2201 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2203 AioFile *af = Jim_CmdPrivData(interp);
2205 Jim_SetResultInt(interp, feof(af->fp));
2206 return JIM_OK;
2209 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2211 if (argc == 3) {
2212 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
2213 static const char * const options[] = { "r", "w", NULL };
2214 enum { OPT_R, OPT_W, };
2215 int option;
2216 AioFile *af = Jim_CmdPrivData(interp);
2218 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2219 return JIM_ERR;
2221 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
2222 return JIM_OK;
2224 JimAioSetError(interp, NULL);
2225 #else
2226 Jim_SetResultString(interp, "async close not supported", -1);
2227 #endif
2228 return JIM_ERR;
2231 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
2234 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2236 AioFile *af = Jim_CmdPrivData(interp);
2237 int orig = SEEK_SET;
2238 jim_wide offset;
2240 if (argc == 2) {
2241 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
2242 orig = SEEK_SET;
2243 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
2244 orig = SEEK_CUR;
2245 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
2246 orig = SEEK_END;
2247 else {
2248 return -1;
2251 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
2252 return JIM_ERR;
2254 if (fseeko(af->fp, offset, orig) == -1) {
2255 JimAioSetError(interp, af->filename);
2256 return JIM_ERR;
2258 return JIM_OK;
2261 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2263 AioFile *af = Jim_CmdPrivData(interp);
2265 Jim_SetResultInt(interp, ftello(af->fp));
2266 return JIM_OK;
2269 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2271 AioFile *af = Jim_CmdPrivData(interp);
2273 Jim_SetResult(interp, af->filename);
2274 return JIM_OK;
2277 #ifdef O_NDELAY
2278 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2280 AioFile *af = Jim_CmdPrivData(interp);
2282 int fmode = fcntl(af->fd, F_GETFL);
2284 if (argc) {
2285 long nb;
2287 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
2288 return JIM_ERR;
2290 if (nb) {
2291 fmode |= O_NDELAY;
2293 else {
2294 fmode &= ~O_NDELAY;
2296 (void)fcntl(af->fd, F_SETFL, fmode);
2298 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
2299 return JIM_OK;
2301 #endif
2303 #ifdef HAVE_FSYNC
2304 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2306 AioFile *af = Jim_CmdPrivData(interp);
2308 fflush(af->fp);
2309 fsync(af->fd);
2310 return JIM_OK;
2312 #endif
2314 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2316 AioFile *af = Jim_CmdPrivData(interp);
2318 static const char * const options[] = {
2319 "none",
2320 "line",
2321 "full",
2322 NULL
2324 enum
2326 OPT_NONE,
2327 OPT_LINE,
2328 OPT_FULL,
2330 int option;
2332 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2333 return JIM_ERR;
2335 switch (option) {
2336 case OPT_NONE:
2337 setvbuf(af->fp, NULL, _IONBF, 0);
2338 break;
2339 case OPT_LINE:
2340 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
2341 break;
2342 case OPT_FULL:
2343 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
2344 break;
2346 return JIM_OK;
2349 #ifdef jim_ext_eventloop
2350 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
2352 Jim_Obj **objPtrPtr = clientData;
2354 Jim_DecrRefCount(interp, *objPtrPtr);
2355 *objPtrPtr = NULL;
2358 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
2360 Jim_Obj **objPtrPtr = clientData;
2362 return Jim_EvalObjBackground(interp, *objPtrPtr);
2365 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
2366 int argc, Jim_Obj * const *argv)
2368 if (argc == 0) {
2370 if (*scriptHandlerObj) {
2371 Jim_SetResult(interp, *scriptHandlerObj);
2373 return JIM_OK;
2376 if (*scriptHandlerObj) {
2378 Jim_DeleteFileHandler(interp, af->fp, mask);
2382 if (Jim_Length(argv[0]) == 0) {
2384 return JIM_OK;
2388 Jim_IncrRefCount(argv[0]);
2389 *scriptHandlerObj = argv[0];
2391 Jim_CreateFileHandler(interp, af->fp, mask,
2392 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
2394 return JIM_OK;
2397 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2399 AioFile *af = Jim_CmdPrivData(interp);
2401 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
2404 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2406 AioFile *af = Jim_CmdPrivData(interp);
2408 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
2411 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2413 AioFile *af = Jim_CmdPrivData(interp);
2415 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
2417 #endif
2420 static const jim_subcmd_type aio_command_table[] = {
2421 { "read",
2422 "?-nonewline? ?len?",
2423 aio_cmd_read,
2428 { "copyto",
2429 "handle ?size?",
2430 aio_cmd_copy,
2435 { "gets",
2436 "?var?",
2437 aio_cmd_gets,
2442 { "puts",
2443 "?-nonewline? str",
2444 aio_cmd_puts,
2449 { "isatty",
2450 NULL,
2451 aio_cmd_isatty,
2456 { "flush",
2457 NULL,
2458 aio_cmd_flush,
2463 { "eof",
2464 NULL,
2465 aio_cmd_eof,
2470 { "close",
2471 "?r(ead)|w(rite)?",
2472 aio_cmd_close,
2475 JIM_MODFLAG_FULLARGV,
2478 { "seek",
2479 "offset ?start|current|end",
2480 aio_cmd_seek,
2485 { "tell",
2486 NULL,
2487 aio_cmd_tell,
2492 { "filename",
2493 NULL,
2494 aio_cmd_filename,
2499 #ifdef O_NDELAY
2500 { "ndelay",
2501 "?0|1?",
2502 aio_cmd_ndelay,
2507 #endif
2508 #ifdef HAVE_FSYNC
2509 { "sync",
2510 NULL,
2511 aio_cmd_sync,
2516 #endif
2517 { "buffering",
2518 "none|line|full",
2519 aio_cmd_buffering,
2524 #ifdef jim_ext_eventloop
2525 { "readable",
2526 "?readable-script?",
2527 aio_cmd_readable,
2532 { "writable",
2533 "?writable-script?",
2534 aio_cmd_writable,
2539 { "onexception",
2540 "?exception-script?",
2541 aio_cmd_onexception,
2546 #endif
2547 { NULL }
2550 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2552 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
2555 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
2556 Jim_Obj *const *argv)
2558 const char *mode;
2560 if (argc != 2 && argc != 3) {
2561 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
2562 return JIM_ERR;
2565 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
2567 #ifdef jim_ext_tclcompat
2569 const char *filename = Jim_String(argv[1]);
2572 if (*filename == '|') {
2573 Jim_Obj *evalObj[3];
2575 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
2576 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
2577 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
2579 return Jim_EvalObjVector(interp, 3, evalObj);
2582 #endif
2583 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
2587 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
2588 const char *hdlfmt, int family, const char *mode)
2590 AioFile *af;
2591 char buf[AIO_CMD_LEN];
2592 int openFlags = 0;
2594 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2596 if (fh) {
2597 openFlags = AIO_KEEPOPEN;
2600 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2601 if (!filename) {
2602 filename = Jim_NewStringObj(interp, buf, -1);
2605 Jim_IncrRefCount(filename);
2607 if (fh == NULL) {
2608 #if !defined(JIM_ANSIC)
2609 if (fd >= 0) {
2610 fh = fdopen(fd, mode);
2612 else
2613 #endif
2614 fh = fopen(Jim_String(filename), mode);
2616 if (fh == NULL) {
2617 JimAioSetError(interp, filename);
2618 #if !defined(JIM_ANSIC)
2619 if (fd >= 0) {
2620 close(fd);
2622 #endif
2623 Jim_DecrRefCount(interp, filename);
2624 return NULL;
2629 af = Jim_Alloc(sizeof(*af));
2630 memset(af, 0, sizeof(*af));
2631 af->fp = fh;
2632 af->fd = fileno(fh);
2633 af->filename = filename;
2634 #ifdef FD_CLOEXEC
2635 if ((openFlags & AIO_KEEPOPEN) == 0) {
2636 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
2638 #endif
2639 af->openFlags = openFlags;
2640 af->addr_family = family;
2641 af->fops = &stdio_fops;
2642 af->ssl = NULL;
2644 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
2646 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
2648 return af;
2651 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
2652 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
2653 const char *hdlfmt, int family, const char *mode[2])
2655 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
2656 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
2657 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2659 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
2660 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2661 Jim_SetResult(interp, objPtr);
2662 return JIM_OK;
2667 close(p[0]);
2668 close(p[1]);
2669 JimAioSetError(interp, NULL);
2670 return JIM_ERR;
2672 #endif
2675 int Jim_MakeTempFile(Jim_Interp *interp, const char *template)
2677 #ifdef HAVE_MKSTEMP
2678 int fd;
2679 mode_t mask;
2680 Jim_Obj *filenameObj;
2682 if (template == NULL) {
2683 const char *tmpdir = getenv("TMPDIR");
2684 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
2685 tmpdir = "/tmp/";
2687 filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
2688 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
2689 Jim_AppendString(interp, filenameObj, "/", 1);
2691 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
2693 else {
2694 filenameObj = Jim_NewStringObj(interp, template, -1);
2697 #if defined(S_IRWXG) && defined(S_IRWXO)
2698 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
2699 #else
2701 mask = umask(S_IXUSR);
2702 #endif
2705 fd = mkstemp(filenameObj->bytes);
2706 umask(mask);
2707 if (fd < 0) {
2708 JimAioSetError(interp, filenameObj);
2709 Jim_FreeNewObj(interp, filenameObj);
2710 return -1;
2713 Jim_SetResult(interp, filenameObj);
2714 return fd;
2715 #else
2716 Jim_SetResultString(interp, "platform has no tempfile support", -1);
2717 return -1;
2718 #endif
2722 int Jim_aioInit(Jim_Interp *interp)
2724 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2725 return JIM_ERR;
2727 #if defined(JIM_SSL)
2728 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2729 #endif
2731 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2732 #ifndef JIM_ANSIC
2733 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2734 #endif
2737 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
2738 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
2739 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
2741 return JIM_OK;
2744 #include <errno.h>
2745 #include <stdio.h>
2746 #include <string.h>
2749 #ifdef HAVE_DIRENT_H
2750 #include <dirent.h>
2751 #endif
2753 int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2755 const char *dirPath;
2756 DIR *dirPtr;
2757 struct dirent *entryPtr;
2758 int nocomplain = 0;
2760 if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) {
2761 nocomplain = 1;
2763 if (argc != 2 && !nocomplain) {
2764 Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
2765 return JIM_ERR;
2768 dirPath = Jim_String(argv[1 + nocomplain]);
2770 dirPtr = opendir(dirPath);
2771 if (dirPtr == NULL) {
2772 if (nocomplain) {
2773 return JIM_OK;
2775 Jim_SetResultString(interp, strerror(errno), -1);
2776 return JIM_ERR;
2778 else {
2779 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
2781 while ((entryPtr = readdir(dirPtr)) != NULL) {
2782 if (entryPtr->d_name[0] == '.') {
2783 if (entryPtr->d_name[1] == '\0') {
2784 continue;
2786 if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0'))
2787 continue;
2789 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1));
2791 closedir(dirPtr);
2793 Jim_SetResult(interp, listObj);
2795 return JIM_OK;
2799 int Jim_readdirInit(Jim_Interp *interp)
2801 if (Jim_PackageProvide(interp, "readdir", "1.0", JIM_ERRMSG))
2802 return JIM_ERR;
2804 Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
2805 return JIM_OK;
2808 #include <stdlib.h>
2809 #include <string.h>
2811 #if defined(JIM_REGEXP)
2812 #else
2813 #include <regex.h>
2814 #endif
2816 static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2818 regfree(objPtr->internalRep.regexpValue.compre);
2819 Jim_Free(objPtr->internalRep.regexpValue.compre);
2822 static const Jim_ObjType regexpObjType = {
2823 "regexp",
2824 FreeRegexpInternalRep,
2825 NULL,
2826 NULL,
2827 JIM_TYPE_NONE
2830 static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
2832 regex_t *compre;
2833 const char *pattern;
2834 int ret;
2837 if (objPtr->typePtr == &regexpObjType &&
2838 objPtr->internalRep.regexpValue.compre && objPtr->internalRep.regexpValue.flags == flags) {
2840 return objPtr->internalRep.regexpValue.compre;
2846 pattern = Jim_String(objPtr);
2847 compre = Jim_Alloc(sizeof(regex_t));
2849 if ((ret = regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
2850 char buf[100];
2852 regerror(ret, compre, buf, sizeof(buf));
2853 Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
2854 regfree(compre);
2855 Jim_Free(compre);
2856 return NULL;
2859 Jim_FreeIntRep(interp, objPtr);
2861 objPtr->typePtr = &regexpObjType;
2862 objPtr->internalRep.regexpValue.flags = flags;
2863 objPtr->internalRep.regexpValue.compre = compre;
2865 return compre;
2868 int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2870 int opt_indices = 0;
2871 int opt_all = 0;
2872 int opt_inline = 0;
2873 regex_t *regex;
2874 int match, i, j;
2875 int offset = 0;
2876 regmatch_t *pmatch = NULL;
2877 int source_len;
2878 int result = JIM_OK;
2879 const char *pattern;
2880 const char *source_str;
2881 int num_matches = 0;
2882 int num_vars;
2883 Jim_Obj *resultListObj = NULL;
2884 int regcomp_flags = 0;
2885 int eflags = 0;
2886 int option;
2887 enum {
2888 OPT_INDICES, OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
2890 static const char * const options[] = {
2891 "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
2894 if (argc < 3) {
2895 wrongNumArgs:
2896 Jim_WrongNumArgs(interp, 1, argv,
2897 "?-switch ...? exp string ?matchVar? ?subMatchVar ...?");
2898 return JIM_ERR;
2901 for (i = 1; i < argc; i++) {
2902 const char *opt = Jim_String(argv[i]);
2904 if (*opt != '-') {
2905 break;
2907 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
2908 return JIM_ERR;
2910 if (option == OPT_END) {
2911 i++;
2912 break;
2914 switch (option) {
2915 case OPT_INDICES:
2916 opt_indices = 1;
2917 break;
2919 case OPT_NOCASE:
2920 regcomp_flags |= REG_ICASE;
2921 break;
2923 case OPT_LINE:
2924 regcomp_flags |= REG_NEWLINE;
2925 break;
2927 case OPT_ALL:
2928 opt_all = 1;
2929 break;
2931 case OPT_INLINE:
2932 opt_inline = 1;
2933 break;
2935 case OPT_START:
2936 if (++i == argc) {
2937 goto wrongNumArgs;
2939 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
2940 return JIM_ERR;
2942 break;
2945 if (argc - i < 2) {
2946 goto wrongNumArgs;
2949 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
2950 if (!regex) {
2951 return JIM_ERR;
2954 pattern = Jim_String(argv[i]);
2955 source_str = Jim_GetString(argv[i + 1], &source_len);
2957 num_vars = argc - i - 2;
2959 if (opt_inline) {
2960 if (num_vars) {
2961 Jim_SetResultString(interp, "regexp match variables not allowed when using -inline",
2962 -1);
2963 result = JIM_ERR;
2964 goto done;
2966 num_vars = regex->re_nsub + 1;
2969 pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch));
2971 if (offset) {
2972 if (offset < 0) {
2973 offset += source_len + 1;
2975 if (offset > source_len) {
2976 source_str += source_len;
2978 else if (offset > 0) {
2979 source_str += offset;
2981 eflags |= REG_NOTBOL;
2984 if (opt_inline) {
2985 resultListObj = Jim_NewListObj(interp, NULL, 0);
2988 next_match:
2989 match = regexec(regex, source_str, num_vars + 1, pmatch, eflags);
2990 if (match >= REG_BADPAT) {
2991 char buf[100];
2993 regerror(match, regex, buf, sizeof(buf));
2994 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
2995 result = JIM_ERR;
2996 goto done;
2999 if (match == REG_NOMATCH) {
3000 goto done;
3003 num_matches++;
3005 if (opt_all && !opt_inline) {
3007 goto try_next_match;
3011 j = 0;
3012 for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
3013 Jim_Obj *resultObj;
3015 if (opt_indices) {
3016 resultObj = Jim_NewListObj(interp, NULL, 0);
3018 else {
3019 resultObj = Jim_NewStringObj(interp, "", 0);
3022 if (pmatch[j].rm_so == -1) {
3023 if (opt_indices) {
3024 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3025 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3028 else {
3029 int len = pmatch[j].rm_eo - pmatch[j].rm_so;
3031 if (opt_indices) {
3032 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
3033 offset + pmatch[j].rm_so));
3034 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
3035 offset + pmatch[j].rm_so + len - 1));
3037 else {
3038 Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, len);
3042 if (opt_inline) {
3043 Jim_ListAppendElement(interp, resultListObj, resultObj);
3045 else {
3047 result = Jim_SetVariable(interp, argv[i], resultObj);
3049 if (result != JIM_OK) {
3050 Jim_FreeObj(interp, resultObj);
3051 break;
3056 try_next_match:
3057 if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
3058 if (pmatch[0].rm_eo) {
3059 offset += pmatch[0].rm_eo;
3060 source_str += pmatch[0].rm_eo;
3062 else {
3063 source_str++;
3064 offset++;
3066 if (*source_str) {
3067 eflags = REG_NOTBOL;
3068 goto next_match;
3072 done:
3073 if (result == JIM_OK) {
3074 if (opt_inline) {
3075 Jim_SetResult(interp, resultListObj);
3077 else {
3078 Jim_SetResultInt(interp, num_matches);
3082 Jim_Free(pmatch);
3083 return result;
3086 #define MAX_SUB_MATCHES 50
3088 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3090 int regcomp_flags = 0;
3091 int regexec_flags = 0;
3092 int opt_all = 0;
3093 int offset = 0;
3094 regex_t *regex;
3095 const char *p;
3096 int result;
3097 regmatch_t pmatch[MAX_SUB_MATCHES + 1];
3098 int num_matches = 0;
3100 int i, j, n;
3101 Jim_Obj *varname;
3102 Jim_Obj *resultObj;
3103 const char *source_str;
3104 int source_len;
3105 const char *replace_str;
3106 int replace_len;
3107 const char *pattern;
3108 int option;
3109 enum {
3110 OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_END
3112 static const char * const options[] = {
3113 "-nocase", "-line", "-all", "-start", "--", NULL
3116 if (argc < 4) {
3117 wrongNumArgs:
3118 Jim_WrongNumArgs(interp, 1, argv,
3119 "?-switch ...? exp string subSpec ?varName?");
3120 return JIM_ERR;
3123 for (i = 1; i < argc; i++) {
3124 const char *opt = Jim_String(argv[i]);
3126 if (*opt != '-') {
3127 break;
3129 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3130 return JIM_ERR;
3132 if (option == OPT_END) {
3133 i++;
3134 break;
3136 switch (option) {
3137 case OPT_NOCASE:
3138 regcomp_flags |= REG_ICASE;
3139 break;
3141 case OPT_LINE:
3142 regcomp_flags |= REG_NEWLINE;
3143 break;
3145 case OPT_ALL:
3146 opt_all = 1;
3147 break;
3149 case OPT_START:
3150 if (++i == argc) {
3151 goto wrongNumArgs;
3153 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3154 return JIM_ERR;
3156 break;
3159 if (argc - i != 3 && argc - i != 4) {
3160 goto wrongNumArgs;
3163 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
3164 if (!regex) {
3165 return JIM_ERR;
3167 pattern = Jim_String(argv[i]);
3169 source_str = Jim_GetString(argv[i + 1], &source_len);
3170 replace_str = Jim_GetString(argv[i + 2], &replace_len);
3171 varname = argv[i + 3];
3174 resultObj = Jim_NewStringObj(interp, "", 0);
3176 if (offset) {
3177 if (offset < 0) {
3178 offset += source_len + 1;
3180 if (offset > source_len) {
3181 offset = source_len;
3183 else if (offset < 0) {
3184 offset = 0;
3189 Jim_AppendString(interp, resultObj, source_str, offset);
3192 n = source_len - offset;
3193 p = source_str + offset;
3194 do {
3195 int match = regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
3197 if (match >= REG_BADPAT) {
3198 char buf[100];
3200 regerror(match, regex, buf, sizeof(buf));
3201 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3202 return JIM_ERR;
3204 if (match == REG_NOMATCH) {
3205 break;
3208 num_matches++;
3210 Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
3213 for (j = 0; j < replace_len; j++) {
3214 int idx;
3215 int c = replace_str[j];
3217 if (c == '&') {
3218 idx = 0;
3220 else if (c == '\\' && j < replace_len) {
3221 c = replace_str[++j];
3222 if ((c >= '0') && (c <= '9')) {
3223 idx = c - '0';
3225 else if ((c == '\\') || (c == '&')) {
3226 Jim_AppendString(interp, resultObj, replace_str + j, 1);
3227 continue;
3229 else {
3230 Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2);
3231 continue;
3234 else {
3235 Jim_AppendString(interp, resultObj, replace_str + j, 1);
3236 continue;
3238 if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
3239 Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
3240 pmatch[idx].rm_eo - pmatch[idx].rm_so);
3244 p += pmatch[0].rm_eo;
3245 n -= pmatch[0].rm_eo;
3248 if (!opt_all || n == 0) {
3249 break;
3253 if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
3254 break;
3258 if (pattern[0] == '\0' && n) {
3260 Jim_AppendString(interp, resultObj, p, 1);
3261 p++;
3262 n--;
3265 regexec_flags |= REG_NOTBOL;
3266 } while (n);
3268 Jim_AppendString(interp, resultObj, p, -1);
3271 if (argc - i == 4) {
3272 result = Jim_SetVariable(interp, varname, resultObj);
3274 if (result == JIM_OK) {
3275 Jim_SetResultInt(interp, num_matches);
3277 else {
3278 Jim_FreeObj(interp, resultObj);
3281 else {
3282 Jim_SetResult(interp, resultObj);
3283 result = JIM_OK;
3286 return result;
3289 int Jim_regexpInit(Jim_Interp *interp)
3291 if (Jim_PackageProvide(interp, "regexp", "1.0", JIM_ERRMSG))
3292 return JIM_ERR;
3294 Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
3295 Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
3296 return JIM_OK;
3299 #include <limits.h>
3300 #include <stdlib.h>
3301 #include <string.h>
3302 #include <stdio.h>
3303 #include <errno.h>
3304 #include <sys/stat.h>
3307 #ifdef HAVE_UTIMES
3308 #include <sys/time.h>
3309 #endif
3310 #ifdef HAVE_UNISTD_H
3311 #include <unistd.h>
3312 #elif defined(_MSC_VER)
3313 #include <direct.h>
3314 #define F_OK 0
3315 #define W_OK 2
3316 #define R_OK 4
3317 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
3318 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
3319 #endif
3321 # ifndef MAXPATHLEN
3322 # define MAXPATHLEN JIM_PATH_LEN
3323 # endif
3325 #if defined(__MINGW32__) || defined(_MSC_VER)
3326 #define ISWINDOWS 1
3327 #else
3328 #define ISWINDOWS 0
3329 #endif
3332 static const char *JimGetFileType(int mode)
3334 if (S_ISREG(mode)) {
3335 return "file";
3337 else if (S_ISDIR(mode)) {
3338 return "directory";
3340 #ifdef S_ISCHR
3341 else if (S_ISCHR(mode)) {
3342 return "characterSpecial";
3344 #endif
3345 #ifdef S_ISBLK
3346 else if (S_ISBLK(mode)) {
3347 return "blockSpecial";
3349 #endif
3350 #ifdef S_ISFIFO
3351 else if (S_ISFIFO(mode)) {
3352 return "fifo";
3354 #endif
3355 #ifdef S_ISLNK
3356 else if (S_ISLNK(mode)) {
3357 return "link";
3359 #endif
3360 #ifdef S_ISSOCK
3361 else if (S_ISSOCK(mode)) {
3362 return "socket";
3364 #endif
3365 return "unknown";
3368 static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
3370 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
3371 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
3374 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
3377 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
3379 AppendStatElement(interp, listObj, "dev", sb->st_dev);
3380 AppendStatElement(interp, listObj, "ino", sb->st_ino);
3381 AppendStatElement(interp, listObj, "mode", sb->st_mode);
3382 AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
3383 AppendStatElement(interp, listObj, "uid", sb->st_uid);
3384 AppendStatElement(interp, listObj, "gid", sb->st_gid);
3385 AppendStatElement(interp, listObj, "size", sb->st_size);
3386 AppendStatElement(interp, listObj, "atime", sb->st_atime);
3387 AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
3388 AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
3389 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
3390 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));
3393 if (varName) {
3394 Jim_Obj *objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
3395 if (objPtr) {
3396 if (Jim_DictSize(interp, objPtr) < 0) {
3398 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
3399 Jim_FreeNewObj(interp, listObj);
3400 return JIM_ERR;
3403 if (Jim_IsShared(objPtr))
3404 objPtr = Jim_DuplicateObj(interp, objPtr);
3407 Jim_ListAppendList(interp, objPtr, listObj);
3408 Jim_DictSize(interp, objPtr);
3409 Jim_InvalidateStringRep(objPtr);
3411 Jim_FreeNewObj(interp, listObj);
3412 listObj = objPtr;
3414 Jim_SetVariable(interp, varName, listObj);
3418 Jim_SetResult(interp, listObj);
3420 return JIM_OK;
3423 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3425 const char *path = Jim_String(argv[0]);
3426 const char *p = strrchr(path, '/');
3428 if (!p && path[0] == '.' && path[1] == '.' && path[2] == '\0') {
3429 Jim_SetResultString(interp, "..", -1);
3430 } else if (!p) {
3431 Jim_SetResultString(interp, ".", -1);
3433 else if (p == path) {
3434 Jim_SetResultString(interp, "/", -1);
3436 else if (ISWINDOWS && p[-1] == ':') {
3438 Jim_SetResultString(interp, path, p - path + 1);
3440 else {
3441 Jim_SetResultString(interp, path, p - path);
3443 return JIM_OK;
3446 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3448 const char *path = Jim_String(argv[0]);
3449 const char *lastSlash = strrchr(path, '/');
3450 const char *p = strrchr(path, '.');
3452 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
3453 Jim_SetResult(interp, argv[0]);
3455 else {
3456 Jim_SetResultString(interp, path, p - path);
3458 return JIM_OK;
3461 static int file_cmd_extension(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 p = "";
3470 Jim_SetResultString(interp, p, -1);
3471 return JIM_OK;
3474 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3476 const char *path = Jim_String(argv[0]);
3477 const char *lastSlash = strrchr(path, '/');
3479 if (lastSlash) {
3480 Jim_SetResultString(interp, lastSlash + 1, -1);
3482 else {
3483 Jim_SetResult(interp, argv[0]);
3485 return JIM_OK;
3488 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3490 #ifdef HAVE_REALPATH
3491 const char *path = Jim_String(argv[0]);
3492 char *newname = Jim_Alloc(MAXPATHLEN + 1);
3494 if (realpath(path, newname)) {
3495 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
3496 return JIM_OK;
3498 else {
3499 Jim_Free(newname);
3500 Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
3501 return JIM_ERR;
3503 #else
3504 Jim_SetResultString(interp, "Not implemented", -1);
3505 return JIM_ERR;
3506 #endif
3509 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3511 int i;
3512 char *newname = Jim_Alloc(MAXPATHLEN + 1);
3513 char *last = newname;
3515 *newname = 0;
3518 for (i = 0; i < argc; i++) {
3519 int len;
3520 const char *part = Jim_GetString(argv[i], &len);
3522 if (*part == '/') {
3524 last = newname;
3526 else if (ISWINDOWS && strchr(part, ':')) {
3528 last = newname;
3530 else if (part[0] == '.') {
3531 if (part[1] == '/') {
3532 part += 2;
3533 len -= 2;
3535 else if (part[1] == 0 && last != newname) {
3537 continue;
3542 if (last != newname && last[-1] != '/') {
3543 *last++ = '/';
3546 if (len) {
3547 if (last + len - newname >= MAXPATHLEN) {
3548 Jim_Free(newname);
3549 Jim_SetResultString(interp, "Path too long", -1);
3550 return JIM_ERR;
3552 memcpy(last, part, len);
3553 last += len;
3557 if (last > newname + 1 && last[-1] == '/') {
3559 if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
3560 *--last = 0;
3565 *last = 0;
3569 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
3571 return JIM_OK;
3574 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
3576 Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1);
3578 return JIM_OK;
3581 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3583 return file_access(interp, argv[0], R_OK);
3586 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3588 return file_access(interp, argv[0], W_OK);
3591 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3593 #ifdef X_OK
3594 return file_access(interp, argv[0], X_OK);
3595 #else
3597 Jim_SetResultBool(interp, 1);
3598 return JIM_OK;
3599 #endif
3602 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3604 return file_access(interp, argv[0], F_OK);
3607 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3609 int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
3611 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
3612 argc++;
3613 argv--;
3616 while (argc--) {
3617 const char *path = Jim_String(argv[0]);
3619 if (unlink(path) == -1 && errno != ENOENT) {
3620 if (rmdir(path) == -1) {
3622 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
3623 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
3624 strerror(errno));
3625 return JIM_ERR;
3629 argv++;
3631 return JIM_OK;
3634 #ifdef HAVE_MKDIR_ONE_ARG
3635 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
3636 #else
3637 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
3638 #endif
3640 static int mkdir_all(char *path)
3642 int ok = 1;
3645 goto first;
3647 while (ok--) {
3650 char *slash = strrchr(path, '/');
3652 if (slash && slash != path) {
3653 *slash = 0;
3654 if (mkdir_all(path) != 0) {
3655 return -1;
3657 *slash = '/';
3660 first:
3661 if (MKDIR_DEFAULT(path) == 0) {
3662 return 0;
3664 if (errno == ENOENT) {
3666 continue;
3669 if (errno == EEXIST) {
3670 struct stat sb;
3672 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
3673 return 0;
3676 errno = EEXIST;
3679 break;
3681 return -1;
3684 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3686 while (argc--) {
3687 char *path = Jim_StrDup(Jim_String(argv[0]));
3688 int rc = mkdir_all(path);
3690 Jim_Free(path);
3691 if (rc != 0) {
3692 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
3693 strerror(errno));
3694 return JIM_ERR;
3696 argv++;
3698 return JIM_OK;
3701 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3703 int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL);
3705 if (fd < 0) {
3706 return JIM_ERR;
3708 close(fd);
3710 return JIM_OK;
3713 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3715 const char *source;
3716 const char *dest;
3717 int force = 0;
3719 if (argc == 3) {
3720 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
3721 return -1;
3723 force++;
3724 argv++;
3725 argc--;
3728 source = Jim_String(argv[0]);
3729 dest = Jim_String(argv[1]);
3731 if (!force && access(dest, F_OK) == 0) {
3732 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
3733 argv[1]);
3734 return JIM_ERR;
3737 if (rename(source, dest) != 0) {
3738 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3739 strerror(errno));
3740 return JIM_ERR;
3743 return JIM_OK;
3746 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
3747 static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3749 int ret;
3750 const char *source;
3751 const char *dest;
3752 static const char * const options[] = { "-hard", "-symbolic", NULL };
3753 enum { OPT_HARD, OPT_SYMBOLIC, };
3754 int option = OPT_HARD;
3756 if (argc == 3) {
3757 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
3758 return JIM_ERR;
3760 argv++;
3761 argc--;
3764 dest = Jim_String(argv[0]);
3765 source = Jim_String(argv[1]);
3767 if (option == OPT_HARD) {
3768 ret = link(source, dest);
3770 else {
3771 ret = symlink(source, dest);
3774 if (ret != 0) {
3775 Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3776 strerror(errno));
3777 return JIM_ERR;
3780 return JIM_OK;
3782 #endif
3784 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3786 const char *path = Jim_String(filename);
3788 if (stat(path, sb) == -1) {
3789 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3790 return JIM_ERR;
3792 return JIM_OK;
3795 #ifdef HAVE_LSTAT
3796 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3798 const char *path = Jim_String(filename);
3800 if (lstat(path, sb) == -1) {
3801 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3802 return JIM_ERR;
3804 return JIM_OK;
3806 #else
3807 #define file_lstat file_stat
3808 #endif
3810 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3812 struct stat sb;
3814 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3815 return JIM_ERR;
3817 Jim_SetResultInt(interp, sb.st_atime);
3818 return JIM_OK;
3821 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3823 struct stat sb;
3825 if (argc == 2) {
3826 #ifdef HAVE_UTIMES
3827 jim_wide newtime;
3828 struct timeval times[2];
3830 if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) {
3831 return JIM_ERR;
3834 times[1].tv_sec = times[0].tv_sec = newtime;
3835 times[1].tv_usec = times[0].tv_usec = 0;
3837 if (utimes(Jim_String(argv[0]), times) != 0) {
3838 Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno));
3839 return JIM_ERR;
3841 #else
3842 Jim_SetResultString(interp, "Not implemented", -1);
3843 return JIM_ERR;
3844 #endif
3846 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3847 return JIM_ERR;
3849 Jim_SetResultInt(interp, sb.st_mtime);
3850 return JIM_OK;
3853 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3855 return Jim_EvalPrefix(interp, "file copy", argc, argv);
3858 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3860 struct stat sb;
3862 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3863 return JIM_ERR;
3865 Jim_SetResultInt(interp, sb.st_size);
3866 return JIM_OK;
3869 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3871 struct stat sb;
3872 int ret = 0;
3874 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3875 ret = S_ISDIR(sb.st_mode);
3877 Jim_SetResultInt(interp, ret);
3878 return JIM_OK;
3881 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3883 struct stat sb;
3884 int ret = 0;
3886 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3887 ret = S_ISREG(sb.st_mode);
3889 Jim_SetResultInt(interp, ret);
3890 return JIM_OK;
3893 #ifdef HAVE_GETEUID
3894 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3896 struct stat sb;
3897 int ret = 0;
3899 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
3900 ret = (geteuid() == sb.st_uid);
3902 Jim_SetResultInt(interp, ret);
3903 return JIM_OK;
3905 #endif
3907 #if defined(HAVE_READLINK)
3908 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3910 const char *path = Jim_String(argv[0]);
3911 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
3913 int linkLength = readlink(path, linkValue, MAXPATHLEN);
3915 if (linkLength == -1) {
3916 Jim_Free(linkValue);
3917 Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
3918 return JIM_ERR;
3920 linkValue[linkLength] = 0;
3921 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
3922 return JIM_OK;
3924 #endif
3926 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3928 struct stat sb;
3930 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
3931 return JIM_ERR;
3933 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
3934 return JIM_OK;
3937 #ifdef HAVE_LSTAT
3938 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3940 struct stat sb;
3942 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
3943 return JIM_ERR;
3945 return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
3947 #else
3948 #define file_cmd_lstat file_cmd_stat
3949 #endif
3951 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3953 struct stat sb;
3955 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3956 return JIM_ERR;
3958 return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
3961 static const jim_subcmd_type file_command_table[] = {
3962 { "atime",
3963 "name",
3964 file_cmd_atime,
3969 { "mtime",
3970 "name ?time?",
3971 file_cmd_mtime,
3976 { "copy",
3977 "?-force? source dest",
3978 file_cmd_copy,
3983 { "dirname",
3984 "name",
3985 file_cmd_dirname,
3990 { "rootname",
3991 "name",
3992 file_cmd_rootname,
3997 { "extension",
3998 "name",
3999 file_cmd_extension,
4004 { "tail",
4005 "name",
4006 file_cmd_tail,
4011 { "normalize",
4012 "name",
4013 file_cmd_normalize,
4018 { "join",
4019 "name ?name ...?",
4020 file_cmd_join,
4025 { "readable",
4026 "name",
4027 file_cmd_readable,
4032 { "writable",
4033 "name",
4034 file_cmd_writable,
4039 { "executable",
4040 "name",
4041 file_cmd_executable,
4046 { "exists",
4047 "name",
4048 file_cmd_exists,
4053 { "delete",
4054 "?-force|--? name ...",
4055 file_cmd_delete,
4060 { "mkdir",
4061 "dir ...",
4062 file_cmd_mkdir,
4067 { "tempfile",
4068 "?template?",
4069 file_cmd_tempfile,
4074 { "rename",
4075 "?-force? source dest",
4076 file_cmd_rename,
4081 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
4082 { "link",
4083 "?-symbolic|-hard? newname target",
4084 file_cmd_link,
4089 #endif
4090 #if defined(HAVE_READLINK)
4091 { "readlink",
4092 "name",
4093 file_cmd_readlink,
4098 #endif
4099 { "size",
4100 "name",
4101 file_cmd_size,
4106 { "stat",
4107 "name ?var?",
4108 file_cmd_stat,
4113 { "lstat",
4114 "name ?var?",
4115 file_cmd_lstat,
4120 { "type",
4121 "name",
4122 file_cmd_type,
4127 #ifdef HAVE_GETEUID
4128 { "owned",
4129 "name",
4130 file_cmd_owned,
4135 #endif
4136 { "isdirectory",
4137 "name",
4138 file_cmd_isdirectory,
4143 { "isfile",
4144 "name",
4145 file_cmd_isfile,
4151 NULL
4155 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4157 const char *path;
4159 if (argc != 2) {
4160 Jim_WrongNumArgs(interp, 1, argv, "dirname");
4161 return JIM_ERR;
4164 path = Jim_String(argv[1]);
4166 if (chdir(path) != 0) {
4167 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
4168 strerror(errno));
4169 return JIM_ERR;
4171 return JIM_OK;
4174 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4176 char *cwd = Jim_Alloc(MAXPATHLEN);
4178 if (getcwd(cwd, MAXPATHLEN) == NULL) {
4179 Jim_SetResultString(interp, "Failed to get pwd", -1);
4180 Jim_Free(cwd);
4181 return JIM_ERR;
4183 else if (ISWINDOWS) {
4185 char *p = cwd;
4186 while ((p = strchr(p, '\\')) != NULL) {
4187 *p++ = '/';
4191 Jim_SetResultString(interp, cwd, -1);
4193 Jim_Free(cwd);
4194 return JIM_OK;
4197 int Jim_fileInit(Jim_Interp *interp)
4199 if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
4200 return JIM_ERR;
4202 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
4203 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
4204 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
4205 return JIM_OK;
4208 #include <string.h>
4209 #include <ctype.h>
4212 #if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
4213 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4215 Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
4216 int i, j;
4217 int rc;
4220 for (i = 1; i < argc; i++) {
4221 int len;
4222 const char *arg = Jim_GetString(argv[i], &len);
4224 if (i > 1) {
4225 Jim_AppendString(interp, cmdlineObj, " ", 1);
4227 if (strpbrk(arg, "\\\" ") == NULL) {
4229 Jim_AppendString(interp, cmdlineObj, arg, len);
4230 continue;
4233 Jim_AppendString(interp, cmdlineObj, "\"", 1);
4234 for (j = 0; j < len; j++) {
4235 if (arg[j] == '\\' || arg[j] == '"') {
4236 Jim_AppendString(interp, cmdlineObj, "\\", 1);
4238 Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
4240 Jim_AppendString(interp, cmdlineObj, "\"", 1);
4242 rc = system(Jim_String(cmdlineObj));
4244 Jim_FreeNewObj(interp, cmdlineObj);
4246 if (rc) {
4247 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
4248 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4249 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
4250 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
4251 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
4252 return JIM_ERR;
4255 return JIM_OK;
4258 int Jim_execInit(Jim_Interp *interp)
4260 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
4261 return JIM_ERR;
4263 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
4264 return JIM_OK;
4266 #else
4269 #include <errno.h>
4270 #include <signal.h>
4272 #if defined(__MINGW32__)
4274 #ifndef STRICT
4275 #define STRICT
4276 #endif
4277 #define WIN32_LEAN_AND_MEAN
4278 #include <windows.h>
4279 #include <fcntl.h>
4281 typedef HANDLE fdtype;
4282 typedef HANDLE pidtype;
4283 #define JIM_BAD_FD INVALID_HANDLE_VALUE
4284 #define JIM_BAD_PID INVALID_HANDLE_VALUE
4285 #define JimCloseFd CloseHandle
4287 #define WIFEXITED(STATUS) 1
4288 #define WEXITSTATUS(STATUS) (STATUS)
4289 #define WIFSIGNALED(STATUS) 0
4290 #define WTERMSIG(STATUS) 0
4291 #define WNOHANG 1
4293 static fdtype JimFileno(FILE *fh);
4294 static pidtype JimWaitPid(pidtype pid, int *status, int nohang);
4295 static fdtype JimDupFd(fdtype infd);
4296 static fdtype JimOpenForRead(const char *filename);
4297 static FILE *JimFdOpenForRead(fdtype fd);
4298 static int JimPipe(fdtype pipefd[2]);
4299 static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env,
4300 fdtype inputId, fdtype outputId, fdtype errorId);
4301 static int JimErrno(void);
4302 #else
4303 #include <unistd.h>
4304 #include <fcntl.h>
4305 #include <sys/wait.h>
4306 #include <sys/stat.h>
4308 typedef int fdtype;
4309 typedef int pidtype;
4310 #define JimPipe pipe
4311 #define JimErrno() errno
4312 #define JIM_BAD_FD -1
4313 #define JIM_BAD_PID -1
4314 #define JimFileno fileno
4315 #define JimReadFd read
4316 #define JimCloseFd close
4317 #define JimWaitPid waitpid
4318 #define JimDupFd dup
4319 #define JimFdOpenForRead(FD) fdopen((FD), "r")
4320 #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0)
4322 #ifndef HAVE_EXECVPE
4323 #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
4324 #endif
4325 #endif
4327 static const char *JimStrError(void);
4328 static char **JimSaveEnv(char **env);
4329 static void JimRestoreEnv(char **env);
4330 static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
4331 pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr);
4332 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr);
4333 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj);
4334 static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents, int len);
4335 static fdtype JimOpenForWrite(const char *filename, int append);
4336 static int JimRewindFd(fdtype fd);
4338 static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
4340 Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError());
4343 static const char *JimStrError(void)
4345 return strerror(JimErrno());
4348 static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
4350 int len;
4351 const char *s = Jim_GetString(objPtr, &len);
4353 if (len > 0 && s[len - 1] == '\n') {
4354 objPtr->length--;
4355 objPtr->bytes[objPtr->length] = '\0';
4359 static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj)
4361 char buf[256];
4362 FILE *fh = JimFdOpenForRead(fd);
4363 int ret = 0;
4365 if (fh == NULL) {
4366 return -1;
4369 while (1) {
4370 int retval = fread(buf, 1, sizeof(buf), fh);
4371 if (retval > 0) {
4372 ret = 1;
4373 Jim_AppendString(interp, strObj, buf, retval);
4375 if (retval != sizeof(buf)) {
4376 break;
4379 fclose(fh);
4380 return ret;
4383 static char **JimBuildEnv(Jim_Interp *interp)
4385 int i;
4386 int size;
4387 int num;
4388 int n;
4389 char **envptr;
4390 char *envdata;
4392 Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
4394 if (!objPtr) {
4395 return Jim_GetEnviron();
4400 num = Jim_ListLength(interp, objPtr);
4401 if (num % 2) {
4403 num--;
4405 size = Jim_Length(objPtr) + 2;
4407 envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
4408 envdata = (char *)&envptr[num / 2 + 1];
4410 n = 0;
4411 for (i = 0; i < num; i += 2) {
4412 const char *s1, *s2;
4413 Jim_Obj *elemObj;
4415 Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
4416 s1 = Jim_String(elemObj);
4417 Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
4418 s2 = Jim_String(elemObj);
4420 envptr[n] = envdata;
4421 envdata += sprintf(envdata, "%s=%s", s1, s2);
4422 envdata++;
4423 n++;
4425 envptr[n] = NULL;
4426 *envdata = 0;
4428 return envptr;
4431 static void JimFreeEnv(char **env, char **original_environ)
4433 if (env != original_environ) {
4434 Jim_Free(env);
4438 #ifndef jim_ext_signal
4440 const char *Jim_SignalId(int sig)
4442 static char buf[10];
4443 snprintf(buf, sizeof(buf), "%d", sig);
4444 return buf;
4447 const char *Jim_SignalName(int sig)
4449 return Jim_SignalId(sig);
4451 #endif
4453 static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
4455 Jim_Obj *errorCode;
4457 if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
4458 return JIM_OK;
4460 errorCode = Jim_NewListObj(interp, NULL, 0);
4462 if (WIFEXITED(waitStatus)) {
4463 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4464 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4465 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
4467 else {
4468 const char *type;
4469 const char *action;
4471 if (WIFSIGNALED(waitStatus)) {
4472 type = "CHILDKILLED";
4473 action = "killed";
4475 else {
4476 type = "CHILDSUSP";
4477 action = "suspended";
4480 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
4482 if (errStrObj) {
4483 Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
4486 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4487 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalId(WTERMSIG(waitStatus)), -1));
4488 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1));
4490 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
4492 return JIM_ERR;
4496 struct WaitInfo
4498 pidtype pid;
4499 int status;
4500 int flags;
4503 struct WaitInfoTable {
4504 struct WaitInfo *info;
4505 int size;
4506 int used;
4510 #define WI_DETACHED 2
4512 #define WAIT_TABLE_GROW_BY 4
4514 static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
4516 struct WaitInfoTable *table = privData;
4518 Jim_Free(table->info);
4519 Jim_Free(table);
4522 static struct WaitInfoTable *JimAllocWaitInfoTable(void)
4524 struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
4525 table->info = NULL;
4526 table->size = table->used = 0;
4528 return table;
4531 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4533 fdtype outputId;
4534 fdtype errorId;
4535 pidtype *pidPtr;
4536 int numPids, result;
4537 int child_siginfo = 1;
4538 Jim_Obj *childErrObj;
4539 Jim_Obj *errStrObj;
4541 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
4542 Jim_Obj *listObj;
4543 int i;
4545 argc--;
4546 numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
4547 if (numPids < 0) {
4548 return JIM_ERR;
4551 listObj = Jim_NewListObj(interp, NULL, 0);
4552 for (i = 0; i < numPids; i++) {
4553 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i]));
4555 Jim_SetResult(interp, listObj);
4556 JimDetachPids(interp, numPids, pidPtr);
4557 Jim_Free(pidPtr);
4558 return JIM_OK;
4561 numPids =
4562 JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
4564 if (numPids < 0) {
4565 return JIM_ERR;
4568 result = JIM_OK;
4570 errStrObj = Jim_NewStringObj(interp, "", 0);
4573 if (outputId != JIM_BAD_FD) {
4574 if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) {
4575 result = JIM_ERR;
4576 Jim_SetResultErrno(interp, "error reading from output pipe");
4581 childErrObj = Jim_NewStringObj(interp, "", 0);
4582 Jim_IncrRefCount(childErrObj);
4584 if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
4585 result = JIM_ERR;
4588 if (errorId != JIM_BAD_FD) {
4589 int ret;
4590 JimRewindFd(errorId);
4591 ret = JimAppendStreamToString(interp, errorId, errStrObj);
4592 if (ret < 0) {
4593 Jim_SetResultErrno(interp, "error reading from error pipe");
4594 result = JIM_ERR;
4596 else if (ret > 0) {
4598 child_siginfo = 0;
4602 if (child_siginfo) {
4604 Jim_AppendObj(interp, errStrObj, childErrObj);
4606 Jim_DecrRefCount(interp, childErrObj);
4609 Jim_RemoveTrailingNewline(errStrObj);
4612 Jim_SetResult(interp, errStrObj);
4614 return result;
4617 static void JimReapDetachedPids(struct WaitInfoTable *table)
4619 struct WaitInfo *waitPtr;
4620 int count;
4621 int dest;
4623 if (!table) {
4624 return;
4627 waitPtr = table->info;
4628 dest = 0;
4629 for (count = table->used; count > 0; waitPtr++, count--) {
4630 if (waitPtr->flags & WI_DETACHED) {
4631 int status;
4632 pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG);
4633 if (pid == waitPtr->pid) {
4635 table->used--;
4636 continue;
4639 if (waitPtr != &table->info[dest]) {
4640 table->info[dest] = *waitPtr;
4642 dest++;
4646 static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
4648 int i;
4651 for (i = 0; i < table->used; i++) {
4652 if (pid == table->info[i].pid) {
4654 JimWaitPid(pid, statusPtr, 0);
4657 if (i != table->used - 1) {
4658 table->info[i] = table->info[table->used - 1];
4660 table->used--;
4661 return pid;
4666 return JIM_BAD_PID;
4669 static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr)
4671 int j;
4672 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4674 for (j = 0; j < numPids; j++) {
4676 int i;
4677 for (i = 0; i < table->used; i++) {
4678 if (pidPtr[j] == table->info[i].pid) {
4679 table->info[i].flags |= WI_DETACHED;
4680 break;
4686 static FILE *JimGetAioFilehandle(Jim_Interp *interp, const char *name)
4688 FILE *fh;
4689 Jim_Obj *fhObj;
4691 fhObj = Jim_NewStringObj(interp, name, -1);
4692 Jim_IncrRefCount(fhObj);
4693 fh = Jim_AioFilehandle(interp, fhObj);
4694 Jim_DecrRefCount(interp, fhObj);
4696 return fh;
4699 static int
4700 JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr,
4701 fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr)
4703 pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all
4704 * the pids of child processes. */
4705 int numPids = 0; /* Actual number of processes that exist
4706 * at *pidPtr right now. */
4707 int cmdCount; /* Count of number of distinct commands
4708 * found in argc/argv. */
4709 const char *input = NULL; /* Describes input for pipeline, depending
4710 * on "inputFile". NULL means take input
4711 * from stdin/pipe. */
4712 int input_len = 0;
4714 #define FILE_NAME 0
4715 #define FILE_APPEND 1
4716 #define FILE_HANDLE 2
4717 #define FILE_TEXT 3
4719 int inputFile = FILE_NAME; /* 1 means input is name of input file.
4720 * 2 means input is filehandle name.
4721 * 0 means input holds actual
4722 * text to be input to command. */
4724 int outputFile = FILE_NAME; /* 0 means output is the name of output file.
4725 * 1 means output is the name of output file, and append.
4726 * 2 means output is filehandle name.
4727 * All this is ignored if output is NULL
4729 int errorFile = FILE_NAME; /* 0 means error is the name of error file.
4730 * 1 means error is the name of error file, and append.
4731 * 2 means error is filehandle name.
4732 * All this is ignored if error is NULL
4734 const char *output = NULL; /* Holds name of output file to pipe to,
4735 * or NULL if output goes to stdout/pipe. */
4736 const char *error = NULL; /* Holds name of stderr file to pipe to,
4737 * or NULL if stderr goes to stderr/pipe. */
4738 fdtype inputId = JIM_BAD_FD;
4739 fdtype outputId = JIM_BAD_FD;
4740 fdtype errorId = JIM_BAD_FD;
4741 fdtype lastOutputId = JIM_BAD_FD;
4742 fdtype pipeIds[2];
4743 int firstArg, lastArg; /* Indexes of first and last arguments in
4744 * current command. */
4745 int lastBar;
4746 int i;
4747 pidtype pid;
4748 char **save_environ;
4749 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4752 char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
4753 int arg_count = 0;
4755 JimReapDetachedPids(table);
4757 if (inPipePtr != NULL) {
4758 *inPipePtr = JIM_BAD_FD;
4760 if (outPipePtr != NULL) {
4761 *outPipePtr = JIM_BAD_FD;
4763 if (errFilePtr != NULL) {
4764 *errFilePtr = JIM_BAD_FD;
4766 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4768 cmdCount = 1;
4769 lastBar = -1;
4770 for (i = 0; i < argc; i++) {
4771 const char *arg = Jim_String(argv[i]);
4773 if (arg[0] == '<') {
4774 inputFile = FILE_NAME;
4775 input = arg + 1;
4776 if (*input == '<') {
4777 inputFile = FILE_TEXT;
4778 input_len = Jim_Length(argv[i]) - 2;
4779 input++;
4781 else if (*input == '@') {
4782 inputFile = FILE_HANDLE;
4783 input++;
4786 if (!*input && ++i < argc) {
4787 input = Jim_GetString(argv[i], &input_len);
4790 else if (arg[0] == '>') {
4791 int dup_error = 0;
4793 outputFile = FILE_NAME;
4795 output = arg + 1;
4796 if (*output == '>') {
4797 outputFile = FILE_APPEND;
4798 output++;
4800 if (*output == '&') {
4802 output++;
4803 dup_error = 1;
4805 if (*output == '@') {
4806 outputFile = FILE_HANDLE;
4807 output++;
4809 if (!*output && ++i < argc) {
4810 output = Jim_String(argv[i]);
4812 if (dup_error) {
4813 errorFile = outputFile;
4814 error = output;
4817 else if (arg[0] == '2' && arg[1] == '>') {
4818 error = arg + 2;
4819 errorFile = FILE_NAME;
4821 if (*error == '@') {
4822 errorFile = FILE_HANDLE;
4823 error++;
4825 else if (*error == '>') {
4826 errorFile = FILE_APPEND;
4827 error++;
4829 if (!*error && ++i < argc) {
4830 error = Jim_String(argv[i]);
4833 else {
4834 if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
4835 if (i == lastBar + 1 || i == argc - 1) {
4836 Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
4837 goto badargs;
4839 lastBar = i;
4840 cmdCount++;
4843 arg_array[arg_count++] = (char *)arg;
4844 continue;
4847 if (i >= argc) {
4848 Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
4849 goto badargs;
4853 if (arg_count == 0) {
4854 Jim_SetResultString(interp, "didn't specify command to execute", -1);
4855 badargs:
4856 Jim_Free(arg_array);
4857 return -1;
4861 save_environ = JimSaveEnv(JimBuildEnv(interp));
4863 if (input != NULL) {
4864 if (inputFile == FILE_TEXT) {
4865 inputId = JimCreateTemp(interp, input, input_len);
4866 if (inputId == JIM_BAD_FD) {
4867 goto error;
4870 else if (inputFile == FILE_HANDLE) {
4872 FILE *fh = JimGetAioFilehandle(interp, input);
4874 if (fh == NULL) {
4875 goto error;
4877 inputId = JimDupFd(JimFileno(fh));
4879 else {
4880 inputId = JimOpenForRead(input);
4881 if (inputId == JIM_BAD_FD) {
4882 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError());
4883 goto error;
4887 else if (inPipePtr != NULL) {
4888 if (JimPipe(pipeIds) != 0) {
4889 Jim_SetResultErrno(interp, "couldn't create input pipe for command");
4890 goto error;
4892 inputId = pipeIds[0];
4893 *inPipePtr = pipeIds[1];
4894 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4897 if (output != NULL) {
4898 if (outputFile == FILE_HANDLE) {
4899 FILE *fh = JimGetAioFilehandle(interp, output);
4900 if (fh == NULL) {
4901 goto error;
4903 fflush(fh);
4904 lastOutputId = JimDupFd(JimFileno(fh));
4906 else {
4907 lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND);
4908 if (lastOutputId == JIM_BAD_FD) {
4909 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError());
4910 goto error;
4914 else if (outPipePtr != NULL) {
4915 if (JimPipe(pipeIds) != 0) {
4916 Jim_SetResultErrno(interp, "couldn't create output pipe");
4917 goto error;
4919 lastOutputId = pipeIds[1];
4920 *outPipePtr = pipeIds[0];
4921 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
4924 if (error != NULL) {
4925 if (errorFile == FILE_HANDLE) {
4926 if (strcmp(error, "1") == 0) {
4928 if (lastOutputId != JIM_BAD_FD) {
4929 errorId = JimDupFd(lastOutputId);
4931 else {
4933 error = "stdout";
4936 if (errorId == JIM_BAD_FD) {
4937 FILE *fh = JimGetAioFilehandle(interp, error);
4938 if (fh == NULL) {
4939 goto error;
4941 fflush(fh);
4942 errorId = JimDupFd(JimFileno(fh));
4945 else {
4946 errorId = JimOpenForWrite(error, errorFile == FILE_APPEND);
4947 if (errorId == JIM_BAD_FD) {
4948 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError());
4949 goto error;
4953 else if (errFilePtr != NULL) {
4954 errorId = JimCreateTemp(interp, NULL, 0);
4955 if (errorId == JIM_BAD_FD) {
4956 goto error;
4958 *errFilePtr = JimDupFd(errorId);
4962 pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
4963 for (i = 0; i < numPids; i++) {
4964 pidPtr[i] = JIM_BAD_PID;
4966 for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
4967 int pipe_dup_err = 0;
4968 fdtype origErrorId = errorId;
4970 for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
4971 if (arg_array[lastArg][0] == '|') {
4972 if (arg_array[lastArg][1] == '&') {
4973 pipe_dup_err = 1;
4975 break;
4979 arg_array[lastArg] = NULL;
4980 if (lastArg == arg_count) {
4981 outputId = lastOutputId;
4983 else {
4984 if (JimPipe(pipeIds) != 0) {
4985 Jim_SetResultErrno(interp, "couldn't create pipe");
4986 goto error;
4988 outputId = pipeIds[1];
4992 if (pipe_dup_err) {
4993 errorId = outputId;
4998 #ifdef __MINGW32__
4999 pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId);
5000 if (pid == JIM_BAD_PID) {
5001 Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
5002 goto error;
5004 #else
5005 pid = vfork();
5006 if (pid < 0) {
5007 Jim_SetResultErrno(interp, "couldn't fork child process");
5008 goto error;
5010 if (pid == 0) {
5013 if (inputId != -1) dup2(inputId, 0);
5014 if (outputId != -1) dup2(outputId, 1);
5015 if (errorId != -1) dup2(errorId, 2);
5017 for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) {
5018 close(i);
5022 (void)signal(SIGPIPE, SIG_DFL);
5024 execvpe(arg_array[firstArg], &arg_array[firstArg], Jim_GetEnviron());
5027 fprintf(stderr, "couldn't exec \"%s\"\n", arg_array[firstArg]);
5028 _exit(127);
5030 #endif
5034 if (table->used == table->size) {
5035 table->size += WAIT_TABLE_GROW_BY;
5036 table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
5039 table->info[table->used].pid = pid;
5040 table->info[table->used].flags = 0;
5041 table->used++;
5043 pidPtr[numPids] = pid;
5046 errorId = origErrorId;
5049 if (inputId != JIM_BAD_FD) {
5050 JimCloseFd(inputId);
5052 if (outputId != JIM_BAD_FD) {
5053 JimCloseFd(outputId);
5055 inputId = pipeIds[0];
5056 pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
5058 *pidArrayPtr = pidPtr;
5061 cleanup:
5062 if (inputId != JIM_BAD_FD) {
5063 JimCloseFd(inputId);
5065 if (lastOutputId != JIM_BAD_FD) {
5066 JimCloseFd(lastOutputId);
5068 if (errorId != JIM_BAD_FD) {
5069 JimCloseFd(errorId);
5071 Jim_Free(arg_array);
5073 JimRestoreEnv(save_environ);
5075 return numPids;
5078 error:
5079 if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) {
5080 JimCloseFd(*inPipePtr);
5081 *inPipePtr = JIM_BAD_FD;
5083 if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) {
5084 JimCloseFd(*outPipePtr);
5085 *outPipePtr = JIM_BAD_FD;
5087 if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) {
5088 JimCloseFd(*errFilePtr);
5089 *errFilePtr = JIM_BAD_FD;
5091 if (pipeIds[0] != JIM_BAD_FD) {
5092 JimCloseFd(pipeIds[0]);
5094 if (pipeIds[1] != JIM_BAD_FD) {
5095 JimCloseFd(pipeIds[1]);
5097 if (pidPtr != NULL) {
5098 for (i = 0; i < numPids; i++) {
5099 if (pidPtr[i] != JIM_BAD_PID) {
5100 JimDetachPids(interp, 1, &pidPtr[i]);
5103 Jim_Free(pidPtr);
5105 numPids = -1;
5106 goto cleanup;
5110 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj)
5112 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
5113 int result = JIM_OK;
5114 int i;
5117 for (i = 0; i < numPids; i++) {
5118 int waitStatus = 0;
5119 if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) {
5120 if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus, errStrObj) != JIM_OK) {
5121 result = JIM_ERR;
5125 Jim_Free(pidPtr);
5127 return result;
5130 int Jim_execInit(Jim_Interp *interp)
5132 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
5133 return JIM_ERR;
5135 #ifdef SIGPIPE
5136 (void)signal(SIGPIPE, SIG_IGN);
5137 #endif
5139 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable);
5140 return JIM_OK;
5143 #if defined(__MINGW32__)
5146 static SECURITY_ATTRIBUTES *JimStdSecAttrs(void)
5148 static SECURITY_ATTRIBUTES secAtts;
5150 secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
5151 secAtts.lpSecurityDescriptor = NULL;
5152 secAtts.bInheritHandle = TRUE;
5153 return &secAtts;
5156 static int JimErrno(void)
5158 switch (GetLastError()) {
5159 case ERROR_FILE_NOT_FOUND: return ENOENT;
5160 case ERROR_PATH_NOT_FOUND: return ENOENT;
5161 case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
5162 case ERROR_ACCESS_DENIED: return EACCES;
5163 case ERROR_INVALID_HANDLE: return EBADF;
5164 case ERROR_BAD_ENVIRONMENT: return E2BIG;
5165 case ERROR_BAD_FORMAT: return ENOEXEC;
5166 case ERROR_INVALID_ACCESS: return EACCES;
5167 case ERROR_INVALID_DRIVE: return ENOENT;
5168 case ERROR_CURRENT_DIRECTORY: return EACCES;
5169 case ERROR_NOT_SAME_DEVICE: return EXDEV;
5170 case ERROR_NO_MORE_FILES: return ENOENT;
5171 case ERROR_WRITE_PROTECT: return EROFS;
5172 case ERROR_BAD_UNIT: return ENXIO;
5173 case ERROR_NOT_READY: return EBUSY;
5174 case ERROR_BAD_COMMAND: return EIO;
5175 case ERROR_CRC: return EIO;
5176 case ERROR_BAD_LENGTH: return EIO;
5177 case ERROR_SEEK: return EIO;
5178 case ERROR_WRITE_FAULT: return EIO;
5179 case ERROR_READ_FAULT: return EIO;
5180 case ERROR_GEN_FAILURE: return EIO;
5181 case ERROR_SHARING_VIOLATION: return EACCES;
5182 case ERROR_LOCK_VIOLATION: return EACCES;
5183 case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
5184 case ERROR_HANDLE_DISK_FULL: return ENOSPC;
5185 case ERROR_NOT_SUPPORTED: return ENODEV;
5186 case ERROR_REM_NOT_LIST: return EBUSY;
5187 case ERROR_DUP_NAME: return EEXIST;
5188 case ERROR_BAD_NETPATH: return ENOENT;
5189 case ERROR_NETWORK_BUSY: return EBUSY;
5190 case ERROR_DEV_NOT_EXIST: return ENODEV;
5191 case ERROR_TOO_MANY_CMDS: return EAGAIN;
5192 case ERROR_ADAP_HDW_ERR: return EIO;
5193 case ERROR_BAD_NET_RESP: return EIO;
5194 case ERROR_UNEXP_NET_ERR: return EIO;
5195 case ERROR_NETNAME_DELETED: return ENOENT;
5196 case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
5197 case ERROR_BAD_DEV_TYPE: return ENODEV;
5198 case ERROR_BAD_NET_NAME: return ENOENT;
5199 case ERROR_TOO_MANY_NAMES: return ENFILE;
5200 case ERROR_TOO_MANY_SESS: return EIO;
5201 case ERROR_SHARING_PAUSED: return EAGAIN;
5202 case ERROR_REDIR_PAUSED: return EAGAIN;
5203 case ERROR_FILE_EXISTS: return EEXIST;
5204 case ERROR_CANNOT_MAKE: return ENOSPC;
5205 case ERROR_OUT_OF_STRUCTURES: return ENFILE;
5206 case ERROR_ALREADY_ASSIGNED: return EEXIST;
5207 case ERROR_INVALID_PASSWORD: return EPERM;
5208 case ERROR_NET_WRITE_FAULT: return EIO;
5209 case ERROR_NO_PROC_SLOTS: return EAGAIN;
5210 case ERROR_DISK_CHANGE: return EXDEV;
5211 case ERROR_BROKEN_PIPE: return EPIPE;
5212 case ERROR_OPEN_FAILED: return ENOENT;
5213 case ERROR_DISK_FULL: return ENOSPC;
5214 case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
5215 case ERROR_INVALID_TARGET_HANDLE: return EBADF;
5216 case ERROR_INVALID_NAME: return ENOENT;
5217 case ERROR_PROC_NOT_FOUND: return ESRCH;
5218 case ERROR_WAIT_NO_CHILDREN: return ECHILD;
5219 case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
5220 case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
5221 case ERROR_SEEK_ON_DEVICE: return ESPIPE;
5222 case ERROR_BUSY_DRIVE: return EAGAIN;
5223 case ERROR_DIR_NOT_EMPTY: return EEXIST;
5224 case ERROR_NOT_LOCKED: return EACCES;
5225 case ERROR_BAD_PATHNAME: return ENOENT;
5226 case ERROR_LOCK_FAILED: return EACCES;
5227 case ERROR_ALREADY_EXISTS: return EEXIST;
5228 case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
5229 case ERROR_BAD_PIPE: return EPIPE;
5230 case ERROR_PIPE_BUSY: return EAGAIN;
5231 case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
5232 case ERROR_DIRECTORY: return ENOTDIR;
5234 return EINVAL;
5237 static int JimPipe(fdtype pipefd[2])
5239 if (CreatePipe(&pipefd[0], &pipefd[1], NULL, 0)) {
5240 return 0;
5242 return -1;
5245 static fdtype JimDupFd(fdtype infd)
5247 fdtype dupfd;
5248 pidtype pid = GetCurrentProcess();
5250 if (DuplicateHandle(pid, infd, pid, &dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
5251 return dupfd;
5253 return JIM_BAD_FD;
5256 static int JimRewindFd(fdtype fd)
5258 return SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ? -1 : 0;
5261 #if 0
5262 static int JimReadFd(fdtype fd, char *buffer, size_t len)
5264 DWORD num;
5266 if (ReadFile(fd, buffer, len, &num, NULL)) {
5267 return num;
5269 if (GetLastError() == ERROR_HANDLE_EOF || GetLastError() == ERROR_BROKEN_PIPE) {
5270 return 0;
5272 return -1;
5274 #endif
5276 static FILE *JimFdOpenForRead(fdtype fd)
5278 return _fdopen(_open_osfhandle((int)fd, _O_RDONLY | _O_TEXT), "r");
5281 static fdtype JimFileno(FILE *fh)
5283 return (fdtype)_get_osfhandle(_fileno(fh));
5286 static fdtype JimOpenForRead(const char *filename)
5288 return CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
5289 JimStdSecAttrs(), OPEN_EXISTING, 0, NULL);
5292 static fdtype JimOpenForWrite(const char *filename, int append)
5294 return CreateFile(filename, append ? FILE_APPEND_DATA : GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
5295 JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, 0, (HANDLE) NULL);
5298 static FILE *JimFdOpenForWrite(fdtype fd)
5300 return _fdopen(_open_osfhandle((int)fd, _O_TEXT), "w");
5303 static pidtype JimWaitPid(pidtype pid, int *status, int nohang)
5305 DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
5306 if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
5308 return JIM_BAD_PID;
5310 GetExitCodeProcess(pid, &ret);
5311 *status = ret;
5312 CloseHandle(pid);
5313 return pid;
5316 static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
5318 char name[MAX_PATH];
5319 HANDLE handle;
5321 if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) {
5322 return JIM_BAD_FD;
5325 handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, JimStdSecAttrs(),
5326 CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
5327 NULL);
5329 if (handle == INVALID_HANDLE_VALUE) {
5330 goto error;
5333 if (contents != NULL) {
5335 FILE *fh = JimFdOpenForWrite(JimDupFd(handle));
5336 if (fh == NULL) {
5337 goto error;
5340 if (fwrite(contents, len, 1, fh) != 1) {
5341 fclose(fh);
5342 goto error;
5344 fseek(fh, 0, SEEK_SET);
5345 fclose(fh);
5347 return handle;
5349 error:
5350 Jim_SetResultErrno(interp, "failed to create temp file");
5351 CloseHandle(handle);
5352 DeleteFile(name);
5353 return JIM_BAD_FD;
5356 static int
5357 JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
5359 int i;
5360 static char extensions[][5] = {".exe", "", ".bat"};
5362 for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
5363 snprintf(fullPath, MAX_PATH, "%s%s", originalName, extensions[i]);
5365 if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
5366 continue;
5368 if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
5369 continue;
5371 return 0;
5374 return -1;
5377 static char **JimSaveEnv(char **env)
5379 return env;
5382 static void JimRestoreEnv(char **env)
5384 JimFreeEnv(env, Jim_GetEnviron());
5387 static Jim_Obj *
5388 JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
5390 char *start, *special;
5391 int quote, i;
5393 Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
5395 for (i = 0; argv[i]; i++) {
5396 if (i > 0) {
5397 Jim_AppendString(interp, strObj, " ", 1);
5400 if (argv[i][0] == '\0') {
5401 quote = 1;
5403 else {
5404 quote = 0;
5405 for (start = argv[i]; *start != '\0'; start++) {
5406 if (isspace(UCHAR(*start))) {
5407 quote = 1;
5408 break;
5412 if (quote) {
5413 Jim_AppendString(interp, strObj, "\"" , 1);
5416 start = argv[i];
5417 for (special = argv[i]; ; ) {
5418 if ((*special == '\\') && (special[1] == '\\' ||
5419 special[1] == '"' || (quote && special[1] == '\0'))) {
5420 Jim_AppendString(interp, strObj, start, special - start);
5421 start = special;
5422 while (1) {
5423 special++;
5424 if (*special == '"' || (quote && *special == '\0')) {
5426 Jim_AppendString(interp, strObj, start, special - start);
5427 break;
5429 if (*special != '\\') {
5430 break;
5433 Jim_AppendString(interp, strObj, start, special - start);
5434 start = special;
5436 if (*special == '"') {
5437 if (special == start) {
5438 Jim_AppendString(interp, strObj, "\"", 1);
5440 else {
5441 Jim_AppendString(interp, strObj, start, special - start);
5443 Jim_AppendString(interp, strObj, "\\\"", 2);
5444 start = special + 1;
5446 if (*special == '\0') {
5447 break;
5449 special++;
5451 Jim_AppendString(interp, strObj, start, special - start);
5452 if (quote) {
5453 Jim_AppendString(interp, strObj, "\"", 1);
5456 return strObj;
5459 static pidtype
5460 JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId)
5462 STARTUPINFO startInfo;
5463 PROCESS_INFORMATION procInfo;
5464 HANDLE hProcess, h;
5465 char execPath[MAX_PATH];
5466 pidtype pid = JIM_BAD_PID;
5467 Jim_Obj *cmdLineObj;
5469 if (JimWinFindExecutable(argv[0], execPath) < 0) {
5470 return JIM_BAD_PID;
5472 argv[0] = execPath;
5474 hProcess = GetCurrentProcess();
5475 cmdLineObj = JimWinBuildCommandLine(interp, argv);
5478 ZeroMemory(&startInfo, sizeof(startInfo));
5479 startInfo.cb = sizeof(startInfo);
5480 startInfo.dwFlags = STARTF_USESTDHANDLES;
5481 startInfo.hStdInput = INVALID_HANDLE_VALUE;
5482 startInfo.hStdOutput= INVALID_HANDLE_VALUE;
5483 startInfo.hStdError = INVALID_HANDLE_VALUE;
5485 if (inputId == JIM_BAD_FD) {
5486 if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) {
5487 CloseHandle(h);
5489 } else {
5490 DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput,
5491 0, TRUE, DUPLICATE_SAME_ACCESS);
5493 if (startInfo.hStdInput == JIM_BAD_FD) {
5494 goto end;
5497 if (outputId == JIM_BAD_FD) {
5498 startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
5499 JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
5500 } else {
5501 DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput,
5502 0, TRUE, DUPLICATE_SAME_ACCESS);
5504 if (startInfo.hStdOutput == JIM_BAD_FD) {
5505 goto end;
5508 if (errorId == JIM_BAD_FD) {
5510 startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
5511 JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
5512 } else {
5513 DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError,
5514 0, TRUE, DUPLICATE_SAME_ACCESS);
5516 if (startInfo.hStdError == JIM_BAD_FD) {
5517 goto end;
5520 if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
5521 0, env, NULL, &startInfo, &procInfo)) {
5522 goto end;
5526 WaitForInputIdle(procInfo.hProcess, 5000);
5527 CloseHandle(procInfo.hThread);
5529 pid = procInfo.hProcess;
5531 end:
5532 Jim_FreeNewObj(interp, cmdLineObj);
5533 if (startInfo.hStdInput != JIM_BAD_FD) {
5534 CloseHandle(startInfo.hStdInput);
5536 if (startInfo.hStdOutput != JIM_BAD_FD) {
5537 CloseHandle(startInfo.hStdOutput);
5539 if (startInfo.hStdError != JIM_BAD_FD) {
5540 CloseHandle(startInfo.hStdError);
5542 return pid;
5544 #else
5546 static int JimOpenForWrite(const char *filename, int append)
5548 return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
5551 static int JimRewindFd(int fd)
5553 return lseek(fd, 0L, SEEK_SET);
5556 static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
5558 int fd = Jim_MakeTempFile(interp, NULL);
5560 if (fd != JIM_BAD_FD) {
5561 unlink(Jim_String(Jim_GetResult(interp)));
5562 if (contents) {
5563 if (write(fd, contents, len) != len) {
5564 Jim_SetResultErrno(interp, "couldn't write temp file");
5565 close(fd);
5566 return -1;
5568 lseek(fd, 0L, SEEK_SET);
5571 return fd;
5574 static char **JimSaveEnv(char **env)
5576 char **saveenv = Jim_GetEnviron();
5577 Jim_SetEnviron(env);
5578 return saveenv;
5581 static void JimRestoreEnv(char **env)
5583 JimFreeEnv(Jim_GetEnviron(), env);
5584 Jim_SetEnviron(env);
5586 #endif
5587 #endif
5590 #ifndef _XOPEN_SOURCE
5591 #define _XOPEN_SOURCE 500
5592 #endif
5594 #include <stdlib.h>
5595 #include <string.h>
5596 #include <stdio.h>
5597 #include <time.h>
5600 #ifdef HAVE_SYS_TIME_H
5601 #include <sys/time.h>
5602 #endif
5604 static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5607 char buf[100];
5608 time_t t;
5609 long seconds;
5611 const char *format = "%a %b %d %H:%M:%S %Z %Y";
5613 if (argc == 2 || (argc == 3 && !Jim_CompareStringImmediate(interp, argv[1], "-format"))) {
5614 return -1;
5617 if (argc == 3) {
5618 format = Jim_String(argv[2]);
5621 if (Jim_GetLong(interp, argv[0], &seconds) != JIM_OK) {
5622 return JIM_ERR;
5624 t = seconds;
5626 if (strftime(buf, sizeof(buf), format, localtime(&t)) == 0) {
5627 Jim_SetResultString(interp, "format string too long", -1);
5628 return JIM_ERR;
5631 Jim_SetResultString(interp, buf, -1);
5633 return JIM_OK;
5636 #ifdef HAVE_STRPTIME
5637 static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5639 char *pt;
5640 struct tm tm;
5641 time_t now = time(0);
5643 if (!Jim_CompareStringImmediate(interp, argv[1], "-format")) {
5644 return -1;
5648 localtime_r(&now, &tm);
5650 pt = strptime(Jim_String(argv[0]), Jim_String(argv[2]), &tm);
5651 if (pt == 0 || *pt != 0) {
5652 Jim_SetResultString(interp, "Failed to parse time according to format", -1);
5653 return JIM_ERR;
5657 Jim_SetResultInt(interp, mktime(&tm));
5659 return JIM_OK;
5661 #endif
5663 static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5665 Jim_SetResultInt(interp, time(NULL));
5667 return JIM_OK;
5670 static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5672 struct timeval tv;
5674 gettimeofday(&tv, NULL);
5676 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec);
5678 return JIM_OK;
5681 static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5683 struct timeval tv;
5685 gettimeofday(&tv, NULL);
5687 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000 + tv.tv_usec / 1000);
5689 return JIM_OK;
5692 static const jim_subcmd_type clock_command_table[] = {
5693 { "seconds",
5694 NULL,
5695 clock_cmd_seconds,
5700 { "clicks",
5701 NULL,
5702 clock_cmd_micros,
5707 { "microseconds",
5708 NULL,
5709 clock_cmd_micros,
5714 { "milliseconds",
5715 NULL,
5716 clock_cmd_millis,
5721 { "format",
5722 "seconds ?-format format?",
5723 clock_cmd_format,
5728 #ifdef HAVE_STRPTIME
5729 { "scan",
5730 "str -format format",
5731 clock_cmd_scan,
5736 #endif
5737 { NULL }
5740 int Jim_clockInit(Jim_Interp *interp)
5742 if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG))
5743 return JIM_ERR;
5745 Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
5746 return JIM_OK;
5749 #include <limits.h>
5750 #include <stdlib.h>
5751 #include <string.h>
5752 #include <stdio.h>
5753 #include <errno.h>
5756 static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5759 Jim_SetResultInt(interp, Jim_GetVariable(interp, argv[0], 0) != 0);
5760 return JIM_OK;
5763 static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5765 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5766 Jim_Obj *patternObj;
5768 if (!objPtr) {
5769 return JIM_OK;
5772 patternObj = (argc == 1) ? NULL : argv[1];
5775 if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) {
5776 if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) {
5778 Jim_SetResult(interp, objPtr);
5779 return JIM_OK;
5784 return Jim_DictValues(interp, objPtr, patternObj);
5787 static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5789 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5791 if (!objPtr) {
5792 return JIM_OK;
5795 return Jim_DictKeys(interp, objPtr, argc == 1 ? NULL : argv[1]);
5798 static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5800 int i;
5801 int len;
5802 Jim_Obj *resultObj;
5803 Jim_Obj *objPtr;
5804 Jim_Obj **dictValuesObj;
5806 if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
5808 Jim_UnsetVariable(interp, argv[0], JIM_NONE);
5809 return JIM_OK;
5812 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5814 if (objPtr == NULL) {
5816 return JIM_OK;
5819 if (Jim_DictPairs(interp, objPtr, &dictValuesObj, &len) != JIM_OK) {
5820 return JIM_ERR;
5824 resultObj = Jim_NewDictObj(interp, NULL, 0);
5826 for (i = 0; i < len; i += 2) {
5827 if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
5828 Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
5831 Jim_Free(dictValuesObj);
5833 Jim_SetVariable(interp, argv[0], resultObj);
5834 return JIM_OK;
5837 static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5839 Jim_Obj *objPtr;
5840 int len = 0;
5843 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5844 if (objPtr) {
5845 len = Jim_DictSize(interp, objPtr);
5846 if (len < 0) {
5847 return JIM_ERR;
5851 Jim_SetResultInt(interp, len);
5853 return JIM_OK;
5856 static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5858 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5859 if (objPtr) {
5860 return Jim_DictInfo(interp, objPtr);
5862 Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL);
5863 return JIM_ERR;
5866 static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5868 int i;
5869 int len;
5870 Jim_Obj *listObj = argv[1];
5871 Jim_Obj *dictObj;
5873 len = Jim_ListLength(interp, listObj);
5874 if (len % 2) {
5875 Jim_SetResultString(interp, "list must have an even number of elements", -1);
5876 return JIM_ERR;
5879 dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
5880 if (!dictObj) {
5882 return Jim_SetVariable(interp, argv[0], listObj);
5884 else if (Jim_DictSize(interp, dictObj) < 0) {
5885 return JIM_ERR;
5888 if (Jim_IsShared(dictObj)) {
5889 dictObj = Jim_DuplicateObj(interp, dictObj);
5892 for (i = 0; i < len; i += 2) {
5893 Jim_Obj *nameObj;
5894 Jim_Obj *valueObj;
5896 Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE);
5897 Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE);
5899 Jim_DictAddElement(interp, dictObj, nameObj, valueObj);
5901 return Jim_SetVariable(interp, argv[0], dictObj);
5904 static const jim_subcmd_type array_command_table[] = {
5905 { "exists",
5906 "arrayName",
5907 array_cmd_exists,
5912 { "get",
5913 "arrayName ?pattern?",
5914 array_cmd_get,
5919 { "names",
5920 "arrayName ?pattern?",
5921 array_cmd_names,
5926 { "set",
5927 "arrayName list",
5928 array_cmd_set,
5933 { "size",
5934 "arrayName",
5935 array_cmd_size,
5940 { "stat",
5941 "arrayName",
5942 array_cmd_stat,
5947 { "unset",
5948 "arrayName ?pattern?",
5949 array_cmd_unset,
5954 { NULL
5958 int Jim_arrayInit(Jim_Interp *interp)
5960 if (Jim_PackageProvide(interp, "array", "1.0", JIM_ERRMSG))
5961 return JIM_ERR;
5963 Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
5964 return JIM_OK;
5966 int Jim_InitStaticExtensions(Jim_Interp *interp)
5968 extern int Jim_bootstrapInit(Jim_Interp *);
5969 extern int Jim_aioInit(Jim_Interp *);
5970 extern int Jim_readdirInit(Jim_Interp *);
5971 extern int Jim_regexpInit(Jim_Interp *);
5972 extern int Jim_fileInit(Jim_Interp *);
5973 extern int Jim_globInit(Jim_Interp *);
5974 extern int Jim_execInit(Jim_Interp *);
5975 extern int Jim_clockInit(Jim_Interp *);
5976 extern int Jim_arrayInit(Jim_Interp *);
5977 extern int Jim_stdlibInit(Jim_Interp *);
5978 extern int Jim_tclcompatInit(Jim_Interp *);
5979 Jim_bootstrapInit(interp);
5980 Jim_aioInit(interp);
5981 Jim_readdirInit(interp);
5982 Jim_regexpInit(interp);
5983 Jim_fileInit(interp);
5984 Jim_globInit(interp);
5985 Jim_execInit(interp);
5986 Jim_clockInit(interp);
5987 Jim_arrayInit(interp);
5988 Jim_stdlibInit(interp);
5989 Jim_tclcompatInit(interp);
5990 return JIM_OK;
5992 #define JIM_OPTIMIZATION
5994 #include <stdio.h>
5995 #include <stdlib.h>
5997 #include <string.h>
5998 #include <stdarg.h>
5999 #include <ctype.h>
6000 #include <limits.h>
6001 #include <assert.h>
6002 #include <errno.h>
6003 #include <time.h>
6004 #include <setjmp.h>
6007 #ifdef HAVE_SYS_TIME_H
6008 #include <sys/time.h>
6009 #endif
6010 #ifdef HAVE_BACKTRACE
6011 #include <execinfo.h>
6012 #endif
6013 #ifdef HAVE_CRT_EXTERNS_H
6014 #include <crt_externs.h>
6015 #endif
6018 #include <math.h>
6024 #ifndef TCL_LIBRARY
6025 #define TCL_LIBRARY "."
6026 #endif
6027 #ifndef TCL_PLATFORM_OS
6028 #define TCL_PLATFORM_OS "unknown"
6029 #endif
6030 #ifndef TCL_PLATFORM_PLATFORM
6031 #define TCL_PLATFORM_PLATFORM "unknown"
6032 #endif
6033 #ifndef TCL_PLATFORM_PATH_SEPARATOR
6034 #define TCL_PLATFORM_PATH_SEPARATOR ":"
6035 #endif
6043 #ifdef JIM_MAINTAINER
6044 #define JIM_DEBUG_COMMAND
6045 #define JIM_DEBUG_PANIC
6046 #endif
6050 #define JIM_INTEGER_SPACE 24
6052 const char *jim_tt_name(int type);
6054 #ifdef JIM_DEBUG_PANIC
6055 static void JimPanicDump(int fail_condition, const char *fmt, ...);
6056 #define JimPanic(X) JimPanicDump X
6057 #else
6058 #define JimPanic(X)
6059 #endif
6062 static char JimEmptyStringRep[] = "";
6064 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
6065 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
6066 int flags);
6067 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
6068 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
6069 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
6070 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len);
6071 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
6072 const char *prefix, const char *const *tablePtr, const char *name);
6073 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
6074 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
6075 static int JimSign(jim_wide w);
6076 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr);
6077 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
6078 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
6082 #define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
6084 #define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none")
6086 static int utf8_tounicode_case(const char *s, int *uc, int upper)
6088 int l = utf8_tounicode(s, uc);
6089 if (upper) {
6090 *uc = utf8_upper(*uc);
6092 return l;
6096 #define JIM_CHARSET_SCAN 2
6097 #define JIM_CHARSET_GLOB 0
6099 static const char *JimCharsetMatch(const char *pattern, int c, int flags)
6101 int not = 0;
6102 int pchar;
6103 int match = 0;
6104 int nocase = 0;
6106 if (flags & JIM_NOCASE) {
6107 nocase++;
6108 c = utf8_upper(c);
6111 if (flags & JIM_CHARSET_SCAN) {
6112 if (*pattern == '^') {
6113 not++;
6114 pattern++;
6118 if (*pattern == ']') {
6119 goto first;
6123 while (*pattern && *pattern != ']') {
6125 if (pattern[0] == '\\') {
6126 first:
6127 pattern += utf8_tounicode_case(pattern, &pchar, nocase);
6129 else {
6131 int start;
6132 int end;
6134 pattern += utf8_tounicode_case(pattern, &start, nocase);
6135 if (pattern[0] == '-' && pattern[1]) {
6137 pattern += utf8_tounicode(pattern, &pchar);
6138 pattern += utf8_tounicode_case(pattern, &end, nocase);
6141 if ((c >= start && c <= end) || (c >= end && c <= start)) {
6142 match = 1;
6144 continue;
6146 pchar = start;
6149 if (pchar == c) {
6150 match = 1;
6153 if (not) {
6154 match = !match;
6157 return match ? pattern : NULL;
6162 static int JimGlobMatch(const char *pattern, const char *string, int nocase)
6164 int c;
6165 int pchar;
6166 while (*pattern) {
6167 switch (pattern[0]) {
6168 case '*':
6169 while (pattern[1] == '*') {
6170 pattern++;
6172 pattern++;
6173 if (!pattern[0]) {
6174 return 1;
6176 while (*string) {
6178 if (JimGlobMatch(pattern, string, nocase))
6179 return 1;
6180 string += utf8_tounicode(string, &c);
6182 return 0;
6184 case '?':
6185 string += utf8_tounicode(string, &c);
6186 break;
6188 case '[': {
6189 string += utf8_tounicode(string, &c);
6190 pattern = JimCharsetMatch(pattern + 1, c, nocase ? JIM_NOCASE : 0);
6191 if (!pattern) {
6192 return 0;
6194 if (!*pattern) {
6196 continue;
6198 break;
6200 case '\\':
6201 if (pattern[1]) {
6202 pattern++;
6205 default:
6206 string += utf8_tounicode_case(string, &c, nocase);
6207 utf8_tounicode_case(pattern, &pchar, nocase);
6208 if (pchar != c) {
6209 return 0;
6211 break;
6213 pattern += utf8_tounicode_case(pattern, &pchar, nocase);
6214 if (!*string) {
6215 while (*pattern == '*') {
6216 pattern++;
6218 break;
6221 if (!*pattern && !*string) {
6222 return 1;
6224 return 0;
6227 static int JimStringCompare(const char *s1, int l1, const char *s2, int l2)
6229 if (l1 < l2) {
6230 return memcmp(s1, s2, l1) <= 0 ? -1 : 1;
6232 else if (l2 < l1) {
6233 return memcmp(s1, s2, l2) >= 0 ? 1 : -1;
6235 else {
6236 return JimSign(memcmp(s1, s2, l1));
6240 static int JimStringCompareLen(const char *s1, const char *s2, int maxchars, int nocase)
6242 while (*s1 && *s2 && maxchars) {
6243 int c1, c2;
6244 s1 += utf8_tounicode_case(s1, &c1, nocase);
6245 s2 += utf8_tounicode_case(s2, &c2, nocase);
6246 if (c1 != c2) {
6247 return JimSign(c1 - c2);
6249 maxchars--;
6251 if (!maxchars) {
6252 return 0;
6255 if (*s1) {
6256 return 1;
6258 if (*s2) {
6259 return -1;
6261 return 0;
6264 static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
6266 int i;
6267 int l1bytelen;
6269 if (!l1 || !l2 || l1 > l2) {
6270 return -1;
6272 if (idx < 0)
6273 idx = 0;
6274 s2 += utf8_index(s2, idx);
6276 l1bytelen = utf8_index(s1, l1);
6278 for (i = idx; i <= l2 - l1; i++) {
6279 int c;
6280 if (memcmp(s2, s1, l1bytelen) == 0) {
6281 return i;
6283 s2 += utf8_tounicode(s2, &c);
6285 return -1;
6288 static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
6290 const char *p;
6292 if (!l1 || !l2 || l1 > l2)
6293 return -1;
6296 for (p = s2 + l2 - 1; p != s2 - 1; p--) {
6297 if (*p == *s1 && memcmp(s1, p, l1) == 0) {
6298 return p - s2;
6301 return -1;
6304 #ifdef JIM_UTF8
6305 static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2)
6307 int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2));
6308 if (n > 0) {
6309 n = utf8_strlen(s2, n);
6311 return n;
6313 #endif
6315 static int JimCheckConversion(const char *str, const char *endptr)
6317 if (str[0] == '\0' || str == endptr) {
6318 return JIM_ERR;
6321 if (endptr[0] != '\0') {
6322 while (*endptr) {
6323 if (!isspace(UCHAR(*endptr))) {
6324 return JIM_ERR;
6326 endptr++;
6329 return JIM_OK;
6332 static int JimNumberBase(const char *str, int *base, int *sign)
6334 int i = 0;
6336 *base = 10;
6338 while (isspace(UCHAR(str[i]))) {
6339 i++;
6342 if (str[i] == '-') {
6343 *sign = -1;
6344 i++;
6346 else {
6347 if (str[i] == '+') {
6348 i++;
6350 *sign = 1;
6353 if (str[i] != '0') {
6355 return 0;
6359 switch (str[i + 1]) {
6360 case 'x': case 'X': *base = 16; break;
6361 case 'o': case 'O': *base = 8; break;
6362 case 'b': case 'B': *base = 2; break;
6363 default: return 0;
6365 i += 2;
6367 if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
6369 return i;
6372 *base = 10;
6373 return 0;
6376 static long jim_strtol(const char *str, char **endptr)
6378 int sign;
6379 int base;
6380 int i = JimNumberBase(str, &base, &sign);
6382 if (base != 10) {
6383 long value = strtol(str + i, endptr, base);
6384 if (endptr == NULL || *endptr != str + i) {
6385 return value * sign;
6390 return strtol(str, endptr, 10);
6394 static jim_wide jim_strtoull(const char *str, char **endptr)
6396 #ifdef HAVE_LONG_LONG
6397 int sign;
6398 int base;
6399 int i = JimNumberBase(str, &base, &sign);
6401 if (base != 10) {
6402 jim_wide value = strtoull(str + i, endptr, base);
6403 if (endptr == NULL || *endptr != str + i) {
6404 return value * sign;
6409 return strtoull(str, endptr, 10);
6410 #else
6411 return (unsigned long)jim_strtol(str, endptr);
6412 #endif
6415 int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
6417 char *endptr;
6419 if (base) {
6420 *widePtr = strtoull(str, &endptr, base);
6422 else {
6423 *widePtr = jim_strtoull(str, &endptr);
6426 return JimCheckConversion(str, endptr);
6429 int Jim_StringToDouble(const char *str, double *doublePtr)
6431 char *endptr;
6434 errno = 0;
6436 *doublePtr = strtod(str, &endptr);
6438 return JimCheckConversion(str, endptr);
6441 static jim_wide JimPowWide(jim_wide b, jim_wide e)
6443 jim_wide i, res = 1;
6445 if ((b == 0 && e != 0) || (e < 0))
6446 return 0;
6447 for (i = 0; i < e; i++) {
6448 res *= b;
6450 return res;
6453 #ifdef JIM_DEBUG_PANIC
6454 static void JimPanicDump(int condition, const char *fmt, ...)
6456 va_list ap;
6458 if (!condition) {
6459 return;
6462 va_start(ap, fmt);
6464 fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
6465 vfprintf(stderr, fmt, ap);
6466 fprintf(stderr, "\n\n");
6467 va_end(ap);
6469 #ifdef HAVE_BACKTRACE
6471 void *array[40];
6472 int size, i;
6473 char **strings;
6475 size = backtrace(array, 40);
6476 strings = backtrace_symbols(array, size);
6477 for (i = 0; i < size; i++)
6478 fprintf(stderr, "[backtrace] %s\n", strings[i]);
6479 fprintf(stderr, "[backtrace] Include the above lines and the output\n");
6480 fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n");
6482 #endif
6484 exit(1);
6486 #endif
6489 void *Jim_Alloc(int size)
6491 return size ? malloc(size) : NULL;
6494 void Jim_Free(void *ptr)
6496 free(ptr);
6499 void *Jim_Realloc(void *ptr, int size)
6501 return realloc(ptr, size);
6504 char *Jim_StrDup(const char *s)
6506 return strdup(s);
6509 char *Jim_StrDupLen(const char *s, int l)
6511 char *copy = Jim_Alloc(l + 1);
6513 memcpy(copy, s, l + 1);
6514 copy[l] = 0;
6515 return copy;
6520 static jim_wide JimClock(void)
6522 struct timeval tv;
6524 gettimeofday(&tv, NULL);
6525 return (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec;
6530 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
6531 static unsigned int JimHashTableNextPower(unsigned int size);
6532 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
6537 unsigned int Jim_IntHashFunction(unsigned int key)
6539 key += ~(key << 15);
6540 key ^= (key >> 10);
6541 key += (key << 3);
6542 key ^= (key >> 6);
6543 key += ~(key << 11);
6544 key ^= (key >> 16);
6545 return key;
6548 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
6550 unsigned int h = 0;
6552 while (len--)
6553 h += (h << 3) + *buf++;
6554 return h;
6560 static void JimResetHashTable(Jim_HashTable *ht)
6562 ht->table = NULL;
6563 ht->size = 0;
6564 ht->sizemask = 0;
6565 ht->used = 0;
6566 ht->collisions = 0;
6567 #ifdef JIM_RANDOMISE_HASH
6568 ht->uniq = (rand() ^ time(NULL) ^ clock());
6569 #else
6570 ht->uniq = 0;
6571 #endif
6574 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
6576 iter->ht = ht;
6577 iter->index = -1;
6578 iter->entry = NULL;
6579 iter->nextEntry = NULL;
6583 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
6585 JimResetHashTable(ht);
6586 ht->type = type;
6587 ht->privdata = privDataPtr;
6588 return JIM_OK;
6591 void Jim_ResizeHashTable(Jim_HashTable *ht)
6593 int minimal = ht->used;
6595 if (minimal < JIM_HT_INITIAL_SIZE)
6596 minimal = JIM_HT_INITIAL_SIZE;
6597 Jim_ExpandHashTable(ht, minimal);
6601 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
6603 Jim_HashTable n;
6604 unsigned int realsize = JimHashTableNextPower(size), i;
6606 if (size <= ht->used)
6607 return;
6609 Jim_InitHashTable(&n, ht->type, ht->privdata);
6610 n.size = realsize;
6611 n.sizemask = realsize - 1;
6612 n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
6614 n.uniq = ht->uniq;
6617 memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
6619 n.used = ht->used;
6620 for (i = 0; ht->used > 0; i++) {
6621 Jim_HashEntry *he, *nextHe;
6623 if (ht->table[i] == NULL)
6624 continue;
6627 he = ht->table[i];
6628 while (he) {
6629 unsigned int h;
6631 nextHe = he->next;
6633 h = Jim_HashKey(ht, he->key) & n.sizemask;
6634 he->next = n.table[h];
6635 n.table[h] = he;
6636 ht->used--;
6638 he = nextHe;
6641 assert(ht->used == 0);
6642 Jim_Free(ht->table);
6645 *ht = n;
6649 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
6651 Jim_HashEntry *entry;
6653 entry = JimInsertHashEntry(ht, key, 0);
6654 if (entry == NULL)
6655 return JIM_ERR;
6658 Jim_SetHashKey(ht, entry, key);
6659 Jim_SetHashVal(ht, entry, val);
6660 return JIM_OK;
6664 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
6666 int existed;
6667 Jim_HashEntry *entry;
6669 entry = JimInsertHashEntry(ht, key, 1);
6670 if (entry->key) {
6671 if (ht->type->valDestructor && ht->type->valDup) {
6672 void *newval = ht->type->valDup(ht->privdata, val);
6673 ht->type->valDestructor(ht->privdata, entry->u.val);
6674 entry->u.val = newval;
6676 else {
6677 Jim_FreeEntryVal(ht, entry);
6678 Jim_SetHashVal(ht, entry, val);
6680 existed = 1;
6682 else {
6684 Jim_SetHashKey(ht, entry, key);
6685 Jim_SetHashVal(ht, entry, val);
6686 existed = 0;
6689 return existed;
6693 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
6695 unsigned int h;
6696 Jim_HashEntry *he, *prevHe;
6698 if (ht->used == 0)
6699 return JIM_ERR;
6700 h = Jim_HashKey(ht, key) & ht->sizemask;
6701 he = ht->table[h];
6703 prevHe = NULL;
6704 while (he) {
6705 if (Jim_CompareHashKeys(ht, key, he->key)) {
6707 if (prevHe)
6708 prevHe->next = he->next;
6709 else
6710 ht->table[h] = he->next;
6711 Jim_FreeEntryKey(ht, he);
6712 Jim_FreeEntryVal(ht, he);
6713 Jim_Free(he);
6714 ht->used--;
6715 return JIM_OK;
6717 prevHe = he;
6718 he = he->next;
6720 return JIM_ERR;
6724 int Jim_FreeHashTable(Jim_HashTable *ht)
6726 unsigned int i;
6729 for (i = 0; ht->used > 0; i++) {
6730 Jim_HashEntry *he, *nextHe;
6732 if ((he = ht->table[i]) == NULL)
6733 continue;
6734 while (he) {
6735 nextHe = he->next;
6736 Jim_FreeEntryKey(ht, he);
6737 Jim_FreeEntryVal(ht, he);
6738 Jim_Free(he);
6739 ht->used--;
6740 he = nextHe;
6744 Jim_Free(ht->table);
6746 JimResetHashTable(ht);
6747 return JIM_OK;
6750 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
6752 Jim_HashEntry *he;
6753 unsigned int h;
6755 if (ht->used == 0)
6756 return NULL;
6757 h = Jim_HashKey(ht, key) & ht->sizemask;
6758 he = ht->table[h];
6759 while (he) {
6760 if (Jim_CompareHashKeys(ht, key, he->key))
6761 return he;
6762 he = he->next;
6764 return NULL;
6767 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
6769 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
6770 JimInitHashTableIterator(ht, iter);
6771 return iter;
6774 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
6776 while (1) {
6777 if (iter->entry == NULL) {
6778 iter->index++;
6779 if (iter->index >= (signed)iter->ht->size)
6780 break;
6781 iter->entry = iter->ht->table[iter->index];
6783 else {
6784 iter->entry = iter->nextEntry;
6786 if (iter->entry) {
6787 iter->nextEntry = iter->entry->next;
6788 return iter->entry;
6791 return NULL;
6797 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
6799 if (ht->size == 0)
6800 Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
6801 if (ht->size == ht->used)
6802 Jim_ExpandHashTable(ht, ht->size * 2);
6806 static unsigned int JimHashTableNextPower(unsigned int size)
6808 unsigned int i = JIM_HT_INITIAL_SIZE;
6810 if (size >= 2147483648U)
6811 return 2147483648U;
6812 while (1) {
6813 if (i >= size)
6814 return i;
6815 i *= 2;
6819 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
6821 unsigned int h;
6822 Jim_HashEntry *he;
6825 JimExpandHashTableIfNeeded(ht);
6828 h = Jim_HashKey(ht, key) & ht->sizemask;
6830 he = ht->table[h];
6831 while (he) {
6832 if (Jim_CompareHashKeys(ht, key, he->key))
6833 return replace ? he : NULL;
6834 he = he->next;
6838 he = Jim_Alloc(sizeof(*he));
6839 he->next = ht->table[h];
6840 ht->table[h] = he;
6841 ht->used++;
6842 he->key = NULL;
6844 return he;
6849 static unsigned int JimStringCopyHTHashFunction(const void *key)
6851 return Jim_GenHashFunction(key, strlen(key));
6854 static void *JimStringCopyHTDup(void *privdata, const void *key)
6856 return Jim_StrDup(key);
6859 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
6861 return strcmp(key1, key2) == 0;
6864 static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
6866 Jim_Free(key);
6869 static const Jim_HashTableType JimPackageHashTableType = {
6870 JimStringCopyHTHashFunction,
6871 JimStringCopyHTDup,
6872 NULL,
6873 JimStringCopyHTKeyCompare,
6874 JimStringCopyHTKeyDestructor,
6875 NULL
6878 typedef struct AssocDataValue
6880 Jim_InterpDeleteProc *delProc;
6881 void *data;
6882 } AssocDataValue;
6884 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
6886 AssocDataValue *assocPtr = (AssocDataValue *) data;
6888 if (assocPtr->delProc != NULL)
6889 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
6890 Jim_Free(data);
6893 static const Jim_HashTableType JimAssocDataHashTableType = {
6894 JimStringCopyHTHashFunction,
6895 JimStringCopyHTDup,
6896 NULL,
6897 JimStringCopyHTKeyCompare,
6898 JimStringCopyHTKeyDestructor,
6899 JimAssocDataHashTableValueDestructor
6902 void Jim_InitStack(Jim_Stack *stack)
6904 stack->len = 0;
6905 stack->maxlen = 0;
6906 stack->vector = NULL;
6909 void Jim_FreeStack(Jim_Stack *stack)
6911 Jim_Free(stack->vector);
6914 int Jim_StackLen(Jim_Stack *stack)
6916 return stack->len;
6919 void Jim_StackPush(Jim_Stack *stack, void *element)
6921 int neededLen = stack->len + 1;
6923 if (neededLen > stack->maxlen) {
6924 stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
6925 stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
6927 stack->vector[stack->len] = element;
6928 stack->len++;
6931 void *Jim_StackPop(Jim_Stack *stack)
6933 if (stack->len == 0)
6934 return NULL;
6935 stack->len--;
6936 return stack->vector[stack->len];
6939 void *Jim_StackPeek(Jim_Stack *stack)
6941 if (stack->len == 0)
6942 return NULL;
6943 return stack->vector[stack->len - 1];
6946 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
6948 int i;
6950 for (i = 0; i < stack->len; i++)
6951 freeFunc(stack->vector[i]);
6956 #define JIM_TT_NONE 0
6957 #define JIM_TT_STR 1
6958 #define JIM_TT_ESC 2
6959 #define JIM_TT_VAR 3
6960 #define JIM_TT_DICTSUGAR 4
6961 #define JIM_TT_CMD 5
6963 #define JIM_TT_SEP 6
6964 #define JIM_TT_EOL 7
6965 #define JIM_TT_EOF 8
6967 #define JIM_TT_LINE 9
6968 #define JIM_TT_WORD 10
6971 #define JIM_TT_SUBEXPR_START 11
6972 #define JIM_TT_SUBEXPR_END 12
6973 #define JIM_TT_SUBEXPR_COMMA 13
6974 #define JIM_TT_EXPR_INT 14
6975 #define JIM_TT_EXPR_DOUBLE 15
6977 #define JIM_TT_EXPRSUGAR 16
6980 #define JIM_TT_EXPR_OP 20
6982 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
6984 struct JimParseMissing {
6985 int ch;
6986 int line;
6989 struct JimParserCtx
6991 const char *p;
6992 int len;
6993 int linenr;
6994 const char *tstart;
6995 const char *tend;
6996 int tline;
6997 int tt;
6998 int eof;
6999 int inquote;
7000 int comment;
7001 struct JimParseMissing missing;
7004 static int JimParseScript(struct JimParserCtx *pc);
7005 static int JimParseSep(struct JimParserCtx *pc);
7006 static int JimParseEol(struct JimParserCtx *pc);
7007 static int JimParseCmd(struct JimParserCtx *pc);
7008 static int JimParseQuote(struct JimParserCtx *pc);
7009 static int JimParseVar(struct JimParserCtx *pc);
7010 static int JimParseBrace(struct JimParserCtx *pc);
7011 static int JimParseStr(struct JimParserCtx *pc);
7012 static int JimParseComment(struct JimParserCtx *pc);
7013 static void JimParseSubCmd(struct JimParserCtx *pc);
7014 static int JimParseSubQuote(struct JimParserCtx *pc);
7015 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
7017 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
7019 pc->p = prg;
7020 pc->len = len;
7021 pc->tstart = NULL;
7022 pc->tend = NULL;
7023 pc->tline = 0;
7024 pc->tt = JIM_TT_NONE;
7025 pc->eof = 0;
7026 pc->inquote = 0;
7027 pc->linenr = linenr;
7028 pc->comment = 1;
7029 pc->missing.ch = ' ';
7030 pc->missing.line = linenr;
7033 static int JimParseScript(struct JimParserCtx *pc)
7035 while (1) {
7036 if (!pc->len) {
7037 pc->tstart = pc->p;
7038 pc->tend = pc->p - 1;
7039 pc->tline = pc->linenr;
7040 pc->tt = JIM_TT_EOL;
7041 pc->eof = 1;
7042 return JIM_OK;
7044 switch (*(pc->p)) {
7045 case '\\':
7046 if (*(pc->p + 1) == '\n' && !pc->inquote) {
7047 return JimParseSep(pc);
7049 pc->comment = 0;
7050 return JimParseStr(pc);
7051 case ' ':
7052 case '\t':
7053 case '\r':
7054 case '\f':
7055 if (!pc->inquote)
7056 return JimParseSep(pc);
7057 pc->comment = 0;
7058 return JimParseStr(pc);
7059 case '\n':
7060 case ';':
7061 pc->comment = 1;
7062 if (!pc->inquote)
7063 return JimParseEol(pc);
7064 return JimParseStr(pc);
7065 case '[':
7066 pc->comment = 0;
7067 return JimParseCmd(pc);
7068 case '$':
7069 pc->comment = 0;
7070 if (JimParseVar(pc) == JIM_ERR) {
7072 pc->tstart = pc->tend = pc->p++;
7073 pc->len--;
7074 pc->tt = JIM_TT_ESC;
7076 return JIM_OK;
7077 case '#':
7078 if (pc->comment) {
7079 JimParseComment(pc);
7080 continue;
7082 return JimParseStr(pc);
7083 default:
7084 pc->comment = 0;
7085 return JimParseStr(pc);
7087 return JIM_OK;
7091 static int JimParseSep(struct JimParserCtx *pc)
7093 pc->tstart = pc->p;
7094 pc->tline = pc->linenr;
7095 while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
7096 if (*pc->p == '\n') {
7097 break;
7099 if (*pc->p == '\\') {
7100 pc->p++;
7101 pc->len--;
7102 pc->linenr++;
7104 pc->p++;
7105 pc->len--;
7107 pc->tend = pc->p - 1;
7108 pc->tt = JIM_TT_SEP;
7109 return JIM_OK;
7112 static int JimParseEol(struct JimParserCtx *pc)
7114 pc->tstart = pc->p;
7115 pc->tline = pc->linenr;
7116 while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
7117 if (*pc->p == '\n')
7118 pc->linenr++;
7119 pc->p++;
7120 pc->len--;
7122 pc->tend = pc->p - 1;
7123 pc->tt = JIM_TT_EOL;
7124 return JIM_OK;
7128 static void JimParseSubBrace(struct JimParserCtx *pc)
7130 int level = 1;
7133 pc->p++;
7134 pc->len--;
7135 while (pc->len) {
7136 switch (*pc->p) {
7137 case '\\':
7138 if (pc->len > 1) {
7139 if (*++pc->p == '\n') {
7140 pc->linenr++;
7142 pc->len--;
7144 break;
7146 case '{':
7147 level++;
7148 break;
7150 case '}':
7151 if (--level == 0) {
7152 pc->tend = pc->p - 1;
7153 pc->p++;
7154 pc->len--;
7155 return;
7157 break;
7159 case '\n':
7160 pc->linenr++;
7161 break;
7163 pc->p++;
7164 pc->len--;
7166 pc->missing.ch = '{';
7167 pc->missing.line = pc->tline;
7168 pc->tend = pc->p - 1;
7171 static int JimParseSubQuote(struct JimParserCtx *pc)
7173 int tt = JIM_TT_STR;
7174 int line = pc->tline;
7177 pc->p++;
7178 pc->len--;
7179 while (pc->len) {
7180 switch (*pc->p) {
7181 case '\\':
7182 if (pc->len > 1) {
7183 if (*++pc->p == '\n') {
7184 pc->linenr++;
7186 pc->len--;
7187 tt = JIM_TT_ESC;
7189 break;
7191 case '"':
7192 pc->tend = pc->p - 1;
7193 pc->p++;
7194 pc->len--;
7195 return tt;
7197 case '[':
7198 JimParseSubCmd(pc);
7199 tt = JIM_TT_ESC;
7200 continue;
7202 case '\n':
7203 pc->linenr++;
7204 break;
7206 case '$':
7207 tt = JIM_TT_ESC;
7208 break;
7210 pc->p++;
7211 pc->len--;
7213 pc->missing.ch = '"';
7214 pc->missing.line = line;
7215 pc->tend = pc->p - 1;
7216 return tt;
7219 static void JimParseSubCmd(struct JimParserCtx *pc)
7221 int level = 1;
7222 int startofword = 1;
7223 int line = pc->tline;
7226 pc->p++;
7227 pc->len--;
7228 while (pc->len) {
7229 switch (*pc->p) {
7230 case '\\':
7231 if (pc->len > 1) {
7232 if (*++pc->p == '\n') {
7233 pc->linenr++;
7235 pc->len--;
7237 break;
7239 case '[':
7240 level++;
7241 break;
7243 case ']':
7244 if (--level == 0) {
7245 pc->tend = pc->p - 1;
7246 pc->p++;
7247 pc->len--;
7248 return;
7250 break;
7252 case '"':
7253 if (startofword) {
7254 JimParseSubQuote(pc);
7255 continue;
7257 break;
7259 case '{':
7260 JimParseSubBrace(pc);
7261 startofword = 0;
7262 continue;
7264 case '\n':
7265 pc->linenr++;
7266 break;
7268 startofword = isspace(UCHAR(*pc->p));
7269 pc->p++;
7270 pc->len--;
7272 pc->missing.ch = '[';
7273 pc->missing.line = line;
7274 pc->tend = pc->p - 1;
7277 static int JimParseBrace(struct JimParserCtx *pc)
7279 pc->tstart = pc->p + 1;
7280 pc->tline = pc->linenr;
7281 pc->tt = JIM_TT_STR;
7282 JimParseSubBrace(pc);
7283 return JIM_OK;
7286 static int JimParseCmd(struct JimParserCtx *pc)
7288 pc->tstart = pc->p + 1;
7289 pc->tline = pc->linenr;
7290 pc->tt = JIM_TT_CMD;
7291 JimParseSubCmd(pc);
7292 return JIM_OK;
7295 static int JimParseQuote(struct JimParserCtx *pc)
7297 pc->tstart = pc->p + 1;
7298 pc->tline = pc->linenr;
7299 pc->tt = JimParseSubQuote(pc);
7300 return JIM_OK;
7303 static int JimParseVar(struct JimParserCtx *pc)
7306 pc->p++;
7307 pc->len--;
7309 #ifdef EXPRSUGAR_BRACKET
7310 if (*pc->p == '[') {
7312 JimParseCmd(pc);
7313 pc->tt = JIM_TT_EXPRSUGAR;
7314 return JIM_OK;
7316 #endif
7318 pc->tstart = pc->p;
7319 pc->tt = JIM_TT_VAR;
7320 pc->tline = pc->linenr;
7322 if (*pc->p == '{') {
7323 pc->tstart = ++pc->p;
7324 pc->len--;
7326 while (pc->len && *pc->p != '}') {
7327 if (*pc->p == '\n') {
7328 pc->linenr++;
7330 pc->p++;
7331 pc->len--;
7333 pc->tend = pc->p - 1;
7334 if (pc->len) {
7335 pc->p++;
7336 pc->len--;
7339 else {
7340 while (1) {
7342 if (pc->p[0] == ':' && pc->p[1] == ':') {
7343 while (*pc->p == ':') {
7344 pc->p++;
7345 pc->len--;
7347 continue;
7349 if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
7350 pc->p++;
7351 pc->len--;
7352 continue;
7354 break;
7357 if (*pc->p == '(') {
7358 int count = 1;
7359 const char *paren = NULL;
7361 pc->tt = JIM_TT_DICTSUGAR;
7363 while (count && pc->len) {
7364 pc->p++;
7365 pc->len--;
7366 if (*pc->p == '\\' && pc->len >= 1) {
7367 pc->p++;
7368 pc->len--;
7370 else if (*pc->p == '(') {
7371 count++;
7373 else if (*pc->p == ')') {
7374 paren = pc->p;
7375 count--;
7378 if (count == 0) {
7379 pc->p++;
7380 pc->len--;
7382 else if (paren) {
7384 paren++;
7385 pc->len += (pc->p - paren);
7386 pc->p = paren;
7388 #ifndef EXPRSUGAR_BRACKET
7389 if (*pc->tstart == '(') {
7390 pc->tt = JIM_TT_EXPRSUGAR;
7392 #endif
7394 pc->tend = pc->p - 1;
7396 if (pc->tstart == pc->p) {
7397 pc->p--;
7398 pc->len++;
7399 return JIM_ERR;
7401 return JIM_OK;
7404 static int JimParseStr(struct JimParserCtx *pc)
7406 if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
7407 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
7409 if (*pc->p == '{') {
7410 return JimParseBrace(pc);
7412 if (*pc->p == '"') {
7413 pc->inquote = 1;
7414 pc->p++;
7415 pc->len--;
7417 pc->missing.line = pc->tline;
7420 pc->tstart = pc->p;
7421 pc->tline = pc->linenr;
7422 while (1) {
7423 if (pc->len == 0) {
7424 if (pc->inquote) {
7425 pc->missing.ch = '"';
7427 pc->tend = pc->p - 1;
7428 pc->tt = JIM_TT_ESC;
7429 return JIM_OK;
7431 switch (*pc->p) {
7432 case '\\':
7433 if (!pc->inquote && *(pc->p + 1) == '\n') {
7434 pc->tend = pc->p - 1;
7435 pc->tt = JIM_TT_ESC;
7436 return JIM_OK;
7438 if (pc->len >= 2) {
7439 if (*(pc->p + 1) == '\n') {
7440 pc->linenr++;
7442 pc->p++;
7443 pc->len--;
7445 else if (pc->len == 1) {
7447 pc->missing.ch = '\\';
7449 break;
7450 case '(':
7452 if (pc->len > 1 && pc->p[1] != '$') {
7453 break;
7456 case ')':
7458 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
7459 if (pc->p == pc->tstart) {
7461 pc->p++;
7462 pc->len--;
7464 pc->tend = pc->p - 1;
7465 pc->tt = JIM_TT_ESC;
7466 return JIM_OK;
7468 break;
7470 case '$':
7471 case '[':
7472 pc->tend = pc->p - 1;
7473 pc->tt = JIM_TT_ESC;
7474 return JIM_OK;
7475 case ' ':
7476 case '\t':
7477 case '\n':
7478 case '\r':
7479 case '\f':
7480 case ';':
7481 if (!pc->inquote) {
7482 pc->tend = pc->p - 1;
7483 pc->tt = JIM_TT_ESC;
7484 return JIM_OK;
7486 else if (*pc->p == '\n') {
7487 pc->linenr++;
7489 break;
7490 case '"':
7491 if (pc->inquote) {
7492 pc->tend = pc->p - 1;
7493 pc->tt = JIM_TT_ESC;
7494 pc->p++;
7495 pc->len--;
7496 pc->inquote = 0;
7497 return JIM_OK;
7499 break;
7501 pc->p++;
7502 pc->len--;
7504 return JIM_OK;
7507 static int JimParseComment(struct JimParserCtx *pc)
7509 while (*pc->p) {
7510 if (*pc->p == '\\') {
7511 pc->p++;
7512 pc->len--;
7513 if (pc->len == 0) {
7514 pc->missing.ch = '\\';
7515 return JIM_OK;
7517 if (*pc->p == '\n') {
7518 pc->linenr++;
7521 else if (*pc->p == '\n') {
7522 pc->p++;
7523 pc->len--;
7524 pc->linenr++;
7525 break;
7527 pc->p++;
7528 pc->len--;
7530 return JIM_OK;
7534 static int xdigitval(int c)
7536 if (c >= '0' && c <= '9')
7537 return c - '0';
7538 if (c >= 'a' && c <= 'f')
7539 return c - 'a' + 10;
7540 if (c >= 'A' && c <= 'F')
7541 return c - 'A' + 10;
7542 return -1;
7545 static int odigitval(int c)
7547 if (c >= '0' && c <= '7')
7548 return c - '0';
7549 return -1;
7552 static int JimEscape(char *dest, const char *s, int slen)
7554 char *p = dest;
7555 int i, len;
7557 for (i = 0; i < slen; i++) {
7558 switch (s[i]) {
7559 case '\\':
7560 switch (s[i + 1]) {
7561 case 'a':
7562 *p++ = 0x7;
7563 i++;
7564 break;
7565 case 'b':
7566 *p++ = 0x8;
7567 i++;
7568 break;
7569 case 'f':
7570 *p++ = 0xc;
7571 i++;
7572 break;
7573 case 'n':
7574 *p++ = 0xa;
7575 i++;
7576 break;
7577 case 'r':
7578 *p++ = 0xd;
7579 i++;
7580 break;
7581 case 't':
7582 *p++ = 0x9;
7583 i++;
7584 break;
7585 case 'u':
7586 case 'U':
7587 case 'x':
7589 unsigned val = 0;
7590 int k;
7591 int maxchars = 2;
7593 i++;
7595 if (s[i] == 'U') {
7596 maxchars = 8;
7598 else if (s[i] == 'u') {
7599 if (s[i + 1] == '{') {
7600 maxchars = 6;
7601 i++;
7603 else {
7604 maxchars = 4;
7608 for (k = 0; k < maxchars; k++) {
7609 int c = xdigitval(s[i + k + 1]);
7610 if (c == -1) {
7611 break;
7613 val = (val << 4) | c;
7616 if (s[i] == '{') {
7617 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
7619 i--;
7620 k = 0;
7622 else {
7624 k++;
7627 if (k) {
7629 if (s[i] == 'x') {
7630 *p++ = val;
7632 else {
7633 p += utf8_fromunicode(p, val);
7635 i += k;
7636 break;
7639 *p++ = s[i];
7641 break;
7642 case 'v':
7643 *p++ = 0xb;
7644 i++;
7645 break;
7646 case '\0':
7647 *p++ = '\\';
7648 i++;
7649 break;
7650 case '\n':
7652 *p++ = ' ';
7653 do {
7654 i++;
7655 } while (s[i + 1] == ' ' || s[i + 1] == '\t');
7656 break;
7657 case '0':
7658 case '1':
7659 case '2':
7660 case '3':
7661 case '4':
7662 case '5':
7663 case '6':
7664 case '7':
7667 int val = 0;
7668 int c = odigitval(s[i + 1]);
7670 val = c;
7671 c = odigitval(s[i + 2]);
7672 if (c == -1) {
7673 *p++ = val;
7674 i++;
7675 break;
7677 val = (val * 8) + c;
7678 c = odigitval(s[i + 3]);
7679 if (c == -1) {
7680 *p++ = val;
7681 i += 2;
7682 break;
7684 val = (val * 8) + c;
7685 *p++ = val;
7686 i += 3;
7688 break;
7689 default:
7690 *p++ = s[i + 1];
7691 i++;
7692 break;
7694 break;
7695 default:
7696 *p++ = s[i];
7697 break;
7700 len = p - dest;
7701 *p = '\0';
7702 return len;
7705 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
7707 const char *start, *end;
7708 char *token;
7709 int len;
7711 start = pc->tstart;
7712 end = pc->tend;
7713 if (start > end) {
7714 len = 0;
7715 token = Jim_Alloc(1);
7716 token[0] = '\0';
7718 else {
7719 len = (end - start) + 1;
7720 token = Jim_Alloc(len + 1);
7721 if (pc->tt != JIM_TT_ESC) {
7723 memcpy(token, start, len);
7724 token[len] = '\0';
7726 else {
7728 len = JimEscape(token, start, len);
7732 return Jim_NewStringObjNoAlloc(interp, token, len);
7735 static int JimParseListSep(struct JimParserCtx *pc);
7736 static int JimParseListStr(struct JimParserCtx *pc);
7737 static int JimParseListQuote(struct JimParserCtx *pc);
7739 static int JimParseList(struct JimParserCtx *pc)
7741 if (isspace(UCHAR(*pc->p))) {
7742 return JimParseListSep(pc);
7744 switch (*pc->p) {
7745 case '"':
7746 return JimParseListQuote(pc);
7748 case '{':
7749 return JimParseBrace(pc);
7751 default:
7752 if (pc->len) {
7753 return JimParseListStr(pc);
7755 break;
7758 pc->tstart = pc->tend = pc->p;
7759 pc->tline = pc->linenr;
7760 pc->tt = JIM_TT_EOL;
7761 pc->eof = 1;
7762 return JIM_OK;
7765 static int JimParseListSep(struct JimParserCtx *pc)
7767 pc->tstart = pc->p;
7768 pc->tline = pc->linenr;
7769 while (isspace(UCHAR(*pc->p))) {
7770 if (*pc->p == '\n') {
7771 pc->linenr++;
7773 pc->p++;
7774 pc->len--;
7776 pc->tend = pc->p - 1;
7777 pc->tt = JIM_TT_SEP;
7778 return JIM_OK;
7781 static int JimParseListQuote(struct JimParserCtx *pc)
7783 pc->p++;
7784 pc->len--;
7786 pc->tstart = pc->p;
7787 pc->tline = pc->linenr;
7788 pc->tt = JIM_TT_STR;
7790 while (pc->len) {
7791 switch (*pc->p) {
7792 case '\\':
7793 pc->tt = JIM_TT_ESC;
7794 if (--pc->len == 0) {
7796 pc->tend = pc->p;
7797 return JIM_OK;
7799 pc->p++;
7800 break;
7801 case '\n':
7802 pc->linenr++;
7803 break;
7804 case '"':
7805 pc->tend = pc->p - 1;
7806 pc->p++;
7807 pc->len--;
7808 return JIM_OK;
7810 pc->p++;
7811 pc->len--;
7814 pc->tend = pc->p - 1;
7815 return JIM_OK;
7818 static int JimParseListStr(struct JimParserCtx *pc)
7820 pc->tstart = pc->p;
7821 pc->tline = pc->linenr;
7822 pc->tt = JIM_TT_STR;
7824 while (pc->len) {
7825 if (isspace(UCHAR(*pc->p))) {
7826 pc->tend = pc->p - 1;
7827 return JIM_OK;
7829 if (*pc->p == '\\') {
7830 if (--pc->len == 0) {
7832 pc->tend = pc->p;
7833 return JIM_OK;
7835 pc->tt = JIM_TT_ESC;
7836 pc->p++;
7838 pc->p++;
7839 pc->len--;
7841 pc->tend = pc->p - 1;
7842 return JIM_OK;
7847 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
7849 Jim_Obj *objPtr;
7852 if (interp->freeList != NULL) {
7854 objPtr = interp->freeList;
7855 interp->freeList = objPtr->nextObjPtr;
7857 else {
7859 objPtr = Jim_Alloc(sizeof(*objPtr));
7862 objPtr->refCount = 0;
7865 objPtr->prevObjPtr = NULL;
7866 objPtr->nextObjPtr = interp->liveList;
7867 if (interp->liveList)
7868 interp->liveList->prevObjPtr = objPtr;
7869 interp->liveList = objPtr;
7871 return objPtr;
7874 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
7877 JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
7878 objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
7881 Jim_FreeIntRep(interp, objPtr);
7883 if (objPtr->bytes != NULL) {
7884 if (objPtr->bytes != JimEmptyStringRep)
7885 Jim_Free(objPtr->bytes);
7888 if (objPtr->prevObjPtr)
7889 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
7890 if (objPtr->nextObjPtr)
7891 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
7892 if (interp->liveList == objPtr)
7893 interp->liveList = objPtr->nextObjPtr;
7894 #ifdef JIM_DISABLE_OBJECT_POOL
7895 Jim_Free(objPtr);
7896 #else
7898 objPtr->prevObjPtr = NULL;
7899 objPtr->nextObjPtr = interp->freeList;
7900 if (interp->freeList)
7901 interp->freeList->prevObjPtr = objPtr;
7902 interp->freeList = objPtr;
7903 objPtr->refCount = -1;
7904 #endif
7908 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
7910 if (objPtr->bytes != NULL) {
7911 if (objPtr->bytes != JimEmptyStringRep)
7912 Jim_Free(objPtr->bytes);
7914 objPtr->bytes = NULL;
7918 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
7920 Jim_Obj *dupPtr;
7922 dupPtr = Jim_NewObj(interp);
7923 if (objPtr->bytes == NULL) {
7925 dupPtr->bytes = NULL;
7927 else if (objPtr->length == 0) {
7929 dupPtr->bytes = JimEmptyStringRep;
7930 dupPtr->length = 0;
7931 dupPtr->typePtr = NULL;
7932 return dupPtr;
7934 else {
7935 dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
7936 dupPtr->length = objPtr->length;
7938 memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
7942 dupPtr->typePtr = objPtr->typePtr;
7943 if (objPtr->typePtr != NULL) {
7944 if (objPtr->typePtr->dupIntRepProc == NULL) {
7945 dupPtr->internalRep = objPtr->internalRep;
7947 else {
7949 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
7952 return dupPtr;
7955 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
7957 if (objPtr->bytes == NULL) {
7959 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7960 objPtr->typePtr->updateStringProc(objPtr);
7962 if (lenPtr)
7963 *lenPtr = objPtr->length;
7964 return objPtr->bytes;
7968 int Jim_Length(Jim_Obj *objPtr)
7970 if (objPtr->bytes == NULL) {
7972 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7973 objPtr->typePtr->updateStringProc(objPtr);
7975 return objPtr->length;
7979 const char *Jim_String(Jim_Obj *objPtr)
7981 if (objPtr->bytes == NULL) {
7983 JimPanic((objPtr->typePtr == NULL, "UpdateStringProc called against typeless value."));
7984 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7985 objPtr->typePtr->updateStringProc(objPtr);
7987 return objPtr->bytes;
7990 static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
7992 objPtr->bytes = Jim_StrDup(str);
7993 objPtr->length = strlen(str);
7996 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
7997 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
7999 static const Jim_ObjType dictSubstObjType = {
8000 "dict-substitution",
8001 FreeDictSubstInternalRep,
8002 DupDictSubstInternalRep,
8003 NULL,
8004 JIM_TYPE_NONE,
8007 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8009 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
8012 static const Jim_ObjType interpolatedObjType = {
8013 "interpolated",
8014 FreeInterpolatedInternalRep,
8015 NULL,
8016 NULL,
8017 JIM_TYPE_NONE,
8020 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8021 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8023 static const Jim_ObjType stringObjType = {
8024 "string",
8025 NULL,
8026 DupStringInternalRep,
8027 NULL,
8028 JIM_TYPE_REFERENCES,
8031 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8033 JIM_NOTUSED(interp);
8035 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
8036 dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
8039 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
8041 if (objPtr->typePtr != &stringObjType) {
8043 if (objPtr->bytes == NULL) {
8045 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8046 objPtr->typePtr->updateStringProc(objPtr);
8049 Jim_FreeIntRep(interp, objPtr);
8051 objPtr->typePtr = &stringObjType;
8052 objPtr->internalRep.strValue.maxLength = objPtr->length;
8054 objPtr->internalRep.strValue.charLength = -1;
8056 return JIM_OK;
8059 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
8061 #ifdef JIM_UTF8
8062 SetStringFromAny(interp, objPtr);
8064 if (objPtr->internalRep.strValue.charLength < 0) {
8065 objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
8067 return objPtr->internalRep.strValue.charLength;
8068 #else
8069 return Jim_Length(objPtr);
8070 #endif
8074 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
8076 Jim_Obj *objPtr = Jim_NewObj(interp);
8079 if (len == -1)
8080 len = strlen(s);
8082 if (len == 0) {
8083 objPtr->bytes = JimEmptyStringRep;
8085 else {
8086 objPtr->bytes = Jim_Alloc(len + 1);
8087 memcpy(objPtr->bytes, s, len);
8088 objPtr->bytes[len] = '\0';
8090 objPtr->length = len;
8093 objPtr->typePtr = NULL;
8094 return objPtr;
8098 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
8100 #ifdef JIM_UTF8
8102 int bytelen = utf8_index(s, charlen);
8104 Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
8107 objPtr->typePtr = &stringObjType;
8108 objPtr->internalRep.strValue.maxLength = bytelen;
8109 objPtr->internalRep.strValue.charLength = charlen;
8111 return objPtr;
8112 #else
8113 return Jim_NewStringObj(interp, s, charlen);
8114 #endif
8117 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
8119 Jim_Obj *objPtr = Jim_NewObj(interp);
8121 objPtr->bytes = s;
8122 objPtr->length = (len == -1) ? strlen(s) : len;
8123 objPtr->typePtr = NULL;
8124 return objPtr;
8127 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
8129 int needlen;
8131 if (len == -1)
8132 len = strlen(str);
8133 needlen = objPtr->length + len;
8134 if (objPtr->internalRep.strValue.maxLength < needlen ||
8135 objPtr->internalRep.strValue.maxLength == 0) {
8136 needlen *= 2;
8138 if (needlen < 7) {
8139 needlen = 7;
8141 if (objPtr->bytes == JimEmptyStringRep) {
8142 objPtr->bytes = Jim_Alloc(needlen + 1);
8144 else {
8145 objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
8147 objPtr->internalRep.strValue.maxLength = needlen;
8149 memcpy(objPtr->bytes + objPtr->length, str, len);
8150 objPtr->bytes[objPtr->length + len] = '\0';
8152 if (objPtr->internalRep.strValue.charLength >= 0) {
8154 objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
8156 objPtr->length += len;
8159 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
8161 JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
8162 SetStringFromAny(interp, objPtr);
8163 StringAppendString(objPtr, str, len);
8166 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
8168 int len;
8169 const char *str = Jim_GetString(appendObjPtr, &len);
8170 Jim_AppendString(interp, objPtr, str, len);
8173 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
8175 va_list ap;
8177 SetStringFromAny(interp, objPtr);
8178 va_start(ap, objPtr);
8179 while (1) {
8180 const char *s = va_arg(ap, const char *);
8182 if (s == NULL)
8183 break;
8184 Jim_AppendString(interp, objPtr, s, -1);
8186 va_end(ap);
8189 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
8191 if (aObjPtr == bObjPtr) {
8192 return 1;
8194 else {
8195 int Alen, Blen;
8196 const char *sA = Jim_GetString(aObjPtr, &Alen);
8197 const char *sB = Jim_GetString(bObjPtr, &Blen);
8199 return Alen == Blen && memcmp(sA, sB, Alen) == 0;
8203 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
8205 return JimGlobMatch(Jim_String(patternObjPtr), Jim_String(objPtr), nocase);
8208 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8210 int l1, l2;
8211 const char *s1 = Jim_GetString(firstObjPtr, &l1);
8212 const char *s2 = Jim_GetString(secondObjPtr, &l2);
8214 if (nocase) {
8216 return JimStringCompareLen(s1, s2, -1, nocase);
8218 return JimStringCompare(s1, l1, s2, l2);
8221 int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8223 const char *s1 = Jim_String(firstObjPtr);
8224 const char *s2 = Jim_String(secondObjPtr);
8226 return JimStringCompareLen(s1, s2, Jim_Utf8Length(interp, firstObjPtr), nocase);
8229 static int JimRelToAbsIndex(int len, int idx)
8231 if (idx < 0)
8232 return len + idx;
8233 return idx;
8236 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
8238 int rangeLen;
8240 if (*firstPtr > *lastPtr) {
8241 rangeLen = 0;
8243 else {
8244 rangeLen = *lastPtr - *firstPtr + 1;
8245 if (rangeLen) {
8246 if (*firstPtr < 0) {
8247 rangeLen += *firstPtr;
8248 *firstPtr = 0;
8250 if (*lastPtr >= len) {
8251 rangeLen -= (*lastPtr - (len - 1));
8252 *lastPtr = len - 1;
8256 if (rangeLen < 0)
8257 rangeLen = 0;
8259 *rangeLenPtr = rangeLen;
8262 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
8263 int len, int *first, int *last, int *range)
8265 if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
8266 return JIM_ERR;
8268 if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
8269 return JIM_ERR;
8271 *first = JimRelToAbsIndex(len, *first);
8272 *last = JimRelToAbsIndex(len, *last);
8273 JimRelToAbsRange(len, first, last, range);
8274 return JIM_OK;
8277 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
8278 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8280 int first, last;
8281 const char *str;
8282 int rangeLen;
8283 int bytelen;
8285 str = Jim_GetString(strObjPtr, &bytelen);
8287 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
8288 return NULL;
8291 if (first == 0 && rangeLen == bytelen) {
8292 return strObjPtr;
8294 return Jim_NewStringObj(interp, str + first, rangeLen);
8297 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
8298 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8300 #ifdef JIM_UTF8
8301 int first, last;
8302 const char *str;
8303 int len, rangeLen;
8304 int bytelen;
8306 str = Jim_GetString(strObjPtr, &bytelen);
8307 len = Jim_Utf8Length(interp, strObjPtr);
8309 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8310 return NULL;
8313 if (first == 0 && rangeLen == len) {
8314 return strObjPtr;
8316 if (len == bytelen) {
8318 return Jim_NewStringObj(interp, str + first, rangeLen);
8320 return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
8321 #else
8322 return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
8323 #endif
8326 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
8327 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
8329 int first, last;
8330 const char *str;
8331 int len, rangeLen;
8332 Jim_Obj *objPtr;
8334 len = Jim_Utf8Length(interp, strObjPtr);
8336 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8337 return NULL;
8340 if (last < first) {
8341 return strObjPtr;
8344 str = Jim_String(strObjPtr);
8347 objPtr = Jim_NewStringObjUtf8(interp, str, first);
8350 if (newStrObj) {
8351 Jim_AppendObj(interp, objPtr, newStrObj);
8355 Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
8357 return objPtr;
8360 static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
8362 while (*str) {
8363 int c;
8364 str += utf8_tounicode(str, &c);
8365 dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c));
8367 *dest = 0;
8370 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
8372 char *buf;
8373 int len;
8374 const char *str;
8376 SetStringFromAny(interp, strObjPtr);
8378 str = Jim_GetString(strObjPtr, &len);
8380 #ifdef JIM_UTF8
8381 len *= 2;
8382 #endif
8383 buf = Jim_Alloc(len + 1);
8384 JimStrCopyUpperLower(buf, str, 0);
8385 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8388 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
8390 char *buf;
8391 const char *str;
8392 int len;
8394 if (strObjPtr->typePtr != &stringObjType) {
8395 SetStringFromAny(interp, strObjPtr);
8398 str = Jim_GetString(strObjPtr, &len);
8400 #ifdef JIM_UTF8
8401 len *= 2;
8402 #endif
8403 buf = Jim_Alloc(len + 1);
8404 JimStrCopyUpperLower(buf, str, 1);
8405 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8408 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
8410 char *buf, *p;
8411 int len;
8412 int c;
8413 const char *str;
8415 str = Jim_GetString(strObjPtr, &len);
8416 if (len == 0) {
8417 return strObjPtr;
8419 #ifdef JIM_UTF8
8420 len *= 2;
8421 #endif
8422 buf = p = Jim_Alloc(len + 1);
8424 str += utf8_tounicode(str, &c);
8425 p += utf8_getchars(p, utf8_title(c));
8427 JimStrCopyUpperLower(p, str, 0);
8429 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8432 static const char *utf8_memchr(const char *str, int len, int c)
8434 #ifdef JIM_UTF8
8435 while (len) {
8436 int sc;
8437 int n = utf8_tounicode(str, &sc);
8438 if (sc == c) {
8439 return str;
8441 str += n;
8442 len -= n;
8444 return NULL;
8445 #else
8446 return memchr(str, c, len);
8447 #endif
8450 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
8452 while (len) {
8453 int c;
8454 int n = utf8_tounicode(str, &c);
8456 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8458 break;
8460 str += n;
8461 len -= n;
8463 return str;
8466 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
8468 str += len;
8470 while (len) {
8471 int c;
8472 int n = utf8_prev_len(str, len);
8474 len -= n;
8475 str -= n;
8477 n = utf8_tounicode(str, &c);
8479 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8480 return str + n;
8484 return NULL;
8487 static const char default_trim_chars[] = " \t\n\r";
8489 static int default_trim_chars_len = sizeof(default_trim_chars);
8491 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8493 int len;
8494 const char *str = Jim_GetString(strObjPtr, &len);
8495 const char *trimchars = default_trim_chars;
8496 int trimcharslen = default_trim_chars_len;
8497 const char *newstr;
8499 if (trimcharsObjPtr) {
8500 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8503 newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
8504 if (newstr == str) {
8505 return strObjPtr;
8508 return Jim_NewStringObj(interp, newstr, len - (newstr - str));
8511 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8513 int len;
8514 const char *trimchars = default_trim_chars;
8515 int trimcharslen = default_trim_chars_len;
8516 const char *nontrim;
8518 if (trimcharsObjPtr) {
8519 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8522 SetStringFromAny(interp, strObjPtr);
8524 len = Jim_Length(strObjPtr);
8525 nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
8527 if (nontrim == NULL) {
8529 return Jim_NewEmptyStringObj(interp);
8531 if (nontrim == strObjPtr->bytes + len) {
8533 return strObjPtr;
8536 if (Jim_IsShared(strObjPtr)) {
8537 strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
8539 else {
8541 strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
8542 strObjPtr->length = (nontrim - strObjPtr->bytes);
8545 return strObjPtr;
8548 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8551 Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
8554 strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
8557 if (objPtr != strObjPtr && objPtr->refCount == 0) {
8559 Jim_FreeNewObj(interp, objPtr);
8562 return strObjPtr;
8566 #ifdef HAVE_ISASCII
8567 #define jim_isascii isascii
8568 #else
8569 static int jim_isascii(int c)
8571 return !(c & ~0x7f);
8573 #endif
8575 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
8577 static const char * const strclassnames[] = {
8578 "integer", "alpha", "alnum", "ascii", "digit",
8579 "double", "lower", "upper", "space", "xdigit",
8580 "control", "print", "graph", "punct",
8581 NULL
8583 enum {
8584 STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
8585 STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
8586 STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT
8588 int strclass;
8589 int len;
8590 int i;
8591 const char *str;
8592 int (*isclassfunc)(int c) = NULL;
8594 if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
8595 return JIM_ERR;
8598 str = Jim_GetString(strObjPtr, &len);
8599 if (len == 0) {
8600 Jim_SetResultBool(interp, !strict);
8601 return JIM_OK;
8604 switch (strclass) {
8605 case STR_IS_INTEGER:
8607 jim_wide w;
8608 Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
8609 return JIM_OK;
8612 case STR_IS_DOUBLE:
8614 double d;
8615 Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
8616 return JIM_OK;
8619 case STR_IS_ALPHA: isclassfunc = isalpha; break;
8620 case STR_IS_ALNUM: isclassfunc = isalnum; break;
8621 case STR_IS_ASCII: isclassfunc = jim_isascii; break;
8622 case STR_IS_DIGIT: isclassfunc = isdigit; break;
8623 case STR_IS_LOWER: isclassfunc = islower; break;
8624 case STR_IS_UPPER: isclassfunc = isupper; break;
8625 case STR_IS_SPACE: isclassfunc = isspace; break;
8626 case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
8627 case STR_IS_CONTROL: isclassfunc = iscntrl; break;
8628 case STR_IS_PRINT: isclassfunc = isprint; break;
8629 case STR_IS_GRAPH: isclassfunc = isgraph; break;
8630 case STR_IS_PUNCT: isclassfunc = ispunct; break;
8631 default:
8632 return JIM_ERR;
8635 for (i = 0; i < len; i++) {
8636 if (!isclassfunc(str[i])) {
8637 Jim_SetResultBool(interp, 0);
8638 return JIM_OK;
8641 Jim_SetResultBool(interp, 1);
8642 return JIM_OK;
8647 static const Jim_ObjType comparedStringObjType = {
8648 "compared-string",
8649 NULL,
8650 NULL,
8651 NULL,
8652 JIM_TYPE_REFERENCES,
8655 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
8657 if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
8658 return 1;
8660 else {
8661 const char *objStr = Jim_String(objPtr);
8663 if (strcmp(str, objStr) != 0)
8664 return 0;
8666 if (objPtr->typePtr != &comparedStringObjType) {
8667 Jim_FreeIntRep(interp, objPtr);
8668 objPtr->typePtr = &comparedStringObjType;
8670 objPtr->internalRep.ptr = (char *)str;
8671 return 1;
8675 static int qsortCompareStringPointers(const void *a, const void *b)
8677 char *const *sa = (char *const *)a;
8678 char *const *sb = (char *const *)b;
8680 return strcmp(*sa, *sb);
8685 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8686 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8688 static const Jim_ObjType sourceObjType = {
8689 "source",
8690 FreeSourceInternalRep,
8691 DupSourceInternalRep,
8692 NULL,
8693 JIM_TYPE_REFERENCES,
8696 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8698 Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
8701 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8703 dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
8704 Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
8707 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
8708 Jim_Obj *fileNameObj, int lineNumber)
8710 JimPanic((Jim_IsShared(objPtr), "JimSetSourceInfo called with shared object"));
8711 JimPanic((objPtr->typePtr != NULL, "JimSetSourceInfo called with typed object"));
8712 Jim_IncrRefCount(fileNameObj);
8713 objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
8714 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
8715 objPtr->typePtr = &sourceObjType;
8718 static const Jim_ObjType scriptLineObjType = {
8719 "scriptline",
8720 NULL,
8721 NULL,
8722 NULL,
8723 JIM_NONE,
8726 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
8728 Jim_Obj *objPtr;
8730 #ifdef DEBUG_SHOW_SCRIPT
8731 char buf[100];
8732 snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
8733 objPtr = Jim_NewStringObj(interp, buf, -1);
8734 #else
8735 objPtr = Jim_NewEmptyStringObj(interp);
8736 #endif
8737 objPtr->typePtr = &scriptLineObjType;
8738 objPtr->internalRep.scriptLineValue.argc = argc;
8739 objPtr->internalRep.scriptLineValue.line = line;
8741 return objPtr;
8744 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8745 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8747 static const Jim_ObjType scriptObjType = {
8748 "script",
8749 FreeScriptInternalRep,
8750 DupScriptInternalRep,
8751 NULL,
8752 JIM_TYPE_REFERENCES,
8755 typedef struct ScriptToken
8757 Jim_Obj *objPtr;
8758 int type;
8759 } ScriptToken;
8761 typedef struct ScriptObj
8763 ScriptToken *token;
8764 Jim_Obj *fileNameObj;
8765 int len;
8766 int substFlags;
8767 int inUse; /* Used to share a ScriptObj. Currently
8768 only used by Jim_EvalObj() as protection against
8769 shimmering of the currently evaluated object. */
8770 int firstline;
8771 int linenr;
8772 int missing;
8773 } ScriptObj;
8775 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8776 static int JimParseCheckMissing(Jim_Interp *interp, int ch);
8777 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
8779 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8781 int i;
8782 struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
8784 if (--script->inUse != 0)
8785 return;
8786 for (i = 0; i < script->len; i++) {
8787 Jim_DecrRefCount(interp, script->token[i].objPtr);
8789 Jim_Free(script->token);
8790 Jim_DecrRefCount(interp, script->fileNameObj);
8791 Jim_Free(script);
8794 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8796 JIM_NOTUSED(interp);
8797 JIM_NOTUSED(srcPtr);
8799 dupPtr->typePtr = NULL;
8802 typedef struct
8804 const char *token;
8805 int len;
8806 int type;
8807 int line;
8808 } ParseToken;
8810 typedef struct
8813 ParseToken *list;
8814 int size;
8815 int count;
8816 ParseToken static_list[20];
8817 } ParseTokenList;
8819 static void ScriptTokenListInit(ParseTokenList *tokenlist)
8821 tokenlist->list = tokenlist->static_list;
8822 tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
8823 tokenlist->count = 0;
8826 static void ScriptTokenListFree(ParseTokenList *tokenlist)
8828 if (tokenlist->list != tokenlist->static_list) {
8829 Jim_Free(tokenlist->list);
8833 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
8834 int line)
8836 ParseToken *t;
8838 if (tokenlist->count == tokenlist->size) {
8840 tokenlist->size *= 2;
8841 if (tokenlist->list != tokenlist->static_list) {
8842 tokenlist->list =
8843 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
8845 else {
8847 tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
8848 memcpy(tokenlist->list, tokenlist->static_list,
8849 tokenlist->count * sizeof(*tokenlist->list));
8852 t = &tokenlist->list[tokenlist->count++];
8853 t->token = token;
8854 t->len = len;
8855 t->type = type;
8856 t->line = line;
8859 static int JimCountWordTokens(ParseToken *t)
8861 int expand = 1;
8862 int count = 0;
8865 if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
8866 if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
8868 expand = -1;
8869 t++;
8874 while (!TOKEN_IS_SEP(t->type)) {
8875 t++;
8876 count++;
8879 return count * expand;
8882 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
8884 Jim_Obj *objPtr;
8886 if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
8888 int len = t->len;
8889 char *str = Jim_Alloc(len + 1);
8890 len = JimEscape(str, t->token, len);
8891 objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
8893 else {
8894 objPtr = Jim_NewStringObj(interp, t->token, t->len);
8896 return objPtr;
8899 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
8900 ParseTokenList *tokenlist)
8902 int i;
8903 struct ScriptToken *token;
8905 int lineargs = 0;
8907 ScriptToken *linefirst;
8908 int count;
8909 int linenr;
8911 #ifdef DEBUG_SHOW_SCRIPT_TOKENS
8912 printf("==== Tokens ====\n");
8913 for (i = 0; i < tokenlist->count; i++) {
8914 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
8915 tokenlist->list[i].len, tokenlist->list[i].token);
8917 #endif
8920 count = tokenlist->count;
8921 for (i = 0; i < tokenlist->count; i++) {
8922 if (tokenlist->list[i].type == JIM_TT_EOL) {
8923 count++;
8926 linenr = script->firstline = tokenlist->list[0].line;
8928 token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
8931 linefirst = token++;
8933 for (i = 0; i < tokenlist->count; ) {
8935 int wordtokens;
8938 while (tokenlist->list[i].type == JIM_TT_SEP) {
8939 i++;
8942 wordtokens = JimCountWordTokens(tokenlist->list + i);
8944 if (wordtokens == 0) {
8946 if (lineargs) {
8947 linefirst->type = JIM_TT_LINE;
8948 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
8949 Jim_IncrRefCount(linefirst->objPtr);
8952 lineargs = 0;
8953 linefirst = token++;
8955 i++;
8956 continue;
8958 else if (wordtokens != 1) {
8960 token->type = JIM_TT_WORD;
8961 token->objPtr = Jim_NewIntObj(interp, wordtokens);
8962 Jim_IncrRefCount(token->objPtr);
8963 token++;
8964 if (wordtokens < 0) {
8966 i++;
8967 wordtokens = -wordtokens - 1;
8968 lineargs--;
8972 if (lineargs == 0) {
8974 linenr = tokenlist->list[i].line;
8976 lineargs++;
8979 while (wordtokens--) {
8980 const ParseToken *t = &tokenlist->list[i++];
8982 token->type = t->type;
8983 token->objPtr = JimMakeScriptObj(interp, t);
8984 Jim_IncrRefCount(token->objPtr);
8986 JimSetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
8987 token++;
8991 if (lineargs == 0) {
8992 token--;
8995 script->len = token - script->token;
8997 JimPanic((script->len >= count, "allocated script array is too short"));
8999 #ifdef DEBUG_SHOW_SCRIPT
9000 printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj));
9001 for (i = 0; i < script->len; i++) {
9002 const ScriptToken *t = &script->token[i];
9003 printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
9005 #endif
9009 int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
9011 ScriptObj *script = JimGetScript(interp, scriptObj);
9012 if (stateCharPtr) {
9013 *stateCharPtr = script->missing;
9015 return (script->missing == ' ');
9018 static int JimParseCheckMissing(Jim_Interp *interp, int ch)
9020 const char *msg;
9022 switch (ch) {
9023 case '\\':
9024 case ' ':
9025 return JIM_OK;
9027 case '[':
9028 msg = "unmatched \"[\"";
9029 break;
9030 case '{':
9031 msg = "missing close-brace";
9032 break;
9033 case '"':
9034 default:
9035 msg = "missing quote";
9036 break;
9039 Jim_SetResultString(interp, msg, -1);
9040 return JIM_ERR;
9043 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
9044 ParseTokenList *tokenlist)
9046 int i;
9047 struct ScriptToken *token;
9049 token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
9051 for (i = 0; i < tokenlist->count; i++) {
9052 const ParseToken *t = &tokenlist->list[i];
9055 token->type = t->type;
9056 token->objPtr = JimMakeScriptObj(interp, t);
9057 Jim_IncrRefCount(token->objPtr);
9058 token++;
9061 script->len = i;
9064 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9066 int scriptTextLen;
9067 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
9068 struct JimParserCtx parser;
9069 struct ScriptObj *script;
9070 ParseTokenList tokenlist;
9071 int line = 1;
9074 if (objPtr->typePtr == &sourceObjType) {
9075 line = objPtr->internalRep.sourceValue.lineNumber;
9079 ScriptTokenListInit(&tokenlist);
9081 JimParserInit(&parser, scriptText, scriptTextLen, line);
9082 while (!parser.eof) {
9083 JimParseScript(&parser);
9084 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
9085 parser.tline);
9089 ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
9092 script = Jim_Alloc(sizeof(*script));
9093 memset(script, 0, sizeof(*script));
9094 script->inUse = 1;
9095 if (objPtr->typePtr == &sourceObjType) {
9096 script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
9098 else {
9099 script->fileNameObj = interp->emptyObj;
9101 Jim_IncrRefCount(script->fileNameObj);
9102 script->missing = parser.missing.ch;
9103 script->linenr = parser.missing.line;
9105 ScriptObjAddTokens(interp, script, &tokenlist);
9108 ScriptTokenListFree(&tokenlist);
9111 Jim_FreeIntRep(interp, objPtr);
9112 Jim_SetIntRepPtr(objPtr, script);
9113 objPtr->typePtr = &scriptObjType;
9116 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script);
9118 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
9120 if (objPtr == interp->emptyObj) {
9122 objPtr = interp->nullScriptObj;
9125 if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
9126 JimSetScriptFromAny(interp, objPtr);
9129 return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
9132 static int JimScriptValid(Jim_Interp *interp, ScriptObj *script)
9134 if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
9135 JimAddErrorToStack(interp, script);
9136 return 0;
9138 return 1;
9142 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
9144 cmdPtr->inUse++;
9147 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
9149 if (--cmdPtr->inUse == 0) {
9150 if (cmdPtr->isproc) {
9151 Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
9152 Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
9153 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9154 if (cmdPtr->u.proc.staticVars) {
9155 Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
9156 Jim_Free(cmdPtr->u.proc.staticVars);
9159 else {
9161 if (cmdPtr->u.native.delProc) {
9162 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
9165 if (cmdPtr->prevCmd) {
9167 JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
9169 Jim_Free(cmdPtr);
9174 static void JimVariablesHTValDestructor(void *interp, void *val)
9176 Jim_DecrRefCount(interp, ((Jim_Var *)val)->objPtr);
9177 Jim_Free(val);
9180 static const Jim_HashTableType JimVariablesHashTableType = {
9181 JimStringCopyHTHashFunction,
9182 JimStringCopyHTDup,
9183 NULL,
9184 JimStringCopyHTKeyCompare,
9185 JimStringCopyHTKeyDestructor,
9186 JimVariablesHTValDestructor
9189 static void JimCommandsHT_ValDestructor(void *interp, void *val)
9191 JimDecrCmdRefCount(interp, val);
9194 static const Jim_HashTableType JimCommandsHashTableType = {
9195 JimStringCopyHTHashFunction,
9196 JimStringCopyHTDup,
9197 NULL,
9198 JimStringCopyHTKeyCompare,
9199 JimStringCopyHTKeyDestructor,
9200 JimCommandsHT_ValDestructor
9205 #ifdef jim_ext_namespace
9206 static Jim_Obj *JimQualifyNameObj(Jim_Interp *interp, Jim_Obj *nsObj)
9208 const char *name = Jim_String(nsObj);
9209 if (name[0] == ':' && name[1] == ':') {
9211 while (*++name == ':') {
9213 nsObj = Jim_NewStringObj(interp, name, -1);
9215 else if (Jim_Length(interp->framePtr->nsObj)) {
9217 nsObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9218 Jim_AppendStrings(interp, nsObj, "::", name, NULL);
9220 return nsObj;
9223 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9225 Jim_Obj *resultObj;
9227 const char *name = Jim_String(nameObjPtr);
9228 if (name[0] == ':' && name[1] == ':') {
9229 return nameObjPtr;
9231 Jim_IncrRefCount(nameObjPtr);
9232 resultObj = Jim_NewStringObj(interp, "::", -1);
9233 Jim_AppendObj(interp, resultObj, nameObjPtr);
9234 Jim_DecrRefCount(interp, nameObjPtr);
9236 return resultObj;
9239 static const char *JimQualifyName(Jim_Interp *interp, const char *name, Jim_Obj **objPtrPtr)
9241 Jim_Obj *objPtr = interp->emptyObj;
9243 if (name[0] == ':' && name[1] == ':') {
9245 while (*++name == ':') {
9248 else if (Jim_Length(interp->framePtr->nsObj)) {
9250 objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9251 Jim_AppendStrings(interp, objPtr, "::", name, NULL);
9252 name = Jim_String(objPtr);
9254 Jim_IncrRefCount(objPtr);
9255 *objPtrPtr = objPtr;
9256 return name;
9259 #define JimFreeQualifiedName(INTERP, OBJ) Jim_DecrRefCount((INTERP), (OBJ))
9261 #else
9263 #define JimQualifyName(INTERP, NAME, DUMMY) (((NAME)[0] == ':' && (NAME)[1] == ':') ? (NAME) + 2 : (NAME))
9264 #define JimFreeQualifiedName(INTERP, DUMMY) (void)(DUMMY)
9266 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9268 return nameObjPtr;
9270 #endif
9272 static int JimCreateCommand(Jim_Interp *interp, const char *name, Jim_Cmd *cmd)
9274 Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, name);
9275 if (he) {
9277 Jim_InterpIncrProcEpoch(interp);
9280 if (he && interp->local) {
9282 cmd->prevCmd = Jim_GetHashEntryVal(he);
9283 Jim_SetHashVal(&interp->commands, he, cmd);
9285 else {
9286 if (he) {
9288 Jim_DeleteHashEntry(&interp->commands, name);
9291 Jim_AddHashEntry(&interp->commands, name, cmd);
9293 return JIM_OK;
9297 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
9298 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
9300 Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
9303 memset(cmdPtr, 0, sizeof(*cmdPtr));
9304 cmdPtr->inUse = 1;
9305 cmdPtr->u.native.delProc = delProc;
9306 cmdPtr->u.native.cmdProc = cmdProc;
9307 cmdPtr->u.native.privData = privData;
9309 JimCreateCommand(interp, cmdNameStr, cmdPtr);
9311 return JIM_OK;
9314 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
9316 int len, i;
9318 len = Jim_ListLength(interp, staticsListObjPtr);
9319 if (len == 0) {
9320 return JIM_OK;
9323 cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
9324 Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
9325 for (i = 0; i < len; i++) {
9326 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
9327 Jim_Var *varPtr;
9328 int subLen;
9330 objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
9332 subLen = Jim_ListLength(interp, objPtr);
9333 if (subLen == 1 || subLen == 2) {
9334 nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
9335 if (subLen == 1) {
9336 initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
9337 if (initObjPtr == NULL) {
9338 Jim_SetResultFormatted(interp,
9339 "variable for initialization of static \"%#s\" not found in the local context",
9340 nameObjPtr);
9341 return JIM_ERR;
9344 else {
9345 initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
9347 if (JimValidName(interp, "static variable", nameObjPtr) != JIM_OK) {
9348 return JIM_ERR;
9351 varPtr = Jim_Alloc(sizeof(*varPtr));
9352 varPtr->objPtr = initObjPtr;
9353 Jim_IncrRefCount(initObjPtr);
9354 varPtr->linkFramePtr = NULL;
9355 if (Jim_AddHashEntry(cmdPtr->u.proc.staticVars,
9356 Jim_String(nameObjPtr), varPtr) != JIM_OK) {
9357 Jim_SetResultFormatted(interp,
9358 "static variable name \"%#s\" duplicated in statics list", nameObjPtr);
9359 Jim_DecrRefCount(interp, initObjPtr);
9360 Jim_Free(varPtr);
9361 return JIM_ERR;
9364 else {
9365 Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
9366 objPtr);
9367 return JIM_ERR;
9370 return JIM_OK;
9373 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, const char *cmdname)
9375 #ifdef jim_ext_namespace
9376 if (cmdPtr->isproc) {
9378 const char *pt = strrchr(cmdname, ':');
9379 if (pt && pt != cmdname && pt[-1] == ':') {
9380 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9381 cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 1);
9382 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9384 if (Jim_FindHashEntry(&interp->commands, pt + 1)) {
9386 Jim_InterpIncrProcEpoch(interp);
9390 #endif
9393 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
9394 Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
9396 Jim_Cmd *cmdPtr;
9397 int argListLen;
9398 int i;
9400 argListLen = Jim_ListLength(interp, argListObjPtr);
9403 cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
9404 memset(cmdPtr, 0, sizeof(*cmdPtr));
9405 cmdPtr->inUse = 1;
9406 cmdPtr->isproc = 1;
9407 cmdPtr->u.proc.argListObjPtr = argListObjPtr;
9408 cmdPtr->u.proc.argListLen = argListLen;
9409 cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
9410 cmdPtr->u.proc.argsPos = -1;
9411 cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
9412 cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
9413 Jim_IncrRefCount(argListObjPtr);
9414 Jim_IncrRefCount(bodyObjPtr);
9415 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9418 if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
9419 goto err;
9424 for (i = 0; i < argListLen; i++) {
9425 Jim_Obj *argPtr;
9426 Jim_Obj *nameObjPtr;
9427 Jim_Obj *defaultObjPtr;
9428 int len;
9431 argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
9432 len = Jim_ListLength(interp, argPtr);
9433 if (len == 0) {
9434 Jim_SetResultString(interp, "argument with no name", -1);
9435 err:
9436 JimDecrCmdRefCount(interp, cmdPtr);
9437 return NULL;
9439 if (len > 2) {
9440 Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
9441 goto err;
9444 if (len == 2) {
9446 nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
9447 defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
9449 else {
9451 nameObjPtr = argPtr;
9452 defaultObjPtr = NULL;
9456 if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
9457 if (cmdPtr->u.proc.argsPos >= 0) {
9458 Jim_SetResultString(interp, "'args' specified more than once", -1);
9459 goto err;
9461 cmdPtr->u.proc.argsPos = i;
9463 else {
9464 if (len == 2) {
9465 cmdPtr->u.proc.optArity++;
9467 else {
9468 cmdPtr->u.proc.reqArity++;
9472 cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
9473 cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
9476 return cmdPtr;
9479 int Jim_DeleteCommand(Jim_Interp *interp, const char *name)
9481 int ret = JIM_OK;
9482 Jim_Obj *qualifiedNameObj;
9483 const char *qualname = JimQualifyName(interp, name, &qualifiedNameObj);
9485 if (Jim_DeleteHashEntry(&interp->commands, qualname) == JIM_ERR) {
9486 Jim_SetResultFormatted(interp, "can't delete \"%s\": command doesn't exist", name);
9487 ret = JIM_ERR;
9489 else {
9490 Jim_InterpIncrProcEpoch(interp);
9493 JimFreeQualifiedName(interp, qualifiedNameObj);
9495 return ret;
9498 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, const char *newName)
9500 int ret = JIM_ERR;
9501 Jim_HashEntry *he;
9502 Jim_Cmd *cmdPtr;
9503 Jim_Obj *qualifiedOldNameObj;
9504 Jim_Obj *qualifiedNewNameObj;
9505 const char *fqold;
9506 const char *fqnew;
9508 if (newName[0] == 0) {
9509 return Jim_DeleteCommand(interp, oldName);
9512 fqold = JimQualifyName(interp, oldName, &qualifiedOldNameObj);
9513 fqnew = JimQualifyName(interp, newName, &qualifiedNewNameObj);
9516 he = Jim_FindHashEntry(&interp->commands, fqold);
9517 if (he == NULL) {
9518 Jim_SetResultFormatted(interp, "can't rename \"%s\": command doesn't exist", oldName);
9520 else if (Jim_FindHashEntry(&interp->commands, fqnew)) {
9521 Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName);
9523 else {
9525 cmdPtr = Jim_GetHashEntryVal(he);
9526 JimIncrCmdRefCount(cmdPtr);
9527 JimUpdateProcNamespace(interp, cmdPtr, fqnew);
9528 Jim_AddHashEntry(&interp->commands, fqnew, cmdPtr);
9531 Jim_DeleteHashEntry(&interp->commands, fqold);
9534 Jim_InterpIncrProcEpoch(interp);
9536 ret = JIM_OK;
9539 JimFreeQualifiedName(interp, qualifiedOldNameObj);
9540 JimFreeQualifiedName(interp, qualifiedNewNameObj);
9542 return ret;
9546 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9548 Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
9551 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9553 dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
9554 dupPtr->typePtr = srcPtr->typePtr;
9555 Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
9558 static const Jim_ObjType commandObjType = {
9559 "command",
9560 FreeCommandInternalRep,
9561 DupCommandInternalRep,
9562 NULL,
9563 JIM_TYPE_REFERENCES,
9566 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9568 Jim_Cmd *cmd;
9570 if (objPtr->typePtr != &commandObjType ||
9571 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch
9572 #ifdef jim_ext_namespace
9573 || !Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
9574 #endif
9579 const char *name = Jim_String(objPtr);
9580 Jim_HashEntry *he;
9582 if (name[0] == ':' && name[1] == ':') {
9583 while (*++name == ':') {
9586 #ifdef jim_ext_namespace
9587 else if (Jim_Length(interp->framePtr->nsObj)) {
9589 Jim_Obj *nameObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9590 Jim_AppendStrings(interp, nameObj, "::", name, NULL);
9591 he = Jim_FindHashEntry(&interp->commands, Jim_String(nameObj));
9592 Jim_FreeNewObj(interp, nameObj);
9593 if (he) {
9594 goto found;
9597 #endif
9600 he = Jim_FindHashEntry(&interp->commands, name);
9601 if (he == NULL) {
9602 if (flags & JIM_ERRMSG) {
9603 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
9605 return NULL;
9607 #ifdef jim_ext_namespace
9608 found:
9609 #endif
9610 cmd = Jim_GetHashEntryVal(he);
9613 Jim_FreeIntRep(interp, objPtr);
9614 objPtr->typePtr = &commandObjType;
9615 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
9616 objPtr->internalRep.cmdValue.cmdPtr = cmd;
9617 objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
9618 Jim_IncrRefCount(interp->framePtr->nsObj);
9620 else {
9621 cmd = objPtr->internalRep.cmdValue.cmdPtr;
9623 while (cmd->u.proc.upcall) {
9624 cmd = cmd->prevCmd;
9626 return cmd;
9631 #define JIM_DICT_SUGAR 100
9633 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
9635 static const Jim_ObjType variableObjType = {
9636 "variable",
9637 NULL,
9638 NULL,
9639 NULL,
9640 JIM_TYPE_REFERENCES,
9643 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr)
9646 if (nameObjPtr->typePtr != &variableObjType) {
9647 int len;
9648 const char *str = Jim_GetString(nameObjPtr, &len);
9649 if (memchr(str, '\0', len)) {
9650 Jim_SetResultFormatted(interp, "%s name contains embedded null", type);
9651 return JIM_ERR;
9654 return JIM_OK;
9657 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9659 const char *varName;
9660 Jim_CallFrame *framePtr;
9661 Jim_HashEntry *he;
9662 int global;
9663 int len;
9666 if (objPtr->typePtr == &variableObjType) {
9667 framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
9668 if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
9670 return JIM_OK;
9674 else if (objPtr->typePtr == &dictSubstObjType) {
9675 return JIM_DICT_SUGAR;
9677 else if (JimValidName(interp, "variable", objPtr) != JIM_OK) {
9678 return JIM_ERR;
9682 varName = Jim_GetString(objPtr, &len);
9685 if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
9686 return JIM_DICT_SUGAR;
9689 if (varName[0] == ':' && varName[1] == ':') {
9690 while (*++varName == ':') {
9692 global = 1;
9693 framePtr = interp->topFramePtr;
9695 else {
9696 global = 0;
9697 framePtr = interp->framePtr;
9701 he = Jim_FindHashEntry(&framePtr->vars, varName);
9702 if (he == NULL) {
9703 if (!global && framePtr->staticVars) {
9705 he = Jim_FindHashEntry(framePtr->staticVars, varName);
9707 if (he == NULL) {
9708 return JIM_ERR;
9713 Jim_FreeIntRep(interp, objPtr);
9714 objPtr->typePtr = &variableObjType;
9715 objPtr->internalRep.varValue.callFrameId = framePtr->id;
9716 objPtr->internalRep.varValue.varPtr = Jim_GetHashEntryVal(he);
9717 objPtr->internalRep.varValue.global = global;
9718 return JIM_OK;
9722 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
9723 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
9725 static Jim_Var *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9727 const char *name;
9728 Jim_CallFrame *framePtr;
9729 int global;
9732 Jim_Var *var = Jim_Alloc(sizeof(*var));
9734 var->objPtr = valObjPtr;
9735 Jim_IncrRefCount(valObjPtr);
9736 var->linkFramePtr = NULL;
9738 name = Jim_String(nameObjPtr);
9739 if (name[0] == ':' && name[1] == ':') {
9740 while (*++name == ':') {
9742 framePtr = interp->topFramePtr;
9743 global = 1;
9745 else {
9746 framePtr = interp->framePtr;
9747 global = 0;
9751 Jim_AddHashEntry(&framePtr->vars, name, var);
9754 Jim_FreeIntRep(interp, nameObjPtr);
9755 nameObjPtr->typePtr = &variableObjType;
9756 nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
9757 nameObjPtr->internalRep.varValue.varPtr = var;
9758 nameObjPtr->internalRep.varValue.global = global;
9760 return var;
9764 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9766 int err;
9767 Jim_Var *var;
9769 switch (SetVariableFromAny(interp, nameObjPtr)) {
9770 case JIM_DICT_SUGAR:
9771 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
9773 case JIM_ERR:
9774 if (JimValidName(interp, "variable", nameObjPtr) != JIM_OK) {
9775 return JIM_ERR;
9777 JimCreateVariable(interp, nameObjPtr, valObjPtr);
9778 break;
9780 case JIM_OK:
9781 var = nameObjPtr->internalRep.varValue.varPtr;
9782 if (var->linkFramePtr == NULL) {
9783 Jim_IncrRefCount(valObjPtr);
9784 Jim_DecrRefCount(interp, var->objPtr);
9785 var->objPtr = valObjPtr;
9787 else {
9788 Jim_CallFrame *savedCallFrame;
9790 savedCallFrame = interp->framePtr;
9791 interp->framePtr = var->linkFramePtr;
9792 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
9793 interp->framePtr = savedCallFrame;
9794 if (err != JIM_OK)
9795 return err;
9798 return JIM_OK;
9801 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9803 Jim_Obj *nameObjPtr;
9804 int result;
9806 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9807 Jim_IncrRefCount(nameObjPtr);
9808 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
9809 Jim_DecrRefCount(interp, nameObjPtr);
9810 return result;
9813 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9815 Jim_CallFrame *savedFramePtr;
9816 int result;
9818 savedFramePtr = interp->framePtr;
9819 interp->framePtr = interp->topFramePtr;
9820 result = Jim_SetVariableStr(interp, name, objPtr);
9821 interp->framePtr = savedFramePtr;
9822 return result;
9825 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
9827 Jim_Obj *nameObjPtr, *valObjPtr;
9828 int result;
9830 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9831 valObjPtr = Jim_NewStringObj(interp, val, -1);
9832 Jim_IncrRefCount(nameObjPtr);
9833 Jim_IncrRefCount(valObjPtr);
9834 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
9835 Jim_DecrRefCount(interp, nameObjPtr);
9836 Jim_DecrRefCount(interp, valObjPtr);
9837 return result;
9840 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
9841 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
9843 const char *varName;
9844 const char *targetName;
9845 Jim_CallFrame *framePtr;
9846 Jim_Var *varPtr;
9849 switch (SetVariableFromAny(interp, nameObjPtr)) {
9850 case JIM_DICT_SUGAR:
9852 Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
9853 return JIM_ERR;
9855 case JIM_OK:
9856 varPtr = nameObjPtr->internalRep.varValue.varPtr;
9858 if (varPtr->linkFramePtr == NULL) {
9859 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
9860 return JIM_ERR;
9864 varPtr->linkFramePtr = NULL;
9865 break;
9870 varName = Jim_String(nameObjPtr);
9872 if (varName[0] == ':' && varName[1] == ':') {
9873 while (*++varName == ':') {
9876 framePtr = interp->topFramePtr;
9878 else {
9879 framePtr = interp->framePtr;
9882 targetName = Jim_String(targetNameObjPtr);
9883 if (targetName[0] == ':' && targetName[1] == ':') {
9884 while (*++targetName == ':') {
9886 targetNameObjPtr = Jim_NewStringObj(interp, targetName, -1);
9887 targetCallFrame = interp->topFramePtr;
9889 Jim_IncrRefCount(targetNameObjPtr);
9891 if (framePtr->level < targetCallFrame->level) {
9892 Jim_SetResultFormatted(interp,
9893 "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
9894 nameObjPtr);
9895 Jim_DecrRefCount(interp, targetNameObjPtr);
9896 return JIM_ERR;
9900 if (framePtr == targetCallFrame) {
9901 Jim_Obj *objPtr = targetNameObjPtr;
9904 while (1) {
9905 if (strcmp(Jim_String(objPtr), varName) == 0) {
9906 Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
9907 Jim_DecrRefCount(interp, targetNameObjPtr);
9908 return JIM_ERR;
9910 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
9911 break;
9912 varPtr = objPtr->internalRep.varValue.varPtr;
9913 if (varPtr->linkFramePtr != targetCallFrame)
9914 break;
9915 objPtr = varPtr->objPtr;
9920 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
9922 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
9923 Jim_DecrRefCount(interp, targetNameObjPtr);
9924 return JIM_OK;
9927 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
9929 switch (SetVariableFromAny(interp, nameObjPtr)) {
9930 case JIM_OK:{
9931 Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr;
9933 if (varPtr->linkFramePtr == NULL) {
9934 return varPtr->objPtr;
9936 else {
9937 Jim_Obj *objPtr;
9940 Jim_CallFrame *savedCallFrame = interp->framePtr;
9942 interp->framePtr = varPtr->linkFramePtr;
9943 objPtr = Jim_GetVariable(interp, varPtr->objPtr, flags);
9944 interp->framePtr = savedCallFrame;
9945 if (objPtr) {
9946 return objPtr;
9951 break;
9953 case JIM_DICT_SUGAR:
9955 return JimDictSugarGet(interp, nameObjPtr, flags);
9957 if (flags & JIM_ERRMSG) {
9958 Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
9960 return NULL;
9963 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
9965 Jim_CallFrame *savedFramePtr;
9966 Jim_Obj *objPtr;
9968 savedFramePtr = interp->framePtr;
9969 interp->framePtr = interp->topFramePtr;
9970 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
9971 interp->framePtr = savedFramePtr;
9973 return objPtr;
9976 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
9978 Jim_Obj *nameObjPtr, *varObjPtr;
9980 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9981 Jim_IncrRefCount(nameObjPtr);
9982 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
9983 Jim_DecrRefCount(interp, nameObjPtr);
9984 return varObjPtr;
9987 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
9989 Jim_CallFrame *savedFramePtr;
9990 Jim_Obj *objPtr;
9992 savedFramePtr = interp->framePtr;
9993 interp->framePtr = interp->topFramePtr;
9994 objPtr = Jim_GetVariableStr(interp, name, flags);
9995 interp->framePtr = savedFramePtr;
9997 return objPtr;
10000 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10002 Jim_Var *varPtr;
10003 int retval;
10004 Jim_CallFrame *framePtr;
10006 retval = SetVariableFromAny(interp, nameObjPtr);
10007 if (retval == JIM_DICT_SUGAR) {
10009 return JimDictSugarSet(interp, nameObjPtr, NULL);
10011 else if (retval == JIM_OK) {
10012 varPtr = nameObjPtr->internalRep.varValue.varPtr;
10015 if (varPtr->linkFramePtr) {
10016 framePtr = interp->framePtr;
10017 interp->framePtr = varPtr->linkFramePtr;
10018 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
10019 interp->framePtr = framePtr;
10021 else {
10022 const char *name = Jim_String(nameObjPtr);
10023 if (nameObjPtr->internalRep.varValue.global) {
10024 name += 2;
10025 framePtr = interp->topFramePtr;
10027 else {
10028 framePtr = interp->framePtr;
10031 retval = Jim_DeleteHashEntry(&framePtr->vars, name);
10032 if (retval == JIM_OK) {
10034 framePtr->id = interp->callFrameEpoch++;
10038 if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
10039 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
10041 return retval;
10046 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
10047 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
10049 const char *str, *p;
10050 int len, keyLen;
10051 Jim_Obj *varObjPtr, *keyObjPtr;
10053 str = Jim_GetString(objPtr, &len);
10055 p = strchr(str, '(');
10056 JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
10058 varObjPtr = Jim_NewStringObj(interp, str, p - str);
10060 p++;
10061 keyLen = (str + len) - p;
10062 if (str[len - 1] == ')') {
10063 keyLen--;
10067 keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
10069 Jim_IncrRefCount(varObjPtr);
10070 Jim_IncrRefCount(keyObjPtr);
10071 *varPtrPtr = varObjPtr;
10072 *keyPtrPtr = keyObjPtr;
10075 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
10077 int err;
10079 SetDictSubstFromAny(interp, objPtr);
10081 err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
10082 &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);
10084 if (err == JIM_OK) {
10086 Jim_SetEmptyResult(interp);
10088 else {
10089 if (!valObjPtr) {
10091 if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
10092 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
10093 objPtr);
10094 return err;
10098 Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
10099 (valObjPtr ? "set" : "unset"), objPtr);
10101 return err;
10104 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
10105 Jim_Obj *keyObjPtr, int flags)
10107 Jim_Obj *dictObjPtr;
10108 Jim_Obj *resObjPtr = NULL;
10109 int ret;
10111 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
10112 if (!dictObjPtr) {
10113 return NULL;
10116 ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
10117 if (ret != JIM_OK) {
10118 Jim_SetResultFormatted(interp,
10119 "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
10120 ret < 0 ? "variable isn't" : "no such element in");
10122 else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
10124 Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
10127 return resObjPtr;
10131 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10133 SetDictSubstFromAny(interp, objPtr);
10135 return JimDictExpandArrayVariable(interp,
10136 objPtr->internalRep.dictSubstValue.varNameObjPtr,
10137 objPtr->internalRep.dictSubstValue.indexObjPtr, flags);
10142 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
10144 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
10145 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
10148 void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
10150 JIM_NOTUSED(interp);
10152 dupPtr->internalRep.dictSubstValue.varNameObjPtr =
10153 srcPtr->internalRep.dictSubstValue.varNameObjPtr;
10154 dupPtr->internalRep.dictSubstValue.indexObjPtr = srcPtr->internalRep.dictSubstValue.indexObjPtr;
10155 dupPtr->typePtr = &dictSubstObjType;
10159 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10161 if (objPtr->typePtr != &dictSubstObjType) {
10162 Jim_Obj *varObjPtr, *keyObjPtr;
10164 if (objPtr->typePtr == &interpolatedObjType) {
10167 varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
10168 keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
10170 Jim_IncrRefCount(varObjPtr);
10171 Jim_IncrRefCount(keyObjPtr);
10173 else {
10174 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
10177 Jim_FreeIntRep(interp, objPtr);
10178 objPtr->typePtr = &dictSubstObjType;
10179 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
10180 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
10184 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10186 Jim_Obj *resObjPtr = NULL;
10187 Jim_Obj *substKeyObjPtr = NULL;
10189 SetDictSubstFromAny(interp, objPtr);
10191 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
10192 &substKeyObjPtr, JIM_NONE)
10193 != JIM_OK) {
10194 return NULL;
10196 Jim_IncrRefCount(substKeyObjPtr);
10197 resObjPtr =
10198 JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
10199 substKeyObjPtr, 0);
10200 Jim_DecrRefCount(interp, substKeyObjPtr);
10202 return resObjPtr;
10205 static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10207 Jim_Obj *resultObjPtr;
10209 if (Jim_EvalExpression(interp, objPtr, &resultObjPtr) == JIM_OK) {
10211 resultObjPtr->refCount--;
10212 return resultObjPtr;
10214 return NULL;
10218 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
10220 Jim_CallFrame *cf;
10222 if (interp->freeFramesList) {
10223 cf = interp->freeFramesList;
10224 interp->freeFramesList = cf->next;
10226 cf->argv = NULL;
10227 cf->argc = 0;
10228 cf->procArgsObjPtr = NULL;
10229 cf->procBodyObjPtr = NULL;
10230 cf->next = NULL;
10231 cf->staticVars = NULL;
10232 cf->localCommands = NULL;
10233 cf->tailcallObj = NULL;
10234 cf->tailcallCmd = NULL;
10236 else {
10237 cf = Jim_Alloc(sizeof(*cf));
10238 memset(cf, 0, sizeof(*cf));
10240 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
10243 cf->id = interp->callFrameEpoch++;
10244 cf->parent = parent;
10245 cf->level = parent ? parent->level + 1 : 0;
10246 cf->nsObj = nsObj;
10247 Jim_IncrRefCount(nsObj);
10249 return cf;
10252 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
10255 if (localCommands) {
10256 Jim_Obj *cmdNameObj;
10258 while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
10259 Jim_HashEntry *he;
10260 Jim_Obj *fqObjName;
10261 Jim_HashTable *ht = &interp->commands;
10263 const char *fqname = JimQualifyName(interp, Jim_String(cmdNameObj), &fqObjName);
10265 he = Jim_FindHashEntry(ht, fqname);
10267 if (he) {
10268 Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
10269 if (cmd->prevCmd) {
10270 Jim_Cmd *prevCmd = cmd->prevCmd;
10271 cmd->prevCmd = NULL;
10274 JimDecrCmdRefCount(interp, cmd);
10277 Jim_SetHashVal(ht, he, prevCmd);
10279 else {
10280 Jim_DeleteHashEntry(ht, fqname);
10281 Jim_InterpIncrProcEpoch(interp);
10284 Jim_DecrRefCount(interp, cmdNameObj);
10285 JimFreeQualifiedName(interp, fqObjName);
10287 Jim_FreeStack(localCommands);
10288 Jim_Free(localCommands);
10290 return JIM_OK;
10294 #define JIM_FCF_FULL 0
10295 #define JIM_FCF_REUSE 1
10296 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
10298 JimDeleteLocalProcs(interp, cf->localCommands);
10300 if (cf->procArgsObjPtr)
10301 Jim_DecrRefCount(interp, cf->procArgsObjPtr);
10302 if (cf->procBodyObjPtr)
10303 Jim_DecrRefCount(interp, cf->procBodyObjPtr);
10304 Jim_DecrRefCount(interp, cf->nsObj);
10305 if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
10306 Jim_FreeHashTable(&cf->vars);
10307 else {
10308 int i;
10309 Jim_HashEntry **table = cf->vars.table, *he;
10311 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
10312 he = table[i];
10313 while (he != NULL) {
10314 Jim_HashEntry *nextEntry = he->next;
10315 Jim_Var *varPtr = Jim_GetHashEntryVal(he);
10317 Jim_DecrRefCount(interp, varPtr->objPtr);
10318 Jim_Free(Jim_GetHashEntryKey(he));
10319 Jim_Free(varPtr);
10320 Jim_Free(he);
10321 table[i] = NULL;
10322 he = nextEntry;
10325 cf->vars.used = 0;
10327 cf->next = interp->freeFramesList;
10328 interp->freeFramesList = cf;
10332 #ifdef JIM_REFERENCES
10334 static void JimReferencesHTValDestructor(void *interp, void *val)
10336 Jim_Reference *refPtr = (void *)val;
10338 Jim_DecrRefCount(interp, refPtr->objPtr);
10339 if (refPtr->finalizerCmdNamePtr != NULL) {
10340 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
10342 Jim_Free(val);
10345 static unsigned int JimReferencesHTHashFunction(const void *key)
10348 const unsigned long *widePtr = key;
10349 unsigned int intValue = (unsigned int)*widePtr;
10351 return Jim_IntHashFunction(intValue);
10354 static void *JimReferencesHTKeyDup(void *privdata, const void *key)
10356 void *copy = Jim_Alloc(sizeof(unsigned long));
10358 JIM_NOTUSED(privdata);
10360 memcpy(copy, key, sizeof(unsigned long));
10361 return copy;
10364 static int JimReferencesHTKeyCompare(void *privdata, const void *key1, const void *key2)
10366 JIM_NOTUSED(privdata);
10368 return memcmp(key1, key2, sizeof(unsigned long)) == 0;
10371 static void JimReferencesHTKeyDestructor(void *privdata, void *key)
10373 JIM_NOTUSED(privdata);
10375 Jim_Free(key);
10378 static const Jim_HashTableType JimReferencesHashTableType = {
10379 JimReferencesHTHashFunction,
10380 JimReferencesHTKeyDup,
10381 NULL,
10382 JimReferencesHTKeyCompare,
10383 JimReferencesHTKeyDestructor,
10384 JimReferencesHTValDestructor
10389 #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN)
10391 static int JimFormatReference(char *buf, Jim_Reference *refPtr, unsigned long id)
10393 const char *fmt = "<reference.<%s>.%020lu>";
10395 sprintf(buf, fmt, refPtr->tag, id);
10396 return JIM_REFERENCE_SPACE;
10399 static void UpdateStringOfReference(struct Jim_Obj *objPtr);
10401 static const Jim_ObjType referenceObjType = {
10402 "reference",
10403 NULL,
10404 NULL,
10405 UpdateStringOfReference,
10406 JIM_TYPE_REFERENCES,
10409 static void UpdateStringOfReference(struct Jim_Obj *objPtr)
10411 char buf[JIM_REFERENCE_SPACE + 1];
10413 JimFormatReference(buf, objPtr->internalRep.refValue.refPtr, objPtr->internalRep.refValue.id);
10414 JimSetStringBytes(objPtr, buf);
10417 static int isrefchar(int c)
10419 return (c == '_' || isalnum(c));
10422 static int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10424 unsigned long value;
10425 int i, len;
10426 const char *str, *start, *end;
10427 char refId[21];
10428 Jim_Reference *refPtr;
10429 Jim_HashEntry *he;
10430 char *endptr;
10433 str = Jim_GetString(objPtr, &len);
10435 if (len < JIM_REFERENCE_SPACE)
10436 goto badformat;
10438 start = str;
10439 end = str + len - 1;
10440 while (*start == ' ')
10441 start++;
10442 while (*end == ' ' && end > start)
10443 end--;
10444 if (end - start + 1 != JIM_REFERENCE_SPACE)
10445 goto badformat;
10447 if (memcmp(start, "<reference.<", 12) != 0)
10448 goto badformat;
10449 if (start[12 + JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>')
10450 goto badformat;
10452 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
10453 if (!isrefchar(start[12 + i]))
10454 goto badformat;
10457 memcpy(refId, start + 14 + JIM_REFERENCE_TAGLEN, 20);
10458 refId[20] = '\0';
10460 value = strtoul(refId, &endptr, 10);
10461 if (JimCheckConversion(refId, endptr) != JIM_OK)
10462 goto badformat;
10464 he = Jim_FindHashEntry(&interp->references, &value);
10465 if (he == NULL) {
10466 Jim_SetResultFormatted(interp, "invalid reference id \"%#s\"", objPtr);
10467 return JIM_ERR;
10469 refPtr = Jim_GetHashEntryVal(he);
10471 Jim_FreeIntRep(interp, objPtr);
10472 objPtr->typePtr = &referenceObjType;
10473 objPtr->internalRep.refValue.id = value;
10474 objPtr->internalRep.refValue.refPtr = refPtr;
10475 return JIM_OK;
10477 badformat:
10478 Jim_SetResultFormatted(interp, "expected reference but got \"%#s\"", objPtr);
10479 return JIM_ERR;
10482 Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr)
10484 struct Jim_Reference *refPtr;
10485 unsigned long id;
10486 Jim_Obj *refObjPtr;
10487 const char *tag;
10488 int tagLen, i;
10491 Jim_CollectIfNeeded(interp);
10493 refPtr = Jim_Alloc(sizeof(*refPtr));
10494 refPtr->objPtr = objPtr;
10495 Jim_IncrRefCount(objPtr);
10496 refPtr->finalizerCmdNamePtr = cmdNamePtr;
10497 if (cmdNamePtr)
10498 Jim_IncrRefCount(cmdNamePtr);
10499 id = interp->referenceNextId++;
10500 Jim_AddHashEntry(&interp->references, &id, refPtr);
10501 refObjPtr = Jim_NewObj(interp);
10502 refObjPtr->typePtr = &referenceObjType;
10503 refObjPtr->bytes = NULL;
10504 refObjPtr->internalRep.refValue.id = id;
10505 refObjPtr->internalRep.refValue.refPtr = refPtr;
10506 interp->referenceNextId++;
10507 tag = Jim_GetString(tagPtr, &tagLen);
10508 if (tagLen > JIM_REFERENCE_TAGLEN)
10509 tagLen = JIM_REFERENCE_TAGLEN;
10510 for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) {
10511 if (i < tagLen && isrefchar(tag[i]))
10512 refPtr->tag[i] = tag[i];
10513 else
10514 refPtr->tag[i] = '_';
10516 refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0';
10517 return refObjPtr;
10520 Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr)
10522 if (objPtr->typePtr != &referenceObjType && SetReferenceFromAny(interp, objPtr) == JIM_ERR)
10523 return NULL;
10524 return objPtr->internalRep.refValue.refPtr;
10527 int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr)
10529 Jim_Reference *refPtr;
10531 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
10532 return JIM_ERR;
10533 Jim_IncrRefCount(cmdNamePtr);
10534 if (refPtr->finalizerCmdNamePtr)
10535 Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr);
10536 refPtr->finalizerCmdNamePtr = cmdNamePtr;
10537 return JIM_OK;
10540 int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr)
10542 Jim_Reference *refPtr;
10544 if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL)
10545 return JIM_ERR;
10546 *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr;
10547 return JIM_OK;
10552 static const Jim_HashTableType JimRefMarkHashTableType = {
10553 JimReferencesHTHashFunction,
10554 JimReferencesHTKeyDup,
10555 NULL,
10556 JimReferencesHTKeyCompare,
10557 JimReferencesHTKeyDestructor,
10558 NULL
10562 int Jim_Collect(Jim_Interp *interp)
10564 int collected = 0;
10565 return collected;
10568 #define JIM_COLLECT_ID_PERIOD 5000
10569 #define JIM_COLLECT_TIME_PERIOD 300
10571 void Jim_CollectIfNeeded(Jim_Interp *interp)
10573 unsigned long elapsedId;
10574 int elapsedTime;
10576 elapsedId = interp->referenceNextId - interp->lastCollectId;
10577 elapsedTime = time(NULL) - interp->lastCollectTime;
10580 if (elapsedId > JIM_COLLECT_ID_PERIOD || elapsedTime > JIM_COLLECT_TIME_PERIOD) {
10581 Jim_Collect(interp);
10584 #endif
10586 int Jim_IsBigEndian(void)
10588 union {
10589 unsigned short s;
10590 unsigned char c[2];
10591 } uval = {0x0102};
10593 return uval.c[0] == 1;
10597 Jim_Interp *Jim_CreateInterp(void)
10599 Jim_Interp *i = Jim_Alloc(sizeof(*i));
10601 memset(i, 0, sizeof(*i));
10603 i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
10604 i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
10605 i->lastCollectTime = time(NULL);
10607 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
10608 #ifdef JIM_REFERENCES
10609 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
10610 #endif
10611 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
10612 Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
10613 i->emptyObj = Jim_NewEmptyStringObj(i);
10614 i->trueObj = Jim_NewIntObj(i, 1);
10615 i->falseObj = Jim_NewIntObj(i, 0);
10616 i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
10617 i->errorFileNameObj = i->emptyObj;
10618 i->result = i->emptyObj;
10619 i->stackTrace = Jim_NewListObj(i, NULL, 0);
10620 i->unknown = Jim_NewStringObj(i, "unknown", -1);
10621 i->errorProc = i->emptyObj;
10622 i->currentScriptObj = Jim_NewEmptyStringObj(i);
10623 i->nullScriptObj = Jim_NewEmptyStringObj(i);
10624 Jim_IncrRefCount(i->emptyObj);
10625 Jim_IncrRefCount(i->errorFileNameObj);
10626 Jim_IncrRefCount(i->result);
10627 Jim_IncrRefCount(i->stackTrace);
10628 Jim_IncrRefCount(i->unknown);
10629 Jim_IncrRefCount(i->currentScriptObj);
10630 Jim_IncrRefCount(i->nullScriptObj);
10631 Jim_IncrRefCount(i->errorProc);
10632 Jim_IncrRefCount(i->trueObj);
10633 Jim_IncrRefCount(i->falseObj);
10636 Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
10637 Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
10639 Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
10640 Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
10641 Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
10642 Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
10643 Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
10644 Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
10645 Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
10646 Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
10648 return i;
10651 void Jim_FreeInterp(Jim_Interp *i)
10653 Jim_CallFrame *cf, *cfx;
10655 Jim_Obj *objPtr, *nextObjPtr;
10658 for (cf = i->framePtr; cf; cf = cfx) {
10659 cfx = cf->parent;
10660 JimFreeCallFrame(i, cf, JIM_FCF_FULL);
10663 Jim_DecrRefCount(i, i->emptyObj);
10664 Jim_DecrRefCount(i, i->trueObj);
10665 Jim_DecrRefCount(i, i->falseObj);
10666 Jim_DecrRefCount(i, i->result);
10667 Jim_DecrRefCount(i, i->stackTrace);
10668 Jim_DecrRefCount(i, i->errorProc);
10669 Jim_DecrRefCount(i, i->unknown);
10670 Jim_DecrRefCount(i, i->errorFileNameObj);
10671 Jim_DecrRefCount(i, i->currentScriptObj);
10672 Jim_DecrRefCount(i, i->nullScriptObj);
10673 Jim_FreeHashTable(&i->commands);
10674 #ifdef JIM_REFERENCES
10675 Jim_FreeHashTable(&i->references);
10676 #endif
10677 Jim_FreeHashTable(&i->packages);
10678 Jim_Free(i->prngState);
10679 Jim_FreeHashTable(&i->assocData);
10681 #ifdef JIM_MAINTAINER
10682 if (i->liveList != NULL) {
10683 objPtr = i->liveList;
10685 printf("\n-------------------------------------\n");
10686 printf("Objects still in the free list:\n");
10687 while (objPtr) {
10688 const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
10690 if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
10691 printf("%p (%d) %-10s: '%.20s...'\n",
10692 (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
10694 else {
10695 printf("%p (%d) %-10s: '%s'\n",
10696 (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
10698 if (objPtr->typePtr == &sourceObjType) {
10699 printf("FILE %s LINE %d\n",
10700 Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
10701 objPtr->internalRep.sourceValue.lineNumber);
10703 objPtr = objPtr->nextObjPtr;
10705 printf("-------------------------------------\n\n");
10706 JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
10708 #endif
10711 objPtr = i->freeList;
10712 while (objPtr) {
10713 nextObjPtr = objPtr->nextObjPtr;
10714 Jim_Free(objPtr);
10715 objPtr = nextObjPtr;
10719 for (cf = i->freeFramesList; cf; cf = cfx) {
10720 cfx = cf->next;
10721 if (cf->vars.table)
10722 Jim_FreeHashTable(&cf->vars);
10723 Jim_Free(cf);
10727 Jim_Free(i);
10730 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10732 long level;
10733 const char *str;
10734 Jim_CallFrame *framePtr;
10736 if (levelObjPtr) {
10737 str = Jim_String(levelObjPtr);
10738 if (str[0] == '#') {
10739 char *endptr;
10741 level = jim_strtol(str + 1, &endptr);
10742 if (str[1] == '\0' || endptr[0] != '\0') {
10743 level = -1;
10746 else {
10747 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
10748 level = -1;
10750 else {
10752 level = interp->framePtr->level - level;
10756 else {
10757 str = "1";
10758 level = interp->framePtr->level - 1;
10761 if (level == 0) {
10762 return interp->topFramePtr;
10764 if (level > 0) {
10766 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10767 if (framePtr->level == level) {
10768 return framePtr;
10773 Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
10774 return NULL;
10777 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10779 long level;
10780 Jim_CallFrame *framePtr;
10782 if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
10783 if (level <= 0) {
10785 level = interp->framePtr->level + level;
10788 if (level == 0) {
10789 return interp->topFramePtr;
10793 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10794 if (framePtr->level == level) {
10795 return framePtr;
10800 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
10801 return NULL;
10804 static void JimResetStackTrace(Jim_Interp *interp)
10806 Jim_DecrRefCount(interp, interp->stackTrace);
10807 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
10808 Jim_IncrRefCount(interp->stackTrace);
10811 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
10813 int len;
10816 Jim_IncrRefCount(stackTraceObj);
10817 Jim_DecrRefCount(interp, interp->stackTrace);
10818 interp->stackTrace = stackTraceObj;
10819 interp->errorFlag = 1;
10821 len = Jim_ListLength(interp, interp->stackTrace);
10822 if (len >= 3) {
10823 if (Jim_Length(Jim_ListGetIndex(interp, interp->stackTrace, len - 2)) == 0) {
10824 interp->addStackTrace = 1;
10829 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
10830 Jim_Obj *fileNameObj, int linenr)
10832 if (strcmp(procname, "unknown") == 0) {
10833 procname = "";
10835 if (!*procname && !Jim_Length(fileNameObj)) {
10837 return;
10840 if (Jim_IsShared(interp->stackTrace)) {
10841 Jim_DecrRefCount(interp, interp->stackTrace);
10842 interp->stackTrace = Jim_DuplicateObj(interp, interp->stackTrace);
10843 Jim_IncrRefCount(interp->stackTrace);
10847 if (!*procname && Jim_Length(fileNameObj)) {
10849 int len = Jim_ListLength(interp, interp->stackTrace);
10851 if (len >= 3) {
10852 Jim_Obj *objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 3);
10853 if (Jim_Length(objPtr)) {
10855 objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 2);
10856 if (Jim_Length(objPtr) == 0) {
10858 ListSetIndex(interp, interp->stackTrace, len - 2, fileNameObj, 0);
10859 ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), 0);
10860 return;
10866 Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewStringObj(interp, procname, -1));
10867 Jim_ListAppendElement(interp, interp->stackTrace, fileNameObj);
10868 Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewIntObj(interp, linenr));
10871 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
10872 void *data)
10874 AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
10876 assocEntryPtr->delProc = delProc;
10877 assocEntryPtr->data = data;
10878 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
10881 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
10883 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
10885 if (entryPtr != NULL) {
10886 AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr);
10887 return assocEntryPtr->data;
10889 return NULL;
10892 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
10894 return Jim_DeleteHashEntry(&interp->assocData, key);
10897 int Jim_GetExitCode(Jim_Interp *interp)
10899 return interp->exitCode;
10902 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
10903 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
10905 static const Jim_ObjType intObjType = {
10906 "int",
10907 NULL,
10908 NULL,
10909 UpdateStringOfInt,
10910 JIM_TYPE_NONE,
10913 static const Jim_ObjType coercedDoubleObjType = {
10914 "coerced-double",
10915 NULL,
10916 NULL,
10917 UpdateStringOfInt,
10918 JIM_TYPE_NONE,
10922 static void UpdateStringOfInt(struct Jim_Obj *objPtr)
10924 char buf[JIM_INTEGER_SPACE + 1];
10925 jim_wide wideValue = JimWideValue(objPtr);
10926 int pos = 0;
10928 if (wideValue == 0) {
10929 buf[pos++] = '0';
10931 else {
10932 char tmp[JIM_INTEGER_SPACE];
10933 int num = 0;
10934 int i;
10936 if (wideValue < 0) {
10937 buf[pos++] = '-';
10938 i = wideValue % 10;
10939 tmp[num++] = (i > 0) ? (10 - i) : -i;
10940 wideValue /= -10;
10943 while (wideValue) {
10944 tmp[num++] = wideValue % 10;
10945 wideValue /= 10;
10948 for (i = 0; i < num; i++) {
10949 buf[pos++] = '0' + tmp[num - i - 1];
10952 buf[pos] = 0;
10954 JimSetStringBytes(objPtr, buf);
10957 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10959 jim_wide wideValue;
10960 const char *str;
10962 if (objPtr->typePtr == &coercedDoubleObjType) {
10964 objPtr->typePtr = &intObjType;
10965 return JIM_OK;
10969 str = Jim_String(objPtr);
10971 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
10972 if (flags & JIM_ERRMSG) {
10973 Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
10975 return JIM_ERR;
10977 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
10978 Jim_SetResultString(interp, "Integer value too big to be represented", -1);
10979 return JIM_ERR;
10982 Jim_FreeIntRep(interp, objPtr);
10983 objPtr->typePtr = &intObjType;
10984 objPtr->internalRep.wideValue = wideValue;
10985 return JIM_OK;
10988 #ifdef JIM_OPTIMIZATION
10989 static int JimIsWide(Jim_Obj *objPtr)
10991 return objPtr->typePtr == &intObjType;
10993 #endif
10995 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10997 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
10998 return JIM_ERR;
10999 *widePtr = JimWideValue(objPtr);
11000 return JIM_OK;
11004 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
11006 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
11007 return JIM_ERR;
11008 *widePtr = JimWideValue(objPtr);
11009 return JIM_OK;
11012 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
11014 jim_wide wideValue;
11015 int retval;
11017 retval = Jim_GetWide(interp, objPtr, &wideValue);
11018 if (retval == JIM_OK) {
11019 *longPtr = (long)wideValue;
11020 return JIM_OK;
11022 return JIM_ERR;
11025 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
11027 Jim_Obj *objPtr;
11029 objPtr = Jim_NewObj(interp);
11030 objPtr->typePtr = &intObjType;
11031 objPtr->bytes = NULL;
11032 objPtr->internalRep.wideValue = wideValue;
11033 return objPtr;
11036 #define JIM_DOUBLE_SPACE 30
11038 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
11039 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
11041 static const Jim_ObjType doubleObjType = {
11042 "double",
11043 NULL,
11044 NULL,
11045 UpdateStringOfDouble,
11046 JIM_TYPE_NONE,
11049 #ifndef HAVE_ISNAN
11050 #undef isnan
11051 #define isnan(X) ((X) != (X))
11052 #endif
11053 #ifndef HAVE_ISINF
11054 #undef isinf
11055 #define isinf(X) (1.0 / (X) == 0.0)
11056 #endif
11058 static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
11060 double value = objPtr->internalRep.doubleValue;
11062 if (isnan(value)) {
11063 JimSetStringBytes(objPtr, "NaN");
11064 return;
11066 if (isinf(value)) {
11067 if (value < 0) {
11068 JimSetStringBytes(objPtr, "-Inf");
11070 else {
11071 JimSetStringBytes(objPtr, "Inf");
11073 return;
11076 char buf[JIM_DOUBLE_SPACE + 1];
11077 int i;
11078 int len = sprintf(buf, "%.12g", value);
11081 for (i = 0; i < len; i++) {
11082 if (buf[i] == '.' || buf[i] == 'e') {
11083 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
11084 char *e = strchr(buf, 'e');
11085 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
11087 e += 2;
11088 memmove(e, e + 1, len - (e - buf));
11090 #endif
11091 break;
11094 if (buf[i] == '\0') {
11095 buf[i++] = '.';
11096 buf[i++] = '0';
11097 buf[i] = '\0';
11099 JimSetStringBytes(objPtr, buf);
11103 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
11105 double doubleValue;
11106 jim_wide wideValue;
11107 const char *str;
11109 str = Jim_String(objPtr);
11111 #ifdef HAVE_LONG_LONG
11113 #define MIN_INT_IN_DOUBLE -(1LL << 53)
11114 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)
11116 if (objPtr->typePtr == &intObjType
11117 && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
11118 && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {
11121 objPtr->typePtr = &coercedDoubleObjType;
11122 return JIM_OK;
11124 else
11125 #endif
11126 if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
11128 Jim_FreeIntRep(interp, objPtr);
11129 objPtr->typePtr = &coercedDoubleObjType;
11130 objPtr->internalRep.wideValue = wideValue;
11131 return JIM_OK;
11133 else {
11135 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
11136 Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
11137 return JIM_ERR;
11140 Jim_FreeIntRep(interp, objPtr);
11142 objPtr->typePtr = &doubleObjType;
11143 objPtr->internalRep.doubleValue = doubleValue;
11144 return JIM_OK;
11147 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
11149 if (objPtr->typePtr == &coercedDoubleObjType) {
11150 *doublePtr = JimWideValue(objPtr);
11151 return JIM_OK;
11153 if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
11154 return JIM_ERR;
11156 if (objPtr->typePtr == &coercedDoubleObjType) {
11157 *doublePtr = JimWideValue(objPtr);
11159 else {
11160 *doublePtr = objPtr->internalRep.doubleValue;
11162 return JIM_OK;
11165 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
11167 Jim_Obj *objPtr;
11169 objPtr = Jim_NewObj(interp);
11170 objPtr->typePtr = &doubleObjType;
11171 objPtr->bytes = NULL;
11172 objPtr->internalRep.doubleValue = doubleValue;
11173 return objPtr;
11176 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
11177 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
11178 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11179 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11180 static void UpdateStringOfList(struct Jim_Obj *objPtr);
11181 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11183 static const Jim_ObjType listObjType = {
11184 "list",
11185 FreeListInternalRep,
11186 DupListInternalRep,
11187 UpdateStringOfList,
11188 JIM_TYPE_NONE,
11191 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11193 int i;
11195 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
11196 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
11198 Jim_Free(objPtr->internalRep.listValue.ele);
11201 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11203 int i;
11205 JIM_NOTUSED(interp);
11207 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
11208 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
11209 dupPtr->internalRep.listValue.ele =
11210 Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
11211 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
11212 sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
11213 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
11214 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
11216 dupPtr->typePtr = &listObjType;
11219 #define JIM_ELESTR_SIMPLE 0
11220 #define JIM_ELESTR_BRACE 1
11221 #define JIM_ELESTR_QUOTE 2
11222 static unsigned char ListElementQuotingType(const char *s, int len)
11224 int i, level, blevel, trySimple = 1;
11227 if (len == 0)
11228 return JIM_ELESTR_BRACE;
11229 if (s[0] == '"' || s[0] == '{') {
11230 trySimple = 0;
11231 goto testbrace;
11233 for (i = 0; i < len; i++) {
11234 switch (s[i]) {
11235 case ' ':
11236 case '$':
11237 case '"':
11238 case '[':
11239 case ']':
11240 case ';':
11241 case '\\':
11242 case '\r':
11243 case '\n':
11244 case '\t':
11245 case '\f':
11246 case '\v':
11247 trySimple = 0;
11249 case '{':
11250 case '}':
11251 goto testbrace;
11254 return JIM_ELESTR_SIMPLE;
11256 testbrace:
11258 if (s[len - 1] == '\\')
11259 return JIM_ELESTR_QUOTE;
11260 level = 0;
11261 blevel = 0;
11262 for (i = 0; i < len; i++) {
11263 switch (s[i]) {
11264 case '{':
11265 level++;
11266 break;
11267 case '}':
11268 level--;
11269 if (level < 0)
11270 return JIM_ELESTR_QUOTE;
11271 break;
11272 case '[':
11273 blevel++;
11274 break;
11275 case ']':
11276 blevel--;
11277 break;
11278 case '\\':
11279 if (s[i + 1] == '\n')
11280 return JIM_ELESTR_QUOTE;
11281 else if (s[i + 1] != '\0')
11282 i++;
11283 break;
11286 if (blevel < 0) {
11287 return JIM_ELESTR_QUOTE;
11290 if (level == 0) {
11291 if (!trySimple)
11292 return JIM_ELESTR_BRACE;
11293 for (i = 0; i < len; i++) {
11294 switch (s[i]) {
11295 case ' ':
11296 case '$':
11297 case '"':
11298 case '[':
11299 case ']':
11300 case ';':
11301 case '\\':
11302 case '\r':
11303 case '\n':
11304 case '\t':
11305 case '\f':
11306 case '\v':
11307 return JIM_ELESTR_BRACE;
11308 break;
11311 return JIM_ELESTR_SIMPLE;
11313 return JIM_ELESTR_QUOTE;
11316 static int BackslashQuoteString(const char *s, int len, char *q)
11318 char *p = q;
11320 while (len--) {
11321 switch (*s) {
11322 case ' ':
11323 case '$':
11324 case '"':
11325 case '[':
11326 case ']':
11327 case '{':
11328 case '}':
11329 case ';':
11330 case '\\':
11331 *p++ = '\\';
11332 *p++ = *s++;
11333 break;
11334 case '\n':
11335 *p++ = '\\';
11336 *p++ = 'n';
11337 s++;
11338 break;
11339 case '\r':
11340 *p++ = '\\';
11341 *p++ = 'r';
11342 s++;
11343 break;
11344 case '\t':
11345 *p++ = '\\';
11346 *p++ = 't';
11347 s++;
11348 break;
11349 case '\f':
11350 *p++ = '\\';
11351 *p++ = 'f';
11352 s++;
11353 break;
11354 case '\v':
11355 *p++ = '\\';
11356 *p++ = 'v';
11357 s++;
11358 break;
11359 default:
11360 *p++ = *s++;
11361 break;
11364 *p = '\0';
11366 return p - q;
11369 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
11371 #define STATIC_QUOTING_LEN 32
11372 int i, bufLen, realLength;
11373 const char *strRep;
11374 char *p;
11375 unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
11378 if (objc > STATIC_QUOTING_LEN) {
11379 quotingType = Jim_Alloc(objc);
11381 else {
11382 quotingType = staticQuoting;
11384 bufLen = 0;
11385 for (i = 0; i < objc; i++) {
11386 int len;
11388 strRep = Jim_GetString(objv[i], &len);
11389 quotingType[i] = ListElementQuotingType(strRep, len);
11390 switch (quotingType[i]) {
11391 case JIM_ELESTR_SIMPLE:
11392 if (i != 0 || strRep[0] != '#') {
11393 bufLen += len;
11394 break;
11397 quotingType[i] = JIM_ELESTR_BRACE;
11399 case JIM_ELESTR_BRACE:
11400 bufLen += len + 2;
11401 break;
11402 case JIM_ELESTR_QUOTE:
11403 bufLen += len * 2;
11404 break;
11406 bufLen++;
11408 bufLen++;
11411 p = objPtr->bytes = Jim_Alloc(bufLen + 1);
11412 realLength = 0;
11413 for (i = 0; i < objc; i++) {
11414 int len, qlen;
11416 strRep = Jim_GetString(objv[i], &len);
11418 switch (quotingType[i]) {
11419 case JIM_ELESTR_SIMPLE:
11420 memcpy(p, strRep, len);
11421 p += len;
11422 realLength += len;
11423 break;
11424 case JIM_ELESTR_BRACE:
11425 *p++ = '{';
11426 memcpy(p, strRep, len);
11427 p += len;
11428 *p++ = '}';
11429 realLength += len + 2;
11430 break;
11431 case JIM_ELESTR_QUOTE:
11432 if (i == 0 && strRep[0] == '#') {
11433 *p++ = '\\';
11434 realLength++;
11436 qlen = BackslashQuoteString(strRep, len, p);
11437 p += qlen;
11438 realLength += qlen;
11439 break;
11442 if (i + 1 != objc) {
11443 *p++ = ' ';
11444 realLength++;
11447 *p = '\0';
11448 objPtr->length = realLength;
11450 if (quotingType != staticQuoting) {
11451 Jim_Free(quotingType);
11455 static void UpdateStringOfList(struct Jim_Obj *objPtr)
11457 JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
11460 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
11462 struct JimParserCtx parser;
11463 const char *str;
11464 int strLen;
11465 Jim_Obj *fileNameObj;
11466 int linenr;
11468 if (objPtr->typePtr == &listObjType) {
11469 return JIM_OK;
11472 if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
11473 Jim_Obj **listObjPtrPtr;
11474 int len;
11475 int i;
11477 listObjPtrPtr = JimDictPairs(objPtr, &len);
11478 for (i = 0; i < len; i++) {
11479 Jim_IncrRefCount(listObjPtrPtr[i]);
11483 Jim_FreeIntRep(interp, objPtr);
11484 objPtr->typePtr = &listObjType;
11485 objPtr->internalRep.listValue.len = len;
11486 objPtr->internalRep.listValue.maxLen = len;
11487 objPtr->internalRep.listValue.ele = listObjPtrPtr;
11489 return JIM_OK;
11493 if (objPtr->typePtr == &sourceObjType) {
11494 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
11495 linenr = objPtr->internalRep.sourceValue.lineNumber;
11497 else {
11498 fileNameObj = interp->emptyObj;
11499 linenr = 1;
11501 Jim_IncrRefCount(fileNameObj);
11504 str = Jim_GetString(objPtr, &strLen);
11506 Jim_FreeIntRep(interp, objPtr);
11507 objPtr->typePtr = &listObjType;
11508 objPtr->internalRep.listValue.len = 0;
11509 objPtr->internalRep.listValue.maxLen = 0;
11510 objPtr->internalRep.listValue.ele = NULL;
11513 if (strLen) {
11514 JimParserInit(&parser, str, strLen, linenr);
11515 while (!parser.eof) {
11516 Jim_Obj *elementPtr;
11518 JimParseList(&parser);
11519 if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
11520 continue;
11521 elementPtr = JimParserGetTokenObj(interp, &parser);
11522 JimSetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
11523 ListAppendElement(objPtr, elementPtr);
11526 Jim_DecrRefCount(interp, fileNameObj);
11527 return JIM_OK;
11530 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
11532 Jim_Obj *objPtr;
11534 objPtr = Jim_NewObj(interp);
11535 objPtr->typePtr = &listObjType;
11536 objPtr->bytes = NULL;
11537 objPtr->internalRep.listValue.ele = NULL;
11538 objPtr->internalRep.listValue.len = 0;
11539 objPtr->internalRep.listValue.maxLen = 0;
11541 if (len) {
11542 ListInsertElements(objPtr, 0, len, elements);
11545 return objPtr;
11548 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
11549 Jim_Obj ***listVec)
11551 *listLen = Jim_ListLength(interp, listObj);
11552 *listVec = listObj->internalRep.listValue.ele;
11556 static int JimSign(jim_wide w)
11558 if (w == 0) {
11559 return 0;
11561 else if (w < 0) {
11562 return -1;
11564 return 1;
11568 struct lsort_info {
11569 jmp_buf jmpbuf;
11570 Jim_Obj *command;
11571 Jim_Interp *interp;
11572 enum {
11573 JIM_LSORT_ASCII,
11574 JIM_LSORT_NOCASE,
11575 JIM_LSORT_INTEGER,
11576 JIM_LSORT_REAL,
11577 JIM_LSORT_COMMAND
11578 } type;
11579 int order;
11580 int index;
11581 int indexed;
11582 int unique;
11583 int (*subfn)(Jim_Obj **, Jim_Obj **);
11586 static struct lsort_info *sort_info;
11588 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11590 Jim_Obj *lObj, *rObj;
11592 if (Jim_ListIndex(sort_info->interp, *lhsObj, sort_info->index, &lObj, JIM_ERRMSG) != JIM_OK ||
11593 Jim_ListIndex(sort_info->interp, *rhsObj, sort_info->index, &rObj, JIM_ERRMSG) != JIM_OK) {
11594 longjmp(sort_info->jmpbuf, JIM_ERR);
11596 return sort_info->subfn(&lObj, &rObj);
11600 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11602 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
11605 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11607 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
11610 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11612 jim_wide lhs = 0, rhs = 0;
11614 if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11615 Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11616 longjmp(sort_info->jmpbuf, JIM_ERR);
11619 return JimSign(lhs - rhs) * sort_info->order;
11622 static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11624 double lhs = 0, rhs = 0;
11626 if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11627 Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11628 longjmp(sort_info->jmpbuf, JIM_ERR);
11630 if (lhs == rhs) {
11631 return 0;
11633 if (lhs > rhs) {
11634 return sort_info->order;
11636 return -sort_info->order;
11639 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11641 Jim_Obj *compare_script;
11642 int rc;
11644 jim_wide ret = 0;
11647 compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
11648 Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
11649 Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);
11651 rc = Jim_EvalObj(sort_info->interp, compare_script);
11653 if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
11654 longjmp(sort_info->jmpbuf, rc);
11657 return JimSign(ret) * sort_info->order;
11660 static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
11662 int src;
11663 int dst = 0;
11664 Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
11666 for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
11667 if (comp(&ele[dst], &ele[src]) == 0) {
11669 Jim_DecrRefCount(sort_info->interp, ele[dst]);
11671 else {
11673 dst++;
11675 ele[dst] = ele[src];
11678 ele[++dst] = ele[src];
11681 listObjPtr->internalRep.listValue.len = dst;
11685 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
11687 struct lsort_info *prev_info;
11689 typedef int (qsort_comparator) (const void *, const void *);
11690 int (*fn) (Jim_Obj **, Jim_Obj **);
11691 Jim_Obj **vector;
11692 int len;
11693 int rc;
11695 JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
11696 SetListFromAny(interp, listObjPtr);
11699 prev_info = sort_info;
11700 sort_info = info;
11702 vector = listObjPtr->internalRep.listValue.ele;
11703 len = listObjPtr->internalRep.listValue.len;
11704 switch (info->type) {
11705 case JIM_LSORT_ASCII:
11706 fn = ListSortString;
11707 break;
11708 case JIM_LSORT_NOCASE:
11709 fn = ListSortStringNoCase;
11710 break;
11711 case JIM_LSORT_INTEGER:
11712 fn = ListSortInteger;
11713 break;
11714 case JIM_LSORT_REAL:
11715 fn = ListSortReal;
11716 break;
11717 case JIM_LSORT_COMMAND:
11718 fn = ListSortCommand;
11719 break;
11720 default:
11721 fn = NULL;
11722 JimPanic((1, "ListSort called with invalid sort type"));
11723 return -1;
11726 if (info->indexed) {
11728 info->subfn = fn;
11729 fn = ListSortIndexHelper;
11732 if ((rc = setjmp(info->jmpbuf)) == 0) {
11733 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
11735 if (info->unique && len > 1) {
11736 ListRemoveDuplicates(listObjPtr, fn);
11739 Jim_InvalidateStringRep(listObjPtr);
11741 sort_info = prev_info;
11743 return rc;
11746 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
11748 int currentLen = listPtr->internalRep.listValue.len;
11749 int requiredLen = currentLen + elemc;
11750 int i;
11751 Jim_Obj **point;
11753 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
11754 if (requiredLen < 2) {
11756 requiredLen = 4;
11758 else {
11759 requiredLen *= 2;
11762 listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
11763 sizeof(Jim_Obj *) * requiredLen);
11765 listPtr->internalRep.listValue.maxLen = requiredLen;
11767 if (idx < 0) {
11768 idx = currentLen;
11770 point = listPtr->internalRep.listValue.ele + idx;
11771 memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
11772 for (i = 0; i < elemc; ++i) {
11773 point[i] = elemVec[i];
11774 Jim_IncrRefCount(point[i]);
11776 listPtr->internalRep.listValue.len += elemc;
11779 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
11781 ListInsertElements(listPtr, -1, 1, &objPtr);
11784 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11786 ListInsertElements(listPtr, -1,
11787 appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele);
11790 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
11792 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object"));
11793 SetListFromAny(interp, listPtr);
11794 Jim_InvalidateStringRep(listPtr);
11795 ListAppendElement(listPtr, objPtr);
11798 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11800 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object"));
11801 SetListFromAny(interp, listPtr);
11802 SetListFromAny(interp, appendListPtr);
11803 Jim_InvalidateStringRep(listPtr);
11804 ListAppendList(listPtr, appendListPtr);
11807 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
11809 SetListFromAny(interp, objPtr);
11810 return objPtr->internalRep.listValue.len;
11813 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11814 int objc, Jim_Obj *const *objVec)
11816 JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
11817 SetListFromAny(interp, listPtr);
11818 if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
11819 idx = listPtr->internalRep.listValue.len;
11820 else if (idx < 0)
11821 idx = 0;
11822 Jim_InvalidateStringRep(listPtr);
11823 ListInsertElements(listPtr, idx, objc, objVec);
11826 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
11828 SetListFromAny(interp, listPtr);
11829 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11830 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11831 return NULL;
11833 if (idx < 0)
11834 idx = listPtr->internalRep.listValue.len + idx;
11835 return listPtr->internalRep.listValue.ele[idx];
11838 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
11840 *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
11841 if (*objPtrPtr == NULL) {
11842 if (flags & JIM_ERRMSG) {
11843 Jim_SetResultString(interp, "list index out of range", -1);
11845 return JIM_ERR;
11847 return JIM_OK;
11850 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11851 Jim_Obj *newObjPtr, int flags)
11853 SetListFromAny(interp, listPtr);
11854 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11855 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11856 if (flags & JIM_ERRMSG) {
11857 Jim_SetResultString(interp, "list index out of range", -1);
11859 return JIM_ERR;
11861 if (idx < 0)
11862 idx = listPtr->internalRep.listValue.len + idx;
11863 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
11864 listPtr->internalRep.listValue.ele[idx] = newObjPtr;
11865 Jim_IncrRefCount(newObjPtr);
11866 return JIM_OK;
11869 int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
11870 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
11872 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
11873 int shared, i, idx;
11875 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
11876 if (objPtr == NULL)
11877 return JIM_ERR;
11878 if ((shared = Jim_IsShared(objPtr)))
11879 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
11880 for (i = 0; i < indexc - 1; i++) {
11881 listObjPtr = objPtr;
11882 if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
11883 goto err;
11884 if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_ERRMSG) != JIM_OK) {
11885 goto err;
11887 if (Jim_IsShared(objPtr)) {
11888 objPtr = Jim_DuplicateObj(interp, objPtr);
11889 ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
11891 Jim_InvalidateStringRep(listObjPtr);
11893 if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
11894 goto err;
11895 if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
11896 goto err;
11897 Jim_InvalidateStringRep(objPtr);
11898 Jim_InvalidateStringRep(varObjPtr);
11899 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
11900 goto err;
11901 Jim_SetResult(interp, varObjPtr);
11902 return JIM_OK;
11903 err:
11904 if (shared) {
11905 Jim_FreeNewObj(interp, varObjPtr);
11907 return JIM_ERR;
11910 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
11912 int i;
11913 int listLen = Jim_ListLength(interp, listObjPtr);
11914 Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
11916 for (i = 0; i < listLen; ) {
11917 Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i));
11918 if (++i != listLen) {
11919 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11922 return resObjPtr;
11925 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
11927 int i;
11929 for (i = 0; i < objc; i++) {
11930 if (!Jim_IsList(objv[i]))
11931 break;
11933 if (i == objc) {
11934 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
11936 for (i = 0; i < objc; i++)
11937 ListAppendList(objPtr, objv[i]);
11938 return objPtr;
11940 else {
11942 int len = 0, objLen;
11943 char *bytes, *p;
11946 for (i = 0; i < objc; i++) {
11947 len += Jim_Length(objv[i]);
11949 if (objc)
11950 len += objc - 1;
11952 p = bytes = Jim_Alloc(len + 1);
11953 for (i = 0; i < objc; i++) {
11954 const char *s = Jim_GetString(objv[i], &objLen);
11957 while (objLen && isspace(UCHAR(*s))) {
11958 s++;
11959 objLen--;
11960 len--;
11963 while (objLen && isspace(UCHAR(s[objLen - 1]))) {
11965 if (objLen > 1 && s[objLen - 2] == '\\') {
11966 break;
11968 objLen--;
11969 len--;
11971 memcpy(p, s, objLen);
11972 p += objLen;
11973 if (i + 1 != objc) {
11974 if (objLen)
11975 *p++ = ' ';
11976 else {
11977 len--;
11981 *p = '\0';
11982 return Jim_NewStringObjNoAlloc(interp, bytes, len);
11986 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr,
11987 Jim_Obj *lastObjPtr)
11989 int first, last;
11990 int len, rangeLen;
11992 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
11993 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
11994 return NULL;
11995 len = Jim_ListLength(interp, listObjPtr);
11996 first = JimRelToAbsIndex(len, first);
11997 last = JimRelToAbsIndex(len, last);
11998 JimRelToAbsRange(len, &first, &last, &rangeLen);
11999 if (first == 0 && last == len) {
12000 return listObjPtr;
12002 return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
12005 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
12006 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
12007 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
12008 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
12011 static unsigned int JimObjectHTHashFunction(const void *key)
12013 int len;
12014 const char *str = Jim_GetString((Jim_Obj *)key, &len);
12015 return Jim_GenHashFunction((const unsigned char *)str, len);
12018 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
12020 return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
12023 static void *JimObjectHTKeyValDup(void *privdata, const void *val)
12025 Jim_IncrRefCount((Jim_Obj *)val);
12026 return (void *)val;
12029 static void JimObjectHTKeyValDestructor(void *interp, void *val)
12031 Jim_DecrRefCount(interp, (Jim_Obj *)val);
12034 static const Jim_HashTableType JimDictHashTableType = {
12035 JimObjectHTHashFunction,
12036 JimObjectHTKeyValDup,
12037 JimObjectHTKeyValDup,
12038 JimObjectHTKeyCompare,
12039 JimObjectHTKeyValDestructor,
12040 JimObjectHTKeyValDestructor
12043 static const Jim_ObjType dictObjType = {
12044 "dict",
12045 FreeDictInternalRep,
12046 DupDictInternalRep,
12047 UpdateStringOfDict,
12048 JIM_TYPE_NONE,
12051 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
12053 JIM_NOTUSED(interp);
12055 Jim_FreeHashTable(objPtr->internalRep.ptr);
12056 Jim_Free(objPtr->internalRep.ptr);
12059 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
12061 Jim_HashTable *ht, *dupHt;
12062 Jim_HashTableIterator htiter;
12063 Jim_HashEntry *he;
12066 ht = srcPtr->internalRep.ptr;
12067 dupHt = Jim_Alloc(sizeof(*dupHt));
12068 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
12069 if (ht->size != 0)
12070 Jim_ExpandHashTable(dupHt, ht->size);
12072 JimInitHashTableIterator(ht, &htiter);
12073 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
12074 Jim_AddHashEntry(dupHt, he->key, he->u.val);
12077 dupPtr->internalRep.ptr = dupHt;
12078 dupPtr->typePtr = &dictObjType;
12081 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len)
12083 Jim_HashTable *ht;
12084 Jim_HashTableIterator htiter;
12085 Jim_HashEntry *he;
12086 Jim_Obj **objv;
12087 int i;
12089 ht = dictPtr->internalRep.ptr;
12092 objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *));
12093 JimInitHashTableIterator(ht, &htiter);
12094 i = 0;
12095 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
12096 objv[i++] = Jim_GetHashEntryKey(he);
12097 objv[i++] = Jim_GetHashEntryVal(he);
12099 *len = i;
12100 return objv;
12103 static void UpdateStringOfDict(struct Jim_Obj *objPtr)
12106 int len;
12107 Jim_Obj **objv = JimDictPairs(objPtr, &len);
12110 JimMakeListStringRep(objPtr, objv, len);
12112 Jim_Free(objv);
12115 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
12117 int listlen;
12119 if (objPtr->typePtr == &dictObjType) {
12120 return JIM_OK;
12123 if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
12124 Jim_String(objPtr);
12128 listlen = Jim_ListLength(interp, objPtr);
12129 if (listlen % 2) {
12130 Jim_SetResultString(interp, "missing value to go with key", -1);
12131 return JIM_ERR;
12133 else {
12135 Jim_HashTable *ht;
12136 int i;
12138 ht = Jim_Alloc(sizeof(*ht));
12139 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
12141 for (i = 0; i < listlen; i += 2) {
12142 Jim_Obj *keyObjPtr = Jim_ListGetIndex(interp, objPtr, i);
12143 Jim_Obj *valObjPtr = Jim_ListGetIndex(interp, objPtr, i + 1);
12145 Jim_ReplaceHashEntry(ht, keyObjPtr, valObjPtr);
12148 Jim_FreeIntRep(interp, objPtr);
12149 objPtr->typePtr = &dictObjType;
12150 objPtr->internalRep.ptr = ht;
12152 return JIM_OK;
12158 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12159 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12161 Jim_HashTable *ht = objPtr->internalRep.ptr;
12163 if (valueObjPtr == NULL) {
12164 return Jim_DeleteHashEntry(ht, keyObjPtr);
12166 Jim_ReplaceHashEntry(ht, keyObjPtr, valueObjPtr);
12167 return JIM_OK;
12170 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12171 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12173 JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
12174 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
12175 return JIM_ERR;
12177 Jim_InvalidateStringRep(objPtr);
12178 return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
12181 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
12183 Jim_Obj *objPtr;
12184 int i;
12186 JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
12188 objPtr = Jim_NewObj(interp);
12189 objPtr->typePtr = &dictObjType;
12190 objPtr->bytes = NULL;
12191 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
12192 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
12193 for (i = 0; i < len; i += 2)
12194 DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
12195 return objPtr;
12198 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
12199 Jim_Obj **objPtrPtr, int flags)
12201 Jim_HashEntry *he;
12202 Jim_HashTable *ht;
12204 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12205 return -1;
12207 ht = dictPtr->internalRep.ptr;
12208 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
12209 if (flags & JIM_ERRMSG) {
12210 Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
12212 return JIM_ERR;
12214 *objPtrPtr = he->u.val;
12215 return JIM_OK;
12219 int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len)
12221 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12222 return JIM_ERR;
12224 *objPtrPtr = JimDictPairs(dictPtr, len);
12226 return JIM_OK;
12231 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
12232 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
12234 int i;
12236 if (keyc == 0) {
12237 *objPtrPtr = dictPtr;
12238 return JIM_OK;
12241 for (i = 0; i < keyc; i++) {
12242 Jim_Obj *objPtr;
12244 int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
12245 if (rc != JIM_OK) {
12246 return rc;
12248 dictPtr = objPtr;
12250 *objPtrPtr = dictPtr;
12251 return JIM_OK;
12254 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
12255 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags)
12257 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
12258 int shared, i;
12260 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
12261 if (objPtr == NULL) {
12262 if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
12264 return JIM_ERR;
12266 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
12267 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
12268 Jim_FreeNewObj(interp, varObjPtr);
12269 return JIM_ERR;
12272 if ((shared = Jim_IsShared(objPtr)))
12273 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
12274 for (i = 0; i < keyc; i++) {
12275 dictObjPtr = objPtr;
12278 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
12279 goto err;
12282 if (i == keyc - 1) {
12284 if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
12285 if (newObjPtr || (flags & JIM_MUSTEXIST)) {
12286 goto err;
12289 break;
12293 Jim_InvalidateStringRep(dictObjPtr);
12294 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
12295 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
12296 if (Jim_IsShared(objPtr)) {
12297 objPtr = Jim_DuplicateObj(interp, objPtr);
12298 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12301 else {
12302 if (newObjPtr == NULL) {
12303 goto err;
12305 objPtr = Jim_NewDictObj(interp, NULL, 0);
12306 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12310 Jim_InvalidateStringRep(objPtr);
12311 Jim_InvalidateStringRep(varObjPtr);
12312 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
12313 goto err;
12315 Jim_SetResult(interp, varObjPtr);
12316 return JIM_OK;
12317 err:
12318 if (shared) {
12319 Jim_FreeNewObj(interp, varObjPtr);
12321 return JIM_ERR;
12324 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
12325 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
12327 static const Jim_ObjType indexObjType = {
12328 "index",
12329 NULL,
12330 NULL,
12331 UpdateStringOfIndex,
12332 JIM_TYPE_NONE,
12335 static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
12337 if (objPtr->internalRep.intValue == -1) {
12338 JimSetStringBytes(objPtr, "end");
12340 else {
12341 char buf[JIM_INTEGER_SPACE + 1];
12342 if (objPtr->internalRep.intValue >= 0) {
12343 sprintf(buf, "%d", objPtr->internalRep.intValue);
12345 else {
12347 sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
12349 JimSetStringBytes(objPtr, buf);
12353 static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12355 int idx, end = 0;
12356 const char *str;
12357 char *endptr;
12360 str = Jim_String(objPtr);
12363 if (strncmp(str, "end", 3) == 0) {
12364 end = 1;
12365 str += 3;
12366 idx = 0;
12368 else {
12369 idx = jim_strtol(str, &endptr);
12371 if (endptr == str) {
12372 goto badindex;
12374 str = endptr;
12378 if (*str == '+' || *str == '-') {
12379 int sign = (*str == '+' ? 1 : -1);
12381 idx += sign * jim_strtol(++str, &endptr);
12382 if (str == endptr || *endptr) {
12383 goto badindex;
12385 str = endptr;
12388 while (isspace(UCHAR(*str))) {
12389 str++;
12391 if (*str) {
12392 goto badindex;
12394 if (end) {
12395 if (idx > 0) {
12396 idx = INT_MAX;
12398 else {
12400 idx--;
12403 else if (idx < 0) {
12404 idx = -INT_MAX;
12408 Jim_FreeIntRep(interp, objPtr);
12409 objPtr->typePtr = &indexObjType;
12410 objPtr->internalRep.intValue = idx;
12411 return JIM_OK;
12413 badindex:
12414 Jim_SetResultFormatted(interp,
12415 "bad index \"%#s\": must be integer?[+-]integer? or end?[+-]integer?", objPtr);
12416 return JIM_ERR;
12419 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
12422 if (objPtr->typePtr == &intObjType) {
12423 jim_wide val = JimWideValue(objPtr);
12425 if (val < 0)
12426 *indexPtr = -INT_MAX;
12427 else if (val > INT_MAX)
12428 *indexPtr = INT_MAX;
12429 else
12430 *indexPtr = (int)val;
12431 return JIM_OK;
12433 if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
12434 return JIM_ERR;
12435 *indexPtr = objPtr->internalRep.intValue;
12436 return JIM_OK;
12441 static const char * const jimReturnCodes[] = {
12442 "ok",
12443 "error",
12444 "return",
12445 "break",
12446 "continue",
12447 "signal",
12448 "exit",
12449 "eval",
12450 NULL
12453 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes))
12455 static const Jim_ObjType returnCodeObjType = {
12456 "return-code",
12457 NULL,
12458 NULL,
12459 NULL,
12460 JIM_TYPE_NONE,
12463 const char *Jim_ReturnCode(int code)
12465 if (code < 0 || code >= (int)jimReturnCodesSize) {
12466 return "?";
12468 else {
12469 return jimReturnCodes[code];
12473 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12475 int returnCode;
12476 jim_wide wideValue;
12479 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
12480 returnCode = (int)wideValue;
12481 else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
12482 Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
12483 return JIM_ERR;
12486 Jim_FreeIntRep(interp, objPtr);
12487 objPtr->typePtr = &returnCodeObjType;
12488 objPtr->internalRep.intValue = returnCode;
12489 return JIM_OK;
12492 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
12494 if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
12495 return JIM_ERR;
12496 *intPtr = objPtr->internalRep.intValue;
12497 return JIM_OK;
12500 static int JimParseExprOperator(struct JimParserCtx *pc);
12501 static int JimParseExprNumber(struct JimParserCtx *pc);
12502 static int JimParseExprIrrational(struct JimParserCtx *pc);
12507 enum
12511 JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
12512 JIM_EXPROP_DIV,
12513 JIM_EXPROP_MOD,
12514 JIM_EXPROP_SUB,
12515 JIM_EXPROP_ADD,
12516 JIM_EXPROP_LSHIFT,
12517 JIM_EXPROP_RSHIFT,
12518 JIM_EXPROP_ROTL,
12519 JIM_EXPROP_ROTR,
12520 JIM_EXPROP_LT,
12521 JIM_EXPROP_GT,
12522 JIM_EXPROP_LTE,
12523 JIM_EXPROP_GTE,
12524 JIM_EXPROP_NUMEQ,
12525 JIM_EXPROP_NUMNE,
12526 JIM_EXPROP_BITAND,
12527 JIM_EXPROP_BITXOR,
12528 JIM_EXPROP_BITOR,
12531 JIM_EXPROP_LOGICAND,
12532 JIM_EXPROP_LOGICAND_LEFT,
12533 JIM_EXPROP_LOGICAND_RIGHT,
12536 JIM_EXPROP_LOGICOR,
12537 JIM_EXPROP_LOGICOR_LEFT,
12538 JIM_EXPROP_LOGICOR_RIGHT,
12542 JIM_EXPROP_TERNARY,
12543 JIM_EXPROP_TERNARY_LEFT,
12544 JIM_EXPROP_TERNARY_RIGHT,
12547 JIM_EXPROP_COLON,
12548 JIM_EXPROP_COLON_LEFT,
12549 JIM_EXPROP_COLON_RIGHT,
12551 JIM_EXPROP_POW,
12554 JIM_EXPROP_STREQ,
12555 JIM_EXPROP_STRNE,
12556 JIM_EXPROP_STRIN,
12557 JIM_EXPROP_STRNI,
12560 JIM_EXPROP_NOT,
12561 JIM_EXPROP_BITNOT,
12562 JIM_EXPROP_UNARYMINUS,
12563 JIM_EXPROP_UNARYPLUS,
12566 JIM_EXPROP_FUNC_FIRST,
12567 JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST,
12568 JIM_EXPROP_FUNC_WIDE,
12569 JIM_EXPROP_FUNC_ABS,
12570 JIM_EXPROP_FUNC_DOUBLE,
12571 JIM_EXPROP_FUNC_ROUND,
12572 JIM_EXPROP_FUNC_RAND,
12573 JIM_EXPROP_FUNC_SRAND,
12576 JIM_EXPROP_FUNC_SIN,
12577 JIM_EXPROP_FUNC_COS,
12578 JIM_EXPROP_FUNC_TAN,
12579 JIM_EXPROP_FUNC_ASIN,
12580 JIM_EXPROP_FUNC_ACOS,
12581 JIM_EXPROP_FUNC_ATAN,
12582 JIM_EXPROP_FUNC_SINH,
12583 JIM_EXPROP_FUNC_COSH,
12584 JIM_EXPROP_FUNC_TANH,
12585 JIM_EXPROP_FUNC_CEIL,
12586 JIM_EXPROP_FUNC_FLOOR,
12587 JIM_EXPROP_FUNC_EXP,
12588 JIM_EXPROP_FUNC_LOG,
12589 JIM_EXPROP_FUNC_LOG10,
12590 JIM_EXPROP_FUNC_SQRT,
12591 JIM_EXPROP_FUNC_POW,
12594 struct JimExprState
12596 Jim_Obj **stack;
12597 int stacklen;
12598 int opcode;
12599 int skip;
12603 typedef struct Jim_ExprOperator
12605 const char *name;
12606 int (*funcop) (Jim_Interp *interp, struct JimExprState * e);
12607 unsigned char precedence;
12608 unsigned char arity;
12609 unsigned char lazy;
12610 unsigned char namelen;
12611 } Jim_ExprOperator;
12613 static void ExprPush(struct JimExprState *e, Jim_Obj *obj)
12615 Jim_IncrRefCount(obj);
12616 e->stack[e->stacklen++] = obj;
12619 static Jim_Obj *ExprPop(struct JimExprState *e)
12621 return e->stack[--e->stacklen];
12624 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e)
12626 int intresult = 1;
12627 int rc = JIM_OK;
12628 Jim_Obj *A = ExprPop(e);
12629 double dA, dC = 0;
12630 jim_wide wA, wC = 0;
12632 if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
12633 switch (e->opcode) {
12634 case JIM_EXPROP_FUNC_INT:
12635 case JIM_EXPROP_FUNC_WIDE:
12636 case JIM_EXPROP_FUNC_ROUND:
12637 case JIM_EXPROP_UNARYPLUS:
12638 wC = wA;
12639 break;
12640 case JIM_EXPROP_FUNC_DOUBLE:
12641 dC = wA;
12642 intresult = 0;
12643 break;
12644 case JIM_EXPROP_FUNC_ABS:
12645 wC = wA >= 0 ? wA : -wA;
12646 break;
12647 case JIM_EXPROP_UNARYMINUS:
12648 wC = -wA;
12649 break;
12650 case JIM_EXPROP_NOT:
12651 wC = !wA;
12652 break;
12653 default:
12654 abort();
12657 else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
12658 switch (e->opcode) {
12659 case JIM_EXPROP_FUNC_INT:
12660 case JIM_EXPROP_FUNC_WIDE:
12661 wC = dA;
12662 break;
12663 case JIM_EXPROP_FUNC_ROUND:
12664 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
12665 break;
12666 case JIM_EXPROP_FUNC_DOUBLE:
12667 case JIM_EXPROP_UNARYPLUS:
12668 dC = dA;
12669 intresult = 0;
12670 break;
12671 case JIM_EXPROP_FUNC_ABS:
12672 dC = dA >= 0 ? dA : -dA;
12673 intresult = 0;
12674 break;
12675 case JIM_EXPROP_UNARYMINUS:
12676 dC = -dA;
12677 intresult = 0;
12678 break;
12679 case JIM_EXPROP_NOT:
12680 wC = !dA;
12681 break;
12682 default:
12683 abort();
12687 if (rc == JIM_OK) {
12688 if (intresult) {
12689 ExprPush(e, Jim_NewIntObj(interp, wC));
12691 else {
12692 ExprPush(e, Jim_NewDoubleObj(interp, dC));
12696 Jim_DecrRefCount(interp, A);
12698 return rc;
12701 static double JimRandDouble(Jim_Interp *interp)
12703 unsigned long x;
12704 JimRandomBytes(interp, &x, sizeof(x));
12706 return (double)x / (unsigned long)~0;
12709 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e)
12711 Jim_Obj *A = ExprPop(e);
12712 jim_wide wA;
12714 int rc = Jim_GetWide(interp, A, &wA);
12715 if (rc == JIM_OK) {
12716 switch (e->opcode) {
12717 case JIM_EXPROP_BITNOT:
12718 ExprPush(e, Jim_NewIntObj(interp, ~wA));
12719 break;
12720 case JIM_EXPROP_FUNC_SRAND:
12721 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
12722 ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12723 break;
12724 default:
12725 abort();
12729 Jim_DecrRefCount(interp, A);
12731 return rc;
12734 static int JimExprOpNone(Jim_Interp *interp, struct JimExprState *e)
12736 JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
12738 ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12740 return JIM_OK;
12743 #ifdef JIM_MATH_FUNCTIONS
12744 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e)
12746 int rc;
12747 Jim_Obj *A = ExprPop(e);
12748 double dA, dC;
12750 rc = Jim_GetDouble(interp, A, &dA);
12751 if (rc == JIM_OK) {
12752 switch (e->opcode) {
12753 case JIM_EXPROP_FUNC_SIN:
12754 dC = sin(dA);
12755 break;
12756 case JIM_EXPROP_FUNC_COS:
12757 dC = cos(dA);
12758 break;
12759 case JIM_EXPROP_FUNC_TAN:
12760 dC = tan(dA);
12761 break;
12762 case JIM_EXPROP_FUNC_ASIN:
12763 dC = asin(dA);
12764 break;
12765 case JIM_EXPROP_FUNC_ACOS:
12766 dC = acos(dA);
12767 break;
12768 case JIM_EXPROP_FUNC_ATAN:
12769 dC = atan(dA);
12770 break;
12771 case JIM_EXPROP_FUNC_SINH:
12772 dC = sinh(dA);
12773 break;
12774 case JIM_EXPROP_FUNC_COSH:
12775 dC = cosh(dA);
12776 break;
12777 case JIM_EXPROP_FUNC_TANH:
12778 dC = tanh(dA);
12779 break;
12780 case JIM_EXPROP_FUNC_CEIL:
12781 dC = ceil(dA);
12782 break;
12783 case JIM_EXPROP_FUNC_FLOOR:
12784 dC = floor(dA);
12785 break;
12786 case JIM_EXPROP_FUNC_EXP:
12787 dC = exp(dA);
12788 break;
12789 case JIM_EXPROP_FUNC_LOG:
12790 dC = log(dA);
12791 break;
12792 case JIM_EXPROP_FUNC_LOG10:
12793 dC = log10(dA);
12794 break;
12795 case JIM_EXPROP_FUNC_SQRT:
12796 dC = sqrt(dA);
12797 break;
12798 default:
12799 abort();
12801 ExprPush(e, Jim_NewDoubleObj(interp, dC));
12804 Jim_DecrRefCount(interp, A);
12806 return rc;
12808 #endif
12811 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e)
12813 Jim_Obj *B = ExprPop(e);
12814 Jim_Obj *A = ExprPop(e);
12815 jim_wide wA, wB;
12816 int rc = JIM_ERR;
12818 if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
12819 jim_wide wC;
12821 rc = JIM_OK;
12823 switch (e->opcode) {
12824 case JIM_EXPROP_LSHIFT:
12825 wC = wA << wB;
12826 break;
12827 case JIM_EXPROP_RSHIFT:
12828 wC = wA >> wB;
12829 break;
12830 case JIM_EXPROP_BITAND:
12831 wC = wA & wB;
12832 break;
12833 case JIM_EXPROP_BITXOR:
12834 wC = wA ^ wB;
12835 break;
12836 case JIM_EXPROP_BITOR:
12837 wC = wA | wB;
12838 break;
12839 case JIM_EXPROP_MOD:
12840 if (wB == 0) {
12841 wC = 0;
12842 Jim_SetResultString(interp, "Division by zero", -1);
12843 rc = JIM_ERR;
12845 else {
12846 int negative = 0;
12848 if (wB < 0) {
12849 wB = -wB;
12850 wA = -wA;
12851 negative = 1;
12853 wC = wA % wB;
12854 if (wC < 0) {
12855 wC += wB;
12857 if (negative) {
12858 wC = -wC;
12861 break;
12862 case JIM_EXPROP_ROTL:
12863 case JIM_EXPROP_ROTR:{
12865 unsigned long uA = (unsigned long)wA;
12866 unsigned long uB = (unsigned long)wB;
12867 const unsigned int S = sizeof(unsigned long) * 8;
12870 uB %= S;
12872 if (e->opcode == JIM_EXPROP_ROTR) {
12873 uB = S - uB;
12875 wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
12876 break;
12878 default:
12879 abort();
12881 ExprPush(e, Jim_NewIntObj(interp, wC));
12885 Jim_DecrRefCount(interp, A);
12886 Jim_DecrRefCount(interp, B);
12888 return rc;
12893 static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e)
12895 int intresult = 1;
12896 int rc = JIM_OK;
12897 double dA, dB, dC = 0;
12898 jim_wide wA, wB, wC = 0;
12900 Jim_Obj *B = ExprPop(e);
12901 Jim_Obj *A = ExprPop(e);
12903 if ((A->typePtr != &doubleObjType || A->bytes) &&
12904 (B->typePtr != &doubleObjType || B->bytes) &&
12905 JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
12909 switch (e->opcode) {
12910 case JIM_EXPROP_POW:
12911 case JIM_EXPROP_FUNC_POW:
12912 wC = JimPowWide(wA, wB);
12913 break;
12914 case JIM_EXPROP_ADD:
12915 wC = wA + wB;
12916 break;
12917 case JIM_EXPROP_SUB:
12918 wC = wA - wB;
12919 break;
12920 case JIM_EXPROP_MUL:
12921 wC = wA * wB;
12922 break;
12923 case JIM_EXPROP_DIV:
12924 if (wB == 0) {
12925 Jim_SetResultString(interp, "Division by zero", -1);
12926 rc = JIM_ERR;
12928 else {
12929 if (wB < 0) {
12930 wB = -wB;
12931 wA = -wA;
12933 wC = wA / wB;
12934 if (wA % wB < 0) {
12935 wC--;
12938 break;
12939 case JIM_EXPROP_LT:
12940 wC = wA < wB;
12941 break;
12942 case JIM_EXPROP_GT:
12943 wC = wA > wB;
12944 break;
12945 case JIM_EXPROP_LTE:
12946 wC = wA <= wB;
12947 break;
12948 case JIM_EXPROP_GTE:
12949 wC = wA >= wB;
12950 break;
12951 case JIM_EXPROP_NUMEQ:
12952 wC = wA == wB;
12953 break;
12954 case JIM_EXPROP_NUMNE:
12955 wC = wA != wB;
12956 break;
12957 default:
12958 abort();
12961 else if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
12962 intresult = 0;
12963 switch (e->opcode) {
12964 case JIM_EXPROP_POW:
12965 case JIM_EXPROP_FUNC_POW:
12966 #ifdef JIM_MATH_FUNCTIONS
12967 dC = pow(dA, dB);
12968 #else
12969 Jim_SetResultString(interp, "unsupported", -1);
12970 rc = JIM_ERR;
12971 #endif
12972 break;
12973 case JIM_EXPROP_ADD:
12974 dC = dA + dB;
12975 break;
12976 case JIM_EXPROP_SUB:
12977 dC = dA - dB;
12978 break;
12979 case JIM_EXPROP_MUL:
12980 dC = dA * dB;
12981 break;
12982 case JIM_EXPROP_DIV:
12983 if (dB == 0) {
12984 #ifdef INFINITY
12985 dC = dA < 0 ? -INFINITY : INFINITY;
12986 #else
12987 dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
12988 #endif
12990 else {
12991 dC = dA / dB;
12993 break;
12994 case JIM_EXPROP_LT:
12995 wC = dA < dB;
12996 intresult = 1;
12997 break;
12998 case JIM_EXPROP_GT:
12999 wC = dA > dB;
13000 intresult = 1;
13001 break;
13002 case JIM_EXPROP_LTE:
13003 wC = dA <= dB;
13004 intresult = 1;
13005 break;
13006 case JIM_EXPROP_GTE:
13007 wC = dA >= dB;
13008 intresult = 1;
13009 break;
13010 case JIM_EXPROP_NUMEQ:
13011 wC = dA == dB;
13012 intresult = 1;
13013 break;
13014 case JIM_EXPROP_NUMNE:
13015 wC = dA != dB;
13016 intresult = 1;
13017 break;
13018 default:
13019 abort();
13022 else {
13026 int i = Jim_StringCompareObj(interp, A, B, 0);
13028 switch (e->opcode) {
13029 case JIM_EXPROP_LT:
13030 wC = i < 0;
13031 break;
13032 case JIM_EXPROP_GT:
13033 wC = i > 0;
13034 break;
13035 case JIM_EXPROP_LTE:
13036 wC = i <= 0;
13037 break;
13038 case JIM_EXPROP_GTE:
13039 wC = i >= 0;
13040 break;
13041 case JIM_EXPROP_NUMEQ:
13042 wC = i == 0;
13043 break;
13044 case JIM_EXPROP_NUMNE:
13045 wC = i != 0;
13046 break;
13047 default:
13048 rc = JIM_ERR;
13049 break;
13053 if (rc == JIM_OK) {
13054 if (intresult) {
13055 ExprPush(e, Jim_NewIntObj(interp, wC));
13057 else {
13058 ExprPush(e, Jim_NewDoubleObj(interp, dC));
13062 Jim_DecrRefCount(interp, A);
13063 Jim_DecrRefCount(interp, B);
13065 return rc;
13068 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
13070 int listlen;
13071 int i;
13073 listlen = Jim_ListLength(interp, listObjPtr);
13074 for (i = 0; i < listlen; i++) {
13075 if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
13076 return 1;
13079 return 0;
13082 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e)
13084 Jim_Obj *B = ExprPop(e);
13085 Jim_Obj *A = ExprPop(e);
13087 jim_wide wC;
13089 switch (e->opcode) {
13090 case JIM_EXPROP_STREQ:
13091 case JIM_EXPROP_STRNE:
13092 wC = Jim_StringEqObj(A, B);
13093 if (e->opcode == JIM_EXPROP_STRNE) {
13094 wC = !wC;
13096 break;
13097 case JIM_EXPROP_STRIN:
13098 wC = JimSearchList(interp, B, A);
13099 break;
13100 case JIM_EXPROP_STRNI:
13101 wC = !JimSearchList(interp, B, A);
13102 break;
13103 default:
13104 abort();
13106 ExprPush(e, Jim_NewIntObj(interp, wC));
13108 Jim_DecrRefCount(interp, A);
13109 Jim_DecrRefCount(interp, B);
13111 return JIM_OK;
13114 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
13116 long l;
13117 double d;
13119 if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
13120 return l != 0;
13122 if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
13123 return d != 0;
13125 return -1;
13128 static int JimExprOpAndLeft(Jim_Interp *interp, struct JimExprState *e)
13130 Jim_Obj *skip = ExprPop(e);
13131 Jim_Obj *A = ExprPop(e);
13132 int rc = JIM_OK;
13134 switch (ExprBool(interp, A)) {
13135 case 0:
13137 e->skip = JimWideValue(skip);
13138 ExprPush(e, Jim_NewIntObj(interp, 0));
13139 break;
13141 case 1:
13143 break;
13145 case -1:
13147 rc = JIM_ERR;
13149 Jim_DecrRefCount(interp, A);
13150 Jim_DecrRefCount(interp, skip);
13152 return rc;
13155 static int JimExprOpOrLeft(Jim_Interp *interp, struct JimExprState *e)
13157 Jim_Obj *skip = ExprPop(e);
13158 Jim_Obj *A = ExprPop(e);
13159 int rc = JIM_OK;
13161 switch (ExprBool(interp, A)) {
13162 case 0:
13164 break;
13166 case 1:
13168 e->skip = JimWideValue(skip);
13169 ExprPush(e, Jim_NewIntObj(interp, 1));
13170 break;
13172 case -1:
13174 rc = JIM_ERR;
13175 break;
13177 Jim_DecrRefCount(interp, A);
13178 Jim_DecrRefCount(interp, skip);
13180 return rc;
13183 static int JimExprOpAndOrRight(Jim_Interp *interp, struct JimExprState *e)
13185 Jim_Obj *A = ExprPop(e);
13186 int rc = JIM_OK;
13188 switch (ExprBool(interp, A)) {
13189 case 0:
13190 ExprPush(e, Jim_NewIntObj(interp, 0));
13191 break;
13193 case 1:
13194 ExprPush(e, Jim_NewIntObj(interp, 1));
13195 break;
13197 case -1:
13199 rc = JIM_ERR;
13200 break;
13202 Jim_DecrRefCount(interp, A);
13204 return rc;
13207 static int JimExprOpTernaryLeft(Jim_Interp *interp, struct JimExprState *e)
13209 Jim_Obj *skip = ExprPop(e);
13210 Jim_Obj *A = ExprPop(e);
13211 int rc = JIM_OK;
13214 ExprPush(e, A);
13216 switch (ExprBool(interp, A)) {
13217 case 0:
13219 e->skip = JimWideValue(skip);
13221 ExprPush(e, Jim_NewIntObj(interp, 0));
13222 break;
13224 case 1:
13226 break;
13228 case -1:
13230 rc = JIM_ERR;
13231 break;
13233 Jim_DecrRefCount(interp, A);
13234 Jim_DecrRefCount(interp, skip);
13236 return rc;
13239 static int JimExprOpColonLeft(Jim_Interp *interp, struct JimExprState *e)
13241 Jim_Obj *skip = ExprPop(e);
13242 Jim_Obj *B = ExprPop(e);
13243 Jim_Obj *A = ExprPop(e);
13246 if (ExprBool(interp, A)) {
13248 e->skip = JimWideValue(skip);
13250 ExprPush(e, B);
13253 Jim_DecrRefCount(interp, skip);
13254 Jim_DecrRefCount(interp, A);
13255 Jim_DecrRefCount(interp, B);
13256 return JIM_OK;
13259 static int JimExprOpNull(Jim_Interp *interp, struct JimExprState *e)
13261 return JIM_OK;
13264 enum
13266 LAZY_NONE,
13267 LAZY_OP,
13268 LAZY_LEFT,
13269 LAZY_RIGHT
13272 #define OPRINIT(N, P, A, F) {N, F, P, A, LAZY_NONE, sizeof(N) - 1}
13273 #define OPRINIT_LAZY(N, P, A, F, L) {N, F, P, A, L, sizeof(N) - 1}
13275 static const struct Jim_ExprOperator Jim_ExprOperators[] = {
13276 OPRINIT("*", 110, 2, JimExprOpBin),
13277 OPRINIT("/", 110, 2, JimExprOpBin),
13278 OPRINIT("%", 110, 2, JimExprOpIntBin),
13280 OPRINIT("-", 100, 2, JimExprOpBin),
13281 OPRINIT("+", 100, 2, JimExprOpBin),
13283 OPRINIT("<<", 90, 2, JimExprOpIntBin),
13284 OPRINIT(">>", 90, 2, JimExprOpIntBin),
13286 OPRINIT("<<<", 90, 2, JimExprOpIntBin),
13287 OPRINIT(">>>", 90, 2, JimExprOpIntBin),
13289 OPRINIT("<", 80, 2, JimExprOpBin),
13290 OPRINIT(">", 80, 2, JimExprOpBin),
13291 OPRINIT("<=", 80, 2, JimExprOpBin),
13292 OPRINIT(">=", 80, 2, JimExprOpBin),
13294 OPRINIT("==", 70, 2, JimExprOpBin),
13295 OPRINIT("!=", 70, 2, JimExprOpBin),
13297 OPRINIT("&", 50, 2, JimExprOpIntBin),
13298 OPRINIT("^", 49, 2, JimExprOpIntBin),
13299 OPRINIT("|", 48, 2, JimExprOpIntBin),
13301 OPRINIT_LAZY("&&", 10, 2, NULL, LAZY_OP),
13302 OPRINIT_LAZY(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT),
13303 OPRINIT_LAZY(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT),
13305 OPRINIT_LAZY("||", 9, 2, NULL, LAZY_OP),
13306 OPRINIT_LAZY(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT),
13307 OPRINIT_LAZY(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT),
13309 OPRINIT_LAZY("?", 5, 2, JimExprOpNull, LAZY_OP),
13310 OPRINIT_LAZY(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT),
13311 OPRINIT_LAZY(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
13313 OPRINIT_LAZY(":", 5, 2, JimExprOpNull, LAZY_OP),
13314 OPRINIT_LAZY(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT),
13315 OPRINIT_LAZY(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),
13317 OPRINIT("**", 250, 2, JimExprOpBin),
13319 OPRINIT("eq", 60, 2, JimExprOpStrBin),
13320 OPRINIT("ne", 60, 2, JimExprOpStrBin),
13322 OPRINIT("in", 55, 2, JimExprOpStrBin),
13323 OPRINIT("ni", 55, 2, JimExprOpStrBin),
13325 OPRINIT("!", 150, 1, JimExprOpNumUnary),
13326 OPRINIT("~", 150, 1, JimExprOpIntUnary),
13327 OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
13328 OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
13332 OPRINIT("int", 200, 1, JimExprOpNumUnary),
13333 OPRINIT("wide", 200, 1, JimExprOpNumUnary),
13334 OPRINIT("abs", 200, 1, JimExprOpNumUnary),
13335 OPRINIT("double", 200, 1, JimExprOpNumUnary),
13336 OPRINIT("round", 200, 1, JimExprOpNumUnary),
13337 OPRINIT("rand", 200, 0, JimExprOpNone),
13338 OPRINIT("srand", 200, 1, JimExprOpIntUnary),
13340 #ifdef JIM_MATH_FUNCTIONS
13341 OPRINIT("sin", 200, 1, JimExprOpDoubleUnary),
13342 OPRINIT("cos", 200, 1, JimExprOpDoubleUnary),
13343 OPRINIT("tan", 200, 1, JimExprOpDoubleUnary),
13344 OPRINIT("asin", 200, 1, JimExprOpDoubleUnary),
13345 OPRINIT("acos", 200, 1, JimExprOpDoubleUnary),
13346 OPRINIT("atan", 200, 1, JimExprOpDoubleUnary),
13347 OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary),
13348 OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary),
13349 OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary),
13350 OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary),
13351 OPRINIT("floor", 200, 1, JimExprOpDoubleUnary),
13352 OPRINIT("exp", 200, 1, JimExprOpDoubleUnary),
13353 OPRINIT("log", 200, 1, JimExprOpDoubleUnary),
13354 OPRINIT("log10", 200, 1, JimExprOpDoubleUnary),
13355 OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary),
13356 OPRINIT("pow", 200, 2, JimExprOpBin),
13357 #endif
13359 #undef OPRINIT
13360 #undef OPRINIT_LAZY
13362 #define JIM_EXPR_OPERATORS_NUM \
13363 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
13365 static int JimParseExpression(struct JimParserCtx *pc)
13368 while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
13369 if (*pc->p == '\n') {
13370 pc->linenr++;
13372 pc->p++;
13373 pc->len--;
13377 pc->tline = pc->linenr;
13378 pc->tstart = pc->p;
13380 if (pc->len == 0) {
13381 pc->tend = pc->p;
13382 pc->tt = JIM_TT_EOL;
13383 pc->eof = 1;
13384 return JIM_OK;
13386 switch (*(pc->p)) {
13387 case '(':
13388 pc->tt = JIM_TT_SUBEXPR_START;
13389 goto singlechar;
13390 case ')':
13391 pc->tt = JIM_TT_SUBEXPR_END;
13392 goto singlechar;
13393 case ',':
13394 pc->tt = JIM_TT_SUBEXPR_COMMA;
13395 singlechar:
13396 pc->tend = pc->p;
13397 pc->p++;
13398 pc->len--;
13399 break;
13400 case '[':
13401 return JimParseCmd(pc);
13402 case '$':
13403 if (JimParseVar(pc) == JIM_ERR)
13404 return JimParseExprOperator(pc);
13405 else {
13407 if (pc->tt == JIM_TT_EXPRSUGAR) {
13408 return JIM_ERR;
13410 return JIM_OK;
13412 break;
13413 case '0':
13414 case '1':
13415 case '2':
13416 case '3':
13417 case '4':
13418 case '5':
13419 case '6':
13420 case '7':
13421 case '8':
13422 case '9':
13423 case '.':
13424 return JimParseExprNumber(pc);
13425 case '"':
13426 return JimParseQuote(pc);
13427 case '{':
13428 return JimParseBrace(pc);
13430 case 'N':
13431 case 'I':
13432 case 'n':
13433 case 'i':
13434 if (JimParseExprIrrational(pc) == JIM_ERR)
13435 return JimParseExprOperator(pc);
13436 break;
13437 default:
13438 return JimParseExprOperator(pc);
13439 break;
13441 return JIM_OK;
13444 static int JimParseExprNumber(struct JimParserCtx *pc)
13446 char *end;
13449 pc->tt = JIM_TT_EXPR_INT;
13451 jim_strtoull(pc->p, (char **)&pc->p);
13453 if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
13454 if (strtod(pc->tstart, &end)) { }
13455 if (end == pc->tstart)
13456 return JIM_ERR;
13457 if (end > pc->p) {
13459 pc->tt = JIM_TT_EXPR_DOUBLE;
13460 pc->p = end;
13463 pc->tend = pc->p - 1;
13464 pc->len -= (pc->p - pc->tstart);
13465 return JIM_OK;
13468 static int JimParseExprIrrational(struct JimParserCtx *pc)
13470 const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
13471 int i;
13473 for (i = 0; irrationals[i]; i++) {
13474 const char *irr = irrationals[i];
13476 if (strncmp(irr, pc->p, 3) == 0) {
13477 pc->p += 3;
13478 pc->len -= 3;
13479 pc->tend = pc->p - 1;
13480 pc->tt = JIM_TT_EXPR_DOUBLE;
13481 return JIM_OK;
13484 return JIM_ERR;
13487 static int JimParseExprOperator(struct JimParserCtx *pc)
13489 int i;
13490 int bestIdx = -1, bestLen = 0;
13493 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
13494 const char * const opname = Jim_ExprOperators[i].name;
13495 const int oplen = Jim_ExprOperators[i].namelen;
13497 if (opname == NULL || opname[0] != pc->p[0]) {
13498 continue;
13501 if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) {
13502 bestIdx = i + JIM_TT_EXPR_OP;
13503 bestLen = oplen;
13506 if (bestIdx == -1) {
13507 return JIM_ERR;
13511 if (bestIdx >= JIM_EXPROP_FUNC_FIRST) {
13512 const char *p = pc->p + bestLen;
13513 int len = pc->len - bestLen;
13515 while (len && isspace(UCHAR(*p))) {
13516 len--;
13517 p++;
13519 if (*p != '(') {
13520 return JIM_ERR;
13523 pc->tend = pc->p + bestLen - 1;
13524 pc->p += bestLen;
13525 pc->len -= bestLen;
13527 pc->tt = bestIdx;
13528 return JIM_OK;
13531 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
13533 static Jim_ExprOperator dummy_op;
13534 if (opcode < JIM_TT_EXPR_OP) {
13535 return &dummy_op;
13537 return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
13540 const char *jim_tt_name(int type)
13542 static const char * const tt_names[JIM_TT_EXPR_OP] =
13543 { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT",
13544 "DBL", "$()" };
13545 if (type < JIM_TT_EXPR_OP) {
13546 return tt_names[type];
13548 else {
13549 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(type);
13550 static char buf[20];
13552 if (op->name) {
13553 return op->name;
13555 sprintf(buf, "(%d)", type);
13556 return buf;
13560 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
13561 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
13562 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13564 static const Jim_ObjType exprObjType = {
13565 "expression",
13566 FreeExprInternalRep,
13567 DupExprInternalRep,
13568 NULL,
13569 JIM_TYPE_REFERENCES,
13573 typedef struct ExprByteCode
13575 ScriptToken *token;
13576 int len;
13577 int inUse;
13578 } ExprByteCode;
13580 static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr)
13582 int i;
13584 for (i = 0; i < expr->len; i++) {
13585 Jim_DecrRefCount(interp, expr->token[i].objPtr);
13587 Jim_Free(expr->token);
13588 Jim_Free(expr);
13591 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
13593 ExprByteCode *expr = (void *)objPtr->internalRep.ptr;
13595 if (expr) {
13596 if (--expr->inUse != 0) {
13597 return;
13600 ExprFreeByteCode(interp, expr);
13604 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
13606 JIM_NOTUSED(interp);
13607 JIM_NOTUSED(srcPtr);
13610 dupPtr->typePtr = NULL;
13614 static int ExprCheckCorrectness(ExprByteCode * expr)
13616 int i;
13617 int stacklen = 0;
13618 int ternary = 0;
13620 for (i = 0; i < expr->len; i++) {
13621 ScriptToken *t = &expr->token[i];
13622 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13624 stacklen -= op->arity;
13625 if (stacklen < 0) {
13626 break;
13628 if (t->type == JIM_EXPROP_TERNARY || t->type == JIM_EXPROP_TERNARY_LEFT) {
13629 ternary++;
13631 else if (t->type == JIM_EXPROP_COLON || t->type == JIM_EXPROP_COLON_LEFT) {
13632 ternary--;
13636 stacklen++;
13638 if (stacklen != 1 || ternary != 0) {
13639 return JIM_ERR;
13641 return JIM_OK;
13644 static int ExprAddLazyOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
13646 int i;
13648 int leftindex, arity, offset;
13651 leftindex = expr->len - 1;
13653 arity = 1;
13654 while (arity) {
13655 ScriptToken *tt = &expr->token[leftindex];
13657 if (tt->type >= JIM_TT_EXPR_OP) {
13658 arity += JimExprOperatorInfoByOpcode(tt->type)->arity;
13660 arity--;
13661 if (--leftindex < 0) {
13662 return JIM_ERR;
13665 leftindex++;
13668 memmove(&expr->token[leftindex + 2], &expr->token[leftindex],
13669 sizeof(*expr->token) * (expr->len - leftindex));
13670 expr->len += 2;
13671 offset = (expr->len - leftindex) - 1;
13673 expr->token[leftindex + 1].type = t->type + 1;
13674 expr->token[leftindex + 1].objPtr = interp->emptyObj;
13676 expr->token[leftindex].type = JIM_TT_EXPR_INT;
13677 expr->token[leftindex].objPtr = Jim_NewIntObj(interp, offset);
13680 expr->token[expr->len].objPtr = interp->emptyObj;
13681 expr->token[expr->len].type = t->type + 2;
13682 expr->len++;
13685 for (i = leftindex - 1; i > 0; i--) {
13686 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(expr->token[i].type);
13687 if (op->lazy == LAZY_LEFT) {
13688 if (JimWideValue(expr->token[i - 1].objPtr) + i - 1 >= leftindex) {
13689 JimWideValue(expr->token[i - 1].objPtr) += 2;
13693 return JIM_OK;
13696 static int ExprAddOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
13698 struct ScriptToken *token = &expr->token[expr->len];
13699 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13701 if (op->lazy == LAZY_OP) {
13702 if (ExprAddLazyOperator(interp, expr, t) != JIM_OK) {
13703 Jim_SetResultFormatted(interp, "Expression has bad operands to %s", op->name);
13704 return JIM_ERR;
13707 else {
13708 token->objPtr = interp->emptyObj;
13709 token->type = t->type;
13710 expr->len++;
13712 return JIM_OK;
13715 static int ExprTernaryGetColonLeftIndex(ExprByteCode *expr, int right_index)
13717 int ternary_count = 1;
13719 right_index--;
13721 while (right_index > 1) {
13722 if (expr->token[right_index].type == JIM_EXPROP_TERNARY_LEFT) {
13723 ternary_count--;
13725 else if (expr->token[right_index].type == JIM_EXPROP_COLON_RIGHT) {
13726 ternary_count++;
13728 else if (expr->token[right_index].type == JIM_EXPROP_COLON_LEFT && ternary_count == 1) {
13729 return right_index;
13731 right_index--;
13735 return -1;
13738 static int ExprTernaryGetMoveIndices(ExprByteCode *expr, int right_index, int *prev_right_index, int *prev_left_index)
13740 int i = right_index - 1;
13741 int ternary_count = 1;
13743 while (i > 1) {
13744 if (expr->token[i].type == JIM_EXPROP_TERNARY_LEFT) {
13745 if (--ternary_count == 0 && expr->token[i - 2].type == JIM_EXPROP_COLON_RIGHT) {
13746 *prev_right_index = i - 2;
13747 *prev_left_index = ExprTernaryGetColonLeftIndex(expr, *prev_right_index);
13748 return 1;
13751 else if (expr->token[i].type == JIM_EXPROP_COLON_RIGHT) {
13752 if (ternary_count == 0) {
13753 return 0;
13755 ternary_count++;
13757 i--;
13759 return 0;
13762 static void ExprTernaryReorderExpression(Jim_Interp *interp, ExprByteCode *expr)
13764 int i;
13766 for (i = expr->len - 1; i > 1; i--) {
13767 int prev_right_index;
13768 int prev_left_index;
13769 int j;
13770 ScriptToken tmp;
13772 if (expr->token[i].type != JIM_EXPROP_COLON_RIGHT) {
13773 continue;
13777 if (ExprTernaryGetMoveIndices(expr, i, &prev_right_index, &prev_left_index) == 0) {
13778 continue;
13781 tmp = expr->token[prev_right_index];
13782 for (j = prev_right_index; j < i; j++) {
13783 expr->token[j] = expr->token[j + 1];
13785 expr->token[i] = tmp;
13787 JimWideValue(expr->token[prev_left_index-1].objPtr) += (i - prev_right_index);
13790 i++;
13794 static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *fileNameObj)
13796 Jim_Stack stack;
13797 ExprByteCode *expr;
13798 int ok = 1;
13799 int i;
13800 int prevtt = JIM_TT_NONE;
13801 int have_ternary = 0;
13804 int count = tokenlist->count - 1;
13806 expr = Jim_Alloc(sizeof(*expr));
13807 expr->inUse = 1;
13808 expr->len = 0;
13810 Jim_InitStack(&stack);
13812 for (i = 0; i < tokenlist->count; i++) {
13813 ParseToken *t = &tokenlist->list[i];
13814 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
13816 if (op->lazy == LAZY_OP) {
13817 count += 2;
13819 if (t->type == JIM_EXPROP_TERNARY) {
13820 have_ternary = 1;
13825 expr->token = Jim_Alloc(sizeof(ScriptToken) * count);
13827 for (i = 0; i < tokenlist->count && ok; i++) {
13828 ParseToken *t = &tokenlist->list[i];
13831 struct ScriptToken *token = &expr->token[expr->len];
13833 if (t->type == JIM_TT_EOL) {
13834 break;
13837 switch (t->type) {
13838 case JIM_TT_STR:
13839 case JIM_TT_ESC:
13840 case JIM_TT_VAR:
13841 case JIM_TT_DICTSUGAR:
13842 case JIM_TT_EXPRSUGAR:
13843 case JIM_TT_CMD:
13844 token->type = t->type;
13845 strexpr:
13846 token->objPtr = Jim_NewStringObj(interp, t->token, t->len);
13847 if (t->type == JIM_TT_CMD) {
13849 JimSetSourceInfo(interp, token->objPtr, fileNameObj, t->line);
13851 expr->len++;
13852 break;
13854 case JIM_TT_EXPR_INT:
13855 case JIM_TT_EXPR_DOUBLE:
13857 char *endptr;
13858 if (t->type == JIM_TT_EXPR_INT) {
13859 token->objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
13861 else {
13862 token->objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
13864 if (endptr != t->token + t->len) {
13866 Jim_FreeNewObj(interp, token->objPtr);
13867 token->type = JIM_TT_STR;
13868 goto strexpr;
13870 token->type = t->type;
13871 expr->len++;
13873 break;
13875 case JIM_TT_SUBEXPR_START:
13876 Jim_StackPush(&stack, t);
13877 prevtt = JIM_TT_NONE;
13878 continue;
13880 case JIM_TT_SUBEXPR_COMMA:
13882 continue;
13884 case JIM_TT_SUBEXPR_END:
13885 ok = 0;
13886 while (Jim_StackLen(&stack)) {
13887 ParseToken *tt = Jim_StackPop(&stack);
13889 if (tt->type == JIM_TT_SUBEXPR_START) {
13890 ok = 1;
13891 break;
13894 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13895 goto err;
13898 if (!ok) {
13899 Jim_SetResultString(interp, "Unexpected close parenthesis", -1);
13900 goto err;
13902 break;
13905 default:{
13907 const struct Jim_ExprOperator *op;
13908 ParseToken *tt;
13911 if (prevtt == JIM_TT_NONE || prevtt >= JIM_TT_EXPR_OP) {
13912 if (t->type == JIM_EXPROP_SUB) {
13913 t->type = JIM_EXPROP_UNARYMINUS;
13915 else if (t->type == JIM_EXPROP_ADD) {
13916 t->type = JIM_EXPROP_UNARYPLUS;
13920 op = JimExprOperatorInfoByOpcode(t->type);
13923 while ((tt = Jim_StackPeek(&stack)) != NULL) {
13924 const struct Jim_ExprOperator *tt_op =
13925 JimExprOperatorInfoByOpcode(tt->type);
13929 if (op->arity != 1 && tt_op->precedence >= op->precedence) {
13930 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13931 ok = 0;
13932 goto err;
13934 Jim_StackPop(&stack);
13936 else {
13937 break;
13940 Jim_StackPush(&stack, t);
13941 break;
13944 prevtt = t->type;
13948 while (Jim_StackLen(&stack)) {
13949 ParseToken *tt = Jim_StackPop(&stack);
13951 if (tt->type == JIM_TT_SUBEXPR_START) {
13952 ok = 0;
13953 Jim_SetResultString(interp, "Missing close parenthesis", -1);
13954 goto err;
13956 if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
13957 ok = 0;
13958 goto err;
13962 if (have_ternary) {
13963 ExprTernaryReorderExpression(interp, expr);
13966 err:
13968 Jim_FreeStack(&stack);
13970 for (i = 0; i < expr->len; i++) {
13971 Jim_IncrRefCount(expr->token[i].objPtr);
13974 if (!ok) {
13975 ExprFreeByteCode(interp, expr);
13976 return NULL;
13979 return expr;
13983 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
13985 int exprTextLen;
13986 const char *exprText;
13987 struct JimParserCtx parser;
13988 struct ExprByteCode *expr;
13989 ParseTokenList tokenlist;
13990 int line;
13991 Jim_Obj *fileNameObj;
13992 int rc = JIM_ERR;
13995 if (objPtr->typePtr == &sourceObjType) {
13996 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
13997 line = objPtr->internalRep.sourceValue.lineNumber;
13999 else {
14000 fileNameObj = interp->emptyObj;
14001 line = 1;
14003 Jim_IncrRefCount(fileNameObj);
14005 exprText = Jim_GetString(objPtr, &exprTextLen);
14008 ScriptTokenListInit(&tokenlist);
14010 JimParserInit(&parser, exprText, exprTextLen, line);
14011 while (!parser.eof) {
14012 if (JimParseExpression(&parser) != JIM_OK) {
14013 ScriptTokenListFree(&tokenlist);
14014 invalidexpr:
14015 Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
14016 expr = NULL;
14017 goto err;
14020 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
14021 parser.tline);
14024 #ifdef DEBUG_SHOW_EXPR_TOKENS
14026 int i;
14027 printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
14028 for (i = 0; i < tokenlist.count; i++) {
14029 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
14030 tokenlist.list[i].len, tokenlist.list[i].token);
14033 #endif
14035 if (JimParseCheckMissing(interp, parser.missing.ch) == JIM_ERR) {
14036 ScriptTokenListFree(&tokenlist);
14037 Jim_DecrRefCount(interp, fileNameObj);
14038 return JIM_ERR;
14042 expr = ExprCreateByteCode(interp, &tokenlist, fileNameObj);
14045 ScriptTokenListFree(&tokenlist);
14047 if (!expr) {
14048 goto err;
14051 #ifdef DEBUG_SHOW_EXPR
14053 int i;
14055 printf("==== Expr ====\n");
14056 for (i = 0; i < expr->len; i++) {
14057 ScriptToken *t = &expr->token[i];
14059 printf("[%2d] %s '%s'\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
14062 #endif
14065 if (ExprCheckCorrectness(expr) != JIM_OK) {
14066 ExprFreeByteCode(interp, expr);
14067 goto invalidexpr;
14070 rc = JIM_OK;
14072 err:
14074 Jim_DecrRefCount(interp, fileNameObj);
14075 Jim_FreeIntRep(interp, objPtr);
14076 Jim_SetIntRepPtr(objPtr, expr);
14077 objPtr->typePtr = &exprObjType;
14078 return rc;
14081 static ExprByteCode *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
14083 if (objPtr->typePtr != &exprObjType) {
14084 if (SetExprFromAny(interp, objPtr) != JIM_OK) {
14085 return NULL;
14088 return (ExprByteCode *) Jim_GetIntRepPtr(objPtr);
14091 #ifdef JIM_OPTIMIZATION
14092 static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, const ScriptToken *token)
14094 if (token->type == JIM_TT_EXPR_INT)
14095 return token->objPtr;
14096 else if (token->type == JIM_TT_VAR)
14097 return Jim_GetVariable(interp, token->objPtr, JIM_NONE);
14098 else if (token->type == JIM_TT_DICTSUGAR)
14099 return JimExpandDictSugar(interp, token->objPtr);
14100 else
14101 return NULL;
14103 #endif
14105 #define JIM_EE_STATICSTACK_LEN 10
14107 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr)
14109 ExprByteCode *expr;
14110 Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN];
14111 int i;
14112 int retcode = JIM_OK;
14113 struct JimExprState e;
14115 expr = JimGetExpression(interp, exprObjPtr);
14116 if (!expr) {
14117 return JIM_ERR;
14120 #ifdef JIM_OPTIMIZATION
14122 Jim_Obj *objPtr;
14125 switch (expr->len) {
14126 case 1:
14127 objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
14128 if (objPtr) {
14129 Jim_IncrRefCount(objPtr);
14130 *exprResultPtrPtr = objPtr;
14131 return JIM_OK;
14133 break;
14135 case 2:
14136 if (expr->token[1].type == JIM_EXPROP_NOT) {
14137 objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
14139 if (objPtr && JimIsWide(objPtr)) {
14140 *exprResultPtrPtr = JimWideValue(objPtr) ? interp->falseObj : interp->trueObj;
14141 Jim_IncrRefCount(*exprResultPtrPtr);
14142 return JIM_OK;
14145 break;
14147 case 3:
14148 objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
14149 if (objPtr && JimIsWide(objPtr)) {
14150 Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, &expr->token[1]);
14151 if (objPtr2 && JimIsWide(objPtr2)) {
14152 jim_wide wideValueA = JimWideValue(objPtr);
14153 jim_wide wideValueB = JimWideValue(objPtr2);
14154 int cmpRes;
14155 switch (expr->token[2].type) {
14156 case JIM_EXPROP_LT:
14157 cmpRes = wideValueA < wideValueB;
14158 break;
14159 case JIM_EXPROP_LTE:
14160 cmpRes = wideValueA <= wideValueB;
14161 break;
14162 case JIM_EXPROP_GT:
14163 cmpRes = wideValueA > wideValueB;
14164 break;
14165 case JIM_EXPROP_GTE:
14166 cmpRes = wideValueA >= wideValueB;
14167 break;
14168 case JIM_EXPROP_NUMEQ:
14169 cmpRes = wideValueA == wideValueB;
14170 break;
14171 case JIM_EXPROP_NUMNE:
14172 cmpRes = wideValueA != wideValueB;
14173 break;
14174 default:
14175 goto noopt;
14177 *exprResultPtrPtr = cmpRes ? interp->trueObj : interp->falseObj;
14178 Jim_IncrRefCount(*exprResultPtrPtr);
14179 return JIM_OK;
14182 break;
14185 noopt:
14186 #endif
14188 expr->inUse++;
14192 if (expr->len > JIM_EE_STATICSTACK_LEN)
14193 e.stack = Jim_Alloc(sizeof(Jim_Obj *) * expr->len);
14194 else
14195 e.stack = staticStack;
14197 e.stacklen = 0;
14200 for (i = 0; i < expr->len && retcode == JIM_OK; i++) {
14201 Jim_Obj *objPtr;
14203 switch (expr->token[i].type) {
14204 case JIM_TT_EXPR_INT:
14205 case JIM_TT_EXPR_DOUBLE:
14206 case JIM_TT_STR:
14207 ExprPush(&e, expr->token[i].objPtr);
14208 break;
14210 case JIM_TT_VAR:
14211 objPtr = Jim_GetVariable(interp, expr->token[i].objPtr, JIM_ERRMSG);
14212 if (objPtr) {
14213 ExprPush(&e, objPtr);
14215 else {
14216 retcode = JIM_ERR;
14218 break;
14220 case JIM_TT_DICTSUGAR:
14221 objPtr = JimExpandDictSugar(interp, expr->token[i].objPtr);
14222 if (objPtr) {
14223 ExprPush(&e, objPtr);
14225 else {
14226 retcode = JIM_ERR;
14228 break;
14230 case JIM_TT_ESC:
14231 retcode = Jim_SubstObj(interp, expr->token[i].objPtr, &objPtr, JIM_NONE);
14232 if (retcode == JIM_OK) {
14233 ExprPush(&e, objPtr);
14235 break;
14237 case JIM_TT_CMD:
14238 retcode = Jim_EvalObj(interp, expr->token[i].objPtr);
14239 if (retcode == JIM_OK) {
14240 ExprPush(&e, Jim_GetResult(interp));
14242 break;
14244 default:{
14246 e.skip = 0;
14247 e.opcode = expr->token[i].type;
14249 retcode = JimExprOperatorInfoByOpcode(e.opcode)->funcop(interp, &e);
14251 i += e.skip;
14252 continue;
14257 expr->inUse--;
14259 if (retcode == JIM_OK) {
14260 *exprResultPtrPtr = ExprPop(&e);
14262 else {
14263 for (i = 0; i < e.stacklen; i++) {
14264 Jim_DecrRefCount(interp, e.stack[i]);
14267 if (e.stack != staticStack) {
14268 Jim_Free(e.stack);
14270 return retcode;
14273 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
14275 int retcode;
14276 jim_wide wideValue;
14277 double doubleValue;
14278 Jim_Obj *exprResultPtr;
14280 retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);
14281 if (retcode != JIM_OK)
14282 return retcode;
14284 if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) {
14285 if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK) {
14286 Jim_DecrRefCount(interp, exprResultPtr);
14287 return JIM_ERR;
14289 else {
14290 Jim_DecrRefCount(interp, exprResultPtr);
14291 *boolPtr = doubleValue != 0;
14292 return JIM_OK;
14295 *boolPtr = wideValue != 0;
14297 Jim_DecrRefCount(interp, exprResultPtr);
14298 return JIM_OK;
14304 typedef struct ScanFmtPartDescr
14306 char *arg;
14307 char *prefix;
14308 size_t width;
14309 int pos;
14310 char type;
14311 char modifier;
14312 } ScanFmtPartDescr;
14315 typedef struct ScanFmtStringObj
14317 jim_wide size;
14318 char *stringRep;
14319 size_t count;
14320 size_t convCount;
14321 size_t maxPos;
14322 const char *error;
14323 char *scratch;
14324 ScanFmtPartDescr descr[1];
14325 } ScanFmtStringObj;
14328 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
14329 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
14330 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
14332 static const Jim_ObjType scanFmtStringObjType = {
14333 "scanformatstring",
14334 FreeScanFmtInternalRep,
14335 DupScanFmtInternalRep,
14336 UpdateStringOfScanFmt,
14337 JIM_TYPE_NONE,
14340 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
14342 JIM_NOTUSED(interp);
14343 Jim_Free((char *)objPtr->internalRep.ptr);
14344 objPtr->internalRep.ptr = 0;
14347 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
14349 size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
14350 ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
14352 JIM_NOTUSED(interp);
14353 memcpy(newVec, srcPtr->internalRep.ptr, size);
14354 dupPtr->internalRep.ptr = newVec;
14355 dupPtr->typePtr = &scanFmtStringObjType;
14358 static void UpdateStringOfScanFmt(Jim_Obj *objPtr)
14360 JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep);
14364 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
14366 ScanFmtStringObj *fmtObj;
14367 char *buffer;
14368 int maxCount, i, approxSize, lastPos = -1;
14369 const char *fmt = objPtr->bytes;
14370 int maxFmtLen = objPtr->length;
14371 const char *fmtEnd = fmt + maxFmtLen;
14372 int curr;
14374 Jim_FreeIntRep(interp, objPtr);
14376 for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
14377 if (fmt[i] == '%')
14378 ++maxCount;
14380 approxSize = sizeof(ScanFmtStringObj)
14381 +(maxCount + 1) * sizeof(ScanFmtPartDescr)
14382 +maxFmtLen * sizeof(char) + 3 + 1
14383 + maxFmtLen * sizeof(char) + 1
14384 + maxFmtLen * sizeof(char)
14385 +(maxCount + 1) * sizeof(char)
14386 +1;
14387 fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
14388 memset(fmtObj, 0, approxSize);
14389 fmtObj->size = approxSize;
14390 fmtObj->maxPos = 0;
14391 fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
14392 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
14393 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
14394 buffer = fmtObj->stringRep + maxFmtLen + 1;
14395 objPtr->internalRep.ptr = fmtObj;
14396 objPtr->typePtr = &scanFmtStringObjType;
14397 for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
14398 int width = 0, skip;
14399 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
14401 fmtObj->count++;
14402 descr->width = 0;
14404 if (*fmt != '%' || fmt[1] == '%') {
14405 descr->type = 0;
14406 descr->prefix = &buffer[i];
14407 for (; fmt < fmtEnd; ++fmt) {
14408 if (*fmt == '%') {
14409 if (fmt[1] != '%')
14410 break;
14411 ++fmt;
14413 buffer[i++] = *fmt;
14415 buffer[i++] = 0;
14418 ++fmt;
14420 if (fmt >= fmtEnd)
14421 goto done;
14422 descr->pos = 0;
14423 if (*fmt == '*') {
14424 descr->pos = -1;
14425 ++fmt;
14427 else
14428 fmtObj->convCount++;
14430 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14431 fmt += skip;
14433 if (descr->pos != -1 && *fmt == '$') {
14434 int prev;
14436 ++fmt;
14437 descr->pos = width;
14438 width = 0;
14440 if ((lastPos == 0 && descr->pos > 0)
14441 || (lastPos > 0 && descr->pos == 0)) {
14442 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
14443 return JIM_ERR;
14446 for (prev = 0; prev < curr; ++prev) {
14447 if (fmtObj->descr[prev].pos == -1)
14448 continue;
14449 if (fmtObj->descr[prev].pos == descr->pos) {
14450 fmtObj->error =
14451 "variable is assigned by multiple \"%n$\" conversion specifiers";
14452 return JIM_ERR;
14456 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14457 descr->width = width;
14458 fmt += skip;
14460 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
14461 fmtObj->maxPos = descr->pos;
14463 else {
14465 descr->width = width;
14469 if (lastPos == -1)
14470 lastPos = descr->pos;
14472 if (*fmt == '[') {
14473 int swapped = 1, beg = i, end, j;
14475 descr->type = '[';
14476 descr->arg = &buffer[i];
14477 ++fmt;
14478 if (*fmt == '^')
14479 buffer[i++] = *fmt++;
14480 if (*fmt == ']')
14481 buffer[i++] = *fmt++;
14482 while (*fmt && *fmt != ']')
14483 buffer[i++] = *fmt++;
14484 if (*fmt != ']') {
14485 fmtObj->error = "unmatched [ in format string";
14486 return JIM_ERR;
14488 end = i;
14489 buffer[i++] = 0;
14491 while (swapped) {
14492 swapped = 0;
14493 for (j = beg + 1; j < end - 1; ++j) {
14494 if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
14495 char tmp = buffer[j - 1];
14497 buffer[j - 1] = buffer[j + 1];
14498 buffer[j + 1] = tmp;
14499 swapped = 1;
14504 else {
14506 if (strchr("hlL", *fmt) != 0)
14507 descr->modifier = tolower((int)*fmt++);
14509 descr->type = *fmt;
14510 if (strchr("efgcsndoxui", *fmt) == 0) {
14511 fmtObj->error = "bad scan conversion character";
14512 return JIM_ERR;
14514 else if (*fmt == 'c' && descr->width != 0) {
14515 fmtObj->error = "field width may not be specified in %c " "conversion";
14516 return JIM_ERR;
14518 else if (*fmt == 'u' && descr->modifier == 'l') {
14519 fmtObj->error = "unsigned wide not supported";
14520 return JIM_ERR;
14523 curr++;
14525 done:
14526 return JIM_OK;
14531 #define FormatGetCnvCount(_fo_) \
14532 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
14533 #define FormatGetMaxPos(_fo_) \
14534 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
14535 #define FormatGetError(_fo_) \
14536 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
14538 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
14540 char *buffer = Jim_StrDup(str);
14541 char *p = buffer;
14543 while (*str) {
14544 int c;
14545 int n;
14547 if (!sdescr && isspace(UCHAR(*str)))
14548 break;
14550 n = utf8_tounicode(str, &c);
14551 if (sdescr && !JimCharsetMatch(sdescr, c, JIM_CHARSET_SCAN))
14552 break;
14553 while (n--)
14554 *p++ = *str++;
14556 *p = 0;
14557 return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
14561 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int strLen,
14562 ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
14564 const char *tok;
14565 const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
14566 size_t scanned = 0;
14567 size_t anchor = pos;
14568 int i;
14569 Jim_Obj *tmpObj = NULL;
14572 *valObjPtr = 0;
14573 if (descr->prefix) {
14574 for (i = 0; pos < strLen && descr->prefix[i]; ++i) {
14576 if (isspace(UCHAR(descr->prefix[i])))
14577 while (pos < strLen && isspace(UCHAR(str[pos])))
14578 ++pos;
14579 else if (descr->prefix[i] != str[pos])
14580 break;
14581 else
14582 ++pos;
14584 if (pos >= strLen) {
14585 return -1;
14587 else if (descr->prefix[i] != 0)
14588 return 0;
14591 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
14592 while (isspace(UCHAR(str[pos])))
14593 ++pos;
14595 scanned = pos - anchor;
14598 if (descr->type == 'n') {
14600 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
14602 else if (pos >= strLen) {
14604 return -1;
14606 else if (descr->type == 'c') {
14607 int c;
14608 scanned += utf8_tounicode(&str[pos], &c);
14609 *valObjPtr = Jim_NewIntObj(interp, c);
14610 return scanned;
14612 else {
14614 if (descr->width > 0) {
14615 size_t sLen = utf8_strlen(&str[pos], strLen - pos);
14616 size_t tLen = descr->width > sLen ? sLen : descr->width;
14618 tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
14619 tok = tmpObj->bytes;
14621 else {
14623 tok = &str[pos];
14625 switch (descr->type) {
14626 case 'd':
14627 case 'o':
14628 case 'x':
14629 case 'u':
14630 case 'i':{
14631 char *endp;
14632 jim_wide w;
14634 int base = descr->type == 'o' ? 8
14635 : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
14638 if (base == 0) {
14639 w = jim_strtoull(tok, &endp);
14641 else {
14642 w = strtoull(tok, &endp, base);
14645 if (endp != tok) {
14647 *valObjPtr = Jim_NewIntObj(interp, w);
14650 scanned += endp - tok;
14652 else {
14653 scanned = *tok ? 0 : -1;
14655 break;
14657 case 's':
14658 case '[':{
14659 *valObjPtr = JimScanAString(interp, descr->arg, tok);
14660 scanned += Jim_Length(*valObjPtr);
14661 break;
14663 case 'e':
14664 case 'f':
14665 case 'g':{
14666 char *endp;
14667 double value = strtod(tok, &endp);
14669 if (endp != tok) {
14671 *valObjPtr = Jim_NewDoubleObj(interp, value);
14673 scanned += endp - tok;
14675 else {
14676 scanned = *tok ? 0 : -1;
14678 break;
14681 if (tmpObj) {
14682 Jim_FreeNewObj(interp, tmpObj);
14685 return scanned;
14689 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
14691 size_t i, pos;
14692 int scanned = 1;
14693 const char *str = Jim_String(strObjPtr);
14694 int strLen = Jim_Utf8Length(interp, strObjPtr);
14695 Jim_Obj *resultList = 0;
14696 Jim_Obj **resultVec = 0;
14697 int resultc;
14698 Jim_Obj *emptyStr = 0;
14699 ScanFmtStringObj *fmtObj;
14702 JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
14704 fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
14706 if (fmtObj->error != 0) {
14707 if (flags & JIM_ERRMSG)
14708 Jim_SetResultString(interp, fmtObj->error, -1);
14709 return 0;
14712 emptyStr = Jim_NewEmptyStringObj(interp);
14713 Jim_IncrRefCount(emptyStr);
14715 resultList = Jim_NewListObj(interp, NULL, 0);
14716 if (fmtObj->maxPos > 0) {
14717 for (i = 0; i < fmtObj->maxPos; ++i)
14718 Jim_ListAppendElement(interp, resultList, emptyStr);
14719 JimListGetElements(interp, resultList, &resultc, &resultVec);
14722 for (i = 0, pos = 0; i < fmtObj->count; ++i) {
14723 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
14724 Jim_Obj *value = 0;
14727 if (descr->type == 0)
14728 continue;
14730 if (scanned > 0)
14731 scanned = ScanOneEntry(interp, str, pos, strLen, fmtObj, i, &value);
14733 if (scanned == -1 && i == 0)
14734 goto eof;
14736 pos += scanned;
14739 if (value == 0)
14740 value = Jim_NewEmptyStringObj(interp);
14742 if (descr->pos == -1) {
14743 Jim_FreeNewObj(interp, value);
14745 else if (descr->pos == 0)
14747 Jim_ListAppendElement(interp, resultList, value);
14748 else if (resultVec[descr->pos - 1] == emptyStr) {
14750 Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
14751 Jim_IncrRefCount(value);
14752 resultVec[descr->pos - 1] = value;
14754 else {
14756 Jim_FreeNewObj(interp, value);
14757 goto err;
14760 Jim_DecrRefCount(interp, emptyStr);
14761 return resultList;
14762 eof:
14763 Jim_DecrRefCount(interp, emptyStr);
14764 Jim_FreeNewObj(interp, resultList);
14765 return (Jim_Obj *)EOF;
14766 err:
14767 Jim_DecrRefCount(interp, emptyStr);
14768 Jim_FreeNewObj(interp, resultList);
14769 return 0;
14773 static void JimPrngInit(Jim_Interp *interp)
14775 #define PRNG_SEED_SIZE 256
14776 int i;
14777 unsigned int *seed;
14778 time_t t = time(NULL);
14780 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
14782 seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
14783 for (i = 0; i < PRNG_SEED_SIZE; i++) {
14784 seed[i] = (rand() ^ t ^ clock());
14786 JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
14787 Jim_Free(seed);
14791 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
14793 Jim_PrngState *prng;
14794 unsigned char *destByte = (unsigned char *)dest;
14795 unsigned int si, sj, x;
14798 if (interp->prngState == NULL)
14799 JimPrngInit(interp);
14800 prng = interp->prngState;
14802 for (x = 0; x < len; x++) {
14803 prng->i = (prng->i + 1) & 0xff;
14804 si = prng->sbox[prng->i];
14805 prng->j = (prng->j + si) & 0xff;
14806 sj = prng->sbox[prng->j];
14807 prng->sbox[prng->i] = sj;
14808 prng->sbox[prng->j] = si;
14809 *destByte++ = prng->sbox[(si + sj) & 0xff];
14814 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
14816 int i;
14817 Jim_PrngState *prng;
14820 if (interp->prngState == NULL)
14821 JimPrngInit(interp);
14822 prng = interp->prngState;
14825 for (i = 0; i < 256; i++)
14826 prng->sbox[i] = i;
14828 for (i = 0; i < seedLen; i++) {
14829 unsigned char t;
14831 t = prng->sbox[i & 0xFF];
14832 prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
14833 prng->sbox[seed[i]] = t;
14835 prng->i = prng->j = 0;
14837 for (i = 0; i < 256; i += seedLen) {
14838 JimRandomBytes(interp, seed, seedLen);
14843 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14845 jim_wide wideValue, increment = 1;
14846 Jim_Obj *intObjPtr;
14848 if (argc != 2 && argc != 3) {
14849 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
14850 return JIM_ERR;
14852 if (argc == 3) {
14853 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
14854 return JIM_ERR;
14856 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
14857 if (!intObjPtr) {
14859 wideValue = 0;
14861 else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
14862 return JIM_ERR;
14864 if (!intObjPtr || Jim_IsShared(intObjPtr)) {
14865 intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
14866 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
14867 Jim_FreeNewObj(interp, intObjPtr);
14868 return JIM_ERR;
14871 else {
14873 Jim_InvalidateStringRep(intObjPtr);
14874 JimWideValue(intObjPtr) = wideValue + increment;
14876 if (argv[1]->typePtr != &variableObjType) {
14878 Jim_SetVariable(interp, argv[1], intObjPtr);
14881 Jim_SetResult(interp, intObjPtr);
14882 return JIM_OK;
14886 #define JIM_EVAL_SARGV_LEN 8
14887 #define JIM_EVAL_SINTV_LEN 8
14890 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14892 int retcode;
14894 if (interp->unknown_called > 50) {
14895 return JIM_ERR;
14900 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
14901 return JIM_ERR;
14903 interp->unknown_called++;
14905 retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
14906 interp->unknown_called--;
14908 return retcode;
14911 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14913 int retcode;
14914 Jim_Cmd *cmdPtr;
14916 #if 0
14917 printf("invoke");
14918 int j;
14919 for (j = 0; j < objc; j++) {
14920 printf(" '%s'", Jim_String(objv[j]));
14922 printf("\n");
14923 #endif
14925 if (interp->framePtr->tailcallCmd) {
14927 cmdPtr = interp->framePtr->tailcallCmd;
14928 interp->framePtr->tailcallCmd = NULL;
14930 else {
14931 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
14932 if (cmdPtr == NULL) {
14933 return JimUnknown(interp, objc, objv);
14935 JimIncrCmdRefCount(cmdPtr);
14938 if (interp->evalDepth == interp->maxEvalDepth) {
14939 Jim_SetResultString(interp, "Infinite eval recursion", -1);
14940 retcode = JIM_ERR;
14941 goto out;
14943 interp->evalDepth++;
14946 Jim_SetEmptyResult(interp);
14947 if (cmdPtr->isproc) {
14948 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
14950 else {
14951 interp->cmdPrivData = cmdPtr->u.native.privData;
14952 retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
14954 interp->evalDepth--;
14956 out:
14957 JimDecrCmdRefCount(interp, cmdPtr);
14959 return retcode;
14962 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14964 int i, retcode;
14967 for (i = 0; i < objc; i++)
14968 Jim_IncrRefCount(objv[i]);
14970 retcode = JimInvokeCommand(interp, objc, objv);
14973 for (i = 0; i < objc; i++)
14974 Jim_DecrRefCount(interp, objv[i]);
14976 return retcode;
14979 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
14981 int ret;
14982 Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
14984 nargv[0] = prefix;
14985 memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
14986 ret = Jim_EvalObjVector(interp, objc + 1, nargv);
14987 Jim_Free(nargv);
14988 return ret;
14991 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script)
14993 if (!interp->errorFlag) {
14995 interp->errorFlag = 1;
14996 Jim_IncrRefCount(script->fileNameObj);
14997 Jim_DecrRefCount(interp, interp->errorFileNameObj);
14998 interp->errorFileNameObj = script->fileNameObj;
14999 interp->errorLine = script->linenr;
15001 JimResetStackTrace(interp);
15003 interp->addStackTrace++;
15007 if (interp->addStackTrace > 0) {
15010 JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr);
15012 if (Jim_Length(script->fileNameObj)) {
15013 interp->addStackTrace = 0;
15016 Jim_DecrRefCount(interp, interp->errorProc);
15017 interp->errorProc = interp->emptyObj;
15018 Jim_IncrRefCount(interp->errorProc);
15022 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
15024 Jim_Obj *objPtr;
15026 switch (token->type) {
15027 case JIM_TT_STR:
15028 case JIM_TT_ESC:
15029 objPtr = token->objPtr;
15030 break;
15031 case JIM_TT_VAR:
15032 objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
15033 break;
15034 case JIM_TT_DICTSUGAR:
15035 objPtr = JimExpandDictSugar(interp, token->objPtr);
15036 break;
15037 case JIM_TT_EXPRSUGAR:
15038 objPtr = JimExpandExprSugar(interp, token->objPtr);
15039 break;
15040 case JIM_TT_CMD:
15041 switch (Jim_EvalObj(interp, token->objPtr)) {
15042 case JIM_OK:
15043 case JIM_RETURN:
15044 objPtr = interp->result;
15045 break;
15046 case JIM_BREAK:
15048 return JIM_BREAK;
15049 case JIM_CONTINUE:
15051 return JIM_CONTINUE;
15052 default:
15053 return JIM_ERR;
15055 break;
15056 default:
15057 JimPanic((1,
15058 "default token type (%d) reached " "in Jim_SubstObj().", token->type));
15059 objPtr = NULL;
15060 break;
15062 if (objPtr) {
15063 *objPtrPtr = objPtr;
15064 return JIM_OK;
15066 return JIM_ERR;
15069 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
15071 int totlen = 0, i;
15072 Jim_Obj **intv;
15073 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
15074 Jim_Obj *objPtr;
15075 char *s;
15077 if (tokens <= JIM_EVAL_SINTV_LEN)
15078 intv = sintv;
15079 else
15080 intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
15082 for (i = 0; i < tokens; i++) {
15083 switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
15084 case JIM_OK:
15085 case JIM_RETURN:
15086 break;
15087 case JIM_BREAK:
15088 if (flags & JIM_SUBST_FLAG) {
15090 tokens = i;
15091 continue;
15095 case JIM_CONTINUE:
15096 if (flags & JIM_SUBST_FLAG) {
15097 intv[i] = NULL;
15098 continue;
15102 default:
15103 while (i--) {
15104 Jim_DecrRefCount(interp, intv[i]);
15106 if (intv != sintv) {
15107 Jim_Free(intv);
15109 return NULL;
15111 Jim_IncrRefCount(intv[i]);
15112 Jim_String(intv[i]);
15113 totlen += intv[i]->length;
15117 if (tokens == 1 && intv[0] && intv == sintv) {
15118 Jim_DecrRefCount(interp, intv[0]);
15119 return intv[0];
15122 objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
15124 if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
15125 && token[2].type == JIM_TT_VAR) {
15127 objPtr->typePtr = &interpolatedObjType;
15128 objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
15129 objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
15130 Jim_IncrRefCount(intv[2]);
15132 else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
15134 JimSetSourceInfo(interp, objPtr, intv[0]->internalRep.sourceValue.fileNameObj, intv[0]->internalRep.sourceValue.lineNumber);
15138 s = objPtr->bytes = Jim_Alloc(totlen + 1);
15139 objPtr->length = totlen;
15140 for (i = 0; i < tokens; i++) {
15141 if (intv[i]) {
15142 memcpy(s, intv[i]->bytes, intv[i]->length);
15143 s += intv[i]->length;
15144 Jim_DecrRefCount(interp, intv[i]);
15147 objPtr->bytes[totlen] = '\0';
15149 if (intv != sintv) {
15150 Jim_Free(intv);
15153 return objPtr;
15157 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15159 int retcode = JIM_OK;
15161 JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
15163 if (listPtr->internalRep.listValue.len) {
15164 Jim_IncrRefCount(listPtr);
15165 retcode = JimInvokeCommand(interp,
15166 listPtr->internalRep.listValue.len,
15167 listPtr->internalRep.listValue.ele);
15168 Jim_DecrRefCount(interp, listPtr);
15170 return retcode;
15173 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15175 SetListFromAny(interp, listPtr);
15176 return JimEvalObjList(interp, listPtr);
15179 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
15181 int i;
15182 ScriptObj *script;
15183 ScriptToken *token;
15184 int retcode = JIM_OK;
15185 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
15186 Jim_Obj *prevScriptObj;
15188 if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
15189 return JimEvalObjList(interp, scriptObjPtr);
15192 Jim_IncrRefCount(scriptObjPtr);
15193 script = JimGetScript(interp, scriptObjPtr);
15194 if (!JimScriptValid(interp, script)) {
15195 Jim_DecrRefCount(interp, scriptObjPtr);
15196 return JIM_ERR;
15199 Jim_SetEmptyResult(interp);
15201 token = script->token;
15203 #ifdef JIM_OPTIMIZATION
15204 if (script->len == 0) {
15205 Jim_DecrRefCount(interp, scriptObjPtr);
15206 return JIM_OK;
15208 if (script->len == 3
15209 && token[1].objPtr->typePtr == &commandObjType
15210 && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
15211 && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
15212 && token[2].objPtr->typePtr == &variableObjType) {
15214 Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
15216 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
15217 JimWideValue(objPtr)++;
15218 Jim_InvalidateStringRep(objPtr);
15219 Jim_DecrRefCount(interp, scriptObjPtr);
15220 Jim_SetResult(interp, objPtr);
15221 return JIM_OK;
15224 #endif
15226 script->inUse++;
15229 prevScriptObj = interp->currentScriptObj;
15230 interp->currentScriptObj = scriptObjPtr;
15232 interp->errorFlag = 0;
15233 argv = sargv;
15235 for (i = 0; i < script->len && retcode == JIM_OK; ) {
15236 int argc;
15237 int j;
15240 argc = token[i].objPtr->internalRep.scriptLineValue.argc;
15241 script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
15244 if (argc > JIM_EVAL_SARGV_LEN)
15245 argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
15248 i++;
15250 for (j = 0; j < argc; j++) {
15251 long wordtokens = 1;
15252 int expand = 0;
15253 Jim_Obj *wordObjPtr = NULL;
15255 if (token[i].type == JIM_TT_WORD) {
15256 wordtokens = JimWideValue(token[i++].objPtr);
15257 if (wordtokens < 0) {
15258 expand = 1;
15259 wordtokens = -wordtokens;
15263 if (wordtokens == 1) {
15265 switch (token[i].type) {
15266 case JIM_TT_ESC:
15267 case JIM_TT_STR:
15268 wordObjPtr = token[i].objPtr;
15269 break;
15270 case JIM_TT_VAR:
15271 wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
15272 break;
15273 case JIM_TT_EXPRSUGAR:
15274 wordObjPtr = JimExpandExprSugar(interp, token[i].objPtr);
15275 break;
15276 case JIM_TT_DICTSUGAR:
15277 wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
15278 break;
15279 case JIM_TT_CMD:
15280 retcode = Jim_EvalObj(interp, token[i].objPtr);
15281 if (retcode == JIM_OK) {
15282 wordObjPtr = Jim_GetResult(interp);
15284 break;
15285 default:
15286 JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
15289 else {
15290 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
15293 if (!wordObjPtr) {
15294 if (retcode == JIM_OK) {
15295 retcode = JIM_ERR;
15297 break;
15300 Jim_IncrRefCount(wordObjPtr);
15301 i += wordtokens;
15303 if (!expand) {
15304 argv[j] = wordObjPtr;
15306 else {
15308 int len = Jim_ListLength(interp, wordObjPtr);
15309 int newargc = argc + len - 1;
15310 int k;
15312 if (len > 1) {
15313 if (argv == sargv) {
15314 if (newargc > JIM_EVAL_SARGV_LEN) {
15315 argv = Jim_Alloc(sizeof(*argv) * newargc);
15316 memcpy(argv, sargv, sizeof(*argv) * j);
15319 else {
15321 argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
15326 for (k = 0; k < len; k++) {
15327 argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
15328 Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
15331 Jim_DecrRefCount(interp, wordObjPtr);
15334 j--;
15335 argc += len - 1;
15339 if (retcode == JIM_OK && argc) {
15341 retcode = JimInvokeCommand(interp, argc, argv);
15343 if (Jim_CheckSignal(interp)) {
15344 retcode = JIM_SIGNAL;
15349 while (j-- > 0) {
15350 Jim_DecrRefCount(interp, argv[j]);
15353 if (argv != sargv) {
15354 Jim_Free(argv);
15355 argv = sargv;
15360 if (retcode == JIM_ERR) {
15361 JimAddErrorToStack(interp, script);
15364 else if (retcode != JIM_RETURN || interp->returnCode != JIM_ERR) {
15366 interp->addStackTrace = 0;
15370 interp->currentScriptObj = prevScriptObj;
15372 Jim_FreeIntRep(interp, scriptObjPtr);
15373 scriptObjPtr->typePtr = &scriptObjType;
15374 Jim_SetIntRepPtr(scriptObjPtr, script);
15375 Jim_DecrRefCount(interp, scriptObjPtr);
15377 return retcode;
15380 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
15382 int retcode;
15384 const char *varname = Jim_String(argNameObj);
15385 if (*varname == '&') {
15387 Jim_Obj *objPtr;
15388 Jim_CallFrame *savedCallFrame = interp->framePtr;
15390 interp->framePtr = interp->framePtr->parent;
15391 objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
15392 interp->framePtr = savedCallFrame;
15393 if (!objPtr) {
15394 return JIM_ERR;
15398 objPtr = Jim_NewStringObj(interp, varname + 1, -1);
15399 Jim_IncrRefCount(objPtr);
15400 retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
15401 Jim_DecrRefCount(interp, objPtr);
15403 else {
15404 retcode = Jim_SetVariable(interp, argNameObj, argValObj);
15406 return retcode;
15409 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
15412 Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
15413 int i;
15415 for (i = 0; i < cmd->u.proc.argListLen; i++) {
15416 Jim_AppendString(interp, argmsg, " ", 1);
15418 if (i == cmd->u.proc.argsPos) {
15419 if (cmd->u.proc.arglist[i].defaultObjPtr) {
15421 Jim_AppendString(interp, argmsg, "?", 1);
15422 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
15423 Jim_AppendString(interp, argmsg, " ...?", -1);
15425 else {
15427 Jim_AppendString(interp, argmsg, "?arg...?", -1);
15430 else {
15431 if (cmd->u.proc.arglist[i].defaultObjPtr) {
15432 Jim_AppendString(interp, argmsg, "?", 1);
15433 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
15434 Jim_AppendString(interp, argmsg, "?", 1);
15436 else {
15437 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
15438 if (*arg == '&') {
15439 arg++;
15441 Jim_AppendString(interp, argmsg, arg, -1);
15445 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
15446 Jim_FreeNewObj(interp, argmsg);
15449 #ifdef jim_ext_namespace
15450 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
15452 Jim_CallFrame *callFramePtr;
15453 int retcode;
15456 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
15457 callFramePtr->argv = &interp->emptyObj;
15458 callFramePtr->argc = 0;
15459 callFramePtr->procArgsObjPtr = NULL;
15460 callFramePtr->procBodyObjPtr = scriptObj;
15461 callFramePtr->staticVars = NULL;
15462 callFramePtr->fileNameObj = interp->emptyObj;
15463 callFramePtr->line = 0;
15464 Jim_IncrRefCount(scriptObj);
15465 interp->framePtr = callFramePtr;
15468 if (interp->framePtr->level == interp->maxCallFrameDepth) {
15469 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15470 retcode = JIM_ERR;
15472 else {
15474 retcode = Jim_EvalObj(interp, scriptObj);
15478 interp->framePtr = interp->framePtr->parent;
15479 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15481 return retcode;
15483 #endif
15485 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
15487 Jim_CallFrame *callFramePtr;
15488 int i, d, retcode, optargs;
15489 ScriptObj *script;
15492 if (argc - 1 < cmd->u.proc.reqArity ||
15493 (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
15494 JimSetProcWrongArgs(interp, argv[0], cmd);
15495 return JIM_ERR;
15498 if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
15500 return JIM_OK;
15504 if (interp->framePtr->level == interp->maxCallFrameDepth) {
15505 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15506 return JIM_ERR;
15510 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
15511 callFramePtr->argv = argv;
15512 callFramePtr->argc = argc;
15513 callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
15514 callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
15515 callFramePtr->staticVars = cmd->u.proc.staticVars;
15518 script = JimGetScript(interp, interp->currentScriptObj);
15519 callFramePtr->fileNameObj = script->fileNameObj;
15520 callFramePtr->line = script->linenr;
15522 Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
15523 Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
15524 interp->framePtr = callFramePtr;
15527 optargs = (argc - 1 - cmd->u.proc.reqArity);
15530 i = 1;
15531 for (d = 0; d < cmd->u.proc.argListLen; d++) {
15532 Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
15533 if (d == cmd->u.proc.argsPos) {
15535 Jim_Obj *listObjPtr;
15536 int argsLen = 0;
15537 if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
15538 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
15540 listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
15543 if (cmd->u.proc.arglist[d].defaultObjPtr) {
15544 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
15546 retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
15547 if (retcode != JIM_OK) {
15548 goto badargset;
15551 i += argsLen;
15552 continue;
15556 if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
15557 retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
15559 else {
15561 retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
15563 if (retcode != JIM_OK) {
15564 goto badargset;
15569 retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
15571 badargset:
15574 interp->framePtr = interp->framePtr->parent;
15575 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15578 if (interp->framePtr->tailcallObj) {
15579 do {
15580 Jim_Obj *tailcallObj = interp->framePtr->tailcallObj;
15582 interp->framePtr->tailcallObj = NULL;
15584 if (retcode == JIM_EVAL) {
15585 retcode = Jim_EvalObjList(interp, tailcallObj);
15586 if (retcode == JIM_RETURN) {
15587 interp->returnLevel++;
15590 Jim_DecrRefCount(interp, tailcallObj);
15591 } while (interp->framePtr->tailcallObj);
15594 if (interp->framePtr->tailcallCmd) {
15595 JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
15596 interp->framePtr->tailcallCmd = NULL;
15601 if (retcode == JIM_RETURN) {
15602 if (--interp->returnLevel <= 0) {
15603 retcode = interp->returnCode;
15604 interp->returnCode = JIM_OK;
15605 interp->returnLevel = 0;
15608 else if (retcode == JIM_ERR) {
15609 interp->addStackTrace++;
15610 Jim_DecrRefCount(interp, interp->errorProc);
15611 interp->errorProc = argv[0];
15612 Jim_IncrRefCount(interp->errorProc);
15615 return retcode;
15618 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
15620 int retval;
15621 Jim_Obj *scriptObjPtr;
15623 scriptObjPtr = Jim_NewStringObj(interp, script, -1);
15624 Jim_IncrRefCount(scriptObjPtr);
15626 if (filename) {
15627 Jim_Obj *prevScriptObj;
15629 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
15631 prevScriptObj = interp->currentScriptObj;
15632 interp->currentScriptObj = scriptObjPtr;
15634 retval = Jim_EvalObj(interp, scriptObjPtr);
15636 interp->currentScriptObj = prevScriptObj;
15638 else {
15639 retval = Jim_EvalObj(interp, scriptObjPtr);
15641 Jim_DecrRefCount(interp, scriptObjPtr);
15642 return retval;
15645 int Jim_Eval(Jim_Interp *interp, const char *script)
15647 return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
15651 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
15653 int retval;
15654 Jim_CallFrame *savedFramePtr = interp->framePtr;
15656 interp->framePtr = interp->topFramePtr;
15657 retval = Jim_Eval(interp, script);
15658 interp->framePtr = savedFramePtr;
15660 return retval;
15663 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
15665 int retval;
15666 Jim_CallFrame *savedFramePtr = interp->framePtr;
15668 interp->framePtr = interp->topFramePtr;
15669 retval = Jim_EvalFile(interp, filename);
15670 interp->framePtr = savedFramePtr;
15672 return retval;
15675 #include <sys/stat.h>
15677 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
15679 FILE *fp;
15680 char *buf;
15681 Jim_Obj *scriptObjPtr;
15682 Jim_Obj *prevScriptObj;
15683 struct stat sb;
15684 int retcode;
15685 int readlen;
15687 if (stat(filename, &sb) != 0 || (fp = fopen(filename, "rt")) == NULL) {
15688 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
15689 return JIM_ERR;
15691 if (sb.st_size == 0) {
15692 fclose(fp);
15693 return JIM_OK;
15696 buf = Jim_Alloc(sb.st_size + 1);
15697 readlen = fread(buf, 1, sb.st_size, fp);
15698 if (ferror(fp)) {
15699 fclose(fp);
15700 Jim_Free(buf);
15701 Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
15702 return JIM_ERR;
15704 fclose(fp);
15705 buf[readlen] = 0;
15707 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
15708 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1);
15709 Jim_IncrRefCount(scriptObjPtr);
15711 prevScriptObj = interp->currentScriptObj;
15712 interp->currentScriptObj = scriptObjPtr;
15714 retcode = Jim_EvalObj(interp, scriptObjPtr);
15717 if (retcode == JIM_RETURN) {
15718 if (--interp->returnLevel <= 0) {
15719 retcode = interp->returnCode;
15720 interp->returnCode = JIM_OK;
15721 interp->returnLevel = 0;
15724 if (retcode == JIM_ERR) {
15726 interp->addStackTrace++;
15729 interp->currentScriptObj = prevScriptObj;
15731 Jim_DecrRefCount(interp, scriptObjPtr);
15733 return retcode;
15736 static void JimParseSubst(struct JimParserCtx *pc, int flags)
15738 pc->tstart = pc->p;
15739 pc->tline = pc->linenr;
15741 if (pc->len == 0) {
15742 pc->tend = pc->p;
15743 pc->tt = JIM_TT_EOL;
15744 pc->eof = 1;
15745 return;
15747 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15748 JimParseCmd(pc);
15749 return;
15751 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15752 if (JimParseVar(pc) == JIM_OK) {
15753 return;
15756 pc->tstart = pc->p;
15757 flags |= JIM_SUBST_NOVAR;
15759 while (pc->len) {
15760 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15761 break;
15763 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15764 break;
15766 if (*pc->p == '\\' && pc->len > 1) {
15767 pc->p++;
15768 pc->len--;
15770 pc->p++;
15771 pc->len--;
15773 pc->tend = pc->p - 1;
15774 pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
15778 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
15780 int scriptTextLen;
15781 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
15782 struct JimParserCtx parser;
15783 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
15784 ParseTokenList tokenlist;
15787 ScriptTokenListInit(&tokenlist);
15789 JimParserInit(&parser, scriptText, scriptTextLen, 1);
15790 while (1) {
15791 JimParseSubst(&parser, flags);
15792 if (parser.eof) {
15794 break;
15796 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
15797 parser.tline);
15801 script->inUse = 1;
15802 script->substFlags = flags;
15803 script->fileNameObj = interp->emptyObj;
15804 Jim_IncrRefCount(script->fileNameObj);
15805 SubstObjAddTokens(interp, script, &tokenlist);
15808 ScriptTokenListFree(&tokenlist);
15810 #ifdef DEBUG_SHOW_SUBST
15812 int i;
15814 printf("==== Subst ====\n");
15815 for (i = 0; i < script->len; i++) {
15816 printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
15817 Jim_String(script->token[i].objPtr));
15820 #endif
15823 Jim_FreeIntRep(interp, objPtr);
15824 Jim_SetIntRepPtr(objPtr, script);
15825 objPtr->typePtr = &scriptObjType;
15826 return JIM_OK;
15829 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
15831 if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
15832 SetSubstFromAny(interp, objPtr, flags);
15833 return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
15836 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
15838 ScriptObj *script = Jim_GetSubst(interp, substObjPtr, flags);
15840 Jim_IncrRefCount(substObjPtr);
15841 script->inUse++;
15843 *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
15845 script->inUse--;
15846 Jim_DecrRefCount(interp, substObjPtr);
15847 if (*resObjPtrPtr == NULL) {
15848 return JIM_ERR;
15850 return JIM_OK;
15853 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
15855 Jim_Obj *objPtr;
15856 Jim_Obj *listObjPtr = Jim_NewListObj(interp, argv, argc);
15858 if (*msg) {
15859 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
15861 Jim_IncrRefCount(listObjPtr);
15862 objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
15863 Jim_DecrRefCount(interp, listObjPtr);
15865 Jim_IncrRefCount(objPtr);
15866 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
15867 Jim_DecrRefCount(interp, objPtr);
15870 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
15871 Jim_HashEntry *he, int type);
15873 #define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL)
15875 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
15876 JimHashtableIteratorCallbackType *callback, int type)
15878 Jim_HashEntry *he;
15879 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
15882 if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
15883 he = Jim_FindHashEntry(ht, Jim_String(patternObjPtr));
15884 if (he) {
15885 callback(interp, listObjPtr, he, type);
15888 else {
15889 Jim_HashTableIterator htiter;
15890 JimInitHashTableIterator(ht, &htiter);
15891 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
15892 if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), he->key, 0)) {
15893 callback(interp, listObjPtr, he, type);
15897 return listObjPtr;
15901 #define JIM_CMDLIST_COMMANDS 0
15902 #define JIM_CMDLIST_PROCS 1
15903 #define JIM_CMDLIST_CHANNELS 2
15905 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15906 Jim_HashEntry *he, int type)
15908 Jim_Cmd *cmdPtr = Jim_GetHashEntryVal(he);
15909 Jim_Obj *objPtr;
15911 if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
15913 return;
15916 objPtr = Jim_NewStringObj(interp, he->key, -1);
15917 Jim_IncrRefCount(objPtr);
15919 if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, objPtr)) {
15920 Jim_ListAppendElement(interp, listObjPtr, objPtr);
15922 Jim_DecrRefCount(interp, objPtr);
15926 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
15928 return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
15932 #define JIM_VARLIST_GLOBALS 0
15933 #define JIM_VARLIST_LOCALS 1
15934 #define JIM_VARLIST_VARS 2
15936 #define JIM_VARLIST_VALUES 0x1000
15938 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15939 Jim_HashEntry *he, int type)
15941 Jim_Var *varPtr = Jim_GetHashEntryVal(he);
15943 if (type != JIM_VARLIST_LOCALS || varPtr->linkFramePtr == NULL) {
15944 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1));
15945 if (type & JIM_VARLIST_VALUES) {
15946 Jim_ListAppendElement(interp, listObjPtr, varPtr->objPtr);
15952 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
15954 if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
15955 return interp->emptyObj;
15957 else {
15958 Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
15959 return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch, mode);
15963 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
15964 Jim_Obj **objPtrPtr, int info_level_cmd)
15966 Jim_CallFrame *targetCallFrame;
15968 targetCallFrame = JimGetCallFrameByInteger(interp, levelObjPtr);
15969 if (targetCallFrame == NULL) {
15970 return JIM_ERR;
15973 if (targetCallFrame == interp->topFramePtr) {
15974 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
15975 return JIM_ERR;
15977 if (info_level_cmd) {
15978 *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
15980 else {
15981 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
15983 Jim_ListAppendElement(interp, listObj, targetCallFrame->argv[0]);
15984 Jim_ListAppendElement(interp, listObj, targetCallFrame->fileNameObj);
15985 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, targetCallFrame->line));
15986 *objPtrPtr = listObj;
15988 return JIM_OK;
15993 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15995 if (argc != 2 && argc != 3) {
15996 Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
15997 return JIM_ERR;
15999 if (argc == 3) {
16000 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
16001 Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
16002 return JIM_ERR;
16004 else {
16005 fputs(Jim_String(argv[2]), stdout);
16008 else {
16009 puts(Jim_String(argv[1]));
16011 return JIM_OK;
16015 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
16017 jim_wide wideValue, res;
16018 double doubleValue, doubleRes;
16019 int i;
16021 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
16023 for (i = 1; i < argc; i++) {
16024 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
16025 goto trydouble;
16026 if (op == JIM_EXPROP_ADD)
16027 res += wideValue;
16028 else
16029 res *= wideValue;
16031 Jim_SetResultInt(interp, res);
16032 return JIM_OK;
16033 trydouble:
16034 doubleRes = (double)res;
16035 for (; i < argc; i++) {
16036 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
16037 return JIM_ERR;
16038 if (op == JIM_EXPROP_ADD)
16039 doubleRes += doubleValue;
16040 else
16041 doubleRes *= doubleValue;
16043 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16044 return JIM_OK;
16048 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
16050 jim_wide wideValue, res = 0;
16051 double doubleValue, doubleRes = 0;
16052 int i = 2;
16054 if (argc < 2) {
16055 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
16056 return JIM_ERR;
16058 else if (argc == 2) {
16059 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
16060 if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
16061 return JIM_ERR;
16063 else {
16064 if (op == JIM_EXPROP_SUB)
16065 doubleRes = -doubleValue;
16066 else
16067 doubleRes = 1.0 / doubleValue;
16068 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16069 return JIM_OK;
16072 if (op == JIM_EXPROP_SUB) {
16073 res = -wideValue;
16074 Jim_SetResultInt(interp, res);
16076 else {
16077 doubleRes = 1.0 / wideValue;
16078 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16080 return JIM_OK;
16082 else {
16083 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
16084 if (Jim_GetDouble(interp, argv[1], &doubleRes)
16085 != JIM_OK) {
16086 return JIM_ERR;
16088 else {
16089 goto trydouble;
16093 for (i = 2; i < argc; i++) {
16094 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
16095 doubleRes = (double)res;
16096 goto trydouble;
16098 if (op == JIM_EXPROP_SUB)
16099 res -= wideValue;
16100 else
16101 res /= wideValue;
16103 Jim_SetResultInt(interp, res);
16104 return JIM_OK;
16105 trydouble:
16106 for (; i < argc; i++) {
16107 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
16108 return JIM_ERR;
16109 if (op == JIM_EXPROP_SUB)
16110 doubleRes -= doubleValue;
16111 else
16112 doubleRes /= doubleValue;
16114 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
16115 return JIM_OK;
16120 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16122 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
16126 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16128 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
16132 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16134 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
16138 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16140 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
16144 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16146 if (argc != 2 && argc != 3) {
16147 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
16148 return JIM_ERR;
16150 if (argc == 2) {
16151 Jim_Obj *objPtr;
16153 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16154 if (!objPtr)
16155 return JIM_ERR;
16156 Jim_SetResult(interp, objPtr);
16157 return JIM_OK;
16160 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
16161 return JIM_ERR;
16162 Jim_SetResult(interp, argv[2]);
16163 return JIM_OK;
16166 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16168 int i = 1;
16169 int complain = 1;
16171 while (i < argc) {
16172 if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
16173 i++;
16174 break;
16176 if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
16177 complain = 0;
16178 i++;
16179 continue;
16181 break;
16184 while (i < argc) {
16185 if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
16186 && complain) {
16187 return JIM_ERR;
16189 i++;
16191 return JIM_OK;
16195 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16197 if (argc != 3) {
16198 Jim_WrongNumArgs(interp, 1, argv, "condition body");
16199 return JIM_ERR;
16203 while (1) {
16204 int boolean, retval;
16206 if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
16207 return retval;
16208 if (!boolean)
16209 break;
16211 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
16212 switch (retval) {
16213 case JIM_BREAK:
16214 goto out;
16215 break;
16216 case JIM_CONTINUE:
16217 continue;
16218 break;
16219 default:
16220 return retval;
16224 out:
16225 Jim_SetEmptyResult(interp);
16226 return JIM_OK;
16230 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16232 int retval;
16233 int boolean = 1;
16234 Jim_Obj *varNamePtr = NULL;
16235 Jim_Obj *stopVarNamePtr = NULL;
16237 if (argc != 5) {
16238 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
16239 return JIM_ERR;
16243 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
16244 return retval;
16247 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16250 #ifdef JIM_OPTIMIZATION
16251 if (retval == JIM_OK && boolean) {
16252 ScriptObj *incrScript;
16253 ExprByteCode *expr;
16254 jim_wide stop, currentVal;
16255 Jim_Obj *objPtr;
16256 int cmpOffset;
16259 expr = JimGetExpression(interp, argv[2]);
16260 incrScript = JimGetScript(interp, argv[3]);
16263 if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
16264 goto evalstart;
16267 if (incrScript->token[1].type != JIM_TT_ESC ||
16268 expr->token[0].type != JIM_TT_VAR ||
16269 (expr->token[1].type != JIM_TT_EXPR_INT && expr->token[1].type != JIM_TT_VAR)) {
16270 goto evalstart;
16273 if (expr->token[2].type == JIM_EXPROP_LT) {
16274 cmpOffset = 0;
16276 else if (expr->token[2].type == JIM_EXPROP_LTE) {
16277 cmpOffset = 1;
16279 else {
16280 goto evalstart;
16284 if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
16285 goto evalstart;
16289 if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->token[0].objPtr)) {
16290 goto evalstart;
16294 if (expr->token[1].type == JIM_TT_EXPR_INT) {
16295 if (Jim_GetWide(interp, expr->token[1].objPtr, &stop) == JIM_ERR) {
16296 goto evalstart;
16299 else {
16300 stopVarNamePtr = expr->token[1].objPtr;
16301 Jim_IncrRefCount(stopVarNamePtr);
16303 stop = 0;
16307 varNamePtr = expr->token[0].objPtr;
16308 Jim_IncrRefCount(varNamePtr);
16310 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
16311 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
16312 goto testcond;
16316 while (retval == JIM_OK) {
16321 if (stopVarNamePtr) {
16322 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
16323 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
16324 goto testcond;
16328 if (currentVal >= stop + cmpOffset) {
16329 break;
16333 retval = Jim_EvalObj(interp, argv[4]);
16334 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16335 retval = JIM_OK;
16337 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
16340 if (objPtr == NULL) {
16341 retval = JIM_ERR;
16342 goto out;
16344 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16345 currentVal = ++JimWideValue(objPtr);
16346 Jim_InvalidateStringRep(objPtr);
16348 else {
16349 if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
16350 Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
16351 ++currentVal)) != JIM_OK) {
16352 goto evalnext;
16357 goto out;
16359 evalstart:
16360 #endif
16362 while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
16364 retval = Jim_EvalObj(interp, argv[4]);
16366 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16368 evalnext:
16369 retval = Jim_EvalObj(interp, argv[3]);
16370 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16372 testcond:
16373 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16377 out:
16378 if (stopVarNamePtr) {
16379 Jim_DecrRefCount(interp, stopVarNamePtr);
16381 if (varNamePtr) {
16382 Jim_DecrRefCount(interp, varNamePtr);
16385 if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
16386 Jim_SetEmptyResult(interp);
16387 return JIM_OK;
16390 return retval;
16394 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16396 int retval;
16397 jim_wide i;
16398 jim_wide limit;
16399 jim_wide incr = 1;
16400 Jim_Obj *bodyObjPtr;
16402 if (argc != 5 && argc != 6) {
16403 Jim_WrongNumArgs(interp, 1, argv, "var first limit ?incr? body");
16404 return JIM_ERR;
16407 if (Jim_GetWide(interp, argv[2], &i) != JIM_OK ||
16408 Jim_GetWide(interp, argv[3], &limit) != JIM_OK ||
16409 (argc == 6 && Jim_GetWide(interp, argv[4], &incr) != JIM_OK)) {
16410 return JIM_ERR;
16412 bodyObjPtr = (argc == 5) ? argv[4] : argv[5];
16414 retval = Jim_SetVariable(interp, argv[1], argv[2]);
16416 while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
16417 retval = Jim_EvalObj(interp, bodyObjPtr);
16418 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16419 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16421 retval = JIM_OK;
16424 i += incr;
16426 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16427 if (argv[1]->typePtr != &variableObjType) {
16428 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16429 return JIM_ERR;
16432 JimWideValue(objPtr) = i;
16433 Jim_InvalidateStringRep(objPtr);
16435 if (argv[1]->typePtr != &variableObjType) {
16436 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16437 retval = JIM_ERR;
16438 break;
16442 else {
16443 objPtr = Jim_NewIntObj(interp, i);
16444 retval = Jim_SetVariable(interp, argv[1], objPtr);
16445 if (retval != JIM_OK) {
16446 Jim_FreeNewObj(interp, objPtr);
16452 if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
16453 Jim_SetEmptyResult(interp);
16454 return JIM_OK;
16456 return retval;
16459 typedef struct {
16460 Jim_Obj *objPtr;
16461 int idx;
16462 } Jim_ListIter;
16464 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
16466 iter->objPtr = objPtr;
16467 iter->idx = 0;
16470 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
16472 if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
16473 return NULL;
16475 return iter->objPtr->internalRep.listValue.ele[iter->idx++];
16478 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
16480 return iter->idx >= Jim_ListLength(interp, iter->objPtr);
16484 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
16486 int result = JIM_OK;
16487 int i, numargs;
16488 Jim_ListIter twoiters[2];
16489 Jim_ListIter *iters;
16490 Jim_Obj *script;
16491 Jim_Obj *resultObj;
16493 if (argc < 4 || argc % 2 != 0) {
16494 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
16495 return JIM_ERR;
16497 script = argv[argc - 1];
16498 numargs = (argc - 1 - 1);
16500 if (numargs == 2) {
16501 iters = twoiters;
16503 else {
16504 iters = Jim_Alloc(numargs * sizeof(*iters));
16506 for (i = 0; i < numargs; i++) {
16507 JimListIterInit(&iters[i], argv[i + 1]);
16508 if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
16509 result = JIM_ERR;
16512 if (result != JIM_OK) {
16513 Jim_SetResultString(interp, "foreach varlist is empty", -1);
16514 return result;
16517 if (doMap) {
16518 resultObj = Jim_NewListObj(interp, NULL, 0);
16520 else {
16521 resultObj = interp->emptyObj;
16523 Jim_IncrRefCount(resultObj);
16525 while (1) {
16527 for (i = 0; i < numargs; i += 2) {
16528 if (!JimListIterDone(interp, &iters[i + 1])) {
16529 break;
16532 if (i == numargs) {
16534 break;
16538 for (i = 0; i < numargs; i += 2) {
16539 Jim_Obj *varName;
16542 JimListIterInit(&iters[i], argv[i + 1]);
16543 while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
16544 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
16545 if (!valObj) {
16547 valObj = interp->emptyObj;
16550 Jim_IncrRefCount(valObj);
16551 result = Jim_SetVariable(interp, varName, valObj);
16552 Jim_DecrRefCount(interp, valObj);
16553 if (result != JIM_OK) {
16554 goto err;
16558 switch (result = Jim_EvalObj(interp, script)) {
16559 case JIM_OK:
16560 if (doMap) {
16561 Jim_ListAppendElement(interp, resultObj, interp->result);
16563 break;
16564 case JIM_CONTINUE:
16565 break;
16566 case JIM_BREAK:
16567 goto out;
16568 default:
16569 goto err;
16572 out:
16573 result = JIM_OK;
16574 Jim_SetResult(interp, resultObj);
16575 err:
16576 Jim_DecrRefCount(interp, resultObj);
16577 if (numargs > 2) {
16578 Jim_Free(iters);
16580 return result;
16584 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16586 return JimForeachMapHelper(interp, argc, argv, 0);
16590 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16592 return JimForeachMapHelper(interp, argc, argv, 1);
16596 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16598 int result = JIM_ERR;
16599 int i;
16600 Jim_ListIter iter;
16601 Jim_Obj *resultObj;
16603 if (argc < 2) {
16604 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
16605 return JIM_ERR;
16608 JimListIterInit(&iter, argv[1]);
16610 for (i = 2; i < argc; i++) {
16611 Jim_Obj *valObj = JimListIterNext(interp, &iter);
16612 result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
16613 if (result != JIM_OK) {
16614 return result;
16618 resultObj = Jim_NewListObj(interp, NULL, 0);
16619 while (!JimListIterDone(interp, &iter)) {
16620 Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
16623 Jim_SetResult(interp, resultObj);
16625 return JIM_OK;
16629 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16631 int boolean, retval, current = 1, falsebody = 0;
16633 if (argc >= 3) {
16634 while (1) {
16636 if (current >= argc)
16637 goto err;
16638 if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
16639 != JIM_OK)
16640 return retval;
16642 if (current >= argc)
16643 goto err;
16644 if (Jim_CompareStringImmediate(interp, argv[current], "then"))
16645 current++;
16647 if (current >= argc)
16648 goto err;
16649 if (boolean)
16650 return Jim_EvalObj(interp, argv[current]);
16652 if (++current >= argc) {
16653 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
16654 return JIM_OK;
16656 falsebody = current++;
16657 if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
16659 if (current != argc - 1)
16660 goto err;
16661 return Jim_EvalObj(interp, argv[current]);
16663 else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
16664 continue;
16666 else if (falsebody != argc - 1)
16667 goto err;
16668 return Jim_EvalObj(interp, argv[falsebody]);
16670 return JIM_OK;
16672 err:
16673 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
16674 return JIM_ERR;
16679 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
16680 Jim_Obj *stringObj, int nocase)
16682 Jim_Obj *parms[4];
16683 int argc = 0;
16684 long eq;
16685 int rc;
16687 parms[argc++] = commandObj;
16688 if (nocase) {
16689 parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
16691 parms[argc++] = patternObj;
16692 parms[argc++] = stringObj;
16694 rc = Jim_EvalObjVector(interp, argc, parms);
16696 if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
16697 eq = -rc;
16700 return eq;
16703 enum
16704 { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
16707 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16709 int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
16710 Jim_Obj *command = 0, *const *caseList = 0, *strObj;
16711 Jim_Obj *script = 0;
16713 if (argc < 3) {
16714 wrongnumargs:
16715 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
16716 "pattern body ... ?default body? or " "{pattern body ?pattern body ...?}");
16717 return JIM_ERR;
16719 for (opt = 1; opt < argc; ++opt) {
16720 const char *option = Jim_String(argv[opt]);
16722 if (*option != '-')
16723 break;
16724 else if (strncmp(option, "--", 2) == 0) {
16725 ++opt;
16726 break;
16728 else if (strncmp(option, "-exact", 2) == 0)
16729 matchOpt = SWITCH_EXACT;
16730 else if (strncmp(option, "-glob", 2) == 0)
16731 matchOpt = SWITCH_GLOB;
16732 else if (strncmp(option, "-regexp", 2) == 0)
16733 matchOpt = SWITCH_RE;
16734 else if (strncmp(option, "-command", 2) == 0) {
16735 matchOpt = SWITCH_CMD;
16736 if ((argc - opt) < 2)
16737 goto wrongnumargs;
16738 command = argv[++opt];
16740 else {
16741 Jim_SetResultFormatted(interp,
16742 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
16743 argv[opt]);
16744 return JIM_ERR;
16746 if ((argc - opt) < 2)
16747 goto wrongnumargs;
16749 strObj = argv[opt++];
16750 patCount = argc - opt;
16751 if (patCount == 1) {
16752 Jim_Obj **vector;
16754 JimListGetElements(interp, argv[opt], &patCount, &vector);
16755 caseList = vector;
16757 else
16758 caseList = &argv[opt];
16759 if (patCount == 0 || patCount % 2 != 0)
16760 goto wrongnumargs;
16761 for (i = 0; script == 0 && i < patCount; i += 2) {
16762 Jim_Obj *patObj = caseList[i];
16764 if (!Jim_CompareStringImmediate(interp, patObj, "default")
16765 || i < (patCount - 2)) {
16766 switch (matchOpt) {
16767 case SWITCH_EXACT:
16768 if (Jim_StringEqObj(strObj, patObj))
16769 script = caseList[i + 1];
16770 break;
16771 case SWITCH_GLOB:
16772 if (Jim_StringMatchObj(interp, patObj, strObj, 0))
16773 script = caseList[i + 1];
16774 break;
16775 case SWITCH_RE:
16776 command = Jim_NewStringObj(interp, "regexp", -1);
16778 case SWITCH_CMD:{
16779 int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, 0);
16781 if (argc - opt == 1) {
16782 Jim_Obj **vector;
16784 JimListGetElements(interp, argv[opt], &patCount, &vector);
16785 caseList = vector;
16788 if (rc < 0) {
16789 return -rc;
16791 if (rc)
16792 script = caseList[i + 1];
16793 break;
16797 else {
16798 script = caseList[i + 1];
16801 for (; i < patCount && Jim_CompareStringImmediate(interp, script, "-"); i += 2)
16802 script = caseList[i + 1];
16803 if (script && Jim_CompareStringImmediate(interp, script, "-")) {
16804 Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
16805 return JIM_ERR;
16807 Jim_SetEmptyResult(interp);
16808 if (script) {
16809 return Jim_EvalObj(interp, script);
16811 return JIM_OK;
16815 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16817 Jim_Obj *listObjPtr;
16819 listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1);
16820 Jim_SetResult(interp, listObjPtr);
16821 return JIM_OK;
16825 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16827 Jim_Obj *objPtr, *listObjPtr;
16828 int i;
16829 int idx;
16831 if (argc < 2) {
16832 Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
16833 return JIM_ERR;
16835 objPtr = argv[1];
16836 Jim_IncrRefCount(objPtr);
16837 for (i = 2; i < argc; i++) {
16838 listObjPtr = objPtr;
16839 if (Jim_GetIndex(interp, argv[i], &idx) != JIM_OK) {
16840 Jim_DecrRefCount(interp, listObjPtr);
16841 return JIM_ERR;
16843 if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_NONE) != JIM_OK) {
16844 Jim_DecrRefCount(interp, listObjPtr);
16845 Jim_SetEmptyResult(interp);
16846 return JIM_OK;
16848 Jim_IncrRefCount(objPtr);
16849 Jim_DecrRefCount(interp, listObjPtr);
16851 Jim_SetResult(interp, objPtr);
16852 Jim_DecrRefCount(interp, objPtr);
16853 return JIM_OK;
16857 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16859 if (argc != 2) {
16860 Jim_WrongNumArgs(interp, 1, argv, "list");
16861 return JIM_ERR;
16863 Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
16864 return JIM_OK;
16868 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16870 static const char * const options[] = {
16871 "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
16872 NULL
16874 enum
16875 { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
16876 OPT_COMMAND };
16877 int i;
16878 int opt_bool = 0;
16879 int opt_not = 0;
16880 int opt_nocase = 0;
16881 int opt_all = 0;
16882 int opt_inline = 0;
16883 int opt_match = OPT_EXACT;
16884 int listlen;
16885 int rc = JIM_OK;
16886 Jim_Obj *listObjPtr = NULL;
16887 Jim_Obj *commandObj = NULL;
16889 if (argc < 3) {
16890 wrongargs:
16891 Jim_WrongNumArgs(interp, 1, argv,
16892 "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? list value");
16893 return JIM_ERR;
16896 for (i = 1; i < argc - 2; i++) {
16897 int option;
16899 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
16900 return JIM_ERR;
16902 switch (option) {
16903 case OPT_BOOL:
16904 opt_bool = 1;
16905 opt_inline = 0;
16906 break;
16907 case OPT_NOT:
16908 opt_not = 1;
16909 break;
16910 case OPT_NOCASE:
16911 opt_nocase = 1;
16912 break;
16913 case OPT_INLINE:
16914 opt_inline = 1;
16915 opt_bool = 0;
16916 break;
16917 case OPT_ALL:
16918 opt_all = 1;
16919 break;
16920 case OPT_COMMAND:
16921 if (i >= argc - 2) {
16922 goto wrongargs;
16924 commandObj = argv[++i];
16926 case OPT_EXACT:
16927 case OPT_GLOB:
16928 case OPT_REGEXP:
16929 opt_match = option;
16930 break;
16934 argv += i;
16936 if (opt_all) {
16937 listObjPtr = Jim_NewListObj(interp, NULL, 0);
16939 if (opt_match == OPT_REGEXP) {
16940 commandObj = Jim_NewStringObj(interp, "regexp", -1);
16942 if (commandObj) {
16943 Jim_IncrRefCount(commandObj);
16946 listlen = Jim_ListLength(interp, argv[0]);
16947 for (i = 0; i < listlen; i++) {
16948 int eq = 0;
16949 Jim_Obj *objPtr = Jim_ListGetIndex(interp, argv[0], i);
16951 switch (opt_match) {
16952 case OPT_EXACT:
16953 eq = Jim_StringCompareObj(interp, argv[1], objPtr, opt_nocase) == 0;
16954 break;
16956 case OPT_GLOB:
16957 eq = Jim_StringMatchObj(interp, argv[1], objPtr, opt_nocase);
16958 break;
16960 case OPT_REGEXP:
16961 case OPT_COMMAND:
16962 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, opt_nocase);
16963 if (eq < 0) {
16964 if (listObjPtr) {
16965 Jim_FreeNewObj(interp, listObjPtr);
16967 rc = JIM_ERR;
16968 goto done;
16970 break;
16974 if (!eq && opt_bool && opt_not && !opt_all) {
16975 continue;
16978 if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
16980 Jim_Obj *resultObj;
16982 if (opt_bool) {
16983 resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
16985 else if (!opt_inline) {
16986 resultObj = Jim_NewIntObj(interp, i);
16988 else {
16989 resultObj = objPtr;
16992 if (opt_all) {
16993 Jim_ListAppendElement(interp, listObjPtr, resultObj);
16995 else {
16996 Jim_SetResult(interp, resultObj);
16997 goto done;
17002 if (opt_all) {
17003 Jim_SetResult(interp, listObjPtr);
17005 else {
17007 if (opt_bool) {
17008 Jim_SetResultBool(interp, opt_not);
17010 else if (!opt_inline) {
17011 Jim_SetResultInt(interp, -1);
17015 done:
17016 if (commandObj) {
17017 Jim_DecrRefCount(interp, commandObj);
17019 return rc;
17023 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17025 Jim_Obj *listObjPtr;
17026 int new_obj = 0;
17027 int i;
17029 if (argc < 2) {
17030 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
17031 return JIM_ERR;
17033 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
17034 if (!listObjPtr) {
17036 listObjPtr = Jim_NewListObj(interp, NULL, 0);
17037 new_obj = 1;
17039 else if (Jim_IsShared(listObjPtr)) {
17040 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
17041 new_obj = 1;
17043 for (i = 2; i < argc; i++)
17044 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
17045 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
17046 if (new_obj)
17047 Jim_FreeNewObj(interp, listObjPtr);
17048 return JIM_ERR;
17050 Jim_SetResult(interp, listObjPtr);
17051 return JIM_OK;
17055 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17057 int idx, len;
17058 Jim_Obj *listPtr;
17060 if (argc < 3) {
17061 Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
17062 return JIM_ERR;
17064 listPtr = argv[1];
17065 if (Jim_IsShared(listPtr))
17066 listPtr = Jim_DuplicateObj(interp, listPtr);
17067 if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
17068 goto err;
17069 len = Jim_ListLength(interp, listPtr);
17070 if (idx >= len)
17071 idx = len;
17072 else if (idx < 0)
17073 idx = len + idx + 1;
17074 Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
17075 Jim_SetResult(interp, listPtr);
17076 return JIM_OK;
17077 err:
17078 if (listPtr != argv[1]) {
17079 Jim_FreeNewObj(interp, listPtr);
17081 return JIM_ERR;
17085 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17087 int first, last, len, rangeLen;
17088 Jim_Obj *listObj;
17089 Jim_Obj *newListObj;
17091 if (argc < 4) {
17092 Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
17093 return JIM_ERR;
17095 if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
17096 Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
17097 return JIM_ERR;
17100 listObj = argv[1];
17101 len = Jim_ListLength(interp, listObj);
17103 first = JimRelToAbsIndex(len, first);
17104 last = JimRelToAbsIndex(len, last);
17105 JimRelToAbsRange(len, &first, &last, &rangeLen);
17109 if (first < len) {
17112 else if (len == 0) {
17114 first = 0;
17116 else {
17117 Jim_SetResultString(interp, "list doesn't contain element ", -1);
17118 Jim_AppendObj(interp, Jim_GetResult(interp), argv[2]);
17119 return JIM_ERR;
17123 newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
17126 ListInsertElements(newListObj, -1, argc - 4, argv + 4);
17129 ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
17131 Jim_SetResult(interp, newListObj);
17132 return JIM_OK;
17136 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17138 if (argc < 3) {
17139 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
17140 return JIM_ERR;
17142 else if (argc == 3) {
17144 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
17145 return JIM_ERR;
17146 Jim_SetResult(interp, argv[2]);
17147 return JIM_OK;
17149 return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
17153 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
17155 static const char * const options[] = {
17156 "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", NULL
17158 enum
17159 { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE };
17160 Jim_Obj *resObj;
17161 int i;
17162 int retCode;
17164 struct lsort_info info;
17166 if (argc < 2) {
17167 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
17168 return JIM_ERR;
17171 info.type = JIM_LSORT_ASCII;
17172 info.order = 1;
17173 info.indexed = 0;
17174 info.unique = 0;
17175 info.command = NULL;
17176 info.interp = interp;
17178 for (i = 1; i < (argc - 1); i++) {
17179 int option;
17181 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
17182 != JIM_OK)
17183 return JIM_ERR;
17184 switch (option) {
17185 case OPT_ASCII:
17186 info.type = JIM_LSORT_ASCII;
17187 break;
17188 case OPT_NOCASE:
17189 info.type = JIM_LSORT_NOCASE;
17190 break;
17191 case OPT_INTEGER:
17192 info.type = JIM_LSORT_INTEGER;
17193 break;
17194 case OPT_REAL:
17195 info.type = JIM_LSORT_REAL;
17196 break;
17197 case OPT_INCREASING:
17198 info.order = 1;
17199 break;
17200 case OPT_DECREASING:
17201 info.order = -1;
17202 break;
17203 case OPT_UNIQUE:
17204 info.unique = 1;
17205 break;
17206 case OPT_COMMAND:
17207 if (i >= (argc - 2)) {
17208 Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
17209 return JIM_ERR;
17211 info.type = JIM_LSORT_COMMAND;
17212 info.command = argv[i + 1];
17213 i++;
17214 break;
17215 case OPT_INDEX:
17216 if (i >= (argc - 2)) {
17217 Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
17218 return JIM_ERR;
17220 if (Jim_GetIndex(interp, argv[i + 1], &info.index) != JIM_OK) {
17221 return JIM_ERR;
17223 info.indexed = 1;
17224 i++;
17225 break;
17228 resObj = Jim_DuplicateObj(interp, argv[argc - 1]);
17229 retCode = ListSortElements(interp, resObj, &info);
17230 if (retCode == JIM_OK) {
17231 Jim_SetResult(interp, resObj);
17233 else {
17234 Jim_FreeNewObj(interp, resObj);
17236 return retCode;
17240 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17242 Jim_Obj *stringObjPtr;
17243 int i;
17245 if (argc < 2) {
17246 Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
17247 return JIM_ERR;
17249 if (argc == 2) {
17250 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17251 if (!stringObjPtr)
17252 return JIM_ERR;
17254 else {
17255 int new_obj = 0;
17256 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
17257 if (!stringObjPtr) {
17259 stringObjPtr = Jim_NewEmptyStringObj(interp);
17260 new_obj = 1;
17262 else if (Jim_IsShared(stringObjPtr)) {
17263 new_obj = 1;
17264 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
17266 for (i = 2; i < argc; i++) {
17267 Jim_AppendObj(interp, stringObjPtr, argv[i]);
17269 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
17270 if (new_obj) {
17271 Jim_FreeNewObj(interp, stringObjPtr);
17273 return JIM_ERR;
17276 Jim_SetResult(interp, stringObjPtr);
17277 return JIM_OK;
17281 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17283 #if !defined(JIM_DEBUG_COMMAND)
17284 Jim_SetResultString(interp, "unsupported", -1);
17285 return JIM_ERR;
17286 #endif
17290 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17292 int rc;
17294 if (argc < 2) {
17295 Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
17296 return JIM_ERR;
17299 if (argc == 2) {
17300 rc = Jim_EvalObj(interp, argv[1]);
17302 else {
17303 rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17306 if (rc == JIM_ERR) {
17308 interp->addStackTrace++;
17310 return rc;
17314 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17316 if (argc >= 2) {
17317 int retcode;
17318 Jim_CallFrame *savedCallFrame, *targetCallFrame;
17319 const char *str;
17322 savedCallFrame = interp->framePtr;
17325 str = Jim_String(argv[1]);
17326 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
17327 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17328 argc--;
17329 argv++;
17331 else {
17332 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17334 if (targetCallFrame == NULL) {
17335 return JIM_ERR;
17337 if (argc < 2) {
17338 Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
17339 return JIM_ERR;
17342 interp->framePtr = targetCallFrame;
17343 if (argc == 2) {
17344 retcode = Jim_EvalObj(interp, argv[1]);
17346 else {
17347 retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17349 interp->framePtr = savedCallFrame;
17350 return retcode;
17352 else {
17353 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
17354 return JIM_ERR;
17359 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17361 Jim_Obj *exprResultPtr;
17362 int retcode;
17364 if (argc == 2) {
17365 retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
17367 else if (argc > 2) {
17368 Jim_Obj *objPtr;
17370 objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
17371 Jim_IncrRefCount(objPtr);
17372 retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
17373 Jim_DecrRefCount(interp, objPtr);
17375 else {
17376 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
17377 return JIM_ERR;
17379 if (retcode != JIM_OK)
17380 return retcode;
17381 Jim_SetResult(interp, exprResultPtr);
17382 Jim_DecrRefCount(interp, exprResultPtr);
17383 return JIM_OK;
17387 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17389 if (argc != 1) {
17390 Jim_WrongNumArgs(interp, 1, argv, "");
17391 return JIM_ERR;
17393 return JIM_BREAK;
17397 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17399 if (argc != 1) {
17400 Jim_WrongNumArgs(interp, 1, argv, "");
17401 return JIM_ERR;
17403 return JIM_CONTINUE;
17407 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17409 int i;
17410 Jim_Obj *stackTraceObj = NULL;
17411 Jim_Obj *errorCodeObj = NULL;
17412 int returnCode = JIM_OK;
17413 long level = 1;
17415 for (i = 1; i < argc - 1; i += 2) {
17416 if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
17417 if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
17418 return JIM_ERR;
17421 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
17422 stackTraceObj = argv[i + 1];
17424 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
17425 errorCodeObj = argv[i + 1];
17427 else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
17428 if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
17429 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
17430 return JIM_ERR;
17433 else {
17434 break;
17438 if (i != argc - 1 && i != argc) {
17439 Jim_WrongNumArgs(interp, 1, argv,
17440 "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
17444 if (stackTraceObj && returnCode == JIM_ERR) {
17445 JimSetStackTrace(interp, stackTraceObj);
17448 if (errorCodeObj && returnCode == JIM_ERR) {
17449 Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
17451 interp->returnCode = returnCode;
17452 interp->returnLevel = level;
17454 if (i == argc - 1) {
17455 Jim_SetResult(interp, argv[i]);
17457 return JIM_RETURN;
17461 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17463 if (interp->framePtr->level == 0) {
17464 Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
17465 return JIM_ERR;
17467 else if (argc >= 2) {
17469 Jim_CallFrame *cf = interp->framePtr->parent;
17471 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17472 if (cmdPtr == NULL) {
17473 return JIM_ERR;
17476 JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));
17479 JimIncrCmdRefCount(cmdPtr);
17480 cf->tailcallCmd = cmdPtr;
17483 JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));
17485 cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
17486 Jim_IncrRefCount(cf->tailcallObj);
17489 return JIM_EVAL;
17491 return JIM_OK;
17494 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17496 Jim_Obj *cmdList;
17497 Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
17500 cmdList = Jim_DuplicateObj(interp, prefixListObj);
17501 Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
17503 return JimEvalObjList(interp, cmdList);
17506 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
17508 Jim_Obj *prefixListObj = privData;
17509 Jim_DecrRefCount(interp, prefixListObj);
17512 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17514 Jim_Obj *prefixListObj;
17515 const char *newname;
17517 if (argc < 3) {
17518 Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
17519 return JIM_ERR;
17522 prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
17523 Jim_IncrRefCount(prefixListObj);
17524 newname = Jim_String(argv[1]);
17525 if (newname[0] == ':' && newname[1] == ':') {
17526 while (*++newname == ':') {
17530 Jim_SetResult(interp, argv[1]);
17532 return Jim_CreateCommand(interp, newname, JimAliasCmd, prefixListObj, JimAliasCmdDelete);
17536 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17538 Jim_Cmd *cmd;
17540 if (argc != 4 && argc != 5) {
17541 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
17542 return JIM_ERR;
17545 if (JimValidName(interp, "procedure", argv[1]) != JIM_OK) {
17546 return JIM_ERR;
17549 if (argc == 4) {
17550 cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
17552 else {
17553 cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
17556 if (cmd) {
17558 Jim_Obj *qualifiedCmdNameObj;
17559 const char *cmdname = JimQualifyName(interp, Jim_String(argv[1]), &qualifiedCmdNameObj);
17561 JimCreateCommand(interp, cmdname, cmd);
17564 JimUpdateProcNamespace(interp, cmd, cmdname);
17566 JimFreeQualifiedName(interp, qualifiedCmdNameObj);
17569 Jim_SetResult(interp, argv[1]);
17570 return JIM_OK;
17572 return JIM_ERR;
17576 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17578 int retcode;
17580 if (argc < 2) {
17581 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17582 return JIM_ERR;
17586 interp->local++;
17587 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17588 interp->local--;
17592 if (retcode == 0) {
17593 Jim_Obj *cmdNameObj = Jim_GetResult(interp);
17595 if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
17596 return JIM_ERR;
17598 if (interp->framePtr->localCommands == NULL) {
17599 interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
17600 Jim_InitStack(interp->framePtr->localCommands);
17602 Jim_IncrRefCount(cmdNameObj);
17603 Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
17606 return retcode;
17610 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17612 if (argc < 2) {
17613 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17614 return JIM_ERR;
17616 else {
17617 int retcode;
17619 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17620 if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
17621 Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
17622 return JIM_ERR;
17625 cmdPtr->u.proc.upcall++;
17626 JimIncrCmdRefCount(cmdPtr);
17629 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17632 cmdPtr->u.proc.upcall--;
17633 JimDecrCmdRefCount(interp, cmdPtr);
17635 return retcode;
17640 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17642 if (argc < 2) {
17643 Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
17644 return JIM_ERR;
17646 else {
17647 int ret;
17648 Jim_Cmd *cmd;
17649 Jim_Obj *argListObjPtr;
17650 Jim_Obj *bodyObjPtr;
17651 Jim_Obj *nsObj = NULL;
17652 Jim_Obj **nargv;
17654 int len = Jim_ListLength(interp, argv[1]);
17655 if (len != 2 && len != 3) {
17656 Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
17657 return JIM_ERR;
17660 if (len == 3) {
17661 #ifdef jim_ext_namespace
17663 nsObj = JimQualifyNameObj(interp, Jim_ListGetIndex(interp, argv[1], 2));
17664 #else
17665 Jim_SetResultString(interp, "namespaces not enabled", -1);
17666 return JIM_ERR;
17667 #endif
17669 argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
17670 bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
17672 cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
17674 if (cmd) {
17676 nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
17677 nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
17678 Jim_IncrRefCount(nargv[0]);
17679 memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
17680 ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
17681 Jim_DecrRefCount(interp, nargv[0]);
17682 Jim_Free(nargv);
17684 JimDecrCmdRefCount(interp, cmd);
17685 return ret;
17687 return JIM_ERR;
17693 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17695 Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17696 return JIM_OK;
17700 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17702 int i;
17703 Jim_CallFrame *targetCallFrame;
17706 if (argc > 3 && (argc % 2 == 0)) {
17707 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17708 argc--;
17709 argv++;
17711 else {
17712 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17714 if (targetCallFrame == NULL) {
17715 return JIM_ERR;
17719 if (argc < 3) {
17720 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
17721 return JIM_ERR;
17725 for (i = 1; i < argc; i += 2) {
17726 if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
17727 return JIM_ERR;
17729 return JIM_OK;
17733 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17735 int i;
17737 if (argc < 2) {
17738 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
17739 return JIM_ERR;
17742 if (interp->framePtr->level == 0)
17743 return JIM_OK;
17744 for (i = 1; i < argc; i++) {
17746 const char *name = Jim_String(argv[i]);
17747 if (name[0] != ':' || name[1] != ':') {
17748 if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
17749 return JIM_ERR;
17752 return JIM_OK;
17755 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
17756 Jim_Obj *objPtr, int nocase)
17758 int numMaps;
17759 const char *str, *noMatchStart = NULL;
17760 int strLen, i;
17761 Jim_Obj *resultObjPtr;
17763 numMaps = Jim_ListLength(interp, mapListObjPtr);
17764 if (numMaps % 2) {
17765 Jim_SetResultString(interp, "list must contain an even number of elements", -1);
17766 return NULL;
17769 str = Jim_String(objPtr);
17770 strLen = Jim_Utf8Length(interp, objPtr);
17773 resultObjPtr = Jim_NewStringObj(interp, "", 0);
17774 while (strLen) {
17775 for (i = 0; i < numMaps; i += 2) {
17776 Jim_Obj *objPtr;
17777 const char *k;
17778 int kl;
17780 objPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
17781 k = Jim_String(objPtr);
17782 kl = Jim_Utf8Length(interp, objPtr);
17784 if (strLen >= kl && kl) {
17785 int rc;
17786 rc = JimStringCompareLen(str, k, kl, nocase);
17787 if (rc == 0) {
17788 if (noMatchStart) {
17789 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17790 noMatchStart = NULL;
17792 Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
17793 str += utf8_index(str, kl);
17794 strLen -= kl;
17795 break;
17799 if (i == numMaps) {
17800 int c;
17801 if (noMatchStart == NULL)
17802 noMatchStart = str;
17803 str += utf8_tounicode(str, &c);
17804 strLen--;
17807 if (noMatchStart) {
17808 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17810 return resultObjPtr;
17814 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17816 int len;
17817 int opt_case = 1;
17818 int option;
17819 static const char * const options[] = {
17820 "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace",
17821 "map", "repeat", "reverse", "index", "first", "last", "cat",
17822 "trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL
17824 enum
17826 OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_REPLACE,
17827 OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST, OPT_CAT,
17828 OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE
17830 static const char * const nocase_options[] = {
17831 "-nocase", NULL
17833 static const char * const nocase_length_options[] = {
17834 "-nocase", "-length", NULL
17837 if (argc < 2) {
17838 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
17839 return JIM_ERR;
17841 if (Jim_GetEnum(interp, argv[1], options, &option, NULL,
17842 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
17843 return JIM_ERR;
17845 switch (option) {
17846 case OPT_LENGTH:
17847 case OPT_BYTELENGTH:
17848 if (argc != 3) {
17849 Jim_WrongNumArgs(interp, 2, argv, "string");
17850 return JIM_ERR;
17852 if (option == OPT_LENGTH) {
17853 len = Jim_Utf8Length(interp, argv[2]);
17855 else {
17856 len = Jim_Length(argv[2]);
17858 Jim_SetResultInt(interp, len);
17859 return JIM_OK;
17861 case OPT_CAT:{
17862 Jim_Obj *objPtr;
17863 if (argc == 3) {
17865 objPtr = argv[2];
17867 else {
17868 int i;
17870 objPtr = Jim_NewStringObj(interp, "", 0);
17872 for (i = 2; i < argc; i++) {
17873 Jim_AppendObj(interp, objPtr, argv[i]);
17876 Jim_SetResult(interp, objPtr);
17877 return JIM_OK;
17880 case OPT_COMPARE:
17881 case OPT_EQUAL:
17884 long opt_length = -1;
17885 int n = argc - 4;
17886 int i = 2;
17887 while (n > 0) {
17888 int subopt;
17889 if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
17890 JIM_ENUM_ABBREV) != JIM_OK) {
17891 badcompareargs:
17892 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? ?-length int? string1 string2");
17893 return JIM_ERR;
17895 if (subopt == 0) {
17897 opt_case = 0;
17898 n--;
17900 else {
17902 if (n < 2) {
17903 goto badcompareargs;
17905 if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
17906 return JIM_ERR;
17908 n -= 2;
17911 if (n) {
17912 goto badcompareargs;
17914 argv += argc - 2;
17915 if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
17917 Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
17919 else {
17920 if (opt_length >= 0) {
17921 n = JimStringCompareLen(Jim_String(argv[0]), Jim_String(argv[1]), opt_length, !opt_case);
17923 else {
17924 n = Jim_StringCompareObj(interp, argv[0], argv[1], !opt_case);
17926 Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
17928 return JIM_OK;
17931 case OPT_MATCH:
17932 if (argc != 4 &&
17933 (argc != 5 ||
17934 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17935 JIM_ENUM_ABBREV) != JIM_OK)) {
17936 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
17937 return JIM_ERR;
17939 if (opt_case == 0) {
17940 argv++;
17942 Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
17943 return JIM_OK;
17945 case OPT_MAP:{
17946 Jim_Obj *objPtr;
17948 if (argc != 4 &&
17949 (argc != 5 ||
17950 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17951 JIM_ENUM_ABBREV) != JIM_OK)) {
17952 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
17953 return JIM_ERR;
17956 if (opt_case == 0) {
17957 argv++;
17959 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
17960 if (objPtr == NULL) {
17961 return JIM_ERR;
17963 Jim_SetResult(interp, objPtr);
17964 return JIM_OK;
17967 case OPT_RANGE:
17968 case OPT_BYTERANGE:{
17969 Jim_Obj *objPtr;
17971 if (argc != 5) {
17972 Jim_WrongNumArgs(interp, 2, argv, "string first last");
17973 return JIM_ERR;
17975 if (option == OPT_RANGE) {
17976 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
17978 else
17980 objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
17983 if (objPtr == NULL) {
17984 return JIM_ERR;
17986 Jim_SetResult(interp, objPtr);
17987 return JIM_OK;
17990 case OPT_REPLACE:{
17991 Jim_Obj *objPtr;
17993 if (argc != 5 && argc != 6) {
17994 Jim_WrongNumArgs(interp, 2, argv, "string first last ?string?");
17995 return JIM_ERR;
17997 objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
17998 if (objPtr == NULL) {
17999 return JIM_ERR;
18001 Jim_SetResult(interp, objPtr);
18002 return JIM_OK;
18006 case OPT_REPEAT:{
18007 Jim_Obj *objPtr;
18008 jim_wide count;
18010 if (argc != 4) {
18011 Jim_WrongNumArgs(interp, 2, argv, "string count");
18012 return JIM_ERR;
18014 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK) {
18015 return JIM_ERR;
18017 objPtr = Jim_NewStringObj(interp, "", 0);
18018 if (count > 0) {
18019 while (count--) {
18020 Jim_AppendObj(interp, objPtr, argv[2]);
18023 Jim_SetResult(interp, objPtr);
18024 return JIM_OK;
18027 case OPT_REVERSE:{
18028 char *buf, *p;
18029 const char *str;
18030 int len;
18031 int i;
18033 if (argc != 3) {
18034 Jim_WrongNumArgs(interp, 2, argv, "string");
18035 return JIM_ERR;
18038 str = Jim_GetString(argv[2], &len);
18039 buf = Jim_Alloc(len + 1);
18040 p = buf + len;
18041 *p = 0;
18042 for (i = 0; i < len; ) {
18043 int c;
18044 int l = utf8_tounicode(str, &c);
18045 memcpy(p - l, str, l);
18046 p -= l;
18047 i += l;
18048 str += l;
18050 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
18051 return JIM_OK;
18054 case OPT_INDEX:{
18055 int idx;
18056 const char *str;
18058 if (argc != 4) {
18059 Jim_WrongNumArgs(interp, 2, argv, "string index");
18060 return JIM_ERR;
18062 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
18063 return JIM_ERR;
18065 str = Jim_String(argv[2]);
18066 len = Jim_Utf8Length(interp, argv[2]);
18067 if (idx != INT_MIN && idx != INT_MAX) {
18068 idx = JimRelToAbsIndex(len, idx);
18070 if (idx < 0 || idx >= len || str == NULL) {
18071 Jim_SetResultString(interp, "", 0);
18073 else if (len == Jim_Length(argv[2])) {
18075 Jim_SetResultString(interp, str + idx, 1);
18077 else {
18078 int c;
18079 int i = utf8_index(str, idx);
18080 Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
18082 return JIM_OK;
18085 case OPT_FIRST:
18086 case OPT_LAST:{
18087 int idx = 0, l1, l2;
18088 const char *s1, *s2;
18090 if (argc != 4 && argc != 5) {
18091 Jim_WrongNumArgs(interp, 2, argv, "subString string ?index?");
18092 return JIM_ERR;
18094 s1 = Jim_String(argv[2]);
18095 s2 = Jim_String(argv[3]);
18096 l1 = Jim_Utf8Length(interp, argv[2]);
18097 l2 = Jim_Utf8Length(interp, argv[3]);
18098 if (argc == 5) {
18099 if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
18100 return JIM_ERR;
18102 idx = JimRelToAbsIndex(l2, idx);
18104 else if (option == OPT_LAST) {
18105 idx = l2;
18107 if (option == OPT_FIRST) {
18108 Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
18110 else {
18111 #ifdef JIM_UTF8
18112 Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
18113 #else
18114 Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
18115 #endif
18117 return JIM_OK;
18120 case OPT_TRIM:
18121 case OPT_TRIMLEFT:
18122 case OPT_TRIMRIGHT:{
18123 Jim_Obj *trimchars;
18125 if (argc != 3 && argc != 4) {
18126 Jim_WrongNumArgs(interp, 2, argv, "string ?trimchars?");
18127 return JIM_ERR;
18129 trimchars = (argc == 4 ? argv[3] : NULL);
18130 if (option == OPT_TRIM) {
18131 Jim_SetResult(interp, JimStringTrim(interp, argv[2], trimchars));
18133 else if (option == OPT_TRIMLEFT) {
18134 Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], trimchars));
18136 else if (option == OPT_TRIMRIGHT) {
18137 Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], trimchars));
18139 return JIM_OK;
18142 case OPT_TOLOWER:
18143 case OPT_TOUPPER:
18144 case OPT_TOTITLE:
18145 if (argc != 3) {
18146 Jim_WrongNumArgs(interp, 2, argv, "string");
18147 return JIM_ERR;
18149 if (option == OPT_TOLOWER) {
18150 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
18152 else if (option == OPT_TOUPPER) {
18153 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
18155 else {
18156 Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
18158 return JIM_OK;
18160 case OPT_IS:
18161 if (argc == 4 || (argc == 5 && Jim_CompareStringImmediate(interp, argv[3], "-strict"))) {
18162 return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
18164 Jim_WrongNumArgs(interp, 2, argv, "class ?-strict? str");
18165 return JIM_ERR;
18167 return JIM_OK;
18171 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18173 long i, count = 1;
18174 jim_wide start, elapsed;
18175 char buf[60];
18176 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
18178 if (argc < 2) {
18179 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
18180 return JIM_ERR;
18182 if (argc == 3) {
18183 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
18184 return JIM_ERR;
18186 if (count < 0)
18187 return JIM_OK;
18188 i = count;
18189 start = JimClock();
18190 while (i-- > 0) {
18191 int retval;
18193 retval = Jim_EvalObj(interp, argv[1]);
18194 if (retval != JIM_OK) {
18195 return retval;
18198 elapsed = JimClock() - start;
18199 sprintf(buf, fmt, count == 0 ? 0 : elapsed / count);
18200 Jim_SetResultString(interp, buf, -1);
18201 return JIM_OK;
18205 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18207 long exitCode = 0;
18209 if (argc > 2) {
18210 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
18211 return JIM_ERR;
18213 if (argc == 2) {
18214 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
18215 return JIM_ERR;
18217 interp->exitCode = exitCode;
18218 return JIM_EXIT;
18222 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18224 int exitCode = 0;
18225 int i;
18226 int sig = 0;
18229 jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
18230 static const int max_ignore_code = sizeof(ignore_mask) * 8;
18232 Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
18234 for (i = 1; i < argc - 1; i++) {
18235 const char *arg = Jim_String(argv[i]);
18236 jim_wide option;
18237 int ignore;
18240 if (strcmp(arg, "--") == 0) {
18241 i++;
18242 break;
18244 if (*arg != '-') {
18245 break;
18248 if (strncmp(arg, "-no", 3) == 0) {
18249 arg += 3;
18250 ignore = 1;
18252 else {
18253 arg++;
18254 ignore = 0;
18257 if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
18258 option = -1;
18260 if (option < 0) {
18261 option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
18263 if (option < 0) {
18264 goto wrongargs;
18267 if (ignore) {
18268 ignore_mask |= ((jim_wide)1 << option);
18270 else {
18271 ignore_mask &= (~((jim_wide)1 << option));
18275 argc -= i;
18276 if (argc < 1 || argc > 3) {
18277 wrongargs:
18278 Jim_WrongNumArgs(interp, 1, argv,
18279 "?-?no?code ... --? script ?resultVarName? ?optionVarName?");
18280 return JIM_ERR;
18282 argv += i;
18284 if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
18285 sig++;
18288 interp->signal_level += sig;
18289 if (Jim_CheckSignal(interp)) {
18291 exitCode = JIM_SIGNAL;
18293 else {
18294 exitCode = Jim_EvalObj(interp, argv[0]);
18296 interp->errorFlag = 0;
18298 interp->signal_level -= sig;
18301 if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
18303 return exitCode;
18306 if (sig && exitCode == JIM_SIGNAL) {
18308 if (interp->signal_set_result) {
18309 interp->signal_set_result(interp, interp->sigmask);
18311 else {
18312 Jim_SetResultInt(interp, interp->sigmask);
18314 interp->sigmask = 0;
18317 if (argc >= 2) {
18318 if (Jim_SetVariable(interp, argv[1], Jim_GetResult(interp)) != JIM_OK) {
18319 return JIM_ERR;
18321 if (argc == 3) {
18322 Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
18324 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
18325 Jim_ListAppendElement(interp, optListObj,
18326 Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
18327 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
18328 Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
18329 if (exitCode == JIM_ERR) {
18330 Jim_Obj *errorCode;
18331 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
18332 -1));
18333 Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
18335 errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
18336 if (errorCode) {
18337 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
18338 Jim_ListAppendElement(interp, optListObj, errorCode);
18341 if (Jim_SetVariable(interp, argv[2], optListObj) != JIM_OK) {
18342 return JIM_ERR;
18346 Jim_SetResultInt(interp, exitCode);
18347 return JIM_OK;
18350 #ifdef JIM_REFERENCES
18353 static int Jim_RefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18355 if (argc != 3 && argc != 4) {
18356 Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?");
18357 return JIM_ERR;
18359 if (argc == 3) {
18360 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL));
18362 else {
18363 Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], argv[3]));
18365 return JIM_OK;
18369 static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18371 Jim_Reference *refPtr;
18373 if (argc != 2) {
18374 Jim_WrongNumArgs(interp, 1, argv, "reference");
18375 return JIM_ERR;
18377 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
18378 return JIM_ERR;
18379 Jim_SetResult(interp, refPtr->objPtr);
18380 return JIM_OK;
18384 static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18386 Jim_Reference *refPtr;
18388 if (argc != 3) {
18389 Jim_WrongNumArgs(interp, 1, argv, "reference newValue");
18390 return JIM_ERR;
18392 if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL)
18393 return JIM_ERR;
18394 Jim_IncrRefCount(argv[2]);
18395 Jim_DecrRefCount(interp, refPtr->objPtr);
18396 refPtr->objPtr = argv[2];
18397 Jim_SetResult(interp, argv[2]);
18398 return JIM_OK;
18402 static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18404 if (argc != 1) {
18405 Jim_WrongNumArgs(interp, 1, argv, "");
18406 return JIM_ERR;
18408 Jim_SetResultInt(interp, Jim_Collect(interp));
18411 while (interp->freeList) {
18412 Jim_Obj *nextObjPtr = interp->freeList->nextObjPtr;
18413 Jim_Free(interp->freeList);
18414 interp->freeList = nextObjPtr;
18417 return JIM_OK;
18421 static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18423 if (argc != 2 && argc != 3) {
18424 Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?");
18425 return JIM_ERR;
18427 if (argc == 2) {
18428 Jim_Obj *cmdNamePtr;
18430 if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK)
18431 return JIM_ERR;
18432 if (cmdNamePtr != NULL)
18433 Jim_SetResult(interp, cmdNamePtr);
18435 else {
18436 if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK)
18437 return JIM_ERR;
18438 Jim_SetResult(interp, argv[2]);
18440 return JIM_OK;
18444 static int JimInfoReferences(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18446 Jim_Obj *listObjPtr;
18447 Jim_HashTableIterator htiter;
18448 Jim_HashEntry *he;
18450 listObjPtr = Jim_NewListObj(interp, NULL, 0);
18452 JimInitHashTableIterator(&interp->references, &htiter);
18453 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18454 char buf[JIM_REFERENCE_SPACE + 1];
18455 Jim_Reference *refPtr = Jim_GetHashEntryVal(he);
18456 const unsigned long *refId = he->key;
18458 JimFormatReference(buf, refPtr, *refId);
18459 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, buf, -1));
18461 Jim_SetResult(interp, listObjPtr);
18462 return JIM_OK;
18464 #endif
18467 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18469 if (argc != 3) {
18470 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
18471 return JIM_ERR;
18474 if (JimValidName(interp, "new procedure", argv[2])) {
18475 return JIM_ERR;
18478 return Jim_RenameCommand(interp, Jim_String(argv[1]), Jim_String(argv[2]));
18481 #define JIM_DICTMATCH_VALUES 0x0001
18483 typedef void JimDictMatchCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type);
18485 static void JimDictMatchKeys(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type)
18487 Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key);
18488 if (type & JIM_DICTMATCH_VALUES) {
18489 Jim_ListAppendElement(interp, listObjPtr, Jim_GetHashEntryVal(he));
18493 static Jim_Obj *JimDictPatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
18494 JimDictMatchCallbackType *callback, int type)
18496 Jim_HashEntry *he;
18497 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
18500 Jim_HashTableIterator htiter;
18501 JimInitHashTableIterator(ht, &htiter);
18502 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18503 if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), Jim_String((Jim_Obj *)he->key), 0)) {
18504 callback(interp, listObjPtr, he, type);
18508 return listObjPtr;
18512 int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObjPtr)
18514 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18515 return JIM_ERR;
18517 Jim_SetResult(interp, JimDictPatternMatch(interp, objPtr->internalRep.ptr, patternObjPtr, JimDictMatchKeys, 0));
18518 return JIM_OK;
18521 int Jim_DictValues(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObjPtr)
18523 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18524 return JIM_ERR;
18526 Jim_SetResult(interp, JimDictPatternMatch(interp, objPtr->internalRep.ptr, patternObjPtr, JimDictMatchKeys, JIM_DICTMATCH_VALUES));
18527 return JIM_OK;
18530 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
18532 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18533 return -1;
18535 return ((Jim_HashTable *)objPtr->internalRep.ptr)->used;
18538 int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
18540 Jim_HashTable *ht;
18541 unsigned int i;
18543 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18544 return JIM_ERR;
18547 ht = (Jim_HashTable *)objPtr->internalRep.ptr;
18550 printf("%d entries in table, %d buckets\n", ht->used, ht->size);
18552 for (i = 0; i < ht->size; i++) {
18553 Jim_HashEntry *he = ht->table[i];
18555 if (he) {
18556 printf("%d: ", i);
18558 while (he) {
18559 printf(" %s", Jim_String(he->key));
18560 he = he->next;
18562 printf("\n");
18565 return JIM_OK;
18568 static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
18570 Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
18572 Jim_AppendString(interp, prefixObj, " ", 1);
18573 Jim_AppendString(interp, prefixObj, subcmd, -1);
18575 return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
18579 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18581 Jim_Obj *objPtr;
18582 int option;
18583 static const char * const options[] = {
18584 "create", "get", "set", "unset", "exists", "keys", "size", "info",
18585 "merge", "with", "append", "lappend", "incr", "remove", "values", "for",
18586 "replace", "update", NULL
18588 enum
18590 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXISTS, OPT_KEYS, OPT_SIZE, OPT_INFO,
18591 OPT_MERGE, OPT_WITH, OPT_APPEND, OPT_LAPPEND, OPT_INCR, OPT_REMOVE, OPT_VALUES, OPT_FOR,
18592 OPT_REPLACE, OPT_UPDATE,
18595 if (argc < 2) {
18596 Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arguments ...?");
18597 return JIM_ERR;
18600 if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) {
18601 return JIM_ERR;
18604 switch (option) {
18605 case OPT_GET:
18606 if (argc < 3) {
18607 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?key ...?");
18608 return JIM_ERR;
18610 if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
18611 JIM_ERRMSG) != JIM_OK) {
18612 return JIM_ERR;
18614 Jim_SetResult(interp, objPtr);
18615 return JIM_OK;
18617 case OPT_SET:
18618 if (argc < 5) {
18619 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
18620 return JIM_ERR;
18622 return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
18624 case OPT_EXISTS:
18625 if (argc < 4) {
18626 Jim_WrongNumArgs(interp, 2, argv, "dictionary key ?key ...?");
18627 return JIM_ERR;
18629 else {
18630 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_ERRMSG);
18631 if (rc < 0) {
18632 return JIM_ERR;
18634 Jim_SetResultBool(interp, rc == JIM_OK);
18635 return JIM_OK;
18638 case OPT_UNSET:
18639 if (argc < 4) {
18640 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
18641 return JIM_ERR;
18643 if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, 0) != JIM_OK) {
18644 return JIM_ERR;
18646 return JIM_OK;
18648 case OPT_KEYS:
18649 if (argc != 3 && argc != 4) {
18650 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?pattern?");
18651 return JIM_ERR;
18653 return Jim_DictKeys(interp, argv[2], argc == 4 ? argv[3] : NULL);
18655 case OPT_SIZE:
18656 if (argc != 3) {
18657 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18658 return JIM_ERR;
18660 else if (Jim_DictSize(interp, argv[2]) < 0) {
18661 return JIM_ERR;
18663 Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
18664 return JIM_OK;
18666 case OPT_MERGE:
18667 if (argc == 2) {
18668 return JIM_OK;
18670 if (Jim_DictSize(interp, argv[2]) < 0) {
18671 return JIM_ERR;
18674 break;
18676 case OPT_UPDATE:
18677 if (argc < 6 || argc % 2) {
18679 argc = 2;
18681 break;
18683 case OPT_CREATE:
18684 if (argc % 2) {
18685 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
18686 return JIM_ERR;
18688 objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
18689 Jim_SetResult(interp, objPtr);
18690 return JIM_OK;
18692 case OPT_INFO:
18693 if (argc != 3) {
18694 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18695 return JIM_ERR;
18697 return Jim_DictInfo(interp, argv[2]);
18700 return Jim_EvalEnsemble(interp, "dict", options[option], argc - 2, argv + 2);
18704 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18706 static const char * const options[] = {
18707 "-nobackslashes", "-nocommands", "-novariables", NULL
18709 enum
18710 { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
18711 int i;
18712 int flags = JIM_SUBST_FLAG;
18713 Jim_Obj *objPtr;
18715 if (argc < 2) {
18716 Jim_WrongNumArgs(interp, 1, argv, "?options? string");
18717 return JIM_ERR;
18719 for (i = 1; i < (argc - 1); i++) {
18720 int option;
18722 if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
18723 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18724 return JIM_ERR;
18726 switch (option) {
18727 case OPT_NOBACKSLASHES:
18728 flags |= JIM_SUBST_NOESC;
18729 break;
18730 case OPT_NOCOMMANDS:
18731 flags |= JIM_SUBST_NOCMD;
18732 break;
18733 case OPT_NOVARIABLES:
18734 flags |= JIM_SUBST_NOVAR;
18735 break;
18738 if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
18739 return JIM_ERR;
18741 Jim_SetResult(interp, objPtr);
18742 return JIM_OK;
18746 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18748 int cmd;
18749 Jim_Obj *objPtr;
18750 int mode = 0;
18752 static const char * const commands[] = {
18753 "body", "statics", "commands", "procs", "channels", "exists", "globals", "level", "frame", "locals",
18754 "vars", "version", "patchlevel", "complete", "args", "hostname",
18755 "script", "source", "stacktrace", "nameofexecutable", "returncodes",
18756 "references", "alias", NULL
18758 enum
18759 { INFO_BODY, INFO_STATICS, INFO_COMMANDS, INFO_PROCS, INFO_CHANNELS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
18760 INFO_FRAME, INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_PATCHLEVEL, INFO_COMPLETE, INFO_ARGS,
18761 INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE,
18762 INFO_RETURNCODES, INFO_REFERENCES, INFO_ALIAS,
18765 #ifdef jim_ext_namespace
18766 int nons = 0;
18768 if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
18770 argc--;
18771 argv++;
18772 nons = 1;
18774 #endif
18776 if (argc < 2) {
18777 Jim_WrongNumArgs(interp, 1, argv, "subcommand ?args ...?");
18778 return JIM_ERR;
18780 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV)
18781 != JIM_OK) {
18782 return JIM_ERR;
18786 switch (cmd) {
18787 case INFO_EXISTS:
18788 if (argc != 3) {
18789 Jim_WrongNumArgs(interp, 2, argv, "varName");
18790 return JIM_ERR;
18792 Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
18793 break;
18795 case INFO_ALIAS:{
18796 Jim_Cmd *cmdPtr;
18798 if (argc != 3) {
18799 Jim_WrongNumArgs(interp, 2, argv, "command");
18800 return JIM_ERR;
18802 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18803 return JIM_ERR;
18805 if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
18806 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
18807 return JIM_ERR;
18809 Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
18810 return JIM_OK;
18813 case INFO_CHANNELS:
18814 mode++;
18815 #ifndef jim_ext_aio
18816 Jim_SetResultString(interp, "aio not enabled", -1);
18817 return JIM_ERR;
18818 #endif
18820 case INFO_PROCS:
18821 mode++;
18823 case INFO_COMMANDS:
18825 if (argc != 2 && argc != 3) {
18826 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18827 return JIM_ERR;
18829 #ifdef jim_ext_namespace
18830 if (!nons) {
18831 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18832 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18835 #endif
18836 Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
18837 break;
18839 case INFO_VARS:
18840 mode++;
18842 case INFO_LOCALS:
18843 mode++;
18845 case INFO_GLOBALS:
18847 if (argc != 2 && argc != 3) {
18848 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18849 return JIM_ERR;
18851 #ifdef jim_ext_namespace
18852 if (!nons) {
18853 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18854 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18857 #endif
18858 Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
18859 break;
18861 case INFO_SCRIPT:
18862 if (argc != 2) {
18863 Jim_WrongNumArgs(interp, 2, argv, "");
18864 return JIM_ERR;
18866 Jim_SetResult(interp, JimGetScript(interp, interp->currentScriptObj)->fileNameObj);
18867 break;
18869 case INFO_SOURCE:{
18870 jim_wide line;
18871 Jim_Obj *resObjPtr;
18872 Jim_Obj *fileNameObj;
18874 if (argc != 3 && argc != 5) {
18875 Jim_WrongNumArgs(interp, 2, argv, "source ?filename line?");
18876 return JIM_ERR;
18878 if (argc == 5) {
18879 if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
18880 return JIM_ERR;
18882 resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
18883 JimSetSourceInfo(interp, resObjPtr, argv[3], line);
18885 else {
18886 if (argv[2]->typePtr == &sourceObjType) {
18887 fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj;
18888 line = argv[2]->internalRep.sourceValue.lineNumber;
18890 else if (argv[2]->typePtr == &scriptObjType) {
18891 ScriptObj *script = JimGetScript(interp, argv[2]);
18892 fileNameObj = script->fileNameObj;
18893 line = script->firstline;
18895 else {
18896 fileNameObj = interp->emptyObj;
18897 line = 1;
18899 resObjPtr = Jim_NewListObj(interp, NULL, 0);
18900 Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
18901 Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
18903 Jim_SetResult(interp, resObjPtr);
18904 break;
18907 case INFO_STACKTRACE:
18908 Jim_SetResult(interp, interp->stackTrace);
18909 break;
18911 case INFO_LEVEL:
18912 case INFO_FRAME:
18913 switch (argc) {
18914 case 2:
18915 Jim_SetResultInt(interp, interp->framePtr->level);
18916 break;
18918 case 3:
18919 if (JimInfoLevel(interp, argv[2], &objPtr, cmd == INFO_LEVEL) != JIM_OK) {
18920 return JIM_ERR;
18922 Jim_SetResult(interp, objPtr);
18923 break;
18925 default:
18926 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
18927 return JIM_ERR;
18929 break;
18931 case INFO_BODY:
18932 case INFO_STATICS:
18933 case INFO_ARGS:{
18934 Jim_Cmd *cmdPtr;
18936 if (argc != 3) {
18937 Jim_WrongNumArgs(interp, 2, argv, "procname");
18938 return JIM_ERR;
18940 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18941 return JIM_ERR;
18943 if (!cmdPtr->isproc) {
18944 Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
18945 return JIM_ERR;
18947 switch (cmd) {
18948 case INFO_BODY:
18949 Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
18950 break;
18951 case INFO_ARGS:
18952 Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
18953 break;
18954 case INFO_STATICS:
18955 if (cmdPtr->u.proc.staticVars) {
18956 int mode = JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES;
18957 Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
18958 NULL, JimVariablesMatch, mode));
18960 break;
18962 break;
18965 case INFO_VERSION:
18966 case INFO_PATCHLEVEL:{
18967 char buf[(JIM_INTEGER_SPACE * 2) + 1];
18969 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
18970 Jim_SetResultString(interp, buf, -1);
18971 break;
18974 case INFO_COMPLETE:
18975 if (argc != 3 && argc != 4) {
18976 Jim_WrongNumArgs(interp, 2, argv, "script ?missing?");
18977 return JIM_ERR;
18979 else {
18980 char missing;
18982 Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing));
18983 if (missing != ' ' && argc == 4) {
18984 Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
18987 break;
18989 case INFO_HOSTNAME:
18991 return Jim_Eval(interp, "os.gethostname");
18993 case INFO_NAMEOFEXECUTABLE:
18995 return Jim_Eval(interp, "{info nameofexecutable}");
18997 case INFO_RETURNCODES:
18998 if (argc == 2) {
18999 int i;
19000 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
19002 for (i = 0; jimReturnCodes[i]; i++) {
19003 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
19004 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
19005 jimReturnCodes[i], -1));
19008 Jim_SetResult(interp, listObjPtr);
19010 else if (argc == 3) {
19011 long code;
19012 const char *name;
19014 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
19015 return JIM_ERR;
19017 name = Jim_ReturnCode(code);
19018 if (*name == '?') {
19019 Jim_SetResultInt(interp, code);
19021 else {
19022 Jim_SetResultString(interp, name, -1);
19025 else {
19026 Jim_WrongNumArgs(interp, 2, argv, "?code?");
19027 return JIM_ERR;
19029 break;
19030 case INFO_REFERENCES:
19031 #ifdef JIM_REFERENCES
19032 return JimInfoReferences(interp, argc, argv);
19033 #else
19034 Jim_SetResultString(interp, "not supported", -1);
19035 return JIM_ERR;
19036 #endif
19038 return JIM_OK;
19042 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19044 Jim_Obj *objPtr;
19045 int result = 0;
19047 static const char * const options[] = {
19048 "-command", "-proc", "-alias", "-var", NULL
19050 enum
19052 OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
19054 int option;
19056 if (argc == 2) {
19057 option = OPT_VAR;
19058 objPtr = argv[1];
19060 else if (argc == 3) {
19061 if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
19062 return JIM_ERR;
19064 objPtr = argv[2];
19066 else {
19067 Jim_WrongNumArgs(interp, 1, argv, "?option? name");
19068 return JIM_ERR;
19071 if (option == OPT_VAR) {
19072 result = Jim_GetVariable(interp, objPtr, 0) != NULL;
19074 else {
19076 Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
19078 if (cmd) {
19079 switch (option) {
19080 case OPT_COMMAND:
19081 result = 1;
19082 break;
19084 case OPT_ALIAS:
19085 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
19086 break;
19088 case OPT_PROC:
19089 result = cmd->isproc;
19090 break;
19094 Jim_SetResultBool(interp, result);
19095 return JIM_OK;
19099 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19101 const char *str, *splitChars, *noMatchStart;
19102 int splitLen, strLen;
19103 Jim_Obj *resObjPtr;
19104 int c;
19105 int len;
19107 if (argc != 2 && argc != 3) {
19108 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
19109 return JIM_ERR;
19112 str = Jim_GetString(argv[1], &len);
19113 if (len == 0) {
19114 return JIM_OK;
19116 strLen = Jim_Utf8Length(interp, argv[1]);
19119 if (argc == 2) {
19120 splitChars = " \n\t\r";
19121 splitLen = 4;
19123 else {
19124 splitChars = Jim_String(argv[2]);
19125 splitLen = Jim_Utf8Length(interp, argv[2]);
19128 noMatchStart = str;
19129 resObjPtr = Jim_NewListObj(interp, NULL, 0);
19132 if (splitLen) {
19133 Jim_Obj *objPtr;
19134 while (strLen--) {
19135 const char *sc = splitChars;
19136 int scLen = splitLen;
19137 int sl = utf8_tounicode(str, &c);
19138 while (scLen--) {
19139 int pc;
19140 sc += utf8_tounicode(sc, &pc);
19141 if (c == pc) {
19142 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
19143 Jim_ListAppendElement(interp, resObjPtr, objPtr);
19144 noMatchStart = str + sl;
19145 break;
19148 str += sl;
19150 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
19151 Jim_ListAppendElement(interp, resObjPtr, objPtr);
19153 else {
19154 Jim_Obj **commonObj = NULL;
19155 #define NUM_COMMON (128 - 9)
19156 while (strLen--) {
19157 int n = utf8_tounicode(str, &c);
19158 #ifdef JIM_OPTIMIZATION
19159 if (c >= 9 && c < 128) {
19161 c -= 9;
19162 if (!commonObj) {
19163 commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
19164 memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
19166 if (!commonObj[c]) {
19167 commonObj[c] = Jim_NewStringObj(interp, str, 1);
19169 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
19170 str++;
19171 continue;
19173 #endif
19174 Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
19175 str += n;
19177 Jim_Free(commonObj);
19180 Jim_SetResult(interp, resObjPtr);
19181 return JIM_OK;
19185 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19187 const char *joinStr;
19188 int joinStrLen;
19190 if (argc != 2 && argc != 3) {
19191 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
19192 return JIM_ERR;
19195 if (argc == 2) {
19196 joinStr = " ";
19197 joinStrLen = 1;
19199 else {
19200 joinStr = Jim_GetString(argv[2], &joinStrLen);
19202 Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
19203 return JIM_OK;
19207 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19209 Jim_Obj *objPtr;
19211 if (argc < 2) {
19212 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
19213 return JIM_ERR;
19215 objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
19216 if (objPtr == NULL)
19217 return JIM_ERR;
19218 Jim_SetResult(interp, objPtr);
19219 return JIM_OK;
19223 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19225 Jim_Obj *listPtr, **outVec;
19226 int outc, i;
19228 if (argc < 3) {
19229 Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
19230 return JIM_ERR;
19232 if (argv[2]->typePtr != &scanFmtStringObjType)
19233 SetScanFmtFromAny(interp, argv[2]);
19234 if (FormatGetError(argv[2]) != 0) {
19235 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
19236 return JIM_ERR;
19238 if (argc > 3) {
19239 int maxPos = FormatGetMaxPos(argv[2]);
19240 int count = FormatGetCnvCount(argv[2]);
19242 if (maxPos > argc - 3) {
19243 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
19244 return JIM_ERR;
19246 else if (count > argc - 3) {
19247 Jim_SetResultString(interp, "different numbers of variable names and "
19248 "field specifiers", -1);
19249 return JIM_ERR;
19251 else if (count < argc - 3) {
19252 Jim_SetResultString(interp, "variable is not assigned by any "
19253 "conversion specifiers", -1);
19254 return JIM_ERR;
19257 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
19258 if (listPtr == 0)
19259 return JIM_ERR;
19260 if (argc > 3) {
19261 int rc = JIM_OK;
19262 int count = 0;
19264 if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
19265 int len = Jim_ListLength(interp, listPtr);
19267 if (len != 0) {
19268 JimListGetElements(interp, listPtr, &outc, &outVec);
19269 for (i = 0; i < outc; ++i) {
19270 if (Jim_Length(outVec[i]) > 0) {
19271 ++count;
19272 if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
19273 rc = JIM_ERR;
19278 Jim_FreeNewObj(interp, listPtr);
19280 else {
19281 count = -1;
19283 if (rc == JIM_OK) {
19284 Jim_SetResultInt(interp, count);
19286 return rc;
19288 else {
19289 if (listPtr == (Jim_Obj *)EOF) {
19290 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
19291 return JIM_OK;
19293 Jim_SetResult(interp, listPtr);
19295 return JIM_OK;
19299 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19301 if (argc != 2 && argc != 3) {
19302 Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
19303 return JIM_ERR;
19305 Jim_SetResult(interp, argv[1]);
19306 if (argc == 3) {
19307 JimSetStackTrace(interp, argv[2]);
19308 return JIM_ERR;
19310 interp->addStackTrace++;
19311 return JIM_ERR;
19315 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19317 Jim_Obj *objPtr;
19319 if (argc != 4) {
19320 Jim_WrongNumArgs(interp, 1, argv, "list first last");
19321 return JIM_ERR;
19323 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
19324 return JIM_ERR;
19325 Jim_SetResult(interp, objPtr);
19326 return JIM_OK;
19330 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19332 Jim_Obj *objPtr;
19333 long count;
19335 if (argc < 2 || Jim_GetLong(interp, argv[1], &count) != JIM_OK || count < 0) {
19336 Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
19337 return JIM_ERR;
19340 if (count == 0 || argc == 2) {
19341 return JIM_OK;
19344 argc -= 2;
19345 argv += 2;
19347 objPtr = Jim_NewListObj(interp, argv, argc);
19348 while (--count) {
19349 ListInsertElements(objPtr, -1, argc, argv);
19352 Jim_SetResult(interp, objPtr);
19353 return JIM_OK;
19356 char **Jim_GetEnviron(void)
19358 #if defined(HAVE__NSGETENVIRON)
19359 return *_NSGetEnviron();
19360 #else
19361 #if !defined(NO_ENVIRON_EXTERN)
19362 extern char **environ;
19363 #endif
19365 return environ;
19366 #endif
19369 void Jim_SetEnviron(char **env)
19371 #if defined(HAVE__NSGETENVIRON)
19372 *_NSGetEnviron() = env;
19373 #else
19374 #if !defined(NO_ENVIRON_EXTERN)
19375 extern char **environ;
19376 #endif
19378 environ = env;
19379 #endif
19383 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19385 const char *key;
19386 const char *val;
19388 if (argc == 1) {
19389 char **e = Jim_GetEnviron();
19391 int i;
19392 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
19394 for (i = 0; e[i]; i++) {
19395 const char *equals = strchr(e[i], '=');
19397 if (equals) {
19398 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
19399 equals - e[i]));
19400 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
19404 Jim_SetResult(interp, listObjPtr);
19405 return JIM_OK;
19408 if (argc < 2) {
19409 Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
19410 return JIM_ERR;
19412 key = Jim_String(argv[1]);
19413 val = getenv(key);
19414 if (val == NULL) {
19415 if (argc < 3) {
19416 Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
19417 return JIM_ERR;
19419 val = Jim_String(argv[2]);
19421 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
19422 return JIM_OK;
19426 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19428 int retval;
19430 if (argc != 2) {
19431 Jim_WrongNumArgs(interp, 1, argv, "fileName");
19432 return JIM_ERR;
19434 retval = Jim_EvalFile(interp, Jim_String(argv[1]));
19435 if (retval == JIM_RETURN)
19436 return JIM_OK;
19437 return retval;
19441 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19443 Jim_Obj *revObjPtr, **ele;
19444 int len;
19446 if (argc != 2) {
19447 Jim_WrongNumArgs(interp, 1, argv, "list");
19448 return JIM_ERR;
19450 JimListGetElements(interp, argv[1], &len, &ele);
19451 len--;
19452 revObjPtr = Jim_NewListObj(interp, NULL, 0);
19453 while (len >= 0)
19454 ListAppendElement(revObjPtr, ele[len--]);
19455 Jim_SetResult(interp, revObjPtr);
19456 return JIM_OK;
19459 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
19461 jim_wide len;
19463 if (step == 0)
19464 return -1;
19465 if (start == end)
19466 return 0;
19467 else if (step > 0 && start > end)
19468 return -1;
19469 else if (step < 0 && end > start)
19470 return -1;
19471 len = end - start;
19472 if (len < 0)
19473 len = -len;
19474 if (step < 0)
19475 step = -step;
19476 len = 1 + ((len - 1) / step);
19477 if (len > INT_MAX)
19478 len = INT_MAX;
19479 return (int)((len < 0) ? -1 : len);
19483 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19485 jim_wide start = 0, end, step = 1;
19486 int len, i;
19487 Jim_Obj *objPtr;
19489 if (argc < 2 || argc > 4) {
19490 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
19491 return JIM_ERR;
19493 if (argc == 2) {
19494 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
19495 return JIM_ERR;
19497 else {
19498 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
19499 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
19500 return JIM_ERR;
19501 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
19502 return JIM_ERR;
19504 if ((len = JimRangeLen(start, end, step)) == -1) {
19505 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
19506 return JIM_ERR;
19508 objPtr = Jim_NewListObj(interp, NULL, 0);
19509 for (i = 0; i < len; i++)
19510 ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
19511 Jim_SetResult(interp, objPtr);
19512 return JIM_OK;
19516 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19518 jim_wide min = 0, max = 0, len, maxMul;
19520 if (argc < 1 || argc > 3) {
19521 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
19522 return JIM_ERR;
19524 if (argc == 1) {
19525 max = JIM_WIDE_MAX;
19526 } else if (argc == 2) {
19527 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
19528 return JIM_ERR;
19529 } else if (argc == 3) {
19530 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
19531 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
19532 return JIM_ERR;
19534 len = max-min;
19535 if (len < 0) {
19536 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
19537 return JIM_ERR;
19539 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
19540 while (1) {
19541 jim_wide r;
19543 JimRandomBytes(interp, &r, sizeof(jim_wide));
19544 if (r < 0 || r >= maxMul) continue;
19545 r = (len == 0) ? 0 : r%len;
19546 Jim_SetResultInt(interp, min+r);
19547 return JIM_OK;
19551 static const struct {
19552 const char *name;
19553 Jim_CmdProc *cmdProc;
19554 } Jim_CoreCommandsTable[] = {
19555 {"alias", Jim_AliasCoreCommand},
19556 {"set", Jim_SetCoreCommand},
19557 {"unset", Jim_UnsetCoreCommand},
19558 {"puts", Jim_PutsCoreCommand},
19559 {"+", Jim_AddCoreCommand},
19560 {"*", Jim_MulCoreCommand},
19561 {"-", Jim_SubCoreCommand},
19562 {"/", Jim_DivCoreCommand},
19563 {"incr", Jim_IncrCoreCommand},
19564 {"while", Jim_WhileCoreCommand},
19565 {"loop", Jim_LoopCoreCommand},
19566 {"for", Jim_ForCoreCommand},
19567 {"foreach", Jim_ForeachCoreCommand},
19568 {"lmap", Jim_LmapCoreCommand},
19569 {"lassign", Jim_LassignCoreCommand},
19570 {"if", Jim_IfCoreCommand},
19571 {"switch", Jim_SwitchCoreCommand},
19572 {"list", Jim_ListCoreCommand},
19573 {"lindex", Jim_LindexCoreCommand},
19574 {"lset", Jim_LsetCoreCommand},
19575 {"lsearch", Jim_LsearchCoreCommand},
19576 {"llength", Jim_LlengthCoreCommand},
19577 {"lappend", Jim_LappendCoreCommand},
19578 {"linsert", Jim_LinsertCoreCommand},
19579 {"lreplace", Jim_LreplaceCoreCommand},
19580 {"lsort", Jim_LsortCoreCommand},
19581 {"append", Jim_AppendCoreCommand},
19582 {"debug", Jim_DebugCoreCommand},
19583 {"eval", Jim_EvalCoreCommand},
19584 {"uplevel", Jim_UplevelCoreCommand},
19585 {"expr", Jim_ExprCoreCommand},
19586 {"break", Jim_BreakCoreCommand},
19587 {"continue", Jim_ContinueCoreCommand},
19588 {"proc", Jim_ProcCoreCommand},
19589 {"concat", Jim_ConcatCoreCommand},
19590 {"return", Jim_ReturnCoreCommand},
19591 {"upvar", Jim_UpvarCoreCommand},
19592 {"global", Jim_GlobalCoreCommand},
19593 {"string", Jim_StringCoreCommand},
19594 {"time", Jim_TimeCoreCommand},
19595 {"exit", Jim_ExitCoreCommand},
19596 {"catch", Jim_CatchCoreCommand},
19597 #ifdef JIM_REFERENCES
19598 {"ref", Jim_RefCoreCommand},
19599 {"getref", Jim_GetrefCoreCommand},
19600 {"setref", Jim_SetrefCoreCommand},
19601 {"finalize", Jim_FinalizeCoreCommand},
19602 {"collect", Jim_CollectCoreCommand},
19603 #endif
19604 {"rename", Jim_RenameCoreCommand},
19605 {"dict", Jim_DictCoreCommand},
19606 {"subst", Jim_SubstCoreCommand},
19607 {"info", Jim_InfoCoreCommand},
19608 {"exists", Jim_ExistsCoreCommand},
19609 {"split", Jim_SplitCoreCommand},
19610 {"join", Jim_JoinCoreCommand},
19611 {"format", Jim_FormatCoreCommand},
19612 {"scan", Jim_ScanCoreCommand},
19613 {"error", Jim_ErrorCoreCommand},
19614 {"lrange", Jim_LrangeCoreCommand},
19615 {"lrepeat", Jim_LrepeatCoreCommand},
19616 {"env", Jim_EnvCoreCommand},
19617 {"source", Jim_SourceCoreCommand},
19618 {"lreverse", Jim_LreverseCoreCommand},
19619 {"range", Jim_RangeCoreCommand},
19620 {"rand", Jim_RandCoreCommand},
19621 {"tailcall", Jim_TailcallCoreCommand},
19622 {"local", Jim_LocalCoreCommand},
19623 {"upcall", Jim_UpcallCoreCommand},
19624 {"apply", Jim_ApplyCoreCommand},
19625 {NULL, NULL},
19628 void Jim_RegisterCoreCommands(Jim_Interp *interp)
19630 int i = 0;
19632 while (Jim_CoreCommandsTable[i].name != NULL) {
19633 Jim_CreateCommand(interp,
19634 Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
19635 i++;
19639 void Jim_MakeErrorMessage(Jim_Interp *interp)
19641 Jim_Obj *argv[2];
19643 argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
19644 argv[1] = interp->result;
19646 Jim_EvalObjVector(interp, 2, argv);
19649 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
19650 const char *prefix, const char *const *tablePtr, const char *name)
19652 int count;
19653 char **tablePtrSorted;
19654 int i;
19656 for (count = 0; tablePtr[count]; count++) {
19659 if (name == NULL) {
19660 name = "option";
19663 Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
19664 tablePtrSorted = Jim_Alloc(sizeof(char *) * count);
19665 memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
19666 qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
19667 for (i = 0; i < count; i++) {
19668 if (i + 1 == count && count > 1) {
19669 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
19671 Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
19672 if (i + 1 != count) {
19673 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
19676 Jim_Free(tablePtrSorted);
19679 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
19680 const char *const *tablePtr, int *indexPtr, const char *name, int flags)
19682 const char *bad = "bad ";
19683 const char *const *entryPtr = NULL;
19684 int i;
19685 int match = -1;
19686 int arglen;
19687 const char *arg = Jim_GetString(objPtr, &arglen);
19689 *indexPtr = -1;
19691 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
19692 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
19694 *indexPtr = i;
19695 return JIM_OK;
19697 if (flags & JIM_ENUM_ABBREV) {
19698 if (strncmp(arg, *entryPtr, arglen) == 0) {
19699 if (*arg == '-' && arglen == 1) {
19700 break;
19702 if (match >= 0) {
19703 bad = "ambiguous ";
19704 goto ambiguous;
19706 match = i;
19712 if (match >= 0) {
19713 *indexPtr = match;
19714 return JIM_OK;
19717 ambiguous:
19718 if (flags & JIM_ERRMSG) {
19719 JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
19721 return JIM_ERR;
19724 int Jim_FindByName(const char *name, const char * const array[], size_t len)
19726 int i;
19728 for (i = 0; i < (int)len; i++) {
19729 if (array[i] && strcmp(array[i], name) == 0) {
19730 return i;
19733 return -1;
19736 int Jim_IsDict(Jim_Obj *objPtr)
19738 return objPtr->typePtr == &dictObjType;
19741 int Jim_IsList(Jim_Obj *objPtr)
19743 return objPtr->typePtr == &listObjType;
19746 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
19749 int len = strlen(format);
19750 int extra = 0;
19751 int n = 0;
19752 const char *params[5];
19753 char *buf;
19754 va_list args;
19755 int i;
19757 va_start(args, format);
19759 for (i = 0; i < len && n < 5; i++) {
19760 int l;
19762 if (strncmp(format + i, "%s", 2) == 0) {
19763 params[n] = va_arg(args, char *);
19765 l = strlen(params[n]);
19767 else if (strncmp(format + i, "%#s", 3) == 0) {
19768 Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
19770 params[n] = Jim_GetString(objPtr, &l);
19772 else {
19773 if (format[i] == '%') {
19774 i++;
19776 continue;
19778 n++;
19779 extra += l;
19782 len += extra;
19783 buf = Jim_Alloc(len + 1);
19784 len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
19786 va_end(args);
19788 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
19792 #ifndef jim_ext_package
19793 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
19795 return JIM_OK;
19797 #endif
19798 #ifndef jim_ext_aio
19799 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
19801 Jim_SetResultString(interp, "aio not enabled", -1);
19802 return NULL;
19804 #endif
19807 #include <stdio.h>
19808 #include <string.h>
19811 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19814 return JIM_OK;
19817 static const jim_subcmd_type dummy_subcmd = {
19818 "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
19821 static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
19823 const char *s = "";
19825 for (; ct->cmd; ct++) {
19826 if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
19827 Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, NULL);
19828 s = sep;
19833 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
19834 Jim_Obj *cmd, Jim_Obj *subcmd)
19836 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19837 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), ", ", type,
19838 " command \"", Jim_String(subcmd), "\": should be ", NULL);
19839 add_commands(interp, command_table, ", ");
19842 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
19843 Jim_Obj *const *argv)
19845 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19846 Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]),
19847 " command ... \", where command is one of: ", NULL);
19848 add_commands(interp, command_table, ", ");
19851 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
19853 if (cmd) {
19854 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
19856 Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
19857 if (ct->args && *ct->args) {
19858 Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
19862 static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd)
19864 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19865 add_cmd_usage(interp, command_table, subcmd);
19866 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19869 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
19870 int argc, Jim_Obj *const *argv)
19872 const jim_subcmd_type *ct;
19873 const jim_subcmd_type *partial = 0;
19874 int cmdlen;
19875 Jim_Obj *cmd;
19876 const char *cmdstr;
19877 const char *cmdname;
19878 int help = 0;
19880 cmdname = Jim_String(argv[0]);
19882 if (argc < 2) {
19883 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19884 Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname,
19885 " command ...\"\n", NULL);
19886 Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help ?command?\" for help", NULL);
19887 return 0;
19890 cmd = argv[1];
19893 if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
19894 if (argc == 2) {
19896 show_cmd_usage(interp, command_table, argc, argv);
19897 return &dummy_subcmd;
19899 help = 1;
19902 cmd = argv[2];
19906 if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
19908 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19909 add_commands(interp, command_table, " ");
19910 return &dummy_subcmd;
19913 cmdstr = Jim_GetString(cmd, &cmdlen);
19915 for (ct = command_table; ct->cmd; ct++) {
19916 if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
19918 break;
19920 if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
19921 if (partial) {
19923 if (help) {
19925 show_cmd_usage(interp, command_table, argc, argv);
19926 return &dummy_subcmd;
19928 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
19929 return 0;
19931 partial = ct;
19933 continue;
19937 if (partial && !ct->cmd) {
19938 ct = partial;
19941 if (!ct->cmd) {
19943 if (help) {
19945 show_cmd_usage(interp, command_table, argc, argv);
19946 return &dummy_subcmd;
19948 bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
19949 return 0;
19952 if (help) {
19953 Jim_SetResultString(interp, "Usage: ", -1);
19955 add_cmd_usage(interp, ct, argv[0]);
19956 return &dummy_subcmd;
19960 if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) {
19961 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19963 add_cmd_usage(interp, ct, argv[0]);
19964 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19966 return 0;
19970 return ct;
19973 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
19975 int ret = JIM_ERR;
19977 if (ct) {
19978 if (ct->flags & JIM_MODFLAG_FULLARGV) {
19979 ret = ct->function(interp, argc, argv);
19981 else {
19982 ret = ct->function(interp, argc - 2, argv + 2);
19984 if (ret < 0) {
19985 set_wrong_args(interp, ct, argv[0]);
19986 ret = JIM_ERR;
19989 return ret;
19992 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19994 const jim_subcmd_type *ct =
19995 Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
19997 return Jim_CallSubCmd(interp, ct, argc, argv);
20000 #include <ctype.h>
20001 #include <stdlib.h>
20002 #include <string.h>
20003 #include <stdio.h>
20004 #include <assert.h>
20007 int utf8_fromunicode(char *p, unsigned uc)
20009 if (uc <= 0x7f) {
20010 *p = uc;
20011 return 1;
20013 else if (uc <= 0x7ff) {
20014 *p++ = 0xc0 | ((uc & 0x7c0) >> 6);
20015 *p = 0x80 | (uc & 0x3f);
20016 return 2;
20018 else if (uc <= 0xffff) {
20019 *p++ = 0xe0 | ((uc & 0xf000) >> 12);
20020 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
20021 *p = 0x80 | (uc & 0x3f);
20022 return 3;
20025 else {
20026 *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
20027 *p++ = 0x80 | ((uc & 0x3f000) >> 12);
20028 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
20029 *p = 0x80 | (uc & 0x3f);
20030 return 4;
20034 #include <ctype.h>
20035 #include <string.h>
20038 #define JIM_INTEGER_SPACE 24
20039 #define MAX_FLOAT_WIDTH 320
20041 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
20043 const char *span, *format, *formatEnd, *msg;
20044 int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
20045 static const char * const mixedXPG =
20046 "cannot mix \"%\" and \"%n$\" conversion specifiers";
20047 static const char * const badIndex[2] = {
20048 "not enough arguments for all format specifiers",
20049 "\"%n$\" argument index out of range"
20051 int formatLen;
20052 Jim_Obj *resultPtr;
20054 char *num_buffer = NULL;
20055 int num_buffer_size = 0;
20057 span = format = Jim_GetString(fmtObjPtr, &formatLen);
20058 formatEnd = format + formatLen;
20059 resultPtr = Jim_NewEmptyStringObj(interp);
20061 while (format != formatEnd) {
20062 char *end;
20063 int gotMinus, sawFlag;
20064 int gotPrecision, useShort;
20065 long width, precision;
20066 int newXpg;
20067 int ch;
20068 int step;
20069 int doubleType;
20070 char pad = ' ';
20071 char spec[2*JIM_INTEGER_SPACE + 12];
20072 char *p;
20074 int formatted_chars;
20075 int formatted_bytes;
20076 const char *formatted_buf;
20078 step = utf8_tounicode(format, &ch);
20079 format += step;
20080 if (ch != '%') {
20081 numBytes += step;
20082 continue;
20084 if (numBytes) {
20085 Jim_AppendString(interp, resultPtr, span, numBytes);
20086 numBytes = 0;
20090 step = utf8_tounicode(format, &ch);
20091 if (ch == '%') {
20092 span = format;
20093 numBytes = step;
20094 format += step;
20095 continue;
20099 newXpg = 0;
20100 if (isdigit(ch)) {
20101 int position = strtoul(format, &end, 10);
20102 if (*end == '$') {
20103 newXpg = 1;
20104 objIndex = position - 1;
20105 format = end + 1;
20106 step = utf8_tounicode(format, &ch);
20109 if (newXpg) {
20110 if (gotSequential) {
20111 msg = mixedXPG;
20112 goto errorMsg;
20114 gotXpg = 1;
20115 } else {
20116 if (gotXpg) {
20117 msg = mixedXPG;
20118 goto errorMsg;
20120 gotSequential = 1;
20122 if ((objIndex < 0) || (objIndex >= objc)) {
20123 msg = badIndex[gotXpg];
20124 goto errorMsg;
20127 p = spec;
20128 *p++ = '%';
20130 gotMinus = 0;
20131 sawFlag = 1;
20132 do {
20133 switch (ch) {
20134 case '-':
20135 gotMinus = 1;
20136 break;
20137 case '0':
20138 pad = ch;
20139 break;
20140 case ' ':
20141 case '+':
20142 case '#':
20143 break;
20144 default:
20145 sawFlag = 0;
20146 continue;
20148 *p++ = ch;
20149 format += step;
20150 step = utf8_tounicode(format, &ch);
20151 } while (sawFlag);
20154 width = 0;
20155 if (isdigit(ch)) {
20156 width = strtoul(format, &end, 10);
20157 format = end;
20158 step = utf8_tounicode(format, &ch);
20159 } else if (ch == '*') {
20160 if (objIndex >= objc - 1) {
20161 msg = badIndex[gotXpg];
20162 goto errorMsg;
20164 if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) {
20165 goto error;
20167 if (width < 0) {
20168 width = -width;
20169 if (!gotMinus) {
20170 *p++ = '-';
20171 gotMinus = 1;
20174 objIndex++;
20175 format += step;
20176 step = utf8_tounicode(format, &ch);
20180 gotPrecision = precision = 0;
20181 if (ch == '.') {
20182 gotPrecision = 1;
20183 format += step;
20184 step = utf8_tounicode(format, &ch);
20186 if (isdigit(ch)) {
20187 precision = strtoul(format, &end, 10);
20188 format = end;
20189 step = utf8_tounicode(format, &ch);
20190 } else if (ch == '*') {
20191 if (objIndex >= objc - 1) {
20192 msg = badIndex[gotXpg];
20193 goto errorMsg;
20195 if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) {
20196 goto error;
20200 if (precision < 0) {
20201 precision = 0;
20203 objIndex++;
20204 format += step;
20205 step = utf8_tounicode(format, &ch);
20209 useShort = 0;
20210 if (ch == 'h') {
20211 useShort = 1;
20212 format += step;
20213 step = utf8_tounicode(format, &ch);
20214 } else if (ch == 'l') {
20216 format += step;
20217 step = utf8_tounicode(format, &ch);
20218 if (ch == 'l') {
20219 format += step;
20220 step = utf8_tounicode(format, &ch);
20224 format += step;
20225 span = format;
20228 if (ch == 'i') {
20229 ch = 'd';
20232 doubleType = 0;
20234 switch (ch) {
20235 case '\0':
20236 msg = "format string ended in middle of field specifier";
20237 goto errorMsg;
20238 case 's': {
20239 formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
20240 formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
20241 if (gotPrecision && (precision < formatted_chars)) {
20243 formatted_chars = precision;
20244 formatted_bytes = utf8_index(formatted_buf, precision);
20246 break;
20248 case 'c': {
20249 jim_wide code;
20251 if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
20252 goto error;
20255 formatted_bytes = utf8_getchars(spec, code);
20256 formatted_buf = spec;
20257 formatted_chars = 1;
20258 break;
20260 case 'b': {
20261 unsigned jim_wide w;
20262 int length;
20263 int i;
20264 int j;
20266 if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
20267 goto error;
20269 length = sizeof(w) * 8;
20273 if (num_buffer_size < length + 1) {
20274 num_buffer_size = length + 1;
20275 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20278 j = 0;
20279 for (i = length; i > 0; ) {
20280 i--;
20281 if (w & ((unsigned jim_wide)1 << i)) {
20282 num_buffer[j++] = '1';
20284 else if (j || i == 0) {
20285 num_buffer[j++] = '0';
20288 num_buffer[j] = 0;
20289 formatted_chars = formatted_bytes = j;
20290 formatted_buf = num_buffer;
20291 break;
20294 case 'e':
20295 case 'E':
20296 case 'f':
20297 case 'g':
20298 case 'G':
20299 doubleType = 1;
20301 case 'd':
20302 case 'u':
20303 case 'o':
20304 case 'x':
20305 case 'X': {
20306 jim_wide w;
20307 double d;
20308 int length;
20311 if (width) {
20312 p += sprintf(p, "%ld", width);
20314 if (gotPrecision) {
20315 p += sprintf(p, ".%ld", precision);
20319 if (doubleType) {
20320 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
20321 goto error;
20323 length = MAX_FLOAT_WIDTH;
20325 else {
20326 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) {
20327 goto error;
20329 length = JIM_INTEGER_SPACE;
20330 if (useShort) {
20331 if (ch == 'd') {
20332 w = (short)w;
20334 else {
20335 w = (unsigned short)w;
20338 *p++ = 'l';
20339 #ifdef HAVE_LONG_LONG
20340 if (sizeof(long long) == sizeof(jim_wide)) {
20341 *p++ = 'l';
20343 #endif
20346 *p++ = (char) ch;
20347 *p = '\0';
20350 if (width > length) {
20351 length = width;
20353 if (gotPrecision) {
20354 length += precision;
20358 if (num_buffer_size < length + 1) {
20359 num_buffer_size = length + 1;
20360 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20363 if (doubleType) {
20364 snprintf(num_buffer, length + 1, spec, d);
20366 else {
20367 formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
20369 formatted_chars = formatted_bytes = strlen(num_buffer);
20370 formatted_buf = num_buffer;
20371 break;
20374 default: {
20376 spec[0] = ch;
20377 spec[1] = '\0';
20378 Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
20379 goto error;
20383 if (!gotMinus) {
20384 while (formatted_chars < width) {
20385 Jim_AppendString(interp, resultPtr, &pad, 1);
20386 formatted_chars++;
20390 Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes);
20392 while (formatted_chars < width) {
20393 Jim_AppendString(interp, resultPtr, &pad, 1);
20394 formatted_chars++;
20397 objIndex += gotSequential;
20399 if (numBytes) {
20400 Jim_AppendString(interp, resultPtr, span, numBytes);
20403 Jim_Free(num_buffer);
20404 return resultPtr;
20406 errorMsg:
20407 Jim_SetResultString(interp, msg, -1);
20408 error:
20409 Jim_FreeNewObj(interp, resultPtr);
20410 Jim_Free(num_buffer);
20411 return NULL;
20415 #if defined(JIM_REGEXP)
20416 #include <stdio.h>
20417 #include <ctype.h>
20418 #include <stdlib.h>
20419 #include <string.h>
20423 #define REG_MAX_PAREN 100
20427 #define END 0
20428 #define BOL 1
20429 #define EOL 2
20430 #define ANY 3
20431 #define ANYOF 4
20432 #define ANYBUT 5
20433 #define BRANCH 6
20434 #define BACK 7
20435 #define EXACTLY 8
20436 #define NOTHING 9
20437 #define REP 10
20438 #define REPMIN 11
20439 #define REPX 12
20440 #define REPXMIN 13
20441 #define BOLX 14
20442 #define EOLX 15
20443 #define WORDA 16
20444 #define WORDZ 17
20446 #define OPENNC 1000
20447 #define OPEN 1001
20452 #define CLOSENC 2000
20453 #define CLOSE 2001
20454 #define CLOSE_END (CLOSE+REG_MAX_PAREN)
20456 #define REG_MAGIC 0xFADED00D
20459 #define OP(preg, p) (preg->program[p])
20460 #define NEXT(preg, p) (preg->program[p + 1])
20461 #define OPERAND(p) ((p) + 2)
20466 #define FAIL(R,M) { (R)->err = (M); return (M); }
20467 #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
20468 #define META "^$.[()|?{+*"
20470 #define HASWIDTH 1
20471 #define SIMPLE 2
20472 #define SPSTART 4
20473 #define WORST 0
20475 #define MAX_REP_COUNT 1000000
20477 static int reg(regex_t *preg, int paren , int *flagp );
20478 static int regpiece(regex_t *preg, int *flagp );
20479 static int regbranch(regex_t *preg, int *flagp );
20480 static int regatom(regex_t *preg, int *flagp );
20481 static int regnode(regex_t *preg, int op );
20482 static int regnext(regex_t *preg, int p );
20483 static void regc(regex_t *preg, int b );
20484 static int reginsert(regex_t *preg, int op, int size, int opnd );
20485 static void regtail(regex_t *preg, int p, int val);
20486 static void regoptail(regex_t *preg, int p, int val );
20487 static int regopsize(regex_t *preg, int p );
20489 static int reg_range_find(const int *string, int c);
20490 static const char *str_find(const char *string, int c, int nocase);
20491 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase);
20494 #ifdef DEBUG
20495 static int regnarrate = 0;
20496 static void regdump(regex_t *preg);
20497 static const char *regprop( int op );
20498 #endif
20501 static int str_int_len(const int *seq)
20503 int n = 0;
20504 while (*seq++) {
20505 n++;
20507 return n;
20510 int regcomp(regex_t *preg, const char *exp, int cflags)
20512 int scan;
20513 int longest;
20514 unsigned len;
20515 int flags;
20517 #ifdef DEBUG
20518 fprintf(stderr, "Compiling: '%s'\n", exp);
20519 #endif
20520 memset(preg, 0, sizeof(*preg));
20522 if (exp == NULL)
20523 FAIL(preg, REG_ERR_NULL_ARGUMENT);
20526 preg->cflags = cflags;
20527 preg->regparse = exp;
20530 preg->proglen = (strlen(exp) + 1) * 5;
20531 preg->program = malloc(preg->proglen * sizeof(int));
20532 if (preg->program == NULL)
20533 FAIL(preg, REG_ERR_NOMEM);
20535 regc(preg, REG_MAGIC);
20536 if (reg(preg, 0, &flags) == 0) {
20537 return preg->err;
20541 if (preg->re_nsub >= REG_MAX_PAREN)
20542 FAIL(preg,REG_ERR_TOO_BIG);
20545 preg->regstart = 0;
20546 preg->reganch = 0;
20547 preg->regmust = 0;
20548 preg->regmlen = 0;
20549 scan = 1;
20550 if (OP(preg, regnext(preg, scan)) == END) {
20551 scan = OPERAND(scan);
20554 if (OP(preg, scan) == EXACTLY) {
20555 preg->regstart = preg->program[OPERAND(scan)];
20557 else if (OP(preg, scan) == BOL)
20558 preg->reganch++;
20560 if (flags&SPSTART) {
20561 longest = 0;
20562 len = 0;
20563 for (; scan != 0; scan = regnext(preg, scan)) {
20564 if (OP(preg, scan) == EXACTLY) {
20565 int plen = str_int_len(preg->program + OPERAND(scan));
20566 if (plen >= len) {
20567 longest = OPERAND(scan);
20568 len = plen;
20572 preg->regmust = longest;
20573 preg->regmlen = len;
20577 #ifdef DEBUG
20578 regdump(preg);
20579 #endif
20581 return 0;
20584 static int reg(regex_t *preg, int paren , int *flagp )
20586 int ret;
20587 int br;
20588 int ender;
20589 int parno = 0;
20590 int flags;
20592 *flagp = HASWIDTH;
20595 if (paren) {
20596 if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
20598 preg->regparse += 2;
20599 parno = -1;
20601 else {
20602 parno = ++preg->re_nsub;
20604 ret = regnode(preg, OPEN+parno);
20605 } else
20606 ret = 0;
20609 br = regbranch(preg, &flags);
20610 if (br == 0)
20611 return 0;
20612 if (ret != 0)
20613 regtail(preg, ret, br);
20614 else
20615 ret = br;
20616 if (!(flags&HASWIDTH))
20617 *flagp &= ~HASWIDTH;
20618 *flagp |= flags&SPSTART;
20619 while (*preg->regparse == '|') {
20620 preg->regparse++;
20621 br = regbranch(preg, &flags);
20622 if (br == 0)
20623 return 0;
20624 regtail(preg, ret, br);
20625 if (!(flags&HASWIDTH))
20626 *flagp &= ~HASWIDTH;
20627 *flagp |= flags&SPSTART;
20631 ender = regnode(preg, (paren) ? CLOSE+parno : END);
20632 regtail(preg, ret, ender);
20635 for (br = ret; br != 0; br = regnext(preg, br))
20636 regoptail(preg, br, ender);
20639 if (paren && *preg->regparse++ != ')') {
20640 preg->err = REG_ERR_UNMATCHED_PAREN;
20641 return 0;
20642 } else if (!paren && *preg->regparse != '\0') {
20643 if (*preg->regparse == ')') {
20644 preg->err = REG_ERR_UNMATCHED_PAREN;
20645 return 0;
20646 } else {
20647 preg->err = REG_ERR_JUNK_ON_END;
20648 return 0;
20652 return(ret);
20655 static int regbranch(regex_t *preg, int *flagp )
20657 int ret;
20658 int chain;
20659 int latest;
20660 int flags;
20662 *flagp = WORST;
20664 ret = regnode(preg, BRANCH);
20665 chain = 0;
20666 while (*preg->regparse != '\0' && *preg->regparse != ')' &&
20667 *preg->regparse != '|') {
20668 latest = regpiece(preg, &flags);
20669 if (latest == 0)
20670 return 0;
20671 *flagp |= flags&HASWIDTH;
20672 if (chain == 0) {
20673 *flagp |= flags&SPSTART;
20675 else {
20676 regtail(preg, chain, latest);
20678 chain = latest;
20680 if (chain == 0)
20681 (void) regnode(preg, NOTHING);
20683 return(ret);
20686 static int regpiece(regex_t *preg, int *flagp)
20688 int ret;
20689 char op;
20690 int next;
20691 int flags;
20692 int min;
20693 int max;
20695 ret = regatom(preg, &flags);
20696 if (ret == 0)
20697 return 0;
20699 op = *preg->regparse;
20700 if (!ISMULT(op)) {
20701 *flagp = flags;
20702 return(ret);
20705 if (!(flags&HASWIDTH) && op != '?') {
20706 preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
20707 return 0;
20711 if (op == '{') {
20712 char *end;
20714 min = strtoul(preg->regparse + 1, &end, 10);
20715 if (end == preg->regparse + 1) {
20716 preg->err = REG_ERR_BAD_COUNT;
20717 return 0;
20719 if (*end == '}') {
20720 max = min;
20722 else {
20723 preg->regparse = end;
20724 max = strtoul(preg->regparse + 1, &end, 10);
20725 if (*end != '}') {
20726 preg->err = REG_ERR_UNMATCHED_BRACES;
20727 return 0;
20730 if (end == preg->regparse + 1) {
20731 max = MAX_REP_COUNT;
20733 else if (max < min || max >= 100) {
20734 preg->err = REG_ERR_BAD_COUNT;
20735 return 0;
20737 if (min >= 100) {
20738 preg->err = REG_ERR_BAD_COUNT;
20739 return 0;
20742 preg->regparse = strchr(preg->regparse, '}');
20744 else {
20745 min = (op == '+');
20746 max = (op == '?' ? 1 : MAX_REP_COUNT);
20749 if (preg->regparse[1] == '?') {
20750 preg->regparse++;
20751 next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret);
20753 else {
20754 next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret);
20756 preg->program[ret + 2] = max;
20757 preg->program[ret + 3] = min;
20758 preg->program[ret + 4] = 0;
20760 *flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART);
20762 if (!(flags & SIMPLE)) {
20763 int back = regnode(preg, BACK);
20764 regtail(preg, back, ret);
20765 regtail(preg, next, back);
20768 preg->regparse++;
20769 if (ISMULT(*preg->regparse)) {
20770 preg->err = REG_ERR_NESTED_COUNT;
20771 return 0;
20774 return ret;
20777 static void reg_addrange(regex_t *preg, int lower, int upper)
20779 if (lower > upper) {
20780 reg_addrange(preg, upper, lower);
20783 regc(preg, upper - lower + 1);
20784 regc(preg, lower);
20787 static void reg_addrange_str(regex_t *preg, const char *str)
20789 while (*str) {
20790 reg_addrange(preg, *str, *str);
20791 str++;
20795 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper)
20797 int l = utf8_tounicode(s, uc);
20798 if (upper) {
20799 *uc = utf8_upper(*uc);
20801 return l;
20804 static int hexdigitval(int c)
20806 if (c >= '0' && c <= '9')
20807 return c - '0';
20808 if (c >= 'a' && c <= 'f')
20809 return c - 'a' + 10;
20810 if (c >= 'A' && c <= 'F')
20811 return c - 'A' + 10;
20812 return -1;
20815 static int parse_hex(const char *s, int n, int *uc)
20817 int val = 0;
20818 int k;
20820 for (k = 0; k < n; k++) {
20821 int c = hexdigitval(*s++);
20822 if (c == -1) {
20823 break;
20825 val = (val << 4) | c;
20827 if (k) {
20828 *uc = val;
20830 return k;
20833 static int reg_decode_escape(const char *s, int *ch)
20835 int n;
20836 const char *s0 = s;
20838 *ch = *s++;
20840 switch (*ch) {
20841 case 'b': *ch = '\b'; break;
20842 case 'e': *ch = 27; break;
20843 case 'f': *ch = '\f'; break;
20844 case 'n': *ch = '\n'; break;
20845 case 'r': *ch = '\r'; break;
20846 case 't': *ch = '\t'; break;
20847 case 'v': *ch = '\v'; break;
20848 case 'u':
20849 if (*s == '{') {
20851 n = parse_hex(s + 1, 6, ch);
20852 if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
20853 s += n + 2;
20855 else {
20857 *ch = 'u';
20860 else if ((n = parse_hex(s, 4, ch)) > 0) {
20861 s += n;
20863 break;
20864 case 'U':
20865 if ((n = parse_hex(s, 8, ch)) > 0) {
20866 s += n;
20868 break;
20869 case 'x':
20870 if ((n = parse_hex(s, 2, ch)) > 0) {
20871 s += n;
20873 break;
20874 case '\0':
20875 s--;
20876 *ch = '\\';
20877 break;
20879 return s - s0;
20882 static int regatom(regex_t *preg, int *flagp)
20884 int ret;
20885 int flags;
20886 int nocase = (preg->cflags & REG_ICASE);
20888 int ch;
20889 int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);
20891 *flagp = WORST;
20893 preg->regparse += n;
20894 switch (ch) {
20896 case '^':
20897 ret = regnode(preg, BOL);
20898 break;
20899 case '$':
20900 ret = regnode(preg, EOL);
20901 break;
20902 case '.':
20903 ret = regnode(preg, ANY);
20904 *flagp |= HASWIDTH|SIMPLE;
20905 break;
20906 case '[': {
20907 const char *pattern = preg->regparse;
20909 if (*pattern == '^') {
20910 ret = regnode(preg, ANYBUT);
20911 pattern++;
20912 } else
20913 ret = regnode(preg, ANYOF);
20916 if (*pattern == ']' || *pattern == '-') {
20917 reg_addrange(preg, *pattern, *pattern);
20918 pattern++;
20921 while (*pattern && *pattern != ']') {
20923 int start;
20924 int end;
20926 pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
20927 if (start == '\\') {
20928 pattern += reg_decode_escape(pattern, &start);
20929 if (start == 0) {
20930 preg->err = REG_ERR_NULL_CHAR;
20931 return 0;
20934 if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
20936 pattern += utf8_tounicode(pattern, &end);
20937 pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
20938 if (end == '\\') {
20939 pattern += reg_decode_escape(pattern, &end);
20940 if (end == 0) {
20941 preg->err = REG_ERR_NULL_CHAR;
20942 return 0;
20946 reg_addrange(preg, start, end);
20947 continue;
20949 if (start == '[' && pattern[0] == ':') {
20950 static const char *character_class[] = {
20951 ":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
20952 ":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
20954 enum {
20955 CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
20956 CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
20957 CC_NUM
20959 int i;
20961 for (i = 0; i < CC_NUM; i++) {
20962 int n = strlen(character_class[i]);
20963 if (strncmp(pattern, character_class[i], n) == 0) {
20965 pattern += n + 1;
20966 break;
20969 if (i != CC_NUM) {
20970 switch (i) {
20971 case CC_ALNUM:
20972 reg_addrange(preg, '0', '9');
20974 case CC_ALPHA:
20975 if ((preg->cflags & REG_ICASE) == 0) {
20976 reg_addrange(preg, 'a', 'z');
20978 reg_addrange(preg, 'A', 'Z');
20979 break;
20980 case CC_SPACE:
20981 reg_addrange_str(preg, " \t\r\n\f\v");
20982 break;
20983 case CC_BLANK:
20984 reg_addrange_str(preg, " \t");
20985 break;
20986 case CC_UPPER:
20987 reg_addrange(preg, 'A', 'Z');
20988 break;
20989 case CC_LOWER:
20990 reg_addrange(preg, 'a', 'z');
20991 break;
20992 case CC_XDIGIT:
20993 reg_addrange(preg, 'a', 'f');
20994 reg_addrange(preg, 'A', 'F');
20996 case CC_DIGIT:
20997 reg_addrange(preg, '0', '9');
20998 break;
20999 case CC_CNTRL:
21000 reg_addrange(preg, 0, 31);
21001 reg_addrange(preg, 127, 127);
21002 break;
21003 case CC_PRINT:
21004 reg_addrange(preg, ' ', '~');
21005 break;
21006 case CC_GRAPH:
21007 reg_addrange(preg, '!', '~');
21008 break;
21009 case CC_PUNCT:
21010 reg_addrange(preg, '!', '/');
21011 reg_addrange(preg, ':', '@');
21012 reg_addrange(preg, '[', '`');
21013 reg_addrange(preg, '{', '~');
21014 break;
21016 continue;
21020 reg_addrange(preg, start, start);
21022 regc(preg, '\0');
21024 if (*pattern) {
21025 pattern++;
21027 preg->regparse = pattern;
21029 *flagp |= HASWIDTH|SIMPLE;
21031 break;
21032 case '(':
21033 ret = reg(preg, 1, &flags);
21034 if (ret == 0)
21035 return 0;
21036 *flagp |= flags&(HASWIDTH|SPSTART);
21037 break;
21038 case '\0':
21039 case '|':
21040 case ')':
21041 preg->err = REG_ERR_INTERNAL;
21042 return 0;
21043 case '?':
21044 case '+':
21045 case '*':
21046 case '{':
21047 preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
21048 return 0;
21049 case '\\':
21050 ch = *preg->regparse++;
21051 switch (ch) {
21052 case '\0':
21053 preg->err = REG_ERR_TRAILING_BACKSLASH;
21054 return 0;
21055 case 'A':
21056 ret = regnode(preg, BOLX);
21057 break;
21058 case 'Z':
21059 ret = regnode(preg, EOLX);
21060 break;
21061 case '<':
21062 case 'm':
21063 ret = regnode(preg, WORDA);
21064 break;
21065 case '>':
21066 case 'M':
21067 ret = regnode(preg, WORDZ);
21068 break;
21069 case 'd':
21070 case 'D':
21071 ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT);
21072 reg_addrange(preg, '0', '9');
21073 regc(preg, '\0');
21074 *flagp |= HASWIDTH|SIMPLE;
21075 break;
21076 case 'w':
21077 case 'W':
21078 ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT);
21079 if ((preg->cflags & REG_ICASE) == 0) {
21080 reg_addrange(preg, 'a', 'z');
21082 reg_addrange(preg, 'A', 'Z');
21083 reg_addrange(preg, '0', '9');
21084 reg_addrange(preg, '_', '_');
21085 regc(preg, '\0');
21086 *flagp |= HASWIDTH|SIMPLE;
21087 break;
21088 case 's':
21089 case 'S':
21090 ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT);
21091 reg_addrange_str(preg," \t\r\n\f\v");
21092 regc(preg, '\0');
21093 *flagp |= HASWIDTH|SIMPLE;
21094 break;
21096 default:
21099 preg->regparse--;
21100 goto de_fault;
21102 break;
21103 de_fault:
21104 default: {
21105 int added = 0;
21108 preg->regparse -= n;
21110 ret = regnode(preg, EXACTLY);
21114 while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
21115 n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
21116 if (ch == '\\' && preg->regparse[n]) {
21117 if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) {
21119 break;
21121 n += reg_decode_escape(preg->regparse + n, &ch);
21122 if (ch == 0) {
21123 preg->err = REG_ERR_NULL_CHAR;
21124 return 0;
21129 if (ISMULT(preg->regparse[n])) {
21131 if (added) {
21133 break;
21136 regc(preg, ch);
21137 added++;
21138 preg->regparse += n;
21139 break;
21143 regc(preg, ch);
21144 added++;
21145 preg->regparse += n;
21147 regc(preg, '\0');
21149 *flagp |= HASWIDTH;
21150 if (added == 1)
21151 *flagp |= SIMPLE;
21152 break;
21154 break;
21157 return(ret);
21160 static void reg_grow(regex_t *preg, int n)
21162 if (preg->p + n >= preg->proglen) {
21163 preg->proglen = (preg->p + n) * 2;
21164 preg->program = realloc(preg->program, preg->proglen * sizeof(int));
21169 static int regnode(regex_t *preg, int op)
21171 reg_grow(preg, 2);
21174 preg->program[preg->p++] = op;
21175 preg->program[preg->p++] = 0;
21178 return preg->p - 2;
21181 static void regc(regex_t *preg, int b )
21183 reg_grow(preg, 1);
21184 preg->program[preg->p++] = b;
21187 static int reginsert(regex_t *preg, int op, int size, int opnd )
21189 reg_grow(preg, size);
21192 memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
21194 memset(preg->program + opnd, 0, sizeof(int) * size);
21196 preg->program[opnd] = op;
21198 preg->p += size;
21200 return opnd + size;
21203 static void regtail(regex_t *preg, int p, int val)
21205 int scan;
21206 int temp;
21207 int offset;
21210 scan = p;
21211 for (;;) {
21212 temp = regnext(preg, scan);
21213 if (temp == 0)
21214 break;
21215 scan = temp;
21218 if (OP(preg, scan) == BACK)
21219 offset = scan - val;
21220 else
21221 offset = val - scan;
21223 preg->program[scan + 1] = offset;
21227 static void regoptail(regex_t *preg, int p, int val )
21230 if (p != 0 && OP(preg, p) == BRANCH) {
21231 regtail(preg, OPERAND(p), val);
21236 static int regtry(regex_t *preg, const char *string );
21237 static int regmatch(regex_t *preg, int prog);
21238 static int regrepeat(regex_t *preg, int p, int max);
21240 int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
21242 const char *s;
21243 int scan;
21246 if (preg == NULL || preg->program == NULL || string == NULL) {
21247 return REG_ERR_NULL_ARGUMENT;
21251 if (*preg->program != REG_MAGIC) {
21252 return REG_ERR_CORRUPTED;
21255 #ifdef DEBUG
21256 fprintf(stderr, "regexec: %s\n", string);
21257 regdump(preg);
21258 #endif
21260 preg->eflags = eflags;
21261 preg->pmatch = pmatch;
21262 preg->nmatch = nmatch;
21263 preg->start = string;
21266 for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) {
21267 int op = OP(preg, scan);
21268 if (op == END)
21269 break;
21270 if (op == REPX || op == REPXMIN)
21271 preg->program[scan + 4] = 0;
21275 if (preg->regmust != 0) {
21276 s = string;
21277 while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
21278 if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
21279 break;
21281 s++;
21283 if (s == NULL)
21284 return REG_NOMATCH;
21288 preg->regbol = string;
21291 if (preg->reganch) {
21292 if (eflags & REG_NOTBOL) {
21294 goto nextline;
21296 while (1) {
21297 if (regtry(preg, string)) {
21298 return REG_NOERROR;
21300 if (*string) {
21301 nextline:
21302 if (preg->cflags & REG_NEWLINE) {
21304 string = strchr(string, '\n');
21305 if (string) {
21306 preg->regbol = ++string;
21307 continue;
21311 return REG_NOMATCH;
21316 s = string;
21317 if (preg->regstart != '\0') {
21319 while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
21320 if (regtry(preg, s))
21321 return REG_NOERROR;
21322 s++;
21325 else
21327 while (1) {
21328 if (regtry(preg, s))
21329 return REG_NOERROR;
21330 if (*s == '\0') {
21331 break;
21333 else {
21334 int c;
21335 s += utf8_tounicode(s, &c);
21340 return REG_NOMATCH;
21344 static int regtry( regex_t *preg, const char *string )
21346 int i;
21348 preg->reginput = string;
21350 for (i = 0; i < preg->nmatch; i++) {
21351 preg->pmatch[i].rm_so = -1;
21352 preg->pmatch[i].rm_eo = -1;
21354 if (regmatch(preg, 1)) {
21355 preg->pmatch[0].rm_so = string - preg->start;
21356 preg->pmatch[0].rm_eo = preg->reginput - preg->start;
21357 return(1);
21358 } else
21359 return(0);
21362 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase)
21364 const char *s = string;
21365 while (proglen && *s) {
21366 int ch;
21367 int n = reg_utf8_tounicode_case(s, &ch, nocase);
21368 if (ch != *prog) {
21369 return -1;
21371 prog++;
21372 s += n;
21373 proglen--;
21375 if (proglen == 0) {
21376 return s - string;
21378 return -1;
21381 static int reg_range_find(const int *range, int c)
21383 while (*range) {
21385 if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
21386 return 1;
21388 range += 2;
21390 return 0;
21393 static const char *str_find(const char *string, int c, int nocase)
21395 if (nocase) {
21397 c = utf8_upper(c);
21399 while (*string) {
21400 int ch;
21401 int n = reg_utf8_tounicode_case(string, &ch, nocase);
21402 if (c == ch) {
21403 return string;
21405 string += n;
21407 return NULL;
21410 static int reg_iseol(regex_t *preg, int ch)
21412 if (preg->cflags & REG_NEWLINE) {
21413 return ch == '\0' || ch == '\n';
21415 else {
21416 return ch == '\0';
21420 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin)
21422 int nextch = '\0';
21423 const char *save;
21424 int no;
21425 int c;
21427 int max = preg->program[scan + 2];
21428 int min = preg->program[scan + 3];
21429 int next = regnext(preg, scan);
21431 if (OP(preg, next) == EXACTLY) {
21432 nextch = preg->program[OPERAND(next)];
21434 save = preg->reginput;
21435 no = regrepeat(preg, scan + 5, max);
21436 if (no < min) {
21437 return 0;
21439 if (matchmin) {
21441 max = no;
21442 no = min;
21445 while (1) {
21446 if (matchmin) {
21447 if (no > max) {
21448 break;
21451 else {
21452 if (no < min) {
21453 break;
21456 preg->reginput = save + utf8_index(save, no);
21457 reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21459 if (reg_iseol(preg, nextch) || c == nextch) {
21460 if (regmatch(preg, next)) {
21461 return(1);
21464 if (matchmin) {
21466 no++;
21468 else {
21470 no--;
21473 return(0);
21476 static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
21478 int *scanpt = preg->program + scan;
21480 int max = scanpt[2];
21481 int min = scanpt[3];
21484 if (scanpt[4] < min) {
21486 scanpt[4]++;
21487 if (regmatch(preg, scan + 5)) {
21488 return 1;
21490 scanpt[4]--;
21491 return 0;
21493 if (scanpt[4] > max) {
21494 return 0;
21497 if (matchmin) {
21499 if (regmatch(preg, regnext(preg, scan))) {
21500 return 1;
21503 scanpt[4]++;
21504 if (regmatch(preg, scan + 5)) {
21505 return 1;
21507 scanpt[4]--;
21508 return 0;
21511 if (scanpt[4] < max) {
21512 scanpt[4]++;
21513 if (regmatch(preg, scan + 5)) {
21514 return 1;
21516 scanpt[4]--;
21519 return regmatch(preg, regnext(preg, scan));
21523 static int regmatch(regex_t *preg, int prog)
21525 int scan;
21526 int next;
21527 const char *save;
21529 scan = prog;
21531 #ifdef DEBUG
21532 if (scan != 0 && regnarrate)
21533 fprintf(stderr, "%s(\n", regprop(scan));
21534 #endif
21535 while (scan != 0) {
21536 int n;
21537 int c;
21538 #ifdef DEBUG
21539 if (regnarrate) {
21540 fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
21542 #endif
21543 next = regnext(preg, scan);
21544 n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21546 switch (OP(preg, scan)) {
21547 case BOLX:
21548 if ((preg->eflags & REG_NOTBOL)) {
21549 return(0);
21552 case BOL:
21553 if (preg->reginput != preg->regbol) {
21554 return(0);
21556 break;
21557 case EOLX:
21558 if (c != 0) {
21560 return 0;
21562 break;
21563 case EOL:
21564 if (!reg_iseol(preg, c)) {
21565 return(0);
21567 break;
21568 case WORDA:
21570 if ((!isalnum(UCHAR(c))) && c != '_')
21571 return(0);
21573 if (preg->reginput > preg->regbol &&
21574 (isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
21575 return(0);
21576 break;
21577 case WORDZ:
21579 if (preg->reginput > preg->regbol) {
21581 if (reg_iseol(preg, c) || !isalnum(UCHAR(c)) || c != '_') {
21582 c = preg->reginput[-1];
21584 if (isalnum(UCHAR(c)) || c == '_') {
21585 break;
21590 return(0);
21592 case ANY:
21593 if (reg_iseol(preg, c))
21594 return 0;
21595 preg->reginput += n;
21596 break;
21597 case EXACTLY: {
21598 int opnd;
21599 int len;
21600 int slen;
21602 opnd = OPERAND(scan);
21603 len = str_int_len(preg->program + opnd);
21605 slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE);
21606 if (slen < 0) {
21607 return(0);
21609 preg->reginput += slen;
21611 break;
21612 case ANYOF:
21613 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) {
21614 return(0);
21616 preg->reginput += n;
21617 break;
21618 case ANYBUT:
21619 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) {
21620 return(0);
21622 preg->reginput += n;
21623 break;
21624 case NOTHING:
21625 break;
21626 case BACK:
21627 break;
21628 case BRANCH:
21629 if (OP(preg, next) != BRANCH)
21630 next = OPERAND(scan);
21631 else {
21632 do {
21633 save = preg->reginput;
21634 if (regmatch(preg, OPERAND(scan))) {
21635 return(1);
21637 preg->reginput = save;
21638 scan = regnext(preg, scan);
21639 } while (scan != 0 && OP(preg, scan) == BRANCH);
21640 return(0);
21643 break;
21644 case REP:
21645 case REPMIN:
21646 return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);
21648 case REPX:
21649 case REPXMIN:
21650 return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);
21652 case END:
21653 return 1;
21655 case OPENNC:
21656 case CLOSENC:
21657 return regmatch(preg, next);
21659 default:
21660 if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
21661 save = preg->reginput;
21662 if (regmatch(preg, next)) {
21663 if (OP(preg, scan) < CLOSE) {
21664 int no = OP(preg, scan) - OPEN;
21665 if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
21666 preg->pmatch[no].rm_so = save - preg->start;
21669 else {
21670 int no = OP(preg, scan) - CLOSE;
21671 if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
21672 preg->pmatch[no].rm_eo = save - preg->start;
21675 return(1);
21677 return(0);
21679 return REG_ERR_INTERNAL;
21682 scan = next;
21685 return REG_ERR_INTERNAL;
21688 static int regrepeat(regex_t *preg, int p, int max)
21690 int count = 0;
21691 const char *scan;
21692 int opnd;
21693 int ch;
21694 int n;
21696 scan = preg->reginput;
21697 opnd = OPERAND(p);
21698 switch (OP(preg, p)) {
21699 case ANY:
21701 while (!reg_iseol(preg, *scan) && count < max) {
21702 count++;
21703 scan++;
21705 break;
21706 case EXACTLY:
21707 while (count < max) {
21708 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21709 if (preg->program[opnd] != ch) {
21710 break;
21712 count++;
21713 scan += n;
21715 break;
21716 case ANYOF:
21717 while (count < max) {
21718 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21719 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) {
21720 break;
21722 count++;
21723 scan += n;
21725 break;
21726 case ANYBUT:
21727 while (count < max) {
21728 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21729 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
21730 break;
21732 count++;
21733 scan += n;
21735 break;
21736 default:
21737 preg->err = REG_ERR_INTERNAL;
21738 count = 0;
21739 break;
21741 preg->reginput = scan;
21743 return(count);
21746 static int regnext(regex_t *preg, int p )
21748 int offset;
21750 offset = NEXT(preg, p);
21752 if (offset == 0)
21753 return 0;
21755 if (OP(preg, p) == BACK)
21756 return(p-offset);
21757 else
21758 return(p+offset);
21761 static int regopsize(regex_t *preg, int p )
21764 switch (OP(preg, p)) {
21765 case REP:
21766 case REPMIN:
21767 case REPX:
21768 case REPXMIN:
21769 return 5;
21771 case ANYOF:
21772 case ANYBUT:
21773 case EXACTLY: {
21774 int s = p + 2;
21775 while (preg->program[s++]) {
21777 return s - p;
21780 return 2;
21784 size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
21786 static const char *error_strings[] = {
21787 "success",
21788 "no match",
21789 "bad pattern",
21790 "null argument",
21791 "unknown error",
21792 "too big",
21793 "out of memory",
21794 "too many ()",
21795 "parentheses () not balanced",
21796 "braces {} not balanced",
21797 "invalid repetition count(s)",
21798 "extra characters",
21799 "*+ of empty atom",
21800 "nested count",
21801 "internal error",
21802 "count follows nothing",
21803 "trailing backslash",
21804 "corrupted program",
21805 "contains null char",
21807 const char *err;
21809 if (errcode < 0 || errcode >= REG_ERR_NUM) {
21810 err = "Bad error code";
21812 else {
21813 err = error_strings[errcode];
21816 return snprintf(errbuf, errbuf_size, "%s", err);
21819 void regfree(regex_t *preg)
21821 free(preg->program);
21824 #endif
21826 #if defined(_WIN32) || defined(WIN32)
21827 #ifndef STRICT
21828 #define STRICT
21829 #endif
21830 #define WIN32_LEAN_AND_MEAN
21831 #include <windows.h>
21833 #if defined(HAVE_DLOPEN_COMPAT)
21834 void *dlopen(const char *path, int mode)
21836 JIM_NOTUSED(mode);
21838 return (void *)LoadLibraryA(path);
21841 int dlclose(void *handle)
21843 FreeLibrary((HANDLE)handle);
21844 return 0;
21847 void *dlsym(void *handle, const char *symbol)
21849 return GetProcAddress((HMODULE)handle, symbol);
21852 char *dlerror(void)
21854 static char msg[121];
21855 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
21856 LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
21857 return msg;
21859 #endif
21861 #ifdef _MSC_VER
21863 #include <sys/timeb.h>
21866 int gettimeofday(struct timeval *tv, void *unused)
21868 struct _timeb tb;
21870 _ftime(&tb);
21871 tv->tv_sec = tb.time;
21872 tv->tv_usec = tb.millitm * 1000;
21874 return 0;
21878 DIR *opendir(const char *name)
21880 DIR *dir = 0;
21882 if (name && name[0]) {
21883 size_t base_length = strlen(name);
21884 const char *all =
21885 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
21887 if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
21888 (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
21889 strcat(strcpy(dir->name, name), all);
21891 if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
21892 dir->result.d_name = 0;
21893 else {
21894 Jim_Free(dir->name);
21895 Jim_Free(dir);
21896 dir = 0;
21899 else {
21900 Jim_Free(dir);
21901 dir = 0;
21902 errno = ENOMEM;
21905 else {
21906 errno = EINVAL;
21908 return dir;
21911 int closedir(DIR * dir)
21913 int result = -1;
21915 if (dir) {
21916 if (dir->handle != -1)
21917 result = _findclose(dir->handle);
21918 Jim_Free(dir->name);
21919 Jim_Free(dir);
21921 if (result == -1)
21922 errno = EBADF;
21923 return result;
21926 struct dirent *readdir(DIR * dir)
21928 struct dirent *result = 0;
21930 if (dir && dir->handle != -1) {
21931 if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
21932 result = &dir->result;
21933 result->d_name = dir->info.name;
21936 else {
21937 errno = EBADF;
21939 return result;
21941 #endif
21942 #endif
21943 #ifndef JIM_BOOTSTRAP_LIB_ONLY
21944 #include <errno.h>
21945 #include <string.h>
21948 #ifdef USE_LINENOISE
21949 #ifdef HAVE_UNISTD_H
21950 #include <unistd.h>
21951 #endif
21952 #include "linenoise.h"
21953 #else
21954 #define MAX_LINE_LEN 512
21955 #endif
21957 char *Jim_HistoryGetline(const char *prompt)
21959 #ifdef USE_LINENOISE
21960 return linenoise(prompt);
21961 #else
21962 int len;
21963 char *line = malloc(MAX_LINE_LEN);
21965 fputs(prompt, stdout);
21966 fflush(stdout);
21968 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
21969 free(line);
21970 return NULL;
21972 len = strlen(line);
21973 if (len && line[len - 1] == '\n') {
21974 line[len - 1] = '\0';
21976 return line;
21977 #endif
21980 void Jim_HistoryLoad(const char *filename)
21982 #ifdef USE_LINENOISE
21983 linenoiseHistoryLoad(filename);
21984 #endif
21987 void Jim_HistoryAdd(const char *line)
21989 #ifdef USE_LINENOISE
21990 linenoiseHistoryAdd(line);
21991 #endif
21994 void Jim_HistorySave(const char *filename)
21996 #ifdef USE_LINENOISE
21997 linenoiseHistorySave(filename);
21998 #endif
22001 void Jim_HistoryShow(void)
22003 #ifdef USE_LINENOISE
22005 int i;
22006 int len;
22007 char **history = linenoiseHistory(&len);
22008 for (i = 0; i < len; i++) {
22009 printf("%4d %s\n", i + 1, history[i]);
22011 #endif
22014 int Jim_InteractivePrompt(Jim_Interp *interp)
22016 int retcode = JIM_OK;
22017 char *history_file = NULL;
22018 #ifdef USE_LINENOISE
22019 const char *home;
22021 home = getenv("HOME");
22022 if (home && isatty(STDIN_FILENO)) {
22023 int history_len = strlen(home) + sizeof("/.jim_history");
22024 history_file = Jim_Alloc(history_len);
22025 snprintf(history_file, history_len, "%s/.jim_history", home);
22026 Jim_HistoryLoad(history_file);
22028 #endif
22030 printf("Welcome to Jim version %d.%d\n",
22031 JIM_VERSION / 100, JIM_VERSION % 100);
22032 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
22034 while (1) {
22035 Jim_Obj *scriptObjPtr;
22036 const char *result;
22037 int reslen;
22038 char prompt[20];
22040 if (retcode != JIM_OK) {
22041 const char *retcodestr = Jim_ReturnCode(retcode);
22043 if (*retcodestr == '?') {
22044 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
22046 else {
22047 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
22050 else {
22051 strcpy(prompt, ". ");
22054 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
22055 Jim_IncrRefCount(scriptObjPtr);
22056 while (1) {
22057 char state;
22058 char *line;
22060 line = Jim_HistoryGetline(prompt);
22061 if (line == NULL) {
22062 if (errno == EINTR) {
22063 continue;
22065 Jim_DecrRefCount(interp, scriptObjPtr);
22066 retcode = JIM_OK;
22067 goto out;
22069 if (Jim_Length(scriptObjPtr) != 0) {
22071 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
22073 Jim_AppendString(interp, scriptObjPtr, line, -1);
22074 free(line);
22075 if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
22076 break;
22078 snprintf(prompt, sizeof(prompt), "%c> ", state);
22080 #ifdef USE_LINENOISE
22081 if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
22083 Jim_HistoryShow();
22084 Jim_DecrRefCount(interp, scriptObjPtr);
22085 continue;
22088 Jim_HistoryAdd(Jim_String(scriptObjPtr));
22089 if (history_file) {
22090 Jim_HistorySave(history_file);
22092 #endif
22093 retcode = Jim_EvalObj(interp, scriptObjPtr);
22094 Jim_DecrRefCount(interp, scriptObjPtr);
22096 if (retcode == JIM_EXIT) {
22097 break;
22099 if (retcode == JIM_ERR) {
22100 Jim_MakeErrorMessage(interp);
22102 result = Jim_GetString(Jim_GetResult(interp), &reslen);
22103 if (reslen) {
22104 printf("%s\n", result);
22107 out:
22108 Jim_Free(history_file);
22109 return retcode;
22112 #include <stdio.h>
22113 #include <stdlib.h>
22114 #include <string.h>
22118 extern int Jim_initjimshInit(Jim_Interp *interp);
22120 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
22122 int n;
22123 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
22126 for (n = 0; n < argc; n++) {
22127 Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
22129 Jim_ListAppendElement(interp, listObj, obj);
22132 Jim_SetVariableStr(interp, "argv", listObj);
22133 Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
22136 static void JimPrintErrorMessage(Jim_Interp *interp)
22138 Jim_MakeErrorMessage(interp);
22139 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
22142 void usage(const char* executable_name)
22144 printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
22145 printf("Usage: %s\n", executable_name);
22146 printf("or : %s [options] [filename]\n", executable_name);
22147 printf("\n");
22148 printf("Without options: Interactive mode\n");
22149 printf("\n");
22150 printf("Options:\n");
22151 printf(" --version : prints the version string\n");
22152 printf(" --help : prints this text\n");
22153 printf(" -e CMD : executes command CMD\n");
22154 printf(" NOTE: all subsequent options will be passed as arguments to the command\n");
22155 printf(" [filename] : executes the script contained in the named file\n");
22156 printf(" NOTE: all subsequent options will be passed to the script\n\n");
22159 int main(int argc, char *const argv[])
22161 int retcode;
22162 Jim_Interp *interp;
22163 char *const orig_argv0 = argv[0];
22166 if (argc > 1 && strcmp(argv[1], "--version") == 0) {
22167 printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
22168 return 0;
22170 else if (argc > 1 && strcmp(argv[1], "--help") == 0) {
22171 usage(argv[0]);
22172 return 0;
22176 interp = Jim_CreateInterp();
22177 Jim_RegisterCoreCommands(interp);
22180 if (Jim_InitStaticExtensions(interp) != JIM_OK) {
22181 JimPrintErrorMessage(interp);
22184 Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
22185 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
22186 retcode = Jim_initjimshInit(interp);
22188 if (argc == 1) {
22190 if (retcode == JIM_ERR) {
22191 JimPrintErrorMessage(interp);
22193 if (retcode != JIM_EXIT) {
22194 JimSetArgv(interp, 0, NULL);
22195 retcode = Jim_InteractivePrompt(interp);
22198 else {
22200 if (argc > 2 && strcmp(argv[1], "-e") == 0) {
22202 JimSetArgv(interp, argc - 3, argv + 3);
22203 retcode = Jim_Eval(interp, argv[2]);
22204 if (retcode != JIM_ERR) {
22205 printf("%s\n", Jim_String(Jim_GetResult(interp)));
22208 else {
22209 Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
22210 JimSetArgv(interp, argc - 2, argv + 2);
22211 retcode = Jim_EvalFile(interp, argv[1]);
22213 if (retcode == JIM_ERR) {
22214 JimPrintErrorMessage(interp);
22217 if (retcode == JIM_EXIT) {
22218 retcode = Jim_GetExitCode(interp);
22220 else if (retcode == JIM_ERR) {
22221 retcode = 1;
22223 else {
22224 retcode = 0;
22226 Jim_FreeInterp(interp);
22227 return retcode;
22229 #endif