Update ssl cert to use a 4096 bit key
[jimtcl.git] / autosetup / jimsh0.c
blob63d459664a08edf4217b312761638f5a789bc0dc
1 /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */
2 #define JIM_TCL_COMPAT
3 #define JIM_ANSIC
4 #define JIM_REGEXP
5 #define HAVE_NO_AUTOCONF
6 #define _JIMAUTOCONF_H
7 #define TCL_LIBRARY "."
8 #define jim_ext_bootstrap
9 #define jim_ext_aio
10 #define jim_ext_readdir
11 #define jim_ext_regexp
12 #define jim_ext_file
13 #define jim_ext_glob
14 #define jim_ext_exec
15 #define jim_ext_clock
16 #define jim_ext_array
17 #define jim_ext_stdlib
18 #define jim_ext_tclcompat
19 #if defined(_MSC_VER)
20 #define TCL_PLATFORM_OS "windows"
21 #define TCL_PLATFORM_PLATFORM "windows"
22 #define TCL_PLATFORM_PATH_SEPARATOR ";"
23 #define HAVE_MKDIR_ONE_ARG
24 #define HAVE_SYSTEM
25 #elif defined(__MINGW32__)
26 #define TCL_PLATFORM_OS "mingw"
27 #define TCL_PLATFORM_PLATFORM "windows"
28 #define TCL_PLATFORM_PATH_SEPARATOR ";"
29 #define HAVE_MKDIR_ONE_ARG
30 #define HAVE_SYSTEM
31 #define HAVE_SYS_TIME_H
32 #define HAVE_DIRENT_H
33 #define HAVE_UNISTD_H
34 #define HAVE_UMASK
35 #include <sys/stat.h>
36 #ifndef S_IRWXG
37 #define S_IRWXG 0
38 #endif
39 #ifndef S_IRWXO
40 #define S_IRWXO 0
41 #endif
42 #else
43 #define TCL_PLATFORM_OS "unknown"
44 #define TCL_PLATFORM_PLATFORM "unix"
45 #define TCL_PLATFORM_PATH_SEPARATOR ":"
46 #ifdef _MINIX
47 #define vfork fork
48 #define _POSIX_SOURCE
49 #else
50 #define _GNU_SOURCE
51 #endif
52 #define HAVE_VFORK
53 #define HAVE_WAITPID
54 #define HAVE_ISATTY
55 #define HAVE_MKSTEMP
56 #define HAVE_LINK
57 #define HAVE_SYS_TIME_H
58 #define HAVE_DIRENT_H
59 #define HAVE_UNISTD_H
60 #define HAVE_UMASK
61 #endif
62 #define JIM_VERSION 78
63 #ifndef JIM_WIN32COMPAT_H
64 #define JIM_WIN32COMPAT_H
68 #ifdef __cplusplus
69 extern "C" {
70 #endif
73 #if defined(_WIN32) || defined(WIN32)
75 #define HAVE_DLOPEN
76 void *dlopen(const char *path, int mode);
77 int dlclose(void *handle);
78 void *dlsym(void *handle, const char *symbol);
79 char *dlerror(void);
82 #if defined(__MINGW32__)
83 #define JIM_SPRINTF_DOUBLE_NEEDS_FIX
84 #endif
86 #ifdef _MSC_VER
89 #if _MSC_VER >= 1000
90 #pragma warning(disable:4146)
91 #endif
93 #include <limits.h>
94 #define jim_wide _int64
95 #ifndef LLONG_MAX
96 #define LLONG_MAX 9223372036854775807I64
97 #endif
98 #ifndef LLONG_MIN
99 #define LLONG_MIN (-LLONG_MAX - 1I64)
100 #endif
101 #define JIM_WIDE_MIN LLONG_MIN
102 #define JIM_WIDE_MAX LLONG_MAX
103 #define JIM_WIDE_MODIFIER "I64d"
104 #define strcasecmp _stricmp
105 #define strtoull _strtoui64
107 #include <io.h>
109 struct timeval {
110 long tv_sec;
111 long tv_usec;
114 int gettimeofday(struct timeval *tv, void *unused);
116 #define HAVE_OPENDIR
117 struct dirent {
118 char *d_name;
121 typedef struct DIR {
122 long handle;
123 struct _finddata_t info;
124 struct dirent result;
125 char *name;
126 } DIR;
128 DIR *opendir(const char *name);
129 int closedir(DIR *dir);
130 struct dirent *readdir(DIR *dir);
132 #endif
134 #endif
136 #ifdef __cplusplus
138 #endif
140 #endif
141 #ifndef UTF8_UTIL_H
142 #define UTF8_UTIL_H
144 #ifdef __cplusplus
145 extern "C" {
146 #endif
150 #define MAX_UTF8_LEN 4
152 int utf8_fromunicode(char *p, unsigned uc);
154 #ifndef JIM_UTF8
155 #include <ctype.h>
158 #define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
159 #define utf8_strwidth(S, B) utf8_strlen((S), (B))
160 #define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
161 #define utf8_getchars(CP, C) (*(CP) = (C), 1)
162 #define utf8_upper(C) toupper(C)
163 #define utf8_title(C) toupper(C)
164 #define utf8_lower(C) tolower(C)
165 #define utf8_index(C, I) (I)
166 #define utf8_charlen(C) 1
167 #define utf8_prev_len(S, L) 1
168 #define utf8_width(C) 1
170 #else
172 #endif
174 #ifdef __cplusplus
176 #endif
178 #endif
180 #ifndef __JIM__H
181 #define __JIM__H
183 #ifdef __cplusplus
184 extern "C" {
185 #endif
187 #include <time.h>
188 #include <limits.h>
189 #include <stdio.h>
190 #include <stdlib.h>
191 #include <stdarg.h>
194 #ifndef HAVE_NO_AUTOCONF
195 #endif
199 #ifndef jim_wide
200 # ifdef HAVE_LONG_LONG
201 # define jim_wide long long
202 # ifndef LLONG_MAX
203 # define LLONG_MAX 9223372036854775807LL
204 # endif
205 # ifndef LLONG_MIN
206 # define LLONG_MIN (-LLONG_MAX - 1LL)
207 # endif
208 # define JIM_WIDE_MIN LLONG_MIN
209 # define JIM_WIDE_MAX LLONG_MAX
210 # else
211 # define jim_wide long
212 # define JIM_WIDE_MIN LONG_MIN
213 # define JIM_WIDE_MAX LONG_MAX
214 # endif
217 # ifdef HAVE_LONG_LONG
218 # define JIM_WIDE_MODIFIER "lld"
219 # else
220 # define JIM_WIDE_MODIFIER "ld"
221 # define strtoull strtoul
222 # endif
223 #endif
225 #define UCHAR(c) ((unsigned char)(c))
228 #define JIM_OK 0
229 #define JIM_ERR 1
230 #define JIM_RETURN 2
231 #define JIM_BREAK 3
232 #define JIM_CONTINUE 4
233 #define JIM_SIGNAL 5
234 #define JIM_EXIT 6
236 #define JIM_EVAL 7
238 #define JIM_MAX_CALLFRAME_DEPTH 1000
239 #define JIM_MAX_EVAL_DEPTH 2000
242 #define JIM_PRIV_FLAG_SHIFT 20
244 #define JIM_NONE 0
245 #define JIM_ERRMSG 1
246 #define JIM_ENUM_ABBREV 2
247 #define JIM_UNSHARED 4
248 #define JIM_MUSTEXIST 8
251 #define JIM_SUBST_NOVAR 1
252 #define JIM_SUBST_NOCMD 2
253 #define JIM_SUBST_NOESC 4
254 #define JIM_SUBST_FLAG 128
257 #define JIM_CASESENS 0
258 #define JIM_NOCASE 1
261 #define JIM_PATH_LEN 1024
264 #define JIM_NOTUSED(V) ((void) V)
266 #define JIM_LIBPATH "auto_path"
267 #define JIM_INTERACTIVE "tcl_interactive"
270 typedef struct Jim_Stack {
271 int len;
272 int maxlen;
273 void **vector;
274 } Jim_Stack;
277 typedef struct Jim_HashEntry {
278 void *key;
279 union {
280 void *val;
281 int intval;
282 } u;
283 struct Jim_HashEntry *next;
284 } Jim_HashEntry;
286 typedef struct Jim_HashTableType {
287 unsigned int (*hashFunction)(const void *key);
288 void *(*keyDup)(void *privdata, const void *key);
289 void *(*valDup)(void *privdata, const void *obj);
290 int (*keyCompare)(void *privdata, const void *key1, const void *key2);
291 void (*keyDestructor)(void *privdata, void *key);
292 void (*valDestructor)(void *privdata, void *obj);
293 } Jim_HashTableType;
295 typedef struct Jim_HashTable {
296 Jim_HashEntry **table;
297 const Jim_HashTableType *type;
298 void *privdata;
299 unsigned int size;
300 unsigned int sizemask;
301 unsigned int used;
302 unsigned int collisions;
303 unsigned int uniq;
304 } Jim_HashTable;
306 typedef struct Jim_HashTableIterator {
307 Jim_HashTable *ht;
308 Jim_HashEntry *entry, *nextEntry;
309 int index;
310 } Jim_HashTableIterator;
313 #define JIM_HT_INITIAL_SIZE 16
316 #define Jim_FreeEntryVal(ht, entry) \
317 if ((ht)->type->valDestructor) \
318 (ht)->type->valDestructor((ht)->privdata, (entry)->u.val)
320 #define Jim_SetHashVal(ht, entry, _val_) do { \
321 if ((ht)->type->valDup) \
322 (entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \
323 else \
324 (entry)->u.val = (_val_); \
325 } while(0)
327 #define Jim_FreeEntryKey(ht, entry) \
328 if ((ht)->type->keyDestructor) \
329 (ht)->type->keyDestructor((ht)->privdata, (entry)->key)
331 #define Jim_SetHashKey(ht, entry, _key_) do { \
332 if ((ht)->type->keyDup) \
333 (entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \
334 else \
335 (entry)->key = (void *)(_key_); \
336 } while(0)
338 #define Jim_CompareHashKeys(ht, key1, key2) \
339 (((ht)->type->keyCompare) ? \
340 (ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \
341 (key1) == (key2))
343 #define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq)
345 #define Jim_GetHashEntryKey(he) ((he)->key)
346 #define Jim_GetHashEntryVal(he) ((he)->u.val)
347 #define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
348 #define Jim_GetHashTableSize(ht) ((ht)->size)
349 #define Jim_GetHashTableUsed(ht) ((ht)->used)
352 typedef struct Jim_Obj {
353 char *bytes;
354 const struct Jim_ObjType *typePtr;
355 int refCount;
356 int length;
358 union {
360 jim_wide wideValue;
362 int intValue;
364 double doubleValue;
366 void *ptr;
368 struct {
369 void *ptr1;
370 void *ptr2;
371 } twoPtrValue;
373 struct {
374 void *ptr;
375 int int1;
376 int int2;
377 } ptrIntValue;
379 struct {
380 struct Jim_Var *varPtr;
381 unsigned long callFrameId;
382 int global;
383 } varValue;
385 struct {
386 struct Jim_Obj *nsObj;
387 struct Jim_Cmd *cmdPtr;
388 unsigned long procEpoch;
389 } cmdValue;
391 struct {
392 struct Jim_Obj **ele;
393 int len;
394 int maxLen;
395 } listValue;
397 struct {
398 int maxLength;
399 int charLength;
400 } strValue;
402 struct {
403 unsigned long id;
404 struct Jim_Reference *refPtr;
405 } refValue;
407 struct {
408 struct Jim_Obj *fileNameObj;
409 int lineNumber;
410 } sourceValue;
412 struct {
413 struct Jim_Obj *varNameObjPtr;
414 struct Jim_Obj *indexObjPtr;
415 } dictSubstValue;
416 struct {
417 int line;
418 int argc;
419 } scriptLineValue;
420 } internalRep;
421 struct Jim_Obj *prevObjPtr;
422 struct Jim_Obj *nextObjPtr;
423 } Jim_Obj;
426 #define Jim_IncrRefCount(objPtr) \
427 ++(objPtr)->refCount
428 #define Jim_DecrRefCount(interp, objPtr) \
429 if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr)
430 #define Jim_IsShared(objPtr) \
431 ((objPtr)->refCount > 1)
433 #define Jim_FreeNewObj Jim_FreeObj
436 #define Jim_FreeIntRep(i,o) \
437 if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \
438 (o)->typePtr->freeIntRepProc(i, o)
441 #define Jim_GetIntRepPtr(o) (o)->internalRep.ptr
444 #define Jim_SetIntRepPtr(o, p) \
445 (o)->internalRep.ptr = (p)
448 struct Jim_Interp;
450 typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp,
451 struct Jim_Obj *objPtr);
452 typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp,
453 struct Jim_Obj *srcPtr, Jim_Obj *dupPtr);
454 typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr);
456 typedef struct Jim_ObjType {
457 const char *name;
458 Jim_FreeInternalRepProc *freeIntRepProc;
459 Jim_DupInternalRepProc *dupIntRepProc;
460 Jim_UpdateStringProc *updateStringProc;
461 int flags;
462 } Jim_ObjType;
465 #define JIM_TYPE_NONE 0
466 #define JIM_TYPE_REFERENCES 1
470 typedef struct Jim_CallFrame {
471 unsigned long id;
472 int level;
473 struct Jim_HashTable vars;
474 struct Jim_HashTable *staticVars;
475 struct Jim_CallFrame *parent;
476 Jim_Obj *const *argv;
477 int argc;
478 Jim_Obj *procArgsObjPtr;
479 Jim_Obj *procBodyObjPtr;
480 struct Jim_CallFrame *next;
481 Jim_Obj *nsObj;
482 Jim_Obj *fileNameObj;
483 int line;
484 Jim_Stack *localCommands;
485 struct Jim_Obj *tailcallObj;
486 struct Jim_Cmd *tailcallCmd;
487 } Jim_CallFrame;
489 typedef struct Jim_Var {
490 Jim_Obj *objPtr;
491 struct Jim_CallFrame *linkFramePtr;
492 } Jim_Var;
495 typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
496 Jim_Obj *const *argv);
497 typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);
501 typedef struct Jim_Cmd {
502 int inUse;
503 int isproc;
504 struct Jim_Cmd *prevCmd;
505 union {
506 struct {
508 Jim_CmdProc *cmdProc;
509 Jim_DelCmdProc *delProc;
510 void *privData;
511 } native;
512 struct {
514 Jim_Obj *argListObjPtr;
515 Jim_Obj *bodyObjPtr;
516 Jim_HashTable *staticVars;
517 int argListLen;
518 int reqArity;
519 int optArity;
520 int argsPos;
521 int upcall;
522 struct Jim_ProcArg {
523 Jim_Obj *nameObjPtr;
524 Jim_Obj *defaultObjPtr;
525 } *arglist;
526 Jim_Obj *nsObj;
527 } proc;
528 } u;
529 } Jim_Cmd;
532 typedef struct Jim_PrngState {
533 unsigned char sbox[256];
534 unsigned int i, j;
535 } Jim_PrngState;
537 typedef struct Jim_Interp {
538 Jim_Obj *result;
539 int errorLine;
540 Jim_Obj *errorFileNameObj;
541 int addStackTrace;
542 int maxCallFrameDepth;
543 int maxEvalDepth;
544 int evalDepth;
545 int returnCode;
546 int returnLevel;
547 int exitCode;
548 long id;
549 int signal_level;
550 jim_wide sigmask;
551 int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask);
552 Jim_CallFrame *framePtr;
553 Jim_CallFrame *topFramePtr;
554 struct Jim_HashTable commands;
555 unsigned long procEpoch; /* Incremented every time the result
556 of procedures names lookup caching
557 may no longer be valid. */
558 unsigned long callFrameEpoch; /* Incremented every time a new
559 callframe is created. This id is used for the
560 'ID' field contained in the Jim_CallFrame
561 structure. */
562 int local;
563 Jim_Obj *liveList;
564 Jim_Obj *freeList;
565 Jim_Obj *currentScriptObj;
566 Jim_Obj *nullScriptObj;
567 Jim_Obj *emptyObj;
568 Jim_Obj *trueObj;
569 Jim_Obj *falseObj;
570 unsigned long referenceNextId;
571 struct Jim_HashTable references;
572 unsigned long lastCollectId; /* reference max Id of the last GC
573 execution. It's set to ~0 while the collection
574 is running as sentinel to avoid to recursive
575 calls via the [collect] command inside
576 finalizers. */
577 time_t lastCollectTime;
578 Jim_Obj *stackTrace;
579 Jim_Obj *errorProc;
580 Jim_Obj *unknown;
581 int unknown_called;
582 int errorFlag;
583 void *cmdPrivData; /* Used to pass the private data pointer to
584 a command. It is set to what the user specified
585 via Jim_CreateCommand(). */
587 struct Jim_CallFrame *freeFramesList;
588 struct Jim_HashTable assocData;
589 Jim_PrngState *prngState;
590 struct Jim_HashTable packages;
591 Jim_Stack *loadHandles;
592 } Jim_Interp;
594 #define Jim_InterpIncrProcEpoch(i) (i)->procEpoch++
595 #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
596 #define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))
598 #define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
599 #define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj)
600 #define Jim_GetResult(i) ((i)->result)
601 #define Jim_CmdPrivData(i) ((i)->cmdPrivData)
603 #define Jim_SetResult(i,o) do { \
604 Jim_Obj *_resultObjPtr_ = (o); \
605 Jim_IncrRefCount(_resultObjPtr_); \
606 Jim_DecrRefCount(i,(i)->result); \
607 (i)->result = _resultObjPtr_; \
608 } while(0)
611 #define Jim_GetId(i) (++(i)->id)
614 #define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference
615 string representation must be fixed length. */
616 typedef struct Jim_Reference {
617 Jim_Obj *objPtr;
618 Jim_Obj *finalizerCmdNamePtr;
619 char tag[JIM_REFERENCE_TAGLEN+1];
620 } Jim_Reference;
623 #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
624 #define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
626 #define JIM_EXPORT
629 JIM_EXPORT void *Jim_Alloc (int size);
630 JIM_EXPORT void *Jim_Realloc(void *ptr, int size);
631 JIM_EXPORT void Jim_Free (void *ptr);
632 JIM_EXPORT char * Jim_StrDup (const char *s);
633 JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
636 JIM_EXPORT char **Jim_GetEnviron(void);
637 JIM_EXPORT void Jim_SetEnviron(char **env);
638 JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file);
641 JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
644 JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);
646 #define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S))
648 JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script);
649 JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename);
650 JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename);
651 JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr);
652 JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc,
653 Jim_Obj *const *objv);
654 JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj);
655 JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
656 int objc, Jim_Obj *const *objv);
657 #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
658 JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
659 JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
660 Jim_Obj **resObjPtrPtr, int flags);
663 JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
664 JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
665 JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
666 JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
667 JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
668 JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
669 JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr));
672 JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
673 const Jim_HashTableType *type, void *privdata);
674 JIM_EXPORT void Jim_ExpandHashTable (Jim_HashTable *ht,
675 unsigned int size);
676 JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key,
677 void *val);
678 JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
679 const void *key, void *val);
680 JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
681 const void *key);
682 JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
683 JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
684 const void *key);
685 JIM_EXPORT void Jim_ResizeHashTable (Jim_HashTable *ht);
686 JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator
687 (Jim_HashTable *ht);
688 JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
689 (Jim_HashTableIterator *iter);
692 JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
693 JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr);
694 JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr);
695 JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
696 Jim_Obj *objPtr);
697 JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
698 int *lenPtr);
699 JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
700 JIM_EXPORT int Jim_Length(Jim_Obj *objPtr);
703 JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp,
704 const char *s, int len);
705 JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp,
706 const char *s, int charlen);
707 JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp,
708 char *s, int len);
709 JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr,
710 const char *str, int len);
711 JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr,
712 Jim_Obj *appendObjPtr);
713 JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp,
714 Jim_Obj *objPtr, ...);
715 JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr);
716 JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr,
717 Jim_Obj *objPtr, int nocase);
718 JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp,
719 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr,
720 Jim_Obj *lastObjPtr);
721 JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp,
722 Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
723 JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
724 Jim_Obj *fmtObjPtr, int flags);
725 JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
726 Jim_Obj *objPtr, const char *str);
727 JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
728 Jim_Obj *secondObjPtr, int nocase);
729 JIM_EXPORT int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
730 Jim_Obj *secondObjPtr, int nocase);
731 JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr);
734 JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp,
735 Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr);
736 JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp,
737 Jim_Obj *objPtr);
738 JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr);
739 JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr);
742 JIM_EXPORT Jim_Interp * Jim_CreateInterp (void);
743 JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i);
744 JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp);
745 JIM_EXPORT const char *Jim_ReturnCode(int code);
746 JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...);
749 JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
750 JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
751 const char *cmdName, Jim_CmdProc *cmdProc, void *privData,
752 Jim_DelCmdProc *delProc);
753 JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
754 const char *cmdName);
755 JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
756 const char *oldName, const char *newName);
757 JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
758 Jim_Obj *objPtr, int flags);
759 JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
760 Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
761 JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
762 const char *name, Jim_Obj *objPtr);
763 JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp,
764 const char *name, Jim_Obj *objPtr);
765 JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp,
766 const char *name, const char *val);
767 JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp,
768 Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr,
769 Jim_CallFrame *targetCallFrame);
770 JIM_EXPORT Jim_Obj * Jim_MakeGlobalNamespaceName(Jim_Interp *interp,
771 Jim_Obj *nameObjPtr);
772 JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp,
773 Jim_Obj *nameObjPtr, int flags);
774 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp,
775 Jim_Obj *nameObjPtr, int flags);
776 JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp,
777 const char *name, int flags);
778 JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp,
779 const char *name, int flags);
780 JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp,
781 Jim_Obj *nameObjPtr, int flags);
784 JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp,
785 Jim_Obj *levelObjPtr);
788 JIM_EXPORT int Jim_Collect (Jim_Interp *interp);
789 JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp);
792 JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr,
793 int *indexPtr);
796 JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp,
797 Jim_Obj *const *elements, int len);
798 JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp,
799 Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec);
800 JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp,
801 Jim_Obj *listPtr, Jim_Obj *objPtr);
802 JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp,
803 Jim_Obj *listPtr, Jim_Obj *appendListPtr);
804 JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr);
805 JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt,
806 int listindex, Jim_Obj **objPtrPtr, int seterr);
807 JIM_EXPORT Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx);
808 JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp,
809 Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc,
810 Jim_Obj *newObjPtr);
811 JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc,
812 Jim_Obj *const *objv);
813 JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp,
814 Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen);
817 JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp,
818 Jim_Obj *const *elements, int len);
819 JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr,
820 Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
821 JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
822 Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
823 Jim_Obj **objPtrPtr, int flags);
824 JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
825 Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
826 Jim_Obj *newObjPtr, int flags);
827 JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp,
828 Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len);
829 JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
830 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
832 #define JIM_DICTMATCH_KEYS 0x0001
833 #define JIM_DICTMATCH_VALUES 0x002
835 JIM_EXPORT int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types);
836 JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
837 JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr);
838 JIM_EXPORT Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv);
841 JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
842 int *intPtr);
845 JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
846 Jim_Obj *exprObjPtr);
847 JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
848 Jim_Obj *exprObjPtr, int *boolPtr);
851 JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
852 int *booleanPtr);
855 JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
856 jim_wide *widePtr);
857 JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
858 long *longPtr);
859 #define Jim_NewWideObj Jim_NewIntObj
860 JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
861 jim_wide wideValue);
864 JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
865 double *doublePtr);
866 JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr,
867 double doubleValue);
868 JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue);
871 JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc,
872 Jim_Obj *const *argv, const char *msg);
873 JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
874 const char * const *tablePtr, int *indexPtr, const char *name, int flags);
875 JIM_EXPORT int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr,
876 const char *const *tablePtr);
877 JIM_EXPORT int Jim_ScriptIsComplete(Jim_Interp *interp,
878 Jim_Obj *scriptObj, char *stateCharPtr);
880 JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);
883 typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
884 JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
885 JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
886 Jim_InterpDeleteProc *delProc, void *data);
887 JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
891 JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
892 const char *name, const char *ver, int flags);
893 JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
894 const char *name, int flags);
897 JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
900 JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
901 JIM_EXPORT void Jim_HistoryLoad(const char *filename);
902 JIM_EXPORT void Jim_HistorySave(const char *filename);
903 JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt);
904 JIM_EXPORT void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj);
905 JIM_EXPORT void Jim_HistoryAdd(const char *line);
906 JIM_EXPORT void Jim_HistoryShow(void);
909 JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
910 JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
911 JIM_EXPORT int Jim_IsBigEndian(void);
913 #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
916 JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
917 JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
920 JIM_EXPORT FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
923 JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
924 JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
926 #ifdef __cplusplus
928 #endif
930 #endif
932 #ifndef JIM_SUBCMD_H
933 #define JIM_SUBCMD_H
936 #ifdef __cplusplus
937 extern "C" {
938 #endif
941 #define JIM_MODFLAG_HIDDEN 0x0001
942 #define JIM_MODFLAG_FULLARGV 0x0002
946 typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
948 typedef struct {
949 const char *cmd;
950 const char *args;
951 jim_subcmd_function *function;
952 short minargs;
953 short maxargs;
954 unsigned short flags;
955 } jim_subcmd_type;
957 const jim_subcmd_type *
958 Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
960 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
962 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
964 #ifdef __cplusplus
966 #endif
968 #endif
969 #ifndef JIMREGEXP_H
970 #define JIMREGEXP_H
973 #ifdef __cplusplus
974 extern "C" {
975 #endif
977 #include <stdlib.h>
979 typedef struct {
980 int rm_so;
981 int rm_eo;
982 } regmatch_t;
985 typedef struct regexp {
987 int re_nsub;
990 int cflags;
991 int err;
992 int regstart;
993 int reganch;
994 int regmust;
995 int regmlen;
996 int *program;
999 const char *regparse;
1000 int p;
1001 int proglen;
1004 int eflags;
1005 const char *start;
1006 const char *reginput;
1007 const char *regbol;
1010 regmatch_t *pmatch;
1011 int nmatch;
1012 } regexp;
1014 typedef regexp regex_t;
1016 #define REG_EXTENDED 0
1017 #define REG_NEWLINE 1
1018 #define REG_ICASE 2
1020 #define REG_NOTBOL 16
1022 enum {
1023 REG_NOERROR,
1024 REG_NOMATCH,
1025 REG_BADPAT,
1026 REG_ERR_NULL_ARGUMENT,
1027 REG_ERR_UNKNOWN,
1028 REG_ERR_TOO_BIG,
1029 REG_ERR_NOMEM,
1030 REG_ERR_TOO_MANY_PAREN,
1031 REG_ERR_UNMATCHED_PAREN,
1032 REG_ERR_UNMATCHED_BRACES,
1033 REG_ERR_BAD_COUNT,
1034 REG_ERR_JUNK_ON_END,
1035 REG_ERR_OPERAND_COULD_BE_EMPTY,
1036 REG_ERR_NESTED_COUNT,
1037 REG_ERR_INTERNAL,
1038 REG_ERR_COUNT_FOLLOWS_NOTHING,
1039 REG_ERR_TRAILING_BACKSLASH,
1040 REG_ERR_CORRUPTED,
1041 REG_ERR_NULL_CHAR,
1042 REG_ERR_NUM
1045 int regcomp(regex_t *preg, const char *regex, int cflags);
1046 int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
1047 size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
1048 void regfree(regex_t *preg);
1050 #ifdef __cplusplus
1052 #endif
1054 #endif
1055 #ifndef JIM_SIGNAL_H
1056 #define JIM_SIGNAL_H
1058 #ifdef __cplusplus
1059 extern "C" {
1060 #endif
1062 const char *Jim_SignalId(int sig);
1064 #ifdef __cplusplus
1066 #endif
1068 #endif
1069 #ifndef JIMIOCOMPAT_H
1070 #define JIMIOCOMPAT_H
1073 #include <stdio.h>
1074 #include <errno.h>
1077 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg);
1079 int Jim_OpenForWrite(const char *filename, int append);
1081 int Jim_OpenForRead(const char *filename);
1083 #if defined(__MINGW32__)
1084 #ifndef STRICT
1085 #define STRICT
1086 #endif
1087 #define WIN32_LEAN_AND_MEAN
1088 #include <windows.h>
1089 #include <fcntl.h>
1090 #include <io.h>
1091 #include <process.h>
1093 typedef HANDLE pidtype;
1094 #define JIM_BAD_PID INVALID_HANDLE_VALUE
1096 #define JIM_NO_PID INVALID_HANDLE_VALUE
1099 #define WIFEXITED(STATUS) (((STATUS) & 0xff00) == 0)
1100 #define WEXITSTATUS(STATUS) ((STATUS) & 0x00ff)
1101 #define WIFSIGNALED(STATUS) (((STATUS) & 0xff00) != 0)
1102 #define WTERMSIG(STATUS) (((STATUS) >> 8) & 0xff)
1103 #define WNOHANG 1
1105 int Jim_Errno(void);
1106 pidtype waitpid(pidtype pid, int *status, int nohang);
1108 #define HAVE_PIPE
1109 #define pipe(P) _pipe((P), 0, O_NOINHERIT)
1111 #elif defined(HAVE_UNISTD_H)
1112 #include <unistd.h>
1113 #include <fcntl.h>
1114 #include <sys/wait.h>
1115 #include <sys/stat.h>
1117 typedef int pidtype;
1118 #define Jim_Errno() errno
1119 #define JIM_BAD_PID -1
1120 #define JIM_NO_PID 0
1122 #ifndef HAVE_EXECVPE
1123 #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
1124 #endif
1125 #endif
1127 #endif
1128 int Jim_bootstrapInit(Jim_Interp *interp)
1130 if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
1131 return JIM_ERR;
1133 return Jim_EvalSource(interp, "bootstrap.tcl", 1,
1134 "\n"
1135 "\n"
1136 "proc package {cmd pkg args} {\n"
1137 " if {$cmd eq \"require\"} {\n"
1138 " foreach path $::auto_path {\n"
1139 " set pkgpath $path/$pkg.tcl\n"
1140 " if {$path eq \".\"} {\n"
1141 " set pkgpath $pkg.tcl\n"
1142 " }\n"
1143 " if {[file exists $pkgpath]} {\n"
1144 " uplevel #0 [list source $pkgpath]\n"
1145 " return\n"
1146 " }\n"
1147 " }\n"
1148 " }\n"
1149 "}\n"
1152 int Jim_initjimshInit(Jim_Interp *interp)
1154 if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
1155 return JIM_ERR;
1157 return Jim_EvalSource(interp, "initjimsh.tcl", 1,
1158 "\n"
1159 "\n"
1160 "\n"
1161 "proc _jimsh_init {} {\n"
1162 " rename _jimsh_init {}\n"
1163 " global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
1164 "\n"
1165 "\n"
1166 " if {[exists jim::argv0]} {\n"
1167 " if {[string match \"*/*\" $jim::argv0]} {\n"
1168 " set jim::exe [file join [pwd] $jim::argv0]\n"
1169 " } else {\n"
1170 " foreach path [split [env PATH \"\"] $tcl_platform(pathSeparator)] {\n"
1171 " set exec [file join [pwd] [string map {\\\\ /} $path] $jim::argv0]\n"
1172 " if {[file executable $exec]} {\n"
1173 " set jim::exe $exec\n"
1174 " break\n"
1175 " }\n"
1176 " }\n"
1177 " }\n"
1178 " }\n"
1179 "\n"
1180 "\n"
1181 " lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
1182 " if {[exists jim::exe]} {\n"
1183 " lappend p [file dirname $jim::exe]\n"
1184 " }\n"
1185 " lappend p {*}$auto_path\n"
1186 " set auto_path $p\n"
1187 "\n"
1188 " if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
1189 " foreach src {.jimrc jimrc.tcl} {\n"
1190 " if {[file exists [env HOME]/$src]} {\n"
1191 " uplevel #0 source [env HOME]/$src\n"
1192 " break\n"
1193 " }\n"
1194 " }\n"
1195 " }\n"
1196 " return \"\"\n"
1197 "}\n"
1198 "\n"
1199 "if {$tcl_platform(platform) eq \"windows\"} {\n"
1200 " set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
1201 "}\n"
1202 "\n"
1203 "\n"
1204 "set tcl::autocomplete_commands {info tcl::prefix socket namespace array clock file package string dict signal history}\n"
1205 "\n"
1206 "\n"
1207 "\n"
1208 "proc tcl::autocomplete {prefix} {\n"
1209 " if {[set space [string first \" \" $prefix]] != -1} {\n"
1210 " set cmd [string range $prefix 0 $space-1]\n"
1211 " if {$cmd in $::tcl::autocomplete_commands || [info channel $cmd] ne \"\"} {\n"
1212 " set arg [string range $prefix $space+1 end]\n"
1213 "\n"
1214 " return [lmap p [$cmd -commands] {\n"
1215 " if {![string match \"${arg}*\" $p]} continue\n"
1216 " function \"$cmd $p\"\n"
1217 " }]\n"
1218 " }\n"
1219 " }\n"
1220 "\n"
1221 " if {[string match \"source *\" $prefix]} {\n"
1222 " set path [string range $prefix 7 end]\n"
1223 " return [lmap p [glob -nocomplain \"${path}*\"] {\n"
1224 " function \"source $p\"\n"
1225 " }]\n"
1226 " }\n"
1227 "\n"
1228 " return [lmap p [lsort [info commands $prefix*]] {\n"
1229 " if {[string match \"* *\" $p]} {\n"
1230 " continue\n"
1231 " }\n"
1232 " function $p\n"
1233 " }]\n"
1234 "}\n"
1235 "\n"
1236 "_jimsh_init\n"
1239 int Jim_globInit(Jim_Interp *interp)
1241 if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
1242 return JIM_ERR;
1244 return Jim_EvalSource(interp, "glob.tcl", 1,
1245 "\n"
1246 "\n"
1247 "\n"
1248 "\n"
1249 "\n"
1250 "\n"
1251 "\n"
1252 "package require readdir\n"
1253 "\n"
1254 "\n"
1255 "proc glob.globdir {dir pattern} {\n"
1256 " if {[file exists $dir/$pattern]} {\n"
1257 "\n"
1258 " return [list $pattern]\n"
1259 " }\n"
1260 "\n"
1261 " set result {}\n"
1262 " set files [readdir $dir]\n"
1263 " lappend files . ..\n"
1264 "\n"
1265 " foreach name $files {\n"
1266 " if {[string match $pattern $name]} {\n"
1267 "\n"
1268 " if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n"
1269 " continue\n"
1270 " }\n"
1271 " lappend result $name\n"
1272 " }\n"
1273 " }\n"
1274 "\n"
1275 " return $result\n"
1276 "}\n"
1277 "\n"
1278 "\n"
1279 "\n"
1280 "\n"
1281 "proc glob.explode {pattern} {\n"
1282 " set oldexp {}\n"
1283 " set newexp {\"\"}\n"
1284 "\n"
1285 " while 1 {\n"
1286 " set oldexp $newexp\n"
1287 " set newexp {}\n"
1288 " set ob [string first \\{ $pattern]\n"
1289 " set cb [string first \\} $pattern]\n"
1290 "\n"
1291 " if {$ob < $cb && $ob != -1} {\n"
1292 " set mid [string range $pattern 0 $ob-1]\n"
1293 " set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n"
1294 " if {$pattern eq \"\"} {\n"
1295 " error \"unmatched open brace in glob pattern\"\n"
1296 " }\n"
1297 " set pattern [string range $pattern 1 end]\n"
1298 "\n"
1299 " foreach subs $subexp {\n"
1300 " foreach sub [split $subs ,] {\n"
1301 " foreach old $oldexp {\n"
1302 " lappend newexp $old$mid$sub\n"
1303 " }\n"
1304 " }\n"
1305 " }\n"
1306 " } elseif {$cb != -1} {\n"
1307 " set suf [string range $pattern 0 $cb-1]\n"
1308 " set rest [string range $pattern $cb end]\n"
1309 " break\n"
1310 " } else {\n"
1311 " set suf $pattern\n"
1312 " set rest \"\"\n"
1313 " break\n"
1314 " }\n"
1315 " }\n"
1316 "\n"
1317 " foreach old $oldexp {\n"
1318 " lappend newexp $old$suf\n"
1319 " }\n"
1320 " list $rest {*}$newexp\n"
1321 "}\n"
1322 "\n"
1323 "\n"
1324 "\n"
1325 "proc glob.glob {base pattern} {\n"
1326 " set dir [file dirname $pattern]\n"
1327 " if {$pattern eq $dir || $pattern eq \"\"} {\n"
1328 " return [list [file join $base $dir] $pattern]\n"
1329 " } elseif {$pattern eq [file tail $pattern]} {\n"
1330 " set dir \"\"\n"
1331 " }\n"
1332 "\n"
1333 "\n"
1334 " set dirlist [glob.glob $base $dir]\n"
1335 " set pattern [file tail $pattern]\n"
1336 "\n"
1337 "\n"
1338 " set result {}\n"
1339 " foreach {realdir dir} $dirlist {\n"
1340 " if {![file isdir $realdir]} {\n"
1341 " continue\n"
1342 " }\n"
1343 " if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n"
1344 " append dir /\n"
1345 " }\n"
1346 " foreach name [glob.globdir $realdir $pattern] {\n"
1347 " lappend result [file join $realdir $name] $dir$name\n"
1348 " }\n"
1349 " }\n"
1350 " return $result\n"
1351 "}\n"
1352 "\n"
1353 "\n"
1354 "\n"
1355 "\n"
1356 "\n"
1357 "\n"
1358 "\n"
1359 "\n"
1360 "\n"
1361 "\n"
1362 "\n"
1363 "\n"
1364 "proc glob {args} {\n"
1365 " set nocomplain 0\n"
1366 " set base \"\"\n"
1367 " set tails 0\n"
1368 "\n"
1369 " set n 0\n"
1370 " foreach arg $args {\n"
1371 " if {[info exists param]} {\n"
1372 " set $param $arg\n"
1373 " unset param\n"
1374 " incr n\n"
1375 " continue\n"
1376 " }\n"
1377 " switch -glob -- $arg {\n"
1378 " -d* {\n"
1379 " set switch $arg\n"
1380 " set param base\n"
1381 " }\n"
1382 " -n* {\n"
1383 " set nocomplain 1\n"
1384 " }\n"
1385 " -ta* {\n"
1386 " set tails 1\n"
1387 " }\n"
1388 " -- {\n"
1389 " incr n\n"
1390 " break\n"
1391 " }\n"
1392 " -* {\n"
1393 " return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1394 " }\n"
1395 " * {\n"
1396 " break\n"
1397 " }\n"
1398 " }\n"
1399 " incr n\n"
1400 " }\n"
1401 " if {[info exists param]} {\n"
1402 " return -code error \"missing argument to \\\"$switch\\\"\"\n"
1403 " }\n"
1404 " if {[llength $args] <= $n} {\n"
1405 " return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n"
1406 " }\n"
1407 "\n"
1408 " set args [lrange $args $n end]\n"
1409 "\n"
1410 " set result {}\n"
1411 " foreach pattern $args {\n"
1412 " set escpattern [string map {\n"
1413 " \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
1414 " } $pattern]\n"
1415 " set patexps [lassign [glob.explode $escpattern] rest]\n"
1416 " if {$rest ne \"\"} {\n"
1417 " return -code error \"unmatched close brace in glob pattern\"\n"
1418 " }\n"
1419 " foreach patexp $patexps {\n"
1420 " set patexp [string map {\n"
1421 " \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
1422 " } $patexp]\n"
1423 " foreach {realname name} [glob.glob $base $patexp] {\n"
1424 " incr n\n"
1425 " if {$tails} {\n"
1426 " lappend result $name\n"
1427 " } else {\n"
1428 " lappend result [file join $base $name]\n"
1429 " }\n"
1430 " }\n"
1431 " }\n"
1432 " }\n"
1433 "\n"
1434 " if {!$nocomplain && [llength $result] == 0} {\n"
1435 " set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
1436 " return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
1437 " }\n"
1438 "\n"
1439 " return $result\n"
1440 "}\n"
1443 int Jim_stdlibInit(Jim_Interp *interp)
1445 if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
1446 return JIM_ERR;
1448 return Jim_EvalSource(interp, "stdlib.tcl", 1,
1449 "\n"
1450 "\n"
1451 "if {![exists -command ref]} {\n"
1452 "\n"
1453 " proc ref {args} {{count 0}} {\n"
1454 " format %08x [incr count]\n"
1455 " }\n"
1456 "}\n"
1457 "\n"
1458 "\n"
1459 "proc lambda {arglist args} {\n"
1460 " tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
1461 "}\n"
1462 "\n"
1463 "proc lambda.finalizer {name val} {\n"
1464 " rename $name {}\n"
1465 "}\n"
1466 "\n"
1467 "\n"
1468 "proc curry {args} {\n"
1469 " alias [ref {} function lambda.finalizer] {*}$args\n"
1470 "}\n"
1471 "\n"
1472 "\n"
1473 "\n"
1474 "\n"
1475 "\n"
1476 "\n"
1477 "\n"
1478 "\n"
1479 "\n"
1480 "proc function {value} {\n"
1481 " return $value\n"
1482 "}\n"
1483 "\n"
1484 "\n"
1485 "\n"
1486 "\n"
1487 "proc stacktrace {{skip 0}} {\n"
1488 " set trace {}\n"
1489 " incr skip\n"
1490 " foreach level [range $skip [info level]] {\n"
1491 " lappend trace {*}[info frame -$level]\n"
1492 " }\n"
1493 " return $trace\n"
1494 "}\n"
1495 "\n"
1496 "\n"
1497 "proc stackdump {stacktrace} {\n"
1498 " set lines {}\n"
1499 " foreach {l f p} [lreverse $stacktrace] {\n"
1500 " set line {}\n"
1501 " if {$p ne \"\"} {\n"
1502 " append line \"in procedure '$p' \"\n"
1503 " if {$f ne \"\"} {\n"
1504 " append line \"called \"\n"
1505 " }\n"
1506 " }\n"
1507 " if {$f ne \"\"} {\n"
1508 " append line \"at file \\\"$f\\\", line $l\"\n"
1509 " }\n"
1510 " if {$line ne \"\"} {\n"
1511 " lappend lines $line\n"
1512 " }\n"
1513 " }\n"
1514 " join $lines \\n\n"
1515 "}\n"
1516 "\n"
1517 "\n"
1518 "\n"
1519 "proc defer {script} {\n"
1520 " upvar jim::defer v\n"
1521 " lappend v $script\n"
1522 "}\n"
1523 "\n"
1524 "\n"
1525 "\n"
1526 "proc errorInfo {msg {stacktrace \"\"}} {\n"
1527 " if {$stacktrace eq \"\"} {\n"
1528 "\n"
1529 " set stacktrace [info stacktrace]\n"
1530 "\n"
1531 " lappend stacktrace {*}[stacktrace 1]\n"
1532 " }\n"
1533 " lassign $stacktrace p f l\n"
1534 " if {$f ne \"\"} {\n"
1535 " set result \"$f:$l: Error: \"\n"
1536 " }\n"
1537 " append result \"$msg\\n\"\n"
1538 " append result [stackdump $stacktrace]\n"
1539 "\n"
1540 "\n"
1541 " string trim $result\n"
1542 "}\n"
1543 "\n"
1544 "\n"
1545 "\n"
1546 "proc {info nameofexecutable} {} {\n"
1547 " if {[exists ::jim::exe]} {\n"
1548 " return $::jim::exe\n"
1549 " }\n"
1550 "}\n"
1551 "\n"
1552 "\n"
1553 "proc {dict update} {&varName args script} {\n"
1554 " set keys {}\n"
1555 " foreach {n v} $args {\n"
1556 " upvar $v var_$v\n"
1557 " if {[dict exists $varName $n]} {\n"
1558 " set var_$v [dict get $varName $n]\n"
1559 " }\n"
1560 " }\n"
1561 " catch {uplevel 1 $script} msg opts\n"
1562 " if {[info exists varName]} {\n"
1563 " foreach {n v} $args {\n"
1564 " if {[info exists var_$v]} {\n"
1565 " dict set varName $n [set var_$v]\n"
1566 " } else {\n"
1567 " dict unset varName $n\n"
1568 " }\n"
1569 " }\n"
1570 " }\n"
1571 " return {*}$opts $msg\n"
1572 "}\n"
1573 "\n"
1574 "proc {dict replace} {dictionary {args {key value}}} {\n"
1575 " if {[llength ${key value}] % 2} {\n"
1576 " tailcall {dict replace}\n"
1577 " }\n"
1578 " tailcall dict merge $dictionary ${key value}\n"
1579 "}\n"
1580 "\n"
1581 "\n"
1582 "proc {dict lappend} {varName key {args value}} {\n"
1583 " upvar $varName dict\n"
1584 " if {[exists dict] && [dict exists $dict $key]} {\n"
1585 " set list [dict get $dict $key]\n"
1586 " }\n"
1587 " lappend list {*}$value\n"
1588 " dict set dict $key $list\n"
1589 "}\n"
1590 "\n"
1591 "\n"
1592 "proc {dict append} {varName key {args value}} {\n"
1593 " upvar $varName dict\n"
1594 " if {[exists dict] && [dict exists $dict $key]} {\n"
1595 " set str [dict get $dict $key]\n"
1596 " }\n"
1597 " append str {*}$value\n"
1598 " dict set dict $key $str\n"
1599 "}\n"
1600 "\n"
1601 "\n"
1602 "proc {dict incr} {varName key {increment 1}} {\n"
1603 " upvar $varName dict\n"
1604 " if {[exists dict] && [dict exists $dict $key]} {\n"
1605 " set value [dict get $dict $key]\n"
1606 " }\n"
1607 " incr value $increment\n"
1608 " dict set dict $key $value\n"
1609 "}\n"
1610 "\n"
1611 "\n"
1612 "proc {dict remove} {dictionary {args key}} {\n"
1613 " foreach k $key {\n"
1614 " dict unset dictionary $k\n"
1615 " }\n"
1616 " return $dictionary\n"
1617 "}\n"
1618 "\n"
1619 "\n"
1620 "proc {dict for} {vars dictionary script} {\n"
1621 " if {[llength $vars] != 2} {\n"
1622 " return -code error \"must have exactly two variable names\"\n"
1623 " }\n"
1624 " dict size $dictionary\n"
1625 " tailcall foreach $vars $dictionary $script\n"
1626 "}\n"
1629 int Jim_tclcompatInit(Jim_Interp *interp)
1631 if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG))
1632 return JIM_ERR;
1634 return Jim_EvalSource(interp, "tclcompat.tcl", 1,
1635 "\n"
1636 "\n"
1637 "\n"
1638 "\n"
1639 "\n"
1640 "\n"
1641 "\n"
1642 "\n"
1643 "set env [env]\n"
1644 "\n"
1645 "\n"
1646 "if {[info commands stdout] ne \"\"} {\n"
1647 "\n"
1648 " foreach p {gets flush close eof seek tell} {\n"
1649 " proc $p {chan args} {p} {\n"
1650 " tailcall $chan $p {*}$args\n"
1651 " }\n"
1652 " }\n"
1653 " unset p\n"
1654 "\n"
1655 "\n"
1656 "\n"
1657 " proc puts {{-nonewline {}} {chan stdout} msg} {\n"
1658 " if {${-nonewline} ni {-nonewline {}}} {\n"
1659 " tailcall ${-nonewline} puts $msg\n"
1660 " }\n"
1661 " tailcall $chan puts {*}${-nonewline} $msg\n"
1662 " }\n"
1663 "\n"
1664 "\n"
1665 "\n"
1666 "\n"
1667 "\n"
1668 " proc read {{-nonewline {}} chan} {\n"
1669 " if {${-nonewline} ni {-nonewline {}}} {\n"
1670 " tailcall ${-nonewline} read {*}${chan}\n"
1671 " }\n"
1672 " tailcall $chan read {*}${-nonewline}\n"
1673 " }\n"
1674 "\n"
1675 " proc fconfigure {f args} {\n"
1676 " foreach {n v} $args {\n"
1677 " switch -glob -- $n {\n"
1678 " -bl* {\n"
1679 " $f ndelay $(!$v)\n"
1680 " }\n"
1681 " -bu* {\n"
1682 " $f buffering $v\n"
1683 " }\n"
1684 " -tr* {\n"
1685 "\n"
1686 " }\n"
1687 " default {\n"
1688 " return -code error \"fconfigure: unknown option $n\"\n"
1689 " }\n"
1690 " }\n"
1691 " }\n"
1692 " }\n"
1693 "}\n"
1694 "\n"
1695 "\n"
1696 "proc fileevent {args} {\n"
1697 " tailcall {*}$args\n"
1698 "}\n"
1699 "\n"
1700 "\n"
1701 "\n"
1702 "proc parray {arrayname {pattern *} {puts puts}} {\n"
1703 " upvar $arrayname a\n"
1704 "\n"
1705 " set max 0\n"
1706 " foreach name [array names a $pattern]] {\n"
1707 " if {[string length $name] > $max} {\n"
1708 " set max [string length $name]\n"
1709 " }\n"
1710 " }\n"
1711 " incr max [string length $arrayname]\n"
1712 " incr max 2\n"
1713 " foreach name [lsort [array names a $pattern]] {\n"
1714 " $puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
1715 " }\n"
1716 "}\n"
1717 "\n"
1718 "\n"
1719 "proc {file copy} {{force {}} source target} {\n"
1720 " try {\n"
1721 " if {$force ni {{} -force}} {\n"
1722 " error \"bad option \\\"$force\\\": should be -force\"\n"
1723 " }\n"
1724 "\n"
1725 " set in [open $source rb]\n"
1726 "\n"
1727 " if {[file exists $target]} {\n"
1728 " if {$force eq \"\"} {\n"
1729 " error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
1730 " }\n"
1731 "\n"
1732 " if {$source eq $target} {\n"
1733 " return\n"
1734 " }\n"
1735 "\n"
1736 "\n"
1737 " file stat $source ss\n"
1738 " file stat $target ts\n"
1739 " if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
1740 " return\n"
1741 " }\n"
1742 " }\n"
1743 " set out [open $target wb]\n"
1744 " $in copyto $out\n"
1745 " $out close\n"
1746 " } on error {msg opts} {\n"
1747 " incr opts(-level)\n"
1748 " return {*}$opts $msg\n"
1749 " } finally {\n"
1750 " catch {$in close}\n"
1751 " }\n"
1752 "}\n"
1753 "\n"
1754 "\n"
1755 "\n"
1756 "proc popen {cmd {mode r}} {\n"
1757 " lassign [pipe] r w\n"
1758 " try {\n"
1759 " if {[string match \"w*\" $mode]} {\n"
1760 " lappend cmd <@$r &\n"
1761 " set pids [exec {*}$cmd]\n"
1762 " $r close\n"
1763 " set f $w\n"
1764 " } else {\n"
1765 " lappend cmd >@$w &\n"
1766 " set pids [exec {*}$cmd]\n"
1767 " $w close\n"
1768 " set f $r\n"
1769 " }\n"
1770 " lambda {cmd args} {f pids} {\n"
1771 " if {$cmd eq \"pid\"} {\n"
1772 " return $pids\n"
1773 " }\n"
1774 " if {$cmd eq \"getfd\"} {\n"
1775 " $f getfd\n"
1776 " }\n"
1777 " if {$cmd eq \"close\"} {\n"
1778 " $f close\n"
1779 "\n"
1780 " set retopts {}\n"
1781 " foreach p $pids {\n"
1782 " lassign [wait $p] status - rc\n"
1783 " if {$status eq \"CHILDSTATUS\"} {\n"
1784 " if {$rc == 0} {\n"
1785 " continue\n"
1786 " }\n"
1787 " set msg \"child process exited abnormally\"\n"
1788 " } else {\n"
1789 " set msg \"child killed: received signal\"\n"
1790 " }\n"
1791 " set retopts [list -code error -errorcode [list $status $p $rc] $msg]\n"
1792 " }\n"
1793 " return {*}$retopts\n"
1794 " }\n"
1795 " tailcall $f $cmd {*}$args\n"
1796 " }\n"
1797 " } on error {error opts} {\n"
1798 " $r close\n"
1799 " $w close\n"
1800 " error $error\n"
1801 " }\n"
1802 "}\n"
1803 "\n"
1804 "\n"
1805 "local proc pid {{channelId {}}} {\n"
1806 " if {$channelId eq \"\"} {\n"
1807 " tailcall upcall pid\n"
1808 " }\n"
1809 " if {[catch {$channelId tell}]} {\n"
1810 " return -code error \"can not find channel named \\\"$channelId\\\"\"\n"
1811 " }\n"
1812 " if {[catch {$channelId pid} pids]} {\n"
1813 " return \"\"\n"
1814 " }\n"
1815 " return $pids\n"
1816 "}\n"
1817 "\n"
1818 "\n"
1819 "\n"
1820 "\n"
1821 "\n"
1822 "\n"
1823 "\n"
1824 "\n"
1825 "\n"
1826 "\n"
1827 "proc try {args} {\n"
1828 " set catchopts {}\n"
1829 " while {[string match -* [lindex $args 0]]} {\n"
1830 " set args [lassign $args opt]\n"
1831 " if {$opt eq \"--\"} {\n"
1832 " break\n"
1833 " }\n"
1834 " lappend catchopts $opt\n"
1835 " }\n"
1836 " if {[llength $args] == 0} {\n"
1837 " return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n"
1838 " }\n"
1839 " set args [lassign $args script]\n"
1840 " set code [catch -eval {*}$catchopts {uplevel 1 $script} msg opts]\n"
1841 "\n"
1842 " set handled 0\n"
1843 "\n"
1844 " foreach {on codes vars script} $args {\n"
1845 " switch -- $on \\\n"
1846 " on {\n"
1847 " if {!$handled && ($codes eq \"*\" || [info returncode $code] in $codes)} {\n"
1848 " lassign $vars msgvar optsvar\n"
1849 " if {$msgvar ne \"\"} {\n"
1850 " upvar $msgvar hmsg\n"
1851 " set hmsg $msg\n"
1852 " }\n"
1853 " if {$optsvar ne \"\"} {\n"
1854 " upvar $optsvar hopts\n"
1855 " set hopts $opts\n"
1856 " }\n"
1857 "\n"
1858 " set code [catch {uplevel 1 $script} msg opts]\n"
1859 " incr handled\n"
1860 " }\n"
1861 " } \\\n"
1862 " finally {\n"
1863 " set finalcode [catch {uplevel 1 $codes} finalmsg finalopts]\n"
1864 " if {$finalcode} {\n"
1865 "\n"
1866 " set code $finalcode\n"
1867 " set msg $finalmsg\n"
1868 " set opts $finalopts\n"
1869 " }\n"
1870 " break\n"
1871 " } \\\n"
1872 " default {\n"
1873 " return -code error \"try: expected 'on' or 'finally', got '$on'\"\n"
1874 " }\n"
1875 " }\n"
1876 "\n"
1877 " if {$code} {\n"
1878 " incr opts(-level)\n"
1879 " return {*}$opts $msg\n"
1880 " }\n"
1881 " return $msg\n"
1882 "}\n"
1883 "\n"
1884 "\n"
1885 "\n"
1886 "proc throw {code {msg \"\"}} {\n"
1887 " return -code $code $msg\n"
1888 "}\n"
1889 "\n"
1890 "\n"
1891 "proc {file delete force} {path} {\n"
1892 " foreach e [readdir $path] {\n"
1893 " file delete -force $path/$e\n"
1894 " }\n"
1895 " file delete $path\n"
1896 "}\n"
1901 #ifndef _GNU_SOURCE
1902 #define _GNU_SOURCE
1903 #endif
1904 #include <stdio.h>
1905 #include <string.h>
1906 #include <errno.h>
1907 #include <fcntl.h>
1908 #ifdef HAVE_UNISTD_H
1909 #include <unistd.h>
1910 #include <sys/stat.h>
1911 #endif
1914 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
1915 #include <sys/socket.h>
1916 #include <netinet/in.h>
1917 #include <netinet/tcp.h>
1918 #include <arpa/inet.h>
1919 #include <netdb.h>
1920 #ifdef HAVE_SYS_UN_H
1921 #include <sys/un.h>
1922 #endif
1923 #define HAVE_SOCKETS
1924 #elif defined (__MINGW32__)
1926 #else
1927 #define JIM_ANSIC
1928 #endif
1930 #if defined(JIM_SSL)
1931 #include <openssl/ssl.h>
1932 #include <openssl/err.h>
1933 #endif
1935 #ifdef HAVE_TERMIOS_H
1936 #endif
1939 #define AIO_CMD_LEN 32
1940 #define AIO_BUF_LEN 256
1942 #ifndef HAVE_FTELLO
1943 #define ftello ftell
1944 #endif
1945 #ifndef HAVE_FSEEKO
1946 #define fseeko fseek
1947 #endif
1949 #define AIO_KEEPOPEN 1
1951 #if defined(JIM_IPV6)
1952 #define IPV6 1
1953 #else
1954 #define IPV6 0
1955 #ifndef PF_INET6
1956 #define PF_INET6 0
1957 #endif
1958 #endif
1960 #ifdef JIM_ANSIC
1962 #undef HAVE_PIPE
1963 #undef HAVE_SOCKETPAIR
1964 #endif
1967 struct AioFile;
1969 typedef struct {
1970 int (*writer)(struct AioFile *af, const char *buf, int len);
1971 int (*reader)(struct AioFile *af, char *buf, int len);
1972 const char *(*getline)(struct AioFile *af, char *buf, int len);
1973 int (*error)(const struct AioFile *af);
1974 const char *(*strerror)(struct AioFile *af);
1975 int (*verify)(struct AioFile *af);
1976 } JimAioFopsType;
1978 typedef struct AioFile
1980 FILE *fp;
1981 Jim_Obj *filename;
1982 int type;
1983 int openFlags;
1984 int fd;
1985 Jim_Obj *rEvent;
1986 Jim_Obj *wEvent;
1987 Jim_Obj *eEvent;
1988 int addr_family;
1989 void *ssl;
1990 const JimAioFopsType *fops;
1991 } AioFile;
1993 static int stdio_writer(struct AioFile *af, const char *buf, int len)
1995 return fwrite(buf, 1, len, af->fp);
1998 static int stdio_reader(struct AioFile *af, char *buf, int len)
2000 return fread(buf, 1, len, af->fp);
2003 static const char *stdio_getline(struct AioFile *af, char *buf, int len)
2005 return fgets(buf, len, af->fp);
2008 static int stdio_error(const AioFile *af)
2010 if (!ferror(af->fp)) {
2011 return JIM_OK;
2013 clearerr(af->fp);
2015 if (feof(af->fp) || errno == EAGAIN || errno == EINTR) {
2016 return JIM_OK;
2018 #ifdef ECONNRESET
2019 if (errno == ECONNRESET) {
2020 return JIM_OK;
2022 #endif
2023 #ifdef ECONNABORTED
2024 if (errno == ECONNABORTED) {
2025 return JIM_OK;
2027 #endif
2028 return JIM_ERR;
2031 static const char *stdio_strerror(struct AioFile *af)
2033 return strerror(errno);
2036 static const JimAioFopsType stdio_fops = {
2037 stdio_writer,
2038 stdio_reader,
2039 stdio_getline,
2040 stdio_error,
2041 stdio_strerror,
2042 NULL
2046 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
2047 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
2048 const char *hdlfmt, int family, const char *mode);
2051 static const char *JimAioErrorString(AioFile *af)
2053 if (af && af->fops)
2054 return af->fops->strerror(af);
2056 return strerror(errno);
2059 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
2061 AioFile *af = Jim_CmdPrivData(interp);
2063 if (name) {
2064 Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
2066 else {
2067 Jim_SetResultString(interp, JimAioErrorString(af), -1);
2071 static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
2073 int ret = af->fops->error(af);
2074 if (ret) {
2075 JimAioSetError(interp, af->filename);
2077 return ret;
2080 static void JimAioDelProc(Jim_Interp *interp, void *privData)
2082 AioFile *af = privData;
2084 JIM_NOTUSED(interp);
2086 Jim_DecrRefCount(interp, af->filename);
2088 #ifdef jim_ext_eventloop
2090 Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
2091 #endif
2093 #if defined(JIM_SSL)
2094 if (af->ssl != NULL) {
2095 SSL_free(af->ssl);
2097 #endif
2098 if (!(af->openFlags & AIO_KEEPOPEN)) {
2099 fclose(af->fp);
2102 Jim_Free(af);
2105 static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2107 AioFile *af = Jim_CmdPrivData(interp);
2108 char buf[AIO_BUF_LEN];
2109 Jim_Obj *objPtr;
2110 int nonewline = 0;
2111 jim_wide neededLen = -1;
2113 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
2114 nonewline = 1;
2115 argv++;
2116 argc--;
2118 if (argc == 1) {
2119 if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
2120 return JIM_ERR;
2121 if (neededLen < 0) {
2122 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
2123 return JIM_ERR;
2126 else if (argc) {
2127 return -1;
2129 objPtr = Jim_NewStringObj(interp, NULL, 0);
2130 while (neededLen != 0) {
2131 int retval;
2132 int readlen;
2134 if (neededLen == -1) {
2135 readlen = AIO_BUF_LEN;
2137 else {
2138 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
2140 retval = af->fops->reader(af, buf, readlen);
2141 if (retval > 0) {
2142 Jim_AppendString(interp, objPtr, buf, retval);
2143 if (neededLen != -1) {
2144 neededLen -= retval;
2147 if (retval != readlen)
2148 break;
2151 if (JimCheckStreamError(interp, af)) {
2152 Jim_FreeNewObj(interp, objPtr);
2153 return JIM_ERR;
2155 if (nonewline) {
2156 int len;
2157 const char *s = Jim_GetString(objPtr, &len);
2159 if (len > 0 && s[len - 1] == '\n') {
2160 objPtr->length--;
2161 objPtr->bytes[objPtr->length] = '\0';
2164 Jim_SetResult(interp, objPtr);
2165 return JIM_OK;
2168 AioFile *Jim_AioFile(Jim_Interp *interp, Jim_Obj *command)
2170 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
2173 if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
2174 return (AioFile *) cmdPtr->u.native.privData;
2176 Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
2177 return NULL;
2180 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
2182 AioFile *af;
2184 af = Jim_AioFile(interp, command);
2185 if (af == NULL) {
2186 return NULL;
2189 return af->fp;
2192 static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2194 AioFile *af = Jim_CmdPrivData(interp);
2196 fflush(af->fp);
2197 Jim_SetResultInt(interp, fileno(af->fp));
2199 return JIM_OK;
2202 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2204 AioFile *af = Jim_CmdPrivData(interp);
2205 jim_wide count = 0;
2206 jim_wide maxlen = JIM_WIDE_MAX;
2207 AioFile *outf = Jim_AioFile(interp, argv[0]);
2209 if (outf == NULL) {
2210 return JIM_ERR;
2213 if (argc == 2) {
2214 if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
2215 return JIM_ERR;
2219 while (count < maxlen) {
2220 char ch;
2222 if (af->fops->reader(af, &ch, 1) != 1) {
2223 break;
2225 if (outf->fops->writer(outf, &ch, 1) != 1) {
2226 break;
2228 count++;
2231 if (JimCheckStreamError(interp, af) || JimCheckStreamError(interp, outf)) {
2232 return JIM_ERR;
2235 Jim_SetResultInt(interp, count);
2237 return JIM_OK;
2240 static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2242 AioFile *af = Jim_CmdPrivData(interp);
2243 char buf[AIO_BUF_LEN];
2244 Jim_Obj *objPtr;
2245 int len;
2247 errno = 0;
2249 objPtr = Jim_NewStringObj(interp, NULL, 0);
2250 while (1) {
2251 buf[AIO_BUF_LEN - 1] = '_';
2253 if (af->fops->getline(af, buf, AIO_BUF_LEN) == NULL)
2254 break;
2256 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
2257 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
2259 else {
2260 len = strlen(buf);
2262 if (len && (buf[len - 1] == '\n')) {
2264 len--;
2267 Jim_AppendString(interp, objPtr, buf, len);
2268 break;
2272 if (JimCheckStreamError(interp, af)) {
2274 Jim_FreeNewObj(interp, objPtr);
2275 return JIM_ERR;
2278 if (argc) {
2279 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
2280 Jim_FreeNewObj(interp, objPtr);
2281 return JIM_ERR;
2284 len = Jim_Length(objPtr);
2286 if (len == 0 && feof(af->fp)) {
2288 len = -1;
2290 Jim_SetResultInt(interp, len);
2292 else {
2293 Jim_SetResult(interp, objPtr);
2295 return JIM_OK;
2298 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2300 AioFile *af = Jim_CmdPrivData(interp);
2301 int wlen;
2302 const char *wdata;
2303 Jim_Obj *strObj;
2305 if (argc == 2) {
2306 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
2307 return -1;
2309 strObj = argv[1];
2311 else {
2312 strObj = argv[0];
2315 wdata = Jim_GetString(strObj, &wlen);
2316 if (af->fops->writer(af, wdata, wlen) == wlen) {
2317 if (argc == 2 || af->fops->writer(af, "\n", 1) == 1) {
2318 return JIM_OK;
2321 JimAioSetError(interp, af->filename);
2322 return JIM_ERR;
2325 static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2327 #ifdef HAVE_ISATTY
2328 AioFile *af = Jim_CmdPrivData(interp);
2329 Jim_SetResultInt(interp, isatty(fileno(af->fp)));
2330 #else
2331 Jim_SetResultInt(interp, 0);
2332 #endif
2334 return JIM_OK;
2338 static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2340 AioFile *af = Jim_CmdPrivData(interp);
2342 if (fflush(af->fp) == EOF) {
2343 JimAioSetError(interp, af->filename);
2344 return JIM_ERR;
2346 return JIM_OK;
2349 static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2351 AioFile *af = Jim_CmdPrivData(interp);
2353 Jim_SetResultInt(interp, feof(af->fp));
2354 return JIM_OK;
2357 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2359 if (argc == 3) {
2360 #if defined(HAVE_SOCKETS) && defined(HAVE_SHUTDOWN)
2361 static const char * const options[] = { "r", "w", NULL };
2362 enum { OPT_R, OPT_W, };
2363 int option;
2364 AioFile *af = Jim_CmdPrivData(interp);
2366 if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2367 return JIM_ERR;
2369 if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
2370 return JIM_OK;
2372 JimAioSetError(interp, NULL);
2373 #else
2374 Jim_SetResultString(interp, "async close not supported", -1);
2375 #endif
2376 return JIM_ERR;
2379 return Jim_DeleteCommand(interp, Jim_String(argv[0]));
2382 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2384 AioFile *af = Jim_CmdPrivData(interp);
2385 int orig = SEEK_SET;
2386 jim_wide offset;
2388 if (argc == 2) {
2389 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
2390 orig = SEEK_SET;
2391 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
2392 orig = SEEK_CUR;
2393 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
2394 orig = SEEK_END;
2395 else {
2396 return -1;
2399 if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
2400 return JIM_ERR;
2402 if (fseeko(af->fp, offset, orig) == -1) {
2403 JimAioSetError(interp, af->filename);
2404 return JIM_ERR;
2406 return JIM_OK;
2409 static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2411 AioFile *af = Jim_CmdPrivData(interp);
2413 Jim_SetResultInt(interp, ftello(af->fp));
2414 return JIM_OK;
2417 static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2419 AioFile *af = Jim_CmdPrivData(interp);
2421 Jim_SetResult(interp, af->filename);
2422 return JIM_OK;
2425 #ifdef O_NDELAY
2426 static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2428 AioFile *af = Jim_CmdPrivData(interp);
2430 int fmode = fcntl(af->fd, F_GETFL);
2432 if (argc) {
2433 long nb;
2435 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
2436 return JIM_ERR;
2438 if (nb) {
2439 fmode |= O_NDELAY;
2441 else {
2442 fmode &= ~O_NDELAY;
2444 (void)fcntl(af->fd, F_SETFL, fmode);
2446 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
2447 return JIM_OK;
2449 #endif
2452 #ifdef HAVE_FSYNC
2453 static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2455 AioFile *af = Jim_CmdPrivData(interp);
2457 fflush(af->fp);
2458 fsync(af->fd);
2459 return JIM_OK;
2461 #endif
2463 static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2465 AioFile *af = Jim_CmdPrivData(interp);
2467 static const char * const options[] = {
2468 "none",
2469 "line",
2470 "full",
2471 NULL
2473 enum
2475 OPT_NONE,
2476 OPT_LINE,
2477 OPT_FULL,
2479 int option;
2481 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
2482 return JIM_ERR;
2484 switch (option) {
2485 case OPT_NONE:
2486 setvbuf(af->fp, NULL, _IONBF, 0);
2487 break;
2488 case OPT_LINE:
2489 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
2490 break;
2491 case OPT_FULL:
2492 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
2493 break;
2495 return JIM_OK;
2498 #ifdef jim_ext_eventloop
2499 static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
2501 Jim_Obj **objPtrPtr = clientData;
2503 Jim_DecrRefCount(interp, *objPtrPtr);
2504 *objPtrPtr = NULL;
2507 static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
2509 Jim_Obj **objPtrPtr = clientData;
2511 return Jim_EvalObjBackground(interp, *objPtrPtr);
2514 static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
2515 int argc, Jim_Obj * const *argv)
2517 if (argc == 0) {
2519 if (*scriptHandlerObj) {
2520 Jim_SetResult(interp, *scriptHandlerObj);
2522 return JIM_OK;
2525 if (*scriptHandlerObj) {
2527 Jim_DeleteFileHandler(interp, af->fd, mask);
2531 if (Jim_Length(argv[0]) == 0) {
2533 return JIM_OK;
2537 Jim_IncrRefCount(argv[0]);
2538 *scriptHandlerObj = argv[0];
2540 Jim_CreateFileHandler(interp, af->fd, mask,
2541 JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer);
2543 return JIM_OK;
2546 static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2548 AioFile *af = Jim_CmdPrivData(interp);
2550 return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
2553 static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2555 AioFile *af = Jim_CmdPrivData(interp);
2557 return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
2560 static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2562 AioFile *af = Jim_CmdPrivData(interp);
2564 return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv);
2566 #endif
2571 static const jim_subcmd_type aio_command_table[] = {
2572 { "read",
2573 "?-nonewline? ?len?",
2574 aio_cmd_read,
2579 { "copyto",
2580 "handle ?size?",
2581 aio_cmd_copy,
2586 { "getfd",
2587 NULL,
2588 aio_cmd_getfd,
2593 { "gets",
2594 "?var?",
2595 aio_cmd_gets,
2600 { "puts",
2601 "?-nonewline? str",
2602 aio_cmd_puts,
2607 { "isatty",
2608 NULL,
2609 aio_cmd_isatty,
2614 { "flush",
2615 NULL,
2616 aio_cmd_flush,
2621 { "eof",
2622 NULL,
2623 aio_cmd_eof,
2628 { "close",
2629 "?r(ead)|w(rite)?",
2630 aio_cmd_close,
2633 JIM_MODFLAG_FULLARGV,
2636 { "seek",
2637 "offset ?start|current|end",
2638 aio_cmd_seek,
2643 { "tell",
2644 NULL,
2645 aio_cmd_tell,
2650 { "filename",
2651 NULL,
2652 aio_cmd_filename,
2657 #ifdef O_NDELAY
2658 { "ndelay",
2659 "?0|1?",
2660 aio_cmd_ndelay,
2665 #endif
2666 #ifdef HAVE_FSYNC
2667 { "sync",
2668 NULL,
2669 aio_cmd_sync,
2674 #endif
2675 { "buffering",
2676 "none|line|full",
2677 aio_cmd_buffering,
2682 #ifdef jim_ext_eventloop
2683 { "readable",
2684 "?readable-script?",
2685 aio_cmd_readable,
2690 { "writable",
2691 "?writable-script?",
2692 aio_cmd_writable,
2697 { "onexception",
2698 "?exception-script?",
2699 aio_cmd_onexception,
2704 #endif
2705 { NULL }
2708 static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2710 return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
2713 static int JimAioOpenCommand(Jim_Interp *interp, int argc,
2714 Jim_Obj *const *argv)
2716 const char *mode;
2718 if (argc != 2 && argc != 3) {
2719 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
2720 return JIM_ERR;
2723 mode = (argc == 3) ? Jim_String(argv[2]) : "r";
2725 #ifdef jim_ext_tclcompat
2727 const char *filename = Jim_String(argv[1]);
2730 if (*filename == '|') {
2731 Jim_Obj *evalObj[3];
2733 evalObj[0] = Jim_NewStringObj(interp, "::popen", -1);
2734 evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
2735 evalObj[2] = Jim_NewStringObj(interp, mode, -1);
2737 return Jim_EvalObjVector(interp, 3, evalObj);
2740 #endif
2741 return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode) ? JIM_OK : JIM_ERR;
2745 static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
2746 const char *hdlfmt, int family, const char *mode)
2748 AioFile *af;
2749 char buf[AIO_CMD_LEN];
2750 int openFlags = 0;
2752 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2754 if (fh) {
2755 openFlags = AIO_KEEPOPEN;
2758 snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
2759 if (!filename) {
2760 filename = Jim_NewStringObj(interp, buf, -1);
2763 Jim_IncrRefCount(filename);
2765 if (fh == NULL) {
2766 if (fd >= 0) {
2767 #ifndef JIM_ANSIC
2768 fh = fdopen(fd, mode);
2769 #endif
2771 else
2772 fh = fopen(Jim_String(filename), mode);
2774 if (fh == NULL) {
2775 JimAioSetError(interp, filename);
2776 #ifndef JIM_ANSIC
2777 if (fd >= 0) {
2778 close(fd);
2780 #endif
2781 Jim_DecrRefCount(interp, filename);
2782 return NULL;
2787 af = Jim_Alloc(sizeof(*af));
2788 memset(af, 0, sizeof(*af));
2789 af->fp = fh;
2790 af->filename = filename;
2791 af->openFlags = openFlags;
2792 #ifndef JIM_ANSIC
2793 af->fd = fileno(fh);
2794 #ifdef FD_CLOEXEC
2795 if ((openFlags & AIO_KEEPOPEN) == 0) {
2796 (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
2798 #endif
2799 #endif
2800 af->addr_family = family;
2801 af->fops = &stdio_fops;
2802 af->ssl = NULL;
2804 Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
2806 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
2808 return af;
2811 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
2812 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
2813 const char *hdlfmt, int family, const char *mode[2])
2815 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
2816 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
2817 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2818 if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
2819 Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
2820 Jim_SetResult(interp, objPtr);
2821 return JIM_OK;
2826 close(p[0]);
2827 close(p[1]);
2828 JimAioSetError(interp, NULL);
2829 return JIM_ERR;
2831 #endif
2833 #ifdef HAVE_PIPE
2834 static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2836 int p[2];
2837 static const char *mode[2] = { "r", "w" };
2839 if (argc != 1) {
2840 Jim_WrongNumArgs(interp, 1, argv, "");
2841 return JIM_ERR;
2844 if (pipe(p) != 0) {
2845 JimAioSetError(interp, NULL);
2846 return JIM_ERR;
2849 return JimMakeChannelPair(interp, p, argv[0], "aio.pipe%ld", 0, mode);
2851 #endif
2855 int Jim_aioInit(Jim_Interp *interp)
2857 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
2858 return JIM_ERR;
2860 #if defined(JIM_SSL)
2861 Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
2862 #endif
2864 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
2865 #ifdef HAVE_SOCKETS
2866 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
2867 #endif
2868 #ifdef HAVE_PIPE
2869 Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
2870 #endif
2873 JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
2874 JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w");
2875 JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w");
2877 return JIM_OK;
2880 #include <errno.h>
2881 #include <stdio.h>
2882 #include <string.h>
2885 #ifdef HAVE_DIRENT_H
2886 #include <dirent.h>
2887 #endif
2889 int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
2891 const char *dirPath;
2892 DIR *dirPtr;
2893 struct dirent *entryPtr;
2894 int nocomplain = 0;
2896 if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) {
2897 nocomplain = 1;
2899 if (argc != 2 && !nocomplain) {
2900 Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
2901 return JIM_ERR;
2904 dirPath = Jim_String(argv[1 + nocomplain]);
2906 dirPtr = opendir(dirPath);
2907 if (dirPtr == NULL) {
2908 if (nocomplain) {
2909 return JIM_OK;
2911 Jim_SetResultString(interp, strerror(errno), -1);
2912 return JIM_ERR;
2914 else {
2915 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
2917 while ((entryPtr = readdir(dirPtr)) != NULL) {
2918 if (entryPtr->d_name[0] == '.') {
2919 if (entryPtr->d_name[1] == '\0') {
2920 continue;
2922 if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0'))
2923 continue;
2925 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1));
2927 closedir(dirPtr);
2929 Jim_SetResult(interp, listObj);
2931 return JIM_OK;
2935 int Jim_readdirInit(Jim_Interp *interp)
2937 if (Jim_PackageProvide(interp, "readdir", "1.0", JIM_ERRMSG))
2938 return JIM_ERR;
2940 Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
2941 return JIM_OK;
2944 #include <stdlib.h>
2945 #include <string.h>
2947 #if defined(JIM_REGEXP)
2948 #else
2949 #include <regex.h>
2950 #endif
2952 static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
2954 regfree(objPtr->internalRep.ptrIntValue.ptr);
2955 Jim_Free(objPtr->internalRep.ptrIntValue.ptr);
2958 static const Jim_ObjType regexpObjType = {
2959 "regexp",
2960 FreeRegexpInternalRep,
2961 NULL,
2962 NULL,
2963 JIM_TYPE_NONE
2966 static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
2968 regex_t *compre;
2969 const char *pattern;
2970 int ret;
2973 if (objPtr->typePtr == &regexpObjType &&
2974 objPtr->internalRep.ptrIntValue.ptr && objPtr->internalRep.ptrIntValue.int1 == flags) {
2976 return objPtr->internalRep.ptrIntValue.ptr;
2982 pattern = Jim_String(objPtr);
2983 compre = Jim_Alloc(sizeof(regex_t));
2985 if ((ret = regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
2986 char buf[100];
2988 regerror(ret, compre, buf, sizeof(buf));
2989 Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
2990 regfree(compre);
2991 Jim_Free(compre);
2992 return NULL;
2995 Jim_FreeIntRep(interp, objPtr);
2997 objPtr->typePtr = &regexpObjType;
2998 objPtr->internalRep.ptrIntValue.int1 = flags;
2999 objPtr->internalRep.ptrIntValue.ptr = compre;
3001 return compre;
3004 int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3006 int opt_indices = 0;
3007 int opt_all = 0;
3008 int opt_inline = 0;
3009 regex_t *regex;
3010 int match, i, j;
3011 int offset = 0;
3012 regmatch_t *pmatch = NULL;
3013 int source_len;
3014 int result = JIM_OK;
3015 const char *pattern;
3016 const char *source_str;
3017 int num_matches = 0;
3018 int num_vars;
3019 Jim_Obj *resultListObj = NULL;
3020 int regcomp_flags = 0;
3021 int eflags = 0;
3022 int option;
3023 enum {
3024 OPT_INDICES, OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
3026 static const char * const options[] = {
3027 "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
3030 if (argc < 3) {
3031 wrongNumArgs:
3032 Jim_WrongNumArgs(interp, 1, argv,
3033 "?-switch ...? exp string ?matchVar? ?subMatchVar ...?");
3034 return JIM_ERR;
3037 for (i = 1; i < argc; i++) {
3038 const char *opt = Jim_String(argv[i]);
3040 if (*opt != '-') {
3041 break;
3043 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3044 return JIM_ERR;
3046 if (option == OPT_END) {
3047 i++;
3048 break;
3050 switch (option) {
3051 case OPT_INDICES:
3052 opt_indices = 1;
3053 break;
3055 case OPT_NOCASE:
3056 regcomp_flags |= REG_ICASE;
3057 break;
3059 case OPT_LINE:
3060 regcomp_flags |= REG_NEWLINE;
3061 break;
3063 case OPT_ALL:
3064 opt_all = 1;
3065 break;
3067 case OPT_INLINE:
3068 opt_inline = 1;
3069 break;
3071 case OPT_START:
3072 if (++i == argc) {
3073 goto wrongNumArgs;
3075 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3076 return JIM_ERR;
3078 break;
3081 if (argc - i < 2) {
3082 goto wrongNumArgs;
3085 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
3086 if (!regex) {
3087 return JIM_ERR;
3090 pattern = Jim_String(argv[i]);
3091 source_str = Jim_GetString(argv[i + 1], &source_len);
3093 num_vars = argc - i - 2;
3095 if (opt_inline) {
3096 if (num_vars) {
3097 Jim_SetResultString(interp, "regexp match variables not allowed when using -inline",
3098 -1);
3099 result = JIM_ERR;
3100 goto done;
3102 num_vars = regex->re_nsub + 1;
3105 pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch));
3107 if (offset) {
3108 if (offset < 0) {
3109 offset += source_len + 1;
3111 if (offset > source_len) {
3112 source_str += source_len;
3114 else if (offset > 0) {
3115 source_str += offset;
3117 eflags |= REG_NOTBOL;
3120 if (opt_inline) {
3121 resultListObj = Jim_NewListObj(interp, NULL, 0);
3124 next_match:
3125 match = regexec(regex, source_str, num_vars + 1, pmatch, eflags);
3126 if (match >= REG_BADPAT) {
3127 char buf[100];
3129 regerror(match, regex, buf, sizeof(buf));
3130 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3131 result = JIM_ERR;
3132 goto done;
3135 if (match == REG_NOMATCH) {
3136 goto done;
3139 num_matches++;
3141 if (opt_all && !opt_inline) {
3143 goto try_next_match;
3147 j = 0;
3148 for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
3149 Jim_Obj *resultObj;
3151 if (opt_indices) {
3152 resultObj = Jim_NewListObj(interp, NULL, 0);
3154 else {
3155 resultObj = Jim_NewStringObj(interp, "", 0);
3158 if (pmatch[j].rm_so == -1) {
3159 if (opt_indices) {
3160 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3161 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
3164 else {
3165 int len = pmatch[j].rm_eo - pmatch[j].rm_so;
3167 if (opt_indices) {
3168 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
3169 offset + pmatch[j].rm_so));
3170 Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp,
3171 offset + pmatch[j].rm_so + len - 1));
3173 else {
3174 Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, len);
3178 if (opt_inline) {
3179 Jim_ListAppendElement(interp, resultListObj, resultObj);
3181 else {
3183 result = Jim_SetVariable(interp, argv[i], resultObj);
3185 if (result != JIM_OK) {
3186 Jim_FreeObj(interp, resultObj);
3187 break;
3192 try_next_match:
3193 if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
3194 if (pmatch[0].rm_eo) {
3195 offset += pmatch[0].rm_eo;
3196 source_str += pmatch[0].rm_eo;
3198 else {
3199 source_str++;
3200 offset++;
3202 if (*source_str) {
3203 eflags = REG_NOTBOL;
3204 goto next_match;
3208 done:
3209 if (result == JIM_OK) {
3210 if (opt_inline) {
3211 Jim_SetResult(interp, resultListObj);
3213 else {
3214 Jim_SetResultInt(interp, num_matches);
3218 Jim_Free(pmatch);
3219 return result;
3222 #define MAX_SUB_MATCHES 50
3224 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3226 int regcomp_flags = 0;
3227 int regexec_flags = 0;
3228 int opt_all = 0;
3229 int offset = 0;
3230 regex_t *regex;
3231 const char *p;
3232 int result;
3233 regmatch_t pmatch[MAX_SUB_MATCHES + 1];
3234 int num_matches = 0;
3236 int i, j, n;
3237 Jim_Obj *varname;
3238 Jim_Obj *resultObj;
3239 const char *source_str;
3240 int source_len;
3241 const char *replace_str;
3242 int replace_len;
3243 const char *pattern;
3244 int option;
3245 enum {
3246 OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_END
3248 static const char * const options[] = {
3249 "-nocase", "-line", "-all", "-start", "--", NULL
3252 if (argc < 4) {
3253 wrongNumArgs:
3254 Jim_WrongNumArgs(interp, 1, argv,
3255 "?-switch ...? exp string subSpec ?varName?");
3256 return JIM_ERR;
3259 for (i = 1; i < argc; i++) {
3260 const char *opt = Jim_String(argv[i]);
3262 if (*opt != '-') {
3263 break;
3265 if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
3266 return JIM_ERR;
3268 if (option == OPT_END) {
3269 i++;
3270 break;
3272 switch (option) {
3273 case OPT_NOCASE:
3274 regcomp_flags |= REG_ICASE;
3275 break;
3277 case OPT_LINE:
3278 regcomp_flags |= REG_NEWLINE;
3279 break;
3281 case OPT_ALL:
3282 opt_all = 1;
3283 break;
3285 case OPT_START:
3286 if (++i == argc) {
3287 goto wrongNumArgs;
3289 if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
3290 return JIM_ERR;
3292 break;
3295 if (argc - i != 3 && argc - i != 4) {
3296 goto wrongNumArgs;
3299 regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
3300 if (!regex) {
3301 return JIM_ERR;
3303 pattern = Jim_String(argv[i]);
3305 source_str = Jim_GetString(argv[i + 1], &source_len);
3306 replace_str = Jim_GetString(argv[i + 2], &replace_len);
3307 varname = argv[i + 3];
3310 resultObj = Jim_NewStringObj(interp, "", 0);
3312 if (offset) {
3313 if (offset < 0) {
3314 offset += source_len + 1;
3316 if (offset > source_len) {
3317 offset = source_len;
3319 else if (offset < 0) {
3320 offset = 0;
3325 Jim_AppendString(interp, resultObj, source_str, offset);
3328 n = source_len - offset;
3329 p = source_str + offset;
3330 do {
3331 int match = regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
3333 if (match >= REG_BADPAT) {
3334 char buf[100];
3336 regerror(match, regex, buf, sizeof(buf));
3337 Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
3338 return JIM_ERR;
3340 if (match == REG_NOMATCH) {
3341 break;
3344 num_matches++;
3346 Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
3349 for (j = 0; j < replace_len; j++) {
3350 int idx;
3351 int c = replace_str[j];
3353 if (c == '&') {
3354 idx = 0;
3356 else if (c == '\\' && j < replace_len) {
3357 c = replace_str[++j];
3358 if ((c >= '0') && (c <= '9')) {
3359 idx = c - '0';
3361 else if ((c == '\\') || (c == '&')) {
3362 Jim_AppendString(interp, resultObj, replace_str + j, 1);
3363 continue;
3365 else {
3366 Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2);
3367 continue;
3370 else {
3371 Jim_AppendString(interp, resultObj, replace_str + j, 1);
3372 continue;
3374 if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
3375 Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
3376 pmatch[idx].rm_eo - pmatch[idx].rm_so);
3380 p += pmatch[0].rm_eo;
3381 n -= pmatch[0].rm_eo;
3384 if (!opt_all || n == 0) {
3385 break;
3389 if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
3390 break;
3394 if (pattern[0] == '\0' && n) {
3396 Jim_AppendString(interp, resultObj, p, 1);
3397 p++;
3398 n--;
3401 regexec_flags |= REG_NOTBOL;
3402 } while (n);
3404 Jim_AppendString(interp, resultObj, p, -1);
3407 if (argc - i == 4) {
3408 result = Jim_SetVariable(interp, varname, resultObj);
3410 if (result == JIM_OK) {
3411 Jim_SetResultInt(interp, num_matches);
3413 else {
3414 Jim_FreeObj(interp, resultObj);
3417 else {
3418 Jim_SetResult(interp, resultObj);
3419 result = JIM_OK;
3422 return result;
3425 int Jim_regexpInit(Jim_Interp *interp)
3427 if (Jim_PackageProvide(interp, "regexp", "1.0", JIM_ERRMSG))
3428 return JIM_ERR;
3430 Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
3431 Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
3432 return JIM_OK;
3435 #include <limits.h>
3436 #include <stdlib.h>
3437 #include <string.h>
3438 #include <stdio.h>
3439 #include <errno.h>
3440 #include <sys/stat.h>
3443 #ifdef HAVE_UTIMES
3444 #include <sys/time.h>
3445 #endif
3446 #ifdef HAVE_UNISTD_H
3447 #include <unistd.h>
3448 #elif defined(_MSC_VER)
3449 #include <direct.h>
3450 #define F_OK 0
3451 #define W_OK 2
3452 #define R_OK 4
3453 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
3454 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
3455 #endif
3457 # ifndef MAXPATHLEN
3458 # define MAXPATHLEN JIM_PATH_LEN
3459 # endif
3461 #if defined(__MINGW32__) || defined(__MSYS__) || defined(_MSC_VER)
3462 #define ISWINDOWS 1
3463 #else
3464 #define ISWINDOWS 0
3465 #endif
3468 #if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
3469 #define STAT_MTIME_US(STAT) ((STAT).st_mtimespec.tv_sec * 1000000ll + (STAT).st_mtimespec.tv_nsec / 1000)
3470 #elif defined(HAVE_STRUCT_STAT_ST_MTIM)
3471 #define STAT_MTIME_US(STAT) ((STAT).st_mtim.tv_sec * 1000000ll + (STAT).st_mtim.tv_nsec / 1000)
3472 #endif
3475 static const char *JimGetFileType(int mode)
3477 if (S_ISREG(mode)) {
3478 return "file";
3480 else if (S_ISDIR(mode)) {
3481 return "directory";
3483 #ifdef S_ISCHR
3484 else if (S_ISCHR(mode)) {
3485 return "characterSpecial";
3487 #endif
3488 #ifdef S_ISBLK
3489 else if (S_ISBLK(mode)) {
3490 return "blockSpecial";
3492 #endif
3493 #ifdef S_ISFIFO
3494 else if (S_ISFIFO(mode)) {
3495 return "fifo";
3497 #endif
3498 #ifdef S_ISLNK
3499 else if (S_ISLNK(mode)) {
3500 return "link";
3502 #endif
3503 #ifdef S_ISSOCK
3504 else if (S_ISSOCK(mode)) {
3505 return "socket";
3507 #endif
3508 return "unknown";
3511 static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
3513 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
3514 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
3517 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
3520 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
3522 AppendStatElement(interp, listObj, "dev", sb->st_dev);
3523 AppendStatElement(interp, listObj, "ino", sb->st_ino);
3524 AppendStatElement(interp, listObj, "mode", sb->st_mode);
3525 AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
3526 AppendStatElement(interp, listObj, "uid", sb->st_uid);
3527 AppendStatElement(interp, listObj, "gid", sb->st_gid);
3528 AppendStatElement(interp, listObj, "size", sb->st_size);
3529 AppendStatElement(interp, listObj, "atime", sb->st_atime);
3530 AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
3531 AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
3532 #ifdef STAT_MTIME_US
3533 AppendStatElement(interp, listObj, "mtimeus", STAT_MTIME_US(*sb));
3534 #endif
3535 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
3536 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));
3539 if (varName) {
3540 Jim_Obj *objPtr;
3541 objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
3543 if (objPtr) {
3544 Jim_Obj *objv[2];
3546 objv[0] = objPtr;
3547 objv[1] = listObj;
3549 objPtr = Jim_DictMerge(interp, 2, objv);
3550 if (objPtr == NULL) {
3552 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
3553 Jim_FreeNewObj(interp, listObj);
3554 return JIM_ERR;
3557 Jim_InvalidateStringRep(objPtr);
3559 Jim_FreeNewObj(interp, listObj);
3560 listObj = objPtr;
3562 Jim_SetVariable(interp, varName, listObj);
3566 Jim_SetResult(interp, listObj);
3568 return JIM_OK;
3571 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3573 const char *path = Jim_String(argv[0]);
3574 const char *p = strrchr(path, '/');
3576 if (!p && path[0] == '.' && path[1] == '.' && path[2] == '\0') {
3577 Jim_SetResultString(interp, "..", -1);
3578 } else if (!p) {
3579 Jim_SetResultString(interp, ".", -1);
3581 else if (p == path) {
3582 Jim_SetResultString(interp, "/", -1);
3584 else if (ISWINDOWS && p[-1] == ':') {
3586 Jim_SetResultString(interp, path, p - path + 1);
3588 else {
3589 Jim_SetResultString(interp, path, p - path);
3591 return JIM_OK;
3594 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3596 const char *path = Jim_String(argv[0]);
3597 const char *lastSlash = strrchr(path, '/');
3598 const char *p = strrchr(path, '.');
3600 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
3601 Jim_SetResult(interp, argv[0]);
3603 else {
3604 Jim_SetResultString(interp, path, p - path);
3606 return JIM_OK;
3609 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3611 const char *path = Jim_String(argv[0]);
3612 const char *lastSlash = strrchr(path, '/');
3613 const char *p = strrchr(path, '.');
3615 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
3616 p = "";
3618 Jim_SetResultString(interp, p, -1);
3619 return JIM_OK;
3622 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3624 const char *path = Jim_String(argv[0]);
3625 const char *lastSlash = strrchr(path, '/');
3627 if (lastSlash) {
3628 Jim_SetResultString(interp, lastSlash + 1, -1);
3630 else {
3631 Jim_SetResult(interp, argv[0]);
3633 return JIM_OK;
3636 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3638 #ifdef HAVE_REALPATH
3639 const char *path = Jim_String(argv[0]);
3640 char *newname = Jim_Alloc(MAXPATHLEN + 1);
3642 if (realpath(path, newname)) {
3643 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
3644 return JIM_OK;
3646 else {
3647 Jim_Free(newname);
3648 Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
3649 return JIM_ERR;
3651 #else
3652 Jim_SetResultString(interp, "Not implemented", -1);
3653 return JIM_ERR;
3654 #endif
3657 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3659 int i;
3660 char *newname = Jim_Alloc(MAXPATHLEN + 1);
3661 char *last = newname;
3663 *newname = 0;
3666 for (i = 0; i < argc; i++) {
3667 int len;
3668 const char *part = Jim_GetString(argv[i], &len);
3670 if (*part == '/') {
3672 last = newname;
3674 else if (ISWINDOWS && strchr(part, ':')) {
3676 last = newname;
3678 else if (part[0] == '.') {
3679 if (part[1] == '/') {
3680 part += 2;
3681 len -= 2;
3683 else if (part[1] == 0 && last != newname) {
3685 continue;
3690 if (last != newname && last[-1] != '/') {
3691 *last++ = '/';
3694 if (len) {
3695 if (last + len - newname >= MAXPATHLEN) {
3696 Jim_Free(newname);
3697 Jim_SetResultString(interp, "Path too long", -1);
3698 return JIM_ERR;
3700 memcpy(last, part, len);
3701 last += len;
3705 if (last > newname + 1 && last[-1] == '/') {
3707 if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
3708 *--last = 0;
3713 *last = 0;
3717 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
3719 return JIM_OK;
3722 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
3724 Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1);
3726 return JIM_OK;
3729 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3731 return file_access(interp, argv[0], R_OK);
3734 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3736 return file_access(interp, argv[0], W_OK);
3739 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3741 #ifdef X_OK
3742 return file_access(interp, argv[0], X_OK);
3743 #else
3745 Jim_SetResultBool(interp, 1);
3746 return JIM_OK;
3747 #endif
3750 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3752 return file_access(interp, argv[0], F_OK);
3755 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3757 int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
3759 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
3760 argc++;
3761 argv--;
3764 while (argc--) {
3765 const char *path = Jim_String(argv[0]);
3767 if (unlink(path) == -1 && errno != ENOENT) {
3768 if (rmdir(path) == -1) {
3770 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
3771 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
3772 strerror(errno));
3773 return JIM_ERR;
3777 argv++;
3779 return JIM_OK;
3782 #ifdef HAVE_MKDIR_ONE_ARG
3783 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
3784 #else
3785 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
3786 #endif
3788 static int mkdir_all(char *path)
3790 int ok = 1;
3793 goto first;
3795 while (ok--) {
3798 char *slash = strrchr(path, '/');
3800 if (slash && slash != path) {
3801 *slash = 0;
3802 if (mkdir_all(path) != 0) {
3803 return -1;
3805 *slash = '/';
3808 first:
3809 if (MKDIR_DEFAULT(path) == 0) {
3810 return 0;
3812 if (errno == ENOENT) {
3814 continue;
3817 if (errno == EEXIST) {
3818 struct stat sb;
3820 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
3821 return 0;
3824 errno = EEXIST;
3827 break;
3829 return -1;
3832 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3834 while (argc--) {
3835 char *path = Jim_StrDup(Jim_String(argv[0]));
3836 int rc = mkdir_all(path);
3838 Jim_Free(path);
3839 if (rc != 0) {
3840 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
3841 strerror(errno));
3842 return JIM_ERR;
3844 argv++;
3846 return JIM_OK;
3849 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3851 int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL, 0);
3853 if (fd < 0) {
3854 return JIM_ERR;
3856 close(fd);
3858 return JIM_OK;
3861 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3863 const char *source;
3864 const char *dest;
3865 int force = 0;
3867 if (argc == 3) {
3868 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
3869 return -1;
3871 force++;
3872 argv++;
3873 argc--;
3876 source = Jim_String(argv[0]);
3877 dest = Jim_String(argv[1]);
3879 if (!force && access(dest, F_OK) == 0) {
3880 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
3881 argv[1]);
3882 return JIM_ERR;
3885 if (rename(source, dest) != 0) {
3886 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3887 strerror(errno));
3888 return JIM_ERR;
3891 return JIM_OK;
3894 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
3895 static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3897 int ret;
3898 const char *source;
3899 const char *dest;
3900 static const char * const options[] = { "-hard", "-symbolic", NULL };
3901 enum { OPT_HARD, OPT_SYMBOLIC, };
3902 int option = OPT_HARD;
3904 if (argc == 3) {
3905 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
3906 return JIM_ERR;
3908 argv++;
3909 argc--;
3912 dest = Jim_String(argv[0]);
3913 source = Jim_String(argv[1]);
3915 if (option == OPT_HARD) {
3916 ret = link(source, dest);
3918 else {
3919 ret = symlink(source, dest);
3922 if (ret != 0) {
3923 Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3924 strerror(errno));
3925 return JIM_ERR;
3928 return JIM_OK;
3930 #endif
3932 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3934 const char *path = Jim_String(filename);
3936 if (stat(path, sb) == -1) {
3937 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3938 return JIM_ERR;
3940 return JIM_OK;
3943 #ifdef HAVE_LSTAT
3944 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3946 const char *path = Jim_String(filename);
3948 if (lstat(path, sb) == -1) {
3949 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
3950 return JIM_ERR;
3952 return JIM_OK;
3954 #else
3955 #define file_lstat file_stat
3956 #endif
3958 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3960 struct stat sb;
3962 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
3963 return JIM_ERR;
3965 Jim_SetResultInt(interp, sb.st_atime);
3966 return JIM_OK;
3969 static int JimSetFileTimes(Jim_Interp *interp, const char *filename, jim_wide us)
3971 #ifdef HAVE_UTIMES
3972 struct timeval times[2];
3974 times[1].tv_sec = times[0].tv_sec = us / 1000000;
3975 times[1].tv_usec = times[0].tv_usec = us % 1000000;
3977 if (utimes(filename, times) != 0) {
3978 Jim_SetResultFormatted(interp, "can't set time on \"%s\": %s", filename, strerror(errno));
3979 return JIM_ERR;
3981 return JIM_OK;
3982 #else
3983 Jim_SetResultString(interp, "Not implemented", -1);
3984 return JIM_ERR;
3985 #endif
3988 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3990 struct stat sb;
3992 if (argc == 2) {
3993 jim_wide secs;
3994 if (Jim_GetWide(interp, argv[1], &secs) != JIM_OK) {
3995 return JIM_ERR;
3997 return JimSetFileTimes(interp, Jim_String(argv[0]), secs * 1000000);
3999 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4000 return JIM_ERR;
4002 Jim_SetResultInt(interp, sb.st_mtime);
4003 return JIM_OK;
4006 #ifdef STAT_MTIME_US
4007 static int file_cmd_mtimeus(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4009 struct stat sb;
4011 if (argc == 2) {
4012 jim_wide us;
4013 if (Jim_GetWide(interp, argv[1], &us) != JIM_OK) {
4014 return JIM_ERR;
4016 return JimSetFileTimes(interp, Jim_String(argv[0]), us);
4018 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4019 return JIM_ERR;
4021 Jim_SetResultInt(interp, STAT_MTIME_US(sb));
4022 return JIM_OK;
4024 #endif
4026 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4028 return Jim_EvalPrefix(interp, "file copy", argc, argv);
4031 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4033 struct stat sb;
4035 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4036 return JIM_ERR;
4038 Jim_SetResultInt(interp, sb.st_size);
4039 return JIM_OK;
4042 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4044 struct stat sb;
4045 int ret = 0;
4047 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4048 ret = S_ISDIR(sb.st_mode);
4050 Jim_SetResultInt(interp, ret);
4051 return JIM_OK;
4054 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4056 struct stat sb;
4057 int ret = 0;
4059 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4060 ret = S_ISREG(sb.st_mode);
4062 Jim_SetResultInt(interp, ret);
4063 return JIM_OK;
4066 #ifdef HAVE_GETEUID
4067 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4069 struct stat sb;
4070 int ret = 0;
4072 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
4073 ret = (geteuid() == sb.st_uid);
4075 Jim_SetResultInt(interp, ret);
4076 return JIM_OK;
4078 #endif
4080 #if defined(HAVE_READLINK)
4081 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4083 const char *path = Jim_String(argv[0]);
4084 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
4086 int linkLength = readlink(path, linkValue, MAXPATHLEN);
4088 if (linkLength == -1) {
4089 Jim_Free(linkValue);
4090 Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
4091 return JIM_ERR;
4093 linkValue[linkLength] = 0;
4094 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
4095 return JIM_OK;
4097 #endif
4099 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4101 struct stat sb;
4103 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
4104 return JIM_ERR;
4106 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
4107 return JIM_OK;
4110 #ifdef HAVE_LSTAT
4111 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4113 struct stat sb;
4115 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
4116 return JIM_ERR;
4118 return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
4120 #else
4121 #define file_cmd_lstat file_cmd_stat
4122 #endif
4124 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4126 struct stat sb;
4128 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
4129 return JIM_ERR;
4131 return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
4134 static const jim_subcmd_type file_command_table[] = {
4135 { "atime",
4136 "name",
4137 file_cmd_atime,
4142 { "mtime",
4143 "name ?time?",
4144 file_cmd_mtime,
4149 #ifdef STAT_MTIME_US
4150 { "mtimeus",
4151 "name ?time?",
4152 file_cmd_mtimeus,
4157 #endif
4158 { "copy",
4159 "?-force? source dest",
4160 file_cmd_copy,
4165 { "dirname",
4166 "name",
4167 file_cmd_dirname,
4172 { "rootname",
4173 "name",
4174 file_cmd_rootname,
4179 { "extension",
4180 "name",
4181 file_cmd_extension,
4186 { "tail",
4187 "name",
4188 file_cmd_tail,
4193 { "normalize",
4194 "name",
4195 file_cmd_normalize,
4200 { "join",
4201 "name ?name ...?",
4202 file_cmd_join,
4207 { "readable",
4208 "name",
4209 file_cmd_readable,
4214 { "writable",
4215 "name",
4216 file_cmd_writable,
4221 { "executable",
4222 "name",
4223 file_cmd_executable,
4228 { "exists",
4229 "name",
4230 file_cmd_exists,
4235 { "delete",
4236 "?-force|--? name ...",
4237 file_cmd_delete,
4242 { "mkdir",
4243 "dir ...",
4244 file_cmd_mkdir,
4249 { "tempfile",
4250 "?template?",
4251 file_cmd_tempfile,
4256 { "rename",
4257 "?-force? source dest",
4258 file_cmd_rename,
4263 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
4264 { "link",
4265 "?-symbolic|-hard? newname target",
4266 file_cmd_link,
4271 #endif
4272 #if defined(HAVE_READLINK)
4273 { "readlink",
4274 "name",
4275 file_cmd_readlink,
4280 #endif
4281 { "size",
4282 "name",
4283 file_cmd_size,
4288 { "stat",
4289 "name ?var?",
4290 file_cmd_stat,
4295 { "lstat",
4296 "name ?var?",
4297 file_cmd_lstat,
4302 { "type",
4303 "name",
4304 file_cmd_type,
4309 #ifdef HAVE_GETEUID
4310 { "owned",
4311 "name",
4312 file_cmd_owned,
4317 #endif
4318 { "isdirectory",
4319 "name",
4320 file_cmd_isdirectory,
4325 { "isfile",
4326 "name",
4327 file_cmd_isfile,
4333 NULL
4337 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4339 const char *path;
4341 if (argc != 2) {
4342 Jim_WrongNumArgs(interp, 1, argv, "dirname");
4343 return JIM_ERR;
4346 path = Jim_String(argv[1]);
4348 if (chdir(path) != 0) {
4349 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
4350 strerror(errno));
4351 return JIM_ERR;
4353 return JIM_OK;
4356 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4358 char *cwd = Jim_Alloc(MAXPATHLEN);
4360 if (getcwd(cwd, MAXPATHLEN) == NULL) {
4361 Jim_SetResultString(interp, "Failed to get pwd", -1);
4362 Jim_Free(cwd);
4363 return JIM_ERR;
4365 else if (ISWINDOWS) {
4367 char *p = cwd;
4368 while ((p = strchr(p, '\\')) != NULL) {
4369 *p++ = '/';
4373 Jim_SetResultString(interp, cwd, -1);
4375 Jim_Free(cwd);
4376 return JIM_OK;
4379 int Jim_fileInit(Jim_Interp *interp)
4381 if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
4382 return JIM_ERR;
4384 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
4385 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
4386 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
4387 return JIM_OK;
4390 #ifndef _GNU_SOURCE
4391 #define _GNU_SOURCE
4392 #endif
4393 #include <string.h>
4394 #include <ctype.h>
4397 #if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
4398 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4400 Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
4401 int i, j;
4402 int rc;
4405 for (i = 1; i < argc; i++) {
4406 int len;
4407 const char *arg = Jim_GetString(argv[i], &len);
4409 if (i > 1) {
4410 Jim_AppendString(interp, cmdlineObj, " ", 1);
4412 if (strpbrk(arg, "\\\" ") == NULL) {
4414 Jim_AppendString(interp, cmdlineObj, arg, len);
4415 continue;
4418 Jim_AppendString(interp, cmdlineObj, "\"", 1);
4419 for (j = 0; j < len; j++) {
4420 if (arg[j] == '\\' || arg[j] == '"') {
4421 Jim_AppendString(interp, cmdlineObj, "\\", 1);
4423 Jim_AppendString(interp, cmdlineObj, &arg[j], 1);
4425 Jim_AppendString(interp, cmdlineObj, "\"", 1);
4427 rc = system(Jim_String(cmdlineObj));
4429 Jim_FreeNewObj(interp, cmdlineObj);
4431 if (rc) {
4432 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
4433 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4434 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0));
4435 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc));
4436 Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
4437 return JIM_ERR;
4440 return JIM_OK;
4443 int Jim_execInit(Jim_Interp *interp)
4445 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
4446 return JIM_ERR;
4448 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
4449 return JIM_OK;
4451 #else
4454 #include <errno.h>
4455 #include <signal.h>
4456 #include <sys/stat.h>
4458 struct WaitInfoTable;
4460 static char **JimOriginalEnviron(void);
4461 static char **JimSaveEnv(char **env);
4462 static void JimRestoreEnv(char **env);
4463 static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
4464 pidtype **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
4465 static void JimDetachPids(struct WaitInfoTable *table, int numPids, const pidtype *pidPtr);
4466 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj);
4467 static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
4469 #if defined(__MINGW32__)
4470 static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId);
4471 #endif
4473 static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
4475 int len;
4476 const char *s = Jim_GetString(objPtr, &len);
4478 if (len > 0 && s[len - 1] == '\n') {
4479 objPtr->length--;
4480 objPtr->bytes[objPtr->length] = '\0';
4484 static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj)
4486 char buf[256];
4487 FILE *fh = fdopen(fd, "r");
4488 int ret = 0;
4490 if (fh == NULL) {
4491 return -1;
4494 while (1) {
4495 int retval = fread(buf, 1, sizeof(buf), fh);
4496 if (retval > 0) {
4497 ret = 1;
4498 Jim_AppendString(interp, strObj, buf, retval);
4500 if (retval != sizeof(buf)) {
4501 break;
4504 fclose(fh);
4505 return ret;
4508 static char **JimBuildEnv(Jim_Interp *interp)
4510 int i;
4511 int size;
4512 int num;
4513 int n;
4514 char **envptr;
4515 char *envdata;
4517 Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
4519 if (!objPtr) {
4520 return JimOriginalEnviron();
4525 num = Jim_ListLength(interp, objPtr);
4526 if (num % 2) {
4528 num--;
4530 size = Jim_Length(objPtr) + 2;
4532 envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
4533 envdata = (char *)&envptr[num / 2 + 1];
4535 n = 0;
4536 for (i = 0; i < num; i += 2) {
4537 const char *s1, *s2;
4538 Jim_Obj *elemObj;
4540 Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
4541 s1 = Jim_String(elemObj);
4542 Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
4543 s2 = Jim_String(elemObj);
4545 envptr[n] = envdata;
4546 envdata += sprintf(envdata, "%s=%s", s1, s2);
4547 envdata++;
4548 n++;
4550 envptr[n] = NULL;
4551 *envdata = 0;
4553 return envptr;
4556 static void JimFreeEnv(char **env, char **original_environ)
4558 if (env != original_environ) {
4559 Jim_Free(env);
4563 static Jim_Obj *JimMakeErrorCode(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
4565 Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
4567 if (pid == JIM_BAD_PID || pid == JIM_NO_PID) {
4568 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
4569 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4570 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, -1));
4572 else if (WIFEXITED(waitStatus)) {
4573 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
4574 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4575 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
4577 else {
4578 const char *type;
4579 const char *action;
4580 const char *signame;
4582 if (WIFSIGNALED(waitStatus)) {
4583 type = "CHILDKILLED";
4584 action = "killed";
4585 signame = Jim_SignalId(WTERMSIG(waitStatus));
4587 else {
4588 type = "CHILDSUSP";
4589 action = "suspended";
4590 signame = "none";
4593 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
4595 if (errStrObj) {
4596 Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
4599 Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
4600 Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, signame, -1));
4602 return errorCode;
4605 static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
4607 if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
4608 return JIM_OK;
4610 Jim_SetGlobalVariableStr(interp, "errorCode", JimMakeErrorCode(interp, pid, waitStatus, errStrObj));
4612 return JIM_ERR;
4616 struct WaitInfo
4618 pidtype pid;
4619 int status;
4620 int flags;
4624 struct WaitInfoTable {
4625 struct WaitInfo *info;
4626 int size;
4627 int used;
4628 int refcount;
4632 #define WI_DETACHED 2
4634 #define WAIT_TABLE_GROW_BY 4
4636 static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
4638 struct WaitInfoTable *table = privData;
4640 if (--table->refcount == 0) {
4641 Jim_Free(table->info);
4642 Jim_Free(table);
4646 static struct WaitInfoTable *JimAllocWaitInfoTable(void)
4648 struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
4649 table->info = NULL;
4650 table->size = table->used = 0;
4651 table->refcount = 1;
4653 return table;
4656 static int JimWaitRemove(struct WaitInfoTable *table, pidtype pid)
4658 int i;
4661 for (i = 0; i < table->used; i++) {
4662 if (pid == table->info[i].pid) {
4663 if (i != table->used - 1) {
4664 table->info[i] = table->info[table->used - 1];
4666 table->used--;
4667 return 0;
4670 return -1;
4673 static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4675 int outputId;
4676 int errorId;
4677 pidtype *pidPtr;
4678 int numPids, result;
4679 int child_siginfo = 1;
4680 Jim_Obj *childErrObj;
4681 Jim_Obj *errStrObj;
4682 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4684 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
4685 Jim_Obj *listObj;
4686 int i;
4688 argc--;
4689 numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
4690 if (numPids < 0) {
4691 return JIM_ERR;
4694 listObj = Jim_NewListObj(interp, NULL, 0);
4695 for (i = 0; i < numPids; i++) {
4696 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i]));
4698 Jim_SetResult(interp, listObj);
4699 JimDetachPids(table, numPids, pidPtr);
4700 Jim_Free(pidPtr);
4701 return JIM_OK;
4704 numPids =
4705 JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
4707 if (numPids < 0) {
4708 return JIM_ERR;
4711 result = JIM_OK;
4713 errStrObj = Jim_NewStringObj(interp, "", 0);
4716 if (outputId != -1) {
4717 if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) {
4718 result = JIM_ERR;
4719 Jim_SetResultErrno(interp, "error reading from output pipe");
4724 childErrObj = Jim_NewStringObj(interp, "", 0);
4725 Jim_IncrRefCount(childErrObj);
4727 if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
4728 result = JIM_ERR;
4731 if (errorId != -1) {
4732 int ret;
4733 lseek(errorId, 0, SEEK_SET);
4734 ret = JimAppendStreamToString(interp, errorId, errStrObj);
4735 if (ret < 0) {
4736 Jim_SetResultErrno(interp, "error reading from error pipe");
4737 result = JIM_ERR;
4739 else if (ret > 0) {
4741 child_siginfo = 0;
4745 if (child_siginfo) {
4747 Jim_AppendObj(interp, errStrObj, childErrObj);
4749 Jim_DecrRefCount(interp, childErrObj);
4752 Jim_RemoveTrailingNewline(errStrObj);
4755 Jim_SetResult(interp, errStrObj);
4757 return result;
4760 static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
4762 if (JimWaitRemove(table, pid) == 0) {
4764 waitpid(pid, statusPtr, 0);
4765 return pid;
4769 return JIM_BAD_PID;
4772 static void JimDetachPids(struct WaitInfoTable *table, int numPids, const pidtype *pidPtr)
4774 int j;
4776 for (j = 0; j < numPids; j++) {
4778 int i;
4779 for (i = 0; i < table->used; i++) {
4780 if (pidPtr[j] == table->info[i].pid) {
4781 table->info[i].flags |= WI_DETACHED;
4782 break;
4788 static int JimGetChannelFd(Jim_Interp *interp, const char *name)
4790 Jim_Obj *objv[2];
4792 objv[0] = Jim_NewStringObj(interp, name, -1);
4793 objv[1] = Jim_NewStringObj(interp, "getfd", -1);
4795 if (Jim_EvalObjVector(interp, 2, objv) == JIM_OK) {
4796 jim_wide fd;
4797 if (Jim_GetWide(interp, Jim_GetResult(interp), &fd) == JIM_OK) {
4798 return fd;
4801 return -1;
4804 static void JimReapDetachedPids(struct WaitInfoTable *table)
4806 struct WaitInfo *waitPtr;
4807 int count;
4808 int dest;
4810 if (!table) {
4811 return;
4814 waitPtr = table->info;
4815 dest = 0;
4816 for (count = table->used; count > 0; waitPtr++, count--) {
4817 if (waitPtr->flags & WI_DETACHED) {
4818 int status;
4819 pidtype pid = waitpid(waitPtr->pid, &status, WNOHANG);
4820 if (pid == waitPtr->pid) {
4822 table->used--;
4823 continue;
4826 if (waitPtr != &table->info[dest]) {
4827 table->info[dest] = *waitPtr;
4829 dest++;
4833 static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4835 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4836 int nohang = 0;
4837 pidtype pid;
4838 long pidarg;
4839 int status;
4840 Jim_Obj *errCodeObj;
4843 if (argc == 1) {
4844 JimReapDetachedPids(table);
4845 return JIM_OK;
4848 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) {
4849 nohang = 1;
4851 if (argc != nohang + 2) {
4852 Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?");
4853 return JIM_ERR;
4855 if (Jim_GetLong(interp, argv[nohang + 1], &pidarg) != JIM_OK) {
4856 return JIM_ERR;
4859 pid = waitpid((pidtype)pidarg, &status, nohang ? WNOHANG : 0);
4861 errCodeObj = JimMakeErrorCode(interp, pid, status, NULL);
4863 if (pid != JIM_BAD_PID && (WIFEXITED(status) || WIFSIGNALED(status))) {
4865 JimWaitRemove(table, pid);
4867 Jim_SetResult(interp, errCodeObj);
4868 return JIM_OK;
4871 static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
4873 if (argc != 1) {
4874 Jim_WrongNumArgs(interp, 1, argv, "");
4875 return JIM_ERR;
4878 Jim_SetResultInt(interp, (jim_wide)getpid());
4879 return JIM_OK;
4882 static int
4883 JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr,
4884 int *inPipePtr, int *outPipePtr, int *errFilePtr)
4886 pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all
4887 * the pids of child processes. */
4888 int numPids = 0; /* Actual number of processes that exist
4889 * at *pidPtr right now. */
4890 int cmdCount; /* Count of number of distinct commands
4891 * found in argc/argv. */
4892 const char *input = NULL; /* Describes input for pipeline, depending
4893 * on "inputFile". NULL means take input
4894 * from stdin/pipe. */
4895 int input_len = 0;
4897 #define FILE_NAME 0
4898 #define FILE_APPEND 1
4899 #define FILE_HANDLE 2
4900 #define FILE_TEXT 3
4902 int inputFile = FILE_NAME; /* 1 means input is name of input file.
4903 * 2 means input is filehandle name.
4904 * 0 means input holds actual
4905 * text to be input to command. */
4907 int outputFile = FILE_NAME; /* 0 means output is the name of output file.
4908 * 1 means output is the name of output file, and append.
4909 * 2 means output is filehandle name.
4910 * All this is ignored if output is NULL
4912 int errorFile = FILE_NAME; /* 0 means error is the name of error file.
4913 * 1 means error is the name of error file, and append.
4914 * 2 means error is filehandle name.
4915 * All this is ignored if error is NULL
4917 const char *output = NULL; /* Holds name of output file to pipe to,
4918 * or NULL if output goes to stdout/pipe. */
4919 const char *error = NULL; /* Holds name of stderr file to pipe to,
4920 * or NULL if stderr goes to stderr/pipe. */
4921 int inputId = -1;
4922 int outputId = -1;
4923 int errorId = -1;
4924 int lastOutputId = -1;
4925 int pipeIds[2];
4926 int firstArg, lastArg; /* Indexes of first and last arguments in
4927 * current command. */
4928 int lastBar;
4929 int i;
4930 pidtype pid;
4931 char **save_environ;
4932 #ifndef __MINGW32__
4933 char **child_environ;
4934 #endif
4935 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
4938 char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
4939 int arg_count = 0;
4941 if (inPipePtr != NULL) {
4942 *inPipePtr = -1;
4944 if (outPipePtr != NULL) {
4945 *outPipePtr = -1;
4947 if (errFilePtr != NULL) {
4948 *errFilePtr = -1;
4950 pipeIds[0] = pipeIds[1] = -1;
4952 cmdCount = 1;
4953 lastBar = -1;
4954 for (i = 0; i < argc; i++) {
4955 const char *arg = Jim_String(argv[i]);
4957 if (arg[0] == '<') {
4958 inputFile = FILE_NAME;
4959 input = arg + 1;
4960 if (*input == '<') {
4961 inputFile = FILE_TEXT;
4962 input_len = Jim_Length(argv[i]) - 2;
4963 input++;
4965 else if (*input == '@') {
4966 inputFile = FILE_HANDLE;
4967 input++;
4970 if (!*input && ++i < argc) {
4971 input = Jim_GetString(argv[i], &input_len);
4974 else if (arg[0] == '>') {
4975 int dup_error = 0;
4977 outputFile = FILE_NAME;
4979 output = arg + 1;
4980 if (*output == '>') {
4981 outputFile = FILE_APPEND;
4982 output++;
4984 if (*output == '&') {
4986 output++;
4987 dup_error = 1;
4989 if (*output == '@') {
4990 outputFile = FILE_HANDLE;
4991 output++;
4993 if (!*output && ++i < argc) {
4994 output = Jim_String(argv[i]);
4996 if (dup_error) {
4997 errorFile = outputFile;
4998 error = output;
5001 else if (arg[0] == '2' && arg[1] == '>') {
5002 error = arg + 2;
5003 errorFile = FILE_NAME;
5005 if (*error == '@') {
5006 errorFile = FILE_HANDLE;
5007 error++;
5009 else if (*error == '>') {
5010 errorFile = FILE_APPEND;
5011 error++;
5013 if (!*error && ++i < argc) {
5014 error = Jim_String(argv[i]);
5017 else {
5018 if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
5019 if (i == lastBar + 1 || i == argc - 1) {
5020 Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
5021 goto badargs;
5023 lastBar = i;
5024 cmdCount++;
5027 arg_array[arg_count++] = (char *)arg;
5028 continue;
5031 if (i >= argc) {
5032 Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
5033 goto badargs;
5037 if (arg_count == 0) {
5038 Jim_SetResultString(interp, "didn't specify command to execute", -1);
5039 badargs:
5040 Jim_Free(arg_array);
5041 return -1;
5045 save_environ = JimSaveEnv(JimBuildEnv(interp));
5047 if (input != NULL) {
5048 if (inputFile == FILE_TEXT) {
5049 inputId = Jim_MakeTempFile(interp, NULL, 1);
5050 if (inputId == -1) {
5051 goto error;
5053 if (write(inputId, input, input_len) != input_len) {
5054 Jim_SetResultErrno(interp, "couldn't write temp file");
5055 close(inputId);
5056 goto error;
5058 lseek(inputId, 0L, SEEK_SET);
5060 else if (inputFile == FILE_HANDLE) {
5061 int fd = JimGetChannelFd(interp, input);
5063 if (fd < 0) {
5064 goto error;
5066 inputId = dup(fd);
5068 else {
5069 inputId = Jim_OpenForRead(input);
5070 if (inputId == -1) {
5071 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, strerror(Jim_Errno()));
5072 goto error;
5076 else if (inPipePtr != NULL) {
5077 if (pipe(pipeIds) != 0) {
5078 Jim_SetResultErrno(interp, "couldn't create input pipe for command");
5079 goto error;
5081 inputId = pipeIds[0];
5082 *inPipePtr = pipeIds[1];
5083 pipeIds[0] = pipeIds[1] = -1;
5086 if (output != NULL) {
5087 if (outputFile == FILE_HANDLE) {
5088 int fd = JimGetChannelFd(interp, output);
5089 if (fd < 0) {
5090 goto error;
5092 lastOutputId = dup(fd);
5094 else {
5095 lastOutputId = Jim_OpenForWrite(output, outputFile == FILE_APPEND);
5096 if (lastOutputId == -1) {
5097 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, strerror(Jim_Errno()));
5098 goto error;
5102 else if (outPipePtr != NULL) {
5103 if (pipe(pipeIds) != 0) {
5104 Jim_SetResultErrno(interp, "couldn't create output pipe");
5105 goto error;
5107 lastOutputId = pipeIds[1];
5108 *outPipePtr = pipeIds[0];
5109 pipeIds[0] = pipeIds[1] = -1;
5112 if (error != NULL) {
5113 if (errorFile == FILE_HANDLE) {
5114 if (strcmp(error, "1") == 0) {
5116 if (lastOutputId != -1) {
5117 errorId = dup(lastOutputId);
5119 else {
5121 error = "stdout";
5124 if (errorId == -1) {
5125 int fd = JimGetChannelFd(interp, error);
5126 if (fd < 0) {
5127 goto error;
5129 errorId = dup(fd);
5132 else {
5133 errorId = Jim_OpenForWrite(error, errorFile == FILE_APPEND);
5134 if (errorId == -1) {
5135 Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, strerror(Jim_Errno()));
5136 goto error;
5140 else if (errFilePtr != NULL) {
5141 errorId = Jim_MakeTempFile(interp, NULL, 1);
5142 if (errorId == -1) {
5143 goto error;
5145 *errFilePtr = dup(errorId);
5149 pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
5150 for (i = 0; i < numPids; i++) {
5151 pidPtr[i] = JIM_BAD_PID;
5153 for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
5154 int pipe_dup_err = 0;
5155 int origErrorId = errorId;
5157 for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
5158 if (strcmp(arg_array[lastArg], "|") == 0) {
5159 break;
5161 if (strcmp(arg_array[lastArg], "|&") == 0) {
5162 pipe_dup_err = 1;
5163 break;
5167 if (lastArg == firstArg) {
5168 Jim_SetResultString(interp, "missing command to exec", -1);
5169 goto error;
5173 arg_array[lastArg] = NULL;
5174 if (lastArg == arg_count) {
5175 outputId = lastOutputId;
5176 lastOutputId = -1;
5178 else {
5179 if (pipe(pipeIds) != 0) {
5180 Jim_SetResultErrno(interp, "couldn't create pipe");
5181 goto error;
5183 outputId = pipeIds[1];
5187 if (pipe_dup_err) {
5188 errorId = outputId;
5193 #ifdef __MINGW32__
5194 pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId);
5195 if (pid == JIM_BAD_PID) {
5196 Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
5197 goto error;
5199 #else
5200 i = strlen(arg_array[firstArg]);
5202 child_environ = Jim_GetEnviron();
5203 pid = vfork();
5204 if (pid < 0) {
5205 Jim_SetResultErrno(interp, "couldn't fork child process");
5206 goto error;
5208 if (pid == 0) {
5211 if (inputId != -1) {
5212 dup2(inputId, fileno(stdin));
5213 close(inputId);
5215 if (outputId != -1) {
5216 dup2(outputId, fileno(stdout));
5217 if (outputId != errorId) {
5218 close(outputId);
5221 if (errorId != -1) {
5222 dup2(errorId, fileno(stderr));
5223 close(errorId);
5226 if (outPipePtr) {
5227 close(*outPipePtr);
5229 if (errFilePtr) {
5230 close(*errFilePtr);
5232 if (pipeIds[0] != -1) {
5233 close(pipeIds[0]);
5235 if (lastOutputId != -1) {
5236 close(lastOutputId);
5240 (void)signal(SIGPIPE, SIG_DFL);
5242 execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ);
5244 if (write(fileno(stderr), "couldn't exec \"", 15) &&
5245 write(fileno(stderr), arg_array[firstArg], i) &&
5246 write(fileno(stderr), "\"\n", 2)) {
5249 #ifdef JIM_MAINTAINER
5252 static char *const false_argv[2] = {"false", NULL};
5253 execvp(false_argv[0],false_argv);
5255 #endif
5256 _exit(127);
5258 #endif
5262 if (table->used == table->size) {
5263 table->size += WAIT_TABLE_GROW_BY;
5264 table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
5267 table->info[table->used].pid = pid;
5268 table->info[table->used].flags = 0;
5269 table->used++;
5271 pidPtr[numPids] = pid;
5274 errorId = origErrorId;
5277 if (inputId != -1) {
5278 close(inputId);
5280 if (outputId != -1) {
5281 close(outputId);
5283 inputId = pipeIds[0];
5284 pipeIds[0] = pipeIds[1] = -1;
5286 *pidArrayPtr = pidPtr;
5289 cleanup:
5290 if (inputId != -1) {
5291 close(inputId);
5293 if (lastOutputId != -1) {
5294 close(lastOutputId);
5296 if (errorId != -1) {
5297 close(errorId);
5299 Jim_Free(arg_array);
5301 JimRestoreEnv(save_environ);
5303 return numPids;
5306 error:
5307 if ((inPipePtr != NULL) && (*inPipePtr != -1)) {
5308 close(*inPipePtr);
5309 *inPipePtr = -1;
5311 if ((outPipePtr != NULL) && (*outPipePtr != -1)) {
5312 close(*outPipePtr);
5313 *outPipePtr = -1;
5315 if ((errFilePtr != NULL) && (*errFilePtr != -1)) {
5316 close(*errFilePtr);
5317 *errFilePtr = -1;
5319 if (pipeIds[0] != -1) {
5320 close(pipeIds[0]);
5322 if (pipeIds[1] != -1) {
5323 close(pipeIds[1]);
5325 if (pidPtr != NULL) {
5326 for (i = 0; i < numPids; i++) {
5327 if (pidPtr[i] != JIM_BAD_PID) {
5328 JimDetachPids(table, 1, &pidPtr[i]);
5331 Jim_Free(pidPtr);
5333 numPids = -1;
5334 goto cleanup;
5338 static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj)
5340 struct WaitInfoTable *table = Jim_CmdPrivData(interp);
5341 int result = JIM_OK;
5342 int i;
5345 for (i = 0; i < numPids; i++) {
5346 int waitStatus = 0;
5347 if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) {
5348 if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus, errStrObj) != JIM_OK) {
5349 result = JIM_ERR;
5353 Jim_Free(pidPtr);
5355 return result;
5358 int Jim_execInit(Jim_Interp *interp)
5360 struct WaitInfoTable *waitinfo;
5361 if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
5362 return JIM_ERR;
5364 #ifdef SIGPIPE
5365 (void)signal(SIGPIPE, SIG_IGN);
5366 #endif
5368 waitinfo = JimAllocWaitInfoTable();
5369 Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable);
5370 waitinfo->refcount++;
5371 Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable);
5372 Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0);
5374 return JIM_OK;
5377 #if defined(__MINGW32__)
5380 static int
5381 JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
5383 int i;
5384 static char extensions[][5] = {".exe", "", ".bat"};
5386 for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
5387 snprintf(fullPath, MAX_PATH, "%s%s", originalName, extensions[i]);
5389 if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) {
5390 continue;
5392 if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
5393 continue;
5395 return 0;
5398 return -1;
5401 static char **JimSaveEnv(char **env)
5403 return env;
5406 static void JimRestoreEnv(char **env)
5408 JimFreeEnv(env, Jim_GetEnviron());
5411 static char **JimOriginalEnviron(void)
5413 return NULL;
5416 static Jim_Obj *
5417 JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
5419 char *start, *special;
5420 int quote, i;
5422 Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0);
5424 for (i = 0; argv[i]; i++) {
5425 if (i > 0) {
5426 Jim_AppendString(interp, strObj, " ", 1);
5429 if (argv[i][0] == '\0') {
5430 quote = 1;
5432 else {
5433 quote = 0;
5434 for (start = argv[i]; *start != '\0'; start++) {
5435 if (isspace(UCHAR(*start))) {
5436 quote = 1;
5437 break;
5441 if (quote) {
5442 Jim_AppendString(interp, strObj, "\"" , 1);
5445 start = argv[i];
5446 for (special = argv[i]; ; ) {
5447 if ((*special == '\\') && (special[1] == '\\' ||
5448 special[1] == '"' || (quote && special[1] == '\0'))) {
5449 Jim_AppendString(interp, strObj, start, special - start);
5450 start = special;
5451 while (1) {
5452 special++;
5453 if (*special == '"' || (quote && *special == '\0')) {
5455 Jim_AppendString(interp, strObj, start, special - start);
5456 break;
5458 if (*special != '\\') {
5459 break;
5462 Jim_AppendString(interp, strObj, start, special - start);
5463 start = special;
5465 if (*special == '"') {
5466 if (special == start) {
5467 Jim_AppendString(interp, strObj, "\"", 1);
5469 else {
5470 Jim_AppendString(interp, strObj, start, special - start);
5472 Jim_AppendString(interp, strObj, "\\\"", 2);
5473 start = special + 1;
5475 if (*special == '\0') {
5476 break;
5478 special++;
5480 Jim_AppendString(interp, strObj, start, special - start);
5481 if (quote) {
5482 Jim_AppendString(interp, strObj, "\"", 1);
5485 return strObj;
5488 static pidtype
5489 JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId)
5491 STARTUPINFO startInfo;
5492 PROCESS_INFORMATION procInfo;
5493 HANDLE hProcess;
5494 char execPath[MAX_PATH];
5495 pidtype pid = JIM_BAD_PID;
5496 Jim_Obj *cmdLineObj;
5497 char *winenv;
5499 if (JimWinFindExecutable(argv[0], execPath) < 0) {
5500 return JIM_BAD_PID;
5502 argv[0] = execPath;
5504 hProcess = GetCurrentProcess();
5505 cmdLineObj = JimWinBuildCommandLine(interp, argv);
5508 ZeroMemory(&startInfo, sizeof(startInfo));
5509 startInfo.cb = sizeof(startInfo);
5510 startInfo.dwFlags = STARTF_USESTDHANDLES;
5511 startInfo.hStdInput = INVALID_HANDLE_VALUE;
5512 startInfo.hStdOutput= INVALID_HANDLE_VALUE;
5513 startInfo.hStdError = INVALID_HANDLE_VALUE;
5515 if (inputId == -1) {
5516 inputId = _fileno(stdin);
5518 DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(inputId), hProcess, &startInfo.hStdInput,
5519 0, TRUE, DUPLICATE_SAME_ACCESS);
5520 if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
5521 goto end;
5524 if (outputId == -1) {
5525 outputId = _fileno(stdout);
5527 DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(outputId), hProcess, &startInfo.hStdOutput,
5528 0, TRUE, DUPLICATE_SAME_ACCESS);
5529 if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
5530 goto end;
5534 if (errorId == -1) {
5535 errorId = _fileno(stderr);
5537 DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(errorId), hProcess, &startInfo.hStdError,
5538 0, TRUE, DUPLICATE_SAME_ACCESS);
5539 if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
5540 goto end;
5543 if (env == NULL) {
5545 winenv = NULL;
5547 else if (env[0] == NULL) {
5548 winenv = (char *)"\0";
5550 else {
5551 winenv = env[0];
5554 if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
5555 0, winenv, NULL, &startInfo, &procInfo)) {
5556 goto end;
5560 WaitForInputIdle(procInfo.hProcess, 5000);
5561 CloseHandle(procInfo.hThread);
5563 pid = procInfo.hProcess;
5565 end:
5566 Jim_FreeNewObj(interp, cmdLineObj);
5567 if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
5568 CloseHandle(startInfo.hStdInput);
5570 if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
5571 CloseHandle(startInfo.hStdOutput);
5573 if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
5574 CloseHandle(startInfo.hStdError);
5576 return pid;
5579 #else
5581 static char **JimOriginalEnviron(void)
5583 return Jim_GetEnviron();
5586 static char **JimSaveEnv(char **env)
5588 char **saveenv = Jim_GetEnviron();
5589 Jim_SetEnviron(env);
5590 return saveenv;
5593 static void JimRestoreEnv(char **env)
5595 JimFreeEnv(Jim_GetEnviron(), env);
5596 Jim_SetEnviron(env);
5598 #endif
5599 #endif
5603 #ifdef STRPTIME_NEEDS_XOPEN_SOURCE
5604 #ifndef _XOPEN_SOURCE
5605 #define _XOPEN_SOURCE 500
5606 #endif
5607 #endif
5610 #ifndef _GNU_SOURCE
5611 #define _GNU_SOURCE
5612 #endif
5614 #include <stdlib.h>
5615 #include <string.h>
5616 #include <stdio.h>
5617 #include <time.h>
5620 #ifdef HAVE_SYS_TIME_H
5621 #include <sys/time.h>
5622 #endif
5624 struct clock_options {
5625 int gmt;
5626 const char *format;
5629 static int parse_clock_options(Jim_Interp *interp, int argc, Jim_Obj *const *argv, struct clock_options *opts)
5631 static const char * const options[] = { "-gmt", "-format", NULL };
5632 enum { OPT_GMT, OPT_FORMAT, };
5633 int i;
5635 for (i = 0; i < argc; i += 2) {
5636 int option;
5637 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
5638 return JIM_ERR;
5640 switch (option) {
5641 case OPT_GMT:
5642 if (Jim_GetBoolean(interp, argv[i + 1], &opts->gmt) != JIM_OK) {
5643 return JIM_ERR;
5645 break;
5646 case OPT_FORMAT:
5647 opts->format = Jim_String(argv[i + 1]);
5648 break;
5651 return JIM_OK;
5654 static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5657 char buf[100];
5658 time_t t;
5659 jim_wide seconds;
5660 struct clock_options options = { 0, "%a %b %d %H:%M:%S %Z %Y" };
5661 struct tm *tm;
5663 if (Jim_GetWide(interp, argv[0], &seconds) != JIM_OK) {
5664 return JIM_ERR;
5666 if (argc % 2 == 0) {
5667 return -1;
5669 if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
5670 return JIM_ERR;
5673 t = seconds;
5674 tm = options.gmt ? gmtime(&t) : localtime(&t);
5676 if (tm == NULL || strftime(buf, sizeof(buf), options.format, tm) == 0) {
5677 Jim_SetResultString(interp, "format string too long or invalid time", -1);
5678 return JIM_ERR;
5681 Jim_SetResultString(interp, buf, -1);
5683 return JIM_OK;
5686 #ifdef HAVE_STRPTIME
5687 static time_t jim_timegm(const struct tm *tm)
5689 int m = tm->tm_mon + 1;
5690 int y = 1900 + tm->tm_year - (m <= 2);
5691 int era = (y >= 0 ? y : y - 399) / 400;
5692 unsigned yoe = (unsigned)(y - era * 400);
5693 unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + tm->tm_mday - 1;
5694 unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
5695 long days = (era * 146097 + (int)doe - 719468);
5696 int secs = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
5698 return days * 24 * 60 * 60 + secs;
5701 static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5703 char *pt;
5704 struct tm tm;
5705 time_t now = time(NULL);
5707 struct clock_options options = { 0, NULL };
5709 if (argc % 2 == 0) {
5710 return -1;
5713 if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
5714 return JIM_ERR;
5716 if (options.format == NULL) {
5717 return -1;
5720 localtime_r(&now, &tm);
5722 pt = strptime(Jim_String(argv[0]), options.format, &tm);
5723 if (pt == 0 || *pt != 0) {
5724 Jim_SetResultString(interp, "Failed to parse time according to format", -1);
5725 return JIM_ERR;
5729 Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm));
5731 return JIM_OK;
5733 #endif
5735 static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5737 Jim_SetResultInt(interp, time(NULL));
5739 return JIM_OK;
5742 static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5744 struct timeval tv;
5746 gettimeofday(&tv, NULL);
5748 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec);
5750 return JIM_OK;
5753 static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5755 struct timeval tv;
5757 gettimeofday(&tv, NULL);
5759 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000 + tv.tv_usec / 1000);
5761 return JIM_OK;
5764 static const jim_subcmd_type clock_command_table[] = {
5765 { "clicks",
5766 NULL,
5767 clock_cmd_micros,
5772 { "format",
5773 "seconds ?-format string? ?-gmt boolean?",
5774 clock_cmd_format,
5779 { "microseconds",
5780 NULL,
5781 clock_cmd_micros,
5786 { "milliseconds",
5787 NULL,
5788 clock_cmd_millis,
5793 #ifdef HAVE_STRPTIME
5794 { "scan",
5795 "str -format format ?-gmt boolean?",
5796 clock_cmd_scan,
5801 #endif
5802 { "seconds",
5803 NULL,
5804 clock_cmd_seconds,
5809 { NULL }
5812 int Jim_clockInit(Jim_Interp *interp)
5814 if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG))
5815 return JIM_ERR;
5817 Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
5818 return JIM_OK;
5821 #include <limits.h>
5822 #include <stdlib.h>
5823 #include <string.h>
5824 #include <stdio.h>
5825 #include <errno.h>
5828 static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5831 Jim_Obj *dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
5832 Jim_SetResultInt(interp, dictObj && Jim_DictSize(interp, dictObj) != -1);
5833 return JIM_OK;
5836 static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5838 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5839 Jim_Obj *patternObj;
5841 if (!objPtr) {
5842 return JIM_OK;
5845 patternObj = (argc == 1) ? NULL : argv[1];
5848 if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) {
5849 if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) {
5851 Jim_SetResult(interp, objPtr);
5852 return JIM_OK;
5856 return Jim_DictMatchTypes(interp, objPtr, patternObj, JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS | JIM_DICTMATCH_VALUES);
5859 static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5861 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5863 if (!objPtr) {
5864 return JIM_OK;
5867 return Jim_DictMatchTypes(interp, objPtr, argc == 1 ? NULL : argv[1], JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS);
5870 static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5872 int i;
5873 int len;
5874 Jim_Obj *resultObj;
5875 Jim_Obj *objPtr;
5876 Jim_Obj **dictValuesObj;
5878 if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
5880 Jim_UnsetVariable(interp, argv[0], JIM_NONE);
5881 return JIM_OK;
5884 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5886 if (objPtr == NULL) {
5888 return JIM_OK;
5891 if (Jim_DictPairs(interp, objPtr, &dictValuesObj, &len) != JIM_OK) {
5893 Jim_SetResultString(interp, "", -1);
5894 return JIM_OK;
5898 resultObj = Jim_NewDictObj(interp, NULL, 0);
5900 for (i = 0; i < len; i += 2) {
5901 if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
5902 Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
5905 Jim_Free(dictValuesObj);
5907 Jim_SetVariable(interp, argv[0], resultObj);
5908 return JIM_OK;
5911 static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5913 Jim_Obj *objPtr;
5914 int len = 0;
5917 objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5918 if (objPtr) {
5919 len = Jim_DictSize(interp, objPtr);
5920 if (len < 0) {
5922 Jim_SetResultInt(interp, 0);
5923 return JIM_OK;
5927 Jim_SetResultInt(interp, len);
5929 return JIM_OK;
5932 static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5934 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
5935 if (objPtr) {
5936 return Jim_DictInfo(interp, objPtr);
5938 Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL);
5939 return JIM_ERR;
5942 static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
5944 int i;
5945 int len;
5946 Jim_Obj *listObj = argv[1];
5947 Jim_Obj *dictObj;
5949 len = Jim_ListLength(interp, listObj);
5950 if (len % 2) {
5951 Jim_SetResultString(interp, "list must have an even number of elements", -1);
5952 return JIM_ERR;
5955 dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
5956 if (!dictObj) {
5958 return Jim_SetVariable(interp, argv[0], listObj);
5960 else if (Jim_DictSize(interp, dictObj) < 0) {
5961 return JIM_ERR;
5964 if (Jim_IsShared(dictObj)) {
5965 dictObj = Jim_DuplicateObj(interp, dictObj);
5968 for (i = 0; i < len; i += 2) {
5969 Jim_Obj *nameObj;
5970 Jim_Obj *valueObj;
5972 Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE);
5973 Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE);
5975 Jim_DictAddElement(interp, dictObj, nameObj, valueObj);
5977 return Jim_SetVariable(interp, argv[0], dictObj);
5980 static const jim_subcmd_type array_command_table[] = {
5981 { "exists",
5982 "arrayName",
5983 array_cmd_exists,
5988 { "get",
5989 "arrayName ?pattern?",
5990 array_cmd_get,
5995 { "names",
5996 "arrayName ?pattern?",
5997 array_cmd_names,
6002 { "set",
6003 "arrayName list",
6004 array_cmd_set,
6009 { "size",
6010 "arrayName",
6011 array_cmd_size,
6016 { "stat",
6017 "arrayName",
6018 array_cmd_stat,
6023 { "unset",
6024 "arrayName ?pattern?",
6025 array_cmd_unset,
6030 { NULL
6034 int Jim_arrayInit(Jim_Interp *interp)
6036 if (Jim_PackageProvide(interp, "array", "1.0", JIM_ERRMSG))
6037 return JIM_ERR;
6039 Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
6040 return JIM_OK;
6042 int Jim_InitStaticExtensions(Jim_Interp *interp)
6044 extern int Jim_bootstrapInit(Jim_Interp *);
6045 extern int Jim_aioInit(Jim_Interp *);
6046 extern int Jim_readdirInit(Jim_Interp *);
6047 extern int Jim_regexpInit(Jim_Interp *);
6048 extern int Jim_fileInit(Jim_Interp *);
6049 extern int Jim_globInit(Jim_Interp *);
6050 extern int Jim_execInit(Jim_Interp *);
6051 extern int Jim_clockInit(Jim_Interp *);
6052 extern int Jim_arrayInit(Jim_Interp *);
6053 extern int Jim_stdlibInit(Jim_Interp *);
6054 extern int Jim_tclcompatInit(Jim_Interp *);
6055 Jim_bootstrapInit(interp);
6056 Jim_aioInit(interp);
6057 Jim_readdirInit(interp);
6058 Jim_regexpInit(interp);
6059 Jim_fileInit(interp);
6060 Jim_globInit(interp);
6061 Jim_execInit(interp);
6062 Jim_clockInit(interp);
6063 Jim_arrayInit(interp);
6064 Jim_stdlibInit(interp);
6065 Jim_tclcompatInit(interp);
6066 return JIM_OK;
6068 #define JIM_OPTIMIZATION
6069 #ifndef _GNU_SOURCE
6070 #define _GNU_SOURCE
6071 #endif
6073 #include <stdio.h>
6074 #include <stdlib.h>
6076 #include <string.h>
6077 #include <stdarg.h>
6078 #include <ctype.h>
6079 #include <limits.h>
6080 #include <assert.h>
6081 #include <errno.h>
6082 #include <time.h>
6083 #include <setjmp.h>
6086 #ifdef HAVE_SYS_TIME_H
6087 #include <sys/time.h>
6088 #endif
6089 #ifdef HAVE_BACKTRACE
6090 #include <execinfo.h>
6091 #endif
6092 #ifdef HAVE_CRT_EXTERNS_H
6093 #include <crt_externs.h>
6094 #endif
6097 #include <math.h>
6103 #ifndef TCL_LIBRARY
6104 #define TCL_LIBRARY "."
6105 #endif
6106 #ifndef TCL_PLATFORM_OS
6107 #define TCL_PLATFORM_OS "unknown"
6108 #endif
6109 #ifndef TCL_PLATFORM_PLATFORM
6110 #define TCL_PLATFORM_PLATFORM "unknown"
6111 #endif
6112 #ifndef TCL_PLATFORM_PATH_SEPARATOR
6113 #define TCL_PLATFORM_PATH_SEPARATOR ":"
6114 #endif
6122 #ifdef JIM_MAINTAINER
6123 #define JIM_DEBUG_COMMAND
6124 #define JIM_DEBUG_PANIC
6125 #endif
6129 #define JIM_INTEGER_SPACE 24
6131 const char *jim_tt_name(int type);
6133 #ifdef JIM_DEBUG_PANIC
6134 static void JimPanicDump(int fail_condition, const char *fmt, ...);
6135 #define JimPanic(X) JimPanicDump X
6136 #else
6137 #define JimPanic(X)
6138 #endif
6140 #ifdef JIM_OPTIMIZATION
6141 #define JIM_IF_OPTIM(X) X
6142 #else
6143 #define JIM_IF_OPTIM(X)
6144 #endif
6147 static char JimEmptyStringRep[] = "";
6149 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
6150 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
6151 int flags);
6152 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
6153 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
6154 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
6155 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len);
6156 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
6157 const char *prefix, const char *const *tablePtr, const char *name);
6158 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
6159 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
6160 static int JimSign(jim_wide w);
6161 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr);
6162 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
6163 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
6167 #define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
6169 #define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none")
6171 static int utf8_tounicode_case(const char *s, int *uc, int upper)
6173 int l = utf8_tounicode(s, uc);
6174 if (upper) {
6175 *uc = utf8_upper(*uc);
6177 return l;
6181 #define JIM_CHARSET_SCAN 2
6182 #define JIM_CHARSET_GLOB 0
6184 static const char *JimCharsetMatch(const char *pattern, int c, int flags)
6186 int not = 0;
6187 int pchar;
6188 int match = 0;
6189 int nocase = 0;
6191 if (flags & JIM_NOCASE) {
6192 nocase++;
6193 c = utf8_upper(c);
6196 if (flags & JIM_CHARSET_SCAN) {
6197 if (*pattern == '^') {
6198 not++;
6199 pattern++;
6203 if (*pattern == ']') {
6204 goto first;
6208 while (*pattern && *pattern != ']') {
6210 if (pattern[0] == '\\') {
6211 first:
6212 pattern += utf8_tounicode_case(pattern, &pchar, nocase);
6214 else {
6216 int start;
6217 int end;
6219 pattern += utf8_tounicode_case(pattern, &start, nocase);
6220 if (pattern[0] == '-' && pattern[1]) {
6222 pattern++;
6223 pattern += utf8_tounicode_case(pattern, &end, nocase);
6226 if ((c >= start && c <= end) || (c >= end && c <= start)) {
6227 match = 1;
6229 continue;
6231 pchar = start;
6234 if (pchar == c) {
6235 match = 1;
6238 if (not) {
6239 match = !match;
6242 return match ? pattern : NULL;
6247 static int JimGlobMatch(const char *pattern, const char *string, int nocase)
6249 int c;
6250 int pchar;
6251 while (*pattern) {
6252 switch (pattern[0]) {
6253 case '*':
6254 while (pattern[1] == '*') {
6255 pattern++;
6257 pattern++;
6258 if (!pattern[0]) {
6259 return 1;
6261 while (*string) {
6263 if (JimGlobMatch(pattern, string, nocase))
6264 return 1;
6265 string += utf8_tounicode(string, &c);
6267 return 0;
6269 case '?':
6270 string += utf8_tounicode(string, &c);
6271 break;
6273 case '[': {
6274 string += utf8_tounicode(string, &c);
6275 pattern = JimCharsetMatch(pattern + 1, c, nocase ? JIM_NOCASE : 0);
6276 if (!pattern) {
6277 return 0;
6279 if (!*pattern) {
6281 continue;
6283 break;
6285 case '\\':
6286 if (pattern[1]) {
6287 pattern++;
6290 default:
6291 string += utf8_tounicode_case(string, &c, nocase);
6292 utf8_tounicode_case(pattern, &pchar, nocase);
6293 if (pchar != c) {
6294 return 0;
6296 break;
6298 pattern += utf8_tounicode_case(pattern, &pchar, nocase);
6299 if (!*string) {
6300 while (*pattern == '*') {
6301 pattern++;
6303 break;
6306 if (!*pattern && !*string) {
6307 return 1;
6309 return 0;
6312 static int JimStringCompare(const char *s1, int l1, const char *s2, int l2)
6314 if (l1 < l2) {
6315 return memcmp(s1, s2, l1) <= 0 ? -1 : 1;
6317 else if (l2 < l1) {
6318 return memcmp(s1, s2, l2) >= 0 ? 1 : -1;
6320 else {
6321 return JimSign(memcmp(s1, s2, l1));
6325 static int JimStringCompareLen(const char *s1, const char *s2, int maxchars, int nocase)
6327 while (*s1 && *s2 && maxchars) {
6328 int c1, c2;
6329 s1 += utf8_tounicode_case(s1, &c1, nocase);
6330 s2 += utf8_tounicode_case(s2, &c2, nocase);
6331 if (c1 != c2) {
6332 return JimSign(c1 - c2);
6334 maxchars--;
6336 if (!maxchars) {
6337 return 0;
6340 if (*s1) {
6341 return 1;
6343 if (*s2) {
6344 return -1;
6346 return 0;
6349 static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
6351 int i;
6352 int l1bytelen;
6354 if (!l1 || !l2 || l1 > l2) {
6355 return -1;
6357 if (idx < 0)
6358 idx = 0;
6359 s2 += utf8_index(s2, idx);
6361 l1bytelen = utf8_index(s1, l1);
6363 for (i = idx; i <= l2 - l1; i++) {
6364 int c;
6365 if (memcmp(s2, s1, l1bytelen) == 0) {
6366 return i;
6368 s2 += utf8_tounicode(s2, &c);
6370 return -1;
6373 static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
6375 const char *p;
6377 if (!l1 || !l2 || l1 > l2)
6378 return -1;
6381 for (p = s2 + l2 - 1; p != s2 - 1; p--) {
6382 if (*p == *s1 && memcmp(s1, p, l1) == 0) {
6383 return p - s2;
6386 return -1;
6389 #ifdef JIM_UTF8
6390 static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2)
6392 int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2));
6393 if (n > 0) {
6394 n = utf8_strlen(s2, n);
6396 return n;
6398 #endif
6400 static int JimCheckConversion(const char *str, const char *endptr)
6402 if (str[0] == '\0' || str == endptr) {
6403 return JIM_ERR;
6406 if (endptr[0] != '\0') {
6407 while (*endptr) {
6408 if (!isspace(UCHAR(*endptr))) {
6409 return JIM_ERR;
6411 endptr++;
6414 return JIM_OK;
6417 static int JimNumberBase(const char *str, int *base, int *sign)
6419 int i = 0;
6421 *base = 10;
6423 while (isspace(UCHAR(str[i]))) {
6424 i++;
6427 if (str[i] == '-') {
6428 *sign = -1;
6429 i++;
6431 else {
6432 if (str[i] == '+') {
6433 i++;
6435 *sign = 1;
6438 if (str[i] != '0') {
6440 return 0;
6444 switch (str[i + 1]) {
6445 case 'x': case 'X': *base = 16; break;
6446 case 'o': case 'O': *base = 8; break;
6447 case 'b': case 'B': *base = 2; break;
6448 default: return 0;
6450 i += 2;
6452 if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
6454 return i;
6457 *base = 10;
6458 return 0;
6461 static long jim_strtol(const char *str, char **endptr)
6463 int sign;
6464 int base;
6465 int i = JimNumberBase(str, &base, &sign);
6467 if (base != 10) {
6468 long value = strtol(str + i, endptr, base);
6469 if (endptr == NULL || *endptr != str + i) {
6470 return value * sign;
6475 return strtol(str, endptr, 10);
6479 static jim_wide jim_strtoull(const char *str, char **endptr)
6481 #ifdef HAVE_LONG_LONG
6482 int sign;
6483 int base;
6484 int i = JimNumberBase(str, &base, &sign);
6486 if (base != 10) {
6487 jim_wide value = strtoull(str + i, endptr, base);
6488 if (endptr == NULL || *endptr != str + i) {
6489 return value * sign;
6494 return strtoull(str, endptr, 10);
6495 #else
6496 return (unsigned long)jim_strtol(str, endptr);
6497 #endif
6500 int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
6502 char *endptr;
6504 if (base) {
6505 *widePtr = strtoull(str, &endptr, base);
6507 else {
6508 *widePtr = jim_strtoull(str, &endptr);
6511 return JimCheckConversion(str, endptr);
6514 int Jim_StringToDouble(const char *str, double *doublePtr)
6516 char *endptr;
6519 errno = 0;
6521 *doublePtr = strtod(str, &endptr);
6523 return JimCheckConversion(str, endptr);
6526 static jim_wide JimPowWide(jim_wide b, jim_wide e)
6528 jim_wide res = 1;
6531 if (b == 1) {
6533 return 1;
6535 if (e < 0) {
6536 if (b != -1) {
6537 return 0;
6539 e = -e;
6541 while (e)
6543 if (e & 1) {
6544 res *= b;
6546 e >>= 1;
6547 b *= b;
6549 return res;
6552 #ifdef JIM_DEBUG_PANIC
6553 static void JimPanicDump(int condition, const char *fmt, ...)
6555 va_list ap;
6557 if (!condition) {
6558 return;
6561 va_start(ap, fmt);
6563 fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
6564 vfprintf(stderr, fmt, ap);
6565 fprintf(stderr, "\n\n");
6566 va_end(ap);
6568 #ifdef HAVE_BACKTRACE
6570 void *array[40];
6571 int size, i;
6572 char **strings;
6574 size = backtrace(array, 40);
6575 strings = backtrace_symbols(array, size);
6576 for (i = 0; i < size; i++)
6577 fprintf(stderr, "[backtrace] %s\n", strings[i]);
6578 fprintf(stderr, "[backtrace] Include the above lines and the output\n");
6579 fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n");
6581 #endif
6583 exit(1);
6585 #endif
6588 void *Jim_Alloc(int size)
6590 return size ? malloc(size) : NULL;
6593 void Jim_Free(void *ptr)
6595 free(ptr);
6598 void *Jim_Realloc(void *ptr, int size)
6600 return realloc(ptr, size);
6603 char *Jim_StrDup(const char *s)
6605 return strdup(s);
6608 char *Jim_StrDupLen(const char *s, int l)
6610 char *copy = Jim_Alloc(l + 1);
6612 memcpy(copy, s, l + 1);
6613 copy[l] = 0;
6614 return copy;
6619 static jim_wide JimClock(void)
6621 struct timeval tv;
6623 gettimeofday(&tv, NULL);
6624 return (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec;
6629 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
6630 static unsigned int JimHashTableNextPower(unsigned int size);
6631 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
6636 unsigned int Jim_IntHashFunction(unsigned int key)
6638 key += ~(key << 15);
6639 key ^= (key >> 10);
6640 key += (key << 3);
6641 key ^= (key >> 6);
6642 key += ~(key << 11);
6643 key ^= (key >> 16);
6644 return key;
6647 unsigned int Jim_GenHashFunction(const unsigned char *buf, int len)
6649 unsigned int h = 0;
6651 while (len--)
6652 h += (h << 3) + *buf++;
6653 return h;
6659 static void JimResetHashTable(Jim_HashTable *ht)
6661 ht->table = NULL;
6662 ht->size = 0;
6663 ht->sizemask = 0;
6664 ht->used = 0;
6665 ht->collisions = 0;
6666 #ifdef JIM_RANDOMISE_HASH
6667 ht->uniq = (rand() ^ time(NULL) ^ clock());
6668 #else
6669 ht->uniq = 0;
6670 #endif
6673 static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
6675 iter->ht = ht;
6676 iter->index = -1;
6677 iter->entry = NULL;
6678 iter->nextEntry = NULL;
6682 int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
6684 JimResetHashTable(ht);
6685 ht->type = type;
6686 ht->privdata = privDataPtr;
6687 return JIM_OK;
6690 void Jim_ResizeHashTable(Jim_HashTable *ht)
6692 int minimal = ht->used;
6694 if (minimal < JIM_HT_INITIAL_SIZE)
6695 minimal = JIM_HT_INITIAL_SIZE;
6696 Jim_ExpandHashTable(ht, minimal);
6700 void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
6702 Jim_HashTable n;
6703 unsigned int realsize = JimHashTableNextPower(size), i;
6705 if (size <= ht->used)
6706 return;
6708 Jim_InitHashTable(&n, ht->type, ht->privdata);
6709 n.size = realsize;
6710 n.sizemask = realsize - 1;
6711 n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
6713 n.uniq = ht->uniq;
6716 memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
6718 n.used = ht->used;
6719 for (i = 0; ht->used > 0; i++) {
6720 Jim_HashEntry *he, *nextHe;
6722 if (ht->table[i] == NULL)
6723 continue;
6726 he = ht->table[i];
6727 while (he) {
6728 unsigned int h;
6730 nextHe = he->next;
6732 h = Jim_HashKey(ht, he->key) & n.sizemask;
6733 he->next = n.table[h];
6734 n.table[h] = he;
6735 ht->used--;
6737 he = nextHe;
6740 assert(ht->used == 0);
6741 Jim_Free(ht->table);
6744 *ht = n;
6748 int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
6750 Jim_HashEntry *entry;
6752 entry = JimInsertHashEntry(ht, key, 0);
6753 if (entry == NULL)
6754 return JIM_ERR;
6757 Jim_SetHashKey(ht, entry, key);
6758 Jim_SetHashVal(ht, entry, val);
6759 return JIM_OK;
6763 int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
6765 int existed;
6766 Jim_HashEntry *entry;
6768 entry = JimInsertHashEntry(ht, key, 1);
6769 if (entry->key) {
6770 if (ht->type->valDestructor && ht->type->valDup) {
6771 void *newval = ht->type->valDup(ht->privdata, val);
6772 ht->type->valDestructor(ht->privdata, entry->u.val);
6773 entry->u.val = newval;
6775 else {
6776 Jim_FreeEntryVal(ht, entry);
6777 Jim_SetHashVal(ht, entry, val);
6779 existed = 1;
6781 else {
6783 Jim_SetHashKey(ht, entry, key);
6784 Jim_SetHashVal(ht, entry, val);
6785 existed = 0;
6788 return existed;
6792 int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
6794 unsigned int h;
6795 Jim_HashEntry *he, *prevHe;
6797 if (ht->used == 0)
6798 return JIM_ERR;
6799 h = Jim_HashKey(ht, key) & ht->sizemask;
6800 he = ht->table[h];
6802 prevHe = NULL;
6803 while (he) {
6804 if (Jim_CompareHashKeys(ht, key, he->key)) {
6806 if (prevHe)
6807 prevHe->next = he->next;
6808 else
6809 ht->table[h] = he->next;
6810 Jim_FreeEntryKey(ht, he);
6811 Jim_FreeEntryVal(ht, he);
6812 Jim_Free(he);
6813 ht->used--;
6814 return JIM_OK;
6816 prevHe = he;
6817 he = he->next;
6819 return JIM_ERR;
6823 int Jim_FreeHashTable(Jim_HashTable *ht)
6825 unsigned int i;
6828 for (i = 0; ht->used > 0; i++) {
6829 Jim_HashEntry *he, *nextHe;
6831 if ((he = ht->table[i]) == NULL)
6832 continue;
6833 while (he) {
6834 nextHe = he->next;
6835 Jim_FreeEntryKey(ht, he);
6836 Jim_FreeEntryVal(ht, he);
6837 Jim_Free(he);
6838 ht->used--;
6839 he = nextHe;
6843 Jim_Free(ht->table);
6845 JimResetHashTable(ht);
6846 return JIM_OK;
6849 Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
6851 Jim_HashEntry *he;
6852 unsigned int h;
6854 if (ht->used == 0)
6855 return NULL;
6856 h = Jim_HashKey(ht, key) & ht->sizemask;
6857 he = ht->table[h];
6858 while (he) {
6859 if (Jim_CompareHashKeys(ht, key, he->key))
6860 return he;
6861 he = he->next;
6863 return NULL;
6866 Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
6868 Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
6869 JimInitHashTableIterator(ht, iter);
6870 return iter;
6873 Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
6875 while (1) {
6876 if (iter->entry == NULL) {
6877 iter->index++;
6878 if (iter->index >= (signed)iter->ht->size)
6879 break;
6880 iter->entry = iter->ht->table[iter->index];
6882 else {
6883 iter->entry = iter->nextEntry;
6885 if (iter->entry) {
6886 iter->nextEntry = iter->entry->next;
6887 return iter->entry;
6890 return NULL;
6896 static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
6898 if (ht->size == 0)
6899 Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
6900 if (ht->size == ht->used)
6901 Jim_ExpandHashTable(ht, ht->size * 2);
6905 static unsigned int JimHashTableNextPower(unsigned int size)
6907 unsigned int i = JIM_HT_INITIAL_SIZE;
6909 if (size >= 2147483648U)
6910 return 2147483648U;
6911 while (1) {
6912 if (i >= size)
6913 return i;
6914 i *= 2;
6918 static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
6920 unsigned int h;
6921 Jim_HashEntry *he;
6924 JimExpandHashTableIfNeeded(ht);
6927 h = Jim_HashKey(ht, key) & ht->sizemask;
6929 he = ht->table[h];
6930 while (he) {
6931 if (Jim_CompareHashKeys(ht, key, he->key))
6932 return replace ? he : NULL;
6933 he = he->next;
6937 he = Jim_Alloc(sizeof(*he));
6938 he->next = ht->table[h];
6939 ht->table[h] = he;
6940 ht->used++;
6941 he->key = NULL;
6943 return he;
6948 static unsigned int JimStringCopyHTHashFunction(const void *key)
6950 return Jim_GenHashFunction(key, strlen(key));
6953 static void *JimStringCopyHTDup(void *privdata, const void *key)
6955 return Jim_StrDup(key);
6958 static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
6960 return strcmp(key1, key2) == 0;
6963 static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
6965 Jim_Free(key);
6968 static const Jim_HashTableType JimPackageHashTableType = {
6969 JimStringCopyHTHashFunction,
6970 JimStringCopyHTDup,
6971 NULL,
6972 JimStringCopyHTKeyCompare,
6973 JimStringCopyHTKeyDestructor,
6974 NULL
6977 typedef struct AssocDataValue
6979 Jim_InterpDeleteProc *delProc;
6980 void *data;
6981 } AssocDataValue;
6983 static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
6985 AssocDataValue *assocPtr = (AssocDataValue *) data;
6987 if (assocPtr->delProc != NULL)
6988 assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
6989 Jim_Free(data);
6992 static const Jim_HashTableType JimAssocDataHashTableType = {
6993 JimStringCopyHTHashFunction,
6994 JimStringCopyHTDup,
6995 NULL,
6996 JimStringCopyHTKeyCompare,
6997 JimStringCopyHTKeyDestructor,
6998 JimAssocDataHashTableValueDestructor
7001 void Jim_InitStack(Jim_Stack *stack)
7003 stack->len = 0;
7004 stack->maxlen = 0;
7005 stack->vector = NULL;
7008 void Jim_FreeStack(Jim_Stack *stack)
7010 Jim_Free(stack->vector);
7013 int Jim_StackLen(Jim_Stack *stack)
7015 return stack->len;
7018 void Jim_StackPush(Jim_Stack *stack, void *element)
7020 int neededLen = stack->len + 1;
7022 if (neededLen > stack->maxlen) {
7023 stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
7024 stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
7026 stack->vector[stack->len] = element;
7027 stack->len++;
7030 void *Jim_StackPop(Jim_Stack *stack)
7032 if (stack->len == 0)
7033 return NULL;
7034 stack->len--;
7035 return stack->vector[stack->len];
7038 void *Jim_StackPeek(Jim_Stack *stack)
7040 if (stack->len == 0)
7041 return NULL;
7042 return stack->vector[stack->len - 1];
7045 void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
7047 int i;
7049 for (i = 0; i < stack->len; i++)
7050 freeFunc(stack->vector[i]);
7055 #define JIM_TT_NONE 0
7056 #define JIM_TT_STR 1
7057 #define JIM_TT_ESC 2
7058 #define JIM_TT_VAR 3
7059 #define JIM_TT_DICTSUGAR 4
7060 #define JIM_TT_CMD 5
7062 #define JIM_TT_SEP 6
7063 #define JIM_TT_EOL 7
7064 #define JIM_TT_EOF 8
7066 #define JIM_TT_LINE 9
7067 #define JIM_TT_WORD 10
7070 #define JIM_TT_SUBEXPR_START 11
7071 #define JIM_TT_SUBEXPR_END 12
7072 #define JIM_TT_SUBEXPR_COMMA 13
7073 #define JIM_TT_EXPR_INT 14
7074 #define JIM_TT_EXPR_DOUBLE 15
7075 #define JIM_TT_EXPR_BOOLEAN 16
7077 #define JIM_TT_EXPRSUGAR 17
7080 #define JIM_TT_EXPR_OP 20
7082 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
7084 #define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA)
7086 #define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP)
7088 struct JimParseMissing {
7089 int ch;
7090 int line;
7093 struct JimParserCtx
7095 const char *p;
7096 int len;
7097 int linenr;
7098 const char *tstart;
7099 const char *tend;
7100 int tline;
7101 int tt;
7102 int eof;
7103 int inquote;
7104 int comment;
7105 struct JimParseMissing missing;
7108 static int JimParseScript(struct JimParserCtx *pc);
7109 static int JimParseSep(struct JimParserCtx *pc);
7110 static int JimParseEol(struct JimParserCtx *pc);
7111 static int JimParseCmd(struct JimParserCtx *pc);
7112 static int JimParseQuote(struct JimParserCtx *pc);
7113 static int JimParseVar(struct JimParserCtx *pc);
7114 static int JimParseBrace(struct JimParserCtx *pc);
7115 static int JimParseStr(struct JimParserCtx *pc);
7116 static int JimParseComment(struct JimParserCtx *pc);
7117 static void JimParseSubCmd(struct JimParserCtx *pc);
7118 static int JimParseSubQuote(struct JimParserCtx *pc);
7119 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
7121 static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
7123 pc->p = prg;
7124 pc->len = len;
7125 pc->tstart = NULL;
7126 pc->tend = NULL;
7127 pc->tline = 0;
7128 pc->tt = JIM_TT_NONE;
7129 pc->eof = 0;
7130 pc->inquote = 0;
7131 pc->linenr = linenr;
7132 pc->comment = 1;
7133 pc->missing.ch = ' ';
7134 pc->missing.line = linenr;
7137 static int JimParseScript(struct JimParserCtx *pc)
7139 while (1) {
7140 if (!pc->len) {
7141 pc->tstart = pc->p;
7142 pc->tend = pc->p - 1;
7143 pc->tline = pc->linenr;
7144 pc->tt = JIM_TT_EOL;
7145 pc->eof = 1;
7146 return JIM_OK;
7148 switch (*(pc->p)) {
7149 case '\\':
7150 if (*(pc->p + 1) == '\n' && !pc->inquote) {
7151 return JimParseSep(pc);
7153 pc->comment = 0;
7154 return JimParseStr(pc);
7155 case ' ':
7156 case '\t':
7157 case '\r':
7158 case '\f':
7159 if (!pc->inquote)
7160 return JimParseSep(pc);
7161 pc->comment = 0;
7162 return JimParseStr(pc);
7163 case '\n':
7164 case ';':
7165 pc->comment = 1;
7166 if (!pc->inquote)
7167 return JimParseEol(pc);
7168 return JimParseStr(pc);
7169 case '[':
7170 pc->comment = 0;
7171 return JimParseCmd(pc);
7172 case '$':
7173 pc->comment = 0;
7174 if (JimParseVar(pc) == JIM_ERR) {
7176 pc->tstart = pc->tend = pc->p++;
7177 pc->len--;
7178 pc->tt = JIM_TT_ESC;
7180 return JIM_OK;
7181 case '#':
7182 if (pc->comment) {
7183 JimParseComment(pc);
7184 continue;
7186 return JimParseStr(pc);
7187 default:
7188 pc->comment = 0;
7189 return JimParseStr(pc);
7191 return JIM_OK;
7195 static int JimParseSep(struct JimParserCtx *pc)
7197 pc->tstart = pc->p;
7198 pc->tline = pc->linenr;
7199 while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
7200 if (*pc->p == '\n') {
7201 break;
7203 if (*pc->p == '\\') {
7204 pc->p++;
7205 pc->len--;
7206 pc->linenr++;
7208 pc->p++;
7209 pc->len--;
7211 pc->tend = pc->p - 1;
7212 pc->tt = JIM_TT_SEP;
7213 return JIM_OK;
7216 static int JimParseEol(struct JimParserCtx *pc)
7218 pc->tstart = pc->p;
7219 pc->tline = pc->linenr;
7220 while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
7221 if (*pc->p == '\n')
7222 pc->linenr++;
7223 pc->p++;
7224 pc->len--;
7226 pc->tend = pc->p - 1;
7227 pc->tt = JIM_TT_EOL;
7228 return JIM_OK;
7232 static void JimParseSubBrace(struct JimParserCtx *pc)
7234 int level = 1;
7237 pc->p++;
7238 pc->len--;
7239 while (pc->len) {
7240 switch (*pc->p) {
7241 case '\\':
7242 if (pc->len > 1) {
7243 if (*++pc->p == '\n') {
7244 pc->linenr++;
7246 pc->len--;
7248 break;
7250 case '{':
7251 level++;
7252 break;
7254 case '}':
7255 if (--level == 0) {
7256 pc->tend = pc->p - 1;
7257 pc->p++;
7258 pc->len--;
7259 return;
7261 break;
7263 case '\n':
7264 pc->linenr++;
7265 break;
7267 pc->p++;
7268 pc->len--;
7270 pc->missing.ch = '{';
7271 pc->missing.line = pc->tline;
7272 pc->tend = pc->p - 1;
7275 static int JimParseSubQuote(struct JimParserCtx *pc)
7277 int tt = JIM_TT_STR;
7278 int line = pc->tline;
7281 pc->p++;
7282 pc->len--;
7283 while (pc->len) {
7284 switch (*pc->p) {
7285 case '\\':
7286 if (pc->len > 1) {
7287 if (*++pc->p == '\n') {
7288 pc->linenr++;
7290 pc->len--;
7291 tt = JIM_TT_ESC;
7293 break;
7295 case '"':
7296 pc->tend = pc->p - 1;
7297 pc->p++;
7298 pc->len--;
7299 return tt;
7301 case '[':
7302 JimParseSubCmd(pc);
7303 tt = JIM_TT_ESC;
7304 continue;
7306 case '\n':
7307 pc->linenr++;
7308 break;
7310 case '$':
7311 tt = JIM_TT_ESC;
7312 break;
7314 pc->p++;
7315 pc->len--;
7317 pc->missing.ch = '"';
7318 pc->missing.line = line;
7319 pc->tend = pc->p - 1;
7320 return tt;
7323 static void JimParseSubCmd(struct JimParserCtx *pc)
7325 int level = 1;
7326 int startofword = 1;
7327 int line = pc->tline;
7330 pc->p++;
7331 pc->len--;
7332 while (pc->len) {
7333 switch (*pc->p) {
7334 case '\\':
7335 if (pc->len > 1) {
7336 if (*++pc->p == '\n') {
7337 pc->linenr++;
7339 pc->len--;
7341 break;
7343 case '[':
7344 level++;
7345 break;
7347 case ']':
7348 if (--level == 0) {
7349 pc->tend = pc->p - 1;
7350 pc->p++;
7351 pc->len--;
7352 return;
7354 break;
7356 case '"':
7357 if (startofword) {
7358 JimParseSubQuote(pc);
7359 continue;
7361 break;
7363 case '{':
7364 JimParseSubBrace(pc);
7365 startofword = 0;
7366 continue;
7368 case '\n':
7369 pc->linenr++;
7370 break;
7372 startofword = isspace(UCHAR(*pc->p));
7373 pc->p++;
7374 pc->len--;
7376 pc->missing.ch = '[';
7377 pc->missing.line = line;
7378 pc->tend = pc->p - 1;
7381 static int JimParseBrace(struct JimParserCtx *pc)
7383 pc->tstart = pc->p + 1;
7384 pc->tline = pc->linenr;
7385 pc->tt = JIM_TT_STR;
7386 JimParseSubBrace(pc);
7387 return JIM_OK;
7390 static int JimParseCmd(struct JimParserCtx *pc)
7392 pc->tstart = pc->p + 1;
7393 pc->tline = pc->linenr;
7394 pc->tt = JIM_TT_CMD;
7395 JimParseSubCmd(pc);
7396 return JIM_OK;
7399 static int JimParseQuote(struct JimParserCtx *pc)
7401 pc->tstart = pc->p + 1;
7402 pc->tline = pc->linenr;
7403 pc->tt = JimParseSubQuote(pc);
7404 return JIM_OK;
7407 static int JimParseVar(struct JimParserCtx *pc)
7410 pc->p++;
7411 pc->len--;
7413 #ifdef EXPRSUGAR_BRACKET
7414 if (*pc->p == '[') {
7416 JimParseCmd(pc);
7417 pc->tt = JIM_TT_EXPRSUGAR;
7418 return JIM_OK;
7420 #endif
7422 pc->tstart = pc->p;
7423 pc->tt = JIM_TT_VAR;
7424 pc->tline = pc->linenr;
7426 if (*pc->p == '{') {
7427 pc->tstart = ++pc->p;
7428 pc->len--;
7430 while (pc->len && *pc->p != '}') {
7431 if (*pc->p == '\n') {
7432 pc->linenr++;
7434 pc->p++;
7435 pc->len--;
7437 pc->tend = pc->p - 1;
7438 if (pc->len) {
7439 pc->p++;
7440 pc->len--;
7443 else {
7444 while (1) {
7446 if (pc->p[0] == ':' && pc->p[1] == ':') {
7447 while (*pc->p == ':') {
7448 pc->p++;
7449 pc->len--;
7451 continue;
7453 if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
7454 pc->p++;
7455 pc->len--;
7456 continue;
7458 break;
7461 if (*pc->p == '(') {
7462 int count = 1;
7463 const char *paren = NULL;
7465 pc->tt = JIM_TT_DICTSUGAR;
7467 while (count && pc->len) {
7468 pc->p++;
7469 pc->len--;
7470 if (*pc->p == '\\' && pc->len >= 1) {
7471 pc->p++;
7472 pc->len--;
7474 else if (*pc->p == '(') {
7475 count++;
7477 else if (*pc->p == ')') {
7478 paren = pc->p;
7479 count--;
7482 if (count == 0) {
7483 pc->p++;
7484 pc->len--;
7486 else if (paren) {
7488 paren++;
7489 pc->len += (pc->p - paren);
7490 pc->p = paren;
7492 #ifndef EXPRSUGAR_BRACKET
7493 if (*pc->tstart == '(') {
7494 pc->tt = JIM_TT_EXPRSUGAR;
7496 #endif
7498 pc->tend = pc->p - 1;
7500 if (pc->tstart == pc->p) {
7501 pc->p--;
7502 pc->len++;
7503 return JIM_ERR;
7505 return JIM_OK;
7508 static int JimParseStr(struct JimParserCtx *pc)
7510 if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
7511 pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
7513 if (*pc->p == '{') {
7514 return JimParseBrace(pc);
7516 if (*pc->p == '"') {
7517 pc->inquote = 1;
7518 pc->p++;
7519 pc->len--;
7521 pc->missing.line = pc->tline;
7524 pc->tstart = pc->p;
7525 pc->tline = pc->linenr;
7526 while (1) {
7527 if (pc->len == 0) {
7528 if (pc->inquote) {
7529 pc->missing.ch = '"';
7531 pc->tend = pc->p - 1;
7532 pc->tt = JIM_TT_ESC;
7533 return JIM_OK;
7535 switch (*pc->p) {
7536 case '\\':
7537 if (!pc->inquote && *(pc->p + 1) == '\n') {
7538 pc->tend = pc->p - 1;
7539 pc->tt = JIM_TT_ESC;
7540 return JIM_OK;
7542 if (pc->len >= 2) {
7543 if (*(pc->p + 1) == '\n') {
7544 pc->linenr++;
7546 pc->p++;
7547 pc->len--;
7549 else if (pc->len == 1) {
7551 pc->missing.ch = '\\';
7553 break;
7554 case '(':
7556 if (pc->len > 1 && pc->p[1] != '$') {
7557 break;
7560 case ')':
7562 if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
7563 if (pc->p == pc->tstart) {
7565 pc->p++;
7566 pc->len--;
7568 pc->tend = pc->p - 1;
7569 pc->tt = JIM_TT_ESC;
7570 return JIM_OK;
7572 break;
7574 case '$':
7575 case '[':
7576 pc->tend = pc->p - 1;
7577 pc->tt = JIM_TT_ESC;
7578 return JIM_OK;
7579 case ' ':
7580 case '\t':
7581 case '\n':
7582 case '\r':
7583 case '\f':
7584 case ';':
7585 if (!pc->inquote) {
7586 pc->tend = pc->p - 1;
7587 pc->tt = JIM_TT_ESC;
7588 return JIM_OK;
7590 else if (*pc->p == '\n') {
7591 pc->linenr++;
7593 break;
7594 case '"':
7595 if (pc->inquote) {
7596 pc->tend = pc->p - 1;
7597 pc->tt = JIM_TT_ESC;
7598 pc->p++;
7599 pc->len--;
7600 pc->inquote = 0;
7601 return JIM_OK;
7603 break;
7605 pc->p++;
7606 pc->len--;
7608 return JIM_OK;
7611 static int JimParseComment(struct JimParserCtx *pc)
7613 while (*pc->p) {
7614 if (*pc->p == '\\') {
7615 pc->p++;
7616 pc->len--;
7617 if (pc->len == 0) {
7618 pc->missing.ch = '\\';
7619 return JIM_OK;
7621 if (*pc->p == '\n') {
7622 pc->linenr++;
7625 else if (*pc->p == '\n') {
7626 pc->p++;
7627 pc->len--;
7628 pc->linenr++;
7629 break;
7631 pc->p++;
7632 pc->len--;
7634 return JIM_OK;
7638 static int xdigitval(int c)
7640 if (c >= '0' && c <= '9')
7641 return c - '0';
7642 if (c >= 'a' && c <= 'f')
7643 return c - 'a' + 10;
7644 if (c >= 'A' && c <= 'F')
7645 return c - 'A' + 10;
7646 return -1;
7649 static int odigitval(int c)
7651 if (c >= '0' && c <= '7')
7652 return c - '0';
7653 return -1;
7656 static int JimEscape(char *dest, const char *s, int slen)
7658 char *p = dest;
7659 int i, len;
7661 for (i = 0; i < slen; i++) {
7662 switch (s[i]) {
7663 case '\\':
7664 switch (s[i + 1]) {
7665 case 'a':
7666 *p++ = 0x7;
7667 i++;
7668 break;
7669 case 'b':
7670 *p++ = 0x8;
7671 i++;
7672 break;
7673 case 'f':
7674 *p++ = 0xc;
7675 i++;
7676 break;
7677 case 'n':
7678 *p++ = 0xa;
7679 i++;
7680 break;
7681 case 'r':
7682 *p++ = 0xd;
7683 i++;
7684 break;
7685 case 't':
7686 *p++ = 0x9;
7687 i++;
7688 break;
7689 case 'u':
7690 case 'U':
7691 case 'x':
7693 unsigned val = 0;
7694 int k;
7695 int maxchars = 2;
7697 i++;
7699 if (s[i] == 'U') {
7700 maxchars = 8;
7702 else if (s[i] == 'u') {
7703 if (s[i + 1] == '{') {
7704 maxchars = 6;
7705 i++;
7707 else {
7708 maxchars = 4;
7712 for (k = 0; k < maxchars; k++) {
7713 int c = xdigitval(s[i + k + 1]);
7714 if (c == -1) {
7715 break;
7717 val = (val << 4) | c;
7720 if (s[i] == '{') {
7721 if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
7723 i--;
7724 k = 0;
7726 else {
7728 k++;
7731 if (k) {
7733 if (s[i] == 'x') {
7734 *p++ = val;
7736 else {
7737 p += utf8_fromunicode(p, val);
7739 i += k;
7740 break;
7743 *p++ = s[i];
7745 break;
7746 case 'v':
7747 *p++ = 0xb;
7748 i++;
7749 break;
7750 case '\0':
7751 *p++ = '\\';
7752 i++;
7753 break;
7754 case '\n':
7756 *p++ = ' ';
7757 do {
7758 i++;
7759 } while (s[i + 1] == ' ' || s[i + 1] == '\t');
7760 break;
7761 case '0':
7762 case '1':
7763 case '2':
7764 case '3':
7765 case '4':
7766 case '5':
7767 case '6':
7768 case '7':
7771 int val = 0;
7772 int c = odigitval(s[i + 1]);
7774 val = c;
7775 c = odigitval(s[i + 2]);
7776 if (c == -1) {
7777 *p++ = val;
7778 i++;
7779 break;
7781 val = (val * 8) + c;
7782 c = odigitval(s[i + 3]);
7783 if (c == -1) {
7784 *p++ = val;
7785 i += 2;
7786 break;
7788 val = (val * 8) + c;
7789 *p++ = val;
7790 i += 3;
7792 break;
7793 default:
7794 *p++ = s[i + 1];
7795 i++;
7796 break;
7798 break;
7799 default:
7800 *p++ = s[i];
7801 break;
7804 len = p - dest;
7805 *p = '\0';
7806 return len;
7809 static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
7811 const char *start, *end;
7812 char *token;
7813 int len;
7815 start = pc->tstart;
7816 end = pc->tend;
7817 len = (end - start) + 1;
7818 if (len < 0) {
7819 len = 0;
7821 token = Jim_Alloc(len + 1);
7822 if (pc->tt != JIM_TT_ESC) {
7824 memcpy(token, start, len);
7825 token[len] = '\0';
7827 else {
7829 len = JimEscape(token, start, len);
7832 return Jim_NewStringObjNoAlloc(interp, token, len);
7835 static int JimParseListSep(struct JimParserCtx *pc);
7836 static int JimParseListStr(struct JimParserCtx *pc);
7837 static int JimParseListQuote(struct JimParserCtx *pc);
7839 static int JimParseList(struct JimParserCtx *pc)
7841 if (isspace(UCHAR(*pc->p))) {
7842 return JimParseListSep(pc);
7844 switch (*pc->p) {
7845 case '"':
7846 return JimParseListQuote(pc);
7848 case '{':
7849 return JimParseBrace(pc);
7851 default:
7852 if (pc->len) {
7853 return JimParseListStr(pc);
7855 break;
7858 pc->tstart = pc->tend = pc->p;
7859 pc->tline = pc->linenr;
7860 pc->tt = JIM_TT_EOL;
7861 pc->eof = 1;
7862 return JIM_OK;
7865 static int JimParseListSep(struct JimParserCtx *pc)
7867 pc->tstart = pc->p;
7868 pc->tline = pc->linenr;
7869 while (isspace(UCHAR(*pc->p))) {
7870 if (*pc->p == '\n') {
7871 pc->linenr++;
7873 pc->p++;
7874 pc->len--;
7876 pc->tend = pc->p - 1;
7877 pc->tt = JIM_TT_SEP;
7878 return JIM_OK;
7881 static int JimParseListQuote(struct JimParserCtx *pc)
7883 pc->p++;
7884 pc->len--;
7886 pc->tstart = pc->p;
7887 pc->tline = pc->linenr;
7888 pc->tt = JIM_TT_STR;
7890 while (pc->len) {
7891 switch (*pc->p) {
7892 case '\\':
7893 pc->tt = JIM_TT_ESC;
7894 if (--pc->len == 0) {
7896 pc->tend = pc->p;
7897 return JIM_OK;
7899 pc->p++;
7900 break;
7901 case '\n':
7902 pc->linenr++;
7903 break;
7904 case '"':
7905 pc->tend = pc->p - 1;
7906 pc->p++;
7907 pc->len--;
7908 return JIM_OK;
7910 pc->p++;
7911 pc->len--;
7914 pc->tend = pc->p - 1;
7915 return JIM_OK;
7918 static int JimParseListStr(struct JimParserCtx *pc)
7920 pc->tstart = pc->p;
7921 pc->tline = pc->linenr;
7922 pc->tt = JIM_TT_STR;
7924 while (pc->len) {
7925 if (isspace(UCHAR(*pc->p))) {
7926 pc->tend = pc->p - 1;
7927 return JIM_OK;
7929 if (*pc->p == '\\') {
7930 if (--pc->len == 0) {
7932 pc->tend = pc->p;
7933 return JIM_OK;
7935 pc->tt = JIM_TT_ESC;
7936 pc->p++;
7938 pc->p++;
7939 pc->len--;
7941 pc->tend = pc->p - 1;
7942 return JIM_OK;
7947 Jim_Obj *Jim_NewObj(Jim_Interp *interp)
7949 Jim_Obj *objPtr;
7952 if (interp->freeList != NULL) {
7954 objPtr = interp->freeList;
7955 interp->freeList = objPtr->nextObjPtr;
7957 else {
7959 objPtr = Jim_Alloc(sizeof(*objPtr));
7962 objPtr->refCount = 0;
7965 objPtr->prevObjPtr = NULL;
7966 objPtr->nextObjPtr = interp->liveList;
7967 if (interp->liveList)
7968 interp->liveList->prevObjPtr = objPtr;
7969 interp->liveList = objPtr;
7971 return objPtr;
7974 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
7977 JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
7978 objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
7981 Jim_FreeIntRep(interp, objPtr);
7983 if (objPtr->bytes != NULL) {
7984 if (objPtr->bytes != JimEmptyStringRep)
7985 Jim_Free(objPtr->bytes);
7988 if (objPtr->prevObjPtr)
7989 objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
7990 if (objPtr->nextObjPtr)
7991 objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
7992 if (interp->liveList == objPtr)
7993 interp->liveList = objPtr->nextObjPtr;
7994 #ifdef JIM_DISABLE_OBJECT_POOL
7995 Jim_Free(objPtr);
7996 #else
7998 objPtr->prevObjPtr = NULL;
7999 objPtr->nextObjPtr = interp->freeList;
8000 if (interp->freeList)
8001 interp->freeList->prevObjPtr = objPtr;
8002 interp->freeList = objPtr;
8003 objPtr->refCount = -1;
8004 #endif
8008 void Jim_InvalidateStringRep(Jim_Obj *objPtr)
8010 if (objPtr->bytes != NULL) {
8011 if (objPtr->bytes != JimEmptyStringRep)
8012 Jim_Free(objPtr->bytes);
8014 objPtr->bytes = NULL;
8018 Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
8020 Jim_Obj *dupPtr;
8022 dupPtr = Jim_NewObj(interp);
8023 if (objPtr->bytes == NULL) {
8025 dupPtr->bytes = NULL;
8027 else if (objPtr->length == 0) {
8028 dupPtr->bytes = JimEmptyStringRep;
8029 dupPtr->length = 0;
8030 dupPtr->typePtr = NULL;
8031 return dupPtr;
8033 else {
8034 dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
8035 dupPtr->length = objPtr->length;
8037 memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
8041 dupPtr->typePtr = objPtr->typePtr;
8042 if (objPtr->typePtr != NULL) {
8043 if (objPtr->typePtr->dupIntRepProc == NULL) {
8044 dupPtr->internalRep = objPtr->internalRep;
8046 else {
8048 objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
8051 return dupPtr;
8054 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
8056 if (objPtr->bytes == NULL) {
8058 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8059 objPtr->typePtr->updateStringProc(objPtr);
8061 if (lenPtr)
8062 *lenPtr = objPtr->length;
8063 return objPtr->bytes;
8067 int Jim_Length(Jim_Obj *objPtr)
8069 if (objPtr->bytes == NULL) {
8071 Jim_GetString(objPtr, NULL);
8073 return objPtr->length;
8077 const char *Jim_String(Jim_Obj *objPtr)
8079 if (objPtr->bytes == NULL) {
8081 Jim_GetString(objPtr, NULL);
8083 return objPtr->bytes;
8086 static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
8088 objPtr->bytes = Jim_StrDup(str);
8089 objPtr->length = strlen(str);
8092 static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8093 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8095 static const Jim_ObjType dictSubstObjType = {
8096 "dict-substitution",
8097 FreeDictSubstInternalRep,
8098 DupDictSubstInternalRep,
8099 NULL,
8100 JIM_TYPE_NONE,
8103 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8104 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8106 static const Jim_ObjType interpolatedObjType = {
8107 "interpolated",
8108 FreeInterpolatedInternalRep,
8109 DupInterpolatedInternalRep,
8110 NULL,
8111 JIM_TYPE_NONE,
8114 static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8116 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
8119 static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8122 dupPtr->internalRep = srcPtr->internalRep;
8124 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
8127 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8128 static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8130 static const Jim_ObjType stringObjType = {
8131 "string",
8132 NULL,
8133 DupStringInternalRep,
8134 NULL,
8135 JIM_TYPE_REFERENCES,
8138 static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8140 JIM_NOTUSED(interp);
8142 dupPtr->internalRep.strValue.maxLength = srcPtr->length;
8143 dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
8146 static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
8148 if (objPtr->typePtr != &stringObjType) {
8150 if (objPtr->bytes == NULL) {
8152 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
8153 objPtr->typePtr->updateStringProc(objPtr);
8156 Jim_FreeIntRep(interp, objPtr);
8158 objPtr->typePtr = &stringObjType;
8159 objPtr->internalRep.strValue.maxLength = objPtr->length;
8161 objPtr->internalRep.strValue.charLength = -1;
8163 return JIM_OK;
8166 int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
8168 #ifdef JIM_UTF8
8169 SetStringFromAny(interp, objPtr);
8171 if (objPtr->internalRep.strValue.charLength < 0) {
8172 objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
8174 return objPtr->internalRep.strValue.charLength;
8175 #else
8176 return Jim_Length(objPtr);
8177 #endif
8181 Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
8183 Jim_Obj *objPtr = Jim_NewObj(interp);
8186 if (len == -1)
8187 len = strlen(s);
8189 if (len == 0) {
8190 objPtr->bytes = JimEmptyStringRep;
8192 else {
8193 objPtr->bytes = Jim_StrDupLen(s, len);
8195 objPtr->length = len;
8198 objPtr->typePtr = NULL;
8199 return objPtr;
8203 Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
8205 #ifdef JIM_UTF8
8207 int bytelen = utf8_index(s, charlen);
8209 Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
8212 objPtr->typePtr = &stringObjType;
8213 objPtr->internalRep.strValue.maxLength = bytelen;
8214 objPtr->internalRep.strValue.charLength = charlen;
8216 return objPtr;
8217 #else
8218 return Jim_NewStringObj(interp, s, charlen);
8219 #endif
8222 Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
8224 Jim_Obj *objPtr = Jim_NewObj(interp);
8226 objPtr->bytes = s;
8227 objPtr->length = (len == -1) ? strlen(s) : len;
8228 objPtr->typePtr = NULL;
8229 return objPtr;
8232 static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
8234 int needlen;
8236 if (len == -1)
8237 len = strlen(str);
8238 needlen = objPtr->length + len;
8239 if (objPtr->internalRep.strValue.maxLength < needlen ||
8240 objPtr->internalRep.strValue.maxLength == 0) {
8241 needlen *= 2;
8243 if (needlen < 7) {
8244 needlen = 7;
8246 if (objPtr->bytes == JimEmptyStringRep) {
8247 objPtr->bytes = Jim_Alloc(needlen + 1);
8249 else {
8250 objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
8252 objPtr->internalRep.strValue.maxLength = needlen;
8254 memcpy(objPtr->bytes + objPtr->length, str, len);
8255 objPtr->bytes[objPtr->length + len] = '\0';
8257 if (objPtr->internalRep.strValue.charLength >= 0) {
8259 objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
8261 objPtr->length += len;
8264 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
8266 JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
8267 SetStringFromAny(interp, objPtr);
8268 StringAppendString(objPtr, str, len);
8271 void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
8273 int len;
8274 const char *str = Jim_GetString(appendObjPtr, &len);
8275 Jim_AppendString(interp, objPtr, str, len);
8278 void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
8280 va_list ap;
8282 SetStringFromAny(interp, objPtr);
8283 va_start(ap, objPtr);
8284 while (1) {
8285 const char *s = va_arg(ap, const char *);
8287 if (s == NULL)
8288 break;
8289 Jim_AppendString(interp, objPtr, s, -1);
8291 va_end(ap);
8294 int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
8296 if (aObjPtr == bObjPtr) {
8297 return 1;
8299 else {
8300 int Alen, Blen;
8301 const char *sA = Jim_GetString(aObjPtr, &Alen);
8302 const char *sB = Jim_GetString(bObjPtr, &Blen);
8304 return Alen == Blen && memcmp(sA, sB, Alen) == 0;
8308 int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
8310 return JimGlobMatch(Jim_String(patternObjPtr), Jim_String(objPtr), nocase);
8313 int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8315 int l1, l2;
8316 const char *s1 = Jim_GetString(firstObjPtr, &l1);
8317 const char *s2 = Jim_GetString(secondObjPtr, &l2);
8319 if (nocase) {
8321 return JimStringCompareLen(s1, s2, -1, nocase);
8323 return JimStringCompare(s1, l1, s2, l2);
8326 int Jim_StringCompareLenObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
8328 const char *s1 = Jim_String(firstObjPtr);
8329 const char *s2 = Jim_String(secondObjPtr);
8331 return JimStringCompareLen(s1, s2, Jim_Utf8Length(interp, firstObjPtr), nocase);
8334 static int JimRelToAbsIndex(int len, int idx)
8336 if (idx < 0)
8337 return len + idx;
8338 return idx;
8341 static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
8343 int rangeLen;
8345 if (*firstPtr > *lastPtr) {
8346 rangeLen = 0;
8348 else {
8349 rangeLen = *lastPtr - *firstPtr + 1;
8350 if (rangeLen) {
8351 if (*firstPtr < 0) {
8352 rangeLen += *firstPtr;
8353 *firstPtr = 0;
8355 if (*lastPtr >= len) {
8356 rangeLen -= (*lastPtr - (len - 1));
8357 *lastPtr = len - 1;
8361 if (rangeLen < 0)
8362 rangeLen = 0;
8364 *rangeLenPtr = rangeLen;
8367 static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
8368 int len, int *first, int *last, int *range)
8370 if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
8371 return JIM_ERR;
8373 if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
8374 return JIM_ERR;
8376 *first = JimRelToAbsIndex(len, *first);
8377 *last = JimRelToAbsIndex(len, *last);
8378 JimRelToAbsRange(len, first, last, range);
8379 return JIM_OK;
8382 Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
8383 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8385 int first, last;
8386 const char *str;
8387 int rangeLen;
8388 int bytelen;
8390 str = Jim_GetString(strObjPtr, &bytelen);
8392 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
8393 return NULL;
8396 if (first == 0 && rangeLen == bytelen) {
8397 return strObjPtr;
8399 return Jim_NewStringObj(interp, str + first, rangeLen);
8402 Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
8403 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
8405 #ifdef JIM_UTF8
8406 int first, last;
8407 const char *str;
8408 int len, rangeLen;
8409 int bytelen;
8411 str = Jim_GetString(strObjPtr, &bytelen);
8412 len = Jim_Utf8Length(interp, strObjPtr);
8414 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8415 return NULL;
8418 if (first == 0 && rangeLen == len) {
8419 return strObjPtr;
8421 if (len == bytelen) {
8423 return Jim_NewStringObj(interp, str + first, rangeLen);
8425 return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen);
8426 #else
8427 return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr);
8428 #endif
8431 Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
8432 Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
8434 int first, last;
8435 const char *str;
8436 int len, rangeLen;
8437 Jim_Obj *objPtr;
8439 len = Jim_Utf8Length(interp, strObjPtr);
8441 if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
8442 return NULL;
8445 if (last < first) {
8446 return strObjPtr;
8449 str = Jim_String(strObjPtr);
8452 objPtr = Jim_NewStringObjUtf8(interp, str, first);
8455 if (newStrObj) {
8456 Jim_AppendObj(interp, objPtr, newStrObj);
8460 Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
8462 return objPtr;
8465 static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
8467 while (*str) {
8468 int c;
8469 str += utf8_tounicode(str, &c);
8470 dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c));
8472 *dest = 0;
8475 static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
8477 char *buf;
8478 int len;
8479 const char *str;
8481 str = Jim_GetString(strObjPtr, &len);
8483 #ifdef JIM_UTF8
8484 len *= 2;
8485 #endif
8486 buf = Jim_Alloc(len + 1);
8487 JimStrCopyUpperLower(buf, str, 0);
8488 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8491 static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
8493 char *buf;
8494 const char *str;
8495 int len;
8497 str = Jim_GetString(strObjPtr, &len);
8499 #ifdef JIM_UTF8
8500 len *= 2;
8501 #endif
8502 buf = Jim_Alloc(len + 1);
8503 JimStrCopyUpperLower(buf, str, 1);
8504 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8507 static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
8509 char *buf, *p;
8510 int len;
8511 int c;
8512 const char *str;
8514 str = Jim_GetString(strObjPtr, &len);
8516 #ifdef JIM_UTF8
8517 len *= 2;
8518 #endif
8519 buf = p = Jim_Alloc(len + 1);
8521 str += utf8_tounicode(str, &c);
8522 p += utf8_getchars(p, utf8_title(c));
8524 JimStrCopyUpperLower(p, str, 0);
8526 return Jim_NewStringObjNoAlloc(interp, buf, -1);
8529 static const char *utf8_memchr(const char *str, int len, int c)
8531 #ifdef JIM_UTF8
8532 while (len) {
8533 int sc;
8534 int n = utf8_tounicode(str, &sc);
8535 if (sc == c) {
8536 return str;
8538 str += n;
8539 len -= n;
8541 return NULL;
8542 #else
8543 return memchr(str, c, len);
8544 #endif
8547 static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen)
8549 while (len) {
8550 int c;
8551 int n = utf8_tounicode(str, &c);
8553 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8555 break;
8557 str += n;
8558 len -= n;
8560 return str;
8563 static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
8565 str += len;
8567 while (len) {
8568 int c;
8569 int n = utf8_prev_len(str, len);
8571 len -= n;
8572 str -= n;
8574 n = utf8_tounicode(str, &c);
8576 if (utf8_memchr(trimchars, trimlen, c) == NULL) {
8577 return str + n;
8581 return NULL;
8584 static const char default_trim_chars[] = " \t\n\r";
8586 static int default_trim_chars_len = sizeof(default_trim_chars);
8588 static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8590 int len;
8591 const char *str = Jim_GetString(strObjPtr, &len);
8592 const char *trimchars = default_trim_chars;
8593 int trimcharslen = default_trim_chars_len;
8594 const char *newstr;
8596 if (trimcharsObjPtr) {
8597 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8600 newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
8601 if (newstr == str) {
8602 return strObjPtr;
8605 return Jim_NewStringObj(interp, newstr, len - (newstr - str));
8608 static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8610 int len;
8611 const char *trimchars = default_trim_chars;
8612 int trimcharslen = default_trim_chars_len;
8613 const char *nontrim;
8615 if (trimcharsObjPtr) {
8616 trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
8619 SetStringFromAny(interp, strObjPtr);
8621 len = Jim_Length(strObjPtr);
8622 nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
8624 if (nontrim == NULL) {
8626 return Jim_NewEmptyStringObj(interp);
8628 if (nontrim == strObjPtr->bytes + len) {
8630 return strObjPtr;
8633 if (Jim_IsShared(strObjPtr)) {
8634 strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
8636 else {
8638 strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0;
8639 strObjPtr->length = (nontrim - strObjPtr->bytes);
8642 return strObjPtr;
8645 static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
8648 Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
8651 strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
8654 if (objPtr != strObjPtr && objPtr->refCount == 0) {
8656 Jim_FreeNewObj(interp, objPtr);
8659 return strObjPtr;
8663 #ifdef HAVE_ISASCII
8664 #define jim_isascii isascii
8665 #else
8666 static int jim_isascii(int c)
8668 return !(c & ~0x7f);
8670 #endif
8672 static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
8674 static const char * const strclassnames[] = {
8675 "integer", "alpha", "alnum", "ascii", "digit",
8676 "double", "lower", "upper", "space", "xdigit",
8677 "control", "print", "graph", "punct", "boolean",
8678 NULL
8680 enum {
8681 STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
8682 STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
8683 STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN,
8685 int strclass;
8686 int len;
8687 int i;
8688 const char *str;
8689 int (*isclassfunc)(int c) = NULL;
8691 if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
8692 return JIM_ERR;
8695 str = Jim_GetString(strObjPtr, &len);
8696 if (len == 0) {
8697 Jim_SetResultBool(interp, !strict);
8698 return JIM_OK;
8701 switch (strclass) {
8702 case STR_IS_INTEGER:
8704 jim_wide w;
8705 Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
8706 return JIM_OK;
8709 case STR_IS_DOUBLE:
8711 double d;
8712 Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
8713 return JIM_OK;
8716 case STR_IS_BOOLEAN:
8718 int b;
8719 Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK);
8720 return JIM_OK;
8723 case STR_IS_ALPHA: isclassfunc = isalpha; break;
8724 case STR_IS_ALNUM: isclassfunc = isalnum; break;
8725 case STR_IS_ASCII: isclassfunc = jim_isascii; break;
8726 case STR_IS_DIGIT: isclassfunc = isdigit; break;
8727 case STR_IS_LOWER: isclassfunc = islower; break;
8728 case STR_IS_UPPER: isclassfunc = isupper; break;
8729 case STR_IS_SPACE: isclassfunc = isspace; break;
8730 case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
8731 case STR_IS_CONTROL: isclassfunc = iscntrl; break;
8732 case STR_IS_PRINT: isclassfunc = isprint; break;
8733 case STR_IS_GRAPH: isclassfunc = isgraph; break;
8734 case STR_IS_PUNCT: isclassfunc = ispunct; break;
8735 default:
8736 return JIM_ERR;
8739 for (i = 0; i < len; i++) {
8740 if (!isclassfunc(UCHAR(str[i]))) {
8741 Jim_SetResultBool(interp, 0);
8742 return JIM_OK;
8745 Jim_SetResultBool(interp, 1);
8746 return JIM_OK;
8751 static const Jim_ObjType comparedStringObjType = {
8752 "compared-string",
8753 NULL,
8754 NULL,
8755 NULL,
8756 JIM_TYPE_REFERENCES,
8759 int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
8761 if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
8762 return 1;
8764 else {
8765 if (strcmp(str, Jim_String(objPtr)) != 0)
8766 return 0;
8768 if (objPtr->typePtr != &comparedStringObjType) {
8769 Jim_FreeIntRep(interp, objPtr);
8770 objPtr->typePtr = &comparedStringObjType;
8772 objPtr->internalRep.ptr = (char *)str;
8773 return 1;
8777 static int qsortCompareStringPointers(const void *a, const void *b)
8779 char *const *sa = (char *const *)a;
8780 char *const *sb = (char *const *)b;
8782 return strcmp(*sa, *sb);
8787 static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8788 static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8790 static const Jim_ObjType sourceObjType = {
8791 "source",
8792 FreeSourceInternalRep,
8793 DupSourceInternalRep,
8794 NULL,
8795 JIM_TYPE_REFERENCES,
8798 void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8800 Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
8803 void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8805 dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
8806 Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
8809 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
8810 Jim_Obj *fileNameObj, int lineNumber)
8812 JimPanic((Jim_IsShared(objPtr), "JimSetSourceInfo called with shared object"));
8813 JimPanic((objPtr->typePtr != NULL, "JimSetSourceInfo called with typed object"));
8814 Jim_IncrRefCount(fileNameObj);
8815 objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
8816 objPtr->internalRep.sourceValue.lineNumber = lineNumber;
8817 objPtr->typePtr = &sourceObjType;
8820 static const Jim_ObjType scriptLineObjType = {
8821 "scriptline",
8822 NULL,
8823 NULL,
8824 NULL,
8825 JIM_NONE,
8828 static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
8830 Jim_Obj *objPtr;
8832 #ifdef DEBUG_SHOW_SCRIPT
8833 char buf[100];
8834 snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
8835 objPtr = Jim_NewStringObj(interp, buf, -1);
8836 #else
8837 objPtr = Jim_NewEmptyStringObj(interp);
8838 #endif
8839 objPtr->typePtr = &scriptLineObjType;
8840 objPtr->internalRep.scriptLineValue.argc = argc;
8841 objPtr->internalRep.scriptLineValue.line = line;
8843 return objPtr;
8846 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8847 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8849 static const Jim_ObjType scriptObjType = {
8850 "script",
8851 FreeScriptInternalRep,
8852 DupScriptInternalRep,
8853 NULL,
8854 JIM_TYPE_REFERENCES,
8857 typedef struct ScriptToken
8859 Jim_Obj *objPtr;
8860 int type;
8861 } ScriptToken;
8863 typedef struct ScriptObj
8865 ScriptToken *token;
8866 Jim_Obj *fileNameObj;
8867 int len;
8868 int substFlags;
8869 int inUse; /* Used to share a ScriptObj. Currently
8870 only used by Jim_EvalObj() as protection against
8871 shimmering of the currently evaluated object. */
8872 int firstline;
8873 int linenr;
8874 int missing;
8875 } ScriptObj;
8877 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8878 static int JimParseCheckMissing(Jim_Interp *interp, int ch);
8879 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
8881 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8883 int i;
8884 struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
8886 if (--script->inUse != 0)
8887 return;
8888 for (i = 0; i < script->len; i++) {
8889 Jim_DecrRefCount(interp, script->token[i].objPtr);
8891 Jim_Free(script->token);
8892 Jim_DecrRefCount(interp, script->fileNameObj);
8893 Jim_Free(script);
8896 void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
8898 JIM_NOTUSED(interp);
8899 JIM_NOTUSED(srcPtr);
8901 dupPtr->typePtr = NULL;
8904 typedef struct
8906 const char *token;
8907 int len;
8908 int type;
8909 int line;
8910 } ParseToken;
8912 typedef struct
8915 ParseToken *list;
8916 int size;
8917 int count;
8918 ParseToken static_list[20];
8919 } ParseTokenList;
8921 static void ScriptTokenListInit(ParseTokenList *tokenlist)
8923 tokenlist->list = tokenlist->static_list;
8924 tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
8925 tokenlist->count = 0;
8928 static void ScriptTokenListFree(ParseTokenList *tokenlist)
8930 if (tokenlist->list != tokenlist->static_list) {
8931 Jim_Free(tokenlist->list);
8935 static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
8936 int line)
8938 ParseToken *t;
8940 if (tokenlist->count == tokenlist->size) {
8942 tokenlist->size *= 2;
8943 if (tokenlist->list != tokenlist->static_list) {
8944 tokenlist->list =
8945 Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
8947 else {
8949 tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
8950 memcpy(tokenlist->list, tokenlist->static_list,
8951 tokenlist->count * sizeof(*tokenlist->list));
8954 t = &tokenlist->list[tokenlist->count++];
8955 t->token = token;
8956 t->len = len;
8957 t->type = type;
8958 t->line = line;
8961 static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t)
8963 int expand = 1;
8964 int count = 0;
8967 if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) {
8968 if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) {
8970 expand = -1;
8971 t++;
8973 else {
8974 if (script->missing == ' ') {
8976 script->missing = '}';
8977 script->linenr = t[1].line;
8983 while (!TOKEN_IS_SEP(t->type)) {
8984 t++;
8985 count++;
8988 return count * expand;
8991 static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t)
8993 Jim_Obj *objPtr;
8995 if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) {
8997 int len = t->len;
8998 char *str = Jim_Alloc(len + 1);
8999 len = JimEscape(str, t->token, len);
9000 objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
9002 else {
9003 objPtr = Jim_NewStringObj(interp, t->token, t->len);
9005 return objPtr;
9008 static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
9009 ParseTokenList *tokenlist)
9011 int i;
9012 struct ScriptToken *token;
9014 int lineargs = 0;
9016 ScriptToken *linefirst;
9017 int count;
9018 int linenr;
9020 #ifdef DEBUG_SHOW_SCRIPT_TOKENS
9021 printf("==== Tokens ====\n");
9022 for (i = 0; i < tokenlist->count; i++) {
9023 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type),
9024 tokenlist->list[i].len, tokenlist->list[i].token);
9026 #endif
9029 count = tokenlist->count;
9030 for (i = 0; i < tokenlist->count; i++) {
9031 if (tokenlist->list[i].type == JIM_TT_EOL) {
9032 count++;
9035 linenr = script->firstline = tokenlist->list[0].line;
9037 token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
9040 linefirst = token++;
9042 for (i = 0; i < tokenlist->count; ) {
9044 int wordtokens;
9047 while (tokenlist->list[i].type == JIM_TT_SEP) {
9048 i++;
9051 wordtokens = JimCountWordTokens(script, tokenlist->list + i);
9053 if (wordtokens == 0) {
9055 if (lineargs) {
9056 linefirst->type = JIM_TT_LINE;
9057 linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
9058 Jim_IncrRefCount(linefirst->objPtr);
9061 lineargs = 0;
9062 linefirst = token++;
9064 i++;
9065 continue;
9067 else if (wordtokens != 1) {
9069 token->type = JIM_TT_WORD;
9070 token->objPtr = Jim_NewIntObj(interp, wordtokens);
9071 Jim_IncrRefCount(token->objPtr);
9072 token++;
9073 if (wordtokens < 0) {
9075 i++;
9076 wordtokens = -wordtokens - 1;
9077 lineargs--;
9081 if (lineargs == 0) {
9083 linenr = tokenlist->list[i].line;
9085 lineargs++;
9088 while (wordtokens--) {
9089 const ParseToken *t = &tokenlist->list[i++];
9091 token->type = t->type;
9092 token->objPtr = JimMakeScriptObj(interp, t);
9093 Jim_IncrRefCount(token->objPtr);
9095 JimSetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
9096 token++;
9100 if (lineargs == 0) {
9101 token--;
9104 script->len = token - script->token;
9106 JimPanic((script->len >= count, "allocated script array is too short"));
9108 #ifdef DEBUG_SHOW_SCRIPT
9109 printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj));
9110 for (i = 0; i < script->len; i++) {
9111 const ScriptToken *t = &script->token[i];
9112 printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
9114 #endif
9118 int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
9120 ScriptObj *script = JimGetScript(interp, scriptObj);
9121 if (stateCharPtr) {
9122 *stateCharPtr = script->missing;
9124 return script->missing == ' ' || script->missing == '}';
9127 static int JimParseCheckMissing(Jim_Interp *interp, int ch)
9129 const char *msg;
9131 switch (ch) {
9132 case '\\':
9133 case ' ':
9134 return JIM_OK;
9136 case '[':
9137 msg = "unmatched \"[\"";
9138 break;
9139 case '{':
9140 msg = "missing close-brace";
9141 break;
9142 case '}':
9143 msg = "extra characters after close-brace";
9144 break;
9145 case '"':
9146 default:
9147 msg = "missing quote";
9148 break;
9151 Jim_SetResultString(interp, msg, -1);
9152 return JIM_ERR;
9155 static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
9156 ParseTokenList *tokenlist)
9158 int i;
9159 struct ScriptToken *token;
9161 token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
9163 for (i = 0; i < tokenlist->count; i++) {
9164 const ParseToken *t = &tokenlist->list[i];
9167 token->type = t->type;
9168 token->objPtr = JimMakeScriptObj(interp, t);
9169 Jim_IncrRefCount(token->objPtr);
9170 token++;
9173 script->len = i;
9176 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9178 int scriptTextLen;
9179 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
9180 struct JimParserCtx parser;
9181 struct ScriptObj *script;
9182 ParseTokenList tokenlist;
9183 int line = 1;
9186 if (objPtr->typePtr == &sourceObjType) {
9187 line = objPtr->internalRep.sourceValue.lineNumber;
9191 ScriptTokenListInit(&tokenlist);
9193 JimParserInit(&parser, scriptText, scriptTextLen, line);
9194 while (!parser.eof) {
9195 JimParseScript(&parser);
9196 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
9197 parser.tline);
9201 ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
9204 script = Jim_Alloc(sizeof(*script));
9205 memset(script, 0, sizeof(*script));
9206 script->inUse = 1;
9207 if (objPtr->typePtr == &sourceObjType) {
9208 script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
9210 else {
9211 script->fileNameObj = interp->emptyObj;
9213 Jim_IncrRefCount(script->fileNameObj);
9214 script->missing = parser.missing.ch;
9215 script->linenr = parser.missing.line;
9217 ScriptObjAddTokens(interp, script, &tokenlist);
9220 ScriptTokenListFree(&tokenlist);
9223 Jim_FreeIntRep(interp, objPtr);
9224 Jim_SetIntRepPtr(objPtr, script);
9225 objPtr->typePtr = &scriptObjType;
9228 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script);
9230 static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
9232 if (objPtr == interp->emptyObj) {
9234 objPtr = interp->nullScriptObj;
9237 if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
9238 JimSetScriptFromAny(interp, objPtr);
9241 return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
9244 static int JimScriptValid(Jim_Interp *interp, ScriptObj *script)
9246 if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
9247 JimAddErrorToStack(interp, script);
9248 return 0;
9250 return 1;
9254 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
9256 cmdPtr->inUse++;
9259 static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
9261 if (--cmdPtr->inUse == 0) {
9262 if (cmdPtr->isproc) {
9263 Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
9264 Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
9265 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9266 if (cmdPtr->u.proc.staticVars) {
9267 Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
9268 Jim_Free(cmdPtr->u.proc.staticVars);
9271 else {
9273 if (cmdPtr->u.native.delProc) {
9274 cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
9277 if (cmdPtr->prevCmd) {
9279 JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
9281 Jim_Free(cmdPtr);
9285 static void JimVariablesHTValDestructor(void *interp, void *val)
9287 Jim_DecrRefCount(interp, ((Jim_Var *)val)->objPtr);
9288 Jim_Free(val);
9291 static const Jim_HashTableType JimVariablesHashTableType = {
9292 JimStringCopyHTHashFunction,
9293 JimStringCopyHTDup,
9294 NULL,
9295 JimStringCopyHTKeyCompare,
9296 JimStringCopyHTKeyDestructor,
9297 JimVariablesHTValDestructor
9300 static void JimCommandsHT_ValDestructor(void *interp, void *val)
9302 JimDecrCmdRefCount(interp, val);
9305 static const Jim_HashTableType JimCommandsHashTableType = {
9306 JimStringCopyHTHashFunction,
9307 JimStringCopyHTDup,
9308 NULL,
9309 JimStringCopyHTKeyCompare,
9310 JimStringCopyHTKeyDestructor,
9311 JimCommandsHT_ValDestructor
9316 #ifdef jim_ext_namespace
9317 static Jim_Obj *JimQualifyNameObj(Jim_Interp *interp, Jim_Obj *nsObj)
9319 const char *name = Jim_String(nsObj);
9320 if (name[0] == ':' && name[1] == ':') {
9322 while (*++name == ':') {
9324 nsObj = Jim_NewStringObj(interp, name, -1);
9326 else if (Jim_Length(interp->framePtr->nsObj)) {
9328 nsObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9329 Jim_AppendStrings(interp, nsObj, "::", name, NULL);
9331 return nsObj;
9334 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9336 Jim_Obj *resultObj;
9338 const char *name = Jim_String(nameObjPtr);
9339 if (name[0] == ':' && name[1] == ':') {
9340 return nameObjPtr;
9342 Jim_IncrRefCount(nameObjPtr);
9343 resultObj = Jim_NewStringObj(interp, "::", -1);
9344 Jim_AppendObj(interp, resultObj, nameObjPtr);
9345 Jim_DecrRefCount(interp, nameObjPtr);
9347 return resultObj;
9350 static const char *JimQualifyName(Jim_Interp *interp, const char *name, Jim_Obj **objPtrPtr)
9352 Jim_Obj *objPtr = interp->emptyObj;
9354 if (name[0] == ':' && name[1] == ':') {
9356 while (*++name == ':') {
9359 else if (Jim_Length(interp->framePtr->nsObj)) {
9361 objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9362 Jim_AppendStrings(interp, objPtr, "::", name, NULL);
9363 name = Jim_String(objPtr);
9365 Jim_IncrRefCount(objPtr);
9366 *objPtrPtr = objPtr;
9367 return name;
9370 #define JimFreeQualifiedName(INTERP, OBJ) Jim_DecrRefCount((INTERP), (OBJ))
9372 #else
9374 #define JimQualifyName(INTERP, NAME, DUMMY) (((NAME)[0] == ':' && (NAME)[1] == ':') ? (NAME) + 2 : (NAME))
9375 #define JimFreeQualifiedName(INTERP, DUMMY) (void)(DUMMY)
9377 Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
9379 return nameObjPtr;
9381 #endif
9383 static int JimCreateCommand(Jim_Interp *interp, const char *name, Jim_Cmd *cmd)
9385 Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, name);
9386 if (he) {
9388 Jim_InterpIncrProcEpoch(interp);
9391 if (he && interp->local) {
9393 cmd->prevCmd = Jim_GetHashEntryVal(he);
9394 Jim_SetHashVal(&interp->commands, he, cmd);
9396 else {
9397 if (he) {
9399 Jim_DeleteHashEntry(&interp->commands, name);
9402 Jim_AddHashEntry(&interp->commands, name, cmd);
9404 return JIM_OK;
9408 int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
9409 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
9411 Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
9414 memset(cmdPtr, 0, sizeof(*cmdPtr));
9415 cmdPtr->inUse = 1;
9416 cmdPtr->u.native.delProc = delProc;
9417 cmdPtr->u.native.cmdProc = cmdProc;
9418 cmdPtr->u.native.privData = privData;
9420 JimCreateCommand(interp, cmdNameStr, cmdPtr);
9422 return JIM_OK;
9425 static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
9427 int len, i;
9429 len = Jim_ListLength(interp, staticsListObjPtr);
9430 if (len == 0) {
9431 return JIM_OK;
9434 cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
9435 Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
9436 for (i = 0; i < len; i++) {
9437 Jim_Obj *objPtr, *initObjPtr, *nameObjPtr;
9438 Jim_Var *varPtr;
9439 int subLen;
9441 objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
9443 subLen = Jim_ListLength(interp, objPtr);
9444 if (subLen == 1 || subLen == 2) {
9445 nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
9446 if (subLen == 1) {
9447 initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
9448 if (initObjPtr == NULL) {
9449 Jim_SetResultFormatted(interp,
9450 "variable for initialization of static \"%#s\" not found in the local context",
9451 nameObjPtr);
9452 return JIM_ERR;
9455 else {
9456 initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
9458 if (JimValidName(interp, "static variable", nameObjPtr) != JIM_OK) {
9459 return JIM_ERR;
9462 varPtr = Jim_Alloc(sizeof(*varPtr));
9463 varPtr->objPtr = initObjPtr;
9464 Jim_IncrRefCount(initObjPtr);
9465 varPtr->linkFramePtr = NULL;
9466 if (Jim_AddHashEntry(cmdPtr->u.proc.staticVars,
9467 Jim_String(nameObjPtr), varPtr) != JIM_OK) {
9468 Jim_SetResultFormatted(interp,
9469 "static variable name \"%#s\" duplicated in statics list", nameObjPtr);
9470 Jim_DecrRefCount(interp, initObjPtr);
9471 Jim_Free(varPtr);
9472 return JIM_ERR;
9475 else {
9476 Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
9477 objPtr);
9478 return JIM_ERR;
9481 return JIM_OK;
9484 static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, const char *cmdname)
9486 #ifdef jim_ext_namespace
9487 if (cmdPtr->isproc) {
9489 const char *pt = strrchr(cmdname, ':');
9490 if (pt && pt != cmdname && pt[-1] == ':') {
9491 Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
9492 cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 1);
9493 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9495 if (Jim_FindHashEntry(&interp->commands, pt + 1)) {
9497 Jim_InterpIncrProcEpoch(interp);
9501 #endif
9504 static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
9505 Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
9507 Jim_Cmd *cmdPtr;
9508 int argListLen;
9509 int i;
9511 argListLen = Jim_ListLength(interp, argListObjPtr);
9514 cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
9515 memset(cmdPtr, 0, sizeof(*cmdPtr));
9516 cmdPtr->inUse = 1;
9517 cmdPtr->isproc = 1;
9518 cmdPtr->u.proc.argListObjPtr = argListObjPtr;
9519 cmdPtr->u.proc.argListLen = argListLen;
9520 cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
9521 cmdPtr->u.proc.argsPos = -1;
9522 cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
9523 cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
9524 Jim_IncrRefCount(argListObjPtr);
9525 Jim_IncrRefCount(bodyObjPtr);
9526 Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
9529 if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
9530 goto err;
9535 for (i = 0; i < argListLen; i++) {
9536 Jim_Obj *argPtr;
9537 Jim_Obj *nameObjPtr;
9538 Jim_Obj *defaultObjPtr;
9539 int len;
9542 argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
9543 len = Jim_ListLength(interp, argPtr);
9544 if (len == 0) {
9545 Jim_SetResultString(interp, "argument with no name", -1);
9546 err:
9547 JimDecrCmdRefCount(interp, cmdPtr);
9548 return NULL;
9550 if (len > 2) {
9551 Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
9552 goto err;
9555 if (len == 2) {
9557 nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
9558 defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
9560 else {
9562 nameObjPtr = argPtr;
9563 defaultObjPtr = NULL;
9567 if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
9568 if (cmdPtr->u.proc.argsPos >= 0) {
9569 Jim_SetResultString(interp, "'args' specified more than once", -1);
9570 goto err;
9572 cmdPtr->u.proc.argsPos = i;
9574 else {
9575 if (len == 2) {
9576 cmdPtr->u.proc.optArity++;
9578 else {
9579 cmdPtr->u.proc.reqArity++;
9583 cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
9584 cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
9587 return cmdPtr;
9590 int Jim_DeleteCommand(Jim_Interp *interp, const char *name)
9592 int ret = JIM_OK;
9593 Jim_Obj *qualifiedNameObj;
9594 const char *qualname = JimQualifyName(interp, name, &qualifiedNameObj);
9596 if (Jim_DeleteHashEntry(&interp->commands, qualname) == JIM_ERR) {
9597 Jim_SetResultFormatted(interp, "can't delete \"%s\": command doesn't exist", name);
9598 ret = JIM_ERR;
9600 else {
9601 Jim_InterpIncrProcEpoch(interp);
9604 JimFreeQualifiedName(interp, qualifiedNameObj);
9606 return ret;
9609 int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, const char *newName)
9611 int ret = JIM_ERR;
9612 Jim_HashEntry *he;
9613 Jim_Cmd *cmdPtr;
9614 Jim_Obj *qualifiedOldNameObj;
9615 Jim_Obj *qualifiedNewNameObj;
9616 const char *fqold;
9617 const char *fqnew;
9619 if (newName[0] == 0) {
9620 return Jim_DeleteCommand(interp, oldName);
9623 fqold = JimQualifyName(interp, oldName, &qualifiedOldNameObj);
9624 fqnew = JimQualifyName(interp, newName, &qualifiedNewNameObj);
9627 he = Jim_FindHashEntry(&interp->commands, fqold);
9628 if (he == NULL) {
9629 Jim_SetResultFormatted(interp, "can't rename \"%s\": command doesn't exist", oldName);
9631 else if (Jim_FindHashEntry(&interp->commands, fqnew)) {
9632 Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName);
9634 else {
9636 cmdPtr = Jim_GetHashEntryVal(he);
9637 JimIncrCmdRefCount(cmdPtr);
9638 JimUpdateProcNamespace(interp, cmdPtr, fqnew);
9639 Jim_AddHashEntry(&interp->commands, fqnew, cmdPtr);
9642 Jim_DeleteHashEntry(&interp->commands, fqold);
9645 Jim_InterpIncrProcEpoch(interp);
9647 ret = JIM_OK;
9650 JimFreeQualifiedName(interp, qualifiedOldNameObj);
9651 JimFreeQualifiedName(interp, qualifiedNewNameObj);
9653 return ret;
9657 static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
9659 Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
9662 static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
9664 dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
9665 dupPtr->typePtr = srcPtr->typePtr;
9666 Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
9669 static const Jim_ObjType commandObjType = {
9670 "command",
9671 FreeCommandInternalRep,
9672 DupCommandInternalRep,
9673 NULL,
9674 JIM_TYPE_REFERENCES,
9677 Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
9679 Jim_Cmd *cmd;
9681 if (objPtr->typePtr != &commandObjType ||
9682 objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch
9683 #ifdef jim_ext_namespace
9684 || !Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
9685 #endif
9690 const char *name = Jim_String(objPtr);
9691 Jim_HashEntry *he;
9693 if (name[0] == ':' && name[1] == ':') {
9694 while (*++name == ':') {
9697 #ifdef jim_ext_namespace
9698 else if (Jim_Length(interp->framePtr->nsObj)) {
9700 Jim_Obj *nameObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
9701 Jim_AppendStrings(interp, nameObj, "::", name, NULL);
9702 he = Jim_FindHashEntry(&interp->commands, Jim_String(nameObj));
9703 Jim_FreeNewObj(interp, nameObj);
9704 if (he) {
9705 goto found;
9708 #endif
9711 he = Jim_FindHashEntry(&interp->commands, name);
9712 if (he == NULL) {
9713 if (flags & JIM_ERRMSG) {
9714 Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
9716 return NULL;
9718 #ifdef jim_ext_namespace
9719 found:
9720 #endif
9721 cmd = Jim_GetHashEntryVal(he);
9724 Jim_FreeIntRep(interp, objPtr);
9725 objPtr->typePtr = &commandObjType;
9726 objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
9727 objPtr->internalRep.cmdValue.cmdPtr = cmd;
9728 objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
9729 Jim_IncrRefCount(interp->framePtr->nsObj);
9731 else {
9732 cmd = objPtr->internalRep.cmdValue.cmdPtr;
9734 while (cmd->u.proc.upcall) {
9735 cmd = cmd->prevCmd;
9737 return cmd;
9742 #define JIM_DICT_SUGAR 100
9744 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
9746 static const Jim_ObjType variableObjType = {
9747 "variable",
9748 NULL,
9749 NULL,
9750 NULL,
9751 JIM_TYPE_REFERENCES,
9754 static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr)
9757 if (nameObjPtr->typePtr != &variableObjType) {
9758 int len;
9759 const char *str = Jim_GetString(nameObjPtr, &len);
9760 if (memchr(str, '\0', len)) {
9761 Jim_SetResultFormatted(interp, "%s name contains embedded null", type);
9762 return JIM_ERR;
9765 return JIM_OK;
9768 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
9770 const char *varName;
9771 Jim_CallFrame *framePtr;
9772 Jim_HashEntry *he;
9773 int global;
9774 int len;
9777 if (objPtr->typePtr == &variableObjType) {
9778 framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
9779 if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
9781 return JIM_OK;
9785 else if (objPtr->typePtr == &dictSubstObjType) {
9786 return JIM_DICT_SUGAR;
9788 else if (JimValidName(interp, "variable", objPtr) != JIM_OK) {
9789 return JIM_ERR;
9793 varName = Jim_GetString(objPtr, &len);
9796 if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
9797 return JIM_DICT_SUGAR;
9800 if (varName[0] == ':' && varName[1] == ':') {
9801 while (*++varName == ':') {
9803 global = 1;
9804 framePtr = interp->topFramePtr;
9806 else {
9807 global = 0;
9808 framePtr = interp->framePtr;
9812 he = Jim_FindHashEntry(&framePtr->vars, varName);
9813 if (he == NULL) {
9814 if (!global && framePtr->staticVars) {
9816 he = Jim_FindHashEntry(framePtr->staticVars, varName);
9818 if (he == NULL) {
9819 return JIM_ERR;
9824 Jim_FreeIntRep(interp, objPtr);
9825 objPtr->typePtr = &variableObjType;
9826 objPtr->internalRep.varValue.callFrameId = framePtr->id;
9827 objPtr->internalRep.varValue.varPtr = Jim_GetHashEntryVal(he);
9828 objPtr->internalRep.varValue.global = global;
9829 return JIM_OK;
9833 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
9834 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
9836 static Jim_Var *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9838 const char *name;
9839 Jim_CallFrame *framePtr;
9840 int global;
9843 Jim_Var *var = Jim_Alloc(sizeof(*var));
9845 var->objPtr = valObjPtr;
9846 Jim_IncrRefCount(valObjPtr);
9847 var->linkFramePtr = NULL;
9849 name = Jim_String(nameObjPtr);
9850 if (name[0] == ':' && name[1] == ':') {
9851 while (*++name == ':') {
9853 framePtr = interp->topFramePtr;
9854 global = 1;
9856 else {
9857 framePtr = interp->framePtr;
9858 global = 0;
9862 Jim_AddHashEntry(&framePtr->vars, name, var);
9865 Jim_FreeIntRep(interp, nameObjPtr);
9866 nameObjPtr->typePtr = &variableObjType;
9867 nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
9868 nameObjPtr->internalRep.varValue.varPtr = var;
9869 nameObjPtr->internalRep.varValue.global = global;
9871 return var;
9875 int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
9877 int err;
9878 Jim_Var *var;
9880 switch (SetVariableFromAny(interp, nameObjPtr)) {
9881 case JIM_DICT_SUGAR:
9882 return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
9884 case JIM_ERR:
9885 if (JimValidName(interp, "variable", nameObjPtr) != JIM_OK) {
9886 return JIM_ERR;
9888 JimCreateVariable(interp, nameObjPtr, valObjPtr);
9889 break;
9891 case JIM_OK:
9892 var = nameObjPtr->internalRep.varValue.varPtr;
9893 if (var->linkFramePtr == NULL) {
9894 Jim_IncrRefCount(valObjPtr);
9895 Jim_DecrRefCount(interp, var->objPtr);
9896 var->objPtr = valObjPtr;
9898 else {
9899 Jim_CallFrame *savedCallFrame;
9901 savedCallFrame = interp->framePtr;
9902 interp->framePtr = var->linkFramePtr;
9903 err = Jim_SetVariable(interp, var->objPtr, valObjPtr);
9904 interp->framePtr = savedCallFrame;
9905 if (err != JIM_OK)
9906 return err;
9909 return JIM_OK;
9912 int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9914 Jim_Obj *nameObjPtr;
9915 int result;
9917 nameObjPtr = Jim_NewStringObj(interp, name, -1);
9918 Jim_IncrRefCount(nameObjPtr);
9919 result = Jim_SetVariable(interp, nameObjPtr, objPtr);
9920 Jim_DecrRefCount(interp, nameObjPtr);
9921 return result;
9924 int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
9926 Jim_CallFrame *savedFramePtr;
9927 int result;
9929 savedFramePtr = interp->framePtr;
9930 interp->framePtr = interp->topFramePtr;
9931 result = Jim_SetVariableStr(interp, name, objPtr);
9932 interp->framePtr = savedFramePtr;
9933 return result;
9936 int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
9938 Jim_Obj *valObjPtr;
9939 int result;
9941 valObjPtr = Jim_NewStringObj(interp, val, -1);
9942 Jim_IncrRefCount(valObjPtr);
9943 result = Jim_SetVariableStr(interp, name, valObjPtr);
9944 Jim_DecrRefCount(interp, valObjPtr);
9945 return result;
9948 int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
9949 Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
9951 const char *varName;
9952 const char *targetName;
9953 Jim_CallFrame *framePtr;
9954 Jim_Var *varPtr;
9957 switch (SetVariableFromAny(interp, nameObjPtr)) {
9958 case JIM_DICT_SUGAR:
9960 Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
9961 return JIM_ERR;
9963 case JIM_OK:
9964 varPtr = nameObjPtr->internalRep.varValue.varPtr;
9966 if (varPtr->linkFramePtr == NULL) {
9967 Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
9968 return JIM_ERR;
9972 varPtr->linkFramePtr = NULL;
9973 break;
9978 varName = Jim_String(nameObjPtr);
9980 if (varName[0] == ':' && varName[1] == ':') {
9981 while (*++varName == ':') {
9984 framePtr = interp->topFramePtr;
9986 else {
9987 framePtr = interp->framePtr;
9990 targetName = Jim_String(targetNameObjPtr);
9991 if (targetName[0] == ':' && targetName[1] == ':') {
9992 while (*++targetName == ':') {
9994 targetNameObjPtr = Jim_NewStringObj(interp, targetName, -1);
9995 targetCallFrame = interp->topFramePtr;
9997 Jim_IncrRefCount(targetNameObjPtr);
9999 if (framePtr->level < targetCallFrame->level) {
10000 Jim_SetResultFormatted(interp,
10001 "bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
10002 nameObjPtr);
10003 Jim_DecrRefCount(interp, targetNameObjPtr);
10004 return JIM_ERR;
10008 if (framePtr == targetCallFrame) {
10009 Jim_Obj *objPtr = targetNameObjPtr;
10012 while (1) {
10013 if (strcmp(Jim_String(objPtr), varName) == 0) {
10014 Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
10015 Jim_DecrRefCount(interp, targetNameObjPtr);
10016 return JIM_ERR;
10018 if (SetVariableFromAny(interp, objPtr) != JIM_OK)
10019 break;
10020 varPtr = objPtr->internalRep.varValue.varPtr;
10021 if (varPtr->linkFramePtr != targetCallFrame)
10022 break;
10023 objPtr = varPtr->objPtr;
10028 Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
10030 nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame;
10031 Jim_DecrRefCount(interp, targetNameObjPtr);
10032 return JIM_OK;
10035 Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10037 switch (SetVariableFromAny(interp, nameObjPtr)) {
10038 case JIM_OK:{
10039 Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr;
10041 if (varPtr->linkFramePtr == NULL) {
10042 return varPtr->objPtr;
10044 else {
10045 Jim_Obj *objPtr;
10048 Jim_CallFrame *savedCallFrame = interp->framePtr;
10050 interp->framePtr = varPtr->linkFramePtr;
10051 objPtr = Jim_GetVariable(interp, varPtr->objPtr, flags);
10052 interp->framePtr = savedCallFrame;
10053 if (objPtr) {
10054 return objPtr;
10059 break;
10061 case JIM_DICT_SUGAR:
10063 return JimDictSugarGet(interp, nameObjPtr, flags);
10065 if (flags & JIM_ERRMSG) {
10066 Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
10068 return NULL;
10071 Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10073 Jim_CallFrame *savedFramePtr;
10074 Jim_Obj *objPtr;
10076 savedFramePtr = interp->framePtr;
10077 interp->framePtr = interp->topFramePtr;
10078 objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
10079 interp->framePtr = savedFramePtr;
10081 return objPtr;
10084 Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
10086 Jim_Obj *nameObjPtr, *varObjPtr;
10088 nameObjPtr = Jim_NewStringObj(interp, name, -1);
10089 Jim_IncrRefCount(nameObjPtr);
10090 varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
10091 Jim_DecrRefCount(interp, nameObjPtr);
10092 return varObjPtr;
10095 Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
10097 Jim_CallFrame *savedFramePtr;
10098 Jim_Obj *objPtr;
10100 savedFramePtr = interp->framePtr;
10101 interp->framePtr = interp->topFramePtr;
10102 objPtr = Jim_GetVariableStr(interp, name, flags);
10103 interp->framePtr = savedFramePtr;
10105 return objPtr;
10108 int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
10110 Jim_Var *varPtr;
10111 int retval;
10112 Jim_CallFrame *framePtr;
10114 retval = SetVariableFromAny(interp, nameObjPtr);
10115 if (retval == JIM_DICT_SUGAR) {
10117 return JimDictSugarSet(interp, nameObjPtr, NULL);
10119 else if (retval == JIM_OK) {
10120 varPtr = nameObjPtr->internalRep.varValue.varPtr;
10123 if (varPtr->linkFramePtr) {
10124 framePtr = interp->framePtr;
10125 interp->framePtr = varPtr->linkFramePtr;
10126 retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE);
10127 interp->framePtr = framePtr;
10129 else {
10130 const char *name = Jim_String(nameObjPtr);
10131 if (nameObjPtr->internalRep.varValue.global) {
10132 name += 2;
10133 framePtr = interp->topFramePtr;
10135 else {
10136 framePtr = interp->framePtr;
10139 retval = Jim_DeleteHashEntry(&framePtr->vars, name);
10140 if (retval == JIM_OK) {
10142 framePtr->id = interp->callFrameEpoch++;
10146 if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
10147 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
10149 return retval;
10154 static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
10155 Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
10157 const char *str, *p;
10158 int len, keyLen;
10159 Jim_Obj *varObjPtr, *keyObjPtr;
10161 str = Jim_GetString(objPtr, &len);
10163 p = strchr(str, '(');
10164 JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str));
10166 varObjPtr = Jim_NewStringObj(interp, str, p - str);
10168 p++;
10169 keyLen = (str + len) - p;
10170 if (str[len - 1] == ')') {
10171 keyLen--;
10175 keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
10177 Jim_IncrRefCount(varObjPtr);
10178 Jim_IncrRefCount(keyObjPtr);
10179 *varPtrPtr = varObjPtr;
10180 *keyPtrPtr = keyObjPtr;
10183 static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr)
10185 int err;
10187 SetDictSubstFromAny(interp, objPtr);
10189 err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
10190 &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_MUSTEXIST);
10192 if (err == JIM_OK) {
10194 Jim_SetEmptyResult(interp);
10196 else {
10197 if (!valObjPtr) {
10199 if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) {
10200 Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array",
10201 objPtr);
10202 return err;
10206 Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
10207 (valObjPtr ? "set" : "unset"), objPtr);
10209 return err;
10212 static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
10213 Jim_Obj *keyObjPtr, int flags)
10215 Jim_Obj *dictObjPtr;
10216 Jim_Obj *resObjPtr = NULL;
10217 int ret;
10219 dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
10220 if (!dictObjPtr) {
10221 return NULL;
10224 ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
10225 if (ret != JIM_OK) {
10226 Jim_SetResultFormatted(interp,
10227 "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
10228 ret < 0 ? "variable isn't" : "no such element in");
10230 else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
10232 Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
10235 return resObjPtr;
10239 static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10241 SetDictSubstFromAny(interp, objPtr);
10243 return JimDictExpandArrayVariable(interp,
10244 objPtr->internalRep.dictSubstValue.varNameObjPtr,
10245 objPtr->internalRep.dictSubstValue.indexObjPtr, flags);
10250 void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
10252 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
10253 Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
10256 static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
10259 dupPtr->internalRep = srcPtr->internalRep;
10261 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr);
10262 Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
10266 static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
10268 if (objPtr->typePtr != &dictSubstObjType) {
10269 Jim_Obj *varObjPtr, *keyObjPtr;
10271 if (objPtr->typePtr == &interpolatedObjType) {
10274 varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
10275 keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
10277 Jim_IncrRefCount(varObjPtr);
10278 Jim_IncrRefCount(keyObjPtr);
10280 else {
10281 JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
10284 Jim_FreeIntRep(interp, objPtr);
10285 objPtr->typePtr = &dictSubstObjType;
10286 objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
10287 objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
10291 static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10293 Jim_Obj *resObjPtr = NULL;
10294 Jim_Obj *substKeyObjPtr = NULL;
10296 SetDictSubstFromAny(interp, objPtr);
10298 if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
10299 &substKeyObjPtr, JIM_NONE)
10300 != JIM_OK) {
10301 return NULL;
10303 Jim_IncrRefCount(substKeyObjPtr);
10304 resObjPtr =
10305 JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
10306 substKeyObjPtr, 0);
10307 Jim_DecrRefCount(interp, substKeyObjPtr);
10309 return resObjPtr;
10312 static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr)
10314 if (Jim_EvalExpression(interp, objPtr) == JIM_OK) {
10315 return Jim_GetResult(interp);
10317 return NULL;
10321 static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
10323 Jim_CallFrame *cf;
10325 if (interp->freeFramesList) {
10326 cf = interp->freeFramesList;
10327 interp->freeFramesList = cf->next;
10329 cf->argv = NULL;
10330 cf->argc = 0;
10331 cf->procArgsObjPtr = NULL;
10332 cf->procBodyObjPtr = NULL;
10333 cf->next = NULL;
10334 cf->staticVars = NULL;
10335 cf->localCommands = NULL;
10336 cf->tailcallObj = NULL;
10337 cf->tailcallCmd = NULL;
10339 else {
10340 cf = Jim_Alloc(sizeof(*cf));
10341 memset(cf, 0, sizeof(*cf));
10343 Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
10346 cf->id = interp->callFrameEpoch++;
10347 cf->parent = parent;
10348 cf->level = parent ? parent->level + 1 : 0;
10349 cf->nsObj = nsObj;
10350 Jim_IncrRefCount(nsObj);
10352 return cf;
10355 static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
10358 if (localCommands) {
10359 Jim_Obj *cmdNameObj;
10361 while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
10362 Jim_HashEntry *he;
10363 Jim_Obj *fqObjName;
10364 Jim_HashTable *ht = &interp->commands;
10366 const char *fqname = JimQualifyName(interp, Jim_String(cmdNameObj), &fqObjName);
10368 he = Jim_FindHashEntry(ht, fqname);
10370 if (he) {
10371 Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
10372 if (cmd->prevCmd) {
10373 Jim_Cmd *prevCmd = cmd->prevCmd;
10374 cmd->prevCmd = NULL;
10377 JimDecrCmdRefCount(interp, cmd);
10380 Jim_SetHashVal(ht, he, prevCmd);
10382 else {
10383 Jim_DeleteHashEntry(ht, fqname);
10385 Jim_InterpIncrProcEpoch(interp);
10387 Jim_DecrRefCount(interp, cmdNameObj);
10388 JimFreeQualifiedName(interp, fqObjName);
10390 Jim_FreeStack(localCommands);
10391 Jim_Free(localCommands);
10393 return JIM_OK;
10396 static int JimInvokeDefer(Jim_Interp *interp, int retcode)
10398 Jim_Obj *objPtr;
10401 if (Jim_FindHashEntry(&interp->framePtr->vars, "jim::defer") == NULL) {
10402 return retcode;
10405 objPtr = Jim_GetVariableStr(interp, "jim::defer", JIM_NONE);
10407 if (objPtr) {
10408 int ret = JIM_OK;
10409 int i;
10410 int listLen = Jim_ListLength(interp, objPtr);
10411 Jim_Obj *resultObjPtr;
10413 Jim_IncrRefCount(objPtr);
10415 resultObjPtr = Jim_GetResult(interp);
10416 Jim_IncrRefCount(resultObjPtr);
10417 Jim_SetEmptyResult(interp);
10420 for (i = listLen; i > 0; i--) {
10422 Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1);
10423 ret = Jim_EvalObj(interp, scriptObjPtr);
10424 if (ret != JIM_OK) {
10425 break;
10429 if (ret == JIM_OK || retcode == JIM_ERR) {
10431 Jim_SetResult(interp, resultObjPtr);
10433 else {
10434 retcode = ret;
10437 Jim_DecrRefCount(interp, resultObjPtr);
10438 Jim_DecrRefCount(interp, objPtr);
10440 return retcode;
10443 #define JIM_FCF_FULL 0
10444 #define JIM_FCF_REUSE 1
10445 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
10447 JimDeleteLocalProcs(interp, cf->localCommands);
10449 if (cf->procArgsObjPtr)
10450 Jim_DecrRefCount(interp, cf->procArgsObjPtr);
10451 if (cf->procBodyObjPtr)
10452 Jim_DecrRefCount(interp, cf->procBodyObjPtr);
10453 Jim_DecrRefCount(interp, cf->nsObj);
10454 if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
10455 Jim_FreeHashTable(&cf->vars);
10456 else {
10457 int i;
10458 Jim_HashEntry **table = cf->vars.table, *he;
10460 for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) {
10461 he = table[i];
10462 while (he != NULL) {
10463 Jim_HashEntry *nextEntry = he->next;
10464 Jim_Var *varPtr = Jim_GetHashEntryVal(he);
10466 Jim_DecrRefCount(interp, varPtr->objPtr);
10467 Jim_Free(Jim_GetHashEntryKey(he));
10468 Jim_Free(varPtr);
10469 Jim_Free(he);
10470 table[i] = NULL;
10471 he = nextEntry;
10474 cf->vars.used = 0;
10476 cf->next = interp->freeFramesList;
10477 interp->freeFramesList = cf;
10482 int Jim_IsBigEndian(void)
10484 union {
10485 unsigned short s;
10486 unsigned char c[2];
10487 } uval = {0x0102};
10489 return uval.c[0] == 1;
10493 Jim_Interp *Jim_CreateInterp(void)
10495 Jim_Interp *i = Jim_Alloc(sizeof(*i));
10497 memset(i, 0, sizeof(*i));
10499 i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
10500 i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
10501 i->lastCollectTime = time(NULL);
10503 Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
10504 #ifdef JIM_REFERENCES
10505 Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
10506 #endif
10507 Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
10508 Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
10509 i->emptyObj = Jim_NewEmptyStringObj(i);
10510 i->trueObj = Jim_NewIntObj(i, 1);
10511 i->falseObj = Jim_NewIntObj(i, 0);
10512 i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
10513 i->errorFileNameObj = i->emptyObj;
10514 i->result = i->emptyObj;
10515 i->stackTrace = Jim_NewListObj(i, NULL, 0);
10516 i->unknown = Jim_NewStringObj(i, "unknown", -1);
10517 i->errorProc = i->emptyObj;
10518 i->currentScriptObj = Jim_NewEmptyStringObj(i);
10519 i->nullScriptObj = Jim_NewEmptyStringObj(i);
10520 Jim_IncrRefCount(i->emptyObj);
10521 Jim_IncrRefCount(i->errorFileNameObj);
10522 Jim_IncrRefCount(i->result);
10523 Jim_IncrRefCount(i->stackTrace);
10524 Jim_IncrRefCount(i->unknown);
10525 Jim_IncrRefCount(i->currentScriptObj);
10526 Jim_IncrRefCount(i->nullScriptObj);
10527 Jim_IncrRefCount(i->errorProc);
10528 Jim_IncrRefCount(i->trueObj);
10529 Jim_IncrRefCount(i->falseObj);
10532 Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
10533 Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
10535 Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
10536 Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
10537 Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
10538 Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
10539 Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
10540 Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
10541 Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
10542 Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
10544 return i;
10547 void Jim_FreeInterp(Jim_Interp *i)
10549 Jim_CallFrame *cf, *cfx;
10551 Jim_Obj *objPtr, *nextObjPtr;
10554 for (cf = i->framePtr; cf; cf = cfx) {
10556 JimInvokeDefer(i, JIM_OK);
10557 cfx = cf->parent;
10558 JimFreeCallFrame(i, cf, JIM_FCF_FULL);
10561 Jim_DecrRefCount(i, i->emptyObj);
10562 Jim_DecrRefCount(i, i->trueObj);
10563 Jim_DecrRefCount(i, i->falseObj);
10564 Jim_DecrRefCount(i, i->result);
10565 Jim_DecrRefCount(i, i->stackTrace);
10566 Jim_DecrRefCount(i, i->errorProc);
10567 Jim_DecrRefCount(i, i->unknown);
10568 Jim_DecrRefCount(i, i->errorFileNameObj);
10569 Jim_DecrRefCount(i, i->currentScriptObj);
10570 Jim_DecrRefCount(i, i->nullScriptObj);
10571 Jim_FreeHashTable(&i->commands);
10572 #ifdef JIM_REFERENCES
10573 Jim_FreeHashTable(&i->references);
10574 #endif
10575 Jim_FreeHashTable(&i->packages);
10576 Jim_Free(i->prngState);
10577 Jim_FreeHashTable(&i->assocData);
10579 #ifdef JIM_MAINTAINER
10580 if (i->liveList != NULL) {
10581 objPtr = i->liveList;
10583 printf("\n-------------------------------------\n");
10584 printf("Objects still in the free list:\n");
10585 while (objPtr) {
10586 const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
10587 Jim_String(objPtr);
10589 if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
10590 printf("%p (%d) %-10s: '%.20s...'\n",
10591 (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
10593 else {
10594 printf("%p (%d) %-10s: '%s'\n",
10595 (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
10597 if (objPtr->typePtr == &sourceObjType) {
10598 printf("FILE %s LINE %d\n",
10599 Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
10600 objPtr->internalRep.sourceValue.lineNumber);
10602 objPtr = objPtr->nextObjPtr;
10604 printf("-------------------------------------\n\n");
10605 JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
10607 #endif
10610 objPtr = i->freeList;
10611 while (objPtr) {
10612 nextObjPtr = objPtr->nextObjPtr;
10613 Jim_Free(objPtr);
10614 objPtr = nextObjPtr;
10618 for (cf = i->freeFramesList; cf; cf = cfx) {
10619 cfx = cf->next;
10620 if (cf->vars.table)
10621 Jim_FreeHashTable(&cf->vars);
10622 Jim_Free(cf);
10626 Jim_Free(i);
10629 Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10631 long level;
10632 const char *str;
10633 Jim_CallFrame *framePtr;
10635 if (levelObjPtr) {
10636 str = Jim_String(levelObjPtr);
10637 if (str[0] == '#') {
10638 char *endptr;
10640 level = jim_strtol(str + 1, &endptr);
10641 if (str[1] == '\0' || endptr[0] != '\0') {
10642 level = -1;
10645 else {
10646 if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
10647 level = -1;
10649 else {
10651 level = interp->framePtr->level - level;
10655 else {
10656 str = "1";
10657 level = interp->framePtr->level - 1;
10660 if (level == 0) {
10661 return interp->topFramePtr;
10663 if (level > 0) {
10665 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10666 if (framePtr->level == level) {
10667 return framePtr;
10672 Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
10673 return NULL;
10676 static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr)
10678 long level;
10679 Jim_CallFrame *framePtr;
10681 if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
10682 if (level <= 0) {
10684 level = interp->framePtr->level + level;
10687 if (level == 0) {
10688 return interp->topFramePtr;
10692 for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
10693 if (framePtr->level == level) {
10694 return framePtr;
10699 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
10700 return NULL;
10703 static void JimResetStackTrace(Jim_Interp *interp)
10705 Jim_DecrRefCount(interp, interp->stackTrace);
10706 interp->stackTrace = Jim_NewListObj(interp, NULL, 0);
10707 Jim_IncrRefCount(interp->stackTrace);
10710 static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
10712 int len;
10715 Jim_IncrRefCount(stackTraceObj);
10716 Jim_DecrRefCount(interp, interp->stackTrace);
10717 interp->stackTrace = stackTraceObj;
10718 interp->errorFlag = 1;
10720 len = Jim_ListLength(interp, interp->stackTrace);
10721 if (len >= 3) {
10722 if (Jim_Length(Jim_ListGetIndex(interp, interp->stackTrace, len - 2)) == 0) {
10723 interp->addStackTrace = 1;
10728 static void JimAppendStackTrace(Jim_Interp *interp, const char *procname,
10729 Jim_Obj *fileNameObj, int linenr)
10731 if (strcmp(procname, "unknown") == 0) {
10732 procname = "";
10734 if (!*procname && !Jim_Length(fileNameObj)) {
10736 return;
10739 if (Jim_IsShared(interp->stackTrace)) {
10740 Jim_DecrRefCount(interp, interp->stackTrace);
10741 interp->stackTrace = Jim_DuplicateObj(interp, interp->stackTrace);
10742 Jim_IncrRefCount(interp->stackTrace);
10746 if (!*procname && Jim_Length(fileNameObj)) {
10748 int len = Jim_ListLength(interp, interp->stackTrace);
10750 if (len >= 3) {
10751 Jim_Obj *objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 3);
10752 if (Jim_Length(objPtr)) {
10754 objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 2);
10755 if (Jim_Length(objPtr) == 0) {
10757 ListSetIndex(interp, interp->stackTrace, len - 2, fileNameObj, 0);
10758 ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), 0);
10759 return;
10765 Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewStringObj(interp, procname, -1));
10766 Jim_ListAppendElement(interp, interp->stackTrace, fileNameObj);
10767 Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewIntObj(interp, linenr));
10770 int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
10771 void *data)
10773 AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
10775 assocEntryPtr->delProc = delProc;
10776 assocEntryPtr->data = data;
10777 return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
10780 void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
10782 Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
10784 if (entryPtr != NULL) {
10785 AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr);
10786 return assocEntryPtr->data;
10788 return NULL;
10791 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
10793 return Jim_DeleteHashEntry(&interp->assocData, key);
10796 int Jim_GetExitCode(Jim_Interp *interp)
10798 return interp->exitCode;
10801 static void UpdateStringOfInt(struct Jim_Obj *objPtr);
10802 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
10804 static const Jim_ObjType intObjType = {
10805 "int",
10806 NULL,
10807 NULL,
10808 UpdateStringOfInt,
10809 JIM_TYPE_NONE,
10812 static const Jim_ObjType coercedDoubleObjType = {
10813 "coerced-double",
10814 NULL,
10815 NULL,
10816 UpdateStringOfInt,
10817 JIM_TYPE_NONE,
10821 static void UpdateStringOfInt(struct Jim_Obj *objPtr)
10823 char buf[JIM_INTEGER_SPACE + 1];
10824 jim_wide wideValue = JimWideValue(objPtr);
10825 int pos = 0;
10827 if (wideValue == 0) {
10828 buf[pos++] = '0';
10830 else {
10831 char tmp[JIM_INTEGER_SPACE];
10832 int num = 0;
10833 int i;
10835 if (wideValue < 0) {
10836 buf[pos++] = '-';
10837 i = wideValue % 10;
10838 tmp[num++] = (i > 0) ? (10 - i) : -i;
10839 wideValue /= -10;
10842 while (wideValue) {
10843 tmp[num++] = wideValue % 10;
10844 wideValue /= 10;
10847 for (i = 0; i < num; i++) {
10848 buf[pos++] = '0' + tmp[num - i - 1];
10851 buf[pos] = 0;
10853 JimSetStringBytes(objPtr, buf);
10856 static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
10858 jim_wide wideValue;
10859 const char *str;
10861 if (objPtr->typePtr == &coercedDoubleObjType) {
10863 objPtr->typePtr = &intObjType;
10864 return JIM_OK;
10868 str = Jim_String(objPtr);
10870 if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
10871 if (flags & JIM_ERRMSG) {
10872 Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
10874 return JIM_ERR;
10876 if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) {
10877 Jim_SetResultString(interp, "Integer value too big to be represented", -1);
10878 return JIM_ERR;
10881 Jim_FreeIntRep(interp, objPtr);
10882 objPtr->typePtr = &intObjType;
10883 objPtr->internalRep.wideValue = wideValue;
10884 return JIM_OK;
10887 #ifdef JIM_OPTIMIZATION
10888 static int JimIsWide(Jim_Obj *objPtr)
10890 return objPtr->typePtr == &intObjType;
10892 #endif
10894 int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10896 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
10897 return JIM_ERR;
10898 *widePtr = JimWideValue(objPtr);
10899 return JIM_OK;
10903 static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
10905 if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
10906 return JIM_ERR;
10907 *widePtr = JimWideValue(objPtr);
10908 return JIM_OK;
10911 int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
10913 jim_wide wideValue;
10914 int retval;
10916 retval = Jim_GetWide(interp, objPtr, &wideValue);
10917 if (retval == JIM_OK) {
10918 *longPtr = (long)wideValue;
10919 return JIM_OK;
10921 return JIM_ERR;
10924 Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
10926 Jim_Obj *objPtr;
10928 objPtr = Jim_NewObj(interp);
10929 objPtr->typePtr = &intObjType;
10930 objPtr->bytes = NULL;
10931 objPtr->internalRep.wideValue = wideValue;
10932 return objPtr;
10935 #define JIM_DOUBLE_SPACE 30
10937 static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
10938 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
10940 static const Jim_ObjType doubleObjType = {
10941 "double",
10942 NULL,
10943 NULL,
10944 UpdateStringOfDouble,
10945 JIM_TYPE_NONE,
10948 #ifndef HAVE_ISNAN
10949 #undef isnan
10950 #define isnan(X) ((X) != (X))
10951 #endif
10952 #ifndef HAVE_ISINF
10953 #undef isinf
10954 #define isinf(X) (1.0 / (X) == 0.0)
10955 #endif
10957 static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
10959 double value = objPtr->internalRep.doubleValue;
10961 if (isnan(value)) {
10962 JimSetStringBytes(objPtr, "NaN");
10963 return;
10965 if (isinf(value)) {
10966 if (value < 0) {
10967 JimSetStringBytes(objPtr, "-Inf");
10969 else {
10970 JimSetStringBytes(objPtr, "Inf");
10972 return;
10975 char buf[JIM_DOUBLE_SPACE + 1];
10976 int i;
10977 int len = sprintf(buf, "%.12g", value);
10980 for (i = 0; i < len; i++) {
10981 if (buf[i] == '.' || buf[i] == 'e') {
10982 #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
10983 char *e = strchr(buf, 'e');
10984 if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
10986 e += 2;
10987 memmove(e, e + 1, len - (e - buf));
10989 #endif
10990 break;
10993 if (buf[i] == '\0') {
10994 buf[i++] = '.';
10995 buf[i++] = '0';
10996 buf[i] = '\0';
10998 JimSetStringBytes(objPtr, buf);
11002 static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
11004 double doubleValue;
11005 jim_wide wideValue;
11006 const char *str;
11008 #ifdef HAVE_LONG_LONG
11010 #define MIN_INT_IN_DOUBLE -(1LL << 53)
11011 #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1)
11013 if (objPtr->typePtr == &intObjType
11014 && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE
11015 && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) {
11018 objPtr->typePtr = &coercedDoubleObjType;
11019 return JIM_OK;
11021 #endif
11022 str = Jim_String(objPtr);
11024 if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
11026 Jim_FreeIntRep(interp, objPtr);
11027 objPtr->typePtr = &coercedDoubleObjType;
11028 objPtr->internalRep.wideValue = wideValue;
11029 return JIM_OK;
11031 else {
11033 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
11034 Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
11035 return JIM_ERR;
11038 Jim_FreeIntRep(interp, objPtr);
11040 objPtr->typePtr = &doubleObjType;
11041 objPtr->internalRep.doubleValue = doubleValue;
11042 return JIM_OK;
11045 int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
11047 if (objPtr->typePtr == &coercedDoubleObjType) {
11048 *doublePtr = JimWideValue(objPtr);
11049 return JIM_OK;
11051 if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
11052 return JIM_ERR;
11054 if (objPtr->typePtr == &coercedDoubleObjType) {
11055 *doublePtr = JimWideValue(objPtr);
11057 else {
11058 *doublePtr = objPtr->internalRep.doubleValue;
11060 return JIM_OK;
11063 Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
11065 Jim_Obj *objPtr;
11067 objPtr = Jim_NewObj(interp);
11068 objPtr->typePtr = &doubleObjType;
11069 objPtr->bytes = NULL;
11070 objPtr->internalRep.doubleValue = doubleValue;
11071 return objPtr;
11074 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
11076 int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
11078 if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
11079 return JIM_ERR;
11080 *booleanPtr = (int) JimWideValue(objPtr);
11081 return JIM_OK;
11084 static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
11086 static const char * const falses[] = {
11087 "0", "false", "no", "off", NULL
11089 static const char * const trues[] = {
11090 "1", "true", "yes", "on", NULL
11093 int boolean;
11095 int index;
11096 if (Jim_GetEnum(interp, objPtr, falses, &index, NULL, 0) == JIM_OK) {
11097 boolean = 0;
11098 } else if (Jim_GetEnum(interp, objPtr, trues, &index, NULL, 0) == JIM_OK) {
11099 boolean = 1;
11100 } else {
11101 if (flags & JIM_ERRMSG) {
11102 Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr);
11104 return JIM_ERR;
11108 Jim_FreeIntRep(interp, objPtr);
11109 objPtr->typePtr = &intObjType;
11110 objPtr->internalRep.wideValue = boolean;
11111 return JIM_OK;
11114 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
11115 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
11116 static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11117 static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11118 static void UpdateStringOfList(struct Jim_Obj *objPtr);
11119 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11121 static const Jim_ObjType listObjType = {
11122 "list",
11123 FreeListInternalRep,
11124 DupListInternalRep,
11125 UpdateStringOfList,
11126 JIM_TYPE_NONE,
11129 void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11131 int i;
11133 for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
11134 Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
11136 Jim_Free(objPtr->internalRep.listValue.ele);
11139 void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
11141 int i;
11143 JIM_NOTUSED(interp);
11145 dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
11146 dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
11147 dupPtr->internalRep.listValue.ele =
11148 Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
11149 memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
11150 sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
11151 for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
11152 Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
11154 dupPtr->typePtr = &listObjType;
11157 #define JIM_ELESTR_SIMPLE 0
11158 #define JIM_ELESTR_BRACE 1
11159 #define JIM_ELESTR_QUOTE 2
11160 static unsigned char ListElementQuotingType(const char *s, int len)
11162 int i, level, blevel, trySimple = 1;
11165 if (len == 0)
11166 return JIM_ELESTR_BRACE;
11167 if (s[0] == '"' || s[0] == '{') {
11168 trySimple = 0;
11169 goto testbrace;
11171 for (i = 0; i < len; i++) {
11172 switch (s[i]) {
11173 case ' ':
11174 case '$':
11175 case '"':
11176 case '[':
11177 case ']':
11178 case ';':
11179 case '\\':
11180 case '\r':
11181 case '\n':
11182 case '\t':
11183 case '\f':
11184 case '\v':
11185 trySimple = 0;
11187 case '{':
11188 case '}':
11189 goto testbrace;
11192 return JIM_ELESTR_SIMPLE;
11194 testbrace:
11196 if (s[len - 1] == '\\')
11197 return JIM_ELESTR_QUOTE;
11198 level = 0;
11199 blevel = 0;
11200 for (i = 0; i < len; i++) {
11201 switch (s[i]) {
11202 case '{':
11203 level++;
11204 break;
11205 case '}':
11206 level--;
11207 if (level < 0)
11208 return JIM_ELESTR_QUOTE;
11209 break;
11210 case '[':
11211 blevel++;
11212 break;
11213 case ']':
11214 blevel--;
11215 break;
11216 case '\\':
11217 if (s[i + 1] == '\n')
11218 return JIM_ELESTR_QUOTE;
11219 else if (s[i + 1] != '\0')
11220 i++;
11221 break;
11224 if (blevel < 0) {
11225 return JIM_ELESTR_QUOTE;
11228 if (level == 0) {
11229 if (!trySimple)
11230 return JIM_ELESTR_BRACE;
11231 for (i = 0; i < len; i++) {
11232 switch (s[i]) {
11233 case ' ':
11234 case '$':
11235 case '"':
11236 case '[':
11237 case ']':
11238 case ';':
11239 case '\\':
11240 case '\r':
11241 case '\n':
11242 case '\t':
11243 case '\f':
11244 case '\v':
11245 return JIM_ELESTR_BRACE;
11246 break;
11249 return JIM_ELESTR_SIMPLE;
11251 return JIM_ELESTR_QUOTE;
11254 static int BackslashQuoteString(const char *s, int len, char *q)
11256 char *p = q;
11258 while (len--) {
11259 switch (*s) {
11260 case ' ':
11261 case '$':
11262 case '"':
11263 case '[':
11264 case ']':
11265 case '{':
11266 case '}':
11267 case ';':
11268 case '\\':
11269 *p++ = '\\';
11270 *p++ = *s++;
11271 break;
11272 case '\n':
11273 *p++ = '\\';
11274 *p++ = 'n';
11275 s++;
11276 break;
11277 case '\r':
11278 *p++ = '\\';
11279 *p++ = 'r';
11280 s++;
11281 break;
11282 case '\t':
11283 *p++ = '\\';
11284 *p++ = 't';
11285 s++;
11286 break;
11287 case '\f':
11288 *p++ = '\\';
11289 *p++ = 'f';
11290 s++;
11291 break;
11292 case '\v':
11293 *p++ = '\\';
11294 *p++ = 'v';
11295 s++;
11296 break;
11297 default:
11298 *p++ = *s++;
11299 break;
11302 *p = '\0';
11304 return p - q;
11307 static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
11309 #define STATIC_QUOTING_LEN 32
11310 int i, bufLen, realLength;
11311 const char *strRep;
11312 char *p;
11313 unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
11316 if (objc > STATIC_QUOTING_LEN) {
11317 quotingType = Jim_Alloc(objc);
11319 else {
11320 quotingType = staticQuoting;
11322 bufLen = 0;
11323 for (i = 0; i < objc; i++) {
11324 int len;
11326 strRep = Jim_GetString(objv[i], &len);
11327 quotingType[i] = ListElementQuotingType(strRep, len);
11328 switch (quotingType[i]) {
11329 case JIM_ELESTR_SIMPLE:
11330 if (i != 0 || strRep[0] != '#') {
11331 bufLen += len;
11332 break;
11335 quotingType[i] = JIM_ELESTR_BRACE;
11337 case JIM_ELESTR_BRACE:
11338 bufLen += len + 2;
11339 break;
11340 case JIM_ELESTR_QUOTE:
11341 bufLen += len * 2;
11342 break;
11344 bufLen++;
11346 bufLen++;
11349 p = objPtr->bytes = Jim_Alloc(bufLen + 1);
11350 realLength = 0;
11351 for (i = 0; i < objc; i++) {
11352 int len, qlen;
11354 strRep = Jim_GetString(objv[i], &len);
11356 switch (quotingType[i]) {
11357 case JIM_ELESTR_SIMPLE:
11358 memcpy(p, strRep, len);
11359 p += len;
11360 realLength += len;
11361 break;
11362 case JIM_ELESTR_BRACE:
11363 *p++ = '{';
11364 memcpy(p, strRep, len);
11365 p += len;
11366 *p++ = '}';
11367 realLength += len + 2;
11368 break;
11369 case JIM_ELESTR_QUOTE:
11370 if (i == 0 && strRep[0] == '#') {
11371 *p++ = '\\';
11372 realLength++;
11374 qlen = BackslashQuoteString(strRep, len, p);
11375 p += qlen;
11376 realLength += qlen;
11377 break;
11380 if (i + 1 != objc) {
11381 *p++ = ' ';
11382 realLength++;
11385 *p = '\0';
11386 objPtr->length = realLength;
11388 if (quotingType != staticQuoting) {
11389 Jim_Free(quotingType);
11393 static void UpdateStringOfList(struct Jim_Obj *objPtr)
11395 JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
11398 static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
11400 struct JimParserCtx parser;
11401 const char *str;
11402 int strLen;
11403 Jim_Obj *fileNameObj;
11404 int linenr;
11406 if (objPtr->typePtr == &listObjType) {
11407 return JIM_OK;
11410 if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
11411 Jim_Obj **listObjPtrPtr;
11412 int len;
11413 int i;
11415 listObjPtrPtr = JimDictPairs(objPtr, &len);
11416 for (i = 0; i < len; i++) {
11417 Jim_IncrRefCount(listObjPtrPtr[i]);
11421 Jim_FreeIntRep(interp, objPtr);
11422 objPtr->typePtr = &listObjType;
11423 objPtr->internalRep.listValue.len = len;
11424 objPtr->internalRep.listValue.maxLen = len;
11425 objPtr->internalRep.listValue.ele = listObjPtrPtr;
11427 return JIM_OK;
11431 if (objPtr->typePtr == &sourceObjType) {
11432 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
11433 linenr = objPtr->internalRep.sourceValue.lineNumber;
11435 else {
11436 fileNameObj = interp->emptyObj;
11437 linenr = 1;
11439 Jim_IncrRefCount(fileNameObj);
11442 str = Jim_GetString(objPtr, &strLen);
11444 Jim_FreeIntRep(interp, objPtr);
11445 objPtr->typePtr = &listObjType;
11446 objPtr->internalRep.listValue.len = 0;
11447 objPtr->internalRep.listValue.maxLen = 0;
11448 objPtr->internalRep.listValue.ele = NULL;
11451 if (strLen) {
11452 JimParserInit(&parser, str, strLen, linenr);
11453 while (!parser.eof) {
11454 Jim_Obj *elementPtr;
11456 JimParseList(&parser);
11457 if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
11458 continue;
11459 elementPtr = JimParserGetTokenObj(interp, &parser);
11460 JimSetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
11461 ListAppendElement(objPtr, elementPtr);
11464 Jim_DecrRefCount(interp, fileNameObj);
11465 return JIM_OK;
11468 Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
11470 Jim_Obj *objPtr;
11472 objPtr = Jim_NewObj(interp);
11473 objPtr->typePtr = &listObjType;
11474 objPtr->bytes = NULL;
11475 objPtr->internalRep.listValue.ele = NULL;
11476 objPtr->internalRep.listValue.len = 0;
11477 objPtr->internalRep.listValue.maxLen = 0;
11479 if (len) {
11480 ListInsertElements(objPtr, 0, len, elements);
11483 return objPtr;
11486 static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
11487 Jim_Obj ***listVec)
11489 *listLen = Jim_ListLength(interp, listObj);
11490 *listVec = listObj->internalRep.listValue.ele;
11494 static int JimSign(jim_wide w)
11496 if (w == 0) {
11497 return 0;
11499 else if (w < 0) {
11500 return -1;
11502 return 1;
11506 struct lsort_info {
11507 jmp_buf jmpbuf;
11508 Jim_Obj *command;
11509 Jim_Interp *interp;
11510 enum {
11511 JIM_LSORT_ASCII,
11512 JIM_LSORT_NOCASE,
11513 JIM_LSORT_INTEGER,
11514 JIM_LSORT_REAL,
11515 JIM_LSORT_COMMAND
11516 } type;
11517 int order;
11518 int index;
11519 int indexed;
11520 int unique;
11521 int (*subfn)(Jim_Obj **, Jim_Obj **);
11524 static struct lsort_info *sort_info;
11526 static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11528 Jim_Obj *lObj, *rObj;
11530 if (Jim_ListIndex(sort_info->interp, *lhsObj, sort_info->index, &lObj, JIM_ERRMSG) != JIM_OK ||
11531 Jim_ListIndex(sort_info->interp, *rhsObj, sort_info->index, &rObj, JIM_ERRMSG) != JIM_OK) {
11532 longjmp(sort_info->jmpbuf, JIM_ERR);
11534 return sort_info->subfn(&lObj, &rObj);
11538 static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11540 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
11543 static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11545 return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
11548 static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11550 jim_wide lhs = 0, rhs = 0;
11552 if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11553 Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11554 longjmp(sort_info->jmpbuf, JIM_ERR);
11557 return JimSign(lhs - rhs) * sort_info->order;
11560 static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11562 double lhs = 0, rhs = 0;
11564 if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
11565 Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
11566 longjmp(sort_info->jmpbuf, JIM_ERR);
11568 if (lhs == rhs) {
11569 return 0;
11571 if (lhs > rhs) {
11572 return sort_info->order;
11574 return -sort_info->order;
11577 static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
11579 Jim_Obj *compare_script;
11580 int rc;
11582 jim_wide ret = 0;
11585 compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command);
11586 Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj);
11587 Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj);
11589 rc = Jim_EvalObj(sort_info->interp, compare_script);
11591 if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
11592 longjmp(sort_info->jmpbuf, rc);
11595 return JimSign(ret) * sort_info->order;
11598 static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
11600 int src;
11601 int dst = 0;
11602 Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
11604 for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
11605 if (comp(&ele[dst], &ele[src]) == 0) {
11607 Jim_DecrRefCount(sort_info->interp, ele[dst]);
11609 else {
11611 dst++;
11613 ele[dst] = ele[src];
11617 dst++;
11618 if (dst < listObjPtr->internalRep.listValue.len) {
11619 ele[dst] = ele[src];
11623 listObjPtr->internalRep.listValue.len = dst;
11627 static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info)
11629 struct lsort_info *prev_info;
11631 typedef int (qsort_comparator) (const void *, const void *);
11632 int (*fn) (Jim_Obj **, Jim_Obj **);
11633 Jim_Obj **vector;
11634 int len;
11635 int rc;
11637 JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
11638 SetListFromAny(interp, listObjPtr);
11641 prev_info = sort_info;
11642 sort_info = info;
11644 vector = listObjPtr->internalRep.listValue.ele;
11645 len = listObjPtr->internalRep.listValue.len;
11646 switch (info->type) {
11647 case JIM_LSORT_ASCII:
11648 fn = ListSortString;
11649 break;
11650 case JIM_LSORT_NOCASE:
11651 fn = ListSortStringNoCase;
11652 break;
11653 case JIM_LSORT_INTEGER:
11654 fn = ListSortInteger;
11655 break;
11656 case JIM_LSORT_REAL:
11657 fn = ListSortReal;
11658 break;
11659 case JIM_LSORT_COMMAND:
11660 fn = ListSortCommand;
11661 break;
11662 default:
11663 fn = NULL;
11664 JimPanic((1, "ListSort called with invalid sort type"));
11665 return -1;
11668 if (info->indexed) {
11670 info->subfn = fn;
11671 fn = ListSortIndexHelper;
11674 if ((rc = setjmp(info->jmpbuf)) == 0) {
11675 qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
11677 if (info->unique && len > 1) {
11678 ListRemoveDuplicates(listObjPtr, fn);
11681 Jim_InvalidateStringRep(listObjPtr);
11683 sort_info = prev_info;
11685 return rc;
11688 static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
11690 int currentLen = listPtr->internalRep.listValue.len;
11691 int requiredLen = currentLen + elemc;
11692 int i;
11693 Jim_Obj **point;
11695 if (requiredLen > listPtr->internalRep.listValue.maxLen) {
11696 if (requiredLen < 2) {
11698 requiredLen = 4;
11700 else {
11701 requiredLen *= 2;
11704 listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
11705 sizeof(Jim_Obj *) * requiredLen);
11707 listPtr->internalRep.listValue.maxLen = requiredLen;
11709 if (idx < 0) {
11710 idx = currentLen;
11712 point = listPtr->internalRep.listValue.ele + idx;
11713 memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
11714 for (i = 0; i < elemc; ++i) {
11715 point[i] = elemVec[i];
11716 Jim_IncrRefCount(point[i]);
11718 listPtr->internalRep.listValue.len += elemc;
11721 static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
11723 ListInsertElements(listPtr, -1, 1, &objPtr);
11726 static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11728 ListInsertElements(listPtr, -1,
11729 appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele);
11732 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
11734 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object"));
11735 SetListFromAny(interp, listPtr);
11736 Jim_InvalidateStringRep(listPtr);
11737 ListAppendElement(listPtr, objPtr);
11740 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
11742 JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object"));
11743 SetListFromAny(interp, listPtr);
11744 SetListFromAny(interp, appendListPtr);
11745 Jim_InvalidateStringRep(listPtr);
11746 ListAppendList(listPtr, appendListPtr);
11749 int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
11751 SetListFromAny(interp, objPtr);
11752 return objPtr->internalRep.listValue.len;
11755 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11756 int objc, Jim_Obj *const *objVec)
11758 JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
11759 SetListFromAny(interp, listPtr);
11760 if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
11761 idx = listPtr->internalRep.listValue.len;
11762 else if (idx < 0)
11763 idx = 0;
11764 Jim_InvalidateStringRep(listPtr);
11765 ListInsertElements(listPtr, idx, objc, objVec);
11768 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
11770 SetListFromAny(interp, listPtr);
11771 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11772 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11773 return NULL;
11775 if (idx < 0)
11776 idx = listPtr->internalRep.listValue.len + idx;
11777 return listPtr->internalRep.listValue.ele[idx];
11780 int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
11782 *objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
11783 if (*objPtrPtr == NULL) {
11784 if (flags & JIM_ERRMSG) {
11785 Jim_SetResultString(interp, "list index out of range", -1);
11787 return JIM_ERR;
11789 return JIM_OK;
11792 static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
11793 Jim_Obj *newObjPtr, int flags)
11795 SetListFromAny(interp, listPtr);
11796 if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
11797 (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
11798 if (flags & JIM_ERRMSG) {
11799 Jim_SetResultString(interp, "list index out of range", -1);
11801 return JIM_ERR;
11803 if (idx < 0)
11804 idx = listPtr->internalRep.listValue.len + idx;
11805 Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
11806 listPtr->internalRep.listValue.ele[idx] = newObjPtr;
11807 Jim_IncrRefCount(newObjPtr);
11808 return JIM_OK;
11811 int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
11812 Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
11814 Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
11815 int shared, i, idx;
11817 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
11818 if (objPtr == NULL)
11819 return JIM_ERR;
11820 if ((shared = Jim_IsShared(objPtr)))
11821 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
11822 for (i = 0; i < indexc - 1; i++) {
11823 listObjPtr = objPtr;
11824 if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
11825 goto err;
11826 if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_ERRMSG) != JIM_OK) {
11827 goto err;
11829 if (Jim_IsShared(objPtr)) {
11830 objPtr = Jim_DuplicateObj(interp, objPtr);
11831 ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
11833 Jim_InvalidateStringRep(listObjPtr);
11835 if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
11836 goto err;
11837 if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
11838 goto err;
11839 Jim_InvalidateStringRep(objPtr);
11840 Jim_InvalidateStringRep(varObjPtr);
11841 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK)
11842 goto err;
11843 Jim_SetResult(interp, varObjPtr);
11844 return JIM_OK;
11845 err:
11846 if (shared) {
11847 Jim_FreeNewObj(interp, varObjPtr);
11849 return JIM_ERR;
11852 Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
11854 int i;
11855 int listLen = Jim_ListLength(interp, listObjPtr);
11856 Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
11858 for (i = 0; i < listLen; ) {
11859 Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i));
11860 if (++i != listLen) {
11861 Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
11864 return resObjPtr;
11867 Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
11869 int i;
11871 for (i = 0; i < objc; i++) {
11872 if (!Jim_IsList(objv[i]))
11873 break;
11875 if (i == objc) {
11876 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
11878 for (i = 0; i < objc; i++)
11879 ListAppendList(objPtr, objv[i]);
11880 return objPtr;
11882 else {
11884 int len = 0, objLen;
11885 char *bytes, *p;
11888 for (i = 0; i < objc; i++) {
11889 len += Jim_Length(objv[i]);
11891 if (objc)
11892 len += objc - 1;
11894 p = bytes = Jim_Alloc(len + 1);
11895 for (i = 0; i < objc; i++) {
11896 const char *s = Jim_GetString(objv[i], &objLen);
11899 while (objLen && isspace(UCHAR(*s))) {
11900 s++;
11901 objLen--;
11902 len--;
11905 while (objLen && isspace(UCHAR(s[objLen - 1]))) {
11907 if (objLen > 1 && s[objLen - 2] == '\\') {
11908 break;
11910 objLen--;
11911 len--;
11913 memcpy(p, s, objLen);
11914 p += objLen;
11915 if (i + 1 != objc) {
11916 if (objLen)
11917 *p++ = ' ';
11918 else {
11919 len--;
11923 *p = '\0';
11924 return Jim_NewStringObjNoAlloc(interp, bytes, len);
11928 Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr,
11929 Jim_Obj *lastObjPtr)
11931 int first, last;
11932 int len, rangeLen;
11934 if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK ||
11935 Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK)
11936 return NULL;
11937 len = Jim_ListLength(interp, listObjPtr);
11938 first = JimRelToAbsIndex(len, first);
11939 last = JimRelToAbsIndex(len, last);
11940 JimRelToAbsRange(len, &first, &last, &rangeLen);
11941 if (first == 0 && last == len) {
11942 return listObjPtr;
11944 return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
11947 static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
11948 static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
11949 static void UpdateStringOfDict(struct Jim_Obj *objPtr);
11950 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
11953 static unsigned int JimObjectHTHashFunction(const void *key)
11955 int len;
11956 const char *str = Jim_GetString((Jim_Obj *)key, &len);
11957 return Jim_GenHashFunction((const unsigned char *)str, len);
11960 static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
11962 return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
11965 static void *JimObjectHTKeyValDup(void *privdata, const void *val)
11967 Jim_IncrRefCount((Jim_Obj *)val);
11968 return (void *)val;
11971 static void JimObjectHTKeyValDestructor(void *interp, void *val)
11973 Jim_DecrRefCount(interp, (Jim_Obj *)val);
11976 static const Jim_HashTableType JimDictHashTableType = {
11977 JimObjectHTHashFunction,
11978 JimObjectHTKeyValDup,
11979 JimObjectHTKeyValDup,
11980 JimObjectHTKeyCompare,
11981 JimObjectHTKeyValDestructor,
11982 JimObjectHTKeyValDestructor
11985 static const Jim_ObjType dictObjType = {
11986 "dict",
11987 FreeDictInternalRep,
11988 DupDictInternalRep,
11989 UpdateStringOfDict,
11990 JIM_TYPE_NONE,
11993 void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
11995 JIM_NOTUSED(interp);
11997 Jim_FreeHashTable(objPtr->internalRep.ptr);
11998 Jim_Free(objPtr->internalRep.ptr);
12001 void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
12003 Jim_HashTable *ht, *dupHt;
12004 Jim_HashTableIterator htiter;
12005 Jim_HashEntry *he;
12008 ht = srcPtr->internalRep.ptr;
12009 dupHt = Jim_Alloc(sizeof(*dupHt));
12010 Jim_InitHashTable(dupHt, &JimDictHashTableType, interp);
12011 if (ht->size != 0)
12012 Jim_ExpandHashTable(dupHt, ht->size);
12014 JimInitHashTableIterator(ht, &htiter);
12015 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
12016 Jim_AddHashEntry(dupHt, he->key, he->u.val);
12019 dupPtr->internalRep.ptr = dupHt;
12020 dupPtr->typePtr = &dictObjType;
12023 static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len)
12025 Jim_HashTable *ht;
12026 Jim_HashTableIterator htiter;
12027 Jim_HashEntry *he;
12028 Jim_Obj **objv;
12029 int i;
12031 ht = dictPtr->internalRep.ptr;
12034 objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *));
12035 JimInitHashTableIterator(ht, &htiter);
12036 i = 0;
12037 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
12038 objv[i++] = Jim_GetHashEntryKey(he);
12039 objv[i++] = Jim_GetHashEntryVal(he);
12041 *len = i;
12042 return objv;
12045 static void UpdateStringOfDict(struct Jim_Obj *objPtr)
12048 int len;
12049 Jim_Obj **objv = JimDictPairs(objPtr, &len);
12052 JimMakeListStringRep(objPtr, objv, len);
12054 Jim_Free(objv);
12057 static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
12059 int listlen;
12061 if (objPtr->typePtr == &dictObjType) {
12062 return JIM_OK;
12065 if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
12066 Jim_String(objPtr);
12070 listlen = Jim_ListLength(interp, objPtr);
12071 if (listlen % 2) {
12072 Jim_SetResultString(interp, "missing value to go with key", -1);
12073 return JIM_ERR;
12075 else {
12077 Jim_HashTable *ht;
12078 int i;
12080 ht = Jim_Alloc(sizeof(*ht));
12081 Jim_InitHashTable(ht, &JimDictHashTableType, interp);
12083 for (i = 0; i < listlen; i += 2) {
12084 Jim_Obj *keyObjPtr = Jim_ListGetIndex(interp, objPtr, i);
12085 Jim_Obj *valObjPtr = Jim_ListGetIndex(interp, objPtr, i + 1);
12087 Jim_ReplaceHashEntry(ht, keyObjPtr, valObjPtr);
12090 Jim_FreeIntRep(interp, objPtr);
12091 objPtr->typePtr = &dictObjType;
12092 objPtr->internalRep.ptr = ht;
12094 return JIM_OK;
12100 static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12101 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12103 Jim_HashTable *ht = objPtr->internalRep.ptr;
12105 if (valueObjPtr == NULL) {
12106 return Jim_DeleteHashEntry(ht, keyObjPtr);
12108 Jim_ReplaceHashEntry(ht, keyObjPtr, valueObjPtr);
12109 return JIM_OK;
12112 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
12113 Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
12115 JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
12116 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
12117 return JIM_ERR;
12119 Jim_InvalidateStringRep(objPtr);
12120 return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
12123 Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
12125 Jim_Obj *objPtr;
12126 int i;
12128 JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
12130 objPtr = Jim_NewObj(interp);
12131 objPtr->typePtr = &dictObjType;
12132 objPtr->bytes = NULL;
12133 objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
12134 Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp);
12135 for (i = 0; i < len; i += 2)
12136 DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
12137 return objPtr;
12140 int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
12141 Jim_Obj **objPtrPtr, int flags)
12143 Jim_HashEntry *he;
12144 Jim_HashTable *ht;
12146 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12147 return -1;
12149 ht = dictPtr->internalRep.ptr;
12150 if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) {
12151 if (flags & JIM_ERRMSG) {
12152 Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
12154 return JIM_ERR;
12156 else {
12157 *objPtrPtr = Jim_GetHashEntryVal(he);
12158 return JIM_OK;
12163 int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len)
12165 if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
12166 return JIM_ERR;
12168 *objPtrPtr = JimDictPairs(dictPtr, len);
12170 return JIM_OK;
12175 int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
12176 Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
12178 int i;
12180 if (keyc == 0) {
12181 *objPtrPtr = dictPtr;
12182 return JIM_OK;
12185 for (i = 0; i < keyc; i++) {
12186 Jim_Obj *objPtr;
12188 int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
12189 if (rc != JIM_OK) {
12190 return rc;
12192 dictPtr = objPtr;
12194 *objPtrPtr = dictPtr;
12195 return JIM_OK;
12198 int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
12199 Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags)
12201 Jim_Obj *varObjPtr, *objPtr, *dictObjPtr;
12202 int shared, i;
12204 varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, flags);
12205 if (objPtr == NULL) {
12206 if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
12208 return JIM_ERR;
12210 varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0);
12211 if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) {
12212 Jim_FreeNewObj(interp, varObjPtr);
12213 return JIM_ERR;
12216 if ((shared = Jim_IsShared(objPtr)))
12217 varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
12218 for (i = 0; i < keyc; i++) {
12219 dictObjPtr = objPtr;
12222 if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
12223 goto err;
12226 if (i == keyc - 1) {
12228 if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
12229 if (newObjPtr || (flags & JIM_MUSTEXIST)) {
12230 goto err;
12233 break;
12237 Jim_InvalidateStringRep(dictObjPtr);
12238 if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
12239 newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
12240 if (Jim_IsShared(objPtr)) {
12241 objPtr = Jim_DuplicateObj(interp, objPtr);
12242 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12245 else {
12246 if (newObjPtr == NULL) {
12247 goto err;
12249 objPtr = Jim_NewDictObj(interp, NULL, 0);
12250 DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
12254 Jim_InvalidateStringRep(objPtr);
12255 Jim_InvalidateStringRep(varObjPtr);
12256 if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
12257 goto err;
12259 Jim_SetResult(interp, varObjPtr);
12260 return JIM_OK;
12261 err:
12262 if (shared) {
12263 Jim_FreeNewObj(interp, varObjPtr);
12265 return JIM_ERR;
12268 static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
12269 static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
12271 static const Jim_ObjType indexObjType = {
12272 "index",
12273 NULL,
12274 NULL,
12275 UpdateStringOfIndex,
12276 JIM_TYPE_NONE,
12279 static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
12281 if (objPtr->internalRep.intValue == -1) {
12282 JimSetStringBytes(objPtr, "end");
12284 else {
12285 char buf[JIM_INTEGER_SPACE + 1];
12286 if (objPtr->internalRep.intValue >= 0) {
12287 sprintf(buf, "%d", objPtr->internalRep.intValue);
12289 else {
12291 sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
12293 JimSetStringBytes(objPtr, buf);
12297 static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12299 int idx, end = 0;
12300 const char *str;
12301 char *endptr;
12304 str = Jim_String(objPtr);
12307 if (strncmp(str, "end", 3) == 0) {
12308 end = 1;
12309 str += 3;
12310 idx = 0;
12312 else {
12313 idx = jim_strtol(str, &endptr);
12315 if (endptr == str) {
12316 goto badindex;
12318 str = endptr;
12322 if (*str == '+' || *str == '-') {
12323 int sign = (*str == '+' ? 1 : -1);
12325 idx += sign * jim_strtol(++str, &endptr);
12326 if (str == endptr || *endptr) {
12327 goto badindex;
12329 str = endptr;
12332 while (isspace(UCHAR(*str))) {
12333 str++;
12335 if (*str) {
12336 goto badindex;
12338 if (end) {
12339 if (idx > 0) {
12340 idx = INT_MAX;
12342 else {
12344 idx--;
12347 else if (idx < 0) {
12348 idx = -INT_MAX;
12352 Jim_FreeIntRep(interp, objPtr);
12353 objPtr->typePtr = &indexObjType;
12354 objPtr->internalRep.intValue = idx;
12355 return JIM_OK;
12357 badindex:
12358 Jim_SetResultFormatted(interp,
12359 "bad index \"%#s\": must be integer?[+-]integer? or end?[+-]integer?", objPtr);
12360 return JIM_ERR;
12363 int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
12366 if (objPtr->typePtr == &intObjType) {
12367 jim_wide val = JimWideValue(objPtr);
12369 if (val < 0)
12370 *indexPtr = -INT_MAX;
12371 else if (val > INT_MAX)
12372 *indexPtr = INT_MAX;
12373 else
12374 *indexPtr = (int)val;
12375 return JIM_OK;
12377 if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
12378 return JIM_ERR;
12379 *indexPtr = objPtr->internalRep.intValue;
12380 return JIM_OK;
12385 static const char * const jimReturnCodes[] = {
12386 "ok",
12387 "error",
12388 "return",
12389 "break",
12390 "continue",
12391 "signal",
12392 "exit",
12393 "eval",
12394 NULL
12397 #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1)
12399 static const Jim_ObjType returnCodeObjType = {
12400 "return-code",
12401 NULL,
12402 NULL,
12403 NULL,
12404 JIM_TYPE_NONE,
12407 const char *Jim_ReturnCode(int code)
12409 if (code < 0 || code >= (int)jimReturnCodesSize) {
12410 return "?";
12412 else {
12413 return jimReturnCodes[code];
12417 static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
12419 int returnCode;
12420 jim_wide wideValue;
12423 if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
12424 returnCode = (int)wideValue;
12425 else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
12426 Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr);
12427 return JIM_ERR;
12430 Jim_FreeIntRep(interp, objPtr);
12431 objPtr->typePtr = &returnCodeObjType;
12432 objPtr->internalRep.intValue = returnCode;
12433 return JIM_OK;
12436 int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
12438 if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
12439 return JIM_ERR;
12440 *intPtr = objPtr->internalRep.intValue;
12441 return JIM_OK;
12444 static int JimParseExprOperator(struct JimParserCtx *pc);
12445 static int JimParseExprNumber(struct JimParserCtx *pc);
12446 static int JimParseExprIrrational(struct JimParserCtx *pc);
12447 static int JimParseExprBoolean(struct JimParserCtx *pc);
12450 enum
12455 JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
12456 JIM_EXPROP_DIV,
12457 JIM_EXPROP_MOD,
12458 JIM_EXPROP_SUB,
12459 JIM_EXPROP_ADD,
12460 JIM_EXPROP_LSHIFT,
12461 JIM_EXPROP_RSHIFT,
12462 JIM_EXPROP_ROTL,
12463 JIM_EXPROP_ROTR,
12464 JIM_EXPROP_LT,
12465 JIM_EXPROP_GT,
12466 JIM_EXPROP_LTE,
12467 JIM_EXPROP_GTE,
12468 JIM_EXPROP_NUMEQ,
12469 JIM_EXPROP_NUMNE,
12470 JIM_EXPROP_BITAND,
12471 JIM_EXPROP_BITXOR,
12472 JIM_EXPROP_BITOR,
12473 JIM_EXPROP_LOGICAND,
12474 JIM_EXPROP_LOGICOR,
12475 JIM_EXPROP_TERNARY,
12476 JIM_EXPROP_COLON,
12477 JIM_EXPROP_POW,
12480 JIM_EXPROP_STREQ,
12481 JIM_EXPROP_STRNE,
12482 JIM_EXPROP_STRIN,
12483 JIM_EXPROP_STRNI,
12486 JIM_EXPROP_NOT,
12487 JIM_EXPROP_BITNOT,
12488 JIM_EXPROP_UNARYMINUS,
12489 JIM_EXPROP_UNARYPLUS,
12492 JIM_EXPROP_FUNC_INT,
12493 JIM_EXPROP_FUNC_WIDE,
12494 JIM_EXPROP_FUNC_ABS,
12495 JIM_EXPROP_FUNC_DOUBLE,
12496 JIM_EXPROP_FUNC_ROUND,
12497 JIM_EXPROP_FUNC_RAND,
12498 JIM_EXPROP_FUNC_SRAND,
12501 JIM_EXPROP_FUNC_SIN,
12502 JIM_EXPROP_FUNC_COS,
12503 JIM_EXPROP_FUNC_TAN,
12504 JIM_EXPROP_FUNC_ASIN,
12505 JIM_EXPROP_FUNC_ACOS,
12506 JIM_EXPROP_FUNC_ATAN,
12507 JIM_EXPROP_FUNC_ATAN2,
12508 JIM_EXPROP_FUNC_SINH,
12509 JIM_EXPROP_FUNC_COSH,
12510 JIM_EXPROP_FUNC_TANH,
12511 JIM_EXPROP_FUNC_CEIL,
12512 JIM_EXPROP_FUNC_FLOOR,
12513 JIM_EXPROP_FUNC_EXP,
12514 JIM_EXPROP_FUNC_LOG,
12515 JIM_EXPROP_FUNC_LOG10,
12516 JIM_EXPROP_FUNC_SQRT,
12517 JIM_EXPROP_FUNC_POW,
12518 JIM_EXPROP_FUNC_HYPOT,
12519 JIM_EXPROP_FUNC_FMOD,
12522 struct JimExprNode {
12523 int type;
12524 struct Jim_Obj *objPtr;
12526 struct JimExprNode *left;
12527 struct JimExprNode *right;
12528 struct JimExprNode *ternary;
12532 typedef struct Jim_ExprOperator
12534 const char *name;
12535 int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode);
12536 unsigned char precedence;
12537 unsigned char arity;
12538 unsigned char attr;
12539 unsigned char namelen;
12540 } Jim_ExprOperator;
12542 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr);
12543 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
12544 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);
12546 static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
12548 int intresult = 1;
12549 int rc;
12550 double dA, dC = 0;
12551 jim_wide wA, wC = 0;
12552 Jim_Obj *A;
12554 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
12555 return rc;
12558 if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
12559 switch (node->type) {
12560 case JIM_EXPROP_FUNC_INT:
12561 case JIM_EXPROP_FUNC_WIDE:
12562 case JIM_EXPROP_FUNC_ROUND:
12563 case JIM_EXPROP_UNARYPLUS:
12564 wC = wA;
12565 break;
12566 case JIM_EXPROP_FUNC_DOUBLE:
12567 dC = wA;
12568 intresult = 0;
12569 break;
12570 case JIM_EXPROP_FUNC_ABS:
12571 wC = wA >= 0 ? wA : -wA;
12572 break;
12573 case JIM_EXPROP_UNARYMINUS:
12574 wC = -wA;
12575 break;
12576 case JIM_EXPROP_NOT:
12577 wC = !wA;
12578 break;
12579 default:
12580 abort();
12583 else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
12584 switch (node->type) {
12585 case JIM_EXPROP_FUNC_INT:
12586 case JIM_EXPROP_FUNC_WIDE:
12587 wC = dA;
12588 break;
12589 case JIM_EXPROP_FUNC_ROUND:
12590 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
12591 break;
12592 case JIM_EXPROP_FUNC_DOUBLE:
12593 case JIM_EXPROP_UNARYPLUS:
12594 dC = dA;
12595 intresult = 0;
12596 break;
12597 case JIM_EXPROP_FUNC_ABS:
12598 #ifdef JIM_MATH_FUNCTIONS
12599 dC = fabs(dA);
12600 #else
12601 dC = dA >= 0 ? dA : -dA;
12602 #endif
12603 intresult = 0;
12604 break;
12605 case JIM_EXPROP_UNARYMINUS:
12606 dC = -dA;
12607 intresult = 0;
12608 break;
12609 case JIM_EXPROP_NOT:
12610 wC = !dA;
12611 break;
12612 default:
12613 abort();
12617 if (rc == JIM_OK) {
12618 if (intresult) {
12619 Jim_SetResultInt(interp, wC);
12621 else {
12622 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
12626 Jim_DecrRefCount(interp, A);
12628 return rc;
12631 static double JimRandDouble(Jim_Interp *interp)
12633 unsigned long x;
12634 JimRandomBytes(interp, &x, sizeof(x));
12636 return (double)x / (unsigned long)~0;
12639 static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node)
12641 jim_wide wA;
12642 Jim_Obj *A;
12643 int rc;
12645 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
12646 return rc;
12649 rc = Jim_GetWide(interp, A, &wA);
12650 if (rc == JIM_OK) {
12651 switch (node->type) {
12652 case JIM_EXPROP_BITNOT:
12653 Jim_SetResultInt(interp, ~wA);
12654 break;
12655 case JIM_EXPROP_FUNC_SRAND:
12656 JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
12657 Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12658 break;
12659 default:
12660 abort();
12664 Jim_DecrRefCount(interp, A);
12666 return rc;
12669 static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node)
12671 JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
12673 Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
12675 return JIM_OK;
12678 #ifdef JIM_MATH_FUNCTIONS
12679 static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node)
12681 int rc;
12682 double dA, dC;
12683 Jim_Obj *A;
12685 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
12686 return rc;
12689 rc = Jim_GetDouble(interp, A, &dA);
12690 if (rc == JIM_OK) {
12691 switch (node->type) {
12692 case JIM_EXPROP_FUNC_SIN:
12693 dC = sin(dA);
12694 break;
12695 case JIM_EXPROP_FUNC_COS:
12696 dC = cos(dA);
12697 break;
12698 case JIM_EXPROP_FUNC_TAN:
12699 dC = tan(dA);
12700 break;
12701 case JIM_EXPROP_FUNC_ASIN:
12702 dC = asin(dA);
12703 break;
12704 case JIM_EXPROP_FUNC_ACOS:
12705 dC = acos(dA);
12706 break;
12707 case JIM_EXPROP_FUNC_ATAN:
12708 dC = atan(dA);
12709 break;
12710 case JIM_EXPROP_FUNC_SINH:
12711 dC = sinh(dA);
12712 break;
12713 case JIM_EXPROP_FUNC_COSH:
12714 dC = cosh(dA);
12715 break;
12716 case JIM_EXPROP_FUNC_TANH:
12717 dC = tanh(dA);
12718 break;
12719 case JIM_EXPROP_FUNC_CEIL:
12720 dC = ceil(dA);
12721 break;
12722 case JIM_EXPROP_FUNC_FLOOR:
12723 dC = floor(dA);
12724 break;
12725 case JIM_EXPROP_FUNC_EXP:
12726 dC = exp(dA);
12727 break;
12728 case JIM_EXPROP_FUNC_LOG:
12729 dC = log(dA);
12730 break;
12731 case JIM_EXPROP_FUNC_LOG10:
12732 dC = log10(dA);
12733 break;
12734 case JIM_EXPROP_FUNC_SQRT:
12735 dC = sqrt(dA);
12736 break;
12737 default:
12738 abort();
12740 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
12743 Jim_DecrRefCount(interp, A);
12745 return rc;
12747 #endif
12750 static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node)
12752 jim_wide wA, wB;
12753 int rc;
12754 Jim_Obj *A, *B;
12756 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
12757 return rc;
12759 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
12760 Jim_DecrRefCount(interp, A);
12761 return rc;
12764 rc = JIM_ERR;
12766 if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
12767 jim_wide wC;
12769 rc = JIM_OK;
12771 switch (node->type) {
12772 case JIM_EXPROP_LSHIFT:
12773 wC = wA << wB;
12774 break;
12775 case JIM_EXPROP_RSHIFT:
12776 wC = wA >> wB;
12777 break;
12778 case JIM_EXPROP_BITAND:
12779 wC = wA & wB;
12780 break;
12781 case JIM_EXPROP_BITXOR:
12782 wC = wA ^ wB;
12783 break;
12784 case JIM_EXPROP_BITOR:
12785 wC = wA | wB;
12786 break;
12787 case JIM_EXPROP_MOD:
12788 if (wB == 0) {
12789 wC = 0;
12790 Jim_SetResultString(interp, "Division by zero", -1);
12791 rc = JIM_ERR;
12793 else {
12794 int negative = 0;
12796 if (wB < 0) {
12797 wB = -wB;
12798 wA = -wA;
12799 negative = 1;
12801 wC = wA % wB;
12802 if (wC < 0) {
12803 wC += wB;
12805 if (negative) {
12806 wC = -wC;
12809 break;
12810 case JIM_EXPROP_ROTL:
12811 case JIM_EXPROP_ROTR:{
12813 unsigned long uA = (unsigned long)wA;
12814 unsigned long uB = (unsigned long)wB;
12815 const unsigned int S = sizeof(unsigned long) * 8;
12818 uB %= S;
12820 if (node->type == JIM_EXPROP_ROTR) {
12821 uB = S - uB;
12823 wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
12824 break;
12826 default:
12827 abort();
12829 Jim_SetResultInt(interp, wC);
12832 Jim_DecrRefCount(interp, A);
12833 Jim_DecrRefCount(interp, B);
12835 return rc;
12840 static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node)
12842 int rc = JIM_OK;
12843 double dA, dB, dC = 0;
12844 jim_wide wA, wB, wC = 0;
12845 Jim_Obj *A, *B;
12847 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
12848 return rc;
12850 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
12851 Jim_DecrRefCount(interp, A);
12852 return rc;
12855 if ((A->typePtr != &doubleObjType || A->bytes) &&
12856 (B->typePtr != &doubleObjType || B->bytes) &&
12857 JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
12861 switch (node->type) {
12862 case JIM_EXPROP_POW:
12863 case JIM_EXPROP_FUNC_POW:
12864 if (wA == 0 && wB < 0) {
12865 Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
12866 rc = JIM_ERR;
12867 goto done;
12869 wC = JimPowWide(wA, wB);
12870 goto intresult;
12871 case JIM_EXPROP_ADD:
12872 wC = wA + wB;
12873 goto intresult;
12874 case JIM_EXPROP_SUB:
12875 wC = wA - wB;
12876 goto intresult;
12877 case JIM_EXPROP_MUL:
12878 wC = wA * wB;
12879 goto intresult;
12880 case JIM_EXPROP_DIV:
12881 if (wB == 0) {
12882 Jim_SetResultString(interp, "Division by zero", -1);
12883 rc = JIM_ERR;
12884 goto done;
12886 else {
12887 if (wB < 0) {
12888 wB = -wB;
12889 wA = -wA;
12891 wC = wA / wB;
12892 if (wA % wB < 0) {
12893 wC--;
12895 goto intresult;
12897 case JIM_EXPROP_LT:
12898 wC = wA < wB;
12899 goto intresult;
12900 case JIM_EXPROP_GT:
12901 wC = wA > wB;
12902 goto intresult;
12903 case JIM_EXPROP_LTE:
12904 wC = wA <= wB;
12905 goto intresult;
12906 case JIM_EXPROP_GTE:
12907 wC = wA >= wB;
12908 goto intresult;
12909 case JIM_EXPROP_NUMEQ:
12910 wC = wA == wB;
12911 goto intresult;
12912 case JIM_EXPROP_NUMNE:
12913 wC = wA != wB;
12914 goto intresult;
12917 if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
12918 switch (node->type) {
12919 #ifndef JIM_MATH_FUNCTIONS
12920 case JIM_EXPROP_POW:
12921 case JIM_EXPROP_FUNC_POW:
12922 case JIM_EXPROP_FUNC_ATAN2:
12923 case JIM_EXPROP_FUNC_HYPOT:
12924 case JIM_EXPROP_FUNC_FMOD:
12925 Jim_SetResultString(interp, "unsupported", -1);
12926 rc = JIM_ERR;
12927 goto done;
12928 #else
12929 case JIM_EXPROP_POW:
12930 case JIM_EXPROP_FUNC_POW:
12931 dC = pow(dA, dB);
12932 goto doubleresult;
12933 case JIM_EXPROP_FUNC_ATAN2:
12934 dC = atan2(dA, dB);
12935 goto doubleresult;
12936 case JIM_EXPROP_FUNC_HYPOT:
12937 dC = hypot(dA, dB);
12938 goto doubleresult;
12939 case JIM_EXPROP_FUNC_FMOD:
12940 dC = fmod(dA, dB);
12941 goto doubleresult;
12942 #endif
12943 case JIM_EXPROP_ADD:
12944 dC = dA + dB;
12945 goto doubleresult;
12946 case JIM_EXPROP_SUB:
12947 dC = dA - dB;
12948 goto doubleresult;
12949 case JIM_EXPROP_MUL:
12950 dC = dA * dB;
12951 goto doubleresult;
12952 case JIM_EXPROP_DIV:
12953 if (dB == 0) {
12954 #ifdef INFINITY
12955 dC = dA < 0 ? -INFINITY : INFINITY;
12956 #else
12957 dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
12958 #endif
12960 else {
12961 dC = dA / dB;
12963 goto doubleresult;
12964 case JIM_EXPROP_LT:
12965 wC = dA < dB;
12966 goto intresult;
12967 case JIM_EXPROP_GT:
12968 wC = dA > dB;
12969 goto intresult;
12970 case JIM_EXPROP_LTE:
12971 wC = dA <= dB;
12972 goto intresult;
12973 case JIM_EXPROP_GTE:
12974 wC = dA >= dB;
12975 goto intresult;
12976 case JIM_EXPROP_NUMEQ:
12977 wC = dA == dB;
12978 goto intresult;
12979 case JIM_EXPROP_NUMNE:
12980 wC = dA != dB;
12981 goto intresult;
12984 else {
12988 int i = Jim_StringCompareObj(interp, A, B, 0);
12990 switch (node->type) {
12991 case JIM_EXPROP_LT:
12992 wC = i < 0;
12993 goto intresult;
12994 case JIM_EXPROP_GT:
12995 wC = i > 0;
12996 goto intresult;
12997 case JIM_EXPROP_LTE:
12998 wC = i <= 0;
12999 goto intresult;
13000 case JIM_EXPROP_GTE:
13001 wC = i >= 0;
13002 goto intresult;
13003 case JIM_EXPROP_NUMEQ:
13004 wC = i == 0;
13005 goto intresult;
13006 case JIM_EXPROP_NUMNE:
13007 wC = i != 0;
13008 goto intresult;
13012 rc = JIM_ERR;
13013 done:
13014 Jim_DecrRefCount(interp, A);
13015 Jim_DecrRefCount(interp, B);
13016 return rc;
13017 intresult:
13018 Jim_SetResultInt(interp, wC);
13019 goto done;
13020 doubleresult:
13021 Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
13022 goto done;
13025 static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
13027 int listlen;
13028 int i;
13030 listlen = Jim_ListLength(interp, listObjPtr);
13031 for (i = 0; i < listlen; i++) {
13032 if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
13033 return 1;
13036 return 0;
13041 static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
13043 Jim_Obj *A, *B;
13044 jim_wide wC;
13045 int rc;
13047 if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
13048 return rc;
13050 if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
13051 Jim_DecrRefCount(interp, A);
13052 return rc;
13055 switch (node->type) {
13056 case JIM_EXPROP_STREQ:
13057 case JIM_EXPROP_STRNE:
13058 wC = Jim_StringEqObj(A, B);
13059 if (node->type == JIM_EXPROP_STRNE) {
13060 wC = !wC;
13062 break;
13063 case JIM_EXPROP_STRIN:
13064 wC = JimSearchList(interp, B, A);
13065 break;
13066 case JIM_EXPROP_STRNI:
13067 wC = !JimSearchList(interp, B, A);
13068 break;
13069 default:
13070 abort();
13072 Jim_SetResultInt(interp, wC);
13074 Jim_DecrRefCount(interp, A);
13075 Jim_DecrRefCount(interp, B);
13077 return rc;
13080 static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
13082 long l;
13083 double d;
13084 int b;
13085 int ret = -1;
13088 Jim_IncrRefCount(obj);
13090 if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
13091 ret = (l != 0);
13093 else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
13094 ret = (d != 0);
13096 else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
13097 ret = (b != 0);
13100 Jim_DecrRefCount(interp, obj);
13101 return ret;
13104 static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node)
13107 int result = JimExprGetTermBoolean(interp, node->left);
13109 if (result == 1) {
13111 result = JimExprGetTermBoolean(interp, node->right);
13113 if (result == -1) {
13114 return JIM_ERR;
13116 Jim_SetResultInt(interp, result);
13117 return JIM_OK;
13120 static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node)
13123 int result = JimExprGetTermBoolean(interp, node->left);
13125 if (result == 0) {
13127 result = JimExprGetTermBoolean(interp, node->right);
13129 if (result == -1) {
13130 return JIM_ERR;
13132 Jim_SetResultInt(interp, result);
13133 return JIM_OK;
13136 static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node)
13139 int result = JimExprGetTermBoolean(interp, node->left);
13141 if (result == 1) {
13143 return JimExprEvalTermNode(interp, node->right);
13145 else if (result == 0) {
13147 return JimExprEvalTermNode(interp, node->ternary);
13150 return JIM_ERR;
13153 enum
13155 OP_FUNC = 0x0001,
13156 OP_RIGHT_ASSOC = 0x0002,
13159 #define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
13160 #define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0)
13162 static const struct Jim_ExprOperator Jim_ExprOperators[] = {
13163 OPRINIT("*", 110, 2, JimExprOpBin),
13164 OPRINIT("/", 110, 2, JimExprOpBin),
13165 OPRINIT("%", 110, 2, JimExprOpIntBin),
13167 OPRINIT("-", 100, 2, JimExprOpBin),
13168 OPRINIT("+", 100, 2, JimExprOpBin),
13170 OPRINIT("<<", 90, 2, JimExprOpIntBin),
13171 OPRINIT(">>", 90, 2, JimExprOpIntBin),
13173 OPRINIT("<<<", 90, 2, JimExprOpIntBin),
13174 OPRINIT(">>>", 90, 2, JimExprOpIntBin),
13176 OPRINIT("<", 80, 2, JimExprOpBin),
13177 OPRINIT(">", 80, 2, JimExprOpBin),
13178 OPRINIT("<=", 80, 2, JimExprOpBin),
13179 OPRINIT(">=", 80, 2, JimExprOpBin),
13181 OPRINIT("==", 70, 2, JimExprOpBin),
13182 OPRINIT("!=", 70, 2, JimExprOpBin),
13184 OPRINIT("&", 50, 2, JimExprOpIntBin),
13185 OPRINIT("^", 49, 2, JimExprOpIntBin),
13186 OPRINIT("|", 48, 2, JimExprOpIntBin),
13188 OPRINIT("&&", 10, 2, JimExprOpAnd),
13189 OPRINIT("||", 9, 2, JimExprOpOr),
13190 OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC),
13191 OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC),
13194 OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),
13196 OPRINIT("eq", 60, 2, JimExprOpStrBin),
13197 OPRINIT("ne", 60, 2, JimExprOpStrBin),
13199 OPRINIT("in", 55, 2, JimExprOpStrBin),
13200 OPRINIT("ni", 55, 2, JimExprOpStrBin),
13202 OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
13203 OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
13204 OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
13205 OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
13209 OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC),
13210 OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC),
13211 OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC),
13212 OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC),
13213 OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC),
13214 OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC),
13215 OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC),
13217 #ifdef JIM_MATH_FUNCTIONS
13218 OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13219 OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13220 OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13221 OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13222 OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13223 OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13224 OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC),
13225 OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13226 OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13227 OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13228 OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13229 OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13230 OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13231 OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13232 OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13233 OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
13234 OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC),
13235 OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC),
13236 OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC),
13237 #endif
13239 #undef OPRINIT
13240 #undef OPRINIT_ATTR
13242 #define JIM_EXPR_OPERATORS_NUM \
13243 (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
13245 static int JimParseExpression(struct JimParserCtx *pc)
13248 while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
13249 if (*pc->p == '\n') {
13250 pc->linenr++;
13252 pc->p++;
13253 pc->len--;
13257 pc->tline = pc->linenr;
13258 pc->tstart = pc->p;
13260 if (pc->len == 0) {
13261 pc->tend = pc->p;
13262 pc->tt = JIM_TT_EOL;
13263 pc->eof = 1;
13264 return JIM_OK;
13266 switch (*(pc->p)) {
13267 case '(':
13268 pc->tt = JIM_TT_SUBEXPR_START;
13269 goto singlechar;
13270 case ')':
13271 pc->tt = JIM_TT_SUBEXPR_END;
13272 goto singlechar;
13273 case ',':
13274 pc->tt = JIM_TT_SUBEXPR_COMMA;
13275 singlechar:
13276 pc->tend = pc->p;
13277 pc->p++;
13278 pc->len--;
13279 break;
13280 case '[':
13281 return JimParseCmd(pc);
13282 case '$':
13283 if (JimParseVar(pc) == JIM_ERR)
13284 return JimParseExprOperator(pc);
13285 else {
13287 if (pc->tt == JIM_TT_EXPRSUGAR) {
13288 return JIM_ERR;
13290 return JIM_OK;
13292 break;
13293 case '0':
13294 case '1':
13295 case '2':
13296 case '3':
13297 case '4':
13298 case '5':
13299 case '6':
13300 case '7':
13301 case '8':
13302 case '9':
13303 case '.':
13304 return JimParseExprNumber(pc);
13305 case '"':
13306 return JimParseQuote(pc);
13307 case '{':
13308 return JimParseBrace(pc);
13310 case 'N':
13311 case 'I':
13312 case 'n':
13313 case 'i':
13314 if (JimParseExprIrrational(pc) == JIM_ERR)
13315 if (JimParseExprBoolean(pc) == JIM_ERR)
13316 return JimParseExprOperator(pc);
13317 break;
13318 case 't':
13319 case 'f':
13320 case 'o':
13321 case 'y':
13322 if (JimParseExprBoolean(pc) == JIM_ERR)
13323 return JimParseExprOperator(pc);
13324 break;
13325 default:
13326 return JimParseExprOperator(pc);
13327 break;
13329 return JIM_OK;
13332 static int JimParseExprNumber(struct JimParserCtx *pc)
13334 char *end;
13337 pc->tt = JIM_TT_EXPR_INT;
13339 jim_strtoull(pc->p, (char **)&pc->p);
13341 if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
13342 if (strtod(pc->tstart, &end)) { }
13343 if (end == pc->tstart)
13344 return JIM_ERR;
13345 if (end > pc->p) {
13347 pc->tt = JIM_TT_EXPR_DOUBLE;
13348 pc->p = end;
13351 pc->tend = pc->p - 1;
13352 pc->len -= (pc->p - pc->tstart);
13353 return JIM_OK;
13356 static int JimParseExprIrrational(struct JimParserCtx *pc)
13358 const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
13359 int i;
13361 for (i = 0; irrationals[i]; i++) {
13362 const char *irr = irrationals[i];
13364 if (strncmp(irr, pc->p, 3) == 0) {
13365 pc->p += 3;
13366 pc->len -= 3;
13367 pc->tend = pc->p - 1;
13368 pc->tt = JIM_TT_EXPR_DOUBLE;
13369 return JIM_OK;
13372 return JIM_ERR;
13375 static int JimParseExprBoolean(struct JimParserCtx *pc)
13377 const char *booleans[] = { "false", "no", "off", "true", "yes", "on", NULL };
13378 const int lengths[] = { 5, 2, 3, 4, 3, 2, 0 };
13379 int i;
13381 for (i = 0; booleans[i]; i++) {
13382 const char *boolean = booleans[i];
13383 int length = lengths[i];
13385 if (strncmp(boolean, pc->p, length) == 0) {
13386 pc->p += length;
13387 pc->len -= length;
13388 pc->tend = pc->p - 1;
13389 pc->tt = JIM_TT_EXPR_BOOLEAN;
13390 return JIM_OK;
13393 return JIM_ERR;
13396 static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
13398 static Jim_ExprOperator dummy_op;
13399 if (opcode < JIM_TT_EXPR_OP) {
13400 return &dummy_op;
13402 return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
13405 static int JimParseExprOperator(struct JimParserCtx *pc)
13407 int i;
13408 const struct Jim_ExprOperator *bestOp = NULL;
13409 int bestLen = 0;
13412 for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
13413 const struct Jim_ExprOperator *op = &Jim_ExprOperators[i];
13415 if (op->name[0] != pc->p[0]) {
13416 continue;
13419 if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) {
13420 bestOp = op;
13421 bestLen = op->namelen;
13424 if (bestOp == NULL) {
13425 return JIM_ERR;
13429 if (bestOp->attr & OP_FUNC) {
13430 const char *p = pc->p + bestLen;
13431 int len = pc->len - bestLen;
13433 while (len && isspace(UCHAR(*p))) {
13434 len--;
13435 p++;
13437 if (*p != '(') {
13438 return JIM_ERR;
13441 pc->tend = pc->p + bestLen - 1;
13442 pc->p += bestLen;
13443 pc->len -= bestLen;
13445 pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
13446 return JIM_OK;
13449 const char *jim_tt_name(int type)
13451 static const char * const tt_names[JIM_TT_EXPR_OP] =
13452 { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT",
13453 "DBL", "BOO", "$()" };
13454 if (type < JIM_TT_EXPR_OP) {
13455 return tt_names[type];
13457 else if (type == JIM_EXPROP_UNARYMINUS) {
13458 return "-VE";
13460 else if (type == JIM_EXPROP_UNARYPLUS) {
13461 return "+VE";
13463 else {
13464 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(type);
13465 static char buf[20];
13467 if (op->name) {
13468 return op->name;
13470 sprintf(buf, "(%d)", type);
13471 return buf;
13475 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
13476 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
13477 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
13479 static const Jim_ObjType exprObjType = {
13480 "expression",
13481 FreeExprInternalRep,
13482 DupExprInternalRep,
13483 NULL,
13484 JIM_TYPE_REFERENCES,
13488 struct ExprTree
13490 struct JimExprNode *expr;
13491 struct JimExprNode *nodes;
13492 int len;
13493 int inUse;
13496 static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num)
13498 int i;
13499 for (i = 0; i < num; i++) {
13500 if (nodes[i].objPtr) {
13501 Jim_DecrRefCount(interp, nodes[i].objPtr);
13504 Jim_Free(nodes);
13507 static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr)
13509 ExprTreeFreeNodes(interp, expr->nodes, expr->len);
13510 Jim_Free(expr);
13513 static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
13515 struct ExprTree *expr = (void *)objPtr->internalRep.ptr;
13517 if (expr) {
13518 if (--expr->inUse != 0) {
13519 return;
13522 ExprTreeFree(interp, expr);
13526 static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
13528 JIM_NOTUSED(interp);
13529 JIM_NOTUSED(srcPtr);
13532 dupPtr->typePtr = NULL;
13535 struct ExprBuilder {
13536 int parencount;
13537 int level;
13538 ParseToken *token;
13539 ParseToken *first_token;
13540 Jim_Stack stack;
13541 Jim_Obj *exprObjPtr;
13542 Jim_Obj *fileNameObj;
13543 struct JimExprNode *nodes;
13544 struct JimExprNode *next;
13547 #ifdef DEBUG_SHOW_EXPR
13548 static void JimShowExprNode(struct JimExprNode *node, int level)
13550 int i;
13551 for (i = 0; i < level; i++) {
13552 printf(" ");
13554 if (TOKEN_IS_EXPR_OP(node->type)) {
13555 printf("%s\n", jim_tt_name(node->type));
13556 if (node->left) {
13557 JimShowExprNode(node->left, level + 1);
13559 if (node->right) {
13560 JimShowExprNode(node->right, level + 1);
13562 if (node->ternary) {
13563 JimShowExprNode(node->ternary, level + 1);
13566 else {
13567 printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr));
13570 #endif
13572 #define EXPR_UNTIL_CLOSE 0x0001
13573 #define EXPR_FUNC_ARGS 0x0002
13574 #define EXPR_TERNARY 0x0004
13576 static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms)
13578 int rc;
13579 struct JimExprNode *node;
13581 int exp_stacklen = builder->stack.len + exp_numterms;
13583 if (builder->level++ > 200) {
13584 Jim_SetResultString(interp, "Expression too complex", -1);
13585 return JIM_ERR;
13588 while (builder->token->type != JIM_TT_EOL) {
13589 ParseToken *t = builder->token++;
13590 int prevtt;
13592 if (t == builder->first_token) {
13593 prevtt = JIM_TT_NONE;
13595 else {
13596 prevtt = t[-1].type;
13599 if (t->type == JIM_TT_SUBEXPR_START) {
13600 if (builder->stack.len == exp_stacklen) {
13601 Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr);
13602 return JIM_ERR;
13604 builder->parencount++;
13605 rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1);
13606 if (rc != JIM_OK) {
13607 return rc;
13611 else if (t->type == JIM_TT_SUBEXPR_END) {
13612 if (!(flags & EXPR_UNTIL_CLOSE)) {
13613 if (builder->stack.len == exp_stacklen && builder->level > 1) {
13614 builder->token--;
13615 builder->level--;
13616 return JIM_OK;
13618 Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr);
13619 return JIM_ERR;
13621 builder->parencount--;
13622 if (builder->stack.len == exp_stacklen) {
13624 break;
13627 else if (t->type == JIM_TT_SUBEXPR_COMMA) {
13628 if (!(flags & EXPR_FUNC_ARGS)) {
13629 if (builder->stack.len == exp_stacklen) {
13631 builder->token--;
13632 builder->level--;
13633 return JIM_OK;
13635 Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr);
13636 return JIM_ERR;
13638 else {
13640 if (builder->stack.len > exp_stacklen) {
13641 Jim_SetResultFormatted(interp, "too many arguments to math function");
13642 return JIM_ERR;
13647 else if (t->type == JIM_EXPROP_COLON) {
13648 if (!(flags & EXPR_TERNARY)) {
13649 if (builder->level != 1) {
13651 builder->token--;
13652 builder->level--;
13653 return JIM_OK;
13655 Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr);
13656 return JIM_ERR;
13658 if (builder->stack.len == exp_stacklen) {
13660 builder->token--;
13661 builder->level--;
13662 return JIM_OK;
13666 else if (TOKEN_IS_EXPR_OP(t->type)) {
13667 const struct Jim_ExprOperator *op;
13670 if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) {
13671 if (t->type == JIM_EXPROP_SUB) {
13672 t->type = JIM_EXPROP_UNARYMINUS;
13674 else if (t->type == JIM_EXPROP_ADD) {
13675 t->type = JIM_EXPROP_UNARYPLUS;
13679 op = JimExprOperatorInfoByOpcode(t->type);
13681 if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) {
13683 builder->token--;
13684 break;
13687 if (op->attr & OP_FUNC) {
13688 if (builder->token->type != JIM_TT_SUBEXPR_START) {
13689 Jim_SetResultString(interp, "missing arguments for math function", -1);
13690 return JIM_ERR;
13692 builder->token++;
13693 if (op->arity == 0) {
13694 if (builder->token->type != JIM_TT_SUBEXPR_END) {
13695 Jim_SetResultString(interp, "too many arguments for math function", -1);
13696 return JIM_ERR;
13698 builder->token++;
13699 goto noargs;
13701 builder->parencount++;
13704 rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity);
13706 else if (t->type == JIM_EXPROP_TERNARY) {
13708 rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2);
13710 else {
13711 rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1);
13714 if (rc != JIM_OK) {
13715 return rc;
13718 noargs:
13719 node = builder->next++;
13720 node->type = t->type;
13722 if (op->arity >= 3) {
13723 node->ternary = Jim_StackPop(&builder->stack);
13724 if (node->ternary == NULL) {
13725 goto missingoperand;
13728 if (op->arity >= 2) {
13729 node->right = Jim_StackPop(&builder->stack);
13730 if (node->right == NULL) {
13731 goto missingoperand;
13734 if (op->arity >= 1) {
13735 node->left = Jim_StackPop(&builder->stack);
13736 if (node->left == NULL) {
13737 missingoperand:
13738 Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr);
13739 builder->next--;
13740 return JIM_ERR;
13746 Jim_StackPush(&builder->stack, node);
13748 else {
13749 Jim_Obj *objPtr = NULL;
13754 if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
13755 Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr);
13756 return JIM_ERR;
13760 if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
13761 char *endptr;
13762 if (t->type == JIM_TT_EXPR_INT) {
13763 objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
13765 else {
13766 objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
13768 if (endptr != t->token + t->len) {
13770 Jim_FreeNewObj(interp, objPtr);
13771 objPtr = NULL;
13775 if (!objPtr) {
13777 objPtr = Jim_NewStringObj(interp, t->token, t->len);
13778 if (t->type == JIM_TT_CMD) {
13780 JimSetSourceInfo(interp, objPtr, builder->fileNameObj, t->line);
13785 node = builder->next++;
13786 node->objPtr = objPtr;
13787 Jim_IncrRefCount(node->objPtr);
13788 node->type = t->type;
13789 Jim_StackPush(&builder->stack, node);
13793 if (builder->stack.len == exp_stacklen) {
13794 builder->level--;
13795 return JIM_OK;
13798 if ((flags & EXPR_FUNC_ARGS)) {
13799 Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many");
13801 else {
13802 if (builder->stack.len < exp_stacklen) {
13803 if (builder->level == 0) {
13804 Jim_SetResultFormatted(interp, "empty expression");
13806 else {
13807 Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr);
13810 else {
13811 Jim_SetResultFormatted(interp, "extra terms after expression");
13815 return JIM_ERR;
13818 static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
13820 struct ExprTree *expr;
13821 struct ExprBuilder builder;
13822 int rc;
13823 struct JimExprNode *top = NULL;
13825 builder.parencount = 0;
13826 builder.level = 0;
13827 builder.token = builder.first_token = tokenlist->list;
13828 builder.exprObjPtr = exprObjPtr;
13829 builder.fileNameObj = fileNameObj;
13831 builder.nodes = malloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
13832 memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
13833 builder.next = builder.nodes;
13834 Jim_InitStack(&builder.stack);
13836 rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);
13838 if (rc == JIM_OK) {
13839 top = Jim_StackPop(&builder.stack);
13841 if (builder.parencount) {
13842 Jim_SetResultString(interp, "missing close parenthesis", -1);
13843 rc = JIM_ERR;
13848 Jim_FreeStack(&builder.stack);
13850 if (rc != JIM_OK) {
13851 ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes);
13852 return NULL;
13855 expr = Jim_Alloc(sizeof(*expr));
13856 expr->inUse = 1;
13857 expr->expr = top;
13858 expr->nodes = builder.nodes;
13859 expr->len = builder.next - builder.nodes;
13861 assert(expr->len <= tokenlist->count - 1);
13863 return expr;
13866 static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
13868 int exprTextLen;
13869 const char *exprText;
13870 struct JimParserCtx parser;
13871 struct ExprTree *expr;
13872 ParseTokenList tokenlist;
13873 int line;
13874 Jim_Obj *fileNameObj;
13875 int rc = JIM_ERR;
13878 if (objPtr->typePtr == &sourceObjType) {
13879 fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
13880 line = objPtr->internalRep.sourceValue.lineNumber;
13882 else {
13883 fileNameObj = interp->emptyObj;
13884 line = 1;
13886 Jim_IncrRefCount(fileNameObj);
13888 exprText = Jim_GetString(objPtr, &exprTextLen);
13891 ScriptTokenListInit(&tokenlist);
13893 JimParserInit(&parser, exprText, exprTextLen, line);
13894 while (!parser.eof) {
13895 if (JimParseExpression(&parser) != JIM_OK) {
13896 ScriptTokenListFree(&tokenlist);
13897 Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
13898 expr = NULL;
13899 goto err;
13902 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
13903 parser.tline);
13906 #ifdef DEBUG_SHOW_EXPR_TOKENS
13908 int i;
13909 printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
13910 for (i = 0; i < tokenlist.count; i++) {
13911 printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
13912 tokenlist.list[i].len, tokenlist.list[i].token);
13915 #endif
13917 if (JimParseCheckMissing(interp, parser.missing.ch) == JIM_ERR) {
13918 ScriptTokenListFree(&tokenlist);
13919 Jim_DecrRefCount(interp, fileNameObj);
13920 return JIM_ERR;
13924 expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj);
13927 ScriptTokenListFree(&tokenlist);
13929 if (!expr) {
13930 goto err;
13933 #ifdef DEBUG_SHOW_EXPR
13934 printf("==== Expr ====\n");
13935 JimShowExprNode(expr->expr, 0);
13936 #endif
13938 rc = JIM_OK;
13940 err:
13942 Jim_DecrRefCount(interp, fileNameObj);
13943 Jim_FreeIntRep(interp, objPtr);
13944 Jim_SetIntRepPtr(objPtr, expr);
13945 objPtr->typePtr = &exprObjType;
13946 return rc;
13949 static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
13951 if (objPtr->typePtr != &exprObjType) {
13952 if (SetExprFromAny(interp, objPtr) != JIM_OK) {
13953 return NULL;
13956 return (struct ExprTree *) Jim_GetIntRepPtr(objPtr);
13959 #ifdef JIM_OPTIMIZATION
13960 static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node)
13962 if (node->type == JIM_TT_EXPR_INT)
13963 return node->objPtr;
13964 else if (node->type == JIM_TT_VAR)
13965 return Jim_GetVariable(interp, node->objPtr, JIM_NONE);
13966 else if (node->type == JIM_TT_DICTSUGAR)
13967 return JimExpandDictSugar(interp, node->objPtr);
13968 else
13969 return NULL;
13971 #endif
13974 static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node)
13976 if (TOKEN_IS_EXPR_OP(node->type)) {
13977 const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type);
13978 return op->funcop(interp, node);
13980 else {
13981 Jim_Obj *objPtr;
13984 switch (node->type) {
13985 case JIM_TT_EXPR_INT:
13986 case JIM_TT_EXPR_DOUBLE:
13987 case JIM_TT_EXPR_BOOLEAN:
13988 case JIM_TT_STR:
13989 Jim_SetResult(interp, node->objPtr);
13990 return JIM_OK;
13992 case JIM_TT_VAR:
13993 objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG);
13994 if (objPtr) {
13995 Jim_SetResult(interp, objPtr);
13996 return JIM_OK;
13998 return JIM_ERR;
14000 case JIM_TT_DICTSUGAR:
14001 objPtr = JimExpandDictSugar(interp, node->objPtr);
14002 if (objPtr) {
14003 Jim_SetResult(interp, objPtr);
14004 return JIM_OK;
14006 return JIM_ERR;
14008 case JIM_TT_ESC:
14009 if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
14010 Jim_SetResult(interp, objPtr);
14011 return JIM_OK;
14013 return JIM_ERR;
14015 case JIM_TT_CMD:
14016 return Jim_EvalObj(interp, node->objPtr);
14018 default:
14020 return JIM_ERR;
14025 static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr)
14027 int rc = JimExprEvalTermNode(interp, node);
14028 if (rc == JIM_OK) {
14029 *objPtrPtr = Jim_GetResult(interp);
14030 Jim_IncrRefCount(*objPtrPtr);
14032 return rc;
14035 static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node)
14037 if (JimExprEvalTermNode(interp, node) == JIM_OK) {
14038 return ExprBool(interp, Jim_GetResult(interp));
14040 return -1;
14043 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
14045 struct ExprTree *expr;
14046 int retcode = JIM_OK;
14048 expr = JimGetExpression(interp, exprObjPtr);
14049 if (!expr) {
14050 return JIM_ERR;
14053 #ifdef JIM_OPTIMIZATION
14055 Jim_Obj *objPtr;
14058 switch (expr->len) {
14059 case 1:
14060 objPtr = JimExprIntValOrVar(interp, expr->expr);
14061 if (objPtr) {
14062 Jim_SetResult(interp, objPtr);
14063 return JIM_OK;
14065 break;
14067 case 2:
14068 if (expr->expr->type == JIM_EXPROP_NOT) {
14069 objPtr = JimExprIntValOrVar(interp, expr->expr->left);
14071 if (objPtr && JimIsWide(objPtr)) {
14072 Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj);
14073 return JIM_OK;
14076 break;
14078 case 3:
14079 objPtr = JimExprIntValOrVar(interp, expr->expr->left);
14080 if (objPtr && JimIsWide(objPtr)) {
14081 Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right);
14082 if (objPtr2 && JimIsWide(objPtr2)) {
14083 jim_wide wideValueA = JimWideValue(objPtr);
14084 jim_wide wideValueB = JimWideValue(objPtr2);
14085 int cmpRes;
14086 switch (expr->expr->type) {
14087 case JIM_EXPROP_LT:
14088 cmpRes = wideValueA < wideValueB;
14089 break;
14090 case JIM_EXPROP_LTE:
14091 cmpRes = wideValueA <= wideValueB;
14092 break;
14093 case JIM_EXPROP_GT:
14094 cmpRes = wideValueA > wideValueB;
14095 break;
14096 case JIM_EXPROP_GTE:
14097 cmpRes = wideValueA >= wideValueB;
14098 break;
14099 case JIM_EXPROP_NUMEQ:
14100 cmpRes = wideValueA == wideValueB;
14101 break;
14102 case JIM_EXPROP_NUMNE:
14103 cmpRes = wideValueA != wideValueB;
14104 break;
14105 default:
14106 goto noopt;
14108 Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);
14109 return JIM_OK;
14112 break;
14115 noopt:
14116 #endif
14118 expr->inUse++;
14121 retcode = JimExprEvalTermNode(interp, expr->expr);
14123 expr->inUse--;
14125 return retcode;
14128 int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
14130 int retcode = Jim_EvalExpression(interp, exprObjPtr);
14132 if (retcode == JIM_OK) {
14133 switch (ExprBool(interp, Jim_GetResult(interp))) {
14134 case 0:
14135 *boolPtr = 0;
14136 break;
14138 case 1:
14139 *boolPtr = 1;
14140 break;
14142 case -1:
14143 retcode = JIM_ERR;
14144 break;
14147 return retcode;
14153 typedef struct ScanFmtPartDescr
14155 const char *arg;
14156 const char *prefix;
14157 size_t width;
14158 int pos;
14159 char type;
14160 char modifier;
14161 } ScanFmtPartDescr;
14164 typedef struct ScanFmtStringObj
14166 jim_wide size;
14167 char *stringRep;
14168 size_t count;
14169 size_t convCount;
14170 size_t maxPos;
14171 const char *error;
14172 char *scratch;
14173 ScanFmtPartDescr descr[1];
14174 } ScanFmtStringObj;
14177 static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
14178 static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
14179 static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
14181 static const Jim_ObjType scanFmtStringObjType = {
14182 "scanformatstring",
14183 FreeScanFmtInternalRep,
14184 DupScanFmtInternalRep,
14185 UpdateStringOfScanFmt,
14186 JIM_TYPE_NONE,
14189 void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
14191 JIM_NOTUSED(interp);
14192 Jim_Free((char *)objPtr->internalRep.ptr);
14193 objPtr->internalRep.ptr = 0;
14196 void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
14198 size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
14199 ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
14201 JIM_NOTUSED(interp);
14202 memcpy(newVec, srcPtr->internalRep.ptr, size);
14203 dupPtr->internalRep.ptr = newVec;
14204 dupPtr->typePtr = &scanFmtStringObjType;
14207 static void UpdateStringOfScanFmt(Jim_Obj *objPtr)
14209 JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep);
14213 static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
14215 ScanFmtStringObj *fmtObj;
14216 char *buffer;
14217 int maxCount, i, approxSize, lastPos = -1;
14218 const char *fmt = Jim_String(objPtr);
14219 int maxFmtLen = Jim_Length(objPtr);
14220 const char *fmtEnd = fmt + maxFmtLen;
14221 int curr;
14223 Jim_FreeIntRep(interp, objPtr);
14225 for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
14226 if (fmt[i] == '%')
14227 ++maxCount;
14229 approxSize = sizeof(ScanFmtStringObj)
14230 +(maxCount + 1) * sizeof(ScanFmtPartDescr)
14231 +maxFmtLen * sizeof(char) + 3 + 1
14232 + maxFmtLen * sizeof(char) + 1
14233 + maxFmtLen * sizeof(char)
14234 +(maxCount + 1) * sizeof(char)
14236 fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize);
14237 memset(fmtObj, 0, approxSize);
14238 fmtObj->size = approxSize;
14239 fmtObj->maxPos = 0;
14240 fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1];
14241 fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
14242 memcpy(fmtObj->stringRep, fmt, maxFmtLen);
14243 buffer = fmtObj->stringRep + maxFmtLen + 1;
14244 objPtr->internalRep.ptr = fmtObj;
14245 objPtr->typePtr = &scanFmtStringObjType;
14246 for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
14247 int width = 0, skip;
14248 ScanFmtPartDescr *descr = &fmtObj->descr[curr];
14250 fmtObj->count++;
14251 descr->width = 0;
14253 if (*fmt != '%' || fmt[1] == '%') {
14254 descr->type = 0;
14255 descr->prefix = &buffer[i];
14256 for (; fmt < fmtEnd; ++fmt) {
14257 if (*fmt == '%') {
14258 if (fmt[1] != '%')
14259 break;
14260 ++fmt;
14262 buffer[i++] = *fmt;
14264 buffer[i++] = 0;
14267 ++fmt;
14269 if (fmt >= fmtEnd)
14270 goto done;
14271 descr->pos = 0;
14272 if (*fmt == '*') {
14273 descr->pos = -1;
14274 ++fmt;
14276 else
14277 fmtObj->convCount++;
14279 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14280 fmt += skip;
14282 if (descr->pos != -1 && *fmt == '$') {
14283 int prev;
14285 ++fmt;
14286 descr->pos = width;
14287 width = 0;
14289 if ((lastPos == 0 && descr->pos > 0)
14290 || (lastPos > 0 && descr->pos == 0)) {
14291 fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
14292 return JIM_ERR;
14295 for (prev = 0; prev < curr; ++prev) {
14296 if (fmtObj->descr[prev].pos == -1)
14297 continue;
14298 if (fmtObj->descr[prev].pos == descr->pos) {
14299 fmtObj->error =
14300 "variable is assigned by multiple \"%n$\" conversion specifiers";
14301 return JIM_ERR;
14304 if (descr->pos < 0) {
14305 fmtObj->error =
14306 "\"%n$\" conversion specifier is negative";
14307 return JIM_ERR;
14310 if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
14311 descr->width = width;
14312 fmt += skip;
14314 if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
14315 fmtObj->maxPos = descr->pos;
14317 else {
14319 descr->width = width;
14323 if (lastPos == -1)
14324 lastPos = descr->pos;
14326 if (*fmt == '[') {
14327 int swapped = 1, beg = i, end, j;
14329 descr->type = '[';
14330 descr->arg = &buffer[i];
14331 ++fmt;
14332 if (*fmt == '^')
14333 buffer[i++] = *fmt++;
14334 if (*fmt == ']')
14335 buffer[i++] = *fmt++;
14336 while (*fmt && *fmt != ']')
14337 buffer[i++] = *fmt++;
14338 if (*fmt != ']') {
14339 fmtObj->error = "unmatched [ in format string";
14340 return JIM_ERR;
14342 end = i;
14343 buffer[i++] = 0;
14345 while (swapped) {
14346 swapped = 0;
14347 for (j = beg + 1; j < end - 1; ++j) {
14348 if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) {
14349 char tmp = buffer[j - 1];
14351 buffer[j - 1] = buffer[j + 1];
14352 buffer[j + 1] = tmp;
14353 swapped = 1;
14358 else {
14360 if (fmt < fmtEnd && strchr("hlL", *fmt))
14361 descr->modifier = tolower((int)*fmt++);
14363 if (fmt >= fmtEnd) {
14364 fmtObj->error = "missing scan conversion character";
14365 return JIM_ERR;
14368 descr->type = *fmt;
14369 if (strchr("efgcsndoxui", *fmt) == 0) {
14370 fmtObj->error = "bad scan conversion character";
14371 return JIM_ERR;
14373 else if (*fmt == 'c' && descr->width != 0) {
14374 fmtObj->error = "field width may not be specified in %c " "conversion";
14375 return JIM_ERR;
14377 else if (*fmt == 'u' && descr->modifier == 'l') {
14378 fmtObj->error = "unsigned wide not supported";
14379 return JIM_ERR;
14382 curr++;
14384 done:
14385 return JIM_OK;
14390 #define FormatGetCnvCount(_fo_) \
14391 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
14392 #define FormatGetMaxPos(_fo_) \
14393 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
14394 #define FormatGetError(_fo_) \
14395 ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
14397 static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
14399 char *buffer = Jim_StrDup(str);
14400 char *p = buffer;
14402 while (*str) {
14403 int c;
14404 int n;
14406 if (!sdescr && isspace(UCHAR(*str)))
14407 break;
14409 n = utf8_tounicode(str, &c);
14410 if (sdescr && !JimCharsetMatch(sdescr, c, JIM_CHARSET_SCAN))
14411 break;
14412 while (n--)
14413 *p++ = *str++;
14415 *p = 0;
14416 return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
14420 static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int strLen,
14421 ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
14423 const char *tok;
14424 const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
14425 size_t scanned = 0;
14426 size_t anchor = pos;
14427 int i;
14428 Jim_Obj *tmpObj = NULL;
14431 *valObjPtr = 0;
14432 if (descr->prefix) {
14433 for (i = 0; pos < strLen && descr->prefix[i]; ++i) {
14435 if (isspace(UCHAR(descr->prefix[i])))
14436 while (pos < strLen && isspace(UCHAR(str[pos])))
14437 ++pos;
14438 else if (descr->prefix[i] != str[pos])
14439 break;
14440 else
14441 ++pos;
14443 if (pos >= strLen) {
14444 return -1;
14446 else if (descr->prefix[i] != 0)
14447 return 0;
14450 if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
14451 while (isspace(UCHAR(str[pos])))
14452 ++pos;
14454 scanned = pos - anchor;
14457 if (descr->type == 'n') {
14459 *valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
14461 else if (pos >= strLen) {
14463 return -1;
14465 else if (descr->type == 'c') {
14466 int c;
14467 scanned += utf8_tounicode(&str[pos], &c);
14468 *valObjPtr = Jim_NewIntObj(interp, c);
14469 return scanned;
14471 else {
14473 if (descr->width > 0) {
14474 size_t sLen = utf8_strlen(&str[pos], strLen - pos);
14475 size_t tLen = descr->width > sLen ? sLen : descr->width;
14477 tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
14478 tok = tmpObj->bytes;
14480 else {
14482 tok = &str[pos];
14484 switch (descr->type) {
14485 case 'd':
14486 case 'o':
14487 case 'x':
14488 case 'u':
14489 case 'i':{
14490 char *endp;
14491 jim_wide w;
14493 int base = descr->type == 'o' ? 8
14494 : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
14497 if (base == 0) {
14498 w = jim_strtoull(tok, &endp);
14500 else {
14501 w = strtoull(tok, &endp, base);
14504 if (endp != tok) {
14506 *valObjPtr = Jim_NewIntObj(interp, w);
14509 scanned += endp - tok;
14511 else {
14512 scanned = *tok ? 0 : -1;
14514 break;
14516 case 's':
14517 case '[':{
14518 *valObjPtr = JimScanAString(interp, descr->arg, tok);
14519 scanned += Jim_Length(*valObjPtr);
14520 break;
14522 case 'e':
14523 case 'f':
14524 case 'g':{
14525 char *endp;
14526 double value = strtod(tok, &endp);
14528 if (endp != tok) {
14530 *valObjPtr = Jim_NewDoubleObj(interp, value);
14532 scanned += endp - tok;
14534 else {
14535 scanned = *tok ? 0 : -1;
14537 break;
14540 if (tmpObj) {
14541 Jim_FreeNewObj(interp, tmpObj);
14544 return scanned;
14548 Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
14550 size_t i, pos;
14551 int scanned = 1;
14552 const char *str = Jim_String(strObjPtr);
14553 int strLen = Jim_Utf8Length(interp, strObjPtr);
14554 Jim_Obj *resultList = 0;
14555 Jim_Obj **resultVec = 0;
14556 int resultc;
14557 Jim_Obj *emptyStr = 0;
14558 ScanFmtStringObj *fmtObj;
14561 JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
14563 fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
14565 if (fmtObj->error != 0) {
14566 if (flags & JIM_ERRMSG)
14567 Jim_SetResultString(interp, fmtObj->error, -1);
14568 return 0;
14571 emptyStr = Jim_NewEmptyStringObj(interp);
14572 Jim_IncrRefCount(emptyStr);
14574 resultList = Jim_NewListObj(interp, NULL, 0);
14575 if (fmtObj->maxPos > 0) {
14576 for (i = 0; i < fmtObj->maxPos; ++i)
14577 Jim_ListAppendElement(interp, resultList, emptyStr);
14578 JimListGetElements(interp, resultList, &resultc, &resultVec);
14581 for (i = 0, pos = 0; i < fmtObj->count; ++i) {
14582 ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
14583 Jim_Obj *value = 0;
14586 if (descr->type == 0)
14587 continue;
14589 if (scanned > 0)
14590 scanned = ScanOneEntry(interp, str, pos, strLen, fmtObj, i, &value);
14592 if (scanned == -1 && i == 0)
14593 goto eof;
14595 pos += scanned;
14598 if (value == 0)
14599 value = Jim_NewEmptyStringObj(interp);
14601 if (descr->pos == -1) {
14602 Jim_FreeNewObj(interp, value);
14604 else if (descr->pos == 0)
14606 Jim_ListAppendElement(interp, resultList, value);
14607 else if (resultVec[descr->pos - 1] == emptyStr) {
14609 Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
14610 Jim_IncrRefCount(value);
14611 resultVec[descr->pos - 1] = value;
14613 else {
14615 Jim_FreeNewObj(interp, value);
14616 goto err;
14619 Jim_DecrRefCount(interp, emptyStr);
14620 return resultList;
14621 eof:
14622 Jim_DecrRefCount(interp, emptyStr);
14623 Jim_FreeNewObj(interp, resultList);
14624 return (Jim_Obj *)EOF;
14625 err:
14626 Jim_DecrRefCount(interp, emptyStr);
14627 Jim_FreeNewObj(interp, resultList);
14628 return 0;
14632 static void JimPrngInit(Jim_Interp *interp)
14634 #define PRNG_SEED_SIZE 256
14635 int i;
14636 unsigned int *seed;
14637 time_t t = time(NULL);
14639 interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
14641 seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
14642 for (i = 0; i < PRNG_SEED_SIZE; i++) {
14643 seed[i] = (rand() ^ t ^ clock());
14645 JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
14646 Jim_Free(seed);
14650 static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len)
14652 Jim_PrngState *prng;
14653 unsigned char *destByte = (unsigned char *)dest;
14654 unsigned int si, sj, x;
14657 if (interp->prngState == NULL)
14658 JimPrngInit(interp);
14659 prng = interp->prngState;
14661 for (x = 0; x < len; x++) {
14662 prng->i = (prng->i + 1) & 0xff;
14663 si = prng->sbox[prng->i];
14664 prng->j = (prng->j + si) & 0xff;
14665 sj = prng->sbox[prng->j];
14666 prng->sbox[prng->i] = sj;
14667 prng->sbox[prng->j] = si;
14668 *destByte++ = prng->sbox[(si + sj) & 0xff];
14673 static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
14675 int i;
14676 Jim_PrngState *prng;
14679 if (interp->prngState == NULL)
14680 JimPrngInit(interp);
14681 prng = interp->prngState;
14684 for (i = 0; i < 256; i++)
14685 prng->sbox[i] = i;
14687 for (i = 0; i < seedLen; i++) {
14688 unsigned char t;
14690 t = prng->sbox[i & 0xFF];
14691 prng->sbox[i & 0xFF] = prng->sbox[seed[i]];
14692 prng->sbox[seed[i]] = t;
14694 prng->i = prng->j = 0;
14696 for (i = 0; i < 256; i += seedLen) {
14697 JimRandomBytes(interp, seed, seedLen);
14702 static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14704 jim_wide wideValue, increment = 1;
14705 Jim_Obj *intObjPtr;
14707 if (argc != 2 && argc != 3) {
14708 Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
14709 return JIM_ERR;
14711 if (argc == 3) {
14712 if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK)
14713 return JIM_ERR;
14715 intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
14716 if (!intObjPtr) {
14718 wideValue = 0;
14720 else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
14721 return JIM_ERR;
14723 if (!intObjPtr || Jim_IsShared(intObjPtr)) {
14724 intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
14725 if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
14726 Jim_FreeNewObj(interp, intObjPtr);
14727 return JIM_ERR;
14730 else {
14732 Jim_InvalidateStringRep(intObjPtr);
14733 JimWideValue(intObjPtr) = wideValue + increment;
14735 if (argv[1]->typePtr != &variableObjType) {
14737 Jim_SetVariable(interp, argv[1], intObjPtr);
14740 Jim_SetResult(interp, intObjPtr);
14741 return JIM_OK;
14745 #define JIM_EVAL_SARGV_LEN 8
14746 #define JIM_EVAL_SINTV_LEN 8
14749 static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
14751 int retcode;
14753 if (interp->unknown_called > 50) {
14754 return JIM_ERR;
14759 if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
14760 return JIM_ERR;
14762 interp->unknown_called++;
14764 retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
14765 interp->unknown_called--;
14767 return retcode;
14770 static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14772 int retcode;
14773 Jim_Cmd *cmdPtr;
14774 void *prevPrivData;
14776 #if 0
14777 printf("invoke");
14778 int j;
14779 for (j = 0; j < objc; j++) {
14780 printf(" '%s'", Jim_String(objv[j]));
14782 printf("\n");
14783 #endif
14785 if (interp->framePtr->tailcallCmd) {
14787 cmdPtr = interp->framePtr->tailcallCmd;
14788 interp->framePtr->tailcallCmd = NULL;
14790 else {
14791 cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
14792 if (cmdPtr == NULL) {
14793 return JimUnknown(interp, objc, objv);
14795 JimIncrCmdRefCount(cmdPtr);
14798 if (interp->evalDepth == interp->maxEvalDepth) {
14799 Jim_SetResultString(interp, "Infinite eval recursion", -1);
14800 retcode = JIM_ERR;
14801 goto out;
14803 interp->evalDepth++;
14804 prevPrivData = interp->cmdPrivData;
14807 Jim_SetEmptyResult(interp);
14808 if (cmdPtr->isproc) {
14809 retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
14811 else {
14812 interp->cmdPrivData = cmdPtr->u.native.privData;
14813 retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
14815 interp->cmdPrivData = prevPrivData;
14816 interp->evalDepth--;
14818 out:
14819 JimDecrCmdRefCount(interp, cmdPtr);
14821 return retcode;
14824 int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
14826 int i, retcode;
14829 for (i = 0; i < objc; i++)
14830 Jim_IncrRefCount(objv[i]);
14832 retcode = JimInvokeCommand(interp, objc, objv);
14835 for (i = 0; i < objc; i++)
14836 Jim_DecrRefCount(interp, objv[i]);
14838 return retcode;
14841 int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
14843 int ret;
14844 Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
14846 nargv[0] = prefix;
14847 memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
14848 ret = Jim_EvalObjVector(interp, objc + 1, nargv);
14849 Jim_Free(nargv);
14850 return ret;
14853 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script)
14855 if (!interp->errorFlag) {
14857 interp->errorFlag = 1;
14858 Jim_IncrRefCount(script->fileNameObj);
14859 Jim_DecrRefCount(interp, interp->errorFileNameObj);
14860 interp->errorFileNameObj = script->fileNameObj;
14861 interp->errorLine = script->linenr;
14863 JimResetStackTrace(interp);
14865 interp->addStackTrace++;
14869 if (interp->addStackTrace > 0) {
14872 JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr);
14874 if (Jim_Length(script->fileNameObj)) {
14875 interp->addStackTrace = 0;
14878 Jim_DecrRefCount(interp, interp->errorProc);
14879 interp->errorProc = interp->emptyObj;
14880 Jim_IncrRefCount(interp->errorProc);
14884 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
14886 Jim_Obj *objPtr;
14888 switch (token->type) {
14889 case JIM_TT_STR:
14890 case JIM_TT_ESC:
14891 objPtr = token->objPtr;
14892 break;
14893 case JIM_TT_VAR:
14894 objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
14895 break;
14896 case JIM_TT_DICTSUGAR:
14897 objPtr = JimExpandDictSugar(interp, token->objPtr);
14898 break;
14899 case JIM_TT_EXPRSUGAR:
14900 objPtr = JimExpandExprSugar(interp, token->objPtr);
14901 break;
14902 case JIM_TT_CMD:
14903 switch (Jim_EvalObj(interp, token->objPtr)) {
14904 case JIM_OK:
14905 case JIM_RETURN:
14906 objPtr = interp->result;
14907 break;
14908 case JIM_BREAK:
14910 return JIM_BREAK;
14911 case JIM_CONTINUE:
14913 return JIM_CONTINUE;
14914 default:
14915 return JIM_ERR;
14917 break;
14918 default:
14919 JimPanic((1,
14920 "default token type (%d) reached " "in Jim_SubstObj().", token->type));
14921 objPtr = NULL;
14922 break;
14924 if (objPtr) {
14925 *objPtrPtr = objPtr;
14926 return JIM_OK;
14928 return JIM_ERR;
14931 static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
14933 int totlen = 0, i;
14934 Jim_Obj **intv;
14935 Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
14936 Jim_Obj *objPtr;
14937 char *s;
14939 if (tokens <= JIM_EVAL_SINTV_LEN)
14940 intv = sintv;
14941 else
14942 intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
14944 for (i = 0; i < tokens; i++) {
14945 switch (JimSubstOneToken(interp, &token[i], &intv[i])) {
14946 case JIM_OK:
14947 case JIM_RETURN:
14948 break;
14949 case JIM_BREAK:
14950 if (flags & JIM_SUBST_FLAG) {
14952 tokens = i;
14953 continue;
14957 case JIM_CONTINUE:
14958 if (flags & JIM_SUBST_FLAG) {
14959 intv[i] = NULL;
14960 continue;
14964 default:
14965 while (i--) {
14966 Jim_DecrRefCount(interp, intv[i]);
14968 if (intv != sintv) {
14969 Jim_Free(intv);
14971 return NULL;
14973 Jim_IncrRefCount(intv[i]);
14974 Jim_String(intv[i]);
14975 totlen += intv[i]->length;
14979 if (tokens == 1 && intv[0] && intv == sintv) {
14981 intv[0]->refCount--;
14982 return intv[0];
14985 objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0);
14987 if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC
14988 && token[2].type == JIM_TT_VAR) {
14990 objPtr->typePtr = &interpolatedObjType;
14991 objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
14992 objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
14993 Jim_IncrRefCount(intv[2]);
14995 else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
14997 JimSetSourceInfo(interp, objPtr, intv[0]->internalRep.sourceValue.fileNameObj, intv[0]->internalRep.sourceValue.lineNumber);
15001 s = objPtr->bytes = Jim_Alloc(totlen + 1);
15002 objPtr->length = totlen;
15003 for (i = 0; i < tokens; i++) {
15004 if (intv[i]) {
15005 memcpy(s, intv[i]->bytes, intv[i]->length);
15006 s += intv[i]->length;
15007 Jim_DecrRefCount(interp, intv[i]);
15010 objPtr->bytes[totlen] = '\0';
15012 if (intv != sintv) {
15013 Jim_Free(intv);
15016 return objPtr;
15020 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15022 int retcode = JIM_OK;
15024 JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
15026 if (listPtr->internalRep.listValue.len) {
15027 Jim_IncrRefCount(listPtr);
15028 retcode = JimInvokeCommand(interp,
15029 listPtr->internalRep.listValue.len,
15030 listPtr->internalRep.listValue.ele);
15031 Jim_DecrRefCount(interp, listPtr);
15033 return retcode;
15036 int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15038 SetListFromAny(interp, listPtr);
15039 return JimEvalObjList(interp, listPtr);
15042 int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
15044 int i;
15045 ScriptObj *script;
15046 ScriptToken *token;
15047 int retcode = JIM_OK;
15048 Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
15049 Jim_Obj *prevScriptObj;
15051 if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
15052 return JimEvalObjList(interp, scriptObjPtr);
15055 Jim_IncrRefCount(scriptObjPtr);
15056 script = JimGetScript(interp, scriptObjPtr);
15057 if (!JimScriptValid(interp, script)) {
15058 Jim_DecrRefCount(interp, scriptObjPtr);
15059 return JIM_ERR;
15062 Jim_SetEmptyResult(interp);
15064 token = script->token;
15066 #ifdef JIM_OPTIMIZATION
15067 if (script->len == 0) {
15068 Jim_DecrRefCount(interp, scriptObjPtr);
15069 return JIM_OK;
15071 if (script->len == 3
15072 && token[1].objPtr->typePtr == &commandObjType
15073 && token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
15074 && token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
15075 && token[2].objPtr->typePtr == &variableObjType) {
15077 Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
15079 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
15080 JimWideValue(objPtr)++;
15081 Jim_InvalidateStringRep(objPtr);
15082 Jim_DecrRefCount(interp, scriptObjPtr);
15083 Jim_SetResult(interp, objPtr);
15084 return JIM_OK;
15087 #endif
15089 script->inUse++;
15092 prevScriptObj = interp->currentScriptObj;
15093 interp->currentScriptObj = scriptObjPtr;
15095 interp->errorFlag = 0;
15096 argv = sargv;
15098 for (i = 0; i < script->len && retcode == JIM_OK; ) {
15099 int argc;
15100 int j;
15103 argc = token[i].objPtr->internalRep.scriptLineValue.argc;
15104 script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
15107 if (argc > JIM_EVAL_SARGV_LEN)
15108 argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
15111 i++;
15113 for (j = 0; j < argc; j++) {
15114 long wordtokens = 1;
15115 int expand = 0;
15116 Jim_Obj *wordObjPtr = NULL;
15118 if (token[i].type == JIM_TT_WORD) {
15119 wordtokens = JimWideValue(token[i++].objPtr);
15120 if (wordtokens < 0) {
15121 expand = 1;
15122 wordtokens = -wordtokens;
15126 if (wordtokens == 1) {
15128 switch (token[i].type) {
15129 case JIM_TT_ESC:
15130 case JIM_TT_STR:
15131 wordObjPtr = token[i].objPtr;
15132 break;
15133 case JIM_TT_VAR:
15134 wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
15135 break;
15136 case JIM_TT_EXPRSUGAR:
15137 wordObjPtr = JimExpandExprSugar(interp, token[i].objPtr);
15138 break;
15139 case JIM_TT_DICTSUGAR:
15140 wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
15141 break;
15142 case JIM_TT_CMD:
15143 retcode = Jim_EvalObj(interp, token[i].objPtr);
15144 if (retcode == JIM_OK) {
15145 wordObjPtr = Jim_GetResult(interp);
15147 break;
15148 default:
15149 JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
15152 else {
15153 wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
15156 if (!wordObjPtr) {
15157 if (retcode == JIM_OK) {
15158 retcode = JIM_ERR;
15160 break;
15163 Jim_IncrRefCount(wordObjPtr);
15164 i += wordtokens;
15166 if (!expand) {
15167 argv[j] = wordObjPtr;
15169 else {
15171 int len = Jim_ListLength(interp, wordObjPtr);
15172 int newargc = argc + len - 1;
15173 int k;
15175 if (len > 1) {
15176 if (argv == sargv) {
15177 if (newargc > JIM_EVAL_SARGV_LEN) {
15178 argv = Jim_Alloc(sizeof(*argv) * newargc);
15179 memcpy(argv, sargv, sizeof(*argv) * j);
15182 else {
15184 argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
15189 for (k = 0; k < len; k++) {
15190 argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
15191 Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
15194 Jim_DecrRefCount(interp, wordObjPtr);
15197 j--;
15198 argc += len - 1;
15202 if (retcode == JIM_OK && argc) {
15204 retcode = JimInvokeCommand(interp, argc, argv);
15206 if (Jim_CheckSignal(interp)) {
15207 retcode = JIM_SIGNAL;
15212 while (j-- > 0) {
15213 Jim_DecrRefCount(interp, argv[j]);
15216 if (argv != sargv) {
15217 Jim_Free(argv);
15218 argv = sargv;
15223 if (retcode == JIM_ERR) {
15224 JimAddErrorToStack(interp, script);
15227 else if (retcode != JIM_RETURN || interp->returnCode != JIM_ERR) {
15229 interp->addStackTrace = 0;
15233 interp->currentScriptObj = prevScriptObj;
15235 Jim_FreeIntRep(interp, scriptObjPtr);
15236 scriptObjPtr->typePtr = &scriptObjType;
15237 Jim_SetIntRepPtr(scriptObjPtr, script);
15238 Jim_DecrRefCount(interp, scriptObjPtr);
15240 return retcode;
15243 static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
15245 int retcode;
15247 const char *varname = Jim_String(argNameObj);
15248 if (*varname == '&') {
15250 Jim_Obj *objPtr;
15251 Jim_CallFrame *savedCallFrame = interp->framePtr;
15253 interp->framePtr = interp->framePtr->parent;
15254 objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
15255 interp->framePtr = savedCallFrame;
15256 if (!objPtr) {
15257 return JIM_ERR;
15261 objPtr = Jim_NewStringObj(interp, varname + 1, -1);
15262 Jim_IncrRefCount(objPtr);
15263 retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
15264 Jim_DecrRefCount(interp, objPtr);
15266 else {
15267 retcode = Jim_SetVariable(interp, argNameObj, argValObj);
15269 return retcode;
15272 static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
15275 Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
15276 int i;
15278 for (i = 0; i < cmd->u.proc.argListLen; i++) {
15279 Jim_AppendString(interp, argmsg, " ", 1);
15281 if (i == cmd->u.proc.argsPos) {
15282 if (cmd->u.proc.arglist[i].defaultObjPtr) {
15284 Jim_AppendString(interp, argmsg, "?", 1);
15285 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
15286 Jim_AppendString(interp, argmsg, " ...?", -1);
15288 else {
15290 Jim_AppendString(interp, argmsg, "?arg...?", -1);
15293 else {
15294 if (cmd->u.proc.arglist[i].defaultObjPtr) {
15295 Jim_AppendString(interp, argmsg, "?", 1);
15296 Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
15297 Jim_AppendString(interp, argmsg, "?", 1);
15299 else {
15300 const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
15301 if (*arg == '&') {
15302 arg++;
15304 Jim_AppendString(interp, argmsg, arg, -1);
15308 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
15311 #ifdef jim_ext_namespace
15312 int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
15314 Jim_CallFrame *callFramePtr;
15315 int retcode;
15318 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
15319 callFramePtr->argv = &interp->emptyObj;
15320 callFramePtr->argc = 0;
15321 callFramePtr->procArgsObjPtr = NULL;
15322 callFramePtr->procBodyObjPtr = scriptObj;
15323 callFramePtr->staticVars = NULL;
15324 callFramePtr->fileNameObj = interp->emptyObj;
15325 callFramePtr->line = 0;
15326 Jim_IncrRefCount(scriptObj);
15327 interp->framePtr = callFramePtr;
15330 if (interp->framePtr->level == interp->maxCallFrameDepth) {
15331 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15332 retcode = JIM_ERR;
15334 else {
15336 retcode = Jim_EvalObj(interp, scriptObj);
15340 interp->framePtr = interp->framePtr->parent;
15341 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15343 return retcode;
15345 #endif
15347 static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
15349 Jim_CallFrame *callFramePtr;
15350 int i, d, retcode, optargs;
15351 ScriptObj *script;
15354 if (argc - 1 < cmd->u.proc.reqArity ||
15355 (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
15356 JimSetProcWrongArgs(interp, argv[0], cmd);
15357 return JIM_ERR;
15360 if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
15362 return JIM_OK;
15366 if (interp->framePtr->level == interp->maxCallFrameDepth) {
15367 Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
15368 return JIM_ERR;
15372 callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
15373 callFramePtr->argv = argv;
15374 callFramePtr->argc = argc;
15375 callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
15376 callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
15377 callFramePtr->staticVars = cmd->u.proc.staticVars;
15380 script = JimGetScript(interp, interp->currentScriptObj);
15381 callFramePtr->fileNameObj = script->fileNameObj;
15382 callFramePtr->line = script->linenr;
15384 Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
15385 Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
15386 interp->framePtr = callFramePtr;
15389 optargs = (argc - 1 - cmd->u.proc.reqArity);
15392 i = 1;
15393 for (d = 0; d < cmd->u.proc.argListLen; d++) {
15394 Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
15395 if (d == cmd->u.proc.argsPos) {
15397 Jim_Obj *listObjPtr;
15398 int argsLen = 0;
15399 if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
15400 argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
15402 listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
15405 if (cmd->u.proc.arglist[d].defaultObjPtr) {
15406 nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
15408 retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
15409 if (retcode != JIM_OK) {
15410 goto badargset;
15413 i += argsLen;
15414 continue;
15418 if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
15419 retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
15421 else {
15423 retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
15425 if (retcode != JIM_OK) {
15426 goto badargset;
15431 retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
15433 badargset:
15436 retcode = JimInvokeDefer(interp, retcode);
15437 interp->framePtr = interp->framePtr->parent;
15438 JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
15441 if (interp->framePtr->tailcallObj) {
15442 do {
15443 Jim_Obj *tailcallObj = interp->framePtr->tailcallObj;
15445 interp->framePtr->tailcallObj = NULL;
15447 if (retcode == JIM_EVAL) {
15448 retcode = Jim_EvalObjList(interp, tailcallObj);
15449 if (retcode == JIM_RETURN) {
15450 interp->returnLevel++;
15453 Jim_DecrRefCount(interp, tailcallObj);
15454 } while (interp->framePtr->tailcallObj);
15457 if (interp->framePtr->tailcallCmd) {
15458 JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
15459 interp->framePtr->tailcallCmd = NULL;
15464 if (retcode == JIM_RETURN) {
15465 if (--interp->returnLevel <= 0) {
15466 retcode = interp->returnCode;
15467 interp->returnCode = JIM_OK;
15468 interp->returnLevel = 0;
15471 else if (retcode == JIM_ERR) {
15472 interp->addStackTrace++;
15473 Jim_DecrRefCount(interp, interp->errorProc);
15474 interp->errorProc = argv[0];
15475 Jim_IncrRefCount(interp->errorProc);
15478 return retcode;
15481 int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
15483 int retval;
15484 Jim_Obj *scriptObjPtr;
15486 scriptObjPtr = Jim_NewStringObj(interp, script, -1);
15487 Jim_IncrRefCount(scriptObjPtr);
15489 if (filename) {
15490 Jim_Obj *prevScriptObj;
15492 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
15494 prevScriptObj = interp->currentScriptObj;
15495 interp->currentScriptObj = scriptObjPtr;
15497 retval = Jim_EvalObj(interp, scriptObjPtr);
15499 interp->currentScriptObj = prevScriptObj;
15501 else {
15502 retval = Jim_EvalObj(interp, scriptObjPtr);
15504 Jim_DecrRefCount(interp, scriptObjPtr);
15505 return retval;
15508 int Jim_Eval(Jim_Interp *interp, const char *script)
15510 return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
15514 int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
15516 int retval;
15517 Jim_CallFrame *savedFramePtr = interp->framePtr;
15519 interp->framePtr = interp->topFramePtr;
15520 retval = Jim_Eval(interp, script);
15521 interp->framePtr = savedFramePtr;
15523 return retval;
15526 int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
15528 int retval;
15529 Jim_CallFrame *savedFramePtr = interp->framePtr;
15531 interp->framePtr = interp->topFramePtr;
15532 retval = Jim_EvalFile(interp, filename);
15533 interp->framePtr = savedFramePtr;
15535 return retval;
15538 #include <sys/stat.h>
15540 int Jim_EvalFile(Jim_Interp *interp, const char *filename)
15542 FILE *fp;
15543 char *buf;
15544 Jim_Obj *scriptObjPtr;
15545 Jim_Obj *prevScriptObj;
15546 struct stat sb;
15547 int retcode;
15548 int readlen;
15550 if (stat(filename, &sb) != 0 || (fp = fopen(filename, "rt")) == NULL) {
15551 Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
15552 return JIM_ERR;
15554 if (sb.st_size == 0) {
15555 fclose(fp);
15556 return JIM_OK;
15559 buf = Jim_Alloc(sb.st_size + 1);
15560 readlen = fread(buf, 1, sb.st_size, fp);
15561 if (ferror(fp)) {
15562 fclose(fp);
15563 Jim_Free(buf);
15564 Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
15565 return JIM_ERR;
15567 fclose(fp);
15568 buf[readlen] = 0;
15570 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
15571 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1);
15572 Jim_IncrRefCount(scriptObjPtr);
15574 prevScriptObj = interp->currentScriptObj;
15575 interp->currentScriptObj = scriptObjPtr;
15577 retcode = Jim_EvalObj(interp, scriptObjPtr);
15580 if (retcode == JIM_RETURN) {
15581 if (--interp->returnLevel <= 0) {
15582 retcode = interp->returnCode;
15583 interp->returnCode = JIM_OK;
15584 interp->returnLevel = 0;
15587 if (retcode == JIM_ERR) {
15589 interp->addStackTrace++;
15592 interp->currentScriptObj = prevScriptObj;
15594 Jim_DecrRefCount(interp, scriptObjPtr);
15596 return retcode;
15599 static void JimParseSubst(struct JimParserCtx *pc, int flags)
15601 pc->tstart = pc->p;
15602 pc->tline = pc->linenr;
15604 if (pc->len == 0) {
15605 pc->tend = pc->p;
15606 pc->tt = JIM_TT_EOL;
15607 pc->eof = 1;
15608 return;
15610 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15611 JimParseCmd(pc);
15612 return;
15614 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15615 if (JimParseVar(pc) == JIM_OK) {
15616 return;
15619 pc->tstart = pc->p;
15620 flags |= JIM_SUBST_NOVAR;
15622 while (pc->len) {
15623 if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
15624 break;
15626 if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
15627 break;
15629 if (*pc->p == '\\' && pc->len > 1) {
15630 pc->p++;
15631 pc->len--;
15633 pc->p++;
15634 pc->len--;
15636 pc->tend = pc->p - 1;
15637 pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
15641 static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags)
15643 int scriptTextLen;
15644 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
15645 struct JimParserCtx parser;
15646 struct ScriptObj *script = Jim_Alloc(sizeof(*script));
15647 ParseTokenList tokenlist;
15650 ScriptTokenListInit(&tokenlist);
15652 JimParserInit(&parser, scriptText, scriptTextLen, 1);
15653 while (1) {
15654 JimParseSubst(&parser, flags);
15655 if (parser.eof) {
15657 break;
15659 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
15660 parser.tline);
15664 script->inUse = 1;
15665 script->substFlags = flags;
15666 script->fileNameObj = interp->emptyObj;
15667 Jim_IncrRefCount(script->fileNameObj);
15668 SubstObjAddTokens(interp, script, &tokenlist);
15671 ScriptTokenListFree(&tokenlist);
15673 #ifdef DEBUG_SHOW_SUBST
15675 int i;
15677 printf("==== Subst ====\n");
15678 for (i = 0; i < script->len; i++) {
15679 printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
15680 Jim_String(script->token[i].objPtr));
15683 #endif
15686 Jim_FreeIntRep(interp, objPtr);
15687 Jim_SetIntRepPtr(objPtr, script);
15688 objPtr->typePtr = &scriptObjType;
15689 return JIM_OK;
15692 static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
15694 if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
15695 SetSubstFromAny(interp, objPtr, flags);
15696 return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
15699 int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
15701 ScriptObj *script = Jim_GetSubst(interp, substObjPtr, flags);
15703 Jim_IncrRefCount(substObjPtr);
15704 script->inUse++;
15706 *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
15708 script->inUse--;
15709 Jim_DecrRefCount(interp, substObjPtr);
15710 if (*resObjPtrPtr == NULL) {
15711 return JIM_ERR;
15713 return JIM_OK;
15716 void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
15718 Jim_Obj *objPtr;
15719 Jim_Obj *listObjPtr;
15721 JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0"));
15723 listObjPtr = Jim_NewListObj(interp, argv, argc);
15725 if (msg && *msg) {
15726 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
15728 Jim_IncrRefCount(listObjPtr);
15729 objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
15730 Jim_DecrRefCount(interp, listObjPtr);
15732 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
15735 typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
15736 Jim_HashEntry *he, int type);
15738 #define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL)
15740 static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
15741 JimHashtableIteratorCallbackType *callback, int type)
15743 Jim_HashEntry *he;
15744 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
15747 if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
15748 he = Jim_FindHashEntry(ht, Jim_String(patternObjPtr));
15749 if (he) {
15750 callback(interp, listObjPtr, he, type);
15753 else {
15754 Jim_HashTableIterator htiter;
15755 JimInitHashTableIterator(ht, &htiter);
15756 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
15757 if (patternObjPtr == NULL || JimGlobMatch(Jim_String(patternObjPtr), he->key, 0)) {
15758 callback(interp, listObjPtr, he, type);
15762 return listObjPtr;
15766 #define JIM_CMDLIST_COMMANDS 0
15767 #define JIM_CMDLIST_PROCS 1
15768 #define JIM_CMDLIST_CHANNELS 2
15770 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15771 Jim_HashEntry *he, int type)
15773 Jim_Cmd *cmdPtr = Jim_GetHashEntryVal(he);
15774 Jim_Obj *objPtr;
15776 if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
15778 return;
15781 objPtr = Jim_NewStringObj(interp, he->key, -1);
15782 Jim_IncrRefCount(objPtr);
15784 if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, objPtr)) {
15785 Jim_ListAppendElement(interp, listObjPtr, objPtr);
15787 Jim_DecrRefCount(interp, objPtr);
15791 static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
15793 return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
15797 #define JIM_VARLIST_GLOBALS 0
15798 #define JIM_VARLIST_LOCALS 1
15799 #define JIM_VARLIST_VARS 2
15801 #define JIM_VARLIST_VALUES 0x1000
15803 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
15804 Jim_HashEntry *he, int type)
15806 Jim_Var *varPtr = Jim_GetHashEntryVal(he);
15808 if (type != JIM_VARLIST_LOCALS || varPtr->linkFramePtr == NULL) {
15809 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1));
15810 if (type & JIM_VARLIST_VALUES) {
15811 Jim_ListAppendElement(interp, listObjPtr, varPtr->objPtr);
15817 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
15819 if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
15820 return interp->emptyObj;
15822 else {
15823 Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
15824 return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch, mode);
15828 static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr,
15829 Jim_Obj **objPtrPtr, int info_level_cmd)
15831 Jim_CallFrame *targetCallFrame;
15833 targetCallFrame = JimGetCallFrameByInteger(interp, levelObjPtr);
15834 if (targetCallFrame == NULL) {
15835 return JIM_ERR;
15838 if (targetCallFrame == interp->topFramePtr) {
15839 Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
15840 return JIM_ERR;
15842 if (info_level_cmd) {
15843 *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
15845 else {
15846 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
15848 Jim_ListAppendElement(interp, listObj, targetCallFrame->argv[0]);
15849 Jim_ListAppendElement(interp, listObj, targetCallFrame->fileNameObj);
15850 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, targetCallFrame->line));
15851 *objPtrPtr = listObj;
15853 return JIM_OK;
15858 static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15860 if (argc != 2 && argc != 3) {
15861 Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
15862 return JIM_ERR;
15864 if (argc == 3) {
15865 if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
15866 Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
15867 return JIM_ERR;
15869 else {
15870 fputs(Jim_String(argv[2]), stdout);
15873 else {
15874 puts(Jim_String(argv[1]));
15876 return JIM_OK;
15880 static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
15882 jim_wide wideValue, res;
15883 double doubleValue, doubleRes;
15884 int i;
15886 res = (op == JIM_EXPROP_ADD) ? 0 : 1;
15888 for (i = 1; i < argc; i++) {
15889 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
15890 goto trydouble;
15891 if (op == JIM_EXPROP_ADD)
15892 res += wideValue;
15893 else
15894 res *= wideValue;
15896 Jim_SetResultInt(interp, res);
15897 return JIM_OK;
15898 trydouble:
15899 doubleRes = (double)res;
15900 for (; i < argc; i++) {
15901 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
15902 return JIM_ERR;
15903 if (op == JIM_EXPROP_ADD)
15904 doubleRes += doubleValue;
15905 else
15906 doubleRes *= doubleValue;
15908 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15909 return JIM_OK;
15913 static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
15915 jim_wide wideValue, res = 0;
15916 double doubleValue, doubleRes = 0;
15917 int i = 2;
15919 if (argc < 2) {
15920 Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
15921 return JIM_ERR;
15923 else if (argc == 2) {
15924 if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
15925 if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
15926 return JIM_ERR;
15928 else {
15929 if (op == JIM_EXPROP_SUB)
15930 doubleRes = -doubleValue;
15931 else
15932 doubleRes = 1.0 / doubleValue;
15933 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15934 return JIM_OK;
15937 if (op == JIM_EXPROP_SUB) {
15938 res = -wideValue;
15939 Jim_SetResultInt(interp, res);
15941 else {
15942 doubleRes = 1.0 / wideValue;
15943 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15945 return JIM_OK;
15947 else {
15948 if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
15949 if (Jim_GetDouble(interp, argv[1], &doubleRes)
15950 != JIM_OK) {
15951 return JIM_ERR;
15953 else {
15954 goto trydouble;
15958 for (i = 2; i < argc; i++) {
15959 if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
15960 doubleRes = (double)res;
15961 goto trydouble;
15963 if (op == JIM_EXPROP_SUB)
15964 res -= wideValue;
15965 else {
15966 if (wideValue == 0) {
15967 Jim_SetResultString(interp, "Division by zero", -1);
15968 return JIM_ERR;
15970 res /= wideValue;
15973 Jim_SetResultInt(interp, res);
15974 return JIM_OK;
15975 trydouble:
15976 for (; i < argc; i++) {
15977 if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
15978 return JIM_ERR;
15979 if (op == JIM_EXPROP_SUB)
15980 doubleRes -= doubleValue;
15981 else
15982 doubleRes /= doubleValue;
15984 Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
15985 return JIM_OK;
15990 static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15992 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD);
15996 static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
15998 return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL);
16002 static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16004 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB);
16008 static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16010 return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV);
16014 static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16016 if (argc != 2 && argc != 3) {
16017 Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?");
16018 return JIM_ERR;
16020 if (argc == 2) {
16021 Jim_Obj *objPtr;
16023 objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16024 if (!objPtr)
16025 return JIM_ERR;
16026 Jim_SetResult(interp, objPtr);
16027 return JIM_OK;
16030 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
16031 return JIM_ERR;
16032 Jim_SetResult(interp, argv[2]);
16033 return JIM_OK;
16036 static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16038 int i = 1;
16039 int complain = 1;
16041 while (i < argc) {
16042 if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
16043 i++;
16044 break;
16046 if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
16047 complain = 0;
16048 i++;
16049 continue;
16051 break;
16054 while (i < argc) {
16055 if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
16056 && complain) {
16057 return JIM_ERR;
16059 i++;
16061 return JIM_OK;
16065 static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16067 if (argc != 3) {
16068 Jim_WrongNumArgs(interp, 1, argv, "condition body");
16069 return JIM_ERR;
16073 while (1) {
16074 int boolean, retval;
16076 if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
16077 return retval;
16078 if (!boolean)
16079 break;
16081 if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
16082 switch (retval) {
16083 case JIM_BREAK:
16084 goto out;
16085 break;
16086 case JIM_CONTINUE:
16087 continue;
16088 break;
16089 default:
16090 return retval;
16094 out:
16095 Jim_SetEmptyResult(interp);
16096 return JIM_OK;
16100 static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16102 int retval;
16103 int boolean = 1;
16104 Jim_Obj *varNamePtr = NULL;
16105 Jim_Obj *stopVarNamePtr = NULL;
16107 if (argc != 5) {
16108 Jim_WrongNumArgs(interp, 1, argv, "start test next body");
16109 return JIM_ERR;
16113 if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
16114 return retval;
16117 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16120 #ifdef JIM_OPTIMIZATION
16121 if (retval == JIM_OK && boolean) {
16122 ScriptObj *incrScript;
16123 struct ExprTree *expr;
16124 jim_wide stop, currentVal;
16125 Jim_Obj *objPtr;
16126 int cmpOffset;
16129 expr = JimGetExpression(interp, argv[2]);
16130 incrScript = JimGetScript(interp, argv[3]);
16133 if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
16134 goto evalstart;
16137 if (incrScript->token[1].type != JIM_TT_ESC) {
16138 goto evalstart;
16141 if (expr->expr->type == JIM_EXPROP_LT) {
16142 cmpOffset = 0;
16144 else if (expr->expr->type == JIM_EXPROP_LTE) {
16145 cmpOffset = 1;
16147 else {
16148 goto evalstart;
16151 if (expr->expr->left->type != JIM_TT_VAR) {
16152 goto evalstart;
16155 if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) {
16156 goto evalstart;
16160 if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
16161 goto evalstart;
16165 if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
16166 goto evalstart;
16170 if (expr->expr->right->type == JIM_TT_EXPR_INT) {
16171 if (Jim_GetWide(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) {
16172 goto evalstart;
16175 else {
16176 stopVarNamePtr = expr->expr->right->objPtr;
16177 Jim_IncrRefCount(stopVarNamePtr);
16179 stop = 0;
16183 varNamePtr = expr->expr->left->objPtr;
16184 Jim_IncrRefCount(varNamePtr);
16186 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
16187 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
16188 goto testcond;
16192 while (retval == JIM_OK) {
16197 if (stopVarNamePtr) {
16198 objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
16199 if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
16200 goto testcond;
16204 if (currentVal >= stop + cmpOffset) {
16205 break;
16209 retval = Jim_EvalObj(interp, argv[4]);
16210 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16211 retval = JIM_OK;
16213 objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
16216 if (objPtr == NULL) {
16217 retval = JIM_ERR;
16218 goto out;
16220 if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16221 currentVal = ++JimWideValue(objPtr);
16222 Jim_InvalidateStringRep(objPtr);
16224 else {
16225 if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
16226 Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
16227 ++currentVal)) != JIM_OK) {
16228 goto evalnext;
16233 goto out;
16235 evalstart:
16236 #endif
16238 while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
16240 retval = Jim_EvalObj(interp, argv[4]);
16242 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16244 JIM_IF_OPTIM(evalnext:)
16245 retval = Jim_EvalObj(interp, argv[3]);
16246 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16248 JIM_IF_OPTIM(testcond:)
16249 retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
16253 JIM_IF_OPTIM(out:)
16254 if (stopVarNamePtr) {
16255 Jim_DecrRefCount(interp, stopVarNamePtr);
16257 if (varNamePtr) {
16258 Jim_DecrRefCount(interp, varNamePtr);
16261 if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
16262 Jim_SetEmptyResult(interp);
16263 return JIM_OK;
16266 return retval;
16270 static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16272 int retval;
16273 jim_wide i;
16274 jim_wide limit;
16275 jim_wide incr = 1;
16276 Jim_Obj *bodyObjPtr;
16278 if (argc != 5 && argc != 6) {
16279 Jim_WrongNumArgs(interp, 1, argv, "var first limit ?incr? body");
16280 return JIM_ERR;
16283 if (Jim_GetWide(interp, argv[2], &i) != JIM_OK ||
16284 Jim_GetWide(interp, argv[3], &limit) != JIM_OK ||
16285 (argc == 6 && Jim_GetWide(interp, argv[4], &incr) != JIM_OK)) {
16286 return JIM_ERR;
16288 bodyObjPtr = (argc == 5) ? argv[4] : argv[5];
16290 retval = Jim_SetVariable(interp, argv[1], argv[2]);
16292 while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
16293 retval = Jim_EvalObj(interp, bodyObjPtr);
16294 if (retval == JIM_OK || retval == JIM_CONTINUE) {
16295 Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
16297 retval = JIM_OK;
16300 i += incr;
16302 if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
16303 if (argv[1]->typePtr != &variableObjType) {
16304 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16305 return JIM_ERR;
16308 JimWideValue(objPtr) = i;
16309 Jim_InvalidateStringRep(objPtr);
16311 if (argv[1]->typePtr != &variableObjType) {
16312 if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
16313 retval = JIM_ERR;
16314 break;
16318 else {
16319 objPtr = Jim_NewIntObj(interp, i);
16320 retval = Jim_SetVariable(interp, argv[1], objPtr);
16321 if (retval != JIM_OK) {
16322 Jim_FreeNewObj(interp, objPtr);
16328 if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
16329 Jim_SetEmptyResult(interp);
16330 return JIM_OK;
16332 return retval;
16335 typedef struct {
16336 Jim_Obj *objPtr;
16337 int idx;
16338 } Jim_ListIter;
16340 static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
16342 iter->objPtr = objPtr;
16343 iter->idx = 0;
16346 static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
16348 if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
16349 return NULL;
16351 return iter->objPtr->internalRep.listValue.ele[iter->idx++];
16354 static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
16356 return iter->idx >= Jim_ListLength(interp, iter->objPtr);
16360 static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
16362 int result = JIM_OK;
16363 int i, numargs;
16364 Jim_ListIter twoiters[2];
16365 Jim_ListIter *iters;
16366 Jim_Obj *script;
16367 Jim_Obj *resultObj;
16369 if (argc < 4 || argc % 2 != 0) {
16370 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
16371 return JIM_ERR;
16373 script = argv[argc - 1];
16374 numargs = (argc - 1 - 1);
16376 if (numargs == 2) {
16377 iters = twoiters;
16379 else {
16380 iters = Jim_Alloc(numargs * sizeof(*iters));
16382 for (i = 0; i < numargs; i++) {
16383 JimListIterInit(&iters[i], argv[i + 1]);
16384 if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
16385 result = JIM_ERR;
16388 if (result != JIM_OK) {
16389 Jim_SetResultString(interp, "foreach varlist is empty", -1);
16390 goto empty_varlist;
16393 if (doMap) {
16394 resultObj = Jim_NewListObj(interp, NULL, 0);
16396 else {
16397 resultObj = interp->emptyObj;
16399 Jim_IncrRefCount(resultObj);
16401 while (1) {
16403 for (i = 0; i < numargs; i += 2) {
16404 if (!JimListIterDone(interp, &iters[i + 1])) {
16405 break;
16408 if (i == numargs) {
16410 break;
16414 for (i = 0; i < numargs; i += 2) {
16415 Jim_Obj *varName;
16418 JimListIterInit(&iters[i], argv[i + 1]);
16419 while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
16420 Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
16421 if (!valObj) {
16423 valObj = interp->emptyObj;
16426 Jim_IncrRefCount(valObj);
16427 result = Jim_SetVariable(interp, varName, valObj);
16428 Jim_DecrRefCount(interp, valObj);
16429 if (result != JIM_OK) {
16430 goto err;
16434 switch (result = Jim_EvalObj(interp, script)) {
16435 case JIM_OK:
16436 if (doMap) {
16437 Jim_ListAppendElement(interp, resultObj, interp->result);
16439 break;
16440 case JIM_CONTINUE:
16441 break;
16442 case JIM_BREAK:
16443 goto out;
16444 default:
16445 goto err;
16448 out:
16449 result = JIM_OK;
16450 Jim_SetResult(interp, resultObj);
16451 err:
16452 Jim_DecrRefCount(interp, resultObj);
16453 empty_varlist:
16454 if (numargs > 2) {
16455 Jim_Free(iters);
16457 return result;
16461 static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16463 return JimForeachMapHelper(interp, argc, argv, 0);
16467 static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16469 return JimForeachMapHelper(interp, argc, argv, 1);
16473 static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16475 int result = JIM_ERR;
16476 int i;
16477 Jim_ListIter iter;
16478 Jim_Obj *resultObj;
16480 if (argc < 2) {
16481 Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
16482 return JIM_ERR;
16485 JimListIterInit(&iter, argv[1]);
16487 for (i = 2; i < argc; i++) {
16488 Jim_Obj *valObj = JimListIterNext(interp, &iter);
16489 result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
16490 if (result != JIM_OK) {
16491 return result;
16495 resultObj = Jim_NewListObj(interp, NULL, 0);
16496 while (!JimListIterDone(interp, &iter)) {
16497 Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
16500 Jim_SetResult(interp, resultObj);
16502 return JIM_OK;
16506 static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16508 int boolean, retval, current = 1, falsebody = 0;
16510 if (argc >= 3) {
16511 while (1) {
16513 if (current >= argc)
16514 goto err;
16515 if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
16516 != JIM_OK)
16517 return retval;
16519 if (current >= argc)
16520 goto err;
16521 if (Jim_CompareStringImmediate(interp, argv[current], "then"))
16522 current++;
16524 if (current >= argc)
16525 goto err;
16526 if (boolean)
16527 return Jim_EvalObj(interp, argv[current]);
16529 if (++current >= argc) {
16530 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
16531 return JIM_OK;
16533 falsebody = current++;
16534 if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
16536 if (current != argc - 1)
16537 goto err;
16538 return Jim_EvalObj(interp, argv[current]);
16540 else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
16541 continue;
16543 else if (falsebody != argc - 1)
16544 goto err;
16545 return Jim_EvalObj(interp, argv[falsebody]);
16547 return JIM_OK;
16549 err:
16550 Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
16551 return JIM_ERR;
16556 int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
16557 Jim_Obj *stringObj, int nocase)
16559 Jim_Obj *parms[4];
16560 int argc = 0;
16561 long eq;
16562 int rc;
16564 parms[argc++] = commandObj;
16565 if (nocase) {
16566 parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
16568 parms[argc++] = patternObj;
16569 parms[argc++] = stringObj;
16571 rc = Jim_EvalObjVector(interp, argc, parms);
16573 if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
16574 eq = -rc;
16577 return eq;
16581 static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16583 enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
16584 int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
16585 Jim_Obj *command = NULL, *scriptObj = NULL, *strObj;
16586 Jim_Obj **caseList;
16588 if (argc < 3) {
16589 wrongnumargs:
16590 Jim_WrongNumArgs(interp, 1, argv, "?options? string "
16591 "pattern body ... ?default body? or " "{pattern body ?pattern body ...?}");
16592 return JIM_ERR;
16594 for (opt = 1; opt < argc; ++opt) {
16595 const char *option = Jim_String(argv[opt]);
16597 if (*option != '-')
16598 break;
16599 else if (strncmp(option, "--", 2) == 0) {
16600 ++opt;
16601 break;
16603 else if (strncmp(option, "-exact", 2) == 0)
16604 matchOpt = SWITCH_EXACT;
16605 else if (strncmp(option, "-glob", 2) == 0)
16606 matchOpt = SWITCH_GLOB;
16607 else if (strncmp(option, "-regexp", 2) == 0)
16608 matchOpt = SWITCH_RE;
16609 else if (strncmp(option, "-command", 2) == 0) {
16610 matchOpt = SWITCH_CMD;
16611 if ((argc - opt) < 2)
16612 goto wrongnumargs;
16613 command = argv[++opt];
16615 else {
16616 Jim_SetResultFormatted(interp,
16617 "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
16618 argv[opt]);
16619 return JIM_ERR;
16621 if ((argc - opt) < 2)
16622 goto wrongnumargs;
16624 strObj = argv[opt++];
16625 patCount = argc - opt;
16626 if (patCount == 1) {
16627 JimListGetElements(interp, argv[opt], &patCount, &caseList);
16629 else
16630 caseList = (Jim_Obj **)&argv[opt];
16631 if (patCount == 0 || patCount % 2 != 0)
16632 goto wrongnumargs;
16633 for (i = 0; scriptObj == NULL && i < patCount; i += 2) {
16634 Jim_Obj *patObj = caseList[i];
16636 if (!Jim_CompareStringImmediate(interp, patObj, "default")
16637 || i < (patCount - 2)) {
16638 switch (matchOpt) {
16639 case SWITCH_EXACT:
16640 if (Jim_StringEqObj(strObj, patObj))
16641 scriptObj = caseList[i + 1];
16642 break;
16643 case SWITCH_GLOB:
16644 if (Jim_StringMatchObj(interp, patObj, strObj, 0))
16645 scriptObj = caseList[i + 1];
16646 break;
16647 case SWITCH_RE:
16648 command = Jim_NewStringObj(interp, "regexp", -1);
16650 case SWITCH_CMD:{
16651 int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, 0);
16653 if (argc - opt == 1) {
16654 JimListGetElements(interp, argv[opt], &patCount, &caseList);
16657 if (rc < 0) {
16658 return -rc;
16660 if (rc)
16661 scriptObj = caseList[i + 1];
16662 break;
16666 else {
16667 scriptObj = caseList[i + 1];
16670 for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2)
16671 scriptObj = caseList[i + 1];
16672 if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) {
16673 Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
16674 return JIM_ERR;
16676 Jim_SetEmptyResult(interp);
16677 if (scriptObj) {
16678 return Jim_EvalObj(interp, scriptObj);
16680 return JIM_OK;
16684 static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16686 Jim_Obj *listObjPtr;
16688 listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1);
16689 Jim_SetResult(interp, listObjPtr);
16690 return JIM_OK;
16694 static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16696 Jim_Obj *objPtr, *listObjPtr;
16697 int i;
16698 int idx;
16700 if (argc < 2) {
16701 Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
16702 return JIM_ERR;
16704 objPtr = argv[1];
16705 Jim_IncrRefCount(objPtr);
16706 for (i = 2; i < argc; i++) {
16707 listObjPtr = objPtr;
16708 if (Jim_GetIndex(interp, argv[i], &idx) != JIM_OK) {
16709 Jim_DecrRefCount(interp, listObjPtr);
16710 return JIM_ERR;
16712 if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_NONE) != JIM_OK) {
16713 Jim_DecrRefCount(interp, listObjPtr);
16714 Jim_SetEmptyResult(interp);
16715 return JIM_OK;
16717 Jim_IncrRefCount(objPtr);
16718 Jim_DecrRefCount(interp, listObjPtr);
16720 Jim_SetResult(interp, objPtr);
16721 Jim_DecrRefCount(interp, objPtr);
16722 return JIM_OK;
16726 static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16728 if (argc != 2) {
16729 Jim_WrongNumArgs(interp, 1, argv, "list");
16730 return JIM_ERR;
16732 Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
16733 return JIM_OK;
16737 static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16739 static const char * const options[] = {
16740 "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
16741 NULL
16743 enum
16744 { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
16745 OPT_COMMAND };
16746 int i;
16747 int opt_bool = 0;
16748 int opt_not = 0;
16749 int opt_nocase = 0;
16750 int opt_all = 0;
16751 int opt_inline = 0;
16752 int opt_match = OPT_EXACT;
16753 int listlen;
16754 int rc = JIM_OK;
16755 Jim_Obj *listObjPtr = NULL;
16756 Jim_Obj *commandObj = NULL;
16758 if (argc < 3) {
16759 wrongargs:
16760 Jim_WrongNumArgs(interp, 1, argv,
16761 "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? list value");
16762 return JIM_ERR;
16765 for (i = 1; i < argc - 2; i++) {
16766 int option;
16768 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
16769 return JIM_ERR;
16771 switch (option) {
16772 case OPT_BOOL:
16773 opt_bool = 1;
16774 opt_inline = 0;
16775 break;
16776 case OPT_NOT:
16777 opt_not = 1;
16778 break;
16779 case OPT_NOCASE:
16780 opt_nocase = 1;
16781 break;
16782 case OPT_INLINE:
16783 opt_inline = 1;
16784 opt_bool = 0;
16785 break;
16786 case OPT_ALL:
16787 opt_all = 1;
16788 break;
16789 case OPT_COMMAND:
16790 if (i >= argc - 2) {
16791 goto wrongargs;
16793 commandObj = argv[++i];
16795 case OPT_EXACT:
16796 case OPT_GLOB:
16797 case OPT_REGEXP:
16798 opt_match = option;
16799 break;
16803 argv += i;
16805 if (opt_all) {
16806 listObjPtr = Jim_NewListObj(interp, NULL, 0);
16808 if (opt_match == OPT_REGEXP) {
16809 commandObj = Jim_NewStringObj(interp, "regexp", -1);
16811 if (commandObj) {
16812 Jim_IncrRefCount(commandObj);
16815 listlen = Jim_ListLength(interp, argv[0]);
16816 for (i = 0; i < listlen; i++) {
16817 int eq = 0;
16818 Jim_Obj *objPtr = Jim_ListGetIndex(interp, argv[0], i);
16820 switch (opt_match) {
16821 case OPT_EXACT:
16822 eq = Jim_StringCompareObj(interp, argv[1], objPtr, opt_nocase) == 0;
16823 break;
16825 case OPT_GLOB:
16826 eq = Jim_StringMatchObj(interp, argv[1], objPtr, opt_nocase);
16827 break;
16829 case OPT_REGEXP:
16830 case OPT_COMMAND:
16831 eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, opt_nocase);
16832 if (eq < 0) {
16833 if (listObjPtr) {
16834 Jim_FreeNewObj(interp, listObjPtr);
16836 rc = JIM_ERR;
16837 goto done;
16839 break;
16843 if (!eq && opt_bool && opt_not && !opt_all) {
16844 continue;
16847 if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
16849 Jim_Obj *resultObj;
16851 if (opt_bool) {
16852 resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
16854 else if (!opt_inline) {
16855 resultObj = Jim_NewIntObj(interp, i);
16857 else {
16858 resultObj = objPtr;
16861 if (opt_all) {
16862 Jim_ListAppendElement(interp, listObjPtr, resultObj);
16864 else {
16865 Jim_SetResult(interp, resultObj);
16866 goto done;
16871 if (opt_all) {
16872 Jim_SetResult(interp, listObjPtr);
16874 else {
16876 if (opt_bool) {
16877 Jim_SetResultBool(interp, opt_not);
16879 else if (!opt_inline) {
16880 Jim_SetResultInt(interp, -1);
16884 done:
16885 if (commandObj) {
16886 Jim_DecrRefCount(interp, commandObj);
16888 return rc;
16892 static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16894 Jim_Obj *listObjPtr;
16895 int new_obj = 0;
16896 int i;
16898 if (argc < 2) {
16899 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
16900 return JIM_ERR;
16902 listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
16903 if (!listObjPtr) {
16905 listObjPtr = Jim_NewListObj(interp, NULL, 0);
16906 new_obj = 1;
16908 else if (Jim_IsShared(listObjPtr)) {
16909 listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
16910 new_obj = 1;
16912 for (i = 2; i < argc; i++)
16913 Jim_ListAppendElement(interp, listObjPtr, argv[i]);
16914 if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
16915 if (new_obj)
16916 Jim_FreeNewObj(interp, listObjPtr);
16917 return JIM_ERR;
16919 Jim_SetResult(interp, listObjPtr);
16920 return JIM_OK;
16924 static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16926 int idx, len;
16927 Jim_Obj *listPtr;
16929 if (argc < 3) {
16930 Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
16931 return JIM_ERR;
16933 listPtr = argv[1];
16934 if (Jim_IsShared(listPtr))
16935 listPtr = Jim_DuplicateObj(interp, listPtr);
16936 if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
16937 goto err;
16938 len = Jim_ListLength(interp, listPtr);
16939 if (idx >= len)
16940 idx = len;
16941 else if (idx < 0)
16942 idx = len + idx + 1;
16943 Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
16944 Jim_SetResult(interp, listPtr);
16945 return JIM_OK;
16946 err:
16947 if (listPtr != argv[1]) {
16948 Jim_FreeNewObj(interp, listPtr);
16950 return JIM_ERR;
16954 static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16956 int first, last, len, rangeLen;
16957 Jim_Obj *listObj;
16958 Jim_Obj *newListObj;
16960 if (argc < 4) {
16961 Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
16962 return JIM_ERR;
16964 if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
16965 Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
16966 return JIM_ERR;
16969 listObj = argv[1];
16970 len = Jim_ListLength(interp, listObj);
16972 first = JimRelToAbsIndex(len, first);
16973 last = JimRelToAbsIndex(len, last);
16974 JimRelToAbsRange(len, &first, &last, &rangeLen);
16977 if (first > len) {
16978 first = len;
16982 newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
16985 ListInsertElements(newListObj, -1, argc - 4, argv + 4);
16988 ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
16990 Jim_SetResult(interp, newListObj);
16991 return JIM_OK;
16995 static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
16997 if (argc < 3) {
16998 Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal");
16999 return JIM_ERR;
17001 else if (argc == 3) {
17003 if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
17004 return JIM_ERR;
17005 Jim_SetResult(interp, argv[2]);
17006 return JIM_OK;
17008 return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
17012 static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
17014 static const char * const options[] = {
17015 "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", NULL
17017 enum
17018 { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE };
17019 Jim_Obj *resObj;
17020 int i;
17021 int retCode;
17022 int shared;
17024 struct lsort_info info;
17026 if (argc < 2) {
17027 Jim_WrongNumArgs(interp, 1, argv, "?options? list");
17028 return JIM_ERR;
17031 info.type = JIM_LSORT_ASCII;
17032 info.order = 1;
17033 info.indexed = 0;
17034 info.unique = 0;
17035 info.command = NULL;
17036 info.interp = interp;
17038 for (i = 1; i < (argc - 1); i++) {
17039 int option;
17041 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
17042 != JIM_OK)
17043 return JIM_ERR;
17044 switch (option) {
17045 case OPT_ASCII:
17046 info.type = JIM_LSORT_ASCII;
17047 break;
17048 case OPT_NOCASE:
17049 info.type = JIM_LSORT_NOCASE;
17050 break;
17051 case OPT_INTEGER:
17052 info.type = JIM_LSORT_INTEGER;
17053 break;
17054 case OPT_REAL:
17055 info.type = JIM_LSORT_REAL;
17056 break;
17057 case OPT_INCREASING:
17058 info.order = 1;
17059 break;
17060 case OPT_DECREASING:
17061 info.order = -1;
17062 break;
17063 case OPT_UNIQUE:
17064 info.unique = 1;
17065 break;
17066 case OPT_COMMAND:
17067 if (i >= (argc - 2)) {
17068 Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
17069 return JIM_ERR;
17071 info.type = JIM_LSORT_COMMAND;
17072 info.command = argv[i + 1];
17073 i++;
17074 break;
17075 case OPT_INDEX:
17076 if (i >= (argc - 2)) {
17077 Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
17078 return JIM_ERR;
17080 if (Jim_GetIndex(interp, argv[i + 1], &info.index) != JIM_OK) {
17081 return JIM_ERR;
17083 info.indexed = 1;
17084 i++;
17085 break;
17088 resObj = argv[argc - 1];
17089 if ((shared = Jim_IsShared(resObj)))
17090 resObj = Jim_DuplicateObj(interp, resObj);
17091 retCode = ListSortElements(interp, resObj, &info);
17092 if (retCode == JIM_OK) {
17093 Jim_SetResult(interp, resObj);
17095 else if (shared) {
17096 Jim_FreeNewObj(interp, resObj);
17098 return retCode;
17102 static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17104 Jim_Obj *stringObjPtr;
17105 int i;
17107 if (argc < 2) {
17108 Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
17109 return JIM_ERR;
17111 if (argc == 2) {
17112 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17113 if (!stringObjPtr)
17114 return JIM_ERR;
17116 else {
17117 int new_obj = 0;
17118 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
17119 if (!stringObjPtr) {
17121 stringObjPtr = Jim_NewEmptyStringObj(interp);
17122 new_obj = 1;
17124 else if (Jim_IsShared(stringObjPtr)) {
17125 new_obj = 1;
17126 stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
17128 for (i = 2; i < argc; i++) {
17129 Jim_AppendObj(interp, stringObjPtr, argv[i]);
17131 if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
17132 if (new_obj) {
17133 Jim_FreeNewObj(interp, stringObjPtr);
17135 return JIM_ERR;
17138 Jim_SetResult(interp, stringObjPtr);
17139 return JIM_OK;
17144 static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17146 #if !defined(JIM_DEBUG_COMMAND)
17147 Jim_SetResultString(interp, "unsupported", -1);
17148 return JIM_ERR;
17149 #endif
17153 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17155 int rc;
17157 if (argc < 2) {
17158 Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
17159 return JIM_ERR;
17162 if (argc == 2) {
17163 rc = Jim_EvalObj(interp, argv[1]);
17165 else {
17166 rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17169 if (rc == JIM_ERR) {
17171 interp->addStackTrace++;
17173 return rc;
17177 static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17179 if (argc >= 2) {
17180 int retcode;
17181 Jim_CallFrame *savedCallFrame, *targetCallFrame;
17182 const char *str;
17185 savedCallFrame = interp->framePtr;
17188 str = Jim_String(argv[1]);
17189 if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
17190 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17191 argc--;
17192 argv++;
17194 else {
17195 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17197 if (targetCallFrame == NULL) {
17198 return JIM_ERR;
17200 if (argc < 2) {
17201 Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
17202 return JIM_ERR;
17205 interp->framePtr = targetCallFrame;
17206 if (argc == 2) {
17207 retcode = Jim_EvalObj(interp, argv[1]);
17209 else {
17210 retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17212 interp->framePtr = savedCallFrame;
17213 return retcode;
17215 else {
17216 Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
17217 return JIM_ERR;
17222 static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17224 int retcode;
17226 if (argc == 2) {
17227 retcode = Jim_EvalExpression(interp, argv[1]);
17229 else if (argc > 2) {
17230 Jim_Obj *objPtr;
17232 objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
17233 Jim_IncrRefCount(objPtr);
17234 retcode = Jim_EvalExpression(interp, objPtr);
17235 Jim_DecrRefCount(interp, objPtr);
17237 else {
17238 Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
17239 return JIM_ERR;
17241 if (retcode != JIM_OK)
17242 return retcode;
17243 return JIM_OK;
17247 static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17249 if (argc != 1) {
17250 Jim_WrongNumArgs(interp, 1, argv, "");
17251 return JIM_ERR;
17253 return JIM_BREAK;
17257 static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17259 if (argc != 1) {
17260 Jim_WrongNumArgs(interp, 1, argv, "");
17261 return JIM_ERR;
17263 return JIM_CONTINUE;
17267 static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17269 int i;
17270 Jim_Obj *stackTraceObj = NULL;
17271 Jim_Obj *errorCodeObj = NULL;
17272 int returnCode = JIM_OK;
17273 long level = 1;
17275 for (i = 1; i < argc - 1; i += 2) {
17276 if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
17277 if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
17278 return JIM_ERR;
17281 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
17282 stackTraceObj = argv[i + 1];
17284 else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
17285 errorCodeObj = argv[i + 1];
17287 else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
17288 if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
17289 Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
17290 return JIM_ERR;
17293 else {
17294 break;
17298 if (i != argc - 1 && i != argc) {
17299 Jim_WrongNumArgs(interp, 1, argv,
17300 "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
17304 if (stackTraceObj && returnCode == JIM_ERR) {
17305 JimSetStackTrace(interp, stackTraceObj);
17308 if (errorCodeObj && returnCode == JIM_ERR) {
17309 Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
17311 interp->returnCode = returnCode;
17312 interp->returnLevel = level;
17314 if (i == argc - 1) {
17315 Jim_SetResult(interp, argv[i]);
17317 return JIM_RETURN;
17321 static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17323 if (interp->framePtr->level == 0) {
17324 Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
17325 return JIM_ERR;
17327 else if (argc >= 2) {
17329 Jim_CallFrame *cf = interp->framePtr->parent;
17331 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17332 if (cmdPtr == NULL) {
17333 return JIM_ERR;
17336 JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));
17339 JimIncrCmdRefCount(cmdPtr);
17340 cf->tailcallCmd = cmdPtr;
17343 JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));
17345 cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
17346 Jim_IncrRefCount(cf->tailcallObj);
17349 return JIM_EVAL;
17351 return JIM_OK;
17354 static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17356 Jim_Obj *cmdList;
17357 Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
17360 cmdList = Jim_DuplicateObj(interp, prefixListObj);
17361 Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
17363 return JimEvalObjList(interp, cmdList);
17366 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
17368 Jim_Obj *prefixListObj = privData;
17369 Jim_DecrRefCount(interp, prefixListObj);
17372 static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17374 Jim_Obj *prefixListObj;
17375 const char *newname;
17377 if (argc < 3) {
17378 Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
17379 return JIM_ERR;
17382 prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
17383 Jim_IncrRefCount(prefixListObj);
17384 newname = Jim_String(argv[1]);
17385 if (newname[0] == ':' && newname[1] == ':') {
17386 while (*++newname == ':') {
17390 Jim_SetResult(interp, argv[1]);
17392 return Jim_CreateCommand(interp, newname, JimAliasCmd, prefixListObj, JimAliasCmdDelete);
17396 static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17398 Jim_Cmd *cmd;
17400 if (argc != 4 && argc != 5) {
17401 Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
17402 return JIM_ERR;
17405 if (JimValidName(interp, "procedure", argv[1]) != JIM_OK) {
17406 return JIM_ERR;
17409 if (argc == 4) {
17410 cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
17412 else {
17413 cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
17416 if (cmd) {
17418 Jim_Obj *qualifiedCmdNameObj;
17419 const char *cmdname = JimQualifyName(interp, Jim_String(argv[1]), &qualifiedCmdNameObj);
17421 JimCreateCommand(interp, cmdname, cmd);
17424 JimUpdateProcNamespace(interp, cmd, cmdname);
17426 JimFreeQualifiedName(interp, qualifiedCmdNameObj);
17429 Jim_SetResult(interp, argv[1]);
17430 return JIM_OK;
17432 return JIM_ERR;
17436 static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17438 int retcode;
17440 if (argc < 2) {
17441 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17442 return JIM_ERR;
17446 interp->local++;
17447 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17448 interp->local--;
17452 if (retcode == 0) {
17453 Jim_Obj *cmdNameObj = Jim_GetResult(interp);
17455 if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
17456 return JIM_ERR;
17458 if (interp->framePtr->localCommands == NULL) {
17459 interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
17460 Jim_InitStack(interp->framePtr->localCommands);
17462 Jim_IncrRefCount(cmdNameObj);
17463 Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
17466 return retcode;
17470 static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17472 if (argc < 2) {
17473 Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
17474 return JIM_ERR;
17476 else {
17477 int retcode;
17479 Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
17480 if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
17481 Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
17482 return JIM_ERR;
17485 cmdPtr->u.proc.upcall++;
17486 JimIncrCmdRefCount(cmdPtr);
17489 retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
17492 cmdPtr->u.proc.upcall--;
17493 JimDecrCmdRefCount(interp, cmdPtr);
17495 return retcode;
17500 static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17502 if (argc < 2) {
17503 Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
17504 return JIM_ERR;
17506 else {
17507 int ret;
17508 Jim_Cmd *cmd;
17509 Jim_Obj *argListObjPtr;
17510 Jim_Obj *bodyObjPtr;
17511 Jim_Obj *nsObj = NULL;
17512 Jim_Obj **nargv;
17514 int len = Jim_ListLength(interp, argv[1]);
17515 if (len != 2 && len != 3) {
17516 Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
17517 return JIM_ERR;
17520 if (len == 3) {
17521 #ifdef jim_ext_namespace
17523 nsObj = JimQualifyNameObj(interp, Jim_ListGetIndex(interp, argv[1], 2));
17524 #else
17525 Jim_SetResultString(interp, "namespaces not enabled", -1);
17526 return JIM_ERR;
17527 #endif
17529 argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
17530 bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
17532 cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
17534 if (cmd) {
17536 nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
17537 nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
17538 Jim_IncrRefCount(nargv[0]);
17539 memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
17540 ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
17541 Jim_DecrRefCount(interp, nargv[0]);
17542 Jim_Free(nargv);
17544 JimDecrCmdRefCount(interp, cmd);
17545 return ret;
17547 return JIM_ERR;
17553 static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17555 Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
17556 return JIM_OK;
17560 static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17562 int i;
17563 Jim_CallFrame *targetCallFrame;
17566 if (argc > 3 && (argc % 2 == 0)) {
17567 targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
17568 argc--;
17569 argv++;
17571 else {
17572 targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
17574 if (targetCallFrame == NULL) {
17575 return JIM_ERR;
17579 if (argc < 3) {
17580 Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
17581 return JIM_ERR;
17585 for (i = 1; i < argc; i += 2) {
17586 if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK)
17587 return JIM_ERR;
17589 return JIM_OK;
17593 static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17595 int i;
17597 if (argc < 2) {
17598 Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?");
17599 return JIM_ERR;
17602 if (interp->framePtr->level == 0)
17603 return JIM_OK;
17604 for (i = 1; i < argc; i++) {
17606 const char *name = Jim_String(argv[i]);
17607 if (name[0] != ':' || name[1] != ':') {
17608 if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
17609 return JIM_ERR;
17612 return JIM_OK;
17615 static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
17616 Jim_Obj *objPtr, int nocase)
17618 int numMaps;
17619 const char *str, *noMatchStart = NULL;
17620 int strLen, i;
17621 Jim_Obj *resultObjPtr;
17623 numMaps = Jim_ListLength(interp, mapListObjPtr);
17624 if (numMaps % 2) {
17625 Jim_SetResultString(interp, "list must contain an even number of elements", -1);
17626 return NULL;
17629 str = Jim_String(objPtr);
17630 strLen = Jim_Utf8Length(interp, objPtr);
17633 resultObjPtr = Jim_NewStringObj(interp, "", 0);
17634 while (strLen) {
17635 for (i = 0; i < numMaps; i += 2) {
17636 Jim_Obj *eachObjPtr;
17637 const char *k;
17638 int kl;
17640 eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
17641 k = Jim_String(eachObjPtr);
17642 kl = Jim_Utf8Length(interp, eachObjPtr);
17644 if (strLen >= kl && kl) {
17645 int rc;
17646 rc = JimStringCompareLen(str, k, kl, nocase);
17647 if (rc == 0) {
17648 if (noMatchStart) {
17649 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17650 noMatchStart = NULL;
17652 Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
17653 str += utf8_index(str, kl);
17654 strLen -= kl;
17655 break;
17659 if (i == numMaps) {
17660 int c;
17661 if (noMatchStart == NULL)
17662 noMatchStart = str;
17663 str += utf8_tounicode(str, &c);
17664 strLen--;
17667 if (noMatchStart) {
17668 Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
17670 return resultObjPtr;
17674 static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17676 int len;
17677 int opt_case = 1;
17678 int option;
17679 static const char * const options[] = {
17680 "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace",
17681 "map", "repeat", "reverse", "index", "first", "last", "cat",
17682 "trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL
17684 enum
17686 OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_REPLACE,
17687 OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST, OPT_CAT,
17688 OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE
17690 static const char * const nocase_options[] = {
17691 "-nocase", NULL
17693 static const char * const nocase_length_options[] = {
17694 "-nocase", "-length", NULL
17697 if (argc < 2) {
17698 Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?");
17699 return JIM_ERR;
17701 if (Jim_GetEnum(interp, argv[1], options, &option, NULL,
17702 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK)
17703 return Jim_CheckShowCommands(interp, argv[1], options);
17705 switch (option) {
17706 case OPT_LENGTH:
17707 case OPT_BYTELENGTH:
17708 if (argc != 3) {
17709 Jim_WrongNumArgs(interp, 2, argv, "string");
17710 return JIM_ERR;
17712 if (option == OPT_LENGTH) {
17713 len = Jim_Utf8Length(interp, argv[2]);
17715 else {
17716 len = Jim_Length(argv[2]);
17718 Jim_SetResultInt(interp, len);
17719 return JIM_OK;
17721 case OPT_CAT:{
17722 Jim_Obj *objPtr;
17723 if (argc == 3) {
17725 objPtr = argv[2];
17727 else {
17728 int i;
17730 objPtr = Jim_NewStringObj(interp, "", 0);
17732 for (i = 2; i < argc; i++) {
17733 Jim_AppendObj(interp, objPtr, argv[i]);
17736 Jim_SetResult(interp, objPtr);
17737 return JIM_OK;
17740 case OPT_COMPARE:
17741 case OPT_EQUAL:
17744 long opt_length = -1;
17745 int n = argc - 4;
17746 int i = 2;
17747 while (n > 0) {
17748 int subopt;
17749 if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
17750 JIM_ENUM_ABBREV) != JIM_OK) {
17751 badcompareargs:
17752 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? ?-length int? string1 string2");
17753 return JIM_ERR;
17755 if (subopt == 0) {
17757 opt_case = 0;
17758 n--;
17760 else {
17762 if (n < 2) {
17763 goto badcompareargs;
17765 if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
17766 return JIM_ERR;
17768 n -= 2;
17771 if (n) {
17772 goto badcompareargs;
17774 argv += argc - 2;
17775 if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
17777 Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
17779 else {
17780 if (opt_length >= 0) {
17781 n = JimStringCompareLen(Jim_String(argv[0]), Jim_String(argv[1]), opt_length, !opt_case);
17783 else {
17784 n = Jim_StringCompareObj(interp, argv[0], argv[1], !opt_case);
17786 Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
17788 return JIM_OK;
17791 case OPT_MATCH:
17792 if (argc != 4 &&
17793 (argc != 5 ||
17794 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17795 JIM_ENUM_ABBREV) != JIM_OK)) {
17796 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
17797 return JIM_ERR;
17799 if (opt_case == 0) {
17800 argv++;
17802 Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
17803 return JIM_OK;
17805 case OPT_MAP:{
17806 Jim_Obj *objPtr;
17808 if (argc != 4 &&
17809 (argc != 5 ||
17810 Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
17811 JIM_ENUM_ABBREV) != JIM_OK)) {
17812 Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
17813 return JIM_ERR;
17816 if (opt_case == 0) {
17817 argv++;
17819 objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
17820 if (objPtr == NULL) {
17821 return JIM_ERR;
17823 Jim_SetResult(interp, objPtr);
17824 return JIM_OK;
17827 case OPT_RANGE:
17828 case OPT_BYTERANGE:{
17829 Jim_Obj *objPtr;
17831 if (argc != 5) {
17832 Jim_WrongNumArgs(interp, 2, argv, "string first last");
17833 return JIM_ERR;
17835 if (option == OPT_RANGE) {
17836 objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
17838 else
17840 objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
17843 if (objPtr == NULL) {
17844 return JIM_ERR;
17846 Jim_SetResult(interp, objPtr);
17847 return JIM_OK;
17850 case OPT_REPLACE:{
17851 Jim_Obj *objPtr;
17853 if (argc != 5 && argc != 6) {
17854 Jim_WrongNumArgs(interp, 2, argv, "string first last ?string?");
17855 return JIM_ERR;
17857 objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
17858 if (objPtr == NULL) {
17859 return JIM_ERR;
17861 Jim_SetResult(interp, objPtr);
17862 return JIM_OK;
17866 case OPT_REPEAT:{
17867 Jim_Obj *objPtr;
17868 jim_wide count;
17870 if (argc != 4) {
17871 Jim_WrongNumArgs(interp, 2, argv, "string count");
17872 return JIM_ERR;
17874 if (Jim_GetWide(interp, argv[3], &count) != JIM_OK) {
17875 return JIM_ERR;
17877 objPtr = Jim_NewStringObj(interp, "", 0);
17878 if (count > 0) {
17879 while (count--) {
17880 Jim_AppendObj(interp, objPtr, argv[2]);
17883 Jim_SetResult(interp, objPtr);
17884 return JIM_OK;
17887 case OPT_REVERSE:{
17888 char *buf, *p;
17889 const char *str;
17890 int i;
17892 if (argc != 3) {
17893 Jim_WrongNumArgs(interp, 2, argv, "string");
17894 return JIM_ERR;
17897 str = Jim_GetString(argv[2], &len);
17898 buf = Jim_Alloc(len + 1);
17899 p = buf + len;
17900 *p = 0;
17901 for (i = 0; i < len; ) {
17902 int c;
17903 int l = utf8_tounicode(str, &c);
17904 memcpy(p - l, str, l);
17905 p -= l;
17906 i += l;
17907 str += l;
17909 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
17910 return JIM_OK;
17913 case OPT_INDEX:{
17914 int idx;
17915 const char *str;
17917 if (argc != 4) {
17918 Jim_WrongNumArgs(interp, 2, argv, "string index");
17919 return JIM_ERR;
17921 if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
17922 return JIM_ERR;
17924 str = Jim_String(argv[2]);
17925 len = Jim_Utf8Length(interp, argv[2]);
17926 if (idx != INT_MIN && idx != INT_MAX) {
17927 idx = JimRelToAbsIndex(len, idx);
17929 if (idx < 0 || idx >= len || str == NULL) {
17930 Jim_SetResultString(interp, "", 0);
17932 else if (len == Jim_Length(argv[2])) {
17934 Jim_SetResultString(interp, str + idx, 1);
17936 else {
17937 int c;
17938 int i = utf8_index(str, idx);
17939 Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
17941 return JIM_OK;
17944 case OPT_FIRST:
17945 case OPT_LAST:{
17946 int idx = 0, l1, l2;
17947 const char *s1, *s2;
17949 if (argc != 4 && argc != 5) {
17950 Jim_WrongNumArgs(interp, 2, argv, "subString string ?index?");
17951 return JIM_ERR;
17953 s1 = Jim_String(argv[2]);
17954 s2 = Jim_String(argv[3]);
17955 l1 = Jim_Utf8Length(interp, argv[2]);
17956 l2 = Jim_Utf8Length(interp, argv[3]);
17957 if (argc == 5) {
17958 if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
17959 return JIM_ERR;
17961 idx = JimRelToAbsIndex(l2, idx);
17963 else if (option == OPT_LAST) {
17964 idx = l2;
17966 if (option == OPT_FIRST) {
17967 Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
17969 else {
17970 #ifdef JIM_UTF8
17971 Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
17972 #else
17973 Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
17974 #endif
17976 return JIM_OK;
17979 case OPT_TRIM:
17980 case OPT_TRIMLEFT:
17981 case OPT_TRIMRIGHT:{
17982 Jim_Obj *trimchars;
17984 if (argc != 3 && argc != 4) {
17985 Jim_WrongNumArgs(interp, 2, argv, "string ?trimchars?");
17986 return JIM_ERR;
17988 trimchars = (argc == 4 ? argv[3] : NULL);
17989 if (option == OPT_TRIM) {
17990 Jim_SetResult(interp, JimStringTrim(interp, argv[2], trimchars));
17992 else if (option == OPT_TRIMLEFT) {
17993 Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], trimchars));
17995 else if (option == OPT_TRIMRIGHT) {
17996 Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], trimchars));
17998 return JIM_OK;
18001 case OPT_TOLOWER:
18002 case OPT_TOUPPER:
18003 case OPT_TOTITLE:
18004 if (argc != 3) {
18005 Jim_WrongNumArgs(interp, 2, argv, "string");
18006 return JIM_ERR;
18008 if (option == OPT_TOLOWER) {
18009 Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
18011 else if (option == OPT_TOUPPER) {
18012 Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
18014 else {
18015 Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
18017 return JIM_OK;
18019 case OPT_IS:
18020 if (argc == 4 || (argc == 5 && Jim_CompareStringImmediate(interp, argv[3], "-strict"))) {
18021 return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
18023 Jim_WrongNumArgs(interp, 2, argv, "class ?-strict? str");
18024 return JIM_ERR;
18026 return JIM_OK;
18030 static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18032 long i, count = 1;
18033 jim_wide start, elapsed;
18034 char buf[60];
18035 const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration";
18037 if (argc < 2) {
18038 Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
18039 return JIM_ERR;
18041 if (argc == 3) {
18042 if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
18043 return JIM_ERR;
18045 if (count < 0)
18046 return JIM_OK;
18047 i = count;
18048 start = JimClock();
18049 while (i-- > 0) {
18050 int retval;
18052 retval = Jim_EvalObj(interp, argv[1]);
18053 if (retval != JIM_OK) {
18054 return retval;
18057 elapsed = JimClock() - start;
18058 sprintf(buf, fmt, count == 0 ? 0 : elapsed / count);
18059 Jim_SetResultString(interp, buf, -1);
18060 return JIM_OK;
18064 static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18066 long exitCode = 0;
18068 if (argc > 2) {
18069 Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
18070 return JIM_ERR;
18072 if (argc == 2) {
18073 if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
18074 return JIM_ERR;
18076 interp->exitCode = exitCode;
18077 return JIM_EXIT;
18081 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18083 int exitCode = 0;
18084 int i;
18085 int sig = 0;
18088 jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
18089 static const int max_ignore_code = sizeof(ignore_mask) * 8;
18091 Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
18093 for (i = 1; i < argc - 1; i++) {
18094 const char *arg = Jim_String(argv[i]);
18095 jim_wide option;
18096 int ignore;
18099 if (strcmp(arg, "--") == 0) {
18100 i++;
18101 break;
18103 if (*arg != '-') {
18104 break;
18107 if (strncmp(arg, "-no", 3) == 0) {
18108 arg += 3;
18109 ignore = 1;
18111 else {
18112 arg++;
18113 ignore = 0;
18116 if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
18117 option = -1;
18119 if (option < 0) {
18120 option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
18122 if (option < 0) {
18123 goto wrongargs;
18126 if (ignore) {
18127 ignore_mask |= ((jim_wide)1 << option);
18129 else {
18130 ignore_mask &= (~((jim_wide)1 << option));
18134 argc -= i;
18135 if (argc < 1 || argc > 3) {
18136 wrongargs:
18137 Jim_WrongNumArgs(interp, 1, argv,
18138 "?-?no?code ... --? script ?resultVarName? ?optionVarName?");
18139 return JIM_ERR;
18141 argv += i;
18143 if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
18144 sig++;
18147 interp->signal_level += sig;
18148 if (Jim_CheckSignal(interp)) {
18150 exitCode = JIM_SIGNAL;
18152 else {
18153 exitCode = Jim_EvalObj(interp, argv[0]);
18155 interp->errorFlag = 0;
18157 interp->signal_level -= sig;
18160 if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
18162 return exitCode;
18165 if (sig && exitCode == JIM_SIGNAL) {
18167 if (interp->signal_set_result) {
18168 interp->signal_set_result(interp, interp->sigmask);
18170 else {
18171 Jim_SetResultInt(interp, interp->sigmask);
18173 interp->sigmask = 0;
18176 if (argc >= 2) {
18177 if (Jim_SetVariable(interp, argv[1], Jim_GetResult(interp)) != JIM_OK) {
18178 return JIM_ERR;
18180 if (argc == 3) {
18181 Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
18183 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
18184 Jim_ListAppendElement(interp, optListObj,
18185 Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
18186 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
18187 Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
18188 if (exitCode == JIM_ERR) {
18189 Jim_Obj *errorCode;
18190 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
18191 -1));
18192 Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
18194 errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
18195 if (errorCode) {
18196 Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
18197 Jim_ListAppendElement(interp, optListObj, errorCode);
18200 if (Jim_SetVariable(interp, argv[2], optListObj) != JIM_OK) {
18201 return JIM_ERR;
18205 Jim_SetResultInt(interp, exitCode);
18206 return JIM_OK;
18211 static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18213 if (argc != 3) {
18214 Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
18215 return JIM_ERR;
18218 if (JimValidName(interp, "new procedure", argv[2])) {
18219 return JIM_ERR;
18222 return Jim_RenameCommand(interp, Jim_String(argv[1]), Jim_String(argv[2]));
18225 #define JIM_DICTMATCH_KEYS 0x0001
18226 #define JIM_DICTMATCH_VALUES 0x002
18228 int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
18230 Jim_HashEntry *he;
18231 Jim_Obj *listObjPtr;
18232 Jim_HashTableIterator htiter;
18234 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18235 return JIM_ERR;
18238 listObjPtr = Jim_NewListObj(interp, NULL, 0);
18240 JimInitHashTableIterator(objPtr->internalRep.ptr, &htiter);
18241 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18242 if (patternObj) {
18243 Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? (Jim_Obj *)he->key : Jim_GetHashEntryVal(he);
18244 if (!JimGlobMatch(Jim_String(patternObj), Jim_String(matchObj), 0)) {
18246 continue;
18249 if (return_types & JIM_DICTMATCH_KEYS) {
18250 Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key);
18252 if (return_types & JIM_DICTMATCH_VALUES) {
18253 Jim_ListAppendElement(interp, listObjPtr, Jim_GetHashEntryVal(he));
18257 Jim_SetResult(interp, listObjPtr);
18258 return JIM_OK;
18261 int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
18263 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18264 return -1;
18266 return ((Jim_HashTable *)objPtr->internalRep.ptr)->used;
18269 Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
18271 Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
18272 int i;
18274 JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));
18278 for (i = 0; i < objc; i++) {
18279 Jim_HashTable *ht;
18280 Jim_HashTableIterator htiter;
18281 Jim_HashEntry *he;
18283 if (SetDictFromAny(interp, objv[i]) != JIM_OK) {
18284 Jim_FreeNewObj(interp, objPtr);
18285 return NULL;
18287 ht = objv[i]->internalRep.ptr;
18288 JimInitHashTableIterator(ht, &htiter);
18289 while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
18290 Jim_ReplaceHashEntry(objPtr->internalRep.ptr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he));
18293 return objPtr;
18296 int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
18298 Jim_HashTable *ht;
18299 unsigned int i;
18300 char buffer[100];
18301 int sum = 0;
18302 int nonzero_count = 0;
18303 Jim_Obj *output;
18304 int bucket_counts[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
18306 if (SetDictFromAny(interp, objPtr) != JIM_OK) {
18307 return JIM_ERR;
18310 ht = (Jim_HashTable *)objPtr->internalRep.ptr;
18313 snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets\n", ht->used, ht->size);
18314 output = Jim_NewStringObj(interp, buffer, -1);
18316 for (i = 0; i < ht->size; i++) {
18317 Jim_HashEntry *he = ht->table[i];
18318 int entries = 0;
18319 while (he) {
18320 entries++;
18321 he = he->next;
18323 if (entries > 9) {
18324 bucket_counts[10]++;
18326 else {
18327 bucket_counts[entries]++;
18329 if (entries) {
18330 sum += entries;
18331 nonzero_count++;
18334 for (i = 0; i < 10; i++) {
18335 snprintf(buffer, sizeof(buffer), "number of buckets with %d entries: %d\n", i, bucket_counts[i]);
18336 Jim_AppendString(interp, output, buffer, -1);
18338 snprintf(buffer, sizeof(buffer), "number of buckets with 10 or more entries: %d\n", bucket_counts[10]);
18339 Jim_AppendString(interp, output, buffer, -1);
18340 snprintf(buffer, sizeof(buffer), "average search distance for entry: %.1f", nonzero_count ? (double)sum / nonzero_count : 0.0);
18341 Jim_AppendString(interp, output, buffer, -1);
18342 Jim_SetResult(interp, output);
18343 return JIM_OK;
18346 static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
18348 Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
18350 Jim_AppendString(interp, prefixObj, " ", 1);
18351 Jim_AppendString(interp, prefixObj, subcmd, -1);
18353 return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
18356 static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj)
18358 int i;
18359 Jim_Obj *objPtr;
18360 Jim_Obj *dictObj;
18361 Jim_Obj **dictValues;
18362 int len;
18363 int ret = JIM_OK;
18366 dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
18367 if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
18368 return JIM_ERR;
18371 if (Jim_DictPairs(interp, objPtr, &dictValues, &len) == JIM_ERR) {
18372 return JIM_ERR;
18374 for (i = 0; i < len; i += 2) {
18375 if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
18376 Jim_Free(dictValues);
18377 return JIM_ERR;
18382 if (Jim_Length(scriptObj)) {
18383 ret = Jim_EvalObj(interp, scriptObj);
18386 if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {
18388 Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
18389 for (i = 0; i < keyc; i++) {
18390 newkeyv[i] = keyv[i];
18393 for (i = 0; i < len; i += 2) {
18395 objPtr = Jim_GetVariable(interp, dictValues[i], 0);
18396 newkeyv[keyc] = dictValues[i];
18397 Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, 0);
18399 Jim_Free(newkeyv);
18403 Jim_Free(dictValues);
18405 return ret;
18409 static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18411 Jim_Obj *objPtr;
18412 int types = JIM_DICTMATCH_KEYS;
18413 int option;
18414 static const char * const options[] = {
18415 "create", "get", "set", "unset", "exists", "keys", "size", "info",
18416 "merge", "with", "append", "lappend", "incr", "remove", "values", "for",
18417 "replace", "update", NULL
18419 enum
18421 OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXISTS, OPT_KEYS, OPT_SIZE, OPT_INFO,
18422 OPT_MERGE, OPT_WITH, OPT_APPEND, OPT_LAPPEND, OPT_INCR, OPT_REMOVE, OPT_VALUES, OPT_FOR,
18423 OPT_REPLACE, OPT_UPDATE,
18426 if (argc < 2) {
18427 Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arguments ...?");
18428 return JIM_ERR;
18431 if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) {
18432 return Jim_CheckShowCommands(interp, argv[1], options);
18435 switch (option) {
18436 case OPT_GET:
18437 if (argc < 3) {
18438 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?key ...?");
18439 return JIM_ERR;
18441 if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
18442 JIM_ERRMSG) != JIM_OK) {
18443 return JIM_ERR;
18445 Jim_SetResult(interp, objPtr);
18446 return JIM_OK;
18448 case OPT_SET:
18449 if (argc < 5) {
18450 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value");
18451 return JIM_ERR;
18453 return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
18455 case OPT_EXISTS:
18456 if (argc < 4) {
18457 Jim_WrongNumArgs(interp, 2, argv, "dictionary key ?key ...?");
18458 return JIM_ERR;
18460 else {
18461 int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_ERRMSG);
18462 if (rc < 0) {
18463 return JIM_ERR;
18465 Jim_SetResultBool(interp, rc == JIM_OK);
18466 return JIM_OK;
18469 case OPT_UNSET:
18470 if (argc < 4) {
18471 Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?");
18472 return JIM_ERR;
18474 if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, 0) != JIM_OK) {
18475 return JIM_ERR;
18477 return JIM_OK;
18479 case OPT_VALUES:
18480 types = JIM_DICTMATCH_VALUES;
18482 case OPT_KEYS:
18483 if (argc != 3 && argc != 4) {
18484 Jim_WrongNumArgs(interp, 2, argv, "dictionary ?pattern?");
18485 return JIM_ERR;
18487 return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);
18489 case OPT_SIZE:
18490 if (argc != 3) {
18491 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18492 return JIM_ERR;
18494 else if (Jim_DictSize(interp, argv[2]) < 0) {
18495 return JIM_ERR;
18497 Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
18498 return JIM_OK;
18500 case OPT_MERGE:
18501 if (argc == 2) {
18502 return JIM_OK;
18504 objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
18505 if (objPtr == NULL) {
18506 return JIM_ERR;
18508 Jim_SetResult(interp, objPtr);
18509 return JIM_OK;
18511 case OPT_UPDATE:
18512 if (argc < 6 || argc % 2) {
18514 argc = 2;
18516 break;
18518 case OPT_CREATE:
18519 if (argc % 2) {
18520 Jim_WrongNumArgs(interp, 2, argv, "?key value ...?");
18521 return JIM_ERR;
18523 objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
18524 Jim_SetResult(interp, objPtr);
18525 return JIM_OK;
18527 case OPT_INFO:
18528 if (argc != 3) {
18529 Jim_WrongNumArgs(interp, 2, argv, "dictionary");
18530 return JIM_ERR;
18532 return Jim_DictInfo(interp, argv[2]);
18534 case OPT_WITH:
18535 if (argc < 4) {
18536 Jim_WrongNumArgs(interp, 2, argv, "dictVar ?key ...? script");
18537 return JIM_ERR;
18539 return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
18542 return Jim_EvalEnsemble(interp, "dict", options[option], argc - 2, argv + 2);
18546 static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18548 static const char * const options[] = {
18549 "-nobackslashes", "-nocommands", "-novariables", NULL
18551 enum
18552 { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
18553 int i;
18554 int flags = JIM_SUBST_FLAG;
18555 Jim_Obj *objPtr;
18557 if (argc < 2) {
18558 Jim_WrongNumArgs(interp, 1, argv, "?options? string");
18559 return JIM_ERR;
18561 for (i = 1; i < (argc - 1); i++) {
18562 int option;
18564 if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
18565 JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18566 return JIM_ERR;
18568 switch (option) {
18569 case OPT_NOBACKSLASHES:
18570 flags |= JIM_SUBST_NOESC;
18571 break;
18572 case OPT_NOCOMMANDS:
18573 flags |= JIM_SUBST_NOCMD;
18574 break;
18575 case OPT_NOVARIABLES:
18576 flags |= JIM_SUBST_NOVAR;
18577 break;
18580 if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
18581 return JIM_ERR;
18583 Jim_SetResult(interp, objPtr);
18584 return JIM_OK;
18588 static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18590 int cmd;
18591 Jim_Obj *objPtr;
18592 int mode = 0;
18594 static const char * const commands[] = {
18595 "body", "statics", "commands", "procs", "channels", "exists", "globals", "level", "frame", "locals",
18596 "vars", "version", "patchlevel", "complete", "args", "hostname",
18597 "script", "source", "stacktrace", "nameofexecutable", "returncodes",
18598 "references", "alias", NULL
18600 enum
18601 { INFO_BODY, INFO_STATICS, INFO_COMMANDS, INFO_PROCS, INFO_CHANNELS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
18602 INFO_FRAME, INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_PATCHLEVEL, INFO_COMPLETE, INFO_ARGS,
18603 INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE,
18604 INFO_RETURNCODES, INFO_REFERENCES, INFO_ALIAS,
18607 #ifdef jim_ext_namespace
18608 int nons = 0;
18610 if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
18612 argc--;
18613 argv++;
18614 nons = 1;
18616 #endif
18618 if (argc < 2) {
18619 Jim_WrongNumArgs(interp, 1, argv, "subcommand ?args ...?");
18620 return JIM_ERR;
18622 if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18623 return Jim_CheckShowCommands(interp, argv[1], commands);
18627 switch (cmd) {
18628 case INFO_EXISTS:
18629 if (argc != 3) {
18630 Jim_WrongNumArgs(interp, 2, argv, "varName");
18631 return JIM_ERR;
18633 Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
18634 break;
18636 case INFO_ALIAS:{
18637 Jim_Cmd *cmdPtr;
18639 if (argc != 3) {
18640 Jim_WrongNumArgs(interp, 2, argv, "command");
18641 return JIM_ERR;
18643 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18644 return JIM_ERR;
18646 if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
18647 Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
18648 return JIM_ERR;
18650 Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
18651 return JIM_OK;
18654 case INFO_CHANNELS:
18655 mode++;
18656 #ifndef jim_ext_aio
18657 Jim_SetResultString(interp, "aio not enabled", -1);
18658 return JIM_ERR;
18659 #endif
18661 case INFO_PROCS:
18662 mode++;
18664 case INFO_COMMANDS:
18666 if (argc != 2 && argc != 3) {
18667 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18668 return JIM_ERR;
18670 #ifdef jim_ext_namespace
18671 if (!nons) {
18672 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18673 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18676 #endif
18677 Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
18678 break;
18680 case INFO_VARS:
18681 mode++;
18683 case INFO_LOCALS:
18684 mode++;
18686 case INFO_GLOBALS:
18688 if (argc != 2 && argc != 3) {
18689 Jim_WrongNumArgs(interp, 2, argv, "?pattern?");
18690 return JIM_ERR;
18692 #ifdef jim_ext_namespace
18693 if (!nons) {
18694 if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimGlobMatch("::*", Jim_String(argv[2]), 0))) {
18695 return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
18698 #endif
18699 Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
18700 break;
18702 case INFO_SCRIPT:
18703 if (argc != 2) {
18704 Jim_WrongNumArgs(interp, 2, argv, "");
18705 return JIM_ERR;
18707 Jim_SetResult(interp, JimGetScript(interp, interp->currentScriptObj)->fileNameObj);
18708 break;
18710 case INFO_SOURCE:{
18711 jim_wide line;
18712 Jim_Obj *resObjPtr;
18713 Jim_Obj *fileNameObj;
18715 if (argc != 3 && argc != 5) {
18716 Jim_WrongNumArgs(interp, 2, argv, "source ?filename line?");
18717 return JIM_ERR;
18719 if (argc == 5) {
18720 if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
18721 return JIM_ERR;
18723 resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
18724 JimSetSourceInfo(interp, resObjPtr, argv[3], line);
18726 else {
18727 if (argv[2]->typePtr == &sourceObjType) {
18728 fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj;
18729 line = argv[2]->internalRep.sourceValue.lineNumber;
18731 else if (argv[2]->typePtr == &scriptObjType) {
18732 ScriptObj *script = JimGetScript(interp, argv[2]);
18733 fileNameObj = script->fileNameObj;
18734 line = script->firstline;
18736 else {
18737 fileNameObj = interp->emptyObj;
18738 line = 1;
18740 resObjPtr = Jim_NewListObj(interp, NULL, 0);
18741 Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
18742 Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
18744 Jim_SetResult(interp, resObjPtr);
18745 break;
18748 case INFO_STACKTRACE:
18749 Jim_SetResult(interp, interp->stackTrace);
18750 break;
18752 case INFO_LEVEL:
18753 case INFO_FRAME:
18754 switch (argc) {
18755 case 2:
18756 Jim_SetResultInt(interp, interp->framePtr->level);
18757 break;
18759 case 3:
18760 if (JimInfoLevel(interp, argv[2], &objPtr, cmd == INFO_LEVEL) != JIM_OK) {
18761 return JIM_ERR;
18763 Jim_SetResult(interp, objPtr);
18764 break;
18766 default:
18767 Jim_WrongNumArgs(interp, 2, argv, "?levelNum?");
18768 return JIM_ERR;
18770 break;
18772 case INFO_BODY:
18773 case INFO_STATICS:
18774 case INFO_ARGS:{
18775 Jim_Cmd *cmdPtr;
18777 if (argc != 3) {
18778 Jim_WrongNumArgs(interp, 2, argv, "procname");
18779 return JIM_ERR;
18781 if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
18782 return JIM_ERR;
18784 if (!cmdPtr->isproc) {
18785 Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
18786 return JIM_ERR;
18788 switch (cmd) {
18789 case INFO_BODY:
18790 Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
18791 break;
18792 case INFO_ARGS:
18793 Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
18794 break;
18795 case INFO_STATICS:
18796 if (cmdPtr->u.proc.staticVars) {
18797 Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
18798 NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES));
18800 break;
18802 break;
18805 case INFO_VERSION:
18806 case INFO_PATCHLEVEL:{
18807 char buf[(JIM_INTEGER_SPACE * 2) + 1];
18809 sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
18810 Jim_SetResultString(interp, buf, -1);
18811 break;
18814 case INFO_COMPLETE:
18815 if (argc != 3 && argc != 4) {
18816 Jim_WrongNumArgs(interp, 2, argv, "script ?missing?");
18817 return JIM_ERR;
18819 else {
18820 char missing;
18822 Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing));
18823 if (missing != ' ' && argc == 4) {
18824 Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
18827 break;
18829 case INFO_HOSTNAME:
18831 return Jim_Eval(interp, "os.gethostname");
18833 case INFO_NAMEOFEXECUTABLE:
18835 return Jim_Eval(interp, "{info nameofexecutable}");
18837 case INFO_RETURNCODES:
18838 if (argc == 2) {
18839 int i;
18840 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
18842 for (i = 0; jimReturnCodes[i]; i++) {
18843 Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
18844 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
18845 jimReturnCodes[i], -1));
18848 Jim_SetResult(interp, listObjPtr);
18850 else if (argc == 3) {
18851 long code;
18852 const char *name;
18854 if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
18855 return JIM_ERR;
18857 name = Jim_ReturnCode(code);
18858 if (*name == '?') {
18859 Jim_SetResultInt(interp, code);
18861 else {
18862 Jim_SetResultString(interp, name, -1);
18865 else {
18866 Jim_WrongNumArgs(interp, 2, argv, "?code?");
18867 return JIM_ERR;
18869 break;
18870 case INFO_REFERENCES:
18871 #ifdef JIM_REFERENCES
18872 return JimInfoReferences(interp, argc, argv);
18873 #else
18874 Jim_SetResultString(interp, "not supported", -1);
18875 return JIM_ERR;
18876 #endif
18878 return JIM_OK;
18882 static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18884 Jim_Obj *objPtr;
18885 int result = 0;
18887 static const char * const options[] = {
18888 "-command", "-proc", "-alias", "-var", NULL
18890 enum
18892 OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
18894 int option;
18896 if (argc == 2) {
18897 option = OPT_VAR;
18898 objPtr = argv[1];
18900 else if (argc == 3) {
18901 if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
18902 return JIM_ERR;
18904 objPtr = argv[2];
18906 else {
18907 Jim_WrongNumArgs(interp, 1, argv, "?option? name");
18908 return JIM_ERR;
18911 if (option == OPT_VAR) {
18912 result = Jim_GetVariable(interp, objPtr, 0) != NULL;
18914 else {
18916 Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
18918 if (cmd) {
18919 switch (option) {
18920 case OPT_COMMAND:
18921 result = 1;
18922 break;
18924 case OPT_ALIAS:
18925 result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
18926 break;
18928 case OPT_PROC:
18929 result = cmd->isproc;
18930 break;
18934 Jim_SetResultBool(interp, result);
18935 return JIM_OK;
18939 static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18941 const char *str, *splitChars, *noMatchStart;
18942 int splitLen, strLen;
18943 Jim_Obj *resObjPtr;
18944 int c;
18945 int len;
18947 if (argc != 2 && argc != 3) {
18948 Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
18949 return JIM_ERR;
18952 str = Jim_GetString(argv[1], &len);
18953 if (len == 0) {
18954 return JIM_OK;
18956 strLen = Jim_Utf8Length(interp, argv[1]);
18959 if (argc == 2) {
18960 splitChars = " \n\t\r";
18961 splitLen = 4;
18963 else {
18964 splitChars = Jim_String(argv[2]);
18965 splitLen = Jim_Utf8Length(interp, argv[2]);
18968 noMatchStart = str;
18969 resObjPtr = Jim_NewListObj(interp, NULL, 0);
18972 if (splitLen) {
18973 Jim_Obj *objPtr;
18974 while (strLen--) {
18975 const char *sc = splitChars;
18976 int scLen = splitLen;
18977 int sl = utf8_tounicode(str, &c);
18978 while (scLen--) {
18979 int pc;
18980 sc += utf8_tounicode(sc, &pc);
18981 if (c == pc) {
18982 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
18983 Jim_ListAppendElement(interp, resObjPtr, objPtr);
18984 noMatchStart = str + sl;
18985 break;
18988 str += sl;
18990 objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
18991 Jim_ListAppendElement(interp, resObjPtr, objPtr);
18993 else {
18994 Jim_Obj **commonObj = NULL;
18995 #define NUM_COMMON (128 - 9)
18996 while (strLen--) {
18997 int n = utf8_tounicode(str, &c);
18998 #ifdef JIM_OPTIMIZATION
18999 if (c >= 9 && c < 128) {
19001 c -= 9;
19002 if (!commonObj) {
19003 commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
19004 memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
19006 if (!commonObj[c]) {
19007 commonObj[c] = Jim_NewStringObj(interp, str, 1);
19009 Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
19010 str++;
19011 continue;
19013 #endif
19014 Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
19015 str += n;
19017 Jim_Free(commonObj);
19020 Jim_SetResult(interp, resObjPtr);
19021 return JIM_OK;
19025 static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19027 const char *joinStr;
19028 int joinStrLen;
19030 if (argc != 2 && argc != 3) {
19031 Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
19032 return JIM_ERR;
19035 if (argc == 2) {
19036 joinStr = " ";
19037 joinStrLen = 1;
19039 else {
19040 joinStr = Jim_GetString(argv[2], &joinStrLen);
19042 Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
19043 return JIM_OK;
19047 static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19049 Jim_Obj *objPtr;
19051 if (argc < 2) {
19052 Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
19053 return JIM_ERR;
19055 objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
19056 if (objPtr == NULL)
19057 return JIM_ERR;
19058 Jim_SetResult(interp, objPtr);
19059 return JIM_OK;
19063 static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19065 Jim_Obj *listPtr, **outVec;
19066 int outc, i;
19068 if (argc < 3) {
19069 Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
19070 return JIM_ERR;
19072 if (argv[2]->typePtr != &scanFmtStringObjType)
19073 SetScanFmtFromAny(interp, argv[2]);
19074 if (FormatGetError(argv[2]) != 0) {
19075 Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
19076 return JIM_ERR;
19078 if (argc > 3) {
19079 int maxPos = FormatGetMaxPos(argv[2]);
19080 int count = FormatGetCnvCount(argv[2]);
19082 if (maxPos > argc - 3) {
19083 Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
19084 return JIM_ERR;
19086 else if (count > argc - 3) {
19087 Jim_SetResultString(interp, "different numbers of variable names and "
19088 "field specifiers", -1);
19089 return JIM_ERR;
19091 else if (count < argc - 3) {
19092 Jim_SetResultString(interp, "variable is not assigned by any "
19093 "conversion specifiers", -1);
19094 return JIM_ERR;
19097 listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
19098 if (listPtr == 0)
19099 return JIM_ERR;
19100 if (argc > 3) {
19101 int rc = JIM_OK;
19102 int count = 0;
19104 if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
19105 int len = Jim_ListLength(interp, listPtr);
19107 if (len != 0) {
19108 JimListGetElements(interp, listPtr, &outc, &outVec);
19109 for (i = 0; i < outc; ++i) {
19110 if (Jim_Length(outVec[i]) > 0) {
19111 ++count;
19112 if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
19113 rc = JIM_ERR;
19118 Jim_FreeNewObj(interp, listPtr);
19120 else {
19121 count = -1;
19123 if (rc == JIM_OK) {
19124 Jim_SetResultInt(interp, count);
19126 return rc;
19128 else {
19129 if (listPtr == (Jim_Obj *)EOF) {
19130 Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
19131 return JIM_OK;
19133 Jim_SetResult(interp, listPtr);
19135 return JIM_OK;
19139 static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19141 if (argc != 2 && argc != 3) {
19142 Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?");
19143 return JIM_ERR;
19145 Jim_SetResult(interp, argv[1]);
19146 if (argc == 3) {
19147 JimSetStackTrace(interp, argv[2]);
19148 return JIM_ERR;
19150 interp->addStackTrace++;
19151 return JIM_ERR;
19155 static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19157 Jim_Obj *objPtr;
19159 if (argc != 4) {
19160 Jim_WrongNumArgs(interp, 1, argv, "list first last");
19161 return JIM_ERR;
19163 if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
19164 return JIM_ERR;
19165 Jim_SetResult(interp, objPtr);
19166 return JIM_OK;
19170 static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19172 Jim_Obj *objPtr;
19173 long count;
19175 if (argc < 2 || Jim_GetLong(interp, argv[1], &count) != JIM_OK || count < 0) {
19176 Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
19177 return JIM_ERR;
19180 if (count == 0 || argc == 2) {
19181 return JIM_OK;
19184 argc -= 2;
19185 argv += 2;
19187 objPtr = Jim_NewListObj(interp, argv, argc);
19188 while (--count) {
19189 ListInsertElements(objPtr, -1, argc, argv);
19192 Jim_SetResult(interp, objPtr);
19193 return JIM_OK;
19196 char **Jim_GetEnviron(void)
19198 #if defined(HAVE__NSGETENVIRON)
19199 return *_NSGetEnviron();
19200 #else
19201 #if !defined(NO_ENVIRON_EXTERN)
19202 extern char **environ;
19203 #endif
19205 return environ;
19206 #endif
19209 void Jim_SetEnviron(char **env)
19211 #if defined(HAVE__NSGETENVIRON)
19212 *_NSGetEnviron() = env;
19213 #else
19214 #if !defined(NO_ENVIRON_EXTERN)
19215 extern char **environ;
19216 #endif
19218 environ = env;
19219 #endif
19223 static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19225 const char *key;
19226 const char *val;
19228 if (argc == 1) {
19229 char **e = Jim_GetEnviron();
19231 int i;
19232 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
19234 for (i = 0; e[i]; i++) {
19235 const char *equals = strchr(e[i], '=');
19237 if (equals) {
19238 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
19239 equals - e[i]));
19240 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
19244 Jim_SetResult(interp, listObjPtr);
19245 return JIM_OK;
19248 if (argc < 2) {
19249 Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
19250 return JIM_ERR;
19252 key = Jim_String(argv[1]);
19253 val = getenv(key);
19254 if (val == NULL) {
19255 if (argc < 3) {
19256 Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
19257 return JIM_ERR;
19259 val = Jim_String(argv[2]);
19261 Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
19262 return JIM_OK;
19266 static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19268 int retval;
19270 if (argc != 2) {
19271 Jim_WrongNumArgs(interp, 1, argv, "fileName");
19272 return JIM_ERR;
19274 retval = Jim_EvalFile(interp, Jim_String(argv[1]));
19275 if (retval == JIM_RETURN)
19276 return JIM_OK;
19277 return retval;
19281 static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19283 Jim_Obj *revObjPtr, **ele;
19284 int len;
19286 if (argc != 2) {
19287 Jim_WrongNumArgs(interp, 1, argv, "list");
19288 return JIM_ERR;
19290 JimListGetElements(interp, argv[1], &len, &ele);
19291 len--;
19292 revObjPtr = Jim_NewListObj(interp, NULL, 0);
19293 while (len >= 0)
19294 ListAppendElement(revObjPtr, ele[len--]);
19295 Jim_SetResult(interp, revObjPtr);
19296 return JIM_OK;
19299 static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
19301 jim_wide len;
19303 if (step == 0)
19304 return -1;
19305 if (start == end)
19306 return 0;
19307 else if (step > 0 && start > end)
19308 return -1;
19309 else if (step < 0 && end > start)
19310 return -1;
19311 len = end - start;
19312 if (len < 0)
19313 len = -len;
19314 if (step < 0)
19315 step = -step;
19316 len = 1 + ((len - 1) / step);
19317 if (len > INT_MAX)
19318 len = INT_MAX;
19319 return (int)((len < 0) ? -1 : len);
19323 static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19325 jim_wide start = 0, end, step = 1;
19326 int len, i;
19327 Jim_Obj *objPtr;
19329 if (argc < 2 || argc > 4) {
19330 Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
19331 return JIM_ERR;
19333 if (argc == 2) {
19334 if (Jim_GetWide(interp, argv[1], &end) != JIM_OK)
19335 return JIM_ERR;
19337 else {
19338 if (Jim_GetWide(interp, argv[1], &start) != JIM_OK ||
19339 Jim_GetWide(interp, argv[2], &end) != JIM_OK)
19340 return JIM_ERR;
19341 if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK)
19342 return JIM_ERR;
19344 if ((len = JimRangeLen(start, end, step)) == -1) {
19345 Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
19346 return JIM_ERR;
19348 objPtr = Jim_NewListObj(interp, NULL, 0);
19349 for (i = 0; i < len; i++)
19350 ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
19351 Jim_SetResult(interp, objPtr);
19352 return JIM_OK;
19356 static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19358 jim_wide min = 0, max = 0, len, maxMul;
19360 if (argc < 1 || argc > 3) {
19361 Jim_WrongNumArgs(interp, 1, argv, "?min? max");
19362 return JIM_ERR;
19364 if (argc == 1) {
19365 max = JIM_WIDE_MAX;
19366 } else if (argc == 2) {
19367 if (Jim_GetWide(interp, argv[1], &max) != JIM_OK)
19368 return JIM_ERR;
19369 } else if (argc == 3) {
19370 if (Jim_GetWide(interp, argv[1], &min) != JIM_OK ||
19371 Jim_GetWide(interp, argv[2], &max) != JIM_OK)
19372 return JIM_ERR;
19374 len = max-min;
19375 if (len < 0) {
19376 Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
19377 return JIM_ERR;
19379 maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
19380 while (1) {
19381 jim_wide r;
19383 JimRandomBytes(interp, &r, sizeof(jim_wide));
19384 if (r < 0 || r >= maxMul) continue;
19385 r = (len == 0) ? 0 : r%len;
19386 Jim_SetResultInt(interp, min+r);
19387 return JIM_OK;
19391 static const struct {
19392 const char *name;
19393 Jim_CmdProc *cmdProc;
19394 } Jim_CoreCommandsTable[] = {
19395 {"alias", Jim_AliasCoreCommand},
19396 {"set", Jim_SetCoreCommand},
19397 {"unset", Jim_UnsetCoreCommand},
19398 {"puts", Jim_PutsCoreCommand},
19399 {"+", Jim_AddCoreCommand},
19400 {"*", Jim_MulCoreCommand},
19401 {"-", Jim_SubCoreCommand},
19402 {"/", Jim_DivCoreCommand},
19403 {"incr", Jim_IncrCoreCommand},
19404 {"while", Jim_WhileCoreCommand},
19405 {"loop", Jim_LoopCoreCommand},
19406 {"for", Jim_ForCoreCommand},
19407 {"foreach", Jim_ForeachCoreCommand},
19408 {"lmap", Jim_LmapCoreCommand},
19409 {"lassign", Jim_LassignCoreCommand},
19410 {"if", Jim_IfCoreCommand},
19411 {"switch", Jim_SwitchCoreCommand},
19412 {"list", Jim_ListCoreCommand},
19413 {"lindex", Jim_LindexCoreCommand},
19414 {"lset", Jim_LsetCoreCommand},
19415 {"lsearch", Jim_LsearchCoreCommand},
19416 {"llength", Jim_LlengthCoreCommand},
19417 {"lappend", Jim_LappendCoreCommand},
19418 {"linsert", Jim_LinsertCoreCommand},
19419 {"lreplace", Jim_LreplaceCoreCommand},
19420 {"lsort", Jim_LsortCoreCommand},
19421 {"append", Jim_AppendCoreCommand},
19422 {"debug", Jim_DebugCoreCommand},
19423 {"eval", Jim_EvalCoreCommand},
19424 {"uplevel", Jim_UplevelCoreCommand},
19425 {"expr", Jim_ExprCoreCommand},
19426 {"break", Jim_BreakCoreCommand},
19427 {"continue", Jim_ContinueCoreCommand},
19428 {"proc", Jim_ProcCoreCommand},
19429 {"concat", Jim_ConcatCoreCommand},
19430 {"return", Jim_ReturnCoreCommand},
19431 {"upvar", Jim_UpvarCoreCommand},
19432 {"global", Jim_GlobalCoreCommand},
19433 {"string", Jim_StringCoreCommand},
19434 {"time", Jim_TimeCoreCommand},
19435 {"exit", Jim_ExitCoreCommand},
19436 {"catch", Jim_CatchCoreCommand},
19437 #ifdef JIM_REFERENCES
19438 {"ref", Jim_RefCoreCommand},
19439 {"getref", Jim_GetrefCoreCommand},
19440 {"setref", Jim_SetrefCoreCommand},
19441 {"finalize", Jim_FinalizeCoreCommand},
19442 {"collect", Jim_CollectCoreCommand},
19443 #endif
19444 {"rename", Jim_RenameCoreCommand},
19445 {"dict", Jim_DictCoreCommand},
19446 {"subst", Jim_SubstCoreCommand},
19447 {"info", Jim_InfoCoreCommand},
19448 {"exists", Jim_ExistsCoreCommand},
19449 {"split", Jim_SplitCoreCommand},
19450 {"join", Jim_JoinCoreCommand},
19451 {"format", Jim_FormatCoreCommand},
19452 {"scan", Jim_ScanCoreCommand},
19453 {"error", Jim_ErrorCoreCommand},
19454 {"lrange", Jim_LrangeCoreCommand},
19455 {"lrepeat", Jim_LrepeatCoreCommand},
19456 {"env", Jim_EnvCoreCommand},
19457 {"source", Jim_SourceCoreCommand},
19458 {"lreverse", Jim_LreverseCoreCommand},
19459 {"range", Jim_RangeCoreCommand},
19460 {"rand", Jim_RandCoreCommand},
19461 {"tailcall", Jim_TailcallCoreCommand},
19462 {"local", Jim_LocalCoreCommand},
19463 {"upcall", Jim_UpcallCoreCommand},
19464 {"apply", Jim_ApplyCoreCommand},
19465 {NULL, NULL},
19468 void Jim_RegisterCoreCommands(Jim_Interp *interp)
19470 int i = 0;
19472 while (Jim_CoreCommandsTable[i].name != NULL) {
19473 Jim_CreateCommand(interp,
19474 Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
19475 i++;
19479 void Jim_MakeErrorMessage(Jim_Interp *interp)
19481 Jim_Obj *argv[2];
19483 argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
19484 argv[1] = interp->result;
19486 Jim_EvalObjVector(interp, 2, argv);
19489 static char **JimSortStringTable(const char *const *tablePtr)
19491 int count;
19492 char **tablePtrSorted;
19495 for (count = 0; tablePtr[count]; count++) {
19499 tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1));
19500 memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
19501 qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
19502 tablePtrSorted[count] = NULL;
19504 return tablePtrSorted;
19507 static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
19508 const char *prefix, const char *const *tablePtr, const char *name)
19510 char **tablePtrSorted;
19511 int i;
19513 if (name == NULL) {
19514 name = "option";
19517 Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
19518 tablePtrSorted = JimSortStringTable(tablePtr);
19519 for (i = 0; tablePtrSorted[i]; i++) {
19520 if (tablePtrSorted[i + 1] == NULL && i > 0) {
19521 Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
19523 Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
19524 if (tablePtrSorted[i + 1]) {
19525 Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
19528 Jim_Free(tablePtrSorted);
19532 int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr)
19534 if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) {
19535 int i;
19536 char **tablePtrSorted = JimSortStringTable(tablePtr);
19537 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
19538 for (i = 0; tablePtrSorted[i]; i++) {
19539 Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1));
19541 Jim_Free(tablePtrSorted);
19542 return JIM_OK;
19544 return JIM_ERR;
19547 static const Jim_ObjType getEnumObjType = {
19548 "get-enum",
19549 NULL,
19550 NULL,
19551 NULL,
19552 JIM_TYPE_REFERENCES
19555 int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
19556 const char *const *tablePtr, int *indexPtr, const char *name, int flags)
19558 const char *bad = "bad ";
19559 const char *const *entryPtr = NULL;
19560 int i;
19561 int match = -1;
19562 int arglen;
19563 const char *arg;
19565 if (objPtr->typePtr == &getEnumObjType) {
19566 if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) {
19567 *indexPtr = objPtr->internalRep.ptrIntValue.int2;
19568 return JIM_OK;
19572 arg = Jim_GetString(objPtr, &arglen);
19574 *indexPtr = -1;
19576 for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
19577 if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
19579 match = i;
19580 goto found;
19582 if (flags & JIM_ENUM_ABBREV) {
19583 if (strncmp(arg, *entryPtr, arglen) == 0) {
19584 if (*arg == '-' && arglen == 1) {
19585 break;
19587 if (match >= 0) {
19588 bad = "ambiguous ";
19589 goto ambiguous;
19591 match = i;
19597 if (match >= 0) {
19598 found:
19600 Jim_FreeIntRep(interp, objPtr);
19601 objPtr->typePtr = &getEnumObjType;
19602 objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr;
19603 objPtr->internalRep.ptrIntValue.int1 = flags;
19604 objPtr->internalRep.ptrIntValue.int2 = match;
19606 *indexPtr = match;
19607 return JIM_OK;
19610 ambiguous:
19611 if (flags & JIM_ERRMSG) {
19612 JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
19614 return JIM_ERR;
19617 int Jim_FindByName(const char *name, const char * const array[], size_t len)
19619 int i;
19621 for (i = 0; i < (int)len; i++) {
19622 if (array[i] && strcmp(array[i], name) == 0) {
19623 return i;
19626 return -1;
19629 int Jim_IsDict(Jim_Obj *objPtr)
19631 return objPtr->typePtr == &dictObjType;
19634 int Jim_IsList(Jim_Obj *objPtr)
19636 return objPtr->typePtr == &listObjType;
19639 void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
19642 int len = strlen(format);
19643 int extra = 0;
19644 int n = 0;
19645 const char *params[5];
19646 int nobjparam = 0;
19647 Jim_Obj *objparam[5];
19648 char *buf;
19649 va_list args;
19650 int i;
19652 va_start(args, format);
19654 for (i = 0; i < len && n < 5; i++) {
19655 int l;
19657 if (strncmp(format + i, "%s", 2) == 0) {
19658 params[n] = va_arg(args, char *);
19660 l = strlen(params[n]);
19662 else if (strncmp(format + i, "%#s", 3) == 0) {
19663 Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
19665 params[n] = Jim_GetString(objPtr, &l);
19666 objparam[nobjparam++] = objPtr;
19667 Jim_IncrRefCount(objPtr);
19669 else {
19670 if (format[i] == '%') {
19671 i++;
19673 continue;
19675 n++;
19676 extra += l;
19679 len += extra;
19680 buf = Jim_Alloc(len + 1);
19681 len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
19683 va_end(args);
19685 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
19687 for (i = 0; i < nobjparam; i++) {
19688 Jim_DecrRefCount(interp, objparam[i]);
19693 #ifndef jim_ext_package
19694 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
19696 return JIM_OK;
19698 #endif
19699 #ifndef jim_ext_aio
19700 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
19702 Jim_SetResultString(interp, "aio not enabled", -1);
19703 return NULL;
19705 #endif
19708 #include <stdio.h>
19709 #include <string.h>
19712 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19715 return JIM_OK;
19718 static const jim_subcmd_type dummy_subcmd = {
19719 "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
19722 static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
19724 const char *s = "";
19726 for (; ct->cmd; ct++) {
19727 if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
19728 Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, NULL);
19729 s = sep;
19734 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
19735 Jim_Obj *cmd, Jim_Obj *subcmd)
19737 Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be ", cmd, type, subcmd);
19738 add_commands(interp, command_table, ", ");
19741 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
19742 Jim_Obj *const *argv)
19744 Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: ", argv[0]);
19745 add_commands(interp, command_table, ", ");
19748 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
19750 if (cmd) {
19751 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
19753 Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
19754 if (ct->args && *ct->args) {
19755 Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
19759 static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd)
19761 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19762 add_cmd_usage(interp, command_table, subcmd);
19763 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19766 static const Jim_ObjType subcmdLookupObjType = {
19767 "subcmd-lookup",
19768 NULL,
19769 NULL,
19770 NULL,
19771 JIM_TYPE_REFERENCES
19774 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
19775 int argc, Jim_Obj *const *argv)
19777 const jim_subcmd_type *ct;
19778 const jim_subcmd_type *partial = 0;
19779 int cmdlen;
19780 Jim_Obj *cmd;
19781 const char *cmdstr;
19782 int help = 0;
19784 if (argc < 2) {
19785 Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n"
19786 "Use \"%#s -help ?command?\" for help", argv[0], argv[0]);
19787 return 0;
19790 cmd = argv[1];
19793 if (cmd->typePtr == &subcmdLookupObjType) {
19794 if (cmd->internalRep.ptrIntValue.ptr == command_table) {
19795 ct = command_table + cmd->internalRep.ptrIntValue.int1;
19796 goto found;
19801 if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
19802 if (argc == 2) {
19804 show_cmd_usage(interp, command_table, argc, argv);
19805 return &dummy_subcmd;
19807 help = 1;
19810 cmd = argv[2];
19814 if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
19816 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
19817 add_commands(interp, command_table, " ");
19818 return &dummy_subcmd;
19821 cmdstr = Jim_GetString(cmd, &cmdlen);
19823 for (ct = command_table; ct->cmd; ct++) {
19824 if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
19826 break;
19828 if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
19829 if (partial) {
19831 if (help) {
19833 show_cmd_usage(interp, command_table, argc, argv);
19834 return &dummy_subcmd;
19836 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
19837 return 0;
19839 partial = ct;
19841 continue;
19845 if (partial && !ct->cmd) {
19846 ct = partial;
19849 if (!ct->cmd) {
19851 if (help) {
19853 show_cmd_usage(interp, command_table, argc, argv);
19854 return &dummy_subcmd;
19856 bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
19857 return 0;
19860 if (help) {
19861 Jim_SetResultString(interp, "Usage: ", -1);
19863 add_cmd_usage(interp, ct, argv[0]);
19864 return &dummy_subcmd;
19868 Jim_FreeIntRep(interp, cmd);
19869 cmd->typePtr = &subcmdLookupObjType;
19870 cmd->internalRep.ptrIntValue.ptr = (void *)command_table;
19871 cmd->internalRep.ptrIntValue.int1 = ct - command_table;
19873 found:
19875 if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) {
19876 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
19878 add_cmd_usage(interp, ct, argv[0]);
19879 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
19881 return 0;
19885 return ct;
19888 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
19890 int ret = JIM_ERR;
19892 if (ct) {
19893 if (ct->flags & JIM_MODFLAG_FULLARGV) {
19894 ret = ct->function(interp, argc, argv);
19896 else {
19897 ret = ct->function(interp, argc - 2, argv + 2);
19899 if (ret < 0) {
19900 set_wrong_args(interp, ct, argv[0]);
19901 ret = JIM_ERR;
19904 return ret;
19907 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
19909 const jim_subcmd_type *ct =
19910 Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
19912 return Jim_CallSubCmd(interp, ct, argc, argv);
19915 #include <ctype.h>
19916 #include <stdlib.h>
19917 #include <string.h>
19918 #include <stdio.h>
19919 #include <assert.h>
19922 int utf8_fromunicode(char *p, unsigned uc)
19924 if (uc <= 0x7f) {
19925 *p = uc;
19926 return 1;
19928 else if (uc <= 0x7ff) {
19929 *p++ = 0xc0 | ((uc & 0x7c0) >> 6);
19930 *p = 0x80 | (uc & 0x3f);
19931 return 2;
19933 else if (uc <= 0xffff) {
19934 *p++ = 0xe0 | ((uc & 0xf000) >> 12);
19935 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
19936 *p = 0x80 | (uc & 0x3f);
19937 return 3;
19940 else {
19941 *p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
19942 *p++ = 0x80 | ((uc & 0x3f000) >> 12);
19943 *p++ = 0x80 | ((uc & 0xfc0) >> 6);
19944 *p = 0x80 | (uc & 0x3f);
19945 return 4;
19949 #include <ctype.h>
19950 #include <string.h>
19953 #define JIM_INTEGER_SPACE 24
19954 #define MAX_FLOAT_WIDTH 320
19956 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
19958 const char *span, *format, *formatEnd, *msg;
19959 int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
19960 static const char * const mixedXPG =
19961 "cannot mix \"%\" and \"%n$\" conversion specifiers";
19962 static const char * const badIndex[2] = {
19963 "not enough arguments for all format specifiers",
19964 "\"%n$\" argument index out of range"
19966 int formatLen;
19967 Jim_Obj *resultPtr;
19969 char *num_buffer = NULL;
19970 int num_buffer_size = 0;
19972 span = format = Jim_GetString(fmtObjPtr, &formatLen);
19973 formatEnd = format + formatLen;
19974 resultPtr = Jim_NewEmptyStringObj(interp);
19976 while (format != formatEnd) {
19977 char *end;
19978 int gotMinus, sawFlag;
19979 int gotPrecision, useShort;
19980 long width, precision;
19981 int newXpg;
19982 int ch;
19983 int step;
19984 int doubleType;
19985 char pad = ' ';
19986 char spec[2*JIM_INTEGER_SPACE + 12];
19987 char *p;
19989 int formatted_chars;
19990 int formatted_bytes;
19991 const char *formatted_buf;
19993 step = utf8_tounicode(format, &ch);
19994 format += step;
19995 if (ch != '%') {
19996 numBytes += step;
19997 continue;
19999 if (numBytes) {
20000 Jim_AppendString(interp, resultPtr, span, numBytes);
20001 numBytes = 0;
20005 step = utf8_tounicode(format, &ch);
20006 if (ch == '%') {
20007 span = format;
20008 numBytes = step;
20009 format += step;
20010 continue;
20014 newXpg = 0;
20015 if (isdigit(ch)) {
20016 int position = strtoul(format, &end, 10);
20017 if (*end == '$') {
20018 newXpg = 1;
20019 objIndex = position - 1;
20020 format = end + 1;
20021 step = utf8_tounicode(format, &ch);
20024 if (newXpg) {
20025 if (gotSequential) {
20026 msg = mixedXPG;
20027 goto errorMsg;
20029 gotXpg = 1;
20030 } else {
20031 if (gotXpg) {
20032 msg = mixedXPG;
20033 goto errorMsg;
20035 gotSequential = 1;
20037 if ((objIndex < 0) || (objIndex >= objc)) {
20038 msg = badIndex[gotXpg];
20039 goto errorMsg;
20042 p = spec;
20043 *p++ = '%';
20045 gotMinus = 0;
20046 sawFlag = 1;
20047 do {
20048 switch (ch) {
20049 case '-':
20050 gotMinus = 1;
20051 break;
20052 case '0':
20053 pad = ch;
20054 break;
20055 case ' ':
20056 case '+':
20057 case '#':
20058 break;
20059 default:
20060 sawFlag = 0;
20061 continue;
20063 *p++ = ch;
20064 format += step;
20065 step = utf8_tounicode(format, &ch);
20067 } while (sawFlag && (p - spec <= 5));
20070 width = 0;
20071 if (isdigit(ch)) {
20072 width = strtoul(format, &end, 10);
20073 format = end;
20074 step = utf8_tounicode(format, &ch);
20075 } else if (ch == '*') {
20076 if (objIndex >= objc - 1) {
20077 msg = badIndex[gotXpg];
20078 goto errorMsg;
20080 if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) {
20081 goto error;
20083 if (width < 0) {
20084 width = -width;
20085 if (!gotMinus) {
20086 *p++ = '-';
20087 gotMinus = 1;
20090 objIndex++;
20091 format += step;
20092 step = utf8_tounicode(format, &ch);
20096 gotPrecision = precision = 0;
20097 if (ch == '.') {
20098 gotPrecision = 1;
20099 format += step;
20100 step = utf8_tounicode(format, &ch);
20102 if (isdigit(ch)) {
20103 precision = strtoul(format, &end, 10);
20104 format = end;
20105 step = utf8_tounicode(format, &ch);
20106 } else if (ch == '*') {
20107 if (objIndex >= objc - 1) {
20108 msg = badIndex[gotXpg];
20109 goto errorMsg;
20111 if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) {
20112 goto error;
20116 if (precision < 0) {
20117 precision = 0;
20119 objIndex++;
20120 format += step;
20121 step = utf8_tounicode(format, &ch);
20125 useShort = 0;
20126 if (ch == 'h') {
20127 useShort = 1;
20128 format += step;
20129 step = utf8_tounicode(format, &ch);
20130 } else if (ch == 'l') {
20132 format += step;
20133 step = utf8_tounicode(format, &ch);
20134 if (ch == 'l') {
20135 format += step;
20136 step = utf8_tounicode(format, &ch);
20140 format += step;
20141 span = format;
20144 if (ch == 'i') {
20145 ch = 'd';
20148 doubleType = 0;
20150 switch (ch) {
20151 case '\0':
20152 msg = "format string ended in middle of field specifier";
20153 goto errorMsg;
20154 case 's': {
20155 formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes);
20156 formatted_chars = Jim_Utf8Length(interp, objv[objIndex]);
20157 if (gotPrecision && (precision < formatted_chars)) {
20159 formatted_chars = precision;
20160 formatted_bytes = utf8_index(formatted_buf, precision);
20162 break;
20164 case 'c': {
20165 jim_wide code;
20167 if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) {
20168 goto error;
20171 formatted_bytes = utf8_getchars(spec, code);
20172 formatted_buf = spec;
20173 formatted_chars = 1;
20174 break;
20176 case 'b': {
20177 unsigned jim_wide w;
20178 int length;
20179 int i;
20180 int j;
20182 if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
20183 goto error;
20185 length = sizeof(w) * 8;
20189 if (num_buffer_size < length + 1) {
20190 num_buffer_size = length + 1;
20191 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20194 j = 0;
20195 for (i = length; i > 0; ) {
20196 i--;
20197 if (w & ((unsigned jim_wide)1 << i)) {
20198 num_buffer[j++] = '1';
20200 else if (j || i == 0) {
20201 num_buffer[j++] = '0';
20204 num_buffer[j] = 0;
20205 formatted_chars = formatted_bytes = j;
20206 formatted_buf = num_buffer;
20207 break;
20210 case 'e':
20211 case 'E':
20212 case 'f':
20213 case 'g':
20214 case 'G':
20215 doubleType = 1;
20217 case 'd':
20218 case 'u':
20219 case 'o':
20220 case 'x':
20221 case 'X': {
20222 jim_wide w;
20223 double d;
20224 int length;
20227 if (width) {
20228 p += sprintf(p, "%ld", width);
20230 if (gotPrecision) {
20231 p += sprintf(p, ".%ld", precision);
20235 if (doubleType) {
20236 if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) {
20237 goto error;
20239 length = MAX_FLOAT_WIDTH;
20241 else {
20242 if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) {
20243 goto error;
20245 length = JIM_INTEGER_SPACE;
20246 if (useShort) {
20247 if (ch == 'd') {
20248 w = (short)w;
20250 else {
20251 w = (unsigned short)w;
20254 *p++ = 'l';
20255 #ifdef HAVE_LONG_LONG
20256 if (sizeof(long long) == sizeof(jim_wide)) {
20257 *p++ = 'l';
20259 #endif
20262 *p++ = (char) ch;
20263 *p = '\0';
20266 if (width > 10000 || length > 10000 || precision > 10000) {
20267 Jim_SetResultString(interp, "format too long", -1);
20268 goto error;
20273 if (width > length) {
20274 length = width;
20276 if (gotPrecision) {
20277 length += precision;
20281 if (num_buffer_size < length + 1) {
20282 num_buffer_size = length + 1;
20283 num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
20286 if (doubleType) {
20287 snprintf(num_buffer, length + 1, spec, d);
20289 else {
20290 formatted_bytes = snprintf(num_buffer, length + 1, spec, w);
20292 formatted_chars = formatted_bytes = strlen(num_buffer);
20293 formatted_buf = num_buffer;
20294 break;
20297 default: {
20299 spec[0] = ch;
20300 spec[1] = '\0';
20301 Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec);
20302 goto error;
20306 if (!gotMinus) {
20307 while (formatted_chars < width) {
20308 Jim_AppendString(interp, resultPtr, &pad, 1);
20309 formatted_chars++;
20313 Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes);
20315 while (formatted_chars < width) {
20316 Jim_AppendString(interp, resultPtr, &pad, 1);
20317 formatted_chars++;
20320 objIndex += gotSequential;
20322 if (numBytes) {
20323 Jim_AppendString(interp, resultPtr, span, numBytes);
20326 Jim_Free(num_buffer);
20327 return resultPtr;
20329 errorMsg:
20330 Jim_SetResultString(interp, msg, -1);
20331 error:
20332 Jim_FreeNewObj(interp, resultPtr);
20333 Jim_Free(num_buffer);
20334 return NULL;
20338 #if defined(JIM_REGEXP)
20339 #include <stdio.h>
20340 #include <ctype.h>
20341 #include <stdlib.h>
20342 #include <string.h>
20346 #define REG_MAX_PAREN 100
20350 #define END 0
20351 #define BOL 1
20352 #define EOL 2
20353 #define ANY 3
20354 #define ANYOF 4
20355 #define ANYBUT 5
20356 #define BRANCH 6
20357 #define BACK 7
20358 #define EXACTLY 8
20359 #define NOTHING 9
20360 #define REP 10
20361 #define REPMIN 11
20362 #define REPX 12
20363 #define REPXMIN 13
20364 #define BOLX 14
20365 #define EOLX 15
20366 #define WORDA 16
20367 #define WORDZ 17
20369 #define OPENNC 1000
20370 #define OPEN 1001
20375 #define CLOSENC 2000
20376 #define CLOSE 2001
20377 #define CLOSE_END (CLOSE+REG_MAX_PAREN)
20379 #define REG_MAGIC 0xFADED00D
20382 #define OP(preg, p) (preg->program[p])
20383 #define NEXT(preg, p) (preg->program[p + 1])
20384 #define OPERAND(p) ((p) + 2)
20389 #define FAIL(R,M) { (R)->err = (M); return (M); }
20390 #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{')
20391 #define META "^$.[()|?{+*"
20393 #define HASWIDTH 1
20394 #define SIMPLE 2
20395 #define SPSTART 4
20396 #define WORST 0
20398 #define MAX_REP_COUNT 1000000
20400 static int reg(regex_t *preg, int paren, int *flagp );
20401 static int regpiece(regex_t *preg, int *flagp );
20402 static int regbranch(regex_t *preg, int *flagp );
20403 static int regatom(regex_t *preg, int *flagp );
20404 static int regnode(regex_t *preg, int op );
20405 static int regnext(regex_t *preg, int p );
20406 static void regc(regex_t *preg, int b );
20407 static int reginsert(regex_t *preg, int op, int size, int opnd );
20408 static void regtail(regex_t *preg, int p, int val);
20409 static void regoptail(regex_t *preg, int p, int val );
20410 static int regopsize(regex_t *preg, int p );
20412 static int reg_range_find(const int *string, int c);
20413 static const char *str_find(const char *string, int c, int nocase);
20414 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase);
20417 #ifdef DEBUG
20418 static int regnarrate = 0;
20419 static void regdump(regex_t *preg);
20420 static const char *regprop( int op );
20421 #endif
20424 static int str_int_len(const int *seq)
20426 int n = 0;
20427 while (*seq++) {
20428 n++;
20430 return n;
20433 int regcomp(regex_t *preg, const char *exp, int cflags)
20435 int scan;
20436 int longest;
20437 unsigned len;
20438 int flags;
20440 #ifdef DEBUG
20441 fprintf(stderr, "Compiling: '%s'\n", exp);
20442 #endif
20443 memset(preg, 0, sizeof(*preg));
20445 if (exp == NULL)
20446 FAIL(preg, REG_ERR_NULL_ARGUMENT);
20449 preg->cflags = cflags;
20450 preg->regparse = exp;
20453 preg->proglen = (strlen(exp) + 1) * 5;
20454 preg->program = malloc(preg->proglen * sizeof(int));
20455 if (preg->program == NULL)
20456 FAIL(preg, REG_ERR_NOMEM);
20458 regc(preg, REG_MAGIC);
20459 if (reg(preg, 0, &flags) == 0) {
20460 return preg->err;
20464 if (preg->re_nsub >= REG_MAX_PAREN)
20465 FAIL(preg,REG_ERR_TOO_BIG);
20468 preg->regstart = 0;
20469 preg->reganch = 0;
20470 preg->regmust = 0;
20471 preg->regmlen = 0;
20472 scan = 1;
20473 if (OP(preg, regnext(preg, scan)) == END) {
20474 scan = OPERAND(scan);
20477 if (OP(preg, scan) == EXACTLY) {
20478 preg->regstart = preg->program[OPERAND(scan)];
20480 else if (OP(preg, scan) == BOL)
20481 preg->reganch++;
20483 if (flags&SPSTART) {
20484 longest = 0;
20485 len = 0;
20486 for (; scan != 0; scan = regnext(preg, scan)) {
20487 if (OP(preg, scan) == EXACTLY) {
20488 int plen = str_int_len(preg->program + OPERAND(scan));
20489 if (plen >= len) {
20490 longest = OPERAND(scan);
20491 len = plen;
20495 preg->regmust = longest;
20496 preg->regmlen = len;
20500 #ifdef DEBUG
20501 regdump(preg);
20502 #endif
20504 return 0;
20507 static int reg(regex_t *preg, int paren, int *flagp )
20509 int ret;
20510 int br;
20511 int ender;
20512 int parno = 0;
20513 int flags;
20515 *flagp = HASWIDTH;
20518 if (paren) {
20519 if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
20521 preg->regparse += 2;
20522 parno = -1;
20524 else {
20525 parno = ++preg->re_nsub;
20527 ret = regnode(preg, OPEN+parno);
20528 } else
20529 ret = 0;
20532 br = regbranch(preg, &flags);
20533 if (br == 0)
20534 return 0;
20535 if (ret != 0)
20536 regtail(preg, ret, br);
20537 else
20538 ret = br;
20539 if (!(flags&HASWIDTH))
20540 *flagp &= ~HASWIDTH;
20541 *flagp |= flags&SPSTART;
20542 while (*preg->regparse == '|') {
20543 preg->regparse++;
20544 br = regbranch(preg, &flags);
20545 if (br == 0)
20546 return 0;
20547 regtail(preg, ret, br);
20548 if (!(flags&HASWIDTH))
20549 *flagp &= ~HASWIDTH;
20550 *flagp |= flags&SPSTART;
20554 ender = regnode(preg, (paren) ? CLOSE+parno : END);
20555 regtail(preg, ret, ender);
20558 for (br = ret; br != 0; br = regnext(preg, br))
20559 regoptail(preg, br, ender);
20562 if (paren && *preg->regparse++ != ')') {
20563 preg->err = REG_ERR_UNMATCHED_PAREN;
20564 return 0;
20565 } else if (!paren && *preg->regparse != '\0') {
20566 if (*preg->regparse == ')') {
20567 preg->err = REG_ERR_UNMATCHED_PAREN;
20568 return 0;
20569 } else {
20570 preg->err = REG_ERR_JUNK_ON_END;
20571 return 0;
20575 return(ret);
20578 static int regbranch(regex_t *preg, int *flagp )
20580 int ret;
20581 int chain;
20582 int latest;
20583 int flags;
20585 *flagp = WORST;
20587 ret = regnode(preg, BRANCH);
20588 chain = 0;
20589 while (*preg->regparse != '\0' && *preg->regparse != ')' &&
20590 *preg->regparse != '|') {
20591 latest = regpiece(preg, &flags);
20592 if (latest == 0)
20593 return 0;
20594 *flagp |= flags&HASWIDTH;
20595 if (chain == 0) {
20596 *flagp |= flags&SPSTART;
20598 else {
20599 regtail(preg, chain, latest);
20601 chain = latest;
20603 if (chain == 0)
20604 (void) regnode(preg, NOTHING);
20606 return(ret);
20609 static int regpiece(regex_t *preg, int *flagp)
20611 int ret;
20612 char op;
20613 int next;
20614 int flags;
20615 int min;
20616 int max;
20618 ret = regatom(preg, &flags);
20619 if (ret == 0)
20620 return 0;
20622 op = *preg->regparse;
20623 if (!ISMULT(op)) {
20624 *flagp = flags;
20625 return(ret);
20628 if (!(flags&HASWIDTH) && op != '?') {
20629 preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY;
20630 return 0;
20634 if (op == '{') {
20635 char *end;
20637 min = strtoul(preg->regparse + 1, &end, 10);
20638 if (end == preg->regparse + 1) {
20639 preg->err = REG_ERR_BAD_COUNT;
20640 return 0;
20642 if (*end == '}') {
20643 max = min;
20645 else if (*end == '\0') {
20646 preg->err = REG_ERR_UNMATCHED_BRACES;
20647 return 0;
20649 else {
20650 preg->regparse = end;
20651 max = strtoul(preg->regparse + 1, &end, 10);
20652 if (*end != '}') {
20653 preg->err = REG_ERR_UNMATCHED_BRACES;
20654 return 0;
20657 if (end == preg->regparse + 1) {
20658 max = MAX_REP_COUNT;
20660 else if (max < min || max >= 100) {
20661 preg->err = REG_ERR_BAD_COUNT;
20662 return 0;
20664 if (min >= 100) {
20665 preg->err = REG_ERR_BAD_COUNT;
20666 return 0;
20669 preg->regparse = strchr(preg->regparse, '}');
20671 else {
20672 min = (op == '+');
20673 max = (op == '?' ? 1 : MAX_REP_COUNT);
20676 if (preg->regparse[1] == '?') {
20677 preg->regparse++;
20678 next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret);
20680 else {
20681 next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret);
20683 preg->program[ret + 2] = max;
20684 preg->program[ret + 3] = min;
20685 preg->program[ret + 4] = 0;
20687 *flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART);
20689 if (!(flags & SIMPLE)) {
20690 int back = regnode(preg, BACK);
20691 regtail(preg, back, ret);
20692 regtail(preg, next, back);
20695 preg->regparse++;
20696 if (ISMULT(*preg->regparse)) {
20697 preg->err = REG_ERR_NESTED_COUNT;
20698 return 0;
20701 return ret;
20704 static void reg_addrange(regex_t *preg, int lower, int upper)
20706 if (lower > upper) {
20707 reg_addrange(preg, upper, lower);
20710 regc(preg, upper - lower + 1);
20711 regc(preg, lower);
20714 static void reg_addrange_str(regex_t *preg, const char *str)
20716 while (*str) {
20717 reg_addrange(preg, *str, *str);
20718 str++;
20722 static int reg_utf8_tounicode_case(const char *s, int *uc, int upper)
20724 int l = utf8_tounicode(s, uc);
20725 if (upper) {
20726 *uc = utf8_upper(*uc);
20728 return l;
20731 static int hexdigitval(int c)
20733 if (c >= '0' && c <= '9')
20734 return c - '0';
20735 if (c >= 'a' && c <= 'f')
20736 return c - 'a' + 10;
20737 if (c >= 'A' && c <= 'F')
20738 return c - 'A' + 10;
20739 return -1;
20742 static int parse_hex(const char *s, int n, int *uc)
20744 int val = 0;
20745 int k;
20747 for (k = 0; k < n; k++) {
20748 int c = hexdigitval(*s++);
20749 if (c == -1) {
20750 break;
20752 val = (val << 4) | c;
20754 if (k) {
20755 *uc = val;
20757 return k;
20760 static int reg_decode_escape(const char *s, int *ch)
20762 int n;
20763 const char *s0 = s;
20765 *ch = *s++;
20767 switch (*ch) {
20768 case 'b': *ch = '\b'; break;
20769 case 'e': *ch = 27; break;
20770 case 'f': *ch = '\f'; break;
20771 case 'n': *ch = '\n'; break;
20772 case 'r': *ch = '\r'; break;
20773 case 't': *ch = '\t'; break;
20774 case 'v': *ch = '\v'; break;
20775 case 'u':
20776 if (*s == '{') {
20778 n = parse_hex(s + 1, 6, ch);
20779 if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
20780 s += n + 2;
20782 else {
20784 *ch = 'u';
20787 else if ((n = parse_hex(s, 4, ch)) > 0) {
20788 s += n;
20790 break;
20791 case 'U':
20792 if ((n = parse_hex(s, 8, ch)) > 0) {
20793 s += n;
20795 break;
20796 case 'x':
20797 if ((n = parse_hex(s, 2, ch)) > 0) {
20798 s += n;
20800 break;
20801 case '\0':
20802 s--;
20803 *ch = '\\';
20804 break;
20806 return s - s0;
20809 static int regatom(regex_t *preg, int *flagp)
20811 int ret;
20812 int flags;
20813 int nocase = (preg->cflags & REG_ICASE);
20815 int ch;
20816 int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase);
20818 *flagp = WORST;
20820 preg->regparse += n;
20821 switch (ch) {
20823 case '^':
20824 ret = regnode(preg, BOL);
20825 break;
20826 case '$':
20827 ret = regnode(preg, EOL);
20828 break;
20829 case '.':
20830 ret = regnode(preg, ANY);
20831 *flagp |= HASWIDTH|SIMPLE;
20832 break;
20833 case '[': {
20834 const char *pattern = preg->regparse;
20836 if (*pattern == '^') {
20837 ret = regnode(preg, ANYBUT);
20838 pattern++;
20839 } else
20840 ret = regnode(preg, ANYOF);
20843 if (*pattern == ']' || *pattern == '-') {
20844 reg_addrange(preg, *pattern, *pattern);
20845 pattern++;
20848 while (*pattern && *pattern != ']') {
20850 int start;
20851 int end;
20853 enum {
20854 CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
20855 CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
20856 CC_NUM
20858 int cc;
20860 pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
20861 if (start == '\\') {
20863 switch (*pattern) {
20864 case 's':
20865 pattern++;
20866 cc = CC_SPACE;
20867 goto cc_switch;
20868 case 'd':
20869 pattern++;
20870 cc = CC_DIGIT;
20871 goto cc_switch;
20872 case 'w':
20873 pattern++;
20874 reg_addrange(preg, '_', '_');
20875 cc = CC_ALNUM;
20876 goto cc_switch;
20878 pattern += reg_decode_escape(pattern, &start);
20879 if (start == 0) {
20880 preg->err = REG_ERR_NULL_CHAR;
20881 return 0;
20884 if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
20886 pattern += utf8_tounicode(pattern, &end);
20887 pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
20888 if (end == '\\') {
20889 pattern += reg_decode_escape(pattern, &end);
20890 if (end == 0) {
20891 preg->err = REG_ERR_NULL_CHAR;
20892 return 0;
20896 reg_addrange(preg, start, end);
20897 continue;
20899 if (start == '[' && pattern[0] == ':') {
20900 static const char *character_class[] = {
20901 ":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
20902 ":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
20905 for (cc = 0; cc < CC_NUM; cc++) {
20906 n = strlen(character_class[cc]);
20907 if (strncmp(pattern, character_class[cc], n) == 0) {
20909 pattern += n + 1;
20910 break;
20913 if (cc != CC_NUM) {
20914 cc_switch:
20915 switch (cc) {
20916 case CC_ALNUM:
20917 reg_addrange(preg, '0', '9');
20919 case CC_ALPHA:
20920 if ((preg->cflags & REG_ICASE) == 0) {
20921 reg_addrange(preg, 'a', 'z');
20923 reg_addrange(preg, 'A', 'Z');
20924 break;
20925 case CC_SPACE:
20926 reg_addrange_str(preg, " \t\r\n\f\v");
20927 break;
20928 case CC_BLANK:
20929 reg_addrange_str(preg, " \t");
20930 break;
20931 case CC_UPPER:
20932 reg_addrange(preg, 'A', 'Z');
20933 break;
20934 case CC_LOWER:
20935 reg_addrange(preg, 'a', 'z');
20936 break;
20937 case CC_XDIGIT:
20938 reg_addrange(preg, 'a', 'f');
20939 reg_addrange(preg, 'A', 'F');
20941 case CC_DIGIT:
20942 reg_addrange(preg, '0', '9');
20943 break;
20944 case CC_CNTRL:
20945 reg_addrange(preg, 0, 31);
20946 reg_addrange(preg, 127, 127);
20947 break;
20948 case CC_PRINT:
20949 reg_addrange(preg, ' ', '~');
20950 break;
20951 case CC_GRAPH:
20952 reg_addrange(preg, '!', '~');
20953 break;
20954 case CC_PUNCT:
20955 reg_addrange(preg, '!', '/');
20956 reg_addrange(preg, ':', '@');
20957 reg_addrange(preg, '[', '`');
20958 reg_addrange(preg, '{', '~');
20959 break;
20961 continue;
20965 reg_addrange(preg, start, start);
20967 regc(preg, '\0');
20969 if (*pattern) {
20970 pattern++;
20972 preg->regparse = pattern;
20974 *flagp |= HASWIDTH|SIMPLE;
20976 break;
20977 case '(':
20978 ret = reg(preg, 1, &flags);
20979 if (ret == 0)
20980 return 0;
20981 *flagp |= flags&(HASWIDTH|SPSTART);
20982 break;
20983 case '\0':
20984 case '|':
20985 case ')':
20986 preg->err = REG_ERR_INTERNAL;
20987 return 0;
20988 case '?':
20989 case '+':
20990 case '*':
20991 case '{':
20992 preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
20993 return 0;
20994 case '\\':
20995 ch = *preg->regparse++;
20996 switch (ch) {
20997 case '\0':
20998 preg->err = REG_ERR_TRAILING_BACKSLASH;
20999 return 0;
21000 case 'A':
21001 ret = regnode(preg, BOLX);
21002 break;
21003 case 'Z':
21004 ret = regnode(preg, EOLX);
21005 break;
21006 case '<':
21007 case 'm':
21008 ret = regnode(preg, WORDA);
21009 break;
21010 case '>':
21011 case 'M':
21012 ret = regnode(preg, WORDZ);
21013 break;
21014 case 'd':
21015 case 'D':
21016 ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT);
21017 reg_addrange(preg, '0', '9');
21018 regc(preg, '\0');
21019 *flagp |= HASWIDTH|SIMPLE;
21020 break;
21021 case 'w':
21022 case 'W':
21023 ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT);
21024 if ((preg->cflags & REG_ICASE) == 0) {
21025 reg_addrange(preg, 'a', 'z');
21027 reg_addrange(preg, 'A', 'Z');
21028 reg_addrange(preg, '0', '9');
21029 reg_addrange(preg, '_', '_');
21030 regc(preg, '\0');
21031 *flagp |= HASWIDTH|SIMPLE;
21032 break;
21033 case 's':
21034 case 'S':
21035 ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT);
21036 reg_addrange_str(preg," \t\r\n\f\v");
21037 regc(preg, '\0');
21038 *flagp |= HASWIDTH|SIMPLE;
21039 break;
21041 default:
21044 preg->regparse--;
21045 goto de_fault;
21047 break;
21048 de_fault:
21049 default: {
21050 int added = 0;
21053 preg->regparse -= n;
21055 ret = regnode(preg, EXACTLY);
21059 while (*preg->regparse && strchr(META, *preg->regparse) == NULL) {
21060 n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE));
21061 if (ch == '\\' && preg->regparse[n]) {
21062 if (strchr("<>mMwWdDsSAZ", preg->regparse[n])) {
21064 break;
21066 n += reg_decode_escape(preg->regparse + n, &ch);
21067 if (ch == 0) {
21068 preg->err = REG_ERR_NULL_CHAR;
21069 return 0;
21074 if (ISMULT(preg->regparse[n])) {
21076 if (added) {
21078 break;
21081 regc(preg, ch);
21082 added++;
21083 preg->regparse += n;
21084 break;
21088 regc(preg, ch);
21089 added++;
21090 preg->regparse += n;
21092 regc(preg, '\0');
21094 *flagp |= HASWIDTH;
21095 if (added == 1)
21096 *flagp |= SIMPLE;
21097 break;
21099 break;
21102 return(ret);
21105 static void reg_grow(regex_t *preg, int n)
21107 if (preg->p + n >= preg->proglen) {
21108 preg->proglen = (preg->p + n) * 2;
21109 preg->program = realloc(preg->program, preg->proglen * sizeof(int));
21114 static int regnode(regex_t *preg, int op)
21116 reg_grow(preg, 2);
21119 preg->program[preg->p++] = op;
21120 preg->program[preg->p++] = 0;
21123 return preg->p - 2;
21126 static void regc(regex_t *preg, int b )
21128 reg_grow(preg, 1);
21129 preg->program[preg->p++] = b;
21132 static int reginsert(regex_t *preg, int op, int size, int opnd )
21134 reg_grow(preg, size);
21137 memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd));
21139 memset(preg->program + opnd, 0, sizeof(int) * size);
21141 preg->program[opnd] = op;
21143 preg->p += size;
21145 return opnd + size;
21148 static void regtail(regex_t *preg, int p, int val)
21150 int scan;
21151 int temp;
21152 int offset;
21155 scan = p;
21156 for (;;) {
21157 temp = regnext(preg, scan);
21158 if (temp == 0)
21159 break;
21160 scan = temp;
21163 if (OP(preg, scan) == BACK)
21164 offset = scan - val;
21165 else
21166 offset = val - scan;
21168 preg->program[scan + 1] = offset;
21172 static void regoptail(regex_t *preg, int p, int val )
21175 if (p != 0 && OP(preg, p) == BRANCH) {
21176 regtail(preg, OPERAND(p), val);
21181 static int regtry(regex_t *preg, const char *string );
21182 static int regmatch(regex_t *preg, int prog);
21183 static int regrepeat(regex_t *preg, int p, int max);
21185 int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
21187 const char *s;
21188 int scan;
21191 if (preg == NULL || preg->program == NULL || string == NULL) {
21192 return REG_ERR_NULL_ARGUMENT;
21196 if (*preg->program != REG_MAGIC) {
21197 return REG_ERR_CORRUPTED;
21200 #ifdef DEBUG
21201 fprintf(stderr, "regexec: %s\n", string);
21202 regdump(preg);
21203 #endif
21205 preg->eflags = eflags;
21206 preg->pmatch = pmatch;
21207 preg->nmatch = nmatch;
21208 preg->start = string;
21211 for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) {
21212 int op = OP(preg, scan);
21213 if (op == END)
21214 break;
21215 if (op == REPX || op == REPXMIN)
21216 preg->program[scan + 4] = 0;
21220 if (preg->regmust != 0) {
21221 s = string;
21222 while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) {
21223 if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) {
21224 break;
21226 s++;
21228 if (s == NULL)
21229 return REG_NOMATCH;
21233 preg->regbol = string;
21236 if (preg->reganch) {
21237 if (eflags & REG_NOTBOL) {
21239 goto nextline;
21241 while (1) {
21242 if (regtry(preg, string)) {
21243 return REG_NOERROR;
21245 if (*string) {
21246 nextline:
21247 if (preg->cflags & REG_NEWLINE) {
21249 string = strchr(string, '\n');
21250 if (string) {
21251 preg->regbol = ++string;
21252 continue;
21256 return REG_NOMATCH;
21261 s = string;
21262 if (preg->regstart != '\0') {
21264 while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) {
21265 if (regtry(preg, s))
21266 return REG_NOERROR;
21267 s++;
21270 else
21272 while (1) {
21273 if (regtry(preg, s))
21274 return REG_NOERROR;
21275 if (*s == '\0') {
21276 break;
21278 else {
21279 int c;
21280 s += utf8_tounicode(s, &c);
21285 return REG_NOMATCH;
21289 static int regtry( regex_t *preg, const char *string )
21291 int i;
21293 preg->reginput = string;
21295 for (i = 0; i < preg->nmatch; i++) {
21296 preg->pmatch[i].rm_so = -1;
21297 preg->pmatch[i].rm_eo = -1;
21299 if (regmatch(preg, 1)) {
21300 preg->pmatch[0].rm_so = string - preg->start;
21301 preg->pmatch[0].rm_eo = preg->reginput - preg->start;
21302 return(1);
21303 } else
21304 return(0);
21307 static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase)
21309 const char *s = string;
21310 while (proglen && *s) {
21311 int ch;
21312 int n = reg_utf8_tounicode_case(s, &ch, nocase);
21313 if (ch != *prog) {
21314 return -1;
21316 prog++;
21317 s += n;
21318 proglen--;
21320 if (proglen == 0) {
21321 return s - string;
21323 return -1;
21326 static int reg_range_find(const int *range, int c)
21328 while (*range) {
21330 if (c >= range[1] && c <= (range[0] + range[1] - 1)) {
21331 return 1;
21333 range += 2;
21335 return 0;
21338 static const char *str_find(const char *string, int c, int nocase)
21340 if (nocase) {
21342 c = utf8_upper(c);
21344 while (*string) {
21345 int ch;
21346 int n = reg_utf8_tounicode_case(string, &ch, nocase);
21347 if (c == ch) {
21348 return string;
21350 string += n;
21352 return NULL;
21355 static int reg_iseol(regex_t *preg, int ch)
21357 if (preg->cflags & REG_NEWLINE) {
21358 return ch == '\0' || ch == '\n';
21360 else {
21361 return ch == '\0';
21365 static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin)
21367 int nextch = '\0';
21368 const char *save;
21369 int no;
21370 int c;
21372 int max = preg->program[scan + 2];
21373 int min = preg->program[scan + 3];
21374 int next = regnext(preg, scan);
21376 if (OP(preg, next) == EXACTLY) {
21377 nextch = preg->program[OPERAND(next)];
21379 save = preg->reginput;
21380 no = regrepeat(preg, scan + 5, max);
21381 if (no < min) {
21382 return 0;
21384 if (matchmin) {
21386 max = no;
21387 no = min;
21390 while (1) {
21391 if (matchmin) {
21392 if (no > max) {
21393 break;
21396 else {
21397 if (no < min) {
21398 break;
21401 preg->reginput = save + utf8_index(save, no);
21402 reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21404 if (reg_iseol(preg, nextch) || c == nextch) {
21405 if (regmatch(preg, next)) {
21406 return(1);
21409 if (matchmin) {
21411 no++;
21413 else {
21415 no--;
21418 return(0);
21421 static int regmatchrepeat(regex_t *preg, int scan, int matchmin)
21423 int *scanpt = preg->program + scan;
21425 int max = scanpt[2];
21426 int min = scanpt[3];
21429 if (scanpt[4] < min) {
21431 scanpt[4]++;
21432 if (regmatch(preg, scan + 5)) {
21433 return 1;
21435 scanpt[4]--;
21436 return 0;
21438 if (scanpt[4] > max) {
21439 return 0;
21442 if (matchmin) {
21444 if (regmatch(preg, regnext(preg, scan))) {
21445 return 1;
21448 scanpt[4]++;
21449 if (regmatch(preg, scan + 5)) {
21450 return 1;
21452 scanpt[4]--;
21453 return 0;
21456 if (scanpt[4] < max) {
21457 scanpt[4]++;
21458 if (regmatch(preg, scan + 5)) {
21459 return 1;
21461 scanpt[4]--;
21464 return regmatch(preg, regnext(preg, scan));
21468 static int regmatch(regex_t *preg, int prog)
21470 int scan;
21471 int next;
21472 const char *save;
21474 scan = prog;
21476 #ifdef DEBUG
21477 if (scan != 0 && regnarrate)
21478 fprintf(stderr, "%s(\n", regprop(scan));
21479 #endif
21480 while (scan != 0) {
21481 int n;
21482 int c;
21483 #ifdef DEBUG
21484 if (regnarrate) {
21485 fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan)));
21487 #endif
21488 next = regnext(preg, scan);
21489 n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE));
21491 switch (OP(preg, scan)) {
21492 case BOLX:
21493 if ((preg->eflags & REG_NOTBOL)) {
21494 return(0);
21497 case BOL:
21498 if (preg->reginput != preg->regbol) {
21499 return(0);
21501 break;
21502 case EOLX:
21503 if (c != 0) {
21505 return 0;
21507 break;
21508 case EOL:
21509 if (!reg_iseol(preg, c)) {
21510 return(0);
21512 break;
21513 case WORDA:
21515 if ((!isalnum(UCHAR(c))) && c != '_')
21516 return(0);
21518 if (preg->reginput > preg->regbol &&
21519 (isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
21520 return(0);
21521 break;
21522 case WORDZ:
21524 if (preg->reginput > preg->regbol) {
21526 if (reg_iseol(preg, c) || !isalnum(UCHAR(c)) || c != '_') {
21527 c = preg->reginput[-1];
21529 if (isalnum(UCHAR(c)) || c == '_') {
21530 break;
21535 return(0);
21537 case ANY:
21538 if (reg_iseol(preg, c))
21539 return 0;
21540 preg->reginput += n;
21541 break;
21542 case EXACTLY: {
21543 int opnd;
21544 int len;
21545 int slen;
21547 opnd = OPERAND(scan);
21548 len = str_int_len(preg->program + opnd);
21550 slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE);
21551 if (slen < 0) {
21552 return(0);
21554 preg->reginput += slen;
21556 break;
21557 case ANYOF:
21558 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) {
21559 return(0);
21561 preg->reginput += n;
21562 break;
21563 case ANYBUT:
21564 if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) {
21565 return(0);
21567 preg->reginput += n;
21568 break;
21569 case NOTHING:
21570 break;
21571 case BACK:
21572 break;
21573 case BRANCH:
21574 if (OP(preg, next) != BRANCH)
21575 next = OPERAND(scan);
21576 else {
21577 do {
21578 save = preg->reginput;
21579 if (regmatch(preg, OPERAND(scan))) {
21580 return(1);
21582 preg->reginput = save;
21583 scan = regnext(preg, scan);
21584 } while (scan != 0 && OP(preg, scan) == BRANCH);
21585 return(0);
21588 break;
21589 case REP:
21590 case REPMIN:
21591 return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN);
21593 case REPX:
21594 case REPXMIN:
21595 return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN);
21597 case END:
21598 return 1;
21600 case OPENNC:
21601 case CLOSENC:
21602 return regmatch(preg, next);
21604 default:
21605 if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
21606 save = preg->reginput;
21607 if (regmatch(preg, next)) {
21608 if (OP(preg, scan) < CLOSE) {
21609 int no = OP(preg, scan) - OPEN;
21610 if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
21611 preg->pmatch[no].rm_so = save - preg->start;
21614 else {
21615 int no = OP(preg, scan) - CLOSE;
21616 if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
21617 preg->pmatch[no].rm_eo = save - preg->start;
21620 return(1);
21622 return(0);
21624 return REG_ERR_INTERNAL;
21627 scan = next;
21630 return REG_ERR_INTERNAL;
21633 static int regrepeat(regex_t *preg, int p, int max)
21635 int count = 0;
21636 const char *scan;
21637 int opnd;
21638 int ch;
21639 int n;
21641 scan = preg->reginput;
21642 opnd = OPERAND(p);
21643 switch (OP(preg, p)) {
21644 case ANY:
21646 while (!reg_iseol(preg, *scan) && count < max) {
21647 count++;
21648 scan++;
21650 break;
21651 case EXACTLY:
21652 while (count < max) {
21653 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21654 if (preg->program[opnd] != ch) {
21655 break;
21657 count++;
21658 scan += n;
21660 break;
21661 case ANYOF:
21662 while (count < max) {
21663 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21664 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) {
21665 break;
21667 count++;
21668 scan += n;
21670 break;
21671 case ANYBUT:
21672 while (count < max) {
21673 n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
21674 if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) {
21675 break;
21677 count++;
21678 scan += n;
21680 break;
21681 default:
21682 preg->err = REG_ERR_INTERNAL;
21683 count = 0;
21684 break;
21686 preg->reginput = scan;
21688 return(count);
21691 static int regnext(regex_t *preg, int p )
21693 int offset;
21695 offset = NEXT(preg, p);
21697 if (offset == 0)
21698 return 0;
21700 if (OP(preg, p) == BACK)
21701 return(p-offset);
21702 else
21703 return(p+offset);
21706 static int regopsize(regex_t *preg, int p )
21709 switch (OP(preg, p)) {
21710 case REP:
21711 case REPMIN:
21712 case REPX:
21713 case REPXMIN:
21714 return 5;
21716 case ANYOF:
21717 case ANYBUT:
21718 case EXACTLY: {
21719 int s = p + 2;
21720 while (preg->program[s++]) {
21722 return s - p;
21725 return 2;
21729 size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
21731 static const char *error_strings[] = {
21732 "success",
21733 "no match",
21734 "bad pattern",
21735 "null argument",
21736 "unknown error",
21737 "too big",
21738 "out of memory",
21739 "too many ()",
21740 "parentheses () not balanced",
21741 "braces {} not balanced",
21742 "invalid repetition count(s)",
21743 "extra characters",
21744 "*+ of empty atom",
21745 "nested count",
21746 "internal error",
21747 "count follows nothing",
21748 "trailing backslash",
21749 "corrupted program",
21750 "contains null char",
21752 const char *err;
21754 if (errcode < 0 || errcode >= REG_ERR_NUM) {
21755 err = "Bad error code";
21757 else {
21758 err = error_strings[errcode];
21761 return snprintf(errbuf, errbuf_size, "%s", err);
21764 void regfree(regex_t *preg)
21766 free(preg->program);
21769 #endif
21770 #include <string.h>
21772 void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
21774 Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno()));
21777 #if defined(__MINGW32__)
21778 #include <sys/stat.h>
21780 int Jim_Errno(void)
21782 switch (GetLastError()) {
21783 case ERROR_FILE_NOT_FOUND: return ENOENT;
21784 case ERROR_PATH_NOT_FOUND: return ENOENT;
21785 case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
21786 case ERROR_ACCESS_DENIED: return EACCES;
21787 case ERROR_INVALID_HANDLE: return EBADF;
21788 case ERROR_BAD_ENVIRONMENT: return E2BIG;
21789 case ERROR_BAD_FORMAT: return ENOEXEC;
21790 case ERROR_INVALID_ACCESS: return EACCES;
21791 case ERROR_INVALID_DRIVE: return ENOENT;
21792 case ERROR_CURRENT_DIRECTORY: return EACCES;
21793 case ERROR_NOT_SAME_DEVICE: return EXDEV;
21794 case ERROR_NO_MORE_FILES: return ENOENT;
21795 case ERROR_WRITE_PROTECT: return EROFS;
21796 case ERROR_BAD_UNIT: return ENXIO;
21797 case ERROR_NOT_READY: return EBUSY;
21798 case ERROR_BAD_COMMAND: return EIO;
21799 case ERROR_CRC: return EIO;
21800 case ERROR_BAD_LENGTH: return EIO;
21801 case ERROR_SEEK: return EIO;
21802 case ERROR_WRITE_FAULT: return EIO;
21803 case ERROR_READ_FAULT: return EIO;
21804 case ERROR_GEN_FAILURE: return EIO;
21805 case ERROR_SHARING_VIOLATION: return EACCES;
21806 case ERROR_LOCK_VIOLATION: return EACCES;
21807 case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
21808 case ERROR_HANDLE_DISK_FULL: return ENOSPC;
21809 case ERROR_NOT_SUPPORTED: return ENODEV;
21810 case ERROR_REM_NOT_LIST: return EBUSY;
21811 case ERROR_DUP_NAME: return EEXIST;
21812 case ERROR_BAD_NETPATH: return ENOENT;
21813 case ERROR_NETWORK_BUSY: return EBUSY;
21814 case ERROR_DEV_NOT_EXIST: return ENODEV;
21815 case ERROR_TOO_MANY_CMDS: return EAGAIN;
21816 case ERROR_ADAP_HDW_ERR: return EIO;
21817 case ERROR_BAD_NET_RESP: return EIO;
21818 case ERROR_UNEXP_NET_ERR: return EIO;
21819 case ERROR_NETNAME_DELETED: return ENOENT;
21820 case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
21821 case ERROR_BAD_DEV_TYPE: return ENODEV;
21822 case ERROR_BAD_NET_NAME: return ENOENT;
21823 case ERROR_TOO_MANY_NAMES: return ENFILE;
21824 case ERROR_TOO_MANY_SESS: return EIO;
21825 case ERROR_SHARING_PAUSED: return EAGAIN;
21826 case ERROR_REDIR_PAUSED: return EAGAIN;
21827 case ERROR_FILE_EXISTS: return EEXIST;
21828 case ERROR_CANNOT_MAKE: return ENOSPC;
21829 case ERROR_OUT_OF_STRUCTURES: return ENFILE;
21830 case ERROR_ALREADY_ASSIGNED: return EEXIST;
21831 case ERROR_INVALID_PASSWORD: return EPERM;
21832 case ERROR_NET_WRITE_FAULT: return EIO;
21833 case ERROR_NO_PROC_SLOTS: return EAGAIN;
21834 case ERROR_DISK_CHANGE: return EXDEV;
21835 case ERROR_BROKEN_PIPE: return EPIPE;
21836 case ERROR_OPEN_FAILED: return ENOENT;
21837 case ERROR_DISK_FULL: return ENOSPC;
21838 case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
21839 case ERROR_INVALID_TARGET_HANDLE: return EBADF;
21840 case ERROR_INVALID_NAME: return ENOENT;
21841 case ERROR_PROC_NOT_FOUND: return ESRCH;
21842 case ERROR_WAIT_NO_CHILDREN: return ECHILD;
21843 case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
21844 case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
21845 case ERROR_SEEK_ON_DEVICE: return ESPIPE;
21846 case ERROR_BUSY_DRIVE: return EAGAIN;
21847 case ERROR_DIR_NOT_EMPTY: return EEXIST;
21848 case ERROR_NOT_LOCKED: return EACCES;
21849 case ERROR_BAD_PATHNAME: return ENOENT;
21850 case ERROR_LOCK_FAILED: return EACCES;
21851 case ERROR_ALREADY_EXISTS: return EEXIST;
21852 case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
21853 case ERROR_BAD_PIPE: return EPIPE;
21854 case ERROR_PIPE_BUSY: return EAGAIN;
21855 case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
21856 case ERROR_DIRECTORY: return ENOTDIR;
21858 return EINVAL;
21861 pidtype waitpid(pidtype pid, int *status, int nohang)
21863 DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
21864 if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
21866 return JIM_BAD_PID;
21868 GetExitCodeProcess(pid, &ret);
21869 *status = ret;
21870 CloseHandle(pid);
21871 return pid;
21874 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
21876 char name[MAX_PATH];
21877 HANDLE handle;
21879 if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) {
21880 return -1;
21883 handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
21884 CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0),
21885 NULL);
21887 if (handle == INVALID_HANDLE_VALUE) {
21888 goto error;
21891 Jim_SetResultString(interp, name, -1);
21892 return _open_osfhandle((int)handle, _O_RDWR | _O_TEXT);
21894 error:
21895 Jim_SetResultErrno(interp, name);
21896 DeleteFile(name);
21897 return -1;
21900 int Jim_OpenForWrite(const char *filename, int append)
21902 if (strcmp(filename, "/dev/null") == 0) {
21903 filename = "nul:";
21905 int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE);
21906 if (fd >= 0 && append) {
21908 _lseek(fd, 0L, SEEK_END);
21910 return fd;
21913 int Jim_OpenForRead(const char *filename)
21915 if (strcmp(filename, "/dev/null") == 0) {
21916 filename = "nul:";
21918 return _open(filename, _O_RDONLY | _O_TEXT, 0);
21921 #elif defined(HAVE_UNISTD_H)
21925 int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
21927 int fd;
21928 mode_t mask;
21929 Jim_Obj *filenameObj;
21931 if (filename_template == NULL) {
21932 const char *tmpdir = getenv("TMPDIR");
21933 if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
21934 tmpdir = "/tmp/";
21936 filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
21937 if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
21938 Jim_AppendString(interp, filenameObj, "/", 1);
21940 Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
21942 else {
21943 filenameObj = Jim_NewStringObj(interp, filename_template, -1);
21947 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
21948 #ifdef HAVE_MKSTEMP
21949 fd = mkstemp(filenameObj->bytes);
21950 #else
21951 if (mktemp(filenameObj->bytes) == NULL) {
21952 fd = -1;
21954 else {
21955 fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC);
21957 #endif
21958 umask(mask);
21959 if (fd < 0) {
21960 Jim_SetResultErrno(interp, Jim_String(filenameObj));
21961 Jim_FreeNewObj(interp, filenameObj);
21962 return -1;
21964 if (unlink_file) {
21965 remove(Jim_String(filenameObj));
21968 Jim_SetResult(interp, filenameObj);
21969 return fd;
21972 int Jim_OpenForWrite(const char *filename, int append)
21974 return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
21977 int Jim_OpenForRead(const char *filename)
21979 return open(filename, O_RDONLY, 0);
21982 #endif
21984 #if defined(_WIN32) || defined(WIN32)
21985 #ifndef STRICT
21986 #define STRICT
21987 #endif
21988 #define WIN32_LEAN_AND_MEAN
21989 #include <windows.h>
21991 #if defined(HAVE_DLOPEN_COMPAT)
21992 void *dlopen(const char *path, int mode)
21994 JIM_NOTUSED(mode);
21996 return (void *)LoadLibraryA(path);
21999 int dlclose(void *handle)
22001 FreeLibrary((HANDLE)handle);
22002 return 0;
22005 void *dlsym(void *handle, const char *symbol)
22007 return GetProcAddress((HMODULE)handle, symbol);
22010 char *dlerror(void)
22012 static char msg[121];
22013 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
22014 LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
22015 return msg;
22017 #endif
22019 #ifdef _MSC_VER
22021 #include <sys/timeb.h>
22024 int gettimeofday(struct timeval *tv, void *unused)
22026 struct _timeb tb;
22028 _ftime(&tb);
22029 tv->tv_sec = tb.time;
22030 tv->tv_usec = tb.millitm * 1000;
22032 return 0;
22036 DIR *opendir(const char *name)
22038 DIR *dir = 0;
22040 if (name && name[0]) {
22041 size_t base_length = strlen(name);
22042 const char *all =
22043 strchr("/\\", name[base_length - 1]) ? "*" : "/*";
22045 if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 &&
22046 (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) {
22047 strcat(strcpy(dir->name, name), all);
22049 if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1)
22050 dir->result.d_name = 0;
22051 else {
22052 Jim_Free(dir->name);
22053 Jim_Free(dir);
22054 dir = 0;
22057 else {
22058 Jim_Free(dir);
22059 dir = 0;
22060 errno = ENOMEM;
22063 else {
22064 errno = EINVAL;
22066 return dir;
22069 int closedir(DIR * dir)
22071 int result = -1;
22073 if (dir) {
22074 if (dir->handle != -1)
22075 result = _findclose(dir->handle);
22076 Jim_Free(dir->name);
22077 Jim_Free(dir);
22079 if (result == -1)
22080 errno = EBADF;
22081 return result;
22084 struct dirent *readdir(DIR * dir)
22086 struct dirent *result = 0;
22088 if (dir && dir->handle != -1) {
22089 if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
22090 result = &dir->result;
22091 result->d_name = dir->info.name;
22094 else {
22095 errno = EBADF;
22097 return result;
22099 #endif
22100 #endif
22101 #include <stdio.h>
22102 #include <signal.h>
22109 #ifndef SIGPIPE
22110 #define SIGPIPE 13
22111 #endif
22112 #ifndef SIGINT
22113 #define SIGINT 2
22114 #endif
22116 const char *Jim_SignalId(int sig)
22118 static char buf[10];
22119 switch (sig) {
22120 case SIGINT: return "SIGINT";
22121 case SIGPIPE: return "SIGPIPE";
22124 snprintf(buf, sizeof(buf), "%d", sig);
22125 return buf;
22127 #ifndef JIM_BOOTSTRAP_LIB_ONLY
22128 #include <errno.h>
22129 #include <string.h>
22132 #ifdef USE_LINENOISE
22133 #ifdef HAVE_UNISTD_H
22134 #include <unistd.h>
22135 #endif
22136 #ifdef HAVE_SYS_STAT_H
22137 #include <sys/stat.h>
22138 #endif
22139 #include "linenoise.h"
22140 #else
22141 #define MAX_LINE_LEN 512
22142 #endif
22144 #ifdef USE_LINENOISE
22145 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata);
22146 static const char completion_callback_assoc_key[] = "interactive-completion";
22147 #endif
22149 char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
22151 #ifdef USE_LINENOISE
22152 struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key);
22153 char *result;
22154 Jim_Obj *objPtr;
22155 long mlmode = 0;
22156 if (compinfo) {
22157 linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
22159 objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE);
22160 if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) {
22161 linenoiseSetMultiLine(mlmode);
22164 result = linenoise(prompt);
22166 linenoiseSetCompletionCallback(NULL, NULL);
22167 return result;
22168 #else
22169 int len;
22170 char *line = malloc(MAX_LINE_LEN);
22172 fputs(prompt, stdout);
22173 fflush(stdout);
22175 if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
22176 free(line);
22177 return NULL;
22179 len = strlen(line);
22180 if (len && line[len - 1] == '\n') {
22181 line[len - 1] = '\0';
22183 return line;
22184 #endif
22187 void Jim_HistoryLoad(const char *filename)
22189 #ifdef USE_LINENOISE
22190 linenoiseHistoryLoad(filename);
22191 #endif
22194 void Jim_HistoryAdd(const char *line)
22196 #ifdef USE_LINENOISE
22197 linenoiseHistoryAdd(line);
22198 #endif
22201 void Jim_HistorySave(const char *filename)
22203 #ifdef USE_LINENOISE
22204 #ifdef HAVE_UMASK
22205 mode_t mask;
22207 mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
22208 #endif
22209 linenoiseHistorySave(filename);
22210 #ifdef HAVE_UMASK
22211 umask(mask);
22212 #endif
22213 #endif
22216 void Jim_HistoryShow(void)
22218 #ifdef USE_LINENOISE
22220 int i;
22221 int len;
22222 char **history = linenoiseHistory(&len);
22223 for (i = 0; i < len; i++) {
22224 printf("%4d %s\n", i + 1, history[i]);
22226 #endif
22229 #ifdef USE_LINENOISE
22230 struct JimCompletionInfo {
22231 Jim_Interp *interp;
22232 Jim_Obj *command;
22235 static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
22237 struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
22238 Jim_Obj *objv[2];
22239 int ret;
22241 objv[0] = info->command;
22242 objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
22244 ret = Jim_EvalObjVector(info->interp, 2, objv);
22247 if (ret == JIM_OK) {
22248 int i;
22249 Jim_Obj *listObj = Jim_GetResult(info->interp);
22250 int len = Jim_ListLength(info->interp, listObj);
22251 for (i = 0; i < len; i++) {
22252 linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
22257 static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
22259 struct JimCompletionInfo *compinfo = data;
22261 Jim_DecrRefCount(interp, compinfo->command);
22263 Jim_Free(compinfo);
22265 #endif
22267 void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj)
22269 #ifdef USE_LINENOISE
22270 if (commandObj) {
22272 Jim_IncrRefCount(commandObj);
22275 Jim_DeleteAssocData(interp, completion_callback_assoc_key);
22277 if (commandObj) {
22278 struct JimCompletionInfo *compinfo = Jim_Alloc(sizeof(*compinfo));
22279 compinfo->interp = interp;
22280 compinfo->command = commandObj;
22282 Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
22284 #endif
22287 int Jim_InteractivePrompt(Jim_Interp *interp)
22289 int retcode = JIM_OK;
22290 char *history_file = NULL;
22291 #ifdef USE_LINENOISE
22292 const char *home;
22294 home = getenv("HOME");
22295 if (home && isatty(STDIN_FILENO)) {
22296 int history_len = strlen(home) + sizeof("/.jim_history");
22297 history_file = Jim_Alloc(history_len);
22298 snprintf(history_file, history_len, "%s/.jim_history", home);
22299 Jim_HistoryLoad(history_file);
22302 Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
22303 #endif
22305 printf("Welcome to Jim version %d.%d\n",
22306 JIM_VERSION / 100, JIM_VERSION % 100);
22307 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
22309 while (1) {
22310 Jim_Obj *scriptObjPtr;
22311 const char *result;
22312 int reslen;
22313 char prompt[20];
22315 if (retcode != JIM_OK) {
22316 const char *retcodestr = Jim_ReturnCode(retcode);
22318 if (*retcodestr == '?') {
22319 snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
22321 else {
22322 snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
22325 else {
22326 strcpy(prompt, ". ");
22329 scriptObjPtr = Jim_NewStringObj(interp, "", 0);
22330 Jim_IncrRefCount(scriptObjPtr);
22331 while (1) {
22332 char state;
22333 char *line;
22335 line = Jim_HistoryGetline(interp, prompt);
22336 if (line == NULL) {
22337 if (errno == EINTR) {
22338 continue;
22340 Jim_DecrRefCount(interp, scriptObjPtr);
22341 retcode = JIM_OK;
22342 goto out;
22344 if (Jim_Length(scriptObjPtr) != 0) {
22346 Jim_AppendString(interp, scriptObjPtr, "\n", 1);
22348 Jim_AppendString(interp, scriptObjPtr, line, -1);
22349 free(line);
22350 if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
22351 break;
22353 snprintf(prompt, sizeof(prompt), "%c> ", state);
22355 #ifdef USE_LINENOISE
22356 if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
22358 Jim_HistoryShow();
22359 Jim_DecrRefCount(interp, scriptObjPtr);
22360 continue;
22363 Jim_HistoryAdd(Jim_String(scriptObjPtr));
22364 if (history_file) {
22365 Jim_HistorySave(history_file);
22367 #endif
22368 retcode = Jim_EvalObj(interp, scriptObjPtr);
22369 Jim_DecrRefCount(interp, scriptObjPtr);
22371 if (retcode == JIM_EXIT) {
22372 break;
22374 if (retcode == JIM_ERR) {
22375 Jim_MakeErrorMessage(interp);
22377 result = Jim_GetString(Jim_GetResult(interp), &reslen);
22378 if (reslen) {
22379 printf("%s\n", result);
22382 out:
22383 Jim_Free(history_file);
22385 return retcode;
22388 #include <stdio.h>
22389 #include <stdlib.h>
22390 #include <string.h>
22394 extern int Jim_initjimshInit(Jim_Interp *interp);
22396 static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[])
22398 int n;
22399 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
22402 for (n = 0; n < argc; n++) {
22403 Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
22405 Jim_ListAppendElement(interp, listObj, obj);
22408 Jim_SetVariableStr(interp, "argv", listObj);
22409 Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
22412 static void JimPrintErrorMessage(Jim_Interp *interp)
22414 Jim_MakeErrorMessage(interp);
22415 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
22418 void usage(const char* executable_name)
22420 printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
22421 printf("Usage: %s\n", executable_name);
22422 printf("or : %s [options] [filename]\n", executable_name);
22423 printf("\n");
22424 printf("Without options: Interactive mode\n");
22425 printf("\n");
22426 printf("Options:\n");
22427 printf(" --version : prints the version string\n");
22428 printf(" --help : prints this text\n");
22429 printf(" -e CMD : executes command CMD\n");
22430 printf(" NOTE: all subsequent options will be passed as arguments to the command\n");
22431 printf(" [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n");
22432 printf(" NOTE: all subsequent options will be passed to the script\n\n");
22435 int main(int argc, char *const argv[])
22437 int retcode;
22438 Jim_Interp *interp;
22439 char *const orig_argv0 = argv[0];
22442 if (argc > 1 && strcmp(argv[1], "--version") == 0) {
22443 printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
22444 return 0;
22446 else if (argc > 1 && strcmp(argv[1], "--help") == 0) {
22447 usage(argv[0]);
22448 return 0;
22452 interp = Jim_CreateInterp();
22453 Jim_RegisterCoreCommands(interp);
22456 if (Jim_InitStaticExtensions(interp) != JIM_OK) {
22457 JimPrintErrorMessage(interp);
22460 Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
22461 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
22462 retcode = Jim_initjimshInit(interp);
22464 if (argc == 1) {
22466 if (retcode == JIM_ERR) {
22467 JimPrintErrorMessage(interp);
22469 if (retcode != JIM_EXIT) {
22470 JimSetArgv(interp, 0, NULL);
22471 retcode = Jim_InteractivePrompt(interp);
22474 else {
22476 if (argc > 2 && strcmp(argv[1], "-e") == 0) {
22478 JimSetArgv(interp, argc - 3, argv + 3);
22479 retcode = Jim_Eval(interp, argv[2]);
22480 if (retcode != JIM_ERR) {
22481 printf("%s\n", Jim_String(Jim_GetResult(interp)));
22484 else {
22485 Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
22486 JimSetArgv(interp, argc - 2, argv + 2);
22487 if (strcmp(argv[1], "-") == 0) {
22488 retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]");
22489 } else {
22490 retcode = Jim_EvalFile(interp, argv[1]);
22493 if (retcode == JIM_ERR) {
22494 JimPrintErrorMessage(interp);
22497 if (retcode == JIM_EXIT) {
22498 retcode = Jim_GetExitCode(interp);
22500 else if (retcode == JIM_ERR) {
22501 retcode = 1;
22503 else {
22504 retcode = 0;
22506 Jim_FreeInterp(interp);
22507 return retcode;
22509 #endif